https://github.com/koparasy updated 
https://github.com/llvm/llvm-project/pull/215382

>From 92c7b5cb675bf8330f061b76097d1015800054cf Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Mon, 10 Aug 2026 11:52:06 -0700
Subject: [PATCH 1/4] [CIR][CodeGen] Emit cir.fmuladd for FP-contracted
 mul+add/sub

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  89 ++++++
 .../CIR/CodeGen/fp-contract-on-pragma.cpp     | 122 +++++++++
 clang/test/CIR/CodeGen/fp-contract-pragma.cpp | 258 ++++++++++++++++++
 clang/test/CIR/CodeGen/fp-contract.c          |  86 ++++++
 4 files changed, 555 insertions(+)
 create mode 100644 clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp
 create mode 100644 clang/test/CIR/CodeGen/fp-contract-pragma.cpp
 create mode 100644 clang/test/CIR/CodeGen/fp-contract.c

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 8d660a0a2c721..f3e8e646c3c07 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1948,6 +1948,89 @@ static bool isIntegerVectorBinOp(mlir::Type ty) {
   return vecTy && mlir::isa<cir::IntType>(vecTy.getElementType());
 }
 
+// Construct a cir.fmuladd op to represent a fused mul-add of `mulOp` and
+// `addend`. Use negMul and negAdd to negate the first operand of the mul or
+// the addend respectively. This allows fmuladd to represent a*b-c, or c-a*b.
+// Patterns in LLVM should catch the negated forms and translate them to
+// efficient operations.
+static mlir::Value buildFMulAdd(cir::FMulOp mulOp, mlir::Value addend,
+                                CIRGenBuilderTy &builder, bool negMul,
+                                bool negAdd) {
+  const mlir::Location loc = mulOp.getLoc();
+  mlir::Value mulOp0 = mulOp.getLhs();
+  mlir::Value mulOp1 = mulOp.getRhs();
+  if (negMul)
+    mulOp0 = builder.createFNeg(loc, mulOp0);
+  if (negAdd)
+    addend = builder.createFNeg(loc, addend);
+
+  mlir::Value fmuladd =
+      cir::FMulAddOp::create(builder, loc, addend.getType(), mulOp0, mulOp1,
+                             addend, builder.getConstrainedFPAttr());
+  mulOp.erase();
+  return fmuladd;
+}
+
+// Check whether it would be legal to emit a cir.fmuladd op to represent op
+// and if so, build it.
+//
+// Checks that (a) the operation is fusable, and (b) -ffp-contract=on.
+// Does NOT check the type of the operation - it's assumed that this function
+// will be called from contexts where it's known that the type is contractable.
+static mlir::Value tryEmitFMulAdd(const BinOpInfo &op, CIRGenBuilderTy 
&builder,
+                                  bool isSub = false) {
+  assert((op.opcode == BO_Add || op.opcode == BO_AddAssign ||
+          op.opcode == BO_Sub || op.opcode == BO_SubAssign) &&
+         "Only fadd/fsub can be the root of an fmuladd.");
+
+  // Check whether this op is marked as fusable.
+  if (!op.fpFeatures.allowFPContractWithinStatement())
+    return nullptr;
+
+  mlir::Value lhs = op.lhs;
+  mlir::Value rhs = op.rhs;
+
+  // Peek through fneg to look for fmul. Make sure the fneg has no other users,
+  // and that it is the only use of its operand.
+  bool negLHS = false;
+  if (auto lhsNeg = lhs.getDefiningOp<cir::FNegOp>()) {
+    if (lhsNeg.getResult().use_empty() && lhsNeg.getInput().hasOneUse()) {
+      lhs = lhsNeg.getInput();
+      negLHS = true;
+    }
+  }
+
+  bool negRHS = false;
+  if (auto rhsNeg = rhs.getDefiningOp<cir::FNegOp>()) {
+    if (rhsNeg.getResult().use_empty() && rhsNeg.getInput().hasOneUse()) {
+      rhs = rhsNeg.getInput();
+      negRHS = true;
+    }
+  }
+
+  // We have a potentially fusable op. Look for a mul on one of the operands.
+  // Also make sure that the mul result isn't used directly. In that case,
+  // there's no point creating a muladd operation.
+  if (auto lhsMul = lhs.getDefiningOp<cir::FMulOp>()) {
+    if (lhsMul.getResult().use_empty() || negLHS) {
+      // If we looked through fneg, erase it.
+      if (negLHS)
+        op.lhs.getDefiningOp<cir::FNegOp>().erase();
+      return buildFMulAdd(lhsMul, op.rhs, builder, negLHS, isSub);
+    }
+  }
+  if (auto rhsMul = rhs.getDefiningOp<cir::FMulOp>()) {
+    if (rhsMul.getResult().use_empty() || negRHS) {
+      // If we looked through fneg, erase it.
+      if (negRHS)
+        op.rhs.getDefiningOp<cir::FNegOp>().erase();
+      return buildFMulAdd(rhsMul, op.lhs, builder, isSub ^ negRHS, false);
+    }
+  }
+
+  return nullptr;
+}
+
 mlir::Value ScalarExprEmitter::emitMul(const BinOpInfo &ops) {
   const mlir::Location loc = cgf.getLoc(ops.loc);
   if (!isIntegerVectorBinOp(ops.lhs.getType()) &&
@@ -2047,6 +2130,9 @@ mlir::Value ScalarExprEmitter::emitAdd(const BinOpInfo 
&ops) {
 
   if (cir::isFPOrVectorOfFPType(ops.lhs.getType())) {
     CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, ops.fpFeatures);
+    // Try to form an fmuladd.
+    if (mlir::Value fmuladd = tryEmitFMulAdd(ops, builder))
+      return fmuladd;
     return builder.createFAdd(loc, ops.lhs, ops.rhs);
   }
 
@@ -2095,6 +2181,9 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo 
&ops) {
 
     if (cir::isFPOrVectorOfFPType(ops.lhs.getType())) {
       CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, ops.fpFeatures);
+      // Try to form an fmuladd.
+      if (mlir::Value fmuladd = tryEmitFMulAdd(ops, builder, /*isSub=*/true))
+        return fmuladd;
       return builder.createFSub(loc, ops.lhs, ops.rhs);
     }
 
diff --git a/clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp 
b/clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp
new file mode 100644
index 0000000000000..2c04dcfd6afc3
--- /dev/null
+++ b/clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp
@@ -0,0 +1,122 @@
+// ClangIR port of clang/test/CodeGen/fp-contract-on-pragma.cpp.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -emit-llvm %s -o %t-ogcg.ll
+// RUN: FileCheck --input-file=%t-ogcg.ll %s -check-prefix=OGCG
+
+// Is FP_CONTRACT honored in a simple case?
+float fp_contract_1(float a, float b, float c) {
+#pragma clang fp contract(on)
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_1fff
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmul
+// LLVM-LABEL: @_Z13fp_contract_1fff
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z13fp_contract_1fff
+// OGCG: call float @llvm.fmuladd.f32
+
+// Is FP_CONTRACT state cleared on exiting compound statements?
+float fp_contract_2(float a, float b, float c) {
+  {
+#pragma clang fp contract(on)
+  }
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_2fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z13fp_contract_2fff
+// LLVM: %[[M:.*]] = fmul float
+// LLVM: fadd float %[[M]],
+// OGCG-LABEL: @_Z13fp_contract_2fff
+// OGCG: %[[M:.*]] = fmul float
+// OGCG: fadd float %[[M]],
+
+// Does FP_CONTRACT survive template instantiation?
+class Foo {};
+Foo operator+(Foo, Foo);
+
+template <typename T>
+T template_muladd(T a, T b, T c) {
+#pragma clang fp contract(on)
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z15template_muladdIfET_S0_S0_S0_
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
+// OGCG: call {{.*}}float @llvm.fmuladd.f32
+
+// fp_contract_3 is just a caller; the fused op lives in the instantiated
+// template_muladd checked above. It is emitted in a different order under the
+// classic CodeGen path, so it carries no checks of its own here.
+float fp_contract_3(float a, float b, float c) {
+  return template_muladd<float>(a, b, c);
+}
+
+template <typename T>
+class fp_contract_4 {
+  float method(float a, float b, float c) {
+#pragma clang fp contract(on)
+    return a * b + c;
+  }
+};
+template class fp_contract_4<int>;
+// CIR-LABEL: cir.func {{.*}}@_ZN13fp_contract_4IiE6methodEfff
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_ZN13fp_contract_4IiE6methodEfff
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_ZN13fp_contract_4IiE6methodEfff
+// OGCG: call float @llvm.fmuladd.f32
+
+// Check file-scoped FP_CONTRACT
+#pragma clang fp contract(on)
+float fp_contract_5(float a, float b, float c) {
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_5fff
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z13fp_contract_5fff
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z13fp_contract_5fff
+// OGCG: call float @llvm.fmuladd.f32
+
+#pragma clang fp contract(off)
+float fp_contract_6(float a, float b, float c) {
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_6fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z13fp_contract_6fff
+// LLVM: %[[M:.*]] = fmul float
+// LLVM: fadd float %[[M]],
+// OGCG-LABEL: @_Z13fp_contract_6fff
+// OGCG: %[[M:.*]] = fmul float
+// OGCG: fadd float %[[M]],
+
+// If the multiply has multiple uses, don't produce fmuladd.
+// This used to assert (PR25719):
+// https://llvm.org/bugs/show_bug.cgi?id=25719
+float fp_contract_7(float a, float b, float c) {
+#pragma clang fp contract(on)
+  return (a = 2 * b) - c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_7fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fsub %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z13fp_contract_7fff
+// LLVM: %[[M:.*]] = fmul float
+// LLVM: fsub float %[[M]],
+// OGCG-LABEL: @_Z13fp_contract_7fff
+// OGCG: %[[M:.*]] = fmul float
+// OGCG: fsub float %[[M]],
diff --git a/clang/test/CIR/CodeGen/fp-contract-pragma.cpp 
b/clang/test/CIR/CodeGen/fp-contract-pragma.cpp
new file mode 100644
index 0000000000000..6819f150205fe
--- /dev/null
+++ b/clang/test/CIR/CodeGen/fp-contract-pragma.cpp
@@ -0,0 +1,258 @@
+// ClangIR port of clang/test/CodeGen/fp-contract-pragma.cpp.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -emit-llvm %s -o %t-ogcg.ll
+// RUN: FileCheck --input-file=%t-ogcg.ll %s -check-prefix=OGCG
+
+// Is FP_CONTRACT honored in a simple case?
+float fp_contract_1(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_1fff
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmul
+// LLVM-LABEL: @_Z13fp_contract_1fff
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z13fp_contract_1fff
+// OGCG: call float @llvm.fmuladd.f32
+
+// Is FP_CONTRACT state cleared on exiting compound statements?
+float fp_contract_2(float a, float b, float c) {
+  {
+  #pragma STDC FP_CONTRACT ON
+  }
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_2fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z13fp_contract_2fff
+// LLVM: %[[M:.*]] = fmul float
+// LLVM: fadd float %[[M]],
+// OGCG-LABEL: @_Z13fp_contract_2fff
+// OGCG: %[[M:.*]] = fmul float
+// OGCG: fadd float %[[M]],
+
+// Does FP_CONTRACT survive template instantiation?
+class Foo {};
+Foo operator+(Foo, Foo);
+
+template <typename T>
+T template_muladd(T a, T b, T c) {
+  #pragma STDC FP_CONTRACT ON
+  return a * b + c;
+}
+// The fmuladd is emitted in the instantiated template body.
+// CIR-LABEL: cir.func {{.*}}@_Z15template_muladdIfET_S0_S0_S0_
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
+// OGCG: call {{.*}}float @llvm.fmuladd.f32
+
+// fp_contract_3 is just a caller; the fused op lives in the instantiated
+// template_muladd checked above. It is emitted in a different order under the
+// classic CodeGen path, so it carries no checks of its own here.
+float fp_contract_3(float a, float b, float c) {
+  return template_muladd<float>(a, b, c);
+}
+
+template<typename T> class fp_contract_4 {
+  float method(float a, float b, float c) {
+    #pragma STDC FP_CONTRACT ON
+    return a * b + c;
+  }
+};
+template class fp_contract_4<int>;
+// CIR-LABEL: cir.func {{.*}}@_ZN13fp_contract_4IiE6methodEfff
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_ZN13fp_contract_4IiE6methodEfff
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_ZN13fp_contract_4IiE6methodEfff
+// OGCG: call float @llvm.fmuladd.f32
+
+// Check file-scoped FP_CONTRACT
+#pragma STDC FP_CONTRACT ON
+float fp_contract_5(float a, float b, float c) {
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_5fff
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z13fp_contract_5fff
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z13fp_contract_5fff
+// OGCG: call float @llvm.fmuladd.f32
+
+#pragma STDC FP_CONTRACT OFF
+float fp_contract_6(float a, float b, float c) {
+  return a * b + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_6fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z13fp_contract_6fff
+// LLVM: %[[M:.*]] = fmul float
+// LLVM: fadd float %[[M]],
+// OGCG-LABEL: @_Z13fp_contract_6fff
+// OGCG: %[[M:.*]] = fmul float
+// OGCG: fadd float %[[M]],
+
+// If the multiply has multiple uses, don't produce fmuladd.
+// This used to assert (PR25719):
+// https://llvm.org/bugs/show_bug.cgi?id=25719
+float fp_contract_7(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return (a = 2 * b) - c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_7fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fsub %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z13fp_contract_7fff
+// LLVM: %[[M:.*]] = fmul float
+// LLVM: fsub float %[[M]],
+// OGCG-LABEL: @_Z13fp_contract_7fff
+// OGCG: %[[M:.*]] = fmul float
+// OGCG: fsub float %[[M]],
+
+// a * b - c  =>  fmuladd(a, b, -c)
+float fp_contract_8(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return a * b - c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_8fff
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z13fp_contract_8fff
+// LLVM: fneg float
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z13fp_contract_8fff
+// OGCG: fneg float
+// OGCG: call float @llvm.fmuladd.f32
+
+// c - a * b  =>  fmuladd(-a, b, c)  (mul on the RHS of a subtraction)
+float fp_contract_9(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return c - a * b;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_9fff
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z13fp_contract_9fff
+// LLVM: fneg float
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z13fp_contract_9fff
+// OGCG: fneg float
+// OGCG: call float @llvm.fmuladd.f32
+
+// -(a * b) + c  =>  fmuladd(-a, b, c)  (peek through fneg on the LHS)
+float fp_contract_10(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return -(a * b) + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z14fp_contract_10fff
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fadd
+// LLVM-LABEL: @_Z14fp_contract_10fff
+// LLVM: fneg float
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z14fp_contract_10fff
+// OGCG: fneg float
+// OGCG: call float @llvm.fmuladd.f32
+
+// -(a * b) - c  =>  fmuladd(-a, b, -c)  (fneg both the mul operand and addend)
+float fp_contract_11(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return -(a * b) - c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z14fp_contract_11fff
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z14fp_contract_11fff
+// LLVM: fneg float
+// LLVM: fneg float
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z14fp_contract_11fff
+// OGCG: fneg float
+// OGCG: fneg float
+// OGCG: call float @llvm.fmuladd.f32
+
+// c + -(a * b)  =>  fmuladd(-a, b, c)  (peek through fneg on the RHS)
+float fp_contract_12(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return c + -(a * b);
+}
+// CIR-LABEL: cir.func {{.*}}@_Z14fp_contract_12fff
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fadd
+// LLVM-LABEL: @_Z14fp_contract_12fff
+// LLVM: fneg float
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z14fp_contract_12fff
+// OGCG: fneg float
+// OGCG: call float @llvm.fmuladd.f32
+
+// c - -(a * b)  =>  fmuladd(a, b, c)  (the two negations cancel; no fneg)
+float fp_contract_13(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  return c - -(a * b);
+}
+// CIR-LABEL: cir.func {{.*}}@_Z14fp_contract_13fff
+// CIR-NOT: cir.fneg
+// CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// LLVM-LABEL: @_Z14fp_contract_13fff
+// LLVM-NOT: fneg float
+// LLVM: call float @llvm.fmuladd.f32
+// OGCG-LABEL: @_Z14fp_contract_13fff
+// OGCG-NOT: fneg float
+// OGCG: call float @llvm.fmuladd.f32
+
+// Mul reused by the assignment, so no fusion. At -O0 the negation stays an
+// fneg+fadd instead of the fsub the -O3 original test expects.
+float fp_contract_14(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  float d;
+  return (d = -(a * b)) + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z14fp_contract_14fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z14fp_contract_14fff
+// LLVM: fmul float
+// LLVM: fneg float
+// LLVM: fadd float
+// OGCG-LABEL: @_Z14fp_contract_14fff
+// OGCG: fmul float
+// OGCG: fneg float
+// OGCG: fadd float
+
+// Same as above, with the negation applied to the assignment result.
+float fp_contract_15(float a, float b, float c) {
+  #pragma STDC FP_CONTRACT ON
+  float d;
+  return -(d = (a * b)) + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z14fp_contract_15fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fneg %{{.*}} : !cir.float
+// CIR: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z14fp_contract_15fff
+// LLVM: fmul float
+// LLVM: fneg float
+// LLVM: fadd float
+// OGCG-LABEL: @_Z14fp_contract_15fff
+// OGCG: fmul float
+// OGCG: fneg float
+// OGCG: fadd float
diff --git a/clang/test/CIR/CodeGen/fp-contract.c 
b/clang/test/CIR/CodeGen/fp-contract.c
new file mode 100644
index 0000000000000..a27ead3d127d3
--- /dev/null
+++ b/clang/test/CIR/CodeGen/fp-contract.c
@@ -0,0 +1,86 @@
+// Test that -ffp-contract=on fuses a*b+c / a*b-c into cir.fmuladd and that
+// -ffp-contract=off does not.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=on -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR-ON
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=off -emit-cir %s -o %t-off.cir
+// RUN: FileCheck --input-file=%t-off.cir %s -check-prefix=CIR-OFF
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=on -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM-ON
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=off -emit-llvm %s -o %t-off.ll
+// RUN: FileCheck --input-file=%t-off.ll %s -check-prefix=LLVM-OFF
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-ffp-contract=on -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG-ON
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-ffp-contract=off -emit-llvm %s -o %t-off.ll
+// RUN: FileCheck --input-file=%t-off.ll %s -check-prefix=OGCG-OFF
+
+// a * b + c  =>  fmuladd(a, b, c)
+float fmuladd_add(float a, float b, float c) {
+  return a * b + c;
+}
+// CIR-ON-LABEL: cir.func {{.*}}@fmuladd_add
+// CIR-ON: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+// CIR-ON-NOT: cir.fmul
+
+// CIR-OFF-LABEL: cir.func {{.*}}@fmuladd_add
+// CIR-OFF: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR-OFF: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-OFF-NOT: cir.fmuladd
+
+// LLVM-ON-LABEL: @fmuladd_add
+// LLVM-ON: call float @llvm.fmuladd.f32
+// LLVM-OFF-LABEL: @fmuladd_add
+// LLVM-OFF: fmul float
+// LLVM-OFF: fadd float
+
+// OGCG-ON-LABEL: @fmuladd_add
+// OGCG-ON: call float @llvm.fmuladd.f32
+// OGCG-OFF-LABEL: @fmuladd_add
+// OGCG-OFF: fmul float
+// OGCG-OFF: fadd float
+
+// c + a * b  =>  fmuladd(a, b, c)  (mul on the RHS)
+float fmuladd_add_rhs(float a, float b, float c) {
+  return c + a * b;
+}
+// CIR-ON-LABEL: cir.func {{.*}}@fmuladd_add_rhs
+// CIR-ON: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
+
+// LLVM-ON-LABEL: @fmuladd_add_rhs
+// LLVM-ON: call float @llvm.fmuladd.f32
+
+// a * b - c  =>  fmuladd(a, b, -c)
+float fmuladd_sub(float a, float b, float c) {
+  return a * b - c;
+}
+// CIR-ON-LABEL: cir.func {{.*}}@fmuladd_sub
+// CIR-ON: %[[NEG:.*]] = cir.fneg %{{.*}} : !cir.float
+// CIR-ON: cir.fmuladd %{{.*}}, %{{.*}}, %[[NEG]] : !cir.float
+
+// LLVM-ON-LABEL: @fmuladd_sub
+// LLVM-ON: %[[NEG:.*]] = fneg float
+// LLVM-ON: call float @llvm.fmuladd.f32(float %{{.*}}, float %{{.*}}, float 
%[[NEG]])
+
+// If the mul result is used elsewhere, it must NOT be fused.
+float no_fmuladd_reused_mul(float a, float b, float c, float *p) {
+  float m = a * b;
+  *p = m;
+  return m + c;
+}
+// CIR-ON-LABEL: cir.func {{.*}}@no_fmuladd_reused_mul
+// CIR-ON: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR-ON: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-ON-NOT: cir.fmuladd
+
+// Vector: a * b + c  =>  fmuladd on the vector type.
+typedef float float4 __attribute__((ext_vector_type(4)));
+float4 fmuladd_vec(float4 a, float4 b, float4 c) {
+  return a * b + c;
+}
+// CIR-ON-LABEL: cir.func {{.*}}@fmuladd_vec
+// CIR-ON: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.vector<4 x !cir.float>
+
+// LLVM-ON-LABEL: @fmuladd_vec
+// LLVM-ON: call <4 x float> @llvm.fmuladd.v4f32

>From 4d0ac875c52ee79abdc73507e8accc03d3ce7f7a Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Tue, 11 Aug 2026 14:28:01 -0700
Subject: [PATCH 2/4] Address comments

---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 32 +++++++++++---------
 clang/test/CIR/CodeGen/fp-contract.c       | 34 ++++++++++++++++++++++
 2 files changed, 52 insertions(+), 14 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index f3e8e646c3c07..0025ab5b94088 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1953,10 +1953,9 @@ static bool isIntegerVectorBinOp(mlir::Type ty) {
 // the addend respectively. This allows fmuladd to represent a*b-c, or c-a*b.
 // Patterns in LLVM should catch the negated forms and translate them to
 // efficient operations.
-static mlir::Value buildFMulAdd(cir::FMulOp mulOp, mlir::Value addend,
-                                CIRGenBuilderTy &builder, bool negMul,
-                                bool negAdd) {
-  const mlir::Location loc = mulOp.getLoc();
+static mlir::Value buildFMulAdd(mlir::Location loc, cir::FMulOp mulOp,
+                                mlir::Value addend, CIRGenBuilderTy &builder,
+                                bool negMul, bool negAdd) {
   mlir::Value mulOp0 = mulOp.getLhs();
   mlir::Value mulOp1 = mulOp.getRhs();
   if (negMul)
@@ -1964,9 +1963,11 @@ static mlir::Value buildFMulAdd(cir::FMulOp mulOp, 
mlir::Value addend,
   if (negAdd)
     addend = builder.createFNeg(loc, addend);
 
-  mlir::Value fmuladd =
-      cir::FMulAddOp::create(builder, loc, addend.getType(), mulOp0, mulOp1,
-                             addend, builder.getConstrainedFPAttr());
+  // Carry the mul's fenv attribute so a constrained fmul yields a constrained
+  // fmuladd; the builder is under the add's FP options, not the mul's.
+  mlir::Value fmuladd = cir::FMulAddOp::create(
+      builder, loc, addend.getType(), mulOp0, mulOp1, addend,
+      mulOp.getFenvAttr());
   mulOp.erase();
   return fmuladd;
 }
@@ -1977,13 +1978,16 @@ static mlir::Value buildFMulAdd(cir::FMulOp mulOp, 
mlir::Value addend,
 // Checks that (a) the operation is fusable, and (b) -ffp-contract=on.
 // Does NOT check the type of the operation - it's assumed that this function
 // will be called from contexts where it's known that the type is contractable.
-static mlir::Value tryEmitFMulAdd(const BinOpInfo &op, CIRGenBuilderTy 
&builder,
-                                  bool isSub = false) {
+static mlir::Value tryEmitFMulAdd(mlir::Location loc, const BinOpInfo &op,
+                                  CIRGenBuilderTy &builder, bool isSub = 
false) {
   assert((op.opcode == BO_Add || op.opcode == BO_AddAssign ||
           op.opcode == BO_Sub || op.opcode == BO_SubAssign) &&
          "Only fadd/fsub can be the root of an fmuladd.");
 
-  // Check whether this op is marked as fusable.
+  // Check whether this op is fusable, i.e. -ffp-contract=on. 
-ffp-contract=fast
+  // needs fast-math flags on the fmul/fadd, which CIR does not model yet, so 
it
+  // fuses nowhere for now.
+  assert(!cir::MissingFeatures::fastMathFlags());
   if (!op.fpFeatures.allowFPContractWithinStatement())
     return nullptr;
 
@@ -2016,7 +2020,7 @@ static mlir::Value tryEmitFMulAdd(const BinOpInfo &op, 
CIRGenBuilderTy &builder,
       // If we looked through fneg, erase it.
       if (negLHS)
         op.lhs.getDefiningOp<cir::FNegOp>().erase();
-      return buildFMulAdd(lhsMul, op.rhs, builder, negLHS, isSub);
+      return buildFMulAdd(loc, lhsMul, op.rhs, builder, negLHS, isSub);
     }
   }
   if (auto rhsMul = rhs.getDefiningOp<cir::FMulOp>()) {
@@ -2024,7 +2028,7 @@ static mlir::Value tryEmitFMulAdd(const BinOpInfo &op, 
CIRGenBuilderTy &builder,
       // If we looked through fneg, erase it.
       if (negRHS)
         op.rhs.getDefiningOp<cir::FNegOp>().erase();
-      return buildFMulAdd(rhsMul, op.lhs, builder, isSub ^ negRHS, false);
+      return buildFMulAdd(loc, rhsMul, op.lhs, builder, isSub ^ negRHS, false);
     }
   }
 
@@ -2131,7 +2135,7 @@ mlir::Value ScalarExprEmitter::emitAdd(const BinOpInfo 
&ops) {
   if (cir::isFPOrVectorOfFPType(ops.lhs.getType())) {
     CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, ops.fpFeatures);
     // Try to form an fmuladd.
-    if (mlir::Value fmuladd = tryEmitFMulAdd(ops, builder))
+    if (mlir::Value fmuladd = tryEmitFMulAdd(loc, ops, builder))
       return fmuladd;
     return builder.createFAdd(loc, ops.lhs, ops.rhs);
   }
@@ -2182,7 +2186,7 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo 
&ops) {
     if (cir::isFPOrVectorOfFPType(ops.lhs.getType())) {
       CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, ops.fpFeatures);
       // Try to form an fmuladd.
-      if (mlir::Value fmuladd = tryEmitFMulAdd(ops, builder, /*isSub=*/true))
+      if (mlir::Value fmuladd = tryEmitFMulAdd(loc, ops, builder, 
/*isSub=*/true))
         return fmuladd;
       return builder.createFSub(loc, ops.lhs, ops.rhs);
     }
diff --git a/clang/test/CIR/CodeGen/fp-contract.c 
b/clang/test/CIR/CodeGen/fp-contract.c
index a27ead3d127d3..c752ca997f445 100644
--- a/clang/test/CIR/CodeGen/fp-contract.c
+++ b/clang/test/CIR/CodeGen/fp-contract.c
@@ -16,6 +16,15 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-ffp-contract=off -emit-llvm %s -o %t-off.ll
 // RUN: FileCheck --input-file=%t-off.ll %s -check-prefix=OGCG-OFF
 
+// Under strict FP the fused op carries an fenv attribute and lowers to the
+// constrained fmuladd intrinsic.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=on -fexperimental-strict-floating-point 
-ffp-exception-behavior=strict -emit-cir %s -o %t-strict.cir
+// RUN: FileCheck --input-file=%t-strict.cir %s -check-prefix=CIR-STRICT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=on -fexperimental-strict-floating-point 
-ffp-exception-behavior=strict -emit-llvm %s -o %t-strict.ll
+// RUN: FileCheck --input-file=%t-strict.ll %s -check-prefix=LLVM-STRICT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-ffp-contract=on -fexperimental-strict-floating-point 
-ffp-exception-behavior=strict -emit-llvm %s -o %t-strict-ogcg.ll
+// RUN: FileCheck --input-file=%t-strict-ogcg.ll %s -check-prefix=OGCG-STRICT
+
 // a * b + c  =>  fmuladd(a, b, c)
 float fmuladd_add(float a, float b, float c) {
   return a * b + c;
@@ -50,6 +59,8 @@ float fmuladd_add_rhs(float a, float b, float c) {
 
 // LLVM-ON-LABEL: @fmuladd_add_rhs
 // LLVM-ON: call float @llvm.fmuladd.f32
+// OGCG-ON-LABEL: @fmuladd_add_rhs
+// OGCG-ON: call float @llvm.fmuladd.f32
 
 // a * b - c  =>  fmuladd(a, b, -c)
 float fmuladd_sub(float a, float b, float c) {
@@ -74,6 +85,15 @@ float no_fmuladd_reused_mul(float a, float b, float c, float 
*p) {
 // CIR-ON: cir.fadd %{{.*}}, %{{.*}} : !cir.float
 // CIR-ON-NOT: cir.fmuladd
 
+// LLVM-ON-LABEL: @no_fmuladd_reused_mul
+// LLVM-ON: fmul float
+// LLVM-ON: fadd float
+// LLVM-ON-NOT: call float @llvm.fmuladd.f32
+// OGCG-ON-LABEL: @no_fmuladd_reused_mul
+// OGCG-ON: fmul float
+// OGCG-ON: fadd float
+// OGCG-ON-NOT: call float @llvm.fmuladd.f32
+
 // Vector: a * b + c  =>  fmuladd on the vector type.
 typedef float float4 __attribute__((ext_vector_type(4)));
 float4 fmuladd_vec(float4 a, float4 b, float4 c) {
@@ -84,3 +104,17 @@ float4 fmuladd_vec(float4 a, float4 b, float4 c) {
 
 // LLVM-ON-LABEL: @fmuladd_vec
 // LLVM-ON: call <4 x float> @llvm.fmuladd.v4f32
+// OGCG-ON-LABEL: @fmuladd_vec
+// OGCG-ON: call <4 x float> @llvm.fmuladd.v4f32
+
+// Strict FP: fused op carries an fenv attr, lowering to the constrained
+// fmuladd intrinsic.
+float fmuladd_strict(float a, float b, float c) {
+  return a * b + c;
+}
+// CIR-STRICT-LABEL: cir.func {{.*}}@fmuladd_strict
+// CIR-STRICT: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float {fenv = 
#cir.fenv<{{.*}}strict_except = true>}
+// LLVM-STRICT-LABEL: @fmuladd_strict
+// LLVM-STRICT: call float @llvm.experimental.constrained.fmuladd.f32
+// OGCG-STRICT-LABEL: @fmuladd_strict
+// OGCG-STRICT: call float @llvm.experimental.constrained.fmuladd.f32

>From 5b90237f793453556c3e6e0e7de6a87a8a1d25a6 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Tue, 11 Aug 2026 14:38:13 -0700
Subject: [PATCH 3/4] Format

---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 0025ab5b94088..74f524e92eec7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1965,9 +1965,9 @@ static mlir::Value buildFMulAdd(mlir::Location loc, 
cir::FMulOp mulOp,
 
   // Carry the mul's fenv attribute so a constrained fmul yields a constrained
   // fmuladd; the builder is under the add's FP options, not the mul's.
-  mlir::Value fmuladd = cir::FMulAddOp::create(
-      builder, loc, addend.getType(), mulOp0, mulOp1, addend,
-      mulOp.getFenvAttr());
+  mlir::Value fmuladd =
+      cir::FMulAddOp::create(builder, loc, addend.getType(), mulOp0, mulOp1,
+                             addend, mulOp.getFenvAttr());
   mulOp.erase();
   return fmuladd;
 }
@@ -1979,7 +1979,8 @@ static mlir::Value buildFMulAdd(mlir::Location loc, 
cir::FMulOp mulOp,
 // Does NOT check the type of the operation - it's assumed that this function
 // will be called from contexts where it's known that the type is contractable.
 static mlir::Value tryEmitFMulAdd(mlir::Location loc, const BinOpInfo &op,
-                                  CIRGenBuilderTy &builder, bool isSub = 
false) {
+                                  CIRGenBuilderTy &builder,
+                                  bool isSub = false) {
   assert((op.opcode == BO_Add || op.opcode == BO_AddAssign ||
           op.opcode == BO_Sub || op.opcode == BO_SubAssign) &&
          "Only fadd/fsub can be the root of an fmuladd.");
@@ -2186,7 +2187,8 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo 
&ops) {
     if (cir::isFPOrVectorOfFPType(ops.lhs.getType())) {
       CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, ops.fpFeatures);
       // Try to form an fmuladd.
-      if (mlir::Value fmuladd = tryEmitFMulAdd(loc, ops, builder, 
/*isSub=*/true))
+      if (mlir::Value fmuladd =
+              tryEmitFMulAdd(loc, ops, builder, /*isSub=*/true))
         return fmuladd;
       return builder.createFSub(loc, ops.lhs, ops.rhs);
     }

>From 53f68f437e43b0f284d2c2f7ab3078a87977b183 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Wed, 12 Aug 2026 11:11:17 -0700
Subject: [PATCH 4/4] Address comments

---
 .../CIR/CodeGen/fp-contract-on-pragma.cpp     | 38 +++++++-------
 clang/test/CIR/CodeGen/fp-contract-pragma.cpp | 49 ++-----------------
 clang/test/CIR/CodeGen/fp-contract.c          | 24 ++-------
 3 files changed, 27 insertions(+), 84 deletions(-)

diff --git a/clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp 
b/clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp
index 2c04dcfd6afc3..1996896edd26b 100644
--- a/clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp
+++ b/clang/test/CIR/CodeGen/fp-contract-on-pragma.cpp
@@ -1,11 +1,12 @@
 // ClangIR port of clang/test/CodeGen/fp-contract-on-pragma.cpp.
+// The CIR-lowered and classic CodeGen LLVM IR match here, so both feed LLVM.
 
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
 // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-llvm %s -o %t.ll
 // RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -emit-llvm %s -o %t-ogcg.ll
-// RUN: FileCheck --input-file=%t-ogcg.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t-ogcg.ll %s -check-prefix=LLVM
 
 // Is FP_CONTRACT honored in a simple case?
 float fp_contract_1(float a, float b, float c) {
@@ -17,8 +18,6 @@ float fp_contract_1(float a, float b, float c) {
 // CIR-NOT: cir.fmul
 // LLVM-LABEL: @_Z13fp_contract_1fff
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z13fp_contract_1fff
-// OGCG: call float @llvm.fmuladd.f32
 
 // Is FP_CONTRACT state cleared on exiting compound statements?
 float fp_contract_2(float a, float b, float c) {
@@ -34,9 +33,6 @@ float fp_contract_2(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_2fff
 // LLVM: %[[M:.*]] = fmul float
 // LLVM: fadd float %[[M]],
-// OGCG-LABEL: @_Z13fp_contract_2fff
-// OGCG: %[[M:.*]] = fmul float
-// OGCG: fadd float %[[M]],
 
 // Does FP_CONTRACT survive template instantiation?
 class Foo {};
@@ -50,9 +46,7 @@ T template_muladd(T a, T b, T c) {
 // CIR-LABEL: cir.func {{.*}}@_Z15template_muladdIfET_S0_S0_S0_
 // CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
 // LLVM-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
-// LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
-// OGCG: call {{.*}}float @llvm.fmuladd.f32
+// LLVM: call {{.*}}float @llvm.fmuladd.f32
 
 // fp_contract_3 is just a caller; the fused op lives in the instantiated
 // template_muladd checked above. It is emitted in a different order under the
@@ -73,8 +67,6 @@ template class fp_contract_4<int>;
 // CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
 // LLVM-LABEL: @_ZN13fp_contract_4IiE6methodEfff
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_ZN13fp_contract_4IiE6methodEfff
-// OGCG: call float @llvm.fmuladd.f32
 
 // Check file-scoped FP_CONTRACT
 #pragma clang fp contract(on)
@@ -85,8 +77,6 @@ float fp_contract_5(float a, float b, float c) {
 // CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
 // LLVM-LABEL: @_Z13fp_contract_5fff
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z13fp_contract_5fff
-// OGCG: call float @llvm.fmuladd.f32
 
 #pragma clang fp contract(off)
 float fp_contract_6(float a, float b, float c) {
@@ -99,9 +89,6 @@ float fp_contract_6(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_6fff
 // LLVM: %[[M:.*]] = fmul float
 // LLVM: fadd float %[[M]],
-// OGCG-LABEL: @_Z13fp_contract_6fff
-// OGCG: %[[M:.*]] = fmul float
-// OGCG: fadd float %[[M]],
 
 // If the multiply has multiple uses, don't produce fmuladd.
 // This used to assert (PR25719):
@@ -117,6 +104,19 @@ float fp_contract_7(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_7fff
 // LLVM: %[[M:.*]] = fmul float
 // LLVM: fsub float %[[M]],
-// OGCG-LABEL: @_Z13fp_contract_7fff
-// OGCG: %[[M:.*]] = fmul float
-// OGCG: fsub float %[[M]],
+
+// contract(on) only fuses within a statement: a mul and add in separate
+// statements are not contracted.
+float fp_contract_8(float a, float b, float c) {
+#pragma clang fp contract(on)
+  float t = a * b;
+  return t + c;
+}
+// CIR-LABEL: cir.func {{.*}}@_Z13fp_contract_8fff
+// CIR: cir.fmul %{{.*}}, %{{.*}} : !cir.float
+// CIR: cir.fadd %{{.*}}, %{{.*}} : !cir.float
+// CIR-NOT: cir.fmuladd
+// LLVM-LABEL: @_Z13fp_contract_8fff
+// LLVM: fmul float
+// LLVM: fadd float
+// LLVM-NOT: call float @llvm.fmuladd.f32
diff --git a/clang/test/CIR/CodeGen/fp-contract-pragma.cpp 
b/clang/test/CIR/CodeGen/fp-contract-pragma.cpp
index 6819f150205fe..f5f0c0884dc52 100644
--- a/clang/test/CIR/CodeGen/fp-contract-pragma.cpp
+++ b/clang/test/CIR/CodeGen/fp-contract-pragma.cpp
@@ -1,11 +1,12 @@
 // ClangIR port of clang/test/CodeGen/fp-contract-pragma.cpp.
+// The CIR-lowered and classic CodeGen LLVM IR match here, so both feed LLVM.
 
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
 // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -fclangir -emit-llvm %s -o %t.ll
 // RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 
-Wno-unused-value -emit-llvm %s -o %t-ogcg.ll
-// RUN: FileCheck --input-file=%t-ogcg.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t-ogcg.ll %s -check-prefix=LLVM
 
 // Is FP_CONTRACT honored in a simple case?
 float fp_contract_1(float a, float b, float c) {
@@ -17,8 +18,6 @@ float fp_contract_1(float a, float b, float c) {
 // CIR-NOT: cir.fmul
 // LLVM-LABEL: @_Z13fp_contract_1fff
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z13fp_contract_1fff
-// OGCG: call float @llvm.fmuladd.f32
 
 // Is FP_CONTRACT state cleared on exiting compound statements?
 float fp_contract_2(float a, float b, float c) {
@@ -34,9 +33,6 @@ float fp_contract_2(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_2fff
 // LLVM: %[[M:.*]] = fmul float
 // LLVM: fadd float %[[M]],
-// OGCG-LABEL: @_Z13fp_contract_2fff
-// OGCG: %[[M:.*]] = fmul float
-// OGCG: fadd float %[[M]],
 
 // Does FP_CONTRACT survive template instantiation?
 class Foo {};
@@ -51,9 +47,7 @@ T template_muladd(T a, T b, T c) {
 // CIR-LABEL: cir.func {{.*}}@_Z15template_muladdIfET_S0_S0_S0_
 // CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
 // LLVM-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
-// LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z15template_muladdIfET_S0_S0_S0_
-// OGCG: call {{.*}}float @llvm.fmuladd.f32
+// LLVM: call {{.*}}float @llvm.fmuladd.f32
 
 // fp_contract_3 is just a caller; the fused op lives in the instantiated
 // template_muladd checked above. It is emitted in a different order under the
@@ -73,8 +67,6 @@ template class fp_contract_4<int>;
 // CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
 // LLVM-LABEL: @_ZN13fp_contract_4IiE6methodEfff
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_ZN13fp_contract_4IiE6methodEfff
-// OGCG: call float @llvm.fmuladd.f32
 
 // Check file-scoped FP_CONTRACT
 #pragma STDC FP_CONTRACT ON
@@ -85,8 +77,6 @@ float fp_contract_5(float a, float b, float c) {
 // CIR: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float
 // LLVM-LABEL: @_Z13fp_contract_5fff
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z13fp_contract_5fff
-// OGCG: call float @llvm.fmuladd.f32
 
 #pragma STDC FP_CONTRACT OFF
 float fp_contract_6(float a, float b, float c) {
@@ -99,9 +89,6 @@ float fp_contract_6(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_6fff
 // LLVM: %[[M:.*]] = fmul float
 // LLVM: fadd float %[[M]],
-// OGCG-LABEL: @_Z13fp_contract_6fff
-// OGCG: %[[M:.*]] = fmul float
-// OGCG: fadd float %[[M]],
 
 // If the multiply has multiple uses, don't produce fmuladd.
 // This used to assert (PR25719):
@@ -117,9 +104,6 @@ float fp_contract_7(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_7fff
 // LLVM: %[[M:.*]] = fmul float
 // LLVM: fsub float %[[M]],
-// OGCG-LABEL: @_Z13fp_contract_7fff
-// OGCG: %[[M:.*]] = fmul float
-// OGCG: fsub float %[[M]],
 
 // a * b - c  =>  fmuladd(a, b, -c)
 float fp_contract_8(float a, float b, float c) {
@@ -132,9 +116,6 @@ float fp_contract_8(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_8fff
 // LLVM: fneg float
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z13fp_contract_8fff
-// OGCG: fneg float
-// OGCG: call float @llvm.fmuladd.f32
 
 // c - a * b  =>  fmuladd(-a, b, c)  (mul on the RHS of a subtraction)
 float fp_contract_9(float a, float b, float c) {
@@ -147,9 +128,6 @@ float fp_contract_9(float a, float b, float c) {
 // LLVM-LABEL: @_Z13fp_contract_9fff
 // LLVM: fneg float
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z13fp_contract_9fff
-// OGCG: fneg float
-// OGCG: call float @llvm.fmuladd.f32
 
 // -(a * b) + c  =>  fmuladd(-a, b, c)  (peek through fneg on the LHS)
 float fp_contract_10(float a, float b, float c) {
@@ -163,9 +141,6 @@ float fp_contract_10(float a, float b, float c) {
 // LLVM-LABEL: @_Z14fp_contract_10fff
 // LLVM: fneg float
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z14fp_contract_10fff
-// OGCG: fneg float
-// OGCG: call float @llvm.fmuladd.f32
 
 // -(a * b) - c  =>  fmuladd(-a, b, -c)  (fneg both the mul operand and addend)
 float fp_contract_11(float a, float b, float c) {
@@ -180,10 +155,6 @@ float fp_contract_11(float a, float b, float c) {
 // LLVM: fneg float
 // LLVM: fneg float
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z14fp_contract_11fff
-// OGCG: fneg float
-// OGCG: fneg float
-// OGCG: call float @llvm.fmuladd.f32
 
 // c + -(a * b)  =>  fmuladd(-a, b, c)  (peek through fneg on the RHS)
 float fp_contract_12(float a, float b, float c) {
@@ -197,9 +168,6 @@ float fp_contract_12(float a, float b, float c) {
 // LLVM-LABEL: @_Z14fp_contract_12fff
 // LLVM: fneg float
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z14fp_contract_12fff
-// OGCG: fneg float
-// OGCG: call float @llvm.fmuladd.f32
 
 // c - -(a * b)  =>  fmuladd(a, b, c)  (the two negations cancel; no fneg)
 float fp_contract_13(float a, float b, float c) {
@@ -212,9 +180,6 @@ float fp_contract_13(float a, float b, float c) {
 // LLVM-LABEL: @_Z14fp_contract_13fff
 // LLVM-NOT: fneg float
 // LLVM: call float @llvm.fmuladd.f32
-// OGCG-LABEL: @_Z14fp_contract_13fff
-// OGCG-NOT: fneg float
-// OGCG: call float @llvm.fmuladd.f32
 
 // Mul reused by the assignment, so no fusion. At -O0 the negation stays an
 // fneg+fadd instead of the fsub the -O3 original test expects.
@@ -232,10 +197,6 @@ float fp_contract_14(float a, float b, float c) {
 // LLVM: fmul float
 // LLVM: fneg float
 // LLVM: fadd float
-// OGCG-LABEL: @_Z14fp_contract_14fff
-// OGCG: fmul float
-// OGCG: fneg float
-// OGCG: fadd float
 
 // Same as above, with the negation applied to the assignment result.
 float fp_contract_15(float a, float b, float c) {
@@ -252,7 +213,3 @@ float fp_contract_15(float a, float b, float c) {
 // LLVM: fmul float
 // LLVM: fneg float
 // LLVM: fadd float
-// OGCG-LABEL: @_Z14fp_contract_15fff
-// OGCG: fmul float
-// OGCG: fneg float
-// OGCG: fadd float
diff --git a/clang/test/CIR/CodeGen/fp-contract.c 
b/clang/test/CIR/CodeGen/fp-contract.c
index c752ca997f445..297655a6f4a80 100644
--- a/clang/test/CIR/CodeGen/fp-contract.c
+++ b/clang/test/CIR/CodeGen/fp-contract.c
@@ -1,5 +1,6 @@
 // Test that -ffp-contract=on fuses a*b+c / a*b-c into cir.fmuladd and that
-// -ffp-contract=off does not.
+// -ffp-contract=off does not. The CIR-lowered and classic CodeGen LLVM IR
+// match here, so both feed the LLVM-* prefixes.
 
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=on -emit-cir %s -o %t.cir
 // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR-ON
@@ -12,9 +13,9 @@
 // RUN: FileCheck --input-file=%t-off.ll %s -check-prefix=LLVM-OFF
 
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-ffp-contract=on -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG-ON
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM-ON
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-ffp-contract=off -emit-llvm %s -o %t-off.ll
-// RUN: FileCheck --input-file=%t-off.ll %s -check-prefix=OGCG-OFF
+// RUN: FileCheck --input-file=%t-off.ll %s -check-prefix=LLVM-OFF
 
 // Under strict FP the fused op carries an fenv attribute and lowers to the
 // constrained fmuladd intrinsic.
@@ -23,7 +24,7 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -ffp-contract=on -fexperimental-strict-floating-point 
-ffp-exception-behavior=strict -emit-llvm %s -o %t-strict.ll
 // RUN: FileCheck --input-file=%t-strict.ll %s -check-prefix=LLVM-STRICT
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-ffp-contract=on -fexperimental-strict-floating-point 
-ffp-exception-behavior=strict -emit-llvm %s -o %t-strict-ogcg.ll
-// RUN: FileCheck --input-file=%t-strict-ogcg.ll %s -check-prefix=OGCG-STRICT
+// RUN: FileCheck --input-file=%t-strict-ogcg.ll %s -check-prefix=LLVM-STRICT
 
 // a * b + c  =>  fmuladd(a, b, c)
 float fmuladd_add(float a, float b, float c) {
@@ -44,11 +45,6 @@ float fmuladd_add(float a, float b, float c) {
 // LLVM-OFF: fmul float
 // LLVM-OFF: fadd float
 
-// OGCG-ON-LABEL: @fmuladd_add
-// OGCG-ON: call float @llvm.fmuladd.f32
-// OGCG-OFF-LABEL: @fmuladd_add
-// OGCG-OFF: fmul float
-// OGCG-OFF: fadd float
 
 // c + a * b  =>  fmuladd(a, b, c)  (mul on the RHS)
 float fmuladd_add_rhs(float a, float b, float c) {
@@ -59,8 +55,6 @@ float fmuladd_add_rhs(float a, float b, float c) {
 
 // LLVM-ON-LABEL: @fmuladd_add_rhs
 // LLVM-ON: call float @llvm.fmuladd.f32
-// OGCG-ON-LABEL: @fmuladd_add_rhs
-// OGCG-ON: call float @llvm.fmuladd.f32
 
 // a * b - c  =>  fmuladd(a, b, -c)
 float fmuladd_sub(float a, float b, float c) {
@@ -89,10 +83,6 @@ float no_fmuladd_reused_mul(float a, float b, float c, float 
*p) {
 // LLVM-ON: fmul float
 // LLVM-ON: fadd float
 // LLVM-ON-NOT: call float @llvm.fmuladd.f32
-// OGCG-ON-LABEL: @no_fmuladd_reused_mul
-// OGCG-ON: fmul float
-// OGCG-ON: fadd float
-// OGCG-ON-NOT: call float @llvm.fmuladd.f32
 
 // Vector: a * b + c  =>  fmuladd on the vector type.
 typedef float float4 __attribute__((ext_vector_type(4)));
@@ -104,8 +94,6 @@ float4 fmuladd_vec(float4 a, float4 b, float4 c) {
 
 // LLVM-ON-LABEL: @fmuladd_vec
 // LLVM-ON: call <4 x float> @llvm.fmuladd.v4f32
-// OGCG-ON-LABEL: @fmuladd_vec
-// OGCG-ON: call <4 x float> @llvm.fmuladd.v4f32
 
 // Strict FP: fused op carries an fenv attr, lowering to the constrained
 // fmuladd intrinsic.
@@ -116,5 +104,3 @@ float fmuladd_strict(float a, float b, float c) {
 // CIR-STRICT: cir.fmuladd %{{.*}}, %{{.*}}, %{{.*}} : !cir.float {fenv = 
#cir.fenv<{{.*}}strict_except = true>}
 // LLVM-STRICT-LABEL: @fmuladd_strict
 // LLVM-STRICT: call float @llvm.experimental.constrained.fmuladd.f32
-// OGCG-STRICT-LABEL: @fmuladd_strict
-// OGCG-STRICT: call float @llvm.experimental.constrained.fmuladd.f32

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to