OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <stdlib.h> | 5 #include <stdlib.h> |
6 #include <utility> | 6 #include <utility> |
7 | 7 |
8 #include "src/v8.h" | 8 #include "src/v8.h" |
9 | 9 |
10 #include "src/compilation-cache.h" | 10 #include "src/compilation-cache.h" |
11 #include "src/execution.h" | 11 #include "src/execution.h" |
12 #include "src/factory.h" | 12 #include "src/factory.h" |
13 #include "src/global-handles.h" | 13 #include "src/global-handles.h" |
14 #include "src/ic/ic.h" | 14 #include "src/ic/ic.h" |
15 #include "src/macro-assembler.h" | 15 #include "src/macro-assembler.h" |
16 #include "test/cctest/cctest.h" | 16 #include "test/cctest/cctest.h" |
17 | 17 |
18 using namespace v8::base; | 18 using namespace v8::base; |
19 using namespace v8::internal; | 19 using namespace v8::internal; |
20 | 20 |
21 #if (V8_DOUBLE_FIELDS_UNBOXING) | 21 #if V8_DOUBLE_FIELDS_UNBOXING |
22 | 22 |
23 | 23 |
24 // | 24 // |
25 // Helper functions. | 25 // Helper functions. |
26 // | 26 // |
27 | 27 |
28 | 28 |
29 static void InitializeVerifiedMapDescriptors( | 29 static void InitializeVerifiedMapDescriptors( |
30 Map* map, DescriptorArray* descriptors, | 30 Map* map, DescriptorArray* descriptors, |
31 LayoutDescriptor* layout_descriptor) { | 31 LayoutDescriptor* layout_descriptor) { |
(...skipping 870 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
902 CHECK(!object->HasFastProperties()); | 902 CHECK(!object->HasFastProperties()); |
903 CHECK(object->map()->HasFastPointerLayout()); | 903 CHECK(object->map()->HasFastPointerLayout()); |
904 | 904 |
905 // Trigger GCs and heap verification. | 905 // Trigger GCs and heap verification. |
906 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); | 906 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); |
907 } | 907 } |
908 | 908 |
909 | 909 |
910 TEST(DoScavenge) { | 910 TEST(DoScavenge) { |
911 CcTest::InitializeVM(); | 911 CcTest::InitializeVM(); |
| 912 v8::HandleScope scope(CcTest::isolate()); |
912 Isolate* isolate = CcTest::i_isolate(); | 913 Isolate* isolate = CcTest::i_isolate(); |
913 Factory* factory = isolate->factory(); | 914 Factory* factory = isolate->factory(); |
914 v8::HandleScope scope(CcTest::isolate()); | |
915 | 915 |
916 CompileRun( | 916 // The plan: create |obj| with double field in new space, do scanvenge so |
917 "function A() {" | 917 // that |obj| is moved to old space, construct a double value that looks like |
918 " this.x = 42.5;" | 918 // a pointer to "from space" pointer. Do scavenge one more time and ensure |
919 " this.o = {};" | 919 // that it didn't crash or corrupt the double value stored in the object. |
920 "};" | |
921 "var o = new A();"); | |
922 | 920 |
923 Handle<String> obj_name = factory->InternalizeUtf8String("o"); | 921 Handle<HeapType> any_type = HeapType::Any(isolate); |
| 922 Handle<Map> map = Map::Create(isolate, 10); |
| 923 map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, |
| 924 Representation::Double(), |
| 925 INSERT_TRANSITION).ToHandleChecked(); |
924 | 926 |
925 Handle<Object> obj_value = | 927 // Create object in new space. |
926 Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked(); | 928 Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED, false); |
927 CHECK(obj_value->IsJSObject()); | 929 |
928 Handle<JSObject> obj = Handle<JSObject>::cast(obj_value); | 930 Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); |
| 931 obj->WriteToField(0, *heap_number); |
929 | 932 |
930 { | 933 { |
931 // Ensure the object is properly set up. | 934 // Ensure the object is properly set up. |
932 Map* map = obj->map(); | 935 FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); |
933 DescriptorArray* descriptors = map->instance_descriptors(); | |
934 CHECK(map->NumberOfOwnDescriptors() == 2); | |
935 CHECK(descriptors->GetDetails(0).representation().IsDouble()); | |
936 CHECK(descriptors->GetDetails(1).representation().IsHeapObject()); | |
937 FieldIndex field_index = FieldIndex::ForDescriptor(map, 0); | |
938 CHECK(field_index.is_inobject() && field_index.is_double()); | 936 CHECK(field_index.is_inobject() && field_index.is_double()); |
939 CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); | 937 CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); |
940 CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); | 938 CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); |
941 } | 939 } |
942 CHECK(isolate->heap()->new_space()->Contains(*obj)); | 940 CHECK(isolate->heap()->new_space()->Contains(*obj)); |
943 | 941 |
944 // Trigger GCs so that the newly allocated object moves to old gen. | 942 // Do scavenge so that |obj| is moved to survivor space. |
945 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now | 943 CcTest::heap()->CollectGarbage(i::NEW_SPACE); |
946 | 944 |
947 // Create temp object in the new space. | 945 // Create temp object in the new space. |
948 Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS, NOT_TENURED); | 946 Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS, NOT_TENURED); |
949 CHECK(isolate->heap()->new_space()->Contains(*temp)); | 947 CHECK(isolate->heap()->new_space()->Contains(*temp)); |
950 | 948 |
951 // Construct a double value that looks like a pointer to the new space object | 949 // Construct a double value that looks like a pointer to the new space object |
952 // and store it into the obj. | 950 // and store it into the obj. |
953 Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize; | 951 Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize; |
954 double boom_value = bit_cast<double>(fake_object); | 952 double boom_value = bit_cast<double>(fake_object); |
955 | 953 |
956 FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0); | 954 FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0); |
957 Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE); | 955 Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE); |
958 obj->FastPropertyAtPut(field_index, *boom_number); | 956 obj->FastPropertyAtPut(field_index, *boom_number); |
959 | 957 |
960 // Now the object moves to old gen and it has a double field that looks like | 958 // Now |obj| moves to old gen and it has a double field that looks like |
961 // a pointer to a from semi-space. | 959 // a pointer to a from semi-space. |
962 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now | 960 CcTest::heap()->CollectGarbage(i::NEW_SPACE, "boom"); |
963 | 961 |
964 CHECK(isolate->heap()->old_pointer_space()->Contains(*obj)); | 962 CHECK(isolate->heap()->old_pointer_space()->Contains(*obj)); |
965 | 963 |
966 CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index)); | 964 CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index)); |
967 } | 965 } |
968 | 966 |
969 | 967 |
| 968 TEST(DoScavengeWithIncrementalWriteBarrier) { |
| 969 if (FLAG_never_compact || !FLAG_incremental_marking) return; |
| 970 CcTest::InitializeVM(); |
| 971 v8::HandleScope scope(CcTest::isolate()); |
| 972 Isolate* isolate = CcTest::i_isolate(); |
| 973 Factory* factory = isolate->factory(); |
| 974 Heap* heap = CcTest::heap(); |
| 975 PagedSpace* old_pointer_space = heap->old_pointer_space(); |
| 976 |
| 977 // The plan: create |obj_value| in old space and ensure that it is allocated |
| 978 // on evacuation candidate page, create |obj| with double and tagged fields |
| 979 // in new space and write |obj_value| to tagged field of |obj|, do two |
| 980 // scavenges to promote |obj| to old space, a GC in old space and ensure that |
| 981 // the tagged value was properly updated after candidates evacuation. |
| 982 |
| 983 Handle<HeapType> any_type = HeapType::Any(isolate); |
| 984 Handle<Map> map = Map::Create(isolate, 10); |
| 985 map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, |
| 986 Representation::Double(), |
| 987 INSERT_TRANSITION).ToHandleChecked(); |
| 988 map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE, |
| 989 Representation::Tagged(), |
| 990 INSERT_TRANSITION).ToHandleChecked(); |
| 991 |
| 992 // Create |obj_value| in old space. |
| 993 Handle<HeapObject> obj_value; |
| 994 Page* ec_page; |
| 995 { |
| 996 AlwaysAllocateScope always_allocate(isolate); |
| 997 // Make sure |obj_value| is placed on an old-space evacuation candidate. |
| 998 SimulateFullSpace(old_pointer_space); |
| 999 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); |
| 1000 ec_page = Page::FromAddress(obj_value->address()); |
| 1001 } |
| 1002 |
| 1003 // Create object in new space. |
| 1004 Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED, false); |
| 1005 |
| 1006 Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); |
| 1007 obj->WriteToField(0, *heap_number); |
| 1008 obj->WriteToField(1, *obj_value); |
| 1009 |
| 1010 { |
| 1011 // Ensure the object is properly set up. |
| 1012 FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); |
| 1013 CHECK(field_index.is_inobject() && field_index.is_double()); |
| 1014 CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); |
| 1015 CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); |
| 1016 |
| 1017 field_index = FieldIndex::ForDescriptor(*map, 1); |
| 1018 CHECK(field_index.is_inobject() && !field_index.is_double()); |
| 1019 CHECK(!map->IsUnboxedDoubleField(field_index)); |
| 1020 } |
| 1021 CHECK(isolate->heap()->new_space()->Contains(*obj)); |
| 1022 |
| 1023 // Heap is ready, force |ec_page| to become an evacuation candidate and |
| 1024 // simulate incremental marking. |
| 1025 FLAG_stress_compaction = true; |
| 1026 FLAG_manual_evacuation_candidates_selection = true; |
| 1027 ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); |
| 1028 SimulateIncrementalMarking(heap); |
| 1029 // Disable stress compaction mode in order to let GC do scavenge. |
| 1030 FLAG_stress_compaction = false; |
| 1031 |
| 1032 // Check that everything is ready for triggering incremental write barrier |
| 1033 // during scavenge (i.e. that |obj| is black and incremental marking is |
| 1034 // in compacting mode and |obj_value|'s page is an evacuation candidate). |
| 1035 IncrementalMarking* marking = heap->incremental_marking(); |
| 1036 CHECK(marking->IsCompacting()); |
| 1037 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj))); |
| 1038 CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
| 1039 |
| 1040 // Trigger GCs so that |obj| moves to old gen. |
| 1041 heap->CollectGarbage(i::NEW_SPACE); // in survivor space now |
| 1042 heap->CollectGarbage(i::NEW_SPACE); // in old gen now |
| 1043 |
| 1044 CHECK(isolate->heap()->old_pointer_space()->Contains(*obj)); |
| 1045 CHECK(isolate->heap()->old_pointer_space()->Contains(*obj_value)); |
| 1046 CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
| 1047 |
| 1048 heap->CollectGarbage(i::OLD_POINTER_SPACE, "boom"); |
| 1049 |
| 1050 // |obj_value| must be evacuated. |
| 1051 CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); |
| 1052 |
| 1053 FieldIndex field_index = FieldIndex::ForDescriptor(*map, 1); |
| 1054 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(field_index)); |
| 1055 } |
| 1056 |
| 1057 |
970 static void TestLayoutDescriptorHelper(Isolate* isolate, | 1058 static void TestLayoutDescriptorHelper(Isolate* isolate, |
971 int inobject_properties, | 1059 int inobject_properties, |
972 Handle<DescriptorArray> descriptors, | 1060 Handle<DescriptorArray> descriptors, |
973 int number_of_descriptors) { | 1061 int number_of_descriptors) { |
974 Handle<Map> map = Map::Create(isolate, inobject_properties); | 1062 Handle<Map> map = Map::Create(isolate, inobject_properties); |
975 | 1063 |
976 Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New( | 1064 Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New( |
977 map, descriptors, descriptors->number_of_descriptors()); | 1065 map, descriptors, descriptors->number_of_descriptors()); |
978 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); | 1066 InitializeVerifiedMapDescriptors(*map, *descriptors, *layout_descriptor); |
979 | 1067 |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1156 CHECK(map2->layout_descriptor()->IsConsistentWithMap(*map2)); | 1244 CHECK(map2->layout_descriptor()->IsConsistentWithMap(*map2)); |
1157 } | 1245 } |
1158 | 1246 |
1159 | 1247 |
1160 TEST(StoreBufferScanOnScavenge) { | 1248 TEST(StoreBufferScanOnScavenge) { |
1161 CcTest::InitializeVM(); | 1249 CcTest::InitializeVM(); |
1162 Isolate* isolate = CcTest::i_isolate(); | 1250 Isolate* isolate = CcTest::i_isolate(); |
1163 Factory* factory = isolate->factory(); | 1251 Factory* factory = isolate->factory(); |
1164 v8::HandleScope scope(CcTest::isolate()); | 1252 v8::HandleScope scope(CcTest::isolate()); |
1165 | 1253 |
1166 CompileRun( | 1254 Handle<HeapType> any_type = HeapType::Any(isolate); |
1167 "function A() {" | 1255 Handle<Map> map = Map::Create(isolate, 10); |
1168 " this.x = 42.5;" | 1256 map = Map::CopyWithField(map, MakeName("prop", 0), any_type, NONE, |
1169 " this.o = {};" | 1257 Representation::Double(), |
1170 "};" | 1258 INSERT_TRANSITION).ToHandleChecked(); |
1171 "var o = new A();"); | |
1172 | 1259 |
1173 Handle<String> obj_name = factory->InternalizeUtf8String("o"); | 1260 // Create object in new space. |
| 1261 Handle<JSObject> obj = factory->NewJSObjectFromMap(map, NOT_TENURED, false); |
1174 | 1262 |
1175 Handle<Object> obj_value = | 1263 Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5); |
1176 Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked(); | 1264 obj->WriteToField(0, *heap_number); |
1177 CHECK(obj_value->IsJSObject()); | |
1178 Handle<JSObject> obj = Handle<JSObject>::cast(obj_value); | |
1179 | 1265 |
1180 { | 1266 { |
1181 // Ensure the object is properly set up. | 1267 // Ensure the object is properly set up. |
1182 Map* map = obj->map(); | |
1183 DescriptorArray* descriptors = map->instance_descriptors(); | 1268 DescriptorArray* descriptors = map->instance_descriptors(); |
1184 CHECK(map->NumberOfOwnDescriptors() == 2); | |
1185 CHECK(descriptors->GetDetails(0).representation().IsDouble()); | 1269 CHECK(descriptors->GetDetails(0).representation().IsDouble()); |
1186 CHECK(descriptors->GetDetails(1).representation().IsHeapObject()); | 1270 FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0); |
1187 FieldIndex field_index = FieldIndex::ForDescriptor(map, 0); | |
1188 CHECK(field_index.is_inobject() && field_index.is_double()); | 1271 CHECK(field_index.is_inobject() && field_index.is_double()); |
1189 CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); | 1272 CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index)); |
1190 CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); | 1273 CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index)); |
1191 } | 1274 } |
1192 CHECK(isolate->heap()->new_space()->Contains(*obj)); | 1275 CHECK(isolate->heap()->new_space()->Contains(*obj)); |
1193 | 1276 |
1194 // Trigger GCs so that the newly allocated object moves to old gen. | 1277 // Trigger GCs so that the newly allocated object moves to old gen. |
1195 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now | 1278 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now |
1196 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now | 1279 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now |
1197 | 1280 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1299 Handle<JSObject> clone(JSObject::cast(clone_obj)); | 1382 Handle<JSObject> clone(JSObject::cast(clone_obj)); |
1300 CHECK(heap->old_pointer_space()->Contains(clone->address())); | 1383 CHECK(heap->old_pointer_space()->Contains(clone->address())); |
1301 | 1384 |
1302 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); | 1385 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); |
1303 | 1386 |
1304 // The value in cloned object should not be corrupted by GC. | 1387 // The value in cloned object should not be corrupted by GC. |
1305 CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); | 1388 CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); |
1306 } | 1389 } |
1307 | 1390 |
1308 #endif | 1391 #endif |
OLD | NEW |