8025505: Constant folding deficiency
authorpgovereau
Wed, 19 Mar 2014 17:39:28 -0400
changeset 23791 f1cdccbc1040
parent 23790 0697e38017ec
child 23792 eabe3e8a29bf
8025505: Constant folding deficiency Summary: Provide constant folding of equality tests involving constant and null. Reviewed-by: jjg
langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java
langtools/test/tools/javac/ConstFoldTest.java
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Wed Mar 19 11:34:27 2014 -0400
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Wed Mar 19 17:39:28 2014 -0400
@@ -2919,15 +2919,65 @@
     // constant propagation would require that we take care to
     // preserve possible side-effects in the condition expression.
 
+    // One common case is equality expressions involving a constant and null.
+    // Since null is not a constant expression (because null cannot be
+    // represented in the constant pool), equality checks involving null are
+    // not captured by Flow.isTrue/isFalse.
+    // Equality checks involving a constant and null, e.g.
+    //     "" == null
+    // are safe to simplify as no side-effects can occur.
+
+    private boolean isTrue(JCTree exp) {
+        if (exp.type.isTrue())
+            return true;
+        Boolean b = expValue(exp);
+        return b == null ? false : b;
+    }
+    private boolean isFalse(JCTree exp) {
+        if (exp.type.isFalse())
+            return true;
+        Boolean b = expValue(exp);
+        return b == null ? false : !b;
+    }
+    /* look for (in)equality relations involving null.
+     * return true - if expression is always true
+     *       false - if expression is always false
+     *        null - if expression cannot be eliminated
+     */
+    private Boolean expValue(JCTree exp) {
+        while (exp.hasTag(PARENS))
+            exp = ((JCParens)exp).expr;
+
+        boolean eq;
+        switch (exp.getTag()) {
+        case EQ: eq = true;  break;
+        case NE: eq = false; break;
+        default:
+            return null;
+        }
+
+        // we have a JCBinary(EQ|NE)
+        // check if we have two literals (constants or null)
+        JCBinary b = (JCBinary)exp;
+        if (b.lhs.type.hasTag(BOT)) return expValueIsNull(eq, b.rhs);
+        if (b.rhs.type.hasTag(BOT)) return expValueIsNull(eq, b.lhs);
+        return null;
+    }
+    private Boolean expValueIsNull(boolean eq, JCTree t) {
+        if (t.type.hasTag(BOT)) return Boolean.valueOf(eq);
+        if (t.hasTag(LITERAL))  return Boolean.valueOf(!eq);
+        return null;
+    }
+
     /** Visitor method for conditional expressions.
      */
     @Override
     public void visitConditional(JCConditional tree) {
         JCTree cond = tree.cond = translate(tree.cond, syms.booleanType);
-        if (cond.type.isTrue()) {
+        if (isTrue(cond)) {
             result = convert(translate(tree.truepart, tree.type), tree.type);
             addPrunedInfo(cond);
-        } else if (cond.type.isFalse()) {
+        } else if (isFalse(cond)) {
             result = convert(translate(tree.falsepart, tree.type), tree.type);
             addPrunedInfo(cond);
         } else {
@@ -2951,10 +3001,10 @@
      */
     public void visitIf(JCIf tree) {
         JCTree cond = tree.cond = translate(tree.cond, syms.booleanType);
-        if (cond.type.isTrue()) {
+        if (isTrue(cond)) {
             result = translate(tree.thenpart);
             addPrunedInfo(cond);
-        } else if (cond.type.isFalse()) {
+        } else if (isFalse(cond)) {
             if (tree.elsepart != null) {
                 result = translate(tree.elsepart);
             } else {
@@ -3333,21 +3383,21 @@
         JCTree lhs = tree.lhs = translate(tree.lhs, formals.head);
         switch (tree.getTag()) {
         case OR:
-            if (lhs.type.isTrue()) {
+            if (isTrue(lhs)) {
                 result = lhs;
                 return;
             }
-            if (lhs.type.isFalse()) {
+            if (isFalse(lhs)) {
                 result = translate(tree.rhs, formals.tail.head);
                 return;
             }
             break;
         case AND:
-            if (lhs.type.isFalse()) {
+            if (isFalse(lhs)) {
                 result = lhs;
                 return;
             }
-            if (lhs.type.isTrue()) {
+            if (isTrue(lhs)) {
                 result = translate(tree.rhs, formals.tail.head);
                 return;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/ConstFoldTest.java	Wed Mar 19 17:39:28 2014 -0400
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8025505
+ * @summary Constant folding deficiency
+ * @library /tools/javac/lib
+ * @build ToolBox
+ * @run main ConstFoldTest
+ */
+
+import java.net.URL;
+import java.util.List;
+
+public class ConstFoldTest {
+    public static void main(String... args) throws Exception {
+        new ConstFoldTest().run();
+    }
+
+    // This is the test case. This class should end up
+    // as straight-line code with no conditionals
+    class CFTest {
+        void m() {
+            int x;
+            if (1 != 2)       x=1; else x=0;
+            if (1 == 2)       x=1; else x=0;
+            if ("" != null) x=1; else x=0;
+            if ("" == null) x=1; else x=0;
+            if (null == null) x=1; else x=0;
+            if (null != null) x=1; else x=0;
+
+            x = 1 != 2        ? 1 : 0;
+            x = 1 == 2        ? 1 : 0;
+            x = "" != null  ? 1 : 0;
+            x = "" == null  ? 1 : 0;
+            x = null == null  ? 1 : 0;
+            x = null != null  ? 1 : 0;
+
+            boolean b;
+            b = 1 != 2         && true;
+            b = 1 == 2         || true;
+            b = ("" != null) && true;
+            b = ("" == null) || true;
+            b = (null == null) && true;
+            b = (null != null) || true;
+        }
+    }
+
+    // All of the conditionals above should be eliminated.
+    // these if* bytecodes should not be seen
+    final String regex = "\\sif(?:null|nonnull|eq|ne){1}\\s";
+
+    void run() throws Exception {
+        URL url = ConstFoldTest.class.getResource("ConstFoldTest$CFTest.class");
+        String result = ToolBox.javap(new ToolBox.JavaToolArgs().setAllArgs("-c", url.getFile()));
+        System.out.println(result);
+
+        List<String> bad_codes = ToolBox.grep(regex, result, "\n");
+        if (!bad_codes.isEmpty()) {
+            for (String code : bad_codes)
+                System.out.println("Bad OpCode Found: " + code);
+            throw new Exception("constant folding failed");
+        }
+    }
+}