Bail out of control-flow analysis on error states
This also changes the worklist usage to avoid pushing duplicates.
Bug: b/295349278
Change-Id: I924e91f169a15eaf196647044a183917d2e57a83
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java
index ad58a5c..6c77bb9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractTransferFunction.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.analysis.framework.intraprocedural;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.FailedDataflowAnalysisResult;
/**
* A transfer function that defines the abstract semantics of the instructions in the program
@@ -57,4 +58,9 @@
StateType throwState) {
return throwState;
}
+
+ default FailedDataflowAnalysisResult createFailedAnalysisResult(
+ Instruction instruction, TransferFunctionResult<StateType> transferResult) {
+ return new FailedDataflowAnalysisResult();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java
index 3a8b75c..f81a516 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java
@@ -73,7 +73,7 @@
private DataflowAnalysisResult run(WorkList<Block> worklist, Timing timing) {
while (worklist.hasNext()) {
- Block initialBlock = worklist.next();
+ Block initialBlock = worklist.removeSeen();
Block block = initialBlock;
Block end = null;
// Compute the abstract state upon entry to the basic block, by joining all the predecessor
@@ -83,7 +83,7 @@
TransferFunctionResult<StateType> blockResult = transfer.applyBlock(initialBlock, state);
if (blockResult.isFailedTransferResult()) {
- return new FailedDataflowAnalysisResult();
+ return transfer.createFailedAnalysisResult(null, state);
}
state = blockResult.asAbstractState();
@@ -105,14 +105,15 @@
TransferFunctionResult<StateType> transferResult =
transfer.apply(instruction, previousState);
if (transferResult.isFailedTransferResult()) {
- timing.end();
- return TraversalContinuation.doBreak(new FailedDataflowAnalysisResult());
+ return TraversalContinuation.doBreak(
+ transfer.createFailedAnalysisResult(instruction, transferResult));
}
assert transferResult.isAbstractState();
return TraversalContinuation.doContinue(transferResult.asAbstractState());
},
state);
if (traversalContinuation.isBreak()) {
+ timing.end();
return traversalContinuation.asBreak().getValue();
}
state = traversalContinuation.asContinue().getValue();
@@ -128,7 +129,7 @@
// Update the block exit state, and re-enqueue all successor blocks if the abstract state
// changed.
if (setBlockExitState(end, state)) {
- cfg.forEachSuccessor(end, worklist::addIgnoringSeenSet);
+ cfg.forEachSuccessor(end, worklist::addIfNotSeen);
}
// Add the computed exit state to the entry state of each normal successor that satisfies the
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
index 437d01d..b15a35f 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
@@ -100,6 +100,11 @@
return false;
}
+ @Override
+ public boolean isFailedTransferResult() {
+ return isError();
+ }
+
public ErroneousCfFrameState asError() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
index 7d65c1e..5d149ed 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
@@ -24,6 +24,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.DataflowAnalysisResult;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.FailedDataflowAnalysisResult;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfBlock;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.cf.CfControlFlowGraph;
@@ -174,6 +175,15 @@
CfIntraproceduralDataflowAnalysis<CfFrameState> analysis =
new CfIntraproceduralDataflowAnalysis<>(appView, CfFrameState.bottom(), cfg, transfer);
DataflowAnalysisResult result = analysis.run(cfg.getEntryBlock());
+ if (result.isFailedAnalysisResult()) {
+ FailedCfAnalysisResult failedResult = (FailedCfAnalysisResult) result;
+ int index =
+ failedResult.instruction == null
+ ? 0
+ : code.getInstructions().indexOf(failedResult.instruction);
+ helper.registerUnverifiableCode(method, index, failedResult.errorState);
+ return;
+ }
assert result.isSuccessfulAnalysisResult();
for (CfBlock block : cfg.getBlocks()) {
if (analysis.isIntermediateBlock(block)) {
@@ -181,6 +191,8 @@
}
CfFrameState state = analysis.computeBlockEntryState(block);
if (state.isError()) {
+ // Any error should terminate with a "FailedAnalysisResult" above.
+ assert false;
helper.registerUnverifiableCode(method, 0, state.asError());
return;
}
@@ -192,6 +204,8 @@
helper.processInstruction(instruction, state);
state = transfer.apply(instruction, state).asAbstractState();
if (state.isError()) {
+ // Any error should terminate with a "FailedAnalysisResult" above.
+ assert false;
helper.registerUnverifiableCode(method, instructionIndex, state.asError());
return;
}
@@ -250,6 +264,17 @@
methods.forEach(method -> reporter.warning(unverifiableCodeDiagnostics.get(method)));
}
+ private static class FailedCfAnalysisResult extends FailedDataflowAnalysisResult {
+
+ private final CfInstruction instruction;
+ private final ErroneousCfFrameState errorState;
+
+ public FailedCfAnalysisResult(CfInstruction instruction, ErroneousCfFrameState errorState) {
+ this.instruction = instruction;
+ this.errorState = errorState;
+ }
+ }
+
private class TransferFunction
implements AbstractTransferFunction<CfBlock, CfInstruction, CfFrameState> {
@@ -262,6 +287,13 @@
}
@Override
+ public FailedDataflowAnalysisResult createFailedAnalysisResult(
+ CfInstruction instruction, TransferFunctionResult<CfFrameState> transferResult) {
+ ErroneousCfFrameState errorState = (ErroneousCfFrameState) transferResult;
+ return new FailedCfAnalysisResult(instruction, errorState);
+ }
+
+ @Override
public TransferFunctionResult<CfFrameState> apply(
CfInstruction instruction, CfFrameState state) {
return instruction.evaluate(state, appView, config);