diff options
Diffstat (limited to 'deps/v8/src/arm')
-rw-r--r-- | deps/v8/src/arm/assembler-arm.cc | 8 | ||||
-rw-r--r-- | deps/v8/src/arm/assembler-arm.h | 20 | ||||
-rw-r--r-- | deps/v8/src/arm/assembler-thumb2.h | 2 | ||||
-rw-r--r-- | deps/v8/src/arm/codegen-arm.cc | 470 | ||||
-rw-r--r-- | deps/v8/src/arm/codegen-arm.h | 11 | ||||
-rw-r--r-- | deps/v8/src/arm/debug-arm.cc | 2 | ||||
-rw-r--r-- | deps/v8/src/arm/full-codegen-arm.cc | 15 | ||||
-rw-r--r-- | deps/v8/src/arm/ic-arm.cc | 171 | ||||
-rw-r--r-- | deps/v8/src/arm/macro-assembler-arm.cc | 4 | ||||
-rw-r--r-- | deps/v8/src/arm/macro-assembler-arm.h | 59 | ||||
-rw-r--r-- | deps/v8/src/arm/simulator-arm.cc | 6 | ||||
-rw-r--r-- | deps/v8/src/arm/stub-cache-arm.cc | 45 | ||||
-rw-r--r-- | deps/v8/src/arm/virtual-frame-arm.cc | 76 | ||||
-rw-r--r-- | deps/v8/src/arm/virtual-frame-arm.h | 19 |
14 files changed, 682 insertions, 226 deletions
diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc index a6d81bfbf9..7990368e66 100644 --- a/deps/v8/src/arm/assembler-arm.cc +++ b/deps/v8/src/arm/assembler-arm.cc @@ -800,9 +800,10 @@ void Assembler::b(int branch_offset, Condition cond) { ASSERT(is_int24(imm24)); emit(cond | B27 | B25 | (imm24 & Imm24Mask)); - if (cond == al) + if (cond == al) { // Dead code is a good location to emit the constant pool. CheckConstPool(false, false); + } } @@ -1784,6 +1785,11 @@ bool Assembler::ImmediateFitsAddrMode1Instruction(int32_t imm32) { } +void Assembler::BlockConstPoolFor(int instructions) { + BlockConstPoolBefore(pc_offset() + instructions * kInstrSize); +} + + // Debugging. void Assembler::RecordJSReturn() { WriteRecordedPositions(); diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index e6284d3c98..839ed67375 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -941,6 +941,10 @@ class Assembler : public Malloced { DISALLOW_IMPLICIT_CONSTRUCTORS(BlockConstPoolScope); }; + // Postpone the generation of the constant pool for the specified number of + // instructions. + void BlockConstPoolFor(int instructions); + // Debugging // Mark address of the ExitJSFrame code. @@ -956,14 +960,7 @@ class Assembler : public Malloced { int pc_offset() const { return pc_ - buffer_; } int current_position() const { return current_position_; } - int current_statement_position() const { return current_position_; } - - void StartBlockConstPool() { - const_pool_blocked_nesting_++; - } - void EndBlockConstPool() { - const_pool_blocked_nesting_--; - } + int current_statement_position() const { return current_statement_position_; } // Read/patch instructions static Instr instr_at(byte* pc) { return *reinterpret_cast<Instr*>(pc); } @@ -1001,6 +998,13 @@ class Assembler : public Malloced { if (no_const_pool_before_ < pc_offset) no_const_pool_before_ = pc_offset; } + void StartBlockConstPool() { + const_pool_blocked_nesting_++; + } + void EndBlockConstPool() { + const_pool_blocked_nesting_--; + } + private: // Code buffer: // The buffer into which code and relocation info are generated. diff --git a/deps/v8/src/arm/assembler-thumb2.h b/deps/v8/src/arm/assembler-thumb2.h index 869ac46195..2da11389ac 100644 --- a/deps/v8/src/arm/assembler-thumb2.h +++ b/deps/v8/src/arm/assembler-thumb2.h @@ -898,7 +898,7 @@ class Assembler : public Malloced { int pc_offset() const { return pc_ - buffer_; } int current_position() const { return current_position_; } - int current_statement_position() const { return current_position_; } + int current_statement_position() const { return current_statement_position_; } protected: int buffer_space() const { return reloc_info_writer.pos() - pc_; } diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 3594aab1d5..291a763fb9 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -351,17 +351,17 @@ void CodeGenerator::Generate(CompilationInfo* info) { int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize; masm_->add(sp, sp, Operand(sp_delta)); masm_->Jump(lr); - } #ifdef DEBUG - // Check that the size of the code used for returning matches what is - // expected by the debugger. If the sp_delts above cannot be encoded in the - // add instruction the add will generate two instructions. - int return_sequence_length = - masm_->InstructionsGeneratedSince(&check_exit_codesize); - CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength || - return_sequence_length == Assembler::kJSReturnSequenceLength + 1); + // Check that the size of the code used for returning matches what is + // expected by the debugger. If the sp_delts above cannot be encoded in + // the add instruction the add will generate two instructions. + int return_sequence_length = + masm_->InstructionsGeneratedSince(&check_exit_codesize); + CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength || + return_sequence_length == Assembler::kJSReturnSequenceLength + 1); #endif + } } // Adjust for function-level loop nesting. @@ -570,9 +570,9 @@ void CodeGenerator::Load(Expression* expr) { void CodeGenerator::LoadGlobal() { - VirtualFrame::SpilledScope spilled_scope(frame_); - __ ldr(r0, GlobalObject()); - frame_->EmitPush(r0); + Register reg = frame_->GetTOSRegister(); + __ ldr(reg, GlobalObject()); + frame_->EmitPush(reg); } @@ -619,7 +619,7 @@ void CodeGenerator::StoreArgumentsObject(bool initial) { __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize)); __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters()))); frame_->Adjust(3); - __ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit()); + __ Push(r2, r1, r0); frame_->CallStub(&stub, 3); frame_->EmitPush(r0); } @@ -687,7 +687,6 @@ Reference::~Reference() { void CodeGenerator::LoadReference(Reference* ref) { - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ LoadReference"); Expression* e = ref->expression(); Property* property = e->AsProperty(); @@ -696,11 +695,11 @@ void CodeGenerator::LoadReference(Reference* ref) { if (property != NULL) { // The expression is either a property or a variable proxy that rewrites // to a property. - LoadAndSpill(property->obj()); + Load(property->obj()); if (property->key()->IsPropertyName()) { ref->set_type(Reference::NAMED); } else { - LoadAndSpill(property->key()); + Load(property->key()); ref->set_type(Reference::KEYED); } } else if (var != NULL) { @@ -715,6 +714,7 @@ void CodeGenerator::LoadReference(Reference* ref) { } } else { // Anything else is a runtime error. + VirtualFrame::SpilledScope spilled_scope(frame_); LoadAndSpill(e); frame_->CallRuntime(Runtime::kThrowReferenceError, 1); } @@ -1527,6 +1527,7 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, LoadAndSpill(applicand); Handle<String> name = Factory::LookupAsciiSymbol("apply"); __ mov(r2, Operand(name)); + __ ldr(r0, MemOperand(sp, 0)); frame_->CallLoadIC(RelocInfo::CODE_TARGET); frame_->EmitPush(r0); @@ -2948,9 +2949,10 @@ void CodeGenerator::VisitConditional(Conditional* node) { void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { if (slot->type() == Slot::LOOKUP) { - VirtualFrame::SpilledScope spilled_scope(frame_); ASSERT(slot->var()->is_dynamic()); + // JumpTargets do not yet support merging frames so the frame must be + // spilled when jumping to these targets. JumpTarget slow; JumpTarget done; @@ -2960,16 +2962,18 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { // perform a runtime call for all variables in the scope // containing the eval. if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { - LoadFromGlobalSlotCheckExtensions(slot, typeof_state, r1, r2, &slow); + LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow); // If there was no control flow to slow, we can exit early. if (!slow.is_linked()) { frame_->EmitPush(r0); return; } + frame_->SpillAll(); done.Jump(); } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { + frame_->SpillAll(); Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); // Only generate the fast case for locals that rewrite to slots. // This rules out argument loads. @@ -2992,6 +2996,7 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { } slow.Bind(); + VirtualFrame::SpilledScope spilled_scope(frame_); frame_->EmitPush(cp); __ mov(r0, Operand(slot->var()->name())); frame_->EmitPush(r0); @@ -3143,16 +3148,17 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot, TypeofState typeof_state, - Register tmp, - Register tmp2, JumpTarget* slow) { // Check that no extension objects have been created by calls to // eval from the current scope to the global scope. + Register tmp = frame_->scratch0(); + Register tmp2 = frame_->scratch1(); Register context = cp; Scope* s = scope(); while (s != NULL) { if (s->num_heap_slots() > 0) { if (s->calls_eval()) { + frame_->SpillAll(); // Check that extension is NULL. __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX)); __ tst(tmp2, tmp2); @@ -3170,6 +3176,7 @@ void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot, } if (s->is_eval_scope()) { + frame_->SpillAll(); Label next, fast; __ Move(tmp, context); __ bind(&next); @@ -3192,6 +3199,7 @@ void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot, // Load the global object. LoadGlobal(); // Setup the name register and call load IC. + frame_->SpillAllButCopyTOSToR0(); __ mov(r2, Operand(slot->var()->name())); frame_->CallLoadIC(typeof_state == INSIDE_TYPEOF ? RelocInfo::CODE_TARGET @@ -3524,7 +3532,6 @@ void CodeGenerator::VisitProperty(Property* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(frame_); Comment cmnt(masm_, "[ Property"); { Reference property(this, node); @@ -3703,7 +3710,7 @@ void CodeGenerator::VisitCall(Call* node) { LoadAndSpill(property->obj()); LoadAndSpill(property->key()); - EmitKeyedLoad(false); + EmitKeyedLoad(); frame_->Drop(); // key // Put the function below the receiver. if (property->is_synthetic()) { @@ -4437,8 +4444,7 @@ class DeferredSearchCache: public DeferredCode { void DeferredSearchCache::Generate() { - __ push(cache_); - __ push(key_); + __ Push(cache_, key_); __ CallRuntime(Runtime::kGetFromCache, 2); if (!dst_.is(r0)) { __ mov(dst_, r0); @@ -5231,34 +5237,105 @@ class DeferredReferenceGetNamedValue: public DeferredCode { set_comment("[ DeferredReferenceGetNamedValue"); } - virtual void BeforeGenerate(); virtual void Generate(); - virtual void AfterGenerate(); private: Handle<String> name_; }; -void DeferredReferenceGetNamedValue::BeforeGenerate() { - __ StartBlockConstPool(); +void DeferredReferenceGetNamedValue::Generate() { + Register scratch1 = VirtualFrame::scratch0(); + Register scratch2 = VirtualFrame::scratch1(); + __ DecrementCounter(&Counters::named_load_inline, 1, scratch1, scratch2); + __ IncrementCounter(&Counters::named_load_inline_miss, 1, scratch1, scratch2); + + // Setup the registers and call load IC. + // On entry to this deferred code, r0 is assumed to already contain the + // receiver from the top of the stack. + __ mov(r2, Operand(name_)); + + // The rest of the instructions in the deferred code must be together. + { Assembler::BlockConstPoolScope block_const_pool(masm_); + Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + // The call must be followed by a nop(1) instruction to indicate that the + // in-object has been inlined. + __ nop(PROPERTY_ACCESS_INLINED); + + // Block the constant pool for one more instruction after leaving this + // constant pool block scope to include the branch instruction ending the + // deferred code. + __ BlockConstPoolFor(1); + } } -void DeferredReferenceGetNamedValue::Generate() { - __ IncrementCounter(&Counters::named_load_inline_miss, 1, r1, r2); - // Setup the name register and call load IC. - __ mov(r2, Operand(name_)); - Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); - __ Call(ic, RelocInfo::CODE_TARGET); - // The call must be followed by a nop(1) instruction to indicate that the - // inobject has been inlined. - __ nop(NAMED_PROPERTY_LOAD_INLINED); +class DeferredReferenceGetKeyedValue: public DeferredCode { + public: + DeferredReferenceGetKeyedValue() { + set_comment("[ DeferredReferenceGetKeyedValue"); + } + + virtual void Generate(); +}; + + +void DeferredReferenceGetKeyedValue::Generate() { + Register scratch1 = VirtualFrame::scratch0(); + Register scratch2 = VirtualFrame::scratch1(); + __ DecrementCounter(&Counters::keyed_load_inline, 1, scratch1, scratch2); + __ IncrementCounter(&Counters::keyed_load_inline_miss, 1, scratch1, scratch2); + + // The rest of the instructions in the deferred code must be together. + { Assembler::BlockConstPoolScope block_const_pool(masm_); + // Call keyed load IC. It has all arguments on the stack. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + // The call must be followed by a nop instruction to indicate that the + // keyed load has been inlined. + __ nop(PROPERTY_ACCESS_INLINED); + + // Block the constant pool for one more instruction after leaving this + // constant pool block scope to include the branch instruction ending the + // deferred code. + __ BlockConstPoolFor(1); + } } -void DeferredReferenceGetNamedValue::AfterGenerate() { - __ EndBlockConstPool(); +class DeferredReferenceSetKeyedValue: public DeferredCode { + public: + DeferredReferenceSetKeyedValue() { + set_comment("[ DeferredReferenceSetKeyedValue"); + } + + virtual void Generate(); +}; + + +void DeferredReferenceSetKeyedValue::Generate() { + Register scratch1 = VirtualFrame::scratch0(); + Register scratch2 = VirtualFrame::scratch1(); + __ DecrementCounter(&Counters::keyed_store_inline, 1, scratch1, scratch2); + __ IncrementCounter( + &Counters::keyed_store_inline_miss, 1, scratch1, scratch2); + + // The rest of the instructions in the deferred code must be together. + { Assembler::BlockConstPoolScope block_const_pool(masm_); + // Call keyed load IC. It has receiver amd key on the stack and the value to + // store in r0. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + // The call must be followed by a nop instruction to indicate that the + // keyed store has been inlined. + __ nop(PROPERTY_ACCESS_INLINED); + + // Block the constant pool for one more instruction after leaving this + // constant pool block scope to include the branch instruction ending the + // deferred code. + __ BlockConstPoolFor(1); + } } @@ -5266,63 +5343,231 @@ void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) { if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) { Comment cmnt(masm(), "[ Load from named Property"); // Setup the name register and call load IC. + frame_->SpillAllButCopyTOSToR0(); __ mov(r2, Operand(name)); frame_->CallLoadIC(is_contextual ? RelocInfo::CODE_TARGET_CONTEXT : RelocInfo::CODE_TARGET); } else { - // Inline the inobject property case. + // Inline the in-object property case. Comment cmnt(masm(), "[ Inlined named property load"); - DeferredReferenceGetNamedValue* deferred = - new DeferredReferenceGetNamedValue(name); + // Counter will be decremented in the deferred code. Placed here to avoid + // having it in the instruction stream below where patching will occur. + __ IncrementCounter(&Counters::named_load_inline, 1, + frame_->scratch0(), frame_->scratch1()); // The following instructions are the inlined load of an in-object property. // Parts of this code is patched, so the exact instructions generated needs // to be fixed. Therefore the instruction pool is blocked when generating // this code + + // Load the receiver from the stack. + frame_->SpillAllButCopyTOSToR0(); + + DeferredReferenceGetNamedValue* deferred = + new DeferredReferenceGetNamedValue(name); + #ifdef DEBUG - int kInlinedNamedLoadInstructions = 8; + int kInlinedNamedLoadInstructions = 7; Label check_inlined_codesize; masm_->bind(&check_inlined_codesize); #endif - { Assembler::BlockConstPoolScope block_const_pool(masm_); - // Load the receiver from the stack. - __ ldr(r1, MemOperand(sp, 0)); + { Assembler::BlockConstPoolScope block_const_pool(masm_); // Check that the receiver is a heap object. - __ tst(r1, Operand(kSmiTagMask)); + __ tst(r0, Operand(kSmiTagMask)); deferred->Branch(eq); // Check the map. The null map used below is patched by the inline cache // code. - __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); __ mov(r3, Operand(Factory::null_value())); __ cmp(r2, r3); deferred->Branch(ne); - // Use initially use an invalid index. The index will be patched by the + // Initially use an invalid index. The index will be patched by the // inline cache code. - __ ldr(r0, MemOperand(r1, 0)); + __ ldr(r0, MemOperand(r0, 0)); + + // Make sure that the expected number of instructions are generated. + ASSERT_EQ(kInlinedNamedLoadInstructions, + masm_->InstructionsGeneratedSince(&check_inlined_codesize)); } - // Make sure that the expected number of instructions are generated. - ASSERT_EQ(kInlinedNamedLoadInstructions, - masm_->InstructionsGeneratedSince(&check_inlined_codesize)); + deferred->BindExit(); + } +} + + +void CodeGenerator::EmitKeyedLoad() { + if (loop_nesting() == 0) { + VirtualFrame::SpilledScope spilled(frame_); + Comment cmnt(masm_, "[ Load from keyed property"); + frame_->CallKeyedLoadIC(); + } else { + // Inline the keyed load. + Comment cmnt(masm_, "[ Inlined load from keyed property"); + + // Counter will be decremented in the deferred code. Placed here to avoid + // having it in the instruction stream below where patching will occur. + __ IncrementCounter(&Counters::keyed_load_inline, 1, + frame_->scratch0(), frame_->scratch1()); + + // Load the receiver and key from the stack. + frame_->SpillAllButCopyTOSToR1R0(); + Register receiver = r0; + Register key = r1; + VirtualFrame::SpilledScope spilled(frame_); + + DeferredReferenceGetKeyedValue* deferred = + new DeferredReferenceGetKeyedValue(); + + // Check that the receiver is a heap object. + __ tst(receiver, Operand(kSmiTagMask)); + deferred->Branch(eq); + + // The following instructions are the part of the inlined load keyed + // property code which can be patched. Therefore the exact number of + // instructions generated need to be fixed, so the constant pool is blocked + // while generating this code. +#ifdef DEBUG + int kInlinedKeyedLoadInstructions = 19; + Label check_inlined_codesize; + masm_->bind(&check_inlined_codesize); +#endif + { Assembler::BlockConstPoolScope block_const_pool(masm_); + Register scratch1 = VirtualFrame::scratch0(); + Register scratch2 = VirtualFrame::scratch1(); + // Check the map. The null map used below is patched by the inline cache + // code. + __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); + __ mov(scratch2, Operand(Factory::null_value())); + __ cmp(scratch1, scratch2); + deferred->Branch(ne); + + // Check that the key is a smi. + __ tst(key, Operand(kSmiTagMask)); + deferred->Branch(ne); + + // Get the elements array from the receiver and check that it + // is not a dictionary. + __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset)); + __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset)); + __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); + __ cmp(scratch2, ip); + deferred->Branch(ne); + + // Check that key is within bounds. Use unsigned comparison to handle + // negative keys. + __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset)); + __ cmp(scratch2, Operand(key, ASR, kSmiTagSize)); + deferred->Branch(ls); // Unsigned less equal. + + // Load and check that the result is not the hole (key is a smi). + __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex); + __ add(scratch1, + scratch1, + Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ ldr(r0, + MemOperand(scratch1, key, LSL, + kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize))); + __ cmp(r0, scratch2); + // This is the only branch to deferred where r0 and r1 do not contain the + // receiver and key. We can't just load undefined here because we have to + // check the prototype. + deferred->Branch(eq); + + // Make sure that the expected number of instructions are generated. + ASSERT_EQ(kInlinedKeyedLoadInstructions, + masm_->InstructionsGeneratedSince(&check_inlined_codesize)); + } - __ IncrementCounter(&Counters::named_load_inline, 1, r1, r2); deferred->BindExit(); } } -void CodeGenerator::EmitKeyedLoad(bool is_global) { - Comment cmnt(masm_, "[ Load from keyed Property"); - Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); - RelocInfo::Mode rmode = is_global - ? RelocInfo::CODE_TARGET_CONTEXT - : RelocInfo::CODE_TARGET; - frame_->CallCodeObject(ic, rmode, 0); +void CodeGenerator::EmitKeyedStore(StaticType* key_type) { + frame_->AssertIsSpilled(); + // Generate inlined version of the keyed store if the code is in a loop + // and the key is likely to be a smi. + if (loop_nesting() > 0 && key_type->IsLikelySmi()) { + // Inline the keyed store. + Comment cmnt(masm_, "[ Inlined store to keyed property"); + + DeferredReferenceSetKeyedValue* deferred = + new DeferredReferenceSetKeyedValue(); + + // Counter will be decremented in the deferred code. Placed here to avoid + // having it in the instruction stream below where patching will occur. + __ IncrementCounter(&Counters::keyed_store_inline, 1, + frame_->scratch0(), frame_->scratch1()); + + // Check that the value is a smi. As this inlined code does not set the + // write barrier it is only possible to store smi values. + __ tst(r0, Operand(kSmiTagMask)); + deferred->Branch(ne); + + // Load the key and receiver from the stack. + __ ldr(r1, MemOperand(sp, 0)); + __ ldr(r2, MemOperand(sp, kPointerSize)); + + // Check that the key is a smi. + __ tst(r1, Operand(kSmiTagMask)); + deferred->Branch(ne); + + // Check that the receiver is a heap object. + __ tst(r2, Operand(kSmiTagMask)); + deferred->Branch(eq); + + // Check that the receiver is a JSArray. + __ CompareObjectType(r2, r3, r3, JS_ARRAY_TYPE); + deferred->Branch(ne); + + // Check that the key is within bounds. Both the key and the length of + // the JSArray are smis. Use unsigned comparison to handle negative keys. + __ ldr(r3, FieldMemOperand(r2, JSArray::kLengthOffset)); + __ cmp(r3, r1); + deferred->Branch(ls); // Unsigned less equal. + + // The following instructions are the part of the inlined store keyed + // property code which can be patched. Therefore the exact number of + // instructions generated need to be fixed, so the constant pool is blocked + // while generating this code. +#ifdef DEBUG + int kInlinedKeyedStoreInstructions = 7; + Label check_inlined_codesize; + masm_->bind(&check_inlined_codesize); +#endif + { Assembler::BlockConstPoolScope block_const_pool(masm_); + // Get the elements array from the receiver and check that it + // is not a dictionary. + __ ldr(r3, FieldMemOperand(r2, JSObject::kElementsOffset)); + __ ldr(r4, FieldMemOperand(r3, JSObject::kMapOffset)); + // Read the fixed array map from the constant pool (not from the root + // array) so that the value can be patched. When debugging, we patch this + // comparison to always fail so that we will hit the IC call in the + // deferred code which will allow the debugger to break for fast case + // stores. + __ mov(r5, Operand(Factory::fixed_array_map())); + __ cmp(r4, r5); + deferred->Branch(ne); + + // Store the value. + __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ str(r0, MemOperand(r3, r1, LSL, + kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize))); + + // Make sure that the expected number of instructions are generated. + ASSERT_EQ(kInlinedKeyedStoreInstructions, + masm_->InstructionsGeneratedSince(&check_inlined_codesize)); + } + + deferred->BindExit(); + } else { + frame()->CallKeyedStoreIC(); + } } @@ -5381,12 +5626,8 @@ void Reference::GetValue() { } case KEYED: { - // TODO(181): Implement inlined version of array indexing once - // loop nesting is properly tracked on ARM. ASSERT(property != NULL); - Variable* var = expression_->AsVariableProxy()->AsVariable(); - ASSERT(var == NULL || var->is_global()); - cgen_->EmitKeyedLoad(var != NULL); + cgen_->EmitKeyedLoad(); cgen_->frame()->EmitPush(r0); break; } @@ -5443,10 +5684,8 @@ void Reference::SetValue(InitState init_state) { ASSERT(property != NULL); cgen_->CodeForSourcePosition(property->position()); - // Call IC code. - Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); - frame->EmitPop(r0); // value - frame->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); + frame->EmitPop(r0); // Value. + cgen_->EmitKeyedStore(property->key()->type()); frame->EmitPush(r0); cgen_->UnloadReference(this); break; @@ -5497,8 +5736,7 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // Create a new closure through the slower runtime call. __ bind(&gc); - __ push(cp); - __ push(r3); + __ Push(cp, r3); __ TailCallRuntime(Runtime::kNewClosure, 2, 1); } @@ -6145,20 +6383,12 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, Register result, Register scratch1, Register scratch2, + Register scratch3, bool object_is_smi, Label* not_found) { - // Currently only lookup for smis. Check for smi if object is not known to be - // a smi. - if (!object_is_smi) { - ASSERT(kSmiTag == 0); - __ tst(object, Operand(kSmiTagMask)); - __ b(ne, not_found); - } - // Use of registers. Register result is used as a temporary. Register number_string_cache = result; - Register mask = scratch1; - Register scratch = scratch2; + Register mask = scratch3; // Load the number string cache. __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex); @@ -6171,9 +6401,55 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, __ sub(mask, mask, Operand(1)); // Make mask. // Calculate the entry in the number string cache. The hash value in the - // number string cache for smis is just the smi value. - __ and_(scratch, mask, Operand(object, ASR, 1)); + // number string cache for smis is just the smi value, and the hash for + // doubles is the xor of the upper and lower words. See + // Heap::GetNumberStringCache. + Label is_smi; + Label load_result_from_cache; + if (!object_is_smi) { + __ BranchOnSmi(object, &is_smi); + if (CpuFeatures::IsSupported(VFP3)) { + CpuFeatures::Scope scope(VFP3); + __ CheckMap(object, + scratch1, + Factory::heap_number_map(), + not_found, + true); + + ASSERT_EQ(8, kDoubleSize); + __ add(scratch1, + object, + Operand(HeapNumber::kValueOffset - kHeapObjectTag)); + __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit()); + __ eor(scratch1, scratch1, Operand(scratch2)); + __ and_(scratch1, scratch1, Operand(mask)); + + // Calculate address of entry in string cache: each entry consists + // of two pointer sized fields. + __ add(scratch1, + number_string_cache, + Operand(scratch1, LSL, kPointerSizeLog2 + 1)); + + Register probe = mask; + __ ldr(probe, + FieldMemOperand(scratch1, FixedArray::kHeaderSize)); + __ BranchOnSmi(probe, not_found); + __ sub(scratch2, object, Operand(kHeapObjectTag)); + __ vldr(d0, scratch2, HeapNumber::kValueOffset); + __ sub(probe, probe, Operand(kHeapObjectTag)); + __ vldr(d1, probe, HeapNumber::kValueOffset); + __ vcmp(d0, d1); + __ vmrs(pc); + __ b(ne, not_found); // The cache did not contain this value. + __ b(&load_result_from_cache); + } else { + __ b(not_found); + } + } + __ bind(&is_smi); + Register scratch = scratch1; + __ and_(scratch, mask, Operand(object, ASR, 1)); // Calculate address of entry in string cache: each entry consists // of two pointer sized fields. __ add(scratch, @@ -6181,15 +6457,15 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, Operand(scratch, LSL, kPointerSizeLog2 + 1)); // Check if the entry is the smi we are looking for. - Register object1 = scratch1; - __ ldr(object1, FieldMemOperand(scratch, FixedArray::kHeaderSize)); - __ cmp(object, object1); + Register probe = mask; + __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize)); + __ cmp(object, probe); __ b(ne, not_found); // Get the result from the cache. + __ bind(&load_result_from_cache); __ ldr(result, FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize)); - __ IncrementCounter(&Counters::number_to_string_native, 1, scratch1, @@ -6203,13 +6479,13 @@ void NumberToStringStub::Generate(MacroAssembler* masm) { __ ldr(r1, MemOperand(sp, 0)); // Generate code to lookup number in the number string cache. - GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, false, &runtime); + GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, false, &runtime); __ add(sp, sp, Operand(1 * kPointerSize)); __ Ret(); __ bind(&runtime); // Handle number to string in the runtime system if not found in the cache. - __ TailCallRuntime(Runtime::kNumberToString, 1, 1); + __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1); } @@ -6328,8 +6604,7 @@ void CompareStub::Generate(MacroAssembler* masm) { __ bind(&slow); - __ push(r1); - __ push(r0); + __ Push(r1, r0); // Figure out which native to call and setup the arguments. Builtins::JavaScript native; if (cc_ == eq) { @@ -6594,8 +6869,7 @@ void GenericBinaryOpStub::HandleBinaryOpSlowCases( __ bind(&slow); // Push arguments to the stack - __ push(r1); - __ push(r0); + __ Push(r1, r0); if (Token::ADD == op_) { // Test for string arguments before calling runtime. @@ -6624,7 +6898,7 @@ void GenericBinaryOpStub::HandleBinaryOpSlowCases( // First argument is a string, second is a smi. Try to lookup the number // string for the smi in the number string cache. NumberToStringStub::GenerateLookupNumberStringCache( - masm, r0, r2, r4, r5, true, &string1); + masm, r0, r2, r4, r5, r6, true, &string1); // Replace second argument on stack and tailcall string add stub to make // the result. @@ -6849,8 +7123,7 @@ void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm, // If all else failed then we go to the runtime system. __ bind(&slow); - __ push(lhs); // restore stack - __ push(rhs); + __ Push(lhs, rhs); // Restore stack. switch (op_) { case Token::BIT_OR: __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); @@ -7248,8 +7521,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { Label get_result; - __ push(r1); - __ push(r0); + __ Push(r1, r0); // Internal frame is necessary to handle exceptions properly. __ EnterInternalFrame(); @@ -7723,7 +7995,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ mov(r6, Operand(Smi::FromInt(marker))); __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address))); __ ldr(r5, MemOperand(r5)); - __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit()); + __ Push(r8, r7, r6, r5); // Setup frame pointer for the frame to be pushed. __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index 525b22b908..80df654489 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -157,7 +157,7 @@ enum ArgumentsAllocationMode { // states of the generated code. enum NopMarkerTypes { NON_MARKING_NOP = 0, - NAMED_PROPERTY_LOAD_INLINED + PROPERTY_ACCESS_INLINED }; @@ -318,12 +318,14 @@ class CodeGenerator: public AstVisitor { // Load a keyed property, leaving it in r0. The receiver and key are // passed on the stack, and remain there. - void EmitKeyedLoad(bool is_global); + void EmitKeyedLoad(); + + // Store a keyed property. Key and receiver are on the stack and the value is + // in r0. Result is returned in r0. + void EmitKeyedStore(StaticType* key_type); void LoadFromGlobalSlotCheckExtensions(Slot* slot, TypeofState typeof_state, - Register tmp, - Register tmp2, JumpTarget* slow); // Special code for typeof expressions: Unfortunately, we must @@ -839,6 +841,7 @@ class NumberToStringStub: public CodeStub { Register result, Register scratch1, Register scratch2, + Register scratch3, bool object_is_smi, Label* not_found); diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc index 1aca7cee44..d8149f0928 100644 --- a/deps/v8/src/arm/debug-arm.cc +++ b/deps/v8/src/arm/debug-arm.cc @@ -133,9 +133,9 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { // Calling convention for IC load (from ic-arm.cc). // ----------- S t a t e ------------- - // -- r0 : receiver // -- r2 : name // -- lr : return address + // -- r0 : receiver // -- [sp] : receiver // ----------------------------------- // Registers r0 and r2 contain objects that need to be pushed on the diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 10ccf9cf15..bed93640ff 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -125,7 +125,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) { __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset + offset)); __ mov(r1, Operand(Smi::FromInt(scope()->num_parameters()))); - __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit()); + __ Push(r3, r2, r1); // Arguments to ArgumentsAccessStub: // function, receiver address, parameter count. @@ -696,8 +696,8 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in r2 and the global // object on the stack. - __ ldr(ip, CodeGenerator::GlobalObject()); - __ push(ip); + __ ldr(r0, CodeGenerator::GlobalObject()); + __ push(r0); __ mov(r2, Operand(var->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); @@ -739,7 +739,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, __ mov(r1, Operand(key_literal->handle())); // Push both as arguments to ic. - __ stm(db_w, sp, r2.bit() | r1.bit()); + __ Push(r2, r1); // Do a keyed property load. Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); @@ -771,7 +771,7 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { __ mov(r3, Operand(Smi::FromInt(expr->literal_index()))); __ mov(r2, Operand(expr->pattern())); __ mov(r1, Operand(expr->flags())); - __ stm(db_w, sp, r4.bit() | r3.bit() | r2.bit() | r1.bit()); + __ Push(r4, r3, r2, r1); __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); __ bind(&done); Apply(context_, r0); @@ -785,7 +785,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); __ mov(r1, Operand(expr->constant_properties())); __ mov(r0, Operand(Smi::FromInt(expr->fast_elements() ? 1 : 0))); - __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit() | r0.bit()); + __ Push(r3, r2, r1, r0); if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); } else { @@ -860,7 +860,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); __ mov(r1, Operand(expr->constant_elements())); - __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit()); + __ Push(r3, r2, r1); if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateArrayLiteral, 3); } else { @@ -997,6 +997,7 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); Literal* key = prop->key()->AsLiteral(); __ mov(r2, Operand(key->handle())); + __ ldr(r0, MemOperand(sp, 0)); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index 2b7dac611e..eec1f21383 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -61,6 +61,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, // dictionary. // // r2 - holds the name of the property and is unchanged. + // r4 - used as temporary. Label done; @@ -108,25 +109,25 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, static const int kProbes = 4; for (int i = 0; i < kProbes; i++) { // Compute the masked index: (hash + i + i * i) & mask. - __ ldr(t1, FieldMemOperand(r2, String::kHashFieldOffset)); + __ ldr(r4, FieldMemOperand(r2, String::kHashFieldOffset)); if (i > 0) { // Add the probe offset (i + i * i) left shifted to avoid right shifting // the hash in a separate instruction. The value hash + i + i * i is right // shifted in the following and instruction. ASSERT(StringDictionary::GetProbeOffset(i) < 1 << (32 - String::kHashFieldOffset)); - __ add(t1, t1, Operand( + __ add(r4, r4, Operand( StringDictionary::GetProbeOffset(i) << String::kHashShift)); } - __ and_(t1, r3, Operand(t1, LSR, String::kHashShift)); + __ and_(r4, r3, Operand(r4, LSR, String::kHashShift)); // Scale the index by multiplying by the element size. ASSERT(StringDictionary::kEntrySize == 3); - __ add(t1, t1, Operand(t1, LSL, 1)); // t1 = t1 * 3 + __ add(r4, r4, Operand(r4, LSL, 1)); // r4 = r4 * 3 // Check if the key is identical to the name. - __ add(t1, t0, Operand(t1, LSL, 2)); - __ ldr(ip, FieldMemOperand(t1, kElementsStartOffset)); + __ add(r4, t0, Operand(r4, LSL, 2)); + __ ldr(ip, FieldMemOperand(r4, kElementsStartOffset)); __ cmp(r2, Operand(ip)); if (i != kProbes - 1) { __ b(eq, &done); @@ -136,13 +137,13 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, } // Check that the value is a normal property. - __ bind(&done); // t1 == t0 + 4*index - __ ldr(r3, FieldMemOperand(t1, kElementsStartOffset + 2 * kPointerSize)); + __ bind(&done); // r4 == t0 + 4*index + __ ldr(r3, FieldMemOperand(r4, kElementsStartOffset + 2 * kPointerSize)); __ tst(r3, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize)); __ b(ne, miss); // Get the value at the masked, scaled index and return. - __ ldr(t1, FieldMemOperand(t1, kElementsStartOffset + 1 * kPointerSize)); + __ ldr(t1, FieldMemOperand(r4, kElementsStartOffset + 1 * kPointerSize)); } @@ -239,12 +240,11 @@ void LoadIC::GenerateArrayLength(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address - // -- [sp] : receiver + // -- r0 : receiver + // -- sp[0] : receiver // ----------------------------------- Label miss; - __ ldr(r0, MemOperand(sp, 0)); - StubCompiler::GenerateLoadArrayLength(masm, r0, r3, &miss); __ bind(&miss); StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); @@ -255,12 +255,11 @@ void LoadIC::GenerateStringLength(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address - // -- [sp] : receiver + // -- r0 : receiver + // -- sp[0] : receiver // ----------------------------------- Label miss; - __ ldr(r0, MemOperand(sp, 0)); - StubCompiler::GenerateLoadStringLength(masm, r0, r1, r3, &miss); // Cache miss: Jump to runtime. __ bind(&miss); @@ -272,13 +271,11 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address - // -- [sp] : receiver + // -- r0 : receiver + // -- sp[0] : receiver // ----------------------------------- Label miss; - // Load receiver. - __ ldr(r0, MemOperand(sp, 0)); - StubCompiler::GenerateLoadFunctionPrototype(masm, r0, r1, r3, &miss); __ bind(&miss); StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); @@ -351,7 +348,8 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { static void GenerateNormalHelper(MacroAssembler* masm, int argc, bool is_global_object, - Label* miss) { + Label* miss, + Register scratch) { // Search dictionary - put result in register r1. GenerateDictionaryLoad(masm, miss, r0, r1); @@ -360,7 +358,7 @@ static void GenerateNormalHelper(MacroAssembler* masm, __ b(eq, miss); // Check that the value is a JSFunction. - __ CompareObjectType(r1, r0, r0, JS_FUNCTION_TYPE); + __ CompareObjectType(r1, scratch, scratch, JS_FUNCTION_TYPE); __ b(ne, miss); // Patch the receiver with the global proxy if necessary. @@ -409,7 +407,7 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset)); __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded)); __ b(ne, &miss); - GenerateNormalHelper(masm, argc, true, &miss); + GenerateNormalHelper(masm, argc, true, &miss, r4); // Accessing non-global object: Check for access to global proxy. Label global_proxy, invoke; @@ -422,7 +420,7 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded)); __ b(ne, &miss); __ bind(&invoke); - GenerateNormalHelper(masm, argc, false, &miss); + GenerateNormalHelper(masm, argc, false, &miss, r4); // Global object access: Check access rights. __ bind(&global_proxy); @@ -447,7 +445,7 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) { __ EnterInternalFrame(); // Push the receiver and the name of the function. - __ stm(db_w, sp, r2.bit() | r3.bit()); + __ Push(r3, r2); // Call the entry. __ mov(r0, Operand(2)); @@ -489,10 +487,10 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address - // -- [sp] : receiver + // -- r0 : receiver + // -- sp[0] : receiver // ----------------------------------- - __ ldr(r0, MemOperand(sp, 0)); // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, NOT_IN_LOOP, @@ -508,11 +506,11 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address - // -- [sp] : receiver + // -- r0 : receiver + // -- sp[0] : receiver // ----------------------------------- Label miss, probe, global; - __ ldr(r0, MemOperand(sp, 0)); // Check that the receiver isn't a smi. __ tst(r0, Operand(kSmiTagMask)); __ b(eq, &miss); @@ -551,11 +549,12 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address - // -- [sp] : receiver + // -- r0 : receiver + // -- sp[0] : receiver // ----------------------------------- - __ ldr(r3, MemOperand(sp, 0)); - __ stm(db_w, sp, r2.bit() | r3.bit()); + __ mov(r3, r0); + __ Push(r3, r2); // Perform tail call to the entry. ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss)); @@ -563,15 +562,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) { } -void LoadIC::ClearInlinedVersion(Address address) { - // Reset the map check of the inlined inobject property load (if present) to - // guarantee failure by holding an invalid map (the null value). The offset - // can be patched to anything. - PatchInlinedLoad(address, Heap::null_value(), 0); -} - - -bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { +static inline bool IsInlinedICSite(Address address, + Address* inline_end_address) { // If the instruction after the call site is not the pseudo instruction nop1 // then this is not related to an inlined in-object property load. The nop1 // instruction is located just after the call to the IC in the deferred code @@ -579,24 +571,42 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { // a branch instruction for jumping back from the deferred code. Address address_after_call = address + Assembler::kCallTargetAddressOffset; Instr instr_after_call = Assembler::instr_at(address_after_call); - if (!Assembler::IsNop(instr_after_call, NAMED_PROPERTY_LOAD_INLINED)) { + if (!Assembler::IsNop(instr_after_call, PROPERTY_ACCESS_INLINED)) { return false; } - ASSERT_EQ(0, RegisterAllocator::kNumRegisters); - Address address_after_nop1 = address_after_call + Assembler::kInstrSize; - Instr instr_after_nop1 = Assembler::instr_at(address_after_nop1); - ASSERT(Assembler::IsBranch(instr_after_nop1)); + Address address_after_nop = address_after_call + Assembler::kInstrSize; + Instr instr_after_nop = Assembler::instr_at(address_after_nop); + ASSERT(Assembler::IsBranch(instr_after_nop)); // Find the end of the inlined code for handling the load. int b_offset = - Assembler::GetBranchOffset(instr_after_nop1) + Assembler::kPcLoadDelta; + Assembler::GetBranchOffset(instr_after_nop) + Assembler::kPcLoadDelta; ASSERT(b_offset < 0); // Jumping back from deferred code. - Address inline_end_address = address_after_nop1 + b_offset; + *inline_end_address = address_after_nop + b_offset; + + return true; +} + + +void LoadIC::ClearInlinedVersion(Address address) { + // Reset the map check of the inlined in-object property load (if present) to + // guarantee failure by holding an invalid map (the null value). The offset + // can be patched to anything. + PatchInlinedLoad(address, Heap::null_value(), 0); +} + + +bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { + // Find the end of the inlined code for handling the load if this is an + // inlined IC call site. + Address inline_end_address; + if (!IsInlinedICSite(address, &inline_end_address)) return false; // Patch the offset of the property load instruction (ldr r0, [r1, #+XXX]). - // The immediate must be represenatble in 12 bits. + // The immediate must be representable in 12 bits. ASSERT((JSObject::kMaxInstanceSize - JSObject::kHeaderSize) < (1 << 12)); - Address ldr_property_instr_address = inline_end_address - 4; + Address ldr_property_instr_address = + inline_end_address - Assembler::kInstrSize; ASSERT(Assembler::IsLdrRegisterImmediate( Assembler::instr_at(ldr_property_instr_address))); Instr ldr_property_instr = Assembler::instr_at(ldr_property_instr_address); @@ -608,29 +618,61 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { CPU::FlushICache(ldr_property_instr_address, 1 * Assembler::kInstrSize); // Patch the map check. - Address ldr_map_instr_address = inline_end_address - 16; + Address ldr_map_instr_address = + inline_end_address - 4 * Assembler::kInstrSize; Assembler::set_target_address_at(ldr_map_instr_address, reinterpret_cast<Address>(map)); return true; } -void KeyedLoadIC::ClearInlinedVersion(Address address) {} +void KeyedLoadIC::ClearInlinedVersion(Address address) { + // Reset the map check of the inlined keyed load (if present) to + // guarantee failure by holding an invalid map (the null value). + PatchInlinedLoad(address, Heap::null_value()); +} bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { - return false; + Address inline_end_address; + if (!IsInlinedICSite(address, &inline_end_address)) return false; + + // Patch the map check. + Address ldr_map_instr_address = + inline_end_address - 18 * Assembler::kInstrSize; + Assembler::set_target_address_at(ldr_map_instr_address, + reinterpret_cast<Address>(map)); + return true; } -void KeyedStoreIC::ClearInlinedVersion(Address address) {} +void KeyedStoreIC::ClearInlinedVersion(Address address) { + // Insert null as the elements map to check for. This will make + // sure that the elements fast-case map check fails so that control + // flows to the IC instead of the inlined version. + PatchInlinedStore(address, Heap::null_value()); +} -void KeyedStoreIC::RestoreInlinedVersion(Address address) {} +void KeyedStoreIC::RestoreInlinedVersion(Address address) { + // Restore the fast-case elements map check so that the inlined + // version can be used again. + PatchInlinedStore(address, Heap::fixed_array_map()); +} bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) { - return false; + // Find the end of the inlined code for handling the store if this is an + // inlined IC call site. + Address inline_end_address; + if (!IsInlinedICSite(address, &inline_end_address)) return false; + + // Patch the map check. + Address ldr_map_instr_address = + inline_end_address - 5 * Assembler::kInstrSize; + Assembler::set_target_address_at(ldr_map_instr_address, + reinterpret_cast<Address>(map)); + return true; } @@ -645,7 +687,7 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { // ----------------------------------- __ ldm(ia, sp, r2.bit() | r3.bit()); - __ stm(db_w, sp, r2.bit() | r3.bit()); + __ Push(r3, r2); ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss)); __ TailCallExternalReference(ref, 2, 1); @@ -660,7 +702,7 @@ void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { // ----------------------------------- __ ldm(ia, sp, r2.bit() | r3.bit()); - __ stm(db_w, sp, r2.bit() | r3.bit()); + __ Push(r3, r2); __ TailCallRuntime(Runtime::kGetProperty, 2, 1); } @@ -778,7 +820,7 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) { __ bind(&index_ok); // Duplicate receiver and key since they are expected on the stack after // the KeyedLoadIC call. - __ stm(db_w, sp, r0.bit() | r1.bit()); + __ Push(r1, r0); __ InvokeBuiltin(Builtins::STRING_CHAR_AT, JUMP_JS); __ bind(&miss); @@ -1094,8 +1136,7 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { __ b(ne, &slow); // Everything is fine, call runtime. - __ push(r1); // receiver - __ push(r0); // key + __ Push(r1, r0); // Receiver, key. // Perform tail call to the entry. __ TailCallExternalReference(ExternalReference( @@ -1115,7 +1156,7 @@ void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { // ----------------------------------- __ ldm(ia, sp, r2.bit() | r3.bit()); - __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit()); + __ Push(r3, r2, r0); ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss)); __ TailCallExternalReference(ref, 3, 1); @@ -1130,7 +1171,7 @@ void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { // -- sp[1] : receiver // ----------------------------------- __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object - __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); + __ Push(r3, r1, r0); __ TailCallRuntime(Runtime::kSetProperty, 3, 1); } @@ -1684,8 +1725,7 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) { // -- lr : return address // ----------------------------------- - __ push(r1); - __ stm(db_w, sp, r2.bit() | r0.bit()); + __ Push(r1, r2, r0); // Perform tail call to the entry. ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss)); @@ -1729,8 +1769,7 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { __ BranchOnNotSmi(value, &miss); // Prepare tail call to StoreIC_ArrayLength. - __ push(receiver); - __ push(value); + __ Push(receiver, value); ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_ArrayLength)); __ TailCallExternalReference(ref, 2, 1); diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 17d6a10053..ccabe2c67e 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1101,11 +1101,11 @@ void MacroAssembler::AllocateAsciiConsString(Register result, } -void MacroAssembler::CompareObjectType(Register function, +void MacroAssembler::CompareObjectType(Register object, Register map, Register type_reg, InstanceType type) { - ldr(map, FieldMemOperand(function, HeapObject::kMapOffset)); + ldr(map, FieldMemOperand(object, HeapObject::kMapOffset)); CompareInstanceType(map, type_reg, type); } diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index a2cd2701b9..062c5c6782 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -93,6 +93,65 @@ class MacroAssembler: public Assembler { // well as the ip register. void RecordWrite(Register object, Register offset, Register scratch); + // Push two registers. Pushes leftmost register first (to highest address). + void Push(Register src1, Register src2, Condition cond = al) { + ASSERT(!src1.is(src2)); + if (src1.code() > src2.code()) { + stm(db_w, sp, src1.bit() | src2.bit(), cond); + } else { + str(src1, MemOperand(sp, 4, NegPreIndex), cond); + str(src2, MemOperand(sp, 4, NegPreIndex), cond); + } + } + + // Push three registers. Pushes leftmost register first (to highest address). + void Push(Register src1, Register src2, Register src3, Condition cond = al) { + ASSERT(!src1.is(src2)); + ASSERT(!src2.is(src3)); + ASSERT(!src1.is(src3)); + if (src1.code() > src2.code()) { + if (src2.code() > src3.code()) { + stm(db_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); + } else { + stm(db_w, sp, src1.bit() | src2.bit(), cond); + str(src3, MemOperand(sp, 4, NegPreIndex), cond); + } + } else { + str(src1, MemOperand(sp, 4, NegPreIndex), cond); + Push(src2, src3, cond); + } + } + + // Push four registers. Pushes leftmost register first (to highest address). + void Push(Register src1, Register src2, + Register src3, Register src4, Condition cond = al) { + ASSERT(!src1.is(src2)); + ASSERT(!src2.is(src3)); + ASSERT(!src1.is(src3)); + ASSERT(!src1.is(src4)); + ASSERT(!src2.is(src4)); + ASSERT(!src3.is(src4)); + if (src1.code() > src2.code()) { + if (src2.code() > src3.code()) { + if (src3.code() > src4.code()) { + stm(db_w, + sp, + src1.bit() | src2.bit() | src3.bit() | src4.bit(), + cond); + } else { + stm(db_w, sp, src1.bit() | src2.bit() | src3.bit(), cond); + str(src4, MemOperand(sp, 4, NegPreIndex), cond); + } + } else { + stm(db_w, sp, src1.bit() | src2.bit(), cond); + Push(src3, src4, cond); + } + } else { + str(src1, MemOperand(sp, 4, NegPreIndex), cond); + Push(src2, src3, src4, cond); + } + } + // --------------------------------------------------------------------------- // Stack limit support diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc index b44a73804b..5fe7d5f76e 100644 --- a/deps/v8/src/arm/simulator-arm.cc +++ b/deps/v8/src/arm/simulator-arm.cc @@ -541,7 +541,6 @@ void Simulator::FlushOnePage(intptr_t start, int size) { void Simulator::CheckICache(Instr* instr) { -#ifdef DEBUG intptr_t address = reinterpret_cast<intptr_t>(instr); void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask)); void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask)); @@ -560,7 +559,6 @@ void Simulator::CheckICache(Instr* instr) { memcpy(cached_line, line, CachePage::kLineLength); *cache_valid_byte = CachePage::LINE_VALID; } -#endif } @@ -2441,7 +2439,9 @@ void Simulator::DecodeType6CoprocessorIns(Instr* instr) { // Executes the current instruction. void Simulator::InstructionDecode(Instr* instr) { - CheckICache(instr); + if (v8::internal::FLAG_check_icache) { + CheckICache(instr); + } pc_modified_ = false; if (::v8::internal::FLAG_trace_sim) { disasm::NameConverter converter; diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index 1cb88b4966..a770d160f0 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -296,7 +296,7 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // We jump to a runtime call that extends the properties array. __ push(receiver_reg); __ mov(r2, Operand(Handle<Map>(transition))); - __ stm(db_w, sp, r2.bit() | r0.bit()); + __ Push(r2, r0); __ TailCallExternalReference( ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1); @@ -464,8 +464,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { __ EnterInternalFrame(); __ push(receiver); - __ push(holder); - __ push(name_); + __ Push(holder, name_); CompileCallLoadPropertyWithInterceptor(masm, receiver, @@ -510,8 +509,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { Label cleanup; __ pop(scratch2); - __ push(receiver); - __ push(scratch2); + __ Push(receiver, scratch2); holder = stub_compiler->CheckPrototypes(holder_obj, holder, lookup->holder(), scratch1, @@ -523,8 +521,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { __ Move(holder, Handle<AccessorInfo>(callback)); __ push(holder); __ ldr(scratch1, FieldMemOperand(holder, AccessorInfo::kDataOffset)); - __ push(scratch1); - __ push(name_); + __ Push(scratch1, name_); ExternalReference ref = ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); @@ -725,13 +722,11 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); // Push the arguments on the JS stack of the caller. - __ push(receiver); // receiver - __ push(reg); // holder + __ push(receiver); // Receiver. + __ push(reg); // Holder. __ mov(ip, Operand(Handle<AccessorInfo>(callback))); // callback data - __ push(ip); __ ldr(reg, FieldMemOperand(ip, AccessorInfo::kDataOffset)); - __ push(reg); - __ push(name_reg); // name + __ Push(ip, reg, name_reg); // Do tail-call to the runtime system. ExternalReference load_callback_property = @@ -1105,8 +1100,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Call the interceptor. __ EnterInternalFrame(); - __ push(holder_reg); - __ push(name_reg); + __ Push(holder_reg, name_reg); CompileCallLoadPropertyWithInterceptor(masm(), receiver, holder_reg, @@ -1233,7 +1227,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); // Jump to the cached code (tail call). - __ IncrementCounter(&Counters::call_global_inline, 1, r1, r3); + __ IncrementCounter(&Counters::call_global_inline, 1, r3, r4); ASSERT(function->is_compiled()); Handle<Code> code(function->code()); ParameterCount expected(function->shared()->formal_parameter_count()); @@ -1309,7 +1303,7 @@ Object* StoreStubCompiler::CompileStoreCallback(JSObject* object, __ push(r1); // receiver __ mov(ip, Operand(Handle<AccessorInfo>(callback))); // callback info - __ stm(db_w, sp, ip.bit() | r2.bit() | r0.bit()); + __ Push(ip, r2, r0); // Do tail-call to the runtime system. ExternalReference store_callback_property = @@ -1354,9 +1348,7 @@ Object* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, // checks. ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded()); - __ push(r1); // receiver. - __ push(r2); // name. - __ push(r0); // value. + __ Push(r1, r2, r0); // Receiver, name, value. // Do tail-call to the runtime system. ExternalReference store_ic_property = @@ -1559,35 +1551,34 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object, // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address - // -- [sp] : receiver + // -- r0 : receiver + // -- sp[0] : receiver // ----------------------------------- Label miss; - // Get the receiver from the stack. - __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); - // If the object is the holder then we know that it's a global // object which can only happen for contextual calls. In this case, // the receiver cannot be a smi. if (object != holder) { - __ tst(r1, Operand(kSmiTagMask)); + __ tst(r0, Operand(kSmiTagMask)); __ b(eq, &miss); } // Check that the map of the global has not changed. - CheckPrototypes(object, r1, holder, r3, r0, name, &miss); + CheckPrototypes(object, r0, holder, r3, r4, name, &miss); // Get the value from the cell. __ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell))); - __ ldr(r0, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset)); + __ ldr(r4, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset)); // Check for deleted property if property can actually be deleted. if (!is_dont_delete) { __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r0, ip); + __ cmp(r4, ip); __ b(eq, &miss); } + __ mov(r0, r4); __ IncrementCounter(&Counters::named_load_global_inline, 1, r1, r3); __ Ret(); diff --git a/deps/v8/src/arm/virtual-frame-arm.cc b/deps/v8/src/arm/virtual-frame-arm.cc index 3183c098b2..ed26c41d17 100644 --- a/deps/v8/src/arm/virtual-frame-arm.cc +++ b/deps/v8/src/arm/virtual-frame-arm.cc @@ -88,7 +88,7 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { break; case CASE_NUMBER(NO_TOS_REGISTERS, R1_R0_TOS): __ pop(r1); - __ pop(r1); + __ pop(r0); break; case CASE_NUMBER(R0_TOS, NO_TOS_REGISTERS): __ push(r0); @@ -121,8 +121,7 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { __ pop(r0); break; case CASE_NUMBER(R0_R1_TOS, NO_TOS_REGISTERS): - __ push(r1); - __ push(r0); + __ Push(r1, r0); break; case CASE_NUMBER(R0_R1_TOS, R0_TOS): __ push(r1); @@ -137,8 +136,7 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { __ Swap(r0, r1, ip); break; case CASE_NUMBER(R1_R0_TOS, NO_TOS_REGISTERS): - __ push(r0); - __ push(r1); + __ Push(r0, r1); break; case CASE_NUMBER(R1_R0_TOS, R0_TOS): __ push(r0); @@ -270,6 +268,7 @@ void VirtualFrame::CallJSFunction(int arg_count) { void VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) { + ASSERT(SpilledScope::is_spilled()); Forget(arg_count); ASSERT(cgen()->HasValidEntryRegisters()); __ CallRuntime(f, arg_count); @@ -305,6 +304,18 @@ void VirtualFrame::CallLoadIC(RelocInfo::Mode mode) { } +void VirtualFrame::CallKeyedLoadIC() { + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); + CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); +} + + +void VirtualFrame::CallKeyedStoreIC() { + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); +} + + void VirtualFrame::CallCodeObject(Handle<Code> code, RelocInfo::Mode rmode, int dropped_args) { @@ -398,6 +409,61 @@ void VirtualFrame::EmitPop(Register reg) { } +void VirtualFrame::SpillAllButCopyTOSToR0() { + switch (top_of_stack_state_) { + case NO_TOS_REGISTERS: + __ ldr(r0, MemOperand(sp, 0)); + break; + case R0_TOS: + __ push(r0); + break; + case R1_TOS: + __ push(r1); + __ mov(r0, r1); + break; + case R0_R1_TOS: + __ Push(r1, r0); + break; + case R1_R0_TOS: + __ Push(r0, r1); + __ mov(r0, r1); + break; + default: + UNREACHABLE(); + } + top_of_stack_state_ = NO_TOS_REGISTERS; +} + + +void VirtualFrame::SpillAllButCopyTOSToR1R0() { + switch (top_of_stack_state_) { + case NO_TOS_REGISTERS: + __ ldr(r1, MemOperand(sp, 0)); + __ ldr(r0, MemOperand(sp, kPointerSize)); + break; + case R0_TOS: + __ push(r0); + __ mov(r1, r0); + __ ldr(r0, MemOperand(sp, kPointerSize)); + break; + case R1_TOS: + __ push(r1); + __ ldr(r0, MemOperand(sp, kPointerSize)); + break; + case R0_R1_TOS: + __ Push(r1, r0); + __ Swap(r0, r1, ip); + break; + case R1_R0_TOS: + __ Push(r0, r1); + break; + default: + UNREACHABLE(); + } + top_of_stack_state_ = NO_TOS_REGISTERS; +} + + Register VirtualFrame::Peek() { AssertIsNotSpilled(); if (top_of_stack_state_ == NO_TOS_REGISTERS) { diff --git a/deps/v8/src/arm/virtual-frame-arm.h b/deps/v8/src/arm/virtual-frame-arm.h index c378b581dd..7b56bc244c 100644 --- a/deps/v8/src/arm/virtual-frame-arm.h +++ b/deps/v8/src/arm/virtual-frame-arm.h @@ -308,10 +308,18 @@ class VirtualFrame : public ZoneObject { InvokeJSFlags flag, int arg_count); - // Call load IC. Receiver on stack and property name in r2. Result returned in - // r0. + // Call load IC. Receiver is on the stack and the property name is in r2. + // Result is returned in r0. void CallLoadIC(RelocInfo::Mode mode); + // Call keyed load IC. Key and receiver are on the stack. Result is returned + // in r0. + void CallKeyedLoadIC(); + + // Call keyed store IC. Key and receiver are on the stack and the value is in + // r0. Result is returned in r0. + void CallKeyedStoreIC(); + // Call into an IC stub given the number of arguments it removes // from the stack. Register arguments to the IC stub are implicit, // and depend on the type of IC stub. @@ -340,6 +348,13 @@ class VirtualFrame : public ZoneObject { // must be copied to a scratch register before modification. Register Peek(); + // Flushes all registers, but it puts a copy of the top-of-stack in r0. + void SpillAllButCopyTOSToR0(); + + // Flushes all registers, but it puts a copy of the top-of-stack in r1 + // and the next value on the stack in r0. + void SpillAllButCopyTOSToR1R0(); + // Pop and save an element from the top of the expression stack and // emit a corresponding pop instruction. void EmitPop(Register reg); |