https://gcc.gnu.org/g:c348f5c5e991dd9d4e13d5943485a938cc4342a8

commit r17-2512-gc348f5c5e991dd9d4e13d5943485a938cc4342a8
Author: Dusan Stojkovic <[email protected]>
Date:   Sat Jul 18 10:38:41 2026 -0600

    [PR target/123883] Inefficient bit manipulation code on RISC-V port
    
    With changes to the RISC-V backend, it's possible to get the desired code
    generation for this test with a simple match.pd pattern.
    
    Essentially we have (1 << N) & (1 << N) where each shift is in a different
    type.  With some constraints, we can collapse that down to just (1 << N) in
    the wider type.  That in turn allows collapsing the entire sequence down to
    a single bit set (6 instructions -> 3 instructions on rv64gcb).
    
            PR target/123883
    
    gcc/ChangeLog:
    
            * match.pd ((1<<N) & (1<<N) with different types): New
            pattern.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/riscv/pr123883-a.c: New test.
            * gcc.target/riscv/pr123883-b.c: New test.

Diff:
---
 gcc/match.pd                                | 21 ++++++++++
 gcc/testsuite/gcc.target/riscv/pr123883-a.c | 59 +++++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/riscv/pr123883-b.c | 58 ++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index 07b3f72d73b3..d750438382e9 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -12545,6 +12545,27 @@ and,
         && @0 == @3)
     (bit_xor (rrotate @0 @4) @2)))
 
