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 }