1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_COMPILER_BACKEND_INLINER_H_
6#define RUNTIME_VM_COMPILER_BACKEND_INLINER_H_
7
8#if defined(DART_PRECOMPILED_RUNTIME)
9#error "AOT runtime should not use compiler sources (including header files)"
10#endif // defined(DART_PRECOMPILED_RUNTIME)
11
12#include "vm/allocation.h"
13#include "vm/growable_array.h"
14#include "vm/token_position.h"
15
16namespace dart {
17
18class Definition;
19class Field;
20class FlowGraph;
21class ForwardInstructionIterator;
22class Function;
23class FunctionEntryInstr;
24class GraphEntryInstr;
25class ICData;
26class InstanceCallInstr;
27class Instruction;
28class Precompiler;
29class StaticCallInstr;
30class TargetEntryInstr;
31
32class SpeculativeInliningPolicy {
33 public:
34 explicit SpeculativeInliningPolicy(bool enable_suppression,
35 intptr_t limit = -1)
36 : enable_suppression_(enable_suppression), remaining_(limit) {}
37
38 bool AllowsSpeculativeInlining() const {
39 return !enable_suppression_ || remaining_ > 0;
40 }
41
42 bool IsAllowedForInlining(intptr_t call_deopt_id) const {
43 // If we are not supressing, we always enable optimistic inlining.
44 if (!enable_suppression_) {
45 return true;
46 }
47
48 // If we have already suppressed the deopt-id we don't allow inlining it.
49 if (IsSuppressed(call_deopt_id)) {
50 return false;
51 }
52
53 // Allow it if we can bailout at least one more time.
54 return remaining_ > 0;
55 }
56
57 bool AddBlockedDeoptId(intptr_t id) {
58 ASSERT(enable_suppression_);
59#if defined(DEBUG)
60 ASSERT(!IsSuppressed(id));
61#endif
62
63 // If we exhausted the number of suppression entries there is no point
64 // in adding entries to the list.
65 if (remaining_ <= 0) return false;
66
67 inlining_suppressions_.Add(id);
68 remaining_ -= 1;
69 return true;
70 }
71
72 intptr_t length() const { return inlining_suppressions_.length(); }
73
74 private:
75 bool IsSuppressed(intptr_t id) const {
76 for (intptr_t i = 0; i < inlining_suppressions_.length(); ++i) {
77 if (inlining_suppressions_[i] == id) return true;
78 }
79 return false;
80 }
81
82 // Whether we enable supressing inlining at specific deopt-ids.
83 const bool enable_suppression_;
84
85 // After we reach [remaining_] number of deopt-ids in [inlining_suppressions_]
86 // list, we'll disable speculative inlining entirely.
87 intptr_t remaining_;
88 GrowableArray<intptr_t> inlining_suppressions_;
89};
90
91class FlowGraphInliner : ValueObject {
92 public:
93 FlowGraphInliner(FlowGraph* flow_graph,
94 GrowableArray<const Function*>* inline_id_to_function,
95 GrowableArray<TokenPosition>* inline_id_to_token_pos,
96 GrowableArray<intptr_t>* caller_inline_id,
97 SpeculativeInliningPolicy* speculative_policy,
98 Precompiler* precompiler);
99
100 // The flow graph is destructively updated upon inlining. Returns the max
101 // depth that we inlined.
102 int Inline();
103
104 // Computes graph information (instruction and call site count).
105 // For the non-specialized cases (num_constants_args == 0), the
106 // method uses a cache to avoid recomputing the counts (the cached
107 // value may still be approximate but close). The 'force' flag is
108 // used to update the cached value at the end of running the full pipeline
109 // on non-specialized cases. Specialized cases (num_constants_args > 0)
110 // always recompute the counts without caching.
111 //
112 // TODO(ajcbik): cache for specific constant argument combinations too?
113 static void CollectGraphInfo(FlowGraph* flow_graph,
114 intptr_t num_constant_args,
115 bool force,
116 intptr_t* instruction_count,
117 intptr_t* call_site_count);
118
119 static void SetInliningId(FlowGraph* flow_graph, intptr_t inlining_id);
120
121 bool AlwaysInline(const Function& function);
122
123 static bool FunctionHasPreferInlinePragma(const Function& function);
124 static bool FunctionHasNeverInlinePragma(const Function& function);
125
126 FlowGraph* flow_graph() const { return flow_graph_; }
127 intptr_t NextInlineId(const Function& function,
128 TokenPosition tp,
129 intptr_t caller_id);
130
131 bool trace_inlining() const { return trace_inlining_; }
132
133 SpeculativeInliningPolicy* speculative_policy() {
134 return speculative_policy_;
135 }
136
137 struct ExactnessInfo {
138 const bool is_exact;
139 bool emit_exactness_guard;
140 };
141
142 static bool TryReplaceInstanceCallWithInline(
143 FlowGraph* flow_graph,
144 ForwardInstructionIterator* iterator,
145 InstanceCallInstr* call,
146 SpeculativeInliningPolicy* policy);
147
148 static bool TryReplaceStaticCallWithInline(
149 FlowGraph* flow_graph,
150 ForwardInstructionIterator* iterator,
151 StaticCallInstr* call,
152 SpeculativeInliningPolicy* policy);
153
154 static bool TryInlineRecognizedMethod(FlowGraph* flow_graph,
155 intptr_t receiver_cid,
156 const Function& target,
157 Definition* call,
158 Definition* receiver,
159 TokenPosition token_pos,
160 const ICData* ic_data,
161 GraphEntryInstr* graph_entry,
162 FunctionEntryInstr** entry,
163 Instruction** last,
164 Definition** result,
165 SpeculativeInliningPolicy* policy,
166 ExactnessInfo* exactness = nullptr);
167
168 private:
169 friend class CallSiteInliner;
170
171 FlowGraph* flow_graph_;
172 GrowableArray<const Function*>* inline_id_to_function_;
173 GrowableArray<TokenPosition>* inline_id_to_token_pos_;
174 GrowableArray<intptr_t>* caller_inline_id_;
175 const bool trace_inlining_;
176 SpeculativeInliningPolicy* speculative_policy_;
177 Precompiler* precompiler_;
178
179 DISALLOW_COPY_AND_ASSIGN(FlowGraphInliner);
180};
181
182} // namespace dart
183
184#endif // RUNTIME_VM_COMPILER_BACKEND_INLINER_H_
185