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,14 @@ /// value? bool canReplaceOperandWithVariable(const Instruction *I, unsigned OpIdx); +/// Restrictive use constraint means that at least one of operands must have one +/// use +bool isRestrictiveUseConstraintMet(Instruction *I); + +/// Relaxed use constraint means that it is either restrictive use constraint is +/// met or all of the operands are the same instruction +bool isRelaxedUseConstraintMet(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())) { + isRelaxedUseConstraintMet(&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())) { + isRelaxedUseConstraintMet(&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,20 @@ } } +bool llvm::isRelaxedUseConstraintMet(Instruction *I) { + assert(I->getNumOperands() && "Must be at least one operand"); + auto OpB = I->op_begin(); + auto NumOps = I->getNumOperands(); + return isRestrictiveUseConstraintMet(I) || + (OpB->get()->hasNUses(NumOps) && + std::equal(++OpB, I->op_end(), I->op_begin())); +} + +bool llvm::isRestrictiveUseConstraintMet(Instruction *I) { + return std::any_of(I->op_begin(), I->op_end(), + [](const Use &op) { return op.get()->hasOneUse(); }); +} + 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(