普通文本  |  803行  |  30.35 KB

// Copyright 2016 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 "src/builtins/builtins-constructor-gen.h"

#include "src/ast/ast.h"
#include "src/builtins/builtins-call-gen.h"
#include "src/builtins/builtins-constructor.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/counters.h"
#include "src/interface-descriptors.h"
#include "src/objects-inl.h"

namespace v8 {
namespace internal {

void Builtins::Generate_ConstructVarargs(MacroAssembler* masm) {
  Generate_CallOrConstructVarargs(masm,
                                  BUILTIN_CODE(masm->isolate(), Construct));
}

void Builtins::Generate_ConstructForwardVarargs(MacroAssembler* masm) {
  Generate_CallOrConstructForwardVarargs(
      masm, CallOrConstructMode::kConstruct,
      BUILTIN_CODE(masm->isolate(), Construct));
}

void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) {
  Generate_CallOrConstructForwardVarargs(
      masm, CallOrConstructMode::kConstruct,
      BUILTIN_CODE(masm->isolate(), ConstructFunction));
}

TF_BUILTIN(ConstructWithArrayLike, CallOrConstructBuiltinsAssembler) {
  TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
  SloppyTNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
  TNode<Object> arguments_list = CAST(Parameter(Descriptor::kArgumentsList));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
  CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
}

TF_BUILTIN(ConstructWithSpread, CallOrConstructBuiltinsAssembler) {
  TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
  SloppyTNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
  TNode<Object> spread = CAST(Parameter(Descriptor::kSpread));
  TNode<Int32T> args_count =
      UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
  CallOrConstructWithSpread(target, new_target, spread, args_count, context);
}

typedef compiler::Node Node;

TF_BUILTIN(FastNewClosure, ConstructorBuiltinsAssembler) {
  Node* shared_function_info = Parameter(Descriptor::kSharedFunctionInfo);
  Node* feedback_cell = Parameter(Descriptor::kFeedbackCell);
  Node* context = Parameter(Descriptor::kContext);

  CSA_ASSERT(this, IsFeedbackCell(feedback_cell));
  CSA_ASSERT(this, IsSharedFunctionInfo(shared_function_info));

  IncrementCounter(isolate()->counters()->fast_new_closure_total(), 1);

  // Bump the closure counter encoded the {feedback_cell}s map.
  {
    Node* const feedback_cell_map = LoadMap(feedback_cell);
    Label no_closures(this), one_closure(this), cell_done(this);

    GotoIf(IsNoClosuresCellMap(feedback_cell_map), &no_closures);
    GotoIf(IsOneClosureCellMap(feedback_cell_map), &one_closure);
    CSA_ASSERT(this, IsManyClosuresCellMap(feedback_cell_map),
               feedback_cell_map, feedback_cell);
    Goto(&cell_done);

    BIND(&no_closures);
    StoreMapNoWriteBarrier(feedback_cell, Heap::kOneClosureCellMapRootIndex);
    Goto(&cell_done);

    BIND(&one_closure);
    StoreMapNoWriteBarrier(feedback_cell, Heap::kManyClosuresCellMapRootIndex);
    Goto(&cell_done);

    BIND(&cell_done);
  }

  // The calculation of |function_map_index| must be in sync with
  // SharedFunctionInfo::function_map_index().
  Node* const flags =
      LoadObjectField(shared_function_info, SharedFunctionInfo::kFlagsOffset,
                      MachineType::Uint32());
  Node* const function_map_index = IntPtrAdd(
      DecodeWordFromWord32<SharedFunctionInfo::FunctionMapIndexBits>(flags),
      IntPtrConstant(Context::FIRST_FUNCTION_MAP_INDEX));
  CSA_ASSERT(this, UintPtrLessThanOrEqual(
                       function_map_index,
                       IntPtrConstant(Context::LAST_FUNCTION_MAP_INDEX)));

  // Get the function map in the current native context and set that
  // as the map of the allocated object.
  Node* const native_context = LoadNativeContext(context);
  Node* const function_map =
      LoadContextElement(native_context, function_map_index);

  // Create a new closure from the given function info in new space
  Node* instance_size_in_bytes =
      TimesPointerSize(LoadMapInstanceSizeInWords(function_map));
  Node* const result = Allocate(instance_size_in_bytes);
  StoreMapNoWriteBarrier(result, function_map);
  InitializeJSObjectBodyNoSlackTracking(result, function_map,
                                        instance_size_in_bytes,
                                        JSFunction::kSizeWithoutPrototype);

  // Initialize the rest of the function.
  StoreObjectFieldRoot(result, JSObject::kPropertiesOrHashOffset,
                       Heap::kEmptyFixedArrayRootIndex);
  StoreObjectFieldRoot(result, JSObject::kElementsOffset,
                       Heap::kEmptyFixedArrayRootIndex);
  {
    // Set function prototype if necessary.
    Label done(this), init_prototype(this);
    Branch(IsFunctionWithPrototypeSlotMap(function_map), &init_prototype,
           &done);

    BIND(&init_prototype);
    StoreObjectFieldRoot(result, JSFunction::kPrototypeOrInitialMapOffset,
                         Heap::kTheHoleValueRootIndex);
    Goto(&done);
    BIND(&done);
  }

  STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
  StoreObjectFieldNoWriteBarrier(result, JSFunction::kFeedbackCellOffset,
                                 feedback_cell);
  StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset,
                                 shared_function_info);
  StoreObjectFieldNoWriteBarrier(result, JSFunction::kContextOffset, context);
  Handle<Code> lazy_builtin_handle(
      isolate()->builtins()->builtin(Builtins::kCompileLazy), isolate());
  Node* lazy_builtin = HeapConstant(lazy_builtin_handle);
  StoreObjectFieldNoWriteBarrier(result, JSFunction::kCodeOffset, lazy_builtin);
  Return(result);
}

TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) {
  Node* context = Parameter(Descriptor::kContext);
  Node* target = Parameter(Descriptor::kTarget);
  Node* new_target = Parameter(Descriptor::kNewTarget);

  Label call_runtime(this);

  Node* result = EmitFastNewObject(context, target, new_target, &call_runtime);
  Return(result);

  BIND(&call_runtime);
  TailCallRuntime(Runtime::kNewObject, context, target, new_target);
}

Node* ConstructorBuiltinsAssembler::EmitFastNewObject(Node* context,
                                                      Node* target,
                                                      Node* new_target) {
  VARIABLE(var_obj, MachineRepresentation::kTagged);
  Label call_runtime(this), end(this);

  Node* result = EmitFastNewObject(context, target, new_target, &call_runtime);
  var_obj.Bind(result);
  Goto(&end);

  BIND(&call_runtime);
  var_obj.Bind(CallRuntime(Runtime::kNewObject, context, target, new_target));
  Goto(&end);

  BIND(&end);
  return var_obj.value();
}

Node* ConstructorBuiltinsAssembler::EmitFastNewObject(Node* context,
                                                      Node* target,
                                                      Node* new_target,
                                                      Label* call_runtime) {
  CSA_ASSERT(this, HasInstanceType(target, JS_FUNCTION_TYPE));
  CSA_ASSERT(this, IsJSReceiver(new_target));

  // Verify that the new target is a JSFunction.
  Label fast(this), end(this);
  GotoIf(HasInstanceType(new_target, JS_FUNCTION_TYPE), &fast);
  Goto(call_runtime);

  BIND(&fast);

  // Load the initial map and verify that it's in fact a map.
  Node* initial_map =
      LoadObjectField(new_target, JSFunction::kPrototypeOrInitialMapOffset);
  GotoIf(TaggedIsSmi(initial_map), call_runtime);
  GotoIf(DoesntHaveInstanceType(initial_map, MAP_TYPE), call_runtime);

  // Fall back to runtime if the target differs from the new target's
  // initial map constructor.
  Node* new_target_constructor =
      LoadObjectField(initial_map, Map::kConstructorOrBackPointerOffset);
  GotoIf(WordNotEqual(target, new_target_constructor), call_runtime);

  VARIABLE(properties, MachineRepresentation::kTagged);

  Label instantiate_map(this), allocate_properties(this);
  GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
  {
    properties.Bind(EmptyFixedArrayConstant());
    Goto(&instantiate_map);
  }
  BIND(&allocate_properties);
  {
    properties.Bind(AllocateNameDictionary(NameDictionary::kInitialCapacity));
    Goto(&instantiate_map);
  }

  BIND(&instantiate_map);
  return AllocateJSObjectFromMap(initial_map, properties.value(), nullptr,
                                 kNone, kWithSlackTracking);
}

Node* ConstructorBuiltinsAssembler::EmitFastNewFunctionContext(
    Node* scope_info, Node* slots, Node* context, ScopeType scope_type) {
  slots = ChangeUint32ToWord(slots);

  // TODO(ishell): Use CSA::OptimalParameterMode() here.
  ParameterMode mode = INTPTR_PARAMETERS;
  Node* min_context_slots = IntPtrConstant(Context::MIN_CONTEXT_SLOTS);
  Node* length = IntPtrAdd(slots, min_context_slots);
  Node* size = GetFixedArrayAllocationSize(length, PACKED_ELEMENTS, mode);

  // Create a new closure from the given function info in new space
  TNode<Context> function_context =
      UncheckedCast<Context>(AllocateInNewSpace(size));

  Heap::RootListIndex context_type;
  switch (scope_type) {
    case EVAL_SCOPE:
      context_type = Heap::kEvalContextMapRootIndex;
      break;
    case FUNCTION_SCOPE:
      context_type = Heap::kFunctionContextMapRootIndex;
      break;
    default:
      UNREACHABLE();
  }
  StoreMapNoWriteBarrier(function_context, context_type);
  StoreObjectFieldNoWriteBarrier(function_context, Context::kLengthOffset,
                                 SmiTag(length));

  // Set up the fixed slots.
  StoreFixedArrayElement(function_context, Context::SCOPE_INFO_INDEX,
                         scope_info, SKIP_WRITE_BARRIER);
  StoreFixedArrayElement(function_context, Context::PREVIOUS_INDEX, context,
                         SKIP_WRITE_BARRIER);
  StoreFixedArrayElement(function_context, Context::EXTENSION_INDEX,
                         TheHoleConstant(), SKIP_WRITE_BARRIER);

  // Copy the native context from the previous context.
  Node* native_context = LoadNativeContext(context);
  StoreFixedArrayElement(function_context, Context::NATIVE_CONTEXT_INDEX,
                         native_context, SKIP_WRITE_BARRIER);

  // Initialize the rest of the slots to undefined.
  Node* undefined = UndefinedConstant();
  BuildFastFixedArrayForEach(
      function_context, PACKED_ELEMENTS, min_context_slots, length,
      [this, undefined](Node* context, Node* offset) {
        StoreNoWriteBarrier(MachineRepresentation::kTagged, context, offset,
                            undefined);
      },
      mode);

  return function_context;
}

TF_BUILTIN(FastNewFunctionContextEval, ConstructorBuiltinsAssembler) {
  Node* scope_info = Parameter(Descriptor::kScopeInfo);
  Node* slots = Parameter(Descriptor::kSlots);
  Node* context = Parameter(Descriptor::kContext);
  Return(EmitFastNewFunctionContext(scope_info, slots, context,
                                    ScopeType::EVAL_SCOPE));
}

TF_BUILTIN(FastNewFunctionContextFunction, ConstructorBuiltinsAssembler) {
  Node* scope_info = Parameter(Descriptor::kScopeInfo);
  Node* slots = Parameter(Descriptor::kSlots);
  Node* context = Parameter(Descriptor::kContext);
  Return(EmitFastNewFunctionContext(scope_info, slots, context,
                                    ScopeType::FUNCTION_SCOPE));
}

Node* ConstructorBuiltinsAssembler::EmitCreateRegExpLiteral(
    Node* feedback_vector, Node* slot, Node* pattern, Node* flags,
    Node* context) {
  Label call_runtime(this, Label::kDeferred), end(this);

  VARIABLE(result, MachineRepresentation::kTagged);
  TNode<Object> literal_site =
      CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS));
  GotoIf(NotHasBoilerplate(literal_site), &call_runtime);
  {
    Node* boilerplate = literal_site;
    CSA_ASSERT(this, IsJSRegExp(boilerplate));
    int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
    Node* copy = Allocate(size);
    for (int offset = 0; offset < size; offset += kPointerSize) {
      Node* value = LoadObjectField(boilerplate, offset);
      StoreObjectFieldNoWriteBarrier(copy, offset, value);
    }
    result.Bind(copy);
    Goto(&end);
  }

  BIND(&call_runtime);
  {
    result.Bind(CallRuntime(Runtime::kCreateRegExpLiteral, context,
                            feedback_vector, SmiTag(slot), pattern, flags));
    Goto(&end);
  }

  BIND(&end);
  return result.value();
}

TF_BUILTIN(CreateRegExpLiteral, ConstructorBuiltinsAssembler) {
  Node* feedback_vector = Parameter(Descriptor::kFeedbackVector);
  Node* slot = SmiUntag(Parameter(Descriptor::kSlot));
  Node* pattern = Parameter(Descriptor::kPattern);
  Node* flags = Parameter(Descriptor::kFlags);
  Node* context = Parameter(Descriptor::kContext);
  Node* result =
      EmitCreateRegExpLiteral(feedback_vector, slot, pattern, flags, context);
  Return(result);
}

Node* ConstructorBuiltinsAssembler::EmitCreateShallowArrayLiteral(
    Node* feedback_vector, Node* slot, Node* context, Label* call_runtime,
    AllocationSiteMode allocation_site_mode) {
  Label zero_capacity(this), cow_elements(this), fast_elements(this),
      return_result(this);
  VARIABLE(result, MachineRepresentation::kTagged);

  TNode<Object> maybe_allocation_site =
      CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS));
  GotoIf(NotHasBoilerplate(maybe_allocation_site), call_runtime);

  TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site);
  TNode<JSArray> boilerplate = CAST(LoadBoilerplate(allocation_site));

  ParameterMode mode = OptimalParameterMode();
  if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
    return CloneFastJSArray(context, boilerplate, mode, allocation_site);
  } else {
    return CloneFastJSArray(context, boilerplate, mode);
  }
}

TF_BUILTIN(CreateShallowArrayLiteral, ConstructorBuiltinsAssembler) {
  Node* feedback_vector = Parameter(Descriptor::kFeedbackVector);
  Node* slot = SmiUntag(Parameter(Descriptor::kSlot));
  Node* constant_elements = Parameter(Descriptor::kConstantElements);
  Node* context = Parameter(Descriptor::kContext);
  Label call_runtime(this, Label::kDeferred);
  Return(EmitCreateShallowArrayLiteral(feedback_vector, slot, context,
                                       &call_runtime,
                                       DONT_TRACK_ALLOCATION_SITE));

  BIND(&call_runtime);
  {
    Comment("call runtime");
    int const flags =
        AggregateLiteral::kDisableMementos | AggregateLiteral::kIsShallow;
    Return(CallRuntime(Runtime::kCreateArrayLiteral, context, feedback_vector,
                       SmiTag(slot), constant_elements, SmiConstant(flags)));
  }
}

Node* ConstructorBuiltinsAssembler::EmitCreateEmptyArrayLiteral(
    Node* feedback_vector, Node* slot, Node* context) {
  // Array literals always have a valid AllocationSite to properly track
  // elements transitions.
  TNode<Object> maybe_allocation_site =
      CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS));
  TVARIABLE(AllocationSite, allocation_site);

  Label create_empty_array(this),
      initialize_allocation_site(this, Label::kDeferred), done(this);
  GotoIf(TaggedIsSmi(maybe_allocation_site), &initialize_allocation_site);
  {
    allocation_site = CAST(maybe_allocation_site);
    Goto(&create_empty_array);
  }
  // TODO(cbruni): create the AllocationSite in CSA.
  BIND(&initialize_allocation_site);
  {
    allocation_site =
        CreateAllocationSiteInFeedbackVector(feedback_vector, SmiTag(slot));
    Goto(&create_empty_array);
  }

  BIND(&create_empty_array);
  TNode<Int32T> kind = LoadElementsKind(allocation_site.value());
  TNode<Context> native_context = LoadNativeContext(context);
  Comment("LoadJSArrayElementsMap");
  Node* array_map = LoadJSArrayElementsMap(kind, native_context);
  Node* zero = SmiConstant(0);
  Comment("Allocate JSArray");
  Node* result =
      AllocateJSArray(GetInitialFastElementsKind(), array_map, zero, zero,
                      allocation_site.value(), ParameterMode::SMI_PARAMETERS);

  Goto(&done);
  BIND(&done);

  return result;
}

TF_BUILTIN(CreateEmptyArrayLiteral, ConstructorBuiltinsAssembler) {
  Node* feedback_vector = Parameter(Descriptor::kFeedbackVector);
  Node* slot = SmiUntag(Parameter(Descriptor::kSlot));
  Node* context = Parameter(Descriptor::kContext);
  Node* result = EmitCreateEmptyArrayLiteral(feedback_vector, slot, context);
  Return(result);
}

Node* ConstructorBuiltinsAssembler::EmitCreateShallowObjectLiteral(
    Node* feedback_vector, Node* slot, Label* call_runtime) {
  TNode<Object> maybe_allocation_site =
      CAST(LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS));
  GotoIf(NotHasBoilerplate(maybe_allocation_site), call_runtime);

  TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site);
  TNode<JSObject> boilerplate = LoadBoilerplate(allocation_site);
  TNode<Map> boilerplate_map = LoadMap(boilerplate);
  CSA_ASSERT(this, IsJSObjectMap(boilerplate_map));

  VARIABLE(var_properties, MachineRepresentation::kTagged);
  {
    Node* bit_field_3 = LoadMapBitField3(boilerplate_map);
    GotoIf(IsSetWord32<Map::IsDeprecatedBit>(bit_field_3), call_runtime);
    // Directly copy over the property store for dict-mode boilerplates.
    Label if_dictionary(this), if_fast(this), done(this);
    Branch(IsSetWord32<Map::IsDictionaryMapBit>(bit_field_3), &if_dictionary,
           &if_fast);
    BIND(&if_dictionary);
    {
      Comment("Copy dictionary properties");
      var_properties.Bind(CopyNameDictionary(
          CAST(LoadSlowProperties(boilerplate)), call_runtime));
      // Slow objects have no in-object properties.
      Goto(&done);
    }
    BIND(&if_fast);
    {
      // TODO(cbruni): support copying out-of-object properties.
      Node* boilerplate_properties = LoadFastProperties(boilerplate);
      GotoIfNot(IsEmptyFixedArray(boilerplate_properties), call_runtime);
      var_properties.Bind(EmptyFixedArrayConstant());
      Goto(&done);
    }
    BIND(&done);
  }

  VARIABLE(var_elements, MachineRepresentation::kTagged);
  {
    // Copy the elements backing store, assuming that it's flat.
    Label if_empty_fixed_array(this), if_copy_elements(this), done(this);
    Node* boilerplate_elements = LoadElements(boilerplate);
    Branch(IsEmptyFixedArray(boilerplate_elements), &if_empty_fixed_array,
           &if_copy_elements);

    BIND(&if_empty_fixed_array);
    var_elements.Bind(boilerplate_elements);
    Goto(&done);

    BIND(&if_copy_elements);
    CSA_ASSERT(this, Word32BinaryNot(
                         IsFixedCOWArrayMap(LoadMap(boilerplate_elements))));
    ExtractFixedArrayFlags flags;
    flags |= ExtractFixedArrayFlag::kAllFixedArrays;
    flags |= ExtractFixedArrayFlag::kNewSpaceAllocationOnly;
    flags |= ExtractFixedArrayFlag::kDontCopyCOW;
    var_elements.Bind(CloneFixedArray(boilerplate_elements, flags));
    Goto(&done);
    BIND(&done);
  }

  // Ensure new-space allocation for a fresh JSObject so we can skip write
  // barriers when copying all object fields.
  STATIC_ASSERT(JSObject::kMaxInstanceSize < kMaxRegularHeapObjectSize);
  Node* instance_size =
      TimesPointerSize(LoadMapInstanceSizeInWords(boilerplate_map));
  Node* allocation_size = instance_size;
  bool needs_allocation_memento = FLAG_allocation_site_pretenuring;
  if (needs_allocation_memento) {
    // Prepare for inner-allocating the AllocationMemento.
    allocation_size =
        IntPtrAdd(instance_size, IntPtrConstant(AllocationMemento::kSize));
  }

  Node* copy = AllocateInNewSpace(allocation_size);
  {
    Comment("Initialize Literal Copy");
    // Initialize Object fields.
    StoreMapNoWriteBarrier(copy, boilerplate_map);
    StoreObjectFieldNoWriteBarrier(copy, JSObject::kPropertiesOrHashOffset,
                                   var_properties.value());
    StoreObjectFieldNoWriteBarrier(copy, JSObject::kElementsOffset,
                                   var_elements.value());
  }

  // Initialize the AllocationMemento before potential GCs due to heap number
  // allocation when copying the in-object properties.
  if (needs_allocation_memento) {
    InitializeAllocationMemento(copy, instance_size, allocation_site);
  }

  {
    // Copy over in-object properties.
    Label continue_with_write_barrier(this), done_init(this);
    VARIABLE(offset, MachineType::PointerRepresentation(),
             IntPtrConstant(JSObject::kHeaderSize));
    // Mutable heap numbers only occur on 32-bit platforms.
    bool may_use_mutable_heap_numbers =
        FLAG_track_double_fields && !FLAG_unbox_double_fields;
    {
      Comment("Copy in-object properties fast");
      Label continue_fast(this, &offset);
      Branch(WordEqual(offset.value(), instance_size), &done_init,
             &continue_fast);
      BIND(&continue_fast);
      Node* field = LoadObjectField(boilerplate, offset.value());
      if (may_use_mutable_heap_numbers) {
        Label store_field(this);
        GotoIf(TaggedIsSmi(field), &store_field);
        GotoIf(IsMutableHeapNumber(field), &continue_with_write_barrier);
        Goto(&store_field);
        BIND(&store_field);
      }
      StoreObjectFieldNoWriteBarrier(copy, offset.value(), field);
      offset.Bind(IntPtrAdd(offset.value(), IntPtrConstant(kPointerSize)));
      Branch(WordNotEqual(offset.value(), instance_size), &continue_fast,
             &done_init);
    }

    if (!may_use_mutable_heap_numbers) {
      BIND(&done_init);
      return copy;
    }
    // Continue initializing the literal after seeing the first sub-object
    // potentially causing allocation. In this case we prepare the new literal
    // by copying all pending fields over from the boilerplate and emit full
    // write barriers from here on.
    BIND(&continue_with_write_barrier);
    {
      Comment("Copy in-object properties slow");
      BuildFastLoop(offset.value(), instance_size,
                    [=](Node* offset) {
                      Node* field = LoadObjectField(boilerplate, offset);
                      StoreObjectFieldNoWriteBarrier(copy, offset, field);
                    },
                    kPointerSize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
      Comment("Copy mutable HeapNumber values");
      BuildFastLoop(offset.value(), instance_size,
                    [=](Node* offset) {
                      Node* field = LoadObjectField(copy, offset);
                      Label copy_mutable_heap_number(this, Label::kDeferred),
                          continue_loop(this);
                      // We only have to clone complex field values.
                      GotoIf(TaggedIsSmi(field), &continue_loop);
                      Branch(IsMutableHeapNumber(field),
                             &copy_mutable_heap_number, &continue_loop);
                      BIND(&copy_mutable_heap_number);
                      {
                        Node* double_value = LoadHeapNumberValue(field);
                        Node* mutable_heap_number =
                            AllocateMutableHeapNumberWithValue(double_value);
                        StoreObjectField(copy, offset, mutable_heap_number);
                        Goto(&continue_loop);
                      }
                      BIND(&continue_loop);
                    },
                    kPointerSize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
      Goto(&done_init);
    }
    BIND(&done_init);
  }
  return copy;
}

TF_BUILTIN(CreateShallowObjectLiteral, ConstructorBuiltinsAssembler) {
  Label call_runtime(this);
  Node* feedback_vector = Parameter(Descriptor::kFeedbackVector);
  Node* slot = SmiUntag(Parameter(Descriptor::kSlot));
  Node* copy =
      EmitCreateShallowObjectLiteral(feedback_vector, slot, &call_runtime);
  Return(copy);

  BIND(&call_runtime);
  Node* object_boilerplate_description =
      Parameter(Descriptor::kObjectBoilerplateDescription);
  Node* flags = Parameter(Descriptor::kFlags);
  Node* context = Parameter(Descriptor::kContext);
  TailCallRuntime(Runtime::kCreateObjectLiteral, context, feedback_vector,
                  SmiTag(slot), object_boilerplate_description, flags);
}

// Used by the CreateEmptyObjectLiteral bytecode and the Object constructor.
Node* ConstructorBuiltinsAssembler::EmitCreateEmptyObjectLiteral(
    Node* context) {
  Node* native_context = LoadNativeContext(context);
  Node* object_function =
      LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX);
  Node* map = LoadObjectField(object_function,
                              JSFunction::kPrototypeOrInitialMapOffset);
  CSA_ASSERT(this, IsMap(map));
  // Ensure that slack tracking is disabled for the map.
  STATIC_ASSERT(Map::kNoSlackTracking == 0);
  CSA_ASSERT(
      this, IsClearWord32<Map::ConstructionCounterBits>(LoadMapBitField3(map)));
  Node* empty_fixed_array = EmptyFixedArrayConstant();
  Node* result =
      AllocateJSObjectFromMap(map, empty_fixed_array, empty_fixed_array);
  return result;
}

