diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3089,33 +3089,32 @@ 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(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(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(Agg)) diff --git a/llvm/test/Transforms/InstCombine/saddo.ll b/llvm/test/Transforms/InstCombine/saddo.ll --- a/llvm/test/Transforms/InstCombine/saddo.ll +++ b/llvm/test/Transforms/InstCombine/saddo.ll @@ -26,8 +26,7 @@ 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) @@ -37,8 +36,7 @@ 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) @@ -48,8 +46,7 @@ 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) @@ -59,8 +56,7 @@ 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) @@ -70,8 +66,7 @@ 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) @@ -81,8 +76,7 @@ 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) @@ -92,8 +86,7 @@ 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) diff --git a/llvm/test/Transforms/InstCombine/smulo.ll b/llvm/test/Transforms/InstCombine/smulo.ll --- a/llvm/test/Transforms/InstCombine/smulo.ll +++ b/llvm/test/Transforms/InstCombine/smulo.ll @@ -35,8 +35,8 @@ 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) @@ -46,8 +46,8 @@ 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) @@ -57,8 +57,8 @@ 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) @@ -69,8 +69,8 @@ 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) @@ -80,8 +80,7 @@ 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) @@ -91,8 +90,7 @@ 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) diff --git a/llvm/test/Transforms/InstCombine/ssubo.ll b/llvm/test/Transforms/InstCombine/ssubo.ll --- a/llvm/test/Transforms/InstCombine/ssubo.ll +++ b/llvm/test/Transforms/InstCombine/ssubo.ll @@ -26,8 +26,7 @@ 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) @@ -37,8 +36,7 @@ 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) @@ -48,8 +46,7 @@ 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) @@ -59,8 +56,7 @@ 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) @@ -71,8 +67,7 @@ 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) @@ -82,8 +77,7 @@ 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) @@ -93,8 +87,7 @@ 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) diff --git a/llvm/test/Transforms/InstCombine/usubo.ll b/llvm/test/Transforms/InstCombine/usubo.ll --- a/llvm/test/Transforms/InstCombine/usubo.ll +++ b/llvm/test/Transforms/InstCombine/usubo.ll @@ -26,8 +26,7 @@ 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) @@ -37,8 +36,7 @@ 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) @@ -48,8 +46,7 @@ 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) @@ -59,8 +56,7 @@ 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) @@ -71,8 +67,7 @@ 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) @@ -82,8 +77,7 @@ 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) @@ -93,8 +87,7 @@ 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)