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 @@ -455,6 +455,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 { \ @@ -503,6 +512,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,12 @@ 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; + } + ///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/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1278,6 +1278,10 @@ ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope | Scope::CompoundStmtScope); + // If '-frounding-math' was specified, FP options are adjusted at the entry + // to function. + Sema::FPFeaturesStateRAII SavedFPOptions(Actions); + // Tell the actions module that we have entered a function definition with the // specified Declarator for the function. Sema::SkipBodyInfo SkipBody; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -378,6 +378,16 @@ DeclarationName BuiltinVaList = &Context.Idents.get("__builtin_va_list"); if (IdResolver.begin(BuiltinVaList) == IdResolver.end()) PushOnScopeChains(Context.getBuiltinVaListDecl(), TUScope); + + if (getLangOpts().getFPRoundingMode() == LangOptions::RoundingMode::Dynamic) { + // If command line options specify '-frounding-math', the default rounding + // mode is 'dynamic'. It however is applied to function bodies only. At the + // file level the constant rounding mode is in effect, which is identical to + // the default mode at the beginning. + FPOptions NewFPO = getCurFPFeatures(); + NewFPO.setRoundingMode(llvm::RoundingMode::NearestTiesToEven); + setCurFPFeatures(NewFPO); + } } Sema::~Sema() { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14134,6 +14134,11 @@ getCurLexicalContext()->getDeclKind() != Decl::ObjCImplementation) Diag(FD->getLocation(), diag::warn_function_def_in_objc_container); + // If the current FP mode differ from that defined by the LangOptions, + // set the required FP mode. + if (getLangOpts().getFPRoundingMode() == LangOptions::RoundingMode::Dynamic) + setCurFPFeatures(FPOptions(getLangOpts())); + return D; } 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,43 @@ +// RUN: %clang_cc1 -S -emit-llvm -ffp-exception-behavior=strict -Wno-unknown-pragmas %s -o - | FileCheck %s + +float PR47807 = -8.6563630030e-03; + +// 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 } diff --git a/clang/test/CodeGenCXX/rounding-math.cpp b/clang/test/CodeGenCXX/rounding-math.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/rounding-math.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -S -emit-llvm -triple i386-linux -Wno-unknown-pragmas -frounding-math %s -o - | FileCheck %s + +constexpr float func_01(float x, float y) { + return x + y; +} + +float V1 = func_01(1.0F, 0x0.000001p0F); +float V2 = 1.0F + 0x0.000001p0F; +float V3 = func_01(1.0F, 2.0F); + +// CHECK: @V1 = {{.*}}global float 0.000000e+00, align 4 +// CHECK: @V2 = {{.*}}global float 1.000000e+00, align 4 +// CHECK: @V3 = {{.*}}global float 3.000000e+00, align 4 + +// CHECK-LABEL: define internal void @__cxx_global_var_init() +// CHECK: call float @_Z7func_01ff(float 1.000000e+00, float 0x3E70000000000000) +// CHECK: store {{.*}}, float* @V1, align 4 +// CHECK: ret void + +// CHECK-LABEL: define {{.*}} float @_Z7func_01ff(float %x, float %y) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: ret float diff --git a/clang/test/SemaCXX/rounding-math-diag.cpp b/clang/test/SemaCXX/rounding-math-diag.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/rounding-math-diag.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple x86_64-linux -verify -frounding-math %s + +void func_01() { + const float C1 = 1.0F + 0x0.000001p0F; // expected-note{{declared here}} + // expected-note@-1{{cannot evaluate this expression if rounding mode is dynamic}} + static_assert(C1 == 1.0F, ""); // expected-error{{static_assert expression is not an integral constant expression}} + // expected-note@-1{{initializer of 'C1' is not a constant expression}} +} diff --git a/clang/test/SemaCXX/rounding-math.cpp b/clang/test/SemaCXX/rounding-math.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/rounding-math.cpp @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple x86_64-linux -verify -frounding-math -Wno-unknown-pragmas %s +// expected-no-diagnostics + +// nextUp(1.F) == 0x1.000002p0F + +static_assert(1.0F + 0x0.000001p0F == 0x1.0p0F, ""); + +char Arr01[1 + (1.0F + 0x0.000001p0F > 1.0F)]; +static_assert(sizeof(Arr01) == 1, ""); + +struct S1 { + int : (1.0F + 0x0.000001p0F > 1.0F); + int f; +}; +static_assert(sizeof(S1) == sizeof(int), ""); + +#pragma STDC FENV_ROUND FE_UPWARD +static_assert(1.0F + 0x0.000001p0F == 0x1.000002p0F, ""); + +char Arr01u[1 + (1.0F + 0x0.000001p0F > 1.0F)]; +static_assert(sizeof(Arr01u) == 2, ""); + +struct S1u { + int : (1.0F + 0x0.000001p0F > 1.0F); + int f; +}; +static_assert(sizeof(S1u) > sizeof(int), ""); + +#pragma STDC FENV_ROUND FE_DOWNWARD +static_assert(1.0F + 0x0.000001p0F == 1.0F, ""); + +char Arr01d[1 + (1.0F + 0x0.000001p0F > 1.0F)]; +static_assert(sizeof(Arr01d) == 1, ""); + +struct S1d { + int : (1.0F + 0x0.000001p0F > 1.0F); + int f; +}; +static_assert(sizeof(S1d) == sizeof(int), "");