/* * 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 * * (C) Copyright IBM Corporation 2010. */ package x10.visit; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import polyglot.ast.Block; import polyglot.ast.Branch; import polyglot.ast.Eval; import polyglot.ast.Expr; import polyglot.ast.For; import polyglot.ast.Id; import polyglot.ast.Labeled; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.Return; import polyglot.ast.Stmt; import polyglot.ast.SwitchBlock; import polyglot.ast.Throw; import polyglot.frontend.Job; import polyglot.types.Name; import polyglot.types.TypeSystem; import polyglot.util.Position; import polyglot.visit.ContextVisitor; import polyglot.visit.NodeVisitor; import x10.ast.StmtExpr; import x10.ast.StmtSeq; import x10.extension.X10Ext; import x10.util.CollectionFactory; /** * The <code>CodeCleaner</code> runs over the AST and performs some trivial dead * code elimination, while flattening blocks wherever possible. **/ public class CodeCleanUp extends ContextVisitor { private TypeSystem xts; private NodeFactory xnf; final protected boolean report; final protected boolean reportStats; static protected long blockCount; static protected long unreachableCount; protected X10AlphaRenamer alphaRenamer; protected Map<Name, Integer> labelInfo; /** * Creates a visitor for cleaning code. * * @param nf The node factory to be used when generating new nodes. **/ public CodeCleanUp(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); xts = ts; xnf = nf; this.report = reporter.should_report("CodeCleanUp", 1); this.reportStats = reporter.should_report("CodeCleanUpStats", 1); this.alphaRenamer = new X10AlphaRenamer(this, true); this.labelInfo = CollectionFactory.newHashMap(); } @Override public Object shallowCopy() { CodeCleanUp copy = (CodeCleanUp) super.shallowCopy(); return copy; } @Override protected NodeVisitor enterCall(Node parent, Node child) { if (child instanceof Labeled) { Name labelName = ((Labeled) child).labelNode().id(); assert !labelInfo.containsKey(labelName) : "CodeCleanup: "+labelName+" already in map! Shadowed labels?"; labelInfo.put(labelName, 0); } else if (child instanceof Branch) { Id labelNode = ((Branch) child).labelNode(); if (labelNode != null) { labelInfo.put(labelNode.id(), labelInfo.get(labelNode.id()) + 1); } } return this; } @Override protected Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) { if (n instanceof Labeled) { int uses = labelInfo.remove(((Labeled) n).labelNode().id()); // unboxing is safe as we always removing an element we put before // If the label was never used, then eliminate the Labeled node and just return the statement itself. if (uses == 0) return ((Labeled)n).statement(); else if (uses == 1 && isBreakLast((Labeled)n)) return removeBreakLast((Labeled)n); else return n; } if (n instanceof Eval && ((Eval)n).expr() instanceof StmtExpr && !(parent instanceof For)) { return sinkEval((StmtExpr)((Eval)n).expr(), n.position()); } if (!(n instanceof Block) || n instanceof SwitchBlock) { return n; } // Flatten any blocks that may be contained in this one // and eliminate unreachable code. Block b = (Block) n; if (!((X10Ext) b.ext()).annotations().isEmpty()) { return n; } b = clean(flattenBlock(b)); return b; } // Eval(StmtExpr(Block(S), e) ===> B(S, Eval(e)) private Block sinkEval(StmtExpr stexp, Position pos) { Block b = nf.Block(pos, stexp.statements()); if (!((X10Ext)stexp.ext()).annotations().isEmpty()) { b = (Block)((X10Ext)b.ext()).annotations(((X10Ext)stexp.ext()).annotations()); } Expr result = stexp.result(); if (result != null) { if (result instanceof StmtExpr) { b = b.append(sinkEval((StmtExpr)result, result.position())); b = clean(flattenBlock(b)); } else { b = b.append(nf.Eval(result.position(), result)); } } return b; } /** * Turns a Block into a list of Stmts. **/ protected Block flattenBlock(Block b) { List<Stmt> stmtList = new LinkedList<Stmt>(); boolean changeMade = false; boolean bIsStmtSeq = b instanceof StmtSeq; for (Stmt stmt : b.statements()) { if (stmt instanceof Block) { boolean innerIsStmtSeq = stmt instanceof StmtSeq; Block inner = (Block) stmt; if ((!bIsStmtSeq || innerIsStmtSeq) && ((X10Ext) inner.ext()).annotations().isEmpty()) { // Alpha-rename local decls in the block that we're // flattening. if (report) System.out.println("Cleaning up a block" + inner.position()); if (reportStats) blockCount++; if (!innerIsStmtSeq) inner = (Block) inner.visit(alphaRenamer); // Could add a check here that scopeLevel is back to 0??? stmtList.addAll(inner.statements()); changeMade = true; } else { if (report) System.out.println("StmtSeq holds a block " + b.position()); stmtList.add(inner); } } else { stmtList.add(stmt); } } if (changeMade) b = b.statements(stmtList); return b; } /** * Performs some trivial unreachable code elimination on a list of * statements. **/ protected Block clean(Block b) { List<Stmt> stmtList = new LinkedList<Stmt>(); boolean changeMade = false; // for (Stmt stmt : stl) { for (Iterator<Stmt> i = b.statements().iterator(); i.hasNext();) { Stmt stmt = i.next(); stmtList.add(stmt); if (stmt instanceof Branch || stmt instanceof Return || stmt instanceof Throw) { if (i.hasNext()) { if (report) System.out.println("Found a block ender in middle " + b); if (reportStats) unreachableCount++; changeMade = true; } break; } } if (changeMade) { b = b.statements(stmtList); } return b; } /** * Checks if `break label;` is the last statement in labeled block `label: { ... }` */ private boolean isBreakLast(Labeled labeled) { Name label = labeled.labelNode().id(); Stmt stmt = labeled.statement(); if (stmt instanceof Block) { Block block = (Block) stmt; List<Stmt> statements = block.statements(); Stmt last = statements.get(statements.size()-1); if (last != null && last instanceof Branch) { Branch branch = (Branch) last; if (branch.kind() == Branch.BREAK && branch.labelNode() != null && branch.labelNode().id().equals(label)) return true; } } return false; } private Block removeBreakLast(Labeled labeled) { List<Stmt> osts = ((Block) labeled.statement()).statements(); List<Stmt> statements = new ArrayList<Stmt>(osts); statements.remove(statements.size() - 1); return xnf.Block(labeled.position(), statements); } public void finish() { if (reportStats) { System.out.println("CodeCleanUp: Blocks removed " + blockCount); System.out.println("CodeCleanUp: Unreachable code removed " + unreachableCount); } } }