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 } } } */ +
