Skip to content

Commit 6fdcdea

Browse files
committed
[InstCombine] Fold (X == 0 ? Y : 0) | X to X == 0 ? Y : X
1 parent fba68b4 commit 6fdcdea

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3575,6 +3575,41 @@ static Value *foldOrOfInversions(BinaryOperator &I,
35753575
return nullptr;
35763576
}
35773577

3578+
// Optimize patterns where an OR operation combines a select-based zero check
3579+
// with its condition value. This handles both scalar and vector types.
3580+
//
3581+
// Given:
3582+
// (X == 0 ? Y : 0) | X --> X == 0 ? Y : X
3583+
// X | (X == 0 ? Y : 0) --> X == 0 ? Y : X
3584+
//
3585+
// Also handles cases where X might be wrapped in zero/sign extensions.
3586+
static Instruction *foldOrOfSelectZero(BinaryOperator &BO, Value *Op0,
3587+
Value *Op1) {
3588+
CmpPredicate Pred;
3589+
Value *X, *Y;
3590+
3591+
// Check both operand orders to handle commutative OR
3592+
for (Value *SelVal : {Op0, Op1}) {
3593+
// The other operand in the OR operation (potentially X or extended X)
3594+
Value *Other = (SelVal == Op0) ? Op1 : Op0;
3595+
3596+
// Attempt to match the select pattern:
3597+
// select(icmp eq X, 0), Y, 0
3598+
// Where X might be:
3599+
// - Original value
3600+
// - Zero extended value (zext)
3601+
// - Sign extended value (sext)
3602+
if (match(SelVal, m_Select(m_c_ICmp(Pred, m_Value(X), m_Zero()), m_Value(Y),
3603+
m_Zero())) &&
3604+
Pred == ICmpInst::ICMP_EQ &&
3605+
match(Other, m_ZExtOrSExtOrSelf(m_Specific(X)))) {
3606+
return SelectInst::Create(cast<SelectInst>(SelVal)->getCondition(), Y,
3607+
Other);
3608+
}
3609+
}
3610+
return nullptr;
3611+
}
3612+
35783613
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
35793614
// here. We should standardize that construct where it is needed or choose some
35803615
// other way to ensure that commutated variants of patterns are not missed.
@@ -3657,6 +3692,11 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
36573692
/*NSW=*/true, /*NUW=*/true))
36583693
return R;
36593694
}
3695+
3696+
// (X == 0 ? Y : 0) | X -> X == 0 ? Y : X
3697+
// X | (X == 0 ? Y : 0) -> X == 0 ? Y : X
3698+
if (Instruction *R = foldOrOfSelectZero(I, Op0, Op1))
3699+
return R;
36603700

