Index: llvm/trunk/docs/LangRef.rst =================================================================== --- llvm/trunk/docs/LangRef.rst +++ llvm/trunk/docs/LangRef.rst @@ -2194,6 +2194,10 @@ Allow Reciprocal - Allow optimizations to use the reciprocal of an argument rather than perform division. +``contract`` + Allow floating-point contraction (e.g. fusing a multiply followed by an + addition into a fused multiply-and-add). + ``fast`` Fast - Allow algebraically equivalent transformations that may dramatically change results in floating point (e.g. reassociate). This Index: llvm/trunk/include/llvm/IR/Instruction.h =================================================================== --- llvm/trunk/include/llvm/IR/Instruction.h +++ llvm/trunk/include/llvm/IR/Instruction.h @@ -345,6 +345,9 @@ /// Determine whether the allow-reciprocal flag is set. bool hasAllowReciprocal() const; + /// Determine whether the allow-contract flag is set. + bool hasAllowContract() const; + /// Convenience function for getting all the fast-math flags, which must be an /// operator which supports these flags. See LangRef.html for the meaning of /// these flags. Index: llvm/trunk/include/llvm/IR/Operator.h =================================================================== --- llvm/trunk/include/llvm/IR/Operator.h +++ llvm/trunk/include/llvm/IR/Operator.h @@ -172,12 +172,15 @@ FastMathFlags(unsigned F) : Flags(F) { } public: + /// This is how the bits are used in Value::SubclassOptionalData so they + /// should fit there too. enum { UnsafeAlgebra = (1 << 0), NoNaNs = (1 << 1), NoInfs = (1 << 2), NoSignedZeros = (1 << 3), - AllowReciprocal = (1 << 4) + AllowReciprocal = (1 << 4), + AllowContract = (1 << 5) }; FastMathFlags() = default; @@ -193,6 +196,7 @@ bool noInfs() const { return 0 != (Flags & NoInfs); } bool noSignedZeros() const { return 0 != (Flags & NoSignedZeros); } bool allowReciprocal() const { return 0 != (Flags & AllowReciprocal); } + bool allowContract() const { return 0 != (Flags & AllowContract); } bool unsafeAlgebra() const { return 0 != (Flags & UnsafeAlgebra); } /// Flag setters @@ -200,12 +204,16 @@ void setNoInfs() { Flags |= NoInfs; } void setNoSignedZeros() { Flags |= NoSignedZeros; } void setAllowReciprocal() { Flags |= AllowReciprocal; } + void setAllowContract(bool B) { + Flags = (Flags & ~AllowContract) | B * AllowContract; + } void setUnsafeAlgebra() { Flags |= UnsafeAlgebra; setNoNaNs(); setNoInfs(); setNoSignedZeros(); setAllowReciprocal(); + setAllowContract(true); } void operator&=(const FastMathFlags &OtherFlags) { @@ -257,6 +265,12 @@ (B * FastMathFlags::AllowReciprocal); } + void setHasAllowContract(bool B) { + SubclassOptionalData = + (SubclassOptionalData & ~FastMathFlags::AllowContract) | + (B * FastMathFlags::AllowContract); + } + /// Convenience function for setting multiple fast-math flags. /// FMF is a mask of the bits to set. void setFastMathFlags(FastMathFlags FMF) { @@ -300,6 +314,12 @@ return (SubclassOptionalData & FastMathFlags::AllowReciprocal) != 0; } + /// Test whether this operation is permitted to + /// be floating-point contracted. + bool hasAllowContract() const { + return (SubclassOptionalData & FastMathFlags::AllowContract) != 0; + } + /// Convenience function for getting all the fast-math flags FastMathFlags getFastMathFlags() const { return FastMathFlags(SubclassOptionalData); Index: llvm/trunk/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLLexer.cpp +++ llvm/trunk/lib/AsmParser/LLLexer.cpp @@ -548,6 +548,7 @@ KEYWORD(ninf); KEYWORD(nsz); KEYWORD(arcp); + KEYWORD(contract); KEYWORD(fast); KEYWORD(nuw); KEYWORD(nsw); Index: llvm/trunk/lib/AsmParser/LLParser.h =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.h +++ llvm/trunk/lib/AsmParser/LLParser.h @@ -193,6 +193,10 @@ case lltok::kw_ninf: FMF.setNoInfs(); Lex.Lex(); continue; case lltok::kw_nsz: FMF.setNoSignedZeros(); Lex.Lex(); continue; case lltok::kw_arcp: FMF.setAllowReciprocal(); Lex.Lex(); continue; + case lltok::kw_contract: + FMF.setAllowContract(true); + Lex.Lex(); + continue; default: return FMF; } return FMF; Index: llvm/trunk/lib/AsmParser/LLToken.h =================================================================== --- llvm/trunk/lib/AsmParser/LLToken.h +++ llvm/trunk/lib/AsmParser/LLToken.h @@ -98,6 +98,7 @@ kw_ninf, kw_nsz, kw_arcp, + kw_contract, kw_fast, kw_nuw, kw_nsw, Index: llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp @@ -971,6 +971,8 @@ FMF.setNoSignedZeros(); if (0 != (Val & FastMathFlags::AllowReciprocal)) FMF.setAllowReciprocal(); + if (0 != (Val & FastMathFlags::AllowContract)) + FMF.setAllowContract(true); return FMF; } Index: llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1336,6 +1336,8 @@ Flags |= FastMathFlags::NoSignedZeros; if (FPMO->hasAllowReciprocal()) Flags |= FastMathFlags::AllowReciprocal; + if (FPMO->hasAllowContract()) + Flags |= FastMathFlags::AllowContract; } return Flags; Index: llvm/trunk/lib/IR/AsmWriter.cpp =================================================================== --- llvm/trunk/lib/IR/AsmWriter.cpp +++ llvm/trunk/lib/IR/AsmWriter.cpp @@ -1073,6 +1073,8 @@ Out << " nsz"; if (FPO->hasAllowReciprocal()) Out << " arcp"; + if (FPO->hasAllowContract()) + Out << " contract"; } } Index: llvm/trunk/lib/IR/Instruction.cpp =================================================================== --- llvm/trunk/lib/IR/Instruction.cpp +++ llvm/trunk/lib/IR/Instruction.cpp @@ -204,6 +204,11 @@ return cast(this)->hasAllowReciprocal(); } +bool Instruction::hasAllowContract() const { + assert(isa(this) && "getting fast-math flag on invalid op"); + return cast(this)->hasAllowContract(); +} + FastMathFlags Instruction::getFastMathFlags() const { assert(isa(this) && "getting fast-math flag on invalid op"); return cast(this)->getFastMathFlags(); Index: llvm/trunk/test/Assembler/fast-math-flags.ll =================================================================== --- llvm/trunk/test/Assembler/fast-math-flags.ll +++ llvm/trunk/test/Assembler/fast-math-flags.ll @@ -74,6 +74,18 @@ ret float %e } +; CHECK: @contract( +define float @contract(float %x, float %y) { +entry: +; CHECK: %a = fsub contract float %x, %y + %a = fsub contract float %x, %y +; CHECK: %b = fadd contract float %x, %y + %b = fadd contract float %x, %y +; CHECK: %c = fmul contract float %a, %b + %c = fmul contract float %a, %b + ret float %c +} + ; CHECK: no_nan_inf define float @no_nan_inf(float %x, float %y) { entry: Index: llvm/trunk/test/Bitcode/compatibility.ll =================================================================== --- llvm/trunk/test/Bitcode/compatibility.ll +++ llvm/trunk/test/Bitcode/compatibility.ll @@ -760,6 +760,8 @@ ; CHECK: %f.nsz = fadd nsz float %op1, %op2 %f.arcp = fadd arcp float %op1, %op2 ; CHECK: %f.arcp = fadd arcp float %op1, %op2 + %f.contract = fadd contract float %op1, %op2 + ; CHECK: %f.contract = fadd contract float %op1, %op2 %f.fast = fadd fast float %op1, %op2 ; CHECK: %f.fast = fadd fast float %op1, %op2 ret void Index: llvm/trunk/unittests/IR/IRBuilderTest.cpp =================================================================== --- llvm/trunk/unittests/IR/IRBuilderTest.cpp +++ llvm/trunk/unittests/IR/IRBuilderTest.cpp @@ -207,7 +207,26 @@ EXPECT_TRUE(FCmp->hasAllowReciprocal()); Builder.clearFastMathFlags(); - + + // Test FP-contract + FC = Builder.CreateFAdd(F, F); + ASSERT_TRUE(isa(FC)); + FAdd = cast(FC); + EXPECT_FALSE(FAdd->hasAllowContract()); + + FMF.clear(); + FMF.setAllowContract(true); + Builder.setFastMathFlags(FMF); + + FC = Builder.CreateFAdd(F, F); + EXPECT_TRUE(Builder.getFastMathFlags().any()); + EXPECT_TRUE(Builder.getFastMathFlags().AllowContract); + ASSERT_TRUE(isa(FC)); + FAdd = cast(FC); + EXPECT_TRUE(FAdd->hasAllowContract()); + + Builder.clearFastMathFlags(); + // Test a call with FMF. auto CalleeTy = FunctionType::get(Type::getFloatTy(Ctx), /*isVarArg=*/false);