In-object double fields unboxing (for 64-bit only).
authorishell@chromium.org <ishell@chromium.org>
Mon, 10 Nov 2014 16:34:30 +0000 (16:34 +0000)
committerishell@chromium.org <ishell@chromium.org>
Mon, 10 Nov 2014 16:35:22 +0000 (16:35 +0000)
This CL introduces LayoutDescriptor which is responsible for tracking which in-object fields are tagged and which are not.
LayoutDescriptor field added to Map. Currently unboxing is disabled.

R=hpayer@chromium.org, verwaest@chromium.org

Review URL: https://codereview.chromium.org/391693002

Cr-Commit-Position: refs/heads/master@{#25250}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25250 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

32 files changed:
BUILD.gn
src/arm64/lithium-arm64.cc
src/arm64/lithium-codegen-arm64.cc
src/bootstrapper.cc
src/code-stubs-hydrogen.cc
src/field-index.h
src/flag-definitions.h
src/globals.h
src/heap/heap.cc
src/heap/mark-compact.cc
src/heap/objects-visiting.cc
src/heap/objects-visiting.h
src/heap/store-buffer.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/json-stringifier.h
src/layout-descriptor-inl.h [new file with mode: 0644]
src/layout-descriptor.cc [new file with mode: 0644]
src/layout-descriptor.h [new file with mode: 0644]
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
src/property-details.h
src/runtime/runtime-object.cc
src/string-stream.cc
src/x64/lithium-codegen-x64.cc
test/cctest/cctest.gyp
test/cctest/test-heap.cc
test/cctest/test-unboxed-doubles.cc [new file with mode: 0644]
tools/gyp/v8.gyp

index d77931c7ec5e9bde20b33f9692daaecf67be0078..adeebf3b175c59199f2a7715d14de9af8e5aeec5 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -782,6 +782,9 @@ source_set("v8_base") {
     "src/jsregexp-inl.h",
     "src/jsregexp.cc",
     "src/jsregexp.h",
+    "src/layout-descriptor-inl.h",
+    "src/layout-descriptor.cc",
+    "src/layout-descriptor.h",
     "src/list-inl.h",
     "src/list.h",
     "src/lithium-allocator-inl.h",
index 241bc4b20bfe705aaaa06580c473e367df521fae..ff3d45005f3489074f4d06f5fe291f0179a63ec6 100644 (file)
@@ -2400,7 +2400,7 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
   LOperand* temp1 = NULL;
 
   if (instr->access().IsExternalMemory() ||
-      instr->field_representation().IsDouble()) {
+      (!FLAG_unbox_double_fields && instr->field_representation().IsDouble())) {
     value = UseRegister(instr->value());
   } else if (instr->NeedsWriteBarrier()) {
     value = UseRegisterAndClobber(instr->value());
index 9fe311c939277109a7348a5b7ff48cc4770f31c1..8129470e158cfa719501209d5fa601f20646e56b 100644 (file)
@@ -3660,6 +3660,7 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
   }
 
   if (instr->hydrogen()->representation().IsDouble()) {
+    DCHECK(access.IsInobject());
     FPRegister result = ToDoubleRegister(instr->result());
     __ Ldr(result, FieldMemOperand(object, offset));
     return;
@@ -5350,7 +5351,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
 
   __ AssertNotSmi(object);
 
-  if (representation.IsDouble()) {
+  if (!FLAG_unbox_double_fields && representation.IsDouble()) {
     DCHECK(access.IsInobject());
     DCHECK(!instr->hydrogen()->has_transition());
     DCHECK(!instr->hydrogen()->NeedsWriteBarrier());
@@ -5359,8 +5360,6 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
     return;
   }
 
-  Register value = ToRegister(instr->value());
-
   DCHECK(!representation.IsSmi() ||
          !instr->value()->IsConstantOperand() ||
          IsInteger32Constant(LConstantOperand::cast(instr->value())));
@@ -5392,8 +5391,12 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
     destination = temp0;
   }
 
-  if (representation.IsSmi() &&
-     instr->hydrogen()->value()->representation().IsInteger32()) {
+  if (FLAG_unbox_double_fields && representation.IsDouble()) {
+    DCHECK(access.IsInobject());
+    FPRegister value = ToDoubleRegister(instr->value());
+    __ Str(value, FieldMemOperand(object, offset));
+  } else if (representation.IsSmi() &&
+             instr->hydrogen()->value()->representation().IsInteger32()) {
     DCHECK(instr->hydrogen()->store_mode() == STORE_TO_INITIALIZED_ENTRY);
 #ifdef DEBUG
     Register temp0 = ToRegister(instr->temp0());
@@ -5408,12 +5411,15 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
 #endif
     STATIC_ASSERT(static_cast<unsigned>(kSmiValueSize) == kWRegSizeInBits);
     STATIC_ASSERT(kSmiTag == 0);
+    Register value = ToRegister(instr->value());
     __ Store(value, UntagSmiFieldMemOperand(destination, offset),
              Representation::Integer32());
   } else {
+    Register value = ToRegister(instr->value());
     __ Store(value, FieldMemOperand(destination, offset), representation);
   }
   if (instr->hydrogen()->NeedsWriteBarrier()) {
+    Register value = ToRegister(instr->value());
     __ RecordWriteField(destination,
                         offset,
                         value,                        // Clobbered.
index 6f37a94ac97e00ce92dec4c64fac04bbbdd849cb..3f9dd3ca3433798a1b86bbb9d2c2e44e2f6b9509 100644 (file)
@@ -951,7 +951,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
       CallbacksDescriptor d(
           Handle<Name>(Name::cast(array_length->name())),
           array_length, attribs);
-      array_function->initial_map()->AppendDescriptor(&d);
+      initial_map->AppendDescriptor(&d);
     }
 
     // array_function is used internally. JS code creating array object should
@@ -1091,7 +1091,6 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
     initial_map->set_unused_property_fields(0);
     initial_map->set_instance_size(
         initial_map->instance_size() + 5 * kPointerSize);
-    initial_map->set_visitor_id(StaticVisitorBase::GetVisitorId(*initial_map));
 
     // RegExp prototype object is itself a RegExp.
     Handle<Map> proto_map = Map::Copy(initial_map, "RegExpPrototype");
@@ -1678,7 +1677,7 @@ Handle<JSFunction> Genesis::InstallInternalArray(
   {  // Add length.
     CallbacksDescriptor d(
         Handle<Name>(Name::cast(array_length->name())), array_length, attribs);
-    array_function->initial_map()->AppendDescriptor(&d);
+    initial_map->AppendDescriptor(&d);
   }
 
   return array_function;
index dc527d7d70ca7f2812c65a9cacc6337e0b2dfda8..45d1417b0de1d5b2e50c0d9b64eb3af0454bb695 100644 (file)
@@ -538,7 +538,8 @@ HLoadNamedField* CodeStubGraphBuilderBase::BuildLoadNamedField(
   HObjectAccess access = index.is_inobject()
       ? HObjectAccess::ForObservableJSObjectOffset(offset, representation)
       : HObjectAccess::ForBackingStoreOffset(offset, representation);
-  if (index.is_double()) {
+  if (index.is_double() &&
+      (!FLAG_unbox_double_fields || !index.is_inobject())) {
     // Load the heap number.
     object = Add<HLoadNamedField>(
         object, static_cast<HValue*>(NULL),
@@ -705,30 +706,32 @@ void CodeStubGraphBuilderBase::BuildStoreNamedField(
           : HObjectAccess::ForBackingStoreOffset(offset, representation);
 
   if (representation.IsDouble()) {
-    HObjectAccess heap_number_access =
-        access.WithRepresentation(Representation::Tagged());
-    if (transition_to_field) {
-      // The store requires a mutable HeapNumber to be allocated.
-      NoObservableSideEffectsScope no_side_effects(this);
-      HInstruction* heap_number_size = Add<HConstant>(HeapNumber::kSize);
-
-      // TODO(hpayer): Allocation site pretenuring support.
-      HInstruction* heap_number =
-          Add<HAllocate>(heap_number_size, HType::HeapObject(), NOT_TENURED,
-                         MUTABLE_HEAP_NUMBER_TYPE);
-      AddStoreMapConstant(heap_number,
-                          isolate()->factory()->mutable_heap_number_map());
-      Add<HStoreNamedField>(heap_number, HObjectAccess::ForHeapNumberValue(),
-                            value);
-      // Store the new mutable heap number into the object.
-      access = heap_number_access;
-      value = heap_number;
-    } else {
-      // Load the heap number.
-      object = Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
-                                    heap_number_access);
-      // Store the double value into it.
-      access = HObjectAccess::ForHeapNumberValue();
+    if (!FLAG_unbox_double_fields || !index.is_inobject()) {
+      HObjectAccess heap_number_access =
+          access.WithRepresentation(Representation::Tagged());
+      if (transition_to_field) {
+        // The store requires a mutable HeapNumber to be allocated.
+        NoObservableSideEffectsScope no_side_effects(this);
+        HInstruction* heap_number_size = Add<HConstant>(HeapNumber::kSize);
+
+        // TODO(hpayer): Allocation site pretenuring support.
+        HInstruction* heap_number =
+            Add<HAllocate>(heap_number_size, HType::HeapObject(), NOT_TENURED,
+                           MUTABLE_HEAP_NUMBER_TYPE);
+        AddStoreMapConstant(heap_number,
+                            isolate()->factory()->mutable_heap_number_map());
+        Add<HStoreNamedField>(heap_number, HObjectAccess::ForHeapNumberValue(),
+                              value);
+        // Store the new mutable heap number into the object.
+        access = heap_number_access;
+        value = heap_number;
+      } else {
+        // Load the heap number.
+        object = Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
+                                      heap_number_access);
+        // Store the double value into it.
+        access = HObjectAccess::ForHeapNumberValue();
+      }
     }
   } else if (representation.IsHeapObject()) {
     BuildCheckHeapObject(value);
index 2558529070b8a09a787208dc897486d0d4d69d27..76b111632d4bb39c236ba5bf8a50f540a80fa0f4 100644 (file)
@@ -34,6 +34,8 @@ class FieldIndex FINAL {
     return IsInObjectBits::decode(bit_field_);
   }
 
+  bool is_hidden_field() const { return IsHiddenField::decode(bit_field_); }
+
   bool is_double() const {
     return IsDoubleBits::decode(bit_field_);
   }
@@ -55,7 +57,7 @@ class FieldIndex FINAL {
   // Zero-based from the first inobject property. Overflows to out-of-object
   // properties.
   int property_index() const {
-    DCHECK(!IsHiddenField::decode(bit_field_));
+    DCHECK(!is_hidden_field());
     int result = index() - first_inobject_property_offset() / kPointerSize;
     if (!is_inobject()) {
       result += InObjectPropertyBits::decode(bit_field_);
@@ -86,7 +88,7 @@ class FieldIndex FINAL {
   explicit FieldIndex(int bit_field) : bit_field_(bit_field) {}
 
   int first_inobject_property_offset() const {
-    DCHECK(!IsHiddenField::decode(bit_field_));
+    DCHECK(!is_hidden_field());
     return FirstInobjectPropertyOffsetBits::decode(bit_field_);
   }
 
index 5f2a67285093ffb0cb97cc4bec074cf292f9f2ac..49d44746e8297ed46c730cd50371a6a509985660 100644 (file)
@@ -947,6 +947,11 @@ DEFINE_INT(dump_allocations_digest_at_alloc, 0,
 DEFINE_BOOL(enable_ool_constant_pool, V8_OOL_CONSTANT_POOL,
             "enable use of out-of-line constant pools (ARM only)")
 
+DEFINE_BOOL(unbox_double_fields, V8_DOUBLE_FIELDS_UNBOXING,
+            "enable in-object double fields unboxing (64-bit only)")
+DEFINE_IMPLICATION(unbox_double_fields, track_double_fields)
+
+
 // Cleanup...
 #undef FLAG_FULL
 #undef FLAG_READONLY
index 7fa43174d4a857a9895a753e0a579dc1e3998f07..dd49ef3b17fcf8c5d55df5ad689d887cfdeb60dc 100644 (file)
@@ -81,6 +81,13 @@ namespace internal {
 #endif
 
 
+// Determine whether double field unboxing feature is enabled.
+#if (V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64)
+#define V8_DOUBLE_FIELDS_UNBOXING 0
+#else
+#define V8_DOUBLE_FIELDS_UNBOXING 0
+#endif
+
 // Support for alternative bool type. This is only enabled if the code is
 // compiled with USE_MYBOOL defined. This catches some nasty type bugs.
 // For instance, 'bool b = "false";' results in b == true! This is a hidden
index 9c57ea3bdb2a0ad16d97d0595c954aa37d20a097..12898d347bb0970f16f65e91442dd1e269b058ae 100644 (file)
@@ -2308,8 +2308,13 @@ AllocationResult Heap::AllocatePartialMap(InstanceType instance_type,
   reinterpret_cast<Map*>(result)->set_map(raw_unchecked_meta_map());
   reinterpret_cast<Map*>(result)->set_instance_type(instance_type);
   reinterpret_cast<Map*>(result)->set_instance_size(instance_size);
+  // Initialize to only containing tagged fields.
   reinterpret_cast<Map*>(result)->set_visitor_id(
-      StaticVisitorBase::GetVisitorId(instance_type, instance_size));
+      StaticVisitorBase::GetVisitorId(instance_type, instance_size, false));
+  if (FLAG_unbox_double_fields) {
+    reinterpret_cast<Map*>(result)
+        ->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
+  }
   reinterpret_cast<Map*>(result)->set_inobject_properties(0);
   reinterpret_cast<Map*>(result)->set_pre_allocated_property_fields(0);
   reinterpret_cast<Map*>(result)->set_unused_property_fields(0);
@@ -2332,8 +2337,6 @@ AllocationResult Heap::AllocateMap(InstanceType instance_type,
   result->set_map_no_write_barrier(meta_map());
   Map* map = Map::cast(result);
   map->set_instance_type(instance_type);
-  map->set_visitor_id(
-      StaticVisitorBase::GetVisitorId(instance_type, instance_size));
   map->set_prototype(null_value(), SKIP_WRITE_BARRIER);
   map->set_constructor(null_value(), SKIP_WRITE_BARRIER);
   map->set_instance_size(instance_size);
@@ -2345,6 +2348,12 @@ AllocationResult Heap::AllocateMap(InstanceType instance_type,
   map->init_back_pointer(undefined_value());
   map->set_unused_property_fields(0);
   map->set_instance_descriptors(empty_descriptor_array());
+  if (FLAG_unbox_double_fields) {
+    map->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
+  }
+  // Must be called only after |instance_type|, |instance_size| and
+  // |layout_descriptor| are set.
+  map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
   map->set_bit_field(0);
   map->set_bit_field2(1 << Map::kIsExtensible);
   int bit_field3 = Map::EnumLengthBits::encode(kInvalidEnumCacheSentinel) |
@@ -2471,28 +2480,46 @@ bool Heap::CreateInitialMaps() {
   meta_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
   meta_map()->init_back_pointer(undefined_value());
   meta_map()->set_instance_descriptors(empty_descriptor_array());
+  if (FLAG_unbox_double_fields) {
+    meta_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
+  }
 
   fixed_array_map()->set_code_cache(empty_fixed_array());
   fixed_array_map()->set_dependent_code(
       DependentCode::cast(empty_fixed_array()));
   fixed_array_map()->init_back_pointer(undefined_value());
   fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
+  if (FLAG_unbox_double_fields) {
+    fixed_array_map()->set_layout_descriptor(
+        LayoutDescriptor::FastPointerLayout());
+  }
 
   undefined_map()->set_code_cache(empty_fixed_array());
   undefined_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
   undefined_map()->init_back_pointer(undefined_value());
   undefined_map()->set_instance_descriptors(empty_descriptor_array());
+  if (FLAG_unbox_double_fields) {
+    undefined_map()->set_layout_descriptor(
+        LayoutDescriptor::FastPointerLayout());
+  }
 
   null_map()->set_code_cache(empty_fixed_array());
   null_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
   null_map()->init_back_pointer(undefined_value());
   null_map()->set_instance_descriptors(empty_descriptor_array());
+  if (FLAG_unbox_double_fields) {
+    null_map()->set_layout_descriptor(LayoutDescriptor::FastPointerLayout());
+  }
 
   constant_pool_array_map()->set_code_cache(empty_fixed_array());
   constant_pool_array_map()->set_dependent_code(
       DependentCode::cast(empty_fixed_array()));
   constant_pool_array_map()->init_back_pointer(undefined_value());
   constant_pool_array_map()->set_instance_descriptors(empty_descriptor_array());
+  if (FLAG_unbox_double_fields) {
+    constant_pool_array_map()->set_layout_descriptor(
+        LayoutDescriptor::FastPointerLayout());
+  }
 
   // Fix prototype object for existing maps.
   meta_map()->set_prototype(null_value());
index 2cefebf980ea1625a81e9e097623c68f12320363..1baae147d48e8082d0f4182d2d44562d68c9f55b 100644 (file)
@@ -2810,12 +2810,23 @@ void MarkCompactCollector::MigrateObject(HeapObject* dst, HeapObject* src,
     Address dst_slot = dst_addr;
     DCHECK(IsAligned(size, kPointerSize));
 
+    bool may_contain_raw_values = src->MayContainRawValues();
+#if V8_DOUBLE_FIELDS_UNBOXING
+    InobjectPropertiesHelper helper(src->map());
+    bool has_only_tagged_fields = helper.all_fields_tagged();
+#endif
     for (int remaining = size / kPointerSize; remaining > 0; remaining--) {
       Object* value = Memory::Object_at(src_slot);
 
       Memory::Object_at(dst_slot) = value;
 
-      if (!src->MayContainRawValues()) {
+#if V8_DOUBLE_FIELDS_UNBOXING
+      if (!may_contain_raw_values &&
+          (has_only_tagged_fields || helper.IsTagged(src_slot - src_addr)))
+#else
+      if (!may_contain_raw_values)
+#endif
+      {
         RecordMigratedSlot(value, dst_slot);
       }
 
index d356917cf7e2be9e5316a6223bedf3ee5c0fa913..20d92de2f9724270d51d2e4bd3c44277d8d44df3 100644 (file)
@@ -11,7 +11,7 @@ namespace internal {
 
 
 StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
-    int instance_type, int instance_size) {
+    int instance_type, int instance_size, bool has_unboxed_fields) {
   if (instance_type < FIRST_NONSTRING_TYPE) {
     switch (instance_type & kStringRepresentationMask) {
       case kSeqStringTag:
@@ -33,7 +33,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
 
       case kExternalStringTag:
         return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric,
-                                   instance_size);
+                                   instance_size, has_unboxed_fields);
     }
     UNREACHABLE();
   }
@@ -74,11 +74,11 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
 
     case JS_SET_TYPE:
       return GetVisitorIdForSize(kVisitStruct, kVisitStructGeneric,
-                                 JSSet::kSize);
+                                 JSSet::kSize, has_unboxed_fields);
 
     case JS_MAP_TYPE:
       return GetVisitorIdForSize(kVisitStruct, kVisitStructGeneric,
-                                 JSMap::kSize);
+                                 JSMap::kSize, has_unboxed_fields);
 
     case JS_WEAK_MAP_TYPE:
     case JS_WEAK_SET_TYPE:
@@ -92,15 +92,15 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
 
     case JS_PROXY_TYPE:
       return GetVisitorIdForSize(kVisitStruct, kVisitStructGeneric,
-                                 JSProxy::kSize);
+                                 JSProxy::kSize, has_unboxed_fields);
 
     case JS_FUNCTION_PROXY_TYPE:
       return GetVisitorIdForSize(kVisitStruct, kVisitStructGeneric,
-                                 JSFunctionProxy::kSize);
+                                 JSFunctionProxy::kSize, has_unboxed_fields);
 
     case FOREIGN_TYPE:
       return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric,
-                                 Foreign::kSize);
+                                 Foreign::kSize, has_unboxed_fields);
 
     case SYMBOL_TYPE:
       return kVisitSymbol;
@@ -131,7 +131,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
     case JS_SET_ITERATOR_TYPE:
     case JS_MAP_ITERATOR_TYPE:
       return GetVisitorIdForSize(kVisitJSObject, kVisitJSObjectGeneric,
-                                 instance_size);
+                                 instance_size, has_unboxed_fields);
 
     case JS_FUNCTION_TYPE:
       return kVisitJSFunction;
@@ -143,7 +143,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
 
       TYPED_ARRAYS(EXTERNAL_ARRAY_CASE)
       return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric,
-                                 instance_size);
+                                 instance_size, has_unboxed_fields);
 #undef EXTERNAL_ARRAY_CASE
 
     case FIXED_UINT8_ARRAY_TYPE:
@@ -167,7 +167,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
       }
 
       return GetVisitorIdForSize(kVisitStruct, kVisitStructGeneric,
-                                 instance_size);
+                                 instance_size, has_unboxed_fields);
 
     default:
       UNREACHABLE();
index db6b892ada05de2ec30dfbdf1b3ffdc1c20c1209..b25b44963627e29d20bbef280b4187b02023b6e8 100644 (file)
@@ -6,6 +6,7 @@
 #define V8_OBJECTS_VISITING_H_
 
 #include "src/allocation.h"
+#include "src/layout-descriptor.h"
 
 // This file provides base classes and auxiliary methods for defining
 // static object visitors used during GC.
@@ -105,21 +106,29 @@ class StaticVisitorBase : public AllStatic {
 
   // Determine which specialized visitor should be used for given instance type
   // and instance type.
-  static VisitorId GetVisitorId(int instance_type, int instance_size);
+  static VisitorId GetVisitorId(int instance_type, int instance_size,
+                                bool has_unboxed_fields);
 
+  // Determine which specialized visitor should be used for given map.
   static VisitorId GetVisitorId(Map* map) {
-    return GetVisitorId(map->instance_type(), map->instance_size());
+    return GetVisitorId(map->instance_type(), map->instance_size(),
+                        FLAG_unbox_double_fields &&
+                            !map->layout_descriptor()->IsFastPointerLayout());
   }
 
   // For visitors that allow specialization by size calculate VisitorId based
   // on size, base visitor id and generic visitor id.
   static VisitorId GetVisitorIdForSize(VisitorId base, VisitorId generic,
-                                       int object_size) {
+                                       int object_size,
+                                       bool has_unboxed_fields) {
     DCHECK((base == kVisitDataObject) || (base == kVisitStruct) ||
            (base == kVisitJSObject));
     DCHECK(IsAligned(object_size, kPointerSize));
     DCHECK(kMinObjectSizeInWords * kPointerSize <= object_size);
     DCHECK(object_size <= Page::kMaxRegularHeapObjectSize);
+    DCHECK(!has_unboxed_fields || (base == kVisitJSObject));
+
+    if (has_unboxed_fields) return generic;
 
     const VisitorId specialization = static_cast<VisitorId>(
         base + (object_size >> kPointerSizeLog2) - kMinObjectSizeInWords);
@@ -158,7 +167,7 @@ class VisitorDispatchTable {
             StaticVisitorBase::VisitorId generic, int object_size_in_words>
   void RegisterSpecialization() {
     static const int size = object_size_in_words * kPointerSize;
-    Register(StaticVisitorBase::GetVisitorIdForSize(base, generic, size),
+    Register(StaticVisitorBase::GetVisitorIdForSize(base, generic, size, false),
              &Visitor::template VisitSpecialized<size>);
   }
 
@@ -189,11 +198,47 @@ class BodyVisitorBase : public AllStatic {
  public:
   INLINE(static void IteratePointers(Heap* heap, HeapObject* object,
                                      int start_offset, int end_offset)) {
-    Object** start_slot =
-        reinterpret_cast<Object**>(object->address() + start_offset);
-    Object** end_slot =
-        reinterpret_cast<Object**>(object->address() + end_offset);
-    StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+    DCHECK(!FLAG_unbox_double_fields ||
+           object->map()->layout_descriptor()->IsFastPointerLayout());
+    IterateRawPointers(heap, object, start_offset, end_offset);
+  }
+
+  INLINE(static void IterateBody(Heap* heap, HeapObject* object,
+                                 int start_offset, int end_offset)) {
+    if (!FLAG_unbox_double_fields ||
+        object->map()->layout_descriptor()->IsFastPointerLayout()) {
+      IterateRawPointers(heap, object, start_offset, end_offset);
+    } else {
+      IterateBodyUsingLayoutDescriptor(heap, object, start_offset, end_offset);
+    }
+  }
+
+ private:
+  INLINE(static void IterateRawPointers(Heap* heap, HeapObject* object,
+                                        int start_offset, int end_offset)) {
+    StaticVisitor::VisitPointers(heap,
+                                 HeapObject::RawField(object, start_offset),
+                                 HeapObject::RawField(object, end_offset));
+  }
+
+  static void IterateBodyUsingLayoutDescriptor(Heap* heap, HeapObject* object,
+                                               int start_offset,
+                                               int end_offset) {
+    DCHECK(FLAG_unbox_double_fields);
+    DCHECK(IsAligned(start_offset, kPointerSize) &&
+           IsAligned(end_offset, kPointerSize));
+
+    InobjectPropertiesHelper helper(object->map());
+    DCHECK(!helper.all_fields_tagged());
+
+    for (int offset = start_offset; offset < end_offset;
+         offset += kPointerSize) {
+      // Visit tagged fields only.
+      if (helper.IsTagged(offset)) {
+        // TODO(ishell): call this once for contiguous region of tagged fields.
+        IterateRawPointers(heap, object, offset, offset + kPointerSize);
+      }
+    }
   }
 };
 
@@ -203,7 +248,7 @@ class FlexibleBodyVisitor : public BodyVisitorBase<StaticVisitor> {
  public:
   INLINE(static ReturnType Visit(Map* map, HeapObject* object)) {
     int object_size = BodyDescriptor::SizeOf(map, object);
-    BodyVisitorBase<StaticVisitor>::IteratePointers(
+    BodyVisitorBase<StaticVisitor>::IterateBody(
         map->GetHeap(), object, BodyDescriptor::kStartOffset, object_size);
     return static_cast<ReturnType>(object_size);
   }
@@ -222,9 +267,9 @@ template <typename StaticVisitor, typename BodyDescriptor, typename ReturnType>
 class FixedBodyVisitor : public BodyVisitorBase<StaticVisitor> {
  public:
   INLINE(static ReturnType Visit(Map* map, HeapObject* object)) {
-    BodyVisitorBase<StaticVisitor>::IteratePointers(
-        map->GetHeap(), object, BodyDescriptor::kStartOffset,
-        BodyDescriptor::kEndOffset);
+    BodyVisitorBase<StaticVisitor>::IterateBody(map->GetHeap(), object,
+                                                BodyDescriptor::kStartOffset,
+                                                BodyDescriptor::kEndOffset);
     return static_cast<ReturnType>(BodyDescriptor::kSize);
   }
 };
index 278e9f2f6ee11a6835d93e6b942e26ab5e16b98d..4b94a95761774a05d3b44affed0b510caec4bbcd 100644 (file)
@@ -507,11 +507,33 @@ void StoreBuffer::IteratePointersToNewSpace(ObjectSlotCallback slot_callback,
             for (HeapObject* heap_object = iterator.Next(); heap_object != NULL;
                  heap_object = iterator.Next()) {
               // We iterate over objects that contain new space pointers only.
-              if (!heap_object->MayContainRawValues()) {
-                FindPointersToNewSpaceInRegion(
-                    heap_object->address() + HeapObject::kHeaderSize,
-                    heap_object->address() + heap_object->Size(), slot_callback,
-                    clear_maps);
+              bool may_contain_raw_values = heap_object->MayContainRawValues();
+              if (!may_contain_raw_values) {
+                Address obj_address = heap_object->address();
+                Address start_address = obj_address + HeapObject::kHeaderSize;
+                Address end_address = obj_address + heap_object->Size();
+#if V8_DOUBLE_FIELDS_UNBOXING
+                InobjectPropertiesHelper helper(heap_object->map());
+                bool has_only_tagged_fields = helper.all_fields_tagged();
+
+                if (!has_only_tagged_fields) {
+                  for (Address slot = start_address; slot < end_address;
+                       slot += kPointerSize) {
+                    if (helper.IsTagged(slot - obj_address)) {
+                      // TODO(ishell): call this once for contiguous region
+                      // of tagged fields.
+                      FindPointersToNewSpaceInRegion(slot, slot + kPointerSize,
+                                                     slot_callback, clear_maps);
+                    }
+                  }
+                } else {
+#endif
+                  // Object has only tagged fields.
+                  FindPointersToNewSpaceInRegion(start_address, end_address,
+                                                 slot_callback, clear_maps);
+#if V8_DOUBLE_FIELDS_UNBOXING
+                }
+#endif
               }
             }
           }
index 9101576b7b91a3b4c9746e1ca11da7bb3eb51320..1a634aff2c42bf582d1fa52a161ae7f78972e23d 100644 (file)
@@ -6943,7 +6943,9 @@ class HStoreNamedField FINAL : public HTemplateInstruction<3> {
   }
 
   bool NeedsWriteBarrier() const {
-    DCHECK(!field_representation().IsDouble() || !has_transition());
+    DCHECK(!field_representation().IsDouble() ||
+           (FLAG_unbox_double_fields && access_.IsInobject()) ||
+           !has_transition());
     if (field_representation().IsDouble()) return false;
     if (field_representation().IsSmi()) return false;
     if (field_representation().IsInteger32()) return false;
index 6184bb9e46b25c6a22015b216a3e60046a537f2d..3386e60cb08cfcedf1a0cc2f4f1cd07bfa0963a8 100644 (file)
@@ -5533,9 +5533,11 @@ static bool IsFastLiteral(Handle<JSObject> boilerplate,
     for (int i = 0; i < limit; i++) {
       PropertyDetails details = descriptors->GetDetails(i);
       if (details.type() != FIELD) continue;
-      int index = descriptors->GetFieldIndex(i);
       if ((*max_properties)-- == 0) return false;
-      Handle<Object> value(boilerplate->InObjectPropertyAt(index), isolate);
+      FieldIndex field_index = FieldIndex::ForDescriptor(boilerplate->map(), i);
+      if (boilerplate->IsUnboxedDoubleField(field_index)) continue;
+      Handle<Object> value(boilerplate->RawFastPropertyAt(field_index),
+                           isolate);
       if (value->IsJSObject()) {
         Handle<JSObject> value_object = Handle<JSObject>::cast(value);
         if (!IsFastLiteral(value_object,
@@ -5838,7 +5840,8 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedField(
   }
 
   HObjectAccess access = info->access();
-  if (access.representation().IsDouble()) {
+  if (access.representation().IsDouble() &&
+      (!FLAG_unbox_double_fields || !access.IsInobject())) {
     // Load the heap number.
     checked_object = Add<HLoadNamedField>(
         checked_object, static_cast<HValue*>(NULL),
@@ -5870,7 +5873,8 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
   HObjectAccess field_access = info->access();
 
   HStoreNamedField *instr;
-  if (field_access.representation().IsDouble()) {
+  if (field_access.representation().IsDouble() &&
+      (!FLAG_unbox_double_fields || !field_access.IsInobject())) {
     HObjectAccess heap_number_access =
         field_access.WithRepresentation(Representation::Tagged());
     if (transition_to_field) {
@@ -11186,18 +11190,27 @@ void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
     PropertyDetails details = descriptors->GetDetails(i);
     if (details.type() != FIELD) continue;
     copied_fields++;
-    int index = descriptors->GetFieldIndex(i);
-    int property_offset = boilerplate_object->GetInObjectPropertyOffset(index);
+    FieldIndex field_index = FieldIndex::ForDescriptor(*boilerplate_map, i);
+
+
+    int property_offset = field_index.offset();
     Handle<Name> name(descriptors->GetKey(i));
-    Handle<Object> value =
-        Handle<Object>(boilerplate_object->InObjectPropertyAt(index),
-        isolate());
 
     // The access for the store depends on the type of the boilerplate.
     HObjectAccess access = boilerplate_object->IsJSArray() ?
         HObjectAccess::ForJSArrayOffset(property_offset) :
         HObjectAccess::ForMapAndOffset(boilerplate_map, property_offset);
 
+    if (boilerplate_object->IsUnboxedDoubleField(field_index)) {
+      CHECK(!boilerplate_object->IsJSArray());
+      double value = boilerplate_object->RawFastDoublePropertyAt(field_index);
+      access = access.WithRepresentation(Representation::Double());
+      Add<HStoreNamedField>(object, access, Add<HConstant>(value));
+      continue;
+    }
+    Handle<Object> value(boilerplate_object->RawFastPropertyAt(field_index),
+                         isolate());
+
     if (value->IsJSObject()) {
       Handle<JSObject> value_object = Handle<JSObject>::cast(value);
       Handle<AllocationSite> current_site = site_context->EnterNewScope();
index f89a19fd4a9b06abf2b1dd4e17a86ef09d38cdd0..1ec6873b1104027cb0396b28fa4129f07068cb05 100644 (file)
@@ -652,8 +652,15 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
       if (details.IsDontEnum()) continue;
       Handle<Object> property;
       if (details.type() == FIELD && *map == object->map()) {
-        property = Handle<Object>(object->RawFastPropertyAt(
-            FieldIndex::ForDescriptor(*map, i)), isolate_);
+        FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
+        Isolate* isolate = object->GetIsolate();
+        if (object->IsUnboxedDoubleField(field_index)) {
+          double value = object->RawFastDoublePropertyAt(field_index);
+          property = isolate->factory()->NewHeapNumber(value);
+
+        } else {
+          property = handle(object->RawFastPropertyAt(field_index), isolate);
+        }
       } else {
         ASSIGN_RETURN_ON_EXCEPTION_VALUE(
             isolate_, property,
diff --git a/src/layout-descriptor-inl.h b/src/layout-descriptor-inl.h
new file mode 100644 (file)
index 0000000..0549d83
--- /dev/null
@@ -0,0 +1,186 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_LAYOUT_DESCRIPTOR_INL_H_
+#define V8_LAYOUT_DESCRIPTOR_INL_H_
+
+#include "src/layout-descriptor.h"
+
+namespace v8 {
+namespace internal {
+
+LayoutDescriptor* LayoutDescriptor::FromSmi(Smi* smi) {
+  return LayoutDescriptor::cast(smi);
+}
+
+
+Handle<LayoutDescriptor> LayoutDescriptor::New(Isolate* isolate, int length) {
+  if (length <= kSmiValueSize) {
+    // The whole bit vector fits into a smi.
+    return handle(LayoutDescriptor::FromSmi(Smi::FromInt(0)), isolate);
+  }
+
+  length = (length + kNumberOfBits - 1) / kNumberOfBits;
+  DCHECK(length > 0);
+
+  if (SmiValuesAre32Bits() && (length & 1)) {
+    // On 64-bit systems if the length is odd then the half-word space would be
+    // lost anyway (due to alignment and the fact that we are allocating
+    // uint32-typed array), so we increase the length of allocated array
+    // to utilize that "lost" space which could also help to avoid layout
+    // descriptor reallocations.
+    ++length;
+  }
+  return Handle<LayoutDescriptor>::cast(
+      isolate->factory()->NewFixedTypedArray(length, kExternalUint32Array));
+}
+
+
+bool LayoutDescriptor::InobjectUnboxedField(int inobject_properties,
+                                            PropertyDetails details) {
+  if (details.type() != FIELD || !details.representation().IsDouble()) {
+    return false;
+  }
+  // We care only about in-object properties.
+  return details.field_index() < inobject_properties;
+}
+
+
+LayoutDescriptor* LayoutDescriptor::FastPointerLayout() {
+  return LayoutDescriptor::FromSmi(Smi::FromInt(0));
+}
+
+
+bool LayoutDescriptor::GetIndexes(int field_index, int* layout_word_index,
+                                  uint32_t* layout_mask) {
+  if (static_cast<unsigned>(field_index) >= static_cast<unsigned>(capacity())) {
+    return false;
+  }
+
+  *layout_word_index = field_index / kNumberOfBits;
+  CHECK((!IsSmi() && (*layout_word_index < length())) ||
+        (IsSmi() && (*layout_word_index < 1)));
+
+  int layout_bit_index = field_index % kNumberOfBits;
+  *layout_mask = static_cast<uint32_t>(1) << layout_bit_index;
+  return true;
+}
+
+
+LayoutDescriptor* LayoutDescriptor::SetTagged(int field_index, bool tagged) {
+  int layout_word_index;
+  uint32_t layout_mask;
+
+  if (!GetIndexes(field_index, &layout_word_index, &layout_mask)) {
+    CHECK(false);
+    return this;
+  }
+
+  if (IsSlowLayout()) {
+    uint32_t value = get_scalar(layout_word_index);
+    if (tagged) {
+      value &= ~layout_mask;
+    } else {
+      value |= layout_mask;
+    }
+    set(layout_word_index, value);
+    return this;
+  } else {
+    uint32_t value = static_cast<uint32_t>(Smi::cast(this)->value());
+    if (tagged) {
+      value &= ~layout_mask;
+    } else {
+      value |= layout_mask;
+    }
+    return LayoutDescriptor::FromSmi(Smi::FromInt(static_cast<int>(value)));
+  }
+}
+
+
+bool LayoutDescriptor::IsTagged(int field_index) {
+  if (IsFastPointerLayout()) return true;
+
+  int layout_word_index;
+  uint32_t layout_mask;
+
+  if (!GetIndexes(field_index, &layout_word_index, &layout_mask)) {
+    // All bits after Out of bounds queries
+    return true;
+  }
+
+  if (IsSlowLayout()) {
+    uint32_t value = get_scalar(layout_word_index);
+    return (value & layout_mask) == 0;
+  } else {
+    uint32_t value = static_cast<uint32_t>(Smi::cast(this)->value());
+    return (value & layout_mask) == 0;
+  }
+}
+
+
+bool LayoutDescriptor::IsFastPointerLayout() {
+  return IsSmi() && (Smi::cast(this)->value() == 0);
+}
+
+
+bool LayoutDescriptor::IsSlowLayout() { return !IsSmi(); }
+
+
+int LayoutDescriptor::capacity() {
+  return IsSlowLayout() ? (length() * kNumberOfBits) : kSmiValueSize;
+}
+
+
+LayoutDescriptor* LayoutDescriptor::cast_gc_safe(Object* object) {
+  if (object->IsSmi()) {
+    // Fast mode layout descriptor.
+    return reinterpret_cast<LayoutDescriptor*>(object);
+  }
+
+  // This is a mixed descriptor which is a fixed typed array.
+  MapWord map_word = reinterpret_cast<HeapObject*>(object)->map_word();
+  if (map_word.IsForwardingAddress()) {
+    // Mark-compact has already moved layout descriptor.
+    object = map_word.ToForwardingAddress();
+  }
+  return LayoutDescriptor::cast(object);
+}
+
+
+// InobjectPropertiesHelper is a helper class for querying whether inobject
+// property at offset is Double or not.
+InobjectPropertiesHelper::InobjectPropertiesHelper(Map* map)
+    : all_fields_tagged_(true),
+      header_size_(0),
+      inobject_properties_count_(0),
+      layout_descriptor_(LayoutDescriptor::FastPointerLayout()) {
+  if (!FLAG_unbox_double_fields) return;
+
+  layout_descriptor_ = map->layout_descriptor_gc_safe();
+  if (layout_descriptor_->IsFastPointerLayout()) {
+    return;
+  }
+
+  int inobject_properties = map->inobject_properties();
+  DCHECK(inobject_properties > 0);
+  header_size_ = map->instance_size() - (inobject_properties * kPointerSize);
+  DCHECK(header_size_ >= 0);
+
+  all_fields_tagged_ = false;
+}
+
+
+bool InobjectPropertiesHelper::IsTagged(int offset_in_bytes) {
+  DCHECK(IsAligned(offset_in_bytes, kPointerSize));
+  if (all_fields_tagged_) return true;
+  // Object headers do not contain non-tagged fields.
+  if (offset_in_bytes < header_size_) return true;
+  int field_index = (offset_in_bytes - header_size_) / kPointerSize;
+
+  return layout_descriptor_->IsTagged(field_index);
+}
+}
+}  // namespace v8::internal
+
+#endif  // V8_LAYOUT_DESCRIPTOR_INL_H_
diff --git a/src/layout-descriptor.cc b/src/layout-descriptor.cc
new file mode 100644 (file)
index 0000000..748b2c1
--- /dev/null
@@ -0,0 +1,146 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sstream>
+
+#include "src/v8.h"
+
+#include "src/layout-descriptor.h"
+
+namespace v8 {
+namespace internal {
+
+Handle<LayoutDescriptor> LayoutDescriptor::New(
+    Handle<Map> map, Handle<DescriptorArray> descriptors, int num_descriptors) {
+  Isolate* isolate = descriptors->GetIsolate();
+  if (!FLAG_unbox_double_fields) return handle(FastPointerLayout(), isolate);
+
+  int inobject_properties = map->inobject_properties();
+  if (inobject_properties == 0) return handle(FastPointerLayout(), isolate);
+
+  DCHECK(num_descriptors <= descriptors->number_of_descriptors());
+
+  int layout_descriptor_length;
+  const int kMaxWordsPerField = kDoubleSize / kPointerSize;
+
+  if (num_descriptors <= kSmiValueSize / kMaxWordsPerField) {
+    // Even in the "worst" case (all fields are doubles) it would fit into
+    // a Smi, so no need to calculate length.
+    layout_descriptor_length = kSmiValueSize;
+
+  } else {
+    layout_descriptor_length = 0;
+
+    for (int i = 0; i < num_descriptors; i++) {
+      PropertyDetails details = descriptors->GetDetails(i);
+      if (!InobjectUnboxedField(inobject_properties, details)) continue;
+      int field_index = details.field_index();
+      int field_width_in_words = details.field_width_in_words();
+      layout_descriptor_length =
+          Max(layout_descriptor_length, field_index + field_width_in_words);
+    }
+
+    if (layout_descriptor_length == 0) {
+      // No double fields were found, use fast pointer layout.
+      return handle(FastPointerLayout(), isolate);
+    }
+  }
+  layout_descriptor_length = Min(layout_descriptor_length, inobject_properties);
+
+  // Initially, layout descriptor corresponds to an object with all fields
+  // tagged.
+  Handle<LayoutDescriptor> layout_descriptor_handle =
+      LayoutDescriptor::New(isolate, layout_descriptor_length);
+
+  DisallowHeapAllocation no_allocation;
+  LayoutDescriptor* layout_descriptor = *layout_descriptor_handle;
+
+  for (int i = 0; i < num_descriptors; i++) {
+    PropertyDetails details = descriptors->GetDetails(i);
+    if (!InobjectUnboxedField(inobject_properties, details)) continue;
+    int field_index = details.field_index();
+    layout_descriptor = layout_descriptor->SetRawData(field_index);
+    if (details.field_width_in_words() > 1) {
+      layout_descriptor = layout_descriptor->SetRawData(field_index + 1);
+    }
+  }
+  return handle(layout_descriptor, isolate);
+}
+
+
+Handle<LayoutDescriptor> LayoutDescriptor::Append(Handle<Map> map,
+                                                  PropertyDetails details) {
+  Handle<LayoutDescriptor> layout_descriptor = map->GetLayoutDescriptor();
+
+  if (!InobjectUnboxedField(map->inobject_properties(), details)) {
+    return layout_descriptor;
+  }
+  Isolate* isolate = map->GetIsolate();
+  int field_index = details.field_index();
+  layout_descriptor = LayoutDescriptor::EnsureCapacity(
+      isolate, layout_descriptor, field_index + details.field_width_in_words());
+
+  DisallowHeapAllocation no_allocation;
+  LayoutDescriptor* layout_desc = *layout_descriptor;
+  layout_desc = layout_desc->SetRawData(field_index);
+  if (details.field_width_in_words() > 1) {
+    layout_desc = layout_desc->SetRawData(field_index + 1);
+  }
+  return handle(layout_desc, isolate);
+}
+
+
+Handle<LayoutDescriptor> LayoutDescriptor::AppendIfFastOrUseFull(
+    Handle<Map> map, PropertyDetails details,
+    Handle<LayoutDescriptor> full_layout_descriptor) {
+  DisallowHeapAllocation no_allocation;
+  LayoutDescriptor* layout_descriptor = map->layout_descriptor();
+  if (layout_descriptor->IsSlowLayout()) {
+    return full_layout_descriptor;
+  }
+  if (!InobjectUnboxedField(map->inobject_properties(), details)) {
+    return handle(layout_descriptor, map->GetIsolate());
+  }
+  int field_index = details.field_index();
+  int new_capacity = field_index + details.field_width_in_words();
+  if (new_capacity > layout_descriptor->capacity()) {
+    // Current map's layout descriptor runs out of space, so use the full
+    // layout descriptor.
+    return full_layout_descriptor;
+  }
+
+  layout_descriptor = layout_descriptor->SetRawData(field_index);
+  if (details.field_width_in_words() > 1) {
+    layout_descriptor = layout_descriptor->SetRawData(field_index + 1);
+  }
+  return handle(layout_descriptor, map->GetIsolate());
+}
+
+
+Handle<LayoutDescriptor> LayoutDescriptor::EnsureCapacity(
+    Isolate* isolate, Handle<LayoutDescriptor> layout_descriptor,
+    int new_capacity) {
+  int old_capacity = layout_descriptor->capacity();
+  if (new_capacity <= old_capacity) {
+    // Nothing to do with layout in Smi-form.
+    return layout_descriptor;
+  }
+  Handle<LayoutDescriptor> new_layout_descriptor =
+      LayoutDescriptor::New(isolate, new_capacity);
+  DCHECK(new_layout_descriptor->IsSlowLayout());
+
+  if (layout_descriptor->IsSlowLayout()) {
+    memcpy(new_layout_descriptor->DataPtr(), layout_descriptor->DataPtr(),
+           layout_descriptor->DataSize());
+    return new_layout_descriptor;
+  } else {
+    // Fast layout.
+    uint32_t value =
+        static_cast<uint32_t>(Smi::cast(*layout_descriptor)->value());
+    new_layout_descriptor->set(0, value);
+    return new_layout_descriptor;
+  }
+}
+}
+}  // namespace v8::internal
diff --git a/src/layout-descriptor.h b/src/layout-descriptor.h
new file mode 100644 (file)
index 0000000..e1d4d49
--- /dev/null
@@ -0,0 +1,127 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_LAYOUT_DESCRIPTOR_H_
+#define V8_LAYOUT_DESCRIPTOR_H_
+
+#include <iosfwd>
+
+#include "src/objects.h"
+
+namespace v8 {
+namespace internal {
+
+// LayoutDescriptor is a bit vector defining which fields contain non-tagged
+// values. It could either be a fixed typed array (slow form) or a Smi
+// if the length fits (fast form).
+// Each bit in the layout represents a FIELD. The bits are referenced by
+// field_index which is a field number. If the bit is set then the corresponding
+// field contains a non-tagged value and therefore must be skipped by GC.
+// Otherwise the field is considered tagged. If the queried bit lays "outside"
+// of the descriptor then the field is also considered tagged.
+// Once a layout descriptor is created it is allowed only to append properties
+// to it.
+class LayoutDescriptor : public FixedTypedArray<Uint32ArrayTraits> {
+ public:
+  V8_INLINE bool IsTagged(int field_index);
+
+  // Returns true if this is a layout of the object having only tagged fields.
+  V8_INLINE bool IsFastPointerLayout();
+
+  // Returns true if the layout descriptor is in non-Smi form.
+  V8_INLINE bool IsSlowLayout();
+
+  V8_INLINE static LayoutDescriptor* cast(Object* object);
+  V8_INLINE static const LayoutDescriptor* cast(const Object* object);
+
+  V8_INLINE static LayoutDescriptor* cast_gc_safe(Object* object);
+
+  // Builds layout descriptor optimized for given |map| by |num_descriptors|
+  // elements of given descriptors array. The |map|'s descriptors could be
+  // different.
+  static Handle<LayoutDescriptor> New(Handle<Map> map,
+                                      Handle<DescriptorArray> descriptors,
+                                      int num_descriptors);
+
+  // Creates new layout descriptor by appending property with |details| to
+  // |map|'s layout descriptor.
+  static Handle<LayoutDescriptor> Append(Handle<Map> map,
+                                         PropertyDetails details);
+
+  // Creates new layout descriptor by appending property with |details| to
+  // |map|'s layout descriptor and if it is still fast then returns it.
+  // Otherwise the |full_layout_descriptor| is returned.
+  static Handle<LayoutDescriptor> AppendIfFastOrUseFull(
+      Handle<Map> map, PropertyDetails details,
+      Handle<LayoutDescriptor> full_layout_descriptor);
+
+  // Layout descriptor that corresponds to an object all fields of which are
+  // tagged (FastPointerLayout).
+  V8_INLINE static LayoutDescriptor* FastPointerLayout();
+
+#ifdef DEBUG
+  // Check that this layout descriptor corresponds to given map.
+  bool IsConsistentWithMap(Map* map);
+#endif
+
+#ifdef OBJECT_PRINT
+  // For our gdb macros, we should perhaps change these in the future.
+  void Print();
+
+  void Print(std::ostream& os);  // NOLINT
+#endif
+
+  // Capacity of layout descriptors in bits.
+  V8_INLINE int capacity();
+
+  V8_INLINE LayoutDescriptor* SetTaggedForTesting(int field_index,
+                                                  bool tagged) {
+    return SetTagged(field_index, tagged);
+  }
+
+ private:
+  static const int kNumberOfBits = 32;
+
+  V8_INLINE static Handle<LayoutDescriptor> New(Isolate* isolate, int length);
+  V8_INLINE static LayoutDescriptor* FromSmi(Smi* smi);
+
+  V8_INLINE static bool InobjectUnboxedField(int inobject_properties,
+                                             PropertyDetails details);
+
+  static Handle<LayoutDescriptor> EnsureCapacity(
+      Isolate* isolate, Handle<LayoutDescriptor> layout_descriptor,
+      int new_capacity);
+
+  // Returns false if requested field_index is out of bounds.
+  V8_INLINE bool GetIndexes(int field_index, int* layout_word_index,
+                            uint32_t* layout_mask);
+
+  V8_INLINE MUST_USE_RESULT LayoutDescriptor* SetRawData(int field_index) {
+    return SetTagged(field_index, false);
+  }
+
+  V8_INLINE MUST_USE_RESULT LayoutDescriptor* SetTagged(int field_index,
+                                                        bool tagged);
+};
+
+
+// InobjectPropertiesHelper is a helper class for querying layout descriptor
+// about whether the field at given offset is tagged or not.
+class InobjectPropertiesHelper {
+ public:
+  inline explicit InobjectPropertiesHelper(Map* map);
+
+  bool all_fields_tagged() { return all_fields_tagged_; }
+  inline bool IsTagged(int offset_in_bytes);
+
+ private:
+  bool all_fields_tagged_;
+  int header_size_;
+  int inobject_properties_count_;
+  LayoutDescriptor* layout_descriptor_;
+};
+}
+}  // namespace v8::internal
+
+#endif  // V8_LAYOUT_DESCRIPTOR_H_
index ca649c723168f85d5582ece7e3ff8d7a10b995cb..94957206548ec3d3ab63ee251526abcf96a042f5 100644 (file)
@@ -275,6 +275,10 @@ void JSObject::JSObjectVerify() {
       if (descriptors->GetDetails(i).type() == FIELD) {
         Representation r = descriptors->GetDetails(i).representation();
         FieldIndex index = FieldIndex::ForDescriptor(map(), i);
+        if (IsUnboxedDoubleField(index)) {
+          DCHECK(r.IsDouble());
+          continue;
+        }
         Object* value = RawFastPropertyAt(index);
         if (r.IsDouble()) DCHECK(value->IsMutableHeapNumber());
         if (value->IsUninitialized()) continue;
@@ -316,6 +320,8 @@ void Map::MapVerify() {
     SLOW_DCHECK(transitions()->IsSortedNoDuplicates());
     SLOW_DCHECK(transitions()->IsConsistentWithBackPointers(this));
   }
+  SLOW_DCHECK(!FLAG_unbox_double_fields ||
+              layout_descriptor()->IsConsistentWithMap(this));
 }
 
 
@@ -325,8 +331,7 @@ void Map::DictionaryMapVerify() {
   CHECK(instance_descriptors()->IsEmpty());
   CHECK_EQ(0, pre_allocated_property_fields());
   CHECK_EQ(0, unused_property_fields());
-  CHECK_EQ(StaticVisitorBase::GetVisitorId(instance_type(), instance_size()),
-      visitor_id());
+  CHECK_EQ(StaticVisitorBase::GetVisitorId(this), visitor_id());
 }
 
 
@@ -1179,6 +1184,27 @@ bool DescriptorArray::IsSortedNoDuplicates(int valid_entries) {
 }
 
 
+bool LayoutDescriptor::IsConsistentWithMap(Map* map) {
+  if (FLAG_unbox_double_fields) {
+    DescriptorArray* descriptors = map->instance_descriptors();
+    int nof_descriptors = map->NumberOfOwnDescriptors();
+    for (int i = 0; i < nof_descriptors; i++) {
+      PropertyDetails details = descriptors->GetDetails(i);
+      if (details.type() != FIELD) continue;
+      FieldIndex field_index = FieldIndex::ForDescriptor(map, i);
+      bool tagged_expected =
+          !field_index.is_inobject() || !details.representation().IsDouble();
+      for (int bit = 0; bit < details.field_width_in_words(); bit++) {
+        bool tagged_actual = IsTagged(details.field_index() + bit);
+        DCHECK_EQ(tagged_expected, tagged_actual);
+        if (tagged_actual != tagged_expected) return false;
+      }
+    }
+  }
+  return true;
+}
+
+
 bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
   DCHECK(valid_entries == -1);
   Name* prev_key = NULL;
index 40ce81a9b7bbee62fe31f22d273c00b82062b045..b093bb98d3076217306025d54ec1888cf3e61075 100644 (file)
@@ -26,6 +26,7 @@
 #include "src/heap/spaces.h"
 #include "src/heap/store-buffer.h"
 #include "src/isolate.h"
+#include "src/layout-descriptor-inl.h"
 #include "src/lookup.h"
 #include "src/objects.h"
 #include "src/property.h"
@@ -56,6 +57,14 @@ PropertyDetails PropertyDetails::AsDeleted() const {
 }
 
 
+int PropertyDetails::field_width_in_words() const {
+  DCHECK(type() == FIELD);
+  if (!FLAG_unbox_double_fields) return 1;
+  if (kDoubleSize == kPointerSize) return 1;
+  return representation().IsDouble() ? kDoubleSize / kPointerSize : 1;
+}
+
+
 #define TYPE_CHECKER(type, instancetype)                                \
   bool Object::Is##type() const {                                       \
   return Object::IsHeapObject() &&                                      \
@@ -704,6 +713,11 @@ bool Object::IsDescriptorArray() const {
 }
 
 
+bool Object::IsLayoutDescriptor() const {
+  return IsSmi() || IsFixedTypedArrayBase();
+}
+
+
 bool Object::IsTransitionArray() const {
   return IsFixedArray();
 }
@@ -2070,10 +2084,24 @@ void JSObject::SetInternalField(int index, Smi* value) {
 }
 
 
+bool JSObject::IsUnboxedDoubleField(FieldIndex index) {
+  if (!FLAG_unbox_double_fields) return false;
+  return map()->IsUnboxedDoubleField(index);
+}
+
+
+bool Map::IsUnboxedDoubleField(FieldIndex index) {
+  if (!FLAG_unbox_double_fields) return false;
+  if (index.is_hidden_field() || !index.is_inobject()) return false;
+  return !layout_descriptor()->IsTagged(index.property_index());
+}
+
+
 // Access fast-case object properties at index. The use of these routines
 // is needed to correctly distinguish between properties stored in-object and
 // properties stored in the properties array.
 Object* JSObject::RawFastPropertyAt(FieldIndex index) {
+  DCHECK(!IsUnboxedDoubleField(index));
   if (index.is_inobject()) {
     return READ_FIELD(this, index.offset());
   } else {
@@ -2082,7 +2110,13 @@ Object* JSObject::RawFastPropertyAt(FieldIndex index) {
 }
 
 
-void JSObject::FastPropertyAtPut(FieldIndex index, Object* value) {
+double JSObject::RawFastDoublePropertyAt(FieldIndex index) {
+  DCHECK(IsUnboxedDoubleField(index));
+  return READ_DOUBLE_FIELD(this, index.offset());
+}
+
+
+void JSObject::RawFastPropertyAtPut(FieldIndex index, Object* value) {
   if (index.is_inobject()) {
     int offset = index.offset();
     WRITE_FIELD(this, offset, value);
@@ -2093,6 +2127,21 @@ void JSObject::FastPropertyAtPut(FieldIndex index, Object* value) {
 }
 
 
+void JSObject::RawFastDoublePropertyAtPut(FieldIndex index, double value) {
+  WRITE_DOUBLE_FIELD(this, index.offset(), value);
+}
+
+
+void JSObject::FastPropertyAtPut(FieldIndex index, Object* value) {
+  if (IsUnboxedDoubleField(index)) {
+    DCHECK(value->IsMutableHeapNumber());
+    RawFastDoublePropertyAtPut(index, HeapNumber::cast(value)->value());
+  } else {
+    RawFastPropertyAtPut(index, value);
+  }
+}
+
+
 int JSObject::GetInObjectPropertyOffset(int index) {
   return map()->GetInObjectPropertyOffset(index);
 }
@@ -3111,8 +3160,7 @@ void DescriptorArray::Set(int descriptor_number,
   NoIncrementalWriteBarrierSet(this,
                                ToValueIndex(descriptor_number),
                                *desc->GetValue());
-  NoIncrementalWriteBarrierSet(this,
-                               ToDetailsIndex(descriptor_number),
+  NoIncrementalWriteBarrierSet(this, ToDetailsIndex(descriptor_number),
                                desc->GetDetails().AsSmi());
 }
 
@@ -3287,6 +3335,7 @@ CAST_ACCESSOR(JSTypedArray)
 CAST_ACCESSOR(JSValue)
 CAST_ACCESSOR(JSWeakMap)
 CAST_ACCESSOR(JSWeakSet)
+CAST_ACCESSOR(LayoutDescriptor)
 CAST_ACCESSOR(Map)
 CAST_ACCESSOR(MapCache)
 CAST_ACCESSOR(Name)
@@ -4329,6 +4378,14 @@ int Map::GetInObjectPropertyOffset(int index) {
 }
 
 
+Handle<Map> Map::CopyInstallDescriptorsForTesting(
+    Handle<Map> map, int new_descriptor, Handle<DescriptorArray> descriptors,
+    Handle<LayoutDescriptor> layout_descriptor) {
+  return CopyInstallDescriptors(map, new_descriptor, descriptors,
+                                layout_descriptor);
+}
+
+
 int HeapObject::SizeFromMap(Map* map) {
   int instance_size = map->instance_size();
   if (instance_size != kVariableSizeSentinel) return instance_size;
@@ -5174,14 +5231,41 @@ static void EnsureHasTransitionArray(Handle<Map> map) {
 }
 
 
-void Map::InitializeDescriptors(DescriptorArray* descriptors) {
+LayoutDescriptor* Map::layout_descriptor_gc_safe() {
+  Object* layout_desc = READ_FIELD(this, kLayoutDecriptorOffset);
+  return LayoutDescriptor::cast_gc_safe(layout_desc);
+}
+
+
+void Map::UpdateDescriptors(DescriptorArray* descriptors,
+                            LayoutDescriptor* layout_desc) {
+  set_instance_descriptors(descriptors);
+  if (FLAG_unbox_double_fields) {
+    if (layout_descriptor()->IsSlowLayout()) {
+      set_layout_descriptor(layout_desc);
+    }
+    SLOW_DCHECK(layout_descriptor()->IsConsistentWithMap(this));
+    DCHECK(visitor_id() == StaticVisitorBase::GetVisitorId(this));
+  }
+}
+
+
+void Map::InitializeDescriptors(DescriptorArray* descriptors,
+                                LayoutDescriptor* layout_desc) {
   int len = descriptors->number_of_descriptors();
   set_instance_descriptors(descriptors);
   SetNumberOfOwnDescriptors(len);
+
+  if (FLAG_unbox_double_fields) {
+    set_layout_descriptor(layout_desc);
+    SLOW_DCHECK(layout_descriptor()->IsConsistentWithMap(this));
+    set_visitor_id(StaticVisitorBase::GetVisitorId(this));
+  }
 }
 
 
 ACCESSORS(Map, instance_descriptors, DescriptorArray, kDescriptorsOffset)
+ACCESSORS(Map, layout_descriptor, LayoutDescriptor, kLayoutDecriptorOffset)
 
 
 void Map::set_bit_field3(uint32_t bits) {
@@ -5197,12 +5281,27 @@ uint32_t Map::bit_field3() {
 }
 
 
+Handle<LayoutDescriptor> Map::GetLayoutDescriptor() {
+  LayoutDescriptor* layout_desc = FLAG_unbox_double_fields
+                                      ? layout_descriptor()
+                                      : LayoutDescriptor::FastPointerLayout();
+  return handle(layout_desc, GetIsolate());
+}
+
+
 void Map::AppendDescriptor(Descriptor* desc) {
   DescriptorArray* descriptors = instance_descriptors();
   int number_of_own_descriptors = NumberOfOwnDescriptors();
   DCHECK(descriptors->number_of_descriptors() == number_of_own_descriptors);
   descriptors->Append(desc);
   SetNumberOfOwnDescriptors(number_of_own_descriptors + 1);
+
+// This function does not support appending double field descriptors and
+// it should never try to (otherwise, layout descriptor must be updated too).
+#ifdef DEBUG
+  PropertyDetails details = desc->GetDetails();
+  CHECK(details.type() != FIELD || !details.representation().IsDouble());
+#endif
 }
 
 
@@ -7279,12 +7378,36 @@ void ExternalTwoByteString::ExternalTwoByteStringIterateBody() {
 }
 
 
+static void IterateBodyUsingLayoutDescriptor(HeapObject* object,
+                                             int start_offset, int end_offset,
+                                             ObjectVisitor* v) {
+  DCHECK(FLAG_unbox_double_fields);
+  DCHECK(IsAligned(start_offset, kPointerSize) &&
+         IsAligned(end_offset, kPointerSize));
+
+  InobjectPropertiesHelper helper(object->map());
+  DCHECK(!helper.all_fields_tagged());
+
+  for (int offset = start_offset; offset < end_offset; offset += kPointerSize) {
+    // Visit all tagged fields.
+    if (helper.IsTagged(offset)) {
+      v->VisitPointer(HeapObject::RawField(object, offset));
+    }
+  }
+}
+
+
 template<int start_offset, int end_offset, int size>
 void FixedBodyDescriptor<start_offset, end_offset, size>::IterateBody(
     HeapObject* obj,
     ObjectVisitor* v) {
+  if (!FLAG_unbox_double_fields ||
+      obj->map()->layout_descriptor()->IsFastPointerLayout()) {
     v->VisitPointers(HeapObject::RawField(obj, start_offset),
                      HeapObject::RawField(obj, end_offset));
+  } else {
+    IterateBodyUsingLayoutDescriptor(obj, start_offset, end_offset, v);
+  }
 }
 
 
@@ -7292,8 +7415,13 @@ template<int start_offset>
 void FlexibleBodyDescriptor<start_offset>::IterateBody(HeapObject* obj,
                                                        int object_size,
                                                        ObjectVisitor* v) {
-  v->VisitPointers(HeapObject::RawField(obj, start_offset),
-                   HeapObject::RawField(obj, object_size));
+  if (!FLAG_unbox_double_fields ||
+      obj->map()->layout_descriptor()->IsFastPointerLayout()) {
+    v->VisitPointers(HeapObject::RawField(obj, start_offset),
+                     HeapObject::RawField(obj, object_size));
+  } else {
+    IterateBodyUsingLayoutDescriptor(obj, start_offset, object_size, v);
+  }
 }
 
 
index 2fb924cfc99a19b94d964ddd364c7545d16f3d81..45ff33c2d289cb20dc2d7e03df7abaa72797d0b8 100644 (file)
@@ -233,8 +233,12 @@ void JSObject::PrintProperties(std::ostream& os) {  // NOLINT
       switch (descs->GetType(i)) {
         case FIELD: {
           FieldIndex index = FieldIndex::ForDescriptor(map(), i);
-          os << Brief(RawFastPropertyAt(index)) << " (field at offset "
-             << index.property_index() << ")\n";
+          if (IsUnboxedDoubleField(index)) {
+            os << "<unboxed double> " << RawFastDoublePropertyAt(index);
+          } else {
+            os << Brief(RawFastPropertyAt(index));
+          }
+          os << " (field at offset " << index.property_index() << ")\n";
           break;
         }
         case CONSTANT:
@@ -430,6 +434,9 @@ void Map::MapPrint(std::ostream& os) {  // NOLINT
   os << "\n - instance descriptors " << (owns_descriptors() ? "(own) " : "")
      << "#" << NumberOfOwnDescriptors() << ": "
      << Brief(instance_descriptors());
+  if (FLAG_unbox_double_fields) {
+    os << "\n - layout descriptor: " << Brief(layout_descriptor());
+  }
   if (HasTransitionArray()) {
     os << "\n - transitions: " << Brief(transitions());
   }
@@ -438,6 +445,9 @@ void Map::MapPrint(std::ostream& os) {  // NOLINT
   os << "\n - code cache: " << Brief(code_cache());
   os << "\n - dependent code: " << Brief(dependent_code());
   os << "\n";
+  instance_descriptors()->PrintDescriptors(os);
+  os << "\n";
+  if (FLAG_unbox_double_fields) layout_descriptor()->Print(os);
 }
 
 
@@ -1076,6 +1086,43 @@ void DescriptorArray::PrintDescriptors(std::ostream& os) {  // NOLINT
 }
 
 
+static void PrintBitMask(std::ostream& os, uint32_t value) {  // NOLINT
+  for (int i = 0; i < 32; i++) {
+    if ((i & 7) == 0) os << " ";
+    os << (((value & 1) == 0) ? "_" : "x");
+    value >>= 1;
+  }
+}
+
+
+void LayoutDescriptor::Print() {
+  OFStream os(stdout);
+  this->Print(os);
+  os << std::flush;
+}
+
+
+void LayoutDescriptor::Print(std::ostream& os) {  // NOLINT
+  os << "Layout descriptor: ";
+  if (IsUninitialized()) {
+    os << "<uninitialized>";
+  } else if (IsFastPointerLayout()) {
+    os << "<all tagged>";
+  } else if (IsSmi()) {
+    os << "fast";
+    PrintBitMask(os, static_cast<uint32_t>(Smi::cast(this)->value()));
+  } else {
+    os << "slow";
+    int len = length();
+    for (int i = 0; i < len; i++) {
+      if (i > 0) os << " |";
+      PrintBitMask(os, get_scalar(i));
+    }
+  }
+  os << "\n";
+}
+
+
 void TransitionArray::Print() {
   OFStream os(stdout);
   this->PrintTransitions(os);
index 2b5b567112977d694b6d93213f3a03e5b05620d5..b27734b268a2f877905528d8ba1f483a761f1377 100644 (file)
@@ -1953,7 +1953,8 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
         // Clear out the old descriptor array to avoid problems to sharing
         // the descriptor array without using an explicit.
         old_map->InitializeDescriptors(
-            old_map->GetHeap()->empty_descriptor_array());
+            old_map->GetHeap()->empty_descriptor_array(),
+            LayoutDescriptor::FastPointerLayout());
         // Ensure that no transition was inserted for prototype migrations.
         DCHECK(!old_map->HasTransitionArray());
         DCHECK(new_map->GetBackPointer()->IsUndefined());
@@ -2012,10 +2013,14 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
 
     if (old_map->unused_property_fields() > 0) {
       if (details.representation().IsDouble()) {
-        Handle<Object> value = isolate->factory()->NewHeapNumber(0, MUTABLE);
         FieldIndex index =
             FieldIndex::ForDescriptor(*new_map, new_map->LastAdded());
-        object->FastPropertyAtPut(index, *value);
+        if (new_map->IsUnboxedDoubleField(index)) {
+          object->RawFastDoublePropertyAtPut(index, 0);
+        } else {
+          Handle<Object> value = isolate->factory()->NewHeapNumber(0, MUTABLE);
+          object->RawFastPropertyAtPut(index, *value);
+        }
       }
       object->synchronized_set_map(*new_map);
       return;
@@ -2067,23 +2072,35 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
       DCHECK(details.representation().IsTagged());
       continue;
     }
+    Representation old_representation = old_details.representation();
+    Representation representation = details.representation();
     DCHECK(old_details.type() == CONSTANT ||
            old_details.type() == FIELD);
-    Object* raw_value = old_details.type() == CONSTANT
-        ? old_descriptors->GetValue(i)
-        : object->RawFastPropertyAt(FieldIndex::ForDescriptor(*old_map, i));
-    Handle<Object> value(raw_value, isolate);
-    if (!old_details.representation().IsDouble() &&
-        details.representation().IsDouble()) {
-      if (old_details.representation().IsNone()) {
-        value = handle(Smi::FromInt(0), isolate);
+    Handle<Object> value;
+    if (old_details.type() == CONSTANT) {
+      value = handle(old_descriptors->GetValue(i), isolate);
+      DCHECK(!old_representation.IsDouble() && !representation.IsDouble());
+    } else {
+      FieldIndex index = FieldIndex::ForDescriptor(*old_map, i);
+      if (object->IsUnboxedDoubleField(index)) {
+        double old = object->RawFastDoublePropertyAt(index);
+        value = isolate->factory()->NewHeapNumber(
+            old, representation.IsDouble() ? MUTABLE : IMMUTABLE);
+
+      } else {
+        value = handle(object->RawFastPropertyAt(index), isolate);
+        if (!old_representation.IsDouble() && representation.IsDouble()) {
+          if (old_representation.IsNone()) {
+            value = handle(Smi::FromInt(0), isolate);
+          }
+          value = Object::NewStorageFor(isolate, value, representation);
+        } else if (old_representation.IsDouble() &&
+                   !representation.IsDouble()) {
+          value = Object::WrapForRead(isolate, value, old_representation);
+        }
       }
-      value = Object::NewStorageFor(isolate, value, details.representation());
-    } else if (old_details.representation().IsDouble() &&
-               !details.representation().IsDouble()) {
-      value = Object::WrapForRead(isolate, value, old_details.representation());
     }
-    DCHECK(!(details.representation().IsDouble() && value->IsSmi()));
+    DCHECK(!(representation.IsDouble() && value->IsSmi()));
     int target_index = new_descriptors->GetFieldIndex(i) - inobject;
     if (target_index < 0) target_index += total_size;
     array->set(target_index, *value);
@@ -2111,7 +2128,16 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
   int limit = Min(inobject, number_of_fields);
   for (int i = 0; i < limit; i++) {
     FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
-    object->FastPropertyAtPut(index, array->get(external + i));
+    Object* value = array->get(external + i);
+    // Can't use JSObject::FastPropertyAtPut() because proper map was not set
+    // yet.
+    if (new_map->IsUnboxedDoubleField(index)) {
+      DCHECK(value->IsMutableHeapNumber());
+      object->RawFastDoublePropertyAtPut(index,
+                                         HeapNumber::cast(value)->value());
+    } else {
+      object->RawFastPropertyAtPut(index, value);
+    }
   }
 
   Heap* heap = isolate->heap();
@@ -2235,7 +2261,8 @@ void Map::DeprecateTransitionTree() {
 // arrays.
 void Map::DeprecateTarget(PropertyType type, Name* key,
                           PropertyAttributes attributes,
-                          DescriptorArray* new_descriptors) {
+                          DescriptorArray* new_descriptors,
+                          LayoutDescriptor* new_layout_descriptor) {
   if (HasTransitionArray()) {
     TransitionArray* transitions = this->transitions();
     int transition = transitions->Search(type, key, attributes);
@@ -2252,7 +2279,7 @@ void Map::DeprecateTarget(PropertyType type, Name* key,
   GetHeap()->incremental_marking()->RecordWrites(to_replace);
   while (current->instance_descriptors() == to_replace) {
     current->SetEnumLength(kInvalidEnumCacheSentinel);
-    current->set_instance_descriptors(new_descriptors);
+    current->UpdateDescriptors(new_descriptors, new_layout_descriptor);
     Object* next = current->GetBackPointer();
     if (next->IsUndefined()) break;
     current = Map::cast(next);
@@ -2591,7 +2618,9 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
   int current_offset = 0;
   for (int i = 0; i < root_nof; ++i) {
     PropertyDetails old_details = old_descriptors->GetDetails(i);
-    if (old_details.type() == FIELD) current_offset++;
+    if (old_details.type() == FIELD) {
+      current_offset += old_details.field_width_in_words();
+    }
     Descriptor d(handle(old_descriptors->GetKey(i), isolate),
                  handle(old_descriptors->GetValue(i), isolate),
                  old_details);
@@ -2629,11 +2658,10 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
         target_field_type = GeneralizeFieldType(
             target_field_type, new_field_type, isolate);
       }
-      FieldDescriptor d(target_key,
-                        current_offset++,
-                        target_field_type,
+      FieldDescriptor d(target_key, current_offset, target_field_type,
                         target_details.attributes(),
                         target_details.representation());
+      current_offset += d.GetDetails().field_width_in_words();
       new_descriptors->Set(i, &d);
     } else {
       DCHECK_NE(FIELD, target_details.type());
@@ -2659,23 +2687,20 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
         old_field_type = GeneralizeFieldType(
             old_field_type, new_field_type, isolate);
       }
-      FieldDescriptor d(old_key,
-                        current_offset++,
-                        old_field_type,
-                        old_details.attributes(),
-                        old_details.representation());
+      FieldDescriptor d(old_key, current_offset, old_field_type,
+                        old_details.attributes(), old_details.representation());
+      current_offset += d.GetDetails().field_width_in_words();
       new_descriptors->Set(i, &d);
     } else {
       DCHECK(old_details.type() == CONSTANT || old_details.type() == CALLBACKS);
       if (modify_index == i && store_mode == FORCE_FIELD) {
-        FieldDescriptor d(old_key,
-                          current_offset++,
-                          GeneralizeFieldType(
-                              old_descriptors->GetValue(i)->OptimalType(
-                                  isolate, old_details.representation()),
-                              new_field_type, isolate),
-                          old_details.attributes(),
-                          old_details.representation());
+        FieldDescriptor d(
+            old_key, current_offset,
+            GeneralizeFieldType(old_descriptors->GetValue(i)->OptimalType(
+                                    isolate, old_details.representation()),
+                                new_field_type, isolate),
+            old_details.attributes(), old_details.representation());
+        current_offset += d.GetDetails().field_width_in_words();
         new_descriptors->Set(i, &d);
       } else {
         DCHECK_NE(FIELD, old_details.type());
@@ -2697,10 +2722,13 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
   int split_nof = split_map->NumberOfOwnDescriptors();
   DCHECK_NE(old_nof, split_nof);
 
+  Handle<LayoutDescriptor> new_layout_descriptor =
+      LayoutDescriptor::New(split_map, new_descriptors, old_nof);
   PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof);
   split_map->DeprecateTarget(split_prop_details.type(),
                              old_descriptors->GetKey(split_nof),
-                             split_prop_details.attributes(), *new_descriptors);
+                             split_prop_details.attributes(), *new_descriptors,
+                             *new_layout_descriptor);
 
   if (FLAG_trace_generalization) {
     PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
@@ -2727,7 +2755,8 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map,
       return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode,
                                               "can't have more transitions");
     }
-    new_map = CopyInstallDescriptors(new_map, i, new_descriptors);
+    new_map = CopyInstallDescriptors(new_map, i, new_descriptors,
+                                     new_layout_descriptor);
   }
   new_map->set_owns_descriptors(true);
   return new_map;
@@ -3150,8 +3179,12 @@ void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
   Handle<DescriptorArray> new_descriptors = DescriptorArray::CopyUpTo(
       descriptors, old_size, slack);
 
+  DisallowHeapAllocation no_allocation;
+  // The descriptors are still the same, so keep the layout descriptor.
+  LayoutDescriptor* layout_descriptor = map->layout_descriptor();
+
   if (old_size == 0) {
-    map->set_instance_descriptors(*new_descriptors);
+    map->UpdateDescriptors(*new_descriptors, layout_descriptor);
     return;
   }
 
@@ -3173,10 +3206,10 @@ void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
        current = walk_map->GetBackPointer()) {
     walk_map = Map::cast(current);
     if (walk_map->instance_descriptors() != *descriptors) break;
-    walk_map->set_instance_descriptors(*new_descriptors);
+    walk_map->UpdateDescriptors(*new_descriptors, layout_descriptor);
   }
 
-  map->set_instance_descriptors(*new_descriptors);
+  map->UpdateDescriptors(*new_descriptors, layout_descriptor);
 }
 
 
@@ -3850,11 +3883,15 @@ void JSObject::WriteToField(int descriptor, Object* value) {
   if (details.representation().IsDouble()) {
     // Nothing more to be done.
     if (value->IsUninitialized()) return;
-    HeapNumber* box = HeapNumber::cast(RawFastPropertyAt(index));
-    DCHECK(box->IsMutableHeapNumber());
-    box->set_value(value->Number());
+    if (IsUnboxedDoubleField(index)) {
+      RawFastDoublePropertyAtPut(index, value->Number());
+    } else {
+      HeapNumber* box = HeapNumber::cast(RawFastPropertyAt(index));
+      DCHECK(box->IsMutableHeapNumber());
+      box->set_value(value->Number());
+    }
   } else {
-    FastPropertyAtPut(index, value);
+    RawFastPropertyAtPut(index, value);
   }
 }
 
@@ -4316,12 +4353,17 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object,
       case FIELD: {
         Handle<Name> key(descs->GetKey(i));
         FieldIndex index = FieldIndex::ForDescriptor(*map, i);
-        Handle<Object> value(
-            object->RawFastPropertyAt(index), isolate);
-        if (details.representation().IsDouble()) {
-          DCHECK(value->IsMutableHeapNumber());
-          Handle<HeapNumber> old = Handle<HeapNumber>::cast(value);
-          value = isolate->factory()->NewHeapNumber(old->value());
+        Handle<Object> value;
+        if (object->IsUnboxedDoubleField(index)) {
+          double old_value = object->RawFastDoublePropertyAt(index);
+          value = isolate->factory()->NewHeapNumber(old_value);
+        } else {
+          value = handle(object->RawFastPropertyAt(index), isolate);
+          if (details.representation().IsDouble()) {
+            DCHECK(value->IsMutableHeapNumber());
+            Handle<HeapNumber> old = Handle<HeapNumber>::cast(value);
+            value = isolate->factory()->NewHeapNumber(old->value());
+          }
         }
         PropertyDetails d =
             PropertyDetails(details.attributes(), NORMAL, i + 1);
@@ -4491,9 +4533,10 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
         int offset = current_offset - inobject_props;
         fields->set(offset, value);
       }
-      FieldDescriptor d(key, current_offset++, details.attributes(),
+      FieldDescriptor d(key, current_offset, details.attributes(),
                         // TODO(verwaest): value->OptimalRepresentation();
                         Representation::Tagged());
+      current_offset += d.GetDetails().field_width_in_words();
       descriptors->Set(enumeration_index - 1, &d);
     } else if (type == CALLBACKS) {
       CallbacksDescriptor d(key, handle(value, isolate), details.attributes());
@@ -4506,8 +4549,11 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
 
   descriptors->Sort();
 
+  Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New(
+      new_map, descriptors, descriptors->number_of_descriptors());
+
   DisallowHeapAllocation no_gc;
-  new_map->InitializeDescriptors(*descriptors);
+  new_map->InitializeDescriptors(*descriptors, *layout_descriptor);
   new_map->set_unused_property_fields(unused_property_fields);
 
   // Transform the object.
@@ -5478,6 +5524,10 @@ Handle<Object> JSObject::FastPropertyAt(Handle<JSObject> object,
                                         Representation representation,
                                         FieldIndex index) {
   Isolate* isolate = object->GetIsolate();
+  if (object->IsUnboxedDoubleField(index)) {
+    double value = object->RawFastDoublePropertyAt(index);
+    return isolate->factory()->NewHeapNumber(value);
+  }
   Handle<Object> raw_value(object->RawFastPropertyAt(index), isolate);
   return Object::WrapForRead(isolate, raw_value, representation);
 }
@@ -5568,18 +5618,28 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
         PropertyDetails details = descriptors->GetDetails(i);
         if (details.type() != FIELD) continue;
         FieldIndex index = FieldIndex::ForDescriptor(copy->map(), i);
-        Handle<Object> value(object->RawFastPropertyAt(index), isolate);
-        if (value->IsJSObject()) {
-          ASSIGN_RETURN_ON_EXCEPTION(
-              isolate, value,
-              VisitElementOrProperty(copy, Handle<JSObject>::cast(value)),
-              JSObject);
+        if (object->IsUnboxedDoubleField(index)) {
+          if (copying) {
+            double value = object->RawFastDoublePropertyAt(index);
+            copy->RawFastDoublePropertyAtPut(index, value);
+          }
         } else {
-          Representation representation = details.representation();
-          value = Object::NewStorageFor(isolate, value, representation);
-        }
-        if (copying) {
-          copy->FastPropertyAtPut(index, *value);
+          Handle<Object> value(object->RawFastPropertyAt(index), isolate);
+          if (value->IsJSObject()) {
+            ASSIGN_RETURN_ON_EXCEPTION(
+                isolate, value,
+                VisitElementOrProperty(copy, Handle<JSObject>::cast(value)),
+                JSObject);
+            if (copying) {
+              copy->FastPropertyAtPut(index, *value);
+            }
+          } else {
+            if (copying) {
+              Representation representation = details.representation();
+              value = Object::NewStorageFor(isolate, value, representation);
+              copy->FastPropertyAtPut(index, *value);
+            }
+          }
         }
       }
     } else {
@@ -5776,16 +5836,17 @@ int Map::NumberOfDescribedProperties(DescriptorFlag which,
 
 
 int Map::NextFreePropertyIndex() {
-  int max_index = -1;
+  int free_index = 0;
   int number_of_own_descriptors = NumberOfOwnDescriptors();
   DescriptorArray* descs = instance_descriptors();
   for (int i = 0; i < number_of_own_descriptors; i++) {
-    if (descs->GetType(i) == FIELD) {
-      int current_index = descs->GetFieldIndex(i);
-      if (current_index > max_index) max_index = current_index;
+    PropertyDetails details = descs->GetDetails(i);
+    if (details.type() == FIELD) {
+      int candidate = details.field_index() + details.field_width_in_words();
+      if (candidate > free_index) free_index = candidate;
     }
   }
-  return max_index + 1;
+  return free_index;
 }
 
 
@@ -6470,17 +6531,27 @@ Object* JSObject::SlowReverseLookup(Object* value) {
   if (HasFastProperties()) {
     int number_of_own_descriptors = map()->NumberOfOwnDescriptors();
     DescriptorArray* descs = map()->instance_descriptors();
+    bool value_is_number = value->IsNumber();
     for (int i = 0; i < number_of_own_descriptors; i++) {
       if (descs->GetType(i) == FIELD) {
-        Object* property =
-            RawFastPropertyAt(FieldIndex::ForDescriptor(map(), i));
-        if (descs->GetDetails(i).representation().IsDouble()) {
-          DCHECK(property->IsMutableHeapNumber());
-          if (value->IsNumber() && property->Number() == value->Number()) {
+        FieldIndex field_index = FieldIndex::ForDescriptor(map(), i);
+        if (IsUnboxedDoubleField(field_index)) {
+          if (value_is_number) {
+            double property = RawFastDoublePropertyAt(field_index);
+            if (property == value->Number()) {
+              return descs->GetKey(i);
+            }
+          }
+        } else {
+          Object* property = RawFastPropertyAt(field_index);
+          if (field_index.is_double()) {
+            DCHECK(property->IsMutableHeapNumber());
+            if (value_is_number && property->Number() == value->Number()) {
+              return descs->GetKey(i);
+            }
+          } else if (property == value) {
             return descs->GetKey(i);
           }
-        } else if (property == value) {
-          return descs->GetKey(i);
         }
       } else if (descs->GetType(i) == CONSTANT) {
         if (descs->GetConstant(i) == value) {
@@ -6634,10 +6705,15 @@ Handle<Map> Map::ShareDescriptor(Handle<Map> map,
     }
   }
 
+  Handle<LayoutDescriptor> layout_descriptor =
+      FLAG_unbox_double_fields
+          ? LayoutDescriptor::Append(map, descriptor->GetDetails())
+          : map->GetLayoutDescriptor();
+
   {
     DisallowHeapAllocation no_gc;
     descriptors->Append(descriptor);
-    result->InitializeDescriptors(*descriptors);
+    result->InitializeDescriptors(*descriptors, *layout_descriptor);
   }
 
   DCHECK(result->NumberOfOwnDescriptors() == map->NumberOfOwnDescriptors() + 1);
@@ -6697,19 +6773,19 @@ void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child,
 }
 
 
-Handle<Map> Map::CopyReplaceDescriptors(Handle<Map> map,
-                                        Handle<DescriptorArray> descriptors,
-                                        TransitionFlag flag,
-                                        MaybeHandle<Name> maybe_name,
-                                        const char* reason,
-                                        SimpleTransitionFlag simple_flag) {
+Handle<Map> Map::CopyReplaceDescriptors(
+    Handle<Map> map, Handle<DescriptorArray> descriptors,
+    Handle<LayoutDescriptor> layout_descriptor, TransitionFlag flag,
+    MaybeHandle<Name> maybe_name, const char* reason,
+    SimpleTransitionFlag simple_flag) {
   DCHECK(descriptors->IsSortedNoDuplicates());
 
   Handle<Map> result = CopyDropDescriptors(map);
-  result->InitializeDescriptors(*descriptors);
 
   if (!map->is_prototype_map()) {
     if (flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()) {
+      result->InitializeDescriptors(*descriptors, *layout_descriptor);
+
       Handle<Name> name;
       CHECK(maybe_name.ToHandle(&name));
       ConnectTransition(map, result, name, simple_flag);
@@ -6721,7 +6797,11 @@ Handle<Map> Map::CopyReplaceDescriptors(Handle<Map> map,
           descriptors->SetValue(i, HeapType::Any());
         }
       }
+      result->InitializeDescriptors(*descriptors,
+                                    LayoutDescriptor::FastPointerLayout());
     }
+  } else {
+    result->InitializeDescriptors(*descriptors, *layout_descriptor);
   }
 #if TRACE_MAPS
   if (FLAG_trace_maps &&
@@ -6740,26 +6820,35 @@ Handle<Map> Map::CopyReplaceDescriptors(Handle<Map> map,
 
 // Since this method is used to rewrite an existing transition tree, it can
 // always insert transitions without checking.
-Handle<Map> Map::CopyInstallDescriptors(Handle<Map> map,
-                                        int new_descriptor,
-                                        Handle<DescriptorArray> descriptors) {
+Handle<Map> Map::CopyInstallDescriptors(
+    Handle<Map> map, int new_descriptor, Handle<DescriptorArray> descriptors,
+    Handle<LayoutDescriptor> full_layout_descriptor) {
   DCHECK(descriptors->IsSortedNoDuplicates());
 
   Handle<Map> result = CopyDropDescriptors(map);
 
-  result->InitializeDescriptors(*descriptors);
+  result->set_instance_descriptors(*descriptors);
   result->SetNumberOfOwnDescriptors(new_descriptor + 1);
 
   int unused_property_fields = map->unused_property_fields();
-  if (descriptors->GetDetails(new_descriptor).type() == FIELD) {
+  PropertyDetails details = descriptors->GetDetails(new_descriptor);
+  if (details.type() == FIELD) {
     unused_property_fields = map->unused_property_fields() - 1;
     if (unused_property_fields < 0) {
       unused_property_fields += JSObject::kFieldsAdded;
     }
   }
-
   result->set_unused_property_fields(unused_property_fields);
 
+  if (FLAG_unbox_double_fields) {
+    Handle<LayoutDescriptor> layout_descriptor =
+        LayoutDescriptor::AppendIfFastOrUseFull(map, details,
+                                                full_layout_descriptor);
+    result->set_layout_descriptor(*layout_descriptor);
+    SLOW_DCHECK(result->layout_descriptor()->IsConsistentWithMap(*result));
+    result->set_visitor_id(StaticVisitorBase::GetVisitorId(*result));
+  }
+
   Handle<Name> name = handle(descriptors->GetKey(new_descriptor));
   ConnectTransition(map, result, name, SIMPLE_PROPERTY_TRANSITION);
 
@@ -6794,7 +6883,9 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
     ConnectElementsTransition(map, new_map);
 
     new_map->set_elements_kind(kind);
-    new_map->InitializeDescriptors(map->instance_descriptors());
+    // The properties did not change, so reuse descriptors.
+    new_map->InitializeDescriptors(map->instance_descriptors(),
+                                   map->layout_descriptor());
     return new_map;
   }
 
@@ -6830,7 +6921,9 @@ Handle<Map> Map::CopyForObserved(Handle<Map> map) {
 
   new_map->set_is_observed();
   if (map->owns_descriptors()) {
-    new_map->InitializeDescriptors(map->instance_descriptors());
+    // The properties did not change, so reuse descriptors.
+    new_map->InitializeDescriptors(map->instance_descriptors(),
+                                   map->layout_descriptor());
   }
 
   if (map->CanHaveMoreTransitions()) {
@@ -6846,8 +6939,9 @@ Handle<Map> Map::Copy(Handle<Map> map, const char* reason) {
   int number_of_own_descriptors = map->NumberOfOwnDescriptors();
   Handle<DescriptorArray> new_descriptors =
       DescriptorArray::CopyUpTo(descriptors, number_of_own_descriptors);
-  return CopyReplaceDescriptors(map, new_descriptors, OMIT_TRANSITION,
-                                MaybeHandle<Name>(), reason,
+  Handle<LayoutDescriptor> new_layout_descriptor = map->GetLayoutDescriptor();
+  return CopyReplaceDescriptors(map, new_descriptors, new_layout_descriptor,
+                                OMIT_TRANSITION, MaybeHandle<Name>(), reason,
                                 SPECIAL_TRANSITION);
 }
 
@@ -6883,9 +6977,10 @@ Handle<Map> Map::CopyForFreeze(Handle<Map> map) {
   Isolate* isolate = map->GetIsolate();
   Handle<DescriptorArray> new_desc = DescriptorArray::CopyUpToAddAttributes(
       handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN);
+  Handle<LayoutDescriptor> new_layout_descriptor = map->GetLayoutDescriptor();
   Handle<Map> new_map = CopyReplaceDescriptors(
-      map, new_desc, INSERT_TRANSITION, isolate->factory()->frozen_symbol(),
-      "CopyForFreeze", SPECIAL_TRANSITION);
+      map, new_desc, new_layout_descriptor, INSERT_TRANSITION,
+      isolate->factory()->frozen_symbol(), "CopyForFreeze", SPECIAL_TRANSITION);
   new_map->freeze();
   new_map->set_is_extensible(false);
   new_map->set_elements_kind(DICTIONARY_ELEMENTS);
@@ -7110,8 +7205,13 @@ Handle<Map> Map::CopyAddDescriptor(Handle<Map> map,
       descriptors, map->NumberOfOwnDescriptors(), 1);
   new_descriptors->Append(descriptor);
 
-  return CopyReplaceDescriptors(map, new_descriptors, flag,
-                                descriptor->GetKey(), "CopyAddDescriptor",
+  Handle<LayoutDescriptor> new_layout_descriptor =
+      FLAG_unbox_double_fields
+          ? LayoutDescriptor::Append(map, descriptor->GetDetails())
+          : map->GetLayoutDescriptor();
+
+  return CopyReplaceDescriptors(map, new_descriptors, new_layout_descriptor,
+                                flag, descriptor->GetKey(), "CopyAddDescriptor",
                                 SIMPLE_PROPERTY_TRANSITION);
 }
 
@@ -7203,13 +7303,16 @@ Handle<Map> Map::CopyReplaceDescriptor(Handle<Map> map,
       descriptors, map->NumberOfOwnDescriptors());
 
   new_descriptors->Replace(insertion_index, descriptor);
+  Handle<LayoutDescriptor> new_layout_descriptor = LayoutDescriptor::New(
+      map, new_descriptors, new_descriptors->number_of_descriptors());
 
   SimpleTransitionFlag simple_flag =
       (insertion_index == descriptors->number_of_descriptors() - 1)
           ? SIMPLE_PROPERTY_TRANSITION
           : PROPERTY_TRANSITION;
-  return CopyReplaceDescriptors(map, new_descriptors, flag, key,
-                                "CopyReplaceDescriptor", simple_flag);
+  return CopyReplaceDescriptors(map, new_descriptors, new_layout_descriptor,
+                                flag, key, "CopyReplaceDescriptor",
+                                simple_flag);
 }
 
 
@@ -8016,8 +8119,7 @@ void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
 }
 
 
-void DescriptorArray::CopyFrom(int index,
-                               DescriptorArray* src,
+void DescriptorArray::CopyFrom(int index, DescriptorArray* src,
                                const WhitenessWitness& witness) {
   Object* value = src->GetValue(index);
   PropertyDetails details = src->GetDetails(index);
index 31d0a16dfb827b7fa14fcab61f14350d1fb48e15..822dc048d33b33ac5198a4f81a1c1b4d54fa8c9d 100644 (file)
@@ -858,8 +858,9 @@ class DictionaryElementsAccessor;
 class ElementsAccessor;
 class FixedArrayBase;
 class GlobalObject;
-class ObjectVisitor;
+class LayoutDescriptor;
 class LookupIterator;
+class ObjectVisitor;
 class StringStream;
 class TypeFeedbackVector;
 class WeakCell;
@@ -934,6 +935,7 @@ template <class C> inline bool Is(Object* obj);
   V(JSContextExtensionObject)      \
   V(JSGeneratorObject)             \
   V(JSModule)                      \
+  V(LayoutDescriptor)              \
   V(Map)                           \
   V(DescriptorArray)               \
   V(TransitionArray)               \
@@ -2063,12 +2065,18 @@ class JSObject: public JSReceiver {
   static void MigrateSlowToFast(Handle<JSObject> object,
                                 int unused_property_fields, const char* reason);
 
+  inline bool IsUnboxedDoubleField(FieldIndex index);
+
   // Access fast-case object properties at index.
   static Handle<Object> FastPropertyAt(Handle<JSObject> object,
                                        Representation representation,
                                        FieldIndex index);
   inline Object* RawFastPropertyAt(FieldIndex index);
+  inline double RawFastDoublePropertyAt(FieldIndex index);
+
   inline void FastPropertyAtPut(FieldIndex index, Object* value);
+  inline void RawFastPropertyAtPut(FieldIndex index, Object* value);
+  inline void RawFastDoublePropertyAtPut(FieldIndex index, double value);
   void WriteToField(int descriptor, Object* value);
 
   // Access to in object properties.
@@ -3132,9 +3140,7 @@ class DescriptorArray: public FixedArray {
 
   // Transfer a complete descriptor from the src descriptor array to this
   // descriptor array.
-  void CopyFrom(int index,
-                DescriptorArray* src,
-                const WhitenessWitness&);
+  void CopyFrom(int index, DescriptorArray* src, const WhitenessWitness&);
 
   inline void Set(int descriptor_number,
                   Descriptor* desc,
@@ -4814,6 +4820,7 @@ TYPED_ARRAYS(FIXED_TYPED_ARRAY_TRAITS)
 
 #undef FIXED_TYPED_ARRAY_TRAITS
 
+
 // DeoptimizationInputData is a fixed array used to hold the deoptimization
 // data for code generated by the Hydrogen/Lithium compiler.  It also
 // contains information about functions that were inlined.  If N different
@@ -5911,7 +5918,19 @@ class Map: public HeapObject {
 
   // [instance descriptors]: describes the object.
   DECL_ACCESSORS(instance_descriptors, DescriptorArray)
-  inline void InitializeDescriptors(DescriptorArray* descriptors);
+
+  // [layout descriptor]: describes the object layout.
+  DECL_ACCESSORS(layout_descriptor, LayoutDescriptor)
+  // |layout descriptor| accessor which can be used from GC.
+  inline LayoutDescriptor* layout_descriptor_gc_safe();
+
+  // |layout descriptor| accessor that returns a handle.
+  inline Handle<LayoutDescriptor> GetLayoutDescriptor();
+
+  inline void UpdateDescriptors(DescriptorArray* descriptors,
+                                LayoutDescriptor* layout_descriptor);
+  inline void InitializeDescriptors(DescriptorArray* descriptors,
+                                    LayoutDescriptor* layout_descriptor);
 
   // [stub cache]: contains stubs compiled for this map.
   DECL_ACCESSORS(code_cache, Object)
@@ -6241,7 +6260,13 @@ class Map: public HeapObject {
       kConstructorOffset + kPointerSize;
   static const int kDescriptorsOffset =
       kTransitionsOrBackPointerOffset + kPointerSize;
+#if V8_DOUBLE_FIELDS_UNBOXING
+  static const int kLayoutDecriptorOffset = kDescriptorsOffset + kPointerSize;
+  static const int kCodeCacheOffset = kLayoutDecriptorOffset + kPointerSize;
+#else
+  static const int kLayoutDecriptorOffset = 1;  // Must not be ever accessed.
   static const int kCodeCacheOffset = kDescriptorsOffset + kPointerSize;
+#endif
   static const int kDependentCodeOffset = kCodeCacheOffset + kPointerSize;
   static const int kSize = kDependentCodeOffset + kPointerSize;
 
@@ -6319,11 +6344,18 @@ class Map: public HeapObject {
   // The "shared" flags of both this map and |other| are ignored.
   bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode);
 
+  // Returns true if given field is unboxed double.
+  inline bool IsUnboxedDoubleField(FieldIndex index);
+
 #if TRACE_MAPS
   static void TraceTransition(const char* what, Map* from, Map* to, Name* name);
   static void TraceAllTransitions(Map* map);
 #endif
 
+  static inline Handle<Map> CopyInstallDescriptorsForTesting(
+      Handle<Map> map, int new_descriptor, Handle<DescriptorArray> descriptors,
+      Handle<LayoutDescriptor> layout_descriptor);
+
  private:
   static void ConnectElementsTransition(Handle<Map> parent, Handle<Map> child);
   static void ConnectTransition(Handle<Map> parent, Handle<Map> child,
@@ -6335,18 +6367,17 @@ class Map: public HeapObject {
                                      Handle<DescriptorArray> descriptors,
                                      Descriptor* descriptor);
   static Handle<Map> CopyInstallDescriptors(
-      Handle<Map> map,
-      int new_descriptor,
-      Handle<DescriptorArray> descriptors);
+      Handle<Map> map, int new_descriptor, Handle<DescriptorArray> descriptors,
+      Handle<LayoutDescriptor> layout_descriptor);
   static Handle<Map> CopyAddDescriptor(Handle<Map> map,
                                        Descriptor* descriptor,
                                        TransitionFlag flag);
-  static Handle<Map> CopyReplaceDescriptors(Handle<Map> map,
-                                            Handle<DescriptorArray> descriptors,
-                                            TransitionFlag flag,
-                                            MaybeHandle<Name> maybe_name,
-                                            const char* reason,
-                                            SimpleTransitionFlag simple_flag);
+  static Handle<Map> CopyReplaceDescriptors(
+      Handle<Map> map, Handle<DescriptorArray> descriptors,
+      Handle<LayoutDescriptor> layout_descriptor, TransitionFlag flag,
+      MaybeHandle<Name> maybe_name, const char* reason,
+      SimpleTransitionFlag simple_flag);
+
   static Handle<Map> CopyReplaceDescriptor(Handle<Map> map,
                                            Handle<DescriptorArray> descriptors,
                                            Descriptor* descriptor,
@@ -6376,7 +6407,8 @@ class Map: public HeapObject {
   void DeprecateTransitionTree();
   void DeprecateTarget(PropertyType type, Name* key,
                        PropertyAttributes attributes,
-                       DescriptorArray* new_descriptors);
+                       DescriptorArray* new_descriptors,
+                       LayoutDescriptor* new_layout_descriptor);
 
   Map* FindLastMatchMap(int verbatim, int length, DescriptorArray* descriptors);
 
index 61fecedd1bf265115cd36d9f8132e76425f979a3..39f633a49968156413e1f7a8c0b7229e70646163 100644 (file)
@@ -248,6 +248,8 @@ class PropertyDetails BASE_EMBEDDED {
     return FieldIndexField::decode(value_);
   }
 
+  inline int field_width_in_words() const;
+
   inline PropertyDetails AsDeleted() const;
 
   static bool IsValidIndex(int index) {
index b2a736d7a5bb41e07532366ac815da3693bcbace..c83b247009dc892984ed7c166f79ef20207cc1c1 100644 (file)
@@ -1400,9 +1400,8 @@ RUNTIME_FUNCTION(Runtime_LoadMutableDouble) {
     RUNTIME_ASSERT(field_index.outobject_array_index() <
                    object->properties()->length());
   }
-  Handle<Object> raw_value(object->RawFastPropertyAt(field_index), isolate);
-  RUNTIME_ASSERT(raw_value->IsMutableHeapNumber());
-  return *Object::WrapForRead(isolate, raw_value, Representation::Double());
+  return *JSObject::FastPropertyAt(object, Representation::Double(),
+                                   field_index);
 }
 
 
index 2101460f1e22d3e886eb23245ab2bbe11a3693f0..8c9c5bb3692da0d6d2a1ea3cdf2206273709b089 100644 (file)
@@ -351,8 +351,13 @@ void StringStream::PrintUsingMap(JSObject* js_object) {
         }
         Add(": ");
         FieldIndex index = FieldIndex::ForDescriptor(map, i);
-        Object* value = js_object->RawFastPropertyAt(index);
-        Add("%o\n", value);
+        if (js_object->IsUnboxedDoubleField(index)) {
+          double value = js_object->RawFastDoublePropertyAt(index);
+          Add("<unboxed double> %.16g\n", value);
+        } else {
+          Object* value = js_object->RawFastPropertyAt(index);
+          Add("%o\n", value);
+        }
       }
     }
   }
index 8ec1af59b6e7ed353a0c29fd205d3f34d77c4e4a..4904360bb3dae41070e940a41523e868057204fc 100644 (file)
@@ -2976,6 +2976,7 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
 
   Register object = ToRegister(instr->object());
   if (instr->hydrogen()->representation().IsDouble()) {
+    DCHECK(access.IsInobject());
     XMMRegister result = ToDoubleRegister(instr->result());
     __ movsd(result, FieldOperand(object, offset));
     return;
@@ -4122,7 +4123,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
   DCHECK(!representation.IsSmi() ||
          !instr->value()->IsConstantOperand() ||
          IsInteger32Constant(LConstantOperand::cast(instr->value())));
-  if (representation.IsDouble()) {
+  if (!FLAG_unbox_double_fields && representation.IsDouble()) {
     DCHECK(access.IsInobject());
     DCHECK(!hinstr->has_transition());
     DCHECK(!hinstr->NeedsWriteBarrier());
@@ -4172,7 +4173,12 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
 
   Operand operand = FieldOperand(write_register, offset);
 
-  if (instr->value()->IsRegister()) {
+  if (FLAG_unbox_double_fields && representation.IsDouble()) {
+    DCHECK(access.IsInobject());
+    XMMRegister value = ToDoubleRegister(instr->value());
+    __ movsd(operand, value);
+
+  } else if (instr->value()->IsRegister()) {
     Register value = ToRegister(instr->value());
     __ Store(operand, value, representation);
   } else {
index b05f0a7ea23f6606bc048c90a269d2a6ccc75de9..97877880d8933d9524546ba303ef0b94b2d8964f 100644 (file)
         'test-transitions.cc',
         'test-types.cc',
         'test-unbound-queue.cc',
+        'test-unboxed-doubles.cc',
         'test-unique.cc',
         'test-unscopables-hidden-prototype.cc',
         'test-utils.cc',
index 543a89dcb18990f31d2fc8b94cb5be23b211245b..a852f06839c54fdf3b81f57aaa2e6d57712eae60 100644 (file)
@@ -2476,12 +2476,21 @@ TEST(OptimizedPretenuringMixedInObjectProperties) {
   FieldIndex idx1 = FieldIndex::ForPropertyIndex(o->map(), 0);
   FieldIndex idx2 = FieldIndex::ForPropertyIndex(o->map(), 1);
   CHECK(CcTest::heap()->InOldPointerSpace(o->RawFastPropertyAt(idx1)));
-  CHECK(CcTest::heap()->InOldDataSpace(o->RawFastPropertyAt(idx2)));
+  if (!o->IsUnboxedDoubleField(idx2)) {
+    CHECK(CcTest::heap()->InOldDataSpace(o->RawFastPropertyAt(idx2)));
+  } else {
+    CHECK_EQ(1.1, o->RawFastDoublePropertyAt(idx2));
+  }
 
   JSObject* inner_object =
       reinterpret_cast<JSObject*>(o->RawFastPropertyAt(idx1));
   CHECK(CcTest::heap()->InOldPointerSpace(inner_object));
-  CHECK(CcTest::heap()->InOldDataSpace(inner_object->RawFastPropertyAt(idx1)));
+  if (!inner_object->IsUnboxedDoubleField(idx1)) {
+    CHECK(
+        CcTest::heap()->InOldDataSpace(inner_object->RawFastPropertyAt(idx1)));
+  } else {
+    CHECK_EQ(2.2, inner_object->RawFastDoublePropertyAt(idx1));
+  }
   CHECK(CcTest::heap()->InOldPointerSpace(
       inner_object->RawFastPropertyAt(idx2)));
 }
diff --git a/test/cctest/test-unboxed-doubles.cc b/test/cctest/test-unboxed-doubles.cc
new file mode 100644 (file)
index 0000000..2abf3c2
--- /dev/null
@@ -0,0 +1,668 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdlib.h>
+#include <utility>
+
+#include "src/v8.h"
+
+#include "src/compilation-cache.h"
+#include "src/execution.h"
+#include "src/factory.h"
+#include "src/global-handles.h"
+#include "src/ic/ic.h"
+#include "src/macro-assembler.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::base;
+using namespace v8::internal;
+
+#if (V8_DOUBLE_FIELDS_UNBOXING)
+
+
+static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) {
+  if (obj->IsUnboxedDoubleField(field_index)) {
+    return obj->RawFastDoublePropertyAt(field_index);
+  } else {
+    Object* value = obj->RawFastPropertyAt(field_index);
+    DCHECK(value->IsMutableHeapNumber());
+    return HeapNumber::cast(value)->value();
+  }
+}
+
+
+enum PropertyKind {
+  PROP_CONSTANT,
+  PROP_SMI,
+  PROP_DOUBLE,
+  PROP_TAGGED,
+  PROP_KIND_NUMBER,
+};
+
+static Representation representations[PROP_KIND_NUMBER] = {
+    Representation::None(), Representation::Smi(), Representation::Double(),
+    Representation::Tagged()};
+
+
+static Handle<DescriptorArray> CreateDescriptorArray(Isolate* isolate,
+                                                     PropertyKind* props,
+                                                     int kPropsCount) {
+  Factory* factory = isolate->factory();
+
+  Handle<String> func_name = factory->InternalizeUtf8String("func");
+  Handle<JSFunction> func = factory->NewFunction(func_name);
+
+  Handle<DescriptorArray> descriptors =
+      DescriptorArray::Allocate(isolate, 0, kPropsCount);
+
+  int next_field_offset = 0;
+  for (int i = 0; i < kPropsCount; i++) {
+    EmbeddedVector<char, 64> buffer;
+    SNPrintF(buffer, "prop%d", i);
+    Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+
+    PropertyKind kind = props[i];
+
+    if (kind == PROP_CONSTANT) {
+      ConstantDescriptor d(name, func, NONE);
+      descriptors->Append(&d);
+
+    } else {
+      FieldDescriptor f(name, next_field_offset, NONE, representations[kind]);
+      next_field_offset += f.GetDetails().field_width_in_words();
+      descriptors->Append(&f);
+    }
+  }
+  return descriptors;
+}
+
+
+TEST(LayoutDescriptorBasicFast) {
+  CcTest::InitializeVM();
+  v8::HandleScope scope(CcTest::isolate());
+
+  LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout();
+
+  CHECK(!layout_desc->IsSlowLayout());
+  CHECK(layout_desc->IsFastPointerLayout());
+  CHECK_EQ(kSmiValueSize, layout_desc->capacity());
+
+  for (int i = 0; i < kSmiValueSize + 13; i++) {
+    CHECK_EQ(true, layout_desc->IsTagged(i));
+  }
+  CHECK_EQ(true, layout_desc->IsTagged(-1));
+  CHECK_EQ(true, layout_desc->IsTagged(-12347));
+  CHECK_EQ(true, layout_desc->IsTagged(15635));
+  CHECK(layout_desc->IsFastPointerLayout());
+
+  for (int i = 0; i < kSmiValueSize; i++) {
+    layout_desc = layout_desc->SetTaggedForTesting(i, false);
+    CHECK_EQ(false, layout_desc->IsTagged(i));
+    layout_desc = layout_desc->SetTaggedForTesting(i, true);
+    CHECK_EQ(true, layout_desc->IsTagged(i));
+  }
+  CHECK(layout_desc->IsFastPointerLayout());
+}
+
+
+TEST(LayoutDescriptorBasicSlow) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  v8::HandleScope scope(CcTest::isolate());
+
+  Handle<LayoutDescriptor> layout_descriptor;
+  const int kPropsCount = kSmiValueSize * 3;
+  PropertyKind props[kPropsCount];
+  for (int i = 0; i < kPropsCount; i++) {
+    // All properties tagged.
+    props[i] = PROP_TAGGED;
+  }
+
+  {
+    Handle<DescriptorArray> descriptors =
+        CreateDescriptorArray(isolate, props, kPropsCount);
+
+    Handle<Map> map = Map::Create(isolate, kPropsCount);
+
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    CHECK_EQ(kSmiValueSize, layout_descriptor->capacity());
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+
+  props[0] = PROP_DOUBLE;
+  props[kPropsCount - 1] = PROP_DOUBLE;
+
+  Handle<DescriptorArray> descriptors =
+      CreateDescriptorArray(isolate, props, kPropsCount);
+
+  {
+    int inobject_properties = kPropsCount - 1;
+    Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+    // Should be fast as the only double property is the first one.
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    CHECK(!layout_descriptor->IsSlowLayout());
+    CHECK(!layout_descriptor->IsFastPointerLayout());
+
+    CHECK_EQ(false, layout_descriptor->IsTagged(0));
+    for (int i = 1; i < kPropsCount; i++) {
+      CHECK_EQ(true, layout_descriptor->IsTagged(i));
+    }
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+
+  {
+    int inobject_properties = kPropsCount;
+    Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    CHECK(layout_descriptor->IsSlowLayout());
+    CHECK(!layout_descriptor->IsFastPointerLayout());
+    CHECK(layout_descriptor->capacity() > kSmiValueSize);
+
+    CHECK_EQ(false, layout_descriptor->IsTagged(0));
+    CHECK_EQ(false, layout_descriptor->IsTagged(kPropsCount - 1));
+    for (int i = 1; i < kPropsCount - 1; i++) {
+      CHECK_EQ(true, layout_descriptor->IsTagged(i));
+    }
+
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+
+    // Here we have truly slow layout descriptor, so play with the bits.
+    CHECK_EQ(true, layout_descriptor->IsTagged(-1));
+    CHECK_EQ(true, layout_descriptor->IsTagged(-12347));
+    CHECK_EQ(true, layout_descriptor->IsTagged(15635));
+
+    LayoutDescriptor* layout_desc = *layout_descriptor;
+    // Play with the bits but leave it in consistent state with map at the end.
+    for (int i = 1; i < kPropsCount - 1; i++) {
+      layout_desc = layout_desc->SetTaggedForTesting(i, false);
+      CHECK_EQ(false, layout_desc->IsTagged(i));
+      layout_desc = layout_desc->SetTaggedForTesting(i, true);
+      CHECK_EQ(true, layout_desc->IsTagged(i));
+    }
+    CHECK(layout_desc->IsSlowLayout());
+    CHECK(!layout_desc->IsFastPointerLayout());
+
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+}
+
+
+TEST(LayoutDescriptorCreateNewFast) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  v8::HandleScope scope(CcTest::isolate());
+
+  Handle<LayoutDescriptor> layout_descriptor;
+  PropertyKind props[] = {
+      PROP_CONSTANT,
+      PROP_TAGGED,  // field #0
+      PROP_CONSTANT,
+      PROP_DOUBLE,  // field #1
+      PROP_CONSTANT,
+      PROP_TAGGED,  // field #2
+      PROP_CONSTANT,
+  };
+  const int kPropsCount = arraysize(props);
+
+  Handle<DescriptorArray> descriptors =
+      CreateDescriptorArray(isolate, props, kPropsCount);
+
+  {
+    Handle<Map> map = Map::Create(isolate, 0);
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+
+  {
+    Handle<Map> map = Map::Create(isolate, 1);
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+
+  {
+    Handle<Map> map = Map::Create(isolate, 2);
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    CHECK(!layout_descriptor->IsSlowLayout());
+    CHECK_EQ(true, layout_descriptor->IsTagged(0));
+    CHECK_EQ(false, layout_descriptor->IsTagged(1));
+    CHECK_EQ(true, layout_descriptor->IsTagged(2));
+    CHECK_EQ(true, layout_descriptor->IsTagged(125));
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+}
+
+
+TEST(LayoutDescriptorCreateNewSlow) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  v8::HandleScope scope(CcTest::isolate());
+
+  Handle<LayoutDescriptor> layout_descriptor;
+  const int kPropsCount = kSmiValueSize * 3;
+  PropertyKind props[kPropsCount];
+  for (int i = 0; i < kPropsCount; i++) {
+    props[i] = static_cast<PropertyKind>(i % PROP_KIND_NUMBER);
+  }
+
+  Handle<DescriptorArray> descriptors =
+      CreateDescriptorArray(isolate, props, kPropsCount);
+
+  {
+    Handle<Map> map = Map::Create(isolate, 0);
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+
+  {
+    Handle<Map> map = Map::Create(isolate, 1);
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+
+  {
+    Handle<Map> map = Map::Create(isolate, 2);
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    CHECK(!layout_descriptor->IsSlowLayout());
+    CHECK_EQ(true, layout_descriptor->IsTagged(0));
+    CHECK_EQ(false, layout_descriptor->IsTagged(1));
+    CHECK_EQ(true, layout_descriptor->IsTagged(2));
+    CHECK_EQ(true, layout_descriptor->IsTagged(125));
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  }
+
+  {
+    int inobject_properties = kPropsCount / 2;
+    Handle<Map> map = Map::Create(isolate, inobject_properties);
+    layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+    CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+    CHECK(layout_descriptor->IsSlowLayout());
+    for (int i = 0; i < inobject_properties; i++) {
+      // PROP_DOUBLE has index 1 among FIELD properties.
+      const bool tagged = (i % (PROP_KIND_NUMBER - 1)) != 1;
+      CHECK_EQ(tagged, layout_descriptor->IsTagged(i));
+    }
+    // Every property after inobject_properties must be tagged.
+    for (int i = inobject_properties; i < kPropsCount; i++) {
+      CHECK_EQ(true, layout_descriptor->IsTagged(i));
+    }
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+    DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+
+    // Now test LayoutDescriptor::cast_gc_safe().
+    Handle<LayoutDescriptor> layout_descriptor_copy =
+        LayoutDescriptor::New(map, descriptors, kPropsCount);
+
+    LayoutDescriptor* layout_desc = *layout_descriptor;
+    CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
+    CHECK_EQ(layout_desc, LayoutDescriptor::cast_gc_safe(layout_desc));
+    CHECK(layout_descriptor->IsFixedTypedArrayBase());
+    // Now make it look like a forwarding pointer to layout_descriptor_copy.
+    MapWord map_word = layout_desc->map_word();
+    CHECK(!map_word.IsForwardingAddress());
+    layout_desc->set_map_word(
+        MapWord::FromForwardingAddress(*layout_descriptor_copy));
+    CHECK(layout_desc->map_word().IsForwardingAddress());
+    CHECK_EQ(*layout_descriptor_copy,
+             LayoutDescriptor::cast_gc_safe(layout_desc));
+
+    // Restore it back.
+    layout_desc->set_map_word(map_word);
+    CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
+  }
+}
+
+
+static Handle<LayoutDescriptor> TestLayoutDescriptorAppend(
+    Isolate* isolate, int inobject_properties, PropertyKind* props,
+    int kPropsCount) {
+  Factory* factory = isolate->factory();
+
+  Handle<String> func_name = factory->InternalizeUtf8String("func");
+  Handle<JSFunction> func = factory->NewFunction(func_name);
+
+  Handle<DescriptorArray> descriptors =
+      DescriptorArray::Allocate(isolate, 0, kPropsCount);
+
+  Handle<Map> map = Map::Create(isolate, inobject_properties);
+  map->InitializeDescriptors(*descriptors,
+                             LayoutDescriptor::FastPointerLayout());
+
+  int next_field_offset = 0;
+  for (int i = 0; i < kPropsCount; i++) {
+    EmbeddedVector<char, 64> buffer;
+    SNPrintF(buffer, "prop%d", i);
+    Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+
+    Handle<LayoutDescriptor> layout_descriptor;
+    PropertyKind kind = props[i];
+    if (kind == PROP_CONSTANT) {
+      ConstantDescriptor d(name, func, NONE);
+      layout_descriptor = LayoutDescriptor::Append(map, d.GetDetails());
+      descriptors->Append(&d);
+
+    } else {
+      FieldDescriptor f(name, next_field_offset, NONE, representations[kind]);
+      int field_width_in_words = f.GetDetails().field_width_in_words();
+      next_field_offset += field_width_in_words;
+      layout_descriptor = LayoutDescriptor::Append(map, f.GetDetails());
+      descriptors->Append(&f);
+
+      int field_index = f.GetDetails().field_index();
+      bool is_inobject = field_index < map->inobject_properties();
+      for (int bit = 0; bit < field_width_in_words; bit++) {
+        CHECK_EQ(is_inobject && (kind == PROP_DOUBLE),
+                 !layout_descriptor->IsTagged(field_index + bit));
+      }
+      CHECK(layout_descriptor->IsTagged(next_field_offset));
+    }
+    map->InitializeDescriptors(*descriptors, *layout_descriptor);
+  }
+  Handle<LayoutDescriptor> layout_descriptor(map->layout_descriptor(), isolate);
+  DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  return layout_descriptor;
+}
+
+
+TEST(LayoutDescriptorAppend) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  v8::HandleScope scope(CcTest::isolate());
+
+  Handle<LayoutDescriptor> layout_descriptor;
+  const int kPropsCount = kSmiValueSize * 3;
+  PropertyKind props[kPropsCount];
+  for (int i = 0; i < kPropsCount; i++) {
+    props[i] = static_cast<PropertyKind>(i % PROP_KIND_NUMBER);
+  }
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2,
+                                                 props, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+}
+
+
+TEST(LayoutDescriptorAppendAllDoubles) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  v8::HandleScope scope(CcTest::isolate());
+
+  Handle<LayoutDescriptor> layout_descriptor;
+  const int kPropsCount = kSmiValueSize * 3;
+  PropertyKind props[kPropsCount];
+  for (int i = 0; i < kPropsCount; i++) {
+    props[i] = PROP_DOUBLE;
+  }
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize + 1,
+                                                 props, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2,
+                                                 props, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  layout_descriptor =
+      TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  {
+    // Ensure layout descriptor switches into slow mode at the right moment.
+    layout_descriptor =
+        TestLayoutDescriptorAppend(isolate, kPropsCount, props, kSmiValueSize);
+    CHECK(!layout_descriptor->IsSlowLayout());
+
+    layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props,
+                                                   kSmiValueSize + 1);
+    CHECK(layout_descriptor->IsSlowLayout());
+  }
+}
+
+
+static Handle<LayoutDescriptor> TestLayoutDescriptorAppendIfFastOrUseFull(
+    Isolate* isolate, int inobject_properties,
+    Handle<DescriptorArray> descriptors, int number_of_descriptors) {
+  Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+  Handle<LayoutDescriptor> full_layout_descriptor = LayoutDescriptor::New(
+      map, descriptors, descriptors->number_of_descriptors());
+
+  int nof = 0;
+  bool switched_to_slow_mode = false;
+
+  for (int i = 0; i < number_of_descriptors; i++) {
+    PropertyDetails details = descriptors->GetDetails(i);
+
+    // This method calls LayoutDescriptor::AppendIfFastOrUseFull() internally
+    // and does all the required map-descriptors related book keeping.
+    map = Map::CopyInstallDescriptorsForTesting(map, i, descriptors,
+                                                full_layout_descriptor);
+
+    LayoutDescriptor* layout_desc = map->layout_descriptor();
+
+    if (layout_desc->IsSlowLayout()) {
+      switched_to_slow_mode = true;
+      CHECK_EQ(*full_layout_descriptor, layout_desc);
+    } else {
+      CHECK(!switched_to_slow_mode);
+      if (details.type() == FIELD) {
+        nof++;
+        int field_index = details.field_index();
+        int field_width_in_words = details.field_width_in_words();
+
+        bool is_inobject = field_index < map->inobject_properties();
+        for (int bit = 0; bit < field_width_in_words; bit++) {
+          CHECK_EQ(is_inobject && details.representation().IsDouble(),
+                   !layout_desc->IsTagged(field_index + bit));
+        }
+        CHECK(layout_desc->IsTagged(field_index + field_width_in_words));
+      }
+    }
+    DCHECK(map->layout_descriptor()->IsConsistentWithMap(*map));
+  }
+
+  Handle<LayoutDescriptor> layout_descriptor = map->GetLayoutDescriptor();
+  DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+  return layout_descriptor;
+}
+
+
+TEST(LayoutDescriptorAppendIfFastOrUseFull) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  v8::HandleScope scope(CcTest::isolate());
+
+  Handle<LayoutDescriptor> layout_descriptor;
+  const int kPropsCount = kSmiValueSize * 3;
+  PropertyKind props[kPropsCount];
+  for (int i = 0; i < kPropsCount; i++) {
+    props[i] = static_cast<PropertyKind>(i % PROP_KIND_NUMBER);
+  }
+  Handle<DescriptorArray> descriptors =
+      CreateDescriptorArray(isolate, props, kPropsCount);
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, 0, descriptors, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, 13, descriptors, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, kSmiValueSize, descriptors, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, kSmiValueSize * 2, descriptors, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, kPropsCount, descriptors, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+}
+
+
+TEST(LayoutDescriptorAppendIfFastOrUseFullAllDoubles) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  v8::HandleScope scope(CcTest::isolate());
+
+  Handle<LayoutDescriptor> layout_descriptor;
+  const int kPropsCount = kSmiValueSize * 3;
+  PropertyKind props[kPropsCount];
+  for (int i = 0; i < kPropsCount; i++) {
+    props[i] = PROP_DOUBLE;
+  }
+  Handle<DescriptorArray> descriptors =
+      CreateDescriptorArray(isolate, props, kPropsCount);
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, 0, descriptors, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, 13, descriptors, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, kSmiValueSize, descriptors, kPropsCount);
+  CHECK(!layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, kSmiValueSize + 1, descriptors, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, kSmiValueSize * 2, descriptors, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+      isolate, kPropsCount, descriptors, kPropsCount);
+  CHECK(layout_descriptor->IsSlowLayout());
+
+  {
+    // Ensure layout descriptor switches into slow mode at the right moment.
+    layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+        isolate, kPropsCount, descriptors, kSmiValueSize);
+    CHECK(!layout_descriptor->IsSlowLayout());
+
+    layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+        isolate, kPropsCount, descriptors, kSmiValueSize + 1);
+    CHECK(layout_descriptor->IsSlowLayout());
+  }
+}
+
+
+TEST(StoreBufferScanOnScavenge) {
+  CcTest::InitializeVM();
+  Isolate* isolate = CcTest::i_isolate();
+  Factory* factory = isolate->factory();
+  v8::HandleScope scope(CcTest::isolate());
+
+  CompileRun(
+      "function A() {"
+      "  this.x = 42.5;"
+      "  this.o = {};"
+      "};"
+      "var o = new A();");
+
+  Handle<String> obj_name = factory->InternalizeUtf8String("o");
+
+  Handle<Object> obj_value =
+      Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked();
+  CHECK(obj_value->IsJSObject());
+  Handle<JSObject> obj = Handle<JSObject>::cast(obj_value);
+
+  {
+    // Ensure the object is properly set up.
+    Map* map = obj->map();
+    DescriptorArray* descriptors = map->instance_descriptors();
+    CHECK(map->NumberOfOwnDescriptors() == 2);
+    CHECK(descriptors->GetDetails(0).representation().IsDouble());
+    CHECK(descriptors->GetDetails(1).representation().IsHeapObject());
+    FieldIndex field_index = FieldIndex::ForDescriptor(map, 0);
+    CHECK(field_index.is_inobject() && field_index.is_double());
+    CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
+    CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
+  }
+  CHECK(isolate->heap()->new_space()->Contains(*obj));
+
+  // Trigger GCs so that the newly allocated object moves to old gen.
+  CcTest::heap()->CollectGarbage(i::NEW_SPACE);  // in survivor space now
+  CcTest::heap()->CollectGarbage(i::NEW_SPACE);  // in old gen now
+
+  CHECK(isolate->heap()->old_pointer_space()->Contains(*obj));
+
+  // Create temp object in the new space.
+  Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS, NOT_TENURED);
+  CHECK(isolate->heap()->new_space()->Contains(*temp));
+
+  // Construct a double value that looks like a pointer to the new space object
+  // and store it into the obj.
+  Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize;
+  double boom_value = bit_cast<double>(fake_object);
+
+  FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0);
+  obj->FastPropertyAtPut(field_index,
+                         *factory->NewHeapNumber(boom_value, MUTABLE));
+
+  // Enforce scan on scavenge for the obj's page.
+  MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+  chunk->set_scan_on_scavenge(true);
+
+  // Trigger GCs and force evacuation. Should not crash there.
+  CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+  CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index));
+}
+
+#endif
index 0ba4e5aa899ad134649bc0deeeaef87556909aec..c0db914a4b650d680360d81a1ba8b1a27c9582e5 100644 (file)
         '../../src/jsregexp-inl.h',
         '../../src/jsregexp.cc',
         '../../src/jsregexp.h',
+       '../../src/layout-descriptor-inl.h',
+       '../../src/layout-descriptor.cc',
+       '../../src/layout-descriptor.h',
         '../../src/list-inl.h',
         '../../src/list.h',
         '../../src/lithium-allocator-inl.h',