/* Soot - a J*va Optimization Framework * Copyright (C) 2003 Navindra Umanee <navindra@cs.mcgill.ca> * * 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. */ package soot.shimple.internal; import soot.*; import soot.util.*; import java.util.*; import soot.shimple.*; import soot.jimple.*; import soot.jimple.toolkits.scalar.*; import soot.toolkits.graph.*; /** * This class does the real high-level work. It takes a Jimple body * or Jimple/Shimple hybrid body and produces pure Shimple. * * <p> The work is done in two main steps: * * <ol> * <li> Trivial Phi nodes are added. * <li> A renaming algorithm is executed. * </ol> * * <p> This class can also translate out of Shimple by producing an * equivalent Jimple body with all Phi nodes removed. * * <p> Note that this is an internal class, understanding it should * not be necessary from a user point-of-view and relying on it * directly is not recommended. * * @author Navindra Umanee * @see soot.shimple.ShimpleBody * @see <a * href="http://citeseer.nj.nec.com/cytron91efficiently.html">Efficiently * Computing Static Single Assignment Form and the Control Dependence * Graph</a> **/ public class PiNodeManager { protected ShimpleBody body; protected ShimpleFactory sf; protected DominatorTree dt; protected DominanceFrontier df; protected ReversibleGraph cfg; protected boolean trimmed; /** * Transforms the provided body to pure SSA form. **/ public PiNodeManager(ShimpleBody body, boolean trimmed) { this.body = body; this.trimmed = trimmed; sf = G.v().shimpleFactory; } public void update() { cfg = sf.getReverseBlockGraph(); dt = sf.getReverseDominatorTree(); df = sf.getReverseDominanceFrontier(); } protected MultiMap varToBlocks; public boolean insertTrivialPiNodes() { update(); boolean change = false; MultiMap localsToUsePoints = new SHashMultiMap(); varToBlocks = new HashMultiMap(); // compute localsToUsePoints and varToBlocks for(Iterator blocksIt = cfg.iterator(); blocksIt.hasNext();){ Block block = (Block)blocksIt.next(); for(Iterator unitsIt = block.iterator(); unitsIt.hasNext();){ Unit unit = (Unit) unitsIt.next(); List useBoxes = unit.getUseBoxes(); for(Iterator useBoxesIt = useBoxes.iterator(); useBoxesIt.hasNext();){ Value use = ((ValueBox)useBoxesIt.next()).getValue(); if(use instanceof Local) localsToUsePoints.put(use, block); } if(Shimple.isPiNode(unit)) varToBlocks.put(Shimple.getLhsLocal(unit), block); } } /* Routine initialisations. */ int[] workFlags = new int[cfg.size()]; int[] hasAlreadyFlags = new int[cfg.size()]; int iterCount = 0; Stack<Block> workList = new Stack<Block>(); /* Main Cytron algorithm. */ { Iterator localsIt = localsToUsePoints.keySet().iterator(); while(localsIt.hasNext()){ Local local = (Local) localsIt.next(); iterCount++; // initialise worklist { Iterator useNodesIt = localsToUsePoints.get(local).iterator(); while(useNodesIt.hasNext()){ Block block = (Block) useNodesIt.next(); workFlags[block.getIndexInMethod()] = iterCount; workList.push(block); } } while(!workList.empty()){ Block block = workList.pop(); DominatorNode node = dt.getDode(block); Iterator frontierNodes = df.getDominanceFrontierOf(node).iterator(); while(frontierNodes.hasNext()){ Block frontierBlock = (Block) ((DominatorNode) frontierNodes.next()).getGode(); int fBIndex = frontierBlock.getIndexInMethod(); if(hasAlreadyFlags[fBIndex] < iterCount){ insertPiNodes(local, frontierBlock); change = true; hasAlreadyFlags[fBIndex] = iterCount; if(workFlags[fBIndex] < iterCount){ workFlags[fBIndex] = iterCount; workList.push(frontierBlock); } } } } } } if(change) sf.clearCache(); return change; } public void insertPiNodes(Local local, Block frontierBlock) { if(varToBlocks.get(local).contains(frontierBlock.getSuccs().get(0))) return; Unit u = frontierBlock.getTail(); TRIMMED: { if(trimmed){ for(Iterator i = u.getUseBoxes().iterator(); i.hasNext();){ Value use = ((ValueBox)i.next()).getValue(); if(use.equals(local)) break TRIMMED; } return; } } if(u instanceof IfStmt) piHandleIfStmt(local, (IfStmt) u); else if((u instanceof LookupSwitchStmt) || (u instanceof TableSwitchStmt)) piHandleSwitchStmt(local, u); else throw new RuntimeException("Assertion failed: Unhandled stmt: " + u); } public void piHandleIfStmt(Local local, IfStmt u) { Unit target = u.getTarget(); PiExpr pit = Shimple.v().newPiExpr(local, u, Boolean.TRUE); PiExpr pif = Shimple.v().newPiExpr(local, u, Boolean.FALSE); Unit addt = Jimple.v().newAssignStmt(local, pit); Unit addf = Jimple.v().newAssignStmt(local, pif); PatchingChain units = body.getUnits(); // insert after should be safe; a new block should result if // the Unit originally after the IfStmt had another predecessor. // what about SPatchingChain? seems sane. units.insertAfter(addf, u); /* we need to be careful with insertBefore, if target already had some other predecessors. */ // handle immediate predecessor if it falls through // *** FIXME: Does SPatchingChain do the right thing? PREDFALLSTHROUGH: { Unit predOfTarget = null; try{ predOfTarget = (Unit) units.getPredOf(target); } catch(NoSuchElementException e){ predOfTarget = null; } if(predOfTarget == null) break PREDFALLSTHROUGH; if(predOfTarget.fallsThrough()){ GotoStmt gotoStmt = Jimple.v().newGotoStmt(target); units.insertAfter(gotoStmt, predOfTarget); } } // we do not want to move the pointers for other branching statements units.getNonPatchingChain().insertBefore(addt, target); u.setTarget(addt); } public void piHandleSwitchStmt(Local local, Unit u) { List<UnitBox> targetBoxes = new ArrayList<UnitBox>(); List targetKeys = new ArrayList(); if(u instanceof LookupSwitchStmt){ LookupSwitchStmt lss = (LookupSwitchStmt) u; targetBoxes.add(lss.getDefaultTargetBox()); targetKeys.add("default"); for(int i = 0; i < lss.getTargetCount(); i++) targetBoxes.add(lss.getTargetBox(i)); targetKeys.addAll(lss.getLookupValues()); } else if(u instanceof TableSwitchStmt){ TableSwitchStmt tss = (TableSwitchStmt) u; int low = tss.getLowIndex(); int hi = tss.getHighIndex(); targetBoxes.add(tss.getDefaultTargetBox()); targetKeys.add("default"); for(int i = 0; i <= (hi - low); i++) targetBoxes.add(tss.getTargetBox(i)); for(int i = low; i <= hi; i++) targetKeys.add(new Integer(i)); } else{ throw new RuntimeException("Assertion failed."); } for(int count = 0; count < targetBoxes.size(); count++){ UnitBox targetBox = targetBoxes.get(count); Unit target = targetBox.getUnit(); Object targetKey = targetKeys.get(count); PiExpr pi1 = Shimple.v().newPiExpr(local, u, targetKey); Unit add1 = Jimple.v().newAssignStmt(local, pi1); PatchingChain units = body.getUnits(); /* we need to be careful with insertBefore, if target already had some other predecessors. */ // handle immediate predecessor if it falls through // *** FIXME: Does SPatchingChain do the right thing? PREDFALLSTHROUGH: { Unit predOfTarget = null; try{ predOfTarget = (Unit) units.getPredOf(target); } catch(NoSuchElementException e){ predOfTarget = null; } if(predOfTarget == null) break PREDFALLSTHROUGH; if(predOfTarget.fallsThrough()){ GotoStmt gotoStmt = Jimple.v().newGotoStmt(target); units.insertAfter(gotoStmt, predOfTarget); } } // we do not want to move the pointers for other branching statements units.getNonPatchingChain().insertBefore(add1, target); targetBox.setUnit(add1); } } public void eliminatePiNodes(boolean smart) { if(smart){ Map<Local, Value> newToOld = new HashMap<Local, Value>(); List boxes = new ArrayList(); for(Iterator unitsIt = body.getUnits().iterator(); unitsIt.hasNext();){ Unit u = (Unit) unitsIt.next(); PiExpr pe = Shimple.getPiExpr(u); if(pe != null){ newToOld.put(Shimple.getLhsLocal(u), pe.getValue()); unitsIt.remove(); } else{ boxes.addAll(u.getUseBoxes()); } } for(Iterator boxesIt = boxes.iterator(); boxesIt.hasNext();){ ValueBox box = (ValueBox) boxesIt.next(); Value value = box.getValue(); Value old = newToOld.get(value); if(old != null) box.setValue(old); } DeadAssignmentEliminator.v().transform(body); CopyPropagator.v().transform(body); DeadAssignmentEliminator.v().transform(body); } else{ for(Iterator unitsIt = body.getUnits().iterator(); unitsIt.hasNext();){ Unit u = (Unit) unitsIt.next(); PiExpr pe = Shimple.getPiExpr(u); if(pe != null) ((AssignStmt)u).setRightOp(pe.getValue()); } } } public static List getUseBoxesFromBlock(Block block) { Iterator unitsIt = block.iterator(); List useBoxesList = new ArrayList(); while(unitsIt.hasNext()) useBoxesList.addAll(((Unit)unitsIt.next()).getUseBoxes()); return useBoxesList; } }