[clang] [FPEnv] Add strictfp in some C++ constructors lacking a FunctionDecl. (PR #74883)

Kevin P. Neal via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 16 05:38:36 PST 2024


https://github.com/kpneal updated https://github.com/llvm/llvm-project/pull/74883

>From eb43ba5adbf57988fdd5f490a74dc59d72f495e5 Mon Sep 17 00:00:00 2001
From: "Kevin P. Neal" <kevin.neal at sas.com>
Date: Fri, 8 Dec 2023 14:43:50 -0500
Subject: [PATCH 1/5] [FPEnv] Add strictfp in some C++ constructors lacking a
 FunctionDecl.

Some C++ constructors require the strictfp attribute on all function calls
but don't get it because the comstructor lacks a FunctionDecl. Use the
IRBuilder's strictfp mode to communicate that the strictfp attribute is
required. Note that the function calls that were missing the attribute
were getting it from the IRBuilder but it then was getting lost when
CGCall.cpp replaced all the attributes for the function call.

Verified with "https://reviews.llvm.org/D146845".
---
 clang/include/clang/AST/ExprCXX.h            |  4 ++
 clang/lib/CodeGen/CGCall.cpp                 |  8 +++
 clang/lib/CodeGen/CGDeclCXX.cpp              |  5 ++
 clang/test/CodeGen/fp-floatcontrol-class.cpp | 75 ++++++++++++++++++--
 4 files changed, 88 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 24278016431837..f9269082c32475 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -1698,6 +1698,10 @@ class CXXConstructExpr : public Expr {
   SourceRange getParenOrBraceRange() const { return ParenOrBraceRange; }
   void setParenOrBraceRange(SourceRange Range) { ParenOrBraceRange = Range; }
 
+  /// Determine whether the function was declared in source context
+  /// that requires constrained FP intrinsics
+  bool UsesFPIntrin() const { return Constructor->UsesFPIntrin(); }
+
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == CXXConstructExprClass ||
            T->getStmtClass() == CXXTemporaryObjectExprClass;
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index a24aeea7ae32bf..7d1bb4dc75ab45 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5520,6 +5520,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
       CGM.AdjustMemoryAttribute(CalleePtr->getName(), Callee.getAbstractInfo(),
                                 Attrs);
   }
+  // We may not have a FunctionDecl*, but we still need to support strictfp.
+  if (Builder.getIsFPConstrained()) {
+    // All calls within a strictfp function are marked strictfp
+    Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::StrictFP);
+  }
+
   // Add call-site nomerge attribute if exists.
   if (InNoMergeAttributedStmt)
     Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoMerge);
@@ -5587,10 +5593,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
       !isa_and_nonnull<FunctionDecl>(TargetDecl))
     EmitKCFIOperandBundle(ConcreteCallee, BundleList);
 
+#if 0 // XXX Why is this here? Duplicate!
   if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl))
     if (FD->hasAttr<StrictFPAttr>())
       // All calls within a strictfp function are marked strictfp
       Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::StrictFP);
+#endif
 
   AssumeAlignedAttrEmitter AssumeAlignedAttrEmitter(*this, TargetDecl);
   Attrs = AssumeAlignedAttrEmitter.TryEmitAsCallSiteAttribute(Attrs);
diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index e08a1e5f42df20..5700b7fcb49d32 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -1012,6 +1012,11 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn,
 
   CurEHLocation = D->getBeginLoc();
 
+  if (const auto *CE = dyn_cast<CXXConstructExpr>(D->getInit())) {
+    if (CE->UsesFPIntrin())
+      Builder.setIsFPConstrained(true);
+  }
+
   StartFunction(GlobalDecl(D, DynamicInitKind::Initializer),
                 getContext().VoidTy, Fn, getTypes().arrangeNullaryFunction(),
                 FunctionArgList());
diff --git a/clang/test/CodeGen/fp-floatcontrol-class.cpp b/clang/test/CodeGen/fp-floatcontrol-class.cpp
index 83a27cb206eb82..c9953119fd3a22 100644
--- a/clang/test/CodeGen/fp-floatcontrol-class.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-class.cpp
@@ -1,3 +1,4 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 4
 // RUN: %clang_cc1 -ffp-contract=on -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
 // Verify that float_control does not pertain to initializer expressions
 
