| Index: src/compiler/js-create-lowering.cc
 | 
| diff --git a/src/compiler/js-create-lowering.cc b/src/compiler/js-create-lowering.cc
 | 
| index 27c7604c45910f041ab948ae141eb580fc16960f..b2445455b080ac54624233bfac9e786051893344 100644
 | 
| --- a/src/compiler/js-create-lowering.cc
 | 
| +++ b/src/compiler/js-create-lowering.cc
 | 
| @@ -4,6 +4,7 @@
 | 
|  
 | 
|  #include "src/compiler/js-create-lowering.h"
 | 
|  
 | 
| +#include "src/allocation-site-scopes.h"
 | 
|  #include "src/code-factory.h"
 | 
|  #include "src/compilation-dependencies.h"
 | 
|  #include "src/compiler/access-builder.h"
 | 
| @@ -13,6 +14,7 @@
 | 
|  #include "src/compiler/linkage.h"
 | 
|  #include "src/compiler/node.h"
 | 
|  #include "src/compiler/node-properties.h"
 | 
| +#include "src/compiler/operator-properties.h"
 | 
|  #include "src/compiler/simplified-operator.h"
 | 
|  #include "src/compiler/state-values-utils.h"
 | 
|  
 | 
| @@ -127,6 +129,73 @@ const int kElementLoopUnrollLimit = 16;
 | 
|  const int kFunctionContextAllocationLimit = 16;
 | 
|  const int kBlockContextAllocationLimit = 16;
 | 
|  
 | 
| +// Determines whether the given array or object literal boilerplate satisfies
 | 
| +// all limits to be considered for fast deep-copying and computes the total
 | 
| +// size of all objects that are part of the graph.
 | 
