[llvm] [InstCombine] Preserve no wrap kinds for trunc in EvaluateInDifferentType (PR #202233)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 09:15:41 PDT 2026


https://github.com/andjo403 updated https://github.com/llvm/llvm-project/pull/202233

>From d05a40df015148b3de1cc38dd66d889a2bc30af8 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 7 Jun 2026 21:32:53 +0200
Subject: [PATCH 1/3] [InstCombine] Precommit test (NFC)

---
 llvm/test/Transforms/InstCombine/trunc.ll | 47 +++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll
index 54f1dac770f85..8984f4a292d20 100644
--- a/llvm/test/Transforms/InstCombine/trunc.ll
+++ b/llvm/test/Transforms/InstCombine/trunc.ll
@@ -1273,3 +1273,50 @@ define i1 @neg_trunc_i1_usub_sat_one_multi_use(i8 %x) {
   call void @use.i8(i8 %call)
   ret i1 %trunc
 }
+
+define i32 @zext_i32_trunc_nuw_i8(i16 %x, i32 %y) {
+; CHECK-LABEL: @zext_i32_trunc_nuw_i8(
+; CHECK-NEXT:    [[TRUNC1:%.*]] = zext i16 [[X:%.*]] to i32
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[Y:%.*]], [[TRUNC1]]
+; CHECK-NEXT:    [[E:%.*]] = and i32 [[OR]], 255
+; CHECK-NEXT:    ret i32 [[E]]
+;
+  %trunc1 = trunc nuw i16 %x to i8
+  %trunc2 = trunc i32 %y to i8
+  %or = or i8 %trunc1, %trunc2
+  %e = zext i8 %or to i32
+  ret i32 %e
+}
+
+define i16 @zext_i16_trunc_nuw_nsw_i8(i32 %x) {
+; CHECK-LABEL: @zext_i16_trunc_nuw_nsw_i8(
+; CHECK-NEXT:    [[C:%.*]] = trunc i32 [[X:%.*]] to i16
+; CHECK-NEXT:    [[E:%.*]] = and i16 [[C]], 255
+; CHECK-NEXT:    ret i16 [[E]]
+;
+  %c = trunc nuw nsw i32 %x to i8
+  %e = zext i8 %c to i16
+  ret i16 %e
+}
+
+define i16 @zext_i16_trunc_nsw_i8(i32 %x) {
+; CHECK-LABEL: @zext_i16_trunc_nsw_i8(
+; CHECK-NEXT:    [[C:%.*]] = trunc i32 [[X:%.*]] to i16
+; CHECK-NEXT:    [[E:%.*]] = and i16 [[C]], 255
+; CHECK-NEXT:    ret i16 [[E]]
+;
+  %c = trunc nsw i32 %x to i8
+  %e = zext i8 %c to i16
+  ret i16 %e
+}
+
+define i16 @zext_i16_trunc_nuw_i8(i32 %x) {
+; CHECK-LABEL: @zext_i16_trunc_nuw_i8(
+; CHECK-NEXT:    [[C:%.*]] = trunc i32 [[X:%.*]] to i16
+; CHECK-NEXT:    [[E:%.*]] = and i16 [[C]], 255
+; CHECK-NEXT:    ret i16 [[E]]
+;
+  %c = trunc nuw i32 %x to i8
+  %e = zext i8 %c to i16
+  ret i16 %e
+}

>From f3db7834d5465cdda705e98f856e01ebaa25fb70 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 7 Jun 2026 21:35:38 +0200
Subject: [PATCH 2/3] [InstCombine] Preserve no wrap kinds for trunc

---
 .../Transforms/InstCombine/InstCombineCasts.cpp    | 14 ++++++++++++++
 llvm/test/Transforms/InstCombine/trunc.ll          |  8 ++++----
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index ca42263f47fa6..a1d5c339e4d85 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -86,6 +86,20 @@ static Value *EvaluateInDifferentTypeImpl(Value *V, Type *Ty, bool isSigned,
     // This also handles the case of zext(trunc(x)) -> zext(x).
     Res = CastInst::CreateIntegerCast(I->getOperand(0), Ty,
                                       Opc == Instruction::SExt);
+    if (auto *Trunc = dyn_cast<TruncInst>(I)) {
+      if (auto *NewTrunc = dyn_cast<TruncInst>(Res)) {
+        if (Trunc->getType()->getScalarSizeInBits() <=
+            Ty->getScalarSizeInBits()) {
+          if (Trunc->hasNoSignedWrap())
+            NewTrunc->setHasNoSignedWrap(true);
+          if (Trunc->hasNoUnsignedWrap())
+            NewTrunc->setHasNoUnsignedWrap(true);
+        }
+      } else if (auto *NewZExt = dyn_cast<ZExtInst>(Res)) {
+        if (Trunc->hasNoUnsignedWrap())
+          NewZExt->setNonNeg();
+      }
+    }
     break;
   case Instruction::Select: {
     Value *True = EvaluateInDifferentTypeImpl(I->getOperand(1), Ty, isSigned,
diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll
index 8984f4a292d20..d03cb571b6452 100644
--- a/llvm/test/Transforms/InstCombine/trunc.ll
+++ b/llvm/test/Transforms/InstCombine/trunc.ll
@@ -1276,7 +1276,7 @@ define i1 @neg_trunc_i1_usub_sat_one_multi_use(i8 %x) {
 
 define i32 @zext_i32_trunc_nuw_i8(i16 %x, i32 %y) {
 ; CHECK-LABEL: @zext_i32_trunc_nuw_i8(
-; CHECK-NEXT:    [[TRUNC1:%.*]] = zext i16 [[X:%.*]] to i32
+; CHECK-NEXT:    [[TRUNC1:%.*]] = zext nneg i16 [[X:%.*]] to i32
 ; CHECK-NEXT:    [[OR:%.*]] = or i32 [[Y:%.*]], [[TRUNC1]]
 ; CHECK-NEXT:    [[E:%.*]] = and i32 [[OR]], 255
 ; CHECK-NEXT:    ret i32 [[E]]
@@ -1290,7 +1290,7 @@ define i32 @zext_i32_trunc_nuw_i8(i16 %x, i32 %y) {
 
 define i16 @zext_i16_trunc_nuw_nsw_i8(i32 %x) {
 ; CHECK-LABEL: @zext_i16_trunc_nuw_nsw_i8(
-; CHECK-NEXT:    [[C:%.*]] = trunc i32 [[X:%.*]] to i16
+; CHECK-NEXT:    [[C:%.*]] = trunc nuw nsw i32 [[X:%.*]] to i16
 ; CHECK-NEXT:    [[E:%.*]] = and i16 [[C]], 255
 ; CHECK-NEXT:    ret i16 [[E]]
 ;
@@ -1301,7 +1301,7 @@ define i16 @zext_i16_trunc_nuw_nsw_i8(i32 %x) {
 
 define i16 @zext_i16_trunc_nsw_i8(i32 %x) {
 ; CHECK-LABEL: @zext_i16_trunc_nsw_i8(
-; CHECK-NEXT:    [[C:%.*]] = trunc i32 [[X:%.*]] to i16
+; CHECK-NEXT:    [[C:%.*]] = trunc nsw i32 [[X:%.*]] to i16
 ; CHECK-NEXT:    [[E:%.*]] = and i16 [[C]], 255
 ; CHECK-NEXT:    ret i16 [[E]]
 ;
@@ -1312,7 +1312,7 @@ define i16 @zext_i16_trunc_nsw_i8(i32 %x) {
 
 define i16 @zext_i16_trunc_nuw_i8(i32 %x) {
 ; CHECK-LABEL: @zext_i16_trunc_nuw_i8(
-; CHECK-NEXT:    [[C:%.*]] = trunc i32 [[X:%.*]] to i16
+; CHECK-NEXT:    [[C:%.*]] = trunc nuw i32 [[X:%.*]] to i16
 ; CHECK-NEXT:    [[E:%.*]] = and i16 [[C]], 255
 ; CHECK-NEXT:    ret i16 [[E]]
 ;

>From c6c50748af62eb305a77a32670221c40ac9ef0c4 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 8 Jun 2026 18:15:21 +0200
Subject: [PATCH 3/3] fixup! [InstCombine] Preserve no wrap kinds for trunc

---
 .../InstCombine/InstCombineCasts.cpp          |  6 ++--
 llvm/test/Transforms/InstCombine/trunc.ll     | 34 +++++++++++++++++++
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index a1d5c339e4d85..336f2b9d9f28e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -90,10 +90,8 @@ static Value *EvaluateInDifferentTypeImpl(Value *V, Type *Ty, bool isSigned,
       if (auto *NewTrunc = dyn_cast<TruncInst>(Res)) {
         if (Trunc->getType()->getScalarSizeInBits() <=
             Ty->getScalarSizeInBits()) {
-          if (Trunc->hasNoSignedWrap())
-            NewTrunc->setHasNoSignedWrap(true);
-          if (Trunc->hasNoUnsignedWrap())
-            NewTrunc->setHasNoUnsignedWrap(true);
+          NewTrunc->setHasNoSignedWrap(Trunc->hasNoSignedWrap());
+          NewTrunc->setHasNoUnsignedWrap(Trunc->hasNoUnsignedWrap());
         }
       } else if (auto *NewZExt = dyn_cast<ZExtInst>(Res)) {
         if (Trunc->hasNoUnsignedWrap())
diff --git a/llvm/test/Transforms/InstCombine/trunc.ll b/llvm/test/Transforms/InstCombine/trunc.ll
index d03cb571b6452..29aff222dbd87 100644
--- a/llvm/test/Transforms/InstCombine/trunc.ll
+++ b/llvm/test/Transforms/InstCombine/trunc.ll
@@ -1288,6 +1288,20 @@ define i32 @zext_i32_trunc_nuw_i8(i16 %x, i32 %y) {
   ret i32 %e
 }
 
+define i32 @neg_zext_i32_trunc_nsw_i8(i16 %x, i32 %y) {
+; CHECK-LABEL: @neg_zext_i32_trunc_nsw_i8(
+; CHECK-NEXT:    [[TRUNC1:%.*]] = zext i16 [[X:%.*]] to i32
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[Y:%.*]], [[TRUNC1]]
+; CHECK-NEXT:    [[E:%.*]] = and i32 [[OR]], 255
+; CHECK-NEXT:    ret i32 [[E]]
+;
+  %trunc1 = trunc nsw i16 %x to i8
+  %trunc2 = trunc i32 %y to i8
+  %or = or i8 %trunc1, %trunc2
+  %e = zext i8 %or to i32
+  ret i32 %e
+}
+
 define i16 @zext_i16_trunc_nuw_nsw_i8(i32 %x) {
 ; CHECK-LABEL: @zext_i16_trunc_nuw_nsw_i8(
 ; CHECK-NEXT:    [[C:%.*]] = trunc nuw nsw i32 [[X:%.*]] to i16
@@ -1320,3 +1334,23 @@ define i16 @zext_i16_trunc_nuw_i8(i32 %x) {
   %e = zext i8 %c to i16
   ret i16 %e
 }
+
+define i8 @neg_trunc_i8_trunc_nuw_i16(i32 %x) {
+; CHECK-LABEL: @neg_trunc_i8_trunc_nuw_i16(
+; CHECK-NEXT:    [[E:%.*]] = trunc i32 [[X:%.*]] to i8
+; CHECK-NEXT:    ret i8 [[E]]
+;
+  %c = trunc nuw i32 %x to i16
+  %e = trunc i16 %c to i8
+  ret i8 %e
+}
+
+define i8 @neg_trunc_nuw_i8_trunc_nuw_i16(i32 %x) {
+; CHECK-LABEL: @neg_trunc_nuw_i8_trunc_nuw_i16(
+; CHECK-NEXT:    [[E:%.*]] = trunc i32 [[X:%.*]] to i8
+; CHECK-NEXT:    ret i8 [[E]]
+;
+  %c = trunc nuw i32 %x to i16
+  %e = trunc nuw i16 %c to i8
+  ret i8 %e
+}



More information about the llvm-commits mailing list