Revision: 6533 Author: sco...@google.com Date: Wed Oct 28 16:56:19 2009 Log: Rewrites some if statements into boolean expressions.
if (<cond>) { <expr_stmt>; } => <cond> && <expr_stmt> if (<cond>) { <then_expr_stmt>; } else { <else_expr_stmt>; } => <cond> ? <then_expr_stmt> : <else_expr_stmt> Also supports lifting return statement: if (<cond>) { return <expr1>; } else { return <expr2>; } => return <cond>?<expr1>:<expr2>; Reduces code size by a tiny amout (approx. 0.5%) mostly because this optimization create a lot of opportunities for inliner to kick in. Patch by: mike.aizatsky Review by: me, spoon http://code.google.com/p/google-web-toolkit/source/detail?r=6533 Modified: /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.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/impl/DeadCodeElimination.java Mon Oct 26 14:02:26 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java Wed Oct 28 16:56:19 2009 @@ -89,6 +89,8 @@ */ public class DeadCodeVisitor extends JModVisitor { + private JMethod currentMethod = null; + /** * Expressions whose result does not matter. A parent node should add any * children whose result does not matter to this set during the parent's @@ -358,7 +360,7 @@ @Override public void endVisit(JIfStatement x, Context ctx) { JStatement updated = simplifier.ifStatement(x, x.getSourceInfo(), - x.getIfExpr(), x.getThenStmt(), x.getElseStmt()); + x.getIfExpr(), x.getThenStmt(), x.getElseStmt(), currentMethod); if (updated != x) { ctx.replaceMe(updated); } @@ -372,6 +374,11 @@ ctx.replaceMe(literal); } } + + @Override + public void endVisit(JMethod x, Context ctx) { + currentMethod = null; + } /** * Resolve method calls that can be computed statically. @@ -594,6 +601,12 @@ ignoringExpressionOutput.add(x.getExpr()); return true; } + + @Override + public boolean visit(JMethod x, Context ctx) { + currentMethod = x; + return true; + } @Override public boolean visit(JMethodCall x, Context ctx) { ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java Thu Apr 9 08:41:34 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java Wed Oct 28 16:56:19 2009 @@ -17,6 +17,7 @@ import com.google.gwt.dev.jjs.ast.Context; import com.google.gwt.dev.jjs.ast.JBinaryOperation; +import com.google.gwt.dev.jjs.ast.JBinaryOperator; import com.google.gwt.dev.jjs.ast.JConditional; import com.google.gwt.dev.jjs.ast.JDeclarationStatement; import com.google.gwt.dev.jjs.ast.JExpression; @@ -56,14 +57,21 @@ JType lhsType = x.getLhs().getType(); JType rhsType = x.getRhs().getType(); JType resultType = x.getType(); + JBinaryOperator op = x.getOp(); if (resultType == program.getTypeJavaLangString()) { // Don't mess with concat. return; } + + if (lhsType == JPrimitiveType.BOOLEAN + && (op == JBinaryOperator.AND || op == JBinaryOperator.OR)) { + // Don't mess with if rewriter. + return; + } // Special case: shift operators always coerce a long RHS to int. - if (x.getOp().isShiftOperator()) { + if (op.isShiftOperator()) { if (rhsType == longType) { rhsType = program.getTypePrimitiveInt(); } @@ -80,7 +88,7 @@ if ((lhsType == floatType || lhsType == doubleType)) { coerceTo = lhsType; } - if (x.getOp().isAssignment()) { + if (op.isAssignment()) { // In an assignment, the lhs must coerce the rhs coerceTo = lhsType; } else if ((rhsType == floatType || rhsType == doubleType)) { @@ -93,7 +101,7 @@ JExpression newRhs = checkAndReplace(x.getRhs(), rhsType); if (newLhs != x.getLhs() || newRhs != x.getRhs()) { JBinaryOperation binOp = new JBinaryOperation(x.getSourceInfo(), - resultType, x.getOp(), newLhs, newRhs); + resultType, op, newLhs, newRhs); ctx.replaceMe(binOp); } } ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java Thu Apr 9 08:41:34 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java Wed Oct 28 16:56:19 2009 @@ -56,7 +56,6 @@ JType lhsType = x.getLhs().getType(); JType rhsType = x.getRhs().getType(); if (lhsType != longType) { - assert (rhsType != longType); return; } ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Simplifier.java Thu Apr 9 08:41:34 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/Simplifier.java Wed Oct 28 16:56:19 2009 @@ -23,10 +23,13 @@ import com.google.gwt.dev.jjs.ast.JCastOperation; import com.google.gwt.dev.jjs.ast.JConditional; import com.google.gwt.dev.jjs.ast.JExpression; +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.JPrefixOperation; import com.google.gwt.dev.jjs.ast.JPrimitiveType; import com.google.gwt.dev.jjs.ast.JProgram; +import com.google.gwt.dev.jjs.ast.JReturnStatement; import com.google.gwt.dev.jjs.ast.JStatement; import com.google.gwt.dev.jjs.ast.JType; import com.google.gwt.dev.jjs.ast.JUnaryOperation; @@ -186,7 +189,8 @@ } public JStatement ifStatement(JIfStatement original, SourceInfo sourceInfo, - JExpression condExpr, JStatement thenStmt, JStatement elseStmt) { + JExpression condExpr, JStatement thenStmt, JStatement elseStmt, + JMethod currentMethod) { if (condExpr instanceof JMultiExpression) { // if(a,b,c) d else e -> {a; b; if(c) d else e; } JMultiExpression condMulti = (JMultiExpression) condExpr; @@ -195,7 +199,7 @@ newBlock.addStmt(expr.makeStatement()); } newBlock.addStmt(ifStatement(null, sourceInfo, last(condMulti.exprs), - thenStmt, elseStmt)); + thenStmt, elseStmt, currentMethod)); // TODO(spoon): immediately simplify the resulting block return newBlock; } @@ -227,9 +231,16 @@ // 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, sourceInfo, unflipped, elseStmt, thenStmt, + currentMethod); } } + + JStatement rewritenStatement = rewriteIfIntoBoolean(sourceInfo, condExpr, + thenStmt, elseStmt, currentMethod); + if (rewritenStatement != null) { + return rewritenStatement; + } // no simplification made if (original != null) { @@ -304,4 +315,84 @@ } return stmt; } -} + + private JExpression extractExpression(JStatement stmt) { + if (stmt instanceof JExpressionStatement) { + JExpressionStatement statement = (JExpressionStatement) stmt; + return statement.getExpr(); + } + + return null; + } + + private JStatement extractSingleStatement(JStatement stmt) { + if (stmt instanceof JBlock) { + JBlock block = (JBlock) stmt; + if (block.getStatements().size() == 1) { + return extractSingleStatement(block.getStatements().get(0)); + } + } + + return stmt; + } + + private JStatement rewriteIfIntoBoolean(SourceInfo sourceInfo, + JExpression condExpr, JStatement thenStmt, JStatement elseStmt, + JMethod currentMethod) { + thenStmt = extractSingleStatement(thenStmt); + elseStmt = extractSingleStatement(elseStmt); + + if (thenStmt instanceof JReturnStatement + && elseStmt instanceof JReturnStatement && currentMethod != null) { + // Special case + // if () { return ..; } else { return ..; } => + // return ... ? ... : ...; + JExpression thenExpression = ((JReturnStatement) thenStmt).getExpr(); + JExpression elseExpression = ((JReturnStatement) elseStmt).getExpr(); + if (thenExpression == null || elseExpression == null) { + // empty returns are not supported. + return null; + } + + JConditional conditional = new JConditional(sourceInfo, + currentMethod.getType(), condExpr, thenExpression, elseExpression); + + JReturnStatement returnStatement = new JReturnStatement(sourceInfo, + conditional); + return returnStatement; + } + + if (elseStmt != null) { + // if () { } else { } -> ... ? ... : ... ; + JExpression thenExpression = extractExpression(thenStmt); + JExpression elseExpression = extractExpression(elseStmt); + + if (thenExpression != null && elseExpression != null) { + JConditional conditional = new JConditional(sourceInfo, + JPrimitiveType.VOID, condExpr, thenExpression, elseExpression); + + return conditional.makeStatement(); + } + } else { + // if () { } -> ... && ...; + JExpression thenExpression = extractExpression(thenStmt); + + if (thenExpression != null) { + JBinaryOperator binaryOperator = JBinaryOperator.AND; + + JExpression unflipExpression = maybeUnflipBoolean(condExpr); + if (unflipExpression != null) { + condExpr = unflipExpression; + binaryOperator = JBinaryOperator.OR; + } + + JBinaryOperation binaryOperation = new JBinaryOperation(sourceInfo, + program.getTypeVoid(), binaryOperator, condExpr, thenExpression); + + return binaryOperation.makeStatement(); + } + } + + return null; + } +} ======================================= --- /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java Wed Oct 28 09:10:53 2009 +++ /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java Wed Oct 28 16:56:19 2009 @@ -39,26 +39,38 @@ expected = getMainMethodSource(program); assertEquals(userCode, expected, optimized); } + + /** + * Compare without compiling expected, needed when optimizations produce + * incorrect java code (e.g. "a" || "b" is incorrect in java). + */ + public void intoString(String expected) { + String actual = optimized; + assertTrue(actual.startsWith("{")); + assertTrue(actual.endsWith("}")); + actual = actual.substring(1, actual.length() - 2).trim(); + // Join lines in actual code and remove indentations + actual = actual.replaceAll(" +", " ").replaceAll("\n", ""); + assertEquals(userCode, expected, actual); + } + } + + @Override + public void setUp() throws Exception { + addSnippetClassDecl("static volatile boolean b;"); + addSnippetClassDecl("static volatile boolean b1;"); + addSnippetClassDecl("static volatile int i;"); + addSnippetClassDecl("static volatile long l;"); } public void testConditionalOptimizations() throws Exception { optimize("int", "return true ? 3 : 4;").into("return 3;"); optimize("int", "return false ? 3 : 4;").into("return 4;"); - addSnippetClassDecl("static volatile boolean TEST"); - addSnippetClassDecl("static volatile boolean RESULT"); - - optimize("boolean", "return TEST ? true : RESULT;").into( - "return TEST || RESULT;"); - - optimize("boolean", "return TEST ? false : RESULT;").into( - "return !TEST && RESULT;"); - - optimize("boolean", "return TEST ? RESULT : true;").into( - "return !TEST || RESULT;"); - - optimize("boolean", "return TEST ? RESULT : false;").into( - "return TEST && RESULT;"); + optimize("boolean", "return b ? true : b1;").into("return b || b1;"); + optimize("boolean", "return b ? false : b1;").into("return !b && b1;"); + optimize("boolean", "return b ? b1 : true;").into("return !b || b1;"); + optimize("boolean", "return b ? b1 : false;").into("return b && b1;"); } public void testIfOptimizations() throws Exception { @@ -69,11 +81,47 @@ optimize("int", "if (true) {} else return 4; return 0;").into("return 0;"); - addSnippetClassDecl("static volatile boolean TEST"); - addSnippetClassDecl("static boolean test() { return TEST; }"); + addSnippetClassDecl("static boolean test() { return b; }"); optimize("int", "if (test()) {} else {}; return 0;").into( "test(); return 0;"); } + + public void testIfStatementToBoolean_NotOptimization() throws Exception { + optimize("void", "if (!b) i = 1;").intoString( + "EntryPoint.b || (EntryPoint.i = 1);"); + optimize("void", "if (!b) i = 1; else i = 2;").intoString( + "EntryPoint.b ? (EntryPoint.i = 2) : (EntryPoint.i = 1);"); + optimize("int", "if (!b) { return 1;} else {return 2;}").into( + "return b ? 2 : 1;"); + } + + public void testIfStatementToBoolean_ReturnLifting() throws Exception { + optimize("int", "if (b) return 1; return 2;").into( + "if (b) return 1; return 2;"); + optimize("int", "if (b) { return 1; } return 2;").into( + "if (b) { return 1; } return 2;"); + optimize("int", "if (b) { return 1;} else {return 2;}").into( + "return b ? 1 : 2;"); + optimize("int", "if (b) return 1; else {return 2;}").into( + "return b ? 1 : 2;"); + optimize("int", "if (b) return 1; else return 2;").into("return b ? 1 : 2;"); + optimize("void", "if (b) return; else return;").into( + "if (b) return; else return;"); + } + + public void testIfStatementToBoolean_ThenElseOptimization() throws Exception { + optimize("void", "if (b) i = 1; else i = 2;").intoString( + "EntryPoint.b ? (EntryPoint.i = 1) : (EntryPoint.i = 2);"); + optimize("void", "if (b) {i = 1;} else {i = 2;}").intoString( + "EntryPoint.b ? (EntryPoint.i = 1) : (EntryPoint.i = 2);"); + } + + public void testIfStatementToBoolean_ThenOptimization() throws Exception { + optimize("void", "if (b) i = 1;").intoString( + "EntryPoint.b && (EntryPoint.i = 1);"); + optimize("void", "if (b) {i = 1;}").intoString( + "EntryPoint.b && (EntryPoint.i = 1);"); + } private Result optimize(final String returnType, final String codeSnippet) throws UnableToCompleteException { --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---