com.ibm.wala.dataflow.IFDS.TabulationSolver Maven / Gradle / Ivy
/*
* 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.dataflow.IFDS;
import com.ibm.wala.cfg.IBasicBlock;
import com.ibm.wala.core.util.CancelRuntimeException;
import com.ibm.wala.core.util.ref.ReferenceCleanser;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Heap;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.collections.ToStringComparator;
import com.ibm.wala.util.heapTrace.HeapTracer;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.IntSetAction;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.intset.MutableSparseIntSet;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A precise interprocedural tabulation solver.
*
* See Reps, Horwitz, Sagiv POPL 95.
*
*
This version differs in some ways from the POPL algorithm. In particular ...
*
*
* - to support exceptional control flow ... there may be several return sites for each call
* site.
*
- it supports an optional merge operator, useful for non-IFDS problems and widening.
*
- it stores summary edges at each callee instead of at each call site.
*
*
*
*
* @param type of node in the supergraph
* @param type of a procedure (like a box in an RSM)
* @param type of factoids propagated when solving this problem
*/
public class TabulationSolver {
/**
* DEBUG_LEVEL:
*
*
* - 0 No output
*
- 1 Print some simple stats and warning information
*
- 2 Detailed debugging
*
- 3 Also print worklists
*
*/
protected static final int DEBUG_LEVEL = 0;
protected static final boolean verbose =
true
&& ("true".equals(System.getProperty("com.ibm.wala.fixedpoint.impl.verbose"))
? true
: false);
static final int VERBOSE_INTERVAL = 1000;
static final boolean VERBOSE_TRACE_MEMORY = false;
private static int verboseCounter = 0;
/** Should we periodically clear out soft reference caches in an attempt to help the GC? */
protected static final boolean PERIODIC_WIPE_SOFT_CACHES = true;
/** Interval which defines the period to clear soft reference caches */
private static final int WIPE_SOFT_CACHE_INTERVAL = 1000000;
/** Counter for wiping soft caches */
private static int wipeCount = WIPE_SOFT_CACHE_INTERVAL;
/** The supergraph which induces this dataflow problem */
protected final ISupergraph supergraph;
/** A map from an edge in a supergraph to a flow function */
protected final IFlowFunctionMap flowFunctionMap;
/** The problem being solved. */
private final TabulationProblem problem;
/**
* A map from Object (entry node in supergraph) -> LocalPathEdges.
*
* Logically, this represents a set of edges (s_p,d_i) -> (n, d_j). The data structure is
* chosen to attempt to save space over representing each edge explicitly.
*/
private final Map pathEdges = HashMapFactory.make();
/**
* A map from Object (entry node in supergraph) -> CallFlowEdges.
*
* Logically, this represents a set of edges (c,d_i) -> (s_p, d_j). The data structure is
* chosen to attempt to save space over representing each edge explicitly.
*/
private final Map callFlowEdges = HashMapFactory.make();
/** A map from Object (procedure) -> LocalSummaryEdges. */
protected final Map summaryEdges = HashMapFactory.make();
/**
* the set of all {@link PathEdge}s that were used as seeds during the tabulation, grouped by
* procedure.
*/
private final Map
>> seeds = HashMapFactory.make();
/** All seeds, stored redundantly for quick access. */
private final Set> allSeeds = HashSetFactory.make();
/** The worklist */
private ITabulationWorklist worklist;
/** A progress monitor. can be null. */
protected final IProgressMonitor progressMonitor;
/**
* the path edge currently being processed in the main loop of {@link #forwardTabulateSLRPs()};
* {@code null} if {@link #forwardTabulateSLRPs()} is not currently running. Note that if we are
* applying a summary edge in {@link #processExit(PathEdge)}, curPathEdge is modified to be the
* path edge terminating at the call node in the caller, to match the behavior in {@link
* #processCall(PathEdge)}.
*/
private PathEdge curPathEdge;
/**
* the summary edge currently being applied in {@link #processCall(PathEdge)} or {@link
* #processExit(PathEdge)}, or {@code null} if summary edges are not currently being processed.
*/
private PathEdge curSummaryEdge;
/**
* @param p a description of the dataflow problem to solve
* @throws IllegalArgumentException if p is null
*/
protected TabulationSolver(TabulationProblem p, IProgressMonitor monitor) {
if (p == null) {
throw new IllegalArgumentException("p is null");
}
this.supergraph = p.getSupergraph();
this.flowFunctionMap = p.getFunctionMap();
this.problem = p;
this.progressMonitor = monitor;
}
/** Subclasses can override this to plug in a different worklist implementation. */
protected ITabulationWorklist makeWorklist() {
return new Worklist();
}
/**
* @param p a description of the dataflow problem to solve
* @throws IllegalArgumentException if p is null
*/
public static TabulationSolver make(TabulationProblem p) {
return new TabulationSolver<>(p, null);
}
/**
* Solve the dataflow problem.
*
* @return a representation of the result
*/
public TabulationResult solve() throws CancelException {
try {
initialize();
forwardTabulateSLRPs();
Result r = new Result();
return r;
} catch (CancelException | CancelRuntimeException e) {
// store a partially-tabulated result in the thrown exception.
Result r = new Result();
throw new TabulationCancelException(e, r);
}
}
/** Start tabulation with the initial seeds. */
protected void initialize() {
for (PathEdge seed : problem.initialSeeds()) {
addSeed(seed);
}
}
/** Restart tabulation from a particular path edge. Use with care. */
public void addSeed(PathEdge seed) {
Set> s = MapUtil.findOrCreateSet(seeds, supergraph.getProcOf(seed.entry));
s.add(seed);
allSeeds.add(seed);
propagate(seed.entry, seed.d1, seed.target, seed.d2);
}
/** See POPL 95 paper for this algorithm, Figure 3 */
@SuppressWarnings("unused")
private void forwardTabulateSLRPs() throws CancelException {
assert curPathEdge == null : "curPathEdge should not be non-null here";
if (worklist == null) {
worklist = makeWorklist();
}
while (worklist.size() > 0) {
MonitorUtil.throwExceptionIfCanceled(progressMonitor);
if (verbose) {
performVerboseAction();
}
if (PERIODIC_WIPE_SOFT_CACHES) {
tendToSoftCaches();
}
final PathEdge edge = popFromWorkList();
if (DEBUG_LEVEL > 0) {
System.err.println("TABULATE " + edge);
}
curPathEdge = edge;
int j = merge(edge.entry, edge.d1, edge.target, edge.d2);
if (j == -1 && DEBUG_LEVEL > 0) {
System.err.println("merge -1: DROPPING");
}
if (j != -1) {
if (j != edge.d2) {
// this means that we don't want to push the edge. instead,
// we'll push the merged fact. a little tricky, but i think should
// work.
if (DEBUG_LEVEL > 0) {
System.err.println("propagating merged fact " + j);
}
propagate(edge.entry, edge.d1, edge.target, j);
} else {
if (supergraph.isCall(edge.target)) {
// [13]
processCall(edge);
} else if (supergraph.isExit(edge.target)) {
// [21]
processExit(edge);
} else {
// [33]
processNormal(edge);
}
}
}
}
curPathEdge = null;
}
/**
* For some reason (either a bug in our code that defeats soft references, or a bad policy in the
* GC), leaving soft reference caches to clear themselves out doesn't work. Help it out.
*
* It's unfortunate that this method exits.
*/
protected void tendToSoftCaches() {
wipeCount++;
if (wipeCount > WIPE_SOFT_CACHE_INTERVAL) {
wipeCount = 0;
ReferenceCleanser.clearSoftCaches();
}
}
/** */
protected final void performVerboseAction() {
verboseCounter++;
if (verboseCounter % VERBOSE_INTERVAL == 0) {
System.err.println("Tabulation Solver " + verboseCounter);
System.err.println(" " + peekFromWorkList());
if (VERBOSE_TRACE_MEMORY) {
ReferenceCleanser.clearSoftCaches();
System.err.println("Analyze leaks..");
HeapTracer.traceHeap(Collections.singleton(this), true);
System.err.println("done analyzing leaks");
}
}
}
/** Handle lines [33-37] of the algorithm */
@SuppressWarnings("unused")
private void processNormal(final PathEdge edge) {
if (DEBUG_LEVEL > 0) {
System.err.println("process normal: " + edge);
}
for (T m : Iterator2Iterable.make(supergraph.getSuccNodes(edge.target))) {
if (DEBUG_LEVEL > 0) {
System.err.println("normal successor: " + m);
}
IUnaryFlowFunction f = flowFunctionMap.getNormalFlowFunction(edge.target, m);
IntSet D3 = computeFlow(edge.d2, f);
if (DEBUG_LEVEL > 0) {
System.err.println(" reached: " + D3);
}
if (D3 != null) {
D3.foreach(
d3 -> {
newNormalExplodedEdge(edge, m, d3);
propagate(edge.entry, edge.d1, m, d3);
});
}
}
}
/**
* Handle lines [21 - 32] of the algorithm, propagating information from an exit node.
*
* Note that we've changed the way we record summary edges. Summary edges are now associated
* with a callee (s_p,exit), where the original algorithm used a call, return pair in the caller.
*/
@SuppressWarnings("unused")
protected void processExit(final PathEdge edge) {
if (DEBUG_LEVEL > 0) {
System.err.println("process exit: " + edge);
}
final LocalSummaryEdges summaries =
findOrCreateLocalSummaryEdges(supergraph.getProcOf(edge.target));
int s_p_n = supergraph.getLocalBlockNumber(edge.entry);
int x = supergraph.getLocalBlockNumber(edge.target);
if (!summaries.contains(s_p_n, x, edge.d1, edge.d2)) {
summaries.insertSummaryEdge(s_p_n, x, edge.d1, edge.d2);
}
assert curSummaryEdge == null : "curSummaryEdge should be null here";
curSummaryEdge = edge;
final CallFlowEdges callFlow = findOrCreateCallFlowEdges(edge.entry);
// [22] for each c /in callers(p)
IntSet callFlowSourceNodes = callFlow.getCallFlowSourceNodes(edge.d1);
if (callFlowSourceNodes != null) {
for (IntIterator it = callFlowSourceNodes.intIterator(); it.hasNext(); ) {
// [23] for each d4 s.t. -> occurred earlier
int globalC = it.next();
final IntSet D4 = callFlow.getCallFlowSources(globalC, edge.d1);
// [23] for each d5 s.t. -> ...
propagateToReturnSites(edge, supergraph.getNode(globalC), D4);
}
}
curSummaryEdge = null;
}
/**
* Propagate information for an "exit" edge to the appropriate return sites
*
* [23] for each d5 s.t. {@literal -> } ..
*
* @param edge the edge being processed
* @param c a call site of edge.s_p
* @param D4 set of d1 s.t. {@literal -> } was recorded as call flow
*/
@SuppressWarnings("unused")
private void propagateToReturnSites(final PathEdge edge, final T c, final IntSet D4) {
P proc = supergraph.getProcOf(c);
final T[] entries = supergraph.getEntriesForProcedure(proc);
// we iterate over each potential return site;
// we might have multiple return sites due to exceptions
// note that we might have different summary edges for each
// potential return site, and different flow functions from this
// exit block to each return site.
for (T retSite :
Iterator2Iterable.make(supergraph.getReturnSites(c, supergraph.getProcOf(edge.target)))) {
if (DEBUG_LEVEL > 1) {
System.err.println(
"candidate return site: " + retSite + ' ' + supergraph.getNumber(retSite));
}
// note: since we might have multiple exit nodes for the callee, (to handle exceptional
// returns)
// not every return site might be valid for this exit node (edge.n).
// so, we'll filter the logic by checking that we only process reachable return sites.
// the supergraph carries the information regarding the legal successors
// of the exit node
if (!supergraph.hasEdge(edge.target, retSite)) {
continue;
}
if (DEBUG_LEVEL > 1) {
System.err.println("feasible return site: " + retSite);
}
final IFlowFunction retf = flowFunctionMap.getReturnFlowFunction(c, edge.target, retSite);
if (retf instanceof IBinaryReturnFlowFunction) {
propagateToReturnSiteWithBinaryFlowFunction(edge, c, D4, entries, retSite, retf);
} else {
final IntSet D5 = computeFlow(edge.d2, (IUnaryFlowFunction) retf);
if (DEBUG_LEVEL > 1) {
System.err.println("D4" + D4);
System.err.println("D5 " + D5);
}
IntSetAction action = d4 -> propToReturnSite(c, entries, retSite, d4, D5, edge);
D4.foreach(action);
}
}
}
/**
* Propagate information for an "exit" edge to a caller return site
*
* [23] for each d5 s.t. {@literal -> } ..
*
* @param edge the edge being processed
* @param c a call site of edge.s_p
* @param D4 set of d1 s.t. {@literal -> } was recorded as call flow
* @param entries the blocks in the supergraph that are entries for the procedure of c
* @param retSite the return site being propagated to
* @param retf the flow function
*/
private void propagateToReturnSiteWithBinaryFlowFunction(
final PathEdge edge,
final T c,
final IntSet D4,
final T[] entries,
final T retSite,
final IFlowFunction retf) {
D4.foreach(
d4 -> {
final IntSet D5 = computeBinaryFlow(d4, edge.d2, (IBinaryReturnFlowFunction) retf);
propToReturnSite(c, entries, retSite, d4, D5, edge);
});
}
/**
* Propagate information to a particular return site.
*
* @param c the corresponding call site
* @param entries entry nodes in the caller
* @param retSite the return site
* @param d4 a fact s.t. {@literal -> } was recorded as call flow and
* {@literal } is the source of the summary edge being applied
* @param D5 facts to propagate to return site
* @param edge the path edge ending at the exit site of the callee
*/
@SuppressWarnings("unused")
private void propToReturnSite(
final T c,
final T[] entries,
final T retSite,
final int d4,
final IntSet D5,
final PathEdge edge) {
if (D5 != null) {
D5.foreach(
d5 -> {
// [26 - 28]
// note that we've modified the algorithm here to account
// for potential
// multiple entry nodes. Instead of propagating the new
// summary edge
// with respect to one s_profOf(c), we have to propagate
// for each
// potential entry node s_p /in s_procof(c)
for (final T s_p : entries) {
if (DEBUG_LEVEL > 1) {
System.err.println(" do entry " + s_p);
}
IntSet D3 = getInversePathEdges(s_p, c, d4);
if (DEBUG_LEVEL > 1) {
System.err.println("D3" + D3);
}
if (D3 != null) {
D3.foreach(
d3 -> {
// set curPathEdge to be consistent with its setting in processCall() when
// applying a summary edge
curPathEdge = PathEdge.createPathEdge(s_p, d3, c, d4);
newSummaryEdge(curPathEdge, edge, retSite, d5);
propagate(s_p, d3, retSite, d5);
});
}
}
});
}
}
/**
* @param d2 note that s_p must be an entry for procof(n)
* @return set of d1 s.t. {@literal -> } is a path edge, or null if none found
*/
protected IntSet getInversePathEdges(T s_p, T n, int d2) {
int number = supergraph.getLocalBlockNumber(n);
LocalPathEdges lp = pathEdges.get(s_p);
if (lp == null) {
return null;
}
return lp.getInverse(number, d2);
}
/**
* Handle lines [14 - 19] of the algorithm, propagating information into and across a call site.
*/
@SuppressWarnings("unused")
protected void processCall(final PathEdge edge) {
if (DEBUG_LEVEL > 0) {
System.err.println("process call: " + edge);
}
// c:= number of the call node
final int c = supergraph.getNumber(edge.target);
Collection allReturnSites = HashSetFactory.make();
// populate allReturnSites with return sites for missing calls.
for (T retSite : Iterator2Iterable.make(supergraph.getReturnSites(edge.target, null))) {
allReturnSites.add(retSite);
}
// [14 - 16]
boolean hasCallee = false;
for (T callee : Iterator2Iterable.make(supergraph.getCalledNodes(edge.target))) {
hasCallee = true;
processParticularCallee(edge, c, allReturnSites, callee);
}
// special logic: in backwards problems, a "call" node can have
// "normal" successors as well. deal with these.
for (T m : Iterator2Iterable.make(supergraph.getNormalSuccessors(edge.target))) {
if (DEBUG_LEVEL > 0) {
System.err.println("normal successor: " + m);
}
IUnaryFlowFunction f = flowFunctionMap.getNormalFlowFunction(edge.target, m);
IntSet D3 = computeFlow(edge.d2, f);
if (DEBUG_LEVEL > 0) {
System.err.println("normal successor reached: " + D3);
}
if (D3 != null) {
D3.foreach(
d3 -> {
newNormalExplodedEdge(edge, m, d3);
propagate(edge.entry, edge.d1, m, d3);
});
}
}
// [17 - 19]
// we modify this to handle each return site individually
for (final T returnSite : allReturnSites) {
if (DEBUG_LEVEL > 0) {
System.err.println(" process return site: " + returnSite);
}
final IUnaryFlowFunction f;
if (hasCallee) {
f = flowFunctionMap.getCallToReturnFlowFunction(edge.target, returnSite);
} else {
f = flowFunctionMap.getCallNoneToReturnFlowFunction(edge.target, returnSite);
}
IntSet reached = computeFlow(edge.d2, f);
if (DEBUG_LEVEL > 0) {
System.err.println("reached: " + reached);
}
if (reached != null) {
reached.foreach(
x -> {
assert x >= 0;
assert edge.d1 >= 0;
newNormalExplodedEdge(edge, returnSite, x);
propagate(edge.entry, edge.d1, returnSite, x);
});
}
}
}
/**
* handle a particular callee for some call node.
*
* @param edge the path edge being processed
* @param callNodeNum the number of the call node in the supergraph
* @param allReturnSites a set collecting return sites for the call. This set is mutated with the
* return sites for this callee.
* @param calleeEntry the entry node of the callee in question
*/
@SuppressWarnings("unused")
protected void processParticularCallee(
final PathEdge edge,
final int callNodeNum,
Collection allReturnSites,
final T calleeEntry) {
if (DEBUG_LEVEL > 0) {
System.err.println(" process callee: " + calleeEntry);
}
// reached := {d1} that reach the callee
MutableSparseIntSet reached = MutableSparseIntSet.makeEmpty();
final Collection returnSitesForCallee =
Iterator2Collection.toSet(
supergraph.getReturnSites(edge.target, supergraph.getProcOf(calleeEntry)));
allReturnSites.addAll(returnSitesForCallee);
// we modify this to handle each return site individually. Some types of problems
// compute different flow functions for each return site.
for (final T returnSite : returnSitesForCallee) {
IUnaryFlowFunction f =
flowFunctionMap.getCallFlowFunction(edge.target, calleeEntry, returnSite);
IntSet r = computeFlow(edge.d2, f);
if (r != null) {
reached.addAll(r);
}
}
// in some problems, we also want to consider flow into a callee that can never flow out
// via a return. in this case, the return site is null.
IUnaryFlowFunction f = flowFunctionMap.getCallFlowFunction(edge.target, calleeEntry, null);
IntSet r = computeFlow(edge.d2, f);
if (r != null) {
reached.addAll(r);
}
if (DEBUG_LEVEL > 0) {
System.err.println(" reached: " + reached);
}
if (reached != null) {
final LocalSummaryEdges summaries = summaryEdges.get(supergraph.getProcOf(calleeEntry));
final CallFlowEdges callFlow = findOrCreateCallFlowEdges(calleeEntry);
final int s_p_num = supergraph.getLocalBlockNumber(calleeEntry);
reached.foreach(
d1 -> {
// we get reuse if we _don't_ propagate a new fact to the callee entry
final boolean gotReuse = !propagate(calleeEntry, d1, calleeEntry, d1);
recordCall(edge.target, calleeEntry, d1, gotReuse);
newCallExplodedEdge(edge, calleeEntry, d1);
// cache the fact that we've flowed -> by a
// call flow
callFlow.addCallEdge(callNodeNum, edge.d2, d1);
// handle summary edges now as well. this is different from the PoPL
// 95 paper.
if (summaries != null) {
// for each exit from the callee
P p = supergraph.getProcOf(calleeEntry);
T[] exits = supergraph.getExitsForProcedure(p);
for (final T exit : exits) {
if (DEBUG_LEVEL > 0) {
assert supergraph.containsNode(exit);
}
int x_num = supergraph.getLocalBlockNumber(exit);
// reachedBySummary := {d2} s.t. ->
// was recorded as a summary edge
IntSet reachedBySummary = summaries.getSummaryEdges(s_p_num, x_num, d1);
if (reachedBySummary != null) {
for (final T returnSite : returnSitesForCallee) {
// if "exit" is a valid exit from the callee to the return
// site being processed
if (supergraph.hasEdge(exit, returnSite)) {
final IFlowFunction retf =
flowFunctionMap.getReturnFlowFunction(edge.target, exit, returnSite);
reachedBySummary.foreach(
d2 -> {
assert curSummaryEdge == null : "curSummaryEdge should be null here";
curSummaryEdge = PathEdge.createPathEdge(calleeEntry, d1, exit, d2);
if (retf instanceof IBinaryReturnFlowFunction) {
final IntSet D51 =
computeBinaryFlow(edge.d2, d2, (IBinaryReturnFlowFunction) retf);
if (D51 != null) {
D51.foreach(
d5 -> {
newSummaryEdge(edge, curSummaryEdge, returnSite, d5);
propagate(edge.entry, edge.d1, returnSite, d5);
});
}
} else {
final IntSet D52 = computeFlow(d2, (IUnaryFlowFunction) retf);
if (D52 != null) {
D52.foreach(
d5 -> {
newSummaryEdge(edge, curSummaryEdge, returnSite, d5);
propagate(edge.entry, edge.d1, returnSite, d5);
});
}
}
curSummaryEdge = null;
});
}
}
}
}
}
});
}
}
/**
* invoked when a callee is processed with a particular entry fact
*
* @param d1 the entry fact
* @param gotReuse whether existing summary edges were applied
*/
@SuppressWarnings("unused")
protected void recordCall(T callNode, T callee, int d1, boolean gotReuse) {}
/**
* @return f(call_d, exit_d);
*/
@SuppressWarnings("unused")
protected IntSet computeBinaryFlow(int call_d, int exit_d, IBinaryReturnFlowFunction f) {
if (DEBUG_LEVEL > 0) {
System.err.println("got binary flow function " + f);
}
IntSet result = f.getTargets(call_d, exit_d);
return result;
}
/**
* @return f(d1)
*/
@SuppressWarnings("unused")
protected IntSet computeFlow(int d1, IUnaryFlowFunction f) {
if (DEBUG_LEVEL > 0) {
System.err.println("got flow function " + f);
}
IntSet result = f.getTargets(d1);
if (result == null) {
return null;
} else {
return result;
}
}
/**
* @return f^{-1}(d2)
*/
protected IntSet computeInverseFlow(int d2, IReversibleFlowFunction f) {
return f.getSources(d2);
}
protected PathEdge popFromWorkList() {
assert worklist != null;
return worklist.take();
}
private PathEdge peekFromWorkList() {
// horrible. don't use in performance-critical
assert worklist != null;
PathEdge result = worklist.take();
worklist.insert(result);
return result;
}
/**
* Propagate the fact <s_p,i> -> <n, j> has arisen as a path edge. Returns
* <code>true</code> iff the path edge was not previously observed.
*
* @param s_p entry block
* @param i dataflow fact on entry
* @param n reached block
* @param j dataflow fact reached
*/
@SuppressWarnings("unused")
protected boolean propagate(T s_p, int i, T n, int j) {
int number = supergraph.getLocalBlockNumber(n);
if (number < 0) {
System.err.println("BOOM " + n);
supergraph.getLocalBlockNumber(n);
}
assert number >= 0;
LocalPathEdges pLocal = findOrCreateLocalPathEdges(s_p);
assert j >= 0;
if (!pLocal.contains(i, number, j)) {
if (DEBUG_LEVEL > 0) {
System.err.println("propagate " + s_p + " " + i + ' ' + number + ' ' + j);
}
pLocal.addPathEdge(i, number, j);
addToWorkList(s_p, i, n, j);
return true;
}
return false;
}
public LocalPathEdges getLocalPathEdges(T s_p) {
return pathEdges.get(s_p);
}
/**
* Merging: suppose we're doing propagate <s_p,i> -> <n,j> but we already have path
* edges <s_p,i> -> <n, x>, <s_p,i> -> <n,y>, and <s_p,i>
* -><n, z>.
*
* let \alpha be the merge function. then instead of <s_p,i> -> <n,j>, we
* propagate <s_p,i> -> <n, \alpha(j,x,y,z) > !!!
*
*
return -1 if no fact should be propagated
*/
private int merge(T s_p, int i, T n, int j) {
assert j >= 0;
IMergeFunction alpha = problem.getMergeFunction();
if (alpha != null) {
LocalPathEdges lp = pathEdges.get(s_p);
IntSet preExistFacts = lp.getReachable(supergraph.getLocalBlockNumber(n), i);
if (preExistFacts == null) {
return j;
} else {
int size = preExistFacts.size();
if ((size == 0) || ((size == 1) && preExistFacts.contains(j))) {
return j;
} else {
int result = alpha.merge(preExistFacts, j);
return result;
}
}
} else {
return j;
}
}
@SuppressWarnings("unused")
protected void addToWorkList(T s_p, int i, T n, int j) {
if (worklist == null) {
worklist = makeWorklist();
}
worklist.insert(PathEdge.createPathEdge(s_p, i, n, j));
if (DEBUG_LEVEL >= 3) {
System.err.println("WORKLIST: " + worklist);
}
}
protected LocalPathEdges findOrCreateLocalPathEdges(T s_p) {
LocalPathEdges result = pathEdges.get(s_p);
if (result == null) {
result = makeLocalPathEdges();
pathEdges.put(s_p, result);
}
return result;
}
private LocalPathEdges makeLocalPathEdges() {
return problem.getMergeFunction() == null
? new LocalPathEdges(false)
: new LocalPathEdges(true);
}
protected LocalSummaryEdges findOrCreateLocalSummaryEdges(P proc) {
LocalSummaryEdges result = summaryEdges.get(proc);
if (result == null) {
result = new LocalSummaryEdges();
summaryEdges.put(proc, result);
}
return result;
}
protected CallFlowEdges findOrCreateCallFlowEdges(T s_p) {
CallFlowEdges result = callFlowEdges.get(s_p);
if (result == null) {
result = new CallFlowEdges();
callFlowEdges.put(s_p, result);
}
return result;
}
/**
* get the bitvector of facts that hold at the entry to a given node
*
* @return IntSet representing the bitvector
*/
public IntSet getResult(T node) {
P proc = supergraph.getProcOf(node);
int n = supergraph.getLocalBlockNumber(node);
T[] entries = supergraph.getEntriesForProcedure(proc);
MutableIntSet result = MutableSparseIntSet.makeEmpty();
Set allEntries = HashSetFactory.make(Arrays.asList(entries));
Set> pSeeds = seeds.get(proc);
if (pSeeds != null) {
for (PathEdge seed : pSeeds) {
allEntries.add(seed.entry);
}
}
for (T entry : allEntries) {
LocalPathEdges lp = pathEdges.get(entry);
if (lp != null) {
result.addAll(lp.getReachable(n));
}
}
return result;
}
public class Result implements TabulationResult {
/**
* get the bitvector of facts that hold at the entry to a given node
*
* @return IntSet representing the bitvector
*/
@Override
public IntSet getResult(T node) {
return TabulationSolver.this.getResult(node);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
TreeMap