diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index d37026f0011799..9b4577dec87c57 100644 --- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -73,6 +73,8 @@ class RISCVInstructionSelector : public InstructionSelector { MachineRegisterInfo &MRI) const; bool selectIntrinsicWithSideEffects(MachineInstr &MI, MachineIRBuilder &MIB, MachineRegisterInfo &MRI) const; + void emitFence(AtomicOrdering FenceOrdering, SyncScope::ID FenceSSID, + MachineIRBuilder &MIB) const; ComplexRendererFns selectShiftMask(MachineOperand &Root) const; ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const; @@ -612,6 +614,15 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) { return selectFPCompare(MI, MIB, MRI); case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: return selectIntrinsicWithSideEffects(MI, MIB, MRI); + case TargetOpcode::G_FENCE: { + AtomicOrdering FenceOrdering = + static_cast(MI.getOperand(0).getImm()); + SyncScope::ID FenceSSID = + static_cast(MI.getOperand(1).getImm()); + emitFence(FenceOrdering, FenceSSID, MIB); + MI.eraseFromParent(); + return true; + } default: return false; } @@ -1087,6 +1098,63 @@ bool RISCVInstructionSelector::selectIntrinsicWithSideEffects( return true; } +void RISCVInstructionSelector::emitFence(AtomicOrdering FenceOrdering, + SyncScope::ID FenceSSID, + MachineIRBuilder &MIB) const { + if (STI.hasStdExtZtso()) { + // The only fence that needs an instruction is a sequentially-consistent + // cross-thread fence. + if (FenceOrdering == AtomicOrdering::SequentiallyConsistent && + FenceSSID == SyncScope::System) { + // fence rw, rw + MIB.buildInstr(RISCV::FENCE, {}, {}) + .addImm(RISCVFenceField::R | RISCVFenceField::W) + .addImm(RISCVFenceField::R | RISCVFenceField::W); + return; + } + + // MEMBARRIER is a compiler barrier; it codegens to a no-op. + MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {}); + return; + } + + // 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) { + MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {}); + return; + } + + // Refer to Table A.6 in the version 2.3 draft of the RISC-V Instruction Set + // Manual: Volume I. + unsigned Pred, Succ; + switch (FenceOrdering) { + default: + llvm_unreachable("Unexpected ordering"); + case AtomicOrdering::AcquireRelease: + // fence acq_rel -> fence.tso + MIB.buildInstr(RISCV::FENCE_TSO, {}, {}); + return; + case AtomicOrdering::Acquire: + // fence acquire -> fence r, rw + Pred = RISCVFenceField::R; + Succ = RISCVFenceField::R | RISCVFenceField::W; + break; + case AtomicOrdering::Release: + // fence release -> fence rw, w + Pred = RISCVFenceField::R | RISCVFenceField::W; + Succ = RISCVFenceField::W; + break; + case AtomicOrdering::SequentiallyConsistent: + // fence seq_cst -> fence rw, rw + Pred = RISCVFenceField::R | RISCVFenceField::W; + Succ = RISCVFenceField::R | RISCVFenceField::W; + break; + } + MIB.buildInstr(RISCV::FENCE, {}, {}).addImm(Pred).addImm(Succ); +} + namespace llvm { InstructionSelector * createRISCVInstructionSelector(const RISCVTargetMachine &TM, diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll new file mode 100644 index 00000000000000..f41a89fc459408 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-fence.ll @@ -0,0 +1,100 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=riscv32 -global-isel -verify-machineinstrs < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,WMO %s +; RUN: llc -mtriple=riscv32 -mattr=+a -global-isel -verify-machineinstrs < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,WMO %s +; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,TSO %s +; RUN: llc -mtriple=riscv64 -global-isel -verify-machineinstrs < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,WMO %s +; RUN: llc -mtriple=riscv64 -mattr=+a -global-isel -verify-machineinstrs < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,WMO %s +; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \ +; RUN: | FileCheck --check-prefixes=CHECK,TSO %s + +define void @fence_acquire() nounwind { +; WMO-LABEL: fence_acquire: +; WMO: # %bb.0: +; WMO-NEXT: fence r, rw +; WMO-NEXT: ret +; +; TSO-LABEL: fence_acquire: +; TSO: # %bb.0: +; TSO-NEXT: #MEMBARRIER +; TSO-NEXT: ret + fence acquire + ret void +} + +define void @fence_release() nounwind { +; WMO-LABEL: fence_release: +; WMO: # %bb.0: +; WMO-NEXT: fence rw, w +; WMO-NEXT: ret +; +; TSO-LABEL: fence_release: +; TSO: # %bb.0: +; TSO-NEXT: #MEMBARRIER +; TSO-NEXT: ret + fence release + ret void +} + +define void @fence_acq_rel() nounwind { +; WMO-LABEL: fence_acq_rel: +; WMO: # %bb.0: +; WMO-NEXT: fence.tso +; WMO-NEXT: ret +; +; TSO-LABEL: fence_acq_rel: +; TSO: # %bb.0: +; TSO-NEXT: #MEMBARRIER +; TSO-NEXT: ret + fence acq_rel + ret void +} + +define void @fence_seq_cst() nounwind { +; CHECK-LABEL: fence_seq_cst: +; CHECK: # %bb.0: +; CHECK-NEXT: fence rw, rw +; CHECK-NEXT: ret + fence seq_cst + ret void +} + +define void @fence_singlethread_acquire() nounwind { +; CHECK-LABEL: fence_singlethread_acquire: +; CHECK: # %bb.0: +; 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: #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: #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: #MEMBARRIER +; CHECK-NEXT: ret + fence syncscope("singlethread") seq_cst + ret void +}