Support abstract computation trees in flow propagation
Change-Id: Iea3af64e2fdff18a256594489b88f06559a9ec58
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 b742d92..9f3483e 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -534,7 +534,6 @@
}
public ComposeReferences getComposeReferences() {
- assert options().getJetpackComposeOptions().isAnyOptimizationsEnabled();
if (composeReferences == null) {
composeReferences = new ComposeReferences(dexItemFactory());
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/path/state/ConcretePathConstraintAnalysisState.java b/src/main/java/com/android/tools/r8/ir/analysis/path/state/ConcretePathConstraintAnalysisState.java
index 7bcb650..28bdcf1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/path/state/ConcretePathConstraintAnalysisState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/path/state/ConcretePathConstraintAnalysisState.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.path.state;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
import java.util.Collections;
import java.util.HashMap;
@@ -62,26 +63,22 @@
@Override
public PathConstraintAnalysisState add(ComputationTreeNode pathConstraint, boolean negate) {
PathConstraintKind previousKind = pathConstraints.get(pathConstraint);
+ PathConstraintKind newKind;
if (previousKind != null) {
- if (previousKind == PathConstraintKind.DISABLED) {
- // There is a loop.
+ // There is a loop.
+ newKind = PathConstraintKind.DISABLED;
+ if (previousKind == newKind) {
return this;
}
- if (previousKind == PathConstraintKind.get(negate)) {
- // This branch is dominated by a previous if-condition that has the same branch condition,
- // e.g., if (x) { if (x) { ...
- return this;
- }
- // This branch is dominated by a previous if-condition that has the negated branch condition,
- // e.g., if (x) { if (!x) { ...
- return bottom();
+ } else {
+ newKind = PathConstraintKind.get(negate);
}
// No jumps can dominate the entry of their own block, so when adding the condition of a jump
// this cannot currently be active.
Map<ComputationTreeNode, PathConstraintKind> newPathConstraints =
new HashMap<>(pathConstraints.size() + 1);
newPathConstraints.putAll(pathConstraints);
- newPathConstraints.put(pathConstraint, PathConstraintKind.get(negate));
+ newPathConstraints.put(pathConstraint, newKind);
return new ConcretePathConstraintAnalysisState(newPathConstraints);
}
@@ -153,7 +150,7 @@
return pathConstraint;
}
}
- return null;
+ return AbstractValue.unknown();
}
public ConcretePathConstraintAnalysisState join(ConcretePathConstraintAnalysisState other) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintKind.java b/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintKind.java
index 4e97d27..6891163 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintKind.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintKind.java
@@ -24,6 +24,9 @@
}
PathConstraintKind join(PathConstraintKind other) {
+ if (other == null) {
+ return this;
+ }
return this == other ? this : DISABLED;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index d3acc96..4f4cbfe 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.path.PathConstraintSupplier;
import com.android.tools.r8.ir.analysis.path.state.ConcretePathConstraintAnalysisState;
@@ -26,7 +27,6 @@
import com.android.tools.r8.ir.code.AbstractValueSupplier;
import com.android.tools.r8.ir.code.AliasedValueConfiguration;
import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
-import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.FieldGet;
import com.android.tools.r8.ir.code.FieldPut;
@@ -65,6 +65,7 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.StateCloner;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownMethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
+import com.android.tools.r8.optimize.argumentpropagation.computation.ComposableComputationTreeBuilder;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.MethodReprocessingCriteria;
@@ -76,10 +77,9 @@
import com.android.tools.r8.utils.DeterminismChecker.LineCallback;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.TraversalUtils;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.objects.Object2IntMap;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -241,8 +241,6 @@
protected final ProgramMethod context;
private final PathConstraintSupplier pathConstraintSupplier;
- private Object2IntMap<Phi> phiNumbering = null;
-
protected CodeScanner(
AbstractValueSupplier abstractValueSupplier,
IRCode code,
@@ -319,6 +317,7 @@
NonEmptyValueState inFlowState =
computeInFlowState(
field.getType(),
+ field,
value,
initialValue,
context,
@@ -379,6 +378,7 @@
// TODO(b/302281503): Canonicalize computed in flow.
private InFlow computeInFlow(
DexType staticType,
+ ProgramMember<?, ?> target,
Value value,
Value initialValue,
ProgramMethod context,
@@ -410,16 +410,23 @@
return castBaseInFlow(
widenBaseInFlow(staticType, fieldValueFactory.create(field), context), value);
} else if (value.isPhi()) {
+ // TODO(b/302281503): Replace IfThenElseAbstractFunction by ComputationTreeNode (?).
return computeIfThenElseAbstractFunction(value.asPhi(), valueStateSupplier);
+ } else if (target != null && appView.getComposeReferences().isComposable(target)) {
+ ComputationTreeNode node =
+ new ComposableComputationTreeBuilder(
+ appView, code, code.context(), methodParameterFactory, pathConstraintSupplier)
+ .getOrBuildComputationTree(value);
+ if (!node.isComputationLeaf() && TraversalUtils.hasNext(node::traverseBaseInFlow)) {
+ recordComputationTreePosition(node, value);
+ return node;
+ }
}
return null;
}
private IfThenElseAbstractFunction computeIfThenElseAbstractFunction(
Phi phi, Function<Value, NonEmptyValueState> valueStateSupplier) {
- if (!appView.testing().enableIfThenElseAbstractFunction) {
- return null;
- }
if (phi.getOperands().size() != 2 || !phi.hasOperandThatMatches(Value::isArgument)) {
return null;
}
@@ -437,11 +444,14 @@
ComputationTreeNode condition =
leftPredecessorPathConstraint.getDifferentiatingPathConstraint(
rightPredecessorPathConstraint);
- if (condition == null || condition.getSingleOpenVariable() == null) {
+ if (condition.getSingleOpenVariable() == null) {
return null;
}
NonEmptyValueState leftValue = valueStateSupplier.apply(phi.getOperand(0));
NonEmptyValueState rightValue = valueStateSupplier.apply(phi.getOperand(1));
+ if (leftValue.isUnknown() && rightValue.isUnknown()) {
+ return null;
+ }
IfThenElseAbstractFunction result =
leftPredecessorPathConstraint.isNegated(condition)
? new IfThenElseAbstractFunction(condition, rightValue, leftValue)
@@ -450,28 +460,25 @@
return result;
}
+ private void recordComputationTreePosition(ComputationTreeNode computation, Value value) {
+ inFlowComparatorBuilder.addComputationTreePosition(
+ computation,
+ SourcePosition.builder()
+ .setMethod(code.context().getReference())
+ .setLine(value.getNumber())
+ .build());
+ }
+
private void recordIfThenElsePosition(
IfThenElseAbstractFunction ifThenElseAbstractFunction, Phi phi) {
inFlowComparatorBuilder.addIfThenElsePosition(
ifThenElseAbstractFunction,
SourcePosition.builder()
.setMethod(code.context().getReference())
- .setLine(getOrCreatePhiNumbering().getInt(phi))
+ .setLine(phi.getNumber())
.build());
}
- private Object2IntMap<Phi> getOrCreatePhiNumbering() {
- if (phiNumbering == null) {
- phiNumbering = new Object2IntOpenHashMap<>();
- for (BasicBlock block : code.getBlocks()) {
- for (Phi phi : block.getPhis()) {
- phiNumbering.put(phi, phiNumbering.size());
- }
- }
- }
- return phiNumbering;
- }
-
private InFlow castBaseInFlow(InFlow inFlow, Value value) {
if (inFlow.isUnknown()) {
return inFlow;
@@ -501,12 +508,14 @@
private NonEmptyValueState computeInFlowState(
DexType staticType,
+ ProgramMember<?, ?> target,
Value value,
Value initialValue,
ProgramMethod context,
Function<Value, NonEmptyValueState> valueStateSupplier) {
assert value == initialValue || initialValue.getAliasedValue().isPhi();
- InFlow inFlow = computeInFlow(staticType, value, initialValue, context, valueStateSupplier);
+ InFlow inFlow =
+ computeInFlow(staticType, target, value, initialValue, context, valueStateSupplier);
if (inFlow == null) {
return null;
}
@@ -514,6 +523,7 @@
return ValueState.unknown();
}
assert inFlow.isBaseInFlow()
+ || inFlow.isAbstractComputation()
|| inFlow.isCastAbstractFunction()
|| inFlow.isIfThenElseAbstractFunction()
|| inFlow.isInstanceFieldReadAbstractFunction();
@@ -859,6 +869,7 @@
Value value,
Value initialValue,
ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
+ assert invoke.isInvokeStatic() || argumentIndex > 0;
assert value == initialValue || initialValue.getAliasedValue().isPhi();
NonEmptyValueState modeledState =
modeling.modelParameterStateForArgumentToFunction(
@@ -884,6 +895,7 @@
NonEmptyValueState inFlowState =
computeInFlowState(
parameterType,
+ singleTarget,
value,
initialValue,
context,
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeValueState.java
index 6eca904..478ec19 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeValueState.java
@@ -60,7 +60,6 @@
if (outStaticType != null) {
return WideningUtils.widenDynamicNonReceiverType(appView, joinedDynamicType, outStaticType);
} else {
- assert !joinedDynamicType.isUnknown();
return joinedDynamicType;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
index 0d0be90..6d1dc8f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FlowGraphStateProvider.java
@@ -18,7 +18,8 @@
// If the abstract function needs to perform state lookups, we restrict state lookups to the
// declared base in flow. This is important for arriving at the correct fix point.
if (abstractFunction.usesFlowGraphStateProvider()) {
- assert abstractFunction.isIfThenElseAbstractFunction()
+ assert abstractFunction.isAbstractComputation()
+ || abstractFunction.isIfThenElseAbstractFunction()
|| abstractFunction.isInstanceFieldReadAbstractFunction();
return new FlowGraphStateProvider() {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IfThenElseAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IfThenElseAbstractFunction.java
index e8a04d9..dc1354f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IfThenElseAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IfThenElseAbstractFunction.java
@@ -21,6 +21,7 @@
* true, then `u` is chosen. If the abstract value is false, then `v` is chosen. Otherwise, the
* result is unknown.
*/
+// TODO(b/302281503): Replace this by a ComputationTreeNode.
// TODO(b/302281503): Evaluate the impact of using the join of `u` and `v` instead of unknown when
// the condition does not evaluate to true or false.
public class IfThenElseAbstractFunction implements AbstractFunction {
@@ -74,7 +75,6 @@
if (variableState == null) {
// TODO(b/302281503): Conservatively return unknown for now. Investigate exactly when this
// happens and whether we can return something more precise instead of unknown.
- assert false;
return AbstractValue.unknown();
}
AbstractValue variableValue = variableState.getAbstractValue(appView);
@@ -95,7 +95,6 @@
assert inFlow.isBaseInFlow();
ValueState inFlowState = flowGraphStateProvider.getState(inFlow.asBaseInFlow(), () -> null);
if (inFlowState == null) {
- assert false;
return ValueState.unknown();
}
// TODO(b/302281503): The IfThenElseAbstractFunction is only used on input to base in flow.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopAndNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopAndNode.java
index 5bddb75..e31a8cd 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopAndNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeLogicalBinopAndNode.java
@@ -30,7 +30,13 @@
Function<MethodParameter, AbstractValue> argumentAssignment) {
assert getNumericType().isInt();
AbstractValue leftValue = left.evaluate(appView, argumentAssignment);
+ if (leftValue.isBottom()) {
+ return leftValue;
+ }
AbstractValue rightValue = right.evaluate(appView, argumentAssignment);
+ if (rightValue.isBottom()) {
+ return rightValue;
+ }
return AbstractCalculator.andIntegers(appView, leftValue, rightValue);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java
index 9976148..c287cb1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeNode.java
@@ -81,6 +81,11 @@
boolean isComputationLeaf();
+ @Override
+ default boolean usesFlowGraphStateProvider() {
+ return true;
+ }
+
default String toStringWithParenthesis() {
if (isComputationLeaf()) {
return toString();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopCompareNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopCompareNode.java
index 0b96bed..03559c9 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopCompareNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/computation/ComputationTreeUnopCompareNode.java
@@ -34,6 +34,9 @@
AppView<AppInfoWithLiveness> appView,
Function<MethodParameter, AbstractValue> argumentAssignment) {
AbstractValue operandValue = operand.evaluate(appView, argumentAssignment);
+ if (operandValue.isBottom()) {
+ return operandValue;
+ }
return type.evaluate(operandValue, appView);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
index 860f3d4..6e82b3b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
@@ -235,7 +235,7 @@
// Find a condition that can be used to distinguish program paths coming from the two
// predecessors.
ComputationTreeNode condition = leftState.getDifferentiatingPathConstraint(rightState);
- if (condition == null || !condition.isArgumentBitSetCompareNode()) {
+ if (!condition.isArgumentBitSetCompareNode()) {
return false;
}
// Extract the state corresponding to the program path where the argument is unused. If the
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ComposeReferences.java b/src/main/java/com/android/tools/r8/optimize/compose/ComposeReferences.java
index e38f9a4..633008d 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/ComposeReferences.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ComposeReferences.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.lens.GraphLens;
public class ComposeReferences {
@@ -42,6 +43,11 @@
this.updatedChangedFlagsMethod = updatedChangedFlagsMethod;
}
+ public boolean isComposable(ProgramDefinition definition) {
+ return definition.isProgramMethod()
+ && definition.asProgramMethod().getAnnotations().hasAnnotation(composableType);
+ }
+
public ComposeReferences rewrittenWithLens(GraphLens graphLens, GraphLens codeLens) {
return new ComposeReferences(
changedFieldName,
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
index a353fa1..f7f9bba 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
@@ -24,6 +24,7 @@
import java.util.Objects;
import java.util.function.Function;
+// TODO(b/302281503): Change this to implement ComputationTreeNode instead?
public class UpdateChangedFlagsAbstractFunction implements AbstractFunction {
private static final int changedLowBitMask = 0b001_001_001_001_001_001_001_001_001_001_0;
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index bcfa888..0e2508f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -142,7 +142,7 @@
.startsWith(options.itemFactory.dalvikAnnotationOptimizationPrefix)) {
return true;
}
- if (isComposableAnnotationToRetain(appView, annotation, kind, mode, options)) {
+ if (isComposableAnnotationToRetain(appView, annotation, kind, mode)) {
return true;
}
return shouldKeepNormalAnnotation(
@@ -309,13 +309,8 @@
}
private static boolean isComposableAnnotationToRetain(
- AppView<?> appView,
- DexAnnotation annotation,
- AnnotatedKind kind,
- Mode mode,
- InternalOptions options) {
- return options.getJetpackComposeOptions().isAnyOptimizationsEnabled()
- && mode.isInitialTreeShaking()
+ AppView<?> appView, DexAnnotation annotation, AnnotatedKind kind, Mode mode) {
+ return mode.isInitialTreeShaking()
&& kind.isMethod()
&& annotation
.getAnnotationType()
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 8abdba1..9f433ec 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2461,7 +2461,6 @@
public boolean enableDeadSwitchCaseElimination = true;
public boolean enableEnqueuerDeferredTracingForReferenceFields =
System.getProperty("com.android.tools.r8.disableEnqueuerDeferredTracing") == null;
- public boolean enableIfThenElseAbstractFunction = false;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableLegacyClassDefOrdering =
System.getProperty("com.android.tools.r8.enableLegacyClassDefOrdering") != null;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/RestartLambdaPropagationWithDefaultArgumentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/RestartLambdaPropagationWithDefaultArgumentTest.java
index 7eee8e6..63f24f0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/RestartLambdaPropagationWithDefaultArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/RestartLambdaPropagationWithDefaultArgumentTest.java
@@ -41,8 +41,6 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- .addOptionsModification(
- options -> options.getTestingOptions().enableIfThenElseAbstractFunction = true)
.enableInliningAnnotations()
.setMinApi(parameters)
.compile()