Fix missing write barrier in store field stub.
R=vegorov@chromium.org BUG=v8:2143,v8:1465,chromium:129355 TEST=cctest/test-heap/Regress2143 Review URL: https://chromiumcodereview.appspot.com/10443052 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11678 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
50fdcca1da
commit
ebe9a0e0b2
@ -2105,16 +2105,28 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
|
||||
bool needs_write_barrier = instr->NeedsWriteBarrier();
|
||||
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
|
||||
instr->NeedsWriteBarrierForMap();
|
||||
|
||||
LOperand* obj = needs_write_barrier
|
||||
? UseTempRegister(instr->object())
|
||||
LOperand* obj;
|
||||
if (needs_write_barrier) {
|
||||
obj = instr->is_in_object()
|
||||
? UseRegister(instr->object())
|
||||
: UseTempRegister(instr->object());
|
||||
} else {
|
||||
obj = needs_write_barrier_for_map
|
||||
? UseRegister(instr->object())
|
||||
: UseRegisterAtStart(instr->object());
|
||||
}
|
||||
|
||||
LOperand* val = needs_write_barrier
|
||||
? UseTempRegister(instr->value())
|
||||
: UseRegister(instr->value());
|
||||
|
||||
return new(zone()) LStoreNamedField(obj, val);
|
||||
// We need a temporary register for write barrier of the map field.
|
||||
LOperand* temp = needs_write_barrier_for_map ? TempRegister() : NULL;
|
||||
|
||||
return new(zone()) LStoreNamedField(obj, val, temp);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1684,11 +1684,12 @@ class LSmiUntag: public LTemplateInstruction<1, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LStoreNamedField: public LTemplateInstruction<0, 2, 0> {
|
||||
class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
|
||||
public:
|
||||
LStoreNamedField(LOperand* obj, LOperand* val) {
|
||||
LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) {
|
||||
inputs_[0] = obj;
|
||||
inputs_[1] = val;
|
||||
temps_[0] = temp;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
|
||||
|
@ -3675,6 +3675,18 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
|
||||
if (!instr->transition().is_null()) {
|
||||
__ mov(scratch, Operand(instr->transition()));
|
||||
__ str(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
|
||||
if (instr->hydrogen()->NeedsWriteBarrierForMap()) {
|
||||
Register temp = ToRegister(instr->TempAt(0));
|
||||
// Update the write barrier for the map field.
|
||||
__ RecordWriteField(object,
|
||||
HeapObject::kMapOffset,
|
||||
scratch,
|
||||
temp,
|
||||
kLRHasBeenSaved,
|
||||
kSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
// Do the store.
|
||||
|
@ -473,10 +473,20 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
if (!transition.is_null()) {
|
||||
// Update the map of the object; no write barrier updating is
|
||||
// needed because the map is never in new space.
|
||||
__ mov(ip, Operand(transition));
|
||||
__ str(ip, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
|
||||
// Update the map of the object.
|
||||
__ mov(scratch, Operand(transition));
|
||||
__ str(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
|
||||
|
||||
// Update the write barrier for the map field and pass the now unused
|
||||
// name_reg as scratch register.
|
||||
__ RecordWriteField(receiver_reg,
|
||||
HeapObject::kMapOffset,
|
||||
scratch,
|
||||
name_reg,
|
||||
kLRHasNotBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
|
||||
// Adjust for the number of properties stored in the object. Even in the
|
||||
|
@ -4198,6 +4198,10 @@ class HStoreNamedField: public HTemplateInstruction<2> {
|
||||
ReceiverObjectNeedsWriteBarrier(object(), new_space_dominator());
|
||||
}
|
||||
|
||||
bool NeedsWriteBarrierForMap() {
|
||||
return ReceiverObjectNeedsWriteBarrier(object(), new_space_dominator());
|
||||
}
|
||||
|
||||
private:
|
||||
Handle<String> name_;
|
||||
bool is_in_object_;
|
||||
|
@ -3368,7 +3368,22 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
|
||||
int offset = instr->offset();
|
||||
|
||||
if (!instr->transition().is_null()) {
|
||||
if (!instr->hydrogen()->NeedsWriteBarrierForMap()) {
|
||||
__ mov(FieldOperand(object, HeapObject::kMapOffset), instr->transition());
|
||||
} else {
|
||||
Register temp = ToRegister(instr->TempAt(0));
|
||||
Register temp_map = ToRegister(instr->TempAt(1));
|
||||
__ mov(temp_map, instr->transition());
|
||||
__ mov(FieldOperand(object, HeapObject::kMapOffset), temp_map);
|
||||
// Update the write barrier for the map field.
|
||||
__ RecordWriteField(object,
|
||||
HeapObject::kMapOffset,
|
||||
temp_map,
|
||||
temp,
|
||||
kSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
// Do the store.
|
||||
|
@ -2116,6 +2116,8 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
|
||||
bool needs_write_barrier = instr->NeedsWriteBarrier();
|
||||
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
|
||||
instr->NeedsWriteBarrierForMap();
|
||||
|
||||
LOperand* obj;
|
||||
if (needs_write_barrier) {
|
||||
@ -2123,7 +2125,9 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
|
||||
? UseRegister(instr->object())
|
||||
: UseTempRegister(instr->object());
|
||||
} else {
|
||||
obj = UseRegisterAtStart(instr->object());
|
||||
obj = needs_write_barrier_for_map
|
||||
? UseRegister(instr->object())
|
||||
: UseRegisterAtStart(instr->object());
|
||||
}
|
||||
|
||||
LOperand* val = needs_write_barrier
|
||||
@ -2132,11 +2136,13 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
|
||||
|
||||
// We only need a scratch register if we have a write barrier or we
|
||||
// have a store into the properties array (not in-object-property).
|
||||
LOperand* temp = (!instr->is_in_object() || needs_write_barrier)
|
||||
? TempRegister()
|
||||
: NULL;
|
||||
LOperand* temp = (!instr->is_in_object() || needs_write_barrier ||
|
||||
needs_write_barrier_for_map) ? TempRegister() : NULL;
|
||||
|
||||
return new(zone()) LStoreNamedField(obj, val, temp);
|
||||
// We need a temporary register for write barrier of the map field.
|
||||
LOperand* temp_map = needs_write_barrier_for_map ? TempRegister() : NULL;
|
||||
|
||||
return new(zone()) LStoreNamedField(obj, val, temp, temp_map);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1715,12 +1715,16 @@ class LSmiUntag: public LTemplateInstruction<1, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
|
||||
class LStoreNamedField: public LTemplateInstruction<0, 2, 2> {
|
||||
public:
|
||||
LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) {
|
||||
LStoreNamedField(LOperand* obj,
|
||||
LOperand* val,
|
||||
LOperand* temp,
|
||||
LOperand* temp_map) {
|
||||
inputs_[0] = obj;
|
||||
inputs_[1] = val;
|
||||
temps_[0] = temp;
|
||||
temps_[1] = temp_map;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
|
||||
|
@ -782,10 +782,19 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
if (!transition.is_null()) {
|
||||
// Update the map of the object; no write barrier updating is
|
||||
// needed because the map is never in new space.
|
||||
__ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset),
|
||||
Immediate(transition));
|
||||
// Update the map of the object.
|
||||
__ mov(scratch, Immediate(transition));
|
||||
__ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch);
|
||||
|
||||
// Update the write barrier for the map field and pass the now unused
|
||||
// name_reg as scratch register.
|
||||
__ RecordWriteField(receiver_reg,
|
||||
HeapObject::kMapOffset,
|
||||
scratch,
|
||||
name_reg,
|
||||
kDontSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
|
||||
// Adjust for the number of properties stored in the object. Even in the
|
||||
|
@ -3455,6 +3455,18 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
|
||||
if (!instr->transition().is_null()) {
|
||||
__ li(scratch, Operand(instr->transition()));
|
||||
__ sw(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
|
||||
if (instr->hydrogen()->NeedsWriteBarrierForMap()) {
|
||||
Register temp = ToRegister(instr->TempAt(0));
|
||||
// Update the write barrier for the map field.
|
||||
__ RecordWriteField(object,
|
||||
HeapObject::kMapOffset,
|
||||
scratch,
|
||||
temp,
|
||||
kRAHasBeenSaved,
|
||||
kSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
// Do the store.
|
||||
|
@ -2046,16 +2046,28 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
|
||||
bool needs_write_barrier = instr->NeedsWriteBarrier();
|
||||
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
|
||||
instr->NeedsWriteBarrierForMap();
|
||||
|
||||
LOperand* obj = needs_write_barrier
|
||||
? UseTempRegister(instr->object())
|
||||
LOperand* obj;
|
||||
if (needs_write_barrier) {
|
||||
obj = instr->is_in_object()
|
||||
? UseRegister(instr->object())
|
||||
: UseTempRegister(instr->object());
|
||||
} else {
|
||||
obj = needs_write_barrier_for_map
|
||||
? UseRegister(instr->object())
|
||||
: UseRegisterAtStart(instr->object());
|
||||
}
|
||||
|
||||
LOperand* val = needs_write_barrier
|
||||
? UseTempRegister(instr->value())
|
||||
: UseRegister(instr->value());
|
||||
|
||||
return new(zone()) LStoreNamedField(obj, val);
|
||||
// We need a temporary register for write barrier of the map field.
|
||||
LOperand* temp = needs_write_barrier_for_map ? TempRegister() : NULL;
|
||||
|
||||
return new(zone()) LStoreNamedField(obj, val, temp);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1650,11 +1650,12 @@ class LSmiUntag: public LTemplateInstruction<1, 1, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LStoreNamedField: public LTemplateInstruction<0, 2, 0> {
|
||||
class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
|
||||
public:
|
||||
LStoreNamedField(LOperand* obj, LOperand* val) {
|
||||
LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) {
|
||||
inputs_[0] = obj;
|
||||
inputs_[1] = val;
|
||||
temps_[0] = temp;
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
|
||||
|
@ -458,10 +458,20 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
if (!transition.is_null()) {
|
||||
// Update the map of the object; no write barrier updating is
|
||||
// needed because the map is never in new space.
|
||||
__ li(t0, Operand(transition));
|
||||
__ sw(t0, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
|
||||
// Update the map of the object.
|
||||
__ li(scratch, Operand(transition));
|
||||
__ sw(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
|
||||
|
||||
// Update the write barrier for the map field and pass the now unused
|
||||
// name_reg as scratch register.
|
||||
__ RecordWriteField(receiver_reg,
|
||||
HeapObject::kMapOffset,
|
||||
scratch,
|
||||
name_reg,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
|
||||
// Adjust for the number of properties stored in the object. Even in the
|
||||
|
@ -3318,7 +3318,22 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
|
||||
int offset = instr->offset();
|
||||
|
||||
if (!instr->transition().is_null()) {
|
||||
__ Move(FieldOperand(object, HeapObject::kMapOffset), instr->transition());
|
||||
if (!instr->hydrogen()->NeedsWriteBarrierForMap()) {
|
||||
__ Move(FieldOperand(object, HeapObject::kMapOffset),
|
||||
instr->transition());
|
||||
} else {
|
||||
Register temp = ToRegister(instr->TempAt(0));
|
||||
__ Move(kScratchRegister, instr->transition());
|
||||
__ movq(FieldOperand(object, HeapObject::kMapOffset), kScratchRegister);
|
||||
// Update the write barrier for the map field.
|
||||
__ RecordWriteField(object,
|
||||
HeapObject::kMapOffset,
|
||||
kScratchRegister,
|
||||
temp,
|
||||
kSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
// Do the store.
|
||||
|
@ -2036,10 +2036,19 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
|
||||
|
||||
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
|
||||
bool needs_write_barrier = instr->NeedsWriteBarrier();
|
||||
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
|
||||
instr->NeedsWriteBarrierForMap();
|
||||
|
||||
LOperand* obj = needs_write_barrier
|
||||
? UseTempRegister(instr->object())
|
||||
LOperand* obj;
|
||||
if (needs_write_barrier) {
|
||||
obj = instr->is_in_object()
|
||||
? UseRegister(instr->object())
|
||||
: UseTempRegister(instr->object());
|
||||
} else {
|
||||
obj = needs_write_barrier_for_map
|
||||
? UseRegister(instr->object())
|
||||
: UseRegisterAtStart(instr->object());
|
||||
}
|
||||
|
||||
LOperand* val = needs_write_barrier
|
||||
? UseTempRegister(instr->value())
|
||||
@ -2047,8 +2056,8 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
|
||||
|
||||
// We only need a scratch register if we have a write barrier or we
|
||||
// have a store into the properties array (not in-object-property).
|
||||
LOperand* temp = (!instr->is_in_object() || needs_write_barrier)
|
||||
? TempRegister() : NULL;
|
||||
LOperand* temp = (!instr->is_in_object() || needs_write_barrier ||
|
||||
needs_write_barrier_for_map) ? TempRegister() : NULL;
|
||||
|
||||
return new(zone()) LStoreNamedField(obj, val, temp);
|
||||
}
|
||||
|
@ -768,9 +768,19 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
if (!transition.is_null()) {
|
||||
// Update the map of the object; no write barrier updating is
|
||||
// needed because the map is never in new space.
|
||||
__ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset), transition);
|
||||
// Update the map of the object.
|
||||
__ Move(scratch, transition);
|
||||
__ movq(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch);
|
||||
|
||||
// Update the write barrier for the map field and pass the now unused
|
||||
// name_reg as scratch register.
|
||||
__ RecordWriteField(receiver_reg,
|
||||
HeapObject::kMapOffset,
|
||||
scratch,
|
||||
name_reg,
|
||||
kDontSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
}
|
||||
|
||||
// Adjust for the number of properties stored in the object. Even in the
|
||||
|
@ -1794,3 +1794,105 @@ TEST(Regress1465) {
|
||||
CompileRun("%DebugPrint(root);");
|
||||
CHECK_EQ(1, transitions_after);
|
||||
}
|
||||
|
||||
|
||||
TEST(Regress2143a) {
|
||||
i::FLAG_collect_maps = true;
|
||||
i::FLAG_incremental_marking = true;
|
||||
InitializeVM();
|
||||
v8::HandleScope scope;
|
||||
|
||||
// Prepare a map transition from the root object together with a yet
|
||||
// untransitioned root object.
|
||||
CompileRun("var root = new Object;"
|
||||
"root.foo = 0;"
|
||||
"root = new Object;");
|
||||
|
||||
// Go through all incremental marking steps in one swoop.
|
||||
IncrementalMarking* marking = HEAP->incremental_marking();
|
||||
CHECK(marking->IsStopped());
|
||||
marking->Start();
|
||||
CHECK(marking->IsMarking());
|
||||
while (!marking->IsComplete()) {
|
||||
marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
|
||||
}
|
||||
CHECK(marking->IsComplete());
|
||||
|
||||
// Compile a StoreIC that performs the prepared map transition. This
|
||||
// will restart incremental marking and should make sure the root is
|
||||
// marked grey again.
|
||||
CompileRun("function f(o) {"
|
||||
" o.foo = 0;"
|
||||
"}"
|
||||
"f(new Object);"
|
||||
"f(root);");
|
||||
|
||||
// This bug only triggers with aggressive IC clearing.
|
||||
HEAP->AgeInlineCaches();
|
||||
|
||||
// Explicitly request GC to perform final marking step and sweeping.
|
||||
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
|
||||
CHECK(marking->IsStopped());
|
||||
|
||||
Handle<JSObject> root =
|
||||
v8::Utils::OpenHandle(
|
||||
*v8::Handle<v8::Object>::Cast(
|
||||
v8::Context::GetCurrent()->Global()->Get(v8_str("root"))));
|
||||
|
||||
// The root object should be in a sane state.
|
||||
CHECK(root->IsJSObject());
|
||||
CHECK(root->map()->IsMap());
|
||||
}
|
||||
|
||||
|
||||
TEST(Regress2143b) {
|
||||
i::FLAG_collect_maps = true;
|
||||
i::FLAG_incremental_marking = true;
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
InitializeVM();
|
||||
v8::HandleScope scope;
|
||||
|
||||
// Prepare a map transition from the root object together with a yet
|
||||
// untransitioned root object.
|
||||
CompileRun("var root = new Object;"
|
||||
"root.foo = 0;"
|
||||
"root = new Object;");
|
||||
|
||||
// Go through all incremental marking steps in one swoop.
|
||||
IncrementalMarking* marking = HEAP->incremental_marking();
|
||||
CHECK(marking->IsStopped());
|
||||
marking->Start();
|
||||
CHECK(marking->IsMarking());
|
||||
while (!marking->IsComplete()) {
|
||||
marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
|
||||
}
|
||||
CHECK(marking->IsComplete());
|
||||
|
||||
// Compile an optimized LStoreNamedField that performs the prepared
|
||||
// map transition. This will restart incremental marking and should
|
||||
// make sure the root is marked grey again.
|
||||
CompileRun("function f(o) {"
|
||||
" o.foo = 0;"
|
||||
"}"
|
||||
"f(new Object);"
|
||||
"f(new Object);"
|
||||
"%OptimizeFunctionOnNextCall(f);"
|
||||
"f(root);"
|
||||
"%DeoptimizeFunction(f);");
|
||||
|
||||
// This bug only triggers with aggressive IC clearing.
|
||||
HEAP->AgeInlineCaches();
|
||||
|
||||
// Explicitly request GC to perform final marking step and sweeping.
|
||||
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
|
||||
CHECK(marking->IsStopped());
|
||||
|
||||
Handle<JSObject> root =
|
||||
v8::Utils::OpenHandle(
|
||||
*v8::Handle<v8::Object>::Cast(
|
||||
v8::Context::GetCurrent()->Global()->Get(v8_str("root"))));
|
||||
|
||||
// The root object should be in a sane state.
|
||||
CHECK(root->IsJSObject());
|
||||
CHECK(root->map()->IsMap());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user