/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o 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 General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package EDU.purdue.cs.bloat.trans;
import java.util.*;
import EDU.purdue.cs.bloat.cfg.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.ssa.*;
import EDU.purdue.cs.bloat.tree.*;
import EDU.purdue.cs.bloat.util.*;
/**
* Eliminate partially redundant local variable loads and stores by replacing
* them with stack variables and dups.
*
* The algorithm is similar to SSAPRE, except:
*
* We need to place phis for locals at the IDF of the blocks containing defs and
* uses (not just defs).
*/
public class StackPRE {
public static boolean DEBUG = false;
protected FlowGraph cfg;
protected List[] varphis;
protected List[] stackvars;
protected Worklist worklist;
int next = 0;
public StackPRE(final FlowGraph cfg) {
this.cfg = cfg;
}
public void transform() {
stackvars = new ArrayList[cfg.size()];
for (int i = 0; i < stackvars.length; i++) {
stackvars[i] = new ArrayList();
}
varphis = new ArrayList[cfg.size()];
for (int i = 0; i < varphis.length; i++) {
varphis[i] = new ArrayList();
}
// Collect local and stack variables into a worklist.
// Mark the variables that are pushed before any are popped.
worklist = new Worklist();
cfg.visit(new TreeVisitor() {
public void visitPhiJoinStmt(final PhiJoinStmt stmt) {
worklist.addVarPhi(stmt);
}
public void visitPhiCatchStmt(final PhiCatchStmt stmt) {
worklist.addLocalVar((LocalExpr) stmt.target());
}
public void visitLocalExpr(final LocalExpr expr) {
worklist.addLocalVar(expr);
}
public void visitStackExpr(final StackExpr expr) {
worklist.addStackVar(expr);
}
});
while (!worklist.isEmpty()) {
final ExprInfo exprInfo = worklist.removeFirst();
if (StackPRE.DEBUG) {
System.out.println("PRE for " + exprInfo.def()
+ " -------------------------");
System.out.println("Placing Phis for " + exprInfo.def()
+ " -------------------------");
}
placePhiFunctions(exprInfo);
if (StackPRE.DEBUG) {
exprInfo.print();
System.out.println("Renaming for " + exprInfo.def()
+ " -------------------------");
}
rename(exprInfo);
if (StackPRE.DEBUG) {
exprInfo.print();
System.out.println("Down safety for " + exprInfo.def()
+ " -------------------------");
}
downSafety(exprInfo);
if (StackPRE.DEBUG) {
System.out.println("Will be available for " + exprInfo.def()
+ " -------------------------");
}
willBeAvail(exprInfo);
if (StackPRE.DEBUG) {
System.out.println("Finalize for " + exprInfo.def()
+ " -------------------------");
}
finalize(exprInfo);
if (StackPRE.DEBUG) {
System.out.println("Code motion for " + exprInfo.def()
+ " -------------------------");
}
final Type type = exprInfo.def().type();
final StackExpr tmp = new StackExpr(0, type);
final SSAConstructionInfo consInfo = new SSAConstructionInfo(cfg,
tmp);
codeMotion(exprInfo, tmp, consInfo);
if (StackPRE.DEBUG) {
System.out.println("Performing incremental SSA for "
+ exprInfo.def() + " -------------------------");
}
SSA.transform(cfg, consInfo);
// Get the stack variable phis.
final Collection defBlocks = consInfo.defBlocks();
final Iterator e = cfg.iteratedDomFrontier(defBlocks).iterator();
while (e.hasNext()) {
final Block block = (Block) e.next();
final Iterator stmts = block.tree().stmts().iterator();
while (stmts.hasNext()) {
final Stmt stmt = (Stmt) stmts.next();
if (stmt instanceof PhiJoinStmt) {
worklist.prependVarPhi((PhiJoinStmt) stmt);
} else if (!(stmt instanceof LabelStmt)) {
// Only labels occur before phis. If we hit
// something else, there are no more phis.
break;
}
}
}
if (StackPRE.DEBUG) {
exprInfo.print();
System.out.println("Done with PRE for " + exprInfo.def()
+ " -------------------------");
}
exprInfo.cleanup();
}
varphis = null;
worklist = null;
}
/**
* For an local variable, v, insert a Phi at the iterated dominance frontier
* of the blocks containing defs and uses of v. This differs from SSA phi
* placement in that uses, not just defs are considered in computing the
* IDF.
*/
private void placePhiFunctions(final ExprInfo exprInfo) {
final ArrayList w = new ArrayList(cfg.size());
final Iterator uses = exprInfo.def().uses().iterator();
w.add(exprInfo.def().block());
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(use.block()).iterator();
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
if (phi.operandAt(pred) == use) {
w.add(pred);
break;
}
}
} else if (!(use.parent() instanceof PhiCatchStmt)) {
w.add(use.block());
}
}
final Iterator df = cfg.iteratedDomFrontier(w).iterator();
while (df.hasNext()) {
final Block block = (Block) df.next();
exprInfo.addPhi(block);
}
// Don't bother with placing phis for catch blocks, since the
// operand stack is zeroed at catch blocks.
}
/**
* Set the definition for the variable occurences. After this step all
* occurences of the variable which are at different heights will have
* different definitions.
*/
private void rename(final ExprInfo exprInfo) {
search(cfg.source(), exprInfo, null, 0, false);
}
private void search(final Block block, final ExprInfo exprInfo, Def top,
int totalBalance, boolean seenDef) {
if (StackPRE.DEBUG) {
System.out.println(" renaming in " + block);
}
if (cfg.catchBlocks().contains(block)) {
if (top != null) {
top.setDownSafe(false);
}
top = null;
}
final Phi phi = exprInfo.exprPhiAtBlock(block);
if (phi != null) {
if (top != null) {
top.setDownSafe(false);
}
top = phi;
if (!seenDef) {
top.setDownSafe(false);
}
}
Node parent = null;
int balance = 0;
final Iterator iter = exprInfo.varsAtBlock(block).iterator();
while (iter.hasNext()) {
final VarExpr node = (VarExpr) iter.next();
// Get the parent of the node. If the parent is a putfield
// or array store, then the node is popped when the grandparent
// is evaluated, not when the parent is evaluated.
// We keep track of the parent so that when it changes, we
// know to update the operand stack balance.
Node p = node.parent();
if ((p instanceof MemRefExpr) && ((MemRefExpr) p).isDef()) {
p = p.parent();
}
if (parent != p) {
parent = p;
totalBalance += balance;
balance = 0;
if ((top != null) && (totalBalance < 0)) {
top.setDownSafe(false);
}
}
if (node instanceof StackExpr) {
if (parent instanceof StackManipStmt) {
switch (((StackManipStmt) parent).kind()) {
case StackManipStmt.DUP:
case StackManipStmt.DUP_X1:
case StackManipStmt.DUP_X2:
balance += 1;
break;
case StackManipStmt.DUP2:
case StackManipStmt.DUP2_X1:
case StackManipStmt.DUP2_X2:
balance += 2;
break;
default:
break;
}
} else if (node.isDef()) {
balance += node.type().stackHeight();
} else {
balance -= node.type().stackHeight();
}
} else {
final LocalExpr var = (LocalExpr) node;
if (var.isDef()) {
seenDef = true;
}
if (StackPRE.DEBUG) {
System.out.println("node = " + var + " in " + parent);
}
if ((totalBalance == 0) && onBottom(var, false)) {
// Copy the def from the top of the stack and
// create a new def.
exprInfo.setDef(var, top);
top = new RealDef(var);
if ((balance != 0) || !onBottom(var, true)) {
top.setDownSafe(false);
}
if (StackPRE.DEBUG) {
System.out.println("New def " + top + " with balance "
+ totalBalance + " + " + balance);
}
} else {
// The occurence is not on the bottom, so it
// must be reloaded from a local.
exprInfo.setDef(var, null);
}
}
if (StackPRE.DEBUG) {
System.out.println("after " + parent + " top = " + top);
}
}
totalBalance += balance;
if ((top != null) && (totalBalance < 0)) {
top.setDownSafe(false);
}
// If we hit the sink node, a def at the top of the stack is not
// down safe.
if ((block == cfg.sink()) || cfg.succs(block).contains(cfg.sink())) {
if (top != null) {
top.setDownSafe(false);
}
}
// First, fill in the operands for the StackPRE phis. Then,
// handle local variable occurences in successor block variable
// phis. We do this after the StackPRE phis since they will
// hoist code above the variable phis.
Iterator succs = cfg.succs(block).iterator();
while (succs.hasNext()) {
final Block succ = (Block) succs.next();
final Phi succPhi = exprInfo.exprPhiAtBlock(succ);
if (succPhi != null) {
succPhi.setOperandAt(block, top);
}
}
succs = cfg.succs(block).iterator();
while (succs.hasNext()) {
final Block succ = (Block) succs.next();
final Iterator phis = varPhisAtBlock(succ).iterator();
while (phis.hasNext()) {
final PhiJoinStmt stmt = (PhiJoinStmt) phis.next();
final Expr operand = stmt.operandAt(block);
if (operand instanceof StackExpr) {
balance += operand.type().stackHeight();
}
if (stmt.target() instanceof StackExpr) {
balance -= stmt.target().type().stackHeight();
if (top != null) {
top.setDownSafe(false);
top = null;
}
}
if ((operand != null) && (operand.def() == exprInfo.def())) {
// Phi operands aren't allowed to define any of the
// locals. This should never happen since none of the
// locals should be dominated by the phi operand,
// but we'll play it safe and set top to null.
exprInfo.setDef((LocalExpr) operand, top);
top = null;
}
if (stmt.target() == exprInfo.def()) {
exprInfo.setDef((LocalExpr) stmt.target(), top);
top = new RealDef((LocalExpr) stmt.target());
}
totalBalance += balance;
if ((top != null) && (totalBalance < 0)) {
top.setDownSafe(false);
}
}
}
final Iterator children = cfg.domChildren(block).iterator();
while (children.hasNext()) {
final Block child = (Block) children.next();
search(child, exprInfo, top, totalBalance, seenDef);
}
}
private boolean onBottom(final LocalExpr var, final boolean really) {
// InitStmts and PhiStmts are always on the bottom.
if ((var.stmt() instanceof InitStmt) || (var.stmt() instanceof PhiStmt)) {
return true;
}
class Bool {
boolean value = true;
}
;
final Bool bottom = new Bool();
var.stmt().visitChildren(new TreeVisitor() {
boolean seen = false;
public void visitExpr(final Expr expr) {
if (StackPRE.DEBUG) {
System.out.println("Checking " + expr + " seen=" + seen
+ " bottom=" + bottom.value);
}
if (!seen) {
expr.visitChildren(this);
}
if (!seen) {
bottom.value = false;
seen = true;
}
if (StackPRE.DEBUG) {
System.out.println("Done with " + expr + " seen=" + seen
+ " bottom=" + bottom.value);
}
}
public void visitLocalExpr(final LocalExpr expr) {
if (StackPRE.DEBUG) {
System.out.println("Checking " + expr + " seen=" + seen
+ " bottom=" + bottom.value);
}
if (!seen) {
if (expr == var) {
seen = true;
} else if (expr.def() != var.def()) {
bottom.value = false;
seen = true;
}
}
if (StackPRE.DEBUG) {
System.out.println("Done with " + expr + " seen=" + seen
+ " bottom=" + bottom.value);
}
}
public void visitStackExpr(final StackExpr expr) {
if (StackPRE.DEBUG) {
System.out.println("Checking " + expr + " seen=" + seen
+ " bottom=" + bottom.value);
}
if (really && !seen) {
bottom.value = false;
seen = true;
}
if (StackPRE.DEBUG) {
System.out.println("Done with " + expr + " seen=" + seen
+ " bottom=" + bottom.value);
}
}
});
return bottom.value;
}
/**
* Mark each def as not down safe if there is a control flow path from that
* Phi along which the expression is not evaluated before exit or being
* altered by refinition of one of the variables of the expression. This can
* happen if:
*
* 1) There is a path to exit along which the Phi target is not used. 2)
* There is a path to exit along which the Phi target is used only as the
* operand of a non-down-safe Phi.
*/
private void downSafety(final ExprInfo exprInfo) {
final Iterator blocks = cfg.nodes().iterator();
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final Phi phi = exprInfo.exprPhiAtBlock(block);
if (phi == null) {
continue;
}
if (StackPRE.DEBUG) {
System.out.println(" down safety for " + phi + " in "
+ block);
}
if (phi.downSafe()) {
if (StackPRE.DEBUG) {
System.out.println(" already down safe");
}
continue;
}
// The phi is not down safe. Make all its operands not
// down safe.
final Iterator e = phi.operands().iterator();
while (e.hasNext()) {
final Def def = (Def) e.next();
if (def != null) {
resetDownSafe(def);
}
}
}
}
private void resetDownSafe(final Def def) {
if (StackPRE.DEBUG) {
System.out.println(" reset down safe for " + def);
}
if (def instanceof Phi) {
final Phi phi = (Phi) def;
if (phi.downSafe()) {
phi.setDownSafe(false);
final Iterator e = phi.operands().iterator();
while (e.hasNext()) {
final Def operand = (Def) e.next();
if (operand != null) {
resetDownSafe(operand);
}
}
}
} else {
def.setDownSafe(false);
}
}
/**
* Predict whether the expression will be available at each Phi result
* following insertions for PRE.
*/
private void willBeAvail(final ExprInfo exprInfo) {
computeCanBeAvail(exprInfo);
computeLater(exprInfo);
}
private void computeCanBeAvail(final ExprInfo exprInfo) {
final Iterator blocks = cfg.nodes().iterator();
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final Phi phi = exprInfo.exprPhiAtBlock(block);
if (phi == null) {
continue;
}
if (!phi.downSafe() && phi.canBeAvail()) {
resetCanBeAvail(exprInfo, phi);
}
}
}
private void resetCanBeAvail(final ExprInfo exprInfo, final Phi phi) {
phi.setCanBeAvail(false);
final Iterator blocks = cfg.nodes().iterator();
// For each phi whose operand is at
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final Phi other = exprInfo.exprPhiAtBlock(block);
if (other == null) {
continue;
}
final Iterator e = cfg.preds(other.block()).iterator();
while (e.hasNext()) {
final Block pred = (Block) e.next();
final Def def = other.operandAt(pred);
if (def == phi) {
other.setOperandAt(pred, null);
if (!other.downSafe() && other.canBeAvail()) {
resetCanBeAvail(exprInfo, other);
}
}
}
}
}
private void computeLater(final ExprInfo exprInfo) {
Iterator blocks = cfg.nodes().iterator();
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final Phi phi = exprInfo.exprPhiAtBlock(block);
if (phi != null) {
phi.setLater(phi.canBeAvail());
}
}
blocks = cfg.nodes().iterator();
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final Phi phi = exprInfo.exprPhiAtBlock(block);
if ((phi != null) && phi.later()) {
final Iterator e = phi.operands().iterator();
while (e.hasNext()) {
final Def def = (Def) e.next();
if (def instanceof RealDef) {
resetLater(exprInfo, phi);
break;
}
}
}
}
}
private void resetLater(final ExprInfo exprInfo, final Phi phi) {
phi.setLater(false);
final Iterator blocks = cfg.nodes().iterator();
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
final Phi other = exprInfo.exprPhiAtBlock(block);
if (other == null) {
continue;
}
final Iterator e = other.operands().iterator();
while (e.hasNext()) {
final Def def = (Def) e.next();
if ((def == phi) && other.later()) {
resetLater(exprInfo, other);
break;
}
}
}
}
private void finalize(final ExprInfo exprInfo) {
final Iterator uses = exprInfo.def().uses().iterator();
while (uses.hasNext()) {
final LocalExpr use = (LocalExpr) uses.next();
if (use.parent() instanceof PhiCatchStmt) {
exprInfo.setSave(true);
break;
}
}
finalizeVisit(exprInfo, cfg.source());
}
private void finalizeVisit(final ExprInfo exprInfo, final Block block) {
if (StackPRE.DEBUG) {
System.out.println(" finalizing " + block);
}
// First finalize normal occurences of the local.
final Iterator reals = exprInfo.varsAtBlock(block).iterator();
while (reals.hasNext()) {
final VarExpr node = (VarExpr) reals.next();
if (node instanceof LocalExpr) {
final LocalExpr real = (LocalExpr) node;
if (StackPRE.DEBUG) {
System.out.println(" -----------");
}
final Def def = exprInfo.def(real);
if ((def != null) && def.downSafe()) {
// We can reload from a stack variable, unless the we
// can't safely push the phi operands.
if (def instanceof Phi) {
if (((Phi) def).willBeAvail()) {
exprInfo.setPop(real, true);
} else {
exprInfo.setSave(true);
}
} else {
exprInfo.setPush(((RealDef) def).var, true);
exprInfo.setPop(real, true);
}
} else {
// The real is not on the bottom. We must reload from a
// local variable.
if (real != exprInfo.def()) {
exprInfo.setSave(true);
}
}
}
}
// Next, handle code motion.
Iterator succs = cfg.succs(block).iterator();
while (succs.hasNext()) {
final Block succ = (Block) succs.next();
final Phi succPhi = exprInfo.exprPhiAtBlock(succ);
if ((succPhi != null) && succPhi.willBeAvail()) {
if (succPhi.insert(block)) {
succPhi.setPushOperand(block, true);
} else {
final Def def = succPhi.operandAt(block);
if (def instanceof RealDef) {
Assert.isTrue(def.downSafe(), succPhi + " operand for "
+ block + " is not DS: " + def);
exprInfo.setPush(((RealDef) def).var, true);
} else {
Assert.isTrue(def instanceof Phi, succPhi
+ " operand for " + block + " is not a phi: "
+ def);
Assert.isTrue(((Phi) def).willBeAvail(), succPhi
+ " operand for " + block + " is not WBA: "
+ def);
}
}
}
}
// Lastly, finalize occurences in variable phis. We do this
// after the StackPRE hoisting since the hoisted code will
// occur before the phis.
succs = cfg.succs(block).iterator();
while (succs.hasNext()) {
final Block succ = (Block) succs.next();
final Iterator phis = varPhisAtBlock(succ).iterator();
while (phis.hasNext()) {
final PhiJoinStmt stmt = (PhiJoinStmt) phis.next();
final Expr operand = stmt.operandAt(block);
if ((operand != null) && (operand.def() == exprInfo.def())) {
final LocalExpr var = (LocalExpr) operand;
final Def def = exprInfo.def(var);
if ((def != null) && def.downSafe()) {
// We can reload from a stack variable, unless the we
// can't safely push the phi operands.
if (def instanceof Phi) {
if (((Phi) def).willBeAvail()) {
exprInfo.setPop(var, true);
} else {
exprInfo.setSave(true);
}
} else {
exprInfo.setPush(((RealDef) def).var, true);
exprInfo.setPop(var, true);
}
}
}
}
}
final Iterator children = cfg.domChildren(block).iterator();
while (children.hasNext()) {
final Block child = (Block) children.next();
finalizeVisit(exprInfo, child);
}
}
private void codeMotion(final ExprInfo exprInfo, final StackExpr tmp,
final SSAConstructionInfo consInfo) {
// Be sure to visit pre-order so at least one predecessor is visited
// before each block.
final Iterator blocks = cfg.preOrder().iterator();
while (blocks.hasNext()) {
final Block block = (Block) blocks.next();
if ((block == cfg.source()) || (block == cfg.sink())) {
continue;
}
boolean added = false;
final Iterator reals = exprInfo.varsAtBlock(block).iterator();
while (reals.hasNext()) {
final VarExpr node = (VarExpr) reals.next();
if (node instanceof LocalExpr) {
final LocalExpr var = (LocalExpr) node;
// If marked push, save it to a stack variable.
// If marked pop, reload from a stack variable.
final boolean push = exprInfo.push(var);
boolean pop = exprInfo.pop(var);
if (var.isDef() && exprInfo.save()) {
pop = false;
}
if (push && pop) {
Assert.isTrue(var != exprInfo.def());
final StackExpr t1 = (StackExpr) tmp.clone();
final StackExpr t2 = (StackExpr) tmp.clone();
final StoreExpr store = new StoreExpr(t1, t2, t2.type());
var.replaceWith(store);
consInfo.addReal(t2);
consInfo.addReal(t1);
added = true;
} else if (push) {
final StackExpr t1 = (StackExpr) tmp.clone();
final LocalExpr t2 = (LocalExpr) var.clone();
t2.setDef(exprInfo.def());
final StoreExpr store = new StoreExpr(t1, t2, t2.type());
if (var != exprInfo.def()) {
var.replaceWith(store);
} else {
final Node parent = var.parent();
if (parent instanceof Stmt) {
// InitStmt or PhiStmt.
final Stmt stmt = new ExprStmt(store);
block.tree().addStmtAfter(stmt, (Stmt) parent);
} else {
// a := E -> a := (S := E)
Assert.isTrue(parent instanceof StoreExpr);
final Expr rhs = ((StoreExpr) parent).expr();
parent.visit(new ReplaceVisitor(rhs, store));
store.visit(new ReplaceVisitor(t2, rhs));
t2.cleanup();
}
}
consInfo.addReal(t1);
added = true;
} else if (pop) {
final StackExpr t1 = (StackExpr) tmp.clone();
var.replaceWith(t1);
consInfo.addReal(t1);
added = true;
}
}
}
final List s = stackvars[cfg.preOrderIndex(block)];
if (added) {
s.clear();
block.tree().visitChildren(new TreeVisitor() {
public void visitStackExpr(final StackExpr expr) {
s.add(expr);
}
});
}
Iterator succs = cfg.succs(block).iterator();
while (succs.hasNext()) {
final Block succ = (Block) succs.next();
final Phi succPhi = exprInfo.exprPhiAtBlock(succ);
if ((succPhi != null) && succPhi.pushOperand(block)) {
final StackExpr t1 = (StackExpr) tmp.clone();
final LocalExpr t2 = (LocalExpr) exprInfo.def().clone();
t2.setDef(exprInfo.def());
final StoreExpr store = new StoreExpr(t1, t2, t1.type());
block.tree().addStmtBeforeJump(new ExprStmt(store));
s.add(t1);
consInfo.addReal(t1);
if (StackPRE.DEBUG) {
System.out.println("insert at end of " + block + ": "
+ store);
}
}
}
succs = cfg.succs(block).iterator();
while (succs.hasNext()) {
final Block succ = (Block) succs.next();
final Iterator phis = varPhisAtBlock(succ).iterator();
while (phis.hasNext()) {
final PhiJoinStmt stmt = (PhiJoinStmt) phis.next();
final Expr operand = stmt.operandAt(block);
if ((operand != null) && (operand.def() == exprInfo.def())) {
final LocalExpr var = (LocalExpr) operand;
Assert.isFalse(exprInfo.push(var));
if (exprInfo.pop(var)) {
final StackExpr t1 = (StackExpr) tmp.clone();
var.replaceWith(t1);
consInfo.addReal(t1);
}
}
}
}
}
}
abstract class Def {
int version;
boolean downSafe;
public Def() {
this.version = next++;
this.downSafe = true;
}
public void setDownSafe(final boolean flag) {
if (StackPRE.DEBUG) {
System.out.println(this + " DS = " + flag);
}
downSafe = flag;
}
public boolean downSafe() {
return downSafe;
}
}
class RealDef extends Def {
LocalExpr var;
public RealDef(final LocalExpr var) {
this.var = var;
if (StackPRE.DEBUG) {
System.out
.println("new def for " + var + " in " + var.parent());
}
}
public LocalExpr var() {
return var;
}
public String toString() {
return var.toString() + "{" + version + ","
+ (downSafe() ? "" : "!") + "DS}";
}
}
class Phi extends Def {
Block block;
HashMap operands;
HashMap saveOperand;
boolean live;
boolean downSafe;
boolean canBeAvail;
boolean later;
public Phi(final Block block) {
this.block = block;
operands = new HashMap(cfg.preds(block).size() * 2);
saveOperand = new HashMap(cfg.preds(block).size() * 2);
downSafe = true;
canBeAvail = true;
later = true;
}
public Block block() {
return block;
}
public Collection operands() {
return new AbstractCollection() {
public int size() {
return cfg.preds(block).size();
}
public boolean contains(final Object obj) {
if (obj == null) {
return operands.size() != cfg.preds(block).size();
}
return operands.containsValue(obj);
}
public Iterator iterator() {
final Iterator iter = cfg.preds(block).iterator();
return new Iterator() {
public boolean hasNext() {
return iter.hasNext();
}
public Object next() {
final Block block = (Block) iter.next();
return operandAt(block);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
public Def operandAt(final Block block) {
return (Def) operands.get(block);
}
public void setOperandAt(final Block block, final Def def) {
if (def != null) {
operands.put(block, def);
} else {
operands.remove(block);
}
}
public void setPushOperand(final Block block, final boolean flag) {
if (StackPRE.DEBUG) {
System.out.println(" operand " + block + " save=" + flag);
}
saveOperand.put(block, new Boolean(flag));
}
public boolean pushOperand(final Block block) {
final Boolean flag = (Boolean) saveOperand.get(block);
return (flag != null) && flag.booleanValue();
}
public boolean insert(final Block block) {
final Def def = operandAt(block);
if (def == null) {
return true;
}
if (!def.downSafe()) {
return true;
}
if ((def instanceof Phi) && !((Phi) def).willBeAvail()) {
return true;
}
return false;
}
public boolean willBeAvail() {
return canBeAvail && !later;
}
public void setCanBeAvail(final boolean flag) {
if (StackPRE.DEBUG) {
System.out.println(this + " CBA = " + flag);
}
canBeAvail = flag;
}
public boolean canBeAvail() {
return canBeAvail;
}
public void setLater(final boolean flag) {
if (StackPRE.DEBUG) {
System.out.println(this + " Later = " + flag);
}
later = flag;
}
public boolean later() {
return later;
}
public String toString() {
String s = "";
final Iterator iter = cfg.preds(block).iterator();
while (iter.hasNext()) {
final Block pred = (Block) iter.next();
final Def def = operandAt(pred);
s += pred.label() + "=";
if (def == null) {
s += "null";
} else {
s += def.version;
}
if (iter.hasNext()) {
s += ", ";
}
}
return "phi" + "{" + version + "," + (downSafe() ? "" : "!")
+ "DS," + (canBeAvail() ? "" : "!") + "CBA,"
+ (later() ? "" : "!") + "Later}(" + s + ")";
}
}
public List varPhisAtBlock(final Block block) {
return varphis[cfg.preOrderIndex(block)];
}
/**
* Maintain the occurences so that they are visited in a preorder traversal
* of the dominator tree.
*/
private final class ExprInfo {
ArrayList[] vars;
Phi[] phis;
boolean save;
Map pushes;
Map pops;
Map defs;
LocalExpr def;
ArrayList cleanup;
public ExprInfo(final LocalExpr def) {
this.def = def;
vars = new ArrayList[cfg.size()];
for (int i = 0; i < vars.length; i++) {
vars[i] = new ArrayList();
}
phis = new Phi[cfg.size()];
save = false;
pushes = new HashMap();
pops = new HashMap();
defs = new HashMap();
cleanup = new ArrayList();
}
public void cleanup() {
final Iterator iter = cleanup.iterator();
while (iter.hasNext()) {
final Node node = (Node) iter.next();
node.cleanup();
}
vars = null;
phis = null;
pushes = null;
pops = null;
defs = null;
def = null;
cleanup = null;
}
public void registerForCleanup(final Node node) {
cleanup.add(node);
}
public void setSave(final boolean flag) {
save = flag;
}
public boolean save() {
return save;
}
public void setPush(final LocalExpr expr, final boolean flag) {
pushes.put(expr, new Boolean(flag));
}
public boolean push(final LocalExpr expr) {
final Boolean b = (Boolean) pushes.get(expr);
return (b != null) && b.booleanValue();
}
public void setPop(final LocalExpr expr, final boolean flag) {
pops.put(expr, new Boolean(flag));
}
public boolean pop(final LocalExpr expr) {
final Boolean b = (Boolean) pops.get(expr);
return (b != null) && b.booleanValue();
}
public void setDef(final LocalExpr expr, final Def def) {
if (StackPRE.DEBUG) {
System.out.println(" setting def for " + expr + " to "
+ def);
}
if (def != null) {
defs.put(expr, def);
} else {
defs.remove(expr);
}
}
public Def def(final LocalExpr expr) {
final Def def = (Def) defs.get(expr);
if (StackPRE.DEBUG) {
System.out.println(" def for " + expr + " is " + def);
}
return def;
}
public LocalExpr def() {
return def;
}
public void addPhi(final Block block) {
Phi phi = phis[cfg.preOrderIndex(block)];
if (phi == null) {
if (StackPRE.DEBUG) {
System.out.println(" add phi for " + def + " at "
+ block);
}
phi = new Phi(block);
phis[cfg.preOrderIndex(block)] = phi;
}
}
public List varsAtBlock(final Block block) {
final int blockIndex = cfg.preOrderIndex(block);
final List list = new ArrayList(vars[blockIndex].size()
+ stackvars[blockIndex].size());
final Iterator viter = vars[blockIndex].iterator();
final Iterator siter = stackvars[blockIndex].iterator();
if (!viter.hasNext() && !siter.hasNext()) {
return new ArrayList(0);
}
block.tree().visitChildren(new TreeVisitor() {
VarExpr vnext = null;
VarExpr snext = null;
{
if (viter.hasNext()) {
vnext = (VarExpr) viter.next();
}
if (siter.hasNext()) {
snext = (VarExpr) siter.next();
}
}
public void visitStmt(final Stmt stmt) {
if (((vnext != null) && (vnext.stmt() == stmt))
|| ((snext != null) && (snext.stmt() == stmt))) {
super.visitStmt(stmt);
}
}
public void visitVarExpr(final VarExpr expr) {
super.visitExpr(expr);
if (expr == vnext) {
if (viter.hasNext()) {
vnext = (VarExpr) viter.next();
} else {
vnext = null;
}
if (expr == snext) {
if (siter.hasNext()) {
snext = (VarExpr) siter.next();
} else {
snext = null;
}
}
list.add(expr);
} else if (expr == snext) {
if (siter.hasNext()) {
snext = (VarExpr) siter.next();
} else {
snext = null;
}
list.add(expr);
}
}
});
return list;
}
public Phi exprPhiAtBlock(final Block block) {
return phis[cfg.preOrderIndex(block)];
}
protected void print() {
System.out.println("Print for " + def + "------------------");
cfg.visit(new PrintVisitor() {
Phi phi = null;
public void visitBlock(final Block block) {
phi = exprPhiAtBlock(block);
super.visitBlock(block);
}
public void visitLabelStmt(final LabelStmt stmt) {
super.visitLabelStmt(stmt);
if (stmt.label().startsBlock()) {
if (phi != null) {
println(phi);
}
}
}
public void visitLocalExpr(final LocalExpr expr) {
super.visitLocalExpr(expr);
if (expr.def() == def) {
super.print("{" + defs.get(expr) + "}");
}
}
});
System.out.println("End Print ----------------------------");
}
}
class Worklist {
Map exprInfos;
LinkedList exprs;
public Worklist() {
exprInfos = new HashMap();
exprs = new LinkedList();
}
public boolean isEmpty() {
return exprs.isEmpty();
}
public ExprInfo removeFirst() {
final ExprInfo exprInfo = (ExprInfo) exprs.removeFirst();
exprInfos.remove(exprInfo.def());
return exprInfo;
}
public void addLocalVar(final LocalExpr var) {
final int blockIndex = cfg.preOrderIndex(var.block());
if (StackPRE.DEBUG) {
System.out.println("add var " + var);
}
ExprInfo exprInfo = (ExprInfo) exprInfos.get(var.def());
if (exprInfo == null) {
exprInfo = new ExprInfo((LocalExpr) var.def());
exprs.add(exprInfo);
exprInfos.put(var.def(), exprInfo);
if (StackPRE.DEBUG) {
System.out.println(" add info for " + var);
}
}
exprInfo.vars[blockIndex].add(var);
}
public void addStackVar(final StackExpr var) {
final int blockIndex = cfg.preOrderIndex(var.block());
if (StackPRE.DEBUG) {
System.out.println("add var " + var);
}
stackvars[blockIndex].add(var);
}
public void addVarPhi(final PhiJoinStmt stmt) {
varphis[cfg.preOrderIndex(stmt.block())].add(stmt);
}
public void prependVarPhi(final PhiJoinStmt stmt) {
final List v = varphis[cfg.preOrderIndex(stmt.block())];
if (!v.contains(stmt)) {
v.add(0, stmt);
}
}
}
}