[InstCombine] Canonicalize range test idiom

InstCombine converts range tests of the form (X > C1 && X < C2) or
(X < C1 || X > C2) into checks of the form (X + C3 < C4) or
(X + C3 > C4). It is possible to express all range tests in either
of these forms (with different choices of constants), but currently
neither of them is considered canonical. We may have equivalent
range tests using either ult or ugt.

This proposes to canonicalize all range tests to use ult. An
alternative would be to canonicalize to either ult or ugt depending
on the specific constants involved -- e.g. in practice we currently
generate ult for && style ranges and ugt for || style ranges when
going through the insertRangeTest() helper. In fact, the "clamp like"
fold was relying on this, which is why I had to tweak it to not
assume whether inversion is needed based on just the predicate.

Proof: https://alive2.llvm.org/ce/z/_SP_rQ

Differential Revision: https://reviews.llvm.org/D113366
This commit is contained in:
Nikita Popov 2021-11-07 00:05:00 +01:00
parent 6d44387e21
commit 1376301c87
17 changed files with 119 additions and 104 deletions

View File

@ -2748,6 +2748,14 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
return new ICmpInst(ICmpInst::ICMP_NE, Builder.CreateAnd(X, ~C),
ConstantExpr::getNeg(cast<Constant>(Y)));
// The range test idiom can use either ult or ugt. Arbitrarily canonicalize
// to the ult form.
// X+C2 >u C -> X+(C2-C-1) <u ~C
if (Pred == ICmpInst::ICMP_UGT)
return new ICmpInst(ICmpInst::ICMP_ULT,
Builder.CreateAdd(X, ConstantInt::get(Ty, *C2 - C - 1)),
ConstantInt::get(Ty, ~C));
return nullptr;
}

View File

