xref: /llvm-project/llvm/test/CodeGen/WebAssembly/function-bitcasts.ll (revision 68a534e9bf69e7e5f081a515e05f1d3cb4c21761)
1; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -enable-emscripten-cxx-exceptions | FileCheck %s
2
3; Test that function pointer casts are replaced with wrappers.
4
5target triple = "wasm32-unknown-unknown"
6
7define void @has_i32_arg(i32) {
8entry:
9  ret void
10}
11
12declare void @has_struct_arg({i32})
13declare i32 @has_i32_ret()
14declare void @vararg(...)
15declare void @plain(i32)
16
17declare void @foo0()
18declare void @foo1()
19declare void @foo2()
20declare void @foo3()
21
22; CHECK-LABEL: test:
23; CHECK:      call       .Lhas_i32_arg_bitcast{{$}}
24; CHECK-NEXT: call       .Lhas_i32_arg_bitcast{{$}}
25; CHECK-NEXT: call        .Lhas_i32_ret_bitcast{{$}}
26; CHECK-NEXT: call        $drop=, has_i32_ret
27; CHECK-NEXT: i32.const   $push[[L0:[0-9]+]]=, 0
28; CHECK-NEXT: call        .Lfoo0_bitcast, $pop[[L0]]{{$}}
29; CHECK-NEXT: i32.const   $push[[L1:[0-9]+]]=, 0
30; CHECK-NEXT: call        .Lfoo0_bitcast, $pop[[L1]]{{$}}
31; CHECK-NEXT: i32.const   $push[[L2:[0-9]+]]=, 0
32; CHECK-NEXT: call        .Lfoo0_bitcast, $pop[[L2]]{{$}}
33; CHECK-NEXT: call        foo0
34; CHECK-NEXT: call        $drop=, .Lfoo1_bitcast{{$}}
35; CHECK-NEXT: call        foo2{{$}}
36; CHECK-NEXT: call        foo1{{$}}
37; CHECK-NEXT: call        foo3{{$}}
38; CHECK-NEXT: end_function
39define void @test() {
40entry:
41  call void @has_i32_arg()
42  call void @has_i32_arg()
43  call void @has_i32_ret()
44  call i32 @has_i32_ret()
45  call void @foo0(i32 0)
46  call void @foo0(i32 0)
47  call void @foo0(i32 0)
48  call void @foo0()
49  %t = call i32 @foo1()
50  call void @foo2()
51  call void @foo1()
52  call void @foo3()
53
54  ret void
55}
56
57; Calling aliases should also generate a wrapper
58
59@alias_i32_arg = weak hidden alias void (i32), ptr @has_i32_arg
60
61; CHECK-LABEL: test_alias:
62; CHECK: call   .Lhas_i32_arg_bitcast
63define void @test_alias() {
64entry:
65  call void @alias_i32_arg()
66  ret void
67}
68
69
70; CHECK-LABEL: test_structs:
71; CHECK: call    .Lhas_i32_arg_bitcast.2, $pop{{[0-9]+}}, $pop{{[0-9]+$}}
72; CHECK: call    .Lhas_i32_arg_bitcast.1, $0, $pop2
73; CHECK: call     .Lhas_struct_arg_bitcast{{$}}
74define void @test_structs() {
75entry:
76  call void @has_i32_arg(i32 5, {i32} {i32 6})
77  call {i32, i64} @has_i32_arg(i32 7)
78  call void @has_struct_arg()
79  ret void
80}
81
82; CHECK-LABEL: test_structs_unhandled:
83; CHECK: call    has_struct_arg, $pop{{[0-9]+$}}
84; CHECK: call    has_struct_arg, $pop{{[0-9]+$}}
85; CHECK: call    has_i32_ret, $pop{{[0-9]+$}}
86define void @test_structs_unhandled() {
87entry:
88  call void @has_struct_arg({i32} {i32 3})
89  call void @has_struct_arg({i64} {i64 4})
90  call {i32, i32} @has_i32_ret()
91  ret void
92}
93
94; CHECK-LABEL: test_varargs:
95; CHECK:      global.set
96; CHECK:      i32.const   $push[[L3:[0-9]+]]=, 0{{$}}
97; CHECK-NEXT: call        .Lvararg_bitcast, $pop[[L3]]{{$}}
98; CHECK-NEXT: i32.const   $push[[L4:[0-9]+]]=, 0{{$}}
99; CHECK-NEXT: i32.store   0($[[L5:[0-9]+]]), $pop[[L4]]{{$}}
100; CHECK-NEXT: call        .Lplain_bitcast, $[[L5]]{{$}}
101define void @test_varargs() {
102  call void @vararg(i32 0)
103  call void (...) @plain(i32 0)
104  ret void
105}
106
107; Don't use wrappers when the value is stored in memory
108
109@global_func = hidden local_unnamed_addr global ptr null
110
111; CHECK-LABEL: test_store:
112; CHECK:      i32.const   $push[[L0:[0-9]+]]=, 0{{$}}
113; CHECK-NEXT: i32.const   $push[[L1:[0-9]+]]=, has_i32_ret{{$}}
114; CHECK-NEXT: i32.store   global_func($pop[[L0]]), $pop[[L1]]{{$}}
115define void @test_store() {
116  store ptr @has_i32_ret, ptr @global_func
117  ret void
118}
119
120; CHECK-LABEL: test_load:
121; CHECK-NEXT: .functype test_load () -> (i32){{$}}
122; CHECK-NEXT: i32.const   $push[[L0:[0-9]+]]=, 0{{$}}
123; CHECK-NEXT: i32.load    $push[[L1:[0-9]+]]=, global_func($pop[[L0]]){{$}}
124; CHECK-NEXT: call_indirect $push{{[0-9]+}}=, $pop[[L1]]{{$}}
125define i32 @test_load() {
126  %1 = load ptr, ptr @global_func
127  %2 = call i32 %1()
128  ret i32 %2
129}
130
131; Don't use wrappers when the value is passed to a function call
132
133declare void @call_func(ptr)
134
135; CHECK-LABEL: test_argument:
136; CHECK:      i32.const   $push[[L0:[0-9]+]]=, has_i32_ret{{$}}
137; CHECK-NEXT: call        call_func, $pop[[L0]]{{$}}
138; CHECK-NEXT: i32.const   $push[[L1:[0-9]+]]=, has_i32_arg{{$}}
139; CHECK-NEXT: call        call_func, $pop[[L1]]{{$}}
140define void @test_argument() {
141  call void @call_func(ptr @has_i32_ret)
142  call void @call_func(ptr @has_i32_arg)
143  ret void
144}
145
146; Invokes should be treated like calls
147
148; CHECK-LABEL: test_invoke:
149; CHECK:      i32.const   $push[[L1:[0-9]+]]=, call_func{{$}}
150; CHECK-NEXT: i32.const   $push[[L0:[0-9]+]]=, has_i32_ret{{$}}
151; CHECK-NEXT: call        invoke_vi, $pop[[L1]], $pop[[L0]]{{$}}
152; CHECK:      i32.const   $push[[L3:[0-9]+]]=, call_func{{$}}
153; CHECK-NEXT: i32.const   $push[[L2:[0-9]+]]=, has_i32_arg{{$}}
154; CHECK-NEXT: call        invoke_vi, $pop[[L3]], $pop[[L2]]{{$}}
155; CHECK:     i32.const   $push[[L4:[0-9]+]]=, .Lhas_i32_arg_bitcast{{$}}
156; CHECK-NEXT: call        invoke_v, $pop[[L4]]{{$}}
157declare i32 @personality(...)
158define void @test_invoke() personality ptr @personality {
159entry:
160  invoke void @call_func(ptr @has_i32_ret)
161          to label %cont unwind label %lpad
162
163cont:
164  invoke void @call_func(ptr @has_i32_arg)
165          to label %cont2 unwind label %lpad
166
167cont2:
168  invoke void @has_i32_arg()
169          to label %end unwind label %lpad
170
171lpad:
172  %0 = landingpad { ptr, i32 }
173          catch ptr null
174  br label %end
175
176end:
177  ret void
178}
179
180; CHECK-LABEL: .Lhas_i32_arg_bitcast:
181; CHECK-NEXT: .functype	.Lhas_i32_arg_bitcast () -> ()
182; CHECK-NEXT: call        has_i32_arg, $0{{$}}
183; CHECK-NEXT: end_function
184
185; CHECK-LABEL: .Lhas_i32_arg_bitcast.1:
186; CHECK-NEXT: .functype .Lhas_i32_arg_bitcast.1 (i32, i32) -> ()
187; CHECK-NEXT: call        has_i32_arg, $1{{$}}
188; CHECK-NEXT: end_function
189
190; CHECK-LABEL: .Lhas_i32_arg_bitcast.2:
191; CHECK-NEXT: .functype .Lhas_i32_arg_bitcast.2 (i32, i32) -> ()
192; CHECK-NEXT: call        has_i32_arg, $0{{$}}
193; CHECK-NEXT: end_function
194
195; CHECK-LABEL: .Lhas_i32_ret_bitcast:
196; CHECK:      call        $drop=, has_i32_ret{{$}}
197; CHECK-NEXT: end_function
198
199; CHECK-LABEL: .Lvararg_bitcast:
200; CHECK: call        vararg, $1{{$}}
201; CHECK: end_function
202
203; CHECK-LABEL: .Lplain_bitcast:
204; CHECK: call        plain, $1{{$}}
205; CHECK: end_function
206
207; CHECK-LABEL: .Lfoo0_bitcast:
208; CHECK-NEXT: .functype .Lfoo0_bitcast (i32) -> ()
209; CHECK-NEXT: call        foo0{{$}}
210; CHECK-NEXT: end_function
211
212; CHECK-LABEL: .Lfoo1_bitcast:
213; CHECK-NEXT: .functype .Lfoo1_bitcast () -> (i32)
214; CHECK-NEXT: call        foo1{{$}}
215; CHECK-NEXT: local.copy  $push0=, $0
216; CHECK-NEXT: end_function
217