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

soot.shimple.toolkits.scalar.SConstantPropagatorAndFolder Maven / Gradle / Ivy

package soot.shimple.toolkits.scalar;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 2003 Navindra Umanee 
 * %%
 * 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.Collection;
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.Local;
import soot.PhaseOptions;
import soot.Singletons;
import soot.Unit;
import soot.UnitBox;
import soot.UnitBoxOwner;
import soot.Value;
import soot.ValueBox;
import soot.jimple.Constant;
import soot.jimple.DefinitionStmt;
import soot.jimple.GotoStmt;
import soot.jimple.IfStmt;
import soot.jimple.IntConstant;
import soot.jimple.Jimple;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.Stmt;
import soot.jimple.TableSwitchStmt;
import soot.options.Options;
import soot.shimple.ShimpleBody;
import soot.shimple.toolkits.scalar.SEvaluator.BottomConstant;
import soot.shimple.toolkits.scalar.SEvaluator.MetaConstant;
import soot.shimple.toolkits.scalar.SEvaluator.TopConstant;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.toolkits.scalar.ArraySparseSet;
import soot.toolkits.scalar.FlowSet;
import soot.toolkits.scalar.ForwardBranchedFlowAnalysis;
import soot.toolkits.scalar.Pair;
import soot.toolkits.scalar.UnitValueBoxPair;
import soot.util.Chain;

/**
 * A powerful constant propagator and folder based on an algorithm sketched by Cytron et al that takes conditional control
 * flow into account. This optimization demonstrates some of the benefits of SSA -- particularly the fact that Phi nodes
 * represent natural merge points in the control flow.
 *
 * @author Navindra Umanee
 * @see Efficiently Computing Static Single Assignment Form and
 *      the Control Dependence Graph
 **/
public class SConstantPropagatorAndFolder extends BodyTransformer {
  private static final Logger logger = LoggerFactory.getLogger(SConstantPropagatorAndFolder.class);

  public SConstantPropagatorAndFolder(Singletons.Global g) {
  }

  public static SConstantPropagatorAndFolder v() {
    return G.v().soot_shimple_toolkits_scalar_SConstantPropagatorAndFolder();
  }

  protected ShimpleBody sb;
  protected boolean debug;

  protected void internalTransform(Body b, String phaseName, Map options) {
    if (!(b instanceof ShimpleBody)) {
      throw new RuntimeException("SConstantPropagatorAndFolder requires a ShimpleBody.");
    }

    this.sb = (ShimpleBody) b;

    if (!sb.isSSA()) {
      throw new RuntimeException("ShimpleBody is not in proper SSA form as required by SConstantPropagatorAndFolder."
          + "You may need to rebuild it or use ConstantPropagatorAndFolder instead.");
    }

    boolean pruneCFG = PhaseOptions.getBoolean(options, "prune-cfg");
    debug = Options.v().debug();
    debug |= sb.getOptions().debug();

    if (Options.v().verbose()) {
      logger.debug("[" + sb.getMethod().getName() + "] Propagating and folding constants (SSA)...");
    }

    // *** FIXME: What happens when Shimple is built with another UnitGraph?
    SCPFAnalysis scpf = new SCPFAnalysis(new ExceptionalUnitGraph(sb));

    propagateResults(scpf.getResults());
    if (pruneCFG) {
      removeStmts(scpf.getDeadStmts());
      replaceStmts(scpf.getStmtsToReplace());
    }
  }

