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 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; | 84 if (scope()->num_heap_slots() > 0) { |
85 if (heap_slots > 0) { | |
86 Comment cmnt(masm_, "[ Allocate local context"); | 85 Comment cmnt(masm_, "[ Allocate local context"); |
87 // Argument to NewContext is the function, which is still in rdi. | 86 // Argument to NewContext is the function, which is still in rdi. |
88 __ push(rdi); | 87 __ push(rdi); |
89 if (heap_slots <= FastNewContextStub::kMaximumSlots) { | 88 __ CallRuntime(Runtime::kNewContext, 1); |
90 FastNewContextStub stub(heap_slots); | |
91 __ CallStub(&stub); | |
92 } else { | |
93 __ CallRuntime(Runtime::kNewContext, 1); | |
94 } | |
95 function_in_register = false; | 89 function_in_register = false; |
96 // Context is returned in both rax and rsi. It replaces the context | 90 // Context is returned in both rax and rsi. It replaces the context |
97 // passed to us. It's saved in the stack and kept live in rsi. | 91 // passed to us. It's saved in the stack and kept live in rsi. |
98 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); | 92 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); |
99 | 93 |
100 // Copy any necessary parameters into the context. | 94 // Copy any necessary parameters into the context. |
101 int num_parameters = scope()->num_parameters(); | 95 int num_parameters = scope()->num_parameters(); |
102 for (int i = 0; i < num_parameters; i++) { | 96 for (int i = 0; i < num_parameters; i++) { |
103 Slot* slot = scope()->parameter(i)->slot(); | 97 Slot* slot = scope()->parameter(i)->slot(); |
104 if (slot != NULL && slot->type() == Slot::CONTEXT) { | 98 if (slot != NULL && slot->type() == Slot::CONTEXT) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
144 // Store new arguments object in both "arguments" and ".arguments" slots. | 138 // Store new arguments object in both "arguments" and ".arguments" slots. |
145 __ movq(rcx, rax); | 139 __ movq(rcx, rax); |
146 Move(arguments->slot(), rax, rbx, rdx); | 140 Move(arguments->slot(), rax, rbx, rdx); |
147 Slot* dot_arguments_slot = | 141 Slot* dot_arguments_slot = |
148 scope()->arguments_shadow()->AsVariable()->slot(); | 142 scope()->arguments_shadow()->AsVariable()->slot(); |
149 Move(dot_arguments_slot, rcx, rbx, rdx); | 143 Move(dot_arguments_slot, rcx, rbx, rdx); |
150 } | 144 } |
151 } | 145 } |
152 | 146 |
153 { Comment cmnt(masm_, "[ Declarations"); | 147 { Comment cmnt(masm_, "[ Declarations"); |
154 // For named function expressions, declare the function name as a | 148 VisitDeclarations(scope()->declarations()); |
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 } | |
166 } | 149 } |
167 | 150 |
168 { Comment cmnt(masm_, "[ Stack check"); | 151 { Comment cmnt(masm_, "[ Stack check"); |
169 Label ok; | 152 Label ok; |
170 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); | 153 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); |
171 __ j(above_equal, &ok); | 154 __ j(above_equal, &ok); |
172 StackCheckStub stub; | 155 StackCheckStub stub; |
173 __ CallStub(&stub); | 156 __ CallStub(&stub); |
174 __ bind(&ok); | 157 __ bind(&ok); |
175 } | 158 } |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 __ movq(result_register(), reg); | 422 __ movq(result_register(), reg); |
440 __ movq(Operand(rsp, 0), result_register()); | 423 __ movq(Operand(rsp, 0), result_register()); |
441 break; | 424 break; |
442 } | 425 } |
443 DoTest(context); | 426 DoTest(context); |
444 break; | 427 break; |
445 } | 428 } |
446 } | 429 } |
447 | 430 |
448 | 431 |
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 | |
482 void FullCodeGenerator::Apply(Expression::Context context, | 432 void FullCodeGenerator::Apply(Expression::Context context, |
483 Label* materialize_true, | 433 Label* materialize_true, |
484 Label* materialize_false) { | 434 Label* materialize_false) { |
485 switch (context) { | 435 switch (context) { |
486 case Expression::kUninitialized: | 436 case Expression::kUninitialized: |
487 | 437 |
488 case Expression::kEffect: | 438 case Expression::kEffect: |
489 ASSERT_EQ(materialize_true, materialize_false); | 439 ASSERT_EQ(materialize_true, materialize_false); |
490 __ bind(materialize_true); | 440 __ bind(materialize_true); |
491 break; | 441 break; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
537 case kStack: | 487 case kStack: |
538 __ Push(Factory::false_value()); | 488 __ Push(Factory::false_value()); |
539 break; | 489 break; |
540 } | 490 } |
541 __ jmp(false_label_); | 491 __ jmp(false_label_); |
542 break; | 492 break; |
543 } | 493 } |
544 } | 494 } |
545 | 495 |
546 | 496 |
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 | |
602 void FullCodeGenerator::DoTest(Expression::Context context) { | 497 void FullCodeGenerator::DoTest(Expression::Context context) { |
603 // The value to test is in the accumulator. If the value might be needed | 498 // The value to test is in the accumulator. If the value might be needed |
604 // on the stack (value/test and test/value contexts with a stack location | 499 // on the stack (value/test and test/value contexts with a stack location |
605 // desired), then the value is already duplicated on the stack. | 500 // desired), then the value is already duplicated on the stack. |
606 ASSERT_NE(NULL, true_label_); | 501 ASSERT_NE(NULL, true_label_); |
607 ASSERT_NE(NULL, false_label_); | 502 ASSERT_NE(NULL, false_label_); |
608 | 503 |
609 // In value/test and test/value expression contexts with stack as the | 504 // In value/test and test/value expression contexts with stack as the |
610 // desired location, there is already an extra value on the stack. Use a | 505 // desired location, there is already an extra value on the stack. Use a |
611 // label to discard it if unneeded. | 506 // label to discard it if unneeded. |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
767 MemOperand location = EmitSlotSearch(dst, scratch1); | 662 MemOperand location = EmitSlotSearch(dst, scratch1); |
768 __ movq(location, src); | 663 __ movq(location, src); |
769 // Emit the write barrier code if the location is in the heap. | 664 // Emit the write barrier code if the location is in the heap. |
770 if (dst->type() == Slot::CONTEXT) { | 665 if (dst->type() == Slot::CONTEXT) { |
771 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; | 666 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; |
772 __ RecordWrite(scratch1, offset, src, scratch2); | 667 __ RecordWrite(scratch1, offset, src, scratch2); |
773 } | 668 } |
774 } | 669 } |
775 | 670 |
776 | 671 |
777 void FullCodeGenerator::EmitDeclaration(Variable* variable, | 672 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { |
778 Variable::Mode mode, | |
779 FunctionLiteral* function) { | |
780 Comment cmnt(masm_, "[ Declaration"); | 673 Comment cmnt(masm_, "[ Declaration"); |
781 ASSERT(variable != NULL); // Must have been resolved. | 674 Variable* var = decl->proxy()->var(); |
782 Slot* slot = variable->slot(); | 675 ASSERT(var != NULL); // Must have been resolved. |
783 Property* prop = variable->AsProperty(); | 676 Slot* slot = var->slot(); |
| 677 Property* prop = var->AsProperty(); |
784 | 678 |
785 if (slot != NULL) { | 679 if (slot != NULL) { |
786 switch (slot->type()) { | 680 switch (slot->type()) { |
787 case Slot::PARAMETER: | 681 case Slot::PARAMETER: |
788 case Slot::LOCAL: | 682 case Slot::LOCAL: |
789 if (mode == Variable::CONST) { | 683 if (decl->mode() == Variable::CONST) { |
790 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | 684 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); |
791 __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); | 685 __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); |
792 } else if (function != NULL) { | 686 } else if (decl->fun() != NULL) { |
793 VisitForValue(function, kAccumulator); | 687 VisitForValue(decl->fun(), kAccumulator); |
794 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); | 688 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); |
795 } | 689 } |
796 break; | 690 break; |
797 | 691 |
798 case Slot::CONTEXT: | 692 case Slot::CONTEXT: |
799 // We bypass the general EmitSlotSearch because we know more about | 693 // We bypass the general EmitSlotSearch because we know more about |
800 // this specific context. | 694 // this specific context. |
801 | 695 |
802 // The variable in the decl always resides in the current context. | 696 // The variable in the decl always resides in the current context. |
803 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); | 697 ASSERT_EQ(0, scope()->ContextChainLength(var->scope())); |
804 if (FLAG_debug_code) { | 698 if (FLAG_debug_code) { |
805 // Check if we have the correct context pointer. | 699 // Check if we have the correct context pointer. |
806 __ movq(rbx, | 700 __ movq(rbx, |
807 CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); | 701 CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); |
808 __ cmpq(rbx, rsi); | 702 __ cmpq(rbx, rsi); |
809 __ Check(equal, "Unexpected declaration in current context."); | 703 __ Check(equal, "Unexpected declaration in current context."); |
810 } | 704 } |
811 if (mode == Variable::CONST) { | 705 if (decl->mode() == Variable::CONST) { |
812 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | 706 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); |
813 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | 707 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
814 kScratchRegister); | 708 kScratchRegister); |
815 // No write barrier since the hole value is in old space. | 709 // No write barrier since the hole value is in old space. |
816 } else if (function != NULL) { | 710 } else if (decl->fun() != NULL) { |
817 VisitForValue(function, kAccumulator); | 711 VisitForValue(decl->fun(), kAccumulator); |
818 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | 712 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), |
819 result_register()); | 713 result_register()); |
820 int offset = Context::SlotOffset(slot->index()); | 714 int offset = Context::SlotOffset(slot->index()); |
821 __ movq(rbx, rsi); | 715 __ movq(rbx, rsi); |
822 __ RecordWrite(rbx, offset, result_register(), rcx); | 716 __ RecordWrite(rbx, offset, result_register(), rcx); |
823 } | 717 } |
824 break; | 718 break; |
825 | 719 |
826 case Slot::LOOKUP: { | 720 case Slot::LOOKUP: { |
827 __ push(rsi); | 721 __ push(rsi); |
828 __ Push(variable->name()); | 722 __ Push(var->name()); |
829 // Declaration nodes are always introduced in one of two modes. | 723 // Declaration nodes are always introduced in one of two modes. |
830 ASSERT(mode == Variable::VAR || mode == Variable::CONST); | 724 ASSERT(decl->mode() == Variable::VAR || |
831 PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY; | 725 decl->mode() == Variable::CONST); |
| 726 PropertyAttributes attr = |
| 727 (decl->mode() == Variable::VAR) ? NONE : READ_ONLY; |
832 __ Push(Smi::FromInt(attr)); | 728 __ Push(Smi::FromInt(attr)); |
833 // Push initial value, if any. | 729 // Push initial value, if any. |
834 // Note: For variables we must not push an initial value (such as | 730 // Note: For variables we must not push an initial value (such as |
835 // 'undefined') because we may have a (legal) redeclaration and we | 731 // 'undefined') because we may have a (legal) redeclaration and we |
836 // must not destroy the current value. | 732 // must not destroy the current value. |
837 if (mode == Variable::CONST) { | 733 if (decl->mode() == Variable::CONST) { |
838 __ PushRoot(Heap::kTheHoleValueRootIndex); | 734 __ PushRoot(Heap::kTheHoleValueRootIndex); |
839 } else if (function != NULL) { | 735 } else if (decl->fun() != NULL) { |
840 VisitForValue(function, kStack); | 736 VisitForValue(decl->fun(), kStack); |
841 } else { | 737 } else { |
842 __ Push(Smi::FromInt(0)); // no initial value! | 738 __ Push(Smi::FromInt(0)); // no initial value! |
843 } | 739 } |
844 __ CallRuntime(Runtime::kDeclareContextSlot, 4); | 740 __ CallRuntime(Runtime::kDeclareContextSlot, 4); |
845 break; | 741 break; |
846 } | 742 } |
847 } | 743 } |
848 | 744 |
849 } else if (prop != NULL) { | 745 } else if (prop != NULL) { |
850 if (function != NULL || mode == Variable::CONST) { | 746 if (decl->fun() != NULL || decl->mode() == Variable::CONST) { |
851 // We are declaring a function or constant that rewrites to a | 747 // We are declaring a function or constant that rewrites to a |
852 // property. Use (keyed) IC to set the initial value. | 748 // property. Use (keyed) IC to set the initial value. |
853 VisitForValue(prop->obj(), kStack); | 749 VisitForValue(prop->obj(), kStack); |
854 VisitForValue(prop->key(), kStack); | 750 VisitForValue(prop->key(), kStack); |
855 | 751 |
856 if (function != NULL) { | 752 if (decl->fun() != NULL) { |
857 VisitForValue(function, kAccumulator); | 753 VisitForValue(decl->fun(), kAccumulator); |
858 } else { | 754 } else { |
859 __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); | 755 __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); |
860 } | 756 } |
861 | 757 |
862 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 758 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
863 __ call(ic, RelocInfo::CODE_TARGET); | 759 __ call(ic, RelocInfo::CODE_TARGET); |
864 // Absence of a test rax instruction following the call | 760 // Absence of a test rax instruction following the call |
865 // indicates that none of the load was inlined. | 761 // indicates that none of the load was inlined. |
866 __ nop(); | 762 __ nop(); |
867 | 763 |
868 // Value in rax is ignored (declarations are statements). Receiver | 764 // Value in rax is ignored (declarations are statements). Receiver |
869 // and key on stack are discarded. | 765 // and key on stack are discarded. |
870 __ Drop(2); | 766 __ Drop(2); |
871 } | 767 } |
872 } | 768 } |
873 } | 769 } |
874 | 770 |
875 | 771 |
876 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { | |
877 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); | |
878 } | |
879 | |
880 | |
881 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { | 772 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { |
882 // Call the runtime to declare the globals. | 773 // Call the runtime to declare the globals. |
883 __ push(rsi); // The context is the first argument. | 774 __ push(rsi); // The context is the first argument. |
884 __ Push(pairs); | 775 __ Push(pairs); |
885 __ Push(Smi::FromInt(is_eval() ? 1 : 0)); | 776 __ Push(Smi::FromInt(is_eval() ? 1 : 0)); |
886 __ CallRuntime(Runtime::kDeclareGlobals, 3); | 777 __ CallRuntime(Runtime::kDeclareGlobals, 3); |
887 // Return value is ignored. | 778 // Return value is ignored. |
888 } | 779 } |
889 | 780 |
890 | 781 |
891 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { | 782 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { |
892 Comment cmnt(masm_, "[ SwitchStatement"); | 783 UNREACHABLE(); |
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()); | |
956 } | 784 } |
957 | 785 |
958 | 786 |
959 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { | 787 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { |
960 Comment cmnt(masm_, "[ ForInStatement"); | 788 UNREACHABLE(); |
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(); | |
1094 } | 789 } |
1095 | 790 |
1096 | 791 |
1097 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { | 792 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { |
1098 // Use the fast case closure allocation code that allocates in new | 793 // Use the fast case closure allocation code that allocates in new |
1099 // space for nested functions that don't need literals cloning. | 794 // space for nested functions that don't need literals cloning. |
1100 if (scope()->is_function_scope() && info->num_literals() == 0) { | 795 if (scope()->is_function_scope() && info->num_literals() == 0) { |
1101 FastNewClosureStub stub; | 796 FastNewClosureStub stub; |
1102 __ Push(info); | 797 __ Push(info); |
1103 __ CallStub(&stub); | 798 __ CallStub(&stub); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1142 Comment cmnt(masm_, "Lookup slot"); | 837 Comment cmnt(masm_, "Lookup slot"); |
1143 __ push(rsi); // Context. | 838 __ push(rsi); // Context. |
1144 __ Push(var->name()); | 839 __ Push(var->name()); |
1145 __ CallRuntime(Runtime::kLoadContextSlot, 2); | 840 __ CallRuntime(Runtime::kLoadContextSlot, 2); |
1146 Apply(context, rax); | 841 Apply(context, rax); |
1147 | 842 |
1148 } else if (slot != NULL) { | 843 } else if (slot != NULL) { |
1149 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) | 844 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) |
1150 ? "Context slot" | 845 ? "Context slot" |
1151 : "Stack slot"); | 846 : "Stack slot"); |
1152 if (var->mode() == Variable::CONST) { | 847 Apply(context, slot); |
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 } | |
1166 | 848 |
1167 } else { | 849 } else { |
1168 Comment cmnt(masm_, "Rewritten parameter"); | 850 Comment cmnt(masm_, "Rewritten parameter"); |
1169 ASSERT_NOT_NULL(property); | 851 ASSERT_NOT_NULL(property); |
1170 // Rewritten parameter accesses are of the form "slot[literal]". | 852 // Rewritten parameter accesses are of the form "slot[literal]". |
1171 | 853 |
1172 // Assert that the object is in a slot. | 854 // Assert that the object is in a slot. |
1173 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); | 855 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); |
1174 ASSERT_NOT_NULL(object_var); | 856 ASSERT_NOT_NULL(object_var); |
1175 Slot* object_slot = object_var->slot(); | 857 Slot* object_slot = object_var->slot(); |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1291 if (result_saved) { | 973 if (result_saved) { |
1292 ApplyTOS(context_); | 974 ApplyTOS(context_); |
1293 } else { | 975 } else { |
1294 Apply(context_, rax); | 976 Apply(context_, rax); |
1295 } | 977 } |
1296 } | 978 } |
1297 | 979 |
1298 | 980 |
1299 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { | 981 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { |
1300 Comment cmnt(masm_, "[ ArrayLiteral"); | 982 Comment cmnt(masm_, "[ ArrayLiteral"); |
1301 | |
1302 ZoneList<Expression*>* subexprs = expr->values(); | |
1303 int length = subexprs->length(); | |
1304 | |
1305 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | 983 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); |
1306 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); | 984 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); |
1307 __ Push(Smi::FromInt(expr->literal_index())); | 985 __ Push(Smi::FromInt(expr->literal_index())); |
1308 __ Push(expr->constant_elements()); | 986 __ Push(expr->constant_elements()); |
1309 if (expr->depth() > 1) { | 987 if (expr->depth() > 1) { |
1310 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); | 988 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); |
1311 } else if (length > FastCloneShallowArrayStub::kMaximumLength) { | 989 } else { |
1312 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); | 990 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); |
1313 } else { | |
1314 FastCloneShallowArrayStub stub(length); | |
1315 __ CallStub(&stub); | |
1316 } | 991 } |
1317 | 992 |
1318 bool result_saved = false; // Is the result saved to the stack? | 993 bool result_saved = false; // Is the result saved to the stack? |
1319 | 994 |
1320 // Emit code to evaluate all the non-constant subexpressions and to store | 995 // Emit code to evaluate all the non-constant subexpressions and to store |
1321 // them into the newly cloned array. | 996 // them into the newly cloned array. |
1322 for (int i = 0; i < length; i++) { | 997 ZoneList<Expression*>* subexprs = expr->values(); |
| 998 for (int i = 0, len = subexprs->length(); i < len; i++) { |
1323 Expression* subexpr = subexprs->at(i); | 999 Expression* subexpr = subexprs->at(i); |
1324 // If the subexpression is a literal or a simple materialized literal it | 1000 // If the subexpression is a literal or a simple materialized literal it |
1325 // is already set in the cloned array. | 1001 // is already set in the cloned array. |
1326 if (subexpr->AsLiteral() != NULL || | 1002 if (subexpr->AsLiteral() != NULL || |
1327 CompileTimeValue::IsCompileTimeValue(subexpr)) { | 1003 CompileTimeValue::IsCompileTimeValue(subexpr)) { |
1328 continue; | 1004 continue; |
1329 } | 1005 } |
1330 | 1006 |
1331 if (!result_saved) { | 1007 if (!result_saved) { |
1332 __ push(rax); | 1008 __ push(rax); |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1474 Expression::Context context) { | 1150 Expression::Context context) { |
1475 __ push(result_register()); | 1151 __ push(result_register()); |
1476 GenericBinaryOpStub stub(op, | 1152 GenericBinaryOpStub stub(op, |
1477 NO_OVERWRITE, | 1153 NO_OVERWRITE, |
1478 NO_GENERIC_BINARY_FLAGS); | 1154 NO_GENERIC_BINARY_FLAGS); |
1479 __ CallStub(&stub); | 1155 __ CallStub(&stub); |
1480 Apply(context, rax); | 1156 Apply(context, rax); |
1481 } | 1157 } |
1482 | 1158 |
1483 | 1159 |
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 | |
1535 void FullCodeGenerator::EmitVariableAssignment(Variable* var, | 1160 void FullCodeGenerator::EmitVariableAssignment(Variable* var, |
1536 Token::Value op, | 1161 Token::Value op, |
1537 Expression::Context context) { | 1162 Expression::Context context) { |
1538 // Left-hand sides that rewrite to explicit property accesses do not reach | 1163 // Left-hand sides that rewrite to explicit property accesses do not reach |
1539 // here. | 1164 // here. |
1540 ASSERT(var != NULL); | 1165 ASSERT(var != NULL); |
1541 ASSERT(var->is_global() || var->slot() != NULL); | 1166 ASSERT(var->is_global() || var->slot() != NULL); |
1542 | 1167 |
1543 if (var->is_global()) { | 1168 if (var->is_global()) { |
1544 ASSERT(!var->is_this()); | 1169 ASSERT(!var->is_this()); |
(...skipping 10 matching lines...) Expand all Loading... |
1555 // Perform the assignment for non-const variables and for initialization | 1180 // Perform the assignment for non-const variables and for initialization |
1556 // of const variables. Const assignments are simply skipped. | 1181 // of const variables. Const assignments are simply skipped. |
1557 Label done; | 1182 Label done; |
1558 Slot* slot = var->slot(); | 1183 Slot* slot = var->slot(); |
1559 switch (slot->type()) { | 1184 switch (slot->type()) { |
1560 case Slot::PARAMETER: | 1185 case Slot::PARAMETER: |
1561 case Slot::LOCAL: | 1186 case Slot::LOCAL: |
1562 if (op == Token::INIT_CONST) { | 1187 if (op == Token::INIT_CONST) { |
1563 // Detect const reinitialization by checking for the hole value. | 1188 // Detect const reinitialization by checking for the hole value. |
1564 __ movq(rdx, Operand(rbp, SlotOffset(slot))); | 1189 __ movq(rdx, Operand(rbp, SlotOffset(slot))); |
1565 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); | 1190 __ Cmp(rdx, Factory::the_hole_value()); |
1566 __ j(not_equal, &done); | 1191 __ j(not_equal, &done); |
1567 } | 1192 } |
1568 // Perform the assignment. | 1193 // Perform the assignment. |
1569 __ movq(Operand(rbp, SlotOffset(slot)), rax); | 1194 __ movq(Operand(rbp, SlotOffset(slot)), rax); |
1570 break; | 1195 break; |
1571 | 1196 |
1572 case Slot::CONTEXT: { | 1197 case Slot::CONTEXT: { |
1573 MemOperand target = EmitSlotSearch(slot, rcx); | 1198 MemOperand target = EmitSlotSearch(slot, rcx); |
1574 if (op == Token::INIT_CONST) { | 1199 if (op == Token::INIT_CONST) { |
1575 // Detect const reinitialization by checking for the hole value. | 1200 // Detect const reinitialization by checking for the hole value. |
1576 __ movq(rdx, target); | 1201 __ movq(rdx, target); |
1577 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); | 1202 __ Cmp(rdx, Factory::the_hole_value()); |
1578 __ j(not_equal, &done); | 1203 __ j(not_equal, &done); |
1579 } | 1204 } |
1580 // Perform the assignment and issue the write barrier. | 1205 // Perform the assignment and issue the write barrier. |
1581 __ movq(target, rax); | 1206 __ movq(target, rax); |
1582 // The value of the assignment is in rax. RecordWrite clobbers its | 1207 // The value of the assignment is in rax. RecordWrite clobbers its |
1583 // register arguments. | 1208 // register arguments. |
1584 __ movq(rdx, rax); | 1209 __ movq(rdx, rax); |
1585 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | 1210 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; |
1586 __ RecordWrite(rcx, offset, rdx, rbx); | 1211 __ RecordWrite(rcx, offset, rdx, rbx); |
1587 break; | 1212 break; |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1730 | 1355 |
1731 void FullCodeGenerator::EmitCallWithStub(Call* expr) { | 1356 void FullCodeGenerator::EmitCallWithStub(Call* expr) { |
1732 // Code common for calls using the call stub. | 1357 // Code common for calls using the call stub. |
1733 ZoneList<Expression*>* args = expr->arguments(); | 1358 ZoneList<Expression*>* args = expr->arguments(); |
1734 int arg_count = args->length(); | 1359 int arg_count = args->length(); |
1735 for (int i = 0; i < arg_count; i++) { | 1360 for (int i = 0; i < arg_count; i++) { |
1736 VisitForValue(args->at(i), kStack); | 1361 VisitForValue(args->at(i), kStack); |
1737 } | 1362 } |
1738 // Record source position for debugger. | 1363 // Record source position for debugger. |
1739 SetSourcePosition(expr->position()); | 1364 SetSourcePosition(expr->position()); |
1740 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; | 1365 CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE); |
1741 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); | |
1742 __ CallStub(&stub); | 1366 __ CallStub(&stub); |
1743 // Restore context register. | 1367 // Restore context register. |
1744 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | 1368 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
1745 // Discard the function left on TOS. | 1369 // Discard the function left on TOS. |
1746 DropAndApply(1, context_, rax); | 1370 DropAndApply(1, context_, rax); |
1747 } | 1371 } |
1748 | 1372 |
1749 | 1373 |
1750 void FullCodeGenerator::VisitCall(Call* expr) { | 1374 void FullCodeGenerator::VisitCall(Call* expr) { |
1751 Comment cmnt(masm_, "[ Call"); | 1375 Comment cmnt(masm_, "[ Call"); |
1752 Expression* fun = expr->expression(); | 1376 Expression* fun = expr->expression(); |
1753 Variable* var = fun->AsVariableProxy()->AsVariable(); | 1377 Variable* var = fun->AsVariableProxy()->AsVariable(); |
1754 | 1378 |
1755 if (var != NULL && var->is_possibly_eval()) { | 1379 if (var != NULL && var->is_possibly_eval()) { |
1756 // In a call to eval, we first call %ResolvePossiblyDirectEval to | 1380 // Call to the identifier 'eval'. |
1757 // resolve the function we need to call and the receiver of the | 1381 UNREACHABLE(); |
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); | |
1797 } else if (var != NULL && !var->is_this() && var->is_global()) { | 1382 } else if (var != NULL && !var->is_this() && var->is_global()) { |
1798 // Call to a global variable. | 1383 // Call to a global variable. |
1799 // Push global object as receiver for the call IC lookup. | 1384 // Push global object as receiver for the call IC lookup. |
1800 __ push(CodeGenerator::GlobalObject()); | 1385 __ push(CodeGenerator::GlobalObject()); |
1801 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); | 1386 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); |
1802 } else if (var != NULL && var->slot() != NULL && | 1387 } else if (var != NULL && var->slot() != NULL && |
1803 var->slot()->type() == Slot::LOOKUP) { | 1388 var->slot()->type() == Slot::LOOKUP) { |
1804 // Call to a lookup slot (dynamically introduced variable). Call | 1389 // Call to a lookup slot. |
1805 // the runtime to find the function to call (returned in rax) and | 1390 UNREACHABLE(); |
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); | |
1813 } else if (fun->AsProperty() != NULL) { | 1391 } else if (fun->AsProperty() != NULL) { |
1814 // Call to an object property. | 1392 // Call to an object property. |
1815 Property* prop = fun->AsProperty(); | 1393 Property* prop = fun->AsProperty(); |
1816 Literal* key = prop->key()->AsLiteral(); | 1394 Literal* key = prop->key()->AsLiteral(); |
1817 if (key != NULL && key->handle()->IsSymbol()) { | 1395 if (key != NULL && key->handle()->IsSymbol()) { |
1818 // Call to a named property, use call IC. | 1396 // Call to a named property, use call IC. |
1819 VisitForValue(prop->obj(), kStack); | 1397 VisitForValue(prop->obj(), kStack); |
1820 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); | 1398 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); |
1821 } else { | 1399 } else { |
1822 // Call to a keyed property, use keyed load IC followed by function | 1400 // 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... |
1893 __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); | 1471 __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); |
1894 | 1472 |
1895 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); | 1473 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); |
1896 __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); | 1474 __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); |
1897 | 1475 |
1898 // Replace function on TOS with result in rax, or pop it. | 1476 // Replace function on TOS with result in rax, or pop it. |
1899 DropAndApply(1, context_, rax); | 1477 DropAndApply(1, context_, rax); |
1900 } | 1478 } |
1901 | 1479 |
1902 | 1480 |
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 | |
2600 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { | 1481 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 | |
2608 Comment cmnt(masm_, "[ CallRuntime"); | 1482 Comment cmnt(masm_, "[ CallRuntime"); |
2609 ZoneList<Expression*>* args = expr->arguments(); | 1483 ZoneList<Expression*>* args = expr->arguments(); |
2610 | 1484 |
2611 if (expr->is_jsruntime()) { | 1485 if (expr->is_jsruntime()) { |
2612 // Prepare for calling JS runtime function. | 1486 // Prepare for calling JS runtime function. |
2613 __ movq(rax, CodeGenerator::GlobalObject()); | 1487 __ movq(rax, CodeGenerator::GlobalObject()); |
2614 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); | 1488 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); |
2615 } | 1489 } |
2616 | 1490 |
2617 // Push the arguments ("left-to-right"). | 1491 // Push the arguments ("left-to-right"). |
(...skipping 12 matching lines...) Expand all Loading... |
2630 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | 1504 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
2631 } else { | 1505 } else { |
2632 __ CallRuntime(expr->function(), arg_count); | 1506 __ CallRuntime(expr->function(), arg_count); |
2633 } | 1507 } |
2634 Apply(context_, rax); | 1508 Apply(context_, rax); |
2635 } | 1509 } |
2636 | 1510 |
2637 | 1511 |
2638 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { | 1512 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { |
2639 switch (expr->op()) { | 1513 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 | |
2680 case Token::VOID: { | 1514 case Token::VOID: { |
2681 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); | 1515 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); |
2682 VisitForEffect(expr->expression()); | 1516 VisitForEffect(expr->expression()); |
2683 switch (context_) { | 1517 switch (context_) { |
2684 case Expression::kUninitialized: | 1518 case Expression::kUninitialized: |
2685 UNREACHABLE(); | 1519 UNREACHABLE(); |
2686 break; | 1520 break; |
2687 case Expression::kEffect: | 1521 case Expression::kEffect: |
2688 break; | 1522 break; |
2689 case Expression::kValue: | 1523 case Expression::kValue: |
(...skipping 20 matching lines...) Expand all Loading... |
2710 case Expression::kTest: | 1544 case Expression::kTest: |
2711 case Expression::kValueTest: | 1545 case Expression::kValueTest: |
2712 __ jmp(false_label_); | 1546 __ jmp(false_label_); |
2713 break; | 1547 break; |
2714 } | 1548 } |
2715 break; | 1549 break; |
2716 } | 1550 } |
2717 | 1551 |
2718 case Token::NOT: { | 1552 case Token::NOT: { |
2719 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); | 1553 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); |
2720 Label materialize_true, materialize_false; | 1554 Label materialize_true, materialize_false, done; |
2721 Label* if_true = NULL; | 1555 // Initially assume a pure test context. Notice that the labels are |
2722 Label* if_false = NULL; | 1556 // swapped. |
2723 | 1557 Label* if_true = false_label_; |
2724 // Notice that the labels are swapped. | 1558 Label* if_false = true_label_; |
2725 PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true); | 1559 switch (context_) { |
2726 | 1560 case Expression::kUninitialized: |
| 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 } |
2727 VisitForControl(expr->expression(), if_true, if_false); | 1580 VisitForControl(expr->expression(), if_true, if_false); |
2728 | |
2729 Apply(context_, if_false, if_true); // Labels swapped. | 1581 Apply(context_, if_false, if_true); // Labels swapped. |
2730 break; | 1582 break; |
2731 } | 1583 } |
2732 | 1584 |
2733 case Token::TYPEOF: { | 1585 case Token::TYPEOF: { |
2734 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); | 1586 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); |
2735 VariableProxy* proxy = expr->expression()->AsVariableProxy(); | 1587 VariableProxy* proxy = expr->expression()->AsVariableProxy(); |
2736 if (proxy != NULL && | 1588 if (proxy != NULL && |
2737 !proxy->var()->is_this() && | 1589 !proxy->var()->is_this() && |
2738 proxy->var()->is_global()) { | 1590 proxy->var()->is_global()) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2814 | 1666 |
2815 default: | 1667 default: |
2816 UNREACHABLE(); | 1668 UNREACHABLE(); |
2817 } | 1669 } |
2818 } | 1670 } |
2819 | 1671 |
2820 | 1672 |
2821 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { | 1673 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { |
2822 Comment cmnt(masm_, "[ CountOperation"); | 1674 Comment cmnt(masm_, "[ CountOperation"); |
2823 | 1675 |
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 | |
2831 // Expression can only be a property, a global or a (parameter or local) | 1676 // Expression can only be a property, a global or a (parameter or local) |
2832 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. | 1677 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
2833 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; | 1678 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
2834 LhsKind assign_type = VARIABLE; | 1679 LhsKind assign_type = VARIABLE; |
2835 Property* prop = expr->expression()->AsProperty(); | 1680 Property* prop = expr->expression()->AsProperty(); |
2836 // In case of a property we use the uninitialized expression context | 1681 // In case of a property we use the uninitialized expression context |
2837 // of the key to detect a named property. | 1682 // of the key to detect a named property. |
2838 if (prop != NULL) { | 1683 if (prop != NULL) { |
2839 assign_type = | 1684 assign_type = |
2840 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; | 1685 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; |
2841 } | 1686 } |
2842 | 1687 |
2843 // Evaluate expression and get value. | 1688 // Evaluate expression and get value. |
2844 if (assign_type == VARIABLE) { | 1689 if (assign_type == VARIABLE) { |
2845 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); | 1690 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); |
2846 Location saved_location = location_; | 1691 Location saved_location = location_; |
2847 location_ = kAccumulator; | 1692 location_ = kAccumulator; |
2848 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), | 1693 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), |
2849 Expression::kValue); | 1694 Expression::kValue); |
2850 location_ = saved_location; | 1695 location_ = saved_location; |
2851 } else { | 1696 } else { |
2852 // Reserve space for result of postfix operation. | 1697 // Reserve space for result of postfix operation. |
2853 if (expr->is_postfix() && context_ != Expression::kEffect) { | 1698 if (expr->is_postfix() && context_ != Expression::kEffect) { |
2854 __ Push(Smi::FromInt(0)); | 1699 __ Push(Smi::FromInt(0)); |
2855 } | 1700 } |
2856 VisitForValue(prop->obj(), kStack); | 1701 VisitForValue(prop->obj(), kStack); |
2857 if (assign_type == NAMED_PROPERTY) { | 1702 if (assign_type == NAMED_PROPERTY) { |
2858 EmitNamedPropertyLoad(prop); | 1703 EmitNamedPropertyLoad(prop); |
2859 } else { | 1704 } else { |
2860 VisitForValue(prop->key(), kStack); | 1705 VisitForValue(prop->key(), kStack); |
2861 EmitKeyedPropertyLoad(prop); | 1706 EmitKeyedPropertyLoad(prop); |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3013 VisitForValue(expr->right(), kAccumulator); | 1858 VisitForValue(expr->right(), kAccumulator); |
3014 EmitBinaryOp(expr->op(), context_); | 1859 EmitBinaryOp(expr->op(), context_); |
3015 break; | 1860 break; |
3016 | 1861 |
3017 default: | 1862 default: |
3018 UNREACHABLE(); | 1863 UNREACHABLE(); |
3019 } | 1864 } |
3020 } | 1865 } |
3021 | 1866 |
3022 | 1867 |
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 | |
3048 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { | 1868 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { |
3049 Comment cmnt(masm_, "[ CompareOperation"); | 1869 Comment cmnt(masm_, "[ CompareOperation"); |
3050 | 1870 |
3051 // Always perform the comparison for its control flow. Pack the result | 1871 // Always perform the comparison for its control flow. Pack the result |
3052 // into the expression's context after the comparison is performed. | 1872 // into the expression's context after the comparison is performed. |
3053 Label materialize_true, materialize_false; | 1873 Label materialize_true, materialize_false, done; |
3054 Label* if_true = NULL; | 1874 // Initially assume we are in a test context. |
3055 Label* if_false = NULL; | 1875 Label* if_true = true_label_; |
3056 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); | 1876 Label* if_false = false_label_; |
| 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 } |
3057 | 1898 |
3058 VisitForValue(expr->left(), kStack); | 1899 VisitForValue(expr->left(), kStack); |
3059 switch (expr->op()) { | 1900 switch (expr->op()) { |
3060 case Token::IN: | 1901 case Token::IN: |
3061 VisitForValue(expr->right(), kStack); | 1902 VisitForValue(expr->right(), kStack); |
3062 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); | 1903 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); |
3063 __ CompareRoot(rax, Heap::kTrueValueRootIndex); | 1904 __ CompareRoot(rax, Heap::kTrueValueRootIndex); |
3064 __ j(equal, if_true); | 1905 __ j(equal, if_true); |
3065 __ jmp(if_false); | 1906 __ jmp(if_false); |
3066 break; | 1907 break; |
3067 | 1908 |
3068 case Token::INSTANCEOF: { | 1909 case Token::INSTANCEOF: { |
3069 VisitForValue(expr->right(), kStack); | 1910 VisitForValue(expr->right(), kStack); |
3070 InstanceofStub stub; | 1911 InstanceofStub stub; |
3071 __ CallStub(&stub); | 1912 __ CallStub(&stub); |
3072 __ testq(rax, rax); | 1913 __ testq(rax, rax); |
3073 __ j(zero, if_true); // The stub returns 0 for true. | 1914 __ j(zero, if_true); // The stub returns 0 for true. |
3074 __ jmp(if_false); | 1915 __ jmp(if_false); |
3075 break; | 1916 break; |
3076 } | 1917 } |
3077 | 1918 |
3078 default: { | 1919 default: { |
3079 VisitForValue(expr->right(), kAccumulator); | 1920 VisitForValue(expr->right(), kAccumulator); |
3080 Condition cc = no_condition; | 1921 Condition cc = no_condition; |
3081 bool strict = false; | 1922 bool strict = false; |
3082 switch (expr->op()) { | 1923 switch (expr->op()) { |
3083 case Token::EQ_STRICT: | 1924 case Token::EQ_STRICT: |
3084 strict = true; | 1925 strict = true; |
3085 // Fall through. | 1926 // Fall through. |
3086 case Token::EQ: { | 1927 case Token::EQ: |
3087 cc = equal; | 1928 cc = equal; |
3088 __ pop(rdx); | 1929 __ 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 } | |
3102 break; | 1930 break; |
3103 } | |
3104 case Token::LT: | 1931 case Token::LT: |
3105 cc = less; | 1932 cc = less; |
3106 __ pop(rdx); | 1933 __ pop(rdx); |
3107 break; | 1934 break; |
3108 case Token::GT: | 1935 case Token::GT: |
3109 // Reverse left and right sizes to obtain ECMA-262 conversion order. | 1936 // Reverse left and right sizes to obtain ECMA-262 conversion order. |
3110 cc = less; | 1937 cc = less; |
3111 __ movq(rdx, result_register()); | 1938 __ movq(rdx, result_register()); |
3112 __ pop(rax); | 1939 __ pop(rax); |
3113 break; | 1940 break; |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3206 __ ret(0); | 2033 __ ret(0); |
3207 } | 2034 } |
3208 | 2035 |
3209 | 2036 |
3210 #undef __ | 2037 #undef __ |
3211 | 2038 |
3212 | 2039 |
3213 } } // namespace v8::internal | 2040 } } // namespace v8::internal |
3214 | 2041 |
3215 #endif // V8_TARGET_ARCH_X64 | 2042 #endif // V8_TARGET_ARCH_X64 |
OLD | NEW |