Fix for issue #851; implements some dead code eliminations suggested by sandymac.

Suggested by: sandymac
Review by: mmendez, alex.tkachman


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@983 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index aad50fd..e2cd025 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -85,9 +85,12 @@
 
         } else if (rhs instanceof JBooleanLiteral) {
           // eg: if (isWhatever() && true) -> if (isWhatever())
+          // eg: if (isWhatever() && false) -> if (false), unless side effects
           JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
           if (booleanLiteral.getValue()) {
             ctx.replaceMe(lhs);
+          } else if (!lhs.hasSideEffects()) {
+            ctx.replaceMe(rhs);
           }
         }
 
@@ -105,9 +108,12 @@
 
         } else if (rhs instanceof JBooleanLiteral) {
           // eg: if (isWhatever() || false) -> if (isWhatever())
+          // eg: if (isWhatever() && true) -> if (true), unless side effects
           JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs;
           if (!booleanLiteral.getValue()) {
             ctx.replaceMe(lhs);
+          } else if (!lhs.hasSideEffects()) {
+            ctx.replaceMe(rhs);
           }
         }
       } else if (op == JBinaryOperator.EQ) {
@@ -146,16 +152,48 @@
     }
 
     public void endVisit(JConditional x, Context ctx) {
-      JExpression expression = x.getIfTest();
-      if (expression instanceof JBooleanLiteral) {
-        JBooleanLiteral booleanLiteral = (JBooleanLiteral) expression;
-
-        if (booleanLiteral.getValue()) {
-          // If true, replace myself with then expression
-          ctx.replaceMe(x.getThenExpr());
+      JExpression condExpr = x.getIfTest();
+      JExpression thenExpr = x.getThenExpr();
+      JExpression elseExpr = x.getElseExpr();
+      if (condExpr instanceof JBooleanLiteral) {
+        if (((JBooleanLiteral) condExpr).getValue()) {
+          // e.g. (true ? then : else) -> then
+          ctx.replaceMe(thenExpr);
         } else {
-          // If false, replace myself with else expression
-          ctx.replaceMe(x.getElseExpr());
+          // e.g. (false ? then : else) -> else
+          ctx.replaceMe(elseExpr);
+        }
+      } else if (thenExpr instanceof JBooleanLiteral) {
+        if (((JBooleanLiteral) thenExpr).getValue()) {
+          // e.g. (cond ? true : else) -> cond || else
+          JBinaryOperation binOp = new JBinaryOperation(program,
+              x.getSourceInfo(), x.getType(), JBinaryOperator.OR, condExpr,
+              elseExpr);
+          ctx.replaceMe(binOp);
+        } else {
+          // e.g. (cond ? false : else) -> !cond && else
+          JPrefixOperation notCondExpr = new JPrefixOperation(program,
+              condExpr.getSourceInfo(), JUnaryOperator.NOT, condExpr);
+          JBinaryOperation binOp = new JBinaryOperation(program,
+              x.getSourceInfo(), x.getType(), JBinaryOperator.AND, notCondExpr,
+              elseExpr);
+          ctx.replaceMe(binOp);
+        }
+      } else if (elseExpr instanceof JBooleanLiteral) {
+        if (((JBooleanLiteral) elseExpr).getValue()) {
+          // e.g. (cond ? then : true) -> !cond || then
+          JPrefixOperation notCondExpr = new JPrefixOperation(program,
+              condExpr.getSourceInfo(), JUnaryOperator.NOT, condExpr);
+          JBinaryOperation binOp = new JBinaryOperation(program,
+              x.getSourceInfo(), x.getType(), JBinaryOperator.OR, notCondExpr,
+              thenExpr);
+          ctx.replaceMe(binOp);
+        } else {
+          // e.g. (cond ? then : false) -> cond && then
+          JBinaryOperation binOp = new JBinaryOperation(program,
+              x.getSourceInfo(), x.getType(), JBinaryOperator.AND, condExpr,
+              thenExpr);
+          ctx.replaceMe(binOp);
         }
       }
     }
@@ -229,13 +267,53 @@
     }
 
     /**
-     * Resolve "!true" into "false" and vice versa.
+     * Simplify the ! operator if possible.
      */
     public void endVisit(JPrefixOperation x, Context ctx) {
       if (x.getOp() == JUnaryOperator.NOT) {
-        if (x.getArg() instanceof JBooleanLiteral) {
-          JBooleanLiteral booleanLiteral = (JBooleanLiteral) x.getArg();
+        JExpression arg = x.getArg();
+        if (arg instanceof JBooleanLiteral) {
+          // e.g. !true -> false; !false -> true
+          JBooleanLiteral booleanLiteral = (JBooleanLiteral) arg;
           ctx.replaceMe(program.getLiteralBoolean(!booleanLiteral.getValue()));
+        } else if (arg instanceof JBinaryOperation) {
+          // try to invert the binary operator
+          JBinaryOperation argOp = (JBinaryOperation) arg;
+          JBinaryOperator op = argOp.getOp();
+          JBinaryOperator newOp = null;
+          if (op == JBinaryOperator.EQ) {
+            // e.g. !(x == y) -> x != y
+            newOp = JBinaryOperator.NEQ;
+          } else if (op == JBinaryOperator.NEQ) {
+            // e.g. !(x != y) -> x == y
+            newOp = JBinaryOperator.EQ;
+          } else if (op == JBinaryOperator.GT) {
+            // e.g. !(x > y) -> x <= y
+            newOp = JBinaryOperator.LTE;
+          } else if (op == JBinaryOperator.LTE) {
+            // e.g. !(x <= y) -> x > y
+            newOp = JBinaryOperator.GT;
+          } else if (op == JBinaryOperator.GTE) {
+            // e.g. !(x >= y) -> x < y
+            newOp = JBinaryOperator.LT;
+          } else if (op == JBinaryOperator.LT) {
+            // e.g. !(x < y) -> x >= y
+            newOp = JBinaryOperator.GTE;
+          }
+          if (newOp != null) {
+            JBinaryOperation newBinOp = new JBinaryOperation(program,
+                argOp.getSourceInfo(), argOp.getType(), newOp, argOp.getLhs(),
+                argOp.getRhs());
+            ctx.replaceMe(newBinOp);
+          }
+        } else if (arg instanceof JPrefixOperation) {
+          // try to invert the unary operator
+          JPrefixOperation argOp = (JPrefixOperation) arg;
+          JUnaryOperator op = argOp.getOp();
+          // e.g. !!x -> x
+          if (op == JUnaryOperator.NOT) {
+            ctx.replaceMe(argOp.getArg());
+          }
         }
       }
     }
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index 1dde413..21466f8 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -90,16 +90,16 @@
     }
   }
 
