| 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 27 matching lines...) Expand all Loading... |
| 38 #include "scopes.h" | 38 #include "scopes.h" |
| 39 #include "stub-cache.h" | 39 #include "stub-cache.h" |
| 40 | 40 |
| 41 #include "arm/code-stubs-arm.h" | 41 #include "arm/code-stubs-arm.h" |
| 42 | 42 |
| 43 namespace v8 { | 43 namespace v8 { |
| 44 namespace internal { | 44 namespace internal { |
| 45 | 45 |
| 46 #define __ ACCESS_MASM(masm_) | 46 #define __ ACCESS_MASM(masm_) |
| 47 | 47 |
| 48 |
| 49 // A patch site is a location in the code which it is possible to patch. This |
| 50 // class has a number of methods to emit the code which is patchable and the |
| 51 // method EmitPatchInfo to record a marker back to the patchable code. This |
| 52 // marker is a cmp rx, #yyy instruction, and x * 0x00000fff + yyy (raw 12 bit |
| 53 // immediate value is used) is the delta from the pc to the first instruction of |
| 54 // the patchable code. |
| 55 class JumpPatchSite BASE_EMBEDDED { |
| 56 public: |
| 57 explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { |
| 58 #ifdef DEBUG |
| 59 info_emitted_ = false; |
| 60 #endif |
| 61 } |
| 62 |
| 63 ~JumpPatchSite() { |
| 64 ASSERT(patch_site_.is_bound() == info_emitted_); |
| 65 } |
| 66 |
| 67 // When initially emitting this ensure that a jump is always generated to skip |
| 68 // the inlined smi code. |
| 69 void EmitJumpIfNotSmi(Register reg, Label* target) { |
| 70 ASSERT(!patch_site_.is_bound() && !info_emitted_); |
| 71 __ bind(&patch_site_); |
| 72 __ cmp(reg, Operand(reg)); |
| 73 // Don't use b(al, ...) as that might emit the constant pool right after the |
| 74 // branch. After patching when the branch is no longer unconditional |
| 75 // execution can continue into the constant pool. |
| 76 __ b(eq, target); // Always taken before patched. |
| 77 } |
| 78 |
| 79 // When initially emitting this ensure that a jump is never generated to skip |
| 80 // the inlined smi code. |
| 81 void EmitJumpIfSmi(Register reg, Label* target) { |
| 82 ASSERT(!patch_site_.is_bound() && !info_emitted_); |
| 83 __ bind(&patch_site_); |
| 84 __ cmp(reg, Operand(reg)); |
| 85 __ b(ne, target); // Never taken before patched. |
| 86 } |
| 87 |
| 88 void EmitPatchInfo() { |
| 89 int delta_to_patch_site = masm_->InstructionsGeneratedSince(&patch_site_); |
| 90 Register reg; |
| 91 reg.set_code(delta_to_patch_site / kOff12Mask); |
| 92 __ cmp_raw_immediate(reg, delta_to_patch_site % kOff12Mask); |
| 93 #ifdef DEBUG |
| 94 info_emitted_ = true; |
| 95 #endif |
| 96 } |
| 97 |
| 98 bool is_bound() const { return patch_site_.is_bound(); } |
| 99 |
| 100 private: |
| 101 MacroAssembler* masm_; |
| 102 Label patch_site_; |
| 103 #ifdef DEBUG |
| 104 bool info_emitted_; |
| 105 #endif |
| 106 }; |
| 107 |
| 108 |
| 48 // Generate code for a JS function. On entry to the function the receiver | 109 // Generate code for a JS function. On entry to the function the receiver |
| 49 // and arguments have been pushed on the stack left to right. The actual | 110 // and arguments have been pushed on the stack left to right. The actual |
| 50 // argument count matches the formal parameter count expected by the | 111 // argument count matches the formal parameter count expected by the |
| 51 // function. | 112 // function. |
| 52 // | 113 // |
| 53 // The live registers are: | 114 // The live registers are: |
| 54 // o r1: the JS function object being called (ie, ourselves) | 115 // o r1: the JS function object being called (ie, ourselves) |
| 55 // o cp: our context | 116 // o cp: our context |
| 56 // o fp: our caller's frame pointer | 117 // o fp: our caller's frame pointer |
| 57 // o sp: stack pointer | 118 // o sp: stack pointer |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 // stack frame was an arguments adapter frame. | 212 // stack frame was an arguments adapter frame. |
| 152 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); | 213 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); |
| 153 __ CallStub(&stub); | 214 __ CallStub(&stub); |
| 154 // Duplicate the value; move-to-slot operation might clobber registers. | 215 // Duplicate the value; move-to-slot operation might clobber registers. |
| 155 __ mov(r3, r0); | 216 __ mov(r3, r0); |
| 156 Move(arguments->AsSlot(), r0, r1, r2); | 217 Move(arguments->AsSlot(), r0, r1, r2); |
| 157 Slot* dot_arguments_slot = scope()->arguments_shadow()->AsSlot(); | 218 Slot* dot_arguments_slot = scope()->arguments_shadow()->AsSlot(); |
| 158 Move(dot_arguments_slot, r3, r1, r2); | 219 Move(dot_arguments_slot, r3, r1, r2); |
| 159 } | 220 } |
| 160 | 221 |
| 161 { Comment cmnt(masm_, "[ Declarations"); | |
| 162 // For named function expressions, declare the function name as a | |
| 163 // constant. | |
| 164 if (scope()->is_function_scope() && scope()->function() != NULL) { | |
| 165 EmitDeclaration(scope()->function(), Variable::CONST, NULL); | |
| 166 } | |
| 167 // Visit all the explicit declarations unless there is an illegal | |
| 168 // redeclaration. | |
| 169 if (scope()->HasIllegalRedeclaration()) { | |
| 170 scope()->VisitIllegalRedeclaration(this); | |
| 171 } else { | |
| 172 VisitDeclarations(scope()->declarations()); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 if (FLAG_trace) { | 222 if (FLAG_trace) { |
| 177 __ CallRuntime(Runtime::kTraceEnter, 0); | 223 __ CallRuntime(Runtime::kTraceEnter, 0); |
| 178 } | 224 } |
| 179 | 225 |
| 180 // Check the stack for overflow or break request. | 226 // Visit the declarations and body unless there is an illegal |
| 181 { Comment cmnt(masm_, "[ Stack check"); | 227 // redeclaration. |
| 182 PrepareForBailout(info->function(), NO_REGISTERS); | 228 if (scope()->HasIllegalRedeclaration()) { |
| 183 Label ok; | 229 Comment cmnt(masm_, "[ Declarations"); |
| 184 __ LoadRoot(ip, Heap::kStackLimitRootIndex); | 230 scope()->VisitIllegalRedeclaration(this); |
| 185 __ cmp(sp, Operand(ip)); | 231 |
| 186 __ b(hs, &ok); | 232 } else { |
| 187 StackCheckStub stub; | 233 { Comment cmnt(masm_, "[ Declarations"); |
| 188 __ CallStub(&stub); | 234 // For named function expressions, declare the function name as a |
| 189 __ bind(&ok); | 235 // constant. |
| 236 if (scope()->is_function_scope() && scope()->function() != NULL) { |
| 237 EmitDeclaration(scope()->function(), Variable::CONST, NULL); |
| 238 } |
| 239 VisitDeclarations(scope()->declarations()); |
| 240 } |
| 241 |
| 242 { Comment cmnt(masm_, "[ Stack check"); |
| 243 PrepareForBailout(info->function(), NO_REGISTERS); |
| 244 Label ok; |
| 245 __ LoadRoot(ip, Heap::kStackLimitRootIndex); |
| 246 __ cmp(sp, Operand(ip)); |
| 247 __ b(hs, &ok); |
| 248 StackCheckStub stub; |
| 249 __ CallStub(&stub); |
| 250 __ bind(&ok); |
| 251 } |
| 252 |
| 253 { Comment cmnt(masm_, "[ Body"); |
| 254 ASSERT(loop_depth() == 0); |
| 255 VisitStatements(function()->body()); |
| 256 ASSERT(loop_depth() == 0); |
| 257 } |
| 190 } | 258 } |
| 191 | 259 |
| 192 { Comment cmnt(masm_, "[ Body"); | 260 // Always emit a 'return undefined' in case control fell off the end of |
| 193 ASSERT(loop_depth() == 0); | 261 // the body. |
| 194 VisitStatements(function()->body()); | |
| 195 ASSERT(loop_depth() == 0); | |
| 196 } | |
| 197 | |
| 198 { Comment cmnt(masm_, "[ return <undefined>;"); | 262 { Comment cmnt(masm_, "[ return <undefined>;"); |
| 199 // Emit a 'return undefined' in case control fell off the end of the | |
| 200 // body. | |
| 201 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); | 263 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
| 202 } | 264 } |
| 203 EmitReturnSequence(); | 265 EmitReturnSequence(); |
| 204 | 266 |
| 205 // Force emit the constant pool, so it doesn't get emitted in the middle | 267 // Force emit the constant pool, so it doesn't get emitted in the middle |
| 206 // of the stack check table. | 268 // of the stack check table. |
| 207 masm()->CheckConstPool(true, false); | 269 masm()->CheckConstPool(true, false); |
| 208 } | 270 } |
| 209 | 271 |
| 210 | 272 |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 270 #ifdef DEBUG | 332 #ifdef DEBUG |
| 271 // Check that the size of the code used for returning is large enough | 333 // Check that the size of the code used for returning is large enough |
| 272 // for the debugger's requirements. | 334 // for the debugger's requirements. |
| 273 ASSERT(Assembler::kJSReturnSequenceInstructions <= | 335 ASSERT(Assembler::kJSReturnSequenceInstructions <= |
| 274 masm_->InstructionsGeneratedSince(&check_exit_codesize)); | 336 masm_->InstructionsGeneratedSince(&check_exit_codesize)); |
| 275 #endif | 337 #endif |
| 276 } | 338 } |
| 277 } | 339 } |
| 278 | 340 |
| 279 | 341 |
| 280 FullCodeGenerator::ConstantOperand FullCodeGenerator::GetConstantOperand( | |
| 281 Token::Value op, Expression* left, Expression* right) { | |
| 282 ASSERT(ShouldInlineSmiCase(op)); | |
| 283 return kNoConstants; | |
| 284 } | |
| 285 | |
| 286 | |
| 287 void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { | 342 void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { |
| 288 } | 343 } |
| 289 | 344 |
| 290 | 345 |
| 291 void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { | 346 void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { |
| 292 codegen()->Move(result_register(), slot); | 347 codegen()->Move(result_register(), slot); |
| 293 } | 348 } |
| 294 | 349 |
| 295 | 350 |
| 296 void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { | 351 void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 495 if (true_label_ != fall_through_) __ b(true_label_); | 550 if (true_label_ != fall_through_) __ b(true_label_); |
| 496 } else { | 551 } else { |
| 497 if (false_label_ != fall_through_) __ b(false_label_); | 552 if (false_label_ != fall_through_) __ b(false_label_); |
| 498 } | 553 } |
| 499 } | 554 } |
| 500 | 555 |
| 501 | 556 |
| 502 void FullCodeGenerator::DoTest(Label* if_true, | 557 void FullCodeGenerator::DoTest(Label* if_true, |
| 503 Label* if_false, | 558 Label* if_false, |
| 504 Label* fall_through) { | 559 Label* fall_through) { |
| 505 // Call the runtime to find the boolean value of the source and then | 560 if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) { |
| 506 // translate it into control flow to the pair of labels. | 561 CpuFeatures::Scope scope(VFP3); |
| 507 __ push(result_register()); | 562 // Emit the inlined tests assumed by the stub. |
| 508 __ CallRuntime(Runtime::kToBool, 1); | 563 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
| 509 __ LoadRoot(ip, Heap::kTrueValueRootIndex); | 564 __ cmp(result_register(), ip); |
| 510 __ cmp(r0, ip); | 565 __ b(eq, if_false); |
| 511 Split(eq, if_true, if_false, fall_through); | 566 __ LoadRoot(ip, Heap::kTrueValueRootIndex); |
| 567 __ cmp(result_register(), ip); |
| 568 __ b(eq, if_true); |
| 569 __ LoadRoot(ip, Heap::kFalseValueRootIndex); |
| 570 __ cmp(result_register(), ip); |
| 571 __ b(eq, if_false); |
| 572 STATIC_ASSERT(kSmiTag == 0); |
| 573 __ tst(result_register(), result_register()); |
| 574 __ b(eq, if_false); |
| 575 __ JumpIfSmi(result_register(), if_true); |
| 576 |
| 577 // Call the ToBoolean stub for all other cases. |
| 578 ToBooleanStub stub(result_register()); |
| 579 __ CallStub(&stub); |
| 580 __ tst(result_register(), result_register()); |
| 581 } else { |
| 582 // Call the runtime to find the boolean value of the source and then |
| 583 // translate it into control flow to the pair of labels. |
| 584 __ push(result_register()); |
| 585 __ CallRuntime(Runtime::kToBool, 1); |
| 586 __ LoadRoot(ip, Heap::kFalseValueRootIndex); |
| 587 __ cmp(r0, ip); |
| 588 } |
| 589 |
| 590 // The stub returns nonzero for true. |
| 591 Split(ne, if_true, if_false, fall_through); |
| 512 } | 592 } |
| 513 | 593 |
| 514 | 594 |
| 515 void FullCodeGenerator::Split(Condition cond, | 595 void FullCodeGenerator::Split(Condition cond, |
| 516 Label* if_true, | 596 Label* if_true, |
| 517 Label* if_false, | 597 Label* if_false, |
| 518 Label* fall_through) { | 598 Label* fall_through) { |
| 519 if (if_false == fall_through) { | 599 if (if_false == fall_through) { |
| 520 __ b(cond, if_true); | 600 __ b(cond, if_true); |
| 521 } else if (if_true == fall_through) { | 601 } else if (if_true == fall_through) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 616 } else if (function != NULL) { | 696 } else if (function != NULL) { |
| 617 VisitForAccumulatorValue(function); | 697 VisitForAccumulatorValue(function); |
| 618 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); | 698 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
| 619 } | 699 } |
| 620 break; | 700 break; |
| 621 | 701 |
| 622 case Slot::CONTEXT: | 702 case Slot::CONTEXT: |
| 623 // We bypass the general EmitSlotSearch because we know more about | 703 // We bypass the general EmitSlotSearch because we know more about |
| 624 // this specific context. | 704 // this specific context. |
| 625 | 705 |
| 626 // The variable in the decl always resides in the current context. | 706 // The variable in the decl always resides in the current function |
| 707 // context. |
| 627 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); | 708 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); |
| 628 if (FLAG_debug_code) { | 709 if (FLAG_debug_code) { |
| 629 // Check if we have the correct context pointer. | 710 // Check that we're not inside a 'with'. |
| 630 __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); | 711 __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); |
| 631 __ cmp(r1, cp); | 712 __ cmp(r1, cp); |
| 632 __ Check(eq, "Unexpected declaration in current context."); | 713 __ Check(eq, "Unexpected declaration in current context."); |
| 633 } | 714 } |
| 634 if (mode == Variable::CONST) { | 715 if (mode == Variable::CONST) { |
| 635 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | 716 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
| 636 __ str(ip, ContextOperand(cp, slot->index())); | 717 __ str(ip, ContextOperand(cp, slot->index())); |
| 637 // No write barrier since the_hole_value is in old space. | 718 // No write barrier since the_hole_value is in old space. |
| 638 } else if (function != NULL) { | 719 } else if (function != NULL) { |
| 639 VisitForAccumulatorValue(function); | 720 VisitForAccumulatorValue(function); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 688 VisitForAccumulatorValue(function); | 769 VisitForAccumulatorValue(function); |
| 689 __ pop(r2); | 770 __ pop(r2); |
| 690 } else { | 771 } else { |
| 691 __ mov(r2, r0); | 772 __ mov(r2, r0); |
| 692 __ LoadRoot(r0, Heap::kTheHoleValueRootIndex); | 773 __ LoadRoot(r0, Heap::kTheHoleValueRootIndex); |
| 693 } | 774 } |
| 694 ASSERT(prop->key()->AsLiteral() != NULL && | 775 ASSERT(prop->key()->AsLiteral() != NULL && |
| 695 prop->key()->AsLiteral()->handle()->IsSmi()); | 776 prop->key()->AsLiteral()->handle()->IsSmi()); |
| 696 __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); | 777 __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); |
| 697 | 778 |
| 698 Handle<Code> ic(isolate()->builtins()->builtin( | 779 Handle<Code> ic(isolate()->builtins()->builtin(is_strict() |
| 699 Builtins::KeyedStoreIC_Initialize)); | 780 ? Builtins::KeyedStoreIC_Initialize_Strict |
| 781 : Builtins::KeyedStoreIC_Initialize)); |
| 700 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 782 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 701 // Value in r0 is ignored (declarations are statements). | 783 // Value in r0 is ignored (declarations are statements). |
| 702 } | 784 } |
| 703 } | 785 } |
| 704 } | 786 } |
| 705 | 787 |
| 706 | 788 |
| 707 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { | 789 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { |
| 708 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); | 790 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); |
| 709 } | 791 } |
| 710 | 792 |
| 711 | 793 |
| 712 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { | 794 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { |
| 713 // Call the runtime to declare the globals. | 795 // Call the runtime to declare the globals. |
| 714 // The context is the first argument. | 796 // The context is the first argument. |
| 715 __ mov(r1, Operand(pairs)); | 797 __ mov(r2, Operand(pairs)); |
| 716 __ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0))); | 798 __ mov(r1, Operand(Smi::FromInt(is_eval() ? 1 : 0))); |
| 717 __ Push(cp, r1, r0); | 799 __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); |
| 718 __ CallRuntime(Runtime::kDeclareGlobals, 3); | 800 __ Push(cp, r2, r1, r0); |
| 801 __ CallRuntime(Runtime::kDeclareGlobals, 4); |
| 719 // Return value is ignored. | 802 // Return value is ignored. |
| 720 } | 803 } |
| 721 | 804 |
| 722 | 805 |
| 723 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { | 806 void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { |
| 724 Comment cmnt(masm_, "[ SwitchStatement"); | 807 Comment cmnt(masm_, "[ SwitchStatement"); |
| 725 Breakable nested_statement(this, stmt); | 808 Breakable nested_statement(this, stmt); |
| 726 SetStatementPosition(stmt); | 809 SetStatementPosition(stmt); |
| 810 |
| 727 // Keep the switch value on the stack until a case matches. | 811 // Keep the switch value on the stack until a case matches. |
| 728 VisitForStackValue(stmt->tag()); | 812 VisitForStackValue(stmt->tag()); |
| 729 | |
| 730 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); | 813 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); |
| 731 | 814 |
| 732 ZoneList<CaseClause*>* clauses = stmt->cases(); | 815 ZoneList<CaseClause*>* clauses = stmt->cases(); |
| 733 CaseClause* default_clause = NULL; // Can occur anywhere in the list. | 816 CaseClause* default_clause = NULL; // Can occur anywhere in the list. |
| 734 | 817 |
| 735 Label next_test; // Recycled for each test. | 818 Label next_test; // Recycled for each test. |
| 736 // Compile all the tests with branches to their bodies. | 819 // Compile all the tests with branches to their bodies. |
| 737 for (int i = 0; i < clauses->length(); i++) { | 820 for (int i = 0; i < clauses->length(); i++) { |
| 738 CaseClause* clause = clauses->at(i); | 821 CaseClause* clause = clauses->at(i); |
| 739 clause->body_target()->entry_label()->Unuse(); | 822 clause->body_target()->entry_label()->Unuse(); |
| 740 | 823 |
| 741 // The default is not a test, but remember it as final fall through. | 824 // The default is not a test, but remember it as final fall through. |
| 742 if (clause->is_default()) { | 825 if (clause->is_default()) { |
| 743 default_clause = clause; | 826 default_clause = clause; |
| 744 continue; | 827 continue; |
| 745 } | 828 } |
| 746 | 829 |
| 747 Comment cmnt(masm_, "[ Case comparison"); | 830 Comment cmnt(masm_, "[ Case comparison"); |
| 748 __ bind(&next_test); | 831 __ bind(&next_test); |
| 749 next_test.Unuse(); | 832 next_test.Unuse(); |
| 750 | 833 |
| 751 // Compile the label expression. | 834 // Compile the label expression. |
| 752 VisitForAccumulatorValue(clause->label()); | 835 VisitForAccumulatorValue(clause->label()); |
| 753 | 836 |
| 754 // Perform the comparison as if via '==='. | 837 // Perform the comparison as if via '==='. |
| 755 __ ldr(r1, MemOperand(sp, 0)); // Switch value. | 838 __ ldr(r1, MemOperand(sp, 0)); // Switch value. |
| 756 bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT); | 839 bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT); |
| 840 JumpPatchSite patch_site(masm_); |
| 757 if (inline_smi_code) { | 841 if (inline_smi_code) { |
| 758 Label slow_case; | 842 Label slow_case; |
| 759 __ orr(r2, r1, r0); | 843 __ orr(r2, r1, r0); |
| 760 __ tst(r2, Operand(kSmiTagMask)); | 844 patch_site.EmitJumpIfNotSmi(r2, &slow_case); |
| 761 __ b(ne, &slow_case); | 845 |
| 762 __ cmp(r1, r0); | 846 __ cmp(r1, r0); |
| 763 __ b(ne, &next_test); | 847 __ b(ne, &next_test); |
| 764 __ Drop(1); // Switch value is no longer needed. | 848 __ Drop(1); // Switch value is no longer needed. |
| 765 __ b(clause->body_target()->entry_label()); | 849 __ b(clause->body_target()->entry_label()); |
| 766 __ bind(&slow_case); | 850 __ bind(&slow_case); |
| 767 } | 851 } |
| 768 | 852 |
| 769 CompareFlags flags = inline_smi_code | 853 // Record position before stub call for type feedback. |
| 770 ? NO_SMI_COMPARE_IN_STUB | 854 SetSourcePosition(clause->position()); |
| 771 : NO_COMPARE_FLAGS; | 855 Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT); |
| 772 CompareStub stub(eq, true, flags, r1, r0); | 856 EmitCallIC(ic, &patch_site); |
| 773 __ CallStub(&stub); | 857 __ cmp(r0, Operand(0)); |
| 774 __ cmp(r0, Operand(0, RelocInfo::NONE)); | |
| 775 __ b(ne, &next_test); | 858 __ b(ne, &next_test); |
| 776 __ Drop(1); // Switch value is no longer needed. | 859 __ Drop(1); // Switch value is no longer needed. |
| 777 __ b(clause->body_target()->entry_label()); | 860 __ b(clause->body_target()->entry_label()); |
| 778 } | 861 } |
| 779 | 862 |
| 780 // Discard the test value and jump to the default if present, otherwise to | 863 // Discard the test value and jump to the default if present, otherwise to |
| 781 // the end of the statement. | 864 // the end of the statement. |
| 782 __ bind(&next_test); | 865 __ bind(&next_test); |
| 783 __ Drop(1); // Switch value is no longer needed. | 866 __ Drop(1); // Switch value is no longer needed. |
| 784 if (default_clause == NULL) { | 867 if (default_clause == NULL) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 808 ForIn loop_statement(this, stmt); | 891 ForIn loop_statement(this, stmt); |
| 809 increment_loop_depth(); | 892 increment_loop_depth(); |
| 810 | 893 |
| 811 // Get the object to enumerate over. Both SpiderMonkey and JSC | 894 // Get the object to enumerate over. Both SpiderMonkey and JSC |
| 812 // ignore null and undefined in contrast to the specification; see | 895 // ignore null and undefined in contrast to the specification; see |
| 813 // ECMA-262 section 12.6.4. | 896 // ECMA-262 section 12.6.4. |
| 814 VisitForAccumulatorValue(stmt->enumerable()); | 897 VisitForAccumulatorValue(stmt->enumerable()); |
| 815 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); | 898 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
| 816 __ cmp(r0, ip); | 899 __ cmp(r0, ip); |
| 817 __ b(eq, &exit); | 900 __ b(eq, &exit); |
| 818 __ LoadRoot(ip, Heap::kNullValueRootIndex); | 901 Register null_value = r5; |
| 819 __ cmp(r0, ip); | 902 __ LoadRoot(null_value, Heap::kNullValueRootIndex); |
| 903 __ cmp(r0, null_value); |
| 820 __ b(eq, &exit); | 904 __ b(eq, &exit); |
| 821 | 905 |
| 822 // Convert the object to a JS object. | 906 // Convert the object to a JS object. |
| 823 Label convert, done_convert; | 907 Label convert, done_convert; |
| 824 __ JumpIfSmi(r0, &convert); | 908 __ JumpIfSmi(r0, &convert); |
| 825 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE); | 909 __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE); |
| 826 __ b(hs, &done_convert); | 910 __ b(hs, &done_convert); |
| 827 __ bind(&convert); | 911 __ bind(&convert); |
| 828 __ push(r0); | 912 __ push(r0); |
| 829 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS); | 913 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS); |
| 830 __ bind(&done_convert); | 914 __ bind(&done_convert); |
| 831 __ push(r0); | 915 __ push(r0); |
| 832 | 916 |
| 833 // BUG(867): Check cache validity in generated code. This is a fast | 917 // Check cache validity in generated code. This is a fast case for |
| 834 // case for the JSObject::IsSimpleEnum cache validity checks. If we | 918 // the JSObject::IsSimpleEnum cache validity checks. If we cannot |
| 835 // cannot guarantee cache validity, call the runtime system to check | 919 // guarantee cache validity, call the runtime system to check cache |
| 836 // cache validity or get the property names in a fixed array. | 920 // validity or get the property names in a fixed array. |
| 921 Label next, call_runtime; |
| 922 // Preload a couple of values used in the loop. |
| 923 Register empty_fixed_array_value = r6; |
| 924 __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); |
| 925 Register empty_descriptor_array_value = r7; |
| 926 __ LoadRoot(empty_descriptor_array_value, |
| 927 Heap::kEmptyDescriptorArrayRootIndex); |
| 928 __ mov(r1, r0); |
| 929 __ bind(&next); |
| 930 |
| 931 // Check that there are no elements. Register r1 contains the |
| 932 // current JS object we've reached through the prototype chain. |
| 933 __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset)); |
| 934 __ cmp(r2, empty_fixed_array_value); |
| 935 __ b(ne, &call_runtime); |
| 936 |
| 937 // Check that instance descriptors are not empty so that we can |
| 938 // check for an enum cache. Leave the map in r2 for the subsequent |
| 939 // prototype load. |
| 940 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 941 __ ldr(r3, FieldMemOperand(r2, Map::kInstanceDescriptorsOffset)); |
| 942 __ cmp(r3, empty_descriptor_array_value); |
| 943 __ b(eq, &call_runtime); |
| 944 |
| 945 // Check that there is an enum cache in the non-empty instance |
| 946 // descriptors (r3). This is the case if the next enumeration |
| 947 // index field does not contain a smi. |
| 948 __ ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumerationIndexOffset)); |
| 949 __ JumpIfSmi(r3, &call_runtime); |
| 950 |
| 951 // For all objects but the receiver, check that the cache is empty. |
| 952 Label check_prototype; |
| 953 __ cmp(r1, r0); |
| 954 __ b(eq, &check_prototype); |
| 955 __ ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 956 __ cmp(r3, empty_fixed_array_value); |
| 957 __ b(ne, &call_runtime); |
| 958 |
| 959 // Load the prototype from the map and loop if non-null. |
| 960 __ bind(&check_prototype); |
| 961 __ ldr(r1, FieldMemOperand(r2, Map::kPrototypeOffset)); |
| 962 __ cmp(r1, null_value); |
| 963 __ b(ne, &next); |
| 964 |
| 965 // The enum cache is valid. Load the map of the object being |
| 966 // iterated over and use the cache for the iteration. |
| 967 Label use_cache; |
| 968 __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); |
| 969 __ b(&use_cache); |
| 837 | 970 |
| 838 // Get the set of properties to enumerate. | 971 // Get the set of properties to enumerate. |
| 972 __ bind(&call_runtime); |
| 839 __ push(r0); // Duplicate the enumerable object on the stack. | 973 __ push(r0); // Duplicate the enumerable object on the stack. |
| 840 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); | 974 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); |
| 841 | 975 |
| 842 // If we got a map from the runtime call, we can do a fast | 976 // If we got a map from the runtime call, we can do a fast |
| 843 // modification check. Otherwise, we got a fixed array, and we have | 977 // modification check. Otherwise, we got a fixed array, and we have |
| 844 // to do a slow check. | 978 // to do a slow check. |
| 845 Label fixed_array; | 979 Label fixed_array; |
| 846 __ mov(r2, r0); | 980 __ mov(r2, r0); |
| 847 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); | 981 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); |
| 848 __ LoadRoot(ip, Heap::kMetaMapRootIndex); | 982 __ LoadRoot(ip, Heap::kMetaMapRootIndex); |
| 849 __ cmp(r1, ip); | 983 __ cmp(r1, ip); |
| 850 __ b(ne, &fixed_array); | 984 __ b(ne, &fixed_array); |
| 851 | 985 |
| 852 // We got a map in register r0. Get the enumeration cache from it. | 986 // We got a map in register r0. Get the enumeration cache from it. |
| 987 __ bind(&use_cache); |
| 853 __ ldr(r1, FieldMemOperand(r0, Map::kInstanceDescriptorsOffset)); | 988 __ ldr(r1, FieldMemOperand(r0, Map::kInstanceDescriptorsOffset)); |
| 854 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); | 989 __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); |
| 855 __ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); | 990 __ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 856 | 991 |
| 857 // Setup the four remaining stack slots. | 992 // Setup the four remaining stack slots. |
| 858 __ push(r0); // Map. | 993 __ push(r0); // Map. |
| 859 __ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset)); | 994 __ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset)); |
| 860 __ mov(r0, Operand(Smi::FromInt(0))); | 995 __ mov(r0, Operand(Smi::FromInt(0))); |
| 861 // Push enumeration cache, enumeration cache length (as smi) and zero. | 996 // Push enumeration cache, enumeration cache length (as smi) and zero. |
| 862 __ Push(r2, r1, r0); | 997 __ Push(r2, r1, r0); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 931 | 1066 |
| 932 // Exit and decrement the loop depth. | 1067 // Exit and decrement the loop depth. |
| 933 __ bind(&exit); | 1068 __ bind(&exit); |
| 934 decrement_loop_depth(); | 1069 decrement_loop_depth(); |
| 935 } | 1070 } |
| 936 | 1071 |
| 937 | 1072 |
| 938 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, | 1073 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, |
| 939 bool pretenure) { | 1074 bool pretenure) { |
| 940 // Use the fast case closure allocation code that allocates in new | 1075 // Use the fast case closure allocation code that allocates in new |
| 941 // space for nested functions that don't need literals cloning. | 1076 // space for nested functions that don't need literals cloning. If |
| 942 if (scope()->is_function_scope() && | 1077 // we're running with the --always-opt or the --prepare-always-opt |
| 1078 // flag, we need to use the runtime function so that the new function |
| 1079 // we are creating here gets a chance to have its code optimized and |
| 1080 // doesn't just get a copy of the existing unoptimized code. |
| 1081 if (!FLAG_always_opt && |
| 1082 !FLAG_prepare_always_opt && |
| 1083 scope()->is_function_scope() && |
| 943 info->num_literals() == 0 && | 1084 info->num_literals() == 0 && |
| 944 !pretenure) { | 1085 !pretenure) { |
| 945 FastNewClosureStub stub; | 1086 FastNewClosureStub stub; |
| 946 __ mov(r0, Operand(info)); | 1087 __ mov(r0, Operand(info)); |
| 947 __ push(r0); | 1088 __ push(r0); |
| 948 __ CallStub(&stub); | 1089 __ CallStub(&stub); |
| 949 } else { | 1090 } else { |
| 950 __ mov(r0, Operand(info)); | 1091 __ mov(r0, Operand(info)); |
| 951 __ LoadRoot(r1, pretenure ? Heap::kTrueValueRootIndex | 1092 __ LoadRoot(r1, pretenure ? Heap::kTrueValueRootIndex |
| 952 : Heap::kFalseValueRootIndex); | 1093 : Heap::kFalseValueRootIndex); |
| 953 __ Push(cp, r0, r1); | 1094 __ Push(cp, r0, r1); |
| 954 __ CallRuntime(Runtime::kNewClosure, 3); | 1095 __ CallRuntime(Runtime::kNewClosure, 3); |
| 955 } | 1096 } |
| 956 context()->Plug(r0); | 1097 context()->Plug(r0); |
| 957 } | 1098 } |
| 958 | 1099 |
| 959 | 1100 |
| 960 void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { | 1101 void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { |
| 961 Comment cmnt(masm_, "[ VariableProxy"); | 1102 Comment cmnt(masm_, "[ VariableProxy"); |
| 962 EmitVariableLoad(expr->var()); | 1103 EmitVariableLoad(expr->var()); |
| 963 } | 1104 } |
| 964 | 1105 |
| 965 | 1106 |
| 966 MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( | 1107 MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( |
| 967 Slot* slot, | 1108 Slot* slot, |
| 968 Label* slow) { | 1109 Label* slow) { |
| 969 ASSERT(slot->type() == Slot::CONTEXT); | 1110 ASSERT(slot->type() == Slot::CONTEXT); |
| 970 Register current = cp; | 1111 Register context = cp; |
| 971 Register next = r3; | 1112 Register next = r3; |
| 972 Register temp = r4; | 1113 Register temp = r4; |
| 973 | 1114 |
| 974 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { | 1115 for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { |
| 975 if (s->num_heap_slots() > 0) { | 1116 if (s->num_heap_slots() > 0) { |
| 976 if (s->calls_eval()) { | 1117 if (s->calls_eval()) { |
| 977 // Check that extension is NULL. | 1118 // Check that extension is NULL. |
| 978 __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); | 1119 __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX)); |
| 979 __ tst(temp, temp); | 1120 __ tst(temp, temp); |
| 980 __ b(ne, slow); | 1121 __ b(ne, slow); |
| 981 } | 1122 } |
| 982 __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX)); | 1123 __ ldr(next, ContextOperand(context, Context::CLOSURE_INDEX)); |
| 983 __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); | 1124 __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); |
| 984 // Walk the rest of the chain without clobbering cp. | 1125 // Walk the rest of the chain without clobbering cp. |
| 985 current = next; | 1126 context = next; |
| 986 } | 1127 } |
| 987 } | 1128 } |
| 988 // Check that last extension is NULL. | 1129 // Check that last extension is NULL. |
| 989 __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); | 1130 __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX)); |
| 990 __ tst(temp, temp); | 1131 __ tst(temp, temp); |
| 991 __ b(ne, slow); | 1132 __ b(ne, slow); |
| 992 __ ldr(temp, ContextOperand(current, Context::FCONTEXT_INDEX)); | 1133 |
| 993 return ContextOperand(temp, slot->index()); | 1134 // This function is used only for loads, not stores, so it's safe to |
| 1135 // return an cp-based operand (the write barrier cannot be allowed to |
| 1136 // destroy the cp register). |
| 1137 return ContextOperand(context, slot->index()); |
| 994 } | 1138 } |
| 995 | 1139 |
| 996 | 1140 |
| 997 void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( | 1141 void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( |
| 998 Slot* slot, | 1142 Slot* slot, |
| 999 TypeofState typeof_state, | 1143 TypeofState typeof_state, |
| 1000 Label* slow, | 1144 Label* slow, |
| 1001 Label* done) { | 1145 Label* done) { |
| 1002 // Generate fast-case code for variables that might be shadowed by | 1146 // Generate fast-case code for variables that might be shadowed by |
| 1003 // eval-introduced variables. Eval is used a lot without | 1147 // eval-introduced variables. Eval is used a lot without |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1187 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1331 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1188 context()->Plug(r0); | 1332 context()->Plug(r0); |
| 1189 } | 1333 } |
| 1190 } | 1334 } |
| 1191 | 1335 |
| 1192 | 1336 |
| 1193 void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { | 1337 void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { |
| 1194 Comment cmnt(masm_, "[ RegExpLiteral"); | 1338 Comment cmnt(masm_, "[ RegExpLiteral"); |
| 1195 Label materialized; | 1339 Label materialized; |
| 1196 // Registers will be used as follows: | 1340 // Registers will be used as follows: |
| 1341 // r5 = materialized value (RegExp literal) |
| 1197 // r4 = JS function, literals array | 1342 // r4 = JS function, literals array |
| 1198 // r3 = literal index | 1343 // r3 = literal index |
| 1199 // r2 = RegExp pattern | 1344 // r2 = RegExp pattern |
| 1200 // r1 = RegExp flags | 1345 // r1 = RegExp flags |
| 1201 // r0 = temp + materialized value (RegExp literal) | 1346 // r0 = RegExp literal clone |
| 1202 __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); | 1347 __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); |
| 1203 __ ldr(r4, FieldMemOperand(r0, JSFunction::kLiteralsOffset)); | 1348 __ ldr(r4, FieldMemOperand(r0, JSFunction::kLiteralsOffset)); |
| 1204 int literal_offset = | 1349 int literal_offset = |
| 1205 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; | 1350 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; |
| 1206 __ ldr(r0, FieldMemOperand(r4, literal_offset)); | 1351 __ ldr(r5, FieldMemOperand(r4, literal_offset)); |
| 1207 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); | 1352 __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
| 1208 __ cmp(r0, ip); | 1353 __ cmp(r5, ip); |
| 1209 __ b(ne, &materialized); | 1354 __ b(ne, &materialized); |
| 1210 | 1355 |
| 1211 // Create regexp literal using runtime function. | 1356 // Create regexp literal using runtime function. |
| 1212 // Result will be in r0. | 1357 // Result will be in r0. |
| 1213 __ mov(r3, Operand(Smi::FromInt(expr->literal_index()))); | 1358 __ mov(r3, Operand(Smi::FromInt(expr->literal_index()))); |
| 1214 __ mov(r2, Operand(expr->pattern())); | 1359 __ mov(r2, Operand(expr->pattern())); |
| 1215 __ mov(r1, Operand(expr->flags())); | 1360 __ mov(r1, Operand(expr->flags())); |
| 1216 __ Push(r4, r3, r2, r1); | 1361 __ Push(r4, r3, r2, r1); |
| 1217 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); | 1362 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); |
| 1363 __ mov(r5, r0); |
| 1218 | 1364 |
| 1219 __ bind(&materialized); | 1365 __ bind(&materialized); |
| 1220 int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; | 1366 int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; |
| 1221 __ push(r0); | 1367 Label allocated, runtime_allocate; |
| 1368 __ AllocateInNewSpace(size, r0, r2, r3, &runtime_allocate, TAG_OBJECT); |
| 1369 __ jmp(&allocated); |
| 1370 |
| 1371 __ bind(&runtime_allocate); |
| 1372 __ push(r5); |
| 1222 __ mov(r0, Operand(Smi::FromInt(size))); | 1373 __ mov(r0, Operand(Smi::FromInt(size))); |
| 1223 __ push(r0); | 1374 __ push(r0); |
| 1224 __ CallRuntime(Runtime::kAllocateInNewSpace, 1); | 1375 __ CallRuntime(Runtime::kAllocateInNewSpace, 1); |
| 1376 __ pop(r5); |
| 1225 | 1377 |
| 1378 __ bind(&allocated); |
| 1226 // After this, registers are used as follows: | 1379 // After this, registers are used as follows: |
| 1227 // r0: Newly allocated regexp. | 1380 // r0: Newly allocated regexp. |
| 1228 // r1: Materialized regexp. | 1381 // r5: Materialized regexp. |
| 1229 // r2: temp. | 1382 // r2: temp. |
| 1230 __ pop(r1); | 1383 __ CopyFields(r0, r5, r2.bit(), size / kPointerSize); |
| 1231 __ CopyFields(r0, r1, r2.bit(), size / kPointerSize); | |
| 1232 context()->Plug(r0); | 1384 context()->Plug(r0); |
| 1233 } | 1385 } |
| 1234 | 1386 |
| 1235 | 1387 |
| 1236 void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { | 1388 void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { |
| 1237 Comment cmnt(masm_, "[ ObjectLiteral"); | 1389 Comment cmnt(masm_, "[ ObjectLiteral"); |
| 1238 __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); | 1390 __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); |
| 1239 __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); | 1391 __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); |
| 1240 __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); | 1392 __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); |
| 1241 __ mov(r1, Operand(expr->constant_properties())); | 1393 __ mov(r1, Operand(expr->constant_properties())); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1288 break; | 1440 break; |
| 1289 } | 1441 } |
| 1290 // Fall through. | 1442 // Fall through. |
| 1291 case ObjectLiteral::Property::PROTOTYPE: | 1443 case ObjectLiteral::Property::PROTOTYPE: |
| 1292 // Duplicate receiver on stack. | 1444 // Duplicate receiver on stack. |
| 1293 __ ldr(r0, MemOperand(sp)); | 1445 __ ldr(r0, MemOperand(sp)); |
| 1294 __ push(r0); | 1446 __ push(r0); |
| 1295 VisitForStackValue(key); | 1447 VisitForStackValue(key); |
| 1296 VisitForStackValue(value); | 1448 VisitForStackValue(value); |
| 1297 if (property->emit_store()) { | 1449 if (property->emit_store()) { |
| 1298 __ CallRuntime(Runtime::kSetProperty, 3); | 1450 __ mov(r0, Operand(Smi::FromInt(NONE))); // PropertyAttributes |
| 1451 __ push(r0); |
| 1452 __ CallRuntime(Runtime::kSetProperty, 4); |
| 1299 } else { | 1453 } else { |
| 1300 __ Drop(3); | 1454 __ Drop(3); |
| 1301 } | 1455 } |
| 1302 break; | 1456 break; |
| 1303 case ObjectLiteral::Property::GETTER: | 1457 case ObjectLiteral::Property::GETTER: |
| 1304 case ObjectLiteral::Property::SETTER: | 1458 case ObjectLiteral::Property::SETTER: |
| 1305 // Duplicate receiver on stack. | 1459 // Duplicate receiver on stack. |
| 1306 __ ldr(r0, MemOperand(sp)); | 1460 __ ldr(r0, MemOperand(sp)); |
| 1307 __ push(r0); | 1461 __ push(r0); |
| 1308 VisitForStackValue(key); | 1462 VisitForStackValue(key); |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1468 } | 1622 } |
| 1469 } | 1623 } |
| 1470 | 1624 |
| 1471 // For property compound assignments we need another deoptimization | 1625 // For property compound assignments we need another deoptimization |
| 1472 // point after the property load. | 1626 // point after the property load. |
| 1473 if (property != NULL) { | 1627 if (property != NULL) { |
| 1474 PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); | 1628 PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); |
| 1475 } | 1629 } |
| 1476 | 1630 |
| 1477 Token::Value op = expr->binary_op(); | 1631 Token::Value op = expr->binary_op(); |
| 1478 ConstantOperand constant = ShouldInlineSmiCase(op) | 1632 __ push(r0); // Left operand goes on the stack. |
| 1479 ? GetConstantOperand(op, expr->target(), expr->value()) | 1633 VisitForAccumulatorValue(expr->value()); |
| 1480 : kNoConstants; | |
| 1481 ASSERT(constant == kRightConstant || constant == kNoConstants); | |
| 1482 if (constant == kNoConstants) { | |
| 1483 __ push(r0); // Left operand goes on the stack. | |
| 1484 VisitForAccumulatorValue(expr->value()); | |
| 1485 } | |
| 1486 | 1634 |
| 1487 OverwriteMode mode = expr->value()->ResultOverwriteAllowed() | 1635 OverwriteMode mode = expr->value()->ResultOverwriteAllowed() |
| 1488 ? OVERWRITE_RIGHT | 1636 ? OVERWRITE_RIGHT |
| 1489 : NO_OVERWRITE; | 1637 : NO_OVERWRITE; |
| 1490 SetSourcePosition(expr->position() + 1); | 1638 SetSourcePosition(expr->position() + 1); |
| 1491 AccumulatorValueContext context(this); | 1639 AccumulatorValueContext context(this); |
| 1492 if (ShouldInlineSmiCase(op)) { | 1640 if (ShouldInlineSmiCase(op)) { |
| 1493 EmitInlineSmiBinaryOp(expr, | 1641 EmitInlineSmiBinaryOp(expr, |
| 1494 op, | 1642 op, |
| 1495 mode, | 1643 mode, |
| 1496 expr->target(), | 1644 expr->target(), |
| 1497 expr->value(), | 1645 expr->value()); |
| 1498 constant); | |
| 1499 } else { | 1646 } else { |
| 1500 EmitBinaryOp(op, mode); | 1647 EmitBinaryOp(op, mode); |
| 1501 } | 1648 } |
| 1502 | 1649 |
| 1503 // Deoptimization point in case the binary operation may have side effects. | 1650 // Deoptimization point in case the binary operation may have side effects. |
| 1504 PrepareForBailout(expr->binary_operation(), TOS_REG); | 1651 PrepareForBailout(expr->binary_operation(), TOS_REG); |
| 1505 } else { | 1652 } else { |
| 1506 VisitForAccumulatorValue(expr->value()); | 1653 VisitForAccumulatorValue(expr->value()); |
| 1507 } | 1654 } |
| 1508 | 1655 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1543 // Call keyed load IC. It has arguments key and receiver in r0 and r1. | 1690 // Call keyed load IC. It has arguments key and receiver in r0 and r1. |
| 1544 Handle<Code> ic(isolate()->builtins()->builtin( | 1691 Handle<Code> ic(isolate()->builtins()->builtin( |
| 1545 Builtins::KeyedLoadIC_Initialize)); | 1692 Builtins::KeyedLoadIC_Initialize)); |
| 1546 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1693 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1547 } | 1694 } |
| 1548 | 1695 |
| 1549 | 1696 |
| 1550 void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, | 1697 void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, |
| 1551 Token::Value op, | 1698 Token::Value op, |
| 1552 OverwriteMode mode, | 1699 OverwriteMode mode, |
| 1553 Expression* left, | 1700 Expression* left_expr, |
| 1554 Expression* right, | 1701 Expression* right_expr) { |
| 1555 ConstantOperand constant) { | 1702 Label done, smi_case, stub_call; |
| 1556 ASSERT(constant == kNoConstants); // Only handled case. | 1703 |
| 1557 EmitBinaryOp(op, mode); | 1704 Register scratch1 = r2; |
| 1705 Register scratch2 = r3; |
| 1706 |
| 1707 // Get the arguments. |
| 1708 Register left = r1; |
| 1709 Register right = r0; |
| 1710 __ pop(left); |
| 1711 |
| 1712 // Perform combined smi check on both operands. |
| 1713 __ orr(scratch1, left, Operand(right)); |
| 1714 STATIC_ASSERT(kSmiTag == 0); |
| 1715 JumpPatchSite patch_site(masm_); |
| 1716 patch_site.EmitJumpIfSmi(scratch1, &smi_case); |
| 1717 |
| 1718 __ bind(&stub_call); |
| 1719 TypeRecordingBinaryOpStub stub(op, mode); |
| 1720 EmitCallIC(stub.GetCode(), &patch_site); |
| 1721 __ jmp(&done); |
| 1722 |
| 1723 __ bind(&smi_case); |
| 1724 // Smi case. This code works the same way as the smi-smi case in the type |
| 1725 // recording binary operation stub, see |
| 1726 // TypeRecordingBinaryOpStub::GenerateSmiSmiOperation for comments. |
| 1727 switch (op) { |
| 1728 case Token::SAR: |
| 1729 __ b(&stub_call); |
| 1730 __ GetLeastBitsFromSmi(scratch1, right, 5); |
| 1731 __ mov(right, Operand(left, ASR, scratch1)); |
| 1732 __ bic(right, right, Operand(kSmiTagMask)); |
| 1733 break; |
| 1734 case Token::SHL: { |
| 1735 __ b(&stub_call); |
| 1736 __ SmiUntag(scratch1, left); |
| 1737 __ GetLeastBitsFromSmi(scratch2, right, 5); |
| 1738 __ mov(scratch1, Operand(scratch1, LSL, scratch2)); |
| 1739 __ add(scratch2, scratch1, Operand(0x40000000), SetCC); |
| 1740 __ b(mi, &stub_call); |
| 1741 __ SmiTag(right, scratch1); |
| 1742 break; |
| 1743 } |
| 1744 case Token::SHR: { |
| 1745 __ b(&stub_call); |
| 1746 __ SmiUntag(scratch1, left); |
| 1747 __ GetLeastBitsFromSmi(scratch2, right, 5); |
| 1748 __ mov(scratch1, Operand(scratch1, LSR, scratch2)); |
| 1749 __ tst(scratch1, Operand(0xc0000000)); |
| 1750 __ b(ne, &stub_call); |
| 1751 __ SmiTag(right, scratch1); |
| 1752 break; |
| 1753 } |
| 1754 case Token::ADD: |
| 1755 __ add(scratch1, left, Operand(right), SetCC); |
| 1756 __ b(vs, &stub_call); |
| 1757 __ mov(right, scratch1); |
| 1758 break; |
| 1759 case Token::SUB: |
| 1760 __ sub(scratch1, left, Operand(right), SetCC); |
| 1761 __ b(vs, &stub_call); |
| 1762 __ mov(right, scratch1); |
| 1763 break; |
| 1764 case Token::MUL: { |
| 1765 __ SmiUntag(ip, right); |
| 1766 __ smull(scratch1, scratch2, left, ip); |
| 1767 __ mov(ip, Operand(scratch1, ASR, 31)); |
| 1768 __ cmp(ip, Operand(scratch2)); |
| 1769 __ b(ne, &stub_call); |
| 1770 __ tst(scratch1, Operand(scratch1)); |
| 1771 __ mov(right, Operand(scratch1), LeaveCC, ne); |
| 1772 __ b(ne, &done); |
| 1773 __ add(scratch2, right, Operand(left), SetCC); |
| 1774 __ mov(right, Operand(Smi::FromInt(0)), LeaveCC, pl); |
| 1775 __ b(mi, &stub_call); |
| 1776 break; |
| 1777 } |
| 1778 case Token::BIT_OR: |
| 1779 __ orr(right, left, Operand(right)); |
| 1780 break; |
| 1781 case Token::BIT_AND: |
| 1782 __ and_(right, left, Operand(right)); |
| 1783 break; |
| 1784 case Token::BIT_XOR: |
| 1785 __ eor(right, left, Operand(right)); |
| 1786 break; |
| 1787 default: |
| 1788 UNREACHABLE(); |
| 1789 } |
| 1790 |
| 1791 __ bind(&done); |
| 1792 context()->Plug(r0); |
| 1558 } | 1793 } |
| 1559 | 1794 |
| 1560 | 1795 |
| 1561 void FullCodeGenerator::EmitBinaryOp(Token::Value op, | 1796 void FullCodeGenerator::EmitBinaryOp(Token::Value op, |
| 1562 OverwriteMode mode) { | 1797 OverwriteMode mode) { |
| 1563 __ pop(r1); | 1798 __ pop(r1); |
| 1564 if (op == Token::ADD || | 1799 TypeRecordingBinaryOpStub stub(op, mode); |
| 1565 op == Token::SUB || | 1800 EmitCallIC(stub.GetCode(), NULL); |
| 1566 op == Token::MUL || | |
| 1567 op == Token::DIV || | |
| 1568 op == Token::MOD || | |
| 1569 op == Token::BIT_OR || | |
| 1570 op == Token::BIT_AND || | |
| 1571 op == Token::BIT_XOR) { | |
| 1572 TypeRecordingBinaryOpStub stub(op, mode); | |
| 1573 __ CallStub(&stub); | |
| 1574 } else { | |
| 1575 GenericBinaryOpStub stub(op, mode, r1, r0); | |
| 1576 __ CallStub(&stub); | |
| 1577 } | |
| 1578 context()->Plug(r0); | 1801 context()->Plug(r0); |
| 1579 } | 1802 } |
| 1580 | 1803 |
| 1581 | 1804 |
| 1582 void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { | 1805 void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { |
| 1583 // Invalid left-hand sides are rewritten to have a 'throw | 1806 // Invalid left-hand sides are rewritten to have a 'throw |
| 1584 // ReferenceError' on the left-hand side. | 1807 // ReferenceError' on the left-hand side. |
| 1585 if (!expr->IsValidLeftHandSide()) { | 1808 if (!expr->IsValidLeftHandSide()) { |
| 1586 VisitForEffect(expr); | 1809 VisitForEffect(expr); |
| 1587 return; | 1810 return; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1605 EmitVariableAssignment(var, Token::ASSIGN); | 1828 EmitVariableAssignment(var, Token::ASSIGN); |
| 1606 break; | 1829 break; |
| 1607 } | 1830 } |
| 1608 case NAMED_PROPERTY: { | 1831 case NAMED_PROPERTY: { |
| 1609 __ push(r0); // Preserve value. | 1832 __ push(r0); // Preserve value. |
| 1610 VisitForAccumulatorValue(prop->obj()); | 1833 VisitForAccumulatorValue(prop->obj()); |
| 1611 __ mov(r1, r0); | 1834 __ mov(r1, r0); |
| 1612 __ pop(r0); // Restore value. | 1835 __ pop(r0); // Restore value. |
| 1613 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); | 1836 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); |
| 1614 Handle<Code> ic(isolate()->builtins()->builtin( | 1837 Handle<Code> ic(isolate()->builtins()->builtin( |
| 1615 Builtins::StoreIC_Initialize)); | 1838 is_strict() ? Builtins::StoreIC_Initialize_Strict |
| 1839 : Builtins::StoreIC_Initialize)); |
| 1616 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1840 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1617 break; | 1841 break; |
| 1618 } | 1842 } |
| 1619 case KEYED_PROPERTY: { | 1843 case KEYED_PROPERTY: { |
| 1620 __ push(r0); // Preserve value. | 1844 __ push(r0); // Preserve value. |
| 1621 VisitForStackValue(prop->obj()); | 1845 if (prop->is_synthetic()) { |
| 1622 VisitForAccumulatorValue(prop->key()); | 1846 ASSERT(prop->obj()->AsVariableProxy() != NULL); |
| 1623 __ mov(r1, r0); | 1847 ASSERT(prop->key()->AsLiteral() != NULL); |
| 1624 __ pop(r2); | 1848 { AccumulatorValueContext for_object(this); |
| 1849 EmitVariableLoad(prop->obj()->AsVariableProxy()->var()); |
| 1850 } |
| 1851 __ mov(r2, r0); |
| 1852 __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); |
| 1853 } else { |
| 1854 VisitForStackValue(prop->obj()); |
| 1855 VisitForAccumulatorValue(prop->key()); |
| 1856 __ mov(r1, r0); |
| 1857 __ pop(r2); |
| 1858 } |
| 1625 __ pop(r0); // Restore value. | 1859 __ pop(r0); // Restore value. |
| 1626 Handle<Code> ic(isolate()->builtins()->builtin( | 1860 Handle<Code> ic(isolate()->builtins()->builtin( |
| 1627 Builtins::KeyedStoreIC_Initialize)); | 1861 is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict |
| 1862 : Builtins::KeyedStoreIC_Initialize)); |
| 1628 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1863 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1629 break; | 1864 break; |
| 1630 } | 1865 } |
| 1631 } | 1866 } |
| 1632 PrepareForBailoutForId(bailout_ast_id, TOS_REG); | 1867 PrepareForBailoutForId(bailout_ast_id, TOS_REG); |
| 1633 context()->Plug(r0); | 1868 context()->Plug(r0); |
| 1634 } | 1869 } |
| 1635 | 1870 |
| 1636 | 1871 |
| 1637 void FullCodeGenerator::EmitVariableAssignment(Variable* var, | 1872 void FullCodeGenerator::EmitVariableAssignment(Variable* var, |
| 1638 Token::Value op) { | 1873 Token::Value op) { |
| 1639 // Left-hand sides that rewrite to explicit property accesses do not reach | 1874 // Left-hand sides that rewrite to explicit property accesses do not reach |
| 1640 // here. | 1875 // here. |
| 1641 ASSERT(var != NULL); | 1876 ASSERT(var != NULL); |
| 1642 ASSERT(var->is_global() || var->AsSlot() != NULL); | 1877 ASSERT(var->is_global() || var->AsSlot() != NULL); |
| 1643 | 1878 |
| 1644 if (var->is_global()) { | 1879 if (var->is_global()) { |
| 1645 ASSERT(!var->is_this()); | 1880 ASSERT(!var->is_this()); |
| 1646 // Assignment to a global variable. Use inline caching for the | 1881 // Assignment to a global variable. Use inline caching for the |
| 1647 // assignment. Right-hand-side value is passed in r0, variable name in | 1882 // assignment. Right-hand-side value is passed in r0, variable name in |
| 1648 // r2, and the global object in r1. | 1883 // r2, and the global object in r1. |
| 1649 __ mov(r2, Operand(var->name())); | 1884 __ mov(r2, Operand(var->name())); |
| 1650 __ ldr(r1, GlobalObjectOperand()); | 1885 __ ldr(r1, GlobalObjectOperand()); |
| 1651 Handle<Code> ic(isolate()->builtins()->builtin( | 1886 Handle<Code> ic(isolate()->builtins()->builtin( |
| 1652 Builtins::StoreIC_Initialize)); | 1887 is_strict() ? Builtins::StoreIC_Initialize_Strict |
| 1653 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1888 : Builtins::StoreIC_Initialize)); |
| 1889 EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); |
| 1654 | 1890 |
| 1655 } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { | 1891 } else if (op == Token::INIT_CONST) { |
| 1656 // Perform the assignment for non-const variables and for initialization | 1892 // Like var declarations, const declarations are hoisted to function |
| 1657 // of const variables. Const assignments are simply skipped. | 1893 // scope. However, unlike var initializers, const initializers are able |
| 1658 Label done; | 1894 // to drill a hole to that function context, even from inside a 'with' |
| 1895 // context. We thus bypass the normal static scope lookup. |
| 1896 Slot* slot = var->AsSlot(); |
| 1897 Label skip; |
| 1898 switch (slot->type()) { |
| 1899 case Slot::PARAMETER: |
| 1900 // No const parameters. |
| 1901 UNREACHABLE(); |
| 1902 break; |
| 1903 case Slot::LOCAL: |
| 1904 // Detect const reinitialization by checking for the hole value. |
| 1905 __ ldr(r1, MemOperand(fp, SlotOffset(slot))); |
| 1906 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
| 1907 __ cmp(r1, ip); |
| 1908 __ b(ne, &skip); |
| 1909 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
| 1910 break; |
| 1911 case Slot::CONTEXT: { |
| 1912 __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); |
| 1913 __ ldr(r2, ContextOperand(r1, slot->index())); |
| 1914 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
| 1915 __ cmp(r2, ip); |
| 1916 __ b(ne, &skip); |
| 1917 __ str(r0, ContextOperand(r1, slot->index())); |
| 1918 int offset = Context::SlotOffset(slot->index()); |
| 1919 __ mov(r3, r0); // Preserve the stored value in r0. |
| 1920 __ RecordWrite(r1, Operand(offset), r3, r2); |
| 1921 break; |
| 1922 } |
| 1923 case Slot::LOOKUP: |
| 1924 __ push(r0); |
| 1925 __ mov(r0, Operand(slot->var()->name())); |
| 1926 __ Push(cp, r0); // Context and name. |
| 1927 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
| 1928 break; |
| 1929 } |
| 1930 __ bind(&skip); |
| 1931 |
| 1932 } else if (var->mode() != Variable::CONST) { |
| 1933 // Perform the assignment for non-const variables. Const assignments |
| 1934 // are simply skipped. |
| 1659 Slot* slot = var->AsSlot(); | 1935 Slot* slot = var->AsSlot(); |
| 1660 switch (slot->type()) { | 1936 switch (slot->type()) { |
| 1661 case Slot::PARAMETER: | 1937 case Slot::PARAMETER: |
| 1662 case Slot::LOCAL: | 1938 case Slot::LOCAL: |
| 1663 if (op == Token::INIT_CONST) { | |
| 1664 // Detect const reinitialization by checking for the hole value. | |
| 1665 __ ldr(r1, MemOperand(fp, SlotOffset(slot))); | |
| 1666 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | |
| 1667 __ cmp(r1, ip); | |
| 1668 __ b(ne, &done); | |
| 1669 } | |
| 1670 // Perform the assignment. | 1939 // Perform the assignment. |
| 1671 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); | 1940 __ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
| 1672 break; | 1941 break; |
| 1673 | 1942 |
| 1674 case Slot::CONTEXT: { | 1943 case Slot::CONTEXT: { |
| 1675 MemOperand target = EmitSlotSearch(slot, r1); | 1944 MemOperand target = EmitSlotSearch(slot, r1); |
| 1676 if (op == Token::INIT_CONST) { | |
| 1677 // Detect const reinitialization by checking for the hole value. | |
| 1678 __ ldr(r2, target); | |
| 1679 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | |
| 1680 __ cmp(r2, ip); | |
| 1681 __ b(ne, &done); | |
| 1682 } | |
| 1683 // Perform the assignment and issue the write barrier. | 1945 // Perform the assignment and issue the write barrier. |
| 1684 __ str(result_register(), target); | 1946 __ str(result_register(), target); |
| 1685 // RecordWrite may destroy all its register arguments. | 1947 // RecordWrite may destroy all its register arguments. |
| 1686 __ mov(r3, result_register()); | 1948 __ mov(r3, result_register()); |
| 1687 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | 1949 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; |
| 1688 __ RecordWrite(r1, Operand(offset), r2, r3); | 1950 __ RecordWrite(r1, Operand(offset), r2, r3); |
| 1689 break; | 1951 break; |
| 1690 } | 1952 } |
| 1691 | 1953 |
| 1692 case Slot::LOOKUP: | 1954 case Slot::LOOKUP: |
| 1693 // Call the runtime for the assignment. The runtime will ignore | 1955 // Call the runtime for the assignment. |
| 1694 // const reinitialization. | |
| 1695 __ push(r0); // Value. | 1956 __ push(r0); // Value. |
| 1696 __ mov(r0, Operand(slot->var()->name())); | 1957 __ mov(r1, Operand(slot->var()->name())); |
| 1697 __ Push(cp, r0); // Context and name. | 1958 __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); |
| 1698 if (op == Token::INIT_CONST) { | 1959 __ Push(cp, r1, r0); // Context, name, strict mode. |
| 1699 // The runtime will ignore const redeclaration. | 1960 __ CallRuntime(Runtime::kStoreContextSlot, 4); |
| 1700 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); | |
| 1701 } else { | |
| 1702 __ CallRuntime(Runtime::kStoreContextSlot, 3); | |
| 1703 } | |
| 1704 break; | 1961 break; |
| 1705 } | 1962 } |
| 1706 __ bind(&done); | |
| 1707 } | 1963 } |
| 1708 } | 1964 } |
| 1709 | 1965 |
| 1710 | 1966 |
| 1711 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { | 1967 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { |
| 1712 // Assignment to a property, using a named store IC. | 1968 // Assignment to a property, using a named store IC. |
| 1713 Property* prop = expr->target()->AsProperty(); | 1969 Property* prop = expr->target()->AsProperty(); |
| 1714 ASSERT(prop != NULL); | 1970 ASSERT(prop != NULL); |
| 1715 ASSERT(prop->key()->AsLiteral() != NULL); | 1971 ASSERT(prop->key()->AsLiteral() != NULL); |
| 1716 | 1972 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1730 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); | 1986 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); |
| 1731 // Load receiver to r1. Leave a copy in the stack if needed for turning the | 1987 // Load receiver to r1. Leave a copy in the stack if needed for turning the |
| 1732 // receiver into fast case. | 1988 // receiver into fast case. |
| 1733 if (expr->ends_initialization_block()) { | 1989 if (expr->ends_initialization_block()) { |
| 1734 __ ldr(r1, MemOperand(sp)); | 1990 __ ldr(r1, MemOperand(sp)); |
| 1735 } else { | 1991 } else { |
| 1736 __ pop(r1); | 1992 __ pop(r1); |
| 1737 } | 1993 } |
| 1738 | 1994 |
| 1739 Handle<Code> ic(isolate()->builtins()->builtin( | 1995 Handle<Code> ic(isolate()->builtins()->builtin( |
| 1740 Builtins::StoreIC_Initialize)); | 1996 is_strict() ? Builtins::StoreIC_Initialize_Strict |
| 1997 : Builtins::StoreIC_Initialize)); |
| 1741 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 1998 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1742 | 1999 |
| 1743 // If the assignment ends an initialization block, revert to fast case. | 2000 // If the assignment ends an initialization block, revert to fast case. |
| 1744 if (expr->ends_initialization_block()) { | 2001 if (expr->ends_initialization_block()) { |
| 1745 __ push(r0); // Result of assignment, saved even if not needed. | 2002 __ push(r0); // Result of assignment, saved even if not needed. |
| 1746 // Receiver is under the result value. | 2003 // Receiver is under the result value. |
| 1747 __ ldr(ip, MemOperand(sp, kPointerSize)); | 2004 __ ldr(ip, MemOperand(sp, kPointerSize)); |
| 1748 __ push(ip); | 2005 __ push(ip); |
| 1749 __ CallRuntime(Runtime::kToFastProperties, 1); | 2006 __ CallRuntime(Runtime::kToFastProperties, 1); |
| 1750 __ pop(r0); | 2007 __ pop(r0); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1775 __ pop(r1); // Key. | 2032 __ pop(r1); // Key. |
| 1776 // Load receiver to r2. Leave a copy in the stack if needed for turning the | 2033 // Load receiver to r2. Leave a copy in the stack if needed for turning the |
| 1777 // receiver into fast case. | 2034 // receiver into fast case. |
| 1778 if (expr->ends_initialization_block()) { | 2035 if (expr->ends_initialization_block()) { |
| 1779 __ ldr(r2, MemOperand(sp)); | 2036 __ ldr(r2, MemOperand(sp)); |
| 1780 } else { | 2037 } else { |
| 1781 __ pop(r2); | 2038 __ pop(r2); |
| 1782 } | 2039 } |
| 1783 | 2040 |
| 1784 Handle<Code> ic(isolate()->builtins()->builtin( | 2041 Handle<Code> ic(isolate()->builtins()->builtin( |
| 1785 Builtins::KeyedStoreIC_Initialize)); | 2042 is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict |
| 2043 : Builtins::KeyedStoreIC_Initialize)); |
| 1786 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 2044 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 1787 | 2045 |
| 1788 // If the assignment ends an initialization block, revert to fast case. | 2046 // If the assignment ends an initialization block, revert to fast case. |
| 1789 if (expr->ends_initialization_block()) { | 2047 if (expr->ends_initialization_block()) { |
| 1790 __ push(r0); // Result of assignment, saved even if not needed. | 2048 __ push(r0); // Result of assignment, saved even if not needed. |
| 1791 // Receiver is under the result value. | 2049 // Receiver is under the result value. |
| 1792 __ ldr(ip, MemOperand(sp, kPointerSize)); | 2050 __ ldr(ip, MemOperand(sp, kPointerSize)); |
| 1793 __ push(ip); | 2051 __ push(ip); |
| 1794 __ CallRuntime(Runtime::kToFastProperties, 1); | 2052 __ CallRuntime(Runtime::kToFastProperties, 1); |
| 1795 __ pop(r0); | 2053 __ pop(r0); |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1892 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; | 2150 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 1893 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); | 2151 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
| 1894 __ CallStub(&stub); | 2152 __ CallStub(&stub); |
| 1895 RecordJSReturnSite(expr); | 2153 RecordJSReturnSite(expr); |
| 1896 // Restore context register. | 2154 // Restore context register. |
| 1897 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); | 2155 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
| 1898 context()->DropAndPlug(1, r0); | 2156 context()->DropAndPlug(1, r0); |
| 1899 } | 2157 } |
| 1900 | 2158 |
| 1901 | 2159 |
| 2160 void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, |
| 2161 int arg_count) { |
| 2162 // Push copy of the first argument or undefined if it doesn't exist. |
| 2163 if (arg_count > 0) { |
| 2164 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); |
| 2165 } else { |
| 2166 __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); |
| 2167 } |
| 2168 __ push(r1); |
| 2169 |
| 2170 // Push the receiver of the enclosing function and do runtime call. |
| 2171 __ ldr(r1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); |
| 2172 __ push(r1); |
| 2173 // Push the strict mode flag. |
| 2174 __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); |
| 2175 __ push(r1); |
| 2176 |
| 2177 __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP |
| 2178 ? Runtime::kResolvePossiblyDirectEvalNoLookup |
| 2179 : Runtime::kResolvePossiblyDirectEval, 4); |
| 2180 } |
| 2181 |
| 2182 |
| 1902 void FullCodeGenerator::VisitCall(Call* expr) { | 2183 void FullCodeGenerator::VisitCall(Call* expr) { |
| 1903 #ifdef DEBUG | 2184 #ifdef DEBUG |
| 1904 // We want to verify that RecordJSReturnSite gets called on all paths | 2185 // We want to verify that RecordJSReturnSite gets called on all paths |
| 1905 // through this function. Avoid early returns. | 2186 // through this function. Avoid early returns. |
| 1906 expr->return_is_recorded_ = false; | 2187 expr->return_is_recorded_ = false; |
| 1907 #endif | 2188 #endif |
| 1908 | 2189 |
| 1909 Comment cmnt(masm_, "[ Call"); | 2190 Comment cmnt(masm_, "[ Call"); |
| 1910 Expression* fun = expr->expression(); | 2191 Expression* fun = expr->expression(); |
| 1911 Variable* var = fun->AsVariableProxy()->AsVariable(); | 2192 Variable* var = fun->AsVariableProxy()->AsVariable(); |
| 1912 | 2193 |
| 1913 if (var != NULL && var->is_possibly_eval()) { | 2194 if (var != NULL && var->is_possibly_eval()) { |
| 1914 // In a call to eval, we first call %ResolvePossiblyDirectEval to | 2195 // In a call to eval, we first call %ResolvePossiblyDirectEval to |
| 1915 // resolve the function we need to call and the receiver of the | 2196 // resolve the function we need to call and the receiver of the |
| 1916 // call. Then we call the resolved function using the given | 2197 // call. Then we call the resolved function using the given |
| 1917 // arguments. | 2198 // arguments. |
| 1918 ZoneList<Expression*>* args = expr->arguments(); | 2199 ZoneList<Expression*>* args = expr->arguments(); |
| 1919 int arg_count = args->length(); | 2200 int arg_count = args->length(); |
| 1920 | 2201 |
| 1921 { PreservePositionScope pos_scope(masm()->positions_recorder()); | 2202 { PreservePositionScope pos_scope(masm()->positions_recorder()); |
| 1922 VisitForStackValue(fun); | 2203 VisitForStackValue(fun); |
| 1923 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); | 2204 __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); |
| 1924 __ push(r2); // Reserved receiver slot. | 2205 __ push(r2); // Reserved receiver slot. |
| 1925 | 2206 |
| 1926 // Push the arguments. | 2207 // Push the arguments. |
| 1927 for (int i = 0; i < arg_count; i++) { | 2208 for (int i = 0; i < arg_count; i++) { |
| 1928 VisitForStackValue(args->at(i)); | 2209 VisitForStackValue(args->at(i)); |
| 1929 } | 2210 } |
| 1930 | 2211 |
| 1931 // Push copy of the function - found below the arguments. | 2212 // If we know that eval can only be shadowed by eval-introduced |
| 2213 // variables we attempt to load the global eval function directly |
| 2214 // in generated code. If we succeed, there is no need to perform a |
| 2215 // context lookup in the runtime system. |
| 2216 Label done; |
| 2217 if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { |
| 2218 Label slow; |
| 2219 EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), |
| 2220 NOT_INSIDE_TYPEOF, |
| 2221 &slow); |
| 2222 // Push the function and resolve eval. |
| 2223 __ push(r0); |
| 2224 EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); |
| 2225 __ jmp(&done); |
| 2226 __ bind(&slow); |
| 2227 } |
| 2228 |
| 2229 // Push copy of the function (found below the arguments) and |
| 2230 // resolve eval. |
| 1932 __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); | 2231 __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); |
| 1933 __ push(r1); | 2232 __ push(r1); |
| 1934 | 2233 EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); |
| 1935 // Push copy of the first argument or undefined if it doesn't exist. | 2234 if (done.is_linked()) { |
| 1936 if (arg_count > 0) { | 2235 __ bind(&done); |
| 1937 __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); | |
| 1938 __ push(r1); | |
| 1939 } else { | |
| 1940 __ push(r2); | |
| 1941 } | 2236 } |
| 1942 | 2237 |
| 1943 // Push the receiver of the enclosing function and do runtime call. | |
| 1944 __ ldr(r1, | |
| 1945 MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); | |
| 1946 __ push(r1); | |
| 1947 // Push the strict mode flag. | |
| 1948 __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); | |
| 1949 __ push(r1); | |
| 1950 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4); | |
| 1951 | |
| 1952 // The runtime call returns a pair of values in r0 (function) and | 2238 // The runtime call returns a pair of values in r0 (function) and |
| 1953 // r1 (receiver). Touch up the stack with the right values. | 2239 // r1 (receiver). Touch up the stack with the right values. |
| 1954 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize)); | 2240 __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize)); |
| 1955 __ str(r1, MemOperand(sp, arg_count * kPointerSize)); | 2241 __ str(r1, MemOperand(sp, arg_count * kPointerSize)); |
| 1956 } | 2242 } |
| 1957 | 2243 |
| 1958 // Record source position for debugger. | 2244 // Record source position for debugger. |
| 1959 SetSourcePosition(expr->position()); | 2245 SetSourcePosition(expr->position()); |
| 1960 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; | 2246 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
| 1961 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); | 2247 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
| (...skipping 795 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2757 VisitForStackValue(args->at(0)); | 3043 VisitForStackValue(args->at(0)); |
| 2758 VisitForStackValue(args->at(1)); | 3044 VisitForStackValue(args->at(1)); |
| 2759 | 3045 |
| 2760 StringCompareStub stub; | 3046 StringCompareStub stub; |
| 2761 __ CallStub(&stub); | 3047 __ CallStub(&stub); |
| 2762 context()->Plug(r0); | 3048 context()->Plug(r0); |
| 2763 } | 3049 } |
| 2764 | 3050 |
| 2765 | 3051 |
| 2766 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { | 3052 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { |
| 2767 // Load the argument on the stack and call the runtime. | 3053 // Load the argument on the stack and call the stub. |
| 3054 TranscendentalCacheStub stub(TranscendentalCache::SIN, |
| 3055 TranscendentalCacheStub::TAGGED); |
| 2768 ASSERT(args->length() == 1); | 3056 ASSERT(args->length() == 1); |
| 2769 VisitForStackValue(args->at(0)); | 3057 VisitForStackValue(args->at(0)); |
| 2770 __ CallRuntime(Runtime::kMath_sin, 1); | 3058 __ CallStub(&stub); |
| 2771 context()->Plug(r0); | 3059 context()->Plug(r0); |
| 2772 } | 3060 } |
| 2773 | 3061 |
| 2774 | 3062 |
| 2775 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { | 3063 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { |
| 2776 // Load the argument on the stack and call the runtime. | 3064 // Load the argument on the stack and call the stub. |
| 3065 TranscendentalCacheStub stub(TranscendentalCache::COS, |
| 3066 TranscendentalCacheStub::TAGGED); |
| 2777 ASSERT(args->length() == 1); | 3067 ASSERT(args->length() == 1); |
| 2778 VisitForStackValue(args->at(0)); | 3068 VisitForStackValue(args->at(0)); |
| 2779 __ CallRuntime(Runtime::kMath_cos, 1); | 3069 __ CallStub(&stub); |
| 3070 context()->Plug(r0); |
| 3071 } |
| 3072 |
| 3073 |
| 3074 void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { |
| 3075 // Load the argument on the stack and call the stub. |
| 3076 TranscendentalCacheStub stub(TranscendentalCache::LOG, |
| 3077 TranscendentalCacheStub::TAGGED); |
| 3078 ASSERT(args->length() == 1); |
| 3079 VisitForStackValue(args->at(0)); |
| 3080 __ CallStub(&stub); |
| 2780 context()->Plug(r0); | 3081 context()->Plug(r0); |
| 2781 } | 3082 } |
| 2782 | 3083 |
| 2783 | 3084 |
| 2784 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { | 3085 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { |
| 2785 // Load the argument on the stack and call the runtime function. | 3086 // Load the argument on the stack and call the runtime function. |
| 2786 ASSERT(args->length() == 1); | 3087 ASSERT(args->length() == 1); |
| 2787 VisitForStackValue(args->at(0)); | 3088 VisitForStackValue(args->at(0)); |
| 2788 __ CallRuntime(Runtime::kMath_sqrt, 1); | 3089 __ CallRuntime(Runtime::kMath_sqrt, 1); |
| 2789 context()->Plug(r0); | 3090 context()->Plug(r0); |
| 2790 } | 3091 } |
| 2791 | 3092 |
| 2792 | |
| 2793 void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { | |
| 2794 // Load the argument on the stack and call the runtime function. | |
| 2795 ASSERT(args->length() == 1); | |
| 2796 VisitForStackValue(args->at(0)); | |
| 2797 __ CallRuntime(Runtime::kMath_log, 1); | |
| 2798 context()->Plug(r0); | |
| 2799 } | |
| 2800 | |
| 2801 | 3093 |
| 2802 void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { | 3094 void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { |
| 2803 ASSERT(args->length() >= 2); | 3095 ASSERT(args->length() >= 2); |
| 2804 | 3096 |
| 2805 int arg_count = args->length() - 2; // For receiver and function. | 3097 int arg_count = args->length() - 2; // For receiver and function. |
| 2806 VisitForStackValue(args->at(0)); // Receiver. | 3098 VisitForStackValue(args->at(0)); // Receiver. |
| 2807 for (int i = 0; i < arg_count; i++) { | 3099 for (int i = 0; i < arg_count; i++) { |
| 2808 VisitForStackValue(args->at(i + 1)); | 3100 VisitForStackValue(args->at(i + 1)); |
| 2809 } | 3101 } |
| 2810 VisitForAccumulatorValue(args->at(arg_count + 1)); // Function. | 3102 VisitForAccumulatorValue(args->at(arg_count + 1)); // Function. |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2946 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); | 3238 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); |
| 2947 Split(eq, if_true, if_false, fall_through); | 3239 Split(eq, if_true, if_false, fall_through); |
| 2948 | 3240 |
| 2949 context()->Plug(if_true, if_false); | 3241 context()->Plug(if_true, if_false); |
| 2950 } | 3242 } |
| 2951 | 3243 |
| 2952 | 3244 |
| 2953 void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { | 3245 void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { |
| 2954 ASSERT(args->length() == 1); | 3246 ASSERT(args->length() == 1); |
| 2955 VisitForAccumulatorValue(args->at(0)); | 3247 VisitForAccumulatorValue(args->at(0)); |
| 3248 |
| 3249 if (FLAG_debug_code) { |
| 3250 __ AbortIfNotString(r0); |
| 3251 } |
| 3252 |
| 2956 __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset)); | 3253 __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset)); |
| 2957 __ IndexFromHash(r0, r0); | 3254 __ IndexFromHash(r0, r0); |
| 3255 |
| 2958 context()->Plug(r0); | 3256 context()->Plug(r0); |
| 2959 } | 3257 } |
| 2960 | 3258 |
| 2961 | 3259 |
| 2962 void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { | 3260 void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { |
| 3261 Label bailout, done, one_char_separator, long_separator, |
| 3262 non_trivial_array, not_size_one_array, loop, |
| 3263 empty_separator_loop, one_char_separator_loop, |
| 3264 one_char_separator_loop_entry, long_separator_loop; |
| 3265 |
| 3266 ASSERT(args->length() == 2); |
| 3267 VisitForStackValue(args->at(1)); |
| 3268 VisitForAccumulatorValue(args->at(0)); |
| 3269 |
| 3270 // All aliases of the same register have disjoint lifetimes. |
| 3271 Register array = r0; |
| 3272 Register elements = no_reg; // Will be r0. |
| 3273 Register result = no_reg; // Will be r0. |
| 3274 Register separator = r1; |
| 3275 Register array_length = r2; |
| 3276 Register result_pos = no_reg; // Will be r2 |
| 3277 Register string_length = r3; |
| 3278 Register string = r4; |
| 3279 Register element = r5; |
| 3280 Register elements_end = r6; |
| 3281 Register scratch1 = r7; |
| 3282 Register scratch2 = r9; |
| 3283 |
| 3284 // Separator operand is on the stack. |
| 3285 __ pop(separator); |
| 3286 |
| 3287 // Check that the array is a JSArray. |
| 3288 __ JumpIfSmi(array, &bailout); |
| 3289 __ CompareObjectType(array, scratch1, scratch2, JS_ARRAY_TYPE); |
| 3290 __ b(ne, &bailout); |
| 3291 |
| 3292 // Check that the array has fast elements. |
| 3293 __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset)); |
| 3294 __ tst(scratch2, Operand(1 << Map::kHasFastElements)); |
| 3295 __ b(eq, &bailout); |
| 3296 |
| 3297 // If the array has length zero, return the empty string. |
| 3298 __ ldr(array_length, FieldMemOperand(array, JSArray::kLengthOffset)); |
| 3299 __ SmiUntag(array_length, SetCC); |
| 3300 __ b(ne, &non_trivial_array); |
| 3301 __ LoadRoot(r0, Heap::kEmptyStringRootIndex); |
| 3302 __ b(&done); |
| 3303 |
| 3304 __ bind(&non_trivial_array); |
| 3305 |
| 3306 // Get the FixedArray containing array's elements. |
| 3307 elements = array; |
| 3308 __ ldr(elements, FieldMemOperand(array, JSArray::kElementsOffset)); |
| 3309 array = no_reg; // End of array's live range. |
| 3310 |
| 3311 // Check that all array elements are sequential ASCII strings, and |
| 3312 // accumulate the sum of their lengths, as a smi-encoded value. |
| 3313 __ mov(string_length, Operand(0)); |
| 3314 __ add(element, |
| 3315 elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
| 3316 __ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2)); |
| 3317 // Loop condition: while (element < elements_end). |
| 3318 // Live values in registers: |
| 3319 // elements: Fixed array of strings. |
| 3320 // array_length: Length of the fixed array of strings (not smi) |
| 3321 // separator: Separator string |
| 3322 // string_length: Accumulated sum of string lengths (smi). |
| 3323 // element: Current array element. |
| 3324 // elements_end: Array end. |
| 3325 if (FLAG_debug_code) { |
| 3326 __ cmp(array_length, Operand(0)); |
| 3327 __ Assert(gt, "No empty arrays here in EmitFastAsciiArrayJoin"); |
| 3328 } |
| 3329 __ bind(&loop); |
| 3330 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3331 __ JumpIfSmi(string, &bailout); |
| 3332 __ ldr(scratch1, FieldMemOperand(string, HeapObject::kMapOffset)); |
| 3333 __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); |
| 3334 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout); |
| 3335 __ ldr(scratch1, FieldMemOperand(string, SeqAsciiString::kLengthOffset)); |
| 3336 __ add(string_length, string_length, Operand(scratch1)); |
| 3337 __ b(vs, &bailout); |
| 3338 __ cmp(element, elements_end); |
| 3339 __ b(lt, &loop); |
| 3340 |
| 3341 // If array_length is 1, return elements[0], a string. |
| 3342 __ cmp(array_length, Operand(1)); |
| 3343 __ b(ne, ¬_size_one_array); |
| 3344 __ ldr(r0, FieldMemOperand(elements, FixedArray::kHeaderSize)); |
| 3345 __ b(&done); |
| 3346 |
| 3347 __ bind(¬_size_one_array); |
| 3348 |
| 3349 // Live values in registers: |
| 3350 // separator: Separator string |
| 3351 // array_length: Length of the array. |
| 3352 // string_length: Sum of string lengths (smi). |
| 3353 // elements: FixedArray of strings. |
| 3354 |
| 3355 // Check that the separator is a flat ASCII string. |
| 3356 __ JumpIfSmi(separator, &bailout); |
| 3357 __ ldr(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset)); |
| 3358 __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); |
| 3359 __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout); |
| 3360 |
| 3361 // Add (separator length times array_length) - separator length to the |
| 3362 // string_length to get the length of the result string. array_length is not |
| 3363 // smi but the other values are, so the result is a smi |
| 3364 __ ldr(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset)); |
| 3365 __ sub(string_length, string_length, Operand(scratch1)); |
| 3366 __ smull(scratch2, ip, array_length, scratch1); |
| 3367 // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are |
| 3368 // zero. |
| 3369 __ cmp(ip, Operand(0)); |
| 3370 __ b(ne, &bailout); |
| 3371 __ tst(scratch2, Operand(0x80000000)); |
| 3372 __ b(ne, &bailout); |
| 3373 __ add(string_length, string_length, Operand(scratch2)); |
| 3374 __ b(vs, &bailout); |
| 3375 __ SmiUntag(string_length); |
| 3376 |
| 3377 // Get first element in the array to free up the elements register to be used |
| 3378 // for the result. |
| 3379 __ add(element, |
| 3380 elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
| 3381 result = elements; // End of live range for elements. |
| 3382 elements = no_reg; |
| 3383 // Live values in registers: |
| 3384 // element: First array element |
| 3385 // separator: Separator string |
| 3386 // string_length: Length of result string (not smi) |
| 3387 // array_length: Length of the array. |
| 3388 __ AllocateAsciiString(result, |
| 3389 string_length, |
| 3390 scratch1, |
| 3391 scratch2, |
| 3392 elements_end, |
| 3393 &bailout); |
| 3394 // Prepare for looping. Set up elements_end to end of the array. Set |
| 3395 // result_pos to the position of the result where to write the first |
| 3396 // character. |
| 3397 __ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2)); |
| 3398 result_pos = array_length; // End of live range for array_length. |
| 3399 array_length = no_reg; |
| 3400 __ add(result_pos, |
| 3401 result, |
| 3402 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3403 |
| 3404 // Check the length of the separator. |
| 3405 __ ldr(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset)); |
| 3406 __ cmp(scratch1, Operand(Smi::FromInt(1))); |
| 3407 __ b(eq, &one_char_separator); |
| 3408 __ b(gt, &long_separator); |
| 3409 |
| 3410 // Empty separator case |
| 3411 __ bind(&empty_separator_loop); |
| 3412 // Live values in registers: |
| 3413 // result_pos: the position to which we are currently copying characters. |
| 3414 // element: Current array element. |
| 3415 // elements_end: Array end. |
| 3416 |
| 3417 // Copy next array element to the result. |
| 3418 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3419 __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset)); |
| 3420 __ SmiUntag(string_length); |
| 3421 __ add(string, string, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3422 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3423 __ cmp(element, elements_end); |
| 3424 __ b(lt, &empty_separator_loop); // End while (element < elements_end). |
| 3425 ASSERT(result.is(r0)); |
| 3426 __ b(&done); |
| 3427 |
| 3428 // One-character separator case |
| 3429 __ bind(&one_char_separator); |
| 3430 // Replace separator with its ascii character value. |
| 3431 __ ldrb(separator, FieldMemOperand(separator, SeqAsciiString::kHeaderSize)); |
| 3432 // Jump into the loop after the code that copies the separator, so the first |
| 3433 // element is not preceded by a separator |
| 3434 __ jmp(&one_char_separator_loop_entry); |
| 3435 |
| 3436 __ bind(&one_char_separator_loop); |
| 3437 // Live values in registers: |
| 3438 // result_pos: the position to which we are currently copying characters. |
| 3439 // element: Current array element. |
| 3440 // elements_end: Array end. |
| 3441 // separator: Single separator ascii char (in lower byte). |
| 3442 |
| 3443 // Copy the separator character to the result. |
| 3444 __ strb(separator, MemOperand(result_pos, 1, PostIndex)); |
| 3445 |
| 3446 // Copy next array element to the result. |
| 3447 __ bind(&one_char_separator_loop_entry); |
| 3448 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3449 __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset)); |
| 3450 __ SmiUntag(string_length); |
| 3451 __ add(string, string, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3452 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3453 __ cmp(element, elements_end); |
| 3454 __ b(lt, &one_char_separator_loop); // End while (element < elements_end). |
| 3455 ASSERT(result.is(r0)); |
| 3456 __ b(&done); |
| 3457 |
| 3458 // Long separator case (separator is more than one character). Entry is at the |
| 3459 // label long_separator below. |
| 3460 __ bind(&long_separator_loop); |
| 3461 // Live values in registers: |
| 3462 // result_pos: the position to which we are currently copying characters. |
| 3463 // element: Current array element. |
| 3464 // elements_end: Array end. |
| 3465 // separator: Separator string. |
| 3466 |
| 3467 // Copy the separator to the result. |
| 3468 __ ldr(string_length, FieldMemOperand(separator, String::kLengthOffset)); |
| 3469 __ SmiUntag(string_length); |
| 3470 __ add(string, |
| 3471 separator, |
| 3472 Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3473 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3474 |
| 3475 __ bind(&long_separator); |
| 3476 __ ldr(string, MemOperand(element, kPointerSize, PostIndex)); |
| 3477 __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset)); |
| 3478 __ SmiUntag(string_length); |
| 3479 __ add(string, string, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 3480 __ CopyBytes(string, result_pos, string_length, scratch1); |
| 3481 __ cmp(element, elements_end); |
| 3482 __ b(lt, &long_separator_loop); // End while (element < elements_end). |
| 3483 ASSERT(result.is(r0)); |
| 3484 __ b(&done); |
| 3485 |
| 3486 __ bind(&bailout); |
| 2963 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); | 3487 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
| 3488 __ bind(&done); |
| 2964 context()->Plug(r0); | 3489 context()->Plug(r0); |
| 2965 return; | |
| 2966 } | 3490 } |
| 2967 | 3491 |
| 2968 | 3492 |
| 2969 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { | 3493 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { |
| 2970 Handle<String> name = expr->name(); | 3494 Handle<String> name = expr->name(); |
| 2971 if (name->length() > 0 && name->Get(0) == '_') { | 3495 if (name->length() > 0 && name->Get(0) == '_') { |
| 2972 Comment cmnt(masm_, "[ InlineRuntimeCall"); | 3496 Comment cmnt(masm_, "[ InlineRuntimeCall"); |
| 2973 EmitInlineRuntimeCall(expr); | 3497 EmitInlineRuntimeCall(expr); |
| 2974 return; | 3498 return; |
| 2975 } | 3499 } |
| (...skipping 29 matching lines...) Expand all Loading... |
| 3005 context()->Plug(r0); | 3529 context()->Plug(r0); |
| 3006 } | 3530 } |
| 3007 | 3531 |
| 3008 | 3532 |
| 3009 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { | 3533 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { |
| 3010 switch (expr->op()) { | 3534 switch (expr->op()) { |
| 3011 case Token::DELETE: { | 3535 case Token::DELETE: { |
| 3012 Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); | 3536 Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); |
| 3013 Property* prop = expr->expression()->AsProperty(); | 3537 Property* prop = expr->expression()->AsProperty(); |
| 3014 Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); | 3538 Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); |
| 3015 if (prop == NULL && var == NULL) { | 3539 |
| 3016 // Result of deleting non-property, non-variable reference is true. | 3540 if (prop != NULL) { |
| 3017 // The subexpression may have side effects. | 3541 if (prop->is_synthetic()) { |
| 3018 VisitForEffect(expr->expression()); | 3542 // Result of deleting parameters is false, even when they rewrite |
| 3019 context()->Plug(true); | 3543 // to accesses on the arguments object. |
| 3020 } else if (var != NULL && | 3544 context()->Plug(false); |
| 3021 !var->is_global() && | 3545 } else { |
| 3022 var->AsSlot() != NULL && | |
| 3023 var->AsSlot()->type() != Slot::LOOKUP) { | |
| 3024 // Result of deleting non-global, non-dynamic variables is false. | |
| 3025 // The subexpression does not have side effects. | |
| 3026 context()->Plug(false); | |
| 3027 } else { | |
| 3028 // Property or variable reference. Call the delete builtin with | |
| 3029 // object and property name as arguments. | |
| 3030 if (prop != NULL) { | |
| 3031 VisitForStackValue(prop->obj()); | 3546 VisitForStackValue(prop->obj()); |
| 3032 VisitForStackValue(prop->key()); | 3547 VisitForStackValue(prop->key()); |
| 3548 __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); |
| 3549 __ push(r1); |
| 3033 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); | 3550 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); |
| 3034 } else if (var->is_global()) { | 3551 context()->Plug(r0); |
| 3035 __ ldr(r1, GlobalObjectOperand()); | 3552 } |
| 3036 __ mov(r0, Operand(var->name())); | 3553 } else if (var != NULL) { |
| 3037 __ Push(r1, r0); | 3554 // Delete of an unqualified identifier is disallowed in strict mode |
| 3555 // but "delete this" is. |
| 3556 ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); |
| 3557 if (var->is_global()) { |
| 3558 __ ldr(r2, GlobalObjectOperand()); |
| 3559 __ mov(r1, Operand(var->name())); |
| 3560 __ mov(r0, Operand(Smi::FromInt(kNonStrictMode))); |
| 3561 __ Push(r2, r1, r0); |
| 3038 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); | 3562 __ InvokeBuiltin(Builtins::DELETE, CALL_JS); |
| 3563 context()->Plug(r0); |
| 3564 } else if (var->AsSlot() != NULL && |
| 3565 var->AsSlot()->type() != Slot::LOOKUP) { |
| 3566 // Result of deleting non-global, non-dynamic variables is false. |
| 3567 // The subexpression does not have side effects. |
| 3568 context()->Plug(false); |
| 3039 } else { | 3569 } else { |
| 3040 // Non-global variable. Call the runtime to delete from the | 3570 // Non-global variable. Call the runtime to try to delete from the |
| 3041 // context where the variable was introduced. | 3571 // context where the variable was introduced. |
| 3042 __ push(context_register()); | 3572 __ push(context_register()); |
| 3043 __ mov(r2, Operand(var->name())); | 3573 __ mov(r2, Operand(var->name())); |
| 3044 __ push(r2); | 3574 __ push(r2); |
| 3045 __ CallRuntime(Runtime::kDeleteContextSlot, 2); | 3575 __ CallRuntime(Runtime::kDeleteContextSlot, 2); |
| 3576 context()->Plug(r0); |
| 3046 } | 3577 } |
| 3047 context()->Plug(r0); | 3578 } else { |
| 3579 // Result of deleting non-property, non-variable reference is true. |
| 3580 // The subexpression may have side effects. |
| 3581 VisitForEffect(expr->expression()); |
| 3582 context()->Plug(true); |
| 3048 } | 3583 } |
| 3049 break; | 3584 break; |
| 3050 } | 3585 } |
| 3051 | 3586 |
| 3052 case Token::VOID: { | 3587 case Token::VOID: { |
| 3053 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); | 3588 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); |
| 3054 VisitForEffect(expr->expression()); | 3589 VisitForEffect(expr->expression()); |
| 3055 context()->Plug(Heap::kUndefinedValueRootIndex); | 3590 context()->Plug(Heap::kUndefinedValueRootIndex); |
| 3056 break; | 3591 break; |
| 3057 } | 3592 } |
| 3058 | 3593 |
| 3059 case Token::NOT: { | 3594 case Token::NOT: { |
| 3060 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); | 3595 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); |
| 3061 Label materialize_true, materialize_false; | 3596 if (context()->IsEffect()) { |
| 3062 Label* if_true = NULL; | 3597 // Unary NOT has no side effects so it's only necessary to visit the |
| 3063 Label* if_false = NULL; | 3598 // subexpression. Match the optimizing compiler by not branching. |
| 3064 Label* fall_through = NULL; | 3599 VisitForEffect(expr->expression()); |
| 3600 } else { |
| 3601 Label materialize_true, materialize_false; |
| 3602 Label* if_true = NULL; |
| 3603 Label* if_false = NULL; |
| 3604 Label* fall_through = NULL; |
| 3065 | 3605 |
| 3066 // Notice that the labels are swapped. | 3606 // Notice that the labels are swapped. |
| 3067 context()->PrepareTest(&materialize_true, &materialize_false, | 3607 context()->PrepareTest(&materialize_true, &materialize_false, |
| 3068 &if_false, &if_true, &fall_through); | 3608 &if_false, &if_true, &fall_through); |
| 3069 if (context()->IsTest()) ForwardBailoutToChild(expr); | 3609 if (context()->IsTest()) ForwardBailoutToChild(expr); |
| 3070 VisitForControl(expr->expression(), if_true, if_false, fall_through); | 3610 VisitForControl(expr->expression(), if_true, if_false, fall_through); |
| 3071 context()->Plug(if_false, if_true); // Labels swapped. | 3611 context()->Plug(if_false, if_true); // Labels swapped. |
| 3612 } |
| 3072 break; | 3613 break; |
| 3073 } | 3614 } |
| 3074 | 3615 |
| 3075 case Token::TYPEOF: { | 3616 case Token::TYPEOF: { |
| 3076 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); | 3617 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); |
| 3077 { StackValueContext context(this); | 3618 { StackValueContext context(this); |
| 3078 VisitForTypeofValue(expr->expression()); | 3619 VisitForTypeofValue(expr->expression()); |
| 3079 } | 3620 } |
| 3080 __ CallRuntime(Runtime::kTypeof, 1); | 3621 __ CallRuntime(Runtime::kTypeof, 1); |
| 3081 context()->Plug(r0); | 3622 context()->Plug(r0); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 3093 __ bind(&no_conversion); | 3634 __ bind(&no_conversion); |
| 3094 context()->Plug(result_register()); | 3635 context()->Plug(result_register()); |
| 3095 break; | 3636 break; |
| 3096 } | 3637 } |
| 3097 | 3638 |
| 3098 case Token::SUB: { | 3639 case Token::SUB: { |
| 3099 Comment cmt(masm_, "[ UnaryOperation (SUB)"); | 3640 Comment cmt(masm_, "[ UnaryOperation (SUB)"); |
| 3100 bool can_overwrite = expr->expression()->ResultOverwriteAllowed(); | 3641 bool can_overwrite = expr->expression()->ResultOverwriteAllowed(); |
| 3101 UnaryOverwriteMode overwrite = | 3642 UnaryOverwriteMode overwrite = |
| 3102 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; | 3643 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; |
| 3103 GenericUnaryOpStub stub(Token::SUB, | 3644 GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS); |
| 3104 overwrite, | |
| 3105 NO_UNARY_FLAGS); | |
| 3106 // GenericUnaryOpStub expects the argument to be in the | 3645 // GenericUnaryOpStub expects the argument to be in the |
| 3107 // accumulator register r0. | 3646 // accumulator register r0. |
| 3108 VisitForAccumulatorValue(expr->expression()); | 3647 VisitForAccumulatorValue(expr->expression()); |
| 3109 __ CallStub(&stub); | 3648 __ CallStub(&stub); |
| 3110 context()->Plug(r0); | 3649 context()->Plug(r0); |
| 3111 break; | 3650 break; |
| 3112 } | 3651 } |
| 3113 | 3652 |
| 3114 case Token::BIT_NOT: { | 3653 case Token::BIT_NOT: { |
| 3115 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)"); | 3654 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)"); |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3228 case KEYED_PROPERTY: | 3767 case KEYED_PROPERTY: |
| 3229 __ str(r0, MemOperand(sp, 2 * kPointerSize)); | 3768 __ str(r0, MemOperand(sp, 2 * kPointerSize)); |
| 3230 break; | 3769 break; |
| 3231 } | 3770 } |
| 3232 } | 3771 } |
| 3233 } | 3772 } |
| 3234 | 3773 |
| 3235 | 3774 |
| 3236 // Inline smi case if we are in a loop. | 3775 // Inline smi case if we are in a loop. |
| 3237 Label stub_call, done; | 3776 Label stub_call, done; |
| 3777 JumpPatchSite patch_site(masm_); |
| 3778 |
| 3238 int count_value = expr->op() == Token::INC ? 1 : -1; | 3779 int count_value = expr->op() == Token::INC ? 1 : -1; |
| 3239 if (ShouldInlineSmiCase(expr->op())) { | 3780 if (ShouldInlineSmiCase(expr->op())) { |
| 3240 __ add(r0, r0, Operand(Smi::FromInt(count_value)), SetCC); | 3781 __ add(r0, r0, Operand(Smi::FromInt(count_value)), SetCC); |
| 3241 __ b(vs, &stub_call); | 3782 __ b(vs, &stub_call); |
| 3242 // We could eliminate this smi check if we split the code at | 3783 // We could eliminate this smi check if we split the code at |
| 3243 // the first smi check before calling ToNumber. | 3784 // the first smi check before calling ToNumber. |
| 3244 __ JumpIfSmi(r0, &done); | 3785 patch_site.EmitJumpIfSmi(r0, &done); |
| 3786 |
| 3245 __ bind(&stub_call); | 3787 __ bind(&stub_call); |
| 3246 // Call stub. Undo operation first. | 3788 // Call stub. Undo operation first. |
| 3247 __ sub(r0, r0, Operand(Smi::FromInt(count_value))); | 3789 __ sub(r0, r0, Operand(Smi::FromInt(count_value))); |
| 3248 } | 3790 } |
| 3249 __ mov(r1, Operand(Smi::FromInt(count_value))); | 3791 __ mov(r1, Operand(Smi::FromInt(count_value))); |
| 3250 | 3792 |
| 3251 // Record position before stub call. | 3793 // Record position before stub call. |
| 3252 SetSourcePosition(expr->position()); | 3794 SetSourcePosition(expr->position()); |
| 3253 | 3795 |
| 3254 GenericBinaryOpStub stub(Token::ADD, NO_OVERWRITE, r1, r0); | 3796 TypeRecordingBinaryOpStub stub(Token::ADD, NO_OVERWRITE); |
| 3255 __ CallStub(&stub); | 3797 EmitCallIC(stub.GetCode(), &patch_site); |
| 3256 __ bind(&done); | 3798 __ bind(&done); |
| 3257 | 3799 |
| 3258 // Store the value returned in r0. | 3800 // Store the value returned in r0. |
| 3259 switch (assign_type) { | 3801 switch (assign_type) { |
| 3260 case VARIABLE: | 3802 case VARIABLE: |
| 3261 if (expr->is_postfix()) { | 3803 if (expr->is_postfix()) { |
| 3262 { EffectContext context(this); | 3804 { EffectContext context(this); |
| 3263 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | 3805 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), |
| 3264 Token::ASSIGN); | 3806 Token::ASSIGN); |
| 3265 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3807 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3266 context.Plug(r0); | 3808 context.Plug(r0); |
| 3267 } | 3809 } |
| 3268 // For all contexts except EffectConstant We have the result on | 3810 // For all contexts except EffectConstant We have the result on |
| 3269 // top of the stack. | 3811 // top of the stack. |
| 3270 if (!context()->IsEffect()) { | 3812 if (!context()->IsEffect()) { |
| 3271 context()->PlugTOS(); | 3813 context()->PlugTOS(); |
| 3272 } | 3814 } |
| 3273 } else { | 3815 } else { |
| 3274 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | 3816 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), |
| 3275 Token::ASSIGN); | 3817 Token::ASSIGN); |
| 3276 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3818 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3277 context()->Plug(r0); | 3819 context()->Plug(r0); |
| 3278 } | 3820 } |
| 3279 break; | 3821 break; |
| 3280 case NAMED_PROPERTY: { | 3822 case NAMED_PROPERTY: { |
| 3281 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); | 3823 __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); |
| 3282 __ pop(r1); | 3824 __ pop(r1); |
| 3283 Handle<Code> ic(isolate()->builtins()->builtin( | 3825 Handle<Code> ic(isolate()->builtins()->builtin( |
| 3284 Builtins::StoreIC_Initialize)); | 3826 is_strict() ? Builtins::StoreIC_Initialize_Strict |
| 3827 : Builtins::StoreIC_Initialize)); |
| 3285 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 3828 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 3286 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3829 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3287 if (expr->is_postfix()) { | 3830 if (expr->is_postfix()) { |
| 3288 if (!context()->IsEffect()) { | 3831 if (!context()->IsEffect()) { |
| 3289 context()->PlugTOS(); | 3832 context()->PlugTOS(); |
| 3290 } | 3833 } |
| 3291 } else { | 3834 } else { |
| 3292 context()->Plug(r0); | 3835 context()->Plug(r0); |
| 3293 } | 3836 } |
| 3294 break; | 3837 break; |
| 3295 } | 3838 } |
| 3296 case KEYED_PROPERTY: { | 3839 case KEYED_PROPERTY: { |
| 3297 __ pop(r1); // Key. | 3840 __ pop(r1); // Key. |
| 3298 __ pop(r2); // Receiver. | 3841 __ pop(r2); // Receiver. |
| 3299 Handle<Code> ic(isolate()->builtins()->builtin( | 3842 Handle<Code> ic(isolate()->builtins()->builtin( |
| 3300 Builtins::KeyedStoreIC_Initialize)); | 3843 is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict |
| 3844 : Builtins::KeyedStoreIC_Initialize)); |
| 3301 EmitCallIC(ic, RelocInfo::CODE_TARGET); | 3845 EmitCallIC(ic, RelocInfo::CODE_TARGET); |
| 3302 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); | 3846 PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); |
| 3303 if (expr->is_postfix()) { | 3847 if (expr->is_postfix()) { |
| 3304 if (!context()->IsEffect()) { | 3848 if (!context()->IsEffect()) { |
| 3305 context()->PlugTOS(); | 3849 context()->PlugTOS(); |
| 3306 } | 3850 } |
| 3307 } else { | 3851 } else { |
| 3308 context()->Plug(r0); | 3852 context()->Plug(r0); |
| 3309 } | 3853 } |
| 3310 break; | 3854 break; |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3527 cond = ge; | 4071 cond = ge; |
| 3528 __ pop(r1); | 4072 __ pop(r1); |
| 3529 break; | 4073 break; |
| 3530 case Token::IN: | 4074 case Token::IN: |
| 3531 case Token::INSTANCEOF: | 4075 case Token::INSTANCEOF: |
| 3532 default: | 4076 default: |
| 3533 UNREACHABLE(); | 4077 UNREACHABLE(); |
| 3534 } | 4078 } |
| 3535 | 4079 |
| 3536 bool inline_smi_code = ShouldInlineSmiCase(op); | 4080 bool inline_smi_code = ShouldInlineSmiCase(op); |
| 4081 JumpPatchSite patch_site(masm_); |
| 3537 if (inline_smi_code) { | 4082 if (inline_smi_code) { |
| 3538 Label slow_case; | 4083 Label slow_case; |
| 3539 __ orr(r2, r0, Operand(r1)); | 4084 __ orr(r2, r0, Operand(r1)); |
| 3540 __ JumpIfNotSmi(r2, &slow_case); | 4085 patch_site.EmitJumpIfNotSmi(r2, &slow_case); |
| 3541 __ cmp(r1, r0); | 4086 __ cmp(r1, r0); |
| 3542 Split(cond, if_true, if_false, NULL); | 4087 Split(cond, if_true, if_false, NULL); |
| 3543 __ bind(&slow_case); | 4088 __ bind(&slow_case); |
| 3544 } | 4089 } |
| 3545 CompareFlags flags = inline_smi_code | 4090 |
| 3546 ? NO_SMI_COMPARE_IN_STUB | 4091 // Record position and call the compare IC. |
| 3547 : NO_COMPARE_FLAGS; | 4092 SetSourcePosition(expr->position()); |
| 3548 CompareStub stub(cond, strict, flags, r1, r0); | 4093 Handle<Code> ic = CompareIC::GetUninitialized(op); |
| 3549 __ CallStub(&stub); | 4094 EmitCallIC(ic, &patch_site); |
| 3550 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); | 4095 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); |
| 3551 __ cmp(r0, Operand(0, RelocInfo::NONE)); | 4096 __ cmp(r0, Operand(0)); |
| 3552 Split(cond, if_true, if_false, fall_through); | 4097 Split(cond, if_true, if_false, fall_through); |
| 3553 } | 4098 } |
| 3554 } | 4099 } |
| 3555 | 4100 |
| 3556 // Convert the result of the comparison into one expected for this | 4101 // Convert the result of the comparison into one expected for this |
| 3557 // expression's context. | 4102 // expression's context. |
| 3558 context()->Plug(if_true, if_false); | 4103 context()->Plug(if_true, if_false); |
| 3559 } | 4104 } |
| 3560 | 4105 |
| 3561 | 4106 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3604 | 4149 |
| 3605 | 4150 |
| 3606 Register FullCodeGenerator::context_register() { | 4151 Register FullCodeGenerator::context_register() { |
| 3607 return cp; | 4152 return cp; |
| 3608 } | 4153 } |
| 3609 | 4154 |
| 3610 | 4155 |
| 3611 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) { | 4156 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) { |
| 3612 ASSERT(mode == RelocInfo::CODE_TARGET || | 4157 ASSERT(mode == RelocInfo::CODE_TARGET || |
| 3613 mode == RelocInfo::CODE_TARGET_CONTEXT); | 4158 mode == RelocInfo::CODE_TARGET_CONTEXT); |
| 4159 switch (ic->kind()) { |
| 4160 case Code::LOAD_IC: |
| 4161 __ IncrementCounter(COUNTERS->named_load_full(), 1, r1, r2); |
| 4162 break; |
| 4163 case Code::KEYED_LOAD_IC: |
| 4164 __ IncrementCounter(COUNTERS->keyed_load_full(), 1, r1, r2); |
| 4165 break; |
| 4166 case Code::STORE_IC: |
| 4167 __ IncrementCounter(COUNTERS->named_store_full(), 1, r1, r2); |
| 4168 break; |
| 4169 case Code::KEYED_STORE_IC: |
| 4170 __ IncrementCounter(COUNTERS->keyed_store_full(), 1, r1, r2); |
| 4171 default: |
| 4172 break; |
| 4173 } |
| 4174 |
| 3614 __ Call(ic, mode); | 4175 __ Call(ic, mode); |
| 3615 } | 4176 } |
| 3616 | 4177 |
| 3617 | 4178 |
| 4179 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) { |
| 4180 switch (ic->kind()) { |
| 4181 case Code::LOAD_IC: |
| 4182 __ IncrementCounter(COUNTERS->named_load_full(), 1, r1, r2); |
| 4183 break; |
| 4184 case Code::KEYED_LOAD_IC: |
| 4185 __ IncrementCounter(COUNTERS->keyed_load_full(), 1, r1, r2); |
| 4186 break; |
| 4187 case Code::STORE_IC: |
| 4188 __ IncrementCounter(COUNTERS->named_store_full(), 1, r1, r2); |
| 4189 break; |
| 4190 case Code::KEYED_STORE_IC: |
| 4191 __ IncrementCounter(COUNTERS->keyed_store_full(), 1, r1, r2); |
| 4192 default: |
| 4193 break; |
| 4194 } |
| 4195 |
| 4196 __ Call(ic, RelocInfo::CODE_TARGET); |
| 4197 if (patch_site != NULL && patch_site->is_bound()) { |
| 4198 patch_site->EmitPatchInfo(); |
| 4199 } else { |
| 4200 __ nop(); // Signals no inlined code. |
| 4201 } |
| 4202 } |
| 4203 |
| 4204 |
| 3618 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { | 4205 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { |
| 3619 ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset); | 4206 ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset); |
| 3620 __ str(value, MemOperand(fp, frame_offset)); | 4207 __ str(value, MemOperand(fp, frame_offset)); |
| 3621 } | 4208 } |
| 3622 | 4209 |
| 3623 | 4210 |
| 3624 void FullCodeGenerator::LoadContextField(Register dst, int context_index) { | 4211 void FullCodeGenerator::LoadContextField(Register dst, int context_index) { |
| 3625 __ ldr(dst, ContextOperand(cp, context_index)); | 4212 __ ldr(dst, ContextOperand(cp, context_index)); |
| 3626 } | 4213 } |
| 3627 | 4214 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 3652 __ mov(r1, Operand(r1, ASR, 1)); // Un-smi-tag value. | 4239 __ mov(r1, Operand(r1, ASR, 1)); // Un-smi-tag value. |
| 3653 __ add(pc, r1, Operand(masm_->CodeObject())); | 4240 __ add(pc, r1, Operand(masm_->CodeObject())); |
| 3654 } | 4241 } |
| 3655 | 4242 |
| 3656 | 4243 |
| 3657 #undef __ | 4244 #undef __ |
| 3658 | 4245 |
| 3659 } } // namespace v8::internal | 4246 } } // namespace v8::internal |
| 3660 | 4247 |
| 3661 #endif // V8_TARGET_ARCH_ARM | 4248 #endif // V8_TARGET_ARCH_ARM |
| OLD | NEW |