| Index: src/builtins/builtins-constructor.cc
|
| diff --git a/src/builtins/builtins-constructor.cc b/src/builtins/builtins-constructor.cc
|
| index e60f02f74d5839c9dffbc0fd6d03a0588f105c78..241c6d6458fe206b30f9f3c693e02816b46e65ee 100644
|
| --- a/src/builtins/builtins-constructor.cc
|
| +++ b/src/builtins/builtins-constructor.cc
|
| @@ -3,6 +3,7 @@
|
| // 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"
|
| @@ -356,5 +357,386 @@ Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) {
|
| 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* literals_array = LoadObjectField(closure, JSFunction::kLiteralsOffset);
|
| + Node* boilerplate =
|
| + LoadFixedArrayElement(literals_array, literal_index,
|
| + LiteralsArray::kFirstLiteralIndex * kPointerSize,
|
| + 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* literals_array = LoadObjectField(closure, JSFunction::kLiteralsOffset);
|
| + Node* allocation_site =
|
| + LoadFixedArrayElement(literals_array, literal_index,
|
| + LiteralsArray::kFirstLiteralIndex * kPointerSize,
|
| + CodeStubAssembler::SMI_PARAMETERS);
|
| +
|
| + GotoIf(IsUndefined(allocation_site), call_runtime);
|
| + allocation_site =
|
| + LoadFixedArrayElement(literals_array, literal_index,
|
| + LiteralsArray::kFirstLiteralIndex * kPointerSize,
|
| + 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* literals_array = LoadObjectField(closure, JSFunction::kLiteralsOffset);
|
| + Node* allocation_site =
|
| + LoadFixedArrayElement(literals_array, literals_index,
|
| + LiteralsArray::kFirstLiteralIndex * kPointerSize,
|
| + 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);
|
| + GotoUnless(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)));
|
| + GotoUnless(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
|
|
|