1; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt 2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=+atomics,+sign-ext | FileCheck %s 3 4; Test that atomic loads are assembled properly. 5 6target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 7target triple = "wasm32-unknown-unknown" 8 9;===---------------------------------------------------------------------------- 10; Atomic loads: 32-bit 11;===---------------------------------------------------------------------------- 12 13; Basic load. 14 15; CHECK-LABEL: load_i32_no_offset: 16; CHECK: i32.atomic.load $push0=, 0($0){{$}} 17; CHECK-NEXT: return $pop0{{$}} 18define i32 @load_i32_no_offset(i32 *%p) { 19 %v = load atomic i32, i32* %p seq_cst, align 4 20 ret i32 %v 21} 22 23; With an nuw add, we can fold an offset. 24 25; CHECK-LABEL: load_i32_with_folded_offset: 26; CHECK: i32.atomic.load $push0=, 24($0){{$}} 27define i32 @load_i32_with_folded_offset(i32* %p) { 28 %q = ptrtoint i32* %p to i32 29 %r = add nuw i32 %q, 24 30 %s = inttoptr i32 %r to i32* 31 %t = load atomic i32, i32* %s seq_cst, align 4 32 ret i32 %t 33} 34 35; With an inbounds gep, we can fold an offset. 36 37; CHECK-LABEL: load_i32_with_folded_gep_offset: 38; CHECK: i32.atomic.load $push0=, 24($0){{$}} 39define i32 @load_i32_with_folded_gep_offset(i32* %p) { 40 %s = getelementptr inbounds i32, i32* %p, i32 6 41 %t = load atomic i32, i32* %s seq_cst, align 4 42 ret i32 %t 43} 44 45; We can't fold a negative offset though, even with an inbounds gep. 46 47; CHECK-LABEL: load_i32_with_unfolded_gep_negative_offset: 48; CHECK: i32.const $push0=, -24{{$}} 49; CHECK: i32.add $push1=, $0, $pop0{{$}} 50; CHECK: i32.atomic.load $push2=, 0($pop1){{$}} 51define i32 @load_i32_with_unfolded_gep_negative_offset(i32* %p) { 52 %s = getelementptr inbounds i32, i32* %p, i32 -6 53 %t = load atomic i32, i32* %s seq_cst, align 4 54 ret i32 %t 55} 56 57; Without nuw, and even with nsw, we can't fold an offset. 58 59; CHECK-LABEL: load_i32_with_unfolded_offset: 60; CHECK: i32.const $push0=, 24{{$}} 61; CHECK: i32.add $push1=, $0, $pop0{{$}} 62; CHECK: i32.atomic.load $push2=, 0($pop1){{$}} 63define i32 @load_i32_with_unfolded_offset(i32* %p) { 64 %q = ptrtoint i32* %p to i32 65 %r = add nsw i32 %q, 24 66 %s = inttoptr i32 %r to i32* 67 %t = load atomic i32, i32* %s seq_cst, align 4 68 ret i32 %t 69} 70 71; Without inbounds, we can't fold a gep offset. 72 73; CHECK-LABEL: load_i32_with_unfolded_gep_offset: 74; CHECK: i32.const $push0=, 24{{$}} 75; CHECK: i32.add $push1=, $0, $pop0{{$}} 76; CHECK: i32.atomic.load $push2=, 0($pop1){{$}} 77define i32 @load_i32_with_unfolded_gep_offset(i32* %p) { 78 %s = getelementptr i32, i32* %p, i32 6 79 %t = load atomic i32, i32* %s seq_cst, align 4 80 ret i32 %t 81} 82 83; When loading from a fixed address, materialize a zero. 84 85; CHECK-LABEL: load_i32_from_numeric_address 86; CHECK: i32.const $push0=, 0{{$}} 87; CHECK: i32.atomic.load $push1=, 42($pop0){{$}} 88define i32 @load_i32_from_numeric_address() { 89 %s = inttoptr i32 42 to i32* 90 %t = load atomic i32, i32* %s seq_cst, align 4 91 ret i32 %t 92} 93 94; CHECK-LABEL: load_i32_from_global_address 95; CHECK: i32.const $push0=, 0{{$}} 96; CHECK: i32.atomic.load $push1=, gv($pop0){{$}} 97@gv = global i32 0 98define i32 @load_i32_from_global_address() { 99 %t = load atomic i32, i32* @gv seq_cst, align 4 100 ret i32 %t 101} 102 103;===---------------------------------------------------------------------------- 104; Atomic loads: 64-bit 105;===---------------------------------------------------------------------------- 106 107; Basic load. 108 109; CHECK-LABEL: load_i64_no_offset: 110; CHECK: i64.atomic.load $push0=, 0($0){{$}} 111; CHECK-NEXT: return $pop0{{$}} 112define i64 @load_i64_no_offset(i64 *%p) { 113 %v = load atomic i64, i64* %p seq_cst, align 8 114 ret i64 %v 115} 116 117; With an nuw add, we can fold an offset. 118 119; CHECK-LABEL: load_i64_with_folded_offset: 120; CHECK: i64.atomic.load $push0=, 24($0){{$}} 121define i64 @load_i64_with_folded_offset(i64* %p) { 122 %q = ptrtoint i64* %p to i32 123 %r = add nuw i32 %q, 24 124 %s = inttoptr i32 %r to i64* 125 %t = load atomic i64, i64* %s seq_cst, align 8 126 ret i64 %t 127} 128 129; With an inbounds gep, we can fold an offset. 130 131; CHECK-LABEL: load_i64_with_folded_gep_offset: 132; CHECK: i64.atomic.load $push0=, 24($0){{$}} 133define i64 @load_i64_with_folded_gep_offset(i64* %p) { 134 %s = getelementptr inbounds i64, i64* %p, i32 3 135 %t = load atomic i64, i64* %s seq_cst, align 8 136 ret i64 %t 137} 138 139; We can't fold a negative offset though, even with an inbounds gep. 140 141; CHECK-LABEL: load_i64_with_unfolded_gep_negative_offset: 142; CHECK: i32.const $push0=, -24{{$}} 143; CHECK: i32.add $push1=, $0, $pop0{{$}} 144; CHECK: i64.atomic.load $push2=, 0($pop1){{$}} 145define i64 @load_i64_with_unfolded_gep_negative_offset(i64* %p) { 146 %s = getelementptr inbounds i64, i64* %p, i32 -3 147 %t = load atomic i64, i64* %s seq_cst, align 8 148 ret i64 %t 149} 150 151; Without nuw, and even with nsw, we can't fold an offset. 152 153; CHECK-LABEL: load_i64_with_unfolded_offset: 154; CHECK: i32.const $push0=, 24{{$}} 155; CHECK: i32.add $push1=, $0, $pop0{{$}} 156; CHECK: i64.atomic.load $push2=, 0($pop1){{$}} 157define i64 @load_i64_with_unfolded_offset(i64* %p) { 158 %q = ptrtoint i64* %p to i32 159 %r = add nsw i32 %q, 24 160 %s = inttoptr i32 %r to i64* 161 %t = load atomic i64, i64* %s seq_cst, align 8 162 ret i64 %t 163} 164 165; Without inbounds, we can't fold a gep offset. 166 167; CHECK-LABEL: load_i64_with_unfolded_gep_offset: 168; CHECK: i32.const $push0=, 24{{$}} 169; CHECK: i32.add $push1=, $0, $pop0{{$}} 170; CHECK: i64.atomic.load $push2=, 0($pop1){{$}} 171define i64 @load_i64_with_unfolded_gep_offset(i64* %p) { 172 %s = getelementptr i64, i64* %p, i32 3 173 %t = load atomic i64, i64* %s seq_cst, align 8 174 ret i64 %t 175} 176 177;===---------------------------------------------------------------------------- 178; Atomic stores: 32-bit 179;===---------------------------------------------------------------------------- 180 181; Basic store. 182 183; CHECK-LABEL: store_i32_no_offset: 184; CHECK-NEXT: .param i32, i32{{$}} 185; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}} 186; CHECK-NEXT: return{{$}} 187define void @store_i32_no_offset(i32 *%p, i32 %v) { 188 store atomic i32 %v, i32* %p seq_cst, align 4 189 ret void 190} 191 192; With an nuw add, we can fold an offset. 193 194; CHECK-LABEL: store_i32_with_folded_offset: 195; CHECK: i32.atomic.store 24($0), $pop0{{$}} 196define void @store_i32_with_folded_offset(i32* %p) { 197 %q = ptrtoint i32* %p to i32 198 %r = add nuw i32 %q, 24 199 %s = inttoptr i32 %r to i32* 200 store atomic i32 0, i32* %s seq_cst, align 4 201 ret void 202} 203 204; With an inbounds gep, we can fold an offset. 205 206; CHECK-LABEL: store_i32_with_folded_gep_offset: 207; CHECK: i32.atomic.store 24($0), $pop0{{$}} 208define void @store_i32_with_folded_gep_offset(i32* %p) { 209 %s = getelementptr inbounds i32, i32* %p, i32 6 210 store atomic i32 0, i32* %s seq_cst, align 4 211 ret void 212} 213 214; We can't fold a negative offset though, even with an inbounds gep. 215 216; CHECK-LABEL: store_i32_with_unfolded_gep_negative_offset: 217; CHECK: i32.const $push0=, -24{{$}} 218; CHECK: i32.add $push1=, $0, $pop0{{$}} 219; CHECK: i32.atomic.store 0($pop1), $pop2{{$}} 220define void @store_i32_with_unfolded_gep_negative_offset(i32* %p) { 221 %s = getelementptr inbounds i32, i32* %p, i32 -6 222 store atomic i32 0, i32* %s seq_cst, align 4 223 ret void 224} 225 226; Without nuw, and even with nsw, we can't fold an offset. 227 228; CHECK-LABEL: store_i32_with_unfolded_offset: 229; CHECK: i32.const $push0=, 24{{$}} 230; CHECK: i32.add $push1=, $0, $pop0{{$}} 231; CHECK: i32.atomic.store 0($pop1), $pop2{{$}} 232define void @store_i32_with_unfolded_offset(i32* %p) { 233 %q = ptrtoint i32* %p to i32 234 %r = add nsw i32 %q, 24 235 %s = inttoptr i32 %r to i32* 236 store atomic i32 0, i32* %s seq_cst, align 4 237 ret void 238} 239 240; Without inbounds, we can't fold a gep offset. 241 242; CHECK-LABEL: store_i32_with_unfolded_gep_offset: 243; CHECK: i32.const $push0=, 24{{$}} 244; CHECK: i32.add $push1=, $0, $pop0{{$}} 245; CHECK: i32.atomic.store 0($pop1), $pop2{{$}} 246define void @store_i32_with_unfolded_gep_offset(i32* %p) { 247 %s = getelementptr i32, i32* %p, i32 6 248 store atomic i32 0, i32* %s seq_cst, align 4 249 ret void 250} 251 252; When storing from a fixed address, materialize a zero. 253 254; CHECK-LABEL: store_i32_to_numeric_address: 255; CHECK-NEXT: i32.const $push0=, 0{{$}} 256; CHECK-NEXT: i32.const $push1=, 0{{$}} 257; CHECK-NEXT: i32.atomic.store 42($pop0), $pop1{{$}} 258define void @store_i32_to_numeric_address() { 259 %s = inttoptr i32 42 to i32* 260 store atomic i32 0, i32* %s seq_cst, align 4 261 ret void 262} 263 264; CHECK-LABEL: store_i32_to_global_address: 265; CHECK: i32.const $push0=, 0{{$}} 266; CHECK: i32.const $push1=, 0{{$}} 267; CHECK: i32.atomic.store gv($pop0), $pop1{{$}} 268define void @store_i32_to_global_address() { 269 store atomic i32 0, i32* @gv seq_cst, align 4 270 ret void 271} 272 273;===---------------------------------------------------------------------------- 274; Atomic stores: 64-bit 275;===---------------------------------------------------------------------------- 276 277; Basic store. 278 279; CHECK-LABEL: store_i64_no_offset: 280; CHECK-NEXT: .param i32, i64{{$}} 281; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}} 282; CHECK-NEXT: return{{$}} 283define void @store_i64_no_offset(i64 *%p, i64 %v) { 284 store atomic i64 %v, i64* %p seq_cst, align 8 285 ret void 286} 287 288; With an nuw add, we can fold an offset. 289 290; CHECK-LABEL: store_i64_with_folded_offset: 291; CHECK: i64.atomic.store 24($0), $pop0{{$}} 292define void @store_i64_with_folded_offset(i64* %p) { 293 %q = ptrtoint i64* %p to i32 294 %r = add nuw i32 %q, 24 295 %s = inttoptr i32 %r to i64* 296 store atomic i64 0, i64* %s seq_cst, align 8 297 ret void 298} 299 300; With an inbounds gep, we can fold an offset. 301 302; CHECK-LABEL: store_i64_with_folded_gep_offset: 303; CHECK: i64.atomic.store 24($0), $pop0{{$}} 304define void @store_i64_with_folded_gep_offset(i64* %p) { 305 %s = getelementptr inbounds i64, i64* %p, i32 3 306 store atomic i64 0, i64* %s seq_cst, align 8 307 ret void 308} 309 310; We can't fold a negative offset though, even with an inbounds gep. 311 312; CHECK-LABEL: store_i64_with_unfolded_gep_negative_offset: 313; CHECK: i32.const $push0=, -24{{$}} 314; CHECK: i32.add $push1=, $0, $pop0{{$}} 315; CHECK: i64.atomic.store 0($pop1), $pop2{{$}} 316define void @store_i64_with_unfolded_gep_negative_offset(i64* %p) { 317 %s = getelementptr inbounds i64, i64* %p, i32 -3 318 store atomic i64 0, i64* %s seq_cst, align 8 319 ret void 320} 321 322; Without nuw, and even with nsw, we can't fold an offset. 323 324; CHECK-LABEL: store_i64_with_unfolded_offset: 325; CHECK: i32.const $push0=, 24{{$}} 326; CHECK: i32.add $push1=, $0, $pop0{{$}} 327; CHECK: i64.atomic.store 0($pop1), $pop2{{$}} 328define void @store_i64_with_unfolded_offset(i64* %p) { 329 %q = ptrtoint i64* %p to i32 330 %r = add nsw i32 %q, 24 331 %s = inttoptr i32 %r to i64* 332 store atomic i64 0, i64* %s seq_cst, align 8 333 ret void 334} 335 336; Without inbounds, we can't fold a gep offset. 337 338; CHECK-LABEL: store_i64_with_unfolded_gep_offset: 339; CHECK: i32.const $push0=, 24{{$}} 340; CHECK: i32.add $push1=, $0, $pop0{{$}} 341; CHECK: i64.atomic.store 0($pop1), $pop2{{$}} 342define void @store_i64_with_unfolded_gep_offset(i64* %p) { 343 %s = getelementptr i64, i64* %p, i32 3 344 store atomic i64 0, i64* %s seq_cst, align 8 345 ret void 346} 347 348;===---------------------------------------------------------------------------- 349; Atomic sign-extending loads 350;===---------------------------------------------------------------------------- 351 352; Fold an offset into a sign-extending load. 353 354; CHECK-LABEL: load_i8_i32_s_with_folded_offset: 355; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}} 356; CHECK-NEXT: i32.extend8_s $push1=, $pop0 357define i32 @load_i8_i32_s_with_folded_offset(i8* %p) { 358 %q = ptrtoint i8* %p to i32 359 %r = add nuw i32 %q, 24 360 %s = inttoptr i32 %r to i8* 361 %t = load atomic i8, i8* %s seq_cst, align 1 362 %u = sext i8 %t to i32 363 ret i32 %u 364} 365 366; 32->64 sext load gets selected as i32.atomic.load, i64_extend_s/i32 367; CHECK-LABEL: load_i32_i64_s_with_folded_offset: 368; CHECK: i32.atomic.load $push0=, 24($0){{$}} 369; CHECK-NEXT: i64.extend_s/i32 $push1=, $pop0{{$}} 370define i64 @load_i32_i64_s_with_folded_offset(i32* %p) { 371 %q = ptrtoint i32* %p to i32 372 %r = add nuw i32 %q, 24 373 %s = inttoptr i32 %r to i32* 374 %t = load atomic i32, i32* %s seq_cst, align 4 375 %u = sext i32 %t to i64 376 ret i64 %u 377} 378 379; Fold a gep offset into a sign-extending load. 380 381; CHECK-LABEL: load_i8_i32_s_with_folded_gep_offset: 382; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}} 383; CHECK-NEXT: i32.extend8_s $push1=, $pop0 384define i32 @load_i8_i32_s_with_folded_gep_offset(i8* %p) { 385 %s = getelementptr inbounds i8, i8* %p, i32 24 386 %t = load atomic i8, i8* %s seq_cst, align 1 387 %u = sext i8 %t to i32 388 ret i32 %u 389} 390 391; CHECK-LABEL: load_i16_i32_s_with_folded_gep_offset: 392; CHECK: i32.atomic.load16_u $push0=, 48($0){{$}} 393; CHECK-NEXT: i32.extend16_s $push1=, $pop0 394define i32 @load_i16_i32_s_with_folded_gep_offset(i16* %p) { 395 %s = getelementptr inbounds i16, i16* %p, i32 24 396 %t = load atomic i16, i16* %s seq_cst, align 2 397 %u = sext i16 %t to i32 398 ret i32 %u 399} 400 401; CHECK-LABEL: load_i16_i64_s_with_folded_gep_offset: 402; CHECK: i64.atomic.load16_u $push0=, 48($0){{$}} 403; CHECK-NEXT: i64.extend16_s $push1=, $pop0 404define i64 @load_i16_i64_s_with_folded_gep_offset(i16* %p) { 405 %s = getelementptr inbounds i16, i16* %p, i32 24 406 %t = load atomic i16, i16* %s seq_cst, align 2 407 %u = sext i16 %t to i64 408 ret i64 %u 409} 410 411; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as 412; an 'add' if the or'ed bits are known to be zero. 413 414; CHECK-LABEL: load_i8_i32_s_with_folded_or_offset: 415; CHECK: i32.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}} 416; CHECK-NEXT: i32.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}} 417define i32 @load_i8_i32_s_with_folded_or_offset(i32 %x) { 418 %and = and i32 %x, -4 419 %t0 = inttoptr i32 %and to i8* 420 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 421 %t1 = load atomic i8, i8* %arrayidx seq_cst, align 1 422 %conv = sext i8 %t1 to i32 423 ret i32 %conv 424} 425 426; CHECK-LABEL: load_i8_i64_s_with_folded_or_offset: 427; CHECK: i64.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}} 428; CHECK-NEXT: i64.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}} 429define i64 @load_i8_i64_s_with_folded_or_offset(i32 %x) { 430 %and = and i32 %x, -4 431 %t0 = inttoptr i32 %and to i8* 432 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 433 %t1 = load atomic i8, i8* %arrayidx seq_cst, align 1 434 %conv = sext i8 %t1 to i64 435 ret i64 %conv 436} 437 438; When loading from a fixed address, materialize a zero. 439 440; CHECK-LABEL: load_i16_i32_s_from_numeric_address 441; CHECK: i32.const $push0=, 0{{$}} 442; CHECK: i32.atomic.load16_u $push1=, 42($pop0){{$}} 443; CHECK-NEXT: i32.extend16_s $push2=, $pop1 444define i32 @load_i16_i32_s_from_numeric_address() { 445 %s = inttoptr i32 42 to i16* 446 %t = load atomic i16, i16* %s seq_cst, align 2 447 %u = sext i16 %t to i32 448 ret i32 %u 449} 450 451; CHECK-LABEL: load_i8_i32_s_from_global_address 452; CHECK: i32.const $push0=, 0{{$}} 453; CHECK: i32.atomic.load8_u $push1=, gv8($pop0){{$}} 454; CHECK-NEXT: i32.extend8_s $push2=, $pop1{{$}} 455@gv8 = global i8 0 456define i32 @load_i8_i32_s_from_global_address() { 457 %t = load atomic i8, i8* @gv8 seq_cst, align 1 458 %u = sext i8 %t to i32 459 ret i32 %u 460} 461 462;===---------------------------------------------------------------------------- 463; Atomic zero-extending loads 464;===---------------------------------------------------------------------------- 465 466; Fold an offset into a zero-extending load. 467 468; CHECK-LABEL: load_i8_i32_z_with_folded_offset: 469; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}} 470define i32 @load_i8_i32_z_with_folded_offset(i8* %p) { 471 %q = ptrtoint i8* %p to i32 472 %r = add nuw i32 %q, 24 473 %s = inttoptr i32 %r to i8* 474 %t = load atomic i8, i8* %s seq_cst, align 1 475 %u = zext i8 %t to i32 476 ret i32 %u 477} 478 479; CHECK-LABEL: load_i32_i64_z_with_folded_offset: 480; CHECK: i64.atomic.load32_u $push0=, 24($0){{$}} 481define i64 @load_i32_i64_z_with_folded_offset(i32* %p) { 482 %q = ptrtoint i32* %p to i32 483 %r = add nuw i32 %q, 24 484 %s = inttoptr i32 %r to i32* 485 %t = load atomic i32, i32* %s seq_cst, align 4 486 %u = zext i32 %t to i64 487 ret i64 %u 488} 489 490; Fold a gep offset into a zero-extending load. 491 492; CHECK-LABEL: load_i8_i32_z_with_folded_gep_offset: 493; CHECK: i32.atomic.load8_u $push0=, 24($0){{$}} 494define i32 @load_i8_i32_z_with_folded_gep_offset(i8* %p) { 495 %s = getelementptr inbounds i8, i8* %p, i32 24 496 %t = load atomic i8, i8* %s seq_cst, align 1 497 %u = zext i8 %t to i32 498 ret i32 %u 499} 500 501; CHECK-LABEL: load_i16_i32_z_with_folded_gep_offset: 502; CHECK: i32.atomic.load16_u $push0=, 48($0){{$}} 503define i32 @load_i16_i32_z_with_folded_gep_offset(i16* %p) { 504 %s = getelementptr inbounds i16, i16* %p, i32 24 505 %t = load atomic i16, i16* %s seq_cst, align 2 506 %u = zext i16 %t to i32 507 ret i32 %u 508} 509 510; CHECK-LABEL: load_i16_i64_z_with_folded_gep_offset: 511; CHECK: i64.atomic.load16_u $push0=, 48($0){{$}} 512define i64 @load_i16_i64_z_with_folded_gep_offset(i16* %p) { 513 %s = getelementptr inbounds i16, i16* %p, i64 24 514 %t = load atomic i16, i16* %s seq_cst, align 2 515 %u = zext i16 %t to i64 516 ret i64 %u 517} 518 519; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as 520; an 'add' if the or'ed bits are known to be zero. 521 522; CHECK-LABEL: load_i8_i32_z_with_folded_or_offset: 523; CHECK: i32.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}} 524define i32 @load_i8_i32_z_with_folded_or_offset(i32 %x) { 525 %and = and i32 %x, -4 526 %t0 = inttoptr i32 %and to i8* 527 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 528 %t1 = load atomic i8, i8* %arrayidx seq_cst, align 1 529 %conv = zext i8 %t1 to i32 530 ret i32 %conv 531} 532 533; CHECK-LABEL: load_i8_i64_z_with_folded_or_offset: 534; CHECK: i64.atomic.load8_u $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}){{$}} 535define i64 @load_i8_i64_z_with_folded_or_offset(i32 %x) { 536 %and = and i32 %x, -4 537 %t0 = inttoptr i32 %and to i8* 538 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 539 %t1 = load atomic i8, i8* %arrayidx seq_cst, align 1 540 %conv = zext i8 %t1 to i64 541 ret i64 %conv 542} 543 544; When loading from a fixed address, materialize a zero. 545 546; CHECK-LABEL: load_i16_i32_z_from_numeric_address 547; CHECK: i32.const $push0=, 0{{$}} 548; CHECK: i32.atomic.load16_u $push1=, 42($pop0){{$}} 549define i32 @load_i16_i32_z_from_numeric_address() { 550 %s = inttoptr i32 42 to i16* 551 %t = load atomic i16, i16* %s seq_cst, align 2 552 %u = zext i16 %t to i32 553 ret i32 %u 554} 555 556; CHECK-LABEL: load_i8_i32_z_from_global_address 557; CHECK: i32.const $push0=, 0{{$}} 558; CHECK: i32.atomic.load8_u $push1=, gv8($pop0){{$}} 559define i32 @load_i8_i32_z_from_global_address() { 560 %t = load atomic i8, i8* @gv8 seq_cst, align 1 561 %u = zext i8 %t to i32 562 ret i32 %u 563} 564 565; i8 return value should test anyext loads 566 567; CHECK-LABEL: load_i8_i32_retvalue: 568; CHECK: i32.atomic.load8_u $push0=, 0($0){{$}} 569; CHECK-NEXT: return $pop0{{$}} 570define i8 @load_i8_i32_retvalue(i8 *%p) { 571 %v = load atomic i8, i8* %p seq_cst, align 1 572 ret i8 %v 573} 574 575;===---------------------------------------------------------------------------- 576; Atomic truncating stores 577;===---------------------------------------------------------------------------- 578 579; Fold an offset into a truncating store. 580 581; CHECK-LABEL: store_i8_i32_with_folded_offset: 582; CHECK: i32.atomic.store8 24($0), $1{{$}} 583define void @store_i8_i32_with_folded_offset(i8* %p, i32 %v) { 584 %q = ptrtoint i8* %p to i32 585 %r = add nuw i32 %q, 24 586 %s = inttoptr i32 %r to i8* 587 %t = trunc i32 %v to i8 588 store atomic i8 %t, i8* %s seq_cst, align 1 589 ret void 590} 591 592; CHECK-LABEL: store_i32_i64_with_folded_offset: 593; CHECK: i64.atomic.store32 24($0), $1{{$}} 594define void @store_i32_i64_with_folded_offset(i32* %p, i64 %v) { 595 %q = ptrtoint i32* %p to i32 596 %r = add nuw i32 %q, 24 597 %s = inttoptr i32 %r to i32* 598 %t = trunc i64 %v to i32 599 store atomic i32 %t, i32* %s seq_cst, align 4 600 ret void 601} 602 603; Fold a gep offset into a truncating store. 604 605; CHECK-LABEL: store_i8_i32_with_folded_gep_offset: 606; CHECK: i32.atomic.store8 24($0), $1{{$}} 607define void @store_i8_i32_with_folded_gep_offset(i8* %p, i32 %v) { 608 %s = getelementptr inbounds i8, i8* %p, i32 24 609 %t = trunc i32 %v to i8 610 store atomic i8 %t, i8* %s seq_cst, align 1 611 ret void 612} 613 614; CHECK-LABEL: store_i16_i32_with_folded_gep_offset: 615; CHECK: i32.atomic.store16 48($0), $1{{$}} 616define void @store_i16_i32_with_folded_gep_offset(i16* %p, i32 %v) { 617 %s = getelementptr inbounds i16, i16* %p, i32 24 618 %t = trunc i32 %v to i16 619 store atomic i16 %t, i16* %s seq_cst, align 2 620 ret void 621} 622 623; CHECK-LABEL: store_i16_i64_with_folded_gep_offset: 624; CHECK: i64.atomic.store16 48($0), $1{{$}} 625define void @store_i16_i64_with_folded_gep_offset(i16* %p, i64 %v) { 626 %s = getelementptr inbounds i16, i16* %p, i32 24 627 %t = trunc i64 %v to i16 628 store atomic i16 %t, i16* %s seq_cst, align 2 629 ret void 630} 631 632; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as 633; an 'add' if the or'ed bits are known to be zero. 634 635; CHECK-LABEL: store_i8_i32_with_folded_or_offset: 636; CHECK: i32.atomic.store8 2($pop{{[0-9]+}}), $1{{$}} 637define void @store_i8_i32_with_folded_or_offset(i32 %x, i32 %v) { 638 %and = and i32 %x, -4 639 %p = inttoptr i32 %and to i8* 640 %arrayidx = getelementptr inbounds i8, i8* %p, i32 2 641 %t = trunc i32 %v to i8 642 store atomic i8 %t, i8* %arrayidx seq_cst, align 1 643 ret void 644} 645 646; CHECK-LABEL: store_i8_i64_with_folded_or_offset: 647; CHECK: i64.atomic.store8 2($pop{{[0-9]+}}), $1{{$}} 648define void @store_i8_i64_with_folded_or_offset(i32 %x, i64 %v) { 649 %and = and i32 %x, -4 650 %p = inttoptr i32 %and to i8* 651 %arrayidx = getelementptr inbounds i8, i8* %p, i32 2 652 %t = trunc i64 %v to i8 653 store atomic i8 %t, i8* %arrayidx seq_cst, align 1 654 ret void 655} 656 657;===---------------------------------------------------------------------------- 658; Atomic binary read-modify-writes: 32-bit 659;===---------------------------------------------------------------------------- 660 661; There are several RMW instructions, but here we only test 'add' as an example. 662 663; Basic RMW. 664 665; CHECK-LABEL: rmw_add_i32_no_offset: 666; CHECK-NEXT: .param i32, i32{{$}} 667; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}} 668; CHECK-NEXT: return $pop0{{$}} 669define i32 @rmw_add_i32_no_offset(i32* %p, i32 %v) { 670 %old = atomicrmw add i32* %p, i32 %v seq_cst 671 ret i32 %old 672} 673 674; With an nuw add, we can fold an offset. 675 676; CHECK-LABEL: rmw_add_i32_with_folded_offset: 677; CHECK: i32.atomic.rmw.add $push0=, 24($0), $1{{$}} 678define i32 @rmw_add_i32_with_folded_offset(i32* %p, i32 %v) { 679 %q = ptrtoint i32* %p to i32 680 %r = add nuw i32 %q, 24 681 %s = inttoptr i32 %r to i32* 682 %old = atomicrmw add i32* %s, i32 %v seq_cst 683 ret i32 %old 684} 685 686; With an inbounds gep, we can fold an offset. 687 688; CHECK-LABEL: rmw_add_i32_with_folded_gep_offset: 689; CHECK: i32.atomic.rmw.add $push0=, 24($0), $1{{$}} 690define i32 @rmw_add_i32_with_folded_gep_offset(i32* %p, i32 %v) { 691 %s = getelementptr inbounds i32, i32* %p, i32 6 692 %old = atomicrmw add i32* %s, i32 %v seq_cst 693 ret i32 %old 694} 695 696; We can't fold a negative offset though, even with an inbounds gep. 697 698; CHECK-LABEL: rmw_add_i32_with_unfolded_gep_negative_offset: 699; CHECK: i32.const $push0=, -24{{$}} 700; CHECK: i32.add $push1=, $0, $pop0{{$}} 701; CHECK: i32.atomic.rmw.add $push2=, 0($pop1), $1{{$}} 702define i32 @rmw_add_i32_with_unfolded_gep_negative_offset(i32* %p, i32 %v) { 703 %s = getelementptr inbounds i32, i32* %p, i32 -6 704 %old = atomicrmw add i32* %s, i32 %v seq_cst 705 ret i32 %old 706} 707 708; Without nuw, and even with nsw, we can't fold an offset. 709 710; CHECK-LABEL: rmw_add_i32_with_unfolded_offset: 711; CHECK: i32.const $push0=, 24{{$}} 712; CHECK: i32.add $push1=, $0, $pop0{{$}} 713; CHECK: i32.atomic.rmw.add $push2=, 0($pop1), $1{{$}} 714define i32 @rmw_add_i32_with_unfolded_offset(i32* %p, i32 %v) { 715 %q = ptrtoint i32* %p to i32 716 %r = add nsw i32 %q, 24 717 %s = inttoptr i32 %r to i32* 718 %old = atomicrmw add i32* %s, i32 %v seq_cst 719 ret i32 %old 720} 721 722; Without inbounds, we can't fold a gep offset. 723 724; CHECK-LABEL: rmw_add_i32_with_unfolded_gep_offset: 725; CHECK: i32.const $push0=, 24{{$}} 726; CHECK: i32.add $push1=, $0, $pop0{{$}} 727; CHECK: i32.atomic.rmw.add $push2=, 0($pop1), $1{{$}} 728define i32 @rmw_add_i32_with_unfolded_gep_offset(i32* %p, i32 %v) { 729 %s = getelementptr i32, i32* %p, i32 6 730 %old = atomicrmw add i32* %s, i32 %v seq_cst 731 ret i32 %old 732} 733 734; When loading from a fixed address, materialize a zero. 735 736; CHECK-LABEL: rmw_add_i32_from_numeric_address 737; CHECK: i32.const $push0=, 0{{$}} 738; CHECK: i32.atomic.rmw.add $push1=, 42($pop0), $0{{$}} 739define i32 @rmw_add_i32_from_numeric_address(i32 %v) { 740 %s = inttoptr i32 42 to i32* 741 %old = atomicrmw add i32* %s, i32 %v seq_cst 742 ret i32 %old 743} 744 745; CHECK-LABEL: rmw_add_i32_from_global_address 746; CHECK: i32.const $push0=, 0{{$}} 747; CHECK: i32.atomic.rmw.add $push1=, gv($pop0), $0{{$}} 748define i32 @rmw_add_i32_from_global_address(i32 %v) { 749 %old = atomicrmw add i32* @gv, i32 %v seq_cst 750 ret i32 %old 751} 752 753;===---------------------------------------------------------------------------- 754; Atomic binary read-modify-writes: 64-bit 755;===---------------------------------------------------------------------------- 756 757; Basic RMW. 758 759; CHECK-LABEL: rmw_add_i64_no_offset: 760; CHECK-NEXT: .param i32, i64{{$}} 761; CHECK: i64.atomic.rmw.add $push0=, 0($0), $1{{$}} 762; CHECK-NEXT: return $pop0{{$}} 763define i64 @rmw_add_i64_no_offset(i64* %p, i64 %v) { 764 %old = atomicrmw add i64* %p, i64 %v seq_cst 765 ret i64 %old 766} 767 768; With an nuw add, we can fold an offset. 769 770; CHECK-LABEL: rmw_add_i64_with_folded_offset: 771; CHECK: i64.atomic.rmw.add $push0=, 24($0), $1{{$}} 772define i64 @rmw_add_i64_with_folded_offset(i64* %p, i64 %v) { 773 %q = ptrtoint i64* %p to i32 774 %r = add nuw i32 %q, 24 775 %s = inttoptr i32 %r to i64* 776 %old = atomicrmw add i64* %s, i64 %v seq_cst 777 ret i64 %old 778} 779 780; With an inbounds gep, we can fold an offset. 781 782; CHECK-LABEL: rmw_add_i64_with_folded_gep_offset: 783; CHECK: i64.atomic.rmw.add $push0=, 24($0), $1{{$}} 784define i64 @rmw_add_i64_with_folded_gep_offset(i64* %p, i64 %v) { 785 %s = getelementptr inbounds i64, i64* %p, i32 3 786 %old = atomicrmw add i64* %s, i64 %v seq_cst 787 ret i64 %old 788} 789 790; We can't fold a negative offset though, even with an inbounds gep. 791 792; CHECK-LABEL: rmw_add_i64_with_unfolded_gep_negative_offset: 793; CHECK: i32.const $push0=, -24{{$}} 794; CHECK: i32.add $push1=, $0, $pop0{{$}} 795; CHECK: i64.atomic.rmw.add $push2=, 0($pop1), $1{{$}} 796define i64 @rmw_add_i64_with_unfolded_gep_negative_offset(i64* %p, i64 %v) { 797 %s = getelementptr inbounds i64, i64* %p, i32 -3 798 %old = atomicrmw add i64* %s, i64 %v seq_cst 799 ret i64 %old 800} 801 802; Without nuw, and even with nsw, we can't fold an offset. 803 804; CHECK-LABEL: rmw_add_i64_with_unfolded_offset: 805; CHECK: i32.const $push0=, 24{{$}} 806; CHECK: i32.add $push1=, $0, $pop0{{$}} 807; CHECK: i64.atomic.rmw.add $push2=, 0($pop1), $1{{$}} 808define i64 @rmw_add_i64_with_unfolded_offset(i64* %p, i64 %v) { 809 %q = ptrtoint i64* %p to i32 810 %r = add nsw i32 %q, 24 811 %s = inttoptr i32 %r to i64* 812 %old = atomicrmw add i64* %s, i64 %v seq_cst 813 ret i64 %old 814} 815 816; Without inbounds, we can't fold a gep offset. 817 818; CHECK-LABEL: rmw_add_i64_with_unfolded_gep_offset: 819; CHECK: i32.const $push0=, 24{{$}} 820; CHECK: i32.add $push1=, $0, $pop0{{$}} 821; CHECK: i64.atomic.rmw.add $push2=, 0($pop1), $1{{$}} 822define i64 @rmw_add_i64_with_unfolded_gep_offset(i64* %p, i64 %v) { 823 %s = getelementptr i64, i64* %p, i32 3 824 %old = atomicrmw add i64* %s, i64 %v seq_cst 825 ret i64 %old 826} 827 828;===---------------------------------------------------------------------------- 829; Atomic truncating & sign-extending binary RMWs 830;===---------------------------------------------------------------------------- 831 832; Fold an offset into a sign-extending rmw. 833 834; CHECK-LABEL: rmw_add_i8_i32_s_with_folded_offset: 835; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}} 836; CHECK-NEXT: i32.extend8_s $push1=, $pop0 837define i32 @rmw_add_i8_i32_s_with_folded_offset(i8* %p, i32 %v) { 838 %q = ptrtoint i8* %p to i32 839 %r = add nuw i32 %q, 24 840 %s = inttoptr i32 %r to i8* 841 %t = trunc i32 %v to i8 842 %old = atomicrmw add i8* %s, i8 %t seq_cst 843 %u = sext i8 %old to i32 844 ret i32 %u 845} 846 847; 32->64 sext rmw gets selected as i32.atomic.rmw.add, i64_extend_s/i32 848; CHECK-LABEL: rmw_add_i32_i64_s_with_folded_offset: 849; CHECK: i32.wrap/i64 $push0=, $1 850; CHECK-NEXT: i32.atomic.rmw.add $push1=, 24($0), $pop0{{$}} 851; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}} 852define i64 @rmw_add_i32_i64_s_with_folded_offset(i32* %p, i64 %v) { 853 %q = ptrtoint i32* %p to i32 854 %r = add nuw i32 %q, 24 855 %s = inttoptr i32 %r to i32* 856 %t = trunc i64 %v to i32 857 %old = atomicrmw add i32* %s, i32 %t seq_cst 858 %u = sext i32 %old to i64 859 ret i64 %u 860} 861 862; Fold a gep offset into a sign-extending rmw. 863 864; CHECK-LABEL: rmw_add_i8_i32_s_with_folded_gep_offset: 865; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}} 866; CHECK-NEXT: i32.extend8_s $push1=, $pop0 867define i32 @rmw_add_i8_i32_s_with_folded_gep_offset(i8* %p, i32 %v) { 868 %s = getelementptr inbounds i8, i8* %p, i32 24 869 %t = trunc i32 %v to i8 870 %old = atomicrmw add i8* %s, i8 %t seq_cst 871 %u = sext i8 %old to i32 872 ret i32 %u 873} 874 875; CHECK-LABEL: rmw_add_i16_i32_s_with_folded_gep_offset: 876; CHECK: i32.atomic.rmw16_u.add $push0=, 48($0), $1{{$}} 877; CHECK-NEXT: i32.extend16_s $push1=, $pop0 878define i32 @rmw_add_i16_i32_s_with_folded_gep_offset(i16* %p, i32 %v) { 879 %s = getelementptr inbounds i16, i16* %p, i32 24 880 %t = trunc i32 %v to i16 881 %old = atomicrmw add i16* %s, i16 %t seq_cst 882 %u = sext i16 %old to i32 883 ret i32 %u 884} 885 886; CHECK-LABEL: rmw_add_i16_i64_s_with_folded_gep_offset: 887; CHECK: i64.atomic.rmw16_u.add $push0=, 48($0), $1{{$}} 888; CHECK-NEXT: i64.extend16_s $push1=, $pop0 889define i64 @rmw_add_i16_i64_s_with_folded_gep_offset(i16* %p, i64 %v) { 890 %s = getelementptr inbounds i16, i16* %p, i32 24 891 %t = trunc i64 %v to i16 892 %old = atomicrmw add i16* %s, i16 %t seq_cst 893 %u = sext i16 %old to i64 894 ret i64 %u 895} 896 897; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as 898; an 'add' if the or'ed bits are known to be zero. 899 900; CHECK-LABEL: rmw_add_i8_i32_s_with_folded_or_offset: 901; CHECK: i32.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}} 902; CHECK-NEXT: i32.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}} 903define i32 @rmw_add_i8_i32_s_with_folded_or_offset(i32 %x, i32 %v) { 904 %and = and i32 %x, -4 905 %t0 = inttoptr i32 %and to i8* 906 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 907 %t = trunc i32 %v to i8 908 %old = atomicrmw add i8* %arrayidx, i8 %t seq_cst 909 %conv = sext i8 %old to i32 910 ret i32 %conv 911} 912 913; CHECK-LABEL: rmw_add_i8_i64_s_with_folded_or_offset: 914; CHECK: i64.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}} 915; CHECK-NEXT: i64.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}} 916define i64 @rmw_add_i8_i64_s_with_folded_or_offset(i32 %x, i64 %v) { 917 %and = and i32 %x, -4 918 %t0 = inttoptr i32 %and to i8* 919 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 920 %t = trunc i64 %v to i8 921 %old = atomicrmw add i8* %arrayidx, i8 %t seq_cst 922 %conv = sext i8 %old to i64 923 ret i64 %conv 924} 925 926; When loading from a fixed address, materialize a zero. 927 928; CHECK-LABEL: rmw_add_i16_i32_s_from_numeric_address 929; CHECK: i32.const $push0=, 0{{$}} 930; CHECK: i32.atomic.rmw16_u.add $push1=, 42($pop0), $0{{$}} 931; CHECK-NEXT: i32.extend16_s $push2=, $pop1 932define i32 @rmw_add_i16_i32_s_from_numeric_address(i32 %v) { 933 %s = inttoptr i32 42 to i16* 934 %t = trunc i32 %v to i16 935 %old = atomicrmw add i16* %s, i16 %t seq_cst 936 %u = sext i16 %old to i32 937 ret i32 %u 938} 939 940; CHECK-LABEL: rmw_add_i8_i32_s_from_global_address 941; CHECK: i32.const $push0=, 0{{$}} 942; CHECK: i32.atomic.rmw8_u.add $push1=, gv8($pop0), $0{{$}} 943; CHECK-NEXT: i32.extend8_s $push2=, $pop1{{$}} 944define i32 @rmw_add_i8_i32_s_from_global_address(i32 %v) { 945 %t = trunc i32 %v to i8 946 %old = atomicrmw add i8* @gv8, i8 %t seq_cst 947 %u = sext i8 %old to i32 948 ret i32 %u 949} 950 951;===---------------------------------------------------------------------------- 952; Atomic truncating & zero-extending binary RMWs 953;===---------------------------------------------------------------------------- 954 955; Fold an offset into a zero-extending rmw. 956 957; CHECK-LABEL: rmw_add_i8_i32_z_with_folded_offset: 958; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}} 959define i32 @rmw_add_i8_i32_z_with_folded_offset(i8* %p, i32 %v) { 960 %q = ptrtoint i8* %p to i32 961 %r = add nuw i32 %q, 24 962 %s = inttoptr i32 %r to i8* 963 %t = trunc i32 %v to i8 964 %old = atomicrmw add i8* %s, i8 %t seq_cst 965 %u = zext i8 %old to i32 966 ret i32 %u 967} 968 969; CHECK-LABEL: rmw_add_i32_i64_z_with_folded_offset: 970; CHECK: i64.atomic.rmw32_u.add $push0=, 24($0), $1{{$}} 971define i64 @rmw_add_i32_i64_z_with_folded_offset(i32* %p, i64 %v) { 972 %q = ptrtoint i32* %p to i32 973 %r = add nuw i32 %q, 24 974 %s = inttoptr i32 %r to i32* 975 %t = trunc i64 %v to i32 976 %old = atomicrmw add i32* %s, i32 %t seq_cst 977 %u = zext i32 %old to i64 978 ret i64 %u 979} 980 981; Fold a gep offset into a zero-extending rmw. 982 983; CHECK-LABEL: rmw_add_i8_i32_z_with_folded_gep_offset: 984; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}} 985define i32 @rmw_add_i8_i32_z_with_folded_gep_offset(i8* %p, i32 %v) { 986 %s = getelementptr inbounds i8, i8* %p, i32 24 987 %t = trunc i32 %v to i8 988 %old = atomicrmw add i8* %s, i8 %t seq_cst 989 %u = zext i8 %old to i32 990 ret i32 %u 991} 992 993; CHECK-LABEL: rmw_add_i16_i32_z_with_folded_gep_offset: 994; CHECK: i32.atomic.rmw16_u.add $push0=, 48($0), $1{{$}} 995define i32 @rmw_add_i16_i32_z_with_folded_gep_offset(i16* %p, i32 %v) { 996 %s = getelementptr inbounds i16, i16* %p, i32 24 997 %t = trunc i32 %v to i16 998 %old = atomicrmw add i16* %s, i16 %t seq_cst 999 %u = zext i16 %old to i32 1000 ret i32 %u 1001} 1002 1003; CHECK-LABEL: rmw_add_i16_i64_z_with_folded_gep_offset: 1004; CHECK: i64.atomic.rmw16_u.add $push0=, 48($0), $1{{$}} 1005define i64 @rmw_add_i16_i64_z_with_folded_gep_offset(i16* %p, i64 %v) { 1006 %s = getelementptr inbounds i16, i16* %p, i32 24 1007 %t = trunc i64 %v to i16 1008 %old = atomicrmw add i16* %s, i16 %t seq_cst 1009 %u = zext i16 %old to i64 1010 ret i64 %u 1011} 1012 1013; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as 1014; an 'add' if the or'ed bits are known to be zero. 1015 1016; CHECK-LABEL: rmw_add_i8_i32_z_with_folded_or_offset: 1017; CHECK: i32.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}} 1018define i32 @rmw_add_i8_i32_z_with_folded_or_offset(i32 %x, i32 %v) { 1019 %and = and i32 %x, -4 1020 %t0 = inttoptr i32 %and to i8* 1021 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 1022 %t = trunc i32 %v to i8 1023 %old = atomicrmw add i8* %arrayidx, i8 %t seq_cst 1024 %conv = zext i8 %old to i32 1025 ret i32 %conv 1026} 1027 1028; CHECK-LABEL: rmw_add_i8_i64_z_with_folded_or_offset: 1029; CHECK: i64.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}} 1030define i64 @rmw_add_i8_i64_z_with_folded_or_offset(i32 %x, i64 %v) { 1031 %and = and i32 %x, -4 1032 %t0 = inttoptr i32 %and to i8* 1033 %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2 1034 %t = trunc i64 %v to i8 1035 %old = atomicrmw add i8* %arrayidx, i8 %t seq_cst 1036 %conv = zext i8 %old to i64 1037 ret i64 %conv 1038} 1039 1040; When loading from a fixed address, materialize a zero. 1041 1042; CHECK-LABEL: rmw_add_i16_i32_z_from_numeric_address 1043; CHECK: i32.const $push0=, 0{{$}} 1044; CHECK: i32.atomic.rmw16_u.add $push1=, 42($pop0), $0{{$}} 1045define i32 @rmw_add_i16_i32_z_from_numeric_address(i32 %v) { 1046 %s = inttoptr i32 42 to i16* 1047 %t = trunc i32 %v to i16 1048 %old = atomicrmw add i16* %s, i16 %t seq_cst 1049 %u = zext i16 %old to i32 1050 ret i32 %u 1051} 1052 1053; CHECK-LABEL: rmw_add_i8_i32_z_from_global_address 1054; CHECK: i32.const $push0=, 0{{$}} 1055; CHECK: i32.atomic.rmw8_u.add $push1=, gv8($pop0), $0{{$}} 1056define i32 @rmw_add_i8_i32_z_from_global_address(i32 %v) { 1057 %t = trunc i32 %v to i8 1058 %old = atomicrmw add i8* @gv8, i8 %t seq_cst 1059 %u = zext i8 %old to i32 1060 ret i32 %u 1061} 1062 1063; i8 return value should test anyext RMWs 1064 1065; CHECK-LABEL: rmw_add_i8_i32_retvalue: 1066; CHECK: i32.atomic.rmw8_u.add $push0=, 0($0), $1{{$}} 1067; CHECK-NEXT: return $pop0{{$}} 1068define i8 @rmw_add_i8_i32_retvalue(i8 *%p, i32 %v) { 1069 %t = trunc i32 %v to i8 1070 %old = atomicrmw add i8* %p, i8 %t seq_cst 1071 ret i8 %old 1072} 1073