diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2582,7 +2582,11 @@ */ CXCursor_OMPScanDirective = 287, - CXCursor_LastStmt = CXCursor_OMPScanDirective, + /** Pragma changing floating point options. + */ + CXcursor_FloatingPragmaStmt = 288, + + CXCursor_LastStmt = CXcursor_FloatingPragmaStmt, /** * Cursor that represents the translation unit itself. 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 @@ -3573,12 +3573,6 @@ return FPOptions(BinaryOperatorBits.FPFeatures); } - // Get the FP contractability status of this operator. Only meaningful for - // operations on floating point types. - bool isFPContractableWithinStatement() const { - return getFPFeatures().allowFPContractWithinStatement(); - } - // Get the FENV_ACCESS status of this operator. Only meaningful for // operations on floating point types. bool isFEnvAccessOn() const { return getFPFeatures().allowFEnvAccess(); } diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -171,12 +171,6 @@ FPOptions getFPFeatures() const { return FPOptions(CXXOperatorCallExprBits.FPFeatures); } - - // Get the FP contractability status of this operator. Only meaningful for - // operations on floating point types. - bool isFPContractableWithinStatement() const { - return getFPFeatures().allowFPContractWithinStatement(); - } }; /// Represents a call to a member function that diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2655,6 +2655,7 @@ DEF_TRAVERSE_STMT(SEHFinallyStmt, {}) DEF_TRAVERSE_STMT(SEHLeaveStmt, {}) DEF_TRAVERSE_STMT(CapturedStmt, { TRY_TO(TraverseDecl(S->getCapturedDecl())); }) +DEF_TRAVERSE_STMT(FloatingPragmaStmt, {}) DEF_TRAVERSE_STMT(CXXOperatorCallExpr, {}) DEF_TRAVERSE_STMT(CXXRewrittenBinaryOperator, { 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 @@ -18,6 +18,7 @@ #include "clang/AST/StmtIterator.h" #include "clang/Basic/CapturedStmt.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/ArrayRef.h" @@ -3590,6 +3591,88 @@ const_child_range children() const; }; +/// This represents a pragma that affects interpretations of the subsequent AST +/// nodes that specify floating point operations. +/// +/// An example of such pragma is 'pragma STDC FE_CONTRACT', which specifies +/// whether operations may be contracted. It results in different code generated +/// for some floating point operations. This and similar pragmas affect the AST +/// nodes contained in the same compound statement where the pragma occurs. +/// +/// Object of this class keeps the state of all properties, that can be modified +/// using pragmas. If more than one of such pragma act in the same compound +/// statement, each subsequent pragma keep options set by this and all preceding +/// pragmas. If a pragma is specified at file level, each affected function will +/// have synthesized pragma statement in its body. Similarly, if FP options +/// differ from defaults because of compilation options, each function body +/// receives such synthesized pragma statement. +/// +class FloatingPragmaStmt : public Stmt { + friend class ASTReader; + friend class ASTStmtReader; + +public: + + /// Enumerates possible pragma representations in source code. + enum PragmaKind { + /// This statement do not correspond to any pragma inside function + /// definition, it represents either pragmas at global level, or changes to + /// floating point environment made by compilation options. + Synthesized, + /// #pragma clang fp + PragmaClangFP, + /// #pragma STDC FP_CONTRACT + PragmaFPContract + }; + +private: + + PragmaKind Kind; + SourceLocation PragmaLoc; + FPOptions FPFeatures; + +public: + + FloatingPragmaStmt(PragmaKind K, SourceLocation Loc, const FPOptions &FPO) + : Stmt(FloatingPragmaStmtClass), Kind(K), PragmaLoc(Loc), FPFeatures(FPO) {} + + explicit FloatingPragmaStmt(EmptyShell E) : Stmt(FloatingPragmaStmtClass, E) {} + + PragmaKind getKind() const { return Kind; } + void setKind(PragmaKind K) { Kind = K; } + + SourceLocation getPragmaLoc() const { return PragmaLoc; } + void setPragmaLoc(SourceLocation L) { PragmaLoc = L; } + + const FPOptions &getFPOptions() const { return FPFeatures; } + FPOptions &getFPOptions() { return FPFeatures; } + + LangOptions::FPContractModeKind getContract() const { + return FPFeatures.getContract(); + } + void setContract(LangOptions::FPContractModeKind X) { + FPFeatures.setContract(X); + } + + SourceLocation getBeginLoc() const LLVM_READONLY { return PragmaLoc; } + SourceLocation getEndLoc() const LLVM_READONLY { return PragmaLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == FloatingPragmaStmtClass; + } + + static bool classof(FloatingPragmaStmt *) { return true; } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_STMT_H diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -229,6 +229,7 @@ void VisitLabelStmt(const LabelStmt *Node); void VisitGotoStmt(const GotoStmt *Node); void VisitCaseStmt(const CaseStmt *Node); + void VisitFloatingPragmaStmt(const FloatingPragmaStmt *Node); void VisitConstantExpr(const ConstantExpr *Node); void VisitCallExpr(const CallExpr *Node); void VisitCastExpr(const CastExpr *Node); 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 @@ -353,6 +353,12 @@ VersionTuple getOpenCLVersionTuple() const; }; +/// Returns text representation for the given contract mode value. +/// If \a ShortMode is true, only corresponding switch value ("ON", "OFF" or +/// "FAST") is returned, otherwise the output string looks like "contract(on)". +/// +StringRef spell(LangOptions::FPContractModeKind X, bool ShortForm); + /// Floating point control options class FPOptions { public: @@ -396,6 +402,14 @@ void setDisallowFPContract() { fp_contract = LangOptions::FPC_Off; } + LangOptions::FPContractModeKind getContract() const { + return static_cast(fp_contract); + } + + void setContract(LangOptions::FPContractModeKind X) { + fp_contract = X; + } + bool allowFEnvAccess() const { return fenv_access == LangOptions::FEA_On; } @@ -428,12 +442,25 @@ allowFEnvAccess(); } + bool isDefault() const { + return *this == FPOptions(); + } + /// Used to serialize this. unsigned getInt() const { return fp_contract | (fenv_access << 2) | (rounding << 3) | (exceptions << 6); } + bool operator == (const FPOptions &O) const { + return fp_contract == O.fp_contract && + fenv_access == O.fenv_access && + rounding == O.rounding && + exceptions == O.exceptions; + } + + bool operator != (const FPOptions &O) const { return !operator==(O); } + private: /// Adjust BinaryOperatorBitfields::FPFeatures and /// CXXOperatorCallExprBitfields::FPFeatures to match the total bit-field size diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -24,6 +24,7 @@ def CaseStmt : StmtNode; def DefaultStmt : StmtNode; def CapturedStmt : StmtNode; +def FloatingPragmaStmt : StmtNode; // Statements that might produce a value (for example, as the last non-null // statement in a GNU statement-expression). 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 @@ -4417,6 +4417,17 @@ SourceLocation Location, bool AlwaysCreate); + /// Statements created by floating point pragmas inside the current function. + /// The first element may be synthesized to represent file scope pragmas or + /// compilation options. The last element must be in sync with FPFeatures. + SmallVector PragmaStatements; + + void insertPragmaStatements(SmallVectorImpl &Stmts); + + StmtResult ActOnFloatingPragmaStmt(SourceLocation PragmaLoc, + FloatingPragmaStmt::PragmaKind K); + void ActOnFPContract(StmtResult S, LangOptions::FPContractModeKind X); + VarDecl *BuildObjCExceptionDecl(TypeSourceInfo *TInfo, QualType ExceptionType, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, 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 @@ -1505,6 +1505,9 @@ /// A MS-style AsmStmt record. STMT_MSASM, + /// A floating point option pragma. + STMT_FLOATING_PRAGMA, + /// A constant expression context. EXPR_CONSTANT, diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -748,6 +748,9 @@ public: ASTContext &Ctx; + /// Keeps floating point options used in evaluation. + FPOptions FPFeatures; + /// EvalStatus - Contains information about the evaluation. Expr::EvalStatus &EvalStatus; @@ -1335,6 +1338,18 @@ }; typedef ScopeRAII BlockScopeRAII; typedef ScopeRAII FullExpressionRAII; + + class FPOptionsStateRAII { + EvalInfo &Info; + FPOptions SavedState; + + public: + explicit FPOptionsStateRAII(EvalInfo &Info) + : Info(Info), SavedState(Info.FPFeatures) { + } + + ~FPOptionsStateRAII() { Info.FPFeatures = SavedState; } + }; } bool SubobjectDesignator::checkSubobject(EvalInfo &Info, const Expr *E, @@ -4690,6 +4705,7 @@ case Stmt::CompoundStmtClass: { BlockScopeRAII Scope(Info); + FPOptionsStateRAII SavedFPFeatures(Info); const CompoundStmt *CS = cast(S); for (const auto *BI : CS->body()) { @@ -4913,6 +4929,10 @@ case Stmt::CXXTryStmtClass: // Evaluate try blocks by evaluating all sub statements. return EvaluateStmt(Result, Info, cast(S)->getTryBlock(), Case); + + case Stmt::FloatingPragmaStmtClass: + Info.FPFeatures = cast(S)->getFPOptions(); + return ESR_Succeeded; } } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -954,6 +954,28 @@ } //===----------------------------------------------------------------------===// +// Miscellaneous directives printing methods +//===----------------------------------------------------------------------===// + +void StmtPrinter::VisitFloatingPragmaStmt(FloatingPragmaStmt *Node) { + switch (Node->getKind()) { + case FloatingPragmaStmt::PragmaClangFP: + case FloatingPragmaStmt::Synthesized: + Indent() << "#pragma clang fp " + << spell(Node->getFPOptions().getContract(), false); + break; + case FloatingPragmaStmt::PragmaFPContract: + Indent() << "#pragma STDC FP_CONTRACT " + << spell(Node->getFPOptions().getContract(), true); + break; + default: + llvm_unreachable("Unexpected pragma kind"); + } + if (Policy.IncludeNewlines) + OS << NL; +} + +//===----------------------------------------------------------------------===// // Expr printing methods. //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -405,6 +405,10 @@ VisitStmt(S); } +void StmtProfiler::VisitFloatingPragmaStmt(const FloatingPragmaStmt *S) { + VisitStmt(S); +} + namespace { class OMPClauseProfiler : public ConstOMPClauseVisitor { StmtProfiler *Profiler; diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -707,6 +707,22 @@ OS << " gnu_range"; } +void TextNodeDumper::VisitFloatingPragmaStmt(const FloatingPragmaStmt *Node) { + switch (Node->getKind()) { + case FloatingPragmaStmt::Synthesized: + case FloatingPragmaStmt::PragmaClangFP: + OS << " pragma clang fp " + << spell(Node->getFPOptions().getContract(), false); + break; + case FloatingPragmaStmt::PragmaFPContract: + OS << " pragma fp_contract " + << spell(Node->getFPOptions().getContract(), true); + break; + default: + break; + } +} + void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) { if (Node->getResultAPValueKind() != APValue::None) { ColorScope Color(OS, ShowColors, ValueColor); diff --git a/clang/lib/Basic/LangOptions.cpp b/clang/lib/Basic/LangOptions.cpp --- a/clang/lib/Basic/LangOptions.cpp +++ b/clang/lib/Basic/LangOptions.cpp @@ -47,3 +47,15 @@ const int Ver = OpenCLCPlusPlus ? OpenCLCPlusPlusVersion : OpenCLVersion; return VersionTuple(Ver / 100, (Ver % 100) / 10); } + +StringRef clang::spell(LangOptions::FPContractModeKind X, bool ShortForm) { + switch (X) { + case LangOptions::FPC_Off: + return ShortForm ? "OFF" : "contract(off)"; + case LangOptions::FPC_On: + return ShortForm ? "ON" : "contract(on)"; + case LangOptions::FPC_Fast: + return ShortForm ? "FAST" : "contract(fast)"; + } + llvm_unreachable("Unexpected value"); +} 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 @@ -213,17 +213,12 @@ (2 * Ctx.getTypeSize(RHSTy)) < PromotedSize; } -/// Update the FastMathFlags of LLVM IR from the FPOptions in LangOptions. -static void updateFastMathFlags(llvm::FastMathFlags &FMF, - FPOptions FPFeatures) { - FMF.setAllowContract(FPFeatures.allowFPContractAcrossStatement()); -} - -/// Propagate fast-math flags from \p Op to the instruction in \p V. -static Value *propagateFMFlags(Value *V, const BinOpInfo &Op) { +/// Propagate fast-math flags from the current FPOtions to the instruction +/// in \p V. +static Value *propagateFMFlags(Value *V, CodeGenFunction &CGF) { if (auto *I = dyn_cast(V)) { llvm::FastMathFlags FMF = I->getFastMathFlags(); - updateFastMathFlags(FMF, Op.FPFeatures); + FMF.setAllowContract(CGF.FPFeatures.allowFPContractAcrossStatement()); I->setFastMathFlags(FMF); } return V; @@ -744,7 +739,7 @@ if (Ops.LHS->getType()->isFPOrFPVectorTy()) { Value *V = Builder.CreateFMul(Ops.LHS, Ops.RHS, "mul"); - return propagateFMFlags(V, Ops); + return propagateFMFlags(V, CGF); } return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); } @@ -3416,7 +3411,7 @@ "Only fadd/fsub can be the root of an fmuladd."); // Check whether this op is marked as fusable. - if (!op.FPFeatures.allowFPContractWithinStatement()) + if (!CGF.FPFeatures.allowFPContractWithinStatement()) return nullptr; // We have a potentially fusable op. Look for a mul on one of the operands. @@ -3480,7 +3475,7 @@ return FMulAdd; Value *V = Builder.CreateFAdd(op.LHS, op.RHS, "add"); - return propagateFMFlags(V, op); + return propagateFMFlags(V, CGF); } if (op.isFixedPointBinOp()) @@ -3624,7 +3619,7 @@ if (Value *FMulAdd = tryEmitFMulAdd(op, CGF, Builder, true)) return FMulAdd; Value *V = Builder.CreateFSub(op.LHS, op.RHS, "sub"); - return propagateFMFlags(V, op); + return propagateFMFlags(V, CGF); } if (op.isFixedPointBinOp()) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -159,6 +159,9 @@ EmitCapturedStmt(*CS, CS->getCapturedRegionKind()); } break; + case Stmt::FloatingPragmaStmtClass: + EmitFloatingPragmaStmt(cast(*S)); + break; case Stmt::ObjCAtTryStmtClass: EmitObjCAtTryStmt(cast(*S)); break; @@ -411,6 +414,7 @@ assert((!GetLast || (GetLast && ExprResult)) && "If GetLast is true then the CompoundStmt must have a StmtExprResult"); + FPOptions SaveFPFeatures = FPFeatures; Address RetAlloca = Address::invalid(); for (auto *CurStmt : S.body()) { @@ -451,6 +455,7 @@ EmitStmt(CurStmt); } } + FPFeatures = SaveFPFeatures; return RetAlloca; } @@ -2368,6 +2373,10 @@ } } +void CodeGenFunction::EmitFloatingPragmaStmt(const FloatingPragmaStmt& S) { + FPFeatures = S.getFPOptions(); +} + LValue CodeGenFunction::InitCapturedStruct(const CapturedStmt &S) { const RecordDecl *RD = S.getCapturedRecordDecl(); QualType RecordTy = getContext().getRecordType(RD); 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 @@ -366,6 +366,7 @@ CodeGenModule &CGM; // Per-module state. const TargetInfo &Target; + FPOptions FPFeatures; typedef std::pair ComplexPairTy; LoopInfoStack LoopStack; @@ -3009,6 +3010,7 @@ void EmitCaseStmt(const CaseStmt &S); void EmitCaseStmtRange(const CaseStmt &S); void EmitAsmStmt(const AsmStmt &S); + void EmitFloatingPragmaStmt(const FloatingPragmaStmt &S); void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S); void EmitObjCAtTryStmt(const ObjCAtTryStmt &S); 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 @@ -642,7 +642,10 @@ break; } - Actions.ActOnPragmaFPContract(FPC); + StmtResult S = Actions.ActOnFloatingPragmaStmt( + Tok.getLocation(), FloatingPragmaStmt::PragmaFPContract); + Actions.ActOnFPContract(S, FPC); + ConsumeAnnotationToken(); } @@ -2790,7 +2793,9 @@ break; } - Actions.ActOnPragmaFPContract(FPC); + StmtResult S = Actions.ActOnFloatingPragmaStmt( + Tok.getLocation(), FloatingPragmaStmt::PragmaClangFP); + Actions.ActOnFPContract(S, FPC); ConsumeAnnotationToken(); } 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 @@ -1030,6 +1030,9 @@ StmtVector Stmts; + // If parsing leading pragmas created statements, put them into the body. + Actions.insertPragmaStatements(Stmts); + // "__label__ X, Y, Z;" is the GNU "Local Label" extension. These are // only allowed at the start of a compound stmt regardless of the language. while (Tok.is(tok::kw___label__)) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13777,6 +13777,8 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, SkipBodyInfo *SkipBody) { + PragmaStatements.clear(); + if (!D) { // Parsing the function declaration failed in some way. Push on a fake scope // anyway so we can try to parse the function body. @@ -13925,6 +13927,12 @@ getCurLexicalContext()->getDeclKind() != Decl::ObjCImplementation) Diag(FD->getLocation(), diag::warn_function_def_in_objc_container); + // If compilation options or file scope pragmas modified floating point state, + // create pragma statement to represent this state. + if (!FPFeatures.isDefault()) + PragmaStatements.push_back(new (Context) FloatingPragmaStmt( + FloatingPragmaStmt::Synthesized, SourceLocation(), FPFeatures)); + return D; } 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 @@ -2090,6 +2090,9 @@ return false; return true; + case Stmt::FloatingPragmaStmtClass: + return true; + default: if (!isa(S)) break; diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1566,6 +1566,9 @@ return mergeCanThrow(CT, canThrow(TS->getTryBody())); } + case Stmt::FloatingPragmaStmtClass: + return CT_Cannot; + case Stmt::NoStmtClass: llvm_unreachable("Invalid class for statement"); } 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 @@ -4467,3 +4467,27 @@ return Res; } + +void Sema::insertPragmaStatements(SmallVectorImpl &Stmts) { + if (!PragmaStatements.empty()) { + Stmts.insert(Stmts.begin(), PragmaStatements.begin(), + PragmaStatements.end()); + } + PragmaStatements.clear(); +} + +StmtResult Sema::ActOnFloatingPragmaStmt(SourceLocation PragmaLoc, + FloatingPragmaStmt::PragmaKind K) { + if (getCurScope()->getFnParent()) { + auto S = new (Context) FloatingPragmaStmt(K, PragmaLoc, FPFeatures); + PragmaStatements.push_back(S); + return S; + } + return StmtEmpty(); +} + +void Sema::ActOnFPContract(StmtResult S, LangOptions::FPContractModeKind X) { + if (S.isUsable()) + cast(S.get())->setContract(X); + FPFeatures.setContract(X); +} diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8035,6 +8035,12 @@ return S; } +template +StmtResult +TreeTransform::TransformFloatingPragmaStmt(FloatingPragmaStmt *S) { + return S; +} + //===----------------------------------------------------------------------===// // OpenMP directive transformation //===----------------------------------------------------------------------===// 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 @@ -2119,6 +2119,13 @@ S->setLeaveLoc(readSourceLocation()); } +void ASTStmtReader::VisitFloatingPragmaStmt(FloatingPragmaStmt *S) { + VisitStmt(S); + S->setKind(static_cast(Record.readInt())); + S->getFPOptions() = FPOptions(Record.readInt()); + S->setPragmaLoc(readSourceLocation()); +} + void ASTStmtReader::VisitSEHExceptStmt(SEHExceptStmt *S) { VisitStmt(S); S->Loc = readSourceLocation(); @@ -3743,7 +3750,7 @@ break; } - case EXPR_REQUIRES: + case EXPR_REQUIRES: { unsigned numLocalParameters = Record[ASTStmtReader::NumExprFields]; unsigned numRequirement = Record[ASTStmtReader::NumExprFields + 1]; S = RequiresExpr::Create(Context, Empty, numLocalParameters, @@ -3751,6 +3758,11 @@ break; } + case STMT_FLOATING_PRAGMA: + S = new (Context) FloatingPragmaStmt(Empty); + break; + } + // We hit a STMT_STOP, so we're done with this expression. if (Finished) break; 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 @@ -567,6 +567,7 @@ RECORD(STMT_DECL); RECORD(STMT_GCCASM); RECORD(STMT_MSASM); + RECORD(STMT_FLOATING_PRAGMA); RECORD(EXPR_PREDEFINED); RECORD(EXPR_DECL_REF); RECORD(EXPR_INTEGER_LITERAL); 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 @@ -2059,6 +2059,14 @@ Code = serialization::STMT_SEH_LEAVE; } +void ASTStmtWriter::VisitFloatingPragmaStmt(FloatingPragmaStmt *S) { + VisitStmt(S); + Record.writeEnum(S->getKind()); + Record.push_back(S->getFPOptions().getInt()); + Record.AddSourceLocation(S->getPragmaLoc()); + Code = serialization::STMT_FLOATING_PRAGMA; +} + //===----------------------------------------------------------------------===// // OpenMP Directives. //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1395,6 +1395,7 @@ case Stmt::PredefinedExprClass: case Stmt::AddrLabelExprClass: case Stmt::AttributedStmtClass: + case Stmt::FloatingPragmaStmtClass: case Stmt::IntegerLiteralClass: case Stmt::FixedPointLiteralClass: case Stmt::CharacterLiteralClass: diff --git a/clang/test/AST/ast-dump-pragma.cpp b/clang/test/AST/ast-dump-pragma.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/ast-dump-pragma.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -ast-dump %s | FileCheck --check-prefixes=CHECK,NOOPT %s +// RUN: %clang_cc1 -ffp-contract=on -ast-dump %s | FileCheck --check-prefix=FPCONTRACT %s + + +float func_00(float x, float y, float z) { + return x + y * z; +} +// NOOPT-LABEL: FunctionDecl {{.*}} func_00 +// NOOPT: CompoundStmt +// NOOPT-NEXT: ReturnStmt + +// FPCONTRACT-LABEL: FunctionDecl {{.*}} func_00 +// FPCONTRACT: CompoundStmt +// FPCONTRACT-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(on) +// FPCONTRACT-NEXT: ReturnStmt + + +float func_01(float x, float y, float z) { + #pragma clang fp contract(on) + return x + y * z; +} +// CHECK-LABEL: FunctionDecl {{.*}} func_01 +// CHECK: CompoundStmt +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(on) +// CHECK-NEXT: ReturnStmt + + +float func_03(float x, float y, float z) { + #pragma STDC FP_CONTRACT ON + return x + y * z; +} +// CHECK-LABEL: FunctionDecl {{.*}} func_03 +// CHECK: CompoundStmt +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma fp_contract ON +// CHECK-NEXT: ReturnStmt + + +float func_05(float x, float y, float z) { + #pragma clang fp contract(on) + #pragma clang fp contract(off) + #pragma clang fp contract(fast) + return x + y * z; +} +// CHECK-LABEL: FunctionDecl {{.*}} func_05 +// CHECK: CompoundStmt +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(on) +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(off) +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(fast) +// CHECK-NEXT: ReturnStmt + + +constexpr float func_06(float x, float y, float z) { + #pragma clang fp contract(on) + return x + y * z; +} +// CHECK-LABEL: FunctionDecl {{.*}} func_06 +// CHECK: CompoundStmt +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(on) +// CHECK-NEXT: ReturnStmt + + +#pragma clang fp contract(on) + + +float func_07(float x, float y, float z) { + return x + y * z; +} +// CHECK-LABEL: FunctionDecl {{.*}} func_07 +// CHECK: CompoundStmt +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(on) +// CHECK-NEXT: ReturnStmt + + +float func_08(float x, float y, float z) { + #pragma clang fp contract(off) + return x + y * z; +} +// CHECK-LABEL: FunctionDecl {{.*}} func_08 +// CHECK: CompoundStmt +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(on) +// CHECK-NEXT: FloatingPragmaStmt {{.*}} pragma clang fp contract(off) +// CHECK-NEXT: ReturnStmt 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 @@ -89,3 +89,20 @@ #pragma STDC FP_CONTRACT ON return c - a * b; } + +float fp_contract_10(float a, float x, float y, float z) { +// CHECK-LABEL: _Z14fp_contract_10ffff +// CHECK: call float @llvm.fmuladd.f32 +// CHECK: fmul float +// CHECK: fadd float +// CHECK: fadd float +// CHECK: call float @llvm.fmuladd.f32 + #pragma STDC FP_CONTRACT ON + float r = x + y * z; + { + #pragma STDC FP_CONTRACT OFF + r += a + x * y; + } + r += z + x * a; + return r; +} diff --git a/clang/test/Coverage/c-language-features.inc b/clang/test/Coverage/c-language-features.inc --- a/clang/test/Coverage/c-language-features.inc +++ b/clang/test/Coverage/c-language-features.inc @@ -210,3 +210,9 @@ void f11() { struct s12 var = { .aa = 33 }; } + +float pragma_01(float x, float y, float z) { + #pragma clang fp contract(on) + #pragma STDC FP_CONTRACT OFF + return x + y * z; +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -5612,6 +5612,8 @@ return cxstring::createRef("attribute(warn_unused_result)"); case CXCursor_AlignedAttr: return cxstring::createRef("attribute(aligned)"); + case CXcursor_FloatingPragmaStmt: + return cxstring::createRef("FloatingPragma"); } llvm_unreachable("Unhandled CXCursorKind"); diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -742,6 +742,10 @@ break; case Stmt::BuiltinBitCastExprClass: K = CXCursor_BuiltinBitCastExpr; + break; + case Stmt::FloatingPragmaStmtClass: + K = CXcursor_FloatingPragmaStmt; + break; } CXCursor C = { K, 0, { Parent, S, TU } };