All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.toolkits.graph.LoopConditionUnroller Maven / Gradle / Ivy

There is a newer version: 2.5.0-9
Show newest version
/* Soot - a J*va Optimization Framework
 * Copyright (C) 2002 Florian Loitsch
 *
 * 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-2002.
 * 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.graph;
import soot.options.*;


import soot.*;
import soot.util.*;
import java.util.*;

import soot.jimple.*;
import soot.toolkits.graph.*;

/**
 * "unrolls" the condition of while/for loops.
* before the first test of a while-loop, we can't be sure, if the body will be * taken or not, and several optimizations (especially LCM) can't be done. In * this class we try to solve this problem by unrolling the condition of the * while-block: we make a copy of the condition-block, and redirect the * back-edge of the while-loop to the new block.
* After this transformation the edge between the original condition-block and * the loop-body is only executed once (and hence suitable for LCM) and we can * be sure, that the loop-body will get executed.
* Exceptions are ignored (the transformation is done on a * BriefBlockGraph. */ public class LoopConditionUnroller extends BodyTransformer { /** * contained blocks are currently visiting successors. We need this to find * back-edges. The "visitedBlocks" is not enough, as Java Bytecodes migth not * be in tree-form. */ private Set visitingSuccs; private Set visitedBlocks; private int maxSize; private Body body; private Map unitsToTraps; /** * unrolls conditions. */ /* this implementation still fails in finding all possible while-loops, but * does a good job. */ protected void internalTransform(Body body, String phaseName, Map options) { if(Options.v().verbose()) G.v().out.println("[" + body.getMethod().getName() + "] Unrolling Loop Conditions..."); visitingSuccs = new HashSet(); visitedBlocks = new HashSet(); this.body = body; this.maxSize = PhaseOptions.getInt(options, "maxSize"); BlockGraph bg = new BriefBlockGraph(body); Iterator headIter = bg.getHeads().iterator(); while (headIter.hasNext()) unrollConditions((Block)headIter.next()); if(Options.v().verbose()) G.v().out.println("[" + body.getMethod().getName() + "] Unrolling Loop Conditions done."); } /** * inserts a JimpleGoto to target, directly after * node in the unitChain of the body.
* As we use JGoto the chain must contain Jimple-stmts. * * @param node the Goto will be inserted just after this node. * @param target is the Unit the goto will jump to. * @return the newly inserted Goto */ private Unit insertGotoAfter(Unit node, Unit target) { Unit newGoto = Jimple.v().newGotoStmt(target); body.getUnits().insertAfter(newGoto, node); return newGoto; } /** * inserts a clone of toClone after node in the * unitChain.
* Everything is done in Jimple. * * @param node the Unit after which we insert the clone. * @param toClone the Unit that will get cloned and then inserted. */ private Unit insertCloneAfter(Chain unitChain, Unit node, Unit toClone) { Unit clone = (Unit)toClone.clone(); body.getUnits().insertAfter(clone, node); return clone; } /** * "calculates" the length of the given block in Units. * * @param block * @return the size of block. */ private int getSize(Block block) { int size = 0; Chain unitChain = body.getUnits(); for (Unit unit = block.getHead(); unit != block.getTail(); unit = (Unit)unitChain.getSuccOf(unit)) size++; size++; //add 1 for the tail we did not take into account. return size; } /** * returns a mapping of units to trap-changes. whenever the scope of * a trap changes (ie. a trap opens or closes), an entry is added in * the map, and the unit is mapped to the trap. The values * associated to the keys are lists, as more than one exception can * change at a unit.
Even if a trap opens and closes at a unit, * this trap is only reported once (ie. is only once in the list). * * @return the map of units to changing traps. */ private Map getTraps() { /* if we already did the "calculation" return the cached result.*/ if (unitsToTraps != null) return unitsToTraps; unitsToTraps = new HashMap(); Iterator trapsIt = body.getTraps().iterator(); while (trapsIt.hasNext()) { Trap trap = (Trap)trapsIt.next(); Unit beginUnit = trap.getBeginUnit(); List unitTraps = unitsToTraps.get(beginUnit); if (unitTraps == null) { unitTraps = new ArrayList(); unitsToTraps.put(beginUnit, unitTraps); } unitTraps.add(trap); Unit endUnit = trap.getEndUnit(); if (endUnit != beginUnit) { unitTraps = unitsToTraps.get(endUnit); if (unitTraps == null) { unitTraps = new ArrayList(); unitsToTraps.put(endUnit, unitTraps); } unitTraps.add(trap); } } return unitsToTraps; } /** * puts a copy (clone) of the given block in the unitChain. The * block is ensured to have the same exceptions as the original * block. (So we will modify the exception-chain). Furthermore the * inserted block will not change the behaviour of the program.
* Without any further modifications the returned block is * unreachable. To make it reachable one must goto to * the returned head of the new block. * * @param block the Block to clone. * @return the head of the copied block. */ private Unit copyBlock(Block block) { Map traps = getTraps(); Set openedTraps = new HashSet(); Map copiedTraps = new HashMap(); Chain unitChain = body.getUnits(); Unit tail = block.getTail(); Unit immediateSucc = (Unit)unitChain.getSuccOf(tail); Unit newGoto = insertGotoAfter(tail, immediateSucc); Unit last = newGoto; //the last inserted unit. boolean first = true; Unit copiedHead = null; for (Unit currentUnit = block.getHead(); currentUnit != newGoto; currentUnit = (Unit)unitChain.getSuccOf(currentUnit)) { last = insertCloneAfter(unitChain, last, currentUnit); if (first) { first = false; copiedHead = last; } /* the traps...: if a trap is closed (in the original block) that hasn't * been opened before, we have to open it at the beginning of the copied * block. If a trap gets opened, but not closed, we only have to close it * at the end of the (original) block (as it will be open at the end of * the copied block.)*/ List currentTraps = traps.get(currentUnit); if (currentTraps != null) { Iterator trapIt = currentTraps.iterator(); while(trapIt.hasNext()) { Trap trap = (Trap)trapIt.next(); if (trap.getBeginUnit() == currentUnit) { Trap copiedTrap = (Trap)trap.clone(); copiedTrap.setBeginUnit(last); copiedTraps.put(trap, copiedTrap); openedTraps.add(copiedTrap); // insertAfter(toInsert, point) body.getTraps().insertAfter(copiedTrap, trap); } if (trap.getEndUnit() == currentUnit) { Trap copiedTrap = copiedTraps.get(trap); if (copiedTrap == null) { /* trap has been opened before the current block */ copiedTrap = (Trap)trap.clone(); copiedTrap.setBeginUnit(copiedHead); body.getTraps().insertAfter(copiedTrap, trap); } else { openedTraps.remove(copiedTrap); } copiedTrap.setEndUnit(last); } } } } /* close all open traps */ Iterator openedIterator = openedTraps.iterator(); while(openedIterator.hasNext()) { openedIterator.next().setEndUnit(last); } return copiedHead; } /** * recursively searches for back-edges. if the found block is a * condition-block makes a clone and redirects the back-edge. * * @param block the current Block. */ private void unrollConditions(Block block) { /* if the block was already visited we can leave... */ if (visitedBlocks.contains(block)) return; // should never happen visitedBlocks.add(block); visitingSuccs.add(block); //currently visiting successors Iterator succsIt = block.getSuccs().iterator(); while (succsIt.hasNext()) { Block succ = (Block)succsIt.next(); if (visitedBlocks.contains(succ)) { if (succ != block && visitingSuccs.contains(succ)) { /* we only want blocks with at least 2 predecessors, to avoid that a * copied while-condition gets copied again in a future pass of * unrollConditions */ if (succ.getPreds().size() >= 2 && succ.getSuccs().size() == 2) { Block condition = succ; //just renaming for clearer code Block loopTailBlock = block; //just renaming for clearer code if (getSize(condition) <= maxSize) { Unit copiedHead = copyBlock(condition); /* now just redirect the tail of the loop-body */ Unit loopTail = loopTailBlock.getTail(); if (loopTail instanceof GotoStmt) ((GotoStmt)loopTail).setTarget(copiedHead); else if (loopTail instanceof IfStmt) { if (((IfStmt)loopTail).getTarget() == condition.getHead()) ((IfStmt)loopTail).setTarget(copiedHead); else insertGotoAfter(loopTail, copiedHead); } else insertGotoAfter(loopTail, copiedHead); } } } } else { /* unvisited successor */ unrollConditions(succ); } } visitingSuccs.remove(block); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy