changeset 57651:9c53fdf6ba63

8236721: C2 should better optimize not-equal integer comparisons Summary: Narrow the type if an integer value is found to be unequal to it's lower/upper boundary. Reviewed-by: roland, neliasso
author thartmann
date Wed, 15 Jan 2020 08:44:53 +0100
parents 7f2642fd19a1
children 255d1be6295f
files src/hotspot/share/opto/cfgnode.hpp src/hotspot/share/opto/ifnode.cpp test/hotspot/jtreg/compiler/types/TestFoldNECompares.java
diffstat 3 files changed, 180 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/opto/cfgnode.hpp	Wed Jan 15 13:24:51 2020 +0800
+++ b/src/hotspot/share/opto/cfgnode.hpp	Wed Jan 15 08:44:53 2020 +0100
@@ -289,7 +289,7 @@
 
 private:
   // Helper methods for fold_compares
-  bool cmpi_folds(PhaseIterGVN* igvn);
+  bool cmpi_folds(PhaseIterGVN* igvn, bool fold_ne = false);
   bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn);
   bool has_shared_region(ProjNode* proj, ProjNode*& success, ProjNode*& fail);
   bool has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNode*& fail, PhaseIterGVN* igvn);
--- a/src/hotspot/share/opto/ifnode.cpp	Wed Jan 15 13:24:51 2020 +0800
+++ b/src/hotspot/share/opto/ifnode.cpp	Wed Jan 15 08:44:53 2020 +0100
@@ -615,9 +615,19 @@
             jint hi = cmp2_t->_hi;
             BoolTest::mask msk = if_proj->Opcode() == Op_IfTrue ? bol->_test._test : bol->_test.negate();
             switch (msk) {
-            case BoolTest::ne:
+            case BoolTest::ne: {
+              // If val is compared to its lower or upper bound, we can narrow the type
+              const TypeInt* val_t = gvn->type(val)->isa_int();
+              if (val_t != NULL && !val_t->singleton() && cmp2_t->is_con()) {
+                if (val_t->_lo == lo) {
+                  return TypeInt::make(val_t->_lo + 1, val_t->_hi, val_t->_widen);
+                } else if (val_t->_hi == hi) {
+                  return TypeInt::make(val_t->_lo, val_t->_hi - 1, val_t->_widen);
+                }
+              }
               // Can't refine type
               return NULL;
+            }
             case BoolTest::eq:
               return cmp2_t;
             case BoolTest::lt:
@@ -691,7 +701,7 @@
 //
 
 // Is the comparison for this If suitable for folding?
-bool IfNode::cmpi_folds(PhaseIterGVN* igvn) {
+bool IfNode::cmpi_folds(PhaseIterGVN* igvn, bool fold_ne) {
   return in(1) != NULL &&
     in(1)->is_Bool() &&
     in(1)->in(1) != NULL &&
@@ -699,7 +709,8 @@
     in(1)->in(1)->in(2) != NULL &&
     in(1)->in(1)->in(2) != igvn->C->top() &&
     (in(1)->as_Bool()->_test.is_less() ||
-     in(1)->as_Bool()->_test.is_greater());
+     in(1)->as_Bool()->_test.is_greater() ||
+     (fold_ne && in(1)->as_Bool()->_test._test == BoolTest::ne));
 }
 
 // Is a dominating control suitable for folding with this if?
@@ -709,7 +720,7 @@
     ctrl->in(0) != NULL &&
     ctrl->in(0)->Opcode() == Op_If &&
     ctrl->in(0)->outcnt() == 2 &&
-    ctrl->in(0)->as_If()->cmpi_folds(igvn) &&
+    ctrl->in(0)->as_If()->cmpi_folds(igvn, true) &&
     // Must compare same value
     ctrl->in(0)->in(1)->in(1)->in(1) != NULL &&
     ctrl->in(0)->in(1)->in(1)->in(1) == in(1)->in(1)->in(1);
@@ -871,7 +882,7 @@
   // sets the lower bound if any.
   Node* adjusted_lim = NULL;
   if (lo_type != NULL && hi_type != NULL && hi_type->_lo > lo_type->_hi &&
-      hi_type->_hi == max_jint && lo_type->_lo == min_jint) {
+      hi_type->_hi == max_jint && lo_type->_lo == min_jint && lo_test != BoolTest::ne) {
     assert((dom_bool->_test.is_less() && !proj->_con) ||
            (dom_bool->_test.is_greater() && proj->_con), "incorrect test");
     // this test was canonicalized
@@ -912,7 +923,7 @@
       }
     }
   } else if (lo_type != NULL && hi_type != NULL && lo_type->_lo > hi_type->_hi &&
-             lo_type->_hi == max_jint && hi_type->_lo == min_jint) {
+             lo_type->_hi == max_jint && hi_type->_lo == min_jint && lo_test != BoolTest::ne) {
 
     // this_bool = <
     //   dom_bool = < (proj = True) or dom_bool = >= (proj = False)
@@ -963,7 +974,7 @@
       }
     }
   } else {
-    const TypeInt* failtype  = filtered_int_type(igvn, n, proj);
+    const TypeInt* failtype = filtered_int_type(igvn, n, proj);
     if (failtype != NULL) {
       const TypeInt* type2 = filtered_int_type(igvn, n, fail);
       if (type2 != NULL) {
@@ -1600,11 +1611,11 @@
   return this;
 }
 
-// Map BoolTest to local table ecoding. The BoolTest (e)numerals
+// Map BoolTest to local table encoding. The BoolTest (e)numerals
 //   { eq = 0, ne = 4, le = 5, ge = 7, lt = 3, gt = 1 }
 // are mapped to table indices, while the remaining (e)numerals in BoolTest
 //   { overflow = 2, no_overflow = 6, never = 8, illegal = 9 }
-// are ignored (these are not modelled in the table).
+// are ignored (these are not modeled in the table).
 //
 static int subsuming_bool_test_encode(Node* node) {
   precond(node->is_Bool());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/types/TestFoldNECompares.java	Wed Jan 15 08:44:53 2020 +0100
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2020, 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 8236721
+ * @summary Test folding of != integer comparisons.
+ * @run main/othervm -XX:CompileCommand=compileonly,compiler.c2.TestFoldNECompares::test*
+ *                   -XX:CompileCommand=inline,compiler.c2.TestFoldNECompares::getNarrowInt*
+ *                   -Xbatch -XX:-TieredCompilation
+ *                   compiler.c2.TestFoldNECompares
+ */
+
+package compiler.c2;
+
+public class TestFoldNECompares {
+
+    public static int getNarrowInt(boolean b, int lo, int hi) {
+        return b ? lo : hi;
+    }
+
+    public static void test1(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i != 42) {
+            // i: 43..142
+            if (i <= 42) {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void test2(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i != 42) {
+            // i: 43..142
+            if (i > 42) {
+
+            } else {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void test3(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i == 42) {
+
+        } else {
+            // i: 43..142
+            if (i <= 42) {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void test4(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i == 42) {
+
+        } else {
+            // i: 43..142
+            if (i > 42) {
+
+            } else {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void test5(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i != 142) {
+            // i: 42..141
+            if (i >= 142) {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void test6(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i != 142) {
+            // i: 42..141
+            if (i < 142) {
+
+            } else {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void test7(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i == 142) {
+
+        } else {
+            // i: 42..141
+            if (i >= 142) {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void test8(boolean b) {
+        int i = getNarrowInt(b, 42, 142);
+        // i: 42..142
+        if (i == 142) {
+
+        } else {
+            // i: 42..141
+            if (i < 142) {
+
+            } else {
+                throw new RuntimeException("Should not reach here");
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 100_000; ++i) {
+            boolean b = ((i % 2) == 0);
+            test1(b);
+            test2(b);
+            test3(b);
+            test4(b);
+            test5(b);
+            test6(b);
+            test7(b);
+            test8(b);
+        }
+    }
+}