@ -1298,15 +1298,23 @@ static Value *canonicalizeClampLike(SelectInst &Sel0, ICmpInst &Cmp0,
// Said condition must be one-use.
if (!Cmp0.hasOneUse())
return nullptr;
ICmpInst::Predicate Pred0 = Cmp0.getPredicate();
Value *Cmp00 = Cmp0.getOperand(0);
Constant *C0;
if (!match(Cmp0.getOperand(1),
m_CombineAnd(m_AnyIntegralConstant(), m_Constant(C0))))
return nullptr;
// Canonicalize Cmp0 into the form we expect.
if (!isa<SelectInst>(Sel1)) {
Pred0 = ICmpInst::getInversePredicate(Pred0);
std::swap(X, Sel1);
}
// Canonicalize Cmp0 into ult or uge.
// FIXME: we shouldn't care about lanes that are 'undef' in the end?
switch (Cmp0.getPredicate()) {
switch (Pred0) {
case ICmpInst::Predicate::ICMP_ULT:
case ICmpInst::Predicate::ICMP_UGE:
// Although icmp ult %x, 0 is an unusual thing to try and should generally
// have been simplified, it does not verify with undef inputs so ensure we
// are not in a strange state.
@ -1316,25 +1324,16 @@ static Value *canonicalizeClampLike(SelectInst &Sel0, ICmpInst &Cmp0,
return nullptr;
break; // Great!
case ICmpInst::Predicate::ICMP_ULE:
// We'd have to increment C0 by one, and for that it must not have all-ones
// element, but then it would have been canonicalized to 'ult' before
// we get here. So we can't do anything useful with 'ule'.
return nullptr;
case ICmpInst::Predicate::ICMP_UGT:
// We want to canonicalize it to 'ult', so we'll need to increment C0,
// which again means it must not have any all-ones elements.
// We want to canonicalize it to 'ult' or 'uge', so we'll need to increment
// C0, which again means it must not have any all-ones elements.
if (!match(C0,
m_SpecificInt_ICMP(
ICmpInst::Predicate::ICMP_NE,
APInt::getAllOnes(C0->getType()->getScalarSizeInBits()))))
return nullptr; // Can't do, have all-ones element[s].
C0 = InstCombiner::AddOne(C0);
std::swap(X, Sel1);
break;
case ICmpInst::Predicate::ICMP_UGE:
// The only way we'd get this predicate if this `icmp` has extra uses,
// but then we won't be able to do this fold.
return nullptr;
default:
return nullptr; // Unknown predicate.
}
@ -1407,6 +1406,8 @@ static Value *canonicalizeClampLike(SelectInst &Sel0, ICmpInst &Cmp0,
// The thresholds of this clamp-like pattern.
auto *ThresholdLowIncl = ConstantExpr::getNeg(C1);
auto *ThresholdHighExcl = ConstantExpr::getSub(C0, C1);
if (Pred0 == ICmpInst::Predicate::ICMP_UGE)
std::swap(ThresholdLowIncl, ThresholdHighExcl);
// The fold has a precondition 1: C2 s>= ThresholdLow
auto *Precond1 = ConstantExpr::getICmp(ICmpInst::Predicate::ICMP_SGE, C2,

View File

@ -39,10 +39,11 @@ entry:
; CHECK: if [[REG2]] s> [[REG1]] goto
; CHECK: if [[REG1]] s> 7 goto
; CHECK-DISABLE: [[REG1:r[0-9]+]] += -1
; CHECK-DISABLE: [[REG1:r[0-9]+]] += -8
; CHECK-DISABLE: [[REG1]] <<= 32
; CHECK-DISABLE: [[REG1]] >>= 32
; CHECK-DISABLE: if [[REG1]] > 6 goto
; CHECK-DISABLE: [[REG2:r[0-9]+]] = 4294967289
; CHECK-DISABLE: if [[REG2]] > [[REG1]] goto
lor.lhs.false: ; preds = %entry
%2 = load i32, i32* %ret, align 4, !tbaa !2

View File

@ -37,10 +37,11 @@ entry:
; CHECK: if [[REG2]] s> [[REG1]] goto
; CHECK: if [[REG1]] s> 7 goto
; CHECK-DISABLE: [[REG1:r[0-9]+]] += -1
; CHECK-DISABLE: [[REG1:r[0-9]+]] += -8
; CHECK-DISABLE: [[REG1]] <<= 32
; CHECK-DISABLE: [[REG1]] >>= 32
; CHECK-DISABLE: if [[REG1]] > 6 goto
; CHECK-DISABLE: [[REG2:r[0-9]+]] = 4294967289
; CHECK-DISABLE: if [[REG2]] > [[REG1]] goto
if.then: ; preds = %entry
store i32 0, i32* %retval, align 4

View File

@ -18,9 +18,9 @@ define i1 @print_pgm_cond_true(i32 %tmp12.reload, i32* %tmp16.out) {
; CHECK: cond_true:
; CHECK-NEXT: [[TMP15:%.*]] = getelementptr [17 x i32], [17 x i32]* @r, i32 0, i32 [[TMP12_RELOAD:%.*]]
; CHECK-NEXT: [[TMP16]] = load i32, i32* [[TMP15]], align 4
; CHECK-NEXT: [[TMP16_OFF:%.*]] = add i32 [[TMP16]], 31
; CHECK-NEXT: [[TMP0:%.*]] = icmp ugt i32 [[TMP16_OFF]], 62
; CHECK-NEXT: br i1 [[TMP0]], label [[BB27_EXITSTUB:%.*]], label [[COND_NEXT23_EXITSTUB:%.*]]
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[TMP16]], -32
; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[TMP0]], -63
; CHECK-NEXT: br i1 [[TMP1]], label [[BB27_EXITSTUB:%.*]], label [[COND_NEXT23_EXITSTUB:%.*]]
;
newFuncRoot:
br label %cond_true
@ -55,9 +55,9 @@ define i1 @print_pgm_cond_true_logical(i32 %tmp12.reload, i32* %tmp16.out) {
; CHECK: cond_true:
; CHECK-NEXT: [[TMP15:%.*]] = getelementptr [17 x i32], [17 x i32]* @r, i32 0, i32 [[TMP12_RELOAD:%.*]]
; CHECK-NEXT: [[TMP16]] = load i32, i32* [[TMP15]], align 4
; CHECK-NEXT: [[TMP16_OFF:%.*]] = add i32 [[TMP16]], 31
; CHECK-NEXT: [[TMP0:%.*]] = icmp ugt i32 [[TMP16_OFF]], 62
; CHECK-NEXT: br i1 [[TMP0]], label [[BB27_EXITSTUB:%.*]], label [[COND_NEXT23_EXITSTUB:%.*]]
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[TMP16]], -32
; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[TMP0]], -63
; CHECK-NEXT: br i1 [[TMP1]], label [[BB27_EXITSTUB:%.*]], label [[COND_NEXT23_EXITSTUB:%.*]]
;
newFuncRoot:
br label %cond_true

View File

@ -5,9 +5,9 @@
define i1 @test(i32 %tmp6) {
; CHECK-LABEL: @test(
; CHECK-NEXT: [[TMP6_OFF:%.*]] = add i32 %tmp6, 83
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[TMP6_OFF]], 11
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP6:%.*]], 71
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -12
; CHECK-NEXT: ret i1 [[TMP2]]
;
%tmp7 = sdiv i32 %tmp6, 12
icmp ne i32 %tmp7, -6
@ -16,9 +16,9 @@ define i1 @test(i32 %tmp6) {
define <2 x i1> @test_vec(<2 x i32> %tmp6) {
; CHECK-LABEL: @test_vec(
; CHECK-NEXT: [[TMP6_OFF:%.*]] = add <2 x i32> %tmp6, <i32 83, i32 83>
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt <2 x i32> [[TMP6_OFF]], <i32 11, i32 11>
; CHECK-NEXT: ret <2 x i1> [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[TMP6:%.*]], <i32 71, i32 71>
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult <2 x i32> [[TMP1]], <i32 -12, i32 -12>
; CHECK-NEXT: ret <2 x i1> [[TMP2]]
;
%tmp7 = sdiv <2 x i32> %tmp6, <i32 12, i32 12>
icmp ne <2 x i32> %tmp7, <i32 -6, i32 -6>

View File

@ -8,10 +8,10 @@ define void @f(i8* %x) nounwind {
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[L1:%.*]] = load i8, i8* [[X:%.*]], align 1
; CHECK-NEXT: [[S1:%.*]] = add i8 [[L1]], -6
; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[S1]], 2
; CHECK-NEXT: [[S2:%.*]] = add i8 [[L1]], -10
; CHECK-NEXT: [[C2:%.*]] = icmp ugt i8 [[S2]], 2
; CHECK-NEXT: [[TMP0:%.*]] = add i8 [[L1]], -9
; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[TMP0]], -3
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[L1]], -13
; CHECK-NEXT: [[C2:%.*]] = icmp ult i8 [[TMP1]], -3
; CHECK-NEXT: [[A1:%.*]] = and i1 [[C1]], [[C2]]
; CHECK-NEXT: br i1 [[A1]], label [[INCOMPATIBLE:%.*]], label [[OKAY:%.*]]
; CHECK: okay:
@ -45,10 +45,10 @@ define void @f_logical(i8* %x) nounwind {
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[L1:%.*]] = load i8, i8* [[X:%.*]], align 1
; CHECK-NEXT: [[S1:%.*]] = add i8 [[L1]], -6
; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[S1]], 2
; CHECK-NEXT: [[S2:%.*]] = add i8 [[L1]], -10
; CHECK-NEXT: [[C2:%.*]] = icmp ugt i8 [[S2]], 2
; CHECK-NEXT: [[TMP0:%.*]] = add i8 [[L1]], -9
; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[TMP0]], -3
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[L1]], -13
; CHECK-NEXT: [[C2:%.*]] = icmp ult i8 [[TMP1]], -3
; CHECK-NEXT: [[A1:%.*]] = and i1 [[C1]], [[C2]]
; CHECK-NEXT: br i1 [[A1]], label [[INCOMPATIBLE:%.*]], label [[OKAY:%.*]]
; CHECK: okay:

