On Tue, Sep 1, 2026 at 10:20 AM Georg-Johann Lay <[email protected]> wrote:
>
> Am 28.08.26 um 15:49 schrieb Richard Biener:
> > 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"?
>
> As far as I know, these libcalls are introduced at expand / rtl
> lowering.  The lowering routines query the backend for rtx costs and
> chose the arithmetic according to the costs (provided there is more than
> one way).  When that arithmetic is expanded, it may be represented as a
> call or as (a sequence of) insn(s).
>
> So the backend never sees a libcall in rtx_costs but only the very
> arithmetic it will be performing.  The backend knows whether some
> operation will be emit as libcall or not, and knows the costs.

True.  But the above says that if the original code expands to a libcall
(aka, there is no optab for the requested mode), we can assume that's
always more costly than the rtx_cost of the inline sequence.  Because,
as said, there's no way to cost the unsupported operation (or rather,
I would not expect backends to assign a cost to, say, a TImode MULT
when it has no expander to deal with that).

> So the tree optimizers don't need to bother about how expand will
> express some arithmetic.
>
> Getting rtx costs in tree passes might be expensive and requires extra
> attention of the author, but that's true for many other optimizations:
> There's no free lunch.
>
> As an aside, it's still beyond me why tree code that uses 300 cycles to
> test a single bit (PR118012 etc) is still there and nothing being done
> about it (except flawed attempts to hack around it in affected
> backends).  Or just reverted such code that contributes to bad GCC
> reputation.

I have seen no concrete patch proposal or call for reversion.  It's like
with the many bugs (and regressions) we have, they are addressed
according to peoples priorities.  And this is also obviously an area
where a trivial solution is not present (and IMO we need something
sound design-wise), and people have different ideas where a solution
should be.

Richard.

>
> >>  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