Reapply "Extend single caller inliner to all invoke types"
This reverts commit e81aa3800fa5ddcf963ee3661982eb9b37ead80f.
Change-Id: I0a5ebdf8d3091541aa6572c4c62d3b8c288e8bea
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index aa82269..1f95169 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -66,6 +66,7 @@
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -128,7 +129,7 @@
this.reprocessingCriteriaCollection = reprocessingCriteriaCollection;
}
- public synchronized void addMonomorphicVirtualMethods(Set<DexMethod> extension) {
+ public synchronized void addMonomorphicVirtualMethods(Collection<DexMethod> extension) {
monomorphicVirtualMethods.addAll(extension);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
index dd7eac12..3a9e9c6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.optimize.argumentpropagation.codescanner;
-import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
@@ -12,16 +11,11 @@
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorCodeScanner;
-import com.android.tools.r8.optimize.argumentpropagation.utils.DepthFirstTopDownClassHierarchyTraversal;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.collections.DexMethodSignatureMap;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Sets;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.IdentityHashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.function.Consumer;
/**
@@ -31,83 +25,7 @@
* <p>The analysis can be used to easily mark effectively final classes and methods as final, and
* therefore does this as a side effect.
*/
-public class VirtualRootMethodsAnalysis extends DepthFirstTopDownClassHierarchyTraversal {
-
- static class VirtualRootMethod {
-
- private final VirtualRootMethod parent;
- private final ProgramMethod root;
- private final ProgramMethodSet overrides = ProgramMethodSet.create();
-
- VirtualRootMethod(ProgramMethod root) {
- this(root, null);
- }
-
- VirtualRootMethod(ProgramMethod root, VirtualRootMethod parent) {
- assert root != null;
- this.parent = parent;
- this.root = root;
- }
-
- void addOverride(ProgramMethod override) {
- assert override.getDefinition() != root.getDefinition();
- assert override.getMethodSignature().equals(root.getMethodSignature());
- overrides.add(override);
- if (hasParent()) {
- getParent().addOverride(override);
- }
- }
-
- boolean hasParent() {
- return parent != null;
- }
-
- VirtualRootMethod getParent() {
- return parent;
- }
-
- ProgramMethod getRoot() {
- return root;
- }
-
- ProgramMethod getSingleNonAbstractMethod() {
- ProgramMethod singleNonAbstractMethod = root.getAccessFlags().isAbstract() ? null : root;
- for (ProgramMethod override : overrides) {
- if (!override.getAccessFlags().isAbstract()) {
- if (singleNonAbstractMethod != null) {
- // Not a single non-abstract method.
- return null;
- }
- singleNonAbstractMethod = override;
- }
- }
- assert singleNonAbstractMethod == null
- || !singleNonAbstractMethod.getAccessFlags().isAbstract();
- return singleNonAbstractMethod;
- }
-
- void forEach(Consumer<ProgramMethod> consumer) {
- consumer.accept(root);
- overrides.forEach(consumer);
- }
-
- boolean hasOverrides() {
- return !overrides.isEmpty();
- }
-
- boolean isInterfaceMethodWithSiblings() {
- // TODO(b/190154391): Conservatively returns true for all interface methods, but should only
- // return true for those with siblings.
- return root.getHolder().isInterface();
- }
- }
-
- private final Map<DexProgramClass, DexMethodSignatureMap<VirtualRootMethod>>
- virtualRootMethodsPerClass = new IdentityHashMap<>();
-
- private final Set<DexMethod> monomorphicVirtualMethods = Sets.newIdentityHashSet();
-
- private final Map<DexMethod, DexMethod> virtualRootMethods = new IdentityHashMap<>();
+public class VirtualRootMethodsAnalysis extends VirtualRootMethodsAnalysisBase {
public VirtualRootMethodsAnalysis(
AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
@@ -121,11 +39,23 @@
run(stronglyConnectedComponent);
// Commit the result to the code scanner.
- codeScanner.addMonomorphicVirtualMethods(monomorphicVirtualMethods);
+ List<DexMethod> monomorphicVirtualMethodReferences =
+ new ArrayList<>(
+ monomorphicVirtualRootMethods.size() + monomorphicVirtualNonRootMethods.size());
+ for (ProgramMethod method :
+ Iterables.concat(monomorphicVirtualRootMethods, monomorphicVirtualNonRootMethods)) {
+ monomorphicVirtualMethodReferences.add(method.getReference());
+ }
+ codeScanner.addMonomorphicVirtualMethods(monomorphicVirtualMethodReferences);
codeScanner.addVirtualRootMethods(virtualRootMethods);
}
@Override
+ protected void acceptVirtualMethod(ProgramMethod method, VirtualRootMethod virtualRootMethod) {
+ promoteToFinalIfPossible(method, virtualRootMethod);
+ }
+
+ @Override
public void forEachSubClass(DexProgramClass clazz, Consumer<DexProgramClass> consumer) {
List<DexProgramClass> subclasses = immediateSubtypingInfo.getSubclasses(clazz);
if (subclasses.isEmpty()) {
@@ -135,78 +65,6 @@
}
}
- @Override
- public void visit(DexProgramClass clazz) {
- DexMethodSignatureMap<VirtualRootMethod> state = computeVirtualRootMethodsState(clazz);
- virtualRootMethodsPerClass.put(clazz, state);
- }
-
- private DexMethodSignatureMap<VirtualRootMethod> computeVirtualRootMethodsState(
- DexProgramClass clazz) {
- DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForClass =
- DexMethodSignatureMap.create();
- immediateSubtypingInfo.forEachImmediateProgramSuperClass(
- clazz,
- superclass -> {
- DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForSuperclass =
- virtualRootMethodsPerClass.get(superclass);
- virtualRootMethodsForSuperclass.forEach(
- (signature, info) ->
- virtualRootMethodsForClass.computeIfAbsent(
- signature, ignoreKey(() -> new VirtualRootMethod(info.getRoot(), info))));
- });
- clazz.forEachProgramVirtualMethod(
- method -> {
- if (virtualRootMethodsForClass.containsKey(method)) {
- virtualRootMethodsForClass.get(method).getParent().addOverride(method);
- } else {
- virtualRootMethodsForClass.put(method, new VirtualRootMethod(method));
- }
- });
- return virtualRootMethodsForClass;
- }
-
- @Override
- public void prune(DexProgramClass clazz) {
- // Record the overrides for each virtual method that is rooted at this class.
- DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForClass =
- virtualRootMethodsPerClass.remove(clazz);
- clazz.forEachProgramVirtualMethod(
- rootCandidate -> {
- VirtualRootMethod virtualRootMethod =
- virtualRootMethodsForClass.remove(rootCandidate.getMethodSignature());
- promoteToFinalIfPossible(rootCandidate, virtualRootMethod);
- if (!rootCandidate.isStructurallyEqualTo(virtualRootMethod.getRoot())) {
- return;
- }
- boolean isMonomorphicVirtualMethod =
- !clazz.isInterface() && !virtualRootMethod.hasOverrides();
- if (isMonomorphicVirtualMethod) {
- monomorphicVirtualMethods.add(rootCandidate.getReference());
- } else {
- ProgramMethod singleNonAbstractMethod = virtualRootMethod.getSingleNonAbstractMethod();
- if (singleNonAbstractMethod != null
- && !virtualRootMethod.isInterfaceMethodWithSiblings()) {
- virtualRootMethod.forEach(
- method -> {
- // Interface methods can have siblings and can therefore not be mapped to their
- // unique non-abstract implementation, unless the interface method does not have
- // any siblings.
- virtualRootMethods.put(
- method.getReference(), singleNonAbstractMethod.getReference());
- });
- if (!singleNonAbstractMethod.getHolder().isInterface()) {
- monomorphicVirtualMethods.add(singleNonAbstractMethod.getReference());
- }
- } else {
- virtualRootMethod.forEach(
- method ->
- virtualRootMethods.put(method.getReference(), rootCandidate.getReference()));
- }
- }
- });
- }
-
private void promoteToFinalIfPossible(DexProgramClass clazz) {
if (!appView.testing().disableMarkingClassesFinal
&& !clazz.isAbstract()
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
new file mode 100644
index 0000000..b5f1374
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
@@ -0,0 +1,184 @@
+// Copyright (c) 2024, 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.optimize.argumentpropagation.codescanner;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.argumentpropagation.utils.DepthFirstTopDownClassHierarchyTraversal;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.DexMethodSignatureMap;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Computes the set of virtual methods for which we can use a monomorphic method state as well as
+ * the mapping from virtual methods to their representative root methods.
+ */
+public class VirtualRootMethodsAnalysisBase extends DepthFirstTopDownClassHierarchyTraversal {
+
+ protected static class VirtualRootMethod {
+
+ private final VirtualRootMethod parent;
+ private final ProgramMethod root;
+ private final ProgramMethodSet overrides = ProgramMethodSet.create();
+
+ VirtualRootMethod(ProgramMethod root) {
+ this(root, null);
+ }
+
+ VirtualRootMethod(ProgramMethod root, VirtualRootMethod parent) {
+ assert root != null;
+ this.parent = parent;
+ this.root = root;
+ }
+
+ void addOverride(ProgramMethod override) {
+ assert override.getDefinition() != root.getDefinition();
+ assert override.getMethodSignature().equals(root.getMethodSignature());
+ overrides.add(override);
+ if (hasParent()) {
+ getParent().addOverride(override);
+ }
+ }
+
+ boolean hasParent() {
+ return parent != null;
+ }
+
+ VirtualRootMethod getParent() {
+ return parent;
+ }
+
+ ProgramMethod getRoot() {
+ return root;
+ }
+
+ ProgramMethod getSingleNonAbstractMethod() {
+ ProgramMethod singleNonAbstractMethod = root.getAccessFlags().isAbstract() ? null : root;
+ for (ProgramMethod override : overrides) {
+ if (!override.getAccessFlags().isAbstract()) {
+ if (singleNonAbstractMethod != null) {
+ // Not a single non-abstract method.
+ return null;
+ }
+ singleNonAbstractMethod = override;
+ }
+ }
+ assert singleNonAbstractMethod == null
+ || !singleNonAbstractMethod.getAccessFlags().isAbstract();
+ return singleNonAbstractMethod;
+ }
+
+ void forEach(Consumer<ProgramMethod> consumer) {
+ consumer.accept(root);
+ overrides.forEach(consumer);
+ }
+
+ boolean hasOverrides() {
+ return !overrides.isEmpty();
+ }
+
+ boolean isInterfaceMethodWithSiblings() {
+ // TODO(b/190154391): Conservatively returns true for all interface methods, but should only
+ // return true for those with siblings.
+ return root.getHolder().isInterface();
+ }
+ }
+
+ private final Map<DexProgramClass, DexMethodSignatureMap<VirtualRootMethod>>
+ virtualRootMethodsPerClass = new IdentityHashMap<>();
+
+ protected final ProgramMethodSet monomorphicVirtualRootMethods = ProgramMethodSet.create();
+ protected final ProgramMethodSet monomorphicVirtualNonRootMethods = ProgramMethodSet.create();
+
+ protected final Map<DexMethod, DexMethod> virtualRootMethods = new IdentityHashMap<>();
+
+ protected VirtualRootMethodsAnalysisBase(
+ AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ super(appView, immediateSubtypingInfo);
+ }
+
+ @Override
+ public void visit(DexProgramClass clazz) {
+ DexMethodSignatureMap<VirtualRootMethod> state = computeVirtualRootMethodsState(clazz);
+ virtualRootMethodsPerClass.put(clazz, state);
+ }
+
+ private DexMethodSignatureMap<VirtualRootMethod> computeVirtualRootMethodsState(
+ DexProgramClass clazz) {
+ DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForClass =
+ DexMethodSignatureMap.create();
+ immediateSubtypingInfo.forEachImmediateProgramSuperClass(
+ clazz,
+ superclass -> {
+ DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForSuperclass =
+ virtualRootMethodsPerClass.get(superclass);
+ virtualRootMethodsForSuperclass.forEach(
+ (signature, info) ->
+ virtualRootMethodsForClass.computeIfAbsent(
+ signature, ignoreKey(() -> new VirtualRootMethod(info.getRoot(), info))));
+ });
+ clazz.forEachProgramVirtualMethod(
+ method -> {
+ if (virtualRootMethodsForClass.containsKey(method)) {
+ virtualRootMethodsForClass.get(method).getParent().addOverride(method);
+ } else {
+ virtualRootMethodsForClass.put(method, new VirtualRootMethod(method));
+ }
+ });
+ return virtualRootMethodsForClass;
+ }
+
+ @Override
+ public void prune(DexProgramClass clazz) {
+ // Record the overrides for each virtual method that is rooted at this class.
+ DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForClass =
+ virtualRootMethodsPerClass.remove(clazz);
+ clazz.forEachProgramVirtualMethod(
+ rootCandidate -> {
+ VirtualRootMethod virtualRootMethod =
+ virtualRootMethodsForClass.remove(rootCandidate.getMethodSignature());
+ acceptVirtualMethod(rootCandidate, virtualRootMethod);
+ if (!rootCandidate.isStructurallyEqualTo(virtualRootMethod.getRoot())) {
+ return;
+ }
+ boolean isMonomorphicVirtualMethod =
+ !clazz.isInterface() && !virtualRootMethod.hasOverrides();
+ if (isMonomorphicVirtualMethod) {
+ monomorphicVirtualRootMethods.add(rootCandidate);
+ } else {
+ ProgramMethod singleNonAbstractMethod = virtualRootMethod.getSingleNonAbstractMethod();
+ if (singleNonAbstractMethod != null
+ && !virtualRootMethod.isInterfaceMethodWithSiblings()) {
+ virtualRootMethod.forEach(
+ method -> {
+ // Interface methods can have siblings and can therefore not be mapped to their
+ // unique non-abstract implementation, unless the interface method does not have
+ // any siblings.
+ virtualRootMethods.put(
+ method.getReference(), singleNonAbstractMethod.getReference());
+ });
+ if (!singleNonAbstractMethod.getHolder().isInterface()) {
+ monomorphicVirtualNonRootMethods.add(singleNonAbstractMethod);
+ }
+ } else {
+ virtualRootMethod.forEach(
+ method ->
+ virtualRootMethods.put(method.getReference(), rootCandidate.getReference()));
+ }
+ }
+ });
+ }
+
+ protected void acceptVirtualMethod(ProgramMethod method, VirtualRootMethod virtualRootMethod) {
+ // Intentionally empty.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/MonomorphicVirtualMethodsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/MonomorphicVirtualMethodsAnalysis.java
new file mode 100644
index 0000000..8d589a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/MonomorphicVirtualMethodsAnalysis.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2024, 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.optimize.singlecaller;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.VirtualRootMethodsAnalysisBase;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class MonomorphicVirtualMethodsAnalysis extends VirtualRootMethodsAnalysisBase {
+
+ public MonomorphicVirtualMethodsAnalysis(
+ AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ super(appView, immediateSubtypingInfo);
+ }
+
+ public static ProgramMethodSet computeMonomorphicVirtualRootMethods(
+ AppView<AppInfoWithLiveness> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ List<Set<DexProgramClass>> stronglyConnectedComponents,
+ ExecutorService executorService)
+ throws ExecutionException {
+ ProgramMethodSet monomorphicVirtualMethods = ProgramMethodSet.createConcurrent();
+ ThreadUtils.processItems(
+ stronglyConnectedComponents,
+ stronglyConnectedComponent -> {
+ ProgramMethodSet monomorphicVirtualMethodsInComponent =
+ computeMonomorphicVirtualRootMethodsInComponent(
+ appView, immediateSubtypingInfo, stronglyConnectedComponent);
+ monomorphicVirtualMethods.addAll(monomorphicVirtualMethodsInComponent);
+ },
+ appView.options().getThreadingModule(),
+ executorService);
+ return monomorphicVirtualMethods;
+ }
+
+ private static ProgramMethodSet computeMonomorphicVirtualRootMethodsInComponent(
+ AppView<AppInfoWithLiveness> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ Set<DexProgramClass> stronglyConnectedComponent) {
+ MonomorphicVirtualMethodsAnalysis analysis =
+ new MonomorphicVirtualMethodsAnalysis(appView, immediateSubtypingInfo);
+ analysis.run(stronglyConnectedComponent);
+ return analysis.monomorphicVirtualRootMethods;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
index 780ae8a..02b5231 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
@@ -33,13 +34,15 @@
import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.Deque;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -61,16 +64,15 @@
}
private boolean shouldRun() {
- InternalOptions options = appView.options();
- return !options.debug
- && !options.intermediate
- && options.isOptimizing()
- && options.isShrinking();
+ return appView.options().getSingleCallerInlinerOptions().isEnabled();
}
public void run(ExecutorService executorService) throws ExecutionException {
+ ProgramMethodSet monomorphicVirtualMethods =
+ computeMonomorphicVirtualRootMethods(executorService);
ProgramMethodMap<ProgramMethod> singleCallerMethods =
- new SingleCallerScanner(appView).getSingleCallerMethods(executorService);
+ new SingleCallerScanner(appView, monomorphicVirtualMethods)
+ .getSingleCallerMethods(executorService);
if (singleCallerMethods.isEmpty()) {
return;
}
@@ -81,6 +83,21 @@
pruneItems(singleCallerMethods, executorService);
}
+ // We only allow single caller inlining of "direct dispatch virtual methods". We currently only
+ // deal with (rooted) virtual methods that do not override abstract/interface methods. In order to
+ // also deal with virtual methods that override abstract/interface methods we would need to record
+ // calls to the abstract/interface methods as calls to the non-abstract virtual method.
+ private ProgramMethodSet computeMonomorphicVirtualRootMethods(ExecutorService executorService)
+ throws ExecutionException {
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo =
+ ImmediateProgramSubtypingInfo.create(appView);
+ List<Set<DexProgramClass>> stronglyConnectedComponents =
+ new ProgramClassesBidirectedGraph(appView, immediateSubtypingInfo)
+ .computeStronglyConnectedComponents();
+ return MonomorphicVirtualMethodsAnalysis.computeMonomorphicVirtualRootMethods(
+ appView, immediateSubtypingInfo, stronglyConnectedComponents, executorService);
+ }
+
private void processCallees(
Inliner inliner,
ProgramMethodMap<ProgramMethod> singleCallerMethods,
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInlinerOptions.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInlinerOptions.java
new file mode 100644
index 0000000..fa8d1f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInlinerOptions.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2024, 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.optimize.singlecaller;
+
+import com.android.tools.r8.utils.InternalOptions;
+
+public class SingleCallerInlinerOptions {
+
+ private final InternalOptions options;
+
+ private boolean enable = true;
+
+ public SingleCallerInlinerOptions(InternalOptions options) {
+ this.options = options;
+ }
+
+ public boolean isEnabled() {
+ return enable
+ && !options.debug
+ && !options.intermediate
+ && options.isOptimizing()
+ && options.isShrinking();
+ }
+
+ public void setEnable(boolean enable) {
+ this.enable = enable;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
index 6ac2b54..fa0bdfd 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize.singlecaller;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.graph.AppView;
@@ -17,7 +16,7 @@
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.lightir.LirInstructionView;
-import com.android.tools.r8.lightir.LirOpcodes;
+import com.android.tools.r8.lightir.LirOpcodeUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ObjectUtils;
@@ -32,9 +31,12 @@
private static final ProgramMethod MULTIPLE_CALLERS = ProgramMethod.createSentinel();
private final AppView<AppInfoWithLiveness> appView;
+ private final ProgramMethodSet monomorphicVirtualMethods;
- SingleCallerScanner(AppView<AppInfoWithLiveness> appView) {
+ SingleCallerScanner(
+ AppView<AppInfoWithLiveness> appView, ProgramMethodSet monomorphicVirtualMethods) {
this.appView = appView;
+ this.monomorphicVirtualMethods = monomorphicVirtualMethods;
}
public ProgramMethodMap<ProgramMethod> getSingleCallerMethods(ExecutorService executorService)
@@ -139,28 +141,13 @@
if (referencedMethod.getHolderType().isArrayType()) {
return;
}
- if (referencedMethod.isInstanceInitializer(appView.dexItemFactory())) {
- ProgramMethod referencedProgramMethod =
- appView
- .appInfo()
- .unsafeResolveMethodDueToDexFormat(referencedMethod)
- .getResolvedProgramMethod();
- if (referencedProgramMethod != null) {
- recordCallEdge(method, referencedProgramMethod, threadLocalSingleCallerMethods);
- }
- } else {
- DexProgramClass referencedProgramMethodHolder =
- asProgramClassOrNull(
- appView
- .appInfo()
- .definitionForWithoutExistenceAssert(referencedMethod.getHolderType()));
- ProgramMethod referencedProgramMethod =
- referencedMethod.lookupOnProgramClass(referencedProgramMethodHolder);
- if (referencedProgramMethod != null
- && referencedProgramMethod.getAccessFlags().isPrivate()
- && !referencedProgramMethod.getAccessFlags().isStatic()) {
- recordCallEdge(method, referencedProgramMethod, threadLocalSingleCallerMethods);
- }
+ ProgramMethod resolvedMethod =
+ appView
+ .appInfo()
+ .unsafeResolveMethodDueToDexFormat(referencedMethod)
+ .getResolvedProgramMethod();
+ if (resolvedMethod != null) {
+ recordCallEdge(method, resolvedMethod, threadLocalSingleCallerMethods);
}
}
@@ -187,10 +174,7 @@
ProgramMethodMap<Integer> counters = ProgramMethodMap.create();
for (LirInstructionView view : code) {
int opcode = view.getOpcode();
- if (opcode != LirOpcodes.INVOKEDIRECT
- && opcode != LirOpcodes.INVOKEDIRECT_ITF
- // JDK 17 generates invokevirtual to private methods.
- && opcode != LirOpcodes.INVOKEVIRTUAL) {
+ if (!LirOpcodeUtils.isInvokeMethod(opcode)) {
continue;
}
DexMethod invokedMethod =
@@ -198,11 +182,17 @@
ProgramMethod resolvedMethod =
appView
.appInfo()
- .resolveMethod(invokedMethod, opcode == LirOpcodes.INVOKEDIRECT_ITF)
+ .resolveMethod(
+ invokedMethod, LirOpcodeUtils.getInterfaceBitFromInvokeOpcode(opcode))
.getResolvedProgramMethod();
- if (resolvedMethod != null && callees.contains(resolvedMethod)) {
- counters.put(resolvedMethod, counters.getOrDefault(resolvedMethod, 0) + 1);
+ if (resolvedMethod == null || !callees.contains(resolvedMethod)) {
+ continue;
}
+ if (resolvedMethod.getAccessFlags().belongsToVirtualPool()
+ && !monomorphicVirtualMethods.contains(resolvedMethod)) {
+ continue;
+ }
+ counters.put(resolvedMethod, counters.getOrDefault(resolvedMethod, 0) + 1);
}
callees.forEach(
(callee) -> {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index a021521..629d003 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -519,6 +519,7 @@
appView.getResourceShrinkerState().setEnqueuerCallback(this::recordReferenceFromResources);
}
if (mode.isTreeShaking()) {
+ InitializedClassesInInstanceMethodsAnalysis.register(appView, this);
GetArrayOfMissingTypeVerifyErrorWorkaround.register(appView, this);
InitializedClassesInInstanceMethodsAnalysis.register(appView, this);
InvokeVirtualToInterfaceVerifyErrorWorkaround.register(appView, this);
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
index 966addb..cac41ff 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
@@ -48,10 +48,10 @@
.addKeepClassRules(A.class)
.addKeepAttributeSignature()
.addOptionsModification(
- options ->
- options
- .getVerticalClassMergerOptions()
- .setEnableBridgeAnalysis(enableBridgeAnalysis))
+ options -> {
+ options.getSingleCallerInlinerOptions().setEnable(false);
+ options.getVerticalClassMergerOptions().setEnableBridgeAnalysis(enableBridgeAnalysis);
+ })
.addVerticallyMergedClassesInspector(
inspector -> inspector.assertMergedIntoSubtype(B.class).assertNoOtherClassesMerged())
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 55a961e..64e4c42 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.internal.proto;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -249,9 +249,8 @@
// Verify that the registry methods are still present in the output.
//
- // We expect findLiteExtensionByNumber2() to be inlined into findLiteExtensionByNumber1(). The
- // method findLiteExtensionByNumber1() has two call sites from findLiteExtensionByNumber(),
- // which prevents it from being single-caller inlined.
+ // We expect findLiteExtensionByNumber2() to be inlined into findLiteExtensionByNumber1() and
+ // findLiteExtensionByNumber1() to be inlined into findLiteExtensionByNumber().
{
ClassSubject generatedExtensionRegistryLoader = outputInspector.clazz(extensionRegistryName);
assertThat(generatedExtensionRegistryLoader, isPresent());
@@ -262,11 +261,11 @@
assertThat(
generatedExtensionRegistryLoader.uniqueMethodWithOriginalName(
"findLiteExtensionByNumber1"),
- isPresent());
+ isAbsentIf(enableMinification));
assertThat(
generatedExtensionRegistryLoader.uniqueMethodWithOriginalName(
"findLiteExtensionByNumber2"),
- notIf(isPresent(), enableMinification));
+ isAbsentIf(enableMinification));
}
// Verify that unused extensions have been removed with -allowaccessmodification.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithCastsInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithCastsInliningTest.java
index cf1b311..f5e7361 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithCastsInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithCastsInliningTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverSingleCallerInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -55,6 +56,7 @@
})
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNeverSingleCallerInlineAnnotations()
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
@@ -106,6 +108,7 @@
foo(o, o, o, o, o);
}
+ @NeverSingleCallerInline
static void foo(Object o1, Object o2, Object o3, Object o4, Object o5) {
A a1 = (A) o1;
A a2 = (A) o2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithUnboxingInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithUnboxingInliningTest.java
index 2623bc0..2dea095 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithUnboxingInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/BridgeWithUnboxingInliningTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverSingleCallerInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -55,6 +56,7 @@
})
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNeverSingleCallerInlineAnnotations()
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
@@ -114,6 +116,7 @@
foo(o1, o2, o3, o4, o5);
}
+ @NeverSingleCallerInline
static void foo(Integer o1, Integer o2, Integer o3, Integer o4, Integer o5) {
int i1 = o1;
int i2 = o2;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOfVirtualMethodOnKeptClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOfVirtualMethodOnKeptClassTest.java
index 2b4c4bb..946d711 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOfVirtualMethodOnKeptClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningOfVirtualMethodOnKeptClassTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.ir.optimize.inliner;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -50,7 +50,7 @@
private void verifyOutput(CodeInspector inspector) {
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(classSubject, isPresent());
- assertThat(classSubject.uniqueMethodWithOriginalName("foo"), not(isPresent()));
+ assertThat(classSubject.uniqueMethodWithOriginalName("foo"), isAbsent());
assertThat(classSubject.uniqueMethodWithOriginalName("bar"), isPresent());
assertThat(classSubject.uniqueMethodWithOriginalName("baz"), isPresent());
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index 95cf23e..59e609d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -50,9 +50,11 @@
.addInnerClasses(SingleTargetAfterInliningTest.class)
.addKeepMainRule(TestClass.class)
.addOptionsModification(
- options ->
- options.inlinerOptions().applyInliningToInlineePredicateForTesting =
- (appView, inlinee, inliningDepth) -> inliningDepth <= maxInliningDepth)
+ options -> {
+ options.inlinerOptions().applyInliningToInlineePredicateForTesting =
+ (appView, inlinee, inliningDepth) -> inliningDepth <= maxInliningDepth;
+ options.getSingleCallerInlinerOptions().setEnable(false);
+ })
.enableAlwaysInliningAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
index a80be3c..3e9b7ee 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
@@ -64,10 +64,14 @@
assertTrue(parameters.isExtractRules());
// PG and R8 with keep rules will keep the residual class.
assertThat(classA, isPresentAndRenamed());
- // R8 using keep rules will soft-pin the precondition method too.
+ // R8 using keep rules will soft-pin the precondition method too. The soft pinning is only
+ // applied in the first round of tree shaking, however, so R8 can still single caller inline
+ // the method after the final round of tree shaking.
assertThat(
classA.uniqueMethodWithOriginalName("foo"),
- parameters.isPG() ? isAbsent() : isPresentAndRenamed());
+ parameters.isPG() || (parameters.isCurrentR8() && parameters.isExtractRules())
+ ? isAbsent()
+ : isPresentAndRenamed());
}
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/compatissues/BackReferenceIssuesTest.java b/src/test/java/com/android/tools/r8/keepanno/compatissues/BackReferenceIssuesTest.java
index 63692c5..85464e5 100644
--- a/src/test/java/com/android/tools/r8/keepanno/compatissues/BackReferenceIssuesTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/compatissues/BackReferenceIssuesTest.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -82,7 +81,7 @@
assertThat(
inspector.clazz(A.class).uniqueMethodWithOriginalName("foo"),
// The rule is not valid and does not keep the method in R8.
- shrinker.isPG() ? isPresentAndNotRenamed() : isPresentAndRenamed()));
+ shrinker.isPG() ? isPresentAndNotRenamed() : isAbsent()));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index cb94b5d..34b7502 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -73,7 +73,8 @@
private int expectedActualStackTraceHeight() {
// In RELEASE mode a synthetic bridge is added by the vertical class merger if the method is
// targeted by the invoke-super (which is modeled by setting enableBridgeAnalysis to false).
- return mode == CompilationMode.DEBUG || enableBridgeAnalysis ? 2 : 3;
+ // Due to single caller inlining we still end up with a stack trace height of 2.
+ return 2;
}
private boolean filterSynthesizedMethodWhenLineNumberAvailable(
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfMemberRuleWithUnusedParameterTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfMemberRuleWithUnusedParameterTest.java
index 2d871fc..4490fff 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfMemberRuleWithUnusedParameterTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfMemberRuleWithUnusedParameterTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -38,6 +39,7 @@
" public static void print(" + Object.class.getTypeName() + ");",
"}",
"-keep class " + KeptByIf.class.getTypeName())
+ .enableInliningAnnotations()
.setMinApi(parameters)
.compile()
.inspect(
@@ -61,6 +63,7 @@
print(args);
}
+ @NeverInline
public static void print(Object unused) {
System.out.println("Hello, world!");
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
index 07eb940..4f0a320 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/inlining/IfRuleWithInlining.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking.ifrule.inlining;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -70,8 +71,10 @@
CodeInspector inspector = new CodeInspector(app);
ClassSubject clazzA = inspector.clazz(A.class);
assertThat(clazzA, isPresent());
- // A.a should not be inlined.
- assertThat(clazzA.method("int", "a", ImmutableList.of()), isPresent());
+ // A.a may be inlined when neverInlineMethod is false.
+ assertThat(
+ clazzA.uniqueMethodWithOriginalName("a"),
+ isAbsentIf(shrinker.isR8() && !neverInlineMethod));
assertThat(inspector.clazz(D.class), isPresent());
ProcessResult result;
if (shrinker == Shrinker.R8) {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
index eb770fe..67ba97e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.shaking.ifrule.verticalclassmerging;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoAccessModification;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.R8FullTestBuilder;
@@ -58,6 +58,7 @@
@NoHorizontalClassMerging
static class SuperTestClass {
+ @NeverInline
public static void method(A obj) {
System.out.print(obj.getClass().getName());
}
@@ -92,6 +93,7 @@
inspector
.applyIf(enableVerticalClassMerging, i -> i.assertMergedIntoSubtype(A.class))
.assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations();
}
@@ -118,11 +120,6 @@
assertThat(testClassSubject, isPresent());
if (enableVerticalClassMerging) {
- // Verify that SuperTestClass has been merged into TestClass.
- assertThat(inspector.clazz(SuperTestClass.class), isAbsent());
- assertEquals(
- "java.lang.Object", testClassSubject.getDexProgramClass().superType.toSourceString());
-
// Verify that TestClass.method has been removed.
List<FoundMethodSubject> methods =
testClassSubject.allMethods().stream()
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
index 7bb0f91..57f5d05 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
@@ -6,14 +6,15 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.AssumeMayHaveSideEffects;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoAccessModification;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.shaking.ifrule.verticalclassmerging.MergedParameterTypeTest.MergedParameterTypeWithCollisionTest.SuperTestClass;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -67,6 +68,7 @@
static class SuperTestClass {
@AssumeMayHaveSideEffects
+ @NeverInline
public static A method() {
return new B();
}
@@ -101,6 +103,7 @@
inspector
.applyIf(enableVerticalClassMerging, i -> i.assertMergedIntoSubtype(A.class))
.assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableSideEffectAnnotations();
}
@@ -128,11 +131,6 @@
assertThat(testClassSubject, isPresent());
if (enableVerticalClassMerging) {
- // Verify that SuperTestClass has been merged into TestClass.
- assertThat(inspector.clazz(SuperTestClass.class), not(isPresent()));
- assertEquals(
- "java.lang.Object", testClassSubject.getDexProgramClass().superType.toSourceString());
-
// Verify that TestClass.method has been removed.
List<FoundMethodSubject> methods =
testClassSubject.allMethods().stream()
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalOnMethodTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalOnMethodTest.java
index 6a9341c..235de24 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalOnMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByConditionalOnMethodTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.references.Reference.methodFromMethod;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -24,6 +25,7 @@
public class KeptByConditionalOnMethodTest extends TestBase {
public static class IfClass {
+ @NeverInline
public void foo(String name) throws Exception {
Class<?> clazz = Class.forName(name);
Object object = clazz.getDeclaredConstructor().newInstance();
@@ -77,6 +79,7 @@
.addProgramClasses(Main.class, IfClass.class, ThenClass.class)
.addKeepMainRule(Main.class)
.addKeepRules(ifRuleContent)
+ .enableInliningAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), Main.class, ThenClass.class.getTypeName())
.assertSuccessWithOutput(EXPECTED)
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 2f41828..f391aff 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -162,6 +162,7 @@
testBuilder ->
configureStartupOptions(
testBuilder, instrumentationCompileResult.inspector(), startupList))
+ .noInliningOfSynthetics()
.setMinApi(parameters)
.compile()
.inspectDiagnosticMessages(