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 }