Merge commit 'ed1d94f032b04f833b5efdeaa7459e977c38c710' into dev-release
diff --git a/.gitignore b/.gitignore index b8c394a..90f5857 100644 --- a/.gitignore +++ b/.gitignore
@@ -87,6 +87,10 @@ third_party/dart-sdk.tar.gz third_party/ddmlib third_party/ddmlib.tar.gz +third_party/dependencies/ +third_party/dependencies.tar.gz +third_party/dependencies_new/ +third_party/dependencies_new.tar.gz third_party/desugar/desugar_*.tar.gz third_party/desugar/desugar_*/ third_party/framework
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java index 875a4fe..7e8904d 100644 --- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java +++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -1124,15 +1124,22 @@ protos.clear(); types.clear(); strings.clear(); + callSites.clear(); + methodHandles.clear(); indexedItemsReferencedFromClassesInTransaction.clear(); } public boolean isEmpty() { - return classes.isEmpty() && fields.isEmpty() && methods.isEmpty() && protos.isEmpty() - && types.isEmpty() && strings.isEmpty(); + return classes.isEmpty() + && fields.isEmpty() + && methods.isEmpty() + && protos.isEmpty() + && types.isEmpty() + && strings.isEmpty() + && callSites.isEmpty() + && methodHandles.isEmpty(); } - } /**
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java index ee86680..54fecaa 100644 --- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java +++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -236,6 +236,10 @@ return self(); } + public boolean isPromotedFromPrivateToPublic() { + return isDemoted(Constants.ACC_PRIVATE) && isPromoted(Constants.ACC_PUBLIC); + } + public boolean isPromotedToPublic() { return isPromoted(Constants.ACC_PUBLIC); } @@ -277,6 +281,10 @@ modifiedFlags &= ~flag; } + protected boolean isDemoted(int flag) { + return wasSet(flag) && !isSet(flag); + } + protected boolean isPromoted(int flag) { return !wasSet(flag) && isSet(flag); }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java index aff2b31..4eff317 100644 --- a/src/main/java/com/android/tools/r8/graph/AppView.java +++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -41,6 +41,7 @@ import com.android.tools.r8.shaking.AssumeInfoCollection; import com.android.tools.r8.shaking.KeepClassInfo; import com.android.tools.r8.shaking.KeepFieldInfo; +import com.android.tools.r8.shaking.KeepInfo; import com.android.tools.r8.shaking.KeepInfoCollection; import com.android.tools.r8.shaking.KeepMethodInfo; import com.android.tools.r8.shaking.LibraryModeledPredicate; @@ -679,6 +680,15 @@ return keepInfo; } + public KeepInfo<?, ?> getKeepInfo(ProgramDefinition definition) { + return definition + .getReference() + .apply( + clazz -> getKeepInfo(definition.asProgramClass()), + field -> getKeepInfo(definition.asProgramField()), + method -> getKeepInfo(definition.asProgramMethod())); + } + public KeepClassInfo getKeepInfo(DexProgramClass clazz) { return getKeepInfo().getClassInfo(clazz); }
diff --git a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java index 589dd76..a021545 100644 --- a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java +++ b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
@@ -5,6 +5,8 @@ package com.android.tools.r8.graph; import com.android.tools.r8.references.ClassReference; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; import java.util.function.Consumer; public interface ClassDefinition extends Definition { @@ -15,6 +17,11 @@ void forEachClassMethod(Consumer<? super DexClassAndMethod> consumer); + void forEachImmediateSuperClassMatching( + DexDefinitionSupplier definitions, + BiPredicate<? super DexType, ? super DexClass> predicate, + BiConsumer<? super DexType, ? super DexClass> consumer); + MethodCollection getMethodCollection(); ClassReference getClassReference();
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java index 1d7c8cf..30909c5 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -37,6 +37,7 @@ import java.util.ListIterator; import java.util.Set; import java.util.function.BiConsumer; +import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -803,6 +804,20 @@ } } + @Override + public void forEachImmediateSuperClassMatching( + DexDefinitionSupplier definitions, + BiPredicate<? super DexType, ? super DexClass> predicate, + BiConsumer<? super DexType, ? super DexClass> consumer) { + forEachImmediateSupertype( + supertype -> { + DexClass superclass = definitions.definitionFor(supertype); + if (predicate.test(supertype, superclass)) { + consumer.accept(supertype, superclass); + } + }); + } + public void forEachImmediateSupertype(Consumer<DexType> fn) { if (superType != null) { fn.accept(superType);
diff --git a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java index 521d6c7..8b9a38b 100644 --- a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java +++ b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
@@ -9,6 +9,7 @@ import static com.google.common.base.Predicates.alwaysTrue; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; @@ -32,8 +33,18 @@ public static ImmediateProgramSubtypingInfo create( AppView<? extends AppInfoWithClassHierarchy> appView) { + return internalCreate(appView, appView.appInfo().classes()); + } + + public static ImmediateProgramSubtypingInfo createWithDeterministicOrder( + AppView<? extends AppInfoWithClassHierarchy> appView) { + return internalCreate(appView, appView.appInfo().classesWithDeterministicOrder()); + } + + private static ImmediateProgramSubtypingInfo internalCreate( + AppView<? extends AppInfoWithClassHierarchy> appView, Collection<DexProgramClass> classes) { Map<DexProgramClass, List<DexProgramClass>> immediateSubtypes = new IdentityHashMap<>(); - for (DexProgramClass clazz : appView.appInfo().classes()) { + for (DexProgramClass clazz : classes) { clazz.forEachImmediateSupertype( supertype -> { DexProgramClass superclass = asProgramClassOrNull(appView.definitionFor(supertype));
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java index 24c8b0a..77d8fbd 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java +++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -372,7 +372,7 @@ DexEncodedMethod method = directMethods[i]; DexEncodedMethod newMethod = replacement.apply(method); assert newMethod != null; - if (method != newMethod) { + if (method != newMethod || !method.belongsToDirectPool()) { if (belongsToDirectPool(newMethod)) { directMethods[i] = newMethod; } else { @@ -404,7 +404,7 @@ for (int i = 0; i < virtualMethods.length; i++) { DexEncodedMethod method = virtualMethods[i]; DexEncodedMethod newMethod = replacement.apply(method); - if (method != newMethod) { + if (method != newMethod || !method.belongsToVirtualPool()) { if (belongsToVirtualPool(newMethod)) { virtualMethods[i] = newMethod; } else {
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 a31e669..47d8a02 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java +++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -269,6 +269,13 @@ backing.replaceMethods(replacement); } + @SuppressWarnings("unchecked") + public <T extends DexClassAndMethod> void replaceClassAndMethods( + Function<T, DexEncodedMethod> replacement) { + assert holder.isProgramClass(); + replaceMethods(method -> replacement.apply((T) DexClassAndMethod.create(holder, method))); + } + public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) { resetDirectMethodCaches(); backing.replaceDirectMethods(replacement);
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java b/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java index 8a4c7bd..ce17027 100644 --- a/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java +++ b/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ClasspathOrLibraryClass; import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexMethodSignature; import com.android.tools.r8.graph.DexProgramClass; @@ -17,33 +16,34 @@ import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph; +import com.android.tools.r8.optimize.utils.ConcurrentNonProgramMethodsCollection; +import com.android.tools.r8.optimize.utils.NonProgramMethodsCollection; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.KeepMethodInfo; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; -import com.android.tools.r8.utils.collections.DexMethodSignatureSet; +import com.android.tools.r8.utils.collections.DexMethodSignatureBiMap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; public class ConcurrentMethodFixup { private final AppView<AppInfoWithLiveness> appView; - private final Map<ClasspathOrLibraryClass, DexMethodSignatureSet> nonProgramVirtualMethods = - new ConcurrentHashMap<>(); + private final NonProgramMethodsCollection nonProgramVirtualMethods; private final ProgramClassFixer programClassFixer; public ConcurrentMethodFixup( AppView<AppInfoWithLiveness> appView, ProgramClassFixer programClassFixer) { this.appView = appView; + this.nonProgramVirtualMethods = + ConcurrentNonProgramMethodsCollection.createVirtualMethodsCollection(appView); this.programClassFixer = programClassFixer; } @@ -77,7 +77,8 @@ private void processConnectedProgramComponents(Set<DexProgramClass> classes) { List<DexProgramClass> sorted = new ArrayList<>(classes); sorted.sort(Comparator.comparing(DexClass::getType)); - BiMap<DexMethodSignature, DexMethodSignature> componentSignatures = HashBiMap.create(); + DexMethodSignatureBiMap<DexMethodSignature> componentSignatures = + new DexMethodSignatureBiMap<>(); // 1) Reserve all library overrides and pinned virtual methods. reserveComponentPinnedAndInterfaceMethodSignatures(sorted, componentSignatures); @@ -105,7 +106,7 @@ private void processClass( DexProgramClass clazz, Set<DexProgramClass> processedClasses, - BiMap<DexMethodSignature, DexMethodSignature> componentSignatures) { + DexMethodSignatureBiMap<DexMethodSignature> componentSignatures) { assert !clazz.isInterface(); if (!processedClasses.add(clazz)) { return; @@ -122,7 +123,7 @@ private void processInterface( DexProgramClass clazz, Set<DexProgramClass> processedInterfaces, - BiMap<DexMethodSignature, DexMethodSignature> componentSignatures) { + DexMethodSignatureBiMap<DexMethodSignature> componentSignatures) { assert clazz.isInterface(); if (!processedInterfaces.add(clazz)) { return; @@ -148,7 +149,7 @@ } private MethodNamingUtility createMethodNamingUtility( - BiMap<DexMethodSignature, DexMethodSignature> inheritedSignatures, DexProgramClass clazz) { + DexMethodSignatureBiMap<DexMethodSignature> componentSignatures, DexProgramClass clazz) { BiMap<DexMethod, DexMethod> localSignatures = HashBiMap.create(); clazz.forEachProgramInstanceInitializer( method -> { @@ -156,12 +157,12 @@ localSignatures.put(method.getReference(), method.getReference()); } }); - return new MethodNamingUtility(appView.dexItemFactory(), inheritedSignatures, localSignatures); + return new MethodNamingUtility(appView.dexItemFactory(), componentSignatures, localSignatures); } private void reserveComponentPinnedAndInterfaceMethodSignatures( List<DexProgramClass> stronglyConnectedProgramClasses, - BiMap<DexMethodSignature, DexMethodSignature> componentSignatures) { + DexMethodSignatureBiMap<DexMethodSignature> componentSignatures) { Set<ClasspathOrLibraryClass> seenNonProgramClasses = Sets.newIdentityHashSet(); for (DexProgramClass clazz : stronglyConnectedProgramClasses) { // If a private or static method is pinned, we need to reserve the mapping to avoid creating @@ -173,47 +174,16 @@ componentSignatures.put(method.getMethodSignature(), method.getMethodSignature()); } }); - clazz.forEachImmediateSupertype( - supertype -> { - DexClass superclass = appView.definitionFor(supertype); - if (superclass != null - && !superclass.isProgramClass() - && seenNonProgramClasses.add(superclass.asClasspathOrLibraryClass())) { - for (DexMethodSignature vMethod : - getOrComputeNonProgramVirtualMethods(superclass.asClasspathOrLibraryClass())) { - componentSignatures.put(vMethod, vMethod); - } - } - }); + clazz.forEachImmediateSuperClassMatching( + appView, + (supertype, superclass) -> + superclass != null + && !superclass.isProgramClass() + && seenNonProgramClasses.add(superclass.asClasspathOrLibraryClass()), + (supertype, superclass) -> + componentSignatures.putAllToIdentity( + nonProgramVirtualMethods.getOrComputeNonProgramMethods( + superclass.asClasspathOrLibraryClass()))); } } - - private DexMethodSignatureSet getOrComputeNonProgramVirtualMethods( - ClasspathOrLibraryClass clazz) { - DexMethodSignatureSet libraryMethodsOnClass = nonProgramVirtualMethods.get(clazz); - if (libraryMethodsOnClass != null) { - return libraryMethodsOnClass; - } - return computeNonProgramVirtualMethods(clazz); - } - - private DexMethodSignatureSet computeNonProgramVirtualMethods( - ClasspathOrLibraryClass classpathOrLibraryClass) { - DexClass clazz = classpathOrLibraryClass.asDexClass(); - DexMethodSignatureSet libraryMethodsOnClass = DexMethodSignatureSet.create(); - clazz.forEachImmediateSupertype( - supertype -> { - DexClass superclass = appView.definitionFor(supertype); - if (superclass != null) { - assert !superclass.isProgramClass(); - libraryMethodsOnClass.addAll( - getOrComputeNonProgramVirtualMethods(superclass.asClasspathOrLibraryClass())); - } - }); - clazz.forEachClassMethodMatching( - DexEncodedMethod::belongsToVirtualPool, - method -> libraryMethodsOnClass.add(method.getMethodSignature())); - nonProgramVirtualMethods.put(classpathOrLibraryClass, libraryMethodsOnClass); - return libraryMethodsOnClass; - } }
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/MethodNamingUtility.java b/src/main/java/com/android/tools/r8/graph/fixup/MethodNamingUtility.java index 977e01c..688853d 100644 --- a/src/main/java/com/android/tools/r8/graph/fixup/MethodNamingUtility.java +++ b/src/main/java/com/android/tools/r8/graph/fixup/MethodNamingUtility.java
@@ -10,18 +10,19 @@ import com.android.tools.r8.graph.DexMethodSignature; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.utils.collections.DexMethodSignatureBiMap; import com.google.common.collect.BiMap; import java.util.function.BiConsumer; public class MethodNamingUtility { private final DexItemFactory factory; - private final BiMap<DexMethodSignature, DexMethodSignature> inheritedSignatures; + private final DexMethodSignatureBiMap<DexMethodSignature> inheritedSignatures; private final BiMap<DexMethod, DexMethod> localSignatures; public MethodNamingUtility( DexItemFactory factory, - BiMap<DexMethodSignature, DexMethodSignature> inheritedSignatures, + DexMethodSignatureBiMap<DexMethodSignature> inheritedSignatures, BiMap<DexMethod, DexMethod> localSignatures) { this.factory = factory; this.inheritedSignatures = inheritedSignatures;
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java index a46b0a2..d72358a 100644 --- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java +++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -42,6 +42,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.collect.Streams; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -692,18 +694,28 @@ return false; } - private boolean consistentDefUseChains() { - Set<Value> values = Sets.newIdentityHashSet(); + private void addValueAndCheckUniqueNumber(Int2ReferenceMap<Value> values, Value value) { + assert value != null; + int number = value.getNumber(); + Value old = values.put(number, value); + assert options.testing.ignoreValueNumbering + || old == null + || old == value + || (number == -1 && value.isValueOnStack()) + : "Multiple value definitions with number " + number + ": " + value + " and " + old; + } + private boolean consistentDefUseChains() { + Int2ReferenceMap<Value> values = new Int2ReferenceOpenHashMap<>(); for (BasicBlock block : blocks) { int predecessorCount = block.getPredecessors().size(); // Check that all phi uses are consistent. for (Phi phi : block.getPhis()) { assert !phi.isTrivialPhi(); assert phi.getOperands().size() == predecessorCount; - values.add(phi); + addValueAndCheckUniqueNumber(values, phi); for (Value value : phi.getOperands()) { - values.add(value); + addValueAndCheckUniqueNumber(values, value); assert value.uniquePhiUsers().contains(phi); assert !phi.hasLocalInfo() || phi.getLocalInfo() == value.getLocalInfo(); assert value.isPhi() || value.definition.hasBlock(); @@ -713,21 +725,21 @@ assert instruction.getBlock() == block; Value outValue = instruction.outValue(); if (outValue != null) { - values.add(outValue); + addValueAndCheckUniqueNumber(values, outValue); assert outValue.definition == instruction; } for (Value value : instruction.inValues()) { - values.add(value); + addValueAndCheckUniqueNumber(values, value); assert value.uniqueUsers().contains(instruction); } for (Value value : instruction.getDebugValues()) { - values.add(value); + addValueAndCheckUniqueNumber(values, value); assert value.debugUsers().contains(instruction); } } } - for (Value value : values) { + for (Value value : values.values()) { assert verifyValue(value); assert consistentValueUses(value); }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java index 5560d5b..313f4d1 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java
@@ -9,7 +9,9 @@ import com.android.tools.r8.ir.conversion.callgraph.CallSiteInformation.CallGraphBasedCallSiteInformation; import com.android.tools.r8.ir.conversion.callgraph.CycleEliminator.CycleEliminationResult; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.collections.ProgramMethodSet; +import com.android.tools.r8.utils.collections.SortedProgramMethodSet; import com.google.common.collect.Sets; import java.util.Collection; import java.util.Iterator; @@ -77,7 +79,10 @@ } private ProgramMethodSet extractNodes(Predicate<Node> predicate, Consumer<Node> clean) { - ProgramMethodSet result = ProgramMethodSet.create(); + ProgramMethodSet result = + InternalOptions.DETERMINISTIC_DEBUGGING + ? SortedProgramMethodSet.create() + : ProgramMethodSet.create(); Set<Node> removed = Sets.newIdentityHashSet(); Iterator<Node> nodeIterator = nodes.values().iterator(); while (nodeIterator.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java index 93ad3ff..a0edfc5 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java
@@ -4,9 +4,11 @@ package com.android.tools.r8.ir.conversion.passes; +import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppInfo; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.Add; import com.android.tools.r8.ir.code.And; import com.android.tools.r8.ir.code.Binop; @@ -42,18 +44,17 @@ private Map<Class<?>, BinopDescriptor> createBinopDescriptors() { ImmutableMap.Builder<Class<?>, BinopDescriptor> builder = ImmutableMap.builder(); - builder.put(Add.class, new BinopDescriptor(0, 0, null, null)); - builder.put(Sub.class, new BinopDescriptor(null, 0, null, null)); - builder.put(Mul.class, new BinopDescriptor(1, 1, 0, 0)); - // The following two can be improved if we handle ZeroDivide. - builder.put(Div.class, new BinopDescriptor(null, 1, null, null)); - builder.put(Rem.class, new BinopDescriptor(null, null, null, null)); - builder.put(And.class, new BinopDescriptor(ALL_BITS_SET, ALL_BITS_SET, 0, 0)); - builder.put(Or.class, new BinopDescriptor(0, 0, ALL_BITS_SET, ALL_BITS_SET)); - builder.put(Xor.class, new BinopDescriptor(0, 0, null, null)); - builder.put(Shl.class, new BinopDescriptor(null, 0, 0, null)); - builder.put(Shr.class, new BinopDescriptor(null, 0, 0, null)); - builder.put(Ushr.class, new BinopDescriptor(null, 0, 0, null)); + builder.put(Add.class, BinopDescriptor.ADD); + builder.put(Sub.class, BinopDescriptor.SUB); + builder.put(Mul.class, BinopDescriptor.MUL); + builder.put(Div.class, BinopDescriptor.DIV); + builder.put(Rem.class, BinopDescriptor.REM); + builder.put(And.class, BinopDescriptor.AND); + builder.put(Or.class, BinopDescriptor.OR); + builder.put(Xor.class, BinopDescriptor.XOR); + builder.put(Shl.class, BinopDescriptor.SHL); + builder.put(Shr.class, BinopDescriptor.SHR); + builder.put(Ushr.class, BinopDescriptor.USHR); return builder.build(); } @@ -64,24 +65,176 @@ * - i is right identity if for each x in K, x * i = x. * - a is left absorbing if for each x in K, a * x = a. * - a is right absorbing if for each x in K, x * a = a. + * In a space K, a binop * is associative if for each x,y,z in K, (x * y) * z = x * (y * z). * </code> */ - private static class BinopDescriptor { + private enum BinopDescriptor { + ADD(0, 0, null, null, true) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return Add.create(numericType, dest, left, right); + } + + @Override + int evaluate(int left, int right) { + return left + right; + } + + @Override + long evaluate(long left, long right) { + return left + right; + } + }, + SUB(null, 0, null, null, false) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return new Sub(numericType, dest, left, right); + } + + @Override + int evaluate(int left, int right) { + return left - right; + } + + @Override + long evaluate(long left, long right) { + return left - right; + } + }, + MUL(1, 1, 0, 0, true) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return Mul.create(numericType, dest, left, right); + } + + @Override + int evaluate(int left, int right) { + return left * right; + } + + @Override + long evaluate(long left, long right) { + return left * right; + } + }, + // The following two can be improved if we handle ZeroDivide. + DIV(null, 1, null, null, false), + REM(null, null, null, null, false), + AND(ALL_BITS_SET, ALL_BITS_SET, 0, 0, true) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return And.create(numericType, dest, left, right); + } + + @Override + int evaluate(int left, int right) { + return left & right; + } + + @Override + long evaluate(long left, long right) { + return left & right; + } + }, + OR(0, 0, ALL_BITS_SET, ALL_BITS_SET, true) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return Or.create(numericType, dest, left, right); + } + + @Override + int evaluate(int left, int right) { + return left | right; + } + + @Override + long evaluate(long left, long right) { + return left | right; + } + }, + XOR(0, 0, null, null, true) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return Xor.create(numericType, dest, left, right); + } + + @Override + int evaluate(int left, int right) { + return left ^ right; + } + + @Override + long evaluate(long left, long right) { + return left ^ right; + } + }, + SHL(null, 0, 0, null, false) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return new Shl(numericType, dest, left, right); + } + + @Override + boolean isShift() { + return true; + } + }, + SHR(null, 0, 0, null, false) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return new Shr(numericType, dest, left, right); + } + + @Override + boolean isShift() { + return true; + } + }, + USHR(null, 0, 0, null, false) { + @Override + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + return new Ushr(numericType, dest, left, right); + } + + @Override + boolean isShift() { + return true; + } + }; final Integer leftIdentity; final Integer rightIdentity; final Integer leftAbsorbing; final Integer rightAbsorbing; + final boolean associativeAndCommutative; - private BinopDescriptor( + BinopDescriptor( Integer leftIdentity, Integer rightIdentity, Integer leftAbsorbing, - Integer rightAbsorbing) { + Integer rightAbsorbing, + boolean associativeAndCommutative) { this.leftIdentity = leftIdentity; this.rightIdentity = rightIdentity; this.leftAbsorbing = leftAbsorbing; this.rightAbsorbing = rightAbsorbing; + this.associativeAndCommutative = associativeAndCommutative; + } + + Binop instantiate(NumericType numericType, Value dest, Value left, Value right) { + throw new Unreachable(); + } + + int evaluate(int left, int right) { + throw new Unreachable(); + } + + long evaluate(long left, long right) { + throw new Unreachable(); + } + + boolean isShift() { + return false; } } @@ -106,30 +259,10 @@ || binop.getNumericType() == NumericType.LONG) { BinopDescriptor binopDescriptor = descriptors.get(binop.getClass()); assert binopDescriptor != null; - ConstNumber constNumber = getConstNumber(binop.leftValue()); - if (constNumber != null) { - if (simplify( - binop, - iterator, - constNumber, - binopDescriptor.leftIdentity, - binop.rightValue(), - binopDescriptor.leftAbsorbing, - binop.leftValue())) { - continue; - } + if (identityAbsorbingSimplification(iterator, binop, binopDescriptor)) { + continue; } - constNumber = getConstNumber(binop.rightValue()); - if (constNumber != null) { - simplify( - binop, - iterator, - constNumber, - binopDescriptor.rightIdentity, - binop.leftValue(), - binopDescriptor.rightAbsorbing, - binop.rightValue()); - } + successiveSimplification(iterator, binop, binopDescriptor, code); } } } @@ -137,6 +270,148 @@ assert code.isConsistentSSA(appView); } + private void successiveSimplification( + InstructionListIterator iterator, Binop binop, BinopDescriptor binopDescriptor, IRCode code) { + if (binop.outValue().hasDebugUsers()) { + return; + } + ConstNumber constBLeft = getConstNumber(binop.leftValue()); + ConstNumber constBRight = getConstNumber(binop.rightValue()); + if ((constBLeft != null && constBRight != null) + || (constBLeft == null && constBRight == null)) { + return; + } + Value otherValue = constBLeft == null ? binop.leftValue() : binop.rightValue(); + if (otherValue.isPhi() || !otherValue.getDefinition().isBinop()) { + return; + } + Binop prevBinop = otherValue.getDefinition().asBinop(); + ConstNumber constALeft = getConstNumber(prevBinop.leftValue()); + ConstNumber constARight = getConstNumber(prevBinop.rightValue()); + if ((constALeft != null && constARight != null) + || (constALeft == null && constARight == null)) { + return; + } + ConstNumber constB = constBLeft == null ? constBRight : constBLeft; + ConstNumber constA = constALeft == null ? constARight : constALeft; + Value input = constALeft == null ? prevBinop.leftValue() : prevBinop.rightValue(); + // We have two successive binops so that a,b constants, x the input and a * x * b. + if (prevBinop.getClass() == binop.getClass()) { + if (binopDescriptor.associativeAndCommutative) { + // a * x * b => x * (a * b) where (a * b) is a constant. + assert binop.isCommutative(); + Value newConst = addNewConstNumber(code, iterator, constB, constA, binopDescriptor); + replaceBinop(iterator, code, input, newConst, binopDescriptor); + } else if (binopDescriptor.isShift()) { + // x shift: a shift: b => x shift: (a + b) where a + b is a constant. + if (constBRight != null && constARight != null) { + Value newConst = addNewConstNumber(code, iterator, constB, constA, BinopDescriptor.ADD); + replaceBinop(iterator, code, input, newConst, binopDescriptor); + } + } else if (binop.isSub() && constBRight != null) { + // a - x - b => (a - b) - x where (a - b) is a constant. + // x - a - b => x - (a + b) where (a + b) is a constant. + // We ignore b - (x - a) and b - (a - x) with constBRight != null. + if (constARight == null) { + Value newConst = addNewConstNumber(code, iterator, constA, constB, BinopDescriptor.SUB); + replaceBinop(iterator, code, newConst, input, BinopDescriptor.SUB); + } else { + Value newConst = addNewConstNumber(code, iterator, constB, constA, BinopDescriptor.ADD); + replaceBinop(iterator, code, input, newConst, BinopDescriptor.SUB); + } + } + } else { + if (binop.isSub() && prevBinop.isAdd() && constBRight != null) { + // x + a - b => x + (a - b) where (a - b) is a constant. + // a + x - b => x + (a - b) where (a - b) is a constant. + // We ignore b - (x + a) and b - (a + x) with constBRight != null. + Value newConst = addNewConstNumber(code, iterator, constA, constB, BinopDescriptor.SUB); + replaceBinop(iterator, code, newConst, input, BinopDescriptor.ADD); + } else if (binop.isAdd() && prevBinop.isSub()) { + // x - a + b => x - (a - b) where (a - b) is a constant. + // a - x + b => (a + b) - x where (a + b) is a constant. + if (constALeft == null) { + Value newConst = addNewConstNumber(code, iterator, constA, constB, BinopDescriptor.SUB); + replaceBinop(iterator, code, input, newConst, BinopDescriptor.SUB); + } else { + Value newConst = addNewConstNumber(code, iterator, constB, constA, BinopDescriptor.ADD); + replaceBinop(iterator, code, newConst, input, BinopDescriptor.SUB); + } + } + } + } + + private void replaceBinop( + InstructionListIterator iterator, + IRCode code, + Value left, + Value right, + BinopDescriptor binopDescriptor) { + Binop newBinop = instantiateBinop(code, left, right, binopDescriptor); + iterator.replaceCurrentInstruction(newBinop); + // We need to reset the iterator state after replaceCurrentInstruction so that Iterator#remove() + // can work in identityAbsorbingSimplification by calling previous then next. + iterator.previous(); + iterator.next(); + identityAbsorbingSimplification(iterator, newBinop, binopDescriptor); + } + + private Binop instantiateBinop(IRCode code, Value left, Value right, BinopDescriptor descriptor) { + TypeElement representative = left.getType().isInt() ? right.getType() : left.getType(); + Value newValue = code.createValue(representative); + NumericType numericType = representative.isInt() ? NumericType.INT : NumericType.LONG; + return descriptor.instantiate(numericType, newValue, left, right); + } + + private Value addNewConstNumber( + IRCode code, + InstructionListIterator iterator, + ConstNumber left, + ConstNumber right, + BinopDescriptor descriptor) { + TypeElement representative = + left.outValue().getType().isInt() ? right.outValue().getType() : left.outValue().getType(); + long result = + representative.isInt() + ? descriptor.evaluate(left.getIntValue(), right.getIntValue()) + : descriptor.evaluate(left.getLongValue(), right.getLongValue()); + iterator.previous(); + Value value = + iterator.insertConstNumberInstruction( + code, appView.options(), result, left.outValue().getType()); + iterator.next(); + return value; + } + + private boolean identityAbsorbingSimplification( + InstructionListIterator iterator, Binop binop, BinopDescriptor binopDescriptor) { + ConstNumber constNumber = getConstNumber(binop.leftValue()); + if (constNumber != null) { + if (simplify( + binop, + iterator, + constNumber, + binopDescriptor.leftIdentity, + binop.rightValue(), + binopDescriptor.leftAbsorbing, + binop.leftValue())) { + return true; + } + } + constNumber = getConstNumber(binop.rightValue()); + if (constNumber != null) { + return simplify( + binop, + iterator, + constNumber, + binopDescriptor.rightIdentity, + binop.leftValue(), + binopDescriptor.rightAbsorbing, + binop.rightValue()); + } + return false; + } + private ConstNumber getConstNumber(Value val) { ConstNumber constNumber = getConstNumberIfConstant(val); if (constNumber != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java index 69d5822..088c331 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -80,7 +80,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import java.util.function.Function; final class InlineCandidateProcessor { @@ -695,8 +694,7 @@ private void removeFieldReadsFromNewInstance( IRCode code, Set<Value> affectedValues, AssumeRemover assumeRemover) { - TreeSet<InstanceGet> uniqueInstanceGetUsersWithDeterministicOrder = - new TreeSet<>(Comparator.comparingInt(x -> x.outValue().getNumber())); + List<InstanceGet> uniqueInstanceGetUsersWithDeterministicOrder = new ArrayList<>(); for (Instruction user : eligibleInstance.uniqueUsers()) { if (user.isInstanceGet()) { assumeRemover.markAssumeDynamicTypeUsersForRemoval(user.outValue()); @@ -722,6 +720,7 @@ + user); } + uniqueInstanceGetUsersWithDeterministicOrder.sort(Comparator.comparing(Instruction::outValue)); Map<DexField, FieldValueHelper> fieldHelpers = new IdentityHashMap<>(); for (InstanceGet user : uniqueInstanceGetUsersWithDeterministicOrder) { // Replace a field read with appropriate value.
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java index 5a71db9..da66dfd 100644 --- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java +++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind; import com.android.tools.r8.naming.mappinginformation.MappingInformation; import com.android.tools.r8.naming.mappinginformation.OutlineCallsiteMappingInformation; +import com.android.tools.r8.naming.mappinginformation.OutlineMappingInformation; import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation; import com.android.tools.r8.utils.ChainableStringConsumer; import com.android.tools.r8.utils.CollectionUtils; @@ -632,6 +633,15 @@ MappingInformation::asRewriteFrameMappingInformation); } + public OutlineMappingInformation getOutlineMappingInformation() { + List<OutlineMappingInformation> outlineMappingInformation = + filter( + MappingInformation::isOutlineMappingInformation, + MappingInformation::asOutlineMappingInformation); + assert outlineMappingInformation.size() <= 1; + return outlineMappingInformation.isEmpty() ? null : outlineMappingInformation.get(0); + } + public int getOriginalLineNumber(int lineNumberAfterMinification) { if (minifiedRange == null) { // General mapping without concrete line numbers: "a() -> b" @@ -659,6 +669,10 @@ } } + public Range getOriginalRangeOrIdentity() { + return originalRange != null ? originalRange : minifiedRange; + } + @Override public Signature getOriginalSignature() { return signature; @@ -761,6 +775,11 @@ return Collections.unmodifiableList(additionalMappingInformation); } + public void setAdditionalMappingInformationInternal( + List<MappingInformation> mappingInformation) { + this.additionalMappingInformation = mappingInformation; + } + public MappedRange partitionOnMinifiedRange(Range minifiedRange) { if (minifiedRange.equals(this.minifiedRange)) { return this; @@ -776,5 +795,15 @@ } return splitMappedRange; } + + public boolean isOriginalRangePreamble() { + return originalRange != null && originalRange.isPreamble(); + } + + public MappedRange withMinifiedRange(Range newMinifiedRange) { + return newMinifiedRange.equals(minifiedRange) + ? this + : new MappedRange(newMinifiedRange, signature, originalRange, renamedName); + } } }
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java index 22cf5e4..d87e036 100644 --- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java +++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -27,6 +27,7 @@ import com.android.tools.r8.utils.Box; import com.android.tools.r8.utils.ChainableStringConsumer; import com.android.tools.r8.utils.ConsumerUtils; +import com.android.tools.r8.utils.IntBox; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.Pair; @@ -42,12 +43,14 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.TreeMap; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class ComposingBuilder { @@ -408,7 +411,7 @@ private static MappedRangeOriginalToMinifiedMap build(List<MappedRange> mappedRanges) { Int2ReferenceMap<List<Integer>> positionMap = new Int2ReferenceOpenHashMap<>(); for (MappedRange mappedRange : mappedRanges) { - Range originalRange = mappedRange.originalRange; + Range originalRange = mappedRange.getOriginalRangeOrIdentity(); for (int position = originalRange.from; position <= originalRange.to; position++) { // It is perfectly fine to have multiple minified ranges mapping to the same source, we // just need to keep the additional information. @@ -420,11 +423,6 @@ return new MappedRangeOriginalToMinifiedMap(positionMap); } - public int lookupFirst(int originalPosition) { - List<Integer> minifiedPositions = originalToMinified.get(originalPosition); - return minifiedPositions == null ? 0 : minifiedPositions.get(0); - } - public void visitMinified(int originalPosition, Consumer<Integer> consumer) { List<Integer> minifiedPositions = originalToMinified.get(originalPosition); if (minifiedPositions != null) { @@ -608,7 +606,7 @@ listSegmentTree.findEntry(firstPositionOfOriginalRange); if (existingEntry == null && firstPositionOfOriginalRange == 0 - && !mappedRange.originalRange.isPreamble()) { + && !mappedRange.isOriginalRangePreamble()) { existingEntry = listSegmentTree.findEntry(mappedRange.getLastPositionOfOriginalRange()); } @@ -619,8 +617,7 @@ } else { // The original can be discarded if it no longer exists or if the method is // non-throwing. - if (mappedRange.originalRange != null - && !mappedRange.originalRange.isPreamble() + if (!mappedRange.isOriginalRangePreamble() && !options.mappingComposeOptions().allowNonExistingOriginalRanges) { throw new MappingComposeException( "Could not find original starting position of '" @@ -629,7 +626,7 @@ + firstPositionOfOriginalRange); } } - assert minified.hasValue(); + assert minified.hasValue() || mappedRange.getOriginalRangeOrIdentity() == null; } else { MappedRange existingMappedRange = existingClassBuilder.methodsWithoutPosition.get(signature); @@ -646,7 +643,10 @@ if (composedInlineFrames.isEmpty()) { splitOnNewMinifiedRange( composeMappedRangesForMethod( - existingMappedRanges, mappedRange, computedOutlineInformation), + existingClassBuilder, + existingMappedRanges, + mappedRange, + computedOutlineInformation), Collections.emptyList(), newComposedInlineFrames::add); } else { @@ -655,7 +655,10 @@ mappedRange.partitionOnMinifiedRange(composedInlineFrame.get(0).minifiedRange); splitOnNewMinifiedRange( composeMappedRangesForMethod( - existingMappedRanges, splitMappedRange, computedOutlineInformation), + existingClassBuilder, + existingMappedRanges, + splitMappedRange, + computedOutlineInformation), composedInlineFrame, newComposedInlineFrames::add); } @@ -668,42 +671,195 @@ composedInlineFrames = Collections.emptyList(); } } - MappedRange lastComposedRange = ListUtils.last(composedRanges); - if (computedOutlineInformation.seenOutlineMappingInformation != null) { + // Check if we could have inlined an outline which is true if we see both an outline and + // call site to patch up. + if (!computedOutlineInformation.seenOutlineMappingInformation.isEmpty() + && !computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp.isEmpty()) { + Set<OutlineCallsiteMappingInformation> outlineCallSitesToRemove = + Sets.newIdentityHashSet(); + Set<OutlineMappingInformation> outlinesToRemove = Sets.newIdentityHashSet(); + // We patch up all ranges from top to bottom with the invariant that all at a given + // index, all above have been updated correctly. We will do expansion of frames + // when separating out single minified lines, but we keep the outline information + // present such that we can fix them when seeing them later. + int composedRangeIndex = 0; + while (composedRangeIndex < composedRanges.size() - 1) { + MappedRange outline = composedRanges.get(composedRangeIndex++); + if (outline.isOutlineFrame() + && outline.minifiedRange.equals( + composedRanges.get(composedRangeIndex).minifiedRange)) { + // We should replace the inlined outline frame positions with the synthesized + // positions from the outline call site. + MappedRange outlineCallSite = composedRanges.get(composedRangeIndex); + if (outlineCallSite.getOutlineCallsiteInformation().size() != 1) { + // If we have an inlined outline it must be such that the outer frame is an + // outline callsite. + throw new MappingComposeException( + "Expected exactly one outline call site for a mapped range with signature '" + + outlineCallSite.getOriginalSignature() + + "'."); + } + OutlineCallsiteMappingInformation outlineCallSiteInformation = + outlineCallSite.getOutlineCallsiteInformation().get(0); + // The original positions in the outline callsite have been composed, so we have to + // find the existing mapped range and iterate the original positions for that range. + ComputedMappedRangeForOutline computedInformationForCallSite = + computedOutlineInformation.getComputedRange( + outlineCallSiteInformation, outlineCallSite); + if (computedInformationForCallSite == null) { + continue; + } + Map<Integer, List<MappedRange>> mappedRangesForOutline = + new HashMap<>(outlineCallSiteInformation.getPositions().size()); + visitOutlineMappedPositions( + outlineCallSiteInformation, + computedInformationForCallSite + .current + .getOriginalSignature() + .asMethodSignature(), + mappedRangesForOutline::put); + List<MappedRange> newComposedRanges = new ArrayList<>(); + // Copy all previous handled mapped ranges into a new list. + for (MappedRange previousMappedRanges : composedRanges) { + if (previousMappedRanges == outline) { + break; + } + newComposedRanges.add(previousMappedRanges); + } + // The original positions in the outline have been composed, so we have to find the + // existing mapped range and iterate the original positions for that range. + ComputedMappedRangeForOutline computedInformationForOutline = + computedOutlineInformation.getComputedRange( + outline.getOutlineMappingInformation(), outline); + if (computedInformationForOutline == null) { + continue; + } + // The outline could have additional inlined positions in it, but we should be + // guaranteed to find call site information on all original line numbers. We + // therefore iterate one by one and amend the subsequent outer frames as well. + MappedRange current = computedInformationForOutline.current; + int minifiedLine = outline.minifiedRange.from; + for (int originalLine = current.getOriginalRangeOrIdentity().from; + originalLine <= current.getOriginalRangeOrIdentity().to; + originalLine++) { + // If the outline is itself an inline frame it is bound to only have one original + // position and we can simply insert all inline frames on that position with the + // existing minified range. + Range newMinifiedRange = + outline.originalRange.isCardinal + ? outline.minifiedRange + : new Range(minifiedLine, minifiedLine); + List<MappedRange> outlineMappedRanges = mappedRangesForOutline.get(originalLine); + if (outlineMappedRanges != null) { + outlineMappedRanges.forEach( + range -> { + if (range != ListUtils.last(outlineMappedRanges)) { + newComposedRanges.add( + new MappedRange( + newMinifiedRange, + range.getOriginalSignature().asMethodSignature(), + range.originalRange, + outlineCallSite.getRenamedName())); + } + }); + newComposedRanges.add( + new MappedRange( + newMinifiedRange, + outlineCallSite.getOriginalSignature().asMethodSignature(), + ListUtils.last(outlineMappedRanges).originalRange, + outlineCallSite.getRenamedName())); + } + for (int tailInlineFrameIndex = composedRangeIndex + 1; + tailInlineFrameIndex < composedRanges.size(); + tailInlineFrameIndex++) { + MappedRange originalMappedRange = composedRanges.get(tailInlineFrameIndex); + if (!originalMappedRange.minifiedRange.equals(outlineCallSite.minifiedRange)) { + break; + } + MappedRange newMappedRange = + originalMappedRange.withMinifiedRange(newMinifiedRange); + newMappedRange.setAdditionalMappingInformationInternal( + originalMappedRange.getAdditionalMappingInformation()); + newComposedRanges.add(newMappedRange); + } + minifiedLine++; + } + // We have patched up the the inlined outline and all subsequent inline frames + // (although some of the subsequent frames above could also be inlined outlines). We + // therefore need to copy the remaining frames. + boolean seenMinifiedRange = false; + for (MappedRange range : composedRanges) { + if (range.minifiedRange.equals(outline.minifiedRange)) { + seenMinifiedRange = true; + } else if (seenMinifiedRange) { + newComposedRanges.add(range); + } + } + composedRanges = newComposedRanges; + outlineCallSitesToRemove.add(outlineCallSiteInformation); + outlinesToRemove.add(outline.getOutlineMappingInformation()); + } + } + // If we removed any outlines or call site, remove the processing of them. + outlineCallSitesToRemove.forEach( + computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp::remove); + outlinesToRemove.forEach( + computedOutlineInformation.seenOutlineMappingInformation::remove); + } + if (!computedOutlineInformation.seenOutlineMappingInformation.isEmpty()) { + MappedRange lastComposedRange = ListUtils.last(composedRanges); current .getUpdateOutlineCallsiteInformation( committedPreviousClassBuilder.getRenamedName(), ListUtils.last(newMappedRanges).signature.getName(), lastComposedRange.getRenamedName()) .setNewMappedRanges(newMappedRanges); - lastComposedRange.addMappingInformation( - computedOutlineInformation.seenOutlineMappingInformation, - ConsumerUtils.emptyConsumer()); } if (!computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp.isEmpty()) { - MappedRangeOriginalToMinifiedMap originalToMinifiedMap = - MappedRangeOriginalToMinifiedMap.build(newMappedRanges); + MappedRange lastComposedRange = ListUtils.last(composedRanges); + List<MappedRange> composedRangesFinal = composedRanges; + // Outline positions are synthetic positions and they have no position in the residual + // program. We therefore have to find the original positions and copy all inline frames + // and amend the outermost frame with the residual signature and the next free position. List<OutlineCallsiteMappingInformation> outlineCallSites = new ArrayList<>( - computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp); + computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp.keySet()); outlineCallSites.sort(Comparator.comparing(mapping -> mapping.getOutline().toString())); + IntBox firstAvailableRange = new IntBox(lastComposedRange.minifiedRange.to + 1); for (OutlineCallsiteMappingInformation outlineCallSite : outlineCallSites) { - Int2IntSortedMap positionMap = outlineCallSite.getPositions(); - for (Integer keyPosition : positionMap.keySet()) { - int keyPositionInt = keyPosition; - int originalDestination = positionMap.get(keyPositionInt); - int newDestination = originalToMinifiedMap.lookupFirst(originalDestination); - positionMap.put(keyPositionInt, newDestination); - } - lastComposedRange.addMappingInformation( - outlineCallSite, ConsumerUtils.emptyConsumer()); + Int2IntSortedMap newPositionMap = + new Int2IntLinkedOpenHashMap(outlineCallSite.getPositions().size()); + visitOutlineMappedPositions( + outlineCallSite, + memberNaming.getOriginalSignature().asMethodSignature(), + (originalPosition, mappedRangesForOutlinePosition) -> { + int newIndex = firstAvailableRange.getAndIncrement(); + Range newMinifiedRange = new Range(newIndex, newIndex); + MappedRange outerMostOutlineFrame = + ListUtils.last(mappedRangesForOutlinePosition); + for (MappedRange inlineMappedRangeInOutlinePosition : + mappedRangesForOutlinePosition) { + if (inlineMappedRangeInOutlinePosition != outerMostOutlineFrame) { + composedRangesFinal.add( + inlineMappedRangeInOutlinePosition.withMinifiedRange(newMinifiedRange)); + } + } + composedRangesFinal.add( + new MappedRange( + newMinifiedRange, + lastComposedRange.signature, + outerMostOutlineFrame.originalRange, + lastComposedRange.getRenamedName())); + newPositionMap.put((int) originalPosition, newIndex); + outlineCallSite.setPositionsInternal(newPositionMap); + }); } } MethodSignature residualSignature = memberNaming .computeResidualSignature(type -> inverseClassMapping.getOrDefault(type, type)) .asMethodSignature(); - if (lastComposedRange.minifiedRange != null) { + if (ListUtils.last(composedRanges).minifiedRange != null) { SegmentTree<List<MappedRange>> listSegmentTree = methodsWithPosition.computeIfAbsent( residualSignature, ignored -> new SegmentTree<>(false)); @@ -711,12 +867,57 @@ minified.getStartOrNoRangeFrom(), minified.getEndOrNoRangeFrom(), composedRanges); } else { assert composedRanges.size() == 1; - methodsWithoutPosition.put(residualSignature, lastComposedRange); + methodsWithoutPosition.put(residualSignature, ListUtils.last(composedRanges)); } } } } + private void visitOutlineMappedPositions( + OutlineCallsiteMappingInformation outlineCallSite, + MethodSignature originalSignature, + BiConsumer<Integer, List<MappedRange>> outlinePositionConsumer) + throws MappingComposeException { + Int2IntSortedMap positionMap = outlineCallSite.getPositions(); + ComposingClassBuilder existingClassBuilder = getExistingClassBuilder(originalSignature); + if (existingClassBuilder == null) { + throw new MappingComposeException( + "Could not find builder with original signature '" + originalSignature + "'."); + } + SegmentTree<List<MappedRange>> outlineSegmentTree = + existingClassBuilder.methodsWithPosition.get( + originalSignature.toUnqualifiedSignatureIfQualified().asMethodSignature()); + if (outlineSegmentTree == null) { + throw new MappingComposeException( + "Could not find method positions for original signature '" + originalSignature + "'."); + } + for (Integer keyPosition : positionMap.keySet()) { + int keyPositionInt = keyPosition; + int originalDestination = positionMap.get(keyPositionInt); + List<MappedRange> mappedRanges = outlineSegmentTree.find(originalDestination); + if (mappedRanges == null) { + throw new MappingComposeException( + "Could not find ranges for outline position '" + + keyPosition + + "' with original signature '" + + originalSignature + + "'."); + } + ExistingMapping existingMapping = computeExistingMapping(mappedRanges); + List<MappedRange> mappedRangesForOutlinePosition = + existingMapping.getMappedRangesForPosition(originalDestination); + if (mappedRangesForOutlinePosition == null) { + throw new MappingComposeException( + "Could not find ranges for outline position '" + + keyPosition + + "' with original signature '" + + originalSignature + + "'."); + } + outlinePositionConsumer.accept(keyPositionInt, mappedRangesForOutlinePosition); + } + } + private void splitOnNewMinifiedRange( List<MappedRange> mappedRanges, List<MappedRange> previouslyMapped, @@ -784,6 +985,7 @@ } private List<MappedRange> composeMappedRangesForMethod( + ComposingClassBuilder existingClassBuilder, List<MappedRange> existingRanges, MappedRange newRange, ComputedOutlineInformation computedOutlineInformation) @@ -793,10 +995,16 @@ return Collections.singletonList(newRange); } MappedRange lastExistingRange = ListUtils.last(existingRanges); - if (newRange.originalRange == null) { + if (newRange.getOriginalRangeOrIdentity() == null) { MappedRange newComposedRange = new MappedRange( - newRange.minifiedRange, lastExistingRange.signature, null, newRange.renamedName); + newRange.minifiedRange, + potentiallyQualifySignature( + newRange.signature, + lastExistingRange.signature, + existingClassBuilder.getOriginalName()), + null, + newRange.renamedName); composeMappingInformation( newComposedRange.getAdditionalMappingInformation(), lastExistingRange.getAdditionalMappingInformation(), @@ -815,18 +1023,21 @@ if (existingMappedRanges == null) { // If we cannot lookup the original position because it has been removed we compose with // the existing method signature. - if (newRange.originalRange.isPreamble() + if (newRange.isOriginalRangePreamble() || (existingRanges.size() == 1 && lastExistingRange.minifiedRange == null)) { return Collections.singletonList( new MappedRange( newRange.minifiedRange, - lastExistingRange.signature, + potentiallyQualifySignature( + newRange.signature, + lastExistingRange.signature, + existingClassBuilder.getOriginalName()), lastExistingRange.originalRange != null && lastExistingRange.originalRange.span() == 1 ? lastExistingRange.originalRange : EMPTY_RANGE, newRange.renamedName)); - } else if (newRange.originalRange.from == 0) { + } else if (newRange.getOriginalRangeOrIdentity().from == 0) { // Similar to the trick below we create a synthetic range to map the preamble to. Pair<Integer, MappedRange> emptyRange = createEmptyRange( @@ -842,8 +1053,9 @@ assert lastExistingMappedRange != null; // If the existing mapped minified range is equal to the original range of the new range // then we have a perfect mapping that we can translate directly. - if (lastExistingMappedRange.minifiedRange.equals(newRange.originalRange)) { + if (lastExistingMappedRange.minifiedRange.equals(newRange.getOriginalRangeOrIdentity())) { computeComposedMappedRange( + existingClassBuilder, newComposedRanges, newRange, existingMappedRanges, @@ -869,6 +1081,7 @@ lastExistingMappedRangeForPosition.minifiedRange)) { // We have seen an existing range we have to compute a splitting for. computeComposedMappedRange( + existingClassBuilder, newComposedRanges, newRange, existingMappedRanges, @@ -911,6 +1124,7 @@ } } computeComposedMappedRange( + existingClassBuilder, newComposedRanges, newRange, existingMappedRanges, @@ -1000,6 +1214,7 @@ } private void computeComposedMappedRange( + ComposingClassBuilder existingClassBuilder, List<MappedRange> newComposedRanges, MappedRange newMappedRange, List<MappedRange> existingMappedRanges, @@ -1010,9 +1225,9 @@ Range existingRange = existingMappedRanges.get(0).minifiedRange; assert existingMappedRanges.stream().allMatch(x -> x.minifiedRange.equals(existingRange)); Range newMinifiedRange = new Range(lastStartingMinifiedFrom, position); - boolean copyOriginalRange = existingRange.equals(newMappedRange.originalRange); + boolean copyOriginalRange = existingRange.equals(newMappedRange.getOriginalRangeOrIdentity()); for (MappedRange existingMappedRange : existingMappedRanges) { - Range existingOriginalRange = existingMappedRange.originalRange; + Range existingOriginalRange = existingMappedRange.getOriginalRangeOrIdentity(); Range newOriginalRange; if (copyOriginalRange || existingOriginalRange == null @@ -1022,7 +1237,7 @@ // Find the window that the new range points to into the original range. int existingMinifiedPos = newMappedRange.getOriginalLineNumber(lastStartingMinifiedFrom); int newOriginalStart = existingMappedRange.getOriginalLineNumber(existingMinifiedPos); - if (newMappedRange.originalRange.span() == 1) { + if (newMappedRange.getOriginalRangeOrIdentity().span() == 1) { newOriginalRange = new Range(newOriginalStart, newOriginalStart); } else { assert newMinifiedRange.span() <= existingOriginalRange.span(); @@ -1033,7 +1248,10 @@ MappedRange computedRange = new MappedRange( newMinifiedRange, - existingMappedRange.signature, + potentiallyQualifySignature( + newMappedRange.signature, + existingMappedRange.signature, + existingClassBuilder.getOriginalName()), newOriginalRange, newMappedRange.renamedName); List<MappingInformation> mappingInformationToCompose = new ArrayList<>(); @@ -1042,14 +1260,19 @@ .forEach( info -> { if (info.isOutlineMappingInformation()) { - computedOutlineInformation.seenOutlineMappingInformation = - info.asOutlineMappingInformation(); + computedOutlineInformation + .seenOutlineMappingInformation + .computeIfAbsent( + info.asOutlineMappingInformation(), ignoreArgument(ArrayList::new)) + .add(new ComputedMappedRangeForOutline(newMappedRange, computedRange)); } else if (info.isOutlineCallsiteInformation()) { - computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp.add( - info.asOutlineCallsiteInformation()); - } else { - mappingInformationToCompose.add(info); + computedOutlineInformation + .outlineCallsiteMappingInformationToPatchUp + .computeIfAbsent( + info.asOutlineCallsiteInformation(), ignoreArgument(ArrayList::new)) + .add(new ComputedMappedRangeForOutline(newMappedRange, computedRange)); } + mappingInformationToCompose.add(info); }); composeMappingInformation( computedRange.getAdditionalMappingInformation(), @@ -1190,6 +1413,14 @@ } } + private MethodSignature potentiallyQualifySignature( + MethodSignature newSignature, MethodSignature signature, String originalHolder) { + return !newSignature.isQualified() || signature.isQualified() + ? signature + : new MethodSignature( + originalHolder + "." + signature.name, signature.type, signature.parameters); + } + private static class RangeBuilder { private int start = Integer.MAX_VALUE; @@ -1217,9 +1448,35 @@ } private static class ComputedOutlineInformation { - private final Set<OutlineCallsiteMappingInformation> - outlineCallsiteMappingInformationToPatchUp = Sets.newIdentityHashSet(); - private OutlineMappingInformation seenOutlineMappingInformation = null; + private final Map<OutlineCallsiteMappingInformation, List<ComputedMappedRangeForOutline>> + outlineCallsiteMappingInformationToPatchUp = new IdentityHashMap<>(); + private final Map<OutlineMappingInformation, List<ComputedMappedRangeForOutline>> + seenOutlineMappingInformation = new IdentityHashMap<>(); + + private ComputedMappedRangeForOutline getComputedRange( + MappingInformation outline, MappedRange current) { + List<ComputedMappedRangeForOutline> outlineMappingInformations = + outline.isOutlineMappingInformation() + ? seenOutlineMappingInformation.get(outline.asOutlineMappingInformation()) + : outlineCallsiteMappingInformationToPatchUp.get( + outline.asOutlineCallsiteInformation()); + if (outlineMappingInformations == null) { + return null; + } + return ListUtils.firstMatching( + outlineMappingInformations, + pair -> pair.composed.minifiedRange.contains(current.minifiedRange.from)); + } + } + + private static class ComputedMappedRangeForOutline { + private final MappedRange current; + private final MappedRange composed; + + private ComputedMappedRangeForOutline(MappedRange current, MappedRange composed) { + this.current = current; + this.composed = composed; + } } } }
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java index d1bfb78..5ddf11e 100644 --- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java +++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -510,6 +510,10 @@ return new MethodSignature(toUnqualifiedName(), type, parameters); } + public Signature toUnqualifiedSignatureIfQualified() { + return isQualified() ? new MethodSignature(toUnqualifiedName(), type, parameters) : this; + } + @Override public Signature toQualifiedSignature(String holder) { return new MethodSignature(holder + "." + name, type, parameters);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java index 1a2eaa6..94a7d22 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.contexts.CompilationContext.ProcessorContext; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; @@ -36,6 +35,8 @@ import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo; import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorGraphLens.Builder; import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils; +import com.android.tools.r8.optimize.utils.ConcurrentNonProgramMethodsCollection; +import com.android.tools.r8.optimize.utils.NonProgramMethodsCollection; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.KeepFieldInfo; import com.android.tools.r8.shaking.KeepMethodInfo; @@ -73,7 +74,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; @@ -173,9 +173,7 @@ private final AppView<AppInfoWithLiveness> appView; private final ImmediateProgramSubtypingInfo immediateSubtypingInfo; private final Map<Set<DexProgramClass>, DexMethodSignatureSet> interfaceDispatchOutsideProgram; - - private final Map<DexClass, DexMethodSignatureSet> libraryVirtualMethods = - new ConcurrentHashMap<>(); + private final NonProgramMethodsCollection nonProgramMethodsCollection; public ArgumentPropagatorProgramOptimizer( AppView<AppInfoWithLiveness> appView, @@ -184,6 +182,8 @@ this.appView = appView; this.immediateSubtypingInfo = immediateSubtypingInfo; this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram; + this.nonProgramMethodsCollection = + ConcurrentNonProgramMethodsCollection.createVirtualMethodsCollection(appView); } public ArgumentPropagatorGraphLens run( @@ -221,28 +221,6 @@ return graphLens; } - private DexMethodSignatureSet getOrComputeLibraryVirtualMethods(DexClass clazz) { - DexMethodSignatureSet libraryMethodsOnClass = libraryVirtualMethods.get(clazz); - if (libraryMethodsOnClass != null) { - return libraryMethodsOnClass; - } - return computeLibraryVirtualMethods(clazz); - } - - private DexMethodSignatureSet computeLibraryVirtualMethods(DexClass clazz) { - DexMethodSignatureSet libraryMethodsOnClass = DexMethodSignatureSet.create(); - immediateSubtypingInfo.forEachImmediateSuperClassMatching( - clazz, - (supertype, superclass) -> superclass != null, - (supertype, superclass) -> - libraryMethodsOnClass.addAll(getOrComputeLibraryVirtualMethods(superclass))); - clazz.forEachClassMethodMatching( - DexEncodedMethod::belongsToVirtualPool, - method -> libraryMethodsOnClass.add(method.getMethodSignature())); - libraryVirtualMethods.put(clazz, libraryMethodsOnClass); - return libraryMethodsOnClass; - } - public class StronglyConnectedComponentOptimizer { private final DexItemFactory dexItemFactory = appView.dexItemFactory(); @@ -325,7 +303,7 @@ private void reservePinnedMethodSignatures( Set<DexProgramClass> stronglyConnectedProgramClasses) { DexMethodSignatureSet pinnedMethodSignatures = DexMethodSignatureSet.create(); - Set<DexClass> seenLibraryClasses = Sets.newIdentityHashSet(); + Set<DexClass> seenNonProgramClasses = Sets.newIdentityHashSet(); for (DexProgramClass clazz : stronglyConnectedProgramClasses) { clazz.forEachProgramMethodMatching( method -> !method.isInstanceInitializer(), @@ -341,9 +319,11 @@ (supertype, superclass) -> superclass != null && !superclass.isProgramClass() - && seenLibraryClasses.add(superclass), + && seenNonProgramClasses.add(superclass), (supertype, superclass) -> - pinnedMethodSignatures.addAll(getOrComputeLibraryVirtualMethods(superclass))); + pinnedMethodSignatures.addAll( + nonProgramMethodsCollection.getOrComputeNonProgramMethods( + superclass.asClasspathOrLibraryClass()))); } pinnedMethodSignatures.forEach( signature ->
diff --git a/src/main/java/com/android/tools/r8/optimize/utils/ConcurrentNonProgramMethodsCollection.java b/src/main/java/com/android/tools/r8/optimize/utils/ConcurrentNonProgramMethodsCollection.java new file mode 100644 index 0000000..0d5e0eb --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/utils/ConcurrentNonProgramMethodsCollection.java
@@ -0,0 +1,27 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.optimize.utils; + +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexClassAndMethod; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class ConcurrentNonProgramMethodsCollection extends NonProgramMethodsCollection { + + ConcurrentNonProgramMethodsCollection(AppView<? extends AppInfoWithClassHierarchy> appView) { + super(appView, new ConcurrentHashMap<>()); + } + + public static ConcurrentNonProgramMethodsCollection createVirtualMethodsCollection( + AppView<? extends AppInfoWithClassHierarchy> appView) { + return new ConcurrentNonProgramMethodsCollection(appView) { + @Override + public boolean test(DexClassAndMethod method) { + return method.getAccessFlags().belongsToVirtualPool(); + } + }; + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/utils/NonProgramMethodsCollection.java b/src/main/java/com/android/tools/r8/optimize/utils/NonProgramMethodsCollection.java new file mode 100644 index 0000000..da93905 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/utils/NonProgramMethodsCollection.java
@@ -0,0 +1,60 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.optimize.utils; + +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.ClasspathOrLibraryClass; +import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.utils.collections.DexMethodSignatureSet; +import java.util.Map; + +public abstract class NonProgramMethodsCollection { + + private final AppView<? extends AppInfoWithClassHierarchy> appView; + private final Map<DexClass, DexMethodSignatureSet> nonProgramMethods; + + NonProgramMethodsCollection( + AppView<? extends AppInfoWithClassHierarchy> appView, + Map<DexClass, DexMethodSignatureSet> nonProgramMethods) { + this.appView = appView; + this.nonProgramMethods = nonProgramMethods; + } + + public DexMethodSignatureSet getOrComputeNonProgramMethods(ClasspathOrLibraryClass clazz) { + return getOrComputeNonProgramMethods(clazz.asDexClass()); + } + + // Parameter is typed as DexClass to account for program classes above classpath or library + // classes. + private DexMethodSignatureSet getOrComputeNonProgramMethods(DexClass clazz) { + DexMethodSignatureSet nonProgramMethodsOnClass = nonProgramMethods.get(clazz); + return nonProgramMethodsOnClass != null + ? nonProgramMethodsOnClass + : computeNonProgramMethods(clazz); + } + + private DexMethodSignatureSet computeNonProgramMethods(DexClass clazz) { + DexMethodSignatureSet nonProgramMethodsOnClass = DexMethodSignatureSet.create(); + clazz.forEachImmediateSuperClassMatching( + appView, + (supertype, superclass) -> superclass != null, + (supertype, superclass) -> + nonProgramMethodsOnClass.addAll(getOrComputeNonProgramMethods(superclass))); + if (!clazz.isProgramClass()) { + clazz.forEachClassMethod( + method -> { + if (test(method)) { + nonProgramMethodsOnClass.add(method.getMethodSignature()); + } + }); + } + nonProgramMethods.put(clazz, nonProgramMethodsOnClass); + return nonProgramMethodsOnClass; + } + + public abstract boolean test(DexClassAndMethod method); +}
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 b1673ba..ead4063 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -633,7 +633,7 @@ if (!profileCollectionAdditions.isNop()) { for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) { profileCollectionAdditions.applyIfContextIsInProfile( - synthesizedBridge.originalMethod, + lens.getPreviousMethodSignature(synthesizedBridge.method), additionsBuilder -> additionsBuilder.addRule(synthesizedBridge.method)); } }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java index cf548f9..9e8ec26 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -112,6 +112,7 @@ DexProgramClass clazz, SyntheticKind kind, AppView<?> appView) { // TODO(b/158159959): Consider moving this to the dex writer similar to the CF case. assert !appView.options().isGeneratingClassFiles(); + assert !isDefinitelyNotSyntheticProgramClass(clazz); clazz.setAnnotations( clazz .annotations() @@ -148,9 +149,6 @@ private static SyntheticMarker internalStripMarkerFromClass( DexProgramClass clazz, AppView<?> appView) { - if (clazz.superType != appView.dexItemFactory().objectType) { - return NO_MARKER; - } if (isDefinitelyNotSyntheticProgramClass(clazz)) { return NO_MARKER; }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 89edec3..538b9ee 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2078,6 +2078,7 @@ public boolean roundtripThroughLir = false; public boolean checkReceiverAlwaysNullInCallSiteOptimization = true; public boolean forceInlineAPIConversions = false; + public boolean ignoreValueNumbering = false; private boolean hasReadCheckDeterminism = false; private DeterminismChecker determinismChecker = null; public boolean usePcEncodingInCfForTesting = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java index c998cf6..d09475d 100644 --- a/src/main/java/com/android/tools/r8/utils/ListUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -287,7 +287,7 @@ void accept(T item, int index); } - public static <T> List<T> sort(List<T> items, Comparator<T> comparator) { + public static <T> List<T> sort(Collection<T> items, Comparator<T> comparator) { List<T> sorted = new ArrayList<>(items); sorted.sort(comparator); return sorted;
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureBiMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureBiMap.java new file mode 100644 index 0000000..64ce1ec --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureBiMap.java
@@ -0,0 +1,14 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import com.google.common.collect.HashBiMap; + +public class DexMethodSignatureBiMap<T> extends DexMethodSignatureMap<T> { + + public DexMethodSignatureBiMap() { + super(HashBiMap.create()); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java index 84d1cae..bbf5afb 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java +++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
@@ -4,10 +4,12 @@ package com.android.tools.r8.utils.collections; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexMethodSignature; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -20,7 +22,7 @@ private final Map<DexMethodSignature, T> backing; - private DexMethodSignatureMap(Map<DexMethodSignature, T> backing) { + DexMethodSignatureMap(Map<DexMethodSignature, T> backing) { this.backing = backing; } @@ -28,10 +30,18 @@ return new DexMethodSignatureMap<>(new HashMap<>()); } + public static <T> DexMethodSignatureMap<T> create(DexMethodSignatureMap<T> map) { + return new DexMethodSignatureMap<>(new HashMap<>(map.backing)); + } + public static <T> DexMethodSignatureMap<T> createLinked() { return new DexMethodSignatureMap<>(new LinkedHashMap<>()); } + public static <T> DexMethodSignatureMap<T> empty() { + return new DexMethodSignatureMap<>(Collections.emptyMap()); + } + @Override public T put(DexMethodSignature signature, T value) { return backing.put(signature, value); @@ -100,6 +110,11 @@ return backing.replace(key, value); } + public T computeIfAbsent( + DexClassAndMethod key, Function<? super DexMethodSignature, ? extends T> mappingFunction) { + return computeIfAbsent(key.getMethodSignature(), mappingFunction); + } + @Override public T computeIfAbsent( DexMethodSignature key, Function<? super DexMethodSignature, ? extends T> mappingFunction) { @@ -189,7 +204,16 @@ } @Override - public void putAll(Map<? extends DexMethodSignature, ? extends T> m) {} + public void putAll(Map<? extends DexMethodSignature, ? extends T> map) { + map.forEach(this::put); + } + + @SuppressWarnings("unchecked") + public void putAllToIdentity(Collection<? extends DexMethodSignature> signatures) { + for (DexMethodSignature signature : signatures) { + put(signature, (T) signature); + } + } public T remove(DexMethodSignature signature) { return backing.remove(signature);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ThreadLocalBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ThreadLocalBackportTest.java index 3c6e599..8fbadfa 100644 --- a/src/test/java/com/android/tools/r8/desugar/backports/ThreadLocalBackportTest.java +++ b/src/test/java/com/android/tools/r8/desugar/backports/ThreadLocalBackportTest.java
@@ -8,6 +8,8 @@ import static com.android.tools.r8.utils.AndroidApiLevel.LATEST; import static org.hamcrest.CoreMatchers.containsString; +import com.android.tools.r8.D8TestCompileResult; +import com.android.tools.r8.OutputMode; import com.android.tools.r8.TestDiagnosticMessages; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -56,6 +58,24 @@ } @Test + public void testIntermediateD8() throws Exception { + D8TestCompileResult intermediate = + testForD8(parameters.getBackend()) + .addInnerClasses(getClass()) + .setOutputMode( + parameters.isCfRuntime() ? OutputMode.ClassFile : OutputMode.DexFilePerClass) + .setMinApi(parameters) + .compileWithExpectedDiagnostics(this::checkDiagnostics); + + testForD8(parameters.getBackend()) + .addProgramFiles(intermediate.writeToZip()) + .setMinApi(parameters) + .compile() + .run(parameters.getRuntime(), TestClass.class) + .apply(this::checkExpected); + } + + @Test public void testR8() throws Exception { parameters.assumeR8TestParameters(); testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/ir/AssociativeIntTest.java b/src/test/java/com/android/tools/r8/ir/AssociativeIntTest.java new file mode 100644 index 0000000..84e8e70 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/AssociativeIntTest.java
@@ -0,0 +1,463 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class AssociativeIntTest extends TestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "Associative", + "7", + "47", + "-2147483644", + "-2147483643", + "12", + "252", + "-6", + "0", + "2", + "2", + "2", + "0", + "3", + "43", + "2147483647", + "-2147483645", + "3", + "43", + "2147483646", + "-2147483647", + "Shift", + "64", + "1344", + "-32", + "0", + "0", + "1", + "67108863", + "-67108864", + "0", + "1", + "67108863", + "67108864", + "Sub", + "-1", + "-41", + "-2147483646", + "-2147483647", + "-3", + "37", + "2147483642", + "2147483643", + "Mixed", + "3", + "43", + "-2147483648", + "-2147483647", + "3", + "-37", + "-2147483642", + "-2147483643", + "-1", + "-41", + "-2147483646", + "-2147483647", + "25", + "-15", + "-2147483620", + "-2147483621", + "3", + "43", + "-2147483648", + "-2147483647", + "3", + "43", + "-2147483648", + "-2147483647", + "1", + "41", + "2147483646", + "2147483647", + "Double Associative", + "12", + "52", + "84", + "1764", + "2", + "2", + "7", + "47", + "4", + "44", + "Double Shift", + "128", + "2688", + "0", + "0", + "0", + "0", + "Double Sub", + "-1", + "-41", + "-10", + "30", + "Double Mixed", + "-4", + "36", + "7", + "-33", + "-4", + "36", + "5", + "45"); + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build(); + } + + public AssociativeIntTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testD8() throws Exception { + testForRuntime(parameters) + .addProgramClasses(Main.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .setMinApi(parameters) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + private void inspect(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz(Main.class); + for (FoundMethodSubject method : + clazz.allMethods(m -> m.getParameters().size() > 0 && m.getParameter(0).is("int"))) { + assertEquals( + method.getOriginalName().contains("NotSimplified") ? 2 : 1, + method + .streamInstructions() + .filter(i -> i.isIntArithmeticBinop() || i.isIntLogicalBinop()) + .count()); + } + } + + public static class Main { + + public static void main(String[] args) { + simple(); + doubleOps(); + } + + @NeverInline + private static void simple() { + // Associative + * & | ^. + System.out.println("Associative"); + add(2); + add(42); + add(Integer.MAX_VALUE); + add(Integer.MIN_VALUE); + mul(2); + mul(42); + mul(Integer.MAX_VALUE); + mul(Integer.MIN_VALUE); + and(2); + and(42); + and(Integer.MAX_VALUE); + and(Integer.MIN_VALUE); + or(2); + or(42); + or(Integer.MAX_VALUE); + or(Integer.MIN_VALUE); + xor(2); + xor(42); + xor(Integer.MAX_VALUE); + xor(Integer.MIN_VALUE); + + // Shift composition. + System.out.println("Shift"); + shl(2); + shl(42); + shl(Integer.MAX_VALUE); + shl(Integer.MIN_VALUE); + shr(2); + shr(42); + shr(Integer.MAX_VALUE); + shr(Integer.MIN_VALUE); + ushr(2); + ushr(42); + ushr(Integer.MAX_VALUE); + ushr(Integer.MIN_VALUE); + + // Special for -. + System.out.println("Sub"); + sub(2); + sub(42); + sub(Integer.MAX_VALUE); + sub(Integer.MIN_VALUE); + sub2(2); + sub2(42); + sub2(Integer.MAX_VALUE); + sub2(Integer.MIN_VALUE); + + // Mixed for + and -. + System.out.println("Mixed"); + addSub(2); + addSub(42); + addSub(Integer.MAX_VALUE); + addSub(Integer.MIN_VALUE); + subAdd(2); + subAdd(42); + subAdd(Integer.MAX_VALUE); + subAdd(Integer.MIN_VALUE); + addSubNotSimplified_1(2); + addSubNotSimplified_1(42); + addSubNotSimplified_1(Integer.MAX_VALUE); + addSubNotSimplified_1(Integer.MIN_VALUE); + addSubNotSimplified_2(2); + addSubNotSimplified_2(42); + addSubNotSimplified_2(Integer.MAX_VALUE); + addSubNotSimplified_2(Integer.MIN_VALUE); + addSubNotSimplified_3(2); + addSubNotSimplified_3(42); + addSubNotSimplified_3(Integer.MAX_VALUE); + addSubNotSimplified_3(Integer.MIN_VALUE); + addSub2(2); + addSub2(42); + addSub2(Integer.MAX_VALUE); + addSub2(Integer.MIN_VALUE); + subAdd2(2); + subAdd2(42); + subAdd2(Integer.MAX_VALUE); + subAdd2(Integer.MIN_VALUE); + } + + @NeverInline + private static void doubleOps() { + // Associative + * & | ^. + System.out.println("Double Associative"); + addDouble(2); + addDouble(42); + mulDouble(2); + mulDouble(42); + andDouble(2); + andDouble(42); + orDouble(2); + orDouble(42); + xorDouble(2); + xorDouble(42); + + // Shift composition. + System.out.println("Double Shift"); + shlDouble(2); + shlDouble(42); + shrDouble(2); + shrDouble(42); + ushrDouble(2); + ushrDouble(42); + + // Special for -. + System.out.println("Double Sub"); + subDouble(2); + subDouble(42); + sub2Double(2); + sub2Double(42); + + // Mixed for + and -. + System.out.println("Double Mixed"); + addSubDouble(2); + addSubDouble(42); + subAddDouble(2); + subAddDouble(42); + addSub2Double(2); + addSub2Double(42); + subAdd2Double(2); + subAdd2Double(42); + } + + @NeverInline + public static void add(int x) { + System.out.println(3 + x + 2); + } + + @NeverInline + public static void mul(int x) { + System.out.println(3 * x * 2); + } + + @NeverInline + public static void and(int x) { + System.out.println(3 & x & 2); + } + + @NeverInline + public static void or(int x) { + System.out.println(3 | x | 2); + } + + @NeverInline + public static void xor(int x) { + System.out.println(3 ^ x ^ 2); + } + + @NeverInline + public static void shl(int x) { + System.out.println(x << 2 << 3); + } + + @NeverInline + public static void shr(int x) { + System.out.println(x >> 2 >> 3); + } + + @NeverInline + public static void ushr(int x) { + System.out.println(x >>> 2 >>> 3); + } + + @NeverInline + public static void sub(int x) { + System.out.println(3 - x - 2); + } + + @NeverInline + public static void sub2(int x) { + System.out.println(x - 3 - 2); + } + + @NeverInline + public static void addSub(int x) { + System.out.println(3 + x - 2); + } + + @NeverInline + public static void addSub2(int x) { + System.out.println(x + 3 - 2); + } + + @NeverInline + public static void addSubNotSimplified_1(int x) { + System.out.println(14 - (x + 13)); + } + + @NeverInline + public static void addSubNotSimplified_2(int x) { + System.out.println(14 - (x - 13)); + } + + @NeverInline + public static void addSubNotSimplified_3(int x) { + System.out.println(14 - (13 - x)); + } + + @NeverInline + public static void subAdd(int x) { + System.out.println(3 - x + 2); + } + + @NeverInline + public static void subAdd2(int x) { + System.out.println(x - 3 + 2); + } + + @NeverInline + public static void addDouble(int x) { + System.out.println(3 + x + 2 + 5); + } + + @NeverInline + public static void mulDouble(int x) { + System.out.println(3 * x * 2 * 7); + } + + @NeverInline + public static void andDouble(int x) { + System.out.println(3 & x & 2 & 7); + } + + @NeverInline + public static void orDouble(int x) { + System.out.println(3 | x | 2 | 7); + } + + @NeverInline + public static void xorDouble(int x) { + System.out.println(3 ^ x ^ 2 ^ 7); + } + + @NeverInline + public static void shlDouble(int x) { + System.out.println(x << 2 << 3 << 1); + } + + @NeverInline + public static void shrDouble(int x) { + System.out.println(x >> 2 >> 3 >> 1); + } + + @NeverInline + public static void ushrDouble(int x) { + System.out.println(x >>> 2 >>> 3 >>> 1); + } + + @NeverInline + public static void subDouble(int x) { + System.out.println(3 - x - 2); + } + + @NeverInline + public static void sub2Double(int x) { + System.out.println(x - 3 - 2 - 7); + } + + @NeverInline + public static void addSubDouble(int x) { + System.out.println(3 + x - 2 - 7); + } + + @NeverInline + public static void addSub2Double(int x) { + System.out.println(x + 3 - 2 - 7); + } + + @NeverInline + public static void subAddDouble(int x) { + System.out.println(3 - x + 2 + 4); + } + + @NeverInline + public static void subAdd2Double(int x) { + System.out.println(x - 3 + 2 + 4); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/AssociativeLongTest.java b/src/test/java/com/android/tools/r8/ir/AssociativeLongTest.java new file mode 100644 index 0000000..1d041b8 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/AssociativeLongTest.java
@@ -0,0 +1,424 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class AssociativeLongTest extends TestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "Associative", + "7", + "47", + "-9223372036854775804", + "-9223372036854775803", + "12", + "252", + "-6", + "0", + "2", + "2", + "2", + "0", + "3", + "43", + "9223372036854775807", + "-9223372036854775805", + "3", + "43", + "9223372036854775806", + "-9223372036854775807", + "Shift", + "64", + "1344", + "-32", + "0", + "0", + "1", + "288230376151711743", + "-288230376151711744", + "0", + "1", + "288230376151711743", + "288230376151711744", + "Sub", + "-1", + "-41", + "-9223372036854775806", + "-9223372036854775807", + "-3", + "37", + "9223372036854775802", + "9223372036854775803", + "Mixed", + "3", + "43", + "-9223372036854775808", + "-9223372036854775807", + "3", + "-37", + "-9223372036854775802", + "-9223372036854775803", + "3", + "43", + "-9223372036854775808", + "-9223372036854775807", + "1", + "41", + "9223372036854775806", + "9223372036854775807", + "Double Associative", + "12", + "52", + "84", + "1764", + "2", + "2", + "7", + "47", + "4", + "44", + "Double Shift", + "128", + "2688", + "0", + "0", + "0", + "0", + "Double Sub", + "-1", + "-41", + "-10", + "30", + "Double Mixed", + "-4", + "36", + "7", + "-33", + "-4", + "36", + "5", + "45"); + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build(); + } + + public AssociativeLongTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testD8() throws Exception { + testForRuntime(parameters) + .addProgramClasses(Main.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .setMinApi(parameters) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + private void inspect(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz(Main.class); + for (FoundMethodSubject method : + clazz.allMethods(m -> m.getParameters().size() > 0 && m.getParameter(0).is("long"))) { + assertEquals( + 1, + method + .streamInstructions() + .filter(i -> i.isLongArithmeticBinop() || i.isLongLogicalBinop()) + .count()); + } + } + + public static class Main { + + public static void main(String[] args) { + simple(); + doubleOps(); + } + + @NeverInline + private static void simple() { + // Associative + * & | ^. + System.out.println("Associative"); + add(2); + add(42); + add(Long.MAX_VALUE); + add(Long.MIN_VALUE); + mul(2); + mul(42); + mul(Long.MAX_VALUE); + mul(Long.MIN_VALUE); + and(2); + and(42); + and(Long.MAX_VALUE); + and(Long.MIN_VALUE); + or(2); + or(42); + or(Long.MAX_VALUE); + or(Long.MIN_VALUE); + xor(2); + xor(42); + xor(Long.MAX_VALUE); + xor(Long.MIN_VALUE); + + // Shift composition. + System.out.println("Shift"); + shl(2); + shl(42); + shl(Long.MAX_VALUE); + shl(Long.MIN_VALUE); + shr(2); + shr(42); + shr(Long.MAX_VALUE); + shr(Long.MIN_VALUE); + ushr(2); + ushr(42); + ushr(Long.MAX_VALUE); + ushr(Long.MIN_VALUE); + + // Special for -. + System.out.println("Sub"); + sub(2); + sub(42); + sub(Long.MAX_VALUE); + sub(Long.MIN_VALUE); + sub2(2); + sub2(42); + sub2(Long.MAX_VALUE); + sub2(Long.MIN_VALUE); + + // Mixed for + and -. + System.out.println("Mixed"); + addSub(2); + addSub(42); + addSub(Long.MAX_VALUE); + addSub(Long.MIN_VALUE); + subAdd(2); + subAdd(42); + subAdd(Long.MAX_VALUE); + subAdd(Long.MIN_VALUE); + addSub2(2); + addSub2(42); + addSub2(Long.MAX_VALUE); + addSub2(Long.MIN_VALUE); + subAdd2(2); + subAdd2(42); + subAdd2(Long.MAX_VALUE); + subAdd2(Long.MIN_VALUE); + } + + @NeverInline + private static void doubleOps() { + // Associative + * & | ^. + System.out.println("Double Associative"); + addDouble(2); + addDouble(42); + mulDouble(2); + mulDouble(42); + andDouble(2); + andDouble(42); + orDouble(2); + orDouble(42); + xorDouble(2); + xorDouble(42); + + // Shift composition. + System.out.println("Double Shift"); + shlDouble(2); + shlDouble(42); + shrDouble(2); + shrDouble(42); + ushrDouble(2); + ushrDouble(42); + + // Special for -. + System.out.println("Double Sub"); + subDouble(2); + subDouble(42); + sub2Double(2); + sub2Double(42); + + // Mixed for + and -. + System.out.println("Double Mixed"); + addSubDouble(2); + addSubDouble(42); + subAddDouble(2); + subAddDouble(42); + addSub2Double(2); + addSub2Double(42); + subAdd2Double(2); + subAdd2Double(42); + } + + @NeverInline + public static void add(long x) { + System.out.println(3L + x + 2L); + } + + @NeverInline + public static void mul(long x) { + System.out.println(3L * x * 2L); + } + + @NeverInline + public static void and(long x) { + System.out.println(3L & x & 2L); + } + + @NeverInline + public static void or(long x) { + System.out.println(3L | x | 2L); + } + + @NeverInline + public static void xor(long x) { + System.out.println(3L ^ x ^ 2L); + } + + @NeverInline + public static void shl(long x) { + System.out.println(x << 2L << 3L); + } + + @NeverInline + public static void shr(long x) { + System.out.println(x >> 2L >> 3L); + } + + @NeverInline + public static void ushr(long x) { + System.out.println(x >>> 2L >>> 3L); + } + + @NeverInline + public static void sub(long x) { + System.out.println(3L - x - 2L); + } + + @NeverInline + public static void sub2(long x) { + System.out.println(x - 3L - 2L); + } + + @NeverInline + public static void addSub(long x) { + System.out.println(3L + x - 2L); + } + + @NeverInline + public static void addSub2(long x) { + System.out.println(x + 3L - 2L); + } + + @NeverInline + public static void subAdd(long x) { + System.out.println(3L - x + 2L); + } + + @NeverInline + public static void subAdd2(long x) { + System.out.println(x - 3L + 2L); + } + + @NeverInline + public static void addDouble(long x) { + System.out.println(3L + x + 2L + 5); + } + + @NeverInline + public static void mulDouble(long x) { + System.out.println(3L * x * 2L * 7L); + } + + @NeverInline + public static void andDouble(long x) { + System.out.println(3L & x & 2L & 7L); + } + + @NeverInline + public static void orDouble(long x) { + System.out.println(3L | x | 2L | 7L); + } + + @NeverInline + public static void xorDouble(long x) { + System.out.println(3L ^ x ^ 2L ^ 7L); + } + + @NeverInline + public static void shlDouble(long x) { + System.out.println(x << 2L << 3L << 1L); + } + + @NeverInline + public static void shrDouble(long x) { + System.out.println(x >> 2L >> 3L >> 1L); + } + + @NeverInline + public static void ushrDouble(long x) { + System.out.println(x >>> 2L >>> 3L >>> 1L); + } + + @NeverInline + public static void subDouble(long x) { + System.out.println(3L - x - 2L); + } + + @NeverInline + public static void sub2Double(long x) { + System.out.println(x - 3L - 2L - 7L); + } + + @NeverInline + public static void addSubDouble(long x) { + System.out.println(3L + x - 2L - 7L); + } + + @NeverInline + public static void addSub2Double(long x) { + System.out.println(x + 3L - 2L - 7L); + } + + @NeverInline + public static void subAddDouble(long x) { + System.out.println(3L - x + 2L + 4L); + } + + @NeverInline + public static void subAdd2Double(long x) { + System.out.println(x - 3L + 2L + 4L); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingIntTest.java b/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingIntTest.java new file mode 100644 index 0000000..9d7b58d --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingIntTest.java
@@ -0,0 +1,603 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class IdentityAbsorbingIntTest extends TestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "2147483647", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "2147483647", + "2147483647", + "2147483647", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "2147483646", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "2147483646", + "2147483646", + "2147483646", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483648", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "-2147483648", + "-2147483648", + "-2147483648", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "-2147483647", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "-2147483647", + "-2147483647", + "-2147483647", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "1", + "1", + "1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build(); + } + + public IdentityAbsorbingIntTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testD8() throws Exception { + testForRuntime(parameters) + .addProgramClasses(Main.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .setMinApi(parameters) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + private void inspect(CodeInspector inspector) { + inspector + .clazz(Main.class) + .forAllMethods( + m -> + assertTrue( + m.streamInstructions() + .noneMatch(i -> i.isIntLogicalBinop() || i.isIntArithmeticBinop()))); + } + + static class Main { + + public static void main(String[] args) { + intTests(Integer.MAX_VALUE); + intTests(Integer.MAX_VALUE - 1); + intTests(Integer.MIN_VALUE); + intTests(Integer.MIN_VALUE + 1); + intTests(System.currentTimeMillis() > 0 ? 0 : 1); + intTests(System.currentTimeMillis() > 0 ? 1 : 9); + intTests(System.currentTimeMillis() > 0 ? -1 : 1); + } + + private static void intTests(int val) { + identityIntTest(val); + absorbingIntTest(val); + identityDoubleIntTest(val); + absorbingDoubleIntTest(val); + chainIntTest(val); + associativeIdentityIntTest(val); + } + + @NeverInline + private static void identityDoubleIntTest(int val) { + System.out.println(val + 0 + 0); + System.out.println(0 + val + 0); + System.out.println(0 + 0 + val); + System.out.println(val - 0 - 0); + System.out.println(val * 1 * 1); + System.out.println(1 * val * 1); + System.out.println(1 * 1 * val); + System.out.println(val / 1 / 1); + + System.out.println(val & -1 & -1); + System.out.println(-1 & val & -1); + System.out.println(-1 & -1 & val); + System.out.println(val | 0 | 0); + System.out.println(0 | val | 0); + System.out.println(0 | 0 | val); + System.out.println(val ^ 0 ^ 0); + System.out.println(0 ^ val ^ 0); + System.out.println(0 ^ 0 ^ val); + System.out.println(val << 0 << 0); + System.out.println(val >> 0 >> 0); + System.out.println(val >>> 0 >>> 0); + } + + @NeverInline + private static void identityIntTest(int val) { + System.out.println(val + 0); + System.out.println(0 + val); + System.out.println(val - 0); + System.out.println(val * 1); + System.out.println(1 * val); + System.out.println(val / 1); + + System.out.println(val & -1); + System.out.println(-1 & val); + System.out.println(val | 0); + System.out.println(0 | val); + System.out.println(val ^ 0); + System.out.println(0 ^ val); + System.out.println(val << 0); + System.out.println(val >> 0); + System.out.println(val >>> 0); + } + + @NeverInline + private static void associativeIdentityIntTest(int val) { + int minusOne = -1; + System.out.println(val + 1 + minusOne); + System.out.println(val + 1 - 1); + System.out.println(val - 1 - minusOne); + } + + @NeverInline + private static void absorbingDoubleIntTest(int val) { + System.out.println(val * 0 * 0); + System.out.println(0 * val * 0); + System.out.println(0 * 0 * val); + // val would need to be proven non zero. + // System.out.println(0 / val); + // System.out.println(0 % val); + + System.out.println(0 & 0 & val); + System.out.println(0 & val & 0); + System.out.println(val & 0 & 0); + System.out.println(-1 | -1 | val); + System.out.println(-1 | val | -1); + System.out.println(val | -1 | -1); + System.out.println(0 << 0 << val); + System.out.println(0 >> 0 >> val); + System.out.println(0 >>> 0 >>> val); + } + + @NeverInline + private static void absorbingIntTest(int val) { + System.out.println(val * 0); + System.out.println(0 * val); + // val would need to be proven non zero. + // System.out.println(0 / val); + // System.out.println(0 % val); + + System.out.println(0 & val); + System.out.println(val & 0); + System.out.println(-1 | val); + System.out.println(val | -1); + System.out.println(0 << val); + System.out.println(0 >> val); + System.out.println(0 >>> val); + } + + private static void chainIntTest(int val) { + int abs = System.currentTimeMillis() > 0 ? val * 0 : 0 * val; + System.out.println(abs * val); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingLongTest.java b/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingLongTest.java new file mode 100644 index 0000000..a1f0c1c --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingLongTest.java
@@ -0,0 +1,590 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class IdentityAbsorbingLongTest extends TestBase { + + private static final String EXPECTED_RESULT = + StringUtils.lines( + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "9223372036854775807", + "9223372036854775807", + "9223372036854775807", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "9223372036854775806", + "9223372036854775806", + "9223372036854775806", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775808", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "-9223372036854775807", + "-9223372036854775807", + "-9223372036854775807", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "1", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "1", + "1", + "1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "-1", + "-1", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "0", + "0", + "0", + "-1", + "-1", + "-1", + "0", + "0", + "0", + "-1", + "-1", + "-1"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build(); + } + + public IdentityAbsorbingLongTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testD8() throws Exception { + testForRuntime(parameters) + .addProgramClasses(Main.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .setMinApi(parameters) + .compile() + .inspect(this::inspect) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + private void inspect(CodeInspector inspector) { + inspector + .clazz(Main.class) + .forAllMethods( + m -> + assertTrue( + m.streamInstructions() + .noneMatch(i -> i.isLongArithmeticBinop() || i.isLongLogicalBinop()))); + } + + static class Main { + + public static void main(String[] args) { + longTests(Long.MAX_VALUE); + longTests(Long.MAX_VALUE - 1); + longTests(Long.MIN_VALUE); + longTests(Long.MIN_VALUE + 1); + longTests(System.currentTimeMillis() > 0 ? 0L : 1L); + longTests(System.currentTimeMillis() > 0 ? 1L : 9L); + longTests(System.currentTimeMillis() > 0 ? -1L : 1L); + } + + private static void longTests(long val) { + identityLongTest(val); + absorbingLongTest(val); + identityDoubleLongTest(val); + absorbingDoubleLongTest(val); + associativeIdentityLongTest(val); + } + + @NeverInline + private static void identityDoubleLongTest(long val) { + System.out.println(val + 0L + 0L); + System.out.println(0L + val + 0L); + System.out.println(0L + 0L + val); + System.out.println(val - 0L - 0L); + System.out.println(val * 1L * 1L); + System.out.println(1L * val * 1L); + System.out.println(1L * 1L * val); + System.out.println(val / 1L / 1L); + + System.out.println(val & -1L & -1L); + System.out.println(-1L & val & -1L); + System.out.println(-1L & -1L & val); + System.out.println(val | 0L | 0L); + System.out.println(0L | val | 0L); + System.out.println(0L | 0L | val); + System.out.println(val ^ 0L ^ 0L); + System.out.println(0L ^ val ^ 0L); + System.out.println(0L ^ 0L ^ val); + System.out.println(val << 0L << 0L); + System.out.println(val >> 0L >> 0L); + System.out.println(val >>> 0L >>> 0L); + } + + @NeverInline + private static void identityLongTest(long val) { + System.out.println(val + 0L); + System.out.println(0L + val); + System.out.println(val - 0L); + System.out.println(val * 1L); + System.out.println(1L * val); + System.out.println(val / 1L); + + System.out.println(val & -1L); + System.out.println(-1L & val); + System.out.println(val | 0L); + System.out.println(0L | val); + System.out.println(val ^ 0L); + System.out.println(0L ^ val); + System.out.println(val << 0L); + System.out.println(val >> 0L); + System.out.println(val >>> 0L); + } + + @NeverInline + private static void associativeIdentityLongTest(long val) { + long minusOne = -1L; + System.out.println(val + 1L + minusOne); + System.out.println(val + 1L - 1L); + System.out.println(val - 1L - minusOne); + } + + @NeverInline + private static void absorbingDoubleLongTest(long val) { + System.out.println(val * 0L * 0L); + System.out.println(0L * val * 0L); + System.out.println(0L * 0L * val); + // val would need to be proven non zero. + // System.out.println(0L / val); + // System.out.println(0L % val); + + System.out.println(0L & 0L & val); + System.out.println(0L & val & 0L); + System.out.println(val & 0L & 0L); + System.out.println(-1L | -1L | val); + System.out.println(-1L | val | -1L); + System.out.println(val | -1L | -1L); + System.out.println(0L << 0L << val); + System.out.println(0L >> 0L >> val); + System.out.println(0L >>> 0L >>> val); + } + + @NeverInline + private static void absorbingLongTest(long val) { + System.out.println(val * 0L); + System.out.println(0L * val); + // val would need to be proven non zero. + // System.out.println(0L / val); + // System.out.println(0L % val); + + System.out.println(0L & val); + System.out.println(val & 0L); + System.out.println(-1L | val); + System.out.println(val | -1L); + System.out.println(0L << val); + System.out.println(0L >> val); + System.out.println(0L >>> val); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingTest.java b/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingTest.java deleted file mode 100644 index 390d6ca..0000000 --- a/src/test/java/com/android/tools/r8/ir/IdentityAbsorbingTest.java +++ /dev/null
@@ -1,1063 +0,0 @@ -// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -package com.android.tools.r8.ir; - -import static org.junit.Assert.assertTrue; - -import com.android.tools.r8.NeverInline; -import com.android.tools.r8.TestBase; -import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.utils.StringUtils; -import com.android.tools.r8.utils.codeinspector.CodeInspector; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -public class IdentityAbsorbingTest extends TestBase { - - private static final String EXPECTED_RESULT = - StringUtils.lines( - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "2147483647", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "2147483646", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "-2147483648", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "-2147483647", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "9223372036854775807", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "9223372036854775806", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "-9223372036854775808", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "-9223372036854775807", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "1", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "-1", - "-1", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "-1", - "0", - "0", - "0", - "0", - "0", - "0", - "-1", - "-1", - "-1", - "0", - "0", - "0"); - - private final TestParameters parameters; - - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build(); - } - - public IdentityAbsorbingTest(TestParameters parameters) { - this.parameters = parameters; - } - - @Test - public void testD8() throws Exception { - testForRuntime(parameters) - .addProgramClasses(Main.class) - .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutput(EXPECTED_RESULT); - } - - @Test - public void testR8() throws Exception { - testForR8(parameters.getBackend()) - .addProgramClasses(Main.class) - .addKeepMainRule(Main.class) - .enableInliningAnnotations() - .setMinApi(parameters) - .compile() - .inspect(this::inspect) - .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutput(EXPECTED_RESULT); - } - - private void inspect(CodeInspector inspector) { - inspector - .clazz(Main.class) - .forAllMethods( - m -> - assertTrue( - m.streamInstructions() - .noneMatch( - i -> i.isIntOrLongLogicalBinop() || i.isIntOrLongArithmeticBinop()))); - } - - static class Main { - - public static void main(String[] args) { - intTests(Integer.MAX_VALUE); - intTests(Integer.MAX_VALUE - 1); - intTests(Integer.MIN_VALUE); - intTests(Integer.MIN_VALUE + 1); - intTests(System.currentTimeMillis() > 0 ? 0 : 1); - intTests(System.currentTimeMillis() > 0 ? 1 : 9); - intTests(System.currentTimeMillis() > 0 ? -1 : 1); - - longTests(Long.MAX_VALUE); - longTests(Long.MAX_VALUE - 1); - longTests(Long.MIN_VALUE); - longTests(Long.MIN_VALUE + 1); - longTests(System.currentTimeMillis() > 0 ? 0L : 1L); - longTests(System.currentTimeMillis() > 0 ? 1L : 9L); - longTests(System.currentTimeMillis() > 0 ? -1L : 1L); - } - - private static void longTests(long val) { - identityLongTest(val); - absorbingLongTest(val); - identityDoubleLongTest(val); - absorbingDoubleLongTest(val); - } - - private static void intTests(int val) { - identityIntTest(val); - absorbingIntTest(val); - identityDoubleIntTest(val); - absorbingDoubleIntTest(val); - chainIntTest(val); - } - - @NeverInline - private static void identityDoubleIntTest(int val) { - System.out.println(val + 0 + 0); - System.out.println(0 + val + 0); - System.out.println(0 + 0 + val); - System.out.println(val - 0 - 0); - System.out.println(val * 1 * 1); - System.out.println(1 * val * 1); - System.out.println(1 * 1 * val); - System.out.println(val / 1 / 1); - - System.out.println(val & -1 & -1); - System.out.println(-1 & val & -1); - System.out.println(-1 & -1 & val); - System.out.println(val | 0 | 0); - System.out.println(0 | val | 0); - System.out.println(0 | 0 | val); - System.out.println(val ^ 0 ^ 0); - System.out.println(0 ^ val ^ 0); - System.out.println(0 ^ 0 ^ val); - System.out.println(val << 0 << 0); - System.out.println(val >> 0 >> 0); - System.out.println(val >>> 0 >>> 0); - } - - @NeverInline - private static void identityDoubleLongTest(long val) { - System.out.println(val + 0L + 0L); - System.out.println(0L + val + 0L); - System.out.println(0L + 0L + val); - System.out.println(val - 0L - 0L); - System.out.println(val * 1L * 1L); - System.out.println(1L * val * 1L); - System.out.println(1L * 1L * val); - System.out.println(val / 1L / 1L); - - System.out.println(val & -1L & -1L); - System.out.println(-1L & val & -1L); - System.out.println(-1L & -1L & val); - System.out.println(val | 0L | 0L); - System.out.println(0L | val | 0L); - System.out.println(0L | 0L | val); - System.out.println(val ^ 0L ^ 0L); - System.out.println(0L ^ val ^ 0L); - System.out.println(0L ^ 0L ^ val); - System.out.println(val << 0L << 0L); - System.out.println(val >> 0L >> 0L); - System.out.println(val >>> 0L >>> 0L); - } - - @NeverInline - private static void identityIntTest(int val) { - System.out.println(val + 0); - System.out.println(0 + val); - System.out.println(val - 0); - System.out.println(val * 1); - System.out.println(1 * val); - System.out.println(val / 1); - - System.out.println(val & -1); - System.out.println(-1 & val); - System.out.println(val | 0); - System.out.println(0 | val); - System.out.println(val ^ 0); - System.out.println(0 ^ val); - System.out.println(val << 0); - System.out.println(val >> 0); - System.out.println(val >>> 0); - } - - @NeverInline - private static void identityLongTest(long val) { - System.out.println(val + 0L); - System.out.println(0L + val); - System.out.println(val - 0L); - System.out.println(val * 1L); - System.out.println(1L * val); - System.out.println(val / 1L); - - System.out.println(val & -1L); - System.out.println(-1L & val); - System.out.println(val | 0L); - System.out.println(0L | val); - System.out.println(val ^ 0L); - System.out.println(0L ^ val); - System.out.println(val << 0L); - System.out.println(val >> 0L); - System.out.println(val >>> 0L); - } - - @NeverInline - private static void absorbingDoubleIntTest(int val) { - System.out.println(val * 0 * 0); - System.out.println(0 * val * 0); - System.out.println(0 * 0 * val); - // val would need to be proven non zero. - // System.out.println(0 / val); - // System.out.println(0 % val); - - System.out.println(0 & 0 & val); - System.out.println(0 & val & 0); - System.out.println(val & 0 & 0); - System.out.println(-1 | -1 | val); - System.out.println(-1 | val | -1); - System.out.println(val | -1 | -1); - System.out.println(0 << 0 << val); - System.out.println(0 >> 0 >> val); - System.out.println(0 >>> 0 >>> val); - } - - @NeverInline - private static void absorbingDoubleLongTest(long val) { - System.out.println(val * 0L * 0L); - System.out.println(0L * val * 0L); - System.out.println(0L * 0L * val); - // val would need to be proven non zero. - // System.out.println(0L / val); - // System.out.println(0L % val); - - System.out.println(0L & 0L & val); - System.out.println(0L & val & 0L); - System.out.println(val & 0L & 0L); - System.out.println(-1L | -1L | val); - System.out.println(-1L | val | -1L); - System.out.println(val | -1L | -1L); - System.out.println(0L << 0L << val); - System.out.println(0L >> 0L >> val); - System.out.println(0L >>> 0L >>> val); - } - - @NeverInline - private static void absorbingIntTest(int val) { - System.out.println(val * 0); - System.out.println(0 * val); - // val would need to be proven non zero. - // System.out.println(0 / val); - // System.out.println(0 % val); - - System.out.println(0 & val); - System.out.println(val & 0); - System.out.println(-1 | val); - System.out.println(val | -1); - System.out.println(0 << val); - System.out.println(0 >> val); - System.out.println(0 >>> val); - } - - @NeverInline - private static void absorbingLongTest(long val) { - System.out.println(val * 0L); - System.out.println(0L * val); - // val would need to be proven non zero. - // System.out.println(0L / val); - // System.out.println(0L % val); - - System.out.println(0L & val); - System.out.println(val & 0L); - System.out.println(-1L | val); - System.out.println(val | -1L); - System.out.println(0L << val); - System.out.println(0L >> val); - System.out.println(0L >>> val); - } - - private static void chainIntTest(int val) { - int abs = System.currentTimeMillis() > 0 ? val * 0 : 0 * val; - System.out.println(abs * val); - } - } -}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java index d89807c..9100c82 100644 --- a/src/test/java/com/android/tools/r8/ir/InlineTest.java +++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -72,6 +72,10 @@ MethodSubject method, List<IRCode> additionalCode) throws ExecutionException { + // Some tests play fast and loose with IR and the SSA value numbers are not generally unique. + if (additionalCode != null && !additionalCode.isEmpty()) { + options.testing.ignoreValueNumbering = true; + } AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(application.asDirect()); appView.setAppServices(AppServices.builder(appView).build()); ProfileCollectionAdditions profileCollectionAdditions = ProfileCollectionAdditions.nop();
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java index 0d23c77..25fc777 100644 --- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java +++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -9,9 +9,11 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionIterator; import com.android.tools.r8.ir.code.InstructionListIterator; import com.android.tools.r8.ir.code.NumberGenerator; +import com.android.tools.r8.ir.code.Phi; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.smali.SmaliBuilder.MethodSignature; import com.android.tools.r8.smali.SmaliTestBase; @@ -69,6 +71,20 @@ this.code = method.buildIR(); this.additionalCode = additionalCode; this.consumers = new AndroidAppConsumers(appView.options()); + int largestValueNumber = -1; + for (BasicBlock block : code.blocks) { + for (Phi phi : block.getPhis()) { + largestValueNumber = Math.max(largestValueNumber, phi.getNumber()); + } + for (Instruction instruction : block.getInstructions()) { + if (instruction.hasOutValue()) { + largestValueNumber = Math.max(largestValueNumber, instruction.outValue().getNumber()); + } + } + } + while (valueNumberGenerator.peek() <= largestValueNumber) { + valueNumberGenerator.next(); + } } public int countArgumentInstructions() {
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodWithLineNumberTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodWithLineNumberTest.java index a6aded1..132b0f5 100644 --- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodWithLineNumberTest.java +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDifferentMethodWithLineNumberTest.java
@@ -46,7 +46,7 @@ "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", "com.foo -> b:", " 1:1:int f1(boolean) -> f2", - " 8:8:void f1(int) -> f3", + " 8:8:void f1(int):2:2 -> f3", " # {'id':'com.android.tools.r8.residualsignature','signature':'(Z)V'}"); @Test
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineFollowedByInlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineFollowedByInlineTest.java index 04ffc15..dbfcdb5 100644 --- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineFollowedByInlineTest.java +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineFollowedByInlineTest.java
@@ -58,9 +58,9 @@ StringUtils.unixLines( "# {'id':'com.android.tools.r8.mapping','version':'experimental'}", "com.bar -> c:", - " 2:2:void inlinee1():43:43 -> y", + " 2:2:void com.foo.inlinee1():43:43 -> y", " 2:2:void foo.bar.baz.inlinee1():41 -> y", - " 2:2:void caller():40 -> y", + " 2:2:void com.foo.caller():40 -> y", " 2:2:void inlinee2():42:42 -> y", " 2:2:void foo.bar.baz.inlinee1():41 -> y", " 2:2:void caller():40 -> y",
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineResidualQualifiedTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineResidualQualifiedTest.java new file mode 100644 index 0000000..1912d84 --- /dev/null +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeInlineResidualQualifiedTest.java
@@ -0,0 +1,62 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.mappingcompose; + +import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.MappingComposer; +import com.android.tools.r8.utils.StringUtils; +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; + +@RunWith(Parameterized.class) +public class ComposeInlineResidualQualifiedTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + private static final String mappingFoo = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "com.foo -> A:", + " 1:3:void method1():42:44 -> x", + "com.bar -> B:", + " 4:6:void method2():104:106 -> x"); + private static final String mappingBar = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "B -> C:", + " 1:3:void A.x():2:2 -> y", + " 1:3:void x():4 -> y", + " 2:3:void x():5:6 -> y"); + private static final String mappingResult = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "com.bar -> C:", + " 1:3:void com.foo.method1():43:43 -> y", + " 1:3:void method2():104:104 -> y", + " 2:3:void method2():105:106 -> y", + "com.foo -> A:"); + + @Test + public void testCompose() throws Exception { + ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo); + ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar); + String composed = MappingComposer.compose(mappingForFoo, mappingForBar); + assertEquals(mappingResult, doubleToSingleQuote(composed)); + } +}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMethodWithLineNumberRemovedTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMethodWithLineNumberRemovedTest.java new file mode 100644 index 0000000..9b28d99 --- /dev/null +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMethodWithLineNumberRemovedTest.java
@@ -0,0 +1,75 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.mappingcompose; + +import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.MappingComposer; +import com.android.tools.r8.utils.StringUtils; +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 a regression test for b/284925475. + */ +@RunWith(Parameterized.class) +public class ComposeMethodWithLineNumberRemovedTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + private static final String mappingFoo = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.FieldDefinition$FullFieldDefinition -> package.internal.Th:", + "package.FieldDefinition -> package.internal.Uh:", + "# {'id':'sourceFile','fileName':'FieldDefinition.java'}", + " 1:1:void <init>():13:13 -> <init>", + " 1:1:package.FieldDefinition$FullFieldDefinition asFullFieldDefinition():0:0 -> a", + " # {'id':'com.android.tools.r8.residualsignature'," + + "'signature':'()Lpackage/internal/Th;'}"); + private static final String mappingBar = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.ClassReference ->" + " package.ClassReference:", + "package.internal.Th -> package.other_internal.F1:", + "package.internal.Uh -> package.other_internal.M1:", + "# {'id':'sourceFile','fileName':'R8_new_hash_name'}", + " 1:1:void <init>() -> <init>", + " package.internal.Th a() -> b", + " # {'id':'com.android.tools.r8.residualsignature'," + + "'signature':'()Lpackage/retrace_internal/F1;'}"); + private static final String mappingResult = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.ClassReference -> package.ClassReference:", + "package.FieldDefinition -> package.other_internal.M1:", + "# {'id':'sourceFile','fileName':'FieldDefinition.java'}", + " package.internal.Th a() -> b", + " # {'id':'com.android.tools.r8.residualsignature'," + + "'signature':'()Lpackage/retrace_internal/F1;'}", + " 1:1:void <init>():13:13 -> <init>", + "package.FieldDefinition$FullFieldDefinition -> package.other_internal.F1:"); + + @Test + public void testCompose() throws Exception { + ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo); + ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar); + String composed = MappingComposer.compose(mappingForFoo, mappingForBar); + assertEquals(mappingResult, doubleToSingleQuote(composed)); + } +}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodTest.java index 80c4dec..2991061 100644 --- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodTest.java +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodTest.java
@@ -46,7 +46,7 @@ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}", "com.bar -> b:", " int some.other.Class.f1() -> h1", - " void f2() -> h2", + " void com.foo.f2() -> h2", "com.foo -> a:"); @Test
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodWithPositionTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodWithPositionTest.java index 68d2c86..db17b8d 100644 --- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodWithPositionTest.java +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedMethodWithPositionTest.java
@@ -48,7 +48,7 @@ "com.bar -> b:", " 12:14:int some.other.Class.f1():102:104 -> h11", " 15:16:int some.other.Class.f1():102:103 -> h12", - " 22:23:void f2():114:114 -> h2", + " 22:23:void com.foo.f2():114:114 -> h2", "com.foo -> a:"); @Test
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineHavingInlineInlinedTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineHavingInlineInlinedTest.java new file mode 100644 index 0000000..03e93e6 --- /dev/null +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineHavingInlineInlinedTest.java
@@ -0,0 +1,73 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.mappingcompose; + +import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.MappingComposer; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +public class ComposeOutlineHavingInlineInlinedTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + private static final String mappingFoo = + StringUtils.unixLines( + "# { id: 'com.android.tools.r8.mapping', version: '2.2' }", + "outline.Class -> A:", + " 1:2:int some.inlinee():75:76 -> a", + " 1:2:int outline():0 -> a", + " # { 'id':'com.android.tools.r8.outline' }", + "outline.Callsite -> X:", + " 1:2:int outlineCaller(int):41:42 -> s", + " 3:3:int outlineCaller(int):0:0 -> s", + " # { 'id':'com.android.tools.r8.outlineCallsite'," + + "'positions': { '1': 9, '2': 10 }," + + "'outline':'La;a()I' }", + " 9:9:int outlineCaller(int):23 -> s", + " 10:10:int foo.bar.baz.outlineCaller(int):98:98 -> s", + " 10:10:int outlineCaller(int):24 -> s"); + private static final String mappingBar = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "X -> Y:", + " 1:1:int A.a():1:1 -> s", + " 1:1:int s(int):3 -> s", + " 2:4:int another.inline():102:104 -> s", + " 2:4:int A.a():2 -> s", + " 2:4:int s(int):3 -> s"); + private static final String mappingResult = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "outline.Callsite -> Y:", + " 1:1:int some.inlinee():75:75 -> s", + " 1:1:int outlineCaller(int):23 -> s", + " 2:4:int another.inline():102:104 -> s", + " 2:4:int some.inlinee():76:76 -> s", + " 2:4:int foo.bar.baz.outlineCaller(int):98:98 -> s", + " 2:4:int outlineCaller(int):24 -> s", + "outline.Class -> A:"); + + @Test + public void testCompose() throws Exception { + ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo); + ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar); + String composed = MappingComposer.compose(mappingForFoo, mappingForBar); + assertEquals(mappingResult, doubleToSingleQuote(composed)); + } +}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineInlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineInlineTest.java new file mode 100644 index 0000000..0c50e79 --- /dev/null +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineInlineTest.java
@@ -0,0 +1,80 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.mappingcompose; + +import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.MappingComposer; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +public class ComposeOutlineInlineTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + private static final String mappingFoo = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:", + "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}", + "# {'id':'com.android.tools.r8.synthesized'}", + " 1:2:long package.Int2IntLinkedOpenHashMap$$InternalSyntheticOutline$HASH$0" + + ".m(long,long,long):0:1" + + " -> a", + " # {'id':'com.android.tools.r8.synthesized'}", + " # {'id':'com.android.tools.r8.outline'}", + "package.Class -> package.internal.Y:", + "# {'id':'sourceFile','fileName':'FieldDefinition.java'}", + " 1:6:void foo():21:26 -> a", + " 7:7:void foo():0:0 -> a", + " # {'id':'com.android.tools.r8.outlineCallsite'," + + "'positions':{'1':10,'2':11}," + + "'outline':'Lpackage/internal/X;a(JJJ)J'}", + " 8:9:void foo():38:39 -> a", + " 10:10:void inlineeInOutline():1337:1337 -> a", + " 10:10:void foo():42 -> a", + " 11:11:void foo():44:44 -> a"); + private static final String mappingBar = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.internal.Y -> package.new_internal.Y:", + " 1:6:void a() -> b", + " 7:8:long package.internal.X.a(long,long,long):1:2 -> b", + " 7:8:void a():7 -> b", + " 9:10:void a():8:9 -> b"); + private static final String mappingResult = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.Class -> package.new_internal.Y:", + "# {'id':'sourceFile','fileName':'FieldDefinition.java'}", + " 1:6:void foo():21:26 -> b", + " 7:7:void inlineeInOutline():1337:1337 -> b", + " 7:7:void foo():42 -> b", + " 8:8:void foo():44:44 -> b", + " 9:10:void foo():38:39 -> b", + "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:", + "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}", + "# {'id':'com.android.tools.r8.synthesized'}"); + + @Test + public void testCompose() throws Exception { + ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo); + ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar); + String composed = MappingComposer.compose(mappingForFoo, mappingForBar); + assertEquals(mappingResult, doubleToSingleQuote(composed)); + } +}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineInlinedIntoOutlineAndInlinedTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineInlinedIntoOutlineAndInlinedTest.java new file mode 100644 index 0000000..9dc41ea --- /dev/null +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineInlinedIntoOutlineAndInlinedTest.java
@@ -0,0 +1,84 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.mappingcompose; + +import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.MappingComposer; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +public class ComposeOutlineInlinedIntoOutlineAndInlinedTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + private static final String mappingFoo = + StringUtils.unixLines( + "# { id: 'com.android.tools.r8.mapping', version: '2.2' }", + "Outline1 -> A:", + " 1:2:int outline1():0:1 -> a", + " # { 'id':'com.android.tools.r8.outline' }", + "Outline2 -> B:", + " 1:2:int outline2():0:1 -> a", + " # { 'id':'com.android.tools.r8.outline' }", + "outline.Callsite1 -> X:", + " 1:2:int caller1(int):41:42 -> s", + " 3:3:int caller1(int):0:0 -> s", + " # { 'id':'com.android.tools.r8.outlineCallsite'," + + "'positions': { '1': 9, '2': 10 }," + + "'outline':'La;a()I' }", + " 4:6:int caller1(int):48:49 -> s", + " 9:9:int caller1(int):23 -> s", + " 10:10:int caller1(int):24 -> s", + "outline.Callsite2 -> Y:", + " 1:1:int caller2(int):0:0 -> s", + " # { 'id':'com.android.tools.r8.outlineCallsite'," + + "'positions': { '1': 2, '2': 3 }," + + "'outline':'La;a()I' }", + " 2:2:int caller2(int):23:23 -> s", + " 3:3:int caller2(int):24:24 -> s"); + private static final String mappingBar = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "Y -> Z:", + " 1:2:int A.a():1:2 -> a", + " 1:2:int X.s(int):3 -> a", + " 1:2:int B.a():1 -> a", + " 1:2:int s(int):1 -> a", + " 3:3:int B.a():2:2 -> a", + " 3:3:int s(int):1 -> a"); + private static final String mappingResult = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "Outline1 -> A:", + "Outline2 -> B:", + "outline.Callsite1 -> X:", + "outline.Callsite2 -> Z:", + " 1:1:int outline.Callsite1.caller1(int):23 -> a", + " 1:1:int caller2(int):23:23 -> a", + " 2:2:int outline.Callsite1.caller1(int):24 -> a", + " 2:2:int caller2(int):23:23 -> a", + " 3:3:int caller2(int):24:24 -> a"); + + @Test + public void testCompose() throws Exception { + ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo); + ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar); + String composed = MappingComposer.compose(mappingForFoo, mappingForBar); + assertEquals(mappingResult, doubleToSingleQuote(composed)); + } +}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java index 657b551..f722cc3 100644 --- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
@@ -41,8 +41,9 @@ " 5:5:int foo.bar.baz.outlineCaller(int):98:98 -> s", " 5:5:int outlineCaller(int):24 -> s", " 27:27:int outlineCaller(int):0:0 -> s", - " # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '1': 4, '2': 5 }," - + " 'outline':'La;a()I' }"); + " # { 'id':'com.android.tools.r8.outlineCallsite'," + + "'positions': { '1': 4, '2': 5 }," + + "'outline':'La;a()I' }"); private static final String mappingBar = StringUtils.unixLines("# {'id':'com.android.tools.r8.mapping','version':'2.2'}", "a -> b:"); private static final String mappingBaz = @@ -51,18 +52,18 @@ "b -> c:", " 4:5:int a():1:2 -> m", "x -> y:", - " 8:9:int s(int):4:5 -> o", " 42:42:int s(int):27:27 -> o"); private static final String mappingResult = StringUtils.unixLines( "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", "outline.Callsite -> y:", - " 8:8:int outlineCaller(int):23 -> o", - " 9:9:int foo.bar.baz.outlineCaller(int):98:98 -> o", - " 9:9:int outlineCaller(int):24 -> o", " 42:42:int outlineCaller(int):0:0 -> o", - " #" - + " {'id':'com.android.tools.r8.outlineCallsite','positions':{'4':8,'5':9},'outline':'Lc;m()I'}", + " # {'id':'com.android.tools.r8.outlineCallsite'," + + "'positions':{'4':43,'5':44}," + + "'outline':'Lc;m()I'}", + " 43:43:int outlineCaller(int):23 -> o", + " 44:44:int foo.bar.baz.outlineCaller(int):98:98 -> s", + " 44:44:int outlineCaller(int):24 -> o", "outline.Class -> c:", " 4:5:int some.inlinee():75:76 -> m", " 4:5:int outline():0 -> m",
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineWithIdRangeTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineWithIdRangeTest.java new file mode 100644 index 0000000..20b1cb8 --- /dev/null +++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineWithIdRangeTest.java
@@ -0,0 +1,87 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.mappingcompose; + +import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.naming.MappingComposer; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +public class ComposeOutlineWithIdRangeTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + private static final String mappingFoo = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:", + "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}", + "# {'id':'com.android.tools.r8.synthesized'}", + " 1:3:long package.Int2IntLinkedOpenHashMap$$InternalSyntheticOutline$HASH$0" + + ".m(long,long,long):0:2" + + " -> a", + " # {'id':'com.android.tools.r8.synthesized'}", + " # {'id':'com.android.tools.r8.outline'}", + "package.Class -> package.internal.Y:", + "# {'id':'sourceFile','fileName':'FieldDefinition.java'}", + " 1:6:void foo():21:26 -> a", + " 7:7:void foo():0:0 -> a", + " # {'id':'com.android.tools.r8.outlineCallsite'," + + "'positions':{'1':10,'2':11}," + + "'outline':'Lpackage/internal/X;a(JJJ)J'}", + " 8:9:void foo():38:39 -> a", + " 10:10:void inlineeInOutline():1337:1337 -> a", + " 10:10:void foo():42 -> a", + " 11:11:void foo():44:44 -> a"); + private static final String mappingBar = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.internal.X -> package.new_internal.X:", + " 1:3:long a(long,long,long) -> b", + "package.internal.Y -> package.new_internal.Y:", + " 1:8:void a() -> b"); + private static final String mappingResult = + StringUtils.unixLines( + "# {'id':'com.android.tools.r8.mapping','version':'2.2'}", + "package.Class -> package.new_internal.Y:", + "# {'id':'sourceFile','fileName':'FieldDefinition.java'}", + " 1:6:void foo():21:26 -> b", + " 7:7:void foo():0:0 -> b", + " # {'id':'com.android.tools.r8.outlineCallsite'," + + "'positions':{'1':9,'2':10}," + + "'outline':'Lpackage/new_internal/X;b(JJJ)J'}", + " 8:8:void foo():38:38 -> b", + " 9:9:void inlineeInOutline():1337:1337 -> a", + " 9:9:void foo():42 -> b", + " 10:10:void foo():44:44 -> b", + "package.Class$$ExternalSyntheticOutline0 -> package.new_internal.X:", + "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}", + "# {'id':'com.android.tools.r8.synthesized'}", + " 1:3:long package.Int2IntLinkedOpenHashMap$$InternalSyntheticOutline$HASH$0" + + ".m(long,long,long):0:2 -> b", + " # {'id':'com.android.tools.r8.synthesized'}", + " # {'id':'com.android.tools.r8.outline'}"); + + @Test + public void testCompose() throws Exception { + ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo); + ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar); + String composed = MappingComposer.compose(mappingForFoo, mappingForBar); + assertEquals(mappingResult, doubleToSingleQuote(composed)); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMovedMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMovedMethodTest.java new file mode 100644 index 0000000..0059297 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMovedMethodTest.java
@@ -0,0 +1,137 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.naming.applymapping; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.R8TestCompileResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.MemberSubject; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +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 a reproduction of b/269178203. */ +@RunWith(Parameterized.class) +public class ApplyMappingMovedMethodTest extends TestBase { + + private static final String mapping = + StringUtils.lines( + "com.android.tools.r8.naming.applymapping.ApplyMappingMovedMethodTest$One -> b.b:", + " void foo() -> m1", + " void" + + " com.android.tools.r8.naming.applymapping.ApplyMappingMovedMethodTest$Other.foo()" + + " -> m2"); + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testR8ApplyMappingSameCompilation() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(One.class, Other.class, Main.class) + .setMinApi(parameters) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .addApplyMapping(mapping) + .addHorizontallyMergedClassesInspector( + inspector -> inspector.assertClassesMerged(One.class, Other.class)) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("One::foo") + .inspect( + inspector -> { + ClassSubject clazz = inspector.clazz("b.b"); + assertThat(clazz, isPresent()); + Set<String> expected = new HashSet<>(); + expected.add("m1"); + // TODO(b/269178203): We should be able to rewrite this to m2. + expected.add("a"); + assertEquals( + expected, + clazz.allMethods().stream() + .map(MemberSubject::getFinalName) + .collect(Collectors.toSet())); + }); + } + + @Test + public void testR8TestReference() throws Exception { + R8TestCompileResult libraryResult = + testForR8(parameters.getBackend()) + .addProgramClasses(One.class, Other.class, Main.class) + .setMinApi(parameters) + .addKeepMainRule(Main.class) + .addHorizontallyMergedClassesInspector( + inspector -> inspector.assertClassesMerged(One.class, Other.class)) + .enableInliningAnnotations() + .compile(); + testForR8(parameters.getBackend()) + .addProgramClasses(TestClass.class) + .addClasspathClasses(One.class, Other.class) + .addKeepAllClassesRule() + .addApplyMapping(libraryResult.getProguardMap()) + .setMinApi(parameters) + .compile() + .addRunClasspathFiles(libraryResult.writeToZip()) + .run(parameters.getRuntime(), TestClass.class) + // TODO(b/269178203): We could rewrite calls to m2 but it would be better to just keep the + // endpoints in the program when tests are referencing it - in this example it is this + // compilation that is the test and it referencing the libraryResult, which is the program. + .assertFailureWithErrorThatThrows(NoClassDefFoundError.class) + .assertFailureWithErrorThatMatches(containsString(typeName(Other.class))); + } + + public static class One { + + @NeverInline + public static void foo() { + System.out.println("One::foo"); + } + } + + public static class Other { + + @NeverInline + public static void foo() { + System.out.println("Other::foo"); + } + } + + public static class Main { + + public static void main(String[] args) { + if (System.currentTimeMillis() > 0) { + One.foo(); + } else { + Other.foo(); + } + } + } + + public static class TestClass { + + public static void main(String[] args) { + One.foo(); + Other.foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/HorizontalMergingWithTryCatchLineNumberTest.java b/src/test/java/com/android/tools/r8/retrace/HorizontalMergingWithTryCatchLineNumberTest.java new file mode 100644 index 0000000..ff42a2d --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/HorizontalMergingWithTryCatchLineNumberTest.java
@@ -0,0 +1,99 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.retrace; + +import static com.android.tools.r8.naming.retrace.StackTrace.isSame; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.R8TestCompileResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.naming.retrace.StackTrace; +import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; +import com.android.tools.r8.retrace.classes.SynthesizeLineNumber; +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 a test for b/283757617 showing how we potentially could change line information at + * runtime. + */ +@RunWith(Parameterized.class) +public class HorizontalMergingWithTryCatchLineNumberTest extends TestBase { + + private static final String FILENAME = "SynthesizeLineNumber.java"; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private static final StackTrace expectedStackTrace = + StackTrace.builder() + .add( + StackTraceLine.builder() + .setClassName(typeName(SynthesizeLineNumber.A.class)) + .setMethodName("foo") + .setFileName(FILENAME) + .setLineNumber(14) + .build()) + .add( + StackTraceLine.builder() + .setClassName(typeName(SynthesizeLineNumber.Main.class)) + .setMethodName("call") + .setFileName(FILENAME) + .setLineNumber(34) + .build()) + .add( + StackTraceLine.builder() + .setClassName(typeName(SynthesizeLineNumber.Main.class)) + .setMethodName("main") + .setFileName(FILENAME) + .setLineNumber(28) + .build()) + .build(); + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addInnerClasses(SynthesizeLineNumber.class) + .run(parameters.getRuntime(), SynthesizeLineNumber.Main.class, "normal") + .inspectStackTrace(stackTrace -> assertThat(stackTrace, isSame(expectedStackTrace))); + } + + @Test + public void testR8() throws Exception { + R8TestCompileResult compilationResult = + testForR8(parameters.getBackend()) + .addInnerClasses(SynthesizeLineNumber.class) + .setMinApi(parameters) + .addKeepMainRule(SynthesizeLineNumber.Main.class) + .addKeepAttributeLineNumberTable() + .enableInliningAnnotations() + .addHorizontallyMergedClassesInspector( + inspector -> + inspector.assertClassesMerged( + SynthesizeLineNumber.A.class, SynthesizeLineNumber.B.class)) + .compile(); + // Mock changes to proguard map to simulate what R8 could emit. + String proguardMap = + compilationResult.getProguardMap() + + "\n 1000:1000:void call(int,boolean):34:34 -> main" + + "\n 1000:1000:void main(java.lang.String[]):28 -> main"; + compilationResult + .run(parameters.getRuntime(), SynthesizeLineNumber.Main.class, "synthesize") + .inspectOriginalStackTrace( + originalStackTrace -> { + StackTrace retraced = originalStackTrace.retrace(proguardMap); + assertThat(retraced, isSame(expectedStackTrace)); + }); + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/classes/SynthesizeLineNumber.java b/src/test/java/com/android/tools/r8/retrace/classes/SynthesizeLineNumber.java new file mode 100644 index 0000000..a2b4c80 --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/classes/SynthesizeLineNumber.java
@@ -0,0 +1,71 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.retrace.classes; + +import com.android.tools.r8.NeverInline; + +public class SynthesizeLineNumber { + + public static class A { + @NeverInline + public static void foo() throws Exception { + throw new Exception("A::foo"); + } + } + + public static class B { + @NeverInline + public static void bar() throws Exception { + throw new Exception("B::bar"); + } + } + + public static class Main { + + public static void main(String[] args) throws Exception { + call(System.currentTimeMillis() > 0 ? 0 : 1, args[0].equals("synthesize")); + } + + public static void call(int classId, boolean shouldSynthesizeLineNumber) throws Exception { + try { + if (classId == 0) { + A.foo(); + } else { + B.bar(); + } + } catch (Exception e) { + if (shouldSynthesizeLineNumber) { + Exception exception = new Exception(e.getMessage(), e.getCause()); + StackTraceElement[] stackTrace = e.getStackTrace(); + // The obfuscated class name after repackaging and class name minification. + String className = "com.android.tools.r8.retrace.classes.SynthesizeLineNumber$Main"; + // The obfuscated method name after minification. Since this is inlined into main it is + // just main. + String obfuscatedMethodName = "main"; + for (int i = 0; i < stackTrace.length; i++) { + StackTraceElement original = stackTrace[i]; + // The line number range will need to be the range of the try block for catching the + // exception. + if (original.getClassName().equals(className) + && original.getMethodName().equals(obfuscatedMethodName) + && original.getLineNumber() >= 0 + && original.getLineNumber() <= 10) { + stackTrace[i] = + new StackTraceElement( + original.getClassName(), + original.getMethodName(), + original.getFileName(), + classId + 1000); + break; + } + } + exception.setStackTrace(stackTrace); + throw exception; + } + throw e; + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/startup/SingleCallerBridgeStartupTest.java b/src/test/java/com/android/tools/r8/startup/SingleCallerBridgeStartupTest.java new file mode 100644 index 0000000..4a27e0f --- /dev/null +++ b/src/test/java/com/android/tools/r8/startup/SingleCallerBridgeStartupTest.java
@@ -0,0 +1,89 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.startup; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.startup.profile.ExternalStartupItem; +import com.android.tools.r8.startup.profile.ExternalStartupMethod; +import com.android.tools.r8.startup.utils.StartupTestingUtils; +import com.google.common.collect.ImmutableList; +import java.util.Collection; +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; + +/** Regression test for b/285021603. */ +@RunWith(Parameterized.class) +public class SingleCallerBridgeStartupTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void test() throws Exception { + // Create a startup profile with method A.bar() and A.callBarInStartup() to allow inlining + // of A.bar() into A.callBarInStartup(). + MethodReference barMethod = Reference.methodFromMethod(A.class.getDeclaredMethod("bar")); + Collection<ExternalStartupItem> startupProfile = + ImmutableList.of( + ExternalStartupMethod.builder().setMethodReference(barMethod).build(), + ExternalStartupMethod.builder() + .setMethodReference( + Reference.methodFromMethod(A.class.getDeclaredMethod("callBarInStartup"))) + .build()); + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .apply(testBuilder -> StartupTestingUtils.addStartupProfile(testBuilder, startupProfile)) + .addOptionsModification( + options -> + // Simulate proto optimization where we allow reprocessing of inlinee. + options.inlinerOptions().applyInliningToInlineePredicateForTesting = + (appView, inlinee, inliningDepth) -> + inlinee.getMethodReference().equals(barMethod)) + .setMinApi(parameters) + .run(parameters.getRuntime(), Main.class) + // TODO(b/285021603): We should not fail here. + .assertFailureWithErrorThatThrows(NoSuchMethodError.class); + } + + static class Main { + + public static void main(String[] args) { + A.callBarInStartup(); + A.callBarOutsideStartup(); + } + } + + public static class A { + + private static void foo() { + System.out.println("A::foo"); + } + + private static void bar() { + foo(); + } + + public static void callBarInStartup() { + bar(); + } + + public static void callBarOutsideStartup() { + bar(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java index 45d6202..79425a3 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -339,17 +339,27 @@ } @Override - public boolean isIntOrLongArithmeticBinop() { + public boolean isIntArithmeticBinop() { return instruction instanceof CfArithmeticBinop - && (((CfArithmeticBinop) instruction).getType() == NumericType.INT - || ((CfArithmeticBinop) instruction).getType() == NumericType.LONG); + && ((CfArithmeticBinop) instruction).getType() == NumericType.INT; } @Override - public boolean isIntOrLongLogicalBinop() { + public boolean isIntLogicalBinop() { return instruction instanceof CfLogicalBinop - && (((CfLogicalBinop) instruction).getType() == NumericType.INT - || ((CfLogicalBinop) instruction).getType() == NumericType.LONG); + && ((CfLogicalBinop) instruction).getType() == NumericType.INT; + } + + @Override + public boolean isLongArithmeticBinop() { + return instruction instanceof CfArithmeticBinop + && ((CfArithmeticBinop) instruction).getType() == NumericType.LONG; + } + + @Override + public boolean isLongLogicalBinop() { + return instruction instanceof CfLogicalBinop + && ((CfLogicalBinop) instruction).getType() == NumericType.LONG; } @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java index 38421a2..bd68422 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -122,6 +122,8 @@ import com.android.tools.r8.dex.code.DexReturn; import com.android.tools.r8.dex.code.DexReturnObject; import com.android.tools.r8.dex.code.DexReturnVoid; +import com.android.tools.r8.dex.code.DexRsubInt; +import com.android.tools.r8.dex.code.DexRsubIntLit8; import com.android.tools.r8.dex.code.DexSget; import com.android.tools.r8.dex.code.DexSgetBoolean; import com.android.tools.r8.dex.code.DexSgetByte; @@ -501,69 +503,77 @@ return instruction instanceof DexSparseSwitch; } - public boolean isIntOrLongArithmeticBinop() { + public boolean isIntArithmeticBinop() { return instruction instanceof DexMulInt || instruction instanceof DexMulIntLit8 || instruction instanceof DexMulIntLit16 || instruction instanceof DexMulInt2Addr - || instruction instanceof DexMulLong - || instruction instanceof DexMulLong2Addr || instruction instanceof DexAddInt || instruction instanceof DexAddIntLit8 || instruction instanceof DexAddIntLit16 || instruction instanceof DexAddInt2Addr - || instruction instanceof DexAddLong - || instruction instanceof DexAddLong2Addr + || instruction instanceof DexRsubInt + || instruction instanceof DexRsubIntLit8 || instruction instanceof DexSubInt || instruction instanceof DexSubInt2Addr - || instruction instanceof DexSubLong - || instruction instanceof DexSubLong2Addr || instruction instanceof DexDivInt || instruction instanceof DexDivIntLit8 || instruction instanceof DexDivIntLit16 || instruction instanceof DexDivInt2Addr - || instruction instanceof DexDivLong - || instruction instanceof DexDivLong2Addr || instruction instanceof DexRemInt || instruction instanceof DexRemIntLit8 || instruction instanceof DexRemIntLit16 - || instruction instanceof DexRemInt2Addr + || instruction instanceof DexRemInt2Addr; + } + + public boolean isLongArithmeticBinop() { + return instruction instanceof DexMulLong + || instruction instanceof DexMulLong2Addr + || instruction instanceof DexAddLong + || instruction instanceof DexAddLong2Addr + || instruction instanceof DexSubLong + || instruction instanceof DexSubLong2Addr + || instruction instanceof DexDivLong + || instruction instanceof DexDivLong2Addr || instruction instanceof DexRemLong || instruction instanceof DexRemLong2Addr; } - public boolean isIntOrLongLogicalBinop() { + public boolean isIntLogicalBinop() { return instruction instanceof DexAndInt || instruction instanceof DexAndIntLit8 || instruction instanceof DexAndIntLit16 || instruction instanceof DexAndInt2Addr - || instruction instanceof DexAndLong - || instruction instanceof DexAndLong2Addr || instruction instanceof DexOrInt || instruction instanceof DexOrIntLit8 || instruction instanceof DexOrIntLit16 || instruction instanceof DexOrInt2Addr - || instruction instanceof DexOrLong - || instruction instanceof DexOrLong2Addr || instruction instanceof DexXorInt || instruction instanceof DexXorIntLit8 || instruction instanceof DexXorIntLit16 || instruction instanceof DexXorInt2Addr - || instruction instanceof DexXorLong - || instruction instanceof DexXorLong2Addr || instruction instanceof DexShrInt || instruction instanceof DexShrIntLit8 || instruction instanceof DexShrInt2Addr - || instruction instanceof DexShrLong - || instruction instanceof DexShrLong2Addr || instruction instanceof DexShlInt || instruction instanceof DexShlIntLit8 || instruction instanceof DexShlInt2Addr - || instruction instanceof DexShlLong - || instruction instanceof DexShlLong2Addr || instruction instanceof DexUshrInt || instruction instanceof DexUshrIntLit8 - || instruction instanceof DexUshrInt2Addr + || instruction instanceof DexUshrInt2Addr; + } + + public boolean isLongLogicalBinop() { + return instruction instanceof DexAndLong + || instruction instanceof DexAndLong2Addr + || instruction instanceof DexOrLong + || instruction instanceof DexOrLong2Addr + || instruction instanceof DexXorLong + || instruction instanceof DexXorLong2Addr + || instruction instanceof DexShrLong + || instruction instanceof DexShrLong2Addr + || instruction instanceof DexShlLong + || instruction instanceof DexShlLong2Addr || instruction instanceof DexUshrLong || instruction instanceof DexUshrLong2Addr; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java index 6d1bd94..b77029f 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -140,9 +140,13 @@ boolean isSparseSwitch(); - boolean isIntOrLongArithmeticBinop(); + boolean isIntArithmeticBinop(); - boolean isIntOrLongLogicalBinop(); + boolean isIntLogicalBinop(); + + boolean isLongArithmeticBinop(); + + boolean isLongLogicalBinop(); boolean isMultiplication();