diff options
author | Lang Hames <lhames@gmail.com> | 2012-10-02 04:45:10 +0000 |
---|---|---|
committer | Lang Hames <lhames@gmail.com> | 2012-10-02 04:45:10 +0000 |
commit | be9af1288881110e406b87914162eaa59f1e5918 (patch) | |
tree | d5dad9578b613b3a7d8e5b6612e686b76bb22203 | |
parent | d13eff6f77216a6fb25e18f600b492db4f0492e8 (diff) | |
download | clang-be9af1288881110e406b87914162eaa59f1e5918.tar.gz |
Add FP_CONTRACT support for clang.
Clang will now honor the FP_CONTRACT pragma and emit LLVM
fmuladd intrinsics for expressions of the form A * B + C (when they occur in a
single statement).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164989 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Expr.h | 27 | ||||
-rw-r--r-- | include/clang/AST/ExprCXX.h | 17 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 14 | ||||
-rw-r--r-- | lib/AST/ASTImporter.cpp | 6 | ||||
-rw-r--r-- | lib/Analysis/BodyFarm.cpp | 2 | ||||
-rw-r--r-- | lib/CodeGen/CGExprScalar.cpp | 86 | ||||
-rw-r--r-- | lib/CodeGen/CGObjC.cpp | 4 | ||||
-rw-r--r-- | lib/Parse/ParseStmt.cpp | 5 | ||||
-rw-r--r-- | lib/Rewrite/Frontend/RewriteModernObjC.cpp | 5 | ||||
-rw-r--r-- | lib/Rewrite/Frontend/RewriteObjC.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 6 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 30 | ||||
-rw-r--r-- | lib/Sema/SemaPseudoObject.cpp | 10 | ||||
-rw-r--r-- | lib/Sema/TreeTransform.h | 6 | ||||
-rw-r--r-- | lib/Serialization/ASTReaderStmt.cpp | 2 | ||||
-rw-r--r-- | lib/Serialization/ASTWriterStmt.cpp | 2 | ||||
-rw-r--r-- | test/CodeGen/ffp-contract-option.c (renamed from test/CodeGen/fp-contract.c) | 0 | ||||
-rw-r--r-- | test/CodeGen/fp-contract-pragma.cpp | 39 | ||||
-rw-r--r-- | test/CodeGenOpenCL/single-precision-constant.cl | 3 |
21 files changed, 229 insertions, 43 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 6629e3e60c..443d8be92b 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -2785,6 +2785,12 @@ public: private: unsigned Opc : 6; + + // Records the FP_CONTRACT pragma status at the point that this binary + // operator was parsed. This bit is only meaningful for operations on + // floating point types. For all other types it should default to + // false. + unsigned FPContractable : 1; SourceLocation OpLoc; enum { LHS, RHS, END_EXPR }; @@ -2793,7 +2799,7 @@ public: BinaryOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResTy, ExprValueKind VK, ExprObjectKind OK, - SourceLocation opLoc) + SourceLocation opLoc, bool fpContractable) : Expr(BinaryOperatorClass, ResTy, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), @@ -2801,7 +2807,7 @@ public: rhs->isInstantiationDependent()), (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())), - Opc(opc), OpLoc(opLoc) { + Opc(opc), FPContractable(fpContractable), OpLoc(opLoc) { SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; assert(!isCompoundAssignmentOp() && @@ -2902,10 +2908,18 @@ public: return child_range(&SubExprs[0], &SubExprs[0]+END_EXPR); } + // Set the FP contractability status of this operator. Only meaningful for + // operations on floating point types. + void setFPContractable(bool FPC) { FPContractable = FPC; } + + // Get the FP contractability status of this operator. Only meaningful for + // operations on floating point types. + bool isFPContractable() const { return FPContractable; } + protected: BinaryOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResTy, ExprValueKind VK, ExprObjectKind OK, - SourceLocation opLoc, bool dead) + SourceLocation opLoc, bool fpContractable, bool dead2) : Expr(CompoundAssignOperatorClass, ResTy, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), @@ -2913,7 +2927,7 @@ protected: rhs->isInstantiationDependent()), (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())), - Opc(opc), OpLoc(opLoc) { + Opc(opc), FPContractable(fpContractable), OpLoc(opLoc) { SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; } @@ -2935,8 +2949,9 @@ public: CompoundAssignOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResType, ExprValueKind VK, ExprObjectKind OK, QualType CompLHSType, QualType CompResultType, - SourceLocation OpLoc) - : BinaryOperator(lhs, rhs, opc, ResType, VK, OK, OpLoc, true), + SourceLocation OpLoc, bool fpContractable) + : BinaryOperator(lhs, rhs, opc, ResType, VK, OK, OpLoc, fpContractable, + true), ComputationLHSType(CompLHSType), ComputationResultType(CompResultType) { assert(isCompoundAssignmentOp() && diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 0c4ca8e979..1d0e3a005b 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -53,14 +53,19 @@ class CXXOperatorCallExpr : public CallExpr { OverloadedOperatorKind Operator; SourceRange Range; + // Record the FP_CONTRACT state that applies to this operator call. Only + // meaningful for floating point types. For other types this value can be + // set to false. + unsigned FPContractable : 1; + SourceRange getSourceRangeImpl() const LLVM_READONLY; public: CXXOperatorCallExpr(ASTContext& C, OverloadedOperatorKind Op, Expr *fn, ArrayRef<Expr*> args, QualType t, ExprValueKind VK, - SourceLocation operatorloc) + SourceLocation operatorloc, bool fpContractable) : CallExpr(C, CXXOperatorCallExprClass, fn, 0, args, t, VK, operatorloc), - Operator(Op) { + Operator(Op), FPContractable(fpContractable) { Range = getSourceRangeImpl(); } explicit CXXOperatorCallExpr(ASTContext& C, EmptyShell Empty) : @@ -85,6 +90,14 @@ public: } static bool classof(const CXXOperatorCallExpr *) { return true; } + // Set the FP contractability status of this operator. Only meaningful for + // operations on floating point types. + void setFPContractable(bool FPC) { FPContractable = FPC; } + + // Get the FP contractability status of this operator. Only meaningful for + // operations on floating point types. + bool isFPContractable() const { return FPContractable; } + friend class ASTStmtReader; friend class ASTStmtWriter; }; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c53c4f4194..1847220ddf 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -735,6 +735,20 @@ public: /// should not be used elsewhere. void EmitCurrentDiagnostic(unsigned DiagID); + /// Records and restores the FP_CONTRACT state on entry/exit of compound + /// statements. + class FPContractStateRAII { + public: + FPContractStateRAII(Sema& S) + : S(S), OldFPContractState(S.FPFeatures.fp_contract) {} + ~FPContractStateRAII() { + S.FPFeatures.fp_contract = OldFPContractState; + } + private: + Sema& S; + bool OldFPContractState : 1; + }; + public: Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TranslationUnitKind TUKind = TU_Complete, diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index a90026072b..7dd1f7de8b 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -4082,7 +4082,8 @@ Expr *ASTNodeImporter::VisitBinaryOperator(BinaryOperator *E) { return new (Importer.getToContext()) BinaryOperator(LHS, RHS, E->getOpcode(), T, E->getValueKind(), E->getObjectKind(), - Importer.Import(E->getOperatorLoc())); + Importer.Import(E->getOperatorLoc()), + E->isFPContractable()); } Expr *ASTNodeImporter::VisitCompoundAssignOperator(CompoundAssignOperator *E) { @@ -4111,7 +4112,8 @@ Expr *ASTNodeImporter::VisitCompoundAssignOperator(CompoundAssignOperator *E) { T, E->getValueKind(), E->getObjectKind(), CompLHSType, CompResultType, - Importer.Import(E->getOperatorLoc())); + Importer.Import(E->getOperatorLoc()), + E->isFPContractable()); } static bool ImportCastPath(CastExpr *E, CXXCastPath &Path) { diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index be998581d7..3dcbfb927e 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -70,7 +70,7 @@ BinaryOperator *ASTMaker::makeAssignment(const Expr *LHS, const Expr *RHS, QualType Ty) { return new (C) BinaryOperator(const_cast<Expr*>(LHS), const_cast<Expr*>(RHS), BO_Assign, Ty, VK_RValue, - OK_Ordinary, SourceLocation()); + OK_Ordinary, SourceLocation(), false); } DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) { diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index ad373095de..28062456cc 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -45,6 +45,7 @@ struct BinOpInfo { Value *RHS; QualType Ty; // Computation Type. BinaryOperator::Opcode Opcode; // Opcode of BinOp to perform + bool FPContractable; const Expr *E; // Entire expr, for error unsupported. May not be binop. }; @@ -1654,6 +1655,7 @@ BinOpInfo ScalarExprEmitter::EmitBinOps(const BinaryOperator *E) { Result.RHS = Visit(E->getRHS()); Result.Ty = E->getType(); Result.Opcode = E->getOpcode(); + Result.FPContractable = E->isFPContractable(); Result.E = E; return Result; } @@ -1982,6 +1984,77 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, return CGF.Builder.CreateInBoundsGEP(pointer, index, "add.ptr"); } +// Construct an fmuladd intrinsic to represent a fused mul-add of MulOp and +// Addend. Use negMul and negAdd to negate the first operand of the Mul or +// the add operand respectively. This allows fmuladd to represent a*b-c, or +// c-a*b. Patterns in LLVM should catch the negated forms and translate them to +// efficient operations. +static Value* buildFMulAdd(llvm::BinaryOperator *MulOp, Value *Addend, + const CodeGenFunction &CGF, CGBuilderTy &Builder, + bool negMul, bool negAdd) { + assert(!(negMul && negAdd) && "Only one of negMul and negAdd should be set."); + + Value *MulOp0 = MulOp->getOperand(0); + Value *MulOp1 = MulOp->getOperand(1); + if (negMul) { + MulOp0 = + Builder.CreateFSub( + llvm::ConstantFP::getZeroValueForNegation(MulOp0->getType()), MulOp0, + "neg"); + } else if (negAdd) { + Addend = + Builder.CreateFSub( + llvm::ConstantFP::getZeroValueForNegation(Addend->getType()), Addend, + "neg"); + } + + Value *FMulAdd = + Builder.CreateCall3( + CGF.CGM.getIntrinsic(llvm::Intrinsic::fmuladd, Addend->getType()), + MulOp0, MulOp1, Addend); + MulOp->eraseFromParent(); + + return FMulAdd; +} + +// Check whether it would be legal to emit an fmuladd intrinsic call to +// represent op and if so, build the fmuladd. +// +// Checks that (a) the operation is fusable, and (b) -ffp-contract=on. +// Does NOT check the type of the operation - it's assumed that this function +// will be called from contexts where it's known that the type is contractable. +static Value* tryEmitFMulAdd(const BinOpInfo &op, + const CodeGenFunction &CGF, CGBuilderTy &Builder, + bool isSub=false) { + + assert((op.Opcode == BO_Add || op.Opcode == BO_AddAssign || + op.Opcode == BO_Sub || op.Opcode == BO_SubAssign) && + "Only fadd/fsub can be the root of an fmuladd."); + + // Check whether this op is marked as fusable. + if (!op.FPContractable) + return 0; + + // Check whether -ffp-contract=on. (If -ffp-contract=off/fast, fusing is + // either disabled, or handled entirely by the LLVM backend). + if (CGF.getContext().getLangOpts().getFPContractMode() != LangOptions::FPC_On) + return 0; + + // We have a potentially fusable op. Look for a mul on one of the operands. + if (llvm::BinaryOperator* LHSBinOp = dyn_cast<llvm::BinaryOperator>(op.LHS)) { + if (LHSBinOp->getOpcode() == llvm::Instruction::FMul) { + return buildFMulAdd(LHSBinOp, op.RHS, CGF, Builder, false, isSub); + } + } else if (llvm::BinaryOperator* RHSBinOp = + dyn_cast<llvm::BinaryOperator>(op.RHS)) { + if (RHSBinOp->getOpcode() == llvm::Instruction::FMul) { + return buildFMulAdd(RHSBinOp, op.LHS, CGF, Builder, isSub, false); + } + } + + return 0; +} + Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) { if (op.LHS->getType()->isPointerTy() || op.RHS->getType()->isPointerTy()) @@ -2000,8 +2073,13 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) { } } - if (op.LHS->getType()->isFPOrFPVectorTy()) + if (op.LHS->getType()->isFPOrFPVectorTy()) { + // Try to form an fmuladd. + if (Value *FMulAdd = tryEmitFMulAdd(op, CGF, Builder)) + return FMulAdd; + return Builder.CreateFAdd(op.LHS, op.RHS, "add"); + } return Builder.CreateAdd(op.LHS, op.RHS, "add"); } @@ -2022,8 +2100,12 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { } } - if (op.LHS->getType()->isFPOrFPVectorTy()) + if (op.LHS->getType()->isFPOrFPVectorTy()) { + // Try to form an fmuladd. + if (Value *FMulAdd = tryEmitFMulAdd(op, CGF, Builder, true)) + return FMulAdd; return Builder.CreateFSub(op.LHS, op.RHS, "sub"); + } return Builder.CreateSub(op.LHS, op.RHS, "sub"); } diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index fe525fa151..9e01b39748 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -1213,7 +1213,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, BinaryOperator assign(&ivarRef, finalArg, BO_Assign, ivarRef.getType(), VK_RValue, OK_Ordinary, - SourceLocation()); + SourceLocation(), false); EmitStmt(&assign); } @@ -2850,7 +2850,7 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( CallExpr *CalleeExp = cast<CallExpr>(PID->getSetterCXXAssignment()); CXXOperatorCallExpr TheCall(C, OO_Equal, CalleeExp->getCallee(), Args, DestTy->getPointeeType(), - VK_LValue, SourceLocation()); + VK_LValue, SourceLocation(), false); EmitStmt(&TheCall); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index db6b3d48f3..eb2a672fdc 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -679,6 +679,11 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), Tok.getLocation(), "in compound statement ('{}')"); + + // Record the state of the FP_CONTRACT pragma, restore on leaving the + // compound statement. + Sema::FPContractStateRAII SaveFPContractState(Actions); + InMessageExpressionRAIIObject InMessage(*this, false); BalancedDelimiterTracker T(*this, tok::l_brace); if (T.consumeOpen()) diff --git a/lib/Rewrite/Frontend/RewriteModernObjC.cpp b/lib/Rewrite/Frontend/RewriteModernObjC.cpp index b2ba0f0359..245e809c90 100644 --- a/lib/Rewrite/Frontend/RewriteModernObjC.cpp +++ b/lib/Rewrite/Frontend/RewriteModernObjC.cpp @@ -3576,7 +3576,8 @@ Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, SourceLocation()); BinaryOperator *lessThanExpr = new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, - VK_RValue, OK_Ordinary, SourceLocation()); + VK_RValue, OK_Ordinary, SourceLocation(), + false); // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) ConditionalOperator *CondExpr = new (Context) ConditionalOperator(lessThanExpr, @@ -7473,7 +7474,7 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { BinaryOperator *addExpr = new (Context) BinaryOperator(castExpr, DRE, BO_Add, Context->getPointerType(Context->CharTy), - VK_RValue, OK_Ordinary, SourceLocation()); + VK_RValue, OK_Ordinary, SourceLocation(), false); // Don't forget the parens to enforce the proper binding. ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), diff --git a/lib/Rewrite/Frontend/RewriteObjC.cpp b/lib/Rewrite/Frontend/RewriteObjC.cpp index 62def16ab5..a6dcc6b8d8 100644 --- a/lib/Rewrite/Frontend/RewriteObjC.cpp +++ b/lib/Rewrite/Frontend/RewriteObjC.cpp @@ -3078,7 +3078,8 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, SourceLocation()); BinaryOperator *lessThanExpr = new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, - VK_RValue, OK_Ordinary, SourceLocation()); + VK_RValue, OK_Ordinary, SourceLocation(), + false); // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) ConditionalOperator *CondExpr = new (Context) ConditionalOperator(lessThanExpr, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index dccce6ef90..b3836a2602 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -7504,7 +7504,7 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, = new (S.Context) BinaryOperator(IterationVarRefRVal, IntegerLiteral::Create(S.Context, Upper, SizeType, Loc), BO_NE, S.Context.BoolTy, - VK_RValue, OK_Ordinary, Loc); + VK_RValue, OK_Ordinary, Loc, false); // Create the pre-increment of the iteration variable. Expr *Increment diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 0570dd38c7..75bc8658c6 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -8416,7 +8416,8 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, if (CompResultTy.isNull()) return Owned(new (Context) BinaryOperator(LHS.take(), RHS.take(), Opc, - ResultTy, VK, OK, OpLoc)); + ResultTy, VK, OK, OpLoc, + FPFeatures.fp_contract)); if (getLangOpts().CPlusPlus && LHS.get()->getObjectKind() != OK_ObjCProperty) { VK = VK_LValue; @@ -8424,7 +8425,8 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, } return Owned(new (Context) CompoundAssignOperator(LHS.take(), RHS.take(), Opc, ResultTy, VK, OK, CompLHSTy, - CompResultTy, OpLoc)); + CompResultTy, OpLoc, + FPFeatures.fp_contract)); } /// DiagnoseBitwisePrecedence - Emit a warning when bitwise and comparison diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 763ef28919..ad4a17f99c 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -4833,7 +4833,8 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) { BO_Comma, BO->getType(), BO->getValueKind(), BO->getObjectKind(), - BO->getOperatorLoc())); + BO->getOperatorLoc(), + BO->isFPContractable())); } } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index e9c2c6c5f6..10b96c9ec3 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -9981,7 +9981,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, llvm::makeArrayRef(Args, NumArgs), Context.DependentTy, VK_RValue, - OpLoc)); + OpLoc, false)); } // Build an empty overload set. @@ -10058,7 +10058,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, CallExpr *TheCall = new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.take(), llvm::makeArrayRef(Args, NumArgs), - ResultTy, VK, OpLoc); + ResultTy, VK, OpLoc, false); if (CheckCallReturnType(FnDecl->getResultType(), OpLoc, TheCall, FnDecl)) @@ -10159,7 +10159,8 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, return Owned(new (Context) BinaryOperator(Args[0], Args[1], Opc, Context.DependentTy, VK_RValue, OK_Ordinary, - OpLoc)); + OpLoc, + FPFeatures.fp_contract)); return Owned(new (Context) CompoundAssignOperator(Args[0], Args[1], Opc, Context.DependentTy, @@ -10167,7 +10168,8 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, OK_Ordinary, Context.DependentTy, Context.DependentTy, - OpLoc)); + OpLoc, + FPFeatures.fp_contract)); } // FIXME: save results of ADL from here? @@ -10179,11 +10181,9 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, NestedNameSpecifierLoc(), OpNameInfo, /*ADL*/ true, IsOverloaded(Fns), Fns.begin(), Fns.end()); - return Owned(new (Context) CXXOperatorCallExpr(Context, Op, Fn, - Args, - Context.DependentTy, - VK_RValue, - OpLoc)); + return Owned(new (Context) CXXOperatorCallExpr(Context, Op, Fn, Args, + Context.DependentTy, VK_RValue, + OpLoc, FPFeatures.fp_contract)); } // Always do placeholder-like conversions on the RHS. @@ -10298,7 +10298,8 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, CXXOperatorCallExpr *TheCall = new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.take(), - Args, ResultTy, VK, OpLoc); + Args, ResultTy, VK, OpLoc, + FPFeatures.fp_contract); if (CheckCallReturnType(FnDecl->getResultType(), OpLoc, TheCall, FnDecl)) @@ -10430,7 +10431,7 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, Args, Context.DependentTy, VK_RValue, - RLoc)); + RLoc, false)); } // Handle placeholders on both operands. @@ -10507,7 +10508,8 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, CXXOperatorCallExpr *TheCall = new (Context) CXXOperatorCallExpr(Context, OO_Subscript, FnExpr.take(), Args, - ResultTy, VK, RLoc); + ResultTy, VK, RLoc, + false); if (CheckCallReturnType(FnDecl->getResultType(), LLoc, TheCall, FnDecl)) @@ -11035,7 +11037,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, CXXOperatorCallExpr *TheCall = new (Context) CXXOperatorCallExpr(Context, OO_Call, NewFn.take(), llvm::makeArrayRef(MethodArgs, NumArgs+1), - ResultTy, VK, RParenLoc); + ResultTy, VK, RParenLoc, false); delete [] MethodArgs; if (CheckCallReturnType(Method->getResultType(), LParenLoc, TheCall, @@ -11208,7 +11210,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) { ResultTy = ResultTy.getNonLValueExprType(Context); CXXOperatorCallExpr *TheCall = new (Context) CXXOperatorCallExpr(Context, OO_Arrow, FnExpr.take(), - Base, ResultTy, VK, OpLoc); + Base, ResultTy, VK, OpLoc, false); if (CheckCallReturnType(Method->getResultType(), OpLoc, TheCall, Method)) diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp index 501fa113ce..22a24a947f 100644 --- a/lib/Sema/SemaPseudoObject.cpp +++ b/lib/Sema/SemaPseudoObject.cpp @@ -356,7 +356,7 @@ PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc, syntactic = new (S.Context) BinaryOperator(syntacticLHS, capturedRHS, opcode, capturedRHS->getType(), capturedRHS->getValueKind(), - OK_Ordinary, opcLoc); + OK_Ordinary, opcLoc, false); } else { ExprResult opLHS = buildGet(); if (opLHS.isInvalid()) return ExprError(); @@ -375,7 +375,7 @@ PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc, OK_Ordinary, opLHS.get()->getType(), result.get()->getType(), - opcLoc); + opcLoc, false); } // The result of the assignment, if not void, is the value set into @@ -1366,7 +1366,7 @@ ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc, // Do nothing if either argument is dependent. if (LHS->isTypeDependent() || RHS->isTypeDependent()) return new (Context) BinaryOperator(LHS, RHS, opcode, Context.DependentTy, - VK_RValue, OK_Ordinary, opcLoc); + VK_RValue, OK_Ordinary, opcLoc, false); // Filter out non-overload placeholder types in the RHS. if (RHS->getType()->isNonOverloadPlaceholderType()) { @@ -1437,14 +1437,14 @@ Expr *Sema::recreateSyntacticForm(PseudoObjectExpr *E) { cop->getObjectKind(), cop->getComputationLHSType(), cop->getComputationResultType(), - cop->getOperatorLoc()); + cop->getOperatorLoc(), false); } else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(syntax)) { Expr *lhs = stripOpaqueValuesFromPseudoObjectRef(*this, bop->getLHS()); Expr *rhs = cast<OpaqueValueExpr>(bop->getRHS())->getSourceExpr(); return new (Context) BinaryOperator(lhs, rhs, bop->getOpcode(), bop->getType(), bop->getValueKind(), bop->getObjectKind(), - bop->getOperatorLoc()); + bop->getOperatorLoc(), false); } else { assert(syntax->hasPlaceholderType(BuiltinType::PseudoObject)); return stripOpaqueValuesFromPseudoObjectRef(*this, syntax); diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index e0451c4c49..f76c947e31 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -6429,6 +6429,9 @@ TreeTransform<Derived>::TransformBinaryOperator(BinaryOperator *E) { RHS.get() == E->getRHS()) return SemaRef.Owned(E); + Sema::FPContractStateRAII FPContractState(getSema()); + getSema().FPFeatures.fp_contract = E->isFPContractable(); + return getDerived().RebuildBinaryOperator(E->getOperatorLoc(), E->getOpcode(), LHS.get(), RHS.get()); } @@ -6852,6 +6855,9 @@ TreeTransform<Derived>::TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { (E->getNumArgs() != 2 || Second.get() == E->getArg(1))) return SemaRef.MaybeBindToTemporary(E); + Sema::FPContractStateRAII FPContractState(getSema()); + getSema().FPFeatures.fp_contract = E->isFPContractable(); + return getDerived().RebuildCXXOperatorCallExpr(E->getOperator(), E->getOperatorLoc(), Callee.get(), diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index cfe22b5208..3a02e76d19 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -565,6 +565,7 @@ void ASTStmtReader::VisitBinaryOperator(BinaryOperator *E) { E->setRHS(Reader.ReadSubExpr()); E->setOpcode((BinaryOperator::Opcode)Record[Idx++]); E->setOperatorLoc(ReadSourceLocation(Record, Idx)); + E->setFPContractable((bool)Record[Idx++]); } void ASTStmtReader::VisitCompoundAssignOperator(CompoundAssignOperator *E) { @@ -1086,6 +1087,7 @@ void ASTStmtReader::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); E->Operator = (OverloadedOperatorKind)Record[Idx++]; E->Range = Reader.ReadSourceRange(F, Record, Idx); + E->setFPContractable((bool)Record[Idx++]); } void ASTStmtReader::VisitCXXConstructExpr(CXXConstructExpr *E) { diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 9ca8130586..0337089659 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -536,6 +536,7 @@ void ASTStmtWriter::VisitBinaryOperator(BinaryOperator *E) { Writer.AddStmt(E->getRHS()); Record.push_back(E->getOpcode()); // FIXME: stable encoding Writer.AddSourceLocation(E->getOperatorLoc(), Record); + Record.push_back(E->isFPContractable()); Code = serialization::EXPR_BINARY_OPERATOR; } @@ -1055,6 +1056,7 @@ void ASTStmtWriter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) { VisitCallExpr(E); Record.push_back(E->getOperator()); Writer.AddSourceRange(E->Range, Record); + Record.push_back(E->isFPContractable()); Code = serialization::EXPR_CXX_OPERATOR_CALL; } diff --git a/test/CodeGen/fp-contract.c b/test/CodeGen/ffp-contract-option.c index eb95f1e217..eb95f1e217 100644 --- a/test/CodeGen/fp-contract.c +++ b/test/CodeGen/ffp-contract-option.c diff --git a/test/CodeGen/fp-contract-pragma.cpp b/test/CodeGen/fp-contract-pragma.cpp new file mode 100644 index 0000000000..8c51469c47 --- /dev/null +++ b/test/CodeGen/fp-contract-pragma.cpp @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -O3 -emit-llvm -o - %s | FileCheck %s + +// Is FP_CONTRACT is honored in a simple case? +float fp_contract_1(float a, float b, float c) { +// CHECK: _Z13fp_contract_1fff +// CHECK-NEXT: entry +// CHECK-NEXT: %0 = tail call float @llvm.fmuladd + #pragma STDC FP_CONTRACT ON + return a * b + c; +} + +// Is FP_CONTRACT state cleared on exiting compound statements? +float fp_contract_2(float a, float b, float c) { +// CHECK: _Z13fp_contract_2fff +// CHECK-NEXT: entry +// CHECK-NEXT: %mul = fmul float %a, %b +// CHECK-NEXT: %add = fadd float %mul, %c + { + #pragma STDC FP_CONTRACT ON + } + return a * b + c; +} + +// Does FP_CONTRACT survive template instatiation? +class Foo {}; +Foo operator+(Foo, Foo); + +template <typename T> +T template_muladd(T a, T b, T c) { + #pragma STDC FP_CONTRACT ON + return a * b + c; +} + +float fp_contract_3(float a, float b, float c) { +// CHECK: _Z13fp_contract_3fff +// CHECK-NEXT: entry +// CHECK-NEXT: %0 = tail call float @llvm.fmuladd + return template_muladd<float>(a, b, c); +} diff --git a/test/CodeGenOpenCL/single-precision-constant.cl b/test/CodeGenOpenCL/single-precision-constant.cl index 62b37c1361..9ad1fb4ea0 100644 --- a/test/CodeGenOpenCL/single-precision-constant.cl +++ b/test/CodeGenOpenCL/single-precision-constant.cl @@ -1,7 +1,6 @@ // RUN: %clang_cc1 %s -cl-single-precision-constant -emit-llvm -o - | FileCheck %s float fn(float f) { - // CHECK: fmul float - // CHECK: fadd float + // CHECK: %0 = tail call float @llvm.fmuladd.f32(float %f, float 2.000000e+00, float 1.000000e+00) return f*2. + 1.; } |