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

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

Issue 453833003: 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
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 // 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
OLDNEW
« no previous file with comments | « src/compiler/js-inlining.h ('k') | src/compiler/pipeline.cc » ('j') | src/compiler/pipeline.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698