https://gcc.gnu.org/g:bc19036af435b239c0118e9b83be1d4f58aaea2b
commit r17-577-gbc19036af435b239c0118e9b83be1d4f58aaea2b Author: Jeff Law <[email protected]> Date: Mon May 18 15:17:27 2026 -0600 [RISC-V] Improve ext-dce's live bit tracking for IOR/AND with a constant argument Investigation of a regression with some RISC-V target changes exposed a clear missed optimization in ext-dce.c In particular if we mask off bits via a logical AND, then the masked off bits are not live-in for the other input. Tracking that can in turn allow us to eliminate more extensions. There's a similar case for logical IOR when it unconditionally turns bits on. So if we look at this testcase: typedef long unsigned int size_t; struct function { unsigned int curr_properties; unsigned int last_verified; }; extern struct function *cfun; void execute_function_todo (void *data) { unsigned int flags = (size_t) data; if (flags & (1 << 5)) flags |= (1 << 15); (cfun + 0)->last_verified = flags & ((1 << 2) | (1 << 3) | (1 << 4)); } It currently generates this code for rv64gcbv: andi a5,a0,32 sext.w a0,a0 beq a5,zero,.L2 bseti a0,a0,15 .L2: lui a5,%hi(cfun) ld a5,%lo(cfun)(a5) andi a0,a0,28 sw a0,4(a5) ret Note carefully the 2nd andi instruction. That's unconditionally turning off bits 32..63 (and others). Thus those bits are not relevant/live for the incoming value in a0. Walking backwards we find the sext.w which sign extends from bit 31 into bits 32..63. But with bits 32..63 not being live, the sext.w is useless. After this patch we get: andi a5,a0,32 beq a5,zero,.L2 bseti a0,a0,15 .L2: lui a5,%hi(cfun) ld a5,%lo(cfun)(a5) andi a0,a0,28 sw a0,4(a5) ret It doesn't trigger often based on my quick testing. Bootstrapped and regression tested on various targets including x86 and riscv. Also tested on the usual assortment of embedded targets in my tester. I'll wait for pre-commit CI to give a final verdict. gcc/ * ext-dce.cc (carry_backpropagate): Handle AND and IOR with a constant argument. gcc/testsuite/ * gcc.target/riscv/ext-dce-2.c: New test. Diff: --- gcc/ext-dce.cc | 13 +++++++++++++ gcc/testsuite/gcc.target/riscv/ext-dce-2.c | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/gcc/ext-dce.cc b/gcc/ext-dce.cc index d75f7e48574c..9475b18cd344 100644 --- a/gcc/ext-dce.cc +++ b/gcc/ext-dce.cc @@ -966,6 +966,19 @@ carry_backpropagate (unsigned HOST_WIDE_INT mask, enum rtx_code code, rtx x) /* Recurse into the operand. */ return carry_backpropagate (mask, GET_CODE (XEXP (x, 0)), XEXP (x, 0)); + /* If an AND/IOR unconditionally clears/sets bits in the output, then + we do not care about the liveness of those bits in the inputs. + + So AND MASK with the constant for AND and with the complemented constant + for IOR. */ + case AND: + case IOR: + if (CONST_INT_P (XEXP (x, 1))) + mask &= (code == AND + ? UINTVAL (XEXP (x, 1)) + : ~UINTVAL (XEXP (x, 1))); + return mask; + /* We propagate for the shifted operand, but not the shift count. The count is handled specially. */ case SS_ASHIFT: diff --git a/gcc/testsuite/gcc.target/riscv/ext-dce-2.c b/gcc/testsuite/gcc.target/riscv/ext-dce-2.c new file mode 100644 index 000000000000..c73e45f8d301 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/ext-dce-2.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target rv64 } } */ +/* { dg-options "-O2 -fdump-rtl-ext_dce" } */ +typedef long unsigned int size_t; +struct function +{ + unsigned int curr_properties; + unsigned int last_verified; +}; +extern struct function *cfun; + +void +execute_function_todo (void *data) +{ + unsigned int flags = (size_t) data; + if (flags & (1 << 5)) + flags |= (1 << 15); + (cfun + 0)->last_verified = flags & ((1 << 2) | (1 << 3) | (1 << 4)); +} + +/* { dg-final { scan-rtl-dump "Successfully transformed to:" "ext_dce" } } */ +
