aboutsummaryrefslogtreecommitdiff
path: root/test/CodeGen/X86/tailcall-msvc-conventions.ll
blob: 98b02c9c07e82218bfb62f783f25f01a36ac7efd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
; RUN: llc -mtriple=i686-unknown-linux-gnu -O1 < %s | FileCheck %s
; RUN: llc -mtriple=i686-unknown-linux-gnu -O0 < %s | FileCheck %s

; The MSVC family of x86 calling conventions makes tail calls really tricky.
; Tests of all the various combinations should live here.

declare i32 @cdecl_i32()
declare void @cdecl_void()

; Don't allow tail calling these cdecl functions, because we need to clear the
; incoming stack arguments for these argument-clearing conventions.

define x86_thiscallcc void @thiscall_cdecl_notail(i32 %a, i32 %b, i32 %c) {
  tail call void @cdecl_void()
  ret void
}
; CHECK-LABEL: thiscall_cdecl_notail
; CHECK: calll cdecl_void
; CHECK: retl $8

define x86_stdcallcc void @stdcall_cdecl_notail(i32 %a, i32 %b, i32 %c) {
  tail call void @cdecl_void()
  ret void
}
; CHECK-LABEL: stdcall_cdecl_notail
; CHECK: calll cdecl_void
; CHECK: retl $12

define x86_vectorcallcc void @vectorcall_cdecl_notail(i32 inreg %a, i32 inreg %b, i32 %c) {
  tail call void @cdecl_void()
  ret void
}
; CHECK-LABEL: vectorcall_cdecl_notail
; CHECK: calll cdecl_void
; CHECK: retl $4

define x86_fastcallcc void @fastcall_cdecl_notail(i32 inreg %a, i32 inreg %b, i32 %c) {
  tail call void @cdecl_void()
  ret void
}
; CHECK-LABEL: fastcall_cdecl_notail
; CHECK: calll cdecl_void
; CHECK: retl $4


; Tail call to/from callee pop functions can work under the right circumstances:

declare x86_thiscallcc void @no_args_method(i8*)
declare x86_thiscallcc void @one_arg_method(i8*, i32)
declare x86_thiscallcc void @two_args_method(i8*, i32, i32)
declare void @ccall_func()
declare void @ccall_func1(i32)

define x86_thiscallcc void @thiscall_thiscall_tail(i8* %this) {
entry:
  tail call x86_thiscallcc void @no_args_method(i8* %this)
  ret void
}
; CHECK-LABEL: thiscall_thiscall_tail:
; CHECK: jmp no_args_method

define x86_thiscallcc void @thiscall_thiscall_tail2(i8* %this, i32 %a, i32 %b) {
entry:
  tail call x86_thiscallcc void @two_args_method(i8* %this, i32 %a, i32 %b)
  ret void
}
; @two_args_method will take care of popping %a and %b from the stack for us.
; CHECK-LABEL: thiscall_thiscall_tail2:
; CHECK: jmp two_args_method

define x86_thiscallcc void @thiscall_thiscall_notail(i8* %this, i32 %a, i32 %b, i32 %x) {
entry:
  tail call x86_thiscallcc void @two_args_method(i8* %this, i32 %a, i32 %b)
  ret void
}
; @two_args_method would not pop %x.
; CHECK-LABEL: thiscall_thiscall_notail:
; CHECK: calll two_args_method
; CHECK: retl $12

define x86_thiscallcc void @thiscall_thiscall_notail2(i8* %this, i32 %a) {
entry:
  tail call x86_thiscallcc void @no_args_method(i8* %this)
  ret void
}
; @no_args_method would not pop %x for us. Make sure this is checked even
; when there are no arguments to the call.
; CHECK-LABEL: thiscall_thiscall_notail2:
; CHECK: calll no_args_method
; CHECK: retl $4

define void @ccall_thiscall_tail(i8* %x) {
entry:
  tail call x86_thiscallcc void @no_args_method(i8* %x)
  ret void
}
; Tail calling from ccall to thiscall works.
; CHECK-LABEL: ccall_thiscall_tail:
; CHECK: jmp no_args_method

define void @ccall_thiscall_notail(i8* %x, i32 %y) {
entry:
  tail call x86_thiscallcc void @one_arg_method(i8* %x, i32 %y);
  ret void
}
; @one_arg_method would pop %y off the stack.
; CHECK-LABEL: ccall_thiscall_notail:
; CHECK: calll one_arg_method

define x86_thiscallcc void @thiscall_ccall_tail(i8* %this) {
entry:
  tail call void @ccall_func()
  ret void
}
; Tail call from thiscall to ccall works if no arguments need popping.
; CHECK-LABEL: thiscall_ccall_tail:
; CHECK: jmp ccall_func

define x86_thiscallcc void @thiscall_ccall_notail(i8* %this, i32 %x) {
entry:
  tail call void @ccall_func1(i32 %x)
  ret void
}
; No tail call: %x needs to be popped.
; CHECK-LABEL: thiscall_ccall_notail:
; CHECK: calll ccall_func1
; CHECK: retl $4

%S = type { i32 (...)** }
define x86_thiscallcc void @tailcall_through_pointer(%S* %this, i32 %a) {
entry:
  %0 = bitcast %S* %this to void (%S*, i32)***
  %vtable = load void (%S*, i32)**, void (%S*, i32)*** %0
  %1 = load void (%S*, i32)*, void (%S*, i32)** %vtable
  tail call x86_thiscallcc void %1(%S* %this, i32 %a)
  ret void
}
; Tail calling works through function pointers too.
; CHECK-LABEL: tailcall_through_pointer:
; CHECK: jmpl

define x86_stdcallcc void @stdcall_cdecl_tail() {
  tail call void @ccall_func()
  ret void
}
; stdcall to cdecl works if no arguments need popping.
; CHECK-LABEL: stdcall_cdecl_tail
; CHECK: jmp ccall_func

define x86_vectorcallcc void @vectorcall_cdecl_tail(i32 inreg %a, i32 inreg %b) {
  tail call void @ccall_func()
  ret void
}
; vectorcall to cdecl works if no arguments need popping.
; CHECK-LABEL: vectorcall_cdecl_tail
; CHECK: jmp ccall_func

define x86_fastcallcc void @fastcall_cdecl_tail(i32 inreg %a, i32 inreg %b) {
  tail call void @ccall_func()
  ret void
}
; fastcall to cdecl works if no arguments need popping.
; CHECK-LABEL: fastcall_cdecl_tail
; CHECK: jmp ccall_func

define x86_stdcallcc void @stdcall_thiscall_notail(i8* %this, i32 %a, i32 %b) {
  tail call x86_thiscallcc void @two_args_method(i8* %this, i32 %a, i32 %b)
  ret void
}
; two_args_method will not pop %this.
; CHECK-LABEL: stdcall_thiscall_notail
; CHECK: calll two_args_method

define x86_stdcallcc void @stdcall_thiscall_tail(i32 %a, i32 %b) {
  tail call x86_thiscallcc void @two_args_method(i8* null, i32 %a, i32 %b)
  ret void
}
; The callee pop amounts match up.
; CHECK-LABEL: stdcall_thiscall_tail
; CHECK: jmp two_args_method

declare x86_fastcallcc void @fastcall2(i32 inreg %a, i32 inreg %b)
define void @cdecl_fastcall_tail(i32 %a, i32 %b) {
  tail call x86_fastcallcc void @fastcall2(i32 %a, i32 %b)
  ret void
}
; fastcall2 won't pop anything.
; CHECK-LABEL: cdecl_fastcall_tail
; CHECK: jmp fastcall2