Skip to content

Commit ffcc5c7

Browse files
authored
[RISCV][GISel] Select G_FENCE. (#73184)
Using IR test to make it easier to compare with the SelectionDAG test output. The constant operands otherwise make it harder to understand.
1 parent a3b7b2d commit ffcc5c7

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class RISCVInstructionSelector : public InstructionSelector {
7373
MachineRegisterInfo &MRI) const;
7474
bool selectIntrinsicWithSideEffects(MachineInstr &MI, MachineIRBuilder &MIB,
7575
MachineRegisterInfo &MRI) const;
76+
void emitFence(AtomicOrdering FenceOrdering, SyncScope::ID FenceSSID,
77+
MachineIRBuilder &MIB) const;
7678

7779
ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
7880
ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
@@ -612,6 +614,15 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
612614
return selectFPCompare(MI, MIB, MRI);
613615
case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
614616
return selectIntrinsicWithSideEffects(MI, MIB, MRI);
617+
case TargetOpcode::G_FENCE: {
618+
AtomicOrdering FenceOrdering =
619+
static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
620+
SyncScope::ID FenceSSID =
621+
static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
622+
emitFence(FenceOrdering, FenceSSID, MIB);
623+
MI.eraseFromParent();
624+
return true;
625+
}
615626
default:
616627
return false;
617628
}
@@ -1087,6 +1098,63 @@ bool RISCVInstructionSelector::selectIntrinsicWithSideEffects(
10871098
return true;
10881099
}
10891100

