]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ifcvt: Keep only reaching definitions when rewiring sets [PR126184]
authorKyrylo Tkachov <ktkachov@nvidia.com>
Wed, 22 Jul 2026 14:45:51 +0000 (16:45 +0200)
committerKyrylo Tkachov <ktkachov@nvidia.com>
Sat, 25 Jul 2026 08:33:23 +0000 (10:33 +0200)
init_noce_multiple_sets_info records earlier SET destinations that are
mentioned by a later SET source.  When a pseudo is set more than once, only
its most recent prior definition reaches that source.

Recording every definition is unsafe in the second
noce_convert_multiple_sets_1 attempt.  The newest definition can be emitted
directly into its target pseudo, making its replacement a no-op.  A later
replacement using an older definition then substitutes a stale value.

PR126184 contains the following dependency chain:

  c = x + 1;
  x = c * y;
  c = z + 3;
  y = c * x;

The unfixed conversion computes the last multiply with the temporary that
holds `x + 1`, rather than the reaching `z + 3` value.

The unfixed final sequence forms `x + 1` in x0 and later uses x0 as the
multiply operand:

  add     x0, x1, 1
  csel    x1, x1, x3, eq
  mul     x0, x1, x0

With the fix, x3 retains z until `z + 3` is formed and used by the multiply:

  add     x3, x3, 3
  csel    x0, x0, x1, ne
  mul     x3, x0, x3

Walk definitions from newest to oldest and record only the first match for
each pseudo.  Add an AArch64 execution test for the reported dependency.

Bootstrapped and tested on aarch64-none-linux-gnu and
x86_64-pc-linux-gnu.

gcc/ChangeLog:

PR rtl-optimization/126184
* ifcvt.cc (init_noce_multiple_sets_info): Record only the most
recent prior definition of each pseudo register.

gcc/testsuite/ChangeLog:

PR rtl-optimization/126184
* gcc.target/aarch64/pr126184.c: New test.

Signed-off-by: Kyrylo Tkachov <ktkachov@nvidia.com>
gcc/ifcvt.cc
gcc/testsuite/gcc.target/aarch64/pr126184.c [new file with mode: 0644]

index 5c5580de9d12a56c2dcc98543fac3fc9bee2bf94..5ea25f8fbe719e4c22d08f1441cd0bc3e12f8208 100644 (file)
@@ -4355,12 +4355,14 @@ init_noce_multiple_sets_info (basic_block bb,
       gcc_checking_assert (REG_P (dest) && !HARD_REGISTER_P (dest));
       info->need_cmov = bitmap_bit_p (bb_live_out, REGNO (dest));
 
-      /* Check if the current SET's source is the same
-        as any previously seen destination.
+      /* Check if the current SET's source mentions any previously seen
+        destination.  Keep only the newest definition of each pseudo register.
         This is quadratic but the number of insns in BB
         is bounded by PARAM_MAX_RTL_IF_CONVERSION_INSNS.  */
+      auto_bitmap rewired_regs;
       for (int i = count - 1; i >= 0; --i)
-       if (reg_mentioned_p (dests[i], src))
+       if (reg_mentioned_p (dests[i], src)
+           && bitmap_set_bit (rewired_regs, REGNO (dests[i])))
          insn_info[count]->rewired_src.safe_push (i);
 
       dests.safe_push (dest);
diff --git a/gcc/testsuite/gcc.target/aarch64/pr126184.c b/gcc/testsuite/gcc.target/aarch64/pr126184.c
new file mode 100644 (file)
index 0000000..920d474
--- /dev/null
@@ -0,0 +1,31 @@
+/* PR rtl-optimization/126184 */
+/* { dg-do run } */
+/* { dg-options "-O2 -fno-tree-ter -fno-tree-coalesce-vars -fdump-rtl-ce1" } */
+/* { dg-additional-options "--param=max-rtl-if-conversion-unpredictable-cost=100" } */
+
+__attribute__ ((noipa)) unsigned long long
+f (unsigned long long c, unsigned long long x, unsigned long long y,
+   unsigned long long z)
+{
+  if (c != 0)
+    {
+      c = x + 1;
+      x = c * y;
+      c = z + 3;
+      y = c * x;
+    }
+
+  return x + y;
+}
+
+int
+main (void)
+{
+  if (f (1, 13, 17, 23) != 6426)
+    __builtin_abort ();
+  if (f (0, 13, 17, 23) != 30)
+    __builtin_abort ();
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through noce_convert_multiple_sets" 1 "ce1" } } */