Bug 95731 - Failure to optimize a >= 0 && b >= 0 to (a | b) >= 0
Summary: Failure to optimize a >= 0 && b >= 0 to (a | b) >= 0
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 11.0
: P3 normal
Target Milestone: 11.0
Assignee: Jakub Jelinek
URL:
Keywords: easyhack, missed-optimization
: 83123 (view as bug list)
Depends on:
Blocks: 19987
  Show dependency treegraph
 
Reported: 2020-06-17 21:58 UTC by Gabriel Ravier
Modified: 2024-01-04 06:00 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-08-04 00:00:00


Attachments
gcc11-pr95731.patch (2.52 KB, patch)
2021-01-11 14:44 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Gabriel Ravier 2020-06-17 21:58:23 UTC
bool f(int a, int b)
{
    return a >= 0 && b >= 0;
}

This can be optimized to `return (a | b) >= 0;`. LLVM does this transformation, but GCC does not.
Comment 1 Richard Biener 2020-06-18 08:41:19 UTC
Confirmed.  Two pieces depending on SHORT_CIRCUIT_... maybe_fold_and_comparisons for ifcombine and a match.pd pattern for

  _1 = a_3(D) >= 0;
  _2 = b_4(D) >= 0;
  _5 = _1 & _2;

that should also make it work through maybe_fold_and_comparisons.  We have
something like this in fold-const.c btw.
Comment 2 Jakub Jelinek 2020-06-18 09:19:14 UTC
Or reassoc could do it across different BBs.
Comment 3 Wilco 2020-08-04 11:21:25 UTC
(In reply to Gabriel Ravier from comment #0)
> bool f(int a, int b)
> {
>     return a >= 0 && b >= 0;
> }
> 
> This can be optimized to `return (a | b) >= 0;`. LLVM does this
> transformation, but GCC does not.

For orthogonality you also want:

a < 0 && b < 0   -> (a & b) < 0
a >= 0 || b >= 0 -> (a & b) >= 0
a < 0 || b < 0   -> (a | b) < 0
Comment 4 Jakub Jelinek 2020-08-04 11:30:57 UTC
I think doing it only in the last reassoc would have the advantage that it wouldn't break other optimizations done by reassoc.
E.g.
if (a >= 0 && b >= 0 && a < 32 && b < 128)
which can be now optimized into a < 32U && b < 128U couldn't be optimized unless we teach the reassoc code that (a | b) >= 0 is equivalent to a >= 0 && b >= 0.
The user can write it that way though:
void bar (int, int);

void
foo (int a, int b)
{
  if (a >= 0 && b >= 0 && a < 32 && b < 128)
    bar (a, b);
}

void
baz (int a, int b)
{
  if ((a | b) >= 0 && a < 32 && b < 128)
    bar (a, b);
}
Comment 5 Jakub Jelinek 2021-01-11 14:44:23 UTC
Created attachment 49938 [details]
gcc11-pr95731.patch

Untested fix.
Comment 6 GCC Commits 2021-01-12 10:05:54 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:13d47c37a2c043f3e5981e73e4c82158a39f41e8

commit r11-6609-g13d47c37a2c043f3e5981e73e4c82158a39f41e8
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Tue Jan 12 11:03:40 2021 +0100

    reassoc: Optimize in reassoc x < 0 && y < 0 to (x | y) < 0 etc. [PR95731]
    
    We already had x != 0 && y != 0 to (x | y) != 0 and
    x != -1 && y != -1 to (x & y) != -1 and
    x < 32U && y < 32U to (x | y) < 32U, this patch adds signed
    x < 0 && y < 0 to (x | y) < 0.  In that case, the low/high seem to be
    always the same and just in_p indices whether it is >= 0 or < 0,
    also, all types in the same bucket (same precision) should be type
    compatible, but we can have some >= 0 and some < 0 comparison mixed,
    so the patch handles that by using the right BIT_IOR_EXPR or BIT_AND_EXPR
    and doing one set of < 0 or >= 0 first, then BIT_NOT_EXPR and then the other
    one.  I had to move optimize_range_tests_var_bound before this optimization
    because that one deals with signed a >= 0 && a < b, and limited it to the
    last reassoc pass as reassoc itself can't virtually undo this optimization
    yet (and not sure if vrp would be able to).
    
    2021-01-12  Jakub Jelinek  <jakub@redhat.com>
    
            PR tree-optimization/95731
            * tree-ssa-reassoc.c (optimize_range_tests_cmp_bitwise): Also optimize
            x < 0 && y < 0 && z < 0 into (x | y | z) < 0 for signed x, y, z.
            (optimize_range_tests): Call optimize_range_tests_cmp_bitwise
            only after optimize_range_tests_var_bound.
    
            * gcc.dg/tree-ssa/pr95731.c: New test.
            * gcc.c-torture/execute/pr95731.c: New test.
Comment 7 Jakub Jelinek 2021-01-16 08:42:29 UTC
Fixed.
Comment 8 Andrew Pinski 2021-08-14 21:28:12 UTC
*** Bug 83123 has been marked as a duplicate of this bug. ***