diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3028,6 +3028,35 @@ section of the code. This can be useful when fast contraction is otherwise enabled for the translation unit with the ``-ffp-contract=fast`` flag. +The ``#pragma float_control`` pragma allows precise floating-point +semantics and floating-point exception behavior to be specified +for a section of the source code. This pragma can only appear at file scope or +at the start of a compound statement (excluding comments). When using within a +compound statement, the pragma is active within the scope of the compound +statement. This pragma is modeled after a Microsoft pragma with the +same spelling and syntax. For pragmas specified at file scope, a stack +is supported so that the pragma float_control settings can be pushed or popped. + +When ``float_control(precise, on)`` is enabled, the section of code governed +by the pragma behaves as though the command-line option ``ffp-model=precise`` +is enabled. That is, fast-math is disabled and fp-contract=on (fused +multiple add) is enabled. + +When ``float_control(except, on)`` is enabled, the section of code governed +by the pragma behaves as though the command-line + ``ffp-exception-behavior=strict`` is enabled, ``float-control(precise, off)`` +selects ``ffp-exception-behavior=ignore``. + +The full syntax this pragma supports is ``float_control(except|precise, on|off, [push])`` and ``float_control(push|pop)``. The ``push`` and ``pop`` forms can only occur at file scope. + +.. code-block:: c++ + + for(...) { + // This block will be compiled with fno-fast-math and ffp-contract=on + #pragma float_control(precise, on) + a = b[i] * c[i] + e; + } + Specifying an attribute for multiple declarations (#pragma clang attribute) =========================================================================== diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1110,8 +1110,8 @@ "Microsoft environment">, InGroup; // - #pragma fp_contract -def err_pragma_fp_contract_scope : Error< - "'#pragma fp_contract' can only appear at file scope or at the start of a " +def err_pragma_file_or_compound_scope : Error< + "'#pragma %0' can only appear at file scope or at the start of a " "compound statement">; // - #pragma stdc unknown def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">, @@ -1130,6 +1130,11 @@ def err_pragma_detect_mismatch_malformed : Error< "pragma detect_mismatch is malformed; it requires two comma-separated " "string literals">; +// - #pragma float_control +def err_pragma_float_control_malformed : Error< + "pragma float_control is malformed; it requires one or two comma-separated " + "arguments">; +def err_pragma_float_control_unknown_kind : Error<"unknown kind of pragma float control">; // - #pragma pointers_to_members def err_pragma_pointers_to_members_unknown_kind : Error< "unexpected %0, expected to see one of %select{|'best_case', 'full_generality', }1" @@ -1297,9 +1302,8 @@ def err_pragma_fp_invalid_argument : Error< "unexpected argument '%0' to '#pragma clang fp %1'; " "expected 'on', 'fast' or 'off'">; -def err_pragma_fp_scope : Error< - "'#pragma clang fp' can only appear at file scope or at the start of a " - "compound statement">; +def err_pragma_fc_pp_scope : Error< + "'#pragma float_control push/pop' can only appear at file scope">; def err_pragma_invalid_keyword : Error< "invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">; 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 @@ -359,7 +359,8 @@ FPOptions() : fp_contract(LangOptions::FPC_Off), fenv_access(LangOptions::FEA_Off), rounding(LangOptions::FPR_ToNearest), - exceptions(LangOptions::FPE_Ignore) + exceptions(LangOptions::FPE_Ignore), + fp_precise(false) {} // Used for serializing. @@ -367,14 +368,17 @@ : fp_contract(static_cast(I & 3)), fenv_access(static_cast((I >> 2) & 1)), rounding(static_cast((I >> 3) & 7)), - exceptions(static_cast((I >> 6) & 3)) + exceptions(static_cast((I >> 6) & 3)), + fp_precise(static_cast((I >> 8) & 1)) {} explicit FPOptions(const LangOptions &LangOpts) : fp_contract(LangOpts.getDefaultFPContractMode()), fenv_access(LangOptions::FEA_Off), rounding(LangOptions::FPR_ToNearest), - exceptions(LangOptions::FPE_Ignore) + exceptions(LangOpts.getFPExceptionMode()), + fp_precise(!LangOpts.OpenCL && !LangOpts.FastMath && + LangOpts.getDefaultFPContractMode() == LangOptions::FPC_On) {} // FIXME: Use getDefaultFEnvAccessMode() when available. @@ -404,6 +408,17 @@ fenv_access = LangOptions::FEA_On; } + void setFPPreciseEnabled(bool Value) { + fp_precise = Value; + + /* Precise mode implies fp_contract and disables ffast-math */ + if (fp_precise) + setAllowFPContractWithinStatement(); + else + setDisallowFPContract(); + } + bool fpPreciseEnabled() { return fp_precise; } + void setDisallowFEnvAccess() { fenv_access = LangOptions::FEA_Off; } LangOptions::FPRoundingModeKind getRoundingMode() const { @@ -431,7 +446,7 @@ /// Used to serialize this. unsigned getInt() const { return fp_contract | (fenv_access << 2) | (rounding << 3) - | (exceptions << 6); + | (exceptions << 6) | (fp_precise << 8); } private: @@ -442,6 +457,7 @@ unsigned fenv_access : 1; unsigned rounding : 3; unsigned exceptions : 2; + unsigned fp_precise : 1; }; /// Describes the kind of translation unit being processed. diff --git a/clang/include/clang/Basic/PragmaKinds.h b/clang/include/clang/Basic/PragmaKinds.h --- a/clang/include/clang/Basic/PragmaKinds.h +++ b/clang/include/clang/Basic/PragmaKinds.h @@ -25,6 +25,16 @@ PMSST_ON // #pragms ms_struct on }; +enum PragmaFloatControlKind { + PFC_Unknown, + PFC_Precise, // #pragma float_control(precise, [,on]) + PFC_NoPrecise, // #pragma float_control(precise, off) + PFC_Except, // #pragma float_control(except [,on]) + PFC_NoExcept, // #pragma float_control(except, off) + PFC_Push, // #pragma float_control(push) + PFC_Pop // #pragma float_control(pop) +}; + } #endif diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -806,6 +806,11 @@ // handles them. PRAGMA_ANNOTATION(pragma_fenv_access) +// Annotation for #pragma float_control +// The lexer produces these so that they only take effect when the parser +// handles them. +PRAGMA_ANNOTATION(pragma_float_control) + // Annotation for #pragma pointers_to_members... // The lexer produces these so that they only take effect when the parser // handles them. diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -178,6 +178,7 @@ std::unique_ptr PCSectionHandler; std::unique_ptr MSCommentHandler; std::unique_ptr MSDetectMismatchHandler; + std::unique_ptr FloatControlHandler; std::unique_ptr MSPointersToMembers; std::unique_ptr MSVtorDisp; std::unique_ptr MSInitSeg; @@ -721,6 +722,10 @@ /// #pragma STDC FENV_ACCESS... void HandlePragmaFEnvAccess(); + /// Handle the annotation token produced for + /// #pragma float_control + void HandlePragmaFloatControl(); + /// \brief Handle the annotation token produced for /// #pragma clang fp ... void HandlePragmaFP(); 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 @@ -552,6 +552,22 @@ PragmaStack ConstSegStack; PragmaStack CodeSegStack; + class FpPragmaStateType { + public: + bool HasPragma; + bool IsPrecise; + bool IsExcept; + FpPragmaStateType() : HasPragma(false), IsPrecise(false), IsExcept(false){}; + explicit FpPragmaStateType(unsigned I) : + HasPragma(I & 0B100), IsPrecise(I & 0B10), IsExcept(I & 0B01) {}; + FpPragmaStateType(bool HasPragma, bool IsPrecise, bool IsExcept) : + HasPragma(HasPragma), IsPrecise(IsPrecise), IsExcept(IsExcept){}; + unsigned getInt() const { + return (HasPragma<<2) | (IsPrecise << 1) | IsExcept; + } + }; + PragmaStack FpPragmaStack; + // 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 { @@ -9367,6 +9383,11 @@ void ActOnPragmaDetectMismatch(SourceLocation Loc, StringRef Name, StringRef Value); + /// ActOnPragmaFloatControl - Call on well-formed \#pragma float_control + void ActOnPragmaFloatControl(SourceLocation Loc, + PragmaMsStackAction Action, + PragmaFloatControlKind Value); + /// ActOnPragmaUnused - Called on well-formed '\#pragma unused'. void ActOnPragmaUnused(const Token &Identifier, Scope *curScope, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1748,6 +1748,10 @@ if (CodeGenOpts.NullPointerIsValid) FuncAttrs.addAttribute("null-pointer-is-valid", "true"); + if (getLangOpts().getFPRoundingMode() != LangOptions::FPR_ToNearest || + getLangOpts().getFPExceptionMode() != LangOptions::FPE_Ignore) + FuncAttrs.addAttribute(llvm::Attribute::StrictFP); + // TODO: Omit attribute when the default is IEEE. if (CodeGenOpts.FPDenormalMode != llvm::DenormalMode::Invalid) FuncAttrs.addAttribute("denormal-fp-math", diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -423,6 +423,29 @@ Value *Visit(Expr *E) { ApplyDebugLocation DL(CGF, E); + if (BinaryOperator * BinOp = dyn_cast(E)) { + // Preserve the old values + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + + auto RoundingBehavior = Builder.getDefaultConstrainedRounding(); + auto NewExceptionBehavior = ToConstrainedExceptMD( + BinOp->getFPFeatures().getExceptionMode()); + Builder.setDefaultConstrainedExcept(NewExceptionBehavior); + if (BinOp->getFPFeatures().fpPreciseEnabled()) { + Builder.clearFastMathFlags(); + assert(BinOp->getFPFeatures().allowFPContractWithinStatement() && + "Expected FPFeatures to allow constract within statement"); + } + assert((CGF.CurFuncDecl==nullptr || + Builder.getIsFPConstrained() || + isa(CGF.CurFuncDecl) || + isa(CGF.CurFuncDecl) || + (NewExceptionBehavior == llvm::fp::ebIgnore && + RoundingBehavior == llvm::fp::rmToNearest)) && + "FPConstrained should be enabled on entire function"); + + return StmtVisitor::Visit(E); + } return StmtVisitor::Visit(E); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4418,6 +4418,16 @@ } } // end namespace CodeGen + +// Map the LangOption for floating point rounding mode into +// the corresponding enum in the IR. +llvm::fp::RoundingMode ToConstrainedRoundingMD( + LangOptions::FPRoundingModeKind Kind); + +// Map the LangOption for floating point exception behavior into +// the corresponding enum in the IR. +llvm::fp::ExceptionBehavior ToConstrainedExceptMD( + LangOptions::FPExceptionModeKind Kind); } // end namespace clang #endif 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 @@ -108,7 +108,7 @@ // Map the LangOption for rounding mode into // the corresponding enum in the IR. -static llvm::fp::RoundingMode ToConstrainedRoundingMD( +llvm::fp::RoundingMode clang::ToConstrainedRoundingMD( LangOptions::FPRoundingModeKind Kind) { switch (Kind) { @@ -123,7 +123,7 @@ // Map the LangOption for exception behavior into // the corresponding enum in the IR. -static llvm::fp::ExceptionBehavior ToConstrainedExceptMD( +llvm::fp::ExceptionBehavior clang::ToConstrainedExceptMD( LangOptions::FPExceptionModeKind Kind) { switch (Kind) { @@ -140,14 +140,14 @@ auto fpExceptionBehavior = ToConstrainedExceptMD( getLangOpts().getFPExceptionMode()); + Builder.setDefaultConstrainedRounding(fpRoundingMode); + Builder.setDefaultConstrainedExcept(fpExceptionBehavior); if (fpExceptionBehavior == llvm::fp::ebIgnore && fpRoundingMode == llvm::fp::rmToNearest) // Constrained intrinsics are not used. - ; + Builder.setIsFPConstrained(false); else { Builder.setIsFPConstrained(true); - Builder.setDefaultConstrainedRounding(fpRoundingMode); - Builder.setDefaultConstrainedExcept(fpExceptionBehavior); } } @@ -1263,6 +1263,9 @@ FunctionArgList Args; QualType ResTy = BuildFunctionArgList(GD, Args); + if (FD->usesFPIntrin()) + Builder.setIsFPConstrained(true); + // Check if we should generate debug info for this function. if (FD->hasAttr()) DebugInfo = nullptr; // disable debug info indefinitely for this function 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 @@ -184,6 +184,16 @@ Sema &Actions; }; +struct PragmaFloatControlHandler : public PragmaHandler { + PragmaFloatControlHandler(Sema &Actions) + : PragmaHandler("float_control"), Actions(Actions) {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; + +private: + Sema &Actions; +}; + struct PragmaMSPointersToMembers : public PragmaHandler { explicit PragmaMSPointersToMembers() : PragmaHandler("pointers_to_members") {} void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, @@ -334,6 +344,9 @@ PP.AddPragmaHandler(MSCommentHandler.get()); } + FloatControlHandler = + std::make_unique(Actions); + PP.AddPragmaHandler(FloatControlHandler.get()); if (getLangOpts().MicrosoftExt) { MSDetectMismatchHandler = std::make_unique(Actions); @@ -438,6 +451,8 @@ PP.RemovePragmaHandler("clang", PCSectionHandler.get()); PCSectionHandler.reset(); + PP.RemovePragmaHandler(FloatControlHandler.get()); + FloatControlHandler.reset(); if (getLangOpts().MicrosoftExt) { PP.RemovePragmaHandler(MSDetectMismatchHandler.get()); MSDetectMismatchHandler.reset(); @@ -646,6 +661,18 @@ ConsumeAnnotationToken(); } +void Parser::HandlePragmaFloatControl() { + assert(Tok.is(tok::annot_pragma_float_control)); + + uintptr_t Value = reinterpret_cast(Tok.getAnnotationValue()); + Sema::PragmaMsStackAction Action = + static_cast((Value >> 16) & 0xFFFF); + PragmaFloatControlKind Kind = + PragmaFloatControlKind(Value & 0xFFFF); + SourceLocation PragmaLoc = ConsumeAnnotationToken(); + Actions.ActOnPragmaFloatControl(PragmaLoc, Action, Kind); +} + void Parser::HandlePragmaFEnvAccess() { assert(Tok.is(tok::annot_pragma_fenv_access)); tok::OnOffSwitch OOS = @@ -2489,6 +2516,139 @@ PP.EnterToken(AnnotTok, /*IsReinject*/ false); } +/// Handle the \#pragma float_control extension. +/// +/// The syntax is: +/// \code +/// #pragma float_control(keyword[, setting] [,push]) +/// \endcode +/// Where 'keyword' and 'setting' are identifiers. +// 'keyword' can be: precise, except, push, pop +// 'setting' can be: on, off +/// The optional arguments 'setting' and 'push' are supported only +/// when the keyword is 'precise' or 'except'. +void PragmaFloatControlHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &Tok) { + Sema::PragmaMsStackAction Action = Sema::PSK_Set; + SourceLocation FloatControlLoc = Tok.getLocation(); + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(FloatControlLoc, diag::err_expected) << tok::l_paren; + return; + } + + // Read the identifier. + PP.Lex(Tok); + if (Tok.isNot(tok::identifier)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + + // Verify that this is one of the float control options. + IdentifierInfo *II = Tok.getIdentifierInfo(); + PragmaFloatControlKind Kind = + llvm::StringSwitch( + II->getName()) + .Case("precise", PFC_Precise) + .Case("except", PFC_Except) + .Case("push", PFC_Push) + .Case("pop", PFC_Pop) + .Default(PFC_Unknown); + PP.Lex(Tok); // the identifier + if (Kind == PFC_Unknown) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_unknown_kind); + return; + } else if (Kind == PFC_Push || + Kind == PFC_Pop) { + if (Actions.getCurScope()->getParent() != nullptr) { + // FIXME this doesn't detect file-scope: + // the token immediately following a function definition + // returns false, but the token immediately following a forward + // class declaration returns true + //FIXME PP.Diag(Tok.getLocation(), diag::err_pragma_fc_pp_scope); + return; + } + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // Eat the r_paren + Action = (Kind == PFC_Pop) ? Sema::PSK_Pop : Sema::PSK_Push; + } else { + if (Tok.is(tok::r_paren)) + // Selecting Precise or Except + PP.Lex(Tok); // the r_paren + else if (Tok.isNot(tok::comma)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } else { + PP.Lex(Tok); // , + if (!Tok.isAnyIdentifier()) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + StringRef PushOnOff = Tok.getIdentifierInfo()->getName(); + if (PushOnOff == "on") + // Kind is set correctly + ; + else if (PushOnOff == "off") { + if (Kind == PFC_Precise ) + Kind = PFC_NoPrecise ; + if (Kind == PFC_Except ) + Kind = PFC_NoExcept ; + } else if (PushOnOff == "push") { + if (!Actions.CurContext->isTranslationUnit()) { + //FIXME PP.Diag(Tok.getLocation(), diag::err_pragma_fc_pp_scope); + return; + } + Action = Sema::PSK_Push_Set; + } else { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // the identifier + if (Tok.is(tok::comma)) { + PP.Lex(Tok); // , + if (!Tok.isAnyIdentifier()) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + StringRef ExpectedPush = Tok.getIdentifierInfo()->getName(); + if (ExpectedPush == "push") { + if (!Actions.CurContext->isTranslationUnit()) { + //FIXME PP.Diag(Tok.getLocation(), diag::err_pragma_fc_pp_scope); + return; + } + Action = Sema::PSK_Push_Set; + } else { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // the push identifier + } + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // the r_paren + } + } + SourceLocation EndLoc = Tok.getLocation(); + + // Note: there is no accomodation for PP callback for this pragma. + + // Enter the annotation. + Token AnnotTok; + AnnotTok.startToken(); + AnnotTok.setKind(tok::annot_pragma_float_control); + AnnotTok.setLocation(FloatControlLoc); + AnnotTok.setAnnotationEndLoc(EndLoc); + AnnotTok.setAnnotationValue(reinterpret_cast( + static_cast((Action << 16) | (Kind & 0xFFFF)))); + PP.EnterToken(AnnotTok, /*IsReinject=*/false); +} + /// Handle the Microsoft \#pragma detect_mismatch extension. /// /// The syntax is: 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 @@ -353,13 +353,13 @@ case tok::annot_pragma_fp_contract: ProhibitAttributes(Attrs); - Diag(Tok, diag::err_pragma_fp_contract_scope); + Diag(Tok, diag::err_pragma_file_or_compound_scope) << "fp_contract"; ConsumeAnnotationToken(); return StmtError(); case tok::annot_pragma_fp: ProhibitAttributes(Attrs); - Diag(Tok, diag::err_pragma_fp_scope); + Diag(Tok, diag::err_pragma_file_or_compound_scope) << "clang fp"; ConsumeAnnotationToken(); return StmtError(); @@ -368,6 +368,12 @@ HandlePragmaFEnvAccess(); return StmtEmpty(); + case tok::annot_pragma_float_control: + ProhibitAttributes(Attrs); + Diag(Tok, diag::err_pragma_file_or_compound_scope) << "float_control"; + ConsumeAnnotationToken(); + return StmtError(); + case tok::annot_pragma_opencl_extension: ProhibitAttributes(Attrs); HandlePragmaOpenCLExtension(); @@ -936,6 +942,9 @@ case tok::annot_pragma_fenv_access: HandlePragmaFEnvAccess(); break; + case tok::annot_pragma_float_control: + HandlePragmaFloatControl(); + break; case tok::annot_pragma_ms_pointers_to_members: HandlePragmaMSPointersToMembers(); break; 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 @@ -763,6 +763,9 @@ case tok::annot_pragma_fenv_access: HandlePragmaFEnvAccess(); return nullptr; + case tok::annot_pragma_float_control: + HandlePragmaFloatControl(); + return nullptr; case tok::annot_pragma_fp: HandlePragmaFP(); break; 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 @@ -158,6 +158,8 @@ VtorDispStack(LangOpts.getVtorDispMode()), PackStack(0), DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr), CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr), + FpPragmaStack({false, false, pp.getLangOpts().getFPExceptionMode() == + LangOptions::FPE_Strict}), PragmaAttributeCurrentTargetDecl(nullptr), IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp), 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 @@ -407,6 +407,37 @@ Consumer.HandleTopLevelDecl(DeclGroupRef(PDMD)); } +void Sema::ActOnPragmaFloatControl(SourceLocation Loc, + PragmaMsStackAction Action, + PragmaFloatControlKind Value) { + auto NewValue = FpPragmaStack.CurrentValue; + NewValue.HasPragma = true; + switch(Value) { + default: + llvm_unreachable("invalid pragma float_control kind"); + case PFC_Precise: + NewValue.IsPrecise = true; + break; + case PFC_NoPrecise: + NewValue.IsPrecise = false; + break; + case PFC_Except: + NewValue.IsExcept = true; + break; + case PFC_NoExcept: + NewValue.IsExcept = false; + break; + case PFC_Push: + case PFC_Pop: + break; + } + FpPragmaStack.Act(Loc, Action, StringRef(), NewValue); + NewValue = FpPragmaStack.CurrentValue; + FPFeatures.setFPPreciseEnabled(NewValue.IsPrecise); + FPFeatures.setExceptionMode(NewValue.IsExcept ? + LangOptions::FPE_Strict : LangOptions::FPE_Ignore); +} + void Sema::ActOnPragmaMSPointersToMembers( LangOptions::PragmaMSPointersToMembersKind RepresentationMethod, SourceLocation PragmaLoc) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13132,10 +13132,15 @@ return ExprError(); if (ResultTy->isRealFloatingType() && - (getLangOpts().getFPRoundingMode() != LangOptions::FPR_ToNearest || + ((FpPragmaStack.CurrentValue.HasPragma && + FpPragmaStack.CurrentValue.IsExcept) || + getLangOpts().getFPRoundingMode() != LangOptions::FPR_ToNearest || getLangOpts().getFPExceptionMode() != LangOptions::FPE_Ignore)) // Mark the current function as usng floating point constrained intrinsics if (FunctionDecl *F = dyn_cast(CurContext)) { + // The pragma's do not pertain to expressions that + // occur, for example, inside an internal global_var_init_function + // (In this case, the CurContext function decl is null.) F->setUsesFPIntrin(true); } diff --git a/clang/test/CodeGen/fp-floatcontrol-class.cpp b/clang/test/CodeGen/fp-floatcontrol-class.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fp-floatcontrol-class.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +#pragma float_control(except, on) +float y(); +class ON { +float z = y() * 1; +// CHECK-LABEL: define {{.*}} void @_ZN2ONC2Ev{{.*}} +// float_control does not pertain to initializer expressions +//CHECK: fmul float +}; +ON on; +#pragma float_control( except, off) +class OFF { +float w = y() * 1; +// CHECK-LABEL: define {{.*}} void @_ZN3OFFC2Ev{{.*}} +//CHECK: fmul float +}; +OFF off; diff --git a/clang/test/CodeGen/fp-floatcontrol-pragma.cpp b/clang/test/CodeGen/fp-floatcontrol-pragma.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fp-floatcontrol-pragma.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +float fff(float x, float y) { +// CHECK-LABEL: define float @_Z3fffff{{.*}} +// CHECK: entry +#pragma float_control(except, on) + float z; + z = z*z; +//CHECK: llvm.experimental.constrained.fmul{{.*}} + { + z = x*y; +//CHECK: llvm.experimental.constrained.fmul{{.*}} + } + { +// This pragma has no effect since if there are any fp intrin in the +// function then all the operations need to be fp intrin +#pragma float_control(except, off) + z = z + x*y; +//CHECK: llvm.experimental.constrained.fmul{{.*}} + } + z = z*z; +//CHECK: llvm.experimental.constrained.fmul{{.*}} + return z; +} +float check_precise(float x, float y) { +// CHECK-LABEL: define float @_Z13check_preciseff{{.*}} + float z; + { +#pragma float_control(precise, on) + z = x*y + z; +//CHECK: llvm.fmuladd{{.*}} + } + { +#pragma float_control(precise, off) + z = x*y + z; +//CHECK: fmul float +//CHECK: fadd float + } + return z; +} +float fma_test1(float a, float b, float c) { +// CHECK-LABEL define float @_Z9fma_test1fff{{.*}} +#pragma float_control(precise, on) + float x = a * b + c; +//CHECK: fmuladd + return x; +} diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp @@ -0,0 +1,171 @@ +// RUN: %clang_cc1 -DDEFAULT=1 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DDEFAULT %s +// RUN: %clang_cc1 -DEBSTRICT=1 -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DEBSTRICT %s + +#define FUN(n) (float z) { return n * z + n; } + +float fun_default FUN(1) +//CHECK-LABEL: define {{.*}} @_Z11fun_defaultf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +//CHECK-DDEFAULT: fadd float +#endif +#if EBSTRICT +// Note that backend wants constrained intrinsics used "everywhere" +// if they have been requested on the command line, so fp operations +// will be built with constrained intrinsics and default settings +// for exception behavior and rounding mode. +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict +#endif +class ResetScope; + +#pragma float_control(except, on, push) +float exc_on FUN(2) +//CHECK-LABEL: define {{.*}} @_Z6exc_onf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: llvm.experimental.constrained.fmul{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict +#endif + +class ResetScope; +#pragma float_control(pop) +float exc_pop FUN(5) +//CHECK-LABEL: define {{.*}} @_Z7exc_popf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +//CHECK-DDEFAULT: fadd float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict +#endif + +class ResetScope; +#pragma float_control(except, off) +float exc_off FUN(5) +//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +//CHECK-DDEFAULT: fadd float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +#pragma float_control(precise, on, push) +float precise_on FUN(3) +//CHECK-LABEL: define {{.*}} @_Z10precise_onf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//Usually 'precise' forces the creation of fmuladd but I believe +//the backend doesn't yet support a constrained fmuladd. +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +//CHECK-DEBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +#pragma float_control(pop) +float precise_pop FUN(3) +//CHECK-LABEL: define {{.*}} @_Z11precise_popf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +//CHECK-DDEFAULT: fadd float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +//CHECK-DEBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +#pragma float_control(precise, off) +float precise_off FUN(4) +//CHECK-LABEL: define {{.*}} @_Z11precise_offf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +//CHECK-DDEFAULT: fadd float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +//CHECK-DEBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +#pragma float_control(precise, on) +float precise_on2 FUN(3) +//CHECK-LABEL: define {{.*}} @_Z11precise_on2f{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +//CHECK-DEBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +#pragma float_control(push) +float precise_push FUN(3) +//CHECK-LABEL: define {{.*}} @_Z12precise_pushf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +//CHECK-DEBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +#pragma float_control(precise, off) +float precise_off2 FUN(4) +//CHECK-LABEL: define {{.*}} @_Z12precise_off2f{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +//CHECK-DDEFAULT: fadd float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +//CHECK-DEBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +#pragma float_control(pop) +float precise_pop2 FUN(3) +//CHECK-LABEL: define {{.*}} @_Z12precise_pop2f{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +//CHECK-DEBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}ignore +#endif + +class ResetScope; +// --------- end of push pop test +#pragma float_control(except, on) +float y(); +class ON { +float z = y() * 1; +//CHECK-LABEL: define {{.*}} void @_ZN2ONC2Ev{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict +#endif +}; +ON on; +#pragma float_control( except, off) +class OFF { +float w = y() * 1; +//CHECK-LABEL: define {{.*}} void @_ZN3OFFC2Ev{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}ignore +#endif +}; +OFF off; + diff --git a/clang/test/Parser/fp-floatcontrol-syntax.cpp b/clang/test/Parser/fp-floatcontrol-syntax.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/fp-floatcontrol-syntax.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -DCHECK_ERROR %s +// XFAIL: * + +float function_scope(float a) { + return a; +} + +// There seems to be a bug in Actions.CurContext->isTranslationUnit() +// unless this dummy class is used. FIXME Fix the issue then remove this +// workaround. +#define TU_WORKAROUND +#ifdef TU_WORKAROUND +class ResetTUScope; +#endif +#ifdef CHECK_ERROR +# pragma float_control(push) +# pragma float_control(pop) +# pragma float_control(precise,on,push) +void check_stack() { +#pragma float_control(push) // expected-error {{can only appear at file scope}} +#pragma float_control(pop) // expected-error {{can only appear at file scope}} +#pragma float_control(precise,on,push) // expected-error {{can only appear at file scope}} + return; +} +#endif diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -299,10 +299,16 @@ IRBuilderBase &Builder; FastMathFlags FMF; MDNode *FPMathTag; + bool IsFPConstrained; + fp::ExceptionBehavior DefaultConstrainedExcept; + fp::RoundingMode DefaultConstrainedRounding; public: FastMathFlagGuard(IRBuilderBase &B) - : Builder(B), FMF(B.FMF), FPMathTag(B.DefaultFPMathTag) {} + : Builder(B), FMF(B.FMF), FPMathTag(B.DefaultFPMathTag), + IsFPConstrained(B.IsFPConstrained), + DefaultConstrainedExcept(B.DefaultConstrainedExcept), + DefaultConstrainedRounding(B.DefaultConstrainedRounding) {} FastMathFlagGuard(const FastMathFlagGuard &) = delete; FastMathFlagGuard &operator=(const FastMathFlagGuard &) = delete; @@ -310,6 +316,9 @@ ~FastMathFlagGuard() { Builder.FMF = FMF; Builder.DefaultFPMathTag = FPMathTag; + Builder.IsFPConstrained = IsFPConstrained; + Builder.DefaultConstrainedExcept = DefaultConstrainedExcept; + Builder.DefaultConstrainedRounding = DefaultConstrainedRounding; } };