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 #include "inliner.h"
18
19 #include "art_method-inl.h"
20 #include "base/enums.h"
21 #include "base/logging.h"
22 #include "builder.h"
23 #include "class_linker.h"
24 #include "class_root-inl.h"
25 #include "constant_folding.h"
26 #include "data_type-inl.h"
27 #include "dead_code_elimination.h"
28 #include "dex/inline_method_analyser.h"
29 #include "driver/compiler_options.h"
30 #include "driver/dex_compilation_unit.h"
31 #include "instruction_simplifier.h"
32 #include "intrinsics.h"
33 #include "jit/jit.h"
34 #include "jit/jit_code_cache.h"
35 #include "mirror/class_loader.h"
36 #include "mirror/dex_cache.h"
37 #include "mirror/object_array-alloc-inl.h"
38 #include "mirror/object_array-inl.h"
39 #include "nodes.h"
40 #include "reference_type_propagation.h"
41 #include "register_allocator_linear_scan.h"
42 #include "scoped_thread_state_change-inl.h"
43 #include "sharpening.h"
44 #include "ssa_builder.h"
45 #include "ssa_phi_elimination.h"
46 #include "thread.h"
47 #include "verifier/verifier_compiler_binding.h"
48
49 namespace art {
50
51 // Instruction limit to control memory.
52 static constexpr size_t kMaximumNumberOfTotalInstructions = 1024;
53
54 // Maximum number of instructions for considering a method small,
55 // which we will always try to inline if the other non-instruction limits
56 // are not reached.
57 static constexpr size_t kMaximumNumberOfInstructionsForSmallMethod = 3;
58
59 // Limit the number of dex registers that we accumulate while inlining
60 // to avoid creating large amount of nested environments.
61 static constexpr size_t kMaximumNumberOfCumulatedDexRegisters = 32;
62
63 // Limit recursive call inlining, which do not benefit from too
64 // much inlining compared to code locality.
65 static constexpr size_t kMaximumNumberOfRecursiveCalls = 4;
66
67 // Limit recursive polymorphic call inlining to prevent code bloat, since it can quickly get out of
68 // hand in the presence of multiple Wrapper classes. We set this to 0 to disallow polymorphic
69 // recursive calls at all.
70 static constexpr size_t kMaximumNumberOfPolymorphicRecursiveCalls = 0;
71
72 // Controls the use of inline caches in AOT mode.
73 static constexpr bool kUseAOTInlineCaches = true;
74
75 // We check for line numbers to make sure the DepthString implementation
76 // aligns the output nicely.
77 #define LOG_INTERNAL(msg) \
78 static_assert(__LINE__ > 10, "Unhandled line number"); \
79 static_assert(__LINE__ < 10000, "Unhandled line number"); \
80 VLOG(compiler) << DepthString(__LINE__) << msg
81
82 #define LOG_TRY() LOG_INTERNAL("Try inlinining call: ")
83 #define LOG_NOTE() LOG_INTERNAL("Note: ")
84 #define LOG_SUCCESS() LOG_INTERNAL("Success: ")
85 #define LOG_FAIL(stats_ptr, stat) MaybeRecordStat(stats_ptr, stat); LOG_INTERNAL("Fail: ")
86 #define LOG_FAIL_NO_STAT() LOG_INTERNAL("Fail: ")
87
DepthString(int line) const88 std::string HInliner::DepthString(int line) const {
89 std::string value;
90 // Indent according to the inlining depth.
91 size_t count = depth_;
92 // Line numbers get printed in the log, so add a space if the log's line is less
93 // than 1000, and two if less than 100. 10 cannot be reached as it's the copyright.
94 if (!kIsTargetBuild) {
95 if (line < 100) {
96 value += " ";
97 }
98 if (line < 1000) {
99 value += " ";
100 }
101 // Safeguard if this file reaches more than 10000 lines.
102 DCHECK_LT(line, 10000);
103 }
104 for (size_t i = 0; i < count; ++i) {
105 value += " ";
106 }
107 return value;
108 }
109
CountNumberOfInstructions(HGraph * graph)110 static size_t CountNumberOfInstructions(HGraph* graph) {
111 size_t number_of_instructions = 0;
112 for (HBasicBlock* block : graph->GetReversePostOrderSkipEntryBlock()) {
113 for (HInstructionIterator instr_it(block->GetInstructions());
114 !instr_it.Done();
115 instr_it.Advance()) {
116 ++number_of_instructions;
117 }
118 }
119 return number_of_instructions;
120 }
121
UpdateInliningBudget()122 void HInliner::UpdateInliningBudget() {
123 if (total_number_of_instructions_ >= kMaximumNumberOfTotalInstructions) {
124 // Always try to inline small methods.
125 inlining_budget_ = kMaximumNumberOfInstructionsForSmallMethod;
126 } else {
127 inlining_budget_ = std::max(
128 kMaximumNumberOfInstructionsForSmallMethod,
129 kMaximumNumberOfTotalInstructions - total_number_of_instructions_);
130 }
131 }
132
Run()133 bool HInliner::Run() {
134 if (codegen_->GetCompilerOptions().GetInlineMaxCodeUnits() == 0) {
135 // Inlining effectively disabled.
136 return false;
137 } else if (graph_->IsDebuggable()) {
138 // For simplicity, we currently never inline when the graph is debuggable. This avoids
139 // doing some logic in the runtime to discover if a method could have been inlined.
140 return false;
141 }
142
143 bool did_inline = false;
144 bool did_set_always_throws = false;
145
146 // Initialize the number of instructions for the method being compiled. Recursive calls
147 // to HInliner::Run have already updated the instruction count.
148 if (outermost_graph_ == graph_) {
149 total_number_of_instructions_ = CountNumberOfInstructions(graph_);
150 }
151
152 UpdateInliningBudget();
153 DCHECK_NE(total_number_of_instructions_, 0u);
154 DCHECK_NE(inlining_budget_, 0u);
155
156 // If we're compiling tests, honor inlining directives in method names:
157 // - if a method's name contains the substring "$noinline$", do not
158 // inline that method;
159 // - if a method's name contains the substring "$inline$", ensure
160 // that this method is actually inlined.
161 // We limit the latter to AOT compilation, as the JIT may or may not inline
162 // depending on the state of classes at runtime.
163 const bool honor_noinline_directives = codegen_->GetCompilerOptions().CompileArtTest();
164 const bool honor_inline_directives =
165 honor_noinline_directives && Runtime::Current()->IsAotCompiler();
166
167 // Keep a copy of all blocks when starting the visit.
168 ArenaVector<HBasicBlock*> blocks = graph_->GetReversePostOrder();
169 DCHECK(!blocks.empty());
170 // Because we are changing the graph when inlining,
171 // we just iterate over the blocks of the outer method.
172 // This avoids doing the inlining work again on the inlined blocks.
173 for (HBasicBlock* block : blocks) {
174 for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
175 HInstruction* next = instruction->GetNext();
176 HInvoke* call = instruction->AsInvoke();
177 // As long as the call is not intrinsified, it is worth trying to inline.
178 if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) {
179 if (honor_noinline_directives) {
180 // Debugging case: directives in method names control or assert on inlining.
181 std::string callee_name =
182 call->GetMethodReference().PrettyMethod(/* with_signature= */ false);
183 // Tests prevent inlining by having $noinline$ in their method names.
184 if (callee_name.find("$noinline$") == std::string::npos) {
185 if (TryInline(call, &did_set_always_throws)) {
186 did_inline = true;
187 } else if (honor_inline_directives) {
188 bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos);
189 CHECK(!should_have_inlined) << "Could not inline " << callee_name;
190 }
191 }
192 } else {
193 DCHECK(!honor_inline_directives);
194 // Normal case: try to inline.
195 if (TryInline(call, &did_set_always_throws)) {
196 did_inline = true;
197 }
198 }
199 }
200 instruction = next;
201 }
202 }
203
204 return did_inline || did_set_always_throws;
205 }
206
IsMethodOrDeclaringClassFinal(ArtMethod * method)207 static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
208 REQUIRES_SHARED(Locks::mutator_lock_) {
209 return method->IsFinal() || method->GetDeclaringClass()->IsFinal();
210 }
211
212 /**
213 * Given the `resolved_method` looked up in the dex cache, try to find
214 * the actual runtime target of an interface or virtual call.
215 * Return nullptr if the runtime target cannot be proven.
216 */
FindVirtualOrInterfaceTarget(HInvoke * invoke)217 static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke)
218 REQUIRES_SHARED(Locks::mutator_lock_) {
219 ArtMethod* resolved_method = invoke->GetResolvedMethod();
220 if (IsMethodOrDeclaringClassFinal(resolved_method)) {
221 // No need to lookup further, the resolved method will be the target.
222 return resolved_method;
223 }
224
225 HInstruction* receiver = invoke->InputAt(0);
226 if (receiver->IsNullCheck()) {
227 // Due to multiple levels of inlining within the same pass, it might be that
228 // null check does not have the reference type of the actual receiver.
229 receiver = receiver->InputAt(0);
230 }
231 ReferenceTypeInfo info = receiver->GetReferenceTypeInfo();
232 DCHECK(info.IsValid()) << "Invalid RTI for " << receiver->DebugName();
233 if (!info.IsExact()) {
234 // We currently only support inlining with known receivers.
235 // TODO: Remove this check, we should be able to inline final methods
236 // on unknown receivers.
237 return nullptr;
238 } else if (info.GetTypeHandle()->IsInterface()) {
239 // Statically knowing that the receiver has an interface type cannot
240 // help us find what is the target method.
241 return nullptr;
242 } else if (!resolved_method->GetDeclaringClass()->IsAssignableFrom(info.GetTypeHandle().Get())) {
243 // The method that we're trying to call is not in the receiver's class or super classes.
244 return nullptr;
245 } else if (info.GetTypeHandle()->IsErroneous()) {
246 // If the type is erroneous, do not go further, as we are going to query the vtable or
247 // imt table, that we can only safely do on non-erroneous classes.
248 return nullptr;
249 }
250
251 ClassLinker* cl = Runtime::Current()->GetClassLinker();
252 PointerSize pointer_size = cl->GetImagePointerSize();
253 if (invoke->IsInvokeInterface()) {
254 resolved_method = info.GetTypeHandle()->FindVirtualMethodForInterface(
255 resolved_method, pointer_size);
256 } else {
257 DCHECK(invoke->IsInvokeVirtual());
258 resolved_method = info.GetTypeHandle()->FindVirtualMethodForVirtual(
259 resolved_method, pointer_size);
260 }
261
262 if (resolved_method == nullptr) {
263 // The information we had on the receiver was not enough to find
264 // the target method. Since we check above the exact type of the receiver,
265 // the only reason this can happen is an IncompatibleClassChangeError.
266 return nullptr;
267 } else if (!resolved_method->IsInvokable()) {
268 // The information we had on the receiver was not enough to find
269 // the target method. Since we check above the exact type of the receiver,
270 // the only reason this can happen is an IncompatibleClassChangeError.
271 return nullptr;
272 } else if (IsMethodOrDeclaringClassFinal(resolved_method)) {
273 // A final method has to be the target method.
274 return resolved_method;
275 } else if (info.IsExact()) {
276 // If we found a method and the receiver's concrete type is statically
277 // known, we know for sure the target.
278 return resolved_method;
279 } else {
280 // Even if we did find a method, the receiver type was not enough to
281 // statically find the runtime target.
282 return nullptr;
283 }
284 }
285
FindMethodIndexIn(ArtMethod * method,const DexFile & dex_file,uint32_t name_and_signature_index)286 static uint32_t FindMethodIndexIn(ArtMethod* method,
287 const DexFile& dex_file,
288 uint32_t name_and_signature_index)
289 REQUIRES_SHARED(Locks::mutator_lock_) {
290 if (IsSameDexFile(*method->GetDexFile(), dex_file)) {
291 return method->GetDexMethodIndex();
292 } else {
293 return method->FindDexMethodIndexInOtherDexFile(dex_file, name_and_signature_index);
294 }
295 }
296
FindClassIndexIn(ObjPtr<mirror::Class> cls,const DexCompilationUnit & compilation_unit)297 static dex::TypeIndex FindClassIndexIn(ObjPtr<mirror::Class> cls,
298 const DexCompilationUnit& compilation_unit)
299 REQUIRES_SHARED(Locks::mutator_lock_) {
300 const DexFile& dex_file = *compilation_unit.GetDexFile();
301 dex::TypeIndex index;
302 if (cls->GetDexCache() == nullptr) {
303 DCHECK(cls->IsArrayClass()) << cls->PrettyClass();
304 index = cls->FindTypeIndexInOtherDexFile(dex_file);
305 } else if (!cls->GetDexTypeIndex().IsValid()) {
306 DCHECK(cls->IsProxyClass()) << cls->PrettyClass();
307 // TODO: deal with proxy classes.
308 } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
309 DCHECK_EQ(cls->GetDexCache(), compilation_unit.GetDexCache().Get());
310 index = cls->GetDexTypeIndex();
311 } else {
312 index = cls->FindTypeIndexInOtherDexFile(dex_file);
313 // We cannot guarantee the entry will resolve to the same class,
314 // as there may be different class loaders. So only return the index if it's
315 // the right class already resolved with the class loader.
316 if (index.IsValid()) {
317 ObjPtr<mirror::Class> resolved = compilation_unit.GetClassLinker()->LookupResolvedType(
318 index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get());
319 if (resolved != cls) {
320 index = dex::TypeIndex::Invalid();
321 }
322 }
323 }
324
325 return index;
326 }
327
GetInlineCacheType(const StackHandleScope<InlineCache::kIndividualCacheSize> & classes)328 HInliner::InlineCacheType HInliner::GetInlineCacheType(
329 const StackHandleScope<InlineCache::kIndividualCacheSize>& classes) {
330 DCHECK_EQ(classes.NumberOfReferences(), InlineCache::kIndividualCacheSize);
331 uint8_t number_of_types = InlineCache::kIndividualCacheSize - classes.RemainingSlots();
332 if (number_of_types == 0) {
333 return kInlineCacheUninitialized;
334 } else if (number_of_types == 1) {
335 return kInlineCacheMonomorphic;
336 } else if (number_of_types == InlineCache::kIndividualCacheSize) {
337 return kInlineCacheMegamorphic;
338 } else {
339 return kInlineCachePolymorphic;
340 }
341 }
342
GetMonomorphicType(const StackHandleScope<InlineCache::kIndividualCacheSize> & classes)343 static inline ObjPtr<mirror::Class> GetMonomorphicType(
344 const StackHandleScope<InlineCache::kIndividualCacheSize>& classes)
345 REQUIRES_SHARED(Locks::mutator_lock_) {
346 DCHECK(classes.GetReference(0) != nullptr);
347 return classes.GetReference(0)->AsClass();
348 }
349
FindMethodFromCHA(ArtMethod * resolved_method)350 ArtMethod* HInliner::FindMethodFromCHA(ArtMethod* resolved_method) {
351 if (!resolved_method->HasSingleImplementation()) {
352 return nullptr;
353 }
354 if (Runtime::Current()->IsAotCompiler()) {
355 // No CHA-based devirtulization for AOT compiler (yet).
356 return nullptr;
357 }
358 if (Runtime::Current()->IsZygote()) {
359 // No CHA-based devirtulization for Zygote, as it compiles with
360 // offline information.
361 return nullptr;
362 }
363 if (outermost_graph_->IsCompilingOsr()) {
364 // We do not support HDeoptimize in OSR methods.
365 return nullptr;
366 }
367 PointerSize pointer_size = caller_compilation_unit_.GetClassLinker()->GetImagePointerSize();
368 ArtMethod* single_impl = resolved_method->GetSingleImplementation(pointer_size);
369 if (single_impl == nullptr) {
370 return nullptr;
371 }
372 if (single_impl->IsProxyMethod()) {
373 // Proxy method is a generic invoker that's not worth
374 // devirtualizing/inlining. It also causes issues when the proxy
375 // method is in another dex file if we try to rewrite invoke-interface to
376 // invoke-virtual because a proxy method doesn't have a real dex file.
377 return nullptr;
378 }
379 if (!single_impl->GetDeclaringClass()->IsResolved()) {
380 // There's a race with the class loading, which updates the CHA info
381 // before setting the class to resolved. So we just bail for this
382 // rare occurence.
383 return nullptr;
384 }
385 return single_impl;
386 }
387
IsMethodVerified(ArtMethod * method)388 static bool IsMethodVerified(ArtMethod* method)
389 REQUIRES_SHARED(Locks::mutator_lock_) {
390 if (method->GetDeclaringClass()->IsVerified()) {
391 return true;
392 }
393 // For AOT, we check if the class has a verification status that allows us to
394 // inline / analyze.
395 // At runtime, we know this is cold code if the class is not verified, so don't
396 // bother analyzing.
397 if (Runtime::Current()->IsAotCompiler()) {
398 if (method->GetDeclaringClass()->IsVerifiedNeedsAccessChecks() ||
399 method->GetDeclaringClass()->ShouldVerifyAtRuntime()) {
400 return true;
401 }
402 }
403 return false;
404 }
405
AlwaysThrows(ArtMethod * method)406 static bool AlwaysThrows(ArtMethod* method)
407 REQUIRES_SHARED(Locks::mutator_lock_) {
408 DCHECK(method != nullptr);
409 // Skip non-compilable and unverified methods.
410 if (!method->IsCompilable() || !IsMethodVerified(method)) {
411 return false;
412 }
413 // Skip native methods, methods with try blocks, and methods that are too large.
414 CodeItemDataAccessor accessor(method->DexInstructionData());
415 if (!accessor.HasCodeItem() ||
416 accessor.TriesSize() != 0 ||
417 accessor.InsnsSizeInCodeUnits() > kMaximumNumberOfTotalInstructions) {
418 return false;
419 }
420 // Scan for exits.
421 bool throw_seen = false;
422 for (const DexInstructionPcPair& pair : accessor) {
423 switch (pair.Inst().Opcode()) {
424 case Instruction::RETURN:
425 case Instruction::RETURN_VOID:
426 case Instruction::RETURN_WIDE:
427 case Instruction::RETURN_OBJECT:
428 return false; // found regular control flow back
429 case Instruction::THROW:
430 throw_seen = true;
431 break;
432 default:
433 break;
434 }
435 }
436 return throw_seen;
437 }
438
TryInline(HInvoke * invoke_instruction,bool * did_set_always_throws)439 bool HInliner::TryInline(HInvoke* invoke_instruction, /*inout*/ bool* did_set_always_throws) {
440 MaybeRecordStat(stats_, MethodCompilationStat::kTryInline);
441
442 // Don't bother to move further if we know the method is unresolved or the invocation is
443 // polymorphic (invoke-{polymorphic,custom}).
444 if (invoke_instruction->IsInvokeUnresolved()) {
445 MaybeRecordStat(stats_, MethodCompilationStat::kNotInlinedUnresolved);
446 return false;
447 } else if (invoke_instruction->IsInvokePolymorphic()) {
448 MaybeRecordStat(stats_, MethodCompilationStat::kNotInlinedPolymorphic);
449 return false;
450 } else if (invoke_instruction->IsInvokeCustom()) {
451 MaybeRecordStat(stats_, MethodCompilationStat::kNotInlinedCustom);
452 return false;
453 }
454
455 ScopedObjectAccess soa(Thread::Current());
456 LOG_TRY() << invoke_instruction->GetMethodReference().PrettyMethod();
457
458 ArtMethod* resolved_method = invoke_instruction->GetResolvedMethod();
459 if (resolved_method == nullptr) {
460 DCHECK(invoke_instruction->IsInvokeStaticOrDirect());
461 DCHECK(invoke_instruction->AsInvokeStaticOrDirect()->IsStringInit());
462 LOG_FAIL_NO_STAT() << "Not inlining a String.<init> method";
463 return false;
464 }
465
466 ArtMethod* actual_method = invoke_instruction->IsInvokeStaticOrDirect()
467 ? invoke_instruction->GetResolvedMethod()
468 : FindVirtualOrInterfaceTarget(invoke_instruction);
469
470 if (actual_method != nullptr) {
471 // Single target.
472 bool result = TryInlineAndReplace(invoke_instruction,
473 actual_method,
474 ReferenceTypeInfo::CreateInvalid(),
475 /* do_rtp= */ true);
476 if (result) {
477 MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
478 if (outermost_graph_ == graph_) {
479 MaybeRecordStat(stats_, MethodCompilationStat::kInlinedLastInvokeVirtualOrInterface);
480 }
481 } else {
482 HInvoke* invoke_to_analyze = nullptr;
483 if (TryDevirtualize(invoke_instruction, actual_method, &invoke_to_analyze)) {
484 // Consider devirtualization as inlining.
485 result = true;
486 MaybeRecordStat(stats_, MethodCompilationStat::kDevirtualized);
487 } else {
488 invoke_to_analyze = invoke_instruction;
489 }
490 // Set always throws property for non-inlined method call with single
491 // target.
492 if (AlwaysThrows(actual_method)) {
493 invoke_to_analyze->SetAlwaysThrows(true);
494 *did_set_always_throws = true;
495 }
496 }
497 return result;
498 }
499
500 DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
501
502 if (TryInlineFromCHA(invoke_instruction)) {
503 return true;
504 }
505 return TryInlineFromInlineCache(invoke_instruction);
506 }
507
TryInlineFromCHA(HInvoke * invoke_instruction)508 bool HInliner::TryInlineFromCHA(HInvoke* invoke_instruction) {
509 ArtMethod* method = FindMethodFromCHA(invoke_instruction->GetResolvedMethod());
510 if (method == nullptr) {
511 return false;
512 }
513 LOG_NOTE() << "Try CHA-based inlining of " << method->PrettyMethod();
514
515 uint32_t dex_pc = invoke_instruction->GetDexPc();
516 HInstruction* cursor = invoke_instruction->GetPrevious();
517 HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
518 if (!TryInlineAndReplace(invoke_instruction,
519 method,
520 ReferenceTypeInfo::CreateInvalid(),
521 /* do_rtp= */ true)) {
522 return false;
523 }
524 AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor);
525 // Add dependency due to devirtualization: we are assuming the resolved method
526 // has a single implementation.
527 outermost_graph_->AddCHASingleImplementationDependency(invoke_instruction->GetResolvedMethod());
528 MaybeRecordStat(stats_, MethodCompilationStat::kCHAInline);
529 return true;
530 }
531
UseOnlyPolymorphicInliningWithNoDeopt()532 bool HInliner::UseOnlyPolymorphicInliningWithNoDeopt() {
533 // If we are compiling AOT or OSR, pretend the call using inline caches is polymorphic and
534 // do not generate a deopt.
535 //
536 // For AOT:
537 // Generating a deopt does not ensure that we will actually capture the new types;
538 // and the danger is that we could be stuck in a loop with "forever" deoptimizations.
539 // Take for example the following scenario:
540 // - we capture the inline cache in one run
541 // - the next run, we deoptimize because we miss a type check, but the method
542 // never becomes hot again
543 // In this case, the inline cache will not be updated in the profile and the AOT code
544 // will keep deoptimizing.
545 // Another scenario is if we use profile compilation for a process which is not allowed
546 // to JIT (e.g. system server). If we deoptimize we will run interpreted code for the
547 // rest of the lifetime.
548 // TODO(calin):
549 // This is a compromise because we will most likely never update the inline cache
550 // in the profile (unless there's another reason to deopt). So we might be stuck with
551 // a sub-optimal inline cache.
552 // We could be smarter when capturing inline caches to mitigate this.
553 // (e.g. by having different thresholds for new and old methods).
554 //
555 // For OSR:
556 // We may come from the interpreter and it may have seen different receiver types.
557 return Runtime::Current()->IsAotCompiler() || outermost_graph_->IsCompilingOsr();
558 }
TryInlineFromInlineCache(HInvoke * invoke_instruction)559 bool HInliner::TryInlineFromInlineCache(HInvoke* invoke_instruction)
560 REQUIRES_SHARED(Locks::mutator_lock_) {
561 if (Runtime::Current()->IsAotCompiler() && !kUseAOTInlineCaches) {
562 return false;
563 }
564
565 StackHandleScope<InlineCache::kIndividualCacheSize> classes(Thread::Current());
566 // The Zygote JIT compiles based on a profile, so we shouldn't use runtime inline caches
567 // for it.
568 InlineCacheType inline_cache_type =
569 (Runtime::Current()->IsAotCompiler() || Runtime::Current()->IsZygote())
570 ? GetInlineCacheAOT(invoke_instruction, &classes)
571 : GetInlineCacheJIT(invoke_instruction, &classes);
572
573 switch (inline_cache_type) {
574 case kInlineCacheNoData: {
575 LOG_FAIL_NO_STAT()
576 << "No inline cache information for call to "
577 << invoke_instruction->GetMethodReference().PrettyMethod();
578 return false;
579 }
580
581 case kInlineCacheUninitialized: {
582 LOG_FAIL_NO_STAT()
583 << "Interface or virtual call to "
584 << invoke_instruction->GetMethodReference().PrettyMethod()
585 << " is not hit and not inlined";
586 return false;
587 }
588
589 case kInlineCacheMonomorphic: {
590 MaybeRecordStat(stats_, MethodCompilationStat::kMonomorphicCall);
591 if (UseOnlyPolymorphicInliningWithNoDeopt()) {
592 return TryInlinePolymorphicCall(invoke_instruction, classes);
593 } else {
594 return TryInlineMonomorphicCall(invoke_instruction, classes);
595 }
596 }
597
598 case kInlineCachePolymorphic: {
599 MaybeRecordStat(stats_, MethodCompilationStat::kPolymorphicCall);
600 return TryInlinePolymorphicCall(invoke_instruction, classes);
601 }
602
603 case kInlineCacheMegamorphic: {
604 LOG_FAIL_NO_STAT()
605 << "Interface or virtual call to "
606 << invoke_instruction->GetMethodReference().PrettyMethod()
607 << " is megamorphic and not inlined";
608 MaybeRecordStat(stats_, MethodCompilationStat::kMegamorphicCall);
609 return false;
610 }
611
612 case kInlineCacheMissingTypes: {
613 LOG_FAIL_NO_STAT()
614 << "Interface or virtual call to "
615 << invoke_instruction->GetMethodReference().PrettyMethod()
616 << " is missing types and not inlined";
617 return false;
618 }
619 }
620 UNREACHABLE();
621 }
622
GetInlineCacheJIT(HInvoke * invoke_instruction,StackHandleScope<InlineCache::kIndividualCacheSize> * classes)623 HInliner::InlineCacheType HInliner::GetInlineCacheJIT(
624 HInvoke* invoke_instruction,
625 /*out*/StackHandleScope<InlineCache::kIndividualCacheSize>* classes) {
626 DCHECK(codegen_->GetCompilerOptions().IsJitCompiler());
627
628 ArtMethod* caller = graph_->GetArtMethod();
629 // Under JIT, we should always know the caller.
630 DCHECK(caller != nullptr);
631 ProfilingInfo* profiling_info = graph_->GetProfilingInfo();
632 if (profiling_info == nullptr) {
633 return kInlineCacheNoData;
634 }
635
636 Runtime::Current()->GetJit()->GetCodeCache()->CopyInlineCacheInto(
637 *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()),
638 classes);
639 return GetInlineCacheType(*classes);
640 }
641
GetInlineCacheAOT(HInvoke * invoke_instruction,StackHandleScope<InlineCache::kIndividualCacheSize> * classes)642 HInliner::InlineCacheType HInliner::GetInlineCacheAOT(
643 HInvoke* invoke_instruction,
644 /*out*/StackHandleScope<InlineCache::kIndividualCacheSize>* classes) {
645 DCHECK_EQ(classes->NumberOfReferences(), InlineCache::kIndividualCacheSize);
646 DCHECK_EQ(classes->RemainingSlots(), InlineCache::kIndividualCacheSize);
647
648 const ProfileCompilationInfo* pci = codegen_->GetCompilerOptions().GetProfileCompilationInfo();
649 if (pci == nullptr) {
650 return kInlineCacheNoData;
651 }
652
653 ProfileCompilationInfo::MethodHotness hotness = pci->GetMethodHotness(MethodReference(
654 caller_compilation_unit_.GetDexFile(), caller_compilation_unit_.GetDexMethodIndex()));
655 if (!hotness.IsHot()) {
656 return kInlineCacheNoData; // no profile information for this invocation.
657 }
658
659 const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
660 DCHECK(inline_caches != nullptr);
661 const auto it = inline_caches->find(invoke_instruction->GetDexPc());
662 if (it == inline_caches->end()) {
663 return kInlineCacheUninitialized;
664 }
665
666 const ProfileCompilationInfo::DexPcData& dex_pc_data = it->second;
667 if (dex_pc_data.is_missing_types) {
668 return kInlineCacheMissingTypes;
669 }
670 if (dex_pc_data.is_megamorphic) {
671 return kInlineCacheMegamorphic;
672 }
673 DCHECK_LE(dex_pc_data.classes.size(), InlineCache::kIndividualCacheSize);
674
675 // Walk over the class descriptors and look up the actual classes.
676 // If we cannot find a type we return kInlineCacheMissingTypes.
677 ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
678 for (const dex::TypeIndex& type_index : dex_pc_data.classes) {
679 const DexFile* dex_file = caller_compilation_unit_.GetDexFile();
680 const char* descriptor = pci->GetTypeDescriptor(dex_file, type_index);
681 ObjPtr<mirror::ClassLoader> class_loader = caller_compilation_unit_.GetClassLoader().Get();
682 ObjPtr<mirror::Class> clazz = class_linker->LookupResolvedType(descriptor, class_loader);
683 if (clazz == nullptr) {
684 VLOG(compiler) << "Could not find class from inline cache in AOT mode "
685 << invoke_instruction->GetMethodReference().PrettyMethod()
686 << " : "
687 << descriptor;
688 return kInlineCacheMissingTypes;
689 }
690 DCHECK_NE(classes->RemainingSlots(), 0u);
691 classes->NewHandle(clazz);
692 }
693
694 return GetInlineCacheType(*classes);
695 }
696
BuildGetReceiverClass(ClassLinker * class_linker,HInstruction * receiver,uint32_t dex_pc) const697 HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
698 HInstruction* receiver,
699 uint32_t dex_pc) const {
700 ArtField* field = GetClassRoot<mirror::Object>(class_linker)->GetInstanceField(0);
701 DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
702 HInstanceFieldGet* result = new (graph_->GetAllocator()) HInstanceFieldGet(
703 receiver,
704 field,
705 DataType::Type::kReference,
706 field->GetOffset(),
707 field->IsVolatile(),
708 field->GetDexFieldIndex(),
709 field->GetDeclaringClass()->GetDexClassDefIndex(),
710 *field->GetDexFile(),
711 dex_pc);
712 // The class of a field is effectively final, and does not have any memory dependencies.
713 result->SetSideEffects(SideEffects::None());
714 return result;
715 }
716
ResolveMethodFromInlineCache(Handle<mirror::Class> klass,HInvoke * invoke_instruction,PointerSize pointer_size)717 static ArtMethod* ResolveMethodFromInlineCache(Handle<mirror::Class> klass,
718 HInvoke* invoke_instruction,
719 PointerSize pointer_size)
720 REQUIRES_SHARED(Locks::mutator_lock_) {
721 ArtMethod* resolved_method = invoke_instruction->GetResolvedMethod();
722 if (Runtime::Current()->IsAotCompiler()) {
723 // We can get unrelated types when working with profiles (corruption,
724 // systme updates, or anyone can write to it). So first check if the class
725 // actually implements the declaring class of the method that is being
726 // called in bytecode.
727 // Note: the lookup methods used below require to have assignable types.
728 if (!resolved_method->GetDeclaringClass()->IsAssignableFrom(klass.Get())) {
729 return nullptr;
730 }
731
732 // Also check whether the type in the inline cache is an interface or an
733 // abstract class. We only expect concrete classes in inline caches, so this
734 // means the class was changed.
735 if (klass->IsAbstract() || klass->IsInterface()) {
736 return nullptr;
737 }
738 }
739
740 if (invoke_instruction->IsInvokeInterface()) {
741 resolved_method = klass->FindVirtualMethodForInterface(resolved_method, pointer_size);
742 } else {
743 DCHECK(invoke_instruction->IsInvokeVirtual());
744 resolved_method = klass->FindVirtualMethodForVirtual(resolved_method, pointer_size);
745 }
746 // Even if the class exists we can still not have the function the
747 // inline-cache targets if the profile is from far enough in the past/future.
748 // We need to allow this since we don't update boot-profiles very often. This
749 // can occur in boot-profiles with inline-caches.
750 DCHECK(Runtime::Current()->IsAotCompiler() || resolved_method != nullptr);
751 return resolved_method;
752 }
753
TryInlineMonomorphicCall(HInvoke * invoke_instruction,const StackHandleScope<InlineCache::kIndividualCacheSize> & classes)754 bool HInliner::TryInlineMonomorphicCall(
755 HInvoke* invoke_instruction,
756 const StackHandleScope<InlineCache::kIndividualCacheSize>& classes) {
757 DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
758 << invoke_instruction->DebugName();
759
760 dex::TypeIndex class_index = FindClassIndexIn(
761 GetMonomorphicType(classes), caller_compilation_unit_);
762 if (!class_index.IsValid()) {
763 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCacheInaccessibleToCaller)
764 << "Call to " << ArtMethod::PrettyMethod(invoke_instruction->GetResolvedMethod())
765 << " from inline cache is not inlined because its class is not"
766 << " accessible to the caller";
767 return false;
768 }
769
770 ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
771 PointerSize pointer_size = class_linker->GetImagePointerSize();
772 Handle<mirror::Class> monomorphic_type =
773 graph_->GetHandleCache()->NewHandle(GetMonomorphicType(classes));
774 ArtMethod* resolved_method = ResolveMethodFromInlineCache(
775 monomorphic_type, invoke_instruction, pointer_size);
776 if (resolved_method == nullptr) {
777 // Bogus AOT profile, bail.
778 DCHECK(Runtime::Current()->IsAotCompiler());
779 return false;
780 }
781
782 LOG_NOTE() << "Try inline monomorphic call to " << resolved_method->PrettyMethod();
783 HInstruction* receiver = invoke_instruction->InputAt(0);
784 HInstruction* cursor = invoke_instruction->GetPrevious();
785 HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
786 if (!TryInlineAndReplace(invoke_instruction,
787 resolved_method,
788 ReferenceTypeInfo::Create(monomorphic_type, /* is_exact= */ true),
789 /* do_rtp= */ false)) {
790 return false;
791 }
792
793 // We successfully inlined, now add a guard.
794 AddTypeGuard(receiver,
795 cursor,
796 bb_cursor,
797 class_index,
798 monomorphic_type,
799 invoke_instruction,
800 /* with_deoptimization= */ true);
801
802 // Run type propagation to get the guard typed, and eventually propagate the
803 // type of the receiver.
804 ReferenceTypePropagation rtp_fixup(graph_,
805 outer_compilation_unit_.GetClassLoader(),
806 outer_compilation_unit_.GetDexCache(),
807 /* is_first_run= */ false);
808 rtp_fixup.Run();
809
810 MaybeRecordStat(stats_, MethodCompilationStat::kInlinedMonomorphicCall);
811 return true;
812 }
813
AddCHAGuard(HInstruction * invoke_instruction,uint32_t dex_pc,HInstruction * cursor,HBasicBlock * bb_cursor)814 void HInliner::AddCHAGuard(HInstruction* invoke_instruction,
815 uint32_t dex_pc,
816 HInstruction* cursor,
817 HBasicBlock* bb_cursor) {
818 HShouldDeoptimizeFlag* deopt_flag = new (graph_->GetAllocator())
819 HShouldDeoptimizeFlag(graph_->GetAllocator(), dex_pc);
820 // ShouldDeoptimizeFlag is used to perform a deoptimization because of a CHA
821 // invalidation or for debugging reasons. It is OK to just check for non-zero
822 // value here instead of the specific CHA value. When a debugging deopt is
823 // requested we deoptimize before we execute any code and hence we shouldn't
824 // see that case here.
825 HInstruction* compare = new (graph_->GetAllocator()) HNotEqual(
826 deopt_flag, graph_->GetIntConstant(0, dex_pc));
827 HInstruction* deopt = new (graph_->GetAllocator()) HDeoptimize(
828 graph_->GetAllocator(), compare, DeoptimizationKind::kCHA, dex_pc);
829
830 if (cursor != nullptr) {
831 bb_cursor->InsertInstructionAfter(deopt_flag, cursor);
832 } else {
833 bb_cursor->InsertInstructionBefore(deopt_flag, bb_cursor->GetFirstInstruction());
834 }
835 bb_cursor->InsertInstructionAfter(compare, deopt_flag);
836 bb_cursor->InsertInstructionAfter(deopt, compare);
837
838 // Add receiver as input to aid CHA guard optimization later.
839 deopt_flag->AddInput(invoke_instruction->InputAt(0));
840 DCHECK_EQ(deopt_flag->InputCount(), 1u);
841 deopt->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
842 outermost_graph_->IncrementNumberOfCHAGuards();
843 }
844
AddTypeGuard(HInstruction * receiver,HInstruction * cursor,HBasicBlock * bb_cursor,dex::TypeIndex class_index,Handle<mirror::Class> klass,HInstruction * invoke_instruction,bool with_deoptimization)845 HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
846 HInstruction* cursor,
847 HBasicBlock* bb_cursor,
848 dex::TypeIndex class_index,
849 Handle<mirror::Class> klass,
850 HInstruction* invoke_instruction,
851 bool with_deoptimization) {
852 ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
853 HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
854 class_linker, receiver, invoke_instruction->GetDexPc());
855 if (cursor != nullptr) {
856 bb_cursor->InsertInstructionAfter(receiver_class, cursor);
857 } else {
858 bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
859 }
860
861 const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
862 bool is_referrer;
863 ArtMethod* outermost_art_method = outermost_graph_->GetArtMethod();
864 if (outermost_art_method == nullptr) {
865 DCHECK(Runtime::Current()->IsAotCompiler());
866 // We are in AOT mode and we don't have an ART method to determine
867 // if the inlined method belongs to the referrer. Assume it doesn't.
868 is_referrer = false;
869 } else {
870 is_referrer = klass.Get() == outermost_art_method->GetDeclaringClass();
871 }
872
873 // Note that we will just compare the classes, so we don't need Java semantics access checks.
874 // Note that the type index and the dex file are relative to the method this type guard is
875 // inlined into.
876 HLoadClass* load_class = new (graph_->GetAllocator()) HLoadClass(graph_->GetCurrentMethod(),
877 class_index,
878 caller_dex_file,
879 klass,
880 is_referrer,
881 invoke_instruction->GetDexPc(),
882 /* needs_access_check= */ false);
883 HLoadClass::LoadKind kind = HSharpening::ComputeLoadClassKind(
884 load_class, codegen_, caller_compilation_unit_);
885 DCHECK(kind != HLoadClass::LoadKind::kInvalid)
886 << "We should always be able to reference a class for inline caches";
887 // Load kind must be set before inserting the instruction into the graph.
888 load_class->SetLoadKind(kind);
889 bb_cursor->InsertInstructionAfter(load_class, receiver_class);
890 // In AOT mode, we will most likely load the class from BSS, which will involve a call
891 // to the runtime. In this case, the load instruction will need an environment so copy
892 // it from the invoke instruction.
893 if (load_class->NeedsEnvironment()) {
894 DCHECK(Runtime::Current()->IsAotCompiler());
895 load_class->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
896 }
897
898 HNotEqual* compare = new (graph_->GetAllocator()) HNotEqual(load_class, receiver_class);
899 bb_cursor->InsertInstructionAfter(compare, load_class);
900 if (with_deoptimization) {
901 HDeoptimize* deoptimize = new (graph_->GetAllocator()) HDeoptimize(
902 graph_->GetAllocator(),
903 compare,
904 receiver,
905 Runtime::Current()->IsAotCompiler()
906 ? DeoptimizationKind::kAotInlineCache
907 : DeoptimizationKind::kJitInlineCache,
908 invoke_instruction->GetDexPc());
909 bb_cursor->InsertInstructionAfter(deoptimize, compare);
910 deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
911 DCHECK_EQ(invoke_instruction->InputAt(0), receiver);
912 receiver->ReplaceUsesDominatedBy(deoptimize, deoptimize);
913 deoptimize->SetReferenceTypeInfo(receiver->GetReferenceTypeInfo());
914 }
915 return compare;
916 }
917
MaybeReplaceAndRemove(HInstruction * new_instruction,HInstruction * old_instruction)918 static void MaybeReplaceAndRemove(HInstruction* new_instruction, HInstruction* old_instruction) {
919 DCHECK(new_instruction != old_instruction);
920 if (new_instruction != nullptr) {
921 old_instruction->ReplaceWith(new_instruction);
922 }
923 old_instruction->GetBlock()->RemoveInstruction(old_instruction);
924 }
925
TryInlinePolymorphicCall(HInvoke * invoke_instruction,const StackHandleScope<InlineCache::kIndividualCacheSize> & classes)926 bool HInliner::TryInlinePolymorphicCall(
927 HInvoke* invoke_instruction,
928 const StackHandleScope<InlineCache::kIndividualCacheSize>& classes) {
929 DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
930 << invoke_instruction->DebugName();
931
932 if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, classes)) {
933 return true;
934 }
935
936 ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
937 PointerSize pointer_size = class_linker->GetImagePointerSize();
938
939 bool all_targets_inlined = true;
940 bool one_target_inlined = false;
941 DCHECK_EQ(classes.NumberOfReferences(), InlineCache::kIndividualCacheSize);
942 uint8_t number_of_types = InlineCache::kIndividualCacheSize - classes.RemainingSlots();
943 for (size_t i = 0; i != number_of_types; ++i) {
944 DCHECK(classes.GetReference(i) != nullptr);
945 Handle<mirror::Class> handle =
946 graph_->GetHandleCache()->NewHandle(classes.GetReference(i)->AsClass());
947 ArtMethod* method = ResolveMethodFromInlineCache(handle, invoke_instruction, pointer_size);
948 if (method == nullptr) {
949 DCHECK(Runtime::Current()->IsAotCompiler());
950 // AOT profile is bogus. This loop expects to iterate over all entries,
951 // so just just continue.
952 all_targets_inlined = false;
953 continue;
954 }
955
956 HInstruction* receiver = invoke_instruction->InputAt(0);
957 HInstruction* cursor = invoke_instruction->GetPrevious();
958 HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
959
960 dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_);
961 HInstruction* return_replacement = nullptr;
962
963 // In monomorphic cases when UseOnlyPolymorphicInliningWithNoDeopt() is true, we call
964 // `TryInlinePolymorphicCall` even though we are monomorphic.
965 const bool actually_monomorphic = number_of_types == 1;
966 DCHECK_IMPLIES(actually_monomorphic, UseOnlyPolymorphicInliningWithNoDeopt());
967
968 // We only want to limit recursive polymorphic cases, not monomorphic ones.
969 const bool too_many_polymorphic_recursive_calls =
970 !actually_monomorphic &&
971 CountRecursiveCallsOf(method) > kMaximumNumberOfPolymorphicRecursiveCalls;
972 if (too_many_polymorphic_recursive_calls) {
973 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedPolymorphicRecursiveBudget)
974 << "Method " << method->PrettyMethod()
975 << " is not inlined because it has reached its polymorphic recursive call budget.";
976 } else if (class_index.IsValid()) {
977 LOG_NOTE() << "Try inline polymorphic call to " << method->PrettyMethod();
978 }
979
980 if (too_many_polymorphic_recursive_calls ||
981 !class_index.IsValid() ||
982 !TryBuildAndInline(invoke_instruction,
983 method,
984 ReferenceTypeInfo::Create(handle, /* is_exact= */ true),
985 &return_replacement)) {
986 all_targets_inlined = false;
987 } else {
988 one_target_inlined = true;
989
990 LOG_SUCCESS() << "Polymorphic call to "
991 << invoke_instruction->GetMethodReference().PrettyMethod()
992 << " has inlined " << ArtMethod::PrettyMethod(method);
993
994 // If we have inlined all targets before, and this receiver is the last seen,
995 // we deoptimize instead of keeping the original invoke instruction.
996 bool deoptimize = !UseOnlyPolymorphicInliningWithNoDeopt() &&
997 all_targets_inlined &&
998 (i + 1 == number_of_types);
999
1000 HInstruction* compare = AddTypeGuard(receiver,
1001 cursor,
1002 bb_cursor,
1003 class_index,
1004 handle,
1005 invoke_instruction,
1006 deoptimize);
1007 if (deoptimize) {
1008 MaybeReplaceAndRemove(return_replacement, invoke_instruction);
1009 } else {
1010 CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
1011 }
1012 }
1013 }
1014
1015 if (!one_target_inlined) {
1016 LOG_FAIL_NO_STAT()
1017 << "Call to " << invoke_instruction->GetMethodReference().PrettyMethod()
1018 << " from inline cache is not inlined because none"
1019 << " of its targets could be inlined";
1020 return false;
1021 }
1022
1023 MaybeRecordStat(stats_, MethodCompilationStat::kInlinedPolymorphicCall);
1024
1025 // Run type propagation to get the guards typed.
1026 ReferenceTypePropagation rtp_fixup(graph_,
1027 outer_compilation_unit_.GetClassLoader(),
1028 outer_compilation_unit_.GetDexCache(),
1029 /* is_first_run= */ false);
1030 rtp_fixup.Run();
1031 return true;
1032 }
1033
CreateDiamondPatternForPolymorphicInline(HInstruction * compare,HInstruction * return_replacement,HInstruction * invoke_instruction)1034 void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
1035 HInstruction* return_replacement,
1036 HInstruction* invoke_instruction) {
1037 uint32_t dex_pc = invoke_instruction->GetDexPc();
1038 HBasicBlock* cursor_block = compare->GetBlock();
1039 HBasicBlock* original_invoke_block = invoke_instruction->GetBlock();
1040 ArenaAllocator* allocator = graph_->GetAllocator();
1041
1042 // Spit the block after the compare: `cursor_block` will now be the start of the diamond,
1043 // and the returned block is the start of the then branch (that could contain multiple blocks).
1044 HBasicBlock* then = cursor_block->SplitAfterForInlining(compare);
1045
1046 // Split the block containing the invoke before and after the invoke. The returned block
1047 // of the split before will contain the invoke and will be the otherwise branch of
1048 // the diamond. The returned block of the split after will be the merge block
1049 // of the diamond.
1050 HBasicBlock* end_then = invoke_instruction->GetBlock();
1051 HBasicBlock* otherwise = end_then->SplitBeforeForInlining(invoke_instruction);
1052 HBasicBlock* merge = otherwise->SplitAfterForInlining(invoke_instruction);
1053
1054 // If the methods we are inlining return a value, we create a phi in the merge block
1055 // that will have the `invoke_instruction and the `return_replacement` as inputs.
1056 if (return_replacement != nullptr) {
1057 HPhi* phi = new (allocator) HPhi(
1058 allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke_instruction->GetType()), dex_pc);
1059 merge->AddPhi(phi);
1060 invoke_instruction->ReplaceWith(phi);
1061 phi->AddInput(return_replacement);
1062 phi->AddInput(invoke_instruction);
1063 }
1064
1065 // Add the control flow instructions.
1066 otherwise->AddInstruction(new (allocator) HGoto(dex_pc));
1067 end_then->AddInstruction(new (allocator) HGoto(dex_pc));
1068 cursor_block->AddInstruction(new (allocator) HIf(compare, dex_pc));
1069
1070 // Add the newly created blocks to the graph.
1071 graph_->AddBlock(then);
1072 graph_->AddBlock(otherwise);
1073 graph_->AddBlock(merge);
1074
1075 // Set up successor (and implictly predecessor) relations.
1076 cursor_block->AddSuccessor(otherwise);
1077 cursor_block->AddSuccessor(then);
1078 end_then->AddSuccessor(merge);
1079 otherwise->AddSuccessor(merge);
1080
1081 // Set up dominance information.
1082 then->SetDominator(cursor_block);
1083 cursor_block->AddDominatedBlock(then);
1084 otherwise->SetDominator(cursor_block);
1085 cursor_block->AddDominatedBlock(otherwise);
1086 merge->SetDominator(cursor_block);
1087 cursor_block->AddDominatedBlock(merge);
1088
1089 // Update the revert post order.
1090 size_t index = IndexOfElement(graph_->reverse_post_order_, cursor_block);
1091 MakeRoomFor(&graph_->reverse_post_order_, 1, index);
1092 graph_->reverse_post_order_[++index] = then;
1093 index = IndexOfElement(graph_->reverse_post_order_, end_then);
1094 MakeRoomFor(&graph_->reverse_post_order_, 2, index);
1095 graph_->reverse_post_order_[++index] = otherwise;
1096 graph_->reverse_post_order_[++index] = merge;
1097
1098
1099 graph_->UpdateLoopAndTryInformationOfNewBlock(
1100 then, original_invoke_block, /* replace_if_back_edge= */ false);
1101 graph_->UpdateLoopAndTryInformationOfNewBlock(
1102 otherwise, original_invoke_block, /* replace_if_back_edge= */ false);
1103
1104 // In case the original invoke location was a back edge, we need to update
1105 // the loop to now have the merge block as a back edge.
1106 graph_->UpdateLoopAndTryInformationOfNewBlock(
1107 merge, original_invoke_block, /* replace_if_back_edge= */ true);
1108 }
1109
TryInlinePolymorphicCallToSameTarget(HInvoke * invoke_instruction,const StackHandleScope<InlineCache::kIndividualCacheSize> & classes)1110 bool HInliner::TryInlinePolymorphicCallToSameTarget(
1111 HInvoke* invoke_instruction,
1112 const StackHandleScope<InlineCache::kIndividualCacheSize>& classes) {
1113 // This optimization only works under JIT for now.
1114 if (!codegen_->GetCompilerOptions().IsJitCompiler()) {
1115 return false;
1116 }
1117
1118 ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
1119 PointerSize pointer_size = class_linker->GetImagePointerSize();
1120
1121 ArtMethod* actual_method = nullptr;
1122 size_t method_index = invoke_instruction->IsInvokeVirtual()
1123 ? invoke_instruction->AsInvokeVirtual()->GetVTableIndex()
1124 : invoke_instruction->AsInvokeInterface()->GetImtIndex();
1125
1126 // Check whether we are actually calling the same method among
1127 // the different types seen.
1128 DCHECK_EQ(classes.NumberOfReferences(), InlineCache::kIndividualCacheSize);
1129 uint8_t number_of_types = InlineCache::kIndividualCacheSize - classes.RemainingSlots();
1130 for (size_t i = 0; i != number_of_types; ++i) {
1131 DCHECK(classes.GetReference(i) != nullptr);
1132 ArtMethod* new_method = nullptr;
1133 if (invoke_instruction->IsInvokeInterface()) {
1134 new_method = classes.GetReference(i)->AsClass()->GetImt(pointer_size)->Get(
1135 method_index, pointer_size);
1136 if (new_method->IsRuntimeMethod()) {
1137 // Bail out as soon as we see a conflict trampoline in one of the target's
1138 // interface table.
1139 return false;
1140 }
1141 } else {
1142 DCHECK(invoke_instruction->IsInvokeVirtual());
1143 new_method =
1144 classes.GetReference(i)->AsClass()->GetEmbeddedVTableEntry(method_index, pointer_size);
1145 }
1146 DCHECK(new_method != nullptr);
1147 if (actual_method == nullptr) {
1148 actual_method = new_method;
1149 } else if (actual_method != new_method) {
1150 // Different methods, bailout.
1151 return false;
1152 }
1153 }
1154
1155 HInstruction* receiver = invoke_instruction->InputAt(0);
1156 HInstruction* cursor = invoke_instruction->GetPrevious();
1157 HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
1158
1159 HInstruction* return_replacement = nullptr;
1160 if (!TryBuildAndInline(invoke_instruction,
1161 actual_method,
1162 ReferenceTypeInfo::CreateInvalid(),
1163 &return_replacement)) {
1164 return false;
1165 }
1166
1167 // We successfully inlined, now add a guard.
1168 HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
1169 class_linker, receiver, invoke_instruction->GetDexPc());
1170
1171 DataType::Type type = Is64BitInstructionSet(graph_->GetInstructionSet())
1172 ? DataType::Type::kInt64
1173 : DataType::Type::kInt32;
1174 HClassTableGet* class_table_get = new (graph_->GetAllocator()) HClassTableGet(
1175 receiver_class,
1176 type,
1177 invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable
1178 : HClassTableGet::TableKind::kIMTable,
1179 method_index,
1180 invoke_instruction->GetDexPc());
1181
1182 HConstant* constant;
1183 if (type == DataType::Type::kInt64) {
1184 constant = graph_->GetLongConstant(
1185 reinterpret_cast<intptr_t>(actual_method), invoke_instruction->GetDexPc());
1186 } else {
1187 constant = graph_->GetIntConstant(
1188 reinterpret_cast<intptr_t>(actual_method), invoke_instruction->GetDexPc());
1189 }
1190
1191 HNotEqual* compare = new (graph_->GetAllocator()) HNotEqual(class_table_get, constant);
1192 if (cursor != nullptr) {
1193 bb_cursor->InsertInstructionAfter(receiver_class, cursor);
1194 } else {
1195 bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
1196 }
1197 bb_cursor->InsertInstructionAfter(class_table_get, receiver_class);
1198 bb_cursor->InsertInstructionAfter(compare, class_table_get);
1199
1200 if (outermost_graph_->IsCompilingOsr()) {
1201 CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
1202 } else {
1203 HDeoptimize* deoptimize = new (graph_->GetAllocator()) HDeoptimize(
1204 graph_->GetAllocator(),
1205 compare,
1206 receiver,
1207 DeoptimizationKind::kJitSameTarget,
1208 invoke_instruction->GetDexPc());
1209 bb_cursor->InsertInstructionAfter(deoptimize, compare);
1210 deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
1211 MaybeReplaceAndRemove(return_replacement, invoke_instruction);
1212 receiver->ReplaceUsesDominatedBy(deoptimize, deoptimize);
1213 deoptimize->SetReferenceTypeInfo(receiver->GetReferenceTypeInfo());
1214 }
1215
1216 // Run type propagation to get the guard typed.
1217 ReferenceTypePropagation rtp_fixup(graph_,
1218 outer_compilation_unit_.GetClassLoader(),
1219 outer_compilation_unit_.GetDexCache(),
1220 /* is_first_run= */ false);
1221 rtp_fixup.Run();
1222
1223 MaybeRecordStat(stats_, MethodCompilationStat::kInlinedPolymorphicCall);
1224
1225 LOG_SUCCESS() << "Inlined same polymorphic target " << actual_method->PrettyMethod();
1226 return true;
1227 }
1228
MaybeRunReferenceTypePropagation(HInstruction * replacement,HInvoke * invoke_instruction)1229 void HInliner::MaybeRunReferenceTypePropagation(HInstruction* replacement,
1230 HInvoke* invoke_instruction) {
1231 if (ReturnTypeMoreSpecific(replacement, invoke_instruction)) {
1232 // Actual return value has a more specific type than the method's declared
1233 // return type. Run RTP again on the outer graph to propagate it.
1234 ReferenceTypePropagation(graph_,
1235 outer_compilation_unit_.GetClassLoader(),
1236 outer_compilation_unit_.GetDexCache(),
1237 /* is_first_run= */ false).Run();
1238 }
1239 }
1240
TryDevirtualize(HInvoke * invoke_instruction,ArtMethod * method,HInvoke ** replacement)1241 bool HInliner::TryDevirtualize(HInvoke* invoke_instruction,
1242 ArtMethod* method,
1243 HInvoke** replacement) {
1244 DCHECK(invoke_instruction != *replacement);
1245 if (!invoke_instruction->IsInvokeInterface() && !invoke_instruction->IsInvokeVirtual()) {
1246 return false;
1247 }
1248
1249 // Don't bother trying to call directly a default conflict method. It
1250 // doesn't have a proper MethodReference, but also `GetCanonicalMethod`
1251 // will return an actual default implementation.
1252 if (method->IsDefaultConflicting()) {
1253 return false;
1254 }
1255 DCHECK(!method->IsProxyMethod());
1256 ClassLinker* cl = Runtime::Current()->GetClassLinker();
1257 PointerSize pointer_size = cl->GetImagePointerSize();
1258 // The sharpening logic assumes the caller isn't passing a copied method.
1259 method = method->GetCanonicalMethod(pointer_size);
1260 uint32_t dex_method_index = FindMethodIndexIn(
1261 method,
1262 *invoke_instruction->GetMethodReference().dex_file,
1263 invoke_instruction->GetMethodReference().index);
1264 if (dex_method_index == dex::kDexNoIndex) {
1265 return false;
1266 }
1267 HInvokeStaticOrDirect::DispatchInfo dispatch_info =
1268 HSharpening::SharpenLoadMethod(method,
1269 /* has_method_id= */ true,
1270 /* for_interface_call= */ false,
1271 codegen_);
1272 DCHECK_NE(dispatch_info.code_ptr_location, CodePtrLocation::kCallCriticalNative);
1273 if (dispatch_info.method_load_kind == MethodLoadKind::kRuntimeCall) {
1274 // If sharpening returns that we need to load the method at runtime, keep
1275 // the virtual/interface call which will be faster.
1276 // Also, the entrypoints for runtime calls do not handle devirtualized
1277 // calls.
1278 return false;
1279 }
1280
1281 HInvokeStaticOrDirect* new_invoke = new (graph_->GetAllocator()) HInvokeStaticOrDirect(
1282 graph_->GetAllocator(),
1283 invoke_instruction->GetNumberOfArguments(),
1284 invoke_instruction->GetType(),
1285 invoke_instruction->GetDexPc(),
1286 MethodReference(invoke_instruction->GetMethodReference().dex_file, dex_method_index),
1287 method,
1288 dispatch_info,
1289 kDirect,
1290 MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
1291 HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
1292 HInputsRef inputs = invoke_instruction->GetInputs();
1293 DCHECK_EQ(inputs.size(), invoke_instruction->GetNumberOfArguments());
1294 for (size_t index = 0; index != inputs.size(); ++index) {
1295 new_invoke->SetArgumentAt(index, inputs[index]);
1296 }
1297 if (HInvokeStaticOrDirect::NeedsCurrentMethodInput(dispatch_info)) {
1298 new_invoke->SetRawInputAt(new_invoke->GetCurrentMethodIndexUnchecked(),
1299 graph_->GetCurrentMethod());
1300 }
1301 invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction);
1302 new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
1303 if (invoke_instruction->GetType() == DataType::Type::kReference) {
1304 new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo());
1305 }
1306 *replacement = new_invoke;
1307
1308 MaybeReplaceAndRemove(*replacement, invoke_instruction);
1309 // No need to call MaybeRunReferenceTypePropagation, as we know the return type
1310 // cannot be more specific.
1311 DCHECK(!ReturnTypeMoreSpecific(*replacement, invoke_instruction));
1312 return true;
1313 }
1314
1315
TryInlineAndReplace(HInvoke * invoke_instruction,ArtMethod * method,ReferenceTypeInfo receiver_type,bool do_rtp)1316 bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
1317 ArtMethod* method,
1318 ReferenceTypeInfo receiver_type,
1319 bool do_rtp) {
1320 DCHECK(!invoke_instruction->IsIntrinsic());
1321 HInstruction* return_replacement = nullptr;
1322
1323 if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) {
1324 return false;
1325 }
1326
1327 MaybeReplaceAndRemove(return_replacement, invoke_instruction);
1328 FixUpReturnReferenceType(method, return_replacement);
1329 if (do_rtp) {
1330 MaybeRunReferenceTypePropagation(return_replacement, invoke_instruction);
1331 }
1332 return true;
1333 }
1334
CountRecursiveCallsOf(ArtMethod * method) const1335 size_t HInliner::CountRecursiveCallsOf(ArtMethod* method) const {
1336 const HInliner* current = this;
1337 size_t count = 0;
1338 do {
1339 if (current->graph_->GetArtMethod() == method) {
1340 ++count;
1341 }
1342 current = current->parent_;
1343 } while (current != nullptr);
1344 return count;
1345 }
1346
MayInline(const CompilerOptions & compiler_options,const DexFile & inlined_from,const DexFile & inlined_into)1347 static inline bool MayInline(const CompilerOptions& compiler_options,
1348 const DexFile& inlined_from,
1349 const DexFile& inlined_into) {
1350 // We're not allowed to inline across dex files if we're the no-inline-from dex file.
1351 if (!IsSameDexFile(inlined_from, inlined_into) &&
1352 ContainsElement(compiler_options.GetNoInlineFromDexFile(), &inlined_from)) {
1353 return false;
1354 }
1355
1356 return true;
1357 }
1358
1359 // Returns whether inlining is allowed based on ART semantics.
IsInliningAllowed(ArtMethod * method,const CodeItemDataAccessor & accessor) const1360 bool HInliner::IsInliningAllowed(ArtMethod* method, const CodeItemDataAccessor& accessor) const {
1361 if (!accessor.HasCodeItem()) {
1362 LOG_FAIL_NO_STAT()
1363 << "Method " << method->PrettyMethod() << " is not inlined because it is native";
1364 return false;
1365 }
1366
1367 if (!method->IsCompilable()) {
1368 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotCompilable)
1369 << "Method " << method->PrettyMethod()
1370 << " has soft failures un-handled by the compiler, so it cannot be inlined";
1371 return false;
1372 }
1373
1374 if (!IsMethodVerified(method)) {
1375 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified)
1376 << "Method " << method->PrettyMethod()
1377 << " couldn't be verified, so it cannot be inlined";
1378 return false;
1379 }
1380
1381 return true;
1382 }
1383
1384 // Returns whether ART supports inlining this method.
1385 //
1386 // Some methods are not supported because they have features for which inlining
1387 // is not implemented. For example, we do not currently support inlining throw
1388 // instructions into a try block.
IsInliningSupported(const HInvoke * invoke_instruction,ArtMethod * method,const CodeItemDataAccessor & accessor) const1389 bool HInliner::IsInliningSupported(const HInvoke* invoke_instruction,
1390 ArtMethod* method,
1391 const CodeItemDataAccessor& accessor) const {
1392 if (method->IsProxyMethod()) {
1393 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedProxy)
1394 << "Method " << method->PrettyMethod()
1395 << " is not inlined because of unimplemented inline support for proxy methods.";
1396 return false;
1397 }
1398
1399 if (accessor.TriesSize() != 0) {
1400 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCallee)
1401 << "Method " << method->PrettyMethod() << " is not inlined because of try block";
1402 return false;
1403 }
1404
1405 if (invoke_instruction->IsInvokeStaticOrDirect() &&
1406 invoke_instruction->AsInvokeStaticOrDirect()->IsStaticWithImplicitClinitCheck()) {
1407 // Case of a static method that cannot be inlined because it implicitly
1408 // requires an initialization check of its declaring class.
1409 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCacheClinitCheck)
1410 << "Method " << method->PrettyMethod()
1411 << " is not inlined because it is static and requires a clinit"
1412 << " check that cannot be emitted due to Dex cache limitations";
1413 return false;
1414 }
1415
1416 return true;
1417 }
1418
1419 // Returns whether our resource limits allow inlining this method.
IsInliningBudgetAvailable(ArtMethod * method,const CodeItemDataAccessor & accessor) const1420 bool HInliner::IsInliningBudgetAvailable(ArtMethod* method,
1421 const CodeItemDataAccessor& accessor) const {
1422 if (CountRecursiveCallsOf(method) > kMaximumNumberOfRecursiveCalls) {
1423 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRecursiveBudget)
1424 << "Method "
1425 << method->PrettyMethod()
1426 << " is not inlined because it has reached its recursive call budget.";
1427 return false;
1428 }
1429
1430 size_t inline_max_code_units = codegen_->GetCompilerOptions().GetInlineMaxCodeUnits();
1431 if (accessor.InsnsSizeInCodeUnits() > inline_max_code_units) {
1432 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCodeItem)
1433 << "Method " << method->PrettyMethod()
1434 << " is not inlined because its code item is too big: "
1435 << accessor.InsnsSizeInCodeUnits()
1436 << " > "
1437 << inline_max_code_units;
1438 return false;
1439 }
1440
1441 return true;
1442 }
1443
TryBuildAndInline(HInvoke * invoke_instruction,ArtMethod * method,ReferenceTypeInfo receiver_type,HInstruction ** return_replacement)1444 bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
1445 ArtMethod* method,
1446 ReferenceTypeInfo receiver_type,
1447 HInstruction** return_replacement) {
1448 // If invoke_instruction is devirtualized to a different method, give intrinsics
1449 // another chance before we try to inline it.
1450 if (invoke_instruction->GetResolvedMethod() != method && method->IsIntrinsic()) {
1451 MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized);
1452 // For simplicity, always create a new instruction to replace the existing
1453 // invoke.
1454 HInvokeVirtual* new_invoke = new (graph_->GetAllocator()) HInvokeVirtual(
1455 graph_->GetAllocator(),
1456 invoke_instruction->GetNumberOfArguments(),
1457 invoke_instruction->GetType(),
1458 invoke_instruction->GetDexPc(),
1459 invoke_instruction->GetMethodReference(), // Use existing invoke's method's reference.
1460 method,
1461 MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
1462 method->GetMethodIndex());
1463 DCHECK_NE(new_invoke->GetIntrinsic(), Intrinsics::kNone);
1464 HInputsRef inputs = invoke_instruction->GetInputs();
1465 for (size_t index = 0; index != inputs.size(); ++index) {
1466 new_invoke->SetArgumentAt(index, inputs[index]);
1467 }
1468 invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction);
1469 new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
1470 if (invoke_instruction->GetType() == DataType::Type::kReference) {
1471 new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo());
1472 }
1473 *return_replacement = new_invoke;
1474 return true;
1475 }
1476
1477 // Check whether we're allowed to inline. The outermost compilation unit is the relevant
1478 // dex file here (though the transitivity of an inline chain would allow checking the caller).
1479 if (!MayInline(codegen_->GetCompilerOptions(),
1480 *method->GetDexFile(),
1481 *outer_compilation_unit_.GetDexFile())) {
1482 if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) {
1483 LOG_SUCCESS() << "Successfully replaced pattern of invoke "
1484 << method->PrettyMethod();
1485 MaybeRecordStat(stats_, MethodCompilationStat::kReplacedInvokeWithSimplePattern);
1486 return true;
1487 }
1488 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedWont)
1489 << "Won't inline " << method->PrettyMethod() << " in "
1490 << outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
1491 << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
1492 << method->GetDexFile()->GetLocation();
1493 return false;
1494 }
1495
1496 CodeItemDataAccessor accessor(method->DexInstructionData());
1497
1498 if (!IsInliningAllowed(method, accessor)) {
1499 return false;
1500 }
1501
1502 if (!IsInliningSupported(invoke_instruction, method, accessor)) {
1503 return false;
1504 }
1505
1506 if (!IsInliningBudgetAvailable(method, accessor)) {
1507 return false;
1508 }
1509
1510 if (!TryBuildAndInlineHelper(
1511 invoke_instruction, method, receiver_type, return_replacement)) {
1512 return false;
1513 }
1514
1515 LOG_SUCCESS() << method->PrettyMethod();
1516 MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvoke);
1517 if (outermost_graph_ == graph_) {
1518 MaybeRecordStat(stats_, MethodCompilationStat::kInlinedLastInvoke);
1519 }
1520 return true;
1521 }
1522
GetInvokeInputForArgVRegIndex(HInvoke * invoke_instruction,size_t arg_vreg_index)1523 static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
1524 size_t arg_vreg_index)
1525 REQUIRES_SHARED(Locks::mutator_lock_) {
1526 size_t input_index = 0;
1527 for (size_t i = 0; i < arg_vreg_index; ++i, ++input_index) {
1528 DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
1529 if (DataType::Is64BitType(invoke_instruction->InputAt(input_index)->GetType())) {
1530 ++i;
1531 DCHECK_NE(i, arg_vreg_index);
1532 }
1533 }
1534 DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
1535 return invoke_instruction->InputAt(input_index);
1536 }
1537
1538 // Try to recognize known simple patterns and replace invoke call with appropriate instructions.
TryPatternSubstitution(HInvoke * invoke_instruction,ArtMethod * method,HInstruction ** return_replacement)1539 bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
1540 ArtMethod* method,
1541 HInstruction** return_replacement) {
1542 InlineMethod inline_method;
1543 if (!InlineMethodAnalyser::AnalyseMethodCode(method, &inline_method)) {
1544 return false;
1545 }
1546
1547 switch (inline_method.opcode) {
1548 case kInlineOpNop:
1549 DCHECK_EQ(invoke_instruction->GetType(), DataType::Type::kVoid);
1550 *return_replacement = nullptr;
1551 break;
1552 case kInlineOpReturnArg:
1553 *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
1554 inline_method.d.return_data.arg);
1555 break;
1556 case kInlineOpNonWideConst: {
1557 char shorty0 = method->GetShorty()[0];
1558 if (shorty0 == 'L') {
1559 DCHECK_EQ(inline_method.d.data, 0u);
1560 *return_replacement = graph_->GetNullConstant();
1561 } else if (shorty0 == 'F') {
1562 *return_replacement = graph_->GetFloatConstant(
1563 bit_cast<float, int32_t>(static_cast<int32_t>(inline_method.d.data)));
1564 } else {
1565 *return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
1566 }
1567 break;
1568 }
1569 case kInlineOpIGet: {
1570 const InlineIGetIPutData& data = inline_method.d.ifield_data;
1571 if (data.method_is_static || data.object_arg != 0u) {
1572 // TODO: Needs null check.
1573 return false;
1574 }
1575 HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
1576 HInstanceFieldGet* iget = CreateInstanceFieldGet(data.field_idx, method, obj);
1577 DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
1578 DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
1579 invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
1580 *return_replacement = iget;
1581 break;
1582 }
1583 case kInlineOpIPut: {
1584 const InlineIGetIPutData& data = inline_method.d.ifield_data;
1585 if (data.method_is_static || data.object_arg != 0u) {
1586 // TODO: Needs null check.
1587 return false;
1588 }
1589 HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
1590 HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
1591 HInstanceFieldSet* iput = CreateInstanceFieldSet(data.field_idx, method, obj, value);
1592 DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
1593 DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
1594 invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
1595 if (data.return_arg_plus1 != 0u) {
1596 size_t return_arg = data.return_arg_plus1 - 1u;
1597 *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
1598 }
1599 break;
1600 }
1601 case kInlineOpConstructor: {
1602 const InlineConstructorData& data = inline_method.d.constructor_data;
1603 // Get the indexes to arrays for easier processing.
1604 uint16_t iput_field_indexes[] = {
1605 data.iput0_field_index, data.iput1_field_index, data.iput2_field_index
1606 };
1607 uint16_t iput_args[] = { data.iput0_arg, data.iput1_arg, data.iput2_arg };
1608 static_assert(arraysize(iput_args) == arraysize(iput_field_indexes), "Size mismatch");
1609 // Count valid field indexes.
1610 size_t number_of_iputs = 0u;
1611 while (number_of_iputs != arraysize(iput_field_indexes) &&
1612 iput_field_indexes[number_of_iputs] != DexFile::kDexNoIndex16) {
1613 // Check that there are no duplicate valid field indexes.
1614 DCHECK_EQ(0, std::count(iput_field_indexes + number_of_iputs + 1,
1615 iput_field_indexes + arraysize(iput_field_indexes),
1616 iput_field_indexes[number_of_iputs]));
1617 ++number_of_iputs;
1618 }
1619 // Check that there are no valid field indexes in the rest of the array.
1620 DCHECK_EQ(0, std::count_if(iput_field_indexes + number_of_iputs,
1621 iput_field_indexes + arraysize(iput_field_indexes),
1622 [](uint16_t index) { return index != DexFile::kDexNoIndex16; }));
1623
1624 // Create HInstanceFieldSet for each IPUT that stores non-zero data.
1625 HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction,
1626 /* arg_vreg_index= */ 0u);
1627 bool needs_constructor_barrier = false;
1628 for (size_t i = 0; i != number_of_iputs; ++i) {
1629 HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, iput_args[i]);
1630 if (!value->IsConstant() || !value->AsConstant()->IsZeroBitPattern()) {
1631 uint16_t field_index = iput_field_indexes[i];
1632 bool is_final;
1633 HInstanceFieldSet* iput =
1634 CreateInstanceFieldSet(field_index, method, obj, value, &is_final);
1635 invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
1636
1637 // Check whether the field is final. If it is, we need to add a barrier.
1638 if (is_final) {
1639 needs_constructor_barrier = true;
1640 }
1641 }
1642 }
1643 if (needs_constructor_barrier) {
1644 // See DexCompilationUnit::RequiresConstructorBarrier for more details.
1645 DCHECK(obj != nullptr) << "only non-static methods can have a constructor fence";
1646
1647 HConstructorFence* constructor_fence =
1648 new (graph_->GetAllocator()) HConstructorFence(obj, kNoDexPc, graph_->GetAllocator());
1649 invoke_instruction->GetBlock()->InsertInstructionBefore(constructor_fence,
1650 invoke_instruction);
1651 }
1652 *return_replacement = nullptr;
1653 break;
1654 }
1655 default:
1656 LOG(FATAL) << "UNREACHABLE";
1657 UNREACHABLE();
1658 }
1659 return true;
1660 }
1661
CreateInstanceFieldGet(uint32_t field_index,ArtMethod * referrer,HInstruction * obj)1662 HInstanceFieldGet* HInliner::CreateInstanceFieldGet(uint32_t field_index,
1663 ArtMethod* referrer,
1664 HInstruction* obj)
1665 REQUIRES_SHARED(Locks::mutator_lock_) {
1666 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
1667 ArtField* resolved_field =
1668 class_linker->LookupResolvedField(field_index, referrer, /* is_static= */ false);
1669 DCHECK(resolved_field != nullptr);
1670 HInstanceFieldGet* iget = new (graph_->GetAllocator()) HInstanceFieldGet(
1671 obj,
1672 resolved_field,
1673 DataType::FromShorty(resolved_field->GetTypeDescriptor()[0]),
1674 resolved_field->GetOffset(),
1675 resolved_field->IsVolatile(),
1676 field_index,
1677 resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
1678 *referrer->GetDexFile(),
1679 // Read barrier generates a runtime call in slow path and we need a valid
1680 // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
1681 /* dex_pc= */ 0);
1682 if (iget->GetType() == DataType::Type::kReference) {
1683 // Use the same dex_cache that we used for field lookup as the hint_dex_cache.
1684 Handle<mirror::DexCache> dex_cache =
1685 graph_->GetHandleCache()->NewHandle(referrer->GetDexCache());
1686 ReferenceTypePropagation rtp(graph_,
1687 outer_compilation_unit_.GetClassLoader(),
1688 dex_cache,
1689 /* is_first_run= */ false);
1690 rtp.Visit(iget);
1691 }
1692 return iget;
1693 }
1694
CreateInstanceFieldSet(uint32_t field_index,ArtMethod * referrer,HInstruction * obj,HInstruction * value,bool * is_final)1695 HInstanceFieldSet* HInliner::CreateInstanceFieldSet(uint32_t field_index,
1696 ArtMethod* referrer,
1697 HInstruction* obj,
1698 HInstruction* value,
1699 bool* is_final)
1700 REQUIRES_SHARED(Locks::mutator_lock_) {
1701 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
1702 ArtField* resolved_field =
1703 class_linker->LookupResolvedField(field_index, referrer, /* is_static= */ false);
1704 DCHECK(resolved_field != nullptr);
1705 if (is_final != nullptr) {
1706 // This information is needed only for constructors.
1707 DCHECK(referrer->IsConstructor());
1708 *is_final = resolved_field->IsFinal();
1709 }
1710 HInstanceFieldSet* iput = new (graph_->GetAllocator()) HInstanceFieldSet(
1711 obj,
1712 value,
1713 resolved_field,
1714 DataType::FromShorty(resolved_field->GetTypeDescriptor()[0]),
1715 resolved_field->GetOffset(),
1716 resolved_field->IsVolatile(),
1717 field_index,
1718 resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
1719 *referrer->GetDexFile(),
1720 // Read barrier generates a runtime call in slow path and we need a valid
1721 // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
1722 /* dex_pc= */ 0);
1723 return iput;
1724 }
1725
1726 template <typename T>
NewHandleIfDifferent(ObjPtr<T> object,Handle<T> hint,HGraph * graph)1727 static inline Handle<T> NewHandleIfDifferent(ObjPtr<T> object, Handle<T> hint, HGraph* graph)
1728 REQUIRES_SHARED(Locks::mutator_lock_) {
1729 return (object != hint.Get()) ? graph->GetHandleCache()->NewHandle(object) : hint;
1730 }
1731
CanEncodeInlinedMethodInStackMap(const DexFile & outer_dex_file,ArtMethod * callee,const CodeGenerator * codegen,bool * out_needs_bss_check)1732 static bool CanEncodeInlinedMethodInStackMap(const DexFile& outer_dex_file,
1733 ArtMethod* callee,
1734 const CodeGenerator* codegen,
1735 bool* out_needs_bss_check)
1736 REQUIRES_SHARED(Locks::mutator_lock_) {
1737 if (!Runtime::Current()->IsAotCompiler()) {
1738 // JIT can always encode methods in stack maps.
1739 return true;
1740 }
1741
1742 const DexFile* dex_file = callee->GetDexFile();
1743 if (IsSameDexFile(outer_dex_file, *dex_file)) {
1744 return true;
1745 }
1746
1747 // Inline across dexfiles if the callee's DexFile is:
1748 // 1) in the bootclasspath, or
1749 if (callee->GetDeclaringClass()->IsBootStrapClassLoaded()) {
1750 // In multi-image, each BCP DexFile has their own OatWriter. Since they don't cooperate with
1751 // each other, we request the BSS check for them.
1752 // TODO(solanes, 154012332): Add .bss support for BCP multi-image.
1753 *out_needs_bss_check = codegen->GetCompilerOptions().IsMultiImage();
1754 return true;
1755 }
1756
1757 // 2) is a non-BCP dexfile with the OatFile we are compiling.
1758 if (codegen->GetCompilerOptions().WithinOatFile(dex_file)) {
1759 return true;
1760 }
1761
1762 // TODO(solanes): Support more AOT cases for inlining:
1763 // - methods in class loader context's DexFiles
1764 return false;
1765 }
1766
1767 // Substitutes parameters in the callee graph with their values from the caller.
SubstituteArguments(HGraph * callee_graph,HInvoke * invoke_instruction,ReferenceTypeInfo receiver_type,const DexCompilationUnit & dex_compilation_unit)1768 void HInliner::SubstituteArguments(HGraph* callee_graph,
1769 HInvoke* invoke_instruction,
1770 ReferenceTypeInfo receiver_type,
1771 const DexCompilationUnit& dex_compilation_unit) {
1772 ArtMethod* const resolved_method = callee_graph->GetArtMethod();
1773 size_t parameter_index = 0;
1774 bool run_rtp = false;
1775 for (HInstructionIterator instructions(callee_graph->GetEntryBlock()->GetInstructions());
1776 !instructions.Done();
1777 instructions.Advance()) {
1778 HInstruction* current = instructions.Current();
1779 if (current->IsParameterValue()) {
1780 HInstruction* argument = invoke_instruction->InputAt(parameter_index);
1781 if (argument->IsNullConstant()) {
1782 current->ReplaceWith(callee_graph->GetNullConstant());
1783 } else if (argument->IsIntConstant()) {
1784 current->ReplaceWith(callee_graph->GetIntConstant(argument->AsIntConstant()->GetValue()));
1785 } else if (argument->IsLongConstant()) {
1786 current->ReplaceWith(callee_graph->GetLongConstant(argument->AsLongConstant()->GetValue()));
1787 } else if (argument->IsFloatConstant()) {
1788 current->ReplaceWith(
1789 callee_graph->GetFloatConstant(argument->AsFloatConstant()->GetValue()));
1790 } else if (argument->IsDoubleConstant()) {
1791 current->ReplaceWith(
1792 callee_graph->GetDoubleConstant(argument->AsDoubleConstant()->GetValue()));
1793 } else if (argument->GetType() == DataType::Type::kReference) {
1794 if (!resolved_method->IsStatic() && parameter_index == 0 && receiver_type.IsValid()) {
1795 run_rtp = true;
1796 current->SetReferenceTypeInfo(receiver_type);
1797 } else {
1798 current->SetReferenceTypeInfo(argument->GetReferenceTypeInfo());
1799 }
1800 current->AsParameterValue()->SetCanBeNull(argument->CanBeNull());
1801 }
1802 ++parameter_index;
1803 }
1804 }
1805
1806 // We have replaced formal arguments with actual arguments. If actual types
1807 // are more specific than the declared ones, run RTP again on the inner graph.
1808 if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
1809 ReferenceTypePropagation(callee_graph,
1810 outer_compilation_unit_.GetClassLoader(),
1811 dex_compilation_unit.GetDexCache(),
1812 /* is_first_run= */ false).Run();
1813 }
1814 }
1815
1816 // Returns whether we can inline the callee_graph into the target_block.
1817 //
1818 // This performs a combination of semantics checks, compiler support checks, and
1819 // resource limit checks.
1820 //
1821 // If this function returns true, it will also set out_number_of_instructions to
1822 // the number of instructions in the inlined body.
CanInlineBody(const HGraph * callee_graph,const HBasicBlock * target_block,size_t * out_number_of_instructions) const1823 bool HInliner::CanInlineBody(const HGraph* callee_graph,
1824 const HBasicBlock* target_block,
1825 size_t* out_number_of_instructions) const {
1826 ArtMethod* const resolved_method = callee_graph->GetArtMethod();
1827
1828 HBasicBlock* exit_block = callee_graph->GetExitBlock();
1829 if (exit_block == nullptr) {
1830 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop)
1831 << "Method " << resolved_method->PrettyMethod()
1832 << " could not be inlined because it has an infinite loop";
1833 return false;
1834 }
1835
1836 bool has_one_return = false;
1837 for (HBasicBlock* predecessor : exit_block->GetPredecessors()) {
1838 if (predecessor->GetLastInstruction()->IsThrow()) {
1839 if (target_block->IsTryBlock()) {
1840 // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto.
1841 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCaller)
1842 << "Method " << resolved_method->PrettyMethod()
1843 << " could not be inlined because one branch always throws and"
1844 << " caller is in a try/catch block";
1845 return false;
1846 } else if (graph_->GetExitBlock() == nullptr) {
1847 // TODO(ngeoffray): Support adding HExit in the caller graph.
1848 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop)
1849 << "Method " << resolved_method->PrettyMethod()
1850 << " could not be inlined because one branch always throws and"
1851 << " caller does not have an exit block";
1852 return false;
1853 } else if (graph_->HasIrreducibleLoops()) {
1854 // TODO(ngeoffray): Support re-computing loop information to graphs with
1855 // irreducible loops?
1856 VLOG(compiler) << "Method " << resolved_method->PrettyMethod()
1857 << " could not be inlined because one branch always throws and"
1858 << " caller has irreducible loops";
1859 return false;
1860 }
1861 } else {
1862 has_one_return = true;
1863 }
1864 }
1865
1866 if (!has_one_return) {
1867 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedAlwaysThrows)
1868 << "Method " << resolved_method->PrettyMethod()
1869 << " could not be inlined because it always throws";
1870 return false;
1871 }
1872
1873 const bool too_many_registers =
1874 total_number_of_dex_registers_ > kMaximumNumberOfCumulatedDexRegisters;
1875 bool needs_bss_check = false;
1876 const bool can_encode_in_stack_map = CanEncodeInlinedMethodInStackMap(
1877 *outer_compilation_unit_.GetDexFile(), resolved_method, codegen_, &needs_bss_check);
1878 size_t number_of_instructions = 0;
1879 // Skip the entry block, it does not contain instructions that prevent inlining.
1880 for (HBasicBlock* block : callee_graph->GetReversePostOrderSkipEntryBlock()) {
1881 if (block->IsLoopHeader()) {
1882 if (block->GetLoopInformation()->IsIrreducible()) {
1883 // Don't inline methods with irreducible loops, they could prevent some
1884 // optimizations to run.
1885 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoop)
1886 << "Method " << resolved_method->PrettyMethod()
1887 << " could not be inlined because it contains an irreducible loop";
1888 return false;
1889 }
1890 if (!block->GetLoopInformation()->HasExitEdge()) {
1891 // Don't inline methods with loops without exit, since they cause the
1892 // loop information to be computed incorrectly when updating after
1893 // inlining.
1894 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedLoopWithoutExit)
1895 << "Method " << resolved_method->PrettyMethod()
1896 << " could not be inlined because it contains a loop with no exit";
1897 return false;
1898 }
1899 }
1900
1901 for (HInstructionIterator instr_it(block->GetInstructions());
1902 !instr_it.Done();
1903 instr_it.Advance()) {
1904 if (++number_of_instructions > inlining_budget_) {
1905 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInstructionBudget)
1906 << "Method " << resolved_method->PrettyMethod()
1907 << " is not inlined because the outer method has reached"
1908 << " its instruction budget limit.";
1909 return false;
1910 }
1911 HInstruction* current = instr_it.Current();
1912 if (current->NeedsEnvironment()) {
1913 if (too_many_registers) {
1914 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedEnvironmentBudget)
1915 << "Method " << resolved_method->PrettyMethod()
1916 << " is not inlined because its caller has reached"
1917 << " its environment budget limit.";
1918 return false;
1919 }
1920
1921 if (!can_encode_in_stack_map) {
1922 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedStackMaps)
1923 << "Method " << resolved_method->PrettyMethod() << " could not be inlined because "
1924 << current->DebugName() << " needs an environment, is in a different dex file"
1925 << ", and cannot be encoded in the stack maps.";
1926 return false;
1927 }
1928 }
1929
1930 if (current->IsUnresolvedStaticFieldGet() ||
1931 current->IsUnresolvedInstanceFieldGet() ||
1932 current->IsUnresolvedStaticFieldSet() ||
1933 current->IsUnresolvedInstanceFieldSet()) {
1934 // Entrypoint for unresolved fields does not handle inlined frames.
1935 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedUnresolvedEntrypoint)
1936 << "Method " << resolved_method->PrettyMethod()
1937 << " could not be inlined because it is using an unresolved"
1938 << " entrypoint";
1939 return false;
1940 }
1941
1942 // We currently don't have support for inlining across dex files if we are:
1943 // 1) In AoT,
1944 // 2) cross-dex inlining,
1945 // 3) the callee is a BCP DexFile,
1946 // 4) we are compiling multi image, and
1947 // 5) have an instruction that needs a bss entry, which will always be
1948 // 5)b) an instruction that needs an environment.
1949 // 1) - 4) are encoded in `needs_bss_check` (see CanEncodeInlinedMethodInStackMap).
1950 if (needs_bss_check && current->NeedsBss()) {
1951 DCHECK(current->NeedsEnvironment());
1952 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedBss)
1953 << "Method " << resolved_method->PrettyMethod()
1954 << " could not be inlined because it needs a BSS check";
1955 return false;
1956 }
1957 }
1958 }
1959
1960 *out_number_of_instructions = number_of_instructions;
1961 return true;
1962 }
1963
TryBuildAndInlineHelper(HInvoke * invoke_instruction,ArtMethod * resolved_method,ReferenceTypeInfo receiver_type,HInstruction ** return_replacement)1964 bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
1965 ArtMethod* resolved_method,
1966 ReferenceTypeInfo receiver_type,
1967 HInstruction** return_replacement) {
1968 DCHECK(!(resolved_method->IsStatic() && receiver_type.IsValid()));
1969 const dex::CodeItem* code_item = resolved_method->GetCodeItem();
1970 const DexFile& callee_dex_file = *resolved_method->GetDexFile();
1971 uint32_t method_index = resolved_method->GetDexMethodIndex();
1972 CodeItemDebugInfoAccessor code_item_accessor(resolved_method->DexInstructionDebugInfo());
1973 ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
1974 Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(),
1975 caller_compilation_unit_.GetDexCache(),
1976 graph_);
1977 Handle<mirror::ClassLoader> class_loader =
1978 NewHandleIfDifferent(resolved_method->GetDeclaringClass()->GetClassLoader(),
1979 caller_compilation_unit_.GetClassLoader(),
1980 graph_);
1981
1982 Handle<mirror::Class> compiling_class =
1983 graph_->GetHandleCache()->NewHandle(resolved_method->GetDeclaringClass());
1984 DexCompilationUnit dex_compilation_unit(
1985 class_loader,
1986 class_linker,
1987 callee_dex_file,
1988 code_item,
1989 resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
1990 method_index,
1991 resolved_method->GetAccessFlags(),
1992 /* verified_method= */ nullptr,
1993 dex_cache,
1994 compiling_class);
1995
1996 InvokeType invoke_type = invoke_instruction->GetInvokeType();
1997 if (invoke_type == kInterface) {
1998 // We have statically resolved the dispatch. To please the class linker
1999 // at runtime, we change this call as if it was a virtual call.
2000 invoke_type = kVirtual;
2001 }
2002
2003 bool caller_dead_reference_safe = graph_->IsDeadReferenceSafe();
2004 const dex::ClassDef& callee_class = resolved_method->GetClassDef();
2005 // MethodContainsRSensitiveAccess is currently slow, but HasDeadReferenceSafeAnnotation()
2006 // is currently rarely true.
2007 bool callee_dead_reference_safe =
2008 annotations::HasDeadReferenceSafeAnnotation(callee_dex_file, callee_class)
2009 && !annotations::MethodContainsRSensitiveAccess(callee_dex_file, callee_class, method_index);
2010
2011 const int32_t caller_instruction_counter = graph_->GetCurrentInstructionId();
2012 HGraph* callee_graph = new (graph_->GetAllocator()) HGraph(
2013 graph_->GetAllocator(),
2014 graph_->GetArenaStack(),
2015 graph_->GetHandleCache()->GetHandles(),
2016 callee_dex_file,
2017 method_index,
2018 codegen_->GetCompilerOptions().GetInstructionSet(),
2019 invoke_type,
2020 callee_dead_reference_safe,
2021 graph_->IsDebuggable(),
2022 graph_->GetCompilationKind(),
2023 /* start_instruction_id= */ caller_instruction_counter);
2024 callee_graph->SetArtMethod(resolved_method);
2025
2026 ScopedProfilingInfoUse spiu(Runtime::Current()->GetJit(), resolved_method, Thread::Current());
2027 if (Runtime::Current()->GetJit() != nullptr) {
2028 callee_graph->SetProfilingInfo(spiu.GetProfilingInfo());
2029 }
2030
2031 // When they are needed, allocate `inline_stats_` on the Arena instead
2032 // of on the stack, as Clang might produce a stack frame too large
2033 // for this function, that would not fit the requirements of the
2034 // `-Wframe-larger-than` option.
2035 if (stats_ != nullptr) {
2036 // Reuse one object for all inline attempts from this caller to keep Arena memory usage low.
2037 if (inline_stats_ == nullptr) {
2038 void* storage = graph_->GetAllocator()->Alloc<OptimizingCompilerStats>(kArenaAllocMisc);
2039 inline_stats_ = new (storage) OptimizingCompilerStats;
2040 } else {
2041 inline_stats_->Reset();
2042 }
2043 }
2044 HGraphBuilder builder(callee_graph,
2045 code_item_accessor,
2046 &dex_compilation_unit,
2047 &outer_compilation_unit_,
2048 codegen_,
2049 inline_stats_);
2050
2051 if (builder.BuildGraph() != kAnalysisSuccess) {
2052 LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCannotBuild)
2053 << "Method " << callee_dex_file.PrettyMethod(method_index)
2054 << " could not be built, so cannot be inlined";
2055 return false;
2056 }
2057
2058 SubstituteArguments(callee_graph, invoke_instruction, receiver_type, dex_compilation_unit);
2059
2060 RunOptimizations(callee_graph, code_item, dex_compilation_unit);
2061
2062 size_t number_of_instructions = 0;
2063 if (!CanInlineBody(callee_graph, invoke_instruction->GetBlock(), &number_of_instructions)) {
2064 return false;
2065 }
2066
2067 DCHECK_EQ(caller_instruction_counter, graph_->GetCurrentInstructionId())
2068 << "No instructions can be added to the outer graph while inner graph is being built";
2069
2070 // Inline the callee graph inside the caller graph.
2071 const int32_t callee_instruction_counter = callee_graph->GetCurrentInstructionId();
2072 graph_->SetCurrentInstructionId(callee_instruction_counter);
2073 *return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
2074 // Update our budget for other inlining attempts in `caller_graph`.
2075 total_number_of_instructions_ += number_of_instructions;
2076 UpdateInliningBudget();
2077
2078 DCHECK_EQ(callee_instruction_counter, callee_graph->GetCurrentInstructionId())
2079 << "No instructions can be added to the inner graph during inlining into the outer graph";
2080
2081 if (stats_ != nullptr) {
2082 DCHECK(inline_stats_ != nullptr);
2083 inline_stats_->AddTo(stats_);
2084 }
2085
2086 if (caller_dead_reference_safe && !callee_dead_reference_safe) {
2087 // Caller was dead reference safe, but is not anymore, since we inlined dead
2088 // reference unsafe code. Prior transformations remain valid, since they did not
2089 // affect the inlined code.
2090 graph_->MarkDeadReferenceUnsafe();
2091 }
2092
2093 return true;
2094 }
2095
RunOptimizations(HGraph * callee_graph,const dex::CodeItem * code_item,const DexCompilationUnit & dex_compilation_unit)2096 void HInliner::RunOptimizations(HGraph* callee_graph,
2097 const dex::CodeItem* code_item,
2098 const DexCompilationUnit& dex_compilation_unit) {
2099 // Note: if the outermost_graph_ is being compiled OSR, we should not run any
2100 // optimization that could lead to a HDeoptimize. The following optimizations do not.
2101 HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner");
2102 HConstantFolding fold(callee_graph, "constant_folding$inliner");
2103 InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_);
2104
2105 HOptimization* optimizations[] = {
2106 &simplify,
2107 &fold,
2108 &dce,
2109 };
2110
2111 for (size_t i = 0; i < arraysize(optimizations); ++i) {
2112 HOptimization* optimization = optimizations[i];
2113 optimization->Run();
2114 }
2115
2116 // Bail early for pathological cases on the environment (for example recursive calls,
2117 // or too large environment).
2118 if (total_number_of_dex_registers_ > kMaximumNumberOfCumulatedDexRegisters) {
2119 LOG_NOTE() << "Calls in " << callee_graph->GetArtMethod()->PrettyMethod()
2120 << " will not be inlined because the outer method has reached"
2121 << " its environment budget limit.";
2122 return;
2123 }
2124
2125 // Bail early if we know we already are over the limit.
2126 size_t number_of_instructions = CountNumberOfInstructions(callee_graph);
2127 if (number_of_instructions > inlining_budget_) {
2128 LOG_NOTE() << "Calls in " << callee_graph->GetArtMethod()->PrettyMethod()
2129 << " will not be inlined because the outer method has reached"
2130 << " its instruction budget limit. " << number_of_instructions;
2131 return;
2132 }
2133
2134 CodeItemDataAccessor accessor(callee_graph->GetDexFile(), code_item);
2135 HInliner inliner(callee_graph,
2136 outermost_graph_,
2137 codegen_,
2138 outer_compilation_unit_,
2139 dex_compilation_unit,
2140 inline_stats_,
2141 total_number_of_dex_registers_ + accessor.RegistersSize(),
2142 total_number_of_instructions_ + number_of_instructions,
2143 this,
2144 depth_ + 1);
2145 inliner.Run();
2146 }
2147
IsReferenceTypeRefinement(ObjPtr<mirror::Class> declared_class,bool declared_is_exact,bool declared_can_be_null,HInstruction * actual_obj)2148 static bool IsReferenceTypeRefinement(ObjPtr<mirror::Class> declared_class,
2149 bool declared_is_exact,
2150 bool declared_can_be_null,
2151 HInstruction* actual_obj)
2152 REQUIRES_SHARED(Locks::mutator_lock_) {
2153 if (declared_can_be_null && !actual_obj->CanBeNull()) {
2154 return true;
2155 }
2156
2157 ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo();
2158 ObjPtr<mirror::Class> actual_class = actual_rti.GetTypeHandle().Get();
2159 return (actual_rti.IsExact() && !declared_is_exact) ||
2160 (declared_class != actual_class && declared_class->IsAssignableFrom(actual_class));
2161 }
2162
IsReferenceTypeRefinement(ObjPtr<mirror::Class> declared_class,bool declared_can_be_null,HInstruction * actual_obj)2163 static bool IsReferenceTypeRefinement(ObjPtr<mirror::Class> declared_class,
2164 bool declared_can_be_null,
2165 HInstruction* actual_obj)
2166 REQUIRES_SHARED(Locks::mutator_lock_) {
2167 bool admissible = ReferenceTypePropagation::IsAdmissible(declared_class);
2168 return IsReferenceTypeRefinement(
2169 admissible ? declared_class : GetClassRoot<mirror::Class>(),
2170 /*declared_is_exact=*/ admissible && declared_class->CannotBeAssignedFromOtherTypes(),
2171 declared_can_be_null,
2172 actual_obj);
2173 }
2174
ArgumentTypesMoreSpecific(HInvoke * invoke_instruction,ArtMethod * resolved_method)2175 bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) {
2176 // If this is an instance call, test whether the type of the `this` argument
2177 // is more specific than the class which declares the method.
2178 if (!resolved_method->IsStatic()) {
2179 if (IsReferenceTypeRefinement(resolved_method->GetDeclaringClass(),
2180 /*declared_can_be_null=*/ false,
2181 invoke_instruction->InputAt(0u))) {
2182 return true;
2183 }
2184 }
2185
2186 // Iterate over the list of parameter types and test whether any of the
2187 // actual inputs has a more specific reference type than the type declared in
2188 // the signature.
2189 const dex::TypeList* param_list = resolved_method->GetParameterTypeList();
2190 for (size_t param_idx = 0,
2191 input_idx = resolved_method->IsStatic() ? 0 : 1,
2192 e = (param_list == nullptr ? 0 : param_list->Size());
2193 param_idx < e;
2194 ++param_idx, ++input_idx) {
2195 HInstruction* input = invoke_instruction->InputAt(input_idx);
2196 if (input->GetType() == DataType::Type::kReference) {
2197 ObjPtr<mirror::Class> param_cls = resolved_method->LookupResolvedClassFromTypeIndex(
2198 param_list->GetTypeItem(param_idx).type_idx_);
2199 if (IsReferenceTypeRefinement(param_cls, /*declared_can_be_null=*/ true, input)) {
2200 return true;
2201 }
2202 }
2203 }
2204
2205 return false;
2206 }
2207
ReturnTypeMoreSpecific(HInstruction * return_replacement,HInvoke * invoke_instruction)2208 bool HInliner::ReturnTypeMoreSpecific(HInstruction* return_replacement,
2209 HInvoke* invoke_instruction) {
2210 // Check the integrity of reference types and run another type propagation if needed.
2211 if (return_replacement != nullptr) {
2212 if (return_replacement->GetType() == DataType::Type::kReference) {
2213 // Test if the return type is a refinement of the declared return type.
2214 ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo();
2215 if (IsReferenceTypeRefinement(invoke_rti.GetTypeHandle().Get(),
2216 invoke_rti.IsExact(),
2217 /*declared_can_be_null=*/ true,
2218 return_replacement)) {
2219 return true;
2220 } else if (return_replacement->IsInstanceFieldGet()) {
2221 HInstanceFieldGet* field_get = return_replacement->AsInstanceFieldGet();
2222 if (field_get->GetFieldInfo().GetField() ==
2223 GetClassRoot<mirror::Object>()->GetInstanceField(0)) {
2224 return true;
2225 }
2226 }
2227 } else if (return_replacement->IsInstanceOf()) {
2228 // Inlining InstanceOf into an If may put a tighter bound on reference types.
2229 return true;
2230 }
2231 }
2232
2233 return false;
2234 }
2235
FixUpReturnReferenceType(ArtMethod * resolved_method,HInstruction * return_replacement)2236 void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
2237 HInstruction* return_replacement) {
2238 if (return_replacement != nullptr) {
2239 if (return_replacement->GetType() == DataType::Type::kReference) {
2240 if (!return_replacement->GetReferenceTypeInfo().IsValid()) {
2241 // Make sure that we have a valid type for the return. We may get an invalid one when
2242 // we inline invokes with multiple branches and create a Phi for the result.
2243 // TODO: we could be more precise by merging the phi inputs but that requires
2244 // some functionality from the reference type propagation.
2245 DCHECK(return_replacement->IsPhi());
2246 ObjPtr<mirror::Class> cls = resolved_method->LookupResolvedReturnType();
2247 ReferenceTypeInfo rti = ReferenceTypePropagation::IsAdmissible(cls)
2248 ? ReferenceTypeInfo::Create(graph_->GetHandleCache()->NewHandle(cls))
2249 : graph_->GetInexactObjectRti();
2250 return_replacement->SetReferenceTypeInfo(rti);
2251 }
2252 }
2253 }
2254 }
2255
2256 } // namespace art
2257