[llvm] r338617 - [WebAssembly] Support for a ternary atomic RMW instruction

Heejin Ahn via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 1 12:40:28 PDT 2018


Author: aheejin
Date: Wed Aug  1 12:40:28 2018
New Revision: 338617

URL: http://llvm.org/viewvc/llvm-project?rev=338617&view=rev
Log:
[WebAssembly] Support for a ternary atomic RMW instruction

Summary: This adds support for a ternary atomic RMW instruction: cmpxchg.

Reviewers: dschuff

Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits

Differential Revision: https://reviews.llvm.org/D49195

Modified:
    llvm/trunk/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
    llvm/trunk/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td
    llvm/trunk/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
    llvm/trunk/test/CodeGen/WebAssembly/atomic-mem-consistency.ll
    llvm/trunk/test/CodeGen/WebAssembly/atomic-rmw.ll
    llvm/trunk/test/CodeGen/WebAssembly/offset-atomics.ll

Modified: llvm/trunk/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h?rev=338617&r1=338616&r2=338617&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h (original)
+++ llvm/trunk/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h Wed Aug  1 12:40:28 2018
@@ -149,6 +149,10 @@ inline unsigned GetDefaultP2Align(unsign
   case WebAssembly::ATOMIC_RMW8_U_XCHG_I32_S:
   case WebAssembly::ATOMIC_RMW8_U_XCHG_I64:
   case WebAssembly::ATOMIC_RMW8_U_XCHG_I64_S:
+  case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I32:
+  case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I32_S:
+  case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I64:
+  case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I64_S:
     return 0;
   case WebAssembly::LOAD16_S_I32:
   case WebAssembly::LOAD16_S_I32_S:
@@ -194,6 +198,10 @@ inline unsigned GetDefaultP2Align(unsign
   case WebAssembly::ATOMIC_RMW16_U_XCHG_I32_S:
   case WebAssembly::ATOMIC_RMW16_U_XCHG_I64:
   case WebAssembly::ATOMIC_RMW16_U_XCHG_I64_S:
+  case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I32:
+  case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I32_S:
+  case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I64:
+  case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I64_S:
     return 1;
   case WebAssembly::LOAD_I32:
   case WebAssembly::LOAD_I32_S:
@@ -241,6 +249,10 @@ inline unsigned GetDefaultP2Align(unsign
   case WebAssembly::ATOMIC_RMW_XCHG_I32_S:
   case WebAssembly::ATOMIC_RMW32_U_XCHG_I64:
   case WebAssembly::ATOMIC_RMW32_U_XCHG_I64_S:
+  case WebAssembly::ATOMIC_RMW_CMPXCHG_I32:
+  case WebAssembly::ATOMIC_RMW_CMPXCHG_I32_S:
+  case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_I64:
+  case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_I64_S:
     return 2;
   case WebAssembly::LOAD_I64:
   case WebAssembly::LOAD_I64_S:
@@ -266,6 +278,8 @@ inline unsigned GetDefaultP2Align(unsign
   case WebAssembly::ATOMIC_RMW_XOR_I64_S:
   case WebAssembly::ATOMIC_RMW_XCHG_I64:
   case WebAssembly::ATOMIC_RMW_XCHG_I64_S:
+  case WebAssembly::ATOMIC_RMW_CMPXCHG_I64:
+  case WebAssembly::ATOMIC_RMW_CMPXCHG_I64_S:
     return 3;
   default:
     llvm_unreachable("Only loads and stores have p2align values");

Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td?rev=338617&r1=338616&r2=338617&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td Wed Aug  1 12:40:28 2018
@@ -655,3 +655,246 @@ defm : BinRMWTruncExtPattern<
   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]
+
+//===----------------------------------------------------------------------===//
+// Atomic ternary read-modify-writes
+//===----------------------------------------------------------------------===//
+
+// TODO LLVM IR's cmpxchg instruction returns a pair of {loaded value,
+// success flag}. When we use a success flag or both values, we can't make use
+// of truncate/extend versions of instructions for now, which is suboptimal. Add
+// selection rules for those cases too.
+
+let Defs = [ARGUMENTS] in {
+
+multiclass WebAssemblyTerRMW<WebAssemblyRegClass rc, string Name, int Opcode> {
+  defm "" : I<(outs rc:$dst),
+              (ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$exp,
+                   rc:$new),
+              (outs), (ins P2Align:$p2align, offset32_op:$off), [],
+              !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new"),
+              !strconcat(Name, "\t${off}, ${p2align}"), Opcode>;
+}
+
+defm ATOMIC_RMW_CMPXCHG_I32 :
+  WebAssemblyTerRMW<I32, "i32.atomic.rmw.cmpxchg", 0xfe48>;
+defm ATOMIC_RMW_CMPXCHG_I64 :
+  WebAssemblyTerRMW<I64, "i64.atomic.rmw.cmpxchg", 0xfe49>;
+defm ATOMIC_RMW8_U_CMPXCHG_I32 :
+  WebAssemblyTerRMW<I32, "i32.atomic.rmw8_u.cmpxchg", 0xfe4a>;
+defm ATOMIC_RMW16_U_CMPXCHG_I32 :
+  WebAssemblyTerRMW<I32, "i32.atomic.rmw16_u.cmpxchg", 0xfe4b>;
+defm ATOMIC_RMW8_U_CMPXCHG_I64 :
+  WebAssemblyTerRMW<I64, "i64.atomic.rmw8_u.cmpxchg", 0xfe4c>;
+defm ATOMIC_RMW16_U_CMPXCHG_I64 :
+  WebAssemblyTerRMW<I64, "i64.atomic.rmw16_u.cmpxchg", 0xfe4d>;
+defm ATOMIC_RMW32_U_CMPXCHG_I64 :
+  WebAssemblyTerRMW<I64, "i64.atomic.rmw32_u.cmpxchg", 0xfe4e>;
+}
+
+// Select ternary RMWs with no constant offset.
+class TerRMWPatNoOffset<ValueType ty, PatFrag kind, NI inst> :
+  Pat<(ty (kind I32:$addr, ty:$exp, ty:$new)),
+      (inst 0, 0, I32:$addr, ty:$exp, ty:$new)>;
+
+// Select ternary RMWs with a constant offset.
+
+// Pattern with address + immediate offset
+class TerRMWPatImmOff<ValueType ty, PatFrag kind, PatFrag operand, NI inst> :
+  Pat<(ty (kind (operand I32:$addr, imm:$off), ty:$exp, ty:$new)),
+      (inst 0, imm:$off, I32:$addr, ty:$exp, ty:$new)>;
+
+class TerRMWPatGlobalAddr<ValueType ty, PatFrag kind, NI inst> :
+  Pat<(ty (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
+                ty:$exp, ty:$new)),
+      (inst 0, tglobaladdr:$off, I32:$addr, ty:$exp, ty:$new)>;
+
+class TerRMWPatExternalSym<ValueType ty, PatFrag kind, NI inst> :
+  Pat<(ty (kind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
+                ty:$exp, ty:$new)),
+      (inst 0, texternalsym:$off, I32:$addr, ty:$exp, ty:$new)>;
+
+// Select ternary RMWs with just a constant offset.
+class TerRMWPatOffsetOnly<ValueType ty, PatFrag kind, NI inst> :
+  Pat<(ty (kind imm:$off, ty:$exp, ty:$new)),
+      (inst 0, imm:$off, (CONST_I32 0), ty:$exp, ty:$new)>;
+
+class TerRMWPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
+  Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off), ty:$exp, ty:$new)),
+      (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$exp, ty:$new)>;
+
+class TerRMWPatExternSymOffOnly<ValueType ty, PatFrag kind, NI inst> :
+  Pat<(ty (kind (WebAssemblywrapper texternalsym:$off), ty:$exp, ty:$new)),
+      (inst 0, texternalsym:$off, (CONST_I32 0), ty:$exp, ty:$new)>;
+
+// Patterns for various addressing modes.
+multiclass TerRMWPattern<PatFrag rmw_32, PatFrag rmw_64, NI inst_32,
+                         NI inst_64> {
+  def : TerRMWPatNoOffset<i32, rmw_32, inst_32>;
+  def : TerRMWPatNoOffset<i64, rmw_64, inst_64>;
+
+  def : TerRMWPatImmOff<i32, rmw_32, regPlusImm, inst_32>;
+  def : TerRMWPatImmOff<i64, rmw_64, regPlusImm, inst_64>;
+  def : TerRMWPatImmOff<i32, rmw_32, or_is_add, inst_32>;
+  def : TerRMWPatImmOff<i64, rmw_64, or_is_add, inst_64>;
+
+  def : TerRMWPatGlobalAddr<i32, rmw_32, inst_32>;
+  def : TerRMWPatGlobalAddr<i64, rmw_64, inst_64>;
+
+  def : TerRMWPatExternalSym<i32, rmw_32, inst_32>;
+  def : TerRMWPatExternalSym<i64, rmw_64, inst_64>;
+
+  def : TerRMWPatOffsetOnly<i32, rmw_32, inst_32>;
+  def : TerRMWPatOffsetOnly<i64, rmw_64, inst_64>;
+
+  def : TerRMWPatGlobalAddrOffOnly<i32, rmw_32, inst_32>;
+  def : TerRMWPatGlobalAddrOffOnly<i64, rmw_64, inst_64>;
+
+  def : TerRMWPatExternSymOffOnly<i32, rmw_32, inst_32>;
+  def : TerRMWPatExternSymOffOnly<i64, rmw_64, inst_64>;
+}
+
+let Predicates = [HasAtomics] in {
+defm : TerRMWPattern<atomic_cmp_swap_32, atomic_cmp_swap_64,
+                     ATOMIC_RMW_CMPXCHG_I32, ATOMIC_RMW_CMPXCHG_I64>;
+} // Predicates = [HasAtomics]
+
+// Truncating & zero-extending ternary RMW patterns.
+// DAG legalization & optimization before instruction selection may introduce
+// additional nodes such as anyext or assertzext depending on operand types.
+class zext_ter_rmw_8_32<PatFrag kind> :
+  PatFrag<(ops node:$addr, node:$exp, node:$new),
+          (and (i32 (kind node:$addr, node:$exp, node:$new)), 255)>;
+class zext_ter_rmw_16_32<PatFrag kind> :
+  PatFrag<(ops node:$addr, node:$exp, node:$new),
+          (and (i32 (kind node:$addr, node:$exp, node:$new)), 65535)>;
+class zext_ter_rmw_8_64<PatFrag kind> :
+  PatFrag<(ops node:$addr, node:$exp, node:$new),
+          (zext (i32 (assertzext (i32 (kind node:$addr,
+                                            (i32 (trunc (i64 node:$exp))),
+                                            (i32 (trunc (i64 node:$new))))))))>;
+class zext_ter_rmw_16_64<PatFrag kind> : zext_ter_rmw_8_64<kind>;
+class zext_ter_rmw_32_64<PatFrag kind> :
+  PatFrag<(ops node:$addr, node:$exp, node:$new),
+          (zext (i32 (kind node:$addr,
+                           (i32 (trunc (i64 node:$exp))),
+                           (i32 (trunc (i64 node:$new))))))>;
+
+// Truncating & sign-extending ternary RMW patterns.
+// 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_ter_rmw_8_32<PatFrag kind> :
+  PatFrag<(ops node:$addr, node:$exp, node:$new),
+          (kind node:$addr, node:$exp, node:$new)>;
+class sext_ter_rmw_16_32<PatFrag kind> : sext_ter_rmw_8_32<kind>;
+class sext_ter_rmw_8_64<PatFrag kind> :
+  PatFrag<(ops node:$addr, node:$exp, node:$new),
+          (anyext (i32 (assertzext (i32
+            (kind node:$addr,
+                  (i32 (trunc (i64 node:$exp))),
+                  (i32 (trunc (i64 node:$new))))))))>;
+class sext_ter_rmw_16_64<PatFrag kind> : sext_ter_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 ternary RMWs.
+multiclass TerRMWTruncExtPattern<
+  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 ternary RMWs with no constant offset
+  def : TerRMWPatNoOffset<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatNoOffset<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatNoOffset<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatNoOffset<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+  def : TerRMWPatNoOffset<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+  def : TerRMWPatNoOffset<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatNoOffset<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatNoOffset<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatNoOffset<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+  // Truncating-extending ternary RMWs with a constant offset
+  def : TerRMWPatImmOff<i32, zext_ter_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
+  def : TerRMWPatImmOff<i32, zext_ter_rmw_16_32<rmw_16>, regPlusImm, inst16_32>;
+  def : TerRMWPatImmOff<i64, zext_ter_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
+  def : TerRMWPatImmOff<i64, zext_ter_rmw_16_64<rmw_16>, regPlusImm, inst16_64>;
+  def : TerRMWPatImmOff<i64, zext_ter_rmw_32_64<rmw_32>, regPlusImm, inst32_64>;
+  def : TerRMWPatImmOff<i32, zext_ter_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
+  def : TerRMWPatImmOff<i32, zext_ter_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
+  def : TerRMWPatImmOff<i64, zext_ter_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
+  def : TerRMWPatImmOff<i64, zext_ter_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
+  def : TerRMWPatImmOff<i64, zext_ter_rmw_32_64<rmw_32>, or_is_add, inst32_64>;
+
+  def : TerRMWPatImmOff<i32, sext_ter_rmw_8_32<rmw_8>, regPlusImm, inst8_32>;
+  def : TerRMWPatImmOff<i32, sext_ter_rmw_16_32<rmw_16>, regPlusImm, inst16_32>;
+  def : TerRMWPatImmOff<i64, sext_ter_rmw_8_64<rmw_8>, regPlusImm, inst8_64>;
+  def : TerRMWPatImmOff<i64, sext_ter_rmw_16_64<rmw_16>, regPlusImm, inst16_64>;
+  def : TerRMWPatImmOff<i32, sext_ter_rmw_8_32<rmw_8>, or_is_add, inst8_32>;
+  def : TerRMWPatImmOff<i32, sext_ter_rmw_16_32<rmw_16>, or_is_add, inst16_32>;
+  def : TerRMWPatImmOff<i64, sext_ter_rmw_8_64<rmw_8>, or_is_add, inst8_64>;
+  def : TerRMWPatImmOff<i64, sext_ter_rmw_16_64<rmw_16>, or_is_add, inst16_64>;
+
+  def : TerRMWPatGlobalAddr<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatGlobalAddr<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatGlobalAddr<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatGlobalAddr<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+  def : TerRMWPatGlobalAddr<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+  def : TerRMWPatGlobalAddr<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatGlobalAddr<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatGlobalAddr<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatGlobalAddr<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+  def : TerRMWPatExternalSym<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatExternalSym<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatExternalSym<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatExternalSym<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+  def : TerRMWPatExternalSym<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+  def : TerRMWPatExternalSym<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatExternalSym<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatExternalSym<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatExternalSym<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+  // Truncating-extending ternary RMWs with just a constant offset
+  def : TerRMWPatOffsetOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatOffsetOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatOffsetOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatOffsetOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+  def : TerRMWPatOffsetOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+  def : TerRMWPatOffsetOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatOffsetOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatOffsetOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatOffsetOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+  def : TerRMWPatGlobalAddrOffOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatGlobalAddrOffOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+  def : TerRMWPatGlobalAddrOffOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+  def : TerRMWPatGlobalAddrOffOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatGlobalAddrOffOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatGlobalAddrOffOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatGlobalAddrOffOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+
+  def : TerRMWPatExternSymOffOnly<i32, zext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatExternSymOffOnly<i32, zext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatExternSymOffOnly<i64, zext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatExternSymOffOnly<i64, zext_ter_rmw_16_64<rmw_16>, inst16_64>;
+  def : TerRMWPatExternSymOffOnly<i64, zext_ter_rmw_32_64<rmw_32>, inst32_64>;
+
+  def : TerRMWPatExternSymOffOnly<i32, sext_ter_rmw_8_32<rmw_8>, inst8_32>;
+  def : TerRMWPatExternSymOffOnly<i32, sext_ter_rmw_16_32<rmw_16>, inst16_32>;
+  def : TerRMWPatExternSymOffOnly<i64, sext_ter_rmw_8_64<rmw_8>, inst8_64>;
+  def : TerRMWPatExternSymOffOnly<i64, sext_ter_rmw_16_64<rmw_16>, inst16_64>;
+}
+
+let Predicates = [HasAtomics] in {
+defm : TerRMWTruncExtPattern<
+  atomic_cmp_swap_8, atomic_cmp_swap_16, atomic_cmp_swap_32, atomic_cmp_swap_64,
+  ATOMIC_RMW8_U_CMPXCHG_I32, ATOMIC_RMW16_U_CMPXCHG_I32,
+  ATOMIC_RMW8_U_CMPXCHG_I64, ATOMIC_RMW16_U_CMPXCHG_I64,
+  ATOMIC_RMW32_U_CMPXCHG_I64>;
+} // Predicates = [HasAtomics]

Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp?rev=338617&r1=338616&r2=338617&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp Wed Aug  1 12:40:28 2018
@@ -119,6 +119,8 @@ bool WebAssemblySetP2AlignOperands::runO
       case WebAssembly::ATOMIC_RMW8_U_XOR_I64:
       case WebAssembly::ATOMIC_RMW8_U_XCHG_I32:
       case WebAssembly::ATOMIC_RMW8_U_XCHG_I64:
