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