Skip to content

Commit 8e6d481

Browse files
committed
[ConstraintElimination] Simplify ssub(A,B) if B s>=b && B s>=0.
A first patch to use the reasoning in ConstraintElimination to simplify sub with overflow to a regular sub, if the operation is guaranteed to not overflow. Reviewed By: spatel Differential Revision: https://reviews.llvm.org/D125264
1 parent d2396d8 commit 8e6d481

File tree

2 files changed

+69
-18
lines changed

2 files changed

+69
-18
lines changed

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/Analysis/ValueTracking.h"
2222
#include "llvm/IR/Dominators.h"
2323
#include "llvm/IR/Function.h"
24+
#include "llvm/IR/IRBuilder.h"
2425
#include "llvm/IR/Instructions.h"
2526
#include "llvm/IR/PatternMatch.h"
2627
#include "llvm/InitializePasses.h"
@@ -517,6 +518,55 @@ void State::addInfoFor(BasicBlock &BB) {
517518
WorkList.emplace_back(DT.getNode(Br->getSuccessor(1)), CmpI, true);
518519
}
519520

521+
static void
522+
tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info,
523+
SmallVectorImpl<Instruction *> &ToRemove) {
524+
auto DoesConditionHold = [](CmpInst::Predicate Pred, Value *A, Value *B,
525+
ConstraintInfo &Info) {
526+
DenseMap<Value *, unsigned> NewIndices;
527+
auto R = getConstraint(
528+
Pred, A, B, Info.getValue2Index(CmpInst::isSigned(Pred)), NewIndices);
529+
if (R.size() < 2 || R.needsNewIndices(NewIndices) || !R.isValid(Info))
530+
return false;
531+
532+
auto &CSToUse = Info.getCS(CmpInst::isSigned(Pred));
533+
return CSToUse.isConditionImplied(R.Coefficients);
534+
};
535+
536+
if (II->getIntrinsicID() == Intrinsic::ssub_with_overflow) {
537+
// If A s>= B && B s>= 0, ssub.with.overflow(a, b) should not overflow and
538+
// can be simplified to a regular sub.
539+
Value *A = II->getArgOperand(0);
540+
Value *B = II->getArgOperand(1);
541+
if (!DoesConditionHold(CmpInst::ICMP_SGE, A, B, Info) ||
542+
!DoesConditionHold(CmpInst::ICMP_SGE, B,
543+
ConstantInt::get(A->getType(), 0), Info))
544+
return;
545+
546+
IRBuilder<> Builder(II->getParent(), II->getIterator());
547+
Value *Sub = nullptr;
548+
for (User *U : make_early_inc_range(II->users())) {
549+
if (match(U, m_ExtractValue<0>(m_Value()))) {
550+
if (!Sub)
551+
Sub = Builder.CreateSub(A, B);
552+
U->replaceAllUsesWith(Sub);
553+
} else if (match(U, m_ExtractValue<1>(m_Value())))
554+
U->replaceAllUsesWith(Builder.getFalse());
555+
else
556+
continue;
557+
558+
if (U->use_empty()) {
559+
auto *I = cast<Instruction>(U);
560+
ToRemove.push_back(I);
561+
I->setOperand(0, PoisonValue::get(II->getType()));
562+
}
563+
}
564+
565+
if (II->use_empty())
566+
II->eraseFromParent();
567+
}
568+
}
569+
520570
static bool eliminateConstraints(Function &F, DominatorTree &DT) {
521571
bool Changed = false;
522572
DT.updateDFSNumbers();
@@ -540,6 +590,8 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) {
540590
return std::tie(A.NumIn, A.IsBlock) < std::tie(B.NumIn, B.IsBlock);
541591
});
542592

