mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
replace DFAEngine implementation: use topological order
- add createGraph(flow) to ControlFlowUtil; - use DFSTBuilder to get SCCs for a control flow; - dfa iterates all components once; - dfa iterates strongly-connected component before convergence; - deprecated method DfaInstance#isForward() is removed; - remove unused DFAEngine#performDfa(info); - remove unused ControlFlowUtil#postOrder. New dfa analysis does not have a convergence issue in merge points. (IDEA-CR-28352)
This commit is contained in:
committed by
Vladimir Koshelev
parent
f2e6fca603
commit
35b77e8d95
@@ -21,9 +21,13 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.IntStack;
|
||||
import com.intellij.util.graph.Graph;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author oleg
|
||||
@@ -34,35 +38,27 @@ public class ControlFlowUtil {
|
||||
private ControlFlowUtil() {
|
||||
}
|
||||
|
||||
public static int[] postOrder(Instruction[] flow) {
|
||||
final int length = flow.length;
|
||||
int[] result = new int[length];
|
||||
boolean[] visited = new boolean[length];
|
||||
Arrays.fill(visited, false);
|
||||
final IntStack stack = new IntStack(length);
|
||||
@NotNull
|
||||
public static Graph<Instruction> createGraph(@NotNull final Instruction[] flow) {
|
||||
return new Graph<Instruction>() {
|
||||
@NotNull
|
||||
final private List<Instruction> myList = Arrays.asList(flow);
|
||||
|
||||
int N = 0;
|
||||
for (int i = 0; i < length; i++) { //graph might not be connected
|
||||
if (!visited[i]) {
|
||||
visited[i] = true;
|
||||
stack.clear();
|
||||
stack.push(i);
|
||||
|
||||
while (!stack.empty()) {
|
||||
final int num = stack.pop();
|
||||
result[N++] = num;
|
||||
for (Instruction succ : flow[num].allSucc()) {
|
||||
final int succNum = succ.num();
|
||||
if (!visited[succNum]) {
|
||||
visited[succNum] = true;
|
||||
stack.push(succNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Collection<Instruction> getNodes() {
|
||||
return myList;
|
||||
}
|
||||
}
|
||||
LOG.assertTrue(N == length);
|
||||
return result;
|
||||
|
||||
@Override
|
||||
public Iterator<Instruction> getIn(Instruction n) {
|
||||
return n.allPred().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Instruction> getOut(Instruction n) {
|
||||
return n.allSucc().iterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static int findInstructionNumberByElement(final Instruction[] flow, final PsiElement element){
|
||||
@@ -141,7 +137,7 @@ public class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Operation {
|
||||
public enum Operation {
|
||||
/**
|
||||
* CONTINUE is used to ignore previous elements processing for the node, however it doesn't stop the iteration process
|
||||
*/
|
||||
|
||||
@@ -18,12 +18,16 @@ import com.intellij.codeInsight.controlflow.ControlFlowUtil;
|
||||
import com.intellij.codeInsight.controlflow.Instruction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.util.graph.DFSTBuilder;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class DFAEngine<E> {
|
||||
private static final Logger LOG = Logger.getInstance(DFAEngine.class.getName());
|
||||
private static final double TIME_LIMIT = 10e9; // In nanoseconds, 10e9 = 1 sec
|
||||
private static final long TIME_LIMIT = 1_000_000_000L; // In nanoseconds, 1_000_000_000 = 1 sec
|
||||
|
||||
private final Instruction[] myFlow;
|
||||
|
||||
@@ -38,99 +42,101 @@ public class DFAEngine<E> {
|
||||
mySemilattice = semilattice;
|
||||
}
|
||||
|
||||
|
||||
public List<E> performDFA() throws DFALimitExceededException {
|
||||
final ArrayList<E> info = new ArrayList<>(myFlow.length);
|
||||
return performDFA(info);
|
||||
}
|
||||
|
||||
public List<E> performDFA(final List<E> info) throws DFALimitExceededException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Performing DFA\n" + "Instance: " + myDfa + " Semilattice: " + mySemilattice + "\nCon");
|
||||
}
|
||||
|
||||
// initializing dfa
|
||||
final E initial = myDfa.initial();
|
||||
for (int i = 0; i < myFlow.length; i++) {
|
||||
final int length = myFlow.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
info.add(i, initial);
|
||||
}
|
||||
|
||||
final boolean[] visited = new boolean[myFlow.length];
|
||||
|
||||
final int[] order = ControlFlowUtil.postOrder(myFlow);
|
||||
|
||||
// Count limit for number of iterations per worklist
|
||||
// Count limit for loops
|
||||
final int limit = getIterationLimit();
|
||||
int dfaCount = 0;
|
||||
final long startTime = System.nanoTime();
|
||||
DFSTBuilder<Instruction> dfsTBuilder = new DFSTBuilder<>(ControlFlowUtil.createGraph(myFlow));
|
||||
|
||||
for (int i = 0; i < myFlow.length; i++) {
|
||||
// Check if canceled
|
||||
ProgressManager.checkCanceled();
|
||||
int[] instructionNumToNNumber = new int[myFlow.length];
|
||||
for (int i = 0; i < myFlow.length; ++i) {
|
||||
instructionNumToNNumber[dfsTBuilder.getNodeByNNumber(i).num()] = i;
|
||||
}
|
||||
final int[] lastUpdate = new int[length];
|
||||
int count = 0;
|
||||
|
||||
if (System.nanoTime() - startTime > TIME_LIMIT) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Time limit exceeded");
|
||||
}
|
||||
throw new DFALimitExceededException("Time limit exceeded");
|
||||
}
|
||||
|
||||
// Iteration count per one worklist
|
||||
int count = 0;
|
||||
final Instruction instruction = myFlow[order[i]];
|
||||
final int number = instruction.num();
|
||||
|
||||
if (!visited[number]) {
|
||||
final Queue<Instruction> worklist = new LinkedList<>();
|
||||
worklist.add(instruction);
|
||||
visited[number] = true;
|
||||
|
||||
while (true) {
|
||||
// Check if canceled
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
// It is essential to apply this check!!!
|
||||
// This gives us more chances that resulting info will be closer to expected result
|
||||
// Also it is used as indicator that "equals" method is implemented correctly in E
|
||||
count++;
|
||||
if (count > limit) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Iteration count exceeded on worklist");
|
||||
}
|
||||
throw new DFALimitExceededException("Iteration count exceeded on worklist");
|
||||
}
|
||||
|
||||
final Instruction currentInstruction = worklist.poll();
|
||||
if (currentInstruction == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
final int currentNumber = currentInstruction.num();
|
||||
final E oldE = info.get(currentNumber);
|
||||
final E joinedE = join(currentInstruction, info);
|
||||
final E newE = myDfa.fun(joinedE, currentInstruction);
|
||||
if (!mySemilattice.eq(newE, oldE)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Number: " + currentNumber + " old: " + oldE.toString() + " new: " + newE.toString());
|
||||
}
|
||||
info.set(currentNumber, newE);
|
||||
for (Instruction next : getNext(currentInstruction)) {
|
||||
worklist.add(next);
|
||||
visited[next.num()] = true;
|
||||
}
|
||||
}
|
||||
List<Instruction> instructionsWithBackEdges = new ArrayList<>();
|
||||
for (Collection<Instruction> component : dfsTBuilder.getComponents()) {
|
||||
List<Instruction> sortedInstructions = new ArrayList<>(component);
|
||||
// component returns its instructions using getNodeByTNumber
|
||||
// unfortunately its ordering is not suitable for dataflow goals because
|
||||
// it does not start order in a SCC from entry nodes
|
||||
// so We should resort nodes in a SCC by NNumber
|
||||
sortedInstructions.sort(Comparator.comparingInt(it -> instructionNumToNNumber[it.num()]));
|
||||
instructionsWithBackEdges.clear();
|
||||
for (Instruction instruction : sortedInstructions) {
|
||||
applyTransferFunction(info, instruction);
|
||||
if (instruction.allPred().stream().anyMatch(predecessor ->
|
||||
instructionNumToNNumber[predecessor.num()] > instructionNumToNNumber[instruction.num()])) {
|
||||
instructionsWithBackEdges.add(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
// Move to another worklist
|
||||
dfaCount += count;
|
||||
int iteration = 0;
|
||||
while (true) {
|
||||
++iteration;
|
||||
final int currentIteration = iteration;
|
||||
boolean anyUpdates = false;
|
||||
for (Instruction instruction : instructionsWithBackEdges) {
|
||||
if (applyTransferFunction(info, instruction)) {
|
||||
lastUpdate[instruction.num()] = currentIteration;
|
||||
anyUpdates = true;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (!anyUpdates) {
|
||||
break;
|
||||
}
|
||||
for (Instruction instruction : sortedInstructions) {
|
||||
if (instruction.allPred().stream().anyMatch(it -> lastUpdate[it.num()] == currentIteration)
|
||||
&& applyTransferFunction(info, instruction)) {
|
||||
lastUpdate[instruction.num()] = currentIteration;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > limit || (System.nanoTime() - startTime) > TIME_LIMIT) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Iteration count exceeded on worklist");
|
||||
}
|
||||
throw new DFALimitExceededException("Iteration count exceeded on worklist");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Done in: " + (System.nanoTime() - startTime) / 10e6 + "ms. Ratio: " + dfaCount / myFlow.length);
|
||||
LOG.debug("Done in: " + (System.nanoTime() - startTime) / 10e6 + "ms. Ratio: " + count / length);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
private boolean applyTransferFunction(List<E> info, Instruction currentInstruction) {
|
||||
ProgressManager.checkCanceled();
|
||||
final int currentNumber = currentInstruction.num();
|
||||
final E oldE = info.get(currentNumber);
|
||||
final E joinedE = join(currentInstruction, info);
|
||||
final E newE = myDfa.fun(joinedE, currentInstruction);
|
||||
if (!mySemilattice.eq(newE, oldE)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Number: " + currentNumber + " old: " + oldE.toString() + " new: " + newE.toString());
|
||||
}
|
||||
info.set(currentNumber, newE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count limit for dfa number of iterations.
|
||||
@@ -146,15 +152,11 @@ public class DFAEngine<E> {
|
||||
}
|
||||
|
||||
private E join(final Instruction instruction, final List<E> info) {
|
||||
final Iterable<? extends Instruction> prev = myDfa.isForward() ? instruction.allPred() : instruction.allSucc();
|
||||
final Iterable<? extends Instruction> prev = instruction.allPred();
|
||||
final ArrayList<E> prevInfos = new ArrayList<>();
|
||||
for (Instruction i : prev) {
|
||||
prevInfos.add(info.get(i.num()));
|
||||
}
|
||||
return mySemilattice.join(prevInfos);
|
||||
}
|
||||
|
||||
private Collection<Instruction> getNext(final Instruction curr) {
|
||||
return myDfa.isForward() ? curr.allSucc() : curr.allPred();
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,4 @@ public interface DfaInstance<E> {
|
||||
|
||||
@NotNull
|
||||
E initial();
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
boolean isForward();
|
||||
}
|
||||
|
||||
@@ -107,8 +107,4 @@ public class PyReachingDefsDfaInstance implements DfaMapInstance<ScopeVariable>
|
||||
public DFAMap<ScopeVariable> initial() {
|
||||
return INITIAL_MAP;
|
||||
}
|
||||
|
||||
public boolean isForward() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
f1 = 10
|
||||
print(f1)
|
||||
|
||||
if f1:
|
||||
a1 = 1
|
||||
a2 = 2
|
||||
a3 = 3
|
||||
a4 = 4
|
||||
a5 = 5
|
||||
a6 = 6
|
||||
elif f1:
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
a1 = 1
|
||||
a2 = 2
|
||||
a3 = 3
|
||||
a4 = 4
|
||||
a5 = 5
|
||||
elif f1:
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
a1 = 1
|
||||
a2 = 2
|
||||
a3 = 3
|
||||
a4 = 4
|
||||
elif f1:
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
a1 = 1
|
||||
a2 = 2
|
||||
a3 = 3
|
||||
elif f1:
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
a1 = 1
|
||||
a2 = 2
|
||||
elif f1:
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
a1 = 1
|
||||
else:
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
print(f1)
|
||||
+4
@@ -197,6 +197,10 @@ public class PyUnboundLocalVariableInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testTooLargeToAnalyze() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user