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 }