Rewrite virtual method merging to synthesize CF code
This also ensures that CfCode synthesized by the horizontal class merger is never rewritten using lenses created prior to horizontal class merging.
Bug: 169303041
Change-Id: I622f0af1a1edab215139b5cbf59cdcfd7e5a8b3b
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index fd28cde..1d05d43 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -166,6 +166,11 @@
}
@Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return originalMethodSignatures.inverse().getOrDefault(method, method);
+ }
+
+ @Override
public boolean isContextFreeForMethods() {
return true;
}
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 7a06b3d..a260816 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -33,6 +33,10 @@
+ getClass().getCanonicalName());
}
+ public GraphLens getCodeLens(AppView<?> appView) {
+ return appView.codeLens();
+ }
+
public BytecodeMetadata<? extends CfOrDexInstruction> getMetadata() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 3041ccf..952c31d 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -543,6 +543,10 @@
return false;
}
+ public boolean isHorizontalClassMergerGraphLens() {
+ return false;
+ }
+
public abstract boolean isIdentityLens();
public boolean isMemberRebindingLens() {
@@ -911,6 +915,8 @@
public abstract DexMethod getPreviousMethodSignature(DexMethod method);
+ public abstract DexMethod getNextMethodSignature(DexMethod method);
+
@Override
public final boolean isIdentityLens() {
return false;
@@ -1122,6 +1128,11 @@
}
@Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
public boolean isContextFreeForMethods() {
return getIdentityLens().isContextFreeForMethods();
}
diff --git a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
index 87e55e9..fe15a02 100644
--- a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
@@ -145,7 +145,7 @@
return originalMethod;
}
DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
- return internalGetNextMethodSignature(renamedMethod);
+ return getNextMethodSignature(renamedMethod);
}
@Override
@@ -257,7 +257,8 @@
return newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
}
- protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+ @Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
return newMethodSignatures.getRepresentativeValueOrDefault(method, method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 09d477d..1ce97e6 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -41,7 +41,8 @@
}
public GraphLens getCodeLens() {
- return appView.codeLens();
+ assert context.isMethod();
+ return getMethodContext().getDefinition().getCode().getCodeLens(appView);
}
public final T getContext() {
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 ad8d9df..18872a7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TraversalContinuation;
import java.util.ArrayList;
@@ -136,6 +137,7 @@
// Record where the synthesized $r8$classId fields are read and written.
if (mode.isInitial()) {
createFieldAccessInfoCollectionModifier(groups).modify(appView.withLiveness());
+ transformIncompleteCode(groups, horizontalClassMergerGraphLens, executorService);
} else {
assert groups.stream().noneMatch(MergeGroup::hasClassIdField);
}
@@ -164,6 +166,32 @@
return builder.build();
}
+ private void transformIncompleteCode(
+ Collection<MergeGroup> groups,
+ HorizontalClassMergerGraphLens horizontalClassMergerGraphLens,
+ ExecutorService executorService)
+ throws ExecutionException {
+ ThreadUtils.processItems(
+ groups,
+ group -> {
+ if (group.hasClassIdField()) {
+ DexProgramClass target = group.getTarget();
+ target.forEachProgramVirtualMethodMatching(
+ definition ->
+ definition.hasCode()
+ && definition.getCode() instanceof IncompleteVirtuallyMergedMethodCode,
+ method -> {
+ IncompleteVirtuallyMergedMethodCode code =
+ (IncompleteVirtuallyMergedMethodCode) method.getDefinition().getCode();
+ method
+ .getDefinition()
+ .setCode(code.toCfCode(method, horizontalClassMergerGraphLens), appView);
+ });
+ }
+ },
+ executorService);
+ }
+
private DirectMappedDexApplication getNewApplication(HorizontallyMergedClasses mergedClasses) {
// In the second round of class merging, we must forcefully remove the merged classes from the
// application, since we won't run tree shaking before writing the application.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
index 839b6d5..ce6f6be 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexType;
import java.util.List;
@@ -17,13 +16,8 @@
public class HorizontalClassMergerCfCode extends CfCode {
HorizontalClassMergerCfCode(
- DexType originalHolder,
- int maxStack,
- int maxLocals,
- List<CfInstruction> instructions,
- List<CfTryCatch> tryCatchRanges,
- List<LocalVariableInfo> localVariables) {
- super(originalHolder, maxStack, maxLocals, instructions, tryCatchRanges, localVariables);
+ DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) {
+ super(originalHolder, maxStack, maxLocals, instructions);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 937a1fc..75743d6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -45,6 +45,11 @@
}
@Override
+ public boolean isHorizontalClassMergerGraphLens() {
+ return true;
+ }
+
+ @Override
protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
new file mode 100644
index 0000000..65aed84
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
@@ -0,0 +1,244 @@
+// Copyright (c) 2022, 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.horizontalclassmerging;
+
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.cf.code.CfInstanceFieldRead;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfSwitch;
+import com.android.tools.r8.cf.code.CfSwitch.Kind;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.ClasspathMethod;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.IterableUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A short-lived piece of code that will be converted into {@link CfCode} using the method {@link
+ * #toCfCode(ProgramMethod, HorizontalClassMergerGraphLens)}.
+ */
+public class IncompleteVirtuallyMergedMethodCode extends Code {
+
+ private final DexField classIdField;
+ private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
+ private final DexMethod originalMethod;
+ private final DexMethod superMethod;
+
+ public IncompleteVirtuallyMergedMethodCode(
+ DexField classIdField,
+ Int2ReferenceSortedMap<DexMethod> mappedMethods,
+ DexMethod originalMethod,
+ DexMethod superMethod) {
+ this.mappedMethods = mappedMethods;
+ this.classIdField = classIdField;
+ this.superMethod = superMethod;
+ this.originalMethod = originalMethod;
+ }
+
+ @Override
+ public boolean isHorizontalClassMergingCode() {
+ return true;
+ }
+
+ /**
+ * Given a mapping from class ids to methods to invoke, this creates a piece of {@link CfCode} on
+ * the following form.
+ *
+ * <pre>
+ * public Bar m(Foo foo) {
+ * switch (this.classId) {
+ * case 0:
+ * return this.m1(foo);
+ * case 1:
+ * return this.m2(foo);
+ * ...
+ * default:
+ * return this.mN(foo); // or super.m(foo);
+ * }
+ * }
+ * </pre>
+ *
+ * <p>Note that the methods to invoke must be rewritten using {@param lens}, since the invoked
+ * methods may be changed as a result of the horizontal class merger's fixup (e.g., if the method
+ * signature refers to a horizontally merged type).
+ */
+ public CfCode toCfCode(ProgramMethod method, HorizontalClassMergerGraphLens lens) {
+ // We store each argument in a local.
+ int maxLocals = 1 + IterableUtils.sumInt(method.getParameters(), DexType::getRequiredRegisters);
+
+ // We load all arguments on the stack and then the receiver to fetch the class id.
+ int maxStack = maxLocals + 1;
+
+ // Create instructions.
+ List<CfInstruction> instructions = new ArrayList<>();
+
+ // Setup keys and labels for switch.
+ IntBidirectionalIterator classIdIterator = mappedMethods.keySet().iterator();
+ int[] keys = new int[mappedMethods.size() - BooleanUtils.intValue(superMethod == null)];
+ List<CfLabel> labels = new ArrayList<>();
+ for (int key = 0; key < keys.length; key++) {
+ keys[key] = classIdIterator.nextInt();
+ labels.add(new CfLabel());
+ }
+ CfLabel fallthroughLabel = new CfLabel();
+
+ // Add instructions.
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ int localIndex = 1;
+ for (DexType parameter : method.getParameters()) {
+ instructions.add(new CfLoad(ValueType.fromDexType(parameter), localIndex));
+ localIndex += parameter.getRequiredRegisters();
+ }
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(new CfInstanceFieldRead(classIdField));
+
+ // Emit switch.
+ instructions.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, keys, labels));
+ for (int key = 0; key < keys.length; key++) {
+ int classId = keys[key];
+ DexMethod target = lens.getNextMethodSignature(mappedMethods.get(classId));
+ instructions.add(labels.get(key));
+ instructions.add(createCfFrameForSwitchCase(method, maxLocals));
+ instructions.add(
+ new CfInvoke(Opcodes.INVOKESPECIAL, target, method.getHolder().isInterface()));
+ if (method.getReturnType().isVoidType()) {
+ instructions.add(new CfReturnVoid());
+ } else {
+ instructions.add(new CfReturn(ValueType.fromDexType(method.getReturnType())));
+ }
+ }
+
+ // Emit fallthrough.
+ instructions.add(fallthroughLabel);
+ instructions.add(createCfFrameForSwitchCase(method, maxLocals));
+
+ DexMethod fallthroughTarget =
+ lens.getNextMethodSignature(
+ superMethod != null ? superMethod : mappedMethods.get(mappedMethods.lastIntKey()));
+ instructions.add(
+ new CfInvoke(Opcodes.INVOKESPECIAL, fallthroughTarget, method.getHolder().isInterface()));
+
+ // Emit return.
+ if (method.getReturnType().isVoidType()) {
+ instructions.add(new CfReturnVoid());
+ } else {
+ instructions.add(new CfReturn(ValueType.fromDexType(method.getReturnType())));
+ }
+ return new CfCode(originalMethod.getHolderType(), maxStack, maxLocals, instructions) {
+
+ @Override
+ public GraphLens getCodeLens(AppView<?> appView) {
+ GraphLens graphLens =
+ appView
+ .graphLens()
+ .asNonIdentityLens()
+ .find(GraphLens::isHorizontalClassMergerGraphLens);
+ assert graphLens != null;
+ return graphLens;
+ }
+ };
+ }
+
+ private static CfFrame createCfFrameForSwitchCase(ProgramMethod representative, int localsSize) {
+ Deque<FrameType> stack =
+ new ArrayDeque<>(representative.getDefinition().getNumberOfArguments());
+ for (int argumentIndex = 0;
+ argumentIndex < representative.getDefinition().getNumberOfArguments();
+ argumentIndex++) {
+ stack.add(FrameType.initialized(representative.getArgumentType(argumentIndex)));
+ }
+ return new CfFrame(createLocalFrames(representative, localsSize), stack);
+ }
+
+ private static Int2ReferenceAVLTreeMap<FrameType> createLocalFrames(
+ ProgramMethod representative, int localsSize) {
+ Int2ReferenceAVLTreeMap<FrameType> locals = new Int2ReferenceAVLTreeMap<>();
+ for (int argumentIndex = 0, localIndex = 0;
+ argumentIndex < representative.getDefinition().getNumberOfArguments();
+ argumentIndex++) {
+ FrameType frameType = FrameType.initialized(representative.getArgumentType(argumentIndex));
+ locals.put(localIndex++, frameType);
+ if (frameType.isWide()) {
+ locals.put(localIndex++, frameType);
+ }
+ }
+ assert locals.size() == localsSize;
+ return locals;
+ }
+
+ // Implement Code.
+
+ @Override
+ public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+ throw new Unreachable();
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ throw new Unreachable();
+ }
+
+ @Override
+ protected int computeHashCode() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int estimatedDexCodeSizeUpperBoundInBytes() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean isEmptyVoidMethod() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String toString() {
+ return "IncompleteVirtuallyMergedMethodCode";
+ }
+
+ @Override
+ public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+ return toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index 2affeaf..7aa753c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -167,12 +167,7 @@
instructionBuilder.add(new CfReturnVoid());
return new HorizontalClassMergerCfCode(
- newMethodReference.getHolderType(),
- maxStack.get(),
- maxLocals,
- instructionBuilder.build(),
- ImmutableList.of(),
- ImmutableList.of());
+ newMethodReference.getHolderType(), maxStack.get(), maxLocals, instructionBuilder.build());
}
private static void addCfInstructionsForInstanceFieldAssignments(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPoint.java
deleted file mode 100644
index 494e383..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPoint.java
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.utils.IntBox;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Assuming a method signature <code>
- * void method([args]);
- * </code>. This class generates code depending on which of the following cases it matches.
- *
- * <p>If the method does not override a method and is implemented by many (e.g. 2) classes:
- *
- * <pre>
- * void method([args]) {
- * switch (classId) {
- * case 0:
- * return method$1([args]);
- * default:
- * return method$2([args]);
- * }
- * }
- * </pre>
- *
- * <p>If the method overrides a method and is implemented by any number of classes:
- *
- * <pre>
- * void method([args]) {
- * switch (classId) {
- * case 0:
- * return method$1([args]);
- * // ... further cases ...
- * default:
- * return super.method$1([args]);
- * }
- * }
- * </pre>
- */
-public class VirtualMethodEntryPoint extends SyntheticSourceCode {
- private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
- private final DexField classIdField;
- private final DexMethod superMethod;
-
- public VirtualMethodEntryPoint(
- Int2ReferenceSortedMap<DexMethod> mappedMethods,
- DexField classIdField,
- DexMethod superMethod,
- DexMethod newMethod,
- Position callerPosition,
- DexMethod originalMethod) {
- super(newMethod.holder, newMethod, callerPosition, originalMethod);
-
- assert classIdField != null;
-
- this.mappedMethods = mappedMethods;
- this.classIdField = classIdField;
- this.superMethod = superMethod;
- }
-
- void addInvokeDirect(DexMethod method) {
- add(
- builder -> {
- List<Value> arguments = new ArrayList<>(method.getArity() + 1);
- arguments.add(builder.getReceiverValue());
- if (builder.getArgumentValues() != null) {
- arguments.addAll(builder.getArgumentValues());
- }
- builder.addInvoke(Type.DIRECT, method, method.proto, arguments, false);
- });
- }
-
- void addInvokeSuper() {
- assert superMethod != null;
-
- add(
- builder -> {
- List<Value> arguments = new ArrayList<>(method.getArity() + 1);
- arguments.add(builder.getReceiverValue());
- if (builder.getArgumentValues() != null) {
- arguments.addAll(builder.getArgumentValues());
- }
- builder.addInvoke(Type.SUPER, superMethod, superMethod.proto, arguments, false);
- });
- }
-
- void handleReturn(int retRegister) {
- if (proto.returnType.isVoidType()) {
- add(IRBuilder::addReturn, endsBlock);
- } else {
- add(builder -> builder.addMoveResult(retRegister));
- add(builder -> builder.addReturn(retRegister), endsBlock);
- }
- }
-
- @Override
- protected void prepareInstructions() {
- int casesCount = mappedMethods.size();
-
- // If there is no super method, use one of the cases as a fallthrough case.
- if (superMethod == null) {
- casesCount--;
- }
-
- assert casesCount > 0;
-
- // Return value register if needed.
- int returnRegister =
- !proto.returnType.isVoidType() ? nextRegister(ValueType.fromDexType(proto.returnType)) : -1;
-
- int[] keys = new int[casesCount];
- int[] offsets = new int[casesCount];
- IntBox fallthrough = new IntBox();
-
- // Fetch the class id from the class id field.
- int idRegister = nextRegister(ValueType.INT);
- add(builder -> builder.addInstanceGet(idRegister, getReceiverRegister(), classIdField));
-
- int switchIndex = lastInstructionIndex();
- add(
- builder -> builder.addSwitch(idRegister, keys, fallthrough.get(), offsets),
- builder -> endsSwitch(builder, switchIndex, fallthrough.get(), offsets));
-
- int index = 0;
- for (Entry<DexMethod> entry : mappedMethods.int2ReferenceEntrySet()) {
- int classId = entry.getIntKey();
- DexMethod mappedMethod = entry.getValue();
-
- // If there is no super method, then use the last case as the default case.
- if (index >= casesCount) {
- fallthrough.set(nextInstructionIndex());
- } else {
- keys[index] = classId;
- offsets[index] = nextInstructionIndex();
- }
-
- addInvokeDirect(mappedMethod);
- handleReturn(returnRegister);
-
- index++;
- }
-
- // If the super class implements this method, then the fallthrough case should execute it.
- if (superMethod != null) {
- fallthrough.set(nextInstructionIndex());
- addInvokeSuper();
- handleReturn(returnRegister);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index b18a69c..07d1fd0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -10,13 +10,10 @@
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.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.code.VirtualMethodEntryPointSynthesizedCode;
-import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.structural.Ordered;
@@ -57,14 +54,13 @@
/** Get the super method handle if this method overrides a parent method. */
private DexMethod superMethod(
- AppView<? extends AppInfoWithClassHierarchy> appView, DexProgramClass target) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
DexMethod template = methods.iterator().next().getReference();
SingleResolutionResult resolutionResult =
appView
.appInfo()
- .resolveMethodOnClass(template, target.getSuperType())
+ .resolveMethodOnClass(template, group.getSuperType())
.asSingleResolution();
-
if (resolutionResult == null || resolutionResult.getResolvedMethod().isAbstract()) {
// If there is no super method or the method is abstract it should not be called.
return null;
@@ -74,7 +70,7 @@
return resolutionResult
.getResolvedMethod()
.getReference()
- .withHolder(target.getSuperType(), appView.dexItemFactory());
+ .withHolder(group.getSuperType(), appView.dexItemFactory());
}
return resolutionResult.getResolvedMethod().getReference();
}
@@ -82,8 +78,7 @@
public VirtualMethodMerger build(
AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
// If not all the classes are in the merge group, find the fallback super method to call.
- DexMethod superMethod =
- methods.size() < group.size() ? superMethod(appView, group.getTarget()) : null;
+ DexMethod superMethod = methods.size() < group.size() ? superMethod(appView, group) : null;
return new VirtualMethodMerger(appView, group, methods, superMethod);
}
}
@@ -252,7 +247,8 @@
}
DexMethod newMethod = moveMethod(classMethodsBuilder, method);
lensBuilder.recordNewMethodSignature(method.getReference(), newMethod);
- classIdToMethodMap.put(classIdentifiers.getInt(method.getHolderType()), newMethod);
+ classIdToMethodMap.put(
+ classIdentifiers.getInt(method.getHolderType()), method.getReference());
if (representative == null) {
representative = method;
}
@@ -271,14 +267,9 @@
classMethodsBuilder::isFresh);
DexEncodedMethod representativeMethod = representative.getDefinition();
DexMethod newMethodReference = getNewMethodReference();
- AbstractSynthesizedCode synthesizedCode =
- new VirtualMethodEntryPointSynthesizedCode(
- classIdToMethodMap,
- group.getClassIdField(),
- superMethod,
- newMethodReference,
- bridgeMethodReference,
- appView.dexItemFactory());
+ IncompleteVirtuallyMergedMethodCode synthesizedCode =
+ new IncompleteVirtuallyMergedMethodCode(
+ group.getClassIdField(), classIdToMethodMap, originalMethodReference, superMethod);
DexEncodedMethod newMethod =
DexEncodedMethod.syntheticBuilder()
.setMethod(newMethodReference)
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
deleted file mode 100644
index 7cfd3dc..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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.horizontalclassmerging.code;
-
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.horizontalclassmerging.VirtualMethodEntryPoint;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
-import java.util.function.Consumer;
-
-public class VirtualMethodEntryPointSynthesizedCode extends SynthesizedCode {
-
- private final DexItemFactory dexItemFactory;
- private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
- private final DexMethod superMethod;
-
- public VirtualMethodEntryPointSynthesizedCode(
- Int2ReferenceSortedMap<DexMethod> mappedMethods,
- DexField classIdField,
- DexMethod superMethod,
- DexMethod method,
- DexMethod originalMethod,
- DexItemFactory factory) {
- super(
- (context, position) ->
- new VirtualMethodEntryPoint(
- mappedMethods,
- classIdField,
- computeSuperMethodTarget(superMethod, context, factory),
- method,
- position,
- originalMethod));
- this.dexItemFactory = factory;
- this.mappedMethods = mappedMethods;
- this.superMethod = superMethod;
- }
-
- private static DexMethod computeSuperMethodTarget(
- DexMethod superMethod, ProgramMethod method, DexItemFactory factory) {
- // We are only using superMethod as a bit but if this is changed to generate CfCode directly,
- // the superMethod needs to be computed by mapping through the lens.
- if (superMethod == null) {
- return null;
- }
- return method.getReference().withHolder(method.getHolder().superType, factory);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method) {
- return registry -> registerReachableDefinitions(method, registry);
- }
-
- private void registerReachableDefinitions(DexClassAndMethod method, UseRegistry registry) {
- assert registry.getTraversalContinuation().shouldContinue();
- for (DexMethod mappedMethod : mappedMethods.values()) {
- registry.registerInvokeDirect(mappedMethod);
- if (registry.getTraversalContinuation().shouldBreak()) {
- return;
- }
- }
- DexMethod superMethodTarget =
- computeSuperMethodTarget(superMethod, method.asProgramMethod(), dexItemFactory);
- if (superMethodTarget != null) {
- registry.registerInvokeSuper(superMethodTarget);
- }
- }
-
- @Override
- public boolean isHorizontalClassMergingCode() {
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 29bee42..4d1cfc2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -435,7 +435,7 @@
return new IRBuilder(
method,
appView,
- appView.codeLens(),
+ method.getDefinition().getCode().getCodeLens(appView),
source,
origin,
lookupPrototypeChanges(appView, method),
@@ -459,14 +459,6 @@
return appView.graphLens().lookupPrototypeChangesForMethodDefinition(method.getReference());
}
- public static RewrittenPrototypeDescription lookupPrototypeChangesForInlinee(
- AppView<?> appView, ProgramMethod method, MethodProcessor methodProcessor) {
- if (methodProcessor.shouldApplyCodeRewritings(method)) {
- return appView.graphLens().lookupPrototypeChangesForMethodDefinition(method.getReference());
- }
- return RewrittenPrototypeDescription.none();
- }
-
private IRBuilder(
ProgramMethod method,
AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index ba22a02..613ab36 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -969,7 +969,7 @@
private Deque<GraphLensInterval> getUnappliedLenses(ProgramMethod method) {
Deque<GraphLensInterval> unappliedLenses = new ArrayDeque<>(8);
- GraphLens codeLens = appView.codeLens();
+ GraphLens codeLens = method.getDefinition().getCode().getCodeLens(appView);
GraphLens currentLens = appView.graphLens();
DexMethod currentMethod = method.getReference();
while (currentLens != codeLens) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 3aec49b..06f2d76 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -420,7 +420,7 @@
}
@Override
- protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+ public DexMethod getNextMethodSignature(DexMethod method) {
return newMethodSignatures.getRepresentativeValueOrDefault(
method, extraNewMethodSignatures.getRepresentativeValueOrDefault(method, method));
}
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index 00867f4..a980c52 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -100,6 +100,11 @@
}
@Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
DexMethod method, GraphLens codeLens) {
if (this == codeLens) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index b77fbd1..ebf61af 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -123,6 +123,11 @@
}
@Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
DexMethod method, GraphLens codeLens) {
if (this == codeLens) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index edba428..5065a75 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -138,6 +138,11 @@
return method;
}
+ @Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return method;
+ }
+
public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index e72be3d..8dd8cb0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -94,7 +94,7 @@
method -> {
DexMethod methodReferenceBeforeParameterRemoval = method.getReference();
DexMethod methodReferenceAfterParameterRemoval =
- graphLens.internalGetNextMethodSignature(methodReferenceBeforeParameterRemoval);
+ graphLens.getNextMethodSignature(methodReferenceBeforeParameterRemoval);
if (methodReferenceAfterParameterRemoval == methodReferenceBeforeParameterRemoval
&& !graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
return method;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index d67718a..958c85b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -98,8 +98,8 @@
}
@Override
- public DexMethod internalGetNextMethodSignature(DexMethod method) {
- return super.internalGetNextMethodSignature(method);
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return super.getNextMethodSignature(method);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index 6e6e88c..fdf81ab 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -87,7 +87,7 @@
if (graphLens != null) {
DexMethod rewrittenMethodSignature =
- graphLens.internalGetNextMethodSignature(method.getReference());
+ graphLens.getNextMethodSignature(method.getReference());
if (graphLens.hasPrototypeChanges(rewrittenMethodSignature)) {
assert !appView.appInfo().isNeverReprocessMethod(method);
postMethodProcessorBuilder.add(method, currentGraphLens);
@@ -195,7 +195,7 @@
ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
DexMethod rewrittenMethodReference =
- graphLens.internalGetNextMethodSignature(resolvedMethod.getReference());
+ graphLens.getNextMethodSignature(resolvedMethod.getReference());
if (rewrittenMethodReference != resolvedMethod.getReference()
|| graphLens.hasPrototypeChanges(rewrittenMethodReference)) {
markAffected();
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
index 9827d9a..d47ecd6 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -29,7 +29,7 @@
@Override
public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
- return bridgeToHoistedBridgeMap.getOrDefault(renamedMethod, renamedMethod);
+ return getNextMethodSignature(renamedMethod);
}
@Override
@@ -39,6 +39,11 @@
}
@Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return bridgeToHoistedBridgeMap.getOrDefault(method, method);
+ }
+
+ @Override
public DexType getOriginalType(DexType type) {
return getPrevious().getOriginalType(type);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
index 89d95d6..dc91a10 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
@@ -115,6 +115,11 @@
return newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
}
+ @Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ return newMethodSignatures.getOrDefault(method, method);
+ }
+
public static class Builder {
private final AppView<AppInfoWithLiveness> appView;
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 0ad5ad4..91f926c 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -2006,6 +2006,11 @@
}
@Override
+ public DexMethod getNextMethodSignature(DexMethod method) {
+ throw new Unreachable();
+ }
+
+ @Override
public MethodLookupResult lookupMethod(
DexMethod method, DexMethod context, Type type, GraphLens codeLens) {
// First look up the method using the existing graph lens (for example, the type will have