1 /** 2 Copyright: Copyright (c) 2017-2019 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module vox.be.ir_to_lir_amd64; 7 8 import std.stdio; 9 import vox.all; 10 11 12 void pass_ir_to_lir_amd64(CompilationContext* context, IrBuilder* builder, ModuleDeclNode* mod, FunctionDeclNode* func) 13 { 14 assert(!func.isExternal); 15 16 func.backendData.lirData = context.appendAst!IrFunction; 17 IrFunction* lirData = context.getAst!IrFunction(func.backendData.lirData); 18 19 mod.lirModule.addFunction(context, lirData); 20 IrFunction* irData = context.getAst!IrFunction(func.backendData.loweredIrData); 21 22 lirData.name = irData.name; 23 24 builder.beginLir(lirData, irData, context); 25 processFunc(context, builder, mod, irData, lirData); 26 builder.finalizeIr; 27 28 if (context.validateIr) validateIrFunction(context, lirData, "IR -> LIR"); 29 if (context.printLir && context.printDumpOf(func)) dumpFunction(context, lirData, "IR -> LIR"); 30 } 31 32 void processFunc(CompilationContext* context, IrBuilder* builder, ModuleDeclNode* mod, IrFunction* ir, IrFunction* lir) 33 { 34 string funName = context.idString(ir.name); 35 //writefln("IR to LIR %s", funName); 36 lir.instructionSet = IrInstructionSet.lir_amd64; 37 38 // Mirror of original IR, we will put the new IrIndex of copied entities there 39 // and later use this info to rewire all connections between basic blocks 40 IrMirror!IrIndex mirror; 41 mirror.createVirtRegMirror(context, ir); 42 43 enum MAX_ARGS = 16; // only used for physical registers in calls 44 IrIndex[MAX_ARGS] argBuffer = void; 45 46 lir.type = ir.type; 47 48 // copy stack slots 49 lir.stackSlotPtr = ir.stackSlotPtr; 50 lir.numStackSlots = ir.numStackSlots; 51 dupSingleIrStorage(context.irStorage.stackSlotBuffer, lir.stackSlotPtr, lir.numStackSlots); 52 53 // save map from old index to new index 54 void recordIndex(IrIndex oldIndex, IrIndex newIndex) 55 { 56 //writefln("rec %s -> %s", oldIndex, newIndex); 57 assert(oldIndex.isDefined); 58 assert(newIndex.isDefined); 59 assert(oldIndex.isVirtReg); 60 mirror[oldIndex] = newIndex; 61 } 62 63 IrIndex getFixedIndex(IrIndex index) 64 { 65 assert(index.isDefined); 66 assert(!(index.isBasicBlock || index.isPhi || index.isInstruction), format("%s", index)); 67 if (index.isVirtReg) { 68 return mirror[index]; 69 } 70 return index; 71 } 72 73 void fixIndex(ref IrIndex index) 74 { 75 assert(index.isDefined); 76 assert(!(index.isBasicBlock || index.isPhi || index.isInstruction)); 77 if (index.isVirtReg) { 78 //writefln("%s -> %s", index, mirror[index]); 79 assert(mirror[index].isDefined); 80 index = mirror[index]; 81 } 82 } 83 84 // Fixes virtual registers 85 // Legalizes complex arguments into a separate instruction 86 IrIndex getFixedLegalIndex(IrIndex index, IrIndex lirBlockIndex) 87 { 88 final switch (index.kind) with(IrValueKind) { 89 case none, array, instruction, basicBlock, phi, type, variable: context.internal_error("getFixedLegalIndex %s", index.kind); 90 case physicalRegister: 91 case constantAggregate: 92 case constantZero: 93 return index; 94 case constant: 95 final switch(index.constantKind) with(IrConstantKind) { 96 case smallZx, smallSx: return index; 97 case big: 98 auto con = context.constants.get(index); 99 if (con.type.isTypeFloat) break; 100 if (con.type.isTypeInteger && !con.intFitsIn32Bits) break; 101 return index; 102 } 103 goto case; // big constant 104 case global: 105 case stackSlot: 106 case func: 107 // copy to temp register 108 IrIndex type = ir.getValueType(context, index); 109 ExtraInstrArgs extra = { type : type }; 110 return builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, index).result; 111 112 case virtualRegister: 113 return mirror[index]; 114 } 115 } 116 117 void makeMov(IrIndex to, IrIndex from, IrArgSize argSize, IrIndex block) { 118 ExtraInstrArgs extra = { addUsers : false, result : to, argSize : argSize }; 119 builder.emitInstr!(Amd64Opcode.mov)(block, extra, from); 120 } 121 122 IrIndex genAddressOffset(IrIndex lirPtr, uint offset, IrIndex ptrType, IrIndex lirBlockIndex) { 123 IrIndex ptr; 124 if (offset == 0) { 125 ExtraInstrArgs extra = { addUsers : false, type : ptrType }; 126 InstrWithResult movInstr = builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, lirPtr); 127 ptr = movInstr.result; 128 } else { 129 IrIndex offsetIndex = context.constants.add(makeIrType(IrBasicType.i32), offset); 130 ExtraInstrArgs extra = { addUsers : false, type : ptrType }; 131 InstrWithResult addressInstr = builder.emitInstr!(Amd64Opcode.add)(lirBlockIndex, extra, lirPtr, offsetIndex); 132 ptr = addressInstr.result; 133 } 134 return ptr; 135 } 136 137 IrIndex genLoad(IrIndex lirPtr, uint offset, IrIndex ptrType, IrIndex lirBlockIndex) { 138 IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex); 139 IrIndex valType = context.types.getPointerBaseType(ptrType); 140 IrArgSize argSize = typeToIrArgSize(valType, context); 141 ExtraInstrArgs extra = { addUsers : false, type : valType, argSize : argSize }; 142 InstrWithResult instr = builder.emitInstr!(Amd64Opcode.load)(lirBlockIndex, extra, ptr); 143 return instr.result; 144 } 145 146 void genMemZero(IrIndex lirPtr, ulong numBytes, IrIndex lirBlockIndex) { 147 // generate rep stos rax, rcx, rdi 148 IrIndex dataReg = IrIndex(amd64_reg.ax, IrArgSize.size64); 149 IrIndex sizeReg = IrIndex(amd64_reg.cx, IrArgSize.size64); 150 IrIndex ptrReg = IrIndex(amd64_reg.di, IrArgSize.size64); 151 // data 152 makeMov(dataReg, context.constants.addZeroConstant(makeIrType(IrBasicType.i64)), IrArgSize.size64, lirBlockIndex); 153 // size 154 makeMov(sizeReg, context.constants.add(makeIrType(IrBasicType.i64), numBytes), IrArgSize.size64, lirBlockIndex); 155 // ptr 156 makeMov(ptrReg, lirPtr, IrArgSize.size64, lirBlockIndex); 157 158 ExtraInstrArgs extra = { addUsers : false }; 159 builder.emitInstr!(Amd64Opcode.rep_stos)(lirBlockIndex, extra, dataReg, sizeReg, ptrReg); 160 } 161 162 // fromOffset is used when irValue is pointer that needs deferencing 163 void genStore(IrIndex lirPtr, uint offset, IrIndex irValue, uint fromOffset, IrIndex valueType, IrIndex lirBlockIndex, IrFunction* ir) 164 { 165 //writefln("genStore %s %s %s %s %s", lirPtr, offset, irValue, fromOffset, IrIndexDump(valueType, context, ir)); 166 IrIndex dstType = getValueType(lirPtr, lir, context); 167 IrIndex srcType = getValueType(irValue, ir, context); 168 //writefln(" %s %s <- %s %s", IrTypeDump(dstType, *context), dstType, IrTypeDump(srcType, *context), srcType); 169 context.assertf(dstType.isTypePointer, "%s", IrIndexDump(dstType, context, lir)); 170 IrIndex ptrType = context.types.appendPtr(valueType); 171 172 switch(valueType.typeKind) with(IrTypeKind) { 173 case basic, pointer: 174 IrArgSize argSize = typeToIrArgSize(valueType, context); 175 176 // check if irValue is l-value 177 if (context.types.isSameType(dstType, srcType)) { 178 // load from irValue ptr. loadedVal is already fixed 179 IrIndex loadedVal = genLoad(getFixedIndex(irValue), fromOffset, ptrType, lirBlockIndex); 180 181 IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex); 182 ExtraInstrArgs extra = { addUsers : false, argSize : argSize }; 183 builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, extra, ptr, loadedVal); 184 break; 185 } 186 187 IrIndex rvalue = getFixedLegalIndex(irValue, lirBlockIndex); 188 ExtraInstrArgs extra = { addUsers : false, argSize : argSize }; 189 IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex); 190 builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, extra, ptr, rvalue); 191 break; 192 193 case struct_t: 194 IrTypeStruct* structType = &context.types.get!IrTypeStruct(valueType); 195 IrIndex[] members; 196 197 switch(irValue.kind) with(IrValueKind) 198 { 199 case virtualRegister: 200 IrIndex origin = ir.getVirtReg(irValue).definition; 201 IrInstrHeader* instr = ir.getInstr(origin); 202 203 switch (instr.op) 204 { 205 case IrOpcode.load_aggregate, IrOpcode.load: 206 foreach (i, IrTypeStructMember member; structType.members) 207 { 208 IrIndex ptr = genAddressOffset(lirPtr, offset + member.offset, ptrType, lirBlockIndex); 209 genStore(ptr, 0, instr.arg(ir, 0), fromOffset + member.offset, member.type, lirBlockIndex, ir); 210 } 211 return; 212 213 case IrOpcode.create_aggregate: 214 members = instr.args(ir); 215 break; 216 217 case IrOpcode.call: 218 // register fixup for return stack slot 219 IrIndex resultSlot = getFixedIndex(irValue); 220 //context.internal_error("call . todo %s", resultSlot); 221 foreach (i, IrTypeStructMember member; structType.members) 222 { 223 IrIndex ptr = genAddressOffset(lirPtr, offset + member.offset, ptrType, lirBlockIndex); 224 genStore(ptr, 0, resultSlot, fromOffset + member.offset, member.type, lirBlockIndex, ir); 225 } 226 return; 227 228 case IrOpcode.move, IrOpcode.or: 229 // value fits into register, store as is 230 IrIndex rvalue = getFixedIndex(irValue); 231 IrArgSize argSize = typeToIrArgSize(valueType, context); 232 ExtraInstrArgs extra = { addUsers : false, argSize : argSize }; 233 IrIndex ptr = genAddressOffset(lirPtr, offset, ptrType, lirBlockIndex); 234 builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, extra, ptr, rvalue); 235 return; 236 237 default: 238 context.internal_error("%s", cast(IrOpcode)instr.op); 239 } 240 break; 241 242 case constantZero: 243 IrIndex ptr = genAddressOffset(lirPtr, offset, context.i8PtrType, lirBlockIndex); 244 genMemZero(ptr, structType.sizealign.size, lirBlockIndex); 245 return; 246 247 case constantAggregate: 248 members = context.constants.getAggregate(irValue).members; 249 break; 250 251 default: context.internal_error("%s", irValue.kind); 252 } 253 254 context.assertf(members.length == structType.numMembers, "%s != %s", members.length, structType.numMembers); 255 256 foreach (i, IrTypeStructMember member; structType.members) 257 { 258 genStore(lirPtr, offset + member.offset, members[i], fromOffset, member.type, lirBlockIndex, ir); 259 } 260 break; 261 262 case array: 263 IrTypeArray* arrayType = &context.types.get!IrTypeArray(valueType); 264 uint elemSize = context.types.typeSize(arrayType.elemType); 265 IrIndex[] members; 266 267 switch(irValue.kind) with(IrValueKind) 268 { 269 case virtualRegister: 270 IrIndex origin = ir.getVirtReg(irValue).definition; 271 IrInstrHeader* instr = ir.getInstr(origin); 272 273 switch (instr.op) 274 { 275 case IrOpcode.load_aggregate, IrOpcode.load: 276 foreach (i; 0..arrayType.numElements) 277 { 278 IrIndex ptr = genAddressOffset(lirPtr, offset + elemSize * i, ptrType, lirBlockIndex); 279 genStore(ptr, 0, instr.arg(ir, 0), fromOffset + elemSize * i, arrayType.elemType, lirBlockIndex, ir); 280 } 281 return; 282 283 case IrOpcode.create_aggregate: 284 members = instr.args(ir); 285 break; 286 287 case IrOpcode.call: 288 // register fixup for return stack slot 289 IrIndex resultSlot = getFixedIndex(irValue); 290 //context.internal_error("call . todo %s", resultSlot); 291 foreach (i; 0..arrayType.numElements) 292 { 293 IrIndex ptr = genAddressOffset(lirPtr, offset + elemSize * i, ptrType, lirBlockIndex); 294 genStore(ptr, 0, resultSlot, fromOffset + elemSize * i, arrayType.elemType, lirBlockIndex, ir); 295 } 296 return; 297 298 default: 299 context.internal_error("%s", cast(IrOpcode)instr.op); 300 } 301 break; 302 303 case constantZero: 304 IrIndex ptr = genAddressOffset(lirPtr, offset, context.i8PtrType, lirBlockIndex); 305 genMemZero(ptr, elemSize * arrayType.numElements, lirBlockIndex); 306 return; 307 308 case constantAggregate: 309 members = context.constants.getAggregate(irValue).members; 310 break; 311 312 default: context.internal_error("%s", irValue.kind); 313 } 314 315 context.assertf(members.length == arrayType.numElements, "%s != %s", members.length, arrayType.numElements); 316 317 foreach (i; 0..arrayType.numElements) 318 { 319 genStore(lirPtr, offset + elemSize * i, members[i], fromOffset, arrayType.elemType, lirBlockIndex, ir); 320 } 321 break; 322 323 default: 324 context.internal_error("%s", valueType.typeKind); 325 } 326 } 327 328 // copy single integer 329 void genCopyInt(IrIndex dst, IrIndex src, IrIndex valType, IrArgSize argSize, IrIndex lirBlockIndex) 330 { 331 ExtraInstrArgs loadExtra = { addUsers : false, type : valType, argSize : argSize }; 332 InstrWithResult loadInstr = builder.emitInstr!(Amd64Opcode.load)(lirBlockIndex, loadExtra, src); 333 ExtraInstrArgs storeExtra = { addUsers : false, argSize : argSize }; 334 IrIndex storeInstr = builder.emitInstr!(Amd64Opcode.store)(lirBlockIndex, storeExtra, dst, loadInstr.result); 335 } 336 337 // copy single item of any type 338 void genCopy(IrIndex dst, IrIndex src, IrIndex lirBlockIndex) 339 { 340 //writefln("genCopy %s %s", dst, src); 341 IrIndex ptrType = getValueType(dst, lir, context); 342 //writefln("ptrType %s %s", ptrType, getValueType(src, lir, context)); 343 IrIndex valType = context.types.getPointerBaseType(ptrType); 344 uint typeSize = context.types.typeSize(valType); 345 346 uint offset = 0; 347 IrIndex elemType = makeIrType(IrBasicType.i64); // i64, i32, i16, i8 348 IrArgSize elemArgSize = IrArgSize.size64; // size64, size32, size16, size8 349 uint elemSize = 8; // 8, 4, 2, 1 350 while (elemSize > 0) 351 { 352 IrIndex elemPtrType = context.types.appendPtr(elemType); 353 while (typeSize >= elemSize) 354 { 355 IrIndex ptrFrom = genAddressOffset(src, offset, elemPtrType, lirBlockIndex); 356 IrIndex ptrTo = genAddressOffset(dst, offset, elemPtrType, lirBlockIndex); 357 //writefln("from %s to %s", ptrFrom, ptrTo); 358 genCopyInt(ptrTo, ptrFrom, elemType, elemArgSize, lirBlockIndex); 359 360 offset += elemSize; 361 typeSize -= elemSize; 362 } 363 elemSize /= 2; 364 elemType.typeIndex = elemType.typeIndex - 1; 365 --elemArgSize; 366 } 367 } 368 369 // dup basic blocks 370 // old and new blocks have the same indices 371 foreach (size_t i, ref IrBasicBlock irBlock; ir.blocksArray) 372 { 373 IrIndex blockIndex = IrIndex(cast(uint)i, IrValueKind.basicBlock); 374 IrIndex lirBlock = builder.appendBasicBlockSlot; 375 376 lir.getBlock(lirBlock).isLoopHeader = irBlock.isLoopHeader; 377 378 foreach(IrIndex pred; irBlock.predecessors.range(ir)) { 379 lir.getBlock(lirBlock).predecessors.append(builder, pred); 380 } 381 foreach(IrIndex succ; irBlock.successors.range(ir)) { 382 lir.getBlock(lirBlock).successors.append(builder, succ); 383 } 384 385 lir.getBlock(lirBlock).prevBlock = irBlock.prevBlock; 386 lir.getBlock(lirBlock).nextBlock = irBlock.nextBlock; 387 388 // Add phis with old args 389 foreach(IrIndex phiIndex, ref IrPhi phi; irBlock.phis(ir)) 390 { 391 IrVirtualRegister* oldReg = ir.getVirtReg(phi.result); 392 IrIndex newPhi = builder.addPhi(lirBlock, oldReg.type, phi.var); 393 IrIndex newResult = lir.getPhi(newPhi).result; 394 IrVirtualRegister* newReg = lir.getVirtReg(newResult); 395 newReg.type = oldReg.type; 396 397 recordIndex(phi.result, lir.getPhi(newPhi).result); 398 foreach(size_t arg_i, ref IrIndex phiArg; phi.args(ir)) 399 { 400 builder.addPhiArg(newPhi, phiArg); 401 } 402 } 403 } 404 405 //writefln("%s", IrIndexDump(lir.type, context, lir)); 406 407 // fix successors predecessors links 408 foreach (IrIndex lirBlockIndex, ref IrBasicBlock lirBlock; lir.blocks) 409 { 410 lirBlock.isSealed = true; 411 lirBlock.isFinished = true; 412 413 // old and new blocks have the same indices 414 IrIndex irBlockIndex = lirBlockIndex; 415 416 // Add instructions with old args 417 foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; ir.getBlock(irBlockIndex).instructions(ir)) 418 { 419 void emitLirInstr(alias I)() 420 { 421 static assert(!getInstrInfo!I.hasVariadicArgs); 422 static assert(!getInstrInfo!I.hasVariadicResult); 423 424 IrIndex[getInstrInfo!I.numArgs] fixedArgs = instrHeader.args(ir); 425 foreach(ref arg; fixedArgs) arg = getFixedLegalIndex(arg, lirBlockIndex); 426 427 static if (getInstrInfo!I.hasResult) 428 { 429 ExtraInstrArgs extra = { addUsers : false, argSize : instrHeader.argSize }; 430 IrIndex result = instrHeader.result(ir); 431 if (result.isVirtReg) 432 extra.type = ir.getVirtReg(result).type; 433 InstrWithResult res = builder.emitInstr!I(lirBlockIndex, extra, fixedArgs); 434 recordIndex(result, res.result); 435 } 436 else 437 { 438 ExtraInstrArgs extra = { addUsers : false, argSize : instrHeader.argSize }; 439 IrIndex res = builder.emitInstr!I(lirBlockIndex, extra, fixedArgs); 440 } 441 } 442 443 // for movsx/movzx 444 // arg is not fixed 445 IrIndex emit_nonfinal(alias I)(IrIndex resType, IrIndex arg) { 446 arg = getFixedLegalIndex(arg, lirBlockIndex); 447 ExtraInstrArgs extra = { addUsers : false, type : resType }; 448 return builder.emitInstr!I(lirBlockIndex, extra, arg).result; 449 } 450 451 IrIndex legalizeFloatArg(IrIndex arg, IrIndex type) { 452 switch (arg.kind) { 453 case IrValueKind.constant: 454 IrIndex globalIndex = context.globals.add(); 455 IrGlobal* global = context.globals.get(globalIndex); 456 global.type = context.types.appendPtr(type); 457 458 ObjectSymbol sym = { 459 kind : ObjectSymbolKind.isLocal, 460 sectionIndex : context.builtinSections[ObjectSectionType.ro_data], 461 moduleIndex : mod.objectSymIndex, 462 flags : ObjectSymbolFlags.isFloat, 463 id : context.idMap.getOrReg(context, ":float"), 464 }; 465 global.objectSymIndex = context.objSymTab.addSymbol(sym); 466 467 ObjectSymbol* globalSym = context.objSymTab.getSymbol(global.objectSymIndex); 468 469 SizeAndAlignment valueSizealign = context.types.typeSizeAndAlignment(type); 470 ubyte[] buffer = context.globals.allocateInitializer(valueSizealign.size); 471 constantToMem(buffer, arg, context); 472 //writefln("%s %s", arg, buffer); 473 globalSym.setInitializer(buffer); 474 475 ExtraInstrArgs extra = { addUsers : false, type : type, argSize : instrHeader.argSize }; 476 return builder.emitInstr!(Amd64Opcode.load)(lirBlockIndex, extra, globalIndex).result; 477 478 case IrValueKind.constantZero: 479 ExtraInstrArgs extra = { addUsers : false, type : type, argSize : instrHeader.argSize }; 480 return builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, arg).result; 481 482 default: return getFixedLegalIndex(arg, lirBlockIndex); 483 } 484 } 485 486 // args are fixed 487 void emit_final(alias I)(IrIndex resType, IrIndex[] args...) { 488 ExtraInstrArgs extra = { addUsers : false, type : resType, argSize : instrHeader.argSize }; 489 InstrWithResult res = builder.emitInstr!I(lirBlockIndex, extra, args); 490 IrIndex result = instrHeader.result(ir); 491 recordIndex(result, res.result); 492 } 493 494 switch_instr: 495 switch(instrHeader.op) 496 { 497 case IrOpcode.call: 498 lir.numCalls += 1; 499 foreach(i; 0..instrHeader.numArgs) { 500 argBuffer[i] = getFixedIndex(instrHeader.arg(ir, i)); 501 } 502 IrIndex returnReg; 503 if (instrHeader.hasResult) returnReg = instrHeader.result(ir); 504 ExtraInstrArgs callExtra = { 505 addUsers : false, 506 hasResult : instrHeader.hasResult, 507 result : returnReg // will be used if function has result 508 }; 509 InstrWithResult callInstr = builder.emitInstr!(Amd64Opcode.call)( 510 lirBlockIndex, callExtra, argBuffer[0..instrHeader.numArgs]); 511 lir.getInstr(callInstr.instruction).extendFixedArgRange = instrHeader.extendFixedArgRange; 512 lir.getInstr(callInstr.instruction).extendFixedResultRange = instrHeader.extendFixedResultRange; 513 break; 514 515 case IrOpcode.syscall: 516 lir.numCalls += 1; 517 foreach(i; 0..instrHeader.numArgs) { 518 argBuffer[i] = getFixedIndex(instrHeader.arg(ir, i)); 519 } 520 IrIndex returnReg; 521 if (instrHeader.hasResult) returnReg = instrHeader.result(ir); 522 ExtraInstrArgs callExtra = { 523 addUsers : false, 524 hasResult : instrHeader.hasResult, 525 result : returnReg // will be used if function has result 526 }; 527 InstrWithResult callInstr = builder.emitInstr!(Amd64Opcode.syscall)( 528 lirBlockIndex, callExtra, argBuffer[0..instrHeader.numArgs]); 529 lir.getInstr(callInstr.instruction).extendFixedArgRange = instrHeader.extendFixedArgRange; 530 lir.getInstr(callInstr.instruction).extendFixedResultRange = instrHeader.extendFixedResultRange; 531 break; 532 533 case IrOpcode.add: emitLirInstr!(Amd64Opcode.add); break; 534 case IrOpcode.sub: emitLirInstr!(Amd64Opcode.sub); break; 535 case IrOpcode.umul, IrOpcode.smul: emitLirInstr!(Amd64Opcode.imul); break; 536 case IrOpcode.not: emitLirInstr!(Amd64Opcode.not); break; 537 case IrOpcode.neg: emitLirInstr!(Amd64Opcode.neg); break; 538 case IrOpcode.fneg: emitLirInstr!(Amd64Opcode.fneg); break; 539 case IrOpcode.and: emitLirInstr!(Amd64Opcode.and); break; 540 case IrOpcode.or: emitLirInstr!(Amd64Opcode.or); break; 541 case IrOpcode.xor: emitLirInstr!(Amd64Opcode.xor); break; 542 543 case IrOpcode.fadd: 544 IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type; 545 emit_final!(Amd64Opcode.fadd)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type)); 546 break; 547 case IrOpcode.fsub: 548 IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type; 549 emit_final!(Amd64Opcode.fsub)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type)); 550 break; 551 case IrOpcode.fmul: 552 IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type; 553 emit_final!(Amd64Opcode.fmul)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type)); 554 break; 555 case IrOpcode.fdiv: 556 IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type; 557 emit_final!(Amd64Opcode.fdiv)(type, legalizeFloatArg(instrHeader.arg(ir, 0), type), legalizeFloatArg(instrHeader.arg(ir, 1), type)); 558 break; 559 560 case IrOpcode.udiv, IrOpcode.sdiv, IrOpcode.urem, IrOpcode.srem: 561 // v1 = div v2, v3 562 // is converted into: 563 // mov ax, v2 564 // zx/sx dx:ax 565 // ax = div ax, dx, v3 566 // mov v1, ax 567 // since 8 bit division doesn't follow dx:ax pattern and uses ax instead, we use bigger division there 568 569 bool isSigned = instrHeader.op == IrOpcode.sdiv || instrHeader.op == IrOpcode.srem; 570 bool isDivision = instrHeader.op == IrOpcode.udiv || instrHeader.op == IrOpcode.sdiv; 571 572 IrIndex dividendBottom; 573 574 IrArgSize argSize = instrHeader.argSize; 575 switch(argSize) with(IrArgSize) 576 { 577 case size8: 578 // we transform 8 bit div/rem into 16 bit, so we don't need to deal with ah register 579 argSize = size16; 580 dividendBottom = IrIndex(amd64_reg.ax, argSize); 581 ExtraInstrArgs extra = { addUsers : false, result : dividendBottom }; 582 if (isSigned) builder.emitInstr!(Amd64Opcode.movsx_btod)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0))); 583 else builder.emitInstr!(Amd64Opcode.movzx_btod)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0))); 584 break; 585 case size16: 586 case size32: 587 case size64: 588 // copy bottom half of dividend 589 dividendBottom = IrIndex(amd64_reg.ax, argSize); 590 makeMov(dividendBottom, getFixedIndex(instrHeader.arg(ir, 0)), argSize, lirBlockIndex); 591 break; 592 default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, argSize); 593 } 594 595 596 // divisor must be in register 597 IrIndex divisor = instrHeader.arg(ir, 1); 598 if (divisor.isSimpleConstant) { 599 auto con = context.constants.get(divisor); 600 if (instrHeader.argSize == IrArgSize.size8) { 601 divisor = context.constants.add(makeIrType(IrBasicType.i16), con.i16); 602 } 603 ExtraInstrArgs extra = { addUsers : false, type : getValueType(divisor, ir, context) }; 604 divisor = builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra, divisor).result; 605 } 606 else 607 { 608 fixIndex(divisor); 609 if (instrHeader.argSize == IrArgSize.size8) { 610 ExtraInstrArgs extra = { addUsers : false, type : makeIrType(IrBasicType.i16) }; 611 if (isSigned) divisor = builder.emitInstr!(Amd64Opcode.movsx_btod)(lirBlockIndex, extra, divisor).result; 612 else divisor = builder.emitInstr!(Amd64Opcode.movzx_btod)(lirBlockIndex, extra, divisor).result; 613 } 614 } 615 616 IrIndex dividendTop = IrIndex(amd64_reg.dx, argSize); 617 618 if (isSigned) { 619 IrIndex divsxResult = IrIndex(amd64_reg.dx, argSize); 620 // sign-extend top half of dividend 621 ExtraInstrArgs extra2 = { argSize : argSize, result : divsxResult }; 622 builder.emitInstr!(Amd64Opcode.divsx)(lirBlockIndex, extra2); 623 } else { 624 // zero top half of dividend 625 makeMov(dividendTop, context.constants.addZeroConstant(makeIrType(IrBasicType.i32)), IrArgSize.size32, lirBlockIndex); 626 } 627 628 // choose result 629 IrIndex resultReg = dividendTop; // remainder 630 if (isDivision) { 631 resultReg = dividendBottom; // dividend 632 } 633 634 // divide 635 ExtraInstrArgs extra3 = { addUsers : false, argSize : argSize, result : resultReg }; 636 InstrWithResult res; 637 if (isSigned) 638 res = builder.emitInstr!(Amd64Opcode.idiv)(lirBlockIndex, extra3, dividendBottom, dividendTop, divisor); 639 else 640 res = builder.emitInstr!(Amd64Opcode.div)(lirBlockIndex, extra3, dividendBottom, dividendTop, divisor); 641 642 // fix size for size8 643 resultReg.physRegSize = instrHeader.argSize; 644 645 // copy result (quotient) with truncation in case of 8/16 bits 646 ExtraInstrArgs extra4 = { addUsers : false, argSize : instrHeader.argSize, type : ir.getVirtReg(instrHeader.result(ir)).type }; 647 InstrWithResult movResult = builder.emitInstr!(Amd64Opcode.mov)(lirBlockIndex, extra4, resultReg); 648 recordIndex(instrHeader.result(ir), movResult.result); 649 break; 650 651 case IrOpcode.shl, IrOpcode.lshr, IrOpcode.ashr: 652 IrIndex rightArg; 653 if (instrHeader.arg(ir, 1).isSimpleConstant) 654 rightArg = instrHeader.arg(ir, 1); 655 else 656 { 657 rightArg = IrIndex(amd64_reg.cx, IrArgSize.size8); 658 makeMov(rightArg, getFixedIndex(instrHeader.arg(ir, 1)), instrHeader.argSize, lirBlockIndex); 659 } 660 IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type; 661 ExtraInstrArgs extra = { addUsers : false, argSize : instrHeader.argSize, type : type }; 662 InstrWithResult res; 663 switch(instrHeader.op) { 664 case IrOpcode.shl: 665 res = builder.emitInstr!(Amd64Opcode.shl)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), rightArg); 666 break; 667 case IrOpcode.lshr: 668 res = builder.emitInstr!(Amd64Opcode.shr)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), rightArg); 669 break; 670 case IrOpcode.ashr: 671 res = builder.emitInstr!(Amd64Opcode.sar)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), rightArg); 672 break; 673 default: assert(false); 674 } 675 recordIndex(instrHeader.result(ir), res.result); 676 break; 677 case IrOpcode.grow_stack: 678 IrIndex stackPtrReg = IrIndex(lir.getCallConv(context).stackPointer, IrArgSize.size64); 679 ExtraInstrArgs extra = { addUsers : false, result : stackPtrReg }; 680 builder.emitInstr!(Amd64Opcode.sub)( 681 lirBlockIndex, extra, stackPtrReg, getFixedIndex(instrHeader.arg(ir, 0))); 682 break; 683 case IrOpcode.shrink_stack: 684 IrIndex stackPtrReg = IrIndex(lir.getCallConv(context).stackPointer, IrArgSize.size64); 685 ExtraInstrArgs extra = { addUsers : false, result : stackPtrReg }; 686 builder.emitInstr!(Amd64Opcode.add)( 687 lirBlockIndex, extra, stackPtrReg, getFixedIndex(instrHeader.arg(ir, 0))); 688 break; 689 case IrOpcode.push: 690 IrIndex fixedArg = getFixedLegalIndex(instrHeader.arg(ir, 0), lirBlockIndex); 691 ExtraInstrArgs extra = { addUsers : false }; 692 builder.emitInstr!(Amd64Opcode.push)(lirBlockIndex, extra, fixedArg); 693 break; 694 case IrOpcode.move: 695 if (instrHeader.result(ir).isVirtReg) 696 emitLirInstr!(Amd64Opcode.mov); 697 else 698 makeMov(instrHeader.result(ir), getFixedIndex(instrHeader.arg(ir, 0)), instrHeader.argSize, lirBlockIndex); 699 break; 700 case IrOpcode.copy: 701 genCopy(getFixedIndex(instrHeader.arg(ir, 0)), getFixedIndex(instrHeader.arg(ir, 1)), lirBlockIndex); 702 break; 703 case IrOpcode.load: emitLirInstr!(Amd64Opcode.load); break; 704 case IrOpcode.store: 705 IrIndex srcType = ir.getValueType(context, instrHeader.arg(ir, 1)); 706 genStore(getFixedIndex(instrHeader.arg(ir, 0)), 0, instrHeader.arg(ir, 1), 0, srcType, lirBlockIndex, ir); 707 break; 708 case IrOpcode.conv: 709 // Incomplete implementation 710 IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context); 711 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 712 uint typeSizeFrom = context.types.typeSize(typeFrom); 713 uint typeSizeTo = context.types.typeSize(typeTo); 714 context.assertf(typeSizeTo <= typeSizeFrom, 715 "Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo); 716 emitLirInstr!(Amd64Opcode.mov); 717 break; 718 case IrOpcode.sext: 719 IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context); 720 IrArgSize sizeFrom = getTypeArgSize(typeFrom, context); 721 IrArgSize sizeTo = instrHeader.argSize; 722 final switch(sizeFrom) with(IrArgSize) 723 { 724 case size8: 725 switch(sizeTo) with(IrArgSize) 726 { 727 case size16: emitLirInstr!(Amd64Opcode.movsx_btow); break; 728 case size32: emitLirInstr!(Amd64Opcode.movsx_btod); break; 729 case size64: emitLirInstr!(Amd64Opcode.movsx_btoq); break; 730 default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo); 731 } 732 break; 733 case size16: 734 switch(sizeTo) with(IrArgSize) 735 { 736 case size32: emitLirInstr!(Amd64Opcode.movsx_wtod); break; 737 case size64: emitLirInstr!(Amd64Opcode.movsx_wtoq); break; 738 default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo); 739 } 740 break; 741 case size32: 742 context.assertf(sizeTo == size64, "%s:%s: Invalid target size %s", funName, instrIndex, sizeTo); 743 emitLirInstr!(Amd64Opcode.movsx_dtoq); 744 break; 745 case size64, size128, size256, size512: context.internal_error("%s:%s: Invalid source size %s", funName, instrIndex, sizeFrom); 746 } 747 break; 748 case IrOpcode.zext: 749 IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context); 750 IrArgSize sizeFrom = getTypeArgSize(typeFrom, context); 751 IrArgSize sizeTo = instrHeader.argSize; 752 final switch(sizeFrom) with(IrArgSize) 753 { 754 case size8: 755 switch(sizeTo) with(IrArgSize) 756 { 757 case size16: emitLirInstr!(Amd64Opcode.movzx_btow); break; 758 case size32: emitLirInstr!(Amd64Opcode.movzx_btod); break; 759 case size64: emitLirInstr!(Amd64Opcode.movzx_btoq); break; // TODO use movzx_btod 760 default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo); 761 } 762 break; 763 case size16: 764 switch(sizeTo) with(IrArgSize) 765 { 766 case size32: emitLirInstr!(Amd64Opcode.movzx_wtod); break; 767 case size64: emitLirInstr!(Amd64Opcode.movzx_wtoq); break; // TODO use movzx_wtod 768 default: context.internal_error("%s:%s: Invalid target size %s", funName, instrIndex, sizeTo); 769 } 770 break; 771 case size32: 772 context.assertf(sizeTo == size64, "Invalid target size %s", sizeTo); 773 emitLirInstr!(Amd64Opcode.mov); 774 break; 775 case size64, size128, size256, size512: context.internal_error("%s:%s: Invalid source size %s", funName, instrIndex, sizeFrom); 776 } 777 break; 778 case IrOpcode.trunc: 779 IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context); 780 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 781 uint typeSizeFrom = context.types.typeSize(typeFrom); 782 uint typeSizeTo = context.types.typeSize(typeTo); 783 context.assertf(typeSizeTo < typeSizeFrom, 784 "Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo); 785 emitLirInstr!(Amd64Opcode.mov); 786 break; 787 788 case IrOpcode.fpext: 789 IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context); 790 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 791 uint typeSizeFrom = context.types.typeSize(typeFrom); 792 uint typeSizeTo = context.types.typeSize(typeTo); 793 context.assertf(typeSizeTo > typeSizeFrom, 794 "Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo); 795 emitLirInstr!(Amd64Opcode.f32_to_f64); 796 break; 797 798 case IrOpcode.fptrunc: 799 IrIndex typeFrom = getValueType(instrHeader.arg(ir, 0), ir, context); 800 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 801 uint typeSizeFrom = context.types.typeSize(typeFrom); 802 uint typeSizeTo = context.types.typeSize(typeTo); 803 context.assertf(typeSizeTo < typeSizeFrom, 804 "Can't cast from %s bytes to %s bytes", typeSizeFrom, typeSizeTo); 805 emitLirInstr!(Amd64Opcode.f64_to_f32); 806 break; 807 808 case IrOpcode.uitofp: 809 IrIndex arg0 = instrHeader.arg(ir, 0); 810 IrIndex typeFrom = getValueType(arg0, ir, context); 811 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 812 uint typeSizeFrom = context.types.typeSize(typeFrom); 813 uint typeSizeTo = context.types.typeSize(typeTo); 814 815 switch(typeSizeTo) { 816 case 4: 817 switch(typeSizeFrom) { 818 case 1: 819 IrIndex u32 = emit_nonfinal!(Amd64Opcode.movzx_btod)(makeIrType(IrBasicType.i32), arg0); 820 emit_final!(Amd64Opcode.i32_to_f32)(typeTo, u32); 821 break switch_instr; 822 case 2: 823 IrIndex u32 = emit_nonfinal!(Amd64Opcode.movzx_wtod)(makeIrType(IrBasicType.i32), arg0); 824 emit_final!(Amd64Opcode.i32_to_f32)(typeTo, u32); 825 break switch_instr; 826 case 4: 827 IrIndex i64 = emit_nonfinal!(Amd64Opcode.mov)(makeIrType(IrBasicType.i64), arg0); 828 emit_final!(Amd64Opcode.i64_to_f32)(typeTo, i64); 829 break switch_instr; 830 case 8: emitLirInstr!(Amd64Opcode.i64_to_f32); break switch_instr; // u64 -> f32 831 default: context.internal_error("Unexpected source type size %s", typeSizeFrom); 832 } 833 834 case 8: 835 switch(typeSizeFrom) { 836 case 1: 837 IrIndex i32 = emit_nonfinal!(Amd64Opcode.movzx_btod)(makeIrType(IrBasicType.i32), arg0); 838 emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32); 839 break switch_instr; 840 case 2: 841 IrIndex i32 = emit_nonfinal!(Amd64Opcode.movzx_wtod)(makeIrType(IrBasicType.i32), arg0); 842 emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32); 843 break switch_instr; 844 case 4: 845 IrIndex i64 = emit_nonfinal!(Amd64Opcode.mov)(makeIrType(IrBasicType.i64), arg0); 846 emit_final!(Amd64Opcode.i64_to_f64)(typeTo, i64); 847 break switch_instr; 848 case 8: emitLirInstr!(Amd64Opcode.i64_to_f64); break switch_instr; // u64 -> f64 849 default: context.internal_error("Unexpected source type size %s", typeSizeFrom); 850 } 851 default: context.internal_error("Unexpected target type size %s", typeSizeTo); 852 } 853 854 case IrOpcode.sitofp: 855 IrIndex arg0 = instrHeader.arg(ir, 0); 856 IrIndex typeFrom = getValueType(arg0, ir, context); 857 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 858 uint typeSizeFrom = context.types.typeSize(typeFrom); 859 uint typeSizeTo = context.types.typeSize(typeTo); 860 861 switch(typeSizeTo) { 862 case 4: 863 switch(typeSizeFrom) { 864 case 1: 865 IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_btod)(makeIrType(IrBasicType.i32), arg0); 866 emit_final!(Amd64Opcode.i32_to_f32)(typeTo, i32); 867 break switch_instr; 868 case 2: 869 IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_wtod)(makeIrType(IrBasicType.i32), arg0); 870 emit_final!(Amd64Opcode.i32_to_f32)(typeTo, i32); 871 break switch_instr; 872 case 4: emitLirInstr!(Amd64Opcode.i32_to_f32); break switch_instr; 873 case 8: emitLirInstr!(Amd64Opcode.i64_to_f32); break switch_instr; 874 default: context.internal_error("Unexpected source type size %s", typeSizeFrom); 875 } 876 877 case 8: 878 switch(typeSizeFrom) { 879 case 1: 880 IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_btod)(makeIrType(IrBasicType.i32), arg0); 881 emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32); 882 break switch_instr; 883 case 2: 884 IrIndex i32 = emit_nonfinal!(Amd64Opcode.movsx_wtod)(makeIrType(IrBasicType.i32), arg0); 885 emit_final!(Amd64Opcode.i32_to_f64)(typeTo, i32); 886 break switch_instr; 887 case 4: emitLirInstr!(Amd64Opcode.i32_to_f64); break switch_instr; 888 case 8: emitLirInstr!(Amd64Opcode.i64_to_f64); break switch_instr; 889 default: context.internal_error("Unexpected source type size %s", typeSizeFrom); 890 } 891 default: context.internal_error("Unexpected target type size %s", typeSizeTo); 892 } 893 894 case IrOpcode.fptoui: 895 IrIndex arg0 = instrHeader.arg(ir, 0); 896 IrIndex typeFrom = getValueType(arg0, ir, context); 897 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 898 uint typeSizeFrom = context.types.typeSize(typeFrom); 899 uint typeSizeTo = context.types.typeSize(typeTo); 900 901 switch(typeSizeFrom) { 902 case 4: 903 switch(typeSizeTo) { 904 case 1: 905 case 2: 906 IrIndex i32 = emit_nonfinal!(Amd64Opcode.f32_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0); 907 emit_final!(Amd64Opcode.mov)(typeTo, i32); 908 break switch_instr; 909 case 4: emitLirInstr!(Amd64Opcode.f32_to_i64_trunc); break switch_instr; // f32 -> u32 910 case 8: emitLirInstr!(Amd64Opcode.f32_to_i64_trunc); break switch_instr; // f32 -> u64 911 default: context.internal_error("Unexpected source type size %s", typeSizeTo); 912 } 913 914 case 8: 915 switch(typeSizeTo) { 916 case 1: 917 case 2: 918 IrIndex i32 = emit_nonfinal!(Amd64Opcode.f64_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0); 919 emit_final!(Amd64Opcode.mov)(typeTo, i32); 920 break switch_instr; 921 case 4: 922 IrIndex i64 = emit_nonfinal!(Amd64Opcode.f64_to_i64_trunc)(makeIrType(IrBasicType.i64), arg0); 923 emit_final!(Amd64Opcode.mov)(typeTo, i64); 924 break switch_instr; 925 case 8: emitLirInstr!(Amd64Opcode.f64_to_i64_trunc); break switch_instr; // f64 -> u64 926 default: context.internal_error("Unexpected source type size %s", typeSizeTo); 927 } 928 default: context.internal_error("Unexpected target type size %s", typeSizeFrom); 929 } 930 931 case IrOpcode.fptosi: 932 IrIndex arg0 = instrHeader.arg(ir, 0); 933 IrIndex typeFrom = getValueType(arg0, ir, context); 934 IrIndex typeTo = ir.getVirtReg(instrHeader.result(ir)).type; 935 uint typeSizeFrom = context.types.typeSize(typeFrom); 936 uint typeSizeTo = context.types.typeSize(typeTo); 937 938 switch(typeSizeFrom) { 939 case 4: 940 switch(typeSizeTo) { 941 case 1: 942 case 2: 943 IrIndex i32 = emit_nonfinal!(Amd64Opcode.f32_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0); 944 emit_final!(Amd64Opcode.mov)(typeTo, i32); 945 break switch_instr; 946 case 4: emitLirInstr!(Amd64Opcode.f32_to_i32_trunc); break switch_instr; 947 case 8: emitLirInstr!(Amd64Opcode.f32_to_i64_trunc); break switch_instr; 948 default: context.internal_error("Unexpected source type size %s", typeSizeTo); 949 } 950 951 case 8: 952 switch(typeSizeTo) { 953 case 1: 954 case 2: 955 IrIndex i32 = emit_nonfinal!(Amd64Opcode.f64_to_i32_trunc)(makeIrType(IrBasicType.i32), arg0); 956 emit_final!(Amd64Opcode.mov)(typeTo, i32); 957 break switch_instr; 958 case 4: emitLirInstr!(Amd64Opcode.f64_to_i32_trunc); break switch_instr; 959 case 8: emitLirInstr!(Amd64Opcode.f64_to_i64_trunc); break switch_instr; 960 default: context.internal_error("Unexpected source type size %s", typeSizeTo); 961 } 962 default: context.internal_error("Unexpected target type size %s", typeSizeFrom); 963 } 964 965 case IrOpcode.set_unary_cond: 966 IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type; 967 ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize, type : type }; 968 InstrWithResult res = builder.emitInstr!(Amd64Opcode.set_unary_cond)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0))); 969 recordIndex(instrHeader.result(ir), res.result); 970 break; 971 972 case IrOpcode.set_binary_cond: 973 IrIndex type = ir.getVirtReg(instrHeader.result(ir)).type; 974 ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize, type : type }; 975 InstrWithResult res = builder.emitInstr!(Amd64Opcode.set_binary_cond)(lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), getFixedIndex(instrHeader.arg(ir, 1))); 976 recordIndex(instrHeader.result(ir), res.result); 977 break; 978 979 case IrOpcode.jump: 980 builder.emitInstr!(Amd64Opcode.jmp)(lirBlockIndex); 981 break; 982 983 case IrOpcode.branch_unary: 984 ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize }; 985 IrIndex instruction = builder.emitInstr!(Amd64Opcode.un_branch)( 986 lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0))); 987 break; 988 989 case IrOpcode.branch_binary: 990 ExtraInstrArgs extra = { addUsers : false, cond : instrHeader.cond, argSize : instrHeader.argSize }; 991 IrIndex instruction = builder.emitInstr!(Amd64Opcode.bin_branch)( 992 lirBlockIndex, extra, getFixedIndex(instrHeader.arg(ir, 0)), getFixedIndex(instrHeader.arg(ir, 1))); 993 break; 994 995 case IrOpcode.ret: 996 builder.emitInstr!(Amd64Opcode.ret)(lirBlockIndex); 997 break; 998 999 case IrOpcode.unreachable: 1000 builder.emitInstr!(Amd64Opcode.ud2)(lirBlockIndex); 1001 break; 1002 1003 default: 1004 context.internal_error("IrToLir unimplemented IR instr %s", cast(IrOpcode)instrHeader.op); 1005 } 1006 } 1007 } 1008 1009 void fixInstrs(IrIndex blockIndex, ref IrBasicBlock lirBlock) 1010 { 1011 // all instructions already contain final arguments, add users here 1012 foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; lirBlock.instructions(lir)) 1013 { 1014 foreach(ref IrIndex arg; instrHeader.args(lir)) 1015 { 1016 builder.addUser(instrIndex, arg); 1017 } 1018 } 1019 } 1020 1021 void fixPhis(IrIndex blockIndex, ref IrBasicBlock lirBlock) 1022 { 1023 // fix phi args and add users 1024 foreach(IrIndex phiIndex, ref IrPhi phi; lirBlock.phis(lir)) 1025 { 1026 foreach(size_t arg_i, ref IrIndex phiArg; phi.args(lir)) 1027 { 1028 fixIndex(phiArg); 1029 builder.addUser(phiIndex, phiArg); 1030 } 1031 } 1032 } 1033 1034 //dumpFunction(context, lir, "IR -> LIR end"); // uncomment to see generated LIR before fixing 1035 1036 foreach (IrIndex blockIndex, ref IrBasicBlock lirBlock; lir.blocks) 1037 { 1038 fixInstrs(blockIndex, lirBlock); 1039 fixPhis(blockIndex, lirBlock); 1040 } 1041 }