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 /// IR Index. Points to any entity in function's IR
7 module vox.ir.ir_index;
8 
9 import std.format : formattedWrite;
10 import std.bitmanip : bitfields;
11 
12 import vox.all;
13 
14 /// Represent index of any IR entity inside function's ir array
15 @(IrValueKind.none)
16 struct IrIndex
17 {
18 	///
19 	this(uint _storageUintIndex, IrValueKind _kind)
20 	{
21 		storageUintIndex = _storageUintIndex;
22 		kind = _kind;
23 	}
24 
25 	/// Constructor for physicalRegister
26 	this(PhysReg reg, uint regSize)
27 	{
28 		physRegIndex = reg.regIndex;
29 		physRegSize = regSize;
30 		physRegClass = reg.regClass;
31 		kind = IrValueKind.physicalRegister;
32 	}
33 
34 	/// ditto
35 	this(uint index, uint regSize, uint regClass)
36 	{
37 		physRegIndex = index;
38 		physRegSize = regSize;
39 		physRegClass = regClass;
40 		kind = IrValueKind.physicalRegister;
41 	}
42 
43 	// Create from uint representation
44 	static IrIndex fromUint(uint data)
45 	{
46 		IrIndex res;
47 		res.asUint = data;
48 		return res;
49 	}
50 
51 	union
52 	{
53 		mixin(bitfields!(
54 			uint,        "storageUintIndex", 28, // may be 0 for defined index
55 			IrValueKind, "kind",              4  // is never 0 for defined index
56 		));
57 
58 		// used when kind == IrValueKind.constant
59 		mixin(bitfields!(
60 			// Big constants use constantIndex as index into IrConstantStorage
61 			// Small constants store data directly in constantIndex.
62 			uint,            "constantIndex", 24,
63 			IrArgSize,       "constantSize",   2,
64 			// kind of constant
65 			IrConstantKind,  "constantKind",   2,
66 			IrValueKind,     "",               4  // index kind
67 		));
68 
69 		enum TYPE_INDEX_BITS = 24;
70 
71 		// used when kind == IrValueKind.type || kind == IrValueKind.constantZero
72 		// types are stored in 8-byte chunked buffer
73 		mixin(bitfields!(
74 			// if typeKind is basic, then typeIndex contains IrBasicType
75 			uint,        "typeIndex", TYPE_INDEX_BITS, // may be 0 for defined index
76 			IrTypeKind,  "typeKind",                4, // type kind
77 			IrValueKind, "",                        4  // index kind
78 		));
79 
80 		// used when kind == IrValueKind.physicalRegister
81 		mixin(bitfields!(
82 			// machine-specific index
83 			uint,        "physRegIndex",     12,
84 			// physical register size
85 			// Not in bytes, but a machine-specific enum value
86 			uint,        "physRegSize",       8,
87 			// physical register class
88 			uint,        "physRegClass",      8,
89 			IrValueKind, "",                  4  // index `kind`
90 		));
91 
92 		// is 0 for undefined index
93 		uint asUint;
94 	}
95 	static assert(IrValueKind.max <= 0b1111, "4 bits are reserved for IrValueKind");
96 
97 	bool isDefined() const { return asUint != 0; }
98 	bool isUndefined() const { return asUint == 0; }
99 
100 	void toString(scope void delegate(const(char)[]) sink) const {
101 		if (asUint == 0) {
102 			sink("<null>");
103 			return;
104 		}
105 
106 		switch(kind) with(IrValueKind) {
107 			default: sink.formattedWrite("0x%X", asUint); break;
108 			case array: sink.formattedWrite("arr%s", storageUintIndex); break;
109 			case instruction: sink.formattedWrite("i.%s", storageUintIndex); break;
110 			case basicBlock: sink.formattedWrite("@%s", storageUintIndex); break;
111 			case constant:
112 				final switch(constantKind) with(IrConstantKind) {
113 					case smallZx: sink.formattedWrite("%s", constantIndex); break;
114 					case smallSx: sink.formattedWrite("%s", (cast(int)constantIndex << 8) >> 8); break;
115 					case big: sink.formattedWrite("cu.%s", constantIndex); break;
116 				}
117 				break;
118 
119 			case constantAggregate: sink.formattedWrite("caggr.%s", storageUintIndex); break;
120 			case constantZero:
121 				if (typeKind == IrTypeKind.basic)
122 					sink("0");
123 				else
124 					sink("zeroinit");
125 				break;
126 
127 			case global: sink.formattedWrite("g%s", storageUintIndex); break;
128 			case phi: sink.formattedWrite("phi%s", storageUintIndex); break;
129 			case stackSlot: sink.formattedWrite("s%s", storageUintIndex); break;
130 			case virtualRegister: sink.formattedWrite("v%s", storageUintIndex); break;
131 			case physicalRegister: sink.formattedWrite("r%s<c%s s%s>", physRegIndex, physRegClass, physRegSize); break;
132 			case type: sink.formattedWrite("type.%s.%s", typeKind, typeIndex); break;
133 			case variable: sink.formattedWrite("var%s", storageUintIndex); break;
134 			case func: sink.formattedWrite("f%s", storageUintIndex); break;
135 		}
136 	}
137 
138 	/// When this index represents index of 0's array item, produces
139 	/// index of this array items. Calling with 0 returns itself.
140 	IrIndex indexOf(T)(size_t offset)
141 	{
142 		static assert(T.alignof == 4, "Can only point to types aligned to 4 bytes");
143 		IrIndex result = this;
144 		result.storageUintIndex = cast(uint)(storageUintIndex + divCeil(T.sizeof, uint.sizeof) * offset);
145 		return result;
146 	}
147 
148 	const:
149 
150 	bool isInstruction() { return kind == IrValueKind.instruction; }
151 	bool isBasicBlock() { return kind == IrValueKind.basicBlock; }
152 	bool isPhi() { return kind == IrValueKind.phi; }
153 	bool isConstant() { return kind == IrValueKind.constant; }
154 	bool isSimpleConstant() { return kind == IrValueKind.constant || kind == IrValueKind.constantZero; }
155 	bool isConstantAggregate() { return kind == IrValueKind.constantAggregate; }
156 	bool isConstantZero() { return kind == IrValueKind.constantZero; }
157 	bool isSomeConstant() { return
158 			kind == IrValueKind.constant ||
159 			kind == IrValueKind.constantAggregate ||
160 			kind == IrValueKind.constantZero; }
161 	bool isGlobal() { return kind == IrValueKind.global; }
162 	bool isVirtReg() { return kind == IrValueKind.virtualRegister; }
163 	bool isPhysReg() { return kind == IrValueKind.physicalRegister; }
164 	bool isSomeReg() {
165 		return kind == IrValueKind.virtualRegister ||
166 			kind == IrValueKind.physicalRegister;
167 	}
168 	bool isStackSlot() { return kind == IrValueKind.stackSlot; }
169 	bool isType() { return kind == IrValueKind.type; }
170 	bool isVariable() { return kind == IrValueKind.variable; }
171 	bool isFunction() { return kind == IrValueKind.func; }
172 
173 	bool isTypeBasic() { return kind == IrValueKind.type && typeKind == IrTypeKind.basic; }
174 	bool isTypePointer() { return kind == IrValueKind.type && typeKind == IrTypeKind.pointer; }
175 	bool isTypeArray() { return kind == IrValueKind.type && typeKind == IrTypeKind.array; }
176 	bool isTypeStruct() { return kind == IrValueKind.type && typeKind == IrTypeKind.struct_t; }
177 	bool isTypeAggregate() {
178 		return kind == IrValueKind.type &&
179 			(typeKind == IrTypeKind.struct_t || typeKind == IrTypeKind.array);
180 	}
181 	bool isTypeFunction() { return kind == IrValueKind.type && typeKind == IrTypeKind.func_t; }
182 	bool isTypeVoid() {
183 		return kind == IrValueKind.type && typeKind == IrTypeKind.basic && typeIndex == IrBasicType.void_t;
184 	}
185 	bool isTypeNoreturn() {
186 		return kind == IrValueKind.type && typeKind == IrTypeKind.basic && typeIndex == IrBasicType.noreturn_t;
187 	}
188 	bool isTypeFloat() {
189 		return kind == IrValueKind.type && typeKind == IrTypeKind.basic && (typeIndex == IrBasicType.f32 || typeIndex == IrBasicType.f64);
190 	}
191 	bool isTypeInteger() {
192 		return kind == IrValueKind.type && typeKind == IrTypeKind.basic && (typeIndex >= IrBasicType.i8 && typeIndex <= IrBasicType.i64);
193 	}
194 	IrBasicType basicType(CompilationContext* c) {
195 		c.assertf(kind == IrValueKind.type, "%s != IrValueKind.type", kind);
196 		c.assertf(typeKind == IrTypeKind.basic, "%s != IrTypeKind.basic", typeKind);
197 		return cast(IrBasicType)typeIndex;
198 	}
199 
200 	IrIndex typeOfConstantZero() {
201 		assert(isConstantZero);
202 		IrIndex copy = this;
203 		copy.kind = IrValueKind.type;
204 		return copy;
205 	}
206 
207 	IrIndex zeroConstantOfType() {
208 		assert(isType);
209 		IrIndex copy = this;
210 		copy.kind = IrValueKind.constantZero;
211 		return copy;
212 	}
213 }
214 
215 // compares physical registers size agnostically
216 // if not physical register compares as usual
217 bool sameIndexOrPhysReg(IrIndex a, IrIndex b) pure @nogc
218 {
219 	if (a.asUint == b.asUint) return true;
220 	if (a.kind == IrValueKind.physicalRegister)
221 	{
222 		a.physRegSize = 0;
223 		b.physRegSize = 0;
224 		return a.asUint == b.asUint;
225 	}
226 	return false;
227 }