[llvm] [InstSimplify] Fold `zext(X) | sext(X)` --> `sext(X)` and `zext(X) & sext(X)` --> `zext(X)` (PR #213313)

via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 1 07:10:01 PDT 2026


https://github.com/VachanVY updated https://github.com/llvm/llvm-project/pull/213313

>From 1ae3ec7a0ae02247e726a72e1632ef96158ff0ec Mon Sep 17 00:00:00 2001
From: Vachan V Y <vachanvy05 at gmail.com>
Date: Fri, 31 Jul 2026 22:56:37 +0530
Subject: [PATCH 1/3] [InstCombine] Precommit Tests. NFC

---
 .../InstCombine/fold-or-zext-sext.ll          | 110 ++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll

diff --git a/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll b/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll
new file mode 100644
index 0000000000000..8a269d7ecd6e5
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll
@@ -0,0 +1,110 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use32(i32)
+
+define i32 @fold_zext_sext(i8 %x) {
+; CHECK-LABEL: @fold_zext_sext(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %x to i32
+  %or = or i32 %zext, %sext
+  ret i32 %or
+}
+
+define i32 @fold_zext_sext_commuted(i8 %x) {
+; CHECK-LABEL: @fold_zext_sext_commuted(
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X]] to i32
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[SEXT]], [[ZEXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %sext = sext i8 %x to i32
+  %zext = zext i8 %x to i32
+  %or = or i32 %sext, %zext
+  ret i32 %or
+}
+
+define <4 x i32> @fold_zext_sext_vec(<4 x i16> %x) {
+; CHECK-LABEL: @fold_zext_sext_vec(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext <4 x i16> [[X:%.*]] to <4 x i32>
+; CHECK-NEXT:    [[SEXT:%.*]] = sext <4 x i16> [[X]] to <4 x i32>
+; CHECK-NEXT:    [[R:%.*]] = or <4 x i32> [[ZEXT]], [[SEXT]]
+; CHECK-NEXT:    ret <4 x i32> [[R]]
+;
+  %zext = zext <4 x i16> %x to <4 x i32>
+  %sext = sext <4 x i16> %x to <4 x i32>
+  %or = or <4 x i32> %zext, %sext
+  ret <4 x i32> %or
+}
+
+define i32 @multi_use_fold_zext_sext_1(i8 %x) {
+; CHECK-LABEL: @multi_use_fold_zext_sext_1(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %x to i32
+  call void @use32(i32 %zext)
+  %or = or i32 %zext, %sext
+  ret i32 %or
+}
+
+define i32 @multi_use_fold_zext_sext_2(i8 %x) {
+; CHECK-LABEL: @multi_use_fold_zext_sext_2(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    call void @use32(i32 [[SEXT]])
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %x to i32
+  call void @use32(i32 %sext)
+  %or = or i32 %zext, %sext
+  ret i32 %or
+}
+
+define i32 @neg_different_sources(i8 %x, i8 %y) {
+; CHECK-LABEL: @neg_different_sources(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[Y:%.*]] to i32
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %y to i32
+  %or = or i32 %zext, %sext
+  ret i32 %or
+}
+
+define i32 @neg_zext_only(i8 %x, i8 %y) {
+; CHECK-LABEL: @neg_zext_only(
+; CHECK-NEXT:    [[R1:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = zext i8 [[R1]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %z0 = zext i8 %x to i32
+  %z1 = zext i8 %y to i32
+  %or = or i32 %z0, %z1
+  ret i32 %or
+}
+
+define i32 @neg_sext_only(i8 %x, i8 %y) {
+; CHECK-LABEL: @neg_sext_only(
+; CHECK-NEXT:    [[R1:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = sext i8 [[R1]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %s0 = sext i8 %x to i32
+  %s1 = sext i8 %y to i32
+  %or = or i32 %s0, %s1
+  ret i32 %or
+}

>From 472fdfd62537532c9d4277e9cb162df8525d7576 Mon Sep 17 00:00:00 2001
From: Vachan V Y <vachanvy05 at gmail.com>
Date: Sat, 1 Aug 2026 19:38:56 +0530
Subject: [PATCH 2/3] [InstSimplify] Optimization + Check diff

---
 llvm/lib/Analysis/InstructionSimplify.cpp     | 20 +++++++++++++++
 .../InstCombine/fold-or-zext-sext.ll          | 25 ++++++-------------
 2 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index ddaa44f43e631..1f247a9dafd33 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -2135,6 +2135,16 @@ static Value *simplifyAndInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
   if (Value *V = simplifyAndOrOfCmps(Q, Op0, Op1, true))
     return V;
 
+  // zext(X) & sext(X) --> zext(X)
+  // sext(X) & zext(X) --> zext(X)
+  {
+    Value *X = nullptr;
+    if (match(Op0, m_ZExt(m_Value(X))) && match(Op1, m_SExt(m_Specific(X))))
+      return Op0;
+    if (match(Op1, m_ZExt(m_Value(X))) && match(Op0, m_SExt(m_Specific(X))))
+      return Op1;
+  }
+
   // Try some generic simplifications for associative operations.
   if (Value *V =
           simplifyAssociativeBinOp(Instruction::And, Op0, Op1, Q, MaxRecurse))
@@ -2388,6 +2398,16 @@ static Value *simplifyOrInst(Value *Op0, Value *Op1, const SimplifyQuery &Q,
     }
   }
 
+  // zext(X) | sext(X) --> sext(X)
+  // sext(X) | zext(X) --> sext(X)
+  {
+    Value *X = nullptr;
+    if (match(Op0, m_ZExt(m_Value(X))) && match(Op1, m_SExt(m_Specific(X))))
+      return Op1;
+    if (match(Op1, m_ZExt(m_Value(X))) && match(Op0, m_SExt(m_Specific(X))))
+      return Op0;
+  }
+
   // A funnel shift (rotate) can be decomposed into simpler shifts. See if we
   // are mixing in another shift that is redundant with the funnel shift.
 
diff --git a/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll b/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll
index 8a269d7ecd6e5..8ccfa48022065 100644
--- a/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll
+++ b/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll
@@ -5,10 +5,8 @@ declare void @use32(i32)
 
 define i32 @fold_zext_sext(i8 %x) {
 ; CHECK-LABEL: @fold_zext_sext(
-; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
-; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X]] to i32
-; CHECK-NEXT:    [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]]
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    ret i32 [[SEXT]]
 ;
   %zext = zext i8 %x to i32
   %sext = sext i8 %x to i32
@@ -19,9 +17,7 @@ define i32 @fold_zext_sext(i8 %x) {
 define i32 @fold_zext_sext_commuted(i8 %x) {
 ; CHECK-LABEL: @fold_zext_sext_commuted(
 ; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32
-; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X]] to i32
-; CHECK-NEXT:    [[R:%.*]] = or i32 [[SEXT]], [[ZEXT]]
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    ret i32 [[SEXT]]
 ;
   %sext = sext i8 %x to i32
   %zext = zext i8 %x to i32
@@ -31,10 +27,8 @@ define i32 @fold_zext_sext_commuted(i8 %x) {
 
 define <4 x i32> @fold_zext_sext_vec(<4 x i16> %x) {
 ; CHECK-LABEL: @fold_zext_sext_vec(
-; CHECK-NEXT:    [[ZEXT:%.*]] = zext <4 x i16> [[X:%.*]] to <4 x i32>
-; CHECK-NEXT:    [[SEXT:%.*]] = sext <4 x i16> [[X]] to <4 x i32>
-; CHECK-NEXT:    [[R:%.*]] = or <4 x i32> [[ZEXT]], [[SEXT]]
-; CHECK-NEXT:    ret <4 x i32> [[R]]
+; CHECK-NEXT:    [[SEXT:%.*]] = sext <4 x i16> [[X:%.*]] to <4 x i32>
+; CHECK-NEXT:    ret <4 x i32> [[SEXT]]
 ;
   %zext = zext <4 x i16> %x to <4 x i32>
   %sext = sext <4 x i16> %x to <4 x i32>
@@ -47,8 +41,7 @@ define i32 @multi_use_fold_zext_sext_1(i8 %x) {
 ; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
 ; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X]] to i32
 ; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
-; CHECK-NEXT:    [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]]
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    ret i32 [[SEXT]]
 ;
   %zext = zext i8 %x to i32
   %sext = sext i8 %x to i32
@@ -59,11 +52,9 @@ define i32 @multi_use_fold_zext_sext_1(i8 %x) {
 
 define i32 @multi_use_fold_zext_sext_2(i8 %x) {
 ; CHECK-LABEL: @multi_use_fold_zext_sext_2(
-; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
-; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32
 ; CHECK-NEXT:    call void @use32(i32 [[SEXT]])
-; CHECK-NEXT:    [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]]
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    ret i32 [[SEXT]]
 ;
   %zext = zext i8 %x to i32
   %sext = sext i8 %x to i32

>From 0940b808ed7bb820b0b5fb044aea3d2e391c2344 Mon Sep 17 00:00:00 2001
From: Vachan V Y <vachanvy05 at gmail.com>
Date: Sat, 1 Aug 2026 19:39:30 +0530
Subject: [PATCH 3/3] [InstSimplify] Tests for `zext(X) & sext(X)` -->
 `zext(X)`

---
 .../InstCombine/fold-and-zext-sext.ll         | 101 ++++++++++++++++++
 1 file changed, 101 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/fold-and-zext-sext.ll

diff --git a/llvm/test/Transforms/InstCombine/fold-and-zext-sext.ll b/llvm/test/Transforms/InstCombine/fold-and-zext-sext.ll
new file mode 100644
index 0000000000000..525d85256936e
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fold-and-zext-sext.ll
@@ -0,0 +1,101 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use32(i32)
+
+define i32 @fold_zext_sext(i8 %x) {
+; CHECK-LABEL: @fold_zext_sext(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    ret i32 [[ZEXT]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %x to i32
+  %and = and i32 %zext, %sext
+  ret i32 %and
+}
+
+define i32 @fold_zext_sext_commuted(i8 %x) {
+; CHECK-LABEL: @fold_zext_sext_commuted(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    ret i32 [[ZEXT]]
+;
+  %sext = sext i8 %x to i32
+  %zext = zext i8 %x to i32
+  %and = and i32 %sext, %zext
+  ret i32 %and
+}
+
+define <4 x i32> @fold_zext_sext_vec(<4 x i16> %x) {
+; CHECK-LABEL: @fold_zext_sext_vec(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext <4 x i16> [[X:%.*]] to <4 x i32>
+; CHECK-NEXT:    ret <4 x i32> [[ZEXT]]
+;
+  %zext = zext <4 x i16> %x to <4 x i32>
+  %sext = sext <4 x i16> %x to <4 x i32>
+  %and = and <4 x i32> %zext, %sext
+  ret <4 x i32> %and
+}
+
+define i32 @multi_use_fold_zext_sext_1(i8 %x) {
+; CHECK-LABEL: @multi_use_fold_zext_sext_1(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
+; CHECK-NEXT:    ret i32 [[ZEXT]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %x to i32
+  call void @use32(i32 %zext)
+  %and = and i32 %zext, %sext
+  ret i32 %and
+}
+
+define i32 @multi_use_fold_zext_sext_2(i8 %x) {
+; CHECK-LABEL: @multi_use_fold_zext_sext_2(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    call void @use32(i32 [[SEXT]])
+; CHECK-NEXT:    ret i32 [[ZEXT]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %x to i32
+  call void @use32(i32 %sext)
+  %and = and i32 %zext, %sext
+  ret i32 %and
+}
+
+define i32 @neg_different_sources(i8 %x, i8 %y) {
+; CHECK-LABEL: @neg_different_sources(
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[SEXT:%.*]] = sext i8 [[Y:%.*]] to i32
+; CHECK-NEXT:    [[R:%.*]] = and i32 [[ZEXT]], [[SEXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %zext = zext i8 %x to i32
+  %sext = sext i8 %y to i32
+  %and = and i32 %zext, %sext
+  ret i32 %and
+}
+
+define i32 @neg_zext_only(i8 %x, i8 %y) {
+; CHECK-LABEL: @neg_zext_only(
+; CHECK-NEXT:    [[R1:%.*]] = and i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = zext i8 [[R1]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %z0 = zext i8 %x to i32
+  %z1 = zext i8 %y to i32
+  %and = and i32 %z0, %z1
+  ret i32 %and
+}
+
+define i32 @neg_sext_only(i8 %x, i8 %y) {
+; CHECK-LABEL: @neg_sext_only(
+; CHECK-NEXT:    [[R1:%.*]] = and i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = sext i8 [[R1]] to i32
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %s0 = sext i8 %x to i32
+  %s1 = sext i8 %y to i32
+  %and = and i32 %s0, %s1
+  ret i32 %and
+}



More information about the llvm-commits mailing list