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.
/* 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:
--- /dev/null
+/* { 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" } } */
+