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
This commit is contained in:
parent
ff6c4fe680
commit
6044b33766
@ -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, ¬_in_new_space);
|
||||
InNewSpace(object, scratch, ne, ¬_in_new_space);
|
||||
Abort("new-space object passed to RecordWriteHelper");
|
||||
bind(¬_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the value is a normal property.
|
||||
|
||||
|
||||
// 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.
|
||||
|
||||
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
|
||||
|
22
src/ic.cc
22
src/ic.cc
@ -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: {
|
||||
|
1
src/ic.h
1
src/ic.h
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the value is a normal property.
|
||||
|
||||
// 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 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.
|
||||
|
65
test/mjsunit/store-dictionary.js
Normal file
65
test/mjsunit/store-dictionary.js
Normal file
@ -0,0 +1,65 @@
|
||||
// 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);
|
||||
|
Loading…
Reference in New Issue
Block a user