336 lines
11 KiB
Java
336 lines
11 KiB
Java
/******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*****************************************************************************/
|
|
package com.ibm.wala.cast.ir.ssa.analysis;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
|
import com.ibm.wala.dataflow.graph.AbstractMeetOperator;
|
|
import com.ibm.wala.dataflow.graph.BitVectorSolver;
|
|
import com.ibm.wala.dataflow.graph.BitVectorUnion;
|
|
import com.ibm.wala.dataflow.graph.IKilldallFramework;
|
|
import com.ibm.wala.dataflow.graph.ITransferFunctionProvider;
|
|
import com.ibm.wala.fixpoint.BitVectorVariable;
|
|
import com.ibm.wala.fixpoint.UnaryOperator;
|
|
import com.ibm.wala.ssa.IR;
|
|
import com.ibm.wala.ssa.ISSABasicBlock;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSAPhiInstruction;
|
|
import com.ibm.wala.ssa.SymbolTable;
|
|
import com.ibm.wala.util.CancelException;
|
|
import com.ibm.wala.util.CancelRuntimeException;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.graph.Graph;
|
|
import com.ibm.wala.util.graph.impl.GraphInverter;
|
|
import com.ibm.wala.util.intset.BitVector;
|
|
import com.ibm.wala.util.intset.BitVectorIntSet;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
|
|
/**
|
|
* @author Julian Dolby
|
|
*
|
|
* Live-value analysis for a method's IR (or {@link ControlFlowGraph} and {@link SymbolTable}) using a {@link IKilldallFramework}
|
|
* based implementation.
|
|
*
|
|
* Pre-requisites
|
|
* - Knowledge of SSA form: control flow graphs, basic blocks, Phi instructions
|
|
* - Knowledge of data flow analysis theory: see http://en.wikipedia.org/wiki/Data_flow_analysis
|
|
*
|
|
* Implementation notes:
|
|
*
|
|
* - The solver uses node transfer functions only.
|
|
* - Performance: inverts the CFG to traverse backwards (backward analysis).
|
|
*/
|
|
public class LiveAnalysis {
|
|
|
|
public interface Result {
|
|
boolean isLiveEntry(ISSABasicBlock bb, int valueNumber);
|
|
|
|
boolean isLiveExit(ISSABasicBlock bb, int valueNumber);
|
|
|
|
BitVector getLiveBefore(int instr);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public static Result perform(IR ir) {
|
|
return perform(ir.getControlFlowGraph(), ir.getSymbolTable());
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public static Result perform(final ControlFlowGraph<SSAInstruction, ISSABasicBlock> cfg, final SymbolTable symtab) {
|
|
return perform(cfg, symtab, new BitVector());
|
|
}
|
|
|
|
/**
|
|
* @param considerLiveAtExit given set (of variables) to consider to be live after the exit.
|
|
*
|
|
* todo: used once in {@link com.ibm.wala.cast.ir.ssa.SSAConversion}; Explain better the purpose.
|
|
*/
|
|
public static Result perform(final ControlFlowGraph<SSAInstruction, ISSABasicBlock> cfg, final SymbolTable symtab, final BitVector considerLiveAtExit) {
|
|
final BitVectorIntSet liveAtExit = new BitVectorIntSet(considerLiveAtExit);
|
|
final SSAInstruction[] instructions = cfg.getInstructions();
|
|
|
|
/**
|
|
* Gen/kill operator specific to exit basic blocks
|
|
*/
|
|
final class ExitBlockGenKillOperator extends UnaryOperator<BitVectorVariable> {
|
|
@Override
|
|
public String toString() {
|
|
return "ExitGenKill";
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return o == this;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return 37721;
|
|
}
|
|
|
|
/**
|
|
* Evaluate the transfer between two nodes in the flow graph within an exit block.
|
|
*/
|
|
@Override
|
|
public byte evaluate(BitVectorVariable lhs, BitVectorVariable rhs) {
|
|
boolean changed = lhs.getValue() == null ? !considerLiveAtExit.isZero() : !lhs.getValue().sameValue(liveAtExit);
|
|
|
|
lhs.addAll(considerLiveAtExit);
|
|
|
|
return changed ? CHANGED : NOT_CHANGED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gen/kill operator for a regular basic block.
|
|
*/
|
|
final class BlockValueGenKillOperator extends UnaryOperator<BitVectorVariable> {
|
|
private final ISSABasicBlock block;
|
|
|
|
BlockValueGenKillOperator(ISSABasicBlock block) {
|
|
this.block = block;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "GenKill:" + block;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return (o instanceof BlockValueGenKillOperator) && ((BlockValueGenKillOperator) o).block.equals(block);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return block.hashCode() * 17;
|
|
}
|
|
|
|
/**
|
|
* Kills the definitions (variables written to).
|
|
*/
|
|
private void processDefs(SSAInstruction inst, BitVector bits) {
|
|
for (int j = 0; j < inst.getNumberOfDefs(); j++) {
|
|
bits.clear(inst.getDef(j));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates variables that are read (skips constants).
|
|
*/
|
|
private void processUses(SSAInstruction inst, BitVector bits) {
|
|
for (int j = 0; j < inst.getNumberOfUses(); j++) {
|
|
assert inst.getUse(j) != -1 : inst.toString();
|
|
if (!symtab.isConstant(inst.getUse(j))) {
|
|
bits.set(inst.getUse(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Evaluate the transfer between two nodes in the flow graph within one basic block.
|
|
*/
|
|
@Override
|
|
public byte evaluate(BitVectorVariable lhs, BitVectorVariable rhs) {
|
|
// Calculate here the result of the transfer
|
|
BitVectorIntSet bits = new BitVectorIntSet();
|
|
|
|
IntSet s = rhs.getValue();
|
|
if (s != null) {
|
|
bits.addAll(s);
|
|
}
|
|
// Include all uses generated by the current basic block into the successor's Phi instructions todo: rephrase
|
|
for (Iterator<? extends ISSABasicBlock> succBBs = cfg.getSuccNodes(block); succBBs.hasNext();) {
|
|
ISSABasicBlock succBB = succBBs.next();
|
|
|
|
int rval = com.ibm.wala.cast.ir.cfg.Util.whichPred(cfg, succBB, block);
|
|
for (Iterator<SSAPhiInstruction> sphis = succBB.iteratePhis(); sphis.hasNext();) {
|
|
bits.add(sphis.next().getUse(rval));
|
|
}
|
|
}
|
|
// For all instructions, in reverse order, 'kill' variables written to and 'gen' variables read.
|
|
for (int i = block.getLastInstructionIndex(); i >= block.getFirstInstructionIndex(); i--) {
|
|
SSAInstruction inst = instructions[i];
|
|
if (inst != null) {
|
|
processDefs(inst, bits.getBitVector());
|
|
processUses(inst, bits.getBitVector());
|
|
}
|
|
}
|
|
// 'kill' the variables defined by the Phi instructions in the current block.
|
|
for (Iterator<? extends SSAInstruction> SS = block.iteratePhis(); SS.hasNext();) {
|
|
processDefs(SS.next(), bits.getBitVector());
|
|
}
|
|
|
|
BitVectorVariable U = new BitVectorVariable();
|
|
U.addAll(bits.getBitVector());
|
|
|
|
if (!lhs.sameValue(U)) {
|
|
lhs.copyState(U);
|
|
return CHANGED;
|
|
} else {
|
|
return NOT_CHANGED;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create the solver
|
|
*/
|
|
final BitVectorSolver<ISSABasicBlock> S = new BitVectorSolver<>(new IKilldallFramework<ISSABasicBlock, BitVectorVariable>() {
|
|
private final Graph<ISSABasicBlock> G = GraphInverter.invert(cfg);
|
|
|
|
@Override
|
|
public Graph<ISSABasicBlock> getFlowGraph() {
|
|
return G;
|
|
}
|
|
|
|
@Override
|
|
public ITransferFunctionProvider<ISSABasicBlock, BitVectorVariable> getTransferFunctionProvider() {
|
|
return new ITransferFunctionProvider<ISSABasicBlock, BitVectorVariable>() {
|
|
|
|
@Override
|
|
public boolean hasNodeTransferFunctions() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasEdgeTransferFunctions() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Create the specialized operator for regular and exit basic blocks.
|
|
*/
|
|
@Override
|
|
public UnaryOperator<BitVectorVariable> getNodeTransferFunction(ISSABasicBlock node) {
|
|
if (node.isExitBlock()) {
|
|
return new ExitBlockGenKillOperator();
|
|
} else {
|
|
return new BlockValueGenKillOperator(node);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public UnaryOperator<BitVectorVariable> getEdgeTransferFunction(ISSABasicBlock s, ISSABasicBlock d) {
|
|
Assertions.UNREACHABLE();
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Live analysis uses 'union' as 'meet operator'
|
|
*/
|
|
@Override
|
|
public AbstractMeetOperator<BitVectorVariable> getMeetOperator() {
|
|
return BitVectorUnion.instance();
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Solve the analysis problem
|
|
*/
|
|
try {
|
|
S.solve(null);
|
|
} catch (CancelException e) {
|
|
throw new CancelRuntimeException(e);
|
|
}
|
|
|
|
/**
|
|
* Prepare the lazy result with a closure.
|
|
*/
|
|
return new Result() {
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuffer s = new StringBuffer();
|
|
for (int i = 0; i < cfg.getNumberOfNodes(); i++) {
|
|
ISSABasicBlock bb = cfg.getNode(i);
|
|
s.append("live entering ").append(bb).append(":").append(S.getOut(bb)).append("\n");
|
|
s.append("live exiting ").append(bb).append(":").append(S.getIn(bb)).append("\n");
|
|
}
|
|
|
|
return s.toString();
|
|
}
|
|
|
|
@Override
|
|
public boolean isLiveEntry(ISSABasicBlock bb, int valueNumber) {
|
|
return S.getOut(bb).get(valueNumber);
|
|
}
|
|
|
|
@Override
|
|
public boolean isLiveExit(ISSABasicBlock bb, int valueNumber) {
|
|
return S.getIn(bb).get(valueNumber);
|
|
}
|
|
|
|
/**
|
|
* Calculate set of variables live before instruction {@code instr}.
|
|
*
|
|
* @see <a href="http://en.wikipedia.org/wiki/Data_flow_analysis#Backward_Analysis">
|
|
* how the "in" and "out" variable sets work</a>
|
|
*/
|
|
@Override
|
|
public BitVector getLiveBefore(int instr) {
|
|
ISSABasicBlock bb = cfg.getBlockForInstruction(instr);
|
|
|
|
// Start with the variables live at the 'in' of the basic block of the instruction. todo???
|
|
BitVectorIntSet bits = new BitVectorIntSet();
|
|
IntSet s = S.getIn(bb).getValue();
|
|
if (s != null) {
|
|
bits.addAll(s);
|
|
}
|
|
// For all instructions in the basic block, going backwards, from the last,
|
|
// up to the desired instruction, 'kill' written variables and 'gen' read variables.
|
|
for (int i = bb.getLastInstructionIndex(); i >= instr; i--) {
|
|
SSAInstruction inst = instructions[i];
|
|
if (inst != null) {
|
|
for (int j = 0; j < inst.getNumberOfDefs(); j++) {
|
|
bits.remove(inst.getDef(j));
|
|
}
|
|
for (int j = 0; j < inst.getNumberOfUses(); j++) {
|
|
if (!symtab.isConstant(inst.getUse(j))) {
|
|
bits.add(inst.getUse(j));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bits.getBitVector();
|
|
}
|
|
};
|
|
}
|
|
}
|