Merge commit 'c6539655d527593303332f12a9aeb76c8a2ed1c5' into dev-release
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 9b4bb2a..2312362 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -5,9 +5,14 @@
from os import path
import datetime
from subprocess import check_output, Popen, PIPE, STDOUT
-import sys
import inspect
+import os
+import sys
+# Add both current path to allow us to package import utils and the tools
+# dir to allow transitive (for utils) dependendies to be loaded.
sys.path.append(path.dirname(inspect.getfile(lambda: None)))
+sys.path.append(os.path.join(
+ path.dirname(inspect.getfile(lambda: None)), 'tools'))
from tools.utils import EnsureDepFromGoogleCloudStorage
FMT_CMD = path.join(
diff --git a/build.gradle b/build.gradle
index 834e3d6..04704bc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -139,11 +139,6 @@
srcDirs = ['src/test/examplesJava20']
}
}
- examplesTestNGRunner {
- java {
- srcDirs = ['src/test/testngrunner']
- }
- }
examplesAndroidN {
java {
srcDirs = ['src/test/examplesAndroidN']
@@ -245,8 +240,6 @@
main17Implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
main17Implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
- examplesTestNGRunnerCompile group: 'org.testng', name: 'testng', version: testngVersion
-
testCompile sourceSets.examples.output
testCompile "junit:junit:$junitVersion"
testCompile "com.google.guava:guava:$guavaVersion"
@@ -624,11 +617,6 @@
JavaVersion.VERSION_11,
false)
setJdkCompilationWithCompatibility(
- sourceSets.examplesTestNGRunner.compileJavaTaskName,
- 'jdk-11',
- JavaVersion.VERSION_11,
- false)
-setJdkCompilationWithCompatibility(
sourceSets.examplesJava17.compileJavaTaskName,
'jdk-17',
JavaVersion.VERSION_17,
@@ -640,19 +628,6 @@
JavaVersion.VERSION_17,
false)
-task provideJdk11TestsDependencies(type: org.gradle.api.tasks.Copy) {
- from sourceSets.examplesTestNGRunner.compileClasspath
- include "**/**.jar"
- into file("$buildDir/test/jdk11Tests")
-}
-
-task compileTestNGRunner (type: JavaCompile) {
- dependsOn provideJdk11TestsDependencies
- destinationDir = file("$buildDir/classes/java/examplesTestNGRunner")
- source = sourceSets.examplesTestNGRunner.allSource
- classpath = sourceSets.examplesTestNGRunner.compileClasspath
-}
-
if (!project.hasProperty('without_error_prone') &&
// Don't enable error prone on Java 8 as the plugin setup does not support it.
!org.gradle.internal.jvm.Jvm.current().javaVersion.java8) {
@@ -2232,7 +2207,6 @@
dependsOn buildExamples
dependsOn buildKotlinR8TestResources
dependsOn buildPreNJdwpTestsJar
- dependsOn compileTestNGRunner
dependsOn provideArtFrameworksDependencies
} else {
logger.lifecycle("WARNING: Testing in not supported on your platform. Testing is only fully supported on " +
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 080447c..10d9755 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -64,6 +64,7 @@
import com.android.tools.r8.naming.ProguardMapMinifier;
import com.android.tools.r8.naming.RecordRewritingNamingLens;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
+import com.android.tools.r8.optimize.LegacyAccessModifier;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
@@ -452,6 +453,9 @@
// to clear the cache, so that we will recompute the type lattice elements.
appView.dexItemFactory().clearTypeElementsCache();
+ // TODO(b/132677331): Remove legacy access modifier.
+ LegacyAccessModifier.run(appViewWithLiveness, executorService, timing);
+
// This pass attempts to reduce the number of nests and nest size to allow further passes, and
// should therefore be run after the publicizer.
new NestReducer(appViewWithLiveness).run(executorService, timing);
@@ -506,7 +510,7 @@
.notifyHorizontalClassMergerFinished(HorizontalClassMerger.Mode.INITIAL);
// TODO(b/225838009): Horizontal merging currently assumes pre-phase CF conversion.
- appView.testing().enterLirSupportedPhase();
+ appView.testing().enterLirSupportedPhase(appView, executorService);
new ProtoNormalizer(appViewWithLiveness).run(executorService, timing);
@@ -544,10 +548,10 @@
appView.setGraphLens(new AppliedGraphLens(appView));
timing.end();
- // TODO(b/225838009): Support tracing and building LIR in Enqueuer.
- PrimaryR8IRConverter.finalizeLirToOutputFormat(appView, timing, executorService);
-
- if (options.shouldRerunEnqueuer()) {
+ if (!options.shouldRerunEnqueuer()) {
+ // TODO(b/225838009): Support tracing and building LIR in Enqueuer.
+ PrimaryR8IRConverter.finalizeLirToOutputFormat(appView, timing, executorService);
+ } else {
timing.begin("Post optimization code stripping");
try {
GraphConsumer keptGraphConsumer = null;
@@ -574,9 +578,11 @@
EnqueuerResult enqueuerResult =
enqueuer.traceApplication(appView.rootSet(), executorService, timing);
appView.setAppInfo(enqueuerResult.getAppInfo());
+ appView.dissallowFurtherInitClassUses();
+
// Rerunning the enqueuer should not give rise to any method rewritings.
MutableMethodConversionOptions conversionOptions =
- MethodConversionOptions.forPostLirPhase(appView);
+ MethodConversionOptions.forLirPhase(appView);
appView.withGeneratedMessageLiteBuilderShrinker(
shrinker ->
shrinker.rewriteDeadBuilderReferencesFromDynamicMethods(
@@ -650,6 +656,9 @@
timing.end();
}
+ // TODO(b/225838009): Support LIR in proto shrinking.
+ PrimaryR8IRConverter.finalizeLirToOutputFormat(appView, timing, executorService);
+
if (appView.options().protoShrinking().isProtoShrinkingEnabled()) {
if (appView.options().protoShrinking().isEnumLiteProtoShrinkingEnabled()) {
appView.protoShrinker().enumLiteProtoShrinker.verifyDeadEnumLiteMapsAreDead();
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index fe6e49c..825655a 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -10,6 +10,7 @@
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
/** Access flags common to classes, methods and fields. */
public abstract class AccessFlags<T extends AccessFlags<T>> implements StructuralItem<T> {
@@ -65,6 +66,13 @@
return AccessFlags::specify;
}
+ public T applyIf(boolean condition, Consumer<T> fn) {
+ if (condition) {
+ fn.accept(self());
+ }
+ return self();
+ }
+
public abstract T copy();
@Override
@@ -231,8 +239,9 @@
demote(Constants.ACC_SYNTHETIC);
}
- public void promoteToFinal() {
+ public T promoteToFinal() {
promote(Constants.ACC_FINAL);
+ return self();
}
public T demoteFromFinal() {
@@ -248,9 +257,10 @@
return isPromoted(Constants.ACC_PUBLIC);
}
- public void promoteToPublic() {
+ public T promoteToPublic() {
demote(Constants.ACC_PRIVATE | Constants.ACC_PROTECTED);
promote(Constants.ACC_PUBLIC);
+ return self();
}
public T withPublic() {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 06deed3..d8597d2 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -619,8 +619,14 @@
return false;
}
+ private boolean disallowFurtherInitClassUses = false;
+
+ public void dissallowFurtherInitClassUses() {
+ disallowFurtherInitClassUses = true;
+ }
+
public boolean canUseInitClass() {
- return options().isShrinking() && !initClassLens.isFinal();
+ return !disallowFurtherInitClassUses && options().isShrinking();
}
public InitClassLens initClassLens() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index a87febd..538f0e3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -1047,6 +1047,16 @@
permittedSubclasses.removeIf(predicate);
}
+ public void replacePermittedSubclass(
+ DexType currentPermittedSubclass, DexType newPermittedSubclass) {
+ for (int i = 0; i < permittedSubclasses.size(); i++) {
+ if (permittedSubclasses.get(i).getPermittedSubclass() == currentPermittedSubclass) {
+ permittedSubclasses.set(i, new PermittedSubclassAttribute(newPermittedSubclass));
+ return;
+ }
+ }
+ }
+
public boolean isLocalClass() {
InnerClassAttribute innerClass = getInnerClassAttributeForThisClass();
// The corresponding enclosing-method attribute might be not available, e.g., CF version 50.
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java b/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
index b2b6111..c2627bc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.FieldInstruction;
@@ -26,7 +27,7 @@
* that it is dead.
*
* <p>Pruning code that does not verify is necessary in order to be able to assert that the types
- * are sound using {@link Instruction#verifyTypes(AppView, VerifyTypesHelper)}.
+ * are sound using {@link Instruction#verifyTypes(AppView, ProgramMethod, VerifyTypesHelper)}.
*/
public class TypeChecker {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
index 9676db7..1c46b14 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
@@ -19,8 +18,8 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.utils.BooleanBox;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Deque;
@@ -28,7 +27,6 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Implementation of Sparse Conditional Constant Propagation from the paper of Wegman and Zadeck
@@ -107,12 +105,11 @@
}
}
boolean hasChanged = rewriteConstants();
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(hasChanged);
}
private boolean rewriteConstants() {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
List<BasicBlock> blockToAnalyze = new ArrayList<>();
BooleanBox hasChanged = new BooleanBox(false);
mapping.entrySet().stream()
@@ -122,7 +119,6 @@
Value value = entry.getKey();
ConstNumber evaluatedConst = entry.getValue().asConst().getConstNumber();
if (value.definition != evaluatedConst) {
- value.addAffectedValuesTo(affectedValues);
if (value.isPhi()) {
// D8 relies on dead code removal to get rid of the dead phi itself.
if (value.hasAnyUsers()) {
@@ -139,14 +135,14 @@
iterator.previous();
}
iterator.add(newConst);
- value.replaceUsers(newConst.outValue());
+ value.replaceUsers(newConst.outValue(), affectedValues);
hasChanged.set();
}
} else {
BasicBlock block = value.definition.getBlock();
InstructionListIterator iterator = block.listIterator(code);
iterator.nextUntil(i -> i == value.definition);
- iterator.replaceCurrentInstruction(evaluatedConst);
+ iterator.replaceCurrentInstruction(evaluatedConst, affectedValues);
hasChanged.set();
}
}
@@ -154,9 +150,7 @@
for (BasicBlock block : blockToAnalyze) {
block.deduplicatePhis();
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
boolean changed = hasChanged.get();
if (changed) {
code.removeAllDeadAndTrivialPhis();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 613d763..5907290 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -156,6 +156,7 @@
}
IRCodeUtils.removeInstructionAndTransitiveInputsIfNotUsed(code, instruction);
}
+ assert code.isConsistentSSA(appView);
}
public boolean wasRemoved(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 9ecd2a9..322907a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -21,13 +21,13 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.CheckCast;
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.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.SafeCheckCast;
import com.android.tools.r8.ir.code.StaticGet;
@@ -36,6 +36,7 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.callgraph.Node;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
@@ -250,6 +251,10 @@
instruction ->
instruction.isNewInstance() && instruction.asNewInstance().clazz == builder.type);
assert newInstance != null;
+ // Once the new instance is found, create a new linear iterator to allow subsequent instructions
+ // to be in trivially split blocks.
+ instructionIterator = new LinearFlowInstructionListIterator(code, newInstance.getBlock());
+ instructionIterator.nextUntil(i -> i == newInstance);
instructionIterator.replaceCurrentInstruction(new NewInstance(builder.superType, builderValue));
// Replace `builder.<init>()` by `builder.<init>(Message.DEFAULT_INSTANCE)`.
@@ -391,7 +396,7 @@
* MethodToInvoke.NEW_MUTABLE_INSTANCE will create an instance of the enclosing class.
*/
private void strengthenCheckCastInstructions(IRCode code) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
InstructionListIterator instructionIterator = code.instructionListIterator();
CheckCast checkCast;
while ((checkCast = instructionIterator.nextUntil(Instruction::isCheckCast)) != null) {
@@ -425,9 +430,7 @@
}
}
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
}
private static class RootSetExtension {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 608c06f..49b2e91 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
-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.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -44,16 +43,15 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.DependentMinimumKeepInfoCollection;
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
@@ -159,7 +157,7 @@
}
private void optimizeNewMutableInstance(AppView<AppInfoWithLiveness> appView, IRCode code) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
BasicBlockIterator blockIterator = code.listIterator();
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
@@ -214,9 +212,8 @@
}
}
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ assert code.isConsistentSSA(appView);
}
private DexType getNewMutableInstanceType(
@@ -314,6 +311,7 @@
assert false;
}
}
+ assert code.isConsistentSSA(appView);
}
private void rewriteArgumentsToNewMessageInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
index 3cf7971..a22f001 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObjectFromStaticGet;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoTypeObject;
import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
@@ -333,50 +334,67 @@
// Create an iterator for the block of interest.
InstructionIterator instructionIterator = newArrayEmpty.getBlock().iterator();
instructionIterator.nextUntil(instruction -> instruction == newArrayEmpty);
+ return new ArrayPutIterator(instructionIterator, objectsValue);
+ }
- return new ThrowingIterator<Value, InvalidRawMessageInfoException>() {
+ private static class ArrayPutIterator
+ extends ThrowingIterator<Value, InvalidRawMessageInfoException> {
- private int expectedNextIndex = 0;
+ private InstructionIterator instructionIterator;
+ private final Value objectsValue;
+ private int expectedNextIndex;
- @Override
- public boolean hasNext() {
- while (instructionIterator.hasNext()) {
- Instruction next = instructionIterator.peekNext();
- if (isArrayPutOfInterest(next)) {
- return true;
- }
- if (next.isJumpInstruction()) {
- return false;
- }
- instructionIterator.next();
+ public ArrayPutIterator(InstructionIterator instructionIterator, Value objectsValue) {
+ this.instructionIterator = instructionIterator;
+ this.objectsValue = objectsValue;
+ expectedNextIndex = 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (instructionIterator.hasNext()) {
+ Instruction next = instructionIterator.peekNext();
+ if (isArrayPutOfInterest(next)) {
+ return true;
}
- return false;
+ if (next.isGoto()) {
+ // We may have split the block so allow continuing in linear jumps.
+ BasicBlock target = next.asGoto().getTarget();
+ assert target.hasUniquePredecessor();
+ instructionIterator = target.iterator();
+ continue;
+ }
+ if (next.isJumpInstruction()) {
+ return false;
+ }
+ instructionIterator.next();
+ }
+ return false;
+ }
+
+ @Override
+ public Value next() throws InvalidRawMessageInfoException {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ArrayPut arrayPut = instructionIterator.next().asArrayPut();
+
+ // Verify that the index correct.
+ Value indexValue = arrayPut.index().getAliasedValue();
+ if (indexValue.isPhi()
+ || !indexValue.definition.isConstNumber()
+ || indexValue.definition.asConstNumber().getIntValue() != expectedNextIndex) {
+ throw new InvalidRawMessageInfoException();
}
- @Override
- public Value next() throws InvalidRawMessageInfoException {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- ArrayPut arrayPut = instructionIterator.next().asArrayPut();
+ expectedNextIndex++;
+ return arrayPut.value().getAliasedValue();
+ }
- // Verify that the index correct.
- Value indexValue = arrayPut.index().getAliasedValue();
- if (indexValue.isPhi()
- || !indexValue.definition.isConstNumber()
- || indexValue.definition.asConstNumber().getIntValue() != expectedNextIndex) {
- throw new InvalidRawMessageInfoException();
- }
-
- expectedNextIndex++;
- return arrayPut.value().getAliasedValue();
- }
-
- private boolean isArrayPutOfInterest(Instruction instruction) {
- return instruction.isArrayPut()
- && instruction.asArrayPut().array().getAliasedValue() == objectsValue;
- }
- };
+ private boolean isArrayPutOfInterest(Instruction instruction) {
+ return instruction.isArrayPut()
+ && instruction.asArrayPut().array().getAliasedValue() == objectsValue;
+ }
}
private static class InvalidRawMessageInfoException extends Exception {}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
index b0fc54d..1e624e9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
-import com.google.common.collect.Sets;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.ListIterator;
@@ -56,7 +56,7 @@
// replaced with correct types and all other phi operands are BOTTOM.
assert verifyAllPhiOperandsAreBottom(affectedPhis);
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
worklist.addAll(affectedPhis);
while (!worklist.isEmpty()) {
Phi phi = worklist.poll();
@@ -69,13 +69,11 @@
}
}
- assert new TypeAnalysis(appView).verifyValuesUpToDate(affectedPhis);
+ assert TypeAnalysis.verifyValuesUpToDate(appView, code, affectedPhis);
// Now that the types of all transitively type affected phis have been reset, we can
// perform a narrowing, starting from the values that are affected by those phis.
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
}
private boolean verifyAllPhiOperandsAreBottom(Set<Phi> affectedPhis) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
index 1f06630..7fcdde4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeElement.java
@@ -56,13 +56,7 @@
@Override
public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof NullElement)) {
- return false;
- }
- return true;
+ return this == o;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index a0fd66f..9752734 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -7,15 +7,20 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.AssumeRemover;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.ArrayDeque;
-import java.util.Deque;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.function.Consumer;
public class TypeAnalysis {
@@ -29,30 +34,42 @@
private final boolean mayHaveImpreciseTypes;
+ private boolean keepRedundantBlocksAfterAssumeRemoval = false;
private Mode mode = Mode.UNSET;
private final AppView<?> appView;
+ private final AssumeRemover assumeRemover;
+ private final IRCode code;
- private final Deque<Value> worklist = new ArrayDeque<>();
+ private final WorkList<Value> worklist = WorkList.newIdentityWorkList();
- public TypeAnalysis(AppView<?> appView) {
- this(appView, false);
+ public TypeAnalysis(AppView<?> appView, IRCode code) {
+ this(appView, code, false);
}
- public TypeAnalysis(AppView<?> appView, boolean mayHaveImpreciseTypes) {
+ public TypeAnalysis(AppView<?> appView, IRCode code, boolean mayHaveImpreciseTypes) {
this.appView = appView;
+ this.assumeRemover = new AssumeRemover(appView, code);
+ this.code = code;
this.mayHaveImpreciseTypes = mayHaveImpreciseTypes;
}
+ // Allows disabling the removal of redundant blocks after assume removal.
+ public TypeAnalysis setKeepRedundantBlocksAfterAssumeRemoval(
+ boolean keepRedundantBlocksAfterAssumeRemoval) {
+ this.keepRedundantBlocksAfterAssumeRemoval = keepRedundantBlocksAfterAssumeRemoval;
+ return this;
+ }
+
private void analyze() {
- while (!worklist.isEmpty()) {
- analyzeValue(worklist.poll());
+ while (worklist.hasNext()) {
+ analyzeValue(worklist.removeSeen());
}
}
- public void widening(IRCode code) {
+ public void widening() {
mode = Mode.WIDENING;
- assert worklist.isEmpty();
+ assert verifyIsEmpty();
code.topologicallySortedBlocks().forEach(this::analyzeBasicBlock);
analyze();
}
@@ -61,9 +78,9 @@
analyzeValues(values, Mode.WIDENING);
}
- public void narrowing(IRCode code) {
+ public void narrowing() {
mode = Mode.NARROWING;
- assert worklist.isEmpty();
+ assert verifyIsEmpty();
code.topologicallySortedBlocks().forEach(this::analyzeBasicBlock);
analyze();
}
@@ -72,8 +89,47 @@
analyzeValues(values, Mode.NARROWING);
}
- public boolean verifyValuesUpToDate(Iterable<? extends Value> values) {
- analyzeValues(values, Mode.NO_CHANGE);
+ public void narrowingWithAssumeRemoval(Iterable<? extends Value> values) {
+ narrowingWithAssumeRemoval(values, ConsumerUtils.emptyConsumer());
+ }
+
+ public void narrowingWithAssumeRemoval(
+ Iterable<? extends Value> values, Consumer<Assume> redundantAssumeConsumer) {
+ narrowing(values);
+ removeRedundantAssumeInstructions(redundantAssumeConsumer);
+ }
+
+ private void removeRedundantAssumeInstructions(Consumer<Assume> redundantAssumeConsumer) {
+ Set<Value> affectedValuesFromAssumeRemoval = Sets.newIdentityHashSet();
+ while (assumeRemover.removeRedundantAssumeInstructions(
+ affectedValuesFromAssumeRemoval, redundantAssumeConsumer)) {
+ widening(affectedValuesFromAssumeRemoval);
+ Set<Value> affectedValuesFromPhiRemoval = Sets.newIdentityHashSet();
+ code.removeAllDeadAndTrivialPhis(affectedValuesFromPhiRemoval);
+ narrowing(affectedValuesFromPhiRemoval);
+ affectedValuesFromAssumeRemoval.clear();
+ }
+ if (!keepRedundantBlocksAfterAssumeRemoval) {
+ code.removeRedundantBlocks();
+ }
+ }
+
+ public boolean verifyIsEmpty() {
+ assert !assumeRemover.hasAffectedAssumeInstructions();
+ assert worklist.isEmpty();
+ return true;
+ }
+
+ public static void verifyValuesUpToDate(AppView<?> appView, IRCode code) {
+ TypeAnalysis typeAnalysis = new TypeAnalysis(appView, code);
+ typeAnalysis.mode = Mode.NO_CHANGE;
+ code.topologicallySortedBlocks().forEach(typeAnalysis::analyzeBasicBlock);
+ typeAnalysis.analyze();
+ }
+
+ public static boolean verifyValuesUpToDate(
+ AppView<?> appView, IRCode code, Iterable<? extends Value> values) {
+ new TypeAnalysis(appView, code).analyzeValues(values, Mode.NO_CHANGE);
return true;
}
@@ -85,10 +141,7 @@
}
private void enqueue(Value v) {
- assert v != null;
- if (!worklist.contains(v)) {
- worklist.add(v);
- }
+ worklist.addFirstIfNotSeen(v);
}
private void analyzeBasicBlock(BasicBlock block) {
@@ -124,12 +177,27 @@
private void updateTypeOfValue(Value value, TypeElement type) {
assert mode != Mode.UNSET;
+ if (value.isDefinedByInstructionSatisfying(Instruction::isAssume)) {
+ assumeRemover.addAffectedAssumeInstruction(value.getDefinition().asAssume());
+ }
+
TypeElement current = value.getType();
if (current.equals(type)) {
return;
}
- assert mode != Mode.NO_CHANGE;
+ assert mode != Mode.NO_CHANGE
+ : "Unexpected type change for value "
+ + value
+ + " defined by "
+ + (value.isPhi() ? "phi" : value.getDefinition())
+ + ": was "
+ + type
+ + ", but expected "
+ + current
+ + " (context: "
+ + code.context()
+ + ")";
if (type.isBottom()) {
return;
@@ -139,7 +207,7 @@
value.widening(appView, type);
} else {
assert mode == Mode.NARROWING;
- value.narrowing(appView, type);
+ value.narrowing(appView, code.context(), type);
}
// propagate the type change to (instruction) users if any.
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
index a4ed229..020db76 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingDefinition.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.dex.code.DexConst4;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
@@ -55,6 +56,12 @@
}
@Override
+ public TypeElement evaluate(AppView<?> appView) {
+ assert outValue.getType().isInt();
+ return TypeElement.getInt();
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return false;
}
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 174b913..279d1b9 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
@@ -10,14 +10,15 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
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.lightir.LirBuilder;
-import java.util.Objects;
import java.util.Set;
public class Assume extends Instruction {
@@ -25,41 +26,32 @@
private static final String ERROR_MESSAGE =
"Expected Assume instructions to be removed after IR processing.";
- private DynamicTypeAssumption dynamicTypeAssumption;
- private final NonNullAssumption nonNullAssumption;
+ private DynamicType dynamicType;
private final Instruction origin;
- public Assume(
- DynamicTypeAssumption dynamicTypeAssumption,
- NonNullAssumption nonNullAssumption,
- Value dest,
- Value src,
- Instruction origin,
- AppView<?> appView) {
+ public Assume(DynamicType dynamicType, Value dest, Value src, Instruction origin) {
super(dest, src);
- assert dynamicTypeAssumption != null || nonNullAssumption != null;
- assert dynamicTypeAssumption == null
- || dynamicTypeAssumption.verifyCorrectnessOfValues(dest, src, appView);
- assert nonNullAssumption == null
- || nonNullAssumption.verifyCorrectnessOfValues(dest, src, appView);
- assert dest != null;
- this.dynamicTypeAssumption = dynamicTypeAssumption;
- this.nonNullAssumption = nonNullAssumption;
+ assert dynamicType != null;
+ assert !dynamicType.isUnknown();
+ this.dynamicType = dynamicType;
this.origin = origin;
}
- public static Assume createAssumeNonNullInstruction(
- Value dest, Value src, Instruction origin, AppView<?> appView) {
- return new Assume(null, NonNullAssumption.get(), dest, src, origin, appView);
- }
-
- public static Assume createAssumeDynamicTypeInstruction(
- DynamicTypeWithUpperBound dynamicType,
+ public static Assume create(
+ DynamicType dynamicType,
Value dest,
Value src,
Instruction origin,
- AppView<?> appView) {
- return new Assume(new DynamicTypeAssumption(dynamicType), null, dest, src, origin, appView);
+ AppView<?> appView,
+ ProgramMethod context) {
+ Assume assume = new Assume(dynamicType, dest, src, origin);
+ assert assume.verifyInstruction(appView, context);
+ return assume;
+ }
+
+ public void clearDynamicTypeAssumption() {
+ assert dynamicType.getNullability().isDefinitelyNotNull();
+ dynamicType = DynamicType.definitelyNotNull();
}
@Override
@@ -67,24 +59,17 @@
return Opcodes.ASSUME;
}
- public boolean verifyInstructionIsNeeded(AppView<?> appView) {
- if (hasDynamicTypeAssumption()) {
- assert dynamicTypeAssumption.verifyCorrectnessOfValues(outValue(), src(), appView);
- }
- return true;
- }
-
@Override
public <T> T accept(InstructionVisitor<T> visitor) {
return visitor.visit(this);
}
- public DynamicTypeAssumption getDynamicTypeAssumption() {
- return dynamicTypeAssumption;
+ public DynamicType getDynamicType() {
+ return dynamicType;
}
- public NonNullAssumption getNonNullAssumption() {
- return nonNullAssumption;
+ public DynamicTypeWithUpperBound getDynamicTypeAssumption() {
+ return dynamicType.asDynamicTypeWithUpperBound();
}
public Value src() {
@@ -115,16 +100,17 @@
return this;
}
- public boolean hasDynamicTypeAssumption() {
- return dynamicTypeAssumption != null;
- }
-
- public void unsetDynamicTypeAssumption() {
- dynamicTypeAssumption = null;
+ public boolean hasDynamicTypeIgnoringNullability() {
+ if (dynamicType.isNotNullType()) {
+ return false;
+ }
+ assert !dynamicType.isUnknown();
+ assert dynamicType.isDynamicTypeWithUpperBound();
+ return true;
}
public boolean hasNonNullAssumption() {
- return nonNullAssumption != null;
+ return dynamicType.getNullability().isDefinitelyNotNull();
}
@Override
@@ -135,8 +121,8 @@
if (outType.isPrimitiveType()) {
return false;
}
- if (hasDynamicTypeAssumption()) {
- outType = dynamicTypeAssumption.getDynamicType().getDynamicUpperBoundType();
+ if (hasDynamicTypeIgnoringNullability()) {
+ outType = dynamicType.asDynamicTypeWithUpperBound().getDynamicUpperBoundType();
}
if (appView.appInfo().hasLiveness()) {
if (outType.isClassType()
@@ -199,8 +185,7 @@
return false;
}
Assume assumeInstruction = other.asAssume();
- return Objects.equals(dynamicTypeAssumption, assumeInstruction.dynamicTypeAssumption)
- && Objects.equals(nonNullAssumption, assumeInstruction.nonNullAssumption);
+ return dynamicType.equals(assumeInstruction.dynamicType);
}
@Override
@@ -249,22 +234,9 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- assert super.verifyTypes(appView, verifyTypesHelper);
-
- TypeElement inType = src().getType();
- assert inType.isReferenceType() : inType;
-
- TypeElement outType = getOutType();
- if (hasNonNullAssumption()) {
- assert inType.isNullType() || outType.equals(inType.asReferenceType().asMeetWithNotNull())
- : "At " + this + System.lineSeparator() + outType + " != " + inType;
- } else {
- assert hasDynamicTypeAssumption();
- assert !src().isConstNumber();
- assert outType.equals(inType)
- : "At " + this + System.lineSeparator() + outType + " != " + inType;
- }
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
+ verifyInstruction(appView, context);
return true;
}
@@ -279,8 +251,8 @@
if (hasNonNullAssumption()) {
builder.append("; not null");
}
- if (hasDynamicTypeAssumption()) {
- DynamicTypeWithUpperBound dynamicType = dynamicTypeAssumption.getDynamicType();
+ if (hasDynamicTypeIgnoringNullability()) {
+ DynamicTypeWithUpperBound dynamicType = getDynamicType().asDynamicTypeWithUpperBound();
if (hasOutValue()) {
if (!dynamicType.getDynamicUpperBoundType().equalUpToNullability(outValue.getType())) {
builder.append("; upper bound: ").append(dynamicType.getDynamicUpperBoundType());
@@ -293,59 +265,58 @@
return builder.toString();
}
- public static class DynamicTypeAssumption {
-
- private final DynamicTypeWithUpperBound dynamicType;
-
- public DynamicTypeAssumption(DynamicTypeWithUpperBound dynamicType) {
- assert dynamicType != null;
- this.dynamicType = dynamicType;
- }
-
- public DynamicTypeWithUpperBound getDynamicType() {
- return dynamicType;
- }
-
- public boolean verifyCorrectnessOfValues(Value dest, Value src, AppView<?> appView) {
+ public boolean verifyInstruction(AppView<?> appView, ProgramMethod context) {
+ assert !src().isConstant()
+ : "Unexpected Assume value "
+ + outValue()
+ + " for constant value "
+ + src()
+ + " defined by "
+ + src().getDefinition()
+ + " (context: "
+ + context.toSourceString()
+ + ", type: "
+ + src().getType()
+ + ")";
+ assert !src().getType().isDefinitelyNull();
+ assert !src().getType().isNullType();
+ assert hasOutValue();
+ if (hasDynamicTypeIgnoringNullability()) {
assert !dynamicType.isBottom();
+ assert !dynamicType.isNotNullType();
+ assert !dynamicType.isNullType();
assert !dynamicType.isUnknown();
- assert dynamicType
+ DynamicTypeWithUpperBound dynamicTypeWithUpperBound =
+ dynamicType.asDynamicTypeWithUpperBound();
+ assert dynamicTypeWithUpperBound
.getDynamicUpperBoundType()
- .lessThanOrEqualUpToNullability(src.getType(), appView);
- return true;
+ .lessThanOrEqualUpToNullability(src().getType(), appView);
+ } else {
+ assert dynamicType.isNotNullType();
+ assert hasNonNullAssumption();
+ assert !src().getType().isDefinitelyNotNull()
+ : "Unexpected AssumeNotNull instruction for non-null value "
+ + src()
+ + " defined by "
+ + (src().isPhi() ? "phi" : src().getDefinition())
+ + " (context: "
+ + context.toSourceString()
+ + ", type: "
+ + src().getType()
+ + ")";
}
-
- @Override
- public boolean equals(Object other) {
- if (other == null) {
- return false;
- }
- if (getClass() != other.getClass()) {
- return false;
- }
- DynamicTypeAssumption assumption = (DynamicTypeAssumption) other;
- return dynamicType.equals(assumption.dynamicType);
- }
-
- @Override
- public int hashCode() {
- return dynamicType.hashCode();
- }
- }
-
- public static class NonNullAssumption {
-
- private static final NonNullAssumption instance = new NonNullAssumption();
-
- private NonNullAssumption() {}
-
- public static NonNullAssumption get() {
- return instance;
- }
-
- public boolean verifyCorrectnessOfValues(Value dest, Value src, AppView<?> appView) {
- assert !src.isNeverNull();
- return true;
- }
+ assert !hasNonNullAssumption() || outValue().getType().isDefinitelyNotNull()
+ : "Unexpected nullability for value "
+ + outValue()
+ + " defined by "
+ + this
+ + ": "
+ + outValue().getType().nullability()
+ + ", but expected: "
+ + Nullability.definitelyNotNull()
+ + " (context: "
+ + context.toSourceString()
+ + ")";
+ return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index bef0a26..588a39a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -103,9 +103,10 @@
return true;
}
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
assert instructions.stream()
- .allMatch(instruction -> instruction.verifyTypes(appView, verifyTypesHelper));
+ .allMatch(instruction -> instruction.verifyTypes(appView, context, verifyTypesHelper));
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index bc3297f..510ed2d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -242,7 +242,7 @@
}
if (current.hasUsedOutValue()) {
assert newInstruction.outValue() != null;
- if (affectedValues != null && newInstruction.getOutType() != current.getOutType()) {
+ if (affectedValues != null && !newInstruction.getOutType().equals(current.getOutType())) {
current.outValue().addAffectedValuesTo(affectedValues);
}
current.outValue().replaceUsers(newInstruction.outValue());
@@ -881,7 +881,7 @@
assert entryBlock.getInstructions().stream().noneMatch(Instruction::isArgument);
// Actual arguments are flown to the inlinee.
- new TypeAnalysis(appView).narrowing(argumentUsers);
+ new TypeAnalysis(appView, code).narrowing(argumentUsers);
// The inline entry is the first block now the argument instructions are gone.
BasicBlock inlineEntry = inlinee.entryBlock();
@@ -900,8 +900,9 @@
Return returnInstruction = inlineeIterator.peekNext().asReturn();
invoke.outValue().replaceUsers(returnInstruction.returnValue());
// The return type is flown to the original context.
- new TypeAnalysis(appView)
- .narrowing(
+ new TypeAnalysis(appView, code)
+ .setKeepRedundantBlocksAfterAssumeRemoval(true)
+ .narrowingWithAssumeRemoval(
Iterables.concat(
ImmutableList.of(returnInstruction.returnValue()), affectedValues));
}
@@ -962,7 +963,9 @@
DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
Set<Value> affectedValues = Sets.newIdentityHashSet();
blocksToRemove.addAll(invokePredecessor.unlink(invokeBlock, dominatorTree, affectedValues));
- new TypeAnalysis(appView).narrowing(affectedValues);
+ new TypeAnalysis(appView, code)
+ .setKeepRedundantBlocksAfterAssumeRemoval(true)
+ .narrowingWithAssumeRemoval(affectedValues);
}
// Position the iterator after the invoke block.
@@ -1013,7 +1016,7 @@
null,
RegisterReadType.NORMAL);
phi.addOperands(operands);
- new TypeAnalysis(appView).widening(ImmutableSet.of(phi));
+ new TypeAnalysis(appView, code).widening(ImmutableSet.of(phi));
value = phi;
}
newReturn = new Return(value);
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index ed02fe2..a202a35 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -216,8 +216,9 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- assert super.verifyTypes(appView, verifyTypesHelper);
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
+ assert super.verifyTypes(appView, context, verifyTypesHelper);
TypeElement inType = object().getType();
@@ -226,31 +227,24 @@
TypeElement outType = getOutType();
TypeElement castType = TypeElement.fromDexType(getType(), inType.nullability(), appView);
- if (inType.lessThanOrEqual(castType, appView)) {
- // Cast can be removed. Check that it is sound to replace all users of the out-value by the
- // in-value.
- assert inType.lessThanOrEqual(outType, appView);
+ // We don't have enough information to remove the cast. Check that the out-value does not
+ // have a more precise type than the cast-type.
+ assert outType.equalUpToNullability(castType);
- // TODO(b/72693244): Consider checking equivalence. This requires that the types are always
- // as precise as possible, though, meaning that almost all changes to the IR must be followed
- // by a fix-point analysis.
- // assert outType.equals(inType);
- } else {
- // We don't have enough information to remove the cast. Check that the out-value does not
- // have a more precise type than the cast-type.
- assert outType.equalUpToNullability(castType);
+ // Check soundness of null information.
+ assert inType.nullability() == outType.nullability() || inType.isNullType()
+ : "Expected nullability of value "
+ + outValue()
+ + " defined by "
+ + this
+ + " to be "
+ + inType.nullability()
+ + ", but was "
+ + outType.nullability()
+ + "(context: "
+ + context.toSourceString()
+ + ")";
- // Check soundness of null information.
- assert inType.nullability() == outType.nullability();
-
- // Since we cannot remove the cast the in-value must be different from null.
- assert !inType.isNullType();
-
- // TODO(b/72693244): Consider checking equivalence. This requires that the types are always
- // as precise as possible, though, meaning that almost all changes to the IR must be followed
- // by a fix-point analysis.
- // assert outType.equals(castType);
- }
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 00b983b..ddb8bf1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -332,8 +332,9 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- assert super.verifyTypes(appView, verifyTypesHelper);
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
+ assert super.verifyTypes(appView, context, verifyTypesHelper);
assert !isZero() || getOutType().isPrimitiveType() || getOutType().isNullType();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 803223f..9c1a421 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -178,8 +178,9 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- assert super.verifyTypes(appView, verifyTypesHelper);
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
+ assert super.verifyTypes(appView, context, verifyTypesHelper);
TypeElement expectedType = TypeElement.stringClassType(appView, definitelyNotNull());
assert getOutType().equals(expectedType);
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index 9bb5f9a..7304858 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.cf.code.CfStore;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.lightir.LirBuilder;
@@ -83,8 +84,9 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- super.verifyTypes(appView, verifyTypesHelper);
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
+ super.verifyTypes(appView, context, verifyTypesHelper);
assert verifyTypesHelper.isAssignable(src().getType(), getOutType());
return true;
}
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 a0dc9f6..a4db6a0 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
@@ -20,11 +20,13 @@
import com.android.tools.r8.ir.analysis.framework.intraprocedural.IRControlFlowGraph;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.Nullability;
+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.Phi.RegisterReadType;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DequeUtils;
@@ -38,7 +40,6 @@
import com.android.tools.r8.utils.TriFunction;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
@@ -589,12 +590,14 @@
public boolean isConsistentSSA(AppView<?> appView) {
isConsistentSSABeforeTypesAreCorrect(appView);
assert verifyNoImpreciseOrBottomTypes();
+ assert verifyTypes(appView);
return true;
}
public boolean isConsistentSSAAllowingRedundantBlocks(AppView<?> appView) {
isConsistentSSABeforeTypesAreCorrectAllowingRedundantBlocks(appView);
assert verifyNoImpreciseOrBottomTypes();
+ assert verifyTypes(appView);
return true;
}
@@ -658,22 +661,11 @@
// We can only type check the program if we have subtyping information. Therefore, we do not
// require that the program type checks in D8.
VerifyTypesHelper verifyTypesHelper = VerifyTypesHelper.create(appView);
- if (appView.enableWholeProgramOptimizations()) {
- assert validAssumeInstructions(appView);
- assert new TypeChecker(appView.withLiveness(), verifyTypesHelper).check(this);
- }
- assert blocks.stream().allMatch(block -> block.verifyTypes(appView, verifyTypesHelper));
- return true;
- }
-
- private boolean validAssumeInstructions(AppView<?> appView) {
- for (BasicBlock block : blocks) {
- for (Instruction instruction : block.getInstructions()) {
- if (instruction.isAssume()) {
- assert instruction.asAssume().verifyInstructionIsNeeded(appView);
- }
- }
- }
+ assert !appView.enableWholeProgramOptimizations()
+ || new TypeChecker(appView.withLiveness(), verifyTypesHelper).check(this);
+ TypeAnalysis.verifyValuesUpToDate(appView, this);
+ assert blocks.stream()
+ .allMatch(block -> block.verifyTypes(appView, context(), verifyTypesHelper));
return true;
}
@@ -723,7 +715,7 @@
int predecessorCount = block.getPredecessors().size();
// Check that all phi uses are consistent.
for (Phi phi : block.getPhis()) {
- assert !phi.isTrivialPhi();
+ assert !phi.isTrivialPhi() : "Unexpected trivial phi in " + context().toSourceString();
assert phi.getOperands().size() == predecessorCount;
addValueAndCheckUniqueNumber(values, phi);
for (Value value : phi.getOperands()) {
@@ -1247,7 +1239,7 @@
}
public ConstClass createConstClass(AppView<?> appView, DexType type) {
- Value out = createValue(TypeElement.fromDexType(type, definitelyNotNull(), appView));
+ Value out = createValue(TypeElement.classClassType(appView, definitelyNotNull()));
return new ConstClass(out, type);
}
@@ -1507,20 +1499,20 @@
return unreachableBlocks;
}
- public Set<Value> removeUnreachableBlocks() {
- ImmutableSet.Builder<Value> affectedValueBuilder = ImmutableSet.builder();
+ public AffectedValues removeUnreachableBlocks() {
+ AffectedValues affectedValues = new AffectedValues();
int color = reserveMarkingColor();
markTransitiveSuccessors(entryBlock(), color);
ListIterator<BasicBlock> blockIterator = listIterator();
while (blockIterator.hasNext()) {
BasicBlock current = blockIterator.next();
if (!current.isMarked(color)) {
- affectedValueBuilder.addAll(current.cleanForRemoval());
+ affectedValues.addAll(current.cleanForRemoval());
blockIterator.remove();
}
}
returnMarkingColor(color);
- return affectedValueBuilder.build();
+ return affectedValues;
}
// Note: It is the responsibility of the caller to return the marking color.
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index 0149b70..b0ecd2d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -125,15 +125,22 @@
@Override
public String toString() {
- return super.toString()
- + " "
- + type
- + (isZeroTest() ? "Z" : " ")
- + " block "
- + getTrueTarget().getNumberAsString()
- + " (fallthrough "
- + fallthroughBlock().getNumberAsString()
- + ")";
+ StringBuilder builder =
+ new StringBuilder(super.toString())
+ .append(' ')
+ .append(type)
+ .append(isZeroTest() ? 'Z' : ' ');
+ // If this instruction is in a block that has been marked for removal, but not yet removed from
+ // the IR, make sure we can still print the code.
+ if (getBlock().exit() == this && getBlock().getSuccessors().size() >= 2) {
+ builder
+ .append(" block ")
+ .append(getTrueTarget().getNumberAsString())
+ .append(" (fallthrough ")
+ .append(fallthroughBlock().getNumberAsString())
+ .append(')');
+ }
+ return builder.toString();
}
@Override
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 9321203..2da97aa 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
@@ -95,6 +95,14 @@
this.position = position;
}
+ public void setPosition(Position position, InternalOptions options) {
+ if (instructionTypeCanThrow() || options.debug) {
+ setPosition(position);
+ } else {
+ setPosition(Position.none());
+ }
+ }
+
public void forceOverwritePosition(Position position) {
assert position != null;
assert this.position != null;
@@ -228,6 +236,10 @@
public abstract void buildLir(LirBuilder<Value, ?> builder);
public void replaceValue(Value oldValue, Value newValue) {
+ replaceValue(oldValue, newValue, null);
+ }
+
+ public void replaceValue(Value oldValue, Value newValue, Set<Value> affectedValues) {
for (int i = 0; i < inValues.size(); i++) {
if (oldValue == inValues.get(i)) {
inValues.set(i, newValue);
@@ -235,6 +247,9 @@
}
}
oldValue.removeUser(this);
+ if (affectedValues != null && hasOutValue()) {
+ affectedValues.add(outValue());
+ }
}
public void replaceValue(int index, Value newValue) {
@@ -757,7 +772,7 @@
}
public final boolean isAssumeWithDynamicTypeAssumption() {
- return isAssume() && asAssume().hasDynamicTypeAssumption();
+ return isAssume() && asAssume().hasDynamicTypeIgnoringNullability();
}
public final boolean isAssumeWithNonNullAssumption() {
@@ -1459,21 +1474,8 @@
"Implement type lattice evaluation for: " + getInstructionName());
}
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- if (outValue != null) {
- TypeElement outTypeElement = outValue.getType();
- if (outTypeElement.isArrayType()) {
- DexType outBaseType =
- outTypeElement
- .asArrayType()
- .toDexType(appView.dexItemFactory())
- .toBaseType(appView.dexItemFactory());
- assert appView.graphLens().lookupType(outBaseType) == outBaseType;
- } else if (outTypeElement.isClassType()) {
- DexType outType = outTypeElement.asClassType().getClassType();
- assert appView.graphLens().lookupType(outType) == outType;
- }
- }
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
return true;
}
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 a0c67d5..9d49a0b 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
@@ -146,8 +146,9 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- assert super.verifyTypes(appView, verifyTypesHelper);
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
+ assert super.verifyTypes(appView, context, verifyTypesHelper);
Value receiver = getReceiver();
TypeElement receiverType = receiver.getType();
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index d4951ac..b397c80 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -54,6 +54,11 @@
}
@Override
+ public void setInsertionPosition(Position position) {
+ currentBlockIterator.setInsertionPosition(position);
+ }
+
+ @Override
public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
currentBlockIterator.replaceCurrentInstruction(newInstruction, affectedValues);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
index 5dc04da..a1c5475 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Move.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -131,8 +131,9 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
- super.verifyTypes(appView, verifyTypesHelper);
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
+ super.verifyTypes(appView, context, verifyTypesHelper);
// DebugLocalWrite defines it's own verification of types but should be allowed to call super.
if (!this.isDebugLocalWrite()) {
assert src().getType().equals(getOutType());
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 0fa2583..56de63b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -227,7 +227,8 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
TypeElement type = getOutType();
assert type.isClassType();
assert type.asClassType().getClassType() == clazz || appView.options().testing.allowTypeErrors;
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
index d47c812..d42756d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -155,7 +155,8 @@
}
@Override
- public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
+ public boolean verifyTypes(
+ AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
TypeElement type = getOutType();
assert type.isClassType();
assert type.asClassType().getClassType() == clazz || appView.options().testing.allowTypeErrors;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 049c514..944d68e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -208,19 +208,33 @@
}
public void replaceOperandAt(int predIndex, Value newValue) {
+ replaceOperandAt(predIndex, newValue, null);
+ }
+
+ public void replaceOperandAt(int predIndex, Value newValue, Set<Value> affectedValues) {
Value current = operands.get(predIndex);
operands.set(predIndex, newValue);
newValue.addPhiUser(this);
current.removePhiUser(this);
+ if (affectedValues != null) {
+ affectedValues.add(this);
+ }
}
public void replaceOperand(Value current, Value newValue) {
+ replaceOperand(current, newValue, null);
+ }
+
+ public void replaceOperand(Value current, Value newValue, Set<Value> affectedValues) {
for (int i = 0; i < operands.size(); i++) {
if (operands.get(i) == current) {
operands.set(i, newValue);
newValue.addPhiUser(this);
}
}
+ if (affectedValues != null) {
+ affectedValues.add(this);
+ }
}
public boolean isTrivialPhi() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java b/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
index 5d079559..1b4372e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
@@ -97,7 +97,11 @@
@Override
public void buildLir(LirBuilder<Value, ?> builder) {
- throw new Unreachable();
+ BasicBlock[] targetBlocks = new BasicBlock[keys.length];
+ for (int i = 0; i < keys.length; i++) {
+ targetBlocks[i] = targetBlock(i);
+ }
+ builder.addStringSwitch(value(), keys, targetBlocks, fallthroughBlock());
}
@Override
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 fcec4f3..3257f90 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
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.regalloc.LiveIntervals;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
@@ -582,10 +583,10 @@
}
// Returns the set of Value that are affected if the current value's type lattice is updated.
- public Set<Value> affectedValues() {
- ImmutableSet.Builder<Value> affectedValues = ImmutableSet.builder();
+ public AffectedValues affectedValues() {
+ AffectedValues affectedValues = new AffectedValues();
forEachAffectedValue(affectedValues::add);
- return affectedValues.build();
+ return affectedValues;
}
public void addAffectedValuesTo(Set<Value> affectedValues) {
@@ -610,16 +611,10 @@
return;
}
for (Instruction user : uniqueUsers()) {
- user.replaceValue(this, newValue);
- if (affectedValues != null && user.hasOutValue()) {
- affectedValues.add(user.outValue);
- }
+ user.replaceValue(this, newValue, affectedValues);
}
for (Phi user : uniquePhiUsers()) {
- user.replaceOperand(this, newValue);
- if (affectedValues != null) {
- affectedValues.add(user);
- }
+ user.replaceOperand(this, newValue, affectedValues);
}
if (debugData != null) {
for (Instruction user : debugData.users) {
@@ -656,6 +651,14 @@
Value newValue,
Set<Instruction> selectedInstructions,
Map<Phi, IntList> selectedPhisWithPredecessorIndexes) {
+ replaceSelectiveUsers(newValue, selectedInstructions, selectedPhisWithPredecessorIndexes, null);
+ }
+
+ public void replaceSelectiveUsers(
+ Value newValue,
+ Set<Instruction> selectedInstructions,
+ Map<Phi, IntList> selectedPhisWithPredecessorIndexes,
+ Set<Value> affectedValues) {
if (this == newValue) {
return;
}
@@ -665,7 +668,7 @@
for (Instruction user : uniqueUsers()) {
if (selectedInstructions.contains(user)) {
fullyRemoveUser(user);
- user.replaceValue(this, newValue);
+ user.replaceValue(this, newValue, affectedValues);
}
}
Set<Phi> selectedPhis = selectedPhisWithPredecessorIndexes.keySet();
@@ -679,7 +682,7 @@
}
for (int position : positionsToUpdate) {
assert user.getOperand(position) == this;
- user.replaceOperandAt(position, newValue);
+ user.replaceOperandAt(position, newValue, affectedValues);
}
}
}
@@ -1017,7 +1020,7 @@
setType(newType);
}
- public void narrowing(AppView<?> appView, TypeElement newType) {
+ public void narrowing(AppView<?> appView, ProgramMethod context, TypeElement newType) {
// During NARROWING (e.g., after inlining), type update is monotonically downwards,
// i.e., towards something narrower, with more specific type info.
assert skipWideningOrNarrowingCheck(appView) || !this.type.strictlyLessThan(newType, appView)
@@ -1026,7 +1029,10 @@
+ " < "
+ newType
+ " at "
- + (isPhi() ? asPhi().printPhi() : definition.toString());
+ + (isPhi() ? asPhi().printPhi() : definition.toString())
+ + " (context: "
+ + context
+ + ")";
setType(newType);
}
@@ -1050,54 +1056,39 @@
public TypeElement getDynamicUpperBoundType(
AppView<? extends AppInfoWithClassHierarchy> appView) {
- Value root = getAliasedValue();
- if (root.isPhi()) {
- assert getSpecificAliasedValue(
- value ->
- value.isDefinedByInstructionSatisfying(
- Instruction::isAssumeWithDynamicTypeAssumption))
- == null;
- TypeElement result = root.getDynamicUpperBoundType(appView);
- if (getType().isReferenceType() && getType().isDefinitelyNotNull()) {
- return result.asReferenceType().asMeetWithNotNull();
- }
- return result;
- }
-
// 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));
- TypeElement lattice;
+ Value root = getAliasedValue();
+ TypeElement upperBoundType;
if (aliasedValue != null) {
// If there is an alias of the receiver, which is defined by an Assume instruction that
// carries a dynamic type, then use the dynamic type as the refined receiver type.
- lattice =
+ upperBoundType =
aliasedValue
- .definition
+ .getDefinition()
.asAssume()
.getDynamicTypeAssumption()
- .getDynamicType()
.getDynamicUpperBoundType();
-
- // For precision, verify that the dynamic type is at least as precise as the static type.
- assert lattice.lessThanOrEqualUpToNullability(type, appView) : type + " < " + lattice;
+ } else if (root.isPhi()) {
+ upperBoundType = root.getDynamicUpperBoundType(appView);
} else {
// Otherwise, simply use the static type.
- lattice = type;
+ upperBoundType = type;
}
// Account for nullability, which could be flown from non-null assumption in between dynamic
// type assumption or simply from array/object creation.
- if (type.isDefinitelyNotNull() && lattice.isNullable()) {
+ if (type.isDefinitelyNotNull() && upperBoundType.isNullable()) {
// Having non-null assumption means it is a reference type.
- assert lattice.isReferenceType();
+ assert upperBoundType.isReferenceType();
// Then, we can return the non-null variant of dynamic type if both assumptions are aliased.
- return lattice.asReferenceType().asMeetWithNotNull();
+ return upperBoundType.asReferenceType().asMeetWithNotNull();
}
- return lattice;
+ return upperBoundType;
}
public ClassTypeElement getDynamicLowerBoundType(AppView<AppInfoWithLiveness> appView) {
@@ -1146,7 +1137,6 @@
.getDefinition()
.asAssume()
.getDynamicTypeAssumption()
- .getDynamicType()
.getDynamicLowerBoundType();
if (aliasedValueType != null) {
aliasedValueType = aliasedValueType.meetNullability(getType().nullability());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index e1b5d36..b5b9532 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -170,7 +170,7 @@
timing.end();
}
code.removeRedundantBlocks();
- assert code.isConsistentSSA(appView);
+ assert code.isConsistentGraph(appView, false);
// Insert reads for uninitialized read blocks to ensure correct stack maps.
timing.begin("Insert uninitialized local reads");
Set<UninitializedThisLocalRead> uninitializedThisLocalReads =
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 3afb79b..d45ffdf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -738,11 +738,11 @@
} else if (!canUseStackMapTypes() || hasIncorrectStackMapTypes) {
// TODO(b/169137397): We may have ended up generating StackMapPhi's before concluding
// having incorrect stack map types. Figure out a way to clean that up.
- new TypeAnalysis(appView).widening(ir);
+ new TypeAnalysis(appView, ir).widening();
} else {
assert canUseStackMapTypes() && !hasIncorrectStackMapTypes;
assert allPhisAreStackMapPhis(ir);
- new TypeAnalysis(appView).narrowing(ir);
+ new TypeAnalysis(appView, ir).narrowing();
}
if (conversionOptions.isStringSwitchConversionEnabled()) {
@@ -750,7 +750,7 @@
}
ir.removeRedundantBlocks();
- assert ir.isConsistentSSA(appView);
+ assert ir.isConsistentSSABeforeTypesAreCorrect(appView);
// Clear the code so we don't build multiple times.
source.clear();
@@ -2411,10 +2411,10 @@
}
private boolean verifyOutValueType(Instruction ir) {
- assert ir.outValue() == null || ir.isArrayGet() || ir.evaluate(appView) == ir.getOutType();
+ assert ir.outValue() == null || ir.isArrayGet() || ir.evaluate(appView).equals(ir.getOutType());
assert ir.outValue() == null
|| !ir.isArrayGet()
- || ir.evaluate(appView) == ir.getOutType()
+ || ir.evaluate(appView).equals(ir.getOutType())
|| (ir.getOutType().isBottom() && ir.evaluate(appView).isReferenceType());
return true;
}
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 cd7af3b..a7cd093 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
@@ -386,7 +386,6 @@
*/
public void replaceCodeForTesting(IRCode code) {
ProgramMethod method = code.context();
- DexEncodedMethod definition = method.getDefinition();
assert code.isConsistentSSA(appView);
Timing timing = Timing.empty();
deadCodeRemover.run(code, timing);
@@ -548,24 +547,6 @@
options.testing.irModifier.accept(code, appView);
}
- if (options.canHaveArtStringNewInitBug()) {
- timing.begin("Check for new-init issue");
- TrivialPhiSimplifier.ensureDirectStringNewToInit(code, appView.dexItemFactory());
- timing.end();
- }
-
- if (options.canHaveInvokeInterfaceToObjectMethodBug()) {
- timing.begin("JDK-8272564 fix rewrite");
- CodeRewriter.rewriteJdk8272564Fix(code, context, appView);
- timing.end();
- }
-
- boolean isDebugMode = options.debug || context.getOrComputeReachabilitySensitive(appView);
-
- if (isDebugMode) {
- codeRewriter.simplifyDebugLocals(code);
- }
-
if (lensCodeRewriter != null) {
timing.begin("Lens rewrite");
lensCodeRewriter.rewrite(code, context, methodProcessor);
@@ -573,6 +554,7 @@
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
}
+ boolean isDebugMode = options.debug || context.getOrComputeReachabilitySensitive(appView);
assert !method.isProcessed() || !isDebugMode
: "Method already processed: "
+ context.toSourceString()
@@ -600,7 +582,6 @@
// assert fails, then the types that we have inferred are unsound, or the method does not type
// check. In the latter case, the type checker should be extended to detect the issue such that
// we will return with a throw-null method above.
- assert code.verifyTypes(appView);
assert code.isConsistentSSA(appView);
if (shouldPassThrough(context)) {
@@ -620,6 +601,22 @@
return timing;
}
+ if (options.canHaveArtStringNewInitBug()) {
+ timing.begin("Check for new-init issue");
+ TrivialPhiSimplifier.ensureDirectStringNewToInit(appView, code);
+ timing.end();
+ }
+
+ if (options.canHaveInvokeInterfaceToObjectMethodBug()) {
+ timing.begin("JDK-8272564 fix rewrite");
+ CodeRewriter.rewriteJdk8272564Fix(code, context, appView);
+ timing.end();
+ }
+
+ if (isDebugMode) {
+ codeRewriter.simplifyDebugLocals(code);
+ }
+
assertionsRewriter.run(method, code, deadCodeRemover, timing);
CheckNotNullConverter.runIfNecessary(appView, code);
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
@@ -636,7 +633,6 @@
timing.begin("Decouple identifier-name strings");
identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(code);
timing.end();
- assert code.isConsistentSSA(appView);
previous = printMethod(code, "IR after identifier-name strings (SSA)", previous);
}
@@ -868,7 +864,6 @@
// Insert code to log arguments if requested.
if (options.methodMatchesLogArgumentsFilter(method) && !method.isProcessed()) {
codeRewriter.logArgumentTypes(method, code);
- assert code.isConsistentSSA(appView);
}
previous = printMethod(code, "IR after argument type logging (SSA)", previous);
@@ -1040,29 +1035,17 @@
BytecodeMetadataProvider bytecodeMetadataProvider,
Timing timing) {
if (options.testing.roundtripThroughLir) {
- code = roundtripThroughLir(code, feedback, bytecodeMetadataProvider, timing);
+ code = roundtripThroughLir(code, bytecodeMetadataProvider, timing);
}
- MethodConversionOptions conversionOptions = code.getConversionOptions();
- if (conversionOptions.isGeneratingLir()) {
- timing.begin("IR->LIR");
- finalizeToLir(code, feedback, bytecodeMetadataProvider, timing);
- timing.end();
- } else if (conversionOptions.isGeneratingClassFiles()) {
- timing.begin("IR->CF");
- finalizeToCf(code, feedback, bytecodeMetadataProvider, timing);
- timing.end();
- } else {
- assert conversionOptions.isGeneratingDex();
- timing.begin("IR->DEX");
- finalizeToDex(code, feedback, bytecodeMetadataProvider, timing);
- timing.end();
- }
+ ProgramMethod method = code.context();
+ IRFinalizer<?> finalizer = code.getConversionOptions().getFinalizer(deadCodeRemover, appView);
+ method.setCode(finalizer.finalizeCode(code, bytecodeMetadataProvider, timing), appView);
+ markProcessed(code, feedback);
printMethod(code.context(), "After finalization");
}
private IRCode roundtripThroughLir(
IRCode code,
- OptimizationFeedback feedback,
BytecodeMetadataProvider bytecodeMetadataProvider,
Timing timing) {
IRCode round1 =
@@ -1111,50 +1094,6 @@
return irCode;
}
- private void finalizeToLir(
- IRCode code,
- OptimizationFeedback feedback,
- BytecodeMetadataProvider bytecodeMetadataProvider,
- Timing timing) {
- assert deadCodeRemover.verifyNoDeadCode(code);
- LirCode<Integer> lirCode =
- IR2LirConverter.translate(
- code,
- bytecodeMetadataProvider,
- LirStrategy.getDefaultStrategy().getEncodingStrategy(),
- appView.options());
- ProgramMethod method = code.context();
- method.setCode(lirCode, appView);
- markProcessed(code, feedback);
- }
-
- private void finalizeToCf(
- IRCode code,
- OptimizationFeedback feedback,
- BytecodeMetadataProvider bytecodeMetadataProvider,
- Timing timing) {
- ProgramMethod method = code.context();
- method.setCode(
- new IRToCfFinalizer(appView, deadCodeRemover)
- .finalizeCode(code, bytecodeMetadataProvider, timing),
- appView);
- markProcessed(code, feedback);
- }
-
- private void finalizeToDex(
- IRCode code,
- OptimizationFeedback feedback,
- BytecodeMetadataProvider bytecodeMetadataProvider,
- Timing timing) {
- ProgramMethod method = code.context();
- DexEncodedMethod definition = method.getDefinition();
- method.setCode(
- new IRToDexFinalizer(appView, deadCodeRemover)
- .finalizeCode(code, bytecodeMetadataProvider, timing),
- appView);
- markProcessed(code, feedback);
- }
-
public void markProcessed(IRCode code, OptimizationFeedback feedback) {
// After all the optimizations have take place, we compute whether method should be inlined.
ProgramMethod method = code.context();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRToLirFinalizer.java b/src/main/java/com/android/tools/r8/ir/conversion/IRToLirFinalizer.java
new file mode 100644
index 0000000..d981b26
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRToLirFinalizer.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover;
+import com.android.tools.r8.lightir.IR2LirConverter;
+import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.lightir.LirStrategy;
+import com.android.tools.r8.utils.Timing;
+
+public class IRToLirFinalizer extends IRFinalizer<LirCode<Integer>> {
+
+ public IRToLirFinalizer(AppView<?> appView, DeadCodeRemover deadCodeRemover) {
+ super(appView, deadCodeRemover);
+ }
+
+ @Override
+ public LirCode<Integer> finalizeCode(
+ IRCode code, BytecodeMetadataProvider bytecodeMetadataProvider, Timing timing) {
+ assert deadCodeRemover.verifyNoDeadCode(code);
+ timing.begin("Finalize LIR code");
+ LirCode<Integer> lirCode =
+ IR2LirConverter.translate(
+ code,
+ bytecodeMetadataProvider,
+ LirStrategy.getDefaultStrategy().getEncodingStrategy(),
+ appView.options());
+ timing.end();
+ return lirCode;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 565fa11..82f55e3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -61,11 +61,13 @@
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Argument;
+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.CatchHandlers;
@@ -105,6 +107,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.passes.TrivialPhiSimplifier;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.argumentpropagation.lenscoderewriter.NullCheckInserter;
@@ -218,6 +221,7 @@
// Rewriting types that affects phi can cause us to compute TOP for cyclic phi's. To solve this
// we track all phi's that needs to be re-computed.
Set<Phi> affectedPhis = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
Set<UnusedArgument> unusedArguments = Sets.newIdentityHashSet();
rewriteArguments(
code, originalMethodReference, prototypeChanges, affectedPhis, unusedArguments);
@@ -236,7 +240,14 @@
}
}
rewritePartialDefault(
- code, method, graphLens, codeLens, prototypeChanges, affectedPhis, unusedArguments);
+ code,
+ method,
+ graphLens,
+ codeLens,
+ prototypeChanges,
+ affectedPhis,
+ affectedValues,
+ unusedArguments);
}
private void rewritePartialDefault(
@@ -246,6 +257,7 @@
GraphLens codeLens,
RewrittenPrototypeDescription prototypeChangesForMethod,
Set<Phi> affectedPhis,
+ AffectedValues affectedValues,
Set<UnusedArgument> unusedArguments) {
BasicBlockIterator blocks = code.listIterator();
LazyBox<LensCodeRewriterUtils> helper =
@@ -460,9 +472,11 @@
}
invoke
.outValue()
- .replaceUsers(constantReturnMaterializingInstruction.outValue());
- if (invoke.getOutType()
- != constantReturnMaterializingInstruction.getOutType()) {
+ .replaceUsers(
+ constantReturnMaterializingInstruction.outValue(), affectedValues);
+ if (!invoke
+ .getOutType()
+ .equals(constantReturnMaterializingInstruction.getOutType())) {
affectedPhis.addAll(
constantReturnMaterializingInstruction.outValue().uniquePhiUsers());
}
@@ -703,21 +717,43 @@
case CONST_CLASS:
{
ConstClass constClass = current.asConstClass();
- new InstructionReplacer(code, current, iterator, affectedPhis)
- .replaceInstructionIfTypeChanged(
- constClass.getValue(),
- (t, v) ->
- t.isPrimitiveType() || t.isVoidType()
- ? StaticGet.builder()
- .setField(
- factory
- .getBoxedMembersForPrimitiveOrVoidType(t)
- .getTypeField())
- .setOutValue(v)
- .build()
- : new ConstClass(v, t),
- graphLens,
- codeLens);
+ Instruction replacement =
+ new InstructionReplacer(code, current, iterator, affectedPhis)
+ .replaceInstructionIfTypeChanged(
+ constClass.getValue(),
+ (t, v) ->
+ t.isPrimitiveType() || t.isVoidType()
+ ? StaticGet.builder()
+ .setField(
+ factory
+ .getBoxedMembersForPrimitiveOrVoidType(t)
+ .getTypeField())
+ .setOutValue(v)
+ .build()
+ : new ConstClass(v, t),
+ graphLens,
+ codeLens);
+ if (replacement != null && replacement.isStaticGet()) {
+ Value nonNullableValue = replacement.outValue();
+ Value nullableValue =
+ code.createValue(
+ nonNullableValue
+ .getType()
+ .asReferenceType()
+ .getOrCreateVariant(Nullability.maybeNull()),
+ nonNullableValue.getLocalInfo());
+ replacement.setOutValue(nullableValue);
+ Assume assume =
+ Assume.create(
+ DynamicType.definitelyNotNull(),
+ nonNullableValue,
+ nullableValue,
+ replacement,
+ appView,
+ method);
+ assume.setPosition(replacement.getPosition(), options);
+ iterator.add(assume);
+ }
}
break;
@@ -848,6 +884,7 @@
if (mayHaveUnreachableBlocks) {
code.removeUnreachableBlocks();
}
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
if (!affectedPhis.isEmpty()) {
new DestructivePhiTypeUpdater(appView, graphLens, codeLens)
.recomputeAndPropagateTypes(code, affectedPhis);
@@ -876,6 +913,7 @@
RewrittenPrototypeDescription prototypeChanges,
Set<Phi> affectedPhis,
Set<UnusedArgument> unusedArguments) {
+ AffectedValues affectedValues = new AffectedValues();
ArgumentInfoCollection argumentInfoCollection = prototypeChanges.getArgumentInfoCollection();
List<Instruction> argumentPostlude = new LinkedList<>();
int oldArgumentIndex = 0;
@@ -898,6 +936,7 @@
argument,
argumentInfo.asRemovedArgumentInfo(),
affectedPhis,
+ affectedValues,
argumentPostlude,
unusedArguments);
numberOfRemovedArguments++;
@@ -957,6 +996,8 @@
instructionIterator.add(instruction);
}
}
+
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
}
private void rewriteRemovedArgument(
@@ -966,6 +1007,7 @@
Argument argument,
RemovedArgumentInfo removedArgumentInfo,
Set<Phi> affectedPhis,
+ AffectedValues affectedValues,
List<Instruction> argumentPostlude,
Set<UnusedArgument> unusedArguments) {
Instruction replacement;
@@ -986,7 +1028,7 @@
replacement.setPosition(Position.none());
unusedArguments.add(replacement.asUnusedArgument());
}
- argument.outValue().replaceUsers(replacement.outValue());
+ argument.outValue().replaceUsers(replacement.outValue(), affectedValues);
affectedPhis.addAll(replacement.outValue().uniquePhiUsers());
argumentPostlude.add(replacement);
instructionIterator.removeOrReplaceByDebugLocalRead();
@@ -1061,7 +1103,11 @@
CheckCast checkCast =
SafeCheckCast.builder()
.setObject(fieldPut.value())
- .setFreshOutValue(code, lookup.getWriteCastType().toTypeElement(appView))
+ .setFreshOutValue(
+ code,
+ lookup
+ .getWriteCastType()
+ .toTypeElement(appView, fieldPut.value().getType().nullability()))
.setCastType(lookup.getWriteCastType())
.setPosition(fieldPut.getPosition())
.build();
@@ -1322,7 +1368,7 @@
this.affectedPhis = affectedPhis;
}
- void replaceInstructionIfTypeChanged(
+ Instruction replaceInstructionIfTypeChanged(
DexType type,
BiFunction<DexType, Value, Instruction> constructor,
NonIdentityGraphLens graphLens,
@@ -1333,7 +1379,7 @@
Instruction newInstruction = constructor.apply(newType, newOutValue);
iterator.replaceCurrentInstruction(newInstruction);
if (newOutValue != null) {
- if (newOutValue.getType() != current.getOutType()) {
+ if (!newOutValue.getType().equals(current.getOutType())) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
} else {
assert current.hasInvariantOutType();
@@ -1344,7 +1390,9 @@
&& current.asInvokeVirtual().getInvokedMethod().holder.isArrayType());
}
}
+ return newInstruction;
}
+ return null;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
index 69c916e..ceed103 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodConversionOptions.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover;
import com.android.tools.r8.utils.InternalOptions;
public abstract class MethodConversionOptions {
@@ -24,7 +25,8 @@
}
assert appView.testing().isPostLirPhase();
Target target = appView.options().isGeneratingClassFiles() ? Target.CF : Target.DEX;
- return new MutableMethodConversionOptions(target, appView.options());
+ return new MutableMethodConversionOptions(target, appView.options())
+ .disableStringSwitchConversion();
}
public static MutableMethodConversionOptions forLirPhase(AppView<?> appView) {
@@ -44,6 +46,17 @@
return new ThrowingMethodConversionOptions();
}
+ public IRFinalizer<?> getFinalizer(DeadCodeRemover deadCodeRemover, AppView<?> appView) {
+ if (isGeneratingLir()) {
+ return new IRToLirFinalizer(appView, deadCodeRemover);
+ }
+ if (isGeneratingClassFiles()) {
+ return new IRToCfFinalizer(appView, deadCodeRemover);
+ }
+ assert isGeneratingDex();
+ return new IRToDexFinalizer(appView, deadCodeRemover);
+ }
+
private enum Target {
CF,
DEX,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 93ce2a2..b34e8b3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -255,24 +255,15 @@
Timing onThreadTiming = Timing.empty();
IRCode irCode = method.buildIR(appView, MethodConversionOptions.forPostLirPhase(appView));
// Processing is done and no further uses of the meta-data should arise.
- BytecodeMetadataProvider bytecodeMetadataProvider = BytecodeMetadataProvider.empty();
+ BytecodeMetadataProvider noMetadata = BytecodeMetadataProvider.empty();
// During processing optimization info may cause previously live code to become dead.
// E.g., we may now have knowledge that an invoke does not have side effects.
// Thus, we re-run the dead-code remover now as it is assumed complete by CF/DEX finalization.
deadCodeRemover.run(irCode, onThreadTiming);
MethodConversionOptions conversionOptions = irCode.getConversionOptions();
- if (conversionOptions.isGeneratingClassFiles()) {
- method.setCode(
- new IRToCfFinalizer(appView, deadCodeRemover)
- .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
- appView);
- } else {
- assert conversionOptions.isGeneratingDex();
- method.setCode(
- new IRToDexFinalizer(appView, deadCodeRemover)
- .finalizeCode(irCode, bytecodeMetadataProvider, onThreadTiming),
- appView);
- }
+ assert !conversionOptions.isGeneratingLir();
+ IRFinalizer<?> finalizer = conversionOptions.getFinalizer(deadCodeRemover, appView);
+ method.setCode(finalizer.finalizeCode(irCode, noMetadata, onThreadTiming), appView);
}
private void clearDexMethodCompilationState() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
index f08253d..489f0a6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/StringSwitchConverter.java
@@ -105,9 +105,10 @@
* }
* </pre>
*/
-class StringSwitchConverter {
+public class StringSwitchConverter {
- static void convertToStringSwitchInstructions(IRCode code, DexItemFactory dexItemFactory) {
+ public static boolean convertToStringSwitchInstructions(
+ IRCode code, DexItemFactory dexItemFactory) {
List<BasicBlock> rewritingCandidates = getRewritingCandidates(code, dexItemFactory);
if (rewritingCandidates != null) {
boolean changed = false;
@@ -120,7 +121,9 @@
code.removeAllDeadAndTrivialPhis();
code.removeUnreachableBlocks();
}
+ return changed;
}
+ return false;
}
private static List<BasicBlock> getRewritingCandidates(
@@ -349,9 +352,15 @@
intermediateIdValue = operand.asPhi();
}
}
- assert intermediateIdValue == null
- || intermediateIdValue.getOperands().stream().noneMatch(Value::isPhi);
- return intermediateIdValue != null ? intermediateIdValue : defaultValue;
+ if (intermediateIdValue == null) {
+ return defaultValue;
+ }
+ for (Value operand : intermediateIdValue.getOperands()) {
+ if (operand.isPhi()) {
+ return defaultValue;
+ }
+ }
+ return intermediateIdValue;
}
// Attempts to build a mapping from strings to their ids starting from the given block. The
@@ -732,7 +741,9 @@
}
}
- if (idValue == null || (toBeExtended != null && idValue != toBeExtended.idValue)) {
+ if (idValue == null
+ || !idValue.getType().isInt()
+ || (toBeExtended != null && idValue != toBeExtended.idValue)) {
// Not an extension of `toBeExtended`.
return setFallthroughBlock(toBeExtended, fallthroughBlock);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
index 2c6e476..8dfef13 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
@@ -97,7 +97,7 @@
List<Value> remainingImpreciseValues = resolveRoundOne(code);
// Round two will resolve any remaining single and wide types. These can depend on the types
// of array instructions, thus we need to complete the type fixed point prior to resolving.
- new TypeAnalysis(appView, true).widening(code);
+ new TypeAnalysis(appView, code, true).widening();
// Round two resolves any remaining imprecision and finally selects a final precise type for
// any unconstrained imprecise type.
resolveRoundTwo(code, impreciseInstructions, remainingImpreciseValues);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java
index aed22c2..7852989 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/BinopRewriter.java
@@ -274,7 +274,6 @@
code.removeAllDeadAndTrivialPhis();
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(hasChanged);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
index a239404..bc9c351 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
import com.android.tools.r8.ir.analysis.equivalence.BasicBlockBehavioralSubsumption;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.ConstantOrNonConstantNumberValue;
import com.android.tools.r8.ir.analysis.value.SingleConstClassValue;
@@ -40,13 +39,13 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.controlflow.SwitchCaseAnalyzer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOutputMode;
import com.android.tools.r8.utils.LongInterval;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -136,10 +135,8 @@
}
}
}
- Set<Value> affectedValues = code.removeUnreachableBlocks();
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ AffectedValues affectedValues = code.removeUnreachableBlocks();
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeRedundantBlocks();
assert code.isConsistentSSA(appView);
return create(!affectedValues.isEmpty(), simplified);
@@ -673,15 +670,12 @@
// critical edges at exit.
code.splitCriticalEdges();
- Set<Value> affectedValues =
- needToRemoveUnreachableBlocks ? code.removeUnreachableBlocks() : ImmutableSet.of();
- boolean anyAffectedValues = !affectedValues.isEmpty();
- if (anyAffectedValues) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ AffectedValues affectedValues =
+ needToRemoveUnreachableBlocks ? code.removeUnreachableBlocks() : AffectedValues.empty();
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeRedundantBlocks();
assert code.isConsistentSSA(appView);
- return create(anyAffectedValues, anySimplifications);
+ return create(affectedValues.hasNext(), anySimplifications);
}
public void rewriteSingleKeySwitchToIf(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
index 5f1705f..389dee9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
@@ -50,7 +50,14 @@
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
if (shouldRewriteCode(code)) {
- return rewriteCode(code, methodProcessor, methodProcessingContext);
+ assert isAcceptingSSA()
+ ? code.isConsistentSSA(appView)
+ : code.isConsistentGraph(appView, false);
+ CodeRewriterResult result = rewriteCode(code, methodProcessor, methodProcessingContext);
+ assert isProducingSSA()
+ ? code.isConsistentSSA(appView)
+ : code.isConsistentGraph(appView, false);
+ return result;
}
return noChange();
}
@@ -65,6 +72,14 @@
protected abstract String getTimingId();
+ protected boolean isAcceptingSSA() {
+ return true;
+ }
+
+ protected boolean isProducingSSA() {
+ return true;
+ }
+
protected CodeRewriterResult rewriteCode(IRCode code) {
throw new Unreachable("Should Override or use overload");
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CommonSubexpressionElimination.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CommonSubexpressionElimination.java
index 33ec433..03178bb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CommonSubexpressionElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CommonSubexpressionElimination.java
@@ -83,7 +83,6 @@
if (hasChanged) {
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(hasChanged);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
index bcfd88c..edecd38 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
@@ -150,8 +150,6 @@
}
}
}
-
- assert code.isConsistentSSA(appView);
}
// Check if a binop can be represented in the binop/lit8 or binop/lit16 form.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/KnownArrayLengthRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/KnownArrayLengthRewriter.java
index 05c3702..76138c9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/KnownArrayLengthRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/KnownArrayLengthRewriter.java
@@ -84,7 +84,6 @@
if (hasChanged) {
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(hasChanged);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/MoveResultRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/MoveResultRewriter.java
index 18c3757..0516fdc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/MoveResultRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/MoveResultRewriter.java
@@ -18,10 +18,10 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
-import com.android.tools.r8.ir.optimize.AssumeRemover;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.utils.BooleanBox;
import com.google.common.collect.Sets;
-import java.util.Collections;
import java.util.ListIterator;
import java.util.Set;
@@ -44,11 +44,12 @@
// Replace result uses for methods where something is known about what is returned.
@Override
protected CodeRewriterResult rewriteCode(IRCode code) {
- AssumeRemover assumeRemover = new AssumeRemover(appView, code);
boolean changed = false;
boolean mayHaveRemovedTrivialPhi = false;
Set<BasicBlock> blocksToBeRemoved = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blockIterator = code.listIterator();
+ TypeAnalysis typeAnalysis =
+ new TypeAnalysis(appView, code).setKeepRedundantBlocksAfterAssumeRemoval(true);
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
if (blocksToBeRemoved.contains(block)) {
@@ -90,38 +91,42 @@
continue;
}
- Set<Value> affectedValues =
+ AffectedValues affectedValues =
argument.getType().equals(outValue.getType())
- ? Collections.emptySet()
+ ? AffectedValues.empty()
: outValue.affectedValues();
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(outValue);
mayHaveRemovedTrivialPhi |= outValue.numberOfPhiUsers() > 0;
outValue.replaceUsers(argument);
- invoke.setOutValue(null);
+ invoke.clearOutValue();
changed = true;
if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
+ BooleanBox removedAssumeInstructionInCurrentBlock = new BooleanBox();
+ typeAnalysis
+ .setKeepRedundantBlocksAfterAssumeRemoval(true)
+ .narrowingWithAssumeRemoval(
+ affectedValues,
+ assume -> removedAssumeInstructionInCurrentBlock.or(assume.getBlock() == block));
+ if (removedAssumeInstructionInCurrentBlock.isTrue()) {
+ // Workaround ConcurrentModificationException.
+ iterator = block.listIterator(code);
+ }
}
}
}
- assumeRemover.removeMarkedInstructions(blocksToBeRemoved).finish();
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
if (!blocksToBeRemoved.isEmpty()) {
code.removeBlocks(blocksToBeRemoved);
code.removeAllDeadAndTrivialPhis(affectedValues);
assert code.getUnreachableBlocks().isEmpty();
- } else if (mayHaveRemovedTrivialPhi || assumeRemover.mayHaveIntroducedTrivialPhi()) {
+ } else if (mayHaveRemovedTrivialPhi) {
code.removeAllDeadAndTrivialPhis(affectedValues);
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ typeAnalysis.narrowingWithAssumeRemoval(affectedValues);
if (changed) {
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(changed);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java
index 2666be2..3bb77d8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/NaturalIntLoopRemover.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.code.Sub;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -43,15 +44,16 @@
@Override
protected CodeRewriterResult rewriteCode(IRCode code) {
boolean loopRemoved = false;
+ AffectedValues affectedValues = new AffectedValues();
for (BasicBlock comparisonBlockCandidate : code.blocks) {
if (isComparisonBlock(comparisonBlockCandidate)) {
- loopRemoved |= tryRemoveLoop(code, comparisonBlockCandidate.exit().asIf());
+ loopRemoved |= tryRemoveLoop(code, comparisonBlockCandidate.exit().asIf(), affectedValues);
}
}
if (loopRemoved) {
- code.removeAllDeadAndTrivialPhis();
+ code.removeAllDeadAndTrivialPhis(affectedValues);
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeRedundantBlocks();
- assert code.isConsistentSSA(appView);
}
return CodeRewriterResult.hasChanged(loopRemoved);
}
@@ -78,7 +80,7 @@
throw new Unreachable();
}
- private boolean tryRemoveLoop(IRCode code, If comparison) {
+ private boolean tryRemoveLoop(IRCode code, If comparison, AffectedValues affectedValues) {
Phi loopPhi = computeLoopPhi(comparison);
if (loopPhi == null) {
return false;
@@ -111,7 +113,7 @@
NaturalIntLoopWithKnowIterations loop = builder.build();
if (loop.has1Iteration()) {
- loop.remove1IterationLoop(code);
+ loop.remove1IterationLoop(code, affectedValues);
return true;
}
return false;
@@ -399,9 +401,9 @@
&& target(initCounter + counterIncrement) == loopExit;
}
- private void remove1IterationLoop(IRCode code) {
+ private void remove1IterationLoop(IRCode code, AffectedValues affectedValues) {
BasicBlock comparisonBlock = comparison.getBlock();
- updatePhis(comparisonBlock);
+ updatePhis(comparisonBlock, affectedValues);
patchControlFlow(code, comparisonBlock);
}
@@ -416,23 +418,23 @@
loopExit.replacePredecessor(comparisonBlock, backPredecessor);
}
- private void updatePhis(BasicBlock comparisonBlock) {
+ private void updatePhis(BasicBlock comparisonBlock, AffectedValues affectedValues) {
int backIndex = comparisonBlock.getPredecessors().indexOf(backPredecessor);
for (Phi phi : comparisonBlock.getPhis()) {
Value loopEntryValue = phi.getOperand(1 - backIndex);
Value loopExitValue = phi.getOperand(backIndex);
for (Instruction uniqueUser : phi.uniqueUsers()) {
if (loopBody.contains(uniqueUser.getBlock())) {
- uniqueUser.replaceValue(phi, loopEntryValue);
+ uniqueUser.replaceValue(phi, loopEntryValue, affectedValues);
} else {
- uniqueUser.replaceValue(phi, loopExitValue);
+ uniqueUser.replaceValue(phi, loopExitValue, affectedValues);
}
}
for (Phi phiUser : phi.uniquePhiUsers()) {
if (loopBody.contains(phiUser.getBlock())) {
- phiUser.replaceOperand(phi, loopEntryValue);
+ phiUser.replaceOperand(phi, loopEntryValue, affectedValues);
} else {
- phiUser.replaceOperand(phi, loopExitValue);
+ phiUser.replaceOperand(phi, loopExitValue, affectedValues);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ParentConstructorHoistingCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ParentConstructorHoistingCodeRewriter.java
index 6a7c040..704669d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/ParentConstructorHoistingCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ParentConstructorHoistingCodeRewriter.java
@@ -53,6 +53,7 @@
for (InvokeDirect invoke : getOrComputeSideEffectFreeConstructorCalls(code)) {
hoistSideEffectFreeConstructorCall(code, invoke);
}
+ code.removeRedundantBlocks();
return CodeRewriterResult.NONE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/RedundantConstNumberRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/RedundantConstNumberRemover.java
index 99caca6..4c542af 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/RedundantConstNumberRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/RedundantConstNumberRemover.java
@@ -162,7 +162,6 @@
if (changed) {
code.removeAllDeadAndTrivialPhis();
}
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(changed);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java
index 6ceb56d..1e1b91c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.Goto;
@@ -15,9 +14,9 @@
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -63,17 +62,13 @@
return CodeRewriterResult.NO_CHANGE;
}
retargetGotos(newTargets);
- Set<Value> affectedValues = Sets.newIdentityHashSet();
- affectedValues.addAll(code.removeUnreachableBlocks());
+ AffectedValues affectedValues = code.removeUnreachableBlocks();
code.removeAllDeadAndTrivialPhis(affectedValues);
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
if (ALLOW_PARTIAL_REWRITE) {
code.splitCriticalEdges();
}
code.removeRedundantBlocks();
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.HAS_CHANGED;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
index 920417a..fc64a45 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ThrowCatchOptimizer.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.ProgramMethod;
-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.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
@@ -33,6 +32,7 @@
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
@@ -200,15 +200,12 @@
}
}
if (shouldRemoveUnreachableBlocks) {
- Set<Value> affectedValues = code.removeUnreachableBlocks();
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ AffectedValues affectedValues = code.removeUnreachableBlocks();
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
}
if (hasChanged) {
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return hasChanged;
}
@@ -216,7 +213,7 @@
// it with a block throwing a null value (which should result in NPE). Note that this throw is not
// expected to be ever reached, but is intended to satisfy verifier.
private boolean optimizeAlwaysThrowingInstructions(IRCode code) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blockIterator = code.listIterator();
ProgramMethod context = code.context();
@@ -336,13 +333,10 @@
affectedValues.addAll(code.removeUnreachableBlocks());
}
assert code.getUnreachableBlocks().isEmpty();
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
if (hasChanged) {
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return hasChanged;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java
index a2654cf..028dcb1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialCheckCastAndInstanceOfRemover.java
@@ -13,13 +13,12 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
-import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.type.TypeUtils;
+import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstanceOf;
import com.android.tools.r8.ir.code.Instruction;
@@ -79,7 +78,8 @@
// CheckCast at line 4 unless we update v7 with the most precise information by narrowing the
// affected values of v5. We therefore have to run the type analysis after each CheckCast
// removal.
- TypeAnalysis typeAnalysis = new TypeAnalysis(appView);
+ TypeAnalysis typeAnalysis =
+ new TypeAnalysis(appView, code).setKeepRedundantBlocksAfterAssumeRemoval(true);
Set<Value> affectedValues = Sets.newIdentityHashSet();
InstructionListIterator it = code.instructionListIterator();
boolean needToRemoveTrivialPhis = false;
@@ -121,14 +121,11 @@
// v3 <- phi(v1, v1)
if (needToRemoveTrivialPhis) {
code.removeAllDeadAndTrivialPhis(affectedValues);
- if (!affectedValues.isEmpty()) {
- typeAnalysis.narrowing(affectedValues);
- }
}
+ typeAnalysis.narrowingWithAssumeRemoval(affectedValues);
if (hasChanged) {
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(hasChanged);
}
@@ -140,7 +137,11 @@
private enum InstanceOfResult {
UNKNOWN,
TRUE,
- FALSE
+ FALSE;
+
+ boolean isTrue() {
+ return this == TRUE;
+ }
}
// Returns true if the given check-cast instruction was removed.
@@ -346,27 +347,23 @@
value.isDefinedByInstructionSatisfying(
Instruction::isAssumeWithDynamicTypeAssumption));
if (aliasedValue != null) {
- DynamicTypeWithUpperBound dynamicType =
- aliasedValue.getDefinition().asAssume().getDynamicTypeAssumption().getDynamicType();
- Nullability nullability = dynamicType.getNullability();
- if (nullability.isDefinitelyNull()) {
+ Assume assumeInstruction = aliasedValue.getDefinition().asAssume();
+ DynamicType dynamicType = assumeInstruction.getDynamicType();
+ if (dynamicType.getNullability().isDefinitelyNull()) {
result = InstanceOfResult.FALSE;
- } else if (dynamicType.getDynamicUpperBoundType().lessThanOrEqual(instanceOfType, appView)
- && (!inType.isNullable() || !nullability.isNullable())) {
+ } else if (dynamicType.isDynamicTypeWithUpperBound()
+ && dynamicType
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType()
+ .lessThanOrEqual(instanceOfType, appView)
+ && (!inType.isNullable() || dynamicType.getNullability().isDefinitelyNotNull())) {
result = InstanceOfResult.TRUE;
}
}
}
}
if (result != InstanceOfResult.UNKNOWN) {
- ConstNumber newInstruction =
- new ConstNumber(
- new Value(
- code.valueNumberGenerator.next(),
- TypeElement.getInt(),
- instanceOf.outValue().getLocalInfo()),
- result == InstanceOfResult.TRUE ? 1 : 0);
- it.replaceCurrentInstruction(newInstruction);
+ it.replaceCurrentInstructionWithConstBoolean(code, result.isTrue());
return true;
}
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialGotosCollapser.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialGotosCollapser.java
index a08d82a..3055661 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialGotosCollapser.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialGotosCollapser.java
@@ -34,8 +34,17 @@
}
@Override
+ protected boolean isAcceptingSSA() {
+ return false;
+ }
+
+ @Override
+ protected boolean isProducingSSA() {
+ return false;
+ }
+
+ @Override
protected CodeRewriterResult rewriteCode(IRCode code) {
- assert code.isConsistentGraph(appView);
List<BasicBlock> blocksToRemove = new ArrayList<>();
// Rewrite all non-fallthrough targets to the end of trivial goto chains and remove
// first round of trivial goto blocks.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialPhiSimplifier.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialPhiSimplifier.java
index 6c4c66f..9bd4eb5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialPhiSimplifier.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/TrivialPhiSimplifier.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.algorithms.scc.SCC;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.IRCode;
@@ -29,7 +30,8 @@
assert !unusedArgument.outValue().hasPhiUsers();
}
- public static void ensureDirectStringNewToInit(IRCode code, DexItemFactory dexItemFactory) {
+ public static void ensureDirectStringNewToInit(AppView<?> appView, IRCode code) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
for (Instruction instruction : code.instructions()) {
if (instruction.isInvokeDirect()) {
InvokeDirect invoke = instruction.asInvokeDirect();
@@ -47,6 +49,7 @@
}
}
}
+ assert code.isConsistentSSA(appView);
}
private static NewInstance findNewInstance(Phi phi) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AffectedValues.java b/src/main/java/com/android/tools/r8/ir/optimize/AffectedValues.java
new file mode 100644
index 0000000..dc3f975
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AffectedValues.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Value;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+public class AffectedValues implements Set<Value> {
+
+ private static final AffectedValues EMPTY = new AffectedValues(ImmutableSet.of());
+
+ private final Set<Value> affectedValues;
+
+ public AffectedValues() {
+ this(Sets.newIdentityHashSet());
+ }
+
+ private AffectedValues(Set<Value> affectedValues) {
+ this.affectedValues = affectedValues;
+ }
+
+ public static AffectedValues empty() {
+ return EMPTY;
+ }
+
+ public void narrowingWithAssumeRemoval(AppView<?> appView, IRCode code) {
+ if (hasNext()) {
+ new TypeAnalysis(appView, code).narrowingWithAssumeRemoval(this);
+ }
+ }
+
+ public void widening(AppView<?> appView, IRCode code) {
+ if (hasNext()) {
+ new TypeAnalysis(appView, code).widening(this);
+ }
+ }
+
+ @Override
+ public boolean add(Value value) {
+ return affectedValues.add(value);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Value> c) {
+ return affectedValues.addAll(c);
+ }
+
+ @Override
+ public void clear() {
+ affectedValues.clear();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return affectedValues.contains(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return affectedValues.containsAll(c);
+ }
+
+ public boolean hasNext() {
+ return !isEmpty();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return affectedValues.isEmpty();
+ }
+
+ @Override
+ public Iterator<Value> iterator() {
+ return affectedValues.iterator();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return affectedValues.remove(o);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return affectedValues.removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return affectedValues.retainAll(c);
+ }
+
+ @Override
+ public int size() {
+ return affectedValues.size();
+ }
+
+ @Override
+ public Object[] toArray() {
+ return affectedValues.toArray();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ return affectedValues.toArray(a);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
index ca2babb..1ba1bb4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
@@ -13,11 +13,11 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
@@ -26,11 +26,9 @@
import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
-import java.util.Set;
public class AssertionErrorTwoArgsConstructorRewriter {
@@ -52,36 +50,34 @@
if (options.canUseAssertionErrorTwoArgumentConstructor()) {
return;
}
- Set<Value> newOutValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
ListIterator<BasicBlock> blockIterator = code.listIterator();
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
InstructionListIterator insnIterator = block.listIterator(code);
List<NewInstance> newInstancesToRemove = new ArrayList<>();
while (insnIterator.hasNext()) {
- Instruction current = insnIterator.next();
- if (current.isInvokeMethod()) {
- DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
+ InvokeDirect invoke = insnIterator.next().asInvokeDirect();
+ if (invoke != null) {
+ DexMethod invokedMethod = invoke.getInvokedMethod();
if (invokedMethod == dexItemFactory.assertionErrorMethods.initMessageAndCause) {
- List<Value> inValues = current.inValues();
- assert inValues.size() == 3; // receiver, message, cause
- Value newInstanceValue = current.getFirstOperand();
+ assert invoke.arguments().size() == 3; // receiver, message, cause
+ Value newInstanceValue = invoke.getReceiver();
Instruction definition = newInstanceValue.getDefinition();
if (!definition.isNewInstance()) {
continue;
}
- InvokeStatic invoke =
+ InvokeStatic replacement =
InvokeStatic.builder()
.setMethod(
createSynthetic(methodProcessor, methodProcessingContext).getReference())
.setFreshOutValue(
code, dexItemFactory.assertionErrorType.toTypeElement(appView))
- .setPosition(current)
- .setArguments(inValues.subList(1, 3))
+ .setPosition(invoke)
+ .setArguments(invoke.arguments().subList(1, 3))
.build();
- insnIterator.replaceCurrentInstruction(invoke);
- newInstanceValue.replaceUsers(invoke.outValue());
- newOutValues.add(invoke.outValue());
+ insnIterator.replaceCurrentInstruction(replacement);
+ newInstanceValue.replaceUsers(replacement.outValue(), affectedValues);
newInstancesToRemove.add(definition.asNewInstance());
}
}
@@ -89,9 +85,7 @@
newInstancesToRemove.forEach(
newInstance -> newInstance.removeOrReplaceByDebugLocalRead(code));
}
- if (!newOutValues.isEmpty()) {
- new TypeAnalysis(appView).widening(newOutValues);
- }
+ affectedValues.widening(appView, code);
assert code.isConsistentSSA(appView);
}
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 ef8b039..0680915 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
@@ -18,8 +18,6 @@
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;
@@ -40,7 +38,6 @@
import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfoLookup;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
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;
@@ -62,13 +59,21 @@
public class AssumeInserter {
private final AppView<AppInfoWithLiveness> appView;
+ private final boolean keepRedundantBlocks;
public AssumeInserter(AppView<AppInfoWithLiveness> appView) {
+ this(appView, false);
+ }
+
+ public AssumeInserter(AppView<AppInfoWithLiveness> appView, boolean keepRedundantBlocks) {
this.appView = appView;
+ this.keepRedundantBlocks = keepRedundantBlocks;
}
public void insertAssumeInstructions(IRCode code, Timing timing) {
insertAssumeInstructionsInBlocks(code, code.listIterator(), alwaysTrue(), timing);
+ code.removeRedundantBlocks();
+ assert code.isConsistentSSA(appView);
}
public void insertAssumeInstructionsInBlocks(
@@ -283,7 +288,7 @@
return false;
}
- SingleFieldResolutionResult resolutionResult =
+ SingleFieldResolutionResult<?> resolutionResult =
appView.appInfo().resolveField(fieldGet.getField()).asSingleFieldResolutionResult();
if (resolutionResult == null) {
return false;
@@ -340,7 +345,7 @@
assumedValues.removeIf(
(instruction, assumedValue, assumedValueInfo) -> {
// Assumed values with dynamic type information are never redundant.
- if (assumedValueInfo.hasDynamicTypeInfo()) {
+ if (assumedValueInfo.hasDynamicTypeInfoIgnoringNullability()) {
return false;
}
@@ -352,7 +357,7 @@
return false;
}
- Instruction definition = assumedValue.definition;
+ Instruction definition = assumedValue.getDefinition();
if (definition == instruction) {
return false;
}
@@ -364,10 +369,12 @@
}
if (!otherAssumedValueInfo.isNonNull()) {
- // This is not redundant, but we can strenghten it with the dynamic type information
+ // This is not redundant, but we can strengthen it with the dynamic type information
// from the other assume instruction.
- assumedValueInfo.setDynamicTypeAssumption(
- otherAssumedValueInfo.getDynamicTypeAssumption());
+ assumedValueInfo.setDynamicType(
+ otherAssumedValueInfo
+ .getDynamicType()
+ .withNullability(Nullability.definitelyNotNull()));
return false;
}
@@ -537,7 +544,9 @@
}
});
if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
+ new TypeAnalysis(appView, code)
+ .setKeepRedundantBlocksAfterAssumeRemoval(keepRedundantBlocks)
+ .narrowingWithAssumeRemoval(affectedValues);
}
}
@@ -597,13 +606,13 @@
assumeInstruction = new ConstNumber(newValue, 0);
} else {
assumeInstruction =
- new Assume(
- assumedValueInfo.dynamicTypeAssumption,
- assumedValueInfo.nonNullAssumption,
+ Assume.create(
+ assumedValueInfo.dynamicType,
newValue,
assumedValue,
instruction,
- appView);
+ appView,
+ code.context());
}
assumeInstruction.setPosition(instruction.getPosition());
if (insertionBlock != block) {
@@ -656,7 +665,9 @@
private static boolean isNullableReferenceType(Value value) {
TypeElement type = value.getType();
- return type.isReferenceType() && type.asReferenceType().isNullable();
+ return type.isReferenceType()
+ && type.asReferenceType().isNullable()
+ && !type.nullability().isDefinitelyNull();
}
private static boolean isNullableReferenceTypeWithOtherNonDebugUsers(
@@ -677,8 +688,7 @@
static class AssumedValueInfo {
AssumedDominance dominance;
- DynamicTypeAssumption dynamicTypeAssumption;
- NonNullAssumption nonNullAssumption;
+ DynamicType dynamicType = DynamicType.unknown();
AssumedValueInfo(AssumedDominance dominance) {
this.dominance = dominance;
@@ -692,49 +702,53 @@
this.dominance = dominance;
}
- boolean hasDynamicTypeInfo() {
- return dynamicTypeAssumption != null;
+ boolean hasDynamicTypeInfoIgnoringNullability() {
+ return dynamicType.isDynamicTypeWithUpperBound() && !dynamicType.isUnknown();
}
- DynamicTypeAssumption getDynamicTypeAssumption() {
- return dynamicTypeAssumption;
+ DynamicType getDynamicType() {
+ return dynamicType;
}
- void setDynamicTypeAssumption(DynamicTypeAssumption dynamicTypeAssumption) {
- this.dynamicTypeAssumption = dynamicTypeAssumption;
- }
-
- void setDynamicTypeAssumption(DynamicTypeWithUpperBound dynamicType) {
+ void setDynamicType(DynamicType dynamicType) {
assert dynamicType != null;
- dynamicTypeAssumption = new DynamicTypeAssumption(dynamicType);
- if (dynamicType.getDynamicUpperBoundType().isDefinitelyNotNull()) {
- setNotNull();
- }
+ assert this.dynamicType.isNotNullType()
+ ? dynamicType.getNullability().isDefinitelyNotNull()
+ : this.dynamicType.isUnknown();
+ this.dynamicType = dynamicType;
+ }
+
+ Nullability getNullability() {
+ return dynamicType.getNullability();
}
boolean isNull() {
- return dynamicTypeAssumption != null
- && dynamicTypeAssumption.getDynamicType().getNullability().isDefinitelyNull();
+ return getNullability().isDefinitelyNull();
}
boolean isNonNull() {
- return nonNullAssumption != null;
+ return getNullability().isDefinitelyNotNull();
}
void setNotNull() {
- nonNullAssumption = NonNullAssumption.get();
+ if (dynamicType.isUnknown()) {
+ dynamicType = DynamicType.definitelyNotNull();
+ } else {
+ dynamicType = dynamicType.withNullability(Nullability.definitelyNotNull());
+ }
}
boolean isSubsumedBy(AssumedValueInfo other) {
- return !hasDynamicTypeInfo() && other.isNonNull();
+ return !hasDynamicTypeInfoIgnoringNullability() && other.isNonNull();
}
void strengthenWith(AssumedValueInfo info) {
if (info.isNonNull()) {
setNotNull();
}
- if (!hasDynamicTypeInfo() && info.hasDynamicTypeInfo()) {
- setDynamicTypeAssumption(info.getDynamicTypeAssumption());
+ if (!hasDynamicTypeInfoIgnoringNullability()
+ && info.hasDynamicTypeInfoIgnoringNullability()) {
+ setDynamicType(info.getDynamicType().withNullability(getNullability()));
}
}
}
@@ -799,14 +813,6 @@
return assumedValues.isEmpty();
}
- void forEach(TriConsumer<Instruction, Value, AssumedValueInfo> consumer) {
- assumedValues.forEach(
- (instruction, dominancePerValue) ->
- dominancePerValue.forEach(
- (assumedValue, assumedValueInfo) ->
- consumer.accept(instruction, assumedValue, assumedValueInfo)));
- }
-
void removeAll(Map<Instruction, Map<Value, AssumedValueInfo>> keys) {
keys.forEach(
(instruction, redundantAssumedValues) -> {
@@ -872,7 +878,7 @@
instruction,
assumedValue,
AssumedDominance.everything(),
- assumedValueInfo -> assumedValueInfo.setDynamicTypeAssumption(dynamicType));
+ assumedValueInfo -> assumedValueInfo.setDynamicType(dynamicType));
}
void addNonNullValueKnownToDominateAllUsers(Instruction instruction, Value nonNullValue) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
index 70057fc..20ba610 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeRemover.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
@@ -13,8 +12,8 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Value;
import com.google.common.collect.Sets;
-import java.util.Collection;
import java.util.Set;
+import java.util.function.Consumer;
/**
* When we have Assume instructions we generally verify that the Assume instructions contribute with
@@ -33,98 +32,101 @@
private final AppView<?> appView;
private final IRCode code;
- private final Set<Value> affectedValues;
- private final Set<Assume> assumeInstructionsToRemove = Sets.newIdentityHashSet();
-
- private boolean mayHaveIntroducedTrivialPhi = false;
+ private final Set<Assume> affectedAssumeInstructions = Sets.newIdentityHashSet();
public AssumeRemover(AppView<?> appView, IRCode code) {
- this(appView, code, Sets.newIdentityHashSet());
- }
-
- public AssumeRemover(AppView<?> appView, IRCode code, Set<Value> affectedValues) {
this.appView = appView;
this.code = code;
- this.affectedValues = affectedValues;
}
- public Set<Value> getAffectedValues() {
- return affectedValues;
+ public void addAffectedAssumeInstruction(Assume assumeInstruction) {
+ affectedAssumeInstructions.add(assumeInstruction);
}
- public boolean mayHaveIntroducedTrivialPhi() {
- return mayHaveIntroducedTrivialPhi;
+ public boolean hasAffectedAssumeInstructions() {
+ return !affectedAssumeInstructions.isEmpty();
}
- public void markAssumeDynamicTypeUsersForRemoval(Value value) {
- for (Instruction user : value.aliasedUsers()) {
- if (user.isAssume()) {
- Assume assumeInstruction = user.asAssume();
- assumeInstruction.unsetDynamicTypeAssumption();
- if (!assumeInstruction.hasNonNullAssumption()) {
- markForRemoval(assumeInstruction);
+ private boolean removeAssumeInstructionIfRedundant(
+ Assume assumeInstruction,
+ InstructionListIterator instructionIterator,
+ Set<Value> newAffectedValues,
+ Consumer<Assume> redundantAssumeConsumer) {
+ if (!affectedAssumeInstructions.remove(assumeInstruction)) {
+ return false;
+ }
+ if (assumeInstruction.src().isConstant()) {
+ removeRedundantAssumeInstruction(
+ assumeInstruction, instructionIterator, newAffectedValues, redundantAssumeConsumer);
+ return true;
+ }
+ if (assumeInstruction.hasDynamicTypeIgnoringNullability()
+ && assumeInstruction
+ .getDynamicType()
+ .asDynamicTypeWithUpperBound()
+ .getDynamicUpperBoundType()
+ .strictlyLessThan(assumeInstruction.src().getType(), appView)) {
+ assert assumeInstruction
+ .getDynamicType()
+ .getNullability()
+ .lessThanOrEqual(assumeInstruction.src().getType().nullability());
+ return false;
+ }
+ if (assumeInstruction.hasNonNullAssumption()
+ && !assumeInstruction.src().isConstant()
+ && assumeInstruction.src().getType().isNullable()
+ && !assumeInstruction.src().getType().isDefinitelyNull()) {
+ assumeInstruction.clearDynamicTypeAssumption();
+ return false;
+ }
+ removeRedundantAssumeInstruction(
+ assumeInstruction, instructionIterator, newAffectedValues, redundantAssumeConsumer);
+ return true;
+ }
+
+ private void removeRedundantAssumeInstruction(
+ Assume assumeInstruction,
+ InstructionListIterator instructionIterator,
+ Set<Value> newAffectedValues,
+ Consumer<Assume> redundantAssumeConsumer) {
+ Value inValue = assumeInstruction.src();
+ Value outValue = assumeInstruction.outValue();
+ if (outValue == null) {
+ // Already removed.
+ return;
+ }
+
+ // Check if we need to run the type analysis for the affected values of the out-value.
+ if (!outValue.getType().equals(inValue.getType())) {
+ newAffectedValues.addAll(outValue.affectedValues());
+ }
+
+ outValue.replaceUsers(inValue);
+ redundantAssumeConsumer.accept(assumeInstruction);
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+
+ public boolean removeRedundantAssumeInstructions(
+ Set<Value> newAffectedValues, Consumer<Assume> redundantAssumeConsumer) {
+ if (affectedAssumeInstructions.isEmpty()) {
+ return false;
+ }
+ boolean changed = false;
+ for (BasicBlock block : code.getBlocks()) {
+ InstructionListIterator instructionIterator = block.listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (instruction.isAssume()) {
+ changed |=
+ removeAssumeInstructionIfRedundant(
+ instruction.asAssume(),
+ instructionIterator,
+ newAffectedValues,
+ redundantAssumeConsumer);
}
}
}
- }
-
- public void markUnusedAssumeValuesForRemoval(Collection<Value> values) {
- for (Value value : values) {
- if (value.isDefinedByInstructionSatisfying(Instruction::isAssume) && !value.hasAnyUsers()) {
- markForRemoval(value.getDefinition().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();
-
- // 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()) {
- mayHaveIntroducedTrivialPhi = true;
- }
-
- outValue.replaceUsers(inValue);
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- }
-
- public AssumeRemover removeMarkedInstructions() {
- return removeMarkedInstructions(null);
- }
-
- public AssumeRemover removeMarkedInstructions(Set<BasicBlock> blocksToBeRemoved) {
- if (!assumeInstructionsToRemove.isEmpty()) {
- for (BasicBlock block : code.blocks) {
- if (blocksToBeRemoved != null && blocksToBeRemoved.contains(block)) {
- continue;
- }
- InstructionListIterator instructionIterator = block.listIterator(code);
- while (instructionIterator.hasNext()) {
- Instruction instruction = instructionIterator.next();
- if (instruction.isAssume()) {
- removeIfMarked(instruction.asAssume(), instructionIterator);
- }
- }
- }
- }
- return this;
- }
-
- public void finish() {
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedAssumeInstructions.clear();
+ return changed;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CheckNotNullConverter.java b/src/main/java/com/android/tools/r8/ir/optimize/CheckNotNullConverter.java
index 9cffada..58a667d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CheckNotNullConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CheckNotNullConverter.java
@@ -21,6 +21,7 @@
public static void runIfNecessary(AppView<?> appView, IRCode code) {
if (appView.enableWholeProgramOptimizations()) {
run(appView.withClassHierarchy(), code);
+ assert code.isConsistentSSA(appView);
}
}
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 19f053f..e96ef16 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
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
@@ -113,10 +114,13 @@
}
if (!valuesThatRequireWidening.isEmpty()) {
- new TypeAnalysis(appView).widening(valuesThatRequireWidening);
+ new TypeAnalysis(appView, code).widening(valuesThatRequireWidening);
}
+ code.removeRedundantBlocks();
+
assert Streams.stream(code.instructions()).noneMatch(Instruction::isAssume);
+ assert code.isConsistentSSA(appView);
}
public static void removeOrReplaceByDebugLocalWrite(
@@ -200,6 +204,7 @@
}
}
}
+ assert code.isConsistentSSA(appView);
}
/**
@@ -361,8 +366,7 @@
DexType javaLangSystemType = dexItemFactory.javaLangSystemType;
DexType javaIoPrintStreamType = dexItemFactory.javaIoPrintStreamType;
Value out =
- code.createValue(
- TypeElement.fromDexType(javaIoPrintStreamType, definitelyNotNull(), appView));
+ code.createValue(TypeElement.fromDexType(javaIoPrintStreamType, maybeNull(), appView));
DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
DexMethod print = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "print");
@@ -431,7 +435,7 @@
iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, nul)));
iterator = isNotNullBlock.listIterator(code);
iterator.setInsertionPosition(position);
- value = code.createValue(TypeElement.classClassType(appView, definitelyNotNull()));
+ value = code.createValue(TypeElement.classClassType(appView, maybeNull()));
iterator.add(
new InvokeVirtual(
dexItemFactory.objectMembers.getClass, value, ImmutableList.of(arguments.get(i))));
@@ -449,6 +453,7 @@
}
// When we fall out of the loop the iterator is in the last eol block.
iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, empty)));
+ assert code.isConsistentSSA(appView);
}
// The javac fix for JDK-8272564 has to be rewritten back to invoke-virtual on Object if the
@@ -474,5 +479,6 @@
}
}
}
+ assert code.isConsistentSSA(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index b41131a..b74dff0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -53,13 +53,15 @@
// more dead instructions.
Deque<BasicBlock> worklist = new ArrayDeque<>();
do {
+ AffectedValues affectedValues = new AffectedValues();
ValueIsDeadAnalysis valueIsDeadAnalysis = new ValueIsDeadAnalysis(appView, code);
worklist.addAll(code.topologicallySortedBlocks());
while (!worklist.isEmpty()) {
BasicBlock block = worklist.removeLast();
- removeDeadInstructions(worklist, code, block, valueIsDeadAnalysis);
+ removeDeadInstructions(worklist, code, block, affectedValues, valueIsDeadAnalysis);
removeDeadPhis(worklist, block, valueIsDeadAnalysis);
}
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
} while (branchSimplifier
.simplifyIf(code)
.asControlFlowSimplificationResult()
@@ -134,6 +136,7 @@
Queue<BasicBlock> worklist,
IRCode code,
BasicBlock block,
+ AffectedValues affectedValues,
ValueIsDeadAnalysis valueIsDeadAnalysis) {
InstructionListIterator iterator = block.listIterator(code, block.getInstructions().size());
while (iterator.hasPrevious()) {
@@ -145,7 +148,7 @@
if (!checkCast.isRefiningStaticType(appView.options())
&& checkCast.outValue().getLocalInfo() == checkCast.object().getLocalInfo()) {
updateWorklistWithNonDebugUses(worklist, checkCast);
- checkCast.outValue().replaceUsers(checkCast.object());
+ checkCast.outValue().replaceUsers(checkCast.object(), affectedValues);
checkCast.object().uniquePhiUsers().forEach(Phi::removeTrivialPhi);
}
}
@@ -235,7 +238,8 @@
}
}
if (mayHaveIntroducedUnreachableBlocks) {
- code.removeUnreachableBlocks();
+ AffectedValues affectedValues = code.removeUnreachableBlocks();
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
}
assert code.isConsistentGraph(appView);
return mayHaveIntroducedUnreachableBlocks;
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 9a086cd..f53576a 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
@@ -13,7 +13,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-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.BasicBlock;
@@ -57,8 +56,7 @@
}
public void devirtualizeInvokeInterface(IRCode code) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
- AssumeRemover assumeRemover = new AssumeRemover(appView, code);
+ AffectedValues affectedValues = new AffectedValues();
ProgramMethod context = code.context();
Map<InvokeInterface, InvokeVirtual> devirtualizedCall = new IdentityHashMap<>();
DominatorTree dominatorTree = new DominatorTree(code);
@@ -115,7 +113,7 @@
assert nonNull.src() == oldReceiver;
assert !oldReceiver.hasLocalInfo();
oldReceiver.replaceSelectiveUsers(
- newReceiver, ImmutableSet.of(nonNull), ImmutableMap.of());
+ newReceiver, ImmutableSet.of(nonNull), ImmutableMap.of(), affectedValues);
}
}
}
@@ -297,24 +295,20 @@
it.next();
}
}
- affectedValues.addAll(receiver.affectedValues());
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(receiver);
if (!receiver.hasLocalInfo()) {
receiver.replaceSelectiveUsers(
- newReceiver, ImmutableSet.of(devirtualizedInvoke), ImmutableMap.of());
+ newReceiver,
+ ImmutableSet.of(devirtualizedInvoke),
+ ImmutableMap.of(),
+ affectedValues);
} else {
- receiver.removeUser(devirtualizedInvoke);
- devirtualizedInvoke.replaceValue(receiver, newReceiver);
+ devirtualizedInvoke.replaceValue(receiver, newReceiver, affectedValues);
}
}
}
}
}
- assumeRemover.removeMarkedInstructions();
- affectedValues.addAll(assumeRemover.getAffectedValues());
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeRedundantBlocks();
assert code.isConsistentSSA(appView);
}
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 11d016e..95f43b9 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
@@ -25,7 +25,6 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy;
import com.android.tools.r8.ir.analysis.type.Nullability;
-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.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
@@ -667,9 +666,7 @@
// using a const-class instruction.
Value lockValue;
if (target.getDefinition().isStatic()) {
- lockValue =
- code.createValue(
- TypeElement.fromDexType(dexItemFactory.objectType, definitelyNotNull(), appView));
+ lockValue = code.createValue(TypeElement.classClassType(appView, definitelyNotNull()));
monitorEnterBlockIterator.add(new ConstClass(lockValue, target.getHolderType()));
} else {
lockValue = entryBlock.getInstructions().getFirst().asArgument().outValue();
@@ -940,7 +937,7 @@
InliningIRProvider inliningIRProvider,
MethodProcessor methodProcessor,
Timing timing) {
- AssumeRemover assumeRemover = new AssumeRemover(appView, code);
+ AffectedValues affectedValues = new AffectedValues();
Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
BasicBlockIterator blockIterator = code.listIterator();
ClassInitializationAnalysis classInitializationAnalysis =
@@ -973,7 +970,7 @@
}
if (tryInlineMethodWithoutSideEffects(
- code, iterator, invoke, resolutionResult.getResolutionPair(), assumeRemover)) {
+ code, iterator, invoke, resolutionResult.getResolutionPair())) {
continue;
}
@@ -1032,12 +1029,6 @@
// Verify this code went through the full pipeline.
assert singleTarget.getDefinition().isProcessed();
- // Mark AssumeDynamicType instruction for the out-value for removal, if any.
- Value outValue = invoke.outValue();
- if (outValue != null) {
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(outValue);
- }
-
boolean inlineeMayHaveInvokeMethod = inlinee.code.metadata().mayHaveInvokeMethod();
// Inline the inlinee code in place of the invoke instruction
@@ -1065,7 +1056,8 @@
}
classInitializationAnalysis.notifyCodeHasChanged();
- postProcessInlineeBlocks(code, blockIterator, block, blocksToRemove, timing);
+ postProcessInlineeBlocks(
+ code, blockIterator, block, affectedValues, blocksToRemove, timing);
// The synthetic and bridge flags are maintained only if the inlinee has also these flags.
if (context.getDefinition().isBridge() && !inlinee.code.method().isBridge()) {
@@ -1089,15 +1081,13 @@
blockIterator.next();
}
}
- } else if (current.isAssume()) {
- assumeRemover.removeIfMarked(current.asAssume(), iterator);
}
}
}
assert inlineeStack.isEmpty();
- assumeRemover.removeMarkedInstructions(blocksToRemove).finish();
- classInitializationAnalysis.finish();
code.removeBlocks(blocksToRemove);
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ classInitializationAnalysis.finish();
code.removeAllDeadAndTrivialPhis();
code.removeRedundantBlocks();
assert code.isConsistentSSA(appView);
@@ -1107,8 +1097,7 @@
IRCode code,
InstructionListIterator iterator,
InvokeMethod invoke,
- DexClassAndMethod resolvedMethod,
- AssumeRemover assumeRemover) {
+ DexClassAndMethod resolvedMethod) {
if (invoke.isInvokeMethodWithReceiver()) {
if (!iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context())) {
return false;
@@ -1123,7 +1112,6 @@
}
// Succeeded.
- assumeRemover.markUnusedAssumeValuesForRemoval(invoke.arguments());
return true;
}
@@ -1149,6 +1137,7 @@
IRCode code,
BasicBlockIterator blockIterator,
BasicBlock block,
+ AffectedValues affectedValues,
Set<BasicBlock> blocksToRemove,
Timing timing) {
BasicBlock state = IteratorUtils.peekNext(blockIterator);
@@ -1164,7 +1153,7 @@
inlineeBlocks.add(inlineeBlock);
}
});
- applyMemberValuePropagationToInlinee(code, blockIterator, inlineeBlocks);
+ applyMemberValuePropagationToInlinee(code, blockIterator, affectedValues, inlineeBlocks);
// Add non-null IRs only to the inlinee blocks.
rewindBlockIterator(blockIterator, block);
@@ -1179,19 +1168,19 @@
BasicBlockIterator blockIterator,
Set<BasicBlock> inlineeBlocks,
Timing timing) {
- new AssumeInserter(appView)
+ boolean keepRedundantBlocks = true; // since we have a live block iterator
+ new AssumeInserter(appView, keepRedundantBlocks)
.insertAssumeInstructionsInBlocks(code, blockIterator, inlineeBlocks::contains, timing);
assert !blockIterator.hasNext();
}
private void applyMemberValuePropagationToInlinee(
- IRCode code, BasicBlockIterator blockIterator, Set<BasicBlock> inlineeBlocks) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ AffectedValues affectedValues,
+ Set<BasicBlock> inlineeBlocks) {
new R8MemberValuePropagation(appView)
.run(code, blockIterator, affectedValues, inlineeBlocks::contains);
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
assert !blockIterator.hasNext();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
new file mode 100644
index 0000000..bd0376b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
@@ -0,0 +1,276 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.function.BiFunction;
+import java.util.function.Predicate;
+
+// Per-class collection of member signatures.
+public abstract class MemberPoolCollection<R extends DexMember<?, R>> {
+
+ final Equivalence<R> equivalence;
+ final AppView<AppInfoWithLiveness> appView;
+ final SubtypingInfo subtypingInfo;
+ final Map<DexClass, MemberPool<R>> memberPools = new ConcurrentHashMap<>();
+
+ MemberPoolCollection(
+ AppView<AppInfoWithLiveness> appView,
+ Equivalence<R> equivalence,
+ SubtypingInfo subtypingInfo) {
+ this.appView = appView;
+ this.equivalence = equivalence;
+ this.subtypingInfo = subtypingInfo;
+ }
+
+ public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("Building member pool collection");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+
+ // Generate a future for each class that will build the member pool collection for the
+ // corresponding class. Note that, we visit the classes using a top-down class hierarchy
+ // traversal, since this ensures that we do not visit library classes that are not
+ // reachable from any program class.
+ TopDownClassHierarchyTraversal.forAllClasses(appView)
+ .visit(appView.appInfo().classes(), clazz -> submit(clazz, futures, executorService));
+ ThreadUtils.awaitFutures(futures);
+ } finally {
+ timing.end();
+ }
+ }
+
+ public MemberPool<R> buildForHierarchy(
+ DexClass clazz, ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("Building member pool collection");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+ submitAll(
+ getAllSuperTypesInclusive(clazz, memberPools::containsKey), futures, executorService);
+ submitAll(getAllSubTypesExclusive(clazz, memberPools::containsKey), futures, executorService);
+ ThreadUtils.awaitFutures(futures);
+ } finally {
+ timing.end();
+ }
+ return get(clazz);
+ }
+
+ public boolean hasPool(DexClass clazz) {
+ return memberPools.containsKey(clazz);
+ }
+
+ public MemberPool<R> get(DexClass clazz) {
+ assert hasPool(clazz);
+ return memberPools.get(clazz);
+ }
+
+ public boolean markIfNotSeen(DexClass clazz, R reference) {
+ MemberPool<R> memberPool = get(clazz);
+ Wrapper<R> key = equivalence.wrap(reference);
+ if (memberPool.hasSeen(key)) {
+ return true;
+ }
+ memberPool.seen(key);
+ return false;
+ }
+
+ private void submitAll(
+ Iterable<? extends DexClass> classes,
+ List<Future<?>> futures,
+ ExecutorService executorService) {
+ for (DexClass clazz : classes) {
+ submit(clazz, futures, executorService);
+ }
+ }
+
+ private void submit(DexClass clazz, List<Future<?>> futures, ExecutorService executorService) {
+ futures.add(executorService.submit(computeMemberPoolForClass(clazz)));
+ }
+
+ abstract Runnable computeMemberPoolForClass(DexClass clazz);
+
+ private Set<DexClass> getAllSuperTypesInclusive(
+ DexClass subject, Predicate<DexClass> stoppingCriterion) {
+ Set<DexClass> superTypes = new HashSet<>();
+ Deque<DexClass> worklist = new ArrayDeque<>();
+ worklist.add(subject);
+ while (!worklist.isEmpty()) {
+ DexClass clazz = worklist.pop();
+ if (stoppingCriterion.test(clazz)) {
+ continue;
+ }
+ if (superTypes.add(clazz)) {
+ if (clazz.superType != null) {
+ addNonNull(worklist, appView.definitionFor(clazz.superType));
+ }
+ for (DexType interfaceType : clazz.interfaces.values) {
+ addNonNull(worklist, appView.definitionFor(interfaceType));
+ }
+ }
+ }
+ return superTypes;
+ }
+
+ private Set<DexClass> getAllSubTypesExclusive(
+ DexClass subject, Predicate<DexClass> stoppingCriterion) {
+ Set<DexClass> subTypes = new HashSet<>();
+ Deque<DexClass> worklist = new ArrayDeque<>();
+ subtypingInfo.forAllImmediateExtendsSubtypes(
+ subject.type, type -> addNonNull(worklist, appView.definitionFor(type)));
+ subtypingInfo.forAllImmediateImplementsSubtypes(
+ subject.type, type -> addNonNull(worklist, appView.definitionFor(type)));
+ while (!worklist.isEmpty()) {
+ DexClass clazz = worklist.pop();
+ if (stoppingCriterion.test(clazz)) {
+ continue;
+ }
+ if (subTypes.add(clazz)) {
+ subtypingInfo.forAllImmediateExtendsSubtypes(
+ clazz.type, type -> addNonNull(worklist, appView.definitionFor(type)));
+ subtypingInfo.forAllImmediateImplementsSubtypes(
+ clazz.type, type -> addNonNull(worklist, appView.definitionFor(type)));
+ }
+ }
+ return subTypes;
+ }
+
+ public static class MemberPool<T> {
+
+ private final DexClass clazz;
+ private final Equivalence<T> equivalence;
+ private MemberPool<T> superType;
+ private final Set<MemberPool<T>> interfaces = new HashSet<>();
+ private final Set<MemberPool<T>> subTypes = new HashSet<>();
+ private final Set<Wrapper<T>> memberPool = new HashSet<>();
+
+ MemberPool(Equivalence<T> equivalence, DexClass clazz) {
+ this.equivalence = equivalence;
+ this.clazz = clazz;
+ }
+
+ synchronized void linkSupertype(MemberPool<T> superType) {
+ assert this.superType == null;
+ this.superType = superType;
+ }
+
+ synchronized void linkSubtype(MemberPool<T> subType) {
+ boolean added = subTypes.add(subType);
+ assert added;
+ }
+
+ synchronized void linkInterface(MemberPool<T> itf) {
+ boolean added = interfaces.add(itf);
+ assert added;
+ }
+
+ public void seen(T member) {
+ seen(equivalence.wrap(member));
+ }
+
+ public synchronized void seen(Wrapper<T> member) {
+ boolean added = memberPool.add(member);
+ assert added;
+ }
+
+ public boolean hasSeen(Wrapper<T> member) {
+ return fold(member, false, true, (t, ignored) -> true);
+ }
+
+ public boolean hasSeenDirectly(Wrapper<T> member) {
+ return here(member, false, (t, ignored) -> true);
+ }
+
+ public boolean hasSeenStrictlyAbove(Wrapper<T> member) {
+ return above(member, false, false, true, (t, ignored) -> true);
+ }
+
+ public boolean hasSeenStrictlyBelow(Wrapper<T> member) {
+ return below(member, false, true, (t, ignored) -> true);
+ }
+
+ private <S> S above(
+ Wrapper<T> member,
+ boolean inclusive,
+ S value,
+ S terminator,
+ BiFunction<DexClass, S, S> accumulator) {
+ WorkList<MemberPool<T>> workList = WorkList.newIdentityWorkList(this);
+ while (workList.hasNext()) {
+ MemberPool<T> next = workList.next();
+ if (inclusive) {
+ value = next.here(member, value, accumulator);
+ if (value == terminator) {
+ return value;
+ }
+ }
+ inclusive = true;
+ if (next.superType != null) {
+ workList.addIfNotSeen(next.superType);
+ }
+ workList.addIfNotSeen(next.interfaces);
+ }
+ return value;
+ }
+
+ private <S> S here(Wrapper<T> member, S value, BiFunction<DexClass, S, S> accumulator) {
+ if (memberPool.contains(member)) {
+ return accumulator.apply(clazz, value);
+ }
+ return value;
+ }
+
+ public <S> S below(
+ Wrapper<T> member, S value, S terminator, BiFunction<DexClass, S, S> accumulator) {
+ WorkList<MemberPool<T>> workList = WorkList.newIdentityWorkList(this.subTypes);
+ while (workList.hasNext()) {
+ MemberPool<T> next = workList.next();
+ value = next.here(member, value, accumulator);
+ if (value == terminator) {
+ return value;
+ }
+ workList.addIfNotSeen(next.interfaces);
+ workList.addIfNotSeen(next.subTypes);
+ }
+ return value;
+ }
+
+ public <S> S fold(
+ Wrapper<T> member, S initialValue, S terminator, BiFunction<DexClass, S, S> accumulator) {
+ S value = above(member, true, initialValue, terminator, accumulator);
+ if (value == terminator) {
+ return value;
+ }
+ return below(member, initialValue, terminator, accumulator);
+ }
+ }
+
+ private static <T> void addNonNull(Collection<T> collection, T item) {
+ if (item != null) {
+ collection.add(item);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
new file mode 100644
index 0000000..78eb343
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.google.common.base.Predicates;
+import java.util.function.Predicate;
+
+// Per-class collection of method signatures.
+//
+// Example use cases:
+// *) in publicizer,
+// to determine if a private method does not collide with methods in that class hierarchy.
+// *) in vertical class merger,
+// before moving a default interface method to its subtype, check if it does not collide with one
+// in the given class hierarchy.
+public class MethodPoolCollection extends MemberPoolCollection<DexMethod> {
+
+ private final Predicate<DexEncodedMethod> methodTester;
+
+ public MethodPoolCollection(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo) {
+ this(appView, subtypingInfo, Predicates.alwaysTrue());
+ }
+
+ public MethodPoolCollection(
+ AppView<AppInfoWithLiveness> appView,
+ SubtypingInfo subtypingInfo,
+ Predicate<DexEncodedMethod> methodTester) {
+ super(appView, MethodSignatureEquivalence.get(), subtypingInfo);
+ this.methodTester = methodTester;
+ }
+
+ public static boolean excludesPrivateInstanceMethod(DexEncodedMethod method) {
+ return !method.isPrivateMethod() || method.isStatic();
+ }
+
+ @Override
+ Runnable computeMemberPoolForClass(DexClass clazz) {
+ return () -> {
+ MemberPool<DexMethod> methodPool =
+ memberPools.computeIfAbsent(clazz, k -> new MemberPool<>(equivalence, k));
+ clazz.forEachMethod(
+ encodedMethod -> {
+ if (methodTester.test(encodedMethod)) {
+ methodPool.seen(equivalence.wrap(encodedMethod.getReference()));
+ }
+ });
+ if (clazz.superType != null) {
+ DexClass superClazz = appView.definitionFor(clazz.superType);
+ if (superClazz != null) {
+ MemberPool<DexMethod> superPool =
+ memberPools.computeIfAbsent(
+ superClazz, k -> new MemberPool<>(equivalence, superClazz));
+ superPool.linkSubtype(methodPool);
+ methodPool.linkSupertype(superPool);
+ }
+ }
+ if (clazz.isInterface()) {
+ for (DexType subtype : subtypingInfo.allImmediateSubtypes(clazz.type)) {
+ DexClass subClazz = appView.definitionFor(subtype);
+ if (subClazz != null) {
+ MemberPool<DexMethod> childPool =
+ memberPools.computeIfAbsent(subClazz, k -> new MemberPool<>(equivalence, subClazz));
+ methodPool.linkSubtype(childPool);
+ childPool.linkInterface(methodPool);
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index b2b0782..e9c9747 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.ir.code.ArrayGet;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.FieldGet;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InitClass;
@@ -232,7 +231,7 @@
private final boolean release;
// Values that may require type propagation.
- private final Set<Value> affectedValues = Sets.newIdentityHashSet();
+ private final AffectedValues affectedValues = new AffectedValues();
// Maps keeping track of fields that have an already loaded value at basic block entry.
private final BlockStates activeStates = new BlockStates();
@@ -268,8 +267,7 @@
@Override
public void eliminateRedundantRead(InstructionListIterator it, Instruction redundant) {
- affectedValues.addAll(redundant.outValue().affectedValues());
- redundant.outValue().replaceUsers(value);
+ redundant.outValue().replaceUsers(value, affectedValues);
it.removeOrReplaceByDebugLocalRead();
value.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
hasChanged = true;
@@ -359,7 +357,6 @@
}
}
- AssumeRemover assumeRemover = new AssumeRemover(appView, code, affectedValues);
for (BasicBlock head : code.topologicallySortedBlocks()) {
if (head.hasUniquePredecessor() && head.getUniquePredecessor().hasUniqueNormalSuccessor()) {
// Already visited.
@@ -389,16 +386,14 @@
}
if (instruction.isInstanceGet()) {
- handleInstanceGet(it, instruction.asInstanceGet(), field, assumeRemover);
+ handleInstanceGet(it, instruction.asInstanceGet(), field);
} else if (instruction.isInstancePut()) {
handleInstancePut(instruction.asInstancePut(), field);
} else if (instruction.isStaticGet()) {
- handleStaticGet(it, instruction.asStaticGet(), field, assumeRemover);
+ handleStaticGet(it, instruction.asStaticGet(), field);
} else if (instruction.isStaticPut()) {
handleStaticPut(instruction.asStaticPut(), field);
}
- } else if (instruction.isAssume()) {
- assumeRemover.removeIfMarked(instruction.asAssume(), it);
} else if (instruction.isInitClass()) {
handleInitClass(it, instruction.asInitClass());
} else if (instruction.isMonitor()) {
@@ -468,11 +463,10 @@
activeStates.recordActiveStateOnBlockExit(end, activeState);
}
processInstructionsToRemove();
- assumeRemover.removeMarkedInstructions().finish();
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
if (hasChanged) {
code.removeRedundantBlocks();
}
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.hasChanged(hasChanged);
}
@@ -669,10 +663,7 @@
}
private void handleInstanceGet(
- InstructionListIterator it,
- InstanceGet instanceGet,
- DexClassAndField field,
- AssumeRemover assumeRemover) {
+ InstructionListIterator it, InstanceGet instanceGet, DexClassAndField field) {
if (instanceGet.outValue().hasLocalInfo()) {
clearMostRecentInstanceFieldWrite(instanceGet, field);
return;
@@ -683,7 +674,6 @@
FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
if (replacement != null) {
if (isRedundantFieldLoadEliminationAllowed(field)) {
- markAssumeDynamicTypeUsersForRemoval(instanceGet, replacement, assumeRemover);
replacement.eliminateRedundantRead(it, instanceGet);
}
return;
@@ -723,21 +713,6 @@
}
}
- private void markAssumeDynamicTypeUsersForRemoval(
- FieldGet fieldGet, FieldValue replacement, AssumeRemover assumeRemover) {
- ExistingValue existingValue = replacement.asExistingValue();
- if (existingValue == null
- || !existingValue
- .getValue()
- .isDefinedByInstructionSatisfying(
- definition ->
- definition.isFieldGet()
- && definition.asFieldGet().getField().getType()
- == fieldGet.getField().getType())) {
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(fieldGet.outValue());
- }
- }
-
private void handleInstancePut(InstancePut instancePut, DexClassAndField field) {
// An instance-put instruction can potentially write the given field on all objects because of
// aliases.
@@ -778,10 +753,7 @@
}
private void handleStaticGet(
- InstructionListIterator instructionIterator,
- StaticGet staticGet,
- DexClassAndField field,
- AssumeRemover assumeRemover) {
+ InstructionListIterator instructionIterator, StaticGet staticGet, DexClassAndField field) {
markClassAsInitialized(field.getHolderType());
if (staticGet.outValue().hasLocalInfo()) {
@@ -792,7 +764,6 @@
FieldValue replacement = activeState.getStaticFieldValue(field.getReference());
if (replacement != null) {
- markAssumeDynamicTypeUsersForRemoval(staticGet, replacement, assumeRemover);
replacement.eliminateRedundantRead(instructionIterator, staticGet);
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index d7d41bd..d03fccb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-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.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
@@ -28,7 +27,6 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.google.common.collect.Sets;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -41,7 +39,7 @@
if (!appView.appInfo().canUseConstClassInstructions(appView.options())) {
return;
}
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
ProgramMethod context = code.context();
BasicBlockIterator blockIterator = code.listIterator();
while (blockIterator.hasNext()) {
@@ -71,9 +69,7 @@
}
}
// Newly introduced const-class is not null, and thus propagate that information.
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeRedundantBlocks();
assert code.isConsistentSSA(appView);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 9a75fc0..e79cac4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -239,6 +239,7 @@
new Rewriter(code, instructionIterator, serviceLoaderLoad)
.perform(classLoaderInvoke, synthesizedMethod.getReference());
}
+ assert code.isConsistentSSA(appView);
}
public void onLastWaveDone(PostMethodProcessor.Builder postMethodProcessorBuilder) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
index 1a594fc..2a90c2e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
@@ -166,7 +166,7 @@
}
// We are changing a NewInstance to a method call where we loose that the type is not null.
assert !newOutValues.isEmpty();
- new TypeAnalysis(appView).widening(newOutValues);
+ new TypeAnalysis(appView, code).widening(newOutValues);
// Outlining of instance initializers will in most cases change the api level of the context
// since all other soft verification issues has been outlined. To ensure that we do not inline
@@ -175,6 +175,8 @@
if (appView.enableWholeProgramOptimizations()) {
recomputeApiLevel(context, code);
}
+
+ assert code.isConsistentSSA(appView);
}
public void onLastWaveDone(PostMethodProcessor.Builder postMethodProcessorBuilder) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 19b2d81..112697a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -17,11 +17,10 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionOrPhi;
-import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.passes.BranchSimplifier;
import com.android.tools.r8.ir.conversion.passes.TrivialCheckCastAndInstanceOfRemover;
-import com.android.tools.r8.ir.optimize.AssumeRemover;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.ir.optimize.classinliner.InlineCandidateProcessor.IllegalClassInlinerStateException;
@@ -32,10 +31,8 @@
import com.android.tools.r8.utils.LazyBox;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public final class ClassInliner {
@@ -207,11 +204,9 @@
}
// Inline the class instance.
- Set<Value> affectedValues = Sets.newIdentityHashSet();
- AssumeRemover assumeRemover = new AssumeRemover(appView, code, affectedValues);
+ AffectedValues affectedValues = new AffectedValues();
try {
- anyInlinedMethods |=
- processor.processInlining(code, affectedValues, assumeRemover, inliningIRProvider);
+ anyInlinedMethods |= processor.processInlining(code, affectedValues, inliningIRProvider);
} catch (IllegalClassInlinerStateException e) {
// We introduced a user that we cannot handle in the class inliner as a result of force
// inlining. Abort gracefully from class inlining without removing the instance.
@@ -225,10 +220,9 @@
}
// Restore normality.
- assumeRemover.removeMarkedInstructions();
code.removeAllDeadAndTrivialPhis(affectedValues);
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeRedundantBlocks();
- assumeRemover.finish();
assert code.isConsistentSSA(appView);
rootsIterator.remove();
repeat = true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 52eaa3a..256d368 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -28,7 +28,6 @@
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleConstValue;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -52,7 +51,7 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
-import com.android.tools.r8.ir.optimize.AssumeRemover;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -379,10 +378,7 @@
//
// Returns `true` if at least one method was inlined.
boolean processInlining(
- IRCode code,
- Set<Value> affectedValues,
- AssumeRemover assumeRemover,
- InliningIRProvider inliningIRProvider)
+ IRCode code, AffectedValues affectedValues, InliningIRProvider inliningIRProvider)
throws IllegalClassInlinerStateException {
// Verify that `eligibleInstance` is not aliased.
assert eligibleInstance == eligibleInstance.getAliasedValue();
@@ -392,7 +388,7 @@
rebindIndirectEligibleInstanceUsersFromPhis();
removeMiscUsages(code, affectedValues);
- removeFieldReads(code, assumeRemover);
+ removeFieldReads(code, affectedValues);
removeFieldWrites();
removeInstruction(root);
return anyInlinedMethods;
@@ -590,7 +586,7 @@
}
// Remove miscellaneous users before handling field reads.
- private void removeMiscUsages(IRCode code, Set<Value> affectedValues) {
+ private void removeMiscUsages(IRCode code, AffectedValues affectedValues) {
boolean needToRemoveUnreachableBlocks = false;
for (Instruction user : eligibleInstance.uniqueUsers()) {
if (user.isInstanceOf()) {
@@ -682,25 +678,19 @@
}
// Replace field reads with appropriate values, insert phis when needed.
- private void removeFieldReads(IRCode code, AssumeRemover assumeRemover) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ private void removeFieldReads(IRCode code, AffectedValues affectedValues) {
if (root.isNewInstance()) {
- removeFieldReadsFromNewInstance(code, affectedValues, assumeRemover);
+ removeFieldReadsFromNewInstance(code, affectedValues);
} else {
assert root.isStaticGet();
- removeFieldReadsFromStaticGet(code, affectedValues, assumeRemover);
- }
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
+ removeFieldReadsFromStaticGet(code, affectedValues);
}
}
- private void removeFieldReadsFromNewInstance(
- IRCode code, Set<Value> affectedValues, AssumeRemover assumeRemover) {
+ private void removeFieldReadsFromNewInstance(IRCode code, AffectedValues affectedValues) {
List<InstanceGet> uniqueInstanceGetUsersWithDeterministicOrder = new ArrayList<>();
for (Instruction user : eligibleInstance.uniqueUsers()) {
if (user.isInstanceGet()) {
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(user.outValue());
if (user.hasUsedOutValue()) {
uniqueInstanceGetUsersWithDeterministicOrder.add(user.asInstanceGet());
} else {
@@ -734,7 +724,7 @@
private void removeFieldReadFromNewInstance(
IRCode code,
InstanceGet fieldRead,
- Set<Value> affectedValues,
+ AffectedValues affectedValues,
Map<DexField, FieldValueHelper> fieldHelpers) {
Value value = fieldRead.outValue();
if (value != null) {
@@ -756,8 +746,7 @@
removeInstruction(fieldRead);
}
- private void removeFieldReadsFromStaticGet(
- IRCode code, Set<Value> affectedValues, AssumeRemover assumeRemover) {
+ private void removeFieldReadsFromStaticGet(IRCode code, AffectedValues affectedValues) {
Set<BasicBlock> seen = Sets.newIdentityHashSet();
Set<Instruction> users = eligibleInstance.uniqueUsers();
for (Instruction user : users) {
@@ -778,7 +767,6 @@
}
if (instruction.isInstanceGet()) {
- assumeRemover.markAssumeDynamicTypeUsersForRemoval(instruction.outValue());
if (instruction.hasUsedOutValue()) {
replaceFieldReadFromStaticGet(
code, instructionIterator, user.asInstanceGet(), affectedValues);
@@ -806,7 +794,7 @@
IRCode code,
InstructionListIterator instructionIterator,
InstanceGet fieldRead,
- Set<Value> affectedValues) {
+ AffectedValues affectedValues) {
DexField fieldReference = fieldRead.getField();
DexClass holder = appView.definitionFor(fieldReference.getHolderType(), method);
DexEncodedField field = fieldReference.lookupOnClass(holder);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 7f34299..4ca2bb2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -527,7 +527,6 @@
Assume assume = enumUser.asAssume();
if (assume
.getDynamicTypeAssumption()
- .getDynamicType()
.getDynamicUpperBoundType()
.equalUpToNullability(enumClass.getType().toTypeElement(appView))) {
// OK.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index c88b48d..6ae8689 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -15,7 +15,6 @@
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.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
@@ -34,10 +33,10 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ArrayUtils;
-import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -63,7 +62,7 @@
protected CodeRewriterResult rewriteCode(IRCode code) {
assert appView.enableWholeProgramOptimizations();
boolean hasChanged = false;
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
Instruction current = iterator.next();
@@ -171,10 +170,7 @@
hasChanged = true;
}
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
- assert code.isConsistentSSA(appView);
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
return CodeRewriterResult.hasChanged(hasChanged);
}
@@ -221,7 +217,7 @@
return;
}
assert appView.enableWholeProgramOptimizations();
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
boolean mayHaveIntroducedUnreachableBlocks = false;
for (BasicBlock block : code.blocks) {
IntSwitch switchInsn = block.exit().asIntSwitch();
@@ -323,9 +319,8 @@
if (mayHaveIntroducedUnreachableBlocks) {
affectedValues.addAll(code.removeUnreachableBlocks());
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ assert code.isConsistentSSA(appView);
}
private Int2IntArrayMap computeOrdinalToTargetMap(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
index ee5dc08..fd78da3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -47,7 +47,8 @@
if (appView.hasLiveness()
&& singleTarget.getReference() == appView.dexItemFactory().enumMembers.valueOf
&& invoke.inValues().get(0).isConstClass()) {
- insertAssumeDynamicType(appView.withLiveness(), code, instructionIterator, invoke);
+ insertAssumeDynamicType(
+ appView.withLiveness(), code, instructionIterator, invoke, affectedValues);
}
}
@@ -55,7 +56,8 @@
AppView<AppInfoWithLiveness> appView,
IRCode code,
InstructionListIterator instructionIterator,
- InvokeMethod invoke) {
+ InvokeMethod invoke,
+ Set<Value> affectedValues) {
// TODO(b/152516470): Support unboxing enums with Enum#valueOf in try-catch.
if (invoke.getBlock().hasCatchHandlers()) {
return;
@@ -72,14 +74,15 @@
return;
}
// Replace usages of out-value by the out-value of the AssumeDynamicType instruction.
- Value specializedOutValue = code.createValue(outValue.getType(), outValue.getLocalInfo());
- outValue.replaceUsers(specializedOutValue);
+ Value specializedOutValue =
+ code.createValue(
+ outValue.getType().asReferenceType().asMeetWithNotNull(), outValue.getLocalInfo());
+ outValue.replaceUsers(specializedOutValue, affectedValues);
// Insert AssumeDynamicType instruction.
DynamicTypeWithUpperBound dynamicType = enumType.toDynamicType(appView, definitelyNotNull());
Assume assumeInstruction =
- Assume.createAssumeDynamicTypeInstruction(
- dynamicType, specializedOutValue, outValue, invoke, appView);
+ Assume.create(dynamicType, specializedOutValue, outValue, invoke, appView, code.context());
assumeInstruction.setPosition(appView.options().debug ? invoke.getPosition() : Position.none());
instructionIterator.add(assumeInstruction);
}
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 b00db83..a162069 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
@@ -12,16 +12,15 @@
import com.android.tools.r8.graph.DexItemFactory.LibraryMembers;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
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.Value;
import com.android.tools.r8.ir.conversion.CodeOptimization;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
@@ -115,7 +114,7 @@
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
BasicBlockIterator blockIterator = code.listIterator();
Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
while (blockIterator.hasNext()) {
@@ -170,9 +169,8 @@
}
code.removeBlocks(blocksToRemove);
-
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ code.removeRedundantBlocks();
+ assert code.isConsistentSSA(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
index 456ecc7..fb14d81 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
@@ -86,6 +86,7 @@
instructionIterator,
invokeWithReceiver,
singleTarget,
+ affectedValues,
state,
methodProcessor,
methodProcessingContext);
@@ -99,12 +100,13 @@
InstructionListIterator instructionIterator,
InvokeMethodWithReceiver invoke,
DexClassAndMethod singleTarget,
+ Set<Value> affectedValues,
State state,
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
boolean isStringBuilderUnused = state.isUnusedBuilder(invoke.getReceiver());
if (invoke.hasOutValue() && (options.isGeneratingDex() || isStringBuilderUnused)) {
- invoke.outValue().replaceUsers(invoke.getReceiver());
+ invoke.outValue().replaceUsers(invoke.getReceiver(), affectedValues);
invoke.getReceiver().uniquePhiUsers().forEach(Phi::removeTrivialPhi);
invoke.clearOutValue();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagation.java
index 90fd365..e48be77 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/MemberValuePropagation.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.ArrayGet;
@@ -36,9 +35,9 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
import com.android.tools.r8.utils.IteratorUtils;
-import com.google.common.collect.Sets;
import java.util.Set;
import java.util.function.Predicate;
@@ -60,14 +59,11 @@
if (!metadata.mayHaveFieldInstruction() && !metadata.mayHaveInvokeMethod()) {
return;
}
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
run(code, code.listIterator(), affectedValues, alwaysTrue());
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeRedundantBlocks();
assert code.isConsistentSSA(appView);
- assert code.verifyTypes(appView);
}
public void run(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
index 45f03bc..50154ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Add;
@@ -64,6 +65,7 @@
import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.SourceCode;
import com.android.tools.r8.ir.conversion.passes.MoveResultRewriter;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
@@ -1233,6 +1235,7 @@
@Override
protected void handle(int start, int end, Outline outline) {
DexMethod outlineMethod = generatedOutlines.get(outline);
+ AffectedValues affectedValues = new AffectedValues();
if (outlineMethod != null) {
assert removeMethodFromOutlineList(outline);
List<Value> in = new ArrayList<>();
@@ -1295,9 +1298,23 @@
lastInstruction.getBlock().listIterator(code, lastInstruction);
Instruction instructionBeforeEnd = endIterator.previous();
assert instructionBeforeEnd == lastInstruction;
- endIterator.set(outlineInvoke); // Replaces instructionBeforeEnd.
+ endIterator.set(outlineInvoke);
+ if (outlineInvoke.hasOutValue()
+ && returnValue.getType().isReferenceType()
+ && returnValue.getType().nullability().isDefinitelyNotNull()) {
+ Value nullableReturnValue =
+ code.createValue(
+ returnValue
+ .getType()
+ .asReferenceType()
+ .getOrCreateVariant(Nullability.maybeNull()),
+ returnValue.getLocalInfo());
+ outlineInvoke.outValue().replaceUsers(nullableReturnValue, affectedValues);
+ outlineInvoke.setOutValue(nullableReturnValue);
+ }
invokesToOutlineMethods.add(outlineInvoke);
}
+ affectedValues.widening(appView, code);
}
/** When assertions are enabled, remove method from the outline's list. */
@@ -1612,7 +1629,10 @@
}
});
}
+ code.removeRedundantBlocks();
}
+ code.removeRedundantBlocks();
+ assert code.isConsistentSSA(appView);
}
public boolean checkAllOutlineSitesFoundAgain() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
index fd6ae9b..c19f65e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.google.common.collect.ImmutableList;
/** StringBuilderAction defines an interface for updating the IR code based on optimizations. */
@@ -25,6 +26,7 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle);
default boolean isAllowedToBeOverwrittenByRemoveStringBuilderAction() {
@@ -50,8 +52,9 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle) {
- removeStringBuilderInstruction(iterator, instruction, oracle);
+ removeStringBuilderInstruction(iterator, instruction, affectedValues, oracle);
}
static RemoveStringBuilderAction getInstance() {
@@ -81,8 +84,12 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle) {
assert oracle.isToString(instruction, instruction.getFirstOperand());
+ if (instruction.hasOutValue()) {
+ instruction.outValue().addAffectedValuesTo(affectedValues);
+ }
iterator.replaceCurrentInstructionWithConstString(appView, code, replacement);
}
}
@@ -106,6 +113,7 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle) {
Instruction previous = iterator.previous();
InvokeMethodWithReceiver invoke = previous.asInvokeMethodWithReceiver();
@@ -168,8 +176,9 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle) {
- instruction.outValue().replaceUsers(existingString);
+ instruction.outValue().replaceUsers(existingString, affectedValues);
iterator.removeOrReplaceByDebugLocalRead();
}
@@ -214,6 +223,7 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle) {
Value constString = null;
if (newConstant != null) {
@@ -252,6 +262,7 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle) {
instruction.replaceValue(1, string);
}
@@ -305,6 +316,7 @@
IRCode code,
InstructionListIterator iterator,
Instruction instruction,
+ AffectedValues affectedValues,
StringBuilderOracle oracle) {
assert instruction.isInvokeMethod();
assert instruction.inValues().size() == 2;
@@ -330,7 +342,7 @@
Instruction next = iterator.next();
assert next == instruction;
if (removeInstruction) {
- removeStringBuilderInstruction(iterator, instruction, oracle);
+ removeStringBuilderInstruction(iterator, instruction, affectedValues, oracle);
} else {
instruction.replaceValue(1, outValue);
}
@@ -389,16 +401,19 @@
}
static void removeStringBuilderInstruction(
- InstructionListIterator iterator, Instruction instruction, StringBuilderOracle oracle) {
+ InstructionListIterator iterator,
+ Instruction instruction,
+ AffectedValues affectedValues,
+ StringBuilderOracle oracle) {
assert oracle.isModeledStringBuilderInstruction(
instruction,
value ->
value.getType().isClassType()
&& oracle.isStringBuilderType(value.getType().asClassType().getClassType()));
- if (oracle.isAppend(instruction) && instruction.outValue() != null) {
+ if (oracle.isAppend(instruction) && instruction.hasOutValue()) {
// Append will return the string builder instance. Before removing, ensure that
// all users of the output values uses the receiver.
- instruction.outValue().replaceUsers(instruction.getFirstOperand());
+ instruction.outValue().replaceUsers(instruction.getFirstOperand(), affectedValues);
}
iterator.removeOrReplaceByDebugLocalRead();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
index 2bc3346..af05e80 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.AppendNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
@@ -108,17 +109,18 @@
if (actions.isEmpty()) {
return CodeRewriterResult.NO_CHANGE;
}
+ AffectedValues affectedValues = new AffectedValues();
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
Instruction instruction = it.next();
StringBuilderAction stringBuilderAction = actions.get(instruction);
if (stringBuilderAction != null) {
- stringBuilderAction.perform(appView, code, it, instruction, oracle);
+ stringBuilderAction.perform(appView, code, it, instruction, affectedValues, oracle);
}
}
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
code.removeAllDeadAndTrivialPhis();
code.removeRedundantBlocks();
- assert code.isConsistentSSA(appView);
return CodeRewriterResult.HAS_CHANGED;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 5972b0e9..202dc62 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.escape.EscapeAnalysis;
import com.android.tools.r8.ir.analysis.escape.EscapeAnalysisConfiguration;
-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.ConstClass;
import com.android.tools.r8.ir.code.ConstNumber;
@@ -32,10 +31,9 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo;
-import com.google.common.collect.Sets;
import java.io.UTFDataFormatException;
-import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -71,7 +69,7 @@
if (!code.metadata().mayHaveConstString()) {
return;
}
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
Instruction instr = it.next();
@@ -125,8 +123,8 @@
Value stringValue =
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
- affectedValues.addAll(invoke.outValue().affectedValues());
- it.replaceCurrentInstruction(new ConstString(stringValue, factory.createString(sub)));
+ it.replaceCurrentInstruction(
+ new ConstString(stringValue, factory.createString(sub)), affectedValues);
continue;
}
@@ -140,8 +138,7 @@
Value newOutValue =
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
- affectedValues.addAll(invoke.outValue().affectedValues());
- it.replaceCurrentInstruction(new ConstString(newOutValue, resultString));
+ it.replaceCurrentInstruction(new ConstString(newOutValue, resultString), affectedValues);
continue;
}
@@ -228,14 +225,13 @@
it.replaceCurrentInstruction(constNumber);
}
// Computed substring is not null, and thus propagate that information.
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ assert code.isConsistentSSA(appView);
}
// Find Class#get*Name() with a constant-class and replace it with a const-string if possible.
public void rewriteClassGetName(AppView<?> appView, IRCode code) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
Instruction instr = it.next();
@@ -313,9 +309,12 @@
DexString name = null;
if (invokedMethod == factory.classMethods.getName) {
if (mayBeRenamed) {
+ Value stringValue =
+ code.createValue(
+ TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
deferred =
new DexItemBasedConstString(
- invoke.outValue(), baseType, ClassNameComputationInfo.create(NAME, arrayDepth));
+ stringValue, baseType, ClassNameComputationInfo.create(NAME, arrayDepth));
} else {
name = NAME.map(descriptor, holder, factory, arrayDepth);
}
@@ -325,9 +324,8 @@
} else if (invokedMethod == factory.classMethods.getCanonicalName) {
// Always returns null if the target type is local or anonymous class.
if (holder.isLocalClass() || holder.isAnonymousClass()) {
- affectedValues.addAll(invoke.outValue().affectedValues());
ConstNumber constNull = code.createConstNull();
- it.replaceCurrentInstruction(constNull);
+ it.replaceCurrentInstruction(constNull, affectedValues);
} else {
// b/119471127: If an outer class is shrunk, we may compute a wrong canonical name.
// Leave it as-is so that the class's canonical name is consistent across the app.
@@ -335,9 +333,13 @@
continue;
}
if (mayBeRenamed) {
+ Value stringValue =
+ code.createValue(
+ TypeElement.stringClassType(appView, definitelyNotNull()),
+ invoke.getLocalInfo());
deferred =
new DexItemBasedConstString(
- invoke.outValue(),
+ stringValue,
baseType,
ClassNameComputationInfo.create(CANONICAL_NAME, arrayDepth));
} else {
@@ -355,9 +357,13 @@
continue;
}
if (mayBeRenamed) {
+ Value stringValue =
+ code.createValue(
+ TypeElement.stringClassType(appView, definitelyNotNull()),
+ invoke.getLocalInfo());
deferred =
new DexItemBasedConstString(
- invoke.outValue(),
+ stringValue,
baseType,
ClassNameComputationInfo.create(SIMPLE_NAME, arrayDepth));
} else {
@@ -366,29 +372,26 @@
}
}
if (name != null) {
- affectedValues.addAll(invoke.outValue().affectedValues());
Value stringValue =
code.createValue(
TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
ConstString constString = new ConstString(stringValue, name);
- it.replaceCurrentInstruction(constString);
+ it.replaceCurrentInstruction(constString, affectedValues);
} else if (deferred != null) {
- affectedValues.addAll(invoke.outValue().affectedValues());
- it.replaceCurrentInstruction(deferred);
+ it.replaceCurrentInstruction(deferred, affectedValues);
}
}
// Computed name is not null or literally null (for canonical name of local/anonymous class).
// In either way, that is narrower information, and thus propagate that.
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ assert code.isConsistentSSA(appView);
}
// String#valueOf(null) -> "null"
// String#valueOf(String s) -> s
// str.toString() -> str
public void removeTrivialConversions(IRCode code) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
Instruction instr = it.next();
@@ -407,11 +410,7 @@
TypeElement inType = in.getType();
if (out != null && in.isAlwaysNull(appView)) {
affectedValues.addAll(out.affectedValues());
- Value nullStringValue =
- code.createValue(
- TypeElement.stringClassType(appView, definitelyNotNull()), invoke.getLocalInfo());
- ConstString nullString = new ConstString(nullStringValue, factory.createString("null"));
- it.replaceCurrentInstruction(nullString);
+ it.replaceCurrentInstructionWithConstString(appView, code, factory.createString("null"));
} else if (inType.nullability().isDefinitelyNotNull()
&& inType.isClassType()
&& inType.asClassType().getClassType().equals(factory.stringType)) {
@@ -445,9 +444,9 @@
}
}
// Newly added "null" string is not null, and thus propagate that information.
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ code.removeRedundantBlocks();
+ assert code.isConsistentSSA(appView);
}
static class StringOptimizerEscapeAnalysisConfiguration
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index 079b862..16a09d6 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -91,6 +91,7 @@
import com.android.tools.r8.ir.code.Shr;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.StringSwitch;
import com.android.tools.r8.ir.code.Sub;
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Ushr;
@@ -100,7 +101,9 @@
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.StringSwitchConverter;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
import com.android.tools.r8.lightir.LirCode.PositionEntry;
import com.android.tools.r8.lightir.LirCode.StructuredPositionEntry;
import com.android.tools.r8.lightir.LirCode.TryCatchTable;
@@ -116,6 +119,7 @@
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.function.BiFunction;
public class Lir2IRConverter {
@@ -144,7 +148,14 @@
lirCode.forEach(view -> view.accept(parser));
IRCode irCode = parser.getIRCode(method, conversionOptions);
// Some instructions have bottom types (e.g., phis). Compute their actual types by widening.
- new TypeAnalysis(appView).widening(irCode);
+ new TypeAnalysis(appView, irCode).widening();
+
+ if (conversionOptions.isStringSwitchConversionEnabled()) {
+ if (StringSwitchConverter.convertToStringSwitchInstructions(
+ irCode, appView.dexItemFactory())) {
+ irCode.removeRedundantBlocks();
+ }
+ }
return irCode;
}
@@ -168,6 +179,7 @@
private BasicBlock currentBlock = null;
private int nextInstructionIndex = 0;
+ private final Position entryPosition;
private Position currentPosition;
private PositionEntry nextPositionEntry = null;
private int nextIndexInPositionsTable = 0;
@@ -226,6 +238,11 @@
.build()));
}
}
+ if (positionTable.length > 0 && positionTable[0].getFromInstructionIndex() == 0) {
+ entryPosition = positionTable[0].getPosition(originalMethod);
+ } else {
+ entryPosition = currentPosition;
+ }
}
@Override
@@ -360,7 +377,7 @@
return new IRCode(
appView.options(),
method,
- Position.syntheticNone(),
+ entryPosition,
blockList,
strategy.getValueNumberGenerator(),
basicBlockNumberGenerator,
@@ -702,27 +719,47 @@
public void onIntSwitch(EV value, IntSwitchPayload payload) {
// keys is the 'value' -> 'target index' mapping.
int[] keys = payload.keys;
+ addSwitchInstruction(
+ payload.targets,
+ (successors, fallthrough) ->
+ new IntSwitch(getValue(value), keys, successors, fallthrough));
+ }
+
+ @Override
+ public void onStringSwitch(EV value, StringSwitchPayload payload) {
+ int size = payload.keys.length;
+ DexString[] keys = new DexString[size];
+ for (int i = 0; i < size; i++) {
+ keys[i] = (DexString) code.getConstantItem(payload.keys[i]);
+ }
+ addSwitchInstruction(
+ payload.targets,
+ (successors, fallthrough) ->
+ new StringSwitch(getValue(value), keys, successors, fallthrough));
+ }
+
+ private void addSwitchInstruction(
+ int[] targets, BiFunction<int[], Integer, Instruction> createSwitchInstruction) {
+ int size = targets.length;
// successorIndices is the 'target index' to 'IR successor index'.
- int[] successorIndices = new int[keys.length];
- List<BasicBlock> successorBlocks = new ArrayList<>();
- {
- // The mapping from instruction to successor is a temp mapping to track if any targets
- // point to the same block.
- Int2IntMap instructionToSuccessor = new Int2IntOpenHashMap();
- for (int i = 0; i < successorIndices.length; i++) {
- int instructionIndex = payload.targets[i];
- if (instructionToSuccessor.containsKey(instructionIndex)) {
- successorIndices[i] = instructionToSuccessor.get(instructionIndex);
- } else {
- int successorIndex = successorBlocks.size();
- successorIndices[i] = successorIndex;
- instructionToSuccessor.put(instructionIndex, successorIndex);
- successorBlocks.add(getBasicBlock(instructionIndex));
- }
+ int[] successorIndices = new int[size];
+ List<BasicBlock> successorBlocks = new ArrayList<>(size);
+ // The mapping from instruction to successor is a temp mapping to track if any targets
+ // point to the same block.
+ Int2IntMap instructionToSuccessor = new Int2IntOpenHashMap(size);
+ for (int i = 0; i < targets.length; i++) {
+ int instructionIndex = targets[i];
+ if (instructionToSuccessor.containsKey(instructionIndex)) {
+ successorIndices[i] = instructionToSuccessor.get(instructionIndex);
+ } else {
+ int successorIndex = successorBlocks.size();
+ successorIndices[i] = successorIndex;
+ instructionToSuccessor.put(instructionIndex, successorIndex);
+ successorBlocks.add(getBasicBlock(instructionIndex));
}
}
int fallthrough = successorBlocks.size();
- addInstruction(new IntSwitch(getValue(value), keys, successorIndices, fallthrough));
+ addInstruction(createSwitchInstruction.apply(successorIndices, fallthrough));
// The call to addInstruction will ensure the current block so don't amend to it before here.
// If the block has successors then the index mappings are not valid / need to be offset.
assert currentBlock.getSuccessors().isEmpty();
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 68b5c0e..0cd52a4 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -132,6 +132,17 @@
}
}
+ public static class StringSwitchPayload extends InstructionPayload {
+ public final int[] keys;
+ public final int[] targets;
+
+ public StringSwitchPayload(int[] keys, int[] targets) {
+ assert keys.length == targets.length;
+ this.keys = keys;
+ this.targets = targets;
+ }
+ }
+
public static class NameComputationPayload extends InstructionPayload {
public final NameComputationInfo<?> nameComputationInfo;
@@ -666,6 +677,23 @@
Collections.singletonList(value));
}
+ public LirBuilder<V, EV> addStringSwitch(
+ V value, DexString[] keys, BasicBlock[] targetsBlocks, BasicBlock fallthroughBlock) {
+ int size = keys.length;
+ assert targetsBlocks.length == size;
+ int[] keyIndices = new int[size];
+ int[] targetsIndices = new int[size];
+ for (int i = 0; i < size; i++) {
+ keyIndices[i] = getConstantIndex(keys[i]);
+ targetsIndices[i] = getBlockIndex(targetsBlocks[i]);
+ }
+ StringSwitchPayload payload = new StringSwitchPayload(keyIndices, targetsIndices);
+ return addInstructionTemplate(
+ LirOpcodes.STRINGSWITCH,
+ Collections.singletonList(payload),
+ Collections.singletonList(value));
+ }
+
public LirBuilder<V, EV> addIf(
IfType ifKind, ValueType valueType, V value, BasicBlock trueTarget) {
int opcode;
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index 0fccf4e..b14c481 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.google.common.collect.ImmutableMap;
@@ -39,6 +40,7 @@
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Function;
public class LirCode<EV> extends Code implements Iterable<LirInstructionView> {
@@ -90,7 +92,7 @@
}
public static class TryCatchTable {
- final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers;
+ private final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers;
public TryCatchTable(Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers) {
assert !tryCatchHandlers.isEmpty();
@@ -101,6 +103,10 @@
public CatchHandlers<Integer> getHandlersForBlock(int blockIndex) {
return tryCatchHandlers.get(blockIndex);
}
+
+ public void forEachHandler(BiConsumer<Integer, CatchHandlers<Integer>> fn) {
+ tryCatchHandlers.forEach(fn);
+ }
}
public static class DebugLocalInfoTable<EV> {
@@ -451,4 +457,23 @@
positionConsumer.accept(entry.getPosition(method));
}
}
+
+ public LirCode<EV> newCodeWithRewrittenConstantPool(Function<DexItem, DexItem> rewriter) {
+ DexItem[] rewrittenConstants = ArrayUtils.map(constants, rewriter, new DexItem[0]);
+ if (constants == rewrittenConstants) {
+ return this;
+ }
+ return new LirCode<>(
+ irMetadata,
+ rewrittenConstants,
+ positionTable,
+ argumentCount,
+ instructions,
+ instructionCount,
+ tryCatchTable,
+ debugLocalInfoTable,
+ strategyInfo,
+ useDexEstimationStrategy,
+ metadataMap);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
index fc1ccc6..9ac6b6c 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -211,6 +211,7 @@
int CHECKCAST_SAFE = 224;
int CHECKCAST_IGNORE_COMPAT = 225;
int CONSTCLASS_IGNORE_COMPAT = 226;
+ int STRINGSWITCH = 227;
static String toString(int opcode) {
switch (opcode) {
@@ -548,6 +549,8 @@
return "CHECKCAST_IGNORE_COMPAT";
case CONSTCLASS_IGNORE_COMPAT:
return "CONSTCLASS_IGNORE_COMPAT";
+ case STRINGSWITCH:
+ return "STRINGSWITCH";
default:
throw new Unreachable("Unexpected LIR opcode: " + opcode);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index d979ada..e3ca61d 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.lightir.LirBuilder.NameComputationPayload;
import com.android.tools.r8.lightir.LirBuilder.RecordFieldValuesPayload;
+import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import java.util.ArrayList;
import java.util.List;
@@ -372,6 +373,10 @@
onInstruction();
}
+ public void onStringSwitch(EV value, StringSwitchPayload payload) {
+ onInstruction();
+ }
+
public void onFallthrough() {
onInstruction();
}
@@ -1009,6 +1014,14 @@
onIntSwitch(value, payload);
return;
}
+ case LirOpcodes.STRINGSWITCH:
+ {
+ StringSwitchPayload payload =
+ (StringSwitchPayload) getConstantItem(view.getNextConstantOperand());
+ EV value = getNextValueOperand(view);
+ onStringSwitch(value, payload);
+ return;
+ }
case LirOpcodes.INVOKEDIRECT:
case LirOpcodes.INVOKEDIRECT_ITF:
{
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index cfe1a08..c3a8406 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
@@ -90,8 +91,7 @@
if (code.getTryCatchTable() != null) {
builder.append("try-catch-handlers:\n");
code.getTryCatchTable()
- .tryCatchHandlers
- .forEach(
+ .forEachHandler(
(index, handlers) -> {
builder.append(index).append(":\n");
for (CatchHandler<Integer> handler : handlers) {
@@ -241,6 +241,12 @@
}
@Override
+ public void onStringSwitch(EV value, StringSwitchPayload payload) {
+ appendValueArguments(value);
+ // TODO(b/225838009): Consider printing the switch payload info.
+ }
+
+ @Override
public void onFallthrough() {
// Nothing to append.
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java b/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
index eaa93d0..0a37c4d 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
@@ -20,8 +20,10 @@
import com.android.tools.r8.dex.code.DexFillArrayDataPayload;
import com.android.tools.r8.dex.code.DexFilledNewArray;
import com.android.tools.r8.dex.code.DexGoto;
+import com.android.tools.r8.dex.code.DexIfEq;
import com.android.tools.r8.dex.code.DexInstanceOf;
import com.android.tools.r8.dex.code.DexInvokeCustom;
+import com.android.tools.r8.dex.code.DexInvokeVirtual;
import com.android.tools.r8.dex.code.DexMonitorEnter;
import com.android.tools.r8.dex.code.DexMonitorExit;
import com.android.tools.r8.dex.code.DexMove;
@@ -36,6 +38,7 @@
import com.android.tools.r8.dex.code.DexThrow;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
public class LirSizeEstimation<EV> extends LirParsedInstructionCallback<EV> implements LirOpcodes {
@@ -66,13 +69,29 @@
sizeEstimate += instructionSize(view.getOpcode(), view);
}
+ private static int baseSwitchSize(int cases) {
+ return DexPackedSwitch.SIZE + DexPackedSwitchPayload.SIZE + ((2 + 2) * cases);
+ }
+
@Override
public void onIntSwitch(EV unusedValue, IntSwitchPayload payload) {
+ sizeEstimate += baseSwitchSize(payload.keys.length);
+ }
+
+ @Override
+ public void onStringSwitch(EV value, StringSwitchPayload payload) {
+ // Invoke hashcode and switch on it.
+ sizeEstimate += DexInvokeVirtual.SIZE + baseSwitchSize(payload.keys.length);
+ // Each case has an additional const-string, invoke equals, if check, cont-number and goto.
sizeEstimate +=
- DexPackedSwitch.SIZE
- + DexPackedSwitchPayload.SIZE
- + (2 * payload.keys.length)
- + (2 * payload.targets.length);
+ payload.keys.length
+ * (DexConstString.SIZE
+ + DexInvokeVirtual.SIZE
+ + DexIfEq.SIZE
+ + DexConst4.SIZE
+ + DexGoto.SIZE);
+ // Finally the id is then the subject of another switch.
+ sizeEstimate += baseSwitchSize(payload.keys.length);
}
@Override
@@ -83,6 +102,7 @@
private int instructionSize(int opcode, LirInstructionView view) {
switch (opcode) {
case TABLESWITCH:
+ case STRINGSWITCH:
case NEWARRAYFILLEDDATA:
// The payload instructions use the "parsed callback" to compute the payloads.
super.onInstructionView(view);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirUseRegistryCallback.java b/src/main/java/com/android/tools/r8/lightir/LirUseRegistryCallback.java
index 11918fa..3b43c56 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirUseRegistryCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirUseRegistryCallback.java
@@ -4,16 +4,20 @@
package com.android.tools.r8.lightir;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
+import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import java.util.List;
@@ -38,6 +42,71 @@
}
@Override
+ public void onInstruction() {
+ // TODO(b/225838009): Consider defining an abstract base class for the parsed exception so that
+ // missing a callback is a compile-time error.
+ boolean debug = false;
+ if (debug) {
+ throw new Unimplemented();
+ }
+ }
+
+ @Override
+ public void onFallthrough() {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onConstString(DexString string) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onThrow(EV exception) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onMoveException(DexType exceptionType) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onReturnVoid() {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onReturn(EV value) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onGoto(int blockIndex) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onPhi(DexType type, List<EV> operands) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onConstNumber(NumericType type, long value) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onCmpInstruction(int opcode, EV leftValue, EV rightValue) {
+ // Nothing to register.
+ }
+
+ @Override
+ public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
+ // Nothing to register.
+ }
+
+ @Override
public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) {
registry.registerCheckCast(type, ignoreCompatRules);
}
@@ -156,4 +225,9 @@
public void onNewUnboxedEnumInstance(DexType type, int ordinal) {
registry.registerNewUnboxedEnumInstance(type);
}
+
+ @Override
+ public void onRecordFieldValues(DexField[] fields, List<EV> values) {
+ registry.registerRecordFieldValues(fields);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 04e7817..e4fc96d 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -858,8 +858,13 @@
Comparator.comparing(mapping -> mapping.getOutline().toString()));
IntBox firstAvailableRange = new IntBox(lastComposedRange.minifiedRange.to + 1);
for (OutlineCallsiteMappingInformation outlineCallSite : outlineCallSites) {
- for (ComputedMappedRangeForOutline computedMappedRangeForOutline :
- outlineCallsitesToPatchUp.get(outlineCallSite)) {
+ // It should be sufficient to patch any call-site because they are referencing the same
+ // outline call-site information due to using an identity hashmap.
+ List<ComputedMappedRangeForOutline> computedMappedRangeForOutlines =
+ outlineCallsitesToPatchUp.get(outlineCallSite);
+ assert verifyAllOutlineCallSitesAreEqualTo(outlineCallSite, computedMappedRangeForOutlines);
+ ComputedMappedRangeForOutline computedMappedRangeForOutline =
+ ListUtils.first(computedMappedRangeForOutlines);
Int2IntSortedMap newPositionMap =
new Int2IntLinkedOpenHashMap(outlineCallSite.getPositions().size());
visitOutlineMappedPositions(
@@ -892,7 +897,19 @@
outlineCallSite.setPositionsInternal(newPositionMap);
});
}
+ }
+
+ private boolean verifyAllOutlineCallSitesAreEqualTo(
+ OutlineCallsiteMappingInformation outlineCallSite,
+ List<ComputedMappedRangeForOutline> computedMappedRangeForOutlines) {
+ for (ComputedMappedRangeForOutline computedMappedRangeForOutline :
+ computedMappedRangeForOutlines) {
+ for (OutlineCallsiteMappingInformation outlineCallsiteMappingInformation :
+ computedMappedRangeForOutline.composed.getOutlineCallsiteInformation()) {
+ assert outlineCallSite == outlineCallsiteMappingInformation;
+ }
}
+ return true;
}
/**
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 05fe545..7f17e72 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -92,6 +92,7 @@
public void decoupleIdentifierNameStringsInMethod(IRCode code) {
decoupleIdentifierNameStringsInBlocks(code, null);
+ assert code.isConsistentSSA(appView);
}
public void decoupleIdentifierNameStringsInBlocks(IRCode code, Set<BasicBlock> blocks) {
diff --git a/src/main/java/com/android/tools/r8/optimize/LegacyAccessModifier.java b/src/main/java/com/android/tools/r8/optimize/LegacyAccessModifier.java
new file mode 100644
index 0000000..c51b13c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/LegacyAccessModifier.java
@@ -0,0 +1,225 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import static com.android.tools.r8.dex.Constants.ACC_PRIVATE;
+import static com.android.tools.r8.dex.Constants.ACC_PROTECTED;
+import static com.android.tools.r8.dex.Constants.ACC_PUBLIC;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
+import com.android.tools.r8.ir.optimize.MethodPoolCollection;
+import com.android.tools.r8.optimize.PublicizerLens.PublicizedLensBuilder;
+import com.android.tools.r8.optimize.accessmodification.AccessModifierOptions;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public final class LegacyAccessModifier {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final SubtypingInfo subtypingInfo;
+ private final MethodPoolCollection methodPoolCollection;
+
+ private final PublicizedLensBuilder lensBuilder = PublicizerLens.createBuilder();
+
+ private LegacyAccessModifier(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ this.subtypingInfo = appView.appInfo().computeSubtypingInfo();
+ this.methodPoolCollection =
+ // We will add private instance methods when we promote them.
+ new MethodPoolCollection(
+ appView, subtypingInfo, MethodPoolCollection::excludesPrivateInstanceMethod);
+ }
+
+ /**
+ * Marks all package private and protected methods and fields as public. Makes all private static
+ * methods public. Makes private instance methods public final instance methods, if possible.
+ *
+ * <p>This will destructively update the DexApplication passed in as argument.
+ */
+ public static void run(
+ AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
+ throws ExecutionException {
+ AccessModifierOptions accessModifierOptions = appView.options().getAccessModifierOptions();
+ if (accessModifierOptions.isAccessModificationEnabled()
+ && accessModifierOptions.isLegacyAccessModifierEnabled()) {
+ timing.begin("Access modification");
+ new LegacyAccessModifier(appView).internalRun(executorService, timing);
+ timing.end();
+ if (appView.graphLens().isPublicizerLens()) {
+ // We can now remove redundant bridges. Note that we do not need to update the
+ // invoke-targets here, as the existing invokes will simply dispatch to the now
+ // visible super-method. MemberRebinding, if run, will then dispatch it correctly.
+ new RedundantBridgeRemover(appView.withLiveness()).run(executorService, timing);
+ }
+ }
+ }
+
+ private void internalRun(ExecutorService executorService, Timing timing)
+ throws ExecutionException {
+ // Phase 1: Collect methods to check if private instance methods don't have conflicts.
+ methodPoolCollection.buildAll(executorService, timing);
+
+ // Phase 2: Visit classes and promote class/member to public if possible.
+ timing.begin("Phase 2: promoteToPublic");
+ appView.appInfo().forEachReachableInterface(clazz -> processType(clazz.getType()));
+ processType(appView.dexItemFactory().objectType);
+ timing.end();
+
+ PublicizerLens publicizerLens = lensBuilder.build(appView);
+ if (publicizerLens != null) {
+ appView.setGraphLens(publicizerLens);
+ }
+
+ appView.notifyOptimizationFinishedForTesting();
+ }
+
+ private void doPublicize(ProgramDefinition definition) {
+ definition.getAccessFlags().promoteToPublic();
+ }
+
+ private void processType(DexType type) {
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
+ if (clazz != null) {
+ processClass(clazz);
+ }
+ subtypingInfo.forAllImmediateExtendsSubtypes(type, this::processType);
+ }
+
+ private void processClass(DexProgramClass clazz) {
+ if (appView.appInfo().isAccessModificationAllowed(clazz)) {
+ doPublicize(clazz);
+ }
+
+ // Publicize fields.
+ clazz.forEachProgramField(this::processField);
+
+ // Publicize methods.
+ Set<DexEncodedMethod> privateInstanceMethods = new LinkedHashSet<>();
+ clazz.forEachProgramMethod(
+ method -> {
+ if (publicizeMethod(method)) {
+ privateInstanceMethods.add(method.getDefinition());
+ }
+ });
+ if (!privateInstanceMethods.isEmpty()) {
+ clazz.virtualizeMethods(privateInstanceMethods);
+ }
+
+ // Publicize inner class attribute.
+ InnerClassAttribute attr = clazz.getInnerClassAttributeForThisClass();
+ if (attr != null) {
+ int accessFlags = ((attr.getAccess() | ACC_PUBLIC) & ~ACC_PRIVATE) & ~ACC_PROTECTED;
+ clazz.replaceInnerClassAttributeForThisClass(
+ new InnerClassAttribute(
+ accessFlags, attr.getInner(), attr.getOuter(), attr.getInnerName()));
+ }
+ }
+
+ private void processField(ProgramField field) {
+ if (appView.appInfo().isAccessModificationAllowed(field)) {
+ publicizeField(field);
+ }
+ }
+
+ private void publicizeField(ProgramField field) {
+ FieldAccessFlags flags = field.getAccessFlags();
+ if (!flags.isPublic()) {
+ flags.promoteToPublic();
+ }
+ }
+
+ private boolean publicizeMethod(ProgramMethod method) {
+ MethodAccessFlags accessFlags = method.getAccessFlags();
+ if (accessFlags.isPublic()) {
+ return false;
+ }
+ // If this method is mentioned in keep rules, do not transform (rule applications changed).
+ DexEncodedMethod definition = method.getDefinition();
+ if (!appView.appInfo().isAccessModificationAllowed(method)) {
+ // TODO(b/131130038): Also do not publicize package-private and protected methods that are
+ // kept.
+ if (definition.isPrivate()) {
+ return false;
+ }
+ }
+
+ if (method.getDefinition().isInstanceInitializer() || accessFlags.isProtected()) {
+ doPublicize(method);
+ return false;
+ }
+
+ if (accessFlags.isPackagePrivate()) {
+ // If we publicize a package private method we have to ensure there is no overrides of it. We
+ // could potentially publicize a method if it only has package-private overrides.
+ // TODO(b/182136236): See if we can break the hierarchy for clusters.
+ MemberPool<DexMethod> memberPool = methodPoolCollection.get(method.getHolder());
+ Wrapper<DexMethod> methodKey = MethodSignatureEquivalence.get().wrap(method.getReference());
+ if (memberPool.below(
+ methodKey,
+ false,
+ true,
+ (clazz, ignored) ->
+ !method.getContextType().getPackageName().equals(clazz.getType().getPackageName()))) {
+ return false;
+ }
+ doPublicize(method);
+ return false;
+ }
+
+ assert accessFlags.isPrivate();
+
+ if (accessFlags.isStatic()) {
+ // For private static methods we can just relax the access to public, since
+ // even though JLS prevents from declaring static method in derived class if
+ // an instance method with same signature exists in superclass, JVM actually
+ // does not take into account access of the static methods.
+ doPublicize(method);
+ return false;
+ }
+
+ // We can't publicize private instance methods in interfaces or methods that are copied from
+ // interfaces to lambda-desugared classes because this will be added as a new default method.
+ // TODO(b/111118390): It might be possible to transform it into static methods, though.
+ if (method.getHolder().isInterface() || accessFlags.isSynthetic()) {
+ return false;
+ }
+
+ boolean wasSeen = methodPoolCollection.markIfNotSeen(method.getHolder(), method.getReference());
+ if (wasSeen) {
+ // We can't do anything further because even renaming is not allowed due to the keep rule.
+ if (!appView.appInfo().isMinificationAllowed(method)) {
+ return false;
+ }
+ // TODO(b/111118390): Renaming will enable more private instance methods to be publicized.
+ return false;
+ }
+ lensBuilder.add(method.getReference());
+ accessFlags.promoteToFinal();
+ doPublicize(method);
+ // The method just became public and is therefore not a library override.
+ definition.setLibraryMethodOverride(OptionalBool.FALSE);
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
index 6304490..b388d09 100644
--- a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
+++ b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
@@ -61,7 +61,9 @@
AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
throws ExecutionException {
timing.begin("Access modification");
- if (appView.options().getAccessModifierOptions().isAccessModificationEnabled()) {
+ AccessModifierOptions accessModifierOptions = appView.options().getAccessModifierOptions();
+ if (accessModifierOptions.isAccessModificationEnabled()
+ && !accessModifierOptions.isLegacyAccessModifierEnabled()) {
new AccessModifier(appView)
.processStronglyConnectedComponents(executorService)
.installLens(executorService, timing);
@@ -165,23 +167,35 @@
return commitMethod(method, localNamingState, namingState);
}
+ if (method.getDefinition().isClassInitializer()) {
+ return commitMethod(method, localNamingState, namingState);
+ }
+
if (method.getDefinition().isInstanceInitializer()
|| (accessFlags.isPackagePrivate()
&& !traversalState.hasIllegalOverrideOfPackagePrivateMethod(method))
|| accessFlags.isProtected()) {
- method.getAccessFlags().promoteToPublic();
+ accessFlags.promoteToPublic();
return commitMethod(method, localNamingState, namingState);
}
if (accessFlags.isPrivate()) {
if (isRenamingAllowed(method)) {
- method.getAccessFlags().promoteToPublic();
+ accessFlags
+ .promoteToPublic()
+ .applyIf(
+ !method.getHolder().isInterface() && accessFlags.belongsToVirtualPool(),
+ MethodAccessFlags::promoteToFinal);
return commitMethod(method, localNamingState, namingState);
}
assert localNamingState.containsKey(method.getReference());
assert localNamingState.get(method.getReference()) == method.getReference();
if (namingState.isFree(method.getMethodSignature())) {
- method.getAccessFlags().promoteToPublic();
+ accessFlags
+ .promoteToPublic()
+ .applyIf(
+ !method.getHolder().isInterface() && accessFlags.belongsToVirtualPool(),
+ MethodAccessFlags::promoteToFinal);
namingState.addBlockedMethodSignature(method.getMethodSignature());
}
return commitMethod(method, method.getReference());
diff --git a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierOptions.java b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierOptions.java
index 99a0645..81c1f03 100644
--- a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierOptions.java
+++ b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierOptions.java
@@ -5,9 +5,14 @@
package com.android.tools.r8.optimize.accessmodification;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SystemPropertyUtils;
public class AccessModifierOptions {
+ private boolean enableLegacyAccessModifier =
+ SystemPropertyUtils.parseSystemPropertyOrDefault(
+ "com.android.tools.r8.accessmodification.legacy", false);
+
// TODO(b/131130038): Do not allow accessmodification when kept.
private boolean forceModifyPackagePrivateAndProtectedMethods = true;
@@ -22,14 +27,16 @@
}
public boolean isAccessModificationEnabled() {
- // TODO(b/288062771): Enable access modification for L8.
- if (!options.synthesizedClassPrefix.isEmpty()) {
+ if (isAccessModificationRulePresent()) {
+ return true;
+ }
+ if (isLegacyAccessModifierEnabled()) {
return false;
}
- if (options.forceProguardCompatibility) {
- return isAccessModificationRulePresent();
- }
- return true;
+ // TODO(b/288062771): Enable access modification by default for L8.
+ return options.synthesizedClassPrefix.isEmpty()
+ && !options.forceProguardCompatibility
+ && options.isOptimizing();
}
private boolean isAccessModificationRulePresent() {
@@ -46,4 +53,8 @@
this.forceModifyPackagePrivateAndProtectedMethods =
forceModifyPackagePrivateAndProtectedMethods;
}
+
+ public boolean isLegacyAccessModifierEnabled() {
+ return enableLegacyAccessModifier;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
index c76e842..91ec959 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
@@ -7,7 +7,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.Argument;
@@ -17,12 +18,11 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
public class ArgumentPropagatorIROptimizer {
@@ -39,7 +39,7 @@
AppView<AppInfoWithLiveness> appView,
IRCode code,
ConcreteCallSiteOptimizationInfo optimizationInfo) {
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
List<Assume> assumeInstructions = new LinkedList<>();
List<Instruction> instructionsToAdd = new LinkedList<>();
InstructionListIterator iterator = code.entryBlock().listIterator(code);
@@ -66,8 +66,7 @@
Instruction replacement =
singleValue.createMaterializingInstruction(appView, code, argument);
replacement.setPosition(argument.getPosition());
- affectedValues.addAll(argumentValue.affectedValues());
- argumentValue.replaceUsers(replacement.outValue());
+ argumentValue.replaceUsers(replacement.outValue(), affectedValues);
instructionsToAdd.add(replacement);
continue;
}
@@ -89,8 +88,7 @@
if (dynamicType.getNullability().isDefinitelyNull()) {
ConstNumber nullInstruction = code.createConstNull();
nullInstruction.setPosition(argument.getPosition());
- affectedValues.addAll(argumentValue.affectedValues());
- argumentValue.replaceUsers(nullInstruction.outValue());
+ argumentValue.replaceUsers(nullInstruction.outValue(), affectedValues);
instructionsToAdd.add(nullInstruction);
continue;
}
@@ -100,8 +98,13 @@
code.createValue(argumentValue.getType().asReferenceType().asMeetWithNotNull());
argumentValue.replaceUsers(nonNullValue, affectedValues);
Assume assumeNotNull =
- Assume.createAssumeNonNullInstruction(
- nonNullValue, argumentValue, argument, appView);
+ Assume.create(
+ DynamicType.definitelyNotNull(),
+ nonNullValue,
+ argumentValue,
+ argument,
+ appView,
+ code.context());
assumeNotNull.setPosition(argument.getPosition());
assumeInstructions.add(assumeNotNull);
}
@@ -109,33 +112,44 @@
}
DynamicTypeWithUpperBound dynamicTypeWithUpperBound =
dynamicType.asDynamicTypeWithUpperBound();
- Value specializedArg;
if (dynamicTypeWithUpperBound.strictlyLessThan(argumentValue.getType(), appView)) {
- specializedArg = code.createValue(argumentValue.getType());
- affectedValues.addAll(argumentValue.affectedValues());
- argumentValue.replaceUsers(specializedArg);
+ TypeElement specializedArgumentType =
+ argumentValue
+ .getType()
+ .asReferenceType()
+ .getOrCreateVariant(
+ dynamicType.getNullability().isDefinitelyNotNull()
+ ? Nullability.definitelyNotNull()
+ : argumentValue.getType().nullability());
+ Value specializedArg = code.createValue(specializedArgumentType);
+ argumentValue.replaceUsers(specializedArg, affectedValues);
Assume assumeType =
- Assume.createAssumeDynamicTypeInstruction(
- dynamicTypeWithUpperBound, specializedArg, argumentValue, argument, appView);
+ Assume.create(
+ dynamicTypeWithUpperBound,
+ specializedArg,
+ argumentValue,
+ argument,
+ appView,
+ code.context());
assumeType.setPosition(argument.getPosition());
assumeInstructions.add(assumeType);
- } else {
- specializedArg = argumentValue;
+ continue;
}
- assert specializedArg != null && specializedArg.getType().isReferenceType();
- if (dynamicType.getNullability().isDefinitelyNotNull()) {
- // If we already knew `arg` is never null, e.g., receiver, skip adding non-null.
- if (!specializedArg.getType().isDefinitelyNotNull()) {
- Value nonNullArg =
- code.createValue(specializedArg.getType().asReferenceType().asMeetWithNotNull());
- affectedValues.addAll(specializedArg.affectedValues());
- specializedArg.replaceUsers(nonNullArg);
- Assume assumeNotNull =
- Assume.createAssumeNonNullInstruction(
- nonNullArg, specializedArg, argument, appView);
- assumeNotNull.setPosition(argument.getPosition());
- assumeInstructions.add(assumeNotNull);
- }
+ if (dynamicType.getNullability().isDefinitelyNotNull()
+ && argumentValue.getType().isNullable()) {
+ Value nonNullArg =
+ code.createValue(argumentValue.getType().asReferenceType().asMeetWithNotNull());
+ argumentValue.replaceUsers(nonNullArg, affectedValues);
+ Assume assumeNotNull =
+ Assume.create(
+ DynamicType.definitelyNotNull(),
+ nonNullArg,
+ argumentValue,
+ argument,
+ appView,
+ code.context());
+ assumeNotNull.setPosition(argument.getPosition());
+ assumeInstructions.add(assumeNotNull);
}
}
}
@@ -145,12 +159,9 @@
iterator.previous();
assert iterator.peekPrevious().isArgument();
assumeInstructions.forEach(iterator::add);
-
- // TODO(b/190154391): Can update method signature and save more on call sites.
instructionsToAdd.forEach(iterator::add);
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
+ assert code.isConsistentSSA(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index ae0133f..4a737f4 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.bridge.VirtualBridgeInfo;
+import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
@@ -326,6 +327,9 @@
private Code createCodeForVirtualBridge(ProgramMethod representative, DexMethod methodToInvoke) {
Code code = representative.getDefinition().getCode();
+ if (code.isLirCode()) {
+ return createLirCodeForVirtualBridge(code.asLirCode(), methodToInvoke);
+ }
if (code.isCfCode()) {
return createCfCodeForVirtualBridge(code.asCfCode(), methodToInvoke);
}
@@ -335,6 +339,18 @@
throw new Unreachable("Unexpected code object of type " + code.getClass().getTypeName());
}
+ private LirCode<Integer> createLirCodeForVirtualBridge(
+ LirCode<Integer> code, DexMethod methodToInvoke) {
+ return code.newCodeWithRewrittenConstantPool(
+ item -> {
+ if (item instanceof DexMethod) {
+ assert methodToInvoke.match((DexMethod) item);
+ return methodToInvoke;
+ }
+ return item;
+ });
+ }
+
private CfCode createCfCodeForVirtualBridge(CfCode code, DexMethod methodToInvoke) {
List<CfInstruction> newInstructions = new ArrayList<>();
boolean modified = false;
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 5b459c3..8f6b03f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -497,18 +497,21 @@
@Override
public void notifyHorizontalClassMergerFinished(
HorizontalClassMerger.Mode horizontalClassMergerMode) {
- if (horizontalClassMergerMode.isInitial()) {
- methodAccessInfoCollection.destroy();
+ if (horizontalClassMergerMode.isInitial()
+ && !options().getAccessModifierOptions().isLegacyAccessModifierEnabled()) {
+ getMethodAccessInfoCollection().destroy();
}
}
public void notifyMemberRebindingFinished(AppView<AppInfoWithLiveness> appView) {
getFieldAccessInfoCollection().restrictToProgram(appView);
- getMethodAccessInfoCollection().destroyNonDirectNonSuperInvokes();
+ if (!options().getAccessModifierOptions().isLegacyAccessModifierEnabled()) {
+ getMethodAccessInfoCollection().destroyNonDirectNonSuperInvokes();
+ }
}
public void notifyRedundantBridgeRemoverFinished(boolean initial) {
- if (initial) {
+ if (initial && !options().getAccessModifierOptions().isLegacyAccessModifierEnabled()) {
getMethodAccessInfoCollection().destroySuperInvokes();
}
}
@@ -516,7 +519,9 @@
@Override
public void notifyMinifierFinished() {
liveMethods = ThrowingSet.get();
- methodAccessInfoCollection.destroy();
+ if (!options().getAccessModifierOptions().isLegacyAccessModifierEnabled()) {
+ getMethodAccessInfoCollection().destroy();
+ }
}
public void notifyTreePrunerFinished(Enqueuer.Mode mode) {
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index adbf8d0..22d9468 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -22,8 +22,6 @@
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.IRFinalizer;
-import com.android.tools.r8.ir.conversion.IRToCfFinalizer;
-import com.android.tools.r8.ir.conversion.IRToDexFinalizer;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.ir.conversion.passes.ThrowCatchOptimizer;
@@ -266,7 +264,7 @@
MutableMethodConversionOptions conversionOptions =
mode.isInitialTreeShaking()
? MethodConversionOptions.forPreLirPhase(appView)
- : MethodConversionOptions.forPostLirPhase(appView);
+ : MethodConversionOptions.forLirPhase(appView);
conversionOptions.disableStringSwitchConversion();
IRCode ir = method.buildIR(appView, conversionOptions);
@@ -278,11 +276,9 @@
new ThrowCatchOptimizer(appView).run(ir, Timing.empty());
rewriter.getDeadCodeRemover().run(ir, Timing.empty());
- // Finalize to class files or dex.
+ // Finalize out of IR.
IRFinalizer<?> finalizer =
- conversionOptions.isGeneratingClassFiles()
- ? new IRToCfFinalizer(appView, rewriter.getDeadCodeRemover())
- : new IRToDexFinalizer(appView, rewriter.getDeadCodeRemover());
+ conversionOptions.getFinalizer(rewriter.getDeadCodeRemover(), appView);
Code newCode = finalizer.finalizeCode(ir, BytecodeMetadataProvider.empty(), Timing.empty());
method.setCode(newCode, appView);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingRewriter.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingRewriter.java
index 7d25797..b0b905a 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingRewriter.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingRewriter.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-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.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
@@ -31,10 +30,10 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.AffectedValues;
import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.DeadCodeRemover;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Sets;
import java.util.Map;
import java.util.Set;
@@ -67,7 +66,7 @@
ProgramMethod context = code.context();
// Rewrite field instructions that reference a pruned field.
- Set<Value> affectedValues = Sets.newIdentityHashSet();
+ AffectedValues affectedValues = new AffectedValues();
BasicBlockIterator blockIterator = code.listIterator();
boolean hasChanged = false;
while (blockIterator.hasNext()) {
@@ -115,9 +114,7 @@
}
}
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ affectedValues.narrowingWithAssumeRemoval(appView, code);
if (hasChanged) {
code.removeRedundantBlocks();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 4ad6ef2..380efdb 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1683,6 +1683,8 @@
clazz.getMethodCollection().replaceMethods(this::fixupMethod);
clazz.setStaticFields(fixupFields(clazz.staticFields()));
clazz.setInstanceFields(fixupFields(clazz.instanceFields()));
+ clazz.setPermittedSubclassAttributes(
+ fixupPermittedSubclassAttribute(clazz.getPermittedSubclassAttributes()));
}
for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
synthesizedBridge.updateMethodSignatures(this::fixupMethodReference);
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 d49b405..41c295f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -57,6 +57,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
@@ -65,6 +66,7 @@
import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.desugar.TypeRewriter.MachineTypeRewriter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
@@ -73,6 +75,9 @@
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
+import com.android.tools.r8.lightir.IR2LirConverter;
+import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.lightir.LirStrategy;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MapConsumer;
import com.android.tools.r8.naming.MapVersion;
@@ -119,6 +124,8 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -2104,6 +2111,7 @@
public boolean roundtripThroughLir = false;
private boolean useLir = System.getProperty("com.android.tools.r8.nolir") == null;
+ private boolean convertLir = System.getProperty("com.android.tools.r8.convertlir") == null;
public void enableLir() {
useLir = true;
@@ -2127,9 +2135,42 @@
private LirPhase currentPhase = LirPhase.PRE;
- public void enterLirSupportedPhase() {
+ public void enterLirSupportedPhase(AppView<?> appView, ExecutorService executorService)
+ throws ExecutionException {
assert isPreLirPhase();
currentPhase = LirPhase.SUPPORTED;
+ if (!canUseLir(appView) || !convertLir) {
+ return;
+ }
+ // Convert code objects to LIR.
+ ThreadUtils.processItems(
+ appView.appInfo().classes(),
+ clazz -> {
+ // TODO(b/225838009): Also convert instance initializers to LIR, by adding support for
+ // computing the inlining constraint for LIR and using that in the class mergers, and
+ // class initializers, by updating the concatenation of clinits in horizontal class
+ // merging.
+ clazz.forEachProgramMethodMatching(
+ method ->
+ method.hasCode()
+ && !method.isInitializer()
+ && !appView.isCfByteCodePassThrough(method),
+ method -> {
+ IRCode code =
+ method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
+ LirCode<Integer> lirCode =
+ IR2LirConverter.translate(
+ code,
+ BytecodeMetadataProvider.empty(),
+ LirStrategy.getDefaultStrategy().getEncodingStrategy(),
+ appView.options());
+ method.setCode(lirCode, appView);
+ });
+ },
+ executorService);
+ // Conversion to LIR via IR will allocate type elements.
+ // They are not needed after construction so remove them again.
+ appView.dexItemFactory().clearTypeElementsCache();
}
public void exitLirSupportedPhase() {
@@ -2137,6 +2178,11 @@
currentPhase = LirPhase.POST;
}
+ public void skipLirPhasesForTestingFinalOutput() {
+ assert currentPhase == LirPhase.PRE;
+ currentPhase = LirPhase.POST;
+ }
+
public boolean isPreLirPhase() {
return currentPhase == LirPhase.PRE;
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 447399f..8ffd594 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -118,6 +118,7 @@
public static final String GENERATED_TEST_BUILD_DIR = BUILD_DIR + "generated/test/";
public static final String LIBS_DIR = BUILD_DIR + "libs/";
public static final String THIRD_PARTY_DIR = getProjectRoot() + "third_party/";
+ public static final String DEPENDENCIES = THIRD_PARTY_DIR + "dependencies/";
public static final String TOOLS_DIR = getProjectRoot() + "tools/";
public static final String TESTS_DIR = getProjectRoot() + "src/test/";
public static final String TESTS_SOURCE_DIR = TESTS_DIR + "java/";
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
index a27783e..f694cd7 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
@@ -14,8 +14,10 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
-import java.util.stream.IntStream;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -42,6 +44,7 @@
.addKeepMainRule(TestClass.class)
.addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
.setMinApi(parameters)
+ .addOptionsModification(o -> o.testing.enableLir())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
@@ -59,15 +62,15 @@
assertThat(inspector.clazz(ExceptionA.class), not(isPresent()));
assertThat(inspector.clazz(Exception1.class), not(isPresent()));
- // Check that the second catch handler has been removed.
+ // Check that only two exception guard types remain.
MethodSubject mainMethodSubject = inspector.clazz(TestClass.class).mainMethod();
assertThat(mainMethodSubject, isPresent());
- assertEquals(
- 2,
+ Set<TypeSubject> guards =
mainMethodSubject
.streamTryCatches()
- .flatMapToInt(x -> IntStream.of(x.getNumberOfHandlers()))
- .sum());
+ .flatMap(tryCatch -> tryCatch.guards().stream())
+ .collect(Collectors.toSet());
+ assertEquals(2, guards.size());
}
public static class TestClass {
@@ -89,11 +92,15 @@
}
private static void doSomethingThatMightThrowExceptionB() throws ExceptionB {
- throw new ExceptionB("Ouch!");
+ if (System.nanoTime() > 0) {
+ throw new ExceptionB("Ouch!");
+ }
}
private static void doSomethingThatMightThrowException2() throws Exception2 {
- throw new Exception2("Ouch!");
+ if (System.nanoTime() > 0) {
+ throw new Exception2("Ouch!");
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
index ddeddc0..3ac00b5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11AtomicTests.java
@@ -4,8 +4,9 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getPathsFiles;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getTestNGMainRunner;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGPath;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGSupportProgramFiles;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8SHRINK;
@@ -27,7 +28,6 @@
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.Collections;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -79,8 +79,7 @@
public static void compileAtomicClasses() throws Exception {
ATOMIC_COMPILED_TESTS_FOLDER = getStaticTemp().newFolder("atomic").toPath();
javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
- .addClasspathFiles(
- Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
+ .addClasspathFiles(testNGPath())
.addSourceFiles(ATOMIC_TESTS_FILES)
.setOutputPath(ATOMIC_COMPILED_TESTS_FOLDER)
.compile();
@@ -93,6 +92,7 @@
.addProgramFiles(
ATOMIC_COMPILED_TESTS_FOLDER.resolve(ATOMIC_REFERENCE_TEST + CLASS_EXTENSION))
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.applyIf(
!libraryDesugaringSpecification.hasNioFileDesugaring(parameters),
b -> b.addProgramFiles(getPathsFiles()))
@@ -110,6 +110,7 @@
.addProgramFiles(
getAllFilesWithSuffixInDirectory(ATOMIC_COMPILED_TESTS_FOLDER, CLASS_EXTENSION))
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.applyIf(
!libraryDesugaringSpecification.hasNioFileDesugaring(parameters),
b -> b.addProgramFiles(getPathsFiles()))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java
index d8bc956..87478e0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentLinkedQueueTests.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getTestNGMainRunner;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGPath;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGSupportProgramFiles;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8SHRINK;
@@ -40,7 +41,6 @@
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.Collections;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Ignore;
@@ -92,8 +92,7 @@
Path jdk11ConcurrentLinkedQueueTestsDir =
getStaticTemp().newFolder("ConcurrentLinkedQueue").toPath();
javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
- .addClasspathFiles(
- Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
+ .addClasspathFiles(testNGPath())
.addSourceFiles(JDK_11_CONCURRENT_LINKED_QUEUE_JAVA_FILES)
.setOutputPath(jdk11ConcurrentLinkedQueueTestsDir)
.compile();
@@ -182,6 +181,7 @@
parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramFiles(JDK_11_CONCURRENT_LINKED_QUEUE_TEST_CLASS_FILES)
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
// The WhiteBox test is using VarHandle and MethodHandles.privateLookupIn to inspect the
// internal state of the implementation, so desugaring is needed for the program here.
.addOptionsModification(options -> options.enableVarHandleDesugaring = true)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
index f4b8b17..c27bb8b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11ConcurrentMapTests.java
@@ -4,8 +4,9 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getPathsFiles;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getTestNGMainRunner;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGPath;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGSupportProgramFiles;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8SHRINK;
@@ -82,8 +83,7 @@
public static void compileConcurrentClasses() throws Exception {
Path concurrentCompiledTestsFolder = getStaticTemp().newFolder("concurrentmap").toPath();
javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
- .addClasspathFiles(
- Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
+ .addClasspathFiles(testNGPath())
.addSourceFiles(getAllFilesWithSuffixInDirectory(CONCURRENT_TESTS_FOLDER, JAVA_EXTENSION))
.setOutputPath(concurrentCompiledTestsFolder)
.compile();
@@ -100,8 +100,7 @@
Path concurrentHashCompiledTestsFolder =
getStaticTemp().newFolder("concurrenthashmap").toPath();
javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
- .addClasspathFiles(
- Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
+ .addClasspathFiles(testNGPath())
.addSourceFiles(classesToCompile)
.setOutputPath(concurrentHashCompiledTestsFolder)
.compile();
@@ -118,6 +117,7 @@
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramFiles(CONCURRENT_COMPILED_TESTS_FILES)
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.applyIf(
!libraryDesugaringSpecification.hasNioFileDesugaring(parameters),
b -> b.addProgramFiles(getPathsFiles()))
@@ -168,6 +168,7 @@
parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramFiles(concurrentHashTestToCompile())
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.applyIf(
!libraryDesugaringSpecification.hasNioFileDesugaring(parameters),
b -> b.addProgramFiles(getPathsFiles()))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
index 0f735fe..ed6b91a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getTestNGMainRunner;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGPath;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGSupportProgramFiles;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11TestLibraryDesugaringSpecification.EXTENSION_PATH;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11TestLibraryDesugaringSpecification.JDK11_PATH_JAVA_BASE_EXT;
@@ -262,7 +263,7 @@
Path tmpDirectory = getStaticTemp().newFolder(name).toPath();
List<Path> classpath = new ArrayList<>();
classpath.add(EXTENSION_PATH);
- classpath.add(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar"));
+ classpath.add(testNGPath());
if (cp != null) {
classpath.add(cp);
}
@@ -284,6 +285,7 @@
.addProgramFiles(TEST_UTIL_JAR)
.addProgramClassFileData(TEST_PROGRAM_CLASS_DATA)
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.compile()
.withArt6Plus64BitsLib();
int success = 0;
@@ -341,6 +343,7 @@
.addProgramFiles(TEST_UTIL_JAR)
.addProgramClassFileData(TEST_PROGRAM_CLASS_DATA)
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
.compile()
.withArt6Plus64BitsLib();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamAbstractTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamAbstractTests.java
index 9ff2346..78b8776 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamAbstractTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11StreamAbstractTests.java
@@ -4,9 +4,10 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getPathsFiles;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getSafeVarArgsFile;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getTestNGMainRunner;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGPath;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGSupportProgramFiles;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11TestLibraryDesugaringSpecification.EXTENSION_PATH;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11TestLibraryDesugaringSpecification.JDK11_PATH_JAVA_BASE_EXT;
@@ -256,8 +257,7 @@
"java.base=" + EXTENSION_PATH);
javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
.addOptions(options)
- .addClasspathFiles(
- ImmutableList.of(EXTENSION_PATH, Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
+ .addClasspathFiles(testNGPath())
.addSourceFiles(getJdk11StreamTestFiles())
.setOutputPath(JDK_11_STREAM_TEST_CLASSES_DIR)
.compile();
@@ -297,6 +297,7 @@
b -> b.addProgramFiles(getPathsFiles()))
.addProgramFiles(getSafeVarArgsFile())
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
.disableL8AnnotationRemoval()
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11SupportFiles.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11SupportFiles.java
index d3507a2..6d99f62 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11SupportFiles.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11SupportFiles.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
+import static com.android.tools.r8.TestBase.descriptor;
+import static com.android.tools.r8.TestBase.transformer;
import com.android.tools.r8.ToolHelper;
import java.nio.file.Path;
@@ -16,10 +17,11 @@
public class Jdk11SupportFiles {
+ // TODO(b/289346278): See if we can remove the xxx subfolder.
private static final Path ANDROID_PATHS_FILES_DIR =
- Paths.get("third_party/android_jar/lib-v26/xxx/");
+ Paths.get(ToolHelper.THIRD_PARTY_DIR + "android_jar/lib-v26/xxx/");
private static final Path ANDROID_SAFE_VAR_ARGS_LOCATION =
- Paths.get("third_party/android_jar/lib-v26/java/lang/SafeVarargs.class");
+ Paths.get(ToolHelper.THIRD_PARTY_DIR + "android_jar/lib-v26/java/lang/SafeVarargs.class");
private static final Path[] ANDROID_PATHS_FILES =
new Path[] {
Paths.get("java/nio/file/Files.class"),
@@ -40,10 +42,79 @@
}
public static Path[] testNGSupportProgramFiles() {
- return new Path[] {
- Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar"),
- Paths.get(JDK_TESTS_BUILD_DIR + "jcommander-1.48.jar"),
- Paths.get(ToolHelper.JAVA_CLASSES_DIR + "examplesTestNGRunner/TestNGMainRunner.class")
- };
+ return new Path[] {testNGPath(), jcommanderPath()};
+ }
+
+ public static Path testNGPath() {
+ return Paths.get(ToolHelper.DEPENDENCIES + "org/testng/testng/6.10/testng-6.10.jar");
+ }
+
+ public static Path jcommanderPath() {
+ return Paths.get(ToolHelper.DEPENDENCIES + "com/beust/jcommander/1.48/jcommander-1.48.jar");
+ }
+
+ public static byte[] getTestNGMainRunner() throws Exception {
+ return transformer(TestNGMainRunner.class)
+ .setClassDescriptor("LTestNGMainRunner;")
+ .replaceClassDescriptorInMethodInstructions(descriptor(TestNG.class), "Lorg/testng/TestNG;")
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(TextReporter.class), "Lorg/testng/reporters/TextReporter;")
+ .transform();
+ }
+
+ /** TestNGMainRunner used as the test runner in JDK11 tests. */
+ public static class TestNGMainRunner {
+
+ private static void runTestNg(Class<?> testClass, int verbose) {
+ System.out.println("Running tests in " + testClass.getName());
+ TestNG testng = new TestNG(false);
+ testng.setTestClasses(new Class<?>[] {testClass});
+ testng.setVerbose(verbose);
+ // Deprecated API used because it works on Android unlike the recommended one.
+ testng.addListener(new TextReporter(testClass.getName(), verbose));
+ try {
+ testng.run();
+ System.out.print("Tests result in " + testClass.getName() + ": ");
+ if (testng.hasFailure()) {
+ System.out.println("FAILURE");
+ } else {
+ System.out.println("SUCCESS");
+ }
+ } catch (RuntimeException | Error e) {
+ System.out.print("Tests result in " + testClass.getName() + ": ");
+ System.out.println("ERROR");
+ e.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ // First arg is the verbosity level.
+ // Second arg is the class to run.
+ int verbose = Integer.parseInt(args[0]);
+ runTestNg(Class.forName(args[1]), verbose);
+ }
+ }
+
+ /** Stubs for the TestNGRunner */
+ public static class TextReporter {
+
+ public TextReporter(String name, int verbose) {}
+ }
+
+ public static class TestNG {
+
+ public TestNG(boolean val) {}
+
+ public void setTestClasses(Class<?>[] classes) {}
+
+ public void setVerbose(int verbose) {}
+
+ public void addListener(Object textReporter) {}
+
+ public void run() {}
+
+ public boolean hasFailure() {
+ return false;
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TestLibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TestLibraryDesugaringSpecification.java
index 7a2760a..f909be9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TestLibraryDesugaringSpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TestLibraryDesugaringSpecification.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.getAllFilesWithSuffixInDirectory;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGPath;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
@@ -98,8 +98,7 @@
"java.base=" + JDK_11_JAVA_BASE_EXTENSION_FILES_DIR);
JavaCompilerTool.create(TestRuntime.getCheckedInJdk11(), folder)
.addOptions(options)
- .addClasspathFiles(
- Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
+ .addClasspathFiles(testNGPath())
.addSourceFiles(getExtensionsFiles())
.setOutputPath(output)
.compile();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeAbstractTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeAbstractTests.java
index a2c95c1..f59341e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeAbstractTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11TimeAbstractTests.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.desugar.desugaredlibrary.jdktests;
-import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getPathsFiles;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.getTestNGMainRunner;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.jcommanderPath;
+import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGPath;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11SupportFiles.testNGSupportProgramFiles;
import static com.android.tools.r8.desugar.desugaredlibrary.jdktests.Jdk11TestLibraryDesugaringSpecification.EXTENSION_PATH;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
@@ -108,10 +110,7 @@
"java.base=" + EXTENSION_PATH);
javac(TestRuntime.getCheckedInJdk11(), getStaticTemp())
.addOptions(options)
- .addClasspathFiles(
- ImmutableList.of(
- Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar"),
- Paths.get(JDK_TESTS_BUILD_DIR + "jcommander-1.48.jar")))
+ .addClasspathFiles(testNGPath(), jcommanderPath())
.addSourceFiles(getJdk11TimeTestFiles())
.setOutputPath(tmpDirectory)
.compile();
@@ -261,6 +260,7 @@
parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramFiles(JDK_11_TIME_TEST_COMPILED_FILES)
.addProgramFiles(testNGSupportProgramFiles())
+ .addProgramClassFileData(getTestNGMainRunner())
.applyIf(
!libraryDesugaringSpecification.hasNioFileDesugaring(parameters),
b -> b.addProgramFiles(getPathsFiles()))
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
index 2cf1e98..c871859 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
@@ -38,6 +38,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -182,6 +183,10 @@
return this;
}
+ public DesugaredLibraryTestBuilder<T> addProgramClassFileData(byte[]... classes) {
+ return addProgramClassFileData(Arrays.asList(classes));
+ }
+
public DesugaredLibraryTestBuilder<T> addProgramClassFileData(
Collection<byte[]> programClassFileData) {
builder.addProgramClassFileData(programClassFileData);
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumImplementsTest.java
similarity index 61%
copy from src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
copy to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumImplementsTest.java
index 997e4fb..b462df9 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesEnumImplementsTest.java
@@ -5,12 +5,13 @@
package com.android.tools.r8.desugar.sealed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.NoUnusedInterfaceRemoval;
+import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestBuilder;
import com.android.tools.r8.TestParameters;
@@ -20,7 +21,6 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,7 +29,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedClassesTest extends TestBase {
+public class SealedClassesEnumImplementsTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
@@ -37,22 +37,18 @@
@Parameter(1)
public boolean keepPermittedSubclassesAttribute;
- @Parameter(2)
- public boolean repackage;
+ static final String EXPECTED = StringUtils.lines("0", "1", "true");
- static final String EXPECTED = StringUtils.lines("Success!");
-
- @Parameters(name = "{0}, keepPermittedSubclasses = {1}, repackage = {2}")
+ @Parameters(name = "{0}, keepPermittedSubclasses = {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
- BooleanUtils.values(),
BooleanUtils.values());
}
private void addTestClasses(TestBuilder<?, ?> builder) throws Exception {
builder
- .addProgramClasses(TestClass.class, Sub1.class, Sub2.class)
+ .addProgramClasses(TestClass.class, Enum.class)
.addProgramClassFileData(getTransformedClasses());
}
@@ -82,22 +78,9 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(Super.class);
- assertThat(clazz, isPresentAndRenamed());
- ClassSubject sub1 = inspector.clazz(Sub1.class);
- ClassSubject sub2 = inspector.clazz(Sub2.class);
- assertThat(sub1, isPresentAndRenamed());
- assertThat(sub2, isPresentAndRenamed());
- if (repackage) {
- assertEquals(-1, sub1.getFinalName().indexOf('.'));
- } else {
- assertTrue(sub1.getFinalName().startsWith(getClass().getPackage().getName()));
- }
- assertEquals(
- parameters.isCfRuntime() && keepPermittedSubclassesAttribute
- ? ImmutableList.of(sub1.asTypeSubject(), sub2.asTypeSubject())
- : ImmutableList.of(),
- clazz.getFinalPermittedSubclassAttributes());
+ ClassSubject iface1 = inspector.clazz(Iface.class);
+ assertThat(iface1, isPresentAndRenamed());
+ assertTrue(iface1.getFinalPermittedSubclassAttributes().isEmpty());
}
@Test
@@ -109,37 +92,42 @@
.applyIf(
keepPermittedSubclassesAttribute,
TestShrinkerBuilder::addKeepAttributePermittedSubclasses)
- // Keep the sealed class to ensure the PermittedSubclasses attribute stays live.
- .addKeepPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
+ // Keep the sealed interface to ensure the PermittedSubclasses attribute stays live.
+ .addKeepPermittedSubclasses(Iface.class)
.addKeepMainRule(TestClass.class)
- .applyIf(repackage, b -> b.addKeepRules("-repackageclasses"))
+ .addEnumUnboxingInspector(
+ inspector -> {
+ inspector.assertUnboxed(Enum.class);
+ })
+ .enableNoUnusedInterfaceRemovalAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
- .applyIf(
- !parameters.isCfRuntime() || parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
- r -> r.assertSuccessWithOutput(EXPECTED),
- r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
+ // The Iface interface which still has class file version 61 is never accessed, so no
+ // UnsupportedClassVersionError is thrown.
+ .assertSuccessWithOutput(EXPECTED);
}
public byte[] getTransformedClasses() throws Exception {
- return transformer(Super.class)
- .setPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
- .transform();
+ return transformer(Iface.class).setPermittedSubclasses(Iface.class, Enum.class).transform();
}
static class TestClass {
public static void main(String[] args) {
- new Sub1();
- new Sub2();
- System.out.println("Success!");
+ System.out.println(Enum.A.ordinal());
+ System.out.println(Enum.B.ordinal());
+ System.out.println(Enum.A instanceof Iface);
}
}
- public abstract static class Super /* permits Sub1, Sub2 */ {}
+ @NoUnusedInterfaceRemoval
+ @NoVerticalClassMerging
+ interface Iface /* permits Enum */ {}
- public static class Sub1 extends Super {}
-
- public static class Sub2 extends Super {}
+ enum Enum implements Iface {
+ A,
+ B
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesExtendsTest.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
rename to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesExtendsTest.java
index 997e4fb..08fde0f 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesExtendsTest.java
@@ -29,7 +29,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedClassesTest extends TestBase {
+public class SealedClassesExtendsTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesExtendsVerticalMergeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesExtendsVerticalMergeTest.java
index fdd28aa..c833f8a 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesExtendsVerticalMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesExtendsVerticalMergeTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static junit.framework.Assert.assertEquals;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
@@ -19,6 +18,7 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.google.common.collect.ImmutableList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,16 +45,21 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(Super.class);
- assertThat(clazz, isPresentAndRenamed());
+ ClassSubject superClass = inspector.clazz(Super.class);
+ assertThat(superClass, isPresentAndRenamed());
ClassSubject sub2 = inspector.clazz(Sub2.class);
assertThat(sub2, isPresentAndRenamed());
ClassSubject subSub = inspector.clazz(SubSub.class);
assertThat(subSub, isPresentAndRenamed());
- // TODO(b/227160052): Should be both subSub.asTypeSubject() and sub2.asTypeSubject().
- assertEquals(
- parameters.isCfRuntime() ? ImmutableList.of(sub2.asTypeSubject()) : ImmutableList.of(),
- clazz.getFinalPermittedSubclassAttributes());
+ ClassSubject unrelated = inspector.clazz(UnrelatedSuper.class);
+ assertThat(subSub, isPresentAndRenamed());
+ for (ClassSubject clazz : ImmutableList.of(superClass, unrelated)) {
+ assertEquals(
+ parameters.isCfRuntime()
+ ? ImmutableList.of(subSub.asTypeSubject(), sub2.asTypeSubject())
+ : ImmutableList.of(),
+ clazz.getFinalPermittedSubclassAttributes());
+ }
}
@Test
@@ -64,8 +69,7 @@
.apply(this::addTestClasses)
.setMinApi(parameters)
.addKeepAttributePermittedSubclasses()
- .addKeepPermittedSubclasses(Super.class)
- .addKeepPermittedSubclasses(Sub2.class)
+ .addKeepPermittedSubclasses(Super.class, Sub2.class, UnrelatedSuper.class)
.addKeepMainRule(TestClass.class)
.addVerticallyMergedClassesInspector(
inspector -> {
@@ -77,19 +81,19 @@
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
- parameters.isDexRuntime(),
+ parameters.isDexRuntime() || parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
r -> r.assertSuccessWithOutput(EXPECTED),
- parameters.isCfRuntime() && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
- r ->
- r.assertFailureWithErrorThatMatches(
- containsString("cannot inherit from sealed class")),
r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
}
- public byte[] getTransformedClasses() throws Exception {
- return transformer(Super.class)
- .setPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
- .transform();
+ public List<byte[]> getTransformedClasses() throws Exception {
+ return ImmutableList.of(
+ transformer(Super.class)
+ .setPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
+ .transform(),
+ transformer(UnrelatedSuper.class)
+ .setPermittedSubclasses(UnrelatedSuper.class, Sub1.class, Sub2.class)
+ .transform());
}
static class TestClass {
@@ -107,4 +111,6 @@
static class Sub2 extends Super {}
static class SubSub extends Sub1 {}
+
+ abstract static class UnrelatedSuper /* permits Sub1, Sub2 */ {}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesImplementsTest.java
similarity index 70%
copy from src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
copy to src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesImplementsTest.java
index 997e4fb..ac61cf8 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesImplementsTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -20,6 +19,8 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
@@ -29,7 +30,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class SealedClassesTest extends TestBase {
+public class SealedClassesImplementsTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
@@ -37,16 +38,12 @@
@Parameter(1)
public boolean keepPermittedSubclassesAttribute;
- @Parameter(2)
- public boolean repackage;
-
static final String EXPECTED = StringUtils.lines("Success!");
- @Parameters(name = "{0}, keepPermittedSubclasses = {1}, repackage = {2}")
+ @Parameters(name = "{0}, keepPermittedSubclasses = {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
- BooleanUtils.values(),
BooleanUtils.values());
}
@@ -82,22 +79,24 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(Super.class);
- assertThat(clazz, isPresentAndRenamed());
+ ClassSubject iface1 = inspector.clazz(Iface1.class);
+ assertThat(iface1, isPresentAndRenamed());
+ ClassSubject iface2 = inspector.clazz(Iface2.class);
+ assertThat(iface2, isPresentAndRenamed());
ClassSubject sub1 = inspector.clazz(Sub1.class);
ClassSubject sub2 = inspector.clazz(Sub2.class);
assertThat(sub1, isPresentAndRenamed());
assertThat(sub2, isPresentAndRenamed());
- if (repackage) {
- assertEquals(-1, sub1.getFinalName().indexOf('.'));
- } else {
- assertTrue(sub1.getFinalName().startsWith(getClass().getPackage().getName()));
- }
assertEquals(
parameters.isCfRuntime() && keepPermittedSubclassesAttribute
? ImmutableList.of(sub1.asTypeSubject(), sub2.asTypeSubject())
: ImmutableList.of(),
- clazz.getFinalPermittedSubclassAttributes());
+ iface1.getFinalPermittedSubclassAttributes());
+ assertEquals(
+ parameters.isCfRuntime() && keepPermittedSubclassesAttribute
+ ? ImmutableList.of(sub1.asTypeSubject(), sub2.asTypeSubject())
+ : ImmutableList.of(),
+ iface2.getFinalPermittedSubclassAttributes());
}
@Test
@@ -109,10 +108,13 @@
.applyIf(
keepPermittedSubclassesAttribute,
TestShrinkerBuilder::addKeepAttributePermittedSubclasses)
- // Keep the sealed class to ensure the PermittedSubclasses attribute stays live.
- .addKeepPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
+ // Keep the sealed interface to ensure the PermittedSubclasses attribute stays live.
+ .addKeepPermittedSubclasses(Iface1.class, Iface2.class, Sub1.class, Sub2.class)
.addKeepMainRule(TestClass.class)
- .applyIf(repackage, b -> b.addKeepRules("-repackageclasses"))
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .addVerticallyMergedClassesInspector(
+ VerticallyMergedClassesInspector::assertNoClassesMerged)
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
@@ -122,10 +124,14 @@
r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
}
- public byte[] getTransformedClasses() throws Exception {
- return transformer(Super.class)
- .setPermittedSubclasses(Super.class, Sub1.class, Sub2.class)
- .transform();
+ public List<byte[]> getTransformedClasses() throws Exception {
+ return ImmutableList.of(
+ transformer(Iface1.class)
+ .setPermittedSubclasses(Iface1.class, Sub1.class, Sub2.class)
+ .transform(),
+ transformer(Iface2.class)
+ .setPermittedSubclasses(Iface2.class, Sub1.class, Sub2.class)
+ .transform());
}
static class TestClass {
@@ -137,9 +143,11 @@
}
}
- public abstract static class Super /* permits Sub1, Sub2 */ {}
+ interface Iface1 /* permits Sub1, Sub2 */ {}
- public static class Sub1 extends Super {}
+ interface Iface2 /* permits Sub1, Sub2 */ {}
- public static class Sub2 extends Super {}
+ static class Sub1 implements Iface1, Iface2 {}
+
+ static class Sub2 implements Iface1 {}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesImplementsVerticalMergeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesImplementsVerticalMergeTest.java
index 43f4a41..3f218f0 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesImplementsVerticalMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesImplementsVerticalMergeTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static junit.framework.Assert.assertEquals;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
@@ -52,14 +51,17 @@
assertThat(iface1, isPresentAndRenamed());
ClassSubject iface2 = inspector.clazz(Iface2.class);
assertThat(iface2, isPresentAndRenamed());
+ ClassSubject ifaceUnrelated = inspector.clazz(IfaceUnrelated.class);
+ assertThat(ifaceUnrelated, isPresentAndRenamed());
ClassSubject sub2 = inspector.clazz(Sub2.class);
assertThat(sub2, isPresentAndRenamed());
ClassSubject subSub = inspector.clazz(SubSub.class);
assertThat(subSub, Matchers.isPresentAndRenamed());
- for (ClassSubject clazz : ImmutableList.of(iface1, iface2)) {
+ for (ClassSubject clazz : ImmutableList.of(iface1, iface2, ifaceUnrelated)) {
assertEquals(
- // TODO(b/227160052): Should be both subSub.asTypeSubject() and sub2.asTypeSubject().
- parameters.isCfRuntime() ? ImmutableList.of(sub2.asTypeSubject()) : ImmutableList.of(),
+ parameters.isCfRuntime()
+ ? ImmutableList.of(subSub.asTypeSubject(), sub2.asTypeSubject())
+ : ImmutableList.of(),
clazz.getFinalPermittedSubclassAttributes());
}
}
@@ -71,7 +73,8 @@
.apply(this::addTestClasses)
.setMinApi(parameters)
.addKeepAttributePermittedSubclasses()
- .addKeepPermittedSubclasses(Super.class, Iface1.class, Iface2.class, Sub2.class)
+ .addKeepPermittedSubclasses(
+ Super.class, Iface1.class, Iface2.class, Sub2.class, IfaceUnrelated.class)
.addKeepMainRule(TestClass.class)
.addVerticallyMergedClassesInspector(
inspector -> {
@@ -84,12 +87,8 @@
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
- parameters.isDexRuntime(),
+ parameters.isDexRuntime() || parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
r -> r.assertSuccessWithOutput(EXPECTED),
- parameters.isCfRuntime() && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
- r ->
- r.assertFailureWithErrorThatMatches(
- containsString("cannot implement sealed interface")),
r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
}
@@ -100,6 +99,9 @@
.transform(),
transformer(Iface2.class)
.setPermittedSubclasses(Iface2.class, Sub1.class, Sub2.class)
+ .transform(),
+ transformer(IfaceUnrelated.class)
+ .setPermittedSubclasses(IfaceUnrelated.class, Sub1.class, Sub2.class)
.transform());
}
@@ -117,6 +119,9 @@
@NoUnusedInterfaceRemoval
interface Iface2 /* permits Sub1, Sub2 */ {}
+ @NoUnusedInterfaceRemoval
+ interface IfaceUnrelated /* permits Sub1, Sub2 */ {}
+
abstract static class Super {}
static class Sub1 extends Super implements Iface1, Iface2 {}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index 9100c82..9d1a5e8 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -48,6 +48,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
import org.antlr.runtime.RecognitionException;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,7 +71,7 @@
DexApplication application,
InternalOptions options,
MethodSubject method,
- List<IRCode> additionalCode)
+ List<Function<AppView<?>, IRCode>> additionalCode)
throws ExecutionException {
// Some tests play fast and loose with IR and the SSA value numbers are not generally unique.
if (additionalCode != null && !additionalCode.isEmpty()) {
@@ -161,10 +162,10 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
return buildTestApplication(
application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -243,7 +244,7 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
return buildTestApplication(application, options, methodSubject, ImmutableList.of(codeA));
}
@@ -320,16 +321,16 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
// Build three copies of a and b for inlining three times.
- List<IRCode> additionalCode = new ArrayList<>();
+ List<Function<AppView<?>, IRCode>> additionalCode = new ArrayList<>();
for (int i = 0; i < 3; i++) {
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
additionalCode.add(codeA);
}
for (int i = 0; i < 3; i++) {
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
additionalCode.add(codeB);
}
@@ -453,10 +454,10 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
return buildTestApplication(
application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -579,10 +580,10 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
return buildTestApplication(
application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -690,10 +691,10 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
return buildTestApplication(
application, options, methodSubject, ImmutableList.of(codeA, codeB));
@@ -803,16 +804,16 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
// Build three copies of a and b for inlining three times.
- List<IRCode> additionalCode = new ArrayList<>();
+ List<Function<AppView<?>, IRCode>> additionalCode = new ArrayList<>();
for (int i = 0; i < 3; i++) {
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
additionalCode.add(codeA);
}
for (int i = 0; i < 3; i++) {
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
additionalCode.add(codeB);
}
@@ -959,16 +960,16 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
// Build three copies of a and b for inlining three times.
- List<IRCode> additionalCode = new ArrayList<>();
+ List<Function<AppView<?>, IRCode>> additionalCode = new ArrayList<>();
for (int i = 0; i < 3; i++) {
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
additionalCode.add(codeA);
}
for (int i = 0; i < 3; i++) {
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
additionalCode.add(codeB);
}
@@ -1205,10 +1206,10 @@
MethodSubject methodSubject = getMethodSubject(application, signature);
MethodSubject methodASubject = getMethodSubject(application, signatureA);
- IRCode codeA = methodASubject.buildIR();
+ Function<AppView<?>, IRCode> codeA = appView -> methodASubject.buildIR(appView);
MethodSubject methodBSubject = getMethodSubject(application, signatureB);
- IRCode codeB = methodBSubject.buildIR();
+ Function<AppView<?>, IRCode> codeB = appView -> methodBSubject.buildIR(appView);
return buildTestApplication(
application, options, methodSubject, ImmutableList.of(codeA, codeB));
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 3143cb0..d835003 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -15,17 +15,21 @@
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.smali.SmaliTestBase;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.function.Function;
public class IrInjectionTestBase extends SmaliTestBase {
@@ -61,16 +65,23 @@
public final NumberGenerator valueNumberGenerator = new NumberGenerator();
public TestApplication(AppView<?> appView, MethodSubject method) {
- this(appView, method, null);
+ this(appView, method, ImmutableList.of());
}
- public TestApplication(AppView<?> appView, MethodSubject method, List<IRCode> additionalCode) {
+ public TestApplication(
+ AppView<?> appView,
+ MethodSubject method,
+ List<Function<AppView<?>, IRCode>> additionalCode) {
this.application = appView.appInfo().app();
this.appView = appView;
this.method = method.getMethod();
- this.code = method.buildIR();
+ appView.testing().skipLirPhasesForTestingFinalOutput();
+ this.code =
+ method
+ .getProgramMethod()
+ .buildIR(appView, MethodConversionOptions.forPostLirPhase(appView));
code.removeRedundantBlocks();
- this.additionalCode = additionalCode;
+ this.additionalCode = ListUtils.map(additionalCode, fn -> fn.apply(appView));
this.consumers = new AndroidAppConsumers(appView.options());
int largestValueNumber = -1;
for (BasicBlock block : code.blocks) {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 1769dc9..a1d31af 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -72,7 +72,7 @@
buildClasses(classes).addLibraryFile(getMostRecentAndroidJar()).build());
CodeInspector codeInspector = new CodeInspector(appView.appInfo().app());
MethodSubject fooSubject = codeInspector.clazz(mainClass.getName()).method(signature);
- IRCode irCode = fooSubject.buildIR();
+ IRCode irCode = fooSubject.buildIR(appView);
new AssumeInserter(appView).insertAssumeInstructions(irCode, Timing.empty());
inspector.accept(appView, irCode);
verifyLastInvoke(irCode, npeCaught);
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 f041526..bb65fdc 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
@@ -59,7 +59,7 @@
buildClasses(classes).addLibraryFile(getMostRecentAndroidJar()).build());
CodeInspector codeInspector = new CodeInspector(appView.appInfo().app());
MethodSubject fooSubject = codeInspector.clazz(testClass.getName()).method(signature);
- IRCode code = fooSubject.buildIR();
+ IRCode code = fooSubject.buildIR(appView);
checkCountOfNonNull(code, 0);
AssumeInserter assumeInserter = new AssumeInserter(appView);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java
index 482ed74..211bad5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineStaticSynchronizedMethodTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -58,9 +59,10 @@
// vms. See issue b/238399429 for details.
if (parameters.isCfRuntime()
|| parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M)) {
- assertThat(classSubject.uniqueMethodWithOriginalName("m1"), isPresent());
- assertThat(classSubject.uniqueMethodWithOriginalName("m2"), not(isPresent()));
-
+ int remaining = 0;
+ remaining += classSubject.uniqueMethodWithOriginalName("m1").isPresent() ? 1 : 0;
+ remaining += classSubject.uniqueMethodWithOriginalName("m2").isPresent() ? 1 : 0;
+ assertEquals("Expected only one of m1 and m2 to be inlined", 1, remaining);
} else {
assertThat(classSubject.uniqueMethodWithOriginalName("m1"), not(isPresent()));
assertThat(classSubject.uniqueMethodWithOriginalName("m2"), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
index c8859fe..e322f8d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTest.java
@@ -285,7 +285,7 @@
.run(parameters.getRuntime(), MAIN)
// TODO(b/154813140): Invalidly assumes that getClass on kept classes can be optimized.
.assertSuccessWithOutputThatMatches(not(equalTo(JAVA_OUTPUT)));
- test(result, 8);
+ test(result, 6);
}
@Test
@@ -316,6 +316,6 @@
} else {
result.assertSuccessWithOutputThatMatches(not(equalTo(JAVA_OUTPUT)));
}
- test(result, 8);
+ test(result, 6);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 82ef602..5c3fe96 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -204,8 +204,8 @@
assertEquals(
Lists.newArrayList(
- "STATIC: String SimpleWithLazyInit.bar$1(String)",
- "STATIC: String SimpleWithLazyInit.foo$1()",
+ "STATIC: String SimpleWithLazyInit.bar(String)",
+ "STATIC: String SimpleWithLazyInit.foo()",
"STATIC: String TrivialTestClass.next()"),
references(clazz, "testSimpleWithThrowingGetter", "void"));
@@ -222,8 +222,8 @@
}
Collections.addAll(
expectedReferencesInTestSimpleWithLazyInit,
- "STATIC: String SimpleWithLazyInit.bar(String)",
- "STATIC: String SimpleWithLazyInit.foo()",
+ "STATIC: String SimpleWithLazyInit.bar$1(String)",
+ "STATIC: String SimpleWithLazyInit.foo$1()",
"STATIC: String TrivialTestClass.next()",
"SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
"SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
@@ -298,6 +298,7 @@
.allowAccessModification()
.addDontObfuscate()
.addOptionsModification(this::configure)
+ .addOptionsModification(o -> o.testing.enableLir())
.setMinApi(parameters)
.run(parameters.getRuntime(), main)
.assertSuccessWithOutput(javaOutput);
@@ -340,11 +341,6 @@
assertThat(inspector.clazz(CandidateConflictMethod.class), isPresent());
List<String> expectedReferencesInTestConflictField = new ArrayList<>();
- if (!parameters.canHaveNonReboundConstructorInvoke()) {
- Collections.addAll(
- expectedReferencesInTestConflictField,
- "DIRECT: void movetohost.HostConflictField.<init>()");
- }
Collections.addAll(
expectedReferencesInTestConflictField,
"STATIC: String movetohost.CandidateConflictField.bar(String)",
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
index 580b42c..4896ebd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
@@ -93,23 +93,17 @@
assertEquals(0, lambdasInInput.getNumberOfJStyleLambdas());
assertEquals(28, lambdasInInput.getNumberOfKStyleLambdas());
- if (!parameters.isAccessModificationEnabled(allowAccessModification)) {
- // Only a subset of all K-style Kotlin lambdas are merged without -allowaccessmodification.
- Set<ClassReference> unmergedLambdas =
- ImmutableSet.of(
- lambdasInInput.getKStyleLambdaReferenceFromTypeName(
- getTestName(), "inner.InnerKt$testInnerStateless$7"));
- inspector
- .assertClassReferencesMerged(
- lambdasInInput.getKStyleLambdas().stream()
- .filter(not(unmergedLambdas::contains))
- .collect(Collectors.toList()))
- .assertClassReferencesNotMerged(unmergedLambdas);
- return;
- }
-
- // All K-style Kotlin lambdas are merged with -allowaccessmodification.
- inspector.assertClassReferencesMerged(lambdasInInput.getKStyleLambdas());
+ // Only a subset of all K-style Kotlin lambdas are merged.
+ Set<ClassReference> unmergedLambdas =
+ ImmutableSet.of(
+ lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInnerStateless$7"));
+ inspector
+ .assertClassReferencesMerged(
+ lambdasInInput.getKStyleLambdas().stream()
+ .filter(not(unmergedLambdas::contains))
+ .collect(Collectors.toList()))
+ .assertClassReferencesNotMerged(unmergedLambdas);
}
private String getExpectedOutput() {
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateOutlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateOutlineTest.java
new file mode 100644
index 0000000..5c15d3d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateOutlineTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/* This is a regression test for b/289788048 */
+public class ComposeDuplicateOutlineTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ private static final String mappingFoo =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+ "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:",
+ "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}",
+ "# {'id':'com.android.tools.r8.synthesized'}",
+ " 1:2:long package.Int2IntLinkedOpenHashMap$$InternalSyntheticOutline$HASH$0"
+ + ".m(long,long,long):0:1"
+ + " -> a",
+ " # {'id':'com.android.tools.r8.synthesized'}",
+ " # {'id':'com.android.tools.r8.outline'}",
+ "package.Class -> package.internal.Y:",
+ "# {'id':'sourceFile','fileName':'FieldDefinition.java'}",
+ " 1:1:void foo():21:21 -> a",
+ " # {'id':'com.android.tools.r8.outlineCallsite',"
+ + "'positions':{'1':2,'2':3},"
+ + "'outline':'Lpackage/internal/X;a(JJJ)J'}",
+ " 2:2:void foo():1337:1337 -> a",
+ " 3:3:void foo():44:44 -> a");
+ private static final String mappingBar =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+ "package.internal.Y -> package.new_internal.Y:",
+ // If there are multiple throwing instructions for a line number, we can get multiple
+ // mappings in D8.
+ " 1:10:void a():1:1 -> b",
+ " 11:20:void a():1:1 -> b");
+ private static final String mappingResult =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+ "package.Class -> package.new_internal.Y:",
+ "# {'id':'sourceFile','fileName':'FieldDefinition.java'}",
+ " 1:10:void foo():21:21 -> b",
+ " # {'id':'com.android.tools.r8.outlineCallsite',"
+ + "'positions':{'1':21,'2':22},"
+ + "'outline':'Lpackage/internal/X;a(JJJ)J'}",
+ " 11:20:void foo():21:21 -> b",
+ " # {'id':'com.android.tools.r8.outlineCallsite',"
+ + "'positions':{'1':21,'2':22},"
+ + "'outline':'Lpackage/internal/X;a(JJJ)J'}",
+ " 21:21:void foo():1337:1337 -> b",
+ " 22:22:void foo():44:44 -> b",
+ "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:",
+ "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}",
+ "# {'id':'com.android.tools.r8.synthesized'}",
+ " 1:2:long package.Int2IntLinkedOpenHashMap"
+ + "$$InternalSyntheticOutline$HASH$0.m(long,long,long):0:1 -> a",
+ " # {'id':'com.android.tools.r8.synthesized'}",
+ " # {'id':'com.android.tools.r8.outline'}");
+
+ @Test
+ public void testCompose() throws Exception {
+ ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+ ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+ String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
+ }
+}
diff --git a/src/test/testngrunner/TestNGMainRunner.java b/src/test/testngrunner/TestNGMainRunner.java
deleted file mode 100644
index 22d96ef..0000000
--- a/src/test/testngrunner/TestNGMainRunner.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2019, 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.
-
-// This class is kept in a separate package because of the org.testng dependency.
-// R8 tests do not depend on testng, this class does.
-public class TestNGMainRunner {
-
- private static void runTestNg(Class<?> testClass, int verbose) {
- System.out.println("Running tests in " + testClass.getName());
- org.testng.TestNG testng = new org.testng.TestNG(false);
- testng.setTestClasses(new Class<?>[] {testClass});
- testng.setVerbose(verbose);
- // Deprecated API used because it works on Android unlike the recommended one.
- testng.addListener(new org.testng.reporters.TextReporter(testClass.getName(), verbose));
- try {
- testng.run();
- System.out.print("Tests result in " + testClass.getName() + ": ");
- if (testng.hasFailure()) {
- System.out.println("FAILURE");
- } else {
- System.out.println("SUCCESS");
- }
- } catch (RuntimeException | Error e) {
- System.out.print("Tests result in " + testClass.getName() + ": ");
- System.out.println("ERROR");
- e.printStackTrace();
- }
- }
-
- public static void main(String[] args) throws Exception {
- // First arg is the verbosity level.
- // Second arg is the class to run.
- int verbose = Integer.parseInt(args[0]);
- runTestNg(Class.forName(args[1]), verbose);
- }
-}
diff --git a/tools/internal_test.py b/tools/internal_test.py
index a726e1e..cab2d60 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -62,6 +62,8 @@
BENCHMARK_APPS = [chrome_data, iosched_data, r8_data, youtube_data]
+DEPENDENT_PYTHON_FILES = [gradle, utils, run_on_app]
+
def find_min_xmx_command(app_data):
record = app_data.GetMemoryData(app_data.GetLatestVersion())
assert record['find-xmx-min'] < record['find-xmx-max']
@@ -150,8 +152,8 @@
contents = []
with open(sys.argv[0], 'r') as us:
contents.append(us.read())
- for app_data in BENCHMARK_APPS:
- with open(app_data.__file__, 'r') as us:
+ for deps in BENCHMARK_APPS + DEPENDENT_PYTHON_FILES:
+ with open(deps.__file__, 'r') as us:
contents.append(us.read())
return contents