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