Test path constraint analysis on a small example
Bug: b/302281503
Change-Id: I93456ef564a0cba740d060ca7de5dfd51e18ac14
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
index 73eeb55..550019d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
@@ -44,6 +44,10 @@
this.blockExitStates = blockExitStates;
}
+ public StateType getBlockExitState(Block block) {
+ return blockExitStates.get(block);
+ }
+
public StateType join(AppView<?> appView) {
StateType result = null;
for (StateType blockExitState : blockExitStates.values()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java
index 492b2a0..dd5b2c9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysis.java
@@ -37,10 +37,11 @@
public class PathConstraintAnalysis
extends IntraproceduralDataflowAnalysis<PathConstraintAnalysisState> {
- public PathConstraintAnalysis(
- AppView<AppInfoWithLiveness> appView,
- IRCode code,
- PathConstraintAnalysisTransferFunction transfer) {
- super(appView, PathConstraintAnalysisState.bottom(), code, transfer);
+ public PathConstraintAnalysis(AppView<AppInfoWithLiveness> appView, IRCode code) {
+ super(
+ appView,
+ PathConstraintAnalysisState.bottom(),
+ code,
+ new PathConstraintAnalysisTransferFunction(appView.abstractValueFactory()));
}
}
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 47eeba1..361ed99 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
@@ -88,6 +88,19 @@
return newPathConstraints;
}
+ public Set<ComputationTreeNode> getPathConstraints() {
+ return pathConstraints;
+ }
+
+ public Set<ComputationTreeNode> getNegatedPathConstraints() {
+ return negatedPathConstraints;
+ }
+
+ @Override
+ public boolean isConcrete() {
+ return true;
+ }
+
@Override
public ConcretePathConstraintAnalysisState asConcreteState() {
return this;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintAnalysisState.java b/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintAnalysisState.java
index 09f16c2..fea03cc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintAnalysisState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/path/state/PathConstraintAnalysisState.java
@@ -25,6 +25,10 @@
return false;
}
+ public boolean isConcrete() {
+ return false;
+ }
+
public boolean isUnknown() {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisUnitTest.java b/src/test/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisUnitTest.java
new file mode 100644
index 0000000..56573a5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/analysis/path/PathConstraintAnalysisUnitTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2024, 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.path;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult;
+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.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeNode;
+import com.android.tools.r8.optimize.argumentpropagation.computation.ComputationTreeUnopCompareNode;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PathConstraintAnalysisUnitTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ AndroidApp app =
+ AndroidApp.builder()
+ .addProgramFiles(ToolHelper.getClassFileForTestClass(Main.class))
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build();
+ AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(app);
+ CodeInspector inspector = new CodeInspector(app);
+ IRCode code =
+ inspector.clazz(Main.class).uniqueMethodWithOriginalName("greet").buildIR(appView);
+ PathConstraintAnalysis analysis = new PathConstraintAnalysis(appView, code);
+ DataflowAnalysisResult result = analysis.run(code.entryBlock());
+ assertTrue(result.isSuccessfulAnalysisResult());
+ SuccessfulDataflowAnalysisResult<BasicBlock, PathConstraintAnalysisState> successfulResult =
+ result.asSuccessfulAnalysisResult();
+
+ // Inspect ENTRY state.
+ PathConstraintAnalysisState entryConstraint =
+ successfulResult.getBlockExitState(code.entryBlock());
+ assertTrue(entryConstraint.isBottom());
+
+ // Inspect THEN state.
+ PathConstraintAnalysisState thenConstraint =
+ successfulResult.getBlockExitState(code.entryBlock().exit().asIf().getTrueTarget());
+ assertTrue(thenConstraint.isConcrete());
+
+ ConcretePathConstraintAnalysisState concreteThenConstraint = thenConstraint.asConcreteState();
+ assertEquals(1, concreteThenConstraint.getPathConstraints().size());
+ assertEquals(0, concreteThenConstraint.getNegatedPathConstraints().size());
+
+ ComputationTreeNode thenPathConstraint =
+ concreteThenConstraint.getPathConstraints().iterator().next();
+ assertTrue(thenPathConstraint instanceof ComputationTreeUnopCompareNode);
+
+ // Inspect ELSE state.
+ PathConstraintAnalysisState elseConstraint =
+ successfulResult.getBlockExitState(code.entryBlock().exit().asIf().fallthroughBlock());
+ assertTrue(elseConstraint.isConcrete());
+
+ ConcretePathConstraintAnalysisState concreteElseConstraint = elseConstraint.asConcreteState();
+ assertEquals(0, concreteElseConstraint.getPathConstraints().size());
+ assertEquals(1, concreteElseConstraint.getNegatedPathConstraints().size());
+
+ ComputationTreeNode elsePathConstraint =
+ concreteElseConstraint.getNegatedPathConstraints().iterator().next();
+ assertEquals(thenPathConstraint, elsePathConstraint);
+
+ // Inspect RETURN state.
+ PathConstraintAnalysisState returnConstraint =
+ successfulResult.getBlockExitState(code.computeNormalExitBlocks().get(0));
+ assertTrue(returnConstraint.isConcrete());
+
+ ConcretePathConstraintAnalysisState concreteReturnConstraint =
+ returnConstraint.asConcreteState();
+ assertEquals(1, concreteReturnConstraint.getPathConstraints().size());
+ assertEquals(
+ concreteReturnConstraint.getPathConstraints(),
+ concreteReturnConstraint.getNegatedPathConstraints());
+ }
+
+ static class Main {
+
+ public static void greet(String greeting, int flags) {
+ if ((flags & 1) != 0) {
+ greeting = "Hello, world!";
+ }
+ System.out.println(greeting);
+ }
+ }
+}