// ES #sec-object-constructor
TF_BUILTIN(ObjectConstructor, ConstructorBuiltinsAssembler) {
  int const kValueArg = 0;
  Node* argc =
      ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
  CodeStubArguments args(this, argc);
  Node* context = Parameter(Descriptor::kContext);
  Node* new_target = Parameter(Descriptor::kJSNewTarget);

  VARIABLE(var_result, MachineRepresentation::kTagged);
  Label if_subclass(this, Label::kDeferred), if_notsubclass(this),
      return_result(this);
  GotoIf(IsUndefined(new_target), &if_notsubclass);
  TNode<JSFunction> target = CAST(Parameter(Descriptor::kJSTarget));
  Branch(WordEqual(new_target, target), &if_notsubclass, &if_subclass);

  BIND(&if_subclass);
  {
    Node* result =
        CallBuiltin(Builtins::kFastNewObject, context, target, new_target);
    var_result.Bind(result);
    Goto(&return_result);
  }

  BIND(&if_notsubclass);
  {
    Label if_newobject(this, Label::kDeferred), if_toobject(this);

    Node* value_index = IntPtrConstant(kValueArg);
    GotoIf(UintPtrGreaterThanOrEqual(value_index, argc), &if_newobject);
    Node* value = args.AtIndex(value_index);
    GotoIf(IsNull(value), &if_newobject);
    Branch(IsUndefined(value), &if_newobject, &if_toobject);

    BIND(&if_newobject);
    {
      Node* result = EmitCreateEmptyObjectLiteral(context);
      var_result.Bind(result);
      Goto(&return_result);
    }

    BIND(&if_toobject);
    {
      Node* result = CallBuiltin(Builtins::kToObject, context, value);
      var_result.Bind(result);
      Goto(&return_result);
    }
  }

  BIND(&return_result);
  args.PopAndReturn(var_result.value());
}

