Fix incorrect join of path constraint elements
Change-Id: Ie21c8ba6235f5ec9c12810e11c4fd80f7b2fd988
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java
index 11fe070..c61df2a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisTransferFunction.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
+import com.android.tools.r8.ir.analysis.path.state.ConcretePathConstraintAnalysisState;
import com.android.tools.r8.ir.analysis.path.state.PathConstraintAnalysisState;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -41,6 +42,14 @@
}
@Override
+ public PathConstraintAnalysisState computeInitialState(
+ BasicBlock entryBlock, PathConstraintAnalysisState bottom) {
+ // Intentionally returns an empty state instead of BOTTOM, as BOTTOM is used to represent the
+ // path constraint for unreachable program points.
+ return new ConcretePathConstraintAnalysisState();
+ }
+
+ @Override
public PathConstraintAnalysisState computeBlockEntryState(
BasicBlock block, BasicBlock predecessor, PathConstraintAnalysisState predecessorExitState) {
if (predecessorExitState.isUnknown()) {
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 d056fba..7bcb650 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
@@ -3,8 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.analysis.path.state;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
-import com.android.tools.r8.utils.CollectionUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -44,10 +44,13 @@
// TODO(b/302281503): Consider changing this to an ImmutableMap.
private final Map<ComputationTreeNode, PathConstraintKind> pathConstraints;
+ public ConcretePathConstraintAnalysisState() {
+ this.pathConstraints = Collections.emptyMap();
+ }
+
ConcretePathConstraintAnalysisState(
Map<ComputationTreeNode, PathConstraintKind> pathConstraints) {
this.pathConstraints = pathConstraints;
- assert !isEffectivelyBottom();
}
static ConcretePathConstraintAnalysisState create(
@@ -58,13 +61,23 @@
@Override
public PathConstraintAnalysisState add(ComputationTreeNode pathConstraint, boolean negate) {
- if (pathConstraints.get(pathConstraint) == PathConstraintKind.DISABLED) {
- // There is a loop.
- return this;
+ PathConstraintKind previousKind = pathConstraints.get(pathConstraint);
+ if (previousKind != null) {
+ if (previousKind == PathConstraintKind.DISABLED) {
+ // There is a loop.
+ 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();
}
// No jumps can dominate the entry of their own block, so when adding the condition of a jump
// this cannot currently be active.
- assert !pathConstraints.containsKey(pathConstraint);
Map<ComputationTreeNode, PathConstraintKind> newPathConstraints =
new HashMap<>(pathConstraints.size() + 1);
newPathConstraints.putAll(pathConstraints);
@@ -86,8 +99,35 @@
return this;
}
- private boolean isEffectivelyBottom() {
- return pathConstraints.isEmpty();
+ @Override
+ public boolean isGreaterThanOrEquals(AppView<?> appView, PathConstraintAnalysisState state) {
+ if (state.isConcrete()) {
+ return isGreaterThanOrEquals(state.asConcreteState());
+ }
+ return super.isGreaterThanOrEquals(appView, state);
+ }
+
+ private boolean isGreaterThanOrEquals(ConcretePathConstraintAnalysisState other) {
+ // The current state must contain all keys of the other state.
+ if (pathConstraints.size() < other.pathConstraints.size()) {
+ return false;
+ }
+ for (ComputationTreeNode otherPathConstraint : other.pathConstraints.keySet()) {
+ if (!pathConstraints.containsKey(otherPathConstraint)) {
+ return false;
+ }
+ }
+ // The current path constraint kinds must be the join of the kinds.
+ for (Entry<ComputationTreeNode, PathConstraintKind> entry : pathConstraints.entrySet()) {
+ ComputationTreeNode pathConstraint = entry.getKey();
+ PathConstraintKind kind = entry.getValue();
+ PathConstraintKind otherKind = other.pathConstraints.get(pathConstraint);
+ PathConstraintKind joinKind = kind.join(otherKind);
+ if (kind != joinKind) {
+ return false;
+ }
+ }
+ return true;
}
public boolean isNegated(ComputationTreeNode pathConstraint) {
@@ -117,40 +157,29 @@
}
public ConcretePathConstraintAnalysisState join(ConcretePathConstraintAnalysisState other) {
- Map<ComputationTreeNode, PathConstraintKind> newPathConstraints = join(other, null);
- newPathConstraints = other.join(this, newPathConstraints);
- if (newPathConstraints == null) {
- assert equals(other);
+ if (isGreaterThanOrEquals(other)) {
return this;
}
+ if (other.isGreaterThanOrEquals(this)) {
+ return other;
+ }
+ Map<ComputationTreeNode, PathConstraintKind> newPathConstraints =
+ new HashMap<>(pathConstraints.size() + other.pathConstraints.size());
+ join(other, newPathConstraints);
+ other.join(this, newPathConstraints);
return new ConcretePathConstraintAnalysisState(newPathConstraints);
}
- private Map<ComputationTreeNode, PathConstraintKind> join(
+ private void join(
ConcretePathConstraintAnalysisState other,
Map<ComputationTreeNode, PathConstraintKind> newPathConstraints) {
for (Entry<ComputationTreeNode, PathConstraintKind> entry : pathConstraints.entrySet()) {
ComputationTreeNode pathConstraint = entry.getKey();
PathConstraintKind kind = entry.getValue();
- PathConstraintKind otherKind =
- other.pathConstraints.getOrDefault(pathConstraint, PathConstraintKind.DISABLED);
+ PathConstraintKind otherKind = other.pathConstraints.get(pathConstraint);
PathConstraintKind joinKind = kind.join(otherKind);
- if (newPathConstraints == null) {
- if (joinKind == kind) {
- continue;
- }
- // We need to make a change. Allocate the new result.
- Map<ComputationTreeNode, PathConstraintKind> copy =
- new HashMap<>(pathConstraints.size() + other.pathConstraints.size());
- CollectionUtils.<Entry<ComputationTreeNode, PathConstraintKind>>forEachUntilExclusive(
- pathConstraints.entrySet(),
- previousEntry -> copy.put(previousEntry.getKey(), previousEntry.getValue()),
- maybeEntry -> maybeEntry.getKey() == pathConstraint);
- newPathConstraints = copy;
- }
newPathConstraints.put(pathConstraint, joinKind);
}
- return newPathConstraints;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
index 161f1a1..450c351 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
@@ -105,7 +105,7 @@
@Override
public int hashCode() {
- int hash = 31 * (31 * (31 + Long.hashCode(minInclusive)) + Long.hashCode(maxInclusive));
+ int hash = 31 * (31 + Long.hashCode(minInclusive)) + Long.hashCode(maxInclusive);
assert hash == Objects.hash(minInclusive, maxInclusive);
return hash;
}