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)