From 4a5cadb6184e0d5f2efc8e11c92b1e0c16679602 Mon Sep 17 00:00:00 2001 From: Noah Goldstein Date: Sun, 7 Apr 2024 14:01:15 -0500 Subject: [PATCH 1/2] [InstCombine] Add tests for folding `(icmp pred (trunc nuw/nsw X), C)`; NFC --- .../test/Transforms/InstCombine/icmp-trunc.ll | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/icmp-trunc.ll b/llvm/test/Transforms/InstCombine/icmp-trunc.ll index b2de9dddb2194..c49c6a36823b2 100644 --- a/llvm/test/Transforms/InstCombine/icmp-trunc.ll +++ b/llvm/test/Transforms/InstCombine/icmp-trunc.ll @@ -555,3 +555,195 @@ 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: [[TMP1:%.*]] = and i32 [[X:%.*]], 255 +; DL64-NEXT: [[R:%.*]] = icmp eq i32 [[TMP1]], 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) { +; 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]] +; + %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]] +; + %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) { +; 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]] +; + %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]] +; + %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: [[TMP1:%.*]] = and i32 [[X:%.*]], 255 +; DL64-NEXT: [[R:%.*]] = icmp ne i32 [[TMP1]], 133 +; 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) { +; 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]] +; + %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]] +; + %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) { +; 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]] +; + %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]] +; + %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]], +; 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, + 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]], +; 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, + ret <2 x i1> %r +} + From 5b4e99eb0fa07370c8bf31fd7de62304739c4a45 Mon Sep 17 00:00:00 2001 From: Noah Goldstein Date: Sun, 7 Apr 2024 14:01:29 -0500 Subject: [PATCH 2/2] [InstCombine] Fold `(icmp pred (trunc nuw/nsw X), C)` -> `(icmp pred 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. --- .../InstCombine/InstCombineCompares.cpp | 17 ++- .../test/Transforms/InstCombine/icmp-trunc.ll | 118 ++++++++++++------ 2 files changed, 91 insertions(+), 44 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 9883d02c87a32..542a1c82b127a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -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))); + 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; @@ -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; diff --git a/llvm/test/Transforms/InstCombine/icmp-trunc.ll b/llvm/test/Transforms/InstCombine/icmp-trunc.ll index c49c6a36823b2..4de64d85bbf25 100644 --- a/llvm/test/Transforms/InstCombine/icmp-trunc.ll +++ b/llvm/test/Transforms/InstCombine/icmp-trunc.ll @@ -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( @@ -573,10 +572,14 @@ 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 @@ -584,11 +587,17 @@ define i1 @ult_nuw(i32 %x) { } 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 @@ -597,10 +606,14 @@ 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 @@ -608,11 +621,17 @@ define i1 @ugt_nuw(i32 %x) { } 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 @@ -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( @@ -661,10 +679,14 @@ 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 @@ -672,11 +694,17 @@ define i1 @slt_nsw(i32 %x) { } 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 @@ -685,10 +713,14 @@ 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 @@ -696,11 +728,17 @@ define i1 @sgt_nsw(i32 %x) { } 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