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 constant
7 module vox.ir.ir_constant;
8 
9 import std.string : format;
10 import vox.all;
11 
12 ///
13 enum IrConstantKind : ubyte {
14 	/// Sign-extended integer constant. Up to 24 bits. Stored directly in IrIndex.
15 	smallSx,
16 	/// Zero-extended integer constant. Up to 24 bits. Stored directly in IrIndex.
17 	smallZx,
18 	/// integer or float constant. Stored in constants buffer. Stores precise type.
19 	big,
20 }
21 
22 /// Stores numeric constant data
23 /// Type is implicitly the smallest signed int type. TODO more types of constants
24 @(IrValueKind.constant)
25 struct IrConstant
26 {
27 	this(IrIndex type, ulong value) {
28 		this.type = type;
29 		this.u64 = value;
30 	}
31 
32 	this(float value) {
33 		this.type = makeIrType(IrBasicType.f32);
34 		this.f32 = value;
35 	}
36 
37 	this(double value) {
38 		this.type = makeIrType(IrBasicType.f64);
39 		this.f64 = value;
40 	}
41 
42 	bool intFitsIn32Bits() {
43 		return u32 == u64 || i32 == i64;
44 	}
45 
46 	IrConstVal value;
47 	IrIndex type;
48 
49 	alias value this;
50 }
51 
52 union IrConstVal {
53 	ulong u64;
54 	long i64;
55 	bool i1;
56 	byte i8;
57 	ubyte u8;
58 	short i16;
59 	ushort u16;
60 	int i32;
61 	struct {
62 		uint u32;
63 		uint u32_top;
64 	}
65 	float f32;
66 	double f64;
67 }
68 
69 enum IsSigned : bool {
70 	no = false,
71 	yes = true,
72 }
73 
74 enum IsSignExtended : bool {
75 	no = false,
76 	yes = true,
77 }
78 
79 enum ulong MASK_24_BITS = (1 << 24) - 1;
80 
81 @(IrValueKind.constantAggregate)
82 struct IrAggregateConstant
83 {
84 	IrIndex type;
85 	uint numMembers;
86 
87 	// Prevent type from copying because members will not be copied. Need to use ptr.
88 	@disable this(this);
89 
90 	IrIndex[0] _memberPayload;
91 	IrIndex[] members() return {
92 		return _memberPayload.ptr[0..numMembers];
93 	}
94 }
95 
96 ///
97 struct IrConstantStorage
98 {
99 	Arena!IrConstant buffer;
100 	Arena!uint aggregateBuffer;
101 
102 	///
103 	IrIndex add(float value)
104 	{
105 		if (value == 0) return makeIrType(IrBasicType.f32).zeroConstantOfType;
106 		IrIndex result;
107 		result.kind = IrValueKind.constant;
108 		result.constantIndex = cast(uint)buffer.length;
109 		result.constantKind = IrConstantKind.big;
110 		buffer.put(IrConstant(value));
111 		return result;
112 	}
113 
114 	IrIndex add(double value)
115 	{
116 		if (value == 0) return makeIrType(IrBasicType.f64).zeroConstantOfType;
117 		IrIndex result;
118 		result.kind = IrValueKind.constant;
119 		result.constantIndex = cast(uint)buffer.length;
120 		result.constantKind = IrConstantKind.big;
121 		buffer.put(IrConstant(value));
122 		return result;
123 	}
124 
125 	///
126 	IrIndex add(IrIndex type, long value)
127 	{
128 		if (value == 0) return type.zeroConstantOfType;
129 
130 		IrIndex result;
131 		result.kind = IrValueKind.constant;
132 
133 		if (type.isTypeInteger) {
134 			bool fitsInSmallIntWithSx = ((value << 40) >> 40) == value;
135 			if (fitsInSmallIntWithSx) {
136 				result.constantSize = cast(IrArgSize)(type.typeIndex - IrBasicType.i8);
137 				result.constantIndex = cast(uint)(value & MASK_24_BITS);
138 				result.constantKind = IrConstantKind.smallSx;
139 				return result;
140 			}
141 
142 			bool fitsInSmallIntWithZx = (value & MASK_24_BITS) == value;
143 			if (fitsInSmallIntWithZx) {
144 				result.constantSize = cast(IrArgSize)(type.typeIndex - IrBasicType.i8);
145 				result.constantIndex = cast(uint)(value & MASK_24_BITS);
146 				result.constantKind = IrConstantKind.smallZx;
147 				return result;
148 			}
149 		}
150 		result.constantIndex = cast(uint)buffer.length;
151 		result.constantKind = IrConstantKind.big;
152 		buffer.put(IrConstant(type, value));
153 		return result;
154 	}
155 
156 	/// Creates aggrecate constant without initializing members
157 	IrIndex addAggrecateConstant(IrIndex type, uint numMembers)
158 	{
159 		assert(type.isTypeAggregate);
160 		IrIndex resultIndex = IrIndex(cast(uint)aggregateBuffer.length, IrValueKind.constantAggregate);
161 		uint allocSize = cast(uint)divCeil(IrAggregateConstant.sizeof, uint.sizeof) + numMembers;
162 		aggregateBuffer.voidPut(allocSize);
163 		IrAggregateConstant* agg = &getAggregate(resultIndex);
164 		agg.type = type;
165 		agg.numMembers = numMembers;
166 		return resultIndex;
167 	}
168 
169 	///
170 	IrIndex addAggrecateConstant(IrIndex type, IrIndex[] members...) {
171 		IrIndex resultIndex = addAggrecateConstant(type, cast(uint)members.length);
172 		IrAggregateConstant* agg = &getAggregate(resultIndex);
173 		agg.members[] = members;
174 		return resultIndex;
175 	}
176 
177 	static IrIndex addZeroConstant(IrIndex type)
178 	{
179 		type.kind = IrValueKind.constantZero;
180 		return type;
181 	}
182 
183 	///
184 	ref IrAggregateConstant getAggregate(IrIndex index) {
185 		assert(index.isConstantAggregate, format("Not a constantAggregate (%s)", index));
186 		return *cast(IrAggregateConstant*)(&aggregateBuffer[index.storageUintIndex]);
187 	}
188 
189 	/// memberIndex must be an integer constant
190 	// IrIndex aggrType, CompilationContext* c, IrIndex[] indices...
191 	IrIndex getAggregateMember(IrIndex aggrValue, IrIndex memberIndex, CompilationContext* c) {
192 		if (aggrValue.isConstantAggregate) {
193 			uint memberIndexVal = get(memberIndex).i32;
194 			return getAggregate(aggrValue).members[memberIndexVal];
195 		} else if (aggrValue.isConstantZero) {
196 			IrIndex type = aggrValue.typeOfConstantZero;
197 			IrTypeStructMember member = c.types.getAggregateMember(type, c, memberIndex);
198 			return member.type.zeroConstantOfType;
199 		}
200 		else {
201 			c.unreachable;
202 		}
203 	}
204 
205 	///
206 	IrConstant get(IrIndex index)
207 	{
208 		if (index.kind == IrValueKind.constant)
209 		{
210 			final switch(index.constantKind) with(IrConstantKind) {
211 				case smallZx: return IrConstant(makeIrType(cast(IrBasicType)(IrBasicType.i8 + index.constantSize)), index.constantIndex);
212 				case smallSx: return IrConstant(makeIrType(cast(IrBasicType)(IrBasicType.i8 + index.constantSize)), (cast(int)index.constantIndex << 8) >> 8);
213 				case big:
214 					assert(index.constantIndex < buffer.length,
215 						format("Not in bounds: index.constantIndex(%s) < buffer.length(%s)",
216 							index.constantIndex, buffer.length));
217 					return buffer[index.constantIndex];
218 			}
219 		}
220 		else if (index.kind == IrValueKind.constantZero)
221 		{
222 			return IrConstant(index.typeOfConstantZero, 0);
223 		}
224 		else
225 			assert(false, format("Not a constant (%s)", index));
226 	}
227 }
228 
229 /// Stores constant into buffer
230 alias UnknownValueHandler = void delegate(ubyte[] buffer, IrIndex index, CompilationContext* c);
231 
232 void constantToMem(ubyte[] buffer, IrIndex index, CompilationContext* c, UnknownValueHandler handler = null)
233 {
234 	if (index.isConstant)
235 	{
236 		IrConstant con = c.constants.get(index);
237 		switch(buffer.length)
238 		{
239 			case 1:
240 				if (c.types.typeSize(con.type) > 1) goto default;
241 				buffer[0] = con.i8;
242 				break;
243 			case 2:
244 				if (c.types.typeSize(con.type) > 2) goto default;
245 				*(cast(short*)buffer.ptr) = con.i16;
246 				break;
247 			case 4:
248 				if (c.types.typeSize(con.type) > 4) goto default;
249 				*(cast(int*)buffer.ptr) = con.i32;
250 				break;
251 			case 8:
252 				*(cast(long*)buffer.ptr) = con.i64;
253 				break;
254 			default:
255 				c.internal_error("Cannot store constant %s of size %s, into memory of size %s bytes",
256 					con.i64, c.types.typeSize(con.type), buffer.length);
257 		}
258 	}
259 	else if (index.isConstantZero)
260 	{
261 		uint typeSize = c.types.typeSize(index.typeOfConstantZero);
262 		c.assertf(typeSize == buffer.length,
263 			"Cannot store zero constant of size %s, into memory of size %s bytes",
264 			typeSize, buffer.length);
265 		buffer[] = 0;
266 	}
267 	else if (index.isConstantAggregate)
268 	{
269 		IrAggregateConstant* con = &c.constants.getAggregate(index);
270 
271 		switch(con.type.typeKind) with(IrTypeKind) {
272 			case struct_t:
273 				IrTypeStruct* structType = &c.types.get!IrTypeStruct(con.type);
274 				c.assertf(structType.sizealign.size == buffer.length,
275 					"Cannot store struct constant of size %s, into memory of size %s bytes",
276 					structType.sizealign.size, buffer.length);
277 				IrIndex[] args = con.members;
278 				foreach (i, IrTypeStructMember member; structType.members)
279 				{
280 					uint memberOffset = member.offset;
281 					uint memberSize = c.types.typeSize(member.type);
282 					constantToMem(buffer[memberOffset..memberOffset+memberSize], args[i], c, handler);
283 				}
284 				break;
285 			case array:
286 				IrTypeArray* arrayType = &c.types.get!IrTypeArray(con.type);
287 				uint elemSize = c.types.typeSize(arrayType.elemType);
288 				uint typeSize = arrayType.numElements * elemSize;
289 				c.assertf(typeSize == buffer.length,
290 					"Cannot store array constant of size %s, into memory of size %s bytes",
291 					typeSize, buffer.length);
292 				IrIndex[] args = con.members;
293 				foreach (i; 0..arrayType.numElements)
294 				{
295 					uint memberOffset = i * elemSize;
296 					constantToMem(buffer[memberOffset..memberOffset+elemSize], args[i], c, handler);
297 				}
298 				break;
299 			default: c.internal_error("%s", con.type.typeKind);
300 		}
301 	}
302 	else
303 	{
304 		if (handler) handler(buffer, index, c);
305 		else c.internal_error("%s is not a constant", index);
306 	}
307 }
308 
309 IrIndex memToConstant(ubyte[] buffer, IrIndex type, CompilationContext* c)
310 {
311 	switch(type.basicType(c))
312 	{
313 		case IrBasicType.i8:
314 			c.assertf(1 == buffer.length,
315 				"Cannot load i8 constant from memory of size %s bytes", buffer.length);
316 			return c.constants.add(type, *(cast(byte*)buffer.ptr));
317 		case IrBasicType.i16:
318 			c.assertf(2 == buffer.length,
319 				"Cannot load i8 constant from memory of size %s bytes", buffer.length);
320 			return c.constants.add(type, *(cast(short*)buffer.ptr));
321 		case IrBasicType.i32:
322 			c.assertf(4 == buffer.length,
323 				"Cannot load i8 constant from memory of size %s bytes", buffer.length);
324 			return c.constants.add(type, *(cast(int*)buffer.ptr));
325 		case IrBasicType.i64:
326 			c.assertf(8 == buffer.length,
327 				"Cannot load i8 constant from memory of size %s bytes", buffer.length);
328 			return c.constants.add(type, *(cast(long*)buffer.ptr));
329 		default:
330 			c.internal_error("memToConstant %s", cast(IrBasicType)type.typeIndex);
331 	}
332 }
333 
334 // vm may be null, in which case only constants can be parsed
335 T irValueToNative(T)(IrVm* vm, IrIndex value, CompilationContext* c)
336 {
337 	static union Repr {
338 		T native;
339 		ubyte[T.sizeof] buf;
340 	}
341 	Repr repr;
342 
343 	if (value.isVirtReg)
344 	{
345 		c.assertf(vm !is null, "Cannot read vreg without VM");
346 		repr.buf[] = vm.slotToSlice(vm.vregSlot(value));
347 		return repr.native;
348 	}
349 
350 	void onGlobal(ubyte[] subbuffer, IrIndex index, CompilationContext* c)
351 	{
352 		c.assertf(index.isGlobal, "%s is not a global", index);
353 
354 		IrGlobal* global = c.globals.get(index);
355 		ObjectSymbol* globalSym = c.objSymTab.getSymbol(global.objectSymIndex);
356 		if (globalSym.isMutable) c.internal_error("%s is not a constant", index);
357 
358 		assert(globalSym.dataPtr.sizeof == subbuffer.length);
359 		subbuffer[] = *cast(ubyte[8]*)&globalSym.dataPtr;
360 	}
361 	constantToMem(repr.buf, value, c, &onGlobal);
362 	return repr.native;
363 }
364 
365 string stringFromIrValue(IrVm* vm, IrIndex value, CompilationContext* c) {
366 	return cast(string)irValueToNative!SliceString(vm, value, c).slice;
367 }
368 
369 AstIndex astIndexFromIrValue(IrVm* vm, IrIndex value, CompilationContext* c) {
370 	return irValueToNative!AstIndex(vm, value, c);
371 }