1; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck -DPTR=32 %s
2; RUN: llc < %s --mtriple=wasm64-unknown-unknown -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck -DPTR=64 %s
3
4declare void @ext_func(i64* %ptr)
5declare void @ext_func_i32(i32* %ptr)
6
7; CHECK-LABEL: alloca32:
8; Check that there is an extra local for the stack pointer.
9; CHECK: .local i[[PTR]]{{$}}
10define void @alloca32() noredzone {
11 ; CHECK-NEXT: global.get $push[[L2:.+]]=, __stack_pointer{{$}}
12 ; CHECK-NEXT: i[[PTR]].const $push[[L3:.+]]=, 16
13 ; CHECK-NEXT: i[[PTR]].sub $push[[L9:.+]]=, $pop[[L2]], $pop[[L3]]
14 ; CHECK-NEXT: local.tee $push[[L8:.+]]=, [[SP:.+]], $pop[[L9]]{{$}}
15 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L8]]{{$}}
16 %retval = alloca i32
17 ; CHECK: local.get $push[[L4:.+]]=, [[SP]]{{$}}
18 ; CHECK: i32.const $push[[L0:.+]]=, 0
19 ; CHECK: i32.store 12($pop[[L4]]), $pop[[L0]]
20 store i32 0, i32* %retval
21 ; CHECK: local.get $push[[L6:.+]]=, [[SP]]{{$}}
22 ; CHECK-NEXT: i[[PTR]].const $push[[L5:.+]]=, 16
23 ; CHECK-NEXT: i[[PTR]].add $push[[L7:.+]]=, $pop[[L6]], $pop[[L5]]
24 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L7]]
25 ret void
26}
27
28; CHECK-LABEL: alloca3264:
29; CHECK: .local i[[PTR]]{{$}}
30define void @alloca3264() {
31 ; CHECK: global.get $push[[L3:.+]]=, __stack_pointer{{$}}
32 ; CHECK-NEXT: i[[PTR]].const $push[[L4:.+]]=, 16
33 ; CHECK-NEXT: i[[PTR]].sub $push[[L6:.+]]=, $pop[[L3]], $pop[[L4]]
34 ; CHECK-NEXT: local.tee $push[[L5:.+]]=, [[SP:.+]], $pop[[L6]]
35 %r1 = alloca i32
36 %r2 = alloca double
37 store i32 0, i32* %r1
38 store double 0.0, double* %r2
39 ; CHECK-NEXT: i64.const $push[[L1:.+]]=, 0
40 ; CHECK-NEXT: i64.store 0($pop[[L5]]), $pop[[L1]]
41 ; CHECK-NEXT: local.get $push[[L2:.+]]=, [[SP]]{{$}}
42 ; CHECK-NEXT: i32.const $push[[L0:.+]]=, 0
43 ; CHECK-NEXT: i32.store 12($pop[[L2]]), $pop[[L0]]
44 ; CHECK-NEXT: return
45 ret void
46}
47
48; CHECK-LABEL: allocarray:
49; CHECK: .local i[[PTR]]{{$}}
50define void @allocarray() {
51 ; CHECK-NEXT: global.get $push[[L4:.+]]=, __stack_pointer{{$}}
52 ; CHECK-NEXT: i[[PTR]].const $push[[L5:.+]]=, 144{{$}}
53 ; CHECK-NEXT: i[[PTR]].sub $push[[L12:.+]]=, $pop[[L4]], $pop[[L5]]
54 ; CHECK-NEXT: local.tee $push[[L11:.+]]=, 0, $pop[[L12]]
55 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L11]]
56 %r = alloca [33 x i32]
57
58 ; CHECK:      i[[PTR]].const $push{{.+}}=, 24
59 ; CHECK-NEXT: i[[PTR]].add $push[[L3:.+]]=, $pop{{.+}}, $pop{{.+}}
60 ; CHECK-NEXT: i32.const $push[[L1:.+]]=, 1{{$}}
61 ; CHECK-NEXT: i32.store 0($pop[[L3]]), $pop[[L1]]{{$}}
62 ; CHECK-NEXT: local.get $push[[L4:.+]]=, 0{{$}}
63 ; CHECK-NEXT: i32.const $push[[L10:.+]]=, 1{{$}}
64 ; CHECK-NEXT: i32.store 12($pop[[L4]]), $pop[[L10]]{{$}}
65 %p = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 0
66 store i32 1, i32* %p
67 %p2 = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 3
68 store i32 1, i32* %p2
69
70 ; CHECK-NEXT: local.get $push[[L2:.+]]=, [[SP]]{{$}}
71 ; CHECK-NEXT: i[[PTR]].const $push[[L7:.+]]=, 144
72 ; CHECK-NEXT: i[[PTR]].add $push[[L8:.+]]=, $pop[[L2]], $pop[[L7]]
73 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L8]]
74 ret void
75}
76
77; CHECK-LABEL: non_mem_use
78define void @non_mem_use(i8** %addr) {
79 ; CHECK: i[[PTR]].const $push[[L2:.+]]=, 48
80 ; CHECK-NEXT: i[[PTR]].sub $push[[L12:.+]]=, {{.+}}, $pop[[L2]]
81 ; CHECK-NEXT: local.tee $push[[L11:.+]]=, [[SP:.+]], $pop[[L12]]
82 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L11]]
83 %buf = alloca [27 x i8], align 16
84 %r = alloca i64
85 %r2 = alloca i64
86 ; %r is at SP+8
87 ; CHECK: local.get $push[[L3:.+]]=, [[SP]]
88 ; CHECK: i[[PTR]].const $push[[OFF:.+]]=, 8
89 ; CHECK-NEXT: i[[PTR]].add $push[[ARG1:.+]]=, $pop[[L3]], $pop[[OFF]]
90 ; CHECK-NEXT: call ext_func, $pop[[ARG1]]
91 call void @ext_func(i64* %r)
92 ; %r2 is at SP+0, no add needed
93 ; CHECK: local.get $push[[L4:.+]]=, [[SP]]
94 ; CHECK-NEXT: call ext_func, $pop[[L4]]
95 call void @ext_func(i64* %r2)
96 ; Use as a value, but in a store
97 ; %buf is at SP+16
98 ; CHECK: local.get $push[[L5:.+]]=, [[SP]]
99 ; CHECK: i[[PTR]].const $push[[OFF:.+]]=, 16
100 ; CHECK-NEXT: i[[PTR]].add $push[[VAL:.+]]=, $pop[[L5]], $pop[[OFF]]
101 ; CHECK-NEXT: i[[PTR]].store 0($pop{{.+}}), $pop[[VAL]]
102 %gep = getelementptr inbounds [27 x i8], [27 x i8]* %buf, i32 0, i32 0
103 store i8* %gep, i8** %addr
104 ret void
105}
106
107; CHECK-LABEL: allocarray_inbounds:
108; CHECK: .local i[[PTR]]{{$}}
109define void @allocarray_inbounds() {
110 ; CHECK: global.get $push[[L3:.+]]=, __stack_pointer{{$}}
111 ; CHECK-NEXT: i[[PTR]].const $push[[L4:.+]]=, 32{{$}}
112 ; CHECK-NEXT: i[[PTR]].sub $push[[L11:.+]]=, $pop[[L3]], $pop[[L4]]
113 ; CHECK-NEXT: local.tee $push[[L10:.+]]=, [[SP:.+]], $pop[[L11]]
114 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L10]]{{$}}
115 %r = alloca [5 x i32]
116 ; CHECK: i32.const $push[[L3:.+]]=, 1
117 ; CHECK-DAG: i32.store 24(${{.+}}), $pop[[L3]]
118 %p = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 0
119 store i32 1, i32* %p
120 ; This store should have both the GEP and the FI folded into it.
121 ; CHECK-DAG: i32.store 12(${{.+}}), $pop
122 %p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 3
123 store i32 1, i32* %p2
124 call void @ext_func(i64* null);
125 ; CHECK: call ext_func
126 ; CHECK: i[[PTR]].const $push[[L5:.+]]=, 32{{$}}
127 ; CHECK-NEXT: i[[PTR]].add $push[[L7:.+]]=, ${{.+}}, $pop[[L5]]
128 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L7]]
129 ret void
130}
131
132; CHECK-LABEL: dynamic_alloca:
133define void @dynamic_alloca(i32 %alloc) {
134 ; CHECK: global.get $push[[L13:.+]]=, __stack_pointer{{$}}
135 ; CHECK-NEXT: local.tee $push[[L12:.+]]=, [[SP:.+]], $pop[[L13]]{{$}}
136 ; Target independent codegen bumps the stack pointer.
137 ; CHECK: i[[PTR]].sub
138 ; Check that SP is written back to memory after decrement
139 ; CHECK: global.set __stack_pointer,
140 %r = alloca i32, i32 %alloc
141 ; Target-independent codegen also calculates the store addr
142 ; CHECK: call ext_func_i32
143 call void @ext_func_i32(i32* %r)
144 ; CHECK: global.set __stack_pointer, $pop{{.+}}
145 ret void
146}
147
148; CHECK-LABEL: dynamic_alloca_redzone:
149define void @dynamic_alloca_redzone(i32 %alloc) {
150 ; CHECK: global.get $push[[L13:.+]]=, __stack_pointer{{$}}
151 ; CHECK-NEXT: local.tee $push[[L12:.+]]=, [[SP:.+]], $pop[[L13]]{{$}}
152 ; Target independent codegen bumps the stack pointer
153 ; CHECK: i[[PTR]].sub
154 %r = alloca i32, i32 %alloc
155 ; CHECK-NEXT: local.tee $push[[L8:.+]]=, [[SP2:.+]], $pop
156 ; CHECK: local.get $push[[L7:.+]]=, [[SP2]]{{$}}
157 ; CHECK-NEXT: i32.const $push[[L6:.+]]=, 0{{$}}
158 ; CHECK-NEXT: i32.store 0($pop[[L7]]), $pop[[L6]]{{$}}
159 store i32 0, i32* %r
160 ; CHECK-NEXT: return
161 ret void
162}
163
164; CHECK-LABEL: dynamic_static_alloca:
165define void @dynamic_static_alloca(i32 %alloc) noredzone {
166 ; Decrement SP in the prolog by the static amount and writeback to memory.
167 ; CHECK: global.get $push[[L11:.+]]=, __stack_pointer{{$}}
168 ; CHECK-NEXT: i[[PTR]].const $push[[L12:.+]]=, 16
169 ; CHECK-NEXT: i[[PTR]].sub $push[[L23:.+]]=, $pop[[L11]], $pop[[L12]]
170 ; CHECK-NEXT: local.tee $push[[L22:.+]]=, [[SP:.+]], $pop[[L23]]
171 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L22]]
172
173 ; Alloc and write to a static alloca
174 ; CHECK: local.get $push[[L21:.+]]=, [[SP:.+]]
175 ; CHECK-NEXT: local.tee $push[[pushedFP:.+]]=, [[FP:.+]], $pop[[L21]]
176 ; CHECK-NEXT: i32.const $push[[L0:.+]]=, 101
177 ; CHECK-NEXT: i32.store [[static_offset:.+]]($pop[[pushedFP]]), $pop[[L0]]
178 %static = alloca i32
179 store volatile i32 101, i32* %static
180
181 ; Decrement SP in the body by the dynamic amount.
182 ; CHECK: i[[PTR]].sub
183 ; CHECK: local.tee $push[[L16:.+]]=, [[dynamic_local:.+]], $pop{{.+}}
184 ; CHECK: local.tee $push[[L15:.+]]=, [[other:.+]], $pop[[L16]]{{$}}
185 ; CHECK: global.set __stack_pointer, $pop[[L15]]{{$}}
186 %dynamic = alloca i32, i32 %alloc
187
188 ; Ensure we don't modify the frame pointer after assigning it.
189 ; CHECK-NOT: $[[FP]]=
190
191 ; Ensure the static address doesn't change after modifying the stack pointer.
192 ; CHECK: local.get $push[[L17:.+]]=, [[FP]]
193 ; CHECK: i32.const $push[[L7:.+]]=, 102
194 ; CHECK-NEXT: i32.store [[static_offset]]($pop[[L17]]), $pop[[L7]]
195 ; CHECK-NEXT: local.get $push[[L9:.+]]=, [[dynamic_local]]{{$}}
196 ; CHECK-NEXT: i32.const $push[[L8:.+]]=, 103
197 ; CHECK-NEXT: i32.store 0($pop[[L9]]), $pop[[L8]]
198 store volatile i32 102, i32* %static
199 store volatile i32 103, i32* %dynamic
200
201 ; Decrement SP in the body by the dynamic amount.
202 ; CHECK: i[[PTR]].sub
203 ; CHECK: local.tee $push{{.+}}=, [[dynamic2_local:.+]], $pop{{.+}}
204 %dynamic.2 = alloca i32, i32 %alloc
205
206 ; CHECK-NOT: $[[FP]]=
207
208 ; Ensure neither the static nor dynamic address changes after the second
209 ; modification of the stack pointer.
210 ; CHECK: local.get $push[[L22:.+]]=, [[FP]]
211 ; CHECK: i32.const $push[[L9:.+]]=, 104
212 ; CHECK-NEXT: i32.store [[static_offset]]($pop[[L22]]), $pop[[L9]]
213 ; CHECK-NEXT: local.get $push[[L23:.+]]=, [[dynamic_local]]
214 ; CHECK-NEXT: i32.const $push[[L10:.+]]=, 105
215 ; CHECK-NEXT: i32.store 0($pop[[L23]]), $pop[[L10]]
216 ; CHECK-NEXT: local.get $push[[L23:.+]]=, [[dynamic2_local]]
217 ; CHECK-NEXT: i32.const $push[[L11:.+]]=, 106
218 ; CHECK-NEXT: i32.store 0($pop[[L23]]), $pop[[L11]]
219 store volatile i32 104, i32* %static
220 store volatile i32 105, i32* %dynamic
221 store volatile i32 106, i32* %dynamic.2
222
223 ; Writeback to memory.
224 ; CHECK: local.get $push[[L24:.+]]=, [[FP]]{{$}}
225 ; CHECK: i[[PTR]].const $push[[L18:.+]]=, 16
226 ; CHECK-NEXT: i[[PTR]].add $push[[L19:.+]]=, $pop[[L24]], $pop[[L18]]
227 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L19]]
228 ret void
229}
230
231declare i8* @llvm.stacksave.p0i8()
232declare void @llvm.stackrestore.p0i8(i8*)
233
234; CHECK-LABEL: llvm_stack_builtins:
235define void @llvm_stack_builtins(i32 %alloc) noredzone {
236 ; CHECK: global.get $push[[L11:.+]]=, __stack_pointer{{$}}
237 ; CHECK-NEXT: local.tee $push[[L10:.+]]=, {{.+}}, $pop[[L11]]
238 ; CHECK-NEXT: local.set [[STACK:.+]], $pop[[L10]]
239 %stack = call i8* @llvm.stacksave.p0i8()
240
241 ; Ensure we don't reassign the stacksave local
242 ; CHECK-NOT: local.set [[STACK]],
243 %dynamic = alloca i32, i32 %alloc
244
245 ; CHECK: local.get $push[[L12:.+]]=, [[STACK]]
246 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L12]]
247 call void @llvm.stackrestore.p0i8(i8* %stack)
248
249 ret void
250}
251
252; Not actually using the alloca'd variables exposed an issue with register
253; stackification, where copying the stack pointer into the frame pointer was
254; moved after the stack pointer was updated for the dynamic alloca.
255; CHECK-LABEL: dynamic_alloca_nouse:
256define void @dynamic_alloca_nouse(i32 %alloc) noredzone {
257 ; CHECK: global.get $push[[L11:.+]]=, __stack_pointer{{$}}
258 ; CHECK-NEXT: local.tee $push[[L10:.+]]=, {{.+}}, $pop[[L11]]
259 ; CHECK-NEXT: local.set [[FP:.+]], $pop[[L10]]
260 %dynamic = alloca i32, i32 %alloc
261
262 ; CHECK-NOT: local.set [[FP]],
263
264 ; CHECK: local.get $push[[L12:.+]]=, [[FP]]
265 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L12]]
266 ret void
267}
268
269; The use of the alloca in a phi causes a CopyToReg DAG node to be generated,
270; which has to have special handling because CopyToReg can't have a FI operand
271; CHECK-LABEL: copytoreg_fi:
272define void @copytoreg_fi(i1 %cond, i32* %b) {
273entry:
274 ; CHECK: i[[PTR]].const $push[[L1:.+]]=, 16
275 ; CHECK-NEXT: i[[PTR]].sub $push[[L3:.+]]=, {{.+}}, $pop[[L1]]
276 %addr = alloca i32
277 ; CHECK: i[[PTR]].const $push[[OFF:.+]]=, 12
278 ; CHECK-NEXT: i[[PTR]].add $push[[ADDR:.+]]=, $pop[[L3]], $pop[[OFF]]
279 ; CHECK-NEXT: local.set [[COPY:.+]], $pop[[ADDR]]
280 br label %body
281body:
282 %a = phi i32* [%addr, %entry], [%b, %body]
283 store i32 1, i32* %a
284 ; CHECK: local.get $push[[L12:.+]]=, [[COPY]]
285 ; CHECK: i32.store 0($pop[[L12]]),
286 br i1 %cond, label %body, label %exit
287exit:
288 ret void
289}
290
291declare void @use_i8_star(i8*)
292declare i8* @llvm.frameaddress(i32)
293
294; Test __builtin_frame_address(0).
295; CHECK-LABEL: frameaddress_0:
296; CHECK: global.get $push[[L3:.+]]=, __stack_pointer{{$}}
297; CHECK-NEXT: local.tee $push[[L2:.+]]=, [[FP:.+]], $pop[[L3]]{{$}}
298; CHECK-NEXT: call use_i8_star, $pop[[L2]]
299; CHECK-NEXT: local.get $push[[L5:.+]]=, [[FP]]
300; CHECK-NEXT: global.set __stack_pointer, $pop[[L5]]
301define void @frameaddress_0() {
302  %t = call i8* @llvm.frameaddress(i32 0)
303  call void @use_i8_star(i8* %t)
304  ret void
305}
306
307; Test __builtin_frame_address(1).
308
309; CHECK-LABEL: frameaddress_1:
310; CHECK:      i[[PTR]].const $push0=, 0{{$}}
311; CHECK-NEXT: call use_i8_star, $pop0{{$}}
312; CHECK-NEXT: return{{$}}
313define void @frameaddress_1() {
314  %t = call i8* @llvm.frameaddress(i32 1)
315  call void @use_i8_star(i8* %t)
316  ret void
317}
318
319; Test a stack address passed to an inline asm.
320; CHECK-LABEL: inline_asm:
321; CHECK:       global.get {{.+}}, __stack_pointer{{$}}
322; CHECK:       #APP
323; CHECK-NEXT:  # %{{[0-9]+}}{{$}}
324; CHECK-NEXT:  #NO_APP
325define void @inline_asm() {
326  %tmp = alloca i8
327  call void asm sideeffect "# %0", "r"(i8* %tmp)
328  ret void
329}
330
331; CHECK: .globaltype	__stack_pointer, i[[PTR]]{{$}}
332
333; TODO: test over-aligned alloca
334