https://gcc.gnu.org/g:97ea68a802c9b271f208d77df7f1d7ce948737d0

commit 97ea68a802c9b271f208d77df7f1d7ce948737d0
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} } } */

Reply via email to