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) {
void RecordWriteStub::Generate(MacroAssembler* masm) {
__ RecordWriteHelper(object_, Operand(offset_), offset_, scratch_);
__ add(offset_, object_, Operand(offset_));
__ RecordWriteHelper(object_, offset_, scratch_);
__ Ret();
}
......
......@@ -64,12 +64,12 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors.
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
Register receiver,
Register elements,
Register t0,
Register t1,
Label* miss) {
static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
Register receiver,
Register elements,
Register t0,
Register t1,
Label* miss) {
// Register usage:
// receiver: holds the receiver on entry and is unchanged.
// elements: holds the property dictionary on fall through.
......@@ -105,33 +105,16 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
}
// 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 string dictionary in the |elements| register. Jump to the
// |done| label if a property with the given name is found. Jump to
// the |miss| label otherwise.
static void GenerateStringDictionaryProbes(MacroAssembler* masm,
Label* miss,
Label* done,
Register elements,
Register name,
Register scratch1,
Register scratch2) {
// Compute the capacity mask.
const int kCapacityOffset = StringDictionary::kHeaderSize +
StringDictionary::kCapacityIndex * kPointerSize;
......@@ -170,16 +153,56 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
__ cmp(name, Operand(ip));
if (i != kProbes - 1) {
__ b(eq, &done);
__ b(eq, done);
} else {
__ 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
__ ldr(scratch1,
FieldMemOperand(scratch2, kElementsStartOffset + 2 * kPointerSize));
const int kElementsStartOffset = StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
__ tst(scratch1, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
__ b(ne, miss);
......@@ -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,
Label* miss,
Register elements,
......@@ -560,7 +640,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack into r1.
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
GenerateDictionaryLoadReceiverCheck(masm, r1, r0, r3, r4, &miss);
GenerateStringDictionaryReceiverCheck(masm, r1, r0, r3, r4, &miss);
// r0: elements
// Search the dictionary - put result in register r1.
......@@ -815,7 +895,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// -----------------------------------
Label miss;
GenerateDictionaryLoadReceiverCheck(masm, r0, r1, r3, r4, &miss);
GenerateStringDictionaryReceiverCheck(masm, r0, r1, r3, r4, &miss);
// r1: elements
GenerateDictionaryLoad(masm, &miss, r1, r2, r0, r3, r4);
......@@ -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 __
......
......@@ -310,32 +310,28 @@ void MacroAssembler::StoreRoot(Register source,
void MacroAssembler::RecordWriteHelper(Register object,
Operand offset,
Register scratch0,
Register scratch1) {
Register address,
Register scratch) {
if (FLAG_debug_code) {
// Check that the object is 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");
bind(&not_in_new_space);
}
// Add offset into the object.
add(scratch0, object, offset);
// Calculate page address.
Bfc(object, 0, kPageSizeBits);
// Calculate region number.
Ubfx(scratch0, scratch0, Page::kRegionSizeLog2,
Ubfx(address, address, Page::kRegionSizeLog2,
kPageSizeBits - Page::kRegionSizeLog2);
// Mark region dirty.
ldr(scratch1, MemOperand(object, Page::kDirtyFlagOffset));
ldr(scratch, MemOperand(object, Page::kDirtyFlagOffset));
mov(ip, Operand(1));
orr(scratch1, scratch1, Operand(ip, LSL, scratch0));
str(scratch1, MemOperand(object, Page::kDirtyFlagOffset));
orr(scratch, scratch, Operand(ip, LSL, address));
str(scratch, MemOperand(object, Page::kDirtyFlagOffset));
}
......@@ -368,8 +364,11 @@ void MacroAssembler::RecordWrite(Register object,
// region marks for new space pages.
InNewSpace(object, scratch0, eq, &done);
// Add offset into the object.
add(scratch0, object, offset);
// Record the actual write.
RecordWriteHelper(object, offset, scratch0, scratch1);
RecordWriteHelper(object, scratch0, scratch1);
bind(&done);
......@@ -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,
const MemOperand& src, Condition cond) {
ASSERT(src.rm().is(no_reg));
......
......@@ -137,22 +137,32 @@ class MacroAssembler: public Assembler {
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.
void RecordWriteHelper(Register object,
Operand offset,
Register scratch0,
Register scratch1);
Register address,
Register scratch);
// For the page containing |object| mark the region covering [object+offset]
// dirty. The object address must be in the first 8K of an allocated page.
// The 'scratch' registers are used in the implementation and all 3 registers
// are clobbered by the operation, as well as the ip register.
// For the page containing |object| mark the region covering
// [object+offset] dirty. The object address must be in the first 8K
// of an allocated page. The 'scratch' registers are used in the
// 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,
Operand offset,
Register scratch0,
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).
void Push(Register src1, Register src2, Condition cond = al) {
ASSERT(!src1.is(src2));
......
......@@ -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) {
StoreIC::GenerateMegamorphic(masm);
}
......
......@@ -98,6 +98,7 @@ enum BuiltinExtraArguments {
\
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \
V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC) \
V(StoreIC_Normal, STORE_IC, MONOMORPHIC) \
V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \
\
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \
......
......@@ -61,11 +61,11 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors.
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
Register receiver,
Register r0,
Register r1,
Label* miss) {
static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
Register receiver,
Register r0,
Register r1,
Label* miss) {
// Register usage:
// receiver: holds the receiver on entry and is unchanged.
// r0: used to hold receiver instance type.
......@@ -98,36 +98,17 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
}
// 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.
//
// Scratch registers:
//
// r0 - used for the index into the property dictionary
//
// r1 - used to hold the capacity of the property dictionary.
//
// result - holds the result on exit.
Label done;
// Probe the string dictionary in the |elements| register. Jump to the
// |done| label if a property with the given name is found leaving the
// index into the dictionary in |r0|. Jump to the |miss| label
// otherwise.
static void GenerateStringDictionaryProbes(MacroAssembler* masm,
Label* miss,
Label* done,
Register elements,
Register name,
Register r0,
Register r1) {
// Compute the capacity mask.
const int kCapacityOffset =
StringDictionary::kHeaderSize +
......@@ -160,14 +141,61 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ cmp(name, Operand(elements, r0, times_4,
kElementsStartOffset - kHeapObjectTag));
if (i != kProbes - 1) {
__ j(equal, &done, taken);
__ j(equal, done, taken);
} else {
__ j(not_equal, miss_label, not_taken);
__ j(not_equal, miss, not_taken);
}
}
}
// Helper function used to load a property from a dictionary backing
// storage. This function may fail to load a property even though it is
// in the dictionary, so code at 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.
//
// Scratch registers:
//
// r0 - used for the index into the property dictionary
//
// r1 - used to hold the capacity of the property dictionary.
//
// result - holds the result on exit.
// Check that the value is a normal property.
Label done;
// 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);
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize));
......@@ -179,6 +207,69 @@ 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 eventhough 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 r0,
Register r1) {
// 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.
//
// r0 - used for index into the property dictionary and is clobbered.
//
// r1 - used to hold the capacity of the property dictionary and is clobbered.
Label done;
// 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 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, r0, times_4, kDetailsOffset - kHeapObjectTag),
Immediate(kTypeAndReadOnlyMask));
__ j(not_zero, miss_label, not_taken);
// Store the value at the masked, scaled index.
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ lea(r0, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
__ mov(Operand(r0, 0), value);
// Update write barrier. Make sure not to clobber the value.
__ mov(r1, value);
__ RecordWrite(elements, r0, r1);
}
static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
Label* miss,
Register elements,
......@@ -1238,7 +1329,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack; 1 ~ return address.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
GenerateDictionaryLoadReceiverCheck(masm, edx, eax, ebx, &miss);
GenerateStringDictionaryReceiverCheck(masm, edx, eax, ebx, &miss);
// eax: elements
// Search the dictionary placing the result in edi.
......@@ -1517,7 +1608,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// -----------------------------------
Label miss;
GenerateDictionaryLoadReceiverCheck(masm, eax, edx, ebx, &miss);
GenerateStringDictionaryReceiverCheck(masm, eax, edx, ebx, &miss);
// edx: elements
// Search the dictionary placing the result in eax.
......@@ -1775,6 +1866,36 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
}
void StoreIC::GenerateNormal(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : name
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
Label miss, restore_miss;
GenerateStringDictionaryReceiverCheck(masm, edx, ebx, edi, &miss);
// A lot of registers are needed for storing to slow case
// objects. Push and restore receiver but rely on
// GenerateDictionaryStore preserving the value and name.
__ push(edx);
GenerateDictionaryStore(masm, &restore_miss, ebx, ecx, eax, edx, edi);
__ Drop(1);
__ IncrementCounter(&Counters::store_normal_hit, 1);
__ ret(0);
__ bind(&restore_miss);
__ pop(edx);
__ bind(&miss);
__ IncrementCounter(&Counters::store_normal_miss, 1);
GenerateMiss(masm);
}
// Defined in ic.cc.
Object* KeyedStoreIC_Miss(Arguments args);
......
......@@ -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,
Register value, Register scratch) {
// The compiled code assumes that record write doesn't change the
......@@ -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) {
cmp(esp,
Operand::StaticVariable(ExternalReference::address_of_stack_limit()));
......
......@@ -73,16 +73,27 @@ class MacroAssembler: public Assembler {
Condition cc, // equal for new space, not_equal otherwise.
Label* branch);
// 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.
// 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. RecordWrite
// filters out smis so it does not update the write barrier if the
// value is a smi.
void RecordWrite(Register object,
int offset,
Register value,
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
// ---------------------------------------------------------------------------
// Debugger Support
......
......@@ -836,7 +836,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
// property must be found in the receiver for the stub to be
// applicable.
if (lookup->holder() != *receiver) return;
code = StubCache::ComputeLoadNormal(*name, *receiver);
code = StubCache::ComputeLoadNormal();
}
break;
}
......@@ -1198,16 +1198,18 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
break;
}
case NORMAL: {
if (!receiver->IsGlobalObject()) {
return;
if (receiver->IsGlobalObject()) {
// 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;
}
case CALLBACKS: {
......
......@@ -384,6 +384,7 @@ class StoreIC: public IC {
static void GenerateMiss(MacroAssembler* masm);
static void GenerateMegamorphic(MacroAssembler* masm);
static void GenerateArrayLength(MacroAssembler* masm);
static void GenerateNormal(MacroAssembler* masm);
private:
// Update the inline cache and the global stub cache based on the
......
......@@ -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);
}
......@@ -371,6 +371,11 @@ Object* StubCache::ComputeStoreField(String* name,
}
Object* StubCache::ComputeStoreNormal() {
return Builtins::builtin(Builtins::StoreIC_Normal);
}
Object* StubCache::ComputeStoreGlobal(String* name,
GlobalObject* receiver,
JSGlobalPropertyCell* cell) {
......
......@@ -77,7 +77,7 @@ class StubCache : public AllStatic {
JSObject* receiver,
JSObject* holder);
static Object* ComputeLoadNormal(String* name, JSObject* receiver);
static Object* ComputeLoadNormal();
static Object* ComputeLoadGlobal(String* name,
......@@ -121,6 +121,8 @@ class StubCache : public AllStatic {
int field_index,
Map* transition = NULL);
static Object* ComputeStoreNormal();
static Object* ComputeStoreGlobal(String* name,
GlobalObject* receiver,
JSGlobalPropertyCell* cell);
......
......@@ -153,6 +153,8 @@ namespace internal {
SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
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(keyed_call_miss, V8.KeyedCallMiss) \
SC(load_miss, V8.LoadMiss) \
......
......@@ -61,11 +61,11 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors.
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
Register receiver,
Register r0,
Register r1,
Label* miss) {
static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
Register receiver,
Register r0,
Register r1,
Label* miss) {
// Register usage:
// receiver: holds the receiver on entry and is unchanged.
// r0: used to hold receiver instance type.
......@@ -98,34 +98,17 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
}
// 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;
// Probe the string dictionary in the |elements| register. Jump to the
// |done| label if a property with the given name is found leaving the
// index into the dictionary in |r1|. Jump to the |miss| label
// otherwise.
static void GenerateStringDictionaryProbes(MacroAssembler* masm,
Label* miss,
Label* done,
Register elements,
Register name,
Register r0,
Register r1) {
// Compute the capacity mask.
const int kCapacityOffset =
StringDictionary::kHeaderSize +
......@@ -157,14 +140,58 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ cmpq(name, Operand(elements, r1, times_pointer_size,
kElementsStartOffset - kHeapObjectTag));
if (i != kProbes - 1) {
__ j(equal, &done);
__ j(equal, done);
} 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);
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ Test(Operand(elements, r1, times_pointer_size,
kDetailsOffset - kHeapObjectTag),
......@@ -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,
Label* miss,
Register elements,
......@@ -1332,7 +1428,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
GenerateDictionaryLoadReceiverCheck(masm, rdx, rax, rbx, &miss);
GenerateStringDictionaryReceiverCheck(masm, rdx, rax, rbx, &miss);
// rax: elements
// Search the dictionary placing the result in rdi.
......@@ -1616,7 +1712,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// -----------------------------------
Label miss;
GenerateDictionaryLoadReceiverCheck(masm, rax, rdx, rbx, &miss);
GenerateStringDictionaryReceiverCheck(masm, rax, rdx, rbx, &miss);
// rdx: elements
// Search the dictionary placing the result in rax.
......@@ -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 __
......
......@@ -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,
int offset,
Register value,
......@@ -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,
int offset,
Register scratch,
......
......@@ -93,16 +93,27 @@ class MacroAssembler: public Assembler {
Condition cc,
Label* branch);
// 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.
// 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. RecordWrite filters out smis so it does not update the
// write barrier if the value is a smi.
void RecordWrite(Register object,
int offset,
Register value,
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.
// The value is known to not be a smi.
// 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