diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3197,6 +3197,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 uses precise floating point semantics, effectively +``-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 @@ -2107,26 +2107,48 @@ /// applied to a non-complex value, the former returns its operand and the /// later returns zero in the type of the operand. /// -class UnaryOperator : public Expr { +class UnaryOperator final + : public Expr, + private llvm::TrailingObjects { Stmt *Val; + size_t numTrailingObjects(OverloadToken) const { + return UnaryOperatorBits.HasFPFeatures ? 1 : 0; + } + + FPOptions &getTrailingFPFeatures() { + assert(UnaryOperatorBits.HasFPFeatures); + return *getTrailingObjects(); + } + + const FPOptions &getTrailingFPFeatures() const { + assert(UnaryOperatorBits.HasFPFeatures); + return *getTrailingObjects(); + } + public: typedef UnaryOperatorKind Opcode; - UnaryOperator(Expr *input, Opcode opc, QualType type, ExprValueKind VK, - ExprObjectKind OK, SourceLocation l, bool CanOverflow) - : Expr(UnaryOperatorClass, type, VK, OK), Val(input) { - UnaryOperatorBits.Opc = opc; - UnaryOperatorBits.CanOverflow = CanOverflow; - UnaryOperatorBits.Loc = l; - setDependence(computeDependence(this)); - } +protected: + UnaryOperator(const ASTContext &Ctx, Expr *input, Opcode opc, QualType type, + ExprValueKind VK, ExprObjectKind OK, SourceLocation l, + bool CanOverflow, FPOptions FPFeatures); /// Build an empty unary operator. - explicit UnaryOperator(EmptyShell Empty) : Expr(UnaryOperatorClass, Empty) { + explicit UnaryOperator(bool HasFPFeatures, EmptyShell Empty) + : Expr(UnaryOperatorClass, Empty) { UnaryOperatorBits.Opc = UO_AddrOf; + UnaryOperatorBits.HasFPFeatures = HasFPFeatures; } +public: + static UnaryOperator *CreateEmpty(const ASTContext &C, bool hasFPFeatures); + + static UnaryOperator *Create(const ASTContext &C, Expr *input, Opcode opc, + QualType type, ExprValueKind VK, + ExprObjectKind OK, SourceLocation l, + bool CanOverflow, FPOptions FPFeatures); + Opcode getOpcode() const { return static_cast(UnaryOperatorBits.Opc); } @@ -2148,6 +2170,18 @@ bool canOverflow() const { return UnaryOperatorBits.CanOverflow; } void setCanOverflow(bool C) { UnaryOperatorBits.CanOverflow = C; } + // Get the FP contractability status of this operator. Only meaningful for + // operations on floating point types. + bool isFPContractableWithinStatement(const LangOptions &LO) const { + return getFPFeatures(LO).allowFPContractWithinStatement(); + } + + // Get the FENV_ACCESS status of this operator. Only meaningful for + // operations on floating point types. + bool isFEnvAccessOn(const LangOptions &LO) const { + return getFPFeatures(LO).allowFEnvAccess(); + } + /// isPostfix - Return true if this is a postfix operation, like x++. static bool isPostfix(Opcode Op) { return Op == UO_PostInc || Op == UO_PostDec; @@ -2214,6 +2248,30 @@ const_child_range children() const { return const_child_range(&Val, &Val + 1); } + + /// Is FPFeatures in Trailing Storage? + bool hasStoredFPFeatures() const { return UnaryOperatorBits.HasFPFeatures; } + +protected: + /// Get FPFeatures from trailing storage + FPOptions getStoredFPFeatures() const { return getTrailingFPFeatures(); } + + /// Set FPFeatures in trailing storage, used only by Serialization + void setStoredFPFeatures(FPOptions F) { getTrailingFPFeatures() = F; } + +public: + // Get the FP features status of this operator. Only meaningful for + // operations on floating point types. + FPOptions getFPFeatures(const LangOptions &LO) const { + if (UnaryOperatorBits.HasFPFeatures) + return getStoredFPFeatures(); + return FPOptions::defaultWithoutTrailingStorage(LO); + } + + friend TrailingObjects; + friend class ASTReader; + friend class ASTStmtReader; + friend class ASTStmtWriter; }; /// Helper class for OffsetOfExpr. 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 @@ -427,6 +427,11 @@ unsigned Opc : 5; unsigned CanOverflow : 1; + // + /// This is only meaningful for operations on floating point + /// types when additional values need to be in trailing storage. + /// It is 0 otherwise. + unsigned HasFPFeatures : 1; SourceLocation Loc; }; @@ -610,7 +615,7 @@ unsigned OperatorKind : 6; // Only meaningful for floating point types. - unsigned FPFeatures : 8; + unsigned FPFeatures : 14; }; 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 @@ -1097,9 +1097,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">, @@ -1118,6 +1118,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" @@ -1332,9 +1336,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 @@ -858,6 +858,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 @@ -304,6 +304,8 @@ /// input is a header file (i.e. -x c-header). bool IsHeaderFile = false; + bool denormalIsIEEE = false; + LangOptions(); // Define accessors/mutators for language options of enumeration type. @@ -377,28 +379,33 @@ using RoundingMode = llvm::RoundingMode; public: - FPOptions() : fp_contract(LangOptions::FPC_Off), - fenv_access(LangOptions::FEA_Off), - rounding(LangOptions::FPR_ToNearest), - exceptions(LangOptions::FPE_Ignore) - {} + FPOptions() + : fp_contract(LangOptions::FPC_Off), fenv_access(LangOptions::FEA_Off), + rounding(LangOptions::FPR_ToNearest), + 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(I & 3), - fenv_access((I >> 2) & 1), - rounding ((I >> 3) & 7), - exceptions ((I >> 6) & 3) - {} + explicit FPOptions(unsigned I) { getFromOpaqueInt(I); } explicit FPOptions(const LangOptions &LangOpts) : fp_contract(LangOpts.getDefaultFPContractMode()), fenv_access(LangOptions::FEA_Off), - rounding(LangOptions::FPR_ToNearest), - exceptions(LangOptions::FPE_Ignore) - {} + rounding(static_cast(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; + } + /// Return the default value of FPOptions that's used when trailing /// storage isn't required. static FPOptions defaultWithoutTrailingStorage(const LangOptions &LO); @@ -433,6 +440,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; } RoundingMode getRoundingMode() const { @@ -451,6 +470,22 @@ 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() != RoundingMode::NearestTiesToEven || getExceptionMode() != LangOptions::FPE_Ignore || @@ -460,7 +495,23 @@ /// Used to serialize this. 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(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: @@ -471,6 +522,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 @@ -191,6 +191,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,15 @@ 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 @@ -809,6 +809,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 @@ -182,6 +182,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; @@ -741,6 +742,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 @@ -555,6 +555,9 @@ PragmaStack ConstSegStack; PragmaStack CodeSegStack; + // This stack tracks the current state of Sema.CurFPFeatures. + 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 { @@ -1350,7 +1353,7 @@ /// should not be used elsewhere. void EmitCurrentDiagnostic(unsigned DiagID); - /// Records and restores the FPFeatures state on entry/exit of compound + /// Records and restores the CurFPFeatures state on entry/exit of compound /// statements. class FPFeaturesStateRAII { public: @@ -9566,6 +9569,18 @@ void ActOnPragmaDetectMismatch(SourceLocation Loc, StringRef Name, StringRef Value); + /// Are precise floating point semantics currently enabled? + bool isPreciseFPEnabled() { + return LangOpts.denormalIsIEEE && !CurFPFeatures.allowAssociativeMath() && + !CurFPFeatures.noSignedZeros() && + !CurFPFeatures.allowReciprocalMath() && + !CurFPFeatures.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, @@ -9606,7 +9621,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(llvm::RoundingMode); 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 @@ -686,6 +686,9 @@ /// Record code for the Decls to be checked for deferred diags. DECLS_TO_CHECK_FOR_DEFERRED_DIAGS = 64, + + /// Record code for \#pragma float_control options. + FLOAT_CONTROL_PRAGMA_OPTIONS = 65, }; /// 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 @@ -857,6 +857,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 @@ -505,6 +505,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 @@ -6660,9 +6660,10 @@ if (Err) return std::move(Err); - return new (Importer.getToContext()) UnaryOperator( - ToSubExpr, E->getOpcode(), ToType, E->getValueKind(), E->getObjectKind(), - ToOperatorLoc, E->canOverflow()); + return UnaryOperator::Create( + Importer.getToContext(), ToSubExpr, E->getOpcode(), ToType, + E->getValueKind(), E->getObjectKind(), ToOperatorLoc, E->canOverflow(), + E->getFPFeatures(Importer.getFromContext().getLangOpts())); } ExpectedStmt 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 @@ -1358,7 +1358,8 @@ 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, + /*MinNumArgs=*/0, UsesADL); } CallExpr *CallExpr::CreateEmpty(const ASTContext &Ctx, unsigned NumArgs, @@ -4445,6 +4446,38 @@ CompLHSType, CompResultType); } +UnaryOperator *UnaryOperator::CreateEmpty(const ASTContext &C, + bool hasFPFeatures) { + void *Mem = C.Allocate(totalSizeToAlloc(hasFPFeatures), + alignof(UnaryOperator)); + return new (Mem) UnaryOperator(hasFPFeatures, EmptyShell()); +} + +UnaryOperator::UnaryOperator(const ASTContext &Ctx, Expr *input, Opcode opc, + QualType type, ExprValueKind VK, ExprObjectKind OK, + SourceLocation l, bool CanOverflow, + FPOptions FPFeatures) + : Expr(UnaryOperatorClass, type, VK, OK), Val(input) { + UnaryOperatorBits.Opc = opc; + UnaryOperatorBits.CanOverflow = CanOverflow; + UnaryOperatorBits.Loc = l; + UnaryOperatorBits.HasFPFeatures = + FPFeatures.requiresTrailingStorage(Ctx.getLangOpts()); + setDependence(computeDependence(this)); +} + +UnaryOperator *UnaryOperator::Create(const ASTContext &C, Expr *input, + Opcode opc, QualType type, + ExprValueKind VK, ExprObjectKind OK, + SourceLocation l, bool CanOverflow, + FPOptions FPFeatures) { + bool HasFPFeatures = FPFeatures.requiresTrailingStorage(C.getLangOpts()); + unsigned Size = totalSizeToAlloc(HasFPFeatures); + void *Mem = C.Allocate(Size, alignof(UnaryOperator)); + return new (Mem) + UnaryOperator(C, input, opc, type, VK, OK, l, CanOverflow, FPFeatures); +} + const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) { if (const ExprWithCleanups *ewc = dyn_cast(e)) e = ewc->getSubExpr(); 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 @@ -146,9 +146,10 @@ } UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) { - return new (C) UnaryOperator(const_cast(Arg), UO_Deref, Ty, + return UnaryOperator::Create(C, const_cast(Arg), UO_Deref, Ty, VK_LValue, OK_Ordinary, SourceLocation(), - /*CanOverflow*/ false); + /*CanOverflow*/ false, + FPOptions(C.getLangOpts())); } ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) { @@ -447,15 +448,16 @@ QualType DerefType = Deref->getType(); // Negation predicate. - UnaryOperator *FlagCheck = new (C) UnaryOperator( + UnaryOperator *FlagCheck = UnaryOperator::Create( + C, /* input=*/ M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType), DerefType, CK_IntegralToBoolean), - /* opc=*/ UO_LNot, - /* QualType=*/ C.IntTy, - /* ExprValueKind=*/ VK_RValue, - /* ExprObjectKind=*/ OK_Ordinary, SourceLocation(), - /* CanOverflow*/ false); + /* opc=*/UO_LNot, + /* QualType=*/C.IntTy, + /* ExprValueKind=*/VK_RValue, + /* ExprObjectKind=*/OK_Ordinary, SourceLocation(), + /* CanOverflow*/ false, FPOptions(C.getLangOpts())); // Create assignment. BinaryOperator *FlagAssignment = M.makeAssignment( @@ -518,9 +520,9 @@ // (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); + UnaryOperator::Create(C, M.makeIntegerLiteral(0, C.LongTy), UO_Not, + C.LongTy, VK_RValue, OK_Ordinary, SourceLocation(), + /*CanOverflow*/ false, FPOptions(C.getLangOpts())); BinaryOperator *B = M.makeAssignment( 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 @@ -217,7 +217,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. @@ -230,6 +237,25 @@ return V; } +static void setBuilderFlagsFromFPFeatures(CGBuilderTy &Builder, + CodeGenFunction &CGF, + FPOptions FPFeatures) { + auto NewRoundingBehavior = 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::RoundingMode::NearestTiesToEven)) && + "FPConstrained should be enabled on entire function"); +} + class ScalarExprEmitter : public StmtVisitor { CodeGenFunction &CGF; @@ -744,6 +770,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); } @@ -2333,13 +2362,14 @@ //===----------------------------------------------------------------------===// static BinOpInfo createBinOpInfoFromIncDec(const UnaryOperator *E, - llvm::Value *InVal, bool IsInc) { + llvm::Value *InVal, bool IsInc, + FPOptions FPFeatures) { BinOpInfo BinOp; BinOp.LHS = InVal; 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 = FPFeatures; BinOp.E = E; return BinOp; } @@ -2359,7 +2389,8 @@ case LangOptions::SOB_Trapping: if (!E->canOverflow()) return Builder.CreateNSWAdd(InVal, Amount, Name); - return EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(E, InVal, IsInc)); + return EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec( + E, InVal, IsInc, E->getFPFeatures(CGF.getLangOpts()))); } llvm_unreachable("Unknown SignedOverflowBehaviorTy"); } @@ -2505,8 +2536,8 @@ value = EmitIncDecConsiderOverflowBehavior(E, value, isInc); } else if (E->canOverflow() && type->isUnsignedIntegerType() && CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) { - value = - EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec(E, value, isInc)); + value = EmitOverflowCheckedBinOp(createBinOpInfoFromIncDec( + E, value, isInc, E->getFPFeatures(CGF.getLangOpts()))); } else { llvm::Value *amt = llvm::ConstantInt::get(value->getType(), amount, true); value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec"); @@ -2706,7 +2737,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(CGF.getLangOpts()); BinOp.E = E; return EmitSub(BinOp); } @@ -2723,9 +2754,12 @@ 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(CGF.getLangOpts())); 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"); } @@ -3134,7 +3168,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 @@ -3506,6 +3543,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; @@ -3688,6 +3727,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; @@ -4014,6 +4055,8 @@ if (BOInfo.isFixedPointOp()) { 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 @@ -4166,6 +4209,9 @@ 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(CGF.getLangOpts())); LHS = Builder.CreateFCmp(llvm::CmpInst::FCMP_UNE, LHS, Zero, "cmp"); RHS = Builder.CreateFCmp(llvm::CmpInst::FCMP_UNE, RHS, Zero, "cmp"); } else { @@ -4250,6 +4296,9 @@ 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(CGF.getLangOpts())); 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 @@ -3556,17 +3556,17 @@ StartFunction(FD, ReturnTy, Fn, FI, args); - DeclRefExpr DstExpr(getContext(), &DstDecl, false, DestTy, VK_RValue, - SourceLocation()); - UnaryOperator DST(&DstExpr, UO_Deref, DestTy->getPointeeType(), - VK_LValue, OK_Ordinary, SourceLocation(), false); + DeclRefExpr DstExpr(C, &DstDecl, false, DestTy, VK_RValue, SourceLocation()); + UnaryOperator *DST = UnaryOperator::Create( + C, &DstExpr, UO_Deref, DestTy->getPointeeType(), VK_LValue, OK_Ordinary, + SourceLocation(), false, FPOptions(C.getLangOpts())); - DeclRefExpr SrcExpr(getContext(), &SrcDecl, false, SrcTy, VK_RValue, - SourceLocation()); - UnaryOperator SRC(&SrcExpr, UO_Deref, SrcTy->getPointeeType(), - VK_LValue, OK_Ordinary, SourceLocation(), false); + DeclRefExpr SrcExpr(C, &SrcDecl, false, SrcTy, VK_RValue, SourceLocation()); + UnaryOperator *SRC = UnaryOperator::Create( + C, &SrcExpr, UO_Deref, SrcTy->getPointeeType(), VK_LValue, OK_Ordinary, + SourceLocation(), false, FPOptions(C.getLangOpts())); - Expr *Args[2] = { &DST, &SRC }; + Expr *Args[2] = {DST, SRC}; CallExpr *CalleeExp = cast(PID->getSetterCXXAssignment()); CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create( C, OO_Equal, CalleeExp->getCallee(), Args, DestTy->getPointeeType(), @@ -3642,14 +3642,15 @@ DeclRefExpr SrcExpr(getContext(), &SrcDecl, false, SrcTy, VK_RValue, SourceLocation()); - UnaryOperator SRC(&SrcExpr, UO_Deref, SrcTy->getPointeeType(), - VK_LValue, OK_Ordinary, SourceLocation(), false); + UnaryOperator *SRC = UnaryOperator::Create( + C, &SrcExpr, UO_Deref, SrcTy->getPointeeType(), VK_LValue, OK_Ordinary, + SourceLocation(), false, FPOptions(C.getLangOpts())); CXXConstructExpr *CXXConstExpr = cast(PID->getGetterCXXConstructor()); SmallVector ConstructorArgs; - ConstructorArgs.push_back(&SRC); + ConstructorArgs.push_back(SRC); ConstructorArgs.append(std::next(CXXConstExpr->arg_begin()), CXXConstExpr->arg_end()); 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 @@ -2934,8 +2934,9 @@ C, &IVRefExpr, &UBRefExpr, BO_LE, C.BoolTy, VK_RValue, OK_Ordinary, S.getBeginLoc(), FPOptions(C.getLangOpts())); // Increment for loop counter. - UnaryOperator Inc(&IVRefExpr, UO_PreInc, KmpInt32Ty, VK_RValue, OK_Ordinary, - S.getBeginLoc(), true); + UnaryOperator *Inc = UnaryOperator::Create( + C, &IVRefExpr, UO_PreInc, KmpInt32Ty, VK_RValue, OK_Ordinary, + S.getBeginLoc(), true, FPOptions(C.getLangOpts())); auto &&BodyGen = [CapturedStmt, CS, &S, &IV](CodeGenFunction &CGF) { // Iterate through all sections and emit a switch construct: // switch (IV) { @@ -3005,7 +3006,7 @@ // IV = LB; CGF.EmitStoreOfScalar(CGF.EmitLoadOfScalar(LB, S.getBeginLoc()), IV); // while (idx <= UB) { BODY; ++idx; } - CGF.EmitOMPInnerLoop(S, /*RequiresCleanup=*/false, Cond, &Inc, BodyGen, + CGF.EmitOMPInnerLoop(S, /*RequiresCleanup=*/false, Cond, Inc, BodyGen, [](CodeGenFunction &) {}); // Tell the runtime we are done. auto &&CodeGen = [&S](CodeGenFunction &CGF) { 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 @@ -4575,6 +4575,11 @@ } } // end namespace CodeGen + +// 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,8 +117,8 @@ // Map the LangOption for exception behavior into // the corresponding enum in the IR. -static llvm::fp::ExceptionBehavior ToConstrainedExceptMD( - LangOptions::FPExceptionModeKind Kind) { +llvm::fp::ExceptionBehavior +clang::ToConstrainedExceptMD(LangOptions::FPExceptionModeKind Kind) { switch (Kind) { case LangOptions::FPE_Ignore: return llvm::fp::ebIgnore; @@ -133,15 +133,10 @@ auto fpExceptionBehavior = ToConstrainedExceptMD( getLangOpts().getFPExceptionMode()); - if (fpExceptionBehavior == llvm::fp::ebIgnore && - RM == llvm::RoundingMode::NearestTiesToEven) - // Constrained intrinsics are not used. - ; - else { - Builder.setIsFPConstrained(true); - Builder.setDefaultConstrainedRounding(RM); - Builder.setDefaultConstrainedExcept(fpExceptionBehavior); - } + Builder.setDefaultConstrainedRounding(RM); + Builder.setDefaultConstrainedExcept(fpExceptionBehavior); + Builder.setIsFPConstrained(fpExceptionBehavior != llvm::fp::ebIgnore || + RM != llvm::RoundingMode::NearestTiesToEven); } CharUnits CodeGenFunction::getNaturalPointeeTypeAlignment(QualType T, @@ -919,9 +914,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 @@ -2455,7 +2455,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, const TargetOptions &TargetOpts, - PreprocessorOptions &PPOpts, + PreprocessorOptions &PPOpts, CodeGenOptions &CGOpts, DiagnosticsEngine &Diags) { // FIXME: Cleanup per-file based stuff. LangStandard::Kind LangStd = LangStandard::lang_unspecified; @@ -3187,6 +3187,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(); @@ -3640,7 +3653,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 @@ -2587,9 +2587,10 @@ strType, nullptr, SC_Static); DeclRefExpr *DRE = new (Context) 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); + Expr *Unop = UnaryOperator::Create( + const_cast(*Context), DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); // cast to NSConstantString * CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), CK_CPointerToObjCPointerCast, Unop); @@ -3283,10 +3284,10 @@ // we need the cast below. For example: // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + SuperRep = UnaryOperator::Create( + const_cast(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -3301,10 +3302,10 @@ superType, VK_LValue, ILE, false); // struct __rw_objc_super * - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + SuperRep = UnaryOperator::Create( + const_cast(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); } MsgExprs.push_back(SuperRep); break; @@ -3378,10 +3379,10 @@ // we need the cast below. For example: // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + SuperRep = UnaryOperator::Create( + const_cast(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -4705,9 +4706,10 @@ if (VarDecl *Var = dyn_cast(VD)) if (!ImportedLocalExternalDecls.count(Var)) return DRE; - Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), - VK_LValue, OK_Ordinary, - DRE->getLocation(), false); + Expr *Exp = UnaryOperator::Create(const_cast(*Context), DRE, + UO_Deref, DRE->getType(), VK_LValue, + OK_Ordinary, DRE->getLocation(), false, + FPOptions(Context->getLangOpts())); // Need parens to enforce precedence. ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), Exp); @@ -5293,11 +5295,12 @@ VarDecl *NewVD = VarDecl::Create( *Context, TUDecl, SourceLocation(), SourceLocation(), &Context->Idents.get(DescData), Context->VoidPtrTy, nullptr, SC_Static); - UnaryOperator *DescRefExpr = new (Context) UnaryOperator( + UnaryOperator *DescRefExpr = UnaryOperator::Create( + const_cast(*Context), 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(Context->getLangOpts())); InitExprs.push_back(DescRefExpr); // Add initializers for any closure decl refs. @@ -5314,9 +5317,10 @@ if (HasLocalVariableExternalStorage(*I)) { QualType QT = (*I)->getType(); QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation(), - false); + Exp = UnaryOperator::Create(const_cast(*Context), Exp, + UO_AddrOf, QT, VK_RValue, OK_Ordinary, + SourceLocation(), false, + FPOptions(Context->getLangOpts())); } } else if (isTopLevelBlockPointerType((*I)->getType())) { FD = SynthBlockInitFunctionDecl((*I)->getName()); @@ -5331,9 +5335,10 @@ if (HasLocalVariableExternalStorage(*I)) { QualType QT = (*I)->getType(); QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation(), - false); + Exp = UnaryOperator::Create(const_cast(*Context), Exp, + UO_AddrOf, QT, VK_RValue, OK_Ordinary, + SourceLocation(), false, + FPOptions(Context->getLangOpts())); } } @@ -5371,10 +5376,10 @@ // captured nested byref variable has its address passed. Do not take // its address again. if (!isNestedCapturedVar) - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, - Context->getPointerType(Exp->getType()), - VK_RValue, OK_Ordinary, SourceLocation(), - false); + Exp = UnaryOperator::Create( + const_cast(*Context), Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); InitExprs.push_back(Exp); } @@ -5398,9 +5403,10 @@ NewRep = DRE; } - NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, - Context->getPointerType(NewRep->getType()), - VK_RValue, OK_Ordinary, SourceLocation(), false); + NewRep = UnaryOperator::Create( + const_cast(*Context), NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, NewRep); // Put Paren around the call. @@ -7539,10 +7545,10 @@ CK_BitCast, PE); - - Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT, - VK_LValue, OK_Ordinary, - SourceLocation(), false); + Expr *Exp = UnaryOperator::Create(const_cast(*Context), + castExpr, UO_Deref, IvarT, VK_LValue, + OK_Ordinary, SourceLocation(), false, + FPOptions(Context->getLangOpts())); 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 @@ -2514,9 +2514,10 @@ strType, nullptr, SC_Static); DeclRefExpr *DRE = new (Context) 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); + Expr *Unop = UnaryOperator::Create( + const_cast(*Context), DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); // cast to NSConstantString * CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), CK_CPointerToObjCPointerCast, Unop); @@ -2714,10 +2715,10 @@ // we need the cast below. For example: // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + SuperRep = UnaryOperator::Create( + const_cast(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -2732,10 +2733,10 @@ superType, VK_LValue, ILE, false); // struct objc_super * - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + SuperRep = UnaryOperator::Create( + const_cast(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); } MsgExprs.push_back(SuperRep); break; @@ -2809,10 +2810,10 @@ // we need the cast below. For example: // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation(), false); + SuperRep = UnaryOperator::Create( + const_cast(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); SuperRep = NoTypeInfoCStyleCastExpr(Context, Context->getPointerType(superType), CK_BitCast, SuperRep); @@ -3048,9 +3049,10 @@ nullptr, SC_Extern); 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); + Expr *DerefExpr = UnaryOperator::Create( + const_cast(*Context), DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), CK_BitCast, DerefExpr); @@ -3875,9 +3877,10 @@ if (VarDecl *Var = dyn_cast(VD)) if (!ImportedLocalExternalDecls.count(Var)) return DRE; - Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), - VK_LValue, OK_Ordinary, - DRE->getLocation(), false); + Expr *Exp = UnaryOperator::Create(const_cast(*Context), DRE, + UO_Deref, DRE->getType(), VK_LValue, + OK_Ordinary, DRE->getLocation(), false, + FPOptions(Context->getLangOpts())); // Need parens to enforce precedence. ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), Exp); @@ -4432,11 +4435,12 @@ VarDecl *NewVD = VarDecl::Create( *Context, TUDecl, SourceLocation(), SourceLocation(), &Context->Idents.get(DescData), Context->VoidPtrTy, nullptr, SC_Static); - UnaryOperator *DescRefExpr = new (Context) UnaryOperator( + UnaryOperator *DescRefExpr = UnaryOperator::Create( + const_cast(*Context), 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(Context->getLangOpts())); InitExprs.push_back(DescRefExpr); // Add initializers for any closure decl refs. @@ -4453,9 +4457,10 @@ if (HasLocalVariableExternalStorage(*I)) { QualType QT = (*I)->getType(); QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation(), - false); + Exp = UnaryOperator::Create(const_cast(*Context), Exp, + UO_AddrOf, QT, VK_RValue, OK_Ordinary, + SourceLocation(), false, + FPOptions(Context->getLangOpts())); } } else if (isTopLevelBlockPointerType((*I)->getType())) { FD = SynthBlockInitFunctionDecl((*I)->getName()); @@ -4470,9 +4475,10 @@ if (HasLocalVariableExternalStorage(*I)) { QualType QT = (*I)->getType(); QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation(), - false); + Exp = UnaryOperator::Create(const_cast(*Context), Exp, + UO_AddrOf, QT, VK_RValue, OK_Ordinary, + SourceLocation(), false, + FPOptions(Context->getLangOpts())); } } InitExprs.push_back(Exp); @@ -4509,9 +4515,10 @@ // captured nested byref variable has its address passed. Do not take // its address again. if (!isNestedCapturedVar) - Exp = new (Context) UnaryOperator( - Exp, UO_AddrOf, Context->getPointerType(Exp->getType()), VK_RValue, - OK_Ordinary, SourceLocation(), false); + Exp = UnaryOperator::Create( + const_cast(*Context), Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); InitExprs.push_back(Exp); } @@ -4527,9 +4534,10 @@ } NewRep = CallExpr::Create(*Context, DRE, InitExprs, FType, VK_LValue, SourceLocation()); - NewRep = new (Context) UnaryOperator( - NewRep, UO_AddrOf, Context->getPointerType(NewRep->getType()), VK_RValue, - OK_Ordinary, SourceLocation(), false); + NewRep = UnaryOperator::Create( + const_cast(*Context), NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), VK_RValue, OK_Ordinary, + SourceLocation(), false, FPOptions(Context->getLangOpts())); 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 @@ -3365,6 +3365,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.CurFPFeatures.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,8 @@ PP.AddPragmaHandler(MSCommentHandler.get()); } + FloatControlHandler = std::make_unique(Actions); + PP.AddPragmaHandler(FloatControlHandler.get()); if (getLangOpts().MicrosoftExt) { MSDetectMismatchHandler = std::make_unique(Actions); @@ -438,6 +450,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 +660,22 @@ ConsumeAnnotationToken(); } +void Parser::HandlePragmaFloatControl() { + assert(Tok.is(tok::annot_pragma_float_control)); + + // The value that is held on the PragmaFloatControlStack encodes + // the PragmaFloatControl kind and the MSStackAction kind + // into a single 32-bit word. The MsStackAction is the high 16 bits + // and the FloatControl is the lower 16 bits. Use shift and bit-and + // to decode the parts. + 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 +695,8 @@ break; } - Actions.ActOnPragmaFEnvAccess(FPC); - ConsumeAnnotationToken(); + SourceLocation PragmaLoc = ConsumeAnnotationToken(); + Actions.ActOnPragmaFEnvAccess(PragmaLoc, FPC); } @@ -2489,6 +2519,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); + // Create an encoding of Action and Value by shifting the Action into + // the high 16 bits then union with the Kind. + 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: @@ -2749,7 +2902,7 @@ auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue{*FlagKind, *FlagValue}; - // Generate the loop hint token. + // Generate the fp annotation token. Token FPTok; FPTok.startToken(); FPTok.setKind(tok::annot_pragma_fp); 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 @@ -354,13 +354,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(); @@ -369,6 +369,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(); @@ -937,6 +943,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 @@ -752,6 +752,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 @@ -159,7 +159,8 @@ LangOpts.getMSPointerToMemberRepresentationMethod()), VtorDispStack(LangOpts.getVtorDispMode()), PackStack(0), DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr), - CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr), + CodeSegStack(nullptr), FpPragmaStack(CurFPFeatures.getAsOpaqueInt()), + CurInitSeg(nullptr), VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr), IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp), diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -407,6 +407,65 @@ Consumer.HandleTopLevelDecl(DeclGroupRef(PDMD)); } +void Sema::ActOnPragmaFloatControl(SourceLocation Loc, + PragmaMsStackAction Action, + PragmaFloatControlKind Value) { + auto NewValue = FpPragmaStack.CurrentValue; + FPOptions NewFPFeatures(NewValue); + 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: + CurFPFeatures.setFPPreciseEnabled(true); + NewValue = CurFPFeatures.getAsOpaqueInt(); + FpPragmaStack.Act(Loc, Action, StringRef(), NewValue); + break; + case PFC_NoPrecise: + if (CurFPFeatures.getExceptionMode() == LangOptions::FPE_Strict) + Diag(Loc, diag::err_pragma_fc_noprecise_requires_noexcept); + else if (CurFPFeatures.allowFEnvAccess()) + Diag(Loc, diag::err_pragma_fc_noprecise_requires_nofenv); + else + CurFPFeatures.setFPPreciseEnabled(false); + NewValue = CurFPFeatures.getAsOpaqueInt(); + FpPragmaStack.Act(Loc, Action, StringRef(), NewValue); + break; + case PFC_Except: + if (!isPreciseFPEnabled()) + Diag(Loc, diag::err_pragma_fc_except_requires_precise); + else + CurFPFeatures.setExceptionMode(LangOptions::FPE_Strict); + NewValue = CurFPFeatures.getAsOpaqueInt(); + FpPragmaStack.Act(Loc, Action, StringRef(), NewValue); + break; + case PFC_NoExcept: + CurFPFeatures.setExceptionMode(LangOptions::FPE_Ignore); + NewValue = CurFPFeatures.getAsOpaqueInt(); + FpPragmaStack.Act(Loc, Action, StringRef(), NewValue); + break; + case PFC_Push: + Action = Sema::PSK_Push_Set; + FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures.getAsOpaqueInt()); + break; + case PFC_Pop: + if (FpPragmaStack.Stack.empty()) { + Diag(Loc, diag::warn_pragma_pop_failed) << "float_control" + << "stack empty"; + return; + } + FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures.getAsOpaqueInt()); + NewValue = FpPragmaStack.CurrentValue; + CurFPFeatures.getFromOpaqueInt(NewValue); + break; + } +} + void Sema::ActOnPragmaMSPointersToMembers( LangOptions::PragmaMSPointersToMembersKind RepresentationMethod, SourceLocation PragmaLoc) { @@ -948,9 +1007,16 @@ CurFPFeatures.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); CurFPFeatures.setAllowFEnvAccess(); break; case LangOptions::FEA_Off: @@ -959,7 +1025,6 @@ } } - void Sema::PushNamespaceVisibilityAttr(const VisibilityAttr *Attr, SourceLocation Loc) { // Visibility calculations will consider the namespace's visibility. 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 @@ -13455,13 +13455,13 @@ // directly construct UnaryOperators here because semantic analysis // does not permit us to take the address of an xvalue. 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); + From = UnaryOperator::Create( + S.Context, From, UO_AddrOf, S.Context.getPointerType(From->getType()), + VK_RValue, OK_Ordinary, Loc, false, S.CurFPFeatures); 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); + To = UnaryOperator::Create(S.Context, To, UO_AddrOf, + S.Context.getPointerType(To->getType()), VK_RValue, + OK_Ordinary, Loc, false, S.CurFPFeatures); const Type *E = T->getBaseElementTypeUnsafe(); bool NeedsCollectableMemCpy = @@ -13703,9 +13703,9 @@ // Create the pre-increment of the iteration variable. We can determine // whether the increment will overflow based on the value of the array // bound. - Expr *Increment = new (S.Context) - UnaryOperator(IterationVarRef.build(S, Loc), UO_PreInc, SizeType, - VK_LValue, OK_Ordinary, Loc, Upper.isMaxValue()); + Expr *Increment = UnaryOperator::Create( + S.Context, IterationVarRef.build(S, Loc), UO_PreInc, SizeType, VK_LValue, + OK_Ordinary, Loc, Upper.isMaxValue(), S.CurFPFeatures); // 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 @@ -13662,14 +13662,6 @@ if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) return ExprError(); - if (ResultTy->isRealFloatingType() && - (getLangOpts().getFPRoundingMode() != RoundingMode::NearestTiesToEven || - 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 @@ -14319,8 +14311,8 @@ if (Opc != UO_AddrOf && Opc != UO_Deref) CheckArrayAccess(Input.get()); - auto *UO = new (Context) - UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow); + auto *UO = UnaryOperator::Create(Context, Input.get(), Opc, resultType, VK, + OK, OpLoc, CanOverflow, CurFPFeatures); if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && !isa(UO->getType().getDesugaredType(Context))) 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 @@ -4427,9 +4427,9 @@ } else if (UnaryOperator *uo = dyn_cast(e)) { assert(uo->getOpcode() == UO_Extension); Expr *sub = stripARCUnbridgedCast(uo->getSubExpr()); - return new (Context) - UnaryOperator(sub, UO_Extension, sub->getType(), sub->getValueKind(), - sub->getObjectKind(), uo->getOperatorLoc(), false); + return UnaryOperator::Create(Context, sub, UO_Extension, sub->getType(), + sub->getValueKind(), sub->getObjectKind(), + uo->getOperatorLoc(), false, CurFPFeatures); } else if (GenericSelectionExpr *gse = dyn_cast(e)) { assert(!gse->isResultDependent()); 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 @@ -13001,8 +13001,9 @@ if (Input->isTypeDependent()) { if (Fns.empty()) - return new (Context) UnaryOperator(Input, Opc, Context.DependentTy, - VK_RValue, OK_Ordinary, OpLoc, false); + return UnaryOperator::Create(Context, Input, Opc, Context.DependentTy, + VK_RValue, OK_Ordinary, OpLoc, false, + CurFPFeatures); CXXRecordDecl *NamingClass = nullptr; // lookup ignores member operators UnresolvedLookupExpr *Fn = UnresolvedLookupExpr::Create( @@ -14802,9 +14803,9 @@ if (Context.getTargetInfo().getCXXABI().isMicrosoft()) (void)isCompleteType(UnOp->getOperatorLoc(), MemPtrType); - return new (Context) UnaryOperator(SubExpr, UO_AddrOf, MemPtrType, - VK_RValue, OK_Ordinary, - UnOp->getOperatorLoc(), false); + return UnaryOperator::Create( + Context, SubExpr, UO_AddrOf, MemPtrType, VK_RValue, OK_Ordinary, + UnOp->getOperatorLoc(), false, CurFPFeatures); } } Expr *SubExpr = FixOverloadedFunctionReference(UnOp->getSubExpr(), @@ -14812,10 +14813,9 @@ if (SubExpr == UnOp->getSubExpr()) return UnOp; - return new (Context) UnaryOperator(SubExpr, UO_AddrOf, - Context.getPointerType(SubExpr->getType()), - VK_RValue, OK_Ordinary, - UnOp->getOperatorLoc(), false); + return UnaryOperator::Create( + Context, SubExpr, UO_AddrOf, Context.getPointerType(SubExpr->getType()), + VK_RValue, OK_Ordinary, UnOp->getOperatorLoc(), false, CurFPFeatures); } 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 @@ -127,12 +127,10 @@ if (UnaryOperator *uop = dyn_cast(e)) { assert(uop->getOpcode() == UO_Extension); e = rebuild(uop->getSubExpr()); - return new (S.Context) UnaryOperator(e, uop->getOpcode(), - uop->getType(), - uop->getValueKind(), - uop->getObjectKind(), - uop->getOperatorLoc(), - uop->canOverflow()); + return UnaryOperator::Create( + S.Context, e, uop->getOpcode(), uop->getType(), uop->getValueKind(), + uop->getObjectKind(), uop->getOperatorLoc(), uop->canOverflow(), + S.CurFPFeatures); } if (GenericSelectionExpr *gse = dyn_cast(e)) { @@ -526,12 +524,14 @@ (result.get()->isTypeDependent() || CanCaptureValue(result.get()))) setResultToLastSemantic(); - UnaryOperator *syntactic = new (S.Context) UnaryOperator( - syntacticOp, opcode, resultType, VK_LValue, OK_Ordinary, opcLoc, - !resultType->isDependentType() - ? S.Context.getTypeSize(resultType) >= - S.Context.getTypeSize(S.Context.IntTy) - : false); + UnaryOperator *syntactic = + UnaryOperator::Create(S.Context, syntacticOp, opcode, resultType, + VK_LValue, OK_Ordinary, opcLoc, + !resultType->isDependentType() + ? S.Context.getTypeSize(resultType) >= + S.Context.getTypeSize(S.Context.IntTy) + : false, + S.CurFPFeatures); return complete(syntactic); } @@ -1551,8 +1551,9 @@ UnaryOperatorKind opcode, Expr *op) { // Do nothing if the operand is dependent. if (op->isTypeDependent()) - return new (Context) UnaryOperator(op, opcode, Context.DependentTy, - VK_RValue, OK_Ordinary, opcLoc, false); + return UnaryOperator::Create(Context, op, opcode, Context.DependentTy, + VK_RValue, OK_Ordinary, opcLoc, false, + CurFPFeatures); assert(UnaryOperator::isIncrementDecrementOp(opcode)); Expr *opaqueRef = op->IgnoreParens(); @@ -1636,9 +1637,10 @@ Expr *syntax = E->getSyntacticForm(); if (UnaryOperator *uop = dyn_cast(syntax)) { Expr *op = stripOpaqueValuesFromPseudoObjectRef(*this, uop->getSubExpr()); - return new (Context) UnaryOperator( - op, uop->getOpcode(), uop->getType(), uop->getValueKind(), - uop->getObjectKind(), uop->getOperatorLoc(), uop->canOverflow()); + return UnaryOperator::Create(Context, op, uop->getOpcode(), uop->getType(), + uop->getValueKind(), uop->getObjectKind(), + uop->getOperatorLoc(), uop->canOverflow(), + CurFPFeatures); } 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 @@ -394,6 +394,11 @@ ArrayRef Elts, bool isStmtExpr) { const unsigned NumElts = Elts.size(); + // Mark the current function as usng floating point constrained intrinsics + if (getCurFPFeatures().isFPConstrained()) + if (FunctionDecl *F = dyn_cast(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/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3779,6 +3779,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; + } + case DECLS_TO_CHECK_FOR_DEFERRED_DIAGS: for (unsigned I = 0, N = Record.size(); I != N; ++I) DeclsToCheckForDeferredDiags.push_back(getGlobalDeclID(F, Record[I])); @@ -7853,6 +7876,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 @@ -682,10 +682,14 @@ void ASTStmtReader::VisitUnaryOperator(UnaryOperator *E) { VisitExpr(E); + bool hasFP_Features = Record.readInt(); + assert(hasFP_Features == E->hasStoredFPFeatures()); E->setSubExpr(Record.readSubExpr()); E->setOpcode((UnaryOperator::Opcode)Record.readInt()); E->setOperatorLoc(readSourceLocation()); E->setCanOverflow(Record.readInt()); + if (hasFP_Features) + E->setStoredFPFeatures(FPOptions(Record.readInt())); } void ASTStmtReader::VisitOffsetOfExpr(OffsetOfExpr *E) { @@ -2889,7 +2893,8 @@ break; case EXPR_UNARY_OPERATOR: - S = new (Context) UnaryOperator(Empty); + S = UnaryOperator::CreateEmpty(Context, + Record[ASTStmtReader::NumExprFields]); break; case EXPR_OFFSETOF: 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 @@ -4139,6 +4139,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 float_control 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. @@ -4867,6 +4887,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 @@ -702,10 +702,16 @@ void ASTStmtWriter::VisitUnaryOperator(UnaryOperator *E) { VisitExpr(E); + bool HasFPFeatures = E->hasStoredFPFeatures(); + // Write this first for easy access when deserializing, as they affect the + // size of the UnaryOperator. + Record.push_back(HasFPFeatures); Record.AddStmt(E->getSubExpr()); Record.push_back(E->getOpcode()); // FIXME: stable encoding Record.AddSourceLocation(E->getOperatorLoc()); Record.push_back(E->canOverflow()); + if (HasFPFeatures) + Record.push_back(E->getStoredFPFeatures().getAsOpaqueInt()); Code = serialization::EXPR_UNARY_OPERATOR; } diff --git a/clang/test/CodeGen/builtins-nvptx.c b/clang/test/CodeGen/builtins-nvptx.c --- a/clang/test/CodeGen/builtins-nvptx.c +++ b/clang/test/CodeGen/builtins-nvptx.c @@ -1,11 +1,11 @@ // REQUIRES: nvptx-registered-target -// RUN: %clang_cc1 -triple nvptx-unknown-unknown -target-cpu sm_60 \ +// RUN: %clang_cc1 -ffp-contract=off -triple nvptx-unknown-unknown -target-cpu sm_60 \ // RUN: -fcuda-is-device -S -emit-llvm -o - -x cuda %s \ // RUN: | FileCheck -check-prefix=CHECK -check-prefix=LP32 %s -// RUN: %clang_cc1 -triple nvptx64-unknown-unknown -target-cpu sm_60 \ +// RUN: %clang_cc1 -ffp-contract=off -triple nvptx64-unknown-unknown -target-cpu sm_60 \ // RUN: -fcuda-is-device -S -emit-llvm -o - -x cuda %s \ // RUN: | FileCheck -check-prefix=CHECK -check-prefix=LP64 %s -// RUN: %clang_cc1 -triple nvptx64-unknown-unknown -target-cpu sm_61 \ +// RUN: %clang_cc1 -ffp-contract=off -triple nvptx64-unknown-unknown -target-cpu sm_61 \ // RUN: -fcuda-is-device -S -emit-llvm -o - -x cuda %s \ // RUN: | FileCheck -check-prefix=CHECK -check-prefix=LP64 %s // RUN: %clang_cc1 -triple nvptx-unknown-unknown -target-cpu sm_53 \ 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: fneg -// CHECK: call double @llvm.experimental.constrained.fmuladd.f64 -// CHECK: fneg -// CHECK: call x86_fp80 @llvm.experimental.constrained.fmuladd.f80 + // CHECK: call contract float @llvm.experimental.constrained.fmuladd.f32 + // CHECK: fneg + // CHECK: call contract double @llvm.experimental.constrained.fmuladd.f64 + // CHECK: fneg + // 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 @@ -2,9 +2,9 @@ // 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 - #pragma STDC FP_CONTRACT ON + // CHECK: _Z13fp_contract_1fff + // CHECK: tail call contract float @llvm.fmuladd +#pragma STDC FP_CONTRACT ON return a * b + c; } @@ -30,8 +30,8 @@ } float fp_contract_3(float a, float b, float c) { -// CHECK: _Z13fp_contract_3fff -// CHECK: tail call float @llvm.fmuladd + // CHECK: _Z13fp_contract_3fff + // 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: _Z13fp_contract_5fff + // CHECK: tail call contract float @llvm.fmuladd return a * b + c; } @@ -67,25 +67,25 @@ // https://llvm.org/bugs/show_bug.cgi?id=25719 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 - #pragma STDC FP_CONTRACT ON + // CHECK: _Z13fp_contract_7fff + // 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 - #pragma STDC FP_CONTRACT ON + // CHECK: _Z13fp_contract_8fff + // 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 - #pragma STDC FP_CONTRACT ON + // CHECK: _Z13fp_contract_9fff + // 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,253 @@ +// 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 @@ -20,16 +20,17 @@ // CHECK-LABEL: define {{.*}}void @_ZN4aaaaIiED2Ev{{.*}} } catch (...) { - // MAYTRAP: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.maytrap") - // EXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") - // FPMODELSTRICT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") - // STRICTEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") - // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") - // PRECISE: fadd contract float %{{.*}}, %{{.*}} - // FAST: fadd fast - f0 = f1 + f2; + // MAYTRAP: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.maytrap") + // EXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // FPMODELSTRICT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICTEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // 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 + // CHECK: ret void } } 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/Headers/nvptx_device_math_sin.c b/clang/test/Headers/nvptx_device_math_sin.c --- a/clang/test/Headers/nvptx_device_math_sin.c +++ b/clang/test/Headers/nvptx_device_math_sin.c @@ -1,8 +1,8 @@ // REQUIRES: nvptx-registered-target // RUN: %clang_cc1 -x c -internal-isystem %S/Inputs/include -fopenmp -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc // RUN: %clang_cc1 -x c -include __clang_openmp_device_functions.h -internal-isystem %S/../../lib/Headers/openmp_wrappers -internal-isystem %S/Inputs/include -fopenmp -triple nvptx64-nvidia-cuda -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - | FileCheck %s --check-prefix=SLOW -// RUN: %clang_cc1 -x c -internal-isystem %S/Inputs/include -fopenmp -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc -ffast-math -// RUN: %clang_cc1 -x c -include __clang_openmp_device_functions.h -internal-isystem %S/../../lib/Headers/openmp_wrappers -internal-isystem %S/Inputs/include -fopenmp -triple nvptx64-nvidia-cuda -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -ffast-math | FileCheck %s --check-prefix=FAST +// RUN: %clang_cc1 -x c -internal-isystem %S/Inputs/include -fopenmp -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc -ffast-math -ffp-contract=fast +// RUN: %clang_cc1 -x c -include __clang_openmp_device_functions.h -internal-isystem %S/../../lib/Headers/openmp_wrappers -internal-isystem %S/Inputs/include -fopenmp -triple nvptx64-nvidia-cuda -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -ffast-math -ffp-contract=fast | FileCheck %s --check-prefix=FAST // expected-no-diagnostics #include diff --git a/clang/test/Headers/nvptx_device_math_sin.cpp b/clang/test/Headers/nvptx_device_math_sin.cpp --- a/clang/test/Headers/nvptx_device_math_sin.cpp +++ b/clang/test/Headers/nvptx_device_math_sin.cpp @@ -1,8 +1,8 @@ // REQUIRES: nvptx-registered-target // RUN: %clang_cc1 -x c++ -internal-isystem %S/Inputs/include -fopenmp -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc // RUN: %clang_cc1 -x c++ -include __clang_openmp_device_functions.h -internal-isystem %S/../../lib/Headers/openmp_wrappers -internal-isystem %S/Inputs/include -fopenmp -triple nvptx64-nvidia-cuda -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - | FileCheck %s --check-prefix=SLOW -// RUN: %clang_cc1 -x c++ -internal-isystem %S/Inputs/include -fopenmp -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc -ffast-math -// RUN: %clang_cc1 -x c++ -include __clang_openmp_device_functions.h -internal-isystem %S/../../lib/Headers/openmp_wrappers -internal-isystem %S/Inputs/include -fopenmp -triple nvptx64-nvidia-cuda -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -ffast-math | FileCheck %s --check-prefix=FAST +// RUN: %clang_cc1 -x c++ -internal-isystem %S/Inputs/include -fopenmp -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc -ffast-math -ffp-contract=fast +// RUN: %clang_cc1 -x c++ -include __clang_openmp_device_functions.h -internal-isystem %S/../../lib/Headers/openmp_wrappers -internal-isystem %S/Inputs/include -fopenmp -triple nvptx64-nvidia-cuda -aux-triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -ffast-math -ffp-contract=fast | FileCheck %s --check-prefix=FAST // expected-no-diagnostics #include 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,63 @@ +// 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; + 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; } };