WALA/com.ibm.wala.util/src/com/ibm/wala/fixedpoint/impl/AbstractFixedPointSolver.java

592 lines
17 KiB
Java

/*******************************************************************************
* Copyright (c) 2002 - 2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.fixedpoint.impl;
import java.util.Iterator;
import java.util.LinkedList;
import com.ibm.wala.fixpoint.AbstractOperator;
import com.ibm.wala.fixpoint.AbstractStatement;
import com.ibm.wala.fixpoint.FixedPointConstants;
import com.ibm.wala.fixpoint.IFixedPointSolver;
import com.ibm.wala.fixpoint.IFixedPointStatement;
import com.ibm.wala.fixpoint.IVariable;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.fixpoint.UnaryStatement;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.debug.VerboseAction;
/**
* Represents a set of {@link IFixedPointStatement}s to be solved by a {@link IFixedPointSolver}
*
* <p>
* Implementation Note:
*
* The set of steps and variables is internally represented as a graph. Each step and each variable is a node in the graph. If a
* step produces a variable that is used by another step, the graph has a directed edge from the producer to the consumer.
* Fixed-point iteration proceeds in a topological order according to these edges.
*/
@SuppressWarnings("rawtypes")
public abstract class AbstractFixedPointSolver<T extends IVariable> implements IFixedPointSolver<T>, FixedPointConstants,
VerboseAction {
static final boolean DEBUG = false;
static public final boolean verbose = ("true".equals(System.getProperty("com.ibm.wala.fixedpoint.impl.verbose")) ? true : false);
static public final int DEFAULT_VERBOSE_INTERVAL = 100000;
static final boolean MORE_VERBOSE = true;
static public final int DEFAULT_PERIODIC_MAINTENANCE_INTERVAL = 100000;
/**
* A tuning parameter; how may new IStatementDefinitionss must be added before doing a new topological sort? TODO: Tune this
* empirically.
*/
private int minSizeForTopSort = 0;
/**
* A tuning parameter; by what percentage must the number of equations grow before we perform a topological sort?
*/
private double topologicalGrowthFactor = 0.1;
/**
* A tuning parameter: how many evaluations are allowed to take place between topological re-orderings. The idea is that many
* evaluations may be a sign of a bad ordering, even when few new equations are being added.
*
* A number less than zero mean infinite.
*/
private int maxEvalBetweenTopo = 500000;
private int evaluationsAtLastOrdering = 0;
/**
* How many equations have been added since the last topological sort?
*/
int topologicalCounter = 0;
/**
* The next order number to assign to a new equation
*/
int nextOrderNumber = 1;
/**
* During verbose evaluation, holds the number of dataflow equations evaluated
*/
private int nEvaluated = 0;
/**
* During verbose evaluation, holds the number of dataflow equations created
*/
private int nCreated = 0;
/**
* worklist for the iterative solver
*/
protected Worklist workList = new Worklist();
/**
* A boolean which is initially true, but set to false after the first call to solve();
*/
private boolean firstSolve = true;
protected abstract T[] makeStmtRHS(int size);
/**
* Some setup which occurs only before the first solve
*/
public void initForFirstSolve() {
orderStatements();
initializeVariables();
initializeWorkList();
firstSolve = false;
}
/**
* @return true iff work list is empty
*/
public boolean emptyWorkList() {
return workList.isEmpty();
}
/**
* Solve the set of dataflow graph.
* <p>
* PRECONDITION: graph is set up
*
* @return true iff the evaluation of some equation caused a change in the value of some variable.
*/
@SuppressWarnings("unchecked")
public boolean solve(IProgressMonitor monitor) throws CancelException {
boolean globalChange = false;
if (firstSolve) {
initForFirstSolve();
}
while (!workList.isEmpty()) {
MonitorUtil.throwExceptionIfCanceled(monitor);
orderStatements();
// duplicate insertion detection
AbstractStatement s = workList.takeStatement();
if (DEBUG) {
System.err.println(("Before evaluation " + s));
}
byte code = s.evaluate();
if (verbose) {
nEvaluated++;
if (nEvaluated % getVerboseInterval() == 0) {
performVerboseAction();
}
if (nEvaluated % getPeriodicMaintainInterval() == 0) {
periodicMaintenance();
}
}
if (DEBUG) {
System.err.println(("After evaluation " + s + " " + isChanged(code)));
}
if (isChanged(code)) {
globalChange = true;
updateWorkList(s);
}
if (isFixed(code)) {
removeStatement(s);
}
}
return globalChange;
}
public void performVerboseAction() {
System.err.println("Evaluated " + nEvaluated);
System.err.println("Created " + nCreated);
System.err.println("Worklist " + workList.size());
if (MORE_VERBOSE) {
if (!workList.isEmpty()) {
AbstractStatement s = workList.takeStatement();
System.err.println("Peek " + lineBreak(s.toString(), 132));
if (s instanceof VerboseAction) {
((VerboseAction) s).performVerboseAction();
}
workList.insertStatement(s);
}
}
}
public static String lineBreak(String string, int wrap) {
if (string == null) {
throw new IllegalArgumentException("string is null");
}
if (string.length() > wrap) {
StringBuffer result = new StringBuffer();
int start = 0;
while (start < string.length()) {
int end = Math.min(start + wrap, string.length());
result.append(string.substring(start, end));
result.append("\n ");
start = end;
}
return result.toString();
} else {
return string;
}
}
public void removeStatement(AbstractStatement<T, ?> s) {
getFixedPointSystem().removeStatement(s);
}
@Override
public String toString() {
StringBuffer result = new StringBuffer("Fixed Point Sytem:\n");
for (Iterator it = getStatements(); it.hasNext();) {
result.append(it.next()).append("\n");
}
return result.toString();
}
public Iterator getStatements() {
return getFixedPointSystem().getStatements();
}
/**
* Add a step to the work list.
*
* @param s the step to add
*/
public void addToWorkList(AbstractStatement s) {
workList.insertStatement(s);
}
/**
* Add all to the work list.
*/
public void addAllStatementsToWorkList() {
for (Iterator i = getStatements(); i.hasNext();) {
AbstractStatement eq = (AbstractStatement) i.next();
addToWorkList(eq);
}
}
/**
* Call this method when the contents of a variable changes. This routine adds all graph using this variable to the set of new
* graph.
*
* @param v the variable that has changed
*/
public void changedVariable(T v) {
for (Iterator it = getFixedPointSystem().getStatementsThatUse(v); it.hasNext();) {
AbstractStatement s = (AbstractStatement) it.next();
addToWorkList(s);
}
}
/**
* Add a step with zero operands on the right-hand side.
*
* TODO: this is a little odd, in that this equation will never fire unless explicitly added to a work list. I think in most cases
* we shouldn't be creating this nullary form.
*
* @param lhs the variable set by this equation
* @param operator the step operator
* @throws IllegalArgumentException if lhs is null
*/
public void newStatement(final T lhs, final NullaryOperator<T> operator, final boolean toWorkList, final boolean eager) {
if (lhs == null) {
throw new IllegalArgumentException("lhs is null");
}
// add to the list of graph
lhs.setOrderNumber(nextOrderNumber++);
final NullaryStatement<T> s = new BasicNullaryStatement<T>(lhs, operator);
if (getFixedPointSystem().containsStatement(s)) {
return;
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
}
@SuppressWarnings("unchecked")
private void incorporateNewStatement(boolean toWorkList, boolean eager, AbstractStatement s) {
if (eager) {
byte code = s.evaluate();
if (verbose) {
nEvaluated++;
if (nEvaluated % getVerboseInterval() == 0) {
performVerboseAction();
}
if (nEvaluated % getPeriodicMaintainInterval() == 0) {
periodicMaintenance();
}
}
if (isChanged(code)) {
updateWorkList(s);
}
if (isFixed(code)) {
removeStatement(s);
}
} else if (toWorkList) {
addToWorkList(s);
}
}
/**
* Add a step with one operand on the right-hand side.
*
* @param lhs the lattice variable set by this equation
* @param operator the step's operator
* @param rhs first operand on the rhs
* @return true iff the system changes
* @throws IllegalArgumentException if operator is null
*/
public boolean newStatement(T lhs, UnaryOperator<T> operator, T rhs, boolean toWorkList, boolean eager) {
if (operator == null) {
throw new IllegalArgumentException("operator is null");
}
// add to the list of graph
UnaryStatement<T> s = operator.makeEquation(lhs, rhs);
if (getFixedPointSystem().containsStatement(s)) {
return false;
}
if (lhs != null) {
lhs.setOrderNumber(nextOrderNumber++);
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
return true;
}
protected class Statement extends GeneralStatement<T> {
public Statement(T lhs, AbstractOperator<T> operator, T op1, T op2, T op3) {
super(lhs, operator, op1, op2, op3);
}
public Statement(T lhs, AbstractOperator<T> operator, T op1, T op2) {
super(lhs, operator, op1, op2);
}
public Statement(T lhs, AbstractOperator<T> operator, T[] rhs) {
super(lhs, operator, rhs);
}
public Statement(T lhs, AbstractOperator<T> operator) {
super(lhs, operator);
}
@Override
protected T[] makeRHS(int size) {
return makeStmtRHS(size);
}
}
/**
* Add an equation with two operands on the right-hand side.
*
* @param lhs the lattice variable set by this equation
* @param operator the equation operator
* @param op1 first operand on the rhs
* @param op2 second operand on the rhs
*/
public void newStatement(T lhs, AbstractOperator<T> operator, T op1, T op2, boolean toWorkList, boolean eager) {
// add to the list of graph
GeneralStatement<T> s = new Statement(lhs, operator, op1, op2);
if (getFixedPointSystem().containsStatement(s)) {
return;
}
if (lhs != null) {
lhs.setOrderNumber(nextOrderNumber++);
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
}
/**
* Add a step with three operands on the right-hand side.
*
* @param lhs the lattice variable set by this equation
* @param operator the equation operator
* @param op1 first operand on the rhs
* @param op2 second operand on the rhs
* @param op3 third operand on the rhs
* @throws IllegalArgumentException if lhs is null
*/
public void newStatement(T lhs, AbstractOperator<T> operator, T op1, T op2, T op3, boolean toWorkList, boolean eager) {
if (lhs == null) {
throw new IllegalArgumentException("lhs is null");
}
// add to the list of graph
lhs.setOrderNumber(nextOrderNumber++);
GeneralStatement<T> s = new Statement(lhs, operator, op1, op2, op3);
if (getFixedPointSystem().containsStatement(s)) {
nextOrderNumber--;
return;
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
}
/**
* Add a step to the system with an arbitrary number of operands on the right-hand side.
*
* @param lhs lattice variable set by this equation
* @param operator the operator
* @param rhs the operands on the rhs
*/
public void newStatement(T lhs, AbstractOperator<T> operator, T[] rhs, boolean toWorkList, boolean eager) {
// add to the list of graph
if (lhs != null)
lhs.setOrderNumber(nextOrderNumber++);
GeneralStatement<T> s = new Statement(lhs, operator, rhs);
if (getFixedPointSystem().containsStatement(s)) {
nextOrderNumber--;
return;
}
nCreated++;
getFixedPointSystem().addStatement(s);
incorporateNewStatement(toWorkList, eager, s);
topologicalCounter++;
}
/**
* Initialize all lattice vars in the system.
*/
abstract protected void initializeVariables();
/**
* Initialize the work list for iteration.j
*/
abstract protected void initializeWorkList();
/**
* Update the worklist, assuming that a particular equation has been re-evaluated
*
* @param s the equation that has been re-evaluated.
*/
private void updateWorkList(AbstractStatement<T, ?> s) {
// find each equation which uses this lattice cell, and
// add it to the work list
T v = s.getLHS();
if (v == null) {
return;
}
changedVariable(v);
}
/**
* Number the graph in topological order.
*/
private void orderStatementsInternal() {
if (verbose) {
if (nEvaluated > 0) {
System.err.println("Reorder " + nEvaluated + " " + nCreated);
}
}
reorder();
if (verbose) {
if (nEvaluated > 0) {
System.err.println("Reorder finished " + nEvaluated + " " + nCreated);
}
}
topologicalCounter = 0;
evaluationsAtLastOrdering = nEvaluated;
}
/**
*
*/
public void orderStatements() {
if (nextOrderNumber > minSizeForTopSort) {
if (((double) topologicalCounter / (double) nextOrderNumber) > topologicalGrowthFactor) {
orderStatementsInternal();
return;
}
}
if ((nEvaluated - evaluationsAtLastOrdering) > maxEvalBetweenTopo) {
orderStatementsInternal();
return;
}
}
/**
* Re-order the step definitions.
*/
private void reorder() {
// drain the worklist
LinkedList<AbstractStatement> temp = new LinkedList<AbstractStatement>();
while (!workList.isEmpty()) {
AbstractStatement eq = workList.takeStatement();
temp.add(eq);
}
workList = new Worklist();
// compute new ordering
getFixedPointSystem().reorder();
// re-populate worklist
for (Iterator<AbstractStatement> it = temp.iterator(); it.hasNext();) {
AbstractStatement s = it.next();
workList.insertStatement(s);
}
}
public static boolean isChanged(byte code) {
return (code & CHANGED_MASK) != 0;
}
public static boolean isSideEffect(byte code) {
return (code & SIDE_EFFECT_MASK) != 0;
}
public static boolean isFixed(byte code) {
return (code & FIXED_MASK) != 0;
}
public int getMinSizeForTopSort() {
return minSizeForTopSort;
}
/**
* @param i
*/
public void setMinEquationsForTopSort(int i) {
minSizeForTopSort = i;
}
public int getMaxEvalBetweenTopo() {
return maxEvalBetweenTopo;
}
public double getTopologicalGrowthFactor() {
return topologicalGrowthFactor;
}
/**
* @param i
*/
public void setMaxEvalBetweenTopo(int i) {
maxEvalBetweenTopo = i;
}
/**
* @param d
*/
public void setTopologicalGrowthFactor(double d) {
topologicalGrowthFactor = d;
}
public int getNumberOfEvaluations() {
return nEvaluated;
}
public void incNumberOfEvaluations() {
nEvaluated++;
}
/**
* a method that will be called every N evaluations. subclasses should override as desired.
*/
protected void periodicMaintenance() {
}
/**
* subclasses should override as desired.
*/
protected int getVerboseInterval() {
return DEFAULT_VERBOSE_INTERVAL;
}
/**
* subclasses should override as desired.
*/
protected int getPeriodicMaintainInterval() {
return DEFAULT_PERIODIC_MAINTENANCE_INTERVAL;
}
}