[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);
|
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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) \
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user