added documentation

git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@3824 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
msridhar1 2010-04-28 20:00:20 +00:00
parent 2e724aac3e
commit e25b29ed48
3 changed files with 158 additions and 41 deletions

View File

@ -45,28 +45,37 @@ import com.ibm.wala.util.intset.OrdinalSetMapping;
public class ContextInsensitiveReachingDefs {
/**
* the exploded control-flow graph on which to compute the analysis
* the exploded interprocedural control-flow graph on which to compute the analysis
*/
private final ExplodedInterproceduralCFG icfg;
/**
* maps method and instruction index of putstatic to more compact numbering for bitvectors
* maps call graph node and instruction index of putstatic instructions to more compact numbering for bitvectors
*/
private final OrdinalSetMapping<Pair<CGNode, Integer>> putInstrNumbering;
/**
* for resolving field references in putstatic instructions
*/
private final IClassHierarchy cha;
/**
* maps each static field to the numbers of the statements that define it
* maps each static field to the numbers of the statements (in {@link #putInstrNumbering}) that define it; used for kills in flow
* functions
*/
private final Map<IField, BitVector> staticField2DefStatements = HashMapFactory.make();
private static final boolean VERBOSE = true;
public ContextInsensitiveReachingDefs(ExplodedInterproceduralCFG icfg, IClassHierarchy cha) {
this.icfg = icfg;
this.cha = cha;
this.putInstrNumbering = numberPutStatics();
}
/**
* generate a numbering of the putstatic instructions
*/
@SuppressWarnings("unchecked")
private OrdinalSetMapping<Pair<CGNode, Integer>> numberPutStatics() {
ArrayList<Pair<CGNode, Integer>> putInstrs = new ArrayList<Pair<CGNode, Integer>>();
@ -80,8 +89,10 @@ public class ContextInsensitiveReachingDefs {
SSAInstruction instruction = instructions[i];
if (instruction instanceof SSAPutInstruction && ((SSAPutInstruction) instruction).isStatic()) {
SSAPutInstruction putInstr = (SSAPutInstruction) instruction;
// instrNum is the number that will be assigned to this putstatic
int instrNum = putInstrs.size();
putInstrs.add(Pair.make(node, i));
// also update the mapping of static fields to def'ing statements
IField field = cha.resolveField(putInstr.getDeclaredField());
assert field != null;
BitVector bv = staticField2DefStatements.get(field);
@ -98,8 +109,10 @@ public class ContextInsensitiveReachingDefs {
private class TransferFunctions implements ITransferFunctionProvider<BasicBlockInContext<IExplodedBasicBlock>, BitVectorVariable> {
/**
* our meet operator is set union
*/
public AbstractMeetOperator<BitVectorVariable> getMeetOperator() {
// meet is union
return BitVectorUnion.instance();
}
@ -109,7 +122,7 @@ public class ContextInsensitiveReachingDefs {
int instructionIndex = ebb.getFirstInstructionIndex();
CGNode cgNode = node.getNode();
if (instruction instanceof SSAPutInstruction && ((SSAPutInstruction) instruction).isStatic()) {
// kill all defs of the same static field
// kill all defs of the same static field, and gen this instruction
final SSAPutInstruction putInstr = (SSAPutInstruction) instruction;
final IField field = cha.resolveField(putInstr.getDeclaredField());
assert field != null;
@ -118,11 +131,15 @@ public class ContextInsensitiveReachingDefs {
gen.set(putInstrNumbering.getMappedIndex(Pair.make(cgNode, instructionIndex)));
return new BitVectorKillGen(kill, gen);
} else {
// nothing defined
// identity function for non-putstatic instructions
return BitVectorIdentity.instance();
}
}
/**
* here we need an edge transfer function for call-to-return edges (see
* {@link #getEdgeTransferFunction(BasicBlockInContext, BasicBlockInContext)})
*/
public boolean hasEdgeTransferFunctions() {
return true;
}
@ -131,9 +148,13 @@ public class ContextInsensitiveReachingDefs {
return true;
}
/**
* for direct call-to-return edges at a call site, the edge transfer function will kill all facts, since we only want to
* consider facts that arise from going through the callee
*/
public UnaryOperator<BitVectorVariable> getEdgeTransferFunction(BasicBlockInContext<IExplodedBasicBlock> src,
BasicBlockInContext<IExplodedBasicBlock> dst) {
if (isCallToReturnEdge(src,dst)) {
if (isCallToReturnEdge(src, dst)) {
return BitVectorKillAll.instance();
} else {
return BitVectorIdentity.instance();
@ -147,7 +168,13 @@ public class ContextInsensitiveReachingDefs {
}
/**
* run the analysis
*
* @return the solver used for the analysis, which contains the analysis result
*/
public BitVectorSolver<BasicBlockInContext<IExplodedBasicBlock>> analyze() {
// the framework describes the dataflow problem, in particular the underlying graph and the transfer functions
BitVectorFramework<BasicBlockInContext<IExplodedBasicBlock>, Pair<CGNode, Integer>> framework = new BitVectorFramework<BasicBlockInContext<IExplodedBasicBlock>, Pair<CGNode, Integer>>(
icfg, new TransferFunctions(), putInstrNumbering);
BitVectorSolver<BasicBlockInContext<IExplodedBasicBlock>> solver = new BitVectorSolver<BasicBlockInContext<IExplodedBasicBlock>>(
@ -155,18 +182,23 @@ public class ContextInsensitiveReachingDefs {
try {
solver.solve(null);
} catch (CancelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// this shouldn't happen
assert false;
}
for (BasicBlockInContext<IExplodedBasicBlock> ebb : icfg) {
System.out.println(ebb);
System.out.println(ebb.getDelegate().getInstruction());
System.out.println(solver.getIn(ebb));
System.out.println(solver.getOut(ebb));
if (VERBOSE) {
for (BasicBlockInContext<IExplodedBasicBlock> ebb : icfg) {
System.out.println(ebb);
System.out.println(ebb.getDelegate().getInstruction());
System.out.println(solver.getIn(ebb));
System.out.println(solver.getOut(ebb));
}
}
return solver;
}
/**
* gets putstatic instruction corresponding to some fact number from a bitvector in the analysis result
*/
public Pair<CGNode, Integer> getNodeAndInstrForNumber(int num) {
return putInstrNumbering.getMappedObject(num);
}

View File

@ -48,17 +48,30 @@ import com.ibm.wala.util.intset.MutableSparseIntSet;
*/
public class ContextSensitiveReachingDefs {
/**
* used for resolving field references in putstatic instructions
*/
private final IClassHierarchy cha;
private final ISupergraph<BasicBlockInContext<IExplodedBasicBlock>, CGNode> supergraph;
/**
* the supergraph over which tabulation is performed
*/
private final ISupergraph<BasicBlockInContext<IExplodedBasicBlock>, CGNode> supergraph;
private ReachingDefsDomain domain = new ReachingDefsDomain();
/**
* the tabulation domain
*/
private final ReachingDefsDomain domain = new ReachingDefsDomain();
public ContextSensitiveReachingDefs(CallGraph cg, AnalysisCache cache) {
this.cha = cg.getClassHierarchy();
// we use an ICFGSupergraph, which basically adapts ExplodedInterproceduralCFG to the ISupergraph interface
this.supergraph = ICFGSupergraph.make(cg, cache);
}
/**
* controls numbering of putstatic instructions for use in tabulation
*/
private class ReachingDefsDomain extends MutableMapping<Pair<CGNode, Integer>> implements
TabulationDomain<Pair<CGNode, Integer>, BasicBlockInContext<IExplodedBasicBlock>> {
@ -78,29 +91,45 @@ public class ContextSensitiveReachingDefs {
this.domain = domain;
}
/**
* the flow function for flow from a callee to caller where there was no flow from caller to callee; just the identity function
*
* @see ReachingDefsProblem
*/
public IFlowFunction getUnbalancedReturnFlowFunction(BasicBlockInContext<IExplodedBasicBlock> src,
BasicBlockInContext<IExplodedBasicBlock> dest) {
return IdentityFlowFunction.identity();
}
/**
* flow function from caller to callee; just the identity function
*/
public IUnaryFlowFunction getCallFlowFunction(BasicBlockInContext<IExplodedBasicBlock> src,
BasicBlockInContext<IExplodedBasicBlock> dest, BasicBlockInContext<IExplodedBasicBlock> ret) {
// just send the fact into the callee
return IdentityFlowFunction.identity();
}
/**
* flow function from call node to return node when there are no targets for the call site; not a case we are expecting
*/
public IUnaryFlowFunction getCallNoneToReturnFlowFunction(BasicBlockInContext<IExplodedBasicBlock> src,
BasicBlockInContext<IExplodedBasicBlock> dest) {
// if we're missing callees, just give up and kill everything
return KillEverything.singleton();
}
/**
* flow function from call node to return node at a call site when callees exist. We kill everything; surviving facts should
* flow out of the callee
*/
public IUnaryFlowFunction getCallToReturnFlowFunction(BasicBlockInContext<IExplodedBasicBlock> src,
BasicBlockInContext<IExplodedBasicBlock> dest) {
// kill everything; surviving facts should flow out of the callee
return KillEverything.singleton();
}
/**
* flow function for normal intraprocedural edges
*/
public IUnaryFlowFunction getNormalFlowFunction(final BasicBlockInContext<IExplodedBasicBlock> src,
BasicBlockInContext<IExplodedBasicBlock> dest) {
final IExplodedBasicBlock ebb = src.getDelegate();
@ -111,15 +140,16 @@ public class ContextSensitiveReachingDefs {
return new IUnaryFlowFunction() {
public IntSet getTargets(int d1) {
// first, gen this statement
int factNum = domain.getMappedIndex(Pair.make(src.getNode(), ebb.getFirstInstructionIndex()));
IField staticField = cha.resolveField(putInstr.getDeclaredField());
assert staticField != null;
assert factNum != -1;
MutableSparseIntSet result = MutableSparseIntSet.makeEmpty();
result.add(factNum);
// if incoming statement is some different statement that defs the same static field, kill it; otherwise, keep it
if (d1 != factNum) {
IField staticField = cha.resolveField(putInstr.getDeclaredField());
assert staticField != null;
Pair<CGNode, Integer> otherPutInstrAndNode = domain.getMappedObject(d1);
// if it writes the same field, kill it
SSAPutInstruction otherPutInstr = (SSAPutInstruction) otherPutInstrAndNode.fst.getIR().getInstructions()[otherPutInstrAndNode.snd];
IField otherStaticField = cha.resolveField(otherPutInstr.getDeclaredField());
if (!staticField.equals(otherStaticField)) {
@ -135,9 +165,13 @@ public class ContextSensitiveReachingDefs {
};
}
}
// identity function when src block isn't for a putstatic
return IdentityFlowFunction.identity();
}
/**
* standard flow function from callee to caller; just identity
*/
public IFlowFunction getReturnFlowFunction(BasicBlockInContext<IExplodedBasicBlock> call,
BasicBlockInContext<IExplodedBasicBlock> src, BasicBlockInContext<IExplodedBasicBlock> dest) {
return IdentityFlowFunction.identity();
@ -145,11 +179,24 @@ public class ContextSensitiveReachingDefs {
}
/**
* Definition of the reaching definitions tabulation problem. Note that we choose to make the problem a <em>partially</em>
* balanced tabulation problem, where the solver is seeded with the putstatic instructions themselves. The problem is partially
* balanced since a definition in a callee used as a seed for the analysis may then reach a caller, yielding a "return" without a
* corresponding "call." An alternative to this approach, used in the Reps-Horwitz-Sagiv POPL95 paper, would be to "lift" the
* domain of putstatic instructions with a 0 (bottom) element, have a 0->0 transition in all transfer functions, and then seed the
* analysis with the path edge (main_entry, 0) -> (main_entry, 0). We choose the partially-balanced approach to avoid pollution of
* the flow functions.
*
*/
private class ReachingDefsProblem implements
PartiallyBalancedTabulationProblem<BasicBlockInContext<IExplodedBasicBlock>, CGNode, Pair<CGNode, Integer>> {
private ReachingDefsFlowFunctions flowFunctions = new ReachingDefsFlowFunctions(domain);
/**
* path edges corresponding to all putstatic instructions, used as seeds for the analysis
*/
private Collection<PathEdge<BasicBlockInContext<IExplodedBasicBlock>>> initialSeeds = collectInitialSeeds();
/**
@ -161,7 +208,7 @@ public class ContextSensitiveReachingDefs {
}
/**
* we use the entry block of the CGNode as the fake entry when propagating from callee to caller with unbalanced parens
* we use the entry block of the CGNode as the "fake" entry when propagating from callee to caller with unbalanced parens
*/
private BasicBlockInContext<IExplodedBasicBlock> getFakeEntry(final CGNode cgNode) {
BasicBlockInContext<IExplodedBasicBlock>[] entriesForProcedure = supergraph.getEntriesForProcedure(cgNode);
@ -169,6 +216,9 @@ public class ContextSensitiveReachingDefs {
return entriesForProcedure[0];
}
/**
* collect the putstatic instructions in the call graph as {@link PathEdge} seeds for the analysis
*/
private Collection<PathEdge<BasicBlockInContext<IExplodedBasicBlock>>> collectInitialSeeds() {
Collection<PathEdge<BasicBlockInContext<IExplodedBasicBlock>>> result = HashSetFactory.make();
for (BasicBlockInContext<IExplodedBasicBlock> bb : supergraph) {
@ -181,6 +231,7 @@ public class ContextSensitiveReachingDefs {
Pair<CGNode, Integer> fact = Pair.make(cgNode, ebb.getFirstInstructionIndex());
int factNum = domain.add(fact);
BasicBlockInContext<IExplodedBasicBlock> fakeEntry = getFakeEntry(cgNode);
// note that the fact number used for the source of this path edge doesn't really matter
result.add(PathEdge.createPathEdge(fakeEntry, factNum, bb, factNum));
}
@ -197,6 +248,9 @@ public class ContextSensitiveReachingDefs {
return domain;
}
/**
* we don't need a merge function; the default unioning of tabulation works fine
*/
public IMergeFunction getMergeFunction() {
return null;
}
@ -211,19 +265,28 @@ public class ContextSensitiveReachingDefs {
}
public TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, Pair<CGNode, Integer>> analyze() throws CancelException {
/**
* perform the tabulation analysis and return the {@link TabulationResult}
*/
public TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, Pair<CGNode, Integer>> analyze() {
PartiallyBalancedTabulationSolver<BasicBlockInContext<IExplodedBasicBlock>, CGNode, Pair<CGNode, Integer>> solver = PartiallyBalancedTabulationSolver
.createPartiallyBalancedTabulationSolver(new ReachingDefsProblem(), null);
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, Pair<CGNode, Integer>> result = solver.solve();
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, Pair<CGNode, Integer>> result = null;
try {
result = solver.solve();
} catch (CancelException e) {
// this shouldn't happen
assert false;
}
return result;
}
public ISupergraph<BasicBlockInContext<IExplodedBasicBlock>, CGNode> getSupergraph() {
return supergraph;
}
public TabulationDomain<Pair<CGNode,Integer>, BasicBlockInContext<IExplodedBasicBlock>> getDomain() {
public TabulationDomain<Pair<CGNode, Integer>, BasicBlockInContext<IExplodedBasicBlock>> getDomain() {
return domain;
}
}

View File

@ -47,23 +47,32 @@ public class IntraprocReachingDefs {
private final ExplodedControlFlowGraph ecfg;
/**
* maps instruction index of putstatic to more compact numbering for bitvectors
* maps the index of a putstatic IR instruction to a more compact numbering for use in bitvectors
*/
private final OrdinalSetMapping<Integer> putInstrNumbering;
/**
* used to resolve references to fields in putstatic instructions
*/
private final IClassHierarchy cha;
/**
* maps each static field to the numbers of the statements that define it
* maps each static field to the numbers of the statements (in {@link #putInstrNumbering}) that define it; used for kills in flow
* functions
*/
private final Map<IField, BitVector> staticField2DefStatements = HashMapFactory.make();
private static final boolean VERBOSE = true;
public IntraprocReachingDefs(ExplodedControlFlowGraph ecfg, IClassHierarchy cha) {
this.ecfg = ecfg;
this.cha = cha;
this.putInstrNumbering = numberPutStatics();
}
/**
* generate a numbering of the putstatic instructions
*/
private OrdinalSetMapping<Integer> numberPutStatics() {
ArrayList<Integer> putInstrs = new ArrayList<Integer>();
IR ir = ecfg.getIR();
@ -72,8 +81,10 @@ public class IntraprocReachingDefs {
SSAInstruction instruction = instructions[i];
if (instruction instanceof SSAPutInstruction && ((SSAPutInstruction) instruction).isStatic()) {
SSAPutInstruction putInstr = (SSAPutInstruction) instruction;
// instrNum is the number that will be assigned to this putstatic
int instrNum = putInstrs.size();
putInstrs.add(i);
// also update the mapping of static fields to def'ing statements
IField field = cha.resolveField(putInstr.getDeclaredField());
assert field != null;
BitVector bv = staticField2DefStatements.get(field);
@ -93,8 +104,10 @@ public class IntraprocReachingDefs {
throw new UnsupportedOperationException();
}
/**
* our meet operator is set union
*/
public AbstractMeetOperator<BitVectorVariable> getMeetOperator() {
// meet is union
return BitVectorUnion.instance();
}
@ -102,7 +115,7 @@ public class IntraprocReachingDefs {
SSAInstruction instruction = node.getInstruction();
int instructionIndex = node.getFirstInstructionIndex();
if (instruction instanceof SSAPutInstruction && ((SSAPutInstruction) instruction).isStatic()) {
// kill all defs of the same static field
// kill all defs of the same static field, and gen this instruction
final SSAPutInstruction putInstr = (SSAPutInstruction) instruction;
final IField field = cha.resolveField(putInstr.getDeclaredField());
assert field != null;
@ -111,12 +124,13 @@ public class IntraprocReachingDefs {
gen.set(putInstrNumbering.getMappedIndex(instructionIndex));
return new BitVectorKillGen(kill, gen);
} else {
// nothing defined
// identity function for non-putstatic instructions
return BitVectorIdentity.instance();
}
}
public boolean hasEdgeTransferFunctions() {
// we only need transfer functions on nodes
return false;
}
@ -126,21 +140,29 @@ public class IntraprocReachingDefs {
}
/**
* run the analysis
*
* @return the solver used for the analysis, which contains the analysis result
*/
public BitVectorSolver<IExplodedBasicBlock> analyze() {
BitVectorFramework<IExplodedBasicBlock, Integer> framework = new BitVectorFramework<IExplodedBasicBlock, Integer>(
ecfg, new TransferFunctions(), putInstrNumbering);
// the framework describes the dataflow problem, in particular the underlying graph and the transfer functions
BitVectorFramework<IExplodedBasicBlock, Integer> framework = new BitVectorFramework<IExplodedBasicBlock, Integer>(ecfg,
new TransferFunctions(), putInstrNumbering);
BitVectorSolver<IExplodedBasicBlock> solver = new BitVectorSolver<IExplodedBasicBlock>(framework);
try {
solver.solve(null);
} catch (CancelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// this shouldn't happen
assert false;
}
for (IExplodedBasicBlock ebb : ecfg) {
System.out.println(ebb);
System.out.println(ebb.getInstruction());
System.out.println(solver.getIn(ebb));
System.out.println(solver.getOut(ebb));
if (VERBOSE) {
for (IExplodedBasicBlock ebb : ecfg) {
System.out.println(ebb);
System.out.println(ebb.getInstruction());
System.out.println(solver.getIn(ebb));
System.out.println(solver.getOut(ebb));
}
}
return solver;
}