This patch implement new pattern for match.pd that recognize the
sign-extension idiom (x & low_bits) - (x & sign_bit) and tests that covers it.
Pattern is based on conversation from bug report.

TRUNK riscv output:
 sext8_hd:
          andi    a5,a0,127
          andi    a0,a0,128
          sub     a0,a5,a0
          ret

 sext16_hd:
          slli    a5,a0,49
          li      a4,32768
          srli    a5,a5,49
          and     a0,a0,a4
          sub     a0,a5,a0
          ret

NEW riscv output:
sext8_hd:
        slliw   a0,a0,24
        sraiw   a0,a0,24
        ret

sext16_hd:
        slliw   a0,a0,16
        sraiw   a0,a0,16
        ret

TRUNK x86 output:
"sext8_hd":
        mov     rax, rdi
        and     edi, 128
        and     eax, 127
        sub     rax, rdi
        ret

"sext16_hd":
        mov     rax, rdi
        and     edi, 32768
        and     eax, 32767
        sub     rax, rdi
        ret

new x86 output:
sext8_hd:
        movsbq  %dil, %rax
        ret

sext16_hd:
        movswq  %di, %rax
        ret

2026-06-15  Milan Tripkovic  <[email protected]>

       PR target/125405

gcc/ChangeLog:

      * match.pd: New pattern added

gcc/testsuite/ChangeLog:

      * gcc.dg/pr125405-bitint.c: New test.
      * gcc.dg/tree-ssa/pr125405.c: New test.
      * gcc.target/riscv/pr125405.c: New test.




CONFIDENTIALITY: The contents of this e-mail are confidential and intended only 
for the above addressee(s). If you are not the intended recipient, or the 
person responsible for delivering it to the intended recipient, copying or 
delivering it to anyone else or using it in any unauthorized manner is 
prohibited and may be unlawful. If you receive this e-mail by mistake, please 
notify the sender and the systems administrator at [email protected] 
immediately.
---
 gcc/match.pd                              | 17 +++++++++++
 gcc/testsuite/gcc.dg/pr125405-bitint.c    | 36 +++++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr125405.c  | 24 +++++++++++++++
 gcc/testsuite/gcc.target/riscv/pr125405.c | 22 ++++++++++++++
 4 files changed, 99 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/pr125405-bitint.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr125405.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/pr125405.c

diff --git a/gcc/match.pd b/gcc/match.pd
index e0d7ef80e1..32d803f362 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -4927,6 +4927,23 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
      (if (stype && (width == 1 || type_has_mode_precision_p (stype)))
       (convert (convert:stype @0))))))))
 
+/* PR125405: (x & ((1 << (N - 1)) - 1)) - (x & (1 << (N - 1)))
+   -> sign_extend<N> (x). */
+(simplify
+ (minus (bit_and @0 INTEGER_CST@1) (bit_and @0 integer_pow2p@2))
+ (if (INTEGRAL_TYPE_P (type)
+      && TYPE_PRECISION (type) <= MAX_FIXED_MODE_SIZE
+      && wi::to_wide (@1) + 1 == wi::to_wide (@2))
+  (with {
+    int width = wi::exact_log2 (wi::to_wide (@2)) + 1;
+    tree ntype = NULL_TREE;
+
+    if (width > 0 && width < TYPE_PRECISION (type))
+      ntype = build_nonstandard_integer_type (width, SIGNED);
+   }
+   (if (ntype && type_has_mode_precision_p (ntype))
+    (convert (convert:ntype @0))))))
+
 /* Optimize x >> x into 0 */
 (simplify
  (rshift @0 @0)
diff --git a/gcc/testsuite/gcc.dg/pr125405-bitint.c 
b/gcc/testsuite/gcc.dg/pr125405-bitint.c
new file mode 100644
index 0000000000..857dc0f7b3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr125405-bitint.c
@@ -0,0 +1,36 @@
+/* PR tree-optimization/125405 */
+/* { dg-do compile { target bitint } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+unsigned _BitInt(32)
+sext8_bitint32 (unsigned _BitInt(32) x)
+{
+  return (x & (unsigned _BitInt(32)) 0x7f)
+         - (x & (unsigned _BitInt(32)) 0x80);
+}
+
+unsigned _BitInt(32)
+sext16_bitint32 (unsigned _BitInt(32) x)
+{
+  return (x & (unsigned _BitInt(32)) 0x7fff)
+         - (x & (unsigned _BitInt(32)) 0x8000);
+}
+
+unsigned _BitInt(32)
+no_sext_bad_masks (unsigned _BitInt(32) x)
+{
+  return (x & (unsigned _BitInt(32)) 0x7e)
+         - (x & (unsigned _BitInt(32)) 0x80);
+}
+
+unsigned _BitInt(32)
+no_sext_bad_pair (unsigned _BitInt(32) x)
+{
+  return (x & (unsigned _BitInt(32)) 0xff)
+         - (x & (unsigned _BitInt(32)) 0x80);
+}
+
+/* { dg-final { scan-tree-dump-times "\\(signed char\\)" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\(signed short\\)" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump " & 126" "optimized" } } */
+/* { dg-final { scan-tree-dump " & 255" "optimized" } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125405.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125405.c
new file mode 100644
index 0000000000..175c6b7af6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125405.c
@@ -0,0 +1,24 @@
+/* PR tree-optimization/125405 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+typedef unsigned long xlen_t;
+
+xlen_t
+sext8_hd (xlen_t x)
+{
+  return (x & 0x7fUL) - (x & 0x80UL);
+}
+
+xlen_t
+sext16_hd (xlen_t x)
+{
+  return (x & 0x7fffUL) - (x & 0x8000UL);
+}
+
+/* { dg-final { scan-tree-dump-times "\\(signed char\\)" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\(signed short\\)" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not " & 127" "optimized" } } */
+/* { dg-final { scan-tree-dump-not " & 128" "optimized" } } */
+/* { dg-final { scan-tree-dump-not " & 32767" "optimized" } } */
+/* { dg-final { scan-tree-dump-not " & 32768" "optimized" } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/pr125405.c 
b/gcc/testsuite/gcc.target/riscv/pr125405.c
new file mode 100644
index 0000000000..2aacad89b6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr125405.c
@@ -0,0 +1,22 @@
+/* PR tree-optimization/125405 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d" } */
+
+typedef unsigned long xlen_t;
+
+xlen_t
+sext8_hd (xlen_t x)
+{
+  return (x & 0x7fUL) - (x & 0x80UL);
+}
+
+xlen_t
+sext16_hd (xlen_t x)
+{
+  return (x & 0x7fffUL) - (x & 0x8000UL);
+}
+
+/* { dg-final { scan-assembler-times "\\mslliw\\M" 2 } } */
+/* { dg-final { scan-assembler-times "\\msraiw\\M" 2 } } */
+/* { dg-final { scan-assembler-not "\\mandi\\M" } } */
+/* { dg-final { scan-assembler-not "\\msub\\M" } } */
\ No newline at end of file
-- 
2.34.1

Reply via email to