Thanks Richard.
On point 1 (get_precision on the int_range): I looked into this, and it
does not give the narrowed precision. irange has no get_precision ();
the only one is irange_bitmask::get_precision (), which returns the
mask's wide_int width, that is, the type precision (32 for int), not the
number of bits the values actually occupy. The current computation
instead derives the precision the values need (for example 20 when the
dividend lies in [0, 1000000]), which is what choose_multiplier takes.
On point 2 (passing a value_range object to expand_divmod): I would
prefer to keep the int precision here. The multiply-shift narrowing
uses only the precision: choose_multiplier takes an int precision, and
once that number is known the lower bound, the sign and the exact
endpoints play no part in the computation. Passing the whole range
object would give the callee more than it uses, to arrive at the same
single number it needs today.
The range object would help only a different job, the {1, power-of-two}
work you mentioned, which is not part of this patch. So I would prefer
to keep the int and let that work decide for itself how it wants the
range, rather than add a heavier parameter here that this optimization
does not need.
This patch is small and self-contained, and it is already bootstrapped
and regression-tested, so I would prefer to keep it as it is. Would
that work?
On Wed, Jul 1, 2026 at 7:02 PM Richard Biener <[email protected]>
wrote:
> On Mon, Jun 22, 2026 at 4:30 AM Nguyen Tran <[email protected]>
> wrote:
> >
> > For divisions by a compile-time-constant divisor, expand_divmod calls
> > choose_multiplier with the dividend's full type precision (size or
> > size - 1). When the dividend's value range is known to be narrower
> > than the type, choose_multiplier could pick a smaller multiplier and
> > post-shift, but expand_divmod has no path to receive that range
> > information.
> >
> > This patch threads the VRP-derived value range from the GIMPLE side
> > into expand_divmod and forwards it to choose_multiplier:
> >
> > * expand_expr_divmod (gcc/expr.cc) computes a dividend precision from
> > treeop0's range via a new helper determine_value_range, which
> > queries the active range_query during pass_expand.
> >
> > * For unsigned (or non-negative-known signed) operands the precision
> > is wi::min_precision(max_val, UNSIGNED). For signed operands with
> > possibly negative values it is wi::min_precision of the larger
> > of -(min + 1) and max under unsigned interpretation, the bound
> > choose_multiplier itself relies on.
> >
> > * expand_divmod gets a new dividend_prec parameter and an inline
> > helper adjusted_dividend_prec that passes it to choose_multiplier
> > raised to LGUP, so the lgup <= precision precondition is preserved.
> > The helper is invoked at three call sites: unsigned TRUNC, signed
> > TRUNC, and signed FLOOR. Passing -1 selects the dividend's full
> > type precision and reproduces the baseline codegen. dividend_prec
> > is a required parameter, placed before the optional methods
> > argument; all callers other than expand_expr_divmod pass -1.
> >
> > For the example in PR target/91883,
> >
> > unsigned g (unsigned a) {
> > if (a > 1000000) __builtin_unreachable ();
> > return a / 10;
> > }
> >
> > baseline -O2 emits
> >
> > movl %edi, %eax
> > movl $3435973837, %edx
> > imulq %rdx, %rax
> > shrq $35, %rax
> >
> > with this patch:
> >
> > movl %edi, %eax
> > imulq $429497139, %rax, %rax
> > shrq $32, %rax
> >
> > The smaller multiplier fits imulq's 32-bit signed immediate, saving
> > the separate movl, and the post-shift drops from 35 to 32. Similar
> > effects for /3, /7, /100. For signed /7, codegen also shifts from
> > the long path (addl-fixup) to the short path, reducing seven
> > instructions to five.
> >
> > Functions without VRP range information pass -1 and are unaffected.
> >
> > gcc.target/i386/pr115910.c is updated. The test has two functions
> > both doing x / 3U: foo with __builtin_unreachable when x < 0, and
> > bar with no range information. Previously both produced identical
> > codegen; the test asserted 2 imulq + 2 shrq $33. With this patch foo's
> > __builtin_unreachable narrows x to [0, INT_MAX] giving VRP-derived
> > precision 31, so choose_multiplier picks a smaller M (1431655766,
> > fits imulq's 32-bit immediate) and post-shift 32 instead of 33.
> > bar is unchanged. The dg-final assertions are split: still 2 imulq,
> > plus 1 shrq $32 (foo) and 1 shrq $33 (bar).
> >
> > One drawback of this patch is some code bloat: the same
> > divisor at two call sites with different ranges produces different
> > assembly instructions. Unmodified GCC produces the same assembly
> > instructions regardless of range, allowing the linker's identical-
> > code-folding (ICF) pass to merge the two function bodies into a
> > single copy in the final binary. With this patch the bodies differ,
> > so ICF cannot fold them. Clang has the same drawback: it also
> > produces different assembly instructions for the same divisor at
> > different ranges, which ICF cannot fold.
> >
> > For example, consider /3 at two different ranges:
> >
> > unsigned int f3 (unsigned int a) {
> > if (a > 65535) __builtin_unreachable ();
> > return a / 3;
> > }
> > unsigned int f (unsigned int a) {
> > if (a > 655356) __builtin_unreachable ();
> > return a / 3;
> > }
> >
> > Unmodified GCC produces byte-identical assembly instructions for
> > both functions, which ICF folds into a single copy:
> > f3:
> > mov eax, edi
> > mov edx, 2863311531
> > imul rax, rdx
> > shr rax, 33
> > ret
> > f:
> > mov eax, edi
> > mov edx, 2863311531
> > imul rax, rdx
> > shr rax, 33
> > ret
> >
> > Clang produces different assembly instructions; ICF cannot fold them:
> > f3:
> > imul eax, edi, 43691
> > shr eax, 17
> > ret
> > f:
> > mov ecx, edi
> > mov eax, 2863311531
> > imul rax, rcx
> > shr rax, 33
> > ret
> >
> > Patched GCC produces different assembly instructions; ICF cannot
> > fold them:
> > f3:
> > mov eax, edi
> > imul rax, rax, 1431677610
> > shr rax, 32
> > ret
> > f:
> > mov eax, edi
> > imul rax, rax, 1431657130
> > shr rax, 32
> > ret
> >
> > A live demonstration: https://godbolt.org/z/75r6axnvG, compiled with
> >
> > -O2 -ffunction-sections -fuse-ld=gold -Wl,--icf=all
> >
> > The flags:
> > -ffunction-sections put each function into its own section, so
> > the linker can compare and fold them
> > individually
> > -fuse-ld=gold use the gold linker (the default linker
> > does not support ICF)
> > -Wl,--icf=all enable ICF in the linker
> >
> > Under unmodified GCC, f3 and f end up at the same address. Under
> > clang and patched GCC, they end up at different addresses.
> >
> > Under default compiler/linker flags this patch likely has no meaningful
> > downside: ICF is not enabled by default, so unmodified and patched
> > both keep two copies of the body in .text. We consider this an
> > acceptable tradeoff: each individual division is at least as small
> > as before, and clang accepts the same one.
> >
> > Bootstrapped on x86_64-pc-linux-gnu with the default language set
> > (c, c++, fortran, objc, obj-c++, lto): "make bootstrap" followed by
> > "make -k check" from the top of the build tree. Stage-2 vs. stage-3
> > binary comparison successful (the two compilers GCC produces of itself
> > are byte-identical). No new regressions in the testsuite, and all
> > dg-final assertions across the seven new gcc.target/i386/divmod-range-*.c
> > tests pass.
> >
> > gcc/ChangeLog:
> >
> > PR target/91883
> > * explow.cc (round_push, align_dynamic_address): Pass -1 for
> > dividend_prec.
> > * expmed.cc (choose_multiplier): Prepend comment stating the
> > multiply-shift formula it computes.
> > (adjusted_dividend_prec): New helper.
> > (expand_divmod): Add dividend_prec parameter. Forward to
> > adjusted_dividend_prec at the unsigned TRUNC, signed TRUNC,
> > and signed FLOOR XOR-sign-flip call sites. Pass -1 at the
> > recursive calls.
> > * expmed.h (expand_divmod): Add dividend_prec parameter.
> > * expr.cc (force_operand): Pass -1 for dividend_prec.
> > (determine_value_range): New helper, queries range_query
> > during pass_expand.
> > (expand_expr_divmod): Compute dividend_prec from treeop0's
> > range and forward to expand_divmod.
> > * optabs.cc (expand_doubleword_mod, expand_doubleword_divmod):
> > Pass -1 for dividend_prec.
> >
> > gcc/testsuite/ChangeLog:
> >
> > PR target/91883
> > * gcc.target/i386/pr115910.c: Adjust shrq assertions for
> > VRP-narrowed multiplier on foo.
> > * gcc.target/i386/divmod-range-1.c: New test: VRP-narrowed
> > multiplier for unsigned /10.
> > * gcc.target/i386/divmod-range-2.c: New test: VRP-narrowed
> > multiplier for unsigned %10.
> > * gcc.target/i386/divmod-range-3.c: New test: VRP-narrowed
> > multiplier for signed /10.
> > * gcc.target/i386/divmod-range-4.c: New test: VRP shifts
> > signed /7 from the long (addl-fixup) path to the short path.
> > * gcc.target/i386/divmod-range-5.c: New test: regression guard,
> > no narrower range preserves baseline unsigned divmod codegen.
> > * gcc.target/i386/divmod-range-6.c: New test: regression guard,
> > no narrower range preserves signed /7 long-path codegen.
> > * gcc.target/i386/divmod-range-7.c: New test: VRP-narrowed
> > multiplier for signed FLOOR_DIV (uses GIMPLE Front End since
> > FLOOR_DIV_EXPR cannot be produced from C source).
> >
> > Signed-off-by: Nguyen Tran <[email protected]>
> > ---
> > This is v2 of the patch originally posted here:
> > https://gcc.gnu.org/pipermail/gcc-patches/2026-May/717904.html
> > Jeff's review:
> > https://gcc.gnu.org/pipermail/gcc-patches/2026-June/719238.html
> > Thanks Jeff for the review. v2 addresses all the points raised:
> > 1. DCO / sign-off: Added a Signed-off-by: tag to put the contribution
> > under the DCO.
> > 2. "hint" -> "range"/"precision": Reworded throughout. You're right
> > that "hint" implies getting it wrong is harmless, which is not the
> > case here: a wrong value produces wrong codegen. All comments and
> > the new parameter are now phrased in terms of the dividend's range /
> > precision.
> > 3. No default argument: Dropped the default value on the new
> > dividend_prec parameter. It is now a required parameter and all call
> > sites are updated explicitly to pass -1 (which selects the dividend's
> > full type precision), except expand_expr_divmod which passes the
> > VRP-derived value.
> > 4. Overflow in -min - 1: Reworked the signed negative-range
> > computation to use -(min + 1), which yields the same value but cannot
> > overflow at any intermediate step (because min is guaranteed to be
> > < 0 here, so min + 1 cannot overflow, and negating a value in
> > [INT_MIN+1 .. 0] is always representable).
> > 5. Bootstrap + regression, platform stated: Bootstrapped and
> > regression-tested on x86_64-pc-linux-gnu (languages: c, c++, fortran,
> > objc, obj-c++, lto). "make bootstrap" + "make -k check" from the top
> > of the build tree; stage-2 vs. stage-3 comparison is clean
> > (byte-identical), no new regressions.
> > ---
> > gcc/explow.cc | 4 +-
> > gcc/expmed.cc | 51 ++++++++++--
> > gcc/expmed.h | 3 +-
> > gcc/expr.cc | 77 +++++++++++++++++--
> > gcc/optabs.cc | 6 +-
> > .../gcc.target/i386/divmod-range-1.c | 15 ++++
> > .../gcc.target/i386/divmod-range-2.c | 14 ++++
> > .../gcc.target/i386/divmod-range-3.c | 14 ++++
> > .../gcc.target/i386/divmod-range-4.c | 14 ++++
> > .../gcc.target/i386/divmod-range-5.c | 13 ++++
> > .../gcc.target/i386/divmod-range-6.c | 13 ++++
> > .../gcc.target/i386/divmod-range-7.c | 34 ++++++++
> > gcc/testsuite/gcc.target/i386/pr115910.c | 3 +-
> > 13 files changed, 241 insertions(+), 20 deletions(-)
> > create mode 100644 gcc/testsuite/gcc.target/i386/divmod-range-1.c
> > create mode 100644 gcc/testsuite/gcc.target/i386/divmod-range-2.c
> > create mode 100644 gcc/testsuite/gcc.target/i386/divmod-range-3.c
> > create mode 100644 gcc/testsuite/gcc.target/i386/divmod-range-4.c
> > create mode 100644 gcc/testsuite/gcc.target/i386/divmod-range-5.c
> > create mode 100644 gcc/testsuite/gcc.target/i386/divmod-range-6.c
> > create mode 100644 gcc/testsuite/gcc.target/i386/divmod-range-7.c
> >
> > diff --git a/gcc/explow.cc b/gcc/explow.cc
> > index 755c1d2e6dc..750c155467f 100644
> > --- a/gcc/explow.cc
> > +++ b/gcc/explow.cc
> > @@ -1114,7 +1114,7 @@ round_push (rtx size)
> > size = expand_binop (Pmode, add_optab, size, alignm1_rtx,
> > NULL_RTX, 1, OPTAB_LIB_WIDEN);
> > size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, align_rtx,
> > - NULL_RTX, 1);
> > + NULL_RTX, 1, -1);
> > size = expand_mult (Pmode, size, align_rtx, NULL_RTX, 1);
> >
> > return size;
> > @@ -1285,7 +1285,7 @@ align_dynamic_address (rtx target, unsigned
> required_align)
> > target = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, target,
> > gen_int_mode (required_align / BITS_PER_UNIT,
> > Pmode),
> > - NULL_RTX, 1);
> > + NULL_RTX, 1, -1);
> > target = expand_mult (Pmode, target,
> > gen_int_mode (required_align / BITS_PER_UNIT,
> > Pmode),
> > diff --git a/gcc/expmed.cc b/gcc/expmed.cc
> > index fe39506b6bd..95b2a52cae2 100644
> > --- a/gcc/expmed.cc
> > +++ b/gcc/expmed.cc
> > @@ -3733,6 +3733,15 @@ expand_widening_mult (machine_mode mode, rtx op0,
> rtx op1, rtx target,
> > unsignedp, OPTAB_LIB_WIDEN);
> > }
> >
> > +/* For any x satisfying 0 <= x < 2^PRECISION, this function picks
> > + integers M and K satisfying
> > +
> > + floor (x / D) = floor ((x * M) / 2^K)
> > +
> > + so an unsigned divide by the constant D can be replaced by one
> > + multiply and one right shift. K is internal, recovered from the
> outputs as
> > + K = N + (*POST_SHIFT_PTR). */
> > +
> > /* Choose a minimal N + 1 bit approximation to 2**K / D that can be
> used to
> > replace division by D, put the least significant N bits of the
> result in
> > *MULTIPLIER_PTR, the value K - N in *POST_SHIFT_PTR, and return the
> most
> > @@ -4243,6 +4252,21 @@ expand_sdiv_pow2 (scalar_int_mode mode, rtx op0,
> HOST_WIDE_INT d)
> > emit_label (label);
> > return expand_shift (RSHIFT_EXPR, mode, temp, logd, NULL_RTX, 0);
> > }
> > +
> > +/* Return the precision to pass to choose_multiplier. When
> DIVIDEND_PREC
> > + is non-negative, use it as the value-range-derived precision, raised
> > + to at least LGUP since choose_multiplier asserts lgup <= precision.
> > + When -1, the value range was not used; return DEFAULT_PREC. By
> > + construction in the caller, DIVIDEND_PREC never exceeds
> DEFAULT_PREC. */
> > +
> > +static inline int
> > +adjusted_dividend_prec (int dividend_prec, int default_prec, int lgup)
> > +{
> > + if (dividend_prec >= 0)
> > + return dividend_prec < lgup ? lgup : dividend_prec;
> > + return default_prec;
> > +}
> > +
> >
> > /* Emit the code to divide OP0 by OP1, putting the result in TARGET
> > if that is convenient, and returning where the result is.
> > @@ -4285,7 +4309,7 @@ expand_sdiv_pow2 (scalar_int_mode mode, rtx op0,
> HOST_WIDE_INT d)
> > rtx
> > expand_divmod (int rem_flag, enum tree_code code, machine_mode mode,
> > rtx op0, rtx op1, rtx target, int unsignedp,
> > - enum optab_methods methods)
> > + int dividend_prec, enum optab_methods methods)
> > {
> > machine_mode compute_mode;
> > rtx tquotient;
> > @@ -4528,7 +4552,12 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> > {
> > /* Find a suitable multiplier and right shift
> count
> > instead of directly dividing by D. */
> > - mh = choose_multiplier (d, size, size,
> > +
> > + /* Use reduced precision if range info
> available. */
> > + int prec = adjusted_dividend_prec
> (dividend_prec, size,
> > + ceil_log2
> (d));
> > +
> > + mh = choose_multiplier (d, size, prec,
> > &ml, &post_shift);
> >
> > /* If the suggested multiplier is more than SIZE
> bits,
> > @@ -4537,6 +4566,9 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> > if (mh != 0 && (d & 1) == 0)
> > {
> > pre_shift = ctz_or_zero (d);
> > +
> > + /* Only reached when the prior call used
> full SIZE,
> > + so DIVIDEND_PREC here is saturated. */
> > mh = choose_multiplier (d >> pre_shift, size,
> > size - pre_shift,
> > &ml, &post_shift);
> > @@ -4681,7 +4713,7 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> > int_mode, op0,
> > gen_int_mode (abs_d,
> > int_mode),
> > - NULL_RTX, 0);
> > + NULL_RTX, 0, -1);
> > else
> > quotient = expand_sdiv_pow2 (int_mode, op0, abs_d);
> >
> > @@ -4706,8 +4738,12 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> > }
> > else if (size <= HOST_BITS_PER_WIDE_INT)
> > {
> > - choose_multiplier (abs_d, size, size - 1,
> > + int prec = adjusted_dividend_prec (dividend_prec,
> size - 1,
> > + ceil_log2
> (abs_d));
> > +
> > + choose_multiplier (abs_d, size, prec,
> > &ml, &post_shift);
> > +
> > if (ml < HOST_WIDE_INT_1U << (size - 1))
> > {
> > rtx t1, t2, t3;
> > @@ -4827,7 +4863,10 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> > {
> > rtx t1, t2, t3, t4;
> >
> > - mh = choose_multiplier (d, size, size - 1,
> > + int prec = adjusted_dividend_prec (dividend_prec,
> size - 1,
> > + ceil_log2 (d));
> > +
> > + mh = choose_multiplier (d, size, prec,
> > &ml, &post_shift);
> > gcc_assert (!mh);
> >
> > @@ -4869,7 +4908,7 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> > t3 = force_operand (gen_rtx_MINUS (int_mode, t1, nsign),
> > NULL_RTX);
> > t4 = expand_divmod (0, TRUNC_DIV_EXPR, int_mode, t3, op1,
> > - NULL_RTX, 0);
> > + NULL_RTX, 0, -1);
> > if (t4)
> > {
> > rtx t5;
> > diff --git a/gcc/expmed.h b/gcc/expmed.h
> > index 557b58392ba..2678d2f1ad4 100644
> > --- a/gcc/expmed.h
> > +++ b/gcc/expmed.h
> > @@ -711,7 +711,8 @@ extern rtx maybe_expand_shift (enum tree_code,
> machine_mode, rtx, int, rtx,
> > int);
> > #ifdef GCC_OPTABS_H
> > extern rtx expand_divmod (int, enum tree_code, machine_mode, rtx, rtx,
> > - rtx, int, enum optab_methods =
> OPTAB_LIB_WIDEN);
> > + rtx, int, int,
> > + enum optab_methods = OPTAB_LIB_WIDEN);
> > #endif
> > #endif
> >
> > diff --git a/gcc/expr.cc b/gcc/expr.cc
> > index 7ca5dd84ce9..d9158ab4dce 100644
> > --- a/gcc/expr.cc
> > +++ b/gcc/expr.cc
> > @@ -66,6 +66,8 @@ along with GCC; see the file COPYING3. If not see
> > #include "tree-pretty-print.h"
> > #include "flags.h"
> > #include "internal-fn.h"
> > +#include "value-query.h"
> > +#include "value-range.h"
> >
> >
> > /* If this is nonzero, we do not bother generating VOLATILE
> > @@ -8811,16 +8813,16 @@ force_operand (rtx value, rtx target)
> > return expand_divmod (0,
> > FLOAT_MODE_P (GET_MODE (value))
> > ? RDIV_EXPR : TRUNC_DIV_EXPR,
> > - GET_MODE (value), op1, op2, target, 0);
> > + GET_MODE (value), op1, op2, target, 0,
> -1);
> > case MOD:
> > return expand_divmod (1, TRUNC_MOD_EXPR, GET_MODE (value),
> op1, op2,
> > - target, 0);
> > + target, 0, -1);
> > case UDIV:
> > return expand_divmod (0, TRUNC_DIV_EXPR, GET_MODE (value),
> op1, op2,
> > - target, 1);
> > + target, 1, -1);
> > case UMOD:
> > return expand_divmod (1, TRUNC_MOD_EXPR, GET_MODE (value),
> op1, op2,
> > - target, 1);
> > + target, 1, -1);
> > case ASHIFTRT:
> > return expand_simple_binop (GET_MODE (value), code, op1, op2,
> > target, 0, OPTAB_LIB_WIDEN);
> > @@ -9765,6 +9767,35 @@ expand_misaligned_mem_ref (rtx temp, machine_mode
> mode, int unsignedp,
> > return temp;
> > }
> >
> > +/* Determine the value range of OP at the current statement.
> > + Returns true if range is known, stores bounds in MIN_VAL and
> MAX_VAL. */
> > +
> > +static bool
> > +determine_value_range (tree op, wide_int *min_val, wide_int *max_val)
> > +{
> > + if (!currently_expanding_gimple_stmt)
> > + return false;
> > +
> > + if (TREE_CODE (op) != SSA_NAME)
> > + return false;
> > +
> > + tree type = TREE_TYPE (op);
> > + if (!INTEGRAL_TYPE_P (type))
> > + return false;
> > +
> > + int_range_max r;
> > + if (!get_range_query (cfun)->range_of_expr (r, op,
> > +
> currently_expanding_gimple_stmt))
> > + return false;
> > +
> > + if (r.undefined_p () || r.varying_p ())
> > + return false;
> > +
> > + *min_val = r.lower_bound ();
> > + *max_val = r.upper_bound ();
> > + return true;
> > +}
> > +
> > /* Helper function of expand_expr_2, expand a division or modulo.
> > op0 and op1 should be already expanded treeop0 and treeop1, using
> > expand_operands. */
> > @@ -9775,6 +9806,35 @@ expand_expr_divmod (tree_code code, machine_mode
> mode, tree treeop0,
> > {
> > bool mod_p = (code == TRUNC_MOD_EXPR || code == FLOOR_MOD_EXPR
> > || code == CEIL_MOD_EXPR || code == ROUND_MOD_EXPR);
> > +
> > + /* Calculate dividend precision from value range if available. */
> > + int dividend_prec = -1;
> > +
> > + if (SCALAR_INT_MODE_P (mode)
> > + && optimize >= 2
> > + && TREE_CODE (treeop1) == INTEGER_CST)
> > + {
> > + wide_int min_val, max_val;
> > + if (determine_value_range (treeop0, &min_val, &max_val))
>
> can you use ->get_precision () on the int_range instead?
>
> I do wonder whether passing an actual value_range object to
> expand_divmod would be more useful (see the most recent
> patch to attempt to special case division by {1, power-of-two}?
>
> > + {
> > + if (unsignedp || wi::ges_p (min_val, 0))
> > + {
> > + /* Unsigned or known non-negative: precision from upper
> bound. */
> > + dividend_prec = wi::min_precision (max_val, UNSIGNED);
> > + }
> > + else
> > + {
> > + /* Signed with possible negative values. Take the unsigned
> > + precision of the larger of -(min + 1) and max (or just
> > + -(min + 1) when max < 0, since -(min + 1) dominates
> then). */
> > + wide_int neg_side = -(min_val + 1);
> > + wide_int pos_side = wi::ges_p (max_val, 0) ? max_val :
> neg_side;
> > + wide_int worst = wi::umax (neg_side, pos_side);
> > + dividend_prec = wi::min_precision (worst, UNSIGNED);
> > + }
> > + }
> > + }
> > +
> > if (SCALAR_INT_MODE_P (mode)
> > && optimize >= 2
> > && get_range_pos_neg (treeop0, currently_expanding_gimple_stmt)
> == 1
> > @@ -9786,10 +9846,12 @@ expand_expr_divmod (tree_code code, machine_mode
> mode, tree treeop0,
> > bool speed_p = optimize_insn_for_speed_p ();
> > do_pending_stack_adjust ();
> > start_sequence ();
> > - rtx uns_ret = expand_divmod (mod_p, code, mode, op0, op1, target,
> 1);
> > + rtx uns_ret = expand_divmod (mod_p, code, mode, op0, op1, target,
> 1,
> > + dividend_prec);
> > rtx_insn *uns_insns = end_sequence ();
> > start_sequence ();
> > - rtx sgn_ret = expand_divmod (mod_p, code, mode, op0, op1, target,
> 0);
> > + rtx sgn_ret = expand_divmod (mod_p, code, mode, op0, op1, target,
> 0,
> > + dividend_prec);
> > rtx_insn *sgn_insns = end_sequence ();
> > unsigned uns_cost = seq_cost (uns_insns, speed_p);
> > unsigned sgn_cost = seq_cost (sgn_insns, speed_p);
> > @@ -9817,7 +9879,8 @@ expand_expr_divmod (tree_code code, machine_mode
> mode, tree treeop0,
> > emit_insn (sgn_insns);
> > return sgn_ret;
> > }
> > - return expand_divmod (mod_p, code, mode, op0, op1, target, unsignedp);
> > + return expand_divmod (mod_p, code, mode, op0, op1, target, unsignedp,
> > + dividend_prec);
> > }
> >
> > /* Return true if EXP has a range of values [0..1], false
> > diff --git a/gcc/optabs.cc b/gcc/optabs.cc
> > index d17fbcf12ed..9aaa37aacd8 100644
> > --- a/gcc/optabs.cc
> > +++ b/gcc/optabs.cc
> > @@ -1143,7 +1143,7 @@ expand_doubleword_mod (machine_mode mode, rtx op0,
> rtx op1, bool unsignedp)
> > }
> > rtx remainder = expand_divmod (1, TRUNC_MOD_EXPR, word_mode, sum,
> > gen_int_mode (INTVAL (op1),
> word_mode),
> > - NULL_RTX, 1, OPTAB_DIRECT);
> > + NULL_RTX, 1, -1, OPTAB_DIRECT);
> > if (remainder == NULL_RTX)
> > return NULL_RTX;
> >
> > @@ -1246,7 +1246,7 @@ expand_doubleword_divmod (machine_mode mode, rtx
> op0, rtx op1, rtx *rem,
> > if (op11 != const1_rtx)
> > {
> > rtx rem2 = expand_divmod (1, TRUNC_MOD_EXPR, mode, quot1, op11,
> > - NULL_RTX, unsignedp, OPTAB_DIRECT);
> > + NULL_RTX, unsignedp, -1, OPTAB_DIRECT);
> > if (rem2 == NULL_RTX)
> > return NULL_RTX;
> >
> > @@ -1261,7 +1261,7 @@ expand_doubleword_divmod (machine_mode mode, rtx
> op0, rtx op1, rtx *rem,
> > return NULL_RTX;
> >
> > rtx quot2 = expand_divmod (0, TRUNC_DIV_EXPR, mode, quot1, op11,
> > - NULL_RTX, unsignedp, OPTAB_DIRECT);
> > + NULL_RTX, unsignedp, -1, OPTAB_DIRECT);
> > if (quot2 == NULL_RTX)
> > return NULL_RTX;
> >
> > diff --git a/gcc/testsuite/gcc.target/i386/divmod-range-1.c
> b/gcc/testsuite/gcc.target/i386/divmod-range-1.c
> > new file mode 100644
> > index 00000000000..57ab5149374
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/divmod-range-1.c
> > @@ -0,0 +1,15 @@
> > +/* Verify that VRP-derived range info reduces the unsigned divmod
> multiplier. */
> > +/* { dg-do compile { target { ! ia32 } } } */
> > +/* { dg-options "-O2 -march=x86-64 -mtune=generic -masm=att" } */
> > +
> > +unsigned int
> > +foo (unsigned int a)
> > +{
> > + if (a > 1000000) __builtin_unreachable ();
> > + return a / 10;
> > +}
> > +
> > +/* { dg-final { scan-assembler {\timulq\t\$429497139,} } } */
> > +/* { dg-final { scan-assembler {\tshrq\t\$32,} } } */
> > +/* { dg-final { scan-assembler-not {\$3435973837} } } */
> > +/* { dg-final { scan-assembler-not {\tshrq\t\$35,} } } */
> > diff --git a/gcc/testsuite/gcc.target/i386/divmod-range-2.c
> b/gcc/testsuite/gcc.target/i386/divmod-range-2.c
> > new file mode 100644
> > index 00000000000..ac5e9edc7aa
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/divmod-range-2.c
> > @@ -0,0 +1,14 @@
> > +/* Verify that VRP-derived range info reduces the unsigned modulo
> multiplier. */
> > +/* { dg-do compile { target { ! ia32 } } } */
> > +/* { dg-options "-O2 -march=x86-64 -mtune=generic -masm=att" } */
> > +
> > +unsigned int
> > +foo (unsigned int a)
> > +{
> > + if (a > 1000000) __builtin_unreachable ();
> > + return a % 10;
> > +}
> > +
> > +/* { dg-final { scan-assembler {\timulq\t\$429497139,} } } */
> > +/* { dg-final { scan-assembler {\tshrq\t\$32,} } } */
> > +/* { dg-final { scan-assembler-not {\$3435973837} } } */
> > diff --git a/gcc/testsuite/gcc.target/i386/divmod-range-3.c
> b/gcc/testsuite/gcc.target/i386/divmod-range-3.c
> > new file mode 100644
> > index 00000000000..bbca89b42c0
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/divmod-range-3.c
> > @@ -0,0 +1,14 @@
> > +/* Verify that VRP-derived range info reduces the signed-divide-by-10
> multiplier. */
> > +/* { dg-do compile { target { ! ia32 } } } */
> > +/* { dg-options "-O2 -march=x86-64 -mtune=generic -masm=att" } */
> > +
> > +int
> > +foo (int a)
> > +{
> > + if (a > 1000000 || a < -1000000) __builtin_unreachable ();
> > + return a / 10;
> > +}
> > +
> > +/* { dg-final { scan-assembler {\timulq\t\$429497139,} } } */
> > +/* { dg-final { scan-assembler-not {\$1717986919} } } */
> > +/* { dg-final { scan-assembler-not {\tsarq\t\$34,} } } */
> > diff --git a/gcc/testsuite/gcc.target/i386/divmod-range-4.c
> b/gcc/testsuite/gcc.target/i386/divmod-range-4.c
> > new file mode 100644
> > index 00000000000..ea9b7926862
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/divmod-range-4.c
> > @@ -0,0 +1,14 @@
> > +/* Verify that VRP-derived range info shifts signed /7 from LONG to
> SHORT codegen. */
> > +/* { dg-do compile { target { ! ia32 } } } */
> > +/* { dg-options "-O2 -march=x86-64 -mtune=generic -masm=att" } */
> > +
> > +int
> > +foo (int a)
> > +{
> > + if (a > 1000000 || a < -1000000) __builtin_unreachable ();
> > + return a / 7;
> > +}
> > +
> > +/* { dg-final { scan-assembler {\timulq\t\$613567341,} } } */
> > +/* { dg-final { scan-assembler-not {\$-1840700269} } } */
> > +/* { dg-final { scan-assembler-not {\taddl\t%edi, %eax} } } */
> > diff --git a/gcc/testsuite/gcc.target/i386/divmod-range-5.c
> b/gcc/testsuite/gcc.target/i386/divmod-range-5.c
> > new file mode 100644
> > index 00000000000..195f41bca9b
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/divmod-range-5.c
> > @@ -0,0 +1,13 @@
> > +/* Regression test: dividend with no narrower range must preserve
> baseline
> > + codegen. */
> > +/* { dg-do compile { target { ! ia32 } } } */
> > +/* { dg-options "-O2 -march=x86-64 -mtune=generic -masm=att" } */
> > +
> > +unsigned int
> > +foo (unsigned int a)
> > +{
> > + return a / 10;
> > +}
> > +
> > +/* { dg-final { scan-assembler {\$3435973837} } } */
> > +/* { dg-final { scan-assembler {\tshrq\t\$35,} } } */
> > diff --git a/gcc/testsuite/gcc.target/i386/divmod-range-6.c
> b/gcc/testsuite/gcc.target/i386/divmod-range-6.c
> > new file mode 100644
> > index 00000000000..b1a178bca9f
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/divmod-range-6.c
> > @@ -0,0 +1,13 @@
> > +/* Regression test: dividend with no narrower range must preserve
> LONG-path
> > + codegen. */
> > +/* { dg-do compile { target { ! ia32 } } } */
> > +/* { dg-options "-O2 -march=x86-64 -mtune=generic -masm=att" } */
> > +
> > +int
> > +foo (int a)
> > +{
> > + return a / 7;
> > +}
> > +
> > +/* { dg-final { scan-assembler {\$-1840700269} } } */
> > +/* { dg-final { scan-assembler {\taddl\t%edi, %eax} } } */
> > diff --git a/gcc/testsuite/gcc.target/i386/divmod-range-7.c
> b/gcc/testsuite/gcc.target/i386/divmod-range-7.c
> > new file mode 100644
> > index 00000000000..524b14b824c
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/divmod-range-7.c
> > @@ -0,0 +1,34 @@
> > +/* Verify that VRP-derived range info reduces the signed FLOOR_DIV
> multiplier.
> > + Uses __GIMPLE FE because FLOOR_DIV_EXPR cannot be produced from C
> source. */
> > +/* { dg-do compile { target { ! ia32 } } } */
> > +/* { dg-options "-O2 -fgimple -march=x86-64 -mtune=generic -masm=att" }
> */
> > +
> > +int __GIMPLE (ssa)
> > +foo (int a)
> > +{
> > + int t_2;
> > + unsigned int au_4;
> > + unsigned int range_5;
> > +
> > +__BB(2):
> > + au_4 = (unsigned int) a_3(D);
> > + range_5 = au_4 + 1000000u;
> > + if (range_5 > 2000000u)
> > + goto __BB3;
> > + else
> > + goto __BB4;
> > +
> > +__BB(3):
> > + __builtin_unreachable ();
> > +
> > +__BB(4):
> > + t_2 = a_3(D) __FLOOR_DIV 7;
> > + return t_2;
> > +}
> > +
> > +/* { dg-final { scan-assembler {\timulq\t\$613567341,} } } */
> > +/* { dg-final { scan-assembler {\tshrq\t\$32,} } } */
> > +/* { dg-final { scan-assembler {\tsarl\t\$31,} } } */
> > +/* { dg-final { scan-assembler-times {\txorl\t} 2 } } */
> > +/* { dg-final { scan-assembler-not {\$2454267027} } } */
> > +/* { dg-final { scan-assembler-not {\tshrq\t\$34,} } } */
> > diff --git a/gcc/testsuite/gcc.target/i386/pr115910.c
> b/gcc/testsuite/gcc.target/i386/pr115910.c
> > index 5f1cd9aa010..41e94142392 100644
> > --- a/gcc/testsuite/gcc.target/i386/pr115910.c
> > +++ b/gcc/testsuite/gcc.target/i386/pr115910.c
> > @@ -2,7 +2,8 @@
> > /* { dg-do compile { target { ! ia32 } } } */
> > /* { dg-options "-O2 -march=x86-64 -mtune=generic -masm=att" } */
> > /* { dg-final { scan-assembler-times {\timulq\t} 2 } } */
> > -/* { dg-final { scan-assembler-times {\tshrq\t\$33,} 2 } } */
> > +/* { dg-final { scan-assembler-times {\tshrq\t\$32,} 1 } } */
> > +/* { dg-final { scan-assembler-times {\tshrq\t\$33,} 1 } } */
> > /* { dg-final { scan-assembler-not {\tsarl\t} } } */
> >
> > int
> > --
> > 2.43.0
> >
>