> Some fixes:
> +/* (T)(1 << x) & (T)(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);
> + tree largertype = ltype0;
> + tree largertypeone = build_one_cst (largertype);
> Remove largertypeone.
> + }
> + (if (INTEGRAL_TYPE_P (type)
> + && INTEGRAL_TYPE_P (ltype0)
> + && INTEGRAL_TYPE_P (ltype1)
> + && element_precision (type) <= element_precision (ltype0)
> + && element_precision (type) <= element_precision (ltype1))
> So the check should be:
> && element_precision (type) <= element_precision (ltype1)
> && element_precision (ltype1) <= element_precision (ltype0))
> Such that `type <= itype1 <= itype0`
> + (convert:type
> + (lshift:largertype { largertypeone; } @0)))))
> Replace `{ largertypeone; }` with `@1`.
> Even though INTEGER_CSTs are shared and small integers are kept
> around; this makes the code slightly faster as you don't need to look
> up the constant.
Thanks, the updated pattern includes all of this.
> if you want a future expansion on this, a nice change would remove the
> `:c` and add a check for the other way around; that is `type <=
> itype0 <= itype1` and using itype1 for the inner type.
I removed the `:c` and arrived at the pattern in the updated patch.
First I added a new test which swaps the placement of the WTYPE
cast. The pattern catches these cases as well somewhat.
Additionally, the tests try to cast to int are added now as well.
I also tried combinations in which I swap the shifts, ands and ors around
in the test case, but this produced the same GIMPLE in the optimized pass.
That is why `:c` is dropped entirely.
2026-10-07 Dusan Stojkovic <[email protected]>
PR target/123883
gcc/ChangeLog:
* match.pd: New pattern.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/pr123883-a.c: New test.
* gcc.target/riscv/pr123883-b.c: New test.
Co-authored-by: Jeff Law <[email protected]>
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 | 22 ++++++++
gcc/testsuite/gcc.target/riscv/pr123883-a.c | 59 +++++++++++++++++++++
gcc/testsuite/gcc.target/riscv/pr123883-b.c | 58 ++++++++++++++++++++
3 files changed, 139 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/pr123883-a.c
create mode 100644 gcc/testsuite/gcc.target/riscv/pr123883-b.c
diff --git a/gcc/match.pd b/gcc/match.pd
index a7cec25dbad..0de4addf44a 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -12335,6 +12335,28 @@ 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
+ (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);
+ tree largertype = ltype0;
+ }
+ (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:largertype { @1; } @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 00000000000..c24468dc922
--- /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 "bset" 14 { target rv64 } } } */
+/* Current remaining missed SI-mode cases. */
+/* { dg-final { scan-assembler-times "sllw" 4 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "binv" 2 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "sext.w" 4 { target rv64 } } } */
+
+
+/* { dg-final { scan-assembler-times "bset" 16 { target rv32 } } } */
+/* Testcases with ULL. */
+/* { dg-final { scan-assembler-times "xor" 2 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "and\\t" 2 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "srai" 1 { 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 00000000000..37681286b38
--- /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 "bset" 16 { target rv64 } } } */
+/* { dg-final { scan-assembler-not "sllw" { target rv64 } } } */
+/* { dg-final { scan-assembler-not "binv" { target rv64 } } } */
+/* { dg-final { scan-assembler-not "xor" { target rv64 } } } */
+/* { dg-final { scan-assembler-not "and\\t" { target rv64 } } } */
+
+/* { dg-final { scan-assembler-times "bset" 18 { target rv32 } } } */
+/* Testcases with ULL. */
+/* { dg-final { scan-assembler-times "binv" 2 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "blt" 2 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "srai" 4 { target rv32 } } } */
+
--
2.43.0