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" |
(...skipping 30 matching lines...) Expand all Loading... |
41 } | 41 } |
42 | 42 |
43 | 43 |
44 static Handle<String> MakeName(const char* str, int suffix) { | 44 static Handle<String> MakeName(const char* str, int suffix) { |
45 EmbeddedVector<char, 128> buffer; | 45 EmbeddedVector<char, 128> buffer; |
46 SNPrintF(buffer, "%s%d", str, suffix); | 46 SNPrintF(buffer, "%s%d", str, suffix); |
47 return MakeString(buffer.start()); | 47 return MakeString(buffer.start()); |
48 } | 48 } |
49 | 49 |
50 | 50 |
51 Handle<JSObject> GetObject(const char* name) { | |
52 return v8::Utils::OpenHandle( | |
53 *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str(name)))); | |
54 } | |
55 | |
56 | |
57 static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) { | 51 static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) { |
58 if (obj->IsUnboxedDoubleField(field_index)) { | 52 if (obj->IsUnboxedDoubleField(field_index)) { |
59 return obj->RawFastDoublePropertyAt(field_index); | 53 return obj->RawFastDoublePropertyAt(field_index); |
60 } else { | 54 } else { |
61 Object* value = obj->RawFastPropertyAt(field_index); | 55 Object* value = obj->RawFastPropertyAt(field_index); |
62 DCHECK(value->IsMutableHeapNumber()); | 56 DCHECK(value->IsMutableHeapNumber()); |
63 return HeapNumber::cast(value)->value(); | 57 return HeapNumber::cast(value)->value(); |
64 } | 58 } |
65 } | 59 } |
66 | 60 |
(...skipping 1237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1304 Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked(); | 1298 Object* clone_obj = heap->CopyJSObject(jsobject).ToObjectChecked(); |
1305 Handle<JSObject> clone(JSObject::cast(clone_obj)); | 1299 Handle<JSObject> clone(JSObject::cast(clone_obj)); |
1306 CHECK(heap->old_pointer_space()->Contains(clone->address())); | 1300 CHECK(heap->old_pointer_space()->Contains(clone->address())); |
1307 | 1301 |
1308 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); | 1302 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); |
1309 | 1303 |
1310 // The value in cloned object should not be corrupted by GC. | 1304 // The value in cloned object should not be corrupted by GC. |
1311 CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); | 1305 CHECK_EQ(boom_value, clone->RawFastDoublePropertyAt(index)); |
1312 } | 1306 } |
1313 | 1307 |
1314 | |
1315 static void TestWriteBarrier(Handle<Map> map, Handle<Map> new_map, | |
1316 int tagged_descriptor, int double_descriptor, | |
1317 bool check_tagged_value = true) { | |
1318 FLAG_stress_compaction = true; | |
1319 FLAG_manual_evacuation_candidates_selection = true; | |
1320 Isolate* isolate = CcTest::i_isolate(); | |
1321 Factory* factory = isolate->factory(); | |
1322 Heap* heap = CcTest::heap(); | |
1323 PagedSpace* old_pointer_space = heap->old_pointer_space(); | |
1324 | |
1325 // The plan: create |obj| by |map| in old space, create |obj_value| in | |
1326 // new space and ensure that write barrier is triggered when |obj_value| is | |
1327 // written to property |tagged_descriptor| of |obj|. | |
1328 // Then migrate object to |new_map| and set proper value for property | |
1329 // |double_descriptor|. Call GC and ensure that it did not crash during | |
1330 // store buffer entries updating. | |
1331 | |
1332 Handle<JSObject> obj; | |
1333 Handle<HeapObject> obj_value; | |
1334 { | |
1335 AlwaysAllocateScope always_allocate(isolate); | |
1336 obj = factory->NewJSObjectFromMap(map, TENURED, false); | |
1337 CHECK(old_pointer_space->Contains(*obj)); | |
1338 | |
1339 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS); | |
1340 } | |
1341 | |
1342 CHECK(heap->InNewSpace(*obj_value)); | |
1343 | |
1344 { | |
1345 FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); | |
1346 const int n = 153; | |
1347 for (int i = 0; i < n; i++) { | |
1348 obj->FastPropertyAtPut(index, *obj_value); | |
1349 } | |
1350 } | |
1351 | |
1352 // Migrate |obj| to |new_map| which should shift fields and put the | |
1353 // |boom_value| to the slot that was earlier recorded by write barrier. | |
1354 JSObject::MigrateToMap(obj, new_map); | |
1355 | |
1356 Address fake_object = reinterpret_cast<Address>(*obj_value) + kPointerSize; | |
1357 double boom_value = bit_cast<double>(fake_object); | |
1358 | |
1359 FieldIndex double_field_index = | |
1360 FieldIndex::ForDescriptor(*new_map, double_descriptor); | |
1361 CHECK(obj->IsUnboxedDoubleField(double_field_index)); | |
1362 obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); | |
1363 | |
1364 // Trigger GC to evacuate all candidates. | |
1365 CcTest::heap()->CollectGarbage(NEW_SPACE, "boom"); | |
1366 | |
1367 if (check_tagged_value) { | |
1368 FieldIndex tagged_field_index = | |
1369 FieldIndex::ForDescriptor(*new_map, tagged_descriptor); | |
1370 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); | |
1371 } | |
1372 CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); | |
1373 } | |
1374 | |
1375 | |
1376 static void TestIncrementalWriteBarrier(Handle<Map> map, Handle<Map> new_map, | |
1377 int tagged_descriptor, | |
1378 int double_descriptor, | |
1379 bool check_tagged_value = true) { | |
1380 if (FLAG_never_compact || !FLAG_incremental_marking) return; | |
1381 FLAG_stress_compaction = true; | |
1382 FLAG_manual_evacuation_candidates_selection = true; | |
1383 Isolate* isolate = CcTest::i_isolate(); | |
1384 Factory* factory = isolate->factory(); | |
1385 Heap* heap = CcTest::heap(); | |
1386 PagedSpace* old_pointer_space = heap->old_pointer_space(); | |
1387 | |
1388 // The plan: create |obj| by |map| in old space, create |obj_value| in | |
1389 // old space and ensure it end up in evacuation candidate page. Start | |
1390 // incremental marking and ensure that incremental write barrier is triggered | |
1391 // when |obj_value| is written to property |tagged_descriptor| of |obj|. | |
1392 // Then migrate object to |new_map| and set proper value for property | |
1393 // |double_descriptor|. Call GC and ensure that it did not crash during | |
1394 // slots buffer entries updating. | |
1395 | |
1396 Handle<JSObject> obj; | |
1397 Handle<HeapObject> obj_value; | |
1398 Page* ec_page; | |
1399 { | |
1400 AlwaysAllocateScope always_allocate(isolate); | |
1401 obj = factory->NewJSObjectFromMap(map, TENURED, false); | |
1402 CHECK(old_pointer_space->Contains(*obj)); | |
1403 | |
1404 // Make sure |obj_value| is placed on an old-space evacuation candidate. | |
1405 SimulateFullSpace(old_pointer_space); | |
1406 obj_value = factory->NewJSArray(32 * KB, FAST_HOLEY_ELEMENTS, TENURED); | |
1407 ec_page = Page::FromAddress(obj_value->address()); | |
1408 CHECK_NE(ec_page, Page::FromAddress(obj->address())); | |
1409 } | |
1410 | |
1411 // Heap is ready, force |ec_page| to become an evacuation candidate and | |
1412 // simulate incremental marking. | |
1413 ec_page->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); | |
1414 SimulateIncrementalMarking(heap); | |
1415 | |
1416 // Check that everything is ready for triggering incremental write barrier | |
1417 // (i.e. that both |obj| and |obj_value| are black and the marking phase is | |
1418 // still active and |obj_value|'s page is indeed an evacuation candidate). | |
1419 IncrementalMarking* marking = heap->incremental_marking(); | |
1420 CHECK(marking->IsMarking()); | |
1421 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj))); | |
1422 CHECK(Marking::IsBlack(Marking::MarkBitFrom(*obj_value))); | |
1423 CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); | |
1424 | |
1425 // Trigger incremental write barrier, which should add a slot to |ec_page|'s | |
1426 // slots buffer. | |
1427 { | |
1428 int slots_buffer_len = SlotsBuffer::SizeOfChain(ec_page->slots_buffer()); | |
1429 FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor); | |
1430 const int n = SlotsBuffer::kNumberOfElements + 10; | |
1431 for (int i = 0; i < n; i++) { | |
1432 obj->FastPropertyAtPut(index, *obj_value); | |
1433 } | |
1434 // Ensure that the slot was actually added to the |ec_page|'s slots buffer. | |
1435 CHECK_EQ(slots_buffer_len + n, | |
1436 SlotsBuffer::SizeOfChain(ec_page->slots_buffer())); | |
1437 } | |
1438 | |
1439 // Migrate |obj| to |new_map| which should shift fields and put the | |
1440 // |boom_value| to the slot that was earlier recorded by incremental write | |
1441 // barrier. | |
1442 JSObject::MigrateToMap(obj, new_map); | |
1443 | |
1444 double boom_value = bit_cast<double>(UINT64_C(0xbaad0176a37c28e1)); | |
1445 | |
1446 FieldIndex double_field_index = | |
1447 FieldIndex::ForDescriptor(*new_map, double_descriptor); | |
1448 CHECK(obj->IsUnboxedDoubleField(double_field_index)); | |
1449 obj->RawFastDoublePropertyAtPut(double_field_index, boom_value); | |
1450 | |
1451 // Trigger GC to evacuate all candidates. | |
1452 CcTest::heap()->CollectGarbage(OLD_POINTER_SPACE, "boom"); | |
1453 | |
1454 // Ensure that the values are still there and correct. | |
1455 CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value)); | |
1456 | |
1457 if (check_tagged_value) { | |
1458 FieldIndex tagged_field_index = | |
1459 FieldIndex::ForDescriptor(*new_map, tagged_descriptor); | |
1460 CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index)); | |
1461 } | |
1462 CHECK_EQ(boom_value, obj->RawFastDoublePropertyAt(double_field_index)); | |
1463 } | |
1464 | |
1465 | |
1466 enum WriteBarrierKind { OLD_TO_OLD_WRITE_BARRIER, OLD_TO_NEW_WRITE_BARRIER }; | |
1467 static void TestWriteBarrierObjectShiftFieldsRight( | |
1468 WriteBarrierKind write_barrier_kind) { | |
1469 CcTest::InitializeVM(); | |
1470 Isolate* isolate = CcTest::i_isolate(); | |
1471 v8::HandleScope scope(CcTest::isolate()); | |
1472 | |
1473 Handle<HeapType> any_type = HeapType::Any(isolate); | |
1474 | |
1475 CompileRun("function func() { return 1; }"); | |
1476 | |
1477 Handle<JSObject> func = GetObject("func"); | |
1478 | |
1479 Handle<Map> map = Map::Create(isolate, 10); | |
1480 map = Map::CopyWithConstant(map, MakeName("prop", 0), func, NONE, | |
1481 INSERT_TRANSITION).ToHandleChecked(); | |
1482 map = Map::CopyWithField(map, MakeName("prop", 1), any_type, NONE, | |
1483 Representation::Double(), | |
1484 INSERT_TRANSITION).ToHandleChecked(); | |
1485 map = Map::CopyWithField(map, MakeName("prop", 2), any_type, NONE, | |
1486 Representation::Tagged(), | |
1487 INSERT_TRANSITION).ToHandleChecked(); | |
1488 | |
1489 // Shift fields right by turning constant property to a field. | |
1490 Handle<Map> new_map = Map::ReconfigureProperty( | |
1491 map, 0, kData, NONE, Representation::Tagged(), any_type, FORCE_FIELD); | |
1492 | |
1493 if (write_barrier_kind == OLD_TO_NEW_WRITE_BARRIER) { | |
1494 TestWriteBarrier(map, new_map, 2, 1); | |
1495 } else { | |
1496 CHECK_EQ(OLD_TO_OLD_WRITE_BARRIER, write_barrier_kind); | |
1497 TestIncrementalWriteBarrier(map, new_map, 2, 1); | |
1498 } | |
1499 } | |
1500 | |
1501 | |
1502 // TODO(ishell): enable when this issue is fixed. | |
1503 DISABLED_TEST(WriteBarrierObjectShiftFieldsRight) { | |
1504 TestWriteBarrierObjectShiftFieldsRight(OLD_TO_NEW_WRITE_BARRIER); | |
1505 } | |
1506 | |
1507 | |
1508 TEST(IncrementalWriteBarrierObjectShiftFieldsRight) { | |
1509 TestWriteBarrierObjectShiftFieldsRight(OLD_TO_OLD_WRITE_BARRIER); | |
1510 } | |
1511 | |
1512 | |
1513 // TODO(ishell): add respective tests for property kind reconfiguring from | |
1514 // accessor field to double, once accessor fields are supported by | |
1515 // Map::ReconfigureProperty(). | |
1516 | |
1517 | |
1518 // TODO(ishell): add respective tests for fast property removal case once | |
1519 // Map::ReconfigureProperty() supports that. | |
1520 | |
1521 #endif | 1308 #endif |
OLD | NEW |