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

Side by Side Diff: src/compiler/js-inlining.cc

Issue 484083003: Reland "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
« no previous file with comments | « src/compiler/js-inlining.h ('k') | src/compiler/js-typed-lowering.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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 NodeVector values(NodeVector::allocator_type(jsgraph_->zone()));
156 NodeVector effects(NodeVector::allocator_type(jsgraph_->zone()));
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 =
178 graph->NewNode(op_phi, static_cast<int>(values.size()), &values.front());
179 Node* ephi = graph->NewNode(op_ephi, static_cast<int>(effects.size()),
180 &effects.front());
181 Node* new_return =
182 graph->NewNode(jsgraph_->common()->Return(), phi, ephi, final_merge);
183 graph->end()->ReplaceInput(0, new_return);
184 }
185
186
187 void Inlinee::InlineAtCall(JSGraph* jsgraph, Node* call) {
188 MachineOperatorBuilder machine(jsgraph->zone());
189
190 Node* control = NodeProperties::GetControlInput(call);
191 // Move all the nodes to the end block.
192 MoveAllControlNodes(control, end_block());
193 // Now move the ones the call depends on back up.
194 // We have to do this back-and-forth to treat the case where the call is
195 // pinned to the start block.
196 MoveWithDependencies(graph(), call, end_block(), control);
197
198 // The inlinee uses the context from the JSFunction object. This will
199 // also be the effect dependency for the inlinee as it produces an effect.
200 // TODO(sigurds) Use simplified load once it is ready.
201 Node* context = jsgraph->graph()->NewNode(
202 machine.Load(kMachAnyTagged), NodeProperties::GetValueInput(call, 0),
203 jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag),
204 NodeProperties::GetEffectInput(call));
205
206 // {inlinee_inputs} counts JSFunction, Receiver, arguments, context,
207 // but not effect, control.
208 int inlinee_inputs = graph()->start()->op()->OutputCount();
209 // Context is last argument.
210 int inlinee_context_index = inlinee_inputs - 1;
211 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not
212 // context, effect, control.
213 int inliner_inputs = OperatorProperties::GetValueInputCount(call->op());
214 // Iterate over all uses of the start node.
215 UseIter iter = graph()->start()->uses().begin();
216 while (iter != graph()->start()->uses().end()) {
217 Node* use = *iter;
218 switch (use->opcode()) {
219 case IrOpcode::kParameter: {
220 int index = 1 + static_cast<Operator1<int>*>(use->op())->parameter();
221 if (index < inliner_inputs && index < inlinee_context_index) {
222 // There is an input from the call, and the index is a value
223 // projection but not the context, so rewire the input.
224 NodeProperties::ReplaceWithValue(*iter, call->InputAt(index));
225 } else if (index == inlinee_context_index) {
226 // This is the context projection, rewire it to the context from the
227 // JSFunction object.
228 NodeProperties::ReplaceWithValue(*iter, context);
229 } else if (index < inlinee_context_index) {
230 // Call has fewer arguments than required, fill with undefined.
231 NodeProperties::ReplaceWithValue(*iter, jsgraph->UndefinedConstant());
232 } else {
233 // We got too many arguments, discard for now.
234 // TODO(sigurds): Fix to treat arguments array correctly.
235 }
236 ++iter;
237 break;
238 }
239 default:
240 if (NodeProperties::IsEffectEdge(iter.edge())) {
241 iter.UpdateToAndIncrement(context);
242 } else if (NodeProperties::IsControlEdge(iter.edge())) {
243 iter.UpdateToAndIncrement(control);
244 } else {
245 UNREACHABLE();
246 }
247 break;
248 }
249 }
250
251 // Iterate over all uses of the call node.
252 iter = call->uses().begin();
253 while (iter != call->uses().end()) {
254 if (NodeProperties::IsEffectEdge(iter.edge())) {
255 iter.UpdateToAndIncrement(effect_output());
256 } else if (NodeProperties::IsControlEdge(iter.edge())) {
257 UNREACHABLE();
258 } else {
259 DCHECK(NodeProperties::IsValueEdge(iter.edge()));
260 iter.UpdateToAndIncrement(value_output());
261 }
262 }
263 call->RemoveAllInputs();
264 DCHECK_EQ(0, call->UseCount());
265 // TODO(sigurds) Remove this once we copy.
266 unique_return()->RemoveAllInputs();
267 }
268
269
270 void JSInliner::TryInlineCall(Node* node) {
271 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
272
273 ValueMatcher<Handle<JSFunction> > match(node->InputAt(0));
274 if (!match.HasValue()) {
275 return;
276 }
277
278 Handle<JSFunction> function = match.Value();
279
280 if (function->shared()->native()) {
281 if (FLAG_trace_turbo_inlining) {
282 SmartArrayPointer<char> name =
283 function->shared()->DebugName()->ToCString();
284 PrintF("Not Inlining %s into %s because inlinee is native\n", name.get(),
285 info_->shared_info()->DebugName()->ToCString().get());
286 }
287 return;
288 }
289
290 CompilationInfoWithZone info(function);
291 Parse(function, &info);
292
293 if (info.scope()->arguments() != NULL) {
294 // For now do not inline functions that use their arguments array.
295 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString();
296 if (FLAG_trace_turbo_inlining) {
297 PrintF(
298 "Not Inlining %s into %s because inlinee uses arguments "
299 "array\n",
300 name.get(), info_->shared_info()->DebugName()->ToCString().get());
301 }
302 return;
303 }
304
305 if (FLAG_trace_turbo_inlining) {
306 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString();
307 PrintF("Inlining %s into %s\n", name.get(),
308 info_->shared_info()->DebugName()->ToCString().get());
309 }
310
311 Graph graph(info_->zone());
312 graph.SetNextNodeId(jsgraph_->graph()->NextNodeID());
313
314 Typer typer(info_->zone());
315 CommonOperatorBuilder common(info_->zone());
316 JSGraph jsgraph(&graph, &common, &typer);
317
318 AstGraphBuilder graph_builder(&info, &jsgraph);
319 graph_builder.CreateGraph();
320
321 Inlinee inlinee(&jsgraph);
322 inlinee.UnifyReturn();
323 inlinee.InlineAtCall(jsgraph_, node);
324
325 jsgraph_->graph()->SetNextNodeId(inlinee.graph()->NextNodeID());
326 }
327 }
328 }
329 } // namespace v8::internal::compiler
OLDNEW
« no previous file with comments | « src/compiler/js-inlining.h ('k') | src/compiler/js-typed-lowering.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698