| // Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.android.tools.r8.ir.optimize.inliner; |
| |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.ir.code.InstancePut; |
| import com.android.tools.r8.ir.code.Instruction; |
| import com.android.tools.r8.ir.code.InvokeDirect; |
| import com.android.tools.r8.ir.optimize.Inliner.Reason; |
| import com.android.tools.r8.utils.StringUtils; |
| import java.io.PrintStream; |
| import java.util.Set; |
| |
| class WhyAreYouNotInliningReporterImpl extends WhyAreYouNotInliningReporter { |
| |
| private final ProgramMethod callee; |
| private final ProgramMethod context; |
| private final PrintStream output; |
| |
| private boolean reasonHasBeenReported = false; |
| |
| WhyAreYouNotInliningReporterImpl( |
| ProgramMethod callee, ProgramMethod context, PrintStream output) { |
| this.callee = callee; |
| this.context = context; |
| this.output = output; |
| } |
| |
| private void print(String reason) { |
| output.print("Method `"); |
| output.print(callee.toSourceString()); |
| output.print("` was not inlined into `"); |
| output.print(context.toSourceString()); |
| if (reason != null) { |
| output.print("`: "); |
| output.println(reason); |
| } else { |
| output.println("`."); |
| } |
| reasonHasBeenReported = true; |
| } |
| |
| private void printWithExceededThreshold( |
| String reason, String description, int value, int threshold) { |
| print(reason + " (" + description + ": " + value + ", threshold: " + threshold + ")."); |
| } |
| |
| @Override |
| public void reportExtraNeverInline() { |
| print("method is marked as an additional never inline method."); |
| } |
| |
| @Override |
| public void reportCallerNotSameClass() { |
| print("inlinee can only be inlined into methods in the same class."); |
| } |
| |
| @Override |
| public void reportCallerNotSameNest() { |
| print("inlinee can only be inlined into methods in the same class (and its nest members)."); |
| } |
| |
| @Override |
| public void reportCallerNotSamePackage() { |
| print( |
| "inlinee can only be inlined into methods in the same package " |
| + "(declared package private or accesses package private type or member)."); |
| } |
| |
| @Override |
| public void reportCallerNotSubtype() { |
| print( |
| "inlinee can only be inlined into methods in the same package and methods in subtypes of " |
| + "the inlinee's enclosing class" |
| + "(declared protected or accesses protected type or member)."); |
| } |
| |
| @Override |
| public void reportClasspathMethod() { |
| print("inlinee is on the classpath."); |
| } |
| |
| @Override |
| public void reportInaccessible() { |
| print("inlinee is not accessible from the caller context."); |
| } |
| |
| @Override |
| public void reportIncorrectArity(int numberOfArguments, int arity) { |
| print( |
| "number of arguments (" |
| + numberOfArguments |
| + ") does not match arity of method (" |
| + arity |
| + ")."); |
| } |
| |
| @Override |
| public void reportInlineeDoesNotHaveCode() { |
| print("inlinee does not have code."); |
| } |
| |
| @Override |
| public void reportInlineeNotInliningCandidate() { |
| print("unsupported instruction in inlinee."); |
| } |
| |
| @Override |
| public void reportInlineeNotProcessed() { |
| print("inlinee not processed yet."); |
| } |
| |
| @Override |
| public void reportInlineeNotSimple() { |
| print( |
| "not inlining due to code size heuristic " |
| + "(inlinee may have multiple callers and is not considered trivial)."); |
| } |
| |
| @Override |
| public void reportInlineeRefersToClassesNotInMainDex() { |
| print( |
| "inlining could increase the main dex size " |
| + "(caller is in main dex and inlinee refers to classes not in main dex)."); |
| } |
| |
| @Override |
| public void reportInliningAcrossFeatureSplit() { |
| print("cannot inline across feature splits."); |
| } |
| |
| @Override |
| public void reportInstructionBudgetIsExceeded() { |
| print("caller's instruction budget is exceeded."); |
| } |
| |
| @Override |
| public void reportInvalidDoubleInliningCandidate() { |
| print("inlinee is invoked more than once and could not be inlined into all call sites."); |
| } |
| |
| @Override |
| public void reportInvalidInliningReason(Reason reason, Set<Reason> validInliningReasons) { |
| print( |
| "not a valid inlining reason (was: " |
| + reason |
| + ", allowed: one of " |
| + StringUtils.join(validInliningReasons, ", ") |
| + ")."); |
| } |
| |
| @Override |
| public void reportLibraryMethod() { |
| print("inlinee is a library method."); |
| } |
| |
| @Override |
| public void reportMarkedAsNeverInline() { |
| print("method is marked by a -neverinline rule."); |
| } |
| |
| @Override |
| public void reportMustTriggerClassInitialization() { |
| print( |
| "cannot guarantee that the enclosing class of the inlinee is guaranteed to be class " |
| + "initialized before the first side-effecting instruction in the inlinee."); |
| } |
| |
| @Override |
| public void reportNoInliningIntoConstructorsWhenGeneratingClassFiles() { |
| print("inlining into constructors not supported when generating class files."); |
| } |
| |
| @Override |
| public void reportPinned() { |
| print("method is kept by a Proguard configuration rule."); |
| } |
| |
| @Override |
| public void reportPotentialExplosionInExceptionalControlFlowResolutionBlocks( |
| int estimatedNumberOfControlFlowResolutionBlocks, int threshold) { |
| printWithExceededThreshold( |
| "could lead to an explosion in the number of moves due to the exceptional control flow", |
| "estimated number of control flow resolution blocks", |
| estimatedNumberOfControlFlowResolutionBlocks, |
| threshold); |
| } |
| |
| @Override |
| public void reportProcessedConcurrently() { |
| print( |
| "could lead to nondeterministic output since the inlinee is being optimized concurrently."); |
| } |
| |
| @Override |
| public void reportReceiverDefinitelyNull() { |
| print("the receiver is always null at the call site."); |
| } |
| |
| @Override |
| public void reportReceiverMaybeNull() { |
| print("the receiver may be null at the call site."); |
| } |
| |
| @Override |
| public void reportRecursiveMethod() { |
| print("recursive calls are not inlined."); |
| } |
| |
| @Override |
| public void reportUnknownTarget() { |
| print("could not find a single target."); |
| } |
| |
| @Override |
| public void reportUnsafeConstructorInliningDueToFinalFieldAssignment(InstancePut instancePut) { |
| print( |
| "final field `" |
| + instancePut.getField() |
| + "` must be initialized in a constructor of `" |
| + callee.getHolderType().toSourceString() |
| + "`."); |
| } |
| |
| @Override |
| public void reportUnsafeConstructorInliningDueToIndirectConstructorCall(InvokeDirect invoke) { |
| print( |
| "must invoke a constructor from the class being instantiated (would invoke `" |
| + invoke.getInvokedMethod().toSourceString() |
| + "`)."); |
| } |
| |
| @Override |
| public void reportUnsafeConstructorInliningDueToUninitializedObjectUse(Instruction user) { |
| print("would lead to use of uninitialized object (user: `" + user.toString() + "`)."); |
| } |
| |
| @Override |
| public void reportWillExceedInstructionBudget(int numberOfInstructions, int threshold) { |
| printWithExceededThreshold( |
| "would exceed the caller's instruction budget", |
| "number of instructions in inlinee", |
| numberOfInstructions, |
| threshold); |
| } |
| |
| @Override |
| public void reportWillExceedMaxInliningDepth(int actualInliningDepth, int threshold) { |
| printWithExceededThreshold( |
| "would exceed the maximum inlining depth", |
| "current inlining depth", |
| actualInliningDepth, |
| threshold); |
| } |
| |
| @Override |
| public void reportWillExceedMonitorEnterValuesBudget( |
| int numberOfMonitorEnterValuesAfterInlining, int threshold) { |
| printWithExceededThreshold( |
| "could negatively impact register allocation due to the number of monitor instructions", |
| "estimated number of locks after inlining", |
| numberOfMonitorEnterValuesAfterInlining, |
| threshold); |
| } |
| |
| @Override |
| public boolean unsetReasonHasBeenReportedFlag() { |
| assert reasonHasBeenReported; |
| reasonHasBeenReported = false; |
| return true; |
| } |
| } |