Reland "Move insertion of assume-dynamic-type to AssumeInserter"
This reverts commit 1b6d08f3c5deac6ae7f886be7da3061180470caa.
Change-Id: I63c74d4e662f1982133bb3974909307de0960d24
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 5781482..cf475fa 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -393,7 +393,6 @@
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 b1072df..9b0f9de 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -162,7 +162,6 @@
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 656f3aa..5abbc25 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -842,7 +842,6 @@
? 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 117af7b..7aed1d9 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,7 +5,6 @@
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;
@@ -16,7 +15,6 @@
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;
@@ -25,11 +23,11 @@
private static final String ERROR_MESSAGE =
"Expected Assume instructions to be removed after IR processing.";
- private final DynamicTypeAssumption dynamicTypeAssumption;
+ private DynamicTypeAssumption dynamicTypeAssumption;
private final NonNullAssumption nonNullAssumption;
private final Instruction origin;
- private Assume(
+ public Assume(
DynamicTypeAssumption dynamicTypeAssumption,
NonNullAssumption nonNullAssumption,
Value dest,
@@ -38,9 +36,6 @@
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
@@ -78,7 +73,7 @@
}
public boolean verifyInstructionIsNeeded(AppView<?> appView) {
- if (isAssumeDynamicType()) {
+ if (hasDynamicTypeAssumption()) {
assert dynamicTypeAssumption.verifyCorrectnessOfValues(outValue(), src(), appView);
}
return true;
@@ -112,13 +107,7 @@
@Override
public String getInstructionName() {
- if (isAssumeDynamicType()) {
- return "AssumeDynamicType";
- }
- if (isAssumeNonNull()) {
- return "AssumeNonNull";
- }
- throw new Unimplemented();
+ return "Assume";
}
@Override
@@ -131,13 +120,15 @@
return this;
}
- @Override
- public boolean isAssumeDynamicType() {
+ public boolean hasDynamicTypeAssumption() {
return dynamicTypeAssumption != null;
}
- @Override
- public boolean isAssumeNonNull() {
+ public void unsetDynamicTypeAssumption() {
+ dynamicTypeAssumption = null;
+ }
+
+ public boolean hasNonNullAssumption() {
return nonNullAssumption != null;
}
@@ -149,7 +140,7 @@
if (outType.isPrimitiveType()) {
return false;
}
- if (isAssumeDynamicType()) {
+ if (hasDynamicTypeAssumption()) {
outType = dynamicTypeAssumption.getDynamicUpperBoundType();
}
if (appView.appInfo().hasLiveness()) {
@@ -220,14 +211,11 @@
@Override
public TypeElement evaluate(AppView<?> appView) {
- if (isAssumeDynamicType()) {
- return src().getType();
- }
- if (isAssumeNonNull()) {
+ if (hasNonNullAssumption()) {
assert src().getType().isReferenceType();
return src().getType().asReferenceType().asMeetWithNotNull();
}
- throw new Unimplemented();
+ return src().getType();
}
@Override
@@ -255,15 +243,14 @@
assert super.verifyTypes(appView);
TypeElement inType = src().getType();
+ assert inType.isReferenceType() : inType;
+
TypeElement outType = getOutType();
- if (isAssumeDynamicType()) {
- assert inType.isReferenceType() : inType;
- assert outType.equals(inType)
+ if (hasNonNullAssumption()) {
+ assert inType.isNullType() || outType.equals(inType.asReferenceType().asMeetWithNotNull())
: "At " + this + System.lineSeparator() + outType + " != " + inType;
} else {
- assert isAssumeNonNull() : this;
- assert inType.isReferenceType() : inType;
- assert inType.isNullType() || outType.equals(inType.asReferenceType().asMeetWithNotNull())
+ assert outType.equals(inType)
: "At " + this + System.lineSeparator() + outType + " != " + inType;
}
return true;
@@ -276,21 +263,21 @@
// assumption became "truth."
// 2) invoke-interface could be devirtualized, while its dynamic type and/or non-null receiver
// are still valid.
- String originString =
- origin.hasBlock() ? " (origin: `" + origin.toString() + "`)" : " (obsolete origin)";
- if (isAssumeNonNull()) {
- return super.toString() + originString;
+ StringBuilder builder = new StringBuilder(super.toString());
+ if (hasNonNullAssumption()) {
+ builder.append("; not null");
}
- if (isAssumeDynamicType()) {
- return super.toString()
- + "; upper bound: "
- + dynamicTypeAssumption.dynamicUpperBoundType
- + (dynamicTypeAssumption.dynamicLowerBoundType != null
- ? "; lower bound: " + dynamicTypeAssumption.dynamicLowerBoundType
- : "")
- + 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);
+ }
}
- return super.toString();
+ return builder.toString();
}
public static class DynamicTypeAssumption {
@@ -298,7 +285,7 @@
private final TypeElement dynamicUpperBoundType;
private final ClassTypeElement dynamicLowerBoundType;
- private DynamicTypeAssumption(
+ public 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 32e5dea..3ef2eee 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
@@ -613,17 +613,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 validAssumeDynamicTypeInstructions(appView);
+ assert validAssumeInstructions(appView);
assert new TypeChecker(appView.withLiveness()).check(this);
}
assert blocks.stream().allMatch(block -> block.verifyTypes(appView));
return true;
}
- private boolean validAssumeDynamicTypeInstructions(AppView<?> appView) {
+ private boolean validAssumeInstructions(AppView<?> appView) {
for (BasicBlock block : blocks) {
for (Instruction instruction : block.getInstructions()) {
- if (instruction.isAssumeDynamicType()) {
+ if (instruction.isAssume()) {
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 6e5dfa3..75b810d 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
@@ -711,12 +711,12 @@
return null;
}
- public boolean isAssumeDynamicType() {
- return false;
+ public final boolean isAssumeWithDynamicTypeAssumption() {
+ return isAssume() && asAssume().hasDynamicTypeAssumption();
}
- public boolean isAssumeNonNull() {
- return false;
+ public final boolean isAssumeWithNonNullAssumption() {
+ return isAssume() && asAssume().hasNonNullAssumption();
}
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 a2aa502..f3a889f 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,7 +150,8 @@
return false;
}
// Check that the receiver information comes from a dynamic type.
- if (!getReceiver().definition.isAssumeDynamicType()) {
+ if (!getReceiver()
+ .isDefinedByInstructionSatisfying(Instruction::isAssumeWithDynamicTypeAssumption)) {
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 4b9b2d1..1fc8c4b 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
@@ -402,6 +402,10 @@
return debugData != null && !debugData.users.isEmpty();
}
+ public boolean hasNonDebugUsers() {
+ return hasUsers() || hasPhiUsers();
+ }
+
public boolean hasPhiUsers() {
return !phiUsers.isEmpty();
}
@@ -831,8 +835,8 @@
*/
public boolean isNeverNull() {
assert type.isReferenceType();
- return (definition != null && definition.isAssumeNonNull())
- || type.nullability().isDefinitelyNotNull();
+ return isDefinedByInstructionSatisfying(Instruction::isAssumeWithNonNullAssumption)
+ || type.isDefinitelyNotNull();
}
public boolean isArgument() {
@@ -1051,14 +1055,19 @@
Value root = getAliasedValue();
if (root.isPhi()) {
assert getSpecificAliasedValue(
- value -> !value.isPhi() && value.definition.isAssumeDynamicType())
+ value ->
+ value.isDefinedByInstructionSatisfying(
+ Instruction::isAssumeWithDynamicTypeAssumption))
== 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.isPhi() && value.definition.isAssumeDynamicType());
+ getSpecificAliasedValue(
+ value ->
+ value.isDefinedByInstructionSatisfying(
+ Instruction::isAssumeWithDynamicTypeAssumption));
TypeElement lattice;
if (aliasedValue != null) {
// If there is an alias of the receiver, which is defined by an Assume instruction that
@@ -1111,7 +1120,8 @@
}
// Try to find an alias of the receiver, which is defined by an instruction of the type Assume.
- Value aliasedValue = getSpecificAliasedValue(value -> value.definition.isAssumeDynamicType());
+ Value aliasedValue =
+ getSpecificAliasedValue(value -> value.definition.isAssumeWithDynamicTypeAssumption());
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 dd481a9..f59cbd3 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,7 +53,6 @@
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;
@@ -167,8 +166,7 @@
private final EnumValueOptimizer enumValueOptimizer;
private final EnumUnboxer enumUnboxer;
- // Assumers that will insert Assume instructions.
- public final Collection<Assumer> assumers = new ArrayList<>();
+ public final AssumeInserter assumeInserter;
private final DynamicTypeOptimization dynamicTypeOptimization;
final AssertionsRewriter assertionsRewriter;
@@ -264,6 +262,7 @@
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
this.enumUnboxer = null;
+ this.assumeInserter = null;
return;
}
this.lambdaRewriter =
@@ -291,20 +290,12 @@
assert appView.appInfo().hasLiveness();
assert appView.rootSet() != null;
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- if (options.enableNonNullTracking) {
- assumers.add(new AssumeInserter(appViewWithLiveness));
- }
+ assumeInserter = new AssumeInserter(appViewWithLiveness);
this.classInliner =
options.enableClassInlining && options.enableInlining ? new ClassInliner() : null;
this.classStaticizer =
options.enableClassStaticizer ? new ClassStaticizer(appViewWithLiveness, this) : null;
- this.dynamicTypeOptimization =
- options.enableDynamicTypeOptimization
- ? new DynamicTypeOptimization(appViewWithLiveness)
- : null;
- if (dynamicTypeOptimization != null) {
- assumers.add(dynamicTypeOptimization);
- }
+ this.dynamicTypeOptimization = new DynamicTypeOptimization(appViewWithLiveness);
this.fieldAccessAnalysis =
FieldAccessAnalysis.enable(options) ? new FieldAccessAnalysis(appViewWithLiveness) : null;
this.libraryMethodOverrideAnalysis =
@@ -347,6 +338,7 @@
this.enumValueOptimizer =
options.enableEnumValueOptimization ? new EnumValueOptimizer(appViewWithLiveness) : null;
} else {
+ this.assumeInserter = null;
this.classInliner = null;
this.classStaticizer = null;
this.dynamicTypeOptimization = null;
@@ -1259,9 +1251,9 @@
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
- timing.begin("Insert assume instructions");
- CodeRewriter.insertAssumeInstructions(code, assumers, timing);
- timing.end();
+ if (assumeInserter != null) {
+ assumeInserter.insertAssumeInstructions(code, timing);
+ }
previous = printMethod(code, "IR after inserting assume instructions (SSA)", previous);
@@ -1597,7 +1589,7 @@
timing.end();
}
- if (!assumers.isEmpty()) {
+ if (assumeInserter != null) {
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 7211439..e54ec6c 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,8 +44,6 @@
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/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index 611a0a1..5651954 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,23 @@
// 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.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
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;
+import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -29,8 +33,11 @@
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;
@@ -44,24 +51,23 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.function.BiFunction;
-import java.util.function.BiPredicate;
+import java.util.function.Consumer;
import java.util.function.Predicate;
-public class AssumeInserter implements Assumer {
+public class AssumeInserter {
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,
@@ -89,7 +95,7 @@
timing.end();
timing.begin("Part 3: Compute dominated users");
- Map<Instruction, Set<Value>> redundantKeys =
+ Map<Instruction, Map<Value, AssumedValueInfo>> redundantAssumedValues =
computeDominanceForAssumedValues(code, assumedValues);
timing.end();
if (assumedValues.isEmpty()) {
@@ -97,7 +103,7 @@
}
timing.begin("Part 4: Remove redundant dominated assume instructions");
- removeRedundantDominatedAssumeInstructions(assumedValues, redundantKeys);
+ removeRedundantDominatedAssumeInstructions(assumedValues, redundantAssumedValues);
timing.end();
if (assumedValues.isEmpty()) {
return;
@@ -145,29 +151,15 @@
}
}
- Value outValue = current.outValue();
if (current.isInvokeMethod()) {
- InvokeMethod invoke = current.asInvokeMethod();
- if (invoke.hasOutValue() || !invoke.getInvokedMethod().proto.parameters.isEmpty()) {
- // Case (2) and (3).
- needsAssumeInstruction |=
- computeAssumedValuesFromSingleTarget(code, invoke, assumedValuesBuilder);
- }
+ // Case (2) and (3).
+ needsAssumeInstruction |=
+ computeAssumedValuesForInvokeMethod(
+ code, current.asInvokeMethod(), assumedValuesBuilder);
} else if (current.isFieldGet()) {
// Case (4), field-get instructions that are guaranteed to read a non-null value.
- 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;
- }
- }
- }
+ needsAssumeInstruction |=
+ computeAssumedValuesForFieldGet(current.asFieldInstruction(), assumedValuesBuilder);
}
// If we need to insert an assume instruction into a block with catch handlers, we split the
@@ -202,6 +194,35 @@
}
}
+ 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());
@@ -214,11 +235,13 @@
// Case (2), invocations that are guaranteed to return a non-null value.
Value outValue = invoke.outValue();
- if (outValue != null
- && optimizationInfo.neverReturnsNull()
- && isNullableReferenceTypeWithNonDebugUsers(outValue)) {
- assumedValuesBuilder.addNonNullValueKnownToDominateAllUsers(invoke, outValue);
- needsAssumeInstruction = true;
+ if (outValue != null && outValue.hasNonDebugUsers()) {
+ needsAssumeInstruction =
+ computeAssumedValuesForOutValue(
+ invoke,
+ optimizationInfo.getDynamicUpperBoundTypeOrElse(outValue.getType()),
+ optimizationInfo.getDynamicLowerBoundType(),
+ assumedValuesBuilder);
}
// Case (3), parameters that are not null after the invocation.
@@ -239,31 +262,116 @@
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) -> {
+ (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.
if (assumedValue.isPhi()) {
return false;
}
+
Instruction definition = assumedValue.definition;
- return definition != instruction && assumedValues.contains(definition, assumedValue);
+ 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;
});
}
- private Map<Instruction, Set<Value>> computeDominanceForAssumedValues(
+ private Map<Instruction, Map<Value, AssumedValueInfo>> computeDominanceForAssumedValues(
IRCode code, AssumedValues assumedValues) {
- Map<Instruction, Set<Value>> redundantKeys = new IdentityHashMap<>();
+ Map<Instruction, Map<Value, AssumedValueInfo>> redundantAssumedValues = new IdentityHashMap<>();
LazyDominatorTree lazyDominatorTree = new LazyDominatorTree(code);
Map<BasicBlock, Set<BasicBlock>> dominatedBlocksCache = new IdentityHashMap<>();
assumedValues.computeDominance(
- (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();
+ (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);
+ }
}
- // If this value is non-null since its definition, then it is known to dominate all users.
+ // If this value is the out-value of some instruction it is known to dominate all users.
if (assumedValue == instruction.outValue()) {
return AssumedDominance.everything();
}
@@ -327,9 +435,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.
- redundantKeys
- .computeIfAbsent(user, ignore -> Sets.newIdentityHashSet())
- .add(assumedValue);
+ redundantAssumedValues
+ .computeIfAbsent(user, ignore -> new IdentityHashMap<>())
+ .put(assumedValue, assumedValueInfo);
}
}
for (Phi user : assumedValue.uniquePhiUsers()) {
@@ -341,68 +449,54 @@
}
return dominance.build();
});
- return redundantKeys;
+ return redundantAssumedValues;
}
private void removeRedundantDominatedAssumeInstructions(
- AssumedValues assumedValues, Map<Instruction, Set<Value>> redundantKeys) {
- assumedValues.removeAll(redundantKeys);
+ AssumedValues assumedValues,
+ Map<Instruction, Map<Value, AssumedValueInfo>> redundantAssumedValues) {
+ assumedValues.removeAll(redundantAssumedValues);
}
private void materializeAssumeInstructions(IRCode code, AssumedValues assumedValues) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
Map<BasicBlock, Map<Instruction, List<Instruction>>> pendingInsertions =
new IdentityHashMap<>();
- assumedValues.forEach(
- (instruction, assumedValue, assumedValueInfo) -> {
- BasicBlock block = instruction.getBlock();
- BasicBlock insertionBlock = getInsertionBlock(instruction);
- AssumedDominance dominance = assumedValueInfo.getDominance();
- Value newValue =
- code.createValue(
- assumedValue.getType().asReferenceType().asMeetWithNotNull(),
- assumedValue.getLocalInfo());
- if (dominance.isEverything()) {
- assumedValue.replaceUsers(newValue);
- } else if (dominance.isEverythingElse()) {
- assumedValue.replaceSelectiveInstructionUsers(newValue, user -> user != instruction);
- assumedValue.replacePhiUsers(newValue);
- } else if (dominance.isSomething()) {
- SomethingAssumedDominance somethingDominance = dominance.asSomething();
- somethingDominance
- .getDominatedPhiUsers()
- .forEach(
- (user, indices) -> {
- IntListIterator iterator = indices.iterator();
- while (iterator.hasNext()) {
- Value operand = user.getOperand(iterator.nextInt());
- if (operand != assumedValue) {
- assert operand.isDefinedByInstructionSatisfying(
- Instruction::isAssumeNonNull);
- iterator.remove();
- }
- }
- });
- assumedValue.replaceSelectiveUsers(
- newValue,
- somethingDominance.getDominatedUsers(),
- somethingDominance.getDominatedPhiUsers());
- }
- affectedValues.addAll(newValue.affectedValues());
-
- Assume assumeInstruction =
- Assume.createAssumeNonNullInstruction(newValue, assumedValue, instruction, appView);
- assumeInstruction.setPosition(instruction.getPosition());
- if (insertionBlock != block) {
- insertionBlock.listIterator(code).add(assumeInstruction);
- } else {
- pendingInsertions
- .computeIfAbsent(block, ignore -> new IdentityHashMap<>())
- .computeIfAbsent(instruction, ignore -> new ArrayList<>())
- .add(assumeInstruction);
- }
- });
+ // 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);
@@ -420,6 +514,83 @@
}
}
+ private void materializeSelectedAssumeInstructions(
+ IRCode code,
+ AssumedValues assumedValues,
+ Set<Value> affectedValues,
+ Map<BasicBlock, Map<Instruction, List<Instruction>>> pendingInsertions,
+ Predicate<AssumedValueInfo> predicate) {
+ assumedValues.removeIf(
+ (instruction, assumedValue, assumedValueInfo) -> {
+ if (!predicate.test(assumedValueInfo)) {
+ return false;
+ }
+
+ BasicBlock block = instruction.getBlock();
+ BasicBlock insertionBlock = getInsertionBlock(instruction);
+
+ AssumedDominance dominance = assumedValueInfo.getDominance();
+ Value newValue =
+ assumedValueInfo.isNull()
+ ? code.createValue(TypeElement.getNull())
+ : code.createValue(
+ assumedValueInfo.isNonNull()
+ ? assumedValue.getType().asReferenceType().asMeetWithNotNull()
+ : assumedValue.getType(),
+ assumedValue.getLocalInfo());
+ if (dominance.isEverything()) {
+ assumedValue.replaceUsers(newValue);
+ } else if (dominance.isEverythingElse()) {
+ assumedValue.replaceSelectiveInstructionUsers(newValue, user -> user != instruction);
+ assumedValue.replacePhiUsers(newValue);
+ } else if (dominance.isSomething()) {
+ SomethingAssumedDominance somethingDominance = dominance.asSomething();
+ somethingDominance
+ .getDominatedPhiUsers()
+ .forEach(
+ (user, indices) -> {
+ IntListIterator iterator = indices.iterator();
+ while (iterator.hasNext()) {
+ Value operand = user.getOperand(iterator.nextInt());
+ if (operand != assumedValue) {
+ assert operand.isDefinedByInstructionSatisfying(Instruction::isAssume);
+ iterator.remove();
+ }
+ }
+ });
+ assumedValue.replaceSelectiveUsers(
+ newValue,
+ somethingDominance.getDominatedUsers(),
+ somethingDominance.getDominatedPhiUsers());
+ }
+ affectedValues.addAll(newValue.affectedValues());
+
+ Instruction assumeInstruction;
+ if (assumedValueInfo.isNull()) {
+ assumeInstruction = new ConstNumber(newValue, 0);
+ } else {
+ assumeInstruction =
+ new Assume(
+ assumedValueInfo.dynamicTypeAssumption,
+ assumedValueInfo.nonNullAssumption,
+ newValue,
+ assumedValue,
+ instruction,
+ appView);
+ }
+ assumeInstruction.setPosition(instruction.getPosition());
+ if (insertionBlock != block) {
+ insertionBlock.listIterator(code).add(assumeInstruction);
+ } else {
+ pendingInsertions
+ .computeIfAbsent(block, ignore -> new IdentityHashMap<>())
+ .computeIfAbsent(instruction, ignore -> new ArrayList<>())
+ .add(assumeInstruction);
+ }
+ return true;
+ });
+ }
+
private BasicBlock getInsertionBlock(Instruction instruction) {
if (instruction.isIf()) {
return instruction.asIf().targetFromNonNullObject();
@@ -461,10 +632,6 @@
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)) {
@@ -483,6 +650,7 @@
static class AssumedValueInfo {
AssumedDominance dominance;
+ DynamicTypeAssumption dynamicTypeAssumption;
NonNullAssumption nonNullAssumption;
AssumedValueInfo(AssumedDominance dominance) {
@@ -497,9 +665,52 @@
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 isNull() {
+ return dynamicTypeAssumption != null
+ && dynamicTypeAssumption.getDynamicUpperBoundType().isNullType();
+ }
+
+ 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 {
@@ -519,7 +730,8 @@
return new Builder();
}
- void computeDominance(BiFunction<Instruction, Value, AssumedDominance> function) {
+ void computeDominance(
+ TriFunction<Instruction, Value, AssumedValueInfo, AssumedDominance> function) {
Iterator<Entry<Instruction, Map<Value, AssumedValueInfo>>> outerIterator =
assumedValues.entrySet().iterator();
while (outerIterator.hasNext()) {
@@ -539,7 +751,7 @@
continue;
}
assert dominance.isUnknown();
- dominance = function.apply(instruction, assumedValue);
+ dominance = function.apply(instruction, assumedValue, assumedValueInfo);
if ((dominance.isNothing() && !assumedValue.isArgument()) || dominance.isUnknown()) {
innerIterator.remove();
} else {
@@ -552,9 +764,9 @@
}
}
- boolean contains(Instruction instruction, Value assumedValue) {
+ AssumedValueInfo getAssumedValueInfo(Instruction instruction, Value assumedValue) {
Map<Value, AssumedValueInfo> dominancePerValue = assumedValues.get(instruction);
- return dominancePerValue != null && dominancePerValue.containsKey(assumedValue);
+ return dominancePerValue != null ? dominancePerValue.get(assumedValue) : null;
}
boolean isEmpty() {
@@ -569,12 +781,12 @@
consumer.accept(instruction, assumedValue, assumedValueInfo)));
}
- void removeAll(Map<Instruction, Set<Value>> keys) {
+ void removeAll(Map<Instruction, Map<Value, AssumedValueInfo>> keys) {
keys.forEach(
- (instruction, values) -> {
+ (instruction, redundantAssumedValues) -> {
Map<Value, AssumedValueInfo> dominancePerValue = assumedValues.get(instruction);
if (dominancePerValue != null) {
- values.forEach(dominancePerValue::remove);
+ redundantAssumedValues.keySet().forEach(dominancePerValue::remove);
if (dominancePerValue.isEmpty()) {
assumedValues.remove(instruction);
}
@@ -582,7 +794,7 @@
});
}
- void removeIf(BiPredicate<Instruction, Value> predicate) {
+ void removeIf(TriPredicate<Instruction, Value, AssumedValueInfo> predicate) {
Iterator<Entry<Instruction, Map<Value, AssumedValueInfo>>> outerIterator =
assumedValues.entrySet().iterator();
while (outerIterator.hasNext()) {
@@ -592,8 +804,10 @@
Iterator<Entry<Value, AssumedValueInfo>> innerIterator =
dominancePerValue.entrySet().iterator();
while (innerIterator.hasNext()) {
- Value assumedValue = innerIterator.next().getKey();
- if (predicate.test(instruction, assumedValue)) {
+ Entry<Value, AssumedValueInfo> innerEntry = innerIterator.next();
+ Value assumedValue = innerEntry.getKey();
+ AssumedValueInfo assumedValueInfo = innerEntry.getValue();
+ if (predicate.test(instruction, assumedValue, assumedValueInfo)) {
innerIterator.remove();
}
}
@@ -609,29 +823,49 @@
new LinkedHashMap<>();
// Used to avoid unnecessary block splitting during phase 1.
- private final Set<Value> assumedValuesKnownToDominateAllUsers = Sets.newIdentityHashSet();
+ private final Set<Value> nonNullValuesKnownToDominateAllUsers = Sets.newIdentityHashSet();
- 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);
+ 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);
}
}
+ 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) {
- addNonNullValue(instruction, nonNullValue, AssumedDominance.everything());
+ updateAssumedValueInfo(
+ instruction, nonNullValue, AssumedDominance.everything(), AssumedValueInfo::setNotNull);
}
void addNonNullValueWithUnknownDominance(Instruction instruction, Value nonNullValue) {
- addNonNullValue(instruction, nonNullValue, AssumedDominance.unknown());
+ updateAssumedValueInfo(
+ instruction, nonNullValue, AssumedDominance.unknown(), AssumedValueInfo::setNotNull);
}
public boolean isMaybeNull(Value value) {
- return !assumedValuesKnownToDominateAllUsers.contains(value);
+ return !nonNullValuesKnownToDominateAllUsers.contains(value);
}
public AssumedValues build() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
similarity index 60%
rename from src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
rename to src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
index 7f45db3..41f72cb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeDynamicTypeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
@@ -16,60 +16,68 @@
import java.util.Set;
/**
- * 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.
+ * 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).
*
* <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, not does it remove all Assume instructions with a DynamicTypeAssumption.
+ * instructions.
*/
-public class AssumeDynamicTypeRemover {
+public class AssumeRemover {
private final AppView<?> appView;
private final IRCode code;
private final Set<Value> affectedValues = Sets.newIdentityHashSet();
- private final Set<Assume> assumeDynamicTypeInstructionsToRemove = Sets.newIdentityHashSet();
+ private final Set<Assume> assumeInstructionsToRemove = Sets.newIdentityHashSet();
private boolean mayHaveIntroducedTrivialPhi = false;
- public AssumeDynamicTypeRemover(AppView<?> appView, IRCode code) {
+ public AssumeRemover(AppView<?> appView, IRCode code) {
this.appView = appView;
this.code = code;
}
+ public Set<Value> getAffectedValues() {
+ return affectedValues;
+ }
+
public boolean mayHaveIntroducedTrivialPhi() {
return mayHaveIntroducedTrivialPhi;
}
- public void markForRemoval(Assume assumeDynamicTypeInstruction) {
- assumeDynamicTypeInstructionsToRemove.add(assumeDynamicTypeInstruction);
- }
-
- public void markUsersForRemoval(Value value) {
+ public void markAssumeDynamicTypeUsersForRemoval(Value value) {
for (Instruction user : value.aliasedUsers()) {
- if (user.isAssumeDynamicType()) {
- markForRemoval(user.asAssume());
+ if (user.isAssume()) {
+ Assume assumeInstruction = user.asAssume();
+ assumeInstruction.unsetDynamicTypeAssumption();
+ if (!assumeInstruction.hasNonNullAssumption()) {
+ assumeInstruction.unsetDynamicTypeAssumption();
+ }
}
}
}
+ private void markForRemoval(Assume assumeInstruction) {
+ assumeInstructionsToRemove.add(assumeInstruction);
+ }
+
public void removeIfMarked(
- Assume assumeDynamicTypeInstruction, InstructionListIterator instructionIterator) {
- if (assumeDynamicTypeInstructionsToRemove.remove(assumeDynamicTypeInstruction)) {
- Value inValue = assumeDynamicTypeInstruction.src();
- Value outValue = assumeDynamicTypeInstruction.outValue();
+ Assume assumeInstruction, InstructionListIterator instructionIterator) {
+ if (assumeInstructionsToRemove.remove(assumeInstruction)) {
+ Value inValue = assumeInstruction.src();
+ Value outValue = assumeInstruction.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.numberOfPhiUsers() > 0) {
+ if (outValue.hasPhiUsers()) {
mayHaveIntroducedTrivialPhi = true;
}
@@ -78,16 +86,20 @@
}
}
- public AssumeDynamicTypeRemover removeMarkedInstructions(Set<BasicBlock> blocksToBeRemoved) {
- if (!assumeDynamicTypeInstructionsToRemove.isEmpty()) {
+ public AssumeRemover removeMarkedInstructions() {
+ return removeMarkedInstructions(null);
+ }
+
+ public AssumeRemover removeMarkedInstructions(Set<BasicBlock> blocksToBeRemoved) {
+ if (!assumeInstructionsToRemove.isEmpty()) {
for (BasicBlock block : code.blocks) {
- if (blocksToBeRemoved.contains(block)) {
+ if (blocksToBeRemoved != null && blocksToBeRemoved.contains(block)) {
continue;
}
InstructionListIterator instructionIterator = block.listIterator(code);
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
- if (instruction.isAssumeDynamicType()) {
+ if (instruction.isAssume()) {
removeIfMarked(instruction.asAssume(), instructionIterator);
}
}
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 3181f66..f2cefad 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,7 +7,6 @@
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;
@@ -85,7 +84,6 @@
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;
@@ -159,21 +157,6 @@
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
@@ -1249,7 +1232,7 @@
return false;
}
- AssumeDynamicTypeRemover assumeDynamicTypeRemover = new AssumeDynamicTypeRemover(appView, code);
+ AssumeRemover assumeRemover = new AssumeRemover(appView, code);
boolean changed = false;
boolean mayHaveRemovedTrivialPhi = false;
Set<Value> affectedValues = Sets.newIdentityHashSet();
@@ -1282,7 +1265,7 @@
// return false unless it is object.
if (argument.getType().lessThanOrEqual(outValue.getType(), appView)) {
affectedValues.addAll(outValue.affectedValues());
- assumeDynamicTypeRemover.markUsersForRemoval(outValue);
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(outValue);
mayHaveRemovedTrivialPhi |= outValue.numberOfPhiUsers() > 0;
outValue.replaceUsers(argument);
invoke.setOutValue(null);
@@ -1292,12 +1275,12 @@
}
}
}
- assumeDynamicTypeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
+ assumeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
if (!blocksToBeRemoved.isEmpty()) {
code.removeBlocks(blocksToBeRemoved);
code.removeAllDeadAndTrivialPhis(affectedValues);
assert code.getUnreachableBlocks().isEmpty();
- } else if (mayHaveRemovedTrivialPhi || assumeDynamicTypeRemover.mayHaveIntroducedTrivialPhi()) {
+ } else if (mayHaveRemovedTrivialPhi || assumeRemover.mayHaveIntroducedTrivialPhi()) {
code.removeAllDeadAndTrivialPhis(affectedValues);
}
if (!affectedValues.isEmpty()) {
@@ -1502,7 +1485,9 @@
if (result == InstanceOfResult.UNKNOWN) {
Value aliasedValue =
inValue.getSpecificAliasedValue(
- value -> !value.isPhi() && value.definition.isAssumeDynamicType());
+ value ->
+ value.isDefinedByInstructionSatisfying(
+ Instruction::isAssumeWithDynamicTypeAssumption));
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 7752d6c..cb8f858 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,6 +53,7 @@
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);
@@ -76,7 +77,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.isAssumeNonNull()) {
+ if (current.isAssumeWithNonNullAssumption()) {
Assume nonNull = current.asAssume();
Instruction origin = nonNull.origin();
if (origin.isInvokeInterface()
@@ -253,8 +254,8 @@
it.next();
}
}
-
affectedValues.addAll(receiver.affectedValues());
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(receiver);
if (!receiver.hasLocalInfo()) {
receiver.replaceSelectiveUsers(
newReceiver, ImmutableSet.of(devirtualizedInvoke), ImmutableMap.of());
@@ -266,6 +267,8 @@
}
}
}
+ 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 6e2d9c7..20e0515 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,36 +4,19 @@
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 implements Assumer {
+public class DynamicTypeOptimization {
private final AppView<AppInfoWithLiveness> appView;
@@ -41,125 +24,6 @@
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 17cea8b..1c5ff8b 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) {
- AssumeDynamicTypeRemover assumeDynamicTypeRemover = new AssumeDynamicTypeRemover(appView, code);
+ AssumeRemover assumeRemover = new AssumeRemover(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) {
- assumeDynamicTypeRemover.markUsersForRemoval(outValue);
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(outValue);
}
boolean inlineeMayHaveInvokeMethod = inlinee.code.metadata().mayHaveInvokeMethod();
@@ -1073,14 +1073,14 @@
IteratorUtils.previousUntil(blockIterator, previous -> previous == block);
blockIterator.next();
}
- } else if (current.isAssumeDynamicType()) {
- assumeDynamicTypeRemover.removeIfMarked(current.asAssume(), iterator);
+ } else if (current.isAssume()) {
+ assumeRemover.removeIfMarked(current.asAssume(), iterator);
}
}
}
assert inlineeStack.isEmpty();
- assumeDynamicTypeRemover.removeMarkedInstructions(blocksToRemove);
- assumeDynamicTypeRemover.finish();
+ assumeRemover.removeMarkedInstructions(blocksToRemove);
+ assumeRemover.finish();
classInitializationAnalysis.finish();
code.removeBlocks(blocksToRemove);
code.removeAllDeadAndTrivialPhis();
@@ -1129,50 +1129,33 @@
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 (options.enableValuePropagation) {
+ if (appView.options().enableValuePropagation) {
rewindBlockIteratorToFirstInlineeBlock(blockIterator, block);
applyMemberValuePropagationToInlinee(code, blockIterator, block, inlineeBlocks);
}
// Add non-null IRs only to the inlinee blocks.
- if (options.enableNonNullTracking) {
- Assumer nonNullTracker = new AssumeInserter(appView);
- applyAssumerToInlinee(nonNullTracker, code, blockIterator, block, inlineeBlocks, timing);
- }
+ insertAssumeInstructions(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 applyAssumerToInlinee(
- Assumer assumer,
+ private void insertAssumeInstructions(
IRCode code,
BasicBlockIterator blockIterator,
BasicBlock block,
Set<BasicBlock> inlineeBlocks,
Timing timing) {
rewindBlockIteratorToFirstInlineeBlock(blockIterator, block);
- assumer.insertAssumeInstructionsInBlocks(code, blockIterator, inlineeBlocks::contains, timing);
+ new AssumeInserter(appView)
+ .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 d381afb..da5994e 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) {
- AssumeDynamicTypeRemover assumeDynamicTypeRemover = new AssumeDynamicTypeRemover(appView, code);
+ AssumeRemover assumeRemover = new AssumeRemover(appView, code);
Set<BasicBlock> blocksToBeRemoved = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blockIterator = code.listIterator();
Set<Value> affectedValues = Sets.newIdentityHashSet();
@@ -352,13 +352,13 @@
blockIterator,
instructionIterator,
code,
- assumeDynamicTypeRemover,
+ assumeRemover,
affectedValues,
blocksToBeRemoved);
}
}
}
- assumeDynamicTypeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
+ assumeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
code.removeBlocks(blocksToBeRemoved);
code.removeAllDeadAndTrivialPhis(affectedValues);
code.removeUnreachableBlocks();
@@ -398,7 +398,7 @@
ListIterator<BasicBlock> blockIterator,
InstructionListIterator instructionIterator,
IRCode code,
- AssumeDynamicTypeRemover assumeDynamicTypeRemover,
+ AssumeRemover assumeRemover,
Set<Value> affectedValues,
Set<BasicBlock> blocksToBeRemoved) {
DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.context());
@@ -421,8 +421,7 @@
DexType returnType = target.method.proto.returnType;
if (returnType.isAlwaysNull(appView)) {
- replaceOutValueByNull(
- invoke, instructionIterator, code, assumeDynamicTypeRemover, affectedValues);
+ replaceOutValueByNull(invoke, instructionIterator, code, assumeRemover, affectedValues);
}
}
@@ -430,13 +429,13 @@
Instruction instruction,
InstructionListIterator instructionIterator,
IRCode code,
- AssumeDynamicTypeRemover assumeDynamicTypeRemover,
+ AssumeRemover assumeRemover,
Set<Value> affectedValues) {
assert instructionIterator.peekPrevious() == instruction;
if (instruction.hasOutValue()) {
Value outValue = instruction.outValue();
if (outValue.numberOfAllUsers() > 0) {
- assumeDynamicTypeRemover.markUsersForRemoval(outValue);
+ assumeRemover.markAssumeDynamicTypeUsersForRemoval(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 59e24a6..0e3e339 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 implements MethodOptimizationInfo {
+public class DefaultMethodOptimizationInfo extends MethodOptimizationInfo {
public static final MethodOptimizationInfo DEFAULT_INSTANCE = new DefaultMethodOptimizationInfo();
@@ -124,11 +124,6 @@
}
@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 09ad5e5..0365122 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,6 +26,11 @@
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 16ce797..1b79eb8 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 interface MethodOptimizationInfo {
+public abstract class MethodOptimizationInfo {
enum InlinePreference {
NeverInline,
@@ -23,63 +23,66 @@
Default
}
- boolean isDefaultMethodOptimizationInfo();
+ public abstract boolean isDefaultMethodOptimizationInfo();
- boolean isUpdatableMethodOptimizationInfo();
+ public abstract boolean isUpdatableMethodOptimizationInfo();
- UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo();
+ public abstract UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo();
- boolean cannotBeKept();
+ public abstract boolean cannotBeKept();
- boolean classInitializerMayBePostponed();
+ public abstract boolean classInitializerMayBePostponed();
- TypeElement getDynamicUpperBoundType();
+ public abstract TypeElement getDynamicUpperBoundType();
- ClassTypeElement getDynamicLowerBoundType();
+ public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
+ TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
+ return dynamicUpperBoundType != null ? dynamicUpperBoundType : orElse;
+ }
- ParameterUsage getParameterUsages(int parameter);
+ public abstract ClassTypeElement getDynamicLowerBoundType();
- BitSet getNonNullParamOrThrow();
+ public abstract ParameterUsage getParameterUsages(int parameter);
- BitSet getNonNullParamOnNormalExits();
+ public abstract BitSet getNonNullParamOrThrow();
- boolean hasBeenInlinedIntoSingleCallSite();
+ public abstract BitSet getNonNullParamOnNormalExits();
- boolean isReachabilitySensitive();
+ public abstract boolean hasBeenInlinedIntoSingleCallSite();
- boolean returnsArgument();
+ public abstract boolean isReachabilitySensitive();
- int getReturnedArgument();
+ public abstract boolean returnsArgument();
- boolean neverReturnsNull();
+ public abstract int getReturnedArgument();
- boolean neverReturnsNormally();
+ public abstract boolean neverReturnsNormally();
- BridgeInfo getBridgeInfo();
+ public abstract BridgeInfo getBridgeInfo();
- ClassInlinerEligibilityInfo getClassInlinerEligibility();
+ public abstract ClassInlinerEligibilityInfo getClassInlinerEligibility();
- Set<DexType> getInitializedClassesOnNormalExit();
+ public abstract Set<DexType> getInitializedClassesOnNormalExit();
- InstanceInitializerInfo getInstanceInitializerInfo();
+ public abstract InstanceInitializerInfo getInstanceInitializerInfo();
- boolean isInitializerEnablingJavaVmAssertions();
+ public abstract boolean isInitializerEnablingJavaVmAssertions();
- AbstractValue getAbstractReturnValue();
+ public abstract AbstractValue getAbstractReturnValue();
- boolean forceInline();
+ public abstract boolean forceInline();
- boolean neverInline();
+ public abstract boolean neverInline();
- boolean checksNullReceiverBeforeAnySideEffect();
+ public abstract boolean checksNullReceiverBeforeAnySideEffect();
- boolean triggersClassInitBeforeAnySideEffect();
+ public abstract boolean triggersClassInitBeforeAnySideEffect();
- boolean mayHaveSideEffects();
+ public abstract boolean mayHaveSideEffects();
- boolean returnValueOnlyDependsOnArguments();
+ public abstract boolean returnValueOnlyDependsOnArguments();
- boolean returnValueHasBeenPropagated();
+ public abstract boolean returnValueHasBeenPropagated();
- UpdatableMethodOptimizationInfo mutableCopy();
+ public abstract 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 cac5801..21ec659 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,14 +360,12 @@
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();
@@ -382,9 +380,6 @@
}
}
}
- if (isNeverNull) {
- feedback.methodNeverReturnsNull(method);
- }
}
private void computeInstanceInitializerInfo(
@@ -1156,7 +1151,7 @@
// Collect basic blocks that check nullability of the parameter.
nullCheckedBlocks.clear();
for (Instruction user : argument.uniqueUsers()) {
- if (user.isAssumeNonNull()) {
+ if (user.isAssumeWithNonNullAssumption()) {
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 d4eef20..d9db007 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,11 +201,6 @@
}
@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 435f05f..49ffaf9 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,9 +90,6 @@
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 9696162..221fbd9 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) {
- // Ignored.
+ method.getMutableOptimizationInfo().markReturnsObjectWithUpperBoundType(appView, type);
}
@Override
@@ -124,11 +124,6 @@
}
@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 c1012ca..1728999 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 implements MethodOptimizationInfo {
+public class UpdatableMethodOptimizationInfo extends 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 NEVER_RETURNS_NULL_FLAG = 0x20;
+ private static final int UNUSED_FLAG_1 = 0x20;
private static final int NEVER_RETURNS_NORMALLY_FLAG = 0x40;
- private static final int UNUSED_FLAG = 0x80;
+ private static final int UNUSED_FLAG_2 = 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,11 +97,10 @@
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.returnValueOnlyDependsOnArguments())
* RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS_FLAG;
- defaultFlags |=
- BooleanUtils.intValue(defaultOptInfo.neverReturnsNull()) * NEVER_RETURNS_NULL_FLAG;
+ defaultFlags |= 0 * UNUSED_FLAG_1;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.neverReturnsNormally()) * NEVER_RETURNS_NORMALLY_FLAG;
- defaultFlags |= 0 * UNUSED_FLAG;
+ defaultFlags |= 0 * UNUSED_FLAG_2;
defaultFlags |=
BooleanUtils.intValue(defaultOptInfo.checksNullReceiverBeforeAnySideEffect())
* CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG;
@@ -293,11 +292,6 @@
}
@Override
- public boolean neverReturnsNull() {
- return isFlagSet(NEVER_RETURNS_NULL_FLAG);
- }
-
- @Override
public boolean neverReturnsNormally() {
return isFlagSet(NEVER_RETURNS_NORMALLY_FLAG);
}
@@ -402,10 +396,6 @@
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 3a7e099..ad6313e 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,8 +44,7 @@
register(new ObjectMethodOptimizer(appView));
register(new ObjectsMethodOptimizer(appView));
register(new StringMethodOptimizer(appView));
- if (appView.enableWholeProgramOptimizations()
- && appView.options().enableDynamicTypeOptimization) {
+ if (appView.enableWholeProgramOptimizations()) {
// 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 a677b43..92c78af 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,12 +4,15 @@
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;
@@ -57,7 +60,16 @@
for (DexMethod method : dexItemFactory.libraryMethodsReturningNonNull) {
DexEncodedMethod definition = lookupMethod(method);
if (definition != null) {
- feedback.methodNeverReturnsNull(definition);
+ TypeElement staticType =
+ TypeElement.fromDexType(method.proto.returnType, maybeNull(), appView);
+ feedback.methodReturnsObjectWithUpperBoundType(
+ definition,
+ appView,
+ definition
+ .getOptimizationInfo()
+ .getDynamicUpperBoundTypeOrElse(staticType)
+ .asReferenceType()
+ .asDefinitelyNotNull());
}
}
}
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 0e0e1c9..29250dc 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,6 +35,7 @@
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;
@@ -390,7 +391,10 @@
}
private void insertAssumeInstructions(IRCode code, MethodProcessor methodProcessor) {
- CodeRewriter.insertAssumeInstructions(code, converter.assumers, Timing.empty());
+ AssumeInserter assumeInserter = converter.assumeInserter;
+ if (assumeInserter != null) {
+ assumeInserter.insertAssumeInstructions(code, 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 8c92acf..5a286b7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -172,14 +172,12 @@
void disableAllOptimizations() {
disableGlobalOptimizations();
- enableNonNullTracking = false;
enableNameReflectionOptimization = false;
enableStringConcatenationOptimization = false;
}
public void disableGlobalOptimizations() {
enableArgumentRemoval = false;
- enableDynamicTypeOptimization = false;
enableInlining = false;
enableClassInlining = false;
enableClassStaticizer = false;
@@ -215,7 +213,6 @@
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;
@@ -224,7 +221,6 @@
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
new file mode 100644
index 0000000..42b76a7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/TriPredicate.java
@@ -0,0 +1,9 @@
+// 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 dac8b92..a9f77ce 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.isAssumeNonNull()) {
+ if (curr.isAssumeWithNonNullAssumption()) {
// 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.isAssumeNonNull());
+ assertFalse(iput.value().definition.isAssumeWithNonNullAssumption());
} else if (count == 1) {
// Second one after a safe invocation, which should use the value added by
// NonNullMarker.
- assertTrue(iput.object().definition.isAssumeNonNull());
+ assertTrue(iput.object().definition.isAssumeWithNonNullAssumption());
}
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 4cab645..ba6d920 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,13 +7,11 @@
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;
@@ -26,17 +24,14 @@
@RunWith(Parameterized.class)
public class DynamicTypeOptimizationTest extends TestBase {
- private final boolean enableDynamicTypeOptimization;
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{1}, enable dynamic type optimization: {0}")
+ @Parameterized.Parameters(name = "{0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public DynamicTypeOptimizationTest(
- boolean enableDynamicTypeOptimization, TestParameters parameters) {
- this.enableDynamicTypeOptimization = enableDynamicTypeOptimization;
+ public DynamicTypeOptimizationTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -47,10 +42,8 @@
.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.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
@@ -81,8 +74,7 @@
MethodSubject testInstanceOfRemovalMethod =
mainClassSubject.uniqueMethodWithName("testInstanceOfRemoval");
assertThat(testInstanceOfRemovalMethod, isPresent());
- assertEquals(
- enableDynamicTypeOptimization,
+ assertTrue(
testInstanceOfRemovalMethod
.streamInstructions()
.noneMatch(instruction -> instruction.isInstanceOf(aClassSubject.getFinalName())));
@@ -92,27 +84,16 @@
MethodSubject testMethodInliningMethod =
mainClassSubject.uniqueMethodWithName("testMethodInlining");
assertThat(testMethodInliningMethod, isPresent());
- assertEquals(
- enableDynamicTypeOptimization, interfaceSubject.uniqueMethodWithName("world").isAbsent());
- if (!enableDynamicTypeOptimization) {
- assertThat(
- testMethodInliningMethod, invokesMethod(interfaceSubject.uniqueMethodWithName("world")));
- }
+ assertTrue(interfaceSubject.uniqueMethodWithName("world").isAbsent());
// Verify that exclamationMark() has been rebound in testMethodRebinding() unless the dynamic
// type optimization is disabled.
MethodSubject testMethodRebindingMethod =
mainClassSubject.uniqueMethodWithName("testMethodRebinding");
assertThat(testMethodRebindingMethod, isPresent());
- if (enableDynamicTypeOptimization) {
- assertThat(
- testMethodRebindingMethod,
- invokesMethod(aClassSubject.uniqueMethodWithName("exclamationMark")));
- } else {
- assertThat(
- testMethodRebindingMethod,
- invokesMethod(interfaceSubject.uniqueMethodWithName("exclamationMark")));
- }
+ assertThat(
+ testMethodRebindingMethod,
+ invokesMethod(aClassSubject.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 1cbeaae..791bbae 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,7 +9,6 @@
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;
@@ -111,16 +110,14 @@
}
}
- @Parameters(name = "{1}, enable dynamic type optimization: {0}")
+ @Parameters(name = "{0}")
public static List<Object[]> data() {
- return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
}
- private final boolean enableDynamicTypeOptimization;
private final TestParameters parameters;
- public InstanceOfRemovalTest(boolean enableDynamicTypeOptimization, TestParameters parameters) {
- this.enableDynamicTypeOptimization = enableDynamicTypeOptimization;
+ public InstanceOfRemovalTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -161,10 +158,8 @@
testForR8(parameters.getBackend())
.addProgramClasses(A.class, B.class, TestClass.class)
.addKeepMainRule(TestClass.class)
- .addOptionsModification(
- options -> options.enableDynamicTypeOptimization = enableDynamicTypeOptimization)
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expected)
.inspector();
@@ -182,7 +177,6 @@
MethodSubject barMethodSubject = testClass.uniqueMethodWithName("bar");
Iterator<InstructionSubject> barInstructionIterator =
barMethodSubject.iterateInstructions(InstructionSubject::isInstanceOf);
- assertEquals(
- enableDynamicTypeOptimization ? 4 : 6, Streams.stream(barInstructionIterator).count());
+ assertEquals(4, Streams.stream(barInstructionIterator).count());
}
}