+
+/* (T0)(1 << x) & (T1)(1 << x) -> (T)(1 << x),
+ *  but keep the shift in the wider type to avoid introducing
+ *  undefined behaviour.  */
+(simplify
+ (bit_and:c
+  (convert? (lshift@2 integer_onep@1 @0))
+  (convert? (lshift@3 integer_onep@4 @0)))
+ (with
+  {
+    tree ltype0 = TREE_TYPE (@2);
+    tree ltype1 = TREE_TYPE (@3);
+  }
+  (if (INTEGRAL_TYPE_P (type)
+       && INTEGRAL_TYPE_P (ltype0)
+       && INTEGRAL_TYPE_P (ltype1)
+       && element_precision (type) <= element_precision (ltype0)
+       && element_precision (ltype0) <= element_precision (ltype1))
+       (convert:type
+         (lshift:ltype1 { @4; } @0)))))
+
 /* Optimize extraction from a uniform vector to a representative element as
    long as the requested element is within range.  */
 (simplify (IFN_VEC_EXTRACT (vec_duplicate @0) INTEGER_CST@1)
diff --git a/gcc/testsuite/gcc.target/riscv/pr123883-a.c 
b/gcc/testsuite/gcc.target/riscv/pr123883-a.c
new file mode 100644
index 000000000000..39cc2f5ee09b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr123883-a.c
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcb -mabi=lp64d" { target rv64 } } */
+/* { dg-options "-march=rv32gcb -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+
+#define TEST_STORE_NARROW_SHIFT(TYPE, WTYPE, NAME)               \
+void                                                             \
+NAME (TYPE *data, unsigned int lo_bit)                           \
+{                                                                \
+  WTYPE mask = (((WTYPE) 1UL << 1) - 1) << lo_bit;               \
+  *data = (*data & ~mask) | ((1U << lo_bit) & mask);             \
+}
+
+#define TEST_RET_NARROW_SHIFT(TYPE, WTYPE, NAME)                 \
+TYPE                                                             \
+NAME (TYPE data, unsigned int lo_bit)                            \
+{                                                                \
+  WTYPE mask = (((WTYPE) 1 << 1) - 1) << lo_bit;                 \
+  return (data & ~mask) | ((1U << lo_bit) & mask);               \
+}
+
+/* WTYPE = unsigned int */
+TEST_STORE_NARROW_SHIFT(unsigned char,  unsigned int, store_uc_ui_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned int, store_us_ui_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char,    unsigned int, ret_uc_ui_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short,   unsigned int, ret_us_ui_narrow)
+
+/* WTYPE = unsigned long */
+TEST_STORE_NARROW_SHIFT(unsigned char,  unsigned long, store_uc_ul_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned long, store_us_ul_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned int,   unsigned long, store_ui_ul_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char,    unsigned long, ret_uc_ul_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short,   unsigned long, ret_us_ul_narrow)
+TEST_RET_NARROW_SHIFT(unsigned int,     unsigned long, ret_ui_ul_narrow)
+
+/* WTYPE = unsigned long long */
+TEST_STORE_NARROW_SHIFT(unsigned char,  unsigned long long, 
store_uc_ull_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned long long, 
store_us_ull_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned int,   unsigned long long, 
store_ui_ull_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char,    unsigned long long, ret_uc_ull_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short,   unsigned long long, ret_us_ull_narrow)
+TEST_RET_NARROW_SHIFT(unsigned int,     unsigned long long, ret_ui_ull_narrow)
+
+/* { dg-final { scan-assembler-times "\\tbset\\t" 14 { target rv64 } } } */
+/* Current remaining missed SI-mode cases.  */
+/* { dg-final { scan-assembler-times "\\tsllw\\t" 4 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "\\tbinv\\t" 2 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "\\tsext.w\\t" 4 { target rv64 } } } */
+
+
+/* { dg-final { scan-assembler-times "\\tbset\\t" 16 { target rv32 } } } */
+/* Testcases with ULL. */
+/* { dg-final { scan-assembler-times "\\tbinv\\t" 2 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "\\tand\\t" 8 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "\\tsrai\\t" 6 { target rv32 } } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/pr123883-b.c 
b/gcc/testsuite/gcc.target/riscv/pr123883-b.c
new file mode 100644
index 000000000000..4e623764228e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr123883-b.c
@@ -0,0 +1,58 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcb -mabi=lp64d" { target rv64 } } */
+/* { dg-options "-march=rv32gcb -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+
+#define TEST_STORE_NARROW_SHIFT(TYPE, WTYPE, NAME)               \
+void                                                             \
+NAME (TYPE *data, unsigned int lo_bit)                           \
+{                                                                \
+  WTYPE mask = ((1UL << 1) - 1) << lo_bit;                       \
+  *data = (*data & ~mask) | (((WTYPE) 1U << lo_bit) & mask);     \
+}
+
+#define TEST_RET_NARROW_SHIFT(TYPE, WTYPE, NAME)                 \
+TYPE                                                             \
+NAME (TYPE data, unsigned int lo_bit)                            \
+{                                                                \
+  WTYPE mask = ((1 << 1) - 1) << lo_bit;                         \
+  return (data & ~mask) | (((WTYPE)1U << lo_bit) & mask);        \
+}
+
+/* WTYPE = unsigned int */
+TEST_STORE_NARROW_SHIFT(unsigned char,  unsigned int, store_uc_ui_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned int, store_us_ui_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char,    unsigned int, ret_uc_ui_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short,   unsigned int, ret_us_ui_narrow)
+
+/* WTYPE = unsigned long */
+TEST_STORE_NARROW_SHIFT(unsigned char,  unsigned long, store_uc_ul_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned long, store_us_ul_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned int,   unsigned long, store_ui_ul_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char,    unsigned long, ret_uc_ul_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short,   unsigned long, ret_us_ul_narrow)
+TEST_RET_NARROW_SHIFT(unsigned int,     unsigned long, ret_ui_ul_narrow)
+
+/* WTYPE = unsigned long long */
+TEST_STORE_NARROW_SHIFT(unsigned char,  unsigned long long, 
store_uc_ull_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned long long, 
store_us_ull_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned int,   unsigned long long, 
store_ui_ull_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char,    unsigned long long, ret_uc_ull_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short,   unsigned long long, ret_us_ull_narrow)
+TEST_RET_NARROW_SHIFT(unsigned int,     unsigned long long, ret_ui_ull_narrow)
+
+/* { dg-final { scan-assembler-times "\\tbset\\t" 16 { target rv64 } } } */
+/* { dg-final { scan-assembler-not "\\tsllw\\t" { target rv64 } } } */
+/* { dg-final { scan-assembler-not "\\tbinv\\t" { target rv64 } } } */
+/* { dg-final { scan-assembler-not "\\txor\\t" { target rv64 } } } */
+/* { dg-final { scan-assembler-not "\\tand\\t" { target rv64 } } } */
+
+/* { dg-final { scan-assembler-times "\\tbset\\t" 16 { target rv32 } } } */
+/* Testcases with ULL. */
+/* { dg-final { scan-assembler-times "\\tand\\t" 7 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "\\tbclr\\t" 5 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "\\tsrai\\t" 6 { target rv32 } } } */
+

Reply via email to