Index: test/cctest/test-unboxed-doubles.cc |
diff --git a/test/cctest/test-unboxed-doubles.cc b/test/cctest/test-unboxed-doubles.cc |
index fdcac3af355067c2d1e5945ef39e83cdec439692..f16f2721b53f80b61912f9445f7616a422bc3e2b 100644 |
--- a/test/cctest/test-unboxed-doubles.cc |
+++ b/test/cctest/test-unboxed-doubles.cc |
@@ -18,7 +18,7 @@ |
using namespace v8::base; |
using namespace v8::internal; |
-#if (V8_DOUBLE_FIELDS_UNBOXING) |
+#if V8_DOUBLE_FIELDS_UNBOXING |
// |
@@ -909,40 +909,38 @@ TEST(Regress436816) { |
TEST(DoScavenge) { |
CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
Isolate* isolate = CcTest::i_isolate(); |
Factory* factory = isolate->factory(); |
- v8::HandleScope scope(CcTest::isolate()); |
- CompileRun( |
- "function A() {" |
- " this.x = 42.5;" |
- " this.o = {};" |
- "};" |
- "var o = new A();"); |
+ // The plan: create |obj| with double field in new space, do scanvenge so |
+ // that |obj| is moved to old space, construct a double value that looks like |
+ // a pointer to "from space" pointer. Do scavenge one more time and ensure |
+ // that it didn't crash or corrupt the double value stored in the object. |
- Handle<String> obj_name = factory->InternalizeUtf8String("o"); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<Map> map = Map::Create(isolate, 10); |
+ map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, |
+ Representation::Double(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
- Handle<Object> obj_value = |
- Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked(); |
- CHECK(obj_value->IsJSObject()); |
- Handle<JSObject> obj = Handle<JSObject>::cast(obj_value); |
+ // Create object in new space. |
+ Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED, false); |
+ |
+ Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); |
+ obj->WriteToField(0, *heap_number); |
{ |
// Ensure the object is properly set up. |
- Map* map = obj->map(); |
- DescriptorArray* descriptors = map->instance_descriptors(); |
- CHECK(map->NumberOfOwnDescriptors() == 2); |
- CHECK(descriptors->GetDetails(0).representation().IsDouble()); |
- CHECK(descriptors->GetDetails(1).representation().IsHeapObject()); |
- FieldIndex field_index = FieldIndex::ForDescriptor(map, 0); |
+ FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); |
CHECK(field_index.is_inobject() && field_index.is_double()); |
CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); |
CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); |
} |
CHECK(isolate->heap()->new_space()->Contains(*obj)); |
- // Trigger GCs so that the newly allocated object moves to old gen. |
- CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
+ // Do scavenge so that |obj| is moved to survivor space. |
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
// Create temp object in the new space. |
Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS, NOT_TENURED); |
@@ -957,9 +955,9 @@ TEST(DoScavenge) { |
Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE); |
obj->FastPropertyAtPut(field_index, *boom_number); |
- // Now the object moves to old gen and it has a double field that looks like |
+ // Now |obj| moves to old gen and it has a double field that looks like |
// a pointer to a from semi-space. |
- CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now |
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE, "boom"); |
CHECK(isolate->heap()->old_pointer_space()->Contains(*obj)); |
@@ -967,6 +965,96 @@ TEST(DoScavenge) { |
} |
+TEST(DoScavengeWithIncrementalWriteBarrier) { |
+ if (FLAG_never_compact || !FLAG_incremental_marking) return; |
+ CcTest::InitializeVM(); |
+ v8::HandleScope scope(CcTest::isolate()); |
+ Isolate* isolate = CcTest::i_isolate(); |
+ Factory* factory = isolate->factory(); |
+ Heap* heap = CcTest::heap(); |
+ PagedSpace* old_pointer_space = heap->old_pointer_space(); |
+ |
+ // The plan: create |obj_value| in old space and ensure that it is allocated |
+ // on evacuation candidate page, create |obj| with double and tagged fields |
+ // in new space and write |obj_value| to tagged field of |obj|, do two |
+ // scavenges to promote |obj| to old space, a GC in old space and ensure that |
+ // the tagged value was properly updated after candidates evacuation. |
+ |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<Map> map = Map::Create(isolate, 10); |
+ map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, |
+ Representation::Double(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE, |
+ Representation::Tagged(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
+ |
+ // Create |obj_value| in old space. |
+ Handle<HeapObject> obj_value; |
+ Page* ec_page; |
+ { |
+ AlwaysAllocateScope always_allocate(isolate); |
+ // Make sure |obj_value| is placed on an old-space evacuation candidate. |
+ SimulateFullSpace(old_pointer_space); |
+ obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); |
+ ec_page = Page::FromAddress(obj_value->address()); |
+ } |
+ |
+ // Create object in new space. |
+ Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED, false); |
+ |
+ Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); |
+ obj->WriteToField(0, *heap_number); |
+ obj->WriteToField(1, *obj_value); |
+ |
+ { |
+ // Ensure the object is properly set up. |
+ FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); |
+ CHECK(field_index.is_inobject() && field_index.is_double()); |
+ CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); |
+ CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); |
+ |
+ field_index = FieldIndex::ForDescriptor(*map, 1); |
+ CHECK(field_index.is_inobject() && !field_index.is_double()); |
+ CHECK(!map->IsUnboxedDoubleField(field_index)); |
+ } |
+ CHECK(isolate->heap()->new_space()->Contains(*obj)); |
+ |
+ // Heap is ready, force |ec_page| to become an evacuation candidate and |
+ // simulate incremental marking. |
+ FLAG_stress_compaction = true; |
+ FLAG_manual_evacuation_candidates_selection = true; |
+ ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); |
+ SimulateIncrementalMarking(heap); |
+ // Disable stress compaction mode in order to let GC do scavenge. |
+ FLAG_stress_compaction = false; |
+ |
+ // Check that everything is ready for triggering incremental write barrier |
+ // during scavenge (i.e. that |obj| is black and incremental marking is |
+ // in compacting mode and |obj_value|'s page is an evacuation candidate). |
+ IncrementalMarking* marking = heap->incremental_marking(); |
+ CHECK(marking->IsCompacting()); |
+ CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj))); |
+ CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
+ |
+ // Trigger GCs so that |obj| moves to old gen. |
+ heap->CollectGarbage(i::NEW_SPACE); // in survivor space now |
+ heap->CollectGarbage(i::NEW_SPACE); // in old gen now |
+ |
+ CHECK(isolate->heap()->old_pointer_space()->Contains(*obj)); |
+ CHECK(isolate->heap()->old_pointer_space()->Contains(*obj_value)); |
+ CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
+ |
+ heap->CollectGarbage(i::OLD_POINTER_SPACE, "boom"); |
+ |
+ // |obj_value| must be evacuated. |
+ CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
+ |
+ FieldIndex field_index = FieldIndex::ForDescriptor(*map, 1); |
+ CHECK_EQ(*obj_value, obj->RawFastPropertyAt(field_index)); |
+} |
+ |
+ |
static void TestLayoutDescriptorHelper(Isolate* isolate, |
int inobject_properties, |
Handle<DescriptorArray> descriptors, |
@@ -1163,28 +1251,23 @@ TEST(StoreBufferScanOnScavenge) { |
Factory* factory = isolate->factory(); |
v8::HandleScope scope(CcTest::isolate()); |
- CompileRun( |
- "function A() {" |
- " this.x = 42.5;" |
- " this.o = {};" |
- "};" |
- "var o = new A();"); |
+ Handle<HeapType> any_type = HeapType::Any(isolate); |
+ Handle<Map> map = Map::Create(isolate, 10); |
+ map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, |
+ Representation::Double(), |
+ INSERT_TRANSITION).ToHandleChecked(); |
- Handle<String> obj_name = factory->InternalizeUtf8String("o"); |
+ // Create object in new space. |
+ Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED, false); |
- Handle<Object> obj_value = |
- Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked(); |
- CHECK(obj_value->IsJSObject()); |
- Handle<JSObject> obj = Handle<JSObject>::cast(obj_value); |
+ Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); |
+ obj->WriteToField(0, *heap_number); |
{ |
// Ensure the object is properly set up. |
- Map* map = obj->map(); |
DescriptorArray* descriptors = map->instance_descriptors(); |
- CHECK(map->NumberOfOwnDescriptors() == 2); |
CHECK(descriptors->GetDetails(0).representation().IsDouble()); |
- CHECK(descriptors->GetDetails(1).representation().IsHeapObject()); |
- FieldIndex field_index = FieldIndex::ForDescriptor(map, 0); |
+ FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); |
CHECK(field_index.is_inobject() && field_index.is_double()); |
CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); |
CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); |