@@ -6,14 +7,80 @@ float z();
 #pragma float_control(except, on)
 class ON {
   float w = 2 + y() * z();
-  // CHECK-LABEL: define {{.*}} @_ZN2ONC2Ev{{.*}}
-  // CHECK: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict
 };
 ON on;
 #pragma float_control(except, off)
 class OFF {
   float w = 2 + y() * z();
-  // CHECK-LABEL: define {{.*}} @_ZN3OFFC2Ev{{.*}}
-  // CHECK-NOT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict
 };
 OFF off;
+// CHECK-LABEL: define internal void @__cxx_global_var_init(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] section ".text.startup" {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @_ZN2ONC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @on) #[[ATTR6:[0-9]+]]
+// CHECK-NEXT:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN2ONC1Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] comdat align 2 {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN2ONC2Ev(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]]) #[[ATTR6]]
+// CHECK-NEXT:    ret void
+//
+//
+// CHECK-LABEL: define internal void @__cxx_global_var_init.1(
+// CHECK-SAME: ) #[[ATTR0]] section ".text.startup" {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @_ZN3OFFC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @off)
+// CHECK-NEXT:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN3OFFC1Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2:[0-9]+]] comdat align 2 {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3OFFC2Ev(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]])
+// CHECK-NEXT:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN2ONC2Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1]] comdat align 2 {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_ON:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 2, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_Z1yv() #[[ATTR6]]
+// CHECK-NEXT:    [[CALL2:%.*]] = call noundef float @_Z1zv() #[[ATTR6]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.experimental.constrained.fmuladd.f32(float [[CALL]], float [[CALL2]], float [[CONV]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6]]
+// CHECK-NEXT:    store float [[TMP0]], ptr [[W]], align 4
+// CHECK-NEXT:    ret void
+//
+//
+// CHECK-LABEL: define linkonce_odr void @_ZN3OFFC2Ev(
+// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2]] comdat align 2 {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_OFF:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_Z1yv()
+// CHECK-NEXT:    [[CALL2:%.*]] = call noundef float @_Z1zv()
+// CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.fmuladd.f32(float [[CALL]], float [[CALL2]], float 2.000000e+00)
+// CHECK-NEXT:    store float [[TMP0]], ptr [[W]], align 4
+// CHECK-NEXT:    ret void
+//
+//
+// CHECK-LABEL: define internal void @_GLOBAL__sub_I_fp_floatcontrol_class.cpp(
+// CHECK-SAME: ) #[[ATTR0]] section ".text.startup" {
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    call void @__cxx_global_var_init()
+// CHECK-NEXT:    call void @__cxx_global_var_init.1()
+// CHECK-NEXT:    ret void
+//

>From 40b4c51113831abbcc6d75b3f1d598a0d0462e7e Mon Sep 17 00:00:00 2001
From: "Kevin P. Neal" <kevin.neal at sas.com>
Date: Wed, 13 Dec 2023 15:16:12 -0500
Subject: [PATCH 2/5] Update for review comments. Add missing bits to test
 case, eliminate unnecessary code.

---
 clang/lib/CodeGen/CGCall.cpp                 |  7 ---
 clang/test/CodeGen/fp-floatcontrol-class.cpp | 63 +++++++++++---------
 2 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 7d1bb4dc75ab45..27b265c330aa96 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5593,13 +5593,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
       !isa_and_nonnull<FunctionDecl>(TargetDecl))
     EmitKCFIOperandBundle(ConcreteCallee, BundleList);
 
-#if 0 // XXX Why is this here? Duplicate!
-  if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl))
-    if (FD->hasAttr<StrictFPAttr>())
-      // All calls within a strictfp function are marked strictfp
-      Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::StrictFP);
-#endif
-
   AssumeAlignedAttrEmitter AssumeAlignedAttrEmitter(*this, TargetDecl);
   Attrs = AssumeAlignedAttrEmitter.TryEmitAsCallSiteAttribute(Attrs);
 
diff --git a/clang/test/CodeGen/fp-floatcontrol-class.cpp b/clang/test/CodeGen/fp-floatcontrol-class.cpp
index c9953119fd3a22..d7cc3a1a12cfbe 100644
--- a/clang/test/CodeGen/fp-floatcontrol-class.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-class.cpp
@@ -1,4 +1,4 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 4
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 4
 // RUN: %clang_cc1 -ffp-contract=on -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
 // Verify that float_control does not pertain to initializer expressions
 
@@ -14,69 +14,76 @@ class OFF {
   float w = 2 + y() * z();
 };
 OFF off;
+// CHECK: Function Attrs: noinline nounwind
 // CHECK-LABEL: define internal void @__cxx_global_var_init(
 // CHECK-SAME: ) #[[ATTR0:[0-9]+]] section ".text.startup" {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @_ZN2ONC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @on) #[[ATTR6:[0-9]+]]
