https://github.com/maarcosrmz updated https://github.com/llvm/llvm-project/pull/199009
>From d07bfefb40150eec2f5c094c8e5da9a8cfe1c9df Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Sun, 17 May 2026 22:43:47 +0200 Subject: [PATCH 1/8] Add clang warning if fp exception functions are called without appropriate flags/pragmas (#187860) Fixes https://github.com/llvm/llvm-project/issues/128239 The implementation adds warnings for floating-point exception function calls (fenv.h) made without enabling floating-point exception behavior via `-ffp-exception-behavior=maytrap/strict` or `#pragma STDC FENV_ACCESS ON`. To support recognition of all fenv.h builtins, `fexcept_t` and `fenv_t` were added as builtin types. --- clang/include/clang/AST/ASTContext.h | 35 +++++++++- clang/include/clang/Basic/BuiltinHeaders.def | 1 + clang/include/clang/Basic/Builtins.td | 55 +++++++++++++++ .../clang/Basic/DiagnosticSemaKinds.td | 5 ++ clang/include/clang/Basic/TokenKinds.def | 2 + clang/include/clang/Sema/Sema.h | 4 ++ .../include/clang/Serialization/ASTBitCodes.h | 8 ++- clang/lib/AST/ASTContext.cpp | 19 ++++++ clang/lib/Sema/SemaChecking.cpp | 18 +++++ clang/lib/Sema/SemaDecl.cpp | 8 +++ clang/lib/Serialization/ASTReader.cpp | 36 ++++++++++ clang/lib/Serialization/ASTWriter.cpp | 2 + clang/test/PCH/builtins-fenv.c | 25 +++++++ clang/test/PCH/builtins-fenv.h | 18 +++++ clang/test/Sema/builtin-fenv.c | 51 ++++++++++++++ clang/test/Sema/fenv-access-implicit.c | 35 ++++++++++ clang/test/Sema/fenv-access-unevaluated.cpp | 31 +++++++++ clang/test/Sema/fenv-access.c | 68 +++++++++++++++++++ clang/utils/TableGen/ClangBuiltinsEmitter.cpp | 2 + 19 files changed, 421 insertions(+), 2 deletions(-) create mode 100644 clang/test/PCH/builtins-fenv.c create mode 100644 clang/test/PCH/builtins-fenv.h create mode 100644 clang/test/Sema/builtin-fenv.c create mode 100644 clang/test/Sema/fenv-access-implicit.c create mode 100644 clang/test/Sema/fenv-access-unevaluated.cpp create mode 100644 clang/test/Sema/fenv-access.c diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index b2d407e412b3d..cf61ea41e2c50 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -521,6 +521,12 @@ class ASTContext : public RefCountedBase<ASTContext> { /// The type for the C ucontext_t type. TypeDecl *ucontext_tDecl = nullptr; + /// The type for the C fexcept_t type. + TypeDecl *fexcept_tDecl = nullptr; + + /// The type for the C fenv_t type. + TypeDecl *fenv_tDecl = nullptr; + /// Type for the Block descriptor for Blocks CodeGen. /// /// Since this is only used for generation of debug info, it is not @@ -2448,6 +2454,30 @@ class ASTContext : public RefCountedBase<ASTContext> { return QualType(); } + /// Set the type for the C fexcept_t type. + void setfexcept_tDecl(TypeDecl *fexcept_tDecl) { + this->fexcept_tDecl = fexcept_tDecl; + } + + /// Retrieve the C fexcept_t type. + QualType getfexcept_tType() const { + if (fexcept_tDecl) + return getTypeDeclType(ElaboratedTypeKeyword::None, + /*Qualifier=*/std::nullopt, fexcept_tDecl); + return QualType(); + } + + /// Set the type for the C fenv_t type. + void setfenv_tDecl(TypeDecl *fenv_tDecl) { this->fenv_tDecl = fenv_tDecl; } + + /// Retrieve the C fenv_t type. + QualType getfenv_tType() const { + if (fenv_tDecl) + return getTypeDeclType(ElaboratedTypeKeyword::None, + /*Qualifier=*/std::nullopt, fenv_tDecl); + return QualType(); + } + /// The result type of logical operations, '<', '>', '!=', etc. CanQualType getLogicalOperationType() const { return getLangOpts().CPlusPlus ? BoolTy : IntTy; @@ -2744,7 +2774,10 @@ class ASTContext : public RefCountedBase<ASTContext> { GE_Missing_setjmp, /// Missing a type from <ucontext.h> - GE_Missing_ucontext + GE_Missing_ucontext, + + /// Missing a type from <fenv.h> + GE_Missing_fenv }; QualType DecodeTypeStr(const char *&Str, const ASTContext &Context, diff --git a/clang/include/clang/Basic/BuiltinHeaders.def b/clang/include/clang/Basic/BuiltinHeaders.def index b18e470a8bd25..d47803aafafa7 100644 --- a/clang/include/clang/Basic/BuiltinHeaders.def +++ b/clang/include/clang/Basic/BuiltinHeaders.def @@ -17,6 +17,7 @@ HEADER(BLOCKS_H, "Blocks.h") HEADER(COMPLEX_H, "complex.h") HEADER(CTYPE_H, "ctype.h") HEADER(EMMINTRIN_H, "emmintrin.h") +HEADER(FENV_H, "fenv.h") HEADER(FOUNDATION_NSOBJCRUNTIME_H, "Foundation/NSObjCRuntime.h") HEADER(IMMINTRIN_H, "immintrin.h") HEADER(INTRIN_H, "intrin.h") diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 90340ad7f812c..103f49d743bec 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4698,6 +4698,61 @@ def BlockObjectDispose : LibBuiltin<"blocks.h"> { } // FIXME: Also declare NSConcreteGlobalBlock and NSConcreteStackBlock. +def FeClearExcept : LibBuiltin<"fenv.h"> { + let Spellings = ["feclearexcept"]; + let Prototype = "int(int)"; +} + +def FeGetExceptFlag : LibBuiltin<"fenv.h"> { + let Spellings = ["fegetexceptflag"]; + let Prototype = "int(fexcept_t*, int)"; +} + +def FeRaiseExcept : LibBuiltin<"fenv.h"> { + let Spellings = ["feraiseexcept"]; + let Prototype = "int(int)"; +} + +def FeSetExceptFlag : LibBuiltin<"fenv.h"> { + let Spellings = ["fesetexceptflag"]; + let Prototype = "int(fexcept_t const*, int)"; +} + +def FeTestExcept : LibBuiltin<"fenv.h"> { + let Spellings = ["fetestexcept"]; + let Prototype = "int(int)"; +} + +def FeGetRound : LibBuiltin<"fenv.h"> { + let Spellings = ["fegetround"]; + let Prototype = "int()"; +} + +def FeSetRound : LibBuiltin<"fenv.h"> { + let Spellings = ["fesetround"]; + let Prototype = "int(int)"; +} + +def FeGetEnv : LibBuiltin<"fenv.h"> { + let Spellings = ["fegetenv"]; + let Prototype = "int(fenv_t*)"; +} + +def FeHoldExcept : LibBuiltin<"fenv.h"> { + let Spellings = ["feholdexcept"]; + let Prototype = "int(fenv_t*)"; +} + +def FeSetEnv : LibBuiltin<"fenv.h"> { + let Spellings = ["fesetenv"]; + let Prototype = "int(fenv_t const*)"; +} + +def FeUpdateEnv : LibBuiltin<"fenv.h"> { + let Spellings = ["feupdateenv"]; + let Prototype = "int(fenv_t const*)"; +} + def __Addressof : LangBuiltin<"CXX_LANG"> { let Spellings = ["__addressof"]; let Attributes = [FunctionWithoutBuiltinPrefix, NoThrow, Const, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fca68f292f667..48a86f3676afc 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1093,6 +1093,11 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error< "%select{subtraction|addition}0 of address-of-label expressions is not " "supported with ptrauth indirect gotos">; +def warn_fe_access_without_fenv_access : Warning< + "'%0' used without enabling floating-point exception behavior; use 'pragma STDC " + "FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'">, + InGroup<DiagGroup<"fenv-access">>; + // __ptrauth qualifier def err_ptrauth_qualifier_invalid : Error< "%select{return type|parameter type|property}1 may not be qualified with " diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index dc9c7d8109467..59c0b98365e20 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -730,6 +730,8 @@ NOTABLE_IDENTIFIER(FILE) NOTABLE_IDENTIFIER(jmp_buf) NOTABLE_IDENTIFIER(sigjmp_buf) NOTABLE_IDENTIFIER(ucontext_t) +NOTABLE_IDENTIFIER(fexcept_t) +NOTABLE_IDENTIFIER(fenv_t) NOTABLE_IDENTIFIER(float_t) NOTABLE_IDENTIFIER(double_t) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5becfc9fae152..681bf3a451a5a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8231,6 +8231,10 @@ class Sema final : public SemaBase { return currentEvaluationContext().isUnevaluated(); } + bool isPotentiallyEvaluatedContext() const { + return currentEvaluationContext().isPotentiallyEvaluated(); + } + bool isImmediateFunctionContext() const { return currentEvaluationContext().isImmediateFunctionContext(); } diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 6a52a9e4fa780..71dee22488a96 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1220,7 +1220,13 @@ enum SpecialTypeIDs { SPECIAL_TYPE_OBJC_SEL_REDEFINITION = 6, /// C ucontext_t typedef type - SPECIAL_TYPE_UCONTEXT_T = 7 + SPECIAL_TYPE_UCONTEXT_T = 7, + + /// C fexcept_t typedef type + SPECIAL_TYPE_FEXCEPT_T = 8, + + /// C fenv_t typedef type + SPECIAL_TYPE_FENV_T = 9 }; /// The number of special type IDs. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index ee8663e6906af..e20419b8b6446 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12962,6 +12962,25 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, case 'm': Type = Context.MFloat8Ty; break; + case 'T': + switch (*Str++) { + case 'x': { + Type = Context.getfexcept_tType(); + break; + } + case 'e': { + Type = Context.getfenv_tType(); + break; + } + default: { + llvm_unreachable("Unexpected target builtin type"); + } + } + if (Type.isNull()) { + Error = ASTContext::GE_Missing_fenv; + return {}; + } + break; } // If there are modifiers and if we're allowed to parse them, go for it. diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index ea8f3babcb0e0..f668305b3dcbf 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4190,6 +4190,24 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, if (BuiltinCountedByRef(TheCall)) return ExprError(); break; + + case Builtin::BIfeclearexcept: + case Builtin::BIfegetexceptflag: + case Builtin::BIferaiseexcept: + case Builtin::BIfesetexceptflag: + case Builtin::BIfetestexcept: + case Builtin::BIfegetround: + case Builtin::BIfesetround: + case Builtin::BIfegetenv: + case Builtin::BIfeholdexcept: + case Builtin::BIfesetenv: + case Builtin::BIfeupdateenv: + if (TheCall->getFPFeaturesInEffect(getLangOpts()).getExceptionMode() == + LangOptions::FPE_Ignore && + isPotentiallyEvaluatedContext()) { + Diag(TheCall->getBeginLoc(), diag::warn_fe_access_without_fenv_access) + << FDecl->getName() << TheCall->getSourceRange(); + } } if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall)) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e52f99b4cd98d..0a6684901c2b5 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2410,6 +2410,8 @@ static StringRef getHeaderName(Builtin::Context &BuiltinInfo, unsigned ID, return "setjmp.h"; case ASTContext::GE_Missing_ucontext: return "ucontext.h"; + case ASTContext::GE_Missing_fenv: + return "fenv.h"; } llvm_unreachable("unhandled error kind"); } @@ -7080,6 +7082,12 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD, case tok::NotableIdentifierKind::ucontext_t: Context.setucontext_tDecl(NewTD); break; + case tok::NotableIdentifierKind::fexcept_t: + Context.setfexcept_tDecl(NewTD); + break; + case tok::NotableIdentifierKind::fenv_t: + Context.setfenv_tDecl(NewTD); + break; case tok::NotableIdentifierKind::float_t: case tok::NotableIdentifierKind::double_t: NewTD->addAttr(AvailableOnlyInDefaultEvalMethodAttr::Create(Context)); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 76b3ecb8f96db..bed519d2b019d 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -5774,6 +5774,42 @@ void ASTReader::InitializeContext() { } } } + + if (TypeID Fexcept_t = SpecialTypes[SPECIAL_TYPE_FEXCEPT_T]) { + QualType Fexcept_tType = GetType(Fexcept_t); + if (Fexcept_tType.isNull()) { + Error("fexcept_t type is NULL"); + return; + } + + if (!Context.fexcept_tDecl) { + if (const TypedefType *Typedef = Fexcept_tType->getAs<TypedefType>()) + Context.setfexcept_tDecl(Typedef->getDecl()); + else { + const TagType *Tag = Fexcept_tType->getAs<TagType>(); + assert(Tag && "Invalid fexcept_t type in AST file"); + Context.setfexcept_tDecl(Tag->getDecl()); + } + } + } + + if (TypeID Fenv_t = SpecialTypes[SPECIAL_TYPE_FENV_T]) { + QualType Fenv_tType = GetType(Fenv_t); + if (Fenv_tType.isNull()) { + Error("fenv_t type is NULL"); + return; + } + + if (!Context.fenv_tDecl) { + if (const TypedefType *Typedef = Fenv_tType->getAs<TypedefType>()) + Context.setfenv_tDecl(Typedef->getDecl()); + else { + const TagType *Tag = Fenv_tType->getAs<TagType>(); + assert(Tag && "Invalid fenv_t type in AST file"); + Context.setfenv_tDecl(Tag->getDecl()); + } + } + } } ReadPragmaDiagnosticMappings(Context.getDiagnostics()); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 944e68ea6481d..e8e5dddfd0033 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -6162,6 +6162,8 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema *SemaPtr, StringRef isysroot, AddTypeRef(Context, Context.ObjCClassRedefinitionType, SpecialTypes); AddTypeRef(Context, Context.ObjCSelRedefinitionType, SpecialTypes); AddTypeRef(Context, Context.getucontext_tType(), SpecialTypes); + AddTypeRef(Context, Context.getfexcept_tType(), SpecialTypes); + AddTypeRef(Context, Context.getfenv_tType(), SpecialTypes); } if (SemaPtr) diff --git a/clang/test/PCH/builtins-fenv.c b/clang/test/PCH/builtins-fenv.c new file mode 100644 index 0000000000000..72bcf4a134730 --- /dev/null +++ b/clang/test/PCH/builtins-fenv.c @@ -0,0 +1,25 @@ +// Test this without pch. +// RUN: %clang_cc1 -include %S/builtins-fenv.h -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -emit-pch -o %t %S/builtins-fenv.h +// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s + +// expected-no-diagnostics +fexcept_t *flagp = 0; +fenv_t *envp = 0; + +void f(void) { + #pragma STDC FENV_ACCESS ON + feclearexcept(FE_INVALID); + fegetexceptflag(flagp, FE_INVALID); + feraiseexcept(FE_INVALID); + fesetexceptflag(flagp, FE_INVALID); + fetestexcept(FE_INVALID); + fegetround(); + fesetround(0); + fegetenv(envp); + feholdexcept(envp); + fesetenv(envp); + feupdateenv(envp); +} diff --git a/clang/test/PCH/builtins-fenv.h b/clang/test/PCH/builtins-fenv.h new file mode 100644 index 0000000000000..8397c270df58e --- /dev/null +++ b/clang/test/PCH/builtins-fenv.h @@ -0,0 +1,18 @@ +// Header for PCH test builtins-fenv.c + +#define FE_INVALID 1 + +typedef struct {} fenv_t; +typedef unsigned short int fexcept_t; + +int feclearexcept(int excepts); +int fegetexceptflag(fexcept_t *flagp, int excepts); +int feraiseexcept(int excepts); +int fesetexceptflag(const fexcept_t *flagp, int excepts); +int fetestexcept(int excepts); +int fegetround(void); +int fesetround(int rounding_mode); +int fegetenv(fenv_t *envp); +int feholdexcept(fenv_t *envp); +int fesetenv(const fenv_t *envp); +int feupdateenv(const fenv_t *envp); diff --git a/clang/test/Sema/builtin-fenv.c b/clang/test/Sema/builtin-fenv.c new file mode 100644 index 0000000000000..db8a5334d1073 --- /dev/null +++ b/clang/test/Sema/builtin-fenv.c @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DWRONG_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1,CHECK2 +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DRIGHT_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1,CHECK2 +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DONLY_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1,CHECK2 +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DNO_FEGETEXCEPTFLAG %s -ast-dump 2>&1 | FileCheck %s --check-prefixes=CHECK1 + +// tests inspired by clang/test/Sema/builtin-setjmp.c + +#ifdef __cplusplus +extern "C" { +#endif + +#if WRONG_FEXCEPT_T +typedef unsigned short int fexcept_t; +extern int fegetexceptflag(int, int); // c-warning {{incompatible redeclaration of library function 'fegetexceptflag'}} + // c-note@-1 {{'fegetexceptflag' is a builtin with type 'int (fexcept_t *, int)' (aka 'int (unsigned short *, int)')}} +#elif RIGHT_FEXCEPT_T +// c-no-diagnostics +typedef unsigned short int fexcept_t; +extern int fegetexceptflag(unsigned short int *, int); // OK, right type. +#elif ONLY_FEXCEPT_T +typedef long *fexcept_t; +#endif + +void use(void) { + #pragma STDC FENV_ACCESS ON + fegetexceptflag(0, 0); + #if NO_FEGETEXCEPTFLAG + // cxx-error@-2 {{undeclared identifier 'fegetexceptflag'}} + // c-error@-3 {{call to undeclared function 'fegetexceptflag'; ISO C99 and later do not support implicit function declarations}} + // c-warning@-4 {{declaration of built-in function 'fegetexceptflag' requires inclusion of the header <fenv.h>}} + #elif ONLY_FEXCEPT_T + // cxx-error@-6 {{undeclared identifier 'fegetexceptflag'}} + // c-error@-7 {{call to undeclared library function 'fegetexceptflag' with type 'int (fexcept_t *, int)' (aka 'int (long **, int)'); ISO C99 and later do not support implicit function declarations}} + // c-note@-8 {{include the header <fenv.h> or explicitly provide a declaration for 'fegetexceptflag'}} + #else + // cxx-no-diagnostics + #endif + + #ifdef NO_FEGETEXCEPTFLAG + // In this case, the regular AST dump doesn't dump the implicit declaration of 'fegetexceptflag'. + #pragma clang __debug dump fegetexceptflag + #endif +} + +// CHECK1: FunctionDecl {{.*}} used fegetexceptflag +// CHECK2: BuiltinAttr {{.*}} Implicit + + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/Sema/fenv-access-implicit.c b/clang/test/Sema/fenv-access-implicit.c new file mode 100644 index 0000000000000..0c4bd6b0eb855 --- /dev/null +++ b/clang/test/Sema/fenv-access-implicit.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -verify -Wfenv-access %s + +typedef struct {} fenv_t; +typedef unsigned short int fexcept_t; + +fexcept_t *flagp = 0; +fenv_t *envp = 0; + +#define FE_INVALID 1 + +void test_fenv_access_undeclared(void) { + #pragma STDC FENV_ACCESS ON + feclearexcept(FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feclearexcept'}} \ + expected-error {{call to undeclared library function 'feclearexcept' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}} + fegetexceptflag(flagp, FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fegetexceptflag'}} \ + expected-error {{call to undeclared library function 'fegetexceptflag' with type 'int (fexcept_t *, int)' (aka 'int (unsigned short *, int)'); ISO C99 and later do not support implicit function declarations}} + feraiseexcept(FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feraiseexcept'}} \ + expected-error {{call to undeclared library function 'feraiseexcept' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}} + fesetexceptflag(flagp, FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fesetexceptflag'}} \ + expected-error {{call to undeclared library function 'fesetexceptflag' with type 'int (const fexcept_t *, int)' (aka 'int (const unsigned short *, int)'); ISO C99 and later do not support implicit function declarations}} + fetestexcept(FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fetestexcept'}} \ + expected-error {{call to undeclared library function 'fetestexcept' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}} + fegetround(); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fegetround'}} \ + expected-error {{call to undeclared library function 'fegetround' with type 'int (void)'; ISO C99 and later do not support implicit function declarations}} + fesetround(0); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fesetround'}} \ + expected-error {{call to undeclared library function 'fesetround' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}} + fegetenv(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fegetenv'}} \ + expected-error {{call to undeclared library function 'fegetenv' with type 'int (fenv_t *)'; ISO C99 and later do not support implicit function declarations}} + feholdexcept(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feholdexcept'}} \ + expected-error {{call to undeclared library function 'feholdexcept' with type 'int (fenv_t *)'; ISO C99 and later do not support implicit function declarations}} + fesetenv(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fesetenv'}} \ + expected-error {{call to undeclared library function 'fesetenv' with type 'int (const fenv_t *)'; ISO C99 and later do not support implicit function declarations}} + feupdateenv(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feupdateenv'}} \ + expected-error {{call to undeclared library function 'feupdateenv' with type 'int (const fenv_t *)'; ISO C99 and later do not support implicit function declarations}} +} diff --git a/clang/test/Sema/fenv-access-unevaluated.cpp b/clang/test/Sema/fenv-access-unevaluated.cpp new file mode 100644 index 0000000000000..14752ba3c377e --- /dev/null +++ b/clang/test/Sema/fenv-access-unevaluated.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -verify -Wfenv-access %s + +typedef struct {} fenv_t; +typedef unsigned short int fexcept_t; + +int feclearexcept(int excepts); +int fegetexceptflag(fexcept_t *flagp, int excepts); +int feraiseexcept(int excepts); +int fesetexceptflag(const fexcept_t *flagp, int excepts); +int fetestexcept(int excepts); +int fegetround(void); +int fesetround(int rounding_mode); +int fegetenv(fenv_t *envp); +int feholdexcept(fenv_t *envp); +int fesetenv(const fenv_t *envp); +int feupdateenv(const fenv_t *envp); + +// expected-no-diagnostics +void test_fenv_access_unevaluated() { + decltype(::feclearexcept) a; + decltype(::fegetexceptflag) b; + decltype(::feraiseexcept) c; + decltype(::fesetexceptflag) d; + decltype(::fetestexcept) e; + decltype(::fegetround) f; + decltype(::fesetround) g; + decltype(::fegetenv) h; + decltype(::feholdexcept) i; + decltype(::fesetenv) j; + decltype(::feupdateenv) k; +} diff --git a/clang/test/Sema/fenv-access.c b/clang/test/Sema/fenv-access.c new file mode 100644 index 0000000000000..3a7b95af7ab4c --- /dev/null +++ b/clang/test/Sema/fenv-access.c @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -verify -Wfenv-access %s +// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=maytrap -DNO_WARN %s +// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=strict -DNO_WARN %s + +typedef struct {} fenv_t; +typedef unsigned short int fexcept_t; + +int feclearexcept(int excepts); +int fegetexceptflag(fexcept_t *flagp, int excepts); +int feraiseexcept(int excepts); +int fesetexceptflag(const fexcept_t *flagp, int excepts); +int fetestexcept(int excepts); +int fegetround(void); +int fesetround(int rounding_mode); +int fegetenv(fenv_t *envp); +int feholdexcept(fenv_t *envp); +int fesetenv(const fenv_t *envp); +int feupdateenv(const fenv_t *envp); + +#define FE_INVALID 1 + +fexcept_t *flagp = 0; +fenv_t *envp = 0; + +void test_fenv_access_off(void) { +#ifdef NO_WARN + // expected-no-diagnostics + feclearexcept(FE_INVALID); + fegetexceptflag(flagp, FE_INVALID); + feraiseexcept(FE_INVALID); + fesetexceptflag(flagp, FE_INVALID); + fetestexcept(FE_INVALID); + fegetround(); + fesetround(0); + fegetenv(envp); + feholdexcept(envp); + fesetenv(envp); + feupdateenv(envp); +#else + feclearexcept(FE_INVALID); // expected-warning {{'feclearexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + fegetexceptflag(flagp, FE_INVALID); // expected-warning {{'fegetexceptflag' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + feraiseexcept(FE_INVALID); // expected-warning {{'feraiseexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + fesetexceptflag(flagp, FE_INVALID); // expected-warning {{'fesetexceptflag' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + fetestexcept(FE_INVALID); // expected-warning {{'fetestexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + fegetround(); // expected-warning {{'fegetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + fesetround(0); // expected-warning {{'fesetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + fegetenv(envp); // expected-warning {{'fegetenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + feholdexcept(envp); // expected-warning {{'feholdexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + fesetenv(envp); // expected-warning {{'fesetenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} + feupdateenv(envp); // expected-warning {{'feupdateenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} +#endif +} + +void test_fenv_access_on(void) { + #pragma STDC FENV_ACCESS ON + fesetround(0); + feclearexcept(FE_INVALID); + fegetexceptflag(flagp, FE_INVALID); + feraiseexcept(FE_INVALID); + fesetexceptflag(flagp, FE_INVALID); + fetestexcept(FE_INVALID); + fegetround(); + fesetround(0); + fegetenv(envp); + feholdexcept(envp); + fesetenv(envp); + feupdateenv(envp); +} diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp index 22c81522f9e41..3c378fe8bd746 100644 --- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp +++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp @@ -383,6 +383,8 @@ class PrototypeParser { .Case("uint64_t", "UWi") .Case("void", "v") .Case("wchar_t", "w") + .Case("fexcept_t", "Tx") + .Case("fenv_t", "Te") .Case("...", ".") .Default("error"); if (ReturnTypeVal == "error") >From b4b6f9ed5f3af2c20aaf1467007592f0b1751253 Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Thu, 21 May 2026 15:10:06 +0200 Subject: [PATCH 2/8] Added check for strict FP support before warning --- clang/lib/Sema/SemaChecking.cpp | 4 +++- clang/test/Sema/fenv-access.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index f668305b3dcbf..2d76279ca20e3 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4204,7 +4204,9 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, case Builtin::BIfeupdateenv: if (TheCall->getFPFeaturesInEffect(getLangOpts()).getExceptionMode() == LangOptions::FPE_Ignore && - isPotentiallyEvaluatedContext()) { + isPotentiallyEvaluatedContext() && + (getASTContext().getTargetInfo().hasStrictFP() || + getLangOpts().ExpStrictFP)) { Diag(TheCall->getBeginLoc(), diag::warn_fe_access_without_fenv_access) << FDecl->getName() << TheCall->getSourceRange(); } diff --git a/clang/test/Sema/fenv-access.c b/clang/test/Sema/fenv-access.c index 3a7b95af7ab4c..50a4d9fd16aa2 100644 --- a/clang/test/Sema/fenv-access.c +++ b/clang/test/Sema/fenv-access.c @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -verify -Wfenv-access %s // RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=maytrap -DNO_WARN %s // RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=strict -DNO_WARN %s +// RUN: %clang_cc1 -verify -Wfenv-access -triple armv7-linux-gnueabihf -DNO_WARN -DUNSUPPORTED %s typedef struct {} fenv_t; typedef unsigned short int fexcept_t; @@ -52,7 +53,9 @@ void test_fenv_access_off(void) { } void test_fenv_access_on(void) { +#ifndef UNSUPPORTED #pragma STDC FENV_ACCESS ON +#endif fesetround(0); feclearexcept(FE_INVALID); fegetexceptflag(flagp, FE_INVALID); >From 98e2b0e3efb8baf5bab91f9fa8ad4bb76a2b59b1 Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Thu, 21 May 2026 15:19:25 +0200 Subject: [PATCH 3/8] Removed redundant extern "C" {} block in builtin-fenv.c --- clang/test/Sema/builtin-fenv.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/clang/test/Sema/builtin-fenv.c b/clang/test/Sema/builtin-fenv.c index db8a5334d1073..c9b2306326326 100644 --- a/clang/test/Sema/builtin-fenv.c +++ b/clang/test/Sema/builtin-fenv.c @@ -5,10 +5,6 @@ // tests inspired by clang/test/Sema/builtin-setjmp.c -#ifdef __cplusplus -extern "C" { -#endif - #if WRONG_FEXCEPT_T typedef unsigned short int fexcept_t; extern int fegetexceptflag(int, int); // c-warning {{incompatible redeclaration of library function 'fegetexceptflag'}} @@ -44,8 +40,3 @@ void use(void) { // CHECK1: FunctionDecl {{.*}} used fegetexceptflag // CHECK2: BuiltinAttr {{.*}} Implicit - - -#ifdef __cplusplus -} -#endif >From a36b202bb51d3932a6bc77bc4dfd0c6d0aad8a4a Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Sat, 1 Aug 2026 20:24:57 +0200 Subject: [PATCH 4/8] Added release notes to new clang/docs/ReleaseNotes.md file --- clang/docs/ReleaseNotes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 7e3e8468914c7..47b4d4bcd952c 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -518,6 +518,9 @@ features cannot lower the translation-unit ABI level; - Improve Clang diagnoses when unary `__imag` operator with non-complex type operand is used as lvalue. (GH222383) +- Added warnings for floating-point exception function calls (fenv.h) without enabling floating-point + exception behavior via the appropriate flags or pragmas on supported targets. (#GH128239) + ### Improvements to Clang's time-trace ### Improvements to Coverage Mapping >From 2295cc08007938c62731e49515daadacdeeff1f5 Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Wed, 1 Jul 2026 15:55:12 +0200 Subject: [PATCH 5/8] Fix conflict in serialization of fenv_t and fexcept_t builtin types --- clang/lib/AST/ASTContext.cpp | 21 +++++++------------ clang/utils/TableGen/ClangBuiltinsEmitter.cpp | 4 ++-- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index e20419b8b6446..9db7508d0483e 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12962,20 +12962,15 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, case 'm': Type = Context.MFloat8Ty; break; - case 'T': - switch (*Str++) { - case 'x': { - Type = Context.getfexcept_tType(); - break; - } - case 'e': { - Type = Context.getfenv_tType(); - break; - } - default: { - llvm_unreachable("Unexpected target builtin type"); - } + case 't': + Type = Context.getfexcept_tType(); + if (Type.isNull()) { + Error = ASTContext::GE_Missing_fenv; + return {}; } + break; + case 'e': + Type = Context.getfenv_tType(); if (Type.isNull()) { Error = ASTContext::GE_Missing_fenv; return {}; diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp index 3c378fe8bd746..77b9259ffebf0 100644 --- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp +++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp @@ -383,8 +383,8 @@ class PrototypeParser { .Case("uint64_t", "UWi") .Case("void", "v") .Case("wchar_t", "w") - .Case("fexcept_t", "Tx") - .Case("fenv_t", "Te") + .Case("fexcept_t", "t") + .Case("fenv_t", "e") .Case("...", ".") .Default("error"); if (ReturnTypeVal == "error") >From 8ee4019f5825588210de88072c967cb3aa02f3a5 Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Sun, 9 Aug 2026 18:48:44 +0200 Subject: [PATCH 6/8] Make warning gcc compatible & add rounding mode check to warning condition --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 6 +++--- clang/lib/Sema/SemaChecking.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 48a86f3676afc..37b2a4a8b1ac4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1093,9 +1093,9 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error< "%select{subtraction|addition}0 of address-of-label expressions is not " "supported with ptrauth indirect gotos">; -def warn_fe_access_without_fenv_access : Warning< - "'%0' used without enabling floating-point exception behavior; use 'pragma STDC " - "FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'">, +def warn_fenv_access_no_exception_semantics_or_rounding_mode : Warning< + "'%0' used without enabling strict floating-point exception semantics and dynamic rounding mode;" + " use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'">, InGroup<DiagGroup<"fenv-access">>; // __ptrauth qualifier diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 2d76279ca20e3..5c0579755796a 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4202,12 +4202,14 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, case Builtin::BIfeholdexcept: case Builtin::BIfesetenv: case Builtin::BIfeupdateenv: - if (TheCall->getFPFeaturesInEffect(getLangOpts()).getExceptionMode() == - LangOptions::FPE_Ignore && + const FPOptions &fpOpts = TheCall->getFPFeaturesInEffect(getLangOpts()); + if ((fpOpts.getExceptionMode() == LangOptions::FPE_Ignore || + fpOpts.getRoundingMode() != LangOptions::RoundingMode::Dynamic) && isPotentiallyEvaluatedContext() && (getASTContext().getTargetInfo().hasStrictFP() || getLangOpts().ExpStrictFP)) { - Diag(TheCall->getBeginLoc(), diag::warn_fe_access_without_fenv_access) + Diag(TheCall->getBeginLoc(), + diag::warn_fenv_access_no_exception_semantics_or_rounding_mode) << FDecl->getName() << TheCall->getSourceRange(); } } >From 821c7b71054f8d704f65f5aa40601adf2e1fbcad Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Sun, 9 Aug 2026 19:13:05 +0200 Subject: [PATCH 7/8] Fix Sema test for fenv-access warning --- clang/test/Sema/fenv-access.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/clang/test/Sema/fenv-access.c b/clang/test/Sema/fenv-access.c index 50a4d9fd16aa2..2d7d0aa028192 100644 --- a/clang/test/Sema/fenv-access.c +++ b/clang/test/Sema/fenv-access.c @@ -1,6 +1,8 @@ // RUN: %clang_cc1 -verify -Wfenv-access %s -// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=maytrap -DNO_WARN %s -// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=strict -DNO_WARN %s +// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=strict %s +// RUN: %clang_cc1 -verify -Wfenv-access -frounding-math %s +// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=strict -frounding-math -DNO_WARN %s +// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=maytrap -frounding-math -DNO_WARN %s // RUN: %clang_cc1 -verify -Wfenv-access -triple armv7-linux-gnueabihf -DNO_WARN -DUNSUPPORTED %s typedef struct {} fenv_t; @@ -37,18 +39,18 @@ void test_fenv_access_off(void) { feholdexcept(envp); fesetenv(envp); feupdateenv(envp); -#else - feclearexcept(FE_INVALID); // expected-warning {{'feclearexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - fegetexceptflag(flagp, FE_INVALID); // expected-warning {{'fegetexceptflag' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - feraiseexcept(FE_INVALID); // expected-warning {{'feraiseexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - fesetexceptflag(flagp, FE_INVALID); // expected-warning {{'fesetexceptflag' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - fetestexcept(FE_INVALID); // expected-warning {{'fetestexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - fegetround(); // expected-warning {{'fegetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - fesetround(0); // expected-warning {{'fesetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - fegetenv(envp); // expected-warning {{'fegetenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - feholdexcept(envp); // expected-warning {{'feholdexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - fesetenv(envp); // expected-warning {{'fesetenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} - feupdateenv(envp); // expected-warning {{'feupdateenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}} +#else + feclearexcept(FE_INVALID); // expected-warning {{'feclearexcept' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + fegetexceptflag(flagp, FE_INVALID); // expected-warning {{'fegetexceptflag' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + feraiseexcept(FE_INVALID); // expected-warning {{'feraiseexcept' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + fesetexceptflag(flagp, FE_INVALID); // expected-warning {{'fesetexceptflag' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + fetestexcept(FE_INVALID); // expected-warning {{'fetestexcept' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + fegetround(); // expected-warning {{'fegetround' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + fesetround(0); // expected-warning {{'fesetround' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + fegetenv(envp); // expected-warning {{'fegetenv' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + feholdexcept(envp); // expected-warning {{'feholdexcept' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + fesetenv(envp); // expected-warning {{'fesetenv' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} + feupdateenv(envp); // expected-warning {{'feupdateenv' used without enabling strict floating-point exception semantics and dynamic rounding mode; use '#pragma STDC FENV_ACCESS ON' or compile with '-ftrapping-math -frounding-math'}} #endif } >From eeb4094c7930fbc38f3f8fbb3e1c637cfa4b581a Mon Sep 17 00:00:00 2001 From: Marcos Ramirez Joos <[email protected]> Date: Fri, 18 Sep 2026 22:12:45 +0100 Subject: [PATCH 8/8] Add build configuration-time check for FP access pragma --- flang-rt/CMakeLists.txt | 13 ----------- flang-rt/lib/runtime/edit-input.cpp | 2 ++ flang-rt/lib/runtime/exceptions.cpp | 6 +++--- flang-rt/unittests/Runtime/Exceptions.cpp | 8 +++---- flang/cmake/modules/FlangCommon.cmake | 25 ++++++++++++++++++++++ flang/include/flang/Common/fp-control.h | 20 +++++++---------- flang/lib/Evaluate/host.cpp | 3 +++ llvm/cmake/modules/HandleLLVMOptions.cmake | 25 ++++++++++++++++++++++ llvm/include/llvm/Support/Compiler.h | 6 ++++++ llvm/lib/Analysis/ConstantFolding.cpp | 2 ++ 10 files changed, 78 insertions(+), 32 deletions(-) diff --git a/flang-rt/CMakeLists.txt b/flang-rt/CMakeLists.txt index 4f480151eb56b..0e4fb82139950 100644 --- a/flang-rt/CMakeLists.txt +++ b/flang-rt/CMakeLists.txt @@ -171,19 +171,6 @@ if (FLANG_RT_INCLUDE_CUF) endif () endif() -# A recently added check in clang emits warnings for feclearexcept and fesetround: -# 'fesetround' used without enabling floating-point exception behavior; use -# 'pragma STDC FENV_ACCESS ON' or compile with -# '-ffp-exception-behavior=maytrap' [-Werror,-Wfenv-access] -# This breaks the CI build (because of -Werror). -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - check_cxx_compiler_flag("-Werror -Wno-fenv-access" CXX_SUPPORTS_NO_FENV_ACCESS_FLAG) - if (CXX_SUPPORTS_NO_FENV_ACCESS_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-fenv-access") - endif() -endif() # Clang - - ######################## # System Introspection # ######################## diff --git a/flang-rt/lib/runtime/edit-input.cpp b/flang-rt/lib/runtime/edit-input.cpp index 6a05974893e51..a363af2aa736c 100644 --- a/flang-rt/lib/runtime/edit-input.cpp +++ b/flang-rt/lib/runtime/edit-input.cpp @@ -9,6 +9,7 @@ #include "edit-input.h" #include "flang-rt/runtime/namelist.h" #include "flang-rt/runtime/utf.h" +#include "flang/Common/fp-control.h" #include "flang/Common/optional.h" #include "flang/Common/real.h" #include "flang/Common/uint128.h" @@ -559,6 +560,7 @@ static RT_API_ATTRS ScannedRealInput ScanRealInput( static RT_API_ATTRS void RaiseFPExceptions( decimal::ConversionResultFlags flags) { + FLANG_FENV_ACCESS_ON #undef RAISE #if defined(RT_DEVICE_COMPILATION) Terminator terminator(__FILE__, __LINE__); diff --git a/flang-rt/lib/runtime/exceptions.cpp b/flang-rt/lib/runtime/exceptions.cpp index 33cb9945c863a..2f02de5624abc 100644 --- a/flang-rt/lib/runtime/exceptions.cpp +++ b/flang-rt/lib/runtime/exceptions.cpp @@ -89,7 +89,7 @@ uint32_t RTDEF(MapException)(uint32_t excepts) { // component; both are needed. void RTNAME(feclearexcept)(uint32_t excepts) { - FLANG_FP_TRAP_ON + FLANG_FENV_ACCESS_ON feclearexcept(excepts); #if defined(_MM_EXCEPT_DENORM) _mm_setcsr(_mm_getcsr() & ~(excepts & _MM_EXCEPT_MASK)); @@ -97,7 +97,7 @@ void RTNAME(feclearexcept)(uint32_t excepts) { } void RTDEF(feraiseexcept)(uint32_t excepts) { #if !defined(RT_DEVICE_COMPILATION) - FLANG_FP_TRAP_ON + FLANG_FENV_ACCESS_ON feraiseexcept(excepts); #if defined(_MM_EXCEPT_DENORM) _mm_setcsr(_mm_getcsr() | (excepts & _MM_EXCEPT_MASK)); @@ -105,7 +105,7 @@ void RTDEF(feraiseexcept)(uint32_t excepts) { #endif } uint32_t RTNAME(fetestexcept)(uint32_t excepts) { - FLANG_FP_TRAP_ON + FLANG_FENV_ACCESS_ON #if defined(_MM_EXCEPT_DENORM) return (_mm_getcsr() & _MM_EXCEPT_MASK & excepts) | fetestexcept(excepts); #else diff --git a/flang-rt/unittests/Runtime/Exceptions.cpp b/flang-rt/unittests/Runtime/Exceptions.cpp index 2b2526b6cf1e3..2ccc03b845c69 100644 --- a/flang-rt/unittests/Runtime/Exceptions.cpp +++ b/flang-rt/unittests/Runtime/Exceptions.cpp @@ -13,8 +13,8 @@ /// calls: under clang's default `-ffp-exception-behavior=ignore`, the /// compiler is free to drop calls to fenv functions, which would silently /// break ieee_arithmetic flag handling. exceptions.cpp uses -/// `FLANG_FP_TRAP_ON` to disable that optimization; if it were ever removed -/// or weakened, the round-trip assertions below would fail. +/// `FLANG_FENV_ACCESS_ON` to disable that optimization; if it were ever +/// removed or weakened, the round-trip assertions below would fail. // //===----------------------------------------------------------------------===// @@ -35,11 +35,11 @@ namespace { class FenvScope { public: FenvScope() { - FLANG_FP_TRAP_ON + FLANG_FENV_ACCESS_ON fegetenv(&saved_); } ~FenvScope() { - FLANG_FP_TRAP_ON + FLANG_FENV_ACCESS_ON fesetenv(&saved_); } diff --git a/flang/cmake/modules/FlangCommon.cmake b/flang/cmake/modules/FlangCommon.cmake index a1e80f737e543..d5ace5386aeb0 100644 --- a/flang/cmake/modules/FlangCommon.cmake +++ b/flang/cmake/modules/FlangCommon.cmake @@ -11,9 +11,34 @@ #===------------------------------------------------------------------------===# include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) include(CheckIncludeFile) include(CMakePushCheckState) +# Check whether the compiler honors '#pragma STDC FENV_ACCESS ON' +set(FLANG_SAVED_TRY_COMPILE_TARGET_TYPE "${CMAKE_TRY_COMPILE_TARGET_TYPE}") +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +cmake_push_check_state() +if(MSVC) + string(APPEND CMAKE_REQUIRED_FLAGS " /WX") +else() + string(APPEND CMAKE_REQUIRED_FLAGS " -Wall -Werror") +endif() +check_cxx_source_compiles( + "int probe() { + #pragma STDC FENV_ACCESS ON + return 0; + } + " + COMPILER_SUPPORTS_STDC_FENV_ACCESS_PRAGMA) +cmake_pop_check_state() +set(CMAKE_TRY_COMPILE_TARGET_TYPE "${FLANG_SAVED_TRY_COMPILE_TARGET_TYPE}") +unset(FLANG_SAVED_TRY_COMPILE_TARGET_TYPE) + +if(COMPILER_SUPPORTS_STDC_FENV_ACCESS_PRAGMA) + add_compile_definitions(HAVE_STDC_FENV_ACCESS) +endif() + # The out of tree builds of the compiler and the Fortran runtime # must use the same setting of FLANG_RUNTIME_F128_MATH_LIB # to be composable. Failure to synchronize this setting may result diff --git a/flang/include/flang/Common/fp-control.h b/flang/include/flang/Common/fp-control.h index a6e5c3bda3335..67b40d91e64ec 100644 --- a/flang/include/flang/Common/fp-control.h +++ b/flang/include/flang/Common/fp-control.h @@ -6,29 +6,25 @@ // //===----------------------------------------------------------------------===// -// FLANG_FP_TRAP_ON enables floating-point exception access in the -// enclosing scope. It silences clang's -Wfenv-access warning on calls to -// fenv.h primitives (feraiseexcept, fesetround, fetestexcept, ...). +// FLANG_FENV_ACCESS_ON enables floating-point environment access in the +// enclosing scope, so that the compiler does not reorder or elide calls to +// fenv.h primitives (feraiseexcept, fesetround, fetestexcept, ...). It also +// silences clang's -Wfenv-access diagnostic on those calls. // // Use as a statement at the top of a function body: // // void f() { -// FLANG_FP_TRAP_ON +// FLANG_FENV_ACCESS_ON // feraiseexcept(FE_INVALID); // } -// #ifndef FORTRAN_COMMON_FP_CONTROL_H_ #define FORTRAN_COMMON_FP_CONTROL_H_ -#if defined(__clang__) && (__clang_major__ >= 10) -// Clang >= 10 supports `#pragma clang fp exceptions(maytrap)`, which is the -// local-scope equivalent of `-ffp-exception-behavior=maytrap` and is what the -// -Wfenv-access diagnostic recommends. -#define FLANG_FP_TRAP_ON _Pragma("clang fp exceptions(maytrap)") +#ifdef HAVE_STDC_FENV_ACCESS +#define FLANG_FENV_ACCESS_ON _Pragma("STDC FENV_ACCESS ON") #else -// Portable fallback for GCC, MSVC, or older clang. -#define FLANG_FP_TRAP_ON _Pragma("STDC FENV_ACCESS ON") +#define FLANG_FENV_ACCESS_ON #endif #endif // FORTRAN_COMMON_FP_CONTROL_H_ diff --git a/flang/lib/Evaluate/host.cpp b/flang/lib/Evaluate/host.cpp index bf0249647162a..880a8add6dfc8 100644 --- a/flang/lib/Evaluate/host.cpp +++ b/flang/lib/Evaluate/host.cpp @@ -8,6 +8,7 @@ #include "host.h" +#include "flang/Common/fp-control.h" #include "flang/Common/idioms.h" #include "llvm/Support/Errno.h" #include <cfenv> @@ -20,6 +21,7 @@ using namespace Fortran::parser::literals; void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment( FoldingContext &context) { + FLANG_FENV_ACCESS_ON errno = 0; std::fenv_t currentFenv; if (feholdexcept(&originalFenv_) != 0) { @@ -109,6 +111,7 @@ void HostFloatingPointEnvironment::SetUpHostFloatingPointEnvironment( } void HostFloatingPointEnvironment::CheckAndRestoreFloatingPointEnvironment( FoldingContext &context) { + FLANG_FENV_ACCESS_ON int errnoCapture{errno}; if (hardwareFlagsAreReliable()) { int exceptions{fetestexcept(FE_ALL_EXCEPT)}; diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake index 4ff38fbb4c09c..593d3bbdc3ae3 100644 --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -1560,6 +1560,31 @@ function(get_compile_definitions) endfunction() get_compile_definitions() +# Check whether the compiler honors '#pragma STDC FENV_ACCESS ON' +include(CMakePushCheckState) +set(LLVM_SAVED_TRY_COMPILE_TARGET_TYPE "${CMAKE_TRY_COMPILE_TARGET_TYPE}") +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +cmake_push_check_state() +if(MSVC) + string(APPEND CMAKE_REQUIRED_FLAGS " /WX") +else() + string(APPEND CMAKE_REQUIRED_FLAGS " -Wall -Werror") +endif() +check_cxx_source_compiles( + "int probe() { + #pragma STDC FENV_ACCESS ON + return 0; + } + " + COMPILER_SUPPORTS_STDC_FENV_ACCESS_PRAGMA) +cmake_pop_check_state() +set(CMAKE_TRY_COMPILE_TARGET_TYPE "${LLVM_SAVED_TRY_COMPILE_TARGET_TYPE}") +unset(LLVM_SAVED_TRY_COMPILE_TARGET_TYPE) + +if(COMPILER_SUPPORTS_STDC_FENV_ACCESS_PRAGMA) + add_compile_definitions(HAVE_STDC_FENV_ACCESS) +endif() + option(LLVM_FORCE_ENABLE_STATS "Enable statistics collection for builds that wouldn't normally enable it" OFF) check_symbol_exists(os_signpost_interval_begin "os/signpost.h" macos_signposts_available) diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h index 33ea019a87be5..2426f1271a047 100644 --- a/llvm/include/llvm/Support/Compiler.h +++ b/llvm/include/llvm/Support/Compiler.h @@ -811,4 +811,10 @@ void AnnotateIgnoreWritesEnd(const char *file, int line); #define LLVM_CPU_SUPPORTS_SSE42 LLVM_CPU_SUPPORTS("sse4.2") #endif +#ifdef HAVE_STDC_FENV_ACCESS +#define LLVM_FENV_ACCESS_ON _Pragma("STDC FENV_ACCESS ON") +#else +#define LLVM_FENV_ACCESS_ON +#endif + #endif diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index a822ab9c5a748..c6de06adf65fb 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -2241,6 +2241,7 @@ Constant *GetConstantFoldFPValue128(float128 V, Type *Ty) { /// Clear the floating-point exception state. inline void llvm_fenv_clearexcept() { #if defined(FE_ALL_EXCEPT) + LLVM_FENV_ACCESS_ON feclearexcept(FE_ALL_EXCEPT); #endif errno = 0; @@ -2248,6 +2249,7 @@ inline void llvm_fenv_clearexcept() { /// Test if a floating-point exception was raised. inline bool llvm_fenv_testexcept() { + LLVM_FENV_ACCESS_ON int errno_val = errno; if (errno_val == ERANGE || errno_val == EDOM) return true; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
