Introduce a predicate for libraryMethodsWithoutSideEffects
Bug: 133745205
Change-Id: I937839ecf6c3ecac23f89b274f57ca5556d9ff1f
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index d5ebf00..9202120 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement.computeLeastUpperBoundOfInterfaces;
+import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
@@ -22,16 +23,19 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.ReferenceTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.LRUCacheTable;
+import com.android.tools.r8.utils.Pair;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Streams;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@@ -47,7 +51,9 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class DexItemFactory {
@@ -448,14 +454,19 @@
// We assume library methods listed here are `public`, i.e., free from visibility side effects.
// If not, that library method should not be added here because it literally has side effects.
- public Set<DexMethod> libraryMethodsWithoutSideEffects =
- ImmutableSet.<DexMethod>builder()
- .add(objectMethods.constructor)
- .addAll(classMethods.getNames)
- .addAll(stringBufferMethods.constructorMethods)
- .addAll(stringBuilderMethods.constructorMethods)
- .addAll(boxedValueOfMethods())
- .build();
+ public Map<DexMethod, Predicate<InvokeMethod>> libraryMethodsWithoutSideEffects =
+ Streams.<Pair<DexMethod, Predicate<InvokeMethod>>>concat(
+ Stream.of(new Pair<>(objectMethods.constructor, alwaysTrue())),
+ mapToPredicate(classMethods.getNames, alwaysTrue()),
+ mapToPredicate(stringBufferMethods.constructorMethods, alwaysTrue()),
+ mapToPredicate(stringBuilderMethods.constructorMethods, alwaysTrue()),
+ mapToPredicate(boxedValueOfMethods(), alwaysTrue()))
+ .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond));
+
+ private static Stream<Pair<DexMethod, Predicate<InvokeMethod>>> mapToPredicate(
+ Set<DexMethod> methods, Predicate<InvokeMethod> predicate) {
+ return methods.stream().map(method -> new Pair<>(method, predicate));
+ }
// TODO(b/119596718): More idempotent methods? Any singleton accessors? E.g.,
// java.util.Calendar#getInstance(...) // 4 variants
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 9be25b6..a7d3edb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -163,7 +163,9 @@
}
// Check if it is a call to one of library methods that are known to be side-effect free.
- if (appView.dexItemFactory().libraryMethodsWithoutSideEffects.contains(getInvokedMethod())) {
+ Predicate<InvokeMethod> noSideEffectsPredicate =
+ appView.dexItemFactory().libraryMethodsWithoutSideEffects.get(getInvokedMethod());
+ if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(this)) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 56f852b..44b4790 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -25,6 +25,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.function.Predicate;
import org.objectweb.asm.Opcodes;
public class InvokeStatic extends InvokeMethod {
@@ -150,7 +151,9 @@
}
// Check if it is a call to one of library methods that are known to be side-effect free.
- if (appView.dexItemFactory().libraryMethodsWithoutSideEffects.contains(getInvokedMethod())) {
+ Predicate<InvokeMethod> noSideEffectsPredicate =
+ appView.dexItemFactory().libraryMethodsWithoutSideEffects.get(getInvokedMethod());
+ if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(this)) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index a2ce1bc..2c1b5a6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
import java.util.List;
+import java.util.function.Predicate;
import org.objectweb.asm.Opcodes;
public class InvokeVirtual extends InvokeMethodWithReceiver {
@@ -138,7 +139,9 @@
}
// Check if it is a call to one of library methods that are known to be side-effect free.
- if (appView.dexItemFactory().libraryMethodsWithoutSideEffects.contains(getInvokedMethod())) {
+ Predicate<InvokeMethod> noSideEffectsPredicate =
+ appView.dexItemFactory().libraryMethodsWithoutSideEffects.get(getInvokedMethod());
+ if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(this)) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 9b04a16..6e958f1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
/**
* Canonicalize idempotent function calls.
@@ -122,8 +123,7 @@
}
DexMethod invokedMethod = invoke.getInvokedMethod();
// Interested in known-to-be idempotent methods.
- if (!factory.libraryMethodsWithReturnValueDependingOnlyOnArguments.contains(invokedMethod)
- || !factory.libraryMethodsWithoutSideEffects.contains(invokedMethod)) {
+ if (!isIdempotentLibraryMethodInvoke(invoke)) {
if (!appView.enableWholeProgramOptimizations()) {
// Give up in D8
continue;
@@ -246,6 +246,16 @@
assert code.isConsistentSSA();
}
+ private boolean isIdempotentLibraryMethodInvoke(InvokeMethod invoke) {
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ Predicate<InvokeMethod> noSideEffectPredicate =
+ factory.libraryMethodsWithoutSideEffects.get(invokedMethod);
+ if (noSideEffectPredicate == null || !noSideEffectPredicate.test(invoke)) {
+ return false;
+ }
+ return factory.libraryMethodsWithReturnValueDependingOnlyOnArguments.contains(invokedMethod);
+ }
+
private static void insertCanonicalizedInvokeWithInValues(
IRCode code, Invoke canonicalizedInvoke) {
BasicBlock entryBlock = code.entryBlock();
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 47e0cd0..8987418 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -42,6 +42,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Assert;
@@ -1331,7 +1332,7 @@
dexItemFactory.stringBuilderType);
// ... and not assuming that StringBuilder.<init>() cannot have side effects.
dexItemFactory.libraryMethodsWithoutSideEffects =
- new HashSet<>(dexItemFactory.libraryMethodsWithoutSideEffects);
+ new IdentityHashMap<>(dexItemFactory.libraryMethodsWithoutSideEffects);
dexItemFactory.libraryMethodsWithoutSideEffects.remove(
dexItemFactory.stringBuilderMethods.defaultConstructor);
});