Home | History | Annotate | Download | only in WebAssembly
      1 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s
      2 
      3 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
      4 target triple = "wasm32-unknown-unknown"
      5 
      6 declare void @ext_func(i64* %ptr)
      7 declare void @ext_func_i32(i32* %ptr)
      8 
      9 ; CHECK-LABEL: alloca32:
     10 ; Check that there is an extra local for the stack pointer.
     11 ; CHECK: .local i32{{$}}
     12 define void @alloca32() noredzone {
     13  ; CHECK: i32.const $push[[L4:.+]]=, 0{{$}}
     14  ; CHECK: i32.const $push[[L1:.+]]=, 0{{$}}
     15  ; CHECK-NEXT: i32.load $push[[L2:.+]]=, __stack_pointer($pop[[L1]])
     16  ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16
     17  ; CHECK-NEXT: i32.sub $push[[L8:.+]]=, $pop[[L2]], $pop[[L3]]
     18  ; CHECK-NEXT: i32.store $push[[L10:.+]]=, __stack_pointer($pop[[L4]]), $pop[[L8]]{{$}}
     19  ; CHECK-NEXT: tee_local $push[[L9:.+]]=, $[[SP:.+]]=, $pop[[L10]]{{$}}
     20  %retval = alloca i32
     21  ; CHECK: i32.const $push[[L0:.+]]=, 0
     22  ; CHECK: i32.store {{.*}}=, 12($pop[[L9]]), $pop[[L0]]
     23  store i32 0, i32* %retval
     24  ; CHECK: i32.const $push[[L6:.+]]=, 0
     25  ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 16
     26  ; CHECK-NEXT: i32.add $push[[L7:.+]]=, $[[SP]], $pop[[L5]]
     27  ; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L6]]), $pop[[L7]]
     28  ret void
     29 }
     30 
     31 ; CHECK-LABEL: alloca3264:
     32 ; CHECK: .local i32{{$}}
     33 define void @alloca3264() {
     34  ; CHECK: i32.const $push[[L2:.+]]=, 0{{$}}
     35  ; CHECK-NEXT: i32.load $push[[L3:.+]]=, __stack_pointer($pop[[L2]])
     36  ; CHECK-NEXT: i32.const $push[[L4:.+]]=, 16
     37  ; CHECK-NEXT: i32.sub $push[[L6:.+]]=, $pop[[L3]], $pop[[L4]]
     38  ; CHECK-NEXT: tee_local $push[[L5:.+]]=, $[[SP:.+]]=, $pop[[L6]]
     39  %r1 = alloca i32
     40  %r2 = alloca double
     41  ; CHECK-NEXT: i32.const $push[[L0:.+]]=, 0
     42  ; CHECK-NEXT: i32.store $drop=, 12($pop[[L5]]), $pop[[L0]]
     43  store i32 0, i32* %r1
     44  ; CHECK-NEXT: i64.const $push[[L1:.+]]=, 0
     45  ; CHECK-NEXT: i64.store $drop=, 0($[[SP]]), $pop[[L1]]
     46  store double 0.0, double* %r2
     47  ; CHECK-NEXT: return
     48  ret void
     49 }
     50 
     51 ; CHECK-LABEL: allocarray:
     52 ; CHECK: .local i32{{$}}
     53 define void @allocarray() {
     54  ; CHECK: i32.const $push[[L7:.+]]=, 0{{$}}
     55  ; CHECK: i32.const $push[[L4:.+]]=, 0{{$}}
     56  ; CHECK-NEXT: i32.load $push[[L5:.+]]=, __stack_pointer($pop[[L4]])
     57  ; CHECK-NEXT: i32.const $push[[L6:.+]]=, 144{{$}}
     58  ; CHECK-NEXT: i32.sub $push[[L11:.+]]=, $pop[[L5]], $pop[[L6]]
     59  ; CHECK-NEXT: i32.store ${{.+}}=, __stack_pointer($pop[[L7]]), $pop[[L11]]
     60  %r = alloca [33 x i32]
     61 
     62  ; CHECK:      i32.const $push{{.+}}=, 24
     63  ; CHECK-NEXT: i32.add $push[[L3:.+]]=, $[[SP]], $pop{{.+}}
     64  ; CHECK-NEXT: i32.const $push[[L1:.+]]=, 1{{$}}
     65  ; CHECK-NEXT: i32.store $push[[L0:.+]]=, 0($pop[[L3]]), $pop[[L1]]{{$}}
     66  ; CHECK-NEXT: i32.store $drop=, 12(${{.+}}), $pop[[L0]]{{$}}
     67  %p = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 0
     68  store i32 1, i32* %p
     69  %p2 = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 3
     70  store i32 1, i32* %p2
     71 
     72  ; CHECK: i32.const $push[[L10:.+]]=, 0{{$}}
     73  ; CHECK-NEXT: i32.const $push[[L8:.+]]=, 144
     74  ; CHECK-NEXT: i32.add $push[[L19:.+]]=, $[[SP]], $pop[[L8]]
     75  ; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L10]]), $pop[[L9]]
     76  ret void
     77 }
     78 
     79 ; CHECK-LABEL: non_mem_use
     80 define void @non_mem_use(i8** %addr) {
     81  ; CHECK: i32.const $push[[L1:.+]]=, 48
     82  ; CHECK-NEXT: i32.sub $push[[L11:.+]]=, {{.+}}, $pop[[L1]]
     83  ; CHECK-NEXT: i32.store $[[SP:.+]]=, {{.+}}, $pop[[L11]]
     84  %buf = alloca [27 x i8], align 16
     85  %r = alloca i64
     86  %r2 = alloca i64
     87  ; %r is at SP+8
     88  ; CHECK: tee_local $push[[L12:.+]]=, $[[SP:.+]]=, $pop{{.+}}
     89  ; CHECK: i32.const $push[[OFF:.+]]=, 8
     90  ; CHECK-NEXT: i32.add $push[[ARG1:.+]]=, $pop[[L12]], $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{{$}}
    108 define void @allocarray_inbounds() {
    109  ; CHECK: i32.const $push[[L6:.+]]=, 0{{$}}
    110  ; CHECK: i32.const $push[[L3:.+]]=, 0{{$}}
    111  ; CHECK-NEXT: i32.load $push[[L4:.+]]=, __stack_pointer($pop[[L3]])
    112  ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 32{{$}}
    113  ; CHECK-NEXT: i32.sub $push[[L10:.+]]=, $pop[[L4]], $pop[[L5]]
    114  ; CHECK-NEXT: i32.store ${{.+}}=, __stack_pointer($pop[[L6]]), $pop[[L10]]{{$}}
    115  %r = alloca [5 x i32]
    116  ; CHECK: i32.const $push[[L3:.+]]=, 1
    117  ; CHECK-DAG: i32.store $push{{.*}}=, 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: i32.const $push[[L6:.+]]=, 0{{$}}
    127  ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 32{{$}}
    128  ; CHECK-NEXT: i32.add $push[[L7:.+]]=, ${{.+}}, $pop[[L5]]
    129  ; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L6]]), $pop[[L7]]
    130  ret void
    131 }
    132 
    133 ; CHECK-LABEL: dynamic_alloca:
    134 define void @dynamic_alloca(i32 %alloc) {
    135  ; CHECK: i32.const $push[[L7:.+]]=, 0{{$}}
    136  ; CHECK: i32.const $push[[L1:.+]]=, 0{{$}}
    137  ; CHECK-NEXT: i32.load $push[[L13:.+]]=, __stack_pointer($pop[[L1]])
    138  ; CHECK-NEXT: tee_local $push[[L12:.+]]=, [[SP:.+]], $pop[[L13]]{{$}}
    139  ; Target independent codegen bumps the stack pointer.
    140  ; CHECK: i32.sub
    141  ; Check that SP is written back to memory after decrement
    142  ; CHECK: i32.store $drop=, __stack_pointer($pop{{.+}}), 
    143  %r = alloca i32, i32 %alloc
    144  ; Target-independent codegen also calculates the store addr
    145  ; CHECK: call ext_func_i32@FUNCTION
    146  call void @ext_func_i32(i32* %r)
    147  ; CHECK: i32.const $push[[L3:.+]]=, 0{{$}}
    148  ; CHECK: i32.store $drop=, __stack_pointer($pop[[L3]]), $pop{{.+}}
    149  ret void
    150 }
    151 
    152 ; CHECK-LABEL: dynamic_alloca_redzone:
    153 define void @dynamic_alloca_redzone(i32 %alloc) {
    154  ; CHECK: i32.const $push[[L8:.+]]=, 0{{$}}
    155  ; CHECK-NEXT: i32.load $push[[L13:.+]]=, __stack_pointer($pop[[L1]])
    156  ; CHECK-NEXT: tee_local $push[[L12:.+]]=, [[SP:.+]], $pop[[L13]]{{$}}
    157  ; CHECK-NEXT: copy_local [[FP:.+]]=, $pop[[L12]]{{$}}
    158  ; Target independent codegen bumps the stack pointer
    159  ; CHECK: i32.sub
    160  %r = alloca i32, i32 %alloc
    161  ; CHECK-NEXT: tee_local       $push[[L8:.+]]=, $0=, $pop
    162  ; CHECK-NEXT: copy_local      $drop=, $pop[[L8]]{{$}}
    163  ; CHECK-NEXT: i32.const       $push[[L6:.+]]=, 0{{$}}
    164  ; CHECK-NEXT: i32.store       $drop=, 0($0), $pop[[L6]]{{$}}
    165  store i32 0, i32* %r
    166  ; CHECK-NEXT: return
    167  ret void
    168 }
    169 
    170 ; CHECK-LABEL: dynamic_static_alloca:
    171 define void @dynamic_static_alloca(i32 %alloc) noredzone {
    172  ; Decrement SP in the prolog by the static amount and writeback to memory.
    173  ; CHECK: i32.const $push[[L7:.+]]=, 0{{$}}
    174  ; CHECK: i32.const $push[[L8:.+]]=, 0{{$}}
    175  ; CHECK: i32.const $push[[L9:.+]]=, 0{{$}}
    176  ; CHECK-NEXT: i32.load $push[[L10:.+]]=, __stack_pointer($pop[[L9]])
    177  ; CHECK-NEXT: i32.const $push[[L11:.+]]=, 16
    178  ; CHECK-NEXT: i32.sub $push[[L20:.+]]=, $pop[[L10]], $pop[[L11]]
    179  ; CHECK-NEXT: tee_local $push[[L19:.+]]=, $[[FP:.+]]=, $pop[[L20]]
    180  ; CHECK:      i32.store $push[[L0:.+]]=, __stack_pointer($pop{{.+}}), $pop{{.+}}
    181  ; Decrement SP in the body by the dynamic amount.
    182  ; CHECK: i32.sub
    183  ; Writeback to memory.
    184  ; CHECK: i32.store $drop=, __stack_pointer($pop{{.+}}), $pop{{.+}}
    185  %r1 = alloca i32
    186  %r = alloca i32, i32 %alloc
    187  store i32 0, i32* %r
    188  ; CHEC: i32.store $drop=, 0($pop{{.+}}), $pop{{.+}}
    189  ret void
    190 }
    191 
    192 ; The use of the alloca in a phi causes a CopyToReg DAG node to be generated,
    193 ; which has to have special handling because CopyToReg can't have a FI operand
    194 ; CHECK-LABEL: copytoreg_fi:
    195 define void @copytoreg_fi(i1 %cond, i32* %b) {
    196 entry:
    197  ; CHECK: i32.const $push[[L1:.+]]=, 16
    198  ; CHECK-NEXT: i32.sub $push[[L3:.+]]=, {{.+}}, $pop[[L1]]
    199  %addr = alloca i32
    200  ; CHECK: i32.const $push[[OFF:.+]]=, 12
    201  ; CHECK-NEXT: i32.add $push[[ADDR:.+]]=, $pop[[L3]], $pop[[OFF]]
    202  ; CHECK-NEXT: copy_local [[COPY:.+]]=, $pop[[ADDR]]
    203  br label %body
    204 body:
    205  %a = phi i32* [%addr, %entry], [%b, %body]
    206  store i32 1, i32* %a
    207  ; CHECK: i32.store {{.*}}, 0([[COPY]]),
    208  br i1 %cond, label %body, label %exit
    209 exit:
    210  ret void
    211 }
    212 
    213 declare void @use_i8_star(i8*)
    214 declare i8* @llvm.frameaddress(i32)
    215 
    216 ; Test __builtin_frame_address(0).
    217 ; CHECK-LABEL: frameaddress_0:
    218 ; CHECK: i32.const $push[[L0:.+]]=, 0{{$}}
    219 ; CHECK-NEXT: i32.load $push[[L3:.+]]=, __stack_pointer($pop[[L0]])
    220 ; CHECK-NEXT: copy_local $push[[L4:.+]]=, $pop[[L3]]{{$}}
    221 ; CHECK-NEXT: tee_local $push[[L2:.+]]=, $[[FP:.+]]=, $pop[[L4]]{{$}}
    222 ; CHECK-NEXT: call use_i8_star@FUNCTION, $pop[[L2]]
    223 ; CHECK-NEXT: i32.const $push[[L1:.+]]=, 0{{$}}
    224 ; CHECK-NEXT: i32.store $drop=, __stack_pointer($pop[[L1]]), $[[FP]]
    225 define 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{{$}}
    237 define 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
    249 define 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