Introduce NumberUnboxerMethodReprocessingEnqueuer
Change-Id: Ifb675a4f57b262256036f41a9a9de8dc5139353a
diff --git a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
index d906b4c..63aa73a 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
import java.util.Map;
import java.util.function.Function;
-import java.util.stream.Collectors;
/**
* GraphLens implementation with a parent lens where the mapping of types, methods and fields can be
@@ -291,24 +290,6 @@
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
- typeMap.forEach(
- (from, to) ->
- builder
- .append(from.getTypeName())
- .append(" -> ")
- .append(to.getTypeName())
- .append(System.lineSeparator()));
- fieldMap.forEachManyToOneMapping(
- (fromSet, to) -> {
- builder.append(
- fromSet.stream()
- .map(DexField::toSourceString)
- .collect(Collectors.joining("," + System.lineSeparator())));
- builder.append(" -> ");
- builder.append(to.toSourceString()).append(System.lineSeparator());
- });
- builder.append(getPrevious().toString());
- return builder.toString();
+ return getClass().getTypeName();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerImpl.java
index c6b0d70..1e623e1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerImpl.java
@@ -40,18 +40,16 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
public class NumberUnboxerImpl extends NumberUnboxer {
- private AppView<AppInfoWithLiveness> appView;
- private DexItemFactory factory;
- private Set<DexType> boxedTypes;
+ private final AppView<AppInfoWithLiveness> appView;
+ private final DexItemFactory factory;
+ private final Set<DexType> boxedTypes;
// Temporarily keep the information here, and not in the MethodOptimizationInfo as the
// optimization is developed and unfinished.
- private Map<DexMethod, MethodBoxingStatus> methodBoxingStatus = new ConcurrentHashMap<>();
+ private final Map<DexMethod, MethodBoxingStatus> methodBoxingStatus = new ConcurrentHashMap<>();
private Map<DexMethod, DexMethod> virtualMethodsRepresentative;
public NumberUnboxerImpl(AppView<AppInfoWithLiveness> appView) {
@@ -314,13 +312,15 @@
NumberUnboxerLens numberUnboxerLens =
new NumberUnboxerTreeFixer(appView, unboxingResult).fixupTree(executorService, timing);
appView.rewriteWithLens(numberUnboxerLens, executorService, timing);
+ new NumberUnboxerMethodReprocessingEnqueuer(appView, numberUnboxerLens)
+ .enqueueMethodsForReprocessing(postMethodProcessorBuilder, executorService, timing);
- enqueueMethodsForReprocessing(postMethodProcessorBuilder);
+ if (appView.testing().printNumberUnboxed) {
+ printNumberUnboxed(unboxingResult);
+ }
+ }
- // TODO(b/307872552): The result encodes for each method which return value and parameter of
- // each method should be unboxed. We need here to implement the treefixer using it, and set up
- // correctly the reprocessing with a code rewriter similar to the enum unboxing code rewriter.
- // We should implement the optimization, so far, we just print out the result.
+ private void printNumberUnboxed(Map<DexMethod, MethodBoxingStatusResult> unboxingResult) {
StringBuilder stringBuilder = new StringBuilder();
unboxingResult.forEach(
(k, v) -> {
@@ -344,19 +344,6 @@
appView.reporter().warning(stringBuilder.toString());
}
- private void enqueueMethodsForReprocessing(
- PostMethodProcessor.Builder postMethodProcessorBuilder) {
- postMethodProcessorBuilder.rewrittenWithLens(appView);
-
- // TODO(b/307872552): Implement the reprocessing enqueuer so that only relevant methods are
- // reprocessed. For testing we temporarily reprocess all.
- postMethodProcessorBuilder.addAll(
- appView.appInfo().classes().stream()
- .flatMap(c -> StreamSupport.stream(c.programMethods().spliterator(), false))
- .collect(Collectors.toList()),
- appView.graphLens());
- }
-
@Override
public void onMethodPruned(ProgramMethod method) {
// TODO(b/307872552): Should we do something about this? We might need to change the
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerMethodReprocessingEnqueuer.java
new file mode 100644
index 0000000..a48a77f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerMethodReprocessingEnqueuer.java
@@ -0,0 +1,175 @@
+// 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.appInfo().isNeverReprocessMethod(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.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 7e3e4b5..b71ad68 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2174,8 +2174,8 @@
public static class TestingOptions {
public boolean enableNumberUnboxer = false;
+ public boolean printNumberUnboxed = false;
public boolean roundtripThroughLir = false;
-
public boolean canUseLir(AppView<?> appView) {
return appView.enableWholeProgramOptimizations();
}
diff --git a/src/test/java/com/android/tools/r8/numberunboxing/SimpleNumberUnboxingTest.java b/src/test/java/com/android/tools/r8/numberunboxing/SimpleNumberUnboxingTest.java
index c14645d..ceaf904 100644
--- a/src/test/java/com/android/tools/r8/numberunboxing/SimpleNumberUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/numberunboxing/SimpleNumberUnboxingTest.java
@@ -43,6 +43,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.addOptionsModification(opt -> opt.testing.enableNumberUnboxer = true)
+ .addOptionsModification(opt -> opt.testing.printNumberUnboxed = true)
.setMinApi(parameters)
.allowDiagnosticWarningMessages()
.compile()