1; RUN: llc < %s -asm-verbose=false | FileCheck %s 2 3; Test constant load and store address offsets. 4 5target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 6target 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){{$}} 12define 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){{$}} 24define 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){{$}} 36define 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){{$}} 48define 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){{$}} 62define 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){{$}} 72define 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){{$}} 84define 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){{$}} 96define 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){{$}} 108define 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){{$}} 122define 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; Same as above but with store. 129 130; CHECK-LABEL: store_i32_with_folded_offset: 131; CHECK: i32.store $discard=, 24($0), $pop0{{$}} 132define void @store_i32_with_folded_offset(i32* %p) { 133 %q = ptrtoint i32* %p to i32 134 %r = add nuw i32 %q, 24 135 %s = inttoptr i32 %r to i32* 136 store i32 0, i32* %s 137 ret void 138} 139 140; Same as above but with store. 141 142; CHECK-LABEL: store_i32_with_folded_gep_offset: 143; CHECK: i32.store $discard=, 24($0), $pop0{{$}} 144define void @store_i32_with_folded_gep_offset(i32* %p) { 145 %s = getelementptr inbounds i32, i32* %p, i32 6 146 store i32 0, i32* %s 147 ret void 148} 149 150; Same as above but with store. 151 152; CHECK-LABEL: store_i32_with_unfolded_gep_negative_offset: 153; CHECK: i32.const $push0=, -24{{$}} 154; CHECK: i32.add $push1=, $0, $pop0{{$}} 155; CHECK: i32.store $discard=, 0($pop1), $pop2{{$}} 156define void @store_i32_with_unfolded_gep_negative_offset(i32* %p) { 157 %s = getelementptr inbounds i32, i32* %p, i32 -6 158 store i32 0, i32* %s 159 ret void 160} 161 162; Same as above but with store. 163 164; CHECK-LABEL: store_i32_with_unfolded_offset: 165; CHECK: i32.const $push0=, 24{{$}} 166; CHECK: i32.add $push1=, $0, $pop0{{$}} 167; CHECK: i32.store $discard=, 0($pop1), $pop2{{$}} 168define void @store_i32_with_unfolded_offset(i32* %p) { 169 %q = ptrtoint i32* %p to i32 170 %r = add nsw i32 %q, 24 171 %s = inttoptr i32 %r to i32* 172 store i32 0, i32* %s 173 ret void 174} 175 176; Same as above but with store. 177 178; CHECK-LABEL: store_i32_with_unfolded_gep_offset: 179; CHECK: i32.const $push0=, 24{{$}} 180; CHECK: i32.add $push1=, $0, $pop0{{$}} 181; CHECK: i32.store $discard=, 0($pop1), $pop2{{$}} 182define void @store_i32_with_unfolded_gep_offset(i32* %p) { 183 %s = getelementptr i32, i32* %p, i32 6 184 store i32 0, i32* %s 185 ret void 186} 187 188; Same as above but with store with i64. 189 190; CHECK-LABEL: store_i64_with_folded_offset: 191; CHECK: i64.store $discard=, 24($0), $pop0{{$}} 192define void @store_i64_with_folded_offset(i64* %p) { 193 %q = ptrtoint i64* %p to i32 194 %r = add nuw i32 %q, 24 195 %s = inttoptr i32 %r to i64* 196 store i64 0, i64* %s 197 ret void 198} 199 200; Same as above but with store with i64. 201 202; CHECK-LABEL: store_i64_with_folded_gep_offset: 203; CHECK: i64.store $discard=, 24($0), $pop0{{$}} 204define void @store_i64_with_folded_gep_offset(i64* %p) { 205 %s = getelementptr inbounds i64, i64* %p, i32 3 206 store i64 0, i64* %s 207 ret void 208} 209 210; Same as above but with store with i64. 211 212; CHECK-LABEL: store_i64_with_unfolded_gep_negative_offset: 213; CHECK: i32.const $push0=, -24{{$}} 214; CHECK: i32.add $push1=, $0, $pop0{{$}} 215; CHECK: i64.store $discard=, 0($pop1), $pop2{{$}} 216define void @store_i64_with_unfolded_gep_negative_offset(i64* %p) { 217 %s = getelementptr inbounds i64, i64* %p, i32 -3 218 store i64 0, i64* %s 219 ret void 220} 221 222; Same as above but with store with i64. 223 224; CHECK-LABEL: store_i64_with_unfolded_offset: 225; CHECK: i32.const $push0=, 24{{$}} 226; CHECK: i32.add $push1=, $0, $pop0{{$}} 227; CHECK: i64.store $discard=, 0($pop1), $pop2{{$}} 228define void @store_i64_with_unfolded_offset(i64* %p) { 229 %q = ptrtoint i64* %p to i32 230 %r = add nsw i32 %q, 24 231 %s = inttoptr i32 %r to i64* 232 store i64 0, i64* %s 233 ret void 234} 235 236; Same as above but with store with i64. 237 238; CHECK-LABEL: store_i64_with_unfolded_gep_offset: 239; CHECK: i32.const $push0=, 24{{$}} 240; CHECK: i32.add $push1=, $0, $pop0{{$}} 241; CHECK: i64.store $discard=, 0($pop1), $pop2{{$}} 242define void @store_i64_with_unfolded_gep_offset(i64* %p) { 243 %s = getelementptr i64, i64* %p, i32 3 244 store i64 0, i64* %s 245 ret void 246} 247 248; When loading from a fixed address, materialize a zero. 249 250; CHECK-LABEL: load_i32_from_numeric_address 251; CHECK: i32.const $push0=, 0{{$}} 252; CHECK: i32.load $push1=, 42($pop0){{$}} 253define i32 @load_i32_from_numeric_address() { 254 %s = inttoptr i32 42 to i32* 255 %t = load i32, i32* %s 256 ret i32 %t 257} 258 259; CHECK-LABEL: load_i32_from_global_address 260; CHECK: i32.const $push0=, 0{{$}} 261; CHECK: i32.load $push1=, gv($pop0){{$}} 262@gv = global i32 0 263define i32 @load_i32_from_global_address() { 264 %t = load i32, i32* @gv 265 ret i32 %t 266} 267 268; CHECK-LABEL: store_i32_to_numeric_address: 269; CHECK-NEXT: i32.const $push0=, 0{{$}} 270; CHECK-NEXT: i32.const $push1=, 0{{$}} 271; CHECK-NEXT: i32.store $discard=, 42($pop0), $pop1{{$}} 272define void @store_i32_to_numeric_address() { 273 %s = inttoptr i32 42 to i32* 274 store i32 0, i32* %s 275 ret void 276} 277 278; CHECK-LABEL: store_i32_to_global_address: 279; CHECK: i32.const $push0=, 0{{$}} 280; CHECK: i32.const $push1=, 0{{$}} 281; CHECK: i32.store $discard=, gv($pop0), $pop1{{$}} 282define void @store_i32_to_global_address() { 283 store i32 0, i32* @gv 284 ret void 285} 286 287; Fold an offset into a sign-extending load. 288 289; CHECK-LABEL: load_i8_s_with_folded_offset: 290; CHECK: i32.load8_s $push0=, 24($0){{$}} 291define i32 @load_i8_s_with_folded_offset(i8* %p) { 292 %q = ptrtoint i8* %p to i32 293 %r = add nuw i32 %q, 24 294 %s = inttoptr i32 %r to i8* 295 %t = load i8, i8* %s 296 %u = sext i8 %t to i32 297 ret i32 %u 298} 299 300; Fold a gep offset into a sign-extending load. 301 302; CHECK-LABEL: load_i8_s_with_folded_gep_offset: 303; CHECK: i32.load8_s $push0=, 24($0){{$}} 304define i32 @load_i8_s_with_folded_gep_offset(i8* %p) { 305 %s = getelementptr inbounds i8, i8* %p, i32 24 306 %t = load i8, i8* %s 307 %u = sext i8 %t to i32 308 ret i32 %u 309} 310 311; Fold an offset into a zero-extending load. 312 313; CHECK-LABEL: load_i8_u_with_folded_offset: 314; CHECK: i32.load8_u $push0=, 24($0){{$}} 315define i32 @load_i8_u_with_folded_offset(i8* %p) { 316 %q = ptrtoint i8* %p to i32 317 %r = add nuw i32 %q, 24 318 %s = inttoptr i32 %r to i8* 319 %t = load i8, i8* %s 320 %u = zext i8 %t to i32 321 ret i32 %u 322} 323 324; Fold a gep offset into a zero-extending load. 325 326; CHECK-LABEL: load_i8_u_with_folded_gep_offset: 327; CHECK: i32.load8_u $push0=, 24($0){{$}} 328define i32 @load_i8_u_with_folded_gep_offset(i8* %p) { 329 %s = getelementptr inbounds i8, i8* %p, i32 24 330 %t = load i8, i8* %s 331 %u = zext i8 %t to i32 332 ret i32 %u 333} 334 335; Fold an offset into a truncating store. 336 337; CHECK-LABEL: store_i8_with_folded_offset: 338; CHECK: i32.store8 $discard=, 24($0), $pop0{{$}} 339define void @store_i8_with_folded_offset(i8* %p) { 340 %q = ptrtoint i8* %p to i32 341 %r = add nuw i32 %q, 24 342 %s = inttoptr i32 %r to i8* 343 store i8 0, i8* %s 344 ret void 345} 346 347; Fold a gep offset into a truncating store. 348 349; CHECK-LABEL: store_i8_with_folded_gep_offset: 350; CHECK: i32.store8 $discard=, 24($0), $pop0{{$}} 351define void @store_i8_with_folded_gep_offset(i8* %p) { 352 %s = getelementptr inbounds i8, i8* %p, i32 24 353 store i8 0, i8* %s 354 ret void 355} 356 357; Fold the offsets when lowering aggregate loads and stores. 358 359; CHECK-LABEL: aggregate_load_store: 360; CHECK: i32.load $2=, 0($0){{$}} 361; CHECK: i32.load $3=, 4($0){{$}} 362; CHECK: i32.load $4=, 8($0){{$}} 363; CHECK: i32.load $push0=, 12($0){{$}} 364; CHECK: i32.store $discard=, 12($1), $pop0{{$}} 365; CHECK: i32.store $discard=, 8($1), $4{{$}} 366; CHECK: i32.store $discard=, 4($1), $3{{$}} 367; CHECK: i32.store $discard=, 0($1), $2{{$}} 368define void @aggregate_load_store({i32,i32,i32,i32}* %p, {i32,i32,i32,i32}* %q) { 369 ; volatile so that things stay in order for the tests above 370 %t = load volatile {i32,i32,i32,i32}, {i32, i32,i32,i32}* %p 371 store volatile {i32,i32,i32,i32} %t, {i32, i32,i32,i32}* %q 372 ret void 373} 374 375; Fold the offsets when lowering aggregate return values. 376 377; CHECK-LABEL: aggregate_return: 378; CHECK: i32.const $push0=, 0{{$}} 379; CHECK: i32.store $push1=, 12($0), $pop0{{$}} 380; CHECK: i32.store $push2=, 8($0), $pop1{{$}} 381; CHECK: i32.store $push3=, 4($0), $pop2{{$}} 382; CHECK: i32.store $discard=, 0($0), $pop3{{$}} 383define {i32,i32,i32,i32} @aggregate_return() { 384 ret {i32,i32,i32,i32} zeroinitializer 385} 386