Version 2.2.6.
Add "amd64" as recognized architecture in scons build script (by Ryan Dahl <coldredlemur@gmail.com>).
Fix bug in String search and replace with very simple RegExps.
Fix bug in RegExp containing "\b^".
Performance improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@4537 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index a6d81bf..7990368 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -800,9 +800,10 @@
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 @@
}
+void Assembler::BlockConstPoolFor(int instructions) {
+ BlockConstPoolBefore(pc_offset() + instructions * kInstrSize);
+}
+
+
// Debugging.
void Assembler::RecordJSReturn() {
WriteRecordedPositions();
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index e6284d3..839ed67 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -941,6 +941,10 @@
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 @@
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 @@
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/src/arm/assembler-thumb2.h b/src/arm/assembler-thumb2.h
index 869ac46..2da1138 100644
--- a/src/arm/assembler-thumb2.h
+++ b/src/arm/assembler-thumb2.h
@@ -898,7 +898,7 @@
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/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index 3594aab..291a763 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -351,17 +351,17 @@
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::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 @@
__ 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 @@
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 @@
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 @@
}
} else {
// Anything else is a runtime error.
+ VirtualFrame::SpilledScope spilled_scope(frame_);
LoadAndSpill(e);
frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
}
@@ -1527,6 +1527,7 @@
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::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 @@
// 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 @@
}
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::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 @@
}
if (s->is_eval_scope()) {
+ frame_->SpillAll();
Label next, fast;
__ Move(tmp, context);
__ bind(&next);
@@ -3192,6 +3199,7 @@
// 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 @@
#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 @@
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 @@
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 @@
set_comment("[ DeferredReferenceGetNamedValue");
}
- virtual void BeforeGenerate();
virtual void Generate();
- virtual void AfterGenerate();
private:
Handle<String> name_;
};
-void DeferredReferenceGetNamedValue::BeforeGenerate() {
- __ StartBlockConstPool();
-}
-
-
void DeferredReferenceGetNamedValue::Generate() {
- __ IncrementCounter(&Counters::named_load_inline_miss, 1, r1, r2);
- // Setup the name register and call load IC.
+ 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_));
- 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);
+
+ // 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::AfterGenerate() {
- __ EndBlockConstPool();
+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);
+ }
+}
+
+
+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 @@
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));
-
- __ 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::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));
+ }
+
+ deferred->BindExit();
+ }
+}
+
+
+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 @@
}
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 @@
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 @@
// 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 @@
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 @@
__ 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 @@
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 @@
__ 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 @@
__ 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 @@
__ 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 @@
// 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 @@
// 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::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 @@
__ 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/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index 525b22b..80df654 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -157,7 +157,7 @@
// states of the generated code.
enum NopMarkerTypes {
NON_MARKING_NOP = 0,
- NAMED_PROPERTY_LOAD_INLINED
+ PROPERTY_ACCESS_INLINED
};
@@ -318,12 +318,14 @@
// 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 @@
Register result,
Register scratch1,
Register scratch2,
+ Register scratch3,
bool object_is_smi,
Label* not_found);
diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc
index 1aca7ce..d8149f0 100644
--- a/src/arm/debug-arm.cc
+++ b/src/arm/debug-arm.cc
@@ -133,9 +133,9 @@
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/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 10ccf9c..bed9364 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -125,7 +125,7 @@
__ 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 @@
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 @@
__ 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 @@
__ 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 @@
__ 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 @@
__ 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 @@
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/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 2b7dac6..eec1f21 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -61,6 +61,7 @@
// dictionary.
//
// r2 - holds the name of the property and is unchanged.
+ // r4 - used as temporary.
Label done;
@@ -108,25 +109,25 @@
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 @@
}
// 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 @@
// ----------- 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 @@
// ----------- 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 @@
// ----------- 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 @@
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 @@
__ 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 @@
__ 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 @@
__ 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 @@
__ 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 @@
// ----------- 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 @@
// ----------- 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 @@
// ----------- 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::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 @@
// 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 @@
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) {}
-
-
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- return false;
+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());
}
-void KeyedStoreIC::ClearInlinedVersion(Address address) {}
+bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
+ 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::RestoreInlinedVersion(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) {
+ // 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 @@
// -----------------------------------
__ 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 @@
// -----------------------------------
__ 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 @@
__ 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 @@
__ 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 @@
// -----------------------------------
__ 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 @@
// -- 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 @@
// -- 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 @@
__ 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/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 17d6a10..ccabe2c 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -1101,11 +1101,11 @@
}
-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/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index a2cd270..062c5c6 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -93,6 +93,65 @@
// 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/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc
index b44a738..5fe7d5f 100644
--- a/src/arm/simulator-arm.cc
+++ b/src/arm/simulator-arm.cc
@@ -541,7 +541,6 @@
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 @@
memcpy(cached_line, line, CachePage::kLineLength);
*cache_valid_byte = CachePage::LINE_VALID;
}
-#endif
}
@@ -2441,7 +2439,9 @@
// 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/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index 1cb88b4..a770d16 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -296,7 +296,7 @@
// 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 @@
__ EnterInternalFrame();
__ push(receiver);
- __ push(holder);
- __ push(name_);
+ __ Push(holder, name_);
CompileCallLoadPropertyWithInterceptor(masm,
receiver,
@@ -510,8 +509,7 @@
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 @@
__ 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 @@
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 @@
// Call the interceptor.
__ EnterInternalFrame();
- __ push(holder_reg);
- __ push(name_reg);
+ __ Push(holder_reg, name_reg);
CompileCallLoadPropertyWithInterceptor(masm(),
receiver,
holder_reg,
@@ -1233,7 +1227,7 @@
__ 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 @@
__ 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 @@
// 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 @@
// ----------- 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/src/arm/virtual-frame-arm.cc b/src/arm/virtual-frame-arm.cc
index 3183c09..ed26c41 100644
--- a/src/arm/virtual-frame-arm.cc
+++ b/src/arm/virtual-frame-arm.cc
@@ -88,7 +88,7 @@
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 @@
__ 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 @@
__ 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::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::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::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/src/arm/virtual-frame-arm.h b/src/arm/virtual-frame-arm.h
index c378b58..7b56bc2 100644
--- a/src/arm/virtual-frame-arm.h
+++ b/src/arm/virtual-frame-arm.h
@@ -308,10 +308,18 @@
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 @@
// 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);