1; RUN: llc < %s -mattr=+atomics -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s 2 3; Test loads and stores with custom alignment values. 4 5target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 6target triple = "wasm32-unknown-unknown" 7 8;===---------------------------------------------------------------------------- 9; Loads 10;===---------------------------------------------------------------------------- 11 12; CHECK-LABEL: ldi64_a1: 13; CHECK-NEXT: .functype ldi64_a1 (i32) -> (i64){{$}} 14; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} 15; CHECK-NEXT: return $pop[[NUM]]{{$}} 16define i64 @ldi64_a1(i64 *%p) { 17 %v = load i64, i64* %p, align 1 18 ret i64 %v 19} 20 21; CHECK-LABEL: ldi64_a2: 22; CHECK-NEXT: .functype ldi64_a2 (i32) -> (i64){{$}} 23; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}} 24; CHECK-NEXT: return $pop[[NUM]]{{$}} 25define i64 @ldi64_a2(i64 *%p) { 26 %v = load i64, i64* %p, align 2 27 ret i64 %v 28} 29 30; CHECK-LABEL: ldi64_a4: 31; CHECK-NEXT: .functype ldi64_a4 (i32) -> (i64){{$}} 32; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=2{{$}} 33; CHECK-NEXT: return $pop[[NUM]]{{$}} 34define i64 @ldi64_a4(i64 *%p) { 35 %v = load i64, i64* %p, align 4 36 ret i64 %v 37} 38 39; 8 is the default alignment for i64 so no attribute is needed. 40 41; CHECK-LABEL: ldi64_a8: 42; CHECK-NEXT: .functype ldi64_a8 (i32) -> (i64){{$}} 43; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} 44; CHECK-NEXT: return $pop[[NUM]]{{$}} 45define i64 @ldi64_a8(i64 *%p) { 46 %v = load i64, i64* %p, align 8 47 ret i64 %v 48} 49 50; The default alignment in LLVM is the same as the defualt alignment in wasm. 51 52; CHECK-LABEL: ldi64: 53; CHECK-NEXT: .functype ldi64 (i32) -> (i64){{$}} 54; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} 55; CHECK-NEXT: return $pop[[NUM]]{{$}} 56define i64 @ldi64(i64 *%p) { 57 %v = load i64, i64* %p 58 ret i64 %v 59} 60 61; 16 is greater than the default alignment so it is ignored. 62 63; CHECK-LABEL: ldi64_a16: 64; CHECK-NEXT: .functype ldi64_a16 (i32) -> (i64){{$}} 65; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}} 66; CHECK-NEXT: return $pop[[NUM]]{{$}} 67define i64 @ldi64_a16(i64 *%p) { 68 %v = load i64, i64* %p, align 16 69 ret i64 %v 70} 71 72;===---------------------------------------------------------------------------- 73; Extending loads 74;===---------------------------------------------------------------------------- 75 76; CHECK-LABEL: ldi8_a1: 77; CHECK-NEXT: .functype ldi8_a1 (i32) -> (i64){{$}} 78; CHECK-NEXT: i64.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}} 79; CHECK-NEXT: return $pop[[NUM]]{{$}} 80define i64 @ldi8_a1(i8 *%p) { 81 %v = load i8, i8* %p, align 1 82 %w = zext i8 %v to i64 83 ret i64 %w 84} 85 86; CHECK-LABEL: ldi8_a2: 87; CHECK-NEXT: .functype ldi8_a2 (i32) -> (i64){{$}} 88; CHECK-NEXT: i64.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}} 89; CHECK-NEXT: return $pop[[NUM]]{{$}} 90define i64 @ldi8_a2(i8 *%p) { 91 %v = load i8, i8* %p, align 2 92 %w = zext i8 %v to i64 93 ret i64 %w 94} 95 96; CHECK-LABEL: ldi16_a1: 97; CHECK-NEXT: .functype ldi16_a1 (i32) -> (i64){{$}} 98; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} 99; CHECK-NEXT: return $pop[[NUM]]{{$}} 100define i64 @ldi16_a1(i16 *%p) { 101 %v = load i16, i16* %p, align 1 102 %w = zext i16 %v to i64 103 ret i64 %w 104} 105 106; CHECK-LABEL: ldi16_a2: 107; CHECK-NEXT: .functype ldi16_a2 (i32) -> (i64){{$}} 108; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}} 109; CHECK-NEXT: return $pop[[NUM]]{{$}} 110define i64 @ldi16_a2(i16 *%p) { 111 %v = load i16, i16* %p, align 2 112 %w = zext i16 %v to i64 113 ret i64 %w 114} 115 116; CHECK-LABEL: ldi16_a4: 117; CHECK-NEXT: .functype ldi16_a4 (i32) -> (i64){{$}} 118; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}} 119; CHECK-NEXT: return $pop[[NUM]]{{$}} 120define i64 @ldi16_a4(i16 *%p) { 121 %v = load i16, i16* %p, align 4 122 %w = zext i16 %v to i64 123 ret i64 %w 124} 125 126; CHECK-LABEL: ldi32_a1: 127; CHECK-NEXT: .functype ldi32_a1 (i32) -> (i64){{$}} 128; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}} 129; CHECK-NEXT: return $pop[[NUM]]{{$}} 130define i64 @ldi32_a1(i32 *%p) { 131 %v = load i32, i32* %p, align 1 132 %w = zext i32 %v to i64 133 ret i64 %w 134} 135 136; CHECK-LABEL: ldi32_a2: 137; CHECK-NEXT: .functype ldi32_a2 (i32) -> (i64){{$}} 138; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}} 139; CHECK-NEXT: return $pop[[NUM]]{{$}} 140define i64 @ldi32_a2(i32 *%p) { 141 %v = load i32, i32* %p, align 2 142 %w = zext i32 %v to i64 143 ret i64 %w 144} 145 146; CHECK-LABEL: ldi32_a4: 147; CHECK-NEXT: .functype ldi32_a4 (i32) -> (i64){{$}} 148; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0){{$}} 149; CHECK-NEXT: return $pop[[NUM]]{{$}} 150define i64 @ldi32_a4(i32 *%p) { 151 %v = load i32, i32* %p, align 4 152 %w = zext i32 %v to i64 153 ret i64 %w 154} 155 156; CHECK-LABEL: ldi32_a8: 157; CHECK-NEXT: .functype ldi32_a8 (i32) -> (i64){{$}} 158; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0){{$}} 159; CHECK-NEXT: return $pop[[NUM]]{{$}} 160define i64 @ldi32_a8(i32 *%p) { 161 %v = load i32, i32* %p, align 8 162 %w = zext i32 %v to i64 163 ret i64 %w 164} 165 166;===---------------------------------------------------------------------------- 167; Stores 168;===---------------------------------------------------------------------------- 169 170; CHECK-LABEL: sti64_a1: 171; CHECK-NEXT: .functype sti64_a1 (i32, i64) -> (){{$}} 172; CHECK-NEXT: i64.store 0($0):p2align=0, $1{{$}} 173; CHECK-NEXT: return{{$}} 174define void @sti64_a1(i64 *%p, i64 %v) { 175 store i64 %v, i64* %p, align 1 176 ret void 177} 178 179; CHECK-LABEL: sti64_a2: 180; CHECK-NEXT: .functype sti64_a2 (i32, i64) -> (){{$}} 181; CHECK-NEXT: i64.store 0($0):p2align=1, $1{{$}} 182; CHECK-NEXT: return{{$}} 183define void @sti64_a2(i64 *%p, i64 %v) { 184 store i64 %v, i64* %p, align 2 185 ret void 186} 187 188; CHECK-LABEL: sti64_a4: 189; CHECK-NEXT: .functype sti64_a4 (i32, i64) -> (){{$}} 190; CHECK-NEXT: i64.store 0($0):p2align=2, $1{{$}} 191; CHECK-NEXT: return{{$}} 192define void @sti64_a4(i64 *%p, i64 %v) { 193 store i64 %v, i64* %p, align 4 194 ret void 195} 196 197; 8 is the default alignment for i32 so no attribute is needed. 198 199; CHECK-LABEL: sti64_a8: 200; CHECK-NEXT: .functype sti64_a8 (i32, i64) -> (){{$}} 201; CHECK-NEXT: i64.store 0($0), $1{{$}} 202; CHECK-NEXT: return{{$}} 203define void @sti64_a8(i64 *%p, i64 %v) { 204 store i64 %v, i64* %p, align 8 205 ret void 206} 207 208; The default alignment in LLVM is the same as the defualt alignment in wasm. 209 210; CHECK-LABEL: sti64: 211; CHECK-NEXT: .functype sti64 (i32, i64) -> (){{$}} 212; CHECK-NEXT: i64.store 0($0), $1{{$}} 213; CHECK-NEXT: return{{$}} 214define void @sti64(i64 *%p, i64 %v) { 215 store i64 %v, i64* %p 216 ret void 217} 218 219; CHECK-LABEL: sti64_a16: 220; CHECK-NEXT: .functype sti64_a16 (i32, i64) -> (){{$}} 221; CHECK-NEXT: i64.store 0($0), $1{{$}} 222; CHECK-NEXT: return{{$}} 223define void @sti64_a16(i64 *%p, i64 %v) { 224 store i64 %v, i64* %p, align 16 225 ret void 226} 227 228;===---------------------------------------------------------------------------- 229; Truncating stores 230;===---------------------------------------------------------------------------- 231 232; CHECK-LABEL: sti8_a1: 233; CHECK-NEXT: .functype sti8_a1 (i32, i64) -> (){{$}} 234; CHECK-NEXT: i64.store8 0($0), $1{{$}} 235; CHECK-NEXT: return{{$}} 236define void @sti8_a1(i8 *%p, i64 %w) { 237 %v = trunc i64 %w to i8 238 store i8 %v, i8* %p, align 1 239 ret void 240} 241 242; CHECK-LABEL: sti8_a2: 243; CHECK-NEXT: .functype sti8_a2 (i32, i64) -> (){{$}} 244; CHECK-NEXT: i64.store8 0($0), $1{{$}} 245; CHECK-NEXT: return{{$}} 246define void @sti8_a2(i8 *%p, i64 %w) { 247 %v = trunc i64 %w to i8 248 store i8 %v, i8* %p, align 2 249 ret void 250} 251 252; CHECK-LABEL: sti16_a1: 253; CHECK-NEXT: .functype sti16_a1 (i32, i64) -> (){{$}} 254; CHECK-NEXT: i64.store16 0($0):p2align=0, $1{{$}} 255; CHECK-NEXT: return{{$}} 256define void @sti16_a1(i16 *%p, i64 %w) { 257 %v = trunc i64 %w to i16 258 store i16 %v, i16* %p, align 1 259 ret void 260} 261 262; CHECK-LABEL: sti16_a2: 263; CHECK-NEXT: .functype sti16_a2 (i32, i64) -> (){{$}} 264; CHECK-NEXT: i64.store16 0($0), $1{{$}} 265; CHECK-NEXT: return{{$}} 266define void @sti16_a2(i16 *%p, i64 %w) { 267 %v = trunc i64 %w to i16 268 store i16 %v, i16* %p, align 2 269 ret void 270} 271 272; CHECK-LABEL: sti16_a4: 273; CHECK-NEXT: .functype sti16_a4 (i32, i64) -> (){{$}} 274; CHECK-NEXT: i64.store16 0($0), $1{{$}} 275; CHECK-NEXT: return{{$}} 276define void @sti16_a4(i16 *%p, i64 %w) { 277 %v = trunc i64 %w to i16 278 store i16 %v, i16* %p, align 4 279 ret void 280} 281 282; CHECK-LABEL: sti32_a1: 283; CHECK-NEXT: .functype sti32_a1 (i32, i64) -> (){{$}} 284; CHECK-NEXT: i64.store32 0($0):p2align=0, $1{{$}} 285; CHECK-NEXT: return{{$}} 286define void @sti32_a1(i32 *%p, i64 %w) { 287 %v = trunc i64 %w to i32 288 store i32 %v, i32* %p, align 1 289 ret void 290} 291 292; CHECK-LABEL: sti32_a2: 293; CHECK-NEXT: .functype sti32_a2 (i32, i64) -> (){{$}} 294; CHECK-NEXT: i64.store32 0($0):p2align=1, $1{{$}} 295; CHECK-NEXT: return{{$}} 296define void @sti32_a2(i32 *%p, i64 %w) { 297 %v = trunc i64 %w to i32 298 store i32 %v, i32* %p, align 2 299 ret void 300} 301 302; CHECK-LABEL: sti32_a4: 303; CHECK-NEXT: .functype sti32_a4 (i32, i64) -> (){{$}} 304; CHECK-NEXT: i64.store32 0($0), $1{{$}} 305; CHECK-NEXT: return{{$}} 306define void @sti32_a4(i32 *%p, i64 %w) { 307 %v = trunc i64 %w to i32 308 store i32 %v, i32* %p, align 4 309 ret void 310} 311 312; CHECK-LABEL: sti32_a8: 313; CHECK-NEXT: .functype sti32_a8 (i32, i64) -> (){{$}} 314; CHECK-NEXT: i64.store32 0($0), $1{{$}} 315; CHECK-NEXT: return{{$}} 316define void @sti32_a8(i32 *%p, i64 %w) { 317 %v = trunc i64 %w to i32 318 store i32 %v, i32* %p, align 8 319 ret void 320} 321 322;===---------------------------------------------------------------------------- 323; Atomic loads 324;===---------------------------------------------------------------------------- 325 326; Wasm atomics have the alignment field, but it must always have the type's 327; natural alignment. 328 329; CHECK-LABEL: ldi64_atomic_a8: 330; CHECK-NEXT: .functype ldi64_atomic_a8 (i32) -> (i64){{$}} 331; CHECK-NEXT: i64.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}} 332; CHECK-NEXT: return $pop[[NUM]]{{$}} 333define i64 @ldi64_atomic_a8(i64 *%p) { 334 %v = load atomic i64, i64* %p seq_cst, align 8 335 ret i64 %v 336} 337 338; 16 is greater than the default alignment so it is ignored. 339 340; CHECK-LABEL: ldi64_atomic_a16: 341; CHECK-NEXT: .functype ldi64_atomic_a16 (i32) -> (i64){{$}} 342; CHECK-NEXT: i64.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}} 343; CHECK-NEXT: return $pop[[NUM]]{{$}} 344define i64 @ldi64_atomic_a16(i64 *%p) { 345 %v = load atomic i64, i64* %p seq_cst, align 16 346 ret i64 %v 347} 348 349;===---------------------------------------------------------------------------- 350; Atomic stores 351;===---------------------------------------------------------------------------- 352 353; CHECK-LABEL: sti64_atomic_a4: 354; CHECK-NEXT: .functype sti64_atomic_a4 (i32, i64) -> (){{$}} 355; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}} 356; CHECK-NEXT: return{{$}} 357define void @sti64_atomic_a4(i64 *%p, i64 %v) { 358 store atomic i64 %v, i64* %p seq_cst, align 8 359 ret void 360} 361 362; 16 is greater than the default alignment so it is ignored. 363 364; CHECK-LABEL: sti64_atomic_a8: 365; CHECK-NEXT: .functype sti64_atomic_a8 (i32, i64) -> (){{$}} 366; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}} 367; CHECK-NEXT: return{{$}} 368define void @sti64_atomic_a8(i64 *%p, i64 %v) { 369 store atomic i64 %v, i64* %p seq_cst, align 16 370 ret void 371} 372