#include "src/builtins/builtins-constructor-gen.h"
#include <optional>
#include "src/ast/ast.h"
#include "src/builtins/builtins-call-gen.h"
#include "src/builtins/builtins-constructor.h"
#include "src/builtins/builtins-inl.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/codegen/code-stub-assembler-inl.h"
#include "src/codegen/interface-descriptors.h"
#include "src/codegen/macro-assembler.h"
#include "src/common/globals.h"
#include "src/logging/counters.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
#include "src/codegen/define-code-stub-assembler-macros.inc"
void Builtins::Generate_ConstructVarargs(MacroAssembler* masm) {
Generate_CallOrConstructVarargs(masm, Builtin::kConstruct);
}
void Builtins::Generate_ConstructForwardVarargs(MacroAssembler* masm) {
Generate_CallOrConstructForwardVarargs(masm, CallOrConstructMode::kConstruct,
Builtin::kConstruct);
}
void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) {
Generate_CallOrConstructForwardVarargs(masm, CallOrConstructMode::kConstruct,
Builtin::kConstructFunction);
}
void Builtins::Generate_InterpreterForwardAllArgsThenConstruct(
MacroAssembler* masm) {
return Generate_ConstructForwardAllArgsImpl(masm,
ForwardWhichFrame::kParentFrame);
}
void Builtins::Generate_ConstructForwardAllArgs(MacroAssembler* masm) {
return Generate_ConstructForwardAllArgsImpl(masm,
ForwardWhichFrame::kCurrentFrame);
}
TF_BUILTIN(Construct_Baseline, CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
BuildConstruct(
target, new_target, argc, [=, this] { return LoadContextFromBaseline(); },
[=, this] { return LoadFeedbackVectorFromBaseline(); }, slot,
UpdateFeedbackMode::kGuaranteedFeedback);
}
TF_BUILTIN(Construct_WithFeedback, CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
auto context = Parameter<Context>(Descriptor::kContext);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
BuildConstruct(
target, new_target, argc, [=] { return context; },
[=] { return feedback_vector; }, slot,
UpdateFeedbackMode::kOptionalFeedback);
}
void CallOrConstructBuiltinsAssembler::BuildConstruct(
TNode<JSAny> target, TNode<JSAny> new_target, TNode<Int32T> argc,
const LazyNode<Context>& context,
const LazyNode<Union<Undefined, FeedbackVector>>& feedback_vector,
TNode<UintPtrT> slot, UpdateFeedbackMode mode) {
TVARIABLE(AllocationSite, allocation_site);
Label if_construct_generic(this), if_construct_array(this);
TNode<Context> eager_context = context();
CollectConstructFeedback(eager_context, target, new_target, feedback_vector(),
IntPtrToTaggedIndex(Signed(slot)), mode,
&if_construct_generic, &if_construct_array,
&allocation_site);
BIND(&if_construct_generic);
TailCallBuiltin(Builtin::kConstruct, eager_context, target, new_target, argc);
BIND(&if_construct_array);
TailCallBuiltin(Builtin::kArrayConstructorImpl, eager_context, target,
new_target, argc, allocation_site.value());
}
TF_BUILTIN(ConstructWithArrayLike, CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto arguments_list = Parameter<Object>(Descriptor::kArgumentsList);
auto context = Parameter<Context>(Descriptor::kContext);
CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
}
TF_BUILTIN(ConstructWithSpread, CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto spread = Parameter<JSAny>(Descriptor::kSpread);
auto args_count =
UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
auto context = Parameter<Context>(Descriptor::kContext);
CallOrConstructWithSpread(target, new_target, spread, args_count, context);
}
TF_BUILTIN(ConstructWithSpread_Baseline, CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto spread = Parameter<JSAny>(Descriptor::kSpread);
auto args_count =
UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
auto slot = UncheckedParameter<TaggedIndex>(Descriptor::kSlot);
return BuildConstructWithSpread(
target, new_target, spread, args_count,
[=, this] { return LoadContextFromBaseline(); },
[=, this] { return LoadFeedbackVectorFromBaseline(); }, slot,
UpdateFeedbackMode::kGuaranteedFeedback);
}
TF_BUILTIN(ConstructWithSpread_WithFeedback, CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto spread = Parameter<JSAny>(Descriptor::kSpread);
auto args_count =
UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
auto context = Parameter<Context>(Descriptor::kContext);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kVector);
auto slot = UncheckedParameter<TaggedIndex>(Descriptor::kSlot);
return BuildConstructWithSpread(
target, new_target, spread, args_count, [=] { return context; },
[=] { return feedback_vector; }, slot,
UpdateFeedbackMode::kGuaranteedFeedback);
}
void CallOrConstructBuiltinsAssembler::BuildConstructWithSpread(
TNode<JSAny> target, TNode<JSAny> new_target, TNode<JSAny> spread,
TNode<Int32T> argc, const LazyNode<Context>& context,
const LazyNode<Union<Undefined, FeedbackVector>>& feedback_vector,
TNode<TaggedIndex> slot, UpdateFeedbackMode mode) {
TVARIABLE(AllocationSite, allocation_site);
Label if_construct_generic(this), if_construct_array(this);
TNode<Context> eager_context = context();
CollectConstructFeedback(eager_context, target, new_target, feedback_vector(),
slot, UpdateFeedbackMode::kGuaranteedFeedback,
&if_construct_generic, &if_construct_array,
&allocation_site);
BIND(&if_construct_array);
Goto(&if_construct_generic);
BIND(&if_construct_generic);
CallOrConstructWithSpread(target, new_target, spread, argc, eager_context);
}
TF_BUILTIN(ConstructForwardAllArgs_Baseline, CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto slot = UncheckedParameter<TaggedIndex>(Descriptor::kSlot);
return BuildConstructForwardAllArgs(
target, new_target, [=, this] { return LoadContextFromBaseline(); },
[=, this] { return LoadFeedbackVectorFromBaseline(); }, slot);
}
TF_BUILTIN(ConstructForwardAllArgs_WithFeedback,
CallOrConstructBuiltinsAssembler) {
auto target = Parameter<JSAny>(Descriptor::kTarget);
auto new_target = Parameter<JSAny>(Descriptor::kNewTarget);
auto slot = UncheckedParameter<TaggedIndex>(Descriptor::kSlot);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kVector);
auto context = Parameter<Context>(Descriptor::kContext);
return BuildConstructForwardAllArgs(
target, new_target, [=] { return context; },
[=] { return feedback_vector; }, slot);
}
void CallOrConstructBuiltinsAssembler::BuildConstructForwardAllArgs(
TNode<JSAny> target, TNode<JSAny> new_target,
const LazyNode<Context>& context,
const LazyNode<Union<Undefined, FeedbackVector>>& feedback_vector,
TNode<TaggedIndex> slot) {
TVARIABLE(AllocationSite, allocation_site);
TNode<Context> eager_context = context();
Label construct(this);
CollectConstructFeedback(eager_context, target, new_target, feedback_vector(),
slot, UpdateFeedbackMode::kGuaranteedFeedback,
&construct, &construct, &allocation_site);
BIND(&construct);
TailCallBuiltin(Builtin::kConstructForwardAllArgs, eager_context, target,
new_target);
}
TF_BUILTIN(FastNewClosure, ConstructorBuiltinsAssembler) {
auto shared_function_info =
Parameter<SharedFunctionInfo>(Descriptor::kSharedFunctionInfo);
auto feedback_cell = Parameter<FeedbackCell>(Descriptor::kFeedbackCell);
auto context = Parameter<Context>(Descriptor::kContext);
{
const TNode<Map> 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_DCHECK(this, IsManyClosuresCellMap(feedback_cell_map),
feedback_cell_map, feedback_cell);
Goto(&cell_done);
BIND(&no_closures);
StoreMapNoWriteBarrier(feedback_cell, RootIndex::kOneClosureCellMap);
Goto(&cell_done);
BIND(&one_closure);
TailCallRuntime(Runtime::kNewClosure, context, shared_function_info,
feedback_cell);
BIND(&cell_done);
}
TNode<Uint32T> flags = LoadObjectField<Uint32T>(
shared_function_info, SharedFunctionInfo::kFlagsOffset);
const TNode<IntPtrT> function_map_index = Signed(IntPtrAdd(
DecodeWordFromWord32<SharedFunctionInfo::FunctionMapIndexBits>(flags),
IntPtrConstant(Context::FIRST_FUNCTION_MAP_INDEX)));
CSA_DCHECK(this, UintPtrLessThanOrEqual(
function_map_index,
IntPtrConstant(Context::LAST_FUNCTION_MAP_INDEX)));
const TNode<NativeContext> native_context = LoadNativeContext(context);
const TNode<Map> function_map =
CAST(LoadContextElementNoCell(native_context, function_map_index));
TNode<IntPtrT> instance_size_in_bytes =
TimesTaggedSize(LoadMapInstanceSizeInWords(function_map));
TNode<HeapObject> result = Allocate(instance_size_in_bytes);
StoreMapNoWriteBarrier(result, function_map);
InitializeJSObjectBodyNoSlackTracking(result, function_map,
instance_size_in_bytes,
JSFunction::kSizeWithoutPrototype);
StoreObjectFieldRoot(result, JSObject::kPropertiesOrHashOffset,
RootIndex::kEmptyFixedArray);
StoreObjectFieldRoot(result, JSObject::kElementsOffset,
RootIndex::kEmptyFixedArray);
{
Label done(this), init_prototype(this);
Branch(IsFunctionWithPrototypeSlotMap(function_map), &init_prototype,
&done);
BIND(&init_prototype);
StoreObjectFieldRoot(result, JSFunction::kPrototypeOrInitialMapOffset,
RootIndex::kTheHoleValue);
Goto(&done);
BIND(&done);
}
static_assert(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
StoreObjectFieldNoWriteBarrier(result, JSFunction::kFeedbackCellOffset,
feedback_cell);
StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset,
shared_function_info);
StoreObjectFieldNoWriteBarrier(result, JSFunction::kContextOffset, context);
TNode<JSDispatchHandleT> dispatch_handle = LoadObjectField<JSDispatchHandleT>(
feedback_cell, FeedbackCell::kDispatchHandleOffset);
CSA_DCHECK(this,
Word32NotEqual(dispatch_handle,
Int32Constant(kNullJSDispatchHandle.value())));
StoreObjectFieldNoWriteBarrier(result, JSFunction::kDispatchHandleOffset,
dispatch_handle);
Return(result);
}
TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) {
auto context = Parameter<Context>(Descriptor::kContext);
auto target = Parameter<JSFunction>(Descriptor::kTarget);
auto new_target = Parameter<JSReceiver>(Descriptor::kNewTarget);
Label call_runtime(this);
TNode<JSObject> result =
FastNewObject(context, target, new_target, &call_runtime);
Return(result);
BIND(&call_runtime);
TailCallRuntime(Runtime::kNewObject, context, target, new_target);
}
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context, TNode<JSFunction> target,
TNode<JSReceiver> new_target) {
TVARIABLE(JSObject, var_obj);
Label call_runtime(this), end(this);
var_obj = FastNewObject(context, target, new_target, &call_runtime);
Goto(&end);
BIND(&call_runtime);
var_obj = CAST(CallRuntime(Runtime::kNewObject, context, target, new_target));
Goto(&end);
BIND(&end);
return var_obj.value();
}
TNode<JSObject> ConstructorBuiltinsAssembler::FastNewObject(
TNode<Context> context, TNode<JSFunction> target,
TNode<JSReceiver> new_target, Label* call_runtime) {
Label end(this);
TNode<JSFunction> new_target_func =
HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
TNode<Union<JSReceiver, Map, TheHole>> initial_map_or_proto =
LoadJSFunctionPrototypeOrInitialMap(new_target_func);
GotoIf(DoesntHaveInstanceType(initial_map_or_proto, MAP_TYPE), call_runtime);
TNode<Map> initial_map = CAST(initial_map_or_proto);
TNode<Object> new_target_constructor = LoadObjectField(
initial_map, Map::kConstructorOrBackPointerOrNativeContextOffset);
GotoIf(TaggedNotEqual(target, new_target_constructor), call_runtime);
TVARIABLE(HeapObject, properties);
Label instantiate_map(this), allocate_properties(this);
GotoIf(IsDictionaryMap(initial_map), &allocate_properties);
{
properties = EmptyFixedArrayConstant();
Goto(&instantiate_map);
}
BIND(&allocate_properties);
{
properties =
AllocatePropertyDictionary(PropertyDictionary::kInitialCapacity);
Goto(&instantiate_map);
}
BIND(&instantiate_map);
return AllocateJSObjectFromMap(initial_map, properties.value(), std::nullopt,
AllocationFlag::kNone, kWithSlackTracking);
}
TNode<Context> ConstructorBuiltinsAssembler::FastNewFunctionContext(
TNode<ScopeInfo> scope_info, TNode<Uint32T> slots, TNode<Context> context,
ScopeType scope_type, ContextMode context_mode) {
TNode<IntPtrT> slots_intptr = Signed(ChangeUint32ToWord(slots));
TNode<IntPtrT> size = ElementOffsetFromIndex(slots_intptr, PACKED_ELEMENTS,
Context::kTodoHeaderSize);
TNode<Context> function_context =
UncheckedCast<Context>(AllocateInNewSpace(size));
TNode<NativeContext> native_context = LoadNativeContext(context);
Context::Field index;
switch (scope_type) {
case EVAL_SCOPE:
index = Context::EVAL_CONTEXT_MAP_INDEX;
break;
case FUNCTION_SCOPE:
index = Context::FUNCTION_CONTEXT_MAP_INDEX;
break;
default:
UNREACHABLE();
}
TNode<Map> map = CAST(LoadContextElementNoCell(native_context, index));
StoreMapNoWriteBarrier(function_context, map);
TNode<IntPtrT> min_context_slots = IntPtrConstant(Context::MIN_CONTEXT_SLOTS);
TNode<IntPtrT> length = IntPtrAdd(slots_intptr, min_context_slots);
StoreObjectFieldNoWriteBarrier(function_context, Context::kLengthOffset,
SmiTag(length));
StoreObjectFieldNoWriteBarrier(function_context, Context::kScopeInfoOffset,
scope_info);
StoreObjectFieldNoWriteBarrier(function_context, Context::kPreviousOffset,
context);
TNode<Object> undefined = UndefinedConstant();
if (context_mode == ContextMode::kHasContextCells) {
TVARIABLE(IntPtrT, offset, IntPtrConstant(Context::kTodoHeaderSize));
TNode<IntPtrT> local_count = LoadScopeInfoContextLocalCount(scope_info);
Label extension_field_done(this);
GotoIfNot(LoadScopeInfoHasExtensionField(scope_info),
&extension_field_done);
StoreObjectFieldNoWriteBarrier(function_context, Context::kExtensionOffset,
undefined);
offset = IntPtrAdd(offset.value(), IntPtrConstant(kTaggedSize));
Goto(&extension_field_done);
BIND(&extension_field_done);
CodeStubAssembler::VariableList vars({&offset}, zone());
BuildFastLoop<IntPtrT>(
vars, IntPtrConstant(0), local_count,
[&](TNode<IntPtrT> index) {
TNode<Object> init_value =
GetFunctionContextSlotInitialValue(scope_info, index);
StoreObjectFieldNoWriteBarrier(function_context, offset.value(),
init_value);
offset = IntPtrAdd(offset.value(), IntPtrConstant(kTaggedSize));
},
1, LoopUnrollingMode::kYes, IndexAdvanceMode::kPost);
Label done(this);
GotoIf(IntPtrEqual(offset.value(), size), &done);
StoreObjectFieldNoWriteBarrier(function_context, offset.value(), undefined);
CSA_DCHECK(this, IntPtrEqual(
IntPtrAdd(offset.value(), IntPtrConstant(kTaggedSize)),
size));
Goto(&done);
BIND(&done);
return function_context;
} else {
DCHECK_EQ(context_mode, ContextMode::kNoContextCells);
TNode<IntPtrT> start_offset = IntPtrConstant(Context::kTodoHeaderSize);
CodeStubAssembler::VariableList vars(0, zone());
BuildFastLoop<IntPtrT>(
vars, start_offset, size,
[=, this](TNode<IntPtrT> offset) {
StoreObjectFieldNoWriteBarrier(function_context, offset, undefined);
},
kTaggedSize, LoopUnrollingMode::kYes, IndexAdvanceMode::kPost);
return function_context;
}
}
TNode<JSRegExp> ConstructorBuiltinsAssembler::CreateRegExpLiteral(
TNode<HeapObject> maybe_feedback_vector, TNode<TaggedIndex> slot,
TNode<Object> pattern, TNode<Smi> flags, TNode<Context> context) {
Label call_runtime(this, Label::kDeferred), end(this);
GotoIf(IsUndefined(maybe_feedback_vector), &call_runtime);
TVARIABLE(JSRegExp, result);
TNode<FeedbackVector> feedback_vector = CAST(maybe_feedback_vector);
TNode<Object> literal_site =
CAST(LoadFeedbackVectorSlot(feedback_vector, slot));
GotoIfNot(HasBoilerplate(literal_site), &call_runtime);
{
static_assert(JSRegExp::kDataOffset == JSObject::kHeaderSize);
static_assert(JSRegExp::kSourceOffset ==
JSRegExp::kDataOffset + kTaggedSize);
static_assert(JSRegExp::kFlagsOffset ==
JSRegExp::kSourceOffset + kTaggedSize);
static_assert(JSRegExp::kHeaderSize ==
JSRegExp::kFlagsOffset + kTaggedSize);
static_assert(JSRegExp::kLastIndexOffset == JSRegExp::kHeaderSize);
DCHECK_EQ(JSRegExp::Size(), JSRegExp::kLastIndexOffset + kTaggedSize);
TNode<RegExpBoilerplateDescription> boilerplate = CAST(literal_site);
TNode<HeapObject> new_object = Allocate(JSRegExp::Size());
TNode<JSFunction> regexp_function = CAST(LoadContextElementNoCell(
LoadNativeContext(context), Context::REGEXP_FUNCTION_INDEX));
TNode<Map> initial_map = CAST(LoadObjectField(
regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
StoreMapNoWriteBarrier(new_object, initial_map);
StoreObjectFieldRoot(new_object, JSReceiver::kPropertiesOrHashOffset,
RootIndex::kEmptyFixedArray);
StoreObjectFieldRoot(new_object, JSObject::kElementsOffset,
RootIndex::kEmptyFixedArray);
StoreTrustedPointerField(
new_object, JSRegExp::kDataOffset, kRegExpDataIndirectPointerTag,
CAST(LoadTrustedPointerFromObject(
boilerplate, offsetof(RegExpBoilerplateDescription, data_),
kRegExpDataIndirectPointerTag)));
StoreObjectFieldNoWriteBarrier(
new_object, JSRegExp::kSourceOffset,
LoadObjectField(boilerplate,
offsetof(RegExpBoilerplateDescription, source_)));
StoreObjectFieldNoWriteBarrier(
new_object, JSRegExp::kFlagsOffset,
LoadObjectField(boilerplate,
offsetof(RegExpBoilerplateDescription, flags_)));
StoreObjectFieldNoWriteBarrier(
new_object, JSRegExp::kLastIndexOffset,
SmiConstant(JSRegExp::kInitialLastIndexValue));
result = CAST(new_object);
Goto(&end);
}
BIND(&call_runtime);
{
result = CAST(CallRuntime(Runtime::kCreateRegExpLiteral, context,
maybe_feedback_vector, slot, pattern, flags));
Goto(&end);
}
BIND(&end);
return result.value();
}
TNode<JSArray> ConstructorBuiltinsAssembler::CreateShallowArrayLiteral(
TNode<FeedbackVector> feedback_vector, TNode<TaggedIndex> slot,
TNode<Context> context, AllocationSiteMode allocation_site_mode,
Label* call_runtime) {
TNode<Object> maybe_allocation_site =
CAST(LoadFeedbackVectorSlot(feedback_vector, slot));
GotoIfNot(HasBoilerplate(maybe_allocation_site), call_runtime);
TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site);
TNode<JSArray> boilerplate = CAST(LoadBoilerplate(allocation_site));
if (allocation_site_mode == TRACK_ALLOCATION_SITE &&
V8_ALLOCATION_SITE_TRACKING_BOOL) {
return CloneFastJSArray(context, boilerplate, allocation_site);
} else {
return CloneFastJSArray(context, boilerplate);
}
}
TNode<JSArray> ConstructorBuiltinsAssembler::CreateEmptyArrayLiteral(
TNode<FeedbackVector> feedback_vector, TNode<TaggedIndex> slot,
TNode<Context> context) {
TNode<Object> maybe_allocation_site =
CAST(LoadFeedbackVectorSlot(feedback_vector, slot));
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);
}
BIND(&initialize_allocation_site);
{
allocation_site = CreateAllocationSiteInFeedbackVector(
feedback_vector,
Unsigned(TaggedIndexToIntPtr(slot)));
Goto(&create_empty_array);
}
BIND(&create_empty_array);
TNode<Int32T> kind = LoadElementsKind(allocation_site.value());
TNode<NativeContext> native_context = LoadNativeContext(context);
Comment("LoadJSArrayElementsMap");
TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
TNode<IntPtrT> zero_intptr = IntPtrConstant(0);
TNode<Smi> zero = SmiConstant(0);
Comment("Allocate JSArray");
std::optional<TNode<AllocationSite>> site =
V8_ALLOCATION_SITE_TRACKING_BOOL
? std::make_optional(allocation_site.value())
: std::nullopt;
TNode<JSArray> result = AllocateJSArray(GetInitialFastElementsKind(),
array_map, zero_intptr, zero, site);
Goto(&done);
BIND(&done);
return result;
}
TNode<HeapObject> ConstructorBuiltinsAssembler::CreateShallowObjectLiteral(
TNode<FeedbackVector> feedback_vector, TNode<TaggedIndex> slot,
Label* call_runtime) {
TNode<Object> maybe_allocation_site =
CAST(LoadFeedbackVectorSlot(feedback_vector, slot));
GotoIfNot(HasBoilerplate(maybe_allocation_site), call_runtime);
TNode<AllocationSite> allocation_site = CAST(maybe_allocation_site);
TNode<JSObject> boilerplate = LoadBoilerplate(allocation_site);
return CreateShallowObjectLiteral(allocation_site, boilerplate, call_runtime);
}
TNode<HeapObject> ConstructorBuiltinsAssembler::CreateShallowObjectLiteral(
TNode<AllocationSite> allocation_site, TNode<JSObject> boilerplate,
Label* call_runtime, bool bailout_if_dictionary_properties) {
TNode<Map> boilerplate_map = LoadMap(boilerplate);
CSA_DCHECK(this, IsJSObjectMap(boilerplate_map));
TVARIABLE(HeapObject, var_properties);
{
TNode<Uint32T> bit_field_3 = LoadMapBitField3(boilerplate_map);
GotoIf(IsSetWord32<Map::Bits3::IsDeprecatedBit>(bit_field_3), call_runtime);
Label if_dictionary(this), if_fast(this), done(this);
Branch(IsSetWord32<Map::Bits3::IsDictionaryMapBit>(bit_field_3),
&if_dictionary, &if_fast);
BIND(&if_dictionary);
{
if (bailout_if_dictionary_properties) {
Goto(call_runtime);
} else {
Comment("Copy dictionary properties");
if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
var_properties =
CopySwissNameDictionary(CAST(LoadSlowProperties(boilerplate)));
} else {
var_properties = CopyNameDictionary(
CAST(LoadSlowProperties(boilerplate)), call_runtime);
}
Goto(&done);
}
}
BIND(&if_fast);
{
TNode<HeapObject> boilerplate_properties =
LoadFastProperties(boilerplate);
GotoIfNot(IsEmptyFixedArray(boilerplate_properties), call_runtime);
var_properties = EmptyFixedArrayConstant();
Goto(&done);
}
BIND(&done);
}
TVARIABLE(FixedArrayBase, var_elements);
{
Label if_empty_fixed_array(this), if_copy_elements(this), done(this);
TNode<FixedArrayBase> boilerplate_elements = LoadElements(boilerplate);
Branch(IsEmptyFixedArray(boilerplate_elements), &if_empty_fixed_array,
&if_copy_elements);
BIND(&if_empty_fixed_array);
var_elements = boilerplate_elements;
Goto(&done);
BIND(&if_copy_elements);
CSA_DCHECK(this, Word32BinaryNot(
IsFixedCOWArrayMap(LoadMap(boilerplate_elements))));
auto flags = ExtractFixedArrayFlag::kAllFixedArrays;
var_elements = CloneFixedArray(boilerplate_elements, flags);
Goto(&done);
BIND(&done);
}
static_assert(JSObject::kMaxInstanceSize < kMaxRegularHeapObjectSize);
TNode<IntPtrT> instance_size =
TimesTaggedSize(LoadMapInstanceSizeInWords(boilerplate_map));
TNode<IntPtrT> aligned_instance_size =
AlignToAllocationAlignment(instance_size);
TNode<IntPtrT> allocation_size = instance_size;
bool needs_allocation_memento = v8_flags.allocation_site_pretenuring;
if (needs_allocation_memento) {
DCHECK(V8_ALLOCATION_SITE_TRACKING_BOOL);
allocation_size = IntPtrAdd(aligned_instance_size,
IntPtrConstant(ALIGN_TO_ALLOCATION_ALIGNMENT(
sizeof(AllocationMemento))));
}
TNode<HeapObject> copy =
UncheckedCast<HeapObject>(AllocateInNewSpace(allocation_size));
{
Comment("Initialize Literal Copy");
StoreMapNoWriteBarrier(copy, boilerplate_map);
StoreObjectFieldNoWriteBarrier(copy, JSObject::kPropertiesOrHashOffset,
var_properties.value());
StoreObjectFieldNoWriteBarrier(copy, JSObject::kElementsOffset,
var_elements.value());
}
if (needs_allocation_memento) {
InitializeAllocationMemento(copy, aligned_instance_size, allocation_site);
}
{
Label continue_with_write_barrier(this), done_init(this);
TVARIABLE(IntPtrT, offset, IntPtrConstant(JSObject::kHeaderSize));
{
Comment("Copy in-object properties fast");
Label continue_fast(this, &offset);
Branch(IntPtrEqual(offset.value(), instance_size), &done_init,
&continue_fast);
BIND(&continue_fast);
TNode<Object> field = LoadObjectField(boilerplate, offset.value());
Label store_field(this);
GotoIf(TaggedIsSmi(field), &store_field);
GotoIf(IsHeapNumber(CAST(field)), &continue_with_write_barrier);
Goto(&store_field);
BIND(&store_field);
StoreObjectFieldNoWriteBarrier(copy, offset.value(), field);
offset = IntPtrAdd(offset.value(), IntPtrConstant(kTaggedSize));
Branch(WordNotEqual(offset.value(), instance_size), &continue_fast,
&done_init);
}
BIND(&continue_with_write_barrier);
{
Comment("Copy in-object properties slow");
BuildFastLoop<IntPtrT>(
offset.value(), instance_size,
[=, this](TNode<IntPtrT> offset) {
TNode<Object> field = LoadObjectField(boilerplate, offset);
StoreObjectFieldNoWriteBarrier(copy, offset, field);
},
kTaggedSize, LoopUnrollingMode::kYes, IndexAdvanceMode::kPost);
CopyMutableHeapNumbersInObject(copy, offset.value(), instance_size);
Goto(&done_init);
}
BIND(&done_init);
}
return copy;
}
TNode<JSObject> ConstructorBuiltinsAssembler::CreateEmptyObjectLiteral(
TNode<Context> context) {
TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<Map> map = LoadObjectFunctionInitialMap(native_context);
static_assert(Map::kNoSlackTracking == 0);
CSA_DCHECK(this, IsClearWord32<Map::Bits3::ConstructionCounterBits>(
LoadMapBitField3(map)));
TNode<FixedArray> empty_fixed_array = EmptyFixedArrayConstant();
TNode<JSObject> result =
AllocateJSObjectFromMap(map, empty_fixed_array, empty_fixed_array);
return result;
}
void ConstructorBuiltinsAssembler::CopyMutableHeapNumbersInObject(
TNode<HeapObject> copy, TNode<IntPtrT> start_offset,
TNode<IntPtrT> end_offset) {
Comment("Copy mutable HeapNumber values");
BuildFastLoop<IntPtrT>(
start_offset, end_offset,
[=, this](TNode<IntPtrT> offset) {
TNode<Object> field = LoadObjectField(copy, offset);
Label copy_heap_number(this, Label::kDeferred), continue_loop(this);
GotoIf(TaggedIsSmi(field), &continue_loop);
Branch(IsHeapNumber(CAST(field)), ©_heap_number, &continue_loop);
BIND(©_heap_number);
{
TNode<Float64T> double_value = LoadHeapNumberValue(CAST(field));
TNode<HeapNumber> heap_number =
AllocateHeapNumberWithValue(double_value);
StoreObjectField(copy, offset, heap_number);
Goto(&continue_loop);
}
BIND(&continue_loop);
},
kTaggedSize, LoopUnrollingMode::kNo, IndexAdvanceMode::kPost);
}
#include "src/codegen/undef-code-stub-assembler-macros.inc"
}
}