OLD | NEW |
---|---|
(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 | |
OLD | NEW |