On Fri, Aug 28, 2026 at 3:26 PM Philipp Tomsich
<[email protected]> wrote:
>
>
>
> 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.

Well, OK.  So yes, this sounds good.  But then if it's libcall vs.
inline exapand
with large cost as you say you cannot cost the libcall, so you can't see that
it might be N "AVR-style libcalls" vs. one "regular libcall".  Unless
there's some
hook that allows to determine sth like "libcall cost"?

> 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