https://gcc.gnu.org/g:abaf4268958a49d7dfd5f0d36bdb65983582b9d5

commit r17-5244-gabaf4268958a49d7dfd5f0d36bdb65983582b9d5
Author: liuhongt <[email protected]>
Date:   Mon Oct 12 23:10:04 2026 -0700

    i386: Fix double-word zero-extension cost [PR127020]
    
    The zero movzx cost in Skylake and Ice Lake tunings makes CSE duplicate
    double-word zero extensions into stores, preventing STV conversion.
    
    Use the add cost for standalone double-word zero extensions when
    optimizing for speed, since clearing the upper word requires an extra
    instruction.
    
    Similarly for double-word sign_extend, but cost it one more than
    zero_extend, so that when a value is known non-negative, zero_extend
    is preferred to sign_extend: zero_extend needs an extra xor, while
    sign_extend needs an extra cdq.
    
    gcc/ChangeLog:
    
            PR target/127020
            * config/i386/i386.cc (ix86_rtx_costs): Use the add cost for
            standalone double-word zero extensions when optimizing for speed.
            Cost standalone double-word sign extensions one more than that.
    
    gcc/testsuite/ChangeLog:
    
            PR target/127020
            * gcc.target/i386/pr127020.c: New test.
            * gcc.target/i386/pr127020-2.c: New test.
    
    Co-authored-by: H.J. Lu <[email protected]>

Diff:
---
 gcc/config/i386/i386.cc                    | 21 +++++++++++++++++-
 gcc/testsuite/gcc.target/i386/pr127020-2.c | 34 ++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr127020.c   | 16 ++++++++++++++
 3 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index c3548e5d65ae..0b29d9a4026a 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -23031,6 +23031,16 @@ ix86_rtx_costs (rtx x, machine_mode mode, int 
outer_code_i, int opno,
       if (TARGET_64BIT && mode == DImode
          && GET_MODE (XEXP (x, 0)) == SImode)
        *total = 1;
+      /* Zero extension to a double-word mode (TImode on x86-64, DImode
+        on ia32) is not a single movzx: the upper word must be cleared
+        by a separate instruction.  Charge for it; a zero or too-low
+        cost makes the extension look free, so CSE copies the extended
+        value into every double-word store and STV can no longer share
+        one vector register.  */
+      else if (speed && GET_MODE_SIZE (mode) > UNITS_PER_WORD
+              && outer_code == SET
+              && GET_MODE (XEXP (x, 0)) == word_mode)
+       *total = cost->add;
       else if (TARGET_ZERO_EXTEND_WITH_AND)
        *total = cost->add;
       else
@@ -23038,7 +23048,16 @@ ix86_rtx_costs (rtx x, machine_mode mode, int 
outer_code_i, int opno,
       return false;
 
     case SIGN_EXTEND:
-      *total = cost->movsx;
+      /* Similarly to zero_extend for a double-word mode, but cost one
+        more than zero_extend, so that when the sign bit is known to be
+        zero, zero_extend is preferred to sign_extend: zero_extend needs
+        an extra xor, while sign_extend needs an extra cdq.  */
+      if (speed && GET_MODE_SIZE (mode) > UNITS_PER_WORD
+         && outer_code == SET
+         && GET_MODE (XEXP (x, 0)) == word_mode)
+       *total = cost->add + 1;
+      else
+       *total = cost->movsx;
       return false;
 
     case ASHIFT:
diff --git a/gcc/testsuite/gcc.target/i386/pr127020-2.c 
b/gcc/testsuite/gcc.target/i386/pr127020-2.c
new file mode 100644
index 000000000000..6a8eb43179d5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr127020-2.c
@@ -0,0 +1,34 @@
+/* PR target/127020 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=cascadelake" } */
+
+/* The source is known to be nonnegative, so expand can use either a
+   zero or a sign extension.  The zero extension only needs an xor, so
+   the sign extension must cost more, or a tie makes expand choose
+   cltd/cqto/sar.  */
+
+#ifdef __x86_64__
+#define HALF long long
+#define DWORD __int128
+#define MASK __LONG_LONG_MAX__
+#else
+#define HALF int
+#define DWORD long long
+#define MASK __INT_MAX__
+#endif
+
+DWORD g;
+
+DWORD
+foo (HALF x)
+{
+  return x & MASK;
+}
+
+void
+bar (HALF x)
+{
+  g = x & MASK;
+}
+
+/* { dg-final { scan-assembler-not "cltd|cqto|sar\[lq\]" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr127020.c 
b/gcc/testsuite/gcc.target/i386/pr127020.c
new file mode 100644
index 000000000000..4adc17cfc4cb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr127020.c
@@ -0,0 +1,16 @@
+/* PR target/127020 */
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2 -msse4.1 -mtune=cascadelake -mstv -mno-stackrealign" } */
+
+extern __int128 m0, m1, m2, m3;
+
+void
+foo (unsigned long x)
+{
+  m0 = x;
+  m1 = x;
+  m2 = x;
+  m3 = x;
+}
+
+/* { dg-final { scan-assembler-times "(movaps|vmovdqa)" 4 } } */

Reply via email to