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
23#include "AMDGPUBarrierLatency.h"
24#include "GCNSubtarget.h"
25#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
26#include "SIInstrInfo.h"
27#include "llvm/CodeGen/ScheduleDAGInstrs.h"
28#include "llvm/Support/CommandLine.h"
29
30using namespace llvm;
31
32static cl::opt<unsigned> BarrierSignalWaitLatencyOpt(
33 "amdgpu-barrier-signal-wait-latency",
34 cl::desc("Synthetic latency between S_BARRIER_SIGNAL and S_BARRIER_WAIT "
35 "to encourage scheduling independent work between them"),
36 cl::init(Val: 16), cl::Hidden);
37
38namespace {
39
40class BarrierLatency : public ScheduleDAGMutation {
41private:
42 SmallSet<SyncScope::ID, 4> IgnoredScopes;
43
44public:
45 BarrierLatency(MachineFunction *MF) {
46 LLVMContext &Context = MF->getFunction().getContext();
47 IgnoredScopes.insert(V: SyncScope::SingleThread);
48 IgnoredScopes.insert(V: Context.getOrInsertSyncScopeID(SSN: "wavefront"));
49 IgnoredScopes.insert(V: Context.getOrInsertSyncScopeID(SSN: "wavefront-one-as"));
50 IgnoredScopes.insert(V: Context.getOrInsertSyncScopeID(SSN: "singlethread-one-as"));
51
52 const GCNSubtarget &ST = MF->getSubtarget<GCNSubtarget>();
53 bool TgSplit =
54 ST.hasTgSplitSupport() && AMDGPU::isTgSplitEnabled(F: MF->getFunction());
55 if (!ST.requiresWaitOnWorkgroupReleaseFence(TgSplit)) {
56 // Prior to GFX10 workgroup scope does not normally require waitcnts
57 IgnoredScopes.insert(V: Context.getOrInsertSyncScopeID(SSN: "workgroup"));
58 }
59 }
60 void apply(ScheduleDAGInstrs *DAG) override;
61};
62
63void addLatencyToEdge(SDep &PredDep, SUnit &SU, unsigned Latency) {
64 SUnit *PredSU = PredDep.getSUnit();
65 SDep ForwardD = PredDep;
66 ForwardD.setSUnit(&SU);
67 for (SDep &SuccDep : PredSU->Succs) {
68 if (SuccDep == ForwardD) {
69 SuccDep.setLatency(SuccDep.getLatency() + Latency);
70 break;
71 }
72 }
73 PredDep.setLatency(PredDep.getLatency() + Latency);
74 PredSU->setDepthDirty();
75 SU.setDepthDirty();
76}
77
78void setLatencyForEdge(SDep &PredDep, SUnit &SU, unsigned Latency) {
79 SUnit *PredSU = PredDep.getSUnit();
80 SDep ForwardD = PredDep;
81 ForwardD.setSUnit(&SU);
82 for (SDep &SuccDep : PredSU->Succs) {
83 if (SuccDep == ForwardD) {
84 SuccDep.setLatency(Latency);
85 break;
86 }
87 }
88 PredDep.setLatency(Latency);
89 PredSU->setDepthDirty();
90 SU.setDepthDirty();
91}
92
93void BarrierLatency::apply(ScheduleDAGInstrs *DAG) {
94 const SIInstrInfo *TII = static_cast<const SIInstrInfo *>(DAG->TII);
95 constexpr unsigned FenceLatency = 2000;
96 const unsigned BarrierSignalWaitLatency = BarrierSignalWaitLatencyOpt;
97 SmallVector<SUnit *, 8> RegionTDM;
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(i: 1).getImm());
110 if (IgnoredScopes.contains(V: 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 Latency: SchedModel ? SchedModel->computeInstrLatency(MI, UseDefaultDefLatency: 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(Opcode: PredMI->getOpcode())) {
131 addLatencyToEdge(PredDep, SU, Latency: BarrierSignalWaitLatency);
132 }
133 }
134 } else if (TII->isLDSDMA(MI: *MI)) {
135 if (MI->getDesc().TSFlags & SIInstrFlags::TENSOR_CNT)
136 RegionTDM.push_back(Elt: &SU);
137 else if (MI->getDesc().TSFlags & SIInstrFlags::ASYNC_CNT)
138 RegionAsync.push_back(Elt: &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(i: 0).getImm();
162 for (SDep &PredDep : SU.Preds) {
163 if (PredDep.getKind() != SDep::Kind::Data)
164 continue;
165
166 Register DepReg = PredDep.getReg();
167 Register LDSDMACnt = AMDGPU::TENSORcnt;
168 uint64_t LDSDMAFlags = SIInstrFlags::TENSOR_CNT;
169 if (Op == AMDGPU::S_WAIT_ASYNCCNT) {
170 LDSDMACnt = AMDGPU::ASYNCcnt;
171 LDSDMAFlags = SIInstrFlags::ASYNC_CNT;
172 }
173
174 if (DepReg != LDSDMACnt)
175 continue;
176
177 SUnit *PredSU = PredDep.getSUnit();
178
179 // The data dep can be carried by a non-LDSDMA SU
180 // (e.g. an intervening COPY or pseudo). Such predecessors are not
181 // tracked, so needWaitFor cannot reason about them.
182 if (!(PredSU->getInstr()->getDesc().TSFlags & LDSDMAFlags))
183 continue;
184
185 if (!needWaitFor(Op == AMDGPU::S_WAIT_ASYNCCNT ? RegionAsync
186 : RegionTDM,
187 PredSU, WaitVal)) {
188 setLatencyForEdge(PredDep, SU, Latency: 1);
189 }
190 }
191 }
192 }
193}
194
195} // end namespace
196
197std::unique_ptr<ScheduleDAGMutation>
198llvm::createAMDGPUBarrierLatencyDAGMutation(MachineFunction *MF) {
199 return std::make_unique<BarrierLatency>(args&: MF);
200}
201