package polyglot.visit; import polyglot.ast.*; import polyglot.frontend.Job; import polyglot.main.Report; import polyglot.types.*; import polyglot.util.InternalCompilerError; import java.util.*; /** * Visitor which performs copy propagation. */ public class CopyPropagator extends DataFlow { public CopyPropagator(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf, true /* forward analysis */, true /* perform dataflow on entry to CodeDecls */); } protected static class DataFlowItem extends Item { // Map of LocalInstance -> CopyInfo. The CopyInfo nodes form a forest // to represent copy information. private Map map; /** * Constructor for creating an empty set. */ protected DataFlowItem() { this.map = new HashMap(); } /** * Deep copy constructor. */ protected DataFlowItem(DataFlowItem dfi) { map = new HashMap(dfi.map.size()); for (Iterator it = dfi.map.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry)it.next(); LocalInstance li = (LocalInstance)e.getKey(); CopyInfo ci = (CopyInfo)e.getValue(); if (ci.from != null) add(ci.from.li, li); } } protected static class CopyInfo { final LocalInstance li; // Local instance this node pertains to. CopyInfo from; // In edge. Set to; // Out edges. CopyInfo root; // Root CopyInfo node for this tree. protected CopyInfo(LocalInstance li) { if (li == null) { throw new InternalCompilerError("Null local instance " + "encountered during copy propagation."); } this.li = li; this.from = null; this.to = new HashSet(); this.root = this; } protected void setRoot(CopyInfo root) { List worklist = new ArrayList(); worklist.add(this); while (worklist.size() > 0) { CopyInfo ci = (CopyInfo)worklist.remove(worklist.size()-1); worklist.addAll(ci.to); ci.root = root; } } public boolean equals(Object o) { if (!(o instanceof CopyInfo)) return false; CopyInfo ci = (CopyInfo)o; // Assume both are in consistent data structures, so only check // up pointers. Also check root pointers because we can. return li == ci.li && (from == null ? ci.from == null : (ci.from != null && from.li == ci.from.li)) && root.li == ci.root.li; } public int hashCode() { return li.hashCode() + 31*(from == null ? 0 : from.li.hashCode() + 31*root.li.hashCode()); } } protected void add(LocalInstance from, LocalInstance to) { // Get the 'to' node. boolean newTo = !map.containsKey(to); CopyInfo ciTo; if (newTo) { ciTo = new CopyInfo(to); map.put(to, ciTo); } else { ciTo = (CopyInfo)map.get(to); } // Get the 'from' node. CopyInfo ciFrom; if (map.containsKey(from)) { ciFrom = (CopyInfo)map.get(from); } else { ciFrom = new CopyInfo(from); map.put(from, ciFrom); ciFrom.root = ciFrom; } // Make sure ciTo doesn't already have a 'from' node. if (ciTo.from != null) { throw new InternalCompilerError("Error while copying dataflow " + "item during copy propagation."); } // Link up. ciFrom.to.add(ciTo); ciTo.from = ciFrom; // Consistency fix-up. if (newTo) { ciTo.root = ciFrom.root; } else { ciTo.setRoot(ciFrom.root); } } protected void intersect(DataFlowItem dfi) { boolean modified = false; for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry)it.next(); LocalInstance li = (LocalInstance)e.getKey(); CopyInfo ci = (CopyInfo)e.getValue(); if (!dfi.map.containsKey(li)) { modified = true; it.remove(); // Surgery. Bypass and remove the node. We'll fix // consistency later. if (ci.from != null) ci.from.to.remove(ci); for (Iterator i = ci.to.iterator(); i.hasNext(); ) { CopyInfo toCI = (CopyInfo)i.next(); toCI.from = null; } continue; } if (ci.from == null) continue; // Other DFI contains this key. // Make sure that ci and ci.from are also in the same tree in // the other DFI. If not, break the link in the intersection // result. CopyInfo otherCI = (CopyInfo)dfi.map.get(li); CopyInfo otherCIfrom = (CopyInfo)dfi.map.get(ci.from.li); if (otherCIfrom == null || otherCI.root != otherCIfrom.root) { modified = true; // Remove the uplink. ci.from.to.remove(ci); ci.from = null; } } if (!modified) return; // Fix consistency. for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry)it.next(); CopyInfo ci = (CopyInfo)e.getValue(); // Only work on roots. if (ci.from != null) continue; // Cut out singleton nodes. if (ci.to.isEmpty()) { it.remove(); continue; } // Fix root. ci.setRoot(ci); } } public void kill(LocalInstance var) { if (!map.containsKey(var)) return; CopyInfo ci = (CopyInfo)map.get(var); map.remove(var); // Splice out 'ci' and fix consistency. if (ci.from != null) ci.from.to.remove(ci); for (Iterator it = ci.to.iterator(); it.hasNext(); ) { CopyInfo toCI = (CopyInfo)it.next(); toCI.from = ci.from; if (ci.from == null) { toCI.setRoot(toCI); } else { ci.from.to.add(toCI); } } } public LocalInstance getRoot(LocalInstance var) { if (!map.containsKey(var)) return null; return ((CopyInfo)map.get(var)).root.li; } private void die() { throw new InternalCompilerError("Copy propagation dataflow item " + "consistency error."); } private void consistencyCheck() { for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry)it.next(); LocalInstance li = (LocalInstance)e.getKey(); CopyInfo ci = (CopyInfo)e.getValue(); if (li != ci.li) die(); if (!map.containsKey(ci.root.li)) die(); if (map.get(ci.root.li) != ci.root) die(); if (ci.from == null) { if (ci.root != ci) die(); } else { if (!map.containsKey(ci.from.li)) die(); if (map.get(ci.from.li) != ci.from) die(); if (ci.from.root != ci.root) die(); if (!ci.from.to.contains(ci)) die(); } for (Iterator i = ci.to.iterator(); i.hasNext(); ) { CopyInfo toCI = (CopyInfo)i.next(); if (!map.containsKey(toCI.li)) die(); if (map.get(toCI.li) != toCI) die(); if (toCI.root != ci.root) die(); if (toCI.from != ci) die(); } } } public int hashCode() { int result = 0; for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry e = (Map.Entry)it.next(); result = 31*result + e.getKey().hashCode(); result = 31*result + e.getValue().hashCode(); } return result; } public boolean equals(Object o) { if (!(o instanceof DataFlowItem)) return false; DataFlowItem dfi = (DataFlowItem)o; return map.equals(dfi.map); } public String toString() { String result = ""; boolean first = true; for (Iterator it = map.values().iterator(); it.hasNext(); ) { CopyInfo ci = (CopyInfo)it.next(); if (ci.from != null) { if (!first) result += ", "; if (ci.root != ci.from) result += ci.root.li + " ->* "; result += ci.from.li + " -> " + ci.li; first = false; } } return "[" + result + "]"; } } public Item createInitialItem(FlowGraph graph, Term node) { return new DataFlowItem(); } public Item confluence(List inItems, Term node, FlowGraph graph) { DataFlowItem result = null; for (Iterator it = inItems.iterator(); it.hasNext(); ) { DataFlowItem inItem = (DataFlowItem)it.next(); if (result == null) { result = new DataFlowItem(inItem); } else { result.intersect(inItem); } } return result; } private void killDecl(DataFlowItem dfi, Stmt stmt) { if (stmt instanceof LocalDecl) { dfi.kill(((LocalDecl)stmt).localInstance()); } } protected DataFlowItem flow(Item in, FlowGraph graph, Term t) { DataFlowItem result = new DataFlowItem((DataFlowItem)in); if (t instanceof Assign) { Assign n = (Assign)t; Assign.Operator op = n.operator(); Expr left = n.left(); Expr right = n.right(); if (left instanceof Local) { LocalInstance to = ((Local)left).localInstance(); result.kill(to); if (right instanceof Local && op == Assign.ASSIGN) { LocalInstance from = ((Local)right).localInstance(); result.add(from, to); } } } else if (t instanceof Unary) { Unary n = (Unary)t; Unary.Operator op = n.operator(); Expr expr = n.expr(); if (expr instanceof Local && (op == Unary.POST_INC || op == Unary.POST_DEC || op == Unary.PRE_INC || op == Unary.PRE_DEC)) { result.kill(((Local)expr).localInstance()); } } else if (t instanceof LocalDecl) { LocalDecl n = (LocalDecl)t; LocalInstance to = n.localInstance(); result.kill(to); // It's a copy if we're initializing a non-final local declaration // with a value from a local variable. We only care about // non-final local declarations because final locals have special // use in local classes. if (!n.flags().isFinal() && n.init() instanceof Local) { LocalInstance from = ((Local)n.init()).localInstance(); result.add(from, to); } } else if (t instanceof Block) { // Kill locals that were declared in the block. Block n = (Block)t; for (Iterator it = n.statements().iterator(); it.hasNext(); ) { killDecl(result, (Stmt)it.next()); } } else if (t instanceof Loop) { if (t instanceof For) { // Kill locals that were declared in the initializers. For n = (For)t; for (Iterator it = n.inits().iterator(); it.hasNext(); ) { killDecl(result, (Stmt)it.next()); } } // Kill locals that were declared in the body. killDecl(result, ((Loop)t).body()); } else if (t instanceof Catch) { // Kill catch's formal. result.kill(((Catch)t).formal().localInstance()); } else if (t instanceof If) { // Kill locals declared in consequent and alternative. If n = (If)t; killDecl(result, n.consequent()); killDecl(result, n.alternative()); } return result; } public Map flow(Item in, FlowGraph graph, Term t, Set succEdgeKeys) { return itemToMap(flow(in, graph, t), succEdgeKeys); } public void post(FlowGraph graph, Term root) throws SemanticException { // No need to do any checking. if (Report.should_report(Report.cfg, 2)) { dumpFlowGraph(graph, root); } } public void check(FlowGraph graph, Term n, Item inItem, Map outItems) throws SemanticException { throw new InternalCompilerError("CopyPropagator.check should never be " + "called."); } public Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException { if (n instanceof Local) { FlowGraph g = currentFlowGraph(); if (g == null) return n; Local l = (Local)n; Collection peers = g.peers(l); if (peers == null || peers.isEmpty()) return n; List items = new ArrayList(); for (Iterator it = peers.iterator(); it.hasNext(); ) { FlowGraph.Peer p = (FlowGraph.Peer)it.next(); if (p.inItem() != null) items.add(p.inItem()); } DataFlowItem in = (DataFlowItem)confluence(items, l, g); if (in == null) return n; LocalInstance root = in.getRoot(l.localInstance()); if (root == null) return n; return l.name(root.name()).localInstance(root); } if (n instanceof Unary) { return old; } if (n instanceof Assign) { Assign oldAssign = (Assign)old; Assign newAssign = (Assign)n; return newAssign.left(oldAssign.left()); } return n; } }