Skip to content

Commit 11faf88

Browse files
brandonxindtcxzyw
andauthored
[InstCombine] Fold icmp with clamp into unsigned bound check (#161303)
Fix #157315 alive2: https://alive2.llvm.org/ce/z/TEnuFV The equality comparison of `min(max(X, Lo), Hi)` and `X` is actually a range check on `X`. This PR folds this into an unsigned bound check `(X - Lo) u< (Hi - Lo + 1)`. --------- Co-authored-by: Yingwei Zheng <[email protected]>
1 parent 6943906 commit 11faf88

File tree

4 files changed

+360
-1
lines changed

4 files changed

+360
-1
lines changed

llvm/include/llvm/IR/IntrinsicInst.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,26 @@ class MinMaxIntrinsic : public IntrinsicInst {
810810
/// Whether the intrinsic is signed or unsigned.
811811
bool isSigned() const { return isSigned(getIntrinsicID()); };
812812

813+
/// Whether the intrinsic is a smin or umin.
814+
static bool isMin(Intrinsic::ID ID) {
815+
switch (ID) {
816+
case Intrinsic::umin:
817+
case Intrinsic::smin:
818+
return true;
819+
case Intrinsic::umax:
820+
case Intrinsic::smax:
821+
return false;
822+
default:
823+
llvm_unreachable("Invalid intrinsic");
824+
}
825+
}
826+
827+
/// Whether the intrinsic is a smin or a umin.
828+
bool isMin() const { return isMin(getIntrinsicID()); }
829+
830+
/// Whether the intrinsic is a smax or a umax.
831+
bool isMax() const { return !isMin(getIntrinsicID()); }
832+
813833
/// Min/max intrinsics are monotonic, they operate on a fixed-bitwidth values,
814834
/// so there is a certain threshold value, upon reaching which,
815835
/// their value can no longer change. Return said threshold.

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5780,6 +5780,45 @@ Instruction *InstCombinerImpl::foldICmpWithMinMax(Instruction &I,
57805780
return nullptr;
57815781
}
57825782

5783+
/// Match and fold patterns like:
5784+
/// icmp eq/ne X, min(max(X, Lo), Hi)
5785+
/// which represents a range check and can be repsented as a ConstantRange.
5786+
///
5787+
/// For icmp eq, build ConstantRange [Lo, Hi + 1) and convert to:
5788+
/// (X - Lo) u< (Hi + 1 - Lo)
5789+
/// For icmp ne, build ConstantRange [Hi + 1, Lo) and convert to:
5790+
/// (X - (Hi + 1)) u< (Lo - (Hi + 1))
5791+
Instruction *InstCombinerImpl::foldICmpWithClamp(ICmpInst &I, Value *X,
5792+
MinMaxIntrinsic *Min) {
5793+
if (!I.isEquality() || !Min->hasOneUse() || !Min->isMin())
5794+
return nullptr;
5795+
5796+
const APInt *Lo = nullptr, *Hi = nullptr;
5797+
if (Min->isSigned()) {
5798+
if (!match(Min->getLHS(), m_OneUse(m_SMax(m_Specific(X), m_APInt(Lo)))) ||
5799+
!match(Min->getRHS(), m_APInt(Hi)) || !Lo->slt(*Hi))
5800+
return nullptr;
5801+
} else {
5802+
if (!match(Min->getLHS(), m_OneUse(m_UMax(m_Specific(X), m_APInt(Lo)))) ||
5803+
!match(Min->getRHS(), m_APInt(Hi)) || !Lo->ult(*Hi))
5804+
return nullptr;
5805+
}
5806+
5807+
ConstantRange CR = ConstantRange::getNonEmpty(*Lo, *Hi + 1);
5808+
ICmpInst::Predicate Pred;
5809+
APInt C, Offset;
5810+
if (I.getPredicate() == ICmpInst::ICMP_EQ)
5811+
CR.getEquivalentICmp(Pred, C, Offset);
5812+
else
5813+
CR.inverse().getEquivalentICmp(Pred, C, Offset);
5814+
5815+
if (!Offset.isZero())
5816+
X = Builder.CreateAdd(X, ConstantInt::get(X->getType(), Offset));
5817+
5818+
return replaceInstUsesWith(
5819+
I, Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), C)));
5820+
}
5821+
57835822
// Canonicalize checking for a power-of-2-or-zero value:
57845823
static Instruction *foldICmpPow2Test(ICmpInst &I,
57855824
InstCombiner::BuilderTy &Builder) {
@@ -7467,10 +7506,14 @@ Instruction *InstCombinerImpl::foldICmpCommutative(CmpPredicate Pred,
74677506
if (Instruction *NI = foldSelectICmp(Pred, SI, Op1, CxtI))
74687507
return NI;
74697508

7470-
if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(Op0))
7509+
if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(Op0)) {
74717510
if (Instruction *Res = foldICmpWithMinMax(CxtI, MinMax, Op1, Pred))
74727511
return Res;
74737512

7513+
if (Instruction *Res = foldICmpWithClamp(CxtI, Op1, MinMax))
7514+
return Res;
7515+
}
7516+
74747517
{
74757518
Value *X;
74767519
const APInt *C;

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
725725
Instruction *foldICmpBinOp(ICmpInst &Cmp, const SimplifyQuery &SQ);
726726
Instruction *foldICmpWithMinMax(Instruction &I, MinMaxIntrinsic *MinMax,
727727
Value *Z, CmpPredicate Pred);
728+
Instruction *foldICmpWithClamp(ICmpInst &Cmp, Value *X, MinMaxIntrinsic *Min);
728729
Instruction *foldICmpEquality(ICmpInst &Cmp);
729730
Instruction *foldIRemByPowerOfTwoToBitTest(ICmpInst &I);
730731
Instruction *foldSignBitTest(ICmpInst &I);
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
declare void @use(i32)
5+
6+
define i1 @test_i32_eq(i32 %x) {
7+
; CHECK-LABEL: define i1 @test_i32_eq(
8+
; CHECK-SAME: i32 [[X:%.*]]) {
9+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], 95
10+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 256
11+
; CHECK-NEXT: ret i1 [[CMP]]
12+
;
13+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95)
14+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
15+
%cmp = icmp eq i32 %v2, %x
16+
ret i1 %cmp
17+
}
18+
19+
define i1 @test_i32_ne(i32 %x) {
20+
; CHECK-LABEL: define i1 @test_i32_ne(
21+
; CHECK-SAME: i32 [[X:%.*]]) {
22+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], -161
23+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -256
24+
; CHECK-NEXT: ret i1 [[CMP]]
25+
;
26+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95)
27+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
28+
%cmp = icmp ne i32 %v2, %x
29+
ret i1 %cmp
30+
}
31+
32+
define i1 @test_i32_eq_no_add(i32 %x) {
33+
; CHECK-LABEL: define i1 @test_i32_eq_no_add(
34+
; CHECK-SAME: i32 [[X:%.*]]) {
35+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], 161
36+
; CHECK-NEXT: ret i1 [[CMP]]
37+
;
38+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 0)
39+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
40+
%cmp = icmp eq i32 %v2, %x
41+
ret i1 %cmp
42+
}
43+
44+
define i1 @test_i32_ne_no_add(i32 %x) {
45+
; CHECK-LABEL: define i1 @test_i32_ne_no_add(
46+
; CHECK-SAME: i32 [[X:%.*]]) {
47+
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 160
48+
; CHECK-NEXT: ret i1 [[CMP]]
49+
;
50+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 0)
51+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
52+
%cmp = icmp ne i32 %v2, %x
53+
ret i1 %cmp
54+
}
55+
56+
define i1 @test_unsigned_eq(i32 %x) {
57+
; CHECK-LABEL: define i1 @test_unsigned_eq(
58+
; CHECK-SAME: i32 [[X:%.*]]) {
59+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], -10
60+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 91
61+
; CHECK-NEXT: ret i1 [[CMP]]
62+
;
63+
%v1 = tail call i32 @llvm.umax.i32(i32 %x, i32 10)
64+
%v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 100)
65+
%cmp = icmp eq i32 %v2, %x
66+
ret i1 %cmp
67+
}
68+
69+
define i1 @test_unsigned_ne(i32 %x) {
70+
; CHECK-LABEL: define i1 @test_unsigned_ne(
71+
; CHECK-SAME: i32 [[X:%.*]]) {
72+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], -101
73+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -91
74+
; CHECK-NEXT: ret i1 [[CMP]]
75+
;
76+
%v1 = tail call i32 @llvm.umax.i32(i32 %x, i32 10)
77+
%v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 100)
78+
%cmp = icmp ne i32 %v2, %x
79+
ret i1 %cmp
80+
}
81+
82+
83+
; Different bit widths
84+
define i1 @test_i8_eq(i8 %x) {
85+
; CHECK-LABEL: define i1 @test_i8_eq(
86+
; CHECK-SAME: i8 [[X:%.*]]) {
87+
; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X]], 50
88+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[TMP1]], 101
89+
; CHECK-NEXT: ret i1 [[CMP]]
90+
;
91+
%v1 = tail call i8 @llvm.smax.i8(i8 %x, i8 -50)
92+
%v2 = tail call i8 @llvm.smin.i8(i8 %v1, i8 50)
93+
%cmp = icmp eq i8 %v2, %x
94+
ret i1 %cmp
95+
}
96+
97+
define i1 @test_i16_eq(i16 %x) {
98+
; CHECK-LABEL: define i1 @test_i16_eq(
99+
; CHECK-SAME: i16 [[X:%.*]]) {
100+
; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[X]], 1000
101+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i16 [[TMP1]], 2001
102+
; CHECK-NEXT: ret i1 [[CMP]]
103+
;
104+
%v1 = tail call i16 @llvm.smax.i16(i16 %x, i16 -1000)
105+
%v2 = tail call i16 @llvm.smin.i16(i16 %v1, i16 1000)
106+
%cmp = icmp eq i16 %v2, %x
107+
ret i1 %cmp
108+
}
109+
110+
define i1 @test_i64_eq(i64 %x) {
111+
; CHECK-LABEL: define i1 @test_i64_eq(
112+
; CHECK-SAME: i64 [[X:%.*]]) {
113+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[X]], 1
114+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i64 [[TMP1]], -1
115+
; CHECK-NEXT: ret i1 [[CMP]]
116+
;
117+
%v1 = tail call i64 @llvm.smax.i64(i64 %x, i64 -1)
118+
%v2 = tail call i64 @llvm.smin.i64(i64 %v1, i64 9223372036854775806)
119+
%cmp = icmp eq i64 %v2, %x
120+
ret i1 %cmp
121+
}
122+
123+
; Negative tests - wrong predicate
124+
define i1 @test_wrong_pred_slt(i32 %x) {
125+
; CHECK-LABEL: define i1 @test_wrong_pred_slt(
126+
; CHECK-SAME: i32 [[X:%.*]]) {
127+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 160
128+
; CHECK-NEXT: ret i1 [[CMP]]
129+
;
130+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95)
131+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
132+
%cmp = icmp slt i32 %v2, %x
133+
ret i1 %cmp
134+
}
135+
136+
137+
; Negative tests - not a clamp pattern
138+
define i1 @test_not_clamp_pattern(i32 %x, i32 %y) {
139+
; CHECK-LABEL: define i1 @test_not_clamp_pattern(
140+
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
141+
; CHECK-NEXT: [[V1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[Y]], i32 -95)
142+
; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 160)
143+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[V2]], [[X]]
144+
; CHECK-NEXT: ret i1 [[CMP]]
145+
;
146+
%v1 = tail call i32 @llvm.smax.i32(i32 %y, i32 -95)
147+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
148+
%cmp = icmp eq i32 %v2, %x
149+
ret i1 %cmp
150+
}
151+
152+
; Negative tests - Lo >= Hi
153+
define i1 @test_invalid_range(i32 %x) {
154+
; CHECK-LABEL: define i1 @test_invalid_range(
155+
; CHECK-SAME: i32 [[X:%.*]]) {
156+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], 50
157+
; CHECK-NEXT: ret i1 [[CMP]]
158+
;
159+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 100)
160+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 50)
161+
%cmp = icmp eq i32 %v2, %x
162+
ret i1 %cmp
163+
}
164+
165+
; Negative tests - Lo is minimum signed value
166+
define i1 @test_lo_min_signed(i32 %x) {
167+
; CHECK-LABEL: define i1 @test_lo_min_signed(
168+
; CHECK-SAME: i32 [[X:%.*]]) {
169+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 161
170+
; CHECK-NEXT: ret i1 [[CMP]]
171+
;
172+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -2147483648)
173+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
174+
%cmp = icmp eq i32 %v2, %x
175+
ret i1 %cmp
176+
}
177+
178+
; Negative tests - Hi is maximum signed value
179+
define i1 @test_hi_max_signed(i32 %x) {
180+
; CHECK-LABEL: define i1 @test_hi_max_signed(
181+
; CHECK-SAME: i32 [[X:%.*]]) {
182+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], -96
183+
; CHECK-NEXT: ret i1 [[CMP]]
184+
;
185+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95)
186+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 2147483647)
187+
%cmp = icmp eq i32 %v2, %x
188+
ret i1 %cmp
189+
}
190+
191+
; Negative tests - Hi is maximum unsigned value
192+
define i1 @test_hi_max_unsigned(i32 %x) {
193+
; CHECK-LABEL: define i1 @test_hi_max_unsigned(
194+
; CHECK-SAME: i32 [[X:%.*]]) {
195+
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 9
196+
; CHECK-NEXT: ret i1 [[CMP]]
197+
;
198+
%v1 = tail call i32 @llvm.umax.i32(i32 %x, i32 10)
199+
%v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 4294967295)
200+
%cmp = icmp eq i32 %v2, %x
201+
ret i1 %cmp
202+
}
203+
204+
; Multi-use tests - multiple uses of max
205+
define i1 @test_multi_use_max(i32 %x) {
206+
; CHECK-LABEL: define i1 @test_multi_use_max(
207+
; CHECK-SAME: i32 [[X:%.*]]) {
208+
; CHECK-NEXT: [[V1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[X]], i32 -95)
209+
; CHECK-NEXT: call void @use(i32 [[V1]])
210+
; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 160)
211+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[V2]], [[X]]
212+
; CHECK-NEXT: ret i1 [[CMP]]
213+
;
214+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95)
215+
call void @use(i32 %v1)
216+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
217+
%cmp = icmp eq i32 %v2, %x
218+
ret i1 %cmp
219+
}
220+
221+
; Multi-use tests - multiple uses of min
222+
define i1 @test_multi_use_min(i32 %x) {
223+
; CHECK-LABEL: define i1 @test_multi_use_min(
224+
; CHECK-SAME: i32 [[X:%.*]]) {
225+
; CHECK-NEXT: [[V1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[X]], i32 -95)
226+
; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 160)
227+
; CHECK-NEXT: call void @use(i32 [[V2]])
228+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[V2]], [[X]]
229+
; CHECK-NEXT: ret i1 [[CMP]]
230+
;
231+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95)
232+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
233+
call void @use(i32 %v2)
234+
%cmp = icmp eq i32 %v2, %x
235+
ret i1 %cmp
236+
}
237+
238+
; Commuted tests
239+
define i1 @test_commuted_eq(i32 %x) {
240+
; CHECK-LABEL: define i1 @test_commuted_eq(
241+
; CHECK-SAME: i32 [[X:%.*]]) {
242+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], 95
243+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 256
244+
; CHECK-NEXT: ret i1 [[CMP]]
245+
;
246+
%v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95)
247+
%v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160)
248+
%cmp = icmp eq i32 %x, %v2
249+
ret i1 %cmp
250+
}
251+
252+
253+
; Vector tests - splat constants
254+
define <2 x i1> @test_vec_splat_eq(<2 x i32> %x) {
255+
; CHECK-LABEL: define <2 x i1> @test_vec_splat_eq(
256+
; CHECK-SAME: <2 x i32> [[X:%.*]]) {
257+
; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[X]], splat (i32 50)
258+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult <2 x i32> [[TMP1]], splat (i32 101)
259+
; CHECK-NEXT: ret <2 x i1> [[CMP]]
260+
;
261+
%v1 = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> %x, <2 x i32> <i32 -50, i32 -50>)
262+
%v2 = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> %v1, <2 x i32> <i32 50, i32 50>)
263+
%cmp = icmp eq <2 x i32> %v2, %x
264+
ret <2 x i1> %cmp
265+
}
266+
267+
; Vector tests - poison elements
268+
define <2 x i1> @test_vec_poison_eq(<2 x i32> %x) {
269+
; CHECK-LABEL: define <2 x i1> @test_vec_poison_eq(
270+
; CHECK-SAME: <2 x i32> [[X:%.*]]) {
271+
; CHECK-NEXT: [[V1:%.*]] = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> [[X]], <2 x i32> <i32 -50, i32 poison>)
272+
; CHECK-NEXT: [[V2:%.*]] = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[V1]], <2 x i32> <i32 50, i32 poison>)
273+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[V2]], [[X]]
274+
; CHECK-NEXT: ret <2 x i1> [[CMP]]
275+
;
276+
%v1 = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> %x, <2 x i32> <i32 -50, i32 poison>)
277+
%v2 = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> %v1, <2 x i32> <i32 50, i32 poison>)
278+
%cmp = icmp eq <2 x i32> %v2, %x
279+
ret <2 x i1> %cmp
280+
}
281+
282+
; Vector tests - non-splat
283+
define <2 x i1> @test_vec_non_splat_eq(<2 x i32> %x) {
284+
; CHECK-LABEL: define <2 x i1> @test_vec_non_splat_eq(
285+
; CHECK-SAME: <2 x i32> [[X:%.*]]) {
286+
; CHECK-NEXT: [[V1:%.*]] = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> [[X]], <2 x i32> <i32 -50, i32 -30>)
287+
; CHECK-NEXT: [[V2:%.*]] = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[V1]], <2 x i32> <i32 50, i32 70>)
288+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[V2]], [[X]]
289+
; CHECK-NEXT: ret <2 x i1> [[CMP]]
290+
;
291+
%v1 = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> %x, <2 x i32> <i32 -50, i32 -30>)
292+
%v2 = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> %v1, <2 x i32> <i32 50, i32 70>)
293+
%cmp = icmp eq <2 x i32> %v2, %x
294+
ret <2 x i1> %cmp
295+
}

0 commit comments

Comments
 (0)