+      case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I32:
+      case WebAssembly::ATOMIC_RMW8_U_CMPXCHG_I64:
       case WebAssembly::ATOMIC_RMW16_U_ADD_I32:
       case WebAssembly::ATOMIC_RMW16_U_ADD_I64:
       case WebAssembly::ATOMIC_RMW16_U_SUB_I32:
@@ -131,6 +133,8 @@ bool WebAssemblySetP2AlignOperands::runO
       case WebAssembly::ATOMIC_RMW16_U_XOR_I64:
       case WebAssembly::ATOMIC_RMW16_U_XCHG_I32:
       case WebAssembly::ATOMIC_RMW16_U_XCHG_I64:
+      case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I32:
+      case WebAssembly::ATOMIC_RMW16_U_CMPXCHG_I64:
       case WebAssembly::ATOMIC_RMW_ADD_I32:
       case WebAssembly::ATOMIC_RMW32_U_ADD_I64:
       case WebAssembly::ATOMIC_RMW_SUB_I32:
@@ -143,12 +147,15 @@ bool WebAssemblySetP2AlignOperands::runO
       case WebAssembly::ATOMIC_RMW32_U_XOR_I64:
       case WebAssembly::ATOMIC_RMW_XCHG_I32:
       case WebAssembly::ATOMIC_RMW32_U_XCHG_I64:
