OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 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/js-inlining-heuristic.h" | 5 #include "src/compiler/js-inlining-heuristic.h" |
6 | 6 |
7 #include "src/compilation-info.h" | 7 #include "src/compilation-info.h" |
8 #include "src/compiler/common-operator.h" | 8 #include "src/compiler/common-operator.h" |
9 #include "src/compiler/node-matchers.h" | 9 #include "src/compiler/node-matchers.h" |
10 #include "src/compiler/simplified-operator.h" | 10 #include "src/compiler/simplified-operator.h" |
11 #include "src/objects-inl.h" | 11 #include "src/objects-inl.h" |
12 | 12 |
13 namespace v8 { | 13 namespace v8 { |
14 namespace internal { | 14 namespace internal { |
15 namespace compiler { | 15 namespace compiler { |
16 | 16 |
17 #define TRACE(...) \ | 17 #define TRACE(...) \ |
18 do { \ | 18 do { \ |
19 if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \ | 19 if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \ |
20 } while (false) | 20 } while (false) |
21 | 21 |
22 namespace { | 22 namespace { |
23 | 23 |
24 int CollectFunctions(Node* node, Handle<JSFunction>* functions, | 24 int CollectFunctions(Node* node, Handle<JSFunction>* functions, |
25 int functions_size) { | 25 int functions_size, Handle<SharedFunctionInfo>& shared) { |
26 DCHECK_NE(0, functions_size); | 26 DCHECK_NE(0, functions_size); |
27 HeapObjectMatcher m(node); | 27 HeapObjectMatcher m(node); |
28 if (m.HasValue() && m.Value()->IsJSFunction()) { | 28 if (m.HasValue() && m.Value()->IsJSFunction()) { |
29 functions[0] = Handle<JSFunction>::cast(m.Value()); | 29 functions[0] = Handle<JSFunction>::cast(m.Value()); |
30 return 1; | 30 return 1; |
31 } | 31 } |
32 if (m.IsPhi()) { | 32 if (m.IsPhi()) { |
33 int const value_input_count = m.node()->op()->ValueInputCount(); | 33 int const value_input_count = m.node()->op()->ValueInputCount(); |
34 if (value_input_count > functions_size) return 0; | 34 if (value_input_count > functions_size) return 0; |
35 for (int n = 0; n < value_input_count; ++n) { | 35 for (int n = 0; n < value_input_count; ++n) { |
36 HeapObjectMatcher m(node->InputAt(n)); | 36 HeapObjectMatcher m(node->InputAt(n)); |
37 if (!m.HasValue() || !m.Value()->IsJSFunction()) return 0; | 37 if (!m.HasValue() || !m.Value()->IsJSFunction()) return 0; |
38 functions[n] = Handle<JSFunction>::cast(m.Value()); | 38 functions[n] = Handle<JSFunction>::cast(m.Value()); |
39 } | 39 } |
40 return value_input_count; | 40 return value_input_count; |
41 } | 41 } |
| 42 if (m.IsJSCreateClosure()) { |
| 43 CreateClosureParameters const& p = CreateClosureParametersOf(m.op()); |
| 44 functions[0] = Handle<JSFunction>::null(); |
| 45 shared = p.shared_info(); |
| 46 return 1; |
| 47 } |
42 return 0; | 48 return 0; |
43 } | 49 } |
44 | 50 |
45 bool CanInlineFunction(Handle<JSFunction> function) { | 51 bool CanInlineFunction(Handle<SharedFunctionInfo> shared) { |
46 // Built-in functions are handled by the JSBuiltinReducer. | 52 // Built-in functions are handled by the JSBuiltinReducer. |
47 if (function->shared()->HasBuiltinFunctionId()) return false; | 53 if (shared->HasBuiltinFunctionId()) return false; |
48 | 54 |
49 // Only choose user code for inlining. | 55 // Only choose user code for inlining. |
50 if (!function->shared()->IsUserJavaScript()) return false; | 56 if (!shared->IsUserJavaScript()) return false; |
51 | 57 |
52 // Quick check on the size of the AST to avoid parsing large candidate. | 58 // Quick check on the size of the AST to avoid parsing large candidate. |
53 if (function->shared()->ast_node_count() > FLAG_max_inlined_nodes) { | 59 if (shared->ast_node_count() > FLAG_max_inlined_nodes) { |
54 return false; | 60 return false; |
55 } | 61 } |
56 | 62 |
57 // Avoid inlining across the boundary of asm.js code. | 63 // Avoid inlining across the boundary of asm.js code. |
58 if (function->shared()->asm_function()) return false; | 64 if (shared->asm_function()) return false; |
59 return true; | 65 return true; |
60 } | 66 } |
61 | 67 |
62 } // namespace | 68 } // namespace |
63 | 69 |
64 Reduction JSInliningHeuristic::Reduce(Node* node) { | 70 Reduction JSInliningHeuristic::Reduce(Node* node) { |
65 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); | 71 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); |
66 | 72 |
67 // Check if we already saw that {node} before, and if so, just skip it. | 73 // Check if we already saw that {node} before, and if so, just skip it. |
68 if (seen_.find(node->id()) != seen_.end()) return NoChange(); | 74 if (seen_.find(node->id()) != seen_.end()) return NoChange(); |
69 seen_.insert(node->id()); | 75 seen_.insert(node->id()); |
70 | 76 |
71 // Check if the {node} is an appropriate candidate for inlining. | 77 // Check if the {node} is an appropriate candidate for inlining. |
72 Node* callee = node->InputAt(0); | 78 Node* callee = node->InputAt(0); |
73 Candidate candidate; | 79 Candidate candidate; |
74 candidate.node = node; | 80 candidate.node = node; |
75 candidate.num_functions = | 81 candidate.num_functions = CollectFunctions( |
76 CollectFunctions(callee, candidate.functions, kMaxCallPolymorphism); | 82 callee, candidate.functions, kMaxCallPolymorphism, candidate.shared_info); |
77 if (candidate.num_functions == 0) { | 83 if (candidate.num_functions == 0) { |
78 return NoChange(); | 84 return NoChange(); |
79 } else if (candidate.num_functions > 1 && !FLAG_polymorphic_inlining) { | 85 } else if (candidate.num_functions > 1 && !FLAG_polymorphic_inlining) { |
80 TRACE( | 86 TRACE( |
81 "Not considering call site #%d:%s, because polymorphic inlining " | 87 "Not considering call site #%d:%s, because polymorphic inlining " |
82 "is disabled\n", | 88 "is disabled\n", |
83 node->id(), node->op()->mnemonic()); | 89 node->id(), node->op()->mnemonic()); |
84 return NoChange(); | 90 return NoChange(); |
85 } | 91 } |
86 | 92 |
87 // Functions marked with %SetForceInlineFlag are immediately inlined. | 93 // Functions marked with %SetForceInlineFlag are immediately inlined. |
88 bool can_inline = false, force_inline = true; | 94 bool can_inline = false, force_inline = true; |
89 for (int i = 0; i < candidate.num_functions; ++i) { | 95 for (int i = 0; i < candidate.num_functions; ++i) { |
90 Handle<JSFunction> function = candidate.functions[i]; | 96 Handle<SharedFunctionInfo> shared = |
91 if (!function->shared()->force_inline()) { | 97 candidate.functions[i].is_null() |
| 98 ? candidate.shared_info |
| 99 : handle(candidate.functions[i]->shared()); |
| 100 if (!shared->force_inline()) { |
92 force_inline = false; | 101 force_inline = false; |
93 } | 102 } |
94 if (CanInlineFunction(function)) { | 103 if (CanInlineFunction(shared)) { |
95 can_inline = true; | 104 can_inline = true; |
96 } | 105 } |
97 } | 106 } |
98 if (force_inline) return InlineCandidate(candidate); | 107 if (force_inline) return InlineCandidate(candidate); |
99 if (!can_inline) return NoChange(); | 108 if (!can_inline) return NoChange(); |
100 | 109 |
101 // Stop inlining once the maximum allowed level is reached. | 110 // Stop inlining once the maximum allowed level is reached. |
102 int level = 0; | 111 int level = 0; |
103 for (Node* frame_state = NodeProperties::GetFrameStateInput(node); | 112 for (Node* frame_state = NodeProperties::GetFrameStateInput(node); |
104 frame_state->opcode() == IrOpcode::kFrameState; | 113 frame_state->opcode() == IrOpcode::kFrameState; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 Reduction const reduction = InlineCandidate(candidate); | 169 Reduction const reduction = InlineCandidate(candidate); |
161 if (reduction.Changed()) return; | 170 if (reduction.Changed()) return; |
162 } | 171 } |
163 } | 172 } |
164 } | 173 } |
165 | 174 |
166 Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate) { | 175 Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate) { |
167 int const num_calls = candidate.num_functions; | 176 int const num_calls = candidate.num_functions; |
168 Node* const node = candidate.node; | 177 Node* const node = candidate.node; |
169 if (num_calls == 1) { | 178 if (num_calls == 1) { |
170 Handle<JSFunction> function = candidate.functions[0]; | 179 Handle<SharedFunctionInfo> shared = |
171 Reduction const reduction = inliner_.ReduceJSCall(node, function); | 180 candidate.functions[0].is_null() |
| 181 ? candidate.shared_info |
| 182 : handle(candidate.functions[0]->shared()); |
| 183 Reduction const reduction = inliner_.ReduceJSCall(node); |
172 if (reduction.Changed()) { | 184 if (reduction.Changed()) { |
173 cumulative_count_ += function->shared()->ast_node_count(); | 185 cumulative_count_ += shared->ast_node_count(); |
174 } | 186 } |
175 return reduction; | 187 return reduction; |
176 } | 188 } |
177 | 189 |
178 // Expand the JSCall/JSConstruct node to a subgraph first if | 190 // Expand the JSCall/JSConstruct node to a subgraph first if |
179 // we have multiple known target functions. | 191 // we have multiple known target functions. |
180 DCHECK_LT(1, num_calls); | 192 DCHECK_LT(1, num_calls); |
181 Node* calls[kMaxCallPolymorphism + 1]; | 193 Node* calls[kMaxCallPolymorphism + 1]; |
182 Node* if_successes[kMaxCallPolymorphism]; | 194 Node* if_successes[kMaxCallPolymorphism]; |
183 Node* callee = NodeProperties::GetValueInput(node, 0); | 195 Node* callee = NodeProperties::GetValueInput(node, 0); |
184 Node* fallthrough_control = NodeProperties::GetControlInput(node); | 196 Node* fallthrough_control = NodeProperties::GetControlInput(node); |
185 | 197 |
186 // Setup the inputs for the cloned call nodes. | 198 // Setup the inputs for the cloned call nodes. |
187 int const input_count = node->InputCount(); | 199 int const input_count = node->InputCount(); |
188 Node** inputs = graph()->zone()->NewArray<Node*>(input_count); | 200 Node** inputs = graph()->zone()->NewArray<Node*>(input_count); |
189 for (int i = 0; i < input_count; ++i) { | 201 for (int i = 0; i < input_count; ++i) { |
190 inputs[i] = node->InputAt(i); | 202 inputs[i] = node->InputAt(i); |
191 } | 203 } |
192 | 204 |
193 // Create the appropriate control flow to dispatch to the cloned calls. | 205 // Create the appropriate control flow to dispatch to the cloned calls. |
194 for (int i = 0; i < num_calls; ++i) { | 206 for (int i = 0; i < num_calls; ++i) { |
| 207 // TODO(2206): Make comparison be based on underlying SharedFunctionInfo |
| 208 // instead of the target JSFunction reference directly. |
195 Node* target = jsgraph()->HeapConstant(candidate.functions[i]); | 209 Node* target = jsgraph()->HeapConstant(candidate.functions[i]); |
196 if (i != (num_calls - 1)) { | 210 if (i != (num_calls - 1)) { |
197 Node* check = | 211 Node* check = |
198 graph()->NewNode(simplified()->ReferenceEqual(), callee, target); | 212 graph()->NewNode(simplified()->ReferenceEqual(), callee, target); |
199 Node* branch = | 213 Node* branch = |
200 graph()->NewNode(common()->Branch(), check, fallthrough_control); | 214 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
201 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 215 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
202 if_successes[i] = graph()->NewNode(common()->IfTrue(), branch); | 216 if_successes[i] = graph()->NewNode(common()->IfTrue(), branch); |
203 } else { | 217 } else { |
204 if_successes[i] = fallthrough_control; | 218 if_successes[i] = fallthrough_control; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 graph()->NewNode(common()->EffectPhi(num_calls), num_calls + 1, calls); | 262 graph()->NewNode(common()->EffectPhi(num_calls), num_calls + 1, calls); |
249 Node* value = | 263 Node* value = |
250 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, num_calls), | 264 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, num_calls), |
251 num_calls + 1, calls); | 265 num_calls + 1, calls); |
252 ReplaceWithValue(node, value, effect, control); | 266 ReplaceWithValue(node, value, effect, control); |
253 | 267 |
254 // Inline the individual, cloned call sites. | 268 // Inline the individual, cloned call sites. |
255 for (int i = 0; i < num_calls; ++i) { | 269 for (int i = 0; i < num_calls; ++i) { |
256 Handle<JSFunction> function = candidate.functions[i]; | 270 Handle<JSFunction> function = candidate.functions[i]; |
257 Node* node = calls[i]; | 271 Node* node = calls[i]; |
258 Reduction const reduction = inliner_.ReduceJSCall(node, function); | 272 Reduction const reduction = inliner_.ReduceJSCall(node); |
259 if (reduction.Changed()) { | 273 if (reduction.Changed()) { |
260 cumulative_count_ += function->shared()->ast_node_count(); | 274 cumulative_count_ += function->shared()->ast_node_count(); |
261 } | 275 } |
262 } | 276 } |
263 | 277 |
264 return Replace(value); | 278 return Replace(value); |
265 } | 279 } |
266 | 280 |
267 bool JSInliningHeuristic::CandidateCompare::operator()( | 281 bool JSInliningHeuristic::CandidateCompare::operator()( |
268 const Candidate& left, const Candidate& right) const { | 282 const Candidate& left, const Candidate& right) const { |
269 if (left.frequency > right.frequency) { | 283 if (left.frequency > right.frequency) { |
270 return true; | 284 return true; |
271 } else if (left.frequency < right.frequency) { | 285 } else if (left.frequency < right.frequency) { |
272 return false; | 286 return false; |
273 } else { | 287 } else { |
274 return left.node->id() > right.node->id(); | 288 return left.node->id() > right.node->id(); |
275 } | 289 } |
276 } | 290 } |
277 | 291 |
278 void JSInliningHeuristic::PrintCandidates() { | 292 void JSInliningHeuristic::PrintCandidates() { |
279 PrintF("Candidates for inlining (size=%zu):\n", candidates_.size()); | 293 PrintF("Candidates for inlining (size=%zu):\n", candidates_.size()); |
280 for (const Candidate& candidate : candidates_) { | 294 for (const Candidate& candidate : candidates_) { |
281 PrintF(" #%d:%s, frequency:%g\n", candidate.node->id(), | 295 PrintF(" #%d:%s, frequency:%g\n", candidate.node->id(), |
282 candidate.node->op()->mnemonic(), candidate.frequency); | 296 candidate.node->op()->mnemonic(), candidate.frequency); |
283 for (int i = 0; i < candidate.num_functions; ++i) { | 297 for (int i = 0; i < candidate.num_functions; ++i) { |
284 Handle<JSFunction> function = candidate.functions[i]; | 298 Handle<SharedFunctionInfo> shared = |
285 PrintF(" - size:%d, name: %s\n", function->shared()->ast_node_count(), | 299 candidate.functions[i].is_null() |
286 function->shared()->DebugName()->ToCString().get()); | 300 ? candidate.shared_info |
| 301 : handle(candidate.functions[i]->shared()); |
| 302 PrintF(" - size:%d, name: %s\n", shared->ast_node_count(), |
| 303 shared->DebugName()->ToCString().get()); |
287 } | 304 } |
288 } | 305 } |
289 } | 306 } |
290 | 307 |
291 Graph* JSInliningHeuristic::graph() const { return jsgraph()->graph(); } | 308 Graph* JSInliningHeuristic::graph() const { return jsgraph()->graph(); } |
292 | 309 |
293 CommonOperatorBuilder* JSInliningHeuristic::common() const { | 310 CommonOperatorBuilder* JSInliningHeuristic::common() const { |
294 return jsgraph()->common(); | 311 return jsgraph()->common(); |
295 } | 312 } |
296 | 313 |
297 SimplifiedOperatorBuilder* JSInliningHeuristic::simplified() const { | 314 SimplifiedOperatorBuilder* JSInliningHeuristic::simplified() const { |
298 return jsgraph()->simplified(); | 315 return jsgraph()->simplified(); |
299 } | 316 } |
300 | 317 |
301 } // namespace compiler | 318 } // namespace compiler |
302 } // namespace internal | 319 } // namespace internal |
303 } // namespace v8 | 320 } // namespace v8 |
OLD | NEW |