From: Dmitry Venikov Date: Thu, 31 Jan 2019 06:28:10 +0000 (+0000) Subject: [InstCombine] Missed optimization in math expression: simplify calls exp functions X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0027120a34d2a31314f03c7018030bf43eef87af;p=llvm [InstCombine] Missed optimization in math expression: simplify calls exp functions Summary: This patch enables folding following expressions under -ffast-math flag: exp(X) * exp(Y) -> exp(X + Y), exp2(X) * exp2(Y) -> exp2(X + Y). Motivation: https://bugs.llvm.org/show_bug.cgi?id=35594 Reviewers: hfinkel, spatel, efriedma, lebedev.ri Reviewed By: spatel, lebedev.ri Subscribers: lebedev.ri, llvm-commits Differential Revision: https://reviews.llvm.org/D41342 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@352730 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp index ca67348d05c..ae3f081f508 100644 --- a/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ b/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -441,6 +441,26 @@ Instruction *InstCombiner::visitFMul(BinaryOperator &I) { return replaceInstUsesWith(I, Sqrt); } + // exp(X) * exp(Y) -> exp(X + Y) + // Match as long as at least one of exp has only one use. + if (match(Op0, m_Intrinsic(m_Value(X))) && + match(Op1, m_Intrinsic(m_Value(Y))) && + (Op0->hasOneUse() || Op1->hasOneUse())) { + Value *XY = Builder.CreateFAddFMF(X, Y, &I); + Value *Exp = Builder.CreateUnaryIntrinsic(Intrinsic::exp, XY, &I); + return replaceInstUsesWith(I, Exp); + } + + // exp2(X) * exp2(Y) -> exp2(X + Y) + // Match as long as at least one of exp2 has only one use. + if (match(Op0, m_Intrinsic(m_Value(X))) && + match(Op1, m_Intrinsic(m_Value(Y))) && + (Op0->hasOneUse() || Op1->hasOneUse())) { + Value *XY = Builder.CreateFAddFMF(X, Y, &I); + Value *Exp2 = Builder.CreateUnaryIntrinsic(Intrinsic::exp2, XY, &I); + return replaceInstUsesWith(I, Exp2); + } + // (X*Y) * X => (X*X) * Y where Y != X // The purpose is two-fold: // 1) to form a power expression (of X). diff --git a/test/Transforms/InstCombine/fmul-exp.ll b/test/Transforms/InstCombine/fmul-exp.ll index 50f9245479b..28542f45294 100644 --- a/test/Transforms/InstCombine/fmul-exp.ll +++ b/test/Transforms/InstCombine/fmul-exp.ll @@ -21,11 +21,11 @@ define double @exp_a_exp_b(double %a, double %b) { ; exp(a) * exp(b) reassoc, multiple uses define double @exp_a_exp_b_multiple_uses(double %a, double %b) { ; CHECK-LABEL: @exp_a_exp_b_multiple_uses( -; CHECK-NEXT: [[TMP:%.*]] = call double @llvm.exp.f64(double [[A:%.*]]) ; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.exp.f64(double [[B:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul reassoc double [[TMP]], [[TMP1]] +; CHECK-NEXT: [[TMP:%.*]] = fadd reassoc double [[A:%.*]], [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.exp.f64(double [[TMP]]) ; CHECK-NEXT: call void @use(double [[TMP1]]) -; CHECK-NEXT: ret double [[MUL]] +; CHECK-NEXT: ret double [[TMP2]] ; %tmp = call double @llvm.exp.f64(double %a) %tmp1 = call double @llvm.exp.f64(double %b) @@ -55,10 +55,9 @@ define double @exp_a_exp_b_multiple_uses_both(double %a, double %b) { ; exp(a) * exp(b) => exp(a+b) with reassoc define double @exp_a_exp_b_reassoc(double %a, double %b) { ; CHECK-LABEL: @exp_a_exp_b_reassoc( -; CHECK-NEXT: [[TMP:%.*]] = call double @llvm.exp.f64(double [[A:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.exp.f64(double [[B:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul reassoc double [[TMP]], [[TMP1]] -; CHECK-NEXT: ret double [[MUL]] +; CHECK-NEXT: [[TMP:%.*]] = fadd reassoc double [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call reassoc double @llvm.exp.f64(double [[TMP]]) +; CHECK-NEXT: ret double [[TMP1]] ; %tmp = call double @llvm.exp.f64(double %a) %tmp1 = call double @llvm.exp.f64(double %b) @@ -69,14 +68,11 @@ define double @exp_a_exp_b_reassoc(double %a, double %b) { ; exp(a) * exp(b) * exp(c) * exp(d) => exp(a+b+c+d) with reassoc define double @exp_a_exp_b_exp_c_exp_d_fast(double %a, double %b, double %c, double %d) { ; CHECK-LABEL: @exp_a_exp_b_exp_c_exp_d_fast( -; CHECK-NEXT: [[TMP:%.*]] = call double @llvm.exp.f64(double [[A:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.exp.f64(double [[B:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul reassoc double [[TMP]], [[TMP1]] -; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.exp.f64(double [[C:%.*]]) -; CHECK-NEXT: [[MUL1:%.*]] = fmul reassoc double [[MUL]], [[TMP2]] -; CHECK-NEXT: [[TMP3:%.*]] = call double @llvm.exp.f64(double [[D:%.*]]) -; CHECK-NEXT: [[MUL2:%.*]] = fmul reassoc double [[MUL1]], [[TMP3]] -; CHECK-NEXT: ret double [[MUL2]] +; CHECK-NEXT: [[TMP:%.*]] = fadd reassoc double [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = fadd reassoc double [[TMP]], [[C:%.*]] +; CHECK-NEXT: [[TMP2:%.*]] = fadd reassoc double [[TMP1]], [[D:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = call reassoc double @llvm.exp.f64(double [[TMP2]]) +; CHECK-NEXT: ret double [[TMP3]] ; %tmp = call double @llvm.exp.f64(double %a) %tmp1 = call double @llvm.exp.f64(double %b) diff --git a/test/Transforms/InstCombine/fmul-exp2.ll b/test/Transforms/InstCombine/fmul-exp2.ll index d65c32e7d0c..f09013847bb 100644 --- a/test/Transforms/InstCombine/fmul-exp2.ll +++ b/test/Transforms/InstCombine/fmul-exp2.ll @@ -21,11 +21,11 @@ define double @exp2_a_exp2_b(double %a, double %b) { ; exp2(a) * exp2(b) reassoc, multiple uses define double @exp2_a_exp2_b_multiple_uses(double %a, double %b) { ; CHECK-LABEL: @exp2_a_exp2_b_multiple_uses( -; CHECK-NEXT: [[TMP:%.*]] = call double @llvm.exp2.f64(double [[A:%.*]]) ; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.exp2.f64(double [[B:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul reassoc double [[TMP]], [[TMP1]] +; CHECK-NEXT: [[TMP:%.*]] = fadd reassoc double [[A:%.*]], [[B]] +; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.exp2.f64(double [[TMP]]) ; CHECK-NEXT: call void @use(double [[TMP1]]) -; CHECK-NEXT: ret double [[MUL]] +; CHECK-NEXT: ret double [[TMP2]] ; %tmp = call double @llvm.exp2.f64(double %a) %tmp1 = call double @llvm.exp2.f64(double %b) @@ -55,10 +55,9 @@ define double @exp2_a_exp2_b_multiple_uses_both(double %a, double %b) { ; exp2(a) * exp2(b) => exp2(a+b) with reassoc define double @exp2_a_exp2_b_reassoc(double %a, double %b) { ; CHECK-LABEL: @exp2_a_exp2_b_reassoc( -; CHECK-NEXT: [[TMP:%.*]] = call double @llvm.exp2.f64(double [[A:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.exp2.f64(double [[B:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul reassoc double [[TMP]], [[TMP1]] -; CHECK-NEXT: ret double [[MUL]] +; CHECK-NEXT: [[TMP:%.*]] = fadd reassoc double [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = call reassoc double @llvm.exp2.f64(double [[TMP]]) +; CHECK-NEXT: ret double [[TMP1]] ; %tmp = call double @llvm.exp2.f64(double %a) %tmp1 = call double @llvm.exp2.f64(double %b) @@ -69,14 +68,11 @@ define double @exp2_a_exp2_b_reassoc(double %a, double %b) { ; exp2(a) * exp2(b) * exp2(c) * exp2(d) => exp2(a+b+c+d) with reassoc define double @exp2_a_exp2_b_exp2_c_exp2_d(double %a, double %b, double %c, double %d) { ; CHECK-LABEL: @exp2_a_exp2_b_exp2_c_exp2_d( -; CHECK-NEXT: [[TMP:%.*]] = call double @llvm.exp2.f64(double [[A:%.*]]) -; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.exp2.f64(double [[B:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul reassoc double [[TMP]], [[TMP1]] -; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.exp2.f64(double [[C:%.*]]) -; CHECK-NEXT: [[MUL1:%.*]] = fmul reassoc double [[MUL]], [[TMP2]] -; CHECK-NEXT: [[TMP3:%.*]] = call double @llvm.exp2.f64(double [[D:%.*]]) -; CHECK-NEXT: [[MUL2:%.*]] = fmul reassoc double [[MUL1]], [[TMP3]] -; CHECK-NEXT: ret double [[MUL2]] +; CHECK-NEXT: [[TMP:%.*]] = fadd reassoc double [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = fadd reassoc double [[TMP]], [[C:%.*]] +; CHECK-NEXT: [[TMP2:%.*]] = fadd reassoc double [[TMP1]], [[D:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = call reassoc double @llvm.exp2.f64(double [[TMP2]]) +; CHECK-NEXT: ret double [[TMP3]] ; %tmp = call double @llvm.exp2.f64(double %a) %tmp1 = call double @llvm.exp2.f64(double %b)