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
>

Reply via email to