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/CodeGenerator.java

2339 lines
61 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.*;
/**
* CodeGenerator performs some final optimizations and is used (via a visitor)
* to generate bytecode for the contents of a control flow graph.
*
* @see FlowGraph
*/
public class CodeGenerator extends TreeVisitor implements Opcode {
public static boolean DEBUG = false;
public static boolean USE_PERSISTENT = false; // Generate _nowb
/**
* Use information about placement of local variables to eliminate loads and
* stores in favor of stack manipulations
*/
public static boolean OPT_STACK = false;
public static boolean DB_OPT_STACK = false;
protected MethodEditor method;
protected Set visited;
protected Map postponedInstructions;
protected Block next;
protected int stackHeight; // The current height of the stack
StackOptimizer currentSO; // object used to determine where to apply
// stack optimization
/**
* Constructor.
*
* @param method
* The method for which bytecode is generated.
*/
public CodeGenerator(final MethodEditor method) {
this.method = method;
this.postponedInstructions = new HashMap();
}
/**
* Visits the nodes in the method's control flow graph and ensures that
* information about the method's basic blocks is consistent and correct.
*
* @param cfg
* The control flow graph associated with this method.
*/
public void visitFlowGraph(final FlowGraph cfg) {
// Generate the code.
visited = new HashSet();
visited.add(cfg.source());
visited.add(cfg.sink());
final Iterator e = cfg.trace().iterator();
Assert.isTrue(e.hasNext(), "trace is empty");
stackHeight = 0; // At beginning of method stack has height 0
Block block = (Block) e.next();
// Visit each block in the method (via the trace in the method's CFG)
// and ensure that the first (and ONLY the first) label in the code
// is marked as starting a block.
while (block != null) {
if (e.hasNext()) {
next = (Block) e.next();
} else {
next = null;
}
if (CodeGenerator.DEBUG) {
System.out.println("code for " + block);
}
// Make sure the first label is marked as starting a block
// and the rest are marked as not starting a block.
block.visit(new TreeVisitor() {
boolean startsBlock = true;
public void visitLabelStmt(final LabelStmt stmt) {
stmt.label().setStartsBlock(startsBlock);
startsBlock = false;
}
public void visitStmt(final Stmt stmt) {
}
});
// Generate the code for each block
visited.add(block);
// currentSO is the StackOptimizer object that discerns
// where dups may be used instead of loads
if (CodeGenerator.OPT_STACK) {
currentSO = block.stackOptimizer();
}
block.visitChildren(this);
block = next;
}
Assert.isTrue(visited.size() == cfg.size(),
"did not visit all blocks while generating code");
next = null;
visited = null;
// Go through the catch blocks and determine the what the
// protected regions are that correspond to the catch blocks.
// Create TryCatch objects to represent the protected regions.
final Iterator iter = cfg.catchBlocks().iterator();
while (iter.hasNext()) {
final Block catchBlock = (Block) iter.next();
final Handler handler = (Handler) cfg.handlersMap().get(catchBlock);
Type type = handler.catchType();
if (type.isNull()) {
type = null;
}
// First block in protected block
Block begin = null;
final Iterator blocks = cfg.trace().iterator();
while (blocks.hasNext()) {
block = (Block) blocks.next();
if (handler.protectedBlocks().contains(block)) {
if (begin == null) {
begin = block;
}
} else if (begin != null) {
// The block is no longer protected, its the end of the
// protected region
final TryCatch tc = new TryCatch(begin.label(), block
.label(), catchBlock.label(), type);
method.addTryCatch(tc);
begin = null;
}
}
}
}
/**
* Simplifies the control flow of a method by changing jump and return
* statements into gotos where appropriate.
*/
public void simplifyControlFlow(final FlowGraph cfg) {
// Remove any blocks from the CFG that consist of solely jumps
removeEmptyBlocks(cfg);
cfg.visit(new TreeVisitor() {
public void visitJsrStmt(final JsrStmt stmt) {
final Subroutine sub = stmt.sub();
// If there is only 1 path through the sub, replace both
// the jsr and the ret with gotos.
if (sub.numPaths() == 1) {
final Block exit = sub.exit();
// Remember that it is not required for a subroutine to have
// a ret. So, no exit block may be identified and we'll
// have to make sure one exists.
if (exit != null) {
final JumpStmt oldJump = (JumpStmt) exit.tree()
.lastStmt();
final JumpStmt jump = new GotoStmt(stmt.follow());
jump.catchTargets().addAll(oldJump.catchTargets());
oldJump.replaceWith(jump);
}
final JumpStmt jump = new GotoStmt(sub.entry());
jump.catchTargets().addAll(stmt.catchTargets());
stmt.replaceWith(jump);
// The subroutine is no longer really a subroutine
cfg.removeSub(sub);
// Clean up the CFG by removing all AddressStoreStmts that
// store the address of the "removed" subroutine.
cfg.visit(new TreeVisitor() {
Iterator iter;
public void visitTree(final Tree tree) {
iter = tree.stmts().iterator();
while (iter.hasNext()) {
final Stmt s = (Stmt) iter.next();
if (s instanceof AddressStoreStmt) {
final AddressStoreStmt store = (AddressStoreStmt) s;
if (store.sub() == sub) {
iter.remove();
}
}
}
}
});
}
}
public void visitStmt(final Stmt stmt) {
}
});
}
/**
* Replace PhiStmts with copies that accomplish what the PhiStmts represent.
* Then remove the PhiStmts from the control flow graph.
*/
public void replacePhis(final FlowGraph cfg) {
replaceCatchPhis(cfg);
replaceJoinPhis(cfg);
// Remove the phis.
cfg.visit(new TreeVisitor() {
public void visitTree(final Tree tree) {
final Iterator e = tree.stmts().iterator();
while (e.hasNext()) {
final Stmt s = (Stmt) e.next();
if (s instanceof PhiStmt) {
e.remove();
}
}
}
});
}
/**
* Replace each PhiCatchStmt with assignments at its operands' defs.
*/
private void replaceCatchPhis(final FlowGraph cfg) {
cfg.visit(new TreeVisitor() {
HashMap seen = new HashMap();
public void visitFlowGraph(final FlowGraph graph) {
final Iterator iter = graph.catchBlocks().iterator();
// Examine each block that begins an exception handler
while (iter.hasNext()) {
final Block block = (Block) iter.next();
block.visit(this);
}
}
public void visitPhiCatchStmt(final PhiCatchStmt phi) {
final LocalExpr target = (LocalExpr) phi.target();
final int index = target.index();
final Iterator iter = phi.operands().iterator();
// Examine every operand of the PhiCatchStmt. If necessary,
// insert copies of the operand to the target after the last
// occurrence of the operand.
while (iter.hasNext()) {
final LocalExpr expr = (LocalExpr) iter.next();
final LocalExpr def = (LocalExpr) expr.def();
if (def == null) {
continue;
}
if (CodeGenerator.DEBUG) {
System.out.println("inserting for " + phi + " at "
+ def);
}
BitSet s = (BitSet) seen.get(def);
if (s == null) {
s = new BitSet();
seen.put(def, s);
final BitSet t = s;
// Visit the parent expression and make note of which
// local variables were encountered in StoreExprs. That
// is, have we already generated a copy for the operand
// of
// interest?
def.parent().visit(new TreeVisitor() {
public void visitStoreExpr(final StoreExpr expr) {
if (CodeGenerator.DEBUG) {
System.out.println(" merging with "
+ expr);
}
final Expr lhs = expr.target();
final Expr rhs = expr.expr();
if (lhs instanceof LocalExpr) {
t.set(((LocalExpr) lhs).index());
}
if (rhs instanceof LocalExpr) {
t.set(((LocalExpr) rhs).index());
} else if (rhs instanceof StoreExpr) {
// Visit RHS. LHS be ignored by visitNode.
super.visitStoreExpr(expr);
}
}
public void visitNode(final Node node) {
}
});
}
// If we've already inserted a copy (StoreStmt) for the
// local variable, skip it
if (s.get(index)) {
continue;
}
s.set(index);
Assert.isTrue(def != null);
if (def.parent() instanceof Stmt) {
// Insert a new Stmt to copy into the target
final Stmt stmt = (Stmt) def.parent();
final Stmt store = createStore(target, def);
def.block().tree().addStmtAfter(store, stmt);
} else {
Assert.isTrue(def.parent() instanceof StoreExpr);
// Replace s := r with s := (t := r)
final StoreExpr p = (StoreExpr) def.parent();
final Expr rhs = p.expr();
if ((rhs instanceof LocalExpr)
&& (((LocalExpr) rhs).index() == def.index())) {
// No need to insert a copy. Just change the index
// (local variable to which LocalExpr is assigned)
// to be
// the same as the target
def.setIndex(index);
} else {
rhs.setParent(null);
// Copy the rhs into the target
final StoreExpr store = new StoreExpr(
(LocalExpr) target.clone(), rhs, rhs.type());
p.visit(new ReplaceVisitor(rhs, store));
}
}
}
}
public void visitStmt(final Stmt stmt) {
}
});
}
/**
* Replace PhiJoinStmts with assignments at the end of the predecessor
* blocks. Note that from now on the FUD chains are broken since there can
* be more than one def of a variable.
*/
private void replaceJoinPhis(final FlowGraph cfg) {
// Go in trace order since liveness was computed under this
// assumption.
final Iterator iter = cfg.trace().iterator();
while (iter.hasNext()) {
final Block block = (Block) iter.next();
if (block == cfg.sink()) {
continue;
}
block.visit(new TreeVisitor() {
public void visitPhiJoinStmt(final PhiJoinStmt stmt) {
// If an operand of the Phi statement is undefined, insert
// code to assign 0 to the operand. The value should never
// be used, but the verifier will squawk about using an
// undefined local variable.
final Iterator preds = cfg.preds(stmt.block()).iterator();
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
final Expr operand = stmt.operandAt(pred);
if ((stmt.target() instanceof LocalExpr)
&& (operand instanceof LocalExpr)) {
final LocalExpr t = (LocalExpr) stmt.target();
final LocalExpr s = (LocalExpr) operand;
if (t.index() == s.index()) {
// The target and the operand are already
// allocated to
// the same variable. Don't bother making a
// copy.
continue;
}
}
final Tree tree = pred.tree();
// Insert stores before the last stmt to ensure
// we don't redefine locals used the the branch stmt.
final Stmt last = tree.lastStmt();
last.visitChildren(new TreeVisitor() {
// The last statement in the block should be a jump.
// If
// the jump statement contains an expression,
// replace
// that expression with a stack variable. Before the
// jump, insert a store of the expression into the
// stack
// variable. This is done so that the store to the
// PhiJoinStmt's operand does not interfere with any
// local variables that might appear in the
// expression.
//
// operand = ...
// JUMP (exp)
// |
// v
// target = PhiJoin(operand)
// ...
// Becomes
//
// operand = ...
// var = exp
// target = operand
// JUMP (var)
// |
// v
// target = PhiJoin(operand) // Removed later
// ...
public void visitExpr(final Expr expr) {
StackExpr var = tree.newStack(expr.type());
var.setValueNumber(expr.valueNumber());
final Node p = expr.parent();
expr.setParent(null);
p.visit(new ReplaceVisitor(expr, var));
var = (StackExpr) var.clone();
final StoreExpr store = new StoreExpr(var,
expr, expr.type());
store.setValueNumber(expr.valueNumber());
final Stmt storeStmt = new ExprStmt(store);
storeStmt.setValueNumber(expr.valueNumber());
tree.addStmtBeforeJump(storeStmt);
}
public void visitStackExpr(final StackExpr expr) {
}
});
final Stmt store = createStore(stmt.target(), operand);
if (CodeGenerator.DEBUG) {
System.out.println("insert for " + stmt + " "
+ store + " in " + pred);
}
tree.addStmtBeforeJump(store);
}
}
public void visitStmt(final Stmt stmt) {
}
});
}
}
/**
* Removes blocks that contain no other statements than gotos, jumps,
* returns, or labels. Other blocks that are invovled with the blocks being
* removed are updated appropriately.
*/
private void removeEmptyBlocks(final FlowGraph cfg) {
final Set emptyBlocks = new HashSet();
Iterator e = cfg.nodes().iterator();
BLOCKS: while (e.hasNext()) {
final Block block = (Block) e.next();
// Collect any blocks that contain only gotos,
// jsrs, rets, or labels.
final Iterator stmts = block.tree().stmts().iterator();
while (stmts.hasNext()) {
final Stmt stmt = (Stmt) stmts.next();
if ((stmt instanceof GotoStmt) || (stmt instanceof JsrStmt)
|| (stmt instanceof RetStmt)
|| (stmt instanceof LabelStmt)) {
continue;
}
// The block contains something other than the above, it is
// not empty.
continue BLOCKS;
}
emptyBlocks.add(block);
}
// We want to keep the source, init, and sink blocks even if
// they're empty
emptyBlocks.remove(cfg.source());
emptyBlocks.remove(cfg.init());
emptyBlocks.remove(cfg.sink());
// Did the CFG change?
boolean changed = true;
while (changed) {
changed = false;
// Exclude the blocks that are control dependent on other blocks.
final Set empty = new HashSet(emptyBlocks);
empty.removeAll(cfg.iteratedPdomFrontier(cfg.nodes()));
e = empty.iterator();
while (e.hasNext()) {
final Block block = (Block) e.next();
if (CodeGenerator.DEBUG) {
System.out.println("removing empty " + block);
}
final Stmt last = block.tree().lastStmt();
Assert.isTrue((last instanceof GotoStmt)
|| (last instanceof JsrStmt)
|| (last instanceof RetStmt));
if (last instanceof GotoStmt) {
// All a block does is jump to another block
//
// jmp ... L
// L: goto M
// =>
// jmp ... M
final Block target = ((GotoStmt) last).target();
final Iterator preds = new ImmutableIterator(cfg
.preds(block));
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
Assert.isTrue(pred != cfg.source());
final Stmt predLast = pred.tree().lastStmt();
predLast.visit(new ReplaceTarget(block, target));
cfg.removeEdge(pred, block);
cfg.addEdge(pred, target);
changed = true;
}
} else if (last instanceof RetStmt) {
// All a subroutine does is return
final Iterator preds = new ImmutableIterator(cfg
.preds(block));
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
Assert.isTrue(pred != cfg.source());
final Stmt predLast = pred.tree().lastStmt();
if (predLast instanceof JsrStmt) {
// The previous block is the jsr...
//
// jsr L ret to M
// M: ...
// L: ret // The body of the subroutine is empty
// =>
// goto M
// M: ...
final JsrStmt stmt = (JsrStmt) predLast;
final JumpStmt jump = new GotoStmt(stmt.follow());
jump.catchTargets().addAll(stmt.catchTargets());
stmt.replaceWith(jump);
stmt.sub().removePathsContaining(pred);
} else if (predLast instanceof GotoStmt) {
// The previous block ends in a goto. Move the ret
// up
// into the previous block, update catch targets of
// any
// exceptions thrown by the block terminated by the
// jump, and update the subroutine's exit block to
// be
// the previous block (in which the ret now
// resides).
final JumpStmt jump = (RetStmt) last.clone();
jump.catchTargets().addAll(
((JumpStmt) predLast).catchTargets());
predLast.replaceWith(jump);
((RetStmt) last).sub().setExit(pred);
}
// Remove the block from the CFG
cfg.succs(pred).remove(block);
cfg.succs(pred).addAll(cfg.succs(block));
changed = true;
}
} else if (last instanceof JsrStmt) {
// All the block does is a jsr
//
// goto L
// L: jsr M
// =>
// jsr M
// L: jsr M
final Iterator preds = new ImmutableIterator(cfg
.preds(block));
while (preds.hasNext()) {
final Block pred = (Block) preds.next();
Assert.isTrue(pred != cfg.source());
final Stmt predLast = pred.tree().lastStmt();
if (predLast instanceof GotoStmt) {
final JsrStmt stmt = (JsrStmt) last;
final JumpStmt jump = new JsrStmt(stmt.sub(), stmt
.follow());
jump.catchTargets().addAll(
((JumpStmt) predLast).catchTargets());
predLast.replaceWith(jump);
// The block is no longer a viable caller of the
// subroutine
stmt.sub().removePathsContaining(block);
stmt.sub().addPath(pred, stmt.follow());
cfg.addEdge(pred, stmt.sub().entry());
cfg.removeEdge(pred, block);
changed = true;
}
}
} else {
throw new RuntimeException();
}
}
if (changed) {
cfg.removeUnreachable();
// Remove any empty blocks that we've already deleted.
emptyBlocks.retainAll(cfg.nodes());
}
}
}
/**
* Allocate "registers" (LocalVariables) for the return addresses for each
* subroutine in the method.
*
* @param cfg
* Control flow graph for the method
* @param alloc
* Allocation (and information about) the local variables in the
* method.
*
* @see LocalVariable
* @see LocalExpr
*/
public void allocReturnAddresses(final FlowGraph cfg,
final RegisterAllocator alloc) {
// Allocate registers for the returnAddresses. Don't bother trying
// to minimize the number of locals, just get a new one.
final Iterator e = cfg.subroutines().iterator();
while (e.hasNext()) {
final Subroutine sub = (Subroutine) e.next();
final LocalVariable var = alloc.newLocal(Type.ADDRESS);
sub.setReturnAddress(var);
}
}
/**
* Create a ExprStmt that initializes a target variable to a default value
* based on the type of the target.
*/
protected Stmt createUndefinedStore(final VarExpr target) {
if (target.type().isReference()) {
return new ExprStmt(new StoreExpr(target, new ConstantExpr(null,
Type.OBJECT), target.type()));
}
if (target.type().isIntegral()) {
return new ExprStmt(new StoreExpr(target, new ConstantExpr(
new Integer(0), Type.INTEGER), target.type()));
}
if (target.type().equals(Type.LONG)) {
return new ExprStmt(new StoreExpr(target, new ConstantExpr(
new Long(0), Type.LONG), target.type()));
}
if (target.type().equals(Type.FLOAT)) {
return new ExprStmt(new StoreExpr(target, new ConstantExpr(
new Float(0.0F), Type.FLOAT), target.type()));
}
if (target.type().equals(Type.DOUBLE)) {
return new ExprStmt(new StoreExpr(target, new ConstantExpr(
new Double(0.0), Type.DOUBLE), target.type()));
}
throw new RuntimeException("Illegal type: " + target.type());
}
/**
* Returns an ExprStmt that contains a store of the source into the target.
*/
protected Stmt createStore(VarExpr target, final Expr source) {
target = (VarExpr) target.clone();
// Source is an undefined variable, initialize it
if ((source instanceof VarExpr) && (source.def() == null)) {
return createUndefinedStore(target);
}
return new ExprStmt(new StoreExpr(target, (Expr) source.clone(), target
.type()));
}
/*
* Using an InstructionVisitor generate the code...
*/
// Several of the visit methods contain code for stack
// optimization (place dups and swaps and eliminate temporary
// variables). Nodes where swaps are to be placed are so
// marked. The markings may appear at IfCmpStmt, InitStmt,
// StoreExpr, ArithExpr, ArrayRefExpr, CallMethodExpr,
// CallStaticExpr, NewMultiArrayExpr, ShiftExpr.
public void visitExpr(final Expr expr) {
throw new RuntimeException("Unhandled expression type: "
+ expr.getClass().getName());
}
public void visitExprStmt(final ExprStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
stmt.visitChildren(this);
genPostponed(stmt);
if (!(stmt.expr() instanceof StoreExpr)) {
if (!stmt.expr().type().isVoid()) {
if (stmt.expr().type().isWide()) {
method.addInstruction(Opcode.opcx_pop2);
stackHeight -= 2;
} else {
method.addInstruction(Opcode.opcx_pop);
stackHeight -= 1;
}
}
}
}
public void visitInitStmt(final InitStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
}
public void visitGotoStmt(final GotoStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
genPostponed(stmt);
final Block target = stmt.target();
if (target != next) {
method.addInstruction(Opcode.opcx_goto, stmt.target().label());
}
}
public void visitIfCmpStmt(final IfCmpStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
final Block t = stmt.trueTarget();
final Block f = stmt.falseTarget();
if (f == next) {
// Fall through to the false branch.
genIfCmpStmt(stmt);
} else if (t == next) {
// Fall through to the true branch.
stmt.negate();
genIfCmpStmt(stmt);
} else {
// Generate a goto to the false branch after the if statement.
genIfCmpStmt(stmt);
method.addLabel(method.newLabelTrue()); // Tom changed to say "True"
method.addInstruction(Opcode.opcx_goto, f.label());
}
}
private void genIfCmpStmt(final IfCmpStmt stmt) {
int opcode;
stmt.visitChildren(this);
genPostponed(stmt);
final int cmp = stmt.comparison();
if (stmt.left().type().isReference()) {
Assert.isTrue(stmt.right().type().isReference(),
"Illegal statement: " + stmt);
switch (cmp) {
case IfStmt.EQ:
opcode = Opcode.opcx_if_acmpeq;
break;
case IfStmt.NE:
opcode = Opcode.opcx_if_acmpne;
break;
default:
throw new RuntimeException();
}
} else {
Assert.isTrue(stmt.left().type().isIntegral(),
"Illegal statement: " + stmt);
Assert.isTrue(stmt.right().type().isIntegral(),
"Illegal statement: " + stmt);
switch (cmp) {
case IfStmt.EQ:
opcode = Opcode.opcx_if_icmpeq;
break;
case IfStmt.NE:
opcode = Opcode.opcx_if_icmpne;
break;
case IfStmt.GT:
opcode = Opcode.opcx_if_icmpgt;
break;
case IfStmt.GE:
opcode = Opcode.opcx_if_icmpge;
break;
case IfStmt.LT:
opcode = Opcode.opcx_if_icmplt;
break;
case IfStmt.LE:
opcode = Opcode.opcx_if_icmple;
break;
default:
throw new RuntimeException();
}
}
method.addInstruction(opcode, stmt.trueTarget().label());
stackHeight -= 2;
}
public void visitIfZeroStmt(final IfZeroStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
final Block t = stmt.trueTarget();
final Block f = stmt.falseTarget();
if (f == next) {
// Fall through to the false branch.
genIfZeroStmt(stmt);
} else if (t == next) {
// Fall through to the true branch.
stmt.negate();
genIfZeroStmt(stmt);
} else {
// Generate a goto to the false branch after the if statement.
genIfZeroStmt(stmt);
method.addLabel(method.newLabelTrue()); // Tom added "True"
method.addInstruction(Opcode.opcx_goto, f.label());
}
}
private void genIfZeroStmt(final IfZeroStmt stmt) {
int opcode;
stmt.expr().visit(this);
genPostponed(stmt);
final int cmp = stmt.comparison();
if (stmt.expr().type().isReference()) {
switch (cmp) {
case IfStmt.EQ:
opcode = Opcode.opcx_ifnull;
break;
case IfStmt.NE:
opcode = Opcode.opcx_ifnonnull;
break;
default:
throw new RuntimeException();
}
} else {
Assert.isTrue(stmt.expr().type().isIntegral(),
"Illegal statement: " + stmt);
switch (cmp) {
case IfStmt.EQ:
opcode = Opcode.opcx_ifeq;
break;
case IfStmt.NE:
opcode = Opcode.opcx_ifne;
break;
case IfStmt.GT:
opcode = Opcode.opcx_ifgt;
break;
case IfStmt.GE:
opcode = Opcode.opcx_ifge;
break;
case IfStmt.LT:
opcode = Opcode.opcx_iflt;
break;
case IfStmt.LE:
opcode = Opcode.opcx_ifle;
break;
default:
throw new RuntimeException();
}
}
method.addInstruction(opcode, stmt.trueTarget().label());
stackHeight -= 1;
}
public void visitLabelStmt(final LabelStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
stmt.visitChildren(this);
genPostponed(stmt);
method.addLabel(stmt.label());
}
public void visitMonitorStmt(final MonitorStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
stmt.visitChildren(this);
genPostponed(stmt);
if (stmt.kind() == MonitorStmt.ENTER) {
method.addInstruction(Opcode.opcx_monitorenter);
stackHeight -= 1;
} else if (stmt.kind() == MonitorStmt.EXIT) {
method.addInstruction(Opcode.opcx_monitorexit);
stackHeight -= 1;
} else {
throw new IllegalArgumentException();
}
}
public void visitPhiStmt(final PhiStmt stmt) {
throw new RuntimeException("Cannot generate code for " + stmt);
}
public void visitRCExpr(final RCExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
// Move the rc forward as far as possible.
//
// For example, for the expression:
//
// rc(x).m(rc(a).b)
//
// we want to generate:
//
// aload x
// aload a
// rc 0
// getfield <A.b>
// rc 1
// invoke <X.m>
//
// rather than:
//
// aload x
// rc 0
// aload a
// rc 0
// getfield <A.b>
// invoke <X.m>
//
Instruction postpone = null;
Node parent = expr.parent();
// If the parent is wrapped in a ZeroCheckExpr, then look at the
// parent's parent.
if (parent instanceof ZeroCheckExpr) {
parent = parent.parent();
}
// If the depth is going to be > 0, postpone the rc instruction
// to just before the getfield, putfield, invoke, xaload, xastore, etc.
// If the stack depth for the rc is going to be 0, the rc will (be)
// the next instruction generated anyway, so don't postpone.
if (parent instanceof ArrayRefExpr) {
final ArrayRefExpr p = (ArrayRefExpr) parent;
if (expr == p.array()) {
if (p.isDef()) {
// a[i] := r
// Stack at the xastore: ... a i r
postpone = new Instruction(Opcode.opcx_rc, new Integer(p
.type().stackHeight() + 1));
} else {
// use a[i]
// Stack at the xaload: ... a i
postpone = new Instruction(Opcode.opcx_rc, new Integer(1));
}
}
} else if (parent instanceof CallMethodExpr) {
final CallMethodExpr p = (CallMethodExpr) parent;
if (expr == p.receiver()) {
// a.m(b, c)
// Stack at the invoke: ... a b c
final MemberRef method = p.method();
final int depth = method.nameAndType().type().stackHeight();
postpone = new Instruction(Opcode.opcx_rc, new Integer(depth));
}
} else if (parent instanceof FieldExpr) {
final FieldExpr p = (FieldExpr) parent;
if (expr == p.object()) {
if (p.isDef()) {
// a.b := r
// Stack at the putfield: ... a r
postpone = new Instruction(Opcode.opcx_rc, new Integer(p
.type().stackHeight()));
}
}
}
if (postpone == null) {
int depth = 0;
if (expr.expr() instanceof StackExpr) {
// If the rc works on a StackExpr, calculate its depth in the
// stack. In all other cases, the rc will operate on whatever
// is on top of the stack.
final StackExpr stackVar = (StackExpr) expr.expr();
depth = stackHeight - stackVar.index() - 1;
}
method.addInstruction(Opcode.opcx_rc, new Integer(depth));
} else {
postponedInstructions.put(parent, postpone);
}
}
public void visitUCExpr(final UCExpr expr) {
expr.visitChildren(this);
if (true) {
return;
}
genPostponed(expr);
// Move the uc forward as far as possible.
Instruction postpone = null;
final Node parent = expr.parent();
// If the depth is going to be > 0, postpone the uc instruction
// to just before the putfield. If the stack depth for the
// uc is going to be 0, the uc will the next instruction
// generated anyway, so don't postpone.
if (parent instanceof FieldExpr) {
final FieldExpr p = (FieldExpr) parent;
if (expr == p.object()) {
if (p.isDef()) {
// a.b := r
// Stack at the putfield: ... a r
if (expr.kind() == UCExpr.POINTER) {
postpone = new Instruction(Opcode.opcx_aupdate,
new Integer(p.type().stackHeight()));
} else if (expr.kind() == UCExpr.SCALAR) {
postpone = new Instruction(Opcode.opcx_supdate,
new Integer(p.type().stackHeight()));
} else {
throw new RuntimeException();
}
}
}
}
if (postpone == null) {
int depth = 0;
if (expr.expr() instanceof StackExpr) {
// If the UCExpr operates on a stack variable, use that to
// determine the depth. In all other cases, use 0.
final StackExpr stackVar = (StackExpr) expr.expr();
depth = stackHeight - stackVar.index() - 1;
}
if (expr.kind() == UCExpr.POINTER) {
method.addInstruction(Opcode.opcx_aupdate, new Integer(depth));
} else if (expr.kind() == UCExpr.SCALAR) {
method.addInstruction(Opcode.opcx_supdate, new Integer(depth));
} else {
throw new RuntimeException();
}
} else {
postponedInstructions.put(parent, postpone);
}
}
public void visitRetStmt(final RetStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
genPostponed(stmt);
final Subroutine sub = stmt.sub();
Assert.isTrue(sub.returnAddress() != null);
method.addInstruction(Opcode.opcx_ret, sub.returnAddress());
}
public void visitReturnExprStmt(final ReturnExprStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
stmt.visitChildren(this);
genPostponed(stmt);
final Type type = stmt.expr().type();
// Stack should be empty after return
if (type.isReference()) {
method.addInstruction(Opcode.opcx_areturn);
stackHeight = 0;
} else if (type.isIntegral()) {
method.addInstruction(Opcode.opcx_ireturn);
stackHeight = 0;
} else if (type.equals(Type.LONG)) {
method.addInstruction(Opcode.opcx_lreturn);
stackHeight = 0;
} else if (type.equals(Type.FLOAT)) {
method.addInstruction(Opcode.opcx_freturn);
stackHeight = 0;
} else if (type.equals(Type.DOUBLE)) {
method.addInstruction(Opcode.opcx_dreturn);
stackHeight = 0;
}
}
public void visitReturnStmt(final ReturnStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
genPostponed(stmt);
stmt.visitChildren(this);
method.addInstruction(Opcode.opcx_return);
// Stack height is zero after return
stackHeight = 0;
}
public void visitStoreExpr(final StoreExpr expr) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + expr);
}
final MemExpr lhs = expr.target();
final Expr rhs = expr.expr();
boolean returnsValue = !(expr.parent() instanceof ExprStmt);
// eliminate the store if the both sides have the same target
if (!returnsValue) {
if ((lhs instanceof LocalExpr) && (rhs instanceof LocalExpr)) {
// The second condition checks that the right sides have indeed
// been stored-- otherwise (ie, if we're just keeping it on the
// stack), we should not eliminate this store
if ((((LocalExpr) lhs).index() == ((LocalExpr) rhs).index())
&& (!CodeGenerator.OPT_STACK || currentSO
.shouldStore(((LocalExpr) ((LocalExpr) rhs)
.def())))) {
return;
}
}
// Special case to handle L := L + k.
// Generate "iinc L, k" instead of "iload L; ldc k; iadd; istore L".
//
// TODO: for L := M + k, generate "iload M; istore L; iinc L, k".
//
/*
* This next special case was modified for stack optimization. If L
* is marked for a dup, the fact that its value is never on the
* stack means we don't have anything to dup-- we need to load
* instead. (Things get more complicated if it was marked for a
* dup_x2, but that's not likely to happen)
*/
if ((lhs instanceof LocalExpr) && lhs.type().isIntegral()) {
Integer value = null;
// eliminate the store if the both sides have the same target
final int index = ((LocalExpr) lhs).index();
if (rhs instanceof ArithExpr) {
final ArithExpr arith = (ArithExpr) rhs;
final Expr left = arith.left();
final Expr right = arith.right();
if ((left instanceof LocalExpr)
&& (index == ((LocalExpr) left).index())
&& (right instanceof ConstantExpr)) {
final ConstantExpr c = (ConstantExpr) right;
if (c.value() instanceof Integer) {
value = (Integer) c.value();
}
} else if ((right instanceof LocalExpr)
&& (index == ((LocalExpr) right).index())
&& (left instanceof ConstantExpr)
&& (arith.operation() == ArithExpr.ADD)) {
// This will not work for x = c - x because it is not
// the
// same as x = x - c.
final ConstantExpr c = (ConstantExpr) left;
if (c.value() instanceof Integer) {
value = (Integer) c.value();
}
}
if ((value != null) && (arith.operation() == ArithExpr.SUB)) {
value = new Integer(-value.intValue());
} else if (arith.operation() != ArithExpr.ADD) {
value = null;
}
}
if (value != null) {
final int incr = value.intValue();
if (incr == 0) {
// No need to increment by 0.
// for a better understanding of what's going on in
// these
// additions, see VisitLocalExpr, where we do basically
// the
// same thing.
if (CodeGenerator.OPT_STACK) {
int dups, dup_x1s, dup_x2s;
dups = currentSO.dups((LocalExpr) lhs);
dup_x1s = currentSO.dup_x1s((LocalExpr) lhs);
dup_x2s = currentSO.dup_x2s((LocalExpr) lhs);
for (int i = 0; i < dup_x2s; i++) {
// This is really awful, but be consoled in that
// it is
// highly improbable to happen... this is just
// to make correct code in the chance that we
// have something like this.
method.addInstruction(Opcode.opcx_ldc,
new Integer(0));
method.addInstruction(Opcode.opc_dup_x2);
method.addInstruction(Opcode.opc_pop);
stackHeight += 1;
}
for (int i = 0; i < dup_x1s; i++) {
method.addInstruction(Opcode.opcx_ldc,
new Integer(0));
method.addInstruction(Opcode.opc_swap);
stackHeight += 1;
}
for (int i = 0; i < dups; i++) {
method.addInstruction(Opcode.opcx_ldc,
new Integer(0));
stackHeight += 1;
}
}
return;
} else if ((short) incr == incr) {
// Only generate an iinc if the increment fits in
// a short.
method.addInstruction(Opcode.opcx_iinc, new IncOperand(
new LocalVariable(index), incr));
if (CodeGenerator.OPT_STACK) {
int dups, dup_x1s, dup_x2s;
dups = currentSO.dups((LocalExpr) lhs);
dup_x1s = currentSO.dup_x1s((LocalExpr) lhs);
dup_x2s = currentSO.dup_x2s((LocalExpr) lhs);
for (int i = 0; i < dup_x2s; i++) {
method.addInstruction(Opcode.opcx_istore,
new LocalVariable(((LocalExpr) lhs)
.index()));
method.addInstruction(Opcode.opc_dup_x2);
method.addInstruction(Opcode.opc_pop);
stackHeight += 1;
}
for (int i = 0; i < dup_x1s; i++) {
method.addInstruction(Opcode.opcx_iload,
new LocalVariable(((LocalExpr) lhs)
.index()));
method.addInstruction(Opcode.opc_swap);
stackHeight += 1;
}
for (int i = 0; i < dups; i++) {
method.addInstruction(Opcode.opcx_iload,
new LocalVariable(((LocalExpr) lhs)
.index()));
stackHeight += 1;
}
}
return;
}
}
}
}
// Generate, and return the value.
lhs.visitChildren(this);
rhs.visit(this);
if (returnsValue) {
if (lhs instanceof ArrayRefExpr) {
// array index rhs --> rhs array index rhs
if (rhs.type().isWide()) {
method.addInstruction(Opcode.opcx_dup2_x2);
stackHeight += 2;
} else {
method.addInstruction(Opcode.opcx_dup_x2);
stackHeight += 1;
}
} else if (lhs instanceof FieldExpr) {
// object rhs --> rhs object rhs
if (rhs.type().isWide()) {
method.addInstruction(Opcode.opcx_dup2_x1);
stackHeight += 2;
} else {
method.addInstruction(Opcode.opcx_dup_x1);
stackHeight += 1;
}
} else {
// rhs --> rhs rhs
if (rhs.type().isWide()) {
method.addInstruction(Opcode.opcx_dup2);
stackHeight += 2;
} else {
method.addInstruction(Opcode.opcx_dup);
stackHeight += 1;
}
}
}
genPostponed(expr);
lhs.visitOnly(this);
}
public void visitAddressStoreStmt(final AddressStoreStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
genPostponed(stmt);
final Subroutine sub = stmt.sub();
Assert.isTrue(sub.returnAddress() != null);
method.addInstruction(Opcode.opcx_astore, sub.returnAddress());
stackHeight -= 1;
}
public void visitJsrStmt(final JsrStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
genPostponed(stmt);
final Block entry = stmt.sub().entry();
method.addInstruction(Opcode.opcx_jsr, entry.label());
stackHeight += 1;
if (stmt.follow() != next) {
method.addLabel(method.newLabelTrue());
method.addInstruction(Opcode.opcx_goto, stmt.follow().label());
}
}
public void visitSwitchStmt(final SwitchStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
stmt.visitChildren(this);
genPostponed(stmt);
final Label[] targets = new Label[stmt.targets().length];
for (int i = 0; i < targets.length; i++) {
targets[i] = stmt.targets()[i].label();
}
method.addInstruction(Opcode.opcx_switch, new Switch(stmt
.defaultTarget().label(), targets, stmt.values()));
stackHeight -= 1;
}
public void visitStackManipStmt(final StackManipStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
genPostponed(stmt);
// All the children are stack variables, so don't so anything.
switch (stmt.kind()) {
case StackManipStmt.SWAP:
method.addInstruction(Opcode.opcx_swap);
break;
case StackManipStmt.DUP:
method.addInstruction(Opcode.opcx_dup);
stackHeight += 1;
break;
case StackManipStmt.DUP_X1:
method.addInstruction(Opcode.opcx_dup_x1);
stackHeight += 1;
break;
case StackManipStmt.DUP_X2:
method.addInstruction(Opcode.opcx_dup_x2);
stackHeight += 1;
break;
case StackManipStmt.DUP2:
method.addInstruction(Opcode.opcx_dup2);
stackHeight += 2;
break;
case StackManipStmt.DUP2_X1:
method.addInstruction(Opcode.opcx_dup2_x1);
stackHeight += 2;
break;
case StackManipStmt.DUP2_X2:
method.addInstruction(Opcode.opcx_dup2_x2);
stackHeight += 2;
break;
}
}
public void visitThrowStmt(final ThrowStmt stmt) {
if (CodeGenerator.DEBUG) {
System.out.println("code for " + stmt);
}
stmt.visitChildren(this);
genPostponed(stmt);
method.addInstruction(Opcode.opcx_athrow);
}
public void visitSCStmt(final SCStmt stmt) {
stmt.visitChildren(this);
genPostponed(stmt);
method.addInstruction(Opcode.opcx_aswizzle);
stackHeight -= 2;
}
public void visitSRStmt(final SRStmt stmt) {
stmt.visitChildren(this);
genPostponed(stmt);
method.addInstruction(Opcode.opcx_aswrange);
stackHeight -= 3;
}
public void visitArithExpr(final ArithExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
final int[][] opcode = new int[][] {
{ Opcode.opcx_iadd, Opcode.opcx_ladd, Opcode.opcx_fadd,
Opcode.opcx_dadd },
{ Opcode.opcx_iand, Opcode.opcx_land, Opcode.opcx_nop,
Opcode.opcx_nop },
{ Opcode.opcx_idiv, Opcode.opcx_ldiv, Opcode.opcx_fdiv,
Opcode.opcx_ddiv },
{ Opcode.opcx_imul, Opcode.opcx_lmul, Opcode.opcx_fmul,
Opcode.opcx_dmul },
{ Opcode.opcx_ior, Opcode.opcx_lor, Opcode.opcx_nop,
Opcode.opcx_nop },
{ Opcode.opcx_irem, Opcode.opcx_lrem, Opcode.opcx_frem,
Opcode.opcx_drem },
{ Opcode.opcx_isub, Opcode.opcx_lsub, Opcode.opcx_fsub,
Opcode.opcx_dsub },
{ Opcode.opcx_ixor, Opcode.opcx_lxor, Opcode.opcx_nop,
Opcode.opcx_nop },
{ Opcode.opcx_nop, Opcode.opcx_lcmp, Opcode.opcx_nop,
Opcode.opcx_nop },
{ Opcode.opcx_nop, Opcode.opcx_nop, Opcode.opcx_fcmpl,
Opcode.opcx_dcmpl },
{ Opcode.opcx_nop, Opcode.opcx_nop, Opcode.opcx_fcmpg,
Opcode.opcx_dcmpg } };
final int[][] stackChange = new int[][] { { -1, -2, -1, -2 },
{ -1, -2, 0, 0 }, { -1, -2, -1, -2 }, { -1, -2, -1, -2 },
{ -1, -2, 0, 0 }, { -1, -2, -1, -2 }, { -1, -2, -1, -2 },
{ -1, -2, 0, 0 }, { 0, -3, 0, 0 }, { 0, 0, -1, -3 },
{ 0, 0, -1, -3 } };
int type;
if (expr.left().type().isIntegral()) {
type = 0;
} else if (expr.left().type().equals(Type.LONG)) {
type = 1;
} else if (expr.left().type().equals(Type.FLOAT)) {
type = 2;
} else if (expr.left().type().equals(Type.DOUBLE)) {
type = 3;
} else {
throw new IllegalArgumentException("Can't generate code for type: "
+ expr.left().type() + " (expr " + expr + ")");
}
switch (expr.operation()) {
case ArithExpr.ADD:
method.addInstruction(opcode[0][type]);
stackHeight += stackChange[0][type];
break;
case ArithExpr.AND:
method.addInstruction(opcode[1][type]);
stackHeight += stackChange[1][type];
break;
case ArithExpr.DIV:
method.addInstruction(opcode[2][type]);
stackHeight += stackChange[2][type];
break;
case ArithExpr.MUL:
method.addInstruction(opcode[3][type]);
stackHeight += stackChange[3][type];
break;
case ArithExpr.IOR:
method.addInstruction(opcode[4][type]);
stackHeight += stackChange[4][type];
break;
case ArithExpr.REM:
method.addInstruction(opcode[5][type]);
stackHeight += stackChange[5][type];
break;
case ArithExpr.SUB:
method.addInstruction(opcode[6][type]);
stackHeight += stackChange[6][type];
break;
case ArithExpr.XOR:
method.addInstruction(opcode[7][type]);
stackHeight += stackChange[7][type];
break;
case ArithExpr.CMP:
method.addInstruction(opcode[8][type]);
stackHeight += stackChange[8][type];
break;
case ArithExpr.CMPL:
method.addInstruction(opcode[9][type]);
stackHeight += stackChange[9][type];
break;
case ArithExpr.CMPG:
method.addInstruction(opcode[10][type]);
stackHeight += stackChange[10][type];
break;
}
}
public void visitArrayLengthExpr(final ArrayLengthExpr expr) {
expr.visitChildren(this);
method.addInstruction(Opcode.opcx_arraylength);
}
public void visitArrayRefExpr(final ArrayRefExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
int opcode;
if (expr.isDef()) {
if (expr.elementType().isReference()) {
opcode = Opcode.opcx_aastore;
stackHeight -= 3;
} else if (expr.elementType().equals(Type.BYTE)) {
opcode = Opcode.opcx_bastore;
stackHeight -= 3;
} else if (expr.elementType().equals(Type.CHARACTER)) {
opcode = Opcode.opcx_castore;
stackHeight -= 3;
} else if (expr.elementType().equals(Type.SHORT)) {
opcode = Opcode.opcx_sastore;
stackHeight -= 3;
} else if (expr.elementType().equals(Type.INTEGER)) {
opcode = Opcode.opcx_iastore;
stackHeight -= 3;
} else if (expr.elementType().equals(Type.LONG)) {
opcode = Opcode.opcx_lastore;
stackHeight -= 4;
} else if (expr.elementType().equals(Type.FLOAT)) {
opcode = Opcode.opcx_fastore;
stackHeight -= 3;
} else if (expr.elementType().equals(Type.DOUBLE)) {
opcode = Opcode.opcx_dastore;
stackHeight -= 4;
} else {
throw new IllegalArgumentException(
"Can't generate code for type: " + expr.type()
+ " (expr " + expr + ")");
}
} else {
if (expr.elementType().isReference()) {
opcode = Opcode.opcx_aaload;
stackHeight -= 1;
} else if (expr.elementType().equals(Type.BYTE)) {
opcode = Opcode.opcx_baload;
stackHeight -= 1;
} else if (expr.elementType().equals(Type.CHARACTER)) {
opcode = Opcode.opcx_caload;
stackHeight -= 1;
} else if (expr.elementType().equals(Type.SHORT)) {
opcode = Opcode.opcx_saload;
stackHeight -= 1;
} else if (expr.elementType().equals(Type.INTEGER)) {
opcode = Opcode.opcx_iaload;
stackHeight -= 1;
} else if (expr.elementType().equals(Type.LONG)) {
opcode = Opcode.opcx_laload;
stackHeight -= 0;
} else if (expr.elementType().equals(Type.FLOAT)) {
opcode = Opcode.opcx_faload;
stackHeight -= 1;
} else if (expr.elementType().equals(Type.DOUBLE)) {
opcode = Opcode.opcx_daload;
stackHeight -= 0;
} else {
throw new IllegalArgumentException(
"Can't generate code for type: " + expr.type()
+ " (expr " + expr + ")");
}
}
method.addInstruction(opcode);
}
public void visitCallMethodExpr(final CallMethodExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
int opcode;
if (expr.kind() == CallMethodExpr.VIRTUAL) {
opcode = Opcode.opcx_invokevirtual;
} else if (expr.kind() == CallMethodExpr.NONVIRTUAL) {
opcode = Opcode.opcx_invokespecial;
} else if (expr.kind() == CallMethodExpr.INTERFACE) {
opcode = Opcode.opcx_invokeinterface;
} else {
throw new IllegalArgumentException();
}
method.addInstruction(opcode, expr.method());
// Pop reciever object off stack
stackHeight -= 1;
// Pop each parameter off stack
final Expr[] params = expr.params();
for (int i = 0; i < params.length; i++) {
stackHeight -= params[i].type().stackHeight();
}
}
public void visitCallStaticExpr(final CallStaticExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
method.addInstruction(Opcode.opcx_invokestatic, expr.method());
// Pop each parameter off stack
final Expr[] params = expr.params();
for (int i = 0; i < params.length; i++) {
stackHeight -= params[i].type().stackHeight();
}
}
public void visitCastExpr(final CastExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
if (expr.castType().isReference()) {
method.addInstruction(Opcode.opcx_checkcast, expr.castType());
return;
}
final int opType = expr.expr().type().typeCode();
final int castType = expr.castType().typeCode();
switch (opType) {
case Type.BYTE_CODE:
case Type.SHORT_CODE:
case Type.CHARACTER_CODE:
case Type.INTEGER_CODE:
switch (castType) {
case Type.BYTE_CODE:
method.addInstruction(Opcode.opcx_i2b);
return;
case Type.SHORT_CODE:
method.addInstruction(Opcode.opcx_i2s);
return;
case Type.CHARACTER_CODE:
method.addInstruction(Opcode.opcx_i2c);
return;
case Type.INTEGER_CODE:
return;
case Type.LONG_CODE:
method.addInstruction(Opcode.opcx_i2l);
stackHeight += 1;
return;
case Type.FLOAT_CODE:
method.addInstruction(Opcode.opcx_i2f);
return;
case Type.DOUBLE_CODE:
method.addInstruction(Opcode.opcx_i2d);
stackHeight += 1;
return;
}
throw new IllegalArgumentException("Can't generate cast for type "
+ Type.getType(castType));
// new Type(castType));
case Type.LONG_CODE:
switch (castType) {
case Type.BYTE_CODE:
method.addInstruction(Opcode.opcx_l2i);
stackHeight -= 1;
method.addInstruction(Opcode.opcx_i2b);
return;
case Type.SHORT_CODE:
method.addInstruction(Opcode.opcx_l2i);
stackHeight -= 1;
method.addInstruction(Opcode.opcx_i2s);
return;
case Type.CHARACTER_CODE:
method.addInstruction(Opcode.opcx_l2i);
stackHeight -= 1;
method.addInstruction(Opcode.opcx_i2c);
return;
case Type.INTEGER_CODE:
method.addInstruction(Opcode.opcx_l2i);
stackHeight -= 1;
return;
case Type.LONG_CODE:
return;
case Type.FLOAT_CODE:
method.addInstruction(Opcode.opcx_l2f);
stackHeight -= 1;
return;
case Type.DOUBLE_CODE:
method.addInstruction(Opcode.opcx_l2d);
return;
}
throw new IllegalArgumentException("Can't generate cast for type "
+ Type.getType(castType));
// new Type(castType));
case Type.FLOAT_CODE:
switch (castType) {
case Type.BYTE_CODE:
method.addInstruction(Opcode.opcx_f2i);
method.addInstruction(Opcode.opcx_i2b);
return;
case Type.SHORT_CODE:
method.addInstruction(Opcode.opcx_f2i);
method.addInstruction(Opcode.opcx_i2s);
return;
case Type.CHARACTER_CODE:
method.addInstruction(Opcode.opcx_f2i);
method.addInstruction(Opcode.opcx_i2c);
return;
case Type.INTEGER_CODE:
method.addInstruction(Opcode.opcx_f2i);
return;
case Type.LONG_CODE:
method.addInstruction(Opcode.opcx_f2l);
stackHeight += 1;
return;
case Type.FLOAT_CODE:
return;
case Type.DOUBLE_CODE:
method.addInstruction(Opcode.opcx_f2d);
stackHeight += 1;
return;
}
throw new IllegalArgumentException("Can't generate cast for type "
+ Type.getType(castType));
// new Type(castType));
case Type.DOUBLE_CODE:
switch (castType) {
case Type.BYTE_CODE:
method.addInstruction(Opcode.opcx_d2i);
stackHeight -= 1;
method.addInstruction(Opcode.opcx_i2b);
return;
case Type.SHORT_CODE:
method.addInstruction(Opcode.opcx_d2i);
stackHeight -= 1;
method.addInstruction(Opcode.opcx_i2s);
return;
case Type.CHARACTER_CODE:
method.addInstruction(Opcode.opcx_d2i);
stackHeight -= 1;
method.addInstruction(Opcode.opcx_i2c);
return;
case Type.INTEGER_CODE:
method.addInstruction(Opcode.opcx_d2i);
stackHeight -= 1;
return;
case Type.LONG_CODE:
method.addInstruction(Opcode.opcx_d2l);
return;
case Type.FLOAT_CODE:
method.addInstruction(Opcode.opcx_d2f);
return;
case Type.DOUBLE_CODE:
return;
}
throw new IllegalArgumentException("Can't generate cast for type "
+ Type.getType(castType));
// new Type(castType));
default:
throw new IllegalArgumentException("Can't generate cast from type "
+ Type.getType(opType));
// new Type(castType));
}
}
public void visitConstantExpr(final ConstantExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
method.addInstruction(Opcode.opcx_ldc, expr.value());
stackHeight += expr.type().stackHeight();
}
public boolean nowb = false;
public void visitFieldExpr(final FieldExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
if (expr.isDef()) {
boolean UC = false; // Do we need an UC?
// Look at the FieldExpr's object for a UCExpr
Node check = expr.object();
while (check instanceof CheckExpr) {
if (check instanceof UCExpr) {
UC = true;
break;
}
final CheckExpr c = (CheckExpr) check;
check = c.expr();
}
// Do we need to perform the write barrier?
if (!UC && CodeGenerator.USE_PERSISTENT) {
/*
* System.out.println("Emitting a putfield_nowb in " +
* this.method.declaringClass().classInfo().name() + "." +
* this.method.name());
*/
nowb = true;
// I commented out the next line because it generated a compiler
// error, and I figured it was just about some unimportant
// persistance stuff --Tom
// method.addInstruction(opcx_putfield_nowb, expr.field());
} else {
method.addInstruction(Opcode.opcx_putfield, expr.field());
}
stackHeight -= 1; // object
stackHeight -= expr.type().stackHeight();
} else {
method.addInstruction(Opcode.opcx_getfield, expr.field());
stackHeight -= 1; // pop object
stackHeight += expr.type().stackHeight();
}
}
public void visitInstanceOfExpr(final InstanceOfExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
method.addInstruction(Opcode.opcx_instanceof, expr.checkType());
}
public void visitLocalExpr(final LocalExpr expr) {
genPostponed(expr);
final boolean cat2 = expr.type().isWide(); // how many stack positions
// does
// this take up?
int opcode = -1; // -1 is the flag that it hasn't yet been assigned
// a real value
if (CodeGenerator.DB_OPT_STACK) {
currentSO.infoDisplay(expr);
}
if (expr.isDef()) {
if (!CodeGenerator.OPT_STACK || currentSO.shouldStore(expr)) {
if (expr.type().isAddress()) {
opcode = Opcode.opcx_astore;
stackHeight -= 1;
} else if (expr.type().isReference()) {
opcode = Opcode.opcx_astore;
stackHeight -= 1;
} else if (expr.type().isIntegral()) {
opcode = Opcode.opcx_istore;
stackHeight -= 1;
} else if (expr.type().equals(Type.LONG)) {
opcode = Opcode.opcx_lstore;
stackHeight -= 2;
} else if (expr.type().equals(Type.FLOAT)) {
opcode = Opcode.opcx_fstore;
stackHeight -= 1;
} else if (expr.type().equals(Type.DOUBLE)) {
opcode = Opcode.opcx_dstore;
stackHeight -= 2;
} else {
throw new IllegalArgumentException(
"Can't generate code for type: " + expr.type()
+ " (expr " + expr + ")");
}
}
}
else {
if (CodeGenerator.OPT_STACK && currentSO.onStack(expr)) { // don't
// load
// if
// it's
// already
// on
// the stack
if (currentSO.shouldSwap(expr)) {
if (cat2) {
throw new IllegalArgumentException(
"Can't swap for wide expression "
+ expr.toString() + " of type "
+ expr.type().toString());
} else {
opcode = Opcode.opc_swap;
stackHeight -= 1;
}
}
} else {
if (expr.type().isReference()) {
opcode = Opcode.opcx_aload;
stackHeight += 1;
} else if (expr.type().isIntegral()) {
opcode = Opcode.opcx_iload;
stackHeight += 1;
} else if (expr.type().equals(Type.LONG)) {
opcode = Opcode.opcx_lload;
stackHeight += 2;
} else if (expr.type().equals(Type.FLOAT)) {
opcode = Opcode.opcx_fload;
stackHeight += 1;
} else if (expr.type().equals(Type.DOUBLE)) {
opcode = Opcode.opcx_dload;
stackHeight += 2;
} else {
throw new IllegalArgumentException(
"Can't generate code for type: " + expr.type()
+ " (expr " + expr + ")");
}
}
}
if (opcode == Opcode.opc_swap) {
method.addInstruction(opcode); // don't give
} else if ((opcode != -1) && !(expr.isDef())) { // if this is a load, we
// want
// the load before any dups.
method.addInstruction(opcode, new LocalVariable(expr.index()));
if (MethodEditor.OPT_STACK_2) {
method.rememberDef(expr);
}
}
if (CodeGenerator.OPT_STACK) {
// generate dups for this value on top of the stack
int dups, dup_x1s, dup_x2s;
dups = currentSO.dups(expr);
dup_x1s = currentSO.dup_x1s(expr);
dup_x2s = currentSO.dup_x2s(expr);
for (int i = 0; i < dup_x2s; i++) {
if (cat2) { // (cat2 is for wide types)
method.addInstruction(Opcode.opc_dup2_x2);
stackHeight += 2;
} else {
method.addInstruction(Opcode.opc_dup_x2);
stackHeight += 1;
}
}
for (int i = 0; i < dup_x1s; i++) {
if (cat2) {
method.addInstruction(Opcode.opc_dup2_x1);
stackHeight += 2;
} else {
method.addInstruction(Opcode.opc_dup_x1);
stackHeight += 1;
}
}
for (int i = 0; i < dups; i++) {
if (cat2) {
method.addInstruction(Opcode.opc_dup2);
stackHeight += 2;
} else {
method.addInstruction(Opcode.opc_dup);
stackHeight += 1;
}
}
}
// if we have an opcode for a def (i.e., a store), generate it
if ((opcode != -1) && expr.isDef()) {
method.addInstruction(opcode, new LocalVariable(expr.index()));
if (MethodEditor.OPT_STACK_2) {
method.rememberDef(expr);
}
}
if (CodeGenerator.OPT_STACK // if we shouldn't store,
&& !currentSO.shouldStore(expr)) { // an extra thing will be
if (cat2) { // on the stack. pop it
method.addInstruction(Opcode.opc_pop2);
stackHeight -= 2;
} else {
method.addInstruction(Opcode.opc_pop);
stackHeight -= 1;
}
}
// (if this leaves a useless dup/pop combination, let peephole fix it)
// method.addInstruction(opcode, new LocalVariable(expr.index()));
}
public void visitNegExpr(final NegExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
if (expr.type().isIntegral()) {
method.addInstruction(Opcode.opcx_ineg);
} else if (expr.type().equals(Type.FLOAT)) {
method.addInstruction(Opcode.opcx_fneg);
} else if (expr.type().equals(Type.LONG)) {
method.addInstruction(Opcode.opcx_lneg);
} else if (expr.type().equals(Type.DOUBLE)) {
method.addInstruction(Opcode.opcx_dneg);
} else {
throw new IllegalArgumentException("Can't generate code for type: "
+ expr.type() + " (expr " + expr + ")");
}
}
public void visitNewArrayExpr(final NewArrayExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
method.addInstruction(Opcode.opcx_newarray, expr.elementType());
}
public void visitNewExpr(final NewExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
method.addInstruction(Opcode.opcx_new, expr.objectType());
stackHeight += 1;
}
public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
method.addInstruction(Opcode.opcx_multianewarray,
new MultiArrayOperand(expr.elementType().arrayType(
expr.dimensions().length), expr.dimensions().length));
stackHeight -= expr.dimensions().length;
stackHeight += 1;
}
public void visitReturnAddressExpr(final ReturnAddressExpr expr) {
genPostponed(expr);
}
public void visitShiftExpr(final ShiftExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
if (expr.type().isIntegral()) {
if (expr.dir() == ShiftExpr.LEFT) {
method.addInstruction(Opcode.opcx_ishl);
stackHeight -= 1;
} else if (expr.dir() == ShiftExpr.RIGHT) {
method.addInstruction(Opcode.opcx_ishr);
stackHeight -= 1;
} else {
method.addInstruction(Opcode.opcx_iushr);
stackHeight -= 1;
}
} else if (expr.type().equals(Type.LONG)) {
if (expr.dir() == ShiftExpr.LEFT) {
method.addInstruction(Opcode.opcx_lshl);
stackHeight -= 1;
} else if (expr.dir() == ShiftExpr.RIGHT) {
method.addInstruction(Opcode.opcx_lshr);
stackHeight -= 1;
} else {
method.addInstruction(Opcode.opcx_lushr);
stackHeight -= 1;
}
} else {
throw new IllegalArgumentException("Can't generate code for type: "
+ expr.type() + " (expr " + expr + ")");
}
}
public void visitDefExpr(final DefExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
}
public void visitCatchExpr(final CatchExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
}
public void visitStackExpr(final StackExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
}
public void visitStaticFieldExpr(final StaticFieldExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
if (expr.isDef()) {
method.addInstruction(Opcode.opcx_putstatic, expr.field());
stackHeight -= expr.type().stackHeight();
} else {
method.addInstruction(Opcode.opcx_getstatic, expr.field());
stackHeight += expr.type().stackHeight();
}
}
public void visitZeroCheckExpr(final ZeroCheckExpr expr) {
expr.visitChildren(this);
genPostponed(expr);
}
private void genPostponed(final Node node) {
final Instruction inst = (Instruction) postponedInstructions.get(node);
if (inst != null) {
method.addInstruction(inst);
// Luckily, the rc and aupdate don't change the stack!
postponedInstructions.remove(node);
}
}
}