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 // Commute the components the match was checked with commuted.
201 void applyCommutes(const SIInstrInfo &TII, VOPDMatchInfo &Match) {
202 for (unsigned I : {0u, 1u}) {
203 if (Match.Commute[I] && !TII.commuteInstruction(*Match.InOrder[I]))
204 llvm_unreachable("failed to commute VOPD component");
205 }
206 }
207
208 void materializeLiteral(const SIInstrInfo &TII, VOPDCandidate &Candidate) {
209 if (!Candidate.needsMaterialization())
210 return;
211
213 assert(Candidate.MaterializationReg);
214 assert(all_of(Fixups,
215 [Imm = Fixups.front().Imm](const VOPDLiteralFixup &Fixup) {
216 return Fixup.Imm == Imm;
217 }));
218
219 MachineInstr *InsertPt = Candidate.Match.InOrder[0];
220 BuildMI(*InsertPt->getParent(), InsertPt, DebugLoc(),
221 TII.get(AMDGPU::S_MOV_B32), Candidate.MaterializationReg)
222 .addImm(Fixups.front().Imm);
223 ++NumLiteralsMaterialized;
224
225 for (const VOPDLiteralFixup &Fixup : Fixups) {
226 MachineInstr *MI = Fixup.CompIdx == AMDGPU::VOPD::X
227 ? Candidate.Match.getMIX()
228 : Candidate.Match.getMIY();
229 MI->getOperand(Fixup.OpIdx)
230 .ChangeToRegister(Candidate.MaterializationReg, /*isDef=*/false);
231 }
232 }
233
234 bool doReplace(const SIInstrInfo *SII, VOPDMatchInfo &Match) {
235 MachineInstr *MIX = Match.getMIX();
236 MachineInstr *MIY = Match.getMIY();
237 unsigned Opc1 = MIX->getOpcode();
238 unsigned Opc2 = MIY->getOpcode();
239 unsigned EncodingFamily =
241 int NewOpcode =
243 AMDGPU::getVOPDOpcode(Opc2, Match.IsVOPD3),
244 EncodingFamily, Match.IsVOPD3);
245 assert(NewOpcode != -1 &&
246 "Should have previously determined this as a possible VOPD\n");
247
248 auto VOPDInst =
249 BuildMI(*MIX->getParent(), MIX, MIX->getDebugLoc(), SII->get(NewOpcode))
250 .setMIFlags(MIX->getFlags() | MIY->getFlags());
251
252 namespace VOPD = AMDGPU::VOPD;
253 MachineInstr *MI[] = {MIX, MIY};
254 auto InstInfo = AMDGPU::getVOPDInstInfo(MIX->getDesc(), MIY->getDesc());
255
256 for (auto CompIdx : VOPD::COMPONENTS) {
257 auto MCOprIdx = InstInfo[CompIdx].getIndexOfDstInMCOperands();
258 VOPDInst.add(MI[CompIdx]->getOperand(MCOprIdx));
259 }
260
261 const AMDGPU::OpName Mods[2][3] = {
262 {AMDGPU::OpName::src0X_modifiers, AMDGPU::OpName::vsrc1X_modifiers,
263 AMDGPU::OpName::vsrc2X_modifiers},
264 {AMDGPU::OpName::src0Y_modifiers, AMDGPU::OpName::vsrc1Y_modifiers,
265 AMDGPU::OpName::vsrc2Y_modifiers}};
266 const AMDGPU::OpName SrcMods[3] = {AMDGPU::OpName::src0_modifiers,
267 AMDGPU::OpName::src1_modifiers,
268 AMDGPU::OpName::src2_modifiers};
269 const unsigned VOPDOpc = VOPDInst->getOpcode();
270
271 for (auto CompIdx : VOPD::COMPONENTS) {
272 auto CompSrcOprNum = InstInfo[CompIdx].getCompSrcOperandsNum();
273 bool IsVOP3 = SII->isVOP3(*MI[CompIdx]);
274 for (unsigned CompSrcIdx = 0; CompSrcIdx < CompSrcOprNum; ++CompSrcIdx) {
275 if (AMDGPU::hasNamedOperand(VOPDOpc, Mods[CompIdx][CompSrcIdx])) {
276 const MachineOperand *Mod =
277 SII->getNamedOperand(*MI[CompIdx], SrcMods[CompSrcIdx]);
278 VOPDInst.addImm(Mod ? Mod->getImm() : 0);
279 }
280 auto MCOprIdx =
281 InstInfo[CompIdx].getIndexOfSrcInMCOperands(CompSrcIdx, IsVOP3);
282 VOPDInst.add(MI[CompIdx]->getOperand(MCOprIdx));
283 }
284 if (MI[CompIdx]->getOpcode() == AMDGPU::V_CNDMASK_B32_e32 &&
285 Match.IsVOPD3)
286 VOPDInst.addReg(AMDGPU::VCC_LO);
287 }
288
289 if (Match.IsVOPD3) {
290 if (unsigned BitOp2 = AMDGPU::getBitOp2(Opc2))
291 VOPDInst.addImm(BitOp2);
292 }
293
294 SII->fixImplicitOperands(*VOPDInst);
295 for (auto CompIdx : VOPD::COMPONENTS)
296 VOPDInst.copyImplicitOps(*MI[CompIdx]);
297
298 LLVM_DEBUG(dbgs() << "VOPD Fused: " << *VOPDInst << " from\tX: " << *MIX
299 << "\tY: " << *MIY << "\n");
300
301 for (auto CompIdx : VOPD::COMPONENTS)
302 MI[CompIdx]->eraseFromParent();
303
304 ++NumVOPDCreated;
305 return true;
306 }
307
308 bool run(MachineFunction &MF) {
309 ST = &MF.getSubtarget<GCNSubtarget>();
310 if (!AMDGPU::hasVOPD(*ST) || !ST->isWave32())
311 return false;
312 LLVM_DEBUG(dbgs() << "CreateVOPD Pass:\n");
313
314 const SIInstrInfo *SII = ST->getInstrInfo();
315 bool Changed = false;
316
317 for (MachineBasicBlock &MBB : MF) {
319 auto MII = MBB.begin(), E = MBB.end();
320 while (MII != E) {
321 MachineInstr *FirstMI = &*MII;
322 MII = next_nodbg(MII, MBB.end());
323 if (MII == MBB.end())
324 break;
325 if (FirstMI->isDebugInstr())
326 continue;
327 MachineInstr *SecondMI = &*MII;
328
329 if (std::optional<VOPDMatchInfo> Match =
330 tryMatchVOPDPair(*SII, *FirstMI, *SecondMI))
331 Candidates.push_back({std::move(*Match), Register()});
332 }
333
334 assignMaterializationRegisters(MBB, Candidates);
335 SmallVector<VOPDCandidate *, 8> Selected = selectCandidates(Candidates);
336 for (VOPDCandidate *Candidate : Selected) {
337 applyCommutes(*SII, Candidate->Match);
338 materializeLiteral(*SII, *Candidate);
339 Changed |= doReplace(SII, Candidate->Match);
340 }
341 }
342
343 return Changed;
344 }
345};
346
347class GCNCreateVOPDLegacy : public MachineFunctionPass {
348public:
349 static char ID;
350 GCNCreateVOPDLegacy() : MachineFunctionPass(ID) {}
351
352 StringRef getPassName() const override {
353 return "GCN Create VOPD Instructions";
354 }
355
356protected:
357 void getAnalysisUsage(AnalysisUsage &AU) const override {
358 AU.setPreservesCFG();
360 }
361
362 bool runOnMachineFunction(MachineFunction &MF) override {
363 if (skipFunction(MF.getFunction()))
364 return false;
365
366 return GCNCreateVOPD().run(MF);
367 }
368};
369
370} // namespace
371
379
380char GCNCreateVOPDLegacy::ID = 0;
381
382char &llvm::GCNCreateVOPDID = GCNCreateVOPDLegacy::ID;
383
384INITIALIZE_PASS(GCNCreateVOPDLegacy, DEBUG_TYPE, "GCN Create VOPD Instructions",
385 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
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned getVOPDOpcode(unsigned Opc, bool VOPD3)
unsigned getVOPDEncodingFamily(const MCSubtargetInfo &ST)
LLVM_READONLY bool hasNamedOperand(uint32_t Opcode, OpName NamedIdx)
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
bool Commute[2]
Entries in InOrder which only pair once commuted.
MachineInstr * getMIY() const
MachineInstr * InOrder[2]
The component instructions in program order.