593+
SmallVector<Instruction *> ToRemove;
594+
543595
// Finally, process ordered worklist and eliminate implied conditions.
544596
SmallVector<StackEntry, 16> DFSInStack;
545597
for (ConstraintOrBlock &CB : S.WorkList) {
@@ -576,7 +628,11 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) {
576628
// For a block, check if any CmpInsts become known based on the current set
577629
// of constraints.
578630
if (CB.IsBlock) {
579-
for (Instruction &I : *CB.BB) {
631+
for (Instruction &I : make_early_inc_range(*CB.BB)) {
632+
if (auto *II = dyn_cast<WithOverflowInst>(&I)) {
633+
tryToSimplifyOverflowMath(II, Info, ToRemove);
634+
continue;
635+
}
580636
auto *Cmp = dyn_cast<ICmpInst>(&I);
581637
if (!Cmp)
582638
continue;
@@ -699,6 +755,8 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT) {
699755
"updates to CS and DFSInStack are out of sync");
700756
#endif
701757

758+
for (Instruction *I : ToRemove)
759+
I->eraseFromParent();
702760
return Changed;
703761
}
704762

llvm/test/Transforms/ConstraintElimination/ssub-with-overflow.ll

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@ define i8 @ssub_no_overflow_due_to_or_conds(i8 %a, i8 %b) {
1111
; CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[C_2]], [[C_1]]
1212
; CHECK-NEXT: br i1 [[OR_COND]], label [[EXIT_FAIL:%.*]], label [[MATH:%.*]]
1313
; CHECK: math:
14-
; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]])
15-
; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1
16-
; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
14+
; CHECK-NEXT: [[TMP0:%.*]] = sub i8 [[B]], [[A]]
15+
; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
1716
; CHECK: exit.ok:
18-
; CHECK-NEXT: [[RES:%.*]] = extractvalue { i8, i1 } [[OP]], 0
19-
; CHECK-NEXT: ret i8 [[RES]]
17+
; CHECK-NEXT: ret i8 [[TMP0]]
2018
; CHECK: exit.fail:
2119
; CHECK-NEXT: ret i8 0
2220
;
@@ -49,13 +47,12 @@ define i8 @ssub_no_overflow_due_to_or_conds_result_used(i8 %a, i8 %b) {
4947
; CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[C_2]], [[C_1]]
5048
; CHECK-NEXT: br i1 [[OR_COND]], label [[EXIT_FAIL:%.*]], label [[MATH:%.*]]
5149
; CHECK: math:
50+
; CHECK-NEXT: [[TMP0:%.*]] = sub i8 [[B]], [[A]]
5251
; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]])
5352
; CHECK-NEXT: call void @use_res({ i8, i1 } [[OP]])
54-
; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1
55-
; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
53+
; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
5654
; CHECK: exit.ok:
57-
; CHECK-NEXT: [[RES:%.*]] = extractvalue { i8, i1 } [[OP]], 0
58-
; CHECK-NEXT: ret i8 [[RES]]
55+
; CHECK-NEXT: ret i8 [[TMP0]]
5956
; CHECK: exit.fail:
6057
; CHECK-NEXT: ret i8 0
6158
;
@@ -87,12 +84,10 @@ define i8 @ssub_no_overflow_due_to_and_conds(i8 %a, i8 %b) {
8784
; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_2]], [[C_1]]
8885
; CHECK-NEXT: br i1 [[AND]], label [[MATH:%.*]], label [[EXIT_FAIL:%.*]]
8986
; CHECK: math:
90-
; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]])
91-
; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1
92-
; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
87+
; CHECK-NEXT: [[TMP0:%.*]] = sub i8 [[B]], [[A]]
88+
; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
9389
; CHECK: exit.ok:
94-
; CHECK-NEXT: [[RES:%.*]] = extractvalue { i8, i1 } [[OP]], 0
95-
; CHECK-NEXT: ret i8 [[RES]]
90+
; CHECK-NEXT: ret i8 [[TMP0]]
9691
; CHECK: exit.fail:
9792
; CHECK-NEXT: ret i8 0
9893
;
@@ -123,9 +118,7 @@ define i8 @ssub_no_overflow_due_to_and_conds_sub_result_not_used(i8 %a, i8 %b) {
123118
; CHECK-NEXT: [[AND:%.*]] = and i1 [[C_2]], [[C_1]]
124119
; CHECK-NEXT: br i1 [[AND]], label [[MATH:%.*]], label [[EXIT_FAIL:%.*]]
125120
; CHECK: math:
126-
; CHECK-NEXT: [[OP:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[B]], i8 [[A]])
127-
; CHECK-NEXT: [[STATUS:%.*]] = extractvalue { i8, i1 } [[OP]], 1
128-
; CHECK-NEXT: br i1 [[STATUS]], label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
121+
; CHECK-NEXT: br i1 false, label [[EXIT_FAIL]], label [[EXIT_OK:%.*]]
129122
; CHECK: exit.ok:
130123
; CHECK-NEXT: ret i8 20
131124
; CHECK: exit.fail:

0 commit comments

Comments
 (0)