Track initialized classes on normal exits
Bug: 123557989
Change-Id: Ief52cdcb4e1f1186811c5f74a9874316e9cdf301
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 6cbbece..85bc4cb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -47,10 +47,12 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
public class DexEncodedMethod extends KeyedDexItem<DexMethod> implements ResolutionResult {
@@ -822,6 +824,7 @@
public static class DefaultOptimizationInfoImpl implements OptimizationInfo {
public static final OptimizationInfo DEFAULT_INSTANCE = new DefaultOptimizationInfoImpl();
+ public static Set<DexType> UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT = ImmutableSet.of();
public static int UNKNOWN_RETURNED_ARGUMENT = -1;
public static boolean UNKNOWN_NEVER_RETURNS_NULL = false;
public static boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
@@ -842,6 +845,11 @@
private DefaultOptimizationInfoImpl() {}
@Override
+ public Set<DexType> getInitializedClassesOnNormalExit() {
+ return UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
+ }
+
+ @Override
public TrivialInitializer getTrivialInitializerInfo() {
return UNKNOWN_TRIVIAL_INITIALIZER;
}
@@ -963,6 +971,8 @@
public static class OptimizationInfoImpl implements UpdatableOptimizationInfo {
+ private Set<DexType> initializedClassesOnNormalExit =
+ DefaultOptimizationInfoImpl.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
private int returnedArgument = DefaultOptimizationInfoImpl.UNKNOWN_RETURNED_ARGUMENT;
private boolean mayHaveSideEffects = DefaultOptimizationInfoImpl.UNKNOWN_MAY_HAVE_SIDE_EFFECTS;
private boolean neverReturnsNull = DefaultOptimizationInfoImpl.UNKNOWN_NEVER_RETURNS_NULL;
@@ -1035,6 +1045,11 @@
}
@Override
+ public Set<DexType> getInitializedClassesOnNormalExit() {
+ return initializedClassesOnNormalExit;
+ }
+
+ @Override
public TrivialInitializer getTrivialInitializerInfo() {
return trivialInitializerInfo;
}
@@ -1184,6 +1199,11 @@
}
@Override
+ public void markInitializesClassesOnNormalExit(Set<DexType> initializedClassesOnNormalExit) {
+ this.initializedClassesOnNormalExit = initializedClassesOnNormalExit;
+ }
+
+ @Override
public void markReturnsArgument(int argument) {
assert argument >= 0;
assert returnedArgument == -1 || returnedArgument == argument;
diff --git a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
index 33b5f63..98f30cb 100644
--- a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
import java.util.BitSet;
+import java.util.Set;
public interface OptimizationInfo {
@@ -41,6 +42,8 @@
ClassInlinerEligibility getClassInlinerEligibility();
+ Set<DexType> getInitializedClassesOnNormalExit();
+
TrivialInitializer getTrivialInitializerInfo();
boolean isInitializerEnablingJavaAssertions();
diff --git a/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
index 8550abb..f982289 100644
--- a/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
@@ -7,8 +7,12 @@
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import java.util.BitSet;
+import java.util.Set;
public interface UpdatableOptimizationInfo extends OptimizationInfo {
+
+ void markInitializesClassesOnNormalExit(Set<DexType> initializedClasses);
+
void markReturnsArgument(int argument);
void markReturnsConstantNumber(long value);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index ab803ee..f77d9428d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -9,6 +9,9 @@
import com.android.tools.r8.graph.AppInfo.ResolutionResult;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -23,6 +26,7 @@
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.code.InvokeVirtual;
@@ -31,8 +35,12 @@
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.Iterator;
+import java.util.Set;
/**
* Analysis that given an instruction determines if a given type is guaranteed to be class
@@ -85,6 +93,7 @@
}
public boolean isClassDefinitelyLoadedBeforeInstruction(DexType type, Instruction instruction) {
+ DexType context = code.method.method.holder;
BasicBlock block = instruction.getBlock();
// Visit the instructions in `block` prior to `instruction`.
@@ -94,6 +103,7 @@
}
if (previous.definitelyTriggersClassInitialization(
type,
+ context,
appView,
Query.DIRECTLY_OR_INDIRECTLY,
// The given instruction is only reached if none of the instructions in the same
@@ -114,7 +124,7 @@
while (instructionIterator.hasNext()) {
Instruction previous = instructionIterator.next();
if (previous.definitelyTriggersClassInitialization(
- type, appView, Query.DIRECTLY_OR_INDIRECTLY, assumption)) {
+ type, context, appView, Query.DIRECTLY_OR_INDIRECTLY, assumption)) {
return true;
}
if (dominator.hasCatchHandlers() && previous.instructionTypeCanThrow()) {
@@ -278,7 +288,8 @@
return false;
}
}
- return isTypeInitializedBy(type, instruction.getField().holder, appView, mode);
+ DexEncodedField field = appView.appInfo().resolveField(instruction.getField());
+ return field != null && isTypeInitializedBy(type, field, appView, mode);
}
public static boolean forInvokeDirect(
@@ -293,25 +304,14 @@
return false;
}
}
- return isTypeInitializedBy(type, instruction.getInvokedMethod().holder, appView, mode);
+ DexEncodedMethod method = appView.definitionFor(instruction.getInvokedMethod());
+ return method != null && isTypeInitializedBy(type, method, appView, mode);
}
- public static boolean forInvokeStatic(
- InvokeStatic instruction,
+ public static boolean forInvokeInterface(
+ InvokeInterface instruction,
DexType type,
- AppView<? extends AppInfo> appView,
- Query mode,
- AnalysisAssumption assumption) {
- if (assumption == AnalysisAssumption.NONE) {
- // Class initialization may fail with ExceptionInInitializerError.
- return false;
- }
- return isTypeInitializedBy(type, instruction.getInvokedMethod().holder, appView, mode);
- }
-
- public static boolean forInvokeSuper(
- InvokeSuper instruction,
- DexType type,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
@@ -327,6 +327,64 @@
// TODO(christofferqa): We can do better if there is a unique target.
return false;
}
+ if (appView.appInfo().hasLiveness()) {
+ AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
+ DexEncodedMethod singleTarget =
+ instruction.lookupSingleTarget(appInfoWithLiveness, context);
+ if (singleTarget != null) {
+ return isTypeInitializedBy(type, singleTarget, appView, mode);
+ }
+ }
+ DexMethod method = instruction.getInvokedMethod();
+ ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
+ if (!resolutionResult.hasSingleTarget()) {
+ return false;
+ }
+ DexType holder = resolutionResult.asSingleTarget().method.holder;
+ return appView.isSubtype(holder, type).isTrue();
+ }
+
+ public static boolean forInvokeStatic(
+ InvokeStatic instruction,
+ DexType type,
+ AppView<? extends AppInfo> appView,
+ Query mode,
+ AnalysisAssumption assumption) {
+ if (assumption == AnalysisAssumption.NONE) {
+ // Class initialization may fail with ExceptionInInitializerError.
+ return false;
+ }
+ DexEncodedMethod method = appView.definitionFor(instruction.getInvokedMethod());
+ return method != null && isTypeInitializedBy(type, method, appView, mode);
+ }
+
+ public static boolean forInvokeSuper(
+ InvokeSuper instruction,
+ DexType type,
+ DexType context,
+ AppView<? extends AppInfo> appView,
+ Query mode,
+ AnalysisAssumption assumption) {
+ if (assumption == AnalysisAssumption.NONE) {
+ if (instruction.getReceiver().getTypeLattice().isNullable()) {
+ // If the receiver is null we cannot be sure that the holder has been initialized.
+ return false;
+ }
+ }
+ if (mode == Query.DIRECTLY) {
+ // We cannot ensure exactly which class is being loaded because it depends on the runtime
+ // type of the receiver.
+ // TODO(christofferqa): We can do better if there is a unique target.
+ return false;
+ }
+ if (appView.appInfo().hasLiveness()) {
+ AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
+ DexEncodedMethod singleTarget =
+ instruction.lookupSingleTarget(appInfoWithLiveness, context);
+ if (singleTarget != null) {
+ return isTypeInitializedBy(type, singleTarget, appView, mode);
+ }
+ }
DexMethod method = instruction.getInvokedMethod();
DexClass enclosingClass = appView.definitionFor(method.holder);
if (enclosingClass == null) {
@@ -347,6 +405,7 @@
public static boolean forInvokeVirtual(
InvokeVirtual instruction,
DexType type,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
@@ -362,6 +421,14 @@
// TODO(christofferqa): We can do better if there is a unique target.
return false;
}
+ if (appView.appInfo().hasLiveness()) {
+ AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
+ DexEncodedMethod singleTarget =
+ instruction.lookupSingleTarget(appInfoWithLiveness, context);
+ if (singleTarget != null) {
+ return isTypeInitializedBy(type, singleTarget, appView, mode);
+ }
+ }
DexMethod method = instruction.getInvokedMethod();
ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
if (!resolutionResult.hasSingleTarget()) {
@@ -377,7 +444,12 @@
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
- return isTypeInitializedBy(type, instruction.clazz, appView, mode);
+ if (assumption == AnalysisAssumption.NONE) {
+ // Instruction may throw.
+ return false;
+ }
+ DexClass clazz = appView.definitionFor(instruction.clazz);
+ return clazz != null && isTypeInitializedBy(type, clazz, appView, mode);
}
public static boolean forStaticGet(
@@ -409,18 +481,76 @@
// Class initialization may fail with ExceptionInInitializerError.
return false;
}
- return isTypeInitializedBy(type, instruction.getField().holder, appView, mode);
+ DexEncodedField field = appView.appInfo().resolveField(instruction.getField());
+ return field != null && isTypeInitializedBy(type, field, appView, mode);
}
private static boolean isTypeInitializedBy(
DexType typeToBeInitialized,
- DexType typeKnownToBeInitialized,
+ DexDefinition definition,
AppView<? extends AppInfo> appView,
Query mode) {
if (mode == Query.DIRECTLY) {
- return typeKnownToBeInitialized == typeToBeInitialized;
+ if (definition.isDexClass()) {
+ return definition.asDexClass().type == typeToBeInitialized;
+ }
+ if (definition.isDexEncodedField()) {
+ return definition.asDexEncodedField().field.holder == typeToBeInitialized;
+ }
+ if (definition.isDexEncodedMethod()) {
+ return definition.asDexEncodedMethod().method.holder == typeToBeInitialized;
+ }
+ assert false;
+ return false;
+ }
+
+ Set<DexType> visited = Sets.newIdentityHashSet();
+ Deque<DexType> worklist = new ArrayDeque<>();
+
+ if (definition.isDexClass()) {
+ DexClass clazz = definition.asDexClass();
+ enqueue(clazz.type, visited, worklist);
+ } else if (definition.isDexEncodedField()) {
+ DexEncodedField field = definition.asDexEncodedField();
+ enqueue(field.field.holder, visited, worklist);
+ } else if (definition.isDexEncodedMethod()) {
+ DexEncodedMethod method = definition.asDexEncodedMethod();
+ enqueue(method.method.holder, visited, worklist);
+ enqueueInitializedClassesOnNormalExit(method, visited, worklist);
} else {
- return appView.isSubtype(typeKnownToBeInitialized, typeToBeInitialized).isTrue();
+ assert false;
+ }
+
+ while (!worklist.isEmpty()) {
+ DexType typeKnownToBeInitialized = worklist.removeFirst();
+ assert visited.contains(typeKnownToBeInitialized);
+
+ if (appView.isSubtype(typeKnownToBeInitialized, typeToBeInitialized).isTrue()) {
+ return true;
+ }
+
+ DexClass clazz = appView.definitionFor(typeKnownToBeInitialized);
+ if (clazz != null) {
+ DexEncodedMethod classInitializer = clazz.getClassInitializer();
+ if (classInitializer != null) {
+ enqueueInitializedClassesOnNormalExit(classInitializer, visited, worklist);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static void enqueue(DexType type, Set<DexType> visited, Deque<DexType> worklist) {
+ if (type.isClassType() && visited.add(type)) {
+ worklist.add(type);
+ }
+ }
+
+ private static void enqueueInitializedClassesOnNormalExit(
+ DexEncodedMethod method, Set<DexType> visited, Deque<DexType> worklist) {
+ for (DexType type : method.getOptimizationInfo().getInitializedClassesOnNormalExit()) {
+ enqueue(type, visited, worklist);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
new file mode 100644
index 0000000..295c39d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -0,0 +1,152 @@
+// 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.analysis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.DefaultInstructionVisitor;
+import com.android.tools.r8.ir.code.DominatorTree;
+import com.android.tools.r8.ir.code.DominatorTree.Assumption;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An analysis that given a method returns a set of types that are guaranteed to be initialized by
+ * the method on all normal exits of the given method.
+ */
+public class InitializedClassesOnNormalExitAnalysis {
+
+ public static Set<DexType> computeInitializedClassesOnNormalExit(
+ AppView<AppInfoWithLiveness> appView, IRCode code) {
+ DominatorTree dominatorTree = new DominatorTree(code, Assumption.MAY_HAVE_UNREACHABLE_BLOCKS);
+ Visitor visitor = new Visitor(appView, code.method.method.holder);
+ for (BasicBlock dominator : dominatorTree.normalExitDominatorBlocks()) {
+ if (dominator.hasCatchHandlers()) {
+ // When determining which classes that are guaranteed to be initialized from a given
+ // instruction, we assume that the given instruction does not throw. Therefore, we skip
+ // blocks that have a catch handler.
+ continue;
+ }
+
+ for (Instruction instruction : dominator.getInstructions()) {
+ instruction.accept(visitor);
+ }
+ }
+ return visitor.build();
+ }
+
+ private static class Visitor extends DefaultInstructionVisitor<Void> {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final DexType context;
+ private final Set<DexType> initializedClassesOnNormalExit = Sets.newIdentityHashSet();
+
+ Visitor(AppView<AppInfoWithLiveness> appView, DexType context) {
+ this.appView = appView;
+ this.context = context;
+ }
+
+ Set<DexType> build() {
+ return Collections.unmodifiableSet(initializedClassesOnNormalExit);
+ }
+
+ private void markInitializedOnNormalExit(Iterable<DexType> knownToBeInitialized) {
+ knownToBeInitialized.forEach(this::markInitializedOnNormalExit);
+ }
+
+ private void markInitializedOnNormalExit(DexType knownToBeInitialized) {
+ if (knownToBeInitialized == context) {
+ // Do not record that the given method causes its own holder to be initialized, since this
+ // is trivial.
+ return;
+ }
+ DexClass clazz = appView.definitionFor(knownToBeInitialized);
+ if (clazz == null) {
+ return;
+ }
+ if (!clazz.isProgramClass()) {
+ // Only mark program classes as being initialized on normal exits.
+ return;
+ }
+ if (!clazz.classInitializationMayHaveSideEffects(appView)) {
+ // Only mark classes that actually have side effects during class initialization.
+ return;
+ }
+ List<DexType> subsumedByKnownToBeInitialized = null;
+ for (DexType alreadyKnownToBeInitialized : initializedClassesOnNormalExit) {
+ if (appView.isSubtype(alreadyKnownToBeInitialized, knownToBeInitialized).isTrue()) {
+ // The fact that there is a subtype B of the given type A, which is known to be
+ // initialized, implies that the given type A is also be initialized. Therefore, we do not
+ // add this type into the set of classes that have been initialized on all normal exits.
+ return;
+ }
+
+ if (appView.isSubtype(knownToBeInitialized, alreadyKnownToBeInitialized).isTrue()) {
+ if (subsumedByKnownToBeInitialized == null) {
+ subsumedByKnownToBeInitialized = new ArrayList<>();
+ }
+ subsumedByKnownToBeInitialized.add(alreadyKnownToBeInitialized);
+ }
+ }
+ initializedClassesOnNormalExit.add(knownToBeInitialized);
+ if (subsumedByKnownToBeInitialized != null) {
+ initializedClassesOnNormalExit.removeAll(subsumedByKnownToBeInitialized);
+ }
+ }
+
+ @Override
+ public Void handleFieldInstruction(FieldInstruction instruction) {
+ DexEncodedField field = appView.appInfo().resolveField(instruction.getField());
+ if (field != null) {
+ if (field.field.holder.isClassType()) {
+ markInitializedOnNormalExit(field.field.holder);
+ } else {
+ assert false : "Expected holder of field type to be a class type";
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Void handleInvoke(Invoke instruction) {
+ if (instruction.isInvokeMethod()) {
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ DexMethod method = invoke.getInvokedMethod();
+ if (method.holder.isClassType()) {
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView.appInfo(), context);
+ if (singleTarget != null) {
+ markInitializedOnNormalExit(singleTarget.method.holder);
+ markInitializedOnNormalExit(
+ singleTarget.getOptimizationInfo().getInitializedClassesOnNormalExit());
+ } else {
+ markInitializedOnNormalExit(method.holder);
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Void visit(NewInstance instruction) {
+ markInitializedOnNormalExit(instruction.clazz);
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
index 2a8da38..513798f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Add.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -23,6 +23,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isCommutative() {
return true;
}
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 d419cbb..a3e9dba 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
@@ -20,6 +20,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean canBeDeadCode(AppView<? extends AppInfo> appView, IRCode code) {
// This instruction may never be considered dead as it must remain.
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
index 7f8c6eb..a72c180 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
@@ -21,6 +21,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean canBeDeadCode(AppView<? extends AppInfo> appView, IRCode code) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
index c36ee42..6f4eef9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingUser.java
@@ -20,6 +20,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean canBeDeadCode(AppView<? extends AppInfo> appView, IRCode code) {
// This instruction may never be considered dead as it must remain.
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index 73d3158..cc782ff 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -19,6 +19,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isAnd() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index 8cbf90d..7efb914 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -27,6 +27,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean canBeDeadCode(AppView<? extends AppInfo> appview, IRCode code) {
// Never remove argument instructions. That would change the signature of the method.
// TODO(b/65810338): If we can tell that a method never uses an argument we might be able to
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index f5e6e6c..3e2af3d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -39,6 +39,11 @@
this.type = type;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 97a610f..061aa53 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -24,6 +24,11 @@
super(dest, array);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index f0be051..a76bf42 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -43,6 +43,11 @@
this.type = type;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value array() {
return inValues.get(ARRAY_INDEX);
}
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 9aff46e..0c1396f 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
@@ -32,6 +32,11 @@
this.type = type;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public DexType getType() {
return type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index 8ad8d4b..9742a20 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -38,6 +38,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isCommutative() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 83bbb0f..5deee15 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -28,6 +28,11 @@
this.clazz = clazz;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public static ConstClass copyOf(IRCode code, ConstClass original) {
Value newValue =
new Value(
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 40c879f..166a121 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -27,6 +27,11 @@
this.methodHandle = methodHandle;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public static ConstMethodHandle copyOf(IRCode code, ConstMethodHandle original) {
Value newValue =
new Value(
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index f9eee1b..f2a2f57 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -25,6 +25,11 @@
this.methodType = methodType;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public static ConstMethodType copyOf(IRCode code, ConstMethodType original) {
Value newValue =
new Value(
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 4e4e1e6..8c1be98 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
@@ -42,6 +42,11 @@
this.value = value;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public static ConstNumber copyOf(IRCode code, ConstNumber original) {
Value newValue = new Value(
code.valueNumberGenerator.next(),
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 11b9a3d..2db5a33 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
@@ -29,6 +29,11 @@
this.throwingInfo = throwingInfo;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public static ConstString copyOf(IRCode code, ConstString original) {
Value newValue =
new Value(code.valueNumberGenerator.next(),
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index 8697b79..e9afbe3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -21,6 +21,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isDebugLocalRead() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java
index 565204f..7feb7f6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java
@@ -22,6 +22,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isDebugLocalUninitialized() {
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 de1b6fb..a325d9b 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
@@ -32,6 +32,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isDebugLocalWrite() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
index 4f097ff..560d524 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
@@ -30,6 +30,11 @@
this.starting = starting;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Int2ReferenceMap<DebugLocalInfo> getEnding() {
return ending;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index 02957b6..8e61fc3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -21,6 +21,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isDebugPosition() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
new file mode 100644
index 0000000..bcbae23
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DefaultInstructionVisitor.java
@@ -0,0 +1,351 @@
+// 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.code;
+
+public abstract class DefaultInstructionVisitor<T> implements InstructionVisitor<T> {
+
+ public T handleFieldInstruction(FieldInstruction instruction) {
+ return null;
+ }
+
+ public T handleInvoke(Invoke instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Add instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(AlwaysMaterializingDefinition instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(AlwaysMaterializingNop instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(AlwaysMaterializingUser instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(And instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Argument instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ArrayGet instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ArrayLength instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ArrayPut instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(CheckCast instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Cmp instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ConstClass instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ConstMethodHandle instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ConstMethodType instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ConstNumber instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(ConstString instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(DebugLocalRead instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(DebugLocalsChange instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(DebugLocalUninitialized instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(DebugLocalWrite instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(DebugPosition instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(DexItemBasedConstString instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Div instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Dup instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Dup2 instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Goto instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(If instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Inc instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(InstanceGet instruction) {
+ return handleFieldInstruction(instruction);
+ }
+
+ @Override
+ public T visit(InstanceOf instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(InstancePut instruction) {
+ return handleFieldInstruction(instruction);
+ }
+
+ @Override
+ public T visit(InvokeCustom instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokeDirect instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokeInterface instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokeMultiNewArray instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokeNewArray instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokePolymorphic instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokeStatic instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokeSuper instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(InvokeVirtual instruction) {
+ return handleInvoke(instruction);
+ }
+
+ @Override
+ public T visit(Load instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Monitor instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Move instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(MoveException instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Mul instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Neg instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(NewArrayEmpty instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(NewArrayFilledData instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(NewInstance instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(NonNull instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Not instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(NumberConversion instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Or instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Pop instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Rem instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Return instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Shl instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Shr instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(StaticGet instruction) {
+ return handleFieldInstruction(instruction);
+ }
+
+ @Override
+ public T visit(StaticPut instruction) {
+ return handleFieldInstruction(instruction);
+ }
+
+ @Override
+ public T visit(Store instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Sub instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Swap instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Switch instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Throw instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Ushr instruction) {
+ return null;
+ }
+
+ @Override
+ public T visit(Xor instruction) {
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index d5c8c82..0fa50e5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -39,6 +39,11 @@
this.throwingInfo = throwingInfo;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public static DexItemBasedConstString copyOf(Value newValue, DexItemBasedConstString original) {
return new DexItemBasedConstString(
newValue, original.getItem(), original.throwingInfo, original.classNameComputationInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
index 60daec0..ba55d2f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Div.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -25,6 +25,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isDiv() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
index 960ea92..72c64a0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -169,10 +169,6 @@
* Iteration order is always the immediate dominator of the previously returned block. The
* iteration starts by returning <code>dominated</code>.
*/
- public Iterable<BasicBlock> dominatorBlocks(BasicBlock dominated) {
- return dominatorBlocks(dominated, Inclusive.YES);
- }
-
public Iterable<BasicBlock> dominatorBlocks(BasicBlock dominated, Inclusive inclusive) {
assert !obsolete;
return () -> {
@@ -211,7 +207,9 @@
public Iterable<BasicBlock> normalExitDominatorBlocks() {
assert !obsolete;
- return dominatorBlocks(normalExitBlock);
+ // Do not include the `normalExitBlock`, since this is a synthetic block created here in the
+ // DominatorTree.
+ return dominatorBlocks(normalExitBlock, Inclusive.NO);
}
public BasicBlock[] getSortedBlocks() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup.java b/src/main/java/com/android/tools/r8/ir/code/Dup.java
index 89542a7..0a09d78 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup.java
@@ -26,6 +26,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public void setOutValue(Value value) {
assert outValue == null || !outValue.hasUsersInfo() || !outValue.isUsed() ||
value instanceof StackValues;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
index fcb5b8e..6564fbe 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup2.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -40,6 +40,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public void setOutValue(Value value) {
assert outValue == null || !outValue.hasUsersInfo() || !outValue.isUsed() ||
value instanceof StackValues;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
index 0c4bd04..a14098d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Goto.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -22,6 +22,11 @@
setBlock(block);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public BasicBlock getTarget() {
assert getBlock().exit() == this;
List<BasicBlock> successors = getBlock().getSuccessors();
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 25d1fc4..e5ac27f 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
@@ -79,6 +79,11 @@
this.type = type;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public boolean isZeroTest() {
return inValues.size() == 1;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Inc.java b/src/main/java/com/android/tools/r8/ir/code/Inc.java
index 3455108..d92eb4d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Inc.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Inc.java
@@ -28,6 +28,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
protected void addInValue(Value value) {
// Overriding IR addInValue since userinfo is cleared.
assert !value.hasUsersInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 542d635..86d549d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -41,6 +41,11 @@
super(field, dest, object);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
@@ -197,6 +202,7 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index e2cfeb3..b6dcc21 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -25,6 +25,11 @@
this.type = type;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public DexType type() {
return type;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index fd9e819..e4b062f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -39,6 +39,11 @@
assert value().verifyCompatible(ValueType.fromDexType(field.type));
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value object() {
return inValues.get(0);
}
@@ -171,6 +176,7 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
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 f845dc5..85ccb4e 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
@@ -61,6 +61,8 @@
setOutValue(outValue);
}
+ public abstract <T> T accept(InstructionVisitor<T> visitor);
+
public final Position getPosition() {
assert position != null;
return position;
@@ -1262,6 +1264,7 @@
*/
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
new file mode 100644
index 0000000..ec7ea00
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
@@ -0,0 +1,142 @@
+// 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.code;
+
+public interface InstructionVisitor<T> {
+
+ T visit(Add instruction);
+
+ T visit(AlwaysMaterializingDefinition instruction);
+
+ T visit(AlwaysMaterializingNop instruction);
+
+ T visit(AlwaysMaterializingUser instruction);
+
+ T visit(And instruction);
+
+ T visit(Argument instruction);
+
+ T visit(ArrayGet instruction);
+
+ T visit(ArrayLength instruction);
+
+ T visit(ArrayPut instruction);
+
+ T visit(CheckCast instruction);
+
+ T visit(Cmp instruction);
+
+ T visit(ConstClass instruction);
+
+ T visit(ConstMethodHandle instruction);
+
+ T visit(ConstMethodType instruction);
+
+ T visit(ConstNumber instruction);
+
+ T visit(ConstString instruction);
+
+ T visit(DebugLocalRead instruction);
+
+ T visit(DebugLocalsChange instruction);
+
+ T visit(DebugLocalUninitialized instruction);
+
+ T visit(DebugLocalWrite instruction);
+
+ T visit(DebugPosition instruction);
+
+ T visit(DexItemBasedConstString instruction);
+
+ T visit(Div instruction);
+
+ T visit(Dup instruction);
+
+ T visit(Dup2 instruction);
+
+ T visit(Goto instruction);
+
+ T visit(If instruction);
+
+ T visit(Inc instruction);
+
+ T visit(InstanceGet instruction);
+
+ T visit(InstanceOf instruction);
+
+ T visit(InstancePut instruction);
+
+ T visit(InvokeCustom instruction);
+
+ T visit(InvokeDirect instruction);
+
+ T visit(InvokeInterface instruction);
+
+ T visit(InvokeMultiNewArray instruction);
+
+ T visit(InvokeNewArray instruction);
+
+ T visit(InvokePolymorphic instruction);
+
+ T visit(InvokeStatic instruction);
+
+ T visit(InvokeSuper instruction);
+
+ T visit(InvokeVirtual instruction);
+
+ T visit(Load instruction);
+
+ T visit(Monitor instruction);
+
+ T visit(Move instruction);
+
+ T visit(MoveException instruction);
+
+ T visit(Mul instruction);
+
+ T visit(Neg instruction);
+
+ T visit(NewArrayEmpty instruction);
+
+ T visit(NewArrayFilledData instruction);
+
+ T visit(NewInstance instruction);
+
+ T visit(NonNull instruction);
+
+ T visit(Not instruction);
+
+ T visit(NumberConversion instruction);
+
+ T visit(Or instruction);
+
+ T visit(Pop instruction);
+
+ T visit(Rem instruction);
+
+ T visit(Return instruction);
+
+ T visit(Shl instruction);
+
+ T visit(Shr instruction);
+
+ T visit(StaticGet instruction);
+
+ T visit(StaticPut instruction);
+
+ T visit(Store instruction);
+
+ T visit(Sub instruction);
+
+ T visit(Swap instruction);
+
+ T visit(Switch instruction);
+
+ T visit(Throw instruction);
+
+ T visit(Ushr instruction);
+
+ T visit(Xor instruction);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 3d13f750..6ab4e1e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -28,6 +28,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public DexType getReturnType() {
return callSite.methodProto.returnType;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 53d3c8a..b214355 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -47,6 +47,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public Type getType() {
return Type.DIRECT;
}
@@ -134,6 +139,7 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index c790cab..12aa72d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -5,10 +5,15 @@
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
+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.DexType;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
+import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -26,6 +31,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public Type getType() {
return Type.INTERFACE;
}
@@ -98,4 +108,15 @@
public void buildCf(CfBuilder builder) {
builder.add(new CfInvoke(Opcodes.INVOKEINTERFACE, getInvokedMethod(), true));
}
+
+ @Override
+ public boolean definitelyTriggersClassInitialization(
+ DexType clazz,
+ DexType context,
+ AppView<? extends AppInfo> appView,
+ Query mode,
+ AnalysisAssumption assumption) {
+ return ClassInitializationAnalysis.InstructionUtils.forInvokeInterface(
+ this, clazz, context, appView, mode, assumption);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 13a8473..3b9ce30 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -28,6 +28,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isInvokeMultiNewArray() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index f89ccf6..37e3bad 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -29,6 +29,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public DexType getReturnType() {
return getArrayType();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 54e3065..2a57198 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -33,6 +33,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public DexType getReturnType() {
return proto.returnType;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index d25f065..4298299 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -43,6 +43,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public Type getType() {
return Type.STATIC;
}
@@ -127,6 +132,7 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 4b0a773..246f203 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -34,6 +34,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public Type getType() {
return Type.SUPER;
}
@@ -116,10 +121,11 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
return ClassInitializationAnalysis.InstructionUtils.forInvokeSuper(
- this, clazz, appView, mode, assumption);
+ this, clazz, context, appView, mode, assumption);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index faffec2..115ecb4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -31,6 +31,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public Type getType() {
return Type.VIRTUAL;
}
@@ -107,11 +112,12 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
return ClassInitializationAnalysis.InstructionUtils.forInvokeVirtual(
- this, clazz, appView, mode, assumption);
+ this, clazz, context, appView, mode, assumption);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index 4229b41..2245aa9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -23,6 +23,11 @@
super(dest, src);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value src() {
return inValues.get(0);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 4f650f7..60b99f8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -31,6 +31,11 @@
this.type = type;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value object() {
return inValues.get(0);
}
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 53c0134..e9f798a 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
@@ -24,6 +24,11 @@
super(dest, src);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 09619fb..1ffcd78 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -27,6 +27,11 @@
this.options = options;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Mul.java b/src/main/java/com/android/tools/r8/ir/code/Mul.java
index ebe12f8..b6b3ece 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Mul.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Mul.java
@@ -23,6 +23,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isCommutative() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index 856e2e0..f0bc717 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -28,6 +28,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean canBeFolded() {
return (type == NumericType.INT || type == NumericType.LONG || type == NumericType.FLOAT
|| type == NumericType.DOUBLE)
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 641b77e..0828653 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -28,6 +28,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public String toString() {
return super.toString() + " " + type.toString();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index 3151a94..9104992 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -33,6 +33,11 @@
this.data = data;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value src() {
return inValues.get(0);
}
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 3f97ba5..5fb8a0e 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
@@ -34,6 +34,11 @@
this.clazz = clazz;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
@@ -116,6 +121,7 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NonNull.java b/src/main/java/com/android/tools/r8/ir/code/NonNull.java
index 80bd6d9..4074c11 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NonNull.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NonNull.java
@@ -25,6 +25,11 @@
this.origin = origin;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index 013b219..c5d9970 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -26,6 +26,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean canBeFolded() {
return source().isConstant();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index 2b38cba..4ca038e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -38,6 +38,11 @@
this.to = to;
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public boolean isLongToIntConversion() {
return from == NumericType.LONG && to == NumericType.INT;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index fd4f204..67b730f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -18,6 +18,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isOr() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index 32bd532..75e1762 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -21,6 +21,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
protected void addInValue(Value value) {
if (value.hasUsersInfo()) {
super.addInValue(value);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Rem.java b/src/main/java/com/android/tools/r8/ir/code/Rem.java
index bd838bc..f0eb1ca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Rem.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -25,6 +25,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isRem() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 7d845ca..d1073bc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -28,6 +28,11 @@
super(null, value);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public boolean isReturnVoid() {
return inValues.size() == 0;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shl.java b/src/main/java/com/android/tools/r8/ir/code/Shl.java
index d9d0be0..719abf0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shl.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -18,6 +18,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
boolean fitsInDexInstruction(Value value) {
// The shl instruction only has the /lit8 variant.
return fitsInLit8Instruction(value);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shr.java b/src/main/java/com/android/tools/r8/ir/code/Shr.java
index c930d1d..036bdbc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -18,6 +18,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
boolean fitsInDexInstruction(Value value) {
// The shr instruction only has the /lit8 variant.
return fitsInLit8Instruction(value);
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 25d88fb..328b2f2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -39,6 +39,11 @@
super(field, dest, (Value) null);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value dest() {
return outValue;
}
@@ -190,6 +195,7 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index a87c0bb..a87692d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -34,6 +34,11 @@
super(field, null, source);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value inValue() {
assert inValues.size() == 1;
return inValues.get(0);
@@ -158,6 +163,7 @@
@Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
+ DexType context,
AppView<? extends AppInfo> appView,
Query mode,
AnalysisAssumption assumption) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 2058ba0..dc34dd1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -24,6 +24,11 @@
super(dest, src);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value src() {
return inValues.get(0);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Sub.java b/src/main/java/com/android/tools/r8/ir/code/Sub.java
index 7e7a9f8..74c8517 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Sub.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -28,6 +28,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isCommutative() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Swap.java b/src/main/java/com/android/tools/r8/ir/code/Swap.java
index 3c623d6..a43979c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Swap.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Swap.java
@@ -29,6 +29,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public void setOutValue(Value value) {
assert outValue == null || !outValue.hasUsersInfo() || !outValue.isUsed() ||
value instanceof StackValues;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Switch.java b/src/main/java/com/android/tools/r8/ir/code/Switch.java
index 2f82b0b..213ff92 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Switch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Switch.java
@@ -41,6 +41,11 @@
assert valid();
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
private boolean valid() {
assert keys.length <= Constants.U16BIT_MAX;
// Keys must be acceding, and cannot target the fallthrough.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index 23bb76c..6c91191 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -20,6 +20,11 @@
super(null, exception);
}
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
public Value exception() {
return inValues.get(0);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Ushr.java b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
index 65c932c..b7a5406 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Ushr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
@@ -18,6 +18,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
boolean fitsInDexInstruction(Value value) {
// The ushr instruction only has the /lit8 variant.
return fitsInLit8Instruction(value);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
index f3b8858..ec2d8db 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Xor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -18,6 +18,11 @@
}
@Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
public boolean isXor() {
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 bb3e80e..57562d8 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
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.analysis.InitializedClassesOnNormalExitAnalysis;
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -1063,6 +1064,7 @@
computeNonNullParamHints(feedback, method, code);
}
+ computeInitializedClassesOnNormalExit(feedback, method, code);
computeMayHaveSideEffects(feedback, method, code);
}
@@ -1155,6 +1157,19 @@
}
}
+ private void computeInitializedClassesOnNormalExit(
+ OptimizationFeedback feedback, DexEncodedMethod method, IRCode code) {
+ if (options.enableInitializedClassesAnalysis && appView.appInfo().hasLiveness()) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ Set<DexType> initializedClasses =
+ InitializedClassesOnNormalExitAnalysis.computeInitializedClassesOnNormalExit(
+ appViewWithLiveness, code);
+ if (initializedClasses != null && !initializedClasses.isEmpty()) {
+ feedback.methodInitializesClassesOnNormalExit(method, initializedClasses);
+ }
+ }
+ }
+
private void computeMayHaveSideEffects(
OptimizationFeedback feedback, DexEncodedMethod method, IRCode code) {
if (options.enableSideEffectAnalysis
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
index 4cef96f..3669369 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
@@ -8,11 +8,16 @@
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import java.util.BitSet;
+import java.util.Set;
public interface OptimizationFeedback {
+ void methodInitializesClassesOnNormalExit(
+ DexEncodedMethod method, Set<DexType> initializedClasses);
+
void methodReturnsArgument(DexEncodedMethod method, int argument);
void methodReturnsConstantNumber(DexEncodedMethod method, long value);
void methodReturnsConstantString(DexEncodedMethod method, DexString value);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
index 7382f9a..1042a63e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.graph.UpdatableOptimizationInfo;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -15,6 +16,7 @@
import java.util.BitSet;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Set;
public class OptimizationFeedbackDelayed implements OptimizationFeedback {
@@ -35,6 +37,12 @@
}
@Override
+ public synchronized void methodInitializesClassesOnNormalExit(
+ DexEncodedMethod method, Set<DexType> initializedClasses) {
+ getOptimizationInfoForUpdating(method).markInitializesClassesOnNormalExit(initializedClasses);
+ }
+
+ @Override
public synchronized void methodReturnsArgument(DexEncodedMethod method, int argument) {
getOptimizationInfoForUpdating(method).markReturnsArgument(argument);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
index a2a8bf3..2e5f0dd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
@@ -8,13 +8,19 @@
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import java.util.BitSet;
+import java.util.Set;
public class OptimizationFeedbackIgnore implements OptimizationFeedback {
@Override
+ public void methodInitializesClassesOnNormalExit(
+ DexEncodedMethod method, Set<DexType> initializedClasses) {}
+
+ @Override
public void methodReturnsArgument(DexEncodedMethod method, int argument) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
index a908798..225a1e7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackSimple.java
@@ -8,13 +8,21 @@
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import java.util.BitSet;
+import java.util.Set;
public class OptimizationFeedbackSimple implements OptimizationFeedback {
@Override
+ public void methodInitializesClassesOnNormalExit(
+ DexEncodedMethod method, Set<DexType> initializedClasses) {
+ // Ignored.
+ }
+
+ @Override
public void methodReturnsArgument(DexEncodedMethod method, int argument) {
// Ignored.
}
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 dbe8837..52284d1 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
@@ -1489,8 +1489,9 @@
return alwaysTriggerExpectedEffectBeforeAnythingElse(
code,
(instruction, it) -> {
+ DexType context = code.method.method.holder;
if (instruction.definitelyTriggersClassInitialization(
- clazz, appView, DIRECTLY, AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
+ clazz, context, appView, DIRECTLY, AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
// In order to preserve class initialization semantic, the exception must not be caught
// by any handler. Therefore, we must ignore this instruction if it is covered by a
// catch handler.
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 aad45cd..bca5d8f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -140,6 +140,7 @@
!Version.isDev() || System.getProperty("com.android.tools.r8.disableinlining") == null;
public boolean enableClassInlining = true;
public boolean enableClassStaticizer = true;
+ public boolean enableInitializedClassesAnalysis = true;
public boolean enableSideEffectAnalysis = true;
// TODO(b/120138731): Enable this when it is worthwhile, e.g., combined with Class#forName.
public boolean enableNameReflectionOptimization = false;
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/initializedclasses/InitializedClassesOnNormalExitAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/analysis/initializedclasses/InitializedClassesOnNormalExitAnalysisTest.java
new file mode 100644
index 0000000..7d05b2d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/analysis/initializedclasses/InitializedClassesOnNormalExitAnalysisTest.java
@@ -0,0 +1,101 @@
+// 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.analysis.initializedclasses;
+
+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.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InitializedClassesOnNormalExitAnalysisTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public InitializedClassesOnNormalExitAnalysisTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("A.<clinit>()", "A.foo()");
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InitializedClassesOnNormalExitAnalysisTest.class)
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .inspect(this::verifyInlineableHasBeenInlined)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ private void verifyInlineableHasBeenInlined(CodeInspector inspector) {
+ // Verify that main() only invokes loadA() and println().
+ {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject methodSubject = classSubject.mainMethod();
+ assertThat(methodSubject, isPresent());
+
+ assertEquals(
+ 2, methodSubject.streamInstructions().filter(InstructionSubject::isInvoke).count());
+ }
+
+ // Verify absence of inlineable().
+ {
+ ClassSubject classSubject = inspector.clazz(A.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject methodSubject = classSubject.uniqueMethodWithName("inlineable");
+ assertThat(methodSubject, not(isPresent()));
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ loadA();
+ A.inlineable();
+ }
+
+ @NeverInline
+ static void loadA() {
+ A.load();
+ }
+ }
+
+ static class A {
+
+ static {
+ System.out.println("A.<clinit>()");
+ }
+
+ static void load() {}
+
+ static void inlineable() {
+ System.out.println("A.foo()");
+ }
+ }
+}