Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(511)

Side by Side 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. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/compiler/ast-graph-builder.h"
6 #include "src/compiler/common-operator.h"
7 #include "src/compiler/generic-node-inl.h"
8 #include "src/compiler/graph-inl.h"
9 #include "src/compiler/graph-visualizer.h"
10 #include "src/compiler/js-inlining.h"
11 #include "src/compiler/js-operator.h"
12 #include "src/compiler/node-aux-data-inl.h"
13 #include "src/compiler/node-matchers.h"
14 #include "src/compiler/node-properties-inl.h"
15 #include "src/compiler/simplified-operator.h"
16 #include "src/compiler/typer.h"
17 #include "src/parser.h"
18 #include "src/rewriter.h"
19 #include "src/scopes.h"
20
21
22 namespace v8 {
23 namespace internal {
24 namespace compiler {
25
26 // TODO(titzer): factor this out to a common routine with js-typed-lowering.
27 static void ReplaceEffectfulWithValue(Node* node, Node* value) {
titzer 2014/08/14 10:24:31 Can you find a place for this routine? I think thi
sigurds 2014/08/19 11:31:19 Done.
28 Node* effect = NULL;
29 if (OperatorProperties::HasEffectInput(node->op())) {
30 effect = NodeProperties::GetEffectInput(node);
31 }
32
33 // Requires distinguishing between value and effect edges.
34 UseIter iter = node->uses().begin();
35 while (iter != node->uses().end()) {
36 if (NodeProperties::IsEffectEdge(iter.edge())) {
37 DCHECK_NE(NULL, effect);
38 iter = iter.UpdateToAndIncrement(effect);
39 } else {
40 iter = iter.UpdateToAndIncrement(value);
41 }
42 }
43 }
44
45
46 class InlinerVisitor : public NullNodeVisitor {
47 public:
48 explicit InlinerVisitor(JSInliner* spec) : spec_(spec) {}
Michael Starzinger 2014/08/14 10:36:22 nit: s/spec/inliner/
sigurds 2014/08/19 11:31:19 Done.
49
50 GenericGraphVisit::Control Post(Node* node) {
51 switch (node->opcode()) {
52 case IrOpcode::kJSCallFunction:
53 spec_->TryInlineCall(node);
54 break;
55 default:
56 break;
57 }
58 return GenericGraphVisit::CONTINUE;
59 }
60
61 private:
62 JSInliner* spec_;
Michael Starzinger 2014/08/14 10:36:22 nit: s/spec_/inliner_/
sigurds 2014/08/19 11:31:19 Done.
63 };
64
65
66 void JSInliner::Inline() {
67 if (!info_->closure()->PassesFilter(FLAG_turbo_inlining_filter)) {
68 return;
69 }
70 InlinerVisitor visitor(this);
71 jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor);
72 }
73
74
75 static void MoveWithDependencies(Graph* graph, Node* node, Node* old_block,
76 Node* new_block) {
77 if (OperatorProperties::HasControlInput(node->op())) {
78 // Check if we have left the old_block.
79 if (NodeProperties::GetControlInput(node) != old_block) return;
80 // If not, move this node to the new_block.
81 NodeProperties::ReplaceControlInput(node, new_block);
82 }
83 // Independent of whether a node has a control input or not,
84 // it might have a dependency that is pinned to old_block.
85 for (InputIter iter = node->inputs().begin(); iter != node->inputs().end();
86 ++iter) {
87 if (NodeProperties::IsControlEdge(iter.edge())) continue;
88 MoveWithDependencies(graph, *iter, old_block, new_block);
89 }
90 }
91
92
93 static void MoveAllControlNodes(Node* from, Node* to) {
94 for (UseIter iter = from->uses().begin(); iter != from->uses().end();) {
95 if (NodeProperties::IsControlEdge(iter.edge())) {
96 iter.UpdateToAndIncrement(to);
97 } else {
98 ++iter;
99 }
100 }
101 }
102
103
104 // TODO(sigurds) Find a home for this function and reuse it everywhere (esp. in
105 // test cases, where similar code is currently duplicated).
106 static void Parse(Handle<JSFunction> function, CompilationInfoWithZone* info) {
107 CHECK(Parser::Parse(info));
108 StrictMode strict_mode = info->function()->strict_mode();
109 info->SetStrictMode(strict_mode);
110 info->SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
111 CHECK(Rewriter::Rewrite(info));
112 CHECK(Scope::Analyze(info));
113 CHECK_NE(NULL, info->scope());
114 Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope(), info->zone());
115 info->shared_info()->set_scope_info(*scope_info);
116 }
117
118
119 // A facade on a JSFunction's graph to facilitate inlining. It assumes the
120 // that the function graph has only one return statement, and provides
121 // {UnifyReturn} to convert a function graph to that end.
122 // InlineAtCall will create some new nodes using {graph}'s builders (and hence
123 // those nodes will live in {graph}'s zone.
124 class Inlinee {
125 public:
126 explicit Inlinee(JSGraph* graph) : jsgraph_(graph) {}
127
128 Graph* graph() { return jsgraph_->graph(); }
129 JSGraph* jsgraph() { return jsgraph_; }
130
131 // Returns the last regular control node, that is
132 // the last control node before the end node.
133 Node* end_block() { return NodeProperties::GetControlInput(unique_return()); }
134
135 // Return the effect output of the graph,
136 // that is the effect input of the return statement of the inlinee.
137 Node* effect_output() {
138 return NodeProperties::GetEffectInput(unique_return());
139 }
140 // Return the value output of the graph,
141 // that is the value input of the return statement of the inlinee.
142 Node* value_output() {
143 return NodeProperties::GetValueInput(unique_return(), 0);
144 }
145 // Return the unique return statement of the graph.
146 Node* unique_return() {
147 Node* unique_return =
148 NodeProperties::GetControlInput(jsgraph_->graph()->end());
149 DCHECK_EQ(IrOpcode::kReturn, unique_return->opcode());
150 return unique_return;
151 }
152 // Inline this graph at {call}, use {jsgraph} and its zone to create
153 // any new nodes.
154 void InlineAtCall(JSGraph* jsgraph, Node* call);
155 // Ensure that only a single return reaches the end node.
156 void UnifyReturn();
157
158 private:
159 JSGraph* jsgraph_;
160 };
161
162
163 void Inlinee::UnifyReturn() {
164 Graph* graph = jsgraph_->graph();
165
166 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0);
167 if (final_merge->opcode() == IrOpcode::kReturn) {
168 // nothing to do
169 return;
170 }
171 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode());
172
173 int predecessors =
174 OperatorProperties::GetControlInputCount(final_merge->op());
175 Operator* op_phi = jsgraph_->common()->Phi(predecessors);
176 Operator* op_ephi = jsgraph_->common()->EffectPhi(predecessors);
177
178 std::vector<Node*> values;
179 std::vector<Node*> effects;
180 // Iterate over all control flow predecessors,
181 // which must be return statements.
182 InputIter iter = final_merge->inputs().begin();
183 while (iter != final_merge->inputs().end()) {
184 Node* input = *iter;
185 switch (input->opcode()) {
186 case IrOpcode::kReturn:
187 values.push_back(NodeProperties::GetValueInput(input, 0));
188 effects.push_back(NodeProperties::GetEffectInput(input));
189 iter.UpdateToAndIncrement(NodeProperties::GetControlInput(input));
190 input->RemoveAllInputs();
191 break;
192 default:
193 UNREACHABLE();
194 ++iter;
195 break;
196 }
197 }
198 values.push_back(final_merge);
199 effects.push_back(final_merge);
200 Node* phi = graph->NewNode(op_phi, values.size(), values.data());
201 Node* ephi = graph->NewNode(op_ephi, effects.size(), effects.data());
202 Node* new_return =
203 graph->NewNode(jsgraph_->common()->Return(), phi, ephi, final_merge);
204 graph->end()->ReplaceInput(0, new_return);
205 }
206
207
208 void Inlinee::InlineAtCall(JSGraph* jsgraph, Node* call) {
209 MachineOperatorBuilder machine(jsgraph->zone());
210
211 Node* control = NodeProperties::GetControlInput(call);
212 // Move all the nodes to the end block.
213 MoveAllControlNodes(control, end_block());
214 // Now move the ones the call depends on back up.
215 // We have to do this back-and-forth to treat the case where the call is
216 // pinned to the start block.
217 MoveWithDependencies(graph(), call, end_block(), control);
218
219 // The inlinee uses the context from the JSFunction object. This will
220 // also be the effect dependency for the inlinee as it produces an effect.
221 Node* context = jsgraph->graph()->NewNode(
222 machine.Load(kMachineTagged), NodeProperties::GetValueInput(call, 0),
223 jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag),
224 NodeProperties::GetEffectInput(call));
225
226 // {inlinee_inputs} counts JSFunction, Receiver, arguments, context,
227 // but not effect, control.
228 int inlinee_inputs = graph()->start()->op()->OutputCount();
229 // Context is last argument.
230 int inlinee_context_index = inlinee_inputs - 1;
231 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not
232 // context, effect, control.
233 int inliner_inputs = OperatorProperties::GetValueInputCount(call->op());
234 // Iterate over all uses of the start node.
235 UseIter iter = graph()->start()->uses().begin();
236 while (iter != graph()->start()->uses().end()) {
237 Node* use = *iter;
238 switch (use->opcode()) {
239 case IrOpcode::kParameter: {
240 int index = 1 + static_cast<Operator1<int>*>(use->op())->parameter();
241 if (index < inliner_inputs && index < inlinee_context_index) {
242 // There is an input from the call, and the index is a value
243 // projection but not the context, so rewire the input.
244 ReplaceEffectfulWithValue(*iter, call->InputAt(index));
245 } else if (index == inlinee_context_index) {
246 // This is the context projection, rewire it to the context from the
247 // JSFunction object.
248 ReplaceEffectfulWithValue(*iter, context);
249 } else if (index < inlinee_context_index) {
250 // Call has fewer arguments than required, fill with undefined.
251 ReplaceEffectfulWithValue(*iter, jsgraph->UndefinedConstant());
252 } else {
253 // We got too many arguments, discard for now.
254 // TODO(sigurds): Fix to treat arguments array correctly.
255 }
256 ++iter;
257 break;
258 }
259 default:
260 if (NodeProperties::IsEffectEdge(iter.edge())) {
261 iter.UpdateToAndIncrement(context);
262 } else if (NodeProperties::IsControlEdge(iter.edge())) {
263 iter.UpdateToAndIncrement(control);
264 } else {
265 UNREACHABLE();
266 }
267 break;
268 }
269 }
270
271 // Iterate over all uses of the call node.
272 iter = call->uses().begin();
273 while (iter != call->uses().end()) {
274 if (NodeProperties::IsEffectEdge(iter.edge())) {
275 iter.UpdateToAndIncrement(effect_output());
276 } else if (NodeProperties::IsControlEdge(iter.edge())) {
277 UNREACHABLE();
278 } else {
279 DCHECK(NodeProperties::IsValueEdge(iter.edge()));
280 iter.UpdateToAndIncrement(value_output());
281 }
282 }
283 call->RemoveAllInputs();
284 DCHECK_EQ(0, call->UseCount());
285 // TODO(sigurds) Remove this once we copy.
286 unique_return()->RemoveAllInputs();
287 }
288
289
290 void JSInliner::TryInlineCall(Node* node) {
291 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
292
293 ValueMatcher<Handle<JSFunction> > match(node->InputAt(0));
294 if (!match.HasValue()) {
295 return;
296 }
297
298 Handle<JSFunction> function = match.Value();
299
300 if (function->shared()->native()) {
301 if (FLAG_trace_turbo_inlining) {
302 SmartArrayPointer<char> name =
303 function->shared()->DebugName()->ToCString();
304 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(),
305 info_->shared_info()->DebugName()->ToCString().get());
306 }
307 return;
308 }
309
310 CompilationInfoWithZone info(function);
311 Parse(function, &info);
312
313 if (info.scope()->arguments() != NULL) {
314 // For now do not inline functions that use their arguments array.
315 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString();
316 if (FLAG_trace_turbo_inlining) {
317 PrintF(
318 "Not Inlining %s into %s because inlinee uses arguments "
319 "array\n",
320 name.get(), info_->shared_info()->DebugName()->ToCString().get());
321 }
322 return;
323 }
324
325 if (FLAG_trace_turbo_inlining) {
326 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString();
327 PrintF("Inlining %s into %s\n", name.get(),
328 info_->shared_info()->DebugName()->ToCString().get());
329 }
330
331 Graph graph(info_->zone());
332 graph.SetNextNodeId(jsgraph_->graph()->NextNodeID());
333
334 Typer typer(info_->zone());
335 CommonOperatorBuilder common(info_->zone());
336 JSGraph jsgraph(&graph, &common, &typer);
337
338 AstGraphBuilder graph_builder(&info, &jsgraph);
339 graph_builder.CreateGraph();
340
341 Inlinee inlinee(&jsgraph);
342 inlinee.UnifyReturn();
343 inlinee.InlineAtCall(jsgraph_, node);
344
345 jsgraph_->graph()->SetNextNodeId(inlinee.graph()->NextNodeID());
346 }
347 }
348 }
349 } // namespace v8::internal::compiler
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698