xref: /llvm-project/llvm/test/CodeGen/WebAssembly/varargs.ll (revision 73856247eef35f5336e485dc009842a5b991c421)
1; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs | FileCheck %s --check-prefix=CHECK --check-prefix=EMSCRIPTEN
2; RUN: llc < %s -mtriple wasm32-unknown-unknown -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs | FileCheck %s  --check-prefix=CHECK --check-prefix=UNKNOWN
3
4; Test varargs constructs.
5
6target triple = "wasm32-unknown-emscripten"
7
8; Test va_start.
9
10; TODO: Test va_start.
11; CHECK-LABEL: start:
12; CHECK-NEXT: .functype start (i32, i32) -> ()
13; CHECK-NOT: __stack_pointer
14define void @start(ptr %ap, ...) {
15entry:
16; Store the second argument (the hidden vararg buffer pointer) into ap
17; CHECK: i32.store 0($0), $1
18  call void @llvm.va_start(ptr %ap)
19  ret void
20}
21
22; Test va_end.
23
24; CHECK-LABEL: end:
25; CHECK-NEXT: .functype end (i32) -> (){{$}}
26; CHECK-NEXT: return{{$}}
27define void @end(ptr %ap) {
28entry:
29  call void @llvm.va_end(ptr %ap)
30  ret void
31}
32
33; Test va_copy.
34
35; CHECK-LABEL: copy:
36; CHECK-NEXT: .functype copy (i32, i32) -> (){{$}}
37; CHECK-NEXT: i32.load  $push0=, 0($1){{$}}
38; CHECK-NEXT: i32.store 0($0), $pop0{{$}}
39; CHECK-NEXT: return{{$}}
40define void @copy(ptr %ap, ptr %bp) {
41entry:
42  call void @llvm.va_copy(ptr %ap, ptr %bp)
43  ret void
44}
45
46; Test va_arg with an i8 argument.
47
48; CHECK-LABEL: arg_i8:
49; CHECK-NEXT: .functype arg_i8 (i32) -> (i32){{$}}
50; CHECK-NEXT: i32.load   $push[[NUM0:[0-9]+]]=, 0($0){{$}}
51; CHECK-NEXT: local.tee  $push[[NUM1:[0-9]+]]=, $1=, $pop[[NUM0]]{{$}}
52; CHECK-NEXT: i32.const  $push[[NUM2:[0-9]+]]=, 4{{$}}
53; CHECK-NEXT: i32.add    $push[[NUM3:[0-9]+]]=, $pop[[NUM1]], $pop[[NUM2]]{{$}}
54; CHECK-NEXT: i32.store  0($0), $pop[[NUM3]]{{$}}
55; CHECK-NEXT: i32.load   $push[[NUM4:[0-9]+]]=, 0($1){{$}}
56; CHECK-NEXT: return     $pop[[NUM4]]{{$}}
57define i8 @arg_i8(ptr %ap) {
58entry:
59  %t = va_arg ptr %ap, i8
60  ret i8 %t
61}
62
63; Test va_arg with an i32 argument.
64
65; CHECK-LABEL: arg_i32:
66; CHECK-NEXT: .functype arg_i32 (i32) -> (i32){{$}}
67; CHECK-NEXT: i32.load   $push[[NUM0:[0-9]+]]=, 0($0){{$}}
68; CHECK-NEXT: i32.const  $push[[NUM1:[0-9]+]]=, 3{{$}}
69; CHECK-NEXT: i32.add    $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}}
70; CHECK-NEXT: i32.const  $push[[NUM3:[0-9]+]]=, -4{{$}}
71; CHECK-NEXT: i32.and    $push[[NUM4:[0-9]+]]=, $pop[[NUM2]], $pop[[NUM3]]{{$}}
72; CHECK-NEXT: local.tee  $push[[NUM5:[0-9]+]]=, $1=, $pop[[NUM4]]{{$}}
73; CHECK-NEXT: i32.const  $push[[NUM6:[0-9]+]]=, 4{{$}}
74; CHECK-NEXT: i32.add    $push[[NUM7:[0-9]+]]=, $pop[[NUM5]], $pop[[NUM6]]{{$}}
75; CHECK-NEXT: i32.store  0($0), $pop[[NUM7]]{{$}}
76; CHECK-NEXT: i32.load   $push[[NUM8:[0-9]+]]=, 0($1){{$}}
77; CHECK-NEXT: return     $pop[[NUM8]]{{$}}
78define i32 @arg_i32(ptr %ap) {
79entry:
80  %t = va_arg ptr %ap, i32
81  ret i32 %t
82}
83
84; Test va_arg with an i128 argument.
85
86; CHECK-LABEL: arg_i128:
87; CHECK-NEXT: .functype arg_i128 (i32, i32) -> (){{$}}
88; CHECK: i32.and
89; CHECK: i64.load
90; CHECK: i64.load
91; CHECK: return{{$}}
92define i128 @arg_i128(ptr %ap) {
93entry:
94  %t = va_arg ptr %ap, i128
95  ret i128 %t
96}
97
98; Test a varargs call with no actual arguments.
99
100declare void @callee(...)
101
102; CHECK-LABEL: caller_none:
103; CHECK:      i32.const $push0=, 0
104; CHECK-NEXT: call callee, $pop0
105; CHECK-NEXT: return{{$}}
106define void @caller_none() {
107  call void (...) @callee()
108  ret void
109}
110
111; Test a varargs call with some actual arguments.
112; Note that the store of 2.0 is converted to an i64 store; this optimization
113; is not needed on WebAssembly, but there isn't currently a convenient hook for
114; disabling it.
115
116; CHECK-LABEL: caller_some
117; CHECK-DAG: i32.store
118; CHECK-DAG: i64.store
119define void @caller_some() {
120  call void (...) @callee(i32 0, double 2.0)
121  ret void
122}
123
124; Test a va_start call in a non-entry block
125; CHECK-LABEL: startbb:
126; CHECK: .functype startbb (i32, i32, i32) -> ()
127define void @startbb(i1 %cond, ptr %ap, ...) {
128entry:
129  br i1 %cond, label %bb0, label %bb1
130bb0:
131  ret void
132bb1:
133; Store the second argument (the hidden vararg buffer pointer) into ap
134; CHECK: i32.store 0($1), $2
135  call void @llvm.va_start(ptr %ap)
136  ret void
137}
138
139; Test a call to a varargs function with a non-legal fixed argument.
140
141declare void @callee_with_nonlegal_fixed(fp128, ...) nounwind
142
143; CHECK-LABEL: call_nonlegal_fixed:
144; CHECK: i64.const       $push[[L0:[0-9]+]]=, 0
145; CHECK: i64.const       $push[[L1:[0-9]+]]=, 0
146; CHECK: i32.const       $push[[L2:[0-9]+]]=, 0
147; CHECK: call            callee_with_nonlegal_fixed, $pop[[L0]], $pop[[L1]], $pop[[L2]]{{$}}
148define void @call_nonlegal_fixed() nounwind {
149  call void (fp128, ...) @callee_with_nonlegal_fixed(fp128 0xL00000000000000000000000000000000)
150  ret void
151}
152
153; Test a definition a varargs function with a non-legal fixed argument.
154
155; CHECK-LABEL: nonlegal_fixed:
156; CHECK-NEXT: .functype nonlegal_fixed (i64, i64, i32) -> (){{$}}
157define void @nonlegal_fixed(fp128 %x, ...) nounwind {
158  ret void
159}
160
161; Test that an fp128 argument is properly aligned and allocated
162; within a vararg buffer.
163
164; EMSCRIPTEN-LABEL: call_fp128_alignment:
165; EMSCRIPTEN:      global.get      $push5=, __stack_pointer
166; EMSCRIPTEN-NEXT: i32.const       $push6=, 32
167; EMSCRIPTEN-NEXT: i32.sub         $push10=, $pop5, $pop6
168; EMSCRIPTEN-NEXT: local.tee       $push9=, $1=, $pop10
169; EMSCRIPTEN-NEXT: global.set      __stack_pointer, $pop9
170; EMSCRIPTEN-NEXT: i32.const       $push0=, 16
171; EMSCRIPTEN-NEXT: i32.add         $push1=, $1, $pop0
172; EMSCRIPTEN-NEXT: i64.const       $push2=, -9223372036854775808
173; EMSCRIPTEN-NEXT: i64.store       0($pop1), $pop2
174; EMSCRIPTEN-NEXT: i64.const       $push3=, 1
175; EMSCRIPTEN-NEXT: i64.store       8($1), $pop3
176; EMSCRIPTEN-NEXT: i32.const       $push4=, 7
177; EMSCRIPTEN-NEXT: i32.store       0($1), $pop4
178; EMSCRIPTEN-NEXT: call            callee, $1
179
180; Alignment of fp128 is a current disagreement between emscripten and others.
181; UNKNOWN-LABEL: call_fp128_alignment:
182; UNKNOWN:      global.get      $push7=, __stack_pointer
183; UNKNOWN-NEXT: i32.const       $push8=, 32
184; UNKNOWN-NEXT: i32.sub         $push12=, $pop7, $pop8
185; UNKNOWN-NEXT: local.tee       $push11=, $1=, $pop12
186; UNKNOWN-NEXT: global.set      __stack_pointer, $pop11
187; UNKNOWN-NEXT: i32.const       $push0=, 24
188; UNKNOWN-NEXT: i32.add         $push1=, $1, $pop0
189; UNKNOWN-NEXT: i64.const       $push2=, -9223372036854775808
190; UNKNOWN-NEXT: i64.store       0($pop1), $pop2
191; UNKNOWN-NEXT: i32.const       $push3=, 16
192; UNKNOWN-NEXT: i32.add         $push4=, $1, $pop3
193; UNKNOWN-NEXT: i64.const       $push5=, 1
194; UNKNOWN-NEXT: i64.store       0($pop4), $pop5
195; UNKNOWN-NEXT: i32.const       $push6=, 7
196; UNKNOWN-NEXT: i32.store       0($1), $pop6
197; UNKNOWN-NEXT: call            callee, $1
198define void @call_fp128_alignment(ptr %p) {
199entry:
200  call void (...) @callee(i8 7, fp128 0xL00000000000000018000000000000000)
201  ret void
202}
203
204declare void @llvm.va_start(ptr)
205declare void @llvm.va_end(ptr)
206declare void @llvm.va_copy(ptr, ptr)
207