• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
18 #define ART_COMPILER_OPTIMIZING_INLINER_H_
19 
20 #include "dex_file_types.h"
21 #include "invoke_type.h"
22 #include "optimization.h"
23 #include "jit/profile_compilation_info.h"
24 
25 namespace art {
26 
27 class CodeGenerator;
28 class CompilerDriver;
29 class DexCompilationUnit;
30 class HGraph;
31 class HInvoke;
32 class OptimizingCompilerStats;
33 
34 class HInliner : public HOptimization {
35  public:
36   HInliner(HGraph* outer_graph,
37            HGraph* outermost_graph,
38            CodeGenerator* codegen,
39            const DexCompilationUnit& outer_compilation_unit,
40            const DexCompilationUnit& caller_compilation_unit,
41            CompilerDriver* compiler_driver,
42            VariableSizedHandleScope* handles,
43            OptimizingCompilerStats* stats,
44            size_t total_number_of_dex_registers,
45            size_t total_number_of_instructions,
46            HInliner* parent,
47            size_t depth = 0)
HOptimization(outer_graph,kInlinerPassName,stats)48       : HOptimization(outer_graph, kInlinerPassName, stats),
49         outermost_graph_(outermost_graph),
50         outer_compilation_unit_(outer_compilation_unit),
51         caller_compilation_unit_(caller_compilation_unit),
52         codegen_(codegen),
53         compiler_driver_(compiler_driver),
54         total_number_of_dex_registers_(total_number_of_dex_registers),
55         total_number_of_instructions_(total_number_of_instructions),
56         parent_(parent),
57         depth_(depth),
58         inlining_budget_(0),
59         handles_(handles),
60         inline_stats_(nullptr) {}
61 
62   void Run() OVERRIDE;
63 
64   static constexpr const char* kInlinerPassName = "inliner";
65 
66  private:
67   enum InlineCacheType {
68     kInlineCacheNoData = 0,
69     kInlineCacheUninitialized = 1,
70     kInlineCacheMonomorphic = 2,
71     kInlineCachePolymorphic = 3,
72     kInlineCacheMegamorphic = 4,
73     kInlineCacheMissingTypes = 5
74   };
75 
76   bool TryInline(HInvoke* invoke_instruction);
77 
78   // Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether
79   // reference type propagation can run after the inlining. If the inlining is successful, this
80   // method will replace and remove the `invoke_instruction`. If `cha_devirtualize` is true,
81   // a CHA guard needs to be added for the inlining.
82   bool TryInlineAndReplace(HInvoke* invoke_instruction,
83                            ArtMethod* resolved_method,
84                            ReferenceTypeInfo receiver_type,
85                            bool do_rtp,
86                            bool cha_devirtualize)
87     REQUIRES_SHARED(Locks::mutator_lock_);
88 
89   bool TryBuildAndInline(HInvoke* invoke_instruction,
90                          ArtMethod* resolved_method,
91                          ReferenceTypeInfo receiver_type,
92                          HInstruction** return_replacement)
93     REQUIRES_SHARED(Locks::mutator_lock_);
94 
95   bool TryBuildAndInlineHelper(HInvoke* invoke_instruction,
96                                ArtMethod* resolved_method,
97                                ReferenceTypeInfo receiver_type,
98                                bool same_dex_file,
99                                HInstruction** return_replacement);
100 
101   // Run simple optimizations on `callee_graph`.
102   void RunOptimizations(HGraph* callee_graph,
103                         const DexFile::CodeItem* code_item,
104                         const DexCompilationUnit& dex_compilation_unit)
105     REQUIRES_SHARED(Locks::mutator_lock_);
106 
107   // Try to recognize known simple patterns and replace invoke call with appropriate instructions.
108   bool TryPatternSubstitution(HInvoke* invoke_instruction,
109                               ArtMethod* resolved_method,
110                               HInstruction** return_replacement)
111     REQUIRES_SHARED(Locks::mutator_lock_);
112 
113   // Create a new HInstanceFieldGet.
114   HInstanceFieldGet* CreateInstanceFieldGet(uint32_t field_index,
115                                             ArtMethod* referrer,
116                                             HInstruction* obj);
117   // Create a new HInstanceFieldSet.
118   HInstanceFieldSet* CreateInstanceFieldSet(uint32_t field_index,
119                                             ArtMethod* referrer,
120                                             HInstruction* obj,
121                                             HInstruction* value,
122                                             bool* is_final = nullptr);
123 
124   // Try inlining the invoke instruction using inline caches.
125   bool TryInlineFromInlineCache(
126       const DexFile& caller_dex_file,
127       HInvoke* invoke_instruction,
128       ArtMethod* resolved_method)
129     REQUIRES_SHARED(Locks::mutator_lock_);
130 
131   // Try getting the inline cache from JIT code cache.
132   // Return true if the inline cache was successfully allocated and the
133   // invoke info was found in the profile info.
134   InlineCacheType GetInlineCacheJIT(
135       HInvoke* invoke_instruction,
136       StackHandleScope<1>* hs,
137       /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
138     REQUIRES_SHARED(Locks::mutator_lock_);
139 
140   // Try getting the inline cache from AOT offline profile.
141   // Return true if the inline cache was successfully allocated and the
142   // invoke info was found in the profile info.
143   InlineCacheType GetInlineCacheAOT(const DexFile& caller_dex_file,
144       HInvoke* invoke_instruction,
145       StackHandleScope<1>* hs,
146       /*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
147     REQUIRES_SHARED(Locks::mutator_lock_);
148 
149   // Extract the mirror classes from the offline profile and add them to the `inline_cache`.
150   // Note that even if we have profile data for the invoke the inline_cache might contain
151   // only null entries if the types cannot be resolved.
152   InlineCacheType ExtractClassesFromOfflineProfile(
153       const HInvoke* invoke_instruction,
154       const ProfileCompilationInfo::OfflineProfileMethodInfo& offline_profile,
155       /*out*/Handle<mirror::ObjectArray<mirror::Class>> inline_cache)
156     REQUIRES_SHARED(Locks::mutator_lock_);
157 
158   // Compute the inline cache type.
159   InlineCacheType GetInlineCacheType(
160       const Handle<mirror::ObjectArray<mirror::Class>>& classes)
161     REQUIRES_SHARED(Locks::mutator_lock_);
162 
163   // Try to inline the target of a monomorphic call. If successful, the code
164   // in the graph will look like:
165   // if (receiver.getClass() != ic.GetMonomorphicType()) deopt
166   // ... // inlined code
167   bool TryInlineMonomorphicCall(HInvoke* invoke_instruction,
168                                 ArtMethod* resolved_method,
169                                 Handle<mirror::ObjectArray<mirror::Class>> classes)
170     REQUIRES_SHARED(Locks::mutator_lock_);
171 
172   // Try to inline targets of a polymorphic call.
173   bool TryInlinePolymorphicCall(HInvoke* invoke_instruction,
174                                 ArtMethod* resolved_method,
175                                 Handle<mirror::ObjectArray<mirror::Class>> classes)
176     REQUIRES_SHARED(Locks::mutator_lock_);
177 
178   bool TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
179                                             ArtMethod* resolved_method,
180                                             Handle<mirror::ObjectArray<mirror::Class>> classes)
181     REQUIRES_SHARED(Locks::mutator_lock_);
182 
183   // Returns whether or not we should use only polymorphic inlining with no deoptimizations.
184   bool UseOnlyPolymorphicInliningWithNoDeopt();
185 
186   // Try CHA-based devirtualization to change virtual method calls into
187   // direct calls.
188   // Returns the actual method that resolved_method can be devirtualized to.
189   ArtMethod* TryCHADevirtualization(ArtMethod* resolved_method)
190     REQUIRES_SHARED(Locks::mutator_lock_);
191 
192   // Add a CHA guard for a CHA-based devirtualized call. A CHA guard checks a
193   // should_deoptimize flag and if it's true, does deoptimization.
194   void AddCHAGuard(HInstruction* invoke_instruction,
195                    uint32_t dex_pc,
196                    HInstruction* cursor,
197                    HBasicBlock* bb_cursor);
198 
199   HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker,
200                                            HInstruction* receiver,
201                                            uint32_t dex_pc) const
202     REQUIRES_SHARED(Locks::mutator_lock_);
203 
204   void FixUpReturnReferenceType(ArtMethod* resolved_method, HInstruction* return_replacement)
205     REQUIRES_SHARED(Locks::mutator_lock_);
206 
207   // Creates an instance of ReferenceTypeInfo from `klass` if `klass` is
208   // admissible (see ReferenceTypePropagation::IsAdmissible for details).
209   // Otherwise returns inexact Object RTI.
210   ReferenceTypeInfo GetClassRTI(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
211 
212   bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method)
213     REQUIRES_SHARED(Locks::mutator_lock_);
214 
215   bool ReturnTypeMoreSpecific(HInvoke* invoke_instruction, HInstruction* return_replacement)
216     REQUIRES_SHARED(Locks::mutator_lock_);
217 
218   // Add a type guard on the given `receiver`. This will add to the graph:
219   // i0 = HFieldGet(receiver, klass)
220   // i1 = HLoadClass(class_index, is_referrer)
221   // i2 = HNotEqual(i0, i1)
222   //
223   // And if `with_deoptimization` is true:
224   // HDeoptimize(i2)
225   //
226   // The method returns the `HNotEqual`, that will be used for polymorphic inlining.
227   HInstruction* AddTypeGuard(HInstruction* receiver,
228                              HInstruction* cursor,
229                              HBasicBlock* bb_cursor,
230                              dex::TypeIndex class_index,
231                              Handle<mirror::Class> klass,
232                              HInstruction* invoke_instruction,
233                              bool with_deoptimization)
234     REQUIRES_SHARED(Locks::mutator_lock_);
235 
236   /*
237    * Ad-hoc implementation for implementing a diamond pattern in the graph for
238    * polymorphic inlining:
239    * 1) `compare` becomes the input of the new `HIf`.
240    * 2) Everything up until `invoke_instruction` is in the then branch (could
241    *    contain multiple blocks).
242    * 3) `invoke_instruction` is moved to the otherwise block.
243    * 4) If `return_replacement` is not null, the merge block will have
244    *    a phi whose inputs are `return_replacement` and `invoke_instruction`.
245    *
246    * Before:
247    *             Block1
248    *             compare
249    *              ...
250    *         invoke_instruction
251    *
252    * After:
253    *            Block1
254    *            compare
255    *              if
256    *          /        \
257    *         /          \
258    *   Then block    Otherwise block
259    *      ...       invoke_instruction
260    *       \              /
261    *        \            /
262    *          Merge block
263    *  phi(return_replacement, invoke_instruction)
264    */
265   void CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
266                                                 HInstruction* return_replacement,
267                                                 HInstruction* invoke_instruction);
268 
269   // Update the inlining budget based on `total_number_of_instructions_`.
270   void UpdateInliningBudget();
271 
272   // Count the number of calls of `method` being inlined recursively.
273   size_t CountRecursiveCallsOf(ArtMethod* method) const;
274 
275   // Pretty-print for spaces during logging.
276   std::string DepthString(int line) const;
277 
278   HGraph* const outermost_graph_;
279   const DexCompilationUnit& outer_compilation_unit_;
280   const DexCompilationUnit& caller_compilation_unit_;
281   CodeGenerator* const codegen_;
282   CompilerDriver* const compiler_driver_;
283   const size_t total_number_of_dex_registers_;
284   size_t total_number_of_instructions_;
285 
286   // The 'parent' inliner, that means the inlinigng optimization that requested
287   // `graph_` to be inlined.
288   const HInliner* const parent_;
289   const size_t depth_;
290 
291   // The budget left for inlining, in number of instructions.
292   size_t inlining_budget_;
293   VariableSizedHandleScope* const handles_;
294 
295   // Used to record stats about optimizations on the inlined graph.
296   // If the inlining is successful, these stats are merged to the caller graph's stats.
297   OptimizingCompilerStats* inline_stats_;
298 
299   DISALLOW_COPY_AND_ASSIGN(HInliner);
300 };
301 
302 }  // namespace art
303 
304 #endif  // ART_COMPILER_OPTIMIZING_INLINER_H_
305