xref: /llvm-project/llvm/test/CodeGen/X86/movtopush.ll (revision 2f448bf509432c1a19ec46ab8cbc7353c03c6280)
1; RUN: llc < %s -mtriple=i686-windows | FileCheck %s -check-prefix=NORMAL
2; RUN: llc < %s -mtriple=i686-windows -no-x86-call-frame-opt | FileCheck %s -check-prefix=NOPUSH
3; RUN: llc < %s -mtriple=x86_64-windows | FileCheck %s -check-prefix=X64
4; RUN: llc < %s -mtriple=i686-pc-linux | FileCheck %s -check-prefix=LINUX
5
6%class.Class = type { i32 }
7%struct.s = type { i64 }
8
9declare void @good(i32 %a, i32 %b, i32 %c, i32 %d)
10declare void @inreg(i32 %a, i32 inreg %b, i32 %c, i32 %d)
11declare x86_thiscallcc void @thiscall(ptr %class, i32 %a, i32 %b, i32 %c, i32 %d)
12declare void @oneparam(i32 %a)
13declare void @eightparams(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h)
14declare void @eightparams16(i16 %a, i16 %b, i16 %c, i16 %d, i16 %e, i16 %f, i16 %g, i16 %h)
15declare void @eightparams64(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f, i64 %g, i64 %h)
16declare void @struct(ptr byval(%struct.s) %a, i32 %b, i32 %c, i32 %d)
17declare void @inalloca(ptr inalloca(<{ %struct.s }>))
18
19declare ptr @llvm.stacksave()
20declare void @llvm.stackrestore(ptr)
21
22; We should get pushes for x86, even though there is a reserved call frame.
23; Make sure we don't touch x86-64, and that turning it off works.
24; NORMAL-LABEL: test1:
25; NORMAL-NOT: subl {{.*}} %esp
26; NORMAL: pushl   $4
27; NORMAL-NEXT: pushl   $3
28; NORMAL-NEXT: pushl   $2
29; NORMAL-NEXT: pushl   $1
30; NORMAL-NEXT: call
31; NORMAL-NEXT: addl $16, %esp
32; X64-LABEL: test1:
33; X64: movl    $1, %ecx
34; X64-NEXT: movl    $2, %edx
35; X64-NEXT: movl    $3, %r8d
36; X64-NEXT: movl    $4, %r9d
37; X64-NEXT: callq   good
38; NOPUSH-LABEL: test1:
39; NOPUSH: subl    $16, %esp
40; NOPUSH-NEXT: movl    $4, 12(%esp)
41; NOPUSH-NEXT: movl    $3, 8(%esp)
42; NOPUSH-NEXT: movl    $2, 4(%esp)
43; NOPUSH-NEXT: movl    $1, (%esp)
44; NOPUSH-NEXT: call
45; NOPUSH-NEXT: addl $16, %esp
46define void @test1() {
47entry:
48  call void @good(i32 1, i32 2, i32 3, i32 4)
49  ret void
50}
51
52; If we have a reserved frame, we should have pushes
53; NORMAL-LABEL: test2:
54; NORMAL-NOT: subl {{.*}} %esp
55; NORMAL: pushl   $4
56; NORMAL-NEXT: pushl   $3
57; NORMAL-NEXT: pushl   $2
58; NORMAL-NEXT: pushl   $1
59; NORMAL-NEXT: call
60define void @test2(i32 %k) {
61entry:
62  %a = alloca i32, i32 %k
63  call void @good(i32 1, i32 2, i32 3, i32 4)
64  ret void
65}
66
67; Again, we expect a sequence of 4 immediate pushes
68; Checks that we generate the right pushes for >8bit immediates
69; NORMAL-LABEL: test2b:
70; NORMAL-NOT: subl {{.*}} %esp
71; NORMAL: pushl   $4096
72; NORMAL-NEXT: pushl   $3072
73; NORMAL-NEXT: pushl   $2048
74; NORMAL-NEXT: pushl   $1024
75; NORMAL-NEXT: call
76; NORMAL-NEXT: addl $16, %esp
77define void @test2b() optsize {
78entry:
79  call void @good(i32 1024, i32 2048, i32 3072, i32 4096)
80  ret void
81}
82
83; The first push should push a register
84; NORMAL-LABEL: test3:
85; NORMAL-NOT: subl {{.*}} %esp
86; NORMAL: pushl   $4
87; NORMAL-NEXT: pushl   $3
88; NORMAL-NEXT: pushl   $2
89; NORMAL-NEXT: pushl   %e{{..}}
90; NORMAL-NEXT: call
91; NORMAL-NEXT: addl $16, %esp
92define void @test3(i32 %k) optsize {
93entry:
94  %f = add i32 %k, 1
95  call void @good(i32 %f, i32 2, i32 3, i32 4)
96  ret void
97}
98
99; We support weird calling conventions
100; NORMAL-LABEL: test4:
101; NORMAL: movl    $2, %eax
102; NORMAL-NEXT: pushl   $4
103; NORMAL-NEXT: pushl   $3
104; NORMAL-NEXT: pushl   $1
105; NORMAL-NEXT: call
106; NORMAL-NEXT: addl $12, %esp
107define void @test4() optsize {
108entry:
109  call void @inreg(i32 1, i32 inreg 2, i32 3, i32 4)
110  ret void
111}
112
113; NORMAL-LABEL: test4b:
114; NORMAL: movl 4(%esp), %ecx
115; NORMAL-NEXT: pushl   $4
116; NORMAL-NEXT: pushl   $3
117; NORMAL-NEXT: pushl   $2
118; NORMAL-NEXT: pushl   $1
119; NORMAL-NEXT: call
120; NORMAL-NEXT: ret
121define void @test4b(ptr %f) optsize {
122entry:
123  call x86_thiscallcc void @thiscall(ptr %f, i32 1, i32 2, i32 3, i32 4)
124  ret void
125}
126
127; Check that pushing the addresses of globals (Or generally, things that
128; aren't exactly immediates) isn't broken.
129; Fixes PR21878.
130; NORMAL-LABEL: test6:
131; NORMAL: pushl    $_ext
132; NORMAL-NEXT: call
133declare void @f(ptr)
134@ext = external dso_local constant i8
135
136define void @test6() {
137  call void @f(ptr @ext)
138  br label %bb
139bb:
140  alloca i32
141  ret void
142}
143
144; Check that we fold simple cases into the push
145; NORMAL-LABEL: test7:
146; NORMAL-NOT: subl {{.*}} %esp
147; NORMAL: movl 4(%esp), [[EAX:%e..]]
148; NORMAL-NEXT: pushl   $4
149; NORMAL-NEXT: pushl   ([[EAX]])
150; NORMAL-NEXT: pushl   $2
151; NORMAL-NEXT: pushl   $1
152; NORMAL-NEXT: call
153; NORMAL-NEXT: addl $16, %esp
154define void @test7(ptr %ptr) optsize {
155entry:
156  %val = load i32, ptr %ptr
157  call void @good(i32 1, i32 2, i32 %val, i32 4)
158  ret void
159}
160
161; Fold stack-relative loads into the push, with correct offset
162; In particular, at the second push, %b was at 12(%esp) and
163; %a wast at 8(%esp), but the second push bumped %esp, so %a
164; is now it at 12(%esp)
165; NORMAL-LABEL: test8:
166; NORMAL: pushl   $4
167; NORMAL-NEXT: pushl   12(%esp)
168; NORMAL-NEXT: pushl   12(%esp)
169; NORMAL-NEXT: pushl   $1
170; NORMAL-NEXT: call
171; NORMAL-NEXT: addl $16, %esp
172define void @test8(i32 %a, i32 %b) optsize {
173entry:
174  call void @good(i32 1, i32 %a, i32 %b, i32 4)
175  ret void
176}
177
178; If one function is using push instructions, and the other isn't
179; (because it has frame-index references), then we must resolve
180; these references correctly.
181; NORMAL-LABEL: test9:
182; NORMAL-NOT: leal (%esp),
183; NORMAL: pushl $4
184; NORMAL-NEXT: pushl $3
185; NORMAL-NEXT: pushl $2
186; NORMAL-NEXT: pushl $1
187; NORMAL-NEXT: call
188; NORMAL-NEXT: addl $16, %esp
189; NORMAL-NEXT: movl (%esp), [[E1:%e..]]
190; NORMAL-NEXT: movl 4(%esp), [[E2:%e..]]
191; NORMAL-NEXT: leal 16(%esp), [[E3:%e..]]
192; NORMAL-NEXT: leal 12(%esp), [[E4:%e..]]
193; NORMAL-NEXT: pushl   [[E3]]
194; NORMAL-NEXT: pushl   [[E4]]
195; NORMAL-NEXT: pushl   $6
196; NORMAL-NEXT: pushl   [[E2]]
197; NORMAL-NEXT: pushl   [[E1]]
198; NORMAL-NEXT: call
199; NORMAL-NEXT: addl $20, %esp
200define void @test9() optsize {
201entry:
202  %p = alloca i32, align 4
203  %q = alloca i32, align 4
204  %s = alloca %struct.s, align 8
205  call void @good(i32 1, i32 2, i32 3, i32 4)
206  %pv = ptrtoint ptr %p to i32
207  %qv = ptrtoint ptr %q to i32
208  call void @struct(ptr byval(%struct.s) %s, i32 6, i32 %qv, i32 %pv)
209  ret void
210}
211
212; We can end up with an indirect call which gets reloaded on the spot.
213; Make sure we reference the correct stack slot - we spill into (%esp)
214; and reload from 16(%esp) due to the pushes.
215; NORMAL-LABEL: test10:
216; NORMAL: movl $_good, [[ALLOC:.*]]
217; NORMAL-NEXT: movl [[ALLOC]], [[EAX:%e..]]
218; NORMAL-NEXT: movl [[EAX]], (%esp) # 4-byte Spill
219; NORMAL: nop
220; NORMAL: pushl $4
221; NORMAL-NEXT: pushl $3
222; NORMAL-NEXT: pushl $2
223; NORMAL-NEXT: pushl $1
224; NORMAL-NEXT: calll *16(%esp)
225; NORMAL-NEXT: addl $24, %esp
226define void @test10() optsize {
227  %stack_fptr = alloca ptr
228  store ptr @good, ptr %stack_fptr
229  %good_ptr = load volatile ptr, ptr %stack_fptr
230  call void asm sideeffect "nop", "~{ax},~{bx},~{cx},~{dx},~{bp},~{si},~{di}"()
231  call void (i32, i32, i32, i32) %good_ptr(i32 1, i32 2, i32 3, i32 4)
232  ret void
233}
234
235; We can't fold the load from the global into the push because of
236; interference from the store
237; NORMAL-LABEL: test11:
238; NORMAL: movl    _the_global, [[EAX:%e..]]
239; NORMAL-NEXT: movl    $42, _the_global
240; NORMAL-NEXT: pushl $4
241; NORMAL-NEXT: pushl $3
242; NORMAL-NEXT: pushl $2
243; NORMAL-NEXT: pushl [[EAX]]
244; NORMAL-NEXT: call
245; NORMAL-NEXT: addl $16, %esp
246@the_global = external dso_local global i32
247define void @test11() optsize {
248  %myload = load i32, ptr @the_global
249  store i32 42, ptr @the_global
250  call void @good(i32 %myload, i32 2, i32 3, i32 4)
251  ret void
252}
253
254; Converting one mov into a push isn't worth it when
255; doing so forces too much overhead for other calls.
256; NORMAL-LABEL: test12:
257; NORMAL:       pushl  $8
258; NORMAL-NEXT:  pushl  $7
259; NORMAL-NEXT:  pushl  $6
260; NORMAL-NEXT:  pushl  $5
261; NORMAL-NEXT: calll _good
262define void @test12() optsize {
263entry:
264  %s = alloca %struct.s, align 4
265  call void @struct(ptr byval(%struct.s) %s, i32 2, i32 3, i32 4)
266  call void @good(i32 5, i32 6, i32 7, i32 8)
267  call void @struct(ptr byval(%struct.s) %s, i32 10, i32 11, i32 12)
268  ret void
269}
270
271; But if the gains outweigh the overhead, we should do it
272; NORMAL-LABEL: test12b:
273; NORMAL: pushl    $4
274; NORMAL-NEXT: pushl    $3
275; NORMAL-NEXT: pushl    $2
276; NORMAL-NEXT: pushl    $1
277; NORMAL-NEXT: calll _good
278; NORMAL-NEXT: addl    $16, %esp
279; NORMAL=NEXT: movl  (%esp), %eax
280; NORMAL=NEXT: movl  4(%esp), %ecx
281; NORMAL=NEXT: pushl  $8
282; NORMAL=NEXT: pushl  $7
283; NORMAL=NEXT: pushl  $6
284; NORMAL=NEXT: pushl  %ecx
285; NORMAL=NEXT: pushl  %eax
286; NORMAL=NEXT: calll  _struct
287; NORMAL=NEXT: addl  $20, %esp
288; NORMAL=NEXT: pushl  $12
289; NORMAL=NEXT: pushl  $11
290; NORMAL=NEXT: pushl  $10
291; NORMAL=NEXT: pushl  $9
292; NORMAL=NEXT: calll  _good
293; NORMAL=NEXT: addl  $16, %esp
294define void @test12b() optsize {
295entry:
296  %s = alloca %struct.s, align 4
297  call void @good(i32 1, i32 2, i32 3, i32 4)
298  call void @struct(ptr byval(%struct.s) %s, i32 6, i32 7, i32 8)
299  call void @good(i32 9, i32 10, i32 11, i32 12)
300  ret void
301}
302
303; Make sure the add does not prevent folding loads into pushes.
304; val1 and val2 will not be folded into pushes since they have
305; an additional use, but val3 should be.
306; NORMAL-LABEL: test13:
307; NORMAL: movl ([[P1:%e..]]), [[V1:%e..]]
308; NORMAL-NEXT: movl ([[P2:%e..]]), [[V2:%e..]]
309; NORMAL-NEXT: , [[ADD:%e..]]
310; NORMAL-NEXT: pushl [[ADD]]
311; NORMAL-NEXT: pushl ([[P3:%e..]])
312; NORMAL-NEXT: pushl [[V2]]
313; NORMAL-NEXT: pushl [[V1]]
314; NORMAL-NEXT: calll _good
315; NORMAL: movl [[P3]], %eax
316define ptr @test13(ptr inreg %ptr1, ptr inreg %ptr2, ptr inreg %ptr3) optsize {
317entry:
318  %val1 = load i32, ptr %ptr1
319  %val2 = load i32, ptr %ptr2
320  %val3 = load i32, ptr %ptr3
321  %add = add i32 %val1, %val2
322  call void @good(i32 %val1, i32 %val2, i32 %val3, i32 %add)
323  ret ptr %ptr3
324}
325
326; Make sure to fold adjacent stack adjustments.
327; LINUX-LABEL: pr27140:
328; LINUX: subl    $12, %esp
329; LINUX: .cfi_def_cfa_offset 16
330; LINUX-NOT: sub
331; LINUX: pushl   $4
332; LINUX: .cfi_adjust_cfa_offset 4
333; LINUX: pushl   $3
334; LINUX: .cfi_adjust_cfa_offset 4
335; LINUX: pushl   $2
336; LINUX: .cfi_adjust_cfa_offset 4
337; LINUX: pushl   $1
338; LINUX: .cfi_adjust_cfa_offset 4
339; LINUX: calll   good
340; LINUX: addl    $28, %esp
341; LINUX: .cfi_adjust_cfa_offset -28
342; LINUX-NOT: add
343; LINUX: retl
344define void @pr27140() optsize {
345entry:
346  tail call void @good(i32 1, i32 2, i32 3, i32 4)
347  ret void
348}
349
350; Check that a stack restore (leal -4(%ebp), %esp) doesn't get merged with a
351; stack adjustment (addl $12, %esp). Just because it's a lea doesn't mean it's
352; simply decreasing the stack pointer.
353; NORMAL-LABEL: test14:
354; NORMAL: calll _B_func
355; NORMAL: leal -4(%ebp), %esp
356; NORMAL-NOT: %esp
357; NORMAL: retl
358%struct.A = type { i32, i32 }
359%struct.B = type { i8 }
360declare x86_thiscallcc ptr @B_ctor(ptr returned, ptr byval(%struct.A))
361declare void @B_func(ptr sret(%struct.B), ptr, i32)
362define void @test14(ptr %a) {
363entry:
364  %ref.tmp = alloca %struct.B, align 1
365  %agg.tmp = alloca i64, align 8
366  %tmp = alloca %struct.B, align 1
367  %0 = load i64, ptr %a, align 4
368  store i64 %0, ptr %agg.tmp, align 4
369  %call = call x86_thiscallcc ptr @B_ctor(ptr returned %ref.tmp, ptr byval(%struct.A) %agg.tmp)
370  call void @B_func(ptr sret(%struct.B) %tmp, ptr %ref.tmp, i32 1)
371  ret void
372}
373
374; NORMAL-LABEL: pr34863_16
375; NORMAL:       movl  4(%esp), %eax
376; NORMAL-NEXT:  pushl  $65535
377; NORMAL-NEXT:  pushl  $0
378; NORMAL-NEXT:  pushl  %eax
379; NORMAL-NEXT:  pushl  %eax
380; NORMAL-NEXT:  pushl  %eax
381; NORMAL-NEXT:  pushl  %eax
382; NORMAL-NEXT:  pushl  %eax
383; NORMAL-NEXT:  pushl  %eax
384; NORMAL-NEXT:  calll  _eightparams16
385; NORMAL-NEXT:  addl  $32, %esp
386;
387; NOPUSH-LABEL: pr34863_16
388; NOPUSH:       subl  $32, %esp
389; NOPUSH-NEXT:  movl  36(%esp), %eax
390; NOPUSH-NEXT:  movl  %eax, 20(%esp)
391; NOPUSH-NEXT:  movl  %eax, 16(%esp)
392; NOPUSH-NEXT:  movl  %eax, 12(%esp)
393; NOPUSH-NEXT:  movl  %eax, 8(%esp)
394; NOPUSH-NEXT:  movl  %eax, 4(%esp)
395; NOPUSH-NEXT:  movl  %eax, (%esp)
396; NOPUSH-NEXT:  movl  $65535, 28(%esp)
397; NOPUSH-NEXT:  andl  $0, 24(%esp)
398; NOPUSH-NEXT:  calll  _eightparams16
399; NOPUSH-NEXT:   addl  $32, %esp
400define void @pr34863_16(i16 %x) minsize nounwind {
401entry:
402  tail call void @eightparams16(i16 %x, i16 %x, i16 %x, i16 %x, i16 %x, i16 %x, i16 0, i16 -1)
403  ret void
404}
405
406; NORMAL-LABEL: pr34863_32
407; NORMAL:      movl  4(%esp), %eax
408; NORMAL-NEXT: pushl  $-1
409; NORMAL-NEXT: pushl  $0
410; NORMAL-NEXT: pushl  %eax
411; NORMAL-NEXT: pushl  %eax
412; NORMAL-NEXT: pushl  %eax
413; NORMAL-NEXT: pushl  %eax
414; NORMAL-NEXT: pushl  %eax
415; NORMAL-NEXT: pushl  %eax
416; NORMAL-NEXT: calll  _eightparams
417; NORMAL-NEXT: addl  $32, %esp
418;
419; NOPUSH-LABEL: pr34863_32
420; NOPUSH:      subl  $32, %esp
421; NOPUSH-NEXT: movl  36(%esp), %eax
422; NOPUSH-NEXT: movl  %eax, 20(%esp)
423; NOPUSH-NEXT: movl  %eax, 16(%esp)
424; NOPUSH-NEXT: movl  %eax, 12(%esp)
425; NOPUSH-NEXT: movl  %eax, 8(%esp)
426; NOPUSH-NEXT: movl  %eax, 4(%esp)
427; NOPUSH-NEXT: movl  %eax, (%esp)
428; NOPUSH-NEXT: orl  $-1, 28(%esp)
429; NOPUSH-NEXT: andl  $0, 24(%esp)
430; NOPUSH-NEXT: calll  _eightparams
431; NOPUSH-NEXT: addl  $32, %esp
432define void @pr34863_32(i32 %x) minsize nounwind {
433entry:
434  tail call void @eightparams(i32 %x, i32 %x, i32 %x, i32 %x, i32 %x, i32 %x, i32 0, i32 -1)
435  ret void
436}
437
438; NORMAL-LABEL: pr34863_64
439; NORMAL:      movl  4(%esp), %eax
440; NORMAL-NEXT: movl  8(%esp), %ecx
441; NORMAL-NEXT: pushl  $-1
442; NORMAL-NEXT: pushl  $-1
443; NORMAL-NEXT: pushl  $0
444; NORMAL-NEXT: pushl  $0
445; NORMAL-NEXT: pushl  %ecx
446; NORMAL-NEXT: pushl  %eax
447; NORMAL-NEXT: pushl  %ecx
448; NORMAL-NEXT: pushl  %eax
449; NORMAL-NEXT: pushl  %ecx
450; NORMAL-NEXT: pushl  %eax
451; NORMAL-NEXT: pushl  %ecx
452; NORMAL-NEXT: pushl  %eax
453; NORMAL-NEXT: pushl  %ecx
454; NORMAL-NEXT: pushl  %eax
455; NORMAL-NEXT: pushl  %ecx
456; NORMAL-NEXT: pushl  %eax
457; NORMAL-NEXT: calll  _eightparams64
458; NORMAL-NEXT: addl  $64, %esp
459;
460; NOPUSH-LABEL: pr34863_64
461; NOPUSH:      subl  $64, %esp
462; NOPUSH-NEXT: movl  68(%esp), %eax
463; NOPUSH-NEXT: movl  72(%esp), %ecx
464; NOPUSH-NEXT: movl  %ecx, 44(%esp)
465; NOPUSH-NEXT: movl  %eax, 40(%esp)
466; NOPUSH-NEXT: movl  %ecx, 36(%esp)
467; NOPUSH-NEXT: movl  %eax, 32(%esp)
468; NOPUSH-NEXT: movl  %ecx, 28(%esp)
469; NOPUSH-NEXT: movl  %eax, 24(%esp)
470; NOPUSH-NEXT: movl  %ecx, 20(%esp)
471; NOPUSH-NEXT: movl  %eax, 16(%esp)
472; NOPUSH-NEXT: movl  %ecx, 12(%esp)
473; NOPUSH-NEXT: movl  %eax, 8(%esp)
474; NOPUSH-NEXT: movl  %ecx, 4(%esp)
475; NOPUSH-NEXT: movl  %eax, (%esp)
476; NOPUSH-NEXT: orl  $-1, 60(%esp)
477; NOPUSH-NEXT: orl  $-1, 56(%esp)
478; NOPUSH-NEXT: andl  $0, 52(%esp)
479; NOPUSH-NEXT: andl  $0, 48(%esp)
480; NOPUSH-NEXT: calll  _eightparams64
481; NOPUSH-NEXT: addl  $64, %esp
482define void @pr34863_64(i64 %x) minsize nounwind {
483entry:
484  tail call void @eightparams64(i64 %x, i64 %x, i64 %x, i64 %x, i64 %x, i64 %x, i64 0, i64 -1)
485  ret void
486}
487