1101+
void RISCVInstructionSelector::emitFence(AtomicOrdering FenceOrdering,
1102+
SyncScope::ID FenceSSID,
1103+
MachineIRBuilder &MIB) const {
1104+
if (STI.hasStdExtZtso()) {
1105+
// The only fence that needs an instruction is a sequentially-consistent
1106+
// cross-thread fence.
1107+
if (FenceOrdering == AtomicOrdering::SequentiallyConsistent &&
1108+
FenceSSID == SyncScope::System) {
1109+
// fence rw, rw
1110+
MIB.buildInstr(RISCV::FENCE, {}, {})
1111+
.addImm(RISCVFenceField::R | RISCVFenceField::W)
1112+
.addImm(RISCVFenceField::R | RISCVFenceField::W);
1113+
return;
1114+
}
1115+
1116+
// MEMBARRIER is a compiler barrier; it codegens to a no-op.
1117+
MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
1118+
return;
1119+
}
1120+
1121+
// singlethread fences only synchronize with signal handlers on the same
1122+
// thread and thus only need to preserve instruction order, not actually
1123+
// enforce memory ordering.
1124+
if (FenceSSID == SyncScope::SingleThread) {
1125+
MIB.buildInstr(TargetOpcode::MEMBARRIER, {}, {});
1126+
return;
1127+
}
1128+
1129+
// Refer to Table A.6 in the version 2.3 draft of the RISC-V Instruction Set
1130+
// Manual: Volume I.
1131+
unsigned Pred, Succ;
1132+
switch (FenceOrdering) {
1133+
default:
1134+
llvm_unreachable("Unexpected ordering");
1135+
case AtomicOrdering::AcquireRelease:
1136+
// fence acq_rel -> fence.tso
1137+
MIB.buildInstr(RISCV::FENCE_TSO, {}, {});
1138+
return;
1139+
case AtomicOrdering::Acquire:
1140+
// fence acquire -> fence r, rw
1141+
Pred = RISCVFenceField::R;
1142+
Succ = RISCVFenceField::R | RISCVFenceField::W;
1143+
break;
1144+
case AtomicOrdering::Release:
1145+
// fence release -> fence rw, w
1146+
Pred = RISCVFenceField::R | RISCVFenceField::W;
1147+
Succ = RISCVFenceField::W;
1148+
break;
1149+
case AtomicOrdering::SequentiallyConsistent:
1150+
// fence seq_cst -> fence rw, rw
1151+
Pred = RISCVFenceField::R | RISCVFenceField::W;
1152+
Succ = RISCVFenceField::R | RISCVFenceField::W;
1153+
break;
1154+
}
1155+
MIB.buildInstr(RISCV::FENCE, {}, {}).addImm(Pred).addImm(Succ);
1156+
}
1157+
10901158
namespace llvm {
10911159
InstructionSelector *
10921160
createRISCVInstructionSelector(const RISCVTargetMachine &TM,
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv32 -global-isel -verify-machineinstrs < %s \
3+
; RUN: | FileCheck --check-prefixes=CHECK,WMO %s
4+
; RUN: llc -mtriple=riscv32 -mattr=+a -global-isel -verify-machineinstrs < %s \
5+
; RUN: | FileCheck --check-prefixes=CHECK,WMO %s
6+
; RUN: llc -mtriple=riscv32 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \
7+
; RUN: | FileCheck --check-prefixes=CHECK,TSO %s
8+
; RUN: llc -mtriple=riscv64 -global-isel -verify-machineinstrs < %s \
9+
; RUN: | FileCheck --check-prefixes=CHECK,WMO %s
10+
; RUN: llc -mtriple=riscv64 -mattr=+a -global-isel -verify-machineinstrs < %s \
11+
; RUN: | FileCheck --check-prefixes=CHECK,WMO %s
12+
; RUN: llc -mtriple=riscv64 -mattr=+a,+experimental-ztso -global-isel -verify-machineinstrs < %s \
13+
; RUN: | FileCheck --check-prefixes=CHECK,TSO %s
14+
15+
define void @fence_acquire() nounwind {
16+
; WMO-LABEL: fence_acquire:
17+
; WMO: # %bb.0:
18+
; WMO-NEXT: fence r, rw
19+
; WMO-NEXT: ret
20+
;
21+
; TSO-LABEL: fence_acquire:
22+
; TSO: # %bb.0:
23+
; TSO-NEXT: #MEMBARRIER
24+
; TSO-NEXT: ret
25+
fence acquire
26+
ret void
27+
}
28+
29+
define void @fence_release() nounwind {
30+
; WMO-LABEL: fence_release:
31+
; WMO: # %bb.0:
32+
; WMO-NEXT: fence rw, w
33+
; WMO-NEXT: ret
34+
;
35+
; TSO-LABEL: fence_release:
36+
; TSO: # %bb.0:
37+
; TSO-NEXT: #MEMBARRIER
38+
; TSO-NEXT: ret
39+
fence release
40+
ret void
41+
}
42+
43+
define void @fence_acq_rel() nounwind {
44+
; WMO-LABEL: fence_acq_rel:
45+
; WMO: # %bb.0:
46+
; WMO-NEXT: fence.tso
47+
; WMO-NEXT: ret
48+
;
49+
; TSO-LABEL: fence_acq_rel:
50+
; TSO: # %bb.0:
51+
; TSO-NEXT: #MEMBARRIER
52+
; TSO-NEXT: ret
53+
fence acq_rel
54+
ret void
55+
}
56+
57+
define void @fence_seq_cst() nounwind {
58+
; CHECK-LABEL: fence_seq_cst:
59+
; CHECK: # %bb.0:
60+
; CHECK-NEXT: fence rw, rw
61+
; CHECK-NEXT: ret
62+
fence seq_cst
63+
ret void
64+
}
65+
66+
define void @fence_singlethread_acquire() nounwind {
67+
; CHECK-LABEL: fence_singlethread_acquire:
68+
; CHECK: # %bb.0:
69+
; CHECK-NEXT: #MEMBARRIER
70+
; CHECK-NEXT: ret
71+
fence syncscope("singlethread") acquire
72+
ret void
73+
}
74+
75+
define void @fence_singlethread_release() nounwind {
76+
; CHECK-LABEL: fence_singlethread_release:
77+
; CHECK: # %bb.0:
78+
; CHECK-NEXT: #MEMBARRIER
79+
; CHECK-NEXT: ret
80+
fence syncscope("singlethread") release
81+
ret void
82+
}
83+
84+
define void @fence_singlethread_acq_rel() nounwind {
85+
; CHECK-LABEL: fence_singlethread_acq_rel:
86+
; CHECK: # %bb.0:
87+
; CHECK-NEXT: #MEMBARRIER
88+
; CHECK-NEXT: ret
89+
fence syncscope("singlethread") acq_rel
90+
ret void
91+
}
92+
93+
define void @fence_singlethread_seq_cst() nounwind {
94+
; CHECK-LABEL: fence_singlethread_seq_cst:
95+
; CHECK: # %bb.0:
96+
; CHECK-NEXT: #MEMBARRIER
97+
; CHECK-NEXT: ret
98+
fence syncscope("singlethread") seq_cst
99+
ret void
100+
}

0 commit comments

Comments
 (0)