package dk.brics.xact.analysis.dataflow;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import dk.brics.xact.analysis.Debug;
import dk.brics.xact.analysis.flowgraph.Edge;
import dk.brics.xact.analysis.flowgraph.FlowGraph;
import dk.brics.xact.analysis.flowgraph.Statement;
import dk.brics.xact.analysis.flowgraph.VariableFilter;
/**
* Performs a dataflow analysis on a flow graph, given an {@link AnalysisInterface} instance.
*/
public class DataflowAnalyzer<ElementType> {
private FlowGraph graph;
private AnalysisInterface<ElementType> lat;
private Map<Statement,ElementType> before = new LinkedHashMap<Statement,ElementType>();
private Map<Statement,ElementType> after = new LinkedHashMap<Statement,ElementType>();
private Set<Statement> visited = new HashSet<Statement>();
private WorkList wl;
/**
* Constructs a {@link DataflowAnalyzer} for analyzing the given flow graph.
*/
public DataflowAnalyzer(FlowGraph graph, AnalysisInterface<ElementType> lat) {
this.graph = graph;
this.lat = lat;
for (Statement s : graph.getNodes()) {
before.put(s, lat.newBottomElement());
after.put(s, lat.newBottomElement());
}
}
/**
* Computes the least fixed point iteratively.
*/
public void run() {
boolean forward = lat.isForward();
// initialize worklist
wl = new WorkList(graph);
for (Statement s : lat.getInitial(graph))
wl.put(s);
// iterate until fixed point
int steps = 0;
while (!wl.isEmpty()) {
Statement s = wl.removeFirst();
boolean changed;
if (forward)
changed = lat.transfer(before.get(s), s, after.get(s));
else
changed = lat.transfer(after.get(s), s, before.get(s));
steps++;
if (!visited.contains(s)) {
changed = true;
visited.add(s);
}
if (changed) {
if (forward) {
for (Edge<Statement,VariableFilter> se : graph.getOutEdges(s)) {
Statement ss = se.getTo();
if (lat.merge(after.get(s), se.getData(), before.get(ss)) || !visited.contains(ss))
wl.put(ss);
}
} else {
for (Edge<Statement,VariableFilter> se : graph.getInEdges(s)) {
Statement ss = se.getFrom();
if (lat.merge(before.get(s), se.getData(), after.get(ss)) || !visited.contains(ss))
wl.put(ss);
}
}
}
}
Debug.println(3, true, "Iteration steps: " + steps + ", steps/nodes: " + (float)steps/graph.getNodes().size());
}
/**
* Returns the lattice element for the flow into the given statement.
*/
public ElementType getFlowIn(Statement s) {
return before.get(s);
}
/**
* Returns the lattice element for the flow out of the given statement.
*/
public ElementType getFlowOut(Statement s) {
return after.get(s);
}
private static class WorkList {
private TreeMap<Integer,Statement> list = new TreeMap<Integer,Statement>();
private HashMap<Statement,Integer> order = new HashMap<Statement,Integer>();
/**
* Constructs a new empty worklist.
* Nodes are ordered according to their distance from the entries.
*/
public WorkList(FlowGraph graph) {
LinkedList<Statement> queue = new LinkedList<Statement>();
HashMap<Statement,Integer> indegree = new HashMap<Statement,Integer>();
int o = 0;
queue.addAll(graph.getEntries());
while (!queue.isEmpty()) {
Statement s = queue.removeFirst();
if (!order.containsKey(s)) {
int so = o++;
order.put(s, so);
for (Edge<Statement,VariableFilter> out : graph.getOutEdges(s)) {
Statement s2 = out.getTo();
if (indegree.containsKey(s2))
indegree.put(s2, indegree.get(s2)+1);
else
indegree.put(s2, 1);
if (indegree.get(s2) == graph.getInEdges(s2).size())
queue.addFirst(s2);
else
queue.addLast(s2);
}
}
}
// add remaining statements
for (Statement stm : graph.getNodes()) {
if (!order.containsKey(stm)) {
order.put(stm, o++);
}
}
}
public void put(Statement s) {
list.put(order.get(s), s);
}
public boolean isEmpty() {
return list.isEmpty();
}
public Statement removeFirst() {
return list.remove(list.firstKey());
}
}
}