Mirror of the BLOAT repository https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bloat/src/EDU/purdue/cs/bloat/codegen/RegisterAllocator.java

657 lines
17 KiB

/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.codegen;
import java.util.*;
import EDU.purdue.cs.bloat.cfg.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.tree.*;
import EDU.purdue.cs.bloat.util.*;
/**
* RegisterAllocator performs analysis on a control flow graph and determines
* the minimum amount of local variables needed in a method.
*
* @see LocalVariable
*/
// Note that RegisterAllocator uses a different IGNode from Liveness!
public class RegisterAllocator {
FlowGraph cfg;
Liveness liveness;
Map colors;
int colorsUsed;
final static float MAX_WEIGHT = Float.MAX_VALUE;
final static float LOOP_FACTOR = 10.0F;
final static int MAX_DEPTH = (int) (Math.log(RegisterAllocator.MAX_WEIGHT) / Math
.log(RegisterAllocator.LOOP_FACTOR));
/**
* Constructor. Builds an interference graph based on the expression nodes
* found in liveness. Traverses the graph and determines which nodes needs
* to be precolored and which nodes can be coalesced (move statements).
* Nodes are coalesced and local variables are assigned to expressions.
*
* @see FlowGraph
* @see LocalVariable
*/
public RegisterAllocator(final FlowGraph cfg, final Liveness liveness) {
this.cfg = cfg;
this.liveness = liveness;
colorsUsed = 0;
colors = new HashMap();
// Construct the interference graph.
final Graph ig = new Graph();
Iterator iter = liveness.defs().iterator();
while (iter.hasNext()) {
final VarExpr def = (VarExpr) iter.next();
if (!(def instanceof LocalExpr)) {
// Ignore node in the Liveness IG that are not LocalExprs
continue;
}
// Create a new node in the IG, if one does not already exist
IGNode defNode = (IGNode) ig.getNode(def);
if (defNode == null) {
defNode = new IGNode((LocalExpr) def);
ig.addNode(def, defNode);
}
// Examine each variable that interferes with def
final Iterator intersections = liveness.intersections(def);
while (intersections.hasNext()) {
final VarExpr expr = (VarExpr) intersections.next();
if (expr == def) {
// If for some reason, def interferes with itself, ignore it
continue;
}
// Add an edge in RegisterAllocator's IG between the variables
// that interfere
if (expr instanceof LocalExpr) {
IGNode node = (IGNode) ig.getNode(expr);
if (node == null) {
node = new IGNode((LocalExpr) expr);
ig.addNode(expr, node);
}
ig.addEdge(defNode, node);
ig.addEdge(node, defNode);
}
}
}
// Arrays of expressions that invovle a copy of one local variable
// to another. Expressions invovled in copies (i.e. "moves") can
// be coalesced into one expression.
final ArrayList copies = new ArrayList();
// Nodes that are the targets of InitStmt are considered to be
// precolored.
final ArrayList precolor = new ArrayList();
cfg.visit(new TreeVisitor() {
public void visitBlock(final Block block) {
// Don't visit the sink block. There's nothing interesting
// there.
if (block != RegisterAllocator.this.cfg.sink()) {
block.visitChildren(this);
}
}
public void visitPhiStmt(final PhiStmt stmt) {
stmt.visitChildren(this);
if (!(stmt.target() instanceof LocalExpr)) {
return;
}
// A PhiStmt invovles an assignment (copy). So note the copy
// between the target and all of the PhiStmt's operands in the
// copies list.
final IGNode lnode = (IGNode) ig.getNode(stmt.target());
final HashSet set = new HashSet();
final Iterator e = stmt.operands().iterator();
while (e.hasNext()) {
final Expr op = (Expr) e.next();
if ((op instanceof LocalExpr) && (op.def() != null)) {
if (!set.contains(op.def())) {
set.add(op.def());
if (op.def() != stmt.target()) {
final IGNode rnode = (IGNode) ig.getNode(op
.def());
copies.add(new IGNode[] { lnode, rnode });
}
}
}
}
}
public void visitStoreExpr(final StoreExpr expr) {
expr.visitChildren(this);
if (!(expr.target() instanceof LocalExpr)) {
return;
}
final IGNode lnode = (IGNode) ig.getNode(expr.target());
if ((expr.expr() instanceof LocalExpr)
&& (expr.expr().def() != null)) {
// A store of a variable into another variable is a copy
final IGNode rnode = (IGNode) ig.getNode(expr.expr().def());
copies.add(new IGNode[] { lnode, rnode });
return;
}
// Treat L := L + k as a copy so that they get converted
// back to iincs.
if (expr.target().type().equals(Type.INTEGER)) {
if (!(expr.expr() instanceof ArithExpr)) {
return;
}
// We're dealing with integer arithmetic. Remember that an
// ArithExpr has a left and a right operand. If one of the
// operands is a variable and if the other is a constant and
// the operation is addition or substraction, we have an
// increment.
final ArithExpr rhs = (ArithExpr) expr.expr();
LocalExpr var = null;
Integer value = null;
if ((rhs.left() instanceof LocalExpr)
&& (rhs.right() instanceof ConstantExpr)) {
var = (LocalExpr) rhs.left();
final ConstantExpr c = (ConstantExpr) rhs.right();
if (c.value() instanceof Integer) {
value = (Integer) c.value();
}
} else if ((rhs.right() instanceof LocalExpr)
&& (rhs.left() instanceof ConstantExpr)) {
var = (LocalExpr) rhs.right();
final ConstantExpr c = (ConstantExpr) rhs.left();
if (c.value() instanceof Integer) {
value = (Integer) c.value();
}
}
if (rhs.operation() == ArithExpr.SUB) {
if (value != null) {
value = new Integer(-value.intValue());
}
} else if (rhs.operation() != ArithExpr.ADD) {
value = null;
}
if ((value != null) && (var.def() != null)) {
final int incr = value.intValue();
if ((short) incr == incr) {
// Only generate an iinc if the increment
// fits in a short
final IGNode rnode = (IGNode) ig.getNode(var.def());
copies.add(new IGNode[] { lnode, rnode });
}
}
}
}
public void visitInitStmt(final InitStmt stmt) {
stmt.visitChildren(this);
// The initialized variables are precolored.
final LocalExpr[] t = stmt.targets();
for (int i = 0; i < t.length; i++) {
precolor.add(t[i]);
}
}
});
// Coalesce move related nodes, maximum weight first.
while (copies.size() > 0) {
// We want the copy (v <- w) with the maximum:
// weight(v) + weight(w)
// ---------------------
// size(union)
// where union is the intersection of the nodes that conflict
// with v and the nodes that conflict with w. This equation
// appears to be in conflict with the one given on page 38 of
// Nate's thesis.
HashSet union; // The union of neighboring nodes
int max = 0;
IGNode[] copy = (IGNode[]) copies.get(max);
float maxWeight = copy[0].weight + copy[1].weight;
union = new HashSet();
union.addAll(ig.succs(copy[0]));
union.addAll(ig.succs(copy[1]));
maxWeight /= union.size();
for (int i = 1; i < copies.size(); i++) {
copy = (IGNode[]) copies.get(i);
float weight = copy[0].weight + copy[1].weight;
union.clear();
union.addAll(ig.succs(copy[0]));
union.addAll(ig.succs(copy[1]));
weight /= union.size();
if (weight > maxWeight) {
// The ith copy has the maximum weight
maxWeight = weight;
max = i;
}
}
// Remove the copy with the max weight from the copies list. He
// does it in a rather round-about way.
copy = (IGNode[]) copies.get(max);
copies.set(max, copies.get(copies.size() - 1));
copies.remove(copies.size() - 1);
if (!ig.hasEdge(copy[0], copy[1])) {
// If the variables involved in the copy do not interfere with
// each other, they are coalesced.
if (CodeGenerator.DEBUG) {
System.out.println("coalescing " + copy[0] + " " + copy[1]);
System.out.println(" 0 conflicts " + ig.succs(copy[0]));
System.out.println(" 1 conflicts " + ig.succs(copy[1]));
}
ig.succs(copy[0]).addAll(ig.succs(copy[1]));
ig.preds(copy[0]).addAll(ig.preds(copy[1]));
copy[0].coalesce(copy[1]);
if (CodeGenerator.DEBUG) {
System.out.println(" coalesced " + copy[0]);
System.out.println(" conflicts " + ig.succs(copy[0]));
}
// Remove coalesced node from the IG
ig.removeNode(copy[1].key);
iter = copies.iterator();
// Examine all copies. If the copy involves the node that was
// coalesced, the copy is no longer interesting. Remove it.
while (iter.hasNext()) {
final IGNode[] c = (IGNode[]) iter.next();
if ((c[0] == copy[1]) || (c[1] == copy[1])) {
iter.remove();
}
}
}
}
// Create a list of uncolored nodes.
final ArrayList uncoloredNodes = new ArrayList();
Iterator nodes = ig.nodes().iterator();
while (nodes.hasNext()) {
final IGNode node = (IGNode) nodes.next();
final ArrayList p = new ArrayList(precolor);
p.retainAll(node.defs);
// See if any node got coalesced with a precolored node.
if (p.size() == 1) {
// Precolored
node.color = ((LocalExpr) p.get(0)).index();
if (CodeGenerator.DEBUG) {
System.out.println("precolored " + node + " " + node.color);
}
} else if (p.size() == 0) {
// Uncolored (i.e. not coalesced with any of the pre-colored
// nodes.
node.color = -1;
uncoloredNodes.add(node);
} else {
// If two or more pre-colored nodes were coalesced, we have a
// problem.
throw new RuntimeException("coalesced pre-colored defs " + p);
}
}
// Sort the uncolored nodes, by decreasing weight. Wide nodes
// have half their original weight since they take up two indices
// and we want to put color nodes with the lower indices.
Collections.sort(uncoloredNodes, new Comparator() {
public int compare(final Object a, final Object b) {
final IGNode na = (IGNode) a;
final IGNode nb = (IGNode) b;
float wa = na.weight / ig.succs(na).size();
float wb = nb.weight / ig.succs(nb).size();
if (na.wide) {
wa /= 2;
}
if (nb.wide) {
wb /= 2;
}
if (wb > wa) {
return 1;
}
if (wb < wa) {
return -1;
}
return 0;
}
});
nodes = uncoloredNodes.iterator();
while (nodes.hasNext()) {
final IGNode node = (IGNode) nodes.next();
if (CodeGenerator.DEBUG) {
System.out.println("coloring " + node);
System.out.println(" conflicts " + ig.succs(node));
}
// Make sure node has not been colored
Assert.isTrue(node.color == -1);
// Determine which colors have been assigned to the nodes
// conflicting with the node of interest
final BitSet used = new BitSet();
final Iterator succs = ig.succs(node).iterator();
while (succs.hasNext()) {
final IGNode succ = (IGNode) succs.next();
if (succ.color != -1) {
used.set(succ.color);
if (succ.wide) {
used.set(succ.color + 1);
}
}
}
// Find the next available color
for (int i = 0; node.color == -1; i++) {
if (!used.get(i)) {
if (node.wide) {
// Wide variables need two colors
if (!used.get(i + 1)) {
node.color = i;
if (CodeGenerator.DEBUG) {
System.out.println(" assigning color " + i
+ " to " + node);
}
if (i + 1 >= colorsUsed) {
colorsUsed = i + 2;
}
}
} else {
node.color = i;
if (CodeGenerator.DEBUG) {
System.out.println(" assigning color " + i
+ " to " + node);
}
if (i >= colorsUsed) {
colorsUsed = i + 1;
}
}
}
}
}
nodes = ig.nodes().iterator();
while (nodes.hasNext()) {
final IGNode node = (IGNode) nodes.next();
// Make sure each node has been colored
Assert.isTrue(node.color != -1, "No color for " + node);
iter = node.defs.iterator();
// Set the index of the variable and all of its uses to be the
// chosen color.
while (iter.hasNext()) {
final LocalExpr def = (LocalExpr) iter.next();
def.setIndex(node.color);
final Iterator uses = def.uses().iterator();
while (uses.hasNext()) {
final LocalExpr use = (LocalExpr) uses.next();
use.setIndex(node.color);
}
}
}
if (CodeGenerator.DEBUG) {
System.out.println("After allocating locals--------------------");
cfg.print(System.out);
System.out.println("End print----------------------------------");
}
}
/**
* Returns the maximum number of local variables used by the cfg after its
* "registers" (local variables) have been allocated.
*/
public int maxLocals() {
return colorsUsed;
}
/**
* Creates a new local variable in this method (as modeled by the cfg).
* Updates the number of local variables appropriately.
*/
public LocalVariable newLocal(final Type type) {
// Why don't we add Type information to the LocalVariable? Are we
// assuming that type checking has already been done and so its a
// moot point?
final LocalVariable var = new LocalVariable(colorsUsed);
colorsUsed += type.stackHeight();
return var;
}
/**
* IGNode is a node in the interference graph. Note that this node is
* different from the one in Liveness. For instance, this one stores
* information about a node's color, its weight, etc. Because nodes may be
* coalesced, an IGNode may represent more than one LocalExpr. That's why
* there is a list of definitions.
*/
class IGNode extends GraphNode {
Set defs;
LocalExpr key;
int color;
boolean wide; // Is the variable wide?
float weight;
public IGNode(final LocalExpr def) {
color = -1;
key = def;
defs = new HashSet();
defs.add(def);
wide = def.type().isWide();
computeWeight();
}
/**
* Coalesce two nodes in the interference graph. The weight of the other
* node is added to that of this node. This node also inherits all of
* the definitions of the other node.
*/
void coalesce(final IGNode node) {
Assert.isTrue(wide == node.wide);
weight += node.weight;
final Iterator iter = node.defs.iterator();
while (iter.hasNext()) {
final LocalExpr def = (LocalExpr) iter.next();
defs.add(def);
}
}
public String toString() {
return "(color=" + color + " weight=" + weight + " "
+ defs.toString() + ")";
}
/**
* Calculates the weight of a Block based on its loop depth. If the
* block does not exceed the MAX_DEPTH, then the weight is LOOP_FACTOR
* raised to the depth.
*/
private float blockWeight(final Block block) {
int depth = cfg.loopDepth(block);
if (depth > RegisterAllocator.MAX_DEPTH) {
return RegisterAllocator.MAX_WEIGHT;
}
float w = 1.0F;
while (depth-- > 0) {
w *= RegisterAllocator.LOOP_FACTOR;
}
return w;
}
/**
* Computes the weight of a node in the interference graph. The weight
* is based on where the variable represented by this node is used. The
* method blockWeight is used to determine the weight of a variable used
* in a block based on the loop depth of the block. Special care must be
* taken if the variable is used in a PhiStmt.
*/
private void computeWeight() {
weight = 0.0F;
final Iterator iter = defs.iterator();
// Look at all(?) of the definitions of the IGNode
while (iter.hasNext()) {
final LocalExpr def = (LocalExpr) iter.next();
weight += blockWeight(def.block());
final Iterator uses = def.uses().iterator();
// If the variable is used as an operand to a PhiJoinStmt,
// find the predacessor block to the PhiJoinStmt in which the
// variable occurs and add the weight of that block to the
// running total weight.
while (uses.hasNext()) {
final LocalExpr use = (LocalExpr) uses.next();
if (use.parent() instanceof PhiJoinStmt) {
final PhiJoinStmt phi = (PhiJoinStmt) use.parent();
final Iterator preds = cfg.preds(phi.block())
.iterator();
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
final Expr op = phi.operandAt(pred);
if (use == op) {
weight += blockWeight(pred);
break;
}
}
} else if (use.parent() instanceof PhiCatchStmt) {
// If the variable is used in a PhiCatchStmt, add the
// weight of the block in which the variable is defined
// to
// the running total.
weight += blockWeight(use.def().block());
} else {
// Just add in the weight of the block in which the
// variable is used.
weight += blockWeight(use.block());
}
}
}
}
}
}