Commit ceade6cf authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent ce3d04cf
......@@ -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));
__ str(r1, MemOperand(sp, 0 * kPointerSize));
__ mov(r0, Operand(3));
break;
case ONE:
__ mov(r0, Operand(3));
__ 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));
__ Poke(constructor, 0 * kPointerSize);
__ Mov(x0, Operand(3));
break;
case ONE:
__ Mov(x0, Operand(3));
__ 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));
__ mov(Operand(esp, 1 * kPointerSize), edi);
__ mov(eax, Immediate(3));
break;
case ONE:
__ mov(eax, Immediate(3));
__ 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));
__ sw(a1, MemOperand(sp, 0 * kPointerSize));
__ li(a0, Operand(3));
break;
case ONE:
__ li(a0, Operand(3));
__ 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));
__ sd(a1, MemOperand(sp, 0 * kPointerSize));
__ li(a0, Operand(3));
break;
case ONE:
__ li(a0, Operand(3));
__ 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));
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 might need to transition to holey
ElementsKind kind = constructor->initial_map()->elements_kind();
if (holey && !IsFastHoleyElementsKind(kind)) {
kind = GetHoleyElementsKind(kind);
JSObject::TransitionElementsKind(array, 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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment