On Fri, 16 Oct 2026, Paavan Indela wrote:

> On Fri, Oct 16, 2026, Richard Biener wrote:
> 
> It must be after IPA since it otherwise may use the wrong target when
> optimizing functions marked for offloading.
> 
> Agreed. The revised version keeps contraction in widening_mul, after IPA
> and vectorization.
> 
> Also allowing early inlining but not IPA is odd and feels like a heuristic
> specifically tuned to the problem. As you do want to form FMA across calls
> I don't think the issue can be solved without annotation with for example
> __builtin_assoc_barrier
> 
> Agreed, so the boundary is now an annotation. Revised proposal:
> 
> -ffp-contract=local fuses a * b + c only when the multiply and the add come
> from the same function, counting always_inline callees as part of their
> caller. No other call boundary is crossed, whether or not the call gets
> inlined. x86 intrinsics are already always_inline; wrapper types mark their
> operators. __builtin_assoc_barrier still opts out locally.
> 
> The prototype does this in convert_mult_to_fma:
> 
> - the scope of a statement is its innermost inlined-function block that is
> not always_inline (BLOCK_ORIGIN; these blocks are kept at -g0, so -g does
> not change the result);
> - a use in a dominated block qualifies, not only the same block, so a call
> inlined between the multiply and the add changes nothing;
> - loop-carried values stay unfused, so vector and scalar code agree.
> 
> One case still leaks: after IPA inlining, FRE can merge an identical a * b
> from the caller and the callee into one multiply that spans both scopes, so
> neither side fuses. Is that acceptable as a documented limitation, or would
> you handle it differently?

Hmm, so what you actually want is -ffp-contract=standard to also
cover expressions using intrinsics?  I think that's a reasonable
extension and could possibly be achieved with a target hook
helping to identify plus/mult intrinsic calls?  So I'd solve this
particular issue from within the frontend paths implementing
-ffp-contract=standard instead.

Richard.


> Thanks,
> Paavan Kumar
> 
> On Fri, Oct 16, 2026 at 4:14 PM Richard Biener <[email protected]> wrote:
> 
> >
> >
> > Am 16.10.2026 um 12:39 schrieb paavan kumar via Gcc <[email protected]>:
> >
> > 
> >
> > Hi,
> >
> > Before writing a patch, I'd like to know if an opt-in mode like this would
> > be welcome.
> >
> > With -ffp-contract=fast, whether a * b + c gets fused depends on IPA
> > inlining, so unrelated code can change the result of a function that didn't
> > change. Small example (trunk, -O2 -mfma):
> >
> >   static double
> >   scale (const double *w, double a, double b)
> >   {
> >     double k = 0;
> >     for (int i = 0; i < 16; i++)
> >       k += w[i] * w[15 - i] + w[(i * 5) & 15] / (1.0 + w[i]);
> >     if (k == 12345.0)
> >       b = k;
> >     return a * b;
> >   }
> >
> >   __attribute__ ((noipa)) double
> >   f (const double *w, double a, double b, double c)
> >   {
> >     return scale (w, a, b) + c;
> >   }
> >
> > f (w, 1 + 0x1p-27, 1 - 0x1p-27, -1) returns -0x1p-54. Add an unrelated
> > second caller of scale and it returns 0 (see also PR106902, PR126195). We
> > keep hitting this in a large C++ code base: a header change flips a vfmadd
> > in code it doesn't touch, and results differ between builds that should
> > match.
> >
> > -ffp-contract=on fixes the determinism but only fuses within one scalar
> > expression. It can't see through operators of wrapper types or intrinsics
> > like _mm256_add_pd (_mm256_mul_pd (a, b), c), so that kind of code loses
> > most of its FMAs.
> >
> > *Proposal*: -ffp-contract=local. Form FMAs in a small pass at the end of
> > the early optimizations (after early inlining, before IPA), reusing
> > convert_mult_to_fma. No other pass contracts in this mode. Fusion then
> > depends only on the function and what early inlining put into it, not on
> > later inlining, cloning, LTO or unrelated code. In the example f returns 0
> > either way.
> >
> > Not fused: values carried around a loop (see below) and operations that
> > only appear after IPA (complex lowering, vectorizer). Opt out per function
> > with optimize ("fp-contract=off"). Not a guarantee across GCC versions.
> >
> > Numbers from a prototype (i5-13420H, -O3 -march=haswell, ns/element):
> >
> >                               off     on   fast  local
> >   x = x * a + b             1.850  1.060  1.060  1.060
> >   dot, 1 accumulator        0.520  1.040  0.520  0.520
> >   dot, 8 accumulators       0.165  0.136  0.135  0.165
> >   Horner, double            0.430  0.250  0.250  0.250
> >   Horner, wrapper struct    0.420  0.420  0.240  0.240
> >
> > Richard, you've said FMAs belong after vectorization, and the dot rows
> > show why. fast vectorizes the 1-accumulator dot as vector multiplies plus
> > in-order scalar adds. An .FMA in that chain can't be split (PR120823), so
> > it stays a scalar FMA chain at 2x; on pays that today. So the prototype
> > doesn't fuse into loop-carried values, which in turn costs the
> > 8-accumulator case that fast fuses after vectorization. Deciding before
> > vectorization means picking one.
> >
> > Also open: targets with scalar but no vector FMA (maybe contract early
> > only when the vector FMA exists too), and size heuristics moving with early
> > .FMA (at -O2 an inner Horner loop was fully unrolled under local but not
> > under fast).
> >
> > *Questions*:
> >
> > 1. Is an opt-in mode like this acceptable, or would you solve it
> > differently?
> > 2. Is not fusing loop-carried values the right default?
> > 3. Is the end of the early optimizations the right place (it must be
> > before pass_split_functions)?
> >
> > It must be after IPA since it otherwise may use the wrong target when
> > optimizing functions marked for offloading.
> >
> > Also allowing early inlining but not IPA is odd and feels like a heuristic
> > specifically tuned to the problem.  As you do want to form FMA across calls
> > I don’t think the issue can be solved without annotation with for example
> > __builtin_assoc_barrier
> >
> > Richard
> >
> > 4. Name: local, early, stable?
> >
> > Thanks,
> > Paavan Kumar
> >
> >
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to