Commit 55c07a8b authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce JSCallReducer to strength reduce JSCallFunction nodes.

The JSCallReducer runs together with inlining and tries to strength
reduce JSCallFunction nodes; currently it can fold
Function.prototype.call and Function.prototype.apply (with arguments),
and make it possible to inline across them.

In the case of Function.prototype.apply with arguments we still have to
leave the JSCreateArguments node in the graph because there might be
other (frame state) uses. Once escape analysis is ready, it will take
care of removing these nodes and adding appropriate transitions for the
deoptimizer.

R=jarin@chromium.org
BUG=v8:4551
LOG=n

Review URL: https://codereview.chromium.org/1445513002

Cr-Commit-Position: refs/heads/master@{#31979}
parent 83a6ab85
......@@ -781,6 +781,8 @@ source_set("v8_base") {
"src/compiler/interpreter-assembler.h",
"src/compiler/js-builtin-reducer.cc",
"src/compiler/js-builtin-reducer.h",
"src/compiler/js-call-reducer.cc",
"src/compiler/js-call-reducer.h",
"src/compiler/js-context-relaxation.cc",
"src/compiler/js-context-relaxation.h",
"src/compiler/js-context-specialization.cc",
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/diamond.h"
#include "src/compiler/js-builtin-reducer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-matchers.h"
......@@ -91,41 +90,6 @@ JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
: AdvancedReducer(editor), jsgraph_(jsgraph) {}
// ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
Reduction JSBuiltinReducer::ReduceFunctionCall(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
Handle<JSFunction> apply = Handle<JSFunction>::cast(
HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value());
// Change context of {node} to the Function.prototype.call context,
// to ensure any exception is thrown in the correct context.
NodeProperties::ReplaceContextInput(
node, jsgraph()->HeapConstant(handle(apply->context(), isolate())));
// Remove the target from {node} and use the receiver as target instead, and
// the thisArg becomes the new target. If thisArg was not provided, insert
// undefined instead.
size_t arity = p.arity();
DCHECK_LE(2u, arity);
ConvertReceiverMode convert_mode;
if (arity == 2) {
// The thisArg was not provided, use undefined as receiver.
convert_mode = ConvertReceiverMode::kNullOrUndefined;
node->ReplaceInput(0, node->InputAt(1));
node->ReplaceInput(1, jsgraph()->UndefinedConstant());
} else {
// Just remove the target, which is the first value input.
convert_mode = ConvertReceiverMode::kAny;
node->RemoveInput(0);
--arity;
}
// TODO(turbofan): Migrate the call count to the new operator?
NodeProperties::ChangeOp(node, javascript()->CallFunction(
arity, p.language_mode(), VectorSlotPair(),
convert_mode, p.tail_call_mode()));
return Changed(node);
}
// ECMA-262, section 15.8.2.11.
Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
JSCallReduction r(node);
......@@ -185,8 +149,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
// Dispatch according to the BuiltinFunctionId if present.
if (!r.HasBuiltinFunctionId()) return NoChange();
switch (r.GetBuiltinFunctionId()) {
case kFunctionCall:
return ReduceFunctionCall(node);
case kMathMax:
reduction = ReduceMathMax(node);
break;
......@@ -228,11 +190,6 @@ SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
return jsgraph()->simplified();
}
JSOperatorBuilder* JSBuiltinReducer::javascript() const {
return jsgraph()->javascript();
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -14,7 +14,6 @@ namespace compiler {
// Forward declarations.
class CommonOperatorBuilder;
class JSGraph;
class JSOperatorBuilder;
class MachineOperatorBuilder;
class SimplifiedOperatorBuilder;
......@@ -38,7 +37,6 @@ class JSBuiltinReducer final : public AdvancedReducer {
CommonOperatorBuilder* common() const;
MachineOperatorBuilder* machine() const;
SimplifiedOperatorBuilder* simplified() const;
JSOperatorBuilder* javascript() const;
JSGraph* jsgraph_;
};
......
// Copyright 2015 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.
#include "src/compiler/js-call-reducer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-matchers.h"
#include "src/objects-inl.h"
#include "src/type-feedback-vector-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
namespace {
VectorSlotPair CallCountFeedback(VectorSlotPair p) {
// Extract call count from {p}.
if (!p.IsValid()) return VectorSlotPair();
CallICNexus n(p.vector(), p.slot());
int const call_count = n.ExtractCallCount();
if (call_count <= 0) return VectorSlotPair();
// Create megamorphic CallIC feedback with the given {call_count}.
StaticFeedbackVectorSpec spec;
FeedbackVectorSlot slot = spec.AddCallICSlot();
Handle<TypeFeedbackMetadata> metadata =
TypeFeedbackMetadata::New(n.GetIsolate(), &spec);
Handle<TypeFeedbackVector> vector =
TypeFeedbackVector::New(n.GetIsolate(), metadata);
CallICNexus nexus(vector, slot);
nexus.ConfigureMegamorphic(call_count);
return VectorSlotPair(vector, slot);
}
} // namespace
Reduction JSCallReducer::Reduce(Node* node) {
if (node->opcode() == IrOpcode::kJSCallFunction) {
HeapObjectMatcher m(node->InputAt(0));
if (m.HasValue() && m.Value()->IsJSFunction()) {
Handle<SharedFunctionInfo> shared(
Handle<JSFunction>::cast(m.Value())->shared(), isolate());
if (shared->HasBuiltinFunctionId()) {
switch (shared->builtin_function_id()) {
case kFunctionApply:
return ReduceFunctionPrototypeApply(node);
case kFunctionCall:
return ReduceFunctionPrototypeCall(node);
default:
break;
}
}
}
}
return NoChange();
}
// ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
Node* target = NodeProperties::GetValueInput(node, 0);
CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
Handle<JSFunction> apply =
Handle<JSFunction>::cast(HeapObjectMatcher(target).Value());
size_t arity = p.arity();
DCHECK_LE(2u, arity);
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
if (arity == 2) {
// Neither thisArg nor argArray was provided.
convert_mode = ConvertReceiverMode::kNullOrUndefined;
node->ReplaceInput(0, node->InputAt(1));
node->ReplaceInput(1, jsgraph()->UndefinedConstant());
} else if (arity == 3) {
// The argArray was not provided, just remove the {target}.
node->RemoveInput(0);
--arity;
} else if (arity == 4) {
// Check if argArray is an arguments object, and {node} is the only value
// user of argArray (except for value uses in frame states).
Node* arg_array = NodeProperties::GetValueInput(node, 3);
if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
for (Edge edge : arg_array->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 {arg_array}).
CreateArgumentsParameters const& p =
CreateArgumentsParametersOf(arg_array->op());
Node* frame_state = NodeProperties::GetFrameStateInput(arg_array, 0);
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);
if (p.type() == CreateArgumentsParameters::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();
}
// Remove the argArray input from the {node}.
node->RemoveInput(static_cast<int>(--arity));
// Add the actual parameters to the {node}, skipping the receiver.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
for (int i = p.start_index() + 1; i < state_info.parameter_count(); ++i) {
node->InsertInput(graph()->zone(), static_cast<int>(arity),
parameters->InputAt(i));
++arity;
}
// Drop the {target} from the {node}.
node->RemoveInput(0);
--arity;
} else {
return NoChange();
}
// Change {node} to the new {JSCallFunction} operator.
NodeProperties::ChangeOp(
node, javascript()->CallFunction(arity, p.language_mode(),
CallCountFeedback(p.feedback()),
convert_mode, p.tail_call_mode()));
// Change context of {node} to the Function.prototype.apply context,
// to ensure any exception is thrown in the correct context.
NodeProperties::ReplaceContextInput(
node, jsgraph()->HeapConstant(handle(apply->context(), isolate())));
return Changed(node);
}
// ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
Handle<JSFunction> call = Handle<JSFunction>::cast(
HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value());
// Change context of {node} to the Function.prototype.call context,
// to ensure any exception is thrown in the correct context.
NodeProperties::ReplaceContextInput(
node, jsgraph()->HeapConstant(handle(call->context(), isolate())));
// Remove the target from {node} and use the receiver as target instead, and
// the thisArg becomes the new target. If thisArg was not provided, insert
// undefined instead.
size_t arity = p.arity();
DCHECK_LE(2u, arity);
ConvertReceiverMode convert_mode;
if (arity == 2) {
// The thisArg was not provided, use undefined as receiver.
convert_mode = ConvertReceiverMode::kNullOrUndefined;
node->ReplaceInput(0, node->InputAt(1));
node->ReplaceInput(1, jsgraph()->UndefinedConstant());
} else {
// Just remove the target, which is the first value input.
convert_mode = ConvertReceiverMode::kAny;
node->RemoveInput(0);
--arity;
}
NodeProperties::ChangeOp(
node, javascript()->CallFunction(arity, p.language_mode(),
CallCountFeedback(p.feedback()),
convert_mode, p.tail_call_mode()));
return Changed(node);
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
JSOperatorBuilder* JSCallReducer::javascript() const {
return jsgraph()->javascript();
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2015 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.
#ifndef V8_COMPILER_JS_CALL_REDUCER_H_
#define V8_COMPILER_JS_CALL_REDUCER_H_
#include "src/compiler/graph-reducer.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class JSGraph;
class JSOperatorBuilder;
// Performs strength reduction on {JSCallFunction} nodes, which might allow
// inlining or other optimizations to be performed afterwards.
class JSCallReducer final : public Reducer {
public:
explicit JSCallReducer(JSGraph* jsgraph) : jsgraph_(jsgraph) {}
Reduction Reduce(Node* node) final;
private:
Reduction ReduceFunctionPrototypeApply(Node* node);
Reduction ReduceFunctionPrototypeCall(Node* node);
Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const;
JSOperatorBuilder* javascript() const;
JSGraph* const jsgraph_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_JS_CALL_REDUCER_H_
......@@ -28,6 +28,7 @@
#include "src/compiler/instruction.h"
#include "src/compiler/instruction-selector.h"
#include "src/compiler/js-builtin-reducer.h"
#include "src/compiler/js-call-reducer.h"
#include "src/compiler/js-context-relaxation.h"
#include "src/compiler/js-context-specialization.h"
#include "src/compiler/js-frame-specialization.h"
......@@ -537,6 +538,7 @@ struct InliningPhase {
data->common());
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->common(), data->machine());
JSCallReducer call_reducer(data->jsgraph());
JSContextSpecialization context_specialization(
&graph_reducer, data->jsgraph(),
data->info()->is_function_context_specializing()
......@@ -555,6 +557,7 @@ struct InliningPhase {
AddReducer(data, &graph_reducer, &frame_specialization);
}
AddReducer(data, &graph_reducer, &context_specialization);
AddReducer(data, &graph_reducer, &call_reducer);
AddReducer(data, &graph_reducer, &inlining);
graph_reducer.ReduceGraph();
}
......
......@@ -520,6 +520,19 @@ void CallICNexus::ConfigureMonomorphic(Handle<JSFunction> function) {
}
void CallICNexus::ConfigureMegamorphic() {
FeedbackNexus::ConfigureMegamorphic();
}
void CallICNexus::ConfigureMegamorphic(int call_count) {
SetFeedback(*TypeFeedbackVector::MegamorphicSentinel(GetIsolate()),
SKIP_WRITE_BARRIER);
SetFeedbackExtra(Smi::FromInt(call_count * kCallCountIncrement),
SKIP_WRITE_BARRIER);
}
void LoadICNexus::ConfigureMonomorphic(Handle<Map> receiver_map,
Handle<Code> handler) {
Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
......
......@@ -375,9 +375,9 @@ class FeedbackNexus {
inline Object* GetFeedback() const;
inline Object* GetFeedbackExtra() const;
protected:
inline Isolate* GetIsolate() const;
protected:
inline void SetFeedback(Object* feedback,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline void SetFeedbackExtra(Object* feedback_extra,
......@@ -399,7 +399,7 @@ class FeedbackNexus {
};
class CallICNexus : public FeedbackNexus {
class CallICNexus final : public FeedbackNexus {
public:
// Monomorphic call ics store call counts. Platform code needs to increment
// the count appropriately (ie, by 2).
......@@ -418,17 +418,19 @@ class CallICNexus : public FeedbackNexus {
void ConfigureMonomorphicArray();
void ConfigureMonomorphic(Handle<JSFunction> function);
void ConfigureMegamorphic() final;
void ConfigureMegamorphic(int call_count);
InlineCacheState StateFromFeedback() const override;
InlineCacheState StateFromFeedback() const final;
int ExtractMaps(MapHandleList* maps) const override {
int ExtractMaps(MapHandleList* maps) const final {
// CallICs don't record map feedback.
return 0;
}
MaybeHandle<Code> FindHandlerForMap(Handle<Map> map) const override {
MaybeHandle<Code> FindHandlerForMap(Handle<Map> map) const final {
return MaybeHandle<Code>();
}
bool FindHandlers(CodeHandleList* code_list, int length = -1) const override {
bool FindHandlers(CodeHandleList* code_list, int length = -1) const final {
return length == 0;
}
......
......@@ -520,6 +520,8 @@
'../../src/compiler/interpreter-assembler.h',
'../../src/compiler/js-builtin-reducer.cc',
'../../src/compiler/js-builtin-reducer.h',
'../../src/compiler/js-call-reducer.cc',
'../../src/compiler/js-call-reducer.h',
'../../src/compiler/js-context-relaxation.cc',
'../../src/compiler/js-context-relaxation.h',
'../../src/compiler/js-context-specialization.cc',
......
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