Commit 6044b337 authored by ager@chromium.org's avatar ager@chromium.org

Implement IC for storing to dictionary case objects.

The IC stub is completely generic, so there will only be one such stub
in the system.

Added a new overloaded version of the macro assembler RecordWrite
method for cases where we have the address we store to computed up
front.

Review URL: http://codereview.chromium.org/2804029

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4991 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ff6c4fe6
...@@ -7347,7 +7347,8 @@ void NumberToStringStub::Generate(MacroAssembler* masm) { ...@@ -7347,7 +7347,8 @@ void NumberToStringStub::Generate(MacroAssembler* masm) {
void RecordWriteStub::Generate(MacroAssembler* masm) { void RecordWriteStub::Generate(MacroAssembler* masm) {
__ RecordWriteHelper(object_, Operand(offset_), offset_, scratch_); __ add(offset_, object_, Operand(offset_));
__ RecordWriteHelper(object_, offset_, scratch_);
__ Ret(); __ Ret();
} }
......
...@@ -64,12 +64,12 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, ...@@ -64,12 +64,12 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global // Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors. // JS object with slow properties and no interceptors.
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm, static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
Register receiver, Register receiver,
Register elements, Register elements,
Register t0, Register t0,
Register t1, Register t1,
Label* miss) { Label* miss) {
// Register usage: // Register usage:
// receiver: holds the receiver on entry and is unchanged. // receiver: holds the receiver on entry and is unchanged.
// elements: holds the property dictionary on fall through. // elements: holds the property dictionary on fall through.
...@@ -105,33 +105,16 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm, ...@@ -105,33 +105,16 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
} }
// Helper function used from LoadIC/CallIC GenerateNormal. // Probe the string dictionary in the |elements| register. Jump to the
// // |done| label if a property with the given name is found. Jump to
// elements: Property dictionary. It is not clobbered if a jump to the miss // the |miss| label otherwise.
// label is done. static void GenerateStringDictionaryProbes(MacroAssembler* masm,
// name: Property name. It is not clobbered if a jump to the miss label is Label* miss,
// done Label* done,
// result: Register for the result. It is only updated if a jump to the miss Register elements,
// label is not done. Can be the same as elements or name clobbering Register name,
// one of these in the case of not jumping to the miss label. Register scratch1,
// The two scratch registers need to be different from elements, name and Register scratch2) {
// result.
// The generated code assumes that the receiver has slow properties,
// is not a global object and does not have interceptors.
static void GenerateDictionaryLoad(MacroAssembler* masm,
Label* miss,
Register elements,
Register name,
Register result,
Register scratch1,
Register scratch2) {
// Main use of the scratch registers.
// scratch1: Used as temporary and to hold the capacity of the property
// dictionary.
// scratch2: Used as temporary.
Label done;
// Compute the capacity mask. // Compute the capacity mask.
const int kCapacityOffset = StringDictionary::kHeaderSize + const int kCapacityOffset = StringDictionary::kHeaderSize +
StringDictionary::kCapacityIndex * kPointerSize; StringDictionary::kCapacityIndex * kPointerSize;
...@@ -170,16 +153,56 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, ...@@ -170,16 +153,56 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset)); __ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
__ cmp(name, Operand(ip)); __ cmp(name, Operand(ip));
if (i != kProbes - 1) { if (i != kProbes - 1) {
__ b(eq, &done); __ b(eq, done);
} else { } else {
__ b(ne, miss); __ b(ne, miss);
} }
} }
}
// Check that the value is a normal property.
// Helper function used from LoadIC/CallIC GenerateNormal.
//
// elements: Property dictionary. It is not clobbered if a jump to the miss
// label is done.
// name: Property name. It is not clobbered if a jump to the miss label is
// done
// result: Register for the result. It is only updated if a jump to the miss
// label is not done. Can be the same as elements or name clobbering
// one of these in the case of not jumping to the miss label.
// The two scratch registers need to be different from elements, name and
// result.
// The generated code assumes that the receiver has slow properties,
// is not a global object and does not have interceptors.
static void GenerateDictionaryLoad(MacroAssembler* masm,
Label* miss,
Register elements,
Register name,
Register result,
Register scratch1,
Register scratch2) {
// Main use of the scratch registers.
// scratch1: Used as temporary and to hold the capacity of the property
// dictionary.
// scratch2: Used as temporary.
Label done;
// Probe the dictionary.
GenerateStringDictionaryProbes(masm,
miss,
&done,
elements,
name,
scratch1,
scratch2);
// If probing finds an entry check that the value is a normal
// property.
__ bind(&done); // scratch2 == elements + 4 * index __ bind(&done); // scratch2 == elements + 4 * index
__ ldr(scratch1, const int kElementsStartOffset = StringDictionary::kHeaderSize +
FieldMemOperand(scratch2, kElementsStartOffset + 2 * kPointerSize)); StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
__ tst(scratch1, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize)); __ tst(scratch1, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
__ b(ne, miss); __ b(ne, miss);
...@@ -189,6 +212,63 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, ...@@ -189,6 +212,63 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
} }
// Helper function used from StoreIC::GenerateNormal.
//
// elements: Property dictionary. It is not clobbered if a jump to the miss
// label is done.
// name: Property name. It is not clobbered if a jump to the miss label is
// done
// value: The value to store.
// The two scratch registers need to be different from elements, name and
// result.
// The generated code assumes that the receiver has slow properties,
// is not a global object and does not have interceptors.
static void GenerateDictionaryStore(MacroAssembler* masm,
Label* miss,
Register elements,
Register name,
Register value,
Register scratch1,
Register scratch2) {
// Main use of the scratch registers.
// scratch1: Used as temporary and to hold the capacity of the property
// dictionary.
// scratch2: Used as temporary.
Label done;
// Probe the dictionary.
GenerateStringDictionaryProbes(masm,
miss,
&done,
elements,
name,
scratch1,
scratch2);
// If probing finds an entry in the dictionary check that the value
// is a normal property that is not read only.
__ bind(&done); // scratch2 == elements + 4 * index
const int kElementsStartOffset = StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
const int kTypeAndReadOnlyMask
= (PropertyDetails::TypeField::mask() |
PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
__ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
__ tst(scratch1, Operand(kTypeAndReadOnlyMask));
__ b(ne, miss);
// Store the value at the masked, scaled index and return.
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ add(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
__ str(value, MemOperand(scratch2));
// Update the write barrier. Make sure not to clobber the value.
__ mov(scratch1, value);
__ RecordWrite(elements, scratch2, scratch1);
}
static void GenerateNumberDictionaryLoad(MacroAssembler* masm, static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
Label* miss, Label* miss,
Register elements, Register elements,
...@@ -560,7 +640,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { ...@@ -560,7 +640,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack into r1. // Get the receiver of the function from the stack into r1.
__ ldr(r1, MemOperand(sp, argc * kPointerSize)); __ ldr(r1, MemOperand(sp, argc * kPointerSize));
GenerateDictionaryLoadReceiverCheck(masm, r1, r0, r3, r4, &miss); GenerateStringDictionaryReceiverCheck(masm, r1, r0, r3, r4, &miss);
// r0: elements // r0: elements
// Search the dictionary - put result in register r1. // Search the dictionary - put result in register r1.
...@@ -815,7 +895,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { ...@@ -815,7 +895,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// ----------------------------------- // -----------------------------------
Label miss; Label miss;
GenerateDictionaryLoadReceiverCheck(masm, r0, r1, r3, r4, &miss); GenerateStringDictionaryReceiverCheck(masm, r0, r1, r3, r4, &miss);
// r1: elements // r1: elements
GenerateDictionaryLoad(masm, &miss, r1, r2, r0, r3, r4); GenerateDictionaryLoad(masm, &miss, r1, r2, r0, r3, r4);
...@@ -2138,6 +2218,27 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { ...@@ -2138,6 +2218,27 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
} }
void StoreIC::GenerateNormal(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : receiver
// -- r2 : name
// -- lr : return address
// -----------------------------------
Label miss;
GenerateStringDictionaryReceiverCheck(masm, r1, r3, r4, r5, &miss);
GenerateDictionaryStore(masm, &miss, r3, r2, r0, r4, r5);
__ IncrementCounter(&Counters::store_normal_hit, 1, r4, r5);
__ Ret();
__ bind(&miss);
__ IncrementCounter(&Counters::store_normal_miss, 1, r4, r5);
GenerateMiss(masm);
}
#undef __ #undef __
......
...@@ -310,32 +310,28 @@ void MacroAssembler::StoreRoot(Register source, ...@@ -310,32 +310,28 @@ void MacroAssembler::StoreRoot(Register source,
void MacroAssembler::RecordWriteHelper(Register object, void MacroAssembler::RecordWriteHelper(Register object,
Operand offset, Register address,
Register scratch0, Register scratch) {
Register scratch1) {
if (FLAG_debug_code) { if (FLAG_debug_code) {
// Check that the object is not in new space. // Check that the object is not in new space.
Label not_in_new_space; Label not_in_new_space;
InNewSpace(object, scratch1, ne, &not_in_new_space); InNewSpace(object, scratch, ne, &not_in_new_space);
Abort("new-space object passed to RecordWriteHelper"); Abort("new-space object passed to RecordWriteHelper");
bind(&not_in_new_space); bind(&not_in_new_space);
} }
// Add offset into the object.
add(scratch0, object, offset);
// Calculate page address. // Calculate page address.
Bfc(object, 0, kPageSizeBits); Bfc(object, 0, kPageSizeBits);
// Calculate region number. // Calculate region number.
Ubfx(scratch0, scratch0, Page::kRegionSizeLog2, Ubfx(address, address, Page::kRegionSizeLog2,
kPageSizeBits - Page::kRegionSizeLog2); kPageSizeBits - Page::kRegionSizeLog2);
// Mark region dirty. // Mark region dirty.
ldr(scratch1, MemOperand(object, Page::kDirtyFlagOffset)); ldr(scratch, MemOperand(object, Page::kDirtyFlagOffset));
mov(ip, Operand(1)); mov(ip, Operand(1));
orr(scratch1, scratch1, Operand(ip, LSL, scratch0)); orr(scratch, scratch, Operand(ip, LSL, address));
str(scratch1, MemOperand(object, Page::kDirtyFlagOffset)); str(scratch, MemOperand(object, Page::kDirtyFlagOffset));
} }
...@@ -368,8 +364,11 @@ void MacroAssembler::RecordWrite(Register object, ...@@ -368,8 +364,11 @@ void MacroAssembler::RecordWrite(Register object,
// region marks for new space pages. // region marks for new space pages.
InNewSpace(object, scratch0, eq, &done); InNewSpace(object, scratch0, eq, &done);
// Add offset into the object.
add(scratch0, object, offset);
// Record the actual write. // Record the actual write.
RecordWriteHelper(object, offset, scratch0, scratch1); RecordWriteHelper(object, scratch0, scratch1);
bind(&done); bind(&done);
...@@ -383,6 +382,38 @@ void MacroAssembler::RecordWrite(Register object, ...@@ -383,6 +382,38 @@ void MacroAssembler::RecordWrite(Register object,
} }
// Will clobber 4 registers: object, address, scratch, ip. The
// register 'object' contains a heap object pointer. The heap object
// tag is shifted away.
void MacroAssembler::RecordWrite(Register object,
Register address,
Register scratch) {
// The compiled code assumes that record write doesn't change the
// context register, so we check that none of the clobbered
// registers are cp.
ASSERT(!object.is(cp) && !address.is(cp) && !scratch.is(cp));
Label done;
// First, test that the object is not in the new space. We cannot set
// region marks for new space pages.
InNewSpace(object, scratch, eq, &done);
// Record the actual write.
RecordWriteHelper(object, address, scratch);
bind(&done);
// Clobber all input registers when running with the debug-code flag
// turned on to provoke errors.
if (FLAG_debug_code) {
mov(object, Operand(BitCast<int32_t>(kZapValue)));
mov(address, Operand(BitCast<int32_t>(kZapValue)));
mov(scratch, Operand(BitCast<int32_t>(kZapValue)));
}
}
void MacroAssembler::Ldrd(Register dst1, Register dst2, void MacroAssembler::Ldrd(Register dst1, Register dst2,
const MemOperand& src, Condition cond) { const MemOperand& src, Condition cond) {
ASSERT(src.rm().is(no_reg)); ASSERT(src.rm().is(no_reg));
......
...@@ -137,22 +137,32 @@ class MacroAssembler: public Assembler { ...@@ -137,22 +137,32 @@ class MacroAssembler: public Assembler {
Label* branch); Label* branch);
// For the page containing |object| mark the region covering [object+offset] // For the page containing |object| mark the region covering [address]
// dirty. The object address must be in the first 8K of an allocated page. // dirty. The object address must be in the first 8K of an allocated page.
void RecordWriteHelper(Register object, void RecordWriteHelper(Register object,
Operand offset, Register address,
Register scratch0, Register scratch);
Register scratch1);
// For the page containing |object| mark the region covering [object+offset] // For the page containing |object| mark the region covering
// dirty. The object address must be in the first 8K of an allocated page. // [object+offset] dirty. The object address must be in the first 8K
// The 'scratch' registers are used in the implementation and all 3 registers // of an allocated page. The 'scratch' registers are used in the
// are clobbered by the operation, as well as the ip register. // implementation and all 3 registers are clobbered by the
// operation, as well as the ip register. RecordWrite updates the
// write barrier even when storing smis.
void RecordWrite(Register object, void RecordWrite(Register object,
Operand offset, Operand offset,
Register scratch0, Register scratch0,
Register scratch1); Register scratch1);
// For the page containing |object| mark the region covering
// [address] dirty. The object address must be in the first 8K of an
// allocated page. All 3 registers are clobbered by the operation,
// as well as the ip register. RecordWrite updates the write barrier
// even when storing smis.
void RecordWrite(Register object,
Register address,
Register scratch);
// Push two registers. Pushes leftmost register first (to highest address). // Push two registers. Pushes leftmost register first (to highest address).
void Push(Register src1, Register src2, Condition cond = al) { void Push(Register src1, Register src2, Condition cond = al) {
ASSERT(!src1.is(src2)); ASSERT(!src1.is(src2));
......
...@@ -1263,6 +1263,11 @@ static void Generate_StoreIC_Miss(MacroAssembler* masm) { ...@@ -1263,6 +1263,11 @@ static void Generate_StoreIC_Miss(MacroAssembler* masm) {
} }
static void Generate_StoreIC_Normal(MacroAssembler* masm) {
StoreIC::GenerateNormal(masm);
}
static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) { static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) {
StoreIC::GenerateMegamorphic(masm); StoreIC::GenerateMegamorphic(masm);
} }
......
...@@ -98,6 +98,7 @@ enum BuiltinExtraArguments { ...@@ -98,6 +98,7 @@ enum BuiltinExtraArguments {
\ \
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \ V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \
V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC) \ V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC) \
V(StoreIC_Normal, STORE_IC, MONOMORPHIC) \
V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \ V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \
\ \
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \ V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \
......
This diff is collapsed.
...@@ -98,11 +98,6 @@ void MacroAssembler::InNewSpace(Register object, ...@@ -98,11 +98,6 @@ void MacroAssembler::InNewSpace(Register object,
} }
// For page containing |object| mark region covering [object+offset] dirty.
// object is the object being stored into, value is the object being stored.
// If offset is zero, then the scratch register contains the array index into
// the elements array represented as a Smi.
// All registers are clobbered by the operation.
void MacroAssembler::RecordWrite(Register object, int offset, void MacroAssembler::RecordWrite(Register object, int offset,
Register value, Register scratch) { Register value, Register scratch) {
// The compiled code assumes that record write doesn't change the // The compiled code assumes that record write doesn't change the
...@@ -153,6 +148,39 @@ void MacroAssembler::RecordWrite(Register object, int offset, ...@@ -153,6 +148,39 @@ void MacroAssembler::RecordWrite(Register object, int offset,
} }
void MacroAssembler::RecordWrite(Register object,
Register address,
Register value) {
// The compiled code assumes that record write doesn't change the
// context register, so we check that none of the clobbered
// registers are esi.
ASSERT(!object.is(esi) && !value.is(esi) && !address.is(esi));
// First, check if a write barrier is even needed. The tests below
// catch stores of Smis and stores into young gen.
Label done;
// Skip barrier if writing a smi.
ASSERT_EQ(0, kSmiTag);
test(value, Immediate(kSmiTagMask));
j(zero, &done);
InNewSpace(object, value, equal, &done);
RecordWriteHelper(object, address, value);
bind(&done);
// Clobber all input registers when running with the debug-code flag
// turned on to provoke errors.
if (FLAG_debug_code) {
mov(object, Immediate(BitCast<int32_t>(kZapValue)));
mov(address, Immediate(BitCast<int32_t>(kZapValue)));
mov(value, Immediate(BitCast<int32_t>(kZapValue)));
}
}
void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) { void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
cmp(esp, cmp(esp,
Operand::StaticVariable(ExternalReference::address_of_stack_limit())); Operand::StaticVariable(ExternalReference::address_of_stack_limit()));
......
...@@ -73,16 +73,27 @@ class MacroAssembler: public Assembler { ...@@ -73,16 +73,27 @@ class MacroAssembler: public Assembler {
Condition cc, // equal for new space, not_equal otherwise. Condition cc, // equal for new space, not_equal otherwise.
Label* branch); Label* branch);
// For page containing |object| mark region covering [object+offset] dirty. // For page containing |object| mark region covering [object+offset]
// object is the object being stored into, value is the object being stored. // dirty. |object| is the object being stored into, |value| is the
// If offset is zero, then the scratch register contains the array index into // object being stored. If offset is zero, then the scratch register
// the elements array represented as a Smi. // contains the array index into the elements array represented as a
// All registers are clobbered by the operation. // Smi. All registers are clobbered by the operation. RecordWrite
// filters out smis so it does not update the write barrier if the
// value is a smi.
void RecordWrite(Register object, void RecordWrite(Register object,
int offset, int offset,
Register value, Register value,
Register scratch); Register scratch);
// For page containing |object| mark region covering |address|
// dirty. |object| is the object being stored into, |value| is the
// object being stored. All registers are clobbered by the
// operation. RecordWrite filters out smis so it does not update the
// write barrier if the value is a smi.
void RecordWrite(Register object,
Register address,
Register value);
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Debugger Support // Debugger Support
......
...@@ -836,7 +836,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup, ...@@ -836,7 +836,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
// property must be found in the receiver for the stub to be // property must be found in the receiver for the stub to be
// applicable. // applicable.
if (lookup->holder() != *receiver) return; if (lookup->holder() != *receiver) return;
code = StubCache::ComputeLoadNormal(*name, *receiver); code = StubCache::ComputeLoadNormal();
} }
break; break;
} }
...@@ -1198,16 +1198,18 @@ void StoreIC::UpdateCaches(LookupResult* lookup, ...@@ -1198,16 +1198,18 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
break; break;
} }
case NORMAL: { case NORMAL: {
if (!receiver->IsGlobalObject()) { if (receiver->IsGlobalObject()) {
return; // The stub generated for the global object picks the value directly
// from the property cell. So the property must be directly on the
// global object.
Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
JSGlobalPropertyCell* cell =
JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
code = StubCache::ComputeStoreGlobal(*name, *global, cell);
} else {
if (lookup->holder() != *receiver) return;
code = StubCache::ComputeStoreNormal();
} }
// The stub generated for the global object picks the value directly
// from the property cell. So the property must be directly on the
// global object.
Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
JSGlobalPropertyCell* cell =
JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
code = StubCache::ComputeStoreGlobal(*name, *global, cell);
break; break;
} }
case CALLBACKS: { case CALLBACKS: {
......
...@@ -384,6 +384,7 @@ class StoreIC: public IC { ...@@ -384,6 +384,7 @@ class StoreIC: public IC {
static void GenerateMiss(MacroAssembler* masm); static void GenerateMiss(MacroAssembler* masm);
static void GenerateMegamorphic(MacroAssembler* masm); static void GenerateMegamorphic(MacroAssembler* masm);
static void GenerateArrayLength(MacroAssembler* masm); static void GenerateArrayLength(MacroAssembler* masm);
static void GenerateNormal(MacroAssembler* masm);
private: private:
// Update the inline cache and the global stub cache based on the // Update the inline cache and the global stub cache based on the
......
...@@ -198,7 +198,7 @@ Object* StubCache::ComputeLoadInterceptor(String* name, ...@@ -198,7 +198,7 @@ Object* StubCache::ComputeLoadInterceptor(String* name,
} }
Object* StubCache::ComputeLoadNormal(String* name, JSObject* receiver) { Object* StubCache::ComputeLoadNormal() {
return Builtins::builtin(Builtins::LoadIC_Normal); return Builtins::builtin(Builtins::LoadIC_Normal);
} }
...@@ -371,6 +371,11 @@ Object* StubCache::ComputeStoreField(String* name, ...@@ -371,6 +371,11 @@ Object* StubCache::ComputeStoreField(String* name,
} }
Object* StubCache::ComputeStoreNormal() {
return Builtins::builtin(Builtins::StoreIC_Normal);
}
Object* StubCache::ComputeStoreGlobal(String* name, Object* StubCache::ComputeStoreGlobal(String* name,
GlobalObject* receiver, GlobalObject* receiver,
JSGlobalPropertyCell* cell) { JSGlobalPropertyCell* cell) {
......
...@@ -77,7 +77,7 @@ class StubCache : public AllStatic { ...@@ -77,7 +77,7 @@ class StubCache : public AllStatic {
JSObject* receiver, JSObject* receiver,
JSObject* holder); JSObject* holder);
static Object* ComputeLoadNormal(String* name, JSObject* receiver); static Object* ComputeLoadNormal();
static Object* ComputeLoadGlobal(String* name, static Object* ComputeLoadGlobal(String* name,
...@@ -121,6 +121,8 @@ class StubCache : public AllStatic { ...@@ -121,6 +121,8 @@ class StubCache : public AllStatic {
int field_index, int field_index,
Map* transition = NULL); Map* transition = NULL);
static Object* ComputeStoreNormal();
static Object* ComputeStoreGlobal(String* name, static Object* ComputeStoreGlobal(String* name,
GlobalObject* receiver, GlobalObject* receiver,
JSGlobalPropertyCell* cell); JSGlobalPropertyCell* cell);
......
...@@ -153,6 +153,8 @@ namespace internal { ...@@ -153,6 +153,8 @@ namespace internal {
SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \ SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
SC(named_store_global_inline, V8.NamedStoreGlobalInline) \ SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \ SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
SC(store_normal_miss, V8.StoreNormalMiss) \
SC(store_normal_hit, V8.StoreNormalHit) \
SC(call_miss, V8.CallMiss) \ SC(call_miss, V8.CallMiss) \
SC(keyed_call_miss, V8.KeyedCallMiss) \ SC(keyed_call_miss, V8.KeyedCallMiss) \
SC(load_miss, V8.LoadMiss) \ SC(load_miss, V8.LoadMiss) \
......
...@@ -61,11 +61,11 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, ...@@ -61,11 +61,11 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global // Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors. // JS object with slow properties and no interceptors.
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm, static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
Register receiver, Register receiver,
Register r0, Register r0,
Register r1, Register r1,
Label* miss) { Label* miss) {
// Register usage: // Register usage:
// receiver: holds the receiver on entry and is unchanged. // receiver: holds the receiver on entry and is unchanged.
// r0: used to hold receiver instance type. // r0: used to hold receiver instance type.
...@@ -98,34 +98,17 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm, ...@@ -98,34 +98,17 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
} }
// Helper function used to load a property from a dictionary backing storage. // Probe the string dictionary in the |elements| register. Jump to the
// This function may return false negatives, so miss_label // |done| label if a property with the given name is found leaving the
// must always call a backup property load that is complete. // index into the dictionary in |r1|. Jump to the |miss| label
// This function is safe to call if name is not a symbol, and will jump to // otherwise.
// the miss_label in that case. static void GenerateStringDictionaryProbes(MacroAssembler* masm,
// The generated code assumes that the receiver has slow properties, Label* miss,
// is not a global object and does not have interceptors. Label* done,
static void GenerateDictionaryLoad(MacroAssembler* masm, Register elements,
Label* miss_label, Register name,
Register elements, Register r0,
Register name, Register r1) {
Register r0,
Register r1,
Register result) {
// Register use:
//
// elements - holds the property dictionary on entry and is unchanged.
//
// name - holds the name of the property on entry and is unchanged.
//
// r0 - used to hold the capacity of the property dictionary.
//
// r1 - used to hold the index into the property dictionary.
//
// result - holds the result on exit if the load succeeded.
Label done;
// Compute the capacity mask. // Compute the capacity mask.
const int kCapacityOffset = const int kCapacityOffset =
StringDictionary::kHeaderSize + StringDictionary::kHeaderSize +
...@@ -157,14 +140,58 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, ...@@ -157,14 +140,58 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ cmpq(name, Operand(elements, r1, times_pointer_size, __ cmpq(name, Operand(elements, r1, times_pointer_size,
kElementsStartOffset - kHeapObjectTag)); kElementsStartOffset - kHeapObjectTag));
if (i != kProbes - 1) { if (i != kProbes - 1) {
__ j(equal, &done); __ j(equal, done);
} else { } else {
__ j(not_equal, miss_label); __ j(not_equal, miss);
} }
} }
}
// Helper function used to load a property from a dictionary backing storage.
// This function may return false negatives, so miss_label
// must always call a backup property load that is complete.
// This function is safe to call if name is not a symbol, and will jump to
// the miss_label in that case.
// The generated code assumes that the receiver has slow properties,
// is not a global object and does not have interceptors.
static void GenerateDictionaryLoad(MacroAssembler* masm,
Label* miss_label,
Register elements,
Register name,
Register r0,
Register r1,
Register result) {
// Register use:
//
// elements - holds the property dictionary on entry and is unchanged.
//
// name - holds the name of the property on entry and is unchanged.
//
// r0 - used to hold the capacity of the property dictionary.
//
// r1 - used to hold the index into the property dictionary.
//
// result - holds the result on exit if the load succeeded.
Label done;
// Check that the value is a normal property. // Probe the dictionary.
GenerateStringDictionaryProbes(masm,
miss_label,
&done,
elements,
name,
r0,
r1);
// If probing finds an entry in the dictionary, r0 contains the
// index into the dictionary. Check that the value is a normal
// property.
__ bind(&done); __ bind(&done);
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ Test(Operand(elements, r1, times_pointer_size, __ Test(Operand(elements, r1, times_pointer_size,
kDetailsOffset - kHeapObjectTag), kDetailsOffset - kHeapObjectTag),
...@@ -179,6 +206,75 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, ...@@ -179,6 +206,75 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
} }
// Helper function used to store a property to a dictionary backing
// storage. This function may fail to store a property even though it
// is in the dictionary, so code at miss_label must always call a
// backup property store that is complete. This function is safe to
// call if name is not a symbol, and will jump to the miss_label in
// that case. The generated code assumes that the receiver has slow
// properties, is not a global object and does not have interceptors.
static void GenerateDictionaryStore(MacroAssembler* masm,
Label* miss_label,
Register elements,
Register name,
Register value,
Register scratch0,
Register scratch1) {
// Register use:
//
// elements - holds the property dictionary on entry and is clobbered.
//
// name - holds the name of the property on entry and is unchanged.
//
// value - holds the value to store and is unchanged.
//
// scratch0 - used for index into the property dictionary and is clobbered.
//
// scratch1 - used to hold the capacity of the property dictionary and is
// clobbered.
Label done;
// Probe the dictionary.
GenerateStringDictionaryProbes(masm,
miss_label,
&done,
elements,
name,
scratch0,
scratch1);
// If probing finds an entry in the dictionary, scratch0 contains the
// index into the dictionary. Check that the value is a normal
// property that is not read only.
__ bind(&done);
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
const int kTypeAndReadOnlyMask
= (PropertyDetails::TypeField::mask() |
PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
__ Test(Operand(elements,
scratch1,
times_pointer_size,
kDetailsOffset - kHeapObjectTag),
Smi::FromInt(kTypeAndReadOnlyMask));
__ j(not_zero, miss_label);
// Store the value at the masked, scaled index.
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ lea(scratch1, Operand(elements,
scratch1,
times_pointer_size,
kValueOffset - kHeapObjectTag));
__ movq(Operand(scratch1, 0), value);
// Update write barrier. Make sure not to clobber the value.
__ movq(scratch0, value);
__ RecordWrite(elements, scratch1, scratch0);
}
static void GenerateNumberDictionaryLoad(MacroAssembler* masm, static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
Label* miss, Label* miss,
Register elements, Register elements,
...@@ -1332,7 +1428,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { ...@@ -1332,7 +1428,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack. // Get the receiver of the function from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
GenerateDictionaryLoadReceiverCheck(masm, rdx, rax, rbx, &miss); GenerateStringDictionaryReceiverCheck(masm, rdx, rax, rbx, &miss);
// rax: elements // rax: elements
// Search the dictionary placing the result in rdi. // Search the dictionary placing the result in rdi.
...@@ -1616,7 +1712,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { ...@@ -1616,7 +1712,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// ----------------------------------- // -----------------------------------
Label miss; Label miss;
GenerateDictionaryLoadReceiverCheck(masm, rax, rdx, rbx, &miss); GenerateStringDictionaryReceiverCheck(masm, rax, rdx, rbx, &miss);
// rdx: elements // rdx: elements
// Search the dictionary placing the result in rax. // Search the dictionary placing the result in rax.
...@@ -1760,6 +1856,28 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) { ...@@ -1760,6 +1856,28 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
} }
void StoreIC::GenerateNormal(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : name
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss, restore_miss;
GenerateStringDictionaryReceiverCheck(masm, rdx, rbx, rdi, &miss);
GenerateDictionaryStore(masm, &miss, rbx, rcx, rax, r8, r9);
__ IncrementCounter(&Counters::store_normal_hit, 1);
__ ret(0);
__ bind(&miss);
__ IncrementCounter(&Counters::store_normal_miss, 1);
GenerateMiss(masm);
}
#undef __ #undef __
......
...@@ -105,12 +105,6 @@ void MacroAssembler::RecordWriteHelper(Register object, ...@@ -105,12 +105,6 @@ void MacroAssembler::RecordWriteHelper(Register object,
} }
// For page containing |object| mark region covering [object+offset] dirty.
// object is the object being stored into, value is the object being stored.
// If offset is zero, then the index register contains the array index into
// the elements array represented a zero extended int32. Otherwise it can be
// used as a scratch register.
// All registers are clobbered by the operation.
void MacroAssembler::RecordWrite(Register object, void MacroAssembler::RecordWrite(Register object,
int offset, int offset,
Register value, Register value,
...@@ -141,6 +135,35 @@ void MacroAssembler::RecordWrite(Register object, ...@@ -141,6 +135,35 @@ void MacroAssembler::RecordWrite(Register object,
} }
void MacroAssembler::RecordWrite(Register object,
Register address,
Register value) {
// The compiled code assumes that record write doesn't change the
// context register, so we check that none of the clobbered
// registers are esi.
ASSERT(!object.is(rsi) && !value.is(rsi) && !address.is(rsi));
// First, check if a write barrier is even needed. The tests below
// catch stores of Smis and stores into young gen.
Label done;
JumpIfSmi(value, &done);
InNewSpace(object, value, equal, &done);
RecordWriteHelper(object, address, value);
bind(&done);
// Clobber all input registers when running with the debug-code flag
// turned on to provoke errors.
if (FLAG_debug_code) {
movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
movq(address, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
}
}
void MacroAssembler::RecordWriteNonSmi(Register object, void MacroAssembler::RecordWriteNonSmi(Register object,
int offset, int offset,
Register scratch, Register scratch,
......
...@@ -93,16 +93,27 @@ class MacroAssembler: public Assembler { ...@@ -93,16 +93,27 @@ class MacroAssembler: public Assembler {
Condition cc, Condition cc,
Label* branch); Label* branch);
// For page containing |object| mark region covering [object+offset] dirty. // For page containing |object| mark region covering [object+offset]
// object is the object being stored into, value is the object being stored. // dirty. |object| is the object being stored into, |value| is the
// If offset is zero, then the scratch register contains the array index into // object being stored. If |offset| is zero, then the |scratch|
// the elements array represented as a Smi. // register contains the array index into the elements array
// All registers are clobbered by the operation. // represented as a Smi. All registers are clobbered by the
// operation. RecordWrite filters out smis so it does not update the
// write barrier if the value is a smi.
void RecordWrite(Register object, void RecordWrite(Register object,
int offset, int offset,
Register value, Register value,
Register scratch); Register scratch);
// For page containing |object| mark region covering [address]
// dirty. |object| is the object being stored into, |value| is the
// object being stored. All registers are clobbered by the
// operation. RecordWrite filters out smis so it does not update
// the write barrier if the value is a smi.
void RecordWrite(Register object,
Register address,
Register value);
// For page containing |object| mark region covering [object+offset] dirty. // For page containing |object| mark region covering [object+offset] dirty.
// The value is known to not be a smi. // The value is known to not be a smi.
// object is the object being stored into, value is the object being stored. // object is the object being stored into, value is the object being stored.
......
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test dictionary store ICs.
// Function that stores property 'x' on an object.
function store(obj) { obj.x = 42; }
// Create object and force it to dictionary mode by deleting property.
var o = { x: 32, y: 33 };
delete o.y;
// Make the store ic in the 'store' function go into dictionary store
// case.
for (var i = 0; i < 3; i++) {
store(o);
}
assertEquals(42, o.x);
// Test that READ_ONLY property attribute is respected. Make 'x'
// READ_ONLY.
Object.defineProperty(o, 'x', { value: 32, writable: false });
// Attempt to store using the store ic in the 'store' function.
store(o);
// Check that the store did not change the value.
assertEquals(32, o.x);
// Check that bail-out code works.
// Smi.
store(1);
// Fast case object.
o = new Object();
store(o);
assertEquals(42, o.x);
// Slow case object without x property.
delete o.x;
store(o);
assertEquals(42, o.x);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment