Infer lower bound type information for effectively final classes
Change-Id: I8e86776d2bd5be413136fc1527465db85a34e4e9
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 72e372a..c340ae7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -626,6 +626,14 @@
return accessFlags.isAbstract();
}
+ public boolean isFinal() {
+ return accessFlags.isFinal();
+ }
+
+ public boolean isEffectivelyFinal(AppView<?> appView) {
+ return isFinal();
+ }
+
public boolean isInterface() {
return accessFlags.isInterface();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 92a4963..5a89dcf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -228,6 +229,26 @@
return type.toSourceString();
}
+ /**
+ * Returns true if this class is final, or it is a non-pinned program class with no instantiated
+ * subtypes.
+ */
+ @Override
+ public boolean isEffectivelyFinal(AppView<?> appView) {
+ if (isFinal()) {
+ return true;
+ }
+ if (appView.enableWholeProgramOptimizations()) {
+ assert appView.appInfo().hasLiveness();
+ AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
+ if (appInfo.isPinned(type)) {
+ return false;
+ }
+ return !appInfo.hasSubtypes(type) || !appInfo.isInstantiatedIndirectly(type);
+ }
+ return false;
+ }
+
@Override
public boolean isProgramClass() {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
index 4adc57e..3ad41d4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
@@ -89,7 +89,7 @@
}
@Override
- public ReferenceTypeLatticeElement getOrCreateVariant(Nullability nullability) {
+ public ClassTypeLatticeElement getOrCreateVariant(Nullability nullability) {
ClassTypeLatticeElement variant = variants.get(nullability);
if (variant != null) {
return variant;
@@ -116,6 +116,11 @@
}
@Override
+ public ClassTypeLatticeElement asMeetWithNotNull() {
+ return getOrCreateVariant(nullability.meet(Nullability.definitelyNotNull()));
+ }
+
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(nullability);
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 aea5a70..87b93a4 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
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
-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.DexItemFactory;
@@ -17,6 +16,7 @@
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
public abstract class InvokeMethodWithReceiver extends InvokeMethod {
@@ -74,15 +74,15 @@
TypeLatticeElement receiverType = receiver.getTypeLattice();
assert receiverType.isPreciseType();
- if (appView.appInfo().hasSubtyping()) {
- AppView<? extends AppInfoWithSubtyping> appViewWithSubtyping = appView.withSubtyping();
+ if (appView.appInfo().hasLiveness()) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
ClassTypeLatticeElement receiverLowerBoundType =
- receiver.getDynamicLowerBoundType(appViewWithSubtyping);
+ receiver.getDynamicLowerBoundType(appViewWithLiveness);
if (receiverLowerBoundType != null) {
DexType refinedReceiverType =
- TypeAnalysis.getRefinedReceiverType(appViewWithSubtyping, this);
+ TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this);
assert receiverLowerBoundType.getClassType() == refinedReceiverType
- || receiverLowerBoundType.isBasedOnMissingClass(appViewWithSubtyping);
+ || receiverLowerBoundType.isBasedOnMissingClass(appViewWithLiveness);
}
}
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 f9ca31d..97d42fb 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
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.regalloc.LiveIntervals;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SetUtils;
@@ -1114,12 +1115,12 @@
return lattice;
}
- public ClassTypeLatticeElement getDynamicLowerBoundType(
- AppView<? extends AppInfoWithSubtyping> appView) {
+ public ClassTypeLatticeElement getDynamicLowerBoundType(AppView<AppInfoWithLiveness> appView) {
Value root = getAliasedValue();
if (root.isPhi()) {
return null;
}
+
Instruction definition = root.definition;
if (definition.isNewInstance()) {
DexType type = definition.asNewInstance().clazz;
@@ -1129,6 +1130,7 @@
}
return null;
}
+
// Try to find an alias of the receiver, which is defined by an instruction of the type
// Assume<DynamicTypeAssumption>.
Value aliasedValue = getSpecificAliasedValue(value -> value.definition.isAssumeDynamicType());
@@ -1136,9 +1138,20 @@
ClassTypeLatticeElement lattice =
aliasedValue.definition.asAssumeDynamicType().getAssumption().getDynamicLowerBoundType();
return lattice != null && typeLattice.isDefinitelyNotNull() && lattice.isNullable()
- ? lattice.asMeetWithNotNull().asClassTypeLatticeElement()
+ ? lattice.asMeetWithNotNull()
: lattice;
}
+
+ // If it is a final or effectively-final class type, then we know the lower bound.
+ if (getTypeLattice().isClassType()) {
+ ClassTypeLatticeElement classType = getTypeLattice().asClassTypeLatticeElement();
+ DexType type = classType.getClassType();
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz != null && clazz.isEffectivelyFinal(appView)) {
+ return classType;
+ }
+ }
+
return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index d9e51ea..06c5dfe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -76,7 +76,8 @@
MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
if (optimizationInfo.returnsArgument()) {
- assert optimizationInfo.getDynamicLowerBoundType() == null;
+ // Don't insert an assume-instruction since we will replace all usages of the out-value by
+ // the corresponding argument.
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index a467cb2..12992ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -94,7 +94,9 @@
.add(this::insertAssumeInstructions)
.add(collectOptimizationInfo(feedback)));
- // Enqueue instance methods to be staticized (only remove references to 'this').
+ // Enqueue instance methods to be staticized (only remove references to 'this'). Intentionally
+ // not collection optimization info for these methods, since they will be reprocessed again
+ // below once staticized.
enqueueMethodsWithCodeOptimizations(
methodsToBeStaticized, optimizations -> optimizations.add(this::removeReferencesToThis));