On Fri, 1 Dec 2023, Jakub Jelinek wrote:

> Hi!
> 
> When debugging PR112750, I've noticed some issues in the computation
> of precisions and the following patch attempts to fix those.
> 
> The pass uses range_to_prec function, which possibly using ranger returns
> minimum precision of some operand in the style that libgcc _BitInt
> entrypoints expect, i.e. for operands with unsigned types either the
> precision of that type or with help of ranger
> wi::min_precision (upper_bound, UNSIGNED) (done both if the types
> are really unsigned or even when lower_bound is non-negative), while
> for operands with signed types either negated precision of that type or
> with help of ranger negated value of maximum of SIGNED min_precisions
> of lower and upper bound.
> Because _BitInt in C only supports unsigned precisions >= 1 and signed
> precisions >= 2, the function also ensures that 0 is never returned (returns
> 1 instead) and should ensure that -1 is never returned (should return -2).
> That is the first bug I found though, for the ranger case it ensured that,
> but if an operand would be signed 1-bit precision (that means
> non-BITINT_TYPE) operand, it could return -1.
> 
> Another thing is that both lower_addsub_overflow and lower_mul_overflow
> compute from the prec0 and prec1 of the operands (returned by range_to_prec
> with the above value meanings) prec2, which is how many bits of the result
> we actually need to compute to know the infinite precision result.
> This is then used by arith_overflow function together with prec
> (TYPE_PRECISION (type)), type (type of the result), prec0 and prec1 to
> compute which range of bits should be tested (if any, or that there is never
> an overflow) and with which kind (require those bits to be zero vs.
> check if all those bits together all all zeros/ones).
> The arith_overflow function has one special case, when
> prec0 >= 0 && prec1 >= 0 and operation is not .SUB_OVERFLOW; in that case
> we treat prec2 as minimum precision to express any infinite precision
> unsigned result (the result is never negative in that case), while
> in all other cases prec2 is treated as minimum precision to express
> any infinite precision signed result (because the result can be also
> negative).
> The computations of those values were apparently incorrect for all of
> .{ADD,SUB}_OVERFLOW (in that case only if one operand was signed and
> the other unsigned) and for .MUL_OVERFLOW it was sometimes too large.
> 
> It took me a while to get to the right expression how to compute that,
> I've started with writing into the comment the possible results for
> different prec0 and prec1 values (used just 8/-8/10/-10 as placeholders
> for equal or different absolute values of the 2 precisions and cases
> with positive and/or negative signs) and then turned into the attached
> test program that actually printed what I was writing to make sure
> I didn't make mistakes in it and in the end also verified the computation,
> this time for all combinations of 1..14 and -2..-14 precisions.
> The UNSIGNED vs. SIGNED in the table is what arith_overflow expects
> the prec2 to be (see above mentioned exception).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

Richard.

