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 }