From 5a21d735ffa345f956d2c637b4e13f55c907a219 Mon Sep 17 00:00:00 2001
From: wangjiawei <wangjiawei80@huawei.com>
Date: Fri, 28 Apr 2023 10:41:14 +0800
Subject: [PATCH 15/15] 8305541: C2 Div/Mod nodes without zero check could be
 split through iv phi of loop resulting in SIGFPE

DTS/AR: DTS2023041008055
Summary: <hotspot>  : 8305541: C2 Div/Mod nodes without zero check could be split through iv phi of loop resulting in SIGFPE
LLT: NA
Patch Type: backport
Bug url: https://bugs.openjdk.org/browse/JDK-8305541
---
 hotspot/src/share/vm/opto/loopnode.hpp        |   4 +-
 hotspot/src/share/vm/opto/loopopts.cpp        |  40 +++++
 .../c2/TestSplitDivisionThroughPhi.java       | 155 ++++++++++++++++++
 3 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 hotspot/test/compiler/c2/TestSplitDivisionThroughPhi.java

diff --git a/hotspot/src/share/vm/opto/loopnode.hpp b/hotspot/src/share/vm/opto/loopnode.hpp
index 6f70b5065..f8750e54a 100644
--- a/hotspot/src/share/vm/opto/loopnode.hpp
+++ b/hotspot/src/share/vm/opto/loopnode.hpp
@@ -1071,7 +1071,9 @@ private:
   Node *place_near_use( Node *useblock ) const;
   Node* try_move_store_before_loop(Node* n, Node *n_ctrl);
   void try_move_store_after_loop(Node* n);
-
+  bool cannot_split_division(const Node* n, const Node* region) const;
+  static bool is_divisor_counted_loop_phi(const Node* divisor, const Node* loop);
+  bool loop_phi_backedge_type_contains_zero(const Node* phi_divisor, const Type* zero) const;
   bool _created_loop_node;
 public:
   void set_created_loop_node() { _created_loop_node = true; }
diff --git a/hotspot/src/share/vm/opto/loopopts.cpp b/hotspot/src/share/vm/opto/loopopts.cpp
index 20bdb1493..28bfcb75b 100644
--- a/hotspot/src/share/vm/opto/loopopts.cpp
+++ b/hotspot/src/share/vm/opto/loopopts.cpp
@@ -51,6 +51,10 @@ Node *PhaseIdealLoop::split_thru_phi( Node *n, Node *region, int policy ) {
     return NULL;
   }
 
+  if (cannot_split_division(n, region)) {
+    return NULL;
+  }
+
   int wins = 0;
   assert(!n->is_CFG(), "");
   assert(region->is_Region(), "");
@@ -200,6 +204,42 @@ Node *PhaseIdealLoop::split_thru_phi( Node *n, Node *region, int policy ) {
   return phi;
 }
 
+// Return true if 'n' is a Div or Mod node (without zero check If node which was removed earlier) with a loop phi divisor
+// of a trip-counted (integer or long) loop with a backedge input that could be zero (include zero in its type range). In
+// this case, we cannot split the division to the backedge as it could freely float above the loop exit check resulting in
+// a division by zero. This situation is possible because the type of an increment node of an iv phi (trip-counter) could
+// include zero while the iv phi does not (see PhiNode::Value() for trip-counted loops where we improve types of iv phis).
+// We also need to check other loop phis as they could have been created in the same split-if pass when applying
+// PhaseIdealLoop::split_thru_phi() to split nodes through an iv phi.
+bool PhaseIdealLoop::cannot_split_division(const Node* n, const Node* region) const {
+  const Type* zero;
+  switch (n->Opcode()) {
+    case Op_DivI:
+    case Op_ModI:
+      zero = TypeInt::ZERO;
+      break;
+    case Op_DivL:
+    case Op_ModL:
+      zero = TypeLong::ZERO;
+      break;
+    default:
+      return false;
+  }
+
+  assert(n->in(0) == NULL, "divisions with zero check should already have bailed out earlier in split-if");
+  Node* divisor = n->in(2);
+  return is_divisor_counted_loop_phi(divisor, region) &&
+         loop_phi_backedge_type_contains_zero(divisor, zero);
+}
+
+bool PhaseIdealLoop::is_divisor_counted_loop_phi(const Node* divisor, const Node* loop) {
+  return loop->is_CountedLoop() && divisor->is_Phi() && divisor->in(0) == loop;
+}
+
+bool PhaseIdealLoop::loop_phi_backedge_type_contains_zero(const Node* phi_divisor, const Type* zero) const {
+  return _igvn.type(phi_divisor->in(LoopNode::LoopBackControl))->filter_speculative(zero) != Type::TOP;
+}
+
 //------------------------------dominated_by------------------------------------
 // Replace the dominated test with an obvious true or false.  Place it on the
 // IGVN worklist for later cleanup.  Move control-dependent data Nodes on the
