Index: include/llvm/IR/PatternMatch.h =================================================================== --- include/llvm/IR/PatternMatch.h +++ include/llvm/IR/PatternMatch.h @@ -31,6 +31,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" @@ -1436,6 +1437,89 @@ return m_Intrinsic(Op0, Op1); } +/// \brief LibFunc matchers. +struct LibFunc_match { + LibFunc F; + TargetLibraryInfo TLI; + + LibFunc_match(LibFunc Func, TargetLibraryInfo TargetLI) + : F(Func), TLI(TargetLI) {} + + template bool match(OpTy *V) { + if (const auto *CI = dyn_cast(V)) + if (const auto *CalledF = CI->getCalledFunction()) + if (TLI.has(F)) + return TLI.getName(F) == CalledF->getName(); + return false; + } +}; + +/// LibFunc matches are combinations of Name matchers, and argument +/// matchers. +template +struct m_LibFunc_Ty; +template struct m_LibFunc_Ty { + using Ty = match_combine_and>; +}; +template struct m_LibFunc_Ty { + using Ty = + match_combine_and::Ty, + Argument_match>; +}; +template +struct m_LibFunc_Ty { + using Ty = + match_combine_and::Ty, + Argument_match>; +}; +template +struct m_LibFunc_Ty { + using Ty = + match_combine_and::Ty, + Argument_match>; +}; + +/// \brief Match LibFunc calls like this: +/// m_LibFunc(m_Value(X)) +template +inline LibFunc_match m_LibFunc(TargetLibraryInfo TLI) { + return LibFunc_match(F, TLI); +} + +template +inline typename m_LibFunc_Ty::Ty +m_LibFunc(const TargetLibraryInfo TLI, const T0 &Op0) { + return m_CombineAnd(m_LibFunc(TLI), m_Argument<0>(Op0)); +} + +template +inline typename m_LibFunc_Ty::Ty +m_LibFunc(const TargetLibraryInfo TLI, const T0 &Op0, const T0 &Op1) { + return m_CombineAnd(m_LibFunc(TLI, Op0), m_Argument<1>(Op1)); +} + +template +inline typename m_LibFunc_Ty::Ty +m_LibFunc(const TargetLibraryInfo TLI, + const T0 &Op0, + const T0 &Op1, + const T0 &Op2) { + return m_CombineAnd(m_LibFunc(TLI, Op0, Op1), m_Argument<2>(Op2)); +} + +template +inline typename m_LibFunc_Ty::Ty +m_LibFunc(const TargetLibraryInfo TLI, + const T0 &Op0, + const T0 &Op1, + const T0 &Op2, + const T0 &Op3) { + return m_CombineAnd(m_LibFunc(TLI, Op0, Op1, Op2), m_Argument<3>(Op3)); +} + template struct Signum_match { Opnd_t Val; Signum_match(const Opnd_t &V) : Val(V) {} Index: lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -701,6 +701,24 @@ } } + // tan(a) * cos(a) -> sin(a) + if (AllowReassociate) { + Value *Tan = nullptr; + Value *Cos = nullptr; + bool matchCos = match(Op0, m_Intrinsic(m_Value(Cos))); + bool matchTan = match(Op1, m_LibFunc(TLI, m_Value(Tan))) || + match(Op1, m_LibFunc(TLI, m_Value(Tan))) || + match(Op1, m_LibFunc(TLI, m_Value(Tan))); + if (matchCos && matchTan && (Tan == Cos)) { + BuilderTy::FastMathFlagGuard Guard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Value *Sin = Intrinsic::getDeclaration(I.getModule(), + Intrinsic::sin, I.getType()); + Value *SinCall = Builder.CreateCall(Sin, Cos); + return replaceInstUsesWith(I, SinCall); + } + } + // Under unsafe algebra do: // X * log2(0.5*Y) = X*log2(Y) - X if (AllowReassociate) { Index: test/Transforms/InstCombine/fmul.ll =================================================================== --- test/Transforms/InstCombine/fmul.ll +++ test/Transforms/InstCombine/fmul.ll @@ -181,3 +181,51 @@ %mul = fmul float %x.fabs, %y.fabs ret float %mul } + +; CHECK-LABEL @tan_x_cos( +; CHECK: call fast double @llvm.sin.f64(double %a) +define double @tan_x_cos(double %a) { + %call = call double @tan(double %a) + %1 = call double @llvm.cos.f64(double %a) + %mul = fmul double %1, %call + ret double %mul +} + +; CHECK-LABEL @tan_x_cos_fast( +; CHECK: call fast double @llvm.sin.f64(double %a) +define double @tan_x_cos_fast(double %a) { + %call = call fast double @tan(double %a) + %1 = call fast double @llvm.cos.f64(double %a) + %mul = fmul fast double %1, %call + ret double %mul +} + +; CHECK-LABEL @tanf_x_cosf_fast( +; CHECK: call fast float @llvm.sin.f32(float %a) +define float @tanf_x_cosf_fast(float %a) { + %call = call fast float @tanf(float %a) + %1 = call fast float @llvm.cos.f32(float %a) + %mul = fmul fast float %1, %call + ret float %mul +} + +; CHECK-LABEL @tanl_x_cosl_fast( +; CHECK: call fast fp128 @llvm.sin.fp128(fp128 %a) +define fp128 @tanl_x_cosl_fast(fp128 %a) { + %call = call fast fp128 @tanl(fp128 %a) + %1 = call fast fp128 @llvm.cos.fp128(fp128 %a) + %mul = fmul fast fp128 %1, %call + ret fp128 %mul +} + +declare double @llvm.sin.f64(double) +declare float @llvm.sin.f32(float) +declare fp128 @llvm.sin.fp128(fp128) + +declare double @llvm.cos.f64(double) +declare float @llvm.cos.f32(float) +declare fp128 @llvm.cos.fp128(fp128) + +declare double @tan(double) +declare float @tanf(float) +declare fp128 @tanl(fp128)