Line data Source code
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
3 : // found in the LICENSE file.
4 :
5 : #include <stdlib.h>
6 : #include <utility>
7 :
8 : #include "src/v8.h"
9 :
10 : #include "src/accessors.h"
11 : #include "src/api-inl.h"
12 : #include "src/base/overflowing-math.h"
13 : #include "src/compilation-cache.h"
14 : #include "src/execution.h"
15 : #include "src/field-type.h"
16 : #include "src/global-handles.h"
17 : #include "src/heap/factory.h"
18 : #include "src/heap/heap-inl.h"
19 : #include "src/heap/incremental-marking.h"
20 : #include "src/heap/spaces.h"
21 : #include "src/ic/ic.h"
22 : #include "src/layout-descriptor.h"
23 : #include "src/objects-inl.h"
24 : #include "src/objects/api-callbacks.h"
25 : #include "src/objects/heap-number-inl.h"
26 : #include "src/property.h"
27 : #include "test/cctest/cctest.h"
28 : #include "test/cctest/heap/heap-utils.h"
29 :
30 : namespace v8 {
31 : namespace internal {
32 : namespace test_unboxed_doubles {
33 :
34 : #if V8_DOUBLE_FIELDS_UNBOXING
35 :
36 :
37 : //
38 : // Helper functions.
39 : //
40 :
41 125 : static void InitializeVerifiedMapDescriptors(
42 : Isolate* isolate, Map map, DescriptorArray descriptors,
43 : LayoutDescriptor layout_descriptor) {
44 125 : map->InitializeDescriptors(isolate, descriptors, layout_descriptor);
45 125 : CHECK(layout_descriptor->IsConsistentWithMap(map, true));
46 125 : }
47 :
48 : static Handle<String> MakeString(const char* str) {
49 : Isolate* isolate = CcTest::i_isolate();
50 : Factory* factory = isolate->factory();
51 1335 : return factory->InternalizeUtf8String(str);
52 : }
53 :
54 :
55 1315 : static Handle<String> MakeName(const char* str, int suffix) {
56 : EmbeddedVector<char, 128> buffer;
57 1315 : SNPrintF(buffer, "%s%d", str, suffix);
58 1315 : return MakeString(buffer.start());
59 : }
60 :
61 :
62 10 : Handle<JSObject> GetObject(const char* name) {
63 : return Handle<JSObject>::cast(
64 : v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(
65 20 : CcTest::global()
66 10 : ->Get(v8::Isolate::GetCurrent()->GetCurrentContext(),
67 30 : v8_str(name))
68 10 : .ToLocalChecked())));
69 : }
70 :
71 15 : static double GetDoubleFieldValue(JSObject obj, FieldIndex field_index) {
72 15 : if (obj->IsUnboxedDoubleField(field_index)) {
73 15 : return obj->RawFastDoublePropertyAt(field_index);
74 : } else {
75 0 : Object value = obj->RawFastPropertyAt(field_index);
76 0 : CHECK(value->IsMutableHeapNumber());
77 : return MutableHeapNumber::cast(value)->value();
78 : }
79 : }
80 :
81 15 : void WriteToField(JSObject object, int descriptor, Object value) {
82 15 : DescriptorArray descriptors = object->map()->instance_descriptors();
83 15 : PropertyDetails details = descriptors->GetDetails(descriptor);
84 15 : object->WriteToField(descriptor, details, value);
85 15 : }
86 :
87 : const int kNumberOfBits = 32;
88 : const int kBitsInSmiLayout = SmiValuesAre32Bits() ? 32 : kSmiValueSize - 1;
89 :
90 : enum TestPropertyKind {
91 : PROP_ACCESSOR_INFO,
92 : PROP_SMI,
93 : PROP_DOUBLE,
94 : PROP_TAGGED,
95 : PROP_KIND_NUMBER
96 : };
97 :
98 : static Representation representations[PROP_KIND_NUMBER] = {
99 : Representation::None(), Representation::Smi(), Representation::Double(),
100 26639 : Representation::Tagged()};
101 :
102 :
103 50 : static Handle<DescriptorArray> CreateDescriptorArray(Isolate* isolate,
104 : TestPropertyKind* props,
105 : int kPropsCount) {
106 : Factory* factory = isolate->factory();
107 :
108 : Handle<DescriptorArray> descriptors =
109 50 : DescriptorArray::Allocate(isolate, 0, kPropsCount);
110 :
111 : int next_field_offset = 0;
112 8760 : for (int i = 0; i < kPropsCount; i++) {
113 : EmbeddedVector<char, 64> buffer;
114 4355 : SNPrintF(buffer, "prop%d", i);
115 4355 : Handle<String> name = factory->InternalizeUtf8String(buffer.start());
116 :
117 4355 : TestPropertyKind kind = props[i];
118 :
119 4355 : Descriptor d;
120 4355 : if (kind == PROP_ACCESSOR_INFO) {
121 : Handle<AccessorInfo> info =
122 : Accessors::MakeAccessor(isolate, name, nullptr, nullptr);
123 380 : d = Descriptor::AccessorConstant(name, info, NONE);
124 :
125 : } else {
126 7950 : d = Descriptor::DataField(isolate, name, next_field_offset, NONE,
127 11925 : representations[kind]);
128 : }
129 4355 : descriptors->Append(&d);
130 : PropertyDetails details = d.GetDetails();
131 4355 : if (details.location() == kField) {
132 3975 : next_field_offset += details.field_width_in_words();
133 : }
134 : }
135 50 : return descriptors;
136 : }
137 :
138 :
139 26644 : TEST(LayoutDescriptorBasicFast) {
140 5 : CcTest::InitializeVM();
141 10 : v8::HandleScope scope(CcTest::isolate());
142 :
143 5 : LayoutDescriptor layout_desc = LayoutDescriptor::FastPointerLayout();
144 :
145 : CHECK(!layout_desc->IsSlowLayout());
146 : CHECK(layout_desc->IsFastPointerLayout());
147 : CHECK_EQ(kBitsInSmiLayout, layout_desc->capacity());
148 :
149 : for (int i = 0; i < kBitsInSmiLayout + 13; i++) {
150 : CHECK(layout_desc->IsTagged(i));
151 : }
152 : CHECK(layout_desc->IsTagged(-1));
153 : CHECK(layout_desc->IsTagged(-12347));
154 : CHECK(layout_desc->IsTagged(15635));
155 : CHECK(layout_desc->IsFastPointerLayout());
156 :
157 325 : for (int i = 0; i < kBitsInSmiLayout; i++) {
158 160 : layout_desc = layout_desc->SetTaggedForTesting(i, false);
159 160 : CHECK(!layout_desc->IsTagged(i));
160 160 : layout_desc = layout_desc->SetTaggedForTesting(i, true);
161 160 : CHECK(layout_desc->IsTagged(i));
162 : }
163 5 : CHECK(layout_desc->IsFastPointerLayout());
164 :
165 : int sequence_length;
166 5 : CHECK_EQ(true, layout_desc->IsTagged(0, std::numeric_limits<int>::max(),
167 : &sequence_length));
168 5 : CHECK_EQ(std::numeric_limits<int>::max(), sequence_length);
169 :
170 5 : CHECK(layout_desc->IsTagged(0, 7, &sequence_length));
171 5 : CHECK_EQ(7, sequence_length);
172 5 : }
173 :
174 :
175 26644 : TEST(LayoutDescriptorBasicSlow) {
176 5 : CcTest::InitializeVM();
177 : Isolate* isolate = CcTest::i_isolate();
178 10 : v8::HandleScope scope(CcTest::isolate());
179 :
180 : Handle<LayoutDescriptor> layout_descriptor;
181 : const int kPropsCount = kBitsInSmiLayout * 3;
182 : TestPropertyKind props[kPropsCount];
183 965 : for (int i = 0; i < kPropsCount; i++) {
184 : // All properties tagged.
185 480 : props[i] = PROP_TAGGED;
186 : }
187 :
188 : {
189 : Handle<DescriptorArray> descriptors =
190 5 : CreateDescriptorArray(isolate, props, kPropsCount);
191 :
192 5 : Handle<Map> map = Map::Create(isolate, kPropsCount);
193 :
194 : layout_descriptor =
195 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
196 5 : CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
197 5 : CHECK_EQ(kBitsInSmiLayout, layout_descriptor->capacity());
198 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
199 5 : *layout_descriptor);
200 : }
201 :
202 5 : props[0] = PROP_DOUBLE;
203 5 : props[kPropsCount - 1] = PROP_DOUBLE;
204 :
205 : Handle<DescriptorArray> descriptors =
206 5 : CreateDescriptorArray(isolate, props, kPropsCount);
207 :
208 : {
209 : int inobject_properties = kPropsCount - 1;
210 5 : Handle<Map> map = Map::Create(isolate, inobject_properties);
211 :
212 : // Should be fast as the only double property is the first one.
213 : layout_descriptor =
214 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
215 5 : CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
216 5 : CHECK(!layout_descriptor->IsSlowLayout());
217 5 : CHECK(!layout_descriptor->IsFastPointerLayout());
218 :
219 5 : CHECK(!layout_descriptor->IsTagged(0));
220 955 : for (int i = 1; i < kPropsCount; i++) {
221 475 : CHECK(layout_descriptor->IsTagged(i));
222 : }
223 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
224 5 : *layout_descriptor);
225 : }
226 :
227 : {
228 : int inobject_properties = kPropsCount;
229 5 : Handle<Map> map = Map::Create(isolate, inobject_properties);
230 :
231 : layout_descriptor =
232 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
233 5 : CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
234 5 : CHECK(layout_descriptor->IsSlowLayout());
235 5 : CHECK(!layout_descriptor->IsFastPointerLayout());
236 5 : CHECK_GT(layout_descriptor->capacity(), kBitsInSmiLayout);
237 :
238 5 : CHECK(!layout_descriptor->IsTagged(0));
239 5 : CHECK(!layout_descriptor->IsTagged(kPropsCount - 1));
240 945 : for (int i = 1; i < kPropsCount - 1; i++) {
241 470 : CHECK(layout_descriptor->IsTagged(i));
242 : }
243 :
244 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
245 5 : *layout_descriptor);
246 :
247 : // Here we have truly slow layout descriptor, so play with the bits.
248 : CHECK(layout_descriptor->IsTagged(-1));
249 : CHECK(layout_descriptor->IsTagged(-12347));
250 5 : CHECK(layout_descriptor->IsTagged(15635));
251 :
252 5 : LayoutDescriptor layout_desc = *layout_descriptor;
253 : // Play with the bits but leave it in consistent state with map at the end.
254 945 : for (int i = 1; i < kPropsCount - 1; i++) {
255 470 : layout_desc = layout_desc->SetTaggedForTesting(i, false);
256 470 : CHECK(!layout_desc->IsTagged(i));
257 470 : layout_desc = layout_desc->SetTaggedForTesting(i, true);
258 470 : CHECK(layout_desc->IsTagged(i));
259 : }
260 5 : CHECK(layout_desc->IsSlowLayout());
261 5 : CHECK(!layout_desc->IsFastPointerLayout());
262 5 : CHECK(layout_descriptor->IsConsistentWithMap(*map, true));
263 : }
264 5 : }
265 :
266 :
267 215 : static void TestLayoutDescriptorQueries(int layout_descriptor_length,
268 : int* bit_flip_positions,
269 : int max_sequence_length) {
270 : Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::NewForTesting(
271 215 : CcTest::i_isolate(), layout_descriptor_length);
272 : layout_descriptor_length = layout_descriptor->capacity();
273 215 : LayoutDescriptor layout_desc = *layout_descriptor;
274 :
275 : {
276 : // Fill in the layout descriptor.
277 : int cur_bit_flip_index = 0;
278 : bool tagged = true;
279 291735 : for (int i = 0; i < layout_descriptor_length; i++) {
280 145760 : if (i == bit_flip_positions[cur_bit_flip_index]) {
281 22075 : tagged = !tagged;
282 22075 : ++cur_bit_flip_index;
283 22075 : CHECK(i < bit_flip_positions[cur_bit_flip_index]); // check test data
284 : }
285 145760 : layout_desc = layout_desc->SetTaggedForTesting(i, tagged);
286 : }
287 : }
288 :
289 215 : if (layout_desc->IsFastPointerLayout()) {
290 15 : return;
291 : }
292 :
293 : {
294 : // Check queries.
295 : int cur_bit_flip_index = 0;
296 : bool tagged = true;
297 290760 : for (int i = 0; i < layout_descriptor_length; i++) {
298 145280 : if (i == bit_flip_positions[cur_bit_flip_index]) {
299 22075 : tagged = !tagged;
300 22075 : ++cur_bit_flip_index;
301 : }
302 145280 : CHECK_EQ(tagged, layout_desc->IsTagged(i));
303 :
304 145280 : int next_bit_flip_position = bit_flip_positions[cur_bit_flip_index];
305 : int expected_sequence_length;
306 145280 : if (next_bit_flip_position < layout_desc->capacity()) {
307 68390 : expected_sequence_length = next_bit_flip_position - i;
308 : } else {
309 : expected_sequence_length = tagged ? std::numeric_limits<int>::max()
310 112605 : : (layout_desc->capacity() - i);
311 : }
312 : expected_sequence_length =
313 : Min(expected_sequence_length, max_sequence_length);
314 : int sequence_length;
315 145280 : CHECK_EQ(tagged,
316 : layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
317 145280 : CHECK_GT(sequence_length, 0);
318 :
319 145280 : CHECK_EQ(expected_sequence_length, sequence_length);
320 : }
321 :
322 : int sequence_length;
323 200 : CHECK_EQ(true,
324 : layout_desc->IsTagged(layout_descriptor_length,
325 : max_sequence_length, &sequence_length));
326 200 : CHECK_EQ(max_sequence_length, sequence_length);
327 : }
328 : }
329 :
330 :
331 15 : static void TestLayoutDescriptorQueriesFast(int max_sequence_length) {
332 : {
333 15 : LayoutDescriptor layout_desc = LayoutDescriptor::FastPointerLayout();
334 : int sequence_length;
335 975 : for (int i = 0; i < kNumberOfBits; i++) {
336 480 : CHECK_EQ(true,
337 : layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
338 480 : CHECK_GT(sequence_length, 0);
339 480 : CHECK_EQ(max_sequence_length, sequence_length);
340 : }
341 : }
342 :
343 : {
344 15 : int bit_flip_positions[] = {1000};
345 : TestLayoutDescriptorQueries(kBitsInSmiLayout, bit_flip_positions,
346 15 : max_sequence_length);
347 : }
348 :
349 : {
350 15 : int bit_flip_positions[] = {0, 1000};
351 : TestLayoutDescriptorQueries(kBitsInSmiLayout, bit_flip_positions,
352 15 : max_sequence_length);
353 : }
354 :
355 : {
356 : int bit_flip_positions[kNumberOfBits + 1];
357 1005 : for (int i = 0; i <= kNumberOfBits; i++) {
358 495 : bit_flip_positions[i] = i;
359 : }
360 : TestLayoutDescriptorQueries(kBitsInSmiLayout, bit_flip_positions,
361 15 : max_sequence_length);
362 : }
363 :
364 : {
365 15 : int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30, 1000};
366 : TestLayoutDescriptorQueries(kBitsInSmiLayout, bit_flip_positions,
367 15 : max_sequence_length);
368 : }
369 :
370 : {
371 : int bit_flip_positions[] = {0, 1, 2, 3, 5, 7, 9,
372 15 : 12, 15, 18, 22, 26, 29, 1000};
373 : TestLayoutDescriptorQueries(kBitsInSmiLayout, bit_flip_positions,
374 15 : max_sequence_length);
375 : }
376 15 : }
377 :
378 :
379 26644 : TEST(LayoutDescriptorQueriesFastLimited7) {
380 5 : CcTest::InitializeVM();
381 10 : v8::HandleScope scope(CcTest::isolate());
382 :
383 5 : TestLayoutDescriptorQueriesFast(7);
384 5 : }
385 :
386 :
387 26644 : TEST(LayoutDescriptorQueriesFastLimited13) {
388 5 : CcTest::InitializeVM();
389 10 : v8::HandleScope scope(CcTest::isolate());
390 :
391 5 : TestLayoutDescriptorQueriesFast(13);
392 5 : }
393 :
394 :
395 26644 : TEST(LayoutDescriptorQueriesFastUnlimited) {
396 5 : CcTest::InitializeVM();
397 10 : v8::HandleScope scope(CcTest::isolate());
398 :
399 5 : TestLayoutDescriptorQueriesFast(std::numeric_limits<int>::max());
400 5 : }
401 :
402 :
403 20 : static void TestLayoutDescriptorQueriesSlow(int max_sequence_length) {
404 : {
405 20 : int bit_flip_positions[] = {10000};
406 : TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
407 20 : max_sequence_length);
408 : }
409 :
410 : {
411 20 : int bit_flip_positions[] = {0, 10000};
412 : TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
413 20 : max_sequence_length);
414 : }
415 :
416 : {
417 : int bit_flip_positions[kMaxNumberOfDescriptors + 1];
418 40820 : for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
419 20400 : bit_flip_positions[i] = i;
420 : }
421 20 : bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
422 : TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
423 20 : max_sequence_length);
424 : }
425 :
426 : {
427 : int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30,
428 20 : 37, 54, 80, 99, 383, 10000};
429 : TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
430 20 : max_sequence_length);
431 : }
432 :
433 : {
434 : int bit_flip_positions[] = {0, 10, 20, 30, 50, 70, 90,
435 20 : 120, 150, 180, 220, 260, 290, 10000};
436 : TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
437 20 : max_sequence_length);
438 : }
439 :
440 : {
441 : int bit_flip_positions[kMaxNumberOfDescriptors + 1];
442 : int cur = 0;
443 40820 : for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
444 20400 : bit_flip_positions[i] = cur;
445 20400 : cur = base::MulWithWraparound((cur + 1), 2);
446 : }
447 20 : CHECK_LT(cur, 10000);
448 20 : bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
449 : TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
450 20 : max_sequence_length);
451 : }
452 :
453 : {
454 : int bit_flip_positions[kMaxNumberOfDescriptors + 1];
455 : int cur = 3;
456 40820 : for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
457 20400 : bit_flip_positions[i] = cur;
458 20400 : cur = base::MulWithWraparound((cur + 1), 2);
459 : }
460 20 : CHECK_LT(cur, 10000);
461 20 : bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
462 : TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
463 20 : max_sequence_length);
464 : }
465 20 : }
466 :
467 :
468 26644 : TEST(LayoutDescriptorQueriesSlowLimited7) {
469 5 : CcTest::InitializeVM();
470 10 : v8::HandleScope scope(CcTest::isolate());
471 :
472 5 : TestLayoutDescriptorQueriesSlow(7);
473 5 : }
474 :
475 :
476 26644 : TEST(LayoutDescriptorQueriesSlowLimited13) {
477 5 : CcTest::InitializeVM();
478 10 : v8::HandleScope scope(CcTest::isolate());
479 :
480 5 : TestLayoutDescriptorQueriesSlow(13);
481 5 : }
482 :
483 :
484 26644 : TEST(LayoutDescriptorQueriesSlowLimited42) {
485 5 : CcTest::InitializeVM();
486 10 : v8::HandleScope scope(CcTest::isolate());
487 :
488 5 : TestLayoutDescriptorQueriesSlow(42);
489 5 : }
490 :
491 :
492 26644 : TEST(LayoutDescriptorQueriesSlowUnlimited) {
493 5 : CcTest::InitializeVM();
494 10 : v8::HandleScope scope(CcTest::isolate());
495 :
496 5 : TestLayoutDescriptorQueriesSlow(std::numeric_limits<int>::max());
497 5 : }
498 :
499 :
500 26644 : TEST(LayoutDescriptorCreateNewFast) {
501 5 : CcTest::InitializeVM();
502 : Isolate* isolate = CcTest::i_isolate();
503 10 : v8::HandleScope scope(CcTest::isolate());
504 :
505 : Handle<LayoutDescriptor> layout_descriptor;
506 : TestPropertyKind props[] = {
507 : PROP_ACCESSOR_INFO,
508 : PROP_TAGGED, // field #0
509 : PROP_ACCESSOR_INFO,
510 : PROP_DOUBLE, // field #1
511 : PROP_ACCESSOR_INFO,
512 : PROP_TAGGED, // field #2
513 : PROP_ACCESSOR_INFO,
514 5 : };
515 : const int kPropsCount = arraysize(props);
516 :
517 : Handle<DescriptorArray> descriptors =
518 5 : CreateDescriptorArray(isolate, props, kPropsCount);
519 :
520 : {
521 5 : Handle<Map> map = Map::Create(isolate, 0);
522 : layout_descriptor =
523 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
524 5 : CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
525 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
526 5 : *layout_descriptor);
527 : }
528 :
529 : {
530 5 : Handle<Map> map = Map::Create(isolate, 1);
531 : layout_descriptor =
532 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
533 5 : CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
534 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
535 5 : *layout_descriptor);
536 : }
537 :
538 : {
539 5 : Handle<Map> map = Map::Create(isolate, 2);
540 : layout_descriptor =
541 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
542 5 : CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
543 5 : CHECK(!layout_descriptor->IsSlowLayout());
544 5 : CHECK(layout_descriptor->IsTagged(0));
545 5 : CHECK(!layout_descriptor->IsTagged(1));
546 5 : CHECK(layout_descriptor->IsTagged(2));
547 5 : CHECK(layout_descriptor->IsTagged(125));
548 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
549 5 : *layout_descriptor);
550 : }
551 5 : }
552 :
553 :
554 26644 : TEST(LayoutDescriptorCreateNewSlow) {
555 5 : CcTest::InitializeVM();
556 : Isolate* isolate = CcTest::i_isolate();
557 10 : v8::HandleScope scope(CcTest::isolate());
558 :
559 : Handle<LayoutDescriptor> layout_descriptor;
560 : const int kPropsCount = kBitsInSmiLayout * 3;
561 : TestPropertyKind props[kPropsCount];
562 965 : for (int i = 0; i < kPropsCount; i++) {
563 480 : props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
564 : }
565 :
566 : Handle<DescriptorArray> descriptors =
567 5 : CreateDescriptorArray(isolate, props, kPropsCount);
568 :
569 : {
570 5 : Handle<Map> map = Map::Create(isolate, 0);
571 : layout_descriptor =
572 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
573 5 : CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
574 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
575 5 : *layout_descriptor);
576 : }
577 :
578 : {
579 5 : Handle<Map> map = Map::Create(isolate, 1);
580 : layout_descriptor =
581 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
582 5 : CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
583 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
584 5 : *layout_descriptor);
585 : }
586 :
587 : {
588 5 : Handle<Map> map = Map::Create(isolate, 2);
589 : layout_descriptor =
590 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
591 5 : CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
592 5 : CHECK(!layout_descriptor->IsSlowLayout());
593 5 : CHECK(layout_descriptor->IsTagged(0));
594 5 : CHECK(!layout_descriptor->IsTagged(1));
595 5 : CHECK(layout_descriptor->IsTagged(2));
596 5 : CHECK(layout_descriptor->IsTagged(125));
597 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
598 5 : *layout_descriptor);
599 : }
600 :
601 : {
602 : int inobject_properties = kPropsCount / 2;
603 5 : Handle<Map> map = Map::Create(isolate, inobject_properties);
604 : layout_descriptor =
605 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
606 5 : CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
607 5 : CHECK(layout_descriptor->IsSlowLayout());
608 485 : for (int i = 0; i < inobject_properties; i++) {
609 : // PROP_DOUBLE has index 1 among DATA properties.
610 240 : const bool tagged = (i % (PROP_KIND_NUMBER - 1)) != 1;
611 240 : CHECK_EQ(tagged, layout_descriptor->IsTagged(i));
612 : }
613 : // Every property after inobject_properties must be tagged.
614 485 : for (int i = inobject_properties; i < kPropsCount; i++) {
615 240 : CHECK(layout_descriptor->IsTagged(i));
616 : }
617 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
618 5 : *layout_descriptor);
619 :
620 : // Now test LayoutDescriptor::cast_gc_safe().
621 : Handle<LayoutDescriptor> layout_descriptor_copy =
622 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
623 :
624 : LayoutDescriptor layout_desc = *layout_descriptor;
625 : CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
626 : CHECK_EQ(layout_desc, LayoutDescriptor::cast_gc_safe(layout_desc));
627 5 : CHECK(layout_desc->IsSlowLayout());
628 : // Now make it look like a forwarding pointer to layout_descriptor_copy.
629 : MapWord map_word = layout_desc->map_word();
630 5 : CHECK(!map_word.IsForwardingAddress());
631 : layout_desc->set_map_word(
632 : MapWord::FromForwardingAddress(*layout_descriptor_copy));
633 5 : CHECK(layout_desc->map_word().IsForwardingAddress());
634 : CHECK_EQ(layout_desc, LayoutDescriptor::cast_gc_safe(layout_desc));
635 :
636 : // Restore it back.
637 : layout_desc->set_map_word(map_word);
638 : CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
639 : }
640 5 : }
641 :
642 :
643 65 : static Handle<LayoutDescriptor> TestLayoutDescriptorAppend(
644 : Isolate* isolate, int inobject_properties, TestPropertyKind* props,
645 : int kPropsCount) {
646 : Factory* factory = isolate->factory();
647 :
648 : Handle<DescriptorArray> descriptors =
649 65 : DescriptorArray::Allocate(isolate, 0, kPropsCount);
650 :
651 65 : Handle<Map> map = Map::Create(isolate, inobject_properties);
652 130 : map->InitializeDescriptors(isolate, *descriptors,
653 65 : LayoutDescriptor::FastPointerLayout());
654 :
655 : int next_field_offset = 0;
656 11275 : for (int i = 0; i < kPropsCount; i++) {
657 : EmbeddedVector<char, 64> buffer;
658 5605 : SNPrintF(buffer, "prop%d", i);
659 5605 : Handle<String> name = factory->InternalizeUtf8String(buffer.start());
660 :
661 : Handle<LayoutDescriptor> layout_descriptor;
662 5605 : TestPropertyKind kind = props[i];
663 5605 : Descriptor d;
664 5605 : if (kind == PROP_ACCESSOR_INFO) {
665 : Handle<AccessorInfo> info =
666 600 : Accessors::MakeAccessor(isolate, name, nullptr, nullptr);
667 600 : d = Descriptor::AccessorConstant(name, info, NONE);
668 :
669 : } else {
670 10010 : d = Descriptor::DataField(isolate, name, next_field_offset, NONE,
671 15015 : representations[kind]);
672 : }
673 : PropertyDetails details = d.GetDetails();
674 5605 : layout_descriptor = LayoutDescriptor::ShareAppend(isolate, map, details);
675 5605 : descriptors->Append(&d);
676 5605 : if (details.location() == kField) {
677 : int field_width_in_words = details.field_width_in_words();
678 5005 : next_field_offset += field_width_in_words;
679 :
680 : int field_index = details.field_index();
681 5005 : bool is_inobject = field_index < map->GetInObjectProperties();
682 15015 : for (int bit = 0; bit < field_width_in_words; bit++) {
683 10010 : CHECK_EQ(is_inobject && (kind == PROP_DOUBLE),
684 : !layout_descriptor->IsTagged(field_index + bit));
685 : }
686 5005 : CHECK(layout_descriptor->IsTagged(next_field_offset));
687 : }
688 5605 : map->InitializeDescriptors(isolate, *descriptors, *layout_descriptor);
689 : }
690 : Handle<LayoutDescriptor> layout_descriptor(map->layout_descriptor(), isolate);
691 65 : CHECK(layout_descriptor->IsConsistentWithMap(*map, true));
692 65 : return layout_descriptor;
693 : }
694 :
695 :
696 26644 : TEST(LayoutDescriptorAppend) {
697 5 : CcTest::InitializeVM();
698 : Isolate* isolate = CcTest::i_isolate();
699 10 : v8::HandleScope scope(CcTest::isolate());
700 :
701 : Handle<LayoutDescriptor> layout_descriptor;
702 : const int kPropsCount = kBitsInSmiLayout * 3;
703 : TestPropertyKind props[kPropsCount];
704 965 : for (int i = 0; i < kPropsCount; i++) {
705 480 : props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
706 : }
707 :
708 : layout_descriptor =
709 5 : TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
710 5 : CHECK(!layout_descriptor->IsSlowLayout());
711 :
712 : layout_descriptor =
713 5 : TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
714 5 : CHECK(!layout_descriptor->IsSlowLayout());
715 :
716 : layout_descriptor =
717 5 : TestLayoutDescriptorAppend(isolate, kBitsInSmiLayout, props, kPropsCount);
718 5 : CHECK(!layout_descriptor->IsSlowLayout());
719 :
720 : layout_descriptor = TestLayoutDescriptorAppend(isolate, kBitsInSmiLayout * 2,
721 5 : props, kPropsCount);
722 5 : CHECK(layout_descriptor->IsSlowLayout());
723 :
724 : layout_descriptor =
725 5 : TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
726 5 : CHECK(layout_descriptor->IsSlowLayout());
727 5 : }
728 :
729 :
730 26644 : TEST(LayoutDescriptorAppendAllDoubles) {
731 5 : CcTest::InitializeVM();
732 : Isolate* isolate = CcTest::i_isolate();
733 10 : v8::HandleScope scope(CcTest::isolate());
734 :
735 : Handle<LayoutDescriptor> layout_descriptor;
736 : const int kPropsCount = kBitsInSmiLayout * 3;
737 : TestPropertyKind props[kPropsCount];
738 965 : for (int i = 0; i < kPropsCount; i++) {
739 480 : props[i] = PROP_DOUBLE;
740 : }
741 :
742 : layout_descriptor =
743 5 : TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
744 5 : CHECK(!layout_descriptor->IsSlowLayout());
745 :
746 : layout_descriptor =
747 5 : TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
748 5 : CHECK(!layout_descriptor->IsSlowLayout());
749 :
750 : layout_descriptor =
751 5 : TestLayoutDescriptorAppend(isolate, kBitsInSmiLayout, props, kPropsCount);
752 5 : CHECK(!layout_descriptor->IsSlowLayout());
753 :
754 : layout_descriptor = TestLayoutDescriptorAppend(isolate, kBitsInSmiLayout + 1,
755 5 : props, kPropsCount);
756 5 : CHECK(layout_descriptor->IsSlowLayout());
757 :
758 : layout_descriptor = TestLayoutDescriptorAppend(isolate, kBitsInSmiLayout * 2,
759 5 : props, kPropsCount);
760 5 : CHECK(layout_descriptor->IsSlowLayout());
761 :
762 : layout_descriptor =
763 5 : TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
764 5 : CHECK(layout_descriptor->IsSlowLayout());
765 :
766 : {
767 : // Ensure layout descriptor switches into slow mode at the right moment.
768 : layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props,
769 5 : kBitsInSmiLayout);
770 5 : CHECK(!layout_descriptor->IsSlowLayout());
771 :
772 : layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props,
773 5 : kBitsInSmiLayout + 1);
774 5 : CHECK(layout_descriptor->IsSlowLayout());
775 : }
776 5 : }
777 :
778 :
779 65 : static Handle<LayoutDescriptor> TestLayoutDescriptorAppendIfFastOrUseFull(
780 : Isolate* isolate, int inobject_properties,
781 : Handle<DescriptorArray> descriptors, int number_of_descriptors) {
782 65 : Handle<Map> initial_map = Map::Create(isolate, inobject_properties);
783 :
784 : Handle<LayoutDescriptor> full_layout_descriptor = LayoutDescriptor::New(
785 65 : isolate, initial_map, descriptors, descriptors->number_of_descriptors());
786 :
787 : int nof = 0;
788 : bool switched_to_slow_mode = false;
789 :
790 : // This method calls LayoutDescriptor::AppendIfFastOrUseFull() internally
791 : // and does all the required map-descriptors related book keeping.
792 : Handle<Map> last_map = Map::AddMissingTransitionsForTesting(
793 : isolate, initial_map, descriptors, full_layout_descriptor);
794 :
795 : // Follow back pointers to construct a sequence of maps from |map|
796 : // to |last_map|.
797 65 : int descriptors_length = descriptors->number_of_descriptors();
798 65 : std::vector<Handle<Map>> maps(descriptors_length);
799 : {
800 65 : CHECK(last_map->is_stable());
801 65 : Map map = *last_map;
802 12545 : for (int i = 0; i < descriptors_length; i++) {
803 12480 : maps[descriptors_length - 1 - i] = handle(map, isolate);
804 6240 : Object maybe_map = map->GetBackPointer();
805 6240 : CHECK(maybe_map->IsMap());
806 6240 : map = Map::cast(maybe_map);
807 6240 : CHECK(!map->is_stable());
808 : }
809 65 : CHECK_EQ(1, maps[0]->NumberOfOwnDescriptors());
810 : }
811 :
812 : Handle<Map> map;
813 : // Now check layout descriptors of all intermediate maps.
814 11275 : for (int i = 0; i < number_of_descriptors; i++) {
815 5605 : PropertyDetails details = descriptors->GetDetails(i);
816 11210 : map = maps[i];
817 : LayoutDescriptor layout_desc = map->layout_descriptor();
818 :
819 5605 : if (layout_desc->IsSlowLayout()) {
820 : switched_to_slow_mode = true;
821 1465 : CHECK_EQ(*full_layout_descriptor, layout_desc);
822 : } else {
823 4140 : CHECK(!switched_to_slow_mode);
824 4140 : if (details.location() == kField) {
825 : nof++;
826 : int field_index = details.field_index();
827 : int field_width_in_words = details.field_width_in_words();
828 :
829 3660 : bool is_inobject = field_index < map->GetInObjectProperties();
830 10980 : for (int bit = 0; bit < field_width_in_words; bit++) {
831 8910 : CHECK_EQ(is_inobject && details.representation().IsDouble(),
832 : !layout_desc->IsTagged(field_index + bit));
833 : }
834 7320 : CHECK(layout_desc->IsTagged(field_index + field_width_in_words));
835 : }
836 : }
837 5605 : CHECK(map->layout_descriptor()->IsConsistentWithMap(*map));
838 : }
839 :
840 : Handle<LayoutDescriptor> layout_descriptor(map->GetLayoutDescriptor(),
841 : isolate);
842 65 : CHECK(layout_descriptor->IsConsistentWithMap(*map));
843 130 : return layout_descriptor;
844 : }
845 :
846 :
847 26644 : TEST(LayoutDescriptorAppendIfFastOrUseFull) {
848 5 : CcTest::InitializeVM();
849 : Isolate* isolate = CcTest::i_isolate();
850 10 : v8::HandleScope scope(CcTest::isolate());
851 :
852 : Handle<LayoutDescriptor> layout_descriptor;
853 : const int kPropsCount = kBitsInSmiLayout * 3;
854 : TestPropertyKind props[kPropsCount];
855 965 : for (int i = 0; i < kPropsCount; i++) {
856 480 : props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
857 : }
858 : Handle<DescriptorArray> descriptors =
859 5 : CreateDescriptorArray(isolate, props, kPropsCount);
860 :
861 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
862 5 : isolate, 0, descriptors, kPropsCount);
863 5 : CHECK(!layout_descriptor->IsSlowLayout());
864 :
865 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
866 5 : isolate, 13, descriptors, kPropsCount);
867 5 : CHECK(!layout_descriptor->IsSlowLayout());
868 :
869 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
870 5 : isolate, kBitsInSmiLayout, descriptors, kPropsCount);
871 5 : CHECK(!layout_descriptor->IsSlowLayout());
872 :
873 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
874 5 : isolate, kBitsInSmiLayout * 2, descriptors, kPropsCount);
875 5 : CHECK(layout_descriptor->IsSlowLayout());
876 :
877 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
878 5 : isolate, kPropsCount, descriptors, kPropsCount);
879 5 : CHECK(layout_descriptor->IsSlowLayout());
880 5 : }
881 :
882 :
883 26644 : TEST(LayoutDescriptorAppendIfFastOrUseFullAllDoubles) {
884 5 : CcTest::InitializeVM();
885 : Isolate* isolate = CcTest::i_isolate();
886 10 : v8::HandleScope scope(CcTest::isolate());
887 :
888 : Handle<LayoutDescriptor> layout_descriptor;
889 : const int kPropsCount = kBitsInSmiLayout * 3;
890 : TestPropertyKind props[kPropsCount];
891 965 : for (int i = 0; i < kPropsCount; i++) {
892 480 : props[i] = PROP_DOUBLE;
893 : }
894 : Handle<DescriptorArray> descriptors =
895 5 : CreateDescriptorArray(isolate, props, kPropsCount);
896 :
897 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
898 5 : isolate, 0, descriptors, kPropsCount);
899 5 : CHECK(!layout_descriptor->IsSlowLayout());
900 :
901 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
902 5 : isolate, 13, descriptors, kPropsCount);
903 5 : CHECK(!layout_descriptor->IsSlowLayout());
904 :
905 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
906 5 : isolate, kBitsInSmiLayout, descriptors, kPropsCount);
907 5 : CHECK(!layout_descriptor->IsSlowLayout());
908 :
909 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
910 5 : isolate, kBitsInSmiLayout + 1, descriptors, kPropsCount);
911 5 : CHECK(layout_descriptor->IsSlowLayout());
912 :
913 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
914 5 : isolate, kBitsInSmiLayout * 2, descriptors, kPropsCount);
915 5 : CHECK(layout_descriptor->IsSlowLayout());
916 :
917 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
918 5 : isolate, kPropsCount, descriptors, kPropsCount);
919 5 : CHECK(layout_descriptor->IsSlowLayout());
920 :
921 : {
922 : // Ensure layout descriptor switches into slow mode at the right moment.
923 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
924 5 : isolate, kPropsCount, descriptors, kBitsInSmiLayout);
925 5 : CHECK(!layout_descriptor->IsSlowLayout());
926 :
927 : layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
928 5 : isolate, kPropsCount, descriptors, kBitsInSmiLayout + 1);
929 5 : CHECK(layout_descriptor->IsSlowLayout());
930 : }
931 5 : }
932 :
933 :
934 26644 : TEST(Regress436816) {
935 : ManualGCScope manual_gc_scope;
936 5 : CcTest::InitializeVM();
937 : Isolate* isolate = CcTest::i_isolate();
938 : Factory* factory = isolate->factory();
939 10 : v8::HandleScope scope(CcTest::isolate());
940 :
941 : // Force a GC to free up space before we allocate objects whose
942 : // mid-test states would fail heap verification.
943 5 : CcTest::CollectAllGarbage();
944 :
945 : const int kPropsCount = kBitsInSmiLayout * 3;
946 : TestPropertyKind props[kPropsCount];
947 965 : for (int i = 0; i < kPropsCount; i++) {
948 480 : props[i] = PROP_DOUBLE;
949 : }
950 : Handle<DescriptorArray> descriptors =
951 5 : CreateDescriptorArray(isolate, props, kPropsCount);
952 :
953 5 : Handle<Map> map = Map::Create(isolate, kPropsCount);
954 : Handle<LayoutDescriptor> layout_descriptor =
955 5 : LayoutDescriptor::New(isolate, map, descriptors, kPropsCount);
956 5 : map->InitializeDescriptors(isolate, *descriptors, *layout_descriptor);
957 :
958 : Handle<JSObject> object =
959 5 : factory->NewJSObjectFromMap(map, AllocationType::kOld);
960 :
961 : Address fake_address = static_cast<Address>(~kHeapObjectTagMask);
962 : HeapObject fake_object = HeapObject::FromAddress(fake_address);
963 : CHECK(fake_object->IsHeapObject());
964 :
965 : uint64_t boom_value = bit_cast<uint64_t>(fake_object);
966 965 : for (int i = 0; i < kPropsCount; i++) {
967 480 : FieldIndex index = FieldIndex::ForDescriptor(*map, i);
968 480 : CHECK(map->IsUnboxedDoubleField(index));
969 : object->RawFastDoublePropertyAsBitsAtPut(index, boom_value);
970 : }
971 5 : CHECK(object->HasFastProperties());
972 5 : CHECK(!object->map()->HasFastPointerLayout());
973 :
974 : Handle<Map> normalized_map =
975 5 : Map::Normalize(isolate, map, KEEP_INOBJECT_PROPERTIES, "testing");
976 5 : JSObject::MigrateToMap(object, normalized_map);
977 5 : CHECK(!object->HasFastProperties());
978 5 : CHECK(object->map()->HasFastPointerLayout());
979 :
980 : // Trigger GCs and heap verification.
981 5 : CcTest::CollectAllGarbage();
982 5 : }
983 :
984 :
985 26644 : TEST(DescriptorArrayTrimming) {
986 : ManualGCScope manual_gc_scope;
987 5 : CcTest::InitializeVM();
988 10 : v8::HandleScope scope(CcTest::isolate());
989 : Isolate* isolate = CcTest::i_isolate();
990 :
991 : const int kFieldCount = 128;
992 : const int kSplitFieldIndex = 32;
993 : const int kTrimmedLayoutDescriptorLength = 64;
994 :
995 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
996 5 : Handle<Map> map = Map::Create(isolate, kFieldCount);
997 325 : for (int i = 0; i < kSplitFieldIndex; i++) {
998 320 : map = Map::CopyWithField(isolate, map, MakeName("prop", i), any_type, NONE,
999 : PropertyConstness::kMutable, Representation::Smi(),
1000 320 : INSERT_TRANSITION)
1001 : .ToHandleChecked();
1002 : }
1003 10 : map = Map::CopyWithField(isolate, map, MakeName("dbl", kSplitFieldIndex),
1004 : any_type, NONE, PropertyConstness::kMutable,
1005 10 : Representation::Double(), INSERT_TRANSITION)
1006 : .ToHandleChecked();
1007 5 : CHECK(map->layout_descriptor()->IsConsistentWithMap(*map, true));
1008 5 : CHECK(map->layout_descriptor()->IsSlowLayout());
1009 5 : CHECK(map->owns_descriptors());
1010 5 : CHECK_EQ(8, map->layout_descriptor()->length());
1011 :
1012 : {
1013 : // Add transitions to double fields.
1014 10 : v8::HandleScope scope(CcTest::isolate());
1015 :
1016 : Handle<Map> tmp_map = map;
1017 955 : for (int i = kSplitFieldIndex + 1; i < kFieldCount; i++) {
1018 950 : tmp_map = Map::CopyWithField(isolate, tmp_map, MakeName("dbl", i),
1019 : any_type, NONE, PropertyConstness::kMutable,
1020 950 : Representation::Double(), INSERT_TRANSITION)
1021 : .ToHandleChecked();
1022 475 : CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true));
1023 : }
1024 : // Check that descriptors are shared.
1025 5 : CHECK(tmp_map->owns_descriptors());
1026 5 : CHECK_EQ(map->instance_descriptors(), tmp_map->instance_descriptors());
1027 5 : CHECK_EQ(map->layout_descriptor(), tmp_map->layout_descriptor());
1028 : }
1029 5 : CHECK(map->layout_descriptor()->IsSlowLayout());
1030 5 : CHECK_EQ(16, map->layout_descriptor()->length());
1031 :
1032 : // The unused tail of the layout descriptor is now "durty" because of sharing.
1033 5 : CHECK(map->layout_descriptor()->IsConsistentWithMap(*map));
1034 315 : for (int i = kSplitFieldIndex + 1; i < kTrimmedLayoutDescriptorLength; i++) {
1035 155 : CHECK(!map->layout_descriptor()->IsTagged(i));
1036 : }
1037 5 : CHECK_LT(map->NumberOfOwnDescriptors(),
1038 : map->instance_descriptors()->number_of_descriptors());
1039 :
1040 : // Call GC that should trim both |map|'s descriptor array and layout
1041 : // descriptor.
1042 5 : CcTest::CollectAllGarbage();
1043 :
1044 : // The unused tail of the layout descriptor is now "clean" again.
1045 5 : CHECK(map->layout_descriptor()->IsConsistentWithMap(*map, true));
1046 5 : CHECK(map->owns_descriptors());
1047 5 : CHECK_EQ(map->NumberOfOwnDescriptors(),
1048 : map->instance_descriptors()->number_of_descriptors());
1049 5 : CHECK(map->layout_descriptor()->IsSlowLayout());
1050 5 : CHECK_EQ(8, map->layout_descriptor()->length());
1051 :
1052 : {
1053 : // Add transitions to tagged fields.
1054 10 : v8::HandleScope scope(CcTest::isolate());
1055 :
1056 : Handle<Map> tmp_map = map;
1057 945 : for (int i = kSplitFieldIndex + 1; i < kFieldCount - 1; i++) {
1058 940 : tmp_map = Map::CopyWithField(isolate, tmp_map, MakeName("tagged", i),
1059 : any_type, NONE, PropertyConstness::kMutable,
1060 940 : Representation::Tagged(), INSERT_TRANSITION)
1061 : .ToHandleChecked();
1062 470 : CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true));
1063 : }
1064 10 : tmp_map = Map::CopyWithField(isolate, tmp_map, MakeString("dbl"), any_type,
1065 : NONE, PropertyConstness::kMutable,
1066 5 : Representation::Double(), INSERT_TRANSITION)
1067 : .ToHandleChecked();
1068 5 : CHECK(tmp_map->layout_descriptor()->IsConsistentWithMap(*tmp_map, true));
1069 : // Check that descriptors are shared.
1070 5 : CHECK(tmp_map->owns_descriptors());
1071 5 : CHECK_EQ(map->instance_descriptors(), tmp_map->instance_descriptors());
1072 : }
1073 5 : CHECK(map->layout_descriptor()->IsSlowLayout());
1074 5 : }
1075 :
1076 :
1077 26644 : TEST(DoScavenge) {
1078 5 : CcTest::InitializeVM();
1079 10 : v8::HandleScope scope(CcTest::isolate());
1080 : Isolate* isolate = CcTest::i_isolate();
1081 : Factory* factory = isolate->factory();
1082 :
1083 : // The plan: create |obj| with double field in new space, do scanvenge so
1084 : // that |obj| is moved to old space, construct a double value that looks like
1085 : // a pointer to "from space" pointer. Do scavenge one more time and ensure
1086 : // that it didn't crash or corrupt the double value stored in the object.
1087 :
1088 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1089 5 : Handle<Map> map = Map::Create(isolate, 10);
1090 10 : map = Map::CopyWithField(isolate, map, MakeName("prop", 0), any_type, NONE,
1091 : PropertyConstness::kMutable,
1092 10 : Representation::Double(), INSERT_TRANSITION)
1093 : .ToHandleChecked();
1094 :
1095 : // Create object in new space.
1096 : Handle<JSObject> obj =
1097 5 : factory->NewJSObjectFromMap(map, AllocationType::kYoung);
1098 :
1099 5 : Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5);
1100 10 : WriteToField(*obj, 0, *heap_number);
1101 :
1102 : {
1103 : // Ensure the object is properly set up.
1104 5 : FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0);
1105 10 : CHECK(field_index.is_inobject() && field_index.is_double());
1106 5 : CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
1107 5 : CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
1108 : }
1109 5 : CHECK(isolate->heap()->new_space()->Contains(*obj));
1110 :
1111 : // Do scavenge so that |obj| is moved to survivor space.
1112 5 : CcTest::CollectGarbage(i::NEW_SPACE);
1113 :
1114 : // Create temp object in the new space.
1115 : Handle<JSArray> temp = factory->NewJSArray(0, PACKED_ELEMENTS);
1116 5 : CHECK(isolate->heap()->new_space()->Contains(*temp));
1117 :
1118 : // Construct a double value that looks like a pointer to the new space object
1119 : // and store it into the obj.
1120 5 : Address fake_object = temp->ptr() + kSystemPointerSize;
1121 : double boom_value = bit_cast<double>(fake_object);
1122 :
1123 5 : FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0);
1124 5 : auto boom_number = factory->NewMutableHeapNumber(boom_value);
1125 10 : obj->FastPropertyAtPut(field_index, *boom_number);
1126 :
1127 : // Now |obj| moves to old gen and it has a double field that looks like
1128 : // a pointer to a from semi-space.
1129 5 : CcTest::CollectGarbage(i::NEW_SPACE);
1130 :
1131 5 : CHECK(isolate->heap()->old_space()->Contains(*obj));
1132 :
1133 5 : CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index));
1134 5 : }
1135 :
1136 :
1137 26644 : TEST(DoScavengeWithIncrementalWriteBarrier) {
1138 5 : if (FLAG_never_compact || !FLAG_incremental_marking) return;
1139 : ManualGCScope manual_gc_scope;
1140 5 : CcTest::InitializeVM();
1141 10 : v8::HandleScope scope(CcTest::isolate());
1142 : Isolate* isolate = CcTest::i_isolate();
1143 : Factory* factory = isolate->factory();
1144 5 : Heap* heap = CcTest::heap();
1145 : PagedSpace* old_space = heap->old_space();
1146 :
1147 : // The plan: create |obj_value| in old space and ensure that it is allocated
1148 : // on evacuation candidate page, create |obj| with double and tagged fields
1149 : // in new space and write |obj_value| to tagged field of |obj|, do two
1150 : // scavenges to promote |obj| to old space, a GC in old space and ensure that
1151 : // the tagged value was properly updated after candidates evacuation.
1152 :
1153 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1154 5 : Handle<Map> map = Map::Create(isolate, 10);
1155 10 : map = Map::CopyWithField(isolate, map, MakeName("prop", 0), any_type, NONE,
1156 : PropertyConstness::kMutable,
1157 10 : Representation::Double(), INSERT_TRANSITION)
1158 : .ToHandleChecked();
1159 10 : map = Map::CopyWithField(isolate, map, MakeName("prop", 1), any_type, NONE,
1160 : PropertyConstness::kMutable,
1161 10 : Representation::Tagged(), INSERT_TRANSITION)
1162 : .ToHandleChecked();
1163 :
1164 : // Create |obj_value| in old space.
1165 : Handle<HeapObject> obj_value;
1166 : Page* ec_page;
1167 : {
1168 : AlwaysAllocateScope always_allocate(isolate);
1169 : // Make sure |obj_value| is placed on an old-space evacuation candidate.
1170 5 : heap::SimulateFullSpace(old_space);
1171 : obj_value =
1172 : factory->NewJSArray(32 * KB, HOLEY_ELEMENTS, AllocationType::kOld);
1173 : ec_page = Page::FromHeapObject(*obj_value);
1174 : }
1175 :
1176 : // Create object in new space.
1177 : Handle<JSObject> obj =
1178 5 : factory->NewJSObjectFromMap(map, AllocationType::kYoung);
1179 :
1180 5 : Handle<HeapNumber> heap_number = factory->NewHeapNumber(42.5);
1181 10 : WriteToField(*obj, 0, *heap_number);
1182 10 : WriteToField(*obj, 1, *obj_value);
1183 :
1184 : {
1185 : // Ensure the object is properly set up.
1186 5 : FieldIndex field_index = FieldIndex::ForDescriptor(*map, 0);
1187 10 : CHECK(field_index.is_inobject() && field_index.is_double());
1188 5 : CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
1189 5 : CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
1190 :
1191 5 : field_index = FieldIndex::ForDescriptor(*map, 1);
1192 10 : CHECK(field_index.is_inobject() && !field_index.is_double());
1193 5 : CHECK(!map->IsUnboxedDoubleField(field_index));
1194 : }
1195 5 : CHECK(isolate->heap()->new_space()->Contains(*obj));
1196 :
1197 : // Heap is ready, force |ec_page| to become an evacuation candidate and
1198 : // simulate incremental marking.
1199 5 : FLAG_stress_compaction = true;
1200 5 : FLAG_manual_evacuation_candidates_selection = true;
1201 5 : heap::ForceEvacuationCandidate(ec_page);
1202 5 : heap::SimulateIncrementalMarking(heap);
1203 : // Disable stress compaction mode in order to let GC do scavenge.
1204 5 : FLAG_stress_compaction = false;
1205 :
1206 : // Check that everything is ready for triggering incremental write barrier
1207 : // during scavenge (i.e. that |obj| is black and incremental marking is
1208 : // in compacting mode and |obj_value|'s page is an evacuation candidate).
1209 : IncrementalMarking* marking = heap->incremental_marking();
1210 5 : CHECK(marking->IsCompacting());
1211 : IncrementalMarking::MarkingState* marking_state =
1212 : heap->incremental_marking()->marking_state();
1213 5 : CHECK(marking_state->IsBlack(*obj));
1214 5 : CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1215 :
1216 : // Trigger GCs so that |obj| moves to old gen.
1217 5 : CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
1218 5 : CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
1219 :
1220 5 : CHECK(isolate->heap()->old_space()->Contains(*obj));
1221 5 : CHECK(isolate->heap()->old_space()->Contains(*obj_value));
1222 5 : CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1223 :
1224 5 : CcTest::CollectGarbage(i::OLD_SPACE);
1225 :
1226 : // |obj_value| must be evacuated.
1227 5 : CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1228 :
1229 5 : FieldIndex field_index = FieldIndex::ForDescriptor(*map, 1);
1230 10 : CHECK_EQ(*obj_value, obj->RawFastPropertyAt(field_index));
1231 : }
1232 :
1233 :
1234 75 : static void TestLayoutDescriptorHelper(Isolate* isolate,
1235 : int inobject_properties,
1236 : Handle<DescriptorArray> descriptors,
1237 : int number_of_descriptors) {
1238 75 : Handle<Map> map = Map::Create(isolate, inobject_properties);
1239 :
1240 : Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New(
1241 75 : isolate, map, descriptors, descriptors->number_of_descriptors());
1242 : InitializeVerifiedMapDescriptors(isolate, *map, *descriptors,
1243 75 : *layout_descriptor);
1244 :
1245 75 : LayoutDescriptorHelper helper(*map);
1246 : bool all_fields_tagged = true;
1247 :
1248 : int instance_size = map->instance_size();
1249 :
1250 75 : int end_offset = instance_size * 2;
1251 : int first_non_tagged_field_offset = end_offset;
1252 14475 : for (int i = 0; i < number_of_descriptors; i++) {
1253 7200 : PropertyDetails details = descriptors->GetDetails(i);
1254 11445 : if (details.location() != kField) continue;
1255 6600 : FieldIndex index = FieldIndex::ForDescriptor(*map, i);
1256 6600 : if (!index.is_inobject()) continue;
1257 2955 : all_fields_tagged &= !details.representation().IsDouble();
1258 2955 : bool expected_tagged = !index.is_double();
1259 2955 : if (!expected_tagged) {
1260 : first_non_tagged_field_offset =
1261 : Min(first_non_tagged_field_offset, index.offset());
1262 : }
1263 :
1264 : int end_of_region_offset;
1265 2955 : CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
1266 2955 : CHECK_EQ(expected_tagged, helper.IsTagged(index.offset(), instance_size,
1267 : &end_of_region_offset));
1268 2955 : CHECK_GT(end_of_region_offset, 0);
1269 2955 : CHECK_EQ(end_of_region_offset % kTaggedSize, 0);
1270 2955 : CHECK(end_of_region_offset <= instance_size);
1271 :
1272 152685 : for (int offset = index.offset(); offset < end_of_region_offset;
1273 : offset += kTaggedSize) {
1274 74865 : CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
1275 : }
1276 2955 : if (end_of_region_offset < instance_size) {
1277 875 : CHECK_EQ(!expected_tagged, helper.IsTagged(end_of_region_offset));
1278 : } else {
1279 2080 : CHECK(helper.IsTagged(end_of_region_offset));
1280 : }
1281 : }
1282 :
1283 525 : for (int offset = 0; offset < JSObject::kHeaderSize; offset += kTaggedSize) {
1284 : // Header queries
1285 225 : CHECK(helper.IsTagged(offset));
1286 : int end_of_region_offset;
1287 225 : CHECK(helper.IsTagged(offset, end_offset, &end_of_region_offset));
1288 225 : CHECK_EQ(first_non_tagged_field_offset, end_of_region_offset);
1289 :
1290 : // Out of bounds queries
1291 225 : CHECK(helper.IsTagged(offset + instance_size));
1292 : }
1293 :
1294 75 : CHECK_EQ(all_fields_tagged, helper.all_fields_tagged());
1295 75 : }
1296 :
1297 :
1298 26644 : TEST(LayoutDescriptorHelperMixed) {
1299 5 : CcTest::InitializeVM();
1300 : Isolate* isolate = CcTest::i_isolate();
1301 10 : v8::HandleScope scope(CcTest::isolate());
1302 :
1303 : Handle<LayoutDescriptor> layout_descriptor;
1304 : const int kPropsCount = kBitsInSmiLayout * 3;
1305 : TestPropertyKind props[kPropsCount];
1306 965 : for (int i = 0; i < kPropsCount; i++) {
1307 480 : props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
1308 : }
1309 : Handle<DescriptorArray> descriptors =
1310 5 : CreateDescriptorArray(isolate, props, kPropsCount);
1311 :
1312 5 : TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
1313 :
1314 5 : TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
1315 :
1316 : TestLayoutDescriptorHelper(isolate, kBitsInSmiLayout, descriptors,
1317 5 : kPropsCount);
1318 :
1319 : TestLayoutDescriptorHelper(isolate, kBitsInSmiLayout * 2, descriptors,
1320 5 : kPropsCount);
1321 :
1322 5 : TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
1323 5 : }
1324 :
1325 :
1326 26644 : TEST(LayoutDescriptorHelperAllTagged) {
1327 5 : CcTest::InitializeVM();
1328 : Isolate* isolate = CcTest::i_isolate();
1329 10 : v8::HandleScope scope(CcTest::isolate());
1330 :
1331 : Handle<LayoutDescriptor> layout_descriptor;
1332 : const int kPropsCount = kBitsInSmiLayout * 3;
1333 : TestPropertyKind props[kPropsCount];
1334 965 : for (int i = 0; i < kPropsCount; i++) {
1335 480 : props[i] = PROP_TAGGED;
1336 : }
1337 : Handle<DescriptorArray> descriptors =
1338 5 : CreateDescriptorArray(isolate, props, kPropsCount);
1339 :
1340 5 : TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
1341 :
1342 5 : TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
1343 :
1344 : TestLayoutDescriptorHelper(isolate, kBitsInSmiLayout, descriptors,
1345 5 : kPropsCount);
1346 :
1347 : TestLayoutDescriptorHelper(isolate, kBitsInSmiLayout * 2, descriptors,
1348 5 : kPropsCount);
1349 :
1350 5 : TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
1351 5 : }
1352 :
1353 :
1354 26644 : TEST(LayoutDescriptorHelperAllDoubles) {
1355 5 : CcTest::InitializeVM();
1356 : Isolate* isolate = CcTest::i_isolate();
1357 10 : v8::HandleScope scope(CcTest::isolate());
1358 :
1359 : Handle<LayoutDescriptor> layout_descriptor;
1360 : const int kPropsCount = kBitsInSmiLayout * 3;
1361 : TestPropertyKind props[kPropsCount];
1362 965 : for (int i = 0; i < kPropsCount; i++) {
1363 480 : props[i] = PROP_DOUBLE;
1364 : }
1365 : Handle<DescriptorArray> descriptors =
1366 5 : CreateDescriptorArray(isolate, props, kPropsCount);
1367 :
1368 5 : TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
1369 :
1370 5 : TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
1371 :
1372 : TestLayoutDescriptorHelper(isolate, kBitsInSmiLayout, descriptors,
1373 5 : kPropsCount);
1374 :
1375 : TestLayoutDescriptorHelper(isolate, kBitsInSmiLayout * 2, descriptors,
1376 5 : kPropsCount);
1377 :
1378 5 : TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
1379 5 : }
1380 :
1381 :
1382 26644 : TEST(LayoutDescriptorSharing) {
1383 5 : CcTest::InitializeVM();
1384 10 : v8::HandleScope scope(CcTest::isolate());
1385 : Isolate* isolate = CcTest::i_isolate();
1386 5 : Handle<FieldType> any_type = FieldType::Any(isolate);
1387 :
1388 : Handle<Map> split_map;
1389 : {
1390 5 : Handle<Map> map = Map::Create(isolate, 64);
1391 325 : for (int i = 0; i < 32; i++) {
1392 160 : Handle<String> name = MakeName("prop", i);
1393 320 : map = Map::CopyWithField(isolate, map, name, any_type, NONE,
1394 : PropertyConstness::kMutable,
1395 160 : Representation::Smi(), INSERT_TRANSITION)
1396 160 : .ToHandleChecked();
1397 : }
1398 10 : split_map = Map::CopyWithField(isolate, map, MakeString("dbl"), any_type,
1399 : NONE, PropertyConstness::kMutable,
1400 5 : Representation::Double(), INSERT_TRANSITION)
1401 : .ToHandleChecked();
1402 : }
1403 : Handle<LayoutDescriptor> split_layout_descriptor(
1404 : split_map->layout_descriptor(), isolate);
1405 5 : CHECK(split_layout_descriptor->IsConsistentWithMap(*split_map, true));
1406 5 : CHECK(split_layout_descriptor->IsSlowLayout());
1407 5 : CHECK(split_map->owns_descriptors());
1408 :
1409 : Handle<Map> map1 =
1410 10 : Map::CopyWithField(isolate, split_map, MakeString("foo"), any_type, NONE,
1411 : PropertyConstness::kMutable, Representation::Double(),
1412 5 : INSERT_TRANSITION)
1413 : .ToHandleChecked();
1414 5 : CHECK(!split_map->owns_descriptors());
1415 5 : CHECK_EQ(*split_layout_descriptor, split_map->layout_descriptor());
1416 :
1417 : // Layout descriptors should be shared with |split_map|.
1418 5 : CHECK(map1->owns_descriptors());
1419 5 : CHECK_EQ(*split_layout_descriptor, map1->layout_descriptor());
1420 5 : CHECK(map1->layout_descriptor()->IsConsistentWithMap(*map1, true));
1421 :
1422 : Handle<Map> map2 =
1423 10 : Map::CopyWithField(isolate, split_map, MakeString("bar"), any_type, NONE,
1424 : PropertyConstness::kMutable, Representation::Tagged(),
1425 5 : INSERT_TRANSITION)
1426 : .ToHandleChecked();
1427 :
1428 : // Layout descriptors should not be shared with |split_map|.
1429 5 : CHECK(map2->owns_descriptors());
1430 5 : CHECK_NE(*split_layout_descriptor, map2->layout_descriptor());
1431 5 : CHECK(map2->layout_descriptor()->IsConsistentWithMap(*map2, true));
1432 5 : }
1433 :
1434 :
1435 5 : static void TestWriteBarrier(Handle<Map> map, Handle<Map> new_map,
1436 : int tagged_descriptor, int double_descriptor,
1437 : bool check_tagged_value = true) {
1438 5 : FLAG_stress_compaction = true;
1439 5 : FLAG_manual_evacuation_candidates_selection = true;
1440 : Isolate* isolate = CcTest::i_isolate();
1441 : Factory* factory = isolate->factory();
1442 5 : Heap* heap = CcTest::heap();
1443 : PagedSpace* old_space = heap->old_space();
1444 :
1445 : // The plan: create |obj| by |map| in old space, create |obj_value| in
1446 : // new space and ensure that write barrier is triggered when |obj_value| is
1447 : // written to property |tagged_descriptor| of |obj|.
1448 : // Then migrate object to |new_map| and set proper value for property
1449 : // |double_descriptor|. Call GC and ensure that it did not crash during
1450 : // store buffer entries updating.
1451 :
1452 : Handle<JSObject> obj;
1453 : Handle<HeapObject> obj_value;
1454 : {
1455 : AlwaysAllocateScope always_allocate(isolate);
1456 5 : obj = factory->NewJSObjectFromMap(map, AllocationType::kOld);
1457 5 : CHECK(old_space->Contains(*obj));
1458 :
1459 5 : obj_value = factory->NewHeapNumber(0.);
1460 : }
1461 :
1462 5 : CHECK(Heap::InYoungGeneration(*obj_value));
1463 :
1464 : {
1465 5 : FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor);
1466 : const int n = 153;
1467 1535 : for (int i = 0; i < n; i++) {
1468 1530 : obj->FastPropertyAtPut(index, *obj_value);
1469 : }
1470 : }
1471 :
1472 : // Migrate |obj| to |new_map| which should shift fields and put the
1473 : // |boom_value| to the slot that was earlier recorded by write barrier.
1474 5 : JSObject::MigrateToMap(obj, new_map);
1475 :
1476 5 : Address fake_object = obj_value->ptr() + kTaggedSize;
1477 : uint64_t boom_value = bit_cast<uint64_t>(fake_object);
1478 :
1479 : FieldIndex double_field_index =
1480 5 : FieldIndex::ForDescriptor(*new_map, double_descriptor);
1481 5 : CHECK(obj->IsUnboxedDoubleField(double_field_index));
1482 : obj->RawFastDoublePropertyAsBitsAtPut(double_field_index, boom_value);
1483 :
1484 : // Trigger GC to evacuate all candidates.
1485 5 : CcTest::CollectGarbage(NEW_SPACE);
1486 :
1487 5 : if (check_tagged_value) {
1488 : FieldIndex tagged_field_index =
1489 5 : FieldIndex::ForDescriptor(*new_map, tagged_descriptor);
1490 10 : CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index));
1491 : }
1492 5 : CHECK_EQ(boom_value, obj->RawFastDoublePropertyAsBitsAt(double_field_index));
1493 5 : }
1494 :
1495 :
1496 5 : static void TestIncrementalWriteBarrier(Handle<Map> map, Handle<Map> new_map,
1497 : int tagged_descriptor,
1498 : int double_descriptor,
1499 : bool check_tagged_value = true) {
1500 5 : if (FLAG_never_compact || !FLAG_incremental_marking) return;
1501 : ManualGCScope manual_gc_scope;
1502 5 : FLAG_manual_evacuation_candidates_selection = true;
1503 : Isolate* isolate = CcTest::i_isolate();
1504 : Factory* factory = isolate->factory();
1505 5 : Heap* heap = CcTest::heap();
1506 : PagedSpace* old_space = heap->old_space();
1507 :
1508 : // The plan: create |obj| by |map| in old space, create |obj_value| in
1509 : // old space and ensure it end up in evacuation candidate page. Start
1510 : // incremental marking and ensure that incremental write barrier is triggered
1511 : // when |obj_value| is written to property |tagged_descriptor| of |obj|.
1512 : // Then migrate object to |new_map| and set proper value for property
1513 : // |double_descriptor|. Call GC and ensure that it did not crash during
1514 : // slots buffer entries updating.
1515 :
1516 : Handle<JSObject> obj;
1517 : Handle<HeapObject> obj_value;
1518 : Page* ec_page;
1519 : {
1520 : AlwaysAllocateScope always_allocate(isolate);
1521 5 : obj = factory->NewJSObjectFromMap(map, AllocationType::kOld);
1522 5 : CHECK(old_space->Contains(*obj));
1523 :
1524 : // Make sure |obj_value| is placed on an old-space evacuation candidate.
1525 5 : heap::SimulateFullSpace(old_space);
1526 : obj_value =
1527 : factory->NewJSArray(32 * KB, HOLEY_ELEMENTS, AllocationType::kOld);
1528 : ec_page = Page::FromHeapObject(*obj_value);
1529 5 : CHECK_NE(ec_page, Page::FromHeapObject(*obj));
1530 : }
1531 :
1532 : // Heap is ready, force |ec_page| to become an evacuation candidate and
1533 : // simulate incremental marking.
1534 5 : heap::ForceEvacuationCandidate(ec_page);
1535 5 : heap::SimulateIncrementalMarking(heap);
1536 :
1537 : // Check that everything is ready for triggering incremental write barrier
1538 : // (i.e. that both |obj| and |obj_value| are black and the marking phase is
1539 : // still active and |obj_value|'s page is indeed an evacuation candidate).
1540 : IncrementalMarking* marking = heap->incremental_marking();
1541 5 : CHECK(marking->IsMarking());
1542 : IncrementalMarking::MarkingState* marking_state = marking->marking_state();
1543 5 : CHECK(marking_state->IsBlack(*obj));
1544 5 : CHECK(marking_state->IsBlack(*obj_value));
1545 5 : CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1546 :
1547 : // Trigger incremental write barrier, which should add a slot to remembered
1548 : // set.
1549 : {
1550 5 : FieldIndex index = FieldIndex::ForDescriptor(*map, tagged_descriptor);
1551 10 : obj->FastPropertyAtPut(index, *obj_value);
1552 : }
1553 :
1554 : // Migrate |obj| to |new_map| which should shift fields and put the
1555 : // |boom_value| to the slot that was earlier recorded by incremental write
1556 : // barrier.
1557 5 : JSObject::MigrateToMap(obj, new_map);
1558 :
1559 : uint64_t boom_value = UINT64_C(0xBAAD0176A37C28E1);
1560 :
1561 : FieldIndex double_field_index =
1562 5 : FieldIndex::ForDescriptor(*new_map, double_descriptor);
1563 5 : CHECK(obj->IsUnboxedDoubleField(double_field_index));
1564 : obj->RawFastDoublePropertyAsBitsAtPut(double_field_index, boom_value);
1565 :
1566 : // Trigger GC to evacuate all candidates.
1567 5 : CcTest::CollectGarbage(OLD_SPACE);
1568 :
1569 : // Ensure that the values are still there and correct.
1570 5 : CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(*obj_value));
1571 :
1572 5 : if (check_tagged_value) {
1573 : FieldIndex tagged_field_index =
1574 5 : FieldIndex::ForDescriptor(*new_map, tagged_descriptor);
1575 10 : CHECK_EQ(*obj_value, obj->RawFastPropertyAt(tagged_field_index));
1576 : }
1577 5 : CHECK_EQ(boom_value, obj->RawFastDoublePropertyAsBitsAt(double_field_index));
1578 : }
1579 :
1580 : enum OldToWriteBarrierKind {
1581 : OLD_TO_OLD_WRITE_BARRIER,
1582 : OLD_TO_NEW_WRITE_BARRIER
1583 : };
1584 10 : static void TestWriteBarrierObjectShiftFieldsRight(
1585 : OldToWriteBarrierKind write_barrier_kind) {
1586 : ManualGCScope manual_gc_scope;
1587 10 : CcTest::InitializeVM();
1588 : Isolate* isolate = CcTest::i_isolate();
1589 20 : v8::HandleScope scope(CcTest::isolate());
1590 :
1591 10 : Handle<FieldType> any_type = FieldType::Any(isolate);
1592 :
1593 : CompileRun("function func() { return 1; }");
1594 :
1595 10 : Handle<JSObject> func = GetObject("func");
1596 :
1597 10 : Handle<Map> map = Map::Create(isolate, 10);
1598 20 : map = Map::CopyWithConstant(isolate, map, MakeName("prop", 0), func, NONE,
1599 20 : INSERT_TRANSITION)
1600 10 : .ToHandleChecked();
1601 20 : map = Map::CopyWithField(isolate, map, MakeName("prop", 1), any_type, NONE,
1602 : PropertyConstness::kMutable,
1603 20 : Representation::Double(), INSERT_TRANSITION)
1604 10 : .ToHandleChecked();
1605 20 : map = Map::CopyWithField(isolate, map, MakeName("prop", 2), any_type, NONE,
1606 : PropertyConstness::kMutable,
1607 20 : Representation::Tagged(), INSERT_TRANSITION)
1608 10 : .ToHandleChecked();
1609 :
1610 : // Shift fields right by turning constant property to a field.
1611 : Handle<Map> new_map = Map::ReconfigureProperty(
1612 10 : isolate, map, 0, kData, NONE, Representation::Tagged(), any_type);
1613 :
1614 10 : if (write_barrier_kind == OLD_TO_NEW_WRITE_BARRIER) {
1615 5 : TestWriteBarrier(map, new_map, 2, 1);
1616 : } else {
1617 5 : CHECK_EQ(OLD_TO_OLD_WRITE_BARRIER, write_barrier_kind);
1618 5 : TestIncrementalWriteBarrier(map, new_map, 2, 1);
1619 : }
1620 10 : }
1621 :
1622 26644 : TEST(WriteBarrierObjectShiftFieldsRight) {
1623 5 : TestWriteBarrierObjectShiftFieldsRight(OLD_TO_NEW_WRITE_BARRIER);
1624 5 : }
1625 :
1626 :
1627 26644 : TEST(IncrementalWriteBarrierObjectShiftFieldsRight) {
1628 5 : TestWriteBarrierObjectShiftFieldsRight(OLD_TO_OLD_WRITE_BARRIER);
1629 5 : }
1630 :
1631 :
1632 : // TODO(ishell): add respective tests for property kind reconfiguring from
1633 : // accessor field to double, once accessor fields are supported by
1634 : // Map::ReconfigureProperty().
1635 :
1636 :
1637 : // TODO(ishell): add respective tests for fast property removal case once
1638 : // Map::ReconfigureProperty() supports that.
1639 :
1640 : #endif
1641 :
1642 : } // namespace test_unboxed_doubles
1643 : } // namespace internal
1644 79917 : } // namespace v8
|