Author: Melanie Blower Date: 2020-10-25T06:46:25-07:00 New Revision: 2e204e23911b1f8bd1463535da40c6e48747a138
URL: https://github.com/llvm/llvm-project/commit/2e204e23911b1f8bd1463535da40c6e48747a138 DIFF: https://github.com/llvm/llvm-project/commit/2e204e23911b1f8bd1463535da40c6e48747a138.diff LOG: [clang] Enable support for #pragma STDC FENV_ACCESS Reviewers: rjmccall, rsmith, sepavloff Differential Revision: https://reviews.llvm.org/D87528 Added: clang/test/CodeGen/pragma-fenv_access.c clang/test/Parser/pragma-fenv_access.c Modified: clang/docs/UsersManual.rst clang/include/clang/AST/Decl.h clang/include/clang/Basic/Attr.td clang/include/clang/Basic/DiagnosticASTKinds.td clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Basic/LangOptions.h clang/include/clang/Sema/ScopeInfo.h clang/include/clang/Sema/Sema.h clang/lib/AST/ExprConstant.cpp clang/lib/CodeGen/CGCall.cpp clang/lib/CodeGen/CodeGenFunction.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/CodeGen/CodeGenModule.h clang/lib/Parse/ParsePragma.cpp clang/lib/Parse/ParseStmt.cpp clang/lib/Sema/ScopeInfo.cpp clang/lib/Sema/SemaAttr.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaStmt.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriterDecl.cpp clang/test/CXX/expr/expr.const/p2-0x.cpp clang/test/CodeGen/fp-floatcontrol-pragma.cpp clang/test/Parser/fp-floatcontrol-syntax.cpp clang/test/Preprocessor/pragma_unknown.c Removed: ################################################################################ diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 9726a25f7f63..5d18435aab6c 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -1386,7 +1386,7 @@ Note that floating-point operations performed as part of constant initialization Details: * ``precise`` Disables optimizations that are not value-safe on floating-point data, although FP contraction (FMA) is enabled (``-ffp-contract=fast``). This is the default behavior. - * ``strict`` Enables ``-frounding-math`` and ``-ffp-exception-behavior=strict``, and disables contractions (FMA). All of the ``-ffast-math`` enablements are disabled. + * ``strict`` Enables ``-frounding-math`` and ``-ffp-exception-behavior=strict``, and disables contractions (FMA). All of the ``-ffast-math`` enablements are disabled. Enables ``STDC FENV_ACCESS``: by default ``FENV_ACCESS`` is disabled. This option setting behaves as though ``#pragma STDC FENV_ACESS ON`` appeared at the top of the source file. * ``fast`` Behaves identically to specifying both ``-ffast-math`` and ``ffp-contract=fast`` Note: If your command line specifies multiple instances @@ -1408,6 +1408,44 @@ Note that floating-point operations performed as part of constant initialization * ``strict`` The compiler ensures that all transformations strictly preserve the floating point exception semantics of the original code. +.. _fp-constant-eval: + +A note about Floating Point Constant Evaluation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In C, the only place floating point operations are guaranteed to be evaluated +during translation is in the initializers of variables of static storage +duration, which are all notionally initialized before the program begins +executing (and thus before a non-default floating point environment can be +entered). But C++ has many more contexts where floating point constant +evaluation occurs. Specifically: for static/thread-local variables, +first try evaluating the initializer in a constant context, including in the +constant floating point environment (just like in C), and then, if that fails, +fall back to emitting runtime code to perform the initialization (which might +in general be in a diff erent floating point environment). + +Consider this example when compiled with ``-frounding-math`` + + .. code-block:: console + +constexpr float func_01(float x, float y) { + return x + y; +} +float V1 = func_01(1.0F, 0x0.000001p0F); + +The C++ rule is that initializers for static storage duration variables are +first evaluated during translation (therefore, in the default rounding mode), +and only evaluated at runtime (and therefore in the runtime rounding mode) if +the compile-time evaluation fails. This is in line with the C rules; +C11 F.8.5 says: *All computation for automatic initialization is done (as if) +at execution time; thus, it is affected by any operative modes and raises +floating-point exceptions as required by IEC 60559 (provided the state for the +FENV_ACCESS pragma is ββonββ). All computation for initialization of objects +that have static or thread storage duration is done (as if) at translation +time.* C++ generalizes this by adding another phase of initialization +(at runtime) if the translation-time initialization fails, but the +translation-time evaluation of the initializer of succeeds, it will be +treated as a constant initializer. .. _controlling-code-generation: diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 0a9474c3ee86..5d4f18806ff4 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2253,10 +2253,6 @@ class FunctionDecl : public DeclaratorDecl, bool usesSEHTry() const { return FunctionDeclBits.UsesSEHTry; } void setUsesSEHTry(bool UST) { FunctionDeclBits.UsesSEHTry = UST; } - /// Indicates the function uses Floating Point constrained intrinsics - bool usesFPIntrin() const { return FunctionDeclBits.UsesFPIntrin; } - void setUsesFPIntrin(bool Val) { FunctionDeclBits.UsesFPIntrin = Val; } - /// Whether this function has been deleted. /// /// A function that is "deleted" (via the C++0x "= delete" syntax) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index f58870d76013..56079db4d9c5 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2267,6 +2267,14 @@ def PragmaClangRelroSection : InheritableAttr { let Documentation = [Undocumented]; } +def StrictFP : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly. + // Function uses strict floating point operations. + let Spellings = []; + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} + def PragmaClangTextSection : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly. let Spellings = []; diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index b6262518b81f..f6b936f5ccd9 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -74,6 +74,8 @@ def note_constexpr_float_arithmetic : Note< "floating point arithmetic produces %select{an infinity|a NaN}0">; def note_constexpr_dynamic_rounding : Note< "cannot evaluate this expression if rounding mode is dynamic">; +def note_constexpr_float_arithmetic_strict : Note< + "compile time floating point arithmetic suppressed in strict evaluation modes">; def note_constexpr_pointer_subtraction_not_same_array : Note< "subtracted pointers are not elements of the same array">; def note_constexpr_pointer_subtraction_zero_size : Note< diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 78d3a08b3028..24ca8340ef6f 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1135,9 +1135,12 @@ def err_pragma_file_or_compound_scope : Error< // - #pragma stdc unknown def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">, InGroup<UnknownPragmas>; -def warn_stdc_fenv_access_not_supported : - Warning<"pragma STDC FENV_ACCESS ON is not supported, ignoring pragma">, - InGroup<UnknownPragmas>; +// The C standard 7.6.1p2 says "The [FENV_ACCESS] pragma shall occur either +// outside external declarations or preceding all explicit declarations and +// statements inside a compound statement. +def err_pragma_stdc_fenv_access_scope : Error< + "'#pragma STDC FENV_ACCESS' can only appear at file scope or at the start of" + " a compound statement">; def warn_stdc_fenv_round_not_supported : Warning<"pragma STDC FENV_ROUND is not supported">, InGroup<UnknownPragmas>; diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 860b97aace54..dea9d217cf0c 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -413,13 +413,20 @@ class FPOptions { setFPContractMode(LO.getDefaultFPContractMode()); setRoundingMode(LO.getFPRoundingMode()); setFPExceptionMode(LO.getFPExceptionMode()); - setAllowFEnvAccess(LangOptions::FPM_Off); setAllowFPReassociate(LO.AllowFPReassoc); setNoHonorNaNs(LO.NoHonorNaNs); setNoHonorInfs(LO.NoHonorInfs); setNoSignedZero(LO.NoSignedZero); setAllowReciprocal(LO.AllowRecip); setAllowApproxFunc(LO.ApproxFunc); + if (getFPContractMode() == LangOptions::FPM_On && + getRoundingMode() == llvm::RoundingMode::Dynamic && + getFPExceptionMode() == LangOptions::FPE_Strict) + // If the FP settings are set to the "strict" model, then + // FENV access is set to true. (ffp-model=strict) + setAllowFEnvAccess(true); + else + setAllowFEnvAccess(LangOptions::FPM_Off); } bool allowFPContractWithinStatement() const { diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index f0f9cb9e40ae..ff2a20923415 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -127,6 +127,9 @@ class FunctionScopeInfo { /// Whether there is a fallthrough statement in this function. bool HasFallthroughStmt : 1; + /// Whether this function uses constrained floating point intrinsics + bool UsesFPIntrin : 1; + /// Whether we make reference to a declaration that could be /// unavailable. bool HasPotentialAvailabilityViolations : 1; @@ -369,7 +372,8 @@ class FunctionScopeInfo { : Kind(SK_Function), HasBranchProtectedScope(false), HasBranchIntoScope(false), HasIndirectGoto(false), HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false), - HasFallthroughStmt(false), HasPotentialAvailabilityViolations(false), + HasFallthroughStmt(false), UsesFPIntrin(false), + HasPotentialAvailabilityViolations(false), ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false), ObjCIsSecondaryInit(false), ObjCWarnForNoInitDelegation(false), NeedsCoroutineSuspends(true), @@ -431,6 +435,10 @@ class FunctionScopeInfo { HasFallthroughStmt = true; } + void setUsesFPIntrin() { + UsesFPIntrin = true; + } + void setHasCXXTry(SourceLocation TryLoc) { setHasBranchProtectedScope(); FirstCXXTryLoc = TryLoc; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ed409b93545e..4b2311dce31d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4478,6 +4478,7 @@ class Sema final { bool HasLeadingEmptyMacro = false); void ActOnStartOfCompoundStmt(bool IsStmtExpr); + void ActOnAfterCompoundStatementLeadingPragmas(); void ActOnFinishOfCompoundStmt(); StmtResult ActOnCompoundStmt(SourceLocation L, SourceLocation R, ArrayRef<Stmt *> Elts, bool isStmtExpr); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7bd57a80f7f2..b8891055ab61 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2601,6 +2601,14 @@ static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E, return false; } + if ((St != APFloat::opOK) && + (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic || + FPO.getFPExceptionMode() != LangOptions::FPE_Ignore || + FPO.getAllowFEnvAccess())) { + Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); + return false; + } + if ((St & APFloat::opStatus::opInvalidOp) && FPO.getFPExceptionMode() != LangOptions::FPE_Ignore) { // There is no usefully definable result. @@ -2644,11 +2652,17 @@ static APSInt HandleIntToIntCast(EvalInfo &Info, const Expr *E, } static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E, + const FPOptions FPO, QualType SrcType, const APSInt &Value, QualType DestType, APFloat &Result) { Result = APFloat(Info.Ctx.getFloatTypeSemantics(DestType), 1); - Result.convertFromAPInt(Value, Value.isSigned(), - APFloat::rmNearestTiesToEven); + APFloat::opStatus St = Result.convertFromAPInt(Value, Value.isSigned(), + APFloat::rmNearestTiesToEven); + if (!Info.InConstantContext && St != llvm::APFloatBase::opOK && + FPO.isFPConstrained()) { + Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); + return false; + } return true; } @@ -4370,9 +4384,11 @@ struct CompoundAssignSubobjectHandler { Value = HandleIntToIntCast(Info, E, SubobjType, PromotedLHSType, LHS); return true; } else if (RHS.isFloat()) { + const FPOptions FPO = E->getFPFeaturesInEffect( + Info.Ctx.getLangOpts()); APFloat FValue(0.0); - return HandleIntToFloatCast(Info, E, SubobjType, Value, PromotedLHSType, - FValue) && + return HandleIntToFloatCast(Info, E, FPO, SubobjType, Value, + PromotedLHSType, FValue) && handleFloatFloatBinOp(Info, E, FValue, Opcode, RHS.getFloat()) && HandleFloatToIntCast(Info, E, PromotedLHSType, FValue, SubobjType, Value); @@ -12545,8 +12561,16 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, return false; assert(E->isComparisonOp() && "Invalid binary operator!"); + llvm::APFloatBase::cmpResult CmpResult = LHS.compare(RHS); + if (!Info.InConstantContext && + CmpResult == APFloat::cmpUnordered && + E->getFPFeaturesInEffect(Info.Ctx.getLangOpts()).isFPConstrained()) { + // Note: Compares may raise invalid in some cases involving NaN or sNaN. + Info.FFDiag(E, diag::note_constexpr_float_arithmetic_strict); + return false; + } auto GetCmpRes = [&]() { - switch (LHS.compare(RHS)) { + switch (CmpResult) { case APFloat::cmpEqual: return CmpResult::Equal; case APFloat::cmpLessThan: @@ -13604,6 +13628,11 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) { case Builtin::BI__builtin_fabsf: case Builtin::BI__builtin_fabsl: case Builtin::BI__builtin_fabsf128: + // The C standard says "fabs raises no floating-point exceptions, + // even if x is a signaling NaN. The returned value is independent of + // the current rounding direction mode." Therefore constant folding can + // proceed without regard to the floating point settings. + // Reference, WG14 N2478 F.10.4.3 if (!EvaluateFloat(E->getArg(0), Result, Info)) return false; @@ -13662,6 +13691,9 @@ bool FloatExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { case UO_Plus: return EvaluateFloat(E->getSubExpr(), Result, Info); case UO_Minus: + // In C standard, WG14 N2478 F.3 p4 + // "the unary - raises no floating point exceptions, + // even if the operand is signalling." if (!EvaluateFloat(E->getSubExpr(), Result, Info)) return false; Result.changeSign(); @@ -13695,9 +13727,11 @@ bool FloatExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_IntegralToFloating: { APSInt IntResult; + const FPOptions FPO = E->getFPFeaturesInEffect( + Info.Ctx.getLangOpts()); return EvaluateInteger(SubExpr, IntResult, Info) && - HandleIntToFloatCast(Info, E, SubExpr->getType(), IntResult, - E->getType(), Result); + HandleIntToFloatCast(Info, E, FPO, SubExpr->getType(), + IntResult, E->getType(), Result); } case CK_FixedPointToFloating: { @@ -13936,13 +13970,15 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { if (!Visit(E->getSubExpr())) return false; + const FPOptions FPO = E->getFPFeaturesInEffect( + Info.Ctx.getLangOpts()); QualType To = E->getType()->castAs<ComplexType>()->getElementType(); QualType From = E->getSubExpr()->getType()->castAs<ComplexType>()->getElementType(); Result.makeComplexFloat(); - return HandleIntToFloatCast(Info, E, From, Result.IntReal, + return HandleIntToFloatCast(Info, E, FPO, From, Result.IntReal, To, Result.FloatReal) && - HandleIntToFloatCast(Info, E, From, Result.IntImag, + HandleIntToFloatCast(Info, E, FPO, From, Result.IntImag, To, Result.FloatImag); } } diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 93be1b874350..f148397e7fb9 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4837,7 +4837,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, /*AttrOnCallSite=*/true); if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl)) - if (FD->usesFPIntrin()) + if (FD->hasAttr<StrictFPAttr>()) // All calls within a strictfp function are marked strictfp Attrs = Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, @@ -4902,7 +4902,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, getBundlesForFunclet(CalleePtr); if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl)) - if (FD->usesFPIntrin()) + if (FD->hasAttr<StrictFPAttr>()) // All calls within a strictfp function are marked strictfp Attrs = Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 2dcb5314c61e..2498e2ed687f 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -915,8 +915,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, } if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { - Builder.setIsFPConstrained(FD->usesFPIntrin()); - if (FD->usesFPIntrin()) + Builder.setIsFPConstrained(FD->hasAttr<StrictFPAttr>()); + if (FD->hasAttr<StrictFPAttr>()) Fn->addFnAttr(llvm::Attribute::StrictFP); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 809e877e42aa..a1511e099e15 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1742,6 +1742,15 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, } } +void CodeGenModule::setLLVMFunctionFEnvAttributes(const FunctionDecl *D, + llvm::Function *F) { + if (D->hasAttr<StrictFPAttr>()) { + llvm::AttrBuilder FuncAttrs; + FuncAttrs.addAttribute("strictfp"); + F->addAttributes(llvm::AttributeList::FunctionIndex, FuncAttrs); + } +} + void CodeGenModule::SetCommonAttributes(GlobalDecl GD, llvm::GlobalValue *GV) { const Decl *D = GD.getDecl(); if (dyn_cast_or_null<NamedDecl>(D)) @@ -4587,9 +4596,11 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD, MaybeHandleStaticInExternC(D, Fn); - maybeSetTrivialComdat(*D, *Fn); + // Set CodeGen attributes that represent floating point environment. + setLLVMFunctionFEnvAttributes(D, Fn); + CodeGenFunction(*this).GenerateCode(GD, Fn, FI); setNonAliasAttributes(GD, Fn); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index fc732dbea521..b3babc8dc6e0 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1131,6 +1131,10 @@ class CodeGenModule : public CodeGenTypeCache { /// definition. void SetLLVMFunctionAttributesForDefinition(const Decl *D, llvm::Function *F); + /// Set the LLVM function attributes that represent floating point + /// environment. + void setLLVMFunctionFEnvAttributes(const FunctionDecl *D, llvm::Function *F); + /// Return true iff the given type uses 'sret' when used as a return type. bool ReturnTypeUsesSRet(const CGFunctionInfo &FI); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 572fc7115b87..36bfeeebfef3 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -106,10 +106,6 @@ struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler { tok::OnOffSwitch OOS; if (PP.LexOnOffSwitch(OOS)) return; - if (OOS == tok::OOS_ON) { - PP.Diag(Tok, diag::warn_stdc_fenv_access_not_supported); - return; - } MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1), 1); diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index ee35b24b3c81..a48db31ca42d 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -366,7 +366,8 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( case tok::annot_pragma_fenv_access: ProhibitAttributes(Attrs); - HandlePragmaFEnvAccess(); + Diag(Tok, diag::err_pragma_stdc_fenv_access_scope); + ConsumeAnnotationToken(); return StmtEmpty(); case tok::annot_pragma_fenv_round: @@ -1033,9 +1034,9 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { Tok.getLocation(), "in compound statement ('{}')"); - // Record the state of the FPFeatures, restore on leaving the + // Record the current FPFeatures, restore on leaving the // compound statement. - Sema::FPFeaturesStateRAII SaveFPContractState(Actions); + Sema::FPFeaturesStateRAII SaveFPFeatures(Actions); InMessageExpressionRAIIObject InMessage(*this, false); BalancedDelimiterTracker T(*this, tok::l_brace); @@ -1046,6 +1047,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { // Parse any pragmas at the beginning of the compound statement. ParseCompoundStatementLeadingPragmas(); + Actions.ActOnAfterCompoundStatementLeadingPragmas(); StmtVector Stmts; diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index b2a26af9b4a5..4857346018ae 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -29,6 +29,7 @@ void FunctionScopeInfo::Clear() { HasDroppedStmt = false; HasOMPDeclareReductionCombiner = false; HasFallthroughStmt = false; + UsesFPIntrin = false; HasPotentialAvailabilityViolations = false; ObjCShouldCallSuper = false; ObjCIsDesignatedInit = false; diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 91ca2514158d..a7751707649a 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1002,6 +1002,7 @@ void Sema::setExceptionMode(SourceLocation Loc, void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled) { FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + auto LO = getLangOpts(); if (IsEnabled) { // Verify Microsoft restriction: // You can't enable fenv_access unless precise semantics are enabled. @@ -1010,10 +1011,15 @@ void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled) { if (!isPreciseFPEnabled()) Diag(Loc, diag::err_pragma_fenv_requires_precise); NewFPFeatures.setAllowFEnvAccessOverride(true); - } else + // Enabling FENV access sets the RoundingMode to Dynamic. + // and ExceptionBehavior to Strict + NewFPFeatures.setRoundingModeOverride(llvm::RoundingMode::Dynamic); + NewFPFeatures.setFPExceptionModeOverride(LangOptions::FPE_Strict); + } else { NewFPFeatures.setAllowFEnvAccessOverride(false); + } FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); - CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + CurFPFeatures = NewFPFeatures.applyOverrides(LO); } void Sema::PushNamespaceVisibilityAttr(const VisibilityAttr *Attr, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 283d663c5d26..2bffd2a87d08 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14285,12 +14285,16 @@ static void diagnoseImplicitlyRetainedSelf(Sema &S) { Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation) { + FunctionScopeInfo *FSI = getCurFunction(); FunctionDecl *FD = dcl ? dcl->getAsFunction() : nullptr; + if (FSI->UsesFPIntrin && !FD->hasAttr<StrictFPAttr>()) + FD->addAttr(StrictFPAttr::CreateImplicit(Context)); + sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; - if (getLangOpts().Coroutines && getCurFunction()->isCoroutine()) + if (getLangOpts().Coroutines && FSI->isCoroutine()) CheckCompletedCoroutineBody(FD, Body); // Do not call PopExpressionEvaluationContext() if it is a lambda because one @@ -14367,7 +14371,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // to deduce an implicit return type. if (FD->getReturnType()->isRecordType() && (!getLangOpts().CPlusPlus || !FD->isDependentContext())) - computeNRVO(Body, getCurFunction()); + computeNRVO(Body, FSI); } // GNU warning -Wmissing-prototypes: @@ -14491,14 +14495,14 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, MD->getReturnType(), MD); if (Body) - computeNRVO(Body, getCurFunction()); + computeNRVO(Body, FSI); } - if (getCurFunction()->ObjCShouldCallSuper) { + if (FSI->ObjCShouldCallSuper) { Diag(MD->getEndLoc(), diag::warn_objc_missing_super_call) << MD->getSelector().getAsString(); - getCurFunction()->ObjCShouldCallSuper = false; + FSI->ObjCShouldCallSuper = false; } - if (getCurFunction()->ObjCWarnForNoDesignatedInitChain) { + if (FSI->ObjCWarnForNoDesignatedInitChain) { const ObjCMethodDecl *InitMethod = nullptr; bool isDesignated = MD->isDesignatedInitializerForTheInterface(&InitMethod); @@ -14523,14 +14527,14 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, Diag(InitMethod->getLocation(), diag::note_objc_designated_init_marked_here); } - getCurFunction()->ObjCWarnForNoDesignatedInitChain = false; + FSI->ObjCWarnForNoDesignatedInitChain = false; } - if (getCurFunction()->ObjCWarnForNoInitDelegation) { + if (FSI->ObjCWarnForNoInitDelegation) { // Don't issue this warning for unavaialable inits. if (!MD->isUnavailable()) Diag(MD->getLocation(), diag::warn_objc_secondary_init_missing_init_call); - getCurFunction()->ObjCWarnForNoInitDelegation = false; + FSI->ObjCWarnForNoInitDelegation = false; } diagnoseImplicitlyRetainedSelf(*this); @@ -14541,10 +14545,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, return nullptr; } - if (Body && getCurFunction()->HasPotentialAvailabilityViolations) + if (Body && FSI->HasPotentialAvailabilityViolations) DiagnoseUnguardedAvailabilityViolations(dcl); - assert(!getCurFunction()->ObjCShouldCallSuper && + assert(!FSI->ObjCShouldCallSuper && "This should only be set for ObjC methods, which should have been " "handled in the block above."); @@ -14557,7 +14561,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, DiagnoseReturnInConstructorExceptionHandler(cast<CXXTryStmt>(Body)); // Verify that gotos and switch cases don't jump into scopes illegally. - if (getCurFunction()->NeedsScopeChecking() && + if (FSI->NeedsScopeChecking() && !PP.isCodeCompletionEnabled()) DiagnoseInvalidJumps(Body); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index e994ab216fe6..e6314fd65f8c 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -385,6 +385,14 @@ void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) { PushCompoundScope(IsStmtExpr); } +void Sema::ActOnAfterCompoundStatementLeadingPragmas() { + if (getCurFPFeatures().isFPConstrained()) { + FunctionScopeInfo *FSI = getCurFunction(); + assert(FSI); + FSI->setUsesFPIntrin(); + } +} + void Sema::ActOnFinishOfCompoundStmt() { PopCompoundScope(); } @@ -397,11 +405,6 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, ArrayRef<Stmt *> Elts, bool isStmtExpr) { const unsigned NumElts = Elts.size(); - // Mark the current function as usng floating point constrained intrinsics - if (getCurFPFeatures().isFPConstrained()) - if (FunctionDecl *F = dyn_cast<FunctionDecl>(CurContext)) - F->setUsesFPIntrin(true); - // If we're in C89 mode, check that we don't have any decls after stmts. If // so, emit an extension diagnostic. if (!getLangOpts().C99 && !getLangOpts().CPlusPlus) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 51f74d0b1d99..0be868d2a0f4 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1951,7 +1951,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( D->hasWrittenPrototype(), D->getConstexprKind(), TrailingRequiresClause); Function->setRangeEnd(D->getSourceRange().getEnd()); - Function->setUsesFPIntrin(D->usesFPIntrin()); } if (D->isInlined()) diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index fd3b981150a0..797232885687 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -888,7 +888,6 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->ODRHash = Record.readInt(); FD->setHasODRHash(true); - FD->setUsesFPIntrin(Record.readInt()); if (FD->isDefaulted()) { if (unsigned NumLookups = Record.readInt()) { diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index df7304457419..47ed44898f49 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -566,7 +566,6 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { Record.AddSourceLocation(D->getEndLoc()); Record.push_back(D->getODRHash()); - Record.push_back(D->usesFPIntrin()); if (D->isDefaulted()) { if (auto *FDI = D->getDefaultedFunctionInfo()) { diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index e7b6929199ff..82c92b77bd17 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -280,6 +280,16 @@ namespace UndefinedBehavior { constexpr float f10 = f2 - f2; // expected-error {{constant expression}} expected-note {{produces a NaN}} constexpr float f11 = f2 + f4; // expected-error {{constant expression}} expected-note {{produces a NaN}} constexpr float f12 = f2 / f2; // expected-error {{constant expression}} expected-note {{produces a NaN}} +#pragma float_control(push) +#pragma float_control(except, on) +constexpr float pi = 3.14f; +constexpr unsigned ubig = 0xFFFFFFFF; +constexpr float ce = 1.0 / 3.0; // not-expected-error {{constant expression}} not-expected-note {{floating point arithmetic suppressed in strict evaluation modes}} +constexpr int ci = (int) pi; +constexpr float fbig = (float) ubig; // not-expected-error {{constant expression}} not-expected-note {{floating point arithmetic suppressed in strict evaluation modes}} +constexpr float fabspi = __builtin_fabs(pi); // no error expected +constexpr float negpi = -pi; // expect no error on unary operator +#pragma float_control(pop) static_assert(!isinf(f1), ""); static_assert(isinf(f2), ""); static_assert(!isinf(f3), ""); diff --git a/clang/test/CodeGen/fp-floatcontrol-pragma.cpp b/clang/test/CodeGen/fp-floatcontrol-pragma.cpp index 548809521f7f..45eb14bccc06 100644 --- a/clang/test/CodeGen/fp-floatcontrol-pragma.cpp +++ b/clang/test/CodeGen/fp-floatcontrol-pragma.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -DEXCEPT=1 -fcxx-exceptions -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-NS %s // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s -// RUN: %clang_cc1 -verify -DFENV_ON=1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -DFENV_ON=1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-FENV %s // RUN: %clang_cc1 -triple %itanium_abi_triple -O3 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-O3 %s // Verify float_control(precise, off) enables fast math flags on fp operations. @@ -138,7 +138,6 @@ float test_OperatorCall() { // CHECK-LABEL define float {{.*}}test_OperatorCall{{.*}} #if FENV_ON -// expected-warning@+1{{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}} #pragma STDC FENV_ACCESS ON #endif // CHECK-LABEL: define {{.*}}callt{{.*}} @@ -146,7 +145,21 @@ float test_OperatorCall() { void callt() { volatile float z; z = z * z; -//CHECK: = fmul float + //CHECK-FENV: llvm.experimental.constrained.fmul{{.*}} +} + +// CHECK-LABEL: define {{.*}}myAdd{{.*}} +float myAdd(int i, float f) { + if (i<0) + return 1.0 + 2.0; + // Check that floating point constant folding doesn't occur if + // #pragma STC FENV_ACCESS is enabled. + //CHECK-FENV: llvm.experimental.constrained.fadd{{.*}}double 1.0{{.*}}double 2.0{{.*}} + //CHECK: store float 3.0{{.*}}retval{{.*}} + static double v = 1.0 / 3.0; + //CHECK-FENV: llvm.experimental.constrained.fptrunc.f32.f64{{.*}} + //CHECK-NOT: fdiv + return v; } #if EXCEPT @@ -201,3 +214,18 @@ float xx(double x, float z) { return fc_template_namespace::exc_on<float>(x, z); } #endif // EXCEPT + +float try_lam(float x, unsigned n) { +// CHECK: define {{.*}}try_lam{{.*}}class.anon{{.*}} + float result; + auto t = + // Lambda expression begins + [](float a, float b) { +#pragma float_control( except, on) + return a * b; +//CHECK: llvm.experimental.constrained.fmul{{.*}}fpexcept.strict + } // end of lambda expression + (1.0f,2.0f); + result = x + t; + return result; +} diff --git a/clang/test/CodeGen/pragma-fenv_access.c b/clang/test/CodeGen/pragma-fenv_access.c new file mode 100644 index 000000000000..87d12a3a7ab1 --- /dev/null +++ b/clang/test/CodeGen/pragma-fenv_access.c @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck %s + +#pragma STDC FENV_ACCESS ON + +float func_01(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_01 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +float func_02(float x, float y) { + #pragma float_control(except, off) + #pragma STDC FENV_ACCESS OFF + return x + y; +} +// CHECK-LABEL: @func_02 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + + +float func_03(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_03 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +#pragma STDC FENV_ACCESS OFF + +float func_04(float x, float y) { + #pragma float_control(except, off) + return x + y; +} +// CHECK-LABEL: @func_04 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + + +float func_05(float x, float y) { + #pragma STDC FENV_ACCESS ON + return x + y; +} +// CHECK-LABEL: @func_05 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +float func_06(float x, float y) { + #pragma float_control(except, off) + return x + y; +} +// CHECK-LABEL: @func_06 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + + +float func_07(float x, float y) { + x -= y; + if (x) { + #pragma STDC FENV_ACCESS ON + y *= 2; + } + return y + 4; +} +// 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.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") diff --git a/clang/test/Parser/fp-floatcontrol-syntax.cpp b/clang/test/Parser/fp-floatcontrol-syntax.cpp index 0593c2a0385e..5e7e8b1b03bc 100644 --- a/clang/test/Parser/fp-floatcontrol-syntax.cpp +++ b/clang/test/Parser/fp-floatcontrol-syntax.cpp @@ -26,19 +26,13 @@ void check_stack() { double a = 0.0; double b = 1.0; -//FIXME At some point this warning will be removed, until then -// document the warning -#ifdef FAST -// expected-warning@+1{{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}} -#pragma STDC FENV_ACCESS ON -#else -#pragma STDC FENV_ACCESS ON // expected-warning{{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}} -#endif #ifdef STRICT #pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when except is enabled}} #else -// Currently FENV_ACCESS cannot be enabled by pragma, skip error check -#pragma float_control(precise, off) // not-expected-error {{'#pragma float_control(precise, off)' is illegal when fenv_access is enabled}} +#ifndef FAST +#pragma STDC FENV_ACCESS ON +#pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when except is enabled}} +#endif #endif #pragma float_control(precise, on) diff --git a/clang/test/Parser/pragma-fenv_access.c b/clang/test/Parser/pragma-fenv_access.c new file mode 100644 index 000000000000..c789f3313132 --- /dev/null +++ b/clang/test/Parser/pragma-fenv_access.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -ffp-exception-behavior=strict -DSTRICT -fsyntax-only -verify %s +// RUN: %clang_cc1 -x c++ -DCPP -DSTRICT -ffp-exception-behavior=strict -fsyntax-only -verify %s +#ifdef CPP +#define CONST constexpr +#else +#define CONST const +#endif + +#pragma STDC FENV_ACCESS IN_BETWEEN // expected-warning {{expected 'ON' or 'OFF' or 'DEFAULT' in pragma}} + +#pragma STDC FENV_ACCESS OFF + +float func_04(int x, float y) { + if (x) + return y + 2; + #pragma STDC FENV_ACCESS ON // expected-error{{'#pragma STDC FENV_ACCESS' can only appear at file scope or at the start of a compound statement}} + return x + y; +} + +#pragma STDC FENV_ACCESS ON +int main() { + CONST float one = 1.0F ; + CONST float three = 3.0F ; + CONST float four = 4.0F ; + CONST float frac_ok = one/four; +#if !defined(CPP) +//expected-note@+2 {{declared here}} +#endif + CONST float frac = one/three; + CONST double d = one; + CONST int not_too_big = 255; + CONST float fnot_too_big = not_too_big; + CONST int too_big = 0x7ffffff0; +#if defined(CPP) +//expected-warning@+2{{implicit conversion}} +#endif + CONST float fbig = too_big; // inexact +#if !defined(CPP) +#define static_assert _Static_assert +#endif +enum { + e1 = (int)one, e3 = (int)three, e4 = (int)four, e_four_quarters = (int)(frac_ok * 4) +}; +static_assert(e1 == 1 && e3 == 3 && e4 == 4 && e_four_quarters == 1, ""); +enum { +#if !defined(CPP) +// expected-error@+2 {{not an integer constant expression}} expected-note@+2 {{is not a constant expression}} +#endif + e_three_thirds = (int)(frac * 3) +}; + if (one <= four) return 0; + return -1; +} diff --git a/clang/test/Preprocessor/pragma_unknown.c b/clang/test/Preprocessor/pragma_unknown.c index 81fe88cd3855..a7c4829c279d 100644 --- a/clang/test/Preprocessor/pragma_unknown.c +++ b/clang/test/Preprocessor/pragma_unknown.c @@ -16,15 +16,6 @@ // CHECK: {{^}}#pragma STDC FP_CONTRACT DEFAULT{{$}} // CHECK: {{^}}#pragma STDC FP_CONTRACT IN_BETWEEN{{$}} -#pragma STDC FENV_ACCESS ON // expected-warning {{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}} -#pragma STDC FENV_ACCESS OFF -#pragma STDC FENV_ACCESS DEFAULT -#pragma STDC FENV_ACCESS IN_BETWEEN // expected-warning {{expected 'ON' or 'OFF' or 'DEFAULT' in pragma}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS ON{{$}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS OFF{{$}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS DEFAULT{{$}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS IN_BETWEEN{{$}} - #pragma STDC CX_LIMITED_RANGE ON #pragma STDC CX_LIMITED_RANGE OFF #pragma STDC CX_LIMITED_RANGE DEFAULT _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits