On Fri, Oct 16, 2026 at 2:25 AM liuhongt <[email protected]> wrote: > > > 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.
OK. Thanks, Uros. > > 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 >