// ES #sec-number-constructor
TF_BUILTIN(NumberConstructor, ConstructorBuiltinsAssembler) {
  Node* context = Parameter(Descriptor::kContext);
  Node* argc =
      ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
  CodeStubArguments args(this, argc);

  // 1. If no arguments were passed to this function invocation, let n be +0.
  VARIABLE(var_n, MachineRepresentation::kTagged, SmiConstant(0));
  Label if_nloaded(this, &var_n);
  GotoIf(WordEqual(argc, IntPtrConstant(0)), &if_nloaded);

  // 2. Else,
  //    a. Let prim be ? ToNumeric(value).
  //    b. If Type(prim) is BigInt, let n be the Number value for prim.
  //    c. Otherwise, let n be prim.
  Node* value = args.AtIndex(0);
  var_n.Bind(ToNumber(context, value, BigIntHandling::kConvertToNumber));
  Goto(&if_nloaded);

  BIND(&if_nloaded);
  {
    // 3. If NewTarget is undefined, return n.
    Node* n_value = var_n.value();
    Node* new_target = Parameter(Descriptor::kJSNewTarget);
    Label return_n(this), constructnumber(this, Label::kDeferred);
    Branch(IsUndefined(new_target), &return_n, &constructnumber);

    BIND(&return_n);
    { args.PopAndReturn(n_value); }

    BIND(&constructnumber);
    {
      // 4. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
      //    "%NumberPrototype%", « [[NumberData]] »).
      // 5. Set O.[[NumberData]] to n.
      // 6. Return O.

      // We are not using Parameter(Descriptor::kJSTarget) and loading the value
      // from the current frame here in order to reduce register pressure on the
      // fast path.
      TNode<JSFunction> target = LoadTargetFromFrame();
      Node* result =
          CallBuiltin(Builtins::kFastNewObject, context, target, new_target);
      StoreObjectField(result, JSValue::kValueOffset, n_value);
      args.PopAndReturn(result);
    }
  }
}

