Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ValueTracking] Infer relationship for the select with ICmp #66668

Merged
merged 3 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9108,6 +9108,26 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
if (L0 == R0 && L1 == R1)
return isImpliedCondMatchingOperands(LPred, RPred);

// It only really makes sense in the context of signed comparison for "X - Y
// must be positive if X >= Y and no overflow".
// Take SGT as an example: L0:x > L1:y and C >= 0
// ==> R0:(x -nsw y) < R1:(-C) is false
if ((LPred == ICmpInst::ICMP_SGT || LPred == ICmpInst::ICMP_SGE) &&
match(R0, m_NSWSub(m_Specific(L0), m_Specific(L1)))) {
if (match(R1, m_NonPositive()) &&
dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved
isImpliedCondMatchingOperands(LPred, RPred) == false)
return false;
vfdff marked this conversation as resolved.
Show resolved Hide resolved
}
dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved

// Take SLT as an example: L0:x < L1:y and C <= 0
// ==> R0:(x -nsw y) < R1:(-C) is true
if ((LPred == ICmpInst::ICMP_SLT || LPred == ICmpInst::ICMP_SLE) &&
match(R0, m_NSWSub(m_Specific(L0), m_Specific(L1)))) {
if (match(R1, m_NonNegative()) &&
isImpliedCondMatchingOperands(LPred, RPred) == true)
return true;
}
dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved

// L0 = R0 = L1 + R1, L0 >=u L1 implies R0 >=u R1, L0 <u L1 implies R0 <u R1
if (L0 == R0 &&
(LPred == ICmpInst::ICMP_ULT || LPred == ICmpInst::ICMP_UGE) &&
Expand Down
246 changes: 246 additions & 0 deletions llvm/test/Transforms/InstSimplify/select-icmp.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instsimplify -S | FileCheck %s

; TODO: https://alive2.llvm.org/ce/z/3ybZRl
define i32 @pr54735_slt(i32 %x, i32 %y) {
; CHECK-LABEL: @pr54735_slt(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SUB]], 1
; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[SUB]], -1
; CHECK-NEXT: [[ABSCOND:%.*]] = icmp sle i32 [[SUB]], -1
; CHECK-NEXT: [[ABS:%.*]] = select i1 [[ABSCOND]], i32 [[NEG]], i32 [[ADD]]
; CHECK-NEXT: ret i32 [[ABS]]
; CHECK: cond.end:
; CHECK-NEXT: ret i32 0
;
entry:
%cmp = icmp slt i32 %x, %y ; x<y ? abs (x-y+1): 0
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub nsw i32 %x, %y
%add = add nsw i32 %sub, 1
%neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
%abscond = icmp sle i32 %sub, -1
%abs = select i1 %abscond, i32 %neg, i32 %add
ret i32 %abs

cond.end: ; preds = %entry, %cond.true
ret i32 0
}

; https://alive2.llvm.org/ce/z/fTTsdT
define i32 @pr54735_sgt(i32 %x, i32 %y) {
; CHECK-LABEL: @pr54735_sgt(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SUB]], 1
; CHECK-NEXT: ret i32 [[ADD]]
; CHECK: cond.end:
; CHECK-NEXT: ret i32 0
;
entry:
%cmp = icmp sgt i32 %x, %y ; x>y ? abs (x-y+1): 0
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub nsw i32 %x, %y
%add = add nsw i32 %sub, 1
%neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
%abscond = icmp slt i32 %sub, -1
%abs = select i1 %abscond, i32 %neg, i32 %add
ret i32 %abs

cond.end: ; preds = %entry, %cond.true
ret i32 0
}

; https://alive2.llvm.org/ce/z/k9v75c
define i32 @pr54735_sge(i32 %x, i32 %y) {
; CHECK-LABEL: @pr54735_sge(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp sge i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SUB]], 1
; CHECK-NEXT: ret i32 [[ADD]]
; CHECK: cond.end:
; CHECK-NEXT: ret i32 0
;
entry:
%cmp = icmp sge i32 %x, %y ; x>y ? abs (x-y+1): 0
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub nsw i32 %x, %y
%add = add nsw i32 %sub, 1
%neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
%abscond = icmp slt i32 %sub, -1
%abs = select i1 %abscond, i32 %neg, i32 %add
ret i32 %abs

cond.end: ; preds = %entry, %cond.true
ret i32 0
}

