// 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.h" #include "src/ast/ast.h" #include "src/builtins/builtins-utils.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 { typedef compiler::Node Node; Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info, Node* feedback_vector, Node* slot, Node* context) { typedef compiler::CodeAssembler::Label Label; typedef compiler::CodeAssembler::Variable Variable; Isolate* isolate = this->isolate(); Factory* factory = isolate->factory(); IncrementCounter(isolate->counters()->fast_new_closure_total(), 1); // Create a new closure from the given function info in new space Node* result = Allocate(JSFunction::kSize); // Calculate the index of the map we should install on the function based on // the FunctionKind and LanguageMode of the function. // Note: Must be kept in sync with Context::FunctionMapIndex Node* compiler_hints = LoadObjectField(shared_info, SharedFunctionInfo::kCompilerHintsOffset, MachineType::Uint32()); Node* is_strict = Word32And( compiler_hints, Int32Constant(1 << SharedFunctionInfo::kStrictModeBit)); Label if_normal(this), if_generator(this), if_async(this), if_class_constructor(this), if_function_without_prototype(this), load_map(this); Variable map_index(this, MachineType::PointerRepresentation()); STATIC_ASSERT(FunctionKind::kNormalFunction == 0); Node* is_not_normal = Word32And(compiler_hints, Int32Constant(SharedFunctionInfo::kAllFunctionKindBitsMask)); GotoIfNot(is_not_normal, &if_normal); Node* is_generator = Word32And( compiler_hints, Int32Constant(FunctionKind::kGeneratorFunction << SharedFunctionInfo::kFunctionKindShift)); GotoIf(is_generator, &if_generator); Node* is_async = Word32And( compiler_hints, Int32Constant(FunctionKind::kAsyncFunction << SharedFunctionInfo::kFunctionKindShift)); GotoIf(is_async, &if_async); Node* is_class_constructor = Word32And( compiler_hints, Int32Constant(FunctionKind::kClassConstructor << SharedFunctionInfo::kFunctionKindShift)); GotoIf(is_class_constructor, &if_class_constructor); if (FLAG_debug_code) { // Function must be a function without a prototype. CSA_ASSERT( this, Word32And(compiler_hints, Int32Constant((FunctionKind::kAccessorFunction | FunctionKind::kArrowFunction | FunctionKind::kConciseMethod) << SharedFunctionInfo::kFunctionKindShift))); } Goto(&if_function_without_prototype); Bind(&if_normal); { map_index.Bind(SelectIntPtrConstant(is_strict, Context::STRICT_FUNCTION_MAP_INDEX, Context::SLOPPY_FUNCTION_MAP_INDEX)); Goto(&load_map); } Bind(&if_generator); { map_index.Bind(IntPtrConstant(Context::GENERATOR_FUNCTION_MAP_INDEX)); Goto(&load_map); } Bind(&if_async); { map_index.Bind(IntPtrConstant(Context::ASYNC_FUNCTION_MAP_INDEX)); Goto(&load_map); } Bind(&if_class_constructor); { map_index.Bind(IntPtrConstant(Context::CLASS_FUNCTION_MAP_INDEX)); Goto(&load_map); } Bind(&if_function_without_prototype); { map_index.Bind( IntPtrConstant(Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX)); Goto(&load_map); } Bind(&load_map); // Get the function map in the current native context and set that // as the map of the allocated object. Node* native_context = LoadNativeContext(context); Node* map_slot_value = LoadFixedArrayElement(native_context, map_index.value()); StoreMapNoWriteBarrier(result, map_slot_value); // Initialize the rest of the function. Node* empty_fixed_array = HeapConstant(factory->empty_fixed_array()); StoreObjectFieldNoWriteBarrier(result, JSObject::kPropertiesOffset, empty_fixed_array); StoreObjectFieldNoWriteBarrier(result, JSObject::kElementsOffset, empty_fixed_array); Node* literals_cell = LoadFixedArrayElement( feedback_vector, slot, 0, CodeStubAssembler::SMI_PARAMETERS); { // Bump the closure counter encoded in the cell's map. Node* cell_map = LoadMap(literals_cell); Label no_closures(this), one_closure(this), cell_done(this); GotoIf(IsNoClosuresCellMap(cell_map), &no_closures); GotoIf(IsOneClosureCellMap(cell_map), &one_closure); CSA_ASSERT(this, IsManyClosuresCellMap(cell_map)); Goto(&cell_done); Bind(&no_closures); StoreMapNoWriteBarrier(literals_cell, Heap::kOneClosureCellMapRootIndex); Goto(&cell_done); Bind(&one_closure); StoreMapNoWriteBarrier(literals_cell, Heap::kManyClosuresCellMapRootIndex); Goto(&cell_done); Bind(&cell_done); } StoreObjectFieldNoWriteBarrier(result, JSFunction::kFeedbackVectorOffset, literals_cell); StoreObjectFieldNoWriteBarrier( result, JSFunction::kPrototypeOrInitialMapOffset, TheHoleConstant()); StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset, shared_info); StoreObjectFieldNoWriteBarrier(result, JSFunction::kContextOffset, context); Handle<Code> lazy_builtin_handle( isolate->builtins()->builtin(Builtins::kCompileLazy)); Node* lazy_builtin = HeapConstant(lazy_builtin_handle); Node* lazy_builtin_entry = IntPtrAdd(BitcastTaggedToWord(lazy_builtin), IntPtrConstant(Code::kHeaderSize - kHeapObjectTag)); StoreObjectFieldNoWriteBarrier(result, JSFunction::kCodeEntryOffset, lazy_builtin_entry, MachineType::PointerRepresentation()); StoreObjectFieldNoWriteBarrier(result, JSFunction::kNextFunctionLinkOffset, UndefinedConstant()); return result; } TF_BUILTIN(FastNewClosure, ConstructorBuiltinsAssembler) { Node* shared = Parameter(FastNewClosureDescriptor::kSharedFunctionInfo); Node* context = Parameter(FastNewClosureDescriptor::kContext); Node* vector = Parameter(FastNewClosureDescriptor::kVector); Node* slot = Parameter(FastNewClosureDescriptor::kSlot); Return(EmitFastNewClosure(shared, vector, slot, context)); } TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) { typedef FastNewObjectDescriptor Descriptor; 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(this, 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, CodeAssemblerLabel* 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); Node* instance_size_words = ChangeUint32ToWord(LoadObjectField( initial_map, Map::kInstanceSizeOffset, MachineType::Uint8())); Node* instance_size = WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2)); Node* object = Allocate(instance_size); StoreMapNoWriteBarrier(object, initial_map); Node* empty_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex); StoreObjectFieldNoWriteBarrier(object, JSObject::kPropertiesOffset, empty_array); StoreObjectFieldNoWriteBarrier(object, JSObject::kElementsOffset, empty_array); instance_size_words = ChangeUint32ToWord(LoadObjectField( initial_map, Map::kInstanceSizeOffset, MachineType::Uint8())); instance_size = WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2)); // Perform in-object slack tracking if requested. Node* bit_field3 = LoadMapBitField3(initial_map); Label slack_tracking(this), finalize(this, Label::kDeferred), done(this); GotoIf(IsSetWord32<Map::ConstructionCounter>(bit_field3), &slack_tracking); // Initialize remaining fields. { Comment("no slack tracking"); InitializeFieldsWithRoot(object, IntPtrConstant(JSObject::kHeaderSize), instance_size, Heap::kUndefinedValueRootIndex); Goto(&end); } { Bind(&slack_tracking); // Decrease generous allocation count. STATIC_ASSERT(Map::ConstructionCounter::kNext == 32); Comment("update allocation count"); Node* new_bit_field3 = Int32Sub( bit_field3, Int32Constant(1 << Map::ConstructionCounter::kShift)); StoreObjectFieldNoWriteBarrier(initial_map, Map::kBitField3Offset, new_bit_field3, MachineRepresentation::kWord32); GotoIf(IsClearWord32<Map::ConstructionCounter>(new_bit_field3), &finalize); Node* unused_fields = LoadObjectField( initial_map, Map::kUnusedPropertyFieldsOffset, MachineType::Uint8()); Node* used_size = IntPtrSub(instance_size, WordShl(ChangeUint32ToWord(unused_fields), IntPtrConstant(kPointerSizeLog2))); Comment("initialize filler fields (no finalize)"); InitializeFieldsWithRoot(object, used_size, instance_size, Heap::kOnePointerFillerMapRootIndex); Comment("initialize undefined fields (no finalize)"); InitializeFieldsWithRoot(object, IntPtrConstant(JSObject::kHeaderSize), used_size, Heap::kUndefinedValueRootIndex); Goto(&end); } { // Finalize the instance size. Bind(&finalize); Node* unused_fields = LoadObjectField( initial_map, Map::kUnusedPropertyFieldsOffset, MachineType::Uint8()); Node* used_size = IntPtrSub(instance_size, WordShl(ChangeUint32ToWord(unused_fields), IntPtrConstant(kPointerSizeLog2))); Comment("initialize filler fields (finalize)"); InitializeFieldsWithRoot(object, used_size, instance_size, Heap::kOnePointerFillerMapRootIndex); Comment("initialize undefined fields (finalize)"); InitializeFieldsWithRoot(object, IntPtrConstant(JSObject::kHeaderSize), used_size, Heap::kUndefinedValueRootIndex); CallRuntime(Runtime::kFinalizeInstanceSize, context, initial_map); Goto(&end); } Bind(&end); return object; } Node* ConstructorBuiltinsAssembler::EmitFastNewFunctionContext( Node* function, Node* slots, Node* context, ScopeType scope_type) { slots = ChangeUint32ToWord(slots); // TODO(ishell): Use CSA::OptimalParameterMode() here. CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; Node* min_context_slots = IntPtrConstant(Context::MIN_CONTEXT_SLOTS); Node* length = IntPtrAdd(slots, min_context_slots); Node* size = GetFixedArrayAllocationSize(length, FAST_ELEMENTS, mode); // Create a new closure from the given function info in new space Node* function_context = Allocate(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::CLOSURE_INDEX, function, 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, FAST_ELEMENTS, min_context_slots, length, [this, undefined](Node* context, Node* offset) { StoreNoWriteBarrier(MachineRepresentation::kTagged, context, offset, undefined); }, mode); return function_context; } // static int ConstructorBuiltinsAssembler::MaximumFunctionContextSlots() { return FLAG_test_small_max_function_context_stub_size ? kSmallMaximumSlots : kMaximumSlots; } TF_BUILTIN(FastNewFunctionContextEval, ConstructorBuiltinsAssembler) { Node* function = Parameter(FastNewFunctionContextDescriptor::kFunction); Node* slots = Parameter(FastNewFunctionContextDescriptor::kSlots); Node* context = Parameter(FastNewFunctionContextDescriptor::kContext); Return(EmitFastNewFunctionContext(function, slots, context, ScopeType::EVAL_SCOPE)); } TF_BUILTIN(FastNewFunctionContextFunction, ConstructorBuiltinsAssembler) { Node* function = Parameter(FastNewFunctionContextDescriptor::kFunction); Node* slots = Parameter(FastNewFunctionContextDescriptor::kSlots); Node* context = Parameter(FastNewFunctionContextDescriptor::kContext); Return(EmitFastNewFunctionContext(function, slots, context, ScopeType::FUNCTION_SCOPE)); } Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) { switch (scope_type) { case ScopeType::EVAL_SCOPE: return FastNewFunctionContextEval(); case ScopeType::FUNCTION_SCOPE: return FastNewFunctionContextFunction(); default: UNREACHABLE(); } return Handle<Code>::null(); } Node* ConstructorBuiltinsAssembler::EmitFastCloneRegExp(Node* closure, Node* literal_index, Node* pattern, Node* flags, Node* context) { typedef CodeStubAssembler::Label Label; typedef CodeStubAssembler::Variable Variable; typedef compiler::Node Node; Label call_runtime(this, Label::kDeferred), end(this); Variable result(this, MachineRepresentation::kTagged); Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset); Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset); Node* boilerplate = LoadFixedArrayElement(feedback_vector, literal_index, 0, CodeStubAssembler::SMI_PARAMETERS); GotoIf(IsUndefined(boilerplate), &call_runtime); { 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, closure, literal_index, pattern, flags)); Goto(&end); } Bind(&end); return result.value(); } TF_BUILTIN(FastCloneRegExp, ConstructorBuiltinsAssembler) { Node* closure = Parameter(FastCloneRegExpDescriptor::kClosure); Node* literal_index = Parameter(FastCloneRegExpDescriptor::kLiteralIndex); Node* pattern = Parameter(FastCloneRegExpDescriptor::kPattern); Node* flags = Parameter(FastCloneRegExpDescriptor::kFlags); Node* context = Parameter(FastCloneRegExpDescriptor::kContext); Return(EmitFastCloneRegExp(closure, literal_index, pattern, flags, context)); } Node* ConstructorBuiltinsAssembler::NonEmptyShallowClone( Node* boilerplate, Node* boilerplate_map, Node* boilerplate_elements, Node* allocation_site, Node* capacity, ElementsKind kind) { typedef CodeStubAssembler::ParameterMode ParameterMode; ParameterMode param_mode = OptimalParameterMode(); Node* length = LoadJSArrayLength(boilerplate); capacity = TaggedToParameter(capacity, param_mode); Node *array, *elements; std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( kind, boilerplate_map, length, allocation_site, capacity, param_mode); Comment("copy elements header"); // Header consists of map and length. STATIC_ASSERT(FixedArrayBase::kHeaderSize == 2 * kPointerSize); StoreMap(elements, LoadMap(boilerplate_elements)); { int offset = FixedArrayBase::kLengthOffset; StoreObjectFieldNoWriteBarrier( elements, offset, LoadObjectField(boilerplate_elements, offset)); } length = TaggedToParameter(length, param_mode); Comment("copy boilerplate elements"); CopyFixedArrayElements(kind, boilerplate_elements, elements, length, SKIP_WRITE_BARRIER, param_mode); IncrementCounter(isolate()->counters()->inlined_copied_elements(), 1); return array; } Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowArray( Node* closure, Node* literal_index, Node* context, CodeAssemblerLabel* call_runtime, AllocationSiteMode allocation_site_mode) { typedef CodeStubAssembler::Label Label; typedef CodeStubAssembler::Variable Variable; typedef compiler::Node Node; Label zero_capacity(this), cow_elements(this), fast_elements(this), return_result(this); Variable result(this, MachineRepresentation::kTagged); Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset); Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset); Node* allocation_site = LoadFixedArrayElement( feedback_vector, literal_index, 0, CodeStubAssembler::SMI_PARAMETERS); GotoIf(IsUndefined(allocation_site), call_runtime); allocation_site = LoadFixedArrayElement(feedback_vector, literal_index, 0, CodeStubAssembler::SMI_PARAMETERS); Node* boilerplate = LoadObjectField(allocation_site, AllocationSite::kTransitionInfoOffset); Node* boilerplate_map = LoadMap(boilerplate); Node* boilerplate_elements = LoadElements(boilerplate); Node* capacity = LoadFixedArrayBaseLength(boilerplate_elements); allocation_site = allocation_site_mode == TRACK_ALLOCATION_SITE ? allocation_site : nullptr; Node* zero = SmiConstant(Smi::kZero); GotoIf(SmiEqual(capacity, zero), &zero_capacity); Node* elements_map = LoadMap(boilerplate_elements); GotoIf(IsFixedCOWArrayMap(elements_map), &cow_elements); GotoIf(IsFixedArrayMap(elements_map), &fast_elements); { Comment("fast double elements path"); if (FLAG_debug_code) { Label correct_elements_map(this), abort(this, Label::kDeferred); Branch(IsFixedDoubleArrayMap(elements_map), &correct_elements_map, &abort); Bind(&abort); { Node* abort_id = SmiConstant( Smi::FromInt(BailoutReason::kExpectedFixedDoubleArrayMap)); CallRuntime(Runtime::kAbort, context, abort_id); result.Bind(UndefinedConstant()); Goto(&return_result); } Bind(&correct_elements_map); } Node* array = NonEmptyShallowClone(boilerplate, boilerplate_map, boilerplate_elements, allocation_site, capacity, FAST_DOUBLE_ELEMENTS); result.Bind(array); Goto(&return_result); } Bind(&fast_elements); { Comment("fast elements path"); Node* array = NonEmptyShallowClone(boilerplate, boilerplate_map, boilerplate_elements, allocation_site, capacity, FAST_ELEMENTS); result.Bind(array); Goto(&return_result); } Variable length(this, MachineRepresentation::kTagged), elements(this, MachineRepresentation::kTagged); Label allocate_without_elements(this); Bind(&cow_elements); { Comment("fixed cow path"); length.Bind(LoadJSArrayLength(boilerplate)); elements.Bind(boilerplate_elements); Goto(&allocate_without_elements); } Bind(&zero_capacity); { Comment("zero capacity path"); length.Bind(zero); elements.Bind(LoadRoot(Heap::kEmptyFixedArrayRootIndex)); Goto(&allocate_without_elements); } Bind(&allocate_without_elements); { Node* array = AllocateUninitializedJSArrayWithoutElements( FAST_ELEMENTS, boilerplate_map, length.value(), allocation_site); StoreObjectField(array, JSObject::kElementsOffset, elements.value()); result.Bind(array); Goto(&return_result); } Bind(&return_result); return result.value(); } void ConstructorBuiltinsAssembler::CreateFastCloneShallowArrayBuiltin( AllocationSiteMode allocation_site_mode) { typedef compiler::Node Node; typedef CodeStubAssembler::Label Label; Node* closure = Parameter(FastCloneShallowArrayDescriptor::kClosure); Node* literal_index = Parameter(FastCloneShallowArrayDescriptor::kLiteralIndex); Node* constant_elements = Parameter(FastCloneShallowArrayDescriptor::kConstantElements); Node* context = Parameter(FastCloneShallowArrayDescriptor::kContext); Label call_runtime(this, Label::kDeferred); Return(EmitFastCloneShallowArray(closure, literal_index, context, &call_runtime, allocation_site_mode)); Bind(&call_runtime); { Comment("call runtime"); Node* flags = SmiConstant(Smi::FromInt(ArrayLiteral::kShallowElements | (allocation_site_mode == TRACK_ALLOCATION_SITE ? 0 : ArrayLiteral::kDisableMementos))); Return(CallRuntime(Runtime::kCreateArrayLiteral, context, closure, literal_index, constant_elements, flags)); } } TF_BUILTIN(FastCloneShallowArrayTrack, ConstructorBuiltinsAssembler) { CreateFastCloneShallowArrayBuiltin(TRACK_ALLOCATION_SITE); } TF_BUILTIN(FastCloneShallowArrayDontTrack, ConstructorBuiltinsAssembler) { CreateFastCloneShallowArrayBuiltin(DONT_TRACK_ALLOCATION_SITE); } Handle<Code> Builtins::NewCloneShallowArray( AllocationSiteMode allocation_mode) { switch (allocation_mode) { case TRACK_ALLOCATION_SITE: return FastCloneShallowArrayTrack(); case DONT_TRACK_ALLOCATION_SITE: return FastCloneShallowArrayDontTrack(); default: UNREACHABLE(); } return Handle<Code>::null(); } // static int ConstructorBuiltinsAssembler::FastCloneShallowObjectPropertiesCount( int literal_length) { // This heuristic of setting empty literals to have // kInitialGlobalObjectUnusedPropertiesCount must remain in-sync with the // runtime. // TODO(verwaest): Unify this with the heuristic in the runtime. return literal_length == 0 ? JSObject::kInitialGlobalObjectUnusedPropertiesCount : literal_length; } Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject( CodeAssemblerLabel* call_runtime, Node* closure, Node* literals_index, Node* properties_count) { Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset); Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset); Node* allocation_site = LoadFixedArrayElement( feedback_vector, literals_index, 0, CodeStubAssembler::SMI_PARAMETERS); GotoIf(IsUndefined(allocation_site), call_runtime); // Calculate the object and allocation size based on the properties count. Node* object_size = IntPtrAdd(WordShl(properties_count, kPointerSizeLog2), IntPtrConstant(JSObject::kHeaderSize)); Node* allocation_size = object_size; if (FLAG_allocation_site_pretenuring) { allocation_size = IntPtrAdd(object_size, IntPtrConstant(AllocationMemento::kSize)); } Node* boilerplate = LoadObjectField(allocation_site, AllocationSite::kTransitionInfoOffset); Node* boilerplate_map = LoadMap(boilerplate); Node* instance_size = LoadMapInstanceSize(boilerplate_map); Node* size_in_words = WordShr(object_size, kPointerSizeLog2); GotoIfNot(WordEqual(instance_size, size_in_words), call_runtime); Node* copy = Allocate(allocation_size); // Copy boilerplate elements. Variable offset(this, MachineType::PointerRepresentation()); offset.Bind(IntPtrConstant(-kHeapObjectTag)); Node* end_offset = IntPtrAdd(object_size, offset.value()); Label loop_body(this, &offset), loop_check(this, &offset); // We should always have an object size greater than zero. Goto(&loop_body); Bind(&loop_body); { // The Allocate above guarantees that the copy lies in new space. This // allows us to skip write barriers. This is necessary since we may also be // copying unboxed doubles. Node* field = Load(MachineType::IntPtr(), boilerplate, offset.value()); StoreNoWriteBarrier(MachineType::PointerRepresentation(), copy, offset.value(), field); Goto(&loop_check); } Bind(&loop_check); { offset.Bind(IntPtrAdd(offset.value(), IntPtrConstant(kPointerSize))); GotoIfNot(IntPtrGreaterThanOrEqual(offset.value(), end_offset), &loop_body); } if (FLAG_allocation_site_pretenuring) { Node* memento = InnerAllocate(copy, object_size); StoreMapNoWriteBarrier(memento, Heap::kAllocationMementoMapRootIndex); StoreObjectFieldNoWriteBarrier( memento, AllocationMemento::kAllocationSiteOffset, allocation_site); Node* memento_create_count = LoadObjectField( allocation_site, AllocationSite::kPretenureCreateCountOffset); memento_create_count = SmiAdd(memento_create_count, SmiConstant(Smi::FromInt(1))); StoreObjectFieldNoWriteBarrier(allocation_site, AllocationSite::kPretenureCreateCountOffset, memento_create_count); } // TODO(verwaest): Allocate and fill in double boxes. return copy; } void ConstructorBuiltinsAssembler::CreateFastCloneShallowObjectBuiltin( int properties_count) { DCHECK_GE(properties_count, 0); DCHECK_LE(properties_count, kMaximumClonedShallowObjectProperties); Label call_runtime(this); Node* closure = Parameter(0); Node* literals_index = Parameter(1); Node* properties_count_node = IntPtrConstant(FastCloneShallowObjectPropertiesCount(properties_count)); Node* copy = EmitFastCloneShallowObject( &call_runtime, closure, literals_index, properties_count_node); Return(copy); Bind(&call_runtime); Node* constant_properties = Parameter(2); Node* flags = Parameter(3); Node* context = Parameter(4); TailCallRuntime(Runtime::kCreateObjectLiteral, context, closure, literals_index, constant_properties, flags); } #define SHALLOW_OBJECT_BUILTIN(props) \ TF_BUILTIN(FastCloneShallowObject##props, ConstructorBuiltinsAssembler) { \ CreateFastCloneShallowObjectBuiltin(props); \ } SHALLOW_OBJECT_BUILTIN(0); SHALLOW_OBJECT_BUILTIN(1); SHALLOW_OBJECT_BUILTIN(2); SHALLOW_OBJECT_BUILTIN(3); SHALLOW_OBJECT_BUILTIN(4); SHALLOW_OBJECT_BUILTIN(5); SHALLOW_OBJECT_BUILTIN(6); Handle<Code> Builtins::NewCloneShallowObject(int length) { switch (length) { case 0: return FastCloneShallowObject0(); case 1: return FastCloneShallowObject1(); case 2: return FastCloneShallowObject2(); case 3: return FastCloneShallowObject3(); case 4: return FastCloneShallowObject4(); case 5: return FastCloneShallowObject5(); case 6: return FastCloneShallowObject6(); default: UNREACHABLE(); } return Handle<Code>::null(); } } // namespace internal } // namespace v8