This follows up to D104665 (which added umulo handling alongside the existing uaddo case), and generalizes for the remaining overflow intrinsics.
I went to add analogous handling to LVI, and discovered that LVI already had a more general implementation. Instead, we can port was LVI does to instcombine. (For context, LVI uses makeExactNoWrapRegion to constrain the value 'x' in blocks reached after a branch on the condition `op.with.overflow(x, C).overflow`.)
Differential Revision: https://reviews.llvm.org/D104932
assert(*EV.idx_begin() == 1 &&
"unexpected extract index for overflow inst");
- // If the normal result of the computation is dead, and the RHS is a
- // constant, we can transform this into a range comparison for many cases.
- // TODO: We can generalize these for non-constant rhs when the newly
- // formed expressions are known to simplify. Constants are merely one
- // such case.
- // TODO: Handle vector splats.
- switch (WO->getIntrinsicID()) {
- default:
- break;
- case Intrinsic::uadd_with_overflow:
- // overflow = uadd a, -4 --> overflow = icmp ugt a, 3
- if (ConstantInt *CI = dyn_cast<ConstantInt>(WO->getRHS()))
- return new ICmpInst(ICmpInst::ICMP_UGT, WO->getLHS(),
- ConstantExpr::getNot(CI));
- break;
- case Intrinsic::umul_with_overflow:
- // overflow for umul a, C --> a > UINT_MAX udiv C
- // (unless C == 0, in which case no overflow ever occurs)
- if (ConstantInt *CI = dyn_cast<ConstantInt>(WO->getRHS())) {
- assert(!CI->isZero() && "handled by instruction simplify");
- auto UMax = APInt::getMaxValue(CI->getType()->getBitWidth());
- auto *Op =
- ConstantExpr::getUDiv(ConstantInt::get(CI->getType(), UMax), CI);
- return new ICmpInst(ICmpInst::ICMP_UGT, WO->getLHS(), Op);
+ // If only the overflow result is used, and the right hand side is a
+ // constant (or constant splat), we can remove the intrinsic by directly
+ // checking for overflow.
+ const APInt *C;
+ if (match(WO->getRHS(), m_APInt(C))) {
+ // Compute the no-wrap range [X,Y) for LHS given RHS=C, then
+ // check for the inverted range using range offset trick (i.e.
+ // use a subtract to shift the range to bottom of either the
+ // signed or unsigned domain and then use a single compare to
+ // check range membership).
+ ConstantRange NWR =
+ ConstantRange::makeExactNoWrapRegion(WO->getBinaryOp(), *C,
+ WO->getNoWrapKind());
+ APInt Min = WO->isSigned() ? NWR.getSignedMin() : NWR.getUnsignedMin();
+ NWR = NWR.subtract(Min);
+
+ CmpInst::Predicate Pred;
+ APInt NewRHSC;
+ if (NWR.getEquivalentICmp(Pred, NewRHSC)) {
+ auto *OpTy = WO->getRHS()->getType();
+ auto *NewLHS = Builder.CreateSub(WO->getLHS(),
+ ConstantInt::get(OpTy, Min));
+ return new ICmpInst(ICmpInst::getInversePredicate(Pred), NewLHS,
+ ConstantInt::get(OpTy, NewRHSC));
}
- break;
- };
+ }
}
}
if (LoadInst *L = dyn_cast<LoadInst>(Agg))
define i1 @test_constant1(i8 %a) {
; CHECK-LABEL: @test_constant1(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 1)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp eq i8 [[A:%.*]], 127
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %a, i8 1)
define i1 @test_constant2(i8 %a) {
; CHECK-LABEL: @test_constant2(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 2)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp sgt i8 [[A:%.*]], 125
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %a, i8 2)
define i1 @test_constant3(i8 %a) {
; CHECK-LABEL: @test_constant3(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 3)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp sgt i8 [[A:%.*]], 124
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %a, i8 3)
define i1 @test_constant4(i8 %a) {
; CHECK-LABEL: @test_constant4(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 4)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp sgt i8 [[A:%.*]], 123
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %a, i8 4)
define i1 @test_constant127(i8 %a) {
; CHECK-LABEL: @test_constant127(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 127)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp sgt i8 [[A:%.*]], 0
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %a, i8 127)
define i1 @test_constant128(i8 %a) {
; CHECK-LABEL: @test_constant128(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 -128)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp slt i8 [[A:%.*]], 0
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %a, i8 128)
define i1 @test_constant255(i8 %a) {
; CHECK-LABEL: @test_constant255(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 -1)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp eq i8 [[A:%.*]], -128
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %a, i8 255)
define i1 @test_constant2(i8 %a) {
; CHECK-LABEL: @test_constant2(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[A:%.*]], i8 2)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 64
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp slt i8 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 2)
define i1 @test_constant3(i8 %a) {
; CHECK-LABEL: @test_constant3(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[A:%.*]], i8 3)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 42
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ugt i8 [[TMP1]], 84
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 3)
define i1 @test_constant4(i8 %a) {
; CHECK-LABEL: @test_constant4(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[A:%.*]], i8 4)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 32
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ugt i8 [[TMP1]], 63
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 4)
define i1 @test_constant127(i8 %a) {
; CHECK-LABEL: @test_constant127(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[A:%.*]], i8 127)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ugt i8 [[TMP1]], 2
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 127)
define i1 @test_constant128(i8 %a) {
; CHECK-LABEL: @test_constant128(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[A:%.*]], i8 -128)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ugt i8 [[A:%.*]], 1
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 128)
define i1 @test_constant255(i8 %a) {
; CHECK-LABEL: @test_constant255(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[A:%.*]], i8 -1)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp eq i8 [[A:%.*]], -128
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 255)
define i1 @test_constant1(i8 %a) {
; CHECK-LABEL: @test_constant1(
-; CHECK-NEXT: [[TMP1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 -1)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[TMP1]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp eq i8 [[A:%.*]], -128
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %a, i8 1)
define i1 @test_constant2(i8 %a) {
; CHECK-LABEL: @test_constant2(
-; CHECK-NEXT: [[TMP1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 -2)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[TMP1]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp slt i8 [[A:%.*]], -126
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %a, i8 2)
define i1 @test_constant3(i8 %a) {
; CHECK-LABEL: @test_constant3(
-; CHECK-NEXT: [[TMP1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 -3)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[TMP1]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp slt i8 [[A:%.*]], -125
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %a, i8 3)
define i1 @test_constant4(i8 %a) {
; CHECK-LABEL: @test_constant4(
-; CHECK-NEXT: [[TMP1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 -4)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[TMP1]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp slt i8 [[A:%.*]], -124
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %a, i8 4)
define i1 @test_constant127(i8 %a) {
; CHECK-LABEL: @test_constant127(
-; CHECK-NEXT: [[TMP1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 -127)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[TMP1]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp slt i8 [[A:%.*]], -1
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %a, i8 127)
define i1 @test_constant128(i8 %a) {
; CHECK-LABEL: @test_constant128(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[A:%.*]], i8 -128)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp sgt i8 [[A:%.*]], -1
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %a, i8 128)
define i1 @test_constant255(i8 %a) {
; CHECK-LABEL: @test_constant255(
-; CHECK-NEXT: [[TMP1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[A:%.*]], i8 1)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[TMP1]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp eq i8 [[A:%.*]], 127
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %a, i8 255)
define i1 @test_constant1(i8 %a) {
; CHECK-LABEL: @test_constant1(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[A:%.*]], i8 1)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp eq i8 [[A:%.*]], 0
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 1)
define i1 @test_constant2(i8 %a) {
; CHECK-LABEL: @test_constant2(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[A:%.*]], i8 2)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ult i8 [[A:%.*]], 2
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 2)
define i1 @test_constant3(i8 %a) {
; CHECK-LABEL: @test_constant3(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[A:%.*]], i8 3)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ult i8 [[A:%.*]], 3
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 3)
define i1 @test_constant4(i8 %a) {
; CHECK-LABEL: @test_constant4(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[A:%.*]], i8 4)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ult i8 [[A:%.*]], 4
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 4)
define i1 @test_constant127(i8 %a) {
; CHECK-LABEL: @test_constant127(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[A:%.*]], i8 127)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ult i8 [[A:%.*]], 127
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 127)
define i1 @test_constant128(i8 %a) {
; CHECK-LABEL: @test_constant128(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[A:%.*]], i8 -128)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp sgt i8 [[A:%.*]], -1
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 128)
define i1 @test_constant255(i8 %a) {
; CHECK-LABEL: @test_constant255(
-; CHECK-NEXT: [[RES:%.*]] = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[A:%.*]], i8 -1)
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i8, i1 } [[RES]], 1
+; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ne i8 [[A:%.*]], -1
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 255)