OLD | NEW |
---|---|
1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/ast-graph-builder.h" | 5 #include "src/compiler/ast-graph-builder.h" |
6 #include "src/compiler/common-operator.h" | 6 #include "src/compiler/common-operator.h" |
7 #include "src/compiler/generic-node-inl.h" | 7 #include "src/compiler/generic-node-inl.h" |
8 #include "src/compiler/graph-inl.h" | 8 #include "src/compiler/graph-inl.h" |
9 #include "src/compiler/graph-visualizer.h" | 9 #include "src/compiler/graph-visualizer.h" |
10 #include "src/compiler/js-inlining.h" | 10 #include "src/compiler/js-inlining.h" |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
60 CHECK(Scope::Analyze(info)); | 60 CHECK(Scope::Analyze(info)); |
61 CHECK_NE(NULL, info->scope()); | 61 CHECK_NE(NULL, info->scope()); |
62 Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope(), info->zone()); | 62 Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope(), info->zone()); |
63 info->shared_info()->set_scope_info(*scope_info); | 63 info->shared_info()->set_scope_info(*scope_info); |
64 } | 64 } |
65 | 65 |
66 | 66 |
67 // A facade on a JSFunction's graph to facilitate inlining. It assumes the | 67 // A facade on a JSFunction's graph to facilitate inlining. It assumes the |
68 // that the function graph has only one return statement, and provides | 68 // that the function graph has only one return statement, and provides |
69 // {UnifyReturn} to convert a function graph to that end. | 69 // {UnifyReturn} to convert a function graph to that end. |
70 // InlineAtCall will create some new nodes using {graph}'s builders (and hence | 70 // InlineAtCall will create some new nodes using {graph}'s builders (and hence |
Michael Starzinger
2014/09/09 11:52:15
nit: The last sentence of the comment is slightly
sigurds
2014/09/15 10:29:09
Done.
| |
71 // those nodes will live in {graph}'s zone. | 71 // those nodes will live in {graph}'s zone. |
72 class Inlinee { | 72 class Inlinee { |
73 public: | 73 public: |
74 explicit Inlinee(JSGraph* graph) : jsgraph_(graph) {} | 74 Inlinee(Node* start, Node* end) : start_(start), end_(end) {} |
75 | |
76 Graph* graph() { return jsgraph_->graph(); } | |
77 JSGraph* jsgraph() { return jsgraph_; } | |
78 | 75 |
79 // Returns the last regular control node, that is | 76 // Returns the last regular control node, that is |
80 // the last control node before the end node. | 77 // the last control node before the end node. |
81 Node* end_block() { return NodeProperties::GetControlInput(unique_return()); } | 78 Node* end_block() { return NodeProperties::GetControlInput(unique_return()); } |
82 | 79 |
83 // Return the effect output of the graph, | 80 // Return the effect output of the graph, |
84 // that is the effect input of the return statement of the inlinee. | 81 // that is the effect input of the return statement of the inlinee. |
85 Node* effect_output() { | 82 Node* effect_output() { |
86 return NodeProperties::GetEffectInput(unique_return()); | 83 return NodeProperties::GetEffectInput(unique_return()); |
87 } | 84 } |
88 // Return the value output of the graph, | 85 // Return the value output of the graph, |
89 // that is the value input of the return statement of the inlinee. | 86 // that is the value input of the return statement of the inlinee. |
90 Node* value_output() { | 87 Node* value_output() { |
91 return NodeProperties::GetValueInput(unique_return(), 0); | 88 return NodeProperties::GetValueInput(unique_return(), 0); |
92 } | 89 } |
93 // Return the unique return statement of the graph. | 90 // Return the unique return statement of the graph. |
94 Node* unique_return() { | 91 Node* unique_return() { |
95 Node* unique_return = | 92 Node* unique_return = NodeProperties::GetControlInput(end_); |
96 NodeProperties::GetControlInput(jsgraph_->graph()->end()); | |
97 DCHECK_EQ(IrOpcode::kReturn, unique_return->opcode()); | 93 DCHECK_EQ(IrOpcode::kReturn, unique_return->opcode()); |
98 return unique_return; | 94 return unique_return; |
99 } | 95 } |
100 // Inline this graph at {call}, use {jsgraph} and its zone to create | 96 // Inline this graph at {call}, use {jsgraph} and its zone to create |
101 // any new nodes. | 97 // any new nodes. |
102 void InlineAtCall(JSGraph* jsgraph, Node* call); | 98 void InlineAtCall(JSGraph* jsgraph, Node* call); |
99 | |
103 // Ensure that only a single return reaches the end node. | 100 // Ensure that only a single return reaches the end node. |
104 void UnifyReturn(); | 101 static void UnifyReturn(JSGraph* jsgraph); |
105 | 102 |
106 private: | 103 private: |
107 JSGraph* jsgraph_; | 104 Node* start_; |
105 Node* end_; | |
108 }; | 106 }; |
109 | 107 |
110 | 108 |
111 void Inlinee::UnifyReturn() { | 109 void Inlinee::UnifyReturn(JSGraph* jsgraph) { |
112 Graph* graph = jsgraph_->graph(); | 110 Graph* graph = jsgraph->graph(); |
113 | 111 |
114 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); | 112 Node* final_merge = NodeProperties::GetControlInput(graph->end(), 0); |
115 if (final_merge->opcode() == IrOpcode::kReturn) { | 113 if (final_merge->opcode() == IrOpcode::kReturn) { |
116 // nothing to do | 114 // nothing to do |
117 return; | 115 return; |
118 } | 116 } |
119 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); | 117 DCHECK_EQ(IrOpcode::kMerge, final_merge->opcode()); |
120 | 118 |
121 int predecessors = | 119 int predecessors = |
122 OperatorProperties::GetControlInputCount(final_merge->op()); | 120 OperatorProperties::GetControlInputCount(final_merge->op()); |
123 Operator* op_phi = jsgraph_->common()->Phi(kMachAnyTagged, predecessors); | 121 Operator* op_phi = jsgraph->common()->Phi(kMachAnyTagged, predecessors); |
124 Operator* op_ephi = jsgraph_->common()->EffectPhi(predecessors); | 122 Operator* op_ephi = jsgraph->common()->EffectPhi(predecessors); |
125 | 123 |
126 NodeVector values(jsgraph_->zone()); | 124 NodeVector values(jsgraph->zone()); |
127 NodeVector effects(jsgraph_->zone()); | 125 NodeVector effects(jsgraph->zone()); |
128 // Iterate over all control flow predecessors, | 126 // Iterate over all control flow predecessors, |
129 // which must be return statements. | 127 // which must be return statements. |
130 InputIter iter = final_merge->inputs().begin(); | 128 InputIter iter = final_merge->inputs().begin(); |
131 while (iter != final_merge->inputs().end()) { | 129 while (iter != final_merge->inputs().end()) { |
132 Node* input = *iter; | 130 Node* input = *iter; |
133 switch (input->opcode()) { | 131 switch (input->opcode()) { |
134 case IrOpcode::kReturn: | 132 case IrOpcode::kReturn: |
135 values.push_back(NodeProperties::GetValueInput(input, 0)); | 133 values.push_back(NodeProperties::GetValueInput(input, 0)); |
136 effects.push_back(NodeProperties::GetEffectInput(input)); | 134 effects.push_back(NodeProperties::GetEffectInput(input)); |
137 iter.UpdateToAndIncrement(NodeProperties::GetControlInput(input)); | 135 iter.UpdateToAndIncrement(NodeProperties::GetControlInput(input)); |
138 input->RemoveAllInputs(); | 136 input->RemoveAllInputs(); |
139 break; | 137 break; |
140 default: | 138 default: |
141 UNREACHABLE(); | 139 UNREACHABLE(); |
142 ++iter; | 140 ++iter; |
143 break; | 141 break; |
144 } | 142 } |
145 } | 143 } |
146 values.push_back(final_merge); | 144 values.push_back(final_merge); |
147 effects.push_back(final_merge); | 145 effects.push_back(final_merge); |
148 Node* phi = | 146 Node* phi = |
149 graph->NewNode(op_phi, static_cast<int>(values.size()), &values.front()); | 147 graph->NewNode(op_phi, static_cast<int>(values.size()), &values.front()); |
150 Node* ephi = graph->NewNode(op_ephi, static_cast<int>(effects.size()), | 148 Node* ephi = graph->NewNode(op_ephi, static_cast<int>(effects.size()), |
151 &effects.front()); | 149 &effects.front()); |
152 Node* new_return = | 150 Node* new_return = |
153 graph->NewNode(jsgraph_->common()->Return(), phi, ephi, final_merge); | 151 graph->NewNode(jsgraph->common()->Return(), phi, ephi, final_merge); |
154 graph->end()->ReplaceInput(0, new_return); | 152 graph->end()->ReplaceInput(0, new_return); |
155 } | 153 } |
156 | 154 |
157 | 155 |
156 class CopyVisitor : public NullNodeVisitor { | |
157 public: | |
158 CopyVisitor(Graph* source_graph, JSGraph* target_graph, Zone* temp_zone) | |
159 : copies_(source_graph->NodeCount(), NULL, temp_zone), | |
160 sentinels_(source_graph->NodeCount(), NULL, temp_zone), | |
161 source_graph_(source_graph), | |
162 target_graph_(target_graph), | |
163 temp_zone_(temp_zone) {} | |
164 | |
165 GenericGraphVisit::Control Post(Node* original) { | |
166 NodeVector inputs(temp_zone_); | |
167 for (InputIter it = original->inputs().begin(); | |
168 it != original->inputs().end(); ++it) { | |
169 inputs.push_back(GetCopy(*it)); | |
170 } | |
171 | |
172 // Reuse the operator in the copy. This assumes that op lives in a zone | |
173 // that lives longer than graph()'s zone. | |
174 Node* copy = target_graph_->graph()->NewNode(original->op(), inputs.size(), | |
175 &inputs.front()); | |
176 copies_[original->id()] = copy; | |
177 return GenericGraphVisit::CONTINUE; | |
178 } | |
179 | |
180 Node* GetCopy(Node* original) { | |
181 Node* copy = copies_[original->id()]; | |
182 if (copy == NULL) { | |
183 copy = GetSentinel(original); | |
184 } | |
185 return copy; | |
186 } | |
187 | |
188 void CopyGraph() { | |
189 source_graph_->VisitNodeInputsFromEnd(this); | |
190 ReplaceSentinels(); | |
191 } | |
192 | |
193 const NodeVector& copies() { return copies_; } | |
194 | |
195 private: | |
196 void ReplaceSentinels() { | |
197 for (int id = 0; id < source_graph_->NodeCount(); ++id) { | |
198 Node* sentinel = sentinels_[id]; | |
199 if (sentinel == NULL) continue; | |
200 Node* copy = copies_[id]; | |
201 DCHECK_NE(NULL, copy); | |
202 sentinel->ReplaceUses(copy); | |
203 } | |
204 } | |
205 | |
206 Node* GetSentinel(Node* original) { | |
207 Node* sentinel = sentinels_[original->id()]; | |
208 if (sentinel == NULL) { | |
209 // Create a dummy node we can replace later. | |
210 sentinel = target_graph_->graph()->NewNode( | |
211 target_graph_->common()->Int32Constant(0)); | |
212 } | |
213 return sentinel; | |
214 } | |
215 | |
216 NodeVector copies_; | |
217 NodeVector sentinels_; | |
218 Graph* source_graph_; | |
219 JSGraph* target_graph_; | |
Michael Starzinger
2014/09/09 11:52:15
I think the target_graph_ should be of type "Graph
sigurds
2014/09/15 10:29:09
Done.
| |
220 Zone* temp_zone_; | |
221 }; | |
222 | |
223 | |
158 void Inlinee::InlineAtCall(JSGraph* jsgraph, Node* call) { | 224 void Inlinee::InlineAtCall(JSGraph* jsgraph, Node* call) { |
159 MachineOperatorBuilder machine(jsgraph->zone()); | |
160 | |
161 // The scheduler is smart enough to place our code; we just ensure {control} | 225 // The scheduler is smart enough to place our code; we just ensure {control} |
162 // becomes the control input of the start of the inlinee. | 226 // becomes the control input of the start of the inlinee. |
163 Node* control = NodeProperties::GetControlInput(call); | 227 Node* control = NodeProperties::GetControlInput(call); |
164 | 228 |
165 // The inlinee uses the context from the JSFunction object. This will | 229 // The inlinee uses the context from the JSFunction object. This will |
166 // also be the effect dependency for the inlinee as it produces an effect. | 230 // also be the effect dependency for the inlinee as it produces an effect. |
167 // TODO(sigurds) Use simplified load once it is ready. | 231 // TODO(sigurds) Use simplified load once it is ready. |
168 Node* context = jsgraph->graph()->NewNode( | 232 Node* context = jsgraph->graph()->NewNode( |
169 machine.Load(kMachAnyTagged), NodeProperties::GetValueInput(call, 0), | 233 jsgraph->machine()->Load(kMachAnyTagged), |
234 NodeProperties::GetValueInput(call, 0), | |
170 jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag), | 235 jsgraph->Int32Constant(JSFunction::kContextOffset - kHeapObjectTag), |
171 NodeProperties::GetEffectInput(call)); | 236 NodeProperties::GetEffectInput(call)); |
172 | 237 |
173 // {inlinee_inputs} counts JSFunction, Receiver, arguments, context, | 238 // {inlinee_inputs} counts JSFunction, Receiver, arguments, context, |
174 // but not effect, control. | 239 // but not effect, control. |
175 int inlinee_inputs = graph()->start()->op()->OutputCount(); | 240 int inlinee_inputs = start_->op()->OutputCount(); |
176 // Context is last argument. | 241 // Context is last argument. |
177 int inlinee_context_index = inlinee_inputs - 1; | 242 int inlinee_context_index = inlinee_inputs - 1; |
178 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not | 243 // {inliner_inputs} counts JSFunction, Receiver, arguments, but not |
179 // context, effect, control. | 244 // context, effect, control. |
180 int inliner_inputs = OperatorProperties::GetValueInputCount(call->op()); | 245 int inliner_inputs = OperatorProperties::GetValueInputCount(call->op()); |
181 // Iterate over all uses of the start node. | 246 // Iterate over all uses of the start node. |
182 UseIter iter = graph()->start()->uses().begin(); | 247 UseIter iter = start_->uses().begin(); |
183 while (iter != graph()->start()->uses().end()) { | 248 while (iter != start_->uses().end()) { |
184 Node* use = *iter; | 249 Node* use = *iter; |
185 switch (use->opcode()) { | 250 switch (use->opcode()) { |
186 case IrOpcode::kParameter: { | 251 case IrOpcode::kParameter: { |
187 int index = 1 + OpParameter<int>(use->op()); | 252 int index = 1 + OpParameter<int>(use->op()); |
188 if (index < inliner_inputs && index < inlinee_context_index) { | 253 if (index < inliner_inputs && index < inlinee_context_index) { |
189 // There is an input from the call, and the index is a value | 254 // There is an input from the call, and the index is a value |
190 // projection but not the context, so rewire the input. | 255 // projection but not the context, so rewire the input. |
191 NodeProperties::ReplaceWithValue(*iter, call->InputAt(index)); | 256 NodeProperties::ReplaceWithValue(*iter, call->InputAt(index)); |
192 } else if (index == inlinee_context_index) { | 257 } else if (index == inlinee_context_index) { |
193 // This is the context projection, rewire it to the context from the | 258 // This is the context projection, rewire it to the context from the |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
227 iter.UpdateToAndIncrement(value_output()); | 292 iter.UpdateToAndIncrement(value_output()); |
228 } | 293 } |
229 } | 294 } |
230 call->RemoveAllInputs(); | 295 call->RemoveAllInputs(); |
231 DCHECK_EQ(0, call->UseCount()); | 296 DCHECK_EQ(0, call->UseCount()); |
232 // TODO(sigurds) Remove this once we copy. | 297 // TODO(sigurds) Remove this once we copy. |
233 unique_return()->RemoveAllInputs(); | 298 unique_return()->RemoveAllInputs(); |
234 } | 299 } |
235 | 300 |
236 | 301 |
237 void JSInliner::TryInlineCall(Node* node) { | 302 void JSInliner::TryInlineCall(Node* call) { |
238 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | 303 DCHECK_EQ(IrOpcode::kJSCallFunction, call->opcode()); |
239 | 304 |
240 HeapObjectMatcher<JSFunction> match(node->InputAt(0)); | 305 HeapObjectMatcher<JSFunction> match(call->InputAt(0)); |
241 if (!match.HasValue()) { | 306 if (!match.HasValue()) { |
242 return; | 307 return; |
243 } | 308 } |
244 | 309 |
245 Handle<JSFunction> function = match.Value().handle(); | 310 Handle<JSFunction> function = match.Value().handle(); |
246 | 311 |
247 if (function->shared()->native()) { | 312 if (function->shared()->native()) { |
248 if (FLAG_trace_turbo_inlining) { | 313 if (FLAG_trace_turbo_inlining) { |
249 SmartArrayPointer<char> name = | 314 SmartArrayPointer<char> name = |
250 function->shared()->DebugName()->ToCString(); | 315 function->shared()->DebugName()->ToCString(); |
(...skipping 17 matching lines...) Expand all Loading... | |
268 } | 333 } |
269 return; | 334 return; |
270 } | 335 } |
271 | 336 |
272 if (FLAG_trace_turbo_inlining) { | 337 if (FLAG_trace_turbo_inlining) { |
273 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); | 338 SmartArrayPointer<char> name = function->shared()->DebugName()->ToCString(); |
274 PrintF("Inlining %s into %s\n", name.get(), | 339 PrintF("Inlining %s into %s\n", name.get(), |
275 info_->shared_info()->DebugName()->ToCString().get()); | 340 info_->shared_info()->DebugName()->ToCString().get()); |
276 } | 341 } |
277 | 342 |
278 Graph graph(info_->zone()); | 343 Graph graph(info.zone()); |
279 graph.SetNextNodeId(jsgraph_->graph()->NextNodeID()); | 344 Typer typer(info.zone()); |
280 | 345 JSGraph jsgraph(&graph, jsgraph_->common(), jsgraph_->javascript(), &typer, |
281 Typer typer(info_->zone()); | 346 jsgraph_->machine()); |
282 CommonOperatorBuilder common(info_->zone()); | |
283 JSGraph jsgraph(&graph, &common, &typer); | |
284 | 347 |
285 AstGraphBuilder graph_builder(&info, &jsgraph); | 348 AstGraphBuilder graph_builder(&info, &jsgraph); |
286 graph_builder.CreateGraph(); | 349 graph_builder.CreateGraph(); |
350 Inlinee::UnifyReturn(&jsgraph); | |
287 | 351 |
288 Inlinee inlinee(&jsgraph); | 352 CopyVisitor visitor(&graph, jsgraph_, jsgraph_->zone()); |
289 inlinee.UnifyReturn(); | 353 visitor.CopyGraph(); |
290 inlinee.InlineAtCall(jsgraph_, node); | |
291 | 354 |
292 jsgraph_->graph()->SetNextNodeId(inlinee.graph()->NextNodeID()); | 355 Inlinee inlinee(visitor.GetCopy(graph.start()), visitor.GetCopy(graph.end())); |
356 inlinee.InlineAtCall(jsgraph_, call); | |
293 } | 357 } |
294 } | 358 } |
295 } | 359 } |
296 } // namespace v8::internal::compiler | 360 } // namespace v8::internal::compiler |
OLD | NEW |