mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-21 03:53:04 +02:00
[WebAssembly] Support for binary atomic RMW instructions
Summary: This adds support for binary atomic read-modify-write instructions: add, sub, and, or, xor, and xchg. This does not yet support translations of some of LLVM IR atomicrmw instructions (nand, max, min, umax, and umin) that do not have a direct counterpart in wasm instructions. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D49088 llvm-svn: 336615
This commit is contained in:
parent
647946fa14
commit
956ec9b405
@ -128,6 +128,30 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
|
||||
case WebAssembly::ATOMIC_STORE8_I32_S:
|
||||
case WebAssembly::ATOMIC_STORE8_I64:
|
||||
case WebAssembly::ATOMIC_STORE8_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_ADD_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_ADD_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_ADD_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_SUB_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_SUB_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_SUB_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_AND_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_AND_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_AND_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_OR_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_OR_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_OR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XOR_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XOR_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XOR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XCHG_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XCHG_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XCHG_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XCHG_I64_S:
|
||||
return 0;
|
||||
case WebAssembly::LOAD16_S_I32:
|
||||
case WebAssembly::LOAD16_S_I32_S:
|
||||
@ -149,6 +173,30 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
|
||||
case WebAssembly::ATOMIC_STORE16_I32_S:
|
||||
case WebAssembly::ATOMIC_STORE16_I64:
|
||||
case WebAssembly::ATOMIC_STORE16_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_ADD_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_ADD_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_ADD_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_SUB_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_SUB_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_SUB_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_AND_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_AND_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_AND_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_OR_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_OR_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_OR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XOR_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XOR_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XOR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XCHG_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XCHG_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XCHG_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XCHG_I64_S:
|
||||
return 1;
|
||||
case WebAssembly::LOAD_I32:
|
||||
case WebAssembly::LOAD_I32_S:
|
||||
@ -172,6 +220,30 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
|
||||
case WebAssembly::ATOMIC_STORE_I32_S:
|
||||
case WebAssembly::ATOMIC_STORE32_I64:
|
||||
case WebAssembly::ATOMIC_STORE32_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_ADD_I32:
|
||||
case WebAssembly::ATOMIC_RMW_ADD_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW32_U_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW32_U_ADD_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_SUB_I32:
|
||||
case WebAssembly::ATOMIC_RMW_SUB_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW32_U_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW32_U_SUB_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_AND_I32:
|
||||
case WebAssembly::ATOMIC_RMW_AND_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW32_U_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW32_U_AND_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_OR_I32:
|
||||
case WebAssembly::ATOMIC_RMW_OR_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW32_U_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW32_U_OR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_XOR_I32:
|
||||
case WebAssembly::ATOMIC_RMW_XOR_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW32_U_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW32_U_XOR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_XCHG_I32:
|
||||
case WebAssembly::ATOMIC_RMW_XCHG_I32_S:
|
||||
case WebAssembly::ATOMIC_RMW32_U_XCHG_I64:
|
||||
case WebAssembly::ATOMIC_RMW32_U_XCHG_I64_S:
|
||||
return 2;
|
||||
case WebAssembly::LOAD_I64:
|
||||
case WebAssembly::LOAD_I64_S:
|
||||
@ -185,6 +257,18 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
|
||||
case WebAssembly::ATOMIC_LOAD_I64_S:
|
||||
case WebAssembly::ATOMIC_STORE_I64:
|
||||
case WebAssembly::ATOMIC_STORE_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW_ADD_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW_SUB_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW_AND_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW_OR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW_XOR_I64_S:
|
||||
case WebAssembly::ATOMIC_RMW_XCHG_I64:
|
||||
case WebAssembly::ATOMIC_RMW_XCHG_I64_S:
|
||||
return 3;
|
||||
default:
|
||||
llvm_unreachable("Only loads and stores have p2align values");
|
||||
|
@ -330,16 +330,328 @@ def : AStorePatExternSymOffOnly<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
|
||||
} // Predicates = [HasAtomics]
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Low-level exclusive operations
|
||||
// Atomic binary read-modify-writes
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// TODO: add exclusive operations here...
|
||||
let Defs = [ARGUMENTS] in {
|
||||
|
||||
// Load-exclusives.
|
||||
multiclass WebAssemblyBinRMW<WebAssemblyRegClass rc, string Name, int Opcode> {
|
||||
defm "" : I<(outs rc:$dst),
|
||||
(ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
|
||||
(outs), (ins P2Align:$p2align, offset32_op:$off), [],
|
||||
!strconcat(Name, "\t$dst, ${off}(${addr})${p2align}, $val"),
|
||||
!strconcat(Name, "\t${off}, ${p2align}"), Opcode>;
|
||||
}
|
||||
|
||||
// Store-exclusives.
|
||||
defm ATOMIC_RMW_ADD_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.add", 0xfe1e>;
|
||||
defm ATOMIC_RMW_ADD_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.add", 0xfe1f>;
|
||||
defm ATOMIC_RMW8_U_ADD_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.add", 0xfe20>;
|
||||
defm ATOMIC_RMW16_U_ADD_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.add", 0xfe21>;
|
||||
defm ATOMIC_RMW8_U_ADD_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.add", 0xfe22>;
|
||||
defm ATOMIC_RMW16_U_ADD_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.add", 0xfe23>;
|
||||
defm ATOMIC_RMW32_U_ADD_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.add", 0xfe24>;
|
||||
|
||||
// Store-release-exclusives.
|
||||
defm ATOMIC_RMW_SUB_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.sub", 0xfe25>;
|
||||
defm ATOMIC_RMW_SUB_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.sub", 0xfe26>;
|
||||
defm ATOMIC_RMW8_U_SUB_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.sub", 0xfe27>;
|
||||
defm ATOMIC_RMW16_U_SUB_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.sub", 0xfe28>;
|
||||
defm ATOMIC_RMW8_U_SUB_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.sub", 0xfe29>;
|
||||
defm ATOMIC_RMW16_U_SUB_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.sub", 0xfe2a>;
|
||||
defm ATOMIC_RMW32_U_SUB_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.sub", 0xfe2b>;
|
||||
|
||||
// And clear exclusive.
|
||||
defm ATOMIC_RMW_AND_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.and", 0xfe2c>;
|
||||
defm ATOMIC_RMW_AND_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.and", 0xfe2d>;
|
||||
defm ATOMIC_RMW8_U_AND_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.and", 0xfe2e>;
|
||||
defm ATOMIC_RMW16_U_AND_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.and", 0xfe2f>;
|
||||
defm ATOMIC_RMW8_U_AND_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.and", 0xfe30>;
|
||||
defm ATOMIC_RMW16_U_AND_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.and", 0xfe31>;
|
||||
defm ATOMIC_RMW32_U_AND_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.and", 0xfe32>;
|
||||
|
||||
defm ATOMIC_RMW_OR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.or", 0xfe33>;
|
||||
defm ATOMIC_RMW_OR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.or", 0xfe34>;
|
||||
defm ATOMIC_RMW8_U_OR_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.or", 0xfe35>;
|
||||
defm ATOMIC_RMW16_U_OR_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.or", 0xfe36>;
|
||||
defm ATOMIC_RMW8_U_OR_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.or", 0xfe37>;
|
||||
defm ATOMIC_RMW16_U_OR_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.or", 0xfe38>;
|
||||
defm ATOMIC_RMW32_U_OR_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.or", 0xfe39>;
|
||||
|
||||
defm ATOMIC_RMW_XOR_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.xor", 0xfe3a>;
|
||||
defm ATOMIC_RMW_XOR_I64 : WebAssemblyBinRMW<I64, "i64.atomic.rmw.xor", 0xfe3b>;
|
||||
defm ATOMIC_RMW8_U_XOR_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.xor", 0xfe3c>;
|
||||
defm ATOMIC_RMW16_U_XOR_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.xor", 0xfe3d>;
|
||||
defm ATOMIC_RMW8_U_XOR_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.xor", 0xfe3e>;
|
||||
defm ATOMIC_RMW16_U_XOR_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.xor", 0xfe3f>;
|
||||
defm ATOMIC_RMW32_U_XOR_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.xor", 0xfe40>;
|
||||
|
||||
defm ATOMIC_RMW_XCHG_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw.xchg", 0xfe41>;
|
||||
defm ATOMIC_RMW_XCHG_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw.xchg", 0xfe42>;
|
||||
defm ATOMIC_RMW8_U_XCHG_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw8_u.xchg", 0xfe43>;
|
||||
defm ATOMIC_RMW16_U_XCHG_I32 :
|
||||
WebAssemblyBinRMW<I32, "i32.atomic.rmw16_u.xchg", 0xfe44>;
|
||||
defm ATOMIC_RMW8_U_XCHG_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw8_u.xchg", 0xfe45>;
|
||||
defm ATOMIC_RMW16_U_XCHG_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw16_u.xchg", 0xfe46>;
|
||||
defm ATOMIC_RMW32_U_XCHG_I64 :
|
||||
WebAssemblyBinRMW<I64, "i64.atomic.rmw32_u.xchg", 0xfe47>;
|
||||
}
|
||||
|
||||
// Select binary RMWs with no constant offset.
|
||||
class BinRMWPatNoOffset<ValueType ty, PatFrag kind, NI inst> :
|
||||
Pat<(ty (kind I32:$addr, ty:$val)), (inst 0, 0, I32:$addr, ty:$val)>;
|
||||
|
||||
// Select binary RMWs with a constant offset.
|
||||
|
||||
// Pattern with address + immediate offset
|
||||
class BinRMWPatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> :
|
||||
Pat<(ty (kind (operand I32:$addr, imm:$off), ty:$val)),
|
||||
(inst 0, imm:$off, I32:$addr, ty:$val)>;
|
||||
|
||||
class BinRMWPatGlobalAddr<ValueType ty, PatFrag kind, NI inst> :
|
||||
Pat<(ty (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
ty:$val)),
|
||||
(inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>;
|
||||
|
||||
class BinRMWPatExternalSym<ValueType ty, PatFrag kind, NI inst> :
|
||||
Pat<(ty (kind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
|
||||
ty:$val)),
|
||||
(inst 0, texternalsym:$off, I32:$addr, ty:$val)>;
|
||||
|
||||
// Select binary RMWs with just a constant offset.
|
||||
class BinRMWPatOffsetOnly<ValueType ty, PatFrag kind, NI inst> :
|
||||
Pat<(ty (kind imm:$off, ty:$val)),
|
||||
(inst 0, imm:$off, (CONST_I32 0), ty:$val)>;
|
||||
|
||||
class BinRMWPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
|
||||
Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off), ty:$val)),
|
||||
(inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>;
|
||||
|
||||
class BinRMWPatExternSymOffOnly<ValueType ty, PatFrag kind, NI inst> :
|
||||
Pat<(ty (kind (WebAssemblywrapper texternalsym:$off), ty:$val)),
|
||||
(inst 0, texternalsym:$off, (CONST_I32 0), ty:$val)>;
|
||||
|
||||
// Patterns for various addressing modes.
|
||||
multiclass BinRMWPattern<PatFrag rmw_32, PatFrag rmw_64, NI inst_32,
|
||||
NI inst_64> {
|
||||
def : BinRMWPatNoOffset<i32, rmw_32, inst_32>;
|
||||
def : BinRMWPatNoOffset<i64, rmw_64, inst_64>;
|
||||
|
||||
def : BinRMWPatImmOff<i32, rmw_32, regPlusImm, inst_32>;
|
||||
def : BinRMWPatImmOff<i64, rmw_64, regPlusImm, inst_64>;
|
||||
def : BinRMWPatImmOff<i32, rmw_32, or_is_add, inst_32>;
|
||||
def : BinRMWPatImmOff<i64, rmw_64, or_is_add, inst_64>;
|
||||
|
||||
def : BinRMWPatGlobalAddr<i32, rmw_32, inst_32>;
|
||||
def : BinRMWPatGlobalAddr<i64, rmw_64, inst_64>;
|
||||
|
||||
def : BinRMWPatExternalSym<i32, rmw_32, inst_32>;
|
||||
def : BinRMWPatExternalSym<i64, rmw_64, inst_64>;
|
||||
|
||||
def : BinRMWPatOffsetOnly<i32, rmw_32, inst_32>;
|
||||
def : BinRMWPatOffsetOnly<i64, rmw_64, inst_64>;
|
||||
|
||||
def : BinRMWPatGlobalAddrOffOnly<i32, rmw_32, inst_32>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i64, rmw_64, inst_64>;
|
||||
|
||||
def : BinRMWPatExternSymOffOnly<i32, rmw_32, inst_32>;
|
||||
def : BinRMWPatExternSymOffOnly<i64, rmw_64, inst_64>;
|
||||
}
|
||||
|
||||
let Predicates = [HasAtomics] in {
|
||||
defm : BinRMWPattern<atomic_load_add_32, atomic_load_add_64, ATOMIC_RMW_ADD_I32,
|
||||
ATOMIC_RMW_ADD_I64>;
|
||||
defm : BinRMWPattern<atomic_load_sub_32, atomic_load_sub_64, ATOMIC_RMW_SUB_I32,
|
||||
ATOMIC_RMW_SUB_I64>;
|
||||
defm : BinRMWPattern<atomic_load_and_32, atomic_load_and_64, ATOMIC_RMW_AND_I32,
|
||||
ATOMIC_RMW_AND_I64>;
|
||||
defm : BinRMWPattern<atomic_load_or_32, atomic_load_or_64, ATOMIC_RMW_OR_I32,
|
||||
ATOMIC_RMW_OR_I64>;
|
||||
defm : BinRMWPattern<atomic_load_xor_32, atomic_load_xor_64, ATOMIC_RMW_XOR_I32,
|
||||
ATOMIC_RMW_XOR_I64>;
|
||||
defm : BinRMWPattern<atomic_swap_32, atomic_swap_64, ATOMIC_RMW_XCHG_I32,
|
||||
ATOMIC_RMW_XCHG_I64>;
|
||||
} // Predicates = [HasAtomics]
|
||||
|
||||
// Truncating & zero-extending binary RMW patterns.
|
||||
// These are combined patterns of truncating store patterns and zero-extending
|
||||
// load patterns above.
|
||||
class zext_bin_rmw_8_32<PatFrag kind> :
|
||||
PatFrag<(ops node:$addr, node:$val),
|
||||
(and (i32 (kind node:$addr, node:$val)), 255)>;
|
||||
class zext_bin_rmw_16_32<PatFrag kind> :
|
||||
PatFrag<(ops node:$addr, node:$val),
|
||||
(and (i32 (kind node:$addr, node:$val)), 65535)>;
|
||||
class zext_bin_rmw_8_64<PatFrag kind> :
|
||||
PatFrag<(ops node:$addr, node:$val),
|
||||
(and (i64 (anyext (i32 (kind node:$addr,
|
||||
(i32 (trunc (i64 node:$val))))))), 255)>;
|
||||
class zext_bin_rmw_16_64<PatFrag kind> :
|
||||
PatFrag<(ops node:$addr, node:$val),
|
||||
(and (i64 (anyext (i32 (kind node:$addr,
|
||||
(i32 (trunc (i64 node:$val))))))), 65535)>;
|
||||
class zext_bin_rmw_32_64<PatFrag kind> :
|
||||
PatFrag<(ops node:$addr, node:$val),
|
||||
(zext (i32 (kind node:$addr, (i32 (trunc (i64 node:$val))))))>;
|
||||
|
||||
// Truncating & sign-extending binary RMW patterns.
|
||||
// These are combined patterns of truncating store patterns and sign-extending
|
||||
// load patterns above. We match subword RMWs (for 32-bit) and anyext RMWs (for
|
||||
// 64-bit) and select a zext RMW; the next instruction will be sext_inreg which
|
||||
// is selected by itself.
|
||||
class sext_bin_rmw_8_32<PatFrag kind> :
|
||||
PatFrag<(ops node:$addr, node:$val), (kind node:$addr, node:$val)>;
|
||||
class sext_bin_rmw_16_32<PatFrag kind> : sext_bin_rmw_8_32<kind>;
|
||||
class sext_bin_rmw_8_64<PatFrag kind> :
|
||||
PatFrag<(ops node:$addr, node:$val),
|
||||
(anyext (i32 (kind node:$addr, (i32 (trunc (i64 node:$val))))))>;
|
||||
class sext_bin_rmw_16_64<PatFrag kind> : sext_bin_rmw_8_64<kind>;
|
||||
// 32->64 sext RMW gets selected as i32.atomic.rmw.***, i64.extend_s/i32
|
||||
|
||||
// Patterns for various addressing modes for truncating-extending binary RMWs.
|
||||
multiclass BinRMWTruncExtPattern<
|
||||
PatFrag rmw_8, PatFrag rmw_16, PatFrag rmw_32, PatFrag rmw_64,
|
||||
NI inst8_32, NI inst16_32, NI inst8_64, NI inst16_64, NI inst32_64> {
|
||||
// Truncating-extending binary RMWs with no constant offset
|
||||
def : BinRMWPatNoOffset<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatNoOffset<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatNoOffset<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatNoOffset<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
def : BinRMWPatNoOffset<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
|
||||
|
||||
def : BinRMWPatNoOffset<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatNoOffset<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatNoOffset<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatNoOffset<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
|
||||
// Truncating-extending binary RMWs with a constant offset
|
||||
def : BinRMWPatImmOff<i32, zext_bin_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
|
||||
def : BinRMWPatImmOff<i32, zext_bin_rmw_16_32<rmw_16>, regPlusImm, inst16_32>;
|
||||
def : BinRMWPatImmOff<i64, zext_bin_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
|
||||
def : BinRMWPatImmOff<i64, zext_bin_rmw_16_64<rmw_16>, regPlusImm, inst16_64>;
|
||||
def : BinRMWPatImmOff<i64, zext_bin_rmw_32_64<rmw_32>, regPlusImm, inst32_64>;
|
||||
def : BinRMWPatImmOff<i32, zext_bin_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
|
||||
def : BinRMWPatImmOff<i32, zext_bin_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
|
||||
def : BinRMWPatImmOff<i64, zext_bin_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
|
||||
def : BinRMWPatImmOff<i64, zext_bin_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
|
||||
def : BinRMWPatImmOff<i64, zext_bin_rmw_32_64<rmw_32>, or_is_add, inst32_64>;
|
||||
|
||||
def : BinRMWPatImmOff<i32, sext_bin_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
|
||||
def : BinRMWPatImmOff<i32, sext_bin_rmw_16_32<rmw_16>, regPlusImm, inst16_32>;
|
||||
def : BinRMWPatImmOff<i64, sext_bin_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
|
||||
def : BinRMWPatImmOff<i64, sext_bin_rmw_16_64<rmw_16>, regPlusImm, inst16_64>;
|
||||
def : BinRMWPatImmOff<i32, sext_bin_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
|
||||
def : BinRMWPatImmOff<i32, sext_bin_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
|
||||
def : BinRMWPatImmOff<i64, sext_bin_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
|
||||
def : BinRMWPatImmOff<i64, sext_bin_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
|
||||
|
||||
def : BinRMWPatGlobalAddr<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatGlobalAddr<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatGlobalAddr<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatGlobalAddr<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
def : BinRMWPatGlobalAddr<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
|
||||
|
||||
def : BinRMWPatGlobalAddr<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatGlobalAddr<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatGlobalAddr<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatGlobalAddr<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
|
||||
def : BinRMWPatExternalSym<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatExternalSym<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatExternalSym<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatExternalSym<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
def : BinRMWPatExternalSym<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
|
||||
|
||||
def : BinRMWPatExternalSym<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatExternalSym<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatExternalSym<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatExternalSym<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
|
||||
// Truncating-extending binary RMWs with just a constant offset
|
||||
def : BinRMWPatOffsetOnly<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatOffsetOnly<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatOffsetOnly<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatOffsetOnly<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
def : BinRMWPatOffsetOnly<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
|
||||
|
||||
def : BinRMWPatOffsetOnly<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatOffsetOnly<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatOffsetOnly<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatOffsetOnly<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
|
||||
def : BinRMWPatGlobalAddrOffOnly<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
|
||||
|
||||
def : BinRMWPatGlobalAddrOffOnly<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatGlobalAddrOffOnly<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
|
||||
def : BinRMWPatExternSymOffOnly<i32, zext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatExternSymOffOnly<i32, zext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatExternSymOffOnly<i64, zext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatExternSymOffOnly<i64, zext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
def : BinRMWPatExternSymOffOnly<i64, zext_bin_rmw_32_64<rmw_32>, inst32_64>;
|
||||
|
||||
def : BinRMWPatExternSymOffOnly<i32, sext_bin_rmw_8_32<rmw_8>, inst8_32>;
|
||||
def : BinRMWPatExternSymOffOnly<i32, sext_bin_rmw_16_32<rmw_16>, inst16_32>;
|
||||
def : BinRMWPatExternSymOffOnly<i64, sext_bin_rmw_8_64<rmw_8>, inst8_64>;
|
||||
def : BinRMWPatExternSymOffOnly<i64, sext_bin_rmw_16_64<rmw_16>, inst16_64>;
|
||||
}
|
||||
|
||||
let Predicates = [HasAtomics] in {
|
||||
defm : BinRMWTruncExtPattern<
|
||||
atomic_load_add_8, atomic_load_add_16, atomic_load_add_32, atomic_load_add_64,
|
||||
ATOMIC_RMW8_U_ADD_I32, ATOMIC_RMW16_U_ADD_I32,
|
||||
ATOMIC_RMW8_U_ADD_I64, ATOMIC_RMW16_U_ADD_I64, ATOMIC_RMW32_U_ADD_I64>;
|
||||
defm : BinRMWTruncExtPattern<
|
||||
atomic_load_sub_8, atomic_load_sub_16, atomic_load_sub_32, atomic_load_sub_64,
|
||||
ATOMIC_RMW8_U_SUB_I32, ATOMIC_RMW16_U_SUB_I32,
|
||||
ATOMIC_RMW8_U_SUB_I64, ATOMIC_RMW16_U_SUB_I64, ATOMIC_RMW32_U_SUB_I64>;
|
||||
defm : BinRMWTruncExtPattern<
|
||||
atomic_load_and_8, atomic_load_and_16, atomic_load_and_32, atomic_load_and_64,
|
||||
ATOMIC_RMW8_U_AND_I32, ATOMIC_RMW16_U_AND_I32,
|
||||
ATOMIC_RMW8_U_AND_I64, ATOMIC_RMW16_U_AND_I64, ATOMIC_RMW32_U_AND_I64>;
|
||||
defm : BinRMWTruncExtPattern<
|
||||
atomic_load_or_8, atomic_load_or_16, atomic_load_or_32, atomic_load_or_64,
|
||||
ATOMIC_RMW8_U_OR_I32, ATOMIC_RMW16_U_OR_I32,
|
||||
ATOMIC_RMW8_U_OR_I64, ATOMIC_RMW16_U_OR_I64, ATOMIC_RMW32_U_OR_I64>;
|
||||
defm : BinRMWTruncExtPattern<
|
||||
atomic_load_xor_8, atomic_load_xor_16, atomic_load_xor_32, atomic_load_xor_64,
|
||||
ATOMIC_RMW8_U_XOR_I32, ATOMIC_RMW16_U_XOR_I32,
|
||||
ATOMIC_RMW8_U_XOR_I64, ATOMIC_RMW16_U_XOR_I64, ATOMIC_RMW32_U_XOR_I64>;
|
||||
defm : BinRMWTruncExtPattern<
|
||||
atomic_swap_8, atomic_swap_16, atomic_swap_32, atomic_swap_64,
|
||||
ATOMIC_RMW8_U_XCHG_I32, ATOMIC_RMW16_U_XCHG_I32,
|
||||
ATOMIC_RMW8_U_XCHG_I64, ATOMIC_RMW16_U_XCHG_I64, ATOMIC_RMW32_U_XCHG_I64>;
|
||||
} // Predicates = [HasAtomics]
|
||||
|
@ -107,6 +107,48 @@ bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) {
|
||||
case WebAssembly::ATOMIC_LOAD8_U_I64:
|
||||
case WebAssembly::ATOMIC_LOAD16_U_I64:
|
||||
case WebAssembly::ATOMIC_LOAD32_U_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_ADD_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_SUB_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_AND_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_OR_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XOR_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XCHG_I32:
|
||||
case WebAssembly::ATOMIC_RMW8_U_XCHG_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_ADD_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_SUB_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_AND_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_OR_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XOR_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XCHG_I32:
|
||||
case WebAssembly::ATOMIC_RMW16_U_XCHG_I64:
|
||||
case WebAssembly::ATOMIC_RMW_ADD_I32:
|
||||
case WebAssembly::ATOMIC_RMW32_U_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW_SUB_I32:
|
||||
case WebAssembly::ATOMIC_RMW32_U_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW_AND_I32:
|
||||
case WebAssembly::ATOMIC_RMW32_U_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW_OR_I32:
|
||||
case WebAssembly::ATOMIC_RMW32_U_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW_XOR_I32:
|
||||
case WebAssembly::ATOMIC_RMW32_U_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW_XCHG_I32:
|
||||
case WebAssembly::ATOMIC_RMW32_U_XCHG_I64:
|
||||
case WebAssembly::ATOMIC_RMW_ADD_I64:
|
||||
case WebAssembly::ATOMIC_RMW_SUB_I64:
|
||||
case WebAssembly::ATOMIC_RMW_AND_I64:
|
||||
case WebAssembly::ATOMIC_RMW_OR_I64:
|
||||
case WebAssembly::ATOMIC_RMW_XOR_I64:
|
||||
case WebAssembly::ATOMIC_RMW_XCHG_I64:
|
||||
RewriteP2Align(MI, WebAssembly::LoadP2AlignOperandNo);
|
||||
break;
|
||||
case WebAssembly::STORE_I32:
|
||||
|
857
test/CodeGen/WebAssembly/atomic-rmw.ll
Normal file
857
test/CodeGen/WebAssembly/atomic-rmw.ll
Normal file
@ -0,0 +1,857 @@
|
||||
; RUN: not llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt
|
||||
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=+atomics,+sign-ext | FileCheck %s
|
||||
|
||||
; Test atomic RMW (read-modify-write) instructions are assembled properly.
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic read-modify-writes: 32-bit
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; CHECK-LABEL: add_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @add_i32(i32* %p, i32 %v) {
|
||||
%old = atomicrmw add i32* %p, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @sub_i32(i32* %p, i32 %v) {
|
||||
%old = atomicrmw sub i32* %p, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @and_i32(i32* %p, i32 %v) {
|
||||
%old = atomicrmw and i32* %p, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @or_i32(i32* %p, i32 %v) {
|
||||
%old = atomicrmw or i32* %p, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @xor_i32(i32* %p, i32 %v) {
|
||||
%old = atomicrmw xor i32* %p, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @xchg_i32(i32* %p, i32 %v) {
|
||||
%old = atomicrmw xchg i32* %p, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic read-modify-writes: 64-bit
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; CHECK-LABEL: add_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @add_i64(i64* %p, i64 %v) {
|
||||
%old = atomicrmw add i64* %p, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @sub_i64(i64* %p, i64 %v) {
|
||||
%old = atomicrmw sub i64* %p, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @and_i64(i64* %p, i64 %v) {
|
||||
%old = atomicrmw and i64* %p, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @or_i64(i64* %p, i64 %v) {
|
||||
%old = atomicrmw or i64* %p, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xor_i64(i64* %p, i64 %v) {
|
||||
%old = atomicrmw xor i64* %p, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xchg_i64(i64* %p, i64 %v) {
|
||||
%old = atomicrmw xchg i64* %p, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic truncating & sign-extending RMWs
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; add
|
||||
|
||||
; CHECK-LABEL: add_sext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @add_sext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: add_sext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @add_sext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw add i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: add_sext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @add_sext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw add i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: add_sext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @add_sext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw add i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; 32->64 sext rmw gets selected as i32.atomic.rmw.add, i64_extend_s/i32
|
||||
; CHECK-LABEL: add_sext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i32.wrap/i64 $push0=, $1
|
||||
; CHECK: i32.atomic.rmw.add $push1=, 0($0), $pop0{{$}}
|
||||
; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}}
|
||||
; CHECK-NEXT: return $pop2{{$}}
|
||||
define i64 @add_sext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw add i32* %p, i32 %t seq_cst
|
||||
%e = sext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; sub
|
||||
|
||||
; CHECK-LABEL: sub_sext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @sub_sext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw sub i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_sext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @sub_sext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw sub i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_sext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @sub_sext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw sub i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_sext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @sub_sext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw sub i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; 32->64 sext rmw gets selected as i32.atomic.rmw.sub, i64_extend_s/i32
|
||||
; CHECK-LABEL: sub_sext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i32.wrap/i64 $push0=, $1
|
||||
; CHECK: i32.atomic.rmw.sub $push1=, 0($0), $pop0{{$}}
|
||||
; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}}
|
||||
; CHECK-NEXT: return $pop2{{$}}
|
||||
define i64 @sub_sext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw sub i32* %p, i32 %t seq_cst
|
||||
%e = sext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; and
|
||||
|
||||
; CHECK-LABEL: and_sext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @and_sext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw and i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_sext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @and_sext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw and i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_sext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @and_sext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw and i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_sext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @and_sext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw and i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; 32->64 sext rmw gets selected as i32.atomic.rmw.and, i64_extend_s/i32
|
||||
; CHECK-LABEL: and_sext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i32.wrap/i64 $push0=, $1
|
||||
; CHECK: i32.atomic.rmw.and $push1=, 0($0), $pop0{{$}}
|
||||
; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}}
|
||||
; CHECK-NEXT: return $pop2{{$}}
|
||||
define i64 @and_sext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw and i32* %p, i32 %t seq_cst
|
||||
%e = sext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; or
|
||||
|
||||
; CHECK-LABEL: or_sext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @or_sext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw or i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_sext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @or_sext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw or i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_sext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @or_sext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw or i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_sext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @or_sext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw or i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; 32->64 sext rmw gets selected as i32.atomic.rmw.or, i64_extend_s/i32
|
||||
; CHECK-LABEL: or_sext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i32.wrap/i64 $push0=, $1
|
||||
; CHECK: i32.atomic.rmw.or $push1=, 0($0), $pop0{{$}}
|
||||
; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}}
|
||||
; CHECK-NEXT: return $pop2{{$}}
|
||||
define i64 @or_sext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw or i32* %p, i32 %t seq_cst
|
||||
%e = sext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; xor
|
||||
|
||||
; CHECK-LABEL: xor_sext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @xor_sext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw xor i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_sext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @xor_sext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw xor i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_sext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @xor_sext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw xor i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_sext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @xor_sext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw xor i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; 32->64 sext rmw gets selected as i32.atomic.rmw.xor, i64_extend_s/i32
|
||||
; CHECK-LABEL: xor_sext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i32.wrap/i64 $push0=, $1
|
||||
; CHECK: i32.atomic.rmw.xor $push1=, 0($0), $pop0{{$}}
|
||||
; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}}
|
||||
; CHECK-NEXT: return $pop2{{$}}
|
||||
define i64 @xor_sext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw xor i32* %p, i32 %t seq_cst
|
||||
%e = sext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; xchg
|
||||
|
||||
; CHECK-LABEL: xchg_sext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @xchg_sext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw xchg i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_sext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i32 @xchg_sext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw xchg i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_sext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend8_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @xchg_sext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw xchg i8* %p, i8 %t seq_cst
|
||||
%e = sext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_sext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend16_s $push1=, $pop0{{$}}
|
||||
; CHECK-NEXT: return $pop1{{$}}
|
||||
define i64 @xchg_sext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw xchg i16* %p, i16 %t seq_cst
|
||||
%e = sext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; 32->64 sext rmw gets selected as i32.atomic.rmw.xchg, i64_extend_s/i32
|
||||
; CHECK-LABEL: xchg_sext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i32.wrap/i64 $push0=, $1
|
||||
; CHECK: i32.atomic.rmw.xchg $push1=, 0($0), $pop0{{$}}
|
||||
; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}}
|
||||
; CHECK-NEXT: return $pop2{{$}}
|
||||
define i64 @xchg_sext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw xchg i32* %p, i32 %t seq_cst
|
||||
%e = sext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic truncating & zero-extending RMWs
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; add
|
||||
|
||||
; CHECK-LABEL: add_zext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @add_zext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: add_zext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @add_zext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw add i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: add_zext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @add_zext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw add i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: add_zext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @add_zext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw add i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: add_zext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw32_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @add_zext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw add i32* %p, i32 %t seq_cst
|
||||
%e = zext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; sub
|
||||
|
||||
; CHECK-LABEL: sub_zext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @sub_zext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw sub i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_zext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @sub_zext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw sub i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_zext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @sub_zext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw sub i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_zext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @sub_zext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw sub i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sub_zext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw32_u.sub $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @sub_zext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw sub i32* %p, i32 %t seq_cst
|
||||
%e = zext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; and
|
||||
|
||||
; CHECK-LABEL: and_zext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @and_zext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw and i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_zext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @and_zext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw and i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_zext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @and_zext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw and i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_zext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @and_zext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw and i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: and_zext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw32_u.and $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @and_zext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw and i32* %p, i32 %t seq_cst
|
||||
%e = zext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; or
|
||||
|
||||
; CHECK-LABEL: or_zext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @or_zext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw or i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_zext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @or_zext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw or i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_zext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @or_zext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw or i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_zext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @or_zext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw or i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: or_zext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw32_u.or $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @or_zext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw or i32* %p, i32 %t seq_cst
|
||||
%e = zext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; xor
|
||||
|
||||
; CHECK-LABEL: xor_zext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @xor_zext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw xor i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_zext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @xor_zext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw xor i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_zext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xor_zext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw xor i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_zext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xor_zext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw xor i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xor_zext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw32_u.xor $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xor_zext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw xor i32* %p, i32 %t seq_cst
|
||||
%e = zext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; xchg
|
||||
|
||||
; CHECK-LABEL: xchg_zext_i8_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @xchg_zext_i8_i32(i8* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw xchg i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_zext_i16_i32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @xchg_zext_i16_i32(i16* %p, i32 %v) {
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw xchg i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i32
|
||||
ret i32 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_zext_i8_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw8_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xchg_zext_i8_i64(i8* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw xchg i8* %p, i8 %t seq_cst
|
||||
%e = zext i8 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_zext_i16_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw16_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xchg_zext_i16_i64(i16* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw xchg i16* %p, i16 %t seq_cst
|
||||
%e = zext i16 %old to i64
|
||||
ret i64 %e
|
||||
}
|
||||
|
||||
; CHECK-LABEL: xchg_zext_i32_i64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw32_u.xchg $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @xchg_zext_i32_i64(i32* %p, i64 %v) {
|
||||
%t = trunc i64 %v to i32
|
||||
%old = atomicrmw xchg i32* %p, i32 %t seq_cst
|
||||
%e = zext i32 %old to i64
|
||||
ret i64 %e
|
||||
}
|
@ -653,3 +653,420 @@ define void @store_i8_i64_with_folded_or_offset(i32 %x, i64 %v) {
|
||||
store atomic i8 %t, i8* %arrayidx seq_cst, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic binary read-modify-writes: 32-bit
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; There are several RMW instructions, but here we only test 'add' as an example.
|
||||
|
||||
; Basic RMW.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_no_offset:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i32 @rmw_add_i32_no_offset(i32* %p, i32 %v) {
|
||||
%old = atomicrmw add i32* %p, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; With an nuw add, we can fold an offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_with_folded_offset:
|
||||
; CHECK: i32.atomic.rmw.add $push0=, 24($0), $1{{$}}
|
||||
define i32 @rmw_add_i32_with_folded_offset(i32* %p, i32 %v) {
|
||||
%q = ptrtoint i32* %p to i32
|
||||
%r = add nuw i32 %q, 24
|
||||
%s = inttoptr i32 %r to i32*
|
||||
%old = atomicrmw add i32* %s, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; With an inbounds gep, we can fold an offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_with_folded_gep_offset:
|
||||
; CHECK: i32.atomic.rmw.add $push0=, 24($0), $1{{$}}
|
||||
define i32 @rmw_add_i32_with_folded_gep_offset(i32* %p, i32 %v) {
|
||||
%s = getelementptr inbounds i32, i32* %p, i32 6
|
||||
%old = atomicrmw add i32* %s, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; We can't fold a negative offset though, even with an inbounds gep.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_with_unfolded_gep_negative_offset:
|
||||
; CHECK: i32.const $push0=, -24{{$}}
|
||||
; CHECK: i32.add $push1=, $0, $pop0{{$}}
|
||||
; CHECK: i32.atomic.rmw.add $push2=, 0($pop1), $1{{$}}
|
||||
define i32 @rmw_add_i32_with_unfolded_gep_negative_offset(i32* %p, i32 %v) {
|
||||
%s = getelementptr inbounds i32, i32* %p, i32 -6
|
||||
%old = atomicrmw add i32* %s, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; Without nuw, and even with nsw, we can't fold an offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_with_unfolded_offset:
|
||||
; CHECK: i32.const $push0=, 24{{$}}
|
||||
; CHECK: i32.add $push1=, $0, $pop0{{$}}
|
||||
; CHECK: i32.atomic.rmw.add $push2=, 0($pop1), $1{{$}}
|
||||
define i32 @rmw_add_i32_with_unfolded_offset(i32* %p, i32 %v) {
|
||||
%q = ptrtoint i32* %p to i32
|
||||
%r = add nsw i32 %q, 24
|
||||
%s = inttoptr i32 %r to i32*
|
||||
%old = atomicrmw add i32* %s, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; Without inbounds, we can't fold a gep offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_with_unfolded_gep_offset:
|
||||
; CHECK: i32.const $push0=, 24{{$}}
|
||||
; CHECK: i32.add $push1=, $0, $pop0{{$}}
|
||||
; CHECK: i32.atomic.rmw.add $push2=, 0($pop1), $1{{$}}
|
||||
define i32 @rmw_add_i32_with_unfolded_gep_offset(i32* %p, i32 %v) {
|
||||
%s = getelementptr i32, i32* %p, i32 6
|
||||
%old = atomicrmw add i32* %s, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; When loading from a fixed address, materialize a zero.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_from_numeric_address
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.atomic.rmw.add $push1=, 42($pop0), $0{{$}}
|
||||
define i32 @rmw_add_i32_from_numeric_address(i32 %v) {
|
||||
%s = inttoptr i32 42 to i32*
|
||||
%old = atomicrmw add i32* %s, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_from_global_address
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.atomic.rmw.add $push1=, gv($pop0), $0{{$}}
|
||||
define i32 @rmw_add_i32_from_global_address(i32 %v) {
|
||||
%old = atomicrmw add i32* @gv, i32 %v seq_cst
|
||||
ret i32 %old
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic binary read-modify-writes: 64-bit
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; Basic RMW.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i64_no_offset:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK: i64.atomic.rmw.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i64 @rmw_add_i64_no_offset(i64* %p, i64 %v) {
|
||||
%old = atomicrmw add i64* %p, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; With an nuw add, we can fold an offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i64_with_folded_offset:
|
||||
; CHECK: i64.atomic.rmw.add $push0=, 24($0), $1{{$}}
|
||||
define i64 @rmw_add_i64_with_folded_offset(i64* %p, i64 %v) {
|
||||
%q = ptrtoint i64* %p to i32
|
||||
%r = add nuw i32 %q, 24
|
||||
%s = inttoptr i32 %r to i64*
|
||||
%old = atomicrmw add i64* %s, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; With an inbounds gep, we can fold an offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i64_with_folded_gep_offset:
|
||||
; CHECK: i64.atomic.rmw.add $push0=, 24($0), $1{{$}}
|
||||
define i64 @rmw_add_i64_with_folded_gep_offset(i64* %p, i64 %v) {
|
||||
%s = getelementptr inbounds i64, i64* %p, i32 3
|
||||
%old = atomicrmw add i64* %s, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; We can't fold a negative offset though, even with an inbounds gep.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i64_with_unfolded_gep_negative_offset:
|
||||
; CHECK: i32.const $push0=, -24{{$}}
|
||||
; CHECK: i32.add $push1=, $0, $pop0{{$}}
|
||||
; CHECK: i64.atomic.rmw.add $push2=, 0($pop1), $1{{$}}
|
||||
define i64 @rmw_add_i64_with_unfolded_gep_negative_offset(i64* %p, i64 %v) {
|
||||
%s = getelementptr inbounds i64, i64* %p, i32 -3
|
||||
%old = atomicrmw add i64* %s, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; Without nuw, and even with nsw, we can't fold an offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i64_with_unfolded_offset:
|
||||
; CHECK: i32.const $push0=, 24{{$}}
|
||||
; CHECK: i32.add $push1=, $0, $pop0{{$}}
|
||||
; CHECK: i64.atomic.rmw.add $push2=, 0($pop1), $1{{$}}
|
||||
define i64 @rmw_add_i64_with_unfolded_offset(i64* %p, i64 %v) {
|
||||
%q = ptrtoint i64* %p to i32
|
||||
%r = add nsw i32 %q, 24
|
||||
%s = inttoptr i32 %r to i64*
|
||||
%old = atomicrmw add i64* %s, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
; Without inbounds, we can't fold a gep offset.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i64_with_unfolded_gep_offset:
|
||||
; CHECK: i32.const $push0=, 24{{$}}
|
||||
; CHECK: i32.add $push1=, $0, $pop0{{$}}
|
||||
; CHECK: i64.atomic.rmw.add $push2=, 0($pop1), $1{{$}}
|
||||
define i64 @rmw_add_i64_with_unfolded_gep_offset(i64* %p, i64 %v) {
|
||||
%s = getelementptr i64, i64* %p, i32 3
|
||||
%old = atomicrmw add i64* %s, i64 %v seq_cst
|
||||
ret i64 %old
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic truncating & sign-extending binary RMWs
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; Fold an offset into a sign-extending rmw.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_s_with_folded_offset:
|
||||
; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0
|
||||
define i32 @rmw_add_i8_i32_s_with_folded_offset(i8* %p, i32 %v) {
|
||||
%q = ptrtoint i8* %p to i32
|
||||
%r = add nuw i32 %q, 24
|
||||
%s = inttoptr i32 %r to i8*
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %s, i8 %t seq_cst
|
||||
%u = sext i8 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; 32->64 sext rmw gets selected as i32.atomic.rmw.add, i64_extend_s/i32
|
||||
; CHECK-LABEL: rmw_add_i32_i64_s_with_folded_offset:
|
||||
; CHECK: i32.wrap/i64 $push0=, $1
|
||||
; CHECK-NEXT: i32.atomic.rmw.add $push1=, 24($0), $pop0{{$}}
|
||||
; CHECK-NEXT: i64.extend_s/i32 $push2=, $pop1{{$}}
|
||||
define i64 @rmw_add_i32_i64_s_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
|
||||
%old = atomicrmw add i32* %s, i32 %t seq_cst
|
||||
%u = sext i32 %old to i64
|
||||
ret i64 %u
|
||||
}
|
||||
|
||||
; Fold a gep offset into a sign-extending rmw.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_s_with_folded_gep_offset:
|
||||
; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push1=, $pop0
|
||||
define i32 @rmw_add_i8_i32_s_with_folded_gep_offset(i8* %p, i32 %v) {
|
||||
%s = getelementptr inbounds i8, i8* %p, i32 24
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %s, i8 %t seq_cst
|
||||
%u = sext i8 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i16_i32_s_with_folded_gep_offset:
|
||||
; CHECK: i32.atomic.rmw16_u.add $push0=, 48($0), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push1=, $pop0
|
||||
define i32 @rmw_add_i16_i32_s_with_folded_gep_offset(i16* %p, i32 %v) {
|
||||
%s = getelementptr inbounds i16, i16* %p, i32 24
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw add i16* %s, i16 %t seq_cst
|
||||
%u = sext i16 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i16_i64_s_with_folded_gep_offset:
|
||||
; CHECK: i64.atomic.rmw16_u.add $push0=, 48($0), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend16_s $push1=, $pop0
|
||||
define i64 @rmw_add_i16_i64_s_with_folded_gep_offset(i16* %p, i64 %v) {
|
||||
%s = getelementptr inbounds i16, i16* %p, i32 24
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw add i16* %s, i16 %t seq_cst
|
||||
%u = sext i16 %old to i64
|
||||
ret i64 %u
|
||||
}
|
||||
|
||||
; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as
|
||||
; an 'add' if the or'ed bits are known to be zero.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_s_with_folded_or_offset:
|
||||
; CHECK: i32.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
|
||||
define i32 @rmw_add_i8_i32_s_with_folded_or_offset(i32 %x, i32 %v) {
|
||||
%and = and i32 %x, -4
|
||||
%t0 = inttoptr i32 %and to i8*
|
||||
%arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %arrayidx, i8 %t seq_cst
|
||||
%conv = sext i8 %old to i32
|
||||
ret i32 %conv
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i64_s_with_folded_or_offset:
|
||||
; CHECK: i64.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}}
|
||||
; CHECK-NEXT: i64.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
|
||||
define i64 @rmw_add_i8_i64_s_with_folded_or_offset(i32 %x, i64 %v) {
|
||||
%and = and i32 %x, -4
|
||||
%t0 = inttoptr i32 %and to i8*
|
||||
%arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw add i8* %arrayidx, i8 %t seq_cst
|
||||
%conv = sext i8 %old to i64
|
||||
ret i64 %conv
|
||||
}
|
||||
|
||||
; When loading from a fixed address, materialize a zero.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i16_i32_s_from_numeric_address
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.add $push1=, 42($pop0), $0{{$}}
|
||||
; CHECK-NEXT: i32.extend16_s $push2=, $pop1
|
||||
define i32 @rmw_add_i16_i32_s_from_numeric_address(i32 %v) {
|
||||
%s = inttoptr i32 42 to i16*
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw add i16* %s, i16 %t seq_cst
|
||||
%u = sext i16 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_s_from_global_address
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.add $push1=, gv8($pop0), $0{{$}}
|
||||
; CHECK-NEXT: i32.extend8_s $push2=, $pop1{{$}}
|
||||
define i32 @rmw_add_i8_i32_s_from_global_address(i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* @gv8, i8 %t seq_cst
|
||||
%u = sext i8 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic truncating & zero-extending binary RMWs
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; Fold an offset into a zero-extending rmw.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_z_with_folded_offset:
|
||||
; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}}
|
||||
define i32 @rmw_add_i8_i32_z_with_folded_offset(i8* %p, i32 %v) {
|
||||
%q = ptrtoint i8* %p to i32
|
||||
%r = add nuw i32 %q, 24
|
||||
%s = inttoptr i32 %r to i8*
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %s, i8 %t seq_cst
|
||||
%u = zext i8 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i32_i64_z_with_folded_offset:
|
||||
; CHECK: i64.atomic.rmw32_u.add $push0=, 24($0), $1{{$}}
|
||||
define i64 @rmw_add_i32_i64_z_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
|
||||
%old = atomicrmw add i32* %s, i32 %t seq_cst
|
||||
%u = zext i32 %old to i64
|
||||
ret i64 %u
|
||||
}
|
||||
|
||||
; Fold a gep offset into a zero-extending rmw.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_z_with_folded_gep_offset:
|
||||
; CHECK: i32.atomic.rmw8_u.add $push0=, 24($0), $1{{$}}
|
||||
define i32 @rmw_add_i8_i32_z_with_folded_gep_offset(i8* %p, i32 %v) {
|
||||
%s = getelementptr inbounds i8, i8* %p, i32 24
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %s, i8 %t seq_cst
|
||||
%u = zext i8 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i16_i32_z_with_folded_gep_offset:
|
||||
; CHECK: i32.atomic.rmw16_u.add $push0=, 48($0), $1{{$}}
|
||||
define i32 @rmw_add_i16_i32_z_with_folded_gep_offset(i16* %p, i32 %v) {
|
||||
%s = getelementptr inbounds i16, i16* %p, i32 24
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw add i16* %s, i16 %t seq_cst
|
||||
%u = zext i16 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i16_i64_z_with_folded_gep_offset:
|
||||
; CHECK: i64.atomic.rmw16_u.add $push0=, 48($0), $1{{$}}
|
||||
define i64 @rmw_add_i16_i64_z_with_folded_gep_offset(i16* %p, i64 %v) {
|
||||
%s = getelementptr inbounds i16, i16* %p, i32 24
|
||||
%t = trunc i64 %v to i16
|
||||
%old = atomicrmw add i16* %s, i16 %t seq_cst
|
||||
%u = zext i16 %old to i64
|
||||
ret i64 %u
|
||||
}
|
||||
|
||||
; 'add' in this code becomes 'or' after DAG optimization. Treat an 'or' node as
|
||||
; an 'add' if the or'ed bits are known to be zero.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_z_with_folded_or_offset:
|
||||
; CHECK: i32.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}}
|
||||
define i32 @rmw_add_i8_i32_z_with_folded_or_offset(i32 %x, i32 %v) {
|
||||
%and = and i32 %x, -4
|
||||
%t0 = inttoptr i32 %and to i8*
|
||||
%arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %arrayidx, i8 %t seq_cst
|
||||
%conv = zext i8 %old to i32
|
||||
ret i32 %conv
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i64_z_with_folded_or_offset:
|
||||
; CHECK: i64.atomic.rmw8_u.add $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1{{$}}
|
||||
define i64 @rmw_add_i8_i64_z_with_folded_or_offset(i32 %x, i64 %v) {
|
||||
%and = and i32 %x, -4
|
||||
%t0 = inttoptr i32 %and to i8*
|
||||
%arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
|
||||
%t = trunc i64 %v to i8
|
||||
%old = atomicrmw add i8* %arrayidx, i8 %t seq_cst
|
||||
%conv = zext i8 %old to i64
|
||||
ret i64 %conv
|
||||
}
|
||||
|
||||
; When loading from a fixed address, materialize a zero.
|
||||
|
||||
; CHECK-LABEL: rmw_add_i16_i32_z_from_numeric_address
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.atomic.rmw16_u.add $push1=, 42($pop0), $0{{$}}
|
||||
define i32 @rmw_add_i16_i32_z_from_numeric_address(i32 %v) {
|
||||
%s = inttoptr i32 42 to i16*
|
||||
%t = trunc i32 %v to i16
|
||||
%old = atomicrmw add i16* %s, i16 %t seq_cst
|
||||
%u = zext i16 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_z_from_global_address
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.atomic.rmw8_u.add $push1=, gv8($pop0), $0{{$}}
|
||||
define i32 @rmw_add_i8_i32_z_from_global_address(i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* @gv8, i8 %t seq_cst
|
||||
%u = zext i8 %old to i32
|
||||
ret i32 %u
|
||||
}
|
||||
|
||||
; i8 return value should test anyext RMWs
|
||||
|
||||
; CHECK-LABEL: rmw_add_i8_i32_retvalue:
|
||||
; CHECK: i32.atomic.rmw8_u.add $push0=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return $pop0{{$}}
|
||||
define i8 @rmw_add_i8_i32_retvalue(i8 *%p, i32 %v) {
|
||||
%t = trunc i32 %v to i8
|
||||
%old = atomicrmw add i8* %p, i8 %t seq_cst
|
||||
ret i8 %old
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user