Andrew, Raphael and I have all poked at it in various ways over the last year
or so. I think when Raphael and I first looked at it I sent us down a bit of
rathole.
In particular it's odd that we're using a multiply to implement a select and it
seemed like recognizing the idiom and rewriting into a conditional move was the
right path. That looked reasonably good for the test, but runs into problems
with min/max detection elsewhere.
I think that initial investigation somewhat polluted our thinking. The
regression can be fixed with a fairly simple match.pd pattern.
Essentially we want to handle
x * (x || b) -> x
x * !(x || b) -> 0
There's simplifications that can be made for "&&" cases, but I haven't seen
them in practice. Rather than drop in untested patterns, I'm leaving that as a
future todo.
My original was two match.pd patterns. Andrew combined them into a single
pattern. I've made this conditional on GIMPLE as an earlier version that
simplified to a conditional move showed that when applied on GENERIC we could
drop an operand with a side effect which is clearly not good.
I've bootstrapped and regression tested this on x86. I've also tested on the
various embedded targets in my tester.
PR tree-optimization/114277
gcc/
* match.pd (a * (a || b) -> a): New pattern.
(a * !(a || b) -> 0): Likewise.
gcc/testsuite
* gcc.target/i386/pr114277.c: New test.
* gcc.target/riscv/pr114277.c: Likewise.
Co-author: Andrew Pinski <quic_apinski@quicinc.com>
(mult @0 integer_zerop@1)
@1)
+#if GIMPLE
+/* When multiplying a value by a boolean involving the value, we may
+ be able to simplify further.
+ a * ((a || b) != 0) -> a
+ a * ((a || b) == 0) -> 0
+
+ There are also bit-and cases which don't show up in practice yet.
+ a * ((a && b) != 0) -> a * b
+ a * ((a && b) == 0) -> b != 0 ? a : b */
+(for neeq (ne eq)
+ (simplify
+ (mult:c (convert? (neeq (bit_ior:c @0 @1) integer_zerop@2)) @0)
+ (if (neeq == EQ_EXPR)
+ { build_zero_cst (type); }
+ @0)))
+#endif
+
/* -x == x -> x == 0 */
(for cmp (eq ne)
(simplify
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int a,b;
+void func0(int x) { a=x * (x || b); }
+void func1(int x) { a=x * !(x || b); }
+
+/* { dg-final { scan-assembler-not "or" } } */
+/* { dg-final { scan-assembler-not "cmove" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc_zicond -mabi=lp64d" { target rv64 } } */
+/* { dg-options "-O2 -march=rv32gc_zicond -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-O3" "-Og" } } */
+
+#include "../i386/pr114277.c"
+
+/* { dg-final { scan-assembler-not "czero" } } */
+