1; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -disable-wasm-fallthrough-return-opt -mattr=+fp16 | FileCheck %s 2 3; Test constant load and store address offsets. 4 5target triple = "wasm32-unknown-unknown" 6 7;===---------------------------------------------------------------------------- 8; Loads: 32-bit 9;===---------------------------------------------------------------------------- 10 11; Basic load. 12 13; CHECK-LABEL: load_i32_no_offset: 14; CHECK: i32.load $push0=, 0($0){{$}} 15; CHECK-NEXT: return $pop0{{$}} 16define i32 @load_i32_no_offset(ptr %p) { 17 %v = load i32, ptr %p 18 ret i32 %v 19} 20 21; With an nuw add, we can fold an offset. 22 23; CHECK-LABEL: load_i32_with_folded_offset: 24; CHECK: i32.load $push0=, 24($0){{$}} 25define i32 @load_i32_with_folded_offset(ptr %p) { 26 %q = ptrtoint ptr %p to i32 27 %r = add nuw i32 %q, 24 28 %s = inttoptr i32 %r to ptr 29 %t = load i32, ptr %s 30 ret i32 %t 31} 32 33; With an inbounds gep, we can fold an offset. 34 35; CHECK-LABEL: load_i32_with_folded_gep_offset: 36; CHECK: i32.load $push0=, 24($0){{$}} 37define i32 @load_i32_with_folded_gep_offset(ptr %p) { 38 %s = getelementptr inbounds i32, ptr %p, i32 6 39 %t = load i32, ptr %s 40 ret i32 %t 41} 42 43; Same for nusw. 44 45; CHECK-LABEL: load_i32_with_folded_gep_offset_nusw: 46; CHECK: i32.load $push0=, 24($0){{$}} 47define i32 @load_i32_with_folded_gep_offset_nusw(ptr %p) { 48 %s = getelementptr nusw i32, ptr %p, i32 6 49 %t = load i32, ptr %s 50 ret i32 %t 51} 52 53; For nuw we don't need the offset to be positive. 54 55; CHECK-LABEL: load_i32_with_folded_gep_offset_nuw: 56; CHECK: i32.load $push0=, -24($0){{$}} 57define i32 @load_i32_with_folded_gep_offset_nuw(ptr %p) { 58 %s = getelementptr nuw i32, ptr %p, i32 -6 59 %t = load i32, ptr %s 60 ret i32 %t 61} 62 63; We can't fold a negative offset though, even with an inbounds gep. 64 65; CHECK-LABEL: load_i32_with_unfolded_gep_negative_offset: 66; CHECK: i32.const $push0=, -24{{$}} 67; CHECK: i32.add $push1=, $0, $pop0{{$}} 68; CHECK: i32.load $push2=, 0($pop1){{$}} 69define i32 @load_i32_with_unfolded_gep_negative_offset(ptr %p) { 70 %s = getelementptr inbounds i32, ptr %p, i32 -6 71 %t = load i32, ptr %s 72 ret i32 %t 73} 74 75; Without nuw, and even with nsw, we can't fold an offset. 76 77; CHECK-LABEL: load_i32_with_unfolded_offset: 78; CHECK: i32.const $push0=, 24{{$}} 79; CHECK: i32.add $push1=, $0, $pop0{{$}} 80; CHECK: i32.load $push2=, 0($pop1){{$}} 81define i32 @load_i32_with_unfolded_offset(ptr %p) { 82 %q = ptrtoint ptr %p to i32 83 %r = add nsw i32 %q, 24 84 %s = inttoptr i32 %r to ptr 85 %t = load i32, ptr %s 86 ret i32 %t 87} 88 89; Without inbounds, we can't fold a gep offset. 90 91; CHECK-LABEL: load_i32_with_unfolded_gep_offset: 92; CHECK: i32.const $push0=, 24{{$}} 93; CHECK: i32.add $push1=, $0, $pop0{{$}} 94; CHECK: i32.load $push2=, 0($pop1){{$}} 95define i32 @load_i32_with_unfolded_gep_offset(ptr %p) { 96 %s = getelementptr i32, ptr %p, i32 6 97 %t = load i32, ptr %s 98 ret i32 %t 99} 100 101; When loading from a fixed address, materialize a zero. 102 103; CHECK-LABEL: load_i32_from_numeric_address 104; CHECK: i32.const $push0=, 0{{$}} 105; CHECK: i32.load $push1=, 42($pop0){{$}} 106define i32 @load_i32_from_numeric_address() { 107 %s = inttoptr i32 42 to ptr 108 %t = load i32, ptr %s 109 ret i32 %t 110} 111 112; CHECK-LABEL: load_i32_from_global_address 113; CHECK: i32.const $push0=, 0{{$}} 114; CHECK: i32.load $push1=, gv($pop0){{$}} 115@gv = global i32 0 116define i32 @load_i32_from_global_address() { 117 %t = load i32, ptr @gv 118 ret i32 %t 119} 120 121;===---------------------------------------------------------------------------- 122; Loads: 64-bit 123;===---------------------------------------------------------------------------- 124 125; Basic load. 126 127; CHECK-LABEL: load_i64_no_offset: 128; CHECK: i64.load $push0=, 0($0){{$}} 129; CHECK-NEXT: return $pop0{{$}} 130define i64 @load_i64_no_offset(ptr %p) { 131 %v = load i64, ptr %p 132 ret i64 %v 133} 134 135; With an nuw add, we can fold an offset. 136 137; CHECK-LABEL: load_i64_with_folded_offset: 138; CHECK: i64.load $push0=, 24($0){{$}} 139define i64 @load_i64_with_folded_offset(ptr %p) { 140 %q = ptrtoint ptr %p to i32 141 %r = add nuw i32 %q, 24 142 %s = inttoptr i32 %r to ptr 143 %t = load i64, ptr %s 144 ret i64 %t 145} 146 147; With an inbounds gep, we can fold an offset. 148 149; CHECK-LABEL: load_i64_with_folded_gep_offset: 150; CHECK: i64.load $push0=, 24($0){{$}} 151define i64 @load_i64_with_folded_gep_offset(ptr %p) { 152 %s = getelementptr inbounds i64, ptr %p, i32 3 153 %t = load i64, ptr %s 154 ret i64 %t 155} 156 157; We can't fold a negative offset though, even with an inbounds gep. 158 159; CHECK-LABEL: load_i64_with_unfolded_gep_negative_offset: 160; CHECK: i32.const $push0=, -24{{$}} 161; CHECK: i32.add $push1=, $0, $pop0{{$}} 162; CHECK: i64.load $push2=, 0($pop1){{$}} 163define i64 @load_i64_with_unfolded_gep_negative_offset(ptr %p) { 164 %s = getelementptr inbounds i64, ptr %p, i32 -3 165 %t = load i64, ptr %s 166 ret i64 %t 167} 168 169; Without nuw, and even with nsw, we can't fold an offset. 170 171; CHECK-LABEL: load_i64_with_unfolded_offset: 172; CHECK: i32.const $push0=, 24{{$}} 173; CHECK: i32.add $push1=, $0, $pop0{{$}} 174; CHECK: i64.load $push2=, 0($pop1){{$}} 175define i64 @load_i64_with_unfolded_offset(ptr %p) { 176 %q = ptrtoint ptr %p to i32 177 %r = add nsw i32 %q, 24 178 %s = inttoptr i32 %r to ptr 179 %t = load i64, ptr %s 180 ret i64 %t 181} 182 183; Without inbounds, we can't fold a gep offset. 184 185; CHECK-LABEL: load_i64_with_unfolded_gep_offset: 186; CHECK: i32.const $push0=, 24{{$}} 187; CHECK: i32.add $push1=, $0, $pop0{{$}} 188; CHECK: i64.load $push2=, 0($pop1){{$}} 189define i64 @load_i64_with_unfolded_gep_offset(ptr %p) { 190 %s = getelementptr i64, ptr %p, i32 3 191 %t = load i64, ptr %s 192 ret i64 %t 193} 194 195;===---------------------------------------------------------------------------- 196; Stores: 32-bit 197;===---------------------------------------------------------------------------- 198 199; Basic store. 200 201; CHECK-LABEL: store_i32_no_offset: 202; CHECK-NEXT: .functype store_i32_no_offset (i32, i32) -> (){{$}} 203; CHECK-NEXT: i32.store 0($0), $1{{$}} 204; CHECK-NEXT: return{{$}} 205define void @store_i32_no_offset(ptr %p, i32 %v) { 206 store i32 %v, ptr %p 207 ret void 208} 209 210; With an nuw add, we can fold an offset. 211 212; CHECK-LABEL: store_i32_with_folded_offset: 213; CHECK: i32.store 24($0), $pop0{{$}} 214define void @store_i32_with_folded_offset(ptr %p) { 215 %q = ptrtoint ptr %p to i32 216 %r = add nuw i32 %q, 24 217 %s = inttoptr i32 %r to ptr 218 store i32 0, ptr %s 219 ret void 220} 221 222; With an inbounds gep, we can fold an offset. 223 224; CHECK-LABEL: store_i32_with_folded_gep_offset: 225; CHECK: i32.store 24($0), $pop0{{$}} 226define void @store_i32_with_folded_gep_offset(ptr %p) { 227 %s = getelementptr inbounds i32, ptr %p, i32 6 228 store i32 0, ptr %s 229 ret void 230} 231 232; We can't fold a negative offset though, even with an inbounds gep. 233 234; CHECK-LABEL: store_i32_with_unfolded_gep_negative_offset: 235; CHECK: i32.const $push0=, -24{{$}} 236; CHECK: i32.add $push1=, $0, $pop0{{$}} 237; CHECK: i32.store 0($pop1), $pop2{{$}} 238define void @store_i32_with_unfolded_gep_negative_offset(ptr %p) { 239 %s = getelementptr inbounds i32, ptr %p, i32 -6 240 store i32 0, ptr %s 241 ret void 242} 243 244; Without nuw, and even with nsw, we can't fold an offset. 245 246; CHECK-LABEL: store_i32_with_unfolded_offset: 247; CHECK: i32.const $push0=, 24{{$}} 248; CHECK: i32.add $push1=, $0, $pop0{{$}} 249; CHECK: i32.store 0($pop1), $pop2{{$}} 250define void @store_i32_with_unfolded_offset(ptr %p) { 251 %q = ptrtoint ptr %p to i32 252 %r = add nsw i32 %q, 24 253 %s = inttoptr i32 %r to ptr 254 store i32 0, ptr %s 255 ret void 256} 257 258; Without inbounds, we can't fold a gep offset. 259 260; CHECK-LABEL: store_i32_with_unfolded_gep_offset: 261; CHECK: i32.const $push0=, 24{{$}} 262; CHECK: i32.add $push1=, $0, $pop0{{$}} 263; CHECK: i32.store 0($pop1), $pop2{{$}} 264define void @store_i32_with_unfolded_gep_offset(ptr %p) { 265 %s = getelementptr i32, ptr %p, i32 6 266 store i32 0, ptr %s 267 ret void 268} 269 270; When storing from a fixed address, materialize a zero. 271 272; CHECK-LABEL: store_i32_to_numeric_address: 273; CHECK: i32.const $push0=, 0{{$}} 274; CHECK-NEXT: i32.const $push1=, 0{{$}} 275; CHECK-NEXT: i32.store 42($pop0), $pop1{{$}} 276define void @store_i32_to_numeric_address() { 277 %s = inttoptr i32 42 to ptr 278 store i32 0, ptr %s 279 ret void 280} 281 282; CHECK-LABEL: store_i32_to_global_address: 283; CHECK: i32.const $push0=, 0{{$}} 284; CHECK: i32.const $push1=, 0{{$}} 285; CHECK: i32.store gv($pop0), $pop1{{$}} 286define void @store_i32_to_global_address() { 287 store i32 0, ptr @gv 288 ret void 289} 290 291;===---------------------------------------------------------------------------- 292; Stores: 64-bit 293;===---------------------------------------------------------------------------- 294 295; Basic store. 296 297; CHECK-LABEL: store_i64_with_folded_offset: 298; CHECK: i64.store 24($0), $pop0{{$}} 299define void @store_i64_with_folded_offset(ptr %p) { 300 %q = ptrtoint ptr %p to i32 301 %r = add nuw i32 %q, 24 302 %s = inttoptr i32 %r to ptr 303 store i64 0, ptr %s 304 ret void 305} 306 307; With an nuw add, we can fold an offset. 308 309; CHECK-LABEL: store_i64_with_folded_gep_offset: 310; CHECK: i64.store 24($0), $pop0{{$}} 311define void @store_i64_with_folded_gep_offset(ptr %p) { 312 %s = getelementptr inbounds i64, ptr %p, i32 3 313 store i64 0, ptr %s 314 ret void 315} 316 317; With an inbounds gep, we can fold an offset. 318 319; CHECK-LABEL: store_i64_with_unfolded_gep_negative_offset: 320; CHECK: i32.const $push0=, -24{{$}} 321; CHECK: i32.add $push1=, $0, $pop0{{$}} 322; CHECK: i64.store 0($pop1), $pop2{{$}} 323define void @store_i64_with_unfolded_gep_negative_offset(ptr %p) { 324 %s = getelementptr inbounds i64, ptr %p, i32 -3 325 store i64 0, ptr %s 326 ret void 327} 328 329; We can't fold a negative offset though, even with an inbounds gep. 330 331; CHECK-LABEL: store_i64_with_unfolded_offset: 332; CHECK: i32.const $push0=, 24{{$}} 333; CHECK: i32.add $push1=, $0, $pop0{{$}} 334; CHECK: i64.store 0($pop1), $pop2{{$}} 335define void @store_i64_with_unfolded_offset(ptr %p) { 336 %q = ptrtoint ptr %p to i32 337 %r = add nsw i32 %q, 24 338 %s = inttoptr i32 %r to ptr 339 store i64 0, ptr %s 340 ret void 341} 342 343; Without nuw, and even with nsw, we can't fold an offset. 344 345; CHECK-LABEL: store_i64_with_unfolded_gep_offset: 346; CHECK: i32.const $push0=, 24{{$}} 347; CHECK: i32.add $push1=, $0, $pop0{{$}} 348; CHECK: i64.store 0($pop1), $pop2{{$}} 349define void @store_i64_with_unfolded_gep_offset(ptr %p) { 350 %s = getelementptr i64, ptr %p, i32 3 351 store i64 0, ptr %s 352 ret void 353} 354 355; Without inbounds, we can't fold a gep offset. 356 357; CHECK-LABEL: store_i32_with_folded_or_offset: 358; CHECK: i32.store8 2($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}} 359define void @store_i32_with_folded_or_offset(i32 %x) { 360 %and = and i32 %x, -4 361 %t0 = inttoptr i32 %and to ptr 362 %arrayidx = getelementptr inbounds i8, ptr %t0, i32 2 363 store i8 0, ptr %arrayidx, align 1 364 ret void 365} 366 367;===---------------------------------------------------------------------------- 368; Sign-extending loads 369;===---------------------------------------------------------------------------- 370 371; Fold an offset into a sign-extending load. 372 373; CHECK-LABEL: load_i8_i32_s_with_folded_offset: 374; CHECK: i32.load8_s $push0=, 24($0){{$}} 375define i32 @load_i8_i32_s_with_folded_offset(ptr %p) { 376 %q = ptrtoint ptr %p to i32 377 %r = add nuw i32 %q, 24 378 %s = inttoptr i32 %r to ptr 379 %t = load i8, ptr %s 380 %u = sext i8 %t to i32 381 ret i32 %u 382} 383 384; CHECK-LABEL: load_i32_i64_s_with_folded_offset: 385; CHECK: i64.load32_s $push0=, 24($0){{$}} 386define i64 @load_i32_i64_s_with_folded_offset(ptr %p) { 387 %q = ptrtoint ptr %p to i32 388 %r = add nuw i32 %q, 24 389 %s = inttoptr i32 %r to ptr 390 %t = load i32, ptr %s 391 %u = sext i32 %t to i64 392 ret i64 %u 393} 394 395; Fold a gep offset into a sign-extending load. 396 397; CHECK-LABEL: load_i8_i32_s_with_folded_gep_offset: 398; CHECK: i32.load8_s $push0=, 24($0){{$}} 399define i32 @load_i8_i32_s_with_folded_gep_offset(ptr %p) { 400 %s = getelementptr inbounds i8, ptr %p, i32 24 401 %t = load i8, ptr %s 402 %u = sext i8 %t to i32 403 ret i32 %u 404} 405 406; CHECK-LABEL: load_i16_i32_s_with_folded_gep_offset: 407; CHECK: i32.load16_s $push0=, 48($0){{$}} 408define i32 @load_i16_i32_s_with_folded_gep_offset(ptr %p) { 409 %s = getelementptr inbounds i16, ptr %p, i32 24 410 %t = load i16, ptr %s 411 %u = sext i16 %t to i32 412 ret i32 %u 413} 414 415; CHECK-LABEL: load_i16_i64_s_with_folded_gep_offset: 416; CHECK: i64.load16_s $push0=, 48($0){{$}} 417define i64 @load_i16_i64_s_with_folded_gep_offset(ptr %p) { 418 %s = getelementptr inbounds i16, ptr %p, i32 24 419 %t = load i16, ptr %s 420 %u = sext i16 %t to i64 421 ret i64 %u 422} 423 424; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as 425; an 'add' if the or'ed bits are known to be zero. 426 427; CHECK-LABEL: load_i8_i32_s_with_folded_or_offset: 428; CHECK: i32.load8_s $push{{[0-9]+}}=, 2($pop{{[0-9]+}}){{$}} 429define i32 @load_i8_i32_s_with_folded_or_offset(i32 %x) { 430 %and = and i32 %x, -4 431 %t0 = inttoptr i32 %and to ptr 432 %arrayidx = getelementptr inbounds i8, ptr %t0, i32 2 433 %t1 = load i8, ptr %arrayidx 434 %conv = sext i8 %t1 to i32 435 ret i32 %conv 436} 437 438; CHECK-LABEL: load_i8_i64_s_with_folded_or_offset: 439; CHECK: i64.load8_s $push{{[0-9]+}}=, 2($pop{{[0-9]+}}){{$}} 440define i64 @load_i8_i64_s_with_folded_or_offset(i32 %x) { 441 %and = and i32 %x, -4 442 %t0 = inttoptr i32 %and to ptr 443 %arrayidx = getelementptr inbounds i8, ptr %t0, i32 2 444 %t1 = load i8, ptr %arrayidx 445 %conv = sext i8 %t1 to i64 446 ret i64 %conv 447} 448 449; When loading from a fixed address, materialize a zero. 450 451; CHECK-LABEL: load_i16_i32_s_from_numeric_address 452; CHECK: i32.const $push0=, 0{{$}} 453; CHECK: i32.load16_s $push1=, 42($pop0){{$}} 454define i32 @load_i16_i32_s_from_numeric_address() { 455 %s = inttoptr i32 42 to ptr 456 %t = load i16, ptr %s 457 %u = sext i16 %t to i32 458 ret i32 %u 459} 460 461; CHECK-LABEL: load_i8_i32_s_from_global_address 462; CHECK: i32.const $push0=, 0{{$}} 463; CHECK: i32.load8_s $push1=, gv8($pop0){{$}} 464@gv8 = global i8 0 465define i32 @load_i8_i32_s_from_global_address() { 466 %t = load i8, ptr @gv8 467 %u = sext i8 %t to i32 468 ret i32 %u 469} 470 471;===---------------------------------------------------------------------------- 472; Zero-extending loads 473;===---------------------------------------------------------------------------- 474 475; Fold an offset into a zero-extending load. 476 477; CHECK-LABEL: load_i8_i32_z_with_folded_offset: 478; CHECK: i32.load8_u $push0=, 24($0){{$}} 479define i32 @load_i8_i32_z_with_folded_offset(ptr %p) { 480 %q = ptrtoint ptr %p to i32 481 %r = add nuw i32 %q, 24 482 %s = inttoptr i32 %r to ptr 483 %t = load i8, ptr %s 484 %u = zext i8 %t to i32 485 ret i32 %u 486} 487 488; CHECK-LABEL: load_i32_i64_z_with_folded_offset: 489; CHECK: i64.load32_u $push0=, 24($0){{$}} 490define i64 @load_i32_i64_z_with_folded_offset(ptr %p) { 491 %q = ptrtoint ptr %p to i32 492 %r = add nuw i32 %q, 24 493 %s = inttoptr i32 %r to ptr 494 %t = load i32, ptr %s 495 %u = zext i32 %t to i64 496 ret i64 %u 497} 498 499; Fold a gep offset into a zero-extending load. 500 501; CHECK-LABEL: load_i8_i32_z_with_folded_gep_offset: 502; CHECK: i32.load8_u $push0=, 24($0){{$}} 503define i32 @load_i8_i32_z_with_folded_gep_offset(ptr %p) { 504 %s = getelementptr inbounds i8, ptr %p, i32 24 505 %t = load i8, ptr %s 506 %u = zext i8 %t to i32 507 ret i32 %u 508} 509 510; CHECK-LABEL: load_i16_i32_z_with_folded_gep_offset: 511; CHECK: i32.load16_u $push0=, 48($0){{$}} 512define i32 @load_i16_i32_z_with_folded_gep_offset(ptr %p) { 513 %s = getelementptr inbounds i16, ptr %p, i32 24 514 %t = load i16, ptr %s 515 %u = zext i16 %t to i32 516 ret i32 %u 517} 518 519; CHECK-LABEL: load_i16_i64_z_with_folded_gep_offset: 520; CHECK: i64.load16_u $push0=, 48($0){{$}} 521define i64 @load_i16_i64_z_with_folded_gep_offset(ptr %p) { 522 %s = getelementptr inbounds i16, ptr %p, i64 24 523 %t = load i16, ptr %s 524 %u = zext i16 %t to i64 525 ret i64 %u 526} 527 528; When loading from a fixed address, materialize a zero. 529 530; CHECK-LABEL: load_i16_i32_z_from_numeric_address 531; CHECK: i32.const $push0=, 0{{$}} 532; CHECK: i32.load16_u $push1=, 42($pop0){{$}} 533define i32 @load_i16_i32_z_from_numeric_address() { 534 %s = inttoptr i32 42 to ptr 535 %t = load i16, ptr %s 536 %u = zext i16 %t to i32 537 ret i32 %u 538} 539 540; CHECK-LABEL: load_i8_i32_z_from_global_address 541; CHECK: i32.const $push0=, 0{{$}} 542; CHECK: i32.load8_u $push1=, gv8($pop0){{$}} 543define i32 @load_i8_i32_z_from_global_address() { 544 %t = load i8, ptr @gv8 545 %u = zext i8 %t to i32 546 ret i32 %u 547} 548 549; i8 return value should test anyext loads 550; CHECK-LABEL: load_i8_i32_retvalue: 551; CHECK: i32.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}} 552; CHECK-NEXT: return $pop[[NUM]]{{$}} 553define i8 @load_i8_i32_retvalue(ptr %p) { 554 %v = load i8, ptr %p 555 ret i8 %v 556} 557 558;===---------------------------------------------------------------------------- 559; Truncating stores 560;===---------------------------------------------------------------------------- 561 562; Fold an offset into a truncating store. 563 564; CHECK-LABEL: store_i8_i32_with_folded_offset: 565; CHECK: i32.store8 24($0), $1{{$}} 566define void @store_i8_i32_with_folded_offset(ptr %p, i32 %v) { 567 %q = ptrtoint ptr %p to i32 568 %r = add nuw i32 %q, 24 569 %s = inttoptr i32 %r to ptr 570 %t = trunc i32 %v to i8 571 store i8 %t, ptr %s 572 ret void 573} 574 575; CHECK-LABEL: store_i32_i64_with_folded_offset: 576; CHECK: i64.store32 24($0), $1{{$}} 577define void @store_i32_i64_with_folded_offset(ptr %p, i64 %v) { 578 %q = ptrtoint ptr %p to i32 579 %r = add nuw i32 %q, 24 580 %s = inttoptr i32 %r to ptr 581 %t = trunc i64 %v to i32 582 store i32 %t, ptr %s 583 ret void 584} 585 586; Fold a gep offset into a truncating store. 587 588; CHECK-LABEL: store_i8_i32_with_folded_gep_offset: 589; CHECK: i32.store8 24($0), $1{{$}} 590define void @store_i8_i32_with_folded_gep_offset(ptr %p, i32 %v) { 591 %s = getelementptr inbounds i8, ptr %p, i32 24 592 %t = trunc i32 %v to i8 593 store i8 %t, ptr %s 594 ret void 595} 596 597; CHECK-LABEL: store_i16_i32_with_folded_gep_offset: 598; CHECK: i32.store16 48($0), $1{{$}} 599define void @store_i16_i32_with_folded_gep_offset(ptr %p, i32 %v) { 600 %s = getelementptr inbounds i16, ptr %p, i32 24 601 %t = trunc i32 %v to i16 602 store i16 %t, ptr %s 603 ret void 604} 605 606; CHECK-LABEL: store_i16_i64_with_folded_gep_offset: 607; CHECK: i64.store16 48($0), $1{{$}} 608define void @store_i16_i64_with_folded_gep_offset(ptr %p, i64 %v) { 609 %s = getelementptr inbounds i16, ptr %p, i64 24 610 %t = trunc i64 %v to i16 611 store i16 %t, ptr %s 612 ret void 613} 614 615; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as 616; an 'add' if the or'ed bits are known to be zero. 617 618; CHECK-LABEL: store_i8_i32_with_folded_or_offset: 619; CHECK: i32.store8 2($pop{{[0-9]+}}), $1{{$}} 620define void @store_i8_i32_with_folded_or_offset(i32 %x, i32 %v) { 621 %and = and i32 %x, -4 622 %p = inttoptr i32 %and to ptr 623 %arrayidx = getelementptr inbounds i8, ptr %p, i32 2 624 %t = trunc i32 %v to i8 625 store i8 %t, ptr %arrayidx 626 ret void 627} 628 629; CHECK-LABEL: store_i8_i64_with_folded_or_offset: 630; CHECK: i64.store8 2($pop{{[0-9]+}}), $1{{$}} 631define void @store_i8_i64_with_folded_or_offset(i32 %x, i64 %v) { 632 %and = and i32 %x, -4 633 %p = inttoptr i32 %and to ptr 634 %arrayidx = getelementptr inbounds i8, ptr %p, i32 2 635 %t = trunc i64 %v to i8 636 store i8 %t, ptr %arrayidx 637 ret void 638} 639 640;===---------------------------------------------------------------------------- 641; Aggregate values 642;===---------------------------------------------------------------------------- 643 644; Fold the offsets when lowering aggregate loads and stores. 645 646; CHECK-LABEL: aggregate_load_store: 647; CHECK: i32.load $2=, 0($0){{$}} 648; CHECK: i32.load $3=, 4($0){{$}} 649; CHECK: i32.load $4=, 8($0){{$}} 650; CHECK: i32.load $push0=, 12($0){{$}} 651; CHECK: i32.store 12($1), $pop0{{$}} 652; CHECK: i32.store 8($1), $4{{$}} 653; CHECK: i32.store 4($1), $3{{$}} 654; CHECK: i32.store 0($1), $2{{$}} 655define void @aggregate_load_store(ptr %p, ptr %q) { 656 ; volatile so that things stay in order for the tests above 657 %t = load volatile {i32,i32,i32,i32}, ptr %p 658 store volatile {i32,i32,i32,i32} %t, ptr %q 659 ret void 660} 661 662; Fold the offsets when lowering aggregate return values. The stores get 663; merged into i64 stores. 664 665; CHECK-LABEL: aggregate_return: 666; CHECK: i64.const $push[[L0:[0-9]+]]=, 0{{$}} 667; CHECK: i64.store 8($0), $pop[[L0]]{{$}} 668; CHECK: i64.const $push[[L1:[0-9]+]]=, 0{{$}} 669; CHECK: i64.store 0($0), $pop[[L1]]{{$}} 670define {i32,i32,i32,i32} @aggregate_return() { 671 ret {i32,i32,i32,i32} zeroinitializer 672} 673 674; Fold the offsets when lowering aggregate return values. The stores are not 675; merged. 676 677; CHECK-LABEL: aggregate_return_without_merge: 678; CHECK: i32.const $push[[L0:[0-9]+]]=, 0{{$}} 679; CHECK: i32.store8 14($0), $pop[[L0]]{{$}} 680; CHECK: i32.const $push[[L1:[0-9]+]]=, 0{{$}} 681; CHECK: i32.store16 12($0), $pop[[L1]]{{$}} 682; CHECK: i32.const $push[[L2:[0-9]+]]=, 0{{$}} 683; CHECK: i32.store 8($0), $pop[[L2]]{{$}} 684; CHECK: i64.const $push[[L3:[0-9]+]]=, 0{{$}} 685; CHECK: i64.store 0($0), $pop[[L3]]{{$}} 686define {i64,i32,i16,i8} @aggregate_return_without_merge() { 687 ret {i64,i32,i16,i8} zeroinitializer 688} 689 690;===---------------------------------------------------------------------------- 691; Loads: Half Precision 692;===---------------------------------------------------------------------------- 693 694; Fold an offset into a zero-extending load. 695 696; CHECK-LABEL: load_f16_f32_with_folded_offset: 697; CHECK: f32.load_f16 $push0=, 24($0){{$}} 698define float @load_f16_f32_with_folded_offset(ptr %p) { 699 %q = ptrtoint ptr %p to i32 700 %r = add nuw i32 %q, 24 701 %s = inttoptr i32 %r to ptr 702 %t = call float @llvm.wasm.loadf16.f32(ptr %s) 703 ret float %t 704} 705 706; Fold a gep offset into a zero-extending load. 707 708; CHECK-LABEL: load_f16_f32_with_folded_gep_offset: 709; CHECK: f32.load_f16 $push0=, 24($0){{$}} 710define float @load_f16_f32_with_folded_gep_offset(ptr %p) { 711 %s = getelementptr inbounds i8, ptr %p, i32 24 712 %t = call float @llvm.wasm.loadf16.f32(ptr %s) 713 ret float %t 714} 715 716;===---------------------------------------------------------------------------- 717; Stores: Half Precision 718;===---------------------------------------------------------------------------- 719 720; Basic store. 721 722; CHECK-LABEL: store_f16_f32_no_offset: 723; CHECK-NEXT: .functype store_f16_f32_no_offset (i32, f32) -> (){{$}} 724; CHECK-NEXT: f32.store_f16 0($0), $1{{$}} 725; CHECK-NEXT: return{{$}} 726define void @store_f16_f32_no_offset(ptr %p, float %v) { 727 call void @llvm.wasm.storef16.f32(float %v, ptr %p) 728 ret void 729} 730 731; Storing to a fixed address. 732 733; CHECK-LABEL: store_f16_f32_to_numeric_address: 734; CHECK: i32.const $push1=, 0{{$}} 735; CHECK-NEXT: f32.const $push0=, 0x0p0{{$}} 736; CHECK-NEXT: f32.store_f16 42($pop1), $pop0{{$}} 737define void @store_f16_f32_to_numeric_address() { 738 %s = inttoptr i32 42 to ptr 739 call void @llvm.wasm.storef16.f32(float 0.0, ptr %s) 740 ret void 741} 742