On Fri, 28 Aug 2026 at 15:12, Richard Biener <[email protected]>
wrote:

> On Thu, Aug 27, 2026 at 2:30 PM Konstantinos Eleftheriou
> <[email protected]> wrote:
> >
> > Hi all, thanks for the feedback!
> >
> > In case of a libcall we can't calculate the cost of the emitted
> instructions,
> > so I think that we should favour the inline expansion when optimizing
> > for speed, as it at least avoids the call.
> >
> > In case that there is an optab, we could compare its cost against our
> > inline expansion rather than preferring it outright.
> >
> > How does that sound?
>
> The question is how does this detect that AVR expands to a libcall that
> isn't a libcall ...
>

Does if even need to?
avr_rtx_costs already provides the information: For mult:SI at speed it
returns COSTS_N_INSNS (300) when !AVR_HAVE_MUL, against COSTS_N_INSNS (25)
when the multiplier exists. So the gate should drop the optab test entirely
and compare costs.  I believe that this is Johann's point, that
TARGET_RTX_COSTS
is the mechanism for choosing between equivalent forms.

>From my reading, AVR only prices SI/DI multiplies at speed; QI/HI return
false and fall back to the generic factor * factor * COSTS_N_INSNS(5).
This looks cheap even though *mulhi3_call is a call too. @Johann, is that
deliberate?


