package dk.brics.xact.analysis.transformations;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import dk.brics.xact.analysis.Debug;
import dk.brics.xact.analysis.dataflow.DataflowAnalyzer;
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.Variable;
import dk.brics.xact.analysis.flowgraph.VariableFilter;
import dk.brics.xact.analysis.flowgraph.statements.AnalyzeStm;
import dk.brics.xact.analysis.flowgraph.statements.ArrayReadStm;
import dk.brics.xact.analysis.flowgraph.statements.ArrayWriteStm;
import dk.brics.xact.analysis.flowgraph.statements.ArrayWriteStringStm;
import dk.brics.xact.analysis.flowgraph.statements.Assignment;
import dk.brics.xact.analysis.flowgraph.statements.CallStm;
import dk.brics.xact.analysis.flowgraph.statements.CastStm;
import dk.brics.xact.analysis.flowgraph.statements.CheckStm;
import dk.brics.xact.analysis.flowgraph.statements.ConcatStm;
import dk.brics.xact.analysis.flowgraph.statements.ConstStm;
import dk.brics.xact.analysis.flowgraph.statements.CopyStm;
import dk.brics.xact.analysis.flowgraph.statements.EmptyStm;
import dk.brics.xact.analysis.flowgraph.statements.EscapeStm;
import dk.brics.xact.analysis.flowgraph.statements.GapifyStm;
import dk.brics.xact.analysis.flowgraph.statements.GetStm;
import dk.brics.xact.analysis.flowgraph.statements.InsertStm;
import dk.brics.xact.analysis.flowgraph.statements.NodeStm;
import dk.brics.xact.analysis.flowgraph.statements.NopStm;
import dk.brics.xact.analysis.flowgraph.statements.PlugStm;
import dk.brics.xact.analysis.flowgraph.statements.RemoveStm;
import dk.brics.xact.analysis.flowgraph.statements.SetStm;
import dk.brics.xact.analysis.flowgraph.statements.StatementVisitor;
import dk.brics.xact.analysis.flowgraph.statements.UnknownStm;
import dk.brics.xact.analysis.flowgraph.statements.ValidateStm;
import dk.brics.xact.analysis.flowgraph.statements.VarStm;
import dk.brics.xact.analysis.xmlgraph.XMLGraphChecker;
/**
* Reaching definitions analysis and copy propagation.
*/
public class DefUseTransformer {
/**
* Constructs a new <code>DefUseTransformer</code>.
*/
public DefUseTransformer() {}
/**
* Transforms the given flow graph.
*/
public void run(final FlowGraph graph) {
// run reaching definitions analysis
DataflowAnalyzer<Map<Variable,Set<Assignment>>> rda =
new DataflowAnalyzer<Map<Variable,Set<Assignment>>>(graph, new ReachingDefinitionsAnalysis());
Debug.println(2, true, "Performing reaching definitions analysis");
rda.run();
// remove nop nodes and all edges, find new entries
Set<Statement> nodes = graph.getNodes();
for (Statement s : new HashSet<Statement>(nodes))
if (s instanceof NopStm) {
/*// alternative to setting entries as below
if (graph.getEntries().contains(s)) {
for (Edge<Statement,VariableFilter> e : graph.getOutEdges(s))
graph.addEntry(e.getTo());
}
for (Edge<Statement,VariableFilter> in : graph.getInEdges(s))
for (Edge<Statement,VariableFilter> out : graph.getOutEdges(s))
graph.addEdge(in.getFrom(), out.getTo(), out.getData());
*/
graph.removeNode(s); // TODO: retain hotspots?
}
graph.clearEdges();
// put def-use edges
for (Statement s : graph.getNodes()) {
final Map<Variable,Set<Assignment>> var_defs = rda.getFlowIn(s);
s.visitBy(new StatementVisitor() {
private void linkdefs(Statement use, Variable var) {
Set<Assignment> defs = var_defs.get(var);
if (defs != null)
for (Assignment def : defs)
graph.addEdge(def, use, new VariableFilter(var));
}
// statements that contain a definition but no uses - these always become entry nodes
public void visitConstStm(ConstStm s) {
graph.addEntry(s);
}
public void visitUnknownStm(UnknownStm s) {
graph.addEntry(s);
}
public void visitEmptyStm(EmptyStm s) {
graph.addEntry(s); // actually, no need to make this an entry since the value is empty anyway
}
// statements that contain some uses - make def-use edges
public void visitAnalyzeStm(AnalyzeStm s) {
linkdefs(s, s.getBase());
}
public void visitCheckStm(CheckStm s) {
if (s.getBase() != null)
linkdefs(s, s.getBase());
else
graph.addEntry(s);
}
public void visitPlugStm(PlugStm s) {
linkdefs(s, s.getBase());
if (s.getXMLSource() != null)
linkdefs(s, s.getXMLSource());
else
graph.addEntry(s);
}
public void visitInsertStm(InsertStm s) {
linkdefs(s, s.getBase());
if (s.getXMLSource() != null)
linkdefs(s, s.getXMLSource());
else
graph.addEntry(s);
}
public void visitGapifyStm(GapifyStm s) {
linkdefs(s, s.getBase());
}
public void visitValidateStm(ValidateStm s) {
if (s.getBase() != null)
linkdefs(s, s.getBase());
else
graph.addEntry(s);
}
public void visitVarStm(VarStm s) {
linkdefs(s, s.getSource());
}
public void visitGetStm(GetStm s) {
linkdefs(s, s.getBase());
}
public void visitConcatStm(ConcatStm s) {
linkdefs(s, s.getXMLSource());
}
public void visitCopyStm(CopyStm s) {
linkdefs(s, s.getBase());
if (s.getFirstChild() != null)
linkdefs(s, s.getFirstChild());
if (s.getFirstAttr() != null)
linkdefs(s, s.getFirstAttr());
if (s.getNextNode() != null)
linkdefs(s, s.getNextNode());
if (s.getFirstChild() == null && s.getFirstAttr() == null && s.getNextNode() == null)
graph.addEntry(s);
}
public void visitCastStm(CastStm s) {
linkdefs(s, s.getBase());
}
public void visitNodeStm(NodeStm s) {
if (s.getFirstChild() != null)
linkdefs(s, s.getFirstChild());
if (s.getFirstAttr() != null)
linkdefs(s, s.getFirstAttr());
if (s.getNextNode() != null)
linkdefs(s, s.getNextNode());
if (s.getFirstChild() == null && s.getFirstAttr() == null && s.getNextNode() == null)
graph.addEntry(s);
}
public void visitRemoveStm(RemoveStm s) {
linkdefs(s, s.getBase());
}
public void visitSetStm(SetStm s) {
linkdefs(s, s.getBase());
if (s.getXMLSource() != null)
linkdefs(s, s.getXMLSource());
else
graph.addEntry(s);
}
// statements that contain no definitions and no uses
public void visitNopStm(NopStm s) {
// do nothing
}
// not in this phase
public void visitArrayReadStm(ArrayReadStm s) {
}
public void visitArrayWriteStm(ArrayWriteStm s) {
}
public void visitCallStm(CallStm s) {
}
public void visitEscapeStm(EscapeStm s) {
}
public void visitArrayWriteStringStm(ArrayWriteStringStm s) {
}
});
}
// copy propagation
for (Statement s : new LinkedHashSet<Statement>(graph.getNodes())) {
if (s instanceof VarStm) {
VarStm vs = (VarStm)s;
if (graph.getInEdges(vs).size() == 1) {
Assignment pred = (Assignment)(graph.getInEdges(vs).iterator().next()).getFrom();
if (graph.getOutEdges(pred).size()==1) {
// variable assignment with unique predecessor node that has no other successors - bypass this assignment
pred.setDest(vs.getDest());
for (Edge<Statement,VariableFilter> e : graph.getOutEdges(vs))
graph.addEdge(pred, e.getTo(), e.getData()); // XXX: ok to lose some edge info?
graph.removeNode(vs);
}
} else if (graph.getOutEdges(vs).size() == 1) {
Statement succ = graph.getOutEdges(vs).iterator().next().getTo();
if (graph.getInEdges(succ).size()==1) { // TODO: copy propagation could be improved by not requiring "no other predecessors"
// variable assignment with unique successor node that has no other predecessors - bypass this assignment
final Variable vsrc = vs.getSource();
succ.visitBy(new StatementVisitor() {
public void visitAnalyzeStm(AnalyzeStm s) {
s.setBase(vsrc);
}
public void visitGapifyStm(GapifyStm s) {
s.setBase(vsrc);
}
public void visitGetStm(GetStm s) {
s.setBase(vsrc);
}
public void visitRemoveStm(RemoveStm s) {
s.setBase(vsrc);
}
public void visitValidateStm(ValidateStm s) {
if (s.getBase() != null)
s.setBase(vsrc);
}
public void visitVarStm(VarStm s) {
s.setSource(vsrc);
}
public void visitPlugStm(PlugStm s) {
s.setBase(vsrc);
if (s.getXMLSource() != null)
s.setXMLSource(vsrc);
}
public void visitCheckStm(CheckStm s) {
if (s.getBase() != null)
s.setBase(vsrc);
}
public void visitSetStm(SetStm s) {
s.setBase(vsrc);
if (s.getXMLSource() != null)
s.setXMLSource(vsrc);
}
public void visitInsertStm(InsertStm s) {
s.setBase(vsrc);
if (s.getXMLSource() != null)
s.setXMLSource(vsrc);
}
public void visitConcatStm(ConcatStm s) {
s.setXMLSource(vsrc);
}
public void visitCopyStm(CopyStm s) {
s.setBase(vsrc);
if (s.getFirstChild() != null)
s.setFirstChild(vsrc);
if (s.getFirstAttr() != null)
s.setFirstAttr(vsrc);
if (s.getNextNode() != null)
s.setNextNode(vsrc);
}
public void visitCastStm(CastStm s) {
s.setBase(vsrc);
}
public void visitNodeStm(NodeStm s) {
if (s.getFirstChild() != null)
s.setFirstChild(vsrc);
if (s.getFirstAttr() != null)
s.setFirstAttr(vsrc);
if (s.getNextNode() != null)
s.setNextNode(vsrc);
}
// unreachable cases (these nodes cannot be def-use graph successors)
public void visitConstStm(ConstStm s) {}
public void visitEmptyStm(EmptyStm s) {}
public void visitNopStm(NopStm s) {}
public void visitUnknownStm(UnknownStm s) {}
// not in this phase
public void visitArrayReadStm(ArrayReadStm s) {
}
public void visitArrayWriteStm(ArrayWriteStm s) {
}
public void visitCallStm(CallStm s) {
}
public void visitEscapeStm(EscapeStm s) {
}
public void visitArrayWriteStringStm(ArrayWriteStringStm s) {
}
});
for (Edge<Statement,VariableFilter> e : graph.getInEdges(vs))
graph.addEdge(e.getFrom(), succ, e.getData()); // XXX: ok to lose some edge info?
graph.removeNode(vs);
}
}
}
}
// find unreachable nodes
Set<Statement> reachable = new HashSet<Statement>(graph.getEntries());
LinkedList<Statement> queue = new LinkedList<Statement>(graph.getEntries());
while (!queue.isEmpty()) {
Statement stm = queue.removeFirst();
for (Edge<Statement,VariableFilter> edge : graph.getOutEdges(stm)) {
if (!reachable.contains(edge.getTo())) {
reachable.add(edge.getTo());
queue.add(edge.getTo());
}
}
}
// find used nodes
Set<Statement> used = new HashSet<Statement>();
queue.clear();
for (Statement stm : graph.getNodes()) {
if (XMLGraphChecker.isStatementChecked(stm)) {
queue.add(stm);
used.add(stm);
}
}
while (!queue.isEmpty()) {
Statement stm = queue.removeFirst();
for (Edge<Statement,VariableFilter> edge : graph.getInEdges(stm)) {
if (!used.contains(edge.getFrom())) {
used.add(edge.getFrom());
queue.add(edge.getFrom());
}
}
}
for (Statement stm : new ArrayList<Statement>(graph.getNodes())) {
if (!reachable.contains(stm) || !used.contains(stm))
graph.removeNode(stm);
}
}
}