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