| +bool IsFastLiteral(Handle<JSObject> boilerplate, int max_depth,
 | 
| +                   int* max_properties) {
 | 
| +  DCHECK_GE(max_depth, 0);
 | 
| +  DCHECK_GE(*max_properties, 0);
 | 
| +
 | 
| +  // Make sure the boilerplate map is not deprecated.
 | 
| +  if (!JSObject::TryMigrateInstance(boilerplate)) return false;
 | 
| +
 | 
| +  // Check for too deep nesting.
 | 
| +  if (max_depth == 0) return false;
 | 
| +
 | 
| +  // Check the elements.
 | 
| +  Isolate* const isolate = boilerplate->GetIsolate();
 | 
| +  Handle<FixedArrayBase> elements(boilerplate->elements(), isolate);
 | 
| +  if (elements->length() > 0 &&
 | 
| +      elements->map() != isolate->heap()->fixed_cow_array_map()) {
 | 
| +    if (boilerplate->HasFastSmiOrObjectElements()) {
 | 
| +      Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
 | 
| +      int length = elements->length();
 | 
| +      for (int i = 0; i < length; i++) {
 | 
| +        if ((*max_properties)-- == 0) return false;
 | 
| +        Handle<Object> value(fast_elements->get(i), isolate);
 | 
| +        if (value->IsJSObject()) {
 | 
| +          Handle<JSObject> value_object = Handle<JSObject>::cast(value);
 | 
| +          if (!IsFastLiteral(value_object, max_depth - 1, max_properties)) {
 | 
| +            return false;
 | 
| +          }
 | 
| +        }
 | 
| +      }
 | 
| +    } else if (!boilerplate->HasFastDoubleElements()) {
 | 
| +      return false;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // TODO(turbofan): Do we want to support out-of-object properties?
 | 
| +  Handle<FixedArray> properties(boilerplate->properties(), isolate);
 | 
| +  if (properties->length() > 0) return false;
 | 
| +
 | 
| +  // Check the in-object properties.
 | 
| +  Handle<DescriptorArray> descriptors(
 | 
| +      boilerplate->map()->instance_descriptors(), isolate);
 | 
| +  int limit = boilerplate->map()->NumberOfOwnDescriptors();
 | 
| +  for (int i = 0; i < limit; i++) {
 | 
| +    PropertyDetails details = descriptors->GetDetails(i);
 | 
| +    if (details.type() != DATA) continue;
 | 
| +    if ((*max_properties)-- == 0) return false;
 | 
| +    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, max_depth - 1, max_properties)) {
 | 
| +        return false;
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +// Maximum depth and total number of elements and properties for literal
 | 
| +// graphs to be considered for fast deep-copying.
 | 
| +const int kMaxFastLiteralDepth = 3;
 | 
| +const int kMaxFastLiteralProperties = 8;
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
|  Reduction JSCreateLowering::Reduce(Node* node) {
 | 
| @@ -139,6 +208,9 @@ Reduction JSCreateLowering::Reduce(Node* node) {
 | 
|        return ReduceJSCreateArray(node);
 | 
|      case IrOpcode::kJSCreateIterResultObject:
 | 
|        return ReduceJSCreateIterResultObject(node);
 | 
| +    case IrOpcode::kJSCreateLiteralArray:
 | 
| +    case IrOpcode::kJSCreateLiteralObject:
 | 
| +      return ReduceJSCreateLiteral(node);
 | 
|      case IrOpcode::kJSCreateFunctionContext:
 | 
|        return ReduceJSCreateFunctionContext(node);
 | 
|      case IrOpcode::kJSCreateWithContext:
 | 
| @@ -504,6 +576,36 @@ Reduction JSCreateLowering::ReduceJSCreateIterResultObject(Node* node) {
 | 
|    return Changed(node);
 | 
|  }
 | 
|  
 | 
| +Reduction JSCreateLowering::ReduceJSCreateLiteral(Node* node) {
 | 
| +  DCHECK(node->opcode() == IrOpcode::kJSCreateLiteralArray ||
 | 
| +         node->opcode() == IrOpcode::kJSCreateLiteralObject);
 | 
| +  CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
 | 
| +  Node* effect = NodeProperties::GetEffectInput(node);
 | 
| +  Node* control = NodeProperties::GetControlInput(node);
 | 
| +
 | 
| +  Handle<LiteralsArray> literals_array;
 | 
| +  if (GetSpecializationLiterals(node).ToHandle(&literals_array)) {
 | 
| +    Handle<Object> literal(literals_array->literal(p.index()), isolate());
 | 
| +    if (literal->IsAllocationSite()) {
 | 
| +      Handle<AllocationSite> site = Handle<AllocationSite>::cast(literal);
 | 
| +      Handle<JSObject> boilerplate(JSObject::cast(site->transition_info()),
 | 
| +                                   isolate());
 | 
| +      int max_properties = kMaxFastLiteralProperties;
 | 
| +      if (IsFastLiteral(boilerplate, kMaxFastLiteralDepth, &max_properties)) {
 | 
| +        AllocationSiteUsageContext site_context(isolate(), site, false);
 | 
| +        site_context.EnterNewScope();
 | 
| +        Node* value = effect =
 | 
| +            AllocateFastLiteral(effect, control, boilerplate, &site_context);
 | 
| +        site_context.ExitScope(site, boilerplate);
 | 
| +        ReplaceWithValue(node, value, effect, control);
 | 
| +        return Replace(value);
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return NoChange();
 | 
| +}
 | 
| +
 | 
|  Reduction JSCreateLowering::ReduceJSCreateFunctionContext(Node* node) {
 | 
|    DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode());
 | 
|    int slot_count = OpParameter<int>(node->op());
 | 
| @@ -754,6 +856,225 @@ Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
 | 
|    return a.Finish();
 | 
|  }
 | 
|  
 | 
| +Node* JSCreateLowering::AllocateFastLiteral(
 | 
| +    Node* effect, Node* control, Handle<JSObject> boilerplate,
 | 
| +    AllocationSiteUsageContext* site_context) {
 | 
| +  Handle<AllocationSite> current_site(*site_context->current(), isolate());
 | 
| +  dependencies()->AssumeTransitionStable(current_site);
 | 
| +
 | 
| +  PretenureFlag pretenure = NOT_TENURED;
 | 
| +  if (FLAG_allocation_site_pretenuring) {
 | 
| +    Handle<AllocationSite> top_site(*site_context->top(), isolate());
 | 
| +    pretenure = top_site->GetPretenureMode();
 | 
| +    if (current_site.is_identical_to(top_site)) {
 | 
| +      // We install a dependency for pretenuring only on the outermost literal.
 | 
| +      dependencies()->AssumeTenuringDecision(top_site);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Setup the properties backing store.
 | 
| +  Node* properties = jsgraph()->EmptyFixedArrayConstant();
 | 
| +
 | 
| +  // Setup the elements backing store.
 | 
| +  Node* elements = AllocateFastLiteralElements(effect, control, boilerplate,
 | 
| +                                               pretenure, site_context);
 | 
| +  if (elements->op()->EffectOutputCount() > 0) effect = elements;
 | 
| +
 | 
| +  // Compute the in-object properties to store first (might have effects).
 | 
| +  Handle<Map> boilerplate_map(boilerplate->map(), isolate());
 | 
| +  ZoneVector<std::pair<FieldAccess, Node*>> inobject_fields(zone());
 | 
| +  inobject_fields.reserve(boilerplate_map->GetInObjectProperties());
 | 
| +  int const boilerplate_nof = boilerplate_map->NumberOfOwnDescriptors();
 | 
| +  for (int i = 0; i < boilerplate_nof; ++i) {
 | 
| +    PropertyDetails const property_details =
 | 
| +        boilerplate_map->instance_descriptors()->GetDetails(i);
 | 
| +    if (property_details.type() != DATA) continue;
 | 
| +    Handle<Name> property_name(
 | 
| +        boilerplate_map->instance_descriptors()->GetKey(i), isolate());
 | 
| +    FieldIndex index = FieldIndex::ForDescriptor(*boilerplate_map, i);
 | 
| +    FieldAccess access = {kTaggedBase, index.offset(), property_name,
 | 
| +                          Type::Tagged(), MachineType::AnyTagged()};
 | 
| +    Node* value;
 | 
| +    if (boilerplate->IsUnboxedDoubleField(index)) {
 | 
| +      access.machine_type = MachineType::Float64();
 | 
| +      access.type = Type::Number();
 | 
| +      value = jsgraph()->Constant(boilerplate->RawFastDoublePropertyAt(index));
 | 
| +    } else {
 | 
| +      Handle<Object> boilerplate_value(boilerplate->RawFastPropertyAt(index),
 | 
| +                                       isolate());
 | 
| +      if (boilerplate_value->IsJSObject()) {
 | 
| +        Handle<JSObject> boilerplate_object =
 | 
| +            Handle<JSObject>::cast(boilerplate_value);
 | 
| +        Handle<AllocationSite> current_site = site_context->EnterNewScope();
 | 
| +        value = effect = AllocateFastLiteral(effect, control,
 | 
| +                                             boilerplate_object, site_context);
 | 
| +        site_context->ExitScope(current_site, boilerplate_object);
 | 
| +      } else if (property_details.representation().IsDouble()) {
 | 
| +        // Allocate a mutable HeapNumber box and store the value into it.
 | 
| +        value = effect = AllocateMutableHeapNumber(
 | 
| +            Handle<HeapNumber>::cast(boilerplate_value)->value(),
 | 
| +            effect, control);
 | 
| +      } else if (property_details.representation().IsSmi()) {
 | 
| +        // Ensure that value is stored as smi.
 | 
| +        value = boilerplate_value->IsUninitialized()
 | 
| +                    ? jsgraph()->ZeroConstant()
 | 
| +                    : jsgraph()->Constant(boilerplate_value);
 | 
| +      } else {
 | 
| +        value = jsgraph()->Constant(boilerplate_value);
 | 
| +      }
 | 
| +    }
 | 
| +    inobject_fields.push_back(std::make_pair(access, value));
 | 
| +  }
 | 
| +
 | 
| +  // Fill slack at the end of the boilerplate object with filler maps.
 | 
| +  int const boilerplate_length = boilerplate_map->GetInObjectProperties();
 | 
| +  for (int index = static_cast<int>(inobject_fields.size());
 | 
| +       index < boilerplate_length; ++index) {
 | 
| +    FieldAccess access =
 | 
| +        AccessBuilder::ForJSObjectInObjectProperty(boilerplate_map, index);
 | 
| +    Node* value = jsgraph()->HeapConstant(factory()->one_pointer_filler_map());
 | 
| +    inobject_fields.push_back(std::make_pair(access, value));
 | 
| +  }
 | 
| +
 | 
| +  // Actually allocate and initialize the object.
 | 
| +  AllocationBuilder builder(jsgraph(), effect, control);
 | 
| +  builder.Allocate(boilerplate_map->instance_size(), pretenure);
 | 
| +  builder.Store(AccessBuilder::ForMap(), boilerplate_map);
 | 
| +  builder.Store(AccessBuilder::ForJSObjectProperties(), properties);
 | 
| +  builder.Store(AccessBuilder::ForJSObjectElements(), elements);
 | 
| +  if (boilerplate_map->IsJSArrayMap()) {
 | 
| +    Handle<JSArray> boilerplate_array = Handle<JSArray>::cast(boilerplate);
 | 
| +    builder.Store(
 | 
| +        AccessBuilder::ForJSArrayLength(boilerplate_array->GetElementsKind()),
 | 
| +        handle(boilerplate_array->length(), isolate()));
 | 
| +  }
 | 
| +  for (auto const inobject_field : inobject_fields) {
 | 
| +    builder.Store(inobject_field.first, inobject_field.second);
 | 
| +  }
 | 
| +  return builder.Finish();
 | 
| +}
 | 
| +
 | 
| +Node* JSCreateLowering::AllocateFastLiteralElements(
 | 
| +    Node* effect, Node* control, Handle<JSObject> boilerplate,
 | 
| +    PretenureFlag pretenure, AllocationSiteUsageContext* site_context) {
 | 
| +  Handle<FixedArrayBase> boilerplate_elements(boilerplate->elements(),
 | 
| +                                              isolate());
 | 
| +
 | 
| +  // Empty or copy-on-write elements just store a constant.
 | 
| +  if (boilerplate_elements->length() == 0 ||
 | 
| +      boilerplate_elements->map() == isolate()->heap()->fixed_cow_array_map()) {
 | 
| +    if (pretenure == TENURED &&
 | 
| +        isolate()->heap()->InNewSpace(*boilerplate_elements)) {
 | 
| +      // If we would like to pretenure a fixed cow array, we must ensure that
 | 
| +      // the array is already in old space, otherwise we'll create too many
 | 
| +      // old-to-new-space pointers (overflowing the store buffer).
 | 
| +      boilerplate_elements = Handle<FixedArrayBase>(
 | 
| +          isolate()->factory()->CopyAndTenureFixedCOWArray(
 | 
| +              Handle<FixedArray>::cast(boilerplate_elements)));
 | 
| +      boilerplate->set_elements(*boilerplate_elements);
 | 
| +    }
 | 
| +    return jsgraph()->HeapConstant(boilerplate_elements);
 | 
| +  }
 | 
| +
 | 
| +  // Compute the elements to store first (might have effects).
 | 
| +  int const elements_length = boilerplate_elements->length();
 | 
| +  Handle<Map> elements_map(boilerplate_elements->map(), isolate());
 | 
| +  ZoneVector<Node*> elements_values(elements_length, zone());
 | 
| +  if (elements_map->instance_type() == FIXED_DOUBLE_ARRAY_TYPE) {
 | 
| +    Handle<FixedDoubleArray> elements =
 | 
| +        Handle<FixedDoubleArray>::cast(boilerplate_elements);
 | 
| +    for (int i = 0; i < elements_length; ++i) {
 | 
| +      if (elements->is_the_hole(i)) {
 | 
| +        // TODO(turbofan): We cannot currently safely pass thru the (signaling)
 | 
| +        // hole NaN in C++ code, as the C++ compiler on Intel might use FPU
 | 
| +        // instructions/registers for doubles and therefore make the NaN quiet.
 | 
| +        // We should consider passing doubles in the compiler as raw int64
 | 
| +        // values to prevent this.
 | 
| +        elements_values[i] = effect =
 | 
| +            graph()->NewNode(simplified()->LoadElement(
 | 
| +                                 AccessBuilder::ForFixedDoubleArrayElement()),
 | 
| +                             jsgraph()->HeapConstant(elements),
 | 
| +                             jsgraph()->Constant(i), effect, control);
 | 
| +      } else {
 | 
| +        elements_values[i] = jsgraph()->Constant(elements->get_scalar(i));
 | 
| +      }
 | 
| +    }
 | 
| +  } else {
 | 
| +    Handle<FixedArray> elements =
 | 
| +        Handle<FixedArray>::cast(boilerplate_elements);
 | 
| +    for (int i = 0; i < elements_length; ++i) {
 | 
| +      if (elements->is_the_hole(i)) {
 | 
| +        elements_values[i] = jsgraph()->TheHoleConstant();
 | 
| +      } else {
 | 
| +        Handle<Object> element_value(elements->get(i), isolate());
 | 
| +        if (element_value->IsJSObject()) {
 | 
| +          Handle<JSObject> boilerplate_object =
 | 
| +              Handle<JSObject>::cast(element_value);
 | 
| +          Handle<AllocationSite> current_site = site_context->EnterNewScope();
 | 
| +          elements_values[i] = effect = AllocateFastLiteral(
 | 
| +              effect, control, boilerplate_object, site_context);
 | 
| +          site_context->ExitScope(current_site, boilerplate_object);
 | 
| +        } else {
 | 
| +          elements_values[i] = jsgraph()->Constant(element_value);
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Allocate the backing store array and store the elements.
 | 
| +  AllocationBuilder builder(jsgraph(), effect, control);
 | 
| +  builder.AllocateArray(elements_length, elements_map, pretenure);
 | 
| +  ElementAccess const access =
 | 
| +      (elements_map->instance_type() == FIXED_DOUBLE_ARRAY_TYPE)
 | 
| +          ? AccessBuilder::ForFixedDoubleArrayElement()
 | 
| +          : AccessBuilder::ForFixedArrayElement();
 | 
| +  for (int i = 0; i < elements_length; ++i) {
 | 
| +    builder.Store(access, jsgraph()->Constant(i), elements_values[i]);
 | 
| +  }
 | 
| +  return builder.Finish();
 | 
| +}
 | 
| +
 | 
| +Node* JSCreateLowering::AllocateMutableHeapNumber(double value, Node* effect,
 | 
| +                                                  Node* control) {
 | 
| +  // TODO(turbofan): Support inline allocation of MutableHeapNumber
 | 
| +  // (requires proper alignment on Allocate, and Begin/FinishRegion).
 | 
| +  Callable callable = CodeFactory::AllocateMutableHeapNumber(isolate());
 | 
| +  CallDescriptor* desc = Linkage::GetStubCallDescriptor(
 | 
| +      isolate(), jsgraph()->zone(), callable.descriptor(), 0,
 | 
| +      CallDescriptor::kNoFlags, Operator::kNoThrow);
 | 
| +  Node* result = effect = graph()->NewNode(
 | 
| +      common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
 | 
| +      jsgraph()->NoContextConstant(), effect, control);
 | 
| +  effect = graph()->NewNode(
 | 
| +      simplified()->StoreField(AccessBuilder::ForHeapNumberValue()), result,
 | 
| +      jsgraph()->Constant(value), effect, control);
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +MaybeHandle<LiteralsArray> JSCreateLowering::GetSpecializationLiterals(
 | 
| +    Node* node) {
 | 
| +  Node* const closure = NodeProperties::GetValueInput(node, 0);
 | 
| +  switch (closure->opcode()) {
 | 
| +    case IrOpcode::kHeapConstant: {
 | 
| +      Handle<HeapObject> object = OpParameter<Handle<HeapObject>>(closure);
 | 
| +      return handle(Handle<JSFunction>::cast(object)->literals());
 | 
| +    }
 | 
| +    case IrOpcode::kParameter: {
 | 
| +      int const index = ParameterIndexOf(closure->op());
 | 
| +      // The closure is always the last parameter to a JavaScript function, and
 | 
| +      // {Parameter} indices start at -1, so value outputs of {Start} look like
 | 
| +      // this: closure, receiver, param0, ..., paramN, context.
 | 
| +      if (index == -1) {
 | 
| +        return literals_array_;
 | 
| +      }
 | 
| +      break;
 | 
| +    }
 | 
| +    default:
 | 
| +      break;
 | 
| +  }
 | 
| +  return MaybeHandle<LiteralsArray>();
 | 
| +}
 | 
| +
 | 
|  Factory* JSCreateLowering::factory() const { return isolate()->factory(); }
 | 
|  
 | 
|  Graph* JSCreateLowering::graph() const { return jsgraph()->graph(); }
 | 
| 
 |