blob: 0e0508bcd4ccd64f600280580ab002dde1162d6c [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
3// found in the LICENSE file.
4
5#include "src/compiler/js-inlining-heuristic.h"
6
7#include "src/compiler.h"
8#include "src/compiler/node-matchers.h"
9#include "src/objects-inl.h"
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
15Reduction JSInliningHeuristic::Reduce(Node* node) {
16 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
17
18 // Check if we already saw that {node} before, and if so, just skip it.
19 if (seen_.find(node->id()) != seen_.end()) return NoChange();
20 seen_.insert(node->id());
21
22 Node* callee = node->InputAt(0);
23 HeapObjectMatcher match(callee);
24 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange();
25 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
26
27 // Functions marked with %SetForceInlineFlag are immediately inlined.
28 if (function->shared()->force_inline()) {
29 return inliner_.ReduceJSCall(node, function);
30 }
31
32 // Handling of special inlining modes right away:
33 // - For restricted inlining: stop all handling at this point.
34 // - For stressing inlining: immediately handle all functions.
35 switch (mode_) {
36 case kRestrictedInlining:
37 return NoChange();
38 case kStressInlining:
39 return inliner_.ReduceJSCall(node, function);
40 case kGeneralInlining:
41 break;
42 }
43
44 // ---------------------------------------------------------------------------
45 // Everything below this line is part of the inlining heuristic.
46 // ---------------------------------------------------------------------------
47
48 // Built-in functions are handled by the JSBuiltinReducer.
49 if (function->shared()->HasBuiltinFunctionId()) return NoChange();
50
51 // Don't inline builtins.
52 if (function->shared()->IsBuiltin()) return NoChange();
53
54 // Quick check on source code length to avoid parsing large candidate.
55 if (function->shared()->SourceSize() > FLAG_max_inlined_source_size) {
56 return NoChange();
57 }
58
59 // Quick check on the size of the AST to avoid parsing large candidate.
60 if (function->shared()->ast_node_count() > FLAG_max_inlined_nodes) {
61 return NoChange();
62 }
63
64 // Avoid inlining within or across the boundary of asm.js code.
65 if (info_->shared_info()->asm_function()) return NoChange();
66 if (function->shared()->asm_function()) return NoChange();
67
68 // Stop inlinining once the maximum allowed level is reached.
69 int level = 0;
70 for (Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
71 frame_state->opcode() == IrOpcode::kFrameState;
72 frame_state = NodeProperties::GetFrameStateInput(frame_state, 0)) {
73 if (++level > FLAG_max_inlining_levels) return NoChange();
74 }
75
76 // Gather feedback on how often this call site has been hit before.
77 int calls = -1; // Same default as CallICNexus::ExtractCallCount.
78 // TODO(turbofan): We also want call counts for constructor calls.
79 if (node->opcode() == IrOpcode::kJSCallFunction) {
80 CallFunctionParameters p = CallFunctionParametersOf(node->op());
81 if (p.feedback().IsValid()) {
82 CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
83 calls = nexus.ExtractCallCount();
84 }
85 }
86
87 // ---------------------------------------------------------------------------
88 // Everything above this line is part of the inlining heuristic.
89 // ---------------------------------------------------------------------------
90
91 // In the general case we remember the candidate for later.
92 candidates_.insert({function, node, calls});
93 return NoChange();
94}
95
96
97void JSInliningHeuristic::Finalize() {
98 if (candidates_.empty()) return; // Nothing to do without candidates.
99 if (FLAG_trace_turbo_inlining) PrintCandidates();
100
101 // We inline at most one candidate in every iteration of the fixpoint.
102 // This is to ensure that we don't consume the full inlining budget
103 // on things that aren't called very often.
104 // TODO(bmeurer): Use std::priority_queue instead of std::set here.
105 while (!candidates_.empty()) {
106 if (cumulative_count_ > FLAG_max_inlined_nodes_cumulative) return;
107 auto i = candidates_.begin();
108 Candidate candidate = *i;
109 candidates_.erase(i);
110 // Make sure we don't try to inline dead candidate nodes.
111 if (!candidate.node->IsDead()) {
112 Reduction r = inliner_.ReduceJSCall(candidate.node, candidate.function);
113 if (r.Changed()) {
114 cumulative_count_ += candidate.function->shared()->ast_node_count();
115 return;
116 }
117 }
118 }
119}
120
121
122bool JSInliningHeuristic::CandidateCompare::operator()(
123 const Candidate& left, const Candidate& right) const {
Ben Murdochda12d292016-06-02 14:46:10 +0100124 if (left.calls != right.calls) {
125 return left.calls > right.calls;
126 }
127 return left.node < right.node;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000128}
129
130
131void JSInliningHeuristic::PrintCandidates() {
132 PrintF("Candidates for inlining (size=%zu):\n", candidates_.size());
133 for (const Candidate& candidate : candidates_) {
134 PrintF(" id:%d, calls:%d, size[source]:%d, size[ast]:%d / %s\n",
135 candidate.node->id(), candidate.calls,
136 candidate.function->shared()->SourceSize(),
137 candidate.function->shared()->ast_node_count(),
138 candidate.function->shared()->DebugName()->ToCString().get());
139 }
140}
141
142} // namespace compiler
143} // namespace internal
144} // namespace v8