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

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: 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 "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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698