Towards the propagation of constants from call sites to the method body.
Bug: 69963623, 139246447
Change-Id: Iab83db43d5cb62bdf535bf88eafb26d3def59567
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 078410a..90cc0f4 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -77,7 +77,7 @@
this.options = options;
this.rewritePrefix = mapper;
- if (enableWholeProgramOptimizations() && options.enableCallSiteOptimizationInfoPropagation) {
+ if (enableWholeProgramOptimizations() && options.enablePropagationOfDynamicTypesAtCallSites) {
this.callSiteOptimizationInfoPropagator =
new CallSiteOptimizationInfoPropagator(withLiveness());
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index f535e42..f560b29 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -48,6 +48,13 @@
return false;
}
+ public AbstractValue join(AbstractValue other) {
+ if (this.equals(other)) {
+ return this;
+ }
+ return UnknownValue.getInstance();
+ }
+
@Override
public abstract boolean equals(Object o);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index ab931cc..388169b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -10,10 +10,11 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
import com.android.tools.r8.ir.code.Assume.NonNullAssumption;
-import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -171,7 +172,7 @@
.hasUsefulOptimizationInfo(appView, code.method);
Set<Value> affectedValues = Sets.newIdentityHashSet();
List<Assume<?>> assumeInstructions = new LinkedList<>();
- List<ConstInstruction> constants = new LinkedList<>();
+ List<Instruction> constants = new LinkedList<>();
int argumentsSeen = 0;
InstructionListIterator iterator = code.entryBlock().listIterator(code);
while (iterator.hasNext()) {
@@ -184,8 +185,23 @@
if (originalArg.hasLocalInfo() || !originalArg.getTypeLattice().isReference()) {
continue;
}
+ int argIndex = argumentsSeen - 1;
+ AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(argIndex);
+ if (abstractValue.isSingleValue()) {
+ assert appView.options().enablePropagationOfConstantsAtCallSites;
+ SingleValue singleValue = abstractValue.asSingleValue();
+ if (singleValue.isMaterializableInContext(appView, code.method.method.holder)) {
+ Instruction replacement =
+ singleValue.createMaterializingInstruction(appView, code, instr);
+ replacement.setPosition(instr.getPosition());
+ affectedValues.addAll(originalArg.affectedValues());
+ originalArg.replaceUsers(replacement.outValue());
+ constants.add(replacement);
+ continue;
+ }
+ }
TypeLatticeElement dynamicUpperBoundType =
- callSiteOptimizationInfo.getDynamicUpperBoundType(argumentsSeen - 1);
+ callSiteOptimizationInfo.getDynamicUpperBoundType(argIndex);
if (dynamicUpperBoundType == null) {
continue;
}
@@ -197,7 +213,6 @@
constants.add(nullInstruction);
continue;
}
- // TODO(b/69963623): Handle other kinds of constants, e.g. number, string, or class.
Value specializedArg;
if (dynamicUpperBoundType.strictlyLessThan(originalArg.getTypeLattice(), appView)) {
specializedArg = code.createValue(originalArg.getTypeLattice());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
index c99f24c..18c709c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
// A flat lattice structure:
@@ -63,8 +65,12 @@
// TODO(b/139246447): dynamic lower bound type?
- // TODO(b/69963623): collect constants and if they're all same, propagate it to the callee.
- // then, we need to re-run unused argument removal?
+ // TODO(b/69963623): we need to re-run unused argument removal?
+
+ // The index exactly matches with in values of invocation, i.e., even including receiver.
+ public AbstractValue getAbstractArgumentValue(int argIndex) {
+ return UnknownValue.getInstance();
+ }
// TODO(b/139249918): propagate classes that are guaranteed to be initialized.
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index be0bf19..c38c0ed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -11,9 +11,13 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.Value;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.List;
+import java.util.Objects;
// Accumulated optimization info from call sites.
public class ConcreteCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
@@ -21,26 +25,39 @@
// inValues() size == DexMethod.arity + (isStatic ? 0 : 1) // receiver
// That is, this information takes into account the receiver as well.
private final int size;
- private final Int2ReferenceArrayMap<TypeLatticeElement> dynamicUpperBoundTypes;
- // TODO(b/69963623): sparse map from index to ConstantData if any.
+ private final Int2ReferenceMap<TypeLatticeElement> dynamicUpperBoundTypes;
+ private final Int2ReferenceMap<AbstractValue> constants;
- private ConcreteCallSiteOptimizationInfo(DexEncodedMethod encodedMethod) {
- assert encodedMethod.method.getArity() + (encodedMethod.isStatic() ? 0 : 1) > 0;
- this.size = encodedMethod.method.getArity() + (encodedMethod.isStatic() ? 0 : 1);
- this.dynamicUpperBoundTypes = new Int2ReferenceArrayMap<>(size);
+ private ConcreteCallSiteOptimizationInfo(
+ DexEncodedMethod encodedMethod, boolean allowConstantPropagation) {
+ this(encodedMethod.method.getArity() + (encodedMethod.isStatic() ? 0 : 1),
+ allowConstantPropagation);
}
- private ConcreteCallSiteOptimizationInfo(int size) {
+ private ConcreteCallSiteOptimizationInfo(int size, boolean allowConstantPropagation) {
+ assert size > 0;
this.size = size;
this.dynamicUpperBoundTypes = new Int2ReferenceArrayMap<>(size);
+ this.constants = allowConstantPropagation ? new Int2ReferenceArrayMap<>(size) : null;
}
CallSiteOptimizationInfo join(
ConcreteCallSiteOptimizationInfo other, AppView<?> appView, DexEncodedMethod encodedMethod) {
assert this.size == other.size;
- ConcreteCallSiteOptimizationInfo result = new ConcreteCallSiteOptimizationInfo(this.size);
+ boolean allowConstantPropagation = appView.options().enablePropagationOfConstantsAtCallSites;
+ ConcreteCallSiteOptimizationInfo result =
+ new ConcreteCallSiteOptimizationInfo(this.size, allowConstantPropagation);
assert result.dynamicUpperBoundTypes != null;
for (int i = 0; i < result.size; i++) {
+ if (allowConstantPropagation) {
+ assert result.constants != null;
+ AbstractValue abstractValue =
+ getAbstractArgumentValue(i).join(other.getAbstractArgumentValue(i));
+ if (abstractValue.isNonTrivial()) {
+ result.constants.put(i, abstractValue);
+ }
+ }
+
TypeLatticeElement thisUpperBoundType = getDynamicUpperBoundType(i);
if (thisUpperBoundType == null) {
// This means the corresponding argument is primitive. The counterpart should be too.
@@ -82,6 +99,12 @@
public boolean hasUsefulOptimizationInfo(AppView<?> appView, DexEncodedMethod encodedMethod) {
TypeLatticeElement[] staticTypes = getStaticTypes(appView, encodedMethod);
for (int i = 0; i < size; i++) {
+ AbstractValue abstractValue = getAbstractArgumentValue(i);
+ if (abstractValue.isNonTrivial()) {
+ assert appView.options().enablePropagationOfConstantsAtCallSites;
+ return true;
+ }
+
if (!staticTypes[i].isReference()) {
continue;
}
@@ -89,6 +112,7 @@
if (dynamicUpperBoundType == null) {
continue;
}
+ assert appView.options().enablePropagationOfDynamicTypesAtCallSites;
// To avoid the full join of type lattices below, separately check if the nullability of
// arguments is improved, and if so, we can eagerly conclude that we've collected useful
// call site information for this method.
@@ -109,18 +133,43 @@
@Override
public TypeLatticeElement getDynamicUpperBoundType(int argIndex) {
assert 0 <= argIndex && argIndex < size;
+ assert dynamicUpperBoundTypes != null;
return dynamicUpperBoundTypes.getOrDefault(argIndex, null);
}
+ @Override
+ public AbstractValue getAbstractArgumentValue(int argIndex) {
+ assert 0 <= argIndex && argIndex < size;
+ // TODO(b/69963623): Remove this once enabled.
+ if (constants == null) {
+ return UnknownValue.getInstance();
+ }
+ return constants.getOrDefault(argIndex, UnknownValue.getInstance());
+ }
+
public static CallSiteOptimizationInfo fromArguments(
AppView<? extends AppInfoWithSubtyping> appView,
DexEncodedMethod method,
List<Value> inValues) {
- ConcreteCallSiteOptimizationInfo newCallSiteInfo = new ConcreteCallSiteOptimizationInfo(method);
+ boolean allowConstantPropagation = appView.options().enablePropagationOfConstantsAtCallSites;
+ ConcreteCallSiteOptimizationInfo newCallSiteInfo =
+ new ConcreteCallSiteOptimizationInfo(method, allowConstantPropagation);
assert newCallSiteInfo.size == inValues.size();
+ assert newCallSiteInfo.dynamicUpperBoundTypes != null;
for (int i = 0; i < newCallSiteInfo.size; i++) {
Value arg = inValues.get(i);
- // TODO(b/69963623): may need different place to store constants.
+ if (allowConstantPropagation) {
+ assert newCallSiteInfo.constants != null;
+ Value aliasedValue = arg.getAliasedValue();
+ if (!aliasedValue.isPhi()) {
+ AbstractValue abstractValue =
+ aliasedValue.definition.getAbstractValue(appView, method.method.holder);
+ if (abstractValue.isNonTrivial()) {
+ newCallSiteInfo.constants.put(i, abstractValue);
+ }
+ }
+ }
+
if (arg.getTypeLattice().isPrimitive()) {
continue;
}
@@ -151,18 +200,19 @@
return false;
}
ConcreteCallSiteOptimizationInfo otherInfo = (ConcreteCallSiteOptimizationInfo) other;
- assert this.dynamicUpperBoundTypes != null;
- return this.dynamicUpperBoundTypes.equals(otherInfo.dynamicUpperBoundTypes);
+ return Objects.equals(this.dynamicUpperBoundTypes, otherInfo.dynamicUpperBoundTypes)
+ && Objects.equals(this.constants, otherInfo.constants);
}
@Override
public int hashCode() {
assert this.dynamicUpperBoundTypes != null;
- return System.identityHashCode(dynamicUpperBoundTypes);
+ return System.identityHashCode(dynamicUpperBoundTypes) * 7 + System.identityHashCode(constants);
}
@Override
public String toString() {
- return dynamicUpperBoundTypes.toString();
+ return dynamicUpperBoundTypes.toString()
+ + (constants == null ? "" : (System.lineSeparator() + constants.toString()));
}
}
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 d8ef9c4..f59675e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -169,7 +169,8 @@
enableValuePropagation = false;
enableSideEffectAnalysis = false;
enableTreeShakingOfLibraryMethodOverrides = false;
- enableCallSiteOptimizationInfoPropagation = false;
+ enablePropagationOfDynamicTypesAtCallSites = false;
+ enablePropagationOfConstantsAtCallSites = false;
}
public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
@@ -216,7 +217,9 @@
public boolean enableNameReflectionOptimization = true;
public boolean enableStringConcatenationOptimization = true;
public boolean enableTreeShakingOfLibraryMethodOverrides = false;
- public boolean enableCallSiteOptimizationInfoPropagation = true;
+ public boolean enablePropagationOfDynamicTypesAtCallSites = true;
+ // TODO(b/69963623): enable if everything is ready, including signature rewriting at call sites.
+ public boolean enablePropagationOfConstantsAtCallSites = false;
public boolean encodeChecksums = false;
public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
@@ -1047,6 +1050,13 @@
enableNameReflectionOptimization = false;
}
+ // TODO(b/69963623): Remove this once enabled.
+ @VisibleForTesting
+ public void enablePropagationOfConstantsAtCallSites() {
+ assert !enablePropagationOfConstantsAtCallSites;
+ enablePropagationOfConstantsAtCallSites = true;
+ }
+
private boolean hasMinApi(AndroidApiLevel level) {
assert isGeneratingDex();
return minApiLevel >= level.getLevel();
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 4ff1850..9fe3bb4 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -431,9 +432,7 @@
ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
assertEquals(0, javaResult.exitCode);
- AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
- // Disable inlining to avoid the (short) tested method from being inlined and then removed.
- internalOptions -> internalOptions.enableInlining = false);
+ AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig, this::configure);
// Run processed (output) program on ART
ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
@@ -442,4 +441,13 @@
return processedApp;
}
+
+ private void configure(InternalOptions options) {
+ // Callees are invoked with a simple constant, e.g., "Bar". Propagating it into the callees
+ // bothers what the tests want to check, such as exact instructions in the body that include
+ // invocation kinds, like virtual call to a bridge.
+ options.enablePropagationOfConstantsAtCallSites = false;
+ // Disable inlining to avoid the (short) tested method from being inlined and then removed.
+ options.enableInlining = false;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 5e0e77f..a9385a6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -123,7 +123,7 @@
o.inliningInstructionLimit = 6;
// Tests depend on nullability of receiver and argument in general. Learning very accurate
// nullability from actual usage in tests bothers what we want to test.
- o.enableCallSiteOptimizationInfoPropagation = false;
+ o.enablePropagationOfDynamicTypesAtCallSites = false;
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
index c0ec39b..35e9636 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -44,6 +45,7 @@
.enableClassInliningAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(InternalOptions::enablePropagationOfConstantsAtCallSites)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null")
.inspect(this::inspect);
@@ -54,9 +56,8 @@
assertThat(main, isPresent());
MethodSubject test = main.uniqueMethodWithName("test");
assertThat(test, isPresent());
- // TODO(b/69963623):
- // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
- assertTrue(test.streamInstructions().anyMatch(InstructionSubject::isIf));
+ // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
+ assertTrue(test.streamInstructions().noneMatch(InstructionSubject::isIf));
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
index 5372bf3..0e82d6c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -43,6 +44,7 @@
.addKeepMainRule(MAIN)
.enableClassInliningAnnotations()
.enableInliningAnnotations()
+ .addOptionsModification(InternalOptions::enablePropagationOfConstantsAtCallSites)
.addOptionsModification(o -> {
// To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
o.enableDevirtualization = false;
@@ -60,16 +62,14 @@
assertThat(a, isPresent());
MethodSubject a_m = a.uniqueMethodWithName("m");
assertThat(a_m, isPresent());
- // TODO(b/69963623):
- // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
- assertTrue(a_m.streamInstructions().anyMatch(InstructionSubject::isIf));
+ // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
+ assertTrue(a_m.streamInstructions().noneMatch(InstructionSubject::isIf));
ClassSubject b = inspector.clazz(B.class);
assertThat(b, isPresent());
MethodSubject b_m = b.uniqueMethodWithName("m");
assertThat(b_m, isPresent());
- // TODO(b/69963623):
- // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
- assertTrue(b_m.streamInstructions().anyMatch(InstructionSubject::isIf));
+ // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
+ assertTrue(b_m.streamInstructions().noneMatch(InstructionSubject::isIf));
}
interface I {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
index f12d80d..f18d86d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -42,6 +43,7 @@
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(InternalOptions::enablePropagationOfConstantsAtCallSites)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null")
.inspect(this::inspect);
@@ -52,9 +54,8 @@
assertThat(main, isPresent());
MethodSubject test = main.uniqueMethodWithName("test");
assertThat(test, isPresent());
- // TODO(b/69963623):
- // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
- assertTrue(test.streamInstructions().anyMatch(InstructionSubject::isIf));
+ // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
+ assertTrue(test.streamInstructions().noneMatch(InstructionSubject::isIf));
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
index eaf5924..5673765 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -45,6 +46,7 @@
.enableClassInliningAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
+ .addOptionsModification(InternalOptions::enablePropagationOfConstantsAtCallSites)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null", "null")
.inspect(this::inspect);
@@ -56,9 +58,8 @@
MethodSubject a_m = a.uniqueMethodWithName("m");
assertThat(a_m, isPresent());
- // TODO(b/69963623):
- // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
- assertTrue(a_m.streamInstructions().anyMatch(InstructionSubject::isIf));
+ // Can optimize branches since `arg` is definitely "nul", i.e., not containing "null".
+ assertTrue(a_m.streamInstructions().noneMatch(InstructionSubject::isIf));
ClassSubject b = inspector.clazz(B.class);
assertThat(b, isPresent());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 90aeb7c..aa8e1d5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -135,7 +135,7 @@
// In `getMainClass`, a call with `null`, which will throw NPE, is replaced with null throwing
// code. Then, remaining call with non-null argument made getClass() replaceable.
// Disable the propagation of call site information to separate the tests.
- options.enableCallSiteOptimizationInfoPropagation = false;
+ options.enablePropagationOfDynamicTypesAtCallSites = false;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java
index 8869a01..0ac155c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -35,7 +36,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -53,6 +54,13 @@
.assertSuccessWithOutput(JAVA_OUTPUT);
}
+ private void configure(InternalOptions options) {
+ // This test wants to check if compile-time computation is not applied to non-null,
+ // non-constant value. In a simple test setting, call-site optimization knows the argument is
+ // always a non-null, specific constant, but that is beyond the scope of this test.
+ options.enablePropagationOfConstantsAtCallSites = false;
+ }
+
private void test(TestRunResult result, int expectedStringIsEmptyCount) throws Exception {
CodeInspector codeInspector = result.inspector();
ClassSubject mainClass = codeInspector.clazz(MAIN);
@@ -62,7 +70,7 @@
MethodSubject wrapper = mainClass.uniqueMethodWithName("wrapper");
assertThat(wrapper, isPresent());
- // Because of nullable, non-constant argument, isEmpty() should remain.
+ // Due to nullable, non-constant argument (w/o call-site optimization), isEmpty() should remain.
assertEquals(1, countCall(wrapper, "String", "isEmpty"));
}
@@ -74,7 +82,8 @@
testForD8()
.debug()
.addProgramClasses(MAIN)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(this::configure)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
test(result, 3);
@@ -83,7 +92,8 @@
testForD8()
.release()
.addProgramClasses(MAIN)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(this::configure)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
test(result, 1);
@@ -97,7 +107,8 @@
.enableProguardTestOptions()
.enableInliningAnnotations()
.addKeepMainRule(MAIN)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(this::configure)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT);
test(result, 1);
@@ -107,7 +118,7 @@
@NeverInline
static boolean wrapper(String arg) {
- // Cannot be computed at compile time.
+ // Cannot be computed at compile time (unless call-site optimization is enabled).
return arg.isEmpty();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index 85cf999..f9d9d21 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -58,7 +58,7 @@
// Disable the propagation of call site information to test String#valueOf optimization with
// nullable argument. Otherwise, e.g., we know that only `null` is used for `hideNPE`, and then
// simplify everything in that method.
- options.enableCallSiteOptimizationInfoPropagation = false;
+ options.enablePropagationOfDynamicTypesAtCallSites = false;
options.testing.forceNameReflectionOptimization = true;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java
index ecd6635..cc02426 100644
--- a/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java
@@ -83,7 +83,7 @@
// Call site optimization propagation will conclude that the input of B...Caller#call is
// always null, and replace the last call with null-throwing instruction.
// However, we want to test return type and parameter type are kept in this scenario.
- o.enableCallSiteOptimizationInfoPropagation = false;
+ o.enablePropagationOfDynamicTypesAtCallSites = false;
o.enableInlining = false;
})
.run(parameters.getRuntime(), MAIN)