|  | // Copyright (c) 2018, 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; | 
|  |  | 
|  | import static com.android.tools.r8.ir.optimize.Inliner.numberOfInstructions; | 
|  | import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.addMonitorEnterValue; | 
|  | import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.collectAllMonitorEnterValues; | 
|  | import static com.android.tools.r8.utils.AndroidApiLevelUtils.isApiSafeForInlining; | 
|  |  | 
|  | import com.android.tools.r8.dex.code.DexCheckCast; | 
|  | import com.android.tools.r8.dex.code.DexInvokeStatic; | 
|  | import com.android.tools.r8.dex.code.DexMoveResult; | 
|  | import com.android.tools.r8.dex.code.DexMoveResultObject; | 
|  | import com.android.tools.r8.dex.code.DexMoveResultWide; | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils; | 
|  | import com.android.tools.r8.graph.AppView; | 
|  | import com.android.tools.r8.graph.Code; | 
|  | import com.android.tools.r8.graph.DefaultUseRegistryWithResult; | 
|  | import com.android.tools.r8.graph.DexClassAndField; | 
|  | import com.android.tools.r8.graph.DexItemFactory; | 
|  | import com.android.tools.r8.graph.DexItemFactory.BoxUnboxPrimitiveMethodRoundtrip; | 
|  | import com.android.tools.r8.graph.DexMethod; | 
|  | import com.android.tools.r8.graph.DexType; | 
|  | import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; | 
|  | import com.android.tools.r8.graph.ProgramMethod; | 
|  | import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; | 
|  | import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint; | 
|  | import com.android.tools.r8.ir.analysis.type.ClassTypeElement; | 
|  | import com.android.tools.r8.ir.analysis.type.TypeElement; | 
|  | import com.android.tools.r8.ir.code.Argument; | 
|  | import com.android.tools.r8.ir.code.BasicBlock; | 
|  | import com.android.tools.r8.ir.code.CheckCast; | 
|  | import com.android.tools.r8.ir.code.IRCode; | 
|  | 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.code.InvokeMethod; | 
|  | import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; | 
|  | import com.android.tools.r8.ir.code.InvokeStatic; | 
|  | import com.android.tools.r8.ir.code.Monitor; | 
|  | import com.android.tools.r8.ir.code.Value; | 
|  | import com.android.tools.r8.ir.conversion.MethodProcessor; | 
|  | import com.android.tools.r8.ir.optimize.Inliner.InlineAction; | 
|  | import com.android.tools.r8.ir.optimize.Inliner.InlineResult; | 
|  | import com.android.tools.r8.ir.optimize.Inliner.Reason; | 
|  | import com.android.tools.r8.ir.optimize.Inliner.RetryAction; | 
|  | import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider; | 
|  | import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy; | 
|  | import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; | 
|  | import com.android.tools.r8.profile.startup.optimization.StartupBoundaryOptimizationUtils; | 
|  | import com.android.tools.r8.shaking.AppInfoWithLiveness; | 
|  | import com.android.tools.r8.shaking.AssumeInfoCollection; | 
|  | import com.android.tools.r8.shaking.MainDexInfo; | 
|  | import com.android.tools.r8.utils.BooleanUtils; | 
|  | import com.android.tools.r8.utils.InternalOptions; | 
|  | import com.android.tools.r8.utils.InternalOptions.InlinerOptions; | 
|  | import com.android.tools.r8.utils.timing.Timing; | 
|  | import com.google.common.collect.Sets; | 
|  | import java.util.ArrayList; | 
|  | import java.util.BitSet; | 
|  | import java.util.List; | 
|  | import java.util.Optional; | 
|  | import java.util.Set; | 
|  |  | 
|  | public class DefaultInliningOracle implements InliningOracle { | 
|  |  | 
|  | private final AppView<AppInfoWithLiveness> appView; | 
|  | private final InternalOptions options; | 
|  | private final InlinerOptions inlinerOptions; | 
|  | private final MainDexInfo mainDexInfo; | 
|  | private final ProgramMethod method; | 
|  | private final MethodProcessor methodProcessor; | 
|  | private final InliningReasonStrategy reasonStrategy; | 
|  | private int instructionAllowance; | 
|  |  | 
|  | public DefaultInliningOracle( | 
|  | AppView<AppInfoWithLiveness> appView, | 
|  | ProgramMethod method, | 
|  | MethodProcessor methodProcessor, | 
|  | InliningReasonStrategy inliningReasonStrategy, | 
|  | IRCode code) { | 
|  | this( | 
|  | appView, | 
|  | method, | 
|  | methodProcessor, | 
|  | inliningReasonStrategy, | 
|  | appView.options().inlinerOptions().inliningInstructionAllowance | 
|  | - numberOfInstructions(code)); | 
|  | } | 
|  |  | 
|  | public DefaultInliningOracle( | 
|  | AppView<AppInfoWithLiveness> appView, | 
|  | ProgramMethod method, | 
|  | MethodProcessor methodProcessor, | 
|  | InliningReasonStrategy inliningReasonStrategy, | 
|  | int inliningInstructionAllowance) { | 
|  | this.appView = appView; | 
|  | this.options = appView.options(); | 
|  | this.inlinerOptions = options.inlinerOptions(); | 
|  | this.reasonStrategy = inliningReasonStrategy; | 
|  | this.mainDexInfo = appView.appInfo().getMainDexInfo(); | 
|  | this.method = method; | 
|  | this.methodProcessor = methodProcessor; | 
|  | this.instructionAllowance = inliningInstructionAllowance; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AppView<AppInfoWithLiveness> appView() { | 
|  | return appView; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isForcedInliningOracle() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | private boolean isSingleTargetInvalid( | 
|  | InvokeMethod invoke, | 
|  | ProgramMethod singleTarget, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (singleTarget == null) { | 
|  | throw new Unreachable( | 
|  | "Unexpected attempt to inline invoke that does not have a single target"); | 
|  | } | 
|  |  | 
|  | if (singleTarget.getDefinition().isClassInitializer()) { | 
|  | throw new Unreachable( | 
|  | "Unexpected attempt to invoke a class initializer (`" | 
|  | + singleTarget.toSourceString() | 
|  | + "`)"); | 
|  | } | 
|  |  | 
|  | if (!singleTarget.getDefinition().hasCode()) { | 
|  | whyAreYouNotInliningReporter.reportInlineeDoesNotHaveCode(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Ignore the implicit receiver argument. | 
|  | int numberOfArguments = | 
|  | invoke.arguments().size() - BooleanUtils.intValue(invoke.isInvokeMethodWithReceiver()); | 
|  | int arity = singleTarget.getReference().getArity(); | 
|  | if (numberOfArguments != arity) { | 
|  | whyAreYouNotInliningReporter.reportIncorrectArity(numberOfArguments, arity); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean passesInliningConstraints( | 
|  | IRCode code, | 
|  | SingleResolutionResult<?> resolutionResult, | 
|  | ProgramMethod singleTarget, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | // Do not inline if the inlinee is greater than the api caller level. | 
|  | // TODO(b/188498051): We should not force inline lower api method calls. | 
|  | if (!isApiSafeForInlining(method, singleTarget, appView, whyAreYouNotInliningReporter)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (method.isStructurallyEqualTo(singleTarget)) { | 
|  | // Cannot handle recursive inlining at this point. | 
|  | // Force inlined method should never be recursive. | 
|  | assert !singleTarget.getOptimizationInfo().forceInline(); | 
|  | whyAreYouNotInliningReporter.reportRecursiveMethod(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (canHaveIssuesWithMonitors(code, singleTarget)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // We should never even try to inline something that is processed concurrently. It can lead | 
|  | // to non-deterministic behaviour as the inlining IR could be built from either original output | 
|  | // or optimized code. Right now this happens for the class class staticizer, as it just | 
|  | // processes all relevant methods in parallel with the full optimization pipeline enabled. | 
|  | // TODO(sgjesse): Add this assert "assert !isProcessedConcurrently.test(candidate);" | 
|  | if (methodProcessor.isProcessedConcurrently(singleTarget)) { | 
|  | whyAreYouNotInliningReporter.reportProcessedConcurrently(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!FeatureSplitBoundaryOptimizationUtils.isSafeForInlining(method, singleTarget, appView)) { | 
|  | whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!StartupBoundaryOptimizationUtils.isSafeForInlining(method, singleTarget, appView)) { | 
|  | whyAreYouNotInliningReporter.reportInliningAcrossStartupBoundary(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Abort inlining attempt if method -> target access is not right. | 
|  | if (resolutionResult.isAccessibleFrom(method, appView).isPossiblyFalse()) { | 
|  | whyAreYouNotInliningReporter.reportInaccessible(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Don't inline code with references beyond root main dex classes into a root main dex class. | 
|  | // If we do this it can increase the size of the main dex dependent classes. | 
|  | if (mainDexInfo.disallowInliningIntoContext(appView, method, singleTarget)) { | 
|  | whyAreYouNotInliningReporter.reportInlineeRefersToClassesNotInMainDex(); | 
|  | return false; | 
|  | } | 
|  | assert !mainDexInfo.disallowInliningIntoContext(appView, method, singleTarget); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private boolean canHaveIssuesWithMonitors(IRCode code, ProgramMethod singleTarget) { | 
|  | return options.canHaveIssueWithInlinedMonitors() | 
|  | && hasMonitorsOrIsSynchronized(code) | 
|  | && hasMonitorsOrIsSynchronized(singleTarget); | 
|  | } | 
|  |  | 
|  | public static boolean hasMonitorsOrIsSynchronized(IRCode code) { | 
|  | return code.context().getAccessFlags().isSynchronized() | 
|  | || code.metadata().mayHaveMonitorInstruction(); | 
|  | } | 
|  |  | 
|  | public static boolean hasMonitorsOrIsSynchronized(ProgramMethod singleTarget) { | 
|  | return singleTarget.getAccessFlags().isSynchronized() | 
|  | || singleTarget.getDefinition().getCode().hasMonitorInstructions(); | 
|  | } | 
|  |  | 
|  | public boolean satisfiesRequirementsForSimpleInlining( | 
|  | InvokeMethod invoke, ProgramMethod target, Optional<InliningIRProvider> inliningIRProvider) { | 
|  | // Code size modified by inlining, so only read for non-concurrent methods. | 
|  | boolean deterministic = !methodProcessor.isProcessedConcurrently(target); | 
|  | if (deterministic) { | 
|  | // Check if the inlinee is sufficiently small to inline. | 
|  | Code code = target.getDefinition().getCode(); | 
|  | int instructionLimit = inlinerOptions.getSimpleInliningInstructionLimit(); | 
|  | int estimatedMaxIncrement = | 
|  | getEstimatedMaxInliningInstructionLimitIncrement(invoke, target, inliningIRProvider); | 
|  | int estimatedSizeForInlining = | 
|  | code.getEstimatedSizeForInliningIfLessThanOrEquals( | 
|  | instructionLimit + estimatedMaxIncrement); | 
|  | if (estimatedSizeForInlining >= 0) { | 
|  | if (estimatedSizeForInlining <= instructionLimit) { | 
|  | return true; | 
|  | } | 
|  | int actualIncrement = | 
|  | getInliningInstructionLimitIncrement(invoke, target, inliningIRProvider); | 
|  | if (estimatedSizeForInlining <= instructionLimit + actualIncrement) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return satisfiesSimpleInliningConstraint(invoke, target); | 
|  | } | 
|  |  | 
|  | private boolean satisfiesSimpleInliningConstraint(InvokeMethod invoke, ProgramMethod target) { | 
|  | // Even if the inlinee is big it may become simple after inlining. We therefore check if the | 
|  | // inlinee's simple inlining constraint is satisfied by the invoke. | 
|  | SimpleInliningConstraint simpleInliningConstraint = | 
|  | target.getDefinition().getOptimizationInfo().getSimpleInliningConstraint(); | 
|  | return simpleInliningConstraint.isSatisfied(invoke); | 
|  | } | 
|  |  | 
|  | private int getInliningInstructionLimitIncrement( | 
|  | InvokeMethod invoke, ProgramMethod target, Optional<InliningIRProvider> inliningIRProvider) { | 
|  | if (!options.inlinerOptions().enableSimpleInliningInstructionLimitIncrement) { | 
|  | return 0; | 
|  | } | 
|  | return getInliningInstructionLimitIncrementForNonNullParamOrThrow(invoke, target) | 
|  | + getInliningInstructionLimitIncrementForPrelude(invoke, target, inliningIRProvider) | 
|  | + getInliningInstructionLimitIncrementForReturn(invoke); | 
|  | } | 
|  |  | 
|  | private int getEstimatedMaxInliningInstructionLimitIncrement( | 
|  | InvokeMethod invoke, ProgramMethod target, Optional<InliningIRProvider> inliningIRProvider) { | 
|  | if (!options.inlinerOptions().enableSimpleInliningInstructionLimitIncrement) { | 
|  | return 0; | 
|  | } | 
|  | return getEstimatedMaxInliningInstructionLimitIncrementForNonNullParamOrThrow(invoke, target) | 
|  | + getEstimatedMaxInliningInstructionLimitIncrementForPrelude( | 
|  | invoke, target, inliningIRProvider) | 
|  | + getEstimatedMaxInliningInstructionLimitIncrementForReturn(invoke); | 
|  | } | 
|  |  | 
|  | private int getInliningInstructionLimitIncrementForNonNullParamOrThrow( | 
|  | InvokeMethod invoke, ProgramMethod target) { | 
|  | BitSet hints = target.getOptimizationInfo().getNonNullParamOrThrow(); | 
|  | if (hints == null) { | 
|  | return 0; | 
|  | } | 
|  | int instructionLimit = 0; | 
|  | for (int index = invoke.getFirstNonReceiverArgumentIndex(); | 
|  | index < invoke.arguments().size(); | 
|  | index++) { | 
|  | Value argument = invoke.getArgument(index); | 
|  | if (hints.get(index) && argument.getType().isReferenceType() && argument.isNeverNull()) { | 
|  | // 5-4 instructions per parameter check are expected to be removed. | 
|  | instructionLimit += 4; | 
|  | } | 
|  | } | 
|  | return instructionLimit; | 
|  | } | 
|  |  | 
|  | private int getEstimatedMaxInliningInstructionLimitIncrementForNonNullParamOrThrow( | 
|  | InvokeMethod invoke, ProgramMethod target) { | 
|  | return getInliningInstructionLimitIncrementForNonNullParamOrThrow(invoke, target); | 
|  | } | 
|  |  | 
|  | private int getInliningInstructionLimitIncrementForPrelude( | 
|  | InvokeMethod invoke, ProgramMethod target, Optional<InliningIRProvider> inliningIRProvider) { | 
|  | if (inliningIRProvider.isEmpty() | 
|  | || invoke.arguments().isEmpty() | 
|  | || !target.getDefinition().getCode().isLirCode()) { | 
|  | return 0; | 
|  | } | 
|  | IRCode code = inliningIRProvider.get().getAndCacheInliningIR(invoke, target); | 
|  | Iterable<Argument> arguments = code::argumentIterator; | 
|  | DexItemFactory factory = appView.dexItemFactory(); | 
|  | int increment = 0; | 
|  | for (Argument argument : arguments) { | 
|  | Value argumentValue = argument.outValue(); | 
|  | for (Instruction user : argumentValue.uniqueUsers()) { | 
|  | if (user.isCheckCast()) { | 
|  | CheckCast checkCastUser = user.asCheckCast(); | 
|  | TypeElement argumentType = invoke.getArgument(argument.getIndex()).getType(); | 
|  | TypeElement castType = checkCastUser.getType().toTypeElement(appView); | 
|  | if (argumentType.lessThanOrEqual(castType, appView)) { | 
|  | // We can remove the cast inside the inlinee. | 
|  | increment += DexCheckCast.SIZE; | 
|  | } | 
|  | } else { | 
|  | DexType argumentType = target.getArgumentType(argument.getIndex()); | 
|  | BoxUnboxPrimitiveMethodRoundtrip roundtrip = | 
|  | factory.getBoxUnboxPrimitiveMethodRoundtrip(argumentType); | 
|  | if (roundtrip == null) { | 
|  | continue; | 
|  | } | 
|  | Value invokeArgument = invoke.getArgument(argument.getIndex()).getAliasedValue(); | 
|  | if (user.isInvokeMethod(roundtrip.getBoxIfPrimitiveElseUnbox()) | 
|  | && invokeArgument.isDefinedByInstructionSatisfying( | 
|  | definition -> | 
|  | definition.isInvokeMethod(roundtrip.getUnboxIfPrimitiveElseBox()))) { | 
|  | // We can remove the unbox/box operation inside the inlinee. | 
|  | increment += DexInvokeStatic.SIZE + DexMoveResult.SIZE; | 
|  | if (invokeArgument.numberOfAllUsers() == 1 && argumentValue.numberOfAllUsers() == 1) { | 
|  | // We can remove the box/unbox operation inside the caller. | 
|  | increment += DexInvokeStatic.SIZE + DexMoveResult.SIZE; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return increment; | 
|  | } | 
|  |  | 
|  | private int getEstimatedMaxInliningInstructionLimitIncrementForPrelude( | 
|  | InvokeMethod invoke, ProgramMethod target, Optional<InliningIRProvider> inliningIRProvider) { | 
|  | if (inliningIRProvider.isEmpty() | 
|  | || invoke.arguments().isEmpty() | 
|  | || !target.getDefinition().getCode().isLirCode()) { | 
|  | return 0; | 
|  | } | 
|  | int increment = 0; | 
|  | for (int argumentIndex = invoke.getFirstNonReceiverArgumentIndex(); | 
|  | argumentIndex < invoke.arguments().size(); | 
|  | argumentIndex++) { | 
|  | Value argument = invoke.getArgument(argumentIndex).getAliasedValue(); | 
|  | if (argument.getType().isReferenceType()) { | 
|  | // We can maybe remove a cast inside the inlinee. | 
|  | increment += DexCheckCast.SIZE; | 
|  | } | 
|  | DexType argumentType = target.getArgumentType(argumentIndex); | 
|  | BoxUnboxPrimitiveMethodRoundtrip roundtrip = | 
|  | appView.dexItemFactory().getBoxUnboxPrimitiveMethodRoundtrip(argumentType); | 
|  | if (roundtrip != null | 
|  | && argument.isDefinedByInstructionSatisfying( | 
|  | definition -> definition.isInvokeMethod(roundtrip.getUnboxIfPrimitiveElseBox()))) { | 
|  | // We can maybe remove a unbox/box operation inside the inlinee. | 
|  | increment += DexInvokeStatic.SIZE + DexMoveResult.SIZE; | 
|  | // We can maybe remove a box/unbox operation inside the caller. | 
|  | increment += DexInvokeStatic.SIZE + DexMoveResult.SIZE; | 
|  | } | 
|  | } | 
|  | return increment; | 
|  | } | 
|  |  | 
|  | private int getInliningInstructionLimitIncrementForReturn(InvokeMethod invoke) { | 
|  | if (options.isGeneratingDex() && invoke.hasOutValue() && invoke.outValue().hasNonDebugUsers()) { | 
|  | assert DexMoveResult.SIZE == DexMoveResultObject.SIZE; | 
|  | assert DexMoveResult.SIZE == DexMoveResultWide.SIZE; | 
|  | return DexMoveResult.SIZE; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | private int getEstimatedMaxInliningInstructionLimitIncrementForReturn(InvokeMethod invoke) { | 
|  | return getInliningInstructionLimitIncrementForReturn(invoke); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ProgramMethod lookupSingleTarget(InvokeMethod invoke, ProgramMethod context) { | 
|  | return invoke.lookupSingleProgramTarget(appView, context); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public InlineResult computeInlining( | 
|  | IRCode code, | 
|  | InvokeMethod invoke, | 
|  | SingleResolutionResult<?> resolutionResult, | 
|  | ProgramMethod singleTarget, | 
|  | ProgramMethod context, | 
|  | ClassInitializationAnalysis classInitializationAnalysis, | 
|  | InliningIRProvider inliningIRProvider, | 
|  | Timing timing, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (isSingleTargetInvalid(invoke, singleTarget, whyAreYouNotInliningReporter)) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (neverInline(invoke, resolutionResult, singleTarget, whyAreYouNotInliningReporter)) { | 
|  | if (singleTarget.getDefinition().getOptimizationInfo().forceInline()) { | 
|  | throw new Unreachable( | 
|  | "Unexpected attempt to force inline method `" | 
|  | + singleTarget.toSourceString() | 
|  | + "` in `" | 
|  | + context.toSourceString() | 
|  | + "`."); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | Reason reason = | 
|  | reasonStrategy.computeInliningReason( | 
|  | invoke, | 
|  | singleTarget, | 
|  | context, | 
|  | this, | 
|  | inliningIRProvider, | 
|  | methodProcessor, | 
|  | whyAreYouNotInliningReporter); | 
|  | if (reason == Reason.NEVER) { | 
|  | return null; | 
|  | } else if (reason != Reason.ALWAYS | 
|  | && !stillHasBudgetForNonForceInlining(whyAreYouNotInliningReporter)) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (methodProcessor.getCallSiteInformation().hasSingleCallSite(singleTarget, context) | 
|  | && !singleTarget.getDefinition().isProcessed() | 
|  | && methodProcessor.isPrimaryMethodProcessor()) { | 
|  | // The single target has this method as single caller, but the single target is not yet | 
|  | // processed. Enqueue the context for processing in the secondary optimization pass to allow | 
|  | // the single caller inlining to happen. | 
|  | return new RetryAction(); | 
|  | } | 
|  |  | 
|  | if (!singleTarget | 
|  | .getDefinition() | 
|  | .isInliningCandidate(appView, method, whyAreYouNotInliningReporter)) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (!passesInliningConstraints( | 
|  | code, resolutionResult, singleTarget, whyAreYouNotInliningReporter)) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | // Ensure that we don't introduce several monitors in the same method on old device that can | 
|  | // choke on this. If a context is forceinline, e.g., from class merging, don't ever inline | 
|  | // monitors, since that may conflict with a similar other constructor. | 
|  | if (options.canHaveIssueWithInlinedMonitors() && hasMonitorsOrIsSynchronized(singleTarget)) { | 
|  | if (context.getOptimizationInfo().forceInline() | 
|  | || code.metadata().mayHaveMonitorInstruction()) { | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | InlineAction.Builder actionBuilder = | 
|  | invoke.computeInlining( | 
|  | singleTarget, this, classInitializationAnalysis, timing, whyAreYouNotInliningReporter); | 
|  | if (actionBuilder == null) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (!setDowncastTypeIfNeeded(appView, actionBuilder, invoke, singleTarget, context)) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (isInliningBlockedDueToArrayClone(context, singleTarget, appView)) { | 
|  | whyAreYouNotInliningReporter.reportUnsafeDueToArrayCloneCall(); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | // Make sure constructor inlining is legal. | 
|  | if (singleTarget.getDefinition().isInstanceInitializer() | 
|  | && !canInlineInstanceInitializer( | 
|  | actionBuilder, | 
|  | code, | 
|  | invoke.asInvokeDirect(), | 
|  | singleTarget, | 
|  | inliningIRProvider, | 
|  | whyAreYouNotInliningReporter)) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (reason == Reason.MULTI_CALLER_CANDIDATE) { | 
|  | assert methodProcessor.isPrimaryMethodProcessor(); | 
|  | actionBuilder.setReason( | 
|  | satisfiesRequirementsForSimpleInlining( | 
|  | invoke, singleTarget, Optional.of(inliningIRProvider)) | 
|  | ? Reason.SIMPLE | 
|  | : reason); | 
|  | } else { | 
|  | if (reason == Reason.SIMPLE | 
|  | && !satisfiesRequirementsForSimpleInlining( | 
|  | invoke, singleTarget, Optional.of(inliningIRProvider))) { | 
|  | whyAreYouNotInliningReporter.reportInlineeNotSimple(); | 
|  | return null; | 
|  | } | 
|  | actionBuilder.setReason(reason); | 
|  | } | 
|  |  | 
|  | return actionBuilder.build(); | 
|  | } | 
|  |  | 
|  | private boolean neverInline( | 
|  | InvokeMethod invoke, | 
|  | SingleResolutionResult<?> resolutionResult, | 
|  | ProgramMethod singleTarget, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | DexMethod singleTargetReference = singleTarget.getReference(); | 
|  | if (!appView.getKeepInfo(singleTarget).isInliningAllowed(options, singleTarget)) { | 
|  | whyAreYouNotInliningReporter.reportPinned(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | AssumeInfoCollection assumeInfoCollection = appView.getAssumeInfoCollection(); | 
|  | if (assumeInfoCollection.isSideEffectFree(invoke.getInvokedMethod()) | 
|  | || assumeInfoCollection.isSideEffectFree(resolutionResult.getResolutionPair()) | 
|  | || assumeInfoCollection.isSideEffectFree(singleTargetReference)) { | 
|  | return !singleTarget.getDefinition().getOptimizationInfo().forceInline(); | 
|  | } | 
|  |  | 
|  | if (!appView.testing().allowInliningOfSynthetics | 
|  | && appView.getSyntheticItems().isSyntheticClass(singleTarget.getHolder())) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public InlineAction.Builder computeForInvokeWithReceiver( | 
|  | InvokeMethodWithReceiver invoke, | 
|  | ProgramMethod singleTarget, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | Value receiver = invoke.getReceiver(); | 
|  | if (receiver.getType().isDefinitelyNull()) { | 
|  | // A definitely null receiver will throw an error on call site. | 
|  | whyAreYouNotInliningReporter.reportReceiverDefinitelyNull(); | 
|  | return null; | 
|  | } | 
|  | if (receiver.getType().isNullable()) { | 
|  | assert !receiver.getType().isDefinitelyNull(); | 
|  | // When inlining an instance method call, we need to preserve the null check for the | 
|  | // receiver. Therefore, if the receiver may be null and the candidate inlinee does not | 
|  | // throw if the receiver is null before any other side effect, then we must synthesize a | 
|  | // null check. | 
|  | if (!inlinerOptions.enableInliningOfInvokesWithNullableReceivers) { | 
|  | whyAreYouNotInliningReporter.reportReceiverMaybeNull(); | 
|  | return null; | 
|  | } | 
|  | } | 
|  | return InlineAction.builder().setInvoke(invoke).setTarget(singleTarget); | 
|  | } | 
|  |  | 
|  | public InlineAction.Builder computeForInvokeStatic( | 
|  | InvokeStatic invoke, | 
|  | ProgramMethod singleTarget, | 
|  | ClassInitializationAnalysis classInitializationAnalysis, | 
|  | Timing timing, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | InlineAction.Builder actionBuilder = | 
|  | InlineAction.builder().setInvoke(invoke).setTarget(singleTarget); | 
|  | try (Timing t0 = timing.begin("Is target class initialized")) { | 
|  | if (isTargetClassInitialized( | 
|  | invoke, method, singleTarget, classInitializationAnalysis, timing)) { | 
|  | return actionBuilder; | 
|  | } | 
|  | } | 
|  | if (appView.canUseInitClass() | 
|  | && inlinerOptions.enableInliningOfInvokesWithClassInitializationSideEffects) { | 
|  | return actionBuilder.setShouldEnsureStaticInitialization(); | 
|  | } | 
|  | whyAreYouNotInliningReporter.reportMustTriggerClassInitialization(); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private boolean isTargetClassInitialized( | 
|  | InvokeStatic invoke, | 
|  | ProgramMethod context, | 
|  | ProgramMethod target, | 
|  | ClassInitializationAnalysis classInitializationAnalysis, | 
|  | Timing timing) { | 
|  | // Only proceed with inlining a static invoke if: | 
|  | // - the holder for the target is a subtype of the holder for the method, | 
|  | // - the current method has already triggered the holder for the target method to be | 
|  | //   initialized, or | 
|  | // - there is no non-trivial class initializer. | 
|  | try (Timing t0 = timing.begin("Subtype")) { | 
|  | if (appView.appInfo().isSubtype(context.getHolderType(), target.getHolderType())) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | try (Timing t0 = timing.begin("Initialized classes in instance methods")) { | 
|  | if (!context.getDefinition().isStatic()) { | 
|  | boolean targetIsGuaranteedToBeInitialized = | 
|  | appView.withInitializedClassesInInstanceMethods( | 
|  | analysis -> | 
|  | analysis.isClassDefinitelyLoadedInInstanceMethod(target.getHolder(), context), | 
|  | false); | 
|  | if (targetIsGuaranteedToBeInitialized) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | try (Timing t0 = timing.begin("Class init analysis")) { | 
|  | if (classInitializationAnalysis.isClassDefinitelyLoadedBeforeInstruction( | 
|  | target.getHolderType(), invoke, timing)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | // Check for class initializer side effects when loading this class, as inlining might remove | 
|  | // the load operation. | 
|  | // | 
|  | // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-5.html#jvms-5.5. | 
|  | // | 
|  | // For simplicity, we are conservative and consider all interfaces, not only the ones with | 
|  | // default methods. | 
|  | try (Timing t0 = timing.begin("Clinit may have side effects")) { | 
|  | if (!target.getHolder().classInitializationMayHaveSideEffectsInContext(appView, context)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | boolean bypassClinitForInlining = | 
|  | appView.withGeneratedMessageLiteBuilderShrinker( | 
|  | shrinker -> shrinker.bypassClinitForInlining(target), false); | 
|  | if (bypassClinitForInlining) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public static boolean isInliningBlockedDueToArrayClone( | 
|  | ProgramMethod context, ProgramMethod singleTarget, AppView<?> appView) { | 
|  | if (!appView.options().canHaveArtArrayCloneFromInterfaceMethodBug()) { | 
|  | return false; | 
|  | } | 
|  | if (!context.getHolder().isInterface()) { | 
|  | return false; | 
|  | } | 
|  | return singleTarget.registerCodeReferencesWithResult( | 
|  | new DefaultUseRegistryWithResult<>(appView, context, false) { | 
|  | @Override | 
|  | public void registerInvokeVirtual(DexMethod method) { | 
|  | if (appView.dexItemFactory().isArrayClone(method)) { | 
|  | setResult(true); | 
|  | } | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public boolean canInlineInstanceInitializer( | 
|  | InlineAction.Builder actionBuilder, | 
|  | IRCode code, | 
|  | InvokeDirect invoke, | 
|  | ProgramMethod singleTarget, | 
|  | InliningIRProvider inliningIRProvider, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (!inlinerOptions.isConstructorInliningEnabled()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | IRCode inlinee = inliningIRProvider.getInliningIR(invoke, singleTarget); | 
|  |  | 
|  | // In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and | 
|  | // Newly Created Objects" it says: | 
|  | // | 
|  | // Before that method invokes another instance initialization method of myClass or its direct | 
|  | // superclass on this, the only operation the method can perform on this is assigning fields | 
|  | // declared within myClass. | 
|  |  | 
|  | // Allow inlining a constructor into a constructor of the same class, as the constructor code | 
|  | // is expected to adhere to the VM specification. | 
|  | DexType callerMethodHolder = method.getHolderType(); | 
|  | DexType calleeMethodHolder = singleTarget.getHolderType(); | 
|  |  | 
|  | // Forwarding constructor calls that target a constructor in the same class can always be | 
|  | // inlined. | 
|  | if (method.getDefinition().isInstanceInitializer() | 
|  | && callerMethodHolder == calleeMethodHolder | 
|  | && invoke.getReceiver() == code.getThis()) { | 
|  | inliningIRProvider.cacheInliningIR(invoke, inlinee); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Only allow inlining a constructor into a non-constructor if: | 
|  | // (1) the first use of the uninitialized object is the receiver of an invoke of <init>(), | 
|  | // (2) the constructor does not initialize any final fields, as such is only allowed from within | 
|  | //     a constructor of the corresponding class, and | 
|  | // (3) the constructors own <init>() call is on the same class. | 
|  | // | 
|  | // Note that, due to (3), we do allow inlining of `A(int x)` into another class, but not the | 
|  | // default constructor `A()`, since the default constructor invokes Object.<init>(). | 
|  | // | 
|  | //   class A { | 
|  | //     A() { ... } | 
|  | //     A(int x) { | 
|  | //       this() | 
|  | //       ... | 
|  | //     } | 
|  | //   } | 
|  | Value thisValue = inlinee.entryBlock().entry().asArgument().outValue(); | 
|  |  | 
|  | List<InvokeDirect> initCallsOnThis = new ArrayList<>(); | 
|  | for (Instruction instruction : inlinee.instructions()) { | 
|  | if (instruction.isInvokeConstructor(appView.dexItemFactory())) { | 
|  | InvokeDirect initCall = instruction.asInvokeDirect(); | 
|  | Value receiver = initCall.getReceiver().getAliasedValue(); | 
|  | if (receiver == thisValue) { | 
|  | // The <init>() call of the constructor must be on the same class when targeting the JVM | 
|  | // and Dalvik. | 
|  | if (!options.canInitNewInstanceUsingSuperclassConstructor() | 
|  | && calleeMethodHolder != initCall.getInvokedMethod().getHolderType()) { | 
|  | whyAreYouNotInliningReporter | 
|  | .reportUnsafeConstructorInliningDueToIndirectConstructorCall(initCall); | 
|  | return false; | 
|  | } | 
|  | initCallsOnThis.add(initCall); | 
|  | } | 
|  | } else if (instruction.isInstancePut()) { | 
|  | // Final fields may not be initialized outside of a constructor in the enclosing class. | 
|  | InstancePut instancePut = instruction.asInstancePut(); | 
|  | DexClassAndField target = | 
|  | instancePut.resolveField(appView, singleTarget).getResolutionPair(); | 
|  | if (target == null) { | 
|  | whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToMissingFieldAssignment( | 
|  | instancePut); | 
|  | return false; | 
|  | } | 
|  | if (target.getAccessFlags().isFinal()) { | 
|  | if (inlinerOptions.enableConstructorInliningWithFinalFields | 
|  | && (options.canUseJavaLangVarHandleStoreStoreFence(appView) | 
|  | || inlinerOptions.skipStoreStoreFenceInConstructorInlining) | 
|  | && methodProcessor.hasWaves() | 
|  | && target.isProgramField()) { | 
|  | actionBuilder.setShouldEnsureStoreStoreFence(target.asProgramField()); | 
|  | } else { | 
|  | whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToFinalFieldAssignment( | 
|  | instancePut); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (initCallsOnThis.isEmpty()) { | 
|  | // In the unusual case where there is no parent/forwarding constructor call, there must be no | 
|  | // instance-put instructions that assign fields on the receiver. | 
|  | for (Instruction user : thisValue.uniqueUsers()) { | 
|  | if (user.isInstancePut() && user.asInstancePut().object().getAliasedValue() == thisValue) { | 
|  | whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToUninitializedObjectUse( | 
|  | user); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // Check that there are no uses of the uninitialized object before it gets initialized. | 
|  | int markingColor = inlinee.reserveMarkingColor(); | 
|  | for (InvokeDirect initCallOnThis : initCallsOnThis) { | 
|  | BasicBlock block = initCallOnThis.getBlock(); | 
|  | for (Instruction instruction : block.instructionsBefore(initCallOnThis)) { | 
|  | for (Value inValue : instruction.inValues()) { | 
|  | Value root = inValue.getAliasedValue(); | 
|  | if (root == thisValue) { | 
|  | inlinee.returnMarkingColor(markingColor); | 
|  | whyAreYouNotInliningReporter | 
|  | .reportUnsafeConstructorInliningDueToUninitializedObjectUse(instruction); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | for (BasicBlock predecessor : block.getPredecessors()) { | 
|  | inlinee.markTransitivePredecessors(predecessor, markingColor); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (BasicBlock block : inlinee.getBlocks()) { | 
|  | if (block.isMarked(markingColor)) { | 
|  | for (Instruction instruction : block.getInstructions()) { | 
|  | for (Value inValue : instruction.inValues()) { | 
|  | Value root = inValue.getAliasedValue(); | 
|  | if (root == thisValue) { | 
|  | inlinee.returnMarkingColor(markingColor); | 
|  | whyAreYouNotInliningReporter | 
|  | .reportUnsafeConstructorInliningDueToUninitializedObjectUse(instruction); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | inlinee.returnMarkingColor(markingColor); | 
|  | } | 
|  |  | 
|  | inliningIRProvider.cacheInliningIR(invoke, inlinee); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean stillHasBudget( | 
|  | InlineAction action, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (action.mustBeInlined()) { | 
|  | return true; | 
|  | } | 
|  | return stillHasBudgetForNonForceInlining(whyAreYouNotInliningReporter); | 
|  | } | 
|  |  | 
|  | private boolean stillHasBudgetForNonForceInlining( | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (instructionAllowance <= 0) { | 
|  | whyAreYouNotInliningReporter.reportInstructionBudgetIsExceeded(); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean willExceedBudget( | 
|  | InlineAction action, | 
|  | IRCode code, | 
|  | IRCode inlinee, | 
|  | InvokeMethod invoke, | 
|  | BasicBlock block, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (action.mustBeInlined()) { | 
|  | return false; | 
|  | } | 
|  | return willExceedInstructionBudget(inlinee, whyAreYouNotInliningReporter) | 
|  | || willExceedMonitorEnterValuesBudget(code, invoke, inlinee, whyAreYouNotInliningReporter) | 
|  | || willExceedControlFlowResolutionBlocksBudget( | 
|  | inlinee, block, whyAreYouNotInliningReporter); | 
|  | } | 
|  |  | 
|  | private boolean willExceedInstructionBudget( | 
|  | IRCode inlinee, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | int numberOfInstructions = numberOfInstructions(inlinee); | 
|  | if (instructionAllowance < numberOfInstructions(inlinee)) { | 
|  | whyAreYouNotInliningReporter.reportWillExceedInstructionBudget( | 
|  | numberOfInstructions, instructionAllowance); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * If inlining would lead to additional lock values in the caller, then check that the number of | 
|  | * lock values after inlining would not exceed the threshold. | 
|  | * | 
|  | * <p>The motivation for limiting the number of locks in a given method is that the register | 
|  | * allocator will attempt to pin a register for each lock value. Thus, if a method has many locks, | 
|  | * many registers will be pinned, which will lead to high register pressure. | 
|  | */ | 
|  | private boolean willExceedMonitorEnterValuesBudget( | 
|  | IRCode code, | 
|  | InvokeMethod invoke, | 
|  | IRCode inlinee, | 
|  | WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (!code.metadata().mayHaveMonitorInstruction()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!inlinee.metadata().mayHaveMonitorInstruction()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Set<DexType> constantMonitorEnterValues = Sets.newIdentityHashSet(); | 
|  | Set<Value> nonConstantMonitorEnterValues = Sets.newIdentityHashSet(); | 
|  | collectAllMonitorEnterValues(code, constantMonitorEnterValues, nonConstantMonitorEnterValues); | 
|  | if (constantMonitorEnterValues.isEmpty() && nonConstantMonitorEnterValues.isEmpty()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (Monitor monitor : inlinee.<Monitor>instructions(Instruction::isMonitorEnter)) { | 
|  | Value monitorEnterValue = monitor.object().getAliasedValue(); | 
|  | if (monitorEnterValue.isDefinedByInstructionSatisfying(Instruction::isArgument)) { | 
|  | monitorEnterValue = | 
|  | invoke | 
|  | .arguments() | 
|  | .get(monitorEnterValue.definition.asArgument().getIndex()) | 
|  | .getAliasedValue(); | 
|  | } | 
|  | addMonitorEnterValue( | 
|  | monitorEnterValue, constantMonitorEnterValues, nonConstantMonitorEnterValues); | 
|  | } | 
|  |  | 
|  | int numberOfMonitorEnterValuesAfterInlining = | 
|  | constantMonitorEnterValues.size() + nonConstantMonitorEnterValues.size(); | 
|  | int threshold = inlinerOptions.inliningMonitorEnterValuesAllowance; | 
|  | if (numberOfMonitorEnterValuesAfterInlining > threshold) { | 
|  | whyAreYouNotInliningReporter.reportWillExceedMonitorEnterValuesBudget( | 
|  | numberOfMonitorEnterValuesAfterInlining, threshold); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Inlining could lead to an explosion of move-exception and resolution moves. As an example, | 
|  | * consider the following piece of code. | 
|  | * | 
|  | * <pre> | 
|  | *   try { | 
|  | *     ... | 
|  | *     foo(); | 
|  | *     ... | 
|  | *   } catch (A e) { ... } | 
|  | *   } catch (B e) { ... } | 
|  | *   } catch (C e) { ... } | 
|  | * </pre> | 
|  | * | 
|  | * <p>The generated code for the above example will have a move-exception instruction for each of | 
|  | * the three catch handlers. Furthermore, the blocks with these move-exception instructions may | 
|  | * require a number of resolution moves to setup the register state for the catch handlers. When | 
|  | * inlining foo(), the generated code will have a move-exception instruction *for each of the | 
|  | * instructions in foo() that can throw*, along with the necessary resolution moves for each | 
|  | * exception-edge. We therefore abort inlining if the number of exception-edges explode. | 
|  | */ | 
|  | private boolean willExceedControlFlowResolutionBlocksBudget( | 
|  | IRCode inlinee, BasicBlock block, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { | 
|  | if (!block.hasCatchHandlers()) { | 
|  | return false; | 
|  | } | 
|  | int numberOfThrowingInstructionsInInlinee = 0; | 
|  | for (BasicBlock inlineeBlock : inlinee.blocks) { | 
|  | numberOfThrowingInstructionsInInlinee += inlineeBlock.numberOfThrowingInstructions(); | 
|  | } | 
|  | // Estimate the number of "control flow resolution blocks", where we will insert a | 
|  | // move-exception instruction (if needed), along with all the resolution moves that | 
|  | // will be needed to setup the register state for the catch handler. | 
|  | int estimatedNumberOfControlFlowResolutionBlocks = | 
|  | numberOfThrowingInstructionsInInlinee * block.numberOfCatchHandlers(); | 
|  | // Abort if inlining could lead to an explosion in the number of control flow | 
|  | // resolution blocks that setup the register state before the actual catch handler. | 
|  | int threshold = inlinerOptions.inliningControlFlowResolutionBlocksThreshold; | 
|  | if (estimatedNumberOfControlFlowResolutionBlocks >= threshold) { | 
|  | whyAreYouNotInliningReporter.reportPotentialExplosionInExceptionalControlFlowResolutionBlocks( | 
|  | estimatedNumberOfControlFlowResolutionBlocks, threshold); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void markInlined(IRCode inlinee) { | 
|  | // TODO(118734615): All inlining use from the budget - should that only be SIMPLE? | 
|  | instructionAllowance -= numberOfInstructions(inlinee); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ClassTypeElement getReceiverTypeOrDefault( | 
|  | InvokeMethod invoke, ClassTypeElement defaultValue) { | 
|  | return defaultValue; | 
|  | } | 
|  | } |