On Fri, 25 Sep 2026, Jakub Jelinek wrote:

> Hi!
> 
> Most of expand_mul_overflow just assumes mode precision arguments
> of the same mode and perhaps different signedness and in the end only
> expand_arith_overflow_result_store and expand_ubsan_result_store
> deal with the result mode being smaller than the operand mode or
> having non-mode precision and performing additional testing for those
> cases.  The callers of expand_mul_overflow for the non-ubsan cases
> (and bitint lowering) ensure argument promotions so that both
> expand_mul_overflow operands have the same precision.
> They can have non-mode precision though.
> In r14-3745 when adding _BitInt support I've already dealt with this
> and added
>   /* Unsigned types with smaller than mode precision, even if they have most
>      significant bit set, are still zero-extended.  */
>   if (uns0_p && TYPE_PRECISION (TREE_TYPE (arg0)) < GET_MODE_PRECISION (mode))
>     pos_neg0 = 1;
>   if (uns1_p && TYPE_PRECISION (TREE_TYPE (arg1)) < GET_MODE_PRECISION (mode))
>     pos_neg1 = 1;
> after the 2 get_range_pos_neg calls.  Unfortunately the following testcase
> shows that is not good enough.
> In the testcase, one of the arguments is originally
> 0x7fffffffffffffffffffffffffffffffwb in _BitInt(128) type (so its
> signed maximum), but during gimple opts that is optimized into
> unsigned _BitInt(127) 0x7fffffffffffffffffffffffffffffffuwb (unsigned
> 127-bit maximum, same value, that is all __builtin_mul_overflow cares
> about, it extends everything virtually to infinite precision).
> Now, get_range_pos_neg (arg1, currently_expanding_gimple_stmt);
> for the unsigned _BitInt(127) maximum is 2, it always treats it as signed
> and says whether the most significant bit is known to be 0 or 1 or unknown,
> 2 means it is known to be set.  The above mentioned adjustment adjusts
> it to 1, which is correct, when used in the mode precision (128-bit),
> the most significant bit is actually clear, because uns1_p.
> So, we are then handling a s x u -> s overflow multiplication, and
> when we know the most significant bit of the second operand is 0 (pos_neg1
> is 1), we don't do any adjustments and just fallthrough into the
> s x s -> s overflow multiplication.  So far so good.
> We don't have an optab and do the expansion in half mode (DImode), where
> we then call
>           if (arg1 != error_mark_node)
>             mprec1 = get_min_precision (arg1, sign);
> but arg1 still has 127-bit precision rather than 128-bit and sign is SIGNED,
> so this sets mprec1 to 1 rather than 127 that we need (when looking at the
> 127-bit value as signed, it is all sign bit copies, _BitInt(127) -1).
> 
> The following patch fixes this by reverting the r14-3745 changes and
> instead just promoting arg0 and arg1 to corresponding type with mode
> precision and their original signedness.  Both get_range_pos_neg
> and get_min_precision can deal with several NOP_EXPRs/CONVERT_EXPRs around
> an SSA_NAME and we already rely on that.
> 
> The only thing where we actually need the original arg0/arg1 before
> promotion (for .MUL_OVERFLOW the passed in arg0/arg1 are already often
> after promotion anyway) is in the ubsan case to know what kind of types
> of the arguments to print, so the patch adds orig_arg{0,1} variables
> and passes that, otherwise the patch without that breaks
> ubsan/bitint-{1,2}.c.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

Thanks,
Richard.

> 2026-09-25  Jakub Jelinek  <[email protected]>
> 
>       PR middle-end/127517
>       * internal-fn.cc (expand_mul_overflow): Revert the 2023-09-06
>       changes.  Instead promote arg0 and/or arg1 to a type with
>       uns{0,1}_p signedness and mode precision if they have non-mode
>       precision.  Pass the original arg0/arg1 and their type to
>       ubsan_build_overflow_builtin, instead of passing the promoted
>       ones.
> 
>       * gcc.dg/torture/bitint-109.c: New test.
> 
> --- a/gcc/internal-fn.cc      2026-09-16 22:17:08.000000000 +0200
> +++ b/gcc/internal-fn.cc      2026-09-24 11:55:24.348970092 +0200
> @@ -1750,7 +1750,7 @@ expand_mul_overflow (location_t loc, tre
>                    tree *datap)
>  {
>    rtx res, op0, op1;
> -  tree fn, type;
> +  tree fn, type, orig_arg0 = arg0, orig_arg1 = arg1;
>    rtx_code_label *done_label, *do_error;
>    rtx target = NULL_RTX;
>    signop sign;
> @@ -1764,10 +1764,26 @@ expand_mul_overflow (location_t loc, tre
>    do_error = gen_label_rtx ();
>  
>    do_pending_stack_adjust ();
> +
> +  scalar_int_mode mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg0));
> +  /* If the operand types don't have mode precision, extend them
> +     to mode precision.  */
> +  if (TYPE_PRECISION (TREE_TYPE (arg0)) < GET_MODE_PRECISION (mode))
> +    {
> +      tree type = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
> +                                               uns0_p);
> +      arg0 = fold_convert_loc (loc, type, arg0);
> +    }
> +  if (TYPE_PRECISION (TREE_TYPE (arg1)) < GET_MODE_PRECISION (mode))
> +    {
> +      tree type = build_nonstandard_integer_type (GET_MODE_PRECISION (mode),
> +                                               uns1_p);
> +      arg1 = fold_convert_loc (loc, type, arg1);
> +    }
> +
>    op0 = expand_normal (arg0);
>    op1 = expand_normal (arg1);
>  
> -  scalar_int_mode mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg0));
>    bool uns = unsr_p;
>    if (lhs)
>      {
> @@ -1829,12 +1845,6 @@ expand_mul_overflow (location_t loc, tre
>  
>    int pos_neg0 = get_range_pos_neg (arg0, currently_expanding_gimple_stmt);
>    int pos_neg1 = get_range_pos_neg (arg1, currently_expanding_gimple_stmt);
> -  /* Unsigned types with smaller than mode precision, even if they have most
> -     significant bit set, are still zero-extended.  */
> -  if (uns0_p && TYPE_PRECISION (TREE_TYPE (arg0)) < GET_MODE_PRECISION 
> (mode))
> -    pos_neg0 = 1;
> -  if (uns1_p && TYPE_PRECISION (TREE_TYPE (arg1)) < GET_MODE_PRECISION 
> (mode))
> -    pos_neg1 = 1;
>  
>    /* s1 * u2 -> ur  */
>    if (!uns0_p && uns1_p && unsr_p)
> @@ -2559,8 +2569,8 @@ expand_mul_overflow (location_t loc, tre
>      {
>        /* Expand the ubsan builtin call.  */
>        push_temp_slots ();
> -      fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE (arg0),
> -                                      arg0, arg1, datap);
> +      fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE 
> (orig_arg0),
> +                                      orig_arg0, orig_arg1, datap);
>        expand_normal (fn);
>        pop_temp_slots ();
>        do_pending_stack_adjust ();
> --- a/gcc/testsuite/gcc.dg/torture/bitint-109.c       2026-09-23 
> 20:02:01.995248033 +0200
> +++ b/gcc/testsuite/gcc.dg/torture/bitint-109.c       2026-09-23 
> 20:01:41.879511945 +0200
> @@ -0,0 +1,18 @@
> +/* PR middle-end/127517 */
> +/* { dg-do run { target bitint } } */
> +
> +__attribute__((noipa)) void
> +foo (int x)
> +{
> +#if __BITINT_MAXWIDTH__ >= 128
> +  _BitInt(128) t = 0x7fffffffffffffffffffffffffffffffwb;
> +  if (!__builtin_mul_overflow_p (t, x, (_BitInt (127)) 0))
> +    __builtin_abort ();
> +#endif
> +}
> +
> +int
> +main ()
> +{
> +  foo (8);
> +}
> 
>       Jakub
> 
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to