1 /// Copyright: Copyright (c) 2018-2019 Andrey Penechko. 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 /// Authors: Andrey Penechko. 4 5 /// Liveness info storage 6 /// Phi functions use their arguments at the last instruction of corresponding basic block 7 module vox.be.reg_alloc.liveness_info; 8 9 import std.array : empty; 10 import std.stdio : writeln, write, writef, writefln, stdout; 11 import std.string : format; 12 13 import vox.all; 14 15 16 struct LiveBitmap 17 { 18 // We store a bit array per basic block. Each bit shows liveness of virtual register per block 19 // Padding aligns number of bits to multiple of size_t bits. 20 // This way there is a whole number of size_ts per basic block 21 // With padding we can copy size_t's directly between live and liveIn, without bit twiddling 22 23 size_t numBucketsPerBlock; 24 25 // [block0:[vreg index..., padding], block1:[vreg index..., padding]] 26 size_t[] liveInBuckets; 27 28 // [vreg index..., padding] 29 size_t[] liveBuckets; 30 31 void allocSets(CompilationContext* c, uint numBucketsPerBlock, uint numBlocks) { 32 this.numBucketsPerBlock = numBucketsPerBlock; 33 uint numBucketsTotal = numBucketsPerBlock * numBlocks; 34 liveInBuckets = c.allocateTempArray!size_t(numBucketsTotal); 35 liveInBuckets[] = 0; 36 37 liveBuckets = c.allocateTempArray!size_t(numBucketsPerBlock); 38 liveBuckets[] = 0; 39 } 40 41 size_t[] blockLiveInBuckets(IrIndex blockIndex) 42 { 43 size_t from = blockIndex.storageUintIndex * numBucketsPerBlock; 44 size_t to = from + numBucketsPerBlock; 45 return liveInBuckets[from..to]; 46 } 47 } 48 49 50 /// 51 struct LivenessInfo 52 { 53 LiveBitmap bitmap; 54 55 // invariant: all ranges of one interval are sorted by `from` and do not intersect 56 Array!LiveInterval intervals; 57 uint numFixedIntervals; 58 ubyte[NUM_REG_CLASSES] physRegOffsetByClass; 59 /// instructionIndex -> seqIndex 60 /// blockIndex -> seqIndex 61 IrMirror!uint linearIndices; 62 // maps even indices to instructions and basic blocks 63 IrIndex[] evenIndexToIrIndex; 64 uint maxLinearIndex; // max value stored in linearIndices 65 66 auto virtualIntervals() { return intervals[numFixedIntervals..$]; } 67 auto physicalIntervals() { return intervals[0..numFixedIntervals]; } 68 69 LiveInterval* vint(size_t virtSeqIndex) { return &intervals[numFixedIntervals+virtSeqIndex]; } 70 LiveInterval* vint(IrIndex index) { return &intervals[numFixedIntervals + index.storageUintIndex]; } 71 LiveInterval* pint(IrIndex physReg) { return &intervals[physRegOffsetByClass[physReg.physRegClass] + physReg.physRegIndex]; } 72 LiveInterval* pint(PhysReg physReg) { return &intervals[physRegOffsetByClass[physReg.regClass] + physReg.regIndex]; } 73 IntervalIndex vindex(size_t virtSeqIndex) { return IntervalIndex(numFixedIntervals+virtSeqIndex); } 74 IntervalIndex pindex(size_t physSeqIndex) { return IntervalIndex(physSeqIndex); } 75 76 void initStorage(CompilationContext* context, IrFunction* ir) { 77 78 uint numVregs = ir.numVirtualRegisters; 79 uint numBucketsPerBlock = cast(uint)divCeil(numVregs, size_t.sizeof * 8); 80 bitmap.allocSets(context, numBucketsPerBlock, ir.numBasicBlocks); 81 82 foreach(ref i; intervals) { 83 i.ranges.free(context.arrayArena); 84 } 85 intervals.clear; 86 numFixedIntervals = cast(uint)context.machineInfo.numRegisters; 87 intervals.voidPut(context.arrayArena, numFixedIntervals + ir.numVirtualRegisters); 88 89 ubyte physOffset = 0; 90 foreach(ubyte regClass; 0..NUM_REG_CLASSES) { 91 physRegOffsetByClass[regClass] = physOffset; 92 auto numRegsPerClass = context.machineInfo.numRegsPerClass[regClass]; 93 foreach(regIndex; 0..numRegsPerClass) { 94 LiveInterval* it = &intervals[physOffset+regIndex]; 95 *it = LiveInterval(); 96 it.reg = it.definition = IrIndex(regIndex, ArgType.QWORD, regClass); 97 it.regClass = regClass; 98 } 99 physOffset += numRegsPerClass; 100 } 101 102 foreach (IrIndex vregIndex, ref IrVirtualRegister vreg; ir.virtualRegisters) { 103 LiveInterval it = { definition : vregIndex }; 104 *vint(vregIndex.storageUintIndex) = it; 105 } 106 107 linearIndices.createBasicBlockMirror(context, ir); 108 linearIndices.createInstrMirror(context, ir); 109 } 110 111 void assignSequentialIndices(CompilationContext* context, IrFunction* ir) { 112 // enumerate all basic blocks, instructions 113 // phi functions use index of the block 114 // we can assume all blocks to have at least single instruction (exit instruction) 115 evenIndexToIrIndex = cast(IrIndex[])context.tempBuffer.voidPut(ir.numBasicBlocks + ir.numInstructions); 116 uint index = 0; 117 foreach (IrIndex blockIndex, ref IrBasicBlock block; ir.blocks) 118 { 119 version(LivePrint) writefln("[LIVE] %s %s", index * ENUM_STEP, blockIndex); 120 // Allocate index for block start 121 linearIndices.basicBlock(blockIndex) = index * ENUM_STEP; 122 evenIndexToIrIndex[index] = blockIndex; 123 ++index; 124 // enumerate instructions 125 foreach (IrIndex instrIndex, ref IrInstrHeader instrHeader; block.instructions(ir)) 126 { 127 version(LivePrint) writefln("[LIVE] %s %s", index * ENUM_STEP, instrIndex); 128 linearIndices.instr(instrIndex) = index * ENUM_STEP; 129 evenIndexToIrIndex[index] = instrIndex; 130 ++index; 131 } 132 } 133 maxLinearIndex = index * ENUM_STEP; 134 } 135 136 // First we set length 137 // Then we assign last element for each use and decrement the length 138 // We track definition users for instructions 139 void setIntervalUsesLength(CompilationContext* context, IrFunction* ir) { 140 foreach (ref LiveInterval it; virtualIntervals) 141 { 142 IrVirtualRegister* vreg = ir.getVirtReg(it.definition); 143 context.assertf(it.uses.length == 0, "non empty uses %s of %s; %s", it.uses.length, it.definition, it.uses); 144 uint numUses = vreg.definition.isPhi ? vreg.users.length : vreg.users.length + 1; // with definition 145 it.uses = cast(UsePosition[])context.tempBuffer.voidPut(numUses); 146 } 147 } 148 149 // At the end we set the length one more time 150 // This way uses are in correct order 151 void resetIntervalUsesLength(CompilationContext* context, IrFunction* ir) { 152 foreach (ref LiveInterval it; virtualIntervals) 153 { 154 IrVirtualRegister* vreg = ir.getVirtReg(it.definition); 155 context.assertf(it.uses.length == 0, "non empty uses %s of %s; %s", it.uses.length, it.definition, it.uses); 156 uint numUses = vreg.definition.isPhi ? vreg.users.length : vreg.users.length + 1; // with definition 157 it.uses = it.uses.ptr[0..numUses]; 158 } 159 } 160 161 bool isBlockStartAt(IrFunction* ir, uint pos, ref IrIndex next) { 162 if ((pos & 1) == 1) pos += 1; 163 164 while (next.isDefined) 165 { 166 IrBasicBlock* block = ir.getBlock(next); 167 uint blockFromPos = linearIndices.basicBlock(next); 168 if (pos == blockFromPos) return true; 169 uint blockToPos = linearIndices.instr(block.lastInstr); 170 if (pos <= blockToPos) return false; 171 next = block.nextBlock; 172 } 173 return false; 174 } 175 176 IrIndex getRegFor(IrIndex index, uint pos) 177 { 178 if (index.isVirtReg) 179 { 180 IntervalIndex intIndex = numFixedIntervals + index.storageUintIndex; 181 LiveInterval* it = &intervals[intIndex]; 182 while (true) { 183 if (it.coversPosition(pos)) { 184 break; 185 } else { 186 assert(!it.child.isNull, format("no children left to find reg, pos %s %s %s", pos, intIndex, *it)); 187 intIndex = it.child; 188 it = &intervals[it.child]; 189 } 190 } 191 assert(it.reg.isDefined, format("%s %s null reg", intIndex, *it)); 192 return it.reg; 193 } 194 else if (index.isPhysReg) 195 { 196 return index; 197 } 198 writefln("%s", index); 199 assert(false); 200 } 201 202 LiveInterval* get(IrIndex index) 203 { 204 if (index.isVirtReg) 205 { 206 return &intervals[numFixedIntervals + index.storageUintIndex]; 207 } 208 else if (index.isPhysReg) 209 { 210 return &intervals[index.physRegIndex]; 211 } 212 assert(false); 213 } 214 215 // returns new interval or old if no split is possible 216 IntervalIndex splitBefore(CompilationContext* context, IrFunction* lir, IntervalIndex parentInterval, uint before) 217 { 218 LiveInterval* it = &intervals[parentInterval]; 219 220 uint optimalPos = before;//findSplitPos(context, lir, it, before); 221 // Make sure that we split on odd position 222 if ((optimalPos & 1) == 0) optimalPos -= 1; 223 224 // we want to take register from interval starting at the same position 225 // happens for multiple phi functions in the same block 226 // if first is allocated interval with bigger first use position 227 if (optimalPos <= it.from) { 228 //writefln(" splitBefore before start %s %s take register from interval starting at %s", parentInterval, before, it.from); 229 return parentInterval; 230 } 231 232 LiveRangeIndex rightIndex = it.getRightmostRange(optimalPos); 233 234 if (rightIndex.isNull) { 235 //writefln(" splitBefore after end %s %s %s split at max pos", parentInterval, optimalPos, rightIndex); 236 return parentInterval; 237 } 238 239 //writefln(" splitBefore1 %s %s %s", parentInterval, optimalPos, rightIndex); 240 return splitBefore(context, parentInterval, optimalPos, rightIndex); 241 } 242 243 uint findSplitPos(CompilationContext* context, IrFunction* lir, LiveInterval* it, uint before) 244 { 245 uint minPos = it.from + 1; 246 uint maxPos = before; 247 uint maxBlockPos; 248 249 foreach (IrIndex blockIndex, ref IrBasicBlock block; lir.blocks) 250 { 251 uint blockPos = linearIndices.basicBlock(blockIndex); 252 if (blockPos > maxPos) break; 253 maxBlockPos = blockPos; 254 } 255 256 // max pos between minPos and maxPos 257 if (maxBlockPos >= minPos) return maxBlockPos; 258 259 // no block boundaries found 260 return maxPos; 261 } 262 263 IntervalIndex splitBefore(CompilationContext* context, IntervalIndex parentInterval, uint before, LiveRangeIndex rightIndex) 264 { 265 LiveInterval* it = &intervals[parentInterval]; 266 auto right = &it.ranges[rightIndex]; 267 //writefln(" splitBefore %s %s %s", parentInterval, before, right.from); 268 269 assert(before >= it.from); 270 271 InvertedArray!LiveRange newRanges; 272 273 if (right.from < before) 274 { 275 newRanges.put(context.arrayArena, LiveRange(before, right.to)); // piece of splitted range 276 right.to = before; 277 foreach(range; it.ranges[rightIndex+1..$]) 278 newRanges.put(context.arrayArena, range); 279 it.ranges.unput(it.ranges.length - rightIndex - 1); // dont remove splitted range 280 } 281 else 282 { 283 foreach(range; it.ranges[rightIndex..$]) 284 newRanges.put(context.arrayArena, range); 285 it.ranges.unput(it.ranges.length - rightIndex); 286 } 287 288 auto childInterval = IntervalIndex(intervals.length); 289 290 LiveInterval rightInterval = { 291 ranges : newRanges, 292 uses : it.splitUsesBefore(before), 293 definition : it.definition, 294 parent : parentInterval, 295 child : it.child, 296 regClass : it.regClass, 297 }; 298 299 if (!it.child.isNull) 300 intervals[it.child].parent = childInterval; 301 it.child = childInterval; 302 303 intervals.put(context.arrayArena, rightInterval); 304 //writefln(" left %s %s", parentInterval, *it); 305 //writefln(" right %s %s", childInterval, rightInterval); 306 assert(it.ranges.length > 0); 307 assert(rightInterval.ranges.length > 0); 308 309 return childInterval; 310 } 311 312 void dump(ref TextSink sink, CompilationContext* context, IrFunction* lir) { 313 void dumpSub(ref Array!LiveInterval intervals) 314 { 315 foreach (i, it; intervals) { 316 if (it.isFixed && it.ranges.empty) continue; 317 318 if (it.isFixed) sink.put(" p"); 319 else sink.put(" v"); 320 321 if (it.reg.isDefined) 322 sink.putf("% 3s %s [%2s]:", i, 323 IrIndexDump(it.definition, context, lir), 324 IrIndexDump(it.reg, context, lir)); 325 else 326 sink.putf("% 3s %s [no reg]:", i, it.definition); 327 328 foreach(rIndex, range; it.ranges[].enumerate) { 329 if (rIndex > 0) sink.put(" "); 330 sink.putf(" [%s; %s)", range.from, range.to); 331 } 332 333 foreach(UsePosition use; it.uses) { 334 sink.putf(" %s", use); 335 } 336 if (!it.parent.isNull) sink.putf(" parent: v %s", it.parent); 337 if (!it.child.isNull) sink.putf(" child: v %s", it.child); 338 339 sink.putln; 340 } 341 } 342 sink.putfln("intervals %s", context.idString(lir.name)); 343 dumpSub(intervals); 344 //dump2; 345 } 346 void dump(CompilationContext* context, IrFunction* lir) 347 { 348 TextSink sink; 349 dump(sink, context, lir); 350 sink.text.writeln; 351 } 352 void dump2() { 353 writefln("intervals"); 354 foreach (i, it; intervals) { 355 writefln("% 2s %s", i, it); 356 //foreach (j, r; it.ranges) { 357 // writefln(" % 2s %s", j, r); 358 //} 359 } 360 } 361 }