; Negative test: https://alive2.llvm.org/ce/z/oZyu4M
define i8 @pr54735_without_nsw (i8 %x, i8 %y) {
; CHECK-LABEL: @pr54735_without_nsw(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]]
; CHECK-NEXT: [[ADD:%.*]] = add i8 [[SUB]], 1
; CHECK-NEXT: [[NEG:%.*]] = xor i8 [[SUB]], -1
; CHECK-NEXT: [[ABSCOND:%.*]] = icmp slt i8 [[SUB]], -1
; CHECK-NEXT: [[ABS:%.*]] = select i1 [[ABSCOND]], i8 [[NEG]], i8 [[ADD]]
; CHECK-NEXT: ret i8 [[ABS]]
; CHECK: cond.end:
; CHECK-NEXT: ret i8 0
;
entry:
%cmp = icmp sgt i8 %x, %y
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub i8 %x, %y
%add = add i8 %sub, 1
%neg = xor i8 %sub, -1
%abscond = icmp slt i8 %sub, -1
%abs = select i1 %abscond, i8 %neg, i8 %add
ret i8 %abs

cond.end: ; preds = %entry, %cond.true
ret i8 0
}

define i32 @pr54735_sle(i32 %x, i32 %y) {
; CHECK-LABEL: @pr54735_sle(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SUB]], 1
; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[SUB]], -1
; CHECK-NEXT: [[ABSCOND:%.*]] = icmp slt i32 [[SUB]], -1
; CHECK-NEXT: [[ABS:%.*]] = select i1 [[ABSCOND]], i32 [[NEG]], i32 [[ADD]]
; CHECK-NEXT: ret i32 [[ABS]]
; CHECK: cond.end:
; CHECK-NEXT: ret i32 0
;
entry:
%cmp = icmp sle i32 %x, %y ; x<=y ? abs (x-y+1): 0
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub nsw i32 %x, %y
%add = add nsw i32 %sub, 1
%neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
%abscond = icmp slt i32 %sub, -1
%abs = select i1 %abscond, i32 %neg, i32 %add
ret i32 %abs

cond.end: ; preds = %entry, %cond.true
ret i32 0
}

; https://alive2.llvm.org/ce/z/pp9zJi
define i32 @pr54735_slt_neg(i32 %x, i32 %y) {
; CHECK-LABEL: @pr54735_slt_neg(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[SUB]], 12
; CHECK-NEXT: ret i32 [[NEG]]
; CHECK: cond.end:
; CHECK-NEXT: ret i32 0
;
entry:
%cmp = icmp slt i32 %x, %y ; x<y ? abs (x-y-12): 0
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub nsw i32 %x, %y
%add = add nsw i32 %sub, -12 ; %sub - 12
%neg = xor i32 %sub, 12 ; 12 - %sub
%abscond = icmp sle i32 %sub, 12
%abs = select i1 %abscond, i32 %neg, i32 %add
ret i32 %abs

cond.end: ; preds = %entry, %cond.true
ret i32 0
}

; https://alive2.llvm.org/ce/z/9P6grR
define i32 @pr54735_sle_neg(i32 %x, i32 %y) {
; CHECK-LABEL: @pr54735_sle_neg(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[SUB]], 12
; CHECK-NEXT: ret i32 [[NEG]]
; CHECK: cond.end:
; CHECK-NEXT: ret i32 0
;
entry:
%cmp = icmp sle i32 %x, %y ; x<=y ? abs (x-y-12): 0
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub nsw i32 %x, %y
%add = add nsw i32 %sub, -12
%neg = xor i32 %sub, 12 ; %sub - 12
%abscond = icmp sle i32 %sub, 12
%abs = select i1 %abscond, i32 %neg, i32 %add
ret i32 %abs

cond.end: ; preds = %entry, %cond.true
ret i32 0
}

; Negative test: https://alive2.llvm.org/ce/z/Yqv4x2
define i8 @pr54735_unexpect_const (i8 %x, i8 %y) {
; CHECK-LABEL: @pr54735_unexpect_const(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[CMP]], label [[COND_TRUE:%.*]], label [[COND_END:%.*]]
; CHECK: cond.true:
; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]]
; CHECK-NEXT: [[ADD:%.*]] = add i8 [[SUB]], 2
; CHECK-NEXT: [[NEG:%.*]] = xor i8 [[SUB]], -1
; CHECK-NEXT: [[ABSCOND:%.*]] = icmp slt i8 [[SUB]], -2
; CHECK-NEXT: [[ABS:%.*]] = select i1 [[ABSCOND]], i8 [[NEG]], i8 [[ADD]]
; CHECK-NEXT: ret i8 [[ABS]]
; CHECK: cond.end:
; CHECK-NEXT: ret i8 0
;
entry:
%cmp = icmp sgt i8 %x, %y ; x>y ? abs (x-y+2): 0
br i1 %cmp, label %cond.true, label %cond.end

cond.true: ; preds = %entry
%sub = sub i8 %x, %y
%add = add i8 %sub, 2 ; x-y+2
%neg = xor i8 %sub, -1 ; y-x-1
%neg1 = sub i8 %neg, 1 ; y-x-2
%abscond = icmp slt i8 %sub, -2
%abs = select i1 %abscond, i8 %neg, i8 %add
ret i8 %abs

cond.end: ; preds = %entry, %cond.true
ret i8 0
dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved
}
vfdff marked this conversation as resolved.
Show resolved Hide resolved
Loading