On 9/21/2026 10:45 PM, Andrea Pinski wrote:
On Mon, Sep 21, 2026 at 4:45 AM Daniel Barboza <[email protected]> wrote:RTL is able to do this optimization but doing it earlier on gimple can promote further optimizations during tree-opt. Boostrapped and regression tested in x86_64 and aarch64. PR tree-optimization/126523 gcc/ChangeLog: * match.pd(`(SIGNED (bit_not (UNSIGNED signed_var)))`): New pattern. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr126523.c: New test. --- gcc/match.pd | 14 +++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/pr126523.c | 26 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126523.c diff --git a/gcc/match.pd b/gcc/match.pd index e59c11d5d13..2b3713e7360 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -13279,6 +13279,20 @@ and, && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0))) (convert @0))) +/* PR126523: (SIGNED (~(UNSIGNED signed_var))) -> ~signed_var. */ +(simplify + (convert (bit_not (convert@1 @0))) + (if (INTEGRAL_TYPE_P (type) + && !TYPE_UNSIGNED (type) + && TREE_TYPE (@0) == type + && TYPE_UNSIGNED (TREE_TYPE (@1)) + /* Check single_use since we're getting rid of @1. */ + && single_use (@1) + /* The unsigned convert cannot have less bits + than the signed result. */ + && TYPE_PRECISION (TREE_TYPE (@1)) >= TYPE_PRECISION (type)) + (bit_not @0)))Hmm, a few things here. `TREE_TYPE (@0) == type` should be `types_match (@0, type)` (this will allow for more types matching in gimple). We want to remove the converts if the inner and outer precisions are the same irrespective of the signedness. I think if the inner convert is a nop_convert, and the outer one is a truncation, then we should emit `(convert (bit_not @0))`; I think you want a single_use on the bit_not instead of the convert (nop_converts are "free") and not need to check the type of @0 here. For (trunc (bit_not (widdening @0))), it does not matter what the signedness of @0 if precision of @0 matches the precision of the type because the bits which either sign or zero extended (and then `not`ed) will be truncated away. So maybe types_match should really be tree_nop_conversion_p and keep around the convert to type around @0 (for gimple, that will be removed for the case of types_match). Even more we can truncate further than @0 here too; so this should be a TYPE_PRECISION check then.
I came up with a pattern that would cover more cases and managed to bootstrap it in x86. But then when I added more test coverage I realized that the patterns weren't working ... in x86. They do work in aarch64. Or so I thought. In reality this code generates different Gimple between x86 and aarch64, on trunk. Since my main dev machine is an aarch64 box I was misled into thinking that my pattern was doing something. Here's an example: https://godbolt.org/z/59rzjs1a6 The aarch64 dumps have one less type cast than x86 in all the functions. I played a bit to see which patterns aren't already optimized in aarch64 and x86 and, so far, the only pattern that can be optimized by both happens to be the one for the bug, i.e. same precision conversions. The pattern from this bug is covering signed -> unsigned -> signed only so I can send a v2 that covers unsigned -> signed -> unsigned. Aside from that I'm not sure how to proceed with the other patterns. Should we invest time in them given that some targets are already doing a bit of the work in Gimple? Should we optimize these 2 patterns that we have in hand and open a new bug to see how to optimize the rest? Thanks, Daniel
Thanks, Andrea+ /* Optimize c1 = VEC_PERM_EXPR (a, a, mask) c2 = VEC_PERM_EXPR (b, b, mask) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126523.c b/gcc/testsuite/gcc.dg/tree-ssa/pr126523.c new file mode 100644 index 00000000000..a8199c0b7ee --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126523.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fdump-tree-optimized" } */ + +typedef int si; +typedef unsigned ui; + +si f(si a) +{ + ui t = a; + t = ~t; + si a1 = t; + return a1; +} + +si f1(si a) +{ + return ~a; +} + +/* If everything went as planned g() + will reduce to 'return 1'. */ +int g(si a) +{ + return f(a) + f1(a) == (si)(2*(~a)); +} +/* { dg-final { scan-tree-dump-times " return 1;" 1 "optimized" } } */ -- 2.43.0