> > On Wed, Aug 26, 2026 at 5:01 PM Richard Biener <
> [email protected]> wrote:
> >>
> >> On Wed, Aug 26, 2026 at 11:42 AM Georg-Johann Lay <[email protected]> wrote:
> >> >
> >> > Am 26.08.26 um 09:43 schrieb Konstantinos Eleftheriou:
> >> > > From: Philipp Tomsich <[email protected]>
> >> > >
> >> > > lower_long_mul_high_chain lowered every recognized high-part chain
> the
> >> > > target cannot form a narrow high part for, including chains whose
> wide
> >> > > multiply expands just fine. On AVR without hardware multiply this
> turned
> >> > > a single __mulsi3 call into four __mulhi3 calls plus carry
> propagation
> >> > > at -Os.
> >> > >
> >> > > Lower only where the inline longhand is the better code. Optimizing
> for
> >> > > size a single wide multiply is smaller either way, so never lower.
> With
> >> > > a multiply insn for the wide mode the target has asked for the wide
> form
> >> > > even when optimizing for speed: targets without a hardware multiply
> >> >
> >> > Hi Konstantinos,
> >> >
> >> > just the fact that the target provides an insn doesn't mean it is
> asking
> >> > for something, or that the insn should be preferred over different but
> >> > equivalent expansions.
> >> >
> >> > It just means that the target is better of providing the insn than not
> >> > providing it.  That's all.
> >>
> >> That's true.  And for AVR it's kind-of extreme, for good enough reasons.
> >>
> >> > For example, the avr libgcc implements many helper function in asm,
> >> > and the compiler provides insns that model the exact register
> footprint.
> >> > This is profitable since many asm functions don't clobber all callee
> >> > clobbered registers, and the insn is there to express the reduced
> >> > register footprint.  For example, a mulhi3 may look like:
> >> >
> >> > (define_insn "*mulhi3_call"
> >> >    [(set (reg:HI 24)
> >> >          (mult:HI (reg:HI 24)
> >> >                   (reg:HI 22)))
> >> >     (clobber (reg:HI 22))
> >> >     (clobber (reg:QI 21))
> >> >     (clobber (reg:CC REG_CC))]
> >> >    "!AVR_HAVE_MUL && reload_completed"
> >> >    "%~call __mulhi3"
> >> >    [(set_attr "type" "xcall")])
> >> >
> >> > This is exactly what a vanilla libcall insn would do, just with
> >> > a reduced register footprint.  (Missed tail calls are optimized
> >> > by linker relaxation.)
> >> >
> >> > In order to select which of many alternative representations of some
> >> > arithmetic should be used, the targets are providing cost functions
> >> > like targetm.rtx_costs / TARGET_RTX_COSTS.
> >>
> >> Checking rtx_cost on GIMPLE is a bit elaborate (see IVOPTs), so
> >> short of applying heuristics (> word_mode operations are unlikely
> cheap),
> >> we might want to provide a helper computing optab cost for some mode(s)
> >> and that applies some caching (because creating scratch RTL for costing
> >> is costly).  We for example have lshift_cheap_p which does this for
> >> a single optab (with a FIXME).
> >>
> >> > To make thinks even worse, the back end has to hack around madness
> >> > like https://gcc.gnu.org/PR118012 from the tree optimizers that
> >> > use a multiplication -- even on targets that don't have MUL -- to
> >> > perform a single bit test!  avr tries to hack around that my providing
> >> > combiner patterns that try to "roll back" such completely crazy code.
> >> > Just search for 118012 in the avr back end.
> >> >
> >> > Johann
> >> >
> >> > > expose call-bundle patterns (AVR's mulsi3) precisely to route such
> >> > > multiplies to their hand-written helpers. What is left is a libcall
> at
> >> > > speed, where inlining saves only the call, since the callee
> performs the
> >> > > same longhand.
> >> > >
> >> > > A wide mode with no expansion path at all is unaffected: such a
> chain
> >> > > has to be lowered at every optimization level, or it reaches expand
> as
> >> > > a multiply the target cannot expand.
> >> > >
> >> > > Bootstrapped and regtested on AArch64, x86-64 and ARM.
> >> > > Regtested on AVR.
> >> > >
> >> > >       PR tree-optimization/127028
> >> > >
> >> > > gcc/ChangeLog:
> >> > >
> >> > >       * tree-ssa-math-opts.cc (long_mul_lowering_profitable_p): New.
> >> > >       (lower_long_mul_high_chain): Call it when the wide mode can
> >> > >       expand.
> >> > >
> >> > > gcc/testsuite/ChangeLog:
> >> > >
> >> > >       * gcc.target/avr/pr127028.c: New test.
> >> > >
> >> > > Co-authored-by: Konstantinos Eleftheriou <
> [email protected]>
> >> > > ---
> >> > >   gcc/testsuite/gcc.target/avr/pr127028.c | 12 ++++++++++
> >> > >   gcc/tree-ssa-math-opts.cc               | 29
> +++++++++++++++++++++++++
> >> > >   2 files changed, 41 insertions(+)
> >> > >   create mode 100644 gcc/testsuite/gcc.target/avr/pr127028.c
> >> > >
> >> > > diff --git a/gcc/testsuite/gcc.target/avr/pr127028.c
> b/gcc/testsuite/gcc.target/avr/pr127028.c
> >> > > new file mode 100644
> >> > > index 000000000000..e183d02f052d
> >> > > --- /dev/null
> >> > > +++ b/gcc/testsuite/gcc.target/avr/pr127028.c
> >> > > @@ -0,0 +1,12 @@
> >> > > +/* { dg-do compile } */
> >> > > +/* { dg-options "-Os" } */
> >> > > +
> >> > > +typedef __UINT16_TYPE__ uint16_t;
> >> > > +typedef __UINT32_TYPE__ uint32_t;
> >> > > +
> >> > > +uint16_t wmul16 (uint16_t x)
> >> > > +{
> >> > > +  return ((uint32_t) x * 0xaaab) >> 16;
> >> > > +}
> >> > > +
> >> > > +/* { dg-final { scan-assembler-not "__mulhi3" } } */
> >> > > diff --git a/gcc/tree-ssa-math-opts.cc b/gcc/tree-ssa-math-opts.cc
> >> > > index ebc3300ae332..8288fbead751 100644
> >> > > --- a/gcc/tree-ssa-math-opts.cc
> >> > > +++ b/gcc/tree-ssa-math-opts.cc
> >> > > @@ -7101,6 +7101,18 @@ narrow_long_mul_halves (gimple *stmt)
> >> > >     return true;
> >> > >   }
> >> > >
> >> > > +/* True when the inline longhand beats expanding the WIDE_MODE
> multiply.
> >> > > +   At size the wide multiply is smaller either way.  At speed, a
> multiply
> >> > > +   pattern is the target's own expansion -- AVR defines mulsi3
> even on
> >> > > +   cores with no multiply at all -- while a libcall is the generic
> fallback
> >> > > +   whose body is that same longhand, so inlining saves only the
> call.  */
> >> > > +
> >> > > +static bool
> >> > > +long_mul_lowering_profitable_p (scalar_int_mode wide_mode, bool
> speed)
> >> > > +{
> >> > > +  return speed && optab_handler (smul_optab, wide_mode) ==
> CODE_FOR_nothing;
> >> > > +}
> >> > > +
> >> > >   /* Match.pd recognizer for the long-multiply recognizer's
> high-part
> >> > >      emit chain.  */
> >> > >
> >> > > @@ -7148,6 +7160,23 @@ lower_long_mul_high_chain
> (gimple_stmt_iterator *gsi)
> >> > >     if (can_mult_highpart_p (narrow_mode, true))
> >> > >       return false;
> >> > >
> >> > > +  /* Where the 2N multiply expands, lowering is a trade, not a
> rescue, so
> >> > > +     leave the chain alone unless the longhand is the better
> code.  A 2N
> >> > > +     mode with no expansion path must be lowered regardless.  */
> >> > > +  tree wide_type = TREE_TYPE (gimple_assign_lhs (mult_stmt));
> >> > > +  scalar_int_mode wide_mode;
> >> > > +  bool speed = optimize_bb_for_speed_p (gimple_bb (trunc_stmt));
> >> > > +  if (is_a <scalar_int_mode> (TYPE_MODE (wide_type), &wide_mode)
> >> > > +      && targetm.scalar_mode_supported_p (wide_mode)
> >> > > +      && !long_mul_lowering_profitable_p (wide_mode, speed))
> >> > > +    {
> >> > > +      if (dump_file && (dump_flags & TDF_DETAILS))
> >> > > +     fprintf (dump_file, "Not lowering long-mul chain: %s.\n",
> >> > > +              speed ? "the target expands the wide multiply"
> >> > > +                    : "the wide multiply is smaller");
> >> > > +      return false;
> >> > > +    }
> >> > > +
> >> > >     location_t loc = gimple_location (trunc_stmt);
> >> > >     gimple_seq seq = NULL;
> >> > >
> >> >
>

Reply via email to