LLVM 24.0.0git
AMDGPUBarrierLatency.cpp
Go to the documentation of this file.
1//===--- AMDGPUBarrierLatency.cpp - AMDGPU Barrier Latency ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file This file contains a DAG scheduling mutation to add latency to:
10/// 1. Barrier edges between ATOMIC_FENCE instructions and preceding
11/// memory accesses potentially affected by the fence.
12/// This encourages the scheduling of more instructions before
13/// ATOMIC_FENCE instructions. ATOMIC_FENCE instructions may
14/// introduce wait counting or indicate an impending S_BARRIER
15/// wait. Having more instructions in-flight across these
16/// constructs improves latency hiding.
17/// 2. Barrier edges from S_BARRIER_SIGNAL to S_BARRIER_WAIT.
18/// This encourages independent work to be scheduled between
19/// signal and wait, hiding barrier synchronization latency.
20//
21//===----------------------------------------------------------------------===//
22
24#include "GCNSubtarget.h"
25#include "SIInstrInfo.h"
28
29using namespace llvm;
30
31namespace {
32
33class BarrierLatency : public ScheduleDAGMutation {
34private:
35 SmallSet<SyncScope::ID, 4> IgnoredScopes;
36
37public:
38 BarrierLatency(MachineFunction *MF) {
39 LLVMContext &Context = MF->getFunction().getContext();
40 const Triple &TT = MF->getSubtarget<GCNSubtarget>().getTargetTriple();
41 auto ScopeID = [&](AtomicScope Scope, bool OneAS) {
42 return Context.getOrInsertSyncScopeID(
43 *getAtomicScopeIRString(TT, Scope, OneAS));
44 };
45 IgnoredScopes.insert(SyncScope::SingleThread);
46 IgnoredScopes.insert(ScopeID(AtomicScope::Wavefront, /*OneAS=*/false));
47 IgnoredScopes.insert(ScopeID(AtomicScope::Wavefront, /*OneAS=*/true));
48 IgnoredScopes.insert(ScopeID(AtomicScope::Single, /*OneAS=*/true));
49
50 const GCNSubtarget &ST = MF->getSubtarget<GCNSubtarget>();
51 bool TgSplit =
52 ST.hasTgSplitSupport() && AMDGPU::isTgSplitEnabled(MF->getFunction());
53 if (!ST.requiresWaitOnWorkgroupReleaseFence(TgSplit)) {
54 // Prior to GFX10 workgroup scope does not normally require waitcnts
55 IgnoredScopes.insert(ScopeID(AtomicScope::Workgroup, /*OneAS=*/false));
56 }
57 }
58 void apply(ScheduleDAGInstrs *DAG) override;
59};
60
61void addLatencyToEdge(SDep &PredDep, SUnit &SU, unsigned Latency) {
62 SUnit *PredSU = PredDep.getSUnit();
63 SDep ForwardD = PredDep;
64 ForwardD.setSUnit(&SU);
65 for (SDep &SuccDep : PredSU->Succs) {
66 if (SuccDep == ForwardD) {
67 SuccDep.setLatency(SuccDep.getLatency() + Latency);
68 break;
69 }
70 }
71 PredDep.setLatency(PredDep.getLatency() + Latency);
72 PredSU->setDepthDirty();
73 SU.setDepthDirty();
74}
75
76void setLatencyForEdge(SDep &PredDep, SUnit &SU, unsigned Latency) {
77 SUnit *PredSU = PredDep.getSUnit();
78 SDep ForwardD = PredDep;
79 ForwardD.setSUnit(&SU);
80 for (SDep &SuccDep : PredSU->Succs) {
81 if (SuccDep == ForwardD) {
82 SuccDep.setLatency(Latency);
83 break;
84 }
85 }
86 PredDep.setLatency(Latency);
87 PredSU->setDepthDirty();
88 SU.setDepthDirty();
89}
90
91void BarrierLatency::apply(ScheduleDAGInstrs *DAG) {
92 const SIInstrInfo *TII = static_cast<const SIInstrInfo *>(DAG->TII);
93 constexpr unsigned FenceLatency = 2000;
94 const unsigned BarrierSignalWaitLatency = DAG->MF.getSubtarget<GCNSubtarget>()
95 .getCLOpts()
96 .barrier_signal_wait_latency;
98 SmallVector<SUnit *, 8> RegionAsync;
99 const TargetSchedModel *SchedModel = DAG->getSchedModel();
100
101 for (SUnit &SU : DAG->SUnits) {
102 const MachineInstr *MI = SU.getInstr();
103 unsigned Op = MI->getOpcode();
104
105 if (Op == AMDGPU::ATOMIC_FENCE) {
106 // Update latency on barrier edges of ATOMIC_FENCE.
107 // Ignore scopes not expected to have any latency.
108 SyncScope::ID SSID =
109 static_cast<SyncScope::ID>(MI->getOperand(1).getImm());
110 if (IgnoredScopes.contains(SSID))
111 continue;
112
113 for (SDep &PredDep : SU.Preds) {
114 if (!PredDep.isBarrier())
115 continue;
116 SUnit *PredSU = PredDep.getSUnit();
117 MachineInstr *MI = PredSU->getInstr();
118 // Only consider memory loads
119 if (!MI->mayLoad() || MI->mayStore())
120 continue;
121
122 addLatencyToEdge(PredDep, SU,
123 SchedModel ? SchedModel->computeInstrLatency(MI, false)
124 : FenceLatency);
125 }
126 } else if (Op == AMDGPU::S_BARRIER_WAIT) {
127 for (SDep &PredDep : SU.Preds) {
128 SUnit *PredSU = PredDep.getSUnit();
129 const MachineInstr *PredMI = PredSU->getInstr();
130 if (TII->isBarrierStart(PredMI->getOpcode())) {
131 addLatencyToEdge(PredDep, SU, BarrierSignalWaitLatency);
132 }
133 }
134 } else if (TII->isLDSDMA(*MI)) {
136 RegionTDM.push_back(&SU);
138 RegionAsync.push_back(&SU);
139 } else if (Op == AMDGPU::S_WAIT_TENSORCNT ||
140 Op == AMDGPU::S_WAIT_ASYNCCNT) {
141 auto needWaitFor = [&](SmallVectorImpl<SUnit *> &RegionLDSDMA, SUnit *SU,
142 int64_t Count) {
143 if (RegionLDSDMA.size() <= static_cast<uint64_t>(Count)) {
144 return false;
145 }
146
147 int64_t Counter = 0;
148 auto I = RegionLDSDMA.rbegin(), E = RegionLDSDMA.rend();
149 for (; I != E; I++) {
150 if (Counter >= Count)
151 return true;
152
153 if (SU->NodeNum == (*I)->NodeNum)
154 return false;
155
156 ++Counter;
157 }
158 llvm_unreachable("Malformed RegionLDSDMA");
159 };
160
161 int64_t WaitVal = MI->getOperand(0).getImm();
162 for (SDep &PredDep : SU.Preds) {
163 if (PredDep.getKind() != SDep::Kind::Data)
164 continue;
165
166 Register DepReg = PredDep.getReg();
167 bool IsAsync = Op == AMDGPU::S_WAIT_ASYNCCNT;
168 Register LDSDMACnt = IsAsync ? AMDGPU::ASYNCcnt : AMDGPU::TENSORcnt;
169
170 if (DepReg != LDSDMACnt)
171 continue;
172
173 SUnit *PredSU = PredDep.getSUnit();
174
175 // The data dep can be carried by a non-LDSDMA SU
176 // (e.g. an intervening COPY or pseudo). Such predecessors are not
177 // tracked, so needWaitFor cannot reason about them.
178 const MachineInstr &PredMI = *PredSU->getInstr();
179 if (IsAsync ? !SIInstrFlags::usesASYNC_CNT(PredMI)
181 continue;
182
183 if (!needWaitFor(Op == AMDGPU::S_WAIT_ASYNCCNT ? RegionAsync
184 : RegionTDM,
185 PredSU, WaitVal)) {
186 setLatencyForEdge(PredDep, SU, 1);
187 }
188 }
189 }
190 }
191}
192
193} // end namespace
194
195std::unique_ptr<ScheduleDAGMutation>
197 return std::make_unique<BarrierLatency>(MF);
198}
uint64_t uint64_t
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
AMD GCN specific subclass of TargetSubtarget.
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
Promote Memory to Register
Definition Mem2Reg.cpp:110
Interface definition for SIInstrInfo.
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:352
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Function & getFunction()
Return the LLVM function that this machine code represents.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
Scheduling dependency.
Definition ScheduleDAG.h:54
SUnit * getSUnit() const
Kind getKind() const
Returns an enum value representing the kind of the dependence.
void setLatency(unsigned Lat)
Sets the latency for this edge.
unsigned getLatency() const
Returns the latency value for this edge, which roughly means the minimum number of cycles that must e...
void setSUnit(SUnit *SU)
Register getReg() const
Returns the register associated with this edge.
bool isBarrier() const
Tests if this is an Order dependence that is marked as a barrier.
Scheduling unit. This is a node in the scheduling DAG.
SmallVector< SDep, 4 > Succs
All sunit successors.
LLVM_ABI void setDepthDirty()
Sets a flag in this node to indicate that its stored Depth value will require recomputation the next ...
SmallVector< SDep, 4 > Preds
All sunit predecessors.
MachineInstr * getInstr() const
Returns the representative MachineInstr for this SUnit.
A ScheduleDAG for scheduling lists of MachineInstr.
const TargetSchedModel * getSchedModel() const
Gets the machine model for instruction scheduling.
Mutate the DAG as a postpass after normal DAG building.
const TargetInstrInfo * TII
Target instruction information.
std::vector< SUnit > SUnits
The scheduling units.
MachineFunction & MF
Machine function.
bool contains(const T &V) const
Check if the SmallSet contains the given element.
Definition SmallSet.h:229
void push_back(const T &Elt)
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
const AArch64MCOptions & getCLOpts(const MCTargetOptions &Options)
bool isTgSplitEnabled(const Function &F)
constexpr bool usesTENSOR_CNT(const T &...O)
Definition SIDefines.h:307
constexpr bool usesASYNC_CNT(const T &...O)
Definition SIDefines.h:316
@ SingleThread
Synchronized with respect to signal handlers executing in the same thread.
Definition LLVMContext.h:63
void apply(Opt *O, const Mod &M, const Mods &... Ms)
This is an optimization pass for GlobalISel generic memory operations.
std::unique_ptr< ScheduleDAGMutation > createAMDGPUBarrierLatencyDAGMutation(MachineFunction *MF)
AtomicScope
Target-neutral memory synchronization scopes.
Definition AtomicScope.h:23
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
DWARFExpression::Operation Op
std::optional< StringRef > getAtomicScopeIRString(const Triple &T, AtomicScope S, bool IsSingleAddressSpace=false)
Returns the LLVM IR syncscope string that T uses to spell S.
Definition AtomicScope.h:34