1; RUN: llc < %s -asm-verbose=false | FileCheck %s
2; RUN: llc < %s -asm-verbose=false -fast-isel | FileCheck %s
3
4
5target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6target triple = "wasm32-unknown-unknown"
7
8declare void @ext_func(i64* %ptr)
9declare void @ext_func_i32(i32* %ptr)
10
11; CHECK-LABEL: alloca32:
12; Check that there is an extra local for the stack pointer.
13; CHECK: .local i32{{$}}
14define void @alloca32() noredzone {
15 ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer{{$}}
16 ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
17 ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16
18 ; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]]
19 ; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer{{$}}
20 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]]
21 %retval = alloca i32
22 ; CHECK: i32.const $push[[L0:.+]]=, 0
23 ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L0]]
24 store i32 0, i32* %retval
25 ; CHECK: i32.const $push[[L6:.+]]=, __stack_pointer
26 ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 16
27 ; CHECK-NEXT: i32.add $push[[L7:.+]]=, [[SP]], $pop[[L5]]
28 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L6]]), $pop[[L7]]
29 ret void
30}
31
32; CHECK-LABEL: alloca3264:
33; CHECK: .local i32{{$}}
34define void @alloca3264() {
35 ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
36 ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
37 ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16
38 ; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]]
39 %r1 = alloca i32
40 %r2 = alloca double
41 ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 0
42 ; CHECK-NEXT: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]]
43 store i32 0, i32* %r1
44 ; CHECK-NEXT: i64.const $push[[L0:.+]]=, 0
45 ; CHECK-NEXT: i64.store {{.*}}=, 0([[SP]]), $pop[[L0]]
46 store double 0.0, double* %r2
47 ; CHECK-NEXT: return
48 ret void
49}
50
51; CHECK-LABEL: allocarray:
52; CHECK: .local i32{{$}}
53define void @allocarray() {
54 ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
55 ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
56 ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 144{{$}}
57 ; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]]
58 ; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer{{$}}
59 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]]
60 %r = alloca [33 x i32]
61
62 ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 12
63 ; CHECK-NEXT: i32.add $push[[L7:.+]]=, [[SP]], $pop[[L5]]
64 ; CHECK-NEXT: i32.const $push[[L4:.+]]=, 12
65 ; CHECK-NEXT: i32.add $push[[L6:.+]]=, $pop[[L7]], $pop[[L4]]
66 ; CHECK-NEXT: i32.const $push[[L9:.+]]=, 1{{$}}
67 ; CHECK-NEXT: i32.store $push[[L10:.+]]=, 12([[SP]]), $pop[[L9]]{{$}}
68 ; CHECK-NEXT: i32.store $discard=, 0($pop3), $pop[[L10]]{{$}}
69 %p = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 0
70 store i32 1, i32* %p
71 %p2 = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 3
72 store i32 1, i32* %p2
73
74 ; CHECK: i32.const $push[[L12:.+]]=, __stack_pointer
75 ; CHECK-NEXT: i32.const $push[[L11:.+]]=, 144
76 ; CHECK-NEXT: i32.add $push[[L13:.+]]=, [[SP]], $pop[[L11]]
77 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L12]]), $pop[[L13]]
78 ret void
79}
80
81; CHECK-LABEL: non_mem_use
82define void @non_mem_use(i8** %addr) {
83 ; CHECK: i32.const $push[[L1:.+]]=, 48
84 ; CHECK-NEXT: i32.sub [[SP:.+]]=, {{.+}}, $pop[[L1]]
85 %buf = alloca [27 x i8], align 16
86 %r = alloca i64
87 %r2 = alloca i64
88 ; %r is at SP+8
89 ; CHECK: i32.const $push[[OFF:.+]]=, 8
90 ; CHECK-NEXT: i32.add $push[[ARG1:.+]]=, [[SP]], $pop[[OFF]]
91 ; CHECK-NEXT: call ext_func@FUNCTION, $pop[[ARG1]]
92 call void @ext_func(i64* %r)
93 ; %r2 is at SP+0, no add needed
94 ; CHECK-NEXT: call ext_func@FUNCTION, [[SP]]
95 call void @ext_func(i64* %r2)
96 ; Use as a value, but in a store
97 ; %buf is at SP+16
98 ; CHECK: i32.const $push[[OFF:.+]]=, 16
99 ; CHECK-NEXT: i32.add $push[[VAL:.+]]=, [[SP]], $pop[[OFF]]
100 ; CHECK-NEXT: i32.store {{.*}}=, 0($0), $pop[[VAL]]
101 %gep = getelementptr inbounds [27 x i8], [27 x i8]* %buf, i32 0, i32 0
102 store i8* %gep, i8** %addr
103 ret void
104}
105
106; CHECK-LABEL: allocarray_inbounds:
107; CHECK: .local i32{{$}}
108define void @allocarray_inbounds() {
109 ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
110 ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
111 ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 32{{$}}
112 ; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]]
113 %r = alloca [5 x i32]
114 ; CHECK: i32.const $push[[L3:.+]]=, 1
115 ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]]
116 %p = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 0
117 store i32 1, i32* %p
118 ; This store should have both the GEP and the FI folded into it.
119 ; CHECK-NEXT: i32.store {{.*}}=, 24([[SP]]), $pop
120 %p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 3
121 store i32 1, i32* %p2
122 call void @ext_func(i64* null);
123 ; CHECK: i32.const $push[[L6:.+]]=, __stack_pointer
124 ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 32
125 ; CHECK-NEXT: i32.add $push[[L7:.+]]=, [[SP]], $pop[[L5]]
126 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L6]]), $pop[[L7]]
127 ret void
128}
129
130; CHECK-LABEL: dynamic_alloca:
131define void @dynamic_alloca(i32 %alloc) {
132 ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
133 ; CHECK-NEXT: i32.load [[SP:.+]]=, 0($pop[[L1]])
134 ; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
135 ; Target independent codegen bumps the stack pointer.
136 ; CHECK: i32.sub
137 ; CHECK-NEXT: copy_local [[SP]]=,
138 ; Check that SP is written back to memory after decrement
139 ; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer{{$}}
140 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]]
141 %r = alloca i32, i32 %alloc
142 ; Target-independent codegen also calculates the store addr
143 ; CHECK: call ext_func_i32@FUNCTION
144 call void @ext_func_i32(i32* %r)
145 ; CHECK: i32.const $push[[L3:.+]]=, __stack_pointer
146 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L3]]), [[FP]]
147 ret void
148}
149
150; CHECK-LABEL: dynamic_alloca_redzone:
151define void @dynamic_alloca_redzone(i32 %alloc) {
152 ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
153 ; CHECK-NEXT: i32.load [[SP:.+]]=, 0($pop[[L1]])
154 ; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
155 ; Target independent codegen bumps the stack pointer
156 ; CHECK: i32.sub [[R:.+]]=,
157 ; CHECK-NEXT: copy_local [[SP]]=, [[R]]
158 %r = alloca i32, i32 %alloc
159 ; check-next here asserts that SP is not written back.
160 ; CHECK-NEXT: i32.const $push[[ZERO:.+]]=, 0
161 ; CHECK-NEXT: i32.store $discard=, 0([[R]]), $pop[[ZERO]]
162 store i32 0, i32* %r
163 ; CHECK-NEXT: return
164 ret void
165}
166
167; CHECK-LABEL: dynamic_static_alloca:
168define void @dynamic_static_alloca(i32 %alloc) noredzone {
169 ; Decrement SP in the prolog by the static amount and writeback to memory.
170 ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
171 ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
172 ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16
173 ; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]]
174 ; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
175 ; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer
176 ; CHECK-NEXT: i32.store {{.*}}=, 0($pop[[L4]]), [[SP]]
177 ; Decrement SP in the body by the dynamic amount.
178 ; CHECK: i32.sub
179 ; CHECK: copy_local [[SP]]=,
180 ; Writeback to memory.
181 ; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer
182 ; CHECK-NEXT: i32.store {{.*}}=, 0($pop[[L4]]), [[SP]]
183 %r1 = alloca i32
184 %r = alloca i32, i32 %alloc
185 store i32 0, i32* %r
186 ; CHECK: i32.const $push[[L6:.+]]=, __stack_pointer
187 ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 16
188 ; CHECK-NEXT: i32.add $push[[L7:.+]]=, [[FP]], $pop[[L5]]
189 ; CHECK-NEXT: i32.store $discard=, 0($pop[[L6]]), $pop[[L7]]
190 ret void
191}
192
193; The use of the alloca in a phi causes a CopyToReg DAG node to be generated,
194; which has to have special handling because CopyToReg can't have a FI operand
195; CHECK-LABEL: copytoreg_fi:
196define void @copytoreg_fi(i1 %cond, i32* %b) {
197entry:
198 ; CHECK: i32.const $push[[L1:.+]]=, 16
199 ; CHECK-NEXT: i32.sub [[SP:.+]]=, {{.+}}, $pop[[L1]]
200 %addr = alloca i32
201 ; CHECK: i32.const $push[[OFF:.+]]=, 12
202 ; CHECK-NEXT: i32.add $push[[ADDR:.+]]=, [[SP]], $pop[[OFF]]
203 ; CHECK-NEXT: copy_local [[COPY:.+]]=, $pop[[ADDR]]
204 br label %body
205body:
206 %a = phi i32* [%addr, %entry], [%b, %body]
207 store i32 1, i32* %a
208 ; CHECK: i32.store {{.*}}, 0([[COPY]]),
209 br i1 %cond, label %body, label %exit
210exit:
211 ret void
212}
213
214declare void @use_i8_star(i8*)
215declare i8* @llvm.frameaddress(i32)
216
217; Test __builtin_frame_address(0).
218; CHECK-LABEL: frameaddress_0:
219; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
220; CHECK-NEXT: i32.load [[SP:.+]]=, 0($pop[[L1]])
221; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
222; CHECK-NEXT: call use_i8_star@FUNCTION, [[FP]]
223; CHEC K-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer
224; CHEC K-NEXT: i32.store [[SP]]=, 0($pop[[L6]]), [[FP]]
225define void @frameaddress_0() {
226  %t = call i8* @llvm.frameaddress(i32 0)
227  call void @use_i8_star(i8* %t)
228  ret void
229}
230
231; Test __builtin_frame_address(1).
232
233; CHECK-LABEL: frameaddress_1:
234; CHECK-NEXT: i32.const $push0=, 0{{$}}
235; CHECK-NEXT: call use_i8_star@FUNCTION, $pop0{{$}}
236; CHECK-NEXT: return{{$}}
237define void @frameaddress_1() {
238  %t = call i8* @llvm.frameaddress(i32 1)
239  call void @use_i8_star(i8* %t)
240  ret void
241}
242
243; Test a stack address passed to an inline asm.
244; CHECK-LABEL: inline_asm:
245; CHECK:       __stack_pointer
246; CHECK:       #APP
247; CHECK-NEXT:  # %{{[0-9]+}}{{$}}
248; CHECK-NEXT:  #NO_APP
249define void @inline_asm() {
250  %tmp = alloca i8
251  call void asm sideeffect "# %0", "r"(i8* %tmp)
252  ret void
253}
254
255; TODO: test over-aligned alloca
256