https://gcc.gnu.org/g:242deb9ed759f469c1e5de5b328c7e71785d2b69
commit r15-11634-g242deb9ed759f469c1e5de5b328c7e71785d2b69 Author: Jakub Jelinek <[email protected]> Date: Fri Sep 25 11:09:37 2026 +0200 internal-fn: Fix up .MUL_OVERFLOW expansion for non-mode precision arguments [PR127517] 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. 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. Reviewed-by: Richard Biener <[email protected]> (cherry picked from commit 240bfe82a1f937a9b0be9877754a3d23567d5ec9) Diff: --- gcc/internal-fn.cc | 30 ++++++++++++++++++++---------- gcc/testsuite/gcc.dg/torture/bitint-109.c | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc index 044f66830b35..a660715d307d 100644 --- a/gcc/internal-fn.cc +++ b/gcc/internal-fn.cc @@ -1680,7 +1680,7 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1, 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; @@ -1694,10 +1694,26 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1, 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) { @@ -1759,12 +1775,6 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1, int pos_neg0 = get_range_pos_neg (arg0); int pos_neg1 = get_range_pos_neg (arg1); - /* 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) @@ -2489,8 +2499,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1, { /* 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 (); diff --git a/gcc/testsuite/gcc.dg/torture/bitint-109.c b/gcc/testsuite/gcc.dg/torture/bitint-109.c new file mode 100644 index 000000000000..944e9f4deb67 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/bitint-109.c @@ -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); +}
