https://gcc.gnu.org/g:c05164c348242b00b33cdf2988cc5e93d29b0478
commit r17-1680-gc05164c348242b00b33cdf2988cc5e93d29b0478 Author: Milan Tripkovic <[email protected]> Date: Thu Jun 18 17:51:59 2026 -0600 [PATCH] PR tree-optimization/125405: Missed optimization: mask/sub sign-extension idiom not canonicalized to sign extension 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 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. Diff: --- 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(+) diff --git a/gcc/match.pd b/gcc/match.pd index 475939a0b638..fdc2cf88e906 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -4958,6 +4958,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 000000000000..857dc0f7b3e0 --- /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 000000000000..175c6b7af631 --- /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 000000000000..2aacad89b6c2 --- /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