  /**
   * Propagates constants to the definition and uses of the relevant locals given a mapping. Notice that we use the Shimple
   * implementation of LocalDefs and LocalUses.
   **/
  protected void propagateResults(Map localToConstant) {
    Chain units = sb.getUnits();
    Collection locals = sb.getLocals();
    ShimpleLocalDefs localDefs = new ShimpleLocalDefs(sb);
    ShimpleLocalUses localUses = new ShimpleLocalUses(sb);

    Iterator localsIt = locals.iterator();
    while (localsIt.hasNext()) {
      Local local = localsIt.next();
      Constant constant = localToConstant.get(local);

      if (constant instanceof MetaConstant) {
        continue;
      }

      // update definition
      {
        DefinitionStmt stmt = (DefinitionStmt) localDefs.getDefsOf(local).get(0);

        ValueBox defSrcBox = stmt.getRightOpBox();
        Value defSrc = defSrcBox.getValue();

        if (defSrcBox.canContainValue(constant)) {
          defSrcBox.setValue(constant);

          // remove dangling pointers
          if (defSrc instanceof UnitBoxOwner) {
            ((UnitBoxOwner) defSrc).clearUnitBoxes();
          }
        } else if (debug) {
          logger.warn("Couldn't propagate constant " + constant + " to box " + defSrcBox.getValue() + " in unit " + stmt);
        }
      }

      // update uses
      {
        Iterator usesIt = localUses.getUsesOf(local).iterator();

        while (usesIt.hasNext()) {
          UnitValueBoxPair pair = (UnitValueBoxPair) usesIt.next();
          ValueBox useBox = pair.getValueBox();

          if (useBox.canContainValue(constant)) {
            useBox.setValue(constant);
          } else if (debug) {
            logger.warn(
                "Couldn't propagate constant " + constant + " to box " + useBox.getValue() + " in unit " + pair.getUnit());
          }
        }
      }
    }
  }

  /**
   * Removes the given list of fall through IfStmts from the body.
   **/
  protected void removeStmts(List deadStmts) {
    Chain units = sb.getUnits();
    Iterator deadIt = deadStmts.iterator();
    while (deadIt.hasNext()) {
      Unit dead = deadIt.next();
      units.remove(dead);
      dead.clearUnitBoxes();
    }
  }

  /**
   * Replaces conditional branches by unconditional branches as given by the mapping.
   **/
  protected void replaceStmts(Map stmtsToReplace) {
    Chain units = sb.getUnits();
    Iterator stmtsIt = stmtsToReplace.keySet().iterator();
    while (stmtsIt.hasNext()) {
      // important not to call clearUnitBoxes() on booted since
      // replacement uses the same UnitBox
      Unit booted = stmtsIt.next();
      Unit replacement = stmtsToReplace.get(booted);
      units.swapWith(booted, replacement);
    }
  }
}

