[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:
bmeurer 2015-11-19 22:21:11 -08:00 committed by Commit bot
parent ce3d04cff4
commit ceade6cf23
8 changed files with 159 additions and 160 deletions

View File

@ -4821,25 +4821,23 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES); GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES);
__ bind(&subclassing); __ bind(&subclassing);
__ push(r1);
__ push(r3);
// Adjust argc.
switch (argument_count()) { switch (argument_count()) {
case ANY: case ANY:
case MORE_THAN_ONE: case MORE_THAN_ONE:
__ add(r0, r0, Operand(2)); __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
__ add(r0, r0, Operand(3));
break; break;
case NONE: case NONE:
__ mov(r0, Operand(2)); __ str(r1, MemOperand(sp, 0 * kPointerSize));
break;
case ONE:
__ mov(r0, Operand(3)); __ mov(r0, Operand(3));
break; break;
case ONE:
__ str(r1, MemOperand(sp, 1 * kPointerSize));
__ mov(r0, Operand(4));
break;
} }
__ Push(r3, r2);
__ JumpToExternalReference( __ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
} }

View File

@ -5215,22 +5215,23 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
// Subclassing support. // Subclassing support.
__ Bind(&subclassing); __ Bind(&subclassing);
__ Push(constructor, new_target);
// Adjust argc.
switch (argument_count()) { switch (argument_count()) {
case ANY: case ANY:
case MORE_THAN_ONE: case MORE_THAN_ONE:
__ add(x0, x0, Operand(2)); __ Poke(constructor, Operand(x0, LSL, kPointerSizeLog2));
__ Add(x0, x0, Operand(3));
break; break;
case NONE: case NONE:
__ Mov(x0, Operand(2)); __ Poke(constructor, 0 * kPointerSize);
break;
case ONE:
__ Mov(x0, Operand(3)); __ Mov(x0, Operand(3));
break; break;
case ONE:
__ Poke(constructor, 1 * kPointerSize);
__ Mov(x0, Operand(4));
break;
} }
__ JumpToExternalReference( __ Push(new_target, allocation_site);
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate())); __ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
} }

View File

@ -5019,27 +5019,26 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
// Subclassing. // Subclassing.
__ bind(&subclassing); __ bind(&subclassing);
__ pop(ecx); // return address.
__ push(edi);
__ push(edx);
// Adjust argc.
switch (argument_count()) { switch (argument_count()) {
case ANY: case ANY:
case MORE_THAN_ONE: case MORE_THAN_ONE:
__ add(eax, Immediate(2)); __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi);
__ add(eax, Immediate(3));
break; break;
case NONE: case NONE:
__ mov(eax, Immediate(2)); __ mov(Operand(esp, 1 * kPointerSize), edi);
break;
case ONE:
__ mov(eax, Immediate(3)); __ mov(eax, Immediate(3));
break; break;
case ONE:
__ mov(Operand(esp, 2 * kPointerSize), edi);
__ mov(eax, Immediate(4));
break;
} }
__ PopReturnAddressTo(ecx);
__ push(ecx); __ Push(edx);
__ JumpToExternalReference( __ Push(ebx);
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate())); __ PushReturnAddressFrom(ecx);
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
} }

View File

@ -5030,26 +5030,26 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
// Subclassing. // Subclassing.
__ bind(&subclassing); __ bind(&subclassing);
__ Push(a1);
__ Push(a3);
// Adjust argc.
switch (argument_count()) { switch (argument_count()) {
case ANY: case ANY:
case MORE_THAN_ONE: 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); __ addu(a0, a0, at);
break; break;
case NONE: case NONE:
__ li(a0, Operand(2)); __ sw(a1, MemOperand(sp, 0 * kPointerSize));
break;
case ONE:
__ li(a0, Operand(3)); __ li(a0, Operand(3));
break; break;
case ONE:
__ sw(a1, MemOperand(sp, 1 * kPointerSize));
__ li(a0, Operand(4));
break;
} }
__ Push(a3, a2);
__ JumpToExternalReference( __ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
} }

View File

@ -5055,26 +5055,26 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
// Subclassing. // Subclassing.
__ bind(&subclassing); __ bind(&subclassing);
__ Push(a1);
__ Push(a3);
// Adjust argc.
switch (argument_count()) { switch (argument_count()) {
case ANY: case ANY:
case MORE_THAN_ONE: case MORE_THAN_ONE:
__ li(at, Operand(2)); __ dsll(at, a0, kPointerSizeLog2);
__ addu(a0, a0, at); __ Daddu(at, sp, at);
__ sd(a1, MemOperand(at));
__ li(at, Operand(3));
__ Daddu(a0, a0, at);
break; break;
case NONE: case NONE:
__ li(a0, Operand(2)); __ sd(a1, MemOperand(sp, 0 * kPointerSize));
break;
case ONE:
__ li(a0, Operand(3)); __ li(a0, Operand(3));
break; break;
case ONE:
__ sd(a1, MemOperand(sp, 1 * kPointerSize));
__ li(a0, Operand(4));
break;
} }
__ Push(a3, a2);
__ JumpToExternalReference( __ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()));
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()));
} }

View File

