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

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

package soot.jimple.toolkits.graph;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2002 Florian Loitsch
 * %%
 * This program 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 program 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 General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.Body;
import soot.BodyTransformer;
import soot.G;
import soot.Singletons;
import soot.Unit;
import soot.UnitBox;
import soot.jimple.Jimple;
import soot.options.Options;
import soot.util.Chain;

/**
 * removes all critical edges.
* A critical edge is an edge from Block A to block B, if B has more than one predecessor and A has more the one * successor.
* As an example: If we wanted a computation to be only on the path A->B this computation must be directly on the edge. * Otherwise it is either executed on the path through the second predecessor of A or throught the second successor of B.
* Our critical edge-remover overcomes this problem by introducing synthetic nodes on this critical edges.
* Exceptions will be ignored. */ public class CriticalEdgeRemover extends BodyTransformer { private static final Logger logger = LoggerFactory.getLogger(CriticalEdgeRemover.class); public CriticalEdgeRemover(Singletons.Global g) { } public static CriticalEdgeRemover v() { return G.v().soot_jimple_toolkits_graph_CriticalEdgeRemover(); } /** * performs critical edge-removing. */ protected void internalTransform(Body b, String phaseName, Map options) { if (Options.v().verbose()) { logger.debug("[" + b.getMethod().getName() + "] Removing Critical Edges..."); } removeCriticalEdges(b); if (Options.v().verbose()) { logger.debug("[" + b.getMethod().getName() + "] Removing Critical Edges done."); } } /** * inserts a JimpleGoto to target, directly after * node in the given unitChain.
* As we use JGoto the chain must contain Jimple-stmts. * * @param unitChain * the Chain where we will insert the Goto. * @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 static Unit insertGotoAfter(Chain unitChain, Unit node, Unit target) { Unit newGoto = Jimple.v().newGotoStmt(target); unitChain.insertAfter(newGoto, node); return newGoto; } /** * inserts a JimpleGoto to target, directly before * node in the given unitChain.
* As we use JGoto the chain must contain Jimple-stmts. * * @param unitChain * the Chain where we will insert the Goto. * @param node * the Goto will be inserted just before this node. * @param target * is the Unit the goto will jump to. * @return the newly inserted Goto */ /* note, that this method has slightly more overhead than the insertGotoAfter */ private static Unit insertGotoBefore(Chain unitChain, Unit node, Unit target) { Unit newGoto = Jimple.v().newGotoStmt(target); unitChain.insertBefore(newGoto, node); newGoto.redirectJumpsToThisTo(node); return newGoto; } /** * takes node and redirects all branches to oldTarget to newTarget. * * @param node * the Unit where we redirect * @param oldTarget * @param newTarget */ private static void redirectBranch(Unit node, Unit oldTarget, Unit newTarget) { for (UnitBox targetBox : node.getUnitBoxes()) { Unit target = targetBox.getUnit(); if (target == oldTarget) { targetBox.setUnit(newTarget); } } } /** * splits critical edges by introducing synthetic nodes.
* This method will modify the UnitGraph of the body. Synthetic nodes are always JGotos. * Therefore the body must be in Jimple.
* As a side-effect, after the transformation, the direct predecessor of a block/node with multiple predecessors will will * not fall through anymore. This simplifies the algorithm and is nice to work with afterwards. * * @param b * the Jimple-body that will be physicly modified so that there are no critical edges anymore. */ /* * note, that critical edges can only appear on edges between blocks!. Our algorithm will *not* take into account * exceptions. (this is nearly impossible anyways) */ private void removeCriticalEdges(Body b) { Chain unitChain = b.getUnits(); int size = unitChain.size(); Map> predecessors = new HashMap>(2 * size + 1, 0.7f); /* * First get the predecessors of each node (although direct predecessors are predecessors too, we'll not include them in * the lists) */ { Iterator unitIt = unitChain.snapshotIterator(); while (unitIt.hasNext()) { Unit currentUnit = (Unit) unitIt.next(); Iterator succsIt = currentUnit.getUnitBoxes().iterator(); while (succsIt.hasNext()) { Unit target = succsIt.next().getUnit(); List predList = predecessors.get(target); if (predList == null) { predList = new ArrayList(); predList.add(currentUnit); predecessors.put(target, predList); } else { predList.add(currentUnit); } } } } { /* * for each node: if we have more than two predecessors, split these edges if the node at the other end has more than * one successor. */ /* we need a snapshotIterator, as we'll modify the structure */ Iterator unitIt = unitChain.snapshotIterator(); Unit currentUnit = null; Unit directPredecessor; while (unitIt.hasNext()) { directPredecessor = currentUnit; currentUnit = unitIt.next(); List predList = predecessors.get(currentUnit); int nbPreds = (predList == null) ? 0 : predList.size(); if (directPredecessor != null && directPredecessor.fallsThrough()) { nbPreds++; } if (nbPreds >= 2) { /* * redirect the directPredecessor (if it falls through), so we can easily insert the synthetic nodes. This * redirection might not be necessary, but is pleasant anyways (see the Javadoc for this method) */ if (directPredecessor != null && directPredecessor.fallsThrough()) { directPredecessor = insertGotoAfter(unitChain, directPredecessor, currentUnit); } /* * if the predecessors have more than one successor insert the synthetic node. */ Iterator predIt = predList.iterator(); while (predIt.hasNext()) { Unit predecessor = predIt.next(); /* * Although in Jimple there should be only two ways of having more than one successor (If and Case) we'll do it * the hard way:) */ int nbSuccs = predecessor.getUnitBoxes().size(); nbSuccs += predecessor.fallsThrough() ? 1 : 0; if (nbSuccs >= 2) { /* * insert synthetic node (insertGotoAfter should be slightly faster) */ if (directPredecessor == null) { directPredecessor = insertGotoBefore(unitChain, currentUnit, currentUnit); } else { directPredecessor = insertGotoAfter(unitChain, directPredecessor, currentUnit); } /* update the branch */ redirectBranch(predecessor, currentUnit, directPredecessor); } } } } } } }