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