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 }