https://gcc.gnu.org/g:f34fabb8dc3da7d0eca7beec28db2111196dabc6
commit f34fabb8dc3da7d0eca7beec28db2111196dabc6 Author: Shreya Munnangi <[email protected]> Date: Sat Nov 1 16:48:54 2025 -0600 [RISC-V][SH][PR rtl-optimization/67731] Improve logical IOR of single bit bitfields This is Shreya's work except for the SH testcase which I added after realizing her work would also fix the testcases for that port. I bootstrapped and regression tested this on sh4-linux-gnu, x86_64 & risc-v. It also was tested across all the embedded targets in my tester without regressions. -- We are extracting two single-bit bitfields from a structure and determining whether they both have the value 0 or if at least one bit is set. This has been generating poor code: > lw a5,0(a0) > bexti a0,a5,1 > bexti a5,a5,2 > or a0,a0,a5 > ret We address this as a simplification problem and optimize this using an andi of the original value and a mask with just the desired bits set, followed by a snez. This results in a 1 if any of those bits are set or 0 if none. For cases where we want to extract three or more single-bit bitfields, we build on the previous case. We take the result of the 2-bitfield case, extract the mask, update it to include the new single-bit bitfield, and again perform an andi + snez. In our new testfile, we scan to ensure we do not see a bexti or an or instruction, and that we have the correct assembly for both two and three single-bit bitfield cases: lw + andi + snez + ret. PR target/67731 gcc/ * simplify-rtx.cc (simplify_context::simplify_binary_operation_1): Handle IOR of single bit bitfields from the same object. gcc/testsuite/ * gcc.target/riscv/pr67731.c: New test. * gcc.target/sh/pr67731.c: New test. (cherry picked from commit fe0fa2bb61bd2b19acd4f14350123fd6c6c0cb4d) Diff: --- gcc/simplify-rtx.cc | 57 ++++++++++++++++++++++++++++++++ gcc/testsuite/gcc.target/riscv/pr67731.c | 25 ++++++++++++++ gcc/testsuite/gcc.target/sh/pr67731.c | 25 ++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc index 166e44b30fc9..d82e4be727dd 100644 --- a/gcc/simplify-rtx.cc +++ b/gcc/simplify-rtx.cc @@ -3620,6 +3620,63 @@ simplify_context::simplify_binary_operation_1 (rtx_code code, && GET_MODE_CLASS (mode) != MODE_CC) return CONSTM1_RTX (mode); + /* IOR of two single bit bitfields extracted from the same object. + Bitfields are represented as an AND based extraction */ + if (GET_CODE (op0) == AND + && GET_CODE (op1) == AND + /* Verify both AND operands are logical right shifts. */ + && GET_CODE (XEXP (op0, 0)) == LSHIFTRT + && GET_CODE (XEXP (op1, 0)) == LSHIFTRT + /* Verify both bitfields are extracted from the same object. */ + && XEXP (XEXP (op0, 0), 0) == XEXP (XEXP (op1, 0), 0) + /* Verify both fields are a single bit (could be generalized). */ + && XEXP (op0, 1) == CONST1_RTX (mode) + && XEXP (op1, 1) == CONST1_RTX (mode) + /* Verify bit positions (for cases with variable bit position). */ + && CONST_INT_P (XEXP (op0, 1)) + && CONST_INT_P (XEXP (op1, 1))) + { + unsigned HOST_WIDE_INT bitpos1 = INTVAL (XEXP (XEXP (op0, 0), 1)); + unsigned HOST_WIDE_INT bitpos2 = INTVAL (XEXP (XEXP (op1, 0), 1)); + unsigned HOST_WIDE_INT mask + = (HOST_WIDE_INT_1U << bitpos1) | (HOST_WIDE_INT_1U << bitpos2); + + rtx m = GEN_INT (mask); + rtx t = gen_rtx_AND (mode, XEXP (XEXP (op0, 0), 0), m); + t = gen_rtx_NE (mode, t, CONST0_RTX (mode)); + return t; + } + + /* IOR of multiple single bit bitfields extracted from the same object + (building on previous case). + First bitfield is represented as an AND based extraction, as done + above. Second represented as NE based extraction, from + output above. */ + if (GET_CODE (op0) == AND + && GET_CODE (op1) == NE + /* Verify AND operand is logical right shift. */ + && GET_CODE (XEXP (op0, 0)) == LSHIFTRT + /* Verify NE operand is an AND (based on output above). */ + && GET_CODE (XEXP (op1, 0)) == AND + /* Verify both bitfields are extracted from the same object. */ + && XEXP (XEXP (op0, 0), 0) == XEXP (XEXP (op1, 0), 0) + /* Verify masking is with a single bit and that we have a NE 0 + comparison for the other operand. */ + && XEXP (op0, 1) == CONST1_RTX (mode) + && XEXP (op1, 1) == CONST0_RTX (mode) + /* Verify bit position. */ + && CONST_INT_P (XEXP (op0, 1))) + { + unsigned HOST_WIDE_INT bitpos1 = INTVAL (XEXP (XEXP (op0, 0), 1)); + unsigned HOST_WIDE_INT mask + = (HOST_WIDE_INT_1U << bitpos1) | INTVAL (XEXP (XEXP (op1, 0), 1)); + + rtx m = GEN_INT (mask); + rtx t = gen_rtx_AND (mode, XEXP (XEXP (op0, 0), 0), m); + t = gen_rtx_NE (mode, t, CONST0_RTX (mode)); + return t; + } + /* Convert (ior (plus (A - 1)) (neg A)) to -1. */ if (match_plus_neg_pattern (op0, op1, mode)) return CONSTM1_RTX (mode); diff --git a/gcc/testsuite/gcc.target/riscv/pr67731.c b/gcc/testsuite/gcc.target/riscv/pr67731.c new file mode 100644 index 000000000000..6f254fc68f5f --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr67731.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=rv64gcbv -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-O2 -march=rv32gcbv -mabi=ilp32" { target { rv32 } } } */ + +typedef struct +{ + _Bool a : 1; + _Bool b : 1; + _Bool c : 1; + _Bool d : 1; + unsigned int e : 4; +} S; + +_Bool test_00 (S* s) +{ + return s->b | s->c; +} + +_Bool test_01 (S* s) +{ + return s->b | s->c | s->d; +} +/* { dg-final { scan-assembler-times {\tlw\ta0,0\(a0\).*?\n\tandi\ta0,a0,\d+.*?\n\tsnez\ta0,a0.*?\n\tret} 2 } } */ +/* { dg-final { scan-assembler-not {\tor} } } */ +/* { dg-final { scan-assembler-not {\tbexti} } } */ diff --git a/gcc/testsuite/gcc.target/sh/pr67731.c b/gcc/testsuite/gcc.target/sh/pr67731.c new file mode 100644 index 000000000000..43c16577fa58 --- /dev/null +++ b/gcc/testsuite/gcc.target/sh/pr67731.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -m4 -ml" } */ + +typedef struct +{ + _Bool a : 1; + _Bool b : 1; + _Bool c : 1; + _Bool d : 1; + unsigned int e : 4; +} S; + +_Bool test_00 (S* s) +{ + return s->b | s->c; +} + +_Bool test_01 (S* s) +{ + return s->b | s->c | s->d; +} + +/* { dg-final { scan-assembler-times {\ttst} 2 } } */ +/* { dg-final { scan-assembler-times {\tnegc} 2 } } */ +/* { dg-final { scan-assembler-not {\tor} } } */
