1 /// Copyright: Copyright (c) 2017-2020 Andrey Penechko. 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 /// Authors: Andrey Penechko. 4 5 /// Inliner implementation 6 module vox.ir.ir_inline; 7 8 import vox.all; 9 10 // will split basic block and replace call instruction with jump to callee body 11 // Returns first inserted instruction, with which to continue IR walk 12 IrIndex inline_call(IrBuilder* builder, IrFunction* calleeIr, IrIndex callInstrIndex, IrIndex callerBlockIndex) 13 { 14 CompilationContext* c = builder.context; 15 IrFunction* ir = builder.ir; 16 17 IrIndex calleeEntryBlock = IrIndex(ir.numBasicBlocks + 0, IrValueKind.basicBlock); 18 IrIndex calleeExitIndex = IrIndex(ir.numBasicBlocks + 1, IrValueKind.basicBlock); 19 20 //writefln("inline %s into %s", c.idString(calleeIr.name), c.idString(ir.name)); 21 22 // copy buffers of inlined function 23 // this will place data of each buffer at the end of buffer for current function 24 // update length of current function buffers 25 // fix all indices 26 appendIrStorage(ir, calleeIr, c); 27 28 // Gather blocks 29 IrBasicBlock* callerBlock = ir.getBlock(callerBlockIndex); 30 IrIndex callerNextBlock = callerBlock.nextBlock; 31 IrBasicBlock* calleeEntry = ir.getBlock(calleeEntryBlock); 32 IrIndex calleeBodyIndex = calleeEntry.successors[0, ir]; 33 IrBasicBlock* calleeBody = ir.getBlock(calleeBodyIndex); 34 IrBasicBlock* calleeExit = ir.getBlock(calleeExitIndex); 35 36 // Split basic block before call instruction 37 // part before call remains in the same BB (callerBlockIndex) 38 // part after call is moved into exit block of callee (calleeExitIndex) 39 // callee entry block is dropped 40 41 IrInstrHeader* callInstr = ir.getInstr(callInstrIndex); 42 IrIndex[] callArgs = callInstr.args(ir)[1..$]; 43 44 // redirect parameter users to call argument 45 foreach(IrIndex instrIndex, ref IrInstrHeader instrHeader; calleeEntry.instructions(ir)) 46 { 47 if (instrHeader.op == IrOpcode.parameter) 48 { 49 IrInstr_parameter* param = ir.get!IrInstr_parameter(instrIndex); 50 uint paramIndex = param.index(ir); 51 IrIndex result = instrHeader.result(ir); 52 removeUser(c, ir, callInstrIndex, callArgs[paramIndex]); 53 builder.redirectVregUsersTo(result, callArgs[paramIndex]); 54 builder.removeVirtualRegister(result); 55 } 56 else if (instrHeader.op == IrOpcode.jump) 57 { 58 // noop 59 } 60 else 61 { 62 c.internal_error("Unexpected instruction in entry block %s", cast(IrOpcode)instrHeader.op); 63 } 64 } 65 66 // rewire return value to return register 67 if (callInstr.hasResult) 68 { 69 IrIndex retInstrIndex = calleeExit.lastInstr; 70 IrInstrHeader* calleeRetInstr = ir.getInstr(retInstrIndex); 71 72 assert(calleeRetInstr.op == IrOpcode.ret_val); 73 IrIndex returnVal = calleeRetInstr.arg(ir, 0); 74 removeUser(c, ir, retInstrIndex, returnVal); 75 builder.redirectVregUsersTo(callInstr.result(ir), returnVal); 76 builder.removeVirtualRegister(callInstr.result(ir)); 77 } 78 79 // Cache instructions of caller block 80 IrIndex instrBeforeCall = ir.prevInstr(callInstrIndex); // may be block if call is firstInstr 81 IrIndex callerLastInstr = callerBlock.lastInstr; 82 83 // Cache successors of callerBlock. They will be owerwritten with concat 84 IrSmallArray callerSuccessors = callerBlock.successors; 85 86 // Insert callee body instructions insead of call instruction 87 concatBlockInstructions(ir, callerBlockIndex, callInstrIndex, calleeBody.firstInstr, calleeBody.lastInstr); 88 89 // If callee exit has single predecessor, then make it callee exit block 90 if (calleeExit.predecessors.length == 1) 91 { 92 calleeExitIndex = calleeExit.predecessors[0, ir]; 93 calleeExit = ir.getBlock(calleeExitIndex); 94 } 95 96 // append instructions after call to the callee exit block, replacing the jump instruction 97 // we include the call instruction as a marker of the end of inlined code 98 if (calleeExitIndex == calleeBodyIndex) 99 { 100 // callee consists of a single basic block 101 concatBlockInstructions(ir, callerBlockIndex, calleeExit.lastInstr, callInstrIndex, callerLastInstr); 102 } 103 else 104 { 105 concatBlockInstructions(ir, calleeExitIndex, calleeExit.lastInstr, callInstrIndex, callerLastInstr); 106 // Fix successors and predecessors 107 fixBlockSucc(ir, calleeBodyIndex, callerBlockIndex, calleeBody.successors); 108 fixBlockSucc(ir, callerBlockIndex, calleeExitIndex, callerSuccessors); 109 // Reorder blocks 110 makeBlocksSequential(ir, callerBlockIndex, calleeBodyIndex); 111 removeBlockFromChain(ir, calleeBody); 112 makeBlocksSequential(ir, calleeExitIndex, callerNextBlock); 113 } 114 115 // repurpose call instruction as marker for walk loop to look for 116 // it triggers removal of currently walked function from the stack of functions 117 *ir.getInstr(callInstrIndex) = IrInstrHeader(IrOpcode.inline_marker); 118 119 // Find the next instruction to visit 120 if (instrBeforeCall.isBasicBlock) { 121 // basic block means that no instructions preceded the call 122 // inlined code begins with first instruction 123 return callerBlock.firstInstr; 124 } 125 else { 126 // inlined code replaced the call instruction 127 return ir.nextInstr(instrBeforeCall); 128 } 129 }