Commit 4b943f35 authored by sigurds@chromium.org's avatar sigurds@chromium.org

Add initial support for inlining.

* Add stack depth checking to function tester.
* Make context specialization clean up after itself.
* Add UpdateToAndIncrement to Inputs::iterator.
  Uses:iterator already provides this member function.
* Allow next node id in graph to be set.

R=mstarzinger@chromium.org, titzer@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23197 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8394975e
...@@ -499,6 +499,8 @@ source_set("v8_base") { ...@@ -499,6 +499,8 @@ source_set("v8_base") {
"src/compiler/js-generic-lowering.h", "src/compiler/js-generic-lowering.h",
"src/compiler/js-graph.cc", "src/compiler/js-graph.cc",
"src/compiler/js-graph.h", "src/compiler/js-graph.h",
"src/compiler/js-inlining.cc",
"src/compiler/js-inlining.h",
"src/compiler/js-operator.h", "src/compiler/js-operator.h",
"src/compiler/js-typed-lowering.cc", "src/compiler/js-typed-lowering.cc",
"src/compiler/js-typed-lowering.h", "src/compiler/js-typed-lowering.h",
......
...@@ -22,6 +22,7 @@ class GenericGraphBase : public ZoneObject { ...@@ -22,6 +22,7 @@ class GenericGraphBase : public ZoneObject {
NodeId NextNodeID() { return next_node_id_++; } NodeId NextNodeID() { return next_node_id_++; }
NodeId NodeCount() const { return next_node_id_; } NodeId NodeCount() const { return next_node_id_; }
void SetNextNodeId(NodeId next) { next_node_id_ = next; }
private: private:
Zone* zone_; Zone* zone_;
......
...@@ -204,6 +204,12 @@ class GenericNode<B, S>::Inputs::iterator { ...@@ -204,6 +204,12 @@ class GenericNode<B, S>::Inputs::iterator {
++index_; ++index_;
return *this; return *this;
} }
iterator& UpdateToAndIncrement(GenericNode<B, S>* new_to) {
typename GenericNode<B, S>::Input* input = GetInput();
input->Update(new_to);
index_++;
return *this;
}
int index() { return index_; } int index() { return index_; }
private: private:
......
...@@ -15,26 +15,6 @@ namespace v8 { ...@@ -15,26 +15,6 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
// TODO(titzer): factor this out to a common routine with js-typed-lowering.
static void ReplaceEffectfulWithValue(Node* node, Node* value) {
Node* effect = NULL;
if (OperatorProperties::HasEffectInput(node->op())) {
effect = NodeProperties::GetEffectInput(node);
}
// Requires distinguishing between value and effect edges.
UseIter iter = node->uses().begin();
while (iter != node->uses().end()) {
if (NodeProperties::IsEffectEdge(iter.edge())) {
DCHECK_NE(NULL, effect);
iter = iter.UpdateToAndIncrement(effect);
} else {
iter = iter.UpdateToAndIncrement(value);
}
}
}
class ContextSpecializationVisitor : public NullNodeVisitor { class ContextSpecializationVisitor : public NullNodeVisitor {
public: public:
explicit ContextSpecializationVisitor(JSContextSpecializer* spec) explicit ContextSpecializationVisitor(JSContextSpecializer* spec)
...@@ -45,14 +25,16 @@ class ContextSpecializationVisitor : public NullNodeVisitor { ...@@ -45,14 +25,16 @@ class ContextSpecializationVisitor : public NullNodeVisitor {
case IrOpcode::kJSLoadContext: { case IrOpcode::kJSLoadContext: {
Reduction r = spec_->ReduceJSLoadContext(node); Reduction r = spec_->ReduceJSLoadContext(node);
if (r.Changed() && r.replacement() != node) { if (r.Changed() && r.replacement() != node) {
ReplaceEffectfulWithValue(node, r.replacement()); NodeProperties::ReplaceWithValue(node, r.replacement());
node->RemoveAllInputs();
} }
break; break;
} }
case IrOpcode::kJSStoreContext: { case IrOpcode::kJSStoreContext: {
Reduction r = spec_->ReduceJSStoreContext(node); Reduction r = spec_->ReduceJSStoreContext(node);
if (r.Changed() && r.replacement() != node) { if (r.Changed() && r.replacement() != node) {
ReplaceEffectfulWithValue(node, r.replacement()); NodeProperties::ReplaceWithValue(node, r.replacement());
node->RemoveAllInputs();
} }
break; break;
} }
...@@ -68,7 +50,8 @@ class ContextSpecializationVisitor : public NullNodeVisitor { ...@@ -68,7 +50,8 @@ class ContextSpecializationVisitor : public NullNodeVisitor {
void JSContextSpecializer::SpecializeToContext() { void JSContextSpecializer::SpecializeToContext() {
ReplaceEffectfulWithValue(context_, jsgraph_->Constant(info_->context())); NodeProperties::ReplaceWithValue(context_,
jsgraph_->Constant(info_->context()));
ContextSpecializationVisitor visitor(this); ContextSpecializationVisitor visitor(this);
jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor); jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor);
......
This diff is collapsed.
// Copyright 2014 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_INLINING_H_
#define V8_COMPILER_JS_INLINING_H_
#include "src/compiler/js-graph.h"
#include "src/v8.h"
namespace v8 {
namespace internal {
namespace compiler {
class JSInliner {
public:
JSInliner(CompilationInfo* info, JSGraph* jsgraph)
: info_(info), jsgraph_(jsgraph) {}
void Inline();
void TryInlineCall(Node* node);
private:
friend class InlinerVisitor;
CompilationInfo* info_;
JSGraph* jsgraph_;
static void UnifyReturn(Graph* graph);
};
}
}
} // namespace v8::internal::compiler
#endif // V8_COMPILER_JS_INLINING_H_
...@@ -17,39 +17,18 @@ namespace compiler { ...@@ -17,39 +17,18 @@ namespace compiler {
// - relax effects from generic but not-side-effecting operations // - relax effects from generic but not-side-effecting operations
// - relax effects for ToNumber(mixed) // - relax effects for ToNumber(mixed)
// Replace value uses of {node} with {value} and effect uses of {node} with
// {effect}. If {effect == NULL}, then use the effect input to {node}.
// TODO(titzer): move into a GraphEditor?
static void ReplaceUses(Node* node, Node* value, Node* effect) {
if (value == effect) {
// Effect and value updates are the same; no special iteration needed.
if (value != node) node->ReplaceUses(value);
return;
}
if (effect == NULL) effect = NodeProperties::GetEffectInput(node);
// The iteration requires distinguishing between value and effect edges.
UseIter iter = node->uses().begin();
while (iter != node->uses().end()) {
if (NodeProperties::IsEffectEdge(iter.edge())) {
iter = iter.UpdateToAndIncrement(effect);
} else {
iter = iter.UpdateToAndIncrement(value);
}
}
}
// Relax the effects of {node} by immediately replacing effect uses of {node} // Relax the effects of {node} by immediately replacing effect uses of {node}
// with the effect input to {node}. // with the effect input to {node}.
// TODO(turbofan): replace the effect input to {node} with {graph->start()}. // TODO(turbofan): replace the effect input to {node} with {graph->start()}.
// TODO(titzer): move into a GraphEditor? // TODO(titzer): move into a GraphEditor?
static void RelaxEffects(Node* node) { ReplaceUses(node, node, NULL); } static void RelaxEffects(Node* node) {
NodeProperties::ReplaceWithValue(node, node, NULL);
}
Reduction JSTypedLowering::ReplaceEagerly(Node* old, Node* node) { Reduction JSTypedLowering::ReplaceEagerly(Node* old, Node* node) {
ReplaceUses(old, node, node); NodeProperties::ReplaceWithValue(old, node, node);
return Reducer::Changed(node); return Reducer::Changed(node);
} }
...@@ -522,7 +501,7 @@ Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) { ...@@ -522,7 +501,7 @@ Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) {
static Reduction ReplaceWithReduction(Node* node, Reduction reduction) { static Reduction ReplaceWithReduction(Node* node, Reduction reduction) {
if (reduction.Changed()) { if (reduction.Changed()) {
ReplaceUses(node, reduction.replacement(), NULL); NodeProperties::ReplaceWithValue(node, reduction.replacement());
return reduction; return reduction;
} }
return Reducer::NoChange(); return Reducer::NoChange();
...@@ -573,13 +552,13 @@ Reduction JSTypedLowering::Reduce(Node* node) { ...@@ -573,13 +552,13 @@ Reduction JSTypedLowering::Reduce(Node* node) {
// !x => BooleanNot(x) // !x => BooleanNot(x)
value = value =
graph()->NewNode(simplified()->BooleanNot(), result.replacement()); graph()->NewNode(simplified()->BooleanNot(), result.replacement());
ReplaceUses(node, value, NULL); NodeProperties::ReplaceWithValue(node, value);
return Changed(value); return Changed(value);
} else { } else {
// !x => BooleanNot(JSToBoolean(x)) // !x => BooleanNot(JSToBoolean(x))
value = graph()->NewNode(simplified()->BooleanNot(), node); value = graph()->NewNode(simplified()->BooleanNot(), node);
node->set_op(javascript()->ToBoolean()); node->set_op(javascript()->ToBoolean());
ReplaceUses(node, value, node); NodeProperties::ReplaceWithValue(node, value, node);
// Note: ReplaceUses() smashes all uses, so smash it back here. // Note: ReplaceUses() smashes all uses, so smash it back here.
value->ReplaceInput(0, node); value->ReplaceInput(0, node);
return ReplaceWith(value); return ReplaceWith(value);
......
...@@ -148,6 +148,28 @@ inline void NodeProperties::RemoveNonValueInputs(Node* node) { ...@@ -148,6 +148,28 @@ inline void NodeProperties::RemoveNonValueInputs(Node* node) {
} }
// Replace value uses of {node} with {value} and effect uses of {node} with
// {effect}. If {effect == NULL}, then use the effect input to {node}.
inline void NodeProperties::ReplaceWithValue(Node* node, Node* value,
Node* effect) {
DCHECK(!OperatorProperties::HasControlOutput(node->op()));
if (effect == NULL && OperatorProperties::HasEffectInput(node->op())) {
effect = NodeProperties::GetEffectInput(node);
}
// Requires distinguishing between value and effect edges.
UseIter iter = node->uses().begin();
while (iter != node->uses().end()) {
if (NodeProperties::IsEffectEdge(iter.edge())) {
DCHECK_NE(NULL, effect);
iter = iter.UpdateToAndIncrement(effect);
} else {
iter = iter.UpdateToAndIncrement(value);
}
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Type Bounds. // Type Bounds.
......
...@@ -33,6 +33,8 @@ class NodeProperties { ...@@ -33,6 +33,8 @@ class NodeProperties {
static inline void ReplaceEffectInput(Node* node, Node* effect, static inline void ReplaceEffectInput(Node* node, Node* effect,
int index = 0); int index = 0);
static inline void RemoveNonValueInputs(Node* node); static inline void RemoveNonValueInputs(Node* node);
static inline void ReplaceWithValue(Node* node, Node* value,
Node* effect = NULL);
static inline Bounds GetBounds(Node* node); static inline Bounds GetBounds(Node* node);
static inline void SetBounds(Node* node, Bounds bounds); static inline void SetBounds(Node* node, Bounds bounds);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "src/compiler/instruction-selector.h" #include "src/compiler/instruction-selector.h"
#include "src/compiler/js-context-specialization.h" #include "src/compiler/js-context-specialization.h"
#include "src/compiler/js-generic-lowering.h" #include "src/compiler/js-generic-lowering.h"
#include "src/compiler/js-inlining.h"
#include "src/compiler/js-typed-lowering.h" #include "src/compiler/js-typed-lowering.h"
#include "src/compiler/phi-reducer.h" #include "src/compiler/phi-reducer.h"
#include "src/compiler/register-allocator.h" #include "src/compiler/register-allocator.h"
...@@ -186,7 +187,7 @@ Handle<Code> Pipeline::GenerateCode() { ...@@ -186,7 +187,7 @@ Handle<Code> Pipeline::GenerateCode() {
VerifyAndPrintGraph(&graph, "Initial untyped"); VerifyAndPrintGraph(&graph, "Initial untyped");
if (FLAG_context_specialization) { if (FLAG_context_specialization) {
SourcePositionTable::Scope pos_(&source_positions, SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown()); SourcePosition::Unknown());
// Specialize the code to the context as aggressively as possible. // Specialize the code to the context as aggressively as possible.
JSContextSpecializer spec(info(), &jsgraph, context_node); JSContextSpecializer spec(info(), &jsgraph, context_node);
...@@ -194,6 +195,14 @@ Handle<Code> Pipeline::GenerateCode() { ...@@ -194,6 +195,14 @@ Handle<Code> Pipeline::GenerateCode() {
VerifyAndPrintGraph(&graph, "Context specialized"); VerifyAndPrintGraph(&graph, "Context specialized");
} }
if (FLAG_turbo_inlining) {
SourcePositionTable::Scope pos(&source_positions,
SourcePosition::Unknown());
JSInliner inliner(info(), &jsgraph);
inliner.Inline();
VerifyAndPrintGraph(&graph, "Inlined");
}
// Print a replay of the initial graph. // Print a replay of the initial graph.
if (FLAG_print_turbo_replay) { if (FLAG_print_turbo_replay) {
GraphReplayPrinter::PrintReplay(&graph); GraphReplayPrinter::PrintReplay(&graph);
......
...@@ -338,6 +338,8 @@ DEFINE_BOOL(turbo_source_positions, false, ...@@ -338,6 +338,8 @@ DEFINE_BOOL(turbo_source_positions, false,
DEFINE_BOOL(context_specialization, true, DEFINE_BOOL(context_specialization, true,
"enable context specialization in TurboFan") "enable context specialization in TurboFan")
DEFINE_BOOL(turbo_deoptimization, false, "enable deoptimization in TurboFan") DEFINE_BOOL(turbo_deoptimization, false, "enable deoptimization in TurboFan")
DEFINE_BOOL(turbo_inlining, false, "enable inlining in TurboFan")
DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining")
DEFINE_INT(typed_array_max_size_in_heap, 64, DEFINE_INT(typed_array_max_size_in_heap, 64,
"threshold for in-heap typed array") "threshold for in-heap typed array")
......
...@@ -71,6 +71,7 @@ ...@@ -71,6 +71,7 @@
'compiler/test-pipeline.cc', 'compiler/test-pipeline.cc',
'compiler/test-representation-change.cc', 'compiler/test-representation-change.cc',
'compiler/test-run-deopt.cc', 'compiler/test-run-deopt.cc',
'compiler/test-run-inlining.cc',
'compiler/test-run-intrinsics.cc', 'compiler/test-run-intrinsics.cc',
'compiler/test-run-jsbranches.cc', 'compiler/test-run-jsbranches.cc',
'compiler/test-run-jscalls.cc', 'compiler/test-run-jscalls.cc',
......
// Copyright 2014 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/v8.h"
#include "test/cctest/compiler/function-tester.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
// TODO(sigurds) At the moment we do not write optimization frames when
// inlining, thus the reported stack depth changes depending on inlining.
// AssertStackDepth checks the stack depth at a simple way to ensure that
// inlining actually occurs.
// Once inlining creates optimization frames, all these unit tests need to
// check that the optimization frame is there.
static void AssertStackDepth(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 10, v8::StackTrace::kDetailed);
CHECK_EQ(args[0]->ToInt32()->Value(), stackTrace->GetFrameCount());
}
static void InstallAssertStackDepthHelper(v8::Isolate* isolate) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::FunctionTemplate> t =
v8::FunctionTemplate::New(isolate, AssertStackDepth);
context->Global()->Set(v8_str("AssertStackDepth"), t->GetFunction());
}
TEST(SimpleInlining) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function(){"
"function foo(s) { AssertStackDepth(1); return s; };"
"function bar(s, t) { return foo(s); };"
"return bar;})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
}
TEST(SimpleInliningContext) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function () {"
"function foo(s) { AssertStackDepth(1); var x = 12; return s + x; };"
"function bar(s, t) { return foo(s); };"
"return bar;"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
}
TEST(CaptureContext) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"var f = (function () {"
"var x = 42;"
"function bar(s) { return x + s; };"
"return (function (s) { return bar(s); });"
"})();"
"(function (s) { return f(s)})");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
}
// TODO(sigurds) For now we do not inline any native functions. If we do at
// some point, change this test.
TEST(DontInlineEval) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"var x = 42;"
"(function () {"
"function bar(s, t) { return eval(\"AssertStackDepth(2); x\") };"
"return bar;"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(42), T.Val("x"), T.undefined());
}
TEST(InlineOmitArguments) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function () {"
"var x = 42;"
"function bar(s, t, u, v) { AssertStackDepth(1); return x + s; };"
"return (function (s,t) { return bar(s); });"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
}
TEST(InlineSurplusArguments) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function () {"
"var x = 42;"
"function foo(s) { AssertStackDepth(1); return x + s; };"
"function bar(s,t) { return foo(s,t,13); };"
"return bar;"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
}
TEST(InlineTwice) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function () {"
"var x = 42;"
"function bar(s) { AssertStackDepth(1); return x + s; };"
"return (function (s,t) { return bar(s) + bar(t); });"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(2 * 42 + 12 + 4), T.Val(12), T.Val(4));
}
TEST(InlineTwiceDependent) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function () {"
"var x = 42;"
"function foo(s) { AssertStackDepth(1); return x + s; };"
"function bar(s,t) { return foo(foo(s)); };"
"return bar;"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 42 + 12), T.Val(12), T.Val(4));
}
TEST(InlineTwiceDependentDiamond) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function () {"
"function foo(s) { if (true) {"
" return 12 } else { return 13; } };"
"function bar(s,t) { return foo(foo(1)); };"
"return bar;"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(12), T.undefined(), T.undefined());
}
TEST(InlineTwiceDependentDiamondReal) {
i::FLAG_turbo_inlining = true;
FunctionTester T(
"(function () {"
"var x = 41;"
"function foo(s) { AssertStackDepth(1); if (s % 2 == 0) {"
" return x - s } else { return x + s; } };"
"function bar(s,t) { return foo(foo(s)); };"
"return bar;"
"})();");
InstallAssertStackDepthHelper(CcTest::isolate());
T.CheckCall(T.Val(-11), T.Val(11), T.Val(4));
}
...@@ -384,6 +384,8 @@ ...@@ -384,6 +384,8 @@
'../../src/compiler/js-generic-lowering.h', '../../src/compiler/js-generic-lowering.h',
'../../src/compiler/js-graph.cc', '../../src/compiler/js-graph.cc',
'../../src/compiler/js-graph.h', '../../src/compiler/js-graph.h',
'../../src/compiler/js-inlining.cc',
'../../src/compiler/js-inlining.h',
'../../src/compiler/js-operator.h', '../../src/compiler/js-operator.h',
'../../src/compiler/js-typed-lowering.cc', '../../src/compiler/js-typed-lowering.cc',
'../../src/compiler/js-typed-lowering.h', '../../src/compiler/js-typed-lowering.h',
......
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