[RISCV] Avoid emitting hardware fences for singlethread fences
authorPhilip Reames <preames@rivosinc.com>
Tue, 10 Jan 2023 17:51:56 +0000 (09:51 -0800)
committerPhilip Reames <listmail@philipreames.com>
Tue, 10 Jan 2023 18:09:59 +0000 (10:09 -0800)
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

llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
llvm/lib/Target/RISCV/RISCVISelLowering.cpp
llvm/lib/Target/RISCV/RISCVInstrInfo.td
llvm/test/CodeGen/RISCV/atomic-fence.ll

index 5c36a91..3bafbcc 100644 (file)
@@ -116,6 +116,9 @@ void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) {
   case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
     LowerHWASAN_CHECK_MEMACCESS(*MI);
     return;
+  case RISCV::PseudoMemBarrier:
+    OutStreamer->emitRawComment("MEMBARRIER");
+    return;
   }
 
   MCInst TmpInst;
index 74dc737..8c1c8b5 100644 (file)
@@ -461,6 +461,8 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
     setMaxAtomicSizeInBitsSupported(0);
   }
 
+  setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom);
+
   setBooleanContents(ZeroOrOneBooleanContent);
 
   if (Subtarget.hasVInstructions()) {
@@ -3667,11 +3669,28 @@ static SDValue lowerConstant(SDValue Op, SelectionDAG &DAG,
   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:
index c699a94..e46982e 100644 (file)
@@ -1843,6 +1843,9 @@ def : Pat<(binop_allwusers<add> GPR:$rs1, (AddiPair:$rs2)),
                  (AddiPairImmSmall AddiPair:$rs2))>;
 }
 
+let hasSideEffects = 1, isMeta = 1 in
+def PseudoMemBarrier : Pseudo<(outs), (ins), [(membarrier)]>;
+
 //===----------------------------------------------------------------------===//
 // Standard extensions
 //===----------------------------------------------------------------------===//
index ff91652..a8d49ce 100644 (file)
@@ -51,7 +51,7 @@ define void @fence_seq_cst() nounwind {
 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
@@ -60,7 +60,7 @@ define void @fence_singlethread_acquire() nounwind {
 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
@@ -69,7 +69,7 @@ define void @fence_singlethread_release() nounwind {
 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
@@ -78,7 +78,7 @@ define void @fence_singlethread_acq_rel() nounwind {
 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