Skip to content

Commit

Permalink
[InstCombine] Fold (icmp pred (trunc nuw/nsw X), C) -> `(icmp pred …
Browse files Browse the repository at this point in the history
…X, (zext/sext C))`

This is valid as long as the sign of the wrap flag doesn't differ from
the sign of the `pred`.

Proofs: https://alive2.llvm.org/ce/z/35NsrR

NB: The online Alive2 hasn't been updated with `trunc nuw/nsw`
support, so the proofs must be reproduced locally.
  • Loading branch information
goldsteinn committed May 11, 2024
1 parent 6680aa9 commit 0344081
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 48 deletions.
19 changes: 15 additions & 4 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,21 @@ 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(DstBits, SrcBits)) {
// Potential TODO: Swap the two cases so we try the `sext` case first which
// might produce easier to materialize constants.
if (!Cmp.isSigned() && Trunc->hasNoUnsignedWrap())
return new ICmpInst(Pred, X, ConstantInt::get(SrcTy, C.zext(SrcBits)));
if (Trunc->hasNoSignedWrap())
return new ICmpInst(Pred, X, ConstantInt::get(SrcTy, C.sext(SrcBits)));
}

if (C.isOne() && C.getBitWidth() > 1) {
// icmp slt trunc(signum(V)) 1 --> icmp slt V, 1
Value *V = nullptr;
Expand All @@ -1417,10 +1432,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
130 changes: 86 additions & 44 deletions llvm/test/Transforms/InstCombine/icmp-trunc.ll
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,7 @@ define i1 @shl1_trunc_sgt4(i32 %a) {

define i1 @eq_nuw(i32 %x) {
; DL64-LABEL: @eq_nuw(
; DL64-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], 255
; DL64-NEXT: [[R:%.*]] = icmp eq i32 [[TMP1]], 123
; DL64-NEXT: [[R:%.*]] = icmp eq i32 [[X:%.*]], 123
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @eq_nuw(
Expand All @@ -573,22 +572,32 @@ define i1 @eq_nuw(i32 %x) {
}

define i1 @ult_nuw(i32 %x) {
; CHECK-LABEL: @ult_nuw(
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[T]], 45
; CHECK-NEXT: ret i1 [[R]]
; 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) {
; CHECK-LABEL: @ule_nuw(
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[T]], 46
; CHECK-NEXT: call void @use(i8 [[T]])
; CHECK-NEXT: ret i1 [[R]]
; 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
Expand All @@ -597,22 +606,32 @@ define i1 @ule_nuw(i32 %x) {
}

define i1 @ugt_nuw(i32 %x) {
; CHECK-LABEL: @ugt_nuw(
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 12
; CHECK-NEXT: ret i1 [[R]]
; 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) {
; CHECK-LABEL: @uge_nuw(
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 98
; CHECK-NEXT: call void @use(i8 [[T]])
; CHECK-NEXT: ret i1 [[R]]
; 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
Expand Down Expand Up @@ -646,8 +665,7 @@ define i1 @sgt_nuw_fail(i32 %x) {

define i1 @ne_nsw(i32 %x) {
; DL64-LABEL: @ne_nsw(
; DL64-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], 255
; DL64-NEXT: [[R:%.*]] = icmp ne i32 [[TMP1]], 133
; DL64-NEXT: [[R:%.*]] = icmp ne i32 [[X:%.*]], -123
; DL64-NEXT: ret i1 [[R]]
;
; DL8-LABEL: @ne_nsw(
Expand All @@ -661,22 +679,32 @@ define i1 @ne_nsw(i32 %x) {
}

define i1 @slt_nsw(i32 %x) {
; CHECK-LABEL: @slt_nsw(
; CHECK-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[T]], 45
; CHECK-NEXT: ret i1 [[R]]
; 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) {
; CHECK-LABEL: @sle_nsw(
; CHECK-NEXT: [[T:%.*]] = trunc nsw i16 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[T]], 46
; CHECK-NEXT: call void @use(i8 [[T]])
; CHECK-NEXT: ret i1 [[R]]
; 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
Expand All @@ -685,22 +713,32 @@ define i1 @sle_nsw(i16 %x) {
}

define i1 @sgt_nsw(i32 %x) {
; CHECK-LABEL: @sgt_nsw(
; CHECK-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], -12
; CHECK-NEXT: ret i1 [[R]]
; 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) {
; CHECK-LABEL: @sge_nsw(
; CHECK-NEXT: [[T:%.*]] = trunc nsw i64 [[X:%.*]] to i8
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], 98
; CHECK-NEXT: call void @use(i8 [[T]])
; CHECK-NEXT: ret i1 [[R]]
; 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
Expand All @@ -725,10 +763,14 @@ define i1 @sge_nsw_i48(i48 %x) {


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]]
; DL64-LABEL: @uge_nsw(
; DL64-NEXT: [[R:%.*]] = icmp ugt <2 x i32> [[X:%.*]], <i32 -46, i32 -46>
; DL64-NEXT: ret <2 x i1> [[R]]
;
; DL8-LABEL: @uge_nsw(
; DL8-NEXT: [[T:%.*]] = trunc nsw <2 x i32> [[X:%.*]] to <2 x i8>
; DL8-NEXT: [[R:%.*]] = icmp ugt <2 x i8> [[T]], <i8 -46, i8 -46>
; DL8-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>
Expand Down

0 comments on commit 0344081

Please sign in to comment.