| 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 |