+  private static class SideEffectCauser6 extends SideEffectCauser6Super {
+    public static String causeClinitSideEffectOnRead = "bar";
+  }
+
   private static class SideEffectCauser6Super {
     static {
       CompilerTest.sideEffectChecker++;
     }
   }
 
-  private static class SideEffectCauser6 extends SideEffectCauser6Super {
-    public static String causeClinitSideEffectOnRead = "bar";
-  }
-
   private static final class UninstantiableType {
     public Object field;
 
@@ -117,10 +117,6 @@
     return "bar";
   }
 
-  private static native boolean cannotOptimize() /*-{
-    return true;
-  }-*/;
-
   private static void foo(String string) {
     Object o = string;
   }
@@ -133,6 +129,14 @@
     return @com.google.gwt.dev.jjs.test.CompilerTest$SideEffectCauser5::causeClinitSideEffectOnRead;
   }-*/;
 
+  private static native boolean noOptimizeFalse() /*-{
+    return false;
+  }-*/;
+
+  private static native boolean noOptimizeTrue() /*-{
+    return true;
+  }-*/;
+
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
@@ -148,8 +152,8 @@
     oaa[0][0] = "bar";
     assertEquals(oaa[0][0], "bar");
 
-    Apple[] apple = cannotOptimize() ? new Granny[3] : new Apple[3];
-    Apple g = cannotOptimize() ? (Apple) new Granny() : (Apple) new Fuji();
+    Apple[] apple = noOptimizeTrue() ? new Granny[3] : new Apple[3];
+    Apple g = noOptimizeTrue() ? (Apple) new Granny() : (Apple) new Fuji();
     Apple a = apple[0] = g;
     assertEquals(g, a);
   }
