Commit 1edb46cc authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Widen the fast-path for JSCreateArray.

This improves the general Array constructor call performance (w/o
usable AllocationSite feedback) in TurboFan by ~2x, i.e. for example
invoking the Array constructor like this

  var a = Array.call(undefined, n);

instead of

  var a = Array(n);

such that the CallIC doesn't know that it's eventually calling the
Array constructor.

It also thus changes the single argument Array constructor to always
return holey arrays. Previously the single argument case for the Array
constructor was somehow trying to dynamically detect 0 and in that case
returned a packed array instead of a holey one. That adds quite a lot
of churn, and doesn't seem to be very useful, especially since this
might lead to unnecessary feedback pollution later.

R=mvstanton@chromium.org

Bug: v8:2229, v8:5269, v8:6399
Change-Id: I3d7cb9bd975ec0e491e3cdbcf1230185cfd1e3de
Reviewed-on: https://chromium-review.googlesource.com/565721Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46538}
parent be8983da
......@@ -2316,24 +2316,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
// r0 - number of arguments
// r1 - constructor?
// sp[0] - last argument
Label normal_sequence;
if (mode == DONT_OVERRIDE) {
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
// is the low bit set? If so, we are holey and that is good.
__ tst(r3, Operand(1));
__ b(ne, &normal_sequence);
}
// look at the first argument
__ ldr(r5, MemOperand(sp, 0));
__ cmp(r5, Operand::Zero());
__ b(eq, &normal_sequence);
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
if (mode == DISABLE_ALLOCATION_SITES) {
ElementsKind initial = GetInitialFastElementsKind();
......@@ -2343,13 +2331,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
holey_initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub_holey);
__ bind(&normal_sequence);
ArraySingleArgumentConstructorStub stub(masm->isolate(),
initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub);
} else if (mode == DONT_OVERRIDE) {
// is the low bit set? If so, we are holey and that is good.
Label normal_sequence;
__ tst(r3, Operand(1));
__ b(ne, &normal_sequence);
// We are going to create a holey array, but our kind is non-holey.
// Fix kind and retry (only if we have an allocation site in the slot).
__ add(r3, r3, Operand(1));
......
......@@ -2486,23 +2486,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
Register allocation_site = x2;
Register kind = x3;
Label normal_sequence;
if (mode == DONT_OVERRIDE) {
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
// Is the low bit set? If so, the array is holey.
__ Tbnz(kind, 0, &normal_sequence);
}
// Look at the last argument.
// TODO(jbramley): What does a 0 argument represent?
__ Peek(x10, 0);
__ Cbz(x10, &normal_sequence);
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
if (mode == DISABLE_ALLOCATION_SITES) {
ElementsKind initial = GetInitialFastElementsKind();
......@@ -2512,13 +2501,11 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
holey_initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub_holey);
__ Bind(&normal_sequence);
ArraySingleArgumentConstructorStub stub(masm->isolate(),
initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub);
} else if (mode == DONT_OVERRIDE) {
// Is the low bit set? If so, the array is holey.
Label normal_sequence;
__ Tbnz(kind, 0, &normal_sequence);
// We are going to create a holey array, but our kind is non-holey.
// Fix kind and retry (only if we have an allocation site in the slot).
__ Orr(kind, kind, 1);
......
This diff is collapsed.
......@@ -393,13 +393,21 @@ void JSGenericLowering::LowerJSCreateArray(Node* node) {
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
Handle<AllocationSite> const site = p.site();
Node* new_target = node->InputAt(1);
ArrayConstructorDescriptor descriptor(isolate());
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), zone(), descriptor, arity + 1,
CallDescriptor::kNeedsFrameState, node->op()->properties(),
MachineType::AnyTagged());
Node* stub_code = jsgraph()->ArrayConstructorStubConstant();
Node* stub_arity = jsgraph()->Int32Constant(arity);
Node* type_info = site.is_null() ? jsgraph()->UndefinedConstant()
: jsgraph()->HeapConstant(site);
node->RemoveInput(1);
node->InsertInput(zone(), 1 + arity, new_target);
node->InsertInput(zone(), 2 + arity, type_info);
ReplaceWithRuntimeCall(node, Runtime::kNewArray, arity + 3);
Node* receiver = jsgraph()->UndefinedConstant();
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, type_info);
node->InsertInput(zone(), 5, receiver);
NodeProperties::ChangeOp(node, common()->Call(desc));
}
......
......@@ -26,6 +26,11 @@ Node* JSGraph::AllocateInOldSpaceStubConstant() {
HeapConstant(isolate()->builtins()->AllocateInOldSpace()));
}
Node* JSGraph::ArrayConstructorStubConstant() {
return CACHED(kArrayConstructorStubConstant,
HeapConstant(ArrayConstructorStub(isolate()).GetCode()));
}
Node* JSGraph::ToNumberBuiltinConstant() {
return CACHED(kToNumberBuiltinConstant,
HeapConstant(isolate()->builtins()->ToNumber()));
......
......@@ -43,6 +43,7 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
// Canonicalized global constants.
Node* AllocateInNewSpaceStubConstant();
Node* AllocateInOldSpaceStubConstant();
Node* ArrayConstructorStubConstant();
Node* ToNumberBuiltinConstant();
Node* CEntryStubConstant(int result_size,
SaveFPRegsMode save_doubles = kDontSaveFPRegs,
......@@ -165,6 +166,7 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
enum CachedNode {
kAllocateInNewSpaceStubConstant,
kAllocateInOldSpaceStubConstant,
kArrayConstructorStubConstant,
kToNumberBuiltinConstant,
kCEntryStub1Constant,
kCEntryStub2Constant,
......
......@@ -1145,6 +1145,10 @@ struct ConcurrentOptimizationPrepPhase {
data->jsgraph()->CEntryStubConstant(2);
data->jsgraph()->CEntryStubConstant(3);
// TODO(turbofan): Remove this line once the Array constructor code
// is a proper builtin and no longer a CodeStub.
data->jsgraph()->ArrayConstructorStubConstant();
// This is needed for escape analysis.
NodeProperties::SetType(data->jsgraph()->FalseConstant(), Type::Boolean());
NodeProperties::SetType(data->jsgraph()->TrueConstant(), Type::Boolean());
......
......@@ -2223,24 +2223,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
// edi - constructor?
// esp[0] - return address
// esp[4] - last argument
Label normal_sequence;
if (mode == DONT_OVERRIDE) {
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
// is the low bit set? If so, we are holey and that is good.
__ test_b(edx, Immediate(1));
__ j(not_zero, &normal_sequence);
}
// look at the first argument
__ mov(ecx, Operand(esp, kPointerSize));
__ test(ecx, ecx);
__ j(zero, &normal_sequence);
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
if (mode == DISABLE_ALLOCATION_SITES) {
ElementsKind initial = GetInitialFastElementsKind();
......@@ -2250,13 +2238,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
holey_initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub_holey);
__ bind(&normal_sequence);
ArraySingleArgumentConstructorStub stub(masm->isolate(),
initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub);
} else if (mode == DONT_OVERRIDE) {
// is the low bit set? If so, we are holey and that is good.
Label normal_sequence;
__ test_b(edx, Immediate(1));
__ j(not_zero, &normal_sequence);
// We are going to create a holey array, but our kind is non-holey.
// Fix kind and retry.
__ inc(edx);
......
......@@ -2482,8 +2482,6 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
// a0 - number of arguments
// a1 - constructor?
// sp[0] - last argument
Label normal_sequence;
if (mode == DONT_OVERRIDE) {
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
......@@ -2491,15 +2489,6 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
// is the low bit set? If so, we are holey and that is good.
__ And(at, a3, Operand(1));
__ Branch(&normal_sequence, ne, at, Operand(zero_reg));
}
// look at the first argument
__ lw(t1, MemOperand(sp, 0));
__ Branch(&normal_sequence, eq, t1, Operand(zero_reg));
if (mode == DISABLE_ALLOCATION_SITES) {
ElementsKind initial = GetInitialFastElementsKind();
ElementsKind holey_initial = GetHoleyElementsKind(initial);
......@@ -2508,13 +2497,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
holey_initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub_holey);
__ bind(&normal_sequence);
ArraySingleArgumentConstructorStub stub(masm->isolate(),
initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub);
} else if (mode == DONT_OVERRIDE) {
// is the low bit set? If so, we are holey and that is good.
Label normal_sequence;
__ And(at, a3, Operand(1));
__ Branch(&normal_sequence, ne, at, Operand(zero_reg));
// We are going to create a holey array, but our kind is non-holey.
// Fix kind and retry (only if we have an allocation site in the slot).
__ Addu(a3, a3, Operand(1));
......
......@@ -2486,8 +2486,6 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
// a0 - number of arguments
// a1 - constructor?
// sp[0] - last argument
Label normal_sequence;
if (mode == DONT_OVERRIDE) {
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
......@@ -2495,14 +2493,6 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
// is the low bit set? If so, we are holey and that is good.
__ And(at, a3, Operand(1));
__ Branch(&normal_sequence, ne, at, Operand(zero_reg));
}
// look at the first argument
__ Ld(a5, MemOperand(sp, 0));
__ Branch(&normal_sequence, eq, a5, Operand(zero_reg));
if (mode == DISABLE_ALLOCATION_SITES) {
ElementsKind initial = GetInitialFastElementsKind();
ElementsKind holey_initial = GetHoleyElementsKind(initial);
......@@ -2511,13 +2501,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
holey_initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub_holey);
__ bind(&normal_sequence);
ArraySingleArgumentConstructorStub stub(masm->isolate(),
initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub);
} else if (mode == DONT_OVERRIDE) {
// is the low bit set? If so, we are holey and that is good.
Label normal_sequence;
__ And(at, a3, Operand(1));
__ Branch(&normal_sequence, ne, at, Operand(zero_reg));
// We are going to create a holey array, but our kind is non-holey.
// Fix kind and retry (only if we have an allocation site in the slot).
__ Daddu(a3, a3, Operand(1));
......
......@@ -2181,25 +2181,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
// rsp[0] - return address
// rsp[8] - last argument
Label normal_sequence;
if (mode == DONT_OVERRIDE) {
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
// is the low bit set? If so, we are holey and that is good.
__ testb(rdx, Immediate(1));
__ j(not_zero, &normal_sequence);
}
// look at the first argument
StackArgumentsAccessor args(rsp, 1, ARGUMENTS_DONT_CONTAIN_RECEIVER);
__ movp(rcx, args.GetArgumentOperand(0));
__ testp(rcx, rcx);
__ j(zero, &normal_sequence);
STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(PACKED_ELEMENTS == 2);
STATIC_ASSERT(HOLEY_ELEMENTS == 3);
STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
if (mode == DISABLE_ALLOCATION_SITES) {
ElementsKind initial = GetInitialFastElementsKind();
......@@ -2209,13 +2196,12 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
holey_initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub_holey);
__ bind(&normal_sequence);
ArraySingleArgumentConstructorStub stub(masm->isolate(),
initial,
DISABLE_ALLOCATION_SITES);
__ TailCallStub(&stub);
} else if (mode == DONT_OVERRIDE) {
// is the low bit set? If so, we are holey and that is good.
Label normal_sequence;
__ testb(rdx, Immediate(1));
__ j(not_zero, &normal_sequence);
// We are going to create a holey array, but our kind is non-holey.
// Fix kind and retry (only if we have an allocation site in the slot).
__ incl(rdx);
......
......@@ -81,7 +81,7 @@ assertNotHoley(obj);
assertKind(elements_kind.fast_smi_only, obj);
obj = new Array(0);
assertNotHoley(obj);
assertHoley(obj);
assertKind(elements_kind.fast_smi_only, obj);
obj = new Array(2);
......
......@@ -180,7 +180,7 @@ function assertKind(expected, obj, name_opt) {
%OptimizeFunctionOnNextCall(bar);
a = bar(0);
assertOptimized(bar);
assertFalse(isHoley(a));
assertTrue(isHoley(a));
a = bar(1); // ouch!
assertOptimized(bar);
assertTrue(isHoley(a));
......@@ -188,9 +188,7 @@ function assertKind(expected, obj, name_opt) {
assertTrue(isHoley(a));
a = bar(0);
assertOptimized(bar);
// Crankshafted functions don't use mementos, so feedback still
// indicates a packed array is desired.
assertFalse(isHoley(a));
assertTrue(isHoley(a));
})();
// Test: Make sure that crankshaft continues with feedback for large arrays.
......
......@@ -84,7 +84,7 @@ function assertKind(expected, obj, name_opt) {
create1(0);
create1(0);
a = create1(0);
assertFalse(isHoley(a));
assertTrue(isHoley(a));
assertKind(elements_kind.fast_smi_only, a);
a[0] = "hello";
b = create1(10);
......
......@@ -36,16 +36,12 @@ function assertHoley(obj, name_opt) {
assertEquals(true, isHoley(obj), name_opt);
}
function assertNotHoley(obj, name_opt) {
assertEquals(false, isHoley(obj), name_opt);
}
function create_array(arg) {
return new Array(arg);
}
obj = create_array(0);
assertNotHoley(obj);
assertHoley(obj);
create_array(0);
%OptimizeFunctionOnNextCall(create_array);
obj = create_array(10);
......
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