com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis Maven / Gradle / Ivy
package com.ibm.wala.analysis.arraybounds;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.algorithms.ShortestPath;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.NormalOrder;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.ReverseOrder;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.core.util.ssa.InstructionByIIndexMap;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* The array out of bounds analysis uses the inequality graph as described in [1]. And a shortest
* path computation as suggested ibid. as possible solver for the inequality graph.
*
* [1] Bodík, Rastislav, Rajiv Gupta, and Vivek Sarkar. "ABCD: eliminating array bounds checks on
* demand." ACM SIGPLAN Notices. Vol. 35. No. 5. ACM, 2000.
*
* @author Stephan Gocht {@code }
*/
public class ArrayOutOfBoundsAnalysis {
public enum UnnecessaryCheck {
NONE,
UPPER,
LOWER,
BOTH;
public UnnecessaryCheck union(UnnecessaryCheck other) {
final Set set = new HashSet<>();
set.add(this);
set.add(other);
set.remove(NONE);
if (set.contains(BOTH) || (set.contains(UPPER) && set.contains(LOWER))) {
return BOTH;
} else {
switch (set.size()) {
case 0:
return NONE;
case 1:
return set.iterator().next();
default:
throw new RuntimeException(
"Case that should not happen, this method is implemented wrong.");
}
}
}
}
private ArrayBoundsGraph lowerBoundGraph;
private ArrayBoundsGraph upperBoundGraph;
/** List of variables, that are used for array access and if they are neccessary */
private final Map boundsCheckUnnecessary;
/**
* Create and perform the array out of bounds analysis.
*
* Make sure, the given IR was created with pi nodes for each variable, that is part of a
* branch instruction! Otherwise the results will be poor.
*/
public ArrayOutOfBoundsAnalysis(IR ir) {
this.boundsCheckUnnecessary = new InstructionByIIndexMap<>();
this.buildInequalityGraphs(ir);
this.computeLowerBound();
this.computeUpperBounds();
this.lowerBoundGraph = null;
this.upperBoundGraph = null;
}
private void addUnnecessaryCheck(
SSAArrayReferenceInstruction instruction, UnnecessaryCheck checkToAdd) {
final UnnecessaryCheck oldCheck = this.boundsCheckUnnecessary.get(instruction);
final UnnecessaryCheck newCheck = oldCheck.union(checkToAdd);
this.boundsCheckUnnecessary.put(instruction, newCheck);
}
private void buildInequalityGraphs(IR ir) {
ArrayBoundsGraphBuilder builder = new ArrayBoundsGraphBuilder(ir);
this.lowerBoundGraph = builder.getLowerBoundGraph();
this.upperBoundGraph = builder.getUpperBoundGraph();
for (final SSAArrayReferenceInstruction instruction : builder.getArrayReferenceInstructions()) {
this.boundsCheckUnnecessary.put(instruction, UnnecessaryCheck.NONE);
}
builder = null;
}
/** compute lower bound */
private void computeLowerBound() {
final HyperNode zero = this.lowerBoundGraph.getNodes().get(ArrayBoundsGraph.ZERO);
ShortestPath.compute(this.lowerBoundGraph, zero, new NormalOrder());
for (final SSAArrayReferenceInstruction instruction : this.boundsCheckUnnecessary.keySet()) {
Weight weight = this.lowerBoundGraph.getVariableWeight(instruction.getIndex());
if (weight.getType() == Weight.Type.NUMBER && weight.getNumber() >= 0) {
this.addUnnecessaryCheck(instruction, UnnecessaryCheck.LOWER);
}
}
}
/** compute upper bound for each array */
private void computeUpperBounds() {
final Map arrayLengths = this.upperBoundGraph.getArrayLength();
for (final Map.Entry entry : arrayLengths.entrySet()) {
final HyperNode arrayNode = this.upperBoundGraph.getNodes().get(entry.getValue());
ShortestPath.compute(this.upperBoundGraph, arrayNode, new ReverseOrder());
for (final SSAArrayReferenceInstruction instruction : this.boundsCheckUnnecessary.keySet()) {
if (instruction.getArrayRef() == entry.getKey()) {
Weight weight = this.upperBoundGraph.getVariableWeight(instruction.getIndex());
if (weight.getType() == Weight.Type.NUMBER && weight.getNumber() <= -1) {
this.addUnnecessaryCheck(instruction, UnnecessaryCheck.UPPER);
}
}
}
}
}
/**
* @return for each array reference instruction (load or store), if both, lower bound, upper bound
* or no check is unnecessary.
*/
public Map getBoundsCheckNecessary() {
return this.boundsCheckUnnecessary;
}
}