Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ 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: include/llvm/IR/Instruction.h =================================================================== --- include/llvm/IR/Instruction.h +++ include/llvm/IR/Instruction.h @@ -333,6 +333,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: include/llvm/IR/Operator.h =================================================================== --- include/llvm/IR/Operator.h +++ include/llvm/IR/Operator.h @@ -174,12 +174,15 @@ FastMathFlags(unsigned F) : Flags(F) { } public: + /// This is how the bits are used in Value::SubclassData 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; @@ -195,6 +198,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 @@ -202,12 +206,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) { @@ -259,6 +267,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) { @@ -302,6 +316,12 @@ return (SubclassOptionalData & FastMathFlags::AllowReciprocal) != 0; } + /// Test whether this operation is permitted to + /// be floating-point contracted, aka the 'C' fast-math property. + bool hasAllowContract() const { + return (SubclassOptionalData & FastMathFlags::AllowContract) != 0; + } + /// Convenience function for getting all the fast-math flags FastMathFlags getFastMathFlags() const { return FastMathFlags(SubclassOptionalData); Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -548,6 +548,7 @@ KEYWORD(ninf); KEYWORD(nsz); KEYWORD(arcp); + KEYWORD(contract); KEYWORD(fast); KEYWORD(nuw); KEYWORD(nsw); Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ 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: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -98,6 +98,7 @@ kw_ninf, kw_nsz, kw_arcp, + kw_contract, kw_fast, kw_nuw, kw_nsw, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ 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: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1326,6 +1326,8 @@ Flags |= FastMathFlags::NoSignedZeros; if (FPMO->hasAllowReciprocal()) Flags |= FastMathFlags::AllowReciprocal; + if (FPMO->hasAllowContract()) + Flags |= FastMathFlags::AllowContract; } return Flags; Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -1073,6 +1073,8 @@ Out << " nsz"; if (FPO->hasAllowReciprocal()) Out << " arcp"; + if (FPO->hasAllowContract()) + Out << " contract"; } } Index: lib/IR/Instruction.cpp =================================================================== --- lib/IR/Instruction.cpp +++ lib/IR/Instruction.cpp @@ -209,6 +209,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: test/Assembler/fast-math-flags.ll =================================================================== --- test/Assembler/fast-math-flags.ll +++ 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: test/Bitcode/compatibility.ll =================================================================== --- test/Bitcode/compatibility.ll +++ 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: unittests/IR/IRBuilderTest.cpp =================================================================== --- unittests/IR/IRBuilderTest.cpp +++ 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);