LLVM 24.0.0git
GCNCreateVOPD.cpp
Go to the documentation of this file.
1//===- GCNCreateVOPD.cpp - Create VOPD Instructions ----------------------===//
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/// Form VOPD instructions from adjacent VALU operations on wave32. The post-RA
11/// scheduler puts likely component pairs next to each other. This pass checks
12/// their final physical-register constraints and selects a non-overlapping set.
13///
14/// VOPD3 components cannot encode literal operands. When all non-inline
15/// immediates in a pair have the same 32-bit value, the pass can materialize
16/// that value in an SGPR which is free over the pair. The move and its register
17/// stay pair-local, so fusion adds at most one move and does not extend
18/// register pressure across pairs.
19///
20/// The pass considers every adjacent candidate. It first maximizes the number
21/// of pairs, then minimizes scalar moves among equal-size matchings. The
22/// earlier candidate wins an exact tie.
23///
24//
25//===----------------------------------------------------------------------===//
26
27#include "AMDGPU.h"
28#include "GCNSubtarget.h"
29#include "GCNVOPDUtils.h"
30#include "SIInstrInfo.h"
32#include "llvm/ADT/STLExtras.h"
34#include "llvm/ADT/Statistic.h"
41#include "llvm/Support/Debug.h"
42
43#define DEBUG_TYPE "gcn-create-vopd"
44STATISTIC(NumVOPDCreated, "Number of VOPD Insts Created.");
45STATISTIC(NumLiteralsMaterialized,
46 "Number of immediates moved into a scalar register to allow VOPD3 "
47 "pairing.");
48STATISTIC(NumCandidateEdgesWithoutFreeSGPR,
49 "Number of VOPD3 candidate edges skipped because no scalar register "
50 "was free for their immediate.");
51
52using namespace llvm;
53
54namespace {
55
56struct VOPDCandidate {
57 VOPDMatchInfo Match;
58 /// The register for Match.LiteralFixups, or a null register if none is free.
59 Register MaterializationReg;
60
61 bool needsMaterialization() const { return !Match.LiteralFixups.empty(); }
62
63 bool isFeasible() const {
64 return !needsMaterialization() || MaterializationReg;
65 }
66};
67
68} // namespace
69
70/// Add everything the instructions in [\p Begin, \p RangeEnd] touch to
71/// \p Live, which already holds what is live after \p RangeEnd.
73 MachineInstr &RangeEnd) {
75 std::next(MachineBasicBlock::iterator(&RangeEnd));
76 for (MachineInstr &MI : make_range(Begin, After)) {
77 if (!MI.isDebugInstr())
78 Live.accumulate(MI);
79 }
80}
81
82/// Return a scalar register which every fixup can read and which no value in
83/// \p Live occupies, or a null register. Low registers are preferred, because
84/// those are most likely in use already, so the function's register count does
85/// not grow.
87 const MachineRegisterInfo &MRI,
88 const LiveRegUnits &Live,
90 assert(!Fixups.empty());
91 const SIRegisterInfo *TRI = ST.getRegisterInfo();
92 for (MCPhysReg Reg : AMDGPU::SGPR_32RegClass) {
93 // SGPR_32 also holds the halves of VCC. Writing those changes VCCZ,
94 // which is not modelled by \p Live, so a free half is not safe to use.
95 if (MRI.isReserved(Reg) || TRI->isSubRegisterEq(AMDGPU::VCC, Reg) ||
96 !Live.available(Reg))
97 continue;
98 if (!all_of(Fixups, [Reg](const VOPDLiteralFixup &Fixup) {
99 return Fixup.SlotRC->contains(Reg);
100 }))
101 continue;
102 return Reg;
103 }
104 return Register();
105}
106
107namespace {
108
109class GCNCreateVOPD {
110public:
111 const GCNSubtarget *ST = nullptr;
112
113 void
114 assignMaterializationRegisters(MachineBasicBlock &MBB,
116 auto Candidate = Candidates.rbegin();
117 auto SkipPlainCandidates = [&] {
118 while (Candidate != Candidates.rend() &&
119 !Candidate->needsMaterialization())
120 ++Candidate;
121 };
122 SkipPlainCandidates();
123 if (Candidate == Candidates.rend())
124 return;
125
126 const MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo();
127 LiveRegUnits Walk(*ST->getRegisterInfo());
128 Walk.addLiveOuts(MBB);
129
130 // Before stepping over an instruction, Walk holds what is live immediately
131 // after it. This answers every pair-local range in one backward walk.
132 for (MachineInstr &MI : reverse(MBB)) {
133 if (Candidate != Candidates.rend() &&
134 Candidate->Match.InOrder[1] == &MI) {
135 LiveRegUnits RangeLive = Walk;
136 addRangeUses(RangeLive, Candidate->Match.InOrder[0]->getIterator(),
137 *Candidate->Match.InOrder[1]);
138 Candidate->MaterializationReg =
139 takeFreeSGPR(*ST, MRI, RangeLive, Candidate->Match.LiteralFixups);
140 ++Candidate;
141 SkipPlainCandidates();
142 }
143 if (!MI.isDebugInstr())
144 Walk.stepBackward(MI);
145 }
146 assert(Candidate == Candidates.rend() &&
147 "every candidate range must end in this block");
148 }
149
151 selectCandidates(MutableArrayRef<VOPDCandidate> Candidates) {
152 struct Score {
153 unsigned NumPairs = 0;
154 unsigned NumMoves = 0;
155 };
156
157 const size_t NumCandidates = Candidates.size();
158 SmallVector<Score, 8> Best(NumCandidates + 1);
159 SmallBitVector Take(NumCandidates);
160 auto NextNonOverlapping = [&](size_t I) {
161 size_t Next = I + 1;
162 if (Next != NumCandidates &&
163 Candidates[Next].Match.InOrder[0] == Candidates[I].Match.InOrder[1])
164 ++Next;
165 return Next;
166 };
167
168 // Maximize the number of pairs, then minimize the moves they need. Taking
169 // the current edge on an exact tie preserves the old left-to-right choice.
170 for (size_t I = NumCandidates; I-- != 0;) {
171 Best[I] = Best[I + 1];
172 if (!Candidates[I].isFeasible()) {
173 ++NumCandidateEdgesWithoutFreeSGPR;
174 continue;
175 }
176
177 Score With = Best[NextNonOverlapping(I)];
178 ++With.NumPairs;
179 With.NumMoves += Candidates[I].needsMaterialization();
180 if (With.NumPairs > Best[I].NumPairs ||
181 (With.NumPairs == Best[I].NumPairs &&
182 With.NumMoves <= Best[I].NumMoves)) {
183 Best[I] = With;
184 Take.set(I);
185 }
186 }
187
189 for (size_t I = 0; I != NumCandidates;) {
190 if (!Take[I]) {
191 ++I;
192 continue;
193 }
194 Selected.push_back(&Candidates[I]);
195 I = NextNonOverlapping(I);
196 }
197 return Selected;
198 }
199
200 void materializeLiteral(const SIInstrInfo &TII, VOPDCandidate &Candidate) {
201 if (!Candidate.needsMaterialization())
202 return;
203
205 assert(Candidate.MaterializationReg);
206 assert(all_of(Fixups,
207 [Imm = Fixups.front().Imm](const VOPDLiteralFixup &Fixup) {
208 return Fixup.Imm == Imm;
209 }));
210
211 MachineInstr *InsertPt = Candidate.Match.InOrder[0];
212 BuildMI(*InsertPt->getParent(), InsertPt, DebugLoc(),
213 TII.get(AMDGPU::S_MOV_B32), Candidate.MaterializationReg)
214 .addImm(Fixups.front().Imm);
215 ++NumLiteralsMaterialized;
216
217 for (const VOPDLiteralFixup &Fixup : Fixups) {
218 MachineInstr *MI = Fixup.CompIdx == AMDGPU::VOPD::X
219 ? Candidate.Match.getMIX()
220 : Candidate.Match.getMIY();
221 MI->getOperand(Fixup.OpIdx)
222 .ChangeToRegister(Candidate.MaterializationReg, /*isDef=*/false);
223 }
224 }
225
226 bool doReplace(const SIInstrInfo *SII, VOPDMatchInfo &Match) {
227 MachineInstr *MIX = Match.getMIX();
228 MachineInstr *MIY = Match.getMIY();
229 unsigned Opc1 = MIX->getOpcode();
230 unsigned Opc2 = MIY->getOpcode();
231 unsigned EncodingFamily =
233 int NewOpcode =
235 AMDGPU::getVOPDOpcode(Opc2, Match.IsVOPD3),
236 EncodingFamily, Match.IsVOPD3);
237 assert(NewOpcode != -1 &&
238 "Should have previously determined this as a possible VOPD\n");
239
240 auto VOPDInst =
241 BuildMI(*MIX->getParent(), MIX, MIX->getDebugLoc(), SII->get(NewOpcode))
242 .setMIFlags(MIX->getFlags() | MIY->getFlags());
243
244 namespace VOPD = AMDGPU::VOPD;
245 MachineInstr *MI[] = {MIX, MIY};
246 auto InstInfo = AMDGPU::getVOPDInstInfo(MIX->getDesc(), MIY->getDesc());
247
248 for (auto CompIdx : VOPD::COMPONENTS) {
249 auto MCOprIdx = InstInfo[CompIdx].getIndexOfDstInMCOperands();
250 VOPDInst.add(MI[CompIdx]->getOperand(MCOprIdx));
251 }
252
253 const AMDGPU::OpName Mods[2][3] = {
254 {AMDGPU::OpName::src0X_modifiers, AMDGPU::OpName::vsrc1X_modifiers,
255 AMDGPU::OpName::vsrc2X_modifiers},
256 {AMDGPU::OpName::src0Y_modifiers, AMDGPU::OpName::vsrc1Y_modifiers,
257 AMDGPU::OpName::vsrc2Y_modifiers}};
258 const AMDGPU::OpName SrcMods[3] = {AMDGPU::OpName::src0_modifiers,
259 AMDGPU::OpName::src1_modifiers,
260 AMDGPU::OpName::src2_modifiers};
261 const unsigned VOPDOpc = VOPDInst->getOpcode();
262
263 for (auto CompIdx : VOPD::COMPONENTS) {
264 auto CompSrcOprNum = InstInfo[CompIdx].getCompSrcOperandsNum();
265 bool IsVOP3 = SII->isVOP3(*MI[CompIdx]);
266 for (unsigned CompSrcIdx = 0; CompSrcIdx < CompSrcOprNum; ++CompSrcIdx) {
267 if (AMDGPU::hasNamedOperand(VOPDOpc, Mods[CompIdx][CompSrcIdx])) {
268 const MachineOperand *Mod =
269 SII->getNamedOperand(*MI[CompIdx], SrcMods[CompSrcIdx]);
270 VOPDInst.addImm(Mod ? Mod->getImm() : 0);
271 }
272 auto MCOprIdx =
273 InstInfo[CompIdx].getIndexOfSrcInMCOperands(CompSrcIdx, IsVOP3);
274 VOPDInst.add(MI[CompIdx]->getOperand(MCOprIdx));
275 }
276 if (MI[CompIdx]->getOpcode() == AMDGPU::V_CNDMASK_B32_e32 &&
277 Match.IsVOPD3)
278 VOPDInst.addReg(AMDGPU::VCC_LO);
279 }
280
281 if (Match.IsVOPD3) {
282 if (unsigned BitOp2 = AMDGPU::getBitOp2(Opc2))
283 VOPDInst.addImm(BitOp2);
284 }
285
286 SII->fixImplicitOperands(*VOPDInst);
287 for (auto CompIdx : VOPD::COMPONENTS)
288 VOPDInst.copyImplicitOps(*MI[CompIdx]);
289
290 LLVM_DEBUG(dbgs() << "VOPD Fused: " << *VOPDInst << " from\tX: " << *MIX
291 << "\tY: " << *MIY << "\n");
292
293 for (auto CompIdx : VOPD::COMPONENTS)
294 MI[CompIdx]->eraseFromParent();
295
296 ++NumVOPDCreated;
297 return true;
298 }
299
300 bool run(MachineFunction &MF) {
301 ST = &MF.getSubtarget<GCNSubtarget>();
302 if (!AMDGPU::hasVOPD(*ST) || !ST->isWave32())
303 return false;
304 LLVM_DEBUG(dbgs() << "CreateVOPD Pass:\n");
305
306 const SIInstrInfo *SII = ST->getInstrInfo();
307 bool Changed = false;
308
309 for (MachineBasicBlock &MBB : MF) {
311 auto MII = MBB.begin(), E = MBB.end();
312 while (MII != E) {
313 MachineInstr *FirstMI = &*MII;
314 MII = next_nodbg(MII, MBB.end());
315 if (MII == MBB.end())
316 break;
317 if (FirstMI->isDebugInstr())
318 continue;
319 MachineInstr *SecondMI = &*MII;
320
321 if (std::optional<VOPDMatchInfo> Match =
322 tryMatchVOPDPair(*SII, *FirstMI, *SecondMI))
323 Candidates.push_back({std::move(*Match), Register()});
324 }
325
326 assignMaterializationRegisters(MBB, Candidates);
327 SmallVector<VOPDCandidate *, 8> Selected = selectCandidates(Candidates);
328 for (VOPDCandidate *Candidate : Selected) {
329 materializeLiteral(*SII, *Candidate);
330 Changed |= doReplace(SII, Candidate->Match);
331 }
332 }
333
334 return Changed;
335 }
336};
337
338class GCNCreateVOPDLegacy : public MachineFunctionPass {
339public:
340 static char ID;
341 GCNCreateVOPDLegacy() : MachineFunctionPass(ID) {}
342
343 StringRef getPassName() const override {
344 return "GCN Create VOPD Instructions";
345 }
346
347protected:
348 void getAnalysisUsage(AnalysisUsage &AU) const override {
349 AU.setPreservesCFG();
351 }
352
353 bool runOnMachineFunction(MachineFunction &MF) override {
354 if (skipFunction(MF.getFunction()))
355 return false;
356
357 return GCNCreateVOPD().run(MF);
358 }
359};
360
361} // namespace
362
370
371char GCNCreateVOPDLegacy::ID = 0;
372
373char &llvm::GCNCreateVOPDID = GCNCreateVOPDLegacy::ID;
374
375INITIALIZE_PASS(GCNCreateVOPDLegacy, DEBUG_TYPE, "GCN Create VOPD Instructions",
376 false, false)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned Imm
MachineBasicBlock & MBB
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static void addRangeUses(LiveRegUnits &Live, MachineBasicBlock::iterator Begin, MachineInstr &RangeEnd)
Add everything the instructions in [Begin, RangeEnd] touch to Live, which already holds what is live ...
static Register takeFreeSGPR(const GCNSubtarget &ST, const MachineRegisterInfo &MRI, const LiveRegUnits &Live, ArrayRef< VOPDLiteralFixup > Fixups)
Return a scalar register which every fixup can read and which no value in Live occupies,...
AMD GCN specific subclass of TargetSubtarget.
#define DEBUG_TYPE
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
A set of register units.
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
PowerPC TLS Dynamic Call Fixup
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
Interface definition for SIInstrInfo.
This file contains some templates that are useful if you are working with the STL at all.
This file implements the SmallBitVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
#define LLVM_DEBUG(...)
Definition Debug.h:119
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:278
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &AM)
A set of register units used to track register liveness.
bool available(MCRegister Reg) const
Returns true if no part of physical register Reg is live.
LLVM_ABI void accumulate(const MachineInstr &MI)
Adds all register units used, defined or clobbered in MI.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
MachineInstrBundleIterator< MachineInstr > iterator
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.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & setMIFlags(unsigned Flags) const
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
bool isDebugInstr() const
const MCInstrDesc & getDesc() const
Returns the target instruction descriptor of this MachineInstr.
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
const MachineOperand & getOperand(unsigned i) const
uint32_t getFlags() const
Return the MI flags bitvector.
LLVM_ABI void ChangeToRegister(Register Reg, bool isDef, bool isImp=false, bool isKill=false, bool isDead=false, bool isUndef=false, bool isDebug=false)
ChangeToRegister - Replace this operand with a new register operand of the specified value.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
bool isReserved(MCRegister PhysReg) const
isReserved - Returns true when PhysReg is a reserved register.
reverse_iterator rbegin() const
Definition ArrayRef.h:341
reverse_iterator rend() const
Definition ArrayRef.h:342
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
const GCNSubtarget & getSubtarget() const
static bool isVOP3(const MCInstrDesc &Desc)
void fixImplicitOperands(MachineInstr &MI) const
LLVM_READONLY MachineOperand * getNamedOperand(MachineInstr &MI, AMDGPU::OpName OperandName) const
Returns the operand named Op.
void push_back(const T &Elt)
Changed
unsigned getVOPDOpcode(unsigned Opc, bool VOPD3)
LLVM_READONLY bool hasNamedOperand(uint64_t Opcode, OpName NamedIdx)
unsigned getVOPDEncodingFamily(const MCSubtargetInfo &ST)
unsigned getBitOp2(unsigned Opc)
VOPD::InstInfo getVOPDInstInfo(const MCInstrDesc &OpX, const MCInstrDesc &OpY)
bool hasVOPD(const MCSubtargetInfo &STI)
int getVOPDFull(unsigned OpX, unsigned OpY, unsigned EncodingFamily, bool VOPD3)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
unsigned getOpcode(const VPValue *V)
Return the instruction opcode for the recipe defining V or 0 for unsupported recipes and VPValues not...
This is an optimization pass for GlobalISel generic memory operations.
IterT next_nodbg(IterT It, IterT End, bool SkipPseudoOp=true)
Increment It, then continue incrementing it while it points to a debug instruction.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1755
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
auto reverse(ContainerTy &&C)
Definition STLExtras.h:408
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
MutableArrayRef(T &OneElt) -> MutableArrayRef< T >
@ Mod
The access may modify the value stored in memory.
Definition ModRef.h:34
uint16_t MCPhysReg
An unsigned integer type large enough to represent all physical registers, but not necessarily virtua...
Definition MCRegister.h:21
ArrayRef(const T &OneElt) -> ArrayRef< T >
char & GCNCreateVOPDID
std::optional< VOPDMatchInfo > tryMatchVOPDPair(const SIInstrInfo &TII, MachineInstr &FirstMI, MachineInstr &SecondMI)
Check whether FirstMI and SecondMI can be combined into a VOPD instruction.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
A 32-bit immediate which the VOPD encoding cannot hold.
Describes a matched VOPD pair.
SmallVector< VOPDLiteralFixup, 2 > LiteralFixups
Immediates which have to be moved into scalar registers before the pair can be built.
MachineInstr * getMIX() const
MachineInstr * getMIY() const
MachineInstr * InOrder[2]
The component instructions in program order.