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 }