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 "js-inlining.h" | |
Michael Starzinger
2014/08/11 18:42:04
nit: Use full include path here.
sigurds
2014/08/12 12:29:30
Done.
| |
6 | |
7 #include "src/compiler/ast-graph-builder.h" | |
8 #include "src/compiler/common-operator.h" | |
9 #include "src/compiler/generic-node-inl.h" | |
10 #include "src/compiler/graph-inl.h" | |
11 #include "src/compiler/graph-visualizer.h" | |
12 #include "src/compiler/js-operator.h" | |
13 #include "src/compiler/node-aux-data-inl.h" | |
14 #include "src/compiler/node-matchers.h" | |
15 #include "src/compiler/node-properties-inl.h" | |
16 #include "src/compiler/simplified-operator.h" | |
17 #include "src/compiler/typer.h" | |
18 #include "src/parser.h" | |
19 #include "src/rewriter.h" | |
20 #include "src/scopes.h" | |
21 | |
22 | |
23 namespace v8 { | |
24 namespace internal { | |
25 namespace compiler { | |
26 | |
27 // TODO(titzer): factor this out to a common routine with js-typed-lowering. | |
28 static void ReplaceEffectfulWithValue(Node* node, Node* value) { | |
29 Node* effect = NULL; | |
30 if (OperatorProperties::HasEffectInput(node->op())) { | |
31 effect = NodeProperties::GetEffectInput(node); | |
32 } | |
33 | |
34 // Requires distinguishing between value and effect edges. | |
35 UseIter iter = node->uses().begin(); | |
36 while (iter != node->uses().end()) { | |
37 if (NodeProperties::IsEffectEdge(iter.edge())) { | |
38 DCHECK_NE(NULL, effect); | |
39 iter = iter.UpdateToAndIncrement(effect); | |
40 } else { | |
41 iter = iter.UpdateToAndIncrement(value); | |
42 } | |
43 } | |
44 } | |
45 | |
46 | |
47 class InlinerVisitor : public NullNodeVisitor { | |
48 public: | |
49 explicit InlinerVisitor(JSInliner* spec) : spec_(spec) {} | |
50 | |
51 GenericGraphVisit::Control Post(Node* node) { | |
52 switch (node->opcode()) { | |
53 case IrOpcode::kJSCallFunction: | |
54 spec_->InlineCall(node); | |
55 break; | |
56 default: | |
57 break; | |
58 } | |
59 return GenericGraphVisit::CONTINUE; | |
60 } | |
61 | |
62 private: | |
63 JSInliner* spec_; | |
64 }; | |
65 | |
66 | |
67 void JSInliner::Inline() { | |
68 if (!info_->closure()->PassesFilter(FLAG_turbo_inlining_filter)) { | |
69 return; | |
70 } | |
71 InlinerVisitor visitor(this); | |
72 jsgraph_->graph()->VisitNodeInputsFromEnd(&visitor); | |
73 } | |
74 | |
75 | |
76 /// Ensure that only a single return reaches the end node. | |
77 void JSInliner::UnifyReturn(JSGraph* jsgraph) { | |
78 Graph* graph = jsgraph->graph(); | |
79 | |
80 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); | |
81 if (final_merge->opcode() == IrOpcode::kReturn) { | |
82 // nothing to do | |
83 return; | |
84 } | |
85 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); | |
86 | |
87 int predecessors = | |
88 OperatorProperties::GetControlInputCount(final_merge->op()); | |
89 Operator* op_phi = jsgraph->common()->Phi(predecessors); | |
90 Operator* op_ephi = jsgraph->common()->EffectPhi(predecessors); | |
91 | |
92 std::vector<Node*> values; | |
93 std::vector<Node*> effects; | |
94 // Iterate over all control flow predecessors, | |
95 // which must be return statements. | |
96 InputIter iter = final_merge->inputs().begin(); | |
97 while (iter != final_merge->inputs().end()) { | |
98 Node* input = *iter; | |
99 switch (input->opcode()) { | |
100 case IrOpcode::kReturn: | |
101 values.push_back(NodeProperties::GetValueInput(input, 0)); | |
102 effects.push_back(NodeProperties::GetEffectInput(input)); | |
103 iter.UpdateToAndIncrement(NodeProperties::GetControlInput(input)); | |
104 input->RemoveAllInputs(); | |
105 break; | |
106 default: | |
107 UNREACHABLE(); | |
108 ++iter; | |
109 break; | |
110 } | |
111 } | |
112 values.push_back(final_merge); | |
113 effects.push_back(final_merge); | |
114 Node* phi = graph->NewNode(op_phi, values.size(), values.data()); | |
115 Node* ephi = graph->NewNode(op_ephi, effects.size(), effects.data()); | |
116 Node* new_return = | |
117 graph->NewNode(jsgraph->common()->Return(), phi, ephi, final_merge); | |
118 graph->end()->ReplaceInput(0, new_return); | |
119 } | |
120 | |
121 | |
122 void moveWithDependencies(JSGraph* graph, Node* node, Node* old_block, | |
123 Node* new_block) { | |
124 if (OperatorProperties::HasControlInput(node->op())) { | |
125 // Check if we have left the old_block. | |
126 if (NodeProperties::GetControlInput(node) != old_block) return; | |
127 // If not, move this node to the new_block. | |
128 NodeProperties::ReplaceControlInput(node, new_block); | |
129 } | |
130 // Independent of whether a node has a control input or not, | |
131 // it might have a dependency that is pinned to old_block. | |
132 for (InputIter iter = node->inputs().begin(); iter != node->inputs().end(); | |
133 ++iter) { | |
134 if (NodeProperties::IsControlEdge(iter.edge())) continue; | |
135 moveWithDependencies(graph, *iter, old_block, new_block); | |
136 } | |
137 } | |
138 | |
139 | |
140 static bool IsStackGuard(Node* node) { | |
141 if (node->opcode() != IrOpcode::kJSCallRuntime) return false; | |
142 Runtime::FunctionId id = OpParameter<Runtime::FunctionId>(node); | |
143 return id == Runtime::kStackGuard; | |
144 } | |
145 | |
146 | |
147 static void RemoveStackGuard(Node* stackGuard) { | |
148 DCHECK(IsStackGuard(stackGuard)); | |
149 DCHECK_EQ(1, stackGuard->UseCount()); | |
150 NodeProperties::ReplaceEffectInput( | |
151 stackGuard->UseAt(0), NodeProperties::GetEffectInput(stackGuard)); | |
152 stackGuard->RemoveAllInputs(); | |
153 } | |
154 | |
155 | |
156 static void moveAllControlNodes(Node* from, Node* to) { | |
157 for (UseIter iter = from->uses().begin(); iter != from->uses().end();) { | |
158 if (NodeProperties::IsControlEdge(iter.edge())) { | |
159 iter.UpdateToAndIncrement(to); | |
160 } else { | |
161 ++iter; | |
162 } | |
163 } | |
164 } | |
165 | |
166 | |
167 void DoInline(Zone* zone, Node* call, CompilationInfo* info, JSGraph* jsgraph) { | |
168 Graph* graph = jsgraph->graph(); | |
169 MachineOperatorBuilder machine(zone); | |
170 | |
171 Node* control = NodeProperties::GetControlInput(call); | |
172 Node* unified_return = NodeProperties::GetControlInput(graph->end()); | |
173 DCHECK_EQ(IrOpcode::kReturn, unified_return->opcode()); | |
174 Node* inlined_control = NodeProperties::GetControlInput(unified_return); | |
175 Node* bottom_block = inlined_control; | |
176 // Move all the nodes to the bottom block. | |
177 moveAllControlNodes(control, bottom_block); | |
178 // Now move the ones the call depends on back up. | |
179 // We have to do this back-and-forth to treat the case where the call is | |
180 // pinned to the start block. | |
181 moveWithDependencies(jsgraph, call, bottom_block, control); | |
182 | |
183 // The inlinee uses the context from the JSFunction object. | |
184 Node* context = graph->NewNode( | |
185 machine.Load(kMachineTagged), NodeProperties::GetValueInput(call, 0), | |
186 jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag), | |
187 NodeProperties::GetEffectInput(call)); | |
188 | |
189 Node* stackGuard = NULL; | |
190 | |
191 // {inlinee_inputs} counts JSFunction, Receiver, arguments, context, | |
192 // but not effect, control. | |
193 int inlinee_inputs = graph->start()->op()->OutputCount(); | |
194 // Context is last argument. | |
195 int inlinee_context_index = inlinee_inputs - 1; | |
196 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not | |
197 // context, effect, control. | |
198 int inliner_inputs = OperatorProperties::GetValueInputCount(call->op()); | |
199 // Iterate over all uses of the start node. | |
200 UseIter iter = graph->start()->uses().begin(); | |
201 while (iter != graph->start()->uses().end()) { | |
202 Node* use = *iter; | |
203 switch (use->opcode()) { | |
204 case IrOpcode::kParameter: { | |
205 int index = 1 + static_cast<Operator1<int>*>(use->op())->parameter(); | |
206 if (index < inliner_inputs && index < inlinee_context_index) { | |
207 // There is an input from the call, and the index is a value | |
208 // projection but not the context, so rewire the input. | |
209 ReplaceEffectfulWithValue(*iter, call->InputAt(index)); | |
210 } else if (index == inlinee_context_index) { | |
211 // This is the context projection, rewire it to the context from the | |
212 // JSFunction object. | |
213 ReplaceEffectfulWithValue(*iter, context); | |
214 } else if (index < inlinee_context_index) { | |
215 // Call has fewer arguments than required, fill with undefined. | |
216 ReplaceEffectfulWithValue(*iter, jsgraph->UndefinedConstant()); | |
217 } else { | |
218 // We got too many arguments, discard for now. | |
219 // TODO(sigurds): Fix to treat arguments array correctly. | |
220 } | |
221 ++iter; | |
222 break; | |
223 } | |
224 default: | |
225 if (NodeProperties::IsEffectEdge(iter.edge())) { | |
226 iter.UpdateToAndIncrement(context); | |
227 stackGuard = use; | |
228 } else if (NodeProperties::IsControlEdge(iter.edge())) { | |
229 iter.UpdateToAndIncrement(control); | |
230 } else { | |
231 UNREACHABLE(); | |
232 } | |
233 break; | |
234 } | |
235 } | |
236 | |
237 // TODO(sigurds) Do not make assumptions about position of stack guard. | |
238 if (IsStackGuard(stackGuard)) { | |
239 RemoveStackGuard(stackGuard); | |
240 } | |
241 | |
242 // Iterate over all uses of the call node. | |
243 iter = call->uses().begin(); | |
244 while (iter != call->uses().end()) { | |
245 if (NodeProperties::IsEffectEdge(iter.edge())) { | |
246 iter.UpdateToAndIncrement(NodeProperties::GetEffectInput(unified_return)); | |
247 } else if (NodeProperties::IsControlEdge(iter.edge())) { | |
248 UNREACHABLE(); | |
249 } else { | |
250 DCHECK(NodeProperties::IsValueEdge(iter.edge())); | |
251 iter.UpdateToAndIncrement( | |
252 NodeProperties::GetValueInput(unified_return, 0)); | |
253 } | |
254 } | |
255 call->RemoveAllInputs(); | |
256 unified_return->RemoveAllInputs(); | |
257 } | |
258 | |
259 | |
260 void JSInliner::InlineCall(Node* node) { | |
261 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | |
262 | |
263 ValueMatcher<Handle<JSFunction> > match(node->InputAt(0)); | |
264 if (!match.HasValue()) { | |
265 return; | |
266 } | |
267 | |
268 Handle<JSFunction> function = match.Value(); | |
269 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | |
270 | |
271 if (function->shared()->native()) { | |
272 if (FLAG_trace_turbo_inlining) { | |
273 printf("Not Inlining %s into %s because inlinee is native\n", name.get(), | |
274 info_->shared_info()->DebugName()->ToCString().get()); | |
275 } | |
276 return; | |
277 } | |
278 | |
279 CompilationInfoWithZone info(function); | |
280 | |
281 CHECK(Parser::Parse(&info)); | |
282 StrictMode strict_mode = info.function()->strict_mode(); | |
283 info.SetStrictMode(strict_mode); | |
284 info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code())); | |
285 CHECK(Rewriter::Rewrite(&info)); | |
286 CHECK(Scope::Analyze(&info)); | |
287 CHECK_NE(NULL, info.scope()); | |
288 | |
289 if (info.scope()->arguments() != NULL) { | |
290 // For now do not inline functions that use their arguments array. | |
291 if (FLAG_trace_turbo_inlining) { | |
292 printf( | |
293 "Not Inlining %s into %s because inlinee uses arguments " | |
294 "array\n", | |
295 name.get(), info_->shared_info()->DebugName()->ToCString().get()); | |
296 } | |
297 return; | |
298 } | |
299 | |
300 if (!info.shared_info().is_null()) { | |
301 Handle<ScopeInfo> scope_info = ScopeInfo::Create(info.scope(), info.zone()); | |
302 info.shared_info()->set_scope_info(*scope_info); | |
303 } | |
304 if (FLAG_trace_turbo_inlining) { | |
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()->NodeCount()); | |
311 Typer typer(info_->zone()); | |
312 CommonOperatorBuilder common(info_->zone()); | |
313 JSGraph jsgraph(&graph, &common, &typer); | |
314 AstGraphBuilder graph_builder(&info, &jsgraph); | |
315 graph_builder.CreateGraph(); | |
316 | |
317 UnifyReturn(&jsgraph); | |
318 | |
319 DoInline(jsgraph_->zone(), node, &info, &jsgraph); | |
320 | |
321 jsgraph_->graph()->SetNextNodeId(graph.NodeCount()); | |
322 } | |
323 } | |
324 } | |
325 } // namespace v8::internal::compiler | |
OLD | NEW |