[runtime] Introduce a proper %NewArray runtime entry.
This adds a new %NewArray runtime entry, which constructs a new JSArray and does the subclassing correctly (to the same degree that %NewObject does currently), and also deals properly with the AllocationSite feedback mechanism. This runtime entry will be used by TurboFan and is also used as a fallback in the subclassing case in the stub currently. BUG=v8:3101, v8:3330 LOG=n Review URL: https://codereview.chromium.org/1456423003 Cr-Commit-Position: refs/heads/master@{#32131}
This commit is contained in:
parent
ce3d04cff4
commit
ceade6cf23
@ -4821,25 +4821,23 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
|
||||
GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES);
|
||||
|
||||
__ bind(&subclassing);
|
||||
__ push(r1);
|
||||
__ push(r3);
|
||||
|
||||
// Adjust argc.
|
||||
switch (argument_count()) {
|
||||
case ANY:
|
||||
case MORE_THAN_ONE:
|
||||
__ add(r0, r0, Operand(2));
|
||||
__ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
|
||||
__ add(r0, r0, Operand(3));
|
||||
break;
|
||||
case NONE:
|
||||
__ mov(r0, Operand(2));
|
||||
break;
|
||||
case ONE:
|
||||
__ str(r1, MemOperand(sp, 0 * kPointerSize));
|
||||
__ mov(r0, Operand(3));
|
||||
break;
|
||||
case ONE:
|
||||
__ str(r1, MemOperand(sp, 1 * kPointerSize));
|
||||
__ mov(r0, Operand(4));
|
||||
break;
|
||||
}
|
||||
|
||||
__ JumpToExternalReference(
|
||||
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
|
||||
__ Push(r3, r2);
|
||||
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -5215,22 +5215,23 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
// Subclassing support.
|
||||
__ Bind(&subclassing);
|
||||
__ Push(constructor, new_target);
|
||||
// Adjust argc.
|
||||
switch (argument_count()) {
|
||||
case ANY:
|
||||
case MORE_THAN_ONE:
|
||||
__ add(x0, x0, Operand(2));
|
||||
__ Poke(constructor, Operand(x0, LSL, kPointerSizeLog2));
|
||||
__ Add(x0, x0, Operand(3));
|
||||
break;
|
||||
case NONE:
|
||||
__ Mov(x0, Operand(2));
|
||||
break;
|
||||
case ONE:
|
||||
__ Poke(constructor, 0 * kPointerSize);
|
||||
__ Mov(x0, Operand(3));
|
||||
break;
|
||||
case ONE:
|
||||
__ Poke(constructor, 1 * kPointerSize);
|
||||
__ Mov(x0, Operand(4));
|
||||
break;
|
||||
}
|
||||
__ JumpToExternalReference(
|
||||
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
|
||||
__ Push(new_target, allocation_site);
|
||||
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -5019,27 +5019,26 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
// Subclassing.
|
||||
__ bind(&subclassing);
|
||||
__ pop(ecx); // return address.
|
||||
__ push(edi);
|
||||
__ push(edx);
|
||||
|
||||
// Adjust argc.
|
||||
switch (argument_count()) {
|
||||
case ANY:
|
||||
case MORE_THAN_ONE:
|
||||
__ add(eax, Immediate(2));
|
||||
__ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi);
|
||||
__ add(eax, Immediate(3));
|
||||
break;
|
||||
case NONE:
|
||||
__ mov(eax, Immediate(2));
|
||||
break;
|
||||
case ONE:
|
||||
__ mov(Operand(esp, 1 * kPointerSize), edi);
|
||||
__ mov(eax, Immediate(3));
|
||||
break;
|
||||
case ONE:
|
||||
__ mov(Operand(esp, 2 * kPointerSize), edi);
|
||||
__ mov(eax, Immediate(4));
|
||||
break;
|
||||
}
|
||||
|
||||
__ push(ecx);
|
||||
__ JumpToExternalReference(
|
||||
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
|
||||
__ PopReturnAddressTo(ecx);
|
||||
__ Push(edx);
|
||||
__ Push(ebx);
|
||||
__ PushReturnAddressFrom(ecx);
|
||||
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -5030,26 +5030,26 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
// Subclassing.
|
||||
__ bind(&subclassing);
|
||||
__ Push(a1);
|
||||
__ Push(a3);
|
||||
|
||||
// Adjust argc.
|
||||
switch (argument_count()) {
|
||||
case ANY:
|
||||
case MORE_THAN_ONE:
|
||||
__ li(at, Operand(2));
|
||||
__ sll(at, a0, kPointerSizeLog2);
|
||||
__ addu(at, sp, at);
|
||||
__ sw(a1, MemOperand(at));
|
||||
__ li(at, Operand(3));
|
||||
__ addu(a0, a0, at);
|
||||
break;
|
||||
case NONE:
|
||||
__ li(a0, Operand(2));
|
||||
break;
|
||||
case ONE:
|
||||
__ sw(a1, MemOperand(sp, 0 * kPointerSize));
|
||||
__ li(a0, Operand(3));
|
||||
break;
|
||||
case ONE:
|
||||
__ sw(a1, MemOperand(sp, 1 * kPointerSize));
|
||||
__ li(a0, Operand(4));
|
||||
break;
|
||||
}
|
||||
|
||||
__ JumpToExternalReference(
|
||||
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
|
||||
__ Push(a3, a2);
|
||||
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -5055,26 +5055,26 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
// Subclassing.
|
||||
__ bind(&subclassing);
|
||||
__ Push(a1);
|
||||
__ Push(a3);
|
||||
|
||||
// Adjust argc.
|
||||
switch (argument_count()) {
|
||||
case ANY:
|
||||
case MORE_THAN_ONE:
|
||||
__ li(at, Operand(2));
|
||||
__ addu(a0, a0, at);
|
||||
__ dsll(at, a0, kPointerSizeLog2);
|
||||
__ Daddu(at, sp, at);
|
||||
__ sd(a1, MemOperand(at));
|
||||
__ li(at, Operand(3));
|
||||
__ Daddu(a0, a0, at);
|
||||
break;
|
||||
case NONE:
|
||||
__ li(a0, Operand(2));
|
||||
break;
|
||||
case ONE:
|
||||
__ sd(a1, MemOperand(sp, 0 * kPointerSize));
|
||||
__ li(a0, Operand(3));
|
||||
break;
|
||||
case ONE:
|
||||
__ sd(a1, MemOperand(sp, 1 * kPointerSize));
|
||||
__ li(a0, Operand(4));
|
||||
break;
|
||||
}
|
||||
|
||||
__ JumpToExternalReference(
|
||||
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
|
||||
__ Push(a3, a2);
|
||||
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -233,15 +233,24 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
|
||||
}
|
||||
|
||||
|
||||
static Object* ArrayConstructorCommon(Isolate* isolate,
|
||||
Handle<JSFunction> constructor,
|
||||
Handle<JSFunction> new_target,
|
||||
Handle<AllocationSite> site,
|
||||
Arguments* caller_args) {
|
||||
namespace {
|
||||
|
||||
Object* ArrayConstructorCommon(Isolate* isolate, Handle<JSFunction> constructor,
|
||||
Handle<JSReceiver> new_target,
|
||||
Handle<AllocationSite> site,
|
||||
Arguments* caller_args) {
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
// If called through new, new.target can be:
|
||||
// - a subclass of constructor,
|
||||
// - a proxy wrapper around constructor, or
|
||||
// - the constructor itself.
|
||||
// If called through Reflect.construct, it's guaranteed to be a constructor by
|
||||
// REFLECT_CONSTRUCT_PREPARE.
|
||||
DCHECK(new_target->IsConstructor());
|
||||
|
||||
bool holey = false;
|
||||
bool can_use_type_feedback = true;
|
||||
bool can_use_type_feedback = !site.is_null();
|
||||
bool can_inline_array_constructor = true;
|
||||
if (caller_args->length() == 1) {
|
||||
Handle<Object> argument_one = caller_args->at<Object>(0);
|
||||
@ -263,43 +272,42 @@ static Object* ArrayConstructorCommon(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
Handle<JSArray> array;
|
||||
if (!site.is_null() && can_use_type_feedback) {
|
||||
ElementsKind to_kind = site->GetElementsKind();
|
||||
if (holey && !IsFastHoleyElementsKind(to_kind)) {
|
||||
to_kind = GetHoleyElementsKind(to_kind);
|
||||
// Update the allocation site info to reflect the advice alteration.
|
||||
site->SetElementsKind(to_kind);
|
||||
}
|
||||
// TODO(verwaest): new_target could be a proxy. Read new.target.prototype in
|
||||
// that case.
|
||||
Handle<JSFunction> original_function = Handle<JSFunction>::cast(new_target);
|
||||
|
||||
// We should allocate with an initial map that reflects the allocation site
|
||||
// advice. Therefore we use AllocateJSObjectFromMap instead of passing
|
||||
// the constructor.
|
||||
Handle<Map> initial_map(constructor->initial_map(), isolate);
|
||||
if (to_kind != initial_map->elements_kind()) {
|
||||
initial_map = Map::AsElementsKind(initial_map, to_kind);
|
||||
}
|
||||
JSFunction::EnsureHasInitialMap(constructor);
|
||||
|
||||
// If we don't care to track arrays of to_kind ElementsKind, then
|
||||
// don't emit a memento for them.
|
||||
Handle<AllocationSite> allocation_site;
|
||||
if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) {
|
||||
allocation_site = site;
|
||||
}
|
||||
// TODO(verwaest): original_function could have non-instance-prototype
|
||||
// (non-JSReceiver), requiring fallback to the intrinsicDefaultProto.
|
||||
Handle<Map> initial_map =
|
||||
JSFunction::EnsureDerivedHasInitialMap(original_function, constructor);
|
||||
|
||||
array = Handle<JSArray>::cast(
|
||||
factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site));
|
||||
} else {
|
||||
array = Handle<JSArray>::cast(factory->NewJSObject(constructor));
|
||||
|
||||
// We might need to transition to holey
|
||||
ElementsKind kind = constructor->initial_map()->elements_kind();
|
||||
if (holey && !IsFastHoleyElementsKind(kind)) {
|
||||
kind = GetHoleyElementsKind(kind);
|
||||
JSObject::TransitionElementsKind(array, kind);
|
||||
}
|
||||
ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind()
|
||||
: initial_map->elements_kind();
|
||||
if (holey && !IsFastHoleyElementsKind(to_kind)) {
|
||||
to_kind = GetHoleyElementsKind(to_kind);
|
||||
// Update the allocation site info to reflect the advice alteration.
|
||||
if (!site.is_null()) site->SetElementsKind(to_kind);
|
||||
}
|
||||
|
||||
// We should allocate with an initial map that reflects the allocation site
|
||||
// advice. Therefore we use AllocateJSObjectFromMap instead of passing
|
||||
// the constructor.
|
||||
if (to_kind != initial_map->elements_kind()) {
|
||||
initial_map = Map::AsElementsKind(initial_map, to_kind);
|
||||
}
|
||||
|
||||
// If we don't care to track arrays of to_kind ElementsKind, then
|
||||
// don't emit a memento for them.
|
||||
Handle<AllocationSite> allocation_site;
|
||||
if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) {
|
||||
allocation_site = site;
|
||||
}
|
||||
|
||||
Handle<JSArray> array = Handle<JSArray>::cast(
|
||||
factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site));
|
||||
|
||||
factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
|
||||
|
||||
ElementsKind old_kind = array->GetElementsKind();
|
||||
@ -314,21 +322,28 @@ static Object* ArrayConstructorCommon(Isolate* isolate,
|
||||
site->SetDoNotInlineCall();
|
||||
}
|
||||
|
||||
// Set up the prototoype using original function.
|
||||
// TODO(dslomov): instead of setting the __proto__,
|
||||
// use and cache the correct map.
|
||||
if (*new_target != *constructor) {
|
||||
if (new_target->has_instance_prototype()) {
|
||||
Handle<Object> prototype(new_target->instance_prototype(), isolate);
|
||||
MAYBE_RETURN(JSObject::SetPrototype(array, prototype, false,
|
||||
Object::THROW_ON_ERROR),
|
||||
isolate->heap()->exception());
|
||||
}
|
||||
}
|
||||
|
||||
return *array;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NewArray) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_LE(3, args.length());
|
||||
int const argc = args.length() - 3;
|
||||
// TODO(bmeurer): Remove this Arguments nonsense.
|
||||
Arguments argv(argc, args.arguments() - 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2);
|
||||
// TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite.
|
||||
Handle<AllocationSite> site = type_info->IsAllocationSite()
|
||||
? Handle<AllocationSite>::cast(type_info)
|
||||
: Handle<AllocationSite>::null();
|
||||
return ArrayConstructorCommon(isolate, constructor, new_target, site, &argv);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArrayConstructor) {
|
||||
HandleScope scope(isolate);
|
||||
@ -364,25 +379,6 @@ RUNTIME_FUNCTION(Runtime_ArrayConstructor) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArrayConstructorWithSubclassing) {
|
||||
HandleScope scope(isolate);
|
||||
int args_length = args.length();
|
||||
CHECK(args_length >= 2);
|
||||
|
||||
// This variables and checks work around -Werror=strict-overflow.
|
||||
int pre_last_arg_index = args_length - 2;
|
||||
int last_arg_index = args_length - 1;
|
||||
CHECK(pre_last_arg_index >= 0);
|
||||
CHECK(last_arg_index >= 0);
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, pre_last_arg_index);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, new_target, last_arg_index);
|
||||
Arguments caller_args(args_length - 2, args.arguments());
|
||||
return ArrayConstructorCommon(isolate, constructor, new_target,
|
||||
Handle<AllocationSite>::null(), &caller_args);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InternalArrayConstructor) {
|
||||
HandleScope scope(isolate);
|
||||
Arguments empty_args(0, NULL);
|
||||
|
@ -30,26 +30,26 @@ namespace internal {
|
||||
|
||||
// Entries have the form F(name, number of arguments, number of values):
|
||||
|
||||
#define FOR_EACH_INTRINSIC_ARRAY(F) \
|
||||
F(FinishArrayPrototypeSetup, 1, 1) \
|
||||
F(SpecialArrayFunctions, 0, 1) \
|
||||
F(TransitionElementsKind, 2, 1) \
|
||||
F(PushIfAbsent, 2, 1) \
|
||||
F(RemoveArrayHoles, 2, 1) \
|
||||
F(MoveArrayContents, 2, 1) \
|
||||
F(EstimateNumberOfElements, 1, 1) \
|
||||
F(GetArrayKeys, 2, 1) \
|
||||
F(ArrayConstructor, -1, 1) \
|
||||
F(ArrayConstructorWithSubclassing, -1, 1) \
|
||||
F(InternalArrayConstructor, -1, 1) \
|
||||
F(NormalizeElements, 1, 1) \
|
||||
F(GrowArrayElements, 2, 1) \
|
||||
F(HasComplexElements, 1, 1) \
|
||||
F(IsArray, 1, 1) \
|
||||
F(HasCachedArrayIndex, 1, 1) \
|
||||
F(GetCachedArrayIndex, 1, 1) \
|
||||
F(FixedArrayGet, 2, 1) \
|
||||
F(FixedArraySet, 3, 1) \
|
||||
#define FOR_EACH_INTRINSIC_ARRAY(F) \
|
||||
F(FinishArrayPrototypeSetup, 1, 1) \
|
||||
F(SpecialArrayFunctions, 0, 1) \
|
||||
F(TransitionElementsKind, 2, 1) \
|
||||
F(PushIfAbsent, 2, 1) \
|
||||
F(RemoveArrayHoles, 2, 1) \
|
||||
F(MoveArrayContents, 2, 1) \
|
||||
F(EstimateNumberOfElements, 1, 1) \
|
||||
F(GetArrayKeys, 2, 1) \
|
||||
F(ArrayConstructor, -1, 1) \
|
||||
F(NewArray, -1 /* >= 3 */, 1) \
|
||||
F(InternalArrayConstructor, -1, 1) \
|
||||
F(NormalizeElements, 1, 1) \
|
||||
F(GrowArrayElements, 2, 1) \
|
||||
F(HasComplexElements, 1, 1) \
|
||||
F(IsArray, 1, 1) \
|
||||
F(HasCachedArrayIndex, 1, 1) \
|
||||
F(GetCachedArrayIndex, 1, 1) \
|
||||
F(FixedArrayGet, 2, 1) \
|
||||
F(FixedArraySet, 3, 1) \
|
||||
F(FastOneByteArrayJoin, 2, 1)
|
||||
|
||||
|
||||
|
@ -4734,28 +4734,33 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
// Subclassing
|
||||
__ bind(&subclassing);
|
||||
__ Pop(rcx); // return address.
|
||||
__ Push(rdi);
|
||||
__ Push(rdx);
|
||||
|
||||
// Adjust argc.
|
||||
switch (argument_count()) {
|
||||
case ANY:
|
||||
case MORE_THAN_ONE:
|
||||
__ addp(rax, Immediate(2));
|
||||
case MORE_THAN_ONE: {
|
||||
StackArgumentsAccessor args(rsp, rax);
|
||||
__ movp(args.GetReceiverOperand(), rdi);
|
||||
__ addp(rax, Immediate(3));
|
||||
break;
|
||||
case NONE:
|
||||
__ movp(rax, Immediate(2));
|
||||
}
|
||||
case NONE: {
|
||||
StackArgumentsAccessor args(rsp, 0);
|
||||
__ movp(args.GetReceiverOperand(), rdi);
|
||||
__ Set(rax, 3);
|
||||
break;
|
||||
case ONE:
|
||||
__ movp(rax, Immediate(3));
|
||||
}
|
||||
case ONE: {
|
||||
StackArgumentsAccessor args(rsp, 1);
|
||||
__ movp(args.GetReceiverOperand(), rdi);
|
||||
__ Set(rax, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__ Push(rcx);
|
||||
__ JumpToExternalReference(
|
||||
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()),
|
||||
1);
|
||||
__ PopReturnAddressTo(rcx);
|
||||
__ Push(rdx);
|
||||
__ Push(rbx);
|
||||
__ PushReturnAddressFrom(rcx);
|
||||
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()),
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user