diff --git a/hotspot/test/compiler/c2/TestSplitDivisionThroughPhi.java b/hotspot/test/compiler/c2/TestSplitDivisionThroughPhi.java
new file mode 100644
index 000000000..0a59783fa
--- /dev/null
+++ b/hotspot/test/compiler/c2/TestSplitDivisionThroughPhi.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 8299259
+ * @summary Test various cases of divisions/modulo which should not be split through iv phis.
+ * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:LoopUnrollLimit=0 -XX:+StressGCM
+ *                   -XX:CompileCommand=compileonly,TestSplitDivisionThroughPhi::* TestSplitDivisionThroughPhi
+ */
+
+/**
+ * @test
+ * @bug 8299259
+ * @summary Test various cases of divisions/modulo which should not be split through iv phis.
+ * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:LoopUnrollLimit=0 -XX:+StressGCM
+ *                   -XX:CompileCommand=compileonly,TestSplitDivisionThroughPhi::* TestSplitDivisionThroughPhi
+ */
+
+
+public class TestSplitDivisionThroughPhi {
+    static int iFld;
+    static long lFld;
+    static boolean flag;
+
+
+    public static void main(String[] strArr) {
+        for (int i = 0; i < 5000; i++) {
+            testPushDivIThruPhi();
+            testPushDivIThruPhiInChain();
+            testPushModIThruPhi();
+            testPushModIThruPhiInChain();
+            testPushDivLThruPhi();
+            testPushDivLThruPhiInChain();
+            testPushModLThruPhi();
+            testPushModLThruPhiInChain();
+        }
+    }
+
+    // Already fixed by JDK-8248552.
+    static void testPushDivIThruPhi() {
+        for (int i = 10; i > 1; i -= 2) {
+            // The Div node is only split in later loop opts phase because the zero divisor check is only removed
+            // in IGVN after the first loop opts phase.
+            //
+            // iv phi i type: [2..10]
+            // When splitting the DivI through the iv phi, it ends up on the back edge with the trip count decrement
+            // as input which has type [0..8]. We end up executing a division by zero on the last iteration because
+            // the DivI it is not pinned to the loop exit test and can freely float above the loop exit check.
+            iFld = 10 / i;
+        }
+    }
+
+    // Same as above but with an additional Mul node between the iv phi and the Div node. Both nodes are split through
+    // the iv phi in one pass of Split If.
+    static void testPushDivIThruPhiInChain() {
+        for (int i = 10; i > 1; i -= 2) {
+            // Empty one iteration loop which is only removed after split if in first loop opts phase. This prevents
+            // that the Mul node is already split through the iv phi while the Div node cannot be split yet due to
+            // the zero divisor check which can only be removed in the IGVN after the first loop opts pass.
+            for (int j = 0; j < 1; j++) {
+            }
+            iFld = 10 / (i * 100);
+        }
+    }
+
+    // Already fixed by JDK-8248552.
+    static void testPushModIThruPhi() {
+        for (int i = 10; i > 1; i -= 2) {
+            iFld = 10 / i;
+        }
+    }
+
+    // Same as above but with ModI.
+    static void testPushModIThruPhiInChain() {
+        for (int i = 10; i > 1; i -= 2) {
+            for (int j = 0; j < 1; j++) {
+            }
+            iFld = 10 / (i * 100);
+        }
+    }
+
+    // Long cases only trigger since JDK-8256655.
+
+    // Same as above but with DivL.
+    static void testPushDivLThruPhi() {
+        for (long i = 10; i > 1; i -= 2) {
+            lFld = 10L / i;
+
+            // Loop that is not removed such that we do not transform the outer LongCountedLoop (only done if innermost)
+            for (int j = 0; j < 10; j++) {
+                flag = !flag;
+            }
+        }
+    }
+
+    // Same as above but with DivL.
+    static void testPushDivLThruPhiInChain() {
+        for (long i = 10; i > 1; i -= 2) {
+            for (int j = 0; j < 1; j++) {
+            }
+            lFld = 10L / (i * 100L);
+
+            for (int j = 0; j < 10; j++) {
+                flag = !flag;
+            }
+        }
+    }
+
+    // Same as above but with ModL
+    static void testPushModLThruPhi() {
+        for (long i = 10; i > 1; i -= 2) {
+            lFld = 10L % i;
+
+            for (int j = 0; j < 10; j++) {
+                flag = !flag;
+            }
+        }
+    }
+
+    // Same as above but with ModL
+    static void testPushModLThruPhiInChain() {
+        for (long i = 10; i > 1; i -= 2) {
+            for (int j = 0; j < 1; j++) {
+            }
+            lFld = 10L % (i * 100L);
+
+            for (int j = 0; j < 10; j++) {
+                flag = !flag;
+            }
+        }
+    }
+}
+
-- 
2.19.0

