Index: lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -701,6 +701,43 @@ } } + if (AllowReassociate) { + Value *A0 = nullptr; + Value *B0 = nullptr; + Value *A1 = nullptr; + Value *B1 = nullptr; + BuilderTy::FastMathFlagGuard Guard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Value *Pow = Intrinsic::getDeclaration(I.getModule(), + Intrinsic::pow, I.getType()); + // pow(A, B) * A -> pow(A, B+1) + if (match(Op0, m_Intrinsic(m_Value(A0), m_Value(B0))) && + A0 == Op1) { + Value *One = ConstantFP::get(Op0->getType(), 1.0); + Value *FAdd = Builder.CreateFAdd(B0, One); + Value *PowCall = Builder.CreateCall(Pow, { A0, FAdd }); + return replaceInstUsesWith(I, PowCall); + } + + // pow(A, B) * pow(C, B) -> pow(A*C, B) + if (match(Op0, m_Intrinsic(m_Value(A0), m_Value(B0))) && + match(Op1, m_Intrinsic(m_Value(A1), m_Value(B1))) && + B0 == B1) { + Value *AC = Builder.CreateFMul(A0, A1); + Value *PowCall = Builder.CreateCall(Pow, { AC, B0 }); + return replaceInstUsesWith(I, PowCall); + } + + // pow(A, B) * pow(A, C) -> pow(A, B+C) + if (match(Op0, m_Intrinsic(m_Value(A0), m_Value(B0))) && + match(Op1, m_Intrinsic(m_Value(A1), m_Value(B1))) && + A0 == A1) { + Value *BC = Builder.CreateFAdd(B0, B1); + Value *PowCall = Builder.CreateCall(Pow, { A0, BC }); + return replaceInstUsesWith(I, PowCall); + } + } + // Under unsafe algebra do: // X * log2(0.5*Y) = X*log2(Y) - X if (AllowReassociate) { Index: test/Transforms/InstCombine/fmul-pow.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/fmul-pow.ll @@ -0,0 +1,76 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine < %s | FileCheck %s + +define double @pow_ab_x_a(double %a, double %b) { +; CHECK-LABEL: @pow_ab_x_a( +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.pow.f64(double [[A:%.*]], double [[B:%.*]]) +; CHECK-NEXT: [[MUL:%.*]] = fmul double [[TMP1]], [[A]] +; CHECK-NEXT: ret double [[MUL]] +; + %1 = call double @llvm.pow.f64(double %a, double %b) + %mul = fmul double %1, %a + ret double %mul +} + +define double @pow_ab_x_a_fast(double %a, double %b) { +; CHECK-LABEL: @pow_ab_x_a_fast( +; CHECK-NEXT: [[TMP1:%.*]] = fadd fast double [[B:%.*]], 1.000000e+00 +; CHECK-NEXT: [[TMP2:%.*]] = call fast double @llvm.pow.f64(double [[A:%.*]], double [[TMP1]]) +; CHECK-NEXT: ret double [[TMP2]] +; + %1 = call fast double @llvm.pow.f64(double %a, double %b) + %mul = fmul fast double %1, %a + ret double %mul +} + +declare double @llvm.pow.f64(double, double) + +define double @pow_ab_x_pow_cb(double %a, double %b, double %c) { +; CHECK-LABEL: @pow_ab_x_pow_cb( +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.pow.f64(double [[A:%.*]], double [[B:%.*]]) +; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.pow.f64(double [[C:%.*]], double [[B]]) +; CHECK-NEXT: [[MUL:%.*]] = fmul double [[TMP2]], [[TMP1]] +; CHECK-NEXT: ret double [[MUL]] +; + %1 = call double @llvm.pow.f64(double %a, double %b) + %2 = call double @llvm.pow.f64(double %c, double %b) + %mul = fmul double %2, %1 + ret double %mul +} + +define double @pow_ab_x_pow_cb_fast(double %a, double %b, double %c) { +; CHECK-LABEL: @pow_ab_x_pow_cb_fast( +; CHECK-NEXT: [[TMP1:%.*]] = fmul fast double [[C:%.*]], [[A:%.*]] +; CHECK-NEXT: [[TMP2:%.*]] = call fast double @llvm.pow.f64(double [[TMP1]], double [[B:%.*]]) +; CHECK-NEXT: ret double [[TMP2]] +; + %1 = call fast double @llvm.pow.f64(double %a, double %b) + %2 = call fast double @llvm.pow.f64(double %c, double %b) + %mul = fmul fast double %2, %1 + ret double %mul +} + +define double @pow_ab_x_pow_ac(double %a, double %b, double %c) { +; CHECK-LABEL: @pow_ab_x_pow_ac( +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.pow.f64(double [[A:%.*]], double [[B:%.*]]) +; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.pow.f64(double [[A]], double [[C:%.*]]) +; CHECK-NEXT: [[MUL:%.*]] = fmul double [[TMP2]], [[TMP1]] +; CHECK-NEXT: ret double [[MUL]] +; + %1 = call double @llvm.pow.f64(double %a, double %b) + %2 = call double @llvm.pow.f64(double %a, double %c) + %mul = fmul double %2, %1 + ret double %mul +} + +define double @pow_ab_x_pow_ac_fast(double %a, double %b, double %c) { +; CHECK-LABEL: @pow_ab_x_pow_ac_fast( +; CHECK-NEXT: [[TMP1:%.*]] = fadd fast double [[C:%.*]], [[B:%.*]] +; CHECK-NEXT: [[TMP2:%.*]] = call fast double @llvm.pow.f64(double [[A:%.*]], double [[TMP1]]) +; CHECK-NEXT: ret double [[TMP2]] +; + %1 = call fast double @llvm.pow.f64(double %a, double %b) + %2 = call fast double @llvm.pow.f64(double %a, double %c) + %mul = fmul fast double %2, %1 + ret double %mul +}