27#define DEBUG_TYPE "amdgpu-anti-hints"
46 cl::desc(
"Anti-hints rules to select."),
49 "MFMA destination write-after-write"),
51 "XDL MFMA src2 write-after-read"),
53 "XDL WMMA A/B source write-after-read"),
55 "XDL SWMMAC sparse index write-after-read"),
57 "XDL WMMA destination write-after-write"),
59 "TRANS source write-after-read"),
61 "Memory address/data write-after-read (s_wait_xcnt)"),
63 "VALU source write-after-read by a load "
64 "(s_wait_alu va_vdst)"),
66 "Select all rules (default); per-rule "
67 "-amdgpu-anti-hints-for-* still apply")));
71 cl::desc(
"Enable Anti-Hints for memory address "
72 "operands and subsequent VGPR writes to "
78 cl::desc(
"Enable Anti-Hints for VA-VDST."),
83 cl::desc(
"Lookback window for VA_VDST anti-hints."),
88 cl::desc(
"Number of later memory instructions an "
89 "address anti-hint stays open."),
100 if (
TII.isLDSDMA(
MI))
104 else if (
TII.isMFMA(
MI))
110 else if (
TII.isDS(
MI))
112 else if (
TII.isVMEM(
MI))
114 else if (
TII.isSMRD(
MI))
116 else if (
TII.isEXP(
MI))
120 else if (
MI.isCopy() &&
MI.getOperand(0).getReg().isVirtual() &&
121 Ctx.TRI->hasVGPRs(Ctx.MRI->getRegClass(
MI.getOperand(0).getReg())))
128 if (MO.isReg() && MO.getReg().isVirtual() &&
129 Ctx.TRI->hasVGPRs(Ctx.MRI->getRegClass(MO.getReg()))) {
138bool isVirtualVGPR(
const HazardContext &Ctx,
const MachineOperand &MO) {
145 const HazardContext &Ctx,
146 SmallVectorImpl<Register> &Out) {
147 const SIInstrInfo &
TII = *Ctx.
TII;
149 case HazardOperand::None:
151 case HazardOperand::Def:
152 for (
const MachineOperand &MO :
MI.all_defs()) {
153 if (isVirtualVGPR(Ctx, MO))
157 case HazardOperand::Src0:
158 if (
const MachineOperand *MO =
159 TII.getNamedOperand(
MI, AMDGPU::OpName::src0)) {
160 if (isVirtualVGPR(Ctx, *MO))
164 case HazardOperand::Src1:
165 if (
const MachineOperand *MO =
166 TII.getNamedOperand(
MI, AMDGPU::OpName::src1)) {
167 if (isVirtualVGPR(Ctx, *MO))
171 case HazardOperand::Src2:
172 if (
const MachineOperand *MO =
173 TII.getNamedOperand(
MI, AMDGPU::OpName::src2)) {
174 if (isVirtualVGPR(Ctx, *MO))
178 case HazardOperand::Src0Src1:
179 for (AMDGPU::OpName Name : {AMDGPU::OpName::src0, AMDGPU::OpName::src1}) {
180 if (
const MachineOperand *MO =
TII.getNamedOperand(
MI, Name)) {
181 if (isVirtualVGPR(Ctx, *MO))
186 case HazardOperand::Idx:
187 if (
const MachineOperand *MO =
188 TII.getNamedOperand(
MI, AMDGPU::OpName::idx)) {
189 if (isVirtualVGPR(Ctx, *MO))
193 case HazardOperand::Vaddr:
194 if (
const MachineOperand *MO =
195 TII.getNamedOperand(
MI, AMDGPU::OpName::vaddr)) {
196 if (isVirtualVGPR(Ctx, *MO))
200 case HazardOperand::AnySrc:
201 for (AMDGPU::OpName Name :
202 {AMDGPU::OpName::src0, AMDGPU::OpName::src1, AMDGPU::OpName::src2}) {
203 if (
const MachineOperand *MO =
TII.getNamedOperand(
MI, Name)) {
204 if (isVirtualVGPR(Ctx, *MO))
209 case HazardOperand::AnyUse:
210 for (
const MachineOperand &MO :
MI.all_uses()) {
211 if (isVirtualVGPR(Ctx, MO))
218enum class MFMAHazardKind { RAW, WAW, WAR };
222unsigned mfmaWaitStates(
const MachineInstr &
MFMA, MFMAHazardKind Kind,
224 const SIInstrInfo &
TII = *Ctx.
TII;
225 const GCNSubtarget &
ST = *Ctx.
ST;
230 auto GFX940NPass = [&]() ->
unsigned {
232 ? NumPasses + 3 + (NumPasses != 2 &&
ST.hasGFX950Insts())
235 auto SMFMANPass = [&]() ->
unsigned {
249 case MFMAHazardKind::RAW:
256 return Mem ? 18 : (
ST.hasGFX950Insts() ? 19 : 11);
261 return ST.hasGFX940Insts() ? GFX940NPass() : SMFMANPass();
263 case MFMAHazardKind::WAW:
275 return ST.hasGFX940Insts() ? GFX940NPass() : SMFMANPass();
277 case MFMAHazardKind::WAR:
295 const HazardContext &Ctx) {
296 return mfmaWaitStates(
P, MFMAHazardKind::WAW,
HC::None, Ctx);
299 const HazardContext &Ctx) {
300 return mfmaWaitStates(
P, MFMAHazardKind::WAR,
HC::None, Ctx);
303constexpr unsigned NumWMMAHazardCategories = 7;
304unsigned wmmaHazardCategory(
const MachineInstr &Producer,
305 const HazardContext &Ctx) {
307 const bool LowestRate = Ctx.
ST->hasGFX125xLowestRateWMMA();
308 switch (Ctx.
SchedModel->computeInstrLatency(&Producer)) {
314 return LowestRate ? 4 : (
IsSWMMAC ? 3 : 1);
318 return NumWMMAHazardCategories;
323unsigned wmmaCoexecWindow(
const MachineInstr &Producer,
325 const HazardContext &Ctx) {
326 const unsigned Category = wmmaHazardCategory(Producer, Ctx);
327 if (Category >= NumWMMAHazardCategories)
330 static constexpr unsigned WMMAWaitStates[] = {5, 9, 3, 5, 9, 17, 2};
331 static constexpr unsigned VALUWaitStates[] = {4, 8, 2, 4, 8, 16, 1};
332 return (ConsumerClass &
HC::WMMA) ? WMMAWaitStates[Category]
333 : VALUWaitStates[Category];
336unsigned mfmaReaderRawWindow(
const MachineInstr &Producer,
338 const HazardContext &Ctx) {
339 return mfmaWaitStates(Producer, MFMAHazardKind::RAW, ReaderClass, Ctx);
342bool hasMFMAHazard(
const HazardContext &Ctx) {
343 return Ctx.
ST->hasGFX90AInsts();
346bool hasWMMACoexecHazard(
const HazardContext &Ctx) {
347 return Ctx.
ST->hasWMMACoexecutionHazards();
350bool hasTransCoexecHazard(
const HazardContext &Ctx) {
351 return Ctx.
ST->hasTransCoexecutionHazard();
354bool hasGFX1250Insts(
const HazardContext &Ctx) {
355 return Ctx.
ST->hasGFX1250Insts();
366bool isMFMAWAWRuleEnabled(
const HazardContext &Ctx) {
370bool isMFMAWARRuleEnabled(
const HazardContext &Ctx) {
374bool isWMMAWARABRuleEnabled(
const HazardContext &Ctx) {
378bool isSWMMACWARIndexRuleEnabled(
const HazardContext &Ctx) {
382bool isWMMAWAWRuleEnabled(
const HazardContext &Ctx) {
386bool isTransWARRuleEnabled(
const HazardContext &Ctx) {
390bool isAddrWARRuleEnabled(
const HazardContext &Ctx) {
395bool isVAVdstWARRuleEnabled(
const HazardContext &Ctx) {
400bool isXDLMFMA(
const MachineInstr &
MI,
const HazardContext &Ctx) {
404bool isXDLWMMA(
const MachineInstr &
MI,
const HazardContext &Ctx) {
408bool isXDLSWMMAC(
const MachineInstr &
MI,
const HazardContext &Ctx) {
412bool isDSorVMEMLoad(
const MachineInstr &
MI,
const HazardContext &Ctx) {
416unsigned resolveWindow(
const ConsumerTarget &CT,
const MachineInstr &
MI,
417 const HazardContext &Ctx) {
435 HazardAntiHintRule &rule()
const {
return S.Rules[
Idx]; }
438 RuleBuilder(HazardRuleSet &S,
unsigned Idx) : S(S),
Idx(
Idx) {}
447 rule().AdvanceForRawWindow = AdvanceForRawWindow;
456 rule().Consumers.push_back(
466 RuleBuilder &lifetime(
Lifetime Life) {
472 RuleBuilder addRule() {
474 return RuleBuilder(*
this, Rules.
size() - 1);
486 const WindowSpec MfmaWawWindow{0,
nullptr, mfmaWawWindow};
487 const WindowSpec MfmaWarWindow{0,
nullptr, mfmaWarWindow};
492 const WindowSpec WmmaCoexecWindow{0,
nullptr, wmmaCoexecWindow};
500 .enabledIf(isMFMAWAWRuleEnabled)
502 .rawCredit(mfmaReaderRawWindow)
503 .consumer(MfmaConsumers, HO::Def, MfmaWawWindow,
HC::None,
504 AntiHintDirection::OneDirectional);
508 .enabledIf(isMFMAWARRuleEnabled)
509 .producer({
HC::MFMA}, HO::Src2, isXDLMFMA)
510 .rawCredit(mfmaReaderRawWindow)
511 .consumer(MfmaConsumers, HO::Def, MfmaWarWindow,
HC::None,
512 AntiHintDirection::OneDirectional);
516 .enabledIf(isWMMAWARABRuleEnabled)
517 .producer({
HC::WMMA}, HO::Src0Src1, isXDLWMMA)
518 .consumer(WmmaCoexecConsumers, HO::Def, WmmaCoexecWindow, CoexecCounters,
519 AntiHintDirection::OneDirectional)
520 .consumer({
HC::VMEM |
HC::DS}, HO::Def, WmmaCoexecWindow, CoexecCounters,
521 AntiHintDirection::OneDirectional);
525 .enabledIf(isSWMMACWARIndexRuleEnabled)
526 .producer({
HC::WMMA}, HO::Src2, isXDLSWMMAC)
527 .consumer(WmmaCoexecConsumers, HO::Def, WmmaCoexecWindow, CoexecCounters,
528 AntiHintDirection::OneDirectional);
532 .enabledIf(isWMMAWAWRuleEnabled)
533 .producer({
HC::WMMA}, HO::Def, isXDLWMMA)
534 .consumer(WmmaCoexecConsumers, HO::Def, WmmaCoexecWindow, CoexecCounters,
535 AntiHintDirection::OneDirectional);
539 .enabledIf(isTransWARRuleEnabled)
542 CoexecCounters, AntiHintDirection::OneDirectional);
546 .enabledIf(isAddrWARRuleEnabled)
549 AntiHintDirection::Symmetric);
553 .enabledIf(isVAVdstWARRuleEnabled)
554 .lifetime(Lifetime::RegisterCount)
557 AntiHintDirection::Symmetric, isDSorVMEMLoad);
559 return S.buildRules();
567struct AntiHintWindow {
569 const MachineInstr *
Producer =
nullptr;
571 unsigned Elapsed = 0;
578class AntiHintEngine {
579 const HazardContext &Ctx;
583 bool AnyEnabled =
false;
586 AntiHintEngine(
const HazardContext &Ctx)
587 : Ctx(Ctx), Rules(getAntiHintsRules()), RuleApplies(Rules.
size()) {
588 for (
unsigned R = 0;
R < Rules.
size(); ++
R) {
589 const HazardAntiHintRule &Rule = Rules[
R];
591 AnyEnabled |= RuleApplies[
R];
600 for (
unsigned R = 0;
R < Rules.
size(); ++
R)
601 Tracking[R].resize(Rules[R].Consumers.size());
603 for (
const MachineBasicBlock &
MBB : MF) {
604 for (RuleTracking &RT : Tracking) {
605 for (ConsumerTracking &Track : RT)
608 for (
const MachineInstr &
MI :
MBB) {
609 if (
MI.isMetaInstruction())
614 addAntiHintsAndExpire(
MI,
C, WaitStates, Tracking);
615 addWindows(
MI,
C, Tracking);
622 bool sideMatches(
const HazardSide &Side,
const MachineInstr &
MI,
631 for (
unsigned R = 0;
R < Rules.
size(); ++
R) {
632 const HazardAntiHintRule &Rule = Rules[
R];
633 if (!RuleApplies[R] || !sideMatches(Rule.
Producer,
MI,
C))
641 for (
unsigned ConsumerIdx = 0,
E = Rule.
Consumers.size();
642 ConsumerIdx !=
E; ++ConsumerIdx)
643 seedWindow(Rule, Rule.
Consumers[ConsumerIdx],
MI, Regs,
644 Tracking[R][ConsumerIdx]);
648 void seedWindow(
const HazardAntiHintRule &Rule,
const ConsumerTarget &CT,
650 ConsumerTracking &Track) {
651 unsigned Window = resolveWindow(CT,
MI, Ctx);
655 if (Rule.
Life == Lifetime::RegisterCount) {
658 Track.push_back({{}, &
MI, Window, 0});
659 SmallVectorImpl<Register> &Recent = Track.front().Regs;
664 if (Recent.
size() > Window)
674 const HazardAntiHintRule &Rule,
675 const ConsumerTarget &CT, ConsumerTracking &Track) {
679 for (AntiHintWindow &Window : Track) {
682 Window.Producer->all_defs(), [&](
const MachineOperand &MO) {
683 return MO.getReg().isVirtual() &&
684 MI.readsVirtualRegister(MO.getReg());
688 if (ReadsProducerDef) {
689 Window.Elapsed = std::max(
699 for (
unsigned R = 0;
R < Rules.
size(); ++
R) {
700 const HazardAntiHintRule &Rule = Rules[
R];
703 for (
unsigned ConsumerIdx = 0,
E = Rule.
Consumers.size();
704 ConsumerIdx !=
E; ++ConsumerIdx) {
705 const ConsumerTarget &CT = Rule.
Consumers[ConsumerIdx];
706 ConsumerTracking &Track = Tracking[
R][ConsumerIdx];
712 const bool Counts = Rule.
Life == Lifetime::WindowBudget;
714 advanceByRawWindow(
MI,
C, Rule, CT, Track);
716 return Window.Elapsed >= Window.Len;
721 if (sideMatches(CT.
Side,
MI,
C))
722 addAntiHints(CT,
MI, Track);
726 for (AntiHintWindow &Window : Track)
727 Window.Elapsed += WaitStates;
735 return Def &&
Def->
isCopy() &&
Def->getOperand(1).getReg() == HazardReg;
738 void addAntiHints(
const ConsumerTarget &CT,
const MachineInstr &
MI,
739 const ConsumerTracking &Track) {
744 collectOperandRegs(
MI, CT.
Side.
Op, Ctx, ConsumerRegs);
745 if (ConsumerRegs.
empty())
748 auto AntiHint = [&](
Register ProducerReg) {
751 const LiveInterval &ProducerLI = Ctx.
LIS->
getInterval(ProducerReg);
753 if (ProducerLI.
liveAt(Slot))
755 for (
Register ConsumerReg : ConsumerRegs) {
756 if (ConsumerReg == ProducerReg ||
isCopyOf(ConsumerReg, ProducerReg))
760 if (CT.
Direction == AntiHintDirection::Symmetric)
764 << (CT.
Direction == AntiHintDirection::Symmetric ?
" <-> "
767 << Ctx.
TII->getName(
MI.getOpcode()) <<
")\n");
770 for (
const AntiHintWindow &Window : Track) {
771 for (
Register ProducerReg : Window.Regs)
772 AntiHint(ProducerReg);
780 AntiHintEngine(Ctx).run(MF);
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
#define clEnumValN(ENUMVAL, FLAGNAME, DESC)
static cl::opt< unsigned > VAVDSTLookbackWindow("amdgpu-va-vdst-lookback-window", cl::Hidden, cl::desc("Lookback window for VA_VDST anti-hints."), cl::init(32))
static cl::opt< bool > EnableAntiHintsForAddr("amdgpu-anti-hints-for-addr", cl::Hidden, cl::desc("Enable Anti-Hints for memory address " "operands and subsequent VGPR writes to " "avoid wait xcnt."), cl::init(true))
static cl::list< AntiHintRule > AntiHintRuleSelection("amdgpu-anti-hints-rules", cl::Hidden, cl::CommaSeparated, cl::desc("Anti-hints rules to select."), cl::values(clEnumValN(AntiHintRule::None, "none", "Select no rules"), clEnumValN(AntiHintRule::MFMAWAW, "mfma-waw", "MFMA destination write-after-write"), clEnumValN(AntiHintRule::MFMAWAR, "mfma-war", "XDL MFMA src2 write-after-read"), clEnumValN(AntiHintRule::WMMAWARAB, "wmma-war-ab", "XDL WMMA A/B source write-after-read"), clEnumValN(AntiHintRule::SWMMACWARIndex, "swmmac-war-index", "XDL SWMMAC sparse index write-after-read"), clEnumValN(AntiHintRule::WMMAWAW, "wmma-waw", "XDL WMMA destination write-after-write"), clEnumValN(AntiHintRule::TransWAR, "trans-war", "TRANS source write-after-read"), clEnumValN(AntiHintRule::MemAddrWAR, "mem-addr-war", "Memory address/data write-after-read (s_wait_xcnt)"), clEnumValN(AntiHintRule::VAVdstWAR, "va-vdst-war", "VALU source write-after-read by a load " "(s_wait_alu va_vdst)"), clEnumValN(AntiHintRule::All, "all", "Select all rules (default); per-rule " "-amdgpu-anti-hints-for-* still apply")))
static cl::opt< unsigned > AddrAntiHintWindow("amdgpu-addr-anti-hint-window", cl::Hidden, cl::desc("Number of later memory instructions an " "address anti-hint stays open."), cl::init(16))
static cl::opt< bool > EnableAntiHintsForVAVdst("amdgpu-anti-hints-for-va-vdst", cl::Hidden, cl::desc("Enable Anti-Hints for VA-VDST."), cl::init(false))
AMD GCN specific subclass of TargetSubtarget.
const HexagonInstrInfo * TII
static Register isCopyOf(const MachineInstr &MI, Register Reg, const TargetInstrInfo &TII)
isFullCopyOf - If MI is a COPY to or from Reg, return the other register, otherwise return 0.
Loop::LoopBounds::Direction Direction
Promote Memory to Register
Interface definition for SIInstrInfo.
Interface definition for SIRegisterInfo.
size_t size() const
Get the array size.
bool hasInterval(Register Reg) const
SlotIndex getInstructionIndex(const MachineInstr &Instr) const
Returns the base index of the given instruction.
LiveInterval & getInterval(Register Reg)
bool liveAt(SlotIndex index) const
Representation of each machine instruction.
MachineOperand class - Representation of each machine instruction operand.
bool isReg() const
isReg - Tests if this is a MO_Register operand.
Register getReg() const
getReg - Returns the register number.
const TargetRegisterClass * getRegClass(Register Reg) const
Return the register class of the specified virtual register.
void addRegAllocationAntiHints(Register VReg, ArrayRef< Register > AntiHintVRegs)
Add multiple anti-hints at once.
LLVM_ABI LLVM_READONLY MachineInstr * getUniqueVRegDef(Register Reg) const
getUniqueVRegDef - Return the unique machine instr that defines the specified virtual register or nul...
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
static bool isDS(const MachineInstr &MI)
static bool isVMEM(const MachineInstr &MI)
bool isXDLWMMA(const MachineInstr &MI) const
static bool isDGEMM(unsigned Opcode)
static bool isSALU(const MachineInstr &MI)
static bool isSWMMAC(const MachineInstr &MI)
bool isXDL(const MachineInstr &MI) const
static bool isVALU(const MachineInstr &MI, bool AllowLDSDMA)
static bool isTRANS(const MachineInstr &MI)
static unsigned getNumWaitStates(const MachineInstr &MI)
Return the number of wait states that result from executing this instruction.
static bool hasVGPRs(const TargetRegisterClass *RC)
SlotIndex getRegSlot(bool EC=false) const
Returns the register use/def slot in the current instruction for a normal or early-clobber def.
reference emplace_back(ArgTypes &&... Args)
iterator erase(const_iterator CI)
void push_back(const T &Elt)
bool(*)(const HazardContext &) RulePredicate
void applyAntiHintRules(MachineFunction &MF, const HazardContext &Ctx)
unsigned(*)(const MachineInstr &Producer, HazardClassMask ReaderClass, const HazardContext &Ctx) AdvanceForRawWindowFn
bool(*)(const MachineInstr &, const HazardContext &) InstPredicate
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
Predicate
Predicate - These are "(BI << 5) | BO" for various predicates.
ValuesClass values(OptsTy... Options)
Helper to build a ValuesClass by forwarding a variable number of arguments as an initializer list to ...
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
This is an optimization pass for GlobalISel generic memory operations.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
MutableArrayRef(T &OneElt) -> MutableArrayRef< T >
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
LLVM_ABI Printable printReg(Register Reg, const TargetRegisterInfo *TRI=nullptr, unsigned SubIdx=0, const MachineRegisterInfo *MRI=nullptr)
Prints virtual and physical registers with or without a TRI instance.
bool matches(HazardClassMask Mask) const
HazardClassMask CounterMask
AntiHintDirection Direction
SmallVector< ConsumerTarget, 3 > Consumers
AdvanceForRawWindowFn AdvanceForRawWindow
const SIRegisterInfo * TRI
MachineRegisterInfo * MRI
const TargetSchedModel * SchedModel
unsigned(* Fn)(const MachineInstr &Producer, HazardClassMask ConsumerClass, const HazardContext &Ctx)
const cl::opt< unsigned > * OptWindowLength