Index: llvm/include/llvm/Transforms/Utils/Local.h =================================================================== --- llvm/include/llvm/Transforms/Utils/Local.h +++ llvm/include/llvm/Transforms/Utils/Local.h @@ -465,6 +465,10 @@ /// value? bool canReplaceOperandWithVariable(const Instruction *I, unsigned OpIdx); +/// It checks if this instruction is the only user of at least one of +/// its operands. +bool isOnlyUserOfAnyOperand(Instruction *I); + //===----------------------------------------------------------------------===// // Value helper functions // Index: llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -559,7 +559,7 @@ // 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())) { + isOnlyUserOfAnyOperand(&I)) { Value *XY = Builder.CreateFAddFMF(X, Y, &I); Value *Exp = Builder.CreateUnaryIntrinsic(Intrinsic::exp, XY, &I); return replaceInstUsesWith(I, Exp); @@ -569,7 +569,7 @@ // 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())) { + isOnlyUserOfAnyOperand(&I)) { Value *XY = Builder.CreateFAddFMF(X, Y, &I); Value *Exp2 = Builder.CreateUnaryIntrinsic(Intrinsic::exp2, XY, &I); return replaceInstUsesWith(I, Exp2); Index: llvm/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/lib/Transforms/Utils/Local.cpp +++ llvm/lib/Transforms/Utils/Local.cpp @@ -3302,6 +3302,16 @@ } } +bool llvm::isOnlyUserOfAnyOperand(Instruction *I) { + assert(I->getNumOperands() && "Must have at least one operand"); + auto FirstOp = I->getOperand(0); + auto NumOps = I->getNumOperands(); + return std::any_of(I->op_begin(), I->op_end(), + [](const Use &Op) { return Op.get()->hasOneUse(); }) || + (FirstOp->hasNUses(NumOps) && + llvm::is_splat(llvm::make_range(I->op_begin(), I->op_end()))); +} + Value *llvm::invertCondition(Value *Condition) { // First: Check if it's a constant if (Constant *C = dyn_cast(Condition)) 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(