[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/565721 Reviewed-by: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Cr-Commit-Position: refs/heads/master@{#46538}
This commit is contained in:
parent
be8983da7b
commit
1edb46cc04
@ -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);
|
||||
|
@ -731,11 +731,15 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall(
|
||||
Node* target = NodeProperties::GetValueInput(node, 0);
|
||||
Node* new_target = NodeProperties::GetValueInput(node, 1);
|
||||
Type* new_target_type = NodeProperties::GetType(new_target);
|
||||
Node* type_info = site.is_null() ? jsgraph()->UndefinedConstant()
|
||||
: jsgraph()->HeapConstant(site);
|
||||
|
||||
ElementsKind elements_kind = site->GetElementsKind();
|
||||
ElementsKind elements_kind =
|
||||
site.is_null() ? GetInitialFastElementsKind() : site->GetElementsKind();
|
||||
AllocationSiteOverrideMode override_mode =
|
||||
AllocationSite::ShouldTrack(elements_kind) ? DISABLE_ALLOCATION_SITES
|
||||
: DONT_OVERRIDE;
|
||||
(site.is_null() || AllocationSite::ShouldTrack(elements_kind))
|
||||
? DISABLE_ALLOCATION_SITES
|
||||
: DONT_OVERRIDE;
|
||||
|
||||
// The Array constructor can only trigger an observable side-effect
|
||||
// if the new.target may be a proxy.
|
||||
@ -748,137 +752,37 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall(
|
||||
ArrayNoArgumentConstructorStub stub(isolate(), elements_kind,
|
||||
override_mode);
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1,
|
||||
CallDescriptor::kNeedsFrameState, properties);
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(),
|
||||
arity + 1, CallDescriptor::kNeedsFrameState, properties);
|
||||
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
|
||||
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(0));
|
||||
node->InsertInput(graph()->zone(), 2, type_info);
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
|
||||
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
return Changed(node);
|
||||
} else if (arity == 1) {
|
||||
AllocationSiteOverrideMode override_mode =
|
||||
AllocationSite::ShouldTrack(elements_kind) ? DISABLE_ALLOCATION_SITES
|
||||
: DONT_OVERRIDE;
|
||||
|
||||
if (IsHoleyOrDictionaryElementsKind(elements_kind)) {
|
||||
ArraySingleArgumentConstructorStub stub(isolate(), elements_kind,
|
||||
override_mode);
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
|
||||
CallDescriptor::kNeedsFrameState, properties);
|
||||
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
|
||||
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(1));
|
||||
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
Node* length = NodeProperties::GetValueInput(node, 2);
|
||||
Node* equal = graph()->NewNode(simplified()->ReferenceEqual(), length,
|
||||
jsgraph()->ZeroConstant());
|
||||
|
||||
Node* branch =
|
||||
graph()->NewNode(common()->Branch(BranchHint::kFalse), equal, control);
|
||||
Node* call_holey;
|
||||
Node* call_packed;
|
||||
Node* success_holey;
|
||||
Node* success_packed;
|
||||
Node* context = NodeProperties::GetContextInput(node);
|
||||
Node* frame_state = NodeProperties::GetFrameStateInput(node);
|
||||
Node* if_equal = graph()->NewNode(common()->IfTrue(), branch);
|
||||
{
|
||||
ArraySingleArgumentConstructorStub stub(isolate(), elements_kind,
|
||||
override_mode);
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
|
||||
CallDescriptor::kNeedsFrameState, properties);
|
||||
|
||||
Node* inputs[] = {jsgraph()->HeapConstant(stub.GetCode()),
|
||||
node->InputAt(1),
|
||||
jsgraph()->HeapConstant(site),
|
||||
jsgraph()->Constant(1),
|
||||
jsgraph()->UndefinedConstant(),
|
||||
length,
|
||||
context,
|
||||
frame_state,
|
||||
effect,
|
||||
if_equal};
|
||||
|
||||
success_holey = call_holey =
|
||||
graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
|
||||
}
|
||||
Node* if_not_equal = graph()->NewNode(common()->IfFalse(), branch);
|
||||
{
|
||||
// Require elements kind to "go holey."
|
||||
ArraySingleArgumentConstructorStub stub(
|
||||
isolate(), GetHoleyElementsKind(elements_kind), override_mode);
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
|
||||
CallDescriptor::kNeedsFrameState, properties);
|
||||
|
||||
Node* inputs[] = {jsgraph()->HeapConstant(stub.GetCode()),
|
||||
node->InputAt(1),
|
||||
jsgraph()->HeapConstant(site),
|
||||
jsgraph()->Constant(1),
|
||||
jsgraph()->UndefinedConstant(),
|
||||
length,
|
||||
context,
|
||||
frame_state,
|
||||
effect,
|
||||
if_not_equal};
|
||||
|
||||
success_packed = call_packed =
|
||||
graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
|
||||
}
|
||||
|
||||
// Update potential {IfException} uses of {node} to point to the above two
|
||||
// stub call nodes instead, by introducing a merge of two exception cases.
|
||||
Node* on_exception = nullptr;
|
||||
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
|
||||
Node* exception_holey =
|
||||
graph()->NewNode(common()->IfException(), call_holey, call_holey);
|
||||
Node* exception_packed =
|
||||
graph()->NewNode(common()->IfException(), call_packed, call_packed);
|
||||
Node* exception_merge = graph()->NewNode(
|
||||
common()->Merge(2), exception_holey, exception_packed);
|
||||
Node* exception_effect =
|
||||
graph()->NewNode(common()->EffectPhi(2), exception_holey,
|
||||
exception_packed, exception_merge);
|
||||
Node* exception_value =
|
||||
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
||||
exception_holey, exception_packed, exception_merge);
|
||||
ReplaceWithValue(on_exception, exception_value, exception_effect,
|
||||
exception_merge);
|
||||
success_holey = graph()->NewNode(common()->IfSuccess(), call_holey);
|
||||
success_packed = graph()->NewNode(common()->IfSuccess(), call_packed);
|
||||
}
|
||||
|
||||
Node* merge =
|
||||
graph()->NewNode(common()->Merge(2), success_holey, success_packed);
|
||||
Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), call_holey,
|
||||
call_packed, merge);
|
||||
Node* phi =
|
||||
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
||||
call_holey, call_packed, merge);
|
||||
|
||||
ReplaceWithValue(node, phi, effect_phi, merge);
|
||||
return Changed(node);
|
||||
// Require elements kind to "go holey".
|
||||
ArraySingleArgumentConstructorStub stub(
|
||||
isolate(), GetHoleyElementsKind(elements_kind), override_mode);
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(),
|
||||
arity + 1, CallDescriptor::kNeedsFrameState, properties);
|
||||
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
|
||||
node->InsertInput(graph()->zone(), 2, type_info);
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
|
||||
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
} else {
|
||||
DCHECK_GT(arity, 1);
|
||||
ArrayNArgumentsConstructorStub stub(isolate());
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(),
|
||||
arity + 1, CallDescriptor::kNeedsFrameState);
|
||||
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
|
||||
node->InsertInput(graph()->zone(), 2, type_info);
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
|
||||
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
}
|
||||
|
||||
DCHECK(arity > 1);
|
||||
ArrayNArgumentsConstructorStub stub(isolate());
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), arity + 1,
|
||||
CallDescriptor::kNeedsFrameState);
|
||||
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
|
||||
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
|
||||
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
@ -893,38 +797,38 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
|
||||
|
||||
// Check if we have a feedback {site} on the {node}.
|
||||
Handle<AllocationSite> site = p.site();
|
||||
if (p.site().is_null()) return NoChange();
|
||||
|
||||
// Attempt to inline calls to the Array constructor for the relevant cases
|
||||
// where either no arguments are provided, or exactly one unsigned number
|
||||
// argument is given.
|
||||
if (site->CanInlineCall()) {
|
||||
if (p.arity() == 0) {
|
||||
Node* length = jsgraph()->ZeroConstant();
|
||||
int capacity = JSArray::kPreallocatedArrayElements;
|
||||
return ReduceNewArray(node, length, capacity, site);
|
||||
} else if (p.arity() == 1) {
|
||||
Node* length = NodeProperties::GetValueInput(node, 2);
|
||||
Type* length_type = NodeProperties::GetType(length);
|
||||
if (!length_type->Maybe(Type::Number())) {
|
||||
// Handle the single argument case, where we know that the value
|
||||
// cannot be a valid Array length.
|
||||
return ReduceNewArray(node, {length}, site);
|
||||
}
|
||||
if (length_type->Is(Type::SignedSmall()) && length_type->Min() >= 0 &&
|
||||
length_type->Max() <= kElementLoopUnrollLimit &&
|
||||
length_type->Min() == length_type->Max()) {
|
||||
int capacity = static_cast<int>(length_type->Max());
|
||||
if (!site.is_null()) {
|
||||
// Attempt to inline calls to the Array constructor for the relevant cases
|
||||
// where either no arguments are provided, or exactly one unsigned number
|
||||
// argument is given.
|
||||
if (site->CanInlineCall()) {
|
||||
if (p.arity() == 0) {
|
||||
Node* length = jsgraph()->ZeroConstant();
|
||||
int capacity = JSArray::kPreallocatedArrayElements;
|
||||
return ReduceNewArray(node, length, capacity, site);
|
||||
} else if (p.arity() == 1) {
|
||||
Node* length = NodeProperties::GetValueInput(node, 2);
|
||||
Type* length_type = NodeProperties::GetType(length);
|
||||
if (!length_type->Maybe(Type::Number())) {
|
||||
// Handle the single argument case, where we know that the value
|
||||
// cannot be a valid Array length.
|
||||
return ReduceNewArray(node, {length}, site);
|
||||
}
|
||||
if (length_type->Is(Type::SignedSmall()) && length_type->Min() >= 0 &&
|
||||
length_type->Max() <= kElementLoopUnrollLimit &&
|
||||
length_type->Min() == length_type->Max()) {
|
||||
int capacity = static_cast<int>(length_type->Max());
|
||||
return ReduceNewArray(node, length, capacity, site);
|
||||
}
|
||||
} else if (p.arity() <= JSArray::kInitialMaxFastElementArray) {
|
||||
std::vector<Node*> values;
|
||||
values.reserve(p.arity());
|
||||
for (size_t i = 0; i < p.arity(); ++i) {
|
||||
values.push_back(
|
||||
NodeProperties::GetValueInput(node, static_cast<int>(2 + i)));
|
||||
}
|
||||
return ReduceNewArray(node, values, site);
|
||||
}
|
||||
} else if (p.arity() <= JSArray::kInitialMaxFastElementArray) {
|
||||
std::vector<Node*> values;
|
||||
values.reserve(p.arity());
|
||||
for (size_t i = 0; i < p.arity(); ++i) {
|
||||
values.push_back(
|
||||
NodeProperties::GetValueInput(node, static_cast<int>(2 + i)));
|
||||
}
|
||||
return ReduceNewArray(node, values, site);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user