diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index ba6e4ee1124f4..8b4a49c56b68e 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -21,6 +21,7 @@ #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/PatternMatch.h" #include "llvm/InitializePasses.h" @@ -517,6 +518,55 @@ void State::addInfoFor(BasicBlock &BB) { WorkList.emplace_back(DT.getNode(Br->getSuccessor(1)), CmpI, true); } +static void +tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info, + SmallVectorImpl &ToRemove) { + auto DoesConditionHold = [](CmpInst::Predicate Pred, Value *A, Value *B, + ConstraintInfo &Info) { + DenseMap NewIndices; + auto R = getConstraint( + Pred, A, B, Info.getValue2Index(CmpInst::isSigned(Pred)), NewIndices); + if (R.size() < 2 || R.needsNewIndices(NewIndices) || !R.isValid(Info)) + return false; + + auto &CSToUse = Info.getCS(CmpInst::isSigned(Pred)); + return CSToUse.isConditionImplied(R.Coefficients); + }; + + if (II->getIntrinsicID() == Intrinsic::ssub_with_overflow) { + // If A s>= B && B s>= 0, ssub.with.overflow(a, b) should not overflow and + // can be simplified to a regular sub. + Value *A = II->getArgOperand(0); + Value *B = II->getArgOperand(1); + if (!DoesConditionHold(CmpInst::ICMP_SGE, A, B, Info) || + !DoesConditionHold(CmpInst::ICMP_SGE, B, + ConstantInt::get(A->getType(), 0), Info)) + return; + + IRBuilder<> Builder(II->getParent(), II->getIterator()); + Value *Sub = nullptr; + for (User *U : make_early_inc_range(II->users())) { + if (match(U, m_ExtractValue<0>(m_Value()))) { + if (!Sub) + Sub = Builder.CreateSub(A, B); + U->replaceAllUsesWith(Sub); + } else if (match(U, m_ExtractValue<1>(m_Value()))) + U->replaceAllUsesWith(Builder.getFalse()); + else + continue; + + if (U->use_empty()) { + auto *I = cast(U); + ToRemove.push_back(I); + I->setOperand(0, PoisonValue::get(II->getType())); + } + } + + if (II->use_empty()) + II->eraseFromParent(); + } +} + static bool eliminateConstraints(Function &F, DominatorTree &DT) { bool Changed = false; DT.updateDFSNumbers(); @@ -540,6 +590,8 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) { return std::tie(A.NumIn, A.IsBlock) < std::tie(B.NumIn, B.IsBlock); }); + SmallVector ToRemove; + // Finally, process ordered worklist and eliminate implied conditions. SmallVector DFSInStack; for (ConstraintOrBlock &CB : S.WorkList) { @@ -576,7 +628,11 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) { // For a block, check if any CmpInsts become known based on the current set // of constraints. if (CB.IsBlock) { - for (Instruction &I : *CB.BB) { + for (Instruction &I : make_early_inc_range(*CB.BB)) { + if (auto *II = dyn_cast(&I)) { + tryToSimplifyOverflowMath(II, Info, ToRemove); + continue; + } auto *Cmp = dyn_cast(&I); if (!Cmp) continue; @@ -699,6 +755,8 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) { "updates to CS and DFSInStack are out of sync"); #endif + for (Instruction *I : ToRemove) + I->eraseFromParent(); return Changed; } diff --git a/llvm/test/Transforms/ConstraintElimination/ssub-with-overflow.ll b/llvm/test/Transforms/ConstraintElimination/ssub-with-overflow.ll index aa1a63aefde00..254a6a662025e 100644 --- a/llvm/test/Transforms/ConstraintElimination/ssub-with-overflow.ll +++ b/llvm/test/Transforms/ConstraintElimination/ssub-with-overflow.ll @@ -11,12 +11,10 @@ define i8 @ssub_no_overflow_due_to_or_conds(i8 %a, i8 %b) { ; CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[C_2]], [[C_1]] ; CHECK-NEXT: br i1 [[OR_COND]], label [[EXIT_FAIL:%.*]], label [[MATH:%.*]] ; CHECK: math: -; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]]) -; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1 -; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] +; CHECK-NEXT: [[TMP0:%.*]] = sub i8 [[B]], [[A]] +; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] ; CHECK: exit.ok: -; CHECK-NEXT: [[RES:%.*]] = extractvalue { i8, i1 } [[OP]], 0 -; CHECK-NEXT: ret i8 [[RES]] +; CHECK-NEXT: ret i8 [[TMP0]] ; CHECK: exit.fail: ; CHECK-NEXT: ret i8 0 ; @@ -49,13 +47,12 @@ define i8 @ssub_no_overflow_due_to_or_conds_result_used(i8 %a, i8 %b) { ; CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[C_2]], [[C_1]] ; CHECK-NEXT: br i1 [[OR_COND]], label [[EXIT_FAIL:%.*]], label [[MATH:%.*]] ; CHECK: math: +; CHECK-NEXT: [[TMP0:%.*]] = sub i8 [[B]], [[A]] ; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]]) ; CHECK-NEXT: call void @use_res({ i8, i1 } [[OP]]) -; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1 -; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] +; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] ; CHECK: exit.ok: -; CHECK-NEXT: [[RES:%.*]] = extractvalue { i8, i1 } [[OP]], 0 -; CHECK-NEXT: ret i8 [[RES]] +; CHECK-NEXT: ret i8 [[TMP0]] ; CHECK: exit.fail: ; CHECK-NEXT: ret i8 0 ; @@ -87,12 +84,10 @@ define i8 @ssub_no_overflow_due_to_and_conds(i8 %a, i8 %b) { ; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_2]], [[C_1]] ; CHECK-NEXT: br i1 [[AND]], label [[MATH:%.*]], label [[EXIT_FAIL:%.*]] ; CHECK: math: -; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]]) -; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1 -; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] +; CHECK-NEXT: [[TMP0:%.*]] = sub i8 [[B]], [[A]] +; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] ; CHECK: exit.ok: -; CHECK-NEXT: [[RES:%.*]] = extractvalue { i8, i1 } [[OP]], 0 -; CHECK-NEXT: ret i8 [[RES]] +; CHECK-NEXT: ret i8 [[TMP0]] ; CHECK: exit.fail: ; CHECK-NEXT: ret i8 0 ; @@ -123,9 +118,7 @@ define i8 @ssub_no_overflow_due_to_and_conds_sub_result_not_used(i8 %a, i8 %b) { ; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_2]], [[C_1]] ; CHECK-NEXT: br i1 [[AND]], label [[MATH:%.*]], label [[EXIT_FAIL:%.*]] ; CHECK: math: -; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]]) -; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1 -; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] +; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]] ; CHECK: exit.ok: ; CHECK-NEXT: ret i8 20 ; CHECK: exit.fail: