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 }