diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3119,6 +3119,41 @@ 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 ``pragma 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, ``-ffast-math`` is disabled and ``-ffp-contract=on`` +(fused multiply add) is enabled. + +When ``pragma float_control(except, on)`` is enabled, the section of code governed +by the pragma behaves as though the command-line option + ``-ffp-exception-behavior=strict`` is enabled, +when ``pragma float_control(precise, off)`` is enabled, the section of code +governed by the pragma behaves as though the command-line option +``-ffp-exception-behavior=ignore`` is enabled. + +The full syntax this pragma supports is +``float_control(except|precise, on|off [, push])`` and +``float_control(push|pop)``. +The ``push`` and ``pop`` forms, including using ``push`` as the optional +third argument, 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/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2054,9 +2054,11 @@ public: typedef UnaryOperatorKind Opcode; + FPOptions FPFeatures; UnaryOperator(Expr *input, Opcode opc, QualType type, ExprValueKind VK, - ExprObjectKind OK, SourceLocation l, bool CanOverflow) + ExprObjectKind OK, SourceLocation l, bool CanOverflow, + FPOptions FP_Features) : Expr(UnaryOperatorClass, type, VK, OK, input->isTypeDependent() || type->isDependentType(), input->isValueDependent(), @@ -2067,6 +2069,7 @@ UnaryOperatorBits.Opc = opc; UnaryOperatorBits.CanOverflow = CanOverflow; UnaryOperatorBits.Loc = l; + FPFeatures = FP_Features; } /// Build an empty unary operator. @@ -2095,6 +2098,23 @@ bool canOverflow() const { return UnaryOperatorBits.CanOverflow; } void setCanOverflow(bool C) { UnaryOperatorBits.CanOverflow = C; } + void setFPFeatures(FPOptions F) { + FPFeatures = F; + } + FPOptions getFPFeatures() const { + return FPFeatures; + } + + // Get the FP contractability status of this operator. Only meaningful for + // operations on floating point types. + bool isFPContractableWithinStatement() const { + return getFPFeatures().allowFPContractWithinStatement(); + } + + // Get the FENV_ACCESS status of this operator. Only meaningful for + // operations on floating point types. + bool isFEnvAccessOn() const { return getFPFeatures().allowFEnvAccess(); } + /// isPostfix - Return true if this is a postfix operation, like x++. static bool isPostfix(Opcode Op) { return Op == UO_PostInc || Op == UO_PostDec; @@ -2598,12 +2618,15 @@ static constexpr ADLCallKind NotADL = ADLCallKind::NotADL; static constexpr ADLCallKind UsesADL = ADLCallKind::UsesADL; + FPOptions FPFeatures; + protected: /// Build a call expression, assuming that appropriate storage has been /// allocated for the trailing objects. CallExpr(StmtClass SC, Expr *Fn, ArrayRef PreArgs, ArrayRef Args, QualType Ty, ExprValueKind VK, - SourceLocation RParenLoc, unsigned MinNumArgs, ADLCallKind UsesADL); + SourceLocation RParenLoc, FPOptions FP_Features, + unsigned MinNumArgs, ADLCallKind UsesADL); /// Build an empty call expression, for deserialization. CallExpr(StmtClass SC, unsigned NumPreArgs, unsigned NumArgs, @@ -2645,7 +2668,8 @@ /// expression on the stack. static CallExpr *Create(const ASTContext &Ctx, Expr *Fn, ArrayRef Args, QualType Ty, ExprValueKind VK, - SourceLocation RParenLoc, unsigned MinNumArgs = 0, + SourceLocation RParenLoc, FPOptions FP_Features, + unsigned MinNumArgs = 0, ADLCallKind UsesADL = NotADL); /// Create a temporary call expression with no arguments in the memory @@ -2689,6 +2713,21 @@ return dyn_cast_or_null(getCalleeDecl()); } + // Set the FPFeatures status of this CallExpr. Only meaningful for + // operations on floating point types. e.g. intrinsics + void setFPFeatures(FPOptions F) { + FPFeatures = F; + } + FPOptions getFPFeatures() const { + return FPFeatures; + } + + // Get the FP contractability status of this CallExpr. Only meaningful for + // operations on floating point types. e.g. intrinsics + bool isFPContractableWithinStatement() const { + return FPFeatures.allowFPContractWithinStatement(); + } + /// getNumArgs - Return the number of actual arguments to this call. unsigned getNumArgs() const { return NumArgs; } @@ -3441,10 +3480,11 @@ public: typedef BinaryOperatorKind Opcode; + FPOptions FPFeatures; BinaryOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResTy, ExprValueKind VK, ExprObjectKind OK, - SourceLocation opLoc, FPOptions FPFeatures) + SourceLocation opLoc, FPOptions FP_Features) : Expr(BinaryOperatorClass, ResTy, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), @@ -3453,7 +3493,7 @@ (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())) { BinaryOperatorBits.Opc = opc; - BinaryOperatorBits.FPFeatures = FPFeatures.getInt(); + FPFeatures = FP_Features; BinaryOperatorBits.OpLoc = opLoc; SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; @@ -3609,11 +3649,11 @@ // Set the FP contractability status of this operator. Only meaningful for // operations on floating point types. void setFPFeatures(FPOptions F) { - BinaryOperatorBits.FPFeatures = F.getInt(); + FPFeatures = F; } FPOptions getFPFeatures() const { - return FPOptions(BinaryOperatorBits.FPFeatures); + return FPFeatures; } // Get the FP contractability status of this operator. Only meaningful for @@ -3629,7 +3669,7 @@ protected: BinaryOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResTy, ExprValueKind VK, ExprObjectKind OK, - SourceLocation opLoc, FPOptions FPFeatures, bool dead2) + SourceLocation opLoc, FPOptions FP_Features, bool dead2) : Expr(CompoundAssignOperatorClass, ResTy, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), @@ -3638,7 +3678,7 @@ (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())) { BinaryOperatorBits.Opc = opc; - BinaryOperatorBits.FPFeatures = FPFeatures.getInt(); + FPFeatures = FP_Features; BinaryOperatorBits.OpLoc = opLoc; SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -144,20 +144,21 @@ return T->getStmtClass() == CXXOperatorCallExprClass; } - // Set the FP contractability status of this operator. Only meaningful for + // Set the FPFeatures status of this operator. Only meaningful for // operations on floating point types. void setFPFeatures(FPOptions F) { - CXXOperatorCallExprBits.FPFeatures = F.getInt(); + FPFeatures = F; } FPOptions getFPFeatures() const { - return FPOptions(CXXOperatorCallExprBits.FPFeatures); + return FPFeatures; } // Get the FP contractability status of this operator. Only meaningful for // operations on floating point types. bool isFPContractableWithinStatement() const { - return getFPFeatures().allowFPContractWithinStatement(); + return FPFeatures.allowFPContractWithinStatement(); } + }; /// Represents a call to a member function that diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -529,10 +529,6 @@ unsigned Opc : 6; - /// This is only meaningful for operations on floating point - /// types and 0 otherwise. - unsigned FPFeatures : 8; - SourceLocation OpLoc; }; @@ -600,9 +596,6 @@ /// The kind of this overloaded operator. One of the enumerator /// value of OverloadedOperatorKind. unsigned OperatorKind : 6; - - // Only meaningful for floating point types. - unsigned FPFeatures : 8; }; class CXXRewrittenBinaryOperatorBitfields { 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 @@ -1109,9 +1109,9 @@ "'#pragma init_seg' is only supported when targeting a " "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 " +// - #pragma restricted to file scope or start of compound statement +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,10 @@ 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; use 'float_control({push|pop})' or " + "'float_control({precise|except}, {on|off} [,push])'">; // - #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" @@ -1335,9 +1339,6 @@ 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_invalid_keyword : Error< "invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -854,6 +854,16 @@ "specifying both a name and alignment to 'pop' is undefined">; def warn_pragma_pop_failed : Warning<"#pragma %0(pop, ...) failed: %1">, InGroup; +def err_pragma_fc_pp_scope : Error< + "'#pragma float_control push/pop' can only appear at file scope">; +def err_pragma_fc_noprecise_requires_nofenv : Error< + "'#pragma float_control(precise, off)' is illegal when fenv_access is enabled">; +def err_pragma_fc_except_requires_precise : Error< + "'#pragma float_control(except, on)' is illegal when precise is disabled">; +def err_pragma_fc_noprecise_requires_noexcept : Error< + "'#pragma float_control(precise, off)' is illegal when except is enabled">; +def err_pragma_fenv_requires_precise : Error< + "'#pragma STDC FENV_ACCESS ON' is illegal when precise is disabled">; def warn_cxx_ms_struct : Warning<"ms_struct may not produce Microsoft-compatible layouts for classes " "with base classes or virtual functions">, 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 @@ -351,6 +351,8 @@ /// Return the OpenCL C or C++ version as a VersionTuple. VersionTuple getOpenCLVersionTuple() const; + + bool denormalIsIEEE = false; }; /// Floating point control options @@ -359,25 +361,48 @@ FPOptions() : fp_contract(LangOptions::FPC_Off), fenv_access(LangOptions::FEA_Off), rounding(LangOptions::FPR_ToNearest), - exceptions(LangOptions::FPE_Ignore) - {} + exceptions(LangOptions::FPE_Ignore), + allow_reassoc(0), + no_nans(0), + no_infs(0), + no_signed_zeros(0), + allow_reciprocal(0), + approx_func(0) + {} // Used for serializing. explicit FPOptions(unsigned I) : 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)), + allow_reassoc((I>>8) & 1), + no_nans((I>>9) & 1), + no_infs((I>>10) & 1), + no_signed_zeros((I>>11) & 1), + allow_reciprocal((I>>12) & 1), + approx_func((I>>13) & 1) {} explicit FPOptions(const LangOptions &LangOpts) : fp_contract(LangOpts.getDefaultFPContractMode()), fenv_access(LangOptions::FEA_Off), - rounding(LangOptions::FPR_ToNearest), - exceptions(LangOptions::FPE_Ignore) + rounding(LangOpts.getFPRoundingMode()), + exceptions(LangOpts.getFPExceptionMode()), + allow_reassoc(LangOpts.FastMath || LangOpts.AllowFPReassoc), + no_nans(LangOpts.FastMath || LangOpts.NoHonorNaNs), + no_infs(LangOpts.FastMath || LangOpts.NoHonorInfs), + no_signed_zeros(LangOpts.FastMath || LangOpts.NoSignedZero), + allow_reciprocal(LangOpts.FastMath || LangOpts.AllowRecip), + approx_func(LangOpts.FastMath || LangOpts.ApproxFunc) {} // FIXME: Use getDefaultFEnvAccessMode() when available. + void setFastMath(bool B = true) { + allow_reassoc = no_nans = no_infs = no_signed_zeros = approx_func = + allow_reciprocal = B; + } + bool allowFPContractWithinStatement() const { return fp_contract == LangOptions::FPC_On; } @@ -404,6 +429,18 @@ fenv_access = LangOptions::FEA_On; } + void setFPPreciseEnabled(bool Value) { + if (Value) { + /* Precise mode implies fp_contract=on and disables ffast-math */ + setFastMath(false); + setAllowFPContractWithinStatement(); + } else { + /* Precise mode implies fp_contract=fast and enables ffast-math */ + setFastMath(true); + setAllowFPContractAcrossStatement(); + } + } + void setDisallowFEnvAccess() { fenv_access = LangOptions::FEA_Off; } LangOptions::FPRoundingModeKind getRoundingMode() const { @@ -422,6 +459,34 @@ exceptions = EM; } + /// FMF Flag queries + bool allowAssociativeMath() const { return allow_reassoc; } + bool noHonorNaNs() const { return no_nans; } + bool noHonorInfs() const { return no_infs; } + bool noSignedZeros() const { return no_signed_zeros; } + bool allowReciprocalMath() const { return allow_reciprocal; } + bool allowApproximateFunctions() const { return approx_func; } + + /// Flag setters + void setAllowAssociativeMath(bool B = true) { + allow_reassoc = B; + } + void setNoHonorNaNs(bool B = true) { + no_nans = B; + } + void setNoHonorInfs(bool B = true) { + no_infs = B; + } + void setNoSignedZeros(bool B = true) { + no_signed_zeros = B; + } + void setAllowReciprocalMath(bool B = true) { + allow_reciprocal = B; + } + void setAllowApproximateFunctions(bool B = true) { + approx_func = B; + } + bool isFPConstrained() const { return getRoundingMode() != LangOptions::FPR_ToNearest || getExceptionMode() != LangOptions::FPE_Ignore || @@ -429,9 +494,26 @@ } /// Used to serialize this. - unsigned getInt() const { + unsigned getAsOpaqueInt() const { return fp_contract | (fenv_access << 2) | (rounding << 3) - | (exceptions << 6); + | (exceptions << 6) + | (allow_reassoc << 8) | (no_nans << 9) + | (no_infs << 10) | (no_signed_zeros << 11) + | (allow_reciprocal << 12) | (approx_func << 13); + } + + /// Used with getAsOpaqueInt() to manage the float_control pragma stack. + void getFromOpaqueInt(unsigned I) { + 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)); + allow_reassoc = ((I>>8) & 1); + no_nans = ((I>>9) & 1); + no_infs = ((I>>10) & 1); + no_signed_zeros = ((I>>11) & 1); + allow_reciprocal = ((I>>12) & 1); + approx_func = ((I>>13) & 1); } private: @@ -442,6 +524,25 @@ unsigned fenv_access : 1; unsigned rounding : 3; unsigned exceptions : 2; + /// Allow reassociation transformations for floating-point instructions. + unsigned allow_reassoc : 1; + /// No NaNs - Allow optimizations to assume the arguments and result + /// are not NaN. If an argument is a nan, or the result would be a nan, + /// it produces a :ref:`poison value ` instead. + unsigned no_nans : 1; + /// No Infs - Allow optimizations to assume the arguments and result + /// are not +/-Inf. If an argument is +/-Inf, or the result would be +/-Inf, + /// it produces a :ref:`poison value ` instead. + unsigned no_infs : 1; + /// No Signed Zeros - Allow optimizations to treat the sign of a zero + /// argument or result as insignificant. + unsigned no_signed_zeros : 1; + /// Allow Reciprocal - Allow optimizations to use the reciprocal + /// of an argument rather than perform division. + unsigned allow_reciprocal : 1; + /// Approximate functions - Allow substitution of approximate calculations + /// for functions (sin, log, sqrt, etc). + unsigned approx_func : 1; }; /// Describes the kind of translation unit being processed. diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -188,6 +188,12 @@ COMPATIBLE_LANGOPT(FastMath , 1, 0, "fast FP math optimizations, and __FAST_MATH__ predefined macro") COMPATIBLE_LANGOPT(FiniteMathOnly , 1, 0, "__FINITE_MATH_ONLY__ predefined macro") COMPATIBLE_LANGOPT(UnsafeFPMath , 1, 0, "Unsafe Floating Point Math") +COMPATIBLE_LANGOPT(AllowFPReassoc , 1, 0, "Permit Floating Point reassociation") +COMPATIBLE_LANGOPT(NoHonorNaNs , 1, 0, "Permit Floating Point optimization without regard to NaN") +COMPATIBLE_LANGOPT(NoHonorInfs , 1, 0, "Permit Floating Point optimization without regard to infinities") +COMPATIBLE_LANGOPT(NoSignedZero , 1, 0, "Permit Floating Point optimization without regard to signed zeros") +COMPATIBLE_LANGOPT(AllowRecip , 1, 0, "Permit Floating Point reciprocal") +COMPATIBLE_LANGOPT(ApproxFunc , 1, 0, "Permit Floating Point approximation") BENIGN_LANGOPT(ObjCGCBitmapPrint , 1, 0, "printing of GC's bitmap layout for __weak/__strong ivars") 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,9 @@ PragmaStack ConstSegStack; PragmaStack CodeSegStack; + // This stacks the current state of Sema.FPFeatures + 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 { @@ -9397,6 +9400,18 @@ void ActOnPragmaDetectMismatch(SourceLocation Loc, StringRef Name, StringRef Value); + /// Are precise floating point semantics currently enabled? + bool isPreciseFPEnabled() { + return LangOpts.denormalIsIEEE && !FPFeatures.allowAssociativeMath() && + !FPFeatures.noSignedZeros() && !FPFeatures.allowReciprocalMath() && + !FPFeatures.allowApproximateFunctions(); + } + + /// 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, @@ -9437,7 +9452,8 @@ /// ActOnPragmaFenvAccess - Called on well formed /// \#pragma STDC FENV_ACCESS - void ActOnPragmaFEnvAccess(LangOptions::FEnvAccessModeKind FPC); + void ActOnPragmaFEnvAccess(SourceLocation Loc, + LangOptions::FEnvAccessModeKind FPC); /// Called to set rounding mode for floating point operations. void setRoundingMode(LangOptions::FPRoundingModeKind); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -650,7 +650,10 @@ PP_CONDITIONAL_STACK = 62, /// A table of skipped ranges within the preprocessing record. - PPD_SKIPPED_RANGES = 63 + PPD_SKIPPED_RANGES = 63, + + /// Record code for \#pragma float_control options. + FLOAT_CONTROL_PRAGMA_OPTIONS = 64 }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -856,6 +856,18 @@ int PragmaMSPointersToMembersState = -1; SourceLocation PointersToMembersPragmaLocation; + /// The pragma float_control state. + Optional FpPragmaCurrentValue; + SourceLocation FpPragmaCurrentLocation; + struct FpPragmaStackEntry { + unsigned Value; + SourceLocation Location; + SourceLocation PushLocation; + StringRef SlotLabel; + }; + llvm::SmallVector FpPragmaStack; + llvm::SmallVector FpPragmaStrings; + /// The pragma pack state. Optional PragmaPackCurrentValue; SourceLocation PragmaPackCurrentLocation; diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -502,6 +502,7 @@ void WriteMSStructPragmaOptions(Sema &SemaRef); void WriteMSPointersToMembersPragmaOptions(Sema &SemaRef); void WritePackPragmaOptions(Sema &SemaRef); + void WriteFloatControlPragmaOptions(Sema &SemaRef); void WriteModuleFileExtension(Sema &SemaRef, ModuleFileExtensionWriter &Writer); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6646,7 +6646,7 @@ return new (Importer.getToContext()) UnaryOperator( ToSubExpr, E->getOpcode(), ToType, E->getValueKind(), E->getObjectKind(), - ToOperatorLoc, E->canOverflow()); + ToOperatorLoc, E->canOverflow(), E->getFPFeatures()); } ExpectedStmt @@ -7554,7 +7554,8 @@ } return CallExpr::Create(Importer.getToContext(), ToCallee, ToArgs, ToType, - E->getValueKind(), ToRParenLoc, /*MinNumArgs=*/0, + E->getValueKind(), ToRParenLoc, + E->getFPFeatures(), /*MinNumArgs=*/0, E->getADLCallKind()); } diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1306,7 +1306,8 @@ CallExpr::CallExpr(StmtClass SC, Expr *Fn, ArrayRef PreArgs, ArrayRef Args, QualType Ty, ExprValueKind VK, - SourceLocation RParenLoc, unsigned MinNumArgs, + SourceLocation RParenLoc, FPOptions FP_Features, + unsigned MinNumArgs, ADLCallKind UsesADL) : Expr(SC, Ty, VK, OK_Ordinary, Fn->isTypeDependent(), Fn->isValueDependent(), Fn->isInstantiationDependent(), @@ -1314,6 +1315,7 @@ RParenLoc(RParenLoc) { NumArgs = std::max(Args.size(), MinNumArgs); unsigned NumPreArgs = PreArgs.size(); + FPFeatures = FP_Features; CallExprBits.NumPreArgs = NumPreArgs; assert((NumPreArgs == getNumPreArgs()) && "NumPreArgs overflow!"); @@ -1352,15 +1354,15 @@ CallExpr *CallExpr::Create(const ASTContext &Ctx, Expr *Fn, ArrayRef Args, QualType Ty, ExprValueKind VK, - SourceLocation RParenLoc, unsigned MinNumArgs, - ADLCallKind UsesADL) { + SourceLocation RParenLoc, FPOptions FP_Features, + unsigned MinNumArgs, ADLCallKind UsesADL) { unsigned NumArgs = std::max(Args.size(), MinNumArgs); unsigned SizeOfTrailingObjects = CallExpr::sizeOfTrailingObjects(/*NumPreArgs=*/0, NumArgs); void *Mem = Ctx.Allocate(sizeof(CallExpr) + SizeOfTrailingObjects, alignof(CallExpr)); return new (Mem) CallExpr(CallExprClass, Fn, /*PreArgs=*/{}, Args, Ty, VK, - RParenLoc, MinNumArgs, UsesADL); + RParenLoc, FP_Features, MinNumArgs, UsesADL); } CallExpr *CallExpr::CreateTemporary(void *Mem, Expr *Fn, QualType Ty, @@ -1369,7 +1371,7 @@ assert(!(reinterpret_cast(Mem) % alignof(CallExpr)) && "Misaligned memory in CallExpr::CreateTemporary!"); return new (Mem) CallExpr(CallExprClass, Fn, /*PreArgs=*/{}, /*Args=*/{}, Ty, - VK, RParenLoc, /*MinNumArgs=*/0, UsesADL); + VK, RParenLoc, FPOptions(), /*MinNumArgs=*/0, UsesADL); } CallExpr *CallExpr::CreateEmpty(const ASTContext &Ctx, unsigned NumArgs, diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -588,14 +588,11 @@ FPOptions FPFeatures, ADLCallKind UsesADL) : CallExpr(CXXOperatorCallExprClass, Fn, /*PreArgs=*/{}, Args, Ty, VK, - OperatorLoc, /*MinNumArgs=*/0, UsesADL) { + OperatorLoc, FPFeatures, /*MinNumArgs=*/0, UsesADL) { CXXOperatorCallExprBits.OperatorKind = OpKind; - CXXOperatorCallExprBits.FPFeatures = FPFeatures.getInt(); assert( (CXXOperatorCallExprBits.OperatorKind == static_cast(OpKind)) && "OperatorKind overflow!"); - assert((CXXOperatorCallExprBits.FPFeatures == FPFeatures.getInt()) && - "FPFeatures overflow!"); Range = getSourceRangeImpl(); } @@ -655,7 +652,7 @@ QualType Ty, ExprValueKind VK, SourceLocation RP, unsigned MinNumArgs) : CallExpr(CXXMemberCallExprClass, Fn, /*PreArgs=*/{}, Args, Ty, VK, RP, - MinNumArgs, NotADL) {} + FPOptions(), MinNumArgs, NotADL) {} CXXMemberCallExpr::CXXMemberCallExpr(unsigned NumArgs, EmptyShell Empty) : CallExpr(CXXMemberCallExprClass, /*NumPreArgs=*/0, NumArgs, Empty) {} @@ -894,7 +891,7 @@ SourceLocation LitEndLoc, SourceLocation SuffixLoc) : CallExpr(UserDefinedLiteralClass, Fn, /*PreArgs=*/{}, Args, Ty, VK, - LitEndLoc, /*MinNumArgs=*/0, NotADL), + LitEndLoc, FPFeatures, /*MinNumArgs=*/0, NotADL), UDSuffixLoc(SuffixLoc) {} UserDefinedLiteral::UserDefinedLiteral(unsigned NumArgs, EmptyShell Empty) @@ -1692,7 +1689,7 @@ ExprValueKind VK, SourceLocation RP, unsigned MinNumArgs) : CallExpr(CUDAKernelCallExprClass, Fn, /*PreArgs=*/Config, Args, Ty, VK, - RP, MinNumArgs, NotADL) {} + RP, FPOptions(), MinNumArgs, NotADL) {} CUDAKernelCallExpr::CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty) : CallExpr(CUDAKernelCallExprClass, /*NumPreArgs=*/END_PREARG, NumArgs, diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp --- a/clang/lib/Analysis/BodyFarm.cpp +++ b/clang/lib/Analysis/BodyFarm.cpp @@ -149,7 +149,7 @@ UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) { return new (C) UnaryOperator(const_cast(Arg), UO_Deref, Ty, VK_LValue, OK_Ordinary, SourceLocation(), - /*CanOverflow*/ false); + /*CanOverflow*/ false, FPOptions()); } ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) { @@ -269,7 +269,7 @@ } return CallExpr::Create(C, SubExpr, CallArgs, C.VoidTy, VK_RValue, - SourceLocation()); + SourceLocation(), FPOptions()); } static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M, @@ -455,7 +455,7 @@ /* QualType=*/ C.IntTy, /* ExprValueKind=*/ VK_RValue, /* ExprObjectKind=*/ OK_Ordinary, SourceLocation(), - /* CanOverflow*/ false); + /* CanOverflow*/ false, FPOptions()); // Create assignment. BinaryOperator *FlagAssignment = M.makeAssignment( @@ -514,13 +514,13 @@ /*Args=*/None, /*QualType=*/C.VoidTy, /*ExprValueType=*/VK_RValue, - /*SourceLocation=*/SourceLocation()); + /*SourceLocation=*/SourceLocation(), FPOptions()); // (2) Create the assignment to the predicate. Expr *DoneValue = new (C) UnaryOperator(M.makeIntegerLiteral(0, C.LongTy), UO_Not, C.LongTy, VK_RValue, OK_Ordinary, SourceLocation(), - /*CanOverflow*/false); + /*CanOverflow*/false, FPOptions()); BinaryOperator *B = M.makeAssignment( @@ -579,7 +579,8 @@ DeclRefExpr *DR = M.makeDeclRefExpr(PV); ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty); CallExpr *CE = - CallExpr::Create(C, ICE, None, C.VoidTy, VK_RValue, SourceLocation()); + CallExpr::Create(C, ICE, None, C.VoidTy, VK_RValue, SourceLocation(), + FPOptions()); return CE; } 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 @@ -216,7 +216,14 @@ /// Update the FastMathFlags of LLVM IR from the FPOptions in LangOptions. static void updateFastMathFlags(llvm::FastMathFlags &FMF, FPOptions FPFeatures) { - FMF.setAllowContract(FPFeatures.allowFPContractAcrossStatement()); + FMF.setAllowReassoc(FPFeatures.allowAssociativeMath()); + FMF.setNoNaNs(FPFeatures.noHonorNaNs()); + FMF.setNoInfs(FPFeatures.noHonorInfs()); + FMF.setNoSignedZeros(FPFeatures.noSignedZeros()); + FMF.setAllowReciprocal(FPFeatures.allowReciprocalMath()); + FMF.setApproxFunc(FPFeatures.allowApproximateFunctions()); + FMF.setAllowContract(FPFeatures.allowFPContractAcrossStatement() || + FPFeatures.allowFPContractWithinStatement()); } /// Propagate fast-math flags from \p Op to the instruction in \p V. @@ -229,6 +236,27 @@ return V; } +static void setBuilderFlagsFromFPFeatures(CGBuilderTy &Builder, + CodeGenFunction &CGF, + FPOptions FPFeatures) { + auto NewRoundingBehavior = ToConstrainedRoundingMD( + FPFeatures.getRoundingMode()); + Builder.setDefaultConstrainedRounding(NewRoundingBehavior); + auto NewExceptionBehavior = ToConstrainedExceptMD( + FPFeatures.getExceptionMode()); + Builder.setDefaultConstrainedExcept(NewExceptionBehavior); + auto FMF = Builder.getFastMathFlags(); + updateFastMathFlags(FMF, FPFeatures); + Builder.setFastMathFlags(FMF); + assert((CGF.CurFuncDecl==nullptr || + Builder.getIsFPConstrained() || + isa(CGF.CurFuncDecl) || + isa(CGF.CurFuncDecl) || + (NewExceptionBehavior == llvm::fp::ebIgnore && + NewRoundingBehavior == llvm::fp::rmToNearest)) && + "FPConstrained should be enabled on entire function"); +} + class ScalarExprEmitter : public StmtVisitor { CodeGenFunction &CGF; @@ -580,6 +608,9 @@ if (E->getCallReturnType(CGF.getContext())->isReferenceType()) return EmitLoadOfLValue(E); + // Preserve the old values + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, E->getFPFeatures()); Value *V = CGF.EmitCallExpr(E).getScalarVal(); EmitLValueAlignmentAssumption(E, V); @@ -738,6 +769,9 @@ return EmitOverflowCheckedBinOp(Ops); if (Ops.LHS->getType()->isFPOrFPVectorTy()) { + // Preserve the old values + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, Ops.FPFeatures); Value *V = Builder.CreateFMul(Ops.LHS, Ops.RHS, "mul"); return propagateFMFlags(V, Ops); } @@ -2335,7 +2369,7 @@ BinOp.RHS = llvm::ConstantInt::get(InVal->getType(), 1, false); BinOp.Ty = E->getType(); BinOp.Opcode = IsInc ? BO_Add : BO_Sub; - // FIXME: once UnaryOperator carries FPFeatures, copy it here. + BinOp.FPFeatures = E->getFPFeatures(); BinOp.E = E; return BinOp; } @@ -2672,7 +2706,7 @@ BinOp.LHS = llvm::Constant::getNullValue(BinOp.RHS->getType()); BinOp.Ty = E->getType(); BinOp.Opcode = BO_Sub; - // FIXME: once UnaryOperator carries FPFeatures, copy it here. + BinOp.FPFeatures = E->getFPFeatures(); BinOp.E = E; return EmitSub(BinOp); } @@ -2689,9 +2723,11 @@ Value *Oper = Visit(E->getSubExpr()); Value *Zero = llvm::Constant::getNullValue(Oper->getType()); Value *Result; - if (Oper->getType()->isFPOrFPVectorTy()) + if (Oper->getType()->isFPOrFPVectorTy()) { + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, E->getFPFeatures()); Result = Builder.CreateFCmp(llvm::CmpInst::FCMP_OEQ, Oper, Zero, "cmp"); - else + } else Result = Builder.CreateICmp(llvm::CmpInst::ICMP_EQ, Oper, Zero, "cmp"); return Builder.CreateSExt(Result, ConvertType(E->getType()), "sext"); } @@ -3100,7 +3136,10 @@ } if (Ops.LHS->getType()->isFPOrFPVectorTy()) { - llvm::Value *Val = Builder.CreateFDiv(Ops.LHS, Ops.RHS, "div"); + llvm::Value *Val; + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, Ops.FPFeatures); + Val = Builder.CreateFDiv(Ops.LHS, Ops.RHS, "div"); if (CGF.getLangOpts().OpenCL && !CGF.CGM.getCodeGenOpts().CorrectlyRoundedDivSqrt) { // OpenCL v1.1 s7.4: minimum accuracy of single precision / is 2.5ulp @@ -3470,6 +3509,8 @@ return EmitOverflowCheckedBinOp(op); if (op.LHS->getType()->isFPOrFPVectorTy()) { + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, op.FPFeatures); // Try to form an fmuladd. if (Value *FMulAdd = tryEmitFMulAdd(op, CGF, Builder)) return FMulAdd; @@ -3615,6 +3656,8 @@ return EmitOverflowCheckedBinOp(op); if (op.LHS->getType()->isFPOrFPVectorTy()) { + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, op.FPFeatures); // Try to form an fmuladd. if (Value *FMulAdd = tryEmitFMulAdd(op, CGF, Builder, true)) return FMulAdd; @@ -3928,6 +3971,8 @@ if (BOInfo.isFixedPointBinOp()) { Result = EmitFixedPointBinOp(BOInfo); } else if (LHS->getType()->isFPOrFPVectorTy()) { + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, BOInfo.FPFeatures); if (!IsSignaling) Result = Builder.CreateFCmp(FCmpOpc, LHS, RHS, "cmp"); else @@ -4080,6 +4125,8 @@ Value *RHS = Visit(E->getRHS()); Value *Zero = llvm::ConstantAggregateZero::get(LHS->getType()); if (LHS->getType()->isFPOrFPVectorTy()) { + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, E->getFPFeatures()); LHS = Builder.CreateFCmp(llvm::CmpInst::FCMP_UNE, LHS, Zero, "cmp"); RHS = Builder.CreateFCmp(llvm::CmpInst::FCMP_UNE, RHS, Zero, "cmp"); } else { @@ -4164,6 +4211,8 @@ Value *RHS = Visit(E->getRHS()); Value *Zero = llvm::ConstantAggregateZero::get(LHS->getType()); if (LHS->getType()->isFPOrFPVectorTy()) { + llvm::IRBuilder<>::FastMathFlagGuard FMFG(Builder); + setBuilderFlagsFromFPFeatures(Builder, CGF, E->getFPFeatures()); LHS = Builder.CreateFCmp(llvm::CmpInst::FCMP_UNE, LHS, Zero, "cmp"); RHS = Builder.CreateFCmp(llvm::CmpInst::FCMP_UNE, RHS, Zero, "cmp"); } else { diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -3558,18 +3558,18 @@ DeclRefExpr DstExpr(getContext(), &DstDecl, false, DestTy, VK_RValue, SourceLocation()); UnaryOperator DST(&DstExpr, UO_Deref, DestTy->getPointeeType(), - VK_LValue, OK_Ordinary, SourceLocation(), false); + VK_LValue, OK_Ordinary, SourceLocation(), false, FPOptions()); DeclRefExpr SrcExpr(getContext(), &SrcDecl, false, SrcTy, VK_RValue, SourceLocation()); UnaryOperator SRC(&SrcExpr, UO_Deref, SrcTy->getPointeeType(), - VK_LValue, OK_Ordinary, SourceLocation(), false); + VK_LValue, OK_Ordinary, SourceLocation(), false, FPOptions()); Expr *Args[2] = { &DST, &SRC }; CallExpr *CalleeExp = cast(PID->getSetterCXXAssignment()); CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create( C, OO_Equal, CalleeExp->getCallee(), Args, DestTy->getPointeeType(), - VK_LValue, SourceLocation(), FPOptions()); + VK_LValue, SourceLocation(), CalleeExp->getFPFeatures()); EmitStmt(TheCall); @@ -3642,7 +3642,7 @@ SourceLocation()); UnaryOperator SRC(&SrcExpr, UO_Deref, SrcTy->getPointeeType(), - VK_LValue, OK_Ordinary, SourceLocation(), false); + VK_LValue, OK_Ordinary, SourceLocation(), false, FPOptions()); CXXConstructExpr *CXXConstExpr = cast(PID->getGetterCXXConstructor()); diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -2932,7 +2932,7 @@ OK_Ordinary, S.getBeginLoc(), FPOptions()); // Increment for loop counter. UnaryOperator Inc(&IVRefExpr, UO_PreInc, KmpInt32Ty, VK_RValue, OK_Ordinary, - S.getBeginLoc(), true); + S.getBeginLoc(), true, FPOptions()); auto &&BodyGen = [CapturedStmt, CS, &S, &IV](CodeGenFunction &CGF) { // Iterate through all sections and emit a switch construct: // switch (IV) { 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 @@ -4531,6 +4531,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 @@ -117,7 +117,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) { @@ -132,7 +132,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) { @@ -149,14 +149,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); } } @@ -933,9 +933,11 @@ Fn->addFnAttr(llvm::Attribute::NoRecurse); } - if (const FunctionDecl *FD = dyn_cast_or_null(D)) + if (const FunctionDecl *FD = dyn_cast_or_null(D)) { + Builder.setIsFPConstrained(FD->usesFPIntrin()); if (FD->usesFPIntrin()) Fn->addFnAttr(llvm::Attribute::StrictFP); + } // If a custom alignment is used, force realigning to this alignment on // any main function which certainly will need it. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2473,6 +2473,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, const TargetOptions &TargetOpts, PreprocessorOptions &PPOpts, + CodeGenOptions &CGOpts, DiagnosticsEngine &Diags) { // FIXME: Cleanup per-file based stuff. LangStandard::Kind LangStd = LangStandard::lang_unspecified; @@ -3185,6 +3186,19 @@ Opts.UnsafeFPMath = Args.hasArg(OPT_menable_unsafe_fp_math) || Args.hasArg(OPT_cl_unsafe_math_optimizations) || Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.AllowFPReassoc = Opts.FastMath || CGOpts.Reassociate; + Opts.NoHonorNaNs = Opts.FastMath || CGOpts.NoNaNsFPMath || + Opts.FiniteMathOnly; + Opts.NoHonorInfs = Opts.FastMath || CGOpts.NoInfsFPMath || + Opts.FiniteMathOnly; + Opts.NoSignedZero = Opts.FastMath || CGOpts.NoSignedZeros; + Opts.AllowRecip = Opts.FastMath || CGOpts.ReciprocalMath; + // Currently there's no clang option to enable this individually + Opts.ApproxFunc = Opts.FastMath; + Opts.denormalIsIEEE = + !(CGOpts.FPDenormalMode.isValid() && CGOpts.FP32DenormalMode.isValid()) || + (CGOpts.FPDenormalMode == llvm::DenormalMode::getIEEE() && + CGOpts.FP32DenormalMode == llvm::DenormalMode::getIEEE()); if (Arg *A = Args.getLastArg(OPT_ffp_contract)) { StringRef Val = A->getValue(); @@ -3605,7 +3619,7 @@ // Other LangOpts are only initialized when the input is not AST or LLVM IR. // FIXME: Should we really be calling this for an Language::Asm input? ParseLangArgs(LangOpts, Args, DashX, Res.getTargetOpts(), - Res.getPreprocessorOpts(), Diags); + Res.getPreprocessorOpts(), Res.getCodeGenOpts(), Diags); if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; if (T.isOSDarwin() && DashX.isPreprocessed()) { diff --git a/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp --- a/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp +++ b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -2109,7 +2109,8 @@ const auto *FT = msgSendType->castAs(); CallExpr *Exp = CallExpr::Create( - *Context, ICE, Args, FT->getCallResultType(*Context), VK_RValue, EndLoc); + *Context, ICE, Args, FT->getCallResultType(*Context), VK_RValue, EndLoc, + FPOptions()); return Exp; } @@ -2588,7 +2589,7 @@ DeclRefExpr(*Context, NewVD, false, strType, VK_LValue, SourceLocation()); Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, Context->getPointerType(DRE->getType()), - VK_RValue, OK_Ordinary, SourceLocation(), false); + VK_RValue, OK_Ordinary, SourceLocation(), false, FPOptions()); // cast to NSConstantString * CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), CK_CPointerToObjCPointerCast, Unop); @@ -2690,7 +2691,7 @@ const FunctionType *FT = msgSendType->getAs(); CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), - VK_RValue, EndLoc); + VK_RValue, EndLoc, FPOptions()); ReplaceStmt(Exp, CE); return CE; } @@ -2730,7 +2731,7 @@ InitExprs.push_back(Exp->getElement(i)); Expr *NSArrayCallExpr = CallExpr::Create(*Context, NSArrayDRE, InitExprs, NSArrayFType, VK_LValue, - SourceLocation()); + SourceLocation(), FPOptions()); FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), @@ -2811,7 +2812,7 @@ const FunctionType *FT = msgSendType->castAs(); CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), - VK_RValue, EndLoc); + VK_RValue, EndLoc, FPOptions()); ReplaceStmt(Exp, CE); return CE; } @@ -2859,7 +2860,7 @@ // (const id [])objects Expr *NSValueCallExpr = CallExpr::Create(*Context, NSDictDRE, ValueExprs, NSDictFType, VK_LValue, - SourceLocation()); + SourceLocation(), FPOptions()); FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), @@ -2878,7 +2879,8 @@ DictLiteralValueME); // (const id [])keys Expr *NSKeyCallExpr = CallExpr::Create( - *Context, NSDictDRE, KeyExprs, NSDictFType, VK_LValue, SourceLocation()); + *Context, NSDictDRE, KeyExprs, NSDictFType, VK_LValue, SourceLocation(), + FPOptions()); MemberExpr *DictLiteralKeyME = MemberExpr::CreateImplicit(*Context, NSKeyCallExpr, false, ARRFD, @@ -2962,7 +2964,7 @@ const FunctionType *FT = msgSendType->castAs(); CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), - VK_RValue, EndLoc); + VK_RValue, EndLoc, FPOptions()); ReplaceStmt(Exp, CE); return CE; } @@ -3174,7 +3176,7 @@ DeclRefExpr *DRE = new (Context) DeclRefExpr(*Context, FD, false, castType, VK_RValue, SourceLocation()); CallExpr *STCE = CallExpr::Create(*Context, DRE, MsgExprs, castType, - VK_LValue, SourceLocation()); + VK_LValue, SourceLocation(), FPOptions()); FieldDecl *FieldD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), @@ -3275,7 +3277,7 @@ DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, VK_LValue, SourceLocation()); SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, - VK_LValue, SourceLocation()); + VK_LValue, SourceLocation(), FPOptions()); // The code for super is a little tricky to prevent collision with // the structure definition in the header. The rewriter has it's own // internal definition (__rw_objc_super) that is uses. This is why @@ -3284,8 +3286,8 @@ // SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions()); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -3302,8 +3304,8 @@ // struct __rw_objc_super * SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions()); } MsgExprs.push_back(SuperRep); break; @@ -3370,7 +3372,7 @@ DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, VK_LValue, SourceLocation()); SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, - VK_LValue, SourceLocation()); + VK_LValue, SourceLocation(), FPOptions()); // The code for super is a little tricky to prevent collision with // the structure definition in the header. The rewriter has it's own // internal definition (__rw_objc_super) that is uses. This is why @@ -3380,7 +3382,7 @@ SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, - SourceLocation(), false); + SourceLocation(), false, FPOptions()); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -3535,7 +3537,7 @@ const FunctionType *FT = msgSendType->castAs(); CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), - VK_RValue, EndLoc); + VK_RValue, EndLoc, FPOptions()); Stmt *ReplacingStmt = CE; if (MsgSendStretFlavor) { // We have the method which returns a struct/union. Must also generate @@ -4646,7 +4648,7 @@ BlkExprs.push_back(*I); } CallExpr *CE = CallExpr::Create(*Context, PE, BlkExprs, Exp->getType(), - VK_RValue, SourceLocation()); + VK_RValue, SourceLocation(), FPOptions()); return CE; } @@ -4705,8 +4707,8 @@ if (!ImportedLocalExternalDecls.count(Var)) return DRE; Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), - VK_LValue, OK_Ordinary, - DRE->getLocation(), false); + VK_LValue, OK_Ordinary, + DRE->getLocation(), false, FPOptions()); // Need parens to enforce precedence. ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), Exp); @@ -5296,7 +5298,7 @@ new (Context) DeclRefExpr(*Context, NewVD, false, Context->VoidPtrTy, VK_LValue, SourceLocation()), UO_AddrOf, Context->getPointerType(Context->VoidPtrTy), VK_RValue, - OK_Ordinary, SourceLocation(), false); + OK_Ordinary, SourceLocation(), false, FPOptions()); InitExprs.push_back(DescRefExpr); // Add initializers for any closure decl refs. @@ -5315,7 +5317,7 @@ QT = Context->getPointerType(QT); Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, OK_Ordinary, SourceLocation(), - false); + false, FPOptions()); } } else if (isTopLevelBlockPointerType((*I)->getType())) { FD = SynthBlockInitFunctionDecl((*I)->getName()); @@ -5332,7 +5334,7 @@ QT = Context->getPointerType(QT); Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, OK_Ordinary, SourceLocation(), - false); + false, FPOptions()); } } @@ -5373,7 +5375,7 @@ Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, Context->getPointerType(Exp->getType()), VK_RValue, OK_Ordinary, SourceLocation(), - false); + false, FPOptions()); Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); InitExprs.push_back(Exp); } @@ -5388,7 +5390,7 @@ InitExprs.push_back(FlagExp); } NewRep = CallExpr::Create(*Context, DRE, InitExprs, FType, VK_LValue, - SourceLocation()); + SourceLocation(), FPOptions()); if (GlobalBlockExpr) { assert (!GlobalConstructionExp && @@ -5398,8 +5400,8 @@ } NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, - Context->getPointerType(NewRep->getType()), - VK_RValue, OK_Ordinary, SourceLocation(), false); + Context->getPointerType(NewRep->getType()), + VK_RValue, OK_Ordinary, SourceLocation(), false, FPOptions()); NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, NewRep); // Put Paren around the call. @@ -7541,8 +7543,8 @@ Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT, - VK_LValue, OK_Ordinary, - SourceLocation(), false); + VK_LValue, OK_Ordinary, + SourceLocation(), false, FPOptions()); PE = new (Context) ParenExpr(OldRange.getBegin(), OldRange.getEnd(), Exp); diff --git a/clang/lib/Frontend/Rewrite/RewriteObjC.cpp b/clang/lib/Frontend/Rewrite/RewriteObjC.cpp --- a/clang/lib/Frontend/Rewrite/RewriteObjC.cpp +++ b/clang/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -2027,7 +2027,8 @@ const auto *FT = msgSendType->castAs(); CallExpr *Exp = CallExpr::Create( - *Context, ICE, Args, FT->getCallResultType(*Context), VK_RValue, EndLoc); + *Context, ICE, Args, FT->getCallResultType(*Context), VK_RValue, EndLoc, + FPOptions()); return Exp; } @@ -2515,7 +2516,7 @@ DeclRefExpr(*Context, NewVD, false, strType, VK_LValue, SourceLocation()); Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, Context->getPointerType(DRE->getType()), - VK_RValue, OK_Ordinary, SourceLocation(), false); + VK_RValue, OK_Ordinary, SourceLocation(), false, FPOptions()); // cast to NSConstantString * CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), CK_CPointerToObjCPointerCast, Unop); @@ -2613,7 +2614,7 @@ const auto *FT = msgSendType->castAs(); CallExpr *STCE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), - VK_RValue, SourceLocation()); + VK_RValue, SourceLocation(), FPOptions()); return STCE; } @@ -2706,7 +2707,7 @@ DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, VK_LValue, SourceLocation()); SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, - VK_LValue, SourceLocation()); + VK_LValue, SourceLocation(), FPOptions()); // The code for super is a little tricky to prevent collision with // the structure definition in the header. The rewriter has it's own // internal definition (__rw_objc_super) that is uses. This is why @@ -2715,8 +2716,8 @@ // SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions()); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -2733,8 +2734,8 @@ // struct objc_super * SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions()); } MsgExprs.push_back(SuperRep); break; @@ -2801,7 +2802,7 @@ DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, VK_LValue, SourceLocation()); SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, - VK_LValue, SourceLocation()); + VK_LValue, SourceLocation(), FPOptions()); // The code for super is a little tricky to prevent collision with // the structure definition in the header. The rewriter has it's own // internal definition (__rw_objc_super) that is uses. This is why @@ -2811,7 +2812,7 @@ SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, - SourceLocation(), false); + SourceLocation(), false, FPOptions()); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -2966,7 +2967,7 @@ const auto *FT = msgSendType->castAs(); CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), - VK_RValue, EndLoc); + VK_RValue, EndLoc, FPOptions()); Stmt *ReplacingStmt = CE; if (MsgSendStretFlavor) { // We have the method which returns a struct/union. Must also generate @@ -3049,8 +3050,8 @@ DeclRefExpr *DRE = new (Context) DeclRefExpr( *Context, VD, false, getProtocolType(), VK_LValue, SourceLocation()); Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf, - Context->getPointerType(DRE->getType()), - VK_RValue, OK_Ordinary, SourceLocation(), false); + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, SourceLocation(), false, FPOptions()); CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), CK_BitCast, DerefExpr); @@ -3816,7 +3817,7 @@ BlkExprs.push_back(*I); } CallExpr *CE = CallExpr::Create(*Context, PE, BlkExprs, Exp->getType(), - VK_RValue, SourceLocation()); + VK_RValue, SourceLocation(), FPOptions()); return CE; } @@ -3876,8 +3877,8 @@ if (!ImportedLocalExternalDecls.count(Var)) return DRE; Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), - VK_LValue, OK_Ordinary, - DRE->getLocation(), false); + VK_LValue, OK_Ordinary, + DRE->getLocation(), false, FPOptions()); // Need parens to enforce precedence. ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), Exp); @@ -4436,7 +4437,7 @@ new (Context) DeclRefExpr(*Context, NewVD, false, Context->VoidPtrTy, VK_LValue, SourceLocation()), UO_AddrOf, Context->getPointerType(Context->VoidPtrTy), VK_RValue, - OK_Ordinary, SourceLocation(), false); + OK_Ordinary, SourceLocation(), false, FPOptions()); InitExprs.push_back(DescRefExpr); // Add initializers for any closure decl refs. @@ -4455,7 +4456,7 @@ QT = Context->getPointerType(QT); Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, OK_Ordinary, SourceLocation(), - false); + false, FPOptions()); } } else if (isTopLevelBlockPointerType((*I)->getType())) { FD = SynthBlockInitFunctionDecl((*I)->getName()); @@ -4472,7 +4473,7 @@ QT = Context->getPointerType(QT); Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, OK_Ordinary, SourceLocation(), - false); + false, FPOptions()); } } InitExprs.push_back(Exp); @@ -4511,7 +4512,7 @@ if (!isNestedCapturedVar) Exp = new (Context) UnaryOperator( Exp, UO_AddrOf, Context->getPointerType(Exp->getType()), VK_RValue, - OK_Ordinary, SourceLocation(), false); + OK_Ordinary, SourceLocation(), false, FPOptions()); Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); InitExprs.push_back(Exp); } @@ -4522,14 +4523,14 @@ unsigned IntSize = static_cast(Context->getTypeSize(Context->IntTy)); Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), - Context->IntTy, SourceLocation()); + Context->IntTy, SourceLocation()); InitExprs.push_back(FlagExp); } NewRep = CallExpr::Create(*Context, DRE, InitExprs, FType, VK_LValue, - SourceLocation()); + SourceLocation(), FPOptions()); NewRep = new (Context) UnaryOperator( NewRep, UO_AddrOf, Context->getPointerType(NewRep->getType()), VK_RValue, - OK_Ordinary, SourceLocation(), false); + OK_Ordinary, SourceLocation(), false, FPOptions()); NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, NewRep); BlockDeclRefs.clear(); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3361,6 +3361,14 @@ // are complete and we can parse the delayed portions of method // declarations and the lexed inline method definitions, along with any // delayed attributes. + + // Save the state of Sema.FPFeatures, and change the setting + // to the levels specified on the command line. Previous level + // will be restored when the RAII object is destroyed. + Sema::FPFeaturesStateRAII SaveFPFeaturesState(Actions); + FPOptions fpOptions(getLangOpts()); + Actions.FPFeatures.getFromOpaqueInt(fpOptions.getAsOpaqueInt()); + SourceLocation SavedPrevTokLocation = PrevTokLocation; ParseLexedPragmas(getCurrentClass()); ParseLexedAttributes(getCurrentClass()); 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 = @@ -665,8 +692,8 @@ break; } - Actions.ActOnPragmaFEnvAccess(FPC); - ConsumeAnnotationToken(); + SourceLocation PragmaLoc = ConsumeAnnotationToken(); + Actions.ActOnPragmaFEnvAccess(PragmaLoc, FPC); } @@ -2489,6 +2516,129 @@ 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_malformed); + return; + } else if (Kind == PFC_Push || + Kind == PFC_Pop) { + 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") { + 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") { + 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(); + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "float_control"; + return; + } + + // Note: there is no accomodation for PP callback for this pragma. + + // Enter the annotation. + auto TokenArray = std::make_unique(1); + TokenArray[0].startToken(); + TokenArray[0].setKind(tok::annot_pragma_float_control); + TokenArray[0].setLocation(FloatControlLoc); + TokenArray[0].setAnnotationEndLoc(EndLoc); + TokenArray[0].setAnnotationValue(reinterpret_cast( + static_cast((Action << 16) | (Kind & 0xFFFF)))); + PP.EnterTokenStream(std::move(TokenArray), 1, + /*DisableMacroExpansion=*/false, /*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,8 +158,9 @@ LangOpts.getMSPointerToMemberRepresentationMethod()), VtorDispStack(LangOpts.getVtorDispMode()), PackStack(0), DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr), - CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr), - PragmaAttributeCurrentTargetDecl(nullptr), + CodeSegStack(nullptr), FpPragmaStack(FPFeatures.getAsOpaqueInt()), + CurInitSeg(nullptr), + VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr), IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp), StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr), 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,64 @@ Consumer.HandleTopLevelDecl(DeclGroupRef(PDMD)); } +void Sema::ActOnPragmaFloatControl(SourceLocation Loc, + PragmaMsStackAction Action, + PragmaFloatControlKind Value) { + auto NewValue = FpPragmaStack.CurrentValue; + if ((Action == PSK_Push_Set || Action == PSK_Push || Action == PSK_Pop) && + !CurContext->isTranslationUnit()) { + // Push and pop can only occur at file scope. + Diag(Loc, diag::err_pragma_fc_pp_scope); + return; + } + switch(Value) { + default: + llvm_unreachable("invalid pragma float_control kind"); + case PFC_Precise: + case PFC_NoPrecise: + case PFC_Except: + case PFC_NoExcept: + switch(Value) { + default: + llvm_unreachable("invalid pragma float_control kind"); + case PFC_Precise: + FPFeatures.setFPPreciseEnabled(true); + break; + case PFC_NoPrecise: + if (FPFeatures.getExceptionMode() == LangOptions::FPE_Strict) + Diag(Loc, diag::err_pragma_fc_noprecise_requires_noexcept); + else if (FPFeatures.allowFEnvAccess()) + Diag(Loc, diag::err_pragma_fc_noprecise_requires_nofenv); + else + FPFeatures.setFPPreciseEnabled(false); + break; + case PFC_Except: + if (!isPreciseFPEnabled()) + Diag(Loc, diag::err_pragma_fc_except_requires_precise); + else + FPFeatures.setExceptionMode(LangOptions::FPE_Strict); + break; + case PFC_NoExcept: + FPFeatures.setExceptionMode(LangOptions::FPE_Ignore); + break; + } + NewValue = FPFeatures.getAsOpaqueInt(); + FpPragmaStack.Act(Loc, Action, StringRef(), NewValue); + break; + case PFC_Push: + case PFC_Pop: + if (Value == PFC_Pop && FpPragmaStack.Stack.empty()) + Diag(Loc, diag::warn_pragma_pop_failed) << + "float_control" << "stack empty"; + FpPragmaStack.Act(Loc, Action, StringRef(), NewValue); + if (Value == PFC_Pop) { + NewValue = FpPragmaStack.CurrentValue; + FPFeatures.getFromOpaqueInt(NewValue); + } + break; + } +} + void Sema::ActOnPragmaMSPointersToMembers( LangOptions::PragmaMSPointersToMembersKind RepresentationMethod, SourceLocation PragmaLoc) { @@ -948,9 +1006,16 @@ FPFeatures.setExceptionMode(FPE); } -void Sema::ActOnPragmaFEnvAccess(LangOptions::FEnvAccessModeKind FPC) { +void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, + LangOptions::FEnvAccessModeKind FPC) { switch (FPC) { case LangOptions::FEA_On: + // Verify Microsoft restriction: + // You can't enable fenv_access unless precise semantics are enabled. + // Precise semantics can be enabled either by the float_control + // pragma, or by using the /fp:precise or /fp:strict compiler options + if (!isPreciseFPEnabled()) + Diag(Loc, diag::err_pragma_fenv_requires_precise); FPFeatures.setAllowFEnvAccess(); break; case LangOptions::FEA_Off: diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -13224,11 +13224,11 @@ Expr *From = FromB.build(S, Loc); From = new (S.Context) UnaryOperator(From, UO_AddrOf, S.Context.getPointerType(From->getType()), - VK_RValue, OK_Ordinary, Loc, false); + VK_RValue, OK_Ordinary, Loc, false, S.FPFeatures); Expr *To = ToB.build(S, Loc); To = new (S.Context) UnaryOperator(To, UO_AddrOf, S.Context.getPointerType(To->getType()), - VK_RValue, OK_Ordinary, Loc, false); + VK_RValue, OK_Ordinary, Loc, false, S.FPFeatures); const Type *E = T->getBaseElementTypeUnsafe(); bool NeedsCollectableMemCpy = @@ -13473,7 +13473,7 @@ // bound. Expr *Increment = new (S.Context) UnaryOperator(IterationVarRef.build(S, Loc), UO_PreInc, SizeType, - VK_LValue, OK_Ordinary, Loc, Upper.isMaxValue()); + VK_LValue, OK_Ordinary, Loc, Upper.isMaxValue(), S.FPFeatures); // Construct the loop that copies all elements of this array. return S.ActOnForStmt( 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 @@ -5753,7 +5753,7 @@ } return CallExpr::Create(Context, Fn, /*Args=*/{}, Context.VoidTy, - VK_RValue, RParenLoc); + VK_RValue, RParenLoc, FPFeatures); } if (Fn->getType() == Context.PseudoObjectTy) { ExprResult result = CheckPlaceholderExpr(Fn); @@ -5775,7 +5775,7 @@ Fn->getBeginLoc()); return CallExpr::Create(Context, Fn, ArgExprs, Context.DependentTy, - VK_RValue, RParenLoc); + VK_RValue, RParenLoc, FPFeatures); } } @@ -5804,7 +5804,7 @@ if (!find.HasFormOfMemberPointer) { if (Expr::hasAnyTypeDependentArguments(ArgExprs)) return CallExpr::Create(Context, Fn, ArgExprs, Context.DependentTy, - VK_RValue, RParenLoc); + VK_RValue, RParenLoc, FPFeatures); OverloadExpr *ovl = find.Expression; if (UnresolvedLookupExpr *ULE = dyn_cast(ovl)) return BuildOverloadedCallExpr( @@ -6000,7 +6000,7 @@ ResultTy, VK_RValue, RParenLoc, NumParams); } else { TheCall = CallExpr::Create(Context, Fn, Args, ResultTy, VK_RValue, - RParenLoc, NumParams, UsesADL); + RParenLoc, FPFeatures, NumParams, UsesADL); } if (!getLangOpts().CPlusPlus) { @@ -6030,7 +6030,7 @@ RParenLoc, NumParams); else TheCall = CallExpr::Create(Context, Fn, Args, ResultTy, VK_RValue, - RParenLoc, NumParams, UsesADL); + RParenLoc, FPFeatures, NumParams, UsesADL); } // We can now handle the nulled arguments for the default arguments. TheCall->setNumArgsUnsafe(std::max(Args.size(), NumParams)); @@ -13132,14 +13132,6 @@ if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) return ExprError(); - if (ResultTy->isRealFloatingType() && - (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)) { - F->setUsesFPIntrin(true); - } - // Some of the binary operations require promoting operands of half vector to // float vectors and truncating the result back to half vector. For now, we do // this only when HalfArgsAndReturn is set (that is, when the target is arm or @@ -13785,7 +13777,8 @@ CheckArrayAccess(Input.get()); auto *UO = new (Context) - UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow); + UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow, + FPFeatures); if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && !isa(UO->getType().getDesugaredType(Context))) @@ -18301,7 +18294,7 @@ CK_BuiltinFnToFnPtr) .get(); return CallExpr::Create(Context, E, /*Args=*/{}, Context.IntTy, - VK_RValue, SourceLocation()); + VK_RValue, SourceLocation(), FPFeatures); } } diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -4428,7 +4428,8 @@ Expr *sub = stripARCUnbridgedCast(uo->getSubExpr()); return new (Context) UnaryOperator(sub, UO_Extension, sub->getType(), sub->getValueKind(), - sub->getObjectKind(), uo->getOperatorLoc(), false); + sub->getObjectKind(), uo->getOperatorLoc(), false, + FPFeatures); } else if (GenericSelectionExpr *gse = dyn_cast(e)) { assert(!gse->isResultDependent()); diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -14437,7 +14437,8 @@ S.DefaultLvalueConversion(DeclareReductionRef.get()).get()); Expr *Args[] = {LHS.get(), RHS.get()}; ReductionOp = - CallExpr::Create(Context, OVE, Args, Context.VoidTy, VK_RValue, ELoc); + CallExpr::Create(Context, OVE, Args, Context.VoidTy, VK_RValue, ELoc, + S.FPFeatures); } else { ReductionOp = S.BuildBinOp( Stack->getCurScope(), ReductionId.getBeginLoc(), BOK, LHSDRE, RHSDRE); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -12719,7 +12719,7 @@ // lookup to instantiation time to be able to search into type dependent // base classes. CallExpr *CE = CallExpr::Create(Context, Fn, Args, Context.DependentTy, - VK_RValue, RParenLoc); + VK_RValue, RParenLoc, FPFeatures); CE->addDependence(ExprDependence::TypeValueInstantiation); *Result = CE; return true; @@ -12924,7 +12924,8 @@ if (Input->isTypeDependent()) { if (Fns.empty()) return new (Context) UnaryOperator(Input, Opc, Context.DependentTy, - VK_RValue, OK_Ordinary, OpLoc, false); + VK_RValue, OK_Ordinary, OpLoc, false, + FPFeatures); CXXRecordDecl *NamingClass = nullptr; // lookup ignores member operators UnresolvedLookupExpr *Fn = UnresolvedLookupExpr::Create( @@ -12932,7 +12933,7 @@ /*ADL*/ true, IsOverloaded(Fns), Fns.begin(), Fns.end()); return CXXOperatorCallExpr::Create(Context, Op, Fn, ArgsArray, Context.DependentTy, VK_RValue, OpLoc, - FPOptions()); + FPFeatures); } // Build an empty overload set. @@ -13006,7 +13007,7 @@ Args[0] = Input; CallExpr *TheCall = CXXOperatorCallExpr::Create( Context, Op, FnExpr.get(), ArgsArray, ResultTy, VK, OpLoc, - FPOptions(), Best->IsADLCandidate); + FPFeatures, Best->IsADLCandidate); if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl)) return ExprError(); @@ -13642,8 +13643,7 @@ // Can't add any actual overloads yet return CXXOperatorCallExpr::Create(Context, OO_Subscript, Fn, Args, - Context.DependentTy, VK_RValue, RLoc, - FPOptions()); + Context.DependentTy, VK_RValue, RLoc, FPFeatures); } // Handle placeholders on both operands. @@ -13718,7 +13718,7 @@ CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create(Context, OO_Subscript, FnExpr.get(), - Args, ResultTy, VK, RLoc, FPOptions()); + Args, ResultTy, VK, RLoc, FPFeatures); if (CheckCallReturnType(FnDecl->getReturnType(), LLoc, TheCall, FnDecl)) return ExprError(); @@ -13856,7 +13856,7 @@ if (isa(NakedMemExpr)) return CallExpr::Create(Context, MemExprE, Args, Context.VoidTy, VK_RValue, - RParenLoc); + RParenLoc, FPFeatures); UnbridgedCastsSet UnbridgedCasts; if (checkArgPlaceholdersForOverload(*this, Args, UnbridgedCasts)) @@ -14342,7 +14342,7 @@ CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create(Context, OO_Call, NewFn.get(), MethodArgs, - ResultTy, VK, RParenLoc, FPOptions()); + ResultTy, VK, RParenLoc, FPFeatures); if (CheckCallReturnType(Method->getReturnType(), LParenLoc, TheCall, Method)) return true; @@ -14459,7 +14459,7 @@ ExprValueKind VK = Expr::getValueKindForType(ResultTy); ResultTy = ResultTy.getNonLValueExprType(Context); CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create( - Context, OO_Arrow, FnExpr.get(), Base, ResultTy, VK, OpLoc, FPOptions()); + Context, OO_Arrow, FnExpr.get(), Base, ResultTy, VK, OpLoc, FPFeatures); if (CheckCallReturnType(Method->getReturnType(), OpLoc, TheCall, Method)) return ExprError(); @@ -14708,7 +14708,8 @@ return new (Context) UnaryOperator(SubExpr, UO_AddrOf, MemPtrType, VK_RValue, OK_Ordinary, - UnOp->getOperatorLoc(), false); + UnOp->getOperatorLoc(), false, + FPFeatures); } } Expr *SubExpr = FixOverloadedFunctionReference(UnOp->getSubExpr(), @@ -14719,7 +14720,8 @@ return new (Context) UnaryOperator(SubExpr, UO_AddrOf, Context.getPointerType(SubExpr->getType()), VK_RValue, OK_Ordinary, - UnOp->getOperatorLoc(), false); + UnOp->getOperatorLoc(), false, + FPFeatures); } if (UnresolvedLookupExpr *ULE = dyn_cast(E)) { diff --git a/clang/lib/Sema/SemaPseudoObject.cpp b/clang/lib/Sema/SemaPseudoObject.cpp --- a/clang/lib/Sema/SemaPseudoObject.cpp +++ b/clang/lib/Sema/SemaPseudoObject.cpp @@ -132,7 +132,7 @@ uop->getValueKind(), uop->getObjectKind(), uop->getOperatorLoc(), - uop->canOverflow()); + uop->canOverflow(), S.FPFeatures); } if (GenericSelectionExpr *gse = dyn_cast(e)) { @@ -541,7 +541,7 @@ !resultType->isDependentType() ? S.Context.getTypeSize(resultType) >= S.Context.getTypeSize(S.Context.IntTy) - : false); + : false, S.FPFeatures); return complete(syntactic); } @@ -1562,7 +1562,7 @@ // Do nothing if the operand is dependent. if (op->isTypeDependent()) return new (Context) UnaryOperator(op, opcode, Context.DependentTy, - VK_RValue, OK_Ordinary, opcLoc, false); + VK_RValue, OK_Ordinary, opcLoc, false, FPFeatures); assert(UnaryOperator::isIncrementDecrementOp(opcode)); Expr *opaqueRef = op->IgnoreParens(); @@ -1648,7 +1648,8 @@ Expr *op = stripOpaqueValuesFromPseudoObjectRef(*this, uop->getSubExpr()); return new (Context) UnaryOperator( op, uop->getOpcode(), uop->getType(), uop->getValueKind(), - uop->getObjectKind(), uop->getOperatorLoc(), uop->canOverflow()); + uop->getObjectKind(), uop->getOperatorLoc(), uop->canOverflow(), + FPFeatures); } else if (CompoundAssignOperator *cop = dyn_cast(syntax)) { Expr *lhs = stripOpaqueValuesFromPseudoObjectRef(*this, cop->getLHS()); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -374,6 +374,7 @@ } void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) { + PushCompoundScope(IsStmtExpr); } @@ -389,6 +390,13 @@ ArrayRef Elts, bool isStmtExpr) { const unsigned NumElts = Elts.size(); + if (getFPOptions().isFPConstrained()) { + // Mark the current function as usng floating point constrained intrinsics + if (FunctionDecl *F = dyn_cast(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/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3344,7 +3344,8 @@ // Build the CallExpr ExprResult TheCall = CallExpr::Create( SemaRef.Context, Callee, SubExprs, Builtin->getCallResultType(), - Expr::getValueKindForType(Builtin->getReturnType()), RParenLoc); + Expr::getValueKindForType(Builtin->getReturnType()), RParenLoc, + FPOptions()); // Type-check the __builtin_shufflevector expression. return SemaRef.SemaBuiltinShuffleVector(cast(TheCall.get())); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3773,6 +3773,29 @@ } break; } + + case FLOAT_CONTROL_PRAGMA_OPTIONS: { + if (Record.size() < 3) { + Error("invalid pragma pack record"); + return Failure; + } + FpPragmaCurrentValue = Record[0]; + FpPragmaCurrentLocation = ReadSourceLocation(F, Record[1]); + unsigned NumStackEntries = Record[2]; + unsigned Idx = 3; + // Reset the stack when importing a new module. + FpPragmaStack.clear(); + for (unsigned I = 0; I < NumStackEntries; ++I) { + FpPragmaStackEntry Entry; + Entry.Value = Record[Idx++]; + Entry.Location = ReadSourceLocation(F, Record[Idx++]); + Entry.PushLocation = ReadSourceLocation(F, Record[Idx++]); + FpPragmaStrings.push_back(ReadString(Record, Idx)); + Entry.SlotLabel = FpPragmaStrings.back(); + FpPragmaStack.push_back(Entry); + } + break; + } } } } @@ -7824,6 +7847,34 @@ SemaObj->PackStack.CurrentPragmaLocation = PragmaPackCurrentLocation; } } + if (FpPragmaCurrentValue) { + // The bottom of the stack might have a default value. It must be adjusted + // to the current value to ensure that fp-pragma state is preserved after + // popping entries that were included/imported from a PCH/module. + bool DropFirst = false; + if (!FpPragmaStack.empty() && + FpPragmaStack.front().Location.isInvalid()) { + assert(FpPragmaStack.front().Value == SemaObj->FpPragmaStack.DefaultValue + && "Expected a default pragma float_control value"); + SemaObj->FpPragmaStack.Stack.emplace_back( + FpPragmaStack.front().SlotLabel, SemaObj->FpPragmaStack.CurrentValue, + SemaObj->FpPragmaStack.CurrentPragmaLocation, + FpPragmaStack.front().PushLocation); + DropFirst = true; + } + for (const auto &Entry : + llvm::makeArrayRef(FpPragmaStack).drop_front(DropFirst ? 1 : 0)) + SemaObj->FpPragmaStack.Stack.emplace_back(Entry.SlotLabel, Entry.Value, + Entry.Location, Entry.PushLocation); + if (FpPragmaCurrentLocation.isInvalid()) { + assert(*FpPragmaCurrentValue == SemaObj->FpPragmaStack.DefaultValue && + "Expected a default pragma float_control value"); + // Keep the current values. + } else { + SemaObj->FpPragmaStack.CurrentValue = *FpPragmaCurrentValue; + SemaObj->FpPragmaStack.CurrentPragmaLocation = FpPragmaCurrentLocation; + } + } } IdentifierInfo *ASTReader::get(StringRef Name) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -681,6 +681,7 @@ E->setOpcode((UnaryOperator::Opcode)Record.readInt()); E->setOperatorLoc(readSourceLocation()); E->setCanOverflow(Record.readInt()); + E->setFPFeatures(FPOptions(Record.readInt())); } void ASTStmtReader::VisitOffsetOfExpr(OffsetOfExpr *E) { @@ -911,6 +912,7 @@ unsigned NumArgs = Record.readInt(); assert((NumArgs == E->getNumArgs()) && "Wrong NumArgs!"); E->setRParenLoc(readSourceLocation()); + E->setFPFeatures(FPOptions(Record.readInt())); E->setCallee(Record.readSubExpr()); for (unsigned I = 0; I != NumArgs; ++I) E->setArg(I, Record.readSubExpr()); @@ -1574,7 +1576,6 @@ void ASTStmtReader::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); E->CXXOperatorCallExprBits.OperatorKind = Record.readInt(); - E->CXXOperatorCallExprBits.FPFeatures = Record.readInt(); E->Range = Record.readSourceRange(); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -3903,7 +3903,7 @@ /// Write an FP_PRAGMA_OPTIONS block for the given FPOptions. void ASTWriter::WriteFPPragmaOptions(const FPOptions &Opts) { - RecordData::value_type Record[] = {Opts.getInt()}; + RecordData::value_type Record[] = {Opts.getAsOpaqueInt()}; Stream.EmitRecord(FP_PRAGMA_OPTIONS, Record); } @@ -4114,6 +4114,26 @@ Stream.EmitRecord(PACK_PRAGMA_OPTIONS, Record); } +/// Write the state of 'pragma float_control' at the end of the module. +void ASTWriter::WriteFloatControlPragmaOptions(Sema &SemaRef) { + // Don't serialize pragma pack state for modules, since it should only take + // effect on a per-submodule basis. + if (WritingModule) + return; + + RecordData Record; + Record.push_back(SemaRef.FpPragmaStack.CurrentValue); + AddSourceLocation(SemaRef.FpPragmaStack.CurrentPragmaLocation, Record); + Record.push_back(SemaRef.FpPragmaStack.Stack.size()); + for (const auto &StackEntry : SemaRef.FpPragmaStack.Stack) { + Record.push_back(StackEntry.Value); + AddSourceLocation(StackEntry.PragmaLocation, Record); + AddSourceLocation(StackEntry.PragmaPushLocation, Record); + AddString(StackEntry.StackSlotLabel, Record); + } + Stream.EmitRecord(FLOAT_CONTROL_PRAGMA_OPTIONS, Record); +} + void ASTWriter::WriteModuleFileExtension(Sema &SemaRef, ModuleFileExtensionWriter &Writer) { // Enter the extension block. @@ -4832,6 +4852,7 @@ WriteMSPointersToMembersPragmaOptions(SemaRef); } WritePackPragmaOptions(SemaRef); + WriteFloatControlPragmaOptions(SemaRef); // Some simple statistics RecordData::value_type Record[] = { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -703,6 +703,7 @@ Record.push_back(E->getOpcode()); // FIXME: stable encoding Record.AddSourceLocation(E->getOperatorLoc()); Record.push_back(E->canOverflow()); + Record.push_back(E->getFPFeatures().getAsOpaqueInt()); Code = serialization::EXPR_UNARY_OPERATOR; } @@ -777,6 +778,7 @@ VisitExpr(E); Record.push_back(E->getNumArgs()); Record.AddSourceLocation(E->getRParenLoc()); + Record.push_back(E->getFPFeatures().getAsOpaqueInt()); Record.AddStmt(E->getCallee()); for (CallExpr::arg_iterator Arg = E->arg_begin(), ArgEnd = E->arg_end(); Arg != ArgEnd; ++Arg) @@ -871,7 +873,7 @@ Record.AddStmt(E->getRHS()); Record.push_back(E->getOpcode()); // FIXME: stable encoding Record.AddSourceLocation(E->getOperatorLoc()); - Record.push_back(E->getFPFeatures().getInt()); + Record.push_back(E->getFPFeatures().getAsOpaqueInt()); Code = serialization::EXPR_BINARY_OPERATOR; } @@ -1461,7 +1463,6 @@ void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); Record.push_back(E->getOperator()); - Record.push_back(E->getFPFeatures().getInt()); Record.AddSourceRange(E->Range); Code = serialization::EXPR_CXX_OPERATOR_CALL; } diff --git a/clang/test/CodeGen/builtins-nvptx-ptx60.cu b/clang/test/CodeGen/builtins-nvptx-ptx60.cu --- a/clang/test/CodeGen/builtins-nvptx-ptx60.cu +++ b/clang/test/CodeGen/builtins-nvptx-ptx60.cu @@ -37,25 +37,25 @@ // CHECK: call i32 @llvm.nvvm.shfl.sync.down.i32(i32 {{%[0-9]+}}, i32 // expected-error@+1 {{'__nvvm_shfl_sync_down_i32' needs target feature ptx60}} __nvvm_shfl_sync_down_i32(mask, i, a, b); - // CHECK: call float @llvm.nvvm.shfl.sync.down.f32(i32 {{%[0-9]+}}, float + // CHECK: call contract float @llvm.nvvm.shfl.sync.down.f32(i32 {{%[0-9]+}}, float // expected-error@+1 {{'__nvvm_shfl_sync_down_f32' needs target feature ptx60}} __nvvm_shfl_sync_down_f32(mask, f, a, b); // CHECK: call i32 @llvm.nvvm.shfl.sync.up.i32(i32 {{%[0-9]+}}, i32 // expected-error@+1 {{'__nvvm_shfl_sync_up_i32' needs target feature ptx60}} __nvvm_shfl_sync_up_i32(mask, i, a, b); - // CHECK: call float @llvm.nvvm.shfl.sync.up.f32(i32 {{%[0-9]+}}, float + // CHECK: call contract float @llvm.nvvm.shfl.sync.up.f32(i32 {{%[0-9]+}}, float // expected-error@+1 {{'__nvvm_shfl_sync_up_f32' needs target feature ptx60}} __nvvm_shfl_sync_up_f32(mask, f, a, b); // CHECK: call i32 @llvm.nvvm.shfl.sync.bfly.i32(i32 {{%[0-9]+}}, i32 // expected-error@+1 {{'__nvvm_shfl_sync_bfly_i32' needs target feature ptx60}} __nvvm_shfl_sync_bfly_i32(mask, i, a, b); - // CHECK: call float @llvm.nvvm.shfl.sync.bfly.f32(i32 {{%[0-9]+}}, float + // CHECK: call contract float @llvm.nvvm.shfl.sync.bfly.f32(i32 {{%[0-9]+}}, float // expected-error@+1 {{'__nvvm_shfl_sync_bfly_f32' needs target feature ptx60}} __nvvm_shfl_sync_bfly_f32(mask, f, a, b); // CHECK: call i32 @llvm.nvvm.shfl.sync.idx.i32(i32 {{%[0-9]+}}, i32 // expected-error@+1 {{'__nvvm_shfl_sync_idx_i32' needs target feature ptx60}} __nvvm_shfl_sync_idx_i32(mask, i, a, b); - // CHECK: call float @llvm.nvvm.shfl.sync.idx.f32(i32 {{%[0-9]+}}, float + // CHECK: call contract float @llvm.nvvm.shfl.sync.idx.f32(i32 {{%[0-9]+}}, float // expected-error@+1 {{'__nvvm_shfl_sync_idx_f32' needs target feature ptx60}} __nvvm_shfl_sync_idx_f32(mask, f, a, b); diff --git a/clang/test/CodeGen/constrained-math-builtins.c b/clang/test/CodeGen/constrained-math-builtins.c --- a/clang/test/CodeGen/constrained-math-builtins.c +++ b/clang/test/CodeGen/constrained-math-builtins.c @@ -154,9 +154,9 @@ (double)f * f - f; (long double)-f * f + f; -// CHECK: call float @llvm.experimental.constrained.fmuladd.f32 +// CHECK: call contract float @llvm.experimental.constrained.fmuladd.f32 // CHECK: fneg -// CHECK: call double @llvm.experimental.constrained.fmuladd.f64 +// CHECK: call contract double @llvm.experimental.constrained.fmuladd.f64 // CHECK: fneg -// CHECK: call x86_fp80 @llvm.experimental.constrained.fmuladd.f80 +// CHECK: call contract x86_fp80 @llvm.experimental.constrained.fmuladd.f80 }; diff --git a/clang/test/CodeGen/fast-math.c b/clang/test/CodeGen/fast-math.c --- a/clang/test/CodeGen/fast-math.c +++ b/clang/test/CodeGen/fast-math.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -ffast-math -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -ffast-math -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s float f0, f1, f2; void foo(void) { diff --git a/clang/test/CodeGen/fp-contract-on-pragma.cpp b/clang/test/CodeGen/fp-contract-on-pragma.cpp --- a/clang/test/CodeGen/fp-contract-on-pragma.cpp +++ b/clang/test/CodeGen/fp-contract-on-pragma.cpp @@ -3,7 +3,7 @@ // Is FP_CONTRACT honored in a simple case? float fp_contract_1(float a, float b, float c) { // CHECK: _Z13fp_contract_1fff -// CHECK: tail call float @llvm.fmuladd +// CHECK: tail call contract float @llvm.fmuladd #pragma clang fp contract(on) return a * b + c; } @@ -31,7 +31,7 @@ float fp_contract_3(float a, float b, float c) { // CHECK: _Z13fp_contract_3fff - // CHECK: tail call float @llvm.fmuladd + // CHECK: tail call contract float @llvm.fmuladd return template_muladd(a, b, c); } @@ -45,13 +45,13 @@ template class fp_contract_4; // CHECK: _ZN13fp_contract_4IiE6methodEfff -// CHECK: tail call float @llvm.fmuladd +// CHECK: tail call contract float @llvm.fmuladd // Check file-scoped FP_CONTRACT #pragma clang fp contract(on) float fp_contract_5(float a, float b, float c) { // CHECK: _Z13fp_contract_5fff - // CHECK: tail call float @llvm.fmuladd + // CHECK: tail call contract float @llvm.fmuladd return a * b + c; } @@ -69,8 +69,8 @@ float fp_contract_7(float a, float b, float c) { // CHECK: _Z13fp_contract_7fff -// CHECK: %[[M:.+]] = fmul float %b, 2.000000e+00 -// CHECK-NEXT: fsub float %[[M]], %c +// CHECK: %[[M:.+]] = fmul contract float %b, 2.000000e+00 +// CHECK-NEXT: fsub contract float %[[M]], %c #pragma clang fp contract(on) return (a = 2 * b) - c; } diff --git a/clang/test/CodeGen/fp-contract-pragma.cpp b/clang/test/CodeGen/fp-contract-pragma.cpp --- a/clang/test/CodeGen/fp-contract-pragma.cpp +++ b/clang/test/CodeGen/fp-contract-pragma.cpp @@ -3,7 +3,7 @@ // Is FP_CONTRACT honored in a simple case? float fp_contract_1(float a, float b, float c) { // CHECK: _Z13fp_contract_1fff -// CHECK: tail call float @llvm.fmuladd +// CHECK: tail call contract float @llvm.fmuladd #pragma STDC FP_CONTRACT ON return a * b + c; } @@ -31,7 +31,7 @@ float fp_contract_3(float a, float b, float c) { // CHECK: _Z13fp_contract_3fff -// CHECK: tail call float @llvm.fmuladd +// CHECK: tail call contract float @llvm.fmuladd return template_muladd(a, b, c); } @@ -44,13 +44,13 @@ template class fp_contract_4; // CHECK: _ZN13fp_contract_4IiE6methodEfff -// CHECK: tail call float @llvm.fmuladd +// CHECK: tail call contract float @llvm.fmuladd // Check file-scoped FP_CONTRACT #pragma STDC FP_CONTRACT ON float fp_contract_5(float a, float b, float c) { // CHECK: _Z13fp_contract_5fff -// CHECK: tail call float @llvm.fmuladd +// CHECK: tail call contract float @llvm.fmuladd return a * b + c; } @@ -68,24 +68,24 @@ float fp_contract_7(float a, float b, float c) { // CHECK: _Z13fp_contract_7fff -// CHECK: %[[M:.+]] = fmul float %b, 2.000000e+00 -// CHECK-NEXT: fsub float %[[M]], %c +// CHECK: %[[M:.+]] = fmul contract float %b, 2.000000e+00 +// CHECK-NEXT: fsub contract float %[[M]], %c #pragma STDC FP_CONTRACT ON return (a = 2 * b) - c; } float fp_contract_8(float a, float b, float c) { // CHECK: _Z13fp_contract_8fff -// CHECK: fneg float %c -// CHECK: tail call float @llvm.fmuladd +// CHECK: fneg contract float %c +// CHECK: tail call contract float @llvm.fmuladd #pragma STDC FP_CONTRACT ON return a * b - c; } float fp_contract_9(float a, float b, float c) { // CHECK: _Z13fp_contract_9fff -// CHECK: fneg float %a -// CHECK: tail call float @llvm.fmuladd +// CHECK: fneg contract float %a +// CHECK: tail call contract float @llvm.fmuladd #pragma STDC FP_CONTRACT ON return c - a * b; } 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,19 @@ +// RUN: %clang -c -ffp-contract=on -Xclang -emit-llvm -o - %s | FileCheck %s +// Verify that float_control does not pertain to initializer expressions + +float y(); +float z(); +#pragma float_control(except, on) +class ON { +float w = 2 + y() * z(); +// CHECK-LABEL: define {{.*}} void @_ZN2ONC2Ev{{.*}} +//CHECK: call contract float {{.*}}llvm.fmuladd +}; +ON on; +#pragma float_control( except, off) +class OFF { +float w = 2 + y() * z(); +// CHECK-LABEL: define {{.*}} void @_ZN3OFFC2Ev{{.*}} +//CHECK: call contract float {{.*}}llvm.fmuladd +}; +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 fast float +//CHECK: fadd fast 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,252 @@ +// RUN: %clang -c -ffp-contract=on -DDEFAULT=1 -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DDEFAULT %s +// RUN: %clang -c -ffp-contract=on -DEBSTRICT=1 -ffp-exception-behavior=strict -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DEBSTRICT %s +// RUN: %clang -c -DFAST=1 -ffast-math -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-FAST %s +// RUN: %clang -c -ffp-contract=on -DNOHONOR=1 -fno-honor-nans -fno-honor-infinities -Xclang -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-NOHONOR %s + +#define FUN(n) (float z) { return n * z + n; } + +float fun_default FUN(1) +//CHECK-LABEL: define {{.*}} @_Z11fun_defaultf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: call contract float @llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +// Note that backend wants constrained intrinsics used +// throughout the function if they are needed anywhere in the function. +// In that case, operations are built with constrained intrinsics operator +// but using default settings for exception behavior and rounding mode. +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif + +#pragma float_control(push) +#ifndef FAST +// Rule: precise must be enabled +#pragma float_control(except, on) +#endif +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.fmuladd{{.*}}tonearest{{.*}}strict +#endif +#if NOHONOR +//CHECK-NOHONOR: nnan ninf contract float {{.*}}llvm.experimental.constrained.fmuladd{{.*}}tonearest{{.*}}strict +#endif +#if FAST +//Not possible to enable float_control(except) in FAST mode. +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif + +#pragma float_control(pop) +float exc_pop FUN(5) +//CHECK-LABEL: define {{.*}} @_Z7exc_popf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: call contract float @llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: llvm.experimental.constrained.fmuladd{{.*}}tonearest{{.*}}strict +#endif +#if NOHONOR +//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif + +#pragma float_control(except, off) +float exc_off FUN(5) +//CHECK-LABEL: define {{.*}} @_Z7exc_offf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: call contract float @llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: call contract float @llvm.fmuladd{{.*}} +#endif +#if NOHONOR +//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif + +#pragma float_control(precise, on, push) +float precise_on FUN(3) +//CHECK-LABEL: define {{.*}} @_Z10precise_onf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if NOHONOR +// If precise is pushed then all fast-math should be off! +//CHECK-NOHONOR: call contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}} +#endif + +#pragma float_control(pop) +float precise_pop FUN(3) +//CHECK-LABEL: define {{.*}} @_Z11precise_popf{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if NOHONOR +//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif +#pragma float_control(precise, off) +float precise_off FUN(4) +//CHECK-LABEL: define {{.*}} @_Z11precise_offf{{.*}} +#if DEFAULT +// Note: precise_off enables fp_contract=fast and the instructions +// generated do not include the contract flag, although it was enabled +// in IRBuilder. +//CHECK-DDEFAULT: fmul fast float +//CHECK-DDEFAULT: fadd fast float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: fmul fast float +//CHECK-DEBSTRICT: fadd fast float +#endif +#if NOHONOR +// fast math should be enabled, and contract should be fast +//CHECK-NOHONOR: fmul fast float +//CHECK-NOHONOR: fadd fast float +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif + +#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: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if NOHONOR +// fast math should be off, and contract should be on +//CHECK-NOHONOR: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}} +#endif + +#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: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if NOHONOR +//CHECK-NOHONOR: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}} +#endif + +#pragma float_control(precise, off) +float precise_off2 FUN(4) +//CHECK-LABEL: define {{.*}} @_Z12precise_off2f{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: fmul fast float +//CHECK-DDEFAULT: fadd fast float +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: fmul fast float +//CHECK-DEBSTRICT: fadd fast float +#endif +#if NOHONOR +// fast math settings since precise is off +//CHECK-NOHONOR: fmul fast float +//CHECK-NOHONOR: fadd fast float +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif + +#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: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if NOHONOR +//CHECK-NOHONOR: contract float {{.*}}llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: contract float {{.*}}llvm.fmuladd{{.*}} +#endif + +#ifndef FAST +// Rule: precise must be enabled +#pragma float_control(except, on) +#endif +float y(); +class ON { +// Settings for top level class initializer revert to command line +// source pragma's do not pertain. +float z = 2 + y() * 7; +//CHECK-LABEL: define {{.*}} void @_ZN2ONC2Ev{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: call contract float {{.*}}llvm.fmuladd +#endif +#if EBSTRICT +//Currently, same as default [command line options not considered] +//CHECK-DEBSTRICT: call contract float {{.*}}llvm.fmuladd +#endif +#if NOHONOR +//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif +}; +ON on; +#pragma float_control( except, off) +class OFF { +float w = 2 + y() * 7; +//CHECK-LABEL: define {{.*}} void @_ZN3OFFC2Ev{{.*}} +#if DEFAULT +//CHECK-DDEFAULT: call contract float {{.*}}llvm.fmuladd +#endif +#if EBSTRICT +//CHECK-DEBSTRICT: call contract float {{.*}}llvm.fmuladd +#endif +#if NOHONOR +//CHECK-NOHONOR: call nnan ninf contract float @llvm.fmuladd{{.*}} +#endif +#if FAST +//CHECK-FAST: fmul fast float +//CHECK-FAST: fadd fast float +#endif +}; +OFF off; diff --git a/clang/test/CodeGen/fpconstrained.c b/clang/test/CodeGen/fpconstrained.c --- a/clang/test/CodeGen/fpconstrained.c +++ b/clang/test/CodeGen/fpconstrained.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -ftrapping-math -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=FPMODELSTRICT // RUN: %clang_cc1 -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=PRECISE // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST -// RUN: %clang_cc1 -ffast-math -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -ffast-math -emit-llvm -o - %s | FileCheck %s -check-prefix=FASTNOCONTRACT // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT // RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=MAYTRAP @@ -17,6 +17,7 @@ // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") // PRECISE: fadd contract float %{{.*}}, %{{.*}} // FAST: fadd fast + // FASTNOCONTRACT: fadd reassoc nnan ninf nsz arcp afn float f0 = f1 + f2; // CHECK: ret diff --git a/clang/test/CodeGen/fpconstrained.cpp b/clang/test/CodeGen/fpconstrained.cpp --- a/clang/test/CodeGen/fpconstrained.cpp +++ b/clang/test/CodeGen/fpconstrained.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -x c++ -ftrapping-math -fexceptions -fcxx-exceptions -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=FPMODELSTRICT // RUN: %clang_cc1 -x c++ -ffp-contract=fast -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=PRECISE // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST -// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=FASTNOCONTRACT // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT // RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=MAYTRAP @@ -27,6 +27,7 @@ // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") // PRECISE: fadd contract float %{{.*}}, %{{.*}} // FAST: fadd fast + // FASTNOCONTRACT: fadd reassoc nnan ninf nsz arcp afn float f0 = f1 + f2; // CHECK: ret void diff --git a/clang/test/CodeGenCUDA/builtins-amdgcn.cu b/clang/test/CodeGenCUDA/builtins-amdgcn.cu --- a/clang/test/CodeGenCUDA/builtins-amdgcn.cu +++ b/clang/test/CodeGenCUDA/builtins-amdgcn.cu @@ -10,7 +10,7 @@ } // CHECK-LABEL: @_Z12test_ds_fmaxf( -// CHECK: call float @llvm.amdgcn.ds.fmax(float addrspace(3)* @_ZZ12test_ds_fmaxfE6shared, float %{{[^,]*}}, i32 0, i32 0, i1 false) +// CHECK: call contract float @llvm.amdgcn.ds.fmax(float addrspace(3)* @_ZZ12test_ds_fmaxfE6shared, float %{{[^,]*}}, i32 0, i32 0, i1 false) __global__ void test_ds_fmax(float src) { __shared__ float shared; diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl --- a/clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-dl-insts.cl @@ -10,8 +10,8 @@ typedef unsigned short __attribute__((ext_vector_type(2))) ushort2; // CHECK-LABEL: @builtins_amdgcn_dl_insts -// CHECK: call float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 false) -// CHECK: call float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 true) +// CHECK: call contract float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 false) +// CHECK: call contract float @llvm.amdgcn.fdot2(<2 x half> %v2hA, <2 x half> %v2hB, float %fC, i1 true) // CHECK: call i32 @llvm.amdgcn.sdot2(<2 x i16> %v2ssA, <2 x i16> %v2ssB, i32 %siC, i1 false) // CHECK: call i32 @llvm.amdgcn.sdot2(<2 x i16> %v2ssA, <2 x i16> %v2ssB, i32 %siC, i1 true) diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl --- a/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-gfx9.cl @@ -5,7 +5,7 @@ #pragma OPENCL EXTENSION cl_khr_fp16 : enable // CHECK-LABEL: @test_fmed3_f16 -// CHECK: call half @llvm.amdgcn.fmed3.f16(half %a, half %b, half %c) +// CHECK: call contract half @llvm.amdgcn.fmed3.f16(half %a, half %b, half %c) void test_fmed3_f16(global half* out, half a, half b, half c) { *out = __builtin_amdgcn_fmed3h(a, b, c); diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl --- a/clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-interp.cl @@ -19,7 +19,7 @@ // CHECK-LABEL: test_interp_f32 // CHECK: call float @llvm.amdgcn.interp.p1 -// CHECK: call float @llvm.amdgcn.interp.p2 +// CHECK: call contract float @llvm.amdgcn.interp.p2 void test_interp_f32(global float* out, float i, float j, int m0) { float p1 = __builtin_amdgcn_interp_p1(i, 1, 4, m0); @@ -27,7 +27,7 @@ } // CHECK-LABEL: test_interp_mov -// CHECK: call float @llvm.amdgcn.interp.mov +// CHECK: call contract float @llvm.amdgcn.interp.mov void test_interp_mov(global float* out, float i, float j, int m0) { *out = __builtin_amdgcn_interp_mov(2, 3, 4, m0); diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl --- a/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-mfma.cl @@ -20,70 +20,70 @@ // CHECK-LABEL: @test_mfma_f32_32x32x1f32 -// CHECK: call <32 x float> @llvm.amdgcn.mfma.f32.32x32x1f32(float %a, float %b, <32 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <32 x float> @llvm.amdgcn.mfma.f32.32x32x1f32(float %a, float %b, <32 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_32x32x1f32(global v32f* out, float a, float b, v32f c) { *out = __builtin_amdgcn_mfma_f32_32x32x1f32(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_16x16x1f32 -// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.16x16x1f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.16x16x1f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_16x16x1f32(global v16f* out, float a, float b, v16f c) { *out = __builtin_amdgcn_mfma_f32_16x16x1f32(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_4x4x1f32 -// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.4x4x1f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.4x4x1f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_4x4x1f32(global v4f* out, float a, float b, v4f c) { *out = __builtin_amdgcn_mfma_f32_4x4x1f32(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_32x32x2f32 -// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.32x32x2f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.32x32x2f32(float %a, float %b, <16 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_32x32x2f32(global v16f* out, float a, float b, v16f c) { *out = __builtin_amdgcn_mfma_f32_32x32x2f32(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_16x16x4f32 -// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.16x16x4f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.16x16x4f32(float %a, float %b, <4 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_16x16x4f32(global v4f* out, float a, float b, v4f c) { *out = __builtin_amdgcn_mfma_f32_16x16x4f32(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_32x32x4f16 -// CHECK: call <32 x float> @llvm.amdgcn.mfma.f32.32x32x4f16(<4 x half> %a, <4 x half> %b, <32 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <32 x float> @llvm.amdgcn.mfma.f32.32x32x4f16(<4 x half> %a, <4 x half> %b, <32 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_32x32x4f16(global v32f* out, v4h a, v4h b, v32f c) { *out = __builtin_amdgcn_mfma_f32_32x32x4f16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_16x16x4f16 -// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.16x16x4f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.16x16x4f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_16x16x4f16(global v16f* out, v4h a, v4h b, v16f c) { *out = __builtin_amdgcn_mfma_f32_16x16x4f16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_4x4x4f16 -// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.4x4x4f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.4x4x4f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_4x4x4f16(global v4f* out, v4h a, v4h b, v4f c) { *out = __builtin_amdgcn_mfma_f32_4x4x4f16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_32x32x8f16 -// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.32x32x8f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.32x32x8f16(<4 x half> %a, <4 x half> %b, <16 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_32x32x8f16(global v16f* out, v4h a, v4h b, v16f c) { *out = __builtin_amdgcn_mfma_f32_32x32x8f16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_16x16x16f16 -// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.16x16x16f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.16x16x16f16(<4 x half> %a, <4 x half> %b, <4 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_16x16x16f16(global v4f* out, v4h a, v4h b, v4f c) { *out = __builtin_amdgcn_mfma_f32_16x16x16f16(a, b, c, 0, 0, 0); @@ -125,35 +125,35 @@ } // CHECK-LABEL: @test_mfma_f32_32x32x2bf16 -// CHECK: call <32 x float> @llvm.amdgcn.mfma.f32.32x32x2bf16(<2 x i16> %a, <2 x i16> %b, <32 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <32 x float> @llvm.amdgcn.mfma.f32.32x32x2bf16(<2 x i16> %a, <2 x i16> %b, <32 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_32x32x2bf16(global v32f* out, v2s a, v2s b, v32f c) { *out = __builtin_amdgcn_mfma_f32_32x32x2bf16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_16x16x2bf16 -// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.16x16x2bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.16x16x2bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_16x16x2bf16(global v16f* out, v2s a, v2s b, v16f c) { *out = __builtin_amdgcn_mfma_f32_16x16x2bf16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_4x4x2bf16 -// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.4x4x2bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.4x4x2bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_4x4x2bf16(global v4f* out, v2s a, v2s b, v4f c) { *out = __builtin_amdgcn_mfma_f32_4x4x2bf16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_32x32x4bf16 -// CHECK: call <16 x float> @llvm.amdgcn.mfma.f32.32x32x4bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <16 x float> @llvm.amdgcn.mfma.f32.32x32x4bf16(<2 x i16> %a, <2 x i16> %b, <16 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_32x32x4bf16(global v16f* out, v2s a, v2s b, v16f c) { *out = __builtin_amdgcn_mfma_f32_32x32x4bf16(a, b, c, 0, 0, 0); } // CHECK-LABEL: @test_mfma_f32_16x16x8bf16 -// CHECK: call <4 x float> @llvm.amdgcn.mfma.f32.16x16x8bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0) +// CHECK: call contract <4 x float> @llvm.amdgcn.mfma.f32.16x16x8bf16(<2 x i16> %a, <2 x i16> %b, <4 x float> %c, i32 0, i32 0, i32 0) void test_mfma_f32_16x16x8bf16(global v4f* out, v2s a, v2s b, v4f c) { *out = __builtin_amdgcn_mfma_f32_16x16x8bf16(a, b, c, 0, 0, 0); diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl --- a/clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl @@ -9,49 +9,49 @@ typedef unsigned long ulong; // CHECK-LABEL: @test_div_fixup_f16 -// CHECK: call half @llvm.amdgcn.div.fixup.f16 +// CHECK: call contract half @llvm.amdgcn.div.fixup.f16 void test_div_fixup_f16(global half* out, half a, half b, half c) { *out = __builtin_amdgcn_div_fixuph(a, b, c); } // CHECK-LABEL: @test_rcp_f16 -// CHECK: call half @llvm.amdgcn.rcp.f16 +// CHECK: call contract half @llvm.amdgcn.rcp.f16 void test_rcp_f16(global half* out, half a) { *out = __builtin_amdgcn_rcph(a); } // CHECK-LABEL: @test_rsq_f16 -// CHECK: call half @llvm.amdgcn.rsq.f16 +// CHECK: call contract half @llvm.amdgcn.rsq.f16 void test_rsq_f16(global half* out, half a) { *out = __builtin_amdgcn_rsqh(a); } // CHECK-LABEL: @test_sin_f16 -// CHECK: call half @llvm.amdgcn.sin.f16 +// CHECK: call contract half @llvm.amdgcn.sin.f16 void test_sin_f16(global half* out, half a) { *out = __builtin_amdgcn_sinh(a); } // CHECK-LABEL: @test_cos_f16 -// CHECK: call half @llvm.amdgcn.cos.f16 +// CHECK: call contract half @llvm.amdgcn.cos.f16 void test_cos_f16(global half* out, half a) { *out = __builtin_amdgcn_cosh(a); } // CHECK-LABEL: @test_ldexp_f16 -// CHECK: call half @llvm.amdgcn.ldexp.f16 +// CHECK: call contract half @llvm.amdgcn.ldexp.f16 void test_ldexp_f16(global half* out, half a, int b) { *out = __builtin_amdgcn_ldexph(a, b); } // CHECK-LABEL: @test_frexp_mant_f16 -// CHECK: call half @llvm.amdgcn.frexp.mant.f16 +// CHECK: call contract half @llvm.amdgcn.frexp.mant.f16 void test_frexp_mant_f16(global half* out, half a) { *out = __builtin_amdgcn_frexp_manth(a); @@ -65,7 +65,7 @@ } // CHECK-LABEL: @test_fract_f16 -// CHECK: call half @llvm.amdgcn.fract.f16 +// CHECK: call contract half @llvm.amdgcn.fract.f16 void test_fract_f16(global half* out, half a) { *out = __builtin_amdgcn_fracth(a); @@ -107,19 +107,19 @@ } // CHECK-LABEL: @test_ds_fadd -// CHECK: call float @llvm.amdgcn.ds.fadd(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false) +// CHECK: call contract float @llvm.amdgcn.ds.fadd(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false) void test_ds_faddf(local float *out, float src) { *out = __builtin_amdgcn_ds_faddf(out, src, 0, 0, false); } // CHECK-LABEL: @test_ds_fmin -// CHECK: call float @llvm.amdgcn.ds.fmin(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false) +// CHECK: call contract float @llvm.amdgcn.ds.fmin(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false) void test_ds_fminf(local float *out, float src) { *out = __builtin_amdgcn_ds_fminf(out, src, 0, 0, false); } // CHECK-LABEL: @test_ds_fmax -// CHECK: call float @llvm.amdgcn.ds.fmax(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false) +// CHECK: call contract float @llvm.amdgcn.ds.fmax(float addrspace(3)* %out, float %src, i32 0, i32 0, i1 false) void test_ds_fmaxf(local float *out, float src) { *out = __builtin_amdgcn_ds_fmaxf(out, src, 0, 0, false); } diff --git a/clang/test/CodeGenOpenCL/builtins-amdgcn.cl b/clang/test/CodeGenOpenCL/builtins-amdgcn.cl --- a/clang/test/CodeGenOpenCL/builtins-amdgcn.cl +++ b/clang/test/CodeGenOpenCL/builtins-amdgcn.cl @@ -61,133 +61,133 @@ } // CHECK-LABEL: @test_div_fmas_f32 -// CHECK: call float @llvm.amdgcn.div.fmas.f32 +// CHECK: call contract float @llvm.amdgcn.div.fmas.f32 void test_div_fmas_f32(global float* out, float a, float b, float c, int d) { *out = __builtin_amdgcn_div_fmasf(a, b, c, d); } // CHECK-LABEL: @test_div_fmas_f64 -// CHECK: call double @llvm.amdgcn.div.fmas.f64 +// CHECK: call contract double @llvm.amdgcn.div.fmas.f64 void test_div_fmas_f64(global double* out, double a, double b, double c, int d) { *out = __builtin_amdgcn_div_fmas(a, b, c, d); } // CHECK-LABEL: @test_div_fixup_f32 -// CHECK: call float @llvm.amdgcn.div.fixup.f32 +// CHECK: call contract float @llvm.amdgcn.div.fixup.f32 void test_div_fixup_f32(global float* out, float a, float b, float c) { *out = __builtin_amdgcn_div_fixupf(a, b, c); } // CHECK-LABEL: @test_div_fixup_f64 -// CHECK: call double @llvm.amdgcn.div.fixup.f64 +// CHECK: call contract double @llvm.amdgcn.div.fixup.f64 void test_div_fixup_f64(global double* out, double a, double b, double c) { *out = __builtin_amdgcn_div_fixup(a, b, c); } // CHECK-LABEL: @test_trig_preop_f32 -// CHECK: call float @llvm.amdgcn.trig.preop.f32 +// CHECK: call contract float @llvm.amdgcn.trig.preop.f32 void test_trig_preop_f32(global float* out, float a, int b) { *out = __builtin_amdgcn_trig_preopf(a, b); } // CHECK-LABEL: @test_trig_preop_f64 -// CHECK: call double @llvm.amdgcn.trig.preop.f64 +// CHECK: call contract double @llvm.amdgcn.trig.preop.f64 void test_trig_preop_f64(global double* out, double a, int b) { *out = __builtin_amdgcn_trig_preop(a, b); } // CHECK-LABEL: @test_rcp_f32 -// CHECK: call float @llvm.amdgcn.rcp.f32 +// CHECK: call contract float @llvm.amdgcn.rcp.f32 void test_rcp_f32(global float* out, float a) { *out = __builtin_amdgcn_rcpf(a); } // CHECK-LABEL: @test_rcp_f64 -// CHECK: call double @llvm.amdgcn.rcp.f64 +// CHECK: call contract double @llvm.amdgcn.rcp.f64 void test_rcp_f64(global double* out, double a) { *out = __builtin_amdgcn_rcp(a); } // CHECK-LABEL: @test_rsq_f32 -// CHECK: call float @llvm.amdgcn.rsq.f32 +// CHECK: call contract float @llvm.amdgcn.rsq.f32 void test_rsq_f32(global float* out, float a) { *out = __builtin_amdgcn_rsqf(a); } // CHECK-LABEL: @test_rsq_f64 -// CHECK: call double @llvm.amdgcn.rsq.f64 +// CHECK: call contract double @llvm.amdgcn.rsq.f64 void test_rsq_f64(global double* out, double a) { *out = __builtin_amdgcn_rsq(a); } // CHECK-LABEL: @test_rsq_clamp_f32 -// CHECK: call float @llvm.amdgcn.rsq.clamp.f32 +// CHECK: call contract float @llvm.amdgcn.rsq.clamp.f32 void test_rsq_clamp_f32(global float* out, float a) { *out = __builtin_amdgcn_rsq_clampf(a); } // CHECK-LABEL: @test_rsq_clamp_f64 -// CHECK: call double @llvm.amdgcn.rsq.clamp.f64 +// CHECK: call contract double @llvm.amdgcn.rsq.clamp.f64 void test_rsq_clamp_f64(global double* out, double a) { *out = __builtin_amdgcn_rsq_clamp(a); } // CHECK-LABEL: @test_sin_f32 -// CHECK: call float @llvm.amdgcn.sin.f32 +// CHECK: call contract float @llvm.amdgcn.sin.f32 void test_sin_f32(global float* out, float a) { *out = __builtin_amdgcn_sinf(a); } // CHECK-LABEL: @test_cos_f32 -// CHECK: call float @llvm.amdgcn.cos.f32 +// CHECK: call contract float @llvm.amdgcn.cos.f32 void test_cos_f32(global float* out, float a) { *out = __builtin_amdgcn_cosf(a); } // CHECK-LABEL: @test_log_clamp_f32 -// CHECK: call float @llvm.amdgcn.log.clamp.f32 +// CHECK: call contract float @llvm.amdgcn.log.clamp.f32 void test_log_clamp_f32(global float* out, float a) { *out = __builtin_amdgcn_log_clampf(a); } // CHECK-LABEL: @test_ldexp_f32 -// CHECK: call float @llvm.amdgcn.ldexp.f32 +// CHECK: call contract float @llvm.amdgcn.ldexp.f32 void test_ldexp_f32(global float* out, float a, int b) { *out = __builtin_amdgcn_ldexpf(a, b); } // CHECK-LABEL: @test_ldexp_f64 -// CHECK: call double @llvm.amdgcn.ldexp.f64 +// CHECK: call contract double @llvm.amdgcn.ldexp.f64 void test_ldexp_f64(global double* out, double a, int b) { *out = __builtin_amdgcn_ldexp(a, b); } // CHECK-LABEL: @test_frexp_mant_f32 -// CHECK: call float @llvm.amdgcn.frexp.mant.f32 +// CHECK: call contract float @llvm.amdgcn.frexp.mant.f32 void test_frexp_mant_f32(global float* out, float a) { *out = __builtin_amdgcn_frexp_mantf(a); } // CHECK-LABEL: @test_frexp_mant_f64 -// CHECK: call double @llvm.amdgcn.frexp.mant.f64 +// CHECK: call contract double @llvm.amdgcn.frexp.mant.f64 void test_frexp_mant_f64(global double* out, double a) { *out = __builtin_amdgcn_frexp_mant(a); @@ -208,14 +208,14 @@ } // CHECK-LABEL: @test_fract_f32 -// CHECK: call float @llvm.amdgcn.fract.f32 +// CHECK: call contract float @llvm.amdgcn.fract.f32 void test_fract_f32(global int* out, float a) { *out = __builtin_amdgcn_fractf(a); } // CHECK-LABEL: @test_fract_f64 -// CHECK: call double @llvm.amdgcn.fract.f64 +// CHECK: call contract double @llvm.amdgcn.fract.f64 void test_fract_f64(global int* out, double a) { *out = __builtin_amdgcn_fract(a); @@ -417,25 +417,25 @@ } // CHECK-LABEL: @test_cubeid( -// CHECK: call float @llvm.amdgcn.cubeid(float %a, float %b, float %c) +// CHECK: call contract float @llvm.amdgcn.cubeid(float %a, float %b, float %c) void test_cubeid(global float* out, float a, float b, float c) { *out = __builtin_amdgcn_cubeid(a, b, c); } // CHECK-LABEL: @test_cubesc( -// CHECK: call float @llvm.amdgcn.cubesc(float %a, float %b, float %c) +// CHECK: call contract float @llvm.amdgcn.cubesc(float %a, float %b, float %c) void test_cubesc(global float* out, float a, float b, float c) { *out = __builtin_amdgcn_cubesc(a, b, c); } // CHECK-LABEL: @test_cubetc( -// CHECK: call float @llvm.amdgcn.cubetc(float %a, float %b, float %c) +// CHECK: call contract float @llvm.amdgcn.cubetc(float %a, float %b, float %c) void test_cubetc(global float* out, float a, float b, float c) { *out = __builtin_amdgcn_cubetc(a, b, c); } // CHECK-LABEL: @test_cubema( -// CHECK: call float @llvm.amdgcn.cubema(float %a, float %b, float %c) +// CHECK: call contract float @llvm.amdgcn.cubema(float %a, float %b, float %c) void test_cubema(global float* out, float a, float b, float c) { *out = __builtin_amdgcn_cubema(a, b, c); } @@ -528,7 +528,7 @@ } // CHECK-LABEL: @test_fmed3_f32 -// CHECK: call float @llvm.amdgcn.fmed3.f32( +// CHECK: call contract float @llvm.amdgcn.fmed3.f32( void test_fmed3_f32(global float* out, float a, float b, float c) { *out = __builtin_amdgcn_fmed3f(a, b, c); @@ -620,7 +620,7 @@ } // CHECK-LABEL: @test_cvt_pkrtz( -// CHECK: tail call <2 x half> @llvm.amdgcn.cvt.pkrtz(float %src0, float %src1) +// CHECK: tail call contract <2 x half> @llvm.amdgcn.cvt.pkrtz(float %src0, float %src1) kernel void test_cvt_pkrtz(global half2* out, float src0, float src1) { *out = __builtin_amdgcn_cvt_pkrtz(src0, src1); } diff --git a/clang/test/CodeGenOpenCL/builtins-f16.cl b/clang/test/CodeGenOpenCL/builtins-f16.cl --- a/clang/test/CodeGenOpenCL/builtins-f16.cl +++ b/clang/test/CodeGenOpenCL/builtins-f16.cl @@ -6,66 +6,66 @@ void test_half_builtins(half h0, half h1, half h2) { volatile half res; - // CHECK: call half @llvm.copysign.f16(half %h0, half %h1) + // CHECK: call contract half @llvm.copysign.f16(half %h0, half %h1) res = __builtin_copysignf16(h0, h1); - // CHECK: call half @llvm.fabs.f16(half %h0) + // CHECK: call contract half @llvm.fabs.f16(half %h0) res = __builtin_fabsf16(h0); - // CHECK: call half @llvm.ceil.f16(half %h0) + // CHECK: call contract half @llvm.ceil.f16(half %h0) res = __builtin_ceilf16(h0); - // CHECK: call half @llvm.cos.f16(half %h0) + // CHECK: call contract half @llvm.cos.f16(half %h0) res = __builtin_cosf16(h0); - // CHECK: call half @llvm.exp.f16(half %h0) + // CHECK: call contract half @llvm.exp.f16(half %h0) res = __builtin_expf16(h0); - // CHECK: call half @llvm.exp2.f16(half %h0) + // CHECK: call contract half @llvm.exp2.f16(half %h0) res = __builtin_exp2f16(h0); - // CHECK: call half @llvm.floor.f16(half %h0) + // CHECK: call contract half @llvm.floor.f16(half %h0) res = __builtin_floorf16(h0); - // CHECK: call half @llvm.fma.f16(half %h0, half %h1, half %h2) + // CHECK: call contract half @llvm.fma.f16(half %h0, half %h1, half %h2) res = __builtin_fmaf16(h0, h1 ,h2); - // CHECK: call half @llvm.maxnum.f16(half %h0, half %h1) + // CHECK: call contract half @llvm.maxnum.f16(half %h0, half %h1) res = __builtin_fmaxf16(h0, h1); - // CHECK: call half @llvm.minnum.f16(half %h0, half %h1) + // CHECK: call contract half @llvm.minnum.f16(half %h0, half %h1) res = __builtin_fminf16(h0, h1); - // CHECK: frem half %h0, %h1 + // CHECK: frem contract half %h0, %h1 res = __builtin_fmodf16(h0, h1); - // CHECK: call half @llvm.pow.f16(half %h0, half %h1) + // CHECK: call contract half @llvm.pow.f16(half %h0, half %h1) res = __builtin_powf16(h0, h1); - // CHECK: call half @llvm.log10.f16(half %h0) + // CHECK: call contract half @llvm.log10.f16(half %h0) res = __builtin_log10f16(h0); - // CHECK: call half @llvm.log2.f16(half %h0) + // CHECK: call contract half @llvm.log2.f16(half %h0) res = __builtin_log2f16(h0); - // CHECK: call half @llvm.log.f16(half %h0) + // CHECK: call contract half @llvm.log.f16(half %h0) res = __builtin_logf16(h0); - // CHECK: call half @llvm.rint.f16(half %h0) + // CHECK: call contract half @llvm.rint.f16(half %h0) res = __builtin_rintf16(h0); - // CHECK: call half @llvm.round.f16(half %h0) + // CHECK: call contract half @llvm.round.f16(half %h0) res = __builtin_roundf16(h0); - // CHECK: call half @llvm.sin.f16(half %h0) + // CHECK: call contract half @llvm.sin.f16(half %h0) res = __builtin_sinf16(h0); - // CHECK: call half @llvm.sqrt.f16(half %h0) + // CHECK: call contract half @llvm.sqrt.f16(half %h0) res = __builtin_sqrtf16(h0); - // CHECK: call half @llvm.trunc.f16(half %h0) + // CHECK: call contract half @llvm.trunc.f16(half %h0) res = __builtin_truncf16(h0); - // CHECK: call half @llvm.canonicalize.f16(half %h0) + // CHECK: call contract half @llvm.canonicalize.f16(half %h0) res = __builtin_canonicalizef16(h0); } diff --git a/clang/test/CodeGenOpenCL/builtins-r600.cl b/clang/test/CodeGenOpenCL/builtins-r600.cl --- a/clang/test/CodeGenOpenCL/builtins-r600.cl +++ b/clang/test/CodeGenOpenCL/builtins-r600.cl @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -triple r600-unknown-unknown -target-cpu cypress -S -emit-llvm -o - %s | FileCheck %s // CHECK-LABEL: @test_recipsqrt_ieee_f32 -// CHECK: call float @llvm.r600.recipsqrt.ieee.f32 +// CHECK: call contract float @llvm.r600.recipsqrt.ieee.f32 void test_recipsqrt_ieee_f32(global float* out, float a) { *out = __builtin_r600_recipsqrt_ieeef(a); @@ -10,7 +10,7 @@ #if cl_khr_fp64 // XCHECK-LABEL: @test_recipsqrt_ieee_f64 -// XCHECK: call double @llvm.r600.recipsqrt.ieee.f64 +// XCHECK: call contract double @llvm.r600.recipsqrt.ieee.f64 void test_recipsqrt_ieee_f64(global double* out, double a) { *out = __builtin_r600_recipsqrt_ieee(a); diff --git a/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl b/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl --- a/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl +++ b/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl @@ -10,7 +10,7 @@ // Test that Attr.Pure from OpenCLBuiltins.td is lowered to a readonly attribute. // CHECK-LABEL: @test_pure_attr -// CHECK: call <4 x float> @_Z11read_imagef{{.*}} [[ATTR_PURE:#[0-9]]] +// CHECK: call contract <4 x float> @_Z11read_imagef{{.*}} [[ATTR_PURE:#[0-9]]] // CHECK: ret kernel void test_pure_attr(read_only image1d_t img) { float4 resf = read_imagef(img, 42); diff --git a/clang/test/CodeGenOpenCL/relaxed-fpmath.cl b/clang/test/CodeGenOpenCL/relaxed-fpmath.cl --- a/clang/test/CodeGenOpenCL/relaxed-fpmath.cl +++ b/clang/test/CodeGenOpenCL/relaxed-fpmath.cl @@ -8,12 +8,12 @@ float spscalardiv(float a, float b) { // CHECK: @spscalardiv( - // NORMAL: fdiv float + // NORMAL: fdiv contract float // FAST: fdiv fast float - // FINITE: fdiv nnan ninf float - // UNSAFE: fdiv nnan nsz float - // MAD: fdiv float - // NOSIGNED: fdiv nsz float + // FINITE: fdiv nnan ninf contract float + // UNSAFE: fdiv nnan nsz contract float + // MAD: fdiv contract float + // NOSIGNED: fdiv nsz contract float return a / b; } // CHECK: attributes diff --git a/clang/test/CodeGenOpenCL/single-precision-constant.cl b/clang/test/CodeGenOpenCL/single-precision-constant.cl --- a/clang/test/CodeGenOpenCL/single-precision-constant.cl +++ b/clang/test/CodeGenOpenCL/single-precision-constant.cl @@ -1,6 +1,6 @@ // RUN: %clang_cc1 %s -cl-single-precision-constant -emit-llvm -o - | FileCheck %s float fn(float f) { - // CHECK: tail call float @llvm.fmuladd.f32(float %f, float 2.000000e+00, float 1.000000e+00) + // CHECK: tail call contract float @llvm.fmuladd.f32(float %f, float 2.000000e+00, float 1.000000e+00) return f*2. + 1.; } diff --git a/clang/test/PCH/pragma-floatcontrol.c b/clang/test/PCH/pragma-floatcontrol.c new file mode 100644 --- /dev/null +++ b/clang/test/PCH/pragma-floatcontrol.c @@ -0,0 +1,55 @@ +// Test this without pch. +// RUN: %clang_cc1 %s -include %s -verify -fsyntax-only -DSET +// RUN: %clang_cc1 %s -include %s -verify -fsyntax-only -DPUSH +// RUN: %clang_cc1 %s -include %s -verify -fsyntax-only -DPUSH_POP + +// Test with pch. +// RUN: %clang_cc1 %s -DSET -emit-pch -o %t +// RUN: %clang_cc1 %s -DSET -include-pch %t -emit-llvm -o - | FileCheck --check-prefix=CHECK-EBSTRICT %s +// RUN: %clang_cc1 %s -DPUSH -emit-pch -o %t +// RUN: %clang_cc1 %s -DPUSH -verify -include-pch %t +// RUN: %clang_cc1 %s -DPUSH_POP -emit-pch -o %t +// RUN: %clang_cc1 %s -DPUSH_POP -verify -include-pch %t + +#ifndef HEADER +#define HEADER + +#ifdef SET +#pragma float_control(except, on) +#endif + +#ifdef PUSH +#pragma float_control(precise, on) +#pragma float_control (push) +#pragma float_control(precise, off) +#endif + +#ifdef PUSH_POP +#pragma float_control (precise, on, push) +#pragma float_control (push) +#pragma float_control (pop) +#endif +#else + +#ifdef SET +float fun(float a, float b) { +// CHECK-LABEL: define float @fun{{.*}} +//CHECK-EBSTRICT: llvm.experimental.constrained.fmul{{.*}}tonearest{{.*}}strict +//CHECK-EBSTRICT: llvm.experimental.constrained.fadd{{.*}}tonearest{{.*}}strict + return a*b + 2; +} +#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}} +#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}} +#endif + +#ifdef PUSH +#pragma float_control(pop) +#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}} +#endif + +#ifdef PUSH_POP +#pragma float_control(pop) +#pragma float_control(pop) // expected-warning {{#pragma float_control(pop, ...) failed: stack empty}} +#endif + +#endif //ifndef HEADER 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,64 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -DCHECK_ERROR %s + +float function_scope(float a) { +# pragma float_control(precise, on) junk // expected-warning {{extra tokens at end of '#pragma float_control' - ignored}} + return a; +} + +#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}} +#pragma float_control(except,on,push) // expected-error {{can only appear at file scope}} +#pragma float_control(except,on,push,junk) // expected-error {{float_control is malformed}} + return; +} +#endif + +// RUN: %clang -c -fsyntax-only %s -DDEFAULT -Xclang -verify +// RUN: %clang -c -fsyntax-only %s -ffp-model=precise -DPRECISE -Xclang -verify +// RUN: %clang -c -fsyntax-only %s -ffp-model=strict -DSTRICT -Xclang -verify +// RUN: %clang -c -fsyntax-only %s -ffp-model=fast -DFAST -Xclang -verify +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 // expected-error{{'#pragma STDC FENV_ACCESS ON' is illegal when precise is disabled}} +#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 +#pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when fenv_access is enabled}} +#endif +//RUN -ffp-model=strict +//error: '#pragma float_control(precise, off)' is illegal when except is enabled +//with default, fast or precise: no errors + +#pragma float_control(precise, on) +#pragma float_control(except, on) // OK +#ifndef STRICT +#pragma float_control(except, on) +#pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when except is enabled}} +#endif +int main() +{ +#ifdef STRICT +#pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when except is enabled}} +#else +#pragma float_control(precise, off) // expected-error {{'#pragma float_control(precise, off)' is illegal when except is enabled}} +#endif +#pragma float_control(except, on) +// error: '#pragma float_control(except, on)' is illegal when precise is disabled + double x = b / a; // only used for fp flag setting + if (a == a) // only used for fp flag setting + return 0; //(int)x; +} 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 @@ -244,6 +244,8 @@ /// Get the flags to be applied to created floating point ops FastMathFlags getFastMathFlags() const { return FMF; } + FastMathFlags& getFastMathFlags() { return FMF; } + /// Clear the fast-math flags. void clearFastMathFlags() { FMF.clear(); } @@ -332,10 +334,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; @@ -343,6 +351,9 @@ ~FastMathFlagGuard() { Builder.FMF = FMF; Builder.DefaultFPMathTag = FPMathTag; + Builder.IsFPConstrained = IsFPConstrained; + Builder.DefaultConstrainedExcept = DefaultConstrainedExcept; + Builder.DefaultConstrainedRounding = DefaultConstrainedRounding; } };