Commit 409d0180 authored by petermarshall's avatar petermarshall Committed by Commit bot

[turbofan] Reduce CallConstructWithSpread where iteration is not observable.

Where the arguments have already been inlined, we can replace these calls with a
direct call to construct. We have to make sure that the iteration over the arguments is not observable.

BUG=v8:5895

Review-Url: https://codereview.chromium.org/2659623002
Cr-Commit-Position: refs/heads/master@{#42765}
parent ccf428bb
......@@ -6,6 +6,7 @@
#include "src/code-factory.h"
#include "src/code-stubs.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
......@@ -21,6 +22,8 @@ Reduction JSCallReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSCallConstruct:
return ReduceJSCallConstruct(node);
case IrOpcode::kJSCallConstructWithSpread:
return ReduceJSCallConstructWithSpread(node);
case IrOpcode::kJSCallFunction:
return ReduceJSCallFunction(node);
default:
......@@ -691,10 +694,85 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
return NoChange();
}
Reduction JSCallReducer::ReduceJSCallConstructWithSpread(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallConstructWithSpread, node->opcode());
CallConstructWithSpreadParameters const& p =
CallConstructWithSpreadParametersOf(node->op());
DCHECK_LE(3u, p.arity());
int arity = static_cast<int>(p.arity() - 2);
// Do check to make sure we can actually avoid iteration.
if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) {
return NoChange();
}
Node* spread = NodeProperties::GetValueInput(node, arity);
// Check if spread is an arguments object, and {node} is the only value user
// of spread (except for value uses in frame states).
if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
for (Edge edge : spread->use_edges()) {
if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
if (!NodeProperties::IsValueEdge(edge)) continue;
if (edge.from() == node) continue;
return NoChange();
}
// Get to the actual frame state from which to extract the arguments;
// we can only optimize this in case the {node} was already inlined into
// some other function (and same for the {spread}).
CreateArgumentsType type = CreateArgumentsTypeOf(spread->op());
Node* frame_state = NodeProperties::GetFrameStateInput(spread);
Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
// Need to take the parameters from the arguments adaptor.
frame_state = outer_state;
}
FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
int start_index = 0;
if (type == CreateArgumentsType::kMappedArguments) {
// Mapped arguments (sloppy mode) cannot be handled if they are aliased.
Handle<SharedFunctionInfo> shared;
if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
if (shared->internal_formal_parameter_count() != 0) return NoChange();
} else if (type == CreateArgumentsType::kRestParameter) {
Handle<SharedFunctionInfo> shared;
if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
start_index = shared->internal_formal_parameter_count();
// Only check the array iterator protector when we have a rest object.
if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
// Add a code dependency on the array iterator protector.
dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
}
dependencies()->AssumeMapStable(
isolate()->initial_array_iterator_prototype_map());
// Remove the spread input from the {node}.
node->RemoveInput(arity--);
// Add the actual parameters to the {node}, skipping the receiver.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
for (int i = start_index + 1; i < state_info.parameter_count(); ++i) {
node->InsertInput(graph()->zone(), static_cast<int>(++arity),
parameters->InputAt(i));
}
NodeProperties::ChangeOp(
node, javascript()->CallConstruct(arity + 2, 7, VectorSlotPair()));
return Changed(node);
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
Factory* JSCallReducer::factory() const { return isolate()->factory(); }
CommonOperatorBuilder* JSCallReducer::common() const {
return jsgraph()->common();
}
......
......@@ -10,6 +10,11 @@
namespace v8 {
namespace internal {
// Forward declarations.
class CompilationDependencies;
class Factory;
namespace compiler {
// Forward declarations.
......@@ -30,11 +35,13 @@ class JSCallReducer final : public AdvancedReducer {
typedef base::Flags<Flag> Flags;
JSCallReducer(Editor* editor, JSGraph* jsgraph, Flags flags,
Handle<Context> native_context)
Handle<Context> native_context,
CompilationDependencies* dependencies)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
flags_(flags),
native_context_(native_context) {}
native_context_(native_context),
dependencies_(dependencies) {}
Reduction Reduce(Node* node) final;
......@@ -49,6 +56,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceFunctionPrototypeHasInstance(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceJSCallConstruct(Node* node);
Reduction ReduceJSCallConstructWithSpread(Node* node);
Reduction ReduceJSCallFunction(Node* node);
enum HolderLookup { kHolderNotFound, kHolderIsReceiver, kHolderFound };
......@@ -61,14 +69,17 @@ class JSCallReducer final : public AdvancedReducer {
Flags flags() const { return flags_; }
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const;
Factory* factory() const;
Handle<Context> native_context() const { return native_context_; }
CommonOperatorBuilder* common() const;
JSOperatorBuilder* javascript() const;
SimplifiedOperatorBuilder* simplified() const;
CompilationDependencies* dependencies() const { return dependencies_; }
JSGraph* const jsgraph_;
Flags const flags_;
Handle<Context> const native_context_;
CompilationDependencies* const dependencies_;
};
DEFINE_OPERATORS_FOR_FLAGS(JSCallReducer::Flags)
......
......@@ -781,7 +781,8 @@ struct InliningPhase {
call_reducer_flags |= JSCallReducer::kDeoptimizationEnabled;
}
JSCallReducer call_reducer(&graph_reducer, data->jsgraph(),
call_reducer_flags, data->native_context());
call_reducer_flags, data->native_context(),
data->info()->dependencies());
JSContextSpecialization context_specialization(
&graph_reducer, data->jsgraph(),
data->info()->is_function_context_specializing()
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
(function modifyArrayIterator() {
'use strict';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class RestPoint extends Point {
constructor(...args) {
super(...args);
}
}
function testRestPoint(x, y) {
return new RestPoint(x, y);
}
testRestPoint(1, 2);
testRestPoint(1, 2);
% OptimizeFunctionOnNextCall(testRestPoint);
var r = testRestPoint(1, 2);
assertInstanceof(r, RestPoint);
assertInstanceof(r, Point);
assertEquals(1, r.x);
assertEquals(2, r.y);
Object.defineProperty(Array.prototype, Symbol.iterator, {
value: function*
() {
yield 3;
yield 4;
},
configurable: true
});
var r2 = testRestPoint(1, 2);
assertInstanceof(r2, RestPoint);
assertInstanceof(r2, Point);
assertEquals(3, r2.x);
assertEquals(4, r2.y);
})();
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
(function modifyNext() {
'use strict';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ArgumentsPoint extends Point {
constructor() {
super(...arguments);
}
}
var a = [];
var ai = a[Symbol.iterator]();
var original_next = ai.__proto__['next'];
function testArgumentsPoint(x, y) {
return new ArgumentsPoint(x, y);
}
testArgumentsPoint(1, 2);
testArgumentsPoint(1, 2);
% OptimizeFunctionOnNextCall(testArgumentsPoint);
var r = testArgumentsPoint(1, 2);
assertInstanceof(r, ArgumentsPoint);
assertInstanceof(r, Point);
assertEquals(r.x, 1);
assertEquals(r.y, 2);
var called = 0;
Object.defineProperty(ai.__proto__, 'next', {
get: function() {
called++;
return original_next;
}
});
var r2 = testArgumentsPoint(1, 2);
assertEquals(3, called);
assertInstanceof(r2, ArgumentsPoint);
assertInstanceof(r2, Point);
assertEquals(r2.x, 1);
assertEquals(r2.y, 2);
})();
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
(function() {
'use strict';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function testBaselineAndOpt(func) {
func(1, 2);
func(1, 2);
% OptimizeFunctionOnNextCall(func);
return func(1, 2);
}
class RestPoint extends Point {
constructor(...args) {
super(...args);
}
}
var r = testBaselineAndOpt(function(x, y) {
return new RestPoint(x, y);
});
assertInstanceof(r, RestPoint);
assertInstanceof(r, Point);
assertEquals(r.x, 1);
assertEquals(r.y, 2);
class RestExtraPoint extends Point {
constructor(...args) {
super(-1, 0, ...args);
}
}
r = testBaselineAndOpt(function(x, y) {
return new RestExtraPoint(x, y);
});
assertInstanceof(r, RestExtraPoint);
assertInstanceof(r, Point);
assertEquals(r.x, -1);
assertEquals(r.y, 0);
class ArgumentsPoint extends Point {
constructor() {
super(...arguments);
}
}
r = testBaselineAndOpt(function(x, y) {
return new ArgumentsPoint(x, y);
});
assertInstanceof(r, ArgumentsPoint);
assertInstanceof(r, Point);
assertEquals(r.x, 1);
assertEquals(r.y, 2);
class ArgumentsExtraPoint extends Point {
constructor() {
super(1, 2, ...arguments);
}
}
r = testBaselineAndOpt(function(x, y) {
return new ArgumentsExtraPoint(x, y);
});
assertInstanceof(r, ArgumentsExtraPoint);
assertInstanceof(r, Point);
assertEquals(r.x, 1);
assertEquals(r.y, 2);
class LiteralPoint extends Point {
constructor() {
super(...[3, 4]);
}
}
r = testBaselineAndOpt(function(x, y) {
return new LiteralPoint(x, y);
});
assertInstanceof(r, LiteralPoint);
assertInstanceof(r, Point);
assertEquals(r.x, 3);
assertEquals(r.y, 4);
})();
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