[gcc r17-2947] AArch64: block COMPLEX_MUL when honoring signed zeros [PR126589]

Tamar Christina via Gcc-cvs <[email protected]> Tue, 4 Aug 2026 14:26:22 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:30b4b8f2baf42c8842233a2d075c77a01eee4d29

commit r17-2947-g30b4b8f2baf42c8842233a2d075c77a01eee4d29
Author: Tamar Christina <[email protected]>
Date:   Tue Aug 4 15:25:54 2026 +0100

    AArch64: block COMPLEX_MUL when honoring signed zeros [PR126589]
    
    as PR126589 points out on AArch64 we emulate COMPLEX_MUL using COMPLEX_FMA by
    seeding the accumulator with +0.0.  However depending on the rounding/flushing
    behavior we can end up with the wrong sign on the zero result.
    
    There's no real performant way to fix this (though I wonder if a BSL to copy
    the sign bits would be faster) so this adds !HONOR_SIGNED_ZEROS as a requirement
    for AArch64's expanders for cmul.
    
    gcc/ChangeLog:
    
            PR target/126589
            * config/aarch64/aarch64-simd.md (cmul<conj_op><mode>3): Require not
            honor signed zeros.
            * config/aarch64/aarch64-sve.md (cmul<conj_op><mode>3): Require not
            honor signed zeros.
    
    gcc/testsuite/ChangeLog:
    
            PR target/126589
            * gcc.target/aarch64/sve/complex_mul_1.c: Add -fno-signed-zeros.
            * gfortran.target/aarch64/pr122408_1.f90: Likewise.
            * gcc.target/aarch64/pr126589.c: New test.
            * gcc.target/aarch64/sve/pr126589.c: New test.

Diff:
---
 gcc/config/aarch64/aarch64-simd.md                 |  2 +-
 gcc/config/aarch64/aarch64-sve.md                  |  2 +-
 gcc/testsuite/gcc.target/aarch64/pr126589.c        | 36 ++++++++++++++++++++++
 .../gcc.target/aarch64/sve/complex_mul_1.c         |  2 +-
 gcc/testsuite/gcc.target/aarch64/sve/pr126589.c    | 36 ++++++++++++++++++++++
 .../gfortran.target/aarch64/pr122408_1.f90         |  2 +-
 6 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/gcc/config/aarch64/aarch64-simd.md b/gcc/config/aarch64/aarch64-simd.md
index 433f16052bf2..aa6c1fa07353 100644
--- a/gcc/config/aarch64/aarch64-simd.md
+++ b/gcc/config/aarch64/aarch64-simd.md
@@ -670,7 +670,7 @@
 	(unspec:VHSDF [(match_operand:VHSDF 1 "register_operand")
 		       (match_operand:VHSDF 2 "register_operand")]
 		       FCMUL_OP))]
-  "TARGET_COMPLEX && !BYTES_BIG_ENDIAN"
+  "TARGET_COMPLEX && !BYTES_BIG_ENDIAN && !HONOR_SIGNED_ZEROS (<MODE>mode)"
 {
   rtx tmp = force_reg (<MODE>mode, CONST0_RTX (<MODE>mode));
   rtx res1 = gen_reg_rtx (<MODE>mode);
diff --git a/gcc/config/aarch64/aarch64-sve.md b/gcc/config/aarch64/aarch64-sve.md
index 105b34eb8fa9..f665417dba41 100644
--- a/gcc/config/aarch64/aarch64-sve.md
+++ b/gcc/config/aarch64/aarch64-sve.md
@@ -8297,7 +8297,7 @@
 	   [(match_operand:SVE_FULL_F 1 "register_operand")
 	    (match_operand:SVE_FULL_F 2 "register_operand")]
 	  FCMUL_OP))]
-  "TARGET_SVE"
+  "TARGET_SVE && !HONOR_SIGNED_ZEROS (<MODE>mode)"
 {
   rtx pred_reg = aarch64_ptrue_reg (<VPRED>mode);
   rtx gp_mode = gen_int_mode (SVE_RELAXED_GP, SImode);
diff --git a/gcc/testsuite/gcc.target/aarch64/pr126589.c b/gcc/testsuite/gcc.target/aarch64/pr126589.c
new file mode 100644
index 000000000000..cece07d07a77
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr126589.c
@@ -0,0 +1,36 @@
+/* { dg-do run { target arm_v8_3a_complex_neon_hw } } */
+/* { dg-require-effective-target aarch64_little_endian } */
+/* { dg-options "-O3 -march=armv8.3-a" } */
+
+#define N 64
+double a[N], b[N], c[N];
+
+__attribute__((noipa)) void
+mul (double *__restrict cc, double *__restrict aa, double *__restrict bb, int n)
+{
+  for (int i = 0; i < n; i += 2)
+    {
+      cc[i] = aa[i] * bb[i] - aa[i + 1] * bb[i + 1];
+      cc[i + 1] = aa[i] * bb[i + 1] + aa[i + 1] * bb[i];
+    }
+}
+
+int
+main (void)
+{
+  for (int i = 0; i < N; i += 2)
+    {
+      a[i] = -0.0;
+      a[i + 1] = 0.0;
+      b[i] = 1.0;
+      b[i + 1] = 1.0;
+    }
+
+  mul (c, a, b, N);
+
+  for (int i = 0; i < N; i += 2)
+    if (!__builtin_signbit (c[i]))
+      __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/complex_mul_1.c b/gcc/testsuite/gcc.target/aarch64/sve/complex_mul_1.c
index d197e7d0d8e3..7398338f9fd6 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/complex_mul_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/complex_mul_1.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O2 -fgimple -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fgimple -fdump-tree-optimized -fno-signed-zeros" } */
 
 void __GIMPLE
 foo (__SVFloat64_t x, __SVFloat64_t y, __SVFloat64_t *res1,
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr126589.c b/gcc/testsuite/gcc.target/aarch64/sve/pr126589.c
new file mode 100644
index 000000000000..0612df53df5b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/pr126589.c
@@ -0,0 +1,36 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-require-effective-target arm_v8_3a_complex_neon_hw } */
+/* { dg-options "-O3 -march=armv8.3-a+sve -mautovec-preference=sve-only" } */
+
+#define N 64
+double a[N], b[N], c[N];
+
+__attribute__((noipa)) void
+mul (double *__restrict cc, double *__restrict aa, double *__restrict bb, int n)
+{
+  for (int i = 0; i < n; i += 2)
+    {
+      cc[i] = aa[i] * bb[i] - aa[i + 1] * bb[i + 1];
+      cc[i + 1] = aa[i] * bb[i + 1] + aa[i + 1] * bb[i];
+    }
+}
+
+int
+main (void)
+{
+  for (int i = 0; i < N; i += 2)
+    {
+      a[i] = -0.0;
+      a[i + 1] = 0.0;
+      b[i] = 1.0;
+      b[i + 1] = 1.0;
+    }
+
+  mul (c, a, b, N);
+
+  for (int i = 0; i < N; i += 2)
+    if (!__builtin_signbit (c[i]))
+      __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90 b/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
index c1996b748699..b06950d1baae 100644
--- a/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
+++ b/gcc/testsuite/gfortran.target/aarch64/pr122408_1.f90
@@ -1,5 +1,5 @@
 ! { dg-do compile }
-! { dg-additional-options "-O2 -march=armv8.3-a" }
+! { dg-additional-options "-O2 -march=armv8.3-a -fno-signed-zeros" }
 
 subroutine c_add_ab(n, a, c, b)         ! C += A * B
   use iso_fortran_env, only: real64