/**
 * The actual branching flow analysis implementation. Briefly, a sketch of the sketch from the Cytron et al paper:
 *
 * 

* Initially the algorithm assumes that each edge is unexecutable (the entry nodes are reachable) and that each variable is * constant with an unknown value, Top. Assumptions are corrected until they stabilise. * *

* For example, if q is found to be not a constant (Bottom) in if(q == 0) goto label1 then both edges * leaving the the statement are considered executable, if q is found to be a constant then only one of the edges * are executable. * *

* Whenever a reachable definition statement such as "x = 3" is found, the information is propagated to all uses of x (this * works due to the SSA property). * *

* Perhaps the crucial point is that if a node such as x = * Phi(x_1, x_2) is ever found, information on x is assumed as follows: * *

    *
  • If x_1 and x_2 are the same assumed constant, x is assumed to be that constant. If they are * not the same constant, x is Bottom.
  • * *
  • If either one is Top and the other is a constant, x is assumed to be the same as the known constant.
  • * *
  • If either is Bottom, x is assumed to be Bottom.
  • *
* *

* The crucial point about the crucial point is that if definitions of x_1 or x_2 are never reached, the * Phi node will still assume them to be Top and hence they will not influence the decision as to whether x is a * constant or not. **/ class SCPFAnalysis extends ForwardBranchedFlowAnalysis { protected FlowSet emptySet; /** * A mapping of the locals to their current assumed constant value (which may be Top or Bottom). **/ protected Map localToConstant; /** * A map from conditional branches to their possible replacement unit, an unconditional branch. **/ protected Map stmtToReplacement; /** * A list of IfStmts that always fall through. **/ protected List deadStmts; /** * Returns the localToConstant map. **/ public Map getResults() { return localToConstant; } /** * Returns the list of fall through IfStmts. **/ public List getDeadStmts() { return deadStmts; } /** * Returns a Map from conditional branches to the unconditional branches that can replace them. **/ public Map getStmtsToReplace() { return stmtToReplacement; } public SCPFAnalysis(UnitGraph graph) { super(graph); emptySet = new ArraySparseSet(); stmtToReplacement = new HashMap(); deadStmts = new ArrayList(); // initialise localToConstant map -- assume all scalars are // constant (Top) { Collection locals = graph.getBody().getLocals(); Iterator localsIt = locals.iterator(); localToConstant = new HashMap(graph.size() * 2 + 1, 0.7f); while (localsIt.hasNext()) { Local local = (Local) localsIt.next(); localToConstant.put(local, TopConstant.v()); } } doAnalysis(); } // *** NOTE: this is here because ForwardBranchedFlowAnalysis does // *** not handle exceptional control flow properly in the // *** dataflow analysis. this should be removed when // *** ForwardBranchedFlowAnalysis is fixed. protected boolean treatTrapHandlersAsEntries() { return true; } /** * If a node has empty IN sets we assume that it is not reachable. Hence, we initialise the entry sets to be non-empty to * indicate that they are reachable. **/ protected Object entryInitialFlow() { FlowSet entrySet = (FlowSet) emptySet.emptySet(); entrySet.add(TopConstant.v()); return entrySet; } /** * All other nodes are assumed to be unreachable by default. **/ protected Object newInitialFlow() { return emptySet.emptySet(); } /** * Since we are interested in control flow from all branches, take the union. **/ protected void merge(Object in1, Object in2, Object out) { FlowSet fin1 = (FlowSet) in1; FlowSet fin2 = (FlowSet) in2; FlowSet fout = (FlowSet) out; fin1.union(fin2, fout); } /** * Defer copy to FlowSet. **/ protected void copy(Object source, Object dest) { FlowSet fource = (FlowSet) source; FlowSet fest = (FlowSet) dest; fource.copy(fest); } /** * If a node has an empty in set, it is considered unreachable. Otherwise the node is examined and if any assumptions have * to be corrected, a Pair containing the corrected assumptions is flowed to the reachable nodes. If no assumptions have to * be corrected then no information other than the in set is propagated to the reachable nodes. * *

* Pair serves no other purpose than to keep the analysis flowing for as long as needed. The final results are accumulated * in the localToConstant map. **/ protected void flowThrough(Object in, Unit s, List fallOut, List branchOuts) { FlowSet fin = ((FlowSet) in).clone(); // not reachable if (fin.isEmpty()) { return; } // If s is a definition, check if any assumptions have to be // corrected. Pair pair = processDefinitionStmt(s); if (pair != null) { fin.add(pair); } // normal, non-branching statement if (!s.branches() && s.fallsThrough()) { Iterator fallOutIt = fallOut.iterator(); while (fallOutIt.hasNext()) { FlowSet fallSet = (FlowSet) fallOutIt.next(); fallSet.union(fin); } return; } /* determine which nodes are reachable. */ boolean conservative = true; boolean fall = false; boolean branch = false; FlowSet oneBranch = null; IFSTMT: { if (s instanceof IfStmt) { IfStmt ifStmt = (IfStmt) s; Value cond = ifStmt.getCondition(); Constant constant = SEvaluator.getFuzzyConstantValueOf(cond, localToConstant); // flow both ways if (constant instanceof BottomConstant) { deadStmts.remove(ifStmt); stmtToReplacement.remove(ifStmt); break IFSTMT; } // no flow if (constant instanceof TopConstant) { return; } /* determine whether to flow through or branch */ conservative = false; Constant trueC = IntConstant.v(1); Constant falseC = IntConstant.v(0); if (constant.equals(trueC)) { branch = true; GotoStmt gotoStmt = Jimple.v().newGotoStmt(ifStmt.getTargetBox()); stmtToReplacement.put(ifStmt, gotoStmt); } if (constant.equals(falseC)) { fall = true; deadStmts.add(ifStmt); } } } // end IFSTMT TABLESWITCHSTMT: { if (s instanceof TableSwitchStmt) { TableSwitchStmt table = (TableSwitchStmt) s; Value keyV = table.getKey(); Constant keyC = SEvaluator.getFuzzyConstantValueOf(keyV, localToConstant); // flow all branches if (keyC instanceof BottomConstant) { stmtToReplacement.remove(table); break TABLESWITCHSTMT; } // no flow if (keyC instanceof TopConstant) { return; } // flow all branches if (!(keyC instanceof IntConstant)) { break TABLESWITCHSTMT; } /* find the one branch we need to flow to */ conservative = false; int key = ((IntConstant) keyC).value; int low = table.getLowIndex(); int high = table.getHighIndex(); int index = key - low; UnitBox branchBox = null; if (index < 0 || index > high) { branchBox = table.getDefaultTargetBox(); } else { branchBox = table.getTargetBox(index); } GotoStmt gotoStmt = Jimple.v().newGotoStmt(branchBox); stmtToReplacement.put(table, gotoStmt); List unitBoxes = table.getUnitBoxes(); int setIndex = unitBoxes.indexOf(branchBox); oneBranch = (FlowSet) branchOuts.get(setIndex); } } // end TABLESWITCHSTMT LOOKUPSWITCHSTMT: { if (s instanceof LookupSwitchStmt) { LookupSwitchStmt lookup = (LookupSwitchStmt) s; Value keyV = lookup.getKey(); Constant keyC = SEvaluator.getFuzzyConstantValueOf(keyV, localToConstant); // flow all branches if (keyC instanceof BottomConstant) { stmtToReplacement.remove(lookup); break LOOKUPSWITCHSTMT; } // no flow if (keyC instanceof TopConstant) { return; } // flow all branches if (!(keyC instanceof IntConstant)) { break LOOKUPSWITCHSTMT; } /* find the one branch we need to flow to */ conservative = false; int index = lookup.getLookupValues().indexOf(keyC); UnitBox branchBox = null; if (index == -1) { branchBox = lookup.getDefaultTargetBox(); } else { branchBox = lookup.getTargetBox(index); } GotoStmt gotoStmt = Jimple.v().newGotoStmt(branchBox); stmtToReplacement.put(lookup, gotoStmt); List unitBoxes = lookup.getUnitBoxes(); int setIndex = unitBoxes.indexOf(branchBox); oneBranch = (FlowSet) branchOuts.get(setIndex); } } // end LOOKUPSWITCHSTMT // conservative control flow estimates if (conservative) { fall = s.fallsThrough(); branch = s.branches(); } if (fall) { Iterator fallOutIt = fallOut.iterator(); while (fallOutIt.hasNext()) { FlowSet fallSet = (FlowSet) fallOutIt.next(); fallSet.union(fin); } } if (branch) { Iterator branchOutsIt = branchOuts.iterator(); while (branchOutsIt.hasNext()) { FlowSet branchSet = (FlowSet) branchOutsIt.next(); branchSet.union(fin); } } if (oneBranch != null) { oneBranch.union(fin); } } /** * Returns (Unit, Constant) pair if an assumption has changed due to the fact that u is reachable. Else returns null. **/ protected Pair processDefinitionStmt(Unit u) { if (!(u instanceof DefinitionStmt)) { return null; } DefinitionStmt dStmt = (DefinitionStmt) u; Local local; { Value value = dStmt.getLeftOp(); if (!(value instanceof Local)) { return null; } local = (Local) value; } /* update assumptions */ Value rightOp = dStmt.getRightOp(); Constant constant = SEvaluator.getFuzzyConstantValueOf(rightOp, localToConstant); if (!merge(local, constant)) { return null; } return new Pair(u, localToConstant.get(local)); } /** * Verifies if the given assumption "constant" changes the previous assumption about "local" and merges the information * into the localToConstant map. Returns true if something changed. **/ protected boolean merge(Local local, Constant constant) { Constant current = localToConstant.get(local); if (current instanceof BottomConstant) { return false; } if (current instanceof TopConstant) { localToConstant.put(local, constant); return true; } if (current.equals(constant)) { return false; } // not equal localToConstant.put(local, BottomConstant.v()); return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy