Home | History | Annotate | Download | only in WebAssembly
      1 ; RUN: llc < %s -asm-verbose=false | FileCheck %s
      2 
      3 ; Test constant load and store address offsets.
      4 
      5 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
      6 target triple = "wasm32-unknown-unknown"
      7 
      8 ; With an nuw add, we can fold an offset.
      9 
     10 ; CHECK-LABEL: load_i32_with_folded_offset:
     11 ; CHECK: i32.load  $push0=, 24($0){{$}}
     12 define i32 @load_i32_with_folded_offset(i32* %p) {
     13   %q = ptrtoint i32* %p to i32
     14   %r = add nuw i32 %q, 24
     15   %s = inttoptr i32 %r to i32*
     16   %t = load i32, i32* %s
     17   ret i32 %t
     18 }
     19 
     20 ; With an inbounds gep, we can fold an offset.
     21 
     22 ; CHECK-LABEL: load_i32_with_folded_gep_offset:
     23 ; CHECK: i32.load  $push0=, 24($0){{$}}
     24 define i32 @load_i32_with_folded_gep_offset(i32* %p) {
     25   %s = getelementptr inbounds i32, i32* %p, i32 6
     26   %t = load i32, i32* %s
     27   ret i32 %t
     28 }
     29 
     30 ; We can't fold a negative offset though, even with an inbounds gep.
     31 
     32 ; CHECK-LABEL: load_i32_with_unfolded_gep_negative_offset:
     33 ; CHECK: i32.const $push0=, -24{{$}}
     34 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
     35 ; CHECK: i32.load  $push2=, 0($pop1){{$}}
     36 define i32 @load_i32_with_unfolded_gep_negative_offset(i32* %p) {
     37   %s = getelementptr inbounds i32, i32* %p, i32 -6
     38   %t = load i32, i32* %s
     39   ret i32 %t
     40 }
     41 
     42 ; Without nuw, and even with nsw, we can't fold an offset.
     43 
     44 ; CHECK-LABEL: load_i32_with_unfolded_offset:
     45 ; CHECK: i32.const $push0=, 24{{$}}
     46 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
     47 ; CHECK: i32.load  $push2=, 0($pop1){{$}}
     48 define i32 @load_i32_with_unfolded_offset(i32* %p) {
     49   %q = ptrtoint i32* %p to i32
     50   %r = add nsw i32 %q, 24
     51   %s = inttoptr i32 %r to i32*
     52   %t = load i32, i32* %s
     53   ret i32 %t
     54 }
     55 
     56 ; Without inbounds, we can't fold a gep offset.
     57 
     58 ; CHECK-LABEL: load_i32_with_unfolded_gep_offset:
     59 ; CHECK: i32.const $push0=, 24{{$}}
     60 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
     61 ; CHECK: i32.load  $push2=, 0($pop1){{$}}
     62 define i32 @load_i32_with_unfolded_gep_offset(i32* %p) {
     63   %s = getelementptr i32, i32* %p, i32 6
     64   %t = load i32, i32* %s
     65   ret i32 %t
     66 }
     67 
     68 ; Same as above but with i64.
     69 
     70 ; CHECK-LABEL: load_i64_with_folded_offset:
     71 ; CHECK: i64.load  $push0=, 24($0){{$}}
     72 define i64 @load_i64_with_folded_offset(i64* %p) {
     73   %q = ptrtoint i64* %p to i32
     74   %r = add nuw i32 %q, 24
     75   %s = inttoptr i32 %r to i64*
     76   %t = load i64, i64* %s
     77   ret i64 %t
     78 }
     79 
     80 ; Same as above but with i64.
     81 
     82 ; CHECK-LABEL: load_i64_with_folded_gep_offset:
     83 ; CHECK: i64.load  $push0=, 24($0){{$}}
     84 define i64 @load_i64_with_folded_gep_offset(i64* %p) {
     85   %s = getelementptr inbounds i64, i64* %p, i32 3
     86   %t = load i64, i64* %s
     87   ret i64 %t
     88 }
     89 
     90 ; Same as above but with i64.
     91 
     92 ; CHECK-LABEL: load_i64_with_unfolded_gep_negative_offset:
     93 ; CHECK: i32.const $push0=, -24{{$}}
     94 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
     95 ; CHECK: i64.load  $push2=, 0($pop1){{$}}
     96 define i64 @load_i64_with_unfolded_gep_negative_offset(i64* %p) {
     97   %s = getelementptr inbounds i64, i64* %p, i32 -3
     98   %t = load i64, i64* %s
     99   ret i64 %t
    100 }
    101 
    102 ; Same as above but with i64.
    103 
    104 ; CHECK-LABEL: load_i64_with_unfolded_offset:
    105 ; CHECK: i32.const $push0=, 24{{$}}
    106 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    107 ; CHECK: i64.load  $push2=, 0($pop1){{$}}
    108 define i64 @load_i64_with_unfolded_offset(i64* %p) {
    109   %q = ptrtoint i64* %p to i32
    110   %r = add nsw i32 %q, 24
    111   %s = inttoptr i32 %r to i64*
    112   %t = load i64, i64* %s
    113   ret i64 %t
    114 }
    115 
    116 ; Same as above but with i64.
    117 
    118 ; CHECK-LABEL: load_i64_with_unfolded_gep_offset:
    119 ; CHECK: i32.const $push0=, 24{{$}}
    120 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    121 ; CHECK: i64.load  $push2=, 0($pop1){{$}}
    122 define i64 @load_i64_with_unfolded_gep_offset(i64* %p) {
    123   %s = getelementptr i64, i64* %p, i32 3
    124   %t = load i64, i64* %s
    125   ret i64 %t
    126 }
    127 
    128 ; CHECK-LABEL: load_i32_with_folded_or_offset:
    129 ; CHECK: i32.load8_s $push{{[0-9]+}}=, 2($pop{{[0-9]+}}){{$}}
    130 define i32 @load_i32_with_folded_or_offset(i32 %x) {
    131   %and = and i32 %x, -4
    132   %t0 = inttoptr i32 %and to i8*
    133   %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
    134   %t1 = load i8, i8* %arrayidx, align 1
    135   %conv = sext i8 %t1 to i32
    136   ret i32 %conv
    137 }
    138 
    139 ; Same as above but with store.
    140 
    141 ; CHECK-LABEL: store_i32_with_folded_offset:
    142 ; CHECK: i32.store $drop=, 24($0), $pop0{{$}}
    143 define void @store_i32_with_folded_offset(i32* %p) {
    144   %q = ptrtoint i32* %p to i32
    145   %r = add nuw i32 %q, 24
    146   %s = inttoptr i32 %r to i32*
    147   store i32 0, i32* %s
    148   ret void
    149 }
    150 
    151 ; Same as above but with store.
    152 
    153 ; CHECK-LABEL: store_i32_with_folded_gep_offset:
    154 ; CHECK: i32.store $drop=, 24($0), $pop0{{$}}
    155 define void @store_i32_with_folded_gep_offset(i32* %p) {
    156   %s = getelementptr inbounds i32, i32* %p, i32 6
    157   store i32 0, i32* %s
    158   ret void
    159 }
    160 
    161 ; Same as above but with store.
    162 
    163 ; CHECK-LABEL: store_i32_with_unfolded_gep_negative_offset:
    164 ; CHECK: i32.const $push0=, -24{{$}}
    165 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    166 ; CHECK: i32.store $drop=, 0($pop1), $pop2{{$}}
    167 define void @store_i32_with_unfolded_gep_negative_offset(i32* %p) {
    168   %s = getelementptr inbounds i32, i32* %p, i32 -6
    169   store i32 0, i32* %s
    170   ret void
    171 }
    172 
    173 ; Same as above but with store.
    174 
    175 ; CHECK-LABEL: store_i32_with_unfolded_offset:
    176 ; CHECK: i32.const $push0=, 24{{$}}
    177 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    178 ; CHECK: i32.store $drop=, 0($pop1), $pop2{{$}}
    179 define void @store_i32_with_unfolded_offset(i32* %p) {
    180   %q = ptrtoint i32* %p to i32
    181   %r = add nsw i32 %q, 24
    182   %s = inttoptr i32 %r to i32*
    183   store i32 0, i32* %s
    184   ret void
    185 }
    186 
    187 ; Same as above but with store.
    188 
    189 ; CHECK-LABEL: store_i32_with_unfolded_gep_offset:
    190 ; CHECK: i32.const $push0=, 24{{$}}
    191 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    192 ; CHECK: i32.store $drop=, 0($pop1), $pop2{{$}}
    193 define void @store_i32_with_unfolded_gep_offset(i32* %p) {
    194   %s = getelementptr i32, i32* %p, i32 6
    195   store i32 0, i32* %s
    196   ret void
    197 }
    198 
    199 ; Same as above but with store with i64.
    200 
    201 ; CHECK-LABEL: store_i64_with_folded_offset:
    202 ; CHECK: i64.store $drop=, 24($0), $pop0{{$}}
    203 define void @store_i64_with_folded_offset(i64* %p) {
    204   %q = ptrtoint i64* %p to i32
    205   %r = add nuw i32 %q, 24
    206   %s = inttoptr i32 %r to i64*
    207   store i64 0, i64* %s
    208   ret void
    209 }
    210 
    211 ; Same as above but with store with i64.
    212 
    213 ; CHECK-LABEL: store_i64_with_folded_gep_offset:
    214 ; CHECK: i64.store $drop=, 24($0), $pop0{{$}}
    215 define void @store_i64_with_folded_gep_offset(i64* %p) {
    216   %s = getelementptr inbounds i64, i64* %p, i32 3
    217   store i64 0, i64* %s
    218   ret void
    219 }
    220 
    221 ; Same as above but with store with i64.
    222 
    223 ; CHECK-LABEL: store_i64_with_unfolded_gep_negative_offset:
    224 ; CHECK: i32.const $push0=, -24{{$}}
    225 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    226 ; CHECK: i64.store $drop=, 0($pop1), $pop2{{$}}
    227 define void @store_i64_with_unfolded_gep_negative_offset(i64* %p) {
    228   %s = getelementptr inbounds i64, i64* %p, i32 -3
    229   store i64 0, i64* %s
    230   ret void
    231 }
    232 
    233 ; Same as above but with store with i64.
    234 
    235 ; CHECK-LABEL: store_i64_with_unfolded_offset:
    236 ; CHECK: i32.const $push0=, 24{{$}}
    237 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    238 ; CHECK: i64.store $drop=, 0($pop1), $pop2{{$}}
    239 define void @store_i64_with_unfolded_offset(i64* %p) {
    240   %q = ptrtoint i64* %p to i32
    241   %r = add nsw i32 %q, 24
    242   %s = inttoptr i32 %r to i64*
    243   store i64 0, i64* %s
    244   ret void
    245 }
    246 
    247 ; Same as above but with store with i64.
    248 
    249 ; CHECK-LABEL: store_i64_with_unfolded_gep_offset:
    250 ; CHECK: i32.const $push0=, 24{{$}}
    251 ; CHECK: i32.add   $push1=, $0, $pop0{{$}}
    252 ; CHECK: i64.store $drop=, 0($pop1), $pop2{{$}}
    253 define void @store_i64_with_unfolded_gep_offset(i64* %p) {
    254   %s = getelementptr i64, i64* %p, i32 3
    255   store i64 0, i64* %s
    256   ret void
    257 }
    258 
    259 ; CHECK-LABEL: store_i32_with_folded_or_offset:
    260 ; CHECK: i32.store8 $drop=, 2($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}}
    261 define void @store_i32_with_folded_or_offset(i32 %x) {
    262   %and = and i32 %x, -4
    263   %t0 = inttoptr i32 %and to i8*
    264   %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
    265   store i8 0, i8* %arrayidx, align 1
    266   ret void
    267 }
    268 
    269 ; When loading from a fixed address, materialize a zero.
    270 
    271 ; CHECK-LABEL: load_i32_from_numeric_address
    272 ; CHECK: i32.const $push0=, 0{{$}}
    273 ; CHECK: i32.load  $push1=, 42($pop0){{$}}
    274 define i32 @load_i32_from_numeric_address() {
    275   %s = inttoptr i32 42 to i32*
    276   %t = load i32, i32* %s
    277   ret i32 %t
    278 }
    279 
    280 ; CHECK-LABEL: load_i32_from_global_address
    281 ; CHECK: i32.const $push0=, 0{{$}}
    282 ; CHECK: i32.load  $push1=, gv($pop0){{$}}
    283 @gv = global i32 0
    284 define i32 @load_i32_from_global_address() {
    285   %t = load i32, i32* @gv
    286   ret i32 %t
    287 }
    288 
    289 ; CHECK-LABEL: store_i32_to_numeric_address:
    290 ; CHECK-NEXT: i32.const $push0=, 0{{$}}
    291 ; CHECK-NEXT: i32.const $push1=, 0{{$}}
    292 ; CHECK-NEXT: i32.store $drop=, 42($pop0), $pop1{{$}}
    293 define void @store_i32_to_numeric_address() {
    294   %s = inttoptr i32 42 to i32*
    295   store i32 0, i32* %s
    296   ret void
    297 }
    298 
    299 ; CHECK-LABEL: store_i32_to_global_address:
    300 ; CHECK: i32.const $push0=, 0{{$}}
    301 ; CHECK: i32.const $push1=, 0{{$}}
    302 ; CHECK: i32.store $drop=, gv($pop0), $pop1{{$}}
    303 define void @store_i32_to_global_address() {
    304   store i32 0, i32* @gv
    305   ret void
    306 }
    307 
    308 ; Fold an offset into a sign-extending load.
    309 
    310 ; CHECK-LABEL: load_i8_s_with_folded_offset:
    311 ; CHECK: i32.load8_s $push0=, 24($0){{$}}
    312 define i32 @load_i8_s_with_folded_offset(i8* %p) {
    313   %q = ptrtoint i8* %p to i32
    314   %r = add nuw i32 %q, 24
    315   %s = inttoptr i32 %r to i8*
    316   %t = load i8, i8* %s
    317   %u = sext i8 %t to i32
    318   ret i32 %u
    319 }
    320 
    321 ; Fold a gep offset into a sign-extending load.
    322 
    323 ; CHECK-LABEL: load_i8_s_with_folded_gep_offset:
    324 ; CHECK: i32.load8_s $push0=, 24($0){{$}}
    325 define i32 @load_i8_s_with_folded_gep_offset(i8* %p) {
    326   %s = getelementptr inbounds i8, i8* %p, i32 24
    327   %t = load i8, i8* %s
    328   %u = sext i8 %t to i32
    329   ret i32 %u
    330 }
    331 
    332 ; Fold an offset into a zero-extending load.
    333 
    334 ; CHECK-LABEL: load_i8_u_with_folded_offset:
    335 ; CHECK: i32.load8_u $push0=, 24($0){{$}}
    336 define i32 @load_i8_u_with_folded_offset(i8* %p) {
    337   %q = ptrtoint i8* %p to i32
    338   %r = add nuw i32 %q, 24
    339   %s = inttoptr i32 %r to i8*
    340   %t = load i8, i8* %s
    341   %u = zext i8 %t to i32
    342   ret i32 %u
    343 }
    344 
    345 ; Fold a gep offset into a zero-extending load.
    346 
    347 ; CHECK-LABEL: load_i8_u_with_folded_gep_offset:
    348 ; CHECK: i32.load8_u $push0=, 24($0){{$}}
    349 define i32 @load_i8_u_with_folded_gep_offset(i8* %p) {
    350   %s = getelementptr inbounds i8, i8* %p, i32 24
    351   %t = load i8, i8* %s
    352   %u = zext i8 %t to i32
    353   ret i32 %u
    354 }
    355 
    356 ; Fold an offset into a truncating store.
    357 
    358 ; CHECK-LABEL: store_i8_with_folded_offset:
    359 ; CHECK: i32.store8 $drop=, 24($0), $pop0{{$}}
    360 define void @store_i8_with_folded_offset(i8* %p) {
    361   %q = ptrtoint i8* %p to i32
    362   %r = add nuw i32 %q, 24
    363   %s = inttoptr i32 %r to i8*
    364   store i8 0, i8* %s
    365   ret void
    366 }
    367 
    368 ; Fold a gep offset into a truncating store.
    369 
    370 ; CHECK-LABEL: store_i8_with_folded_gep_offset:
    371 ; CHECK: i32.store8 $drop=, 24($0), $pop0{{$}}
    372 define void @store_i8_with_folded_gep_offset(i8* %p) {
    373   %s = getelementptr inbounds i8, i8* %p, i32 24
    374   store i8 0, i8* %s
    375   ret void
    376 }
    377 
    378 ; Fold the offsets when lowering aggregate loads and stores.
    379 
    380 ; CHECK-LABEL: aggregate_load_store:
    381 ; CHECK: i32.load  $2=, 0($0){{$}}
    382 ; CHECK: i32.load  $3=, 4($0){{$}}
    383 ; CHECK: i32.load  $4=, 8($0){{$}}
    384 ; CHECK: i32.load  $push0=, 12($0){{$}}
    385 ; CHECK: i32.store $drop=, 12($1), $pop0{{$}}
    386 ; CHECK: i32.store $drop=, 8($1), $4{{$}}
    387 ; CHECK: i32.store $drop=, 4($1), $3{{$}}
    388 ; CHECK: i32.store $drop=, 0($1), $2{{$}}
    389 define void @aggregate_load_store({i32,i32,i32,i32}* %p, {i32,i32,i32,i32}* %q) {
    390   ; volatile so that things stay in order for the tests above
    391   %t = load volatile {i32,i32,i32,i32}, {i32, i32,i32,i32}* %p
    392   store volatile {i32,i32,i32,i32} %t, {i32, i32,i32,i32}* %q
    393   ret void
    394 }
    395 
    396 ; Fold the offsets when lowering aggregate return values. The stores get
    397 ; merged into i64 stores.
    398 
    399 ; CHECK-LABEL: aggregate_return:
    400 ; CHECK: i64.const   $push[[L0:[0-9]+]]=, 0{{$}}
    401 ; CHECK: i64.store   $push[[L1:[0-9]+]]=, 8($0):p2align=2, $pop[[L0]]{{$}}
    402 ; CHECK: i64.store   $drop=, 0($0):p2align=2, $pop[[L1]]{{$}}
    403 define {i32,i32,i32,i32} @aggregate_return() {
    404   ret {i32,i32,i32,i32} zeroinitializer
    405 }
    406 
    407 ; Fold the offsets when lowering aggregate return values. The stores are not
    408 ; merged.
    409 
    410 ; CHECK-LABEL: aggregate_return_without_merge:
    411 ; CHECK: i32.const   $push[[L0:[0-9]+]]=, 0{{$}}
    412 ; CHECK: i32.store8  $push[[L1:[0-9]+]]=, 14($0), $pop[[L0]]{{$}}
    413 ; CHECK: i32.store16 $push[[L2:[0-9]+]]=, 12($0), $pop[[L1]]{{$}}
    414 ; CHECK: i32.store   $drop=, 8($0), $pop[[L2]]{{$}}
    415 ; CHECK: i64.const   $push[[L3:[0-9]+]]=, 0{{$}}
    416 ; CHECK: i64.store   $drop=, 0($0), $pop[[L3]]{{$}}
    417 define {i64,i32,i16,i8} @aggregate_return_without_merge() {
    418   ret {i64,i32,i16,i8} zeroinitializer
    419 }
    420