@@ -203,6 +207,28 @@
     assertEquals(null, checkRescued);
   }
 
+  public void testConditionals() {
+    assertTrue(noOptimizeTrue() ? noOptimizeTrue() : noOptimizeFalse());
+    assertFalse(noOptimizeFalse() ? noOptimizeTrue() : noOptimizeFalse());
+    assertFalse(noOptimizeTrue() ? noOptimizeFalse() : noOptimizeTrue());
+    assertTrue(noOptimizeFalse() ? noOptimizeFalse() : noOptimizeTrue());
+
+    assertTrue(true ? noOptimizeTrue() : noOptimizeFalse());
+    assertFalse(false ? noOptimizeTrue() : noOptimizeFalse());
+    assertFalse(true ? noOptimizeFalse() : noOptimizeTrue());
+    assertTrue(false ? noOptimizeFalse() : noOptimizeTrue());
+
+    assertTrue(noOptimizeTrue() ? true : noOptimizeFalse());
+    assertFalse(noOptimizeFalse() ? true : noOptimizeFalse());
+    assertFalse(noOptimizeTrue() ? false : noOptimizeTrue());
+    assertTrue(noOptimizeFalse() ? false : noOptimizeTrue());
+
+    assertTrue(noOptimizeTrue() ? noOptimizeTrue() : false);
+    assertFalse(noOptimizeFalse() ? noOptimizeTrue() : false);
+    assertFalse(noOptimizeTrue() ? noOptimizeFalse() : true);
+    assertTrue(noOptimizeFalse() ? noOptimizeFalse() : true);
+  }
+
   public void testDeadCode() {
     while (returnFalse()) {
       break;
@@ -420,7 +446,7 @@
   }
 
   public void testJavaScriptReservedWords() {
-    boolean delete = cannotOptimize();
+    boolean delete = noOptimizeTrue();
     for (int in = 0; in < 10; ++in) {
       assertTrue(in < 10);
       assertTrue(delete);
@@ -480,8 +506,8 @@
   }
   
   public void testLocalRefs() {
-    final String foo = cannotOptimize() ? "foo" : "bar";
-    final String bar = cannotOptimize() ? "bar" : "foo";
+    final String foo = noOptimizeTrue() ? "foo" : "bar";
+    final String bar = noOptimizeTrue() ? "bar" : "foo";
     String result = new Object() {
 
       private String a = foo;
@@ -516,6 +542,27 @@
     assertEquals(result, "foofoofoofoo");
   }
 
+  public void testNotOptimizations() {
+    assertFalse(!true);
+    assertTrue(!false);
+    
+    assertTrue(!(noOptimizeTrue() == noOptimizeFalse()));
+    assertFalse(!(noOptimizeTrue() != noOptimizeFalse()));
+    
+    assertFalse(!(3 < 4));
+    assertFalse(!(3 <= 4));
+    assertTrue(!(3 > 4));
+    assertTrue(!(3 >= 4));
+    
+    assertTrue(!(4 < 3));
+    assertTrue(!(4 <= 3));
+    assertFalse(!(4 > 3));
+    assertFalse(!(4 >= 3));
+    
+    assertTrue(!!noOptimizeTrue());
+    assertFalse(!!noOptimizeFalse());
+  }
+
   public void testNullFlow() {
     UninstantiableType f = null;