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
>