On 6/18/2026 6:32 PM, [email protected] wrote:
> From: MITSUNARI Shigeo <[email protected]>
>
> For 32-bit unsigned integer division by constants that require a wider
> magic multiplier (mh != 0), use a pre-shifted magic constant in a mode
> twice as wide and a single high-part multiply instead of the traditional
> sub/shift/add sequence, when that is no more expensive.
>
> The (size+1)-bit magic constant (2^size + ml) is pre-shifted by
> (size - post_shift) bits so that the quotient is obtained directly from
> the high part of the widened multiply, then truncated back to the
> original mode. The widened sequence is only used when its cost, measured
> with seq_cost, does not exceed that of the classic sub/shift/add sequence.
>
> This reduces the instruction count for divisions like x/7 from 7
> instructions to 4 on x86_64.
>
> Before (x / 7):
> movl %edi, %eax
> imulq $613566757, %rax, %rax
> shrq $32, %rax
> subl %eax, %edi
> shrl %edi
> addl %edi, %eax
> shrl $2, %eax
>
> After:
> movabsq $2635249153617166336, %rcx
> movl %edi, %eax
> mulq %rcx
> movl %edx, %eax
>
> gcc/ChangeLog:
>
> * expmed.cc (expand_wide_mulh_udiv): New function.
> (expand_divmod): Use it for unsigned constant division needing a
> wide multiplier on 64-bit targets, when no more expensive than the
> sub/shift/add sequence.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/i386/mulq-highpart.c: New test.
>
> Signed-off-by: MITSUNARI Shigeo <[email protected]>
> ---
> gcc/expmed.cc | 66 +++++++++++++++++++
> gcc/testsuite/gcc.target/i386/mulq-highpart.c | 22 +++++++
> 2 files changed, 88 insertions(+)
> create mode 100644 gcc/testsuite/gcc.target/i386/mulq-highpart.c
>
> diff --git a/gcc/expmed.cc b/gcc/expmed.cc
> index d57ea78d6b1..84a7a886eca 100644
> --- a/gcc/expmed.cc
> +++ b/gcc/expmed.cc
> @@ -4258,6 +4258,61 @@ expand_sdiv_pow2 (scalar_int_mode mode, rtx op0,
> HOST_WIDE_INT d)
> (x mod 12) == (((x & 1023) + ((x >> 8) & ~3)) * 0x15555558 >> 2 * 3) >>
> 28
> */
>
> +/* Helper for expand_divmod's unsigned constant division. For OP0 in
> + INT_MODE divided by a constant needing a (SIZE+1)-bit multiplier ML
> + with right shift POST_SHIFT (the mh != 0 case), try to obtain
> + the quotient from the high part of a single multiply in a mode twice
> + as wide as INT_MODE. Return the quotient in INT_MODE, having emitted
> + the insns, or NULL_RTX when the transformation is unavailable or not
> + cheaper than the classic sub/shift/add sequence. EXTRA_COST is the
> + cost of that sequence's follow-up ops, MAX_COST bounds the multiply
> + and SPEED selects the cost model.
> +
> + The magic constant occupies at most 2*SIZE bits and so must fit in a
> + HOST_WIDE_INT (always 64 bits today; checked below). A wider INT_MODE
> + such as DImode -- which would need a 128-bit magic and a single-word
> + high-part multiply in a 2x-wide mode that common targets lack -- is
> + therefore excluded. */
> +
> +static rtx
> +expand_wide_mulh_udiv (scalar_int_mode int_mode, rtx op0,
> + unsigned HOST_WIDE_INT ml, int size, int post_shift,
> + int extra_cost, int max_cost, bool speed)
> +{
> + scalar_int_mode wide_mode;
> +
> + /* We need POST_SHIFT >= 1, a wider integer mode that still fits in a
> + word, and the pre-shifted magic constant to fit in a HOST_WIDE_INT. */
> + if (post_shift < 1
> + || !GET_MODE_2XWIDER_MODE (int_mode).exists (&wide_mode)
> + || GET_MODE_BITSIZE (wide_mode) > BITS_PER_WORD
> + || GET_MODE_BITSIZE (wide_mode) > HOST_BITS_PER_WIDE_INT)
> + return NULL_RTX;
So the LLMs both flagged the potential for post_shift to be large enough
to cause problems. So for example if post_shift > size, then we end up
with a negative shift count when computing MAGIC. That may not be
possible and I'm not as familiar with this code as perhaps I should be,
so if you chase down if this is a real issue or not and take appropriate
action, it'd be appreciated. And just to be clear if size can never be
greater than post_shift, then there's nothing to do for this "issue".
It's not a correctness issue, but some targets to have highpart
multipliers so there may be ways to handle cases where WIDE_MODE is
wider than a word. But that's (IMHO) something we could tackle in a
follow-up if we were so inclined.
> +
> + /* Pre-shift the (SIZE+1)-bit magic constant (2^SIZE + ml) by
> + (SIZE - POST_SHIFT) so that the quotient ends up in the high part
> + of the widened product. */
> + unsigned HOST_WIDE_INT magic
> + = ((HOST_WIDE_INT_1U << size) + ml) << (size - post_shift);
Closely related, one flagged this as potentially overflowing depending
on size, so we need to look at that more carefully.
> @@ -4532,6 +4587,16 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> = (shift_cost (speed, int_mode, post_shift - 1)
> + shift_cost (speed, int_mode, 1)
> + 2 * add_cost (speed, int_mode));
> +
> + /* Try a single widened multiply first; use it when
> + it is no more expensive. */
> + quotient
> + = expand_wide_mulh_udiv (int_mode, op0, ml, size,
> + post_shift, extra_cost,
> + max_cost, speed);
> + if (quotient != NULL_RTX)
> + goto mh_done;
> +
> t1 = expmed_mult_highpart
> (int_mode, op0, gen_int_mode (ml, int_mode),
> NULL_RTX, 1, max_cost - extra_cost);
> @@ -4548,6 +4613,7 @@ expand_divmod (int rem_flag, enum tree_code code,
> machine_mode mode,
> quotient = expand_shift
> (RSHIFT_EXPR, int_mode, t4,
> post_shift - 1, tquotient, 1);
> + mh_done:;
Rather than a goto, can we
if (quotient == NULL_RTX)
{
all the code your patch skipped
}
THat does mean you'd need to reindent that block of code, but that's
better than a goto. It'll make review harder, but we've worked through
all the issues I believe so the final review should be a rubber stamp at
this point.
Otherwise it looks good to me.
Jeff