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