diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -450,6 +450,15 @@ return Opts; } + storage_type getDiffWith(FPOptions FPO) const { + storage_type Diff = 0; +#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \ + if (get##NAME() != FPO.get##NAME()) \ + Diff |= NAME##Mask; +#include "clang/Basic/FPOptions.def" + return Diff; + } + // We can define most of the accessors automatically: #define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \ TYPE get##NAME() const { \ @@ -498,6 +507,8 @@ : Options(LO), OverrideMask(OverrideMaskBits) {} FPOptionsOverride(FPOptions FPO) : Options(FPO), OverrideMask(OverrideMaskBits) {} + FPOptionsOverride(FPOptions New, FPOptions Old) + : Options(New), OverrideMask(New.getDiffWith(Old)) {} bool requiresTrailingStorage() const { return OverrideMask != 0; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -589,13 +589,7 @@ // This stack tracks the current state of Sema.CurFPFeatures. PragmaStack FpPragmaStack; FPOptionsOverride CurFPFeatureOverrides() { - FPOptionsOverride result; - if (!FpPragmaStack.hasValue()) { - result = FPOptionsOverride(); - } else { - result = FpPragmaStack.CurrentValue; - } - return result; + return FpPragmaStack.CurrentValue; } // RAII object to push / pop sentinel slots for all MS #pragma stacks. @@ -1429,7 +1423,6 @@ const LangOptions &getLangOpts() const { return LangOpts; } OpenCLOptions &getOpenCLOptions() { return OpenCLFeatures; } - FPOptions &getCurFPFeatures() { return CurFPFeatures; } DiagnosticsEngine &getDiagnostics() const { return Diags; } SourceManager &getSourceManager() const { return SourceMgr; } @@ -1439,6 +1432,24 @@ ASTMutationListener *getASTMutationListener() const; ExternalSemaSource* getExternalSource() const { return ExternalSource; } + const FPOptions &getCurFPFeatures() const { return CurFPFeatures; } + void setCurFPFeatures(FPOptions FPO) { + FpPragmaStack.CurrentValue = FPOptionsOverride(FPO, CurFPFeatures); + CurFPFeatures = FPO; + } + + /// Adjust floating point options applied to parsing some global objects. + void adjustFPFeaturesForGlobal() { + if (getCurFPFeatures().getRoundingMode() == llvm::RoundingMode::Dynamic) { + // If current rounding mode is 'dynamic', it means that a command + // line option line like `-ffpmodel=strict` is in effect. Set + // constant rounding to default in this case. + FPOptions NewFPO = getCurFPFeatures(); + NewFPO.setRoundingMode(llvm::RoundingMode::NearestTiesToEven); + setCurFPFeatures(NewFPO); + } + } + ///Registers an external source. If an external source already exists, /// creates a multiplex external source and appends to it. /// diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2284,6 +2284,17 @@ } PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl); + + Sema::FPFeaturesStateRAII FPO(Actions); + if (auto VD = dyn_cast_or_null(ThisDecl)) + if (!VD->isInvalidDecl()) { + // If variable requires constant initialization, set constant + // rounding mode. + if (VD->isFileVarDecl() || VD->isConstexpr() || + (!getLangOpts().CPlusPlus && VD->isStaticLocal())) + Actions.adjustFPFeaturesForGlobal(); + } + ExprResult Init = ParseInitializer(); // If this is the only decl in (possibly) range based for statement, diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -2253,6 +2253,11 @@ Sema::PragmaStackSentinelRAII PragmaStackSentinel(Actions, "InternalPragmaState", IsCXXMethod); + Sema::FPFeaturesStateRAII SavedFPFeatues(Actions); + if (auto Fn = dyn_cast_or_null(Decl)) + if (Fn->isConstexpr()) + Actions.adjustFPFeaturesForGlobal(); + // Do not enter a scope for the brace, as the arguments are in the same scope // (the function body) as the body itself. Instead, just read the statement // list and put it into a CompoundStmt for safe keeping. diff --git a/clang/test/AST/const-fpfeatures-strict.c b/clang/test/AST/const-fpfeatures-strict.c new file mode 100644 --- /dev/null +++ b/clang/test/AST/const-fpfeatures-strict.c @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -S -emit-llvm -ffp-exception-behavior=strict -Wno-unknown-pragmas %s -o - | FileCheck %s + +// nextUp(1.F) == 0x1.000002p0F + +struct S { + float f; +}; + +static struct S var_01 = {0x1.000001p0}; +struct S *func_01() { + return &var_01; +} + +struct S var_02 = {0x1.000001p0}; + +struct S *func_03() { + static struct S var_03 = {0x1.000001p0}; + return &var_03; +} + +// CHECK: @var_01 = {{.*}} %struct.S { float 1.000000e+00 } +// CHECK: @var_02 = {{.*}} %struct.S { float 1.000000e+00 } +// CHECK: @func_03.var_03 = {{.*}} %struct.S { float 1.000000e+00 } + +#pragma STDC FENV_ROUND FE_UPWARD + +static struct S var_04 = {0x1.000001p0}; +struct S *func_04() { + return &var_04; +} + +struct S var_05 = {0x1.000001p0}; + +struct S *func_06() { + static struct S var_06 = {0x1.000001p0}; + return &var_06; +} + +// CHECK: @var_04 = {{.*}} %struct.S { float 0x3FF0000020000000 } +// CHECK: @var_05 = {{.*}} %struct.S { float 0x3FF0000020000000 } +// CHECK: @func_06.var_06 = {{.*}} %struct.S { float 0x3FF0000020000000 }