View File

@ -257,8 +257,8 @@ define i1 @or_eq_with_diff_one_logical(i8 %x) {
define i1 @and_ne_with_diff_one(i32 %x) {
; CHECK-LABEL: @and_ne_with_diff_one(
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -39
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[TMP1]], 1
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -41
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -2
; CHECK-NEXT: ret i1 [[TMP2]]
;
%cmp1 = icmp ne i32 %x, 40
@ -269,8 +269,8 @@ define i1 @and_ne_with_diff_one(i32 %x) {
define i1 @and_ne_with_diff_one_logical(i32 %x) {
; CHECK-LABEL: @and_ne_with_diff_one_logical(
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -39
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[TMP1]], 1
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -41
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -2
; CHECK-NEXT: ret i1 [[TMP2]]
;
%cmp1 = icmp ne i32 %x, 40
@ -308,8 +308,8 @@ define i1 @or_eq_with_diff_one_signed_logical(i32 %x) {
define i1 @and_ne_with_diff_one_signed(i64 %x) {
; CHECK-LABEL: @and_ne_with_diff_one_signed(
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[X:%.*]], 1
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i64 [[TMP1]], 1
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[X:%.*]], -1
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i64 [[TMP1]], -2
; CHECK-NEXT: ret i1 [[TMP2]]
;
%cmp1 = icmp ne i64 %x, -1
@ -320,8 +320,8 @@ define i1 @and_ne_with_diff_one_signed(i64 %x) {
define i1 @and_ne_with_diff_one_signed_logical(i64 %x) {
; CHECK-LABEL: @and_ne_with_diff_one_signed_logical(
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[X:%.*]], 1
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i64 [[TMP1]], 1
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[X:%.*]], -1
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i64 [[TMP1]], -2
; CHECK-NEXT: ret i1 [[TMP2]]
;
%cmp1 = icmp ne i64 %x, -1
@ -346,8 +346,8 @@ define <2 x i1> @or_eq_with_one_bit_diff_constants2_splatvec(<2 x i32> %x) {
define <2 x i1> @and_ne_with_diff_one_splatvec(<2 x i32> %x) {
; CHECK-LABEL: @and_ne_with_diff_one_splatvec(
; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[X:%.*]], <i32 -39, i32 -39>
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt <2 x i32> [[TMP1]], <i32 1, i32 1>
; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[X:%.*]], <i32 -41, i32 -41>
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult <2 x i32> [[TMP1]], <i32 -2, i32 -2>
; CHECK-NEXT: ret <2 x i1> [[TMP2]]
;
%cmp1 = icmp ne <2 x i32> %x, <i32 40, i32 40>
@ -508,9 +508,9 @@ define i1 @PR42691_4_logical(i32 %x) {
define i1 @PR42691_5(i32 %x) {
; CHECK-LABEL: @PR42691_5(
; CHECK-NEXT: [[X_OFF:%.*]] = add i32 [[X:%.*]], -1
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[X_OFF]], 2147483645
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -2147483647
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -2147483646
; CHECK-NEXT: ret i1 [[TMP2]]
;
%c1 = icmp slt i32 %x, 1
%c2 = icmp eq i32 %x, 2147483647
@ -520,9 +520,9 @@ define i1 @PR42691_5(i32 %x) {
define i1 @PR42691_5_logical(i32 %x) {
; CHECK-LABEL: @PR42691_5_logical(
; CHECK-NEXT: [[X_OFF:%.*]] = add i32 [[X:%.*]], -1
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[X_OFF]], 2147483645
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -2147483647
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -2147483646
; CHECK-NEXT: ret i1 [[TMP2]]
;
%c1 = icmp slt i32 %x, 1
%c2 = icmp eq i32 %x, 2147483647
@ -532,9 +532,9 @@ define i1 @PR42691_5_logical(i32 %x) {
define i1 @PR42691_6(i32 %x) {
; CHECK-LABEL: @PR42691_6(
; CHECK-NEXT: [[X_OFF:%.*]] = add i32 [[X:%.*]], 2147483647
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[X_OFF]], 2147483645
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], 1
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -2147483646
; CHECK-NEXT: ret i1 [[TMP2]]
;
%c1 = icmp ult i32 %x, 2147483649
%c2 = icmp eq i32 %x, 4294967295
@ -544,9 +544,9 @@ define i1 @PR42691_6(i32 %x) {
define i1 @PR42691_6_logical(i32 %x) {
; CHECK-LABEL: @PR42691_6_logical(
; CHECK-NEXT: [[X_OFF:%.*]] = add i32 [[X:%.*]], 2147483647
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[X_OFF]], 2147483645
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], 1
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -2147483646
; CHECK-NEXT: ret i1 [[TMP2]]
;
%c1 = icmp ult i32 %x, 2147483649
%c2 = icmp eq i32 %x, 4294967295

View File

@ -15,8 +15,8 @@
define i1 @p0(i8 %x) {
; CHECK-LABEL: @p0(
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X:%.*]], 4
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i8 [[TMP1]], 7
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X:%.*]], -4
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i8 [[TMP1]], -8
; CHECK-NEXT: ret i1 [[TMP2]]
;
%tmp0 = shl i8 %x, 5
@ -44,8 +44,8 @@ define i1 @pb(i65 %x) {
define <2 x i1> @p1_vec_splat(<2 x i8> %x) {
; CHECK-LABEL: @p1_vec_splat(
; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i8> [[X:%.*]], <i8 4, i8 4>
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt <2 x i8> [[TMP1]], <i8 7, i8 7>
; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i8> [[X:%.*]], <i8 -4, i8 -4>
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult <2 x i8> [[TMP1]], <i8 -8, i8 -8>
; CHECK-NEXT: ret <2 x i1> [[TMP2]]
;
%tmp0 = shl <2 x i8> %x, <i8 5, i8 5>
@ -115,8 +115,8 @@ declare i8 @gen8()
define i1 @c0() {
; CHECK-LABEL: @c0(
; CHECK-NEXT: [[X:%.*]] = call i8 @gen8()
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X]], 4
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i8 [[TMP1]], 7
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X]], -4
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i8 [[TMP1]], -8
; CHECK-NEXT: ret i1 [[TMP2]]
;
%x = call i8 @gen8()
@ -136,8 +136,8 @@ define i1 @n_oneuse0(i8 %x) {
; CHECK-LABEL: @n_oneuse0(
; CHECK-NEXT: [[TMP0:%.*]] = shl i8 [[X:%.*]], 5
; CHECK-NEXT: call void @use8(i8 [[TMP0]])
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X]], 4
; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i8 [[TMP1]], 7
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X]], -4
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i8 [[TMP1]], -8
; CHECK-NEXT: ret i1 [[TMP2]]
;
%tmp0 = shl i8 %x, 5

View File

@ -363,8 +363,8 @@ define i1 @ult_add_nonuw(i8 %in) {
define i1 @uge_add_nonuw(i32 %in) {
; CHECK-LABEL: @uge_add_nonuw(
; CHECK-NEXT: [[A6:%.*]] = add i32 [[IN:%.*]], 3
; CHECK-NEXT: [[A18:%.*]] = icmp ugt i32 [[A6]], 11
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[IN:%.*]], -9
; CHECK-NEXT: [[A18:%.*]] = icmp ult i32 [[TMP1]], -12
; CHECK-NEXT: ret i1 [[A18]]
;
%a6 = add i32 %in, 3
@ -785,8 +785,8 @@ define <2 x i1> @ugt_offset_splat(<2 x i5> %a) {
define i1 @ugt_wrong_offset(i8 %a) {
; CHECK-LABEL: @ugt_wrong_offset(
; CHECK-NEXT: [[T:%.*]] = add i8 [[A:%.*]], 123
; CHECK-NEXT: [[OV:%.*]] = icmp ugt i8 [[T]], -5
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 127
; CHECK-NEXT: [[OV:%.*]] = icmp ult i8 [[TMP1]], 4
; CHECK-NEXT: ret i1 [[OV]]
;
%t = add i8 %a, 123

View File

@ -57,8 +57,8 @@ define i1 @test_negative_nuw_and_signed_pred(i64 %x) {
define i1 @test_negative_nsw_and_unsigned_pred(i64 %x) {
; CHECK-LABEL: @test_negative_nsw_and_unsigned_pred(
; CHECK-NEXT: [[NOTSUB:%.*]] = add nsw i64 [[X:%.*]], -11
; CHECK-NEXT: [[Z:%.*]] = icmp ugt i64 [[NOTSUB]], -4
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[X:%.*]], -8
; CHECK-NEXT: [[Z:%.*]] = icmp ult i64 [[TMP1]], 3
; CHECK-NEXT: ret i1 [[Z]]
;
%y = sub nsw i64 10, %x

View File

@ -35,8 +35,8 @@ define i1 @testi16i8_com(i16 %add) {
define i1 @testi16i8_ne(i16 %add) {
; CHECK-LABEL: @testi16i8_ne(
; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[ADD:%.*]], 128
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ugt i16 [[TMP1]], 255
; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[ADD:%.*]], -128
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i16 [[TMP1]], -256
; CHECK-NEXT: ret i1 [[CMP_NOT_I]]
;
%sh = lshr i16 %add, 8
@ -49,8 +49,8 @@ define i1 @testi16i8_ne(i16 %add) {
define i1 @testi16i8_ne_com(i16 %add) {
; CHECK-LABEL: @testi16i8_ne_com(
; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[ADD:%.*]], 128
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ugt i16 [[TMP1]], 255
; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[ADD:%.*]], -128
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i16 [[TMP1]], -256
; CHECK-NEXT: ret i1 [[CMP_NOT_I]]
;
%sh = lshr i16 %add, 8
@ -77,8 +77,8 @@ define i1 @testi64i32(i64 %add) {
define i1 @testi64i32_ne(i64 %add) {
; CHECK-LABEL: @testi64i32_ne(
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[ADD:%.*]], 2147483648
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ugt i64 [[TMP1]], 4294967295
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[ADD:%.*]], -2147483648
; CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ult i64 [[TMP1]], -4294967296
; CHECK-NEXT: ret i1 [[CMP_NOT_I]]
;
%sh = lshr i64 %add, 32

View File

@ -145,8 +145,8 @@ define i1 @test6(i32 %X) {
define i1 @test7(i32 %X) {
; CHECK-LABEL: @test7(
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -1
; CHECK-NEXT: [[R:%.*]] = icmp ugt i32 [[TMP1]], 2
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -4
; CHECK-NEXT: [[R:%.*]] = icmp ult i32 [[TMP1]], -3
; CHECK-NEXT: ret i1 [[R]]
;
%P = getelementptr inbounds [6 x double], [6 x double]* @GD, i32 0, i32 %X

View File

@ -1120,6 +1120,10 @@ define i32 @add_umax_wrong_pred(i32 %x) {
; Negative test
; Without the nuw that would allow pushing the add through the umax, the
; add + icmp ugt combination can be interpreted as a range check, and would
; normally be canonicalized to use ult instead. However, this is not done when
; used as part of a umax to avoid breaking the SPF pattern.
define i32 @add_umax_wrong_wrap(i32 %x) {
; CHECK-LABEL: @add_umax_wrong_wrap(
; CHECK-NEXT: [[A:%.*]] = add nsw i32 [[X:%.*]], 15

View File

@ -99,9 +99,9 @@ define i32 @test17(i32 %A) {
define i1 @test18(i32 %A) {
; CHECK-LABEL: @test18(
; CHECK-NEXT: [[A_OFF:%.*]] = add i32 [[A:%.*]], -50
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A_OFF]], 49
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[A:%.*]], -100
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -50
; CHECK-NEXT: ret i1 [[TMP2]]
;
%B = icmp sge i32 %A, 100
%C = icmp slt i32 %A, 50
@ -111,9 +111,9 @@ define i1 @test18(i32 %A) {
define i1 @test18_logical(i32 %A) {
; CHECK-LABEL: @test18_logical(
; CHECK-NEXT: [[A_OFF:%.*]] = add i32 [[A:%.*]], -50
; CHECK-NEXT: [[TMP1:%.*]] = icmp ugt i32 [[A_OFF]], 49
; CHECK-NEXT: ret i1 [[TMP1]]
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[A:%.*]], -100
; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], -50
; CHECK-NEXT: ret i1 [[TMP2]]
;
%B = icmp sge i32 %A, 100
%C = icmp slt i32 %A, 50

View File

@ -11,7 +11,7 @@ define i32 @test1(i32 %a, i32 %b) nounwind ssp {
; CHECK-NEXT: [[TMP0:%.*]] = extractvalue { i32, i1 } [[SADD]], 1
; CHECK-NEXT: br i1 [[TMP0]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #2
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #[[ATTR2:[0-9]+]]
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[SADD_RESULT:%.*]] = extractvalue { i32, i1 } [[SADD]], 0
@ -49,7 +49,7 @@ define i32 @test2(i32 %a, i32 %b, i64* %P) nounwind ssp {
; CHECK-NEXT: [[TMP0:%.*]] = icmp ugt i64 [[ADD_OFF]], 4294967295
; CHECK-NEXT: br i1 [[TMP0]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #2
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #[[ATTR2]]
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[CONV9:%.*]] = trunc i64 [[ADD]] to i32
@ -82,11 +82,11 @@ define i64 @test3(i32 %a, i32 %b) nounwind ssp {
; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[A:%.*]] to i64
; CHECK-NEXT: [[CONV2:%.*]] = sext i32 [[B:%.*]] to i64
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[CONV2]], [[CONV]]
; CHECK-NEXT: [[ADD_OFF:%.*]] = add nsw i64 [[ADD]], 2147483648
; CHECK-NEXT: [[TMP0:%.*]] = icmp ugt i64 [[ADD_OFF]], 4294967295
; CHECK-NEXT: br i1 [[TMP0]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK-NEXT: [[TMP0:%.*]] = add nsw i64 [[ADD]], -2147483648
; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i64 [[TMP0]], -4294967296
; CHECK-NEXT: br i1 [[TMP1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #2
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #[[ATTR2]]
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: ret i64 [[ADD]]
@ -116,7 +116,7 @@ define zeroext i8 @test4(i8 signext %a, i8 signext %b) nounwind ssp {
; CHECK-NEXT: [[CMP:%.*]] = extractvalue { i8, i1 } [[SADD]], 1
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #2
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #[[ATTR2]]
; CHECK-NEXT: unreachable
; CHECK: if.end:
; CHECK-NEXT: [[SADD_RESULT:%.*]] = extractvalue { i8, i1 } [[SADD]], 0
@ -146,11 +146,11 @@ define i32 @test8(i64 %a, i64 %b) nounwind ssp {
; CHECK-LABEL: @test8(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADD:%.*]] = add i64 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[ADD_OFF:%.*]] = add i64 [[ADD]], 2147483648
; CHECK-NEXT: [[TMP0:%.*]] = icmp ugt i64 [[ADD_OFF]], 4294967295
; CHECK-NEXT: br i1 [[TMP0]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[ADD]], -2147483648
; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i64 [[TMP0]], -4294967296
; CHECK-NEXT: br i1 [[TMP1]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
; CHECK: if.then:
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #2
; CHECK-NEXT: tail call void @throwAnExceptionOrWhatever() #[[ATTR2]]
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[CONV9:%.*]] = trunc i64 [[ADD]] to i32

View File

@ -46,8 +46,8 @@ define i1 @test_constant2(i8 %a) {
define i1 @test_constant3(i8 %a) {
; CHECK-LABEL: @test_constant3(
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 42
; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ugt i8 [[TMP1]], 84
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], -43
; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ult i8 [[TMP1]], -85
; 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_constant3(i8 %a) {
define i1 @test_constant4(i8 %a) {
; CHECK-LABEL: @test_constant4(
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 32
; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ugt i8 [[TMP1]], 63
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], -32
; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ult i8 [[TMP1]], -64
; 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_constant4(i8 %a) {
define i1 @test_constant127(i8 %a) {
; CHECK-LABEL: @test_constant127(
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], 1
; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ugt i8 [[TMP1]], 2
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[A:%.*]], -2
; CHECK-NEXT: [[OVERFLOW:%.*]] = icmp ult i8 [[TMP1]], -3
; CHECK-NEXT: ret i1 [[OVERFLOW]]
;
%res = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 127)