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

fix #213247

alive2: https://alive2.llvm.org/ce/z/G6se2o
godbolt: https://godbolt.org/z/M3nz5Tvcn
This commit is contained in:
Vachan 2026-08-01 20:16:08 +05:30 committed by GitHub
parent 36b6f48f88
commit efe31d6dfe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 222 additions and 0 deletions

View file

@ -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.

View file

@ -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
}

View file

@ -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: [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32
; CHECK-NEXT: ret i32 [[SEXT]]
;
%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: ret i32 [[SEXT]]
;
%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: [[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>
%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: ret i32 [[SEXT]]
;
%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: [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SEXT]])
; CHECK-NEXT: ret i32 [[SEXT]]
;
%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
}