Package edu.cmu.cs.crystal.flow.worklist

Source Code of edu.cmu.cs.crystal.flow.worklist.WorklistTemplate

/**
* Copyright (c) 2006, 2007, 2008 Marwan Abi-Antoun, Jonathan Aldrich, Nels E. Beckman,
* Kevin Bierhoff, David Dickey, Ciera Jaspan, Thomas LaToza, Gabriel Zenarosa, and others.
*
* This file is part of Crystal.
*
* Crystal 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 3 of the License, or
* (at your option) any later version.
*
* Crystal 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 Crystal.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.cmu.cs.crystal.flow.worklist;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import edu.cmu.cs.crystal.cfg.ICFGEdge;
import edu.cmu.cs.crystal.cfg.ICFGNode;
import edu.cmu.cs.crystal.cfg.IControlFlowGraph;
import edu.cmu.cs.crystal.flow.AnalysisDirection;
import edu.cmu.cs.crystal.flow.BooleanLabel;
import edu.cmu.cs.crystal.flow.IAbstractLatticeOperations;
import edu.cmu.cs.crystal.flow.ILabel;
import edu.cmu.cs.crystal.flow.ILatticeOperations;
import edu.cmu.cs.crystal.flow.IResult;
import edu.cmu.cs.crystal.flow.NormalLabel;

/**
* This class encapsulates a worklist algorithm for computing fixed points
* over flow graphs as a <i>Template Method</i> {@link #performAnalysis()}
* Subclasses in particular need to provide
* <ul>
* <li>a flow graph,</li>
* <li>a analysis direction,</li>
* <li>lattice operations,</li>
* <li>an initial lattice value, and</li>
* <li>a way of transferring over flow graph nodes.</li>
* </ul>
* While branch sensitivity is achieved in specific implementations of the transfer method,
* the implementation keeps incoming analysis results from different branches separate.
* This allows precise treatment of short-circuiting Java operators and backwards analysis results.
*
* @author Kevin Bierhoff
*/
public abstract class WorklistTemplate<LE, N, OP extends IAbstractLatticeOperations<LE, N>>  {
 
  private static final Logger log = Logger.getLogger(WorklistTemplate.class.getName());
 
