Index: llvm/include/llvm/IR/Instruction.h =================================================================== --- llvm/include/llvm/IR/Instruction.h +++ llvm/include/llvm/IR/Instruction.h @@ -170,6 +170,11 @@ bool isExceptionalTerminator() const { return isExceptionalTerminator(getOpcode()); } + + /// It checks if this instruction is the only user of at least one of + /// its operands. + bool isOnlyUserOfAnyOperand(); + bool isIndirectTerminator() const { return isIndirectTerminator(getOpcode()); } Index: llvm/lib/IR/Instruction.cpp =================================================================== --- llvm/lib/IR/Instruction.cpp +++ llvm/lib/IR/Instruction.cpp @@ -117,6 +117,10 @@ return Order < Other->Order; } +bool Instruction::isOnlyUserOfAnyOperand() { + return any_of(operands(), [](Value *V) { return V->hasOneUser(); }); +} + void Instruction::setHasNoUnsignedWrap(bool b) { cast(this)->setHasNoUnsignedWrap(b); } Index: llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -556,20 +556,18 @@ } // 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())) { + I.isOnlyUserOfAnyOperand()) { 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())) { + I.isOnlyUserOfAnyOperand()) { Value *XY = Builder.CreateFAddFMF(X, Y, &I); Value *Exp2 = Builder.CreateUnaryIntrinsic(Intrinsic::exp2, XY, &I); return replaceInstUsesWith(I, Exp2); Index: llvm/test/Transforms/InstCombine/fmul-exp.ll =================================================================== --- llvm/test/Transforms/InstCombine/fmul-exp.ll +++ llvm/test/Transforms/InstCombine/fmul-exp.ll @@ -65,6 +65,17 @@ ret double %mul } +define double @exp_a(double %a) { +; CHECK-LABEL: @exp_a( +; CHECK-NEXT: [[TMP1:%.*]] = fadd reassoc double [[A:%.*]], [[A]] +; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.exp.f64(double [[TMP1]]) +; CHECK-NEXT: ret double [[TMP2]] +; + %tmp = call double @llvm.exp.f64(double %a) + %mul = fmul reassoc double %tmp, %tmp + ret double %mul +} + ; 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( Index: llvm/test/Transforms/InstCombine/fmul-exp2.ll =================================================================== --- llvm/test/Transforms/InstCombine/fmul-exp2.ll +++ llvm/test/Transforms/InstCombine/fmul-exp2.ll @@ -34,6 +34,17 @@ ret double %mul } +define double @exp2_a_multiple_uses_in_one_user(double %a) { +; CHECK-LABEL: @exp2_a_multiple_uses_in_one_user( +; CHECK-NEXT: [[TMP1:%.*]] = fadd reassoc double [[A:%.*]], [[A]] +; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.exp2.f64(double [[TMP1]]) +; CHECK-NEXT: ret double [[TMP2]] +; + %tmp = call double @llvm.exp2.f64(double %a) + %mul = fmul reassoc double %tmp, %tmp + ret double %mul +} + ; exp2(a) * exp2(b) reassoc, both with multiple uses define double @exp2_a_exp2_b_multiple_uses_both(double %a, double %b) { ; CHECK-LABEL: @exp2_a_exp2_b_multiple_uses_both(