[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
Fri Dec 8 12:05:59 PST 2023


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

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

>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] [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 2427801643183..f9269082c3247 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 a24aeea7ae32b..7d1bb4dc75ab4 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 e08a1e5f42df2..5700b7fcb49d3 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 83a27cb206eb8..c9953119fd3a2 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
+//



More information about the cfe-commits mailing list