Avoid IR processing of default instance initializers
Change-Id: I54673b14fe973a7aa9b6f4daae7fd1b6323811c6
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 0263c5f..a0ac288 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -982,7 +982,7 @@
} else if (code.isDexCode()) {
assert verifyOriginalMethodInDebugInfo(code.asDexCode(), originalMethod);
} else {
- assert code.isThrowNullCode();
+ assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
}
}
}));
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 455d586..a48fb5c 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -182,6 +182,11 @@
}
@Override
+ public CfWritableCodeKind getCfWritableCodeKind() {
+ return CfWritableCodeKind.DEFAULT;
+ }
+
+ @Override
public StructuralMapping<CfCode> getStructuralMapping() {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfWritableCode.java b/src/main/java/com/android/tools/r8/graph/CfWritableCode.java
index fd23726..939695f 100644
--- a/src/main/java/com/android/tools/r8/graph/CfWritableCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfWritableCode.java
@@ -5,12 +5,51 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import org.objectweb.asm.MethodVisitor;
public interface CfWritableCode {
+ enum CfWritableCodeKind {
+ DEFAULT,
+ DEFAULT_INSTANCE_INITIALIZER,
+ THROW_NULL
+ }
+
+ default int acceptCompareTo(CfWritableCode code, CompareToVisitor visitor) {
+ CfWritableCodeKind kind = getCfWritableCodeKind();
+ CfWritableCodeKind otherKind = code.getCfWritableCodeKind();
+ if (kind != otherKind) {
+ return kind.compareTo(otherKind);
+ }
+ switch (kind) {
+ case DEFAULT:
+ return asCfCode().acceptCompareTo(code.asCfCode(), visitor);
+ case DEFAULT_INSTANCE_INITIALIZER:
+ return 0;
+ case THROW_NULL:
+ return 0;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ void acceptHashing(HashingVisitor visitor);
+
+ CfWritableCodeKind getCfWritableCodeKind();
+
+ default boolean isCfCode() {
+ return false;
+ }
+
+ default CfCode asCfCode() {
+ return null;
+ }
+
void writeCf(
ProgramMethod method,
CfVersion classFileVersion,
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index ba7a9c9..6e41fa0 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -62,6 +62,14 @@
return false;
}
+ public boolean isDefaultInstanceInitializerCode() {
+ return false;
+ }
+
+ public DefaultInstanceInitializerCode asDefaultInstanceInitializerCode() {
+ return null;
+ }
+
public boolean isDexCode() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
new file mode 100644
index 0000000..df2f50a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -0,0 +1,524 @@
+// Copyright (c) 2021, 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.graph;
+
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.dex.CodeToKeep;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.NumberGenerator;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.google.common.collect.ImmutableList;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A piece of code on the form:
+ *
+ * <pre>
+ * aload_0
+ * invoke-special LSuperClass;-><init>()V
+ * return
+ * </pre>
+ *
+ * <p>Note that (i) {@code SuperClass} may be different from {@link java.lang.Object} and (ii) the
+ * method holding this code object may have a non-empty proto.
+ */
+public class DefaultInstanceInitializerCode extends Code
+ implements CfWritableCode, DexWritableCode {
+
+ private static final DefaultInstanceInitializerCode INSTANCE =
+ new DefaultInstanceInitializerCode();
+
+ private DefaultInstanceInitializerCode() {}
+
+ public static DefaultInstanceInitializerCode get() {
+ return INSTANCE;
+ }
+
+ public static boolean canonicalizeCodeIfPossible(AppView<?> appView, ProgramMethod method) {
+ if (hasDefaultInstanceInitializerCode(method, appView)) {
+ method.getDefinition().setCode(get(), appView);
+ return true;
+ }
+ return false;
+ }
+
+ public static void uncanonicalizeCode(AppView<?> appView, ProgramMethod method) {
+ uncanonicalizeCode(appView, method, method.getHolder().getSuperType());
+ }
+
+ public static void uncanonicalizeCode(
+ AppView<?> appView, ProgramMethod method, DexType superType) {
+ DexEncodedMethod definition = method.getDefinition();
+ assert definition.getCode().isDefaultInstanceInitializerCode();
+ definition.setCode(get().toCfCode(method, appView.dexItemFactory(), superType), appView);
+ }
+
+ private static boolean hasDefaultInstanceInitializerCode(
+ ProgramMethod method, AppView<?> appView) {
+ if (!method.getDefinition().isInstanceInitializer()) {
+ return false;
+ }
+ Code code = method.getDefinition().getCode();
+ if (!code.isCfCode()) {
+ return false;
+ }
+ CfCode cfCode = code.asCfCode();
+ if (!method.getDefinition().isInstanceInitializer()
+ || !cfCode.getLocalVariables().isEmpty()
+ || !cfCode.getTryCatchRanges().isEmpty()) {
+ return false;
+ }
+ if (cfCode.getInstructions().size() > 6) {
+ // Default instance initializers typically have the following instruction sequence:
+ // [CfLabel, CfPosition, CfLoad, CfInvoke, CfReturnVoid, CfLabel].
+ return false;
+ }
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ Iterator<CfInstruction> instructionIterator = cfCode.getInstructions().iterator();
+ // Allow skipping CfPosition instructions in instance initializers that only call Object.<init>.
+ Predicate<CfInstruction> instructionOfInterest =
+ method.getHolder().getSuperType() == dexItemFactory.objectType
+ ? instruction -> !instruction.isLabel() && !instruction.isPosition()
+ : instruction -> !instruction.isLabel();
+ CfLoad load = IteratorUtils.nextUntil(instructionIterator, instructionOfInterest).asLoad();
+ if (load == null || load.getLocalIndex() != 0) {
+ return false;
+ }
+ CfInvoke invoke = instructionIterator.next().asInvoke();
+ if (invoke == null
+ || !invoke.isInvokeConstructor(dexItemFactory)
+ || invoke.getMethod() != getParentConstructor(method, dexItemFactory)) {
+ return false;
+ }
+ return instructionIterator.next().isReturnVoid();
+ }
+
+ @Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(getCfWritableCodeKind().hashCode());
+ }
+
+ @Override
+ public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+ DefaultInstanceInitializerSourceCode source = new DefaultInstanceInitializerSourceCode(method);
+ return IRBuilder.create(method, appView, source, origin).build(method);
+ }
+
+ @Override
+ public IRCode buildInliningIR(
+ ProgramMethod context,
+ ProgramMethod method,
+ AppView<?> appView,
+ GraphLens codeLens,
+ NumberGenerator valueNumberGenerator,
+ Position callerPosition,
+ Origin origin,
+ RewrittenPrototypeDescription protoChanges) {
+ DefaultInstanceInitializerSourceCode source =
+ new DefaultInstanceInitializerSourceCode(method, callerPosition);
+ return IRBuilder.createForInlining(
+ method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
+ .build(context);
+ }
+
+ @Override
+ public int codeSizeInBytes() {
+ return InvokeDirect.SIZE + ReturnVoid.SIZE;
+ }
+
+ @Override
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems,
+ ProgramMethod context,
+ GraphLens graphLens,
+ LensCodeRewriterUtils rewriter) {
+ getParentConstructor(context, rewriter.dexItemFactory()).collectIndexedItems(indexedItems);
+ }
+
+ @Override
+ public void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+ // Intentionally empty.
+ }
+
+ @Override
+ protected int computeHashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ return this == other;
+ }
+
+ @Override
+ public int estimatedDexCodeSizeUpperBoundInBytes() {
+ return codeSizeInBytes();
+ }
+
+ @Override
+ public CfWritableCodeKind getCfWritableCodeKind() {
+ return CfWritableCodeKind.DEFAULT_INSTANCE_INITIALIZER;
+ }
+
+ @Override
+ public DexWritableCodeKind getDexWritableCodeKind() {
+ return DexWritableCodeKind.DEFAULT_INSTANCE_INITIALIZER;
+ }
+
+ @Override
+ public DexDebugInfoForWriting getDebugInfoForWriting() {
+ return null;
+ }
+
+ @Override
+ public TryHandler[] getHandlers() {
+ return new TryHandler[0];
+ }
+
+ @Override
+ public DexString getHighestSortingString() {
+ return null;
+ }
+
+ @Override
+ public int getIncomingRegisterSize(ProgramMethod method) {
+ return getMaxLocals(method);
+ }
+
+ static DexMethod getParentConstructor(DexClassAndMethod method, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createInstanceInitializer(method.getHolder().getSuperType());
+ }
+
+ private int getMaxLocals(ProgramMethod method) {
+ int maxLocals = method.getAccessFlags().isStatic() ? 0 : 1;
+ for (DexType parameter : method.getParameters()) {
+ maxLocals += parameter.getRequiredRegisters();
+ }
+ return maxLocals;
+ }
+
+ private int getMaxStack() {
+ return 1;
+ }
+
+ @Override
+ public int getOutgoingRegisterSize() {
+ return 1;
+ }
+
+ @Override
+ public int getRegisterSize(ProgramMethod method) {
+ return getIncomingRegisterSize(method);
+ }
+
+ @Override
+ public Try[] getTries() {
+ return new Try[0];
+ }
+
+ @Override
+ public boolean isCfWritableCode() {
+ return true;
+ }
+
+ @Override
+ public CfWritableCode asCfWritableCode() {
+ return this;
+ }
+
+ @Override
+ public boolean isDexWritableCode() {
+ return true;
+ }
+
+ @Override
+ public DexWritableCode asDexWritableCode() {
+ return this;
+ }
+
+ @Override
+ public boolean isEmptyVoidMethod() {
+ return false;
+ }
+
+ @Override
+ public boolean isDefaultInstanceInitializerCode() {
+ return true;
+ }
+
+ @Override
+ public DefaultInstanceInitializerCode asDefaultInstanceInitializerCode() {
+ return this;
+ }
+
+ @Override
+ public boolean isSharedCodeObject() {
+ return true;
+ }
+
+ @Override
+ public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+ internalRegisterCodeReferences(method, registry);
+ }
+
+ @Override
+ public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+ internalRegisterCodeReferences(method, registry);
+ }
+
+ private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry<?> registry) {
+ registry.registerInvokeDirect(getParentConstructor(method, registry.dexItemFactory()));
+ }
+
+ @Override
+ public DexWritableCode rewriteCodeWithJumboStrings(
+ ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force) {
+ // Intentionally empty. This piece of code does not have any const-string instructions.
+ return this;
+ }
+
+ @Override
+ public void setCallSiteContexts(ProgramMethod method) {
+ // Intentionally empty. This piece of code does not have any call sites.
+ }
+
+ public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory) {
+ return toCfCode(method, dexItemFactory, method.getHolder().getSuperType());
+ }
+
+ public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory, DexType supertype) {
+ List<CfInstruction> instructions =
+ Arrays.asList(
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ Opcodes.INVOKESPECIAL, dexItemFactory.createInstanceInitializer(supertype), false),
+ new CfReturnVoid());
+ return new CfCode(method.getHolderType(), getMaxStack(), getMaxLocals(method), instructions);
+ }
+
+ @Override
+ public void writeCf(
+ ProgramMethod method,
+ CfVersion classFileVersion,
+ AppView<?> appView,
+ NamingLens namingLens,
+ LensCodeRewriterUtils rewriter,
+ MethodVisitor visitor) {
+ visitor.visitVarInsn(Opcodes.ALOAD, 0);
+ visitor.visitMethodInsn(
+ Opcodes.INVOKESPECIAL,
+ namingLens.lookupInternalName(method.getHolder().getSuperType()),
+ "<init>",
+ "()V",
+ false);
+ visitor.visitInsn(Opcodes.RETURN);
+ visitor.visitEnd();
+ visitor.visitMaxs(getMaxStack(), getMaxLocals(method));
+ }
+
+ @Override
+ public void writeDex(
+ ShortBuffer shortBuffer,
+ ProgramMethod context,
+ GraphLens graphLens,
+ LensCodeRewriterUtils lensCodeRewriter,
+ ObjectToOffsetMapping mapping) {
+ new InvokeDirect(1, getParentConstructor(context, mapping.dexItemFactory()), 0, 0, 0, 0, 0)
+ .write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+ new ReturnVoid().write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+ }
+
+ @Override
+ public void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultInstanceInitializerCode";
+ }
+
+ @Override
+ public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+ return toString();
+ }
+
+ static class DefaultInstanceInitializerSourceCode implements SourceCode {
+
+ private static final List<Consumer<IRBuilder>> instructionBuilders =
+ ImmutableList.of(
+ builder ->
+ builder.add(
+ com.android.tools.r8.ir.code.InvokeDirect.builder()
+ .setMethod(
+ getParentConstructor(
+ builder.getProgramMethod(), builder.dexItemFactory()))
+ .setSingleArgument(builder.getReceiverValue())
+ .build()),
+ IRBuilder::addReturn);
+
+ private final Position position;
+
+ DefaultInstanceInitializerSourceCode(ProgramMethod method) {
+ this(method, null);
+ }
+
+ DefaultInstanceInitializerSourceCode(ProgramMethod method, Position callerPosition) {
+ this.position =
+ SyntheticPosition.builder()
+ .setLine(0)
+ .setMethod(method.getReference())
+ .setCallerPosition(callerPosition)
+ .build();
+ }
+
+ @Override
+ public int instructionCount() {
+ return instructionBuilders.size();
+ }
+
+ @Override
+ public int instructionIndex(int instructionOffset) {
+ return instructionOffset;
+ }
+
+ @Override
+ public int instructionOffset(int instructionIndex) {
+ return instructionIndex;
+ }
+
+ @Override
+ public void buildPrelude(IRBuilder builder) {
+ int firstArgumentRegister = 0;
+ builder.buildArgumentsWithRewrittenPrototypeChanges(
+ firstArgumentRegister, builder.getMethod(), ConsumerUtils.emptyBiConsumer());
+ }
+
+ @Override
+ public void buildInstruction(
+ IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
+ instructionBuilders.get(instructionIndex).accept(builder);
+ }
+
+ @Override
+ public void buildPostlude(IRBuilder builder) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void clear() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public Position getCanonicalDebugPositionAtOffset(int offset) {
+ return null;
+ }
+
+ @Override
+ public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
+ return null;
+ }
+
+ @Override
+ public Position getCurrentPosition() {
+ return position;
+ }
+
+ @Override
+ public DebugLocalInfo getIncomingLocal(int register) {
+ return null;
+ }
+
+ @Override
+ public DebugLocalInfo getIncomingLocalAtBlock(int register, int blockOffset) {
+ return null;
+ }
+
+ @Override
+ public DebugLocalInfo getOutgoingLocal(int register) {
+ return null;
+ }
+
+ @Override
+ public void setUp() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public int traceInstruction(int instructionIndex, IRBuilder builder) {
+ // This instruction does not close the block.
+ return -1;
+ }
+
+ @Override
+ public boolean verifyCurrentInstructionCanThrow() {
+ return true;
+ }
+
+ @Override
+ public boolean verifyRegister(int register) {
+ return true;
+ }
+
+ @Override
+ public void buildBlockTransfer(
+ IRBuilder builder, int predecessorOffset, int successorOffset, boolean isExceptional) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int getMoveExceptionRegister(int instructionIndex) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void resolveAndBuildNewArrayFilledData(
+ int arrayRef, int payloadOffset, IRBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void resolveAndBuildSwitch(
+ int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean verifyLocalInScope(DebugLocalInfo local) {
+ throw new Unreachable();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index c23bf18..8c16e46 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.structural.Equatable;
import com.android.tools.r8.utils.structural.HashCodeVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
@@ -130,6 +131,11 @@
}
@Override
+ public DexWritableCodeKind getDexWritableCodeKind() {
+ return DexWritableCodeKind.DEFAULT;
+ }
+
+ @Override
public StructuralMapping<DexCode> getStructuralMapping() {
return DexCode::specify;
}
@@ -319,6 +325,11 @@
}
@Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visit(this, getStructuralMapping());
+ }
+
+ @Override
public int computeHashCode() {
return incomingRegisterSize * 2
+ registerSize * 3
@@ -531,6 +542,7 @@
return builder.toString();
}
+ @Override
public void collectIndexedItems(
IndexedItemCollection indexedItems,
ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index afdef0a..d3be3c2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -304,21 +304,22 @@
}
private static int compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) {
- if (code1.isCfCode() && code2.isCfCode()) {
- return code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor);
+ if (code1.isCfWritableCode() && code2.isCfWritableCode()) {
+ return code1.asCfWritableCode().acceptCompareTo(code2.asCfWritableCode(), visitor);
}
- if (code1.isDexCode() && code2.isDexCode()) {
- return code1.asDexCode().acceptCompareTo(code2.asDexCode(), visitor);
+ if (code1.isDexWritableCode() && code2.isDexWritableCode()) {
+ return code1.asDexWritableCode().acceptCompareTo(code2.asDexWritableCode(), visitor);
}
throw new Unreachable(
"Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2);
}
private static void hashCodeObject(Code code, HashingVisitor visitor) {
- if (code.isCfCode()) {
- code.asCfCode().acceptHashing(visitor);
+ if (code.isCfWritableCode()) {
+ code.asCfWritableCode().acceptHashing(visitor);
} else {
- code.asDexCode().acceptHashing(visitor);
+ assert code.isDexWritableCode();
+ code.asDexWritableCode().acceptHashing(visitor);
}
}
@@ -394,6 +395,11 @@
return null;
}
+ public ProgramMethod asProgramMethod(DexProgramClass holder) {
+ assert getHolderType() == holder.getType();
+ return new ProgramMethod(holder, this);
+ }
+
public ProgramMethod asProgramMethod(DexDefinitionSupplier definitions) {
assert getReference().holder.isClassType();
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference()));
diff --git a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
index 9aea5e4..664b4e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
@@ -5,22 +5,60 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.dex.CodeToKeep;
+import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCode.Try;
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import java.nio.ShortBuffer;
public interface DexWritableCode {
+ enum DexWritableCodeKind {
+ DEFAULT,
+ DEFAULT_INSTANCE_INITIALIZER,
+ THROW_NULL
+ }
+
+ default int acceptCompareTo(DexWritableCode code, CompareToVisitor visitor) {
+ DexWritableCodeKind kind = getDexWritableCodeKind();
+ DexWritableCodeKind otherKind = code.getDexWritableCodeKind();
+ if (kind != otherKind) {
+ return kind.compareTo(otherKind);
+ }
+ switch (kind) {
+ case DEFAULT:
+ return asDexCode().acceptCompareTo(code.asDexCode(), visitor);
+ case DEFAULT_INSTANCE_INITIALIZER:
+ return 0;
+ case THROW_NULL:
+ return 0;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ void acceptHashing(HashingVisitor visitor);
+
int codeSizeInBytes();
+ void collectIndexedItems(
+ IndexedItemCollection indexedItems,
+ ProgramMethod context,
+ GraphLens graphLens,
+ LensCodeRewriterUtils rewriter);
+
void collectMixedSectionItems(MixedSectionCollection mixedItems);
void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep);
DexDebugInfoForWriting getDebugInfoForWriting();
+ DexWritableCodeKind getDexWritableCodeKind();
+
DexString getHighestSortingString();
TryHandler[] getHandlers();
@@ -33,6 +71,14 @@
int getOutgoingRegisterSize();
+ default boolean isDexCode() {
+ return false;
+ }
+
+ default DexCode asDexCode() {
+ return null;
+ }
+
/** Rewrites the code to have JumboString bytecode if required by mapping. */
DexWritableCode rewriteCodeWithJumboStrings(
ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force);
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index 196bc31..04b3eb0 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -29,6 +29,7 @@
private final static int NOT_FOUND = -1;
private final static int NOT_SET = -2;
+ private final AppView<?> appView;
private final GraphLens graphLens;
private final NamingLens namingLens;
private final InitClassLens initClassLens;
@@ -74,6 +75,7 @@
assert callSites != null;
assert methodHandles != null;
assert initClassLens != null;
+ this.appView = appView;
this.graphLens = graphLens;
this.namingLens = namingLens;
this.initClassLens = initClassLens;
@@ -228,6 +230,10 @@
return map == null ? Collections.emptyList() : map.keySet();
}
+ public DexItemFactory dexItemFactory() {
+ return appView.dexItemFactory();
+ }
+
public GraphLens getGraphLens() {
return graphLens;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 90278d0..844b45e 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -64,11 +64,7 @@
getReference().collectIndexedItems(indexedItems);
if (definition.hasCode()) {
Code code = definition.getCode();
- if (code.isDexCode()) {
- code.asDexCode().collectIndexedItems(indexedItems, this, graphLens, rewriter);
- } else {
- assert code.isThrowNullCode();
- }
+ code.asDexWritableCode().collectIndexedItems(indexedItems, this, graphLens, rewriter);
}
definition.annotations().collectIndexedItems(indexedItems);
definition.parameterAnnotationsList.collectIndexedItems(indexedItems);
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index c9d875a..28a7ace 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.Throw;
import com.android.tools.r8.dex.CodeToKeep;
+import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCode.Try;
@@ -24,6 +25,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.structural.HashingVisitor;
import com.google.common.collect.ImmutableList;
import java.nio.ShortBuffer;
import java.util.List;
@@ -42,6 +44,11 @@
}
@Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitInt(getCfWritableCodeKind().hashCode());
+ }
+
+ @Override
public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
ThrowNullSourceCode source = new ThrowNullSourceCode(method);
return IRBuilder.create(method, appView, source, origin).build(method);
@@ -69,6 +76,15 @@
}
@Override
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems,
+ ProgramMethod context,
+ GraphLens graphLens,
+ LensCodeRewriterUtils rewriter) {
+ // Intentionally empty.
+ }
+
+ @Override
public void collectMixedSectionItems(MixedSectionCollection mixedItems) {
// Intentionally empty.
}
@@ -89,6 +105,16 @@
}
@Override
+ public CfWritableCodeKind getCfWritableCodeKind() {
+ return CfWritableCodeKind.THROW_NULL;
+ }
+
+ @Override
+ public DexWritableCodeKind getDexWritableCodeKind() {
+ return DexWritableCodeKind.THROW_NULL;
+ }
+
+ @Override
public DexDebugInfoForWriting getDebugInfoForWriting() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 2f9a4ee..e16a15f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -117,7 +117,7 @@
appView.setHorizontallyMergedClasses(mergedClasses, mode);
HorizontalClassMergerGraphLens horizontalClassMergerGraphLens =
- createLens(mergedClasses, lensBuilder, syntheticArgumentClass);
+ createLens(mergedClasses, lensBuilder, mode, syntheticArgumentClass);
assert verifyNoCyclesInInterfaceHierarchies(groups);
@@ -235,8 +235,9 @@
private HorizontalClassMergerGraphLens createLens(
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
+ Mode mode,
SyntheticArgumentClass syntheticArgumentClass) {
- return new TreeFixer(appView, mergedClasses, lensBuilder, syntheticArgumentClass)
+ return new TreeFixer(appView, mergedClasses, lensBuilder, mode, syntheticArgumentClass)
.fixupTypeReferences();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 2ab6c46..3c97009 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -375,8 +375,8 @@
syntheticInitializerConverterBuilder.add(
new ProgramMethod(group.getTarget(), newInstanceInitializer));
} else {
- assert !appView.options().isGeneratingClassFiles()
- || newInstanceInitializer.getCode().isCfCode();
+ assert appView.options().isGeneratingDex()
+ || newInstanceInitializer.getCode().isCfWritableCode();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index b85eab3..7a74a2b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -19,6 +20,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.AnnotationFixer;
import com.android.tools.r8.utils.ArrayUtils;
@@ -28,8 +30,10 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -42,9 +46,12 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final HorizontallyMergedClasses mergedClasses;
+ private final Mode mode;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
private final DexItemFactory dexItemFactory;
private final SyntheticArgumentClass syntheticArgumentClass;
+
+ private final Map<DexProgramClass, DexType> originalSuperTypes = new IdentityHashMap<>();
private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures =
HashBiMap.create();
@@ -52,10 +59,12 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
+ Mode mode,
SyntheticArgumentClass syntheticArgumentClass) {
super(appView);
this.appView = appView;
this.mergedClasses = mergedClasses;
+ this.mode = mode;
this.lensBuilder = lensBuilder;
this.syntheticArgumentClass = syntheticArgumentClass;
this.dexItemFactory = appView.dexItemFactory();
@@ -145,7 +154,11 @@
}
private void fixupProgramClassSuperTypes(DexProgramClass clazz) {
- clazz.superType = fixupType(clazz.superType);
+ DexType rewrittenSuperType = fixupType(clazz.getSuperType());
+ if (rewrittenSuperType != clazz.getSuperType()) {
+ originalSuperTypes.put(clazz, clazz.getSuperType());
+ clazz.superType = rewrittenSuperType;
+ }
clazz.setInterfaces(fixupInterfaces(clazz, clazz.getInterfaces()));
}
@@ -166,7 +179,7 @@
method -> fixupVirtualMethod(remappedClassVirtualMethods, newMethodReferences, method));
clazz
.getMethodCollection()
- .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, method));
+ .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, clazz, method));
Set<DexField> newFieldReferences = Sets.newIdentityHashSet();
DexEncodedField[] instanceFields = clazz.clearInstanceFields();
@@ -220,7 +233,7 @@
Set<DexMethodSignature> newDirectMethods = new LinkedHashSet<>();
iface
.getMethodCollection()
- .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, method));
+ .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, iface, method));
iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod);
assert !iface.hasInstanceFields();
@@ -263,7 +276,7 @@
}
private DexEncodedMethod fixupDirectMethod(
- Set<DexMethodSignature> newMethods, DexEncodedMethod method) {
+ Set<DexMethodSignature> newMethods, DexProgramClass clazz, DexEncodedMethod method) {
DexMethod originalMethodReference = method.getReference();
// Fix all type references in the method prototype.
@@ -299,6 +312,17 @@
boolean changed = newMethods.add(newMethodReference.getSignature());
assert changed;
+ // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens
+ // code rewriting.
+ if (mode.isInitial()
+ && method.hasCode()
+ && method.getCode().isDefaultInstanceInitializerCode()
+ && mergedClasses.hasBeenMergedOrIsMergeTarget(clazz.getSuperType())) {
+ DexType originalSuperType = originalSuperTypes.getOrDefault(clazz, clazz.getSuperType());
+ DefaultInstanceInitializerCode.uncanonicalizeCode(
+ appView, method.asProgramMethod(clazz), originalSuperType);
+ }
+
return fixupProgramMethod(newMethodReference, method);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
index fce04fa..6e88f4d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
@@ -34,19 +34,20 @@
}
// For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed.
- if (code == null || !code.isCfCode()) {
+ if (code == null) {
return true;
}
- CfCode cfCode = code.asCfCode();
-
- ConstraintWithTarget constraint =
- cfCode.computeInliningConstraint(method, appView, appView.graphLens(), method);
- if (constraint == ConstraintWithTarget.NEVER) {
+ if (code.isCfCode()) {
+ CfCode cfCode = code.asCfCode();
+ ConstraintWithTarget constraint =
+ cfCode.computeInliningConstraint(method, appView, appView.graphLens(), method);
+ return constraint == ConstraintWithTarget.NEVER;
+ } else if (code.isDefaultInstanceInitializerCode()) {
+ return false;
+ } else {
return true;
}
-
- return false;
}
@Override
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 eb30bd3..513719e 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
@@ -1169,7 +1169,7 @@
assert code.verifyTypes(appView);
assert code.isConsistentSSA();
- if (appView.isCfByteCodePassThrough(method)) {
+ if (shouldPassThrough(context)) {
// If the code is pass trough, do not finalize by overwriting the existing code.
assert appView.enableWholeProgramOptimizations();
timing.begin("Collect optimization info");
@@ -1183,6 +1183,7 @@
BytecodeMetadataProvider.builder(),
timing);
timing.end();
+ markProcessed(code, feedback);
return timing;
}
@@ -1516,6 +1517,15 @@
return timing;
}
+ private boolean shouldPassThrough(ProgramMethod method) {
+ if (appView.isCfByteCodePassThrough(method.getDefinition())) {
+ return true;
+ }
+ Code code = method.getDefinition().getCode();
+ assert !code.isThrowNullCode();
+ return code.isDefaultInstanceInitializerCode();
+ }
+
// Compute optimization info summary for the current method unless it is pinned
// (in that case we should not be making any assumptions about the behavior of the method).
public void collectOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index dd5b328..c411187 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -61,6 +61,10 @@
this.rewrittenCallSiteCache = null;
}
+ public DexItemFactory dexItemFactory() {
+ return definitions.dexItemFactory();
+ }
+
public DexCallSite rewriteCallSite(DexCallSite callSite, ProgramMethod context) {
if (rewrittenCallSiteCache == null) {
return rewriteCallSiteInternal(callSite, context);
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index f00e19c..2e21467 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -176,7 +176,7 @@
instructions);
code.asCfCode().setInstructions(newInstructions);
} else {
- assert code.isThrowNullCode();
+ assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index f837095..db5dcdc 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
@@ -296,15 +297,16 @@
firstUnreachableIndex(methods, method -> appInfo.isLiveMethod(method.getReference()));
// Return the original array if all methods are used.
if (firstUnreachable == -1) {
+ for (DexEncodedMethod method : methods) {
+ canonicalizeCode(method.asProgramMethod(clazz));
+ }
return null;
}
ArrayList<DexEncodedMethod> reachableMethods = new ArrayList<>(methods.size());
- for (int i = 0; i < firstUnreachable; i++) {
- reachableMethods.add(methods.get(i));
- }
- for (int i = firstUnreachable; i < methods.size(); i++) {
+ for (int i = 0; i < methods.size(); i++) {
DexEncodedMethod method = methods.get(i);
if (appInfo.isLiveMethod(method.getReference())) {
+ canonicalizeCode(method.asProgramMethod(clazz));
reachableMethods.add(method);
} else if (options.configurationDebugging) {
// Keep the method but rewrite its body, if it has one.
@@ -339,6 +341,12 @@
: reachableMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
}
+ private void canonicalizeCode(ProgramMethod method) {
+ if (method.getDefinition().hasCode()) {
+ DefaultInstanceInitializerCode.canonicalizeCodeIfPossible(appView, method);
+ }
+ }
+
private DexEncodedField[] reachableFields(List<DexEncodedField> fields) {
AppInfoWithLiveness appInfo = appView.appInfo();
Predicate<DexEncodedField> isReachableOrReferencedField =
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index e157ff8..c53f8ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -1119,6 +1120,12 @@
// Rewrite generic signatures before we merge fields.
rewriteGenericSignatures(target, source, directMethods.values(), virtualMethods.values());
+ // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens
+ // code rewriting.
+ target.forEachProgramInstanceInitializerMatching(
+ method -> method.getCode().isDefaultInstanceInitializerCode(),
+ method -> DefaultInstanceInitializerCode.uncanonicalizeCode(appView, method));
+
// Step 2: Merge fields
Set<DexString> existingFieldNames = new HashSet<>();
for (DexEncodedField field : target.fields()) {
@@ -1891,6 +1898,8 @@
return AbortReason.MAIN_DEX_ROOT_OUTSIDE_REFERENCE;
}
return null;
+ } else if (code.isDefaultInstanceInitializerCode()) {
+ return null;
}
// For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed.
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
index be5c5a2..c9cc17b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
@@ -30,7 +30,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public B135918413(TestParameters parameters) {
@@ -43,7 +43,7 @@
.addInnerClasses(B135918413.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
index af4995f..19138f1 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +54,7 @@
.addDontWarn(MissingSuperType.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.compile()
.inspect(
inspector -> {
@@ -77,6 +79,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
public static class ClassWithSuperCall extends MissingSuperType {
@Override
@@ -88,6 +91,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
public static class ClassWithoutSuperCall extends MissingSuperType {
@Override