+// CHECK-NEXT:    call void @_ZN2ONC1Ev(%class.ON* noundef nonnull align 4 dereferenceable(4) @on)
 // CHECK-NEXT:    ret void
 //
 //
+// CHECK: Function Attrs: noinline nounwind optnone strictfp
 // CHECK-LABEL: define linkonce_odr void @_ZN2ONC1Ev(
-// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] comdat align 2 {
+// CHECK-SAME: %class.ON* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] comdat align 2 {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    call void @_ZN2ONC2Ev(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]]) #[[ATTR6]]
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.ON*, align 8
+// CHECK-NEXT:    store %class.ON* [[THIS]], %class.ON** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %class.ON*, %class.ON** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN2ONC2Ev(%class.ON* noundef nonnull align 4 dereferenceable(4) [[THIS1]])
 // CHECK-NEXT:    ret void
 //
 //
+// CHECK: Function Attrs: noinline nounwind
 // CHECK-LABEL: define internal void @__cxx_global_var_init.1(
 // CHECK-SAME: ) #[[ATTR0]] section ".text.startup" {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @_ZN3OFFC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @off)
+// CHECK-NEXT:    call void @_ZN3OFFC1Ev(%class.OFF* noundef nonnull align 4 dereferenceable(4) @off)
 // CHECK-NEXT:    ret void
 //
 //
+// CHECK: Function Attrs: noinline nounwind optnone
 // CHECK-LABEL: define linkonce_odr void @_ZN3OFFC1Ev(
-// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2:[0-9]+]] comdat align 2 {
+// CHECK-SAME: %class.OFF* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2:[0-9]+]] comdat align 2 {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    call void @_ZN3OFFC2Ev(ptr noundef nonnull align 4 dereferenceable(4) [[THIS1]])
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.OFF*, align 8
+// CHECK-NEXT:    store %class.OFF* [[THIS]], %class.OFF** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %class.OFF*, %class.OFF** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3OFFC2Ev(%class.OFF* noundef nonnull align 4 dereferenceable(4) [[THIS1]])
 // CHECK-NEXT:    ret void
 //
 //
+// CHECK: Function Attrs: noinline nounwind optnone strictfp
 // CHECK-LABEL: define linkonce_odr void @_ZN2ONC2Ev(
-// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1]] comdat align 2 {
+// CHECK-SAME: %class.ON* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1]] comdat align 2 {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_ON:%.*]], ptr [[THIS1]], i32 0, i32 0
-// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 2, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_Z1yv() #[[ATTR6]]
-// CHECK-NEXT:    [[CALL2:%.*]] = call noundef float @_Z1zv() #[[ATTR6]]
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.ON*, align 8
+// CHECK-NEXT:    store %class.ON* [[THIS]], %class.ON** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %class.ON*, %class.ON** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_ON:%.*]], %class.ON* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 2, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6:[0-9]+]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_Z1yv()
+// CHECK-NEXT:    [[CALL2:%.*]] = call noundef float @_Z1zv()
 // CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.experimental.constrained.fmuladd.f32(float [[CALL]], float [[CALL2]], float [[CONV]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6]]
-// CHECK-NEXT:    store float [[TMP0]], ptr [[W]], align 4
+// CHECK-NEXT:    store float [[TMP0]], float* [[W]], align 4
 // CHECK-NEXT:    ret void
 //
 //
+// CHECK: Function Attrs: noinline nounwind optnone
 // CHECK-LABEL: define linkonce_odr void @_ZN3OFFC2Ev(
-// CHECK-SAME: ptr noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2]] comdat align 2 {
+// CHECK-SAME: %class.OFF* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2]] comdat align 2 {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_OFF:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.OFF*, align 8
+// CHECK-NEXT:    store %class.OFF* [[THIS]], %class.OFF** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %class.OFF*, %class.OFF** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_OFF:%.*]], %class.OFF* [[THIS1]], i32 0, i32 0
 // CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_Z1yv()
 // CHECK-NEXT:    [[CALL2:%.*]] = call noundef float @_Z1zv()
 // CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.fmuladd.f32(float [[CALL]], float [[CALL2]], float 2.000000e+00)
-// CHECK-NEXT:    store float [[TMP0]], ptr [[W]], align 4
+// CHECK-NEXT:    store float [[TMP0]], float* [[W]], align 4
 // CHECK-NEXT:    ret void
 //
 //
