Commit a80164ec authored by mvstanton's avatar mvstanton Committed by Commit bot

[TurboFan] Improve Array constructor optimization

And in so doing, enable a handful of excluded tests.

BUG=

Review-Url: https://codereview.chromium.org/2127713003
Cr-Commit-Position: refs/heads/master@{#37559}
parent d3413fa2
......@@ -509,12 +509,144 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
return Changed(node);
}
Reduction JSCreateLowering::ReduceNewArrayToStubCall(
Node* node, Handle<AllocationSite> site) {
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
ElementsKind elements_kind = site->GetElementsKind();
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
if (arity == 0) {
ArrayNoArgumentConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(0));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
} else if (arity == 1) {
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
if (IsHoleyElementsKind(elements_kind)) {
ArraySingleArgumentConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(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(Type::Any()),
length, jsgraph()->ZeroConstant());
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), equal, control);
Node* call_holey;
Node* call_packed;
Node* if_success_packed;
Node* if_success_holey;
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
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);
Node* inputs[] = {jsgraph()->HeapConstant(stub.GetCode()),
node->InputAt(1),
jsgraph()->HeapConstant(site),
jsgraph()->Int32Constant(1),
jsgraph()->UndefinedConstant(),
length,
context,
frame_state,
effect,
if_equal};
call_holey =
graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
if_success_holey = graph()->NewNode(common()->IfSuccess(), call_holey);
}
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);
Node* inputs[] = {jsgraph()->HeapConstant(stub.GetCode()),
node->InputAt(1),
jsgraph()->HeapConstant(site),
jsgraph()->Int32Constant(1),
jsgraph()->UndefinedConstant(),
length,
context,
frame_state,
effect,
if_not_equal};
call_packed =
graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
if_success_packed = graph()->NewNode(common()->IfSuccess(), call_packed);
}
Node* merge = graph()->NewNode(common()->Merge(2), if_success_holey,
if_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);
}
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()->Int32Constant(arity));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
}
Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
Node* target = NodeProperties::GetValueInput(node, 0);
Node* new_target = NodeProperties::GetValueInput(node, 1);
// TODO(mstarzinger): Array constructor can throw. Hook up exceptional edges.
if (NodeProperties::IsExceptionalCall(node)) return NoChange();
// TODO(bmeurer): Optimize the subclassing case.
if (target != new_target) return NoChange();
......@@ -533,16 +665,16 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
} else if (p.arity() == 1) {
Node* length = NodeProperties::GetValueInput(node, 2);
Type* length_type = NodeProperties::GetType(length);
if (length_type->Is(Type::SignedSmall()) &&
length_type->Min() >= 0 &&
length_type->Max() <= kElementLoopUnrollLimit) {
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);
}
}
}
return NoChange();
return ReduceNewArrayToStubCall(node, site);
}
Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
......
......@@ -72,6 +72,8 @@ class JSCreateLowering final : public AdvancedReducer {
PretenureFlag pretenure,
AllocationSiteUsageContext* site_context);
Reduction ReduceNewArrayToStubCall(Node* node, Handle<AllocationSite> site);
// Infers the LiteralsArray to use for a given {node}.
MaybeHandle<LiteralsArray> GetSpecializationLiterals(Node* node);
......
......@@ -393,67 +393,13 @@ 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();
// TODO(turbofan): We embed the AllocationSite from the Operator at this
// point, which we should not do once we want to both consume the feedback
// but at the same time shared the optimized code across native contexts,
// as the AllocationSite is associated with a single native context (it's
// stored in the type feedback vector after all). Once we go for cross
// context code generation, we should somehow find a way to get to the
// allocation site for the actual native context at runtime.
if (!site.is_null()) {
// Reduce {node} to the appropriate ArrayConstructorStub backend.
// Note that these stubs "behave" like JSFunctions, which means they
// expect a receiver on the stack, which they remove. We just push
// undefined for the receiver.
ElementsKind elements_kind = site->GetElementsKind();
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
if (arity == 0) {
ArrayNoArgumentConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(0));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
} else if (arity == 1) {
// TODO(bmeurer): Optimize for the 0 length non-holey case?
ArraySingleArgumentConstructorStub stub(
isolate(), GetHoleyElementsKind(elements_kind), override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(1));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
} else {
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()->Int32Constant(arity));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
}
} else {
Node* new_target = node->InputAt(1);
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* new_target = node->InputAt(1);
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);
}
......
......@@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --expose-gc
// Flags: --noalways-opt
// Test element kind of objects.
......@@ -61,26 +62,6 @@ function assertKind(expected, obj, name_opt) {
assertEquals(expected, getKind(obj), name_opt);
}
// Test: If a call site goes megamorphic, it retains the ability to
// use allocation site feedback (if FLAG_allocation_site_pretenuring
// is on).
(function() {
function bar(t, len) {
return new t(len);
}
a = bar(Array, 10);
a[0] = 3.5;
b = bar(Array, 1);
assertKind(elements_kind.fast_double, b);
c = bar(Object, 3);
b = bar(Array, 10);
// TODO(mvstanton): re-enable when FLAG_allocation_site_pretenuring
// is on in the build.
// assertKind(elements_kind.fast_double, b);
})();
// Test: ensure that crankshafted array constructor sites are deopted
// if another function is used.
(function() {
......
......@@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --expose-gc
// Flags: --noalways-opt
var elements_kind = {
fast_smi_only : 'fast smi only elements',
......
......@@ -59,11 +59,7 @@
# TurboFan compiler failures.
# TODO(verwaest): Some tests are over-restrictive about object layout.
'array-constructor-feedback': [PASS, NO_VARIANTS],
'array-feedback': [PASS, NO_VARIANTS],
'compare-known-objects-slow': [PASS, NO_VARIANTS],
'elements-kind': [PASS, NO_VARIANTS],
'opt-elements-kind': [PASS, NO_VARIANTS],
'smi-representation': [PASS, NO_VARIANTS],
# Some tests are just too slow to run for now.
......@@ -144,9 +140,6 @@
'regress-sync-optimized-lists': [PASS, NO_VARIANTS],
'regress/regress-store-uncacheable': [PASS, NO_VARIANTS],
# issue 4078:
'allocation-site-info': [PASS, NO_VARIANTS],
##############################################################################
# Too slow in debug mode with --stress-opt mode.
'compiler/regress-stacktrace-methods': [PASS, ['mode == debug', SKIP]],
......@@ -326,6 +319,9 @@
'smi-mul-const': [PASS, NO_IGNITION],
'smi-mul': [PASS, NO_IGNITION],
'unary-minus-deopt': [PASS, NO_IGNITION],
'array-constructor-feedback': [PASS, NO_IGNITION],
'array-feedback': [PASS, NO_IGNITION],
'allocation-site-info': [PASS, NO_IGNITION],
# TODO(rmcilroy,5038): Crashes in Deoptimizer::PatchCodeForDeoptimization on
# nosnap builds when --stress-opt and --turbo-from-bytecode is enabled.
......@@ -819,6 +815,9 @@
# TODO(mythria, 4780): Related to type feedback for calls in interpreter.
'array-literal-feedback': [FAIL],
'regress/regress-4121': [FAIL],
'array-constructor-feedback': [FAIL],
'array-feedback': [FAIL],
'allocation-site-info': [FAIL],
# TODO(mythria, 4764): lack of osr support.
'regress/regress-2618': [FAIL],
......
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