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

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

Issue 2626783003: [turbofan] Enable inlining based on SharedFunctionInfo. (Closed)
Patch Set: Rebased. Created 3 years, 10 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
« no previous file with comments | « src/compiler/js-inlining-heuristic.h ('k') | src/objects.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « src/compiler/js-inlining-heuristic.h ('k') | src/objects.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698