Index: lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -75,14 +75,66 @@ return; } - // Few custom selection stuff. If we need WebAssembly-specific selection, - // uncomment this block add corresponding case statements. - /* + // Few custom selection stuff. + SDLoc DL(Node); + MachineFunction &MF = CurDAG->getMachineFunction(); switch (Node->getOpcode()) { + case ISD::ATOMIC_FENCE: { + if (!MF.getSubtarget().hasAtomics()) + break; + + // Wasm does not have a fence instruction, but because all atomic + // instructions in wasm are sequentially consistent, we translate a fence to + // an idempotent atomic RMW instruction to a linear memory address. While + // any address can work, here we use a value stored in __stack_pointer wasm + // global because there's high chance that area is in cache. + // + // So the selected instructions will be in the form of: + // %addr = get_global $__stack_pointer + // i32.atomic.rmw.or %addr, 0 + SDValue StackPtrSym = CurDAG->getExternalSymbol( + "__stack_pointer", TLI->getPointerTy(CurDAG->getDataLayout())); + MachineSDNode *GetGlobal = + CurDAG->getMachineNode(WebAssembly::GET_GLOBAL_I32, // opcode + DL, // debug loc + MVT::i32, // result type + StackPtrSym // __stack_pointer symbol + ); + + SDValue Zero = CurDAG->getTargetConstant(0, DL, MVT::i32); + auto *MMO = MF.getMachineMemOperand( + MachinePointerInfo::getUnknownStack(MF), + // FIXME Volatile isn't really correct, but currently all LLVM atomic + // instructions are treated as volatiles in the backend, so we should be + // consistent. + MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad | + MachineMemOperand::MOStore, + 4, 4, AAMDNodes(), nullptr, SyncScope::System, + AtomicOrdering::SequentiallyConsistent); + MachineSDNode *AtomicRMW = + CurDAG->getMachineNode(WebAssembly::ATOMIC_RMW_OR_I32, // opcode + DL, // debug loc + MVT::i32, // result type + MVT::Other, // outchain + { + Zero, // alignment + Zero, // offset + SDValue(GetGlobal, 0), // __stack_pointer + Zero, // OR with 0 to make it idempotent + Node->getOperand(0) // inchain + }); + MachineSDNode::mmo_iterator MemOp = MF.allocateMemRefsArray(1); + MemOp[0] = MMO; + AtomicRMW->setMemRefs(MemOp, MemOp + 1); + + ReplaceUses(SDValue(Node, 0), SDValue(AtomicRMW, 1)); + CurDAG->RemoveDeadNode(Node); + return; + } + default: break; } - */ // Select the default instruction. SelectCode(Node); Index: test/CodeGen/WebAssembly/atomic-fence.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/atomic-fence.ll @@ -0,0 +1,30 @@ +; 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" + +; CHECK-LABEL: fence_seq_cst: +; CHECK: get_global $push0=, __stack_pointer +; CHECK-NEXT: i32.atomic.rmw.or $drop=, 0($pop0), 0 +define void @fence_seq_cst() { + fence seq_cst + ret void +} + +; Fences with weaker memory orderings should be treated the same because atomic +; memory access in wasm are sequentially consistent. + +; CHECK-LABEL: fence_weak: +; CHECK: get_global $push{{[0-9]+}}=, __stack_pointer +; CHECK: i32.atomic.rmw.or +; CHECK: i32.atomic.rmw.or +; CHECK: i32.atomic.rmw.or +define void @fence_weak() { + fence acquire + fence release + fence acq_rel + ret void +}