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?

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