| // 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.numberunboxer; |
| |
| 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.DexField; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexProgramClass; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.ir.code.InvokeType; |
| import com.android.tools.r8.ir.conversion.PostMethodProcessor; |
| import com.android.tools.r8.shaking.AppInfoWithLiveness; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import com.android.tools.r8.utils.Timing; |
| 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 number unboxing. */ |
| public class NumberUnboxerMethodReprocessingEnqueuer { |
| |
| private final AppView<AppInfoWithLiveness> appView; |
| private final NumberUnboxerLens numberUnboxerLens; |
| |
| public NumberUnboxerMethodReprocessingEnqueuer( |
| AppView<AppInfoWithLiveness> appView, NumberUnboxerLens numberUnboxerLens) { |
| this.appView = appView; |
| this.numberUnboxerLens = numberUnboxerLens; |
| } |
| |
| public void enqueueMethodsForReprocessing( |
| PostMethodProcessor.Builder postMethodProcessorBuilder, |
| ExecutorService executorService, |
| Timing timing) |
| throws ExecutionException { |
| timing.begin("Enqueue methods for reprocessing due to the number unboxer"); |
| assert appView.graphLens() == numberUnboxerLens; |
| postMethodProcessorBuilder.rewrittenWithLens(appView); |
| enqueueAffectedCallees(postMethodProcessorBuilder); |
| enqueueAffectedCallers(postMethodProcessorBuilder, executorService); |
| timing.end(); |
| } |
| |
| boolean shouldReprocess(DexMethod reference) { |
| return reference.isNotIdenticalTo(numberUnboxerLens.getPreviousMethodSignature(reference)); |
| } |
| |
| private void enqueueAffectedCallees(PostMethodProcessor.Builder postMethodProcessorBuilder) { |
| for (DexProgramClass clazz : appView.appInfo().classes()) { |
| clazz.forEachProgramMethodMatching( |
| DexEncodedMethod::hasCode, |
| method -> { |
| if (method.getDefinition().getCode().isSharedCodeObject()) { |
| return; |
| } |
| if (shouldReprocess(method.getReference())) { |
| assert appView.getKeepInfo(method).isReprocessingAllowed(appView.options(), method); |
| postMethodProcessorBuilder.add(method, numberUnboxerLens); |
| } |
| }); |
| } |
| } |
| |
| // TODO(b/190154391): This could invalidate the @NeverReprocessMethod testing annotations (non |
| // critical). If @NeverReprocessMethod is used, we would need to scan the application to mark |
| // methods as unoptimizable prior to removing parameters from the application. |
| private void enqueueAffectedCallers( |
| PostMethodProcessor.Builder postMethodProcessorBuilder, ExecutorService executorService) |
| throws ExecutionException { |
| Collection<List<ProgramMethod>> methodsToReprocess = |
| ThreadUtils.processItemsWithResultsThatMatches( |
| appView.appInfo().classes(), |
| clazz -> { |
| List<ProgramMethod> methodsToReprocessInClass = new ArrayList<>(); |
| clazz.forEachProgramMethodMatching( |
| DexEncodedMethod::hasCode, |
| method -> { |
| AffectedMembersGraphLensUseRegistry registry = |
| new AffectedMembersGraphLensUseRegistry(appView, method); |
| if (method.registerCodeReferencesWithResult(registry)) { |
| assert !method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite(); |
| methodsToReprocessInClass.add(method); |
| } |
| }); |
| return methodsToReprocessInClass; |
| }, |
| res -> !res.isEmpty(), |
| appView.options().getThreadingModule(), |
| executorService); |
| methodsToReprocess.forEach( |
| methodsToReprocessInClass -> |
| postMethodProcessorBuilder.addAll(methodsToReprocessInClass, numberUnboxerLens)); |
| } |
| |
| public class AffectedMembersGraphLensUseRegistry |
| extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> { |
| |
| public AffectedMembersGraphLensUseRegistry( |
| AppView<AppInfoWithLiveness> appViewWithLiveness, ProgramMethod context) { |
| super(appViewWithLiveness, context, false); |
| } |
| |
| private void markAffected() { |
| setResult(Boolean.TRUE); |
| } |
| |
| @Override |
| public void registerInvokeDirect(DexMethod method) { |
| registerInvokeMethod(method, InvokeType.DIRECT); |
| } |
| |
| @Override |
| public void registerInvokeInterface(DexMethod method) { |
| registerInvokeMethod(method, InvokeType.INTERFACE); |
| } |
| |
| @Override |
| public void registerInvokeStatic(DexMethod method) { |
| registerInvokeMethod(method, InvokeType.STATIC); |
| } |
| |
| @Override |
| public void registerInvokeSuper(DexMethod method) { |
| registerInvokeMethod(method, InvokeType.SUPER); |
| } |
| |
| @Override |
| public void registerInvokeVirtual(DexMethod method) { |
| registerInvokeMethod(method, InvokeType.VIRTUAL); |
| } |
| |
| private void registerInvokeMethod(DexMethod method, InvokeType invokeType) { |
| // TODO(b/314117865): This is assuming that there are no non-rebound method references. |
| DexMethod reference = |
| numberUnboxerLens |
| .lookupMethod( |
| method, getContext().getReference(), invokeType, numberUnboxerLens.getPrevious()) |
| .getReference(); |
| assert reference != null; |
| if (shouldReprocess(reference)) { |
| markAffected(); |
| } |
| } |
| |
| @Override |
| public void registerInstanceFieldRead(DexField field) { |
| registerFieldAccess(field); |
| } |
| |
| @Override |
| public void registerInstanceFieldWrite(DexField field) { |
| registerFieldAccess(field); |
| } |
| |
| @Override |
| public void registerStaticFieldRead(DexField field) { |
| registerFieldAccess(field); |
| } |
| |
| @Override |
| public void registerStaticFieldWrite(DexField field) { |
| registerFieldAccess(field); |
| } |
| |
| private void registerFieldAccess(DexField unused) { |
| // TODO(b/307872552): Deal with unboxed fields. |
| } |
| } |
| } |