/*
* 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.Pair;
import polyglot.util.Position;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import polyglot.visit.DataFlow.Item;
import polyglot.visit.FlowGraph.EdgeKey;
/**
* Visitor which performs dead code elimination. (Note that "dead code" is not
* unreachable code, but is actually code that has no effect.)
*/
public class DeadCodeEliminator extends DataFlow {
public DeadCodeEliminator(Job job, TypeSystem ts, NodeFactory nf) {
super(job, ts, nf,
false /* backward analysis */,
true /* perform dataflow on entry to CodeDecls */);
}
protected static class DataFlowItem extends Item {
// Set of LocalInstances of live variables.
private Set<LocalDef> liveVars;
// Set of LocalInstances of live declarations. A LocalDecl is live if
// the declared local is ever live.
private Set<LocalDef> liveDecls;
/**
* Constructor for creating an empty set.
*/
protected DataFlowItem() {
this.liveVars = CollectionFactory.newHashSet();
this.liveDecls = CollectionFactory.newHashSet();
}
/**
* Deep copy constructor.
*/
protected DataFlowItem(DataFlowItem dfi) {
liveVars = CollectionFactory.newHashSet(dfi.liveVars);
liveDecls = CollectionFactory.newHashSet(dfi.liveDecls);
}
public void add(LocalDef li) {
liveVars.add(li);
liveDecls.add(li);
}
public void addAll(Set<LocalDef> lis) {
liveVars.addAll(lis);
liveDecls.addAll(lis);
}
public void remove(LocalDef li) {
liveVars.remove(li);
}
public void removeAll(Set<LocalDef> lis) {
liveVars.removeAll(lis);
}
public void removeDecl(LocalDef li) {
liveVars.remove(li);
liveDecls.remove(li);
}
public void union(DataFlowItem dfi) {
liveVars.addAll(dfi.liveVars);
liveDecls.addAll(dfi.liveDecls);
}
protected boolean needDecl(LocalDef li) {
return liveDecls.contains(li);
}
protected boolean needDef(LocalDef li) {
return liveVars.contains(li);
}
public int hashCode() {
int result = 1;
for (LocalDef ld : liveVars) {
result = 31*result + ld.hashCode();
}
for (LocalDef ld : liveDecls) {
result = 31*result + ld.hashCode();
}
return result;
}
public boolean equals(Object o) {
if (!(o instanceof DataFlowItem)) return false;
DataFlowItem dfi = (DataFlowItem)o;
return liveVars.equals(dfi.liveVars)
&& liveDecls.equals(dfi.liveDecls);
}
public String toString() {
return "<vars=" + liveVars + " ; decls=" + liveDecls + ">";
}
}
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 (Item item : inItems) {
DataFlowItem inItem = (DataFlowItem)item;
if (result == null) {
result = new DataFlowItem(inItem);
} else {
result.union(inItem);
}
}
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);
}
protected DataFlowItem flow(Item in, FlowGraph graph, Term t, boolean entry) {
DataFlowItem result = new DataFlowItem((DataFlowItem)in);
if (entry) {
return result;
}
Pair<Set<LocalDef>, Set<LocalDef>> du = null;
if (t instanceof LocalDecl) {
LocalDecl n = (LocalDecl)t;
LocalDef to = n.localDef();
result.removeDecl(to);
du = getDefUse(n.init());
} else if (t instanceof Stmt && !(t instanceof CompoundStmt)) {
du = getDefUse((Stmt)t);
} else if (t instanceof CompoundStmt) {
if (t instanceof If) {
du = getDefUse(((If)t).cond());
} else if (t instanceof Switch) {
du = getDefUse(((Switch)t).expr());
} else if (t instanceof Do) {
du = getDefUse(((Do)t).cond());
} else if (t instanceof For) {
du = getDefUse(((For)t).cond());
} else if (t instanceof While) {
du = getDefUse(((While)t).cond());
}
}
if (du != null) {
result.removeAll(du.fst());
result.addAll(du.snd());
}
return result;
}
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("DeadCodeEliminator.check should "
+ "never be called.");
}
private DataFlowItem getItem(Term n) {
FlowGraph g = currentFlowGraph();
if (g == null) return null;
Collection<FlowGraph.Peer> peers = g.peers(n, Term.EXIT);
if (peers == null || peers.isEmpty()) return null;
List<Item> items = new ArrayList<Item>();
for (FlowGraph.Peer p : peers) {
if (p.inItem() != null) items.add(p.inItem());
}
return (DataFlowItem)confluence(items, n, false, g);
}
public Node leaveCall(Node old, Node n, NodeVisitor v) {
if (n instanceof LocalDecl) {
LocalDecl ld = (LocalDecl)n;
DataFlowItem in = getItem(ld);
if (in == null || in.needDecl(ld.localDef())) return n;
return getEffects(ld.init());
}
if (n instanceof Eval) {
Eval eval = (Eval)n;
Expr expr = eval.expr();
Local local;
Expr right = null;
if (expr instanceof LocalAssign) {
LocalAssign assign = (LocalAssign)expr;
Expr left = assign.local();
right = assign.right();
if (!(left instanceof Local)) return n;
local = (Local)left;
} else if (expr instanceof Assign) {
return n;
} else if (expr instanceof Unary) {
Unary unary = (Unary)expr;
expr = unary.expr();
if (!(expr instanceof Local)) return n;
local = (Local)expr;
} else {
return n;
}
DataFlowItem in = getItem(eval);
if (in == null || in.needDef(local.localInstance().def())) return n;
if (right != null) {
return getEffects(right);
}
return nf.Empty(Position.COMPILER_GENERATED);
}
if (n instanceof Block) {
// Get rid of empty statements.
Block b = (Block)n;
List<Stmt> stmts = new ArrayList<Stmt>(b.statements());
for (Iterator<Stmt> it = stmts.iterator(); it.hasNext(); ) {
if (it.next() instanceof Empty) it.remove();
}
return b.statements(stmts);
}
return n;
}
/**
* Returns array of sets of local instances.
* Element 0 is the set of local instances DEFined by the node.
* Element 1 is the set of local instances USEd by the node.
*/
protected Pair<Set<LocalDef>, Set<LocalDef>> getDefUse(Node n) {
final Set<LocalDef> def = CollectionFactory.newHashSet();
final Set<LocalDef> use = CollectionFactory.newHashSet();
if (n != null) {
n.visit(createDefUseFinder(def, use));
}
return new Pair<Set<LocalDef>, Set<LocalDef>>(def, use);
}
protected NodeVisitor createDefUseFinder(Set<LocalDef> def, Set<LocalDef> use) {
return new DefUseFinder(def, use);
}
protected static class DefUseFinder extends NodeVisitor {
protected Set<LocalDef> def;
protected Set<LocalDef> use;
public DefUseFinder(Set<LocalDef> def, Set<LocalDef> use) {
this.def = def;
this.use = use;
}
public Node override(Node parent, Node n) {
if (parent instanceof LocalAssign) {
LocalAssign a = (LocalAssign) parent;
if (n == a.local()) {
return n;
}
}
return null;
}
public Node leave(Node old, Node n, NodeVisitor v) {
if (n instanceof Local) {
use.add(((Local)n).localInstance().def());
} else if (n instanceof LocalAssign) {
Expr left = ((LocalAssign)n).local();
if (left instanceof Local) {
def.add(((Local)left).localInstance().def());
}
}
return n;
}
}
/**
* Returns a statement that is side-effect-equivalent to the given
* expression.
*/
protected Stmt getEffects(Expr expr) {
Stmt empty = nf.Empty(Position.COMPILER_GENERATED);
if (expr == null) return empty;
final List<Stmt> result = new LinkedList<Stmt>();
final Position pos = Position.COMPILER_GENERATED;
NodeVisitor v = new NodeVisitor() {
public Node override(Node parent, Node n) {
if (n instanceof Assign || n instanceof ProcedureCall) {
return leave(parent, n, n, this);
}
// XXX Cast
if (n instanceof Unary) {
Unary.Operator op = ((Unary)n).operator();
if (op == Unary.POST_INC || op == Unary.POST_DEC
|| op == Unary.PRE_INC || op == Unary.PRE_INC) {
return leave(parent, n, n, this);
}
}
return n;
}
public Node leave(Node old, Node n, NodeVisitor v) {
if (n instanceof Assign || n instanceof ProcedureCall) {
result.add(nf.Eval(pos, (Expr)n));
} else if (n instanceof Unary) {
Unary.Operator op = ((Unary)n).operator();
if (op == Unary.POST_INC || op == Unary.POST_DEC
|| op == Unary.PRE_INC || op == Unary.PRE_INC) {
result.add(nf.Eval(pos, (Expr)n));
}
}
// XXX Cast
return n;
}
};
expr.visit(v);
if (result.isEmpty()) return empty;
if (result.size() == 1) return result.get(0);
return nf.Block(Position.COMPILER_GENERATED, result);
}
}