1274 lines
44 KiB
Java
1274 lines
44 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 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.ipa.slicer;
|
|
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import com.ibm.wala.analysis.stackMachine.AbstractIntStackMachine;
|
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
|
import com.ibm.wala.cfg.cdg.ControlDependenceGraph;
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
|
import com.ibm.wala.ipa.callgraph.CallGraph;
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
|
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
|
|
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
|
|
import com.ibm.wala.ipa.cfg.ExceptionPrunedCFG;
|
|
import com.ibm.wala.ipa.cfg.PrunedCFG;
|
|
import com.ibm.wala.ipa.modref.ExtendedHeapModel;
|
|
import com.ibm.wala.ipa.modref.ModRef;
|
|
import com.ibm.wala.ipa.slicer.Slicer.ControlDependenceOptions;
|
|
import com.ibm.wala.ipa.slicer.Slicer.DataDependenceOptions;
|
|
import com.ibm.wala.ipa.slicer.Statement.Kind;
|
|
import com.ibm.wala.ssa.DefUse;
|
|
import com.ibm.wala.ssa.IR;
|
|
import com.ibm.wala.ssa.ISSABasicBlock;
|
|
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
|
|
import com.ibm.wala.ssa.SSAAbstractThrowInstruction;
|
|
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
|
|
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
|
|
import com.ibm.wala.ssa.SSACFG;
|
|
import com.ibm.wala.ssa.SSACheckCastInstruction;
|
|
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
|
|
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
|
|
import com.ibm.wala.ssa.SSAInstanceofInstruction;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSANewInstruction;
|
|
import com.ibm.wala.ssa.SSAPhiInstruction;
|
|
import com.ibm.wala.ssa.SSAPiInstruction;
|
|
import com.ibm.wala.ssa.SSAReturnInstruction;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.Predicate;
|
|
import com.ibm.wala.util.collections.FilterIterator;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.collections.HashSetFactory;
|
|
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.config.SetOfClasses;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.debug.UnimplementedError;
|
|
import com.ibm.wala.util.graph.GraphUtil;
|
|
import com.ibm.wala.util.graph.NumberedGraph;
|
|
import com.ibm.wala.util.graph.dominators.Dominators;
|
|
import com.ibm.wala.util.graph.labeled.SlowSparseNumberedLabeledGraph;
|
|
import com.ibm.wala.util.intset.BitVectorIntSet;
|
|
import com.ibm.wala.util.intset.IntIterator;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
import com.ibm.wala.util.intset.OrdinalSet;
|
|
|
|
/**
|
|
* Program dependence graph for a single call graph node
|
|
*/
|
|
public class PDG<T extends InstanceKey> implements NumberedGraph<Statement> {
|
|
|
|
/** BEGIN Custom change: control deps */
|
|
public enum Dependency {CONTROL_DEP, DATA_AND_CONTROL_DEP}
|
|
|
|
private final SlowSparseNumberedLabeledGraph<Statement, Dependency> delegate =
|
|
new SlowSparseNumberedLabeledGraph<Statement, Dependency>(Dependency.DATA_AND_CONTROL_DEP);
|
|
/** END Custom change: control deps */
|
|
|
|
private final static boolean VERBOSE = false;
|
|
|
|
private final CGNode node;
|
|
|
|
private Statement[] paramCalleeStatements;
|
|
|
|
private Statement[] returnStatements;
|
|
|
|
/**
|
|
* TODO: using CallSiteReference is sloppy. clean it up.
|
|
*/
|
|
|
|
private final Map<CallSiteReference, Statement> callSite2Statement = HashMapFactory.make();
|
|
|
|
private final Map<CallSiteReference, Set<Statement>> callerParamStatements = HashMapFactory.make();
|
|
|
|
private final Map<CallSiteReference, Set<Statement>> callerReturnStatements = HashMapFactory.make();
|
|
|
|
private final HeapExclusions exclusions;
|
|
|
|
private final Collection<PointerKey> locationsHandled = HashSetFactory.make();
|
|
|
|
private final PointerAnalysis<T> pa;
|
|
|
|
private final ExtendedHeapModel heapModel;
|
|
|
|
private final Map<CGNode, OrdinalSet<PointerKey>> mod;
|
|
|
|
private final DataDependenceOptions dOptions;
|
|
|
|
private final ControlDependenceOptions cOptions;
|
|
|
|
private final CallGraph cg;
|
|
|
|
private final ModRef<T> modRef;
|
|
|
|
private final Map<CGNode, OrdinalSet<PointerKey>> ref;
|
|
|
|
private final boolean ignoreAllocHeapDefs;
|
|
|
|
private boolean isPopulated = false;
|
|
|
|
/**
|
|
* @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the
|
|
* SDG.
|
|
* @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
|
|
* @throws IllegalArgumentException if node is null
|
|
*/
|
|
public PDG(final CGNode node, PointerAnalysis<T> pa, Map<CGNode, OrdinalSet<PointerKey>> mod,
|
|
Map<CGNode, OrdinalSet<PointerKey>> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions,
|
|
HeapExclusions exclusions, CallGraph cg, ModRef<T> modRef) {
|
|
this(node, pa, mod, ref, dOptions, cOptions, exclusions, cg, modRef, false);
|
|
}
|
|
|
|
/**
|
|
* @param mod the set of heap locations which may be written (transitively) by this node. These are logically return values in the
|
|
* SDG.
|
|
* @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
|
|
* @throws IllegalArgumentException if node is null
|
|
*/
|
|
public PDG(final CGNode node, PointerAnalysis<T> pa, Map<CGNode, OrdinalSet<PointerKey>> mod,
|
|
Map<CGNode, OrdinalSet<PointerKey>> ref, DataDependenceOptions dOptions, ControlDependenceOptions cOptions,
|
|
HeapExclusions exclusions, CallGraph cg, ModRef<T> modRef, boolean ignoreAllocHeapDefs) {
|
|
|
|
super();
|
|
if (node == null) {
|
|
throw new IllegalArgumentException("node is null");
|
|
}
|
|
this.cg = cg;
|
|
this.node = node;
|
|
this.heapModel = pa != null? modRef.makeHeapModel(pa): null;
|
|
this.pa = pa;
|
|
this.dOptions = dOptions;
|
|
this.cOptions = cOptions;
|
|
this.mod = mod;
|
|
this.exclusions = exclusions;
|
|
this.modRef = modRef;
|
|
this.ref = ref;
|
|
this.ignoreAllocHeapDefs = ignoreAllocHeapDefs;
|
|
}
|
|
|
|
/**
|
|
* WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s, and equals() of {@link SSAInstruction} assumes a
|
|
* canonical representative for each instruction, we <bf>must</bf> ensure that we use the same IR object throughout
|
|
* initialization!!
|
|
*/
|
|
private void populate() {
|
|
if (!isPopulated) {
|
|
// ensure that we keep the single, canonical IR live throughout initialization, while the instructionIndices map
|
|
// is live.
|
|
IR ir = node.getIR();
|
|
isPopulated = true;
|
|
|
|
Map<SSAInstruction, Integer> instructionIndices = computeInstructionIndices(ir);
|
|
createNodes(ref, cOptions, ir);
|
|
createScalarEdges(cOptions, ir, instructionIndices);
|
|
}
|
|
}
|
|
|
|
private void createScalarEdges(ControlDependenceOptions cOptions, IR ir, Map<SSAInstruction, Integer> instructionIndices) {
|
|
createScalarDataDependenceEdges(ir, instructionIndices);
|
|
createControlDependenceEdges(cOptions, ir, instructionIndices);
|
|
}
|
|
|
|
/**
|
|
* return the set of all PARAM_CALLER and HEAP_PARAM_CALLER statements associated with a given call
|
|
*/
|
|
public Set<Statement> getCallerParamStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
|
|
if (call == null) {
|
|
throw new IllegalArgumentException("call == null");
|
|
}
|
|
populate();
|
|
return callerParamStatements.get(call.getCallSite());
|
|
}
|
|
|
|
/**
|
|
* return the set of all PARAM_CALLER, HEAP_PARAM_CALLER, and NORMAL statements (i.e., the actual call statement) associated with
|
|
* a given call
|
|
*/
|
|
public Set<Statement> getCallStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
|
|
Set<Statement> callerParamStatements = getCallerParamStatements(call);
|
|
Set<Statement> result = HashSetFactory.make(callerParamStatements.size() + 1);
|
|
result.addAll(callerParamStatements);
|
|
result.add(callSite2Statement.get(call.getCallSite()));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* return the set of all NORMAL_RETURN_CALLER and HEAP_RETURN_CALLER statements associated with a given call.
|
|
*/
|
|
public Set<Statement> getCallerReturnStatements(SSAAbstractInvokeInstruction call) throws IllegalArgumentException {
|
|
if (call == null) {
|
|
throw new IllegalArgumentException("call == null");
|
|
}
|
|
populate();
|
|
return callerReturnStatements.get(call.getCallSite());
|
|
}
|
|
|
|
/**
|
|
* Create all control dependence edges in this PDG.
|
|
*/
|
|
private void createControlDependenceEdges(ControlDependenceOptions cOptions, IR ir,
|
|
Map<SSAInstruction, Integer> instructionIndices) {
|
|
if (cOptions.equals(ControlDependenceOptions.NONE)) {
|
|
return;
|
|
}
|
|
if (ir == null) {
|
|
return;
|
|
}
|
|
ControlFlowGraph<SSAInstruction, ISSABasicBlock> controlFlowGraph = ir.getControlFlowGraph();
|
|
if (cOptions.isIgnoreExceptions()) {
|
|
PrunedCFG<SSAInstruction, ISSABasicBlock> prunedCFG = ExceptionPrunedCFG.make(controlFlowGraph);
|
|
// In case the CFG has only the entry and exit nodes left
|
|
// and no edges because the only control dependencies
|
|
// were exceptional, simply return because at this point there are no nodes.
|
|
// Otherwise, later this may raise an Exception.
|
|
if (prunedCFG.getNumberOfNodes() == 2
|
|
&& prunedCFG.containsNode(controlFlowGraph.entry())
|
|
&& prunedCFG.containsNode(controlFlowGraph.exit())
|
|
&& GraphUtil.countEdges(prunedCFG) == 0) {
|
|
return;
|
|
}
|
|
controlFlowGraph = prunedCFG;
|
|
} else {
|
|
Assertions.productionAssertion(cOptions.equals(ControlDependenceOptions.FULL));
|
|
}
|
|
|
|
ControlDependenceGraph<ISSABasicBlock> cdg = new ControlDependenceGraph<ISSABasicBlock>(
|
|
controlFlowGraph);
|
|
for (ISSABasicBlock bb : cdg) {
|
|
if (bb.isExitBlock()) {
|
|
// nothing should be control-dependent on the exit block.
|
|
continue;
|
|
}
|
|
|
|
Statement src = null;
|
|
if (bb.isEntryBlock()) {
|
|
src = new MethodEntryStatement(node);
|
|
} else {
|
|
SSAInstruction s = ir.getInstructions()[bb.getLastInstructionIndex()];
|
|
if (s == null) {
|
|
// should have no control dependent successors.
|
|
// leave src null.
|
|
} else {
|
|
src = ssaInstruction2Statement(s, ir, instructionIndices);
|
|
// add edges from call statements to parameter passing and return
|
|
// SJF: Alexey and I think that we should just define ParamStatements
|
|
// as
|
|
// being control dependent on nothing ... they only represent pure
|
|
// data dependence. So, I'm commenting out the following.
|
|
// if (s instanceof SSAAbstractInvokeInstruction) {
|
|
// SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction)
|
|
// s;
|
|
// for (Statement st : callerParamStatements.get(call.getCallSite()))
|
|
// {
|
|
// addEdge(src, st);
|
|
// }
|
|
// for (Statement st : callerReturnStatements.get(call.getCallSite()))
|
|
// {
|
|
// addEdge(src, st);
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
// add edges for every control-dependent statement in the IR, if there are
|
|
// any
|
|
// control-dependent successors
|
|
if (src != null) {
|
|
for (Iterator<? extends ISSABasicBlock> succ = cdg.getSuccNodes(bb); succ.hasNext();) {
|
|
ISSABasicBlock bb2 = succ.next();
|
|
for (Iterator<SSAInstruction> it2 = bb2.iterator(); it2.hasNext();) {
|
|
SSAInstruction st = it2.next();
|
|
if (st != null) {
|
|
Statement dest = ssaInstruction2Statement(st, ir, instructionIndices);
|
|
assert src != null;
|
|
delegate.addEdge(src, dest);
|
|
/** BEGIN Custom change: control deps */
|
|
delegate.addEdge(src, dest, Dependency.CONTROL_DEP);
|
|
/** END Custom change: control deps */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// the CDG does not represent control dependences from the entry node.
|
|
// add these manually
|
|
// We add control dependences to all instructions in all basic blocks B that _must_ execute.
|
|
// B is the set of blocks that dominate the exit basic block
|
|
Statement methodEntry = new MethodEntryStatement(node);
|
|
Dominators<ISSABasicBlock> dom = Dominators.make(controlFlowGraph, controlFlowGraph.entry());
|
|
for (ISSABasicBlock exitDom : Iterator2Iterable.make(dom.dominators(controlFlowGraph.exit()))) {
|
|
for (SSAInstruction st : exitDom) {
|
|
Statement dest = ssaInstruction2Statement(st, ir, instructionIndices);
|
|
delegate.addEdge(methodEntry, dest);
|
|
/** BEGIN Custom change: control deps */
|
|
delegate.addEdge(methodEntry, dest, Dependency.CONTROL_DEP);
|
|
/** END Custom change: control deps */
|
|
}
|
|
}
|
|
// add CD from method entry to all callee parameter assignments
|
|
// SJF: Alexey and I think that we should just define ParamStatements as
|
|
// being control dependent on nothing ... they only represent pure
|
|
// data dependence. So, I'm commenting out the following.
|
|
// for (int i = 0; i < paramCalleeStatements.length; i++) {
|
|
// addEdge(methodEntry, paramCalleeStatements[i]);
|
|
// }
|
|
|
|
/**
|
|
* JTD: While phi nodes live in a particular basic block, they represent a meet of values from multiple blocks. Hence, they are
|
|
* really like multiple statements that are control dependent in the manner of the predecessor blocks. When the slicer is
|
|
* following both data and control dependences, it therefore seems right to add control dependence edges to represent how a phi
|
|
* node depends on predecessor blocks.
|
|
*/
|
|
if (!dOptions.equals(DataDependenceOptions.NONE)) {
|
|
for (ISSABasicBlock bb : cdg) {
|
|
for (Iterator<SSAPhiInstruction> ps = bb.iteratePhis(); ps.hasNext();) {
|
|
SSAPhiInstruction phi = ps.next();
|
|
Statement phiSt = ssaInstruction2Statement(phi, ir, instructionIndices);
|
|
int phiUseIndex = 0;
|
|
for (Iterator<? extends ISSABasicBlock> preds = controlFlowGraph.getPredNodes(bb); preds.hasNext();) {
|
|
ISSABasicBlock pb = preds.next();
|
|
int use = phi.getUse(phiUseIndex);
|
|
if (use == AbstractIntStackMachine.TOP) {
|
|
// the predecessor is part of some infeasible bytecode. we probably don't want slices to include such code, so ignore.
|
|
continue;
|
|
}
|
|
if (controlFlowGraph.getSuccNodeCount(pb) > 1) {
|
|
// in this case, there is more than one edge from the
|
|
// predecessor block, hence the phi node actually
|
|
// depends on the last instruction in the previous
|
|
// block, rather than having the same dependences as
|
|
// statements in that block.
|
|
SSAInstruction pss = ir.getInstructions()[pb.getLastInstructionIndex()];
|
|
assert pss != null;
|
|
Statement pst = ssaInstruction2Statement(pss, ir, instructionIndices);
|
|
delegate.addEdge(pst, phiSt);
|
|
/** BEGIN Custom change: control deps */
|
|
delegate.addEdge(pst, phiSt, Dependency.CONTROL_DEP);
|
|
/** END Custom change: control deps */
|
|
} else {
|
|
for (Iterator<? extends ISSABasicBlock> cdps = cdg.getPredNodes(pb); cdps.hasNext();) {
|
|
ISSABasicBlock cpb = cdps.next();
|
|
/** BEGIN Custom change: control deps */
|
|
if (cpb.getLastInstructionIndex() < 0) {
|
|
continue;
|
|
}
|
|
/** END Custom change: control deps */
|
|
SSAInstruction cps = ir.getInstructions()[cpb.getLastInstructionIndex()];
|
|
assert cps != null : "unexpected null final instruction for CDG predecessor " + cpb + " in node " + node;
|
|
Statement cpst = ssaInstruction2Statement(cps, ir, instructionIndices);
|
|
delegate.addEdge(cpst, phiSt);
|
|
/** BEGIN Custom change: control deps */
|
|
delegate.addEdge(cpst, phiSt, Dependency.CONTROL_DEP);
|
|
/** END Custom change: control deps */
|
|
}
|
|
}
|
|
phiUseIndex++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create all data dependence edges in this PDG.
|
|
*
|
|
* Scalar dependences are taken from SSA def-use information.
|
|
*
|
|
* Heap dependences are computed by a reaching defs analysis.
|
|
*
|
|
* @param pa
|
|
* @param mod
|
|
*/
|
|
private void createScalarDataDependenceEdges(IR ir, Map<SSAInstruction, Integer> instructionIndices) {
|
|
if (dOptions.equals(DataDependenceOptions.NONE)) {
|
|
return;
|
|
}
|
|
|
|
if (ir == null) {
|
|
return;
|
|
}
|
|
|
|
// this is tricky .. I'm explicitly creating a new DefUse to make sure it refers to the instructions we need from
|
|
// the "one true" ir of the moment.
|
|
DefUse DU = new DefUse(ir);
|
|
SSAInstruction[] instructions = ir.getInstructions();
|
|
|
|
//
|
|
// TODO: teach some other bit of code about the uses of
|
|
// GetCaughtException, and then delete this code.
|
|
//
|
|
if (!dOptions.isIgnoreExceptions()) {
|
|
for (ISSABasicBlock bb : ir.getControlFlowGraph()) {
|
|
if (bb.isCatchBlock()) {
|
|
SSACFG.ExceptionHandlerBasicBlock ehbb = (SSACFG.ExceptionHandlerBasicBlock) bb;
|
|
|
|
if (ehbb.getCatchInstruction() != null) {
|
|
Statement c = ssaInstruction2Statement(ehbb.getCatchInstruction(), ir, instructionIndices);
|
|
|
|
for (ISSABasicBlock pb : ir.getControlFlowGraph().getExceptionalPredecessors(ehbb)) {
|
|
SSAInstruction st = instructions[pb.getLastInstructionIndex()];
|
|
|
|
if (st instanceof SSAAbstractInvokeInstruction) {
|
|
delegate.addEdge(new ExceptionalReturnCaller(node, pb.getLastInstructionIndex()), c);
|
|
} else if (st instanceof SSAAbstractThrowInstruction) {
|
|
delegate.addEdge(ssaInstruction2Statement(st, ir, instructionIndices), c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Iterator<? extends Statement> it = iterator(); it.hasNext();) {
|
|
Statement s = it.next();
|
|
switch (s.getKind()) {
|
|
case NORMAL:
|
|
case CATCH:
|
|
case PI:
|
|
case PHI: {
|
|
SSAInstruction statement = statement2SSAInstruction(instructions, s);
|
|
// note that data dependencies from invoke instructions will pass
|
|
// interprocedurally
|
|
if (!(statement instanceof SSAAbstractInvokeInstruction)) {
|
|
if (dOptions.isTerminateAtCast() && (statement instanceof SSACheckCastInstruction)) {
|
|
break;
|
|
}
|
|
if (dOptions.isTerminateAtCast() && (statement instanceof SSAInstanceofInstruction)) {
|
|
break;
|
|
}
|
|
// add edges from this statement to every use of the def of this
|
|
// statement
|
|
for (int i = 0; i < statement.getNumberOfDefs(); i++) {
|
|
int def = statement.getDef(i);
|
|
for (Iterator<SSAInstruction> it2 = DU.getUses(def); it2.hasNext();) {
|
|
SSAInstruction use = it2.next();
|
|
if (dOptions.isIgnoreBasePtrs()) {
|
|
if (use instanceof SSANewInstruction) {
|
|
// cut out array length parameters
|
|
continue;
|
|
}
|
|
if (hasBasePointer(use)) {
|
|
int base = getBasePointer(use);
|
|
if (def == base) {
|
|
// skip the edge to the base pointer
|
|
continue;
|
|
}
|
|
if (use instanceof SSAArrayReferenceInstruction) {
|
|
SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use;
|
|
if (def == arr.getIndex()) {
|
|
// skip the edge to the array index
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Statement u = ssaInstruction2Statement(use, ir, instructionIndices);
|
|
delegate.addEdge(s, u);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EXC_RET_CALLER:
|
|
case NORMAL_RET_CALLER:
|
|
case PARAM_CALLEE: {
|
|
if (dOptions.isIgnoreExceptions()) {
|
|
assert !s.getKind().equals(Kind.EXC_RET_CALLER);
|
|
}
|
|
|
|
ValueNumberCarrier a = (ValueNumberCarrier) s;
|
|
for (Iterator<SSAInstruction> it2 = DU.getUses(a.getValueNumber()); it2.hasNext();) {
|
|
SSAInstruction use = it2.next();
|
|
if (dOptions.isIgnoreBasePtrs()) {
|
|
if (use instanceof SSANewInstruction) {
|
|
// cut out array length parameters
|
|
continue;
|
|
}
|
|
if (hasBasePointer(use)) {
|
|
int base = getBasePointer(use);
|
|
if (a.getValueNumber() == base) {
|
|
// skip the edge to the base pointer
|
|
continue;
|
|
}
|
|
if (use instanceof SSAArrayReferenceInstruction) {
|
|
SSAArrayReferenceInstruction arr = (SSAArrayReferenceInstruction) use;
|
|
if (a.getValueNumber() == arr.getIndex()) {
|
|
// skip the edge to the array index
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Statement u = ssaInstruction2Statement(use, ir, instructionIndices);
|
|
delegate.addEdge(s, u);
|
|
}
|
|
break;
|
|
}
|
|
case NORMAL_RET_CALLEE:
|
|
for (NormalStatement ret : computeReturnStatements(ir)) {
|
|
delegate.addEdge(ret, s);
|
|
}
|
|
break;
|
|
case EXC_RET_CALLEE:
|
|
if (dOptions.isIgnoreExceptions()) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
// TODO: this is overly conservative. deal with catch blocks?
|
|
for (IntIterator ii = getPEIs(ir).intIterator(); ii.hasNext();) {
|
|
int index = ii.next();
|
|
SSAInstruction pei = ir.getInstructions()[index];
|
|
if (dOptions.isTerminateAtCast() && (pei instanceof SSACheckCastInstruction)) {
|
|
continue;
|
|
}
|
|
if (pei instanceof SSAAbstractInvokeInstruction) {
|
|
if (! dOptions.isIgnoreExceptions()) {
|
|
Statement st = new ExceptionalReturnCaller(node, index);
|
|
delegate.addEdge(st, s);
|
|
}
|
|
} else {
|
|
delegate.addEdge(new NormalStatement(node, index), s);
|
|
}
|
|
}
|
|
break;
|
|
case PARAM_CALLER: {
|
|
ParamCaller pac = (ParamCaller) s;
|
|
int vn = pac.getValueNumber();
|
|
// note that if the caller is the fake root method and the parameter
|
|
// type is primitive,
|
|
// it's possible to have a value number of -1. If so, just ignore it.
|
|
if (vn > -1) {
|
|
if (ir.getSymbolTable().isParameter(vn)) {
|
|
Statement a = new ParamCallee(node, vn);
|
|
delegate.addEdge(a, pac);
|
|
} else {
|
|
SSAInstruction d = DU.getDef(vn);
|
|
if (dOptions.isTerminateAtCast() && (d instanceof SSACheckCastInstruction)) {
|
|
break;
|
|
}
|
|
if (d != null) {
|
|
if (d instanceof SSAAbstractInvokeInstruction) {
|
|
SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) d;
|
|
if (vn == call.getException()) {
|
|
if (! dOptions.isIgnoreExceptions()) {
|
|
Statement st = new ExceptionalReturnCaller(node, instructionIndices.get(d));
|
|
delegate.addEdge(st, pac);
|
|
}
|
|
} else {
|
|
Statement st = new NormalReturnCaller(node, instructionIndices.get(d));
|
|
delegate.addEdge(st, pac);
|
|
}
|
|
} else {
|
|
Statement ds = ssaInstruction2Statement(d, ir, instructionIndices);
|
|
delegate.addEdge(ds, pac);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HEAP_RET_CALLEE:
|
|
case HEAP_RET_CALLER:
|
|
case HEAP_PARAM_CALLER:
|
|
case HEAP_PARAM_CALLEE:
|
|
case METHOD_ENTRY:
|
|
case METHOD_EXIT:
|
|
// do nothing
|
|
break;
|
|
default:
|
|
Assertions.UNREACHABLE(s.toString());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class SingletonSet extends SetOfClasses implements Serializable {
|
|
|
|
/* Serial version */
|
|
private static final long serialVersionUID = -3256390509887654324L;
|
|
|
|
private final TypeReference t;
|
|
|
|
SingletonSet(TypeReference t) {
|
|
this.t = t;
|
|
}
|
|
|
|
@Override
|
|
public void add(String klass) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public boolean contains(String klassName) {
|
|
return t.getName().toString().substring(1).equals(klassName);
|
|
}
|
|
}
|
|
|
|
private static class SetComplement extends SetOfClasses implements Serializable {
|
|
|
|
/* Serial version */
|
|
private static final long serialVersionUID = -3256390509887654323L;
|
|
|
|
private final SetOfClasses set;
|
|
|
|
SetComplement(SetOfClasses set) {
|
|
this.set = set;
|
|
}
|
|
|
|
static SetComplement complement(SetOfClasses set) {
|
|
return new SetComplement(set);
|
|
}
|
|
|
|
@Override
|
|
public void add(String klass) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public boolean contains(String klassName) {
|
|
return !set.contains(klassName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create heap data dependence edges in this PDG relevant to a particular {@link PointerKey}.
|
|
*/
|
|
private void createHeapDataDependenceEdges(final PointerKey pk) {
|
|
|
|
if (locationsHandled.contains(pk)) {
|
|
return;
|
|
} else {
|
|
locationsHandled.add(pk);
|
|
}
|
|
if (dOptions.isIgnoreHeap() || (exclusions != null && exclusions.excludes(pk))) {
|
|
return;
|
|
}
|
|
|
|
TypeReference t = HeapExclusions.getType(pk);
|
|
if (t == null) {
|
|
return;
|
|
}
|
|
|
|
// It's OK to create a new IR here; we're not keeping any hashing live up to this point
|
|
IR ir = node.getIR();
|
|
if (ir == null) {
|
|
return;
|
|
}
|
|
|
|
if (VERBOSE) {
|
|
System.err.println("Location " + pk);
|
|
}
|
|
|
|
// in reaching defs calculation, exclude heap statements that are
|
|
// irrelevant.
|
|
Predicate f = new Predicate() {
|
|
@Override public boolean test(Object o) {
|
|
if (o instanceof HeapStatement) {
|
|
HeapStatement h = (HeapStatement) o;
|
|
return h.getLocation().equals(pk);
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
};
|
|
Collection<Statement> relevantStatements = Iterator2Collection.toSet(new FilterIterator<Statement>(iterator(), f));
|
|
|
|
Map<Statement, OrdinalSet<Statement>> heapReachingDefs = new HeapReachingDefs<T>(modRef, heapModel).computeReachingDefs(node, ir, pa, mod,
|
|
relevantStatements, new HeapExclusions(SetComplement.complement(new SingletonSet(t))), cg);
|
|
|
|
for (Statement st : heapReachingDefs.keySet()) {
|
|
switch (st.getKind()) {
|
|
case NORMAL:
|
|
case CATCH:
|
|
case PHI:
|
|
case PI: {
|
|
OrdinalSet<Statement> defs = heapReachingDefs.get(st);
|
|
if (defs != null) {
|
|
for (Statement def : defs) {
|
|
delegate.addEdge(def, st);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EXC_RET_CALLER:
|
|
case NORMAL_RET_CALLER:
|
|
case PARAM_CALLEE:
|
|
case NORMAL_RET_CALLEE:
|
|
case PARAM_CALLER:
|
|
case EXC_RET_CALLEE:
|
|
break;
|
|
case HEAP_RET_CALLEE:
|
|
case HEAP_RET_CALLER:
|
|
case HEAP_PARAM_CALLER: {
|
|
OrdinalSet<Statement> defs = heapReachingDefs.get(st);
|
|
if (defs != null) {
|
|
for (Statement def : defs) {
|
|
delegate.addEdge(def, st);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case HEAP_PARAM_CALLEE:
|
|
case METHOD_ENTRY:
|
|
case METHOD_EXIT:
|
|
// do nothing .. there are no incoming edges
|
|
break;
|
|
default:
|
|
Assertions.UNREACHABLE(st.toString());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean hasBasePointer(SSAInstruction use) {
|
|
if (use instanceof SSAFieldAccessInstruction) {
|
|
SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use;
|
|
return !f.isStatic();
|
|
} else if (use instanceof SSAArrayReferenceInstruction) {
|
|
return true;
|
|
} else if (use instanceof SSAArrayLengthInstruction) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static int getBasePointer(SSAInstruction use) {
|
|
if (use instanceof SSAFieldAccessInstruction) {
|
|
SSAFieldAccessInstruction f = (SSAFieldAccessInstruction) use;
|
|
return f.getRef();
|
|
} else if (use instanceof SSAArrayReferenceInstruction) {
|
|
SSAArrayReferenceInstruction a = (SSAArrayReferenceInstruction) use;
|
|
return a.getArrayRef();
|
|
} else if (use instanceof SSAArrayLengthInstruction) {
|
|
SSAArrayLengthInstruction s = (SSAArrayLengthInstruction) use;
|
|
return s.getArrayRef();
|
|
} else {
|
|
Assertions.UNREACHABLE("BOOM");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return Statements representing each return instruction in the ir
|
|
*/
|
|
private Collection<NormalStatement> computeReturnStatements(final IR ir) {
|
|
Predicate filter = new Predicate() {
|
|
@Override public boolean test(Object o) {
|
|
if (o instanceof NormalStatement) {
|
|
NormalStatement s = (NormalStatement) o;
|
|
SSAInstruction st = ir.getInstructions()[s.getInstructionIndex()];
|
|
return st instanceof SSAReturnInstruction;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
return Iterator2Collection.toSet(new FilterIterator<NormalStatement>(iterator(), filter));
|
|
}
|
|
|
|
/**
|
|
* @return {@link IntSet} representing instruction indices of each PEI in the ir
|
|
*/
|
|
private static IntSet getPEIs(final IR ir) {
|
|
BitVectorIntSet result = new BitVectorIntSet();
|
|
for (int i = 0; i < ir.getInstructions().length; i++) {
|
|
if (ir.getInstructions()[i] != null && ir.getInstructions()[i].isPEI()) {
|
|
result.add(i);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Wrap an {@link SSAInstruction} in a {@link Statement}. WARNING: Since we're using a {@link HashMap} of {@link SSAInstruction}s,
|
|
* and equals() of {@link SSAInstruction} assumes a canonical representative for each instruction, we <bf>must</bf> ensure that we
|
|
* use the same IR object throughout initialization!!
|
|
*/
|
|
private Statement ssaInstruction2Statement(SSAInstruction s, IR ir, Map<SSAInstruction, Integer> instructionIndices) {
|
|
return ssaInstruction2Statement(node, s, instructionIndices, ir);
|
|
}
|
|
|
|
public static synchronized Statement ssaInstruction2Statement(CGNode node, SSAInstruction s,
|
|
Map<SSAInstruction, Integer> instructionIndices, IR ir) {
|
|
if (node == null) {
|
|
throw new IllegalArgumentException("null node");
|
|
}
|
|
if (s == null) {
|
|
throw new IllegalArgumentException("null s");
|
|
}
|
|
if (s instanceof SSAPhiInstruction) {
|
|
SSAPhiInstruction phi = (SSAPhiInstruction) s;
|
|
return new PhiStatement(node, phi);
|
|
} else if (s instanceof SSAPiInstruction) {
|
|
SSAPiInstruction pi = (SSAPiInstruction) s;
|
|
return new PiStatement(node, pi);
|
|
} else if (s instanceof SSAGetCaughtExceptionInstruction) {
|
|
return new GetCaughtExceptionStatement(node, ((SSAGetCaughtExceptionInstruction) s));
|
|
} else {
|
|
Integer x = instructionIndices.get(s);
|
|
if (x == null) {
|
|
Assertions.UNREACHABLE(s.toString() + "\nnot found in map of\n" + ir);
|
|
}
|
|
return new NormalStatement(node, x.intValue());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return for each SSAInstruction, its instruction index in the ir instruction array
|
|
*/
|
|
public static Map<SSAInstruction, Integer> computeInstructionIndices(IR ir) {
|
|
Map<SSAInstruction, Integer> result = HashMapFactory.make();
|
|
if (ir != null) {
|
|
SSAInstruction[] instructions = ir.getInstructions();
|
|
for (int i = 0; i < instructions.length; i++) {
|
|
SSAInstruction s = instructions[i];
|
|
if (s != null) {
|
|
result.put(s, new Integer(i));
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Convert a NORMAL or PHI Statement to an SSAInstruction
|
|
*/
|
|
private static SSAInstruction statement2SSAInstruction(SSAInstruction[] instructions, Statement s) {
|
|
SSAInstruction statement = null;
|
|
switch (s.getKind()) {
|
|
case NORMAL:
|
|
NormalStatement n = (NormalStatement) s;
|
|
statement = instructions[n.getInstructionIndex()];
|
|
break;
|
|
case PHI:
|
|
PhiStatement p = (PhiStatement) s;
|
|
statement = p.getPhi();
|
|
break;
|
|
case PI:
|
|
PiStatement ps = (PiStatement) s;
|
|
statement = ps.getPi();
|
|
break;
|
|
case CATCH:
|
|
GetCaughtExceptionStatement g = (GetCaughtExceptionStatement) s;
|
|
statement = g.getInstruction();
|
|
break;
|
|
default:
|
|
Assertions.UNREACHABLE(s.toString());
|
|
}
|
|
return statement;
|
|
}
|
|
|
|
/**
|
|
* Create all nodes in this PDG. Each node is a Statement.
|
|
*/
|
|
private void createNodes(Map<CGNode, OrdinalSet<PointerKey>> ref, ControlDependenceOptions cOptions, IR ir) {
|
|
|
|
if (ir != null) {
|
|
createNormalStatements(ir, ref);
|
|
createSpecialStatements(ir);
|
|
}
|
|
|
|
createCalleeParams();
|
|
createReturnStatements();
|
|
|
|
delegate.addNode(new MethodEntryStatement(node));
|
|
delegate.addNode(new MethodExitStatement(node));
|
|
}
|
|
|
|
/**
|
|
* create nodes representing defs of the return values
|
|
*
|
|
* @param mod the set of heap locations which may be written (transitively) by this node. These are logically parameters in the
|
|
* SDG.
|
|
* @param dOptions
|
|
*/
|
|
private void createReturnStatements() {
|
|
ArrayList<Statement> list = new ArrayList<Statement>();
|
|
if (!node.getMethod().getReturnType().equals(TypeReference.Void)) {
|
|
NormalReturnCallee n = new NormalReturnCallee(node);
|
|
delegate.addNode(n);
|
|
list.add(n);
|
|
}
|
|
if (!dOptions.isIgnoreExceptions()) {
|
|
ExceptionalReturnCallee e = new ExceptionalReturnCallee(node);
|
|
delegate.addNode(e);
|
|
list.add(e);
|
|
}
|
|
if (!dOptions.isIgnoreHeap()) {
|
|
for (PointerKey p : mod.get(node)) {
|
|
Statement h = new HeapStatement.HeapReturnCallee(node, p);
|
|
delegate.addNode(h);
|
|
list.add(h);
|
|
}
|
|
}
|
|
returnStatements = new Statement[list.size()];
|
|
list.toArray(returnStatements);
|
|
}
|
|
|
|
/**
|
|
* create nodes representing defs of formal parameters
|
|
*
|
|
* @param ref the set of heap locations which may be read (transitively) by this node. These are logically parameters in the SDG.
|
|
*/
|
|
private void createCalleeParams() {
|
|
if (paramCalleeStatements == null) {
|
|
ArrayList<Statement> list = new ArrayList<Statement>();
|
|
int paramCount = node.getMethod().getNumberOfParameters();
|
|
|
|
for (int i = 1; i <= paramCount; i++) {
|
|
ParamCallee s = new ParamCallee(node, i);
|
|
delegate.addNode(s);
|
|
list.add(s);
|
|
}
|
|
if (!dOptions.isIgnoreHeap()) {
|
|
for (PointerKey p : ref.get(node)) {
|
|
Statement h = new HeapStatement.HeapParamCallee(node, p);
|
|
delegate.addNode(h);
|
|
list.add(h);
|
|
}
|
|
}
|
|
paramCalleeStatements = new Statement[list.size()];
|
|
list.toArray(paramCalleeStatements);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create nodes corresponding to
|
|
* <ul>
|
|
* <li>phi instructions
|
|
* <li>getCaughtExceptions
|
|
* </ul>
|
|
*/
|
|
private void createSpecialStatements(IR ir) {
|
|
// create a node for instructions which do not correspond to bytecode
|
|
for (Iterator<SSAInstruction> it = ir.iterateAllInstructions(); it.hasNext();) {
|
|
SSAInstruction s = it.next();
|
|
if (s instanceof SSAPhiInstruction) {
|
|
delegate.addNode(new PhiStatement(node, (SSAPhiInstruction) s));
|
|
} else if (s instanceof SSAGetCaughtExceptionInstruction) {
|
|
delegate.addNode(new GetCaughtExceptionStatement(node, (SSAGetCaughtExceptionInstruction) s));
|
|
} else if (s instanceof SSAPiInstruction) {
|
|
delegate.addNode(new PiStatement(node, (SSAPiInstruction) s));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create nodes in the graph corresponding to "normal" (bytecode) instructions
|
|
*/
|
|
private void createNormalStatements(IR ir, Map<CGNode, OrdinalSet<PointerKey>> ref) {
|
|
// create a node for every normal instruction in the IR
|
|
SSAInstruction[] instructions = ir.getInstructions();
|
|
for (int i = 0; i < instructions.length; i++) {
|
|
SSAInstruction s = instructions[i];
|
|
|
|
if (s instanceof SSAGetCaughtExceptionInstruction) {
|
|
continue;
|
|
}
|
|
|
|
if (s != null) {
|
|
final NormalStatement statement = new NormalStatement(node, i);
|
|
delegate.addNode(statement);
|
|
if (s instanceof SSAAbstractInvokeInstruction) {
|
|
callSite2Statement.put(((SSAAbstractInvokeInstruction) s).getCallSite(), statement);
|
|
addParamPassingStatements(i, ref, ir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create nodes in the graph corresponding to in/out parameter passing for a call instruction
|
|
*/
|
|
private void addParamPassingStatements(int callIndex, Map<CGNode, OrdinalSet<PointerKey>> ref, IR ir) {
|
|
SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) ir.getInstructions()[callIndex];
|
|
Collection<Statement> params = MapUtil.findOrCreateSet(callerParamStatements, call.getCallSite());
|
|
Collection<Statement> rets = MapUtil.findOrCreateSet(callerReturnStatements, call.getCallSite());
|
|
for (int j = 0; j < call.getNumberOfUses(); j++) {
|
|
Statement st = new ParamCaller(node, callIndex, call.getUse(j));
|
|
delegate.addNode(st);
|
|
params.add(st);
|
|
}
|
|
if (!call.getDeclaredResultType().equals(TypeReference.Void)) {
|
|
Statement st = new NormalReturnCaller(node, callIndex);
|
|
delegate.addNode(st);
|
|
rets.add(st);
|
|
}
|
|
{
|
|
if (!dOptions.isIgnoreExceptions()) {
|
|
Statement st = new ExceptionalReturnCaller(node, callIndex);
|
|
delegate.addNode(st);
|
|
rets.add(st);
|
|
}
|
|
}
|
|
|
|
if (!dOptions.isIgnoreHeap()) {
|
|
OrdinalSet<PointerKey> uref = unionHeapLocations(cg, node, call, ref);
|
|
for (PointerKey p : uref) {
|
|
Statement st = new HeapStatement.HeapParamCaller(node, callIndex, p);
|
|
delegate.addNode(st);
|
|
params.add(st);
|
|
}
|
|
OrdinalSet<PointerKey> umod = unionHeapLocations(cg, node, call, mod);
|
|
for (PointerKey p : umod) {
|
|
Statement st = new HeapStatement.HeapReturnCaller(node, callIndex, p);
|
|
delegate.addNode(st);
|
|
rets.add(st);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return the set of all locations read by any callee at a call site.
|
|
*/
|
|
private static OrdinalSet<PointerKey> unionHeapLocations(CallGraph cg, CGNode n, SSAAbstractInvokeInstruction call,
|
|
Map<CGNode, OrdinalSet<PointerKey>> loc) {
|
|
BitVectorIntSet bv = new BitVectorIntSet();
|
|
for (CGNode t : cg.getPossibleTargets(n, call.getCallSite())) {
|
|
bv.addAll(loc.get(t).getBackingSet());
|
|
}
|
|
return new OrdinalSet<PointerKey>(bv, loc.get(n).getMapping());
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
populate();
|
|
StringBuffer result = new StringBuffer("PDG for " + node + ":\n");
|
|
result.append(super.toString());
|
|
return result.toString();
|
|
}
|
|
|
|
public Statement[] getParamCalleeStatements() {
|
|
if (paramCalleeStatements == null) {
|
|
createCalleeParams();
|
|
}
|
|
Statement[] result = new Statement[paramCalleeStatements.length];
|
|
System.arraycopy(paramCalleeStatements, 0, result, 0, result.length);
|
|
return result;
|
|
}
|
|
|
|
public Statement[] getReturnStatements() {
|
|
populate();
|
|
Statement[] result = new Statement[returnStatements.length];
|
|
System.arraycopy(returnStatements, 0, result, 0, result.length);
|
|
return result;
|
|
}
|
|
|
|
public CGNode getCallGraphNode() {
|
|
return node;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (obj == null) {
|
|
return false;
|
|
}
|
|
if (getClass().equals(obj.getClass())) {
|
|
return node.equals(((PDG) obj).node);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return 103 * node.hashCode();
|
|
}
|
|
|
|
@Override
|
|
public int getPredNodeCount(Statement N) throws UnimplementedError {
|
|
populate();
|
|
Assertions.UNREACHABLE();
|
|
return delegate.getPredNodeCount(N);
|
|
}
|
|
|
|
@Override
|
|
public Iterator<Statement> getPredNodes(Statement N) {
|
|
populate();
|
|
if (!dOptions.isIgnoreHeap()) {
|
|
computeIncomingHeapDependencies(N);
|
|
}
|
|
return delegate.getPredNodes(N);
|
|
}
|
|
|
|
private void computeIncomingHeapDependencies(Statement N) {
|
|
switch (N.getKind()) {
|
|
case NORMAL:
|
|
NormalStatement st = (NormalStatement) N;
|
|
if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) {
|
|
Collection<PointerKey> ref = modRef.getRef(node, heapModel, pa, st.getInstruction(), exclusions);
|
|
for (PointerKey pk : ref) {
|
|
createHeapDataDependenceEdges(pk);
|
|
}
|
|
}
|
|
break;
|
|
case HEAP_PARAM_CALLEE:
|
|
case HEAP_PARAM_CALLER:
|
|
case HEAP_RET_CALLEE:
|
|
case HEAP_RET_CALLER:
|
|
HeapStatement h = (HeapStatement) N;
|
|
createHeapDataDependenceEdges(h.getLocation());
|
|
}
|
|
}
|
|
|
|
private void computeOutgoingHeapDependencies(Statement N) {
|
|
switch (N.getKind()) {
|
|
case NORMAL:
|
|
NormalStatement st = (NormalStatement) N;
|
|
if (!(ignoreAllocHeapDefs && st.getInstruction() instanceof SSANewInstruction)) {
|
|
Collection<PointerKey> mod = modRef.getMod(node, heapModel, pa, st.getInstruction(), exclusions);
|
|
for (PointerKey pk : mod) {
|
|
createHeapDataDependenceEdges(pk);
|
|
}
|
|
}
|
|
break;
|
|
case HEAP_PARAM_CALLEE:
|
|
case HEAP_PARAM_CALLER:
|
|
case HEAP_RET_CALLEE:
|
|
case HEAP_RET_CALLER:
|
|
HeapStatement h = (HeapStatement) N;
|
|
createHeapDataDependenceEdges(h.getLocation());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getSuccNodeCount(Statement N) throws UnimplementedError {
|
|
populate();
|
|
Assertions.UNREACHABLE();
|
|
return delegate.getSuccNodeCount(N);
|
|
}
|
|
|
|
@Override
|
|
public Iterator<Statement> getSuccNodes(Statement N) {
|
|
populate();
|
|
if (!dOptions.isIgnoreHeap()) {
|
|
computeOutgoingHeapDependencies(N);
|
|
}
|
|
return delegate.getSuccNodes(N);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasEdge(Statement src, Statement dst) throws UnimplementedError {
|
|
populate();
|
|
return delegate.hasEdge(src, dst);
|
|
}
|
|
|
|
@Override
|
|
public void removeNodeAndEdges(Statement N) throws UnsupportedOperationException {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public void addNode(Statement n) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public boolean containsNode(Statement N) {
|
|
populate();
|
|
return delegate.containsNode(N);
|
|
}
|
|
|
|
@Override
|
|
public int getNumberOfNodes() {
|
|
populate();
|
|
return delegate.getNumberOfNodes();
|
|
}
|
|
|
|
@Override
|
|
public Iterator<Statement> iterator() {
|
|
populate();
|
|
return delegate.iterator();
|
|
}
|
|
|
|
@Override
|
|
public void removeNode(Statement n) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public void addEdge(Statement src, Statement dst) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public void removeAllIncidentEdges(Statement node) throws UnsupportedOperationException {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public void removeEdge(Statement src, Statement dst) throws UnsupportedOperationException {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public void removeIncomingEdges(Statement node) throws UnsupportedOperationException {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public void removeOutgoingEdges(Statement node) throws UnsupportedOperationException {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
@Override
|
|
public int getMaxNumber() {
|
|
populate();
|
|
return delegate.getMaxNumber();
|
|
}
|
|
|
|
@Override
|
|
public Statement getNode(int number) {
|
|
populate();
|
|
return delegate.getNode(number);
|
|
}
|
|
|
|
@Override
|
|
public int getNumber(Statement N) {
|
|
populate();
|
|
return delegate.getNumber(N);
|
|
}
|
|
|
|
@Override
|
|
public Iterator<Statement> iterateNodes(IntSet s) {
|
|
Assertions.UNREACHABLE();
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public IntSet getPredNodeNumbers(Statement node) {
|
|
Assertions.UNREACHABLE();
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public IntSet getSuccNodeNumbers(Statement node) {
|
|
Assertions.UNREACHABLE();
|
|
return null;
|
|
}
|
|
/** BEGIN Custom change: control deps */
|
|
public boolean isControlDependend(Statement from, Statement to) {
|
|
return delegate.hasEdge(from, to, Dependency.CONTROL_DEP);
|
|
}
|
|
/** END Custom change: control deps */
|
|
}
|