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) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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_;
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user