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 @@ -669,6 +669,16 @@ getAllowFEnvAccess(); } + RoundingMode getEffectiveRoundingMode() const { + RoundingMode RM = getRoundingMode(); + // C2x: 7.6.2p3 If the FE_DYNAMIC mode is specified and FENV_ACCESS is + // "off", the translator may assume that the default rounding mode is in + // effect. + if (RM == RoundingMode::Dynamic && !getAllowFEnvAccess()) + RM = RoundingMode::NearestTiesToEven; + return RM; + } + bool operator==(FPOptions other) const { return Value == other.Value; } /// Return the default value of FPOptions that's used when trailing 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 @@ -704,6 +704,9 @@ return result; } + /// Was rounding mode overridden by pragma FENV_ROUND? + bool IsRoundingModeSet = false; + // RAII object to push / pop sentinel slots for all MS #pragma stacks. // Actions should be performed only if we enter / exit a C++ method body. class PragmaStackSentinelRAII { @@ -1571,6 +1574,7 @@ FPOptionsOverride OldOverrides; LangOptions::FPEvalMethodKind OldEvalMethod; SourceLocation OldFPPragmaLocation; + bool OldIsRoundingModeSet; }; void addImplicitTypedef(StringRef Name, QualType T); @@ -10282,7 +10286,7 @@ LangOptions::FPExceptionModeKind); /// Called to set constant rounding mode for floating point operations. - void setRoundingMode(SourceLocation Loc, llvm::RoundingMode); + void ActOnPragmaFEnvRound(SourceLocation Loc, llvm::RoundingMode); /// Called to set exception behavior for floating point operations. void setExceptionMode(SourceLocation Loc, LangOptions::FPExceptionModeKind); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -146,7 +146,7 @@ FMFGuard.emplace(CGF.Builder); llvm::RoundingMode NewRoundingBehavior = - static_cast(FPFeatures.getRoundingMode()); + FPFeatures.getEffectiveRoundingMode(); CGF.Builder.setDefaultConstrainedRounding(NewRoundingBehavior); auto NewExceptionBehavior = ToConstrainedExceptMD(static_cast( diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -804,7 +804,7 @@ reinterpret_cast(Tok.getAnnotationValue())); SourceLocation PragmaLoc = ConsumeAnnotationToken(); - Actions.setRoundingMode(PragmaLoc, RM); + Actions.ActOnPragmaFEnvRound(PragmaLoc, RM); } StmtResult Parser::HandlePragmaCaptured() 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 @@ -2662,10 +2662,12 @@ : S(S), OldFPFeaturesState(S.CurFPFeatures), OldOverrides(S.FpPragmaStack.CurrentValue), OldEvalMethod(S.PP.getCurrentFPEvalMethod()), - OldFPPragmaLocation(S.PP.getLastFPEvalPragmaLocation()) {} + OldFPPragmaLocation(S.PP.getLastFPEvalPragmaLocation()), + OldIsRoundingModeSet(S.IsRoundingModeSet) {} Sema::FPFeaturesStateRAII::~FPFeaturesStateRAII() { S.CurFPFeatures = OldFPFeaturesState; S.FpPragmaStack.CurrentValue = OldOverrides; S.PP.setCurrentFPEvalMethod(OldFPPragmaLocation, OldEvalMethod); + S.IsRoundingModeSet = OldIsRoundingModeSet; } diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1246,18 +1246,12 @@ CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); } -void Sema::setRoundingMode(SourceLocation Loc, llvm::RoundingMode FPR) { - // C2x: 7.6.2p3 If the FE_DYNAMIC mode is specified and FENV_ACCESS is "off", - // the translator may assume that the default rounding mode is in effect. - if (FPR == llvm::RoundingMode::Dynamic && - !CurFPFeatures.getAllowFEnvAccess() && - CurFPFeatures.getFPExceptionMode() == LangOptions::FPE_Ignore) - FPR = llvm::RoundingMode::NearestTiesToEven; - +void Sema::ActOnPragmaFEnvRound(SourceLocation Loc, llvm::RoundingMode FPR) { FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); NewFPFeatures.setRoundingModeOverride(FPR); FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + IsRoundingModeSet = true; } void Sema::setExceptionMode(SourceLocation Loc, @@ -1278,12 +1272,24 @@ // pragma, or by using the /fp:precise or /fp:strict compiler options if (!isPreciseFPEnabled()) Diag(Loc, diag::err_pragma_fenv_requires_precise); + // Enabling FENV access sets RoundingMode to Dynamic and ExceptionBehavior + // to Strict unless they are already overwritten. + if (!IsRoundingModeSet) + NewFPFeatures.setRoundingModeOverride(llvm::RoundingMode::Dynamic); + if (!NewFPFeatures.hasFPExceptionModeOverride()) + NewFPFeatures.setFPExceptionModeOverride(LangOptions::FPE_Strict); + else if (NewFPFeatures.getFPExceptionModeOverride() == + LangOptions::FPE_Ignore) { + if (NewFPFeatures.hasAllowFEnvAccessOverride()) + NewFPFeatures.setFPExceptionModeOverride(LangOptions::FPE_Strict); + } NewFPFeatures.setAllowFEnvAccessOverride(true); - // Enabling FENV access sets the RoundingMode to Dynamic. - // and ExceptionBehavior to Strict - NewFPFeatures.setRoundingModeOverride(llvm::RoundingMode::Dynamic); - NewFPFeatures.setFPExceptionModeOverride(LangOptions::FPE_Strict); } else { + if (CurFPFeatures.getRoundingMode() == llvm::RoundingMode::Dynamic) + NewFPFeatures.setRoundingModeOverride( + llvm::RoundingMode::NearestTiesToEven); + if (CurFPFeatures.getFPExceptionMode() != LangOptions::FPE_Ignore) + NewFPFeatures.setFPExceptionModeOverride(LangOptions::FPE_Ignore); NewFPFeatures.setAllowFEnvAccessOverride(false); } FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); diff --git a/clang/test/AST/ast-dump-fpfeatures.cpp b/clang/test/AST/ast-dump-fpfeatures.cpp --- a/clang/test/AST/ast-dump-fpfeatures.cpp +++ b/clang/test/AST/ast-dump-fpfeatures.cpp @@ -109,7 +109,7 @@ } // CHECK-LABEL: FunctionDecl {{.*}} func_12 'float (float, float)' -// CHECK: BinaryOperator {{.*}} 'float' '+' RoundingMode=tonearest +// CHECK: BinaryOperator {{.*}} 'float' '+' RoundingMode=dynamic #pragma STDC FENV_ROUND FE_TONEAREST diff --git a/clang/test/CodeGen/pragma-fenv_access.c b/clang/test/CodeGen/pragma-fenv_access.c --- a/clang/test/CodeGen/pragma-fenv_access.c +++ b/clang/test/CodeGen/pragma-fenv_access.c @@ -20,7 +20,7 @@ return x + y; } // CHECK-LABEL: @func_02 -// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") float func_03(float x, float y) { @@ -41,7 +41,15 @@ return x + y; } // CHECK-LABEL: @func_04 -// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: fadd float + + +float func_04a(float x, float y) { + #pragma float_control(except, on) + return x + y; +} +// CHECK-LABEL: @func_04a +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") float func_05(float x, float y) { @@ -57,18 +65,117 @@ return x + y; } // CHECK-LABEL: @func_06 -// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: fadd float float func_07(float x, float y) { x -= y; if (x) { #pragma STDC FENV_ACCESS ON - y *= 2; + y *= 2.0F; } - return y + 4; + return y + 4.0F; } // CHECK-LABEL: @func_07 -// CHECK: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") // CHECK: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + + +float func_08(float x, float y) { + #pragma STDC FENV_ROUND FE_UPWARD + #pragma STDC FENV_ACCESS ON + return x + y; +} +// CHECK-LABEL: @func_08 +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + + +float func_08a(float x, float y) { + #pragma STDC FENV_ROUND FE_TONEAREST + #pragma STDC FENV_ACCESS ON + return x + y; +} +// CHECK-LABEL: @func_08a +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + + +float func_09(float x, float y) { + #pragma clang fp exceptions(maytrap) + #pragma STDC FENV_ACCESS ON + return x + y; +} +// CHECK-LABEL: @func_09 +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + + +float func_10(float x, float y) { + #pragma clang fp exceptions(maytrap) + #pragma STDC FENV_ROUND FE_UPWARD + #pragma STDC FENV_ACCESS ON + return x + y; +} +// CHECK-LABEL: @func_10 +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + + +float func_11(float x, float y, float z) { + #pragma STDC FENV_ACCESS ON + float res = x * y; + { + #pragma STDC FENV_ACCESS OFF + return res + z; + } +} +// CHECK-LABEL: @func_11 +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + + +float func_12(float x, float y, float z) { + #pragma STDC FENV_ROUND FE_TOWARDZERO + #pragma STDC FENV_ACCESS ON + float res = x * y; + { + #pragma STDC FENV_ACCESS OFF + return res + z; + } +} +// CHECK-LABEL: @func_12 +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") + + +float func_13(float x, float y) { + x -= y; + { + #pragma STDC FENV_ROUND FE_TONEAREST + #pragma STDC FENV_ACCESS ON + y *= 2.0F; + } + { + #pragma STDC FENV_ACCESS ON + return y + 4.0F; + } +} +// CHECK-LABEL: @func_13 +// CHECK: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +float func_14(float x, float y) { + #pragma STDC FENV_ROUND FE_DYNAMIC + #pragma STDC FENV_ACCESS ON + return x + y; +} +// CHECK-LABEL: @func_14 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +float func_15(float x, float y) { + #pragma STDC FENV_ROUND FE_DYNAMIC + return x + y; +} +// CHECK-LABEL: @func_15 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore")