xref: /llvm-project/clang/test/CodeGen/swift-async-call-conv.c (revision de4ce5dd2bde7f9d7cbfe47a542a308779c43ce3)
1 // RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-apple-darwin10 -target-cpu core2 -emit-llvm -o - %s | FileCheck %s
2 // RUN: %clang_cc1 -no-enable-noundef-analysis -triple arm64-apple-ios9 -target-cpu cyclone -emit-llvm -o - %s | FileCheck %s
3 // RUN: %clang_cc1 -no-enable-noundef-analysis -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s
4 // RUN: %clang_cc1 -no-enable-noundef-analysis -triple armv7s-apple-ios9 -emit-llvm -o - %s | FileCheck %s
5 // RUN: %clang_cc1 -no-enable-noundef-analysis -triple armv7k-apple-ios9 -emit-llvm -o - %s | FileCheck %s
6 
7 // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple x86_64-apple-darwin10 -target-cpu core2 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY
8 // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple arm64-apple-ios9 -target-cpu cyclone -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY
9 // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY
10 // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple armv7s-apple-ios9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY
11 // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple armv7k-apple-ios9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY
12 
13 // Test tail call behavior when a swiftasynccall function is called
14 // from another swiftasynccall function.
15 
16 #define SWIFTCALL __attribute__((swiftcall))
17 #define SWIFTASYNCCALL __attribute__((swiftasynccall))
18 #define ASYNC_CONTEXT __attribute__((swift_async_context))
19 
20 // CHECK-LABEL: swifttailcc void {{.*}}async_leaf1{{.*}}(ptr swiftasync
async_leaf1(char * ASYNC_CONTEXT ctx)21 SWIFTASYNCCALL void async_leaf1(char * ASYNC_CONTEXT ctx) {
22   *ctx += 1;
23 }
24 
25 // CHECK-LABEL: swifttailcc void {{.*}}async_leaf2{{.*}}(ptr swiftasync
async_leaf2(char * ASYNC_CONTEXT ctx)26 SWIFTASYNCCALL void async_leaf2(char * ASYNC_CONTEXT ctx) {
27   *ctx += 2;
28 }
29 
30 #if __cplusplus
31   #define MYBOOL bool
32 #else
33   #define MYBOOL _Bool
34 #endif
35 
36 // CHECK-LABEL: swifttailcc void {{.*}}async_branch{{.*}}ptr swiftasync
37 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf1
38 // CHECK-NEXT: ret void
39 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf2
40 // CHECK-NEXT: ret void
async_branch(MYBOOL b,char * ASYNC_CONTEXT ctx)41 SWIFTASYNCCALL void async_branch(MYBOOL b, char * ASYNC_CONTEXT ctx) {
42   if (b) {
43     return async_leaf1(ctx);
44   } else {
45     return async_leaf2(ctx);
46   }
47 }
48 
49 // CHECK-LABEL: swifttailcc void {{.*}}async_not_all_tail
50 // CHECK-NOT:  musttail call swifttailcc void @{{.*}}async_leaf1
51 // CHECK:      call swifttailcc void @{{.*}}async_leaf1
52 // CHECK-NOT:  ret void
53 // CHECK:      musttail call swifttailcc void @{{.*}}async_leaf2
54 // CHECK-NEXT: ret void
async_not_all_tail(char * ASYNC_CONTEXT ctx)55 SWIFTASYNCCALL void async_not_all_tail(char * ASYNC_CONTEXT ctx) {
56   async_leaf1(ctx);
57   return async_leaf2(ctx);
58 }
59 
60 // CHECK-LABEL: swifttailcc void {{.*}}async_loop
61 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf1
62 // CHECK-NEXT: ret void
63 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf2
64 // CHECK-NEXT: ret void
65 // CHECK: musttail call swifttailcc void @{{.*}}async_loop
66 // CHECK-NEXT: ret void
async_loop(unsigned u,char * ASYNC_CONTEXT ctx)67 SWIFTASYNCCALL void async_loop(unsigned u, char * ASYNC_CONTEXT ctx) {
68   if (u == 0) {
69     return async_leaf1(ctx);
70   } else if (u == 1) {
71     return async_leaf2(ctx);
72   }
73   return async_loop(u - 2, ctx);
74 }
75 
76 // Forward-declaration + mutual recursion is okay.
77 
78 SWIFTASYNCCALL void async_mutual_loop2(unsigned u, char * ASYNC_CONTEXT ctx);
79 
80 // CHECK-LABEL: swifttailcc void {{.*}}async_mutual_loop1
81 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf1
82 // CHECK-NEXT: ret void
83 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf2
84 // CHECK-NEXT: ret void
85 // There is some bugginess around FileCheck's greediness/matching,
86 // so skipping the check for async_mutual_loop2 here.
async_mutual_loop1(unsigned u,char * ASYNC_CONTEXT ctx)87 SWIFTASYNCCALL void async_mutual_loop1(unsigned u, char * ASYNC_CONTEXT ctx) {
88   if (u == 0) {
89     return async_leaf1(ctx);
90   } else if (u == 1) {
91     return async_leaf2(ctx);
92   }
93   return async_mutual_loop2(u - 2, ctx);
94 }
95 
96 // CHECK-LABEL: swifttailcc void {{.*}}async_mutual_loop2
97 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf1
98 // CHECK-NEXT: ret void
99 // CHECK: musttail call swifttailcc void @{{.*}}async_leaf2
100 // CHECK-NEXT: ret void
101 // CHECK: musttail call swifttailcc void @{{.*}}async_mutual_loop1
102 // CHECK-NEXT: ret void
async_mutual_loop2(unsigned u,char * ASYNC_CONTEXT ctx)103 SWIFTASYNCCALL void async_mutual_loop2(unsigned u, char * ASYNC_CONTEXT ctx) {
104   if (u == 0) {
105     return async_leaf1(ctx);
106   } else if (u == 1) {
107     return async_leaf2(ctx);
108   }
109   return async_mutual_loop1(u - 2, ctx);
110 }
111 
112 // When swiftasynccall functions are called by non-swiftasynccall functions,
113 // the call isn't marked as a tail call.
114 
115 // CHECK-LABEL: swiftcc i8 {{.*}}sync_calling_async
116 // CHECK-NOT: tail call
117 // CHECK: call swifttailcc void @{{.*}}async_branch
118 // CHECK-NOT: tail call
119 // CHECK: call swifttailcc void @{{.*}}async_loop
sync_calling_async(MYBOOL b,unsigned u)120 SWIFTCALL char sync_calling_async(MYBOOL b, unsigned u) {
121   char x = 'a';
122   async_branch(b, &x);
123   async_loop(u, &x);
124   return x;
125 }
126 
127 // CHECK-LABEL: i8 {{.*}}c_calling_async
128 // CHECK-NOT: tail call
129 // CHECK: call swifttailcc void @{{.*}}async_branch
130 // CHECK-NOT: tail call
131 // CHECK: call swifttailcc void @{{.*}}async_loop
c_calling_async(MYBOOL b,unsigned u)132 char c_calling_async(MYBOOL b, unsigned u) {
133   char x = 'a';
134   async_branch(b, &x);
135   async_loop(u, &x);
136   return x;
137 }
138 
139 #if __cplusplus
140 struct S {
141   SWIFTASYNCCALL void (*fptr)(char * ASYNC_CONTEXT);
142 
async_leaf_methodS143   SWIFTASYNCCALL void async_leaf_method(char * ASYNC_CONTEXT ctx) {
144     *ctx += 1;
145   }
async_nonleaf_method1S146   SWIFTASYNCCALL void async_nonleaf_method1(char * ASYNC_CONTEXT ctx) {
147     return async_leaf_method(ctx);
148   }
async_nonleaf_method2S149   SWIFTASYNCCALL void async_nonleaf_method2(char * ASYNC_CONTEXT ctx) {
150     return this->async_leaf_method(ctx);
151   }
152 };
153 
154 SWIFTASYNCCALL void (S::*async_leaf_method_ptr)(char * ASYNC_CONTEXT) = &S::async_leaf_method;
155 
156 // CPPONLY-LABEL: swifttailcc void {{.*}}async_struct_field_and_methods
157 // CPPONLY: musttail call swifttailcc void %{{[0-9]+}}
158 // CPPONLY: musttail call swifttailcc void @{{.*}}async_nonleaf_method1
159 // CPPONLY: musttail call swifttailcc void %{{[0-9]+}}
160 // CPPONLY: musttail call swifttailcc void @{{.*}}async_nonleaf_method2
161 // CPPONLY-NOT: musttail call swifttailcc void @{{.*}}async_leaf_method
162 // ^ TODO: Member pointers should also work.
async_struct_field_and_methods(int i,S & sref,S * sptr)163 SWIFTASYNCCALL void async_struct_field_and_methods(int i, S &sref, S *sptr) {
164   char x = 'a';
165   if (i == 0) {
166     return (*sref.fptr)(&x);
167   } else if (i == 1) {
168     return sref.async_nonleaf_method1(&x);
169   } else if (i == 2) {
170     return (*(sptr->fptr))(&x);
171   } else if (i == 3) {
172     return sptr->async_nonleaf_method2(&x);
173   } else if (i == 4) {
174     return (sref.*async_leaf_method_ptr)(&x);
175   }
176   return (sptr->*async_leaf_method_ptr)(&x);
177 }
178 
179 // CPPONLY-LABEL: define{{.*}} swifttailcc void @{{.*}}async_nonleaf_method1
180 // CPPONLY: musttail call swifttailcc void @{{.*}}async_leaf_method
181 
182 // CPPONLY-LABEL: define{{.*}} swifttailcc void @{{.*}}async_nonleaf_method2
183 // CPPONLY: musttail call swifttailcc void @{{.*}}async_leaf_method
184 #endif
185 
186 // Passing this as an argument requires a coerce-and-expand operation,
187 // which requires a temporary.  Make sure that cleaning up that temporary
188 // doesn't mess around with the musttail handling.
189 struct coerce_and_expand {
190   char a,b,c,d;
191 };
192 struct coerce_and_expand return_coerced(void);
193 SWIFTASYNCCALL void take_coerced_async(struct coerce_and_expand);
194 
195 // CHECK-LABEL: swifttailcc void @{{.*}}test_coerced
test_coerced()196 SWIFTASYNCCALL void test_coerced() {
197   // CHECK:      musttail call swifttailcc void @{{.*}}take_coerced_async
198   // CHECK-NEXT: ret void
199   return take_coerced_async(return_coerced());
200 }
201