| OLD | NEW |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 208 Operand(StandardFrameConstants::kCallerSPOffset + offset)); | 208 Operand(StandardFrameConstants::kCallerSPOffset + offset)); |
| 209 __ mov(r1, Operand(Smi::FromInt(scope()->num_parameters()))); | 209 __ mov(r1, Operand(Smi::FromInt(scope()->num_parameters()))); |
| 210 __ Push(r3, r2, r1); | 210 __ Push(r3, r2, r1); |
| 211 | 211 |
| 212 // Arguments to ArgumentsAccessStub: | 212 // Arguments to ArgumentsAccessStub: |
| 213 // function, receiver address, parameter count. | 213 // function, receiver address, parameter count. |
| 214 // The stub will rewrite receiever and parameter count if the previous | 214 // The stub will rewrite receiever and parameter count if the previous |
| 215 // stack frame was an arguments adapter frame. | 215 // stack frame was an arguments adapter frame. |
| 216 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); | 216 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); |
| 217 __ CallStub(&stub); | 217 __ CallStub(&stub); |
| 218 // Duplicate the value; move-to-slot operation might clobber registers. | 218 |
| 219 __ mov(r3, r0); | 219 Variable* arguments_shadow = scope()->arguments_shadow(); |
| 220 if (arguments_shadow != NULL) { |
| 221 // Duplicate the value; move-to-slot operation might clobber registers. |
| 222 __ mov(r3, r0); |
| 223 Move(arguments_shadow->AsSlot(), r3, r1, r2); |
| 224 } |
| 220 Move(arguments->AsSlot(), r0, r1, r2); | 225 Move(arguments->AsSlot(), r0, r1, r2); |
| 221 Slot* dot_arguments_slot = scope()->arguments_shadow()->AsSlot(); | |
| 222 Move(dot_arguments_slot, r3, r1, r2); | |
| 223 } | |
| 224 | |
| 225 { Comment cmnt(masm_, "[ Declarations"); | |
| 226 // For named function expressions, declare the function name as a | |
| 227 // constant. | |
| 228 if (scope()->is_function_scope() && scope()->function() != NULL) { | |
| 229 EmitDeclaration(scope()->function(), Variable::CONST, NULL); | |
| 230 } | |
| 231 // Visit all the explicit declarations unless there is an illegal | |
| 232 // redeclaration. | |
| 233 if (scope()->HasIllegalRedeclaration()) { | |
| 234 scope()->VisitIllegalRedeclaration(this); | |
| 235 } else { | |
| 236 VisitDeclarations(scope()->declarations()); | |
| 237 } | |
| 238 } | 226 } |
| 239 | 227 |
| 240 if (FLAG_trace) { | 228 if (FLAG_trace) { |
| 241 __ CallRuntime(Runtime::kTraceEnter, 0); | 229 __ CallRuntime(Runtime::kTraceEnter, 0); |
| 242 } | 230 } |
| 243 | 231 |
| 244 // Check the stack for overflow or break request. | 232 // Visit the declarations and body unless there is an illegal |
| 245 { Comment cmnt(masm_, "[ Stack check"); | 233 // redeclaration. |
| 246 PrepareForBailout(info->function(), NO_REGISTERS); | 234 if (scope()->HasIllegalRedeclaration()) { |
| 247 Label ok; | 235 Comment cmnt(masm_, "[ Declarations"); |
| 248 __ LoadRoot(ip, Heap::kStackLimitRootIndex); | 236 scope()->VisitIllegalRedeclaration(this); |
| 249 __ cmp(sp, Operand(ip)); | 237 |
| 250 __ b(hs, &ok); | 238 } else { |
| 251 StackCheckStub stub; | 239 { Comment cmnt(masm_, "[ Declarations"); |
| 252 __ CallStub(&stub); | 240 // For named function expressions, declare the function name as a |
| 253 __ bind(&ok); | 241 // constant. |
| 242 if (scope()->is_function_scope() && scope()->function() != NULL) { |
| 243 EmitDeclaration(scope()->function(), Variable::CONST, NULL); |
| 244 } |
| 245 VisitDeclarations(scope()->declarations()); |
| 246 } |
| 247 |
| 248 { Comment cmnt(masm_, "[ Stack check"); |
| 249 PrepareForBailout(info->function(), NO_REGISTERS); |
| 250 Label ok; |
| 251 __ LoadRoot(ip, Heap::kStackLimitRootIndex); |
| 252 __ cmp(sp, Operand(ip)); |
| 253 __ b(hs, &ok); |
| 254 StackCheckStub stub; |
| 255 __ CallStub(&stub); |
| 256 __ bind(&ok); |
| 257 } |
| 258 |
| 259 { Comment cmnt(masm_, "[ Body"); |
| 260 ASSERT(loop_depth() == 0); |
| 261 VisitStatements(function()->body()); |
| 262 ASSERT(loop_depth() == 0); |
| 263 } |
| 254 } | 264 } |
| 255 | 265 |
| 256 { Comment cmnt(masm_, "[ Body"); | 266 // Always emit a 'return undefined' in case control fell off the end of |
| 257 ASSERT(loop_depth() == 0); | 267 // the body. |
| 258 VisitStatements(function()->body()); | |
| 259 ASSERT(loop_depth() == 0); | |
| 260 } | |
| 261 | |
| 262 { Comment cmnt(masm_, "[ return <undefined>;"); | 268 { Comment cmnt(masm_, "[ return <undefined>;"); |
| 263 // Emit a 'return undefined' in case control fell off the end of the | |
| 264 // body. | |
| 265 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); | 269 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
| 266 } | 270 } |
| 267 EmitReturnSequence(); | 271 EmitReturnSequence(); |
| 268 | 272 |
| 269 // Force emit the constant pool, so it doesn't get emitted in the middle | 273 // Force emit the constant pool, so it doesn't get emitted in the middle |
| 270 // of the stack check table. | 274 // of the stack check table. |
| 271 masm()->CheckConstPool(true, false); | 275 masm()->CheckConstPool(true, false); |
| 272 } | 276 } |
| 273 | 277 |
| 274 | 278 |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 #ifdef DEBUG | 338 #ifdef DEBUG |
| 335 // Check that the size of the code used for returning is large enough | 339 // Check that the size of the code used for returning is large enough |
| 336 // for the debugger's requirements. | 340 // for the debugger's requirements. |
| 337 ASSERT(Assembler::kJSReturnSequenceInstructions <= | 341 ASSERT(Assembler::kJSReturnSequenceInstructions <= |
| 338 masm_->InstructionsGeneratedSince(&check_exit_codesize)); | 342 masm_->InstructionsGeneratedSince(&check_exit_codesize)); |
| 339 #endif | 343 #endif |
| 340 } | 344 } |
| 341 } | 345 } |
| 342 | 346 |
| 343 | 347 |
| 344 FullCodeGenerator::ConstantOperand FullCodeGenerator::GetConstantOperand( | |
| 345 Token::Value op, Expression* left, Expression* right) { | |
| 346 ASSERT(ShouldInlineSmiCase(op)); | |
| 347 return kNoConstants; | |
| 348 } | |
| 349 | |
| 350 | |
| 351 void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { | 348 void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { |
| 352 } | 349 } |
| 353 | 350 |
| 354 | 351 |
| 355 void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { | 352 void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { |
| 356 codegen()->Move(result_register(), slot); | 353 codegen()->Move(result_register(), slot); |
| 357 } | 354 } |
| 358 | 355 |
| 359 | 356 |
| 360 void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { | 357 void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 if (true_label_ != fall_through_) __ b(true_label_); | 556 if (true_label_ != fall_through_) __ b(true_label_); |
| 560 } else { | 557 } else { |
| 561 if (false_label_ != fall_through_) __ b(false_label_); | 558 if (false_label_ != fall_through_) __ b(false_label_); |
| 562 } | 559 } |
| 563 } | 560 } |
| 564 | 561 |
| 565 | 562 |
| 566 void FullCodeGenerator::DoTest(Label* if_true, | 563 void FullCodeGenerator::DoTest(Label* if_true, |
| 567 Label* if_false, | 564 Label* if_false, |
| 568 Label* fall_through) { | 565 Label* fall_through) { |
| 569 // Call the runtime to find the boolean value of the source and then | 566 if (CpuFeatures::IsSupported(VFP3)) { |
| 570 // translate it into control flow to the pair of labels. | 567 CpuFeatures::Scope scope(VFP3); |
| 571 __ push(result_register()); | 568 // Emit the inlined tests assumed by the stub. |
| 572 __ CallRuntime(Runtime::kToBool, 1); | 569 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
| 573 __ LoadRoot(ip, Heap::kTrueValueRootIndex); | 570 __ cmp(result_register(), ip); |
| 574 __ cmp(r0, ip); | 571 __ b(eq, if_false); |
| 575 Split(eq, if_true, if_false, fall_through); | 572 __ LoadRoot(ip, Heap::kTrueValueRootIndex); |
| 573 __ cmp(result_register(), ip); |
| 574 __ b(eq, if_true); |
| 575 __ LoadRoot(ip, Heap::kFalseValueRootIndex); |
| 576 __ cmp(result_register(), ip); |
| 577 __ b(eq, if_false); |
| 578 STATIC_ASSERT(kSmiTag == 0); |
| 579 __ tst(result_register(), result_register()); |
| 580 __ b(eq, if_false); |
| 581 __ JumpIfSmi(result_register(), if_true); |
| 582 |
| 583 // Call the ToBoolean stub for all other cases. |
| 584 ToBooleanStub stub(result_register()); |
| 585 __ CallStub(&stub); |
| 586 __ tst(result_register(), result_register()); |
| 587 } else { |
| 588 // Call the runtime to find the boolean value of the source and then |
| 589 // translate it into control flow to the pair of labels. |
| 590 __ push(result_register()); |
| 591 __ CallRuntime(Runtime::kToBool, 1); |
| 592 __ LoadRoot(ip, Heap::kFalseValueRootIndex); |
| 593 __ cmp(r0, ip); |
| 594 } |
| 595 |
| 596 // The stub returns nonzero for true. |
| 597 Split(ne, if_true, if_false, fall_through); |
| 576 } | 598 } |
| 577 | 599 |
| 578 | 600 |
| 579 void FullCodeGenerator::Split(Condition cond, | 601 void FullCodeGenerator::Split(Condition cond, |
| 580 Label* if_true, | 602 Label* if_true, |
| 581 Label* if_false, | 603 Label* if_false, |
| 582 Label* fall_through) { | 604 Label* fall_through) { |
| 583 if (if_false == fall_through) { | 605 if (if_false == fall_through) { |
| 584 __ b(cond, if_true); | 606 __ b(cond, if_true); |
| 585 } else if (if_true == fall_through) { | 607 } else if (if_true == fall_through) { |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 683 } else if (function != NULL) { | 705 } else if (function != NULL) { |
| 684 VisitForAccumulatorValue(function); | 706 VisitForAccumulatorValue(function); |
| 685 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); | 707 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
| 686 } | 708 } |
| 687 break; | 709 break; |
| 688 | 710 |
| 689 case Slot::CONTEXT: | 711 case Slot::CONTEXT: |
| 690 // We bypass the general EmitSlotSearch because we know more about | 712 // We bypass the general EmitSlotSearch because we know more about |
| 691 // this specific context. | 713 // this specific context. |
| 692 | 714 |
| 693 // The variable in the decl always resides in the current context. | 715 // The variable in the decl always resides in the current function |
| 716 // context. |
| 694 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); | 717 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); |
| 695 if (FLAG_debug_code) { | 718 if (FLAG_debug_code) { |
| 696 // Check if we have the correct context pointer. | 719 // Check that we're not inside a 'with'. |
| 697 __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); | 720 __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); |
| 698 __ cmp(r1, cp); | 721 __ cmp(r1, cp); |
| 699 __ Check(eq, "Unexpected declaration in current context."); | 722 __ Check(eq, "Unexpected declaration in current context."); |
| 700 } | 723 } |
| 701 if (mode == Variable::CONST) { | 724 if (mode == Variable::CONST) { |
| 702 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | 725 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
| 703 __ str(ip, ContextOperand(cp, slot->index())); | 726 __ str(ip, ContextOperand(cp, slot->index())); |
| 704 // No write barrier since the_hole_value is in old space. | 727 // No write barrier since the_hole_value is in old space. |
| 705 } else if (function != NULL) { | 728 } else if (function != NULL) { |
| 706 VisitForAccumulatorValue(function); | 729 VisitForAccumulatorValue(function); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 758 VisitForAccumulatorValue(function); | 781 VisitForAccumulatorValue(function); |
| 759 __ pop(r2); | 782 __ pop(r2); |
| 760 } else { | 783 } else { |
| 761 __ mov(r2, r0); | 784 __ mov(r2, r0); |
| 762 __ LoadRoot(r0, Heap::kTheHoleValueRootIndex); | 785 __ LoadRoot(r0, Heap::kTheHoleValueRootIndex); |
| 763 } | 786 } |
| 764 ASSERT(prop->key()->AsLiteral() != NULL && | 787 ASSERT(prop->key()->AsLiteral() != NULL && |
| 765 prop->key()->AsLiteral()->handle()->IsSmi()); | 788 prop->key()->AsLiteral()->handle()->IsSmi()); |
| 766 __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); | 789 __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); |
| 767 | 790 |
| 768 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 791 Handle<Code> ic(Builtins::builtin( |
| 792 is_strict_mode() ? Builtins::KeyedStoreIC_Initialize_Strict |
| 793 : Builtins::KeyedStoreIC_Initialize)); |
| 769 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 794 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 770 // Value in r0 is ignored (declarations are statements). | 795 // Value in r0 is ignored (declarations are statements). |
| 771 } | 796 } |
| 772 } | 797 } |
| 773 } | 798 } |
| 774 | 799 |
| 775 | 800 |
| 776 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { | 801 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { |
| 777 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); | 802 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); |
| 778 } | 803 } |
| 779 | 804 |
| 780 | 805 |
| 781 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { | 806 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { |
| 782 // Call the runtime to declare the globals. | 807 // Call the runtime to declare the globals. |
| 783 // The context is the first argument. | 808 // The context is the first argument. |
| 784 __ mov(r1, Operand(pairs)); | 809 __ mov(r2, Operand(pairs)); |
| 785 __ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0))); | 810 __ mov(r1, Operand(Smi::FromInt(is_eval() ? 1 : 0))); |
| 786 __ Push(cp, r1, r0); | 811 __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); |
| 787 __ CallRuntime(Runtime::kDeclareGlobals, 3); | 812 __ Push(cp, r2, r1, r0); |
| 813 __ CallRuntime(Runtime::kDeclareGlobals, 4); |
| 788 // Return value is ignored. | 814 // Return value is ignored. |
| 789 } | 815 } |
| 790 | 816 |
| 791 | 817 |
| 792 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { | 818 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { |
| 793 Comment cmnt(masm_, "[ SwitchStatement"); | 819 Comment cmnt(masm_, "[ SwitchStatement"); |
| 794 Breakable nested_statement(this, stmt); | 820 Breakable nested_statement(this, stmt); |
| 795 SetStatementPosition(stmt); | 821 SetStatementPosition(stmt); |
| 822 |
| 796 // Keep the switch value on the stack until a case matches. | 823 // Keep the switch value on the stack until a case matches. |
| 797 VisitForStackValue(stmt->tag()); | 824 VisitForStackValue(stmt->tag()); |
| 798 | |
| 799 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); | 825 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); |
| 800 | 826 |
| 801 ZoneList<CaseClause*>* clauses = stmt->cases(); | 827 ZoneList<CaseClause*>* clauses = stmt->cases(); |
| 802 CaseClause* default_clause = NULL; // Can occur anywhere in the list. | 828 CaseClause* default_clause = NULL; // Can occur anywhere in the list. |
| 803 | 829 |
| 804 Label next_test; // Recycled for each test. | 830 Label next_test; // Recycled for each test. |
| 805 // Compile all the tests with branches to their bodies. | 831 // Compile all the tests with branches to their bodies. |
| 806 for (int i = 0; i < clauses->length(); i++) { | 832 for (int i = 0; i < clauses->length(); i++) { |
| 807 CaseClause* clause = clauses->at(i); | 833 CaseClause* clause = clauses->at(i); |
| 808 clause->body_target()->entry_label()->Unuse(); | 834 clause->body_target()->entry_label()->Unuse(); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 854 __ b(nested_statement.break_target()); | 880 __ b(nested_statement.break_target()); |
| 855 } else { | 881 } else { |
| 856 __ b(default_clause->body_target()->entry_label()); | 882 __ b(default_clause->body_target()->entry_label()); |
| 857 } | 883 } |
| 858 | 884 |
| 859 // Compile all the case bodies. | 885 // Compile all the case bodies. |
| 860 for (int i = 0; i < clauses->length(); i++) { | 886 for (int i = 0; i < clauses->length(); i++) { |
| 861 Comment cmnt(masm_, "[ Case body"); | 887 Comment cmnt(masm_, "[ Case body"); |
| 862 CaseClause* clause = clauses->at(i); | 888 CaseClause* clause = clauses->at(i); |
| 863 __ bind(clause->body_target()->entry_label()); | 889 __ bind(clause->body_target()->entry_label()); |
| 890 PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS); |
| 864 VisitStatements(clause->statements()); | 891 VisitStatements(clause->statements()); |
| 865 } | 892 } |
| 866 | 893 |
| 867 __ bind(nested_statement.break_target()); | 894 __ bind(nested_statement.break_target()); |
| 868 PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); | 895 PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); |
| 869 } | 896 } |
| 870 | 897 |
| 871 | 898 |
| 872 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { | 899 void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { |
| 873 Comment cmnt(masm_, "[ ForInStatement"); | 900 Comment cmnt(masm_, "[ ForInStatement"); |
| 874 SetStatementPosition(stmt); | 901 SetStatementPosition(stmt); |
| 875 | 902 |
| 876 Label loop, exit; | 903 Label loop, exit; |
| 877 ForIn loop_statement(this, stmt); | 904 ForIn loop_statement(this, stmt); |
| 878 increment_loop_depth(); | 905 increment_loop_depth(); |
| 879 | 906 |
| 880 // Get the object to enumerate over. Both SpiderMonkey and JSC | 907 // Get the object to enumerate over. Both SpiderMonkey and JSC |
| 881 // ignore null and undefined in contrast to the specification; see | 908 // ignore null and undefined in contrast to the specification; see |
| 882 // ECMA-262 section 12.6.4. | 909 // ECMA-262 section 12.6.4. |
| 883 VisitForAccumulatorValue(stmt->enumerable()); | 910 VisitForAccumulatorValue(stmt->enumerable()); |
| 884 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); | 911 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
| 885 __ cmp(r0, ip); | 912 __ cmp(r0, ip); |
| 886 __ b(eq, &exit); | 913 __ b(eq, &exit); |
| 887 __ LoadRoot(ip, Heap::kNullValueRootIndex); | 914 Register null_value = r5; |
| 888 __ cmp(r0, ip); | 915 __ LoadRoot(null_value, Heap::kNullValueRootIndex); |
| 916 __ cmp(r0, null_value); |
| 889 __ b(eq, &exit); | 917 __ b(eq, &exit); |
| 890 | 918 |
| 891 // Convert the object to a JS object. | 919 // Convert the object to a JS object. |
| 892 Label convert, done_convert; | 920 Label convert, done_convert; |
| 893 __ JumpIfSmi(r0, &convert); | 921 __ JumpIfSmi(r0, &convert); |
| 894 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE); | 922 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE); |
| 895 __ b(hs, &done_convert); | 923 __ b(hs, &done_convert); |
| 896 __ bind(&convert); | 924 __ bind(&convert); |
| 897 __ push(r0); | 925 __ push(r0); |
| 898 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS); | 926 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS); |
| 899 __ bind(&done_convert); | 927 __ bind(&done_convert); |
| 900 __ push(r0); | 928 __ push(r0); |
| 901 | 929 |
| 902 // BUG(867): Check cache validity in generated code. This is a fast | 930 // Check cache validity in generated code. This is a fast case for |
| 903 // case for the JSObject::IsSimpleEnum cache validity checks. If we | 931 // the JSObject::IsSimpleEnum cache validity checks. If we cannot |
| 904 // cannot guarantee cache validity, call the runtime system to check | 932 // guarantee cache validity, call the runtime system to check cache |
| 905 // cache validity or get the property names in a fixed array. | 933 // validity or get the property names in a fixed array. |
| 934 Label next, call_runtime; |
| 935 // Preload a couple of values used in the loop. |
| 936 Register empty_fixed_array_value = r6; |
| 937 __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); |
| 938 Register empty_descriptor_array_value = r7; |
| 939 __ LoadRoot(empty_descriptor_array_value, |
| 940 Heap::kEmptyDescriptorArrayRootIndex); |
| 941 __ mov(r1, r0); |
| 942 __ bind(&next); |
| 943 |
| 944 // Check that there are no elements. Register r1 contains the |
| 945 // current JS object we've reached through the prototype chain. |
| 946 __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset)); |
| 947 __ cmp(r2, empty_fixed_array_value); |
| 948 __ b(ne, &call_runtime); |
| 949 |
| 950 // Check that instance descriptors are not empty so that we can |
| 951 // check for an enum cache. Leave the map in r2 for the subsequent |
| 952 // prototype load. |
| 953 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 954 __ ldr(r3, FieldMemOperand(r2, Map::kInstanceDescriptorsOffset)); |
| 955 __ cmp(r3, empty_descriptor_array_value); |
| 956 __ b(eq, &call_runtime); |
| 957 |
| 958 // Check that there is an enum cache in the non-empty instance |
| 959 // descriptors (r3). This is the case if the next enumeration |
| 960 // index field does not contain a smi. |
| 961 __ ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumerationIndexOffset)); |
| 962 __ JumpIfSmi(r3, &call_runtime); |
| 963 |
| 964 // For all objects but the receiver, check that the cache is empty. |
| 965 Label check_prototype; |
| 966 __ cmp(r1, r0); |
| 967 __ b(eq, &check_prototype); |
| 968 __ ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 969 __ cmp(r3, empty_fixed_array_value); |
| 970 __ b(ne, &call_runtime); |
| 971 |
| 972 // Load the prototype from the map and loop if non-null. |
| 973 __ bind(&check_prototype); |
| 974 __ ldr(r1, FieldMemOperand(r2, Map::kPrototypeOffset)); |
| 975 __ cmp(r1, null_value); |
| 976 __ b(ne, &next); |
| 977 |
| 978 // The enum cache is valid. Load the map of the object being |
| 979 // iterated over and use the cache for the iteration. |
| 980 Label use_cache; |
| 981 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); |
| 982 __ b(&use_cache); |
| 906 | 983 |
| 907 // Get the set of properties to enumerate. | 984 // Get the set of properties to enumerate. |
| 985 __ bind(&call_runtime); |
| 908 __ push(r0); // Duplicate the enumerable object on the stack. | 986 __ push(r0); // Duplicate the enumerable object on the stack. |
| 909 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); | 987 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); |
| 910 | 988 |
| 911 // If we got a map from the runtime call, we can do a fast | 989 // If we got a map from the runtime call, we can do a fast |
| 912 // modification check. Otherwise, we got a fixed array, and we have | 990 // modification check. Otherwise, we got a fixed array, and we have |
| 913 // to do a slow check. | 991 // to do a slow check. |
| 914 Label fixed_array; | 992 Label fixed_array; |
| 915 __ mov(r2, r0); | 993 __ mov(r2, r0); |
| 916 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); | 994 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); |
| 917 __ LoadRoot(ip, Heap::kMetaMapRootIndex); | 995 __ LoadRoot(ip, Heap::kMetaMapRootIndex); |
| 918 __ cmp(r1, ip); | 996 __ cmp(r1, ip); |
| 919 __ b(ne, &fixed_array); | 997 __ b(ne, &fixed_array); |
| 920 | 998 |
| 921 // We got a map in register r0. Get the enumeration cache from it. | 999 // We got a map in register r0. Get the enumeration cache from it. |
| 1000 __ bind(&use_cache); |
| 922 __ ldr(r1, FieldMemOperand(r0, Map::kInstanceDescriptorsOffset)); | 1001 __ ldr(r1, FieldMemOperand(r0, Map::kInstanceDescriptorsOffset)); |
| 923 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); | 1002 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); |
| 924 __ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); | 1003 __ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 925 | 1004 |
| 926 // Setup the four remaining stack slots. | 1005 // Setup the four remaining stack slots. |
| 927 __ push(r0); // Map. | 1006 __ push(r0); // Map. |
| 928 __ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset)); | 1007 __ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset)); |
| 929 __ mov(r0, Operand(Smi::FromInt(0))); | 1008 __ mov(r0, Operand(Smi::FromInt(0))); |
| 930 // Push enumeration cache, enumeration cache length (as smi) and zero. | 1009 // Push enumeration cache, enumeration cache length (as smi) and zero. |
| 931 __ Push(r2, r1, r0); | 1010 __ Push(r2, r1, r0); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1000 | 1079 |
| 1001 // Exit and decrement the loop depth. | 1080 // Exit and decrement the loop depth. |
| 1002 __ bind(&exit); | 1081 __ bind(&exit); |
| 1003 decrement_loop_depth(); | 1082 decrement_loop_depth(); |
| 1004 } | 1083 } |
| 1005 | 1084 |
| 1006 | 1085 |
| 1007 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, | 1086 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, |
| 1008 bool pretenure) { | 1087 bool pretenure) { |
| 1009 // Use the fast case closure allocation code that allocates in new | 1088 // Use the fast case closure allocation code that allocates in new |
| 1010 // space for nested functions that don't need literals cloning. | 1089 // space for nested functions that don't need literals cloning. If |
| 1011 if (scope()->is_function_scope() && | 1090 // we're running with the --always-opt or the --prepare-always-opt |
| 1091 // flag, we need to use the runtime function so that the new function |
| 1092 // we are creating here gets a chance to have its code optimized and |
| 1093 // doesn't just get a copy of the existing unoptimized code. |
| 1094 if (!FLAG_always_opt && |
| 1095 !FLAG_prepare_always_opt && |
| 1096 scope()->is_function_scope() && |
| 1012 info->num_literals() == 0 && | 1097 info->num_literals() == 0 && |
| 1013 !pretenure) { | 1098 !pretenure) { |
| 1014 FastNewClosureStub stub; | 1099 FastNewClosureStub stub; |
| 1015 __ mov(r0, Operand(info)); | 1100 __ mov(r0, Operand(info)); |
| 1016 __ push(r0); | 1101 __ push(r0); |
| 1017 __ CallStub(&stub); | 1102 __ CallStub(&stub); |
| 1018 } else { | 1103 } else { |
| 1019 __ mov(r0, Operand(info)); | 1104 __ mov(r0, Operand(info)); |
| 1020 __ LoadRoot(r1, pretenure ? Heap::kTrueValueRootIndex | 1105 __ LoadRoot(r1, pretenure ? Heap::kTrueValueRootIndex |
| 1021 : Heap::kFalseValueRootIndex); | 1106 : Heap::kFalseValueRootIndex); |
| 1022 __ Push(cp, r0, r1); | 1107 __ Push(cp, r0, r1); |
| 1023 __ CallRuntime(Runtime::kNewClosure, 3); | 1108 __ CallRuntime(Runtime::kNewClosure, 3); |
| 1024 } | 1109 } |
| 1025 context()->Plug(r0); | 1110 context()->Plug(r0); |
| 1026 } | 1111 } |
| 1027 | 1112 |
| 1028 | 1113 |
| 1029 void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { | 1114 void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { |
| 1030 Comment cmnt(masm_, "[ VariableProxy"); | 1115 Comment cmnt(masm_, "[ VariableProxy"); |
| 1031 EmitVariableLoad(expr->var()); | 1116 EmitVariableLoad(expr->var()); |
| 1032 } | 1117 } |
| 1033 | 1118 |
| 1034 | 1119 |
| 1035 MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( | 1120 MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( |
| 1036 Slot* slot, | 1121 Slot* slot, |
| 1037 Label* slow) { | 1122 Label* slow) { |
| 1038 ASSERT(slot->type() == Slot::CONTEXT); | 1123 ASSERT(slot->type() == Slot::CONTEXT); |
| 1039 Register current = cp; | 1124 Register context = cp; |
| 1040 Register next = r3; | 1125 Register next = r3; |
| 1041 Register temp = r4; | 1126 Register temp = r4; |
| 1042 | 1127 |
| 1043 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { | 1128 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { |
| 1044 if (s->num_heap_slots() > 0) { | 1129 if (s->num_heap_slots() > 0) { |
| 1045 if (s->calls_eval()) { | 1130 if (s->calls_eval()) { |
| 1046 // Check that extension is NULL. | 1131 // Check that extension is NULL. |
| 1047 __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); | 1132 __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX)); |
| 1048 __ tst(temp, temp); | 1133 __ tst(temp, temp); |
| 1049 __ b(ne, slow); | 1134 __ b(ne, slow); |
| 1050 } | 1135 } |
| 1051 __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX)); | 1136 __ ldr(next, ContextOperand(context, Context::CLOSURE_INDEX)); |
| 1052 __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); | 1137 __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); |
| 1053 // Walk the rest of the chain without clobbering cp. | 1138 // Walk the rest of the chain without clobbering cp. |
| 1054 current = next; | 1139 context = next; |
| 1055 } | 1140 } |
| 1056 } | 1141 } |
| 1057 // Check that last extension is NULL. | 1142 // Check that last extension is NULL. |
| 1058 __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); | 1143 __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX)); |
| 1059 __ tst(temp, temp); | 1144 __ tst(temp, temp); |
| 1060 __ b(ne, slow); | 1145 __ b(ne, slow); |
| 1061 __ ldr(temp, ContextOperand(current, Context::FCONTEXT_INDEX)); | 1146 |
| 1062 return ContextOperand(temp, slot->index()); | 1147 // This function is used only for loads, not stores, so it's safe to |
| 1148 // return an cp-based operand (the write barrier cannot be allowed to |
| 1149 // destroy the cp register). |
| 1150 return ContextOperand(context, slot->index()); |
| 1063 } | 1151 } |
| 1064 | 1152 |
| 1065 | 1153 |
| 1066 void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( | 1154 void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( |
| 1067 Slot* slot, | 1155 Slot* slot, |
| 1068 TypeofState typeof_state, | 1156 TypeofState typeof_state, |
| 1069 Label* slow, | 1157 Label* slow, |
| 1070 Label* done) { | 1158 Label* done) { |
| 1071 // Generate fast-case code for variables that might be shadowed by | 1159 // Generate fast-case code for variables that might be shadowed by |
| 1072 // eval-introduced variables. Eval is used a lot without | 1160 // eval-introduced variables. Eval is used a lot without |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1252 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1340 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1253 context()->Plug(r0); | 1341 context()->Plug(r0); |
| 1254 } | 1342 } |
| 1255 } | 1343 } |
| 1256 | 1344 |
| 1257 | 1345 |
| 1258 void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { | 1346 void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { |
| 1259 Comment cmnt(masm_, "[ RegExpLiteral"); | 1347 Comment cmnt(masm_, "[ RegExpLiteral"); |
| 1260 Label materialized; | 1348 Label materialized; |
| 1261 // Registers will be used as follows: | 1349 // Registers will be used as follows: |
| 1350 // r5 = materialized value (RegExp literal) |
| 1262 // r4 = JS function, literals array | 1351 // r4 = JS function, literals array |
| 1263 // r3 = literal index | 1352 // r3 = literal index |
| 1264 // r2 = RegExp pattern | 1353 // r2 = RegExp pattern |
| 1265 // r1 = RegExp flags | 1354 // r1 = RegExp flags |
| 1266 // r0 = temp + materialized value (RegExp literal) | 1355 // r0 = RegExp literal clone |
| 1267 __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); | 1356 __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); |
| 1268 __ ldr(r4, FieldMemOperand(r0, JSFunction::kLiteralsOffset)); | 1357 __ ldr(r4, FieldMemOperand(r0, JSFunction::kLiteralsOffset)); |
| 1269 int literal_offset = | 1358 int literal_offset = |
| 1270 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; | 1359 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; |
| 1271 __ ldr(r0, FieldMemOperand(r4, literal_offset)); | 1360 __ ldr(r5, FieldMemOperand(r4, literal_offset)); |
| 1272 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); | 1361 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
| 1273 __ cmp(r0, ip); | 1362 __ cmp(r5, ip); |
| 1274 __ b(ne, &materialized); | 1363 __ b(ne, &materialized); |
| 1275 | 1364 |
| 1276 // Create regexp literal using runtime function. | 1365 // Create regexp literal using runtime function. |
| 1277 // Result will be in r0. | 1366 // Result will be in r0. |
| 1278 __ mov(r3, Operand(Smi::FromInt(expr->literal_index()))); | 1367 __ mov(r3, Operand(Smi::FromInt(expr->literal_index()))); |
| 1279 __ mov(r2, Operand(expr->pattern())); | 1368 __ mov(r2, Operand(expr->pattern())); |
| 1280 __ mov(r1, Operand(expr->flags())); | 1369 __ mov(r1, Operand(expr->flags())); |
| 1281 __ Push(r4, r3, r2, r1); | 1370 __ Push(r4, r3, r2, r1); |
| 1282 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); | 1371 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); |
| 1372 __ mov(r5, r0); |
| 1283 | 1373 |
| 1284 __ bind(&materialized); | 1374 __ bind(&materialized); |
| 1285 int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; | 1375 int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; |
| 1286 __ push(r0); | 1376 Label allocated, runtime_allocate; |
| 1377 __ AllocateInNewSpace(size, r0, r2, r3, &runtime_allocate, TAG_OBJECT); |
| 1378 __ jmp(&allocated); |
| 1379 |
| 1380 __ bind(&runtime_allocate); |
| 1381 __ push(r5); |
| 1287 __ mov(r0, Operand(Smi::FromInt(size))); | 1382 __ mov(r0, Operand(Smi::FromInt(size))); |
| 1288 __ push(r0); | 1383 __ push(r0); |
| 1289 __ CallRuntime(Runtime::kAllocateInNewSpace, 1); | 1384 __ CallRuntime(Runtime::kAllocateInNewSpace, 1); |
| 1385 __ pop(r5); |
| 1290 | 1386 |
| 1387 __ bind(&allocated); |
| 1291 // After this, registers are used as follows: | 1388 // After this, registers are used as follows: |
| 1292 // r0: Newly allocated regexp. | 1389 // r0: Newly allocated regexp. |
| 1293 // r1: Materialized regexp. | 1390 // r5: Materialized regexp. |
| 1294 // r2: temp. | 1391 // r2: temp. |
| 1295 __ pop(r1); | 1392 __ CopyFields(r0, r5, r2.bit(), size / kPointerSize); |
| 1296 __ CopyFields(r0, r1, r2.bit(), size / kPointerSize); | |
| 1297 context()->Plug(r0); | 1393 context()->Plug(r0); |
| 1298 } | 1394 } |
| 1299 | 1395 |
| 1300 | 1396 |
| 1301 void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { | 1397 void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { |
| 1302 Comment cmnt(masm_, "[ ObjectLiteral"); | 1398 Comment cmnt(masm_, "[ ObjectLiteral"); |
| 1303 __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); | 1399 __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); |
| 1304 __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); | 1400 __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); |
| 1305 __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); | 1401 __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); |
| 1306 __ mov(r1, Operand(expr->constant_properties())); | 1402 __ mov(r1, Operand(expr->constant_properties())); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1352 break; | 1448 break; |
| 1353 } | 1449 } |
| 1354 // Fall through. | 1450 // Fall through. |
| 1355 case ObjectLiteral::Property::PROTOTYPE: | 1451 case ObjectLiteral::Property::PROTOTYPE: |
| 1356 // Duplicate receiver on stack. | 1452 // Duplicate receiver on stack. |
| 1357 __ ldr(r0, MemOperand(sp)); | 1453 __ ldr(r0, MemOperand(sp)); |
| 1358 __ push(r0); | 1454 __ push(r0); |
| 1359 VisitForStackValue(key); | 1455 VisitForStackValue(key); |
| 1360 VisitForStackValue(value); | 1456 VisitForStackValue(value); |
| 1361 if (property->emit_store()) { | 1457 if (property->emit_store()) { |
| 1362 __ CallRuntime(Runtime::kSetProperty, 3); | 1458 __ mov(r0, Operand(Smi::FromInt(NONE))); // PropertyAttributes |
| 1459 __ push(r0); |
| 1460 __ CallRuntime(Runtime::kSetProperty, 4); |
| 1363 } else { | 1461 } else { |
| 1364 __ Drop(3); | 1462 __ Drop(3); |
| 1365 } | 1463 } |
| 1366 break; | 1464 break; |
| 1367 case ObjectLiteral::Property::GETTER: | 1465 case ObjectLiteral::Property::GETTER: |
| 1368 case ObjectLiteral::Property::SETTER: | 1466 case ObjectLiteral::Property::SETTER: |
| 1369 // Duplicate receiver on stack. | 1467 // Duplicate receiver on stack. |
| 1370 __ ldr(r0, MemOperand(sp)); | 1468 __ ldr(r0, MemOperand(sp)); |
| 1371 __ push(r0); | 1469 __ push(r0); |
| 1372 VisitForStackValue(key); | 1470 VisitForStackValue(key); |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1532 } | 1630 } |
| 1533 } | 1631 } |
| 1534 | 1632 |
| 1535 // For property compound assignments we need another deoptimization | 1633 // For property compound assignments we need another deoptimization |
| 1536 // point after the property load. | 1634 // point after the property load. |
| 1537 if (property != NULL) { | 1635 if (property != NULL) { |
| 1538 PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); | 1636 PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); |
| 1539 } | 1637 } |
| 1540 | 1638 |
| 1541 Token::Value op = expr->binary_op(); | 1639 Token::Value op = expr->binary_op(); |
| 1542 ConstantOperand constant = ShouldInlineSmiCase(op) | 1640 __ push(r0); // Left operand goes on the stack. |
| 1543 ? GetConstantOperand(op, expr->target(), expr->value()) | 1641 VisitForAccumulatorValue(expr->value()); |
| 1544 : kNoConstants; | |
| 1545 ASSERT(constant == kRightConstant || constant == kNoConstants); | |
| 1546 if (constant == kNoConstants) { | |
| 1547 __ push(r0); // Left operand goes on the stack. | |
| 1548 VisitForAccumulatorValue(expr->value()); | |
| 1549 } | |
| 1550 | 1642 |
| 1551 OverwriteMode mode = expr->value()->ResultOverwriteAllowed() | 1643 OverwriteMode mode = expr->value()->ResultOverwriteAllowed() |
| 1552 ? OVERWRITE_RIGHT | 1644 ? OVERWRITE_RIGHT |
| 1553 : NO_OVERWRITE; | 1645 : NO_OVERWRITE; |
| 1554 SetSourcePosition(expr->position() + 1); | 1646 SetSourcePosition(expr->position() + 1); |
| 1555 AccumulatorValueContext context(this); | 1647 AccumulatorValueContext context(this); |
| 1556 if (ShouldInlineSmiCase(op)) { | 1648 if (ShouldInlineSmiCase(op)) { |
| 1557 EmitInlineSmiBinaryOp(expr, | 1649 EmitInlineSmiBinaryOp(expr, |
| 1558 op, | 1650 op, |
| 1559 mode, | 1651 mode, |
| 1560 expr->target(), | 1652 expr->target(), |
| 1561 expr->value(), | 1653 expr->value()); |
| 1562 constant); | |
| 1563 } else { | 1654 } else { |
| 1564 EmitBinaryOp(op, mode); | 1655 EmitBinaryOp(op, mode); |
| 1565 } | 1656 } |
| 1566 | 1657 |
| 1567 // Deoptimization point in case the binary operation may have side effects. | 1658 // Deoptimization point in case the binary operation may have side effects. |
| 1568 PrepareForBailout(expr->binary_operation(), TOS_REG); | 1659 PrepareForBailout(expr->binary_operation(), TOS_REG); |
| 1569 } else { | 1660 } else { |
| 1570 VisitForAccumulatorValue(expr->value()); | 1661 VisitForAccumulatorValue(expr->value()); |
| 1571 } | 1662 } |
| 1572 | 1663 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1605 SetSourcePosition(prop->position()); | 1696 SetSourcePosition(prop->position()); |
| 1606 // Call keyed load IC. It has arguments key and receiver in r0 and r1. | 1697 // Call keyed load IC. It has arguments key and receiver in r0 and r1. |
| 1607 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); | 1698 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
| 1608 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1699 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1609 } | 1700 } |
| 1610 | 1701 |
| 1611 | 1702 |
| 1612 void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, | 1703 void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, |
| 1613 Token::Value op, | 1704 Token::Value op, |
| 1614 OverwriteMode mode, | 1705 OverwriteMode mode, |
| 1615 Expression* left, | 1706 Expression* left_expr, |
| 1616 Expression* right, | 1707 Expression* right_expr) { |
| 1617 ConstantOperand constant) { | 1708 Label done, smi_case, stub_call; |
| 1618 ASSERT(constant == kNoConstants); // Only handled case. | 1709 |
| 1619 EmitBinaryOp(op, mode); | 1710 Register scratch1 = r2; |
| 1711 Register scratch2 = r3; |
| 1712 |
| 1713 // Get the arguments. |
| 1714 Register left = r1; |
| 1715 Register right = r0; |
| 1716 __ pop(left); |
| 1717 |
| 1718 // Perform combined smi check on both operands. |
| 1719 __ orr(scratch1, left, Operand(right)); |
| 1720 STATIC_ASSERT(kSmiTag == 0); |
| 1721 JumpPatchSite patch_site(masm_); |
| 1722 patch_site.EmitJumpIfSmi(scratch1, &smi_case); |
| 1723 |
| 1724 __ bind(&stub_call); |
| 1725 TypeRecordingBinaryOpStub stub(op, mode); |
| 1726 EmitCallIC(stub.GetCode(), &patch_site); |
| 1727 __ jmp(&done); |
| 1728 |
| 1729 __ bind(&smi_case); |
| 1730 // Smi case. This code works the same way as the smi-smi case in the type |
| 1731 // recording binary operation stub, see |
| 1732 // TypeRecordingBinaryOpStub::GenerateSmiSmiOperation for comments. |
| 1733 switch (op) { |
| 1734 case Token::SAR: |
| 1735 __ b(&stub_call); |
| 1736 __ GetLeastBitsFromSmi(scratch1, right, 5); |
| 1737 __ mov(right, Operand(left, ASR, scratch1)); |
| 1738 __ bic(right, right, Operand(kSmiTagMask)); |
| 1739 break; |
| 1740 case Token::SHL: { |
| 1741 __ b(&stub_call); |
| 1742 __ SmiUntag(scratch1, left); |
| 1743 __ GetLeastBitsFromSmi(scratch2, right, 5); |
| 1744 __ mov(scratch1, Operand(scratch1, LSL, scratch2)); |
| 1745 __ add(scratch2, scratch1, Operand(0x40000000), SetCC); |
| 1746 __ b(mi, &stub_call); |
| 1747 __ SmiTag(right, scratch1); |
| 1748 break; |
| 1749 } |
| 1750 case Token::SHR: { |
| 1751 __ b(&stub_call); |
| 1752 __ SmiUntag(scratch1, left); |
| 1753 __ GetLeastBitsFromSmi(scratch2, right, 5); |
| 1754 __ mov(scratch1, Operand(scratch1, LSR, scratch2)); |
| 1755 __ tst(scratch1, Operand(0xc0000000)); |
| 1756 __ b(ne, &stub_call); |
| 1757 __ SmiTag(right, scratch1); |
| 1758 break; |
| 1759 } |
| 1760 case Token::ADD: |
| 1761 __ add(scratch1, left, Operand(right), SetCC); |
| 1762 __ b(vs, &stub_call); |
| 1763 __ mov(right, scratch1); |
| 1764 break; |
| 1765 case Token::SUB: |
| 1766 __ sub(scratch1, left, Operand(right), SetCC); |
| 1767 __ b(vs, &stub_call); |
| 1768 __ mov(right, scratch1); |
| 1769 break; |
| 1770 case Token::MUL: { |
| 1771 __ SmiUntag(ip, right); |
| 1772 __ smull(scratch1, scratch2, left, ip); |
| 1773 __ mov(ip, Operand(scratch1, ASR, 31)); |
| 1774 __ cmp(ip, Operand(scratch2)); |
| 1775 __ b(ne, &stub_call); |
| 1776 __ tst(scratch1, Operand(scratch1)); |
| 1777 __ mov(right, Operand(scratch1), LeaveCC, ne); |
| 1778 __ b(ne, &done); |
| 1779 __ add(scratch2, right, Operand(left), SetCC); |
| 1780 __ mov(right, Operand(Smi::FromInt(0)), LeaveCC, pl); |
| 1781 __ b(mi, &stub_call); |
| 1782 break; |
| 1783 } |
| 1784 case Token::BIT_OR: |
| 1785 __ orr(right, left, Operand(right)); |
| 1786 break; |
| 1787 case Token::BIT_AND: |
| 1788 __ and_(right, left, Operand(right)); |
| 1789 break; |
| 1790 case Token::BIT_XOR: |
| 1791 __ eor(right, left, Operand(right)); |
| 1792 break; |
| 1793 default: |
| 1794 UNREACHABLE(); |
| 1795 } |
| 1796 |
| 1797 __ bind(&done); |
| 1798 context()->Plug(r0); |
| 1620 } | 1799 } |
| 1621 | 1800 |
| 1622 | 1801 |
| 1623 void FullCodeGenerator::EmitBinaryOp(Token::Value op, | 1802 void FullCodeGenerator::EmitBinaryOp(Token::Value op, |
| 1624 OverwriteMode mode) { | 1803 OverwriteMode mode) { |
| 1625 __ pop(r1); | 1804 __ pop(r1); |
| 1626 TypeRecordingBinaryOpStub stub(op, mode); | 1805 TypeRecordingBinaryOpStub stub(op, mode); |
| 1627 EmitCallIC(stub.GetCode(), NULL); | 1806 EmitCallIC(stub.GetCode(), NULL); |
| 1628 context()->Plug(r0); | 1807 context()->Plug(r0); |
| 1629 } | 1808 } |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1654 EffectContext context(this); | 1833 EffectContext context(this); |
| 1655 EmitVariableAssignment(var, Token::ASSIGN); | 1834 EmitVariableAssignment(var, Token::ASSIGN); |
| 1656 break; | 1835 break; |
| 1657 } | 1836 } |
| 1658 case NAMED_PROPERTY: { | 1837 case NAMED_PROPERTY: { |
| 1659 __ push(r0); // Preserve value. | 1838 __ push(r0); // Preserve value. |
| 1660 VisitForAccumulatorValue(prop->obj()); | 1839 VisitForAccumulatorValue(prop->obj()); |
| 1661 __ mov(r1, r0); | 1840 __ mov(r1, r0); |
| 1662 __ pop(r0); // Restore value. | 1841 __ pop(r0); // Restore value. |
| 1663 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); | 1842 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); |
| 1664 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 1843 Handle<Code> ic(Builtins::builtin( |
| 1844 is_strict_mode() ? Builtins::StoreIC_Initialize_Strict |
| 1845 : Builtins::StoreIC_Initialize)); |
| 1665 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1846 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1666 break; | 1847 break; |
| 1667 } | 1848 } |
| 1668 case KEYED_PROPERTY: { | 1849 case KEYED_PROPERTY: { |
| 1669 __ push(r0); // Preserve value. | 1850 __ push(r0); // Preserve value. |
| 1670 if (prop->is_synthetic()) { | 1851 if (prop->is_synthetic()) { |
| 1671 ASSERT(prop->obj()->AsVariableProxy() != NULL); | 1852 ASSERT(prop->obj()->AsVariableProxy() != NULL); |
| 1672 ASSERT(prop->key()->AsLiteral() != NULL); | 1853 ASSERT(prop->key()->AsLiteral() != NULL); |
| 1673 { AccumulatorValueContext for_object(this); | 1854 { AccumulatorValueContext for_object(this); |
| 1674 EmitVariableLoad(prop->obj()->AsVariableProxy()->var()); | 1855 EmitVariableLoad(prop->obj()->AsVariableProxy()->var()); |
| 1675 } | 1856 } |
| 1676 __ mov(r2, r0); | 1857 __ mov(r2, r0); |
| 1677 __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); | 1858 __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); |
| 1678 } else { | 1859 } else { |
| 1679 VisitForStackValue(prop->obj()); | 1860 VisitForStackValue(prop->obj()); |
| 1680 VisitForAccumulatorValue(prop->key()); | 1861 VisitForAccumulatorValue(prop->key()); |
| 1681 __ mov(r1, r0); | 1862 __ mov(r1, r0); |
| 1682 __ pop(r2); | 1863 __ pop(r2); |
| 1683 } | 1864 } |
| 1684 __ pop(r0); // Restore value. | 1865 __ pop(r0); // Restore value. |
| 1685 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 1866 Handle<Code> ic(Builtins::builtin( |
| 1867 is_strict_mode() ? Builtins::KeyedStoreIC_Initialize_Strict |
| 1868 : Builtins::KeyedStoreIC_Initialize)); |
| 1686 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1869 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1687 break; | 1870 break; |
| 1688 } | 1871 } |
| 1689 } | 1872 } |
| 1690 PrepareForBailoutForId(bailout_ast_id, TOS_REG); | 1873 PrepareForBailoutForId(bailout_ast_id, TOS_REG); |
| 1691 context()->Plug(r0); | 1874 context()->Plug(r0); |
| 1692 } | 1875 } |
| 1693 | 1876 |
| 1694 | 1877 |
| 1695 void FullCodeGenerator::EmitVariableAssignment(Variable* var, | 1878 void FullCodeGenerator::EmitVariableAssignment(Variable* var, |
| 1696 Token::Value op) { | 1879 Token::Value op) { |
| 1697 // Left-hand sides that rewrite to explicit property accesses do not reach | 1880 // Left-hand sides that rewrite to explicit property accesses do not reach |
| 1698 // here. | 1881 // here. |
| 1699 ASSERT(var != NULL); | 1882 ASSERT(var != NULL); |
| 1700 ASSERT(var->is_global() || var->AsSlot() != NULL); | 1883 ASSERT(var->is_global() || var->AsSlot() != NULL); |
| 1701 | 1884 |
| 1702 if (var->is_global()) { | 1885 if (var->is_global()) { |
| 1703 ASSERT(!var->is_this()); | 1886 ASSERT(!var->is_this()); |
| 1704 // Assignment to a global variable. Use inline caching for the | 1887 // Assignment to a global variable. Use inline caching for the |
| 1705 // assignment. Right-hand-side value is passed in r0, variable name in | 1888 // assignment. Right-hand-side value is passed in r0, variable name in |
| 1706 // r2, and the global object in r1. | 1889 // r2, and the global object in r1. |
| 1707 __ mov(r2, Operand(var->name())); | 1890 __ mov(r2, Operand(var->name())); |
| 1708 __ ldr(r1, GlobalObjectOperand()); | 1891 __ ldr(r1, GlobalObjectOperand()); |
| 1709 Handle<Code> ic(Builtins::builtin(is_strict() | 1892 Handle<Code> ic(Builtins::builtin( |
| 1710 ? Builtins::StoreIC_Initialize_Strict | 1893 is_strict_mode() ? Builtins::StoreIC_Initialize_Strict |
| 1711 : Builtins::StoreIC_Initialize)); | 1894 : Builtins::StoreIC_Initialize)); |
| 1712 EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); | 1895 EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); |
| 1713 | 1896 |
| 1714 } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { | 1897 } else if (op == Token::INIT_CONST) { |
| 1715 // Perform the assignment for non-const variables and for initialization | 1898 // Like var declarations, const declarations are hoisted to function |
| 1716 // of const variables. Const assignments are simply skipped. | 1899 // scope. However, unlike var initializers, const initializers are able |
| 1717 Label done; | 1900 // to drill a hole to that function context, even from inside a 'with' |
| 1901 // context. We thus bypass the normal static scope lookup. |
| 1902 Slot* slot = var->AsSlot(); |
| 1903 Label skip; |
| 1904 switch (slot->type()) { |
| 1905 case Slot::PARAMETER: |
| 1906 // No const parameters. |
| 1907 UNREACHABLE(); |
| 1908 break; |
| 1909 case Slot::LOCAL: |
| 1910 // Detect const reinitialization by checking for the hole value. |
| 1911 __ ldr(r1, MemOperand(fp, SlotOffset(slot))); |
| 1912 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
| 1913 __ cmp(r1, ip); |
| 1914 __ b(ne, &skip); |
| 1915 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
| 1916 break; |
| 1917 case Slot::CONTEXT: { |
| 1918 __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); |
| 1919 __ ldr(r2, ContextOperand(r1, slot->index())); |
| 1920 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
| 1921 __ cmp(r2, ip); |
| 1922 __ b(ne, &skip); |
| 1923 __ str(r0, ContextOperand(r1, slot->index())); |
| 1924 int offset = Context::SlotOffset(slot->index()); |
| 1925 __ mov(r3, r0); // Preserve the stored value in r0. |
| 1926 __ RecordWrite(r1, Operand(offset), r3, r2); |
| 1927 break; |
| 1928 } |
| 1929 case Slot::LOOKUP: |
| 1930 __ push(r0); |
| 1931 __ mov(r0, Operand(slot->var()->name())); |
| 1932 __ Push(cp, r0); // Context and name. |
| 1933 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
| 1934 break; |
| 1935 } |
| 1936 __ bind(&skip); |
| 1937 |
| 1938 } else if (var->mode() != Variable::CONST) { |
| 1939 // Perform the assignment for non-const variables. Const assignments |
| 1940 // are simply skipped. |
| 1718 Slot* slot = var->AsSlot(); | 1941 Slot* slot = var->AsSlot(); |
| 1719 switch (slot->type()) { | 1942 switch (slot->type()) { |
| 1720 case Slot::PARAMETER: | 1943 case Slot::PARAMETER: |
| 1721 case Slot::LOCAL: | 1944 case Slot::LOCAL: |
| 1722 if (op == Token::INIT_CONST) { | |
| 1723 // Detect const reinitialization by checking for the hole value. | |
| 1724 __ ldr(r1, MemOperand(fp, SlotOffset(slot))); | |
| 1725 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | |
| 1726 __ cmp(r1, ip); | |
| 1727 __ b(ne, &done); | |
| 1728 } | |
| 1729 // Perform the assignment. | 1945 // Perform the assignment. |
| 1730 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); | 1946 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
| 1731 break; | 1947 break; |
| 1732 | 1948 |
| 1733 case Slot::CONTEXT: { | 1949 case Slot::CONTEXT: { |
| 1734 MemOperand target = EmitSlotSearch(slot, r1); | 1950 MemOperand target = EmitSlotSearch(slot, r1); |
| 1735 if (op == Token::INIT_CONST) { | |
| 1736 // Detect const reinitialization by checking for the hole value. | |
| 1737 __ ldr(r2, target); | |
| 1738 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | |
| 1739 __ cmp(r2, ip); | |
| 1740 __ b(ne, &done); | |
| 1741 } | |
| 1742 // Perform the assignment and issue the write barrier. | 1951 // Perform the assignment and issue the write barrier. |
| 1743 __ str(result_register(), target); | 1952 __ str(result_register(), target); |
| 1744 #ifdef ENABLE_CARDMARKING_WRITE_BARRIER | 1953 #ifdef ENABLE_CARDMARKING_WRITE_BARRIER |
| 1745 // RecordWrite may destroy all its register arguments. | 1954 // RecordWrite may destroy all its register arguments. |
| 1746 __ mov(r3, result_register()); | 1955 __ mov(r3, result_register()); |
| 1747 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | 1956 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; |
| 1748 __ RecordWrite(r1, Operand(offset), r2, r3); | 1957 __ RecordWrite(r1, Operand(offset), r2, r3); |
| 1749 #endif | 1958 #endif |
| 1750 break; | 1959 break; |
| 1751 } | 1960 } |
| 1752 | 1961 |
| 1753 case Slot::LOOKUP: | 1962 case Slot::LOOKUP: |
| 1754 // Call the runtime for the assignment. The runtime will ignore | 1963 // Call the runtime for the assignment. |
| 1755 // const reinitialization. | |
| 1756 __ push(r0); // Value. | 1964 __ push(r0); // Value. |
| 1757 __ mov(r0, Operand(slot->var()->name())); | 1965 __ mov(r1, Operand(slot->var()->name())); |
| 1758 __ Push(cp, r0); // Context and name. | 1966 __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); |
| 1759 if (op == Token::INIT_CONST) { | 1967 __ Push(cp, r1, r0); // Context, name, strict mode. |
| 1760 // The runtime will ignore const redeclaration. | 1968 __ CallRuntime(Runtime::kStoreContextSlot, 4); |
| 1761 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); | |
| 1762 } else { | |
| 1763 __ CallRuntime(Runtime::kStoreContextSlot, 3); | |
| 1764 } | |
| 1765 break; | 1969 break; |
| 1766 } | 1970 } |
| 1767 __ bind(&done); | |
| 1768 } | 1971 } |
| 1769 } | 1972 } |
| 1770 | 1973 |
| 1771 | 1974 |
| 1772 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { | 1975 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { |
| 1773 // Assignment to a property, using a named store IC. | 1976 // Assignment to a property, using a named store IC. |
| 1774 Property* prop = expr->target()->AsProperty(); | 1977 Property* prop = expr->target()->AsProperty(); |
| 1775 ASSERT(prop != NULL); | 1978 ASSERT(prop != NULL); |
| 1776 ASSERT(prop->key()->AsLiteral() != NULL); | 1979 ASSERT(prop->key()->AsLiteral() != NULL); |
| 1777 | 1980 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1790 SetSourcePosition(expr->position()); | 1993 SetSourcePosition(expr->position()); |
| 1791 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); | 1994 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); |
| 1792 // Load receiver to r1. Leave a copy in the stack if needed for turning the | 1995 // Load receiver to r1. Leave a copy in the stack if needed for turning the |
| 1793 // receiver into fast case. | 1996 // receiver into fast case. |
| 1794 if (expr->ends_initialization_block()) { | 1997 if (expr->ends_initialization_block()) { |
| 1795 __ ldr(r1, MemOperand(sp)); | 1998 __ ldr(r1, MemOperand(sp)); |
| 1796 } else { | 1999 } else { |
| 1797 __ pop(r1); | 2000 __ pop(r1); |
| 1798 } | 2001 } |
| 1799 | 2002 |
| 1800 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 2003 Handle<Code> ic(Builtins::builtin( |
| 2004 is_strict_mode() ? Builtins::StoreIC_Initialize_Strict |
| 2005 : Builtins::StoreIC_Initialize)); |
| 1801 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 2006 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1802 | 2007 |
| 1803 // If the assignment ends an initialization block, revert to fast case. | 2008 // If the assignment ends an initialization block, revert to fast case. |
| 1804 if (expr->ends_initialization_block()) { | 2009 if (expr->ends_initialization_block()) { |
| 1805 __ push(r0); // Result of assignment, saved even if not needed. | 2010 __ push(r0); // Result of assignment, saved even if not needed. |
| 1806 // Receiver is under the result value. | 2011 // Receiver is under the result value. |
| 1807 __ ldr(ip, MemOperand(sp, kPointerSize)); | 2012 __ ldr(ip, MemOperand(sp, kPointerSize)); |
| 1808 __ push(ip); | 2013 __ push(ip); |
| 1809 __ CallRuntime(Runtime::kToFastProperties, 1); | 2014 __ CallRuntime(Runtime::kToFastProperties, 1); |
| 1810 __ pop(r0); | 2015 __ pop(r0); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1834 SetSourcePosition(expr->position()); | 2039 SetSourcePosition(expr->position()); |
| 1835 __ pop(r1); // Key. | 2040 __ pop(r1); // Key. |
| 1836 // Load receiver to r2. Leave a copy in the stack if needed for turning the | 2041 // Load receiver to r2. Leave a copy in the stack if needed for turning the |
| 1837 // receiver into fast case. | 2042 // receiver into fast case. |
| 1838 if (expr->ends_initialization_block()) { | 2043 if (expr->ends_initialization_block()) { |
| 1839 __ ldr(r2, MemOperand(sp)); | 2044 __ ldr(r2, MemOperand(sp)); |
| 1840 } else { | 2045 } else { |
| 1841 __ pop(r2); | 2046 __ pop(r2); |
| 1842 } | 2047 } |
| 1843 | 2048 |
| 1844 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 2049 Handle<Code> ic(Builtins::builtin( |
| 2050 is_strict_mode() ? Builtins::KeyedStoreIC_Initialize_Strict |
| 2051 : Builtins::KeyedStoreIC_Initialize)); |
| 1845 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 2052 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1846 | 2053 |
| 1847 // If the assignment ends an initialization block, revert to fast case. | 2054 // If the assignment ends an initialization block, revert to fast case. |
| 1848 if (expr->ends_initialization_block()) { | 2055 if (expr->ends_initialization_block()) { |
| 1849 __ push(r0); // Result of assignment, saved even if not needed. | 2056 __ push(r0); // Result of assignment, saved even if not needed. |
| 1850 // Receiver is under the result value. | 2057 // Receiver is under the result value. |
| 1851 __ ldr(ip, MemOperand(sp, kPointerSize)); | 2058 __ ldr(ip, MemOperand(sp, kPointerSize)); |
| 1852 __ push(ip); | 2059 __ push(ip); |
| 1853 __ CallRuntime(Runtime::kToFastProperties, 1); | 2060 __ CallRuntime(Runtime::kToFastProperties, 1); |
| 1854 __ pop(r0); | 2061 __ pop(r0); |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1949 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; | 2156 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 1950 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); | 2157 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
| 1951 __ CallStub(&stub); | 2158 __ CallStub(&stub); |
| 1952 RecordJSReturnSite(expr); | 2159 RecordJSReturnSite(expr); |
| 1953 // Restore context register. | 2160 // Restore context register. |
| 1954 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); | 2161 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
| 1955 context()->DropAndPlug(1, r0); | 2162 context()->DropAndPlug(1, r0); |
| 1956 } | 2163 } |
| 1957 | 2164 |
| 1958 | 2165 |
| 2166 void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, |
| 2167 int arg_count) { |
| 2168 // Push copy of the first argument or undefined if it doesn't exist. |
| 2169 if (arg_count > 0) { |
| 2170 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); |
| 2171 } else { |
| 2172 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); |
| 2173 } |
| 2174 __ push(r1); |
| 2175 |
| 2176 // Push the receiver of the enclosing function and do runtime call. |
| 2177 __ ldr(r1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); |
| 2178 __ push(r1); |
| 2179 // Push the strict mode flag. |
| 2180 __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); |
| 2181 __ push(r1); |
| 2182 |
| 2183 __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP |
| 2184 ? Runtime::kResolvePossiblyDirectEvalNoLookup |
| 2185 : Runtime::kResolvePossiblyDirectEval, 4); |
| 2186 } |
| 2187 |
| 2188 |
| 1959 void FullCodeGenerator::VisitCall(Call* expr) { | 2189 void FullCodeGenerator::VisitCall(Call* expr) { |
| 1960 #ifdef DEBUG | 2190 #ifdef DEBUG |
| 1961 // We want to verify that RecordJSReturnSite gets called on all paths | 2191 // We want to verify that RecordJSReturnSite gets called on all paths |
| 1962 // through this function. Avoid early returns. | 2192 // through this function. Avoid early returns. |
| 1963 expr->return_is_recorded_ = false; | 2193 expr->return_is_recorded_ = false; |
| 1964 #endif | 2194 #endif |
| 1965 | 2195 |
| 1966 Comment cmnt(masm_, "[ Call"); | 2196 Comment cmnt(masm_, "[ Call"); |
| 1967 Expression* fun = expr->expression(); | 2197 Expression* fun = expr->expression(); |
| 1968 Variable* var = fun->AsVariableProxy()->AsVariable(); | 2198 Variable* var = fun->AsVariableProxy()->AsVariable(); |
| 1969 | 2199 |
| 1970 if (var != NULL && var->is_possibly_eval()) { | 2200 if (var != NULL && var->is_possibly_eval()) { |
| 1971 // In a call to eval, we first call %ResolvePossiblyDirectEval to | 2201 // In a call to eval, we first call %ResolvePossiblyDirectEval to |
| 1972 // resolve the function we need to call and the receiver of the | 2202 // resolve the function we need to call and the receiver of the |
| 1973 // call. Then we call the resolved function using the given | 2203 // call. Then we call the resolved function using the given |
| 1974 // arguments. | 2204 // arguments. |
| 1975 ZoneList<Expression*>* args = expr->arguments(); | 2205 ZoneList<Expression*>* args = expr->arguments(); |
| 1976 int arg_count = args->length(); | 2206 int arg_count = args->length(); |
| 1977 | 2207 |
| 1978 { PreservePositionScope pos_scope(masm()->positions_recorder()); | 2208 { PreservePositionScope pos_scope(masm()->positions_recorder()); |
| 1979 VisitForStackValue(fun); | 2209 VisitForStackValue(fun); |
| 1980 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); | 2210 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); |
| 1981 __ push(r2); // Reserved receiver slot. | 2211 __ push(r2); // Reserved receiver slot. |
| 1982 | 2212 |
| 1983 // Push the arguments. | 2213 // Push the arguments. |
| 1984 for (int i = 0; i < arg_count; i++) { | 2214 for (int i = 0; i < arg_count; i++) { |
| 1985 VisitForStackValue(args->at(i)); | 2215 VisitForStackValue(args->at(i)); |
| 1986 } | 2216 } |
| 1987 | 2217 |
| 1988 // Push copy of the function - found below the arguments. | 2218 // If we know that eval can only be shadowed by eval-introduced |
| 2219 // variables we attempt to load the global eval function directly |
| 2220 // in generated code. If we succeed, there is no need to perform a |
| 2221 // context lookup in the runtime system. |
| 2222 Label done; |
| 2223 if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { |
| 2224 Label slow; |
| 2225 EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), |
| 2226 NOT_INSIDE_TYPEOF, |
| 2227 &slow); |
| 2228 // Push the function and resolve eval. |
| 2229 __ push(r0); |
| 2230 EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); |
| 2231 __ jmp(&done); |
| 2232 __ bind(&slow); |
| 2233 } |
| 2234 |
| 2235 // Push copy of the function (found below the arguments) and |
| 2236 // resolve eval. |
| 1989 __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); | 2237 __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); |
| 1990 __ push(r1); | 2238 __ push(r1); |
| 1991 | 2239 EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); |
| 1992 // Push copy of the first argument or undefined if it doesn't exist. | 2240 if (done.is_linked()) { |
| 1993 if (arg_count > 0) { | 2241 __ bind(&done); |
| 1994 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); | |
| 1995 __ push(r1); | |
| 1996 } else { | |
| 1997 __ push(r2); | |
| 1998 } | 2242 } |
| 1999 | 2243 |
| 2000 // Push the receiver of the enclosing function and do runtime call. | |
| 2001 __ ldr(r1, | |
| 2002 MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); | |
| 2003 __ push(r1); | |
| 2004 // Push the strict mode flag. | |
| 2005 __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); | |
| 2006 __ push(r1); | |
| 2007 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4); | |
| 2008 | |
| 2009 // The runtime call returns a pair of values in r0 (function) and | 2244 // The runtime call returns a pair of values in r0 (function) and |
| 2010 // r1 (receiver). Touch up the stack with the right values. | 2245 // r1 (receiver). Touch up the stack with the right values. |
| 2011 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize)); | 2246 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize)); |
| 2012 __ str(r1, MemOperand(sp, arg_count * kPointerSize)); | 2247 __ str(r1, MemOperand(sp, arg_count * kPointerSize)); |
| 2013 } | 2248 } |
| 2014 | 2249 |
| 2015 // Record source position for debugger. | 2250 // Record source position for debugger. |
| 2016 SetSourcePosition(expr->position()); | 2251 SetSourcePosition(expr->position()); |
| 2017 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; | 2252 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 2018 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); | 2253 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
| (...skipping 611 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2630 __ bind(&done); | 2865 __ bind(&done); |
| 2631 context()->Plug(r0); | 2866 context()->Plug(r0); |
| 2632 } | 2867 } |
| 2633 | 2868 |
| 2634 | 2869 |
| 2635 void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { | 2870 void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { |
| 2636 // Load the arguments on the stack and call the runtime function. | 2871 // Load the arguments on the stack and call the runtime function. |
| 2637 ASSERT(args->length() == 2); | 2872 ASSERT(args->length() == 2); |
| 2638 VisitForStackValue(args->at(0)); | 2873 VisitForStackValue(args->at(0)); |
| 2639 VisitForStackValue(args->at(1)); | 2874 VisitForStackValue(args->at(1)); |
| 2640 __ CallRuntime(Runtime::kMath_pow, 2); | 2875 MathPowStub stub; |
| 2876 __ CallStub(&stub); |
| 2641 context()->Plug(r0); | 2877 context()->Plug(r0); |
| 2642 } | 2878 } |
| 2643 | 2879 |
| 2644 | 2880 |
| 2645 void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { | 2881 void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { |
| 2646 ASSERT(args->length() == 2); | 2882 ASSERT(args->length() == 2); |
| 2647 | 2883 |
| 2648 VisitForStackValue(args->at(0)); // Load the object. | 2884 VisitForStackValue(args->at(0)); // Load the object. |
| 2649 VisitForAccumulatorValue(args->at(1)); // Load the value. | 2885 VisitForAccumulatorValue(args->at(1)); // Load the value. |
| 2650 __ pop(r1); // r0 = value. r1 = object. | 2886 __ pop(r1); // r0 = value. r1 = object. |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2814 VisitForStackValue(args->at(0)); | 3050 VisitForStackValue(args->at(0)); |
| 2815 VisitForStackValue(args->at(1)); | 3051 VisitForStackValue(args->at(1)); |
| 2816 | 3052 |
| 2817 StringCompareStub stub; | 3053 StringCompareStub stub; |
| 2818 __ CallStub(&stub); | 3054 __ CallStub(&stub); |
| 2819 context()->Plug(r0); | 3055 context()->Plug(r0); |
| 2820 } | 3056 } |
| 2821 | 3057 |
| 2822 | 3058 |
| 2823 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { | 3059 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { |
| 2824 // Load the argument on the stack and call the runtime. | 3060 // Load the argument on the stack and call the stub. |
| 3061 TranscendentalCacheStub stub(TranscendentalCache::SIN, |
| 3062 TranscendentalCacheStub::TAGGED); |
| 2825 ASSERT(args->length() == 1); | 3063 ASSERT(args->length() == 1); |
| 2826 VisitForStackValue(args->at(0)); | 3064 VisitForStackValue(args->at(0)); |
| 2827 __ CallRuntime(Runtime::kMath_sin, 1); | 3065 __ CallStub(&stub); |
| 2828 context()->Plug(r0); | 3066 context()->Plug(r0); |
| 2829 } | 3067 } |
| 2830 | 3068 |
| 2831 | 3069 |
| 2832 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { | 3070 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { |
| 2833 // Load the argument on the stack and call the runtime. | 3071 // Load the argument on the stack and call the stub. |
| 3072 TranscendentalCacheStub stub(TranscendentalCache::COS, |
| 3073 TranscendentalCacheStub::TAGGED); |
| 2834 ASSERT(args->length() == 1); | 3074 ASSERT(args->length() == 1); |
| 2835 VisitForStackValue(args->at(0)); | 3075 VisitForStackValue(args->at(0)); |
| 2836 __ CallRuntime(Runtime::kMath_cos, 1); | 3076 __ CallStub(&stub); |
| 3077 context()->Plug(r0); |
| 3078 } |
| 3079 |
| 3080 |
| 3081 void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { |
| 3082 // Load the argument on the stack and call the stub. |
| 3083 TranscendentalCacheStub stub(TranscendentalCache::LOG, |
| 3084 TranscendentalCacheStub::TAGGED); |
| 3085 ASSERT(args->length() == 1); |
| 3086 VisitForStackValue(args->at(0)); |
| 3087 __ CallStub(&stub); |
| 2837 context()->Plug(r0); | 3088 context()->Plug(r0); |
| 2838 } | 3089 } |
| 2839 | 3090 |
| 2840 | 3091 |
| 2841 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { | 3092 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { |
| 2842 // Load the argument on the stack and call the runtime function. | 3093 // Load the argument on the stack and call the runtime function. |
| 2843 ASSERT(args->length() == 1); | 3094 ASSERT(args->length() == 1); |
| 2844 VisitForStackValue(args->at(0)); | 3095 VisitForStackValue(args->at(0)); |
| 2845 __ CallRuntime(Runtime::kMath_sqrt, 1); | 3096 __ CallRuntime(Runtime::kMath_sqrt, 1); |
| 2846 context()->Plug(r0); | 3097 context()->Plug(r0); |
| 2847 } | 3098 } |
| 2848 | 3099 |
| 2849 | |
| 2850 void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { | |
| 2851 // Load the argument on the stack and call the runtime function. | |
| 2852 ASSERT(args->length() == 1); | |
| 2853 VisitForStackValue(args->at(0)); | |
| 2854 __ CallRuntime(Runtime::kMath_log, 1); | |
| 2855 context()->Plug(r0); | |
| 2856 } | |
| 2857 | |
| 2858 | 3100 |
| 2859 void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { | 3101 void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { |
| 2860 ASSERT(args->length() >= 2); | 3102 ASSERT(args->length() >= 2); |
| 2861 | 3103 |
| 2862 int arg_count = args->length() - 2; // For receiver and function. | 3104 int arg_count = args->length() - 2; // For receiver and function. |
| 2863 VisitForStackValue(args->at(0)); // Receiver. | 3105 VisitForStackValue(args->at(0)); // Receiver. |
| 2864 for (int i = 0; i < arg_count; i++) { | 3106 for (int i = 0; i < arg_count; i++) { |
| 2865 VisitForStackValue(args->at(i + 1)); | 3107 VisitForStackValue(args->at(i + 1)); |
| 2866 } | 3108 } |
| 2867 VisitForAccumulatorValue(args->at(arg_count + 1)); // Function. | 3109 VisitForAccumulatorValue(args->at(arg_count + 1)); // Function. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 2884 __ CallStub(&stub); | 3126 __ CallStub(&stub); |
| 2885 context()->Plug(r0); | 3127 context()->Plug(r0); |
| 2886 } | 3128 } |
| 2887 | 3129 |
| 2888 | 3130 |
| 2889 void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { | 3131 void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { |
| 2890 ASSERT(args->length() == 3); | 3132 ASSERT(args->length() == 3); |
| 2891 VisitForStackValue(args->at(0)); | 3133 VisitForStackValue(args->at(0)); |
| 2892 VisitForStackValue(args->at(1)); | 3134 VisitForStackValue(args->at(1)); |
| 2893 VisitForStackValue(args->at(2)); | 3135 VisitForStackValue(args->at(2)); |
| 3136 Label done; |
| 3137 Label slow_case; |
| 3138 Register object = r0; |
| 3139 Register index1 = r1; |
| 3140 Register index2 = r2; |
| 3141 Register elements = r3; |
| 3142 Register scratch1 = r4; |
| 3143 Register scratch2 = r5; |
| 3144 |
| 3145 __ ldr(object, MemOperand(sp, 2 * kPointerSize)); |
| 3146 // Fetch the map and check if array is in fast case. |
| 3147 // Check that object doesn't require security checks and |
| 3148 // has no indexed interceptor. |
| 3149 __ CompareObjectType(object, scratch1, scratch2, JS_ARRAY_TYPE); |
| 3150 __ b(ne, &slow_case); |
| 3151 // Map is now in scratch1. |
| 3152 |
| 3153 __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset)); |
| 3154 __ tst(scratch2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask)); |
| 3155 __ b(ne, &slow_case); |
| 3156 |
| 3157 // Check the object's elements are in fast case and writable. |
| 3158 __ ldr(elements, FieldMemOperand(object, JSObject::kElementsOffset)); |
| 3159 __ ldr(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset)); |
| 3160 __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); |
| 3161 __ cmp(scratch1, ip); |
| 3162 __ b(ne, &slow_case); |
| 3163 |
| 3164 // Check that both indices are smis. |
| 3165 __ ldr(index1, MemOperand(sp, 1 * kPointerSize)); |
| 3166 __ ldr(index2, MemOperand(sp, 0)); |
| 3167 __ JumpIfNotBothSmi(index1, index2, &slow_case); |
| 3168 |
| 3169 // Check that both indices are valid. |
| 3170 __ ldr(scratch1, FieldMemOperand(object, JSArray::kLengthOffset)); |
| 3171 __ cmp(scratch1, index1); |
| 3172 __ cmp(scratch1, index2, hi); |
| 3173 __ b(ls, &slow_case); |
| 3174 |
| 3175 // Bring the address of the elements into index1 and index2. |
| 3176 __ add(scratch1, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
| 3177 __ add(index1, |
| 3178 scratch1, |
| 3179 Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize)); |
| 3180 __ add(index2, |
| 3181 scratch1, |
| 3182 Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize)); |
| 3183 |
| 3184 // Swap elements. |
| 3185 __ ldr(scratch1, MemOperand(index1, 0)); |
| 3186 __ ldr(scratch2, MemOperand(index2, 0)); |
| 3187 __ str(scratch1, MemOperand(index2, 0)); |
| 3188 __ str(scratch2, MemOperand(index1, 0)); |
| 3189 |
| 3190 Label new_space; |
| 3191 __ InNewSpace(elements, scratch1, eq, &new_space); |
| 3192 // Possible optimization: do a check that both values are Smis |
| 3193 // (or them and test against Smi mask.) |
| 3194 |
| 3195 __ mov(scratch1, elements); |
| 3196 __ RecordWriteHelper(elements, index1, scratch2); |
| 3197 __ RecordWriteHelper(scratch1, index2, scratch2); // scratch1 holds elements. |
| 3198 |
| 3199 __ bind(&new_space); |
| 3200 // We are done. Drop elements from the stack, and return undefined. |
| 3201 __ Drop(3); |
| 3202 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
| 3203 __ jmp(&done); |
| 3204 |
| 3205 __ bind(&slow_case); |
| 2894 __ CallRuntime(Runtime::kSwapElements, 3); | 3206 __ CallRuntime(Runtime::kSwapElements, 3); |
| 3207 |
| 3208 __ bind(&done); |
| 2895 context()->Plug(r0); | 3209 context()->Plug(r0); |
| 2896 } | 3210 } |
| 2897 | 3211 |
| 2898 | 3212 |
| 2899 void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { | 3213 void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { |
| 2900 ASSERT_EQ(2, args->length()); | 3214 ASSERT_EQ(2, args->length()); |
| 2901 | 3215 |
| 2902 ASSERT_NE(NULL, args->at(0)->AsLiteral()); | 3216 ASSERT_NE(NULL, args->at(0)->AsLiteral()); |
| 2903 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); | 3217 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); |
| 2904 | 3218 |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3003 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); | 3317 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); |
| 3004 Split(eq, if_true, if_false, fall_through); | 3318 Split(eq, if_true, if_false, fall_through); |
| 3005 | 3319 |
| 3006 context()->Plug(if_true, if_false); | 3320 context()->Plug(if_true, if_false); |
| 3007 } | 3321 } |
| 3008 | 3322 |
| 3009 | 3323 |
| 3010 void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { | 3324 void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { |
| 3011 ASSERT(args->length() == 1); | 3325 ASSERT(args->length() == 1); |
| 3012 VisitForAccumulatorValue(args->at(0)); | 3326 VisitForAccumulatorValue(args->at(0)); |
| 3327 |
| 3328 if (FLAG_debug_code) { |
| 3329 __ AbortIfNotString(r0); |
| 3330 } |
| 3331 |
| 3013 __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset)); | 3332 __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset)); |
| 3014 __ IndexFromHash(r0, r0); | 3333 __ IndexFromHash(r0, r0); |
| 3334 |
| 3015 context()->Plug(r0); | 3335 context()->Plug(r0); |
| 3016 } | 3336 } |
| 3017 | 3337 |
| 3018 | 3338 |
| 3019 void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { | 3339 void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { |
| 3340 Label bailout, done, one_char_separator, long_separator, |
| 3341 non_trivial_array, not_size_one_array, loop, |
| 3342 empty_separator_loop, one_char_separator_loop, |
| 3343 one_char_separator_loop_entry, long_separator_loop; |
| 3344 |
| 3345 ASSERT(args->length() == 2); |
| 3346 VisitForStackValue(args->at(1)); |
| 3347 VisitForAccumulatorValue(args->at(0)); |
| 3348 |
| 3349 // All aliases of the same register have disjoint lifetimes. |
| 3350 Register array = r0; |
| 3351 Register elements = no_reg; // Will be r0. |
| 3352 Register result = no_reg; // Will be r0. |
| 3353 Register separator = r1; |
| 3354 Register array_length = r2; |
| 3355 Register result_pos = no_reg; // Will be r2 |
| 3356 Register string_length = r3; |
| 3357 Register string = r4; |
| 3358 Register element = r5; |
| 3359 Register elements_end = r6; |
| 3360 Register scratch1 = r7; |
| 3361 Register scratch2 = r9; |
| 3362 |
| 3363 // Separator operand is on the stack. |
| 3364 __ pop(separator); |
| 3365 |
| 3366 // Check that the array is a JSArray. |
| 3367 __ JumpIfSmi(array, &bailout); |
| 3368 __ CompareObjectType(array, scratch1, scratch2, JS_ARRAY_TYPE); |
| 3369 __ b(ne, &bailout); |
| 3370 |
| 3371 // Check that the array has fast elements. |
| 3372 __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset)); |
| 3373 __ tst(scratch2, Operand(1 << Map::kHasFastElements)); |
| 3374 __ b(eq, &bailout); |
| 3375 |
| 3376 // If the array has length zero, return the empty string. |
| 3377 __ ldr(array_length, FieldMemOperand(array, JSArray::kLengthOffset)); |
| 3378 __ SmiUntag(array_length, SetCC); |
| 3379 __ b(ne, &non_trivial_array); |
| 3380 __ LoadRoot(r0, Heap::kEmptyStringRootIndex); |
| 3381 __ b(&done); |
| 3382 |
| 3383 __ bind(&non_trivial_array); |
| 3384 |
| 3385 // Get the FixedArray containing array's elements. |
| 3386 elements = array; |
| 3387 __ ldr(elements, FieldMemOperand(array, JSArray::kElementsOffset)); |
| 3388 array = no_reg; // End of array's live range. |
| 3389 |
| 3390 // Check that all array elements are sequential ASCII strings, and |
| 3391 // accumulate the sum of their lengths, as a smi-encoded value. |
| 3392 __ mov(string_length, Operand(0)); |
| 3393 __ add(element, |
| 3394 elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
| 3395 __ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2)); |
| 3396 // Loop condition: while (element < elements_end). |
| 3397 // Live values in registers: |
| 3398 // elements: Fixed array of strings. |
| 3399 // array_length: Length of the fixed array of strings (not smi) |
| 3400 // separator: Separator string |
| 3401 // string_length: Accumulated sum of string lengths (smi). |
| 3402 // element: Current array element. |
| 3403 // elements_end: Array end. |
| 3404 if (FLAG_debug_code) { |
| 3405 __ cmp(array_length, Operand(0)); |
| 3406 __ Assert(gt, "No empty arrays here in EmitFastAsciiArrayJoin"); |
| 3407 } |
| 3408 __ bind(&loop); |
| 3409 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3410 __ JumpIfSmi(string, &bailout); |
| 3411 __ ldr(scratch1, FieldMemOperand(string, HeapObject::kMapOffset)); |
| 3412 __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); |
| 3413 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout); |
| 3414 __ ldr(scratch1, FieldMemOperand(string, SeqAsciiString::kLengthOffset)); |
| 3415 __ add(string_length, string_length, Operand(scratch1)); |
| 3416 __ b(vs, &bailout); |
| 3417 __ cmp(element, elements_end); |
| 3418 __ b(lt, &loop); |
| 3419 |
| 3420 // If array_length is 1, return elements[0], a string. |
| 3421 __ cmp(array_length, Operand(1)); |
| 3422 __ b(ne, ¬_size_one_array); |
| 3423 __ ldr(r0, FieldMemOperand(elements, FixedArray::kHeaderSize)); |
| 3424 __ b(&done); |
| 3425 |
| 3426 __ bind(¬_size_one_array); |
| 3427 |
| 3428 // Live values in registers: |
| 3429 // separator: Separator string |
| 3430 // array_length: Length of the array. |
| 3431 // string_length: Sum of string lengths (smi). |
| 3432 // elements: FixedArray of strings. |
| 3433 |
| 3434 // Check that the separator is a flat ASCII string. |
| 3435 __ JumpIfSmi(separator, &bailout); |
| 3436 __ ldr(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset)); |
| 3437 __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); |
| 3438 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout); |
| 3439 |
| 3440 // Add (separator length times array_length) - separator length to the |
| 3441 // string_length to get the length of the result string. array_length is not |
| 3442 // smi but the other values are, so the result is a smi |
| 3443 __ ldr(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset)); |
| 3444 __ sub(string_length, string_length, Operand(scratch1)); |
| 3445 __ smull(scratch2, ip, array_length, scratch1); |
| 3446 // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are |
| 3447 // zero. |
| 3448 __ cmp(ip, Operand(0)); |
| 3449 __ b(ne, &bailout); |
| 3450 __ tst(scratch2, Operand(0x80000000)); |
| 3451 __ b(ne, &bailout); |
| 3452 __ add(string_length, string_length, Operand(scratch2)); |
| 3453 __ b(vs, &bailout); |
| 3454 __ SmiUntag(string_length); |
| 3455 |
| 3456 // Get first element in the array to free up the elements register to be used |
| 3457 // for the result. |
| 3458 __ add(element, |
| 3459 elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
| 3460 result = elements; // End of live range for elements. |
| 3461 elements = no_reg; |
| 3462 // Live values in registers: |
| 3463 // element: First array element |
| 3464 // separator: Separator string |
| 3465 // string_length: Length of result string (not smi) |
| 3466 // array_length: Length of the array. |
| 3467 __ AllocateAsciiString(result, |
| 3468 string_length, |
| 3469 scratch1, |
| 3470 scratch2, |
| 3471 elements_end, |
| 3472 &bailout); |
| 3473 // Prepare for looping. Set up elements_end to end of the array. Set |
| 3474 // result_pos to the position of the result where to write the first |
| 3475 // character. |
| 3476 __ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2)); |
| 3477 result_pos = array_length; // End of live range for array_length. |
| 3478 array_length = no_reg; |
| 3479 __ add(result_pos, |
| 3480 result, |
| 3481 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3482 |
| 3483 // Check the length of the separator. |
| 3484 __ ldr(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset)); |
| 3485 __ cmp(scratch1, Operand(Smi::FromInt(1))); |
| 3486 __ b(eq, &one_char_separator); |
| 3487 __ b(gt, &long_separator); |
| 3488 |
| 3489 // Empty separator case |
| 3490 __ bind(&empty_separator_loop); |
| 3491 // Live values in registers: |
| 3492 // result_pos: the position to which we are currently copying characters. |
| 3493 // element: Current array element. |
| 3494 // elements_end: Array end. |
| 3495 |
| 3496 // Copy next array element to the result. |
| 3497 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3498 __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset)); |
| 3499 __ SmiUntag(string_length); |
| 3500 __ add(string, string, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3501 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3502 __ cmp(element, elements_end); |
| 3503 __ b(lt, &empty_separator_loop); // End while (element < elements_end). |
| 3504 ASSERT(result.is(r0)); |
| 3505 __ b(&done); |
| 3506 |
| 3507 // One-character separator case |
| 3508 __ bind(&one_char_separator); |
| 3509 // Replace separator with its ascii character value. |
| 3510 __ ldrb(separator, FieldMemOperand(separator, SeqAsciiString::kHeaderSize)); |
| 3511 // Jump into the loop after the code that copies the separator, so the first |
| 3512 // element is not preceded by a separator |
| 3513 __ jmp(&one_char_separator_loop_entry); |
| 3514 |
| 3515 __ bind(&one_char_separator_loop); |
| 3516 // Live values in registers: |
| 3517 // result_pos: the position to which we are currently copying characters. |
| 3518 // element: Current array element. |
| 3519 // elements_end: Array end. |
| 3520 // separator: Single separator ascii char (in lower byte). |
| 3521 |
| 3522 // Copy the separator character to the result. |
| 3523 __ strb(separator, MemOperand(result_pos, 1, PostIndex)); |
| 3524 |
| 3525 // Copy next array element to the result. |
| 3526 __ bind(&one_char_separator_loop_entry); |
| 3527 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3528 __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset)); |
| 3529 __ SmiUntag(string_length); |
| 3530 __ add(string, string, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3531 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3532 __ cmp(element, elements_end); |
| 3533 __ b(lt, &one_char_separator_loop); // End while (element < elements_end). |
| 3534 ASSERT(result.is(r0)); |
| 3535 __ b(&done); |
| 3536 |
| 3537 // Long separator case (separator is more than one character). Entry is at the |
| 3538 // label long_separator below. |
| 3539 __ bind(&long_separator_loop); |
| 3540 // Live values in registers: |
| 3541 // result_pos: the position to which we are currently copying characters. |
| 3542 // element: Current array element. |
| 3543 // elements_end: Array end. |
| 3544 // separator: Separator string. |
| 3545 |
| 3546 // Copy the separator to the result. |
| 3547 __ ldr(string_length, FieldMemOperand(separator, String::kLengthOffset)); |
| 3548 __ SmiUntag(string_length); |
| 3549 __ add(string, |
| 3550 separator, |
| 3551 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3552 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3553 |
| 3554 __ bind(&long_separator); |
| 3555 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3556 __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset)); |
| 3557 __ SmiUntag(string_length); |
| 3558 __ add(string, string, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3559 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3560 __ cmp(element, elements_end); |
| 3561 __ b(lt, &long_separator_loop); // End while (element < elements_end). |
| 3562 ASSERT(result.is(r0)); |
| 3563 __ b(&done); |
| 3564 |
| 3565 __ bind(&bailout); |
| 3020 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); | 3566 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
| 3567 __ bind(&done); |
| 3021 context()->Plug(r0); | 3568 context()->Plug(r0); |
| 3022 return; | |
| 3023 } | 3569 } |
| 3024 | 3570 |
| 3025 | 3571 |
| 3026 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { | 3572 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { |
| 3027 Handle<String> name = expr->name(); | 3573 Handle<String> name = expr->name(); |
| 3028 if (name->length() > 0 && name->Get(0) == '_') { | 3574 if (name->length() > 0 && name->Get(0) == '_') { |
| 3029 Comment cmnt(masm_, "[ InlineRuntimeCall"); | 3575 Comment cmnt(masm_, "[ InlineRuntimeCall"); |
| 3030 EmitInlineRuntimeCall(expr); | 3576 EmitInlineRuntimeCall(expr); |
| 3031 return; | 3577 return; |
| 3032 } | 3578 } |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3077 } else { | 3623 } else { |
| 3078 VisitForStackValue(prop->obj()); | 3624 VisitForStackValue(prop->obj()); |
| 3079 VisitForStackValue(prop->key()); | 3625 VisitForStackValue(prop->key()); |
| 3080 __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); | 3626 __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); |
| 3081 __ push(r1); | 3627 __ push(r1); |
| 3082 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); | 3628 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); |
| 3083 context()->Plug(r0); | 3629 context()->Plug(r0); |
| 3084 } | 3630 } |
| 3085 } else if (var != NULL) { | 3631 } else if (var != NULL) { |
| 3086 // Delete of an unqualified identifier is disallowed in strict mode | 3632 // Delete of an unqualified identifier is disallowed in strict mode |
| 3087 // so this code can only be reached in non-strict mode. | 3633 // but "delete this" is. |
| 3088 ASSERT(strict_mode_flag() == kNonStrictMode); | 3634 ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); |
| 3089 if (var->is_global()) { | 3635 if (var->is_global()) { |
| 3090 __ ldr(r2, GlobalObjectOperand()); | 3636 __ ldr(r2, GlobalObjectOperand()); |
| 3091 __ mov(r1, Operand(var->name())); | 3637 __ mov(r1, Operand(var->name())); |
| 3092 __ mov(r0, Operand(Smi::FromInt(kNonStrictMode))); | 3638 __ mov(r0, Operand(Smi::FromInt(kNonStrictMode))); |
| 3093 __ Push(r2, r1, r0); | 3639 __ Push(r2, r1, r0); |
| 3094 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); | 3640 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); |
| 3095 context()->Plug(r0); | 3641 context()->Plug(r0); |
| 3096 } else if (var->AsSlot() != NULL && | 3642 } else if (var->AsSlot() != NULL && |
| 3097 var->AsSlot()->type() != Slot::LOOKUP) { | 3643 var->AsSlot()->type() != Slot::LOOKUP) { |
| 3098 // Result of deleting non-global, non-dynamic variables is false. | 3644 // Result of deleting non-global, non-dynamic variables is false. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 3118 | 3664 |
| 3119 case Token::VOID: { | 3665 case Token::VOID: { |
| 3120 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); | 3666 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); |
| 3121 VisitForEffect(expr->expression()); | 3667 VisitForEffect(expr->expression()); |
| 3122 context()->Plug(Heap::kUndefinedValueRootIndex); | 3668 context()->Plug(Heap::kUndefinedValueRootIndex); |
| 3123 break; | 3669 break; |
| 3124 } | 3670 } |
| 3125 | 3671 |
| 3126 case Token::NOT: { | 3672 case Token::NOT: { |
| 3127 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); | 3673 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); |
| 3128 Label materialize_true, materialize_false; | 3674 if (context()->IsEffect()) { |
| 3129 Label* if_true = NULL; | 3675 // Unary NOT has no side effects so it's only necessary to visit the |
| 3130 Label* if_false = NULL; | 3676 // subexpression. Match the optimizing compiler by not branching. |
| 3131 Label* fall_through = NULL; | 3677 VisitForEffect(expr->expression()); |
| 3678 } else { |
| 3679 Label materialize_true, materialize_false; |
| 3680 Label* if_true = NULL; |
| 3681 Label* if_false = NULL; |
| 3682 Label* fall_through = NULL; |
| 3132 | 3683 |
| 3133 // Notice that the labels are swapped. | 3684 // Notice that the labels are swapped. |
| 3134 context()->PrepareTest(&materialize_true, &materialize_false, | 3685 context()->PrepareTest(&materialize_true, &materialize_false, |
| 3135 &if_false, &if_true, &fall_through); | 3686 &if_false, &if_true, &fall_through); |
| 3136 if (context()->IsTest()) ForwardBailoutToChild(expr); | 3687 if (context()->IsTest()) ForwardBailoutToChild(expr); |
| 3137 VisitForControl(expr->expression(), if_true, if_false, fall_through); | 3688 VisitForControl(expr->expression(), if_true, if_false, fall_through); |
| 3138 context()->Plug(if_false, if_true); // Labels swapped. | 3689 context()->Plug(if_false, if_true); // Labels swapped. |
| 3690 } |
| 3139 break; | 3691 break; |
| 3140 } | 3692 } |
| 3141 | 3693 |
| 3142 case Token::TYPEOF: { | 3694 case Token::TYPEOF: { |
| 3143 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); | 3695 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); |
| 3144 { StackValueContext context(this); | 3696 { StackValueContext context(this); |
| 3145 VisitForTypeofValue(expr->expression()); | 3697 VisitForTypeofValue(expr->expression()); |
| 3146 } | 3698 } |
| 3147 __ CallRuntime(Runtime::kTypeof, 1); | 3699 __ CallRuntime(Runtime::kTypeof, 1); |
| 3148 context()->Plug(r0); | 3700 context()->Plug(r0); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 3160 __ bind(&no_conversion); | 3712 __ bind(&no_conversion); |
| 3161 context()->Plug(result_register()); | 3713 context()->Plug(result_register()); |
| 3162 break; | 3714 break; |
| 3163 } | 3715 } |
| 3164 | 3716 |
| 3165 case Token::SUB: { | 3717 case Token::SUB: { |
| 3166 Comment cmt(masm_, "[ UnaryOperation (SUB)"); | 3718 Comment cmt(masm_, "[ UnaryOperation (SUB)"); |
| 3167 bool can_overwrite = expr->expression()->ResultOverwriteAllowed(); | 3719 bool can_overwrite = expr->expression()->ResultOverwriteAllowed(); |
| 3168 UnaryOverwriteMode overwrite = | 3720 UnaryOverwriteMode overwrite = |
| 3169 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; | 3721 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; |
| 3170 GenericUnaryOpStub stub(Token::SUB, | 3722 GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS); |
| 3171 overwrite, | |
| 3172 NO_UNARY_FLAGS); | |
| 3173 // GenericUnaryOpStub expects the argument to be in the | 3723 // GenericUnaryOpStub expects the argument to be in the |
| 3174 // accumulator register r0. | 3724 // accumulator register r0. |
| 3175 VisitForAccumulatorValue(expr->expression()); | 3725 VisitForAccumulatorValue(expr->expression()); |
| 3176 __ CallStub(&stub); | 3726 __ CallStub(&stub); |
| 3177 context()->Plug(r0); | 3727 context()->Plug(r0); |
| 3178 break; | 3728 break; |
| 3179 } | 3729 } |
| 3180 | 3730 |
| 3181 case Token::BIT_NOT: { | 3731 case Token::BIT_NOT: { |
| 3182 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)"); | 3732 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)"); |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3295 case KEYED_PROPERTY: | 3845 case KEYED_PROPERTY: |
| 3296 __ str(r0, MemOperand(sp, 2 * kPointerSize)); | 3846 __ str(r0, MemOperand(sp, 2 * kPointerSize)); |
| 3297 break; | 3847 break; |
| 3298 } | 3848 } |
| 3299 } | 3849 } |
| 3300 } | 3850 } |
| 3301 | 3851 |
| 3302 | 3852 |
| 3303 // Inline smi case if we are in a loop. | 3853 // Inline smi case if we are in a loop. |
| 3304 Label stub_call, done; | 3854 Label stub_call, done; |
| 3855 JumpPatchSite patch_site(masm_); |
| 3856 |
| 3305 int count_value = expr->op() == Token::INC ? 1 : -1; | 3857 int count_value = expr->op() == Token::INC ? 1 : -1; |
| 3306 if (ShouldInlineSmiCase(expr->op())) { | 3858 if (ShouldInlineSmiCase(expr->op())) { |
| 3307 __ add(r0, r0, Operand(Smi::FromInt(count_value)), SetCC); | 3859 __ add(r0, r0, Operand(Smi::FromInt(count_value)), SetCC); |
| 3308 __ b(vs, &stub_call); | 3860 __ b(vs, &stub_call); |
| 3309 // We could eliminate this smi check if we split the code at | 3861 // We could eliminate this smi check if we split the code at |
| 3310 // the first smi check before calling ToNumber. | 3862 // the first smi check before calling ToNumber. |
| 3311 __ JumpIfSmi(r0, &done); | 3863 patch_site.EmitJumpIfSmi(r0, &done); |
| 3864 |
| 3312 __ bind(&stub_call); | 3865 __ bind(&stub_call); |
| 3313 // Call stub. Undo operation first. | 3866 // Call stub. Undo operation first. |
| 3314 __ sub(r0, r0, Operand(Smi::FromInt(count_value))); | 3867 __ sub(r0, r0, Operand(Smi::FromInt(count_value))); |
| 3315 } | 3868 } |
| 3316 __ mov(r1, Operand(Smi::FromInt(count_value))); | 3869 __ mov(r1, Operand(Smi::FromInt(count_value))); |
| 3317 | 3870 |
| 3318 // Record position before stub call. | 3871 // Record position before stub call. |
| 3319 SetSourcePosition(expr->position()); | 3872 SetSourcePosition(expr->position()); |
| 3320 | 3873 |
| 3321 GenericBinaryOpStub stub(Token::ADD, NO_OVERWRITE, r1, r0); | 3874 TypeRecordingBinaryOpStub stub(Token::ADD, NO_OVERWRITE); |
| 3322 __ CallStub(&stub); | 3875 EmitCallIC(stub.GetCode(), &patch_site); |
| 3323 __ bind(&done); | 3876 __ bind(&done); |
| 3324 | 3877 |
| 3325 // Store the value returned in r0. | 3878 // Store the value returned in r0. |
| 3326 switch (assign_type) { | 3879 switch (assign_type) { |
| 3327 case VARIABLE: | 3880 case VARIABLE: |
| 3328 if (expr->is_postfix()) { | 3881 if (expr->is_postfix()) { |
| 3329 { EffectContext context(this); | 3882 { EffectContext context(this); |
| 3330 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | 3883 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), |
| 3331 Token::ASSIGN); | 3884 Token::ASSIGN); |
| 3332 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3885 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3333 context.Plug(r0); | 3886 context.Plug(r0); |
| 3334 } | 3887 } |
| 3335 // For all contexts except EffectConstant We have the result on | 3888 // For all contexts except EffectConstant We have the result on |
| 3336 // top of the stack. | 3889 // top of the stack. |
| 3337 if (!context()->IsEffect()) { | 3890 if (!context()->IsEffect()) { |
| 3338 context()->PlugTOS(); | 3891 context()->PlugTOS(); |
| 3339 } | 3892 } |
| 3340 } else { | 3893 } else { |
| 3341 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | 3894 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), |
| 3342 Token::ASSIGN); | 3895 Token::ASSIGN); |
| 3343 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3896 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3344 context()->Plug(r0); | 3897 context()->Plug(r0); |
| 3345 } | 3898 } |
| 3346 break; | 3899 break; |
| 3347 case NAMED_PROPERTY: { | 3900 case NAMED_PROPERTY: { |
| 3348 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); | 3901 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); |
| 3349 __ pop(r1); | 3902 __ pop(r1); |
| 3350 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 3903 Handle<Code> ic(Builtins::builtin( |
| 3904 is_strict_mode() ? Builtins::StoreIC_Initialize_Strict |
| 3905 : Builtins::StoreIC_Initialize)); |
| 3351 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 3906 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 3352 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3907 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3353 if (expr->is_postfix()) { | 3908 if (expr->is_postfix()) { |
| 3354 if (!context()->IsEffect()) { | 3909 if (!context()->IsEffect()) { |
| 3355 context()->PlugTOS(); | 3910 context()->PlugTOS(); |
| 3356 } | 3911 } |
| 3357 } else { | 3912 } else { |
| 3358 context()->Plug(r0); | 3913 context()->Plug(r0); |
| 3359 } | 3914 } |
| 3360 break; | 3915 break; |
| 3361 } | 3916 } |
| 3362 case KEYED_PROPERTY: { | 3917 case KEYED_PROPERTY: { |
| 3363 __ pop(r1); // Key. | 3918 __ pop(r1); // Key. |
| 3364 __ pop(r2); // Receiver. | 3919 __ pop(r2); // Receiver. |
| 3365 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | 3920 Handle<Code> ic(Builtins::builtin( |
| 3921 is_strict_mode() ? Builtins::KeyedStoreIC_Initialize_Strict |
| 3922 : Builtins::KeyedStoreIC_Initialize)); |
| 3366 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 3923 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 3367 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3924 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3368 if (expr->is_postfix()) { | 3925 if (expr->is_postfix()) { |
| 3369 if (!context()->IsEffect()) { | 3926 if (!context()->IsEffect()) { |
| 3370 context()->PlugTOS(); | 3927 context()->PlugTOS(); |
| 3371 } | 3928 } |
| 3372 } else { | 3929 } else { |
| 3373 context()->Plug(r0); | 3930 context()->Plug(r0); |
| 3374 } | 3931 } |
| 3375 break; | 3932 break; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3433 UnaryOperation* left_unary = left->AsUnaryOperation(); | 3990 UnaryOperation* left_unary = left->AsUnaryOperation(); |
| 3434 if (left_unary == NULL || left_unary->op() != Token::TYPEOF) return false; | 3991 if (left_unary == NULL || left_unary->op() != Token::TYPEOF) return false; |
| 3435 Handle<String> check = Handle<String>::cast(right_literal_value); | 3992 Handle<String> check = Handle<String>::cast(right_literal_value); |
| 3436 | 3993 |
| 3437 { AccumulatorValueContext context(this); | 3994 { AccumulatorValueContext context(this); |
| 3438 VisitForTypeofValue(left_unary->expression()); | 3995 VisitForTypeofValue(left_unary->expression()); |
| 3439 } | 3996 } |
| 3440 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); | 3997 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); |
| 3441 | 3998 |
| 3442 if (check->Equals(Heap::number_symbol())) { | 3999 if (check->Equals(Heap::number_symbol())) { |
| 3443 __ tst(r0, Operand(kSmiTagMask)); | 4000 __ JumpIfSmi(r0, if_true); |
| 3444 __ b(eq, if_true); | |
| 3445 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); | 4001 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); |
| 3446 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex); | 4002 __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex); |
| 3447 __ cmp(r0, ip); | 4003 __ cmp(r0, ip); |
| 3448 Split(eq, if_true, if_false, fall_through); | 4004 Split(eq, if_true, if_false, fall_through); |
| 3449 } else if (check->Equals(Heap::string_symbol())) { | 4005 } else if (check->Equals(Heap::string_symbol())) { |
| 3450 __ tst(r0, Operand(kSmiTagMask)); | 4006 __ JumpIfSmi(r0, if_false); |
| 3451 __ b(eq, if_false); | |
| 3452 // Check for undetectable objects => false. | 4007 // Check for undetectable objects => false. |
| 3453 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); | 4008 __ CompareObjectType(r0, r0, r1, FIRST_NONSTRING_TYPE); |
| 4009 __ b(ge, if_false); |
| 3454 __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset)); | 4010 __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset)); |
| 3455 __ and_(r1, r1, Operand(1 << Map::kIsUndetectable)); | 4011 __ tst(r1, Operand(1 << Map::kIsUndetectable)); |
| 3456 __ cmp(r1, Operand(1 << Map::kIsUndetectable)); | 4012 Split(eq, if_true, if_false, fall_through); |
| 3457 __ b(eq, if_false); | |
| 3458 __ ldrb(r1, FieldMemOperand(r0, Map::kInstanceTypeOffset)); | |
| 3459 __ cmp(r1, Operand(FIRST_NONSTRING_TYPE)); | |
| 3460 Split(lt, if_true, if_false, fall_through); | |
| 3461 } else if (check->Equals(Heap::boolean_symbol())) { | 4013 } else if (check->Equals(Heap::boolean_symbol())) { |
| 3462 __ LoadRoot(ip, Heap::kTrueValueRootIndex); | 4014 __ CompareRoot(r0, Heap::kTrueValueRootIndex); |
| 3463 __ cmp(r0, ip); | |
| 3464 __ b(eq, if_true); | 4015 __ b(eq, if_true); |
| 3465 __ LoadRoot(ip, Heap::kFalseValueRootIndex); | 4016 __ CompareRoot(r0, Heap::kFalseValueRootIndex); |
| 3466 __ cmp(r0, ip); | |
| 3467 Split(eq, if_true, if_false, fall_through); | 4017 Split(eq, if_true, if_false, fall_through); |
| 3468 } else if (check->Equals(Heap::undefined_symbol())) { | 4018 } else if (check->Equals(Heap::undefined_symbol())) { |
| 3469 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); | 4019 __ CompareRoot(r0, Heap::kUndefinedValueRootIndex); |
| 3470 __ cmp(r0, ip); | |
| 3471 __ b(eq, if_true); | 4020 __ b(eq, if_true); |
| 3472 __ tst(r0, Operand(kSmiTagMask)); | 4021 __ JumpIfSmi(r0, if_false); |
| 3473 __ b(eq, if_false); | |
| 3474 // Check for undetectable objects => true. | 4022 // Check for undetectable objects => true. |
| 3475 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); | 4023 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); |
| 3476 __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset)); | 4024 __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset)); |
| 3477 __ and_(r1, r1, Operand(1 << Map::kIsUndetectable)); | 4025 __ tst(r1, Operand(1 << Map::kIsUndetectable)); |
| 3478 __ cmp(r1, Operand(1 << Map::kIsUndetectable)); | 4026 Split(ne, if_true, if_false, fall_through); |
| 4027 |
| 4028 } else if (check->Equals(Heap::function_symbol())) { |
| 4029 __ JumpIfSmi(r0, if_false); |
| 4030 __ CompareObjectType(r0, r1, r0, FIRST_FUNCTION_CLASS_TYPE); |
| 4031 Split(ge, if_true, if_false, fall_through); |
| 4032 |
| 4033 } else if (check->Equals(Heap::object_symbol())) { |
| 4034 __ JumpIfSmi(r0, if_false); |
| 4035 __ CompareRoot(r0, Heap::kNullValueRootIndex); |
| 4036 __ b(eq, if_true); |
| 4037 // Check for JS objects => true. |
| 4038 __ CompareObjectType(r0, r0, r1, FIRST_JS_OBJECT_TYPE); |
| 4039 __ b(lo, if_false); |
| 4040 __ CompareInstanceType(r0, r1, FIRST_FUNCTION_CLASS_TYPE); |
| 4041 __ b(hs, if_false); |
| 4042 // Check for undetectable objects => false. |
| 4043 __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset)); |
| 4044 __ tst(r1, Operand(1 << Map::kIsUndetectable)); |
| 3479 Split(eq, if_true, if_false, fall_through); | 4045 Split(eq, if_true, if_false, fall_through); |
| 3480 } else if (check->Equals(Heap::function_symbol())) { | |
| 3481 __ tst(r0, Operand(kSmiTagMask)); | |
| 3482 __ b(eq, if_false); | |
| 3483 __ CompareObjectType(r0, r1, r0, JS_FUNCTION_TYPE); | |
| 3484 __ b(eq, if_true); | |
| 3485 // Regular expressions => 'function' (they are callable). | |
| 3486 __ CompareInstanceType(r1, r0, JS_REGEXP_TYPE); | |
| 3487 Split(eq, if_true, if_false, fall_through); | |
| 3488 } else if (check->Equals(Heap::object_symbol())) { | |
| 3489 __ tst(r0, Operand(kSmiTagMask)); | |
| 3490 __ b(eq, if_false); | |
| 3491 __ LoadRoot(ip, Heap::kNullValueRootIndex); | |
| 3492 __ cmp(r0, ip); | |
| 3493 __ b(eq, if_true); | |
| 3494 // Regular expressions => 'function', not 'object'. | |
| 3495 __ CompareObjectType(r0, r1, r0, JS_REGEXP_TYPE); | |
| 3496 __ b(eq, if_false); | |
| 3497 // Check for undetectable objects => false. | |
| 3498 __ ldrb(r0, FieldMemOperand(r1, Map::kBitFieldOffset)); | |
| 3499 __ and_(r0, r0, Operand(1 << Map::kIsUndetectable)); | |
| 3500 __ cmp(r0, Operand(1 << Map::kIsUndetectable)); | |
| 3501 __ b(eq, if_false); | |
| 3502 // Check for JS objects => true. | |
| 3503 __ ldrb(r0, FieldMemOperand(r1, Map::kInstanceTypeOffset)); | |
| 3504 __ cmp(r0, Operand(FIRST_JS_OBJECT_TYPE)); | |
| 3505 __ b(lt, if_false); | |
| 3506 __ cmp(r0, Operand(LAST_JS_OBJECT_TYPE)); | |
| 3507 Split(le, if_true, if_false, fall_through); | |
| 3508 } else { | 4046 } else { |
| 3509 if (if_false != fall_through) __ jmp(if_false); | 4047 if (if_false != fall_through) __ jmp(if_false); |
| 3510 } | 4048 } |
| 3511 | 4049 |
| 3512 return true; | 4050 return true; |
| 3513 } | 4051 } |
| 3514 | 4052 |
| 3515 | 4053 |
| 3516 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { | 4054 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { |
| 3517 Comment cmnt(masm_, "[ CompareOperation"); | 4055 Comment cmnt(masm_, "[ CompareOperation"); |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3669 | 4207 |
| 3670 | 4208 |
| 3671 Register FullCodeGenerator::context_register() { | 4209 Register FullCodeGenerator::context_register() { |
| 3672 return cp; | 4210 return cp; |
| 3673 } | 4211 } |
| 3674 | 4212 |
| 3675 | 4213 |
| 3676 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) { | 4214 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) { |
| 3677 ASSERT(mode == RelocInfo::CODE_TARGET || | 4215 ASSERT(mode == RelocInfo::CODE_TARGET || |
| 3678 mode == RelocInfo::CODE_TARGET_CONTEXT); | 4216 mode == RelocInfo::CODE_TARGET_CONTEXT); |
| 4217 switch (ic->kind()) { |
| 4218 case Code::LOAD_IC: |
| 4219 __ IncrementCounter(&Counters::named_load_full, 1, r1, r2); |
| 4220 break; |
| 4221 case Code::KEYED_LOAD_IC: |
| 4222 __ IncrementCounter(&Counters::keyed_load_full, 1, r1, r2); |
| 4223 break; |
| 4224 case Code::STORE_IC: |
| 4225 __ IncrementCounter(&Counters::named_store_full, 1, r1, r2); |
| 4226 break; |
| 4227 case Code::KEYED_STORE_IC: |
| 4228 __ IncrementCounter(&Counters::keyed_store_full, 1, r1, r2); |
| 4229 default: |
| 4230 break; |
| 4231 } |
| 4232 |
| 3679 __ Call(ic, mode); | 4233 __ Call(ic, mode); |
| 3680 } | 4234 } |
| 3681 | 4235 |
| 3682 | 4236 |
| 3683 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) { | 4237 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) { |
| 4238 switch (ic->kind()) { |
| 4239 case Code::LOAD_IC: |
| 4240 __ IncrementCounter(&Counters::named_load_full, 1, r1, r2); |
| 4241 break; |
| 4242 case Code::KEYED_LOAD_IC: |
| 4243 __ IncrementCounter(&Counters::keyed_load_full, 1, r1, r2); |
| 4244 break; |
| 4245 case Code::STORE_IC: |
| 4246 __ IncrementCounter(&Counters::named_store_full, 1, r1, r2); |
| 4247 break; |
| 4248 case Code::KEYED_STORE_IC: |
| 4249 __ IncrementCounter(&Counters::keyed_store_full, 1, r1, r2); |
| 4250 default: |
| 4251 break; |
| 4252 } |
| 4253 |
| 3684 __ Call(ic, RelocInfo::CODE_TARGET); | 4254 __ Call(ic, RelocInfo::CODE_TARGET); |
| 3685 if (patch_site != NULL && patch_site->is_bound()) { | 4255 if (patch_site != NULL && patch_site->is_bound()) { |
| 3686 patch_site->EmitPatchInfo(); | 4256 patch_site->EmitPatchInfo(); |
| 3687 } else { | 4257 } else { |
| 3688 __ nop(); // Signals no inlined code. | 4258 __ nop(); // Signals no inlined code. |
| 3689 } | 4259 } |
| 3690 } | 4260 } |
| 3691 | 4261 |
| 3692 | 4262 |
| 3693 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { | 4263 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3727 __ mov(r1, Operand(r1, ASR, 1)); // Un-smi-tag value. | 4297 __ mov(r1, Operand(r1, ASR, 1)); // Un-smi-tag value. |
| 3728 __ add(pc, r1, Operand(masm_->CodeObject())); | 4298 __ add(pc, r1, Operand(masm_->CodeObject())); |
| 3729 } | 4299 } |
| 3730 | 4300 |
| 3731 | 4301 |
| 3732 #undef __ | 4302 #undef __ |
| 3733 | 4303 |
| 3734 } } // namespace v8::internal | 4304 } } // namespace v8::internal |
| 3735 | 4305 |
| 3736 #endif // V8_TARGET_ARCH_ARM | 4306 #endif // V8_TARGET_ARCH_ARM |
| OLD | NEW |