@ -233,15 +233,24 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
} }
static Object* ArrayConstructorCommon(Isolate* isolate, namespace {
Handle<JSFunction> constructor,
Handle<JSFunction> new_target, Object* ArrayConstructorCommon(Isolate* isolate, Handle<JSFunction> constructor,
Handle<JSReceiver> new_target,
Handle<AllocationSite> site, Handle<AllocationSite> site,
Arguments* caller_args) { Arguments* caller_args) {
Factory* factory = isolate->factory(); 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 holey = false;
bool can_use_type_feedback = true; bool can_use_type_feedback = !site.is_null();
bool can_inline_array_constructor = true; bool can_inline_array_constructor = true;
if (caller_args->length() == 1) { if (caller_args->length() == 1) {
Handle<Object> argument_one = caller_args->at<Object>(0); Handle<Object> argument_one = caller_args->at<Object>(0);
@ -263,19 +272,28 @@ static Object* ArrayConstructorCommon(Isolate* isolate,
} }
} }
Handle<JSArray> array; // TODO(verwaest): new_target could be a proxy. Read new.target.prototype in
if (!site.is_null() && can_use_type_feedback) { // that case.
ElementsKind to_kind = site->GetElementsKind(); Handle<JSFunction> original_function = Handle<JSFunction>::cast(new_target);
JSFunction::EnsureHasInitialMap(constructor);
// 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);
ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind()
: initial_map->elements_kind();
if (holey && !IsFastHoleyElementsKind(to_kind)) { if (holey && !IsFastHoleyElementsKind(to_kind)) {
to_kind = GetHoleyElementsKind(to_kind); to_kind = GetHoleyElementsKind(to_kind);
// Update the allocation site info to reflect the advice alteration. // Update the allocation site info to reflect the advice alteration.
site->SetElementsKind(to_kind); if (!site.is_null()) site->SetElementsKind(to_kind);
} }
// We should allocate with an initial map that reflects the allocation site // We should allocate with an initial map that reflects the allocation site
// advice. Therefore we use AllocateJSObjectFromMap instead of passing // advice. Therefore we use AllocateJSObjectFromMap instead of passing
// the constructor. // the constructor.
Handle<Map> initial_map(constructor->initial_map(), isolate);
if (to_kind != initial_map->elements_kind()) { if (to_kind != initial_map->elements_kind()) {
initial_map = Map::AsElementsKind(initial_map, to_kind); initial_map = Map::AsElementsKind(initial_map, to_kind);
} }
@ -287,18 +305,8 @@ static Object* ArrayConstructorCommon(Isolate* isolate,
allocation_site = site; allocation_site = site;
} }
array = Handle<JSArray>::cast( Handle<JSArray> array = Handle<JSArray>::cast(
factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site)); 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);
}
}
factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS); factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
@ -314,21 +322,28 @@ static Object* ArrayConstructorCommon(Isolate* isolate,
site->SetDoNotInlineCall(); 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; 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) { RUNTIME_FUNCTION(Runtime_ArrayConstructor) {
HandleScope scope(isolate); 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) { RUNTIME_FUNCTION(Runtime_InternalArrayConstructor) {
HandleScope scope(isolate); HandleScope scope(isolate);
Arguments empty_args(0, NULL); Arguments empty_args(0, NULL);

View File

@ -40,7 +40,7 @@ namespace internal {
F(EstimateNumberOfElements, 1, 1) \ F(EstimateNumberOfElements, 1, 1) \
F(GetArrayKeys, 2, 1) \ F(GetArrayKeys, 2, 1) \
F(ArrayConstructor, -1, 1) \ F(ArrayConstructor, -1, 1) \
F(ArrayConstructorWithSubclassing, -1, 1) \ F(NewArray, -1 /* >= 3 */, 1) \
F(InternalArrayConstructor, -1, 1) \ F(InternalArrayConstructor, -1, 1) \
F(NormalizeElements, 1, 1) \ F(NormalizeElements, 1, 1) \
F(GrowArrayElements, 2, 1) \ F(GrowArrayElements, 2, 1) \

View File

@ -4734,27 +4734,32 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
// Subclassing // Subclassing
__ bind(&subclassing); __ bind(&subclassing);
__ Pop(rcx); // return address.
__ Push(rdi);
__ Push(rdx);
// Adjust argc.
switch (argument_count()) { switch (argument_count()) {
case ANY: case ANY:
case MORE_THAN_ONE: case MORE_THAN_ONE: {
__ addp(rax, Immediate(2)); StackArgumentsAccessor args(rsp, rax);
break; __ movp(args.GetReceiverOperand(), rdi);
case NONE: __ addp(rax, Immediate(3));
__ movp(rax, Immediate(2));
break;
case ONE:
__ movp(rax, Immediate(3));
break; break;
} }
case NONE: {
__ Push(rcx); StackArgumentsAccessor args(rsp, 0);
__ JumpToExternalReference( __ movp(args.GetReceiverOperand(), rdi);
ExternalReference(Runtime::kArrayConstructorWithSubclassing, isolate()), __ Set(rax, 3);
break;
}
case ONE: {
StackArgumentsAccessor args(rsp, 1);
__ movp(args.GetReceiverOperand(), rdi);
__ Set(rax, 4);
break;
}
}
__ PopReturnAddressTo(rcx);
__ Push(rdx);
__ Push(rbx);
__ PushReturnAddressFrom(rcx);
__ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate()),
1); 1);
} }