Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import de.peeeq.datastructures.GraphInterpreter;
import de.peeeq.wurstscript.ast.AstElementWithBody;
import de.peeeq.wurstscript.ast.WStatement;
import de.peeeq.wurstscript.utils.Utils;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;

Expand Down Expand Up @@ -62,7 +63,13 @@ protected Collection<WStatement> getIncidentNodes(WStatement t) {

// 4. Analyze each SCC in topological order
for (List<WStatement> scc : sccs) {
analyzeComponent(scc);
// Nodes are popped from the SCC stack in reverse discovery order.
// Restore forward CFG order so facts propagate through the component
// without requiring a separate pass for nearly every statement.
Collections.reverse(scc);
if (!analyzeComponent(scc)) {
return;
}
}


Expand All @@ -72,19 +79,25 @@ protected Collection<WStatement> getIncidentNodes(WStatement t) {
method.checkFinal(finalState);
}

private void analyzeComponent(List<WStatement> scc) {
private boolean analyzeComponent(List<WStatement> scc) {
Queue<WStatement> worklist = new ArrayDeque<>(scc);
Set<WStatement> queued = new ObjectOpenHashSet<>(scc);
Set<WStatement> componentMembers = new ObjectOpenHashSet<>(scc);

int iterations = 0;
int maxIterations = scc.size() * scc.size() + 100; // Heuristic limit to prevent infinite loops

while (!worklist.isEmpty()) {
if (iterations++ > maxIterations) {
// This should ideally not happen in a correct CFG with a monotonic transfer function
throw new RuntimeException("Dataflow analysis did not converge. Possible infinite loop in component.");
f.addError("Internal compiler error: " + method.getClass().getSimpleName()
+ " did not converge while analyzing " + Utils.printElement(f)
+ " (component size " + scc.size() + ", " + iterations
+ " worklist iterations). Further dataflow checks for this body were skipped.");
return false;
}

WStatement s = worklist.poll();
queued.remove(s);

// Merge states from all predecessors
Collection<T> predecessorStates = get(s.attrPreviousStatements());
Expand All @@ -98,12 +111,13 @@ private void analyzeComponent(List<WStatement> scc) {

// If the value changed, add successors within the same SCC to the worklist
for (WStatement succ : s.attrNextStatements()) {
if (scc.contains(succ)) {
if (componentMembers.contains(succ) && queued.add(succ)) {
worklist.add(succ);
}
}
}
}
return true;
}

private List<WStatement> getAllStatements() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,45 @@ public void destroyThisDataflowTest() {
);
}

@Test
public void dataflowConvergesForManyMutatedLoopLocals() {
testAssertOkLines(false,
"package test",
"function probe()",
" int a01 = 0",
" int a02 = 0",
" int a03 = 0",
" int a04 = 0",
" int a05 = 0",
" int a06 = 0",
" int a07 = 0",
" int a08 = 0",
" int a09 = 0",
" int a10 = 0",
" int a11 = 0",
" int a12 = 0",
" int a13 = 0",
" int a14 = 0",
" int a15 = 0",
" int a16 = 0",
" while true",
" a01++",
" a02++",
" a03++",
" a04++",
" a05++",
" a06++",
" a07++",
" a08++",
" a09++",
" a10++",
" a11++",
" a12++",
" a13++",
" a14++",
" a15++",
" a16++"
);
}

}
Loading