  /**
     * Carries out the worklist algorithm to discover the results
     * of the ASTNode argument.  This method implements the <i>Template
     * Method</i> pattern: It calls abstract methods defined in this class
     * at the appropriate moments.
     *
     * @see #getAnalysisDirection()
     * @see #getControlFlowGraph()
     * @see #getLatticeOperations()
     * @see #getEntryValue()
     * @see #transferNode(ICFGNode, Object, ILabel)
     */
    public AnalysisResult<LE, N, OP> performAnalysis() {
     
    // Setup result mappings
      HashMap<ICFGNode<N>, IResult<LE>> labeledResultsBefore = new HashMap<ICFGNode<N>, IResult<LE>>();
      HashMap<ICFGNode<N>, IResult<LE>> labeledResultsAfter = new HashMap<ICFGNode<N>, IResult<LE>>();
      HashMap<N, Set<ICFGNode<N>>> nodeMap = new HashMap<N, Set<ICFGNode<N>>>();
   
    // 0. Verify and Collect required data: direction, lattice, CFG
    AnalysisDirection direction;
    IControlFlowGraph<N> cfg;
    OP ops;
    LE entry;
   
    direction = getAnalysisDirection();
    assert direction != null : "Cannot perform dataflow analysis without a direction";
    ops = getLatticeOperations();
    assert ops != null : "Cannot perform analysis without lattice operations";
    entry = getEntryValue();
    if(entry == null)
      // always check this one since not sure when this would fail subsequently
      throw new NullPointerException("Cannot perform dataflow analysis without entry analysis information");
    cfg = getControlFlowGraph();
    assert cfg != null : "Cannot perform dataflow analysis without a CFG";

    // Populate fields about the current analysis
    boolean isForward = direction.equals(AnalysisDirection.FORWARD_ANALYSIS);
    Map<ICFGNode<N>, IResult<LE>> resultsBeforeAnalyzing; 
    Map<ICFGNode<N>, IResult<LE>> resultsAfterAnalyzing;
    // make result mappings relative to analysis direction
    // will use results[Before|After]Analyzing throughout the algorithm
    if (isForward) {
      resultsBeforeAnalyzing = labeledResultsBefore;
      resultsAfterAnalyzing = labeledResultsAfter;
    }
    else {
      resultsBeforeAnalyzing = labeledResultsAfter;
      resultsAfterAnalyzing = labeledResultsBefore;
    }

    // 1. Set up worklist with initial node.
    SortedSet<ICFGNode<N>> worklist = new TreeSet<ICFGNode<N>>(
        WorklistNodeOrderComparator.createPostOrderAndPopulateNodeMap(cfg, nodeMap, isForward));

    ICFGNode<N> initialNode = isForward ? cfg.getStartNode() : cfg.getEndNode();
    worklist.add(initialNode);
    resultsBeforeAnalyzing.put(initialNode, new IncomingResult<LE>(entry));
   
    // 2. LOOP Until Worklist is Empty
    while (! worklist.isEmpty()) {
     
      // Pop a ControlFlowNode off the stack
      // Pick last in post-order to visit nodes in "reverse" post-order
      ICFGNode<N> fromNode = worklist.last();
      worklist.remove(fromNode);
     
      try {
        // 2a. Establish before-node analysis result
       
        // Retrieve the lattice information from the fromNode
        // It's an error if this information doesn't exist
        IResult<LE> beforeFromLattice = checkNull(resultsBeforeAnalyzing.get(fromNode));
 
        // 2b. transfer over node
        IResult<LE> afterResults = null;
        for (ILabel transferLabel : beforeFromLattice.keySet()) {
       
          // Create a copy of the lattice to protect it from accidental
          // manipulation by the transfer function.
          LE beforeFromLatticeCopy = checkNull(ops.copy(beforeFromLattice.get(transferLabel)));
         
          // Carry out the associated flow function with the copy lattice
          IResult<LE> transferResults =
            checkNull(transferNode(fromNode, beforeFromLatticeCopy, transferLabel));
             
          if (afterResults == null)
            afterResults = transferResults;
          else
            afterResults = checkNull(afterResults.join(transferResults, ops));
        }   
           
        // put result back in
        resultsAfterAnalyzing.put(fromNode, checkNull(afterResults));
             
        // 2c. Transfer over following edges
        for (ICFGEdge<N> edge : (isForward ? fromNode.getOutputs() : fromNode.getInputs())) {
          ILabel edgeLabel = edge.getLabel();
          ILabel toLabel = incomingLabel(edgeLabel);
         
          // 2c-i. Find node and lattice to merge
          ICFGNode<N> toNode = isForward ? edge.getSink() : edge.getSource();
          LE mergeIntoNode = afterResults.get(edgeLabel);
         
          // 2c-ii. Update following node
          if (resultsBeforeAnalyzing.containsKey(toNode)) {
            // Get the previously stored "before" results of the toNode
            IncomingResult<LE> beforeToResults = (IncomingResult<LE>) resultsBeforeAnalyzing.get(toNode);
            // If the child node's before lattice is not null and the beforeTo
            // is more precise than the result then join and revisit this child.
            if (! beforeToResults.keySet().contains(toLabel)) {
              // no previous result for this branch
              beforeToResults.put(toLabel, checkNull(mergeIntoNode));
            }
            else if (! ops.atLeastAsPrecise(mergeIntoNode, beforeToResults.get(toLabel),
                toNode.getASTNode())) {
              if(ops.atLeastAsPrecise(beforeToResults.get(toLabel), mergeIntoNode, toNode.getASTNode()))
                // no need to join, just override existing result
                beforeToResults.put(toLabel, mergeIntoNode);
              else {
                // Make a deep copy of the result lattice
                LE beforeToLatticeCopy = checkNull(ops.copy(beforeToResults.get(toLabel)));
                LE resultLatticeCopy = checkNull(ops.copy(mergeIntoNode));
                // Store the join of the resultLattice and the beforeToLattice
                beforeToResults.put(toLabel, checkNull(
                    ops.join(beforeToLatticeCopy, resultLatticeCopy, toNode.getASTNode())));
              }
            }
            else
              // in this case we did not update the lattice, so don't change the results
              continue;
          }
          else
            // no previous "before" result for toNode
            resultsBeforeAnalyzing.put(toNode, new IncomingResult<LE>(mergeIntoNode, toLabel));
         
          // 2c-iii. Add to the worklist for further processing
          worklist.add(toNode);
        }
      }
      catch(RuntimeException e) {
        // for debugging purposes, catch and rethrow exceptions to print out source AST node where it happened
        log.log(Level.WARNING, "Runtime exception processing node: " + fromNode + " with code " + fromNode.getASTNode(), e);
        throw e;
      }
    }
    return createAnalysisResult(labeledResultsBefore, labeledResultsAfter, nodeMap,
                            ops, cfg.getStartNode(), cfg.getEndNode());
    }

    /**
     * Creates an analysis result object from the given result maps.
     * @param labeledResultsBefore Labeled results before AST nodes (relative to normal control flow).
     * @param labeledResultsAfter Labeled results after AST nodes (relative to normal control flow).
     * @param nodeMap Map from AST to CFG nodes, to cover the case where one AST node maps to multiple CFG nodes.
     * @param ops Lattice operations used for computing results;
     * this is useful in particular for acquiring {@link ILatticeOperations#bottom()} later
     * @param _startNode Start node in the control flow graph
     * @param _endNode End node in the control flow graph
     * @return Analysis result object holding the given parameters.
     */
  protected AnalysisResult<LE, N, OP> createAnalysisResult(
      Map<ICFGNode<N>, IResult<LE>> labeledResultsBefore,
      Map<ICFGNode<N>, IResult<LE>> labeledResultsAfter,
      Map<N, Set<ICFGNode<N>>> nodeMap,
      OP ops, ICFGNode<N> _startNode, ICFGNode<N> _endNode) {
    return new AnalysisResult<LE, N, OP>(nodeMap, labeledResultsAfter, labeledResultsBefore, ops, _startNode, _endNode);
  }

  /**
   * Turns label on edge into label for incoming analysis results.
   * {@link edu.cmu.cs.crystal.flow.BooleanLabel}s will be used as-is;
   * any other label is replaced by {@link edu.cmu.cs.crystal.flow.NormalLabel}.
   * @param edgeLabel Label retrieved from edge
   * @return Label for {@link IncomingResult}
   *
   * @see IncomingResult
   */
    protected ILabel incomingLabel(ILabel edgeLabel) {
    if(edgeLabel instanceof BooleanLabel)
      return edgeLabel;
    return NormalLabel.getNormalLabel();
  }

  /**
   * Implement this method to determine the analysis direction for this worklist run.
   * This method will be invoked once per worklist instance.
   * @return Analysis direction for this worklist run.
   */
  protected abstract AnalysisDirection getAnalysisDirection();

  /**
   * Implement this method to create a control flow graph for this worklist run.
   * This method will be invoked once per worklist instance.
   * @return Control flow graph for this worklist run.
   */
  protected abstract IControlFlowGraph<N> getControlFlowGraph();

  /**
   * Implement this method to create the lattice operations to be used in this worklist run.
   * This method will be invoked once per worklist instance.
   * @return Lattice operations to be used in this worklist run.
   */
  protected abstract OP getLatticeOperations();

  /**
   * Implement this method to create an entry lattice value to be used in this worklist run.
   * This method will be invoked once per worklist instance.
   * @return Entry lattice value to be used in this worklist run.
   */
  protected abstract LE getEntryValue();
 
  /**
   * Implement this method to transfer over the given CFG node based on an
   * incoming lattice element for a given label.  The label is determined
   * with {@link #incomingLabel(ILabel)} and distinguishes incoming analysis
   * results along different kinds of edges.  It is <i>recommended</i> to
   * treat nodes for &&, ||, and ! specially based on the <code>transferLabel</code>:
   * && and || should only return a result for a given {@link edu.cmu.cs.crystal.flow.BooleanLabel};
   * ! should only return a result for the opposite {@link edu.cmu.cs.crystal.flow.BooleanLabel}.
   * @param cfgNode The CFG node to transfer over.  Notice that, in the case of a dummy node,
   * there may not be an AST node associated with the CFG node.
   * @param incoming The incoming lattice element (relative to the analysis direction).
   * @param transferLabel Label to distinguish analysis results along different
   * kinds of edges.
   * @return Analysis results for labels occurring on the given node's outgoing edges
   * (relative to the analysis direction).
   */
  protected abstract IResult<LE> transferNode(
      ICFGNode<N> cfgNode,
      LE incoming, ILabel transferLabel);
 
  /**
   * Returns a given object only if non-<code>null</code>; throws an exception otherwise.
   * @param <T>
   * @param o An object
   * @return The given object, if non-<code>null</code>.
   * @throws NullPointerException If the given object is null.
   */
  protected static <T> T checkNull(T o) {
    assert o != null : "No value available.";
    return o;
  }

  /**
   * Internal class to hold incoming analysis results distinguished by
   * false, true, and other incoming edges.
   *
   * @author Kevin Bierhoff
   *
   * @param <LE> Lattice element type held by the result.
   */
  protected static class IncomingResult<LE> implements IResult<LE> {
   
