/*
* This file is part of the Polyglot extensible compiler framework.
*
* Copyright (c) 2000-2006 Polyglot project group, Cornell University
*
*/
package x10.visit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import polyglot.ast.Assign;
import polyglot.ast.Block;
import polyglot.ast.Block_c;
import polyglot.ast.Catch;
import polyglot.ast.Do;
import polyglot.ast.Eval_c;
import polyglot.ast.Expr;
import polyglot.ast.For;
import polyglot.ast.If;
import polyglot.ast.Labeled;
import polyglot.ast.Local;
import polyglot.ast.LocalAssign;
import polyglot.ast.LocalDecl;
import polyglot.ast.Loop;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Stmt;
import polyglot.ast.Term;
import polyglot.ast.Unary;
import polyglot.ast.While;
import polyglot.frontend.Job;
import polyglot.main.Reporter;
import polyglot.types.LocalDef;
import polyglot.types.TypeSystem;
import polyglot.util.InternalCompilerError;
import polyglot.visit.DataFlow;
import polyglot.visit.FlowGraph;
import polyglot.visit.FlowGraph.EdgeKey;
import polyglot.visit.NodeVisitor;
import x10.ast.Async;
import x10.ast.AtStmt;
import x10.ast.Closure;
import x10.util.CollectionFactory;
/**
* Visitor which performs copy propagation. Copied over from
* polyglot.ast.CopyPropagator.
*/
public class X10CopyPropagator extends DataFlow {
public X10CopyPropagator(Job job, TypeSystem ts, NodeFactory nf) {
super(job, ts, nf, true /* forward analysis */, true /* perform dataflow on entry to CodeDecls */);
}
protected 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 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 void killall() {
for (LocalDef var : map.keySet()) {
kill(var);
}
}
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 (t instanceof Async) {
// Kill all variables
result.killall();
} else if (t instanceof AtStmt) {
// Kill all variables
result.killall();
} else if (t instanceof Closure) {
// Kill all variables
result.killall();
}
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 if (m instanceof Block_c) {
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 NodeVisitor begin() {
return this;
}
public Node leaveCall(Node old, Node n, NodeVisitor v) {
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 li = l.localInstance().def();
LocalDef root = in.getRoot(li);
if (root == null || root == li) return n;
return l.name(l.name().id(root.name())).localInstance(root.asInstance());
}
if (n instanceof LocalAssign) {
LocalAssign oldAssign = (LocalAssign) old;
LocalAssign newAssign = (LocalAssign) n;
if (oldAssign.local() != newAssign.local()) {
Node r = newAssign.local(oldAssign.local());
return r;
}
}
return n;
}
}