/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.visit; import java.util.*; import polyglot.ast.*; import polyglot.frontend.Job; import polyglot.main.Reporter; import polyglot.types.*; import polyglot.util.InternalCompilerError; import polyglot.util.CollectionUtil; import x10.util.CollectionFactory; import polyglot.visit.DataFlow.FlowGraphSource; import polyglot.visit.DataFlow.Item; import polyglot.visit.FlowGraph.EdgeKey; /** * 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<LocalDef, CopyInfo> map; /** * Constructor for creating an empty set. */ protected DataFlowItem() { this.map = CollectionFactory.newHashMap(); } /** * Deep copy constructor. */ protected DataFlowItem(DataFlowItem dfi) { map = CollectionFactory.newHashMap(dfi.map.size()); for (Map.Entry<LocalDef, CopyInfo> e : dfi.map.entrySet()) { LocalDef li = (LocalDef)e.getKey(); CopyInfo ci = (CopyInfo)e.getValue(); if (ci.from != null) add(ci.from.li, li); } } protected static class CopyInfo { final public LocalDef li; // Local instance this node pertains to. public CopyInfo from; // In edge. public Set<CopyInfo> to; // Out edges. public CopyInfo root; // Root CopyInfo node for this tree. protected CopyInfo(LocalDef li) { if (li == null) { throw new InternalCompilerError("Null local instance " + "encountered during copy propagation."); } this.li = li; this.from = null; this.to = CollectionFactory.newHashSet(); this.root = this; } protected void setRoot(CopyInfo root) { List<CopyInfo> worklist = new ArrayList<CopyInfo>(); 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(LocalDef from, LocalDef 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<Map.Entry<LocalDef, CopyInfo>> it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry<LocalDef, CopyInfo> e = it.next(); LocalDef li = (LocalDef)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 (CopyInfo toCI : ci.to) { 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<Map.Entry<LocalDef, CopyInfo>> it = map.entrySet().iterator(); it.hasNext(); ) { Map.Entry<LocalDef, CopyInfo> e = 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(LocalDef 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 (CopyInfo toCI : ci.to) { toCI.from = ci.from; if (ci.from == null) { toCI.setRoot(toCI); } else { ci.from.to.add(toCI); } } } public LocalDef getRoot(LocalDef 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 (Map.Entry<LocalDef, CopyInfo> e : map.entrySet()) { LocalDef li = (LocalDef)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 (CopyInfo toCI : ci.to) { 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 = 1; for (Map.Entry<LocalDef, CopyInfo> e : map.entrySet()) { 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 (CopyInfo ci : map.values()) { 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, boolean entry) { return new DataFlowItem(); } public Item confluence(List<Item> inItems, Term node, boolean entry, FlowGraph graph) { DataFlowItem result = null; for (Iterator<Item> 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).localDef()); } } protected DataFlowItem flow(Item in, FlowGraph graph, Term t, boolean entry) { DataFlowItem result = new DataFlowItem((DataFlowItem)in); if (entry) { return result; } if (t instanceof LocalAssign) { LocalAssign n = (LocalAssign)t; Assign.Operator op = n.operator(); Expr left = n.local(); Expr right = n.right(); if (left instanceof Local) { LocalDef to = ((Local)left).localInstance().def(); result.kill(to); if (right instanceof Local && op == Assign.ASSIGN) { LocalDef from = ((Local)right).localInstance().def(); 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().def()); } } else if (t instanceof LocalDecl) { LocalDecl n = (LocalDecl)t; LocalDef to = n.localDef(); 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.init() instanceof Local) { LocalDef from = ((Local)n.init()).localInstance().def(); result.add(from, to); } }else if (t instanceof Eval_c){ Expr exp =((Eval_c) t).expr(); if (exp instanceof LocalAssign) { LocalAssign n = (LocalAssign)exp; Assign.Operator op = n.operator(); Expr left = n.local(); Expr right = n.right(); if (left instanceof Local) { LocalDef to = ((Local)left).localInstance().def(); result.kill(to); } } }else if (t instanceof Labeled){ Stmt l = ((Labeled)t).statement(); flow (result, graph, l, entry); } else if (t instanceof Block) { // Kill locals that were declared in the block. Block n = (Block)t; //System.out.println(t+"\n"); for (Stmt s : n.statements()) { if (s instanceof Labeled){ flow (result, graph,s,entry); } killDecl(result, s); } } else if (t instanceof Loop) { if (t instanceof For) { // Kill locals that were declared in the initializers. For n = (For)t; for (Stmt init : n.inits()) { killDecl(result, init); } killDecl(result, ((Loop)t).body()); } // Kill locals that were declared in the body. else if (t instanceof While || t instanceof Do) { Stmt m = ((Loop)t).body(); List<Stmt> st = null; if (m instanceof Eval_c) flow(result, graph, m, entry); else { st = ((Block_c)m).statements(); for (Stmt s : st){ if (s instanceof Labeled || s instanceof Loop || s instanceof Eval_c) flow(result, graph, s, entry); else killDecl(result,s); } } } } else if (t instanceof Catch) { // Kill catch's formal. result.kill(((Catch)t).formal().localDef()); } else if (t instanceof If) { // Kill locals declared in consequent and alternative. If n = (If)t; killDecl(result, n.consequent()); killDecl(result, n.alternative()); } //System.out.println("RESULT: "+ result); return result; } public Map<EdgeKey, Item> flow(Item in, FlowGraph graph, Term t, boolean entry, Set<EdgeKey> succEdgeKeys) { return itemToMap(flow(in, graph, t, entry), succEdgeKeys); } public void post(FlowGraph graph, Term root) { // No need to do any checking. if (reporter.should_report(Reporter.cfg, 2)) { dumpFlowGraph(graph, root); } } public void check(FlowGraph graph, Term n, boolean entry, Item inItem, Map<EdgeKey, Item> outItems) { throw new InternalCompilerError("CopyPropagator.check should never be " + "called."); } public Node leaveCall(Node old, Node n, NodeVisitor v) { //FlowGraph x = currentFlowGraph(); //dumpFlowGraph(currentFlowGraph(), currentFlowGraph().root); if (n instanceof Local) { FlowGraph g = currentFlowGraph(); if (g == null) return n; Local l = (Local)n; Collection<FlowGraph.Peer> peers = g.peers(l, Term.EXIT); if (peers == null || peers.isEmpty()) return n; List<Item> items = new ArrayList<Item>(); for (FlowGraph.Peer p : peers) { if (p.inItem() != null) items.add(p.inItem()); } DataFlowItem in = (DataFlowItem)confluence(items, l, false, g); if (in == null) return n; LocalDef root = in.getRoot(l.localInstance().def()); if (root == null) return n; return l.name(l.name().id(root.name())).localInstance(root.asInstance()); } if (n instanceof Unary) { return old; } if (n instanceof LocalAssign) { LocalAssign oldAssign = (LocalAssign)old; LocalAssign newAssign = (LocalAssign)n; return newAssign.local(oldAssign.local()); } return n; } }