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