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() 232declare void @llvm.stackrestore(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() 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(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; We optimize the format of "frame offset + operand" by folding it, but this is 332; only possible when that operand is an immediate. In this example it is a 333; global address, so we should not fold it. 334; CHECK-LABEL: frame_offset_with_global_address 335; CHECK: i[[PTR]].const ${{.*}}=, str 336@str = local_unnamed_addr global [3 x i8] c"abc", align 16 337define i8 @frame_offset_with_global_address() { 338 %1 = alloca i8, align 4 339 %2 = ptrtoint i8* %1 to i32 340 ;; Here @str is a global address and not an immediate, so cannot be folded 341 %3 = getelementptr [3 x i8], [3 x i8]* @str, i32 0, i32 %2 342 %4 = load i8, i8* %3, align 8 343 %5 = and i8 %4, 67 344 ret i8 %5 345} 346 347; CHECK: .globaltype __stack_pointer, i[[PTR]]{{$}} 348 349; TODO: test over-aligned alloca 350