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.
Bootstrapped and regression tested on x86_64-pc-linux-gnu {,-m32}.
Uros.
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d5b90291fc9..2f499722182 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -3658,6 +3658,24 @@ (define_insn "*extzvqi"
(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 00000000000..bfb75a0d7d8
--- /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 00000000000..cfcfa05e494
--- /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;
+}