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:
mstarzinger@chromium.org 2012-05-29 16:39:26 +00:00
parent 50fdcca1da
commit ebe9a0e0b2
17 changed files with 285 additions and 41 deletions

View File

@ -2105,16 +2105,28 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
bool needs_write_barrier = instr->NeedsWriteBarrier(); bool needs_write_barrier = instr->NeedsWriteBarrier();
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
instr->NeedsWriteBarrierForMap();
LOperand* obj = needs_write_barrier LOperand* obj;
? UseTempRegister(instr->object()) if (needs_write_barrier) {
: UseRegisterAtStart(instr->object()); 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 LOperand* val = needs_write_barrier
? UseTempRegister(instr->value()) ? UseTempRegister(instr->value())
: UseRegister(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);
} }

View File

@ -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: public:
LStoreNamedField(LOperand* obj, LOperand* val) { LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = val; inputs_[1] = val;
temps_[0] = temp;
} }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field") DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")

View File

@ -3675,6 +3675,18 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
if (!instr->transition().is_null()) { if (!instr->transition().is_null()) {
__ mov(scratch, Operand(instr->transition())); __ mov(scratch, Operand(instr->transition()));
__ str(scratch, FieldMemOperand(object, HeapObject::kMapOffset)); __ 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. // Do the store.

View File

@ -473,10 +473,20 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
} }
if (!transition.is_null()) { if (!transition.is_null()) {
// Update the map of the object; no write barrier updating is // Update the map of the object.
// needed because the map is never in new space. __ mov(scratch, Operand(transition));
__ mov(ip, Operand(transition)); __ str(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
__ str(ip, 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 // Adjust for the number of properties stored in the object. Even in the

View File

@ -4198,6 +4198,10 @@ class HStoreNamedField: public HTemplateInstruction<2> {
ReceiverObjectNeedsWriteBarrier(object(), new_space_dominator()); ReceiverObjectNeedsWriteBarrier(object(), new_space_dominator());
} }
bool NeedsWriteBarrierForMap() {
return ReceiverObjectNeedsWriteBarrier(object(), new_space_dominator());
}
private: private:
Handle<String> name_; Handle<String> name_;
bool is_in_object_; bool is_in_object_;

View File

@ -3368,7 +3368,22 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
int offset = instr->offset(); int offset = instr->offset();
if (!instr->transition().is_null()) { if (!instr->transition().is_null()) {
__ mov(FieldOperand(object, HeapObject::kMapOffset), instr->transition()); 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. // Do the store.

View File

@ -2116,6 +2116,8 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
bool needs_write_barrier = instr->NeedsWriteBarrier(); bool needs_write_barrier = instr->NeedsWriteBarrier();
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
instr->NeedsWriteBarrierForMap();
LOperand* obj; LOperand* obj;
if (needs_write_barrier) { if (needs_write_barrier) {
@ -2123,7 +2125,9 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
? UseRegister(instr->object()) ? UseRegister(instr->object())
: UseTempRegister(instr->object()); : UseTempRegister(instr->object());
} else { } else {
obj = UseRegisterAtStart(instr->object()); obj = needs_write_barrier_for_map
? UseRegister(instr->object())
: UseRegisterAtStart(instr->object());
} }
LOperand* val = needs_write_barrier 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 // 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). // have a store into the properties array (not in-object-property).
LOperand* temp = (!instr->is_in_object() || needs_write_barrier) LOperand* temp = (!instr->is_in_object() || needs_write_barrier ||
? TempRegister() needs_write_barrier_for_map) ? TempRegister() : NULL;
: 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);
} }

View File

@ -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: public:
LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) { LStoreNamedField(LOperand* obj,
LOperand* val,
LOperand* temp,
LOperand* temp_map) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = val; inputs_[1] = val;
temps_[0] = temp; temps_[0] = temp;
temps_[1] = temp_map;
} }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field") DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")

View File

@ -782,10 +782,19 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
} }
if (!transition.is_null()) { if (!transition.is_null()) {
// Update the map of the object; no write barrier updating is // Update the map of the object.
// needed because the map is never in new space. __ mov(scratch, Immediate(transition));
__ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch);
Immediate(transition));
// 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 // Adjust for the number of properties stored in the object. Even in the

View File

@ -3455,6 +3455,18 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
if (!instr->transition().is_null()) { if (!instr->transition().is_null()) {
__ li(scratch, Operand(instr->transition())); __ li(scratch, Operand(instr->transition()));
__ sw(scratch, FieldMemOperand(object, HeapObject::kMapOffset)); __ 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. // Do the store.

View File

@ -2046,16 +2046,28 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
bool needs_write_barrier = instr->NeedsWriteBarrier(); bool needs_write_barrier = instr->NeedsWriteBarrier();
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
instr->NeedsWriteBarrierForMap();
LOperand* obj = needs_write_barrier LOperand* obj;
? UseTempRegister(instr->object()) if (needs_write_barrier) {
: UseRegisterAtStart(instr->object()); 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 LOperand* val = needs_write_barrier
? UseTempRegister(instr->value()) ? UseTempRegister(instr->value())
: UseRegister(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);
} }

View File

@ -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: public:
LStoreNamedField(LOperand* obj, LOperand* val) { LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = val; inputs_[1] = val;
temps_[0] = temp;
} }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field") DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")

View File

@ -458,10 +458,20 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
} }
if (!transition.is_null()) { if (!transition.is_null()) {
// Update the map of the object; no write barrier updating is // Update the map of the object.
// needed because the map is never in new space. __ li(scratch, Operand(transition));
__ li(t0, Operand(transition)); __ sw(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
__ sw(t0, 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 // Adjust for the number of properties stored in the object. Even in the

View File

@ -3318,7 +3318,22 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
int offset = instr->offset(); int offset = instr->offset();
if (!instr->transition().is_null()) { 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. // Do the store.

View File

@ -2036,10 +2036,19 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind(
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
bool needs_write_barrier = instr->NeedsWriteBarrier(); bool needs_write_barrier = instr->NeedsWriteBarrier();
bool needs_write_barrier_for_map = !instr->transition().is_null() &&
instr->NeedsWriteBarrierForMap();
LOperand* obj = needs_write_barrier LOperand* obj;
? UseTempRegister(instr->object()) if (needs_write_barrier) {
: UseRegisterAtStart(instr->object()); 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 LOperand* val = needs_write_barrier
? UseTempRegister(instr->value()) ? 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 // 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). // have a store into the properties array (not in-object-property).
LOperand* temp = (!instr->is_in_object() || needs_write_barrier) LOperand* temp = (!instr->is_in_object() || needs_write_barrier ||
? TempRegister() : NULL; needs_write_barrier_for_map) ? TempRegister() : NULL;
return new(zone()) LStoreNamedField(obj, val, temp); return new(zone()) LStoreNamedField(obj, val, temp);
} }

View File

@ -768,9 +768,19 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
} }
if (!transition.is_null()) { if (!transition.is_null()) {
// Update the map of the object; no write barrier updating is // Update the map of the object.
// needed because the map is never in new space. __ Move(scratch, transition);
__ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset), 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 // Adjust for the number of properties stored in the object. Even in the

View File

@ -1794,3 +1794,105 @@ TEST(Regress1465) {
CompileRun("%DebugPrint(root);"); CompileRun("%DebugPrint(root);");
CHECK_EQ(1, transitions_after); 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());
}