+      case WebAssembly::ATOMIC_RMW_CMPXCHG_I32:
+      case WebAssembly::ATOMIC_RMW32_U_CMPXCHG_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:
+      case WebAssembly::ATOMIC_RMW_CMPXCHG_I64:
         RewriteP2Align(MI, WebAssembly::LoadP2AlignOperandNo);
         break;
       case WebAssembly::STORE_I32:

Modified: llvm/trunk/test/CodeGen/WebAssembly/atomic-mem-consistency.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/atomic-mem-consistency.ll?rev=338617&r1=338616&r2=338617&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/atomic-mem-consistency.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/atomic-mem-consistency.ll Wed Aug  1 12:40:28 2018
@@ -139,3 +139,109 @@ define i32 @add_i32_seq_cst(i32* %p, i32
   %old = atomicrmw add i32* %p, i32 %v seq_cst
   ret i32 %old
 }
+
+; Ternary RMW instruction: cmpxchg
+; The success and failure ordering arguments specify how this cmpxchg
+; synchronizes with other atomic operations. Both ordering parameters must be at
+; least monotonic, the ordering constraint on failure must be no stronger than
+; that on success, and the failure ordering cannot be either release or acq_rel.
+
+; CHECK-LABEL: cmpxchg_i32_monotonic_monotonic:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_monotonic_monotonic(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new monotonic monotonic
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_acquire_monotonic:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_acquire_monotonic(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acquire monotonic
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_release_monotonic:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_release_monotonic(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new release monotonic
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_acq_rel_monotonic:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_acq_rel_monotonic(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acq_rel monotonic
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_seq_cst_monotonic:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_seq_cst_monotonic(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst monotonic
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_acquire_acquire:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_acquire_acquire(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acquire acquire
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_release_acquire:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_release_acquire(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new release acquire
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_acq_rel_acquire:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_acq_rel_acquire(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acq_rel acquire
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_seq_cst_acquire:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_seq_cst_acquire(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst acquire
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_seq_cst_seq_cst:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_seq_cst_seq_cst(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}

Modified: llvm/trunk/test/CodeGen/WebAssembly/atomic-rmw.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/atomic-rmw.ll?rev=338617&r1=338616&r2=338617&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/atomic-rmw.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/atomic-rmw.ll Wed Aug  1 12:40:28 2018
@@ -64,6 +64,27 @@ define i32 @xchg_i32(i32* %p, i32 %v) {
   ret i32 %old
 }
 
+; CHECK-LABEL: cmpxchg_i32_loaded_value:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_loaded_value(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_success:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: i32.eq $push1=, $pop0, $1{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define i1 @cmpxchg_i32_success(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst seq_cst
+  %succ = extractvalue { i32, i1 } %pair, 1
+  ret i1 %succ
+}
+
 ;===----------------------------------------------------------------------------
 ; Atomic read-modify-writes: 64-bit
 ;===----------------------------------------------------------------------------
@@ -122,6 +143,27 @@ define i64 @xchg_i64(i64* %p, i64 %v) {
   ret i64 %old
 }
 
+; CHECK-LABEL: cmpxchg_i64_loaded_value:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @cmpxchg_i64_loaded_value(i64* %p, i64 %exp, i64 %new) {
+  %pair = cmpxchg i64* %p, i64 %exp, i64 %new seq_cst seq_cst
+  %old = extractvalue { i64, i1 } %pair, 0
+  ret i64 %old
+}
+
+; CHECK-LABEL: cmpxchg_i64_success:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: i64.eq $push1=, $pop0, $1{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define i1 @cmpxchg_i64_success(i64* %p, i64 %exp, i64 %new) {
+  %pair = cmpxchg i64* %p, i64 %exp, i64 %new seq_cst seq_cst
+  %succ = extractvalue { i64, i1 } %pair, 1
+  ret i1 %succ
+}
+
 ;===----------------------------------------------------------------------------
 ; Atomic truncating & sign-extending RMWs
 ;===----------------------------------------------------------------------------
@@ -510,6 +552,81 @@ define i64 @xchg_sext_i32_i64(i32* %p, i
   ret i64 %e
 }
 
+; cmpxchg
+
+; CHECK-LABEL: cmpxchg_sext_i8_i32:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: i32.extend8_s $push1=, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define i32 @cmpxchg_sext_i8_i32(i8* %p, i32 %exp, i32 %new) {
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %p, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %e = sext i8 %old to i32
+  ret i32 %e
+}
+
+; CHECK-LABEL: cmpxchg_sext_i16_i32:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw16_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: i32.extend16_s $push1=, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define i32 @cmpxchg_sext_i16_i32(i16* %p, i32 %exp, i32 %new) {
+  %exp_t = trunc i32 %exp to i16
+  %new_t = trunc i32 %new to i16
+  %pair = cmpxchg i16* %p, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %e = sext i16 %old to i32
+  ret i32 %e
+}
+
+; CHECK-LABEL: cmpxchg_sext_i8_i64:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw8_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: i64.extend8_s $push1=, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define i64 @cmpxchg_sext_i8_i64(i8* %p, i64 %exp, i64 %new) {
+  %exp_t = trunc i64 %exp to i8
+  %new_t = trunc i64 %new to i8
+  %pair = cmpxchg i8* %p, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %e = sext i8 %old to i64
+  ret i64 %e
+}
+
+; CHECK-LABEL: cmpxchg_sext_i16_i64:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw16_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: i64.extend16_s $push1=, $pop0{{$}}
+; CHECK-NEXT: return $pop1{{$}}
+define i64 @cmpxchg_sext_i16_i64(i16* %p, i64 %exp, i64 %new) {
+  %exp_t = trunc i64 %exp to i16
+  %new_t = trunc i64 %new to i16
+  %pair = cmpxchg i16* %p, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %e = sext i16 %old to i64
+  ret i64 %e
+}
+
+; 32->64 sext rmw gets selected as i32.atomic.rmw.cmpxchg, i64_extend_s/i32
+; CHECK-LABEL: cmpxchg_sext_i32_i64:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i32.wrap/i64 $push1=, $1{{$}}
+; CHECK-NEXT: i32.wrap/i64 $push0=, $2{{$}}
+; CHECK-NEXT: i32.atomic.rmw.cmpxchg $push2=, 0($0), $pop1, $pop0{{$}}
+; CHECK-NEXT: i64.extend_s/i32 $push3=, $pop2{{$}}
+; CHECK-NEXT: return $pop3{{$}}
+define i64 @cmpxchg_sext_i32_i64(i32* %p, i64 %exp, i64 %new) {
+  %exp_t = trunc i64 %exp to i32
+  %new_t = trunc i64 %new to i32
+  %pair = cmpxchg i32* %p, i32 %exp_t, i32 %new_t seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  %e = sext i32 %old to i64
+  ret i64 %e
+}
+
 ;===----------------------------------------------------------------------------
 ; Atomic truncating & zero-extending RMWs
 ;===----------------------------------------------------------------------------
@@ -855,3 +972,70 @@ define i64 @xchg_zext_i32_i64(i32* %p, i
   %e = zext i32 %old to i64
   ret i64 %e
 }
+
+; cmpxchg
+
+; CHECK-LABEL: cmpxchg_zext_i8_i32:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_zext_i8_i32(i8* %p, i32 %exp, i32 %new) {
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %p, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %e = zext i8 %old to i32
+  ret i32 %e
+}
+
+; CHECK-LABEL: cmpxchg_zext_i16_i32:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw16_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_zext_i16_i32(i16* %p, i32 %exp, i32 %new) {
+  %exp_t = trunc i32 %exp to i16
+  %new_t = trunc i32 %new to i16
+  %pair = cmpxchg i16* %p, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %e = zext i16 %old to i32
+  ret i32 %e
+}
+
+; CHECK-LABEL: cmpxchg_zext_i8_i64:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw8_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @cmpxchg_zext_i8_i64(i8* %p, i64 %exp, i64 %new) {
+  %exp_t = trunc i64 %exp to i8
+  %new_t = trunc i64 %new to i8
+  %pair = cmpxchg i8* %p, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %e = zext i8 %old to i64
+  ret i64 %e
+}
+
+; CHECK-LABEL: cmpxchg_zext_i16_i64:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw16_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @cmpxchg_zext_i16_i64(i16* %p, i64 %exp, i64 %new) {
+  %exp_t = trunc i64 %exp to i16
+  %new_t = trunc i64 %new to i16
+  %pair = cmpxchg i16* %p, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %e = zext i16 %old to i64
+  ret i64 %e
+}
+
+; CHECK-LABEL: cmpxchg_zext_i32_i64:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw32_u.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @cmpxchg_zext_i32_i64(i32* %p, i64 %exp, i64 %new) {
+  %exp_t = trunc i64 %exp to i32
+  %new_t = trunc i64 %new to i32
+  %pair = cmpxchg i32* %p, i32 %exp_t, i32 %new_t seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  %e = zext i32 %old to i64
+  ret i64 %e
+}

Modified: llvm/trunk/test/CodeGen/WebAssembly/offset-atomics.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/offset-atomics.ll?rev=338617&r1=338616&r2=338617&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/offset-atomics.ll (original)
+++ llvm/trunk/test/CodeGen/WebAssembly/offset-atomics.ll Wed Aug  1 12:40:28 2018
@@ -1070,3 +1070,458 @@ define i8 @rmw_add_i8_i32_retvalue(i8 *%
   %old = atomicrmw add i8* %p, i8 %t seq_cst
   ret i8 %old
 }
+
+;===----------------------------------------------------------------------------
+; Atomic ternary read-modify-writes: 32-bit
+;===----------------------------------------------------------------------------
+
+; Basic RMW.
+
+; CHECK-LABEL: cmpxchg_i32_no_offset:
+; CHECK-NEXT: .param i32, i32, i32{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i32 @cmpxchg_i32_no_offset(i32* %p, i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; With an nuw add, we can fold an offset.
+
+; CHECK-LABEL: cmpxchg_i32_with_folded_offset:
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 24($0), $1, $2{{$}}
+define i32 @cmpxchg_i32_with_folded_offset(i32* %p, i32 %exp, i32 %new) {
+  %q = ptrtoint i32* %p to i32
+  %r = add nuw i32 %q, 24
+  %s = inttoptr i32 %r to i32*
+  %pair = cmpxchg i32* %s, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; With an inbounds gep, we can fold an offset.
+
+; CHECK-LABEL: cmpxchg_i32_with_folded_gep_offset:
+; CHECK: i32.atomic.rmw.cmpxchg $push0=, 24($0), $1, $2{{$}}
+define i32 @cmpxchg_i32_with_folded_gep_offset(i32* %p, i32 %exp, i32 %new) {
+  %s = getelementptr inbounds i32, i32* %p, i32 6
+  %pair = cmpxchg i32* %s, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; We can't fold a negative offset though, even with an inbounds gep.
+
+; CHECK-LABEL: cmpxchg_i32_with_unfolded_gep_negative_offset:
+; CHECK: i32.const $push0=, -24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push2=, 0($pop1), $1, $2{{$}}
+define i32 @cmpxchg_i32_with_unfolded_gep_negative_offset(i32* %p, i32 %exp, i32 %new) {
+  %s = getelementptr inbounds i32, i32* %p, i32 -6
+  %pair = cmpxchg i32* %s, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; Without nuw, and even with nsw, we can't fold an offset.
+
+; CHECK-LABEL: cmpxchg_i32_with_unfolded_offset:
+; CHECK: i32.const $push0=, 24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push2=, 0($pop1), $1, $2{{$}}
+define i32 @cmpxchg_i32_with_unfolded_offset(i32* %p, i32 %exp, i32 %new) {
+  %q = ptrtoint i32* %p to i32
+  %r = add nsw i32 %q, 24
+  %s = inttoptr i32 %r to i32*
+  %pair = cmpxchg i32* %s, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; Without inbounds, we can't fold a gep offset.
+
+; CHECK-LABEL: cmpxchg_i32_with_unfolded_gep_offset:
+; CHECK: i32.const $push0=, 24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push2=, 0($pop1), $1, $2{{$}}
+define i32 @cmpxchg_i32_with_unfolded_gep_offset(i32* %p, i32 %exp, i32 %new) {
+  %s = getelementptr i32, i32* %p, i32 6
+  %pair = cmpxchg i32* %s, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; When loading from a fixed address, materialize a zero.
+
+; CHECK-LABEL: cmpxchg_i32_from_numeric_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push1=, 42($pop0), $0, $1{{$}}
+define i32 @cmpxchg_i32_from_numeric_address(i32 %exp, i32 %new) {
+  %s = inttoptr i32 42 to i32*
+  %pair = cmpxchg i32* %s, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+; CHECK-LABEL: cmpxchg_i32_from_global_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.atomic.rmw.cmpxchg $push1=, gv($pop0), $0, $1{{$}}
+define i32 @cmpxchg_i32_from_global_address(i32 %exp, i32 %new) {
+  %pair = cmpxchg i32* @gv, i32 %exp, i32 %new seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  ret i32 %old
+}
+
+;===----------------------------------------------------------------------------
+; Atomic ternary read-modify-writes: 64-bit
+;===----------------------------------------------------------------------------
+
+; Basic RMW.
+
+; CHECK-LABEL: cmpxchg_i64_no_offset:
+; CHECK-NEXT: .param i32, i64, i64{{$}}
+; CHECK: i64.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
+; CHECK-NEXT: return $pop0{{$}}
+define i64 @cmpxchg_i64_no_offset(i64* %p, i64 %exp, i64 %new) {
+  %pair = cmpxchg i64* %p, i64 %exp, i64 %new seq_cst seq_cst
+  %old = extractvalue { i64, i1 } %pair, 0
+  ret i64 %old
+}
+
+; With an nuw add, we can fold an offset.
+
+; CHECK-LABEL: cmpxchg_i64_with_folded_offset:
+; CHECK: i64.atomic.rmw.cmpxchg $push0=, 24($0), $1, $2{{$}}
+define i64 @cmpxchg_i64_with_folded_offset(i64* %p, i64 %exp, i64 %new) {
+  %q = ptrtoint i64* %p to i32
+  %r = add nuw i32 %q, 24
+  %s = inttoptr i32 %r to i64*
+  %pair = cmpxchg i64* %s, i64 %exp, i64 %new seq_cst seq_cst
+  %old = extractvalue { i64, i1 } %pair, 0
+  ret i64 %old
+}
+
+; With an inbounds gep, we can fold an offset.
+
+; CHECK-LABEL: cmpxchg_i64_with_folded_gep_offset:
+; CHECK: i64.atomic.rmw.cmpxchg $push0=, 24($0), $1, $2{{$}}
+define i64 @cmpxchg_i64_with_folded_gep_offset(i64* %p, i64 %exp, i64 %new) {
+  %s = getelementptr inbounds i64, i64* %p, i32 3
+  %pair = cmpxchg i64* %s, i64 %exp, i64 %new seq_cst seq_cst
+  %old = extractvalue { i64, i1 } %pair, 0
+  ret i64 %old
+}
+
+; We can't fold a negative offset though, even with an inbounds gep.
+
+; CHECK-LABEL: cmpxchg_i64_with_unfolded_gep_negative_offset:
+; CHECK: i32.const $push0=, -24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i64.atomic.rmw.cmpxchg $push2=, 0($pop1), $1, $2{{$}}
+define i64 @cmpxchg_i64_with_unfolded_gep_negative_offset(i64* %p, i64 %exp, i64 %new) {
+  %s = getelementptr inbounds i64, i64* %p, i32 -3
+  %pair = cmpxchg i64* %s, i64 %exp, i64 %new seq_cst seq_cst
+  %old = extractvalue { i64, i1 } %pair, 0
+  ret i64 %old
+}
+
+; Without nuw, and even with nsw, we can't fold an offset.
+
+; CHECK-LABEL: cmpxchg_i64_with_unfolded_offset:
+; CHECK: i32.const $push0=, 24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i64.atomic.rmw.cmpxchg $push2=, 0($pop1), $1, $2{{$}}
+define i64 @cmpxchg_i64_with_unfolded_offset(i64* %p, i64 %exp, i64 %new) {
+  %q = ptrtoint i64* %p to i32
+  %r = add nsw i32 %q, 24
+  %s = inttoptr i32 %r to i64*
+  %pair = cmpxchg i64* %s, i64 %exp, i64 %new seq_cst seq_cst
+  %old = extractvalue { i64, i1 } %pair, 0
+  ret i64 %old
+}
+
+; Without inbounds, we can't fold a gep offset.
+
+; CHECK-LABEL: cmpxchg_i64_with_unfolded_gep_offset:
+; CHECK: i32.const $push0=, 24{{$}}
+; CHECK: i32.add $push1=, $0, $pop0{{$}}
+; CHECK: i64.atomic.rmw.cmpxchg $push2=, 0($pop1), $1, $2{{$}}
+define i64 @cmpxchg_i64_with_unfolded_gep_offset(i64* %p, i64 %exp, i64 %new) {
+  %s = getelementptr i64, i64* %p, i32 3
+  %pair = cmpxchg i64* %s, i64 %exp, i64 %new seq_cst seq_cst
+  %old = extractvalue { i64, i1 } %pair, 0
+  ret i64 %old
+}
+
+;===----------------------------------------------------------------------------
+; Atomic truncating & sign-extending ternary RMWs
+;===----------------------------------------------------------------------------
+
+; Fold an offset into a sign-extending rmw.
+
+; CHECK-LABEL: cmpxchg_i8_i32_s_with_folded_offset:
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push0=, 24($0), $1, $2{{$}}
+; CHECK-NEXT: i32.extend8_s $push1=, $pop0
+define i32 @cmpxchg_i8_i32_s_with_folded_offset(i8* %p, i32 %exp, i32 %new) {
+  %q = ptrtoint i8* %p to i32
+  %r = add nuw i32 %q, 24
+  %s = inttoptr i32 %r to i8*
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %s, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %u = sext i8 %old to i32
+  ret i32 %u
+}
+
+; 32->64 sext rmw gets selected as i32.atomic.rmw.cmpxchg, i64_extend_s/i32
+; CHECK-LABEL: cmpxchg_i32_i64_s_with_folded_offset:
+; CHECK: i32.wrap/i64 $push1=, $1
+; CHECK-NEXT: i32.wrap/i64 $push0=, $2
+; CHECK-NEXT: i32.atomic.rmw.cmpxchg $push2=, 24($0), $pop1, $pop0{{$}}
+; CHECK-NEXT: i64.extend_s/i32 $push3=, $pop2{{$}}
+define i64 @cmpxchg_i32_i64_s_with_folded_offset(i32* %p, i64 %exp, i64 %new) {
+  %q = ptrtoint i32* %p to i32
+  %r = add nuw i32 %q, 24
+  %s = inttoptr i32 %r to i32*
+  %exp_t = trunc i64 %exp to i32
+  %new_t = trunc i64 %new to i32
+  %pair = cmpxchg i32* %s, i32 %exp_t, i32 %new_t seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  %u = sext i32 %old to i64
+  ret i64 %u
+}
+
+; Fold a gep offset into a sign-extending rmw.
+
+; CHECK-LABEL: cmpxchg_i8_i32_s_with_folded_gep_offset:
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push0=, 24($0), $1, $2{{$}}
+; CHECK-NEXT: i32.extend8_s $push1=, $pop0
+define i32 @cmpxchg_i8_i32_s_with_folded_gep_offset(i8* %p, i32 %exp, i32 %new) {
+  %s = getelementptr inbounds i8, i8* %p, i32 24
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %s, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %u = sext i8 %old to i32
+  ret i32 %u
+}
+
+; CHECK-LABEL: cmpxchg_i16_i32_s_with_folded_gep_offset:
+; CHECK: i32.atomic.rmw16_u.cmpxchg $push0=, 48($0), $1, $2{{$}}
+; CHECK-NEXT: i32.extend16_s $push1=, $pop0
+define i32 @cmpxchg_i16_i32_s_with_folded_gep_offset(i16* %p, i32 %exp, i32 %new) {
+  %s = getelementptr inbounds i16, i16* %p, i32 24
+  %exp_t = trunc i32 %exp to i16
+  %new_t = trunc i32 %new to i16
+  %pair = cmpxchg i16* %s, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %u = sext i16 %old to i32
+  ret i32 %u
+}
+
+; CHECK-LABEL: cmpxchg_i16_i64_s_with_folded_gep_offset:
+; CHECK: i64.atomic.rmw16_u.cmpxchg $push0=, 48($0), $1, $2{{$}}
+; CHECK-NEXT: i64.extend16_s $push1=, $pop0
+define i64 @cmpxchg_i16_i64_s_with_folded_gep_offset(i16* %p, i64 %exp, i64 %new) {
+  %s = getelementptr inbounds i16, i16* %p, i32 24
+  %exp_t = trunc i64 %exp to i16
+  %new_t = trunc i64 %new to i16
+  %pair = cmpxchg i16* %s, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %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: cmpxchg_i8_i32_s_with_folded_or_offset:
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1, $2{{$}}
+; CHECK-NEXT: i32.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
+define i32 @cmpxchg_i8_i32_s_with_folded_or_offset(i32 %x, i32 %exp, i32 %new) {
+  %and = and i32 %x, -4
+  %t0 = inttoptr i32 %and to i8*
+  %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %arrayidx, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %conv = sext i8 %old to i32
+  ret i32 %conv
+}
+
+; CHECK-LABEL: cmpxchg_i8_i64_s_with_folded_or_offset:
+; CHECK: i64.atomic.rmw8_u.cmpxchg $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1, $2{{$}}
+; CHECK-NEXT: i64.extend8_s $push{{[0-9]+}}=, $pop[[R1]]{{$}}
+define i64 @cmpxchg_i8_i64_s_with_folded_or_offset(i32 %x, i64 %exp, i64 %new) {
+  %and = and i32 %x, -4
+  %t0 = inttoptr i32 %and to i8*
+  %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
+  %exp_t = trunc i64 %exp to i8
+  %new_t = trunc i64 %new to i8
+  %pair = cmpxchg i8* %arrayidx, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %conv = sext i8 %old to i64
+  ret i64 %conv
+}
+
+; When loading from a fixed address, materialize a zero.
+
+; CHECK-LABEL: cmpxchg_i16_i32_s_from_numeric_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.atomic.rmw16_u.cmpxchg $push1=, 42($pop0), $0, $1{{$}}
+; CHECK-NEXT: i32.extend16_s $push2=, $pop1
+define i32 @cmpxchg_i16_i32_s_from_numeric_address(i32 %exp, i32 %new) {
+  %s = inttoptr i32 42 to i16*
+  %exp_t = trunc i32 %exp to i16
+  %new_t = trunc i32 %new to i16
+  %pair = cmpxchg i16* %s, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %u = sext i16 %old to i32
+  ret i32 %u
+}
+
+; CHECK-LABEL: cmpxchg_i8_i32_s_from_global_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push1=, gv8($pop0), $0, $1{{$}}
+; CHECK-NEXT: i32.extend8_s $push2=, $pop1{{$}}
+define i32 @cmpxchg_i8_i32_s_from_global_address(i32 %exp, i32 %new) {
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* @gv8, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %u = sext i8 %old to i32
+  ret i32 %u
+}
+
+;===----------------------------------------------------------------------------
+; Atomic truncating & zero-extending ternary RMWs
+;===----------------------------------------------------------------------------
+
+; Fold an offset into a sign-extending rmw.
+
+; CHECK-LABEL: cmpxchg_i8_i32_z_with_folded_offset:
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push0=, 24($0), $1, $2{{$}}
+define i32 @cmpxchg_i8_i32_z_with_folded_offset(i8* %p, i32 %exp, i32 %new) {
+  %q = ptrtoint i8* %p to i32
+  %r = add nuw i32 %q, 24
+  %s = inttoptr i32 %r to i8*
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %s, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %u = zext i8 %old to i32
+  ret i32 %u
+}
+
+; CHECK-LABEL: cmpxchg_i32_i64_z_with_folded_offset:
+; CHECK: i64.atomic.rmw32_u.cmpxchg $push0=, 24($0), $1, $2{{$}}
+define i64 @cmpxchg_i32_i64_z_with_folded_offset(i32* %p, i64 %exp, i64 %new) {
+  %q = ptrtoint i32* %p to i32
+  %r = add nuw i32 %q, 24
+  %s = inttoptr i32 %r to i32*
+  %exp_t = trunc i64 %exp to i32
+  %new_t = trunc i64 %new to i32
+  %pair = cmpxchg i32* %s, i32 %exp_t, i32 %new_t seq_cst seq_cst
+  %old = extractvalue { i32, i1 } %pair, 0
+  %u = zext i32 %old to i64
+  ret i64 %u
+}
+
+; Fold a gep offset into a sign-extending rmw.
+
+; CHECK-LABEL: cmpxchg_i8_i32_z_with_folded_gep_offset:
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push0=, 24($0), $1, $2{{$}}
+define i32 @cmpxchg_i8_i32_z_with_folded_gep_offset(i8* %p, i32 %exp, i32 %new) {
+  %s = getelementptr inbounds i8, i8* %p, i32 24
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %s, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %u = zext i8 %old to i32
+  ret i32 %u
+}
+
+; CHECK-LABEL: cmpxchg_i16_i32_z_with_folded_gep_offset:
+; CHECK: i32.atomic.rmw16_u.cmpxchg $push0=, 48($0), $1, $2{{$}}
+define i32 @cmpxchg_i16_i32_z_with_folded_gep_offset(i16* %p, i32 %exp, i32 %new) {
+  %s = getelementptr inbounds i16, i16* %p, i32 24
+  %exp_t = trunc i32 %exp to i16
+  %new_t = trunc i32 %new to i16
+  %pair = cmpxchg i16* %s, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %u = zext i16 %old to i32
+  ret i32 %u
+}
+
+; CHECK-LABEL: cmpxchg_i16_i64_z_with_folded_gep_offset:
+; CHECK: i64.atomic.rmw16_u.cmpxchg $push0=, 48($0), $1, $2{{$}}
+define i64 @cmpxchg_i16_i64_z_with_folded_gep_offset(i16* %p, i64 %exp, i64 %new) {
+  %s = getelementptr inbounds i16, i16* %p, i32 24
+  %exp_t = trunc i64 %exp to i16
+  %new_t = trunc i64 %new to i16
+  %pair = cmpxchg i16* %s, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %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: cmpxchg_i8_i32_z_with_folded_or_offset:
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1, $2{{$}}
+define i32 @cmpxchg_i8_i32_z_with_folded_or_offset(i32 %x, i32 %exp, i32 %new) {
+  %and = and i32 %x, -4
+  %t0 = inttoptr i32 %and to i8*
+  %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* %arrayidx, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %conv = zext i8 %old to i32
+  ret i32 %conv
+}
+
+; CHECK-LABEL: cmpxchg_i8_i64_z_with_folded_or_offset:
+; CHECK: i64.atomic.rmw8_u.cmpxchg $push[[R1:[0-9]+]]=, 2($pop{{[0-9]+}}), $1, $2{{$}}
+define i64 @cmpxchg_i8_i64_z_with_folded_or_offset(i32 %x, i64 %exp, i64 %new) {
+  %and = and i32 %x, -4
+  %t0 = inttoptr i32 %and to i8*
+  %arrayidx = getelementptr inbounds i8, i8* %t0, i32 2
+  %exp_t = trunc i64 %exp to i8
+  %new_t = trunc i64 %new to i8
+  %pair = cmpxchg i8* %arrayidx, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %conv = zext i8 %old to i64
+  ret i64 %conv
+}
+
+; When loading from a fixed address, materialize a zero.
+
+; CHECK-LABEL: cmpxchg_i16_i32_z_from_numeric_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.atomic.rmw16_u.cmpxchg $push1=, 42($pop0), $0, $1{{$}}
+define i32 @cmpxchg_i16_i32_z_from_numeric_address(i32 %exp, i32 %new) {
+  %s = inttoptr i32 42 to i16*
+  %exp_t = trunc i32 %exp to i16
+  %new_t = trunc i32 %new to i16
+  %pair = cmpxchg i16* %s, i16 %exp_t, i16 %new_t seq_cst seq_cst
+  %old = extractvalue { i16, i1 } %pair, 0
+  %u = zext i16 %old to i32
+  ret i32 %u
+}
+
+; CHECK-LABEL: cmpxchg_i8_i32_z_from_global_address
+; CHECK: i32.const $push0=, 0{{$}}
+; CHECK: i32.atomic.rmw8_u.cmpxchg $push1=, gv8($pop0), $0, $1{{$}}
+define i32 @cmpxchg_i8_i32_z_from_global_address(i32 %exp, i32 %new) {
+  %exp_t = trunc i32 %exp to i8
+  %new_t = trunc i32 %new to i8
+  %pair = cmpxchg i8* @gv8, i8 %exp_t, i8 %new_t seq_cst seq_cst
+  %old = extractvalue { i8, i1 } %pair, 0
+  %u = zext i8 %old to i32
+  ret i32 %u
+}




More information about the llvm-commits mailing list