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