> 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