> 2023-12-01  Jakub Jelinek  <ja...@redhat.com>
> 
>       * gimple-lower-bitint.cc (range_to_prec): Don't return -1 for
>       signed types.
>       (bitint_large_huge::lower_addsub_overflow): Fix up computation of
>       prec2.
>       (bitint_large_huge::lower_mul_overflow): Likewise.
> 
> --- gcc/gimple-lower-bitint.cc.jj     2023-11-30 12:46:34.715093396 +0100
> +++ gcc/gimple-lower-bitint.cc        2023-11-30 17:24:59.828026693 +0100
> @@ -1963,7 +1963,7 @@ range_to_prec (tree op, gimple *stmt)
>        if (TYPE_UNSIGNED (type))
>       return prec;
>        else
> -     return -prec;
> +     return MIN ((int) -prec, -2);
>      }
>  
>    if (!TYPE_UNSIGNED (TREE_TYPE (op)))
> @@ -3792,11 +3792,45 @@ bitint_large_huge::lower_addsub_overflow
>    int prec = TYPE_PRECISION (type);
>    int prec0 = range_to_prec (arg0, stmt);
>    int prec1 = range_to_prec (arg1, stmt);
> -  int prec2 = ((prec0 < 0) == (prec1 < 0)
> -            ? MAX (prec0 < 0 ? -prec0 : prec0,
> -                   prec1 < 0 ? -prec1 : prec1) + 1
> -            : MAX (prec0 < 0 ? -prec0 : prec0 + 1,
> -                   prec1 < 0 ? -prec1 : prec1 + 1) + 1);
> +  /* If PREC0 >= 0 && PREC1 >= 0 and CODE is not MINUS_EXPR, PREC2 is
> +     the be minimum unsigned precision of any possible operation's
> +     result, otherwise it is minimum signed precision.
> +     Some examples:
> +     If PREC0 or PREC1 is 8, it means that argument is [0, 0xff],
> +     if PREC0 or PREC1 is 10, it means that argument is [0, 0x3ff],
> +     if PREC0 or PREC1 is -8, it means that argument is [-0x80, 0x7f],
> +     if PREC0 or PREC1 is -10, it means that argument is [-0x200, 0x1ff].
> +     PREC0  CODE  PREC1  RESULT          PREC2  SIGNED vs. UNSIGNED
> +       8      +     8    [0, 0x1fe]        9    UNSIGNED
> +       8      +    10    [0, 0x4fe]       11    UNSIGNED
> +      -8      +    -8    [-0x100, 0xfe]    9    SIGNED
> +      -8      +   -10    [-0x280, 0x27e]  11    SIGNED
> +       8      +    -8    [-0x80, 0x17e]   10    SIGNED
> +       8      +   -10    [-0x200, 0x2fe]  11    SIGNED
> +      10      +    -8    [-0x80, 0x47e]   12    SIGNED
> +       8      -     8    [-0xff, 0xff]     9    SIGNED
> +       8      -    10    [-0x3ff, 0xff]   11    SIGNED
> +      10      -     8    [-0xff, 0x3ff]   11    SIGNED
> +      -8      -    -8    [-0xff, 0xff]     9    SIGNED
> +      -8      -   -10    [-0x27f, 0x27f]  11    SIGNED
> +     -10      -    -8    [-0x27f, 0x27f]  11    SIGNED
> +       8      -    -8    [-0x7f, 0x17f]   10    SIGNED
> +       8      -   -10    [-0x1ff, 0x2ff]  11    SIGNED
> +      10      -    -8    [-0x7f, 0x47f]   12    SIGNED
> +      -8      -     8    [-0x17f, 0x7f]   10    SIGNED
> +      -8      -    10    [-0x47f, 0x7f]   12    SIGNED
> +     -10      -     8    [-0x2ff, 0x1ff]  11    SIGNED  */
> +  int prec2 = MAX (prec0 < 0 ? -prec0 : prec0,
> +                prec1 < 0 ? -prec1 : prec1);
> +         /* If operands are either both signed or both unsigned,
> +            we need just one additional bit.  */
> +  prec2 = (((prec0 < 0) == (prec1 < 0)
> +            /* If one operand is signed and one unsigned and
> +               the signed one has larger precision, we need
> +               just one extra bit, otherwise two.  */
> +         || (prec0 < 0 ? (prec2 == -prec0 && prec2 != prec1)
> +                       : (prec2 == -prec1 && prec2 != prec0)))
> +        ? prec2 + 1 : prec2 + 2);
>    int prec3 = MAX (prec0 < 0 ? -prec0 : prec0,
>                  prec1 < 0 ? -prec1 : prec1);
>    prec3 = MAX (prec3, prec);
> @@ -4201,8 +4235,9 @@ bitint_large_huge::lower_mul_overflow (t
>    arg0 = handle_operand_addr (arg0, stmt, NULL, &prec0);
>    arg1 = handle_operand_addr (arg1, stmt, NULL, &prec1);
>    int prec2 = ((prec0 < 0 ? -prec0 : prec0)
> -            + (prec1 < 0 ? -prec1 : prec1)
> -            + ((prec0 < 0) != (prec1 < 0)));
> +            + (prec1 < 0 ? -prec1 : prec1));
> +  if (prec0 == 1 || prec1 == 1)
> +    --prec2;
>    tree var = NULL_TREE;
>    tree orig_obj = obj;
>    bool force_var = false;
> 
>       Jakub
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to