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