36613701
Value *X, *Y;
36623702
const APInt *CV;
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
2+
3+
; Basic functional test
4+
define i32 @basic(i32 %a, i32 %b) {
5+
; CHECK-LABEL: @basic(
6+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 0
7+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP]], i32 [[B:%.*]], i32 [[A]]
8+
; CHECK-NEXT: ret i32 [[RES]]
9+
;
10+
%cmp = icmp eq i32 %a, 0
11+
%sel = select i1 %cmp, i32 %b, i32 0
12+
%or = or i32 %sel, %a
13+
ret i32 %or
14+
}
15+
16+
; Operand order swap test
17+
define i32 @swap_operand_order(i32 %x, i32 %y) {
18+
; CHECK-LABEL: @swap_operand_order(
19+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 0
20+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP]], i32 [[Y:%.*]], i32 [[X]]
21+
; CHECK-NEXT: ret i32 [[RES]]
22+
;
23+
%cmp = icmp eq i32 %x, 0
24+
%sel = select i1 %cmp, i32 %y, i32 0
25+
%or = or i32 %x, %sel
26+
ret i32 %or
27+
}
28+
29+
; Negative test: Non-zero false value in select
30+
define i32 @negative_non_zero_false_val(i32 %a, i32 %b) {
31+
; CHECK-LABEL: @negative_non_zero_false_val(
32+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 0
33+
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 [[B:%.*]], i32 1
34+
; CHECK-NEXT: [[OR:%.*]] = or i32 [[SEL]], [[A]]
35+
; CHECK-NEXT: ret i32 [[OR]]
36+
;
37+
%cmp = icmp eq i32 %a, 0
38+
%sel = select i1 %cmp, i32 %b, i32 1
39+
%or = or i32 %sel, %a
40+
ret i32 %or
41+
}
42+
43+
; Negative test: Incorrect comparison predicate (NE)
44+
define i32 @negative_wrong_predicate(i32 %a, i32 %b) {
45+
; CHECK-LABEL: @negative_wrong_predicate(
46+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 0
47+
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 0, i32 [[B:%.*]]
48+
; CHECK-NEXT: [[OR:%.*]] = or i32 [[SEL]], [[A]]
49+
; CHECK-NEXT: ret i32 [[OR]]
50+
;
51+
%cmp = icmp ne i32 %a, 0
52+
%sel = select i1 %cmp, i32 %b, i32 0
53+
%or = or i32 %sel, %a
54+
ret i32 %or
55+
}
56+
57+
; Comparison direction swap test (0 == X)
58+
define i32 @cmp_swapped(i32 %x, i32 %y) {
59+
; CHECK-LABEL: @cmp_swapped(
60+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], 0
61+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP]], i32 [[Y:%.*]], i32 [[X]]
62+
; CHECK-NEXT: ret i32 [[RES]]
63+
;
64+
%cmp = icmp eq i32 0, %x
65+
%sel = select i1 %cmp, i32 %y, i32 0
66+
%or = or i32 %x, %sel
67+
ret i32 %or
68+
}
69+
70+
; Complex expression test
71+
define i32 @complex_expression(i32 %a, i32 %b) {
72+
; CHECK-LABEL: @complex_expression(
73+
; CHECK-NEXT: [[X:%.*]] = add i32 [[A:%.*]], 1
74+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], 0
75+
; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP]], i32 [[B:%.*]], i32 [[X]]
76+
; CHECK-NEXT: ret i32 [[RES]]
77+
;
78+
%x = add i32 %a, 1
79+
%cmp = icmp eq i32 %x, 0
80+
%sel = select i1 %cmp, i32 %b, i32 0
81+
%or = or i32 %sel, %x
82+
ret i32 %or
83+
}
84+
85+
; zext test
86+
define i32 @zext_cond(i8 %a, i32 %b) {
87+
; CHECK-LABEL: @zext_cond(
88+
; CHECK-NEXT: [[Z:%.*]] = zext i8 [[A:%.*]] to i32
89+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A]], 0
90+
; CHECK-NEXT: [[OR:%.*]] = select i1 [[CMP]], i32 [[B:%.*]], i32 [[Z]]
91+
; CHECK-NEXT: ret i32 [[OR]]
92+
%z = zext i8 %a to i32
93+
%cmp = icmp eq i32 %z, 0
94+
%sel = select i1 %cmp, i32 %b, i32 0
95+
%or = or i32 %sel, %z
96+
ret i32 %or
97+
}
98+
99+
; sext test
100+
define i32 @sext_cond(i8 %a, i32 %b) {
101+
; CHECK-LABEL: @sext_cond(
102+
; CHECK-NEXT: [[S:%.*]] = sext i8 [[A:%.*]] to i32
103+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[A]], 0
104+
; CHECK-NEXT: [[OR:%.*]] = select i1 [[CMP]], i32 [[B:%.*]], i32 [[S]]
105+
; CHECK-NEXT: ret i32 [[OR]]
106+
%s = sext i8 %a to i32
107+
%cmp = icmp eq i32 %s, 0
108+
%sel = select i1 %cmp, i32 %b, i32 0
109+
%or = or i32 %sel, %s
110+
ret i32 %or
111+
}
112+
113+
; Vector type test
114+
define <2 x i32> @vector_type(<2 x i32> %a, <2 x i32> %b) {
115+
; CHECK-LABEL: @vector_type(
116+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], zeroinitializer
117+
; CHECK-NEXT: [[RES:%.*]] = select <2 x i1> [[CMP]], <2 x i32> [[B:%.*]], <2 x i32> [[A]]
118+
; CHECK-NEXT: ret <2 x i32> [[RES]]
119+
;
120+
%cmp = icmp eq <2 x i32> %a, zeroinitializer
121+
%sel = select <2 x i1> %cmp, <2 x i32> %b, <2 x i32> zeroinitializer
122+
%or = or <2 x i32> %sel, %a
123+
ret <2 x i32> %or
124+
}
125+
126+
; Pointer type test (should not trigger optimization)
127+
define i32* @pointer_type(i32* %p, i32* %q) {
128+
; CHECK-LABEL: @pointer_type(
129+
; CHECK-NEXT: [[A:%.*]] = ptrtoint ptr [[P:%.*]] to i64
130+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P]], null
131+
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], ptr [[Q:%.*]], ptr null
132+
; CHECK-NEXT: [[SEL_INT:%.*]] = ptrtoint ptr [[SEL]] to i64
133+
; CHECK-NEXT: [[OR:%.*]] = or i64 [[A]], [[SEL_INT]]
134+
; CHECK-NEXT: [[RET:%.*]] = inttoptr i64 [[OR]] to ptr
135+
; CHECK-NEXT: ret ptr [[RET]]
136+
;
137+
%a = ptrtoint i32* %p to i64
138+
%cmp = icmp eq i64 %a, 0
139+
%sel = select i1 %cmp, i32* %q, i32* null
140+
%sel_int = ptrtoint i32* %sel to i64
141+
%or_val = or i64 %a, %sel_int
142+
%ret = inttoptr i64 %or_val to i32*
143+
ret i32* %ret
144+
}

0 commit comments

Comments
 (0)