Revision: 9057
Author: [email protected]
Date: Wed Oct 13 14:39:37 2010
Log: DeadCodeElimination should only run once.
Changes to DeadCodeElimination to ensure that almost all of the
optimizations performed can be done in a single
pass. I only know of one remaining case where running the visitor twice
can result in additional changes: the
optimization performed in endVisit(JNewInstance) involving ignored new
operations to empty constructor calls.
Sometimes the constructor becomes empty later in the optimization pass, and
earlier calls aren't removed. This
seems to be a very rare case, though.
http://gwt-code-reviews.appspot.com/983802/show
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=9057
Modified:
/trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Simplifier.java
/trunk/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java Tue Oct
12 13:20:21 2010
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JModVisitor.java Wed Oct
13 14:39:37 2010
@@ -29,7 +29,7 @@
* Context for traversing a mutable list.
*/
@SuppressWarnings("unchecked")
- private class ListContext<T extends JNode> extends VisitorContext {
+ private class ListContext<T extends JNode> implements Context {
int index;
final List<T> list;
boolean removed;
@@ -50,13 +50,13 @@
public void insertAfter(JNode node) {
checkRemoved();
list.add(index + 1, (T) node);
- numMods++;
+ ++numVisitorChanges;
}
public void insertBefore(JNode node) {
checkRemoved();
list.add(index++, (T) node);
- numMods++;
+ ++numVisitorChanges;
}
public boolean isLvalue() {
@@ -67,7 +67,7 @@
checkState();
list.remove(index--);
removed = true;
- numMods++;
+ ++numVisitorChanges;
}
public void replaceMe(JNode node) {
@@ -75,7 +75,7 @@
checkReplacement(list.get(index), node);
list.set(index, (T) node);
replaced = true;
- numMods++;
+ ++numVisitorChanges;
}
/**
@@ -87,7 +87,6 @@
removed = replaced = false;
list.get(index).traverse(JModVisitor.this, this);
}
- JModVisitor.this.numVisitorChanges += numMods;
return list;
} catch (Throwable e) {
throw translateException(list.get(index), e);
@@ -112,7 +111,7 @@
* Context for traversing an immutable list.
*/
@SuppressWarnings("unchecked")
- private class ListContextImmutable<T extends JNode> extends
VisitorContext {
+ private class ListContextImmutable<T extends JNode> implements Context {
int index;
List<T> list;
boolean removed;
@@ -133,13 +132,13 @@
public void insertAfter(JNode node) {
checkRemoved();
list = Lists.add(list, index + 1, (T) node);
- numMods++;
+ ++numVisitorChanges;
}
public void insertBefore(JNode node) {
checkRemoved();
list = Lists.add(list, index++, (T) node);
- numMods++;
+ ++numVisitorChanges;
}
public boolean isLvalue() {
@@ -150,7 +149,7 @@
checkState();
list = Lists.remove(list, index--);
removed = true;
- numMods++;
+ ++numVisitorChanges;
}
public void replaceMe(JNode node) {
@@ -158,7 +157,7 @@
checkReplacement(list.get(index), node);
list = Lists.set(list, index, (T) node);
replaced = true;
- numMods++;
+ ++numVisitorChanges;
}
/**
@@ -170,7 +169,6 @@
removed = replaced = false;
list.get(index).traverse(JModVisitor.this, this);
}
- numVisitorChanges += numMods;
return list;
} catch (Throwable e) {
throw translateException(list.get(index), e);
@@ -202,7 +200,7 @@
}
}
- private static class NodeContext extends VisitorContext {
+ private class NodeContext implements Context {
boolean canRemove;
JNode node;
boolean replaced;
@@ -237,7 +235,7 @@
}
this.node = null;
- numMods++;
+ ++numVisitorChanges;
}
public void replaceMe(JNode node) {
@@ -247,17 +245,9 @@
checkReplacement(this.node, node);
this.node = node;
replaced = true;
- numMods++;
+ ++numVisitorChanges;
}
}
-
- private abstract static class VisitorContext implements Context {
- int numMods = 0;
-
- public int getNumMods() {
- return numMods;
- }
- }
protected static void checkReplacement(JNode origNode, JNode newNode) {
if (newNode == null) {
@@ -282,7 +272,6 @@
try {
ctx.node = node;
traverse(node, ctx);
- numVisitorChanges += ctx.getNumMods();
return ctx.node;
} catch (Throwable e) {
throw translateException(node, e);
@@ -302,7 +291,6 @@
ctx.replaced = false;
}
}
- numVisitorChanges += ctx.getNumMods();
} catch (Throwable e) {
throw translateException(ctx.node, e);
}
@@ -321,7 +309,6 @@
ctx.replaced = false;
}
}
- numVisitorChanges += ctx.getNumMods();
return list;
} catch (Throwable e) {
throw translateException(ctx.node, e);
@@ -334,7 +321,6 @@
try {
ctx.node = expr;
traverse(expr, ctx);
- numVisitorChanges += ctx.getNumMods();
return (JExpression) ctx.node;
} catch (Throwable e) {
throw translateException(expr, e);
@@ -369,15 +355,7 @@
* Used to determine when the optimization pass can exit.
*/
protected void madeChanges() {
- numVisitorChanges++;
- }
-
- /**
- * Call this method to indicate that a visitor has made multiple changes
to
- * the tree. Used to determine when the optimization pass can exit.
- */
- protected void madeChanges(int numMods) {
- numVisitorChanges += numMods;
+ ++numVisitorChanges;
}
protected void traverse(JNode node, Context context) {
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
Tue Oct 12 13:20:21 2010
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
Wed Oct 13 14:39:37 2010
@@ -77,17 +77,28 @@
import java.util.Set;
/**
- * Attempts to remove dead code.
+ * Removes certain kinds of dead code, and simplifies certain expressions.
This
+ * pass focuses on intraprocedural optimizations, that is, optimizations
that
+ * can be performed inside of a single method body (and usually a single
+ * expression) rather than global transformations. Global optimizations
done in
+ * other passes will feed into this, however.
*/
public class DeadCodeElimination {
/**
* Eliminates dead or unreachable code when possible, and makes local
* simplifications like changing "<code>x || true</code>"
to "<code>x</code>".
*
+ * This visitor should perform all of its optimizations in a single pass.
+ * Except in rare cases, running this pass multiple times should produce
no
+ * changes after the first pass. The only currently known exception to
this
+ * rule is in {...@link #endVisit(JNewInstance, Context)}, where the target
+ * constructor may be non-empty at the beginning of DCE and become empty
+ * during the run, which potentially unlocks optimizations at call sites.
+ *
* TODO: leverage ignoring expression output more to remove intermediary
* operations in favor of pure side effects.
*
- * TODO(spoon): move more simplifications into methods like
+ * TODO: move more simplifications into methods like
* {...@link #cast(JExpression, SourceInfo, JType, JExpression)
simplifyCast}, so
* that more simplifications can be made on a single pass through a tree.
*/
@@ -131,10 +142,10 @@
}
switch (op) {
case AND:
- shortCircuitAnd(lhs, rhs, ctx);
+ maybeReplaceMe(x, simplifier.shortCircuitAnd(x, null, lhs, rhs),
ctx);
break;
case OR:
- shortCircuitOr(lhs, rhs, ctx);
+ maybeReplaceMe(x, simplifier.shortCircuitOr(x, null, lhs, rhs),
ctx);
break;
case BIT_XOR:
simplifyXor(lhs, rhs, ctx);
@@ -251,20 +262,14 @@
@Override
public void endVisit(JCastOperation x, Context ctx) {
- JExpression updated = simplifier.cast(x, x.getSourceInfo(),
- x.getCastType(), x.getExpr());
- if (updated != x) {
- ctx.replaceMe(updated);
- }
+ maybeReplaceMe(x, simplifier.cast(x, x.getSourceInfo(),
x.getCastType(),
+ x.getExpr()), ctx);
}
@Override
public void endVisit(JConditional x, Context ctx) {
- JExpression updated = simplifier.conditional(x, x.getSourceInfo(),
- x.getType(), x.getIfTest(), x.getThenExpr(), x.getElseExpr());
- if (updated != x) {
- ctx.replaceMe(updated);
- }
+ maybeReplaceMe(x, simplifier.conditional(x, x.getSourceInfo(),
+ x.getType(), x.getIfTest(), x.getThenExpr(), x.getElseExpr()),
ctx);
}
@Override
@@ -286,8 +291,7 @@
if (!booleanLiteral.getValue()) {
if (Simplifier.isEmpty(x.getBody())) {
ctx.removeMe();
- } else {
- // Unless it contains break/continue statements
+ } else { // Unless it contains break/continue statements
FindBreakContinueStatementsVisitor visitor = new
FindBreakContinueStatementsVisitor();
visitor.accept(x.getBody());
if (!visitor.hasBreakContinueStatements()) {
@@ -335,7 +339,7 @@
multi.exprs.add(literal);
}
- ctx.replaceMe(accept(multi));
+ ctx.replaceMe(this.accept(multi));
}
/**
@@ -351,7 +355,7 @@
if (!booleanLiteral.getValue()) {
JBlock block = new JBlock(x.getSourceInfo());
block.addStmts(x.getInitializers());
- ctx.replaceMe(block);
+ replaceMe(block, ctx);
}
}
}
@@ -361,11 +365,8 @@
*/
@Override
public void endVisit(JIfStatement x, Context ctx) {
- JStatement updated = simplifier.ifStatement(x, x.getSourceInfo(),
- x.getIfExpr(), x.getThenStmt(), x.getElseStmt(), currentMethod);
- if (updated != x) {
- ctx.replaceMe(updated);
- }
+ maybeReplaceMe(x, simplifier.ifStatement(x, x.getSourceInfo(),
+ x.getIfExpr(), x.getThenStmt(), x.getElseStmt(), currentMethod),
ctx);
}
@Override
@@ -425,10 +426,16 @@
@Override
public void endVisit(JMultiExpression x, Context ctx) {
List<JExpression> exprs = x.exprs;
- if (exprs.size() > 1) {
- // Remove the non-final children we previously added.
- List<JExpression> nonFinalChildren = exprs.subList(0, exprs.size()
- 1);
- ignoringExpressionOutput.removeAll(nonFinalChildren);
+ if (exprs.size() > 0) {
+ if (ignoringExpressionOutput.contains(x)) {
+ // Remove all my children we previously added.
+ ignoringExpressionOutput.removeAll(exprs);
+ } else {
+ // Remove the non-final children we previously added.
+ List<JExpression> nonFinalChildren = exprs.subList(0,
+ exprs.size() - 1);
+ ignoringExpressionOutput.removeAll(nonFinalChildren);
+ }
}
for (int i = 0; i < numRemovableExpressions(x); ++i) {
@@ -451,25 +458,31 @@
}
if (x.exprs.size() == 1) {
- ctx.replaceMe(x.exprs.get(0));
+ maybeReplaceMe(x, x.exprs.get(0), ctx);
}
}
@Override
public void endVisit(JNewInstance x, Context ctx) {
super.endVisit(x, ctx);
- if (!ignoringExpressionOutput.contains(x) |
| !x.getTarget().isEmpty()) {
- return;
- }
- // Replace the new operation with a multi.
- JMultiExpression multi = new JMultiExpression(x.getSourceInfo());
- multi.exprs.addAll(x.getArgs());
- if (x.hasClinit()) {
- multi.exprs.add(createClinitCall(x.getSourceInfo(),
- x.getTarget().getEnclosingType()));
- }
-
- ctx.replaceMe(accept(multi));
+ /*
+ * If the result of a new operation is ignored, we can remove it,
provided
+ * / it has no side effects.
+ */
+ if (ignoringExpressionOutput.contains(x)) {
+ if (!x.getTarget().isEmpty()) {
+ return;
+ }
+ JMultiExpression multi = new JMultiExpression(x.getSourceInfo());
+ multi.exprs.addAll(x.getArgs());
+ if (x.hasClinit()) {
+ multi.exprs.add(createClinitCall(x.getSourceInfo(),
+ x.getTarget().getEnclosingType()));
+ }
+ ignoringExpressionOutput.add(multi);
+ ctx.replaceMe(this.accept(multi));
+ ignoringExpressionOutput.remove(multi);
+ }
}
@Override
@@ -510,16 +523,10 @@
}
}
if (x.getOp() == JUnaryOperator.NOT) {
- JExpression updated = simplifier.not(x, x.getSourceInfo(),
x.getArg());
- if (updated != x) {
- ctx.replaceMe(updated);
- }
+ maybeReplaceMe(x, simplifier.not(x, x.getSourceInfo(),
x.getArg()), ctx);
return;
} else if (x.getOp() == JUnaryOperator.NEG) {
- JExpression updated = simplifyNegate(x, x.getArg());
- if (updated != x) {
- ctx.replaceMe(updated);
- }
+ maybeReplaceMe(x, simplifyNegate(x, x.getArg()), ctx);
}
}
@@ -573,13 +580,13 @@
removeMe(x, ctx);
} else {
// replace the try statement with just the contents of the
finally
- ctx.replaceMe(x.getFinallyBlock());
+ replaceMe(x.getFinallyBlock(), ctx);
}
} else if (noCatch && noFinally) {
// 3) Hoist up try statements with no catches and an empty finally.
// If there's no catch or finally, there's no point in this even
being
// a try statement, replace myself with the try block
- ctx.replaceMe(x.getTryBlock());
+ replaceMe(x.getTryBlock(), ctx);
}
}
@@ -648,8 +655,15 @@
public boolean visit(JMultiExpression x, Context ctx) {
List<JExpression> exprs = x.exprs;
if (exprs.size() > 0) {
- List<JExpression> nonFinalChildren = exprs.subList(0, exprs.size()
- 1);
- ignoringExpressionOutput.addAll(nonFinalChildren);
+ if (ignoringExpressionOutput.contains(x)) {
+ // None of my children matter.
+ ignoringExpressionOutput.addAll(exprs);
+ } else {
+ // Only my final child matters.
+ List<JExpression> nonFinalChildren = exprs.subList(0,
+ exprs.size() - 1);
+ ignoringExpressionOutput.addAll(nonFinalChildren);
+ }
}
return true;
}
@@ -1210,6 +1224,21 @@
private Class<?> mapType(JType type) {
return typeClassMap.get(type);
}
+
+ /**
+ * Replace me only if the the updated expression is different.
+ */
+ private void maybeReplaceMe(JExpression x, JExpression updated,
Context ctx) {
+ if (updated != x) {
+ ctx.replaceMe(updated);
+ }
+ }
+
+ private void maybeReplaceMe(JStatement x, JStatement updated, Context
ctx) {
+ if (updated != x) {
+ replaceMe(updated, ctx);
+ }
+ }
private int numRemovableExpressions(JMultiExpression x) {
if (ignoringExpressionOutput.contains(x)) {
@@ -1279,6 +1308,10 @@
}
}
+ /**
+ * Call this instead of directly calling <code>ctx.removeMe()</code>
for
+ * Statements.
+ */
private void removeMe(JStatement stmt, Context ctx) {
if (ctx.canRemove()) {
ctx.removeMe();
@@ -1289,64 +1322,18 @@
}
/**
- * Simplify short circuit AND expressions.
*
- * <pre>
- * if (true && isWhatever()) -> if (isWhatever())
- * if (false && isWhatever()) -> if (false)
- *
- * if (isWhatever() && true) -> if (isWhatever())
- * if (isWhatever() && false) -> if (false), unless side effects
- * </pre>
+ * Call this instead of directly calling <code>ctx.replaceMe()</code>
for
+ * Statements.
*/
- private void shortCircuitAnd(JExpression lhs, JExpression rhs, Context
ctx) {
- if (lhs instanceof JBooleanLiteral) {
- JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
- if (booleanLiteral.getValue()) {
- ctx.replaceMe(rhs);
- } else {
- ctx.replaceMe(lhs);
- }
-
- } else if (rhs instanceof JBooleanLiteral) {
- JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
- if (booleanLiteral.getValue()) {
- ctx.replaceMe(lhs);
- } else if (!lhs.hasSideEffects()) {
- ctx.replaceMe(rhs);
- }
+ private void replaceMe(JStatement stmt, Context ctx) {
+ stmt = this.accept(stmt, ctx.canRemove());
+ if (stmt == null) {
+ ctx.removeMe();
+ } else {
+ ctx.replaceMe(stmt);
}
}
-
- /**
- * Simplify short circuit OR expressions.
- *
- * <pre>
- * if (true || isWhatever()) -> if (true)
- * if (false || isWhatever()) -> if (isWhatever())
- *
- * if (isWhatever() || false) -> if (isWhatever())
- * if (isWhatever() || true) -> if (true), unless side effects
- * </pre>
- */
- private void shortCircuitOr(JExpression lhs, JExpression rhs, Context
ctx) {
- if (lhs instanceof JBooleanLiteral) {
- JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
- if (booleanLiteral.getValue()) {
- ctx.replaceMe(lhs);
- } else {
- ctx.replaceMe(rhs);
- }
-
- } else if (rhs instanceof JBooleanLiteral) {
- JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
- if (!booleanLiteral.getValue()) {
- ctx.replaceMe(lhs);
- } else if (!lhs.hasSideEffects()) {
- ctx.replaceMe(rhs);
- }
- }
- }
private boolean simplifyAdd(JExpression lhs, JExpression rhs, Context
ctx,
JType type) {
@@ -1666,7 +1653,7 @@
JBlock body = x.getBody();
if (body.getStatements().size() == 0) {
// Empty switch; just run the switch condition.
- ctx.replaceMe(x.getExpr().makeStatement());
+ replaceMe(x.getExpr().makeStatement(), ctx);
} else if (body.getStatements().size() == 2) {
/*
* If there are only two statements, we know it's a case statement
and
@@ -1707,13 +1694,13 @@
block.addStmt(statement);
JIfStatement ifStatement = new JIfStatement(x.getSourceInfo(),
compareOperation, block, null);
- ctx.replaceMe(ifStatement);
+ replaceMe(ifStatement, ctx);
} else {
// All we have is a default case; convert to a JBlock.
JBlock block = new JBlock(x.getSourceInfo());
block.addStmt(x.getExpr().makeStatement());
block.addStmt(statement);
- ctx.replaceMe(block);
+ replaceMe(block, ctx);
}
}
}
@@ -1813,17 +1800,9 @@
Event optimizeEvent =
SpeedTracerLogger.start(CompilerEventType.OPTIMIZE,
"optimizer", NAME);
- // TODO(zundel): see if this loop can be removed and all work done in
one
- // pass of the optimizer to improve performance.
- while (true) {
- DeadCodeVisitor deadCodeVisitor = new DeadCodeVisitor();
- deadCodeVisitor.accept(node);
- stats.recordModified(deadCodeVisitor.getNumMods());
- if (!deadCodeVisitor.didChange()) {
- break;
- }
- }
-
+ DeadCodeVisitor deadCodeVisitor = new DeadCodeVisitor();
+ deadCodeVisitor.accept(node);
+ stats.recordModified(deadCodeVisitor.getNumMods());
optimizeEvent.end("didChange", "" + stats.didChange());
return stats;
}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Simplifier.java Tue
Mar 9 10:54:56 2010
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Simplifier.java Wed Oct
13 14:39:37 2010
@@ -26,6 +26,7 @@
import com.google.gwt.dev.jjs.ast.JExpressionStatement;
import com.google.gwt.dev.jjs.ast.JIfStatement;
import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
@@ -84,8 +85,9 @@
this.program = program;
}
- public JExpression cast(JExpression original, SourceInfo sourceInfo,
- JType type, JExpression exp) {
+ public JExpression cast(JExpression original, SourceInfo info, JType
type,
+ JExpression exp) {
+ info = getBestSourceInfo(original, info, exp);
if (type == exp.getType()) {
return exp;
}
@@ -116,24 +118,25 @@
if (original != null) {
return original;
}
- return new JCastOperation(sourceInfo, type, exp);
+ return new JCastOperation(info, type, exp);
}
public JExpression cast(JType type, JExpression exp) {
return cast(null, exp.getSourceInfo(), type, exp);
}
- public JExpression conditional(JConditional original, SourceInfo
sourceInfo,
+ public JExpression conditional(JConditional original, SourceInfo info,
JType type, JExpression condExpr, JExpression thenExpr,
JExpression elseExpr) {
+ info = getBestSourceInfo(original, info, condExpr);
if (condExpr instanceof JMultiExpression) {
// (a,b,c)?d:e -> a,b,(c?d:e)
// TODO(spoon): do this outward multi movement for all AST nodes
JMultiExpression condMulti = (JMultiExpression) condExpr;
- JMultiExpression newMulti = new JMultiExpression(sourceInfo);
+ JMultiExpression newMulti = new JMultiExpression(info);
newMulti.exprs.addAll(allButLast(condMulti.exprs));
- newMulti.exprs.add(conditional(null, sourceInfo, type,
- last(condMulti.exprs), thenExpr, elseExpr));
+ newMulti.exprs.add(conditional(null, info, type,
last(condMulti.exprs),
+ thenExpr, elseExpr));
// TODO(spoon): immediately simplify the resulting multi
return newMulti;
}
@@ -148,36 +151,26 @@
} else if (thenExpr instanceof JBooleanLiteral) {
if (((JBooleanLiteral) thenExpr).getValue()) {
// e.g. (cond ? true : else) -> cond || else
- JBinaryOperation binOp = new JBinaryOperation(sourceInfo, type,
- JBinaryOperator.OR, condExpr, elseExpr);
- return binOp;
+ return shortCircuitOr(null, info, condExpr, elseExpr);
} else {
// e.g. (cond ? false : else) -> !cond && else
- JPrefixOperation notCondExpr = new JPrefixOperation(
- condExpr.getSourceInfo(), JUnaryOperator.NOT, condExpr);
- JBinaryOperation binOp = new JBinaryOperation(sourceInfo, type,
- JBinaryOperator.AND, notCondExpr, elseExpr);
- return binOp;
+ JExpression notCondExpr = not(null, condExpr.getSourceInfo(),
condExpr);
+ return shortCircuitAnd(null, info, notCondExpr, elseExpr);
}
} else if (elseExpr instanceof JBooleanLiteral) {
if (((JBooleanLiteral) elseExpr).getValue()) {
// e.g. (cond ? then : true) -> !cond || then
- JPrefixOperation notCondExpr = new JPrefixOperation(
- condExpr.getSourceInfo(), JUnaryOperator.NOT, condExpr);
- JBinaryOperation binOp = new JBinaryOperation(sourceInfo, type,
- JBinaryOperator.OR, notCondExpr, thenExpr);
- return binOp;
+ JExpression notCondExpr = not(null, condExpr.getSourceInfo(),
condExpr);
+ return shortCircuitOr(null, info, notCondExpr, thenExpr);
} else {
// e.g. (cond ? then : false) -> cond && then
- JBinaryOperation binOp = new JBinaryOperation(sourceInfo, type,
- JBinaryOperator.AND, condExpr, thenExpr);
- return binOp;
+ return shortCircuitAnd(null, info, condExpr, thenExpr);
}
} else {
// e.g. (!cond ? then : else) -> (cond ? else : then)
JExpression unflipped = maybeUnflipBoolean(condExpr);
if (unflipped != null) {
- return new JConditional(sourceInfo, type, unflipped, elseExpr,
thenExpr);
+ return new JConditional(info, type, unflipped, elseExpr, thenExpr);
}
}
@@ -185,21 +178,22 @@
if (original != null) {
return original;
}
- return new JConditional(sourceInfo, type, condExpr, thenExpr,
elseExpr);
+ return new JConditional(info, type, condExpr, thenExpr, elseExpr);
}
- public JStatement ifStatement(JIfStatement original, SourceInfo
sourceInfo,
+ public JStatement ifStatement(JIfStatement original, SourceInfo info,
JExpression condExpr, JStatement thenStmt, JStatement elseStmt,
JMethod currentMethod) {
+ info = getBestSourceInfo(original, info, condExpr);
if (condExpr instanceof JMultiExpression) {
// if(a,b,c) d else e -> {a; b; if(c) d else e; }
JMultiExpression condMulti = (JMultiExpression) condExpr;
- JBlock newBlock = new JBlock(sourceInfo);
+ JBlock newBlock = new JBlock(info);
for (JExpression expr : allButLast(condMulti.exprs)) {
newBlock.addStmt(expr.makeStatement());
}
- newBlock.addStmt(ifStatement(null, sourceInfo, last(condMulti.exprs),
- thenStmt, elseStmt, currentMethod));
+ newBlock.addStmt(ifStatement(null, info, last(condMulti.exprs),
thenStmt,
+ elseStmt, currentMethod));
// TODO(spoon): immediately simplify the resulting block
return newBlock;
}
@@ -231,12 +225,12 @@
// TODO: this goes away when we normalize the Java AST properly.
thenStmt = ensureBlock(thenStmt);
elseStmt = ensureBlock(elseStmt);
- return ifStatement(null, sourceInfo, unflipped, elseStmt, thenStmt,
+ return ifStatement(null, info, unflipped, elseStmt, thenStmt,
currentMethod);
}
}
- JStatement rewritenStatement = rewriteIfIntoBoolean(sourceInfo,
condExpr,
+ JStatement rewritenStatement = rewriteIfIntoBoolean(info, condExpr,
thenStmt, elseStmt, currentMethod);
if (rewritenStatement != null) {
return rewritenStatement;
@@ -246,18 +240,18 @@
if (original != null) {
return original;
}
- return new JIfStatement(condExpr.getSourceInfo(), condExpr, thenStmt,
- elseStmt);
+ return new JIfStatement(info, condExpr, thenStmt, elseStmt);
}
- public JExpression not(JPrefixOperation original, SourceInfo sourceInfo,
+ public JExpression not(JPrefixOperation original, SourceInfo info,
JExpression arg) {
+ info = getBestSourceInfo(original, info, arg);
if (arg instanceof JMultiExpression) {
// !(a,b,c) -> (a,b,!c)
JMultiExpression argMulti = (JMultiExpression) arg;
- JMultiExpression newMulti = new JMultiExpression(sourceInfo);
+ JMultiExpression newMulti = new JMultiExpression(info);
newMulti.exprs.addAll(allButLast(argMulti.exprs));
- newMulti.exprs.add(not(null, sourceInfo, last(argMulti.exprs)));
+ newMulti.exprs.add(not(null, info, last(argMulti.exprs)));
// TODO(spoon): immediately simplify the newMulti
return newMulti;
}
@@ -286,8 +280,8 @@
newOp = JBinaryOperator.GTE;
}
if (newOp != null) {
- JBinaryOperation newBinOp = new
JBinaryOperation(argOp.getSourceInfo(),
- argOp.getType(), newOp, argOp.getLhs(), argOp.getRhs());
+ JBinaryOperation newBinOp = new JBinaryOperation(info,
argOp.getType(),
+ newOp, argOp.getLhs(), argOp.getRhs());
return newBinOp;
}
} else if (arg instanceof JPrefixOperation) {
@@ -298,13 +292,92 @@
if (op == JUnaryOperator.NOT) {
return argOp.getArg();
}
+ } else if (arg instanceof JBooleanLiteral) {
+ JBooleanLiteral booleanLit = (JBooleanLiteral) arg;
+ return JBooleanLiteral.get(!booleanLit.getValue());
}
// no simplification made
if (original != null) {
return original;
}
- return new JPrefixOperation(arg.getSourceInfo(), JUnaryOperator.NOT,
arg);
+ return new JPrefixOperation(info, JUnaryOperator.NOT, arg);
+ }
+
+ /**
+ * Simplify short circuit AND expressions.
+ *
+ * <pre>
+ * if (true && isWhatever()) -> if (isWhatever())
+ * if (false && isWhatever()) -> if (false)
+ *
+ * if (isWhatever() && true) -> if (isWhatever())
+ * if (isWhatever() && false) -> if (false), unless side effects
+ * </pre>
+ */
+ public JExpression shortCircuitAnd(JBinaryOperation original,
+ SourceInfo info, JExpression lhs, JExpression rhs) {
+ info = getBestSourceInfo(original, info, lhs);
+ if (lhs instanceof JBooleanLiteral) {
+ JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
+ if (booleanLiteral.getValue()) {
+ return rhs;
+ } else {
+ return lhs;
+ }
+
+ } else if (rhs instanceof JBooleanLiteral) {
+ JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
+ if (booleanLiteral.getValue()) {
+ return lhs;
+ } else if (!lhs.hasSideEffects()) {
+ return rhs;
+ }
+ }
+ // no simplification made
+ if (original != null) {
+ return original;
+ }
+ return new JBinaryOperation(info, rhs.getType(), JBinaryOperator.AND,
lhs,
+ rhs);
+ }
+
+ /**
+ * Simplify short circuit OR expressions.
+ *
+ * <pre>
+ * if (true || isWhatever()) -> if (true)
+ * if (false || isWhatever()) -> if (isWhatever())
+ *
+ * if (isWhatever() || false) -> if (isWhatever())
+ * if (isWhatever() || true) -> if (true), unless side effects
+ * </pre>
+ */
+ public JExpression shortCircuitOr(JBinaryOperation original, SourceInfo
info,
+ JExpression lhs, JExpression rhs) {
+ info = getBestSourceInfo(original, info, lhs);
+ if (lhs instanceof JBooleanLiteral) {
+ JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs;
+ if (booleanLiteral.getValue()) {
+ return lhs;
+ } else {
+ return rhs;
+ }
+
+ } else if (rhs instanceof JBooleanLiteral) {
+ JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
+ if (!booleanLiteral.getValue()) {
+ return lhs;
+ } else if (!lhs.hasSideEffects()) {
+ return rhs;
+ }
+ }
+ // no simplification made
+ if (original != null) {
+ return original;
+ }
+ return new JBinaryOperation(info, rhs.getType(), JBinaryOperator.OR,
lhs,
+ rhs);
}
private JStatement ensureBlock(JStatement stmt) {
@@ -338,6 +411,18 @@
return stmt;
}
+
+ private SourceInfo getBestSourceInfo(JNode original, SourceInfo info,
+ JNode defaultNode) {
+ if (info == null) {
+ if (original == null) {
+ info = defaultNode.getSourceInfo();
+ } else {
+ info = original.getSourceInfo();
+ }
+ }
+ return info;
+ }
private JStatement rewriteIfIntoBoolean(SourceInfo sourceInfo,
JExpression condExpr, JStatement thenStmt, JStatement elseStmt,
=======================================
---
/trunk/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
Thu Sep 9 00:20:17 2010
+++
/trunk/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
Wed Oct 13 14:39:37 2010
@@ -15,7 +15,6 @@
*/
package com.google.gwt.dev.jjs.impl;
-import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
@@ -23,6 +22,11 @@
* Tests {...@link DeadCodeElimination}.
*/
public class DeadCodeEliminationTest extends OptimizerTestBase {
+ /*
+ * TODO: this class needs more tests, and more sophisticated cases.
Especially
+ * to ensure we converge in a single pass.
+ */
+
@Override
public void setUp() throws Exception {
addSnippetClassDecl("static volatile boolean b;");
@@ -92,7 +96,7 @@
optimize("void", "if (b) {i = 1;}").intoString(
"EntryPoint.b && (EntryPoint.i = 1);");
}
-
+
public void testDoOptimization() throws Exception {
optimize("void", "do {} while (b);").intoString(
"do {",
@@ -121,6 +125,11 @@
@Override
protected boolean optimizeMethod(JProgram program, JMethod method) {
- return DeadCodeElimination.exec(program, method).didChange();
+ OptimizerStats result = DeadCodeElimination.exec(program, method);
+ if (result.didChange()) {
+ // Make sure we converge in one pass.
+ assertFalse(DeadCodeElimination.exec(program, method).didChange());
+ }
+ return result.didChange();
}
}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors