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