Hi,

Following Richard's suggestion in [1], here is a proposal for function
attributes that let -ffp-contract=on contract through calls to intrinsics
and to the operators of floating-point wrapper types.

*Motivation*

-ffp-contract=on contracts a * b + c within one source expression, so
unlike -ffp-contract=fast the result does not depend on inlining. But in

  _mm256_add_pd (_mm256_mul_pd (a, b), c)

or, with a wrapper type such as vectorclass's Vec4d or a struct around a
double,

  a * b + c    /* operator+ (operator* (a, b), c) */

the operations are calls, so nothing is contracted.

*Proposal*

Three function attributes, usable as __attribute__ or [[gnu::]]:

  fp_contractable_add
  fp_contractable_sub
  fp_contractable_mul

They declare that the function returns the sum, difference or product of
its two arguments, computed as the built-in operator would. With
-ffp-contract=on, a call to such a function counts as that operator when
looking for +-(x * y) +- z within an expression, so the expression is
contracted into an FMA. The body is not examined: like const, the attribute
is a promise. It implies const and nothrow.

The attribute is ignored with a warning unless:

- the function is not a non-static member function;
- it has exactly two parameters, passed by value, and a return value, all
of the same type T (ignoring cv-qualifiers);
- T is a real floating type, a vector of one, or a trivially copyable
structure or class whose only non-static data member has such a type. For
such a structure the operation applies to that member.

Not covered: compound assignment operators (+=, -=), which modify an
argument and so can't be const; unary minus; mixed operands such as Vec4d *
double. With -ffp-contract=off or fast the attributes change nothing beyond
implying const and nothrow.

GCC's own intrinsic headers would use the attributes, so no target hook is
needed. For x86 that is the packed _mm{,256,512}_{add,sub,mul}_p{s,d};
other targets can mark their equivalents, such as NEON's vaddq_f32.

*Example*:

  struct num { double v; };

  [[gnu::fp_contractable_mul]] inline num
  operator* (num a, num b) { return { a.v * b.v }; }

  [[gnu::fp_contractable_add]] inline num
  operator+ (num a, num b) { return { a.v + b.v }; }

  /* .FMA with -ffp-contract=on.  */
  num f (num a, num b, num c) { return a * b + c; }

*Implementation*

A prototype does this next to the existing -ffp-contract=on code in
c-gimplify.cc: calls to such functions are matched like PLUS_EXPR,
MINUS_EXPR and MULT_EXPR, looking through C++ TARGET_EXPR copies, and for a
structure the .FMA works on the member and the result is converted back.
cp_gimplify_expr sends CALL_EXPRs there too. It also extends
-ffp-contract=on to vector float types, which it skips today.

I'd send three patches: -ffp-contract=on for vector types, the attributes
for C and C++, and the x86 intrinsic headers.

*Questions*:

1. Are the names, and the placement in c-family, acceptable?
2. Are the type rules right? In particular, should parameters by const
reference be allowed, and should non-static member operators (with *this as
the left operand) be covered?
3. Should a declaration that doesn't fit get a warning, as in the
prototype, or an error?

[1] https://gcc.gnu.org/pipermail/gcc/2026-October/249010.html

Thanks
Paavan Kumar

Reply via email to