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)