    private LE normalResult;
    private LE falseResult;
    private LE trueResult;
   
    /**
     * Create new result with given lattice value as normal result
     * @param normalResult
     * @see edu.cmu.cs.crystal.flow.NormalLabel
     */
    public IncomingResult(LE normalResult) {
      this.normalResult = checkNull(normalResult);
    }

    /**
     * Create new result with given lattice value as boolean result
     * @param branchResult
     * @param branchValue Determine whether <code>branchResult</code> should
     * be the <code>true</code> or <code>false</code> result.
     * @see edu.cmu.cs.crystal.flow.BooleanLabel
     */
    public IncomingResult(LE branchResult, boolean branchValue) {
      if(branchValue == true)
        trueResult = checkNull(branchResult);
      else
        falseResult = checkNull(branchResult);
    }

    /**
     * Create new result with given lattice value as result for given label.
     * @param label Label to replace value for: {@link edu.cmu.cs.crystal.flow.NormalLabel}
     * or {@link edu.cmu.cs.crystal.flow.BooleanLabel}.
     * @see edu.cmu.cs.crystal.flow.NormalLabel
     */
    public IncomingResult(LE result, ILabel label) {
      put(label, result);
    }

    /**
     * This constructor is to be <b>only used internally by {@link #join(IResult)}</b>.
     * It sets the three lattice elements tracked by this result to the given values.
     * At least one of the parameters must be non-<code>null</code>.
     * @param normalResult
     * @param falseResult
     * @param trueResult
     */
    protected IncomingResult(LE normalResult, LE falseResult, LE trueResult) {
      if(normalResult == null && falseResult == null && trueResult == null)
        throw new NullPointerException("Only null results provided");
      this.normalResult = normalResult;
      this.falseResult = falseResult;
      this.trueResult = trueResult;
    }

    public LE get(ILabel label) {
      if(NormalLabel.getNormalLabel().equals(label))
        return checkNull(normalResult);
      if(BooleanLabel.getBooleanLabel(false).equals(label))
        return checkNull(falseResult);
      if(BooleanLabel.getBooleanLabel(true).equals(label))
        return checkNull(trueResult);
      throw new IllegalArgumentException("Unknown label: " + label);
    }
   
    /**
     * Replaces the lattice value for the given label with a new lattice value
     * @param label Label to set value for: {@link edu.cmu.cs.crystal.flow.NormalLabel}
     * or {@link edu.cmu.cs.crystal.flow.BooleanLabel}.
     * @param result The new lattice value.
     */
    public void put(ILabel label, LE result) {
      if(NormalLabel.getNormalLabel().equals(label))
        normalResult = checkNull(result);
      else if(BooleanLabel.getBooleanLabel(false).equals(label))
        falseResult = checkNull(result);
      else if(BooleanLabel.getBooleanLabel(true).equals(label))
        trueResult = checkNull(result);
      else
        throw new IllegalArgumentException("Unknown label: " + label);
    }

    public Set<ILabel> keySet() {
      HashSet<ILabel> result = new HashSet<ILabel>(3);
      if(normalResult != null)
        result.add(NormalLabel.getNormalLabel());
      if(falseResult != null)
        result.add(BooleanLabel.getBooleanLabel(false));
      if(trueResult != null)
        result.add(BooleanLabel.getBooleanLabel(true));
      return result;
    }

    public IResult<LE> join(IResult<LE> otherResult, IAbstractLatticeOperations<LE, ?> op) {
      if(otherResult == null)
        return this;
      if(otherResult instanceof IncomingResult) {
        IncomingResult<LE> other = (IncomingResult<LE>) otherResult;
        LE nrm = this.normalResult;
        if(other.normalResult != null)
          nrm = (nrm == null) ? other.normalResult : op.join(op.copy(nrm), op.copy(other.normalResult), null);
        LE tru = this.trueResult;
        if(other.trueResult != null)
          tru = (tru == null) ? other.trueResult : op.join(op.copy(tru), op.copy(other.trueResult), null);
        LE fls = this.falseResult;
        if(other.falseResult != null)
          fls = (fls == null) ? other.falseResult : op.join(op.copy(fls), op.copy(other.falseResult), null);
        return new IncomingResult<LE>(nrm, fls, tru);
      }
      throw new IllegalStateException("Internal results should never be joined with results of type: " + otherResult.getClass());
    }
   
  }

}
TOP

Related Classes of edu.cmu.cs.crystal.flow.worklist.WorklistTemplate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.