Revert "Move insertion of assume-dynamic-type to AssumeInserter"
This reverts commit fc582f8d14277dee21727b950132664783ac5563.
Reason for revert: Fails on apps bot and chrome-200430
Change-Id: I34885ac76f0a014dc7dfc83b639f1d5fc7965ace
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index cf475fa..5781482 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -393,6 +393,7 @@
assert internal.neverMergePrefixes.contains("j$.");
// Assert some of R8 optimizations are disabled.
+ assert !internal.enableDynamicTypeOptimization;
assert !internal.enableInlining;
assert !internal.enableClassInlining;
assert !internal.enableHorizontalClassMerging;
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 9b0f9de..b1072df 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -162,6 +162,7 @@
assert !internal.passthroughDexCode;
// Assert some of R8 optimizations are disabled.
+ assert !internal.enableDynamicTypeOptimization;
assert !internal.enableInlining;
assert !internal.enableClassInlining;
assert !internal.enableHorizontalClassMerging;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 5abbc25..656f3aa 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -842,6 +842,7 @@
? LineNumberOptimization.ON
: LineNumberOptimization.OFF;
+ assert internal.enableDynamicTypeOptimization || !proguardConfiguration.isOptimizing();
assert internal.enableHorizontalClassMerging || !proguardConfiguration.isOptimizing();
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
assert internal.enableVerticalClassMerging || !proguardConfiguration.isOptimizing();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 7aed1d9..117af7b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
@@ -15,6 +16,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.utils.BooleanUtils;
import java.util.Objects;
import java.util.Set;
@@ -23,11 +25,11 @@
private static final String ERROR_MESSAGE =
"Expected Assume instructions to be removed after IR processing.";
- private DynamicTypeAssumption dynamicTypeAssumption;
+ private final DynamicTypeAssumption dynamicTypeAssumption;
private final NonNullAssumption nonNullAssumption;
private final Instruction origin;
- public Assume(
+ private Assume(
DynamicTypeAssumption dynamicTypeAssumption,
NonNullAssumption nonNullAssumption,
Value dest,
@@ -36,6 +38,9 @@
AppView<?> appView) {
super(dest, src);
assert dynamicTypeAssumption != null || nonNullAssumption != null;
+ assert BooleanUtils.intValue(dynamicTypeAssumption != null)
+ + BooleanUtils.intValue(nonNullAssumption != null)
+ == 1;
assert dynamicTypeAssumption == null
|| dynamicTypeAssumption.verifyCorrectnessOfValues(dest, src, appView);
assert nonNullAssumption == null
@@ -73,7 +78,7 @@
}
public boolean verifyInstructionIsNeeded(AppView<?> appView) {
- if (hasDynamicTypeAssumption()) {
+ if (isAssumeDynamicType()) {
assert dynamicTypeAssumption.verifyCorrectnessOfValues(outValue(), src(), appView);
}
return true;
@@ -107,7 +112,13 @@
@Override
public String getInstructionName() {
- return "Assume";
+ if (isAssumeDynamicType()) {
+ return "AssumeDynamicType";
+ }
+ if (isAssumeNonNull()) {
+ return "AssumeNonNull";
+ }
+ throw new Unimplemented();
}
@Override
@@ -120,15 +131,13 @@
return this;
}
- public boolean hasDynamicTypeAssumption() {
+ @Override
+ public boolean isAssumeDynamicType() {
return dynamicTypeAssumption != null;
}
- public void unsetDynamicTypeAssumption() {
- dynamicTypeAssumption = null;
- }
-
- public boolean hasNonNullAssumption() {
+ @Override
+ public boolean isAssumeNonNull() {
return nonNullAssumption != null;
}
@@ -140,7 +149,7 @@
if (outType.isPrimitiveType()) {
return false;
}
- if (hasDynamicTypeAssumption()) {
+ if (isAssumeDynamicType()) {
outType = dynamicTypeAssumption.getDynamicUpperBoundType();
}
if (appView.appInfo().hasLiveness()) {
@@ -211,11 +220,14 @@
@Override
public TypeElement evaluate(AppView<?> appView) {
- if (hasNonNullAssumption()) {
+ if (isAssumeDynamicType()) {
+ return src().getType();
+ }
+ if (isAssumeNonNull()) {
assert src().getType().isReferenceType();
return src().getType().asReferenceType().asMeetWithNotNull();
}
- return src().getType();
+ throw new Unimplemented();
}
@Override
@@ -243,14 +255,15 @@
assert super.verifyTypes(appView);
TypeElement inType = src().getType();
- assert inType.isReferenceType() : inType;
-
TypeElement outType = getOutType();
- if (hasNonNullAssumption()) {
- assert inType.isNullType() || outType.equals(inType.asReferenceType().asMeetWithNotNull())
+ if (isAssumeDynamicType()) {
+ assert inType.isReferenceType() : inType;
+ assert outType.equals(inType)
: "At " + this + System.lineSeparator() + outType + " != " + inType;
} else {
- assert outType.equals(inType)
+ assert isAssumeNonNull() : this;
+ assert inType.isReferenceType() : inType;
+ assert inType.isNullType() || outType.equals(inType.asReferenceType().asMeetWithNotNull())
: "At " + this + System.lineSeparator() + outType + " != " + inType;
}
return true;
@@ -263,21 +276,21 @@
// assumption became "truth."
// 2) invoke-interface could be devirtualized, while its dynamic type and/or non-null receiver
// are still valid.
- StringBuilder builder = new StringBuilder(super.toString());
- if (hasNonNullAssumption()) {
- builder.append("; not null");
+ String originString =
+ origin.hasBlock() ? " (origin: `" + origin.toString() + "`)" : " (obsolete origin)";
+ if (isAssumeNonNull()) {
+ return super.toString() + originString;
}
- if (hasDynamicTypeAssumption()) {
- if (hasOutValue()) {
- if (!dynamicTypeAssumption.dynamicUpperBoundType.equalUpToNullability(outValue.getType())) {
- builder.append("; upper bound: ").append(dynamicTypeAssumption.dynamicUpperBoundType);
- }
- }
- if (dynamicTypeAssumption.dynamicLowerBoundType != null) {
- builder.append("; lower bound: ").append(dynamicTypeAssumption.dynamicLowerBoundType);
- }
+ if (isAssumeDynamicType()) {
+ return super.toString()
+ + "; upper bound: "
+ + dynamicTypeAssumption.dynamicUpperBoundType
+ + (dynamicTypeAssumption.dynamicLowerBoundType != null
+ ? "; lower bound: " + dynamicTypeAssumption.dynamicLowerBoundType
+ : "")
+ + originString;
}
- return builder.toString();
+ return super.toString();
}
public static class DynamicTypeAssumption {
@@ -285,7 +298,7 @@
private final TypeElement dynamicUpperBoundType;
private final ClassTypeElement dynamicLowerBoundType;
- public DynamicTypeAssumption(
+ private DynamicTypeAssumption(
TypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
this.dynamicUpperBoundType = dynamicUpperBoundType;
this.dynamicLowerBoundType = dynamicLowerBoundType;
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 9197455..a0af5e2 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
@@ -609,17 +609,17 @@
// We can only type check the program if we have subtyping information. Therefore, we do not
// require that the program type checks in D8.
if (appView.enableWholeProgramOptimizations()) {
- assert validAssumeInstructions(appView);
+ assert validAssumeDynamicTypeInstructions(appView);
assert new TypeChecker(appView.withLiveness()).check(this);
}
assert blocks.stream().allMatch(block -> block.verifyTypes(appView));
return true;
}
- private boolean validAssumeInstructions(AppView<?> appView) {
+ private boolean validAssumeDynamicTypeInstructions(AppView<?> appView) {
for (BasicBlock block : blocks) {
for (Instruction instruction : block.getInstructions()) {
- if (instruction.isAssume()) {
+ if (instruction.isAssumeDynamicType()) {
assert instruction.asAssume().verifyInstructionIsNeeded(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 6fd3f9b..4682ecc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -717,12 +717,12 @@
return null;
}
- public final boolean isAssumeWithDynamicTypeAssumption() {
- return isAssume() && asAssume().hasDynamicTypeAssumption();
+ public boolean isAssumeDynamicType() {
+ return false;
}
- public final boolean isAssumeWithNonNullAssumption() {
- return isAssume() && asAssume().hasNonNullAssumption();
+ public boolean isAssumeNonNull() {
+ return false;
}
public boolean isBinop() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index f3a889f..a2aa502 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -150,8 +150,7 @@
return false;
}
// Check that the receiver information comes from a dynamic type.
- if (!getReceiver()
- .isDefinedByInstructionSatisfying(Instruction::isAssumeWithDynamicTypeAssumption)) {
+ if (!getReceiver().definition.isAssumeDynamicType()) {
return false;
}
// Now, it can be that the upper bound is more precise than the lower:
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index be5340c..f49c1e3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -504,10 +504,6 @@
return debugData != null && !debugData.users.isEmpty();
}
- public boolean hasNonDebugUsers() {
- return hasUsers() || hasPhiUsers();
- }
-
public boolean hasPhiUsers() {
return !phiUsers.isEmpty();
}
@@ -954,8 +950,8 @@
*/
public boolean isNeverNull() {
assert type.isReferenceType();
- return isDefinedByInstructionSatisfying(Instruction::isAssumeWithNonNullAssumption)
- || type.isDefinitelyNotNull();
+ return (definition != null && definition.isAssumeNonNull())
+ || type.nullability().isDefinitelyNotNull();
}
public boolean isArgument() {
@@ -1174,19 +1170,14 @@
Value root = getAliasedValue();
if (root.isPhi()) {
assert getSpecificAliasedValue(
- value ->
- value.isDefinedByInstructionSatisfying(
- Instruction::isAssumeWithDynamicTypeAssumption))
+ value -> !value.isPhi() && value.definition.isAssumeDynamicType())
== null;
return root.getDynamicUpperBoundType(appView);
}
// Try to find an alias of the receiver, which is defined by an instruction of the type Assume.
Value aliasedValue =
- getSpecificAliasedValue(
- value ->
- value.isDefinedByInstructionSatisfying(
- Instruction::isAssumeWithDynamicTypeAssumption));
+ getSpecificAliasedValue(value -> !value.isPhi() && value.definition.isAssumeDynamicType());
TypeElement lattice;
if (aliasedValue != null) {
// If there is an alias of the receiver, which is defined by an Assume instruction that
@@ -1239,8 +1230,7 @@
}
// Try to find an alias of the receiver, which is defined by an instruction of the type Assume.
- Value aliasedValue =
- getSpecificAliasedValue(value -> value.definition.isAssumeWithDynamicTypeAssumption());
+ Value aliasedValue = getSpecificAliasedValue(value -> value.definition.isAssumeDynamicType());
if (aliasedValue != null) {
ClassTypeElement lattice =
aliasedValue.definition.asAssume().getDynamicTypeAssumption().getDynamicLowerBoundType();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 9d55dda..844ccce 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -53,6 +53,7 @@
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.AssumeInserter;
+import com.android.tools.r8.ir.optimize.Assumer;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
import com.android.tools.r8.ir.optimize.CodeRewriter;
@@ -166,7 +167,8 @@
private final EnumValueOptimizer enumValueOptimizer;
private final EnumUnboxer enumUnboxer;
- public final AssumeInserter assumeInserter;
+ // Assumers that will insert Assume instructions.
+ public final Collection<Assumer> assumers = new ArrayList<>();
private final DynamicTypeOptimization dynamicTypeOptimization;
final AssertionsRewriter assertionsRewriter;
@@ -262,7 +264,6 @@
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
this.enumUnboxer = null;
- this.assumeInserter = null;
return;
}
this.lambdaRewriter =
@@ -290,12 +291,20 @@
assert appView.appInfo().hasLiveness();
assert appView.rootSet() != null;
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- assumeInserter = new AssumeInserter(appViewWithLiveness);
+ if (options.enableNonNullTracking) {
+ assumers.add(new AssumeInserter(appViewWithLiveness));
+ }
this.classInliner =
options.enableClassInlining && options.enableInlining ? new ClassInliner() : null;
this.classStaticizer =
options.enableClassStaticizer ? new ClassStaticizer(appViewWithLiveness, this) : null;
- this.dynamicTypeOptimization = new DynamicTypeOptimization(appViewWithLiveness);
+ this.dynamicTypeOptimization =
+ options.enableDynamicTypeOptimization
+ ? new DynamicTypeOptimization(appViewWithLiveness)
+ : null;
+ if (dynamicTypeOptimization != null) {
+ assumers.add(dynamicTypeOptimization);
+ }
this.fieldAccessAnalysis =
FieldAccessAnalysis.enable(options) ? new FieldAccessAnalysis(appViewWithLiveness) : null;
this.libraryMethodOverrideAnalysis =
@@ -338,7 +347,6 @@
this.enumValueOptimizer =
options.enableEnumValueOptimization ? new EnumValueOptimizer(appViewWithLiveness) : null;
} else {
- this.assumeInserter = null;
this.classInliner = null;
this.classStaticizer = null;
this.dynamicTypeOptimization = null;
@@ -1251,9 +1259,9 @@
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
- if (assumeInserter != null) {
- assumeInserter.insertAssumeInstructions(code, timing);
- }
+ timing.begin("Insert assume instructions");
+ CodeRewriter.insertAssumeInstructions(code, assumers, timing);
+ timing.end();
previous = printMethod(code, "IR after inserting assume instructions (SSA)", previous);
@@ -1589,7 +1597,7 @@
timing.end();
}
- if (assumeInserter != null) {
+ if (!assumers.isEmpty()) {
timing.begin("Remove assume instructions");
CodeRewriter.removeAssumeInstructions(appView, code);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index e54ec6c..7211439 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -44,6 +44,8 @@
void methodReturnValueOnlyDependsOnArguments(DexEncodedMethod method);
+ void methodNeverReturnsNull(DexEncodedMethod method);
+
void methodNeverReturnsNormally(DexEncodedMethod method);
void markProcessed(DexEncodedMethod method, ConstraintWithTarget state);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
similarity index 60%
rename from src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
rename to src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
index 41f72cb..7f45db3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
@@ -16,68 +16,60 @@
import java.util.Set;
/**
- * When we have Assume instructions we generally verify that the Assume instructions contribute with
- * non-trivial information to the IR (e.g., the dynamic type should be more precise than the static
- * type).
+ * When we have Assume instructions with a DynamicTypeAssumption we generally verify that the
+ * dynamic type in the Assume node is always at least as precise as the static type of the
+ * corresponding value.
*
* <p>Therefore, when this property may no longer hold for an Assume instruction, we need to remove
* it.
*
* <p>This class is a helper class to remove these instructions. Unlike {@link
* CodeRewriter#removeAssumeInstructions} this class does not unconditionally remove all Assume
- * instructions.
+ * instructions, not does it remove all Assume instructions with a DynamicTypeAssumption.
*/
-public class AssumeRemover {
+public class AssumeDynamicTypeRemover {
private final AppView<?> appView;
private final IRCode code;
private final Set<Value> affectedValues = Sets.newIdentityHashSet();
- private final Set<Assume> assumeInstructionsToRemove = Sets.newIdentityHashSet();
+ private final Set<Assume> assumeDynamicTypeInstructionsToRemove = Sets.newIdentityHashSet();
private boolean mayHaveIntroducedTrivialPhi = false;
- public AssumeRemover(AppView<?> appView, IRCode code) {
+ public AssumeDynamicTypeRemover(AppView<?> appView, IRCode code) {
this.appView = appView;
this.code = code;
}
- public Set<Value> getAffectedValues() {
- return affectedValues;
- }
-
public boolean mayHaveIntroducedTrivialPhi() {
return mayHaveIntroducedTrivialPhi;
}
- public void markAssumeDynamicTypeUsersForRemoval(Value value) {
+ public void markForRemoval(Assume assumeDynamicTypeInstruction) {
+ assumeDynamicTypeInstructionsToRemove.add(assumeDynamicTypeInstruction);
+ }
+
+ public void markUsersForRemoval(Value value) {
for (Instruction user : value.aliasedUsers()) {
- if (user.isAssume()) {
- Assume assumeInstruction = user.asAssume();
- assumeInstruction.unsetDynamicTypeAssumption();
- if (!assumeInstruction.hasNonNullAssumption()) {
- assumeInstruction.unsetDynamicTypeAssumption();
- }
+ if (user.isAssumeDynamicType()) {
+ markForRemoval(user.asAssume());
}
}
}
- private void markForRemoval(Assume assumeInstruction) {
- assumeInstructionsToRemove.add(assumeInstruction);
- }
-
public void removeIfMarked(
- Assume assumeInstruction, InstructionListIterator instructionIterator) {
- if (assumeInstructionsToRemove.remove(assumeInstruction)) {
- Value inValue = assumeInstruction.src();
- Value outValue = assumeInstruction.outValue();
+ Assume assumeDynamicTypeInstruction, InstructionListIterator instructionIterator) {
+ if (assumeDynamicTypeInstructionsToRemove.remove(assumeDynamicTypeInstruction)) {
+ Value inValue = assumeDynamicTypeInstruction.src();
+ Value outValue = assumeDynamicTypeInstruction.outValue();
// Check if we need to run the type analysis for the affected values of the out-value.
if (!outValue.getType().equals(inValue.getType())) {
affectedValues.addAll(outValue.affectedValues());
}
- if (outValue.hasPhiUsers()) {
+ if (outValue.numberOfPhiUsers() > 0) {
mayHaveIntroducedTrivialPhi = true;
}
@@ -86,20 +78,16 @@
}
}
- public AssumeRemover removeMarkedInstructions() {
- return removeMarkedInstructions(null);
- }
-
- public AssumeRemover removeMarkedInstructions(Set<BasicBlock> blocksToBeRemoved) {
- if (!assumeInstructionsToRemove.isEmpty()) {
+ public AssumeDynamicTypeRemover removeMarkedInstructions(Set<BasicBlock> blocksToBeRemoved) {
+ if (!assumeDynamicTypeInstructionsToRemove.isEmpty()) {
for (BasicBlock block : code.blocks) {
- if (blocksToBeRemoved != null && blocksToBeRemoved.contains(block)) {
+ if (blocksToBeRemoved.contains(block)) {
continue;
}
InstructionListIterator instructionIterator = block.listIterator(code);
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
- if (instruction.isAssume()) {
+ if (instruction.isAssumeDynamicType()) {
removeIfMarked(instruction.asAssume(), instructionIterator);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index b4ccb7d..611a0a1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -3,19 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
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.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
@@ -32,11 +29,8 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TriConsumer;
-import com.android.tools.r8.utils.TriFunction;
-import com.android.tools.r8.utils.TriPredicate;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
@@ -50,23 +44,24 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.function.Consumer;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
import java.util.function.Predicate;
-public class AssumeInserter {
+public class AssumeInserter implements Assumer {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final InternalOptions options;
public AssumeInserter(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
- this.options = appView.options();
}
+ @Override
public void insertAssumeInstructions(IRCode code, Timing timing) {
insertAssumeInstructionsInBlocks(code, code.listIterator(), alwaysTrue(), timing);
}
+ @Override
public void insertAssumeInstructionsInBlocks(
IRCode code,
BasicBlockIterator blockIterator,
@@ -94,7 +89,7 @@
timing.end();
timing.begin("Part 3: Compute dominated users");
- Map<Instruction, Map<Value, AssumedValueInfo>> redundantAssumedValues =
+ Map<Instruction, Set<Value>> redundantKeys =
computeDominanceForAssumedValues(code, assumedValues);
timing.end();
if (assumedValues.isEmpty()) {
@@ -102,7 +97,7 @@
}
timing.begin("Part 4: Remove redundant dominated assume instructions");
- removeRedundantDominatedAssumeInstructions(assumedValues, redundantAssumedValues);
+ removeRedundantDominatedAssumeInstructions(assumedValues, redundantKeys);
timing.end();
if (assumedValues.isEmpty()) {
return;
@@ -150,15 +145,29 @@
}
}
+ Value outValue = current.outValue();
if (current.isInvokeMethod()) {
- // Case (2) and (3).
- needsAssumeInstruction |=
- computeAssumedValuesForInvokeMethod(
- code, current.asInvokeMethod(), assumedValuesBuilder);
+ InvokeMethod invoke = current.asInvokeMethod();
+ if (invoke.hasOutValue() || !invoke.getInvokedMethod().proto.parameters.isEmpty()) {
+ // Case (2) and (3).
+ needsAssumeInstruction |=
+ computeAssumedValuesFromSingleTarget(code, invoke, assumedValuesBuilder);
+ }
} else if (current.isFieldGet()) {
// Case (4), field-get instructions that are guaranteed to read a non-null value.
- needsAssumeInstruction |=
- computeAssumedValuesForFieldGet(current.asFieldInstruction(), assumedValuesBuilder);
+ FieldInstruction fieldInstruction = current.asFieldInstruction();
+ DexField field = fieldInstruction.getField();
+ if (isNullableReferenceTypeWithNonDebugUsers(outValue)) {
+ DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
+ if (encodedField != null) {
+ FieldOptimizationInfo optimizationInfo = encodedField.getOptimizationInfo();
+ if (optimizationInfo.getDynamicUpperBoundType() != null
+ && optimizationInfo.getDynamicUpperBoundType().isDefinitelyNotNull()) {
+ assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(current, outValue);
+ needsAssumeInstruction = true;
+ }
+ }
+ }
}
// If we need to insert an assume instruction into a block with catch handlers, we split the
@@ -193,35 +202,6 @@
}
}
- private boolean computeAssumedValuesForInvokeMethod(
- IRCode code, InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
- if (!invoke.hasOutValue() && invoke.getInvokedMethod().proto.parameters.isEmpty()) {
- return false;
- }
-
- DexMethod invokedMethod = invoke.getInvokedMethod();
- if (invokedMethod.holder.isArrayType()
- && invokedMethod.match(appView.dexItemFactory().objectMembers.clone)) {
- return computeAssumedValuesFromArrayClone(invoke, assumedValuesBuilder);
- }
-
- return computeAssumedValuesFromSingleTarget(code, invoke, assumedValuesBuilder);
- }
-
- private boolean computeAssumedValuesFromArrayClone(
- InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
- Value outValue = invoke.outValue();
- if (outValue == null || !outValue.hasNonDebugUsers()) {
- return false;
- }
-
- TypeElement dynamicUpperBoundType =
- TypeElement.fromDexType(invoke.getInvokedMethod().holder, definitelyNotNull(), appView);
- assumedValuesBuilder.addAssumedValueKnownToDominateAllUsers(
- invoke, outValue, dynamicUpperBoundType, null);
- return true;
- }
-
private boolean computeAssumedValuesFromSingleTarget(
IRCode code, InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
@@ -234,13 +214,11 @@
// Case (2), invocations that are guaranteed to return a non-null value.
Value outValue = invoke.outValue();
- if (outValue != null && outValue.hasNonDebugUsers()) {
- needsAssumeInstruction =
- computeAssumedValuesForOutValue(
- invoke,
- optimizationInfo.getDynamicUpperBoundTypeOrElse(outValue.getType()),
- optimizationInfo.getDynamicLowerBoundType(),
- assumedValuesBuilder);
+ if (outValue != null
+ && optimizationInfo.neverReturnsNull()
+ && isNullableReferenceTypeWithNonDebugUsers(outValue)) {
+ assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(invoke, outValue);
+ needsAssumeInstruction = true;
}
// Case (3), parameters that are not null after the invocation.
@@ -261,116 +239,31 @@
return needsAssumeInstruction;
}
- private boolean computeAssumedValuesForFieldGet(
- FieldInstruction fieldGet, AssumedValues.Builder assumedValuesBuilder) {
- Value outValue = fieldGet.outValue();
- if (!outValue.hasNonDebugUsers()) {
- return false;
- }
-
- DexEncodedField field = appView.appInfo().resolveField(fieldGet.getField()).getResolvedField();
- if (field == null) {
- return false;
- }
-
- FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
- return computeAssumedValuesForOutValue(
- fieldGet,
- optimizationInfo.getDynamicUpperBoundTypeOrElse(outValue.getType()),
- optimizationInfo.getDynamicLowerBoundType(),
- assumedValuesBuilder);
- }
-
- private boolean computeAssumedValuesForOutValue(
- Instruction instruction,
- TypeElement dynamicUpperBoundType,
- ClassTypeElement dynamicLowerBoundType,
- AssumedValues.Builder assumedValuesBuilder) {
- Value outValue = instruction.outValue();
-
- // Do not insert dynamic type information if it does not refine the static type.
- boolean isRedundant =
- !dynamicUpperBoundType.strictlyLessThan(outValue.getType(), appView)
- && dynamicLowerBoundType == null;
- if (isRedundant) {
- return false;
- }
-
- // Do not insert dynamic type information if the dynamic type only refines the nullability.
- if (dynamicUpperBoundType.equalUpToNullability(outValue.getType())
- && dynamicLowerBoundType == null) {
- assert dynamicUpperBoundType.isDefinitelyNotNull();
- assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(instruction, outValue);
- } else {
- assumedValuesBuilder.addAssumedValueKnownToDominateAllUsers(
- instruction, outValue, dynamicUpperBoundType, dynamicLowerBoundType);
- }
- return true;
- }
-
private void removeRedundantAssumeInstructions(AssumedValues assumedValues) {
assumedValues.removeIf(
- (instruction, assumedValue, assumedValueInfo) -> {
- // Assumed values with dynamic type information are never redundant.
- if (assumedValueInfo.hasDynamicTypeInfo()) {
- return false;
- }
-
- assert assumedValueInfo.isNonNull();
-
- // Otherwise, it is redundant if it is defined by another instruction that guarantees its
- // non-nullness.
+ (instruction, assumedValue) -> {
if (assumedValue.isPhi()) {
return false;
}
-
Instruction definition = assumedValue.definition;
- if (definition == instruction) {
- return false;
- }
-
- AssumedValueInfo otherAssumedValueInfo =
- assumedValues.getAssumedValueInfo(definition, assumedValue);
- if (otherAssumedValueInfo == null) {
- return false;
- }
-
- if (!otherAssumedValueInfo.isNonNull()) {
- // This is not redundant, but we can strenghten it with the dynamic type information
- // from the other assume instruction.
- assumedValueInfo.setDynamicTypeAssumption(
- otherAssumedValueInfo.getDynamicTypeAssumption());
- return false;
- }
-
- return true;
+ return definition != instruction && assumedValues.contains(definition, assumedValue);
});
}
- private Map<Instruction, Map<Value, AssumedValueInfo>> computeDominanceForAssumedValues(
+ private Map<Instruction, Set<Value>> computeDominanceForAssumedValues(
IRCode code, AssumedValues assumedValues) {
- Map<Instruction, Map<Value, AssumedValueInfo>> redundantAssumedValues = new IdentityHashMap<>();
+ Map<Instruction, Set<Value>> redundantKeys = new IdentityHashMap<>();
LazyDominatorTree lazyDominatorTree = new LazyDominatorTree(code);
Map<BasicBlock, Set<BasicBlock>> dominatedBlocksCache = new IdentityHashMap<>();
assumedValues.computeDominance(
- (instruction, assumedValue, assumedValueInfo) -> {
- Map<Value, AssumedValueInfo> alreadyAssumedValues =
- redundantAssumedValues.get(instruction);
- if (alreadyAssumedValues != null) {
- AssumedValueInfo alreadyAssumedValueInfo = alreadyAssumedValues.get(assumedValue);
- if (alreadyAssumedValueInfo != null) {
- if (assumedValueInfo.isSubsumedBy(alreadyAssumedValueInfo)) {
- // Returning redundant() will cause the entry (instruction, assumedValue) to be
- // removed.
- return AssumedDominance.redundant();
- }
-
- // This assume value is dominated by the other assume value, so strengthen this one.
- assumedValueInfo.strengthenWith(alreadyAssumedValueInfo);
- }
+ (instruction, assumedValue) -> {
+ Set<Value> alreadyAssumedValues = redundantKeys.get(instruction);
+ if (alreadyAssumedValues != null && alreadyAssumedValues.contains(assumedValue)) {
+ // Returning redundant() will cause the entry (instruction, assumedValue) to be removed.
+ return AssumedDominance.redundant();
}
- // If this value is the out-value of some instruction it is known to dominate all users.
+ // If this value is non-null since its definition, then it is known to dominate all users.
if (assumedValue == instruction.outValue()) {
return AssumedDominance.everything();
}
@@ -434,9 +327,9 @@
// Record that there is no need to insert an assume instruction for the non-null-value
// after the given user in case the user is also a null check for the non-null-value.
- redundantAssumedValues
- .computeIfAbsent(user, ignore -> new IdentityHashMap<>())
- .put(assumedValue, assumedValueInfo);
+ redundantKeys
+ .computeIfAbsent(user, ignore -> Sets.newIdentityHashSet())
+ .add(assumedValue);
}
}
for (Phi user : assumedValue.uniquePhiUsers()) {
@@ -448,92 +341,27 @@
}
return dominance.build();
});
- return redundantAssumedValues;
+ return redundantKeys;
}
private void removeRedundantDominatedAssumeInstructions(
- AssumedValues assumedValues,
- Map<Instruction, Map<Value, AssumedValueInfo>> redundantAssumedValues) {
- assumedValues.removeAll(redundantAssumedValues);
+ AssumedValues assumedValues, Map<Instruction, Set<Value>> redundantKeys) {
+ assumedValues.removeAll(redundantKeys);
}
private void materializeAssumeInstructions(IRCode code, AssumedValues assumedValues) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
Map<BasicBlock, Map<Instruction, List<Instruction>>> pendingInsertions =
new IdentityHashMap<>();
-
- // We materialize the assume instructions in two steps. First, we materialize all the assume
- // instructions that do not dominate everything. These assume instructions can refine previous
- // assume instructions, so we materialize those first as they are "stronger".
- //
- // Example:
- // 1. Object value = getNullableValueWithDynamicType();
- // 2. Object nullableValueWithDynamicType = assume(value, ...)
- // 3. checkNotNull(value);
- // 4. Object nonNullValueWithDynamicType = assume(value, ...)
- // 5. return value;
- //
- // In this example, we first materialize the assume instruction in line 4, and replace the
- // dominated use of `value` in line 5 by the new assumed value `nonNullValueWithDynamicType`.
- // Afterwards, we materialize the assume instruction in line 2, and replace all remaining users
- // of `value` by `nullableValueWithDynamicType`.
- //
- // Result:
- // 1. Object value = getNullableValueWithDynamicType();
- // 2. Object nullableValueWithDynamicType = assume(value, ...)
- // 3. checkNotNull(nullableValueWithDynamicType);
- // 4. Object nonNullValueWithDynamicType = assume(value, ...)
- // 5. return nonNullValueWithDynamicType;
- materializeSelectedAssumeInstructions(
- code,
- assumedValues,
- affectedValues,
- pendingInsertions,
- assumedValueInfo -> !assumedValueInfo.dominance.isEverything());
- materializeSelectedAssumeInstructions(
- code,
- assumedValues,
- affectedValues,
- pendingInsertions,
- assumedValueInfo -> assumedValueInfo.dominance.isEverything());
- pendingInsertions.forEach(
- (block, pendingInsertionsPerInstruction) -> {
- InstructionListIterator instructionIterator = block.listIterator(code);
- while (instructionIterator.hasNext() && !pendingInsertionsPerInstruction.isEmpty()) {
- Instruction instruction = instructionIterator.next();
- List<Instruction> pendingAssumeInstructions =
- pendingInsertionsPerInstruction.remove(instruction);
- if (pendingAssumeInstructions != null) {
- pendingAssumeInstructions.forEach(instructionIterator::add);
- }
- }
- });
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
- }
-
- private void materializeSelectedAssumeInstructions(
- IRCode code,
- AssumedValues assumedValues,
- Set<Value> affectedValues,
- Map<BasicBlock, Map<Instruction, List<Instruction>>> pendingInsertions,
- Predicate<AssumedValueInfo> predicate) {
- assumedValues.removeIf(
+ assumedValues.forEach(
(instruction, assumedValue, assumedValueInfo) -> {
- if (!predicate.test(assumedValueInfo)) {
- return false;
- }
-
BasicBlock block = instruction.getBlock();
BasicBlock insertionBlock = getInsertionBlock(instruction);
AssumedDominance dominance = assumedValueInfo.getDominance();
Value newValue =
code.createValue(
- assumedValueInfo.isNonNull()
- ? assumedValue.getType().asReferenceType().asMeetWithNotNull()
- : assumedValue.getType(),
+ assumedValue.getType().asReferenceType().asMeetWithNotNull(),
assumedValue.getLocalInfo());
if (dominance.isEverything()) {
assumedValue.replaceUsers(newValue);
@@ -550,7 +378,8 @@
while (iterator.hasNext()) {
Value operand = user.getOperand(iterator.nextInt());
if (operand != assumedValue) {
- assert operand.isDefinedByInstructionSatisfying(Instruction::isAssume);
+ assert operand.isDefinedByInstructionSatisfying(
+ Instruction::isAssumeNonNull);
iterator.remove();
}
}
@@ -563,13 +392,7 @@
affectedValues.addAll(newValue.affectedValues());
Assume assumeInstruction =
- new Assume(
- assumedValueInfo.dynamicTypeAssumption,
- assumedValueInfo.nonNullAssumption,
- newValue,
- assumedValue,
- instruction,
- appView);
+ Assume.createAssumeNonNullInstruction(newValue, assumedValue, instruction, appView);
assumeInstruction.setPosition(instruction.getPosition());
if (insertionBlock != block) {
insertionBlock.listIterator(code).add(assumeInstruction);
@@ -579,8 +402,22 @@
.computeIfAbsent(instruction, ignore -> new ArrayList<>())
.add(assumeInstruction);
}
- return true;
});
+ pendingInsertions.forEach(
+ (block, pendingInsertionsPerInstruction) -> {
+ InstructionListIterator instructionIterator = block.listIterator(code);
+ while (instructionIterator.hasNext() && !pendingInsertionsPerInstruction.isEmpty()) {
+ Instruction instruction = instructionIterator.next();
+ List<Instruction> pendingAssumeInstructions =
+ pendingInsertionsPerInstruction.remove(instruction);
+ if (pendingAssumeInstructions != null) {
+ pendingAssumeInstructions.forEach(instructionIterator::add);
+ }
+ }
+ });
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
}
private BasicBlock getInsertionBlock(Instruction instruction) {
@@ -624,6 +461,10 @@
return type.isReferenceType() && type.asReferenceType().isNullable();
}
+ private static boolean isNullableReferenceTypeWithNonDebugUsers(Value value) {
+ return isNullableReferenceType(value) && value.numberOfAllNonDebugUsers() > 0;
+ }
+
private static boolean isNullableReferenceTypeWithOtherNonDebugUsers(
Value value, Instruction ignore) {
if (isNullableReferenceType(value)) {
@@ -642,7 +483,6 @@
static class AssumedValueInfo {
AssumedDominance dominance;
- DynamicTypeAssumption dynamicTypeAssumption;
NonNullAssumption nonNullAssumption;
AssumedValueInfo(AssumedDominance dominance) {
@@ -657,47 +497,9 @@
this.dominance = dominance;
}
- boolean hasDynamicTypeInfo() {
- return dynamicTypeAssumption != null;
- }
-
- DynamicTypeAssumption getDynamicTypeAssumption() {
- return dynamicTypeAssumption;
- }
-
- void setDynamicTypeAssumption(DynamicTypeAssumption dynamicTypeAssumption) {
- this.dynamicTypeAssumption = dynamicTypeAssumption;
- }
-
- void setDynamicTypeAssumption(
- TypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
- dynamicTypeAssumption =
- new DynamicTypeAssumption(dynamicUpperBoundType, dynamicLowerBoundType);
- if (dynamicUpperBoundType.isDefinitelyNotNull()) {
- setNotNull();
- }
- }
-
- boolean isNonNull() {
- return nonNullAssumption != null;
- }
-
void setNotNull() {
nonNullAssumption = NonNullAssumption.get();
}
-
- boolean isSubsumedBy(AssumedValueInfo other) {
- return !hasDynamicTypeInfo() && other.isNonNull();
- }
-
- void strengthenWith(AssumedValueInfo info) {
- if (info.isNonNull()) {
- setNotNull();
- }
- if (!hasDynamicTypeInfo() && info.hasDynamicTypeInfo()) {
- setDynamicTypeAssumption(info.getDynamicTypeAssumption());
- }
- }
}
static class AssumedValues {
@@ -717,8 +519,7 @@
return new Builder();
}
- void computeDominance(
- TriFunction<Instruction, Value, AssumedValueInfo, AssumedDominance> function) {
+ void computeDominance(BiFunction<Instruction, Value, AssumedDominance> function) {
Iterator<Entry<Instruction, Map<Value, AssumedValueInfo>>> outerIterator =
assumedValues.entrySet().iterator();
while (outerIterator.hasNext()) {
@@ -738,7 +539,7 @@
continue;
}
assert dominance.isUnknown();
- dominance = function.apply(instruction, assumedValue, assumedValueInfo);
+ dominance = function.apply(instruction, assumedValue);
if ((dominance.isNothing() && !assumedValue.isArgument()) || dominance.isUnknown()) {
innerIterator.remove();
} else {
@@ -751,9 +552,9 @@
}
}
- AssumedValueInfo getAssumedValueInfo(Instruction instruction, Value assumedValue) {
+ boolean contains(Instruction instruction, Value assumedValue) {
Map<Value, AssumedValueInfo> dominancePerValue = assumedValues.get(instruction);
- return dominancePerValue != null ? dominancePerValue.get(assumedValue) : null;
+ return dominancePerValue != null && dominancePerValue.containsKey(assumedValue);
}
boolean isEmpty() {
@@ -768,12 +569,12 @@
consumer.accept(instruction, assumedValue, assumedValueInfo)));
}
- void removeAll(Map<Instruction, Map<Value, AssumedValueInfo>> keys) {
+ void removeAll(Map<Instruction, Set<Value>> keys) {
keys.forEach(
- (instruction, redundantAssumedValues) -> {
+ (instruction, values) -> {
Map<Value, AssumedValueInfo> dominancePerValue = assumedValues.get(instruction);
if (dominancePerValue != null) {
- redundantAssumedValues.keySet().forEach(dominancePerValue::remove);
+ values.forEach(dominancePerValue::remove);
if (dominancePerValue.isEmpty()) {
assumedValues.remove(instruction);
}
@@ -781,7 +582,7 @@
});
}
- void removeIf(TriPredicate<Instruction, Value, AssumedValueInfo> predicate) {
+ void removeIf(BiPredicate<Instruction, Value> predicate) {
Iterator<Entry<Instruction, Map<Value, AssumedValueInfo>>> outerIterator =
assumedValues.entrySet().iterator();
while (outerIterator.hasNext()) {
@@ -791,10 +592,8 @@
Iterator<Entry<Value, AssumedValueInfo>> innerIterator =
dominancePerValue.entrySet().iterator();
while (innerIterator.hasNext()) {
- Entry<Value, AssumedValueInfo> innerEntry = innerIterator.next();
- Value assumedValue = innerEntry.getKey();
- AssumedValueInfo assumedValueInfo = innerEntry.getValue();
- if (predicate.test(instruction, assumedValue, assumedValueInfo)) {
+ Value assumedValue = innerIterator.next().getKey();
+ if (predicate.test(instruction, assumedValue)) {
innerIterator.remove();
}
}
@@ -810,49 +609,29 @@
new LinkedHashMap<>();
// Used to avoid unnecessary block splitting during phase 1.
- private final Set<Value> nonNullValuesKnownToDominateAllUsers = Sets.newIdentityHashSet();
+ private final Set<Value> assumedValuesKnownToDominateAllUsers = Sets.newIdentityHashSet();
- private void updateAssumedValueInfo(
- Instruction instruction,
- Value assumedValue,
- AssumedDominance dominance,
- Consumer<AssumedValueInfo> consumer) {
- AssumedValueInfo assumedValueInfo =
- assumedValues
- .computeIfAbsent(instruction, ignore -> new LinkedHashMap<>())
- .computeIfAbsent(assumedValue, ignore -> new AssumedValueInfo(dominance));
- consumer.accept(assumedValueInfo);
- if (dominance.isEverything() && assumedValueInfo.isNonNull()) {
- nonNullValuesKnownToDominateAllUsers.add(assumedValue);
+ private void addNonNullValue(
+ Instruction instruction, Value nonNullValue, AssumedDominance dominance) {
+ assumedValues
+ .computeIfAbsent(instruction, ignore -> new LinkedHashMap<>())
+ .computeIfAbsent(nonNullValue, ignore -> new AssumedValueInfo(dominance))
+ .setNotNull();
+ if (dominance.isEverything()) {
+ assumedValuesKnownToDominateAllUsers.add(nonNullValue);
}
}
- void addAssumedValueKnownToDominateAllUsers(
- Instruction instruction,
- Value assumedValue,
- TypeElement dynamicUpperBoundType,
- ClassTypeElement dynamicLowerBoundType) {
- updateAssumedValueInfo(
- instruction,
- assumedValue,
- AssumedDominance.everything(),
- assumedValueInfo ->
- assumedValueInfo.setDynamicTypeAssumption(
- dynamicUpperBoundType, dynamicLowerBoundType));
- }
-
void addNonNullValueKnownToDominateAllUsers(Instruction instruction, Value nonNullValue) {
- updateAssumedValueInfo(
- instruction, nonNullValue, AssumedDominance.everything(), AssumedValueInfo::setNotNull);
+ addNonNullValue(instruction, nonNullValue, AssumedDominance.everything());
}
void addNonNullValueWithUnknownDominance(Instruction instruction, Value nonNullValue) {
- updateAssumedValueInfo(
- instruction, nonNullValue, AssumedDominance.unknown(), AssumedValueInfo::setNotNull);
+ addNonNullValue(instruction, nonNullValue, AssumedDominance.unknown());
}
public boolean isMaybeNull(Value value) {
- return !nonNullValuesKnownToDominateAllUsers.contains(value);
+ return !assumedValuesKnownToDominateAllUsers.contains(value);
}
public AssumedValues build() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 4531e4d..172a6b3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
@@ -84,6 +85,7 @@
import com.android.tools.r8.utils.InternalOutputMode;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Suppliers;
@@ -157,6 +159,21 @@
this.dexItemFactory = appView.dexItemFactory();
}
+ public static void insertAssumeInstructions(
+ IRCode code, Collection<Assumer> assumers, Timing timing) {
+ insertAssumeInstructionsInBlocks(code, assumers, alwaysTrue(), timing);
+ }
+
+ public static void insertAssumeInstructionsInBlocks(
+ IRCode code, Collection<Assumer> assumers, Predicate<BasicBlock> blockTester, Timing timing) {
+ timing.begin("Insert assume instructions");
+ for (Assumer assumer : assumers) {
+ assumer.insertAssumeInstructionsInBlocks(code, code.listIterator(), blockTester, timing);
+ assert code.isConsistentSSA();
+ }
+ timing.end();
+ }
+
public static void removeAssumeInstructions(AppView<?> appView, IRCode code) {
// We need to update the types of all values whose definitions depend on a non-null value.
// This is needed to preserve soundness of the types after the Assume instructions have been
@@ -1232,7 +1249,7 @@
return false;
}
- AssumeRemover assumeRemover = new AssumeRemover(appView, code);
+ AssumeDynamicTypeRemover assumeDynamicTypeRemover = new AssumeDynamicTypeRemover(appView, code);
boolean changed = false;
boolean mayHaveRemovedTrivialPhi = false;
Set<Value> affectedValues = Sets.newIdentityHashSet();
@@ -1265,7 +1282,7 @@
// return false unless it is object.
if (argument.getType().lessThanOrEqual(outValue.getType(), appView)) {
affectedValues.addAll(outValue.affectedValues());
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(outValue);
+ assumeDynamicTypeRemover.markUsersForRemoval(outValue);
mayHaveRemovedTrivialPhi |= outValue.numberOfPhiUsers() > 0;
outValue.replaceUsers(argument);
invoke.setOutValue(null);
@@ -1275,12 +1292,12 @@
}
}
}
- assumeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
+ assumeDynamicTypeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
if (!blocksToBeRemoved.isEmpty()) {
code.removeBlocks(blocksToBeRemoved);
code.removeAllDeadAndTrivialPhis(affectedValues);
assert code.getUnreachableBlocks().isEmpty();
- } else if (mayHaveRemovedTrivialPhi || assumeRemover.mayHaveIntroducedTrivialPhi()) {
+ } else if (mayHaveRemovedTrivialPhi || assumeDynamicTypeRemover.mayHaveIntroducedTrivialPhi()) {
code.removeAllDeadAndTrivialPhis(affectedValues);
}
if (!affectedValues.isEmpty()) {
@@ -1485,9 +1502,7 @@
if (result == InstanceOfResult.UNKNOWN) {
Value aliasedValue =
inValue.getSpecificAliasedValue(
- value ->
- value.isDefinedByInstructionSatisfying(
- Instruction::isAssumeWithDynamicTypeAssumption));
+ value -> !value.isPhi() && value.definition.isAssumeDynamicType());
if (aliasedValue != null) {
TypeElement dynamicType =
aliasedValue
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index cb8f858..7752d6c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -53,7 +53,6 @@
public void devirtualizeInvokeInterface(IRCode code) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
- AssumeRemover assumeRemover = new AssumeRemover(appView, code);
ProgramMethod context = code.context();
Map<InvokeInterface, InvokeVirtual> devirtualizedCall = new IdentityHashMap<>();
DominatorTree dominatorTree = new DominatorTree(code);
@@ -77,7 +76,7 @@
// (out <-) invoke-virtual rcv_c, ... C#foo
// ...
// non_null_rcv <- non-null rcv_c // <- Update the input rcv to the non-null, too.
- if (current.isAssumeWithNonNullAssumption()) {
+ if (current.isAssumeNonNull()) {
Assume nonNull = current.asAssume();
Instruction origin = nonNull.origin();
if (origin.isInvokeInterface()
@@ -254,8 +253,8 @@
it.next();
}
}
+
affectedValues.addAll(receiver.affectedValues());
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(receiver);
if (!receiver.hasLocalInfo()) {
receiver.replaceSelectiveUsers(
newReceiver, ImmutableSet.of(devirtualizedInvoke), ImmutableMap.of());
@@ -267,8 +266,6 @@
}
}
}
- assumeRemover.removeMarkedInstructions();
- affectedValues.addAll(assumeRemover.getAffectedValues());
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index 20e0515..6e2d9c7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -4,19 +4,36 @@
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.google.common.base.Predicates.alwaysTrue;
+
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.JumpInstruction;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Timing;
import java.util.ArrayList;
import java.util.List;
+import java.util.ListIterator;
+import java.util.function.Predicate;
-public class DynamicTypeOptimization {
+public class DynamicTypeOptimization implements Assumer {
private final AppView<AppInfoWithLiveness> appView;
@@ -24,6 +41,125 @@
this.appView = appView;
}
+ @Override
+ public void insertAssumeInstructions(IRCode code, Timing timing) {
+ insertAssumeInstructionsInBlocks(code, code.listIterator(), alwaysTrue(), timing);
+ }
+
+ @Override
+ public void insertAssumeInstructionsInBlocks(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ Predicate<BasicBlock> blockTester,
+ Timing timing) {
+ timing.begin("Insert assume dynamic type instructions");
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ if (blockTester.test(block)) {
+ insertAssumeDynamicTypeInstructionsInBlock(code, blockIterator, block);
+ }
+ }
+ timing.end();
+ }
+
+ // TODO(b/127461806): Should also insert AssumeDynamicType instructions after instanceof
+ // instructions.
+ private void insertAssumeDynamicTypeInstructionsInBlock(
+ IRCode code, ListIterator<BasicBlock> blockIterator, BasicBlock block) {
+ InstructionListIterator instructionIterator = block.listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction current = instructionIterator.next();
+ if (!current.hasOutValue() || !current.outValue().isUsed()) {
+ continue;
+ }
+
+ TypeElement dynamicUpperBoundType;
+ ClassTypeElement dynamicLowerBoundType;
+ if (current.isInvokeMethod()) {
+ InvokeMethod invoke = current.asInvokeMethod();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+
+ DexType staticReturnTypeRaw = invokedMethod.proto.returnType;
+ if (!staticReturnTypeRaw.isReferenceType()) {
+ continue;
+ }
+
+ if (invokedMethod.holder.isArrayType()
+ && invokedMethod.match(appView.dexItemFactory().objectMembers.clone)) {
+ dynamicUpperBoundType =
+ TypeElement.fromDexType(invokedMethod.holder, definitelyNotNull(), appView);
+ dynamicLowerBoundType = null;
+ } else {
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
+ if (singleTarget == null) {
+ continue;
+ }
+
+ MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+ if (optimizationInfo.returnsArgument()) {
+ // Don't insert an assume-instruction since we will replace all usages of the out-value
+ // by the corresponding argument.
+ continue;
+ }
+
+ dynamicUpperBoundType = optimizationInfo.getDynamicUpperBoundType();
+ dynamicLowerBoundType = optimizationInfo.getDynamicLowerBoundType();
+ }
+ } else if (current.isStaticGet()) {
+ StaticGet staticGet = current.asStaticGet();
+ DexEncodedField encodedField =
+ appView.appInfo().resolveField(staticGet.getField()).getResolvedField();
+ if (encodedField == null) {
+ continue;
+ }
+
+ dynamicUpperBoundType = encodedField.getOptimizationInfo().getDynamicUpperBoundType();
+ dynamicLowerBoundType = encodedField.getOptimizationInfo().getDynamicLowerBoundType();
+ } else {
+ continue;
+ }
+
+ Value outValue = current.outValue();
+ boolean isTrivial =
+ (dynamicUpperBoundType == null
+ || !dynamicUpperBoundType.strictlyLessThan(outValue.getType(), appView))
+ && dynamicLowerBoundType == null;
+ if (isTrivial) {
+ continue;
+ }
+
+ if (dynamicUpperBoundType == null) {
+ dynamicUpperBoundType = outValue.getType();
+ }
+
+ // Split block if needed (only debug instructions are allowed after the throwing
+ // instruction, if any).
+ BasicBlock insertionBlock =
+ block.hasCatchHandlers() ? instructionIterator.split(code, blockIterator) : block;
+
+ // Replace usages of out-value by the out-value of the AssumeDynamicType instruction.
+ Value specializedOutValue = code.createValue(outValue.getType(), outValue.getLocalInfo());
+ outValue.replaceUsers(specializedOutValue);
+
+ // Insert AssumeDynamicType instruction.
+ Assume assumeInstruction =
+ Assume.createAssumeDynamicTypeInstruction(
+ dynamicUpperBoundType,
+ dynamicLowerBoundType,
+ specializedOutValue,
+ outValue,
+ current,
+ appView);
+ assumeInstruction.setPosition(
+ appView.options().debug ? current.getPosition() : Position.none());
+ if (insertionBlock == block) {
+ instructionIterator.add(assumeInstruction);
+ } else {
+ insertionBlock.listIterator(code).add(assumeInstruction);
+ }
+ }
+ }
+
/**
* Computes the dynamic return type of the given method.
*
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 1c5ff8b..17cea8b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -932,7 +932,7 @@
OptimizationFeedback feedback,
InliningIRProvider inliningIRProvider,
Timing timing) {
- AssumeRemover assumeRemover = new AssumeRemover(appView, code);
+ AssumeDynamicTypeRemover assumeDynamicTypeRemover = new AssumeDynamicTypeRemover(appView, code);
Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
BasicBlockIterator blockIterator = code.listIterator();
ClassInitializationAnalysis classInitializationAnalysis =
@@ -1025,7 +1025,7 @@
// Mark AssumeDynamicType instruction for the out-value for removal, if any.
Value outValue = invoke.outValue();
if (outValue != null) {
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(outValue);
+ assumeDynamicTypeRemover.markUsersForRemoval(outValue);
}
boolean inlineeMayHaveInvokeMethod = inlinee.code.metadata().mayHaveInvokeMethod();
@@ -1073,14 +1073,14 @@
IteratorUtils.previousUntil(blockIterator, previous -> previous == block);
blockIterator.next();
}
- } else if (current.isAssume()) {
- assumeRemover.removeIfMarked(current.asAssume(), iterator);
+ } else if (current.isAssumeDynamicType()) {
+ assumeDynamicTypeRemover.removeIfMarked(current.asAssume(), iterator);
}
}
}
assert inlineeStack.isEmpty();
- assumeRemover.removeMarkedInstructions(blocksToRemove);
- assumeRemover.finish();
+ assumeDynamicTypeRemover.removeMarkedInstructions(blocksToRemove);
+ assumeDynamicTypeRemover.finish();
classInitializationAnalysis.finish();
code.removeBlocks(blocksToRemove);
code.removeAllDeadAndTrivialPhis();
@@ -1129,33 +1129,50 @@
BasicBlockIterator blockIterator,
BasicBlock block,
Timing timing) {
+ InternalOptions options = appView.options();
+ boolean skip =
+ !(options.enableDynamicTypeOptimization
+ || options.enableNonNullTracking
+ || options.enableValuePropagation);
+ if (skip) {
+ return;
+ }
+
BasicBlock state = IteratorUtils.peekNext(blockIterator);
Set<BasicBlock> inlineeBlocks = SetUtils.newIdentityHashSet(inlinee.blocks);
// Run member value propagation on the inlinee blocks.
- if (appView.options().enableValuePropagation) {
+ if (options.enableValuePropagation) {
rewindBlockIteratorToFirstInlineeBlock(blockIterator, block);
applyMemberValuePropagationToInlinee(code, blockIterator, block, inlineeBlocks);
}
// Add non-null IRs only to the inlinee blocks.
- insertAssumeInstructions(code, blockIterator, block, inlineeBlocks, timing);
+ if (options.enableNonNullTracking) {
+ Assumer nonNullTracker = new AssumeInserter(appView);
+ applyAssumerToInlinee(nonNullTracker, code, blockIterator, block, inlineeBlocks, timing);
+ }
+ // Add dynamic type assumptions only to the inlinee blocks.
+ if (options.enableDynamicTypeOptimization) {
+ applyAssumerToInlinee(
+ new DynamicTypeOptimization(appView), code, blockIterator, block, inlineeBlocks, timing);
+ }
// Restore the old state of the iterator.
rewindBlockIteratorToFirstInlineeBlock(blockIterator, state);
// TODO(b/72693244): need a test where refined env in inlinee affects the caller.
}
- private void insertAssumeInstructions(
+ private void applyAssumerToInlinee(
+ Assumer assumer,
IRCode code,
BasicBlockIterator blockIterator,
BasicBlock block,
Set<BasicBlock> inlineeBlocks,
Timing timing) {
rewindBlockIteratorToFirstInlineeBlock(blockIterator, block);
- new AssumeInserter(appView)
- .insertAssumeInstructionsInBlocks(code, blockIterator, inlineeBlocks::contains, timing);
+ assumer.insertAssumeInstructionsInBlocks(code, blockIterator, inlineeBlocks::contains, timing);
assert !blockIterator.hasNext();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index da5994e..d381afb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -326,7 +326,7 @@
}
public void rewrite(IRCode code) {
- AssumeRemover assumeRemover = new AssumeRemover(appView, code);
+ AssumeDynamicTypeRemover assumeDynamicTypeRemover = new AssumeDynamicTypeRemover(appView, code);
Set<BasicBlock> blocksToBeRemoved = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blockIterator = code.listIterator();
Set<Value> affectedValues = Sets.newIdentityHashSet();
@@ -352,13 +352,13 @@
blockIterator,
instructionIterator,
code,
- assumeRemover,
+ assumeDynamicTypeRemover,
affectedValues,
blocksToBeRemoved);
}
}
}
- assumeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
+ assumeDynamicTypeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
code.removeBlocks(blocksToBeRemoved);
code.removeAllDeadAndTrivialPhis(affectedValues);
code.removeUnreachableBlocks();
@@ -398,7 +398,7 @@
ListIterator<BasicBlock> blockIterator,
InstructionListIterator instructionIterator,
IRCode code,
- AssumeRemover assumeRemover,
+ AssumeDynamicTypeRemover assumeDynamicTypeRemover,
Set<Value> affectedValues,
Set<BasicBlock> blocksToBeRemoved) {
DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.context());
@@ -421,7 +421,8 @@
DexType returnType = target.method.proto.returnType;
if (returnType.isAlwaysNull(appView)) {
- replaceOutValueByNull(invoke, instructionIterator, code, assumeRemover, affectedValues);
+ replaceOutValueByNull(
+ invoke, instructionIterator, code, assumeDynamicTypeRemover, affectedValues);
}
}
@@ -429,13 +430,13 @@
Instruction instruction,
InstructionListIterator instructionIterator,
IRCode code,
- AssumeRemover assumeRemover,
+ AssumeDynamicTypeRemover assumeDynamicTypeRemover,
Set<Value> affectedValues) {
assert instructionIterator.peekPrevious() == instruction;
if (instruction.hasOutValue()) {
Value outValue = instruction.outValue();
if (outValue.numberOfAllUsers() > 0) {
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(outValue);
+ assumeDynamicTypeRemover.markUsersForRemoval(outValue);
instructionIterator.previous();
affectedValues.addAll(outValue.affectedValues());
outValue.replaceUsers(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 0e3e339..59e24a6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -18,7 +18,7 @@
import java.util.BitSet;
import java.util.Set;
-public class DefaultMethodOptimizationInfo extends MethodOptimizationInfo {
+public class DefaultMethodOptimizationInfo implements MethodOptimizationInfo {
public static final MethodOptimizationInfo DEFAULT_INSTANCE = new DefaultMethodOptimizationInfo();
@@ -124,6 +124,11 @@
}
@Override
+ public boolean neverReturnsNull() {
+ return UNKNOWN_NEVER_RETURNS_NULL;
+ }
+
+ @Override
public boolean neverReturnsNormally() {
return UNKNOWN_NEVER_RETURNS_NORMALLY;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
index 0365122..09ad5e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
@@ -26,11 +26,6 @@
public abstract TypeElement getDynamicUpperBoundType();
- public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
- TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
- return dynamicUpperBoundType != null ? dynamicUpperBoundType : orElse;
- }
-
public abstract boolean isDead();
public abstract boolean valueHasBeenPropagated();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 1b79eb8..16ce797 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -15,7 +15,7 @@
import java.util.BitSet;
import java.util.Set;
-public abstract class MethodOptimizationInfo {
+public interface MethodOptimizationInfo {
enum InlinePreference {
NeverInline,
@@ -23,66 +23,63 @@
Default
}
- public abstract boolean isDefaultMethodOptimizationInfo();
+ boolean isDefaultMethodOptimizationInfo();
- public abstract boolean isUpdatableMethodOptimizationInfo();
+ boolean isUpdatableMethodOptimizationInfo();
- public abstract UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo();
+ UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo();
- public abstract boolean cannotBeKept();
+ boolean cannotBeKept();
- public abstract boolean classInitializerMayBePostponed();
+ boolean classInitializerMayBePostponed();
- public abstract TypeElement getDynamicUpperBoundType();
+ TypeElement getDynamicUpperBoundType();
- public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
- TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
- return dynamicUpperBoundType != null ? dynamicUpperBoundType : orElse;
- }
+ ClassTypeElement getDynamicLowerBoundType();
- public abstract ClassTypeElement getDynamicLowerBoundType();
+ ParameterUsage getParameterUsages(int parameter);
- public abstract ParameterUsage getParameterUsages(int parameter);
+ BitSet getNonNullParamOrThrow();
- public abstract BitSet getNonNullParamOrThrow();
+ BitSet getNonNullParamOnNormalExits();
- public abstract BitSet getNonNullParamOnNormalExits();
+ boolean hasBeenInlinedIntoSingleCallSite();
- public abstract boolean hasBeenInlinedIntoSingleCallSite();
+ boolean isReachabilitySensitive();
- public abstract boolean isReachabilitySensitive();
+ boolean returnsArgument();
- public abstract boolean returnsArgument();
+ int getReturnedArgument();
- public abstract int getReturnedArgument();
+ boolean neverReturnsNull();
- public abstract boolean neverReturnsNormally();
+ boolean neverReturnsNormally();
- public abstract BridgeInfo getBridgeInfo();
+ BridgeInfo getBridgeInfo();
- public abstract ClassInlinerEligibilityInfo getClassInlinerEligibility();
+ ClassInlinerEligibilityInfo getClassInlinerEligibility();
- public abstract Set<DexType> getInitializedClassesOnNormalExit();
+ Set<DexType> getInitializedClassesOnNormalExit();
- public abstract InstanceInitializerInfo getInstanceInitializerInfo();
+ InstanceInitializerInfo getInstanceInitializerInfo();
- public abstract boolean isInitializerEnablingJavaVmAssertions();
+ boolean isInitializerEnablingJavaVmAssertions();
- public abstract AbstractValue getAbstractReturnValue();
+ AbstractValue getAbstractReturnValue();
- public abstract boolean forceInline();
+ boolean forceInline();
- public abstract boolean neverInline();
+ boolean neverInline();
- public abstract boolean checksNullReceiverBeforeAnySideEffect();
+ boolean checksNullReceiverBeforeAnySideEffect();
- public abstract boolean triggersClassInitBeforeAnySideEffect();
+ boolean triggersClassInitBeforeAnySideEffect();
- public abstract boolean mayHaveSideEffects();
+ boolean mayHaveSideEffects();
- public abstract boolean returnValueOnlyDependsOnArguments();
+ boolean returnValueOnlyDependsOnArguments();
- public abstract boolean returnValueHasBeenPropagated();
+ boolean returnValueHasBeenPropagated();
- public abstract UpdatableMethodOptimizationInfo mutableCopy();
+ UpdatableMethodOptimizationInfo mutableCopy();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 21ec659..cac5801 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -360,12 +360,14 @@
return;
}
Value returnValue = firstExit.returnValue();
+ boolean isNeverNull = returnValue.getType().isReferenceType() && returnValue.isNeverNull();
for (int i = 1; i < normalExits.size(); i++) {
Return exit = normalExits.get(i).exit().asReturn();
Value value = exit.returnValue();
if (value != returnValue) {
returnValue = null;
}
+ isNeverNull &= value.getType().isReferenceType() && value.isNeverNull();
}
if (returnValue != null) {
Value aliasedValue = returnValue.getAliasedValue();
@@ -380,6 +382,9 @@
}
}
}
+ if (isNeverNull) {
+ feedback.methodNeverReturnsNull(method);
+ }
}
private void computeInstanceInitializerInfo(
@@ -1151,7 +1156,7 @@
// Collect basic blocks that check nullability of the parameter.
nullCheckedBlocks.clear();
for (Instruction user : argument.uniqueUsers()) {
- if (user.isAssumeWithNonNullAssumption()) {
+ if (user.isAssumeNonNull()) {
nullCheckedBlocks.add(user.asAssume().getBlock());
}
if (user.isIf()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index d9db007..d4eef20 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -201,6 +201,11 @@
}
@Override
+ public synchronized void methodNeverReturnsNull(DexEncodedMethod method) {
+ getMethodOptimizationInfoForUpdating(method).markNeverReturnsNull();
+ }
+
+ @Override
public synchronized void methodNeverReturnsNormally(DexEncodedMethod method) {
getMethodOptimizationInfoForUpdating(method).markNeverReturnsNormally();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 49ffaf9..435f05f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -90,6 +90,9 @@
public void methodReturnValueOnlyDependsOnArguments(DexEncodedMethod method) {}
@Override
+ public void methodNeverReturnsNull(DexEncodedMethod method) {}
+
+ @Override
public void methodNeverReturnsNormally(DexEncodedMethod method) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 221fbd9..9696162 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -104,7 +104,7 @@
@Override
public void methodReturnsObjectWithUpperBoundType(
DexEncodedMethod method, AppView<?> appView, TypeElement type) {
- method.getMutableOptimizationInfo().markReturnsObjectWithUpperBoundType(appView, type);
+ // Ignored.
}
@Override
@@ -124,6 +124,11 @@
}
@Override
+ public void methodNeverReturnsNull(DexEncodedMethod method) {
+ method.getMutableOptimizationInfo().markNeverReturnsNull();
+ }
+
+ @Override
public void methodNeverReturnsNormally(DexEncodedMethod method) {
// Ignored.
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 1728999..c1012ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -22,7 +22,7 @@
import java.util.Set;
import java.util.function.Function;
-public class UpdatableMethodOptimizationInfo extends MethodOptimizationInfo {
+public class UpdatableMethodOptimizationInfo implements MethodOptimizationInfo {
private Set<DexType> initializedClassesOnNormalExit =
DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
@@ -71,9 +71,9 @@
private static final int HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG = 0x4;
private static final int MAY_HAVE_SIDE_EFFECT_FLAG = 0x8;
private static final int RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG = 0x10;
- private static final int UNUSED_FLAG_1 = 0x20;
+ private static final int NEVER_RETURNS_NULL_FLAG = 0x20;
private static final int NEVER_RETURNS_NORMALLY_FLAG = 0x40;
- private static final int UNUSED_FLAG_2 = 0x80;
+ private static final int UNUSED_FLAG = 0x80;
private static final int CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG = 0x100;
private static final int TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT_FLAG = 0x200;
private static final int INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG = 0x400;
@@ -97,10 +97,11 @@
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.returnValueOnlyDependsOnArguments())
* RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG;
- defaultFlags |= 0 * UNUSED_FLAG_1;
+ defaultFlags |=
+ BooleanUtils.intValue(defaultOptInfo.neverReturnsNull()) * NEVER_RETURNS_NULL_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.neverReturnsNormally()) * NEVER_RETURNS_NORMALLY_FLAG;
- defaultFlags |= 0 * UNUSED_FLAG_2;
+ defaultFlags |= 0 * UNUSED_FLAG;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.checksNullReceiverBeforeAnySideEffect())
* CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG;
@@ -292,6 +293,11 @@
}
@Override
+ public boolean neverReturnsNull() {
+ return isFlagSet(NEVER_RETURNS_NULL_FLAG);
+ }
+
+ @Override
public boolean neverReturnsNormally() {
return isFlagSet(NEVER_RETURNS_NORMALLY_FLAG);
}
@@ -396,6 +402,10 @@
setFlag(RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG);
}
+ void markNeverReturnsNull() {
+ setFlag(NEVER_RETURNS_NULL_FLAG);
+ }
+
void markNeverReturnsNormally() {
setFlag(NEVER_RETURNS_NORMALLY_FLAG);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index ad6313e..3a7e099 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -44,7 +44,8 @@
register(new ObjectMethodOptimizer(appView));
register(new ObjectsMethodOptimizer(appView));
register(new StringMethodOptimizer(appView));
- if (appView.enableWholeProgramOptimizations()) {
+ if (appView.enableWholeProgramOptimizations()
+ && appView.options().enableDynamicTypeOptimization) {
// Subtyping is required to prove the enum class is a subtype of java.lang.Enum.
register(new EnumMethodOptimizer(appView));
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index 92c78af..a677b43 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -4,15 +4,12 @@
package com.android.tools.r8.ir.optimize.library;
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.analysis.value.ObjectState;
import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
@@ -60,16 +57,7 @@
for (DexMethod method : dexItemFactory.libraryMethodsReturningNonNull) {
DexEncodedMethod definition = lookupMethod(method);
if (definition != null) {
- TypeElement staticType =
- TypeElement.fromDexType(method.proto.returnType, maybeNull(), appView);
- feedback.methodReturnsObjectWithUpperBoundType(
- definition,
- appView,
- definition
- .getOptimizationInfo()
- .getDynamicUpperBoundTypeOrElse(staticType)
- .asReferenceType()
- .asDefinitelyNotNull());
+ feedback.methodNeverReturnsNull(definition);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 29250dc..0e0e1c9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -35,7 +35,6 @@
import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
-import com.android.tools.r8.ir.optimize.AssumeInserter;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -391,10 +390,7 @@
}
private void insertAssumeInstructions(IRCode code, MethodProcessor methodProcessor) {
- AssumeInserter assumeInserter = converter.assumeInserter;
- if (assumeInserter != null) {
- assumeInserter.insertAssumeInstructions(code, Timing.empty());
- }
+ CodeRewriter.insertAssumeInstructions(code, converter.assumers, Timing.empty());
}
private BiConsumer<IRCode, MethodProcessor> collectOptimizationInfo(
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 5a286b7..8c92acf 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -172,12 +172,14 @@
void disableAllOptimizations() {
disableGlobalOptimizations();
+ enableNonNullTracking = false;
enableNameReflectionOptimization = false;
enableStringConcatenationOptimization = false;
}
public void disableGlobalOptimizations() {
enableArgumentRemoval = false;
+ enableDynamicTypeOptimization = false;
enableInlining = false;
enableClassInlining = false;
enableClassStaticizer = false;
@@ -213,6 +215,7 @@
public boolean libraryInterfacesMayHaveStaticInitialization = false;
// Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations.
+ public boolean enableDynamicTypeOptimization = true;
public boolean enableFieldAssignmentTracker = true;
public boolean enableFieldBitAccessAnalysis =
System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
@@ -221,6 +224,7 @@
public boolean enableArgumentRemoval = true;
public boolean enableUnusedInterfaceRemoval = true;
public boolean enableDevirtualization = true;
+ public boolean enableNonNullTracking = true;
public boolean enableInlining =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.disableinlining") == null;
diff --git a/src/main/java/com/android/tools/r8/utils/TriPredicate.java b/src/main/java/com/android/tools/r8/utils/TriPredicate.java
deleted file mode 100644
index 42b76a7..0000000
--- a/src/main/java/com/android/tools/r8/utils/TriPredicate.java
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.utils;
-
-public interface TriPredicate<S, T, U> {
-
- boolean test(S s, T t, U u);
-}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index a9f77ce..dac8b92 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -62,7 +62,7 @@
while (it.hasNext()) {
prev = curr != null && !curr.isGoto() ? curr : prev;
curr = it.next();
- if (curr.isAssumeWithNonNullAssumption()) {
+ if (curr.isAssumeNonNull()) {
// Make sure non-null is added to the right place.
assertTrue(prev == null
|| prev.throwsOnNullInput()
@@ -160,11 +160,11 @@
if (count == 0) {
// First one in the very first line: its value should not be replaced by NonNullMarker
// because this instruction will happen _before_ non-null.
- assertFalse(iput.value().definition.isAssumeWithNonNullAssumption());
+ assertFalse(iput.value().definition.isAssumeNonNull());
} else if (count == 1) {
// Second one after a safe invocation, which should use the value added by
// NonNullMarker.
- assertTrue(iput.object().definition.isAssumeWithNonNullAssumption());
+ assertTrue(iput.object().definition.isAssumeNonNull());
}
count++;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
index ba6d920..4cab645 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
@@ -7,11 +7,13 @@
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
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.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -24,14 +26,17 @@
@RunWith(Parameterized.class)
public class DynamicTypeOptimizationTest extends TestBase {
+ private final boolean enableDynamicTypeOptimization;
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameterized.Parameters(name = "{1}, enable dynamic type optimization: {0}")
public static List<Object[]> data() {
- return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+ return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
}
- public DynamicTypeOptimizationTest(TestParameters parameters) {
+ public DynamicTypeOptimizationTest(
+ boolean enableDynamicTypeOptimization, TestParameters parameters) {
+ this.enableDynamicTypeOptimization = enableDynamicTypeOptimization;
this.parameters = parameters;
}
@@ -42,8 +47,10 @@
.addKeepMainRule(TestClass.class)
// Keep B to ensure that we will treat it as being instantiated.
.addKeepClassRulesWithAllowObfuscation(B.class)
+ .addOptionsModification(
+ options -> options.enableDynamicTypeOptimization = enableDynamicTypeOptimization)
.enableInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
+ .setMinApi(parameters.getRuntime())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
@@ -74,7 +81,8 @@
MethodSubject testInstanceOfRemovalMethod =
mainClassSubject.uniqueMethodWithName("testInstanceOfRemoval");
assertThat(testInstanceOfRemovalMethod, isPresent());
- assertTrue(
+ assertEquals(
+ enableDynamicTypeOptimization,
testInstanceOfRemovalMethod
.streamInstructions()
.noneMatch(instruction -> instruction.isInstanceOf(aClassSubject.getFinalName())));
@@ -84,16 +92,27 @@
MethodSubject testMethodInliningMethod =
mainClassSubject.uniqueMethodWithName("testMethodInlining");
assertThat(testMethodInliningMethod, isPresent());
- assertTrue(interfaceSubject.uniqueMethodWithName("world").isAbsent());
+ assertEquals(
+ enableDynamicTypeOptimization, interfaceSubject.uniqueMethodWithName("world").isAbsent());
+ if (!enableDynamicTypeOptimization) {
+ assertThat(
+ testMethodInliningMethod, invokesMethod(interfaceSubject.uniqueMethodWithName("world")));
+ }
// Verify that exclamationMark() has been rebound in testMethodRebinding() unless the dynamic
// type optimization is disabled.
MethodSubject testMethodRebindingMethod =
mainClassSubject.uniqueMethodWithName("testMethodRebinding");
assertThat(testMethodRebindingMethod, isPresent());
- assertThat(
- testMethodRebindingMethod,
- invokesMethod(aClassSubject.uniqueMethodWithName("exclamationMark")));
+ if (enableDynamicTypeOptimization) {
+ assertThat(
+ testMethodRebindingMethod,
+ invokesMethod(aClassSubject.uniqueMethodWithName("exclamationMark")));
+ } else {
+ assertThat(
+ testMethodRebindingMethod,
+ invokesMethod(interfaceSubject.uniqueMethodWithName("exclamationMark")));
+ }
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfRemovalTest.java
index 791bbae..1cbeaae 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/instanceofremoval/InstanceOfRemovalTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -110,14 +111,16 @@
}
}
- @Parameters(name = "{0}")
+ @Parameters(name = "{1}, enable dynamic type optimization: {0}")
public static List<Object[]> data() {
- return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+ return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
}
+ private final boolean enableDynamicTypeOptimization;
private final TestParameters parameters;
- public InstanceOfRemovalTest(TestParameters parameters) {
+ public InstanceOfRemovalTest(boolean enableDynamicTypeOptimization, TestParameters parameters) {
+ this.enableDynamicTypeOptimization = enableDynamicTypeOptimization;
this.parameters = parameters;
}
@@ -158,8 +161,10 @@
testForR8(parameters.getBackend())
.addProgramClasses(A.class, B.class, TestClass.class)
.addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> options.enableDynamicTypeOptimization = enableDynamicTypeOptimization)
.enableInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
+ .setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expected)
.inspector();
@@ -177,6 +182,7 @@
MethodSubject barMethodSubject = testClass.uniqueMethodWithName("bar");
Iterator<InstructionSubject> barInstructionIterator =
barMethodSubject.iterateInstructions(InstructionSubject::isInstanceOf);
- assertEquals(4, Streams.stream(barInstructionIterator).count());
+ assertEquals(
+ enableDynamicTypeOptimization ? 4 : 6, Streams.stream(barInstructionIterator).count());
}
}