https://gcc.gnu.org/g:70e0da089fe77227c7b9e257030e0d9674e7551a
commit r17-5212-g70e0da089fe77227c7b9e257030e0d9674e7551a Author: Uros Bizjak <[email protected]> Date: Wed Oct 14 13:16:12 2026 +0200 i386: Add *extzvdi_shr to implement zero_extract with 32-bit shift [PR127883] Extracting the top bits of the low 32 bits of a DImode value, e.g. (x >> 24) & 0xff where pos + len == 32, can be done with a single 32-bit logical right shift. SImode shifts implicitly zero-extend the result to 64 bits on x86-64, so no separate masking or zero-extension is needed. Add a pre-reload define_insn_and_split that matches such a zero_extract with pos in the 1..31 range and splits it to a zero-extended SImode lshiftrt of the lowpart of the source. A pos of 0 is excluded, since the extraction is then just a zero-extension. PR target/127883 gcc/ChangeLog: * config/i386/i386.md (*extzvdi_shr): New define_insn_and_split pattern. gcc/testsuite/ChangeLog: * gcc.target/i386/pr127883-1.c: New test. * gcc.target/i386/pr127883-2.c: Ditto. Diff: --- gcc/config/i386/i386.md | 18 ++++++++++ gcc/testsuite/gcc.target/i386/pr127883-1.c | 29 ++++++++++++++++ gcc/testsuite/gcc.target/i386/pr127883-2.c | 53 ++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index d5b90291fc9c..2f4997221820 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -3658,6 +3658,24 @@ (const_string "SI") (const_string "QI")))]) +(define_insn_and_split "*extzvdi_shr" + [(set (match_operand:DI 0 "register_operand") + (zero_extract:DI (match_operand 1 "int248_register_operand") + (match_operand:QI 2 "const_1_to_31_operand") + (match_operand:QI 3 "const_1_to_31_operand"))) + (clobber (reg:CC FLAGS_REG))] + "TARGET_64BIT + && INTVAL (operands[2]) == 32 - INTVAL (operands[3]) + && ix86_pre_reload_split ()" + "#" + "&& 1" + [(parallel + [(set (match_dup 0) + (zero_extend:DI + (lshiftrt:SI (match_dup 1) (match_dup 3)))) + (clobber (reg:CC FLAGS_REG))])] + "operands[1] = gen_lowpart (SImode, operands[1]);") + (define_expand "insv<mode>" [(set (zero_extract:SWI248 (match_operand:SWI248 0 "register_operand") (match_operand:QI 1 "const_int_operand") diff --git a/gcc/testsuite/gcc.target/i386/pr127883-1.c b/gcc/testsuite/gcc.target/i386/pr127883-1.c new file mode 100644 index 000000000000..bfb75a0d7d80 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr127883-1.c @@ -0,0 +1,29 @@ +/* PR target/127883 */ +/* { dg-do compile { target { ! ia32 } } } */ +/* { dg-options "-O2 -masm=att" } */ + +/* Check that zero_extract of the top bits of the low 32 bits of a DImode + value is implemented with a single 32-bit logical right shift. */ + +typedef unsigned long long u64; + +/* pos = 1, len = 31 */ +u64 f1 (u64 x) { return (x >> 1) & 0x7fffffff; } +/* pos = 8, len = 24 */ +u64 f8 (u64 x) { return (x >> 8) & 0xffffff; } +/* pos = 16, len = 16 */ +u64 f16 (u64 x) { return (x >> 16) & 0xffff; } +/* pos = 24, len = 8 */ +u64 f24 (u64 x) { return (x >> 24) & 0xff; } +/* pos = 31, len = 1 */ +u64 f31 (u64 x) { return (x >> 31) & 0x1; } + +/* { dg-final { scan-assembler-times "shrl\[ \\t\]+(\\\$1, )?%e.." 1 } } */ +/* { dg-final { scan-assembler-times "shrl\[ \\t\]+\\\$8, %e.." 1 } } */ +/* { dg-final { scan-assembler-times "shrl\[ \\t\]+\\\$16, %e.." 1 } } */ +/* { dg-final { scan-assembler-times "shrl\[ \\t\]+\\\$24, %e.." 1 } } */ +/* { dg-final { scan-assembler-times "shrl\[ \\t\]+\\\$31, %e.." 1 } } */ +/* { dg-final { scan-assembler-not "shrq" } } */ +/* { dg-final { scan-assembler-not "movz\[bw\]l" } } */ +/* { dg-final { scan-assembler-not "movl\[ \\t\]+%eax, %eax" } } */ +/* { dg-final { scan-assembler-not "\tandl" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr127883-2.c b/gcc/testsuite/gcc.target/i386/pr127883-2.c new file mode 100644 index 000000000000..cfcfa05e4945 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr127883-2.c @@ -0,0 +1,53 @@ +/* PR target/127883 */ +/* { dg-do run { target { ! ia32 } } } */ +/* { dg-options "-O2 -masm=att" } */ + +/* Runtime check for the *extzvdi_shr pattern. Garbage in the upper + 32 bits of the source must not leak into the result, and the result + must be zero-extended to 64 bits. */ + +typedef unsigned long long u64; + +#define EXTRACT(POS, LEN) \ + __attribute__((noipa)) u64 \ + e##POS (u64 x) \ + { \ + return (x >> POS) & ((1ULL << LEN) - 1); \ + } + +EXTRACT (1, 31) +EXTRACT (8, 24) +EXTRACT (16, 16) +EXTRACT (24, 8) +EXTRACT (31, 1) + +static const u64 vals[] = +{ + 0x0000000000000000ULL, 0xffffffffffffffffULL, + 0xffffffff00000000ULL, 0x00000000ffffffffULL, + 0x123456789abcdef0ULL, 0xfedcba9876543210ULL, + 0x8000000080000000ULL, 0x0000000100000001ULL, + 0xdeadbeef80000001ULL +}; + +#define CHECK(POS, LEN, X) \ + do { \ + u64 ref = ((X) & 0xffffffffULL) >> POS; \ + if (e##POS (X) != ref) \ + __builtin_abort (); \ + } while (0) + +int +main (void) +{ + for (unsigned i = 0; i < sizeof (vals) / sizeof (vals[0]); i++) + { + u64 x = vals[i]; + CHECK (1, 31, x); + CHECK (8, 24, x); + CHECK (16, 16, x); + CHECK (24, 8, x); + CHECK (31, 1, x); + } + return 0; +}
