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