1; Tests various aspects of x86 immediate encoding. Some encodings are shorter.
2; For example, the encoding is shorter for 8-bit immediates or when using EAX.
3; This assumes that EAX is chosen as the first free register in O2 mode.
4
5; RUN: %p2i --filetype=obj --disassemble -i %s --args -O2 | FileCheck %s
6
7define internal i32 @testXor8Imm8(i32 %arg) {
8entry:
9  %arg_i8 = trunc i32 %arg to i8
10  %result_i8 = xor i8 %arg_i8, 127
11  %result = zext i8 %result_i8 to i32
12  ret i32 %result
13}
14; CHECK-LABEL: testXor8Imm8
15; CHECK: 34 7f   xor al
16
17define internal i32 @testXor8Imm8Neg(i32 %arg) {
18entry:
19  %arg_i8 = trunc i32 %arg to i8
20  %result_i8 = xor i8 %arg_i8, -128
21  %result = zext i8 %result_i8 to i32
22  ret i32 %result
23}
24; CHECK-LABEL: testXor8Imm8Neg
25; CHECK: 34 80   xor al
26
27define internal i32 @testXor8Imm8NotEAX(i32 %arg, i32 %arg2, i32 %arg3) {
28entry:
29  %arg_i8 = trunc i32 %arg to i8
30  %arg2_i8 = trunc i32 %arg2 to i8
31  %arg3_i8 = trunc i32 %arg3 to i8
32  %x1 = xor i8 %arg_i8, 127
33  %x2 = xor i8 %arg2_i8, 127
34  %x3 = xor i8 %arg3_i8, 127
35  %x4 = add i8 %x1, %x2
36  %x5 = add i8 %x4, %x3
37  %result = zext i8 %x5 to i32
38  ret i32 %result
39}
40; CHECK-LABEL: testXor8Imm8NotEAX
41; CHECK: 80 f{{[1-3]}} 7f xor {{[^a]}}l
42
43define internal i32 @testXor16Imm8(i32 %arg) {
44entry:
45  %arg_i16 = trunc i32 %arg to i16
46  %result_i16 = xor i16 %arg_i16, 127
47  %result = zext i16 %result_i16 to i32
48  ret i32 %result
49}
50; CHECK-LABEL: testXor16Imm8
51; CHECK: 66 83 f0 7f  xor ax
52
53define internal i32 @testXor16Imm8Neg(i32 %arg) {
54entry:
55  %arg_i16 = trunc i32 %arg to i16
56  %result_i16 = xor i16 %arg_i16, -128
57  %result = zext i16 %result_i16 to i32
58  ret i32 %result
59}
60; CHECK-LABEL: testXor16Imm8Neg
61; CHECK: 66 83 f0 80  xor ax
62
63define internal i32 @testXor16Imm16Eax(i32 %arg) {
64entry:
65  %arg_i16 = trunc i32 %arg to i16
66  %tmp = xor i16 %arg_i16, 1024
67  %result_i16 = add i16 %tmp, 1
68  %result = zext i16 %result_i16 to i32
69  ret i32 %result
70}
71; CHECK-LABEL: testXor16Imm16Eax
72; CHECK: 66 35 00 04  xor ax
73; CHECK-NEXT: add ax,0x1
74
75define internal i32 @testXor16Imm16NegEax(i32 %arg) {
76entry:
77  %arg_i16 = trunc i32 %arg to i16
78  %tmp = xor i16 %arg_i16, -256
79  %result_i16 = add i16 %tmp, 1
80  %result = zext i16 %result_i16 to i32
81  ret i32 %result
82}
83; CHECK-LABEL: testXor16Imm16NegEax
84; CHECK: 66 35 00 ff  xor ax
85; CHECK-NEXT: add ax,0x1
86
87define internal i32 @testXor16Imm16NotEAX(i32 %arg_i32, i32 %arg2_i32, i32 %arg3_i32) {
88entry:
89  %arg = trunc i32 %arg_i32 to i16
90  %arg2 = trunc i32 %arg2_i32 to i16
91  %arg3 = trunc i32 %arg3_i32 to i16
92  %x = xor i16 %arg, 32767
93  %x2 = xor i16 %arg2, 32767
94  %x3 = xor i16 %arg3, 32767
95  %add1 = add i16 %x, %x2
96  %add2 = add i16 %add1, %x3
97  %result = zext i16 %add2 to i32
98  ret i32 %result
99}
100; CHECK-LABEL: testXor16Imm16NotEAX
101; CHECK: 66 81 f{{[1-3]}} ff 7f  xor {{[^a]}}x
102; CHECK-NEXT: 66 81 f{{[1-3]}} ff 7f  xor {{[^a]}}x
103
104define internal i32 @testXor32Imm8(i32 %arg) {
105entry:
106  %result = xor i32 %arg, 127
107  ret i32 %result
108}
109; CHECK-LABEL: testXor32Imm8
110; CHECK: 83 f0 7f   xor eax
111
112define internal i32 @testXor32Imm8Neg(i32 %arg) {
113entry:
114  %result = xor i32 %arg, -128
115  ret i32 %result
116}
117; CHECK-LABEL: testXor32Imm8Neg
118; CHECK: 83 f0 80   xor eax
119
120define internal i32 @testXor32Imm32Eax(i32 %arg) {
121entry:
122  %result = xor i32 %arg, 16777216
123  ret i32 %result
124}
125; CHECK-LABEL: testXor32Imm32Eax
126; CHECK: 35 00 00 00 01   xor eax
127
128define internal i32 @testXor32Imm32NegEax(i32 %arg) {
129entry:
130  %result = xor i32 %arg, -256
131  ret i32 %result
132}
133; CHECK-LABEL: testXor32Imm32NegEax
134; CHECK: 35 00 ff ff ff   xor eax
135
136define internal i32 @testXor32Imm32NotEAX(i32 %arg, i32 %arg2, i32 %arg3) {
137entry:
138  %x = xor i32 %arg, 32767
139  %x2 = xor i32 %arg2, 32767
140  %x3 = xor i32 %arg3, 32767
141  %add1 = add i32 %x, %x2
142  %add2 = add i32 %add1, %x3
143  ret i32 %add2
144}
145; CHECK-LABEL: testXor32Imm32NotEAX
146; CHECK: 81 f{{[1-3]}} ff 7f 00 00   xor e{{[^a]}}x,
147
148; Should be similar for add, sub, etc., so sample a few.
149
150define internal i32 @testAdd8Imm8(i32 %arg) {
151entry:
152  %arg_i8 = trunc i32 %arg to i8
153  %result_i8 = add i8 %arg_i8, 126
154  %result = zext i8 %result_i8 to i32
155  ret i32 %result
156}
157; CHECK-LABEL: testAdd8Imm8
158; CHECK: 04 7e   add al
159
160define internal i32 @testSub8Imm8(i32 %arg) {
161entry:
162  %arg_i8 = trunc i32 %arg to i8
163  %result_i8 = sub i8 %arg_i8, 125
164  %result = zext i8 %result_i8 to i32
165  ret i32 %result
166}
167; CHECK-LABEL: testSub8Imm8
168; CHECK: 2c 7d  sub al
169
170; imul has some shorter 8-bit immediate encodings.
171; It also has a shorter encoding for eax, but we don't do that yet.
172
173define internal i32 @testMul16Imm8(i32 %arg) {
174entry:
175  %arg_i16 = trunc i32 %arg to i16
176  %tmp = mul i16 %arg_i16, 99
177  %result_i16 = add i16 %tmp, 1
178  %result = zext i16 %result_i16 to i32
179  ret i32 %result
180}
181; CHECK-LABEL: testMul16Imm8
182; CHECK: 66 6b c0 63  imul ax,ax
183; CHECK-NEXT: add ax,0x1
184
185define internal i32 @testMul16Imm8Neg(i32 %arg) {
186entry:
187  %arg_i16 = trunc i32 %arg to i16
188  %tmp = mul i16 %arg_i16, -111
189  %result_i16 = add i16 %tmp, 1
190  %result = zext i16 %result_i16 to i32
191  ret i32 %result
192}
193; CHECK-LABEL: testMul16Imm8Neg
194; CHECK: 66 6b c0 91  imul ax,ax
195; CHECK-NEXT: add ax,0x1
196
197define internal i32 @testMul16Imm16(i32 %arg) {
198entry:
199  %arg_i16 = trunc i32 %arg to i16
200  %tmp = mul i16 %arg_i16, 1025
201  %result_i16 = add i16 %tmp, 1
202  %result = zext i16 %result_i16 to i32
203  ret i32 %result
204}
205; CHECK-LABEL: testMul16Imm16
206; CHECK: 66 69 c0 01 04  imul ax,ax
207; CHECK-NEXT: add ax,0x1
208
209define internal i32 @testMul16Imm16Neg(i32 %arg) {
210entry:
211  %arg_i16 = trunc i32 %arg to i16
212  %tmp = mul i16 %arg_i16, -255
213  %result_i16 = add i16 %tmp, 1
214  %result = zext i16 %result_i16 to i32
215  ret i32 %result
216}
217; CHECK-LABEL: testMul16Imm16Neg
218; CHECK: 66 69 c0 01 ff  imul ax,ax,0xff01
219; CHECK-NEXT: add ax,0x1
220
221define internal i32 @testMul32Imm8(i32 %arg) {
222entry:
223  %result = mul i32 %arg, 99
224  ret i32 %result
225}
226; CHECK-LABEL: testMul32Imm8
227; CHECK: 6b c0 63  imul eax,eax
228
229define internal i32 @testMul32Imm8Neg(i32 %arg) {
230entry:
231  %result = mul i32 %arg, -111
232  ret i32 %result
233}
234; CHECK-LABEL: testMul32Imm8Neg
235; CHECK: 6b c0 91  imul eax,eax
236
237define internal i32 @testMul32Imm16(i32 %arg) {
238entry:
239  %result = mul i32 %arg, 1025
240  ret i32 %result
241}
242; CHECK-LABEL: testMul32Imm16
243; CHECK: 69 c0 01 04 00 00  imul eax,eax
244
245define internal i32 @testMul32Imm16Neg(i32 %arg) {
246entry:
247  %result = mul i32 %arg, -255
248  ret i32 %result
249}
250; CHECK-LABEL: testMul32Imm16Neg
251; CHECK: 69 c0 01 ff ff ff  imul eax,eax,0xffffff01
252
253define internal i32 @testMul32Imm32ThreeAddress(i32 %a) {
254entry:
255  %mul = mul i32 232, %a
256  %add = add i32 %mul, %a
257  ret i32 %add
258}
259; CHECK-LABEL: testMul32Imm32ThreeAddress
260; CHECK: 69 c8 e8 00 00 00  imul ecx,eax,0xe8
261
262define internal i32 @testMul32Mem32Imm32ThreeAddress(i32 %addr_arg) {
263entry:
264  %__1 = inttoptr i32 %addr_arg to i32*
265  %a = load i32, i32* %__1, align 1
266  %mul = mul i32 232, %a
267  ret i32 %mul
268}
269; CHECK-LABEL: testMul32Mem32Imm32ThreeAddress
270; CHECK: 69 00 e8 00 00 00  imul eax,DWORD PTR [eax],0xe8
271
272define internal i32 @testMul32Imm8ThreeAddress(i32 %a) {
273entry:
274  %mul = mul i32 127, %a
275  %add = add i32 %mul, %a
276  ret i32 %add
277}
278; CHECK-LABEL: testMul32Imm8ThreeAddress
279; CHECK: 6b c8 7f imul ecx,eax,0x7f
280
281define internal i32 @testMul32Mem32Imm8ThreeAddress(i32 %addr_arg) {
282entry:
283  %__1 = inttoptr i32 %addr_arg to i32*
284  %a = load i32, i32* %__1, align 1
285  %mul = mul i32 127, %a
286  ret i32 %mul
287}
288; CHECK-LABEL: testMul32Mem32Imm8ThreeAddress
289; CHECK: 6b 00 7f imul eax,DWORD PTR [eax],0x7f
290
291define internal i32 @testMul16Imm16ThreeAddress(i32 %a) {
292entry:
293  %arg_i16 = trunc i32 %a to i16
294  %mul = mul i16 232, %arg_i16
295  %add = add i16 %mul, %arg_i16
296  %result = zext i16 %add to i32
297  ret i32 %result
298}
299; CHECK-LABEL: testMul16Imm16ThreeAddress
300; CHECK: 66 69 c8 e8 00 imul cx,ax,0xe8
301
302define internal i32 @testMul16Mem16Imm16ThreeAddress(i32 %addr_arg) {
303entry:
304  %__1 = inttoptr i32 %addr_arg to i16*
305  %a = load i16, i16* %__1, align 1
306  %mul = mul i16 232, %a
307  %result = zext i16 %mul to i32
308  ret i32 %result
309}
310; CHECK-LABEL: testMul16Mem16Imm16ThreeAddress
311; CHECK: 66 69 00 e8 00 imul ax,WORD PTR [eax],0xe8
312
313define internal i32 @testMul16Imm8ThreeAddress(i32 %a) {
314entry:
315  %arg_i16 = trunc i32 %a to i16
316  %mul = mul i16 127, %arg_i16
317  %add = add i16 %mul, %arg_i16
318  %result = zext i16 %add to i32
319  ret i32 %result
320}
321; CHECK-LABEL: testMul16Imm8ThreeAddress
322; CHECK: 66 6b c8 7f imul cx,ax,0x7f
323
324define internal i32 @testMul16Mem16Imm8ThreeAddress(i32 %addr_arg) {
325entry:
326  %__1 = inttoptr i32 %addr_arg to i16*
327  %a = load i16, i16* %__1, align 1
328  %mul = mul i16 127, %a
329  %result = zext i16 %mul to i32
330  ret i32 %result
331}
332; CHECK-LABEL: testMul16Mem16Imm8ThreeAddress
333; CHECK: 66 6b 00 7f imul ax,WORD PTR [eax],0x7f
334
335; The GPR shift instructions either allow an 8-bit immediate or
336; have a special encoding for "1".
337define internal i32 @testShl16Imm8(i32 %arg) {
338entry:
339  %arg_i16 = trunc i32 %arg to i16
340  %tmp = shl i16 %arg_i16, 13
341  %result = zext i16 %tmp to i32
342  ret i32 %result
343}
344; CHECK-LABEL: testShl16Imm8
345; CHECK: 66 c1 e0 0d shl ax,0xd
346
347define internal i32 @testShl16Imm1(i32 %arg) {
348entry:
349  %arg_i16 = trunc i32 %arg to i16
350  %tmp = shl i16 %arg_i16, 1
351  %result = zext i16 %tmp to i32
352  ret i32 %result
353}
354; CHECK-LABEL: testShl16Imm1
355; CHECK: 66 d1 e0 shl ax
356
357; Currently the "test" instruction is used for 64-bit shifts, and
358; for ctlz 64-bit, so we use those to test the "test" instruction.
359; One optimization for "test": the "test" instruction is essentially a
360; bitwise AND that doesn't modify the two source operands, so for immediates
361; under 8-bits and registers with 8-bit variants we can use the shorter form.
362
363define internal i64 @test_via_shl64Bit(i64 %a, i64 %b) {
364entry:
365  %shl = shl i64 %a, %b
366  ret i64 %shl
367}
368; CHECK-LABEL: test_via_shl64Bit
369; CHECK: 0f a5 c2  shld edx,eax,cl
370; CHECK: d3 e0     shl eax,cl
371; CHECK: f6 c1 20  test cl,0x20
372
373; Test a few register encodings of "test".
374declare i64 @llvm.ctlz.i64(i64, i1)
375
376define internal i64 @test_via_ctlz_64(i64 %x, i64 %y, i64 %z, i64 %w) {
377entry:
378  %r = call i64 @llvm.ctlz.i64(i64 %x, i1 false)
379  %r2 = call i64 @llvm.ctlz.i64(i64 %y, i1 false)
380  %r3 = call i64 @llvm.ctlz.i64(i64 %z, i1 false)
381  %r4 = call i64 @llvm.ctlz.i64(i64 %w, i1 false)
382  %res1 = add i64 %r, %r2
383  %res2 = add i64 %r3, %r4
384  %res = add i64 %res1, %res2
385  ret i64 %res
386}
387; CHECK-LABEL: test_via_ctlz_64
388; CHECK-DAG: 85 c0 test eax,eax
389; CHECK-DAG: 85 db test ebx,ebx
390; CHECK-DAG: 85 f6 test esi,esi
391