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 }