Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -124,6 +124,8 @@ case WebAssembly::STORE8_I32_S: case WebAssembly::STORE8_I64: case WebAssembly::STORE8_I64_S: + case WebAssembly::ATOMIC_STORE8_I32: + case WebAssembly::ATOMIC_STORE8_I64: return 0; case WebAssembly::LOAD16_S_I32: case WebAssembly::LOAD16_S_I32_S: @@ -141,6 +143,8 @@ case WebAssembly::STORE16_I32_S: case WebAssembly::STORE16_I64: case WebAssembly::STORE16_I64_S: + case WebAssembly::ATOMIC_STORE16_I32: + case WebAssembly::ATOMIC_STORE16_I64: return 1; case WebAssembly::LOAD_I32: case WebAssembly::LOAD_I32_S: @@ -160,6 +164,8 @@ case WebAssembly::ATOMIC_LOAD_I32_S: case WebAssembly::ATOMIC_LOAD32_U_I64: case WebAssembly::ATOMIC_LOAD32_U_I64_S: + case WebAssembly::ATOMIC_STORE_I32: + case WebAssembly::ATOMIC_STORE32_I64: return 2; case WebAssembly::LOAD_I64: case WebAssembly::LOAD_I64_S: @@ -171,6 +177,7 @@ case WebAssembly::STORE_F64_S: case WebAssembly::ATOMIC_LOAD_I64: case WebAssembly::ATOMIC_LOAD_I64_S: + case WebAssembly::ATOMIC_STORE_I64: return 3; default: llvm_unreachable("Only loads and stores have p2align values"); Index: lib/Target/WebAssembly/WebAssemblyInstrAtomics.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrAtomics.td +++ lib/Target/WebAssembly/WebAssemblyInstrAtomics.td @@ -196,7 +196,145 @@ // Atomic stores //===----------------------------------------------------------------------===// -// TODO: add atomic stores here... +let Defs = [ARGUMENTS] in { +defm ATOMIC_STORE_I32 : WebAssemblyStore; +defm ATOMIC_STORE_I64 : WebAssemblyStore; +} // Defs = [ARGUMENTS] + +// We need an 'atomic' version of store patterns because store and atomic_store +// nodes have different operand orders: +// store: (store $val, $ptr) +// atomic_store: (store $ptr, $val) + +let Predicates = [HasAtomics] in { + +// Select stores with no constant offset. +class AStorePatNoOffset : + Pat<(node I32:$addr, ty:$val), (inst 0, 0, $addr, $val)>; +def : AStorePatNoOffset; +def : AStorePatNoOffset; + +// Select stores with a constant offset. + +// Pattern with address + immediate offset +class AStorePatImmOff : + Pat<(storekind (operand I32:$addr, imm:$off), ty:$val), + (inst 0, imm:$off, $addr, ty:$val)>; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; + +class AStorePatGlobalAddr : + Pat<(storekind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)), + ty:$val), + (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>; +def : AStorePatGlobalAddr; +def : AStorePatGlobalAddr; + +class AStorePatExternalSym : + Pat<(storekind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)), + ty:$val), + (inst 0, texternalsym:$off, I32:$addr, ty:$val)>; +def : AStorePatExternalSym; +def : AStorePatExternalSym; + +// Select stores with just a constant offset. +class AStorePatOffsetOnly : + Pat<(storekind imm:$off, ty:$val), + (inst 0, imm:$off, (CONST_I32 0), ty:$val)>; +def : AStorePatOffsetOnly; +def : AStorePatOffsetOnly; + +class AStorePatGlobalAddrOffOnly : + Pat<(storekind (WebAssemblywrapper tglobaladdr:$off), ty:$val), + (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>; +def : AStorePatGlobalAddrOffOnly; +def : AStorePatGlobalAddrOffOnly; + +class AStorePatExternSymOffOnly : + Pat<(storekind (WebAssemblywrapper texternalsym:$off), ty:$val), + (inst 0, texternalsym:$off, (CONST_I32 0), ty:$val)>; +def : AStorePatExternSymOffOnly; +def : AStorePatExternSymOffOnly; + +} // Predicates = [HasAtomics] + +// Truncating stores. +let Defs = [ARGUMENTS] in { +defm ATOMIC_STORE8_I32 : WebAssemblyStore; +defm ATOMIC_STORE16_I32 : WebAssemblyStore; +defm ATOMIC_STORE8_I64 : WebAssemblyStore; +defm ATOMIC_STORE16_I64 : WebAssemblyStore; +defm ATOMIC_STORE32_I64 : WebAssemblyStore; +} // Defs = [ARGUMENTS] + +// Fragments for truncating stores. + +// We don't have single truncating atomic store instructions. For 32-bit +// instructions, we just need to match bare atomic stores. On the other hand, +// truncating stores from i64 values are once truncated to i32 first. +class trunc_astore_64 : + PatFrag<(ops node:$addr, node:$val), + (storekind node:$addr, (i32 (trunc (i64 node:$val))))>; +def trunc_astore_8_64 : trunc_astore_64; +def trunc_astore_16_64 : trunc_astore_64; +def trunc_astore_32_64 : trunc_astore_64; + +let Predicates = [HasAtomics] in { + +// Truncating stores with no constant offset +def : AStorePatNoOffset; +def : AStorePatNoOffset; +def : AStorePatNoOffset; +def : AStorePatNoOffset; +def : AStorePatNoOffset; + +// Truncating stores with a constant offset +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; +def : AStorePatImmOff; + +def : AStorePatGlobalAddr; +def : AStorePatGlobalAddr; +def : AStorePatGlobalAddr; +def : AStorePatGlobalAddr; +def : AStorePatGlobalAddr; + +def : AStorePatExternalSym; +def : AStorePatExternalSym; +def : AStorePatExternalSym; +def : AStorePatExternalSym; +def : AStorePatExternalSym; + +// Truncating stores with just a constant offset +def : AStorePatOffsetOnly; +def : AStorePatOffsetOnly; +def : AStorePatOffsetOnly; +def : AStorePatOffsetOnly; +def : AStorePatOffsetOnly; + +def : AStorePatGlobalAddrOffOnly; +def : AStorePatGlobalAddrOffOnly; +def : AStorePatGlobalAddrOffOnly; +def : AStorePatGlobalAddrOffOnly; +def : AStorePatGlobalAddrOffOnly; + +def : AStorePatExternSymOffOnly; +def : AStorePatExternSymOffOnly; +def : AStorePatExternSymOffOnly; +def : AStorePatExternSymOffOnly; +def : AStorePatExternSymOffOnly; + +} // Predicates = [HasAtomics] //===----------------------------------------------------------------------===// // Low-level exclusive operations Index: lib/Target/WebAssembly/WebAssemblyInstrMemory.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ lib/Target/WebAssembly/WebAssemblyInstrMemory.td @@ -27,7 +27,6 @@ def regPlusImm : PatFrag<(ops node:$addr, node:$off), (add node:$addr, node:$off), [{ return N->getFlags().hasNoUnsignedWrap(); }]>; - // Treat an 'or' node as an 'add' if the or'ed bits are known to be zero. def or_is_add : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs),[{ if (ConstantSDNode *CN = dyn_cast(N->getOperand(1))) @@ -173,6 +172,7 @@ def : LoadPatNoOffset; // Select extending loads with a constant offset. + def : LoadPatImmOff; def : LoadPatImmOff; def : LoadPatImmOff; Index: lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp +++ lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp @@ -118,6 +118,13 @@ case WebAssembly::STORE8_I64: case WebAssembly::STORE16_I64: case WebAssembly::STORE32_I64: + case WebAssembly::ATOMIC_STORE_I32: + case WebAssembly::ATOMIC_STORE8_I32: + case WebAssembly::ATOMIC_STORE16_I32: + case WebAssembly::ATOMIC_STORE_I64: + case WebAssembly::ATOMIC_STORE8_I64: + case WebAssembly::ATOMIC_STORE16_I64: + case WebAssembly::ATOMIC_STORE32_I64: RewriteP2Align(MI, WebAssembly::StoreP2AlignOperandNo); break; default: Index: test/CodeGen/WebAssembly/i32-load-store-alignment.ll =================================================================== --- test/CodeGen/WebAssembly/i32-load-store-alignment.ll +++ test/CodeGen/WebAssembly/i32-load-store-alignment.ll @@ -5,6 +5,8 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" +; Loads. + ; CHECK-LABEL: ldi32_a1: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} @@ -236,3 +238,21 @@ %v = load atomic i32, i32* %p seq_cst, align 8 ret i32 %v } + +; CHECK-LABEL: sti32_atomic_a4: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_atomic_a4(i32 *%p, i32 %v) { + store atomic i32 %v, i32* %p seq_cst, align 4 + ret void +} + +; CHECK-LABEL: sti32_atomic_a8: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti32_atomic_a8(i32 *%p, i32 %v) { + store atomic i32 %v, i32* %p seq_cst, align 8 + ret void +} Index: test/CodeGen/WebAssembly/i64-load-store-alignment.ll =================================================================== --- test/CodeGen/WebAssembly/i64-load-store-alignment.ll +++ test/CodeGen/WebAssembly/i64-load-store-alignment.ll @@ -5,6 +5,8 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" +; Loads. + ; CHECK-LABEL: ldi64_a1: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i64{{$}} @@ -325,6 +327,9 @@ } ; Atomics. +; Wasm atomics have the alignment field, but it must always have the type's +; natural alignment. + ; CHECK-LABEL: ldi64_atomic_a8: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i64{{$}} @@ -336,7 +341,6 @@ } ; 16 is greater than the default alignment so it is ignored. - ; CHECK-LABEL: ldi64_atomic_a16: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i64{{$}} @@ -346,3 +350,22 @@ %v = load atomic i64, i64* %p seq_cst, align 16 ret i64 %v } + +; CHECK-LABEL: sti64_atomic_a4: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64_atomic_a4(i64 *%p, i64 %v) { + store atomic i64 %v, i64* %p seq_cst, align 8 + ret void +} + +; 16 is greater than the default alignment so it is ignored. +; CHECK-LABEL: sti64_atomic_a8: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @sti64_atomic_a8(i64 *%p, i64 %v) { + store atomic i64 %v, i64* %p seq_cst, align 16 + ret void +} Index: test/CodeGen/WebAssembly/offset-atomics.ll =================================================================== --- test/CodeGen/WebAssembly/offset-atomics.ll +++ test/CodeGen/WebAssembly/offset-atomics.ll @@ -6,6 +6,8 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" +; Basic load. + ; CHECK-LABEL: load_i32_no_offset: ; CHECK: i32.atomic.load $push[[NUM:[0-9]+]]=, 0($0){{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} @@ -154,6 +156,148 @@ ret i32 %conv } +; Same as above but with store. + +; CHECK-LABEL: store_i32_no_offset: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @store_i32_no_offset(i32 *%p, i32 %v) { + store atomic i32 %v, i32* %p seq_cst, align 4 + ret void +} + +; Same as above but with store. + +; CHECK-LABEL: store_i32_with_folded_offset: +; CHECK: i32.atomic.store 24($0), $pop0{{$}} +define void @store_i32_with_folded_offset(i32* %p) { + %q = ptrtoint i32* %p to i32 + %r = add nuw i32 %q, 24 + %s = inttoptr i32 %r to i32* + store atomic i32 0, i32* %s seq_cst, align 4 + ret void +} + +; Same as above but with store. + +; CHECK-LABEL: store_i32_with_folded_gep_offset: +; CHECK: i32.atomic.store 24($0), $pop0{{$}} +define void @store_i32_with_folded_gep_offset(i32* %p) { + %s = getelementptr inbounds i32, i32* %p, i32 6 + store atomic i32 0, i32* %s seq_cst, align 4 + ret void +} + +; Same as above but with store. + +; CHECK-LABEL: store_i32_with_unfolded_gep_negative_offset: +; CHECK: i32.const $push0=, -24{{$}} +; CHECK: i32.add $push1=, $0, $pop0{{$}} +; CHECK: i32.atomic.store 0($pop1), $pop2{{$}} +define void @store_i32_with_unfolded_gep_negative_offset(i32* %p) { + %s = getelementptr inbounds i32, i32* %p, i32 -6 + store atomic i32 0, i32* %s seq_cst, align 4 + ret void +} + +; Same as above but with store. + +; CHECK-LABEL: store_i32_with_unfolded_offset: +; CHECK: i32.const $push0=, 24{{$}} +; CHECK: i32.add $push1=, $0, $pop0{{$}} +; CHECK: i32.atomic.store 0($pop1), $pop2{{$}} +define void @store_i32_with_unfolded_offset(i32* %p) { + %q = ptrtoint i32* %p to i32 + %r = add nsw i32 %q, 24 + %s = inttoptr i32 %r to i32* + store atomic i32 0, i32* %s seq_cst, align 4 + ret void +} + +; Same as above but with store. + +; CHECK-LABEL: store_i32_with_unfolded_gep_offset: +; CHECK: i32.const $push0=, 24{{$}} +; CHECK: i32.add $push1=, $0, $pop0{{$}} +; CHECK: i32.atomic.store 0($pop1), $pop2{{$}} +define void @store_i32_with_unfolded_gep_offset(i32* %p) { + %s = getelementptr i32, i32* %p, i32 6 + store atomic i32 0, i32* %s seq_cst, align 4 + ret void +} + +; Same as above but with store with i64. + +; CHECK-LABEL: store_i64_no_offset: +; CHECK-NEXT: .param i32, i64{{$}} +; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}} +; CHECK-NEXT: return{{$}} +define void @store_i64_no_offset(i64 *%p, i64 %v) { + store atomic i64 %v, i64* %p seq_cst, align 8 + ret void +} + +; Same as above but with store with i64. + +; CHECK-LABEL: store_i64_with_folded_offset: +; CHECK: i64.atomic.store 24($0), $pop0{{$}} +define void @store_i64_with_folded_offset(i64* %p) { + %q = ptrtoint i64* %p to i32 + %r = add nuw i32 %q, 24 + %s = inttoptr i32 %r to i64* + store atomic i64 0, i64* %s seq_cst, align 8 + ret void +} + +; Same as above but with store with i64. + +; CHECK-LABEL: store_i64_with_folded_gep_offset: +; CHECK: i64.atomic.store 24($0), $pop0{{$}} +define void @store_i64_with_folded_gep_offset(i64* %p) { + %s = getelementptr inbounds i64, i64* %p, i32 3 + store atomic i64 0, i64* %s seq_cst, align 8 + ret void +} + +; Same as above but with store with i64. + +; CHECK-LABEL: store_i64_with_unfolded_gep_negative_offset: +; CHECK: i32.const $push0=, -24{{$}} +; CHECK: i32.add $push1=, $0, $pop0{{$}} +; CHECK: i64.atomic.store 0($pop1), $pop2{{$}} +define void @store_i64_with_unfolded_gep_negative_offset(i64* %p) { + %s = getelementptr inbounds i64, i64* %p, i32 -3 + store atomic i64 0, i64* %s seq_cst, align 8 + ret void +} + +; Same as above but with store with i64. + +; CHECK-LABEL: store_i64_with_unfolded_offset: +; CHECK: i32.const $push0=, 24{{$}} +; CHECK: i32.add $push1=, $0, $pop0{{$}} +; CHECK: i64.atomic.store 0($pop1), $pop2{{$}} +define void @store_i64_with_unfolded_offset(i64* %p) { + %q = ptrtoint i64* %p to i32 + %r = add nsw i32 %q, 24 + %s = inttoptr i32 %r to i64* + store atomic i64 0, i64* %s seq_cst, align 8 + ret void +} + +; Same as above but with store with i64. + +; CHECK-LABEL: store_i64_with_unfolded_gep_offset: +; CHECK: i32.const $push0=, 24{{$}} +; CHECK: i32.add $push1=, $0, $pop0{{$}} +; CHECK: i64.atomic.store 0($pop1), $pop2{{$}} +define void @store_i64_with_unfolded_gep_offset(i64* %p) { + %s = getelementptr i64, i64* %p, i32 3 + store atomic i64 0, i64* %s seq_cst, align 8 + ret void +} + ; When loading from a fixed address, materialize a zero. ; CHECK-LABEL: load_i32_from_numeric_address @@ -175,6 +319,25 @@ ret i32 %t } +; CHECK-LABEL: store_i32_to_numeric_address: +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: i32.const $push1=, 0{{$}} +; CHECK-NEXT: i32.atomic.store 42($pop0), $pop1{{$}} +define void @store_i32_to_numeric_address() { + %s = inttoptr i32 42 to i32* + store atomic i32 0, i32* %s seq_cst, align 4 + ret void +} + +; CHECK-LABEL: store_i32_to_global_address: +; CHECK: i32.const $push0=, 0{{$}} +; CHECK: i32.const $push1=, 0{{$}} +; CHECK: i32.atomic.store gv($pop0), $pop1{{$}} +define void @store_i32_to_global_address() { + store atomic i32 0, i32* @gv seq_cst, align 4 + ret void +} + ; Fold an offset into a sign-extending load. ; CHECK-LABEL: load_i8_s_with_folded_offset: @@ -305,3 +468,158 @@ %v = load atomic i8, i8* %p seq_cst, align 1 ret i8 %v } + +; Fold an offset into a truncating store. + +; CHECK-LABEL: store_i8_with_folded_offset: +; CHECK: i32.atomic.store8 24($0), $pop0{{$}} +define void @store_i8_with_folded_offset(i8* %p) { + %q = ptrtoint i8* %p to i32 + %r = add nuw i32 %q, 24 + %s = inttoptr i32 %r to i8* + store atomic i8 0, i8* %s seq_cst, align 1 + ret void +} + +; CHECK-LABEL: store_i16_with_folded_offset: +; CHECK: i32.atomic.store16 24($0), $pop0{{$}} +define void @store_i16_with_folded_offset(i16* %p) { + %q = ptrtoint i16* %p to i32 + %r = add nuw i32 %q, 24 + %s = inttoptr i32 %r to i16* + store atomic i16 0, i16* %s seq_cst, align 2 + ret void +} + +; CHECK-LABEL: store_i8_i64_with_folded_offset: +; CHECK: i64.atomic.store8 24($0), $1{{$}} +define void @store_i8_i64_with_folded_offset(i8* %p, i64 %v) { + %q = ptrtoint i8* %p to i32 + %r = add nuw i32 %q, 24 + %s = inttoptr i32 %r to i8* + %t = trunc i64 %v to i8 + store atomic i8 %t, i8* %s seq_cst, align 1 + ret void +} + +; CHECK-LABEL: store_i16_i64_with_folded_offset: +; CHECK: i64.atomic.store16 24($0), $1{{$}} +define void @store_i16_i64_with_folded_offset(i16* %p, i64 %v) { + %q = ptrtoint i16* %p to i32 + %r = add nuw i32 %q, 24 + %s = inttoptr i32 %r to i16* + %t = trunc i64 %v to i16 + store atomic i16 %t, i16* %s seq_cst, align 2 + ret void +} + +; CHECK-LABEL: store_i32_i64_with_folded_offset: +; CHECK: i64.atomic.store32 24($0), $1{{$}} +define void @store_i32_i64_with_folded_offset(i32* %p, i64 %v) { + %q = ptrtoint i32* %p to i32 + %r = add nuw i32 %q, 24 + %s = inttoptr i32 %r to i32* + %t = trunc i64 %v to i32 + store atomic i32 %t, i32* %s seq_cst, align 4 + ret void +} + +; Fold a gep offset into a truncating store. + +; CHECK-LABEL: store_i8_with_folded_gep_offset: +; CHECK: i32.atomic.store8 24($0), $pop0{{$}} +define void @store_i8_with_folded_gep_offset(i8* %p) { + %s = getelementptr inbounds i8, i8* %p, i32 24 + store atomic i8 0, i8* %s seq_cst, align 1 + ret void +} + +; CHECK-LABEL: store_i16_with_folded_gep_offset: +; CHECK: i32.atomic.store16 48($0), $pop0{{$}} +define void @store_i16_with_folded_gep_offset(i16* %p) { + %s = getelementptr inbounds i16, i16* %p, i32 24 + store atomic i16 0, i16* %s seq_cst, align 2 + ret void +} + +; CHECK-LABEL: store_i8_i64_with_folded_gep_offset: +; CHECK: i64.atomic.store8 24($0), $1{{$}} +define void @store_i8_i64_with_folded_gep_offset(i8* %p, i64 %v) { + %s = getelementptr inbounds i8, i8* %p, i32 24 + %t = trunc i64 %v to i8 + store atomic i8 %t, i8* %s seq_cst, align 2 + ret void +} + +; CHECK-LABEL: store_i16_i64_with_folded_gep_offset: +; CHECK: i64.atomic.store16 48($0), $1{{$}} +define void @store_i16_i64_with_folded_gep_offset(i16* %p, i64 %v) { + %s = getelementptr inbounds i16, i16* %p, i32 24 + %t = trunc i64 %v to i16 + store atomic i16 %t, i16* %s seq_cst, align 2 + ret void +} + +; CHECK-LABEL: store_i32_i64_with_folded_gep_offset: +; CHECK: i64.atomic.store32 96($0), $1{{$}} +define void @store_i32_i64_with_folded_gep_offset(i32* %p, i64 %v) { + %s = getelementptr inbounds i32, i32* %p, i32 24 + %t = trunc i64 %v to i32 + store atomic i32 %t, i32* %s seq_cst, align 4 + ret void +} + +; Fold an or_is_add pattern based offset into a truncating store. + +; CHECK-LABEL: store_i8_with_folded_or_offset: +; CHECK: i32.atomic.store8 2($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}} +define void @store_i8_with_folded_or_offset(i32 %x) { + %and = and i32 %x, -4 + %p = inttoptr i32 %and to i8* + %arrayidx = getelementptr inbounds i8, i8* %p, i32 2 + store atomic i8 0, i8* %arrayidx seq_cst, align 1 + ret void +} + +; CHECK-LABEL: store_i16_with_folded_or_offset: +; CHECK: i32.atomic.store16 4($pop{{[0-9]+}}), $pop{{[0-9]+}}{{$}} +define void @store_i16_with_folded_or_offset(i32 %x) { + %and = and i32 %x, -4 + %p = inttoptr i32 %and to i16* + %arrayidx = getelementptr inbounds i16, i16* %p, i32 2 + store atomic i16 0, i16* %arrayidx seq_cst, align 2 + ret void +} + +; CHECK-LABEL: store_i8_i64_with_folded_or_offset: +; CHECK: i64.atomic.store8 2($pop{{[0-9]+}}), $1{{$}} +define void @store_i8_i64_with_folded_or_offset(i32 %x, i64 %v) { + %and = and i32 %x, -4 + %p = inttoptr i32 %and to i8* + %arrayidx = getelementptr inbounds i8, i8* %p, i32 2 + %t = trunc i64 %v to i8 + store atomic i8 %t, i8* %arrayidx seq_cst, align 1 + ret void +} + +; CHECK-LABEL: store_i16_i64_with_folded_or_offset: +; CHECK: i64.atomic.store16 4($pop{{[0-9]+}}), $1{{$}} +define void @store_i16_i64_with_folded_or_offset(i32 %x, i64 %v) { + %and = and i32 %x, -4 + %p = inttoptr i32 %and to i16* + %arrayidx = getelementptr inbounds i16, i16* %p, i32 2 + %t = trunc i64 %v to i16 + store atomic i16 %t, i16* %arrayidx seq_cst, align 2 + ret void +} + +; CHECK-LABEL: store_i32_i64_with_folded_or_offset: +; CHECK: i64.atomic.store32 8($pop{{[0-9]+}}), $1{{$}} +define void @store_i32_i64_with_folded_or_offset(i32 %x, i64 %v) { + %and = and i32 %x, -4 + %p = inttoptr i32 %and to i32* + %arrayidx = getelementptr inbounds i32, i32* %p, i32 2 + %t = trunc i64 %v to i32 + store atomic i32 %t, i32* %arrayidx seq_cst, align 4 + ret void +} Index: test/CodeGen/WebAssembly/store-trunc-atomic.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/store-trunc-atomic.ll @@ -0,0 +1,46 @@ +; RUN: llc < %s -mattr=+atomics,+sign-ext -asm-verbose=false -disable-wasm-explicit-locals | FileCheck %s + +; Test that truncating stores are assembled properly. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK-LABEL: trunc_i8_i32: +; CHECK: i32.atomic.store8 0($0), $1{{$}} +define void @trunc_i8_i32(i8 *%p, i32 %v) { + %t = trunc i32 %v to i8 + store atomic i8 %t, i8* %p seq_cst, align 1 + ret void +} + +; CHECK-LABEL: trunc_i16_i32: +; CHECK: i32.atomic.store16 0($0), $1{{$}} +define void @trunc_i16_i32(i16 *%p, i32 %v) { + %t = trunc i32 %v to i16 + store atomic i16 %t, i16* %p seq_cst, align 2 + ret void +} + +; CHECK-LABEL: trunc_i8_i64: +; CHECK: i64.atomic.store8 0($0), $1{{$}} +define void @trunc_i8_i64(i8 *%p, i64 %v) { + %t = trunc i64 %v to i8 + store atomic i8 %t, i8* %p seq_cst, align 1 + ret void +} + +; CHECK-LABEL: trunc_i16_i64: +; CHECK: i64.atomic.store16 0($0), $1{{$}} +define void @trunc_i16_i64(i16 *%p, i64 %v) { + %t = trunc i64 %v to i16 + store atomic i16 %t, i16* %p seq_cst, align 2 + ret void +} + +; CHECK-LABEL: trunc_i32_i64: +; CHECK: i64.atomic.store32 0($0), $1{{$}} +define void @trunc_i32_i64(i32 *%p, i64 %v) { + %t = trunc i64 %v to i32 + store atomic i32 %t, i32* %p seq_cst, align 4 + ret void +}