singlethread fences only synchronize with code running on the same hardware thread (i.e. signal handlers). Because of this, we need to prevent instruction reordering, but do not need to emit hardware fence instructions.
The implementation strategy here matches many other backends. The main motivation of this patch is to introduce the MEMBARRIER node and get some test coverage for it.
Differential Revision: https://reviews.llvm.org/D141311
case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
LowerHWASAN_CHECK_MEMACCESS(*MI);
return;
+ case RISCV::PseudoMemBarrier:
+ OutStreamer->emitRawComment("MEMBARRIER");
+ return;
}
MCInst TmpInst;
setMaxAtomicSizeInBitsSupported(0);
}
+ setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom);
+
setBooleanContents(ZeroOrOneBooleanContent);
if (Subtarget.hasVInstructions()) {
return SDValue();
}
+static SDValue LowerATOMIC_FENCE(SDValue Op, SelectionDAG &DAG) {
+ SDLoc dl(Op);
+ SyncScope::ID FenceSSID =
+ static_cast<SyncScope::ID>(Op.getConstantOperandVal(2));
+
+ // singlethread fences only synchronize with signal handlers on the same
+ // thread and thus only need to preserve instruction order, not actually
+ // enforce memory ordering.
+ if (FenceSSID == SyncScope::SingleThread)
+ // MEMBARRIER is a compiler barrier; it codegens to a no-op.
+ return DAG.getNode(ISD::MEMBARRIER, dl, MVT::Other, Op.getOperand(0));
+
+ return Op;
+}
+
SDValue RISCVTargetLowering::LowerOperation(SDValue Op,
SelectionDAG &DAG) const {
switch (Op.getOpcode()) {
default:
report_fatal_error("unimplemented operand");
+ case ISD::ATOMIC_FENCE:
+ return LowerATOMIC_FENCE(Op, DAG);
case ISD::GlobalAddress:
return lowerGlobalAddress(Op, DAG);
case ISD::BlockAddress:
(AddiPairImmSmall AddiPair:$rs2))>;
}
+let hasSideEffects = 1, isMeta = 1 in
+def PseudoMemBarrier : Pseudo<(outs), (ins), [(membarrier)]>;
+
//===----------------------------------------------------------------------===//
// Standard extensions
//===----------------------------------------------------------------------===//
define void @fence_singlethread_acquire() nounwind {
; CHECK-LABEL: fence_singlethread_acquire:
; CHECK: # %bb.0:
-; CHECK-NEXT: fence r, rw
+; CHECK-NEXT: #MEMBARRIER
; CHECK-NEXT: ret
fence syncscope("singlethread") acquire
ret void
define void @fence_singlethread_release() nounwind {
; CHECK-LABEL: fence_singlethread_release:
; CHECK: # %bb.0:
-; CHECK-NEXT: fence rw, w
+; CHECK-NEXT: #MEMBARRIER
; CHECK-NEXT: ret
fence syncscope("singlethread") release
ret void
define void @fence_singlethread_acq_rel() nounwind {
; CHECK-LABEL: fence_singlethread_acq_rel:
; CHECK: # %bb.0:
-; CHECK-NEXT: fence.tso
+; CHECK-NEXT: #MEMBARRIER
; CHECK-NEXT: ret
fence syncscope("singlethread") acq_rel
ret void
define void @fence_singlethread_seq_cst() nounwind {
; CHECK-LABEL: fence_singlethread_seq_cst:
; CHECK: # %bb.0:
-; CHECK-NEXT: fence rw, rw
+; CHECK-NEXT: #MEMBARRIER
; CHECK-NEXT: ret
fence syncscope("singlethread") seq_cst
ret void