TF_BUILTIN(GenericConstructorLazyDeoptContinuation,
           ConstructorBuiltinsAssembler) {
  Node* result = Parameter(Descriptor::kResult);
  Return(result);
}

// https://tc39.github.io/ecma262/#sec-string-constructor
TF_BUILTIN(StringConstructor, ConstructorBuiltinsAssembler) {
  Node* context = Parameter(Descriptor::kContext);
  Node* argc =
      ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
  CodeStubArguments args(this, argc);

  TNode<Object> new_target = CAST(Parameter(Descriptor::kJSNewTarget));

  // 1. If no arguments were passed to this function invocation, let s be "".
  VARIABLE(var_s, MachineRepresentation::kTagged, EmptyStringConstant());
  Label if_sloaded(this, &var_s);
  GotoIf(WordEqual(argc, IntPtrConstant(0)), &if_sloaded);

  // 2. Else,
  //    a. If NewTarget is undefined [...]
  Node* value = args.AtIndex(0);
  Label if_tostring(this, &var_s);
  GotoIfNot(IsUndefined(new_target), &if_tostring);

  // 2a. [...] and Type(value) is Symbol, return SymbolDescriptiveString(value).
  GotoIf(TaggedIsSmi(value), &if_tostring);
  GotoIfNot(IsSymbol(value), &if_tostring);
  {
    Node* result =
        CallRuntime(Runtime::kSymbolDescriptiveString, context, value);
    args.PopAndReturn(result);
  }

  // 2b. Let s be ? ToString(value).
  BIND(&if_tostring);
  {
    var_s.Bind(CallBuiltin(Builtins::kToString, context, value));
    Goto(&if_sloaded);
  }

  // 3. If NewTarget is undefined, return s.
  BIND(&if_sloaded);
  {
    Node* s_value = var_s.value();
    Label return_s(this), constructstring(this, Label::kDeferred);
    Branch(IsUndefined(new_target), &return_s, &constructstring);

    BIND(&return_s);
    { args.PopAndReturn(s_value); }

    BIND(&constructstring);
    {
      // We are not using Parameter(Descriptor::kJSTarget) and loading the value
      // from the current frame here in order to reduce register pressure on the
      // fast path.
      TNode<JSFunction> target = LoadTargetFromFrame();

      Node* result =
          CallBuiltin(Builtins::kFastNewObject, context, target, new_target);
      StoreObjectField(result, JSValue::kValueOffset, s_value);
      args.PopAndReturn(result);
    }
  }
}

}  // namespace internal
}  // namespace v8