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 type info
7 module vox.ir.ir_type;
8 
9 import std.format : format;
10 import std.traits : getUDAs;
11 import vox.all;
12 
13 /// Integers are two-complement of unknown signedness
14 enum IrBasicType : ubyte
15 {
16 	noreturn_t,
17 	void_t,
18 	i8,
19 	i16,
20 	i32,
21 	i64,
22 	f32,
23 	f64,
24 }
25 
26 enum CovertionKind : ubyte {
27 	itoi, // int to int
28 	itof, // int to float
29 	ftoi, // float to int
30 	ftof, // float to float
31 }
32 
33 ///
34 enum IrTypeKind : ubyte
35 {
36 	basic,
37 	pointer,
38 	array,
39 	struct_t,
40 	func_t
41 }
42 
43 enum getIrTypeKind(T) = getUDAs!(T, IrTypeKind)[0];
44 
45 struct SizeAndAlignment {
46 	uint size;
47 	ubyte alignmentPower;
48 	uint alignment() { return 1 << cast(uint)alignmentPower; }
49 }
50 
51 ///
52 IrIndex makeIrType(IrBasicType t) pure {
53 	IrIndex result;
54 	result.kind = IrValueKind.type;
55 	result.typeKind = IrTypeKind.basic;
56 	result.typeIndex = t;
57 	return result;
58 }
59 
60 ///
61 @(IrValueKind.type)
62 struct IrTypeHeader
63 {
64 	IrIndex prevType; // null for first type
65 	IrIndex nextType; // null for last type
66 }
67 
68 ///
69 @(IrValueKind.type, IrTypeKind.pointer)
70 align(8)
71 struct IrTypePointer
72 {
73 	IrTypeHeader header;
74 	IrIndex baseType;
75 }
76 
77 ///
78 @(IrValueKind.type, IrTypeKind.array)
79 align(8)
80 struct IrTypeArray
81 {
82 	IrTypeHeader header;
83 	IrIndex elemType;
84 	uint numElements;
85 }
86 
87 /// variadic type, members follow the struct in memory
88 @(IrValueKind.type, IrTypeKind.struct_t)
89 align(8)
90 struct IrTypeStruct
91 {
92 	IrTypeHeader header;
93 	uint size;
94 	ubyte alignmentPower;
95 	uint alignment() { return 1 << cast(uint)alignmentPower; }
96 	SizeAndAlignment sizealign() { return SizeAndAlignment(size, alignmentPower); }
97 	void sizealign(SizeAndAlignment sa) { size = sa.size; alignmentPower = sa.alignmentPower; }
98 	// all members have offset of 0
99 	// alignment is max alignment of members
100 	// IrAggregateConstant contains index of member in slot 0, and constant for that member in slot 1
101 	bool isUnion;
102 	uint numMembers;
103 
104 	// Prevent type from copying because members will not be copied. Need to use ptr.
105 	@disable this(this);
106 
107 	IrTypeStructMember[0] members_payload;
108 	/// This must be called on the value in the buffer, not stack-local value
109 	IrTypeStructMember[] members() return { return members_payload.ptr[0..numMembers];}
110 }
111 
112 ///
113 align(8)
114 struct IrTypeStructMember
115 {
116 	IrIndex type;
117 	uint offset;
118 }
119 
120 /// variadic type, members follow the struct in memory
121 @(IrValueKind.type, IrTypeKind.func_t)
122 align(8)
123 struct IrTypeFunction
124 {
125 	IrTypeHeader header;
126 	uint numResults;
127 	uint numParameters;
128 	CallConvention callConv;
129 	ushort syscallNumber;
130 
131 	// Prevent type from copying because members will not be copied. Need to use ptr.
132 	@disable this(this);
133 
134 	IrIndex[0] payload; // result types followed by parameter types
135 	/// This must be called on the value in the buffer, not stack-local value
136 	IrIndex[] resultTypes() return { return payload.ptr[0..numResults];}
137 	IrIndex[] parameterTypes() return { return payload.ptr[numResults..numResults+numParameters];}
138 }
139 
140 ///
141 struct IrTypeStorage
142 {
143 	Arena!ulong buffer;
144 	IrIndex firstType;
145 	IrIndex lastType;
146 
147 	IrIndex appendStruct(uint numMembers, bool isUnion = false)
148 	{
149 		IrIndex result = append!IrTypeStruct;
150 
151 		{ // add member slots
152 			enum allocSize = divCeil(IrTypeStructMember.sizeof, ulong.sizeof);
153 			size_t numAllocatedSlots = allocSize * numMembers;
154 			ulong[] members = buffer.voidPut(numAllocatedSlots);
155 			members[] = 0;
156 		}
157 
158 		get!IrTypeStruct(result).numMembers = numMembers;
159 		get!IrTypeStruct(result).isUnion = isUnion;
160 		return result;
161 	}
162 
163 	IrIndex appendFuncSignature(uint numResults, uint numParameters, CallConvention callConv)
164 	{
165 		IrIndex result = append!IrTypeFunction;
166 
167 		{ // add slots for results and parameters
168 			uint numIndices = numResults + numParameters;
169 			size_t numSlots = divCeil(IrIndex.sizeof * numIndices, ulong.sizeof);
170 			ulong[] data = buffer.voidPut(numSlots);
171 			data[] = 0;
172 		}
173 
174 		auto func = &get!IrTypeFunction(result);
175 		func.numResults = numResults;
176 		func.numParameters = numParameters;
177 		func.callConv = callConv;
178 		return result;
179 	}
180 
181 	IrIndex appendPtr(IrIndex baseType)
182 	{
183 		assert(baseType.isDefined);
184 		IrIndex result = append!IrTypePointer;
185 		get!IrTypePointer(result).baseType = baseType;
186 		return result;
187 	}
188 
189 	IrIndex appendArray(IrIndex elemType, uint numElements)
190 	{
191 		assert(elemType.isDefined);
192 		IrIndex result = append!IrTypeArray;
193 		IrTypeArray* array = &get!IrTypeArray(result);
194 		array.elemType = elemType;
195 		array.numElements = numElements;
196 		return result;
197 	}
198 
199 	///
200 	IrIndex append(T)(uint howMany = 1)
201 	{
202 		static assert(T.alignof == 8, "Can only store types aligned to 8 bytes");
203 		static assert(getIrValueKind!T == IrValueKind.type, "Can only add types");
204 		enum typeKind = getIrTypeKind!T;
205 
206 		//uint len = buffer.uintLength;
207 		IrIndex typeIndex;
208 		if (buffer.uintLength >= (1 << IrIndex.TYPE_INDEX_BITS)) {
209 			throw new Exception("Out of index space in IrIndex.typeIndex in IrTypeStorage");
210 		}
211 		typeIndex.typeIndex = buffer.uintLength;
212 		typeIndex.typeKind = typeKind;
213 		typeIndex.kind = getIrValueKind!T;
214 
215 		enum allocSize = divCeil(T.sizeof, ulong.sizeof);
216 		size_t numAllocatedSlots = allocSize * howMany;
217 		T* type = cast(T*)buffer.voidPut(numAllocatedSlots).ptr;
218 		*type = T.init;
219 		//writefln("append %s %s->%s %s", T.stringof, len, buffer.uintLength, typeIndex);
220 
221 		if (lastType.isDefined)
222 		{
223 			get!IrTypeHeader(lastType).nextType = typeIndex;
224 			type.header.prevType = lastType;
225 		}
226 		else
227 		{
228 			firstType = typeIndex;
229 		}
230 		lastType = typeIndex;
231 
232 		return typeIndex;
233 	}
234 
235 	///
236 	ref T get(T)(IrIndex index)
237 	{
238 		assert(index.isDefined,  format("get!%s null index", T.stringof));
239 		assert(index.kind == getIrValueKind!T, format("%s != %s", index.kind, getIrValueKind!T));
240 		static if (!is(T == IrTypeHeader))
241 			assert(index.typeKind == getIrTypeKind!T, format("%s != %s", index.typeKind, getIrTypeKind!T));
242 		return *cast(T*)(&buffer.bufPtr[index.typeIndex]);
243 	}
244 
245 	uint typeSize(IrIndex type) {
246 		return typeSizeAndAlignment(type).size;
247 	}
248 
249 	SizeAndAlignment typeSizeAndAlignment(IrIndex type) {
250 		assert(type.isType, format("not a type (%s)", type));
251 		final switch (type.typeKind) {
252 			case IrTypeKind.basic:
253 			final switch (cast(IrBasicType)type.typeIndex) {
254 				case IrBasicType.noreturn_t: return SizeAndAlignment(0, 0);
255 				case IrBasicType.void_t: return SizeAndAlignment(0, 0);
256 				case IrBasicType.i8: return SizeAndAlignment(1, 0);
257 				case IrBasicType.i16: return SizeAndAlignment(2, 1);
258 				case IrBasicType.i32: return SizeAndAlignment(4, 2);
259 				case IrBasicType.i64: return SizeAndAlignment(8, 3);
260 				case IrBasicType.f32: return SizeAndAlignment(4, 2);
261 				case IrBasicType.f64: return SizeAndAlignment(8, 3);
262 			}
263 			case IrTypeKind.pointer:
264 				return SizeAndAlignment(8, 3);
265 			case IrTypeKind.array:
266 				IrTypeArray* array = &get!IrTypeArray(type);
267 				SizeAndAlignment elemInfo = typeSizeAndAlignment(array.elemType);
268 				return SizeAndAlignment(elemInfo.size * array.numElements, elemInfo.alignmentPower);
269 			case IrTypeKind.struct_t:
270 				auto s = &get!IrTypeStruct(type);
271 				return s.sizealign;
272 			case IrTypeKind.func_t:
273 				return SizeAndAlignment(0, 0);
274 		}
275 	}
276 
277 	IrIndex getPointerBaseType(IrIndex ptrType)
278 	{
279 		return get!IrTypePointer(ptrType).baseType;
280 	}
281 
282 	IrIndex getArrayElementType(IrIndex arrayType)
283 	{
284 		return get!IrTypeArray(arrayType).elemType;
285 	}
286 
287 	/// Returns offset + type of member indicated by indices
288 	IrTypeStructMember getAggregateMember(IrIndex aggrType, CompilationContext* c, IrIndex[] indices...)
289 	{
290 		ulong offset = 0;
291 		foreach(IrIndex memberIndex; indices)
292 		{
293 			c.assertf(memberIndex.isSimpleConstant, "Aggregates can only be indexed with constants, not with %s", memberIndex);
294 			ulong indexVal = c.constants.get(memberIndex).i64;
295 			switch(aggrType.typeKind)
296 			{
297 				case IrTypeKind.array:
298 					IrIndex elemType = getArrayElementType(aggrType);
299 					offset += indexVal * typeSize(elemType);
300 					aggrType = elemType;
301 					break;
302 
303 				case IrTypeKind.struct_t:
304 					IrTypeStructMember[] members = get!IrTypeStruct(aggrType).members;
305 					c.assertf(indexVal < members.length,
306 						"Indexing member %s of %s-member struct",
307 						indexVal, members.length);
308 					IrTypeStructMember member = members[indexVal];
309 					offset += member.offset;
310 					aggrType = member.type;
311 					break;
312 
313 				default:
314 					c.internal_error("Cannot index into %s", aggrType.typeKind);
315 			}
316 		}
317 		return IrTypeStructMember(aggrType, cast(uint)offset);
318 	}
319 
320 	IrIndex getReturnType(IrIndex funcSigType, CompilationContext* c)
321 	{
322 		auto func = &get!IrTypeFunction(funcSigType);
323 		if (func.numResults == 0)
324 			return makeIrType(IrBasicType.void_t);
325 		c.assertf(func.numResults == 1, "getFuncSignatureReturnType on func with %s results", func.numResults);
326 		return func.resultTypes[0];
327 	}
328 
329 	CallConv* getCalleeCallConv(IrIndex callee, IrFunction* ir, CompilationContext* c)
330 	{
331 		if (callee.isFunction)
332 		{
333 			return ir.getCallConv(c);
334 		}
335 		else
336 		{
337 			IrIndex type = getValueType(callee, ir, c);
338 			if (type.isTypePointer)
339 			{
340 				IrIndex base = getPointerBaseType(type);
341 				if (base.isTypeFunction)
342 				{
343 					CallConvention callConv = get!IrTypeFunction(base).callConv;
344 					return callConventions[callConv];
345 				}
346 			}
347 		}
348 		c.internal_error("cannot get call convention %s", callee);
349 	}
350 
351 	bool isSameType(IrIndex a, IrIndex b) {
352 		//writefln("isSameType %s %s", a, b);
353 		assert(a.isType, format("not a type (%s)", a));
354 		assert(b.isType, format("not a type (%s)", b));
355 
356 		if (a == b) return true;
357 
358 		final switch (a.typeKind) {
359 			case IrTypeKind.basic:
360 				return b.typeKind == IrTypeKind.basic && a.typeIndex == b.typeIndex;
361 			case IrTypeKind.pointer:
362 				if (b.typeKind != IrTypeKind.pointer) return false;
363 				auto baseA = getPointerBaseType(a);
364 				auto baseB = getPointerBaseType(b);
365 				//writefln("  ptr %s %s", baseA, baseB);
366 				return isSameType(baseA, baseB);
367 			case IrTypeKind.array:
368 				if (b.typeKind != IrTypeKind.array) return false;
369 				IrTypeArray* arrayA = &get!IrTypeArray(a);
370 				IrTypeArray* arrayB = &get!IrTypeArray(b);
371 				return isSameType(arrayA.elemType, arrayB.elemType) && arrayA.numElements == arrayB.numElements;
372 			case IrTypeKind.struct_t:
373 				if (b.typeKind != IrTypeKind.struct_t) return false;
374 				IrTypeStructMember[] membersA = get!IrTypeStruct(a).members;
375 				IrTypeStructMember[] membersB = get!IrTypeStruct(b).members;
376 				if (membersA.length != membersB.length) return false;
377 				foreach(i, IrTypeStructMember memA; membersA) {
378 					if (!isSameType(memA.type, membersB[i].type)) return false;
379 					if (memA.offset != membersB[i].offset) return false;
380 				}
381 				return true;
382 			case IrTypeKind.func_t:
383 				if (b.typeKind != IrTypeKind.func_t) return false;
384 				IrTypeFunction* funcA = &get!IrTypeFunction(a);
385 				IrTypeFunction* funcB = &get!IrTypeFunction(b);
386 				if (funcA.numResults != funcB.numResults) return false;
387 				if (funcA.numParameters != funcB.numParameters) return false;
388 				foreach(i, IrIndex typeA; funcA.payload.ptr[0..funcA.numParameters+funcA.numParameters]) {
389 					if (!isSameType(typeA, funcB.payload.ptr[i])) return false;
390 				}
391 				return true;
392 		}
393 	}
394 }
395 
396 /// Returns type of value
397 IrIndex getValueType(IrIndex value, IrFunction* ir, CompilationContext* c)
398 	out (res; res.isType, format("Not a type %s -> %s", value, res))
399 {
400 	switch(value.kind) with(IrValueKind)
401 	{
402 		case constant:
403 			return c.constants.get(value).type;
404 		case constantAggregate:
405 			return c.constants.getAggregate(value).type;
406 		case constantZero:
407 			value.kind = IrValueKind.type;
408 			return value;
409 		case global:
410 			IrGlobal* global = c.globals.get(value);
411 			c.assertf(global.type.isDefined, "Global has no type");
412 			return global.type;
413 		case stackSlot:
414 			return ir.getStackSlot(value).type;
415 		case virtualRegister:
416 			return ir.getVirtReg(value).type;
417 		case func:
418 			return c.types.appendPtr(c.getFunction(value).signature.get!FunctionSignatureNode(c).getIrType(c));
419 		default:
420 			c.internal_error("Cannot get type of %s", value.kind);
421 	}
422 }
423 
424 IrArgSize getValueTypeArgSize(IrIndex value, IrFunction* ir, CompilationContext* context)
425 {
426 	if (value.isPhysReg) return cast(IrArgSize)value.physRegSize;
427 	IrIndex type = getValueType(value, ir, context);
428 	return sizeToIrArgSize(context.types.typeSize(type), context);
429 }
430 
431 IrArgSize getTypeArgSize(IrIndex type, CompilationContext* context)
432 {
433 	return sizeToIrArgSize(context.types.typeSize(type), context);
434 }