LLVM 24.0.0git
AMDGPUGlobalISelDivergenceLowering.cpp
Go to the documentation of this file.
1//===-- AMDGPUGlobalISelDivergenceLowering.cpp ----------------------------===//
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
10/// GlobalISel pass that selects divergent i1 phis as lane mask phis.
11/// Lane mask merging uses same algorithm as SDAG in SILowerI1Copies.
12/// Handles all cases of temporal divergence.
13/// For divergent non-phi i1 and uniform i1 uses outside of the cycle this pass
14/// currently depends on LCSSA to insert phis with one incoming.
15//
16//===----------------------------------------------------------------------===//
17
18#include "AMDGPU.h"
20#include "SILowerI1Copies.h"
27
28#define DEBUG_TYPE "amdgpu-global-isel-divergence-lowering"
29
30using namespace llvm;
31
32namespace {
33
34class AMDGPUGlobalISelDivergenceLoweringLegacy : public MachineFunctionPass {
35public:
36 static char ID;
37
38public:
39 AMDGPUGlobalISelDivergenceLoweringLegacy() : MachineFunctionPass(ID) {}
40
41 bool runOnMachineFunction(MachineFunction &MF) override;
42
43 StringRef getPassName() const override {
44 return "AMDGPU GlobalISel divergence lowering";
45 }
46
47 void getAnalysisUsage(AnalysisUsage &AU) const override {
48 AU.setPreservesCFG();
53 }
54};
55
56class DivergenceLoweringHelper : public AMDGPU::PhiLoweringHelper {
57public:
58 DivergenceLoweringHelper(MachineFunction &MF, MachineDominatorTree &DT,
61
62private:
63 MachineUniformityInfo *MUI = nullptr;
65 Register buildRegCopyToLaneMask(Register Reg);
66
67public:
68 void markAsLaneMask(Register DstReg) const override;
69 void getCandidatesForLowering(
70 SmallVectorImpl<MachineInstr *> &Vreg1Phis) const override;
71 void collectIncomingValuesFromPhi(
72 const MachineInstr *MI,
73 SmallVectorImpl<AMDGPU::Incoming> &Incomings) const override;
74 void replaceDstReg(Register NewReg, Register OldReg,
75 MachineBasicBlock *MBB) override;
76 void buildMergeLaneMasks(MachineBasicBlock &MBB,
78 Register DstReg, Register PrevReg,
79 Register CurReg) override;
80 void constrainAsLaneMask(AMDGPU::Incoming &In) override;
81
82 bool lowerTemporalDivergence();
83 bool lowerTemporalDivergenceI1();
84};
85
86DivergenceLoweringHelper::DivergenceLoweringHelper(
89 : PhiLoweringHelper(MF, DT, PDT), MUI(MUI), B(MF) {}
90
91// _(s1) -> SReg_32/64(s1)
92void DivergenceLoweringHelper::markAsLaneMask(Register DstReg) const {
93 assert(MRI->getType(DstReg) == LLT::scalar(1));
94
95 if (MRI->getRegClassOrNull(DstReg)) {
96 if (MRI->constrainRegClass(DstReg, ST->getBoolRC()))
97 return;
98 llvm_unreachable("Failed to constrain register class");
99 }
100
101 MRI->setRegClass(DstReg, ST->getBoolRC());
102}
103
104void DivergenceLoweringHelper::getCandidatesForLowering(
105 SmallVectorImpl<MachineInstr *> &Vreg1Phis) const {
106 LLT S1 = LLT::scalar(1);
107
108 // Add divergent i1 G_PHIs to the list. Only consider G_PHI instructions,
109 // not PHI instructions that may have been created by earlier lowering stages
110 // (e.g., lowerTemporalDivergenceI1).
111 for (MachineBasicBlock &MBB : MF) {
112 for (MachineInstr &MI : MBB.phis()) {
113 if (MI.getOpcode() != TargetOpcode::G_PHI)
114 continue;
115 Register Dst = MI.getOperand(0).getReg();
116 if (MRI->getType(Dst) == S1 && MUI->isDivergentAtDef(Dst))
117 Vreg1Phis.push_back(&MI);
118 }
119 }
120}
121
122void DivergenceLoweringHelper::collectIncomingValuesFromPhi(
123 const MachineInstr *MI,
124 SmallVectorImpl<AMDGPU::Incoming> &Incomings) const {
125 for (unsigned i = 1; i < MI->getNumOperands(); i += 2) {
126 Incomings.emplace_back(MI->getOperand(i).getReg(),
127 MI->getOperand(i + 1).getMBB(), Register());
128 }
129}
130
131void DivergenceLoweringHelper::replaceDstReg(Register NewReg, Register OldReg,
133 BuildMI(*MBB, MBB->getFirstNonPHI(), {}, TII->get(AMDGPU::COPY), OldReg)
134 .addReg(NewReg);
135}
136
137// Copy Reg to new lane mask register, insert a copy after instruction that
138// defines Reg while skipping phis if needed.
139Register DivergenceLoweringHelper::buildRegCopyToLaneMask(Register Reg) {
140 Register LaneMask = AMDGPU::createLaneMaskReg(MRI, LaneMaskRegAttrs);
141 MachineInstr *Instr = MRI->getVRegDef(Reg);
142 MachineBasicBlock *MBB = Instr->getParent();
143 B.setInsertPt(*MBB, MBB->SkipPHIsAndLabels(std::next(Instr->getIterator())));
144 B.buildCopy(LaneMask, Reg);
145 return LaneMask;
146}
147
148// bb.previous
149// %PrevReg = ...
150//
151// bb.current
152// %CurReg = ...
153//
154// %DstReg - not defined
155//
156// -> (wave32 example, new registers have sreg_32 reg class and S1 LLT)
157//
158// bb.previous
159// %PrevReg = ...
160// %PrevRegCopy:sreg_32(s1) = COPY %PrevReg
161//
162// bb.current
163// %CurReg = ...
164// %CurRegCopy:sreg_32(s1) = COPY %CurReg
165// ...
166// %PrevMaskedReg:sreg_32(s1) = ANDN2 %PrevRegCopy, ExecReg - active lanes 0
167// %CurMaskedReg:sreg_32(s1) = AND %ExecReg, CurRegCopy - inactive lanes to 0
168// %DstReg:sreg_32(s1) = OR %PrevMaskedReg, CurMaskedReg
169//
170// DstReg = for active lanes rewrite bit in PrevReg with bit from CurReg
171void DivergenceLoweringHelper::buildMergeLaneMasks(
173 Register DstReg, Register PrevReg, Register CurReg) {
174 // DstReg = (PrevReg & !EXEC) | (CurReg & EXEC)
175 // TODO: check if inputs are constants or results of a compare.
176
177 Register PrevRegCopy = buildRegCopyToLaneMask(PrevReg);
178 Register CurRegCopy = buildRegCopyToLaneMask(CurReg);
179 Register PrevMaskedReg = AMDGPU::createLaneMaskReg(MRI, LaneMaskRegAttrs);
180 Register CurMaskedReg = AMDGPU::createLaneMaskReg(MRI, LaneMaskRegAttrs);
181
182 B.setInsertPt(MBB, I);
183 B.buildInstr(LMC->AndN2Opc, {PrevMaskedReg}, {PrevRegCopy, LMC->ExecReg})
184 .setOperandDead(3);
185 B.buildInstr(LMC->AndOpc, {CurMaskedReg}, {LMC->ExecReg, CurRegCopy})
186 .setOperandDead(3);
187 B.buildInstr(LMC->OrOpc, {DstReg}, {PrevMaskedReg, CurMaskedReg})
188 .setOperandDead(3);
189}
190
191// GlobalISel has to constrain S1 incoming taken as-is with lane mask register
192// class. Insert a copy of Incoming.Reg to new lane mask inside Incoming.Block,
193// Incoming.Reg becomes that new lane mask.
194void DivergenceLoweringHelper::constrainAsLaneMask(AMDGPU::Incoming &In) {
195 B.setInsertPt(*In.Block, In.Block->getFirstTerminator());
196
197 auto Copy = B.buildCopy(LLT::scalar(1), In.Reg);
198 MRI->setRegClass(Copy.getReg(0), ST->getBoolRC());
199 In.Reg = Copy.getReg(0);
200}
201
202void replaceUsesOfRegInInstWith(Register Reg, MachineInstr *Inst,
203 Register NewReg) {
204 for (MachineOperand &Op : Inst->operands()) {
205 if (Op.isReg() && Op.getReg() == Reg)
206 Op.setReg(NewReg);
207 }
208}
209
210bool DivergenceLoweringHelper::lowerTemporalDivergence() {
213
214 for (auto [Reg, UseInst, _] : MUI->getTemporalDivergenceList()) {
215 if (MRI->getType(Reg) == LLT::scalar(1) || MUI->isDivergentAtDef(Reg) ||
216 ILMA.isS32S64LaneMask(Reg))
217 continue;
218
219 Register CachedTDCopy = TDCache.lookup(Reg);
220 if (CachedTDCopy) {
221 replaceUsesOfRegInInstWith(Reg, UseInst, CachedTDCopy);
222 continue;
223 }
224
225 MachineInstr *Inst = MRI->getVRegDef(Reg);
227 B.setInsertPt(*MBB, MBB->SkipPHIsAndLabels(std::next(Inst->getIterator())));
228
229 Register VgprReg = MRI->createGenericVirtualRegister(MRI->getType(Reg));
230 B.buildInstr(AMDGPU::COPY, {VgprReg}, {Reg})
231 .addUse(LMC->ExecReg, RegState::Implicit);
232
233 replaceUsesOfRegInInstWith(Reg, UseInst, VgprReg);
234 TDCache[Reg] = VgprReg;
235 }
236 return false;
237}
238
239bool DivergenceLoweringHelper::lowerTemporalDivergenceI1() {
240 MachineRegisterInfo::VRegAttrs BoolS1 = {ST->getBoolRC(), LLT::scalar(1)};
241 initializeLaneMaskRegisterAttributes(BoolS1);
243
244 const auto &CInfo = MUI->getCycleInfo();
245
246 // In case of use outside muliple nested cycles or muliple uses we only need
247 // to merge lane mask across largest relevant cycle.
249 for (auto [Reg, UseInst, LRC] : MUI->getTemporalDivergenceList()) {
250 if (MRI->getType(Reg) != LLT::scalar(1))
251 continue;
252
253 auto [LRCCacheIter, RegNotCached] = LRCCache.try_emplace(Reg);
254 auto &CycleMergedMask = LRCCacheIter->getSecond();
255 CycleRef &CachedLRC = CycleMergedMask.first;
256 if (RegNotCached || CInfo.contains(LRC, CachedLRC)) {
257 CachedLRC = LRC;
258 }
259 }
260
261 for (auto &LRCCacheEntry : LRCCache) {
262 Register Reg = LRCCacheEntry.first;
263 auto &CycleMergedMask = LRCCacheEntry.getSecond();
264 CycleRef Cycle = CycleMergedMask.first;
265
266 Register MergedMask = MRI->createVirtualRegister(BoolS1);
267 SSAUpdater.Initialize(MergedMask);
268
269 MachineBasicBlock *MBB = MRI->getDefBlock(Reg);
270 SSAUpdater.AddAvailableValue(MBB, MergedMask);
271
272 for (auto Entry : CInfo.getEntries(Cycle)) {
273 for (MachineBasicBlock *Pred : Entry->predecessors()) {
274 if (!CInfo.contains(Cycle, Pred)) {
275 B.setInsertPt(*Pred, Pred->getFirstTerminator());
276 auto ImplDef = B.buildInstr(AMDGPU::IMPLICIT_DEF, {BoolS1}, {});
277 SSAUpdater.AddAvailableValue(Pred, ImplDef.getReg(0));
278 }
279 }
280 }
281
282 buildMergeLaneMasks(*MBB, MBB->getFirstTerminator(), {}, MergedMask,
284
285 CycleMergedMask.second = MergedMask;
286 }
287
288 for (auto [Reg, UseInst, Cycle] : MUI->getTemporalDivergenceList()) {
289 if (MRI->getType(Reg) != LLT::scalar(1))
290 continue;
291
292 replaceUsesOfRegInInstWith(Reg, UseInst, LRCCache.lookup(Reg).second);
293 }
294
295 return false;
296}
297
298static bool runDivergenceLowering(MachineFunction &MF, MachineDominatorTree &DT,
301 DivergenceLoweringHelper Helper(MF, DT, PDT, &MUI);
302
303 bool Changed = false;
304 // Temporal divergence lowering needs to inspect list of instructions used
305 // outside cycle with divergent exit provided by uniformity analysis. Uniform
306 // instructions from the list require lowering, no instruction is deleted.
307 // Thus it needs to be run before lowerPhis that deletes phis that require
308 // lowering and replaces them with new instructions.
309
310 // Non-i1 temporal divergence lowering.
311 Changed |= Helper.lowerTemporalDivergence();
312 // This covers both uniform and divergent i1s. Lane masks are in sgpr and need
313 // to be updated in each iteration.
314 Changed |= Helper.lowerTemporalDivergenceI1();
315 // Temporal divergence lowering of divergent i1 phi used outside of the cycle
316 // could also be handled by lowerPhis but we do it in lowerTempDivergenceI1
317 // since in some case lowerPhis does unnecessary lane mask merging.
318 Changed |= Helper.lowerPhis();
319 return Changed;
320}
321
322} // End anonymous namespace.
323
324INITIALIZE_PASS_BEGIN(AMDGPUGlobalISelDivergenceLoweringLegacy, DEBUG_TYPE,
325 "AMDGPU GlobalISel divergence lowering", false, false)
329INITIALIZE_PASS_END(AMDGPUGlobalISelDivergenceLoweringLegacy, DEBUG_TYPE,
330 "AMDGPU GlobalISel divergence lowering", false, false)
331
332char AMDGPUGlobalISelDivergenceLoweringLegacy::ID = 0;
333
335 AMDGPUGlobalISelDivergenceLoweringLegacy::ID;
336
338 return new AMDGPUGlobalISelDivergenceLoweringLegacy();
339}
340
341bool AMDGPUGlobalISelDivergenceLoweringLegacy::runOnMachineFunction(
342 MachineFunction &MF) {
344 getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
346 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
348 getAnalysis<MachineUniformityAnalysisPass>().getUniformityInfo();
349
350 return runDivergenceLowering(MF, DT, PDT, MUI);
351}
352
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
constexpr LLT S1
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define DEBUG_TYPE
const HexagonInstrInfo * TII
#define _
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
This file declares the MachineIRBuilder class.
Register Reg
Machine IR instance of the generic uniformity analysis.
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
Interface definition of the PhiLoweringHelper class that implements lane mask merging algorithm for d...
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:278
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
Opaque handle to a cycle within a GenericCycleInfo that wraps the cycle's preorder index.
A debug info location.
Definition DebugLoc.h:126
ValueT lookup(const_arg_type_t< KeyT > Val) const
Return the entry for the specified key, or a default constructed value if no such entry exists.
Definition DenseMap.h:285
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:348
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
bool isDivergentAtDef(ConstValueRefT V) const
Whether V is divergent at its definition.
const CycleInfoT & getCycleInfo() const
The cycle info this analysis was computed with.
iterator_range< TemporalDivergenceTuple * > getTemporalDivergenceList() const
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
iterator_range< pred_iterator > predecessors()
MachineInstrBundleIterator< MachineInstr > iterator
Analysis pass which computes a MachineDominatorTree.
Analysis pass which computes a MachineDominatorTree.
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
Helper class to build MachineInstr.
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
Representation of each machine instruction.
const MachineBasicBlock * getParent() const
mop_range operands()
MachineOperand class - Representation of each machine instruction operand.
MachinePostDominatorTree - an analysis pass wrapper for DominatorTree used to compute the post-domina...
MachineSSAUpdater - This class updates SSA form for a set of virtual registers defined in multiple bl...
Legacy analysis pass which computes a MachineUniformityInfo.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
Wrapper class representing virtual and physical registers.
Definition Register.h:20
Helper class for SSA formation on a set of values defined in multiple blocks.
Definition SSAUpdater.h:39
LLVM_ABI void Initialize(Type *Ty, StringRef Name)
Reset this object to get ready for a new set of SSA updates with type 'Ty'.
LLVM_ABI Value * GetValueInMiddleOfBlock(BasicBlock *BB)
Construct SSA form, materializing a value that is live in the middle of the specified block.
LLVM_ABI void AddAvailableValue(BasicBlock *BB, Value *V)
Indicate that a rewritten value is available in the specified block with the specified value.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
self_iterator getIterator()
Definition ilist_node.h:123
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Register createLaneMaskReg(MachineRegisterInfo *MRI, MachineRegisterInfo::VRegAttrs LaneMaskRegAttrs)
This is an optimization pass for GlobalISel generic memory operations.
char & AMDGPUGlobalISelDivergenceLoweringLegacyID
GenericUniformityInfo< MachineSSAContext > MachineUniformityInfo
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
@ Implicit
Not emitted register (e.g. carry, or temporary result).
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
DWARFExpression::Operation Op
FunctionPass * createAMDGPUGlobalISelDivergenceLoweringPass()
Incoming for lane mask phi as machine instruction, incoming register Reg and incoming block Block are...
All attributes(register class or bank and low-level type) a virtual register can have.