Chromium Code Reviews

Unified Diff: src/compiler/js-inlining.cc

Issue 453833003: Add initial support for inlining. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Add initial support for inlining. Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Index: src/compiler/js-inlining.cc
diff --git a/src/compiler/js-inlining.cc b/src/compiler/js-inlining.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0f6bb93ebc40d35a1b93c98a2888699359e8e789
--- /dev/null
+++ b/src/compiler/js-inlining.cc
@@ -0,0 +1,324 @@
+// 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/compiler/ast-graph-builder.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/js-inlining.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/node-aux-data-inl.h"
+#include "src/compiler/node-matchers.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/simplified-operator.h"
+#include "src/compiler/typer.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+
+
+namespace v8 {
+namespace internal {
+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 InlinerVisitor : public NullNodeVisitor {
+ public:
+ explicit InlinerVisitor(JSInliner* spec) : spec_(spec) {}
+
+ GenericGraphVisit::Control Post(Node* node) {
+ switch (node->opcode()) {
+ case IrOpcode::kJSCallFunction:
+ spec_->InlineCall(node);
+ break;
+ default:
+ break;
+ }
+ return GenericGraphVisit::CONTINUE;
+ }
+
+ private:
+ JSInliner* spec_;
+};
+
+
+void JSInliner::Inline() {
+ if (!info_->closure()->PassesFilter(FLAG_turbo_inlining_filter)) {
+ return;
+ }
+ InlinerVisitor visitor(this);
+ jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor);
+}
+
+
+/// Ensure that only a single return reaches the end node.
Michael Starzinger 2014/08/12 20:04:46 nit: Two slashes ought to be enough for everyone.
sigurds 2014/08/14 09:54:41 Done.
+void JSInliner::UnifyReturn(JSGraph* jsgraph) {
+ Graph* graph = jsgraph->graph();
+
+ Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0);
+ if (final_merge->opcode() == IrOpcode::kReturn) {
+ // nothing to do
+ return;
+ }
+ DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode());
+
+ int predecessors =
+ OperatorProperties::GetControlInputCount(final_merge->op());
+ Operator* op_phi = jsgraph->common()->Phi(predecessors);
+ Operator* op_ephi = jsgraph->common()->EffectPhi(predecessors);
+
+ std::vector<Node*> values;
+ std::vector<Node*> effects;
+ // Iterate over all control flow predecessors,
+ // which must be return statements.
+ InputIter iter = final_merge->inputs().begin();
+ while (iter != final_merge->inputs().end()) {
+ Node* input = *iter;
+ switch (input->opcode()) {
+ case IrOpcode::kReturn:
+ values.push_back(NodeProperties::GetValueInput(input, 0));
+ effects.push_back(NodeProperties::GetEffectInput(input));
+ iter.UpdateToAndIncrement(NodeProperties::GetControlInput(input));
+ input->RemoveAllInputs();
+ break;
+ default:
+ UNREACHABLE();
+ ++iter;
+ break;
+ }
+ }
+ values.push_back(final_merge);
+ effects.push_back(final_merge);
+ Node* phi = graph->NewNode(op_phi, values.size(), values.data());
+ Node* ephi = graph->NewNode(op_ephi, effects.size(), effects.data());
+ Node* new_return =
+ graph->NewNode(jsgraph->common()->Return(), phi, ephi, final_merge);
+ graph->end()->ReplaceInput(0, new_return);
+}
+
+
+void moveWithDependencies(JSGraph* graph, Node* node, Node* old_block,
Michael Starzinger 2014/08/12 20:04:45 style: Uppercase "M". Also needs to be static.
sigurds 2014/08/14 09:54:42 Done.
+ Node* new_block) {
+ if (OperatorProperties::HasControlInput(node->op())) {
+ // Check if we have left the old_block.
+ if (NodeProperties::GetControlInput(node) != old_block) return;
+ // If not, move this node to the new_block.
+ NodeProperties::ReplaceControlInput(node, new_block);
+ }
+ // Independent of whether a node has a control input or not,
+ // it might have a dependency that is pinned to old_block.
+ for (InputIter iter = node->inputs().begin(); iter != node->inputs().end();
+ ++iter) {
+ if (NodeProperties::IsControlEdge(iter.edge())) continue;
+ moveWithDependencies(graph, *iter, old_block, new_block);
+ }
+}
+
+
+static bool IsStackGuard(Node* node) {
+ if (node->opcode() != IrOpcode::kJSCallRuntime) return false;
+ Runtime::FunctionId id = OpParameter<Runtime::FunctionId>(node);
+ return id == Runtime::kStackGuard;
+}
+
+
+static void RemoveStackGuard(Node* stackGuard) {
+ DCHECK(IsStackGuard(stackGuard));
+ DCHECK_EQ(1, stackGuard->UseCount());
+ NodeProperties::ReplaceEffectInput(
+ stackGuard->UseAt(0), NodeProperties::GetEffectInput(stackGuard));
+ stackGuard->RemoveAllInputs();
+}
+
+
+static void moveAllControlNodes(Node* from, Node* to) {
Michael Starzinger 2014/08/12 20:04:46 style: Uppercase "M".
sigurds 2014/08/14 09:54:42 Done.
+ for (UseIter iter = from->uses().begin(); iter != from->uses().end();) {
+ if (NodeProperties::IsControlEdge(iter.edge())) {
+ iter.UpdateToAndIncrement(to);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+
+void DoInline(Zone* zone, Node* call, CompilationInfo* info, JSGraph* jsgraph) {
Michael Starzinger 2014/08/12 20:04:46 Either make this a (private) member method of JSIn
sigurds 2014/08/14 09:54:42 Made it a private method. (Saves Zone* argument)
+ Graph* graph = jsgraph->graph();
+ MachineOperatorBuilder machine(zone);
+
+ Node* control = NodeProperties::GetControlInput(call);
+ Node* unified_return = NodeProperties::GetControlInput(graph->end());
+ DCHECK_EQ(IrOpcode::kReturn, unified_return->opcode());
+ Node* inlined_control = NodeProperties::GetControlInput(unified_return);
+ Node* bottom_block = inlined_control;
+ // Move all the nodes to the bottom block.
+ moveAllControlNodes(control, bottom_block);
+ // Now move the ones the call depends on back up.
+ // We have to do this back-and-forth to treat the case where the call is
+ // pinned to the start block.
+ moveWithDependencies(jsgraph, call, bottom_block, control);
+
+ // The inlinee uses the context from the JSFunction object.
+ Node* context = graph->NewNode(
+ machine.Load(kMachineTagged), NodeProperties::GetValueInput(call, 0),
+ jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag),
+ NodeProperties::GetEffectInput(call));
+
+ Node* stackGuard = NULL;
+
+ // {inlinee_inputs} counts JSFunction, Receiver, arguments, context,
+ // but not effect, control.
+ int inlinee_inputs = graph->start()->op()->OutputCount();
+ // Context is last argument.
+ int inlinee_context_index = inlinee_inputs - 1;
+ // {inliner_inputs} counts JSFunction, Receiver, arguments, but not
+ // context, effect, control.
+ int inliner_inputs = OperatorProperties::GetValueInputCount(call->op());
+ // Iterate over all uses of the start node.
+ UseIter iter = graph->start()->uses().begin();
+ while (iter != graph->start()->uses().end()) {
+ Node* use = *iter;
+ switch (use->opcode()) {
+ case IrOpcode::kParameter: {
+ int index = 1 + static_cast<Operator1<int>*>(use->op())->parameter();
+ if (index < inliner_inputs && index < inlinee_context_index) {
+ // There is an input from the call, and the index is a value
+ // projection but not the context, so rewire the input.
+ ReplaceEffectfulWithValue(*iter, call->InputAt(index));
+ } else if (index == inlinee_context_index) {
+ // This is the context projection, rewire it to the context from the
+ // JSFunction object.
+ ReplaceEffectfulWithValue(*iter, context);
+ } else if (index < inlinee_context_index) {
+ // Call has fewer arguments than required, fill with undefined.
+ ReplaceEffectfulWithValue(*iter, jsgraph->UndefinedConstant());
+ } else {
+ // We got too many arguments, discard for now.
+ // TODO(sigurds): Fix to treat arguments array correctly.
+ }
+ ++iter;
+ break;
+ }
+ default:
+ if (NodeProperties::IsEffectEdge(iter.edge())) {
+ iter.UpdateToAndIncrement(context);
+ stackGuard = use;
+ } else if (NodeProperties::IsControlEdge(iter.edge())) {
+ iter.UpdateToAndIncrement(control);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ }
+
+ // TODO(sigurds) Do not make assumptions about position of stack guard.
+ if (IsStackGuard(stackGuard)) {
Michael Starzinger 2014/08/12 20:04:46 Let's just drop the removal of the stack-guard for
sigurds 2014/08/14 09:54:42 Done.
+ RemoveStackGuard(stackGuard);
+ }
+
+ // Iterate over all uses of the call node.
+ iter = call->uses().begin();
+ while (iter != call->uses().end()) {
+ if (NodeProperties::IsEffectEdge(iter.edge())) {
+ iter.UpdateToAndIncrement(NodeProperties::GetEffectInput(unified_return));
+ } else if (NodeProperties::IsControlEdge(iter.edge())) {
+ UNREACHABLE();
+ } else {
+ DCHECK(NodeProperties::IsValueEdge(iter.edge()));
+ iter.UpdateToAndIncrement(
+ NodeProperties::GetValueInput(unified_return, 0));
+ }
+ }
+ call->RemoveAllInputs();
+ unified_return->RemoveAllInputs();
+}
+
+
+void JSInliner::InlineCall(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
+
+ ValueMatcher<Handle<JSFunction> > match(node->InputAt(0));
+ if (!match.HasValue()) {
+ return;
+ }
+
+ Handle<JSFunction> function = match.Value();
+ SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString();
Michael Starzinger 2014/08/12 20:04:45 nit: The "name" is only use for tracing, please mo
sigurds 2014/08/14 09:54:42 Done.
+
+ if (function->shared()->native()) {
+ if (FLAG_trace_turbo_inlining) {
+ printf("Not Inlining %s into %s because inlinee is native\n", name.get(),
Michael Starzinger 2014/08/12 20:04:45 nit: s/printf/PrintF/
sigurds 2014/08/14 09:54:42 Done.
+ info_->shared_info()->DebugName()->ToCString().get());
+ }
+ return;
+ }
+
+ CompilationInfoWithZone info(function);
+
+ CHECK(Parser::Parse(&info));
+ StrictMode strict_mode = info.function()->strict_mode();
+ info.SetStrictMode(strict_mode);
+ info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
+ CHECK(Rewriter::Rewrite(&info));
+ CHECK(Scope::Analyze(&info));
+ CHECK_NE(NULL, info.scope());
+
+ if (info.scope()->arguments() != NULL) {
+ // For now do not inline functions that use their arguments array.
+ if (FLAG_trace_turbo_inlining) {
+ printf(
Michael Starzinger 2014/08/12 20:04:46 nit: s/printf/PrintF/
sigurds 2014/08/14 09:54:42 Done.
+ "Not Inlining %s into %s because inlinee uses arguments "
+ "array\n",
+ name.get(), info_->shared_info()->DebugName()->ToCString().get());
+ }
+ return;
+ }
+
+ if (!info.shared_info().is_null()) {
+ Handle<ScopeInfo> scope_info = ScopeInfo::Create(info.scope(), info.zone());
+ info.shared_info()->set_scope_info(*scope_info);
+ }
+ if (FLAG_trace_turbo_inlining) {
+ printf("Inlining %s into %s\n", name.get(),
Michael Starzinger 2014/08/12 20:04:46 nit: s/printf/PrintF/
sigurds 2014/08/14 09:54:42 Done.
+ info_->shared_info()->DebugName()->ToCString().get());
+ }
+
+ Graph graph(info_->zone());
+ graph.SetNextNodeId(jsgraph_->graph()->NodeCount());
Michael Starzinger 2014/08/12 20:04:45 I assume this is only temporary for now that we do
sigurds 2014/08/14 09:54:41 Yes. To copying the graph will follow as a separat
+ Typer typer(info_->zone());
+ CommonOperatorBuilder common(info_->zone());
+ JSGraph jsgraph(&graph, &common, &typer);
+ AstGraphBuilder graph_builder(&info, &jsgraph);
+ graph_builder.CreateGraph();
+
+ UnifyReturn(&jsgraph);
Michael Starzinger 2014/08/12 20:04:46 On a high level it is terribly confusing when "jsg
sigurds 2014/08/14 09:54:41 I agree. Effectively, we are reimplementing the pi
+
+ DoInline(jsgraph_->zone(), node, &info, &jsgraph);
+
+ jsgraph_->graph()->SetNextNodeId(graph.NodeCount());
+}
+}
+}
+} // namespace v8::internal::compiler
« no previous file with comments | « src/compiler/js-inlining.h ('k') | src/compiler/pipeline.cc » ('j') | src/compiler/pipeline.cc » ('J')

Powered by Google App Engine