/* 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.io.*;
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.*;
/**
* <tt>ValueFolder</tt> uses a <tt>TreeVisitor</tt> to examine a
* <tt>Node</tt> to determine whether or not it can be simplified. For
* instance, redundent checks are removed and algebraic identities are
* exploited.
*
* @see SideEffectChecker
*/
class ValueFolder extends TreeVisitor {
Node node; // (New) value of the folded node
// If a value number has been reduced down to a constant number
// (ConstantExpr), this array maintains that mapping.
ResizeableArrayList values;
// Keeps track of which value numbers correspond to expressions that
// allocate new objects (NewExpr, NewArrayExpr, and NewMultiArrayExpr).
BitSet news;
// Local variable representing the this pointer
LocalExpr thisPtr;
// Used to determine whether or not a Node in the expression tree
// has side effects
SideEffectChecker sideEffects;
// Do we actually replace expressions with common value #'s? Or do
// we just calculate the value numbers.
boolean replace;
//
ArrayList clean;
/**
* Constructor.
*
* @param replace
* Do we replace values with their folded values?
* @param context
* Needed to create a <Tt>SideEffectChecker</tt>
*/
public ValueFolder(final boolean replace, final EditorContext context) {
this.node = null;
this.replace = replace;
this.clean = new ArrayList();
this.sideEffects = new SideEffectChecker(context);
this.values = new ResizeableArrayList();
this.news = new BitSet();
this.thisPtr = null;
}
/**
* Cleans up nodes that
*/
public void cleanup() {
final Iterator iter = clean.iterator();
while (iter.hasNext()) {
final Node node = (Node) iter.next();
node.cleanup();
}
}
/**
* Returns the simplified version of the most recently simplified
* <tt>Node</tt>. Returns null if no simplification occurred.
*/
public Node replacement() {
return node;
}
public void visitNode(final Node node) {
}
public void visitLocalExpr(final LocalExpr expr) {
// Determines whether or not the LocalExpr in question is the this
// pointer. If the LocalExpr resides within an InitStmt, and the
// LocalExpr is the first variable defined by the InitStmt, it is
// the this pointer.
if (thisPtr != null) {
return;
}
if (expr.parent() instanceof InitStmt) {
final InitStmt stmt = (InitStmt) expr.parent();
final MethodEditor method = stmt.block().graph().method();
if (!method.isStatic()) {
Assert.isTrue(stmt.targets().length > 0);
if (expr == stmt.targets()[0]) {
thisPtr = expr;
if (ValueFolding.DEBUG) {
System.out.println("this = " + thisPtr);
}
}
}
}
}
public void visitPhiJoinStmt(final PhiJoinStmt stmt) {
if (!(stmt.target() instanceof LocalExpr)) {
return;
}
// A PhiJoinStmt may be eliminated if it is meaningless (all of
// its operands have the same value number) or it is redundent
// (its value is already computed by another PhiJoinStmt).
final int v = stmt.valueNumber();
int ov = -1; // Operand's value number
final Iterator iter = stmt.operands().iterator();
// Examine each operand of the PhiJoinStmt.
while (iter.hasNext()) {
final Expr expr = (Expr) iter.next();
if (replace) {
sideEffects.reset();
expr.visit(sideEffects);
if (sideEffects.hasSideEffects()) {
return;
}
}
if (expr.valueNumber() == -1) {
continue;
}
if ((ov != -1) && (expr.valueNumber() != ov)) {
// At least two operands have different value numbers. The
// PhiJoinStmt cannot be simplified.
return;
}
ov = expr.valueNumber();
}
if (ov == -1) {
// We cannot replace an PhiJoinStmt with no operands
Assert.isFalse(replace && (stmt.operands().size() == 0));
ov = v;
}
// All operands have same value number or -1.
values.ensureSize(Math.max(v, ov) + 1);
final ConstantExpr value = (ConstantExpr) values.get(ov);
if (value != null) {
node = value;
values.set(v, value);
if (replace) {
stmt.block().tree().removeStmt(stmt);
}
}
}
public void visitStoreExpr(final StoreExpr expr) {
if (expr.expr() instanceof CheckExpr) {
// This makes copy propagation more effective after PRE.
// x := rc(y) --> rc(x := y)
final CheckExpr rc = (CheckExpr) expr.expr();
if (replace) {
final Node parent = expr.parent();
// x := rc(y) --> x := y
expr.visit(new ReplaceVisitor(rc, rc.expr()));
// rc(y) --> rc(x := y)
rc.visit(new ReplaceVisitor(rc.expr(), expr));
// x := rc(y) --> rc(x := y)
parent.visit(new ReplaceVisitor(expr, rc));
node = rc;
} else {
// Don't bother.
node = null;
}
return;
}
if (expr.target() instanceof LocalExpr) {
// If we're assigning into a local variable,
final int v = expr.valueNumber();
final int lv = expr.target().valueNumber();
final int rv = expr.expr().valueNumber();
int max = v;
max = Math.max(max, lv);
max = Math.max(max, rv);
values.ensureSize(max + 1);
boolean reffects = false;
if (replace) {
sideEffects.reset();
expr.expr().visit(sideEffects);
reffects = sideEffects.hasSideEffects();
}
final ConstantExpr rexpr = (ConstantExpr) values.get(rv);
if (rexpr != null) {
// The entire StoreExpr has a constant value
if (!replace) {
node = rexpr;
values.set(v, node);
} else if (!reffects && (expr.target().uses().size() == 0)) {
// Replace the rhs with constant mapped to its value number
node = new ConstantExpr(rexpr.value(), expr.type());
node.setValueNumber(v);
values.set(v, node);
expr.replaceWith(node);
}
}
}
}
public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) {
// Keep track of the value numbers of expressions that create new
// objects.
if (expr.valueNumber() != -1) {
if (ValueFolding.DEBUG) {
System.out.println("New " + expr);
}
news.set(expr.valueNumber());
}
}
public void visitNewArrayExpr(final NewArrayExpr expr) {
// Keep track of the value numbers of expressions that create new
// objects.
if (expr.valueNumber() != -1) {
if (ValueFolding.DEBUG) {
System.out.println("New " + expr);
}
news.set(expr.valueNumber());
}
}
public void visitNewExpr(final NewExpr expr) {
// Keep track of the value number of expression that create new
// objects.
if (expr.valueNumber() != -1) {
if (ValueFolding.DEBUG) {
System.out.println("New " + expr);
}
news.set(expr.valueNumber());
}
}
public void visitRCExpr(final RCExpr expr) {
boolean move = false; // Can we remove the RCExpr
final int v = expr.expr().valueNumber();
if (expr.expr() instanceof RCExpr) {
// If the expression being checked for residency is itself an
// RCExpr, then the outer one is redundent.
move = true;
if (ValueFolding.DEBUG) {
System.out.println("folding redundant rc in " + expr);
}
} else if (v != -1) {
if ((thisPtr != null) && (thisPtr.valueNumber() == v)) {
// We know that the this pointer is always resident, so we
// don't need to perform an rc on it.
move = true;
if (ValueFolding.DEBUG) {
System.out.println("folding rc(this) = " + expr);
}
} else if (news.get(v)) {
// We know that the result of a new expression is always
// resident, so we don't need to perform an rc on it.
move = true;
if (ValueFolding.DEBUG) {
System.out.println("folding rc(new) = " + expr);
}
}
}
if (move) {
node = expr.expr();
if (replace) {
// rc(this) --> this
// rc(rc(x)) --> rc(x)
node.setParent(null);
expr.replaceWith(node, false);
expr.cleanupOnly();
}
}
}
public void visitZeroCheckExpr(final ZeroCheckExpr expr) {
boolean move = false;
final int v = expr.expr().valueNumber();
if (expr.expr() instanceof ZeroCheckExpr) {
move = true;
if (ValueFolding.DEBUG) {
System.out.println("folding redundant ZeroCheck in " + expr);
}
} else if (v != -1) {
if ((thisPtr != null) && (thisPtr.valueNumber() == v)) {
// The this pointer can't be null.
move = true;
if (ValueFolding.DEBUG) {
System.out.println("folding ZeroCheck(this) = " + expr);
}
} else if (news.get(v)) {
// Newly created objects can't be null.
move = true;
if (ValueFolding.DEBUG) {
System.out.println("folding ZeroCheck(new) = " + expr);
}
} else {
// Check to see if the value number associated with the
// expression being checked for zero-ness has a constant value
// of zero.
ConstantExpr eexpr = null;
if (v < values.size()) {
eexpr = (ConstantExpr) values.get(v);
}
if (eexpr != null) {
final Object value = eexpr.value();
if (value instanceof Long) {
if (((Long) value).longValue() != 0L) {
move = true;
}
} else if ((value instanceof Byte)
|| (value instanceof Short)
|| (value instanceof Integer)) {
if (((Number) value).intValue() != 0) {
move = true;
}
} else if (value instanceof Character) {
if (((Character) value).charValue() != 0) {
move = true;
}
}
}
}
}
if (move) {
node = expr.expr();
if (replace) {
// ZeroCheck(1) --> 1
// ZeroCheck(this) --> this
// ZeroCheck(ZeroCheck(x)) --> ZeroCheck(x)
node.setParent(null);
expr.replaceWith(node, false);
expr.cleanupOnly();
}
}
}
public void visitUCExpr(final UCExpr expr) {
if (expr.expr() instanceof UCExpr) {
// Remove redundent update checks
final UCExpr uc = (UCExpr) expr.expr();
if (uc.kind() == expr.kind()) {
node = uc;
if (replace) {
// uc(uc(x)) --> uc(x)
expr.visit(new ReplaceVisitor(uc, uc.expr()));
uc.cleanupOnly();
}
}
}
}
public void visitArithExpr(final ArithExpr expr) {
if (expr.left().type().isIntegral()) {
foldArithInteger(expr);
} else if (expr.left().type().equals(Type.LONG)) {
foldArithLong(expr);
} else if (expr.left().type().equals(Type.FLOAT)) {
foldArithFloat(expr);
} else if (expr.left().type().equals(Type.DOUBLE)) {
foldArithDouble(expr);
}
}
/**
* Look for integer arithmetic identities...
*/
private void foldArithInteger(final ArithExpr expr) {
final int v = expr.valueNumber();
final int lv = expr.left().valueNumber();
final int rv = expr.right().valueNumber();
int max = v;
max = Math.max(max, lv);
max = Math.max(max, rv);
values.ensureSize(max + 1);
ConstantExpr lexpr = null;
ConstantExpr rexpr = null;
if ((0 <= lv) && (0 <= lv) && (lv < values.size())) {
lexpr = (ConstantExpr) values.get(lv);
}
if ((0 <= rv) && (0 <= rv) && (rv < values.size())) {
rexpr = (ConstantExpr) values.get(rv);
}
boolean leffects = false;
boolean reffects = false;
if (replace) {
sideEffects.reset();
expr.left().visit(sideEffects);
leffects = sideEffects.hasSideEffects();
sideEffects.reset();
expr.right().visit(sideEffects);
reffects = sideEffects.hasSideEffects();
}
if ((lexpr != null) && (rexpr != null) && !leffects && !reffects) {
// Okay, both of the ArithExpr's operands evaluate to constants
// and there are no side effects. We may be able to exploit
// various algebraic identites.
Integer value = null;
final int lval = ((Number) lexpr.value()).intValue();
final int rval = ((Number) rexpr.value()).intValue();
switch (expr.operation()) {
case ArithExpr.ADD:
value = new Integer(lval + rval);
break;
case ArithExpr.AND:
value = new Integer(lval & rval);
break;
case ArithExpr.DIV:
if (rval != 0) {
value = new Integer(lval / rval);
}
break;
case ArithExpr.MUL:
value = new Integer(lval * rval);
break;
case ArithExpr.IOR:
value = new Integer(lval | rval);
break;
case ArithExpr.REM:
if (rval != 0) {
value = new Integer(lval % rval);
}
break;
case ArithExpr.SUB:
value = new Integer(lval - rval);
break;
case ArithExpr.XOR:
value = new Integer(lval ^ rval);
break;
default:
break;
}
if (value != null) {
node = new ConstantExpr(value, expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
}
} else if ((lexpr == null) && (rexpr != null) && !reffects) {
// Only the right operand evaluates to a constant...
final int rval = ((Number) rexpr.value()).intValue();
switch (rval) {
case 0:
// x + 0 = x
// x - 0 = x
// x | 0 = x
// x * 0 = 0
// x & 0 = 0
switch (expr.operation()) {
case ArithExpr.ADD:
case ArithExpr.SUB:
case ArithExpr.IOR:
node = expr.left();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.right().cleanup();
expr.cleanupOnly();
}
break;
case ArithExpr.MUL:
case ArithExpr.AND:
node = new ConstantExpr(new Integer(0), expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
break;
}
break;
case 1:
// x * 1 = x
// x / 1 = x
// x % 1 = 0
switch (expr.operation()) {
case ArithExpr.MUL:
case ArithExpr.DIV:
node = expr.left();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.right().cleanup();
expr.cleanupOnly();
}
break;
case ArithExpr.REM:
node = new ConstantExpr(new Integer(0), expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
break;
}
break;
case -1:
// x * -1 = -x
// x / -1 = -x
switch (expr.operation()) {
case ArithExpr.MUL:
case ArithExpr.DIV:
if (replace) {
expr.left().setParent(null);
node = new NegExpr(expr.left(), expr.type());
node.setValueNumber(v);
expr.replaceWith(node, false);
expr.right().cleanup();
expr.cleanupOnly();
} else {
node = new NegExpr((Expr) expr.left().clone(), expr
.type());
node.setValueNumber(v);
clean.add(node);
}
break;
}
break;
}
} else if ((lexpr != null) && (rexpr == null) && !leffects) {
// Only left operand resolves to a constant value...
final int lval = ((Number) lexpr.value()).intValue();
switch (lval) {
case 0:
// 0 + x = x
// 0 - x = -x
// 0 | x = x
// 0 * x = 0
// 0 & x = 0
switch (expr.operation()) {
case ArithExpr.ADD:
case ArithExpr.IOR:
node = expr.right();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
}
break;
case ArithExpr.SUB:
if (replace) {
expr.right().setParent(null);
node = new NegExpr(expr.right(), expr.type());
node.setValueNumber(v);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
} else {
node = new NegExpr((Expr) expr.right().clone(), expr
.type());
node.setValueNumber(v);
clean.add(node);
}
break;
case ArithExpr.MUL:
case ArithExpr.AND:
node = new ConstantExpr(new Integer(0), expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
break;
}
break;
case 1:
// 1 * x = x
switch (expr.operation()) {
case ArithExpr.MUL:
node = expr.right();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
}
break;
}
break;
case -1:
// -1 * x = -x
switch (expr.operation()) {
case ArithExpr.MUL:
if (replace) {
expr.right().setParent(null);
node = new NegExpr(expr.right(), expr.type());
node.setValueNumber(v);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
} else {
node = new NegExpr((Expr) expr.right().clone(), expr
.type());
node.setValueNumber(v);
clean.add(node);
}
break;
}
break;
}
}
}
/**
* Look for long arithmetic indentities...
*/
private void foldArithLong(final ArithExpr expr) {
final int v = expr.valueNumber();
final int lv = expr.left().valueNumber();
final int rv = expr.right().valueNumber();
int max = v;
max = Math.max(max, lv);
max = Math.max(max, rv);
values.ensureSize(max + 1);
ConstantExpr lexpr = null;
ConstantExpr rexpr = null;
if ((0 <= lv) && (lv < values.size())) {
lexpr = (ConstantExpr) values.get(lv);
}
if ((0 <= rv) && (rv < values.size())) {
rexpr = (ConstantExpr) values.get(rv);
}
boolean leffects = false;
boolean reffects = false;
if (replace) {
sideEffects.reset();
expr.left().visit(sideEffects);
leffects = sideEffects.hasSideEffects();
sideEffects.reset();
expr.right().visit(sideEffects);
reffects = sideEffects.hasSideEffects();
}
if ((lexpr != null) && (rexpr != null) && !leffects && !reffects) {
Number value = null;
final long lval = ((Long) lexpr.value()).longValue();
final long rval = ((Long) rexpr.value()).longValue();
switch (expr.operation()) {
case ArithExpr.ADD:
value = new Long(lval + rval);
break;
case ArithExpr.AND:
value = new Long(lval & rval);
break;
case ArithExpr.DIV:
if (rval != 0) {
value = new Long(lval / rval);
}
break;
case ArithExpr.MUL:
value = new Long(lval * rval);
break;
case ArithExpr.IOR:
value = new Long(lval | rval);
break;
case ArithExpr.REM:
if (rval != 0L) {
value = new Long(lval % rval);
}
break;
case ArithExpr.SUB:
value = new Long(lval - rval);
break;
case ArithExpr.XOR:
value = new Long(lval ^ rval);
break;
case ArithExpr.CMP:
if (lval > rval) {
value = new Integer(1);
} else if (lval < rval) {
value = new Integer(-1);
} else {
value = new Integer(0);
}
break;
default:
break;
}
if (value != null) {
node = new ConstantExpr(value, expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
}
} else if ((lexpr == null) && (rexpr != null)) {
final long rval = ((Long) rexpr.value()).longValue();
if (reffects) {
return;
}
if (rval == 0L) {
// x + 0 = x
// x - 0 = x
// x | 0 = x
// x * 0 = 0
// x & 0 = 0
switch (expr.operation()) {
case ArithExpr.ADD:
case ArithExpr.SUB:
case ArithExpr.IOR:
node = expr.left();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.right().cleanup();
expr.cleanupOnly();
}
break;
case ArithExpr.MUL:
case ArithExpr.AND:
node = new ConstantExpr(new Long(0L), expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
break;
}
} else if (rval == 1L) {
// x * 1 = x
// x / 1 = x
// x % 1 = 0
switch (expr.operation()) {
case ArithExpr.MUL:
case ArithExpr.DIV:
node = expr.left();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.right().cleanup();
expr.cleanupOnly();
}
break;
case ArithExpr.REM:
node = new ConstantExpr(new Long(0L), expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
break;
}
} else if (rval == -1L) {
// x * -1 = -x
// x / -1 = -x
switch (expr.operation()) {
case ArithExpr.MUL:
case ArithExpr.DIV:
if (replace) {
expr.left().setParent(null);
node = new NegExpr(expr.left(), Type.LONG);
node.setValueNumber(v);
expr.replaceWith(node, false);
expr.right().cleanup();
expr.cleanupOnly();
} else {
node = new NegExpr((Expr) expr.left().clone(),
Type.LONG);
node.setValueNumber(v);
clean.add(node);
}
break;
}
}
} else if ((lexpr != null) && (rexpr == null)) {
final long lval = ((Long) lexpr.value()).longValue();
if (lval == 0L) {
// 0 + x = x
// 0 - x = -x
// 0 | x = x
// 0 * x = 0
// 0 & x = 0
switch (expr.operation()) {
case ArithExpr.ADD:
case ArithExpr.IOR:
node = expr.right();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
}
break;
case ArithExpr.SUB:
if (replace) {
expr.right().setParent(null);
node = new NegExpr(expr.right(), Type.LONG);
node.setValueNumber(v);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
} else {
node = new NegExpr((Expr) expr.right().clone(),
Type.LONG);
node.setValueNumber(v);
clean.add(node);
}
break;
case ArithExpr.MUL:
case ArithExpr.AND:
node = new ConstantExpr(new Long(0L), expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
break;
}
} else if (lval == 1L) {
// 1 * x = x
switch (expr.operation()) {
case ArithExpr.MUL:
node = expr.right();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
}
break;
}
} else if (lval == -1L) {
// -1 * x = -x
switch (expr.operation()) {
case ArithExpr.MUL:
if (replace) {
expr.right().setParent(null);
node = new NegExpr(expr.right(), Type.LONG);
node.setValueNumber(v);
expr.replaceWith(node, false);
expr.left().cleanup();
expr.cleanupOnly();
} else {
node = new NegExpr((Expr) expr.right().clone(),
Type.LONG);
node.setValueNumber(v);
clean.add(node);
}
break;
}
}
}
}
/**
* Look for float arithmetic identities...
*/
private void foldArithFloat(final ArithExpr expr) {
final int v = expr.valueNumber();
final int lv = expr.left().valueNumber();
final int rv = expr.right().valueNumber();
int max = v;
max = Math.max(max, lv);
max = Math.max(max, rv);
values.ensureSize(max + 1);
ConstantExpr lexpr = null;
ConstantExpr rexpr = null;
if ((0 <= lv) && (lv < values.size())) {
lexpr = (ConstantExpr) values.get(lv);
}
if ((0 <= rv) && (rv < values.size())) {
rexpr = (ConstantExpr) values.get(rv);
}
if ((lexpr == null) || (rexpr == null)) {
return;
}
final Float rvalue = (Float) rexpr.value();
final Float lvalue = (Float) lexpr.value();
if (lvalue.isNaN() || lvalue.isInfinite()) {
return;
}
if (rvalue.isNaN() || rvalue.isInfinite()) {
return;
}
boolean leffects = false;
boolean reffects = false;
if (replace) {
sideEffects.reset();
expr.left().visit(sideEffects);
leffects = sideEffects.hasSideEffects();
sideEffects.reset();
expr.right().visit(sideEffects);
reffects = sideEffects.hasSideEffects();
if (leffects || reffects) {
return;
}
}
// Can't fold (x op C) = y since x may be NaN or infinite
// or +/-0.0.
Number value = null;
final float lval = lvalue.floatValue();
final float rval = rvalue.floatValue();
switch (expr.operation()) {
case ArithExpr.ADD:
value = new Float(lval + rval);
break;
case ArithExpr.DIV:
value = new Float(lval / rval);
break;
case ArithExpr.MUL:
value = new Float(lval * rval);
break;
case ArithExpr.REM:
value = new Float(lval % rval);
break;
case ArithExpr.SUB:
value = new Float(lval - rval);
break;
case ArithExpr.CMP:
if (lval > rval) {
value = new Integer(1);
} else if (lval < rval) {
value = new Integer(-1);
} else {
value = new Integer(0);
}
break;
default:
break;
}
if (value != null) {
node = new ConstantExpr(value, expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
}
}
/**
* Look for double arithmetic identities...
*/
private void foldArithDouble(final ArithExpr expr) {
final int v = expr.valueNumber();
final int lv = expr.left().valueNumber();
final int rv = expr.right().valueNumber();
int max = v;
max = Math.max(max, lv);
max = Math.max(max, rv);
values.ensureSize(max + 1);
ConstantExpr lexpr = null;
ConstantExpr rexpr = null;
if ((0 <= lv) && (lv < values.size())) {
lexpr = (ConstantExpr) values.get(lv);
}
if ((0 <= rv) && (rv < values.size())) {
rexpr = (ConstantExpr) values.get(rv);
}
if ((lexpr == null) || (rexpr == null)) {
return;
}
final Double rvalue = (Double) rexpr.value();
final Double lvalue = (Double) lexpr.value();
if (lvalue.isNaN() || lvalue.isInfinite()) {
return;
}
if (rvalue.isNaN() || rvalue.isInfinite()) {
return;
}
boolean leffects = false;
boolean reffects = false;
if (replace) {
sideEffects.reset();
expr.left().visit(sideEffects);
leffects = sideEffects.hasSideEffects();
sideEffects.reset();
expr.right().visit(sideEffects);
reffects = sideEffects.hasSideEffects();
if (leffects || reffects) {
return;
}
}
// Can't fold (x op C) = y since x may be NaN or infinite
// or +/-0.0.
Number value = null;
final double lval = lvalue.doubleValue();
final double rval = rvalue.doubleValue();
switch (expr.operation()) {
case ArithExpr.ADD:
value = new Double(lval + rval);
break;
case ArithExpr.DIV:
value = new Double(lval / rval);
break;
case ArithExpr.MUL:
value = new Double(lval * rval);
break;
case ArithExpr.REM:
value = new Double(lval % rval);
break;
case ArithExpr.SUB:
value = new Double(lval - rval);
break;
case ArithExpr.CMP:
if (lval > rval) {
value = new Integer(1);
} else if (lval < rval) {
value = new Integer(-1);
} else {
value = new Integer(0);
}
break;
default:
break;
}
if (value != null) {
node = new ConstantExpr(value, expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
}
}
public void visitCastExpr(final CastExpr expr) {
// Note: we can't fold i2b, i2c, i2s, i2l, i2f, f2i, ...
// We only fold (String) "" and (C) null.
final int v = expr.valueNumber();
final int ev = expr.expr().valueNumber();
values.ensureSize(Math.max(v, ev) + 1);
ConstantExpr eexpr = null;
if ((0 <= ev) && (ev < values.size())) {
eexpr = (ConstantExpr) values.get(ev);
}
if (eexpr == null) {
return;
}
if (replace) {
sideEffects.reset();
expr.expr().visit(sideEffects);
final boolean effects = sideEffects.hasSideEffects();
if (effects) {
return;
}
}
final Object evalue = eexpr.value();
if ((evalue instanceof String) && expr.castType().equals(Type.STRING)) {
// The ConstantExpr must be ""
node = new ConstantExpr(evalue, expr.castType());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
return;
}
if (expr.castType().isReference()) {
if ((evalue == null) && expr.castType().isReference()) {
// The ConstantExpr is null
node = new ConstantExpr(null, expr.castType());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
}
return;
}
}
public void visitNegExpr(final NegExpr expr) {
final int v = expr.valueNumber();
final int ev = expr.expr().valueNumber();
values.ensureSize(Math.max(v, ev) + 1);
ConstantExpr eexpr = null;
if ((0 <= ev) && (ev < values.size())) {
eexpr = (ConstantExpr) values.get(ev);
}
if (eexpr != null) {
// If the operand of the NegExpr is a constant value, simply
// replace the constant with its negation and remove the NegExpr.
final Number evalue = (Number) eexpr.value();
boolean eeffects = false;
if (replace) {
sideEffects.reset();
expr.expr().visit(sideEffects);
eeffects = sideEffects.hasSideEffects();
}
if (!eeffects) {
Number value = null;
if (evalue instanceof Integer) {
value = new Integer(-evalue.intValue());
} else if (value instanceof Long) {
value = new Long(-evalue.longValue());
} else if (value instanceof Float) {
value = new Float(-evalue.floatValue());
} else if (value instanceof Double) {
value = new Double(-evalue.doubleValue());
}
if (value != null) {
node = new ConstantExpr(value, expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
return;
}
}
}
if (expr.expr() instanceof NegExpr) {
// -(-x) --> x
final NegExpr neg = (NegExpr) expr.expr();
node = neg.expr();
if (replace) {
expr.parent().visit(new ReplaceVisitor(expr, node));
expr.cleanupOnly();
neg.cleanupOnly();
}
}
}
public void visitShiftExpr(final ShiftExpr expr) {
// Exploit shifting zero bits or shifting zero
final int v = expr.valueNumber();
final int ev = expr.expr().valueNumber();
final int bv = expr.bits().valueNumber();
int max = v;
max = Math.max(max, ev);
max = Math.max(max, bv);
values.ensureSize(max + 1);
ConstantExpr eexpr = null;
ConstantExpr bexpr = null;
if ((0 <= ev) && (ev < values.size())) {
eexpr = (ConstantExpr) values.get(ev);
}
if ((0 <= bv) && (bv < values.size())) {
bexpr = (ConstantExpr) values.get(bv);
}
Object evalue = null;
Object bvalue = null;
boolean eeffects = false;
boolean beffects = false;
if (eexpr != null) {
evalue = eexpr.value();
}
if (bexpr != null) {
bvalue = bexpr.value();
}
if (replace) {
sideEffects.reset();
expr.expr().visit(sideEffects);
eeffects = sideEffects.hasSideEffects();
sideEffects.reset();
expr.bits().visit(sideEffects);
beffects = sideEffects.hasSideEffects();
}
if ((eexpr == null) && (bexpr != null)) {
if (bvalue.equals(new Integer(0)) || bvalue.equals(new Long(0))) {
// x << 0 = x
// x >> 0 = x
// x >>> 0 = x
if (!beffects) {
node = expr.expr();
if (replace) {
node.setParent(null);
expr.replaceWith(node, false);
expr.bits().cleanup();
expr.cleanupOnly();
}
}
}
return;
}
if (beffects) {
return;
}
Object value = null;
if (evalue instanceof Integer) {
final int eval = ((Integer) evalue).intValue();
if (eval == 0) {
// 0 << x = 0
// 0 >> x = 0
// 0 >>> x = 0
value = evalue;
} else if (bvalue instanceof Integer) {
if (replace && eeffects) {
return;
}
final int bval = ((Integer) bvalue).intValue();
switch (expr.dir()) {
case ShiftExpr.LEFT:
value = new Integer(eval << bval);
break;
case ShiftExpr.RIGHT:
value = new Integer(eval >> bval);
break;
case ShiftExpr.UNSIGNED_RIGHT:
value = new Integer(eval >>> bval);
break;
}
}
} else if (evalue instanceof Long) {
final long eval = ((Long) evalue).longValue();
if (eval == 0) {
// 0 << x = 0
// 0 >> x = 0
// 0 >>> x = 0
value = evalue;
} else if (bvalue instanceof Integer) {
if (replace && eeffects) {
return;
}
final int bval = ((Integer) bvalue).intValue();
switch (expr.dir()) {
case ShiftExpr.LEFT:
value = new Long(eval << bval);
break;
case ShiftExpr.RIGHT:
value = new Long(eval >> bval);
break;
case ShiftExpr.UNSIGNED_RIGHT:
value = new Long(eval >>> bval);
break;
}
}
}
if (value != null) {
node = new ConstantExpr(value, expr.type());
node.setValueNumber(v);
values.set(v, node);
if (replace) {
expr.replaceWith(node);
}
}
}
public void visitIfZeroStmt(final IfZeroStmt stmt) {
// If the expression being compared to zero evaluates to a
// constant, then try to exploit this fact.
final Block block = stmt.block();
final FlowGraph cfg = block.graph();
final int v = stmt.valueNumber();
final int ev = stmt.expr().valueNumber();
values.ensureSize(Math.max(ev, v) + 1);
ConstantExpr eexpr = null;
if ((0 <= ev) && (ev < values.size())) {
eexpr = (ConstantExpr) values.get(ev);
}
if (eexpr == null) {
return;
}
final Object evalue = eexpr.value();
boolean eeffects = false;
if (replace) {
sideEffects.reset();
stmt.expr().visit(sideEffects);
eeffects = sideEffects.hasSideEffects();
if (eeffects) {
return;
}
}
JumpStmt jump;
if (evalue instanceof Integer) {
final int eval = ((Integer) evalue).intValue();
boolean result;
switch (stmt.comparison()) {
case IfStmt.EQ:
result = eval == 0;
break;
case IfStmt.NE:
result = eval != 0;
break;
case IfStmt.GT:
result = eval > 0;
break;
case IfStmt.GE:
result = eval >= 0;
break;
case IfStmt.LT:
result = eval < 0;
break;
case IfStmt.LE:
result = eval <= 0;
break;
default:
throw new RuntimeException();
}
if (result) {
// Result is always true, replace IfZeroStmt with an
// unconditional jump to the true target.
jump = new GotoStmt(stmt.trueTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.falseTarget());
}
} else {
// Result is always false, replace IfZeroStmt with an
// unconditional jump to the false target.
jump = new GotoStmt(stmt.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.trueTarget());
}
}
} else if (evalue == null) {
// The expression always evaluates to null
switch (stmt.comparison()) {
case IfStmt.EQ:
// Always jump to true target
jump = new GotoStmt(stmt.trueTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.falseTarget());
}
break;
case IfStmt.NE:
// Always jump to false target
jump = new GotoStmt(stmt.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.trueTarget());
}
break;
default:
throw new RuntimeException();
}
}
}
public void visitIfCmpStmt(final IfCmpStmt stmt) {
final Block block = stmt.block();
final FlowGraph cfg = block.graph();
final int v = stmt.valueNumber();
final int lv = stmt.left().valueNumber();
final int rv = stmt.right().valueNumber();
int max = v;
max = Math.max(max, lv);
max = Math.max(max, rv);
values.ensureSize(max + 1);
ConstantExpr lexpr = null;
ConstantExpr rexpr = null;
if ((0 <= lv) && (lv < values.size())) {
lexpr = (ConstantExpr) values.get(lv);
}
if ((0 <= rv) && (rv < values.size())) {
rexpr = (ConstantExpr) values.get(rv);
}
Object lvalue = null;
Object rvalue = null;
if (lexpr != null) {
lvalue = lexpr.value();
}
if (rexpr != null) {
rvalue = rexpr.value();
}
boolean leffects = false;
boolean reffects = false;
if (replace) {
sideEffects.reset();
stmt.left().visit(sideEffects);
leffects = sideEffects.hasSideEffects();
sideEffects.reset();
stmt.right().visit(sideEffects);
reffects = sideEffects.hasSideEffects();
}
if ((lvalue instanceof Integer) && !leffects) {
final int lval = ((Integer) lvalue).intValue();
if ((lval == 0) && !((rvalue instanceof Integer) || reffects)) {
// If two integers are being compared and the left operand is
// zero, then we can replace the IfCmpStmt with a IfZeroStmt.
int cmp;
switch (stmt.comparison()) {
case IfStmt.EQ:
cmp = IfStmt.EQ;
break;
case IfStmt.NE:
cmp = IfStmt.NE;
break;
case IfStmt.LT:
cmp = IfStmt.GT;
break;
case IfStmt.LE:
cmp = IfStmt.GE;
break;
case IfStmt.GT:
cmp = IfStmt.LT;
break;
case IfStmt.GE:
cmp = IfStmt.LE;
break;
default:
throw new RuntimeException();
}
if (replace) {
final JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt
.right().clone(), stmt.trueTarget(), stmt
.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
stmt.replaceWith(node);
} else {
// Why bother! -- Nate
// Why ask why! -- Dave
node = null;
}
return;
}
}
if ((rvalue instanceof Integer) && !reffects) {
final int rval = ((Integer) rvalue).intValue();
if ((rval == 0) && !((lvalue instanceof Integer) || leffects)) {
// If IfCmpStmt compares two integers and the right operand is
// zero, then replace the IfCmpStmt with an IfZeroStmt.
final int cmp = stmt.comparison();
if (replace) {
final JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt
.left().clone(), stmt.trueTarget(), stmt
.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
stmt.replaceWith(node);
} else {
// Why bother! -- Cut and paste! Way to go Nate!
node = null;
}
return;
}
}
if ((lexpr != null) && (lvalue == null) && !leffects) {
if ((rexpr == null) || (rvalue != null) || reffects) {
// Left operand evaluates to null. Replace IfCmpStmt with an
// IfZeroStmt.
final int cmp = stmt.comparison();
if (replace) {
final JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt
.right().clone(), stmt.trueTarget(), stmt
.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
stmt.replaceWith(node);
} else {
// Why bother!
node = null;
}
return;
}
}
if ((rexpr != null) && (rvalue == null) && !reffects) {
if ((lexpr == null) || (lvalue != null) || leffects) {
// The right operand evaluates to null. Replace IfCmpStmt
// with an IfZeroStmt. Note that we do not need to mess with
// operators because if the lhs is being compared against
// null, it must be a reference type and the only operators
// are EQ and NE.
final int cmp = stmt.comparison();
if (replace) {
final JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt
.left().clone(), stmt.trueTarget(), stmt
.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
stmt.replaceWith(node);
} else {
// Why bother!
node = null;
}
return;
}
}
if (leffects || reffects) {
return;
}
if ((lexpr == null) || (rexpr == null)) {
return;
}
JumpStmt jump;
if ((lvalue instanceof Integer) && (rvalue instanceof Integer)) {
// Both operands evaluate to non-zero integers, evaluate the
// comparison and go from there.
final int lval = ((Integer) lvalue).intValue();
final int rval = ((Integer) rvalue).intValue();
boolean result;
switch (stmt.comparison()) {
case IfStmt.EQ:
result = lval == rval;
break;
case IfStmt.NE:
result = lval != rval;
break;
case IfStmt.GT:
result = lval > rval;
break;
case IfStmt.GE:
result = lval >= rval;
break;
case IfStmt.LT:
result = lval < rval;
break;
case IfStmt.LE:
result = lval <= rval;
break;
default:
throw new RuntimeException();
}
if (result) {
jump = new GotoStmt(stmt.trueTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.falseTarget());
}
} else {
jump = new GotoStmt(stmt.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.trueTarget());
}
}
} else if ((lvalue == null) && (rvalue == null)) {
switch (stmt.comparison()) {
case IfStmt.EQ:
jump = new GotoStmt(stmt.trueTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.falseTarget());
}
break;
case IfStmt.NE:
jump = new GotoStmt(stmt.falseTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
cfg.removeEdge(block, stmt.trueTarget());
}
break;
default:
throw new RuntimeException();
}
}
}
public void visitSwitchStmt(final SwitchStmt stmt) {
// If the index of the SwitchStmt evaluates to a constant value,
// then always take that target (may be the default target).
// Replace the SwitchStmt with a GotoStmt.
final Block block = stmt.block();
final FlowGraph cfg = block.graph();
final int v = stmt.valueNumber();
final int iv = stmt.index().valueNumber();
values.ensureSize(Math.max(v, iv) + 1);
ConstantExpr iexpr = null;
if ((0 <= iv) && (iv < values.size())) {
iexpr = (ConstantExpr) values.get(iv);
}
boolean ieffects = false;
if (replace) {
sideEffects.reset();
stmt.index().visit(sideEffects);
ieffects = sideEffects.hasSideEffects();
if (ieffects) {
return;
}
}
if (iexpr == null) {
return;
}
if (!(iexpr.value() instanceof Integer)) {
return;
}
JumpStmt jump;
final Integer ivalue = (Integer) iexpr.value();
final int ival = ivalue.intValue();
boolean useDefault = true;
for (int i = 0; i < stmt.values().length; i++) {
if (stmt.values()[i] == ival) {
jump = new GotoStmt(stmt.targets()[i]);
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
}
useDefault = false;
} else {
// Definitely not to this target.
if (replace) {
cfg.removeEdge(block, stmt.targets()[i]);
}
}
}
if (useDefault) {
jump = new GotoStmt(stmt.defaultTarget());
jump.catchTargets().addAll(stmt.catchTargets());
node = jump;
node.setValueNumber(v);
if (replace) {
stmt.replaceWith(node);
}
} else {
// Definitely not to the default target.
if (replace) {
cfg.removeEdge(block, stmt.defaultTarget());
}
}
}
void printValueNumbers(final PrintWriter pw) {
if (pw == null) {
return;
}
final Iterator iter = values.iterator();
pw.println("Value Numbers mapped to constants\n");
for (int i = 0; iter.hasNext(); i++) {
final Object o = iter.next();
if (o != null) {
pw.println(i + " -> " + o);
}
}
}
}