This is my attempt to tackle/workaround PR target/125855, a slowdown
on AMD microarchitectures following my tweak to reduce the number of
memory accesses (to load the sign-bit from the constant pool) for
negation and absolute value on x86 when using SSE math.

This patch optimizes the negation/abs of floating point values
in memory, i.e. where the source and destination are the same
memory location.

As a  motivating example:

float x;
void foo()
{
  x = -x;
}

previously with -O2 generated:

foo:    movss   x(%rip), %xmm0
        xorps   .LC0(%rip), %xmm0
        movss   %xmm0, x(%rip)
        ret

with this patch, we instead generate:

foo:    xorb    $-128, x+3(%rip)
        ret

Less instructions, less memory transfers, less registers,
less bytes (more store forwarding hazards for the hardware
folks).  This matches what LLVM does with similar code.


This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures.  Ok for mainline?


2026-06-23  Roger Sayle  <[email protected]>

gcc/ChangeLog
        * config/i386/i386.md (*neg<MODEF>2_mem): New define_insn_and_split
        for negations with the same source and destination memory address.
        (*abs<MODEF>2_mem): Likewise for ABS.
        (*negabs<MODEF>2_mem): Likewise for NEG of ABS.

gcc/testsuite/ChangeLog
        * gcc.target/i386/fabsneg-2.c: Update test case.
        * gcc.target/i386/fabsneg-3.c: New test case.


Thanks in advance,
Roger
--

diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 8db5d3b0152..3f6847fc1e6 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -15840,6 +15840,55 @@
   [(set_attr "type" "fsgn")
    (set_attr "mode" "<MODE>")])
 
+;; In-memory neg, abs and negabs.
+(define_insn_and_split "*neg<mode>2_mem"
+  [(set (match_operand:MODEF 0 "memory_operand" "=o")
+       (neg:MODEF (match_operand:MODEF 1 "memory_operand" "0")))
+   (clobber (reg:CC FLAGS_REG))]
+  "rtx_equal_p (operands[0], operands[1])"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (match_dup 2)
+             (xor:QI (match_dup 2) (match_dup 3)))
+             (clobber (reg:CC FLAGS_REG))])]
+{
+  operands[2] = adjust_address (operands[0], QImode,
+                               GET_MODE_SIZE (<MODE>mode) - 1);
+  operands[3] = GEN_INT (-128);
+})
+
+(define_insn_and_split "*abs<mode>2_mem"
+  [(set (match_operand:MODEF 0 "memory_operand" "=o")
+       (abs:MODEF (match_operand:MODEF 1 "memory_operand" "0")))
+   (clobber (reg:CC FLAGS_REG))]
+  "rtx_equal_p (operands[0], operands[1])"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (match_dup 2)
+             (and:QI (match_dup 2) (match_dup 3)))
+             (clobber (reg:CC FLAGS_REG))])]
+{
+  operands[2] = adjust_address (operands[0], QImode,
+                               GET_MODE_SIZE (<MODE>mode) - 1);
+  operands[3] = GEN_INT (127);
+})
+
+(define_insn_and_split "*negabs<mode>2_mem"
+  [(set (match_operand:MODEF 0 "memory_operand" "=o")
+       (neg:MODEF (abs:MODEF (match_operand:MODEF 1 "memory_operand" "0"))))
+   (clobber (reg:CC FLAGS_REG))]
+  "rtx_equal_p (operands[0], operands[1])"
+  "#"
+  "&& reload_completed"
+  [(parallel [(set (match_dup 2)
+             (ior:QI (match_dup 2) (match_dup 3)))
+             (clobber (reg:CC FLAGS_REG))])]
+{
+  operands[2] = adjust_address (operands[0], QImode,
+                               GET_MODE_SIZE (<MODE>mode) - 1);
+  operands[3] = GEN_INT (-128);
+})
+
 ;; Copysign instructions
 
 (define_expand "copysign<mode>3"
diff --git a/gcc/testsuite/gcc.target/i386/fabsneg-2.c 
b/gcc/testsuite/gcc.target/i386/fabsneg-2.c
index dd40c755a49..e2b723ca43d 100644
--- a/gcc/testsuite/gcc.target/i386/fabsneg-2.c
+++ b/gcc/testsuite/gcc.target/i386/fabsneg-2.c
@@ -1,22 +1,16 @@
-/* { dg-do compile } */
-/* { dg-options "-O2 -mfpmath=sse -march=skylake" } */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mfpmath=sse" } */
 
-float x;
-float y;
-float z;
+void ext (float, float, float);
 
-void foo()
+void foo(float x, float y, float z)
 {
-  x = -x;
-  y = -y;
-  z = -z;
+  ext (-x, -y, -z);
 }
 
-void bar()
+void bar(float x, float y, float z)
 {
-  x = __builtin_fabsf(x);
-  y = __builtin_fabsf(y);
-  z = __builtin_fabsf(z);
+  ext (__builtin_fabsf(x), __builtin_fabsf(y), __builtin_fabsf(z));
 }
 
 /* { dg-final { scan-assembler-times "\[lL\]C0\[,(\]" 1 } } */
diff --git a/gcc/testsuite/gcc.target/i386/fabsneg-3.c 
b/gcc/testsuite/gcc.target/i386/fabsneg-3.c
new file mode 100644
index 00000000000..1580f4ccd9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/fabsneg-3.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+float x;
+
+void foo()
+{
+  x = -x;
+}
+
+void bar()
+{
+  x = __builtin_fabsf(x);
+}
+
+/* { dg-final { scan-assembler-not "\tmov" } } */
+/* { dg-final { scan-assembler-not "\tfld" } } */

Reply via email to