> This can also handle 32-bit targets with:
>
> else if (speed && GET_MODE_SIZE (mode) > GET_MODE_SIZE (word_mode))
> && ...
> && GET_MODE (...) == word_mode.
>
> (the last check is also semi-redundant, there are no patterns that
> zext from other modes than word mode, but won't hurt anyway)
>
> with a comment updated to something like:
>
> 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
> TImode store and STV can no longer share one vector register.
>
Changed, also updated cost for double-word sign_extend which needs an
extra cdq.

Bootstrapped and regtested on x86_64-pc-linux-gnu{-m32,}.
Ok?

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]>
---
 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(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr127020-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr127020.c

diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index a8edf28f687..e299fadde8b 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -22982,6 +22982,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
@@ -22989,7 +22999,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 00000000000..6a8eb43179d
--- /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 00000000000..4adc17cfc4c
--- /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 } } */
-- 
2.34.1

Reply via email to