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 @@ -729,6 +729,23 @@ } } + // tan(a) * cos(a) -> sin(a) + if (AllowReassociate) { + Value *A; + bool matchCos = match(Op0, m_Intrinsic(m_Value(A))); + bool matchTan = match(Op1, m_LibFunc (TLI, m_Specific(A))) || + match(Op1, m_LibFunc(TLI, m_Specific(A))) || + match(Op1, m_LibFunc(TLI, m_Specific(A))); + if (matchCos && matchTan) { + BuilderTy::FastMathFlagGuard Guard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Value *Sin = Intrinsic::getDeclaration(I.getModule(), + Intrinsic::sin, I.getType()); + Value *SinCall = Builder.CreateCall(Sin, A); + return replaceInstUsesWith(I, SinCall); + } + } + // sqrt(a) * sqrt(b) -> sqrt(a * b) if (AllowReassociate && Op0->hasOneUse() && Op1->hasOneUse()) { Index: test/Transforms/InstCombine/fmul-tan-cos.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/fmul-tan-cos.ll @@ -0,0 +1,97 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine < %s | FileCheck %s + +define double @fmul_tan_cos(double %a) { +; CHECK-LABEL: @fmul_tan_cos( +; CHECK-NEXT: [[CALL:%.*]] = call double @tan(double [[A:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.cos.f64(double [[A]]) +; CHECK-NEXT: [[MUL:%.*]] = fmul double [[TMP1]], [[CALL]] +; CHECK-NEXT: ret double [[MUL]] +; + %call = call double @tan(double %a) + %1 = call double @llvm.cos.f64(double %a) + %mul = fmul double %1, %call + ret double %mul +} + +define double @fmul_strict_tan_strict_cos_fast(double %a) { +; CHECK-LABEL: @fmul_strict_tan_strict_cos_fast( +; CHECK-NEXT: [[CALL:%.*]] = call double @tan(double [[A:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = call fast double @llvm.cos.f64(double [[A]]) +; CHECK-NEXT: [[MUL:%.*]] = fmul double [[TMP1]], [[CALL]] +; CHECK-NEXT: ret double [[MUL]] +; + %call = call double @tan(double %a) + %1 = call fast double @llvm.cos.f64(double %a) + %mul = fmul double %1, %call + ret double %mul +} + +define double @fmul_strict_tan_fast_cos_fast(double %a) { +; CHECK-LABEL: @fmul_strict_tan_fast_cos_fast( +; CHECK-NEXT: [[CALL:%.*]] = call fast double @tan(double [[A:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = call fast double @llvm.cos.f64(double [[A]]) +; CHECK-NEXT: [[MUL:%.*]] = fmul double [[TMP1]], [[CALL]] +; CHECK-NEXT: ret double [[MUL]] +; + %call = call fast double @tan(double %a) + %1 = call fast double @llvm.cos.f64(double %a) + %mul = fmul double %1, %call + ret double %mul +} + +define double @fmul_fast_tan_strict_cos_strict(double %a) { +; CHECK-LABEL: @fmul_fast_tan_strict_cos_strict( +; CHECK-NEXT: [[CALL:%.*]] = call double @tan(double [[A:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = call fast double @llvm.sin.f64(double [[A]]) +; CHECK-NEXT: ret double [[TMP1]] +; + %call = call double @tan(double %a) + %1 = call double @llvm.cos.f64(double %a) + %mul = fmul fast double %1, %call + ret double %mul +} + +define double @fmul_tan_cos_fast(double %a) { +; CHECK-LABEL: @fmul_tan_cos_fast( +; CHECK-NEXT: [[CALL:%.*]] = call fast double @tan(double [[A:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = call fast double @llvm.sin.f64(double [[A]]) +; CHECK-NEXT: ret double [[TMP1]] +; + %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 +} + +define float @fmul_tanf_cosf_fast(float %a) { +; CHECK-LABEL: @fmul_tanf_cosf_fast( +; CHECK-NEXT: [[CALL:%.*]] = call fast float @tanf(float [[A:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = call fast float @llvm.sin.f32(float [[A]]) +; CHECK-NEXT: ret float [[TMP1]] +; + %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 +} + +define fp128 @fmul_tanl_cosl_fast(fp128 %a) { +; CHECK-LABEL: @fmul_tanl_cosl_fast( +; CHECK-NEXT: [[CALL:%.*]] = call fast fp128 @tanl(fp128 [[A:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = call fast fp128 @llvm.sin.f128(fp128 [[A]]) +; CHECK-NEXT: ret fp128 [[TMP1]] +; + %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.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)