+// CHECK: Function Attrs: noinline nounwind
 // CHECK-LABEL: define internal void @_GLOBAL__sub_I_fp_floatcontrol_class.cpp(
 // CHECK-SAME: ) #[[ATTR0]] section ".text.startup" {
 // CHECK-NEXT:  entry:

>From 823baf77a61fe1ef9e83635eba11912356316b17 Mon Sep 17 00:00:00 2001
From: "Kevin P. Neal" <kevin.neal at sas.com>
Date: Thu, 14 Dec 2023 14:29:23 -0500
Subject: [PATCH 3/5] Add a comment on the strictfp use of this test.

---
 clang/test/CodeGen/fp-floatcontrol-class.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/test/CodeGen/fp-floatcontrol-class.cpp b/clang/test/CodeGen/fp-floatcontrol-class.cpp
index d7cc3a1a12cfbe..6b890aac3a3d99 100644
--- a/clang/test/CodeGen/fp-floatcontrol-class.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-class.cpp
@@ -1,6 +1,7 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 4
 // RUN: %clang_cc1 -ffp-contract=on -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
 // Verify that float_control does not pertain to initializer expressions
+// Verify that the strictfp attribute is used when strictfp is enabled.
 
 float y();
 float z();

>From 37f6d7f4dc99c47cbfa487c6f23fc74c9d59741b Mon Sep 17 00:00:00 2001
From: "Kevin P. Neal" <kevin.neal at sas.com>
Date: Wed, 10 Jan 2024 13:29:19 -0500
Subject: [PATCH 4/5] Move comments as requested in review.

---
 clang/lib/CodeGen/CGCall.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 27b265c330aa96..60e6f5d2cb2b3f 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5509,8 +5509,8 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
                              /*IsThunk=*/false);
 
   if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl)) {
+    // All calls within a strictfp function are marked strictfp
     if (FD->hasAttr<StrictFPAttr>())
-      // All calls within a strictfp function are marked strictfp
       Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::StrictFP);
 
     // If -ffast-math is enabled and the function is guarded by an
@@ -5520,9 +5520,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
       CGM.AdjustMemoryAttribute(CalleePtr->getName(), Callee.getAbstractInfo(),
                                 Attrs);
   }
-  // We may not have a FunctionDecl*, but we still need to support strictfp.
+
+  // All calls within a strictfp function are marked strictfp
   if (Builder.getIsFPConstrained()) {
-    // All calls within a strictfp function are marked strictfp
     Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::StrictFP);
   }
 

>From 8b1ef1aebd8671b979e630e1550c8d53d250241c Mon Sep 17 00:00:00 2001
From: "Kevin P. Neal" <kevin.neal at sas.com>
Date: Tue, 16 Jan 2024 08:35:02 -0500
Subject: [PATCH 5/5] Update test to be more precise in what it tests.

---
 clang/test/CodeGen/fp-floatcontrol-class.cpp | 88 +++-----------------
 1 file changed, 10 insertions(+), 78 deletions(-)

diff --git a/clang/test/CodeGen/fp-floatcontrol-class.cpp b/clang/test/CodeGen/fp-floatcontrol-class.cpp
index 6b890aac3a3d99..787deb954c01f5 100644
--- a/clang/test/CodeGen/fp-floatcontrol-class.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-class.cpp
@@ -1,4 +1,3 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 4
 // RUN: %clang_cc1 -ffp-contract=on -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
 // Verify that float_control does not pertain to initializer expressions
 // Verify that the strictfp attribute is used when strictfp is enabled.
@@ -8,87 +7,20 @@ float z();
 #pragma float_control(except, on)
 class ON {
   float w = 2 + y() * z();
+  // CHECK-LABEL: define {{.*}} void @_ZN2ONC1Ev({{.*}})
+  // CHECK-SAME: #[[ATTR1:[0-9]+]]
+  // CHECK-LABEL: define {{.*}} @_ZN2ONC2Ev{{.*}})
+  // CHECK-SAME: #[[ATTR1]]
+  // CHECK: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict
 };
 ON on;
 #pragma float_control(except, off)
 class OFF {
   float w = 2 + y() * z();
+  // CHECK-LABEL: define {{.*}} @_ZN3OFFC2Ev{{.*}}
+  // CHECK-NOT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict
 };
 OFF off;
-// CHECK: Function Attrs: noinline nounwind
-// CHECK-LABEL: define internal void @__cxx_global_var_init(
-// CHECK-SAME: ) #[[ATTR0:[0-9]+]] section ".text.startup" {
-// CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @_ZN2ONC1Ev(%class.ON* noundef nonnull align 4 dereferenceable(4) @on)
-// CHECK-NEXT:    ret void
-//
-//
-// CHECK: Function Attrs: noinline nounwind optnone strictfp
-// CHECK-LABEL: define linkonce_odr void @_ZN2ONC1Ev(
-// CHECK-SAME: %class.ON* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] comdat align 2 {
-// CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.ON*, align 8
-// CHECK-NEXT:    store %class.ON* [[THIS]], %class.ON** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load %class.ON*, %class.ON** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    call void @_ZN2ONC2Ev(%class.ON* noundef nonnull align 4 dereferenceable(4) [[THIS1]])
-// CHECK-NEXT:    ret void
-//
-//
-// CHECK: Function Attrs: noinline nounwind
-// CHECK-LABEL: define internal void @__cxx_global_var_init.1(
-// CHECK-SAME: ) #[[ATTR0]] section ".text.startup" {
-// CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @_ZN3OFFC1Ev(%class.OFF* noundef nonnull align 4 dereferenceable(4) @off)
-// CHECK-NEXT:    ret void
-//
-//
-// CHECK: Function Attrs: noinline nounwind optnone
-// CHECK-LABEL: define linkonce_odr void @_ZN3OFFC1Ev(
-// CHECK-SAME: %class.OFF* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2:[0-9]+]] comdat align 2 {
-// CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.OFF*, align 8
-// CHECK-NEXT:    store %class.OFF* [[THIS]], %class.OFF** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load %class.OFF*, %class.OFF** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    call void @_ZN3OFFC2Ev(%class.OFF* noundef nonnull align 4 dereferenceable(4) [[THIS1]])
-// CHECK-NEXT:    ret void
-//
-//
-// CHECK: Function Attrs: noinline nounwind optnone strictfp
-// CHECK-LABEL: define linkonce_odr void @_ZN2ONC2Ev(
-// CHECK-SAME: %class.ON* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR1]] comdat align 2 {
-// CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.ON*, align 8
-// CHECK-NEXT:    store %class.ON* [[THIS]], %class.ON** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load %class.ON*, %class.ON** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_ON:%.*]], %class.ON* [[THIS1]], i32 0, i32 0
-// CHECK-NEXT:    [[CONV:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 2, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6:[0-9]+]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_Z1yv()
-// CHECK-NEXT:    [[CALL2:%.*]] = call noundef float @_Z1zv()
-// CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.experimental.constrained.fmuladd.f32(float [[CALL]], float [[CALL2]], float [[CONV]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR6]]
-// CHECK-NEXT:    store float [[TMP0]], float* [[W]], align 4
-// CHECK-NEXT:    ret void
-//
-//
-// CHECK: Function Attrs: noinline nounwind optnone
-// CHECK-LABEL: define linkonce_odr void @_ZN3OFFC2Ev(
-// CHECK-SAME: %class.OFF* noundef nonnull align 4 dereferenceable(4) [[THIS:%.*]]) unnamed_addr #[[ATTR2]] comdat align 2 {
-// CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %class.OFF*, align 8
-// CHECK-NEXT:    store %class.OFF* [[THIS]], %class.OFF** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[THIS1:%.*]] = load %class.OFF*, %class.OFF** [[THIS_ADDR]], align 8
-// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds [[CLASS_OFF:%.*]], %class.OFF* [[THIS1]], i32 0, i32 0
-// CHECK-NEXT:    [[CALL:%.*]] = call noundef float @_Z1yv()
-// CHECK-NEXT:    [[CALL2:%.*]] = call noundef float @_Z1zv()
-// CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.fmuladd.f32(float [[CALL]], float [[CALL2]], float 2.000000e+00)
-// CHECK-NEXT:    store float [[TMP0]], float* [[W]], align 4
-// CHECK-NEXT:    ret void
-//
-//
-// CHECK: Function Attrs: noinline nounwind
-// CHECK-LABEL: define internal void @_GLOBAL__sub_I_fp_floatcontrol_class.cpp(
-// CHECK-SAME: ) #[[ATTR0]] section ".text.startup" {
-// CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @__cxx_global_var_init()
-// CHECK-NEXT:    call void @__cxx_global_var_init.1()
-// CHECK-NEXT:    ret void
-//
+
+// CHECK: attributes #[[ATTR1]] = { {{.*}} strictfp {{.*}} }
+



More information about the cfe-commits mailing list