[Clang] ensure mangled names are valid identifiers before being suggested in ifunc...
[llvm-project.git] / llvm / test / Transforms / InstCombine / masked-merge-xor.ll
blob463f220fcbb6dd1100f46fb0541c93b64cada03b
1 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
4 ; https://bugs.llvm.org/show_bug.cgi?id=6773
6 ; Patterns:
7 ;   (x & m) | (y & ~m)
8 ;   (x & m) ^ (y & ~m)
9 ;   (x & m) + (y & ~m)
10 ; Should be transformed into:
11 ;   (x & m) | (y & ~m)
12 ; And then into:
13 ;   ((x ^ y) & m) ^ y
15 ; ============================================================================ ;
16 ; Most basic positive tests
17 ; ============================================================================ ;
19 define i32 @p(i32 %x, i32 %y, i32 noundef %m) {
20 ; CHECK-LABEL: @p(
21 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], [[M:%.*]]
22 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
23 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], [[NEG]]
24 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
25 ; CHECK-NEXT:    ret i32 [[RET]]
27   %and = and i32 %x, %m
28   %neg = xor i32 %m, -1
29   %and1 = and i32 %neg, %y
30   %ret = xor i32 %and, %and1
31   ret i32 %ret
34 define <2 x i32> @p_splatvec(<2 x i32> %x, <2 x i32> %y, <2 x i32> noundef %m) {
35 ; CHECK-LABEL: @p_splatvec(
36 ; CHECK-NEXT:    [[AND:%.*]] = and <2 x i32> [[X:%.*]], [[M:%.*]]
37 ; CHECK-NEXT:    [[NEG:%.*]] = xor <2 x i32> [[M]], splat (i32 -1)
38 ; CHECK-NEXT:    [[AND1:%.*]] = and <2 x i32> [[Y:%.*]], [[NEG]]
39 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint <2 x i32> [[AND]], [[AND1]]
40 ; CHECK-NEXT:    ret <2 x i32> [[RET]]
42   %and = and <2 x i32> %x, %m
43   %neg = xor <2 x i32> %m, <i32 -1, i32 -1>
44   %and1 = and <2 x i32> %neg, %y
45   %ret = xor <2 x i32> %and, %and1
46   ret <2 x i32> %ret
49 define <3 x i32> @p_vec_undef(<3 x i32> %x, <3 x i32> %y, <3 x i32> noundef %m) {
50 ; CHECK-LABEL: @p_vec_undef(
51 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i32> [[X:%.*]], [[M:%.*]]
52 ; CHECK-NEXT:    [[NEG:%.*]] = xor <3 x i32> [[M]], <i32 -1, i32 undef, i32 -1>
53 ; CHECK-NEXT:    [[AND1:%.*]] = and <3 x i32> [[NEG]], [[Y:%.*]]
54 ; CHECK-NEXT:    [[RET:%.*]] = xor <3 x i32> [[AND]], [[AND1]]
55 ; CHECK-NEXT:    ret <3 x i32> [[RET]]
57   %and = and <3 x i32> %x, %m
58   %neg = xor <3 x i32> %m, <i32 -1, i32 undef, i32 -1>
59   %and1 = and <3 x i32> %neg, %y
60   %ret = xor <3 x i32> %and, %and1
61   ret <3 x i32> %ret
64 define <3 x i32> @p_vec_poison(<3 x i32> %x, <3 x i32> %y, <3 x i32> noundef %m) {
65 ; CHECK-LABEL: @p_vec_poison(
66 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i32> [[X:%.*]], [[M:%.*]]
67 ; CHECK-NEXT:    [[NEG:%.*]] = xor <3 x i32> [[M]], <i32 -1, i32 poison, i32 -1>
68 ; CHECK-NEXT:    [[AND1:%.*]] = and <3 x i32> [[Y:%.*]], [[NEG]]
69 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint <3 x i32> [[AND]], [[AND1]]
70 ; CHECK-NEXT:    ret <3 x i32> [[RET]]
72   %and = and <3 x i32> %x, %m
73   %neg = xor <3 x i32> %m, <i32 -1, i32 poison, i32 -1>
74   %and1 = and <3 x i32> %neg, %y
75   %ret = xor <3 x i32> %and, %and1
76   ret <3 x i32> %ret
79 ; ============================================================================ ;
80 ; Constant mask.
81 ; ============================================================================ ;
83 define i32 @p_constmask(i32 %x, i32 %y) {
84 ; CHECK-LABEL: @p_constmask(
85 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], 65280
86 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], -65281
87 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
88 ; CHECK-NEXT:    ret i32 [[RET]]
90   %and = and i32 %x, 65280
91   %and1 = and i32 %y, -65281
92   %ret = xor i32 %and, %and1
93   ret i32 %ret
96 define <2 x i32> @p_constmask_splatvec(<2 x i32> %x, <2 x i32> %y) {
97 ; CHECK-LABEL: @p_constmask_splatvec(
98 ; CHECK-NEXT:    [[AND:%.*]] = and <2 x i32> [[X:%.*]], splat (i32 65280)
99 ; CHECK-NEXT:    [[AND1:%.*]] = and <2 x i32> [[Y:%.*]], splat (i32 -65281)
100 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint <2 x i32> [[AND]], [[AND1]]
101 ; CHECK-NEXT:    ret <2 x i32> [[RET]]
103   %and = and <2 x i32> %x, <i32 65280, i32 65280>
104   %and1 = and <2 x i32> %y, <i32 -65281, i32 -65281>
105   %ret = xor <2 x i32> %and, %and1
106   ret <2 x i32> %ret
109 define <2 x i32> @p_constmask_vec(<2 x i32> %x, <2 x i32> %y) {
110 ; CHECK-LABEL: @p_constmask_vec(
111 ; CHECK-NEXT:    [[AND:%.*]] = and <2 x i32> [[X:%.*]], <i32 65280, i32 16776960>
112 ; CHECK-NEXT:    [[AND1:%.*]] = and <2 x i32> [[Y:%.*]], <i32 -65281, i32 -16776961>
113 ; CHECK-NEXT:    [[RET:%.*]] = xor <2 x i32> [[AND]], [[AND1]]
114 ; CHECK-NEXT:    ret <2 x i32> [[RET]]
116   %and = and <2 x i32> %x, <i32 65280, i32 16776960>
117   %and1 = and <2 x i32> %y, <i32 -65281, i32 -16776961>
118   %ret = xor <2 x i32> %and, %and1
119   ret <2 x i32> %ret
122 define <3 x i32> @p_constmask_vec_undef(<3 x i32> %x, <3 x i32> %y) {
123 ; CHECK-LABEL: @p_constmask_vec_undef(
124 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i32> [[X:%.*]], <i32 65280, i32 undef, i32 65280>
125 ; CHECK-NEXT:    [[AND1:%.*]] = and <3 x i32> [[Y:%.*]], <i32 -65281, i32 undef, i32 -65281>
126 ; CHECK-NEXT:    [[RET:%.*]] = xor <3 x i32> [[AND]], [[AND1]]
127 ; CHECK-NEXT:    ret <3 x i32> [[RET]]
129   %and = and <3 x i32> %x, <i32 65280, i32 undef, i32 65280>
130   %and1 = and <3 x i32> %y, <i32 -65281, i32 undef, i32 -65281>
131   %ret = xor <3 x i32> %and, %and1
132   ret <3 x i32> %ret
135 ; ============================================================================ ;
136 ; Constant mask with no common bits set, but common unset bits.
137 ; ============================================================================ ;
139 define i32 @p_constmask2(i32 %x, i32 %y) {
140 ; CHECK-LABEL: @p_constmask2(
141 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], 61440
142 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], -65281
143 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
144 ; CHECK-NEXT:    ret i32 [[RET]]
146   %and = and i32 %x, 61440
147   %and1 = and i32 %y, -65281
148   %ret = xor i32 %and, %and1
149   ret i32 %ret
152 define <2 x i32> @p_constmask2_splatvec(<2 x i32> %x, <2 x i32> %y) {
153 ; CHECK-LABEL: @p_constmask2_splatvec(
154 ; CHECK-NEXT:    [[AND:%.*]] = and <2 x i32> [[X:%.*]], splat (i32 61440)
155 ; CHECK-NEXT:    [[AND1:%.*]] = and <2 x i32> [[Y:%.*]], splat (i32 -65281)
156 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint <2 x i32> [[AND]], [[AND1]]
157 ; CHECK-NEXT:    ret <2 x i32> [[RET]]
159   %and = and <2 x i32> %x, <i32 61440, i32 61440>
160   %and1 = and <2 x i32> %y, <i32 -65281, i32 -65281>
161   %ret = xor <2 x i32> %and, %and1
162   ret <2 x i32> %ret
165 define <2 x i32> @p_constmask2_vec(<2 x i32> %x, <2 x i32> %y) {
166 ; CHECK-LABEL: @p_constmask2_vec(
167 ; CHECK-NEXT:    [[AND:%.*]] = and <2 x i32> [[X:%.*]], <i32 61440, i32 16711680>
168 ; CHECK-NEXT:    [[AND1:%.*]] = and <2 x i32> [[Y:%.*]], <i32 -65281, i32 -16776961>
169 ; CHECK-NEXT:    [[RET:%.*]] = xor <2 x i32> [[AND]], [[AND1]]
170 ; CHECK-NEXT:    ret <2 x i32> [[RET]]
172   %and = and <2 x i32> %x, <i32 61440, i32 16711680>
173   %and1 = and <2 x i32> %y, <i32 -65281, i32 -16776961>
174   %ret = xor <2 x i32> %and, %and1
175   ret <2 x i32> %ret
178 define <3 x i32> @p_constmask2_vec_undef(<3 x i32> %x, <3 x i32> %y) {
179 ; CHECK-LABEL: @p_constmask2_vec_undef(
180 ; CHECK-NEXT:    [[AND:%.*]] = and <3 x i32> [[X:%.*]], <i32 61440, i32 undef, i32 61440>
181 ; CHECK-NEXT:    [[AND1:%.*]] = and <3 x i32> [[Y:%.*]], <i32 -65281, i32 undef, i32 -65281>
182 ; CHECK-NEXT:    [[RET:%.*]] = xor <3 x i32> [[AND]], [[AND1]]
183 ; CHECK-NEXT:    ret <3 x i32> [[RET]]
185   %and = and <3 x i32> %x, <i32 61440, i32 undef, i32 61440>
186   %and1 = and <3 x i32> %y, <i32 -65281, i32 undef, i32 -65281>
187   %ret = xor <3 x i32> %and, %and1
188   ret <3 x i32> %ret
191 ; ============================================================================ ;
192 ; Commutativity.
193 ; ============================================================================ ;
195 ; Used to make sure that the IR complexity sorting does not interfere.
196 declare i32 @gen32()
198 define i32 @p_commutative0(i32 %x, i32 %y, i32 noundef %m) {
199 ; CHECK-LABEL: @p_commutative0(
200 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[M:%.*]], [[X:%.*]]
201 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
202 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], [[NEG]]
203 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
204 ; CHECK-NEXT:    ret i32 [[RET]]
206   %and = and i32 %m, %x ; swapped order
207   %neg = xor i32 %m, -1
208   %and1 = and i32 %neg, %y
209   %ret = xor i32 %and, %and1
210   ret i32 %ret
213 define i32 @p_commutative1(i32 %x, i32 noundef %m) {
214 ; CHECK-LABEL: @p_commutative1(
215 ; CHECK-NEXT:    [[Y:%.*]] = call i32 @gen32()
216 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], [[M:%.*]]
217 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
218 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y]], [[NEG]]
219 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
220 ; CHECK-NEXT:    ret i32 [[RET]]
222   %y = call i32 @gen32()
223   %and = and i32 %x, %m
224   %neg = xor i32 %m, -1
225   %and1 = and i32 %y, %neg; swapped order
226   %ret = xor i32 %and, %and1
227   ret i32 %ret
230 define i32 @p_commutative2(i32 %x, i32 %y, i32 noundef %m) {
231 ; CHECK-LABEL: @p_commutative2(
232 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], [[M:%.*]]
233 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
234 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], [[NEG]]
235 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND1]], [[AND]]
236 ; CHECK-NEXT:    ret i32 [[RET]]
238   %and = and i32 %x, %m
239   %neg = xor i32 %m, -1
240   %and1 = and i32 %neg, %y
241   %ret = xor i32 %and1, %and ; swapped order
242   ret i32 %ret
245 define i32 @p_commutative3(i32 %x, i32 noundef %m) {
246 ; CHECK-LABEL: @p_commutative3(
247 ; CHECK-NEXT:    [[Y:%.*]] = call i32 @gen32()
248 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[M:%.*]], [[X:%.*]]
249 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
250 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y]], [[NEG]]
251 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
252 ; CHECK-NEXT:    ret i32 [[RET]]
254   %y = call i32 @gen32()
255   %and = and i32 %m, %x ; swapped order
256   %neg = xor i32 %m, -1
257   %and1 = and i32 %y, %neg; swapped order
258   %ret = xor i32 %and, %and1
259   ret i32 %ret
262 define i32 @p_commutative4(i32 %x, i32 %y, i32 noundef %m) {
263 ; CHECK-LABEL: @p_commutative4(
264 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[M:%.*]], [[X:%.*]]
265 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
266 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], [[NEG]]
267 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND1]], [[AND]]
268 ; CHECK-NEXT:    ret i32 [[RET]]
270   %and = and i32 %m, %x ; swapped order
271   %neg = xor i32 %m, -1
272   %and1 = and i32 %neg, %y
273   %ret = xor i32 %and1, %and ; swapped order
274   ret i32 %ret
277 define i32 @p_commutative5(i32 %x, i32 noundef %m) {
278 ; CHECK-LABEL: @p_commutative5(
279 ; CHECK-NEXT:    [[Y:%.*]] = call i32 @gen32()
280 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], [[M:%.*]]
281 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
282 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y]], [[NEG]]
283 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND1]], [[AND]]
284 ; CHECK-NEXT:    ret i32 [[RET]]
286   %y = call i32 @gen32()
287   %and = and i32 %x, %m
288   %neg = xor i32 %m, -1
289   %and1 = and i32 %y, %neg; swapped order
290   %ret = xor i32 %and1, %and ; swapped order
291   ret i32 %ret
294 define i32 @p_commutative6(i32 %x, i32 noundef %m) {
295 ; CHECK-LABEL: @p_commutative6(
296 ; CHECK-NEXT:    [[Y:%.*]] = call i32 @gen32()
297 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[M:%.*]], [[X:%.*]]
298 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
299 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y]], [[NEG]]
300 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND1]], [[AND]]
301 ; CHECK-NEXT:    ret i32 [[RET]]
303   %y = call i32 @gen32()
304   %and = and i32 %m, %x ; swapped order
305   %neg = xor i32 %m, -1
306   %and1 = and i32 %y, %neg; swapped order
307   %ret = xor i32 %and1, %and ; swapped order
308   ret i32 %ret
311 define i32 @p_constmask_commutative(i32 %x, i32 %y) {
312 ; CHECK-LABEL: @p_constmask_commutative(
313 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], 65280
314 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], -65281
315 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND1]], [[AND]]
316 ; CHECK-NEXT:    ret i32 [[RET]]
318   %and = and i32 %x, 65280
319   %and1 = and i32 %y, -65281
320   %ret = xor i32 %and1, %and ; swapped order
321   ret i32 %ret
324 ; ============================================================================ ;
325 ; Negative tests. Should not be folded.
326 ; ============================================================================ ;
328 ; One use only.
330 declare void @use32(i32)
332 define i32 @n0_oneuse(i32 %x, i32 %y, i32 noundef %m) {
333 ; CHECK-LABEL: @n0_oneuse(
334 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], [[M:%.*]]
335 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], -1
336 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], [[NEG]]
337 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
338 ; CHECK-NEXT:    call void @use32(i32 [[AND]])
339 ; CHECK-NEXT:    call void @use32(i32 [[NEG]])
340 ; CHECK-NEXT:    call void @use32(i32 [[AND1]])
341 ; CHECK-NEXT:    ret i32 [[RET]]
343   %and = and i32 %x, %m
344   %neg = xor i32 %m, -1
345   %and1 = and i32 %neg, %y
346   %ret = xor i32 %and, %and1
347   call void @use32(i32 %and)
348   call void @use32(i32 %neg)
349   call void @use32(i32 %and1)
350   ret i32 %ret
353 define i32 @n0_constmask_oneuse(i32 %x, i32 %y) {
354 ; CHECK-LABEL: @n0_constmask_oneuse(
355 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], 65280
356 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], -65281
357 ; CHECK-NEXT:    [[RET:%.*]] = or disjoint i32 [[AND]], [[AND1]]
358 ; CHECK-NEXT:    call void @use32(i32 [[AND]])
359 ; CHECK-NEXT:    call void @use32(i32 [[AND1]])
360 ; CHECK-NEXT:    ret i32 [[RET]]
362   %and = and i32 %x, 65280
363   %and1 = and i32 %y, -65281
364   %ret = xor i32 %and, %and1
365   call void @use32(i32 %and)
366   call void @use32(i32 %and1)
367   ret i32 %ret
370 ; Bad xor constant
372 define i32 @n1_badxor(i32 %x, i32 %y, i32 %m) {
373 ; CHECK-LABEL: @n1_badxor(
374 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], [[M:%.*]]
375 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M]], 1
376 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[NEG]], [[Y:%.*]]
377 ; CHECK-NEXT:    [[RET:%.*]] = xor i32 [[AND]], [[AND1]]
378 ; CHECK-NEXT:    ret i32 [[RET]]
380   %and = and i32 %x, %m
381   %neg = xor i32 %m, 1 ; not -1
382   %and1 = and i32 %neg, %y
383   %ret = xor i32 %and, %and1
384   ret i32 %ret
387 ; Different mask is used
389 define i32 @n2_badmask(i32 %x, i32 %y, i32 %m1, i32 %m2) {
390 ; CHECK-LABEL: @n2_badmask(
391 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[M1:%.*]], [[X:%.*]]
392 ; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[M2:%.*]], -1
393 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], [[NEG]]
394 ; CHECK-NEXT:    [[RET:%.*]] = xor i32 [[AND]], [[AND1]]
395 ; CHECK-NEXT:    ret i32 [[RET]]
397   %and = and i32 %m1, %x
398   %neg = xor i32 %m2, -1 ; different mask, not %m1
399   %and1 = and i32 %neg, %y
400   %ret = xor i32 %and, %and1
401   ret i32 %ret
404 ; Different const mask is used
406 define i32 @n3_constmask_badmask(i32 %x, i32 %y) {
407 ; CHECK-LABEL: @n3_constmask_badmask(
408 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[X:%.*]], 65280
409 ; CHECK-NEXT:    [[AND1:%.*]] = and i32 [[Y:%.*]], -65280
410 ; CHECK-NEXT:    [[RET:%.*]] = xor i32 [[AND]], [[AND1]]
411 ; CHECK-NEXT:    ret i32 [[RET]]
413   %and = and i32 %x, 65280
414   %and1 = and i32 %y, -65280 ; not -65281, so they have one common bit
415   %ret = xor i32 %and, %and1
416   ret i32 %ret
419 define i32 @n3_constmask_samemask(i32 %x, i32 %y) {
420 ; CHECK-LABEL: @n3_constmask_samemask(
421 ; CHECK-NEXT:    [[AND2:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
422 ; CHECK-NEXT:    [[RET:%.*]] = and i32 [[AND2]], 65280
423 ; CHECK-NEXT:    ret i32 [[RET]]
425   %and = and i32 %x, 65280
426   %and1 = and i32 %y, 65280 ; both masks are the same
427   %ret = xor i32 %and, %and1
428   ret i32 %ret