-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RISCV][GISel] Select G_FENCE. #73184
Conversation
@llvm/pr-subscribers-backend-risc-v Author: Craig Topper (topperc) ChangesUsing IR test to make it easier to compare with the SelectionDAG test output. The constant operands otherwise make it harder to understand. Full diff: https://github.com/llvm/llvm-project/pull/73184.diff 2 Files Affected:
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 3c72269d1e00c2f..0ce64e1167ab0af 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -72,6 +72,8 @@ class RISCVInstructionSelector : public InstructionSelector {
MachineRegisterInfo &MRI) const;
bool selectFPCompare(MachineInstr &MI, MachineIRBuilder &MIB,
MachineRegisterInfo &MRI) const;
+ bool selectFence(MachineInstr &MI, MachineIRBuilder &MIB,
+ MachineRegisterInfo &MRI) const;
ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
@@ -564,6 +566,8 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
return selectSelect(MI, MIB, MRI);
case TargetOpcode::G_FCMP:
return selectFPCompare(MI, MIB, MRI);
+ case TargetOpcode::G_FENCE:
+ return selectFence(MI, MIB, MRI);
default:
return false;
}
@@ -1099,6 +1103,59 @@ bool RISCVInstructionSelector::selectFPCompare(MachineInstr &MI,
return true;
}
+bool RISCVInstructionSelector::selectFence(MachineInstr &MI,
+ MachineIRBuilder &MIB,
+ MachineRegisterInfo &MRI) const {
+ AtomicOrdering FenceOrdering =
+ static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+ SyncScope::ID FenceSSID =
+ static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+
+ 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);
+ } else {
+ // MEMBARRIER is a compiler barrier; it codegens to a no-op.
+ MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+ }
+ } else if (FenceSSID == SyncScope::SingleThread) {
+ // singlethread fences only synchronize with signal handlers on the same
+ // thread and thus only need to preserve instruction order, not actually
+ // enforce memory ordering.
+ MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+ } else if (FenceOrdering == AtomicOrdering::AcquireRelease) {
+ MIB.buildInstr(RISCV::FENCE_TSO, {}, {});
+ } else {
+ unsigned Pred, Succ;
+ switch (FenceOrdering) {
+ default:
+ llvm_unreachable("Unexpected ordering");
+ case AtomicOrdering::Acquire:
+ Pred = RISCVFenceField::R;
+ Succ = RISCVFenceField::R | RISCVFenceField::W;
+ break;
+ case AtomicOrdering::Release:
+ Pred = RISCVFenceField::R | RISCVFenceField::W;
+ Succ = RISCVFenceField::W;
+ break;
+ case AtomicOrdering::SequentiallyConsistent:
+ Pred = RISCVFenceField::R | RISCVFenceField::W;
+ Succ = RISCVFenceField::R | RISCVFenceField::W;
+ break;
+ }
+ MIB.buildInstr(RISCV::FENCE, {}, {}).addImm(Pred).addImm(Succ);
+ }
+
+ MI.eraseFromParent();
+ return true;
+}
+
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 000000000000000..f41a89fc4594088
--- /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
+}
|
@llvm/pr-subscribers-llvm-globalisel Author: Craig Topper (topperc) ChangesUsing IR test to make it easier to compare with the SelectionDAG test output. The constant operands otherwise make it harder to understand. Full diff: https://github.com/llvm/llvm-project/pull/73184.diff 2 Files Affected:
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 3c72269d1e00c2f..0ce64e1167ab0af 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -72,6 +72,8 @@ class RISCVInstructionSelector : public InstructionSelector {
MachineRegisterInfo &MRI) const;
bool selectFPCompare(MachineInstr &MI, MachineIRBuilder &MIB,
MachineRegisterInfo &MRI) const;
+ bool selectFence(MachineInstr &MI, MachineIRBuilder &MIB,
+ MachineRegisterInfo &MRI) const;
ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
@@ -564,6 +566,8 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
return selectSelect(MI, MIB, MRI);
case TargetOpcode::G_FCMP:
return selectFPCompare(MI, MIB, MRI);
+ case TargetOpcode::G_FENCE:
+ return selectFence(MI, MIB, MRI);
default:
return false;
}
@@ -1099,6 +1103,59 @@ bool RISCVInstructionSelector::selectFPCompare(MachineInstr &MI,
return true;
}
+bool RISCVInstructionSelector::selectFence(MachineInstr &MI,
+ MachineIRBuilder &MIB,
+ MachineRegisterInfo &MRI) const {
+ AtomicOrdering FenceOrdering =
+ static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+ SyncScope::ID FenceSSID =
+ static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+
+ 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);
+ } else {
+ // MEMBARRIER is a compiler barrier; it codegens to a no-op.
+ MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+ }
+ } else if (FenceSSID == SyncScope::SingleThread) {
+ // singlethread fences only synchronize with signal handlers on the same
+ // thread and thus only need to preserve instruction order, not actually
+ // enforce memory ordering.
+ MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
+ } else if (FenceOrdering == AtomicOrdering::AcquireRelease) {
+ MIB.buildInstr(RISCV::FENCE_TSO, {}, {});
+ } else {
+ unsigned Pred, Succ;
+ switch (FenceOrdering) {
+ default:
+ llvm_unreachable("Unexpected ordering");
+ case AtomicOrdering::Acquire:
+ Pred = RISCVFenceField::R;
+ Succ = RISCVFenceField::R | RISCVFenceField::W;
+ break;
+ case AtomicOrdering::Release:
+ Pred = RISCVFenceField::R | RISCVFenceField::W;
+ Succ = RISCVFenceField::W;
+ break;
+ case AtomicOrdering::SequentiallyConsistent:
+ Pred = RISCVFenceField::R | RISCVFenceField::W;
+ Succ = RISCVFenceField::R | RISCVFenceField::W;
+ break;
+ }
+ MIB.buildInstr(RISCV::FENCE, {}, {}).addImm(Pred).addImm(Succ);
+ }
+
+ MI.eraseFromParent();
+ return true;
+}
+
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 000000000000000..f41a89fc4594088
--- /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
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind adding some documentation for the G_FENCE opcode in another patch? Looks to be missing.
return; | ||
} | ||
|
||
// Refer to Table A.6 in the version 2.3 draft of the RISC-V Instruction Set |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is 2.3 version a thing? I can only find version 20191213 as the only versioned pdf after 2.2. My google search was riscv instruction set volume i "this is version 2.3"
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe not. I copied this comment from RISCVInstrInfo.td which is where I translated this from.
Using IR test to make it easier to compare with the SelectionDAG test output. The constants operands otherwise make it harder to understand.
5ff6382
to
9167364
Compare
Using IR test to make it easier to compare with the SelectionDAG test output. The constant operands otherwise make it harder to understand.