/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/
package kilim.analysis;
import java.util.ArrayList;
import java.util.BitSet;
/**
* Each BasicBlock owns one instance of Usage. This class maintains, in essence, three vectors of
* booleans, indexed by the local variable number. Since it is <i>very</i> rare for a method to have
* more than 31 local variables, the vectors are represented by int bitmaps. For more than this, the
* basic block creates an instance of BigUsage that is functionally identical (TODO)
*
* Note that we don't need to track usage of operand stack. All elements of the operand stack are
* always live, and always need to be stored and restored (during stack switching). This is not true
* of local vars; a var may have a valid value which may not be used downstream, so we track which
* vars must be taken seriously.
*
* @see BasicBlock
*/
public class Usage {
/**
* The number of local vars in the owning BB's frame
*/
private int nLocals;
/**
* bit(i) == 1 (counting from LSB) if the ith local var is live downstream
*/
private BitSet in;
/**
* use.bit(i) == 1 (from LSB) if the ith var is read before it has been written. The bit vector
* as a whole represents the set of vars that the BB needs from its predecessors.
*/
private BitSet use;
/**
* def.bit(i) == 1 (from LSB) if the ith var is written into before it has been read. It
* represents all the vars that this BB is capable of supplying downstream on its own, hence
* those vars are not required to be supplied by its predecessors (even if they do supply them,
* they will be overwritten anyway).
*/
private BitSet def;
public Usage(int numLocals) {
nLocals = numLocals;
in = new BitSet(numLocals);
use = new BitSet(numLocals);
def = new BitSet(numLocals);
}
public void read(int var) {
assert var < nLocals : "local var num=" + var + " exceeds nLocals = " + nLocals;
if (!def.get(var)) {
// is not def'd earlier
use.set(var);
}
}
public void write(int var) {
assert var < nLocals : "local var num=" + var + " exceeds nLocals = " + nLocals;
def.set(var);
}
/**
* return true if var is live at the entrance to this BB.
*/
public boolean isLiveIn(int var) {
return in.get(var);
}
/**
* This is the standard liveness calculation (Dragon Book, section 10.6). At each BB (and its
* corresponding usage), we evaluate "in" using use and def. in = use U (out \ def) where out =
* U succ.in, for all successors
*/
public boolean evalLiveIn(ArrayList<Usage> succUsage) {
BitSet out = new BitSet(nLocals);
BitSet old_in = (BitSet) in.clone();
if (succUsage.size() == 0) {
in = use;
} else {
// calculate out = U succ.in
out = (BitSet) succUsage.get(0).in.clone();
for (int i = 1; i < succUsage.size(); i++) {
out.or(succUsage.get(i).in);
}
// calc out \ def == out & ~def == ~(out | def)
BitSet def1 = (BitSet) def.clone();
def1.flip(0, nLocals);
out.and(def1);
out.or(use);
in = out;
}
return !(in.equals(old_in));
}
/**
* Called to coalesce a successor's usage into the current BB. Important: This should be called
* before live variable analysis begins, because we don't bother merging this.in.
*/
void absorb(Usage succ) {
BitSet b = (BitSet) this.def.clone();
b.flip(0, nLocals);
b.and(succ.use);
this.use.or(b);
this.def.or(succ.def);
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("use");
printBits(sb, use);
sb.append("def");
printBits(sb, def);
sb.append("in");
printBits(sb, in);
return sb.toString();
}
private void printBits(StringBuffer sb, BitSet b) {
int numDefined = 0;
for (int i = 0; i < nLocals; i++) {
if (b.get(i))
numDefined++;
}
sb.append('(').append(numDefined).append("): ");
for (int i = 0; i < nLocals; i++) {
if (b.get(i))
sb.append(i).append(' ');
}
sb.append('\n');
}
/**
* This is purely for testing purposes.
*
* @param var
* local var index
*/
public void setLiveIn(int var) {
in.set(var);
}
Usage copy() {
Usage ret = new Usage(nLocals);
ret.use = use;
ret.def = def;
return ret;
}
}