/* Soot - a J*va Optimization Framework * Copyright (C) 1997-1999 Raja Vallee-Rai * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the Sable Research Group and others 1997-1999. * See the 'credits' file distributed with Soot for the complete list of * contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot) */ package soot.jimple.toolkits.scalar; import soot.options.*; import soot.*; import soot.jimple.*; import soot.toolkits.scalar.*; import soot.util.*; import soot.toolkits.graph.*; import java.util.*; public class DeadAssignmentEliminator extends BodyTransformer { public DeadAssignmentEliminator( Singletons.Global g ) {} public static DeadAssignmentEliminator v() { return G.v().soot_jimple_toolkits_scalar_DeadAssignmentEliminator(); } /** Eliminates dead code in a linear fashion. Complexity is linear with respect to the statements. Does not work on grimp code because of the check on the right hand side for side effects. */ protected void internalTransform(Body b, String phaseName, Map options) { boolean eliminateOnlyStackLocals = PhaseOptions.getBoolean(options, "only-stack-locals"); if(Options.v().verbose()) G.v().out.println("[" + b.getMethod().getName() + "] Eliminating dead code..."); if(Options.v().time()) Timers.v().deadCodeTimer.start(); Set<Stmt> essentialStmts = new HashSet<Stmt>(); LinkedList<Stmt> toVisit = new LinkedList<Stmt>(); Chain units = b.getUnits(); // Make a first pass through the statements, noting // the statements we must absolutely keep. { Iterator stmtIt = units.iterator(); while(stmtIt.hasNext()) { Stmt s = (Stmt) stmtIt.next(); boolean isEssential = true; if(s instanceof NopStmt) isEssential = false; if(s instanceof AssignStmt) { AssignStmt as = (AssignStmt) s; if(as.getLeftOp() instanceof Local && (!eliminateOnlyStackLocals || ((Local) as.getLeftOp()).getName().startsWith("$"))) { Value rhs = as.getRightOp(); isEssential = false; if(rhs instanceof InvokeExpr || rhs instanceof ArrayRef) { // Note that ArrayRef and InvokeExpr all can // have side effects (like throwing a null pointer exception) isEssential = true; } if(rhs instanceof InstanceFieldRef && !(!b.getMethod().isStatic() && ((InstanceFieldRef)rhs).getBase() == b.getThisLocal())) { // Any InstanceFieldRef may have side effects, // unless the base is reading from 'this' // in a non-static method isEssential = true; } else if(rhs instanceof DivExpr || rhs instanceof RemExpr) { BinopExpr expr = (BinopExpr) rhs; if(expr.getOp1().getType().equals(IntType.v()) || expr.getOp2().getType().equals(IntType.v()) || expr.getOp1().getType().equals(LongType.v()) || expr.getOp2().getType().equals(LongType.v())) { // Can trigger a division by zero isEssential = true; } } else if(rhs instanceof CastExpr) { // Can trigger ClassCastException isEssential = true; } else if(rhs instanceof NewArrayExpr || rhs instanceof NewMultiArrayExpr) { // can throw exception isEssential = true; } else if (rhs instanceof NewExpr || (rhs instanceof FieldRef && !(rhs instanceof InstanceFieldRef))) { // Can trigger class initialization isEssential = true; } } } if(isEssential) { essentialStmts.add(s); toVisit.addLast(s); } } } ExceptionalUnitGraph graph = new ExceptionalUnitGraph(b); LocalDefs defs = new SmartLocalDefs(graph, new SimpleLiveLocals(graph)); LocalUses uses = new SimpleLocalUses(graph, defs); // Add all the statements which are used to compute values // for the essential statements, recursively { while(!toVisit.isEmpty()) { Stmt s = toVisit.removeFirst(); Iterator boxIt = s.getUseBoxes().iterator(); while(boxIt.hasNext()) { ValueBox box = (ValueBox) boxIt.next(); if(box.getValue() instanceof Local) { Iterator<Unit> defIt = defs.getDefsOfAt( (Local) box.getValue(), s).iterator(); while(defIt.hasNext()) { // Add all the definitions as essential stmts Stmt def = (Stmt) defIt.next(); if(!essentialStmts.contains(def)) { essentialStmts.add(def); toVisit.addLast(def); } } } } } } // Remove the dead statements { Iterator stmtIt = units.iterator(); while(stmtIt.hasNext()) { Stmt s = (Stmt) stmtIt.next(); if(!essentialStmts.contains(s)){ stmtIt.remove(); s.clearUnitBoxes(); } else if(s instanceof AssignStmt && ((AssignStmt) s).getLeftOp() == ((AssignStmt) s).getRightOp() && ((AssignStmt) s).getLeftOp() instanceof Local) { // Stmt is of the form a = a which is useless stmtIt.remove(); s.clearUnitBoxes(); } } } // Eliminate dead assignments from invokes such as x = f(), where // x is no longer used { Iterator stmtIt = units.snapshotIterator(); while(stmtIt.hasNext()) { Stmt s = (Stmt) stmtIt.next(); if(s instanceof AssignStmt && s.containsInvokeExpr()) { Local l = (Local) ((AssignStmt) s).getLeftOp(); InvokeExpr e = s.getInvokeExpr(); // Just find one use of l which is essential { Iterator useIt = uses.getUsesOf(s).iterator(); boolean isEssential = false; while(useIt.hasNext()) { UnitValueBoxPair pair = (UnitValueBoxPair) useIt.next(); if(essentialStmts.contains(pair.unit)) { isEssential = true; break; } } if(!isEssential) { // Transform it into a simple invoke. Stmt newInvoke = Jimple.v().newInvokeStmt(e); newInvoke.addAllTagsOf(s); units.swapWith(s, newInvoke); } } } } } if(Options.v().time()) Timers.v().deadCodeTimer.end(); } }