/*
* 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;
}
}