OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
11 // with the distribution. | 11 // with the distribution. |
12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
15 // | 15 // |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 #include "v8.h" | 28 #include "v8.h" |
29 | 29 |
| 30 #if defined(V8_TARGET_ARCH_X64) |
| 31 |
30 #include "codegen-inl.h" | 32 #include "codegen-inl.h" |
31 #include "compiler.h" | 33 #include "compiler.h" |
32 #include "debug.h" | 34 #include "debug.h" |
33 #include "full-codegen.h" | 35 #include "full-codegen.h" |
34 #include "parser.h" | 36 #include "parser.h" |
35 #include "scopes.h" | 37 #include "scopes.h" |
36 | 38 |
37 namespace v8 { | 39 namespace v8 { |
38 namespace internal { | 40 namespace internal { |
39 | 41 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); | 74 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); |
73 for (int i = 0; i < locals_count; i++) { | 75 for (int i = 0; i < locals_count; i++) { |
74 __ push(rdx); | 76 __ push(rdx); |
75 } | 77 } |
76 } | 78 } |
77 } | 79 } |
78 | 80 |
79 bool function_in_register = true; | 81 bool function_in_register = true; |
80 | 82 |
81 // Possibly allocate a local context. | 83 // Possibly allocate a local context. |
82 if (scope()->num_heap_slots() > 0) { | 84 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; |
| 85 if (heap_slots > 0) { |
83 Comment cmnt(masm_, "[ Allocate local context"); | 86 Comment cmnt(masm_, "[ Allocate local context"); |
84 // Argument to NewContext is the function, which is still in rdi. | 87 // Argument to NewContext is the function, which is still in rdi. |
85 __ push(rdi); | 88 __ push(rdi); |
86 __ CallRuntime(Runtime::kNewContext, 1); | 89 if (heap_slots <= FastNewContextStub::kMaximumSlots) { |
| 90 FastNewContextStub stub(heap_slots); |
| 91 __ CallStub(&stub); |
| 92 } else { |
| 93 __ CallRuntime(Runtime::kNewContext, 1); |
| 94 } |
87 function_in_register = false; | 95 function_in_register = false; |
88 // Context is returned in both rax and rsi. It replaces the context | 96 // Context is returned in both rax and rsi. It replaces the context |
89 // passed to us. It's saved in the stack and kept live in rsi. | 97 // passed to us. It's saved in the stack and kept live in rsi. |
90 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); | 98 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); |
91 | 99 |
92 // Copy any necessary parameters into the context. | 100 // Copy any necessary parameters into the context. |
93 int num_parameters = scope()->num_parameters(); | 101 int num_parameters = scope()->num_parameters(); |
94 for (int i = 0; i < num_parameters; i++) { | 102 for (int i = 0; i < num_parameters; i++) { |
95 Slot* slot = scope()->parameter(i)->slot(); | 103 Slot* slot = scope()->parameter(i)->slot(); |
96 if (slot != NULL && slot->type() == Slot::CONTEXT) { | 104 if (slot != NULL && slot->type() == Slot::CONTEXT) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 // Store new arguments object in both "arguments" and ".arguments" slots. | 144 // Store new arguments object in both "arguments" and ".arguments" slots. |
137 __ movq(rcx, rax); | 145 __ movq(rcx, rax); |
138 Move(arguments->slot(), rax, rbx, rdx); | 146 Move(arguments->slot(), rax, rbx, rdx); |
139 Slot* dot_arguments_slot = | 147 Slot* dot_arguments_slot = |
140 scope()->arguments_shadow()->AsVariable()->slot(); | 148 scope()->arguments_shadow()->AsVariable()->slot(); |
141 Move(dot_arguments_slot, rcx, rbx, rdx); | 149 Move(dot_arguments_slot, rcx, rbx, rdx); |
142 } | 150 } |
143 } | 151 } |
144 | 152 |
145 { Comment cmnt(masm_, "[ Declarations"); | 153 { Comment cmnt(masm_, "[ Declarations"); |
146 VisitDeclarations(scope()->declarations()); | 154 // For named function expressions, declare the function name as a |
| 155 // constant. |
| 156 if (scope()->is_function_scope() && scope()->function() != NULL) { |
| 157 EmitDeclaration(scope()->function(), Variable::CONST, NULL); |
| 158 } |
| 159 // Visit all the explicit declarations unless there is an illegal |
| 160 // redeclaration. |
| 161 if (scope()->HasIllegalRedeclaration()) { |
| 162 scope()->VisitIllegalRedeclaration(this); |
| 163 } else { |
| 164 VisitDeclarations(scope()->declarations()); |
| 165 } |
147 } | 166 } |
148 | 167 |
149 { Comment cmnt(masm_, "[ Stack check"); | 168 { Comment cmnt(masm_, "[ Stack check"); |
150 Label ok; | 169 Label ok; |
151 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); | 170 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); |
152 __ j(above_equal, &ok); | 171 __ j(above_equal, &ok); |
153 StackCheckStub stub; | 172 StackCheckStub stub; |
154 __ CallStub(&stub); | 173 __ CallStub(&stub); |
155 __ bind(&ok); | 174 __ bind(&ok); |
156 } | 175 } |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
420 __ movq(result_register(), reg); | 439 __ movq(result_register(), reg); |
421 __ movq(Operand(rsp, 0), result_register()); | 440 __ movq(Operand(rsp, 0), result_register()); |
422 break; | 441 break; |
423 } | 442 } |
424 DoTest(context); | 443 DoTest(context); |
425 break; | 444 break; |
426 } | 445 } |
427 } | 446 } |
428 | 447 |
429 | 448 |
| 449 void FullCodeGenerator::PrepareTest(Label* materialize_true, |
| 450 Label* materialize_false, |
| 451 Label** if_true, |
| 452 Label** if_false) { |
| 453 switch (context_) { |
| 454 case Expression::kUninitialized: |
| 455 UNREACHABLE(); |
| 456 break; |
| 457 case Expression::kEffect: |
| 458 // In an effect context, the true and the false case branch to the |
| 459 // same label. |
| 460 *if_true = *if_false = materialize_true; |
| 461 break; |
| 462 case Expression::kValue: |
| 463 *if_true = materialize_true; |
| 464 *if_false = materialize_false; |
| 465 break; |
| 466 case Expression::kTest: |
| 467 *if_true = true_label_; |
| 468 *if_false = false_label_; |
| 469 break; |
| 470 case Expression::kValueTest: |
| 471 *if_true = materialize_true; |
| 472 *if_false = false_label_; |
| 473 break; |
| 474 case Expression::kTestValue: |
| 475 *if_true = true_label_; |
| 476 *if_false = materialize_false; |
| 477 break; |
| 478 } |
| 479 } |
| 480 |
| 481 |
430 void FullCodeGenerator::Apply(Expression::Context context, | 482 void FullCodeGenerator::Apply(Expression::Context context, |
431 Label* materialize_true, | 483 Label* materialize_true, |
432 Label* materialize_false) { | 484 Label* materialize_false) { |
433 switch (context) { | 485 switch (context) { |
434 case Expression::kUninitialized: | 486 case Expression::kUninitialized: |
435 | 487 |
436 case Expression::kEffect: | 488 case Expression::kEffect: |
437 ASSERT_EQ(materialize_true, materialize_false); | 489 ASSERT_EQ(materialize_true, materialize_false); |
438 __ bind(materialize_true); | 490 __ bind(materialize_true); |
439 break; | 491 break; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
485 case kStack: | 537 case kStack: |
486 __ Push(Factory::false_value()); | 538 __ Push(Factory::false_value()); |
487 break; | 539 break; |
488 } | 540 } |
489 __ jmp(false_label_); | 541 __ jmp(false_label_); |
490 break; | 542 break; |
491 } | 543 } |
492 } | 544 } |
493 | 545 |
494 | 546 |
| 547 // Convert constant control flow (true or false) to the result expected for |
| 548 // a given expression context. |
| 549 void FullCodeGenerator::Apply(Expression::Context context, bool flag) { |
| 550 switch (context) { |
| 551 case Expression::kUninitialized: |
| 552 UNREACHABLE(); |
| 553 break; |
| 554 case Expression::kEffect: |
| 555 break; |
| 556 case Expression::kValue: { |
| 557 Heap::RootListIndex value_root_index = |
| 558 flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; |
| 559 switch (location_) { |
| 560 case kAccumulator: |
| 561 __ LoadRoot(result_register(), value_root_index); |
| 562 break; |
| 563 case kStack: |
| 564 __ PushRoot(value_root_index); |
| 565 break; |
| 566 } |
| 567 break; |
| 568 } |
| 569 case Expression::kTest: |
| 570 __ jmp(flag ? true_label_ : false_label_); |
| 571 break; |
| 572 case Expression::kTestValue: |
| 573 switch (location_) { |
| 574 case kAccumulator: |
| 575 // If value is false it's needed. |
| 576 if (!flag) __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); |
| 577 break; |
| 578 case kStack: |
| 579 // If value is false it's needed. |
| 580 if (!flag) __ PushRoot(Heap::kFalseValueRootIndex); |
| 581 break; |
| 582 } |
| 583 __ jmp(flag ? true_label_ : false_label_); |
| 584 break; |
| 585 case Expression::kValueTest: |
| 586 switch (location_) { |
| 587 case kAccumulator: |
| 588 // If value is true it's needed. |
| 589 if (flag) __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); |
| 590 break; |
| 591 case kStack: |
| 592 // If value is true it's needed. |
| 593 if (flag) __ PushRoot(Heap::kTrueValueRootIndex); |
| 594 break; |
| 595 } |
| 596 __ jmp(flag ? true_label_ : false_label_); |
| 597 break; |
| 598 } |
| 599 } |
| 600 |
| 601 |
495 void FullCodeGenerator::DoTest(Expression::Context context) { | 602 void FullCodeGenerator::DoTest(Expression::Context context) { |
496 // The value to test is in the accumulator. If the value might be needed | 603 // The value to test is in the accumulator. If the value might be needed |
497 // on the stack (value/test and test/value contexts with a stack location | 604 // on the stack (value/test and test/value contexts with a stack location |
498 // desired), then the value is already duplicated on the stack. | 605 // desired), then the value is already duplicated on the stack. |
499 ASSERT_NE(NULL, true_label_); | 606 ASSERT_NE(NULL, true_label_); |
500 ASSERT_NE(NULL, false_label_); | 607 ASSERT_NE(NULL, false_label_); |
501 | 608 |
502 // In value/test and test/value expression contexts with stack as the | 609 // In value/test and test/value expression contexts with stack as the |
503 // desired location, there is already an extra value on the stack. Use a | 610 // desired location, there is already an extra value on the stack. Use a |
504 // label to discard it if unneeded. | 611 // label to discard it if unneeded. |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
660 MemOperand location = EmitSlotSearch(dst, scratch1); | 767 MemOperand location = EmitSlotSearch(dst, scratch1); |
661 __ movq(location, src); | 768 __ movq(location, src); |
662 // Emit the write barrier code if the location is in the heap. | 769 // Emit the write barrier code if the location is in the heap. |
663 if (dst->type() == Slot::CONTEXT) { | 770 if (dst->type() == Slot::CONTEXT) { |
664 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; | 771 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; |
665 __ RecordWrite(scratch1, offset, src, scratch2); | 772 __ RecordWrite(scratch1, offset, src, scratch2); |
666 } | 773 } |
667 } | 774 } |
668 | 775 |
669 | 776 |
670 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { | 777 void FullCodeGenerator::EmitDeclaration(Variable* variable, |
| 778 Variable::Mode mode, |
| 779 FunctionLiteral* function) { |
671 Comment cmnt(masm_, "[ Declaration"); | 780 Comment cmnt(masm_, "[ Declaration"); |
672 Variable* var = decl->proxy()->var(); | 781 ASSERT(variable != NULL); // Must have been resolved. |
673 ASSERT(var != NULL); // Must have been resolved. | 782 Slot* slot = variable->slot(); |
674 Slot* slot = var->slot(); | 783 Property* prop = variable->AsProperty(); |
675 Property* prop = var->AsProperty(); | |
676 | 784 |
677 if (slot != NULL) { | 785 if (slot != NULL) { |
678 switch (slot->type()) { | 786 switch (slot->type()) { |
679 case Slot::PARAMETER: | 787 case Slot::PARAMETER: |
680 case Slot::LOCAL: | 788 case Slot::LOCAL: |
681 if (decl->mode() == Variable::CONST) { | 789 if (mode == Variable::CONST) { |
682 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | 790 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); |
683 __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); | 791 __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); |
684 } else if (decl->fun() != NULL) { | 792 } else if (function != NULL) { |
685 VisitForValue(decl->fun(), kAccumulator); | 793 VisitForValue(function, kAccumulator); |
686 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); | 794 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); |
687 } | 795 } |
688 break; | 796 break; |
689 | 797 |
690 case Slot::CONTEXT: | 798 case Slot::CONTEXT: |
691 // We bypass the general EmitSlotSearch because we know more about | 799 // We bypass the general EmitSlotSearch because we know more about |
692 // this specific context. | 800 // this specific context. |
693 | 801 |
694 // The variable in the decl always resides in the current context. | 802 // The variable in the decl always resides in the current context. |
695 ASSERT_EQ(0, scope()->ContextChainLength(var->scope())); | 803 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); |
696 if (FLAG_debug_code) { | 804 if (FLAG_debug_code) { |
697 // Check if we have the correct context pointer. | 805 // Check if we have the correct context pointer. |
698 __ movq(rbx, | 806 __ movq(rbx, |
699 CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); | 807 CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); |
700 __ cmpq(rbx, rsi); | 808 __ cmpq(rbx, rsi); |
701 __ Check(equal, "Unexpected declaration in current context."); | 809 __ Check(equal, "Unexpected declaration in current context."); |
702 } | 810 } |
703 if (decl->mode() == Variable::CONST) { | 811 if (mode == Variable::CONST) { |
704 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | 812 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); |
705 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | 813 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
706 kScratchRegister); | 814 kScratchRegister); |
707 // No write barrier since the hole value is in old space. | 815 // No write barrier since the hole value is in old space. |
708 } else if (decl->fun() != NULL) { | 816 } else if (function != NULL) { |
709 VisitForValue(decl->fun(), kAccumulator); | 817 VisitForValue(function, kAccumulator); |
710 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | 818 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
711 result_register()); | 819 result_register()); |
712 int offset = Context::SlotOffset(slot->index()); | 820 int offset = Context::SlotOffset(slot->index()); |
713 __ movq(rbx, rsi); | 821 __ movq(rbx, rsi); |
714 __ RecordWrite(rbx, offset, result_register(), rcx); | 822 __ RecordWrite(rbx, offset, result_register(), rcx); |
715 } | 823 } |
716 break; | 824 break; |
717 | 825 |
718 case Slot::LOOKUP: { | 826 case Slot::LOOKUP: { |
719 __ push(rsi); | 827 __ push(rsi); |
720 __ Push(var->name()); | 828 __ Push(variable->name()); |
721 // Declaration nodes are always introduced in one of two modes. | 829 // Declaration nodes are always introduced in one of two modes. |
722 ASSERT(decl->mode() == Variable::VAR || | 830 ASSERT(mode == Variable::VAR || mode == Variable::CONST); |
723 decl->mode() == Variable::CONST); | 831 PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY; |
724 PropertyAttributes attr = | |
725 (decl->mode() == Variable::VAR) ? NONE : READ_ONLY; | |
726 __ Push(Smi::FromInt(attr)); | 832 __ Push(Smi::FromInt(attr)); |
727 // Push initial value, if any. | 833 // Push initial value, if any. |
728 // Note: For variables we must not push an initial value (such as | 834 // Note: For variables we must not push an initial value (such as |
729 // 'undefined') because we may have a (legal) redeclaration and we | 835 // 'undefined') because we may have a (legal) redeclaration and we |
730 // must not destroy the current value. | 836 // must not destroy the current value. |
731 if (decl->mode() == Variable::CONST) { | 837 if (mode == Variable::CONST) { |
732 __ PushRoot(Heap::kTheHoleValueRootIndex); | 838 __ PushRoot(Heap::kTheHoleValueRootIndex); |
733 } else if (decl->fun() != NULL) { | 839 } else if (function != NULL) { |
734 VisitForValue(decl->fun(), kStack); | 840 VisitForValue(function, kStack); |
735 } else { | 841 } else { |
736 __ Push(Smi::FromInt(0)); // no initial value! | 842 __ Push(Smi::FromInt(0)); // no initial value! |
737 } | 843 } |
738 __ CallRuntime(Runtime::kDeclareContextSlot, 4); | 844 __ CallRuntime(Runtime::kDeclareContextSlot, 4); |
739 break; | 845 break; |
740 } | 846 } |
741 } | 847 } |
742 | 848 |
743 } else if (prop != NULL) { | 849 } else if (prop != NULL) { |
744 if (decl->fun() != NULL || decl->mode() == Variable::CONST) { | 850 if (function != NULL || mode == Variable::CONST) { |
745 // We are declaring a function or constant that rewrites to a | 851 // We are declaring a function or constant that rewrites to a |
746 // property. Use (keyed) IC to set the initial value. | 852 // property. Use (keyed) IC to set the initial value. |
747 VisitForValue(prop->obj(), kStack); | 853 VisitForValue(prop->obj(), kStack); |
748 VisitForValue(prop->key(), kStack); | 854 if (function != NULL) { |
749 | 855 VisitForValue(prop->key(), kStack); |
750 if (decl->fun() != NULL) { | 856 VisitForValue(function, kAccumulator); |
751 VisitForValue(decl->fun(), kAccumulator); | 857 __ pop(rcx); |
752 } else { | 858 } else { |
| 859 VisitForValue(prop->key(), kAccumulator); |
| 860 __ movq(rcx, result_register()); |
753 __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); | 861 __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); |
754 } | 862 } |
| 863 __ pop(rdx); |
755 | 864 |
756 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 865 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
757 __ call(ic, RelocInfo::CODE_TARGET); | 866 __ call(ic, RelocInfo::CODE_TARGET); |
758 // Absence of a test rax instruction following the call | 867 // Absence of a test rax instruction following the call |
759 // indicates that none of the load was inlined. | 868 // indicates that none of the load was inlined. |
760 __ nop(); | 869 __ nop(); |
761 | |
762 // Value in rax is ignored (declarations are statements). Receiver | |
763 // and key on stack are discarded. | |
764 __ Drop(2); | |
765 } | 870 } |
766 } | 871 } |
767 } | 872 } |
768 | 873 |
769 | 874 |
| 875 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { |
| 876 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); |
| 877 } |
| 878 |
| 879 |
770 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { | 880 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { |
771 // Call the runtime to declare the globals. | 881 // Call the runtime to declare the globals. |
772 __ push(rsi); // The context is the first argument. | 882 __ push(rsi); // The context is the first argument. |
773 __ Push(pairs); | 883 __ Push(pairs); |
774 __ Push(Smi::FromInt(is_eval() ? 1 : 0)); | 884 __ Push(Smi::FromInt(is_eval() ? 1 : 0)); |
775 __ CallRuntime(Runtime::kDeclareGlobals, 3); | 885 __ CallRuntime(Runtime::kDeclareGlobals, 3); |
776 // Return value is ignored. | 886 // Return value is ignored. |
777 } | 887 } |
778 | 888 |
779 | 889 |
780 void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { | 890 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { |
781 Comment cmnt(masm_, "[ FunctionLiteral"); | 891 Comment cmnt(masm_, "[ SwitchStatement"); |
782 | 892 Breakable nested_statement(this, stmt); |
783 // Build the shared function info and instantiate the function based | 893 SetStatementPosition(stmt); |
784 // on it. | 894 // Keep the switch value on the stack until a case matches. |
785 Handle<SharedFunctionInfo> function_info = | 895 VisitForValue(stmt->tag(), kStack); |
786 Compiler::BuildFunctionInfo(expr, script(), this); | 896 |
787 if (HasStackOverflow()) return; | 897 ZoneList<CaseClause*>* clauses = stmt->cases(); |
788 | 898 CaseClause* default_clause = NULL; // Can occur anywhere in the list. |
789 // Create a new closure. | 899 |
790 __ push(rsi); | 900 Label next_test; // Recycled for each test. |
791 __ Push(function_info); | 901 // Compile all the tests with branches to their bodies. |
792 __ CallRuntime(Runtime::kNewClosure, 2); | 902 for (int i = 0; i < clauses->length(); i++) { |
| 903 CaseClause* clause = clauses->at(i); |
| 904 // The default is not a test, but remember it as final fall through. |
| 905 if (clause->is_default()) { |
| 906 default_clause = clause; |
| 907 continue; |
| 908 } |
| 909 |
| 910 Comment cmnt(masm_, "[ Case comparison"); |
| 911 __ bind(&next_test); |
| 912 next_test.Unuse(); |
| 913 |
| 914 // Compile the label expression. |
| 915 VisitForValue(clause->label(), kAccumulator); |
| 916 |
| 917 // Perform the comparison as if via '==='. The comparison stub expects |
| 918 // the smi vs. smi case to be handled before it is called. |
| 919 Label slow_case; |
| 920 __ movq(rdx, Operand(rsp, 0)); // Switch value. |
| 921 __ JumpIfNotBothSmi(rdx, rax, &slow_case); |
| 922 __ SmiCompare(rdx, rax); |
| 923 __ j(not_equal, &next_test); |
| 924 __ Drop(1); // Switch value is no longer needed. |
| 925 __ jmp(clause->body_target()->entry_label()); |
| 926 |
| 927 __ bind(&slow_case); |
| 928 CompareStub stub(equal, true); |
| 929 __ CallStub(&stub); |
| 930 __ testq(rax, rax); |
| 931 __ j(not_equal, &next_test); |
| 932 __ Drop(1); // Switch value is no longer needed. |
| 933 __ jmp(clause->body_target()->entry_label()); |
| 934 } |
| 935 |
| 936 // Discard the test value and jump to the default if present, otherwise to |
| 937 // the end of the statement. |
| 938 __ bind(&next_test); |
| 939 __ Drop(1); // Switch value is no longer needed. |
| 940 if (default_clause == NULL) { |
| 941 __ jmp(nested_statement.break_target()); |
| 942 } else { |
| 943 __ jmp(default_clause->body_target()->entry_label()); |
| 944 } |
| 945 |
| 946 // Compile all the case bodies. |
| 947 for (int i = 0; i < clauses->length(); i++) { |
| 948 Comment cmnt(masm_, "[ Case body"); |
| 949 CaseClause* clause = clauses->at(i); |
| 950 __ bind(clause->body_target()->entry_label()); |
| 951 VisitStatements(clause->statements()); |
| 952 } |
| 953 |
| 954 __ bind(nested_statement.break_target()); |
| 955 } |
| 956 |
| 957 |
| 958 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { |
| 959 Comment cmnt(masm_, "[ ForInStatement"); |
| 960 SetStatementPosition(stmt); |
| 961 |
| 962 Label loop, exit; |
| 963 ForIn loop_statement(this, stmt); |
| 964 increment_loop_depth(); |
| 965 |
| 966 // Get the object to enumerate over. Both SpiderMonkey and JSC |
| 967 // ignore null and undefined in contrast to the specification; see |
| 968 // ECMA-262 section 12.6.4. |
| 969 VisitForValue(stmt->enumerable(), kAccumulator); |
| 970 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
| 971 __ j(equal, &exit); |
| 972 __ CompareRoot(rax, Heap::kNullValueRootIndex); |
| 973 __ j(equal, &exit); |
| 974 |
| 975 // Convert the object to a JS object. |
| 976 Label convert, done_convert; |
| 977 __ JumpIfSmi(rax, &convert); |
| 978 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx); |
| 979 __ j(above_equal, &done_convert); |
| 980 __ bind(&convert); |
| 981 __ push(rax); |
| 982 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); |
| 983 __ bind(&done_convert); |
| 984 __ push(rax); |
| 985 |
| 986 // TODO(kasperl): Check cache validity in generated code. This is a |
| 987 // fast case for the JSObject::IsSimpleEnum cache validity |
| 988 // checks. If we cannot guarantee cache validity, call the runtime |
| 989 // system to check cache validity or get the property names in a |
| 990 // fixed array. |
| 991 |
| 992 // Get the set of properties to enumerate. |
| 993 __ push(rax); // Duplicate the enumerable object on the stack. |
| 994 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); |
| 995 |
| 996 // If we got a map from the runtime call, we can do a fast |
| 997 // modification check. Otherwise, we got a fixed array, and we have |
| 998 // to do a slow check. |
| 999 Label fixed_array; |
| 1000 __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), |
| 1001 Heap::kMetaMapRootIndex); |
| 1002 __ j(not_equal, &fixed_array); |
| 1003 |
| 1004 // We got a map in register rax. Get the enumeration cache from it. |
| 1005 __ movq(rcx, FieldOperand(rax, Map::kInstanceDescriptorsOffset)); |
| 1006 __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); |
| 1007 __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 1008 |
| 1009 // Setup the four remaining stack slots. |
| 1010 __ push(rax); // Map. |
| 1011 __ push(rdx); // Enumeration cache. |
| 1012 __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); |
| 1013 __ Integer32ToSmi(rax, rax); |
| 1014 __ push(rax); // Enumeration cache length (as smi). |
| 1015 __ Push(Smi::FromInt(0)); // Initial index. |
| 1016 __ jmp(&loop); |
| 1017 |
| 1018 // We got a fixed array in register rax. Iterate through that. |
| 1019 __ bind(&fixed_array); |
| 1020 __ Push(Smi::FromInt(0)); // Map (0) - force slow check. |
| 1021 __ push(rax); |
| 1022 __ movq(rax, FieldOperand(rax, FixedArray::kLengthOffset)); |
| 1023 __ Integer32ToSmi(rax, rax); |
| 1024 __ push(rax); // Fixed array length (as smi). |
| 1025 __ Push(Smi::FromInt(0)); // Initial index. |
| 1026 |
| 1027 // Generate code for doing the condition check. |
| 1028 __ bind(&loop); |
| 1029 __ movq(rax, Operand(rsp, 0 * kPointerSize)); // Get the current index. |
| 1030 __ cmpq(rax, Operand(rsp, 1 * kPointerSize)); // Compare to the array length. |
| 1031 __ j(above_equal, loop_statement.break_target()); |
| 1032 |
| 1033 // Get the current entry of the array into register rbx. |
| 1034 __ movq(rbx, Operand(rsp, 2 * kPointerSize)); |
| 1035 SmiIndex index = __ SmiToIndex(rax, rax, kPointerSizeLog2); |
| 1036 __ movq(rbx, FieldOperand(rbx, |
| 1037 index.reg, |
| 1038 index.scale, |
| 1039 FixedArray::kHeaderSize)); |
| 1040 |
| 1041 // Get the expected map from the stack or a zero map in the |
| 1042 // permanent slow case into register rdx. |
| 1043 __ movq(rdx, Operand(rsp, 3 * kPointerSize)); |
| 1044 |
| 1045 // Check if the expected map still matches that of the enumerable. |
| 1046 // If not, we have to filter the key. |
| 1047 Label update_each; |
| 1048 __ movq(rcx, Operand(rsp, 4 * kPointerSize)); |
| 1049 __ cmpq(rdx, FieldOperand(rcx, HeapObject::kMapOffset)); |
| 1050 __ j(equal, &update_each); |
| 1051 |
| 1052 // Convert the entry to a string or null if it isn't a property |
| 1053 // anymore. If the property has been removed while iterating, we |
| 1054 // just skip it. |
| 1055 __ push(rcx); // Enumerable. |
| 1056 __ push(rbx); // Current entry. |
| 1057 __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION); |
| 1058 __ CompareRoot(rax, Heap::kNullValueRootIndex); |
| 1059 __ j(equal, loop_statement.continue_target()); |
| 1060 __ movq(rbx, rax); |
| 1061 |
| 1062 // Update the 'each' property or variable from the possibly filtered |
| 1063 // entry in register rbx. |
| 1064 __ bind(&update_each); |
| 1065 __ movq(result_register(), rbx); |
| 1066 // Perform the assignment as if via '='. |
| 1067 EmitAssignment(stmt->each()); |
| 1068 |
| 1069 // Generate code for the body of the loop. |
| 1070 Label stack_limit_hit, stack_check_done; |
| 1071 Visit(stmt->body()); |
| 1072 |
| 1073 __ StackLimitCheck(&stack_limit_hit); |
| 1074 __ bind(&stack_check_done); |
| 1075 |
| 1076 // Generate code for going to the next element by incrementing the |
| 1077 // index (smi) stored on top of the stack. |
| 1078 __ bind(loop_statement.continue_target()); |
| 1079 __ SmiAddConstant(Operand(rsp, 0 * kPointerSize), Smi::FromInt(1)); |
| 1080 __ jmp(&loop); |
| 1081 |
| 1082 // Slow case for the stack limit check. |
| 1083 StackCheckStub stack_check_stub; |
| 1084 __ bind(&stack_limit_hit); |
| 1085 __ CallStub(&stack_check_stub); |
| 1086 __ jmp(&stack_check_done); |
| 1087 |
| 1088 // Remove the pointers stored on the stack. |
| 1089 __ bind(loop_statement.break_target()); |
| 1090 __ addq(rsp, Immediate(5 * kPointerSize)); |
| 1091 |
| 1092 // Exit and decrement the loop depth. |
| 1093 __ bind(&exit); |
| 1094 decrement_loop_depth(); |
| 1095 } |
| 1096 |
| 1097 |
| 1098 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { |
| 1099 // Use the fast case closure allocation code that allocates in new |
| 1100 // space for nested functions that don't need literals cloning. |
| 1101 if (scope()->is_function_scope() && info->num_literals() == 0) { |
| 1102 FastNewClosureStub stub; |
| 1103 __ Push(info); |
| 1104 __ CallStub(&stub); |
| 1105 } else { |
| 1106 __ push(rsi); |
| 1107 __ Push(info); |
| 1108 __ CallRuntime(Runtime::kNewClosure, 2); |
| 1109 } |
793 Apply(context_, rax); | 1110 Apply(context_, rax); |
794 } | 1111 } |
795 | 1112 |
796 | 1113 |
797 void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { | 1114 void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { |
798 Comment cmnt(masm_, "[ VariableProxy"); | 1115 Comment cmnt(masm_, "[ VariableProxy"); |
799 EmitVariableLoad(expr->var(), context_); | 1116 EmitVariableLoad(expr->var(), context_); |
800 } | 1117 } |
801 | 1118 |
802 | 1119 |
(...skipping 23 matching lines...) Expand all Loading... |
826 Comment cmnt(masm_, "Lookup slot"); | 1143 Comment cmnt(masm_, "Lookup slot"); |
827 __ push(rsi); // Context. | 1144 __ push(rsi); // Context. |
828 __ Push(var->name()); | 1145 __ Push(var->name()); |
829 __ CallRuntime(Runtime::kLoadContextSlot, 2); | 1146 __ CallRuntime(Runtime::kLoadContextSlot, 2); |
830 Apply(context, rax); | 1147 Apply(context, rax); |
831 | 1148 |
832 } else if (slot != NULL) { | 1149 } else if (slot != NULL) { |
833 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) | 1150 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) |
834 ? "Context slot" | 1151 ? "Context slot" |
835 : "Stack slot"); | 1152 : "Stack slot"); |
836 Apply(context, slot); | 1153 if (var->mode() == Variable::CONST) { |
| 1154 // Constants may be the hole value if they have not been initialized. |
| 1155 // Unhole them. |
| 1156 Label done; |
| 1157 MemOperand slot_operand = EmitSlotSearch(slot, rax); |
| 1158 __ movq(rax, slot_operand); |
| 1159 __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); |
| 1160 __ j(not_equal, &done); |
| 1161 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 1162 __ bind(&done); |
| 1163 Apply(context, rax); |
| 1164 } else { |
| 1165 Apply(context, slot); |
| 1166 } |
837 | 1167 |
838 } else { | 1168 } else { |
839 Comment cmnt(masm_, "Rewritten parameter"); | 1169 Comment cmnt(masm_, "Rewritten parameter"); |
840 ASSERT_NOT_NULL(property); | 1170 ASSERT_NOT_NULL(property); |
841 // Rewritten parameter accesses are of the form "slot[literal]". | 1171 // Rewritten parameter accesses are of the form "slot[literal]". |
842 | 1172 |
843 // Assert that the object is in a slot. | 1173 // Assert that the object is in a slot. |
844 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); | 1174 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); |
845 ASSERT_NOT_NULL(object_var); | 1175 ASSERT_NOT_NULL(object_var); |
846 Slot* object_slot = object_var->slot(); | 1176 Slot* object_slot = object_var->slot(); |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
962 if (result_saved) { | 1292 if (result_saved) { |
963 ApplyTOS(context_); | 1293 ApplyTOS(context_); |
964 } else { | 1294 } else { |
965 Apply(context_, rax); | 1295 Apply(context_, rax); |
966 } | 1296 } |
967 } | 1297 } |
968 | 1298 |
969 | 1299 |
970 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { | 1300 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { |
971 Comment cmnt(masm_, "[ ArrayLiteral"); | 1301 Comment cmnt(masm_, "[ ArrayLiteral"); |
| 1302 |
| 1303 ZoneList<Expression*>* subexprs = expr->values(); |
| 1304 int length = subexprs->length(); |
| 1305 |
972 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | 1306 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); |
973 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); | 1307 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); |
974 __ Push(Smi::FromInt(expr->literal_index())); | 1308 __ Push(Smi::FromInt(expr->literal_index())); |
975 __ Push(expr->constant_elements()); | 1309 __ Push(expr->constant_elements()); |
976 if (expr->depth() > 1) { | 1310 if (expr->depth() > 1) { |
977 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); | 1311 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); |
| 1312 } else if (length > FastCloneShallowArrayStub::kMaximumLength) { |
| 1313 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); |
978 } else { | 1314 } else { |
979 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); | 1315 FastCloneShallowArrayStub stub(length); |
| 1316 __ CallStub(&stub); |
980 } | 1317 } |
981 | 1318 |
982 bool result_saved = false; // Is the result saved to the stack? | 1319 bool result_saved = false; // Is the result saved to the stack? |
983 | 1320 |
984 // Emit code to evaluate all the non-constant subexpressions and to store | 1321 // Emit code to evaluate all the non-constant subexpressions and to store |
985 // them into the newly cloned array. | 1322 // them into the newly cloned array. |
986 ZoneList<Expression*>* subexprs = expr->values(); | 1323 for (int i = 0; i < length; i++) { |
987 for (int i = 0, len = subexprs->length(); i < len; i++) { | |
988 Expression* subexpr = subexprs->at(i); | 1324 Expression* subexpr = subexprs->at(i); |
989 // If the subexpression is a literal or a simple materialized literal it | 1325 // If the subexpression is a literal or a simple materialized literal it |
990 // is already set in the cloned array. | 1326 // is already set in the cloned array. |
991 if (subexpr->AsLiteral() != NULL || | 1327 if (subexpr->AsLiteral() != NULL || |
992 CompileTimeValue::IsCompileTimeValue(subexpr)) { | 1328 CompileTimeValue::IsCompileTimeValue(subexpr)) { |
993 continue; | 1329 continue; |
994 } | 1330 } |
995 | 1331 |
996 if (!result_saved) { | 1332 if (!result_saved) { |
997 __ push(rax); | 1333 __ push(rax); |
(...skipping 14 matching lines...) Expand all Loading... |
1012 if (result_saved) { | 1348 if (result_saved) { |
1013 ApplyTOS(context_); | 1349 ApplyTOS(context_); |
1014 } else { | 1350 } else { |
1015 Apply(context_, rax); | 1351 Apply(context_, rax); |
1016 } | 1352 } |
1017 } | 1353 } |
1018 | 1354 |
1019 | 1355 |
1020 void FullCodeGenerator::VisitAssignment(Assignment* expr) { | 1356 void FullCodeGenerator::VisitAssignment(Assignment* expr) { |
1021 Comment cmnt(masm_, "[ Assignment"); | 1357 Comment cmnt(masm_, "[ Assignment"); |
1022 ASSERT(expr->op() != Token::INIT_CONST); | 1358 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' |
| 1359 // on the left-hand side. |
| 1360 if (!expr->target()->IsValidLeftHandSide()) { |
| 1361 VisitForEffect(expr->target()); |
| 1362 return; |
| 1363 } |
| 1364 |
1023 // Left-hand side can only be a property, a global or a (parameter or local) | 1365 // Left-hand side can only be a property, a global or a (parameter or local) |
1024 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. | 1366 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
1025 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; | 1367 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
1026 LhsKind assign_type = VARIABLE; | 1368 LhsKind assign_type = VARIABLE; |
1027 Property* prop = expr->target()->AsProperty(); | 1369 Property* prop = expr->target()->AsProperty(); |
1028 if (prop != NULL) { | 1370 if (prop != NULL) { |
1029 assign_type = | 1371 assign_type = |
1030 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; | 1372 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; |
1031 } | 1373 } |
1032 | 1374 |
1033 // Evaluate LHS expression. | 1375 // Evaluate LHS expression. |
1034 switch (assign_type) { | 1376 switch (assign_type) { |
1035 case VARIABLE: | 1377 case VARIABLE: |
1036 // Nothing to do here. | 1378 // Nothing to do here. |
1037 break; | 1379 break; |
1038 case NAMED_PROPERTY: | 1380 case NAMED_PROPERTY: |
1039 if (expr->is_compound()) { | 1381 if (expr->is_compound()) { |
1040 // We need the receiver both on the stack and in the accumulator. | 1382 // We need the receiver both on the stack and in the accumulator. |
1041 VisitForValue(prop->obj(), kAccumulator); | 1383 VisitForValue(prop->obj(), kAccumulator); |
1042 __ push(result_register()); | 1384 __ push(result_register()); |
1043 } else { | 1385 } else { |
1044 VisitForValue(prop->obj(), kStack); | 1386 VisitForValue(prop->obj(), kStack); |
1045 } | 1387 } |
1046 break; | 1388 break; |
1047 case KEYED_PROPERTY: | 1389 case KEYED_PROPERTY: |
1048 VisitForValue(prop->obj(), kStack); | 1390 if (expr->is_compound()) { |
1049 VisitForValue(prop->key(), kStack); | 1391 VisitForValue(prop->obj(), kStack); |
| 1392 VisitForValue(prop->key(), kAccumulator); |
| 1393 __ movq(rdx, Operand(rsp, 0)); |
| 1394 __ push(rax); |
| 1395 } else { |
| 1396 VisitForValue(prop->obj(), kStack); |
| 1397 VisitForValue(prop->key(), kStack); |
| 1398 } |
1050 break; | 1399 break; |
1051 } | 1400 } |
1052 | 1401 |
1053 // If we have a compound assignment: Get value of LHS expression and | 1402 // If we have a compound assignment: Get value of LHS expression and |
1054 // store in on top of the stack. | 1403 // store in on top of the stack. |
1055 if (expr->is_compound()) { | 1404 if (expr->is_compound()) { |
1056 Location saved_location = location_; | 1405 Location saved_location = location_; |
1057 location_ = kStack; | 1406 location_ = kStack; |
1058 switch (assign_type) { | 1407 switch (assign_type) { |
1059 case VARIABLE: | 1408 case VARIABLE: |
(...skipping 24 matching lines...) Expand all Loading... |
1084 location_ = saved_location; | 1433 location_ = saved_location; |
1085 } | 1434 } |
1086 | 1435 |
1087 // Record source position before possible IC call. | 1436 // Record source position before possible IC call. |
1088 SetSourcePosition(expr->position()); | 1437 SetSourcePosition(expr->position()); |
1089 | 1438 |
1090 // Store the value. | 1439 // Store the value. |
1091 switch (assign_type) { | 1440 switch (assign_type) { |
1092 case VARIABLE: | 1441 case VARIABLE: |
1093 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), | 1442 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), |
| 1443 expr->op(), |
1094 context_); | 1444 context_); |
1095 break; | 1445 break; |
1096 case NAMED_PROPERTY: | 1446 case NAMED_PROPERTY: |
1097 EmitNamedPropertyAssignment(expr); | 1447 EmitNamedPropertyAssignment(expr); |
1098 break; | 1448 break; |
1099 case KEYED_PROPERTY: | 1449 case KEYED_PROPERTY: |
1100 EmitKeyedPropertyAssignment(expr); | 1450 EmitKeyedPropertyAssignment(expr); |
1101 break; | 1451 break; |
1102 } | 1452 } |
1103 } | 1453 } |
(...skipping 21 matching lines...) Expand all Loading... |
1125 Expression::Context context) { | 1475 Expression::Context context) { |
1126 __ push(result_register()); | 1476 __ push(result_register()); |
1127 GenericBinaryOpStub stub(op, | 1477 GenericBinaryOpStub stub(op, |
1128 NO_OVERWRITE, | 1478 NO_OVERWRITE, |
1129 NO_GENERIC_BINARY_FLAGS); | 1479 NO_GENERIC_BINARY_FLAGS); |
1130 __ CallStub(&stub); | 1480 __ CallStub(&stub); |
1131 Apply(context, rax); | 1481 Apply(context, rax); |
1132 } | 1482 } |
1133 | 1483 |
1134 | 1484 |
| 1485 void FullCodeGenerator::EmitAssignment(Expression* expr) { |
| 1486 // Invalid left-hand sides are rewritten to have a 'throw |
| 1487 // ReferenceError' on the left-hand side. |
| 1488 if (!expr->IsValidLeftHandSide()) { |
| 1489 VisitForEffect(expr); |
| 1490 return; |
| 1491 } |
| 1492 |
| 1493 // Left-hand side can only be a property, a global or a (parameter or local) |
| 1494 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
| 1495 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
| 1496 LhsKind assign_type = VARIABLE; |
| 1497 Property* prop = expr->AsProperty(); |
| 1498 if (prop != NULL) { |
| 1499 assign_type = (prop->key()->IsPropertyName()) |
| 1500 ? NAMED_PROPERTY |
| 1501 : KEYED_PROPERTY; |
| 1502 } |
| 1503 |
| 1504 switch (assign_type) { |
| 1505 case VARIABLE: { |
| 1506 Variable* var = expr->AsVariableProxy()->var(); |
| 1507 EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect); |
| 1508 break; |
| 1509 } |
| 1510 case NAMED_PROPERTY: { |
| 1511 __ push(rax); // Preserve value. |
| 1512 VisitForValue(prop->obj(), kAccumulator); |
| 1513 __ movq(rdx, rax); |
| 1514 __ pop(rax); // Restore value. |
| 1515 __ Move(rcx, prop->key()->AsLiteral()->handle()); |
| 1516 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
| 1517 __ call(ic, RelocInfo::CODE_TARGET); |
| 1518 __ nop(); // Signal no inlined code. |
| 1519 break; |
| 1520 } |
| 1521 case KEYED_PROPERTY: { |
| 1522 __ push(rax); // Preserve value. |
| 1523 VisitForValue(prop->obj(), kStack); |
| 1524 VisitForValue(prop->key(), kStack); |
| 1525 __ movq(rax, Operand(rsp, 2 * kPointerSize)); |
| 1526 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
| 1527 __ call(ic, RelocInfo::CODE_TARGET); |
| 1528 __ nop(); // Signal no inlined code. |
| 1529 __ Drop(3); // Receiver, key, and extra copy of value. |
| 1530 break; |
| 1531 } |
| 1532 } |
| 1533 } |
| 1534 |
| 1535 |
1135 void FullCodeGenerator::EmitVariableAssignment(Variable* var, | 1536 void FullCodeGenerator::EmitVariableAssignment(Variable* var, |
| 1537 Token::Value op, |
1136 Expression::Context context) { | 1538 Expression::Context context) { |
1137 // Three main cases: non-this global variables, lookup slots, and | 1539 // Left-hand sides that rewrite to explicit property accesses do not reach |
1138 // all other types of slots. Left-hand-side parameters that rewrite | 1540 // here. |
1139 // to explicit property accesses do not reach here. | |
1140 ASSERT(var != NULL); | 1541 ASSERT(var != NULL); |
1141 ASSERT(var->is_global() || var->slot() != NULL); | 1542 ASSERT(var->is_global() || var->slot() != NULL); |
1142 Slot* slot = var->slot(); | 1543 |
1143 if (var->is_global()) { | 1544 if (var->is_global()) { |
1144 ASSERT(!var->is_this()); | 1545 ASSERT(!var->is_this()); |
1145 // Assignment to a global variable. Use inline caching for the | 1546 // Assignment to a global variable. Use inline caching for the |
1146 // assignment. Right-hand-side value is passed in rax, variable name in | 1547 // assignment. Right-hand-side value is passed in rax, variable name in |
1147 // rcx, and the global object in rdx. | 1548 // rcx, and the global object on the stack. |
1148 __ Move(rcx, var->name()); | 1549 __ Move(rcx, var->name()); |
1149 __ movq(rdx, CodeGenerator::GlobalObject()); | 1550 __ movq(rdx, CodeGenerator::GlobalObject()); |
1150 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 1551 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
1151 __ Call(ic, RelocInfo::CODE_TARGET); | 1552 __ Call(ic, RelocInfo::CODE_TARGET); |
1152 Apply(context, rax); | 1553 __ nop(); |
1153 | 1554 |
1154 } else if (slot != NULL && slot->type() == Slot::LOOKUP) { | 1555 } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { |
1155 __ push(result_register()); // Value. | 1556 // Perform the assignment for non-const variables and for initialization |
1156 __ push(rsi); // Context. | 1557 // of const variables. Const assignments are simply skipped. |
1157 __ Push(var->name()); | 1558 Label done; |
1158 __ CallRuntime(Runtime::kStoreContextSlot, 3); | 1559 Slot* slot = var->slot(); |
1159 Apply(context, rax); | |
1160 | |
1161 } else if (var->slot() != NULL) { | |
1162 switch (slot->type()) { | 1560 switch (slot->type()) { |
| 1561 case Slot::PARAMETER: |
1163 case Slot::LOCAL: | 1562 case Slot::LOCAL: |
1164 case Slot::PARAMETER: | 1563 if (op == Token::INIT_CONST) { |
1165 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); | 1564 // Detect const reinitialization by checking for the hole value. |
| 1565 __ movq(rdx, Operand(rbp, SlotOffset(slot))); |
| 1566 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); |
| 1567 __ j(not_equal, &done); |
| 1568 } |
| 1569 // Perform the assignment. |
| 1570 __ movq(Operand(rbp, SlotOffset(slot)), rax); |
1166 break; | 1571 break; |
1167 | 1572 |
1168 case Slot::CONTEXT: { | 1573 case Slot::CONTEXT: { |
1169 MemOperand target = EmitSlotSearch(slot, rcx); | 1574 MemOperand target = EmitSlotSearch(slot, rcx); |
1170 __ movq(target, result_register()); | 1575 if (op == Token::INIT_CONST) { |
1171 | 1576 // Detect const reinitialization by checking for the hole value. |
1172 // RecordWrite may destroy all its register arguments. | 1577 __ movq(rdx, target); |
1173 __ movq(rdx, result_register()); | 1578 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); |
| 1579 __ j(not_equal, &done); |
| 1580 } |
| 1581 // Perform the assignment and issue the write barrier. |
| 1582 __ movq(target, rax); |
| 1583 // The value of the assignment is in rax. RecordWrite clobbers its |
| 1584 // register arguments. |
| 1585 __ movq(rdx, rax); |
1174 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | 1586 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; |
1175 __ RecordWrite(rcx, offset, rdx, rbx); | 1587 __ RecordWrite(rcx, offset, rdx, rbx); |
1176 break; | 1588 break; |
1177 } | 1589 } |
1178 | 1590 |
1179 case Slot::LOOKUP: | 1591 case Slot::LOOKUP: |
1180 UNREACHABLE(); | 1592 // Call the runtime for the assignment. The runtime will ignore |
| 1593 // const reinitialization. |
| 1594 __ push(rax); // Value. |
| 1595 __ push(rsi); // Context. |
| 1596 __ Push(var->name()); |
| 1597 if (op == Token::INIT_CONST) { |
| 1598 // The runtime will ignore const redeclaration. |
| 1599 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
| 1600 } else { |
| 1601 __ CallRuntime(Runtime::kStoreContextSlot, 3); |
| 1602 } |
1181 break; | 1603 break; |
1182 } | 1604 } |
1183 Apply(context, result_register()); | 1605 __ bind(&done); |
| 1606 } |
1184 | 1607 |
1185 } else { | 1608 Apply(context, rax); |
1186 // Variables rewritten as properties are not treated as variables in | |
1187 // assignments. | |
1188 UNREACHABLE(); | |
1189 } | |
1190 } | 1609 } |
1191 | 1610 |
1192 | 1611 |
1193 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { | 1612 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { |
1194 // Assignment to a property, using a named store IC. | 1613 // Assignment to a property, using a named store IC. |
1195 Property* prop = expr->target()->AsProperty(); | 1614 Property* prop = expr->target()->AsProperty(); |
1196 ASSERT(prop != NULL); | 1615 ASSERT(prop != NULL); |
1197 ASSERT(prop->key()->AsLiteral() != NULL); | 1616 ASSERT(prop->key()->AsLiteral() != NULL); |
1198 | 1617 |
1199 // If the assignment starts a block of assignments to the same object, | 1618 // If the assignment starts a block of assignments to the same object, |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1238 // change to slow case to avoid the quadratic behavior of repeatedly | 1657 // change to slow case to avoid the quadratic behavior of repeatedly |
1239 // adding fast properties. | 1658 // adding fast properties. |
1240 if (expr->starts_initialization_block()) { | 1659 if (expr->starts_initialization_block()) { |
1241 __ push(result_register()); | 1660 __ push(result_register()); |
1242 // Receiver is now under the key and value. | 1661 // Receiver is now under the key and value. |
1243 __ push(Operand(rsp, 2 * kPointerSize)); | 1662 __ push(Operand(rsp, 2 * kPointerSize)); |
1244 __ CallRuntime(Runtime::kToSlowProperties, 1); | 1663 __ CallRuntime(Runtime::kToSlowProperties, 1); |
1245 __ pop(result_register()); | 1664 __ pop(result_register()); |
1246 } | 1665 } |
1247 | 1666 |
| 1667 __ pop(rcx); |
| 1668 if (expr->ends_initialization_block()) { |
| 1669 __ movq(rdx, Operand(rsp, 0)); // Leave receiver on the stack for later. |
| 1670 } else { |
| 1671 __ pop(rdx); |
| 1672 } |
1248 // Record source code position before IC call. | 1673 // Record source code position before IC call. |
1249 SetSourcePosition(expr->position()); | 1674 SetSourcePosition(expr->position()); |
1250 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 1675 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
1251 __ Call(ic, RelocInfo::CODE_TARGET); | 1676 __ Call(ic, RelocInfo::CODE_TARGET); |
1252 // This nop signals to the IC that there is no inlined code at the call | 1677 // This nop signals to the IC that there is no inlined code at the call |
1253 // site for it to patch. | 1678 // site for it to patch. |
1254 __ nop(); | 1679 __ nop(); |
1255 | 1680 |
1256 // If the assignment ends an initialization block, revert to fast case. | 1681 // If the assignment ends an initialization block, revert to fast case. |
1257 if (expr->ends_initialization_block()) { | 1682 if (expr->ends_initialization_block()) { |
| 1683 __ pop(rdx); |
1258 __ push(rax); // Result of assignment, saved even if not needed. | 1684 __ push(rax); // Result of assignment, saved even if not needed. |
1259 // Receiver is under the key and value. | 1685 __ push(rdx); |
1260 __ push(Operand(rsp, 2 * kPointerSize)); | |
1261 __ CallRuntime(Runtime::kToFastProperties, 1); | 1686 __ CallRuntime(Runtime::kToFastProperties, 1); |
1262 __ pop(rax); | 1687 __ pop(rax); |
1263 } | 1688 } |
1264 | 1689 |
1265 // Receiver and key are still on stack. | 1690 Apply(context_, rax); |
1266 DropAndApply(2, context_, rax); | |
1267 } | 1691 } |
1268 | 1692 |
1269 | 1693 |
1270 void FullCodeGenerator::VisitProperty(Property* expr) { | 1694 void FullCodeGenerator::VisitProperty(Property* expr) { |
1271 Comment cmnt(masm_, "[ Property"); | 1695 Comment cmnt(masm_, "[ Property"); |
1272 Expression* key = expr->key(); | 1696 Expression* key = expr->key(); |
1273 | 1697 |
1274 // Evaluate receiver. | 1698 // Evaluate receiver. |
1275 VisitForValue(expr->obj(), kStack); | 1699 VisitForValue(expr->obj(), kStack); |
1276 | 1700 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1312 | 1736 |
1313 void FullCodeGenerator::EmitCallWithStub(Call* expr) { | 1737 void FullCodeGenerator::EmitCallWithStub(Call* expr) { |
1314 // Code common for calls using the call stub. | 1738 // Code common for calls using the call stub. |
1315 ZoneList<Expression*>* args = expr->arguments(); | 1739 ZoneList<Expression*>* args = expr->arguments(); |
1316 int arg_count = args->length(); | 1740 int arg_count = args->length(); |
1317 for (int i = 0; i < arg_count; i++) { | 1741 for (int i = 0; i < arg_count; i++) { |
1318 VisitForValue(args->at(i), kStack); | 1742 VisitForValue(args->at(i), kStack); |
1319 } | 1743 } |
1320 // Record source position for debugger. | 1744 // Record source position for debugger. |
1321 SetSourcePosition(expr->position()); | 1745 SetSourcePosition(expr->position()); |
1322 CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE); | 1746 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 1747 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
1323 __ CallStub(&stub); | 1748 __ CallStub(&stub); |
1324 // Restore context register. | 1749 // Restore context register. |
1325 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | 1750 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
1326 // Discard the function left on TOS. | 1751 // Discard the function left on TOS. |
1327 DropAndApply(1, context_, rax); | 1752 DropAndApply(1, context_, rax); |
1328 } | 1753 } |
1329 | 1754 |
1330 | 1755 |
1331 void FullCodeGenerator::VisitCall(Call* expr) { | 1756 void FullCodeGenerator::VisitCall(Call* expr) { |
1332 Comment cmnt(masm_, "[ Call"); | 1757 Comment cmnt(masm_, "[ Call"); |
1333 Expression* fun = expr->expression(); | 1758 Expression* fun = expr->expression(); |
1334 Variable* var = fun->AsVariableProxy()->AsVariable(); | 1759 Variable* var = fun->AsVariableProxy()->AsVariable(); |
1335 | 1760 |
1336 if (var != NULL && var->is_possibly_eval()) { | 1761 if (var != NULL && var->is_possibly_eval()) { |
1337 // Call to the identifier 'eval'. | 1762 // In a call to eval, we first call %ResolvePossiblyDirectEval to |
1338 UNREACHABLE(); | 1763 // resolve the function we need to call and the receiver of the |
| 1764 // call. The we call the resolved function using the given |
| 1765 // arguments. |
| 1766 VisitForValue(fun, kStack); |
| 1767 __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot. |
| 1768 |
| 1769 // Push the arguments. |
| 1770 ZoneList<Expression*>* args = expr->arguments(); |
| 1771 int arg_count = args->length(); |
| 1772 for (int i = 0; i < arg_count; i++) { |
| 1773 VisitForValue(args->at(i), kStack); |
| 1774 } |
| 1775 |
| 1776 // Push copy of the function - found below the arguments. |
| 1777 __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); |
| 1778 |
| 1779 // Push copy of the first argument or undefined if it doesn't exist. |
| 1780 if (arg_count > 0) { |
| 1781 __ push(Operand(rsp, arg_count * kPointerSize)); |
| 1782 } else { |
| 1783 __ PushRoot(Heap::kUndefinedValueRootIndex); |
| 1784 } |
| 1785 |
| 1786 // Push the receiver of the enclosing function and do runtime call. |
| 1787 __ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize)); |
| 1788 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); |
| 1789 |
| 1790 // The runtime call returns a pair of values in rax (function) and |
| 1791 // rdx (receiver). Touch up the stack with the right values. |
| 1792 __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx); |
| 1793 __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax); |
| 1794 |
| 1795 // Record source position for debugger. |
| 1796 SetSourcePosition(expr->position()); |
| 1797 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 1798 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
| 1799 __ CallStub(&stub); |
| 1800 // Restore context register. |
| 1801 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
| 1802 DropAndApply(1, context_, rax); |
1339 } else if (var != NULL && !var->is_this() && var->is_global()) { | 1803 } else if (var != NULL && !var->is_this() && var->is_global()) { |
1340 // Call to a global variable. | 1804 // Call to a global variable. |
1341 // Push global object as receiver for the call IC lookup. | 1805 // Push global object as receiver for the call IC lookup. |
1342 __ push(CodeGenerator::GlobalObject()); | 1806 __ push(CodeGenerator::GlobalObject()); |
1343 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); | 1807 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); |
1344 } else if (var != NULL && var->slot() != NULL && | 1808 } else if (var != NULL && var->slot() != NULL && |
1345 var->slot()->type() == Slot::LOOKUP) { | 1809 var->slot()->type() == Slot::LOOKUP) { |
1346 // Call to a lookup slot. | 1810 // Call to a lookup slot (dynamically introduced variable). Call |
1347 UNREACHABLE(); | 1811 // the runtime to find the function to call (returned in rax) and |
| 1812 // the object holding it (returned in rdx). |
| 1813 __ push(context_register()); |
| 1814 __ Push(var->name()); |
| 1815 __ CallRuntime(Runtime::kLoadContextSlot, 2); |
| 1816 __ push(rax); // Function. |
| 1817 __ push(rdx); // Receiver. |
| 1818 EmitCallWithStub(expr); |
1348 } else if (fun->AsProperty() != NULL) { | 1819 } else if (fun->AsProperty() != NULL) { |
1349 // Call to an object property. | 1820 // Call to an object property. |
1350 Property* prop = fun->AsProperty(); | 1821 Property* prop = fun->AsProperty(); |
1351 Literal* key = prop->key()->AsLiteral(); | 1822 Literal* key = prop->key()->AsLiteral(); |
1352 if (key != NULL && key->handle()->IsSymbol()) { | 1823 if (key != NULL && key->handle()->IsSymbol()) { |
1353 // Call to a named property, use call IC. | 1824 // Call to a named property, use call IC. |
1354 VisitForValue(prop->obj(), kStack); | 1825 VisitForValue(prop->obj(), kStack); |
1355 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); | 1826 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); |
1356 } else { | 1827 } else { |
1357 // Call to a keyed property, use keyed load IC followed by function | 1828 // Call to a keyed property, use keyed load IC followed by function |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1428 __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); | 1899 __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); |
1429 | 1900 |
1430 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); | 1901 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); |
1431 __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); | 1902 __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); |
1432 | 1903 |
1433 // Replace function on TOS with result in rax, or pop it. | 1904 // Replace function on TOS with result in rax, or pop it. |
1434 DropAndApply(1, context_, rax); | 1905 DropAndApply(1, context_, rax); |
1435 } | 1906 } |
1436 | 1907 |
1437 | 1908 |
| 1909 void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) { |
| 1910 Handle<String> name = expr->name(); |
| 1911 if (strcmp("_IsSmi", *name->ToCString()) == 0) { |
| 1912 EmitIsSmi(expr->arguments()); |
| 1913 } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) { |
| 1914 EmitIsNonNegativeSmi(expr->arguments()); |
| 1915 } else if (strcmp("_IsObject", *name->ToCString()) == 0) { |
| 1916 EmitIsObject(expr->arguments()); |
| 1917 } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) { |
| 1918 EmitIsUndetectableObject(expr->arguments()); |
| 1919 } else if (strcmp("_IsFunction", *name->ToCString()) == 0) { |
| 1920 EmitIsFunction(expr->arguments()); |
| 1921 } else if (strcmp("_IsArray", *name->ToCString()) == 0) { |
| 1922 EmitIsArray(expr->arguments()); |
| 1923 } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) { |
| 1924 EmitIsRegExp(expr->arguments()); |
| 1925 } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) { |
| 1926 EmitIsConstructCall(expr->arguments()); |
| 1927 } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) { |
| 1928 EmitObjectEquals(expr->arguments()); |
| 1929 } else if (strcmp("_Arguments", *name->ToCString()) == 0) { |
| 1930 EmitArguments(expr->arguments()); |
| 1931 } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) { |
| 1932 EmitArgumentsLength(expr->arguments()); |
| 1933 } else if (strcmp("_ClassOf", *name->ToCString()) == 0) { |
| 1934 EmitClassOf(expr->arguments()); |
| 1935 } else if (strcmp("_Log", *name->ToCString()) == 0) { |
| 1936 EmitLog(expr->arguments()); |
| 1937 } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) { |
| 1938 EmitRandomHeapNumber(expr->arguments()); |
| 1939 } else if (strcmp("_SubString", *name->ToCString()) == 0) { |
| 1940 EmitSubString(expr->arguments()); |
| 1941 } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) { |
| 1942 EmitRegExpExec(expr->arguments()); |
| 1943 } else if (strcmp("_ValueOf", *name->ToCString()) == 0) { |
| 1944 EmitValueOf(expr->arguments()); |
| 1945 } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) { |
| 1946 EmitSetValueOf(expr->arguments()); |
| 1947 } else if (strcmp("_NumberToString", *name->ToCString()) == 0) { |
| 1948 EmitNumberToString(expr->arguments()); |
| 1949 } else if (strcmp("_CharFromCode", *name->ToCString()) == 0) { |
| 1950 EmitCharFromCode(expr->arguments()); |
| 1951 } else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) { |
| 1952 EmitFastCharCodeAt(expr->arguments()); |
| 1953 } else if (strcmp("_StringAdd", *name->ToCString()) == 0) { |
| 1954 EmitStringAdd(expr->arguments()); |
| 1955 } else if (strcmp("_StringCompare", *name->ToCString()) == 0) { |
| 1956 EmitStringCompare(expr->arguments()); |
| 1957 } else if (strcmp("_MathPow", *name->ToCString()) == 0) { |
| 1958 EmitMathPow(expr->arguments()); |
| 1959 } else if (strcmp("_MathSin", *name->ToCString()) == 0) { |
| 1960 EmitMathSin(expr->arguments()); |
| 1961 } else if (strcmp("_MathCos", *name->ToCString()) == 0) { |
| 1962 EmitMathCos(expr->arguments()); |
| 1963 } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) { |
| 1964 EmitMathSqrt(expr->arguments()); |
| 1965 } else if (strcmp("_CallFunction", *name->ToCString()) == 0) { |
| 1966 EmitCallFunction(expr->arguments()); |
| 1967 } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) { |
| 1968 EmitRegExpConstructResult(expr->arguments()); |
| 1969 } else if (strcmp("_SwapElements", *name->ToCString()) == 0) { |
| 1970 EmitSwapElements(expr->arguments()); |
| 1971 } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) { |
| 1972 EmitGetFromCache(expr->arguments()); |
| 1973 } else { |
| 1974 UNREACHABLE(); |
| 1975 } |
| 1976 } |
| 1977 |
| 1978 |
| 1979 void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { |
| 1980 ASSERT(args->length() == 1); |
| 1981 |
| 1982 VisitForValue(args->at(0), kAccumulator); |
| 1983 |
| 1984 Label materialize_true, materialize_false; |
| 1985 Label* if_true = NULL; |
| 1986 Label* if_false = NULL; |
| 1987 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 1988 |
| 1989 __ JumpIfSmi(rax, if_true); |
| 1990 __ jmp(if_false); |
| 1991 |
| 1992 Apply(context_, if_true, if_false); |
| 1993 } |
| 1994 |
| 1995 |
| 1996 void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { |
| 1997 ASSERT(args->length() == 1); |
| 1998 |
| 1999 VisitForValue(args->at(0), kAccumulator); |
| 2000 |
| 2001 Label materialize_true, materialize_false; |
| 2002 Label* if_true = NULL; |
| 2003 Label* if_false = NULL; |
| 2004 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2005 |
| 2006 Condition positive_smi = __ CheckPositiveSmi(rax); |
| 2007 __ j(positive_smi, if_true); |
| 2008 __ jmp(if_false); |
| 2009 |
| 2010 Apply(context_, if_true, if_false); |
| 2011 } |
| 2012 |
| 2013 |
| 2014 void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { |
| 2015 ASSERT(args->length() == 1); |
| 2016 |
| 2017 VisitForValue(args->at(0), kAccumulator); |
| 2018 |
| 2019 Label materialize_true, materialize_false; |
| 2020 Label* if_true = NULL; |
| 2021 Label* if_false = NULL; |
| 2022 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2023 |
| 2024 __ JumpIfSmi(rax, if_false); |
| 2025 __ CompareRoot(rax, Heap::kNullValueRootIndex); |
| 2026 __ j(equal, if_true); |
| 2027 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); |
| 2028 // Undetectable objects behave like undefined when tested with typeof. |
| 2029 __ testb(FieldOperand(rbx, Map::kBitFieldOffset), |
| 2030 Immediate(1 << Map::kIsUndetectable)); |
| 2031 __ j(not_zero, if_false); |
| 2032 __ movzxbq(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); |
| 2033 __ cmpq(rbx, Immediate(FIRST_JS_OBJECT_TYPE)); |
| 2034 __ j(below, if_false); |
| 2035 __ cmpq(rbx, Immediate(LAST_JS_OBJECT_TYPE)); |
| 2036 __ j(below_equal, if_true); |
| 2037 __ jmp(if_false); |
| 2038 |
| 2039 Apply(context_, if_true, if_false); |
| 2040 } |
| 2041 |
| 2042 |
| 2043 void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { |
| 2044 ASSERT(args->length() == 1); |
| 2045 |
| 2046 VisitForValue(args->at(0), kAccumulator); |
| 2047 |
| 2048 Label materialize_true, materialize_false; |
| 2049 Label* if_true = NULL; |
| 2050 Label* if_false = NULL; |
| 2051 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2052 |
| 2053 __ JumpIfSmi(rax, if_false); |
| 2054 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); |
| 2055 __ testb(FieldOperand(rbx, Map::kBitFieldOffset), |
| 2056 Immediate(1 << Map::kIsUndetectable)); |
| 2057 __ j(not_zero, if_true); |
| 2058 __ jmp(if_false); |
| 2059 |
| 2060 Apply(context_, if_true, if_false); |
| 2061 } |
| 2062 |
| 2063 |
| 2064 void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { |
| 2065 ASSERT(args->length() == 1); |
| 2066 |
| 2067 VisitForValue(args->at(0), kAccumulator); |
| 2068 |
| 2069 Label materialize_true, materialize_false; |
| 2070 Label* if_true = NULL; |
| 2071 Label* if_false = NULL; |
| 2072 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2073 |
| 2074 __ JumpIfSmi(rax, if_false); |
| 2075 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); |
| 2076 __ j(equal, if_true); |
| 2077 __ jmp(if_false); |
| 2078 |
| 2079 Apply(context_, if_true, if_false); |
| 2080 } |
| 2081 |
| 2082 |
| 2083 void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { |
| 2084 ASSERT(args->length() == 1); |
| 2085 |
| 2086 VisitForValue(args->at(0), kAccumulator); |
| 2087 |
| 2088 Label materialize_true, materialize_false; |
| 2089 Label* if_true = NULL; |
| 2090 Label* if_false = NULL; |
| 2091 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2092 |
| 2093 __ JumpIfSmi(rax, if_false); |
| 2094 __ CmpObjectType(rax, JS_ARRAY_TYPE, rbx); |
| 2095 __ j(equal, if_true); |
| 2096 __ jmp(if_false); |
| 2097 |
| 2098 Apply(context_, if_true, if_false); |
| 2099 } |
| 2100 |
| 2101 |
| 2102 void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { |
| 2103 ASSERT(args->length() == 1); |
| 2104 |
| 2105 VisitForValue(args->at(0), kAccumulator); |
| 2106 |
| 2107 Label materialize_true, materialize_false; |
| 2108 Label* if_true = NULL; |
| 2109 Label* if_false = NULL; |
| 2110 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2111 |
| 2112 __ JumpIfSmi(rax, if_false); |
| 2113 __ CmpObjectType(rax, JS_REGEXP_TYPE, rbx); |
| 2114 __ j(equal, if_true); |
| 2115 __ jmp(if_false); |
| 2116 |
| 2117 Apply(context_, if_true, if_false); |
| 2118 } |
| 2119 |
| 2120 |
| 2121 |
| 2122 void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { |
| 2123 ASSERT(args->length() == 0); |
| 2124 |
| 2125 Label materialize_true, materialize_false; |
| 2126 Label* if_true = NULL; |
| 2127 Label* if_false = NULL; |
| 2128 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2129 |
| 2130 // Get the frame pointer for the calling frame. |
| 2131 __ movq(rax, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); |
| 2132 |
| 2133 // Skip the arguments adaptor frame if it exists. |
| 2134 Label check_frame_marker; |
| 2135 __ SmiCompare(Operand(rax, StandardFrameConstants::kContextOffset), |
| 2136 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); |
| 2137 __ j(not_equal, &check_frame_marker); |
| 2138 __ movq(rax, Operand(rax, StandardFrameConstants::kCallerFPOffset)); |
| 2139 |
| 2140 // Check the marker in the calling frame. |
| 2141 __ bind(&check_frame_marker); |
| 2142 __ SmiCompare(Operand(rax, StandardFrameConstants::kMarkerOffset), |
| 2143 Smi::FromInt(StackFrame::CONSTRUCT)); |
| 2144 __ j(equal, if_true); |
| 2145 __ jmp(if_false); |
| 2146 |
| 2147 Apply(context_, if_true, if_false); |
| 2148 } |
| 2149 |
| 2150 |
| 2151 void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { |
| 2152 ASSERT(args->length() == 2); |
| 2153 |
| 2154 // Load the two objects into registers and perform the comparison. |
| 2155 VisitForValue(args->at(0), kStack); |
| 2156 VisitForValue(args->at(1), kAccumulator); |
| 2157 |
| 2158 Label materialize_true, materialize_false; |
| 2159 Label* if_true = NULL; |
| 2160 Label* if_false = NULL; |
| 2161 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2162 |
| 2163 __ pop(rbx); |
| 2164 __ cmpq(rax, rbx); |
| 2165 __ j(equal, if_true); |
| 2166 __ jmp(if_false); |
| 2167 |
| 2168 Apply(context_, if_true, if_false); |
| 2169 } |
| 2170 |
| 2171 |
| 2172 void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { |
| 2173 ASSERT(args->length() == 1); |
| 2174 |
| 2175 // ArgumentsAccessStub expects the key in edx and the formal |
| 2176 // parameter count in eax. |
| 2177 VisitForValue(args->at(0), kAccumulator); |
| 2178 __ movq(rdx, rax); |
| 2179 __ Move(rax, Smi::FromInt(scope()->num_parameters())); |
| 2180 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); |
| 2181 __ CallStub(&stub); |
| 2182 Apply(context_, rax); |
| 2183 } |
| 2184 |
| 2185 |
| 2186 void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { |
| 2187 ASSERT(args->length() == 0); |
| 2188 |
| 2189 Label exit; |
| 2190 // Get the number of formal parameters. |
| 2191 __ Move(rax, Smi::FromInt(scope()->num_parameters())); |
| 2192 |
| 2193 // Check if the calling frame is an arguments adaptor frame. |
| 2194 __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); |
| 2195 __ SmiCompare(Operand(rbx, StandardFrameConstants::kContextOffset), |
| 2196 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); |
| 2197 __ j(not_equal, &exit); |
| 2198 |
| 2199 // Arguments adaptor case: Read the arguments length from the |
| 2200 // adaptor frame. |
| 2201 __ movq(rax, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| 2202 |
| 2203 __ bind(&exit); |
| 2204 if (FLAG_debug_code) __ AbortIfNotSmi(rax); |
| 2205 Apply(context_, rax); |
| 2206 } |
| 2207 |
| 2208 |
| 2209 void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { |
| 2210 ASSERT(args->length() == 1); |
| 2211 Label done, null, function, non_function_constructor; |
| 2212 |
| 2213 VisitForValue(args->at(0), kAccumulator); |
| 2214 |
| 2215 // If the object is a smi, we return null. |
| 2216 __ JumpIfSmi(rax, &null); |
| 2217 |
| 2218 // Check that the object is a JS object but take special care of JS |
| 2219 // functions to make sure they have 'Function' as their class. |
| 2220 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rax); |
| 2221 __ j(below, &null); |
| 2222 |
| 2223 // As long as JS_FUNCTION_TYPE is the last instance type and it is |
| 2224 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for |
| 2225 // LAST_JS_OBJECT_TYPE. |
| 2226 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); |
| 2227 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); |
| 2228 __ CmpInstanceType(rax, JS_FUNCTION_TYPE); |
| 2229 __ j(equal, &function); |
| 2230 |
| 2231 // Check if the constructor in the map is a function. |
| 2232 __ movq(rax, FieldOperand(rax, Map::kConstructorOffset)); |
| 2233 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); |
| 2234 __ j(not_equal, &non_function_constructor); |
| 2235 |
| 2236 // rax now contains the constructor function. Grab the |
| 2237 // instance class name from there. |
| 2238 __ movq(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset)); |
| 2239 __ movq(rax, FieldOperand(rax, SharedFunctionInfo::kInstanceClassNameOffset)); |
| 2240 __ jmp(&done); |
| 2241 |
| 2242 // Functions have class 'Function'. |
| 2243 __ bind(&function); |
| 2244 __ Move(rax, Factory::function_class_symbol()); |
| 2245 __ jmp(&done); |
| 2246 |
| 2247 // Objects with a non-function constructor have class 'Object'. |
| 2248 __ bind(&non_function_constructor); |
| 2249 __ Move(rax, Factory::Object_symbol()); |
| 2250 __ jmp(&done); |
| 2251 |
| 2252 // Non-JS objects have class null. |
| 2253 __ bind(&null); |
| 2254 __ LoadRoot(rax, Heap::kNullValueRootIndex); |
| 2255 |
| 2256 // All done. |
| 2257 __ bind(&done); |
| 2258 |
| 2259 Apply(context_, rax); |
| 2260 } |
| 2261 |
| 2262 |
| 2263 void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { |
| 2264 // Conditionally generate a log call. |
| 2265 // Args: |
| 2266 // 0 (literal string): The type of logging (corresponds to the flags). |
| 2267 // This is used to determine whether or not to generate the log call. |
| 2268 // 1 (string): Format string. Access the string at argument index 2 |
| 2269 // with '%2s' (see Logger::LogRuntime for all the formats). |
| 2270 // 2 (array): Arguments to the format string. |
| 2271 ASSERT_EQ(args->length(), 3); |
| 2272 #ifdef ENABLE_LOGGING_AND_PROFILING |
| 2273 if (CodeGenerator::ShouldGenerateLog(args->at(0))) { |
| 2274 VisitForValue(args->at(1), kStack); |
| 2275 VisitForValue(args->at(2), kStack); |
| 2276 __ CallRuntime(Runtime::kLog, 2); |
| 2277 } |
| 2278 #endif |
| 2279 // Finally, we're expected to leave a value on the top of the stack. |
| 2280 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 2281 Apply(context_, rax); |
| 2282 } |
| 2283 |
| 2284 |
| 2285 void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { |
| 2286 ASSERT(args->length() == 0); |
| 2287 |
| 2288 Label slow_allocate_heapnumber; |
| 2289 Label heapnumber_allocated; |
| 2290 |
| 2291 __ AllocateHeapNumber(rbx, rcx, &slow_allocate_heapnumber); |
| 2292 __ jmp(&heapnumber_allocated); |
| 2293 |
| 2294 __ bind(&slow_allocate_heapnumber); |
| 2295 // To allocate a heap number, and ensure that it is not a smi, we |
| 2296 // call the runtime function FUnaryMinus on 0, returning the double |
| 2297 // -0.0. A new, distinct heap number is returned each time. |
| 2298 __ Push(Smi::FromInt(0)); |
| 2299 __ CallRuntime(Runtime::kNumberUnaryMinus, 1); |
| 2300 __ movq(rbx, rax); |
| 2301 |
| 2302 __ bind(&heapnumber_allocated); |
| 2303 |
| 2304 // Return a random uint32 number in rax. |
| 2305 // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs. |
| 2306 __ PrepareCallCFunction(0); |
| 2307 __ CallCFunction(ExternalReference::random_uint32_function(), 0); |
| 2308 |
| 2309 // Convert 32 random bits in rax to 0.(32 random bits) in a double |
| 2310 // by computing: |
| 2311 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). |
| 2312 __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single. |
| 2313 __ movd(xmm1, rcx); |
| 2314 __ movd(xmm0, rax); |
| 2315 __ cvtss2sd(xmm1, xmm1); |
| 2316 __ xorpd(xmm0, xmm1); |
| 2317 __ subsd(xmm0, xmm1); |
| 2318 __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0); |
| 2319 |
| 2320 __ movq(rax, rbx); |
| 2321 Apply(context_, rax); |
| 2322 } |
| 2323 |
| 2324 |
| 2325 void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { |
| 2326 // Load the arguments on the stack and call the stub. |
| 2327 SubStringStub stub; |
| 2328 ASSERT(args->length() == 3); |
| 2329 VisitForValue(args->at(0), kStack); |
| 2330 VisitForValue(args->at(1), kStack); |
| 2331 VisitForValue(args->at(2), kStack); |
| 2332 __ CallStub(&stub); |
| 2333 Apply(context_, rax); |
| 2334 } |
| 2335 |
| 2336 |
| 2337 void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { |
| 2338 // Load the arguments on the stack and call the stub. |
| 2339 RegExpExecStub stub; |
| 2340 ASSERT(args->length() == 4); |
| 2341 VisitForValue(args->at(0), kStack); |
| 2342 VisitForValue(args->at(1), kStack); |
| 2343 VisitForValue(args->at(2), kStack); |
| 2344 VisitForValue(args->at(3), kStack); |
| 2345 __ CallStub(&stub); |
| 2346 Apply(context_, rax); |
| 2347 } |
| 2348 |
| 2349 |
| 2350 void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { |
| 2351 ASSERT(args->length() == 1); |
| 2352 |
| 2353 VisitForValue(args->at(0), kAccumulator); // Load the object. |
| 2354 |
| 2355 Label done; |
| 2356 // If the object is a smi return the object. |
| 2357 __ JumpIfSmi(rax, &done); |
| 2358 // If the object is not a value type, return the object. |
| 2359 __ CmpObjectType(rax, JS_VALUE_TYPE, rbx); |
| 2360 __ j(not_equal, &done); |
| 2361 __ movq(rax, FieldOperand(rax, JSValue::kValueOffset)); |
| 2362 |
| 2363 __ bind(&done); |
| 2364 Apply(context_, rax); |
| 2365 } |
| 2366 |
| 2367 |
| 2368 void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { |
| 2369 // Load the arguments on the stack and call the runtime function. |
| 2370 ASSERT(args->length() == 2); |
| 2371 VisitForValue(args->at(0), kStack); |
| 2372 VisitForValue(args->at(1), kStack); |
| 2373 __ CallRuntime(Runtime::kMath_pow, 2); |
| 2374 Apply(context_, rax); |
| 2375 } |
| 2376 |
| 2377 |
| 2378 void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { |
| 2379 ASSERT(args->length() == 2); |
| 2380 |
| 2381 VisitForValue(args->at(0), kStack); // Load the object. |
| 2382 VisitForValue(args->at(1), kAccumulator); // Load the value. |
| 2383 __ pop(rbx); // rax = value. ebx = object. |
| 2384 |
| 2385 Label done; |
| 2386 // If the object is a smi, return the value. |
| 2387 __ JumpIfSmi(rbx, &done); |
| 2388 |
| 2389 // If the object is not a value type, return the value. |
| 2390 __ CmpObjectType(rbx, JS_VALUE_TYPE, rcx); |
| 2391 __ j(not_equal, &done); |
| 2392 |
| 2393 // Store the value. |
| 2394 __ movq(FieldOperand(rbx, JSValue::kValueOffset), rax); |
| 2395 // Update the write barrier. Save the value as it will be |
| 2396 // overwritten by the write barrier code and is needed afterward. |
| 2397 __ movq(rdx, rax); |
| 2398 __ RecordWrite(rbx, JSValue::kValueOffset, rdx, rcx); |
| 2399 |
| 2400 __ bind(&done); |
| 2401 Apply(context_, rax); |
| 2402 } |
| 2403 |
| 2404 |
| 2405 void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { |
| 2406 ASSERT_EQ(args->length(), 1); |
| 2407 |
| 2408 // Load the argument on the stack and call the stub. |
| 2409 VisitForValue(args->at(0), kStack); |
| 2410 |
| 2411 NumberToStringStub stub; |
| 2412 __ CallStub(&stub); |
| 2413 Apply(context_, rax); |
| 2414 } |
| 2415 |
| 2416 |
| 2417 void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) { |
| 2418 ASSERT(args->length() == 1); |
| 2419 |
| 2420 VisitForValue(args->at(0), kAccumulator); |
| 2421 |
| 2422 Label slow_case, done; |
| 2423 // Fast case of Heap::LookupSingleCharacterStringFromCode. |
| 2424 __ JumpIfNotSmi(rax, &slow_case); |
| 2425 __ SmiToInteger32(rcx, rax); |
| 2426 __ cmpl(rcx, Immediate(String::kMaxAsciiCharCode)); |
| 2427 __ j(above, &slow_case); |
| 2428 |
| 2429 __ Move(rbx, Factory::single_character_string_cache()); |
| 2430 __ movq(rbx, FieldOperand(rbx, |
| 2431 rcx, |
| 2432 times_pointer_size, |
| 2433 FixedArray::kHeaderSize)); |
| 2434 |
| 2435 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); |
| 2436 __ j(equal, &slow_case); |
| 2437 __ movq(rax, rbx); |
| 2438 __ jmp(&done); |
| 2439 |
| 2440 __ bind(&slow_case); |
| 2441 __ push(rax); |
| 2442 __ CallRuntime(Runtime::kCharFromCode, 1); |
| 2443 |
| 2444 __ bind(&done); |
| 2445 Apply(context_, rax); |
| 2446 } |
| 2447 |
| 2448 |
| 2449 void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) { |
| 2450 // TODO(fsc): Port the complete implementation from the classic back-end. |
| 2451 // Move the undefined value into the result register, which will |
| 2452 // trigger the slow case. |
| 2453 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 2454 Apply(context_, rax); |
| 2455 } |
| 2456 |
| 2457 void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { |
| 2458 ASSERT_EQ(2, args->length()); |
| 2459 |
| 2460 VisitForValue(args->at(0), kStack); |
| 2461 VisitForValue(args->at(1), kStack); |
| 2462 |
| 2463 StringAddStub stub(NO_STRING_ADD_FLAGS); |
| 2464 __ CallStub(&stub); |
| 2465 Apply(context_, rax); |
| 2466 } |
| 2467 |
| 2468 |
| 2469 void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { |
| 2470 ASSERT_EQ(2, args->length()); |
| 2471 |
| 2472 VisitForValue(args->at(0), kStack); |
| 2473 VisitForValue(args->at(1), kStack); |
| 2474 |
| 2475 StringCompareStub stub; |
| 2476 __ CallStub(&stub); |
| 2477 Apply(context_, rax); |
| 2478 } |
| 2479 |
| 2480 |
| 2481 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { |
| 2482 // Load the argument on the stack and call the stub. |
| 2483 TranscendentalCacheStub stub(TranscendentalCache::SIN); |
| 2484 ASSERT(args->length() == 1); |
| 2485 VisitForValue(args->at(0), kStack); |
| 2486 __ CallStub(&stub); |
| 2487 Apply(context_, rax); |
| 2488 } |
| 2489 |
| 2490 |
| 2491 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { |
| 2492 // Load the argument on the stack and call the stub. |
| 2493 TranscendentalCacheStub stub(TranscendentalCache::COS); |
| 2494 ASSERT(args->length() == 1); |
| 2495 VisitForValue(args->at(0), kStack); |
| 2496 __ CallStub(&stub); |
| 2497 Apply(context_, rax); |
| 2498 } |
| 2499 |
| 2500 |
| 2501 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { |
| 2502 // Load the argument on the stack and call the runtime function. |
| 2503 ASSERT(args->length() == 1); |
| 2504 VisitForValue(args->at(0), kStack); |
| 2505 __ CallRuntime(Runtime::kMath_sqrt, 1); |
| 2506 Apply(context_, rax); |
| 2507 } |
| 2508 |
| 2509 |
| 2510 void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { |
| 2511 ASSERT(args->length() >= 2); |
| 2512 |
| 2513 int arg_count = args->length() - 2; // For receiver and function. |
| 2514 VisitForValue(args->at(0), kStack); // Receiver. |
| 2515 for (int i = 0; i < arg_count; i++) { |
| 2516 VisitForValue(args->at(i + 1), kStack); |
| 2517 } |
| 2518 VisitForValue(args->at(arg_count + 1), kAccumulator); // Function. |
| 2519 |
| 2520 // InvokeFunction requires function in rdi. Move it in there. |
| 2521 if (!result_register().is(rdi)) __ movq(rdi, result_register()); |
| 2522 ParameterCount count(arg_count); |
| 2523 __ InvokeFunction(rdi, count, CALL_FUNCTION); |
| 2524 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
| 2525 Apply(context_, rax); |
| 2526 } |
| 2527 |
| 2528 |
| 2529 void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { |
| 2530 ASSERT(args->length() == 3); |
| 2531 VisitForValue(args->at(0), kStack); |
| 2532 VisitForValue(args->at(1), kStack); |
| 2533 VisitForValue(args->at(2), kStack); |
| 2534 __ CallRuntime(Runtime::kRegExpConstructResult, 3); |
| 2535 Apply(context_, rax); |
| 2536 } |
| 2537 |
| 2538 |
| 2539 void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { |
| 2540 ASSERT(args->length() == 3); |
| 2541 VisitForValue(args->at(0), kStack); |
| 2542 VisitForValue(args->at(1), kStack); |
| 2543 VisitForValue(args->at(2), kStack); |
| 2544 __ CallRuntime(Runtime::kSwapElements, 3); |
| 2545 Apply(context_, rax); |
| 2546 } |
| 2547 |
| 2548 |
| 2549 void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { |
| 2550 ASSERT_EQ(2, args->length()); |
| 2551 |
| 2552 ASSERT_NE(NULL, args->at(0)->AsLiteral()); |
| 2553 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); |
| 2554 |
| 2555 Handle<FixedArray> jsfunction_result_caches( |
| 2556 Top::global_context()->jsfunction_result_caches()); |
| 2557 if (jsfunction_result_caches->length() <= cache_id) { |
| 2558 __ Abort("Attempt to use undefined cache."); |
| 2559 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 2560 Apply(context_, rax); |
| 2561 return; |
| 2562 } |
| 2563 |
| 2564 VisitForValue(args->at(1), kAccumulator); |
| 2565 |
| 2566 Register key = rax; |
| 2567 Register cache = rbx; |
| 2568 Register tmp = rcx; |
| 2569 __ movq(cache, CodeGenerator::ContextOperand(rsi, Context::GLOBAL_INDEX)); |
| 2570 __ movq(cache, |
| 2571 FieldOperand(cache, GlobalObject::kGlobalContextOffset)); |
| 2572 __ movq(cache, |
| 2573 CodeGenerator::ContextOperand( |
| 2574 cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); |
| 2575 __ movq(cache, |
| 2576 FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); |
| 2577 |
| 2578 Label done, not_found; |
| 2579 // tmp now holds finger offset as a smi. |
| 2580 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
| 2581 __ movq(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset)); |
| 2582 SmiIndex index = |
| 2583 __ SmiToIndex(kScratchRegister, tmp, kPointerSizeLog2); |
| 2584 __ cmpq(key, FieldOperand(cache, |
| 2585 index.reg, |
| 2586 index.scale, |
| 2587 FixedArray::kHeaderSize)); |
| 2588 __ j(not_equal, ¬_found); |
| 2589 __ movq(rax, FieldOperand(cache, |
| 2590 index.reg, |
| 2591 index.scale, |
| 2592 FixedArray::kHeaderSize + kPointerSize)); |
| 2593 __ jmp(&done); |
| 2594 |
| 2595 __ bind(¬_found); |
| 2596 // Call runtime to perform the lookup. |
| 2597 __ push(cache); |
| 2598 __ push(key); |
| 2599 __ CallRuntime(Runtime::kGetFromCache, 2); |
| 2600 |
| 2601 __ bind(&done); |
| 2602 Apply(context_, rax); |
| 2603 } |
| 2604 |
| 2605 |
1438 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { | 2606 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { |
| 2607 Handle<String> name = expr->name(); |
| 2608 if (name->length() > 0 && name->Get(0) == '_') { |
| 2609 Comment cmnt(masm_, "[ InlineRuntimeCall"); |
| 2610 EmitInlineRuntimeCall(expr); |
| 2611 return; |
| 2612 } |
| 2613 |
1439 Comment cmnt(masm_, "[ CallRuntime"); | 2614 Comment cmnt(masm_, "[ CallRuntime"); |
1440 ZoneList<Expression*>* args = expr->arguments(); | 2615 ZoneList<Expression*>* args = expr->arguments(); |
1441 | 2616 |
1442 if (expr->is_jsruntime()) { | 2617 if (expr->is_jsruntime()) { |
1443 // Prepare for calling JS runtime function. | 2618 // Prepare for calling JS runtime function. |
1444 __ movq(rax, CodeGenerator::GlobalObject()); | 2619 __ movq(rax, CodeGenerator::GlobalObject()); |
1445 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); | 2620 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); |
1446 } | 2621 } |
1447 | 2622 |
1448 // Push the arguments ("left-to-right"). | 2623 // Push the arguments ("left-to-right"). |
(...skipping 12 matching lines...) Expand all Loading... |
1461 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | 2636 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
1462 } else { | 2637 } else { |
1463 __ CallRuntime(expr->function(), arg_count); | 2638 __ CallRuntime(expr->function(), arg_count); |
1464 } | 2639 } |
1465 Apply(context_, rax); | 2640 Apply(context_, rax); |
1466 } | 2641 } |
1467 | 2642 |
1468 | 2643 |
1469 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { | 2644 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { |
1470 switch (expr->op()) { | 2645 switch (expr->op()) { |
| 2646 case Token::DELETE: { |
| 2647 Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); |
| 2648 Property* prop = expr->expression()->AsProperty(); |
| 2649 Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); |
| 2650 if (prop == NULL && var == NULL) { |
| 2651 // Result of deleting non-property, non-variable reference is true. |
| 2652 // The subexpression may have side effects. |
| 2653 VisitForEffect(expr->expression()); |
| 2654 Apply(context_, true); |
| 2655 } else if (var != NULL && |
| 2656 !var->is_global() && |
| 2657 var->slot() != NULL && |
| 2658 var->slot()->type() != Slot::LOOKUP) { |
| 2659 // Result of deleting non-global, non-dynamic variables is false. |
| 2660 // The subexpression does not have side effects. |
| 2661 Apply(context_, false); |
| 2662 } else { |
| 2663 // Property or variable reference. Call the delete builtin with |
| 2664 // object and property name as arguments. |
| 2665 if (prop != NULL) { |
| 2666 VisitForValue(prop->obj(), kStack); |
| 2667 VisitForValue(prop->key(), kStack); |
| 2668 } else if (var->is_global()) { |
| 2669 __ push(CodeGenerator::GlobalObject()); |
| 2670 __ Push(var->name()); |
| 2671 } else { |
| 2672 // Non-global variable. Call the runtime to look up the context |
| 2673 // where the variable was introduced. |
| 2674 __ push(context_register()); |
| 2675 __ Push(var->name()); |
| 2676 __ CallRuntime(Runtime::kLookupContext, 2); |
| 2677 __ push(rax); |
| 2678 __ Push(var->name()); |
| 2679 } |
| 2680 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); |
| 2681 Apply(context_, rax); |
| 2682 } |
| 2683 break; |
| 2684 } |
| 2685 |
1471 case Token::VOID: { | 2686 case Token::VOID: { |
1472 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); | 2687 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); |
1473 VisitForEffect(expr->expression()); | 2688 VisitForEffect(expr->expression()); |
1474 switch (context_) { | 2689 switch (context_) { |
1475 case Expression::kUninitialized: | 2690 case Expression::kUninitialized: |
1476 UNREACHABLE(); | 2691 UNREACHABLE(); |
1477 break; | 2692 break; |
1478 case Expression::kEffect: | 2693 case Expression::kEffect: |
1479 break; | 2694 break; |
1480 case Expression::kValue: | 2695 case Expression::kValue: |
(...skipping 20 matching lines...) Expand all Loading... |
1501 case Expression::kTest: | 2716 case Expression::kTest: |
1502 case Expression::kValueTest: | 2717 case Expression::kValueTest: |
1503 __ jmp(false_label_); | 2718 __ jmp(false_label_); |
1504 break; | 2719 break; |
1505 } | 2720 } |
1506 break; | 2721 break; |
1507 } | 2722 } |
1508 | 2723 |
1509 case Token::NOT: { | 2724 case Token::NOT: { |
1510 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); | 2725 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); |
1511 Label materialize_true, materialize_false, done; | 2726 Label materialize_true, materialize_false; |
1512 // Initially assume a pure test context. Notice that the labels are | 2727 Label* if_true = NULL; |
1513 // swapped. | 2728 Label* if_false = NULL; |
1514 Label* if_true = false_label_; | 2729 |
1515 Label* if_false = true_label_; | 2730 // Notice that the labels are swapped. |
1516 switch (context_) { | 2731 PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true); |
1517 case Expression::kUninitialized: | 2732 |
1518 UNREACHABLE(); | |
1519 break; | |
1520 case Expression::kEffect: | |
1521 if_true = &done; | |
1522 if_false = &done; | |
1523 break; | |
1524 case Expression::kValue: | |
1525 if_true = &materialize_false; | |
1526 if_false = &materialize_true; | |
1527 break; | |
1528 case Expression::kTest: | |
1529 break; | |
1530 case Expression::kValueTest: | |
1531 if_false = &materialize_true; | |
1532 break; | |
1533 case Expression::kTestValue: | |
1534 if_true = &materialize_false; | |
1535 break; | |
1536 } | |
1537 VisitForControl(expr->expression(), if_true, if_false); | 2733 VisitForControl(expr->expression(), if_true, if_false); |
| 2734 |
1538 Apply(context_, if_false, if_true); // Labels swapped. | 2735 Apply(context_, if_false, if_true); // Labels swapped. |
1539 break; | 2736 break; |
1540 } | 2737 } |
1541 | 2738 |
1542 case Token::TYPEOF: { | 2739 case Token::TYPEOF: { |
1543 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); | 2740 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); |
1544 VariableProxy* proxy = expr->expression()->AsVariableProxy(); | 2741 VariableProxy* proxy = expr->expression()->AsVariableProxy(); |
1545 if (proxy != NULL && | 2742 if (proxy != NULL && |
1546 !proxy->var()->is_this() && | 2743 !proxy->var()->is_this() && |
1547 proxy->var()->is_global()) { | 2744 proxy->var()->is_global()) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1623 | 2820 |
1624 default: | 2821 default: |
1625 UNREACHABLE(); | 2822 UNREACHABLE(); |
1626 } | 2823 } |
1627 } | 2824 } |
1628 | 2825 |
1629 | 2826 |
1630 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { | 2827 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { |
1631 Comment cmnt(masm_, "[ CountOperation"); | 2828 Comment cmnt(masm_, "[ CountOperation"); |
1632 | 2829 |
| 2830 // Invalid left-hand-sides are rewritten to have a 'throw |
| 2831 // ReferenceError' as the left-hand side. |
| 2832 if (!expr->expression()->IsValidLeftHandSide()) { |
| 2833 VisitForEffect(expr->expression()); |
| 2834 return; |
| 2835 } |
| 2836 |
1633 // Expression can only be a property, a global or a (parameter or local) | 2837 // Expression can only be a property, a global or a (parameter or local) |
1634 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. | 2838 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
1635 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; | 2839 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
1636 LhsKind assign_type = VARIABLE; | 2840 LhsKind assign_type = VARIABLE; |
1637 Property* prop = expr->expression()->AsProperty(); | 2841 Property* prop = expr->expression()->AsProperty(); |
1638 // In case of a property we use the uninitialized expression context | 2842 // In case of a property we use the uninitialized expression context |
1639 // of the key to detect a named property. | 2843 // of the key to detect a named property. |
1640 if (prop != NULL) { | 2844 if (prop != NULL) { |
1641 assign_type = | 2845 assign_type = |
1642 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; | 2846 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; |
1643 } | 2847 } |
1644 | 2848 |
1645 // Evaluate expression and get value. | 2849 // Evaluate expression and get value. |
1646 if (assign_type == VARIABLE) { | 2850 if (assign_type == VARIABLE) { |
1647 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); | 2851 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); |
1648 Location saved_location = location_; | 2852 Location saved_location = location_; |
1649 location_ = kAccumulator; | 2853 location_ = kAccumulator; |
1650 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), | 2854 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), |
1651 Expression::kValue); | 2855 Expression::kValue); |
1652 location_ = saved_location; | 2856 location_ = saved_location; |
1653 } else { | 2857 } else { |
1654 // Reserve space for result of postfix operation. | 2858 // Reserve space for result of postfix operation. |
1655 if (expr->is_postfix() && context_ != Expression::kEffect) { | 2859 if (expr->is_postfix() && context_ != Expression::kEffect) { |
1656 __ Push(Smi::FromInt(0)); | 2860 __ Push(Smi::FromInt(0)); |
1657 } | 2861 } |
1658 VisitForValue(prop->obj(), kStack); | 2862 VisitForValue(prop->obj(), kStack); |
1659 if (assign_type == NAMED_PROPERTY) { | 2863 if (assign_type == NAMED_PROPERTY) { |
1660 EmitNamedPropertyLoad(prop); | 2864 EmitNamedPropertyLoad(prop); |
1661 } else { | 2865 } else { |
1662 VisitForValue(prop->key(), kStack); | 2866 VisitForValue(prop->key(), kStack); |
1663 EmitKeyedPropertyLoad(prop); | 2867 EmitKeyedPropertyLoad(prop); |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1728 GenericBinaryOpStub stub(expr->binary_op(), | 2932 GenericBinaryOpStub stub(expr->binary_op(), |
1729 NO_OVERWRITE, | 2933 NO_OVERWRITE, |
1730 NO_GENERIC_BINARY_FLAGS); | 2934 NO_GENERIC_BINARY_FLAGS); |
1731 stub.GenerateCall(masm_, rax, Smi::FromInt(1)); | 2935 stub.GenerateCall(masm_, rax, Smi::FromInt(1)); |
1732 __ bind(&done); | 2936 __ bind(&done); |
1733 | 2937 |
1734 // Store the value returned in rax. | 2938 // Store the value returned in rax. |
1735 switch (assign_type) { | 2939 switch (assign_type) { |
1736 case VARIABLE: | 2940 case VARIABLE: |
1737 if (expr->is_postfix()) { | 2941 if (expr->is_postfix()) { |
| 2942 // Perform the assignment as if via '='. |
1738 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | 2943 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), |
| 2944 Token::ASSIGN, |
1739 Expression::kEffect); | 2945 Expression::kEffect); |
1740 // For all contexts except kEffect: We have the result on | 2946 // For all contexts except kEffect: We have the result on |
1741 // top of the stack. | 2947 // top of the stack. |
1742 if (context_ != Expression::kEffect) { | 2948 if (context_ != Expression::kEffect) { |
1743 ApplyTOS(context_); | 2949 ApplyTOS(context_); |
1744 } | 2950 } |
1745 } else { | 2951 } else { |
| 2952 // Perform the assignment as if via '='. |
1746 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | 2953 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), |
| 2954 Token::ASSIGN, |
1747 context_); | 2955 context_); |
1748 } | 2956 } |
1749 break; | 2957 break; |
1750 case NAMED_PROPERTY: { | 2958 case NAMED_PROPERTY: { |
1751 __ Move(rcx, prop->key()->AsLiteral()->handle()); | 2959 __ Move(rcx, prop->key()->AsLiteral()->handle()); |
1752 __ pop(rdx); | 2960 __ pop(rdx); |
1753 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 2961 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
1754 __ call(ic, RelocInfo::CODE_TARGET); | 2962 __ call(ic, RelocInfo::CODE_TARGET); |
1755 // This nop signals to the IC that there is no inlined code at the call | 2963 // This nop signals to the IC that there is no inlined code at the call |
1756 // site for it to patch. | 2964 // site for it to patch. |
1757 __ nop(); | 2965 __ nop(); |
1758 if (expr->is_postfix()) { | 2966 if (expr->is_postfix()) { |
1759 if (context_ != Expression::kEffect) { | 2967 if (context_ != Expression::kEffect) { |
1760 ApplyTOS(context_); | 2968 ApplyTOS(context_); |
1761 } | 2969 } |
1762 } else { | 2970 } else { |
1763 Apply(context_, rax); | 2971 Apply(context_, rax); |
1764 } | 2972 } |
1765 break; | 2973 break; |
1766 } | 2974 } |
1767 case KEYED_PROPERTY: { | 2975 case KEYED_PROPERTY: { |
| 2976 __ pop(rcx); |
| 2977 __ pop(rdx); |
1768 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 2978 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
1769 __ call(ic, RelocInfo::CODE_TARGET); | 2979 __ call(ic, RelocInfo::CODE_TARGET); |
1770 // This nop signals to the IC that there is no inlined code at the call | 2980 // This nop signals to the IC that there is no inlined code at the call |
1771 // site for it to patch. | 2981 // site for it to patch. |
1772 __ nop(); | 2982 __ nop(); |
1773 if (expr->is_postfix()) { | 2983 if (expr->is_postfix()) { |
1774 __ Drop(2); // Result is on the stack under the key and the receiver. | |
1775 if (context_ != Expression::kEffect) { | 2984 if (context_ != Expression::kEffect) { |
1776 ApplyTOS(context_); | 2985 ApplyTOS(context_); |
1777 } | 2986 } |
1778 } else { | 2987 } else { |
1779 DropAndApply(2, context_, rax); | 2988 Apply(context_, rax); |
1780 } | 2989 } |
1781 break; | 2990 break; |
1782 } | 2991 } |
1783 } | 2992 } |
1784 } | 2993 } |
1785 | 2994 |
1786 void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { | 2995 void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { |
1787 Comment cmnt(masm_, "[ BinaryOperation"); | 2996 Comment cmnt(masm_, "[ BinaryOperation"); |
1788 switch (expr->op()) { | 2997 switch (expr->op()) { |
1789 case Token::COMMA: | 2998 case Token::COMMA: |
(...skipping 21 matching lines...) Expand all Loading... |
1811 VisitForValue(expr->right(), kAccumulator); | 3020 VisitForValue(expr->right(), kAccumulator); |
1812 EmitBinaryOp(expr->op(), context_); | 3021 EmitBinaryOp(expr->op(), context_); |
1813 break; | 3022 break; |
1814 | 3023 |
1815 default: | 3024 default: |
1816 UNREACHABLE(); | 3025 UNREACHABLE(); |
1817 } | 3026 } |
1818 } | 3027 } |
1819 | 3028 |
1820 | 3029 |
| 3030 void FullCodeGenerator::EmitNullCompare(bool strict, |
| 3031 Register obj, |
| 3032 Register null_const, |
| 3033 Label* if_true, |
| 3034 Label* if_false, |
| 3035 Register scratch) { |
| 3036 __ cmpq(obj, null_const); |
| 3037 if (strict) { |
| 3038 __ j(equal, if_true); |
| 3039 } else { |
| 3040 __ j(equal, if_true); |
| 3041 __ CompareRoot(obj, Heap::kUndefinedValueRootIndex); |
| 3042 __ j(equal, if_true); |
| 3043 __ JumpIfSmi(obj, if_false); |
| 3044 // It can be an undetectable object. |
| 3045 __ movq(scratch, FieldOperand(obj, HeapObject::kMapOffset)); |
| 3046 __ testb(FieldOperand(scratch, Map::kBitFieldOffset), |
| 3047 Immediate(1 << Map::kIsUndetectable)); |
| 3048 __ j(not_zero, if_true); |
| 3049 } |
| 3050 __ jmp(if_false); |
| 3051 } |
| 3052 |
| 3053 |
1821 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { | 3054 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { |
1822 Comment cmnt(masm_, "[ CompareOperation"); | 3055 Comment cmnt(masm_, "[ CompareOperation"); |
1823 | 3056 |
1824 // Always perform the comparison for its control flow. Pack the result | 3057 // Always perform the comparison for its control flow. Pack the result |
1825 // into the expression's context after the comparison is performed. | 3058 // into the expression's context after the comparison is performed. |
1826 Label materialize_true, materialize_false, done; | 3059 Label materialize_true, materialize_false; |
1827 // Initially assume we are in a test context. | 3060 Label* if_true = NULL; |
1828 Label* if_true = true_label_; | 3061 Label* if_false = NULL; |
1829 Label* if_false = false_label_; | 3062 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
1830 switch (context_) { | |
1831 case Expression::kUninitialized: | |
1832 UNREACHABLE(); | |
1833 break; | |
1834 case Expression::kEffect: | |
1835 if_true = &done; | |
1836 if_false = &done; | |
1837 break; | |
1838 case Expression::kValue: | |
1839 if_true = &materialize_true; | |
1840 if_false = &materialize_false; | |
1841 break; | |
1842 case Expression::kTest: | |
1843 break; | |
1844 case Expression::kValueTest: | |
1845 if_true = &materialize_true; | |
1846 break; | |
1847 case Expression::kTestValue: | |
1848 if_false = &materialize_false; | |
1849 break; | |
1850 } | |
1851 | 3063 |
1852 VisitForValue(expr->left(), kStack); | 3064 VisitForValue(expr->left(), kStack); |
1853 switch (expr->op()) { | 3065 switch (expr->op()) { |
1854 case Token::IN: | 3066 case Token::IN: |
1855 VisitForValue(expr->right(), kStack); | 3067 VisitForValue(expr->right(), kStack); |
1856 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); | 3068 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); |
1857 __ CompareRoot(rax, Heap::kTrueValueRootIndex); | 3069 __ CompareRoot(rax, Heap::kTrueValueRootIndex); |
1858 __ j(equal, if_true); | 3070 __ j(equal, if_true); |
1859 __ jmp(if_false); | 3071 __ jmp(if_false); |
1860 break; | 3072 break; |
1861 | 3073 |
1862 case Token::INSTANCEOF: { | 3074 case Token::INSTANCEOF: { |
1863 VisitForValue(expr->right(), kStack); | 3075 VisitForValue(expr->right(), kStack); |
1864 InstanceofStub stub; | 3076 InstanceofStub stub; |
1865 __ CallStub(&stub); | 3077 __ CallStub(&stub); |
1866 __ testq(rax, rax); | 3078 __ testq(rax, rax); |
1867 __ j(zero, if_true); // The stub returns 0 for true. | 3079 __ j(zero, if_true); // The stub returns 0 for true. |
1868 __ jmp(if_false); | 3080 __ jmp(if_false); |
1869 break; | 3081 break; |
1870 } | 3082 } |
1871 | 3083 |
1872 default: { | 3084 default: { |
1873 VisitForValue(expr->right(), kAccumulator); | 3085 VisitForValue(expr->right(), kAccumulator); |
1874 Condition cc = no_condition; | 3086 Condition cc = no_condition; |
1875 bool strict = false; | 3087 bool strict = false; |
1876 switch (expr->op()) { | 3088 switch (expr->op()) { |
1877 case Token::EQ_STRICT: | 3089 case Token::EQ_STRICT: |
1878 strict = true; | 3090 strict = true; |
1879 // Fall through. | 3091 // Fall through. |
1880 case Token::EQ: | 3092 case Token::EQ: { |
1881 cc = equal; | 3093 cc = equal; |
1882 __ pop(rdx); | 3094 __ pop(rdx); |
| 3095 // If either operand is constant null we do a fast compare |
| 3096 // against null. |
| 3097 Literal* right_literal = expr->right()->AsLiteral(); |
| 3098 Literal* left_literal = expr->left()->AsLiteral(); |
| 3099 if (right_literal != NULL && right_literal->handle()->IsNull()) { |
| 3100 EmitNullCompare(strict, rdx, rax, if_true, if_false, rcx); |
| 3101 Apply(context_, if_true, if_false); |
| 3102 return; |
| 3103 } else if (left_literal != NULL && left_literal->handle()->IsNull()) { |
| 3104 EmitNullCompare(strict, rax, rdx, if_true, if_false, rcx); |
| 3105 Apply(context_, if_true, if_false); |
| 3106 return; |
| 3107 } |
1883 break; | 3108 break; |
| 3109 } |
1884 case Token::LT: | 3110 case Token::LT: |
1885 cc = less; | 3111 cc = less; |
1886 __ pop(rdx); | 3112 __ pop(rdx); |
1887 break; | 3113 break; |
1888 case Token::GT: | 3114 case Token::GT: |
1889 // Reverse left and right sizes to obtain ECMA-262 conversion order. | 3115 // Reverse left and right sizes to obtain ECMA-262 conversion order. |
1890 cc = less; | 3116 cc = less; |
1891 __ movq(rdx, result_register()); | 3117 __ movq(rdx, result_register()); |
1892 __ pop(rax); | 3118 __ pop(rax); |
1893 break; | 3119 break; |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1984 __ movq(Operand(rsp, 0), rdx); | 3210 __ movq(Operand(rsp, 0), rdx); |
1985 // And return. | 3211 // And return. |
1986 __ ret(0); | 3212 __ ret(0); |
1987 } | 3213 } |
1988 | 3214 |
1989 | 3215 |
1990 #undef __ | 3216 #undef __ |
1991 | 3217 |
1992 | 3218 |
1993 } } // namespace v8::internal | 3219 } } // namespace v8::internal |
| 3220 |
| 3221 #endif // V8_TARGET_ARCH_X64 |
OLD | NEW |