Introduce another round of vertical class merging
Bug: b/315445393
Bug: b/320431939
Change-Id: Ic9bb6ade2a73fca214882750c7a5df8c0c96e6cb
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 65ff14d..b1b7241 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -105,10 +105,18 @@
return getParameter(argumentIndex - 1);
}
+ public DexType getArgumentTypeForNonStaticMethod(int argumentIndex) {
+ return getArgumentType(argumentIndex, false);
+ }
+
public int getNumberOfArguments(boolean isStatic) {
return getArity() + BooleanUtils.intValue(!isStatic);
}
+ public int getNumberOfArgumentsForNonStaticMethod() {
+ return getNumberOfArguments(false);
+ }
+
public DexType getParameter(int index) {
return proto.getParameter(index);
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index cc69b8e..0f10f5a 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -335,6 +335,9 @@
}
public void removeMethods(Set<DexEncodedMethod> methods) {
+ if (methods.isEmpty()) {
+ return;
+ }
backing.removeMethods(methods);
resetDirectMethodCaches();
resetVirtualMethodCaches();
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 9fb677b..b0f61a0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -423,6 +423,13 @@
|| invokedMethod.mustBeInlinedIntoInstanceInitializer(appView)) {
return false;
}
+ } else if (toBeReplaced.isInvokeVirtual()) {
+ // TODO(b/321171043): This is only needed as long as we change constructors in vertical class
+ // merging to non-constructors methods.
+ DexMethod invokedMethod = toBeReplaced.asInvokeVirtual().getInvokedMethod();
+ if (invokedMethod.mustBeInlinedIntoInstanceInitializer(appView)) {
+ return false;
+ }
}
if (toBeReplaced.instructionMayHaveSideEffects(
appView, context, Instruction.SideEffectAssumption.RECEIVER_NOT_NULL)) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 365708f..534c482 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -197,6 +197,10 @@
this.type = type;
}
+ public static Value createNoDebugLocal(int number, TypeElement type) {
+ return new Value(number, type, null);
+ }
+
public boolean isFixedRegisterValue() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 2dc1a3f..db19bb2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -126,7 +126,7 @@
private final StringSwitchRemover stringSwitchRemover;
private final TypeChecker typeChecker;
protected ServiceLoaderRewriter serviceLoaderRewriter;
- protected final EnumUnboxer enumUnboxer;
+ protected EnumUnboxer enumUnboxer;
protected final NumberUnboxer numberUnboxer;
protected final RemoveVerificationErrorForUnknownReturnedValues
removeVerificationErrorForUnknownReturnedValues;
@@ -299,6 +299,14 @@
this(AppView.createForD8(appInfo));
}
+ public void clearEnumUnboxer() {
+ enumUnboxer = EnumUnboxer.empty();
+ }
+
+ public void clearServiceLoaderRewriter() {
+ serviceLoaderRewriter = null;
+ }
+
public Inliner getInliner() {
return inliner;
}
@@ -515,13 +523,14 @@
feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER);
return Timing.empty();
}
- return optimize(code, feedback, methodProcessor, methodProcessingContext);
+ return optimize(code, feedback, conversionOptions, methodProcessor, methodProcessingContext);
}
// TODO(b/140766440): Convert all sub steps an implementer of CodeOptimization
Timing optimize(
IRCode code,
OptimizationFeedback feedback,
+ MethodConversionOptions methodConversionOptions,
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
ProgramMethod context = code.context();
@@ -595,6 +604,14 @@
return timing;
}
+ if (methodConversionOptions.shouldFinalizeAfterLensCodeRewriter()) {
+ deadCodeRemover.run(code, timing);
+ timing.begin("Finalize IR");
+ finalizeIR(code, feedback, BytecodeMetadataProvider.empty(), timing);
+ timing.end();
+ return timing;
+ }
+
if (options.canHaveArtStringNewInitBug()) {
timing.begin("Check for new-init issue");
TrivialPhiSimplifier.ensureDirectStringNewToInit(appView, code);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
index ceed103..7732f47 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
@@ -84,11 +84,14 @@
public abstract boolean isStringSwitchConversionEnabled();
+ public abstract boolean shouldFinalizeAfterLensCodeRewriter();
+
public static class MutableMethodConversionOptions extends MethodConversionOptions {
private Target target;
private boolean enablePeepholeOptimizations = true;
private boolean enableStringSwitchConversion;
+ private boolean finalizeAfterLensCodeRewriter;
private MutableMethodConversionOptions(Target target, boolean enableStringSwitchConversion) {
this.target = target;
@@ -109,6 +112,11 @@
return this;
}
+ public MutableMethodConversionOptions setFinalizeAfterLensCodeRewriter() {
+ finalizeAfterLensCodeRewriter = true;
+ return this;
+ }
+
@Override
public boolean isGeneratingLir() {
return target == Target.LIR;
@@ -133,6 +141,11 @@
public boolean isStringSwitchConversionEnabled() {
return enableStringSwitchConversion;
}
+
+ @Override
+ public boolean shouldFinalizeAfterLensCodeRewriter() {
+ return finalizeAfterLensCodeRewriter;
+ }
}
public static class ThrowingMethodConversionOptions extends MutableMethodConversionOptions {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index 6fed019..b75e95f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -32,6 +32,10 @@
this.wave = wave;
}
+ public static Builder builder(MethodProcessorEventConsumer eventConsumer, AppView<?> appView) {
+ return builder(eventConsumer, appView.createProcessorContext());
+ }
+
public static Builder builder(
MethodProcessorEventConsumer eventConsumer, ProcessorContext processorContext) {
return new Builder(eventConsumer, processorContext);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 5a1d5b0..b6e87c2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -59,7 +59,7 @@
while (!worklist.isEmpty()) {
BasicBlock block = worklist.removeLast();
removeDeadInstructions(worklist, code, block, affectedValues, valueIsDeadAnalysis);
- removeDeadPhis(worklist, block, valueIsDeadAnalysis);
+ removeDeadAndTrivialPhis(worklist, block, valueIsDeadAnalysis);
}
affectedValues.narrowingWithAssumeRemoval(appView, code);
} while (branchSimplifier
@@ -117,7 +117,7 @@
}
}
- private void removeDeadPhis(
+ private void removeDeadAndTrivialPhis(
Queue<BasicBlock> worklist, BasicBlock block, ValueIsDeadAnalysis valueIsDeadAnalysis) {
Iterator<Phi> phiIt = block.getPhis().iterator();
while (phiIt.hasNext()) {
@@ -128,6 +128,9 @@
operand.removePhiUser(phi);
updateWorklist(worklist, operand);
}
+ } else if (phi.isTrivialPhi()) {
+ phiIt.remove();
+ phi.removeTrivialPhi();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 1a6c122..321c554 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -48,7 +48,6 @@
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.verticalclassmerging.VerticallyMergedClasses;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
@@ -508,10 +507,6 @@
}
private boolean verifyWasInstanceInitializer() {
- VerticallyMergedClasses verticallyMergedClasses = appView.getVerticallyMergedClasses();
- assert verticallyMergedClasses != null;
- assert verticallyMergedClasses.isMergeTarget(method.getHolderType())
- || appView.horizontallyMergedClasses().isMergeTarget(method.getHolderType());
assert appView
.dexItemFactory()
.isConstructor(appView.graphLens().getOriginalMethodSignature(method.getReference()));
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 63e6bbb..5279f1d 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5025,11 +5025,15 @@
}
DexReference referencedItem = identifierLookupResult.getReference();
if (referencedItem.isDexType()) {
+ DexType referencedType = referencedItem.asDexType();
+ if (!referencedType.isClassType()
+ || appView.allMergedClasses().isMergeSource(referencedType)) {
+ return;
+ }
assert identifierLookupResult.isTypeResult();
IdentifierNameStringTypeLookupResult identifierTypeLookupResult =
identifierLookupResult.asTypeResult();
- DexProgramClass clazz =
- getProgramClassOrNullFromReflectiveAccess(referencedItem.asDexType(), method);
+ DexProgramClass clazz = getProgramClassOrNullFromReflectiveAccess(referencedType, method);
if (clazz == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
index bf712ad..f93292d 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexEncodedMethod.CompilationState;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
@@ -44,6 +45,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.OptionalBool;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import java.util.Collection;
@@ -193,7 +195,7 @@
candidate ->
availableMethodSignatures.test(candidate)
&& source.lookupVirtualMethod(candidate) == null);
- add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
+ add(virtualMethods, resultingConstructor, MethodSignatureEquivalence.get());
blockRedirectionOfSuperCalls(resultingConstructor);
} else {
DexEncodedMethod resultingDirectMethod =
@@ -264,7 +266,7 @@
// unique name, such that relevant invoke-super instructions can be rewritten to target
// this method directly.
resultingMethod = renameMethod(virtualMethod, availableMethodSignatures, Rename.ALWAYS);
- makePublicFinal(resultingMethod);
+ makePublicFinal(resultingMethod.getAccessFlags());
}
add(
@@ -664,13 +666,28 @@
} while (!availableMethodSignatures.test(newSignature));
DexEncodedMethod result =
- method.toTypeSubstitutedMethodAsInlining(newSignature, dexItemFactory);
+ method.toTypeSubstitutedMethodAsInlining(
+ newSignature,
+ dexItemFactory,
+ // Set the compilation state to satisfy the inliner in the final round of vertical class
+ // merging, which checks that the method is processed.
+ methodBuilder -> {
+ methodBuilder
+ .modifyAccessFlags(
+ accessFlags -> {
+ // TODO(b/321171043): Do not change constructors to non-constructors as this
+ // would avoid the need for force inlining (though we would then need to
+ // find a fresh constructor signature).
+ // Renamed constructors turn into ordinary non-constructor methods.
+ accessFlags.unsetConstructor();
+ makePublicFinal(accessFlags);
+ })
+ .setCompilationState(CompilationState.PROCESSED_INLINING_CANDIDATE_ANY)
+ .setIsLibraryMethodOverride(OptionalBool.FALSE);
+ });
+ // TODO(b/321171043): Do not mark force inlining.
result.getMutableOptimizationInfo().markForceInline();
lensBuilder.recordMove(method, result);
- // Renamed constructors turn into ordinary private functions. They can be private, as
- // they are only references from their direct subclass, which they were merged into.
- result.getAccessFlags().unsetConstructor();
- makePrivate(result);
return result;
}
@@ -738,16 +755,7 @@
return field.toTypeSubstitutedField(appView, newSignature);
}
- private static void makePrivate(DexEncodedMethod method) {
- MethodAccessFlags accessFlags = method.getAccessFlags();
- assert !accessFlags.isAbstract();
- accessFlags.unsetPublic();
- accessFlags.unsetProtected();
- accessFlags.setPrivate();
- }
-
- private static void makePublicFinal(DexEncodedMethod method) {
- MethodAccessFlags accessFlags = method.getAccessFlags();
+ private static void makePublicFinal(MethodAccessFlags accessFlags) {
assert !accessFlags.isAbstract();
accessFlags.unsetPrivate();
accessFlags.unsetProtected();
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java b/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
index 60694ae..f13c3c6 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -11,18 +14,29 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
+import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.lightir.LirEncodingStrategy;
+import com.android.tools.r8.lightir.LirStrategy;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.RetracerForCodePrinting;
+import java.util.ArrayList;
+import java.util.List;
/**
- * A short-lived piece of code that will be converted into {@link CfCode} using the method {@link
- * IncompleteVerticalClassMergerBridgeCode#toCfCode(DexItemFactory, VerticalClassMergerGraphLens)}.
+ * A short-lived piece of code that will be converted into {@link CfCode} using {@link
+ * #toCfCode(DexItemFactory)} or {@link LirCode} using {@link #toLirCode(AppView)}.
*/
public class IncompleteVerticalClassMergerBridgeCode extends Code {
@@ -86,6 +100,48 @@
.build();
}
+ public LirCode<?> toLirCode(AppView<AppInfoWithLiveness> appView) {
+ boolean isD8R8Synthesized = true;
+ LirEncodingStrategy<Value, Integer> strategy =
+ LirStrategy.getDefaultStrategy().getEncodingStrategy();
+ LirBuilder<Value, Integer> lirBuilder =
+ LirCode.builder(method, isD8R8Synthesized, strategy, appView.options())
+ .setMetadata(IRMetadata.unknown());
+
+ // Add all arguments.
+ List<Value> argumentValues = new ArrayList<>();
+ int instructionIndex = 0;
+ for (; instructionIndex < method.getNumberOfArgumentsForNonStaticMethod(); instructionIndex++) {
+ DexType argumentType = method.getArgumentTypeForNonStaticMethod(instructionIndex);
+ TypeElement argumentTypeElement =
+ argumentType.toTypeElement(
+ appView, instructionIndex == 0 ? definitelyNotNull() : maybeNull());
+ Value argumentValue = Value.createNoDebugLocal(instructionIndex, argumentTypeElement);
+ argumentValues.add(argumentValue);
+ strategy.defineValue(argumentValue, argumentValue.getNumber());
+ lirBuilder.addArgument(instructionIndex, argumentType.isBooleanType());
+ }
+
+ if (type.isStatic()) {
+ lirBuilder.addInvokeStatic(invocationTarget, argumentValues, isInterface);
+ } else if (isInterface) {
+ lirBuilder.addInvokeInterface(invocationTarget, argumentValues);
+ } else {
+ lirBuilder.addInvokeVirtual(invocationTarget, argumentValues);
+ }
+
+ if (method.getReturnType().isVoidType()) {
+ lirBuilder.addReturnVoid();
+ } else {
+ Value returnValue =
+ Value.createNoDebugLocal(instructionIndex, method.getReturnType().toTypeElement(appView));
+ strategy.defineValue(returnValue, returnValue.getNumber());
+ lirBuilder.addReturn(returnValue);
+ }
+
+ return lirBuilder.build();
+ }
+
// Implement Code.
@Override
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java
index d365ddd..53ee29c 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelper.java
@@ -49,7 +49,8 @@
if (previousLens != null
&& previousLens != codeLens
&& previousLens.isVerticalClassMergerLens()) {
- return new InterfaceTypeToClassTypeLensCodeRewriterHelperImpl(appView, code);
+ return new InterfaceTypeToClassTypeLensCodeRewriterHelperImpl(
+ appView, code, graphLens, codeLens);
}
return new EmptyInterfaceTypeToClassTypeLensCodeRewriterHelper();
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
index 3087061..56010ad 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InterfaceTypeToClassTypeLensCodeRewriterHelperImpl.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -38,13 +39,20 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final IRCode code;
+ private final GraphLens graphLens;
+ private final GraphLens codeLens;
private final Map<Instruction, Deque<WorklistItem>> worklist = new IdentityHashMap<>();
public InterfaceTypeToClassTypeLensCodeRewriterHelperImpl(
- AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ IRCode code,
+ GraphLens graphLens,
+ GraphLens codeLens) {
this.appView = appView;
this.code = code;
+ this.graphLens = graphLens;
+ this.codeLens = codeLens;
}
@Override
@@ -91,7 +99,7 @@
InstructionListIterator instructionIterator) {
assert !rewrittenReturn.isReturnVoid();
DexMethod originalMethodSignature =
- appView.graphLens().getOriginalMethodSignature(code.context().getReference());
+ graphLens.getOriginalMethodSignature(code.context().getReference(), codeLens);
DexType originalReturnType = originalMethodSignature.getReturnType();
DexType rewrittenReturnType = code.context().getReturnType();
if (needsCastForOperand(
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java
index 8c76978..9dc57d5 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java
@@ -1,5 +1,6 @@
package com.android.tools.r8.verticalclassmerging;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -10,14 +11,30 @@
public class InvokeSpecialToDefaultLibraryMethodUseRegistry
extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> {
+ private final ClassMergerMode mode;
+
public InvokeSpecialToDefaultLibraryMethodUseRegistry(
- AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
+ AppView<AppInfoWithLiveness> appView, ProgramMethod context, ClassMergerMode mode) {
super(appView, context, false);
+ this.mode = mode;
assert context.getHolder().isInterface();
}
@Override
public void registerInvokeSpecial(DexMethod method) {
+ assert mode.isInitial();
+ handleInvokeSpecial(method);
+ }
+
+ // Handle invoke-super callbacks in the final round of class merging where we have LIR instead of
+ // CF.
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ assert mode.isFinal();
+ handleInvokeSpecial(method);
+ }
+
+ private void handleInvokeSpecial(DexMethod method) {
ProgramMethod context = getContext();
if (!method.getHolderType().isIdenticalTo(context.getHolderType())) {
return;
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index d791b84..68f4dbd 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -12,10 +12,16 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
+import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph;
import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
@@ -26,6 +32,7 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Timing.TimingMerger;
+import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Collection;
@@ -113,11 +120,29 @@
ProfileCollectionAdditions.create(appView);
VerticalClassMergerGraphLens lens =
runFixup(profileCollectionAdditions, verticalClassMergerResult, executorService, timing);
- updateKeepInfoForMergedClasses(verticalClassMergerResult);
assert verifyGraphLens(lens, verticalClassMergerResult);
+
+ // Update keep info and art profiles.
+ updateKeepInfoForMergedClasses(verticalClassMergerResult);
updateArtProfiles(profileCollectionAdditions, lens, verticalClassMergerResult);
+
+ // Remove merged classes and rewrite AppView with the new lens.
appView.rewriteWithLens(lens, executorService, timing);
+
+ // The code must be rewritten before we remove the merged classes from the app. Otherwise we
+ // can't build IR.
+ rewriteCodeWithLens(executorService);
+
+ // Remove force inlined constructors.
+ removeForceInlinedConstructors(executorService);
+ removeMergedClasses(verticalClassMergerResult.getVerticallyMergedClasses());
+
+ // Convert the (incomplete) synthesized bridges to CF or LIR.
finalizeSynthesizedBridges(verticalClassMergerResult.getSynthesizedBridges(), lens);
+
+ // Finally update the code lens to signal that the code is fully up to date.
+ markRewrittenWithLens(executorService);
+
appView.notifyOptimizationFinishedForTesting();
}
@@ -148,7 +173,7 @@
connectedComponent -> {
Timing threadTiming = Timing.create("Compute classes to merge in component", options);
ConnectedComponentVerticalClassMerger connectedComponentMerger =
- new VerticalClassMergerPolicyExecutor(appView, immediateSubtypingInfo)
+ new VerticalClassMergerPolicyExecutor(appView, immediateSubtypingInfo, mode)
.run(connectedComponent, executorService, threadTiming);
if (!connectedComponentMerger.isEmpty()) {
synchronized (connectedComponentMergers) {
@@ -217,19 +242,61 @@
return lens;
}
+ // TODO(b/320432664): For code objects where the rewriting is an alpha renaming we can rewrite the
+ // LIR directly without building IR.
+ private void rewriteCodeWithLens(ExecutorService executorService) throws ExecutionException {
+ if (mode.isInitial()) {
+ return;
+ }
+
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
+ OneTimeMethodProcessor.Builder methodProcessorBuilder =
+ OneTimeMethodProcessor.builder(eventConsumer, appView);
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ clazz.forEachProgramMethodMatching(
+ method ->
+ method.hasCode()
+ && !(method.getCode() instanceof IncompleteVerticalClassMergerBridgeCode),
+ methodProcessorBuilder::add);
+ }
+
+ IRConverter converter = new IRConverter(appView);
+ converter.clearEnumUnboxer();
+ converter.clearServiceLoaderRewriter();
+ OneTimeMethodProcessor methodProcessor = methodProcessorBuilder.build();
+ methodProcessor.forEachWaveWithExtension(
+ (method, methodProcessingContext) ->
+ converter.processDesugaredMethod(
+ method,
+ OptimizationFeedbackIgnore.getInstance(),
+ methodProcessor,
+ methodProcessingContext,
+ // TODO(b/321171043): Set setFinalizeAfterLensCodeRewriter() on the
+ // MethodConversionOptions to improve build speed (no need to run all
+ // optimizations!). A prerequisite for this is that we remove all uses of force
+ // inlining in the vertical class merger.
+ MethodConversionOptions.forLirPhase(appView)),
+ options.getThreadingModule(),
+ executorService);
+
+ // Clear type elements created during IR processing.
+ dexItemFactory.clearTypeElementsCache();
+ }
+
private void updateArtProfiles(
ProfileCollectionAdditions profileCollectionAdditions,
VerticalClassMergerGraphLens verticalClassMergerLens,
VerticalClassMergerResult verticalClassMergerResult) {
// Include bridges in art profiles.
- if (!profileCollectionAdditions.isNop()) {
- List<IncompleteVerticalClassMergerBridgeCode> synthesizedBridges =
- verticalClassMergerResult.getSynthesizedBridges();
- for (IncompleteVerticalClassMergerBridgeCode synthesizedBridge : synthesizedBridges) {
- profileCollectionAdditions.applyIfContextIsInProfile(
- verticalClassMergerLens.getPreviousMethodSignature(synthesizedBridge.getMethod()),
- additionsBuilder -> additionsBuilder.addRule(synthesizedBridge.getMethod()));
- }
+ if (profileCollectionAdditions.isNop()) {
+ return;
+ }
+ List<IncompleteVerticalClassMergerBridgeCode> synthesizedBridges =
+ verticalClassMergerResult.getSynthesizedBridges();
+ for (IncompleteVerticalClassMergerBridgeCode synthesizedBridge : synthesizedBridges) {
+ profileCollectionAdditions.applyIfContextIsInProfile(
+ verticalClassMergerLens.getPreviousMethodSignature(synthesizedBridge.getMethod()),
+ additionsBuilder -> additionsBuilder.addRule(synthesizedBridge.getMethod()));
}
profileCollectionAdditions.commit(appView);
}
@@ -247,6 +314,55 @@
});
}
+ // TODO(b/321171043): No need to forcefully remove these force inlining constructors if we don't
+ // use force inlining (though it may be desirable to apply inlining also in the final round of
+ // vertical class merging if a merged constructor has a single caller inside the target class).
+ private void removeForceInlinedConstructors(ExecutorService executorService)
+ throws ExecutionException {
+ if (mode.isInitial()) {
+ return;
+ }
+ PrunedItems.Builder prunedItemsBuilder =
+ PrunedItems.concurrentBuilder().setPrunedApp(appView.app());
+ ThreadUtils.<DexProgramClass, Exception>processItems(
+ consumer -> {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (!clazz.isInterface()) {
+ consumer.accept(clazz);
+ }
+ }
+ },
+ clazz -> {
+ Set<DexEncodedMethod> methodsToRemove = Sets.newIdentityHashSet();
+ clazz.forEachProgramMethodMatching(
+ method -> method.willBeInlinedIntoInstanceInitializer(dexItemFactory),
+ method -> methodsToRemove.add(method.getDefinition()));
+ clazz.getMethodCollection().removeMethods(methodsToRemove);
+ methodsToRemove.forEach(
+ removedMethod -> prunedItemsBuilder.addRemovedMethod(removedMethod.getReference()));
+ },
+ options.getThreadingModule(),
+ executorService);
+ PrunedItems prunedItems = prunedItemsBuilder.build();
+ appView.pruneItems(prunedItems, executorService, Timing.empty());
+ appView.appInfo().getMethodAccessInfoCollection().withoutPrunedItems(prunedItems);
+ }
+
+ private void removeMergedClasses(VerticallyMergedClasses verticallyMergedClasses) {
+ if (mode.isInitial()) {
+ return;
+ }
+
+ DirectMappedDexApplication newApplication =
+ appView
+ .app()
+ .asDirect()
+ .builder()
+ .removeProgramClasses(clazz -> verticallyMergedClasses.isMergeSource(clazz.getType()))
+ .build();
+ appView.setAppInfo(appView.appInfo().rebuildWithLiveness(newApplication));
+ }
+
private void finalizeSynthesizedBridges(
List<IncompleteVerticalClassMergerBridgeCode> bridges, VerticalClassMergerGraphLens lens) {
KeepInfoCollection keepInfo = appView.getKeepInfo();
@@ -258,7 +374,11 @@
assert target != null;
// Finalize code.
- bridge.setCode(code.toCfCode(dexItemFactory, lens), appView);
+ assert mode.isInitial() == appView.testing().isPreLirPhase();
+ assert mode.isFinal() == appView.testing().isSupportedLirPhase();
+ bridge.setCode(
+ mode.isInitial() ? code.toCfCode(dexItemFactory, lens) : code.toLirCode(appView),
+ appView);
// Copy keep info to newly synthesized methods.
keepInfo.mutate(
@@ -267,6 +387,13 @@
}
}
+ private void markRewrittenWithLens(ExecutorService executorService) throws ExecutionException {
+ if (mode.isInitial()) {
+ return;
+ }
+ appView.clearCodeRewritings(executorService);
+ }
+
private boolean verifyGraphLens(
VerticalClassMergerGraphLens graphLens, VerticalClassMergerResult verticalClassMergerResult) {
// Note that the method assertReferencesNotModified() relies on getRenamedFieldSignature() and
@@ -297,12 +424,13 @@
// pinned, because this rewriting does not affect A.method() in any way.
assert graphLens.assertPinnedNotModified(appView);
+ GraphLens previousLens = graphLens.getPrevious();
VerticallyMergedClasses mergedClasses = verticalClassMergerResult.getVerticallyMergedClasses();
for (DexProgramClass clazz : appView.appInfo().classes()) {
for (DexEncodedMethod encodedMethod : clazz.methods()) {
DexMethod method = encodedMethod.getReference();
- DexMethod originalMethod = graphLens.getOriginalMethodSignature(method);
- DexMethod renamedMethod = graphLens.getRenamedMethodSignature(originalMethod);
+ DexMethod originalMethod = graphLens.getOriginalMethodSignature(method, previousLens);
+ DexMethod renamedMethod = graphLens.getRenamedMethodSignature(originalMethod, previousLens);
// Must be able to map back and forth.
if (encodedMethod.hasCode()
@@ -315,7 +443,7 @@
DexMethod implementationMethod =
((IncompleteVerticalClassMergerBridgeCode) encodedMethod.getCode()).getTarget();
DexMethod originalImplementationMethod =
- graphLens.getOriginalMethodSignature(implementationMethod);
+ graphLens.getOriginalMethodSignature(implementationMethod, previousLens);
assert originalMethod.isIdenticalTo(originalImplementationMethod);
assert implementationMethod.isIdenticalTo(renamedMethod);
} else {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
index 994795d..20bb15f 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
@@ -283,6 +283,12 @@
@Override
protected InvokeType mapInvocationType(
DexMethod newMethod, DexMethod previousMethod, InvokeType type) {
+ // TODO(b/321171043): Remove the need to map constructor calls to invoke-virtual.
+ if (dexItemFactory().isConstructor(previousMethod)
+ && !dexItemFactory().isConstructor(newMethod)) {
+ assert newMethod.getName().startsWith(dexItemFactory().temporaryConstructorMethodPrefix);
+ return InvokeType.VIRTUAL;
+ }
if (isStaticized(newMethod)) {
return InvokeType.STATIC;
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
index b7f435c..0accf81 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
@@ -21,7 +21,14 @@
}
public boolean isEnabled(ClassMergerMode mode) {
- return enabled && options.isOptimizing() && options.isShrinking() && mode.isInitial();
+ if (!enabled || !options.isOptimizing() || !options.isShrinking()) {
+ return false;
+ }
+ // TODO(b/320431939): Enable final round of vertical class merging for desugared library.
+ if (mode.isFinal() && !options.synthesizedClassPrefix.isEmpty()) {
+ return false;
+ }
+ return true;
}
public void setEnabled(boolean enabled) {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
index 6b6f314..2691d18 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.Policy;
import com.android.tools.r8.classmerging.PolicyExecutor;
import com.android.tools.r8.graph.AppView;
@@ -17,6 +18,7 @@
import com.android.tools.r8.verticalclassmerging.policies.NoDirectlyInstantiatedClassesPolicy;
import com.android.tools.r8.verticalclassmerging.policies.NoEnclosingMethodAttributesPolicy;
import com.android.tools.r8.verticalclassmerging.policies.NoFieldResolutionChangesPolicy;
+import com.android.tools.r8.verticalclassmerging.policies.NoFinalSourceInstanceFieldsWithConstructorInliningPolicy;
import com.android.tools.r8.verticalclassmerging.policies.NoIllegalAccessesPolicy;
import com.android.tools.r8.verticalclassmerging.policies.NoInnerClassAttributesPolicy;
import com.android.tools.r8.verticalclassmerging.policies.NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy;
@@ -49,11 +51,15 @@
private final AppView<AppInfoWithLiveness> appView;
private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
+ private final ClassMergerMode mode;
VerticalClassMergerPolicyExecutor(
- AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ AppView<AppInfoWithLiveness> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ ClassMergerMode mode) {
this.appView = appView;
this.immediateSubtypingInfo = immediateSubtypingInfo;
+ this.mode = mode;
}
ConnectedComponentVerticalClassMerger run(
@@ -64,6 +70,7 @@
Collection<Policy> policies =
List.of(
new NoDirectlyInstantiatedClassesPolicy(appView),
+ new NoFinalSourceInstanceFieldsWithConstructorInliningPolicy(appView, mode),
new NoInterfacesWithUnknownSubtypesPolicy(appView),
new NoKeptClassesPolicy(appView),
new SameFeatureSplitPolicy(appView),
@@ -82,7 +89,7 @@
new NoMethodResolutionChangesPolicy(appView),
new NoIllegalAccessesPolicy(appView),
new NoClassInitializationChangesPolicy(appView),
- new NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy(appView),
+ new NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy(appView, mode),
new NoInvokeSuperNoSuchMethodErrorsPolicy(appView),
new SuccessfulVirtualMethodResolutionInTargetPolicy(appView),
new NoAbstractMethodsOnAbstractClassesPolicy(appView),
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoClassInitializationChangesPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoClassInitializationChangesPolicy.java
index d8b6933..8d137ee 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoClassInitializationChangesPolicy.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoClassInitializationChangesPolicy.java
@@ -3,12 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging.policies;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.verticalclassmerging.VerticalMergeGroup;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
-public class NoClassInitializationChangesPolicy extends VerticalClassMergerPolicy {
+public class NoClassInitializationChangesPolicy
+ extends VerticalClassMergerPolicyWithPreprocessing<Map<DexProgramClass, Set<DexProgramClass>>> {
private final AppView<AppInfoWithLiveness> appView;
@@ -17,21 +25,47 @@
}
@Override
- public boolean canMerge(VerticalMergeGroup group) {
+ public boolean canMerge(
+ VerticalMergeGroup group,
+ Map<DexProgramClass, Set<DexProgramClass>> sourcesWithClassInitializers) {
// For interface types, this is more complicated, see:
// https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-5.html#jvms-5.5
// We basically can't move the clinit, since it is not called when implementing classes have
// their clinit called - except when the interface has a default method.
DexProgramClass sourceClass = group.getSource();
DexProgramClass targetClass = group.getTarget();
- return (!sourceClass.hasClassInitializer() || !targetClass.hasClassInitializer())
- && !targetClass.classInitializationMayHaveSideEffects(
+ // TODO(b/320433836): Add support for concatenating <clinit>s.
+ if (sourceClass.hasClassInitializer()) {
+ if (targetClass.hasClassInitializer()
+ || sourcesWithClassInitializers.get(targetClass).size() > 1) {
+ boolean removed = sourcesWithClassInitializers.get(targetClass).remove(sourceClass);
+ assert removed;
+ return false;
+ }
+ }
+ assert !sourceClass.hasClassInitializer() || !targetClass.hasClassInitializer();
+ return !targetClass.classInitializationMayHaveSideEffects(
appView, type -> type.isIdenticalTo(sourceClass.getType()))
&& (!sourceClass.isInterface()
|| !sourceClass.classInitializationMayHaveSideEffects(appView));
}
@Override
+ public Map<DexProgramClass, Set<DexProgramClass>> preprocess(
+ Collection<VerticalMergeGroup> groups) {
+ Map<DexProgramClass, Set<DexProgramClass>> sourcesWithClassInitializers =
+ new IdentityHashMap<>();
+ for (VerticalMergeGroup group : groups) {
+ if (group.getSource().hasClassInitializer()) {
+ sourcesWithClassInitializers
+ .computeIfAbsent(group.getTarget(), ignoreKey(Sets::newIdentityHashSet))
+ .add(group.getSource());
+ }
+ }
+ return sourcesWithClassInitializers;
+ }
+
+ @Override
public String getName() {
return "NoClassInitializationChangesPolicy";
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoFinalSourceInstanceFieldsWithConstructorInliningPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoFinalSourceInstanceFieldsWithConstructorInliningPolicy.java
new file mode 100644
index 0000000..14b9ece
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoFinalSourceInstanceFieldsWithConstructorInliningPolicy.java
@@ -0,0 +1,42 @@
+// 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.verticalclassmerging.policies;
+
+import com.android.tools.r8.classmerging.ClassMergerMode;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.verticalclassmerging.VerticalMergeGroup;
+import com.google.common.collect.Iterables;
+
+public class NoFinalSourceInstanceFieldsWithConstructorInliningPolicy
+ extends VerticalClassMergerPolicy {
+
+ private final ClassMergerMode mode;
+ private final InternalOptions options;
+
+ public NoFinalSourceInstanceFieldsWithConstructorInliningPolicy(
+ AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
+ this.mode = mode;
+ this.options = appView.options();
+ }
+
+ @Override
+ public boolean canMerge(VerticalMergeGroup group) {
+ return group.getSource().isInterface()
+ || !options.canInitNewInstanceUsingSuperclassConstructor()
+ || Iterables.isEmpty(group.getSource().instanceFields(DexEncodedField::isFinal));
+ }
+
+ @Override
+ public boolean shouldSkipPolicy() {
+ return mode.isInitial();
+ }
+
+ @Override
+ public String getName() {
+ return "NoFinalSourceInstanceFieldsWithConstructorInliningPolicy";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java
index 13b1342..8f36d05 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -14,10 +15,12 @@
extends VerticalClassMergerPolicy {
private final AppView<AppInfoWithLiveness> appView;
+ private final ClassMergerMode mode;
public NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy(
- AppView<AppInfoWithLiveness> appView) {
+ AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
this.appView = appView;
+ this.mode = mode;
}
@Override
@@ -34,7 +37,7 @@
method -> {
boolean foundInvokeSpecialToDefaultLibraryMethod =
method.registerCodeReferencesWithResult(
- new InvokeSpecialToDefaultLibraryMethodUseRegistry(appView, method));
+ new InvokeSpecialToDefaultLibraryMethodUseRegistry(appView, method, mode));
return TraversalContinuation.breakIf(foundInvokeSpecialToDefaultLibraryMethod);
});
return result.shouldContinue();
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index b64e03a..f803d78 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -669,6 +669,10 @@
return self();
}
+ public T addNoVerticalClassMergingRule(String clazz) {
+ return addInternalKeepRules("-noverticalclassmerging class " + clazz);
+ }
+
public T enableMemberValuePropagationAnnotations() {
return enableMemberValuePropagationAnnotations(true);
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
index a73b895..52fd6e9 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
@@ -28,8 +28,7 @@
@Parameters(name = "{1}, minification: {0}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(),
- getTestParameters().withDexRuntimes().withAllRuntimesAndApiLevels().build());
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
public KeepNonVisibilityBridgeMethodsTest(boolean minification, TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
index 81396ab..313937a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -35,7 +35,7 @@
.assertSuccessWithOutputLines("bar", "foo y", "foo z")
.inspect(
codeInspector -> {
- assertThat(codeInspector.clazz(I.class), isPresent());
+ assertThat(codeInspector.clazz(I.class), isAbsent());
assertThat(codeInspector.clazz(X.class), isPresent());
assertThat(codeInspector.clazz(Y.class), isAbsent());
assertThat(codeInspector.clazz(Z.class), isAbsent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
index 5858809..f356c0b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
@@ -31,7 +31,7 @@
.assertSuccessWithOutputLines("a", "foo a", "b", "foo")
.inspect(
codeInspector -> {
- assertThat(codeInspector.clazz(Parent.class), isPresent());
+ assertThat(codeInspector.clazz(Parent.class), isAbsent());
assertThat(codeInspector.clazz(A.class), isPresent());
assertThat(codeInspector.clazz(B.class), isAbsent());
assertThat(codeInspector.clazz(C.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
index 3cd0ac6..1aade71 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
@@ -35,7 +35,7 @@
.assertSuccess()
.inspect(
codeInspector -> {
- assertThat(codeInspector.clazz(Parent.class), isPresent());
+ assertThat(codeInspector.clazz(Parent.class), isAbsent());
ClassSubject aClassSubject = codeInspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
index 293646e..1d4b66a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
@@ -4,9 +4,10 @@
package com.android.tools.r8.classmerging.horizontal;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+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.hamcrest.core.IsNot.not;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -32,9 +33,11 @@
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(C.class), isPresent());
- assertThat(codeInspector.clazz(B.class), not(isPresent()));
- assertThat(codeInspector.clazz(D.class), not(isPresent()));
+ assertThat(
+ codeInspector.clazz(C.class),
+ isAbsentIf(parameters.canHaveNonReboundConstructorInvoke()));
+ assertThat(codeInspector.clazz(B.class), isAbsent());
+ assertThat(codeInspector.clazz(D.class), isAbsent());
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
index 50d3724..abff6e4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -6,7 +6,7 @@
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.onlyIf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -47,11 +47,13 @@
codeInspector -> {
assertThat(codeInspector.clazz(I.class), isPresent());
assertThat(codeInspector.clazz(J.class), isAbsent());
- assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(A.class),
+ isPresentIf(parameters.canUseDefaultAndStaticInterfaceMethods()));
assertThat(codeInspector.clazz(B1.class), isPresent());
assertThat(
codeInspector.clazz(B2.class),
- onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent()));
+ isPresentIf(parameters.canUseDefaultAndStaticInterfaceMethods()));
assertThat(codeInspector.clazz(C1.class), isPresent());
assertThat(codeInspector.clazz(C2.class), isPresent());
});
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerReflectiveNameTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerReflectiveNameTest.java
index 7bbb17e..4a2eab5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerReflectiveNameTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerReflectiveNameTest.java
@@ -47,6 +47,8 @@
testForR8(parameters.getBackend())
.addProgramClasses(Main.class, A.class, B.class)
.addKeepMainRule(Main.class)
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(A.class))
.setMinApi(parameters)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
@@ -73,7 +75,7 @@
public static class Main {
- private static final String className =
+ private static String className =
"com.android.tools.r8.classmerging.vertical.VerticalClassMergerReflectiveNameTest$A";
static {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 0e87692..edda012 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -388,7 +388,6 @@
Set<String> preservedClassNames =
ImmutableSet.of(
"classmerging.NestedDefaultInterfaceMethodsTest",
- "classmerging.NestedDefaultInterfaceMethodsTest$A",
"classmerging.NestedDefaultInterfaceMethodsTest$C");
runTest(
testForR8(parameters.getBackend())
@@ -411,10 +410,14 @@
JAVA8_CF_DIR.resolve("NeverInline.class")
};
Set<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.NestedDefaultInterfaceMethodsTest",
- "classmerging.NestedDefaultInterfaceMethodsTest$A",
- "classmerging.NestedDefaultInterfaceMethodsTest$C");
+ parameters.isCfRuntime()
+ ? ImmutableSet.of(
+ "classmerging.NestedDefaultInterfaceMethodsTest",
+ "classmerging.NestedDefaultInterfaceMethodsTest$A",
+ "classmerging.NestedDefaultInterfaceMethodsTest$C")
+ : ImmutableSet.of(
+ "classmerging.NestedDefaultInterfaceMethodsTest",
+ "classmerging.NestedDefaultInterfaceMethodsTest$C");
runTestOnInput(
testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(JAVA8_EXAMPLE_KEEP))
@@ -850,7 +853,6 @@
ImmutableSet.of(
"classmerging.SuperCallToMergedClassIsRewrittenTest",
"classmerging.A",
- "classmerging.B",
"classmerging.D",
"classmerging.F");
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
index 8a15c91..5b9b506 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
@@ -38,7 +38,8 @@
public YouTubeCompilationTestBase(int majorVersion, int minorVersion, AndroidApiLevel apiLevel) {
this.base =
- "third_party/youtube/youtube.android_"
+ ToolHelper.THIRD_PARTY_DIR
+ + "youtube/youtube.android_"
+ majorVersion
+ "."
+ String.format("%02d", minorVersion)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java
index 83c7f4f..b0b7907 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java
@@ -110,6 +110,7 @@
interface J extends I {}
@NeverClassInline
+ @NoVerticalClassMerging
static class A implements J {
@Override
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
index d2bf214..867f9c9 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -154,6 +154,11 @@
public boolean isStringSwitchConversionEnabled() {
return false;
}
+
+ @Override
+ public boolean shouldFinalizeAfterLensCodeRewriter() {
+ return false;
+ }
}));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index ba27997..30be52f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -143,7 +143,8 @@
// TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
.addKeepRules("-neverinline class * { void test*State*(...); }")
.addNoHorizontalClassMergingRule(
- "class_inliner_lambda_j_style.SamIface$Consumer"))
+ "class_inliner_lambda_j_style.SamIface$Consumer")
+ .addNoVerticalClassMergingRule("class_inliner_lambda_j_style.SamIface"))
.inspect(
inspector -> {
if (testParameters.isCfRuntime() && !hasKotlinCGeneratedLambdaClasses) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
index 7e3ba53..205d483 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -92,7 +92,10 @@
MethodSubject virtualMethodSubject =
mergeTarget.uniqueMethodThatMatches(
- method -> method.isVirtual() && !method.isSynthetic());
+ method ->
+ method.isVirtual()
+ && !method.isSynthetic()
+ && method.getOriginalName(false).equals("invoke"));
assertThat(virtualMethodSubject, isPresent());
int found = 0;
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 0704294..43808b8 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -8,8 +8,9 @@
import static com.android.tools.r8.ir.desugar.records.RecordDesugaring.GET_FIELDS_AS_OBJECTS_METHOD_NAME;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.ifThen;
+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 com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
@@ -193,23 +194,24 @@
assertThat(mainMethodSubject, isPresent());
ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
- assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
- if (!canUseRecords) {
+ assertThat(
+ recordTagClassSubject, isAbsentIf(canHaveNonReboundConstructorInvoke || canUseRecords));
+ if (recordTagClassSubject.isPresent()) {
assertEquals(
canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
}
MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
- assertThat(
- recordTagInstanceInitializerSubject,
- notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseRecords));
+ assertThat(recordTagInstanceInitializerSubject, isPresentIf(recordTagClassSubject.isPresent()));
ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
assertThat(personRecordClassSubject, isPresent());
assertEquals(
- canUseRecords
- ? inspector.getTypeSubject(RECORD_REFERENCE.getTypeName())
- : recordTagClassSubject.asTypeSubject(),
+ canHaveNonReboundConstructorInvoke
+ ? inspector.getTypeSubject(Object.class.getTypeName())
+ : canUseRecords
+ ? inspector.getTypeSubject(RECORD_REFERENCE.getTypeName())
+ : recordTagClassSubject.asTypeSubject(),
personRecordClassSubject.getSuperType());
assertEquals(canUseRecords ? 6 : 10, personRecordClassSubject.allMethods().size());
@@ -226,7 +228,7 @@
SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter(
Reference.field(PERSON_REFERENCE, "name", STRING_REFERENCE))
.getMethodName());
- assertThat(nameNestAccessorMethodSubject, notIf(isPresent(), canUseNestBasedAccesses));
+ assertThat(nameNestAccessorMethodSubject, isAbsentIf(canUseNestBasedAccesses));
// Age getters.
MethodSubject ageMethodSubject = personRecordClassSubject.uniqueMethodWithOriginalName("age");
@@ -237,16 +239,16 @@
SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter(
Reference.field(PERSON_REFERENCE, "age", Reference.INT))
.getMethodName());
- assertThat(ageNestAccessorMethodSubject, notIf(isPresent(), canUseNestBasedAccesses));
+ assertThat(ageNestAccessorMethodSubject, isAbsentIf(canUseNestBasedAccesses));
// boolean equals(Object)
MethodSubject getFieldsAsObjectsMethodSubject =
personRecordClassSubject.uniqueMethodWithOriginalName(GET_FIELDS_AS_OBJECTS_METHOD_NAME);
- assertThat(getFieldsAsObjectsMethodSubject, notIf(isPresent(), canUseRecords));
+ assertThat(getFieldsAsObjectsMethodSubject, isAbsentIf(canUseRecords));
MethodSubject equalsHelperMethodSubject =
personRecordClassSubject.uniqueMethodWithOriginalName(EQUALS_RECORD_METHOD_NAME);
- assertThat(equalsHelperMethodSubject, notIf(isPresent(), canUseRecords));
+ assertThat(equalsHelperMethodSubject, isAbsentIf(canUseRecords));
MethodSubject equalsMethodSubject =
personRecordClassSubject.uniqueMethodWithOriginalName("equals");
@@ -257,10 +259,10 @@
// int hashCode()
ClassSubject hashCodeHelperClassSubject =
inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 1));
- assertThat(hashCodeHelperClassSubject, notIf(isPresent(), canUseRecords));
+ assertThat(hashCodeHelperClassSubject, isAbsentIf(canUseRecords));
MethodSubject hashCodeHelperMethodSubject = hashCodeHelperClassSubject.uniqueMethod();
- assertThat(hashCodeHelperMethodSubject, notIf(isPresent(), canUseRecords));
+ assertThat(hashCodeHelperMethodSubject, isAbsentIf(canUseRecords));
MethodSubject hashCodeMethodSubject =
personRecordClassSubject.uniqueMethodWithOriginalName("hashCode");
@@ -274,10 +276,10 @@
// String toString()
ClassSubject toStringHelperClassSubject =
inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 0));
- assertThat(toStringHelperClassSubject, notIf(isPresent(), canUseRecords));
+ assertThat(toStringHelperClassSubject, isAbsentIf(canUseRecords));
MethodSubject toStringHelperMethodSubject = toStringHelperClassSubject.uniqueMethod();
- assertThat(toStringHelperMethodSubject, notIf(isPresent(), canUseRecords));
+ assertThat(toStringHelperMethodSubject, isAbsentIf(canUseRecords));
MethodSubject toStringMethodSubject =
personRecordClassSubject.uniqueMethodWithOriginalName("toString");
@@ -306,10 +308,7 @@
.applyIf(
!canUseRecords,
i ->
- i.assertContainsClassRules(
- recordTagClassSubject,
- hashCodeHelperClassSubject,
- toStringHelperClassSubject)
+ i.assertContainsClassRules(hashCodeHelperClassSubject, toStringHelperClassSubject)
.assertContainsMethodRules(
equalsHelperMethodSubject,
getFieldsAsObjectsMethodSubject,
@@ -317,7 +316,9 @@
toStringHelperMethodSubject)
.applyIf(
!canHaveNonReboundConstructorInvoke,
- j -> j.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
+ j ->
+ j.assertContainsClassRules(recordTagClassSubject)
+ .assertContainsMethodRule(recordTagInstanceInitializerSubject)))
.assertContainsNoOtherRules();
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java
index 9b344c5..fe575ed 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumValueOfOptimizationTest.java
@@ -4,36 +4,77 @@
package com.android.tools.r8.rewrite.enums;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase.EnumKeepRules;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+// This is testing various edge cases of the Enum.valueOf(Class, String) optimization, such as
+// passing an enum subclass constant as the first argument to Enum.valueOf. The implementation of
+// Enum.valueOf reflects on the user program and keep rules are therefore needed for the program
+// to produce the expected result after shrinking.
@RunWith(Parameterized.class)
public class EnumValueOfOptimizationTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public boolean enableNoVerticalClassMergingAnnotations;
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
+ @Parameter(1)
+ public EnumKeepRules enumKeepRules;
- public EnumValueOfOptimizationTest(TestParameters parameters) {
- this.parameters = parameters;
+ @Parameter(2)
+ public TestParameters parameters;
+
+ @Parameters(name = "{2}, annotations: {0}, keep: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ EnumKeepRules.values(),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
}
@Test
public void testValueOf() throws Exception {
testForR8(parameters.getBackend())
- .addKeepMainRule(Main.class)
.addInnerClasses(EnumValueOfOptimizationTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .applyIf(
+ enableNoVerticalClassMergingAnnotations,
+ R8TestBuilder::enableNoVerticalClassMergingAnnotations,
+ TestShrinkerBuilder::addNoVerticalClassMergingAnnotations)
.setMinApi(parameters)
.compile()
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "iae4 OK");
+ .applyIf(
+ parameters.isCfRuntime()
+ || enableNoVerticalClassMergingAnnotations
+ || enumKeepRules.isStudio(),
+ runResult ->
+ runResult.assertSuccessWithOutputLines(
+ "npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "iae4 OK"),
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
+ runResult ->
+ runResult.assertSuccessWithOutputLines(
+ "npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "npe OK"),
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V10_0_0),
+ runResult ->
+ runResult.assertSuccessWithOutputLines(
+ "npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "nsme->re OK"),
+ runResult ->
+ runResult.assertSuccessWithOutputLines(
+ "npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "nsme->ae OK"));
}
enum MyEnum {
@@ -41,6 +82,7 @@
B
}
+ @NoVerticalClassMerging
enum ComplexEnum {
A {
@Override
@@ -62,9 +104,25 @@
Enum<?> e = null;
try {
e = subtypeError();
- System.out.println("iae4 KO");
+ } catch (AssertionError ae) {
+ if (ae.getCause() instanceof NoSuchMethodException) {
+ System.out.println("nsme->ae OK");
+ } else {
+ System.out.println(ae.getClass().getName());
+ System.out.println(ae.getCause().getClass().getName());
+ }
} catch (IllegalArgumentException iae) {
System.out.println("iae4 OK");
+ } catch (NullPointerException npe) {
+ System.out.println("npe OK");
+ } catch (RuntimeException re) {
+ if (re.getClass() == RuntimeException.class
+ && re.getCause() instanceof NoSuchMethodException) {
+ System.out.println("nsme->re OK");
+ } else {
+ System.out.println(re.getClass().getName());
+ System.out.println(re.getCause().getClass().getName());
+ }
}
if (e != null) {
throw new Error("enum set");
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 0fe9536..da8944c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -148,8 +148,7 @@
assertThat(foo, isAbsent());
ClassSubject superInterface2 = inspector.clazz(B112452064SuperInterface2.class);
- assertThat(
- superInterface2, isAbsentIf(enableUnusedInterfaceRemoval && enableVerticalClassMerging));
+ assertThat(superInterface2, isAbsentIf(enableVerticalClassMerging));
MethodSubject bar = superInterface2.uniqueMethodWithOriginalName("bar");
assertThat(bar, isAbsent());
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index b573cd5..6ae395d 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -58,7 +58,7 @@
protected abstract String getMainClass();
- private final TestParameters parameters;
+ protected final TestParameters parameters;
private final MinifyMode minify;
public TestParameters getParameters() {
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
index 0b89761..3b80981 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
@@ -3,13 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.examples;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbstract;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.TreeShakingTest;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -40,7 +45,7 @@
@Test
public void testKeeprules() throws Exception {
runTest(
- TreeShaking9Test::shaking9OnlySuperMethodsKept,
+ this::shaking9OnlySuperMethodsKept,
null,
null,
ImmutableList.of("src/test/examples/shaking9/keep-rules.txt"));
@@ -52,11 +57,19 @@
null, null, null, ImmutableList.of("src/test/examples/shaking9/keep-rules-printusage.txt"));
}
- private static void shaking9OnlySuperMethodsKept(CodeInspector inspector) {
+ private void shaking9OnlySuperMethodsKept(CodeInspector inspector) {
ClassSubject superclass = inspector.clazz("shaking9.Superclass");
- Assert.assertTrue(superclass.isAbstract());
- Assert.assertTrue(superclass.method("void", "aMethod", ImmutableList.of()).isPresent());
+ if (parameters.canHaveNonReboundConstructorInvoke()) {
+ assertThat(superclass, isAbsent());
+ } else {
+ assertThat(superclass, isAbstract());
+ assertThat(superclass.method("void", "aMethod", ImmutableList.of()), isPresent());
+ }
+
ClassSubject subclass = inspector.clazz("shaking9.Subclass");
- Assert.assertFalse(subclass.method("void", "aMethod", ImmutableList.of()).isPresent());
+ assertThat(subclass, isPresent());
+ assertThat(
+ subclass.method("void", "aMethod", ImmutableList.of()),
+ isPresentIf(parameters.canHaveNonReboundConstructorInvoke() && !getMinify().isMinify()));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java b/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java
index 97f8d03..943e497 100644
--- a/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/interfaces/RedundantImplementsClauseTest.java
@@ -86,6 +86,7 @@
}
}
+ @NoVerticalClassMerging
interface I {
void m();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 04202fd..be77daa 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -341,22 +341,27 @@
};
}
- public static Matcher<MethodSubject> isAbstract() {
- return new TypeSafeMatcher<MethodSubject>() {
+ public static Matcher<ClassOrMemberSubject> isAbstract() {
+ return new TypeSafeMatcher<ClassOrMemberSubject>() {
@Override
- public boolean matchesSafely(final MethodSubject method) {
- return method.isPresent() && method.isAbstract();
+ public boolean matchesSafely(ClassOrMemberSubject subject) {
+ if (subject instanceof FoundClassSubject) {
+ return ((FoundClassSubject) subject).isAbstract();
+ }
+ if (subject instanceof FoundMethodSubject) {
+ return ((FoundMethodSubject) subject).isAbstract();
+ }
+ return false;
}
@Override
- public void describeTo(final Description description) {
- description.appendText("method abstract");
+ public void describeTo(Description description) {
+ description.appendText("abstract");
}
@Override
- public void describeMismatchSafely(final MethodSubject method, Description description) {
- description
- .appendText("method ").appendValue(method.getOriginalName()).appendText(" was not");
+ public void describeMismatchSafely(ClassOrMemberSubject subject, Description description) {
+ description.appendValue(subject.getOriginalName()).appendText(" was not");
}
};
}