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 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); | 74 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); |
75 for (int i = 0; i < locals_count; i++) { | 75 for (int i = 0; i < locals_count; i++) { |
76 __ push(rdx); | 76 __ push(rdx); |
77 } | 77 } |
78 } | 78 } |
79 } | 79 } |
80 | 80 |
81 bool function_in_register = true; | 81 bool function_in_register = true; |
82 | 82 |
83 // Possibly allocate a local context. | 83 // Possibly allocate a local context. |
84 if (scope()->num_heap_slots() > 0) { | 84 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; |
| 85 if (heap_slots > 0) { |
85 Comment cmnt(masm_, "[ Allocate local context"); | 86 Comment cmnt(masm_, "[ Allocate local context"); |
86 // Argument to NewContext is the function, which is still in rdi. | 87 // Argument to NewContext is the function, which is still in rdi. |
87 __ push(rdi); | 88 __ push(rdi); |
88 __ 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 } |
89 function_in_register = false; | 95 function_in_register = false; |
90 // 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 |
91 // 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. |
92 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); | 98 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); |
93 | 99 |
94 // Copy any necessary parameters into the context. | 100 // Copy any necessary parameters into the context. |
95 int num_parameters = scope()->num_parameters(); | 101 int num_parameters = scope()->num_parameters(); |
96 for (int i = 0; i < num_parameters; i++) { | 102 for (int i = 0; i < num_parameters; i++) { |
97 Slot* slot = scope()->parameter(i)->slot(); | 103 Slot* slot = scope()->parameter(i)->slot(); |
98 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... |
138 // Store new arguments object in both "arguments" and ".arguments" slots. | 144 // Store new arguments object in both "arguments" and ".arguments" slots. |
139 __ movq(rcx, rax); | 145 __ movq(rcx, rax); |
140 Move(arguments->slot(), rax, rbx, rdx); | 146 Move(arguments->slot(), rax, rbx, rdx); |
141 Slot* dot_arguments_slot = | 147 Slot* dot_arguments_slot = |
142 scope()->arguments_shadow()->AsVariable()->slot(); | 148 scope()->arguments_shadow()->AsVariable()->slot(); |
143 Move(dot_arguments_slot, rcx, rbx, rdx); | 149 Move(dot_arguments_slot, rcx, rbx, rdx); |
144 } | 150 } |
145 } | 151 } |
146 | 152 |
147 { Comment cmnt(masm_, "[ Declarations"); | 153 { Comment cmnt(masm_, "[ Declarations"); |
148 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 } |
149 } | 166 } |
150 | 167 |
151 { Comment cmnt(masm_, "[ Stack check"); | 168 { Comment cmnt(masm_, "[ Stack check"); |
152 Label ok; | 169 Label ok; |
153 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); | 170 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); |
154 __ j(above_equal, &ok); | 171 __ j(above_equal, &ok); |
155 StackCheckStub stub; | 172 StackCheckStub stub; |
156 __ CallStub(&stub); | 173 __ CallStub(&stub); |
157 __ bind(&ok); | 174 __ bind(&ok); |
158 } | 175 } |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 __ movq(result_register(), reg); | 439 __ movq(result_register(), reg); |
423 __ movq(Operand(rsp, 0), result_register()); | 440 __ movq(Operand(rsp, 0), result_register()); |
424 break; | 441 break; |
425 } | 442 } |
426 DoTest(context); | 443 DoTest(context); |
427 break; | 444 break; |
428 } | 445 } |
429 } | 446 } |
430 | 447 |
431 | 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 |
432 void FullCodeGenerator::Apply(Expression::Context context, | 482 void FullCodeGenerator::Apply(Expression::Context context, |
433 Label* materialize_true, | 483 Label* materialize_true, |
434 Label* materialize_false) { | 484 Label* materialize_false) { |
435 switch (context) { | 485 switch (context) { |
436 case Expression::kUninitialized: | 486 case Expression::kUninitialized: |
437 | 487 |
438 case Expression::kEffect: | 488 case Expression::kEffect: |
439 ASSERT_EQ(materialize_true, materialize_false); | 489 ASSERT_EQ(materialize_true, materialize_false); |
440 __ bind(materialize_true); | 490 __ bind(materialize_true); |
441 break; | 491 break; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
487 case kStack: | 537 case kStack: |
488 __ Push(Factory::false_value()); | 538 __ Push(Factory::false_value()); |
489 break; | 539 break; |
490 } | 540 } |
491 __ jmp(false_label_); | 541 __ jmp(false_label_); |
492 break; | 542 break; |
493 } | 543 } |
494 } | 544 } |
495 | 545 |
496 | 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 |
497 void FullCodeGenerator::DoTest(Expression::Context context) { | 602 void FullCodeGenerator::DoTest(Expression::Context context) { |
498 // 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 |
499 // 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 |
500 // desired), then the value is already duplicated on the stack. | 605 // desired), then the value is already duplicated on the stack. |
501 ASSERT_NE(NULL, true_label_); | 606 ASSERT_NE(NULL, true_label_); |
502 ASSERT_NE(NULL, false_label_); | 607 ASSERT_NE(NULL, false_label_); |
503 | 608 |
504 // 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 |
505 // 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 |
506 // 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... |
662 MemOperand location = EmitSlotSearch(dst, scratch1); | 767 MemOperand location = EmitSlotSearch(dst, scratch1); |
663 __ movq(location, src); | 768 __ movq(location, src); |
664 // 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. |
665 if (dst->type() == Slot::CONTEXT) { | 770 if (dst->type() == Slot::CONTEXT) { |
666 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; | 771 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; |
667 __ RecordWrite(scratch1, offset, src, scratch2); | 772 __ RecordWrite(scratch1, offset, src, scratch2); |
668 } | 773 } |
669 } | 774 } |
670 | 775 |
671 | 776 |
672 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { | 777 void FullCodeGenerator::EmitDeclaration(Variable* variable, |
| 778 Variable::Mode mode, |
| 779 FunctionLiteral* function) { |
673 Comment cmnt(masm_, "[ Declaration"); | 780 Comment cmnt(masm_, "[ Declaration"); |
674 Variable* var = decl->proxy()->var(); | 781 ASSERT(variable != NULL); // Must have been resolved. |
675 ASSERT(var != NULL); // Must have been resolved. | 782 Slot* slot = variable->slot(); |
676 Slot* slot = var->slot(); | 783 Property* prop = variable->AsProperty(); |
677 Property* prop = var->AsProperty(); | |
678 | 784 |
679 if (slot != NULL) { | 785 if (slot != NULL) { |
680 switch (slot->type()) { | 786 switch (slot->type()) { |
681 case Slot::PARAMETER: | 787 case Slot::PARAMETER: |
682 case Slot::LOCAL: | 788 case Slot::LOCAL: |
683 if (decl->mode() == Variable::CONST) { | 789 if (mode == Variable::CONST) { |
684 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | 790 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); |
685 __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); | 791 __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); |
686 } else if (decl->fun() != NULL) { | 792 } else if (function != NULL) { |
687 VisitForValue(decl->fun(), kAccumulator); | 793 VisitForValue(function, kAccumulator); |
688 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); | 794 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); |
689 } | 795 } |
690 break; | 796 break; |
691 | 797 |
692 case Slot::CONTEXT: | 798 case Slot::CONTEXT: |
693 // We bypass the general EmitSlotSearch because we know more about | 799 // We bypass the general EmitSlotSearch because we know more about |
694 // this specific context. | 800 // this specific context. |
695 | 801 |
696 // The variable in the decl always resides in the current context. | 802 // The variable in the decl always resides in the current context. |
697 ASSERT_EQ(0, scope()->ContextChainLength(var->scope())); | 803 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); |
698 if (FLAG_debug_code) { | 804 if (FLAG_debug_code) { |
699 // Check if we have the correct context pointer. | 805 // Check if we have the correct context pointer. |
700 __ movq(rbx, | 806 __ movq(rbx, |
701 CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); | 807 CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); |
702 __ cmpq(rbx, rsi); | 808 __ cmpq(rbx, rsi); |
703 __ Check(equal, "Unexpected declaration in current context."); | 809 __ Check(equal, "Unexpected declaration in current context."); |
704 } | 810 } |
705 if (decl->mode() == Variable::CONST) { | 811 if (mode == Variable::CONST) { |
706 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | 812 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); |
707 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | 813 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
708 kScratchRegister); | 814 kScratchRegister); |
709 // No write barrier since the hole value is in old space. | 815 // No write barrier since the hole value is in old space. |
710 } else if (decl->fun() != NULL) { | 816 } else if (function != NULL) { |
711 VisitForValue(decl->fun(), kAccumulator); | 817 VisitForValue(function, kAccumulator); |
712 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | 818 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
713 result_register()); | 819 result_register()); |
714 int offset = Context::SlotOffset(slot->index()); | 820 int offset = Context::SlotOffset(slot->index()); |
715 __ movq(rbx, rsi); | 821 __ movq(rbx, rsi); |
716 __ RecordWrite(rbx, offset, result_register(), rcx); | 822 __ RecordWrite(rbx, offset, result_register(), rcx); |
717 } | 823 } |
718 break; | 824 break; |
719 | 825 |
720 case Slot::LOOKUP: { | 826 case Slot::LOOKUP: { |
721 __ push(rsi); | 827 __ push(rsi); |
722 __ Push(var->name()); | 828 __ Push(variable->name()); |
723 // Declaration nodes are always introduced in one of two modes. | 829 // Declaration nodes are always introduced in one of two modes. |
724 ASSERT(decl->mode() == Variable::VAR || | 830 ASSERT(mode == Variable::VAR || mode == Variable::CONST); |
725 decl->mode() == Variable::CONST); | 831 PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY; |
726 PropertyAttributes attr = | |
727 (decl->mode() == Variable::VAR) ? NONE : READ_ONLY; | |
728 __ Push(Smi::FromInt(attr)); | 832 __ Push(Smi::FromInt(attr)); |
729 // Push initial value, if any. | 833 // Push initial value, if any. |
730 // 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 |
731 // 'undefined') because we may have a (legal) redeclaration and we | 835 // 'undefined') because we may have a (legal) redeclaration and we |
732 // must not destroy the current value. | 836 // must not destroy the current value. |
733 if (decl->mode() == Variable::CONST) { | 837 if (mode == Variable::CONST) { |
734 __ PushRoot(Heap::kTheHoleValueRootIndex); | 838 __ PushRoot(Heap::kTheHoleValueRootIndex); |
735 } else if (decl->fun() != NULL) { | 839 } else if (function != NULL) { |
736 VisitForValue(decl->fun(), kStack); | 840 VisitForValue(function, kStack); |
737 } else { | 841 } else { |
738 __ Push(Smi::FromInt(0)); // no initial value! | 842 __ Push(Smi::FromInt(0)); // no initial value! |
739 } | 843 } |
740 __ CallRuntime(Runtime::kDeclareContextSlot, 4); | 844 __ CallRuntime(Runtime::kDeclareContextSlot, 4); |
741 break; | 845 break; |
742 } | 846 } |
743 } | 847 } |
744 | 848 |
745 } else if (prop != NULL) { | 849 } else if (prop != NULL) { |
746 if (decl->fun() != NULL || decl->mode() == Variable::CONST) { | 850 if (function != NULL || mode == Variable::CONST) { |
747 // We are declaring a function or constant that rewrites to a | 851 // We are declaring a function or constant that rewrites to a |
748 // property. Use (keyed) IC to set the initial value. | 852 // property. Use (keyed) IC to set the initial value. |
749 VisitForValue(prop->obj(), kStack); | 853 VisitForValue(prop->obj(), kStack); |
750 VisitForValue(prop->key(), kStack); | 854 VisitForValue(prop->key(), kStack); |
751 | 855 |
752 if (decl->fun() != NULL) { | 856 if (function != NULL) { |
753 VisitForValue(decl->fun(), kAccumulator); | 857 VisitForValue(function, kAccumulator); |
754 } else { | 858 } else { |
755 __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); | 859 __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); |
756 } | 860 } |
757 | 861 |
758 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 862 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
759 __ call(ic, RelocInfo::CODE_TARGET); | 863 __ call(ic, RelocInfo::CODE_TARGET); |
760 // Absence of a test rax instruction following the call | 864 // Absence of a test rax instruction following the call |
761 // indicates that none of the load was inlined. | 865 // indicates that none of the load was inlined. |
762 __ nop(); | 866 __ nop(); |
763 | 867 |
764 // Value in rax is ignored (declarations are statements). Receiver | 868 // Value in rax is ignored (declarations are statements). Receiver |
765 // and key on stack are discarded. | 869 // and key on stack are discarded. |
766 __ Drop(2); | 870 __ Drop(2); |
767 } | 871 } |
768 } | 872 } |
769 } | 873 } |
770 | 874 |
771 | 875 |
| 876 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { |
| 877 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); |
| 878 } |
| 879 |
| 880 |
772 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { | 881 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { |
773 // Call the runtime to declare the globals. | 882 // Call the runtime to declare the globals. |
774 __ push(rsi); // The context is the first argument. | 883 __ push(rsi); // The context is the first argument. |
775 __ Push(pairs); | 884 __ Push(pairs); |
776 __ Push(Smi::FromInt(is_eval() ? 1 : 0)); | 885 __ Push(Smi::FromInt(is_eval() ? 1 : 0)); |
777 __ CallRuntime(Runtime::kDeclareGlobals, 3); | 886 __ CallRuntime(Runtime::kDeclareGlobals, 3); |
778 // Return value is ignored. | 887 // Return value is ignored. |
779 } | 888 } |
780 | 889 |
781 | 890 |
782 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { | 891 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { |
783 UNREACHABLE(); | 892 Comment cmnt(masm_, "[ SwitchStatement"); |
| 893 Breakable nested_statement(this, stmt); |
| 894 SetStatementPosition(stmt); |
| 895 // Keep the switch value on the stack until a case matches. |
| 896 VisitForValue(stmt->tag(), kStack); |
| 897 |
| 898 ZoneList<CaseClause*>* clauses = stmt->cases(); |
| 899 CaseClause* default_clause = NULL; // Can occur anywhere in the list. |
| 900 |
| 901 Label next_test; // Recycled for each test. |
| 902 // Compile all the tests with branches to their bodies. |
| 903 for (int i = 0; i < clauses->length(); i++) { |
| 904 CaseClause* clause = clauses->at(i); |
| 905 // The default is not a test, but remember it as final fall through. |
| 906 if (clause->is_default()) { |
| 907 default_clause = clause; |
| 908 continue; |
| 909 } |
| 910 |
| 911 Comment cmnt(masm_, "[ Case comparison"); |
| 912 __ bind(&next_test); |
| 913 next_test.Unuse(); |
| 914 |
| 915 // Compile the label expression. |
| 916 VisitForValue(clause->label(), kAccumulator); |
| 917 |
| 918 // Perform the comparison as if via '==='. The comparison stub expects |
| 919 // the smi vs. smi case to be handled before it is called. |
| 920 Label slow_case; |
| 921 __ movq(rdx, Operand(rsp, 0)); // Switch value. |
| 922 __ JumpIfNotBothSmi(rdx, rax, &slow_case); |
| 923 __ SmiCompare(rdx, rax); |
| 924 __ j(not_equal, &next_test); |
| 925 __ Drop(1); // Switch value is no longer needed. |
| 926 __ jmp(clause->body_target()->entry_label()); |
| 927 |
| 928 __ bind(&slow_case); |
| 929 CompareStub stub(equal, true); |
| 930 __ CallStub(&stub); |
| 931 __ testq(rax, rax); |
| 932 __ j(not_equal, &next_test); |
| 933 __ Drop(1); // Switch value is no longer needed. |
| 934 __ jmp(clause->body_target()->entry_label()); |
| 935 } |
| 936 |
| 937 // Discard the test value and jump to the default if present, otherwise to |
| 938 // the end of the statement. |
| 939 __ bind(&next_test); |
| 940 __ Drop(1); // Switch value is no longer needed. |
| 941 if (default_clause == NULL) { |
| 942 __ jmp(nested_statement.break_target()); |
| 943 } else { |
| 944 __ jmp(default_clause->body_target()->entry_label()); |
| 945 } |
| 946 |
| 947 // Compile all the case bodies. |
| 948 for (int i = 0; i < clauses->length(); i++) { |
| 949 Comment cmnt(masm_, "[ Case body"); |
| 950 CaseClause* clause = clauses->at(i); |
| 951 __ bind(clause->body_target()->entry_label()); |
| 952 VisitStatements(clause->statements()); |
| 953 } |
| 954 |
| 955 __ bind(nested_statement.break_target()); |
784 } | 956 } |
785 | 957 |
786 | 958 |
787 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { | 959 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { |
788 UNREACHABLE(); | 960 Comment cmnt(masm_, "[ ForInStatement"); |
| 961 SetStatementPosition(stmt); |
| 962 |
| 963 Label loop, exit; |
| 964 ForIn loop_statement(this, stmt); |
| 965 increment_loop_depth(); |
| 966 |
| 967 // Get the object to enumerate over. Both SpiderMonkey and JSC |
| 968 // ignore null and undefined in contrast to the specification; see |
| 969 // ECMA-262 section 12.6.4. |
| 970 VisitForValue(stmt->enumerable(), kAccumulator); |
| 971 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
| 972 __ j(equal, &exit); |
| 973 __ CompareRoot(rax, Heap::kNullValueRootIndex); |
| 974 __ j(equal, &exit); |
| 975 |
| 976 // Convert the object to a JS object. |
| 977 Label convert, done_convert; |
| 978 __ JumpIfSmi(rax, &convert); |
| 979 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx); |
| 980 __ j(above_equal, &done_convert); |
| 981 __ bind(&convert); |
| 982 __ push(rax); |
| 983 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); |
| 984 __ bind(&done_convert); |
| 985 __ push(rax); |
| 986 |
| 987 // TODO(kasperl): Check cache validity in generated code. This is a |
| 988 // fast case for the JSObject::IsSimpleEnum cache validity |
| 989 // checks. If we cannot guarantee cache validity, call the runtime |
| 990 // system to check cache validity or get the property names in a |
| 991 // fixed array. |
| 992 |
| 993 // Get the set of properties to enumerate. |
| 994 __ push(rax); // Duplicate the enumerable object on the stack. |
| 995 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); |
| 996 |
| 997 // If we got a map from the runtime call, we can do a fast |
| 998 // modification check. Otherwise, we got a fixed array, and we have |
| 999 // to do a slow check. |
| 1000 Label fixed_array; |
| 1001 __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), |
| 1002 Heap::kMetaMapRootIndex); |
| 1003 __ j(not_equal, &fixed_array); |
| 1004 |
| 1005 // We got a map in register rax. Get the enumeration cache from it. |
| 1006 __ movq(rcx, FieldOperand(rax, Map::kInstanceDescriptorsOffset)); |
| 1007 __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); |
| 1008 __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 1009 |
| 1010 // Setup the four remaining stack slots. |
| 1011 __ push(rax); // Map. |
| 1012 __ push(rdx); // Enumeration cache. |
| 1013 __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); |
| 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 __ push(rax); // Fixed array length (as smi). |
| 1024 __ Push(Smi::FromInt(0)); // Initial index. |
| 1025 |
| 1026 // Generate code for doing the condition check. |
| 1027 __ bind(&loop); |
| 1028 __ movq(rax, Operand(rsp, 0 * kPointerSize)); // Get the current index. |
| 1029 __ cmpq(rax, Operand(rsp, 1 * kPointerSize)); // Compare to the array length. |
| 1030 __ j(above_equal, loop_statement.break_target()); |
| 1031 |
| 1032 // Get the current entry of the array into register rbx. |
| 1033 __ movq(rbx, Operand(rsp, 2 * kPointerSize)); |
| 1034 SmiIndex index = __ SmiToIndex(rax, rax, kPointerSizeLog2); |
| 1035 __ movq(rbx, FieldOperand(rbx, |
| 1036 index.reg, |
| 1037 index.scale, |
| 1038 FixedArray::kHeaderSize)); |
| 1039 |
| 1040 // Get the expected map from the stack or a zero map in the |
| 1041 // permanent slow case into register rdx. |
| 1042 __ movq(rdx, Operand(rsp, 3 * kPointerSize)); |
| 1043 |
| 1044 // Check if the expected map still matches that of the enumerable. |
| 1045 // If not, we have to filter the key. |
| 1046 Label update_each; |
| 1047 __ movq(rcx, Operand(rsp, 4 * kPointerSize)); |
| 1048 __ cmpq(rdx, FieldOperand(rcx, HeapObject::kMapOffset)); |
| 1049 __ j(equal, &update_each); |
| 1050 |
| 1051 // Convert the entry to a string or null if it isn't a property |
| 1052 // anymore. If the property has been removed while iterating, we |
| 1053 // just skip it. |
| 1054 __ push(rcx); // Enumerable. |
| 1055 __ push(rbx); // Current entry. |
| 1056 __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION); |
| 1057 __ CompareRoot(rax, Heap::kNullValueRootIndex); |
| 1058 __ j(equal, loop_statement.continue_target()); |
| 1059 __ movq(rbx, rax); |
| 1060 |
| 1061 // Update the 'each' property or variable from the possibly filtered |
| 1062 // entry in register rbx. |
| 1063 __ bind(&update_each); |
| 1064 __ movq(result_register(), rbx); |
| 1065 // Perform the assignment as if via '='. |
| 1066 EmitAssignment(stmt->each()); |
| 1067 |
| 1068 // Generate code for the body of the loop. |
| 1069 Label stack_limit_hit, stack_check_done; |
| 1070 Visit(stmt->body()); |
| 1071 |
| 1072 __ StackLimitCheck(&stack_limit_hit); |
| 1073 __ bind(&stack_check_done); |
| 1074 |
| 1075 // Generate code for going to the next element by incrementing the |
| 1076 // index (smi) stored on top of the stack. |
| 1077 __ bind(loop_statement.continue_target()); |
| 1078 __ SmiAddConstant(Operand(rsp, 0 * kPointerSize), Smi::FromInt(1)); |
| 1079 __ jmp(&loop); |
| 1080 |
| 1081 // Slow case for the stack limit check. |
| 1082 StackCheckStub stack_check_stub; |
| 1083 __ bind(&stack_limit_hit); |
| 1084 __ CallStub(&stack_check_stub); |
| 1085 __ jmp(&stack_check_done); |
| 1086 |
| 1087 // Remove the pointers stored on the stack. |
| 1088 __ bind(loop_statement.break_target()); |
| 1089 __ addq(rsp, Immediate(5 * kPointerSize)); |
| 1090 |
| 1091 // Exit and decrement the loop depth. |
| 1092 __ bind(&exit); |
| 1093 decrement_loop_depth(); |
789 } | 1094 } |
790 | 1095 |
791 | 1096 |
792 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { | 1097 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { |
793 // Use the fast case closure allocation code that allocates in new | 1098 // Use the fast case closure allocation code that allocates in new |
794 // space for nested functions that don't need literals cloning. | 1099 // space for nested functions that don't need literals cloning. |
795 if (scope()->is_function_scope() && info->num_literals() == 0) { | 1100 if (scope()->is_function_scope() && info->num_literals() == 0) { |
796 FastNewClosureStub stub; | 1101 FastNewClosureStub stub; |
797 __ Push(info); | 1102 __ Push(info); |
798 __ CallStub(&stub); | 1103 __ CallStub(&stub); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
837 Comment cmnt(masm_, "Lookup slot"); | 1142 Comment cmnt(masm_, "Lookup slot"); |
838 __ push(rsi); // Context. | 1143 __ push(rsi); // Context. |
839 __ Push(var->name()); | 1144 __ Push(var->name()); |
840 __ CallRuntime(Runtime::kLoadContextSlot, 2); | 1145 __ CallRuntime(Runtime::kLoadContextSlot, 2); |
841 Apply(context, rax); | 1146 Apply(context, rax); |
842 | 1147 |
843 } else if (slot != NULL) { | 1148 } else if (slot != NULL) { |
844 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) | 1149 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) |
845 ? "Context slot" | 1150 ? "Context slot" |
846 : "Stack slot"); | 1151 : "Stack slot"); |
847 Apply(context, slot); | 1152 if (var->mode() == Variable::CONST) { |
| 1153 // Constants may be the hole value if they have not been initialized. |
| 1154 // Unhole them. |
| 1155 Label done; |
| 1156 MemOperand slot_operand = EmitSlotSearch(slot, rax); |
| 1157 __ movq(rax, slot_operand); |
| 1158 __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); |
| 1159 __ j(not_equal, &done); |
| 1160 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 1161 __ bind(&done); |
| 1162 Apply(context, rax); |
| 1163 } else { |
| 1164 Apply(context, slot); |
| 1165 } |
848 | 1166 |
849 } else { | 1167 } else { |
850 Comment cmnt(masm_, "Rewritten parameter"); | 1168 Comment cmnt(masm_, "Rewritten parameter"); |
851 ASSERT_NOT_NULL(property); | 1169 ASSERT_NOT_NULL(property); |
852 // Rewritten parameter accesses are of the form "slot[literal]". | 1170 // Rewritten parameter accesses are of the form "slot[literal]". |
853 | 1171 |
854 // Assert that the object is in a slot. | 1172 // Assert that the object is in a slot. |
855 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); | 1173 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); |
856 ASSERT_NOT_NULL(object_var); | 1174 ASSERT_NOT_NULL(object_var); |
857 Slot* object_slot = object_var->slot(); | 1175 Slot* object_slot = object_var->slot(); |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
973 if (result_saved) { | 1291 if (result_saved) { |
974 ApplyTOS(context_); | 1292 ApplyTOS(context_); |
975 } else { | 1293 } else { |
976 Apply(context_, rax); | 1294 Apply(context_, rax); |
977 } | 1295 } |
978 } | 1296 } |
979 | 1297 |
980 | 1298 |
981 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { | 1299 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { |
982 Comment cmnt(masm_, "[ ArrayLiteral"); | 1300 Comment cmnt(masm_, "[ ArrayLiteral"); |
| 1301 |
| 1302 ZoneList<Expression*>* subexprs = expr->values(); |
| 1303 int length = subexprs->length(); |
| 1304 |
983 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | 1305 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); |
984 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); | 1306 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); |
985 __ Push(Smi::FromInt(expr->literal_index())); | 1307 __ Push(Smi::FromInt(expr->literal_index())); |
986 __ Push(expr->constant_elements()); | 1308 __ Push(expr->constant_elements()); |
987 if (expr->depth() > 1) { | 1309 if (expr->depth() > 1) { |
988 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); | 1310 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); |
| 1311 } else if (length > FastCloneShallowArrayStub::kMaximumLength) { |
| 1312 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); |
989 } else { | 1313 } else { |
990 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); | 1314 FastCloneShallowArrayStub stub(length); |
| 1315 __ CallStub(&stub); |
991 } | 1316 } |
992 | 1317 |
993 bool result_saved = false; // Is the result saved to the stack? | 1318 bool result_saved = false; // Is the result saved to the stack? |
994 | 1319 |
995 // Emit code to evaluate all the non-constant subexpressions and to store | 1320 // Emit code to evaluate all the non-constant subexpressions and to store |
996 // them into the newly cloned array. | 1321 // them into the newly cloned array. |
997 ZoneList<Expression*>* subexprs = expr->values(); | 1322 for (int i = 0; i < length; i++) { |
998 for (int i = 0, len = subexprs->length(); i < len; i++) { | |
999 Expression* subexpr = subexprs->at(i); | 1323 Expression* subexpr = subexprs->at(i); |
1000 // If the subexpression is a literal or a simple materialized literal it | 1324 // If the subexpression is a literal or a simple materialized literal it |
1001 // is already set in the cloned array. | 1325 // is already set in the cloned array. |
1002 if (subexpr->AsLiteral() != NULL || | 1326 if (subexpr->AsLiteral() != NULL || |
1003 CompileTimeValue::IsCompileTimeValue(subexpr)) { | 1327 CompileTimeValue::IsCompileTimeValue(subexpr)) { |
1004 continue; | 1328 continue; |
1005 } | 1329 } |
1006 | 1330 |
1007 if (!result_saved) { | 1331 if (!result_saved) { |
1008 __ push(rax); | 1332 __ push(rax); |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1150 Expression::Context context) { | 1474 Expression::Context context) { |
1151 __ push(result_register()); | 1475 __ push(result_register()); |
1152 GenericBinaryOpStub stub(op, | 1476 GenericBinaryOpStub stub(op, |
1153 NO_OVERWRITE, | 1477 NO_OVERWRITE, |
1154 NO_GENERIC_BINARY_FLAGS); | 1478 NO_GENERIC_BINARY_FLAGS); |
1155 __ CallStub(&stub); | 1479 __ CallStub(&stub); |
1156 Apply(context, rax); | 1480 Apply(context, rax); |
1157 } | 1481 } |
1158 | 1482 |
1159 | 1483 |
| 1484 void FullCodeGenerator::EmitAssignment(Expression* expr) { |
| 1485 // Invalid left-hand sides are rewritten to have a 'throw |
| 1486 // ReferenceError' on the left-hand side. |
| 1487 if (!expr->IsValidLeftHandSide()) { |
| 1488 VisitForEffect(expr); |
| 1489 return; |
| 1490 } |
| 1491 |
| 1492 // Left-hand side can only be a property, a global or a (parameter or local) |
| 1493 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
| 1494 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
| 1495 LhsKind assign_type = VARIABLE; |
| 1496 Property* prop = expr->AsProperty(); |
| 1497 if (prop != NULL) { |
| 1498 assign_type = (prop->key()->IsPropertyName()) |
| 1499 ? NAMED_PROPERTY |
| 1500 : KEYED_PROPERTY; |
| 1501 } |
| 1502 |
| 1503 switch (assign_type) { |
| 1504 case VARIABLE: { |
| 1505 Variable* var = expr->AsVariableProxy()->var(); |
| 1506 EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect); |
| 1507 break; |
| 1508 } |
| 1509 case NAMED_PROPERTY: { |
| 1510 __ push(rax); // Preserve value. |
| 1511 VisitForValue(prop->obj(), kAccumulator); |
| 1512 __ movq(rdx, rax); |
| 1513 __ pop(rax); // Restore value. |
| 1514 __ Move(rcx, prop->key()->AsLiteral()->handle()); |
| 1515 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
| 1516 __ call(ic, RelocInfo::CODE_TARGET); |
| 1517 __ nop(); // Signal no inlined code. |
| 1518 break; |
| 1519 } |
| 1520 case KEYED_PROPERTY: { |
| 1521 __ push(rax); // Preserve value. |
| 1522 VisitForValue(prop->obj(), kStack); |
| 1523 VisitForValue(prop->key(), kStack); |
| 1524 __ movq(rax, Operand(rsp, 2 * kPointerSize)); |
| 1525 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
| 1526 __ call(ic, RelocInfo::CODE_TARGET); |
| 1527 __ nop(); // Signal no inlined code. |
| 1528 __ Drop(3); // Receiver, key, and extra copy of value. |
| 1529 break; |
| 1530 } |
| 1531 } |
| 1532 } |
| 1533 |
| 1534 |
1160 void FullCodeGenerator::EmitVariableAssignment(Variable* var, | 1535 void FullCodeGenerator::EmitVariableAssignment(Variable* var, |
1161 Token::Value op, | 1536 Token::Value op, |
1162 Expression::Context context) { | 1537 Expression::Context context) { |
1163 // Left-hand sides that rewrite to explicit property accesses do not reach | 1538 // Left-hand sides that rewrite to explicit property accesses do not reach |
1164 // here. | 1539 // here. |
1165 ASSERT(var != NULL); | 1540 ASSERT(var != NULL); |
1166 ASSERT(var->is_global() || var->slot() != NULL); | 1541 ASSERT(var->is_global() || var->slot() != NULL); |
1167 | 1542 |
1168 if (var->is_global()) { | 1543 if (var->is_global()) { |
1169 ASSERT(!var->is_this()); | 1544 ASSERT(!var->is_this()); |
(...skipping 10 matching lines...) Expand all Loading... |
1180 // Perform the assignment for non-const variables and for initialization | 1555 // Perform the assignment for non-const variables and for initialization |
1181 // of const variables. Const assignments are simply skipped. | 1556 // of const variables. Const assignments are simply skipped. |
1182 Label done; | 1557 Label done; |
1183 Slot* slot = var->slot(); | 1558 Slot* slot = var->slot(); |
1184 switch (slot->type()) { | 1559 switch (slot->type()) { |
1185 case Slot::PARAMETER: | 1560 case Slot::PARAMETER: |
1186 case Slot::LOCAL: | 1561 case Slot::LOCAL: |
1187 if (op == Token::INIT_CONST) { | 1562 if (op == Token::INIT_CONST) { |
1188 // Detect const reinitialization by checking for the hole value. | 1563 // Detect const reinitialization by checking for the hole value. |
1189 __ movq(rdx, Operand(rbp, SlotOffset(slot))); | 1564 __ movq(rdx, Operand(rbp, SlotOffset(slot))); |
1190 __ Cmp(rdx, Factory::the_hole_value()); | 1565 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); |
1191 __ j(not_equal, &done); | 1566 __ j(not_equal, &done); |
1192 } | 1567 } |
1193 // Perform the assignment. | 1568 // Perform the assignment. |
1194 __ movq(Operand(rbp, SlotOffset(slot)), rax); | 1569 __ movq(Operand(rbp, SlotOffset(slot)), rax); |
1195 break; | 1570 break; |
1196 | 1571 |
1197 case Slot::CONTEXT: { | 1572 case Slot::CONTEXT: { |
1198 MemOperand target = EmitSlotSearch(slot, rcx); | 1573 MemOperand target = EmitSlotSearch(slot, rcx); |
1199 if (op == Token::INIT_CONST) { | 1574 if (op == Token::INIT_CONST) { |
1200 // Detect const reinitialization by checking for the hole value. | 1575 // Detect const reinitialization by checking for the hole value. |
1201 __ movq(rdx, target); | 1576 __ movq(rdx, target); |
1202 __ Cmp(rdx, Factory::the_hole_value()); | 1577 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); |
1203 __ j(not_equal, &done); | 1578 __ j(not_equal, &done); |
1204 } | 1579 } |
1205 // Perform the assignment and issue the write barrier. | 1580 // Perform the assignment and issue the write barrier. |
1206 __ movq(target, rax); | 1581 __ movq(target, rax); |
1207 // The value of the assignment is in rax. RecordWrite clobbers its | 1582 // The value of the assignment is in rax. RecordWrite clobbers its |
1208 // register arguments. | 1583 // register arguments. |
1209 __ movq(rdx, rax); | 1584 __ movq(rdx, rax); |
1210 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | 1585 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; |
1211 __ RecordWrite(rcx, offset, rdx, rbx); | 1586 __ RecordWrite(rcx, offset, rdx, rbx); |
1212 break; | 1587 break; |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1355 | 1730 |
1356 void FullCodeGenerator::EmitCallWithStub(Call* expr) { | 1731 void FullCodeGenerator::EmitCallWithStub(Call* expr) { |
1357 // Code common for calls using the call stub. | 1732 // Code common for calls using the call stub. |
1358 ZoneList<Expression*>* args = expr->arguments(); | 1733 ZoneList<Expression*>* args = expr->arguments(); |
1359 int arg_count = args->length(); | 1734 int arg_count = args->length(); |
1360 for (int i = 0; i < arg_count; i++) { | 1735 for (int i = 0; i < arg_count; i++) { |
1361 VisitForValue(args->at(i), kStack); | 1736 VisitForValue(args->at(i), kStack); |
1362 } | 1737 } |
1363 // Record source position for debugger. | 1738 // Record source position for debugger. |
1364 SetSourcePosition(expr->position()); | 1739 SetSourcePosition(expr->position()); |
1365 CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE); | 1740 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 1741 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
1366 __ CallStub(&stub); | 1742 __ CallStub(&stub); |
1367 // Restore context register. | 1743 // Restore context register. |
1368 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | 1744 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
1369 // Discard the function left on TOS. | 1745 // Discard the function left on TOS. |
1370 DropAndApply(1, context_, rax); | 1746 DropAndApply(1, context_, rax); |
1371 } | 1747 } |
1372 | 1748 |
1373 | 1749 |
1374 void FullCodeGenerator::VisitCall(Call* expr) { | 1750 void FullCodeGenerator::VisitCall(Call* expr) { |
1375 Comment cmnt(masm_, "[ Call"); | 1751 Comment cmnt(masm_, "[ Call"); |
1376 Expression* fun = expr->expression(); | 1752 Expression* fun = expr->expression(); |
1377 Variable* var = fun->AsVariableProxy()->AsVariable(); | 1753 Variable* var = fun->AsVariableProxy()->AsVariable(); |
1378 | 1754 |
1379 if (var != NULL && var->is_possibly_eval()) { | 1755 if (var != NULL && var->is_possibly_eval()) { |
1380 // Call to the identifier 'eval'. | 1756 // In a call to eval, we first call %ResolvePossiblyDirectEval to |
1381 UNREACHABLE(); | 1757 // resolve the function we need to call and the receiver of the |
| 1758 // call. The we call the resolved function using the given |
| 1759 // arguments. |
| 1760 VisitForValue(fun, kStack); |
| 1761 __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot. |
| 1762 |
| 1763 // Push the arguments. |
| 1764 ZoneList<Expression*>* args = expr->arguments(); |
| 1765 int arg_count = args->length(); |
| 1766 for (int i = 0; i < arg_count; i++) { |
| 1767 VisitForValue(args->at(i), kStack); |
| 1768 } |
| 1769 |
| 1770 // Push copy of the function - found below the arguments. |
| 1771 __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); |
| 1772 |
| 1773 // Push copy of the first argument or undefined if it doesn't exist. |
| 1774 if (arg_count > 0) { |
| 1775 __ push(Operand(rsp, arg_count * kPointerSize)); |
| 1776 } else { |
| 1777 __ PushRoot(Heap::kUndefinedValueRootIndex); |
| 1778 } |
| 1779 |
| 1780 // Push the receiver of the enclosing function and do runtime call. |
| 1781 __ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize)); |
| 1782 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); |
| 1783 |
| 1784 // The runtime call returns a pair of values in rax (function) and |
| 1785 // rdx (receiver). Touch up the stack with the right values. |
| 1786 __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx); |
| 1787 __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax); |
| 1788 |
| 1789 // Record source position for debugger. |
| 1790 SetSourcePosition(expr->position()); |
| 1791 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 1792 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
| 1793 __ CallStub(&stub); |
| 1794 // Restore context register. |
| 1795 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
| 1796 DropAndApply(1, context_, rax); |
1382 } else if (var != NULL && !var->is_this() && var->is_global()) { | 1797 } else if (var != NULL && !var->is_this() && var->is_global()) { |
1383 // Call to a global variable. | 1798 // Call to a global variable. |
1384 // Push global object as receiver for the call IC lookup. | 1799 // Push global object as receiver for the call IC lookup. |
1385 __ push(CodeGenerator::GlobalObject()); | 1800 __ push(CodeGenerator::GlobalObject()); |
1386 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); | 1801 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); |
1387 } else if (var != NULL && var->slot() != NULL && | 1802 } else if (var != NULL && var->slot() != NULL && |
1388 var->slot()->type() == Slot::LOOKUP) { | 1803 var->slot()->type() == Slot::LOOKUP) { |
1389 // Call to a lookup slot. | 1804 // Call to a lookup slot (dynamically introduced variable). Call |
1390 UNREACHABLE(); | 1805 // the runtime to find the function to call (returned in rax) and |
| 1806 // the object holding it (returned in rdx). |
| 1807 __ push(context_register()); |
| 1808 __ Push(var->name()); |
| 1809 __ CallRuntime(Runtime::kLoadContextSlot, 2); |
| 1810 __ push(rax); // Function. |
| 1811 __ push(rdx); // Receiver. |
| 1812 EmitCallWithStub(expr); |
1391 } else if (fun->AsProperty() != NULL) { | 1813 } else if (fun->AsProperty() != NULL) { |
1392 // Call to an object property. | 1814 // Call to an object property. |
1393 Property* prop = fun->AsProperty(); | 1815 Property* prop = fun->AsProperty(); |
1394 Literal* key = prop->key()->AsLiteral(); | 1816 Literal* key = prop->key()->AsLiteral(); |
1395 if (key != NULL && key->handle()->IsSymbol()) { | 1817 if (key != NULL && key->handle()->IsSymbol()) { |
1396 // Call to a named property, use call IC. | 1818 // Call to a named property, use call IC. |
1397 VisitForValue(prop->obj(), kStack); | 1819 VisitForValue(prop->obj(), kStack); |
1398 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); | 1820 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); |
1399 } else { | 1821 } else { |
1400 // Call to a keyed property, use keyed load IC followed by function | 1822 // 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... |
1471 __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); | 1893 __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); |
1472 | 1894 |
1473 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); | 1895 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); |
1474 __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); | 1896 __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); |
1475 | 1897 |
1476 // Replace function on TOS with result in rax, or pop it. | 1898 // Replace function on TOS with result in rax, or pop it. |
1477 DropAndApply(1, context_, rax); | 1899 DropAndApply(1, context_, rax); |
1478 } | 1900 } |
1479 | 1901 |
1480 | 1902 |
| 1903 void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) { |
| 1904 Handle<String> name = expr->name(); |
| 1905 if (strcmp("_IsSmi", *name->ToCString()) == 0) { |
| 1906 EmitIsSmi(expr->arguments()); |
| 1907 } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) { |
| 1908 EmitIsNonNegativeSmi(expr->arguments()); |
| 1909 } else if (strcmp("_IsObject", *name->ToCString()) == 0) { |
| 1910 EmitIsObject(expr->arguments()); |
| 1911 } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) { |
| 1912 EmitIsUndetectableObject(expr->arguments()); |
| 1913 } else if (strcmp("_IsFunction", *name->ToCString()) == 0) { |
| 1914 EmitIsFunction(expr->arguments()); |
| 1915 } else if (strcmp("_IsArray", *name->ToCString()) == 0) { |
| 1916 EmitIsArray(expr->arguments()); |
| 1917 } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) { |
| 1918 EmitIsRegExp(expr->arguments()); |
| 1919 } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) { |
| 1920 EmitIsConstructCall(expr->arguments()); |
| 1921 } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) { |
| 1922 EmitObjectEquals(expr->arguments()); |
| 1923 } else if (strcmp("_Arguments", *name->ToCString()) == 0) { |
| 1924 EmitArguments(expr->arguments()); |
| 1925 } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) { |
| 1926 EmitArgumentsLength(expr->arguments()); |
| 1927 } else if (strcmp("_ClassOf", *name->ToCString()) == 0) { |
| 1928 EmitClassOf(expr->arguments()); |
| 1929 } else if (strcmp("_Log", *name->ToCString()) == 0) { |
| 1930 EmitLog(expr->arguments()); |
| 1931 } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) { |
| 1932 EmitRandomHeapNumber(expr->arguments()); |
| 1933 } else if (strcmp("_SubString", *name->ToCString()) == 0) { |
| 1934 EmitSubString(expr->arguments()); |
| 1935 } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) { |
| 1936 EmitRegExpExec(expr->arguments()); |
| 1937 } else if (strcmp("_ValueOf", *name->ToCString()) == 0) { |
| 1938 EmitValueOf(expr->arguments()); |
| 1939 } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) { |
| 1940 EmitSetValueOf(expr->arguments()); |
| 1941 } else if (strcmp("_NumberToString", *name->ToCString()) == 0) { |
| 1942 EmitNumberToString(expr->arguments()); |
| 1943 } else if (strcmp("_CharFromCode", *name->ToCString()) == 0) { |
| 1944 EmitCharFromCode(expr->arguments()); |
| 1945 } else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) { |
| 1946 EmitFastCharCodeAt(expr->arguments()); |
| 1947 } else if (strcmp("_StringAdd", *name->ToCString()) == 0) { |
| 1948 EmitStringAdd(expr->arguments()); |
| 1949 } else if (strcmp("_StringCompare", *name->ToCString()) == 0) { |
| 1950 EmitStringCompare(expr->arguments()); |
| 1951 } else if (strcmp("_MathPow", *name->ToCString()) == 0) { |
| 1952 EmitMathPow(expr->arguments()); |
| 1953 } else if (strcmp("_MathSin", *name->ToCString()) == 0) { |
| 1954 EmitMathSin(expr->arguments()); |
| 1955 } else if (strcmp("_MathCos", *name->ToCString()) == 0) { |
| 1956 EmitMathCos(expr->arguments()); |
| 1957 } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) { |
| 1958 EmitMathSqrt(expr->arguments()); |
| 1959 } else if (strcmp("_CallFunction", *name->ToCString()) == 0) { |
| 1960 EmitCallFunction(expr->arguments()); |
| 1961 } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) { |
| 1962 EmitRegExpConstructResult(expr->arguments()); |
| 1963 } else if (strcmp("_SwapElements", *name->ToCString()) == 0) { |
| 1964 EmitSwapElements(expr->arguments()); |
| 1965 } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) { |
| 1966 EmitGetFromCache(expr->arguments()); |
| 1967 } else { |
| 1968 UNREACHABLE(); |
| 1969 } |
| 1970 } |
| 1971 |
| 1972 |
| 1973 void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { |
| 1974 ASSERT(args->length() == 1); |
| 1975 |
| 1976 VisitForValue(args->at(0), kAccumulator); |
| 1977 |
| 1978 Label materialize_true, materialize_false; |
| 1979 Label* if_true = NULL; |
| 1980 Label* if_false = NULL; |
| 1981 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 1982 |
| 1983 __ JumpIfSmi(rax, if_true); |
| 1984 __ jmp(if_false); |
| 1985 |
| 1986 Apply(context_, if_true, if_false); |
| 1987 } |
| 1988 |
| 1989 |
| 1990 void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { |
| 1991 ASSERT(args->length() == 1); |
| 1992 |
| 1993 VisitForValue(args->at(0), kAccumulator); |
| 1994 |
| 1995 Label materialize_true, materialize_false; |
| 1996 Label* if_true = NULL; |
| 1997 Label* if_false = NULL; |
| 1998 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 1999 |
| 2000 Condition positive_smi = __ CheckPositiveSmi(rax); |
| 2001 __ j(positive_smi, if_true); |
| 2002 __ jmp(if_false); |
| 2003 |
| 2004 Apply(context_, if_true, if_false); |
| 2005 } |
| 2006 |
| 2007 |
| 2008 void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { |
| 2009 ASSERT(args->length() == 1); |
| 2010 |
| 2011 VisitForValue(args->at(0), kAccumulator); |
| 2012 |
| 2013 Label materialize_true, materialize_false; |
| 2014 Label* if_true = NULL; |
| 2015 Label* if_false = NULL; |
| 2016 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2017 |
| 2018 __ JumpIfSmi(rax, if_false); |
| 2019 __ CompareRoot(rax, Heap::kNullValueRootIndex); |
| 2020 __ j(equal, if_true); |
| 2021 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); |
| 2022 // Undetectable objects behave like undefined when tested with typeof. |
| 2023 __ testb(FieldOperand(rbx, Map::kBitFieldOffset), |
| 2024 Immediate(1 << Map::kIsUndetectable)); |
| 2025 __ j(not_zero, if_false); |
| 2026 __ movzxbq(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); |
| 2027 __ cmpq(rbx, Immediate(FIRST_JS_OBJECT_TYPE)); |
| 2028 __ j(below, if_false); |
| 2029 __ cmpq(rbx, Immediate(LAST_JS_OBJECT_TYPE)); |
| 2030 __ j(below_equal, if_true); |
| 2031 __ jmp(if_false); |
| 2032 |
| 2033 Apply(context_, if_true, if_false); |
| 2034 } |
| 2035 |
| 2036 |
| 2037 void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { |
| 2038 ASSERT(args->length() == 1); |
| 2039 |
| 2040 VisitForValue(args->at(0), kAccumulator); |
| 2041 |
| 2042 Label materialize_true, materialize_false; |
| 2043 Label* if_true = NULL; |
| 2044 Label* if_false = NULL; |
| 2045 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2046 |
| 2047 __ JumpIfSmi(rax, if_false); |
| 2048 __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); |
| 2049 __ testb(FieldOperand(rbx, Map::kBitFieldOffset), |
| 2050 Immediate(1 << Map::kIsUndetectable)); |
| 2051 __ j(not_zero, if_true); |
| 2052 __ jmp(if_false); |
| 2053 |
| 2054 Apply(context_, if_true, if_false); |
| 2055 } |
| 2056 |
| 2057 |
| 2058 void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { |
| 2059 ASSERT(args->length() == 1); |
| 2060 |
| 2061 VisitForValue(args->at(0), kAccumulator); |
| 2062 |
| 2063 Label materialize_true, materialize_false; |
| 2064 Label* if_true = NULL; |
| 2065 Label* if_false = NULL; |
| 2066 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2067 |
| 2068 __ JumpIfSmi(rax, if_false); |
| 2069 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); |
| 2070 __ j(equal, if_true); |
| 2071 __ jmp(if_false); |
| 2072 |
| 2073 Apply(context_, if_true, if_false); |
| 2074 } |
| 2075 |
| 2076 |
| 2077 void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { |
| 2078 ASSERT(args->length() == 1); |
| 2079 |
| 2080 VisitForValue(args->at(0), kAccumulator); |
| 2081 |
| 2082 Label materialize_true, materialize_false; |
| 2083 Label* if_true = NULL; |
| 2084 Label* if_false = NULL; |
| 2085 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2086 |
| 2087 __ JumpIfSmi(rax, if_false); |
| 2088 __ CmpObjectType(rax, JS_ARRAY_TYPE, rbx); |
| 2089 __ j(equal, if_true); |
| 2090 __ jmp(if_false); |
| 2091 |
| 2092 Apply(context_, if_true, if_false); |
| 2093 } |
| 2094 |
| 2095 |
| 2096 void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { |
| 2097 ASSERT(args->length() == 1); |
| 2098 |
| 2099 VisitForValue(args->at(0), kAccumulator); |
| 2100 |
| 2101 Label materialize_true, materialize_false; |
| 2102 Label* if_true = NULL; |
| 2103 Label* if_false = NULL; |
| 2104 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2105 |
| 2106 __ JumpIfSmi(rax, if_false); |
| 2107 __ CmpObjectType(rax, JS_REGEXP_TYPE, rbx); |
| 2108 __ j(equal, if_true); |
| 2109 __ jmp(if_false); |
| 2110 |
| 2111 Apply(context_, if_true, if_false); |
| 2112 } |
| 2113 |
| 2114 |
| 2115 |
| 2116 void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { |
| 2117 ASSERT(args->length() == 0); |
| 2118 |
| 2119 Label materialize_true, materialize_false; |
| 2120 Label* if_true = NULL; |
| 2121 Label* if_false = NULL; |
| 2122 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2123 |
| 2124 // Get the frame pointer for the calling frame. |
| 2125 __ movq(rax, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); |
| 2126 |
| 2127 // Skip the arguments adaptor frame if it exists. |
| 2128 Label check_frame_marker; |
| 2129 __ SmiCompare(Operand(rax, StandardFrameConstants::kContextOffset), |
| 2130 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); |
| 2131 __ j(not_equal, &check_frame_marker); |
| 2132 __ movq(rax, Operand(rax, StandardFrameConstants::kCallerFPOffset)); |
| 2133 |
| 2134 // Check the marker in the calling frame. |
| 2135 __ bind(&check_frame_marker); |
| 2136 __ SmiCompare(Operand(rax, StandardFrameConstants::kMarkerOffset), |
| 2137 Smi::FromInt(StackFrame::CONSTRUCT)); |
| 2138 __ j(equal, if_true); |
| 2139 __ jmp(if_false); |
| 2140 |
| 2141 Apply(context_, if_true, if_false); |
| 2142 } |
| 2143 |
| 2144 |
| 2145 void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { |
| 2146 ASSERT(args->length() == 2); |
| 2147 |
| 2148 // Load the two objects into registers and perform the comparison. |
| 2149 VisitForValue(args->at(0), kStack); |
| 2150 VisitForValue(args->at(1), kAccumulator); |
| 2151 |
| 2152 Label materialize_true, materialize_false; |
| 2153 Label* if_true = NULL; |
| 2154 Label* if_false = NULL; |
| 2155 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
| 2156 |
| 2157 __ pop(rbx); |
| 2158 __ cmpq(rax, rbx); |
| 2159 __ j(equal, if_true); |
| 2160 __ jmp(if_false); |
| 2161 |
| 2162 Apply(context_, if_true, if_false); |
| 2163 } |
| 2164 |
| 2165 |
| 2166 void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { |
| 2167 ASSERT(args->length() == 1); |
| 2168 |
| 2169 // ArgumentsAccessStub expects the key in edx and the formal |
| 2170 // parameter count in eax. |
| 2171 VisitForValue(args->at(0), kAccumulator); |
| 2172 __ movq(rdx, rax); |
| 2173 __ Move(rax, Smi::FromInt(scope()->num_parameters())); |
| 2174 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); |
| 2175 __ CallStub(&stub); |
| 2176 Apply(context_, rax); |
| 2177 } |
| 2178 |
| 2179 |
| 2180 void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { |
| 2181 ASSERT(args->length() == 0); |
| 2182 |
| 2183 Label exit; |
| 2184 // Get the number of formal parameters. |
| 2185 __ Move(rax, Smi::FromInt(scope()->num_parameters())); |
| 2186 |
| 2187 // Check if the calling frame is an arguments adaptor frame. |
| 2188 __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); |
| 2189 __ SmiCompare(Operand(rbx, StandardFrameConstants::kContextOffset), |
| 2190 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); |
| 2191 __ j(not_equal, &exit); |
| 2192 |
| 2193 // Arguments adaptor case: Read the arguments length from the |
| 2194 // adaptor frame. |
| 2195 __ movq(rax, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| 2196 |
| 2197 __ bind(&exit); |
| 2198 if (FLAG_debug_code) __ AbortIfNotSmi(rax); |
| 2199 Apply(context_, rax); |
| 2200 } |
| 2201 |
| 2202 |
| 2203 void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { |
| 2204 ASSERT(args->length() == 1); |
| 2205 Label done, null, function, non_function_constructor; |
| 2206 |
| 2207 VisitForValue(args->at(0), kAccumulator); |
| 2208 |
| 2209 // If the object is a smi, we return null. |
| 2210 __ JumpIfSmi(rax, &null); |
| 2211 |
| 2212 // Check that the object is a JS object but take special care of JS |
| 2213 // functions to make sure they have 'Function' as their class. |
| 2214 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rax); |
| 2215 __ j(below, &null); |
| 2216 |
| 2217 // As long as JS_FUNCTION_TYPE is the last instance type and it is |
| 2218 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for |
| 2219 // LAST_JS_OBJECT_TYPE. |
| 2220 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); |
| 2221 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); |
| 2222 __ CmpInstanceType(rax, JS_FUNCTION_TYPE); |
| 2223 __ j(equal, &function); |
| 2224 |
| 2225 // Check if the constructor in the map is a function. |
| 2226 __ movq(rax, FieldOperand(rax, Map::kConstructorOffset)); |
| 2227 __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); |
| 2228 __ j(not_equal, &non_function_constructor); |
| 2229 |
| 2230 // rax now contains the constructor function. Grab the |
| 2231 // instance class name from there. |
| 2232 __ movq(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset)); |
| 2233 __ movq(rax, FieldOperand(rax, SharedFunctionInfo::kInstanceClassNameOffset)); |
| 2234 __ jmp(&done); |
| 2235 |
| 2236 // Functions have class 'Function'. |
| 2237 __ bind(&function); |
| 2238 __ Move(rax, Factory::function_class_symbol()); |
| 2239 __ jmp(&done); |
| 2240 |
| 2241 // Objects with a non-function constructor have class 'Object'. |
| 2242 __ bind(&non_function_constructor); |
| 2243 __ Move(rax, Factory::Object_symbol()); |
| 2244 __ jmp(&done); |
| 2245 |
| 2246 // Non-JS objects have class null. |
| 2247 __ bind(&null); |
| 2248 __ LoadRoot(rax, Heap::kNullValueRootIndex); |
| 2249 |
| 2250 // All done. |
| 2251 __ bind(&done); |
| 2252 |
| 2253 Apply(context_, rax); |
| 2254 } |
| 2255 |
| 2256 |
| 2257 void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { |
| 2258 // Conditionally generate a log call. |
| 2259 // Args: |
| 2260 // 0 (literal string): The type of logging (corresponds to the flags). |
| 2261 // This is used to determine whether or not to generate the log call. |
| 2262 // 1 (string): Format string. Access the string at argument index 2 |
| 2263 // with '%2s' (see Logger::LogRuntime for all the formats). |
| 2264 // 2 (array): Arguments to the format string. |
| 2265 ASSERT_EQ(args->length(), 3); |
| 2266 #ifdef ENABLE_LOGGING_AND_PROFILING |
| 2267 if (CodeGenerator::ShouldGenerateLog(args->at(0))) { |
| 2268 VisitForValue(args->at(1), kStack); |
| 2269 VisitForValue(args->at(2), kStack); |
| 2270 __ CallRuntime(Runtime::kLog, 2); |
| 2271 } |
| 2272 #endif |
| 2273 // Finally, we're expected to leave a value on the top of the stack. |
| 2274 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 2275 Apply(context_, rax); |
| 2276 } |
| 2277 |
| 2278 |
| 2279 void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { |
| 2280 ASSERT(args->length() == 0); |
| 2281 |
| 2282 Label slow_allocate_heapnumber; |
| 2283 Label heapnumber_allocated; |
| 2284 |
| 2285 __ AllocateHeapNumber(rbx, rcx, &slow_allocate_heapnumber); |
| 2286 __ jmp(&heapnumber_allocated); |
| 2287 |
| 2288 __ bind(&slow_allocate_heapnumber); |
| 2289 // To allocate a heap number, and ensure that it is not a smi, we |
| 2290 // call the runtime function FUnaryMinus on 0, returning the double |
| 2291 // -0.0. A new, distinct heap number is returned each time. |
| 2292 __ Push(Smi::FromInt(0)); |
| 2293 __ CallRuntime(Runtime::kNumberUnaryMinus, 1); |
| 2294 __ movq(rbx, rax); |
| 2295 |
| 2296 __ bind(&heapnumber_allocated); |
| 2297 |
| 2298 // Return a random uint32 number in rax. |
| 2299 // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs. |
| 2300 __ PrepareCallCFunction(0); |
| 2301 __ CallCFunction(ExternalReference::random_uint32_function(), 0); |
| 2302 |
| 2303 // Convert 32 random bits in rax to 0.(32 random bits) in a double |
| 2304 // by computing: |
| 2305 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). |
| 2306 __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single. |
| 2307 __ movd(xmm1, rcx); |
| 2308 __ movd(xmm0, rax); |
| 2309 __ cvtss2sd(xmm1, xmm1); |
| 2310 __ xorpd(xmm0, xmm1); |
| 2311 __ subsd(xmm0, xmm1); |
| 2312 __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0); |
| 2313 |
| 2314 __ movq(rax, rbx); |
| 2315 Apply(context_, rax); |
| 2316 } |
| 2317 |
| 2318 |
| 2319 void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { |
| 2320 // Load the arguments on the stack and call the stub. |
| 2321 SubStringStub stub; |
| 2322 ASSERT(args->length() == 3); |
| 2323 VisitForValue(args->at(0), kStack); |
| 2324 VisitForValue(args->at(1), kStack); |
| 2325 VisitForValue(args->at(2), kStack); |
| 2326 __ CallStub(&stub); |
| 2327 Apply(context_, rax); |
| 2328 } |
| 2329 |
| 2330 |
| 2331 void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { |
| 2332 // Load the arguments on the stack and call the stub. |
| 2333 RegExpExecStub stub; |
| 2334 ASSERT(args->length() == 4); |
| 2335 VisitForValue(args->at(0), kStack); |
| 2336 VisitForValue(args->at(1), kStack); |
| 2337 VisitForValue(args->at(2), kStack); |
| 2338 VisitForValue(args->at(3), kStack); |
| 2339 __ CallStub(&stub); |
| 2340 Apply(context_, rax); |
| 2341 } |
| 2342 |
| 2343 |
| 2344 void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { |
| 2345 ASSERT(args->length() == 1); |
| 2346 |
| 2347 VisitForValue(args->at(0), kAccumulator); // Load the object. |
| 2348 |
| 2349 Label done; |
| 2350 // If the object is a smi return the object. |
| 2351 __ JumpIfSmi(rax, &done); |
| 2352 // If the object is not a value type, return the object. |
| 2353 __ CmpObjectType(rax, JS_VALUE_TYPE, rbx); |
| 2354 __ j(not_equal, &done); |
| 2355 __ movq(rax, FieldOperand(rax, JSValue::kValueOffset)); |
| 2356 |
| 2357 __ bind(&done); |
| 2358 Apply(context_, rax); |
| 2359 } |
| 2360 |
| 2361 |
| 2362 void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { |
| 2363 // Load the arguments on the stack and call the runtime function. |
| 2364 ASSERT(args->length() == 2); |
| 2365 VisitForValue(args->at(0), kStack); |
| 2366 VisitForValue(args->at(1), kStack); |
| 2367 __ CallRuntime(Runtime::kMath_pow, 2); |
| 2368 Apply(context_, rax); |
| 2369 } |
| 2370 |
| 2371 |
| 2372 void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { |
| 2373 ASSERT(args->length() == 2); |
| 2374 |
| 2375 VisitForValue(args->at(0), kStack); // Load the object. |
| 2376 VisitForValue(args->at(1), kAccumulator); // Load the value. |
| 2377 __ pop(rbx); // rax = value. ebx = object. |
| 2378 |
| 2379 Label done; |
| 2380 // If the object is a smi, return the value. |
| 2381 __ JumpIfSmi(rbx, &done); |
| 2382 |
| 2383 // If the object is not a value type, return the value. |
| 2384 __ CmpObjectType(rbx, JS_VALUE_TYPE, rcx); |
| 2385 __ j(not_equal, &done); |
| 2386 |
| 2387 // Store the value. |
| 2388 __ movq(FieldOperand(rbx, JSValue::kValueOffset), rax); |
| 2389 // Update the write barrier. Save the value as it will be |
| 2390 // overwritten by the write barrier code and is needed afterward. |
| 2391 __ movq(rdx, rax); |
| 2392 __ RecordWrite(rbx, JSValue::kValueOffset, rdx, rcx); |
| 2393 |
| 2394 __ bind(&done); |
| 2395 Apply(context_, rax); |
| 2396 } |
| 2397 |
| 2398 |
| 2399 void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { |
| 2400 ASSERT_EQ(args->length(), 1); |
| 2401 |
| 2402 // Load the argument on the stack and call the stub. |
| 2403 VisitForValue(args->at(0), kStack); |
| 2404 |
| 2405 NumberToStringStub stub; |
| 2406 __ CallStub(&stub); |
| 2407 Apply(context_, rax); |
| 2408 } |
| 2409 |
| 2410 |
| 2411 void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) { |
| 2412 ASSERT(args->length() == 1); |
| 2413 |
| 2414 VisitForValue(args->at(0), kAccumulator); |
| 2415 |
| 2416 Label slow_case, done; |
| 2417 // Fast case of Heap::LookupSingleCharacterStringFromCode. |
| 2418 __ JumpIfNotSmi(rax, &slow_case); |
| 2419 __ SmiToInteger32(rcx, rax); |
| 2420 __ cmpl(rcx, Immediate(String::kMaxAsciiCharCode)); |
| 2421 __ j(above, &slow_case); |
| 2422 |
| 2423 __ Move(rbx, Factory::single_character_string_cache()); |
| 2424 __ movq(rbx, FieldOperand(rbx, |
| 2425 rcx, |
| 2426 times_pointer_size, |
| 2427 FixedArray::kHeaderSize)); |
| 2428 |
| 2429 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); |
| 2430 __ j(equal, &slow_case); |
| 2431 __ movq(rax, rbx); |
| 2432 __ jmp(&done); |
| 2433 |
| 2434 __ bind(&slow_case); |
| 2435 __ push(rax); |
| 2436 __ CallRuntime(Runtime::kCharFromCode, 1); |
| 2437 |
| 2438 __ bind(&done); |
| 2439 Apply(context_, rax); |
| 2440 } |
| 2441 |
| 2442 |
| 2443 void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) { |
| 2444 // TODO(fsc): Port the complete implementation from the classic back-end. |
| 2445 // Move the undefined value into the result register, which will |
| 2446 // trigger the slow case. |
| 2447 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 2448 Apply(context_, rax); |
| 2449 } |
| 2450 |
| 2451 void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { |
| 2452 ASSERT_EQ(2, args->length()); |
| 2453 |
| 2454 VisitForValue(args->at(0), kStack); |
| 2455 VisitForValue(args->at(1), kStack); |
| 2456 |
| 2457 StringAddStub stub(NO_STRING_ADD_FLAGS); |
| 2458 __ CallStub(&stub); |
| 2459 Apply(context_, rax); |
| 2460 } |
| 2461 |
| 2462 |
| 2463 void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { |
| 2464 ASSERT_EQ(2, args->length()); |
| 2465 |
| 2466 VisitForValue(args->at(0), kStack); |
| 2467 VisitForValue(args->at(1), kStack); |
| 2468 |
| 2469 StringCompareStub stub; |
| 2470 __ CallStub(&stub); |
| 2471 Apply(context_, rax); |
| 2472 } |
| 2473 |
| 2474 |
| 2475 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { |
| 2476 // Load the argument on the stack and call the stub. |
| 2477 TranscendentalCacheStub stub(TranscendentalCache::SIN); |
| 2478 ASSERT(args->length() == 1); |
| 2479 VisitForValue(args->at(0), kStack); |
| 2480 __ CallStub(&stub); |
| 2481 Apply(context_, rax); |
| 2482 } |
| 2483 |
| 2484 |
| 2485 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { |
| 2486 // Load the argument on the stack and call the stub. |
| 2487 TranscendentalCacheStub stub(TranscendentalCache::COS); |
| 2488 ASSERT(args->length() == 1); |
| 2489 VisitForValue(args->at(0), kStack); |
| 2490 __ CallStub(&stub); |
| 2491 Apply(context_, rax); |
| 2492 } |
| 2493 |
| 2494 |
| 2495 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { |
| 2496 // Load the argument on the stack and call the runtime function. |
| 2497 ASSERT(args->length() == 1); |
| 2498 VisitForValue(args->at(0), kStack); |
| 2499 __ CallRuntime(Runtime::kMath_sqrt, 1); |
| 2500 Apply(context_, rax); |
| 2501 } |
| 2502 |
| 2503 |
| 2504 void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { |
| 2505 ASSERT(args->length() >= 2); |
| 2506 |
| 2507 int arg_count = args->length() - 2; // For receiver and function. |
| 2508 VisitForValue(args->at(0), kStack); // Receiver. |
| 2509 for (int i = 0; i < arg_count; i++) { |
| 2510 VisitForValue(args->at(i + 1), kStack); |
| 2511 } |
| 2512 VisitForValue(args->at(arg_count + 1), kAccumulator); // Function. |
| 2513 |
| 2514 // InvokeFunction requires function in rdi. Move it in there. |
| 2515 if (!result_register().is(rdi)) __ movq(rdi, result_register()); |
| 2516 ParameterCount count(arg_count); |
| 2517 __ InvokeFunction(rdi, count, CALL_FUNCTION); |
| 2518 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
| 2519 Apply(context_, rax); |
| 2520 } |
| 2521 |
| 2522 |
| 2523 void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { |
| 2524 ASSERT(args->length() == 3); |
| 2525 VisitForValue(args->at(0), kStack); |
| 2526 VisitForValue(args->at(1), kStack); |
| 2527 VisitForValue(args->at(2), kStack); |
| 2528 __ CallRuntime(Runtime::kRegExpConstructResult, 3); |
| 2529 Apply(context_, rax); |
| 2530 } |
| 2531 |
| 2532 |
| 2533 void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { |
| 2534 ASSERT(args->length() == 3); |
| 2535 VisitForValue(args->at(0), kStack); |
| 2536 VisitForValue(args->at(1), kStack); |
| 2537 VisitForValue(args->at(2), kStack); |
| 2538 __ CallRuntime(Runtime::kSwapElements, 3); |
| 2539 Apply(context_, rax); |
| 2540 } |
| 2541 |
| 2542 |
| 2543 void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { |
| 2544 ASSERT_EQ(2, args->length()); |
| 2545 |
| 2546 ASSERT_NE(NULL, args->at(0)->AsLiteral()); |
| 2547 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); |
| 2548 |
| 2549 Handle<FixedArray> jsfunction_result_caches( |
| 2550 Top::global_context()->jsfunction_result_caches()); |
| 2551 if (jsfunction_result_caches->length() <= cache_id) { |
| 2552 __ Abort("Attempt to use undefined cache."); |
| 2553 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 2554 Apply(context_, rax); |
| 2555 return; |
| 2556 } |
| 2557 |
| 2558 VisitForValue(args->at(1), kAccumulator); |
| 2559 |
| 2560 Register key = rax; |
| 2561 Register cache = rbx; |
| 2562 Register tmp = rcx; |
| 2563 __ movq(cache, CodeGenerator::ContextOperand(rsi, Context::GLOBAL_INDEX)); |
| 2564 __ movq(cache, |
| 2565 FieldOperand(cache, GlobalObject::kGlobalContextOffset)); |
| 2566 __ movq(cache, |
| 2567 CodeGenerator::ContextOperand( |
| 2568 cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); |
| 2569 __ movq(cache, |
| 2570 FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); |
| 2571 |
| 2572 Label done, not_found; |
| 2573 // tmp now holds finger offset as a smi. |
| 2574 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
| 2575 __ movq(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset)); |
| 2576 SmiIndex index = |
| 2577 __ SmiToIndex(kScratchRegister, tmp, kPointerSizeLog2); |
| 2578 __ cmpq(key, FieldOperand(cache, |
| 2579 index.reg, |
| 2580 index.scale, |
| 2581 FixedArray::kHeaderSize)); |
| 2582 __ j(not_equal, ¬_found); |
| 2583 __ movq(rax, FieldOperand(cache, |
| 2584 index.reg, |
| 2585 index.scale, |
| 2586 FixedArray::kHeaderSize + kPointerSize)); |
| 2587 __ jmp(&done); |
| 2588 |
| 2589 __ bind(¬_found); |
| 2590 // Call runtime to perform the lookup. |
| 2591 __ push(cache); |
| 2592 __ push(key); |
| 2593 __ CallRuntime(Runtime::kGetFromCache, 2); |
| 2594 |
| 2595 __ bind(&done); |
| 2596 Apply(context_, rax); |
| 2597 } |
| 2598 |
| 2599 |
1481 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { | 2600 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { |
| 2601 Handle<String> name = expr->name(); |
| 2602 if (name->length() > 0 && name->Get(0) == '_') { |
| 2603 Comment cmnt(masm_, "[ InlineRuntimeCall"); |
| 2604 EmitInlineRuntimeCall(expr); |
| 2605 return; |
| 2606 } |
| 2607 |
1482 Comment cmnt(masm_, "[ CallRuntime"); | 2608 Comment cmnt(masm_, "[ CallRuntime"); |
1483 ZoneList<Expression*>* args = expr->arguments(); | 2609 ZoneList<Expression*>* args = expr->arguments(); |
1484 | 2610 |
1485 if (expr->is_jsruntime()) { | 2611 if (expr->is_jsruntime()) { |
1486 // Prepare for calling JS runtime function. | 2612 // Prepare for calling JS runtime function. |
1487 __ movq(rax, CodeGenerator::GlobalObject()); | 2613 __ movq(rax, CodeGenerator::GlobalObject()); |
1488 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); | 2614 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); |
1489 } | 2615 } |
1490 | 2616 |
1491 // Push the arguments ("left-to-right"). | 2617 // Push the arguments ("left-to-right"). |
(...skipping 12 matching lines...) Expand all Loading... |
1504 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | 2630 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
1505 } else { | 2631 } else { |
1506 __ CallRuntime(expr->function(), arg_count); | 2632 __ CallRuntime(expr->function(), arg_count); |
1507 } | 2633 } |
1508 Apply(context_, rax); | 2634 Apply(context_, rax); |
1509 } | 2635 } |
1510 | 2636 |
1511 | 2637 |
1512 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { | 2638 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { |
1513 switch (expr->op()) { | 2639 switch (expr->op()) { |
| 2640 case Token::DELETE: { |
| 2641 Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); |
| 2642 Property* prop = expr->expression()->AsProperty(); |
| 2643 Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); |
| 2644 if (prop == NULL && var == NULL) { |
| 2645 // Result of deleting non-property, non-variable reference is true. |
| 2646 // The subexpression may have side effects. |
| 2647 VisitForEffect(expr->expression()); |
| 2648 Apply(context_, true); |
| 2649 } else if (var != NULL && |
| 2650 !var->is_global() && |
| 2651 var->slot() != NULL && |
| 2652 var->slot()->type() != Slot::LOOKUP) { |
| 2653 // Result of deleting non-global, non-dynamic variables is false. |
| 2654 // The subexpression does not have side effects. |
| 2655 Apply(context_, false); |
| 2656 } else { |
| 2657 // Property or variable reference. Call the delete builtin with |
| 2658 // object and property name as arguments. |
| 2659 if (prop != NULL) { |
| 2660 VisitForValue(prop->obj(), kStack); |
| 2661 VisitForValue(prop->key(), kStack); |
| 2662 } else if (var->is_global()) { |
| 2663 __ push(CodeGenerator::GlobalObject()); |
| 2664 __ Push(var->name()); |
| 2665 } else { |
| 2666 // Non-global variable. Call the runtime to look up the context |
| 2667 // where the variable was introduced. |
| 2668 __ push(context_register()); |
| 2669 __ Push(var->name()); |
| 2670 __ CallRuntime(Runtime::kLookupContext, 2); |
| 2671 __ push(rax); |
| 2672 __ Push(var->name()); |
| 2673 } |
| 2674 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); |
| 2675 Apply(context_, rax); |
| 2676 } |
| 2677 break; |
| 2678 } |
| 2679 |
1514 case Token::VOID: { | 2680 case Token::VOID: { |
1515 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); | 2681 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); |
1516 VisitForEffect(expr->expression()); | 2682 VisitForEffect(expr->expression()); |
1517 switch (context_) { | 2683 switch (context_) { |
1518 case Expression::kUninitialized: | 2684 case Expression::kUninitialized: |
1519 UNREACHABLE(); | 2685 UNREACHABLE(); |
1520 break; | 2686 break; |
1521 case Expression::kEffect: | 2687 case Expression::kEffect: |
1522 break; | 2688 break; |
1523 case Expression::kValue: | 2689 case Expression::kValue: |
(...skipping 20 matching lines...) Expand all Loading... |
1544 case Expression::kTest: | 2710 case Expression::kTest: |
1545 case Expression::kValueTest: | 2711 case Expression::kValueTest: |
1546 __ jmp(false_label_); | 2712 __ jmp(false_label_); |
1547 break; | 2713 break; |
1548 } | 2714 } |
1549 break; | 2715 break; |
1550 } | 2716 } |
1551 | 2717 |
1552 case Token::NOT: { | 2718 case Token::NOT: { |
1553 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); | 2719 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); |
1554 Label materialize_true, materialize_false, done; | 2720 Label materialize_true, materialize_false; |
1555 // Initially assume a pure test context. Notice that the labels are | 2721 Label* if_true = NULL; |
1556 // swapped. | 2722 Label* if_false = NULL; |
1557 Label* if_true = false_label_; | 2723 |
1558 Label* if_false = true_label_; | 2724 // Notice that the labels are swapped. |
1559 switch (context_) { | 2725 PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true); |
1560 case Expression::kUninitialized: | 2726 |
1561 UNREACHABLE(); | |
1562 break; | |
1563 case Expression::kEffect: | |
1564 if_true = &done; | |
1565 if_false = &done; | |
1566 break; | |
1567 case Expression::kValue: | |
1568 if_true = &materialize_false; | |
1569 if_false = &materialize_true; | |
1570 break; | |
1571 case Expression::kTest: | |
1572 break; | |
1573 case Expression::kValueTest: | |
1574 if_false = &materialize_true; | |
1575 break; | |
1576 case Expression::kTestValue: | |
1577 if_true = &materialize_false; | |
1578 break; | |
1579 } | |
1580 VisitForControl(expr->expression(), if_true, if_false); | 2727 VisitForControl(expr->expression(), if_true, if_false); |
| 2728 |
1581 Apply(context_, if_false, if_true); // Labels swapped. | 2729 Apply(context_, if_false, if_true); // Labels swapped. |
1582 break; | 2730 break; |
1583 } | 2731 } |
1584 | 2732 |
1585 case Token::TYPEOF: { | 2733 case Token::TYPEOF: { |
1586 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); | 2734 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); |
1587 VariableProxy* proxy = expr->expression()->AsVariableProxy(); | 2735 VariableProxy* proxy = expr->expression()->AsVariableProxy(); |
1588 if (proxy != NULL && | 2736 if (proxy != NULL && |
1589 !proxy->var()->is_this() && | 2737 !proxy->var()->is_this() && |
1590 proxy->var()->is_global()) { | 2738 proxy->var()->is_global()) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1666 | 2814 |
1667 default: | 2815 default: |
1668 UNREACHABLE(); | 2816 UNREACHABLE(); |
1669 } | 2817 } |
1670 } | 2818 } |
1671 | 2819 |
1672 | 2820 |
1673 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { | 2821 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { |
1674 Comment cmnt(masm_, "[ CountOperation"); | 2822 Comment cmnt(masm_, "[ CountOperation"); |
1675 | 2823 |
| 2824 // Invalid left-hand-sides are rewritten to have a 'throw |
| 2825 // ReferenceError' as the left-hand side. |
| 2826 if (!expr->expression()->IsValidLeftHandSide()) { |
| 2827 VisitForEffect(expr->expression()); |
| 2828 return; |
| 2829 } |
| 2830 |
1676 // Expression can only be a property, a global or a (parameter or local) | 2831 // Expression can only be a property, a global or a (parameter or local) |
1677 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. | 2832 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
1678 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; | 2833 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
1679 LhsKind assign_type = VARIABLE; | 2834 LhsKind assign_type = VARIABLE; |
1680 Property* prop = expr->expression()->AsProperty(); | 2835 Property* prop = expr->expression()->AsProperty(); |
1681 // In case of a property we use the uninitialized expression context | 2836 // In case of a property we use the uninitialized expression context |
1682 // of the key to detect a named property. | 2837 // of the key to detect a named property. |
1683 if (prop != NULL) { | 2838 if (prop != NULL) { |
1684 assign_type = | 2839 assign_type = |
1685 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; | 2840 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; |
1686 } | 2841 } |
1687 | 2842 |
1688 // Evaluate expression and get value. | 2843 // Evaluate expression and get value. |
1689 if (assign_type == VARIABLE) { | 2844 if (assign_type == VARIABLE) { |
1690 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); | 2845 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); |
1691 Location saved_location = location_; | 2846 Location saved_location = location_; |
1692 location_ = kAccumulator; | 2847 location_ = kAccumulator; |
1693 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), | 2848 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), |
1694 Expression::kValue); | 2849 Expression::kValue); |
1695 location_ = saved_location; | 2850 location_ = saved_location; |
1696 } else { | 2851 } else { |
1697 // Reserve space for result of postfix operation. | 2852 // Reserve space for result of postfix operation. |
1698 if (expr->is_postfix() && context_ != Expression::kEffect) { | 2853 if (expr->is_postfix() && context_ != Expression::kEffect) { |
1699 __ Push(Smi::FromInt(0)); | 2854 __ Push(Smi::FromInt(0)); |
1700 } | 2855 } |
1701 VisitForValue(prop->obj(), kStack); | 2856 VisitForValue(prop->obj(), kStack); |
1702 if (assign_type == NAMED_PROPERTY) { | 2857 if (assign_type == NAMED_PROPERTY) { |
1703 EmitNamedPropertyLoad(prop); | 2858 EmitNamedPropertyLoad(prop); |
1704 } else { | 2859 } else { |
1705 VisitForValue(prop->key(), kStack); | 2860 VisitForValue(prop->key(), kStack); |
1706 EmitKeyedPropertyLoad(prop); | 2861 EmitKeyedPropertyLoad(prop); |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1858 VisitForValue(expr->right(), kAccumulator); | 3013 VisitForValue(expr->right(), kAccumulator); |
1859 EmitBinaryOp(expr->op(), context_); | 3014 EmitBinaryOp(expr->op(), context_); |
1860 break; | 3015 break; |
1861 | 3016 |
1862 default: | 3017 default: |
1863 UNREACHABLE(); | 3018 UNREACHABLE(); |
1864 } | 3019 } |
1865 } | 3020 } |
1866 | 3021 |
1867 | 3022 |
| 3023 void FullCodeGenerator::EmitNullCompare(bool strict, |
| 3024 Register obj, |
| 3025 Register null_const, |
| 3026 Label* if_true, |
| 3027 Label* if_false, |
| 3028 Register scratch) { |
| 3029 __ cmpq(obj, null_const); |
| 3030 if (strict) { |
| 3031 __ j(equal, if_true); |
| 3032 } else { |
| 3033 __ j(equal, if_true); |
| 3034 __ CompareRoot(obj, Heap::kUndefinedValueRootIndex); |
| 3035 __ j(equal, if_true); |
| 3036 __ JumpIfSmi(obj, if_false); |
| 3037 // It can be an undetectable object. |
| 3038 __ movq(scratch, FieldOperand(obj, HeapObject::kMapOffset)); |
| 3039 __ testb(FieldOperand(scratch, Map::kBitFieldOffset), |
| 3040 Immediate(1 << Map::kIsUndetectable)); |
| 3041 __ j(not_zero, if_true); |
| 3042 } |
| 3043 __ jmp(if_false); |
| 3044 } |
| 3045 |
| 3046 |
| 3047 |
1868 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { | 3048 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { |
1869 Comment cmnt(masm_, "[ CompareOperation"); | 3049 Comment cmnt(masm_, "[ CompareOperation"); |
1870 | 3050 |
1871 // Always perform the comparison for its control flow. Pack the result | 3051 // Always perform the comparison for its control flow. Pack the result |
1872 // into the expression's context after the comparison is performed. | 3052 // into the expression's context after the comparison is performed. |
1873 Label materialize_true, materialize_false, done; | 3053 Label materialize_true, materialize_false; |
1874 // Initially assume we are in a test context. | 3054 Label* if_true = NULL; |
1875 Label* if_true = true_label_; | 3055 Label* if_false = NULL; |
1876 Label* if_false = false_label_; | 3056 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
1877 switch (context_) { | |
1878 case Expression::kUninitialized: | |
1879 UNREACHABLE(); | |
1880 break; | |
1881 case Expression::kEffect: | |
1882 if_true = &done; | |
1883 if_false = &done; | |
1884 break; | |
1885 case Expression::kValue: | |
1886 if_true = &materialize_true; | |
1887 if_false = &materialize_false; | |
1888 break; | |
1889 case Expression::kTest: | |
1890 break; | |
1891 case Expression::kValueTest: | |
1892 if_true = &materialize_true; | |
1893 break; | |
1894 case Expression::kTestValue: | |
1895 if_false = &materialize_false; | |
1896 break; | |
1897 } | |
1898 | 3057 |
1899 VisitForValue(expr->left(), kStack); | 3058 VisitForValue(expr->left(), kStack); |
1900 switch (expr->op()) { | 3059 switch (expr->op()) { |
1901 case Token::IN: | 3060 case Token::IN: |
1902 VisitForValue(expr->right(), kStack); | 3061 VisitForValue(expr->right(), kStack); |
1903 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); | 3062 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); |
1904 __ CompareRoot(rax, Heap::kTrueValueRootIndex); | 3063 __ CompareRoot(rax, Heap::kTrueValueRootIndex); |
1905 __ j(equal, if_true); | 3064 __ j(equal, if_true); |
1906 __ jmp(if_false); | 3065 __ jmp(if_false); |
1907 break; | 3066 break; |
1908 | 3067 |
1909 case Token::INSTANCEOF: { | 3068 case Token::INSTANCEOF: { |
1910 VisitForValue(expr->right(), kStack); | 3069 VisitForValue(expr->right(), kStack); |
1911 InstanceofStub stub; | 3070 InstanceofStub stub; |
1912 __ CallStub(&stub); | 3071 __ CallStub(&stub); |
1913 __ testq(rax, rax); | 3072 __ testq(rax, rax); |
1914 __ j(zero, if_true); // The stub returns 0 for true. | 3073 __ j(zero, if_true); // The stub returns 0 for true. |
1915 __ jmp(if_false); | 3074 __ jmp(if_false); |
1916 break; | 3075 break; |
1917 } | 3076 } |
1918 | 3077 |
1919 default: { | 3078 default: { |
1920 VisitForValue(expr->right(), kAccumulator); | 3079 VisitForValue(expr->right(), kAccumulator); |
1921 Condition cc = no_condition; | 3080 Condition cc = no_condition; |
1922 bool strict = false; | 3081 bool strict = false; |
1923 switch (expr->op()) { | 3082 switch (expr->op()) { |
1924 case Token::EQ_STRICT: | 3083 case Token::EQ_STRICT: |
1925 strict = true; | 3084 strict = true; |
1926 // Fall through. | 3085 // Fall through. |
1927 case Token::EQ: | 3086 case Token::EQ: { |
1928 cc = equal; | 3087 cc = equal; |
1929 __ pop(rdx); | 3088 __ pop(rdx); |
| 3089 // If either operand is constant null we do a fast compare |
| 3090 // against null. |
| 3091 Literal* right_literal = expr->right()->AsLiteral(); |
| 3092 Literal* left_literal = expr->left()->AsLiteral(); |
| 3093 if (right_literal != NULL && right_literal->handle()->IsNull()) { |
| 3094 EmitNullCompare(strict, rdx, rax, if_true, if_false, rcx); |
| 3095 Apply(context_, if_true, if_false); |
| 3096 return; |
| 3097 } else if (left_literal != NULL && left_literal->handle()->IsNull()) { |
| 3098 EmitNullCompare(strict, rax, rdx, if_true, if_false, rcx); |
| 3099 Apply(context_, if_true, if_false); |
| 3100 return; |
| 3101 } |
1930 break; | 3102 break; |
| 3103 } |
1931 case Token::LT: | 3104 case Token::LT: |
1932 cc = less; | 3105 cc = less; |
1933 __ pop(rdx); | 3106 __ pop(rdx); |
1934 break; | 3107 break; |
1935 case Token::GT: | 3108 case Token::GT: |
1936 // Reverse left and right sizes to obtain ECMA-262 conversion order. | 3109 // Reverse left and right sizes to obtain ECMA-262 conversion order. |
1937 cc = less; | 3110 cc = less; |
1938 __ movq(rdx, result_register()); | 3111 __ movq(rdx, result_register()); |
1939 __ pop(rax); | 3112 __ pop(rax); |
1940 break; | 3113 break; |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2033 __ ret(0); | 3206 __ ret(0); |
2034 } | 3207 } |
2035 | 3208 |
2036 | 3209 |
2037 #undef __ | 3210 #undef __ |
2038 | 3211 |
2039 | 3212 |
2040 } } // namespace v8::internal | 3213 } } // namespace v8::internal |
2041 | 3214 |
2042 #endif // V8_TARGET_ARCH_X64 | 3215 #endif // V8_TARGET_ARCH_X64 |
OLD | NEW |