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.n...@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.n...@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.n...@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.n...@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.n...@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 {{.*}} } + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits