1 module vayne.code.emitter;
2 
3 
4 import std.algorithm;
5 import std.array;
6 import std.string;
7 
8 import vayne.ast.node;
9 import vayne.ast.printer;
10 import vayne.op;
11 import vayne.source.source;
12 import vayne.source.token;
13 
14 
15 private alias Value = Operand;
16 
17 
18 enum Guard = q{
19 	auto __refs = reduce!((a, b) => a + b)(0, refs_);
20 	scope (success) {
21 		auto refs = reduce!((a, b) => a + b)(0, refs_);
22 		assert((refs == __refs) || (refs == 1 + __refs), "leaking registers!");
23 	}
24 	auto __scopes = scopes_.length;
25 	scope (success) {
26 		auto scopes = scopes_.length;
27 		assert(scopes == __scopes, "leaking scopes!");
28 	}
29 };
30 
31 
32 struct Emitter {
33 	auto instrs() const {
34 		return instrs_;
35 	}
36 
37 	auto locs() const {
38 		return locs_;
39 	}
40 
41 	auto registerCount() const {
42 		return registers_;
43 	}
44 
45 	auto constantCount() const {
46 		return cast(uint)constants_.length;
47 	}
48 
49 	static struct ConstantSlot {
50 		enum Type : ubyte {
51 			Null,
52 			Boolean,
53 			String,
54 			Integer,
55 			Float,
56 		}
57 
58 		Type type;
59 		string value;
60 	}
61 
62 	auto constants() const {
63 		return constants_;
64 	}
65 
66 	void emitModule(Module node) {
67 		assert(scopes_.empty);
68 		assert(registers_ == 0);
69 
70 		foreach (child; node.children)
71 			emitStatement(child);
72 		if (!instrs_.empty)
73 			emit(OpCode.Nop, locs_.back);
74 
75 		assert(scopes_.empty);
76 		assert(!registers_ || reduce!((a, b) => a + b)(0, refs_) == 0);
77 		assert(registers_ == frees_.length);
78 	}
79 
80 private:
81 	Value emitExpression(Node node) {
82 		debug mixin(Guard);
83 
84 		assert(node !is null);
85 		if (auto expr = cast(Expression)node) {
86 			return emitExpression(expr.children[0]);
87 		} else if (auto konst = cast(Constant)node) {
88 			return emitConstant(konst);
89 		} else if (auto ident = cast(Identifier)node) {
90 			return emitIdentifier(ident);
91 		} else if (auto binop = cast(BinaryOp)node) {
92 			return emitBinaryOp(binop);
93 		} else if (auto uniop = cast(UnaryOp)node) {
94 			return emitUnaryOp(uniop);
95 		} else if (auto preop = cast(PrefixOp)node) {
96 			return emitPrefixOp(preop);
97 		} else if (auto sufop = cast(SuffixOp)node) {
98 			return emitSuffixOp(sufop);
99 		} else if (auto condexpr = cast(ConditionalExpression)node) {
100 			return emitConditionalExpression(condexpr);
101 		} else if (auto indexop = cast(IndexOp)node) {
102 			return emitIndexOp(indexop);
103 		} else if (auto sliceop = cast(SliceOp)node) {
104 			return emitSliceOp(sliceop);
105 		} else if (auto dispatchop = cast(DispatchOp)node) {
106 			return emitDispatchOp(dispatchop);
107 		} else if (auto call = cast(FunctionCall)node) {
108 			return emitFunctionCall(call);
109 		} else {
110 			assert(false, "unimplemented expression " ~ typeid(node).toString);
111 		}
112 	}
113 
114 	auto emitConstant(Constant node) {
115 		debug mixin(Guard);
116 
117 		assert((node.tok.kind == Token.Kind.Literal) || (node.tok.kind == Token.Kind.Keyword));
118 
119 		if (node.tok.kind == Token.Kind.Literal) {
120 			final switch (node.tok.kindLiteral) with (Token.LiteralKind) {
121 			case Char:
122 			case String:
123 				return constant(ConstantSlot.Type.String, node.tok.unescaped);
124 			case Bin:
125 				return constant(ConstantSlot.Type.Integer, node.tok.value);
126 			case Oct:
127 				return constant(ConstantSlot.Type.Integer, node.tok.value);
128 			case Dec:
129 				return constant(ConstantSlot.Type.Integer, node.tok.value);
130 			case Hex:
131 				return constant(ConstantSlot.Type.Integer, node.tok.value);
132 			case Float:
133 				return constant(ConstantSlot.Type.Float, node.tok.value);
134 			}
135 		} else {
136 			final switch (node.tok.kindKeyword) with (Token.KeywordKind) {
137 			case True:
138 				return constant(ConstantSlot.Type.Boolean, "true");
139 			case False:
140 				return constant(ConstantSlot.Type.Boolean, "false");
141 			case Null:
142 				return constant(ConstantSlot.Type.Null, "null");
143 			case In:
144 			case Set:
145 			case Def:
146 			case Undef:
147 			case Push:
148 			case Pop:
149 			case As:
150 				assert(false, "unimplemented keyword kind constant " ~ node.tok.kindKeyword);
151 			}
152 		}
153 	}
154 
155 	auto emitIdentifier(Identifier node) {
156 		debug mixin(Guard);
157 
158 		return findSymbol(node.tok.value, node.tok.loc);
159 	}
160 
161 	auto emitUnaryOp(UnaryOp node) {
162 		debug mixin(Guard);
163 
164 		auto expr = emitExpression(node.children[0]);
165 		scope(exit) release(expr);
166 
167 		switch (node.tok.name) {
168 		case "-":
169 			auto target = register();
170 			emit(OpCode.Move, node.tok.loc, target, expr);
171 			emit(OpCode.Minus, node.tok.loc, target);
172 			return target;
173 		case "!":
174 			auto target = register();
175 			emit(OpCode.Move, node.tok.loc, target, expr);
176 			emit(OpCode.Not, node.tok.loc, target);
177 			return target;
178 		case "+":
179 			return expr;
180 		default:
181 			assert(false, "unimplemented unary op " ~ node.tok.toString);
182 		}
183 	}
184 
185 	auto emitPrefixOp(PrefixOp node) {
186 		debug mixin(Guard);
187 
188 		auto expr = registerize(node.tok.loc, emitExpression(node.children[0]));
189 		scope(exit) release(expr);
190 
191 		switch (node.tok.name) {
192 		case "++":
193 			auto target = register();
194 			emit(OpCode.Increment, node.tok.loc, expr);
195 			emit(OpCode.Move, node.tok.loc, target, expr);
196 			return target;
197 		case "--":
198 			auto target = register();
199 			emit(OpCode.Decrement, node.tok.loc, expr);
200 			emit(OpCode.Move, node.tok.loc, target, expr);
201 			return target;
202 		default:
203 			assert(false, "unimplemented prefix op " ~ node.tok.toString);
204 		}
205 	}
206 
207 	auto emitSuffixOp(SuffixOp node) {
208 		debug mixin(Guard);
209 
210 		auto expr = registerize(node.tok.loc, emitExpression(node.children[0]));
211 		scope(exit) release(expr);
212 
213 		switch (node.tok.name) {
214 		case "++":
215 			auto target = register();
216 			emit(OpCode.Move, node.tok.loc, target, expr);
217 			emit(OpCode.Increment, node.tok.loc, expr);
218 			return target;
219 		case "--":
220 			auto target = register();
221 			emit(OpCode.Move, node.tok.loc, target, expr);
222 			emit(OpCode.Decrement, node.tok.loc, expr);
223 			return target;
224 		default:
225 			assert(false, "unimplemented suffix op " ~ node.tok.toString);
226 		}
227 	}
228 
229 	auto emitBinaryOp(BinaryOp node) {
230 		debug mixin(Guard);
231 
232 		{
233 			switch (node.tok.name) {
234 			case "&&":
235 			case "||":
236 				auto target = register();
237 				auto lhs = emitExpression(node.children[0]);
238 				emit(OpCode.Test, node.tok.loc, target, lhs);
239 				release(lhs);
240 
241 				size_t jumpShortCircuit = placeholder(node.tok.loc);
242 
243 				auto rhs = emitExpression(node.children[1]);
244 				emit(OpCode.Test, node.tok.loc, target, rhs);
245 				release(rhs);
246 
247 				emitAt((node.tok.name == "&&") ? OpCode.JumpIfZero : OpCode.JumpIfNotZero, node.tok.loc, jumpShortCircuit, Value(Value.Kind.Immediate, ip), target);
248 				return target;
249 			default:
250 				break;
251 			}
252 		}
253 
254 		{
255 			auto lhs = emitExpression(node.children[0]);
256 			auto rhs = emitExpression(node.children[1]);
257 			scope (exit) release(lhs, rhs);
258 
259 			auto target = register();
260 
261 			switch (node.tok.name) {
262 			case "==":
263 				emit(OpCode.Equal, node.tok.loc, target, lhs, rhs);
264 				break;
265 			case "!=":
266 				emit(OpCode.NotEqual, node.tok.loc, target, lhs, rhs);
267 				break;
268 			case ">":
269 				emit(OpCode.Greater, node.tok.loc, target, lhs, rhs);
270 				break;
271 			case ">=":
272 				emit(OpCode.GreaterOrEqual, node.tok.loc, target, lhs, rhs);
273 				break;
274 			case "<":
275 				emit(OpCode.Less, node.tok.loc, target, lhs, rhs);
276 				break;
277 			case "<=":
278 				emit(OpCode.LessOrEqual, node.tok.loc, target, lhs, rhs);
279 				break;
280 			case "+":
281 				emit(OpCode.Add, node.tok.loc, target, lhs, rhs);
282 				break;
283 			case "-":
284 				emit(OpCode.Subtract, node.tok.loc, target, lhs, rhs);
285 				break;
286 			case "*":
287 				emit(OpCode.Multiply, node.tok.loc, target, lhs, rhs);
288 				break;
289 			case "/":
290 				emit(OpCode.Divide, node.tok.loc, target, lhs, rhs);
291 				break;
292 			case "%":
293 				emit(OpCode.Remainder, node.tok.loc, target, lhs, rhs);
294 				break;
295 			case "~":
296 				emit(OpCode.Concat, node.tok.loc, target, lhs, rhs);
297 				break;
298 			case "^^":
299 				emit(OpCode.Power, node.tok.loc, target, lhs, rhs);
300 				break;
301 			case "in":
302 				emit(OpCode.TestKey, node.tok.loc, target, rhs, lhs);
303 				break;
304 			default:
305 				assert(false, "unimplemented binary op " ~ node.tok.toString);
306 			}
307 
308 			return target;
309 		}
310 	}
311 
312 	auto emitConditionalExpression(ConditionalExpression node) {
313 		debug mixin(Guard);
314 
315 		auto target = register();
316 
317 		auto cond = emitExpression(node.children[0]);
318 		scope(exit) release(cond);
319 
320 		emit(OpCode.Test, node.tok.loc, target, cond);
321 		auto jz = placeholder(node.tok.loc);
322 
323 		auto trueCase = emitExpression(node.children[1]);
324 		emit(OpCode.Move, node.tok.loc, target, trueCase);
325 		release(trueCase);
326 
327 		auto jmp = placeholder(node.tok.loc);
328 		auto ipfalse = ip;
329 
330 		auto falseCase = emitExpression(node.children[2]);
331 		emit(OpCode.Move, node.tok.loc, target, falseCase);
332 		release(falseCase);
333 
334 		emitAt(OpCode.JumpIfZero, node.tok.loc, jz, Value(Value.Kind.Immediate, ipfalse), target);
335 		emitAt(OpCode.Jump, node.tok.loc, jmp, Value(Value.Kind.Immediate, ip));
336 
337 		assert(!canFind(frees_, target.value));
338 		assert(refs_[target.value] > 0);
339 
340 		return target;
341 	}
342 
343 	auto emitIndexOp(IndexOp node) {
344 		debug mixin(Guard);
345 
346 		auto target = registerize(node.tok.loc, emitExpression(node.children[0]));
347 		auto index = emitExpression(node.children[1]);
348 		scope(exit) release(index);
349 
350 		emit(OpCode.Element, node.tok.loc, target, target, index);
351 		return target;
352 	}
353 
354 	auto emitSliceOp(SliceOp node) {
355 		debug mixin(Guard);
356 
357 		auto target = registerize(node.tok.loc, emitExpression(node.children[0]));
358 		auto start = emitExpression(node.children[1]);
359 		auto end = emitExpression(node.children[2]);
360 		scope (exit) release(start, end);
361 
362 		emit(OpCode.Slice, node.tok.loc, target, target, start, end);
363 		return target;
364 	}
365 
366 	auto emitDispatchOp(DispatchOp node) {
367 		debug mixin(Guard);
368 
369 		auto obj = emitExpression(node.children[0]);
370 		scope (exit) release(obj);
371 
372 		auto key = constant(ConstantSlot.Type.String, node.target.value);
373 
374 		auto target = register();
375 		emit(OpCode.Element, node.tok.loc, target, obj, key);
376 		return target;
377 	}
378 
379 	auto emitDispatchOpForCall(DispatchOp node, Value obj) {
380 		auto expr = emitExpression(node.children[0]);
381 		scope (exit) release(expr);
382 
383 		auto key = constant(ConstantSlot.Type.String, node.target.value);
384 
385 		emit(OpCode.Move, node.tok.loc, obj, expr);
386 
387 		auto target = register();
388 		emit(OpCode.Dispatch, node.tok.loc, target, expr, key);
389 		return target;
390 	}
391 
392 	auto emitFunctionCall(FunctionCall node) {
393 		debug mixin(Guard);
394 
395 		auto result = register();
396 		auto dispatched = (cast(DispatchOp)node.children[0] !is null);
397 		auto args = node.children[1..$];
398 
399 		if (!dispatched) {
400 			auto func = emitExpression(node.children[0]);
401 			scope (exit) release(func);
402 
403 			if (args.length) {
404 				auto base = registers(args.length);
405 
406 				foreach (i, arg; args) {
407 					auto argValue = emitExpression(arg);
408 					emit(OpCode.Move, node.tok.loc, Value(Value.Kind.Register, base.value + cast(uint)i), argValue);
409 					release(argValue);
410 				}
411 
412 				emit(OpCode.Call, node.tok.loc, result, func, base, Value(Value.Kind.Immediate, cast(uint)args.length));
413 
414 				foreach (i; 0..args.length)
415 					release(Value(Value.Kind.Register, base.value + cast(uint)i));
416 			} else {
417 				emit(OpCode.Call, node.tok.loc, result, func, Value(Value.Kind.Register, 0), Value(Value.Kind.Immediate, 0));
418 			}
419 		} else {
420 			auto base = registers(1 + args.length);
421 
422 			foreach (i, arg; args) {
423 				auto argValue = emitExpression(arg);
424 				emit(OpCode.Move, node.tok.loc, Value(Value.Kind.Register, base.value + 1 + cast(uint)i), argValue);
425 				release(argValue);
426 			}
427 
428 			auto func = emitDispatchOpForCall(cast(DispatchOp)node.children[0], base);
429 			scope(exit) release(func);
430 			emit(OpCode.DispatchCall, node.tok.loc, result, func, base, Value(Value.Kind.Immediate, 1 + cast(uint)args.length));
431 
432 			foreach (i; 0..args.length + 1)
433 				release(Value(Value.Kind.Register, base.value + cast(uint)i));
434 		}
435 		return result;
436 	}
437 
438 	void emitOutput(Output node) {
439 		debug mixin(Guard);
440 
441 		auto expr = emitExpression(node.children[0]);
442 		emit(OpCode.Output, node.tok.loc, expr);
443 		release(expr);
444 	}
445 
446 	void emitStatement(Node node) {
447 		debug mixin(Guard);
448 
449 		if (auto output = cast(Output)node) {
450 			emitOutput(output);
451 		} else if (auto ifstmt = cast(IfStatement)node) {
452 			emitIfStatement(ifstmt);
453 		} else if (auto loopstmt = cast(LoopStatement)node) {
454 			emitLoopStatement(loopstmt);
455 		} else if (auto call = cast(FunctionCall)node) {
456 			release(emitFunctionCall(call));
457 		} else if (auto withstmt = cast(WithStatement)node) {
458 			emitWithStatement(withstmt);
459 		} else if (auto stmtblock = cast(StatementBlock)node) {
460 			emitStatementBlock(stmtblock);
461 		} else {
462 			assert(false, "unimplemented statement " ~ node.tok.toString);
463 		}
464 	}
465 
466 	void emitStatementBlock(StatementBlock node) {
467 		debug mixin(Guard);
468 
469 		foreach (child; node.children)
470 			emitStatement(child);
471 	}
472 
473 	void emitWithStatement(WithStatement node) {
474 		debug mixin(Guard);
475 
476 		Value[] exprs;
477 		exprs.reserve(node.children.length - 1);
478 
479 		pushScope();
480 
481 		uint scopes;
482 		foreach (i, childNode; node.children[0..$-1]) {
483 			auto child = cast(WithExpression)childNode;
484 			assert(child !is null);
485 
486 			auto expr = emitExpression(child.children[0]);
487 
488 			if (child.name.empty) {
489 				++scopes;
490 				emit(OpCode.PushScope, child.tok.loc,  expr);
491 			} else {
492 				auto copy = register();
493 				scope(exit) release(copy);
494 
495 				emit(OpCode.Move, child.tok.loc, copy, expr);
496 				swap(copy, expr);
497 
498 				addSymbol(child.name.value, aquire(expr));
499 			}
500 
501 			exprs ~= expr;
502 		}
503 
504 		emitStatement(node.children[$ - 1]);
505 		if (scopes)
506 			emit(OpCode.PopScope, node.tok.loc, Value(Value.Kind.Immediate, scopes));
507 
508 		foreach (expr; exprs)
509 			release(expr);
510 
511 		popScope();
512 	}
513 
514 	void emitIfStatement(IfStatement node) {
515 		debug mixin(Guard);
516 
517 		auto expr = emitExpression(node.children[0]);
518 		auto cond = register();
519 
520 		emit(OpCode.Test, node.tok.loc, cond, expr);
521 		release(expr);
522 
523 		size_t jumpTrueCase = placeholder(node.tok.loc);
524 		release(cond);
525 
526 		assert(canFind(frees_, cond.value));
527 		assert(refs_[cond.value] == 0);
528 
529 		emitStatement(node.children[1]);
530 
531 		size_t jumpFalseCase;
532 		if (node.children[2] !is null)
533 			jumpFalseCase = placeholder(node.tok.loc);
534 
535 		emitAt(OpCode.JumpIfZero, node.tok.loc, jumpTrueCase, Value(Value.Kind.Immediate, ip), cond);
536 		if (node.children[2] !is null) {
537 			emitStatement(node.children[2]);
538 			emitAt(OpCode.Jump, node.tok.loc, jumpFalseCase, Value(Value.Kind.Immediate, ip));
539 		}
540 	}
541 
542 	void emitLoopStatement(LoopStatement node) {
543 		debug mixin(Guard);
544 
545 		auto body_ = cast(StatementBlock)node.children[2];
546 		assert(body_ !is null);
547 
548 		pushScope();
549 
550 		if (node.children[1]) {
551 			// numeric range
552 			auto end = emitExpression(node.children[1]);
553 			auto it = registerize(node.tok.loc, emitExpression(node.children[0]));
554 			auto value = it;
555 
556 			if (!node.key.empty)
557 				addSymbol(node.key.value, aquire(it));
558 			if (!node.value.empty)
559 				addSymbol(node.value.value, aquire(value));
560 
561 			auto test = ip;
562 			auto cond = register();
563 			emit(OpCode.Less, node.tok.loc, cond, it, end);
564 			size_t jumpBody = placeholder(node.tok.loc);
565 			release(cond);
566 
567 			emitStatementBlock(body_);
568 			emit(OpCode.Increment, node.tok.loc, it);
569 			emit(OpCode.Jump, node.tok.loc, Value(Value.Kind.Immediate, test));
570 			emitAt(OpCode.JumpIfZero, node.tok.loc, jumpBody, Value(Value.Kind.Immediate, ip), cond);
571 			release(it, end);
572 		} else {
573 			// array / object
574 			auto obj = emitExpression(node.children[0]);
575 			auto keys = register();
576 			auto len = register();
577 
578 			emit(OpCode.Keys, node.tok.loc, keys, obj);
579 			emit(OpCode.Length, node.tok.loc, len, keys);
580 
581 			auto it = registerize(node.tok.loc, constant(ConstantSlot.Type.Integer, "0"));
582 			auto key = register();
583 			auto value = register();
584 
585 			if (!node.key.empty)
586 				addSymbol(node.key.value, aquire(key));
587 			if (!node.value.empty)
588 				addSymbol(node.value.value, aquire(value));
589 
590 			auto test = ip;
591 			auto cond = register();
592 			emit(OpCode.Less, node.tok.loc, cond, it, len);
593 			size_t jumpBody = placeholder(node.tok.loc);
594 			release(cond);
595 
596 			emit(OpCode.Element, node.tok.loc, key, keys, it);
597 			emit(OpCode.Element, node.tok.loc, value, obj, key);
598 
599 			emitStatementBlock(body_);
600 			emit(OpCode.Increment, node.tok.loc, it);
601 			emit(OpCode.Jump, node.tok.loc, Value(Value.Kind.Immediate, test));
602 			emitAt(OpCode.JumpIfZero, node.tok.loc, jumpBody, Value(Value.Kind.Immediate, ip), cond);
603 			release(it, keys, len, obj, key, value);
604 		}
605 
606 		popScope();
607 	}
608 
609 	Value register() {
610 		if (frees_.empty) {
611 			refs_ ~= cast(size_t)1;
612 			return Value(Value.Kind.Register, registers_++);
613 		}
614 		auto free = frees_.back;
615 		frees_.popBack;
616 
617 		assert(free < registers_);
618 		assert(refs_[free] == 0);
619 		++refs_[free];
620 		return Value(Value.Kind.Register, free);
621 	}
622 
623 	auto registers(size_t count) {
624 		if (count == 1) {
625 			return register();
626 		} else if (count > 1) {
627 			auto index = registers_;
628 
629 			if (frees_.length >= count) {
630 				frees_.sort();
631 
632 				uint start = 0;
633 				while (start + count <= frees_.length) {
634 					if (frees_[start + count - 1] == frees_[start] + count - 1)
635 						break;
636 					++start;
637 				}
638 
639 				if (start + count <= frees_.length) {
640 					index = frees_[start];
641 
642 					frees_ = frees_[0..start] ~ frees_[start + count..$]; // TODO: find starting from the end, then manually copy + pop
643 				}
644 			}
645 
646 			if (index == registers_) {
647 				registers_ += count;
648 				refs_.length = registers_;
649 			}
650 
651 			foreach (i; index..index + count) {
652 				assert(refs_[i] == 0);
653 				refs_[i] = 1;
654 			}
655 
656 			return Value(Value.Kind.Register, index);
657 		}
658 
659 		return Value(Value.Kind.Register, uint.max);
660 	}
661 
662 	auto aquire(Value v) {
663 		assert(!frees_.canFind(v.value), format("aquiring freed register %s", v.value));
664 		if (v.kind == Value.Kind.Register)
665 			++refs_[v.value];
666 		return v;
667 	}
668 
669 	void release(Values...)(Values values) {
670 		foreach (v; values) {
671 			if (v.kind == Value.Kind.Register) {
672 				if (refs_[v.value] > 0) {
673 					--refs_[v.value];
674 					if (refs_[v.value] == 0) {
675 						assert(!frees_.canFind(v.value), format("double releasing register %s", v.value));
676 						frees_ ~= v.value;
677 					}
678 				} else {
679 					assert(false, format("double releasing register %s", v.value));
680 				}
681 			}
682 		}
683 	}
684 
685 	auto constant(ConstantSlot.Type type, string value) {
686 		auto map = indexConstants_[type];
687 		if (auto pvalue = value in map)
688 			return Value(Value.Kind.Constant, *pvalue);
689 
690 		auto slot = cast(uint)constants_.length;
691 		indexConstants_[type][value] = slot;
692 		constants_ ~= ConstantSlot(type, value);
693 
694 		return Value(Value.Kind.Constant, slot);
695 	}
696 
697 	auto registerize(SourceLoc loc, Value value) {
698 		if (value.kind != Value.Kind.Register) {
699 			assert(value.kind == Value.Kind.Constant);
700 
701 			auto target = register();
702 			emit(OpCode.Move, loc, target, value);
703 			return target;
704 		}
705 		return value;
706 	}
707 
708 	auto placeholder(SourceLoc loc) {
709 		instrs_ ~= Instr(OpCode.Nop);
710 		locs_ ~= loc;
711 		return cast(uint)instrs_.length - 1;
712 	}
713 
714 	auto ip() const {
715 		return cast(uint)instrs_.length;
716 	}
717 
718 	auto emit(Args...)(OpCode op, SourceLoc loc, Args args) {
719 		instrs_ ~= Instr(op, args);
720 		locs_ ~= loc;
721 		return instrs_.length - 1;
722 	}
723 
724 	auto emitAt(Args...)(OpCode op, SourceLoc loc, size_t at, Args args) {
725 		instrs_[at] = Instr(op, args);
726 		locs_[at] = loc;
727 	}
728 
729 	void pushScope() {
730 		++scopes_.length;
731 	}
732 
733 	void addSymbol(string name, Value value) {
734 		assert(!name.empty);
735 
736 		foreach_reverse(s; scopes_) {
737 			if (auto pvalue = name in s)
738 				throw new Exception(format("symbol %s shadows symbol with same name %s", name, name));
739 		}
740 		scopes_.back[name] = value;
741 	}
742 
743 	Value findSymbol(string name, SourceLoc loc) {
744 		foreach_reverse (s; scopes_) {
745 			if (auto pvalue = name in s)
746 				return aquire(*pvalue);
747 		}
748 
749 		auto result = register();
750 		emit(OpCode.LookUp, loc, result, constant(ConstantSlot.Type.String, name));
751 		return result;
752 	}
753 
754 	void popScope() {
755 		foreach (k, v; scopes_.back)
756 			release(v);
757 		--scopes_.length;
758 	}
759 
760 	uint[] frees_;
761 	uint[] refs_;
762 	uint registers_;
763 
764 	Value[string][] scopes_;
765 
766 	ConstantSlot[] constants_;
767 	uint[string][5] indexConstants_;
768 
769 	Instr[] instrs_;
770 	SourceLoc[] locs_;
771 }