[WebAssembly] Support acquire-release atomics in CodeGen (#184900)
Set the correct memory ordering for relaxed atomics after ISel. This allows SelectionDAG to keep the simple generic selection for target-independent AtomicLoad nodes, but keeps the ordering immediate correct in the MIR. Notably, the MachineMemOperand still has the original memory ordering and MIR passes would use that rather than the ordering immedate to make their code motion decisions (if we had any for Wasm, which we don't).
This commit is contained in:
parent
6b1ad6bbfd
commit
18d85a33cc
5 changed files with 1133 additions and 139 deletions
|
|
@ -16,6 +16,7 @@
|
|||
#include "WebAssemblyISelLowering.h"
|
||||
#include "WebAssemblyTargetMachine.h"
|
||||
#include "WebAssemblyUtilities.h"
|
||||
#include "llvm/BinaryFormat/Wasm.h"
|
||||
#include "llvm/CodeGen/MachineFrameInfo.h"
|
||||
#include "llvm/CodeGen/SelectionDAGISel.h"
|
||||
#include "llvm/CodeGen/WasmEHFuncInfo.h"
|
||||
|
|
@ -69,6 +70,12 @@ public:
|
|||
|
||||
bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr);
|
||||
bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr);
|
||||
bool SelectAtomicAddrOperands(SDNode *Op, SDValue N, SDValue &Offset,
|
||||
SDValue &Addr, SDValue &Order, bool Is64);
|
||||
bool SelectAtomicAddrOperands32(SDNode *Op, SDValue N, SDValue &Offset,
|
||||
SDValue &Addr, SDValue &Order);
|
||||
bool SelectAtomicAddrOperands64(SDNode *Op, SDValue N, SDValue &Offset,
|
||||
SDValue &Addr, SDValue &Order);
|
||||
|
||||
// Include the pieces autogenerated from the target description.
|
||||
#include "WebAssemblyGenDAGISel.inc"
|
||||
|
|
@ -174,6 +181,25 @@ static APInt encodeFunctionSignature(SelectionDAG *DAG, SDLoc &DL,
|
|||
return Sig;
|
||||
}
|
||||
|
||||
static unsigned getWebAssemblyMemoryOrder(AtomicOrdering Ordering) {
|
||||
unsigned OrderVal = wasm::WASM_MEM_ORDER_SEQ_CST;
|
||||
switch (Ordering) {
|
||||
case AtomicOrdering::Unordered:
|
||||
case AtomicOrdering::Monotonic:
|
||||
case AtomicOrdering::Acquire:
|
||||
case AtomicOrdering::Release:
|
||||
case AtomicOrdering::AcquireRelease:
|
||||
OrderVal = wasm::WASM_MEM_ORDER_ACQ_REL;
|
||||
break;
|
||||
case AtomicOrdering::SequentiallyConsistent:
|
||||
OrderVal = wasm::WASM_MEM_ORDER_SEQ_CST;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Invalid atomic ordering");
|
||||
}
|
||||
return OrderVal;
|
||||
}
|
||||
|
||||
void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
|
||||
// If we have a custom node, we already have selected!
|
||||
if (Node->isMachineOpcode()) {
|
||||
|
|
@ -186,7 +212,6 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
|
|||
auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
|
||||
: WebAssembly::GLOBAL_GET_I32;
|
||||
|
||||
// Few custom selection stuff.
|
||||
SDLoc DL(Node);
|
||||
MachineFunction &MF = CurDAG->getMachineFunction();
|
||||
switch (Node->getOpcode()) {
|
||||
|
|
@ -207,17 +232,22 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
|
|||
Node->getOperand(0) // inchain
|
||||
);
|
||||
break;
|
||||
case SyncScope::System:
|
||||
// Currently wasm only supports sequentially consistent atomics, so we
|
||||
// always set the order to 0 (sequentially consistent).
|
||||
case SyncScope::System: {
|
||||
unsigned Order = wasm::WASM_MEM_ORDER_SEQ_CST;
|
||||
if (MF.getSubtarget<WebAssemblySubtarget>().hasRelaxedAtomics()) {
|
||||
auto Ordering =
|
||||
static_cast<AtomicOrdering>(Node->getConstantOperandVal(1));
|
||||
Order = getWebAssemblyMemoryOrder(Ordering);
|
||||
}
|
||||
Fence = CurDAG->getMachineNode(
|
||||
WebAssembly::ATOMIC_FENCE,
|
||||
DL, // debug loc
|
||||
MVT::Other, // outchain type
|
||||
CurDAG->getTargetConstant(0, DL, MVT::i32), // order
|
||||
Node->getOperand(0) // inchain
|
||||
DL, // debug loc
|
||||
MVT::Other, // outchain type
|
||||
CurDAG->getTargetConstant(Order, DL, MVT::i32), // order
|
||||
Node->getOperand(0) // inchain
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Unknown scope!");
|
||||
}
|
||||
|
|
@ -548,6 +578,65 @@ bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset,
|
|||
return SelectAddrOperands(MVT::i64, WebAssembly::CONST_I64, Op, Offset, Addr);
|
||||
}
|
||||
|
||||
static MemSDNode *findMemSDNode(SDNode *N) {
|
||||
while (N) {
|
||||
if (auto *MemNode = dyn_cast<MemSDNode>(N))
|
||||
return MemNode;
|
||||
switch (N->getOpcode()) {
|
||||
case ISD::ZERO_EXTEND:
|
||||
case ISD::SIGN_EXTEND:
|
||||
case ISD::ANY_EXTEND:
|
||||
case ISD::SIGN_EXTEND_INREG:
|
||||
case ISD::AssertZext:
|
||||
case ISD::AssertSext:
|
||||
case ISD::TRUNCATE:
|
||||
case ISD::BITCAST:
|
||||
case ISD::AND:
|
||||
N = N->getOperand(0).getNode();
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WebAssemblyDAGToDAGISel::SelectAtomicAddrOperands(SDNode *Op, SDValue N,
|
||||
SDValue &Offset,
|
||||
SDValue &Addr,
|
||||
SDValue &Order,
|
||||
bool Is64) {
|
||||
auto *MemNode = findMemSDNode(Op);
|
||||
if (!MemNode)
|
||||
return false;
|
||||
|
||||
bool Match = Is64 ? SelectAddrOperands64(N, Offset, Addr)
|
||||
: SelectAddrOperands32(N, Offset, Addr);
|
||||
if (!Match)
|
||||
return false;
|
||||
|
||||
auto Ordering = MemNode->getMergedOrdering();
|
||||
unsigned OrderVal = wasm::WASM_MEM_ORDER_SEQ_CST;
|
||||
if (Subtarget->hasRelaxedAtomics())
|
||||
OrderVal = getWebAssemblyMemoryOrder(Ordering);
|
||||
Order = CurDAG->getTargetConstant(OrderVal, SDLoc(Op), MVT::i32);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebAssemblyDAGToDAGISel::SelectAtomicAddrOperands32(SDNode *Op, SDValue N,
|
||||
SDValue &Offset,
|
||||
SDValue &Addr,
|
||||
SDValue &Order) {
|
||||
return SelectAtomicAddrOperands(Op, N, Offset, Addr, Order, /*Is64=*/false);
|
||||
}
|
||||
|
||||
bool WebAssemblyDAGToDAGISel::SelectAtomicAddrOperands64(SDNode *Op, SDValue N,
|
||||
SDValue &Offset,
|
||||
SDValue &Addr,
|
||||
SDValue &Order) {
|
||||
return SelectAtomicAddrOperands(Op, N, Offset, Addr, Order, /*Is64=*/true);
|
||||
}
|
||||
|
||||
/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready
|
||||
/// for instruction scheduling.
|
||||
FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@
|
|||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def AtomicAddrOps32 : ComplexPattern<i32, 3, "SelectAtomicAddrOperands32"> {
|
||||
let WantsRoot = 1;
|
||||
}
|
||||
def AtomicAddrOps64 : ComplexPattern<i64, 3, "SelectAtomicAddrOperands64"> {
|
||||
let WantsRoot = 1;
|
||||
}
|
||||
|
||||
let UseNamedOperandTable = 1 in
|
||||
multiclass ATOMIC_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
|
||||
list<dag> pattern_r, string asmstr_r,
|
||||
|
|
@ -150,11 +157,11 @@ multiclass AtomicLoad<WebAssemblyRegClass rc, string name, int atomic_op> {
|
|||
}
|
||||
|
||||
multiclass AtomicLoadPat<ValueType ty, SDPatternOperator kind, string Name> {
|
||||
def : Pat<(ty (kind (AddrOps32 offset32_op:$offset, I32:$addr))),
|
||||
(!cast<NI>(Name # "_A32") 0, 0, offset32_op:$offset, I32:$addr)>,
|
||||
def : Pat<(ty (kind (AtomicAddrOps32 offset32_op:$offset, I32:$addr, i32imm:$order))),
|
||||
(!cast<NI>(Name # "_A32") $order, 0, offset32_op:$offset, I32:$addr)>,
|
||||
Requires<[HasAddr32, HasAtomics]>;
|
||||
def : Pat<(ty (kind (AddrOps64 offset64_op:$offset, I64:$addr))),
|
||||
(!cast<NI>(Name # "_A64") 0, 0, offset64_op:$offset, I64:$addr)>,
|
||||
def : Pat<(ty (kind (AtomicAddrOps64 offset64_op:$offset, I64:$addr, i32imm:$order))),
|
||||
(!cast<NI>(Name # "_A64") $order, 0, offset64_op:$offset, I64:$addr)>,
|
||||
Requires<[HasAddr64, HasAtomics]>;
|
||||
}
|
||||
|
||||
|
|
@ -245,18 +252,12 @@ multiclass AtomicStore<WebAssemblyRegClass rc, string name, int atomic_op> {
|
|||
defm ATOMIC_STORE_I32 : AtomicStore<I32, "i32.atomic.store", 0x17>;
|
||||
defm ATOMIC_STORE_I64 : AtomicStore<I64, "i64.atomic.store", 0x18>;
|
||||
|
||||
// We used to need an 'atomic' version of store patterns because store and atomic_store
|
||||
// nodes have different operand orders.
|
||||
//
|
||||
// TODO: This is no longer true and atomic_store and store patterns
|
||||
// can be unified.
|
||||
|
||||
multiclass AStorePat<ValueType ty, PatFrag kind, string inst> {
|
||||
def : Pat<(kind ty:$val, (AddrOps32 offset32_op:$offset, I32:$addr)),
|
||||
(!cast<NI>(inst#_A32) 0, 0, $offset, $addr, $val)>,
|
||||
def : Pat<(kind ty:$val, (AtomicAddrOps32 offset32_op:$offset, I32:$addr, i32imm:$order)),
|
||||
(!cast<NI>(inst#_A32) $order, 0, $offset, $addr, $val)>,
|
||||
Requires<[HasAddr32, HasAtomics]>;
|
||||
def : Pat<(kind ty:$val, (AddrOps64 offset64_op:$offset, I64:$addr)),
|
||||
(!cast<NI>(inst#_A64) 0, 0, $offset, $addr, $val)>,
|
||||
def : Pat<(kind ty:$val, (AtomicAddrOps64 offset64_op:$offset, I64:$addr, i32imm:$order)),
|
||||
(!cast<NI>(inst#_A64) $order, 0, $offset, $addr, $val)>,
|
||||
Requires<[HasAddr64, HasAtomics]>;
|
||||
}
|
||||
defm : AStorePat<i32, atomic_store_32, "ATOMIC_STORE_I32">;
|
||||
|
|
@ -393,11 +394,13 @@ defm ATOMIC_RMW32_U_XCHG_I64 :
|
|||
WebAssemblyBinRMW<I64, "i64.atomic.rmw32.xchg_u", 0x47>;
|
||||
|
||||
multiclass BinRMWPat<ValueType ty, PatFrag kind, string inst> {
|
||||
def : Pat<(ty (kind (AddrOps32 offset32_op:$offset, I32:$addr), ty:$val)),
|
||||
(!cast<NI>(inst#_A32) 0, 0, $offset, $addr, $val)>,
|
||||
def : Pat<(ty (kind (AtomicAddrOps32 offset32_op:$offset, I32:$addr,
|
||||
i32imm:$order), ty:$val)),
|
||||
(!cast<NI>(inst#_A32) $order, 0, $offset, $addr, $val)>,
|
||||
Requires<[HasAddr32, HasAtomics]>;
|
||||
def : Pat<(ty (kind (AddrOps64 offset64_op:$offset, I64:$addr), ty:$val)),
|
||||
(!cast<NI>(inst#_A64) 0, 0, $offset, $addr, $val)>,
|
||||
def : Pat<(ty (kind (AtomicAddrOps64 offset64_op:$offset, I64:$addr,
|
||||
i32imm:$order), ty:$val)),
|
||||
(!cast<NI>(inst#_A64) $order, 0, $offset, $addr, $val)>,
|
||||
Requires<[HasAddr64, HasAtomics]>;
|
||||
}
|
||||
|
||||
|
|
@ -536,11 +539,11 @@ defm ATOMIC_RMW32_U_CMPXCHG_I64 :
|
|||
WebAssemblyTerRMW<I64, "i64.atomic.rmw32.cmpxchg_u", 0x4e>;
|
||||
|
||||
multiclass TerRMWPat<ValueType ty, PatFrag kind, string inst> {
|
||||
def : Pat<(ty (kind (AddrOps32 offset32_op:$offset, I32:$addr), ty:$exp, ty:$new)),
|
||||
(!cast<NI>(inst#_A32) 0, 0, $offset, $addr, $exp, $new)>,
|
||||
def : Pat<(ty (kind (AtomicAddrOps32 offset32_op:$offset, I32:$addr, i32imm:$order), ty:$exp, ty:$new)),
|
||||
(!cast<NI>(inst#_A32) $order, 0, $offset, $addr, $exp, $new)>,
|
||||
Requires<[HasAddr32, HasAtomics]>;
|
||||
def : Pat<(ty (kind (AddrOps64 offset64_op:$offset, I64:$addr), ty:$exp, ty:$new)),
|
||||
(!cast<NI>(inst#_A64) 0, 0, $offset, $addr, $exp, $new)>,
|
||||
def : Pat<(ty (kind (AtomicAddrOps64 offset64_op:$offset, I64:$addr, i32imm:$order), ty:$exp, ty:$new)),
|
||||
(!cast<NI>(inst#_A64) $order, 0, $offset, $addr, $exp, $new)>,
|
||||
Requires<[HasAddr64, HasAtomics]>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +1,140 @@
|
|||
; RUN: llc < %s | FileCheck %s --check-prefix NOATOMIC
|
||||
; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+atomics | FileCheck %s
|
||||
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
|
||||
; RUN: llc < %s --mtriple=wasm32 | FileCheck %s --check-prefixes=NO-ATOMICS
|
||||
; RUN: llc < %s --mtriple=wasm32 -mattr=+atomics | FileCheck %s --check-prefixes=ATOMICS
|
||||
; RUN: llc < %s --mtriple=wasm32 -mattr=+atomics,+relaxed-atomics | FileCheck %s --check-prefixes=RELAXED32
|
||||
; RUN: llc < %s --mtriple=wasm64 -mattr=+atomics,+relaxed-atomics | FileCheck %s --check-prefixes=RELAXED64
|
||||
|
||||
; A multithread fence is lowered to an atomic.fence instruction.
|
||||
; CHECK-LABEL: multithread_fence:
|
||||
; CHECK: atomic.fence
|
||||
; NOATOMIC-NOT: i32.atomic.rmw.or
|
||||
define void @multithread_fence() {
|
||||
fence seq_cst
|
||||
define void @fence_acquire() {
|
||||
; NO-ATOMICS-LABEL: fence_acquire:
|
||||
; NO-ATOMICS: .functype fence_acquire () -> ()
|
||||
; NO-ATOMICS-NEXT: # %bb.0:
|
||||
; NO-ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; ATOMICS-LABEL: fence_acquire:
|
||||
; ATOMICS: .functype fence_acquire () -> ()
|
||||
; ATOMICS-NEXT: # %bb.0:
|
||||
; ATOMICS-NEXT: atomic.fence
|
||||
; ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED32-LABEL: fence_acquire:
|
||||
; RELAXED32: .functype fence_acquire () -> ()
|
||||
; RELAXED32-NEXT: # %bb.0:
|
||||
; RELAXED32-NEXT: atomic.fence acqrel
|
||||
; RELAXED32-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED64-LABEL: fence_acquire:
|
||||
; RELAXED64: .functype fence_acquire () -> ()
|
||||
; RELAXED64-NEXT: # %bb.0:
|
||||
; RELAXED64-NEXT: atomic.fence acqrel
|
||||
; RELAXED64-NEXT: # fallthrough-return
|
||||
fence acquire
|
||||
ret void
|
||||
}
|
||||
|
||||
; Fences with weaker memory orderings than seq_cst should be treated the same
|
||||
; because atomic memory access in wasm are sequentially consistent.
|
||||
; CHECK-LABEL: multithread_weak_fence:
|
||||
; CHECK: atomic.fence
|
||||
; CHECK-NEXT: atomic.fence
|
||||
; CHECK-NEXT: atomic.fence
|
||||
define void @multithread_weak_fence() {
|
||||
fence acquire
|
||||
define void @fence_release() {
|
||||
; NO-ATOMICS-LABEL: fence_release:
|
||||
; NO-ATOMICS: .functype fence_release () -> ()
|
||||
; NO-ATOMICS-NEXT: # %bb.0:
|
||||
; NO-ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; ATOMICS-LABEL: fence_release:
|
||||
; ATOMICS: .functype fence_release () -> ()
|
||||
; ATOMICS-NEXT: # %bb.0:
|
||||
; ATOMICS-NEXT: atomic.fence
|
||||
; ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED32-LABEL: fence_release:
|
||||
; RELAXED32: .functype fence_release () -> ()
|
||||
; RELAXED32-NEXT: # %bb.0:
|
||||
; RELAXED32-NEXT: atomic.fence acqrel
|
||||
; RELAXED32-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED64-LABEL: fence_release:
|
||||
; RELAXED64: .functype fence_release () -> ()
|
||||
; RELAXED64-NEXT: # %bb.0:
|
||||
; RELAXED64-NEXT: atomic.fence acqrel
|
||||
; RELAXED64-NEXT: # fallthrough-return
|
||||
fence release
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @fence_acq_rel() {
|
||||
; NO-ATOMICS-LABEL: fence_acq_rel:
|
||||
; NO-ATOMICS: .functype fence_acq_rel () -> ()
|
||||
; NO-ATOMICS-NEXT: # %bb.0:
|
||||
; NO-ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; ATOMICS-LABEL: fence_acq_rel:
|
||||
; ATOMICS: .functype fence_acq_rel () -> ()
|
||||
; ATOMICS-NEXT: # %bb.0:
|
||||
; ATOMICS-NEXT: atomic.fence
|
||||
; ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED32-LABEL: fence_acq_rel:
|
||||
; RELAXED32: .functype fence_acq_rel () -> ()
|
||||
; RELAXED32-NEXT: # %bb.0:
|
||||
; RELAXED32-NEXT: atomic.fence acqrel
|
||||
; RELAXED32-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED64-LABEL: fence_acq_rel:
|
||||
; RELAXED64: .functype fence_acq_rel () -> ()
|
||||
; RELAXED64-NEXT: # %bb.0:
|
||||
; RELAXED64-NEXT: atomic.fence acqrel
|
||||
; RELAXED64-NEXT: # fallthrough-return
|
||||
fence acq_rel
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @fence_seq_cst() {
|
||||
; NO-ATOMICS-LABEL: fence_seq_cst:
|
||||
; NO-ATOMICS: .functype fence_seq_cst () -> ()
|
||||
; NO-ATOMICS-NEXT: # %bb.0:
|
||||
; NO-ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; ATOMICS-LABEL: fence_seq_cst:
|
||||
; ATOMICS: .functype fence_seq_cst () -> ()
|
||||
; ATOMICS-NEXT: # %bb.0:
|
||||
; ATOMICS-NEXT: atomic.fence
|
||||
; ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED32-LABEL: fence_seq_cst:
|
||||
; RELAXED32: .functype fence_seq_cst () -> ()
|
||||
; RELAXED32-NEXT: # %bb.0:
|
||||
; RELAXED32-NEXT: atomic.fence seqcst
|
||||
; RELAXED32-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED64-LABEL: fence_seq_cst:
|
||||
; RELAXED64: .functype fence_seq_cst () -> ()
|
||||
; RELAXED64-NEXT: # %bb.0:
|
||||
; RELAXED64-NEXT: atomic.fence seqcst
|
||||
; RELAXED64-NEXT: # fallthrough-return
|
||||
fence seq_cst
|
||||
ret void
|
||||
}
|
||||
|
||||
; A singlethread fence becomes compiler_fence instruction, a pseudo instruction
|
||||
; that acts as a compiler barrier. The barrier should not be emitted to .s file.
|
||||
; CHECK-LABEL: singlethread_fence:
|
||||
; CHECK-NOT: compiler_fence
|
||||
; CHECK-NOT: atomic_fence
|
||||
define void @singlethread_fence() {
|
||||
; NO-ATOMICS-LABEL: singlethread_fence:
|
||||
; NO-ATOMICS: .functype singlethread_fence () -> ()
|
||||
; NO-ATOMICS-NEXT: # %bb.0:
|
||||
; NO-ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; ATOMICS-LABEL: singlethread_fence:
|
||||
; ATOMICS: .functype singlethread_fence () -> ()
|
||||
; ATOMICS-NEXT: # %bb.0:
|
||||
; ATOMICS-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED32-LABEL: singlethread_fence:
|
||||
; RELAXED32: .functype singlethread_fence () -> ()
|
||||
; RELAXED32-NEXT: # %bb.0:
|
||||
; RELAXED32-NEXT: # fallthrough-return
|
||||
;
|
||||
; RELAXED64-LABEL: singlethread_fence:
|
||||
; RELAXED64: .functype singlethread_fence () -> ()
|
||||
; RELAXED64-NEXT: # %bb.0:
|
||||
; RELAXED64-NEXT: # fallthrough-return
|
||||
fence syncscope("singlethread") seq_cst
|
||||
fence syncscope("singlethread") acquire
|
||||
fence syncscope("singlethread") release
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -41,6 +41,26 @@ define i32 @load_i32_with_folded_gep_offset(ptr %p) {
|
|||
ret i32 %t
|
||||
}
|
||||
|
||||
; Same for nusw.
|
||||
|
||||
; CHECK-LABEL: load_i32_with_folded_gep_offset_nusw:
|
||||
; CHECK: i32.atomic.load $push0=, 24($0){{$}}
|
||||
define i32 @load_i32_with_folded_gep_offset_nusw(ptr %p) {
|
||||
%s = getelementptr nusw i32, ptr %p, i32 6
|
||||
%t = load atomic i32, ptr %s seq_cst, align 4
|
||||
ret i32 %t
|
||||
}
|
||||
|
||||
; For nuw we don't need the offset to be positive.
|
||||
|
||||
; CHECK-LABEL: load_i32_with_folded_gep_offset_nuw:
|
||||
; CHECK: i32.atomic.load $push0=, -24($0){{$}}
|
||||
define i32 @load_i32_with_folded_gep_offset_nuw(ptr %p) {
|
||||
%s = getelementptr nuw i32, ptr %p, i32 -6
|
||||
%t = load atomic i32, ptr %s seq_cst, align 4
|
||||
ret i32 %t
|
||||
}
|
||||
|
||||
; We can't fold a negative offset though, even with an inbounds gep.
|
||||
|
||||
; CHECK-LABEL: load_i32_with_unfolded_gep_negative_offset:
|
||||
|
|
@ -99,8 +119,19 @@ define i32 @load_i32_from_global_address() {
|
|||
ret i32 %t
|
||||
}
|
||||
|
||||
define i32 @load_i32_global_with_folded_gep_offset_nonconst_nuw(i32 %idx) {
|
||||
; CHECK-LABEL: load_i32_global_with_folded_gep_offset_nonconst_nuw:
|
||||
; CHECK: i32.const $push0=, 2
|
||||
; CHECK: i32.shl $push1=, $0, $pop0
|
||||
; CHECK: i32.atomic.load $push2=, gv($pop1)
|
||||
%s = getelementptr nuw i32, ptr @gv, i32 %idx
|
||||
%t = load atomic i32, ptr %s seq_cst, align 4
|
||||
ret i32 %t
|
||||
}
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
; Atomic loads: 64-bit
|
||||
|
||||
;===----------------------------------------------------------------------------
|
||||
|
||||
; Basic load.
|
||||
|
|
|
|||
Loading…
Reference in a new issue