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