1 module d.ir.instruction; 2 3 import source.context; 4 import source.location; 5 import source.name; 6 7 import d.ir.expression; 8 import d.ir.symbol; 9 10 struct Body { 11 private: 12 BasicBlock[] basicBlocks; 13 14 public: 15 ref inout(BasicBlock) opIndex(BasicBlockRef i) inout in { 16 assert(i, "null block ref"); 17 assert(i.index <= basicBlocks.length, "Out of bounds block ref"); 18 } do { 19 return basicBlocks.ptr[i.index - 1]; 20 } 21 22 bool opCast(T : bool)() const { 23 return basicBlocks.length > 0; 24 } 25 26 @property length() const { 27 return basicBlocks.length; 28 } 29 30 // XXX: Required due to conversion rules. 31 BasicBlockRef newBasicBlock(Name name) { 32 BasicBlockRef landingpad; 33 return newBasicBlock(name, landingpad); 34 } 35 36 BasicBlockRef newBasicBlock(Name name, BasicBlockRef landingpad) { 37 auto i = cast(uint) basicBlocks.length; 38 auto bbref = BasicBlockRef(i); 39 // XXX: We can't happend because it is non copyable (DMD bug, IMO). 40 // basicBlocks ~= BasicBlock(bbref); 41 basicBlocks.length += 1; 42 basicBlocks[i] = BasicBlock(name, landingpad); 43 return bbref; 44 } 45 46 void dump(const Context c) const { 47 foreach(ref b; basicBlocks) { 48 dump(c, b); 49 } 50 } 51 52 void dump(const Context c, const ref BasicBlock b) const { 53 import std.stdio; 54 writeln(); 55 dumpBasicBlockName(c, b); 56 write(':'); 57 58 if (b.landingpad) { 59 import std.stdio; 60 write("\t\t\t\tunwind to "); 61 dumpBasicBlockName(c, this[b.landingpad]); 62 } 63 64 writeln(); 65 66 foreach(ref i; b.instructions) { 67 dump(c, b, i); 68 } 69 70 final switch(b.terminator) with(Terminator) { 71 case None: 72 writeln("unterminated..."); 73 break; 74 75 case Branch: 76 write("\tbranch "); 77 if (b.value is null) { 78 dumpBasicBlockName(c, this[b.successors[0]]); 79 } else { 80 write(b.value.toString(c), ", "); 81 dumpBasicBlockName(c, this[b.successors[0]]); 82 write(", "); 83 dumpBasicBlockName(c, this[b.successors[1]]); 84 } 85 writeln(); 86 break; 87 88 case Switch: 89 writeln("\tswitch ", b.value.toString(c)); 90 write("\t\tdefault: "); 91 dumpBasicBlockName(c, this[b.switchTable.defaultBlock]); 92 foreach(ce; b.switchTable.cases) { 93 write("\n\t\tcase ", ce.value, ": "); 94 dumpBasicBlockName(c, this[ce.block]); 95 } 96 writeln(); 97 break; 98 99 case Return: 100 writeln("\treturn ", b.value ? b.value.toString(c) : "void"); 101 break; 102 103 case Throw: 104 if (b.value) { 105 writeln("\tthrow ", b.value.toString(c)); 106 } else if (b.catchTable) { 107 write("\tcatch"); 108 foreach(ca; b.catchTable.catches) { 109 write("\n\t\t", ca.type.name.toString(c), ": "); 110 dumpBasicBlockName(c, this[ca.block]); 111 } 112 writeln(); 113 } else { 114 writeln("\trethrow"); 115 } 116 break; 117 118 case Halt: 119 writeln("\thalt ", b.value ? b.value.toString(c) : ""); 120 break; 121 } 122 } 123 124 void dumpBasicBlockName(const Context c, const ref BasicBlock b) const { 125 size_t index = (cast(size_t) &b - cast(size_t) basicBlocks.ptr) / BasicBlock.sizeof; 126 127 import std.stdio; 128 write(b.name.toString(c), index); 129 } 130 131 void dump(const Context c, const ref BasicBlock b, const ref Instruction i) const { 132 import std.stdio; 133 final switch(i.op) with(OpCode) { 134 case Alloca: 135 writeln("\t", i.var.toString(c)); 136 break; 137 138 case Destroy: 139 writeln("\tdestroy\t", i.var.name.toString(c)); 140 break; 141 142 case Evaluate: 143 writeln("\t", i.expr.toString(c)); 144 break; 145 146 case Declare: 147 writeln("\t", i.sym.toString(c)); 148 break; 149 } 150 } 151 } 152 153 auto range(const Body fbody) { 154 import std.algorithm, std.range; 155 return iota(0, cast(uint) fbody.basicBlocks.length).map!(i => BasicBlockRef(i)); 156 } 157 158 struct BasicBlockRef { 159 private: 160 uint index; 161 162 this(uint index) { 163 this.index = index + 1; 164 } 165 166 public: 167 bool opCast(T : bool)() const { 168 return index != 0; 169 } 170 171 BasicBlockRef opAssign(typeof(null)) { 172 index = 0; 173 return this; 174 } 175 } 176 177 auto range(inout Body fbody, BasicBlockRef b) { 178 return fbody[b].instructions; 179 } 180 181 enum Terminator { 182 None, 183 Branch, 184 Switch, 185 Return, 186 Throw, 187 Halt, 188 } 189 190 struct BasicBlock { 191 private: 192 Instruction[] instructions; 193 194 import source.name; 195 Name _name; 196 197 Terminator _terminator; 198 union { 199 public BasicBlockRef[2] successors; 200 public SwitchTable* switchTable; 201 public CatchTable* catchTable; 202 } 203 204 // We need to win a uint in there so that this is 4 pointers sized. 205 // We probably won't need more than 4G instructions, so we can win 206 // int he instruction array length. 207 BasicBlockRef _landingpad; 208 209 // XXX: Let's say I'm not happy with that layout. 210 public Location location; 211 public Expression value; 212 213 this(Name name, BasicBlockRef landingpad) { 214 _name = name; 215 _landingpad = landingpad; 216 } 217 218 @disable this(this); 219 220 void add(Instruction i) in { 221 assert(!terminate, "block does terminate already"); 222 } do { 223 instructions ~= i; 224 } 225 226 public: 227 @property name() const { 228 return _name; 229 } 230 231 @property terminator() const { 232 return _terminator; 233 } 234 235 @property terminate() const { 236 return terminator != Terminator.None; 237 } 238 239 @property empty() const { 240 return !terminate && instructions.length == 0; 241 } 242 243 @property landingpad() const { 244 return _landingpad; 245 } 246 247 @property landingpad(BasicBlockRef landingpad) { 248 return _landingpad = landingpad; 249 } 250 251 void alloca(Location location, Variable v) { 252 add(Instruction(location, v)); 253 } 254 255 void destroy(Location location, Variable v) { 256 add(Instruction.destroy(location, v)); 257 } 258 259 void eval(Location location, Expression e) { 260 add(Instruction(location, e)); 261 } 262 263 void declare(Location location, Symbol s) { 264 add(Instruction(location, s)); 265 } 266 267 void branch(Location location, BasicBlockRef dst) { 268 _terminator = Terminator.Branch; 269 successors[0] = dst; 270 } 271 272 void branch( 273 Location location, 274 Expression cond, 275 BasicBlockRef ifTrue, 276 BasicBlockRef ifFalse, 277 ) { 278 _terminator = Terminator.Branch; 279 280 // XXX: ALARM! ALARM! 281 value = cond; 282 successors[0] = ifTrue; 283 successors[1] = ifFalse; 284 } 285 286 void doSwitch(Location location, Expression e, SwitchTable* switchTable) { 287 _terminator = Terminator.Switch; 288 this.location = location; 289 this.value = e; 290 this.switchTable = switchTable; 291 } 292 293 void ret(Location location, Expression e = null) { 294 _terminator = Terminator.Return; 295 this.location = location; 296 value = e; 297 } 298 299 void doThrow(Location location, Expression e = null) { 300 _terminator = Terminator.Throw; 301 this.location = location; 302 value = e; 303 } 304 305 void doCatch(Location location, CatchTable* catchTable) { 306 _terminator = Terminator.Throw; 307 this.location = location; 308 this.catchTable = catchTable; 309 } 310 311 void halt(Location location, Expression msg = null) { 312 _terminator = Terminator.Halt; 313 this.location = location; 314 value = msg; 315 } 316 } 317 318 enum OpCode { 319 Alloca, 320 Destroy, 321 Evaluate, 322 323 // FIXME: This is unecessary, but will makes things easier for now. 324 Declare, 325 } 326 327 struct Instruction { 328 Location location; 329 330 OpCode op; 331 332 union { 333 Expression expr; 334 Variable var; 335 Symbol sym; 336 } 337 338 private: 339 this(Location location, Expression e) { 340 this.location = location; 341 op = OpCode.Evaluate; 342 expr = e; 343 } 344 345 this(Location location, Variable v) in { 346 assert(v.step == Step.Processed, "Variable is not processed"); 347 } do { 348 this.location = location; 349 op = OpCode.Alloca; 350 var = v; 351 } 352 353 static destroy(Location location, Variable v) { 354 auto i = Instruction(location, v); 355 i.op = OpCode.Destroy; 356 return i; 357 } 358 359 this(Location location, Symbol s) in { 360 assert(s.step == Step.Processed, "Symbol is not processed"); 361 assert(!cast(Variable) s, "Use alloca for variables"); 362 } do { 363 this.location = location; 364 op = OpCode.Declare; 365 sym = s; 366 } 367 } 368 369 struct SwitchTable { 370 BasicBlockRef defaultBlock; 371 uint entryCount; 372 373 @property inout(CaseEntry)[] cases() inout { 374 return (cast(inout(CaseEntry)*) &(&this)[1])[0 .. entryCount]; 375 } 376 } 377 378 struct CaseEntry { 379 BasicBlockRef block; 380 uint value; 381 } 382 383 struct CatchTable { 384 size_t catchCount; 385 BasicBlockRef catchAll; 386 387 @property inout(CatchPad)[] catches() inout { 388 return (cast(inout(CatchPad)*) &(&this)[1])[0 .. catchCount]; 389 } 390 } 391 392 struct CatchPad { 393 Class type; 394 BasicBlockRef block; 395 }