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 }