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 /// Basic block
7 module vox.ir.ir_basic_block;
8 
9 import std.bitmanip : bitfields;
10 import vox.all;
11 
12 /// Must end with one of block_exit_... instructions
13 /// Only single loop end must be predecessor of loop header
14 /// first and last instructions point to this basic block in prevInstr, nextInstr respectively
15 @(IrValueKind.basicBlock)
16 struct IrBasicBlock
17 {
18 	IrIndex firstInstr; // first instruction
19 	IrIndex lastInstr;  // last instruction
20 	IrIndex prevBlock;  // null only if this is entryBasicBlock
21 	IrIndex nextBlock;  // null only if this is exitBasicBlock
22 	IrIndex firstPhi;   // may be null
23 
24 	PhiIterator phis(IrFunction* ir) return { return PhiIterator(ir, &this); }
25 	InstrIterator instructions(IrFunction* ir) { return InstrIterator(ir, firstInstr); }
26 	InstrReverseIterator instructionsReverse(IrFunction* ir) { return InstrReverseIterator(ir, lastInstr); }
27 	bool hasPhis() { return firstPhi.isDefined; }
28 
29 	IrSmallArray predecessors;
30 	IrSmallArray successors;
31 
32 	// This 4-byte field must be able to be interpreted as an IrIndex
33 	// Top 4 bits must be 0 at the time of inlining, so that IrIndex fixing can be performed
34 	mixin(bitfields!(
35 		/// used for sequential block indexing
36 		uint, "seqIndex",    23,
37 		/// True if all predecessors was added
38 		bool, "isSealed",     1,
39 		/// If true, sealBlock does nothing
40 		/// Used to prevent sealing loop header block until whole body is generated
41 		bool, "preventSeal",  1,
42 		/// True if block_exit instruction is in place
43 		bool, "isFinished",   1,
44 		// if true, block is loop header and has incoming back edges
45 		bool, "isLoopHeader", 1,
46 		// true if block was created to split critial edge
47 		bool, "replacesCriticalEdge", 1,
48 		// used for block ordering. Gets reset to 0 after use
49 		bool, "visitFlag",    1,
50 		uint, "",             3,
51 	));
52 }
53 //pragma(msg, "BB size: ", cast(int)IrBasicBlock.sizeof, " bytes");
54 
55 void removeAllPhis(ref IrBasicBlock block)
56 {
57 	block.firstPhi = IrIndex();
58 }
59 
60 void removeAllInstrs(ref IrBasicBlock block)
61 {
62 	block.firstInstr = IrIndex();
63 	block.lastInstr = IrIndex();
64 }
65 
66 /// INPUT:
67 ///           D >----,
68 ///   A --critical--> B
69 ///    `----> C
70 /// Edge from A to B is called critical when A has 2+ successors and B has 2+ predecessors
71 bool isCriticalEdge(ref IrBasicBlock predBlock, ref IrBasicBlock succBlock)
72 {
73 	return predBlock.successors.length > 1 && succBlock.predecessors.length > 1;
74 }
75 
76 /// INPUT:
77 ///   A1 -> A -> A2  or  A1 -> A  or  A -> A2
78 /// OUTPUT:
79 ///     A1 --> A2    or     A1    or    A2
80 void removeBlockFromChain(IrFunction* ir, IrBasicBlock* blockA)
81 {
82 	if (blockA.prevBlock.isDefined)
83 	{
84 		IrBasicBlock* left = ir.getBlock(blockA.prevBlock);
85 		left.nextBlock = blockA.nextBlock;
86 	}
87 
88 	if (blockA.nextBlock.isDefined)
89 	{
90 		IrBasicBlock* right = ir.getBlock(blockA.nextBlock);
91 		right.prevBlock = blockA.prevBlock;
92 	}
93 }
94 
95 /// blockB must not be an entry block, but may be exit block of IrFunction
96 /// used for block ordering
97 /// INPUT:
98 ///   A1 -> A -> A2  or  A1 -> A
99 ///   B1 -> B
100 /// OUTPUT:
101 ///   A1 -> A2  or  A1
102 ///   B1 -> A -> B -> B2
103 void linkSingleBlockBefore(IrFunction* ir, IrIndex blockA, IrIndex blockB)
104 {
105 	IrBasicBlock* b = ir.getBlock(blockB);
106 	IrBasicBlock* a = ir.getBlock(blockA);
107 
108 	// check if already in correct order
109 	if (b.prevBlock == blockA) return;
110 
111 	removeBlockFromChain(ir, a);
112 
113 	// insert 'a' before 'b'
114 	{
115 		a.prevBlock = b.prevBlock;
116 		if (b.prevBlock.isDefined)
117 		{
118 			IrBasicBlock* left = ir.getBlock(b.prevBlock);
119 			left.nextBlock = blockA;
120 		}
121 		b.prevBlock = blockA;
122 		a.nextBlock = blockB;
123 	}
124 }
125 
126 // blockA must not be an entry block, but may be exit block of IrFunction.
127 // used for block ordering.
128 // INPUT:
129 //   A1 -> A -> A2
130 //   B -> B2  or  B
131 // OUTPUT:
132 //   A1 -> A2
133 //   B -> A -> B2  or  B -> A
134 void moveBlockAfter(IrFunction* ir, IrIndex blockA, IrIndex blockB)
135 {
136 	IrBasicBlock* a = ir.getBlock(blockA);
137 	IrBasicBlock* b = ir.getBlock(blockB);
138 
139 	// check if already in correct order
140 	if (b.nextBlock == blockA) return;
141 
142 	removeBlockFromChain(ir, a);
143 
144 	// insert 'a' after 'b'
145 	{
146 		a.nextBlock = b.nextBlock;
147 		if (b.nextBlock.isDefined)
148 		{
149 			IrBasicBlock* right = ir.getBlock(b.nextBlock);
150 			right.prevBlock = blockA;
151 		}
152 		b.nextBlock = blockA;
153 		a.prevBlock = blockB;
154 	}
155 }
156 
157 // blockB must not be an entry block
158 // INPUT:
159 //   A1 -> A -> A2
160 //   B1 -> B -> B2
161 // OUTPUT:
162 //   -> A2 (A2.prevBlock is untouched)
163 //   B1 -> (B1.nextBlock is untouched)
164 //   A1 -> A -> B -> B2
165 void makeBlocksSequential(IrFunction* ir, IrIndex blockA, IrIndex blockB)
166 {
167 	IrBasicBlock* a = ir.getBlock(blockA);
168 	IrBasicBlock* b = ir.getBlock(blockB);
169 
170 	a.nextBlock = blockB;
171 	b.prevBlock = blockA;
172 	//writefln("%s -> %s", blockA, blockB);
173 }
174 
175 
176 // INPUT:
177 //   A(IAF, IAL): IAF...IAN1,IAN2...IAL
178 //   B(IBF, IBL): IBF...IBL
179 //   - IAF First Instruction of block A
180 //   - IAL Last Instruction of block A
181 //   - IAN1,IAN2 instructions between IAF and IAL, IAN1 is followed by IAN2
182 //   - IBF First Instruction of block B
183 //   - IBL Last Instruction of block B
184 //   - IAF...IAN1 may be empty sequence, which means IAN2 == IAF, IAN1 == A
185 //   - IAN2 may equal IAL or IAF
186 //   - IBF may equal IBL
187 // OUTPUT:
188 //   A(IAF, IBL): IAF...IAN1,IBF...IBL
189 // Instructions of block B are now owned by block A.
190 // Fixes nextInstr / prevInstr of instrucitons.
191 // Fixes firstInstr / lastInstr of block A.
192 // Block B is not touched
193 void concatBlockInstructions(IrFunction* ir, IrIndex blockA, IrIndex IAN2, IrIndex IBF, IrIndex IBL)
194 {
195 	IrBasicBlock* a = ir.getBlock(blockA);
196 
197 	//writefln("A %s %s...%s,%s...%s", blockA, a.firstInstr, ir.prevInstr(IAN2), IAN2, a.lastInstr);
198 	//writefln("B %s...%s", IBF, IBL);
199 
200 	if (a.firstInstr == IAN2)
201 	{
202 		a.firstInstr = IBF;
203 		ir.prevInstr(a.firstInstr) = blockA;
204 	}
205 	else
206 	{
207 		IrIndex IAN1 = ir.prevInstr(IAN2);
208 		ir.nextInstr(IAN1) = IBF;
209 		ir.prevInstr(IBF) = IAN1;
210 	}
211 
212 	a.lastInstr = IBL;
213 	ir.nextInstr(a.lastInstr) = blockA;
214 	//writefln("A %s %s...%s", blockA, a.firstInstr, a.lastInstr);
215 }
216 
217 // Block B replaced some other block A
218 // Fixes predecessors of B with provided array
219 // Fixes successors of predecessors of A to point to B
220 void fixBlockPreds(IrFunction* ir, IrIndex blockA, IrIndex blockB, IrSmallArray predecessorsA)
221 {
222 	IrBasicBlock* b = ir.getBlock(blockB);
223 
224 	b.predecessors = predecessorsA;
225 	foreach (ref IrIndex pred; b.predecessors.range(ir)) {
226 		ir.getBlock(pred).successors.replaceFirst(ir, blockA, blockB);
227 	}
228 }
229 
230 // Block B replaced some other block A
231 // Fixes successors of B
232 // Fixes predecessors of successors of A to point to B
233 void fixBlockSucc(IrFunction* ir, IrIndex blockA, IrIndex blockB, IrSmallArray successorsA)
234 {
235 	IrBasicBlock* b = ir.getBlock(blockB);
236 
237 	//writefln("fixBlockSucc %s -> %s %s -> %s", blockA, blockB, b.successors, successorsA);
238 
239 	b.successors = successorsA;
240 	foreach (ref IrIndex succ; b.successors.range(ir)) {
241 		ir.getBlock(succ).predecessors.replaceFirst(ir, blockA, blockB);
242 	}
243 }
244 
245 struct PhiIterator
246 {
247 	IrFunction* ir;
248 	IrBasicBlock* block;
249 	int opApply(scope int delegate(IrIndex, ref IrPhi) dg) {
250 		IrIndex next = block.firstPhi;
251 		while (next.isDefined)
252 		{
253 			IrPhi* phi = ir.getPhi(next);
254 			IrIndex indexCopy = next;
255 
256 			// save current before invoking delegate, which can remove current phi
257 			next = phi.nextPhi;
258 
259 			if (int res = dg(indexCopy, *phi))
260 				return res;
261 		}
262 		return 0;
263 	}
264 }
265 
266 struct InstrIterator
267 {
268 	IrFunction* ir;
269 	IrIndex firstInstr;
270 	int opApply(scope int delegate(IrIndex, ref IrInstrHeader) dg) {
271 		IrIndex current = firstInstr;
272 		// will be 'none' if no instructions in basic block
273 		// first / last instructions point to basic block in prevInstr / nextInstr respectively
274 		while (current.isInstruction)
275 		{
276 			IrIndex indexCopy = current;
277 			IrInstrHeader* header = ir.getInstr(current);
278 
279 			// save current before invoking delegate, which can remove current instruction
280 			current = header.nextInstr(ir, indexCopy);
281 
282 			if (int res = dg(indexCopy, *header))
283 				return res;
284 		}
285 		return 0;
286 	}
287 }
288 
289 struct InstrReverseIterator
290 {
291 	IrFunction* ir;
292 	IrIndex lastInstr;
293 	int opApply(scope int delegate(IrIndex, ref IrInstrHeader) dg) {
294 		IrIndex current = lastInstr;
295 		// will be 'none' if no instructions in basic block
296 		while (current.isInstruction)
297 		{
298 			IrIndex indexCopy = current;
299 			IrInstrHeader* header = ir.getInstr(current);
300 
301 			// save current before invoking delegate, which can remove current instruction
302 			current = header.prevInstr(ir, indexCopy);
303 
304 			if (int res = dg(indexCopy, *header))
305 				return res;
306 		}
307 		return 0;
308 	}
309 }