Index: include/llvm/IR/IRBuilder.h =================================================================== --- include/llvm/IR/IRBuilder.h +++ include/llvm/IR/IRBuilder.h @@ -31,7 +31,7 @@ #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" @@ -96,12 +96,18 @@ MDNode *DefaultFPMathTag; FastMathFlags FMF; + bool IsFPConstrained; + ConstrainedFPIntrinsic::ExceptionBehavior DefaultConstrainedExcept; + ConstrainedFPIntrinsic::RoundingMode DefaultConstrainedRounding; + ArrayRef DefaultOperandBundles; public: IRBuilderBase(LLVMContext &context, MDNode *FPMathTag = nullptr, ArrayRef OpBundles = None) - : Context(context), DefaultFPMathTag(FPMathTag), + : Context(context), DefaultFPMathTag(FPMathTag), IsFPConstrained(false), + DefaultConstrainedExcept(ConstrainedFPIntrinsic::ebUnspecified), + DefaultConstrainedRounding(ConstrainedFPIntrinsic::rmUnspecified), DefaultOperandBundles(OpBundles) { ClearInsertionPoint(); } @@ -218,6 +224,36 @@ /// Set the fast-math flags to be used with generated fp-math operators void setFastMathFlags(FastMathFlags NewFMF) { FMF = NewFMF; } + /// Enable/Disable use of constrained floating point math. When + /// enabled the CreateF() calls instead create constrained + /// floating point intrinsic calls. + void setIsFPConstrained(bool IsCon) { IsFPConstrained = IsCon; } + + /// Query for the use of constrained floating point math + bool getIsFPConstrained() { return IsFPConstrained; } + + /// Set the exception handling to be used with constrained floating point + void setDefaultConstrainedExcept( + ConstrainedFPIntrinsic::ExceptionBehavior NewExcept) { + DefaultConstrainedExcept = NewExcept; + } + + /// Set the rounding mode handling to be used with constrained floating point + void setDefaultConstrainedRounding( + ConstrainedFPIntrinsic::RoundingMode NewRounding) { + DefaultConstrainedRounding = NewRounding; + } + + /// Get the exception handling used with constrained floating point + ConstrainedFPIntrinsic::ExceptionBehavior getDefaultConstrainedExcept() { + return DefaultConstrainedExcept; + } + + /// Get the rounding mode handling used with constrained floating point + ConstrainedFPIntrinsic::RoundingMode getDefaultConstrainedRounding() { + return DefaultConstrainedRounding; + } + //===--------------------------------------------------------------------===// // RAII helpers. //===--------------------------------------------------------------------===// @@ -1045,6 +1081,35 @@ return (LC && RC) ? Insert(Folder.CreateBinOp(Opc, LC, RC), Name) : nullptr; } + Value * + getConstrainedFPRounding(ConstrainedFPIntrinsic::RoundingMode Rounding) { + // ConstrainedRoundingKind UseRounding = DefaultConstrainedRounding; + ConstrainedFPIntrinsic::RoundingMode UseRounding = + DefaultConstrainedRounding; + + if (Rounding != ConstrainedFPIntrinsic::rmUnspecified) + UseRounding = Rounding; + + auto RoundingStr = ConstrainedFPIntrinsic::RoundingModeToStr(UseRounding); + auto *RoundingMDS = MDString::get(Context, RoundingStr); + + return MetadataAsValue::get(Context, RoundingMDS); + } + + Value * + getConstrainedFPExcept(ConstrainedFPIntrinsic::ExceptionBehavior Except) { + ConstrainedFPIntrinsic::ExceptionBehavior UseExcept = + DefaultConstrainedExcept; + + if (Except != ConstrainedFPIntrinsic::ebUnspecified) + UseExcept = Except; + + auto ExceptStr = ConstrainedFPIntrinsic::ExceptionBehaviorToStr(UseExcept); + auto *ExceptMDS = MDString::get(Context, ExceptStr); + + return MetadataAsValue::get(Context, ExceptMDS); + } + public: Value *CreateAdd(Value *LHS, Value *RHS, const Twine &Name = "", bool HasNUW = false, bool HasNSW = false) { @@ -1247,6 +1312,10 @@ Value *CreateFAdd(Value *L, Value *R, const Twine &Name = "", MDNode *FPMD = nullptr) { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fadd, + L, R, nullptr, Name); + if (Value *V = foldConstant(Instruction::FAdd, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFAdd(L, R), FPMD, FMF); return Insert(I, Name); @@ -1256,6 +1325,10 @@ /// default FMF. Value *CreateFAddFMF(Value *L, Value *R, Instruction *FMFSource, const Twine &Name = "") { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fadd, + L, R, FMFSource, Name); + if (Value *V = foldConstant(Instruction::FAdd, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFAdd(L, R), nullptr, FMFSource->getFastMathFlags()); @@ -1264,6 +1337,10 @@ Value *CreateFSub(Value *L, Value *R, const Twine &Name = "", MDNode *FPMD = nullptr) { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fsub, + L, R, nullptr, Name); + if (Value *V = foldConstant(Instruction::FSub, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFSub(L, R), FPMD, FMF); return Insert(I, Name); @@ -1273,6 +1350,10 @@ /// default FMF. Value *CreateFSubFMF(Value *L, Value *R, Instruction *FMFSource, const Twine &Name = "") { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fsub, + L, R, FMFSource, Name); + if (Value *V = foldConstant(Instruction::FSub, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFSub(L, R), nullptr, FMFSource->getFastMathFlags()); @@ -1281,6 +1362,10 @@ Value *CreateFMul(Value *L, Value *R, const Twine &Name = "", MDNode *FPMD = nullptr) { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fmul, + L, R, nullptr, Name); + if (Value *V = foldConstant(Instruction::FMul, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFMul(L, R), FPMD, FMF); return Insert(I, Name); @@ -1290,6 +1375,10 @@ /// default FMF. Value *CreateFMulFMF(Value *L, Value *R, Instruction *FMFSource, const Twine &Name = "") { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fmul, + L, R, FMFSource, Name); + if (Value *V = foldConstant(Instruction::FMul, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFMul(L, R), nullptr, FMFSource->getFastMathFlags()); @@ -1298,6 +1387,10 @@ Value *CreateFDiv(Value *L, Value *R, const Twine &Name = "", MDNode *FPMD = nullptr) { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fdiv, + L, R, nullptr, Name); + if (Value *V = foldConstant(Instruction::FDiv, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFDiv(L, R), FPMD, FMF); return Insert(I, Name); @@ -1307,6 +1400,10 @@ /// default FMF. Value *CreateFDivFMF(Value *L, Value *R, Instruction *FMFSource, const Twine &Name = "") { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_fdiv, + L, R, FMFSource, Name); + if (Value *V = foldConstant(Instruction::FDiv, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFDiv(L, R), nullptr, FMFSource->getFastMathFlags()); @@ -1315,6 +1412,10 @@ Value *CreateFRem(Value *L, Value *R, const Twine &Name = "", MDNode *FPMD = nullptr) { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_frem, + L, R, nullptr, Name); + if (Value *V = foldConstant(Instruction::FRem, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFRem(L, R), FPMD, FMF); return Insert(I, Name); @@ -1324,6 +1425,10 @@ /// default FMF. Value *CreateFRemFMF(Value *L, Value *R, Instruction *FMFSource, const Twine &Name = "") { + if (IsFPConstrained) + return CreateConstrainedFPBinOp(Intrinsic::experimental_constrained_frem, + L, R, FMFSource, Name); + if (Value *V = foldConstant(Instruction::FRem, L, R, Name)) return V; Instruction *I = setFPAttrs(BinaryOperator::CreateFRem(L, R), nullptr, FMFSource->getFastMathFlags()); @@ -1340,6 +1445,21 @@ return Insert(BinOp, Name); } + CallInst * + CreateConstrainedFPBinOp(Intrinsic::ID ID, Value *L, Value *R, + Instruction *FMFSource = nullptr, + const Twine &Name = "", + ConstrainedFPIntrinsic::RoundingMode Rounding = + ConstrainedFPIntrinsic::rmUnspecified, + ConstrainedFPIntrinsic::ExceptionBehavior Except = + ConstrainedFPIntrinsic::ebUnspecified) { + Value *RoundingV = getConstrainedFPRounding(Rounding); + Value *ExceptV = getConstrainedFPExcept(Except); + + return CreateIntrinsic(ID, {L->getType()}, {L, R, RoundingV, ExceptV}, + FMFSource, Name); + } + Value *CreateNeg(Value *V, const Twine &Name = "", bool HasNUW = false, bool HasNSW = false) { if (auto *VC = dyn_cast(V)) Index: include/llvm/IR/IntrinsicInst.h =================================================================== --- include/llvm/IR/IntrinsicInst.h +++ include/llvm/IR/IntrinsicInst.h @@ -208,20 +208,30 @@ /// This is the common base class for constrained floating point intrinsics. class ConstrainedFPIntrinsic : public IntrinsicInst { public: - enum RoundingMode { - rmInvalid, - rmDynamic, - rmToNearest, - rmDownward, - rmUpward, - rmTowardZero + /// Specifies the rounding mode to be assumed. This is only used when + /// when constrained floating point is enabled. See the LLVM Language + /// Reference Manual for details. + enum RoundingMode : uint8_t { + rmUnspecified = 0, ///< Use as a placeholder to not affect rounding + ///< behavior in the IRBuilder. + rmInvalid = 0, + rmDynamic, ///< This corresponds to "fpround.dynamic". + rmToNearest, ///< This corresponds to "fpround.tonearest". + rmDownward, ///< This corresponds to "fpround.downward". + rmUpward, ///< This corresponds to "fpround.upward". + rmTowardZero ///< This corresponds to "fpround.tozero". }; - enum ExceptionBehavior { - ebInvalid, - ebIgnore, - ebMayTrap, - ebStrict + /// Specifies the required exception behavior. This is only used when + /// when constrained floating point is used. See the LLVM Language + /// Reference Manual for details. + enum ExceptionBehavior : uint8_t { + ebUnspecified = 0, ///< Use as a placeholder to not affect exception + ///< behavior in the IRBuilder. + ebInvalid = 0, + ebIgnore, ///< This corresponds to "fpexcept.ignore". + ebMayTrap, ///< This corresponds to "fpexcept.maytrap". + ebStrict ///< This corresponds to "fpexcept.strict". }; bool isUnaryOp() const; @@ -229,6 +239,11 @@ RoundingMode getRoundingMode() const; ExceptionBehavior getExceptionBehavior() const; + static RoundingMode StrToRoundingMode(StringRef); + static StringRef RoundingModeToStr(RoundingMode); + static ExceptionBehavior StrToExceptionBehavior(StringRef); + static StringRef ExceptionBehaviorToStr(ExceptionBehavior); + // Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const IntrinsicInst *I) { switch (I->getIntrinsicID()) { Index: lib/IR/IntrinsicInst.cpp =================================================================== --- lib/IR/IntrinsicInst.cpp +++ lib/IR/IntrinsicInst.cpp @@ -110,8 +110,11 @@ dyn_cast(getArgOperand(NumOperands - 2))->getMetadata(); if (!MD || !isa(MD)) return rmInvalid; - StringRef RoundingArg = cast(MD)->getString(); + return StrToRoundingMode(cast(MD)->getString()); +} +ConstrainedFPIntrinsic::RoundingMode +ConstrainedFPIntrinsic::StrToRoundingMode(StringRef RoundingArg) { // For dynamic rounding mode, we use round to nearest but we will set the // 'exact' SDNodeFlag so that the value will not be rounded. return StringSwitch(RoundingArg) @@ -123,6 +126,30 @@ .Default(rmInvalid); } +StringRef +ConstrainedFPIntrinsic::RoundingModeToStr(RoundingMode UseRounding) { + StringRef RoundingStr; + switch (UseRounding) { + case ConstrainedFPIntrinsic::rmUnspecified: + case ConstrainedFPIntrinsic::rmDynamic: + RoundingStr = "round.dynamic"; + break; + case ConstrainedFPIntrinsic::rmToNearest: + RoundingStr = "round.tonearest"; + break; + case ConstrainedFPIntrinsic::rmDownward: + RoundingStr = "round.downward"; + break; + case ConstrainedFPIntrinsic::rmUpward: + RoundingStr = "round.upward"; + break; + case ConstrainedFPIntrinsic::rmTowardZero: + RoundingStr = "round.tozero"; + break; + } + return RoundingStr; +} + ConstrainedFPIntrinsic::ExceptionBehavior ConstrainedFPIntrinsic::getExceptionBehavior() const { unsigned NumOperands = getNumArgOperands(); @@ -130,7 +157,11 @@ dyn_cast(getArgOperand(NumOperands - 1))->getMetadata(); if (!MD || !isa(MD)) return ebInvalid; - StringRef ExceptionArg = cast(MD)->getString(); + return StrToExceptionBehavior(cast(MD)->getString()); +} + +ConstrainedFPIntrinsic::ExceptionBehavior +ConstrainedFPIntrinsic::StrToExceptionBehavior(StringRef ExceptionArg) { return StringSwitch(ExceptionArg) .Case("fpexcept.ignore", ebIgnore) .Case("fpexcept.maytrap", ebMayTrap) @@ -138,6 +169,24 @@ .Default(ebInvalid); } +StringRef +ConstrainedFPIntrinsic::ExceptionBehaviorToStr(ExceptionBehavior UseExcept) { + StringRef ExceptStr; + switch (UseExcept) { + case ConstrainedFPIntrinsic::ebUnspecified: + case ConstrainedFPIntrinsic::ebStrict: + ExceptStr = "fpexcept.strict"; + break; + case ConstrainedFPIntrinsic::ebIgnore: + ExceptStr = "fpexcept.ignore"; + break; + case ConstrainedFPIntrinsic::ebMayTrap: + ExceptStr = "fpexcept.maytrap"; + break; + } + return ExceptStr; +} + bool ConstrainedFPIntrinsic::isUnaryOp() const { switch (getIntrinsicID()) { default: Index: unittests/IR/IRBuilderTest.cpp =================================================================== --- unittests/IR/IRBuilderTest.cpp +++ unittests/IR/IRBuilderTest.cpp @@ -122,6 +122,70 @@ EXPECT_FALSE(II->hasNoNaNs()); } +TEST_F(IRBuilderTest, ConstrainedFP) { + IRBuilder<> Builder(BB); + Value *V; + CallInst *Call; + IntrinsicInst *II; + + V = Builder.CreateLoad(GV); + + // See if we get constrained intrinsics instead of non-constrained + // instructions. + Builder.setIsFPConstrained(true); + + V = Builder.CreateFAdd(V, V); + ASSERT_TRUE(isa(V)); + II = cast(V); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fadd); + + V = Builder.CreateFSub(V, V); + ASSERT_TRUE(isa(V)); + II = cast(V); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fsub); + + V = Builder.CreateFMul(V, V); + ASSERT_TRUE(isa(V)); + II = cast(V); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fmul); + + V = Builder.CreateFDiv(V, V); + ASSERT_TRUE(isa(V)); + II = cast(V); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_fdiv); + + V = Builder.CreateFRem(V, V); + ASSERT_TRUE(isa(V)); + II = cast(V); + EXPECT_EQ(II->getIntrinsicID(), Intrinsic::experimental_constrained_frem); + + // Verify the codepaths for setting and overriding the default metadata. + V = Builder.CreateFAdd(V, V); + ASSERT_TRUE(isa(V)); + auto *CII = cast(V); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebStrict); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmDynamic); + + Builder.setDefaultConstrainedExcept(ConstrainedFPIntrinsic::ebIgnore); + Builder.setDefaultConstrainedRounding(ConstrainedFPIntrinsic::rmUpward); + V = Builder.CreateFAdd(V, V); + CII = cast(V); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebIgnore); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmUpward); + + // Now override the defaults. + Call = Builder.CreateConstrainedFPBinOp( + Intrinsic::experimental_constrained_fadd, V, V, nullptr, "", + ConstrainedFPIntrinsic::rmDownward, ConstrainedFPIntrinsic::ebMayTrap); + CII = cast(Call); + EXPECT_EQ(CII->getIntrinsicID(), Intrinsic::experimental_constrained_fadd); + ASSERT_TRUE(CII->getExceptionBehavior() == ConstrainedFPIntrinsic::ebMayTrap); + ASSERT_TRUE(CII->getRoundingMode() == ConstrainedFPIntrinsic::rmDownward); + + Builder.CreateRetVoid(); + EXPECT_FALSE(verifyModule(*M)); +} + TEST_F(IRBuilderTest, Lifetime) { IRBuilder<> Builder(BB); AllocaInst *Var1 = Builder.CreateAlloca(Builder.getInt8Ty());