1 ///Copyright: Copyright (c) 2017-2019 Andrey Penechko.
2 ///License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
3 ///Authors: Andrey Penechko.
4 
5 /// IR Validation routines
6 module vox.ir.ir_validation;
7 
8 import vox.all;
9 import vox.ir.ir_index;
10 
11 ///
12 void validateIrFunction(CompilationContext* context, IrFunction* ir, string passName = null)
13 {
14 	scope(failure) dumpFunction(context, ir, passName);
15 
16 	auto funcInstrInfos = allInstrInfos[ir.instructionSet];
17 	auto instrValidator = instrValidators[ir.instructionSet];
18 
19 	// Defined vregs
20 	size_t[] definedVregsBitmap = context.allocateTempArray!size_t(cast(uint)divCeil(ir.numVirtualRegisters, size_t.sizeof * 8));
21 	scope(exit) context.freeTempArray(cast(uint[])definedVregsBitmap);
22 	definedVregsBitmap[] = 0;
23 
24 	// Verify defined vregs indices
25 	foreach (IrIndex blockIndex, ref IrBasicBlock block; ir.blocks)
26 	{
27 		void checkResult(IrIndex definition, IrIndex result) {
28 			if (!result.isVirtReg) return;
29 			if (result.storageUintIndex > ir.numVirtualRegisters)
30 				context.internal_error("Virtual register %s defined in %s %s is out of bounds (> %s)",
31 					result, blockIndex, definition, ir.numVirtualRegisters);
32 
33 			// Mark all reachable vregs as live
34 			// later we can see if undefined vreg is used by some instruction
35 			definedVregsBitmap.setBitAt(result.storageUintIndex);
36 		}
37 		foreach(IrIndex phiIndex, ref IrPhi phi; block.phis(ir))
38 			checkResult(phiIndex, phi.result);
39 		foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; block.instructions(ir))
40 			if (instrHeader.hasResult)
41 				checkResult(instrIndex, instrHeader.result(ir));
42 	}
43 
44 	// Function must have at least 2 basic blocks
45 	if (ir.numBasicBlocks < 2) {
46 		context.internal_error("IR must have at least 2 basic blocks, but has %s", ir.numBasicBlocks);
47 	}
48 
49 	// Entry block must have 0 phis and 0 predecessors
50 	context.assertf(ir.getBlock(ir.entryBasicBlock).hasPhis == false, "Entry basic block can not have phi functions");
51 	context.assertf(ir.getBlock(ir.entryBasicBlock).predecessors.length == 0, "Entry basic block can not have predecessors");
52 
53 	foreach (IrIndex blockIndex, ref IrBasicBlock block; ir.blocks)
54 	{
55 		context.assertf(blockIndex.storageUintIndex < ir.numBasicBlocks, "basic block out of bounds %s", blockIndex);
56 
57 		if (!block.isSealed)
58 		{
59 			context.internal_error("Unsealed basic block %s", blockIndex);
60 		}
61 
62 		if (!block.isFinished)
63 		{
64 			context.internal_error("Unfinished basic block %s", blockIndex);
65 		}
66 
67 		IrInstrHeader* firstInstr = ir.getInstr(block.firstInstr);
68 		IrInstrHeader* lastInstr = ir.getInstr(block.lastInstr);
69 		context.assertf(funcInstrInfos[lastInstr.op].isBlockExit,
70 			"Basic block %s does not end with jump, branch or return instruction",
71 			blockIndex);
72 
73 		// Check that all users of virtual reg point to definition
74 		void checkArg(IrIndex argUser, IrIndex arg)
75 		{
76 			if (!arg.isVirtReg) return;
77 
78 			if (arg.storageUintIndex > ir.numVirtualRegisters)
79 				context.internal_error("Virtual register %s used in %s %s is out of bounds (> %s)",
80 					arg, blockIndex, argUser, ir.numVirtualRegisters);
81 
82 			if (!definedVregsBitmap.getBitAt(arg.storageUintIndex))
83 				context.internal_error("Undefined virtual register %s is used in %s %s",
84 					arg, blockIndex, argUser);
85 
86 			IrVirtualRegister* vreg = ir.getVirtReg(arg);
87 
88 			// Check case when virtual register is in use,
89 			// but it's definition point is not set
90 			context.assertf(vreg.definition.isDefined,
91 				"Virtual register %s, invalid definition (%s)",
92 				arg, vreg.definition);
93 
94 			// How many times 'argUser' is found in vreg.users
95 			uint numVregUses = vreg.users.contains(ir, argUser);
96 
97 			// How many times 'args' is found in instr.args
98 			uint timesUsed = 0;
99 
100 			if (argUser.isInstruction)
101 			{
102 				foreach (i, IrIndex instrArg; ir.getInstr(argUser).args(ir))
103 					if (instrArg == arg)
104 						++timesUsed;
105 			}
106 			else if (argUser.isPhi)
107 			{
108 				foreach(size_t arg_i, ref IrIndex phiArg; ir.getPhi(argUser).args(ir))
109 					if (phiArg == arg)
110 						++timesUsed;
111 			}
112 			else
113 			{
114 				context.internal_error("Virtual register cannot be used by %s", argUser.kind);
115 			}
116 
117 			// For each use of arg by argUser there must one item in users list of vreg and in args list of user
118 			context.assertf(numVregUses == timesUsed,
119 				"Virtual register %s appears %s times as argument of %s, but instruction appears as user %s times",
120 					arg, timesUsed, argUser, numVregUses);
121 		}
122 
123 		void checkResult(IrIndex definition, IrIndex result)
124 		{
125 			if (!result.isVirtReg) return;
126 
127 			IrVirtualRegister* vreg = ir.getVirtReg(result);
128 
129 			// Type must be set for every virtual register
130 			context.assertf(vreg.type.isType,
131 				"Virtual register %s, invalid type (%s)",
132 				result, vreg.type);
133 
134 			// Check that all users of virtual reg point to definition
135 			context.assertf(vreg.definition == definition,
136 				"Virtual register %s definition %s doesn't match instruction %s",
137 				result, vreg.definition, definition);
138 
139 			foreach (IrIndex user, uint numUses; vreg.users.range(ir))
140 				checkArg(user, result);
141 		}
142 
143 		foreach(IrIndex phiIndex, ref IrPhi phi; block.phis(ir))
144 		{
145 			size_t numPhiArgs = 0;
146 			size_t numUniqueArgs = 0; // not an exact count, but precise in [0..2] range
147 			IrIndex uniqueValue;
148 			foreach(size_t arg_i, ref IrIndex phiArg; phi.args(ir))
149 			{
150 				++numPhiArgs;
151 				checkArg(phiIndex, phiArg);
152 
153 				if (phiArg == uniqueValue || phiArg == phi.result) {
154 					continue;
155 				}
156 				// assignment will be done first time when uniqueValue is undefined and phiArg != phi.result
157 				// second time when phiArg != uniqueValue and phiArg != phi.result,
158 				// so, we are looking for numUniqueArgs > 1
159 				uniqueValue = phiArg;
160 				++numUniqueArgs;
161 			}
162 
163 			// check that phi function is not redundant
164 			context.assertf(numUniqueArgs > 1, "%s is redundant", phiIndex);
165 
166 			// TODO: check that all types of args match type of result
167 
168 			// TODO: check correspondense of basic block indices with phi arg indices
169 
170 			// check that phi-function receives values from all predecessors
171 			size_t numPredecessors = 0;
172 			foreach(IrIndex predIndex; block.predecessors.range(ir))
173 			{
174 				context.assertf(predIndex.storageUintIndex < ir.numBasicBlocks, "basic block out of bounds %s", predIndex);
175 				++numPredecessors;
176 			}
177 			context.assertf(numPredecessors == block.predecessors.length,
178 				"Corrupted list of predecessors %s != %s",
179 				numPredecessors, block.predecessors.length);
180 
181 			context.assertf(numPhiArgs == numPredecessors,
182 				"Number of predecessors: %s doesn't match number of phi arguments: %s",
183 				numPredecessors, numPhiArgs);
184 
185 			checkResult(phiIndex, phi.result);
186 			//writefln("phi %s args %s preds %s", phiIndex, numPhiArgs, numPredecessors);
187 		}
188 
189 		foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; block.instructions(ir))
190 		{
191 			foreach (i, IrIndex arg; instrHeader.args(ir))
192 			{
193 				checkArg(instrIndex, arg);
194 			}
195 
196 			if (instrHeader.hasResult)
197 			{
198 				checkResult(instrIndex, instrHeader.result(ir));
199 			}
200 
201 			if (funcInstrInfos[instrHeader.op].isBlockExit)
202 			{
203 				context.assertf(block.lastInstr == instrIndex,
204 					"Basic block %s has %s as last instruction, not branch %s",
205 					blockIndex, block.lastInstr, instrIndex);
206 
207 				context.assertf(ir.nextInstr(instrIndex) == blockIndex,
208 					"Branch %s has %s as next instruction, not basic block %s",
209 					instrIndex, ir.nextInstr(instrIndex), blockIndex);
210 			}
211 
212 			instrValidator(context, ir, instrIndex, instrHeader);
213 		}
214 	}
215 }
216 
217 immutable void function(CompilationContext*, IrFunction*, IrIndex, ref IrInstrHeader)[] instrValidators = [
218 	&validateIrInstruction, // ir
219 	&validateIrInstruction_dummy, // lir_amd64
220 ];
221 
222 // TODO: check all instructions
223 void validateIrInstruction(CompilationContext* c, IrFunction* ir, IrIndex instrIndex, ref IrInstrHeader instrHeader)
224 {
225 	switch(instrHeader.op)
226 	{
227 		case IrOpcode.load:
228 			IrIndex ptr = instrHeader.arg(ir, 0);
229 			IrIndex value = instrHeader.result(ir);
230 			if (ptr.isPhysReg || value.isPhysReg) break;
231 
232 			IrIndex ptrType = getValueType(ptr, ir, c);
233 			c.assertf(ptrType.isTypePointer, "%s: first argument must be pointer, not: %s", instrIndex, ptrType.kind);
234 			IrIndex valueType = getValueType(value, ir, c);
235 			IrIndex baseType = c.types.getPointerBaseType(ptrType);
236 			c.assertf(c.types.isSameType(baseType, valueType), "%s: cannot load %s from %s",
237 				instrIndex, IrIndexDump(valueType, c, ir), IrIndexDump(ptrType, c, ir));
238 			break;
239 
240 		case IrOpcode.store:
241 			IrIndex ptr = instrHeader.arg(ir, 0);
242 			IrIndex value = instrHeader.arg(ir, 1);
243 			if (ptr.isPhysReg || value.isPhysReg) break;
244 
245 			IrIndex ptrType = getValueType(ptr, ir, c);
246 			c.assertf(ptrType.isTypePointer, "%s: first argument must be pointer, not: %s", instrIndex, IrIndexDump(ptrType, c, ir));
247 			IrIndex valueType = getValueType(value, ir, c);
248 			IrIndex baseType = c.types.getPointerBaseType(ptrType);
249 			if (c.types.isSameType(baseType, valueType)) {
250 				break; // ok
251 			} else if (value.isSimpleConstant) {
252 				// constant is stored into memory
253 				if (baseType.isTypeBasic && valueType.typeIndex <= baseType.typeIndex) {
254 					break; // ok. Constant is stored into big enough memory slot
255 				} else if (baseType.isTypePointer) {
256 					break; // ok. Constant is stored into ptr sized slot
257 				}
258 			}
259 			c.internal_error("%s: cannot store %s %s into %s",
260 				instrIndex, IrIndexDump(value, c, ir), IrIndexDump(valueType, c, ir), IrIndexDump(ptrType, c, ir));
261 
262 		case IrOpcode.create_aggregate:
263 			c.assertf(instrHeader.hasResult, "%s: create_aggregate has no result", instrIndex);
264 
265 			IrIndex result = instrHeader.result(ir);
266 			c.assertf(result.isVirtReg, "%s: create_aggregate result is %s. virtualRegister expected", instrIndex, result.kind);
267 
268 			IrVirtualRegister* vreg = ir.getVirtReg(result);
269 			c.assertf(vreg.type.isType, "%s: result type is not a type: %s", instrIndex, vreg.type.kind);
270 
271 			if (vreg.type.isTypeStruct)
272 			{
273 				IrTypeStruct* structType = &c.types.get!IrTypeStruct(vreg.type);
274 				IrTypeStructMember[] structMembers = structType.members;
275 				if (structType.isUnion)
276 				{
277 					c.assertf(instrHeader.numArgs == 2,
278 						"%s: create_aggregate invalid number of arguments for union type, got %s, expected 2",
279 						instrIndex, instrHeader.numArgs);
280 					IrIndex index = instrHeader.arg(ir, 0);
281 					c.assertf(index.isSimpleConstant,
282 						"%s: create_aggregate agr 0 must contain constant index, got %s",
283 						instrIndex, IrIndexDump(index, c, ir));
284 					ulong indexVal = c.constants.get(index).i64;
285 					c.assertf(indexVal < structType.numMembers,
286 						"%s: create_aggregate member index out of bounds, got %s, while union has %s members",
287 						instrIndex, indexVal, structType.numMembers);
288 					IrIndex value = instrHeader.arg(ir, 1);
289 					IrIndex argType = getValueType(value, ir, c);
290 					IrIndex memberType = structMembers[indexVal].type;
291 					bool sameType = c.types.isSameType(argType, memberType);
292 					c.assertf(sameType,
293 						"%s: create_aggregate argument type mismatch of %s member of %s. Expected %s, got %s",
294 						instrIndex, indexVal, IrIndexDump(vreg.type, c, ir), IrIndexDump(memberType, c, ir), IrIndexDump(argType, c, ir));
295 				}
296 				else
297 				{
298 					c.assertf(instrHeader.numArgs == structType.numMembers,
299 						"%s: create_aggregate invalid number of arguments, got %s, expected %s",
300 						instrIndex, instrHeader.numArgs, structType.numMembers);
301 
302 					foreach (i, IrIndex arg; instrHeader.args(ir))
303 					{
304 						IrIndex memberType = structMembers[i].type;
305 						IrIndex argType = getValueType(arg, ir, c);
306 						bool sameType = c.types.isSameType(argType, memberType);
307 						c.assertf(sameType, "%s: create_aggregate type of arg %s mismatch. Expected %s, got %s",
308 							instrIndex, i+1, IrIndexDump(memberType, c, ir), IrIndexDump(argType, c, ir));
309 					}
310 				}
311 			}
312 			else if (vreg.type.isTypeArray)
313 			{
314 				IrTypeArray* arrayType = &c.types.get!IrTypeArray(vreg.type);
315 				c.assertf(instrHeader.numArgs == arrayType.numElements,
316 					"%s: create_aggregate invalid number of arguments, got %s, expected %s",
317 					instrIndex, instrHeader.numArgs, arrayType.numElements);
318 
319 				IrIndex memberType = arrayType.elemType;
320 				foreach (i, IrIndex arg; instrHeader.args(ir))
321 				{
322 					IrIndex argType = getValueType(arg, ir, c);
323 					bool sameType = c.types.isSameType(argType, memberType);
324 					c.assertf(sameType, "%s: create_aggregate type of arg %s mismatch. Expected %s, got %s",
325 						instrIndex, i+1, IrIndexDump(memberType, c, ir), IrIndexDump(argType, c, ir));
326 				}
327 			}
328 			else
329 				c.internal_error("%s: create_aggregate result type must be struct or array, got %s",
330 					instrIndex, vreg.type.kind);
331 			break;
332 
333 		case IrOpcode.insert_element:
334 			c.assertf(instrHeader.hasResult, "%s: insert_element has no result", instrIndex);
335 
336 			IrIndex result = instrHeader.result(ir);
337 			c.assertf(result.isVirtReg, "%s: insert_element result is %s. virtualRegister expected", instrIndex, result.kind);
338 
339 			IrVirtualRegister* vreg = ir.getVirtReg(result);
340 			c.assertf(vreg.type.isType, "%s: result type is not a type: %s", instrIndex, vreg.type.kind);
341 
342 			IrIndex resultType = vreg.type;
343 			c.assertf(resultType.isTypeAggregate, "%s: result must be an aggregate, not: %s", instrIndex, IrIndexDump(resultType, c, ir));
344 
345 			IrIndex aggr = instrHeader.arg(ir, 0);
346 			IrIndex aggrType = getValueType(aggr, ir, c);
347 			c.assertf(aggrType.isTypeAggregate, "%s: first argument must be an aggregate, not: %s", instrIndex, IrIndexDump(aggrType, c, ir));
348 
349 			bool sameType = c.types.isSameType(resultType, aggrType);
350 			c.assertf(sameType, "%s: type of first argument must match result type: result %s, aggregate %s", instrIndex, IrIndexDump(resultType, c, ir), IrIndexDump(aggrType, c, ir));
351 
352 			// TODO: check indices
353 			break;
354 
355 		case IrOpcode.get_element_ptr:
356 			c.assertf(instrHeader.hasResult, "%s: get_element_ptr has no result", instrIndex);
357 			c.assertf(instrHeader.numArgs >= 2, "%s: get_element_ptr must have at least 2 arguments, while has %s",
358 				instrIndex, instrHeader.numArgs);
359 
360 			IrIndex result = instrHeader.result(ir);
361 			c.assertf(result.isVirtReg, "%s: get_element_ptr result is %s. virtualRegister expected", instrIndex, result.kind);
362 
363 			IrVirtualRegister* vreg = ir.getVirtReg(result);
364 			c.assertf(vreg.type.isType, "%s: get_element_ptr result type is not a type: %s", instrIndex, vreg.type.kind);
365 
366 			IrIndex resultType = vreg.type;
367 			c.assertf(resultType.isTypePointer, "%s: get_element_ptr result must be a pointer, not: %s", instrIndex, IrIndexDump(resultType, c, ir));
368 
369 			IrIndex aggrPtr = instrHeader.arg(ir, 0);
370 			IrIndex aggrPtrType = getValueType(aggrPtr, ir, c);
371 			c.assertf(aggrPtrType.isTypePointer, "%s: first argument must be a pointer, not: %s", instrIndex, IrIndexDump(aggrPtrType, c, ir));
372 
373 			// first index indexes pointer itself
374 
375 			IrIndex ptrIndex = instrHeader.arg(ir, 1);
376 			IrIndex ptrIndexType = getValueType(ptrIndex, ir, c);
377 			c.assertf(ptrIndex.isSimpleConstant || ptrIndex.isVirtReg,
378 				"%s: pointer can only be indexed with constant or virtual register, not: %s", instrIndex, ptrIndex.kind);
379 
380 			IrIndex calculatedResultType = c.types.getPointerBaseType(aggrPtrType);
381 
382 			IrIndex[] indices = instrHeader.args(ir)[2..$];
383 
384 			foreach(IrIndex memberIndex; indices)
385 			{
386 				switch(calculatedResultType.typeKind)
387 				{
388 					case IrTypeKind.array:
389 						IrTypeArray* array = &c.types.get!IrTypeArray(calculatedResultType);
390 
391 						if (memberIndex.isSimpleConstant) {
392 							ulong indexVal = c.constants.get(memberIndex).i64;
393 							c.assertf(indexVal < array.numElements,
394 								"%s: indexing element %s of %s-element array",
395 								instrIndex, indexVal, array.numElements);
396 						} else {
397 							c.assertf(memberIndex.isVirtReg,
398 								"%s: arrays can only be indexed with constants or virtual registers, not with %s",
399 								instrIndex, memberIndex);
400 						}
401 
402 						calculatedResultType = array.elemType;
403 						break;
404 
405 					case IrTypeKind.struct_t:
406 						c.assertf(memberIndex.isSimpleConstant,
407 							"%s: structs can only be indexed with constants, not with %s",
408 							instrIndex, memberIndex);
409 
410 						ulong indexVal = c.constants.get(memberIndex).i64;
411 						IrTypeStructMember[] members = c.types.get!IrTypeStruct(calculatedResultType).members;
412 
413 						c.assertf(indexVal < members.length,
414 							"%s: indexing member %s of %s-member struct",
415 							instrIndex, indexVal, members.length);
416 
417 						IrTypeStructMember member = members[indexVal];
418 						calculatedResultType = member.type;
419 						break;
420 
421 					default:
422 						c.internal_error("%s: get_element_ptr cannot index into %s", instrIndex, calculatedResultType.typeKind);
423 				}
424 			}
425 
426 			IrIndex resultBaseType = c.types.getPointerBaseType(resultType);
427 			bool sameType = c.types.isSameType(resultBaseType, calculatedResultType);
428 			c.assertf(sameType, "%s: type of member must match result type: result %s, member %s", instrIndex, IrIndexDump(resultBaseType, c, ir), IrIndexDump(calculatedResultType, c, ir));
429 			break;
430 
431 		default: break;
432 	}
433 }
434 
435 void validateIrInstruction_dummy(CompilationContext* context, IrFunction* ir, IrIndex instrIndex, ref IrInstrHeader instrHeader)
436 {
437 
438 }