[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