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 }