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;
+}

Reply via email to