Skip to content

[InstCombine] Fold (icmp pred (trunc nuw/nsw X), C) -> (icmp pred X, (zext/sext C)) #87935

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

Closed
Closed
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
17 changes: 13 additions & 4 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,19 @@ Instruction *InstCombinerImpl::foldICmpTruncConstant(ICmpInst &Cmp,
const APInt &C) {
ICmpInst::Predicate Pred = Cmp.getPredicate();
Value *X = Trunc->getOperand(0);
Type *SrcTy = X->getType();
unsigned DstBits = Trunc->getType()->getScalarSizeInBits(),
SrcBits = SrcTy->getScalarSizeInBits();

// Match (icmp pred (trunc nuw/nsw X), C)
// Which we can convert to (icmp pred X, (sext/zext C))
if (shouldChangeType(Trunc->getType(), SrcTy)) {
if (Trunc->hasNoSignedWrap())
return new ICmpInst(Pred, X, ConstantInt::get(SrcTy, C.sext(SrcBits)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the sign-extended constant as it may be cheaper to materialize than the zero-extended one (e.g., -1 vs 4294967296).

Don't do this until we have some performance data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, ill leave as a potential todo for now.

if (!Cmp.isSigned() && Trunc->hasNoUnsignedWrap())
return new ICmpInst(Pred, X, ConstantInt::get(SrcTy, C.zext(SrcBits)));
}

if (C.isOne() && C.getBitWidth() > 1) {
// icmp slt trunc(signum(V)) 1 --> icmp slt V, 1
Value *V = nullptr;
Expand All @@ -1417,10 +1430,6 @@ Instruction *InstCombinerImpl::foldICmpTruncConstant(ICmpInst &Cmp,
ConstantInt::get(V->getType(), 1));
}

Type *SrcTy = X->getType();
unsigned DstBits = Trunc->getType()->getScalarSizeInBits(),
SrcBits = SrcTy->getScalarSizeInBits();

// TODO: Handle any shifted constant by subtracting trailing zeros.
// TODO: Handle non-equality predicates.
Value *Y;
Expand Down
230 changes: 230 additions & 0 deletions llvm/test/Transforms/InstCombine/icmp-trunc.ll
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,233 @@ define i1 @shl1_trunc_sgt4(i32 %a) {
%r = icmp sgt i16 %t, 4
ret i1 %r
}

define i1 @eq_nuw(i32 %x) {
; DL64-LABEL: @eq_nuw(
; DL64-NEXT: [[R:%.*]] = icmp eq i32 [[X:%.*]], 123
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @eq_nuw(
; DL8-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp eq i8 [[T]], 123
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nuw i32 %x to i8
%r = icmp eq i8 %t, 123
ret i1 %r
}

define i1 @ult_nuw(i32 %x) {
; DL64-LABEL: @ult_nuw(
; DL64-NEXT: [[R:%.*]] = icmp ult i32 [[X:%.*]], 45
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @ult_nuw(
; DL8-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp ult i8 [[T]], 45
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nuw i32 %x to i8
%r = icmp ult i8 %t, 45
ret i1 %r
}

define i1 @ule_nuw(i32 %x) {
; DL64-LABEL: @ule_nuw(
; DL64-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; DL64-NEXT: [[R:%.*]] = icmp ult i32 [[X]], 46
; DL64-NEXT: call void @use(i8 [[T]])
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @ule_nuw(
; DL8-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp ult i8 [[T]], 46
; DL8-NEXT: call void @use(i8 [[T]])
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nuw i32 %x to i8
%r = icmp ule i8 %t, 45
call void @use(i8 %t)
ret i1 %r
}

define i1 @ugt_nuw(i32 %x) {
; DL64-LABEL: @ugt_nuw(
; DL64-NEXT: [[R:%.*]] = icmp ugt i32 [[X:%.*]], 12
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @ugt_nuw(
; DL8-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 12
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nuw i32 %x to i8
%r = icmp ugt i8 %t, 12
ret i1 %r
}

define i1 @uge_nuw(i32 %x) {
; DL64-LABEL: @uge_nuw(
; DL64-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; DL64-NEXT: [[R:%.*]] = icmp ugt i32 [[X]], 98
; DL64-NEXT: call void @use(i8 [[T]])
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @uge_nuw(
; DL8-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 98
; DL8-NEXT: call void @use(i8 [[T]])
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nuw i32 %x to i8
%r = icmp uge i8 %t, 99
call void @use(i8 %t)
ret i1 %r
}

define i1 @uge_nuw_i48(i48 %x) {
; CHECK-LABEL: @uge_nuw_i48(
; CHECK-NEXT: [[T:%.*]] = trunc nuw i48 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 98
; CHECK-NEXT: call void @use(i8 [[T]])
; CHECK-NEXT: ret i1 [[R]]
;
%t = trunc nuw i48 %x to i8
%r = icmp uge i8 %t, 99
call void @use(i8 %t)
ret i1 %r
}

define i1 @sgt_nuw_fail(i32 %x) {
; CHECK-LABEL: @sgt_nuw_fail(
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], 12
; CHECK-NEXT: ret i1 [[R]]
;
%t = trunc nuw i32 %x to i8
%r = icmp sgt i8 %t, 12
ret i1 %r
}

define i1 @ne_nsw(i32 %x) {
; DL64-LABEL: @ne_nsw(
; DL64-NEXT: [[R:%.*]] = icmp ne i32 [[X:%.*]], -123
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @ne_nsw(
; DL8-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp ne i8 [[T]], -123
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nsw i32 %x to i8
%r = icmp ne i8 %t, -123
ret i1 %r
}

define i1 @slt_nsw(i32 %x) {
; DL64-LABEL: @slt_nsw(
; DL64-NEXT: [[R:%.*]] = icmp slt i32 [[X:%.*]], 45
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @slt_nsw(
; DL8-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp slt i8 [[T]], 45
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nsw i32 %x to i8
%r = icmp slt i8 %t, 45
ret i1 %r
}

define i1 @sle_nsw(i16 %x) {
; DL64-LABEL: @sle_nsw(
; DL64-NEXT: [[T:%.*]] = trunc nsw i16 [[X:%.*]] to i8
; DL64-NEXT: [[R:%.*]] = icmp slt i16 [[X]], 46
; DL64-NEXT: call void @use(i8 [[T]])
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @sle_nsw(
; DL8-NEXT: [[T:%.*]] = trunc nsw i16 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp slt i8 [[T]], 46
; DL8-NEXT: call void @use(i8 [[T]])
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nsw i16 %x to i8
%r = icmp sle i8 %t, 45
call void @use(i8 %t)
ret i1 %r
}

define i1 @sgt_nsw(i32 %x) {
; DL64-LABEL: @sgt_nsw(
; DL64-NEXT: [[R:%.*]] = icmp sgt i32 [[X:%.*]], -12
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @sgt_nsw(
; DL8-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], -12
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nsw i32 %x to i8
%r = icmp sgt i8 %t, -12
ret i1 %r
}

define i1 @sge_nsw(i64 %x) {
; DL64-LABEL: @sge_nsw(
; DL64-NEXT: [[T:%.*]] = trunc nsw i64 [[X:%.*]] to i8
; DL64-NEXT: [[R:%.*]] = icmp sgt i64 [[X]], 98
; DL64-NEXT: call void @use(i8 [[T]])
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @sge_nsw(
; DL8-NEXT: [[T:%.*]] = trunc nsw i64 [[X:%.*]] to i8
; DL8-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], 98
; DL8-NEXT: call void @use(i8 [[T]])
; DL8-NEXT: ret i1 [[R]]
;
%t = trunc nsw i64 %x to i8
%r = icmp sge i8 %t, 99
call void @use(i8 %t)
ret i1 %r
}



define i1 @sge_nsw_i48(i48 %x) {
; CHECK-LABEL: @sge_nsw_i48(
; CHECK-NEXT: [[T:%.*]] = trunc nsw i48 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], 98
; CHECK-NEXT: call void @use(i8 [[T]])
; CHECK-NEXT: ret i1 [[R]]
;
%t = trunc nsw i48 %x to i8
%r = icmp sge i8 %t, 99
call void @use(i8 %t)
ret i1 %r
}


define <2 x i1> @uge_nsw(<2 x i32> %x) {
; CHECK-LABEL: @uge_nsw(
; CHECK-NEXT: [[T:%.*]] = trunc nsw <2 x i32> [[X:%.*]] to <2 x i8>
; CHECK-NEXT: [[R:%.*]] = icmp ugt <2 x i8> [[T]], <i8 -46, i8 -46>
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%t = trunc nsw <2 x i32> %x to <2 x i8>
%r = icmp uge <2 x i8> %t, <i8 -45, i8 -45>
ret <2 x i1> %r
}


define <2 x i1> @uge_nsw_non_splat(<2 x i32> %x) {
; CHECK-LABEL: @uge_nsw_non_splat(
; CHECK-NEXT: [[T:%.*]] = trunc nsw <2 x i32> [[X:%.*]] to <2 x i8>
; CHECK-NEXT: [[R:%.*]] = icmp ugt <2 x i8> [[T]], <i8 44, i8 45>
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%t = trunc nsw <2 x i32> %x to <2 x i8>
%r = icmp uge <2 x i8> %t, <i8 45, i8 46>
ret <2 x i1> %r
}

Loading