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

via cfe-commits cfe-commits at lists.llvm.org
Fri Dec 8 12:06:28 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Kevin P. Neal (kpneal)

<details>
<summary>Changes</summary>

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".

---
Full diff: https://github.com/llvm/llvm-project/pull/74883.diff


4 Files Affected:

- (modified) clang/include/clang/AST/ExprCXX.h (+4) 
- (modified) clang/lib/CodeGen/CGCall.cpp (+8) 
- (modified) clang/lib/CodeGen/CGDeclCXX.cpp (+5) 
- (modified) clang/test/CodeGen/fp-floatcontrol-class.cpp (+71-4) 


``````````diff
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
+//

``````````

</details>


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


More information about the cfe-commits mailing list