https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108440

--- Comment #8 from CVS Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jakub Jelinek <ja...@gcc.gnu.org>:

https://gcc.gnu.org/g:05b9868b182bb9ed2013b39a0bc6297354a0db49

commit r13-5254-g05b9868b182bb9ed2013b39a0bc6297354a0db49
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Jan 19 10:00:51 2023 +0100

    forwprop: Further fixes for simplify_rotate [PR108440]

    As mentioned in the simplify_rotate comment, for e.g.
       ((T) ((T2) X << (Y & (B - 1)))) | ((T) ((T2) X >> ((-Y) & (B - 1))))
    we already emit
       X r<< (Y & (B - 1))
    as replacement.  This PR is about the
       ((T) ((T2) X << Y)) OP ((T) ((T2) X >> (B - Y)))
       ((T) ((T2) X << (int) Y)) OP ((T) ((T2) X >> (int) (B - Y)))
    forms if T2 is wider than T.  Unlike e.g.
       (X << Y) OP (X >> (B - Y))
    which is valid just for Y in [1, B - 1], the above 2 forms are actually
    valid and do the rotates for Y in [0, B] - for Y 0 the X value is preserved
    by the left shift and right logical shift by B adds just zeros (but because
    the shift is in wider precision B is still valid shift count), while for
    Y equal to B X is preserved through the latter shift and the former adds
    just zeros.
    Now, it is unclear if we in the middle-end treat rotates with rotate count
    equal or larger than precision as UB or not, unlike shifts there are less
    reasons to do so, but e.g. expansion of X r<< Y if there is no rotate optab
    for the mode is emitted as (X << Y) | (((unsigned) X) >> ((-Y) & (B - 1)))
    and so with UB on Y == B.

    The following patch does multiple things:
    1) for the above 2, asks the ranger if Y could be equal to B and if so,
       instead of using X r<< Y uses X r<< (Y & (B - 1))
    2) for the
       ((T) ((T2) X << Y)) | ((T) ((T2) X >> ((-Y) & (B - 1))))
       ((T) ((T2) X << (int) Y)) | ((T) ((T2) X >> (int) ((-Y) & (B - 1))))
       forms that were fixed 2 days ago it only punts if Y might be in the
       [B,B2-1] range but isn't known to be in the
       [0,B][2*B,2*B][3*B,3*B]... range.  Because for Y which is a multiple
       of B but smaller than B2 it acts as a rotate too, left shift provides
       0 and (-Y) & (B - 1) is 0 and so preserves X.  Though, for the cases
       where Y is not known to be in [0,B-1] the patch also uses
       X r<< (Y & (B - 1)) rather than X r<< Y
    3) as discussed with Aldy, instead of using global ranger it uses a pass
       specific copy but lazily created on first simplify_rotate that needs it;
       this e.g. handles rotate inside of if body where the guarding condition
       limits the shift count to some range which will not work with the
       global ranger (unless there is some SSA_NAME to attach the range to).

    Note, e.g. on x86 X r<< (Y & (B - 1)) and X r<< Y actually emit the
    same assembly because rotates work the same even for larger rotate counts,
    but that is handled only during combine.

    2023-01-19  Jakub Jelinek  <ja...@redhat.com>

            PR tree-optimization/108440
            * tree-ssa-forwprop.cc: Include gimple-range.h.
            (simplify_rotate): For the forms with T2 wider than T and shift
counts of
            Y and B - Y add & (B - 1) masking for the rotate count if Y could
be equal
            to B.  For the forms with T2 wider than T and shift counts of
            Y and (-Y) & (B - 1), don't punt if range could be [B, B2], but
only if
            range doesn't guarantee Y < B or Y = N * B.  If range doesn't
guarantee
            Y < B, also add & (B - 1) masking for the rotate count.  Use lazily
created
            pass specific ranger instead of get_global_range_query.
            (pass_forwprop::execute): Disable that ranger at the end of pass if
it has
            been created.

            * c-c++-common/rotate-10.c: New test.
            * c-c++-common/rotate-11.c: New test.

Reply via email to