Skip to content

Commit 42fc67c

Browse files
committed
[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/mxWoFd NB: The online Alive2 hasn't been updated with `trunc nuw/nsw` support, so the proofs must be reproduced locally.
1 parent 0cdff01 commit 42fc67c

File tree

2 files changed

+26
-16
lines changed

2 files changed

+26
-16
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5705,6 +5705,22 @@ Instruction *InstCombinerImpl::foldICmpWithTrunc(ICmpInst &ICmp) {
57055705
ICmpInst::Predicate Pred = ICmp.getPredicate();
57065706
Value *Op0 = ICmp.getOperand(0), *Op1 = ICmp.getOperand(1);
57075707

5708+
// Match (icmp pred (trunc nuw/nsw X), C)
5709+
// Which we can convert to (icmp pred X, (sext/zext C))
5710+
// TODO: Maybe this makes sense as a general canonicalization?
5711+
if (match(Op1, m_ImmConstant())) {
5712+
if (auto *TI = dyn_cast<TruncInst>(Op0)) {
5713+
Value *ExtOp0 = TI->getOperand(0);
5714+
Value *ExtOp1 = nullptr;
5715+
if (!ICmp.isSigned() && TI->hasNoUnsignedWrap())
5716+
ExtOp1 = Builder.CreateZExt(Op1, ExtOp0->getType());
5717+
else if (!ICmp.isUnsigned() && TI->hasNoSignedWrap())
5718+
ExtOp1 = Builder.CreateSExt(Op1, ExtOp0->getType());
5719+
if (ExtOp1)
5720+
return new ICmpInst(Pred, ExtOp0, ExtOp1);
5721+
}
5722+
}
5723+
57085724
// Try to canonicalize trunc + compare-to-constant into a mask + cmp.
57095725
// The trunc masks high bits while the compare may effectively mask low bits.
57105726
Value *X;

llvm/test/Transforms/InstCombine/icmp-trunc.ll

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,7 @@ define i1 @eq_nuw(i32 %x) {
563563
; DL64-NEXT: ret i1 [[R]]
564564
;
565565
; DL8-LABEL: @eq_nuw(
566-
; DL8-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
567-
; DL8-NEXT: [[R:%.*]] = icmp eq i8 [[T]], 123
566+
; DL8-NEXT: [[R:%.*]] = icmp eq i32 [[X:%.*]], 123
568567
; DL8-NEXT: ret i1 [[R]]
569568
;
570569
%t = trunc nuw i32 %x to i8
@@ -574,8 +573,7 @@ define i1 @eq_nuw(i32 %x) {
574573

575574
define i1 @ult_nuw(i32 %x) {
576575
; CHECK-LABEL: @ult_nuw(
577-
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
578-
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[T]], 45
576+
; CHECK-NEXT: [[R:%.*]] = icmp ult i32 [[X:%.*]], 45
579577
; CHECK-NEXT: ret i1 [[R]]
580578
;
581579
%t = trunc nuw i32 %x to i8
@@ -586,7 +584,7 @@ define i1 @ult_nuw(i32 %x) {
586584
define i1 @ule_nuw(i32 %x) {
587585
; CHECK-LABEL: @ule_nuw(
588586
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
589-
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[T]], 46
587+
; CHECK-NEXT: [[R:%.*]] = icmp ult i32 [[X]], 46
590588
; CHECK-NEXT: call void @use(i8 [[T]])
591589
; CHECK-NEXT: ret i1 [[R]]
592590
;
@@ -598,8 +596,7 @@ define i1 @ule_nuw(i32 %x) {
598596

599597
define i1 @ugt_nuw(i32 %x) {
600598
; CHECK-LABEL: @ugt_nuw(
601-
; CHECK-NEXT: [[T:%.*]] = trunc nuw i32 [[X:%.*]] to i8
602-
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 12
599+
; CHECK-NEXT: [[R:%.*]] = icmp ugt i32 [[X:%.*]], 12
603600
; CHECK-NEXT: ret i1 [[R]]
604601
;
605602
%t = trunc nuw i32 %x to i8
@@ -610,7 +607,7 @@ define i1 @ugt_nuw(i32 %x) {
610607
define i1 @uge_nuw(i48 %x) {
611608
; CHECK-LABEL: @uge_nuw(
612609
; CHECK-NEXT: [[T:%.*]] = trunc nuw i48 [[X:%.*]] to i8
613-
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[T]], 98
610+
; CHECK-NEXT: [[R:%.*]] = icmp ugt i48 [[X]], 98
614611
; CHECK-NEXT: call void @use(i8 [[T]])
615612
; CHECK-NEXT: ret i1 [[R]]
616613
;
@@ -638,8 +635,7 @@ define i1 @ne_nsw(i32 %x) {
638635
; DL64-NEXT: ret i1 [[R]]
639636
;
640637
; DL8-LABEL: @ne_nsw(
641-
; DL8-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
642-
; DL8-NEXT: [[R:%.*]] = icmp ne i8 [[T]], 123
638+
; DL8-NEXT: [[R:%.*]] = icmp ne i32 [[X:%.*]], 123
643639
; DL8-NEXT: ret i1 [[R]]
644640
;
645641
%t = trunc nsw i32 %x to i8
@@ -649,8 +645,7 @@ define i1 @ne_nsw(i32 %x) {
649645

650646
define i1 @slt_nsw(i32 %x) {
651647
; CHECK-LABEL: @slt_nsw(
652-
; CHECK-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
653-
; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[T]], 45
648+
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[X:%.*]], 45
654649
; CHECK-NEXT: ret i1 [[R]]
655650
;
656651
%t = trunc nsw i32 %x to i8
@@ -661,7 +656,7 @@ define i1 @slt_nsw(i32 %x) {
661656
define i1 @sle_nsw(i32 %x) {
662657
; CHECK-LABEL: @sle_nsw(
663658
; CHECK-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
664-
; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[T]], 46
659+
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[X]], 46
665660
; CHECK-NEXT: call void @use(i8 [[T]])
666661
; CHECK-NEXT: ret i1 [[R]]
667662
;
@@ -673,8 +668,7 @@ define i1 @sle_nsw(i32 %x) {
673668

674669
define i1 @sgt_nsw(i32 %x) {
675670
; CHECK-LABEL: @sgt_nsw(
676-
; CHECK-NEXT: [[T:%.*]] = trunc nsw i32 [[X:%.*]] to i8
677-
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], 12
671+
; CHECK-NEXT: [[R:%.*]] = icmp sgt i32 [[X:%.*]], 12
678672
; CHECK-NEXT: ret i1 [[R]]
679673
;
680674
%t = trunc nsw i32 %x to i8
@@ -685,7 +679,7 @@ define i1 @sgt_nsw(i32 %x) {
685679
define i1 @sge_nsw(i48 %x) {
686680
; CHECK-LABEL: @sge_nsw(
687681
; CHECK-NEXT: [[T:%.*]] = trunc nsw i48 [[X:%.*]] to i8
688-
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[T]], 98
682+
; CHECK-NEXT: [[R:%.*]] = icmp sgt i48 [[X]], 98
689683
; CHECK-NEXT: call void @use(i8 [[T]])
690684
; CHECK-NEXT: ret i1 [[R]]
691685
;

0 commit comments

Comments
 (0)