| // Copyright (c) 2023, 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.info; |
| |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DefaultUseRegistryWithResult; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.graph.lens.GraphLens; |
| import com.android.tools.r8.ir.conversion.PostMethodProcessor; |
| import com.android.tools.r8.shaking.AppInfoWithLiveness; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.ExecutorService; |
| |
| /** |
| * Finds the methods in the program that should be reprocessed due to non-trivial method resolution |
| * optimization info. |
| */ |
| class MethodResolutionOptimizationInfoReprocessingEnqueuer { |
| |
| private final AppView<AppInfoWithLiveness> appView; |
| |
| MethodResolutionOptimizationInfoReprocessingEnqueuer(AppView<AppInfoWithLiveness> appView) { |
| this.appView = appView; |
| } |
| |
| public void enqueueMethodForReprocessing( |
| ExecutorService executorService, PostMethodProcessor.Builder postMethodProcessorBuilder) |
| throws ExecutionException { |
| // Bring the methods to reprocess set up-to-date with the current graph lens. |
| postMethodProcessorBuilder.rewrittenWithLens(appView); |
| enqueueAffectedCallers(postMethodProcessorBuilder, executorService); |
| } |
| |
| private void enqueueAffectedCallers( |
| PostMethodProcessor.Builder postMethodProcessorBuilder, ExecutorService executorService) |
| throws ExecutionException { |
| GraphLens currentGraphLens = appView.graphLens(); |
| InternalOptions options = appView.options(); |
| Collection<List<ProgramMethod>> methodsToReprocess = |
| ThreadUtils.processItemsWithResults( |
| appView.appInfo().classes(), |
| clazz -> { |
| List<ProgramMethod> methodsToReprocessInClass = new ArrayList<>(); |
| clazz.forEachProgramMethodMatching( |
| DexEncodedMethod::hasCode, |
| method -> { |
| if (!postMethodProcessorBuilder.contains(method, currentGraphLens) |
| && appView.getKeepInfo(method).isReprocessingAllowed(options, method)) { |
| AffectedMethodUseRegistry registry = |
| new AffectedMethodUseRegistry(appView, method); |
| if (method.registerCodeReferencesWithResult(registry)) { |
| methodsToReprocessInClass.add(method); |
| } |
| } |
| }); |
| return methodsToReprocessInClass; |
| }, |
| options.getThreadingModule(), |
| executorService); |
| methodsToReprocess.forEach( |
| methodsToReprocessInClass -> |
| postMethodProcessorBuilder.addAll(methodsToReprocessInClass, currentGraphLens)); |
| } |
| |
| static class AffectedMethodUseRegistry |
| extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> { |
| |
| private final AppView<AppInfoWithLiveness> appViewWithLiveness; |
| |
| AffectedMethodUseRegistry( |
| AppView<AppInfoWithLiveness> appViewWithLiveness, ProgramMethod context) { |
| super(appViewWithLiveness, context, false); |
| this.appViewWithLiveness = appViewWithLiveness; |
| } |
| |
| private void markAffected() { |
| setResult(Boolean.TRUE); |
| } |
| |
| @Override |
| public void registerInvokeDirect(DexMethod method) { |
| DexMethod rewrittenMethod = |
| appViewWithLiveness.graphLens().lookupInvokeDirect(method, getContext()).getReference(); |
| registerInvokeMethod(rewrittenMethod); |
| } |
| |
| @Override |
| public void registerInvokeInterface(DexMethod method) { |
| DexMethod rewrittenMethod = |
| appViewWithLiveness |
| .graphLens() |
| .lookupInvokeInterface(method, getContext()) |
| .getReference(); |
| registerInvokeMethod(rewrittenMethod); |
| } |
| |
| @Override |
| public void registerInvokeStatic(DexMethod method) { |
| DexMethod rewrittenMethod = |
| appViewWithLiveness.graphLens().lookupInvokeStatic(method, getContext()).getReference(); |
| registerInvokeMethod(rewrittenMethod); |
| } |
| |
| @Override |
| public void registerInvokeSuper(DexMethod method) { |
| DexMethod rewrittenMethod = |
| appViewWithLiveness.graphLens().lookupInvokeSuper(method, getContext()).getReference(); |
| registerInvokeMethod(rewrittenMethod); |
| } |
| |
| @Override |
| public void registerInvokeVirtual(DexMethod method) { |
| DexMethod rewrittenMethod = |
| appViewWithLiveness.graphLens().lookupInvokeVirtual(method, getContext()).getReference(); |
| registerInvokeMethod(rewrittenMethod); |
| } |
| |
| private void registerInvokeMethod(DexMethod method) { |
| SingleResolutionResult<?> resolutionResult = |
| appViewWithLiveness |
| .appInfo() |
| .unsafeResolveMethodDueToDexFormatLegacy(method) |
| .asSingleResolution(); |
| if (resolutionResult == null) { |
| return; |
| } |
| |
| MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection = |
| appViewWithLiveness.getMethodResolutionOptimizationInfoCollection(); |
| MethodOptimizationInfo optimizationInfo = |
| methodResolutionOptimizationInfoCollection.get( |
| resolutionResult.getResolvedMethod(), resolutionResult.getResolvedHolder()); |
| if (!optimizationInfo.isDefault()) { |
| markAffected(); |
| } |
| } |
| } |
| } |