#include "src/v8.h"
+#include "src/base/bits.h"
#include "src/layout-descriptor.h"
+using v8::base::bits::CountTrailingZeros32;
+
namespace v8 {
namespace internal {
return new_layout_descriptor;
}
}
+
+
+bool LayoutDescriptor::IsTagged(int field_index, int max_sequence_length,
+ int* out_sequence_length) {
+ DCHECK(max_sequence_length > 0);
+ if (IsFastPointerLayout()) {
+ *out_sequence_length = max_sequence_length;
+ return true;
+ }
+
+ int layout_word_index;
+ int layout_bit_index;
+
+ if (!GetIndexes(field_index, &layout_word_index, &layout_bit_index)) {
+ // Out of bounds queries are considered tagged.
+ *out_sequence_length = max_sequence_length;
+ return true;
+ }
+ uint32_t layout_mask = static_cast<uint32_t>(1) << layout_bit_index;
+
+ uint32_t value = IsSlowLayout()
+ ? get_scalar(layout_word_index)
+ : static_cast<uint32_t>(Smi::cast(this)->value());
+
+ bool is_tagged = (value & layout_mask) == 0;
+ if (!is_tagged) value = ~value; // Count set bits instead of cleared bits.
+ value = value & ~(layout_mask - 1); // Clear bits we are not interested in.
+ int sequence_length = CountTrailingZeros32(value) - layout_bit_index;
+
+ if (layout_bit_index + sequence_length == kNumberOfBits) {
+ // This is a contiguous sequence till the end of current word, proceed
+ // counting in the subsequent words.
+ if (IsSlowLayout()) {
+ int len = length();
+ ++layout_word_index;
+ for (; layout_word_index < len; layout_word_index++) {
+ value = get_scalar(layout_word_index);
+ bool cur_is_tagged = (value & 1) == 0;
+ if (cur_is_tagged != is_tagged) break;
+ if (!is_tagged) value = ~value; // Count set bits instead.
+ int cur_sequence_length = CountTrailingZeros32(value);
+ sequence_length += cur_sequence_length;
+ if (sequence_length >= max_sequence_length) break;
+ if (cur_sequence_length != kNumberOfBits) break;
+ }
+ }
+ if (is_tagged && (field_index + sequence_length == capacity())) {
+ // The contiguous sequence of tagged fields lasts till the end of the
+ // layout descriptor which means that all the fields starting from
+ // field_index are tagged.
+ sequence_length = std::numeric_limits<int>::max();
+ }
+ }
+ *out_sequence_length = Min(sequence_length, max_sequence_length);
+ return is_tagged;
+}
+
+
+Handle<LayoutDescriptor> LayoutDescriptor::NewForTesting(Isolate* isolate,
+ int length) {
+ return New(isolate, length);
+}
+
+
+LayoutDescriptor* LayoutDescriptor::SetTaggedForTesting(int field_index,
+ bool tagged) {
+ return SetTagged(field_index, tagged);
+}
+
+
+bool LayoutDescriptorHelper::IsTagged(
+ int offset_in_bytes, int end_offset,
+ int* out_end_of_contiguous_region_offset) {
+ DCHECK(IsAligned(offset_in_bytes, kPointerSize));
+ DCHECK(IsAligned(end_offset, kPointerSize));
+ DCHECK(offset_in_bytes < end_offset);
+ if (all_fields_tagged_) {
+ *out_end_of_contiguous_region_offset = end_offset;
+ DCHECK(offset_in_bytes < *out_end_of_contiguous_region_offset);
+ return true;
+ }
+ int max_sequence_length = (end_offset - offset_in_bytes) / kPointerSize;
+ int field_index = Max(0, (offset_in_bytes - header_size_) / kPointerSize);
+ int sequence_length;
+ bool tagged = layout_descriptor_->IsTagged(field_index, max_sequence_length,
+ &sequence_length);
+ DCHECK(sequence_length > 0);
+ if (offset_in_bytes < header_size_) {
+ // Object headers do not contain non-tagged fields. Check if the contiguous
+ // region continues after the header.
+ if (tagged) {
+ // First field is tagged, calculate end offset from there.
+ *out_end_of_contiguous_region_offset =
+ header_size_ + sequence_length * kPointerSize;
+
+ } else {
+ *out_end_of_contiguous_region_offset = header_size_;
+ }
+ DCHECK(offset_in_bytes < *out_end_of_contiguous_region_offset);
+ return true;
+ }
+ *out_end_of_contiguous_region_offset =
+ offset_in_bytes + sequence_length * kPointerSize;
+ DCHECK(offset_in_bytes < *out_end_of_contiguous_region_offset);
+ return tagged;
+}
}
} // namespace v8::internal
}
}
+const int kNumberOfBits = 32;
+
enum TestPropertyKind {
PROP_CONSTANT,
CHECK_EQ(true, layout_desc->IsTagged(i));
}
CHECK(layout_desc->IsFastPointerLayout());
+
+ int sequence_length;
+ CHECK_EQ(true, layout_desc->IsTagged(0, std::numeric_limits<int>::max(),
+ &sequence_length));
+ CHECK_EQ(std::numeric_limits<int>::max(), sequence_length);
+
+ CHECK_EQ(true, layout_desc->IsTagged(0, 7, &sequence_length));
+ CHECK_EQ(7, sequence_length);
}
}
+static void TestLayoutDescriptorQueries(int layout_descriptor_length,
+ int* bit_flip_positions,
+ int max_sequence_length) {
+ Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::NewForTesting(
+ CcTest::i_isolate(), layout_descriptor_length);
+ layout_descriptor_length = layout_descriptor->capacity();
+ LayoutDescriptor* layout_desc = *layout_descriptor;
+
+ {
+ // Fill in the layout descriptor.
+ int cur_bit_flip_index = 0;
+ bool tagged = true;
+ for (int i = 0; i < layout_descriptor_length; i++) {
+ if (i == bit_flip_positions[cur_bit_flip_index]) {
+ tagged = !tagged;
+ ++cur_bit_flip_index;
+ CHECK(i < bit_flip_positions[cur_bit_flip_index]); // check test data
+ }
+ layout_desc = layout_desc->SetTaggedForTesting(i, tagged);
+ }
+ }
+
+ if (layout_desc->IsFastPointerLayout()) {
+ return;
+ }
+
+ {
+ // Check queries.
+ int cur_bit_flip_index = 0;
+ bool tagged = true;
+ for (int i = 0; i < layout_descriptor_length; i++) {
+ if (i == bit_flip_positions[cur_bit_flip_index]) {
+ tagged = !tagged;
+ ++cur_bit_flip_index;
+ }
+ CHECK_EQ(tagged, layout_desc->IsTagged(i));
+
+ int next_bit_flip_position = bit_flip_positions[cur_bit_flip_index];
+ int expected_sequence_length;
+ if (next_bit_flip_position < layout_desc->capacity()) {
+ expected_sequence_length = next_bit_flip_position - i;
+ } else {
+ expected_sequence_length = tagged ? std::numeric_limits<int>::max()
+ : (layout_desc->capacity() - i);
+ }
+ expected_sequence_length =
+ Min(expected_sequence_length, max_sequence_length);
+ int sequence_length;
+ CHECK_EQ(tagged,
+ layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
+ CHECK(sequence_length > 0);
+
+ CHECK_EQ(expected_sequence_length, sequence_length);
+ }
+
+ int sequence_length;
+ CHECK_EQ(true,
+ layout_desc->IsTagged(layout_descriptor_length,
+ max_sequence_length, &sequence_length));
+ CHECK_EQ(max_sequence_length, sequence_length);
+ }
+}
+
+
+static void TestLayoutDescriptorQueriesFast(int max_sequence_length) {
+ {
+ LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout();
+ int sequence_length;
+ for (int i = 0; i < kNumberOfBits; i++) {
+ CHECK_EQ(true,
+ layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
+ CHECK(sequence_length > 0);
+ CHECK_EQ(max_sequence_length, sequence_length);
+ }
+ }
+
+ {
+ int bit_flip_positions[] = {1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kNumberOfBits + 1];
+ for (int i = 0; i <= kNumberOfBits; i++) {
+ bit_flip_positions[i] = i;
+ }
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30, 1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 1, 2, 3, 5, 7, 9,
+ 12, 15, 18, 22, 26, 29, 1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+}
+
+
+TEST(LayoutDescriptorQueriesFastLimited7) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesFast(7);
+}
+
+
+TEST(LayoutDescriptorQueriesFastLimited13) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesFast(13);
+}
+
+
+TEST(LayoutDescriptorQueriesFastUnlimited) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesFast(std::numeric_limits<int>::max());
+}
+
+
+static void TestLayoutDescriptorQueriesSlow(int max_sequence_length) {
+ {
+ int bit_flip_positions[] = {10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kMaxNumberOfDescriptors + 1];
+ for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
+ bit_flip_positions[i] = i;
+ }
+ bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30,
+ 37, 54, 80, 99, 383, 10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 10, 20, 30, 50, 70, 90,
+ 120, 150, 180, 220, 260, 290, 10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kMaxNumberOfDescriptors + 1];
+ int cur = 0;
+ for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
+ bit_flip_positions[i] = cur;
+ cur = (cur + 1) * 2;
+ }
+ CHECK(cur < 10000);
+ bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kMaxNumberOfDescriptors + 1];
+ int cur = 3;
+ for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
+ bit_flip_positions[i] = cur;
+ cur = (cur + 1) * 2;
+ }
+ CHECK(cur < 10000);
+ bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+}
+
+
+TEST(LayoutDescriptorQueriesSlowLimited7) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(7);
+}
+
+
+TEST(LayoutDescriptorQueriesSlowLimited13) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(13);
+}
+
+
+TEST(LayoutDescriptorQueriesSlowLimited42) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(42);
+}
+
+
+TEST(LayoutDescriptorQueriesSlowUnlimited) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(std::numeric_limits<int>::max());
+}
+
+
TEST(LayoutDescriptorCreateNewFast) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
}
+static void TestLayoutDescriptorHelper(Isolate* isolate,
+ int inobject_properties,
+ Handle<DescriptorArray> descriptors,
+ int number_of_descriptors) {
+ Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+ Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New(
+ map, descriptors, descriptors->number_of_descriptors());
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+
+ LayoutDescriptorHelper helper(*map);
+ bool all_fields_tagged = true;
+
+ int instance_size = map->instance_size();
+
+ int end_offset = instance_size * 2;
+ int first_non_tagged_field_offset = end_offset;
+ for (int i = 0; i < number_of_descriptors; i++) {
+ PropertyDetails details = descriptors->GetDetails(i);
+ if (details.type() != FIELD) continue;
+ FieldIndex index = FieldIndex::ForDescriptor(*map, i);
+ if (!index.is_inobject()) continue;
+ all_fields_tagged &= !details.representation().IsDouble();
+ bool expected_tagged = !index.is_double();
+ if (!expected_tagged) {
+ first_non_tagged_field_offset =
+ Min(first_non_tagged_field_offset, index.offset());
+ }
+
+ int end_of_region_offset;
+ CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
+ CHECK_EQ(expected_tagged, helper.IsTagged(index.offset(), instance_size,
+ &end_of_region_offset));
+ CHECK(end_of_region_offset > 0);
+ CHECK(end_of_region_offset % kPointerSize == 0);
+ CHECK(end_of_region_offset <= instance_size);
+
+ for (int offset = index.offset(); offset < end_of_region_offset;
+ offset += kPointerSize) {
+ CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
+ }
+ if (end_of_region_offset < instance_size) {
+ CHECK_EQ(!expected_tagged, helper.IsTagged(end_of_region_offset));
+ } else {
+ CHECK_EQ(true, helper.IsTagged(end_of_region_offset));
+ }
+ }
+
+ for (int offset = 0; offset < JSObject::kHeaderSize; offset += kPointerSize) {
+ // Header queries
+ CHECK_EQ(true, helper.IsTagged(offset));
+ int end_of_region_offset;
+ CHECK_EQ(true, helper.IsTagged(offset, end_offset, &end_of_region_offset));
+ CHECK_EQ(first_non_tagged_field_offset, end_of_region_offset);
+
+ // Out of bounds queries
+ CHECK_EQ(true, helper.IsTagged(offset + instance_size));
+ }
+
+ CHECK_EQ(all_fields_tagged, helper.all_fields_tagged());
+}
+
+
+TEST(LayoutDescriptorHelperMixed) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
+ kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
+}
+
+
+TEST(LayoutDescriptorHelperAllTagged) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = PROP_TAGGED;
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
+ kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
+}
+
+
+TEST(LayoutDescriptorHelperAllDoubles) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = PROP_DOUBLE;
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
+ kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
+}
+
+
TEST(StoreBufferScanOnScavenge) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();