Index: lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -701,6 +701,37 @@ } } + if (AllowReassociate) { + Value *A, *B, *C; + 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_Specific(Op1), m_Value(B)))) { + Value *One = ConstantFP::get(Op0->getType(), 1.0); + Value *FAdd = Builder.CreateFAdd(B, One); + Value *PowCall = Builder.CreateCall(Pow, { Op1, FAdd }); + return replaceInstUsesWith(I, PowCall); + } + + // pow(A, B) * pow(C, B) -> pow(A*C, B) + if (match(Op0, m_Intrinsic(m_Value(A), m_Value(B))) && + match(Op1, m_Intrinsic(m_Value(C), m_Specific(B)))) { + Value *AC = Builder.CreateFMul(A, C); + Value *PowCall = Builder.CreateCall(Pow, { AC, B }); + return replaceInstUsesWith(I, PowCall); + } + + // pow(A, B) * pow(A, C) -> pow(A, B+C) + if (match(Op0, m_Intrinsic(m_Value(A), m_Value(B))) && + match(Op1, m_Intrinsic(m_Specific(A), m_Value(C)))) { + Value *BC = Builder.CreateFAdd(B, C); + Value *PowCall = Builder.CreateCall(Pow, { A, BC }); + return replaceInstUsesWith(I, PowCall); + } + } + // Under unsafe algebra do: // X * log2(0.5*Y) = X*log2(Y) - X if (AllowReassociate) { @@ -1478,6 +1509,37 @@ return &I; } + if (AllowReassociate) { + Value *A, *B, *C; + 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_Specific(Op1), m_Value(B)))) { + Value *One = ConstantFP::get(Op0->getType(), 1.0); + Value *FSub = Builder.CreateFSub(B, One); + Value *PowCall = Builder.CreateCall(Pow, { Op1, FSub }); + return replaceInstUsesWith(I, PowCall); + } + + // pow(A, B) / pow(C, B) -> pow(A/C, B) + if (match(Op0, m_Intrinsic(m_Value(A), m_Value(B))) && + match(Op1, m_Intrinsic(m_Value(C), m_Specific(B)))) { + Value *AC = Builder.CreateFDiv(A, C); + Value *PowCall = Builder.CreateCall(Pow, { AC, B }); + return replaceInstUsesWith(I, PowCall); + } + + // pow(A, B) / pow(A, C) -> pow(A, B-C) + if (match(Op0, m_Intrinsic(m_Value(A), m_Value(B))) && + match(Op1, m_Intrinsic(m_Specific(A), m_Value(C)))) { + Value *BC = Builder.CreateFSub(B, C); + Value *PowCall = Builder.CreateCall(Pow, { A, BC }); + return replaceInstUsesWith(I, PowCall); + } + } + return nullptr; } Index: test/Transforms/InstCombine/fdiv-pow.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/fdiv-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 @fdiv_pow_ab_a(double %a, double %b) { +; CHECK-LABEL: @fdiv_pow_ab_a( +; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.pow.f64(double [[A:%.*]], double [[B:%.*]]) +; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[A]] +; CHECK-NEXT: ret double [[DIV]] +; + %1 = call double @llvm.pow.f64(double %a, double %b) + %div = fdiv double %1, %a + ret double %div +} + +define double @fdiv_pow_ab_a_fast(double %a, double %b) { +; CHECK-LABEL: @fdiv_pow_ab_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) + %div = fdiv fast double %1, %a + ret double %div +} + +define double @fdiv_pow_ab_pow_cb(double %a, double %b, double %c) { +; CHECK-LABEL: @fdiv_pow_ab_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: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]] +; CHECK-NEXT: ret double [[DIV]] +; + %1 = call double @llvm.pow.f64(double %a, double %b) + %2 = call double @llvm.pow.f64(double %c, double %b) + %div = fdiv double %1, %2 + ret double %div +} + +define double @fdiv_pow_ab_pow_cb_fast(double %a, double %b, double %c) { +; CHECK-LABEL: @fdiv_pow_ab_pow_cb_fast( +; CHECK-NEXT: [[TMP1:%.*]] = fdiv fast double [[A:%.*]], [[C:%.*]] +; 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) + %div = fdiv fast double %1, %2 + ret double %div +} + +define double @fdiv_pow_ab_pow_ac(double %a, double %b, double %c) { +; CHECK-LABEL: @fdiv_pow_ab_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: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]] +; CHECK-NEXT: ret double [[DIV]] +; + %1 = call double @llvm.pow.f64(double %a, double %b) + %2 = call double @llvm.pow.f64(double %a, double %c) + %div = fdiv double %1, %2 + ret double %div +} + +define double @fdiv_pow_ab_x_pow_ac_fast(double %a, double %b, double %c) { +; CHECK-LABEL: @fdiv_pow_ab_x_pow_ac_fast( +; CHECK-NEXT: [[TMP1:%.*]] = fsub fast double [[B:%.*]], [[C:%.*]] +; 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) + %div = fdiv fast double %1, %2 + ret double %div +} + +declare double @llvm.pow.f64(double, double) 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 +} + +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 +} + +declare double @llvm.pow.f64(double, double)