[v2] forwprop: Reset the range of a rewritten MOD result [PR101179]

Message ID 20260930184631.2777691-1-gcc@gcc.dp11.uk
State Committed
Commit 17c7aeffec9e1f7576957fdcb21e380d4e209c6f
Headers
Series [v2] forwprop: Reset the range of a rewritten MOD result [PR101179] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

DominicP Sept. 30, 2026, 6:46 p.m. UTC
  When X may be negative, r17-3791 still rewrites M = X % PHI <2^a, 2^b>
into M = X & PHI <2^a-1, 2^b-1> because M is only compared with zero.
That keeps the zero-ness of M but not its value, yet the range recorded
for M before the rewrite stays, so VRP folded M to 0 in the testcase.
Reset the flow-sensitive info of M in that case.

Bootstrapped and regression tested on x86_64-pc-linux-gnu; dg.exp,
tree-ssa.exp, execute.exp and the new test were also run on
aarch64-linux-gnu (qemu); no regressions.

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

	PR tree-optimization/101179
	* tree-ssa-forwprop.cc (simplify_phi_result_movdiv): Reset the
	flow-sensitive info of the result when its value changes.

gcc/testsuite/ChangeLog:

	PR tree-optimization/101179
	* gcc.dg/torture/pr101179.c: New test.

Signed-off-by: Dominic P <gcc@gcc.dp11.uk>
---
Changes in v2: the reset is decided where the MOD case is checked, with a
comment, as you suggested.

OK for trunk?

 gcc/testsuite/gcc.dg/torture/pr101179.c | 28 +++++++++++++++++++++++++
 gcc/tree-ssa-forwprop.cc                | 17 ++++++++++++---
 2 files changed, 42 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr101179.c
  

Comments

Andrew Pinski Sept. 30, 2026, 8:22 p.m. UTC | #1
On Wed, Sep 30, 2026, 2:47 PM Dominic P <gcc@gcc.dp11.uk> wrote:

> When X may be negative, r17-3791 still rewrites M = X % PHI <2^a, 2^b>
> into M = X & PHI <2^a-1, 2^b-1> because M is only compared with zero.
> That keeps the zero-ness of M but not its value, yet the range recorded
> for M before the rewrite stays, so VRP folded M to 0 in the testcase.
> Reset the flow-sensitive info of M in that case.
>
> Bootstrapped and regression tested on x86_64-pc-linux-gnu; dg.exp,
> tree-ssa.exp, execute.exp and the new test were also run on
> aarch64-linux-gnu (qemu); no regressions.
>
> Assisted-by: Claude Opus 5 (Anthropic)
>
> gcc/ChangeLog:
>
>         PR tree-optimization/101179
>         * tree-ssa-forwprop.cc (simplify_phi_result_movdiv): Reset the
>         flow-sensitive info of the result when its value changes.
>

Ok. (Using my Gmail as I am in my phone in the airport)



> gcc/testsuite/ChangeLog:
>
>         PR tree-optimization/101179
>         * gcc.dg/torture/pr101179.c: New test.
>
> Signed-off-by: Dominic P <gcc@gcc.dp11.uk>
> ---
> Changes in v2: the reset is decided where the MOD case is checked, with a
> comment, as you suggested.
>
> OK for trunk?
>
>  gcc/testsuite/gcc.dg/torture/pr101179.c | 28 +++++++++++++++++++++++++
>  gcc/tree-ssa-forwprop.cc                | 17 ++++++++++++---
>  2 files changed, 42 insertions(+), 3 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/torture/pr101179.c
>
> diff --git a/gcc/testsuite/gcc.dg/torture/pr101179.c
> b/gcc/testsuite/gcc.dg/torture/pr101179.c
> new file mode 100644
> index 00000000000..0493c01b626
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/torture/pr101179.c
> @@ -0,0 +1,28 @@
> +/* { dg-do run } */
> +
> +static int __attribute__((noinline))
> +f (int a, int b, int x, int p, int q)
> +{
> +  if (x > -1 || x < -100)
> +    return 5;
> +  int y;
> +  if (a)
> +    y = p;
> +  else
> +    y = q;
> +  int m = x % y;
> +  if (b)
> +    return 7;
> +  if (m == 0)
> +    return 1;
> +  return 2;
> +}
> +
> +int
> +main (void)
> +{
> +  volatile int one = 1, zero = 0, v = -100;
> +  if (f (one, zero, v, 64, 16) != 2)
> +    __builtin_abort ();
> +  return 0;
> +}
> diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
> index 64956b05985..f341514a8eb 100644
> --- a/gcc/tree-ssa-forwprop.cc
> +++ b/gcc/tree-ssa-forwprop.cc
> @@ -4819,6 +4819,7 @@ simplify_phi_result_movdiv (gimple *stmt, tree_code
> code)
>  {
>    tree rhs1 = gimple_assign_rhs1 (stmt);
>    tree_code new_code;
> +  bool needs_reset = false;
>
>    /* Skip complex types (PR127163) */
>    if (!INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
> @@ -4828,9 +4829,17 @@ simplify_phi_result_movdiv (gimple *stmt, tree_code
> code)
>      {
>        case TRUNC_MOD_EXPR:
>        case FLOOR_MOD_EXPR:
> -       if (!tree_expr_nonnegative_p (rhs1)
> -           && !use_in_zero_equality (gimple_assign_lhs (stmt), true))
> -         return false;
> +       if (!tree_expr_nonnegative_p (rhs1))
> +         {
> +           /* For a negative X, the truncating X % 2^k and X & (2^k - 1)
> +              differ (e.g. -3 % 4 is -3 but -3 & 3 is 1), though both are
> +              zero for the same X.  That is enough when the result is only
> +              compared with zero, but the range recorded for the MOD
> result
> +              no longer holds for the AND, so reset it.  */
> +           needs_reset = true;
> +           if (!use_in_zero_equality (gimple_assign_lhs (stmt), true))
> +             return false;
> +         }
>
>         new_code = BIT_AND_EXPR;
>         break;
> @@ -4889,6 +4898,8 @@ simplify_phi_result_movdiv (gimple *stmt, tree_code
> code)
>
>    gimple_assign_set_rhs2 (stmt, new_phires);
>    gimple_assign_set_rhs_code (stmt, new_code);
> +  if (needs_reset)
> +    reset_flow_sensitive_info (gimple_assign_lhs (stmt));
>    update_stmt (stmt);
>
>    gsi = gsi_for_phi (phi);
> --
> 2.55.0
>
>
  

Patch

diff --git a/gcc/testsuite/gcc.dg/torture/pr101179.c b/gcc/testsuite/gcc.dg/torture/pr101179.c
new file mode 100644
index 00000000000..0493c01b626
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr101179.c
@@ -0,0 +1,28 @@ 
+/* { dg-do run } */
+
+static int __attribute__((noinline))
+f (int a, int b, int x, int p, int q)
+{
+  if (x > -1 || x < -100)
+    return 5;
+  int y;
+  if (a)
+    y = p;
+  else
+    y = q;
+  int m = x % y;
+  if (b)
+    return 7;
+  if (m == 0)
+    return 1;
+  return 2;
+}
+
+int
+main (void)
+{
+  volatile int one = 1, zero = 0, v = -100;
+  if (f (one, zero, v, 64, 16) != 2)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 64956b05985..f341514a8eb 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -4819,6 +4819,7 @@  simplify_phi_result_movdiv (gimple *stmt, tree_code code)
 {
   tree rhs1 = gimple_assign_rhs1 (stmt);
   tree_code new_code;
+  bool needs_reset = false;
 
   /* Skip complex types (PR127163) */
   if (!INTEGRAL_TYPE_P (TREE_TYPE (rhs1)))
@@ -4828,9 +4829,17 @@  simplify_phi_result_movdiv (gimple *stmt, tree_code code)
     {
       case TRUNC_MOD_EXPR:
       case FLOOR_MOD_EXPR:
-	if (!tree_expr_nonnegative_p (rhs1)
-	    && !use_in_zero_equality (gimple_assign_lhs (stmt), true))
-	  return false;
+	if (!tree_expr_nonnegative_p (rhs1))
+	  {
+	    /* For a negative X, the truncating X % 2^k and X & (2^k - 1)
+	       differ (e.g. -3 % 4 is -3 but -3 & 3 is 1), though both are
+	       zero for the same X.  That is enough when the result is only
+	       compared with zero, but the range recorded for the MOD result
+	       no longer holds for the AND, so reset it.  */
+	    needs_reset = true;
+	    if (!use_in_zero_equality (gimple_assign_lhs (stmt), true))
+	      return false;
+	  }
 
 	new_code = BIT_AND_EXPR;
 	break;
@@ -4889,6 +4898,8 @@  simplify_phi_result_movdiv (gimple *stmt, tree_code code)
 
   gimple_assign_set_rhs2 (stmt, new_phires);
   gimple_assign_set_rhs_code (stmt, new_code);
+  if (needs_reset)
+    reset_flow_sensitive_info (gimple_assign_lhs (stmt));
   update_stmt (stmt);
 
   gsi = gsi_for_phi (phi);