blob: 25ab84cd86e243a8d6ec490f051a1e802d037724 [file] [edit]
//===-- GCNPreRAAntiHints.cpp - MFMA register anti-hints ------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// \file
/// Insert register allocation anti-hints.
///
//===----------------------------------------------------------------------===//
#include "GCNPreRAAntiHints.h"
#include "GCNSubtarget.h"
#include "SIInstrInfo.h"
#include "SIRegisterInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SlotIndexes.h"
#include "llvm/CodeGen/TargetSchedule.h"
using namespace llvm;
using namespace llvm::AMDGPU;
#define DEBUG_TYPE "amdgpu-anti-hints"
namespace HC = llvm::AMDGPU::HazardClass;
enum class AntiHintRule {
None,
MFMAWAW,
MFMAWAR,
WMMAWARAB,
SWMMACWARIndex,
WMMAWAW,
TransWAR,
MemAddrWAR,
VAVdstWAR,
All,
};
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<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::opt<bool>
EnableAntiHintsForVAVdst("amdgpu-anti-hints-for-va-vdst", cl::Hidden,
cl::desc("Enable Anti-Hints for VA-VDST."),
cl::init(false));
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<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));
namespace {
// Classify the MI into a HazardClassMask.
HazardClassMask getInstHazardClass(const MachineInstr &MI,
const HazardContext &Ctx) {
const SIInstrInfo &TII = *Ctx.TII;
HazardClassMask Mask = HC::None;
if (TII.isLDSDMA(MI))
Mask = HC::VALU | HC::VMEM | HC::DS | HC::LDSDMA;
else if (TII.isWMMA(MI) || SIInstrInfo::isSWMMAC(MI))
Mask = HC::WMMA;
else if (TII.isMFMA(MI))
Mask = HC::MFMA;
else if (SIInstrInfo::isTRANS(MI))
Mask = HC::TRANS;
else if (SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true))
Mask = HC::VALU;
else if (TII.isDS(MI))
Mask = HC::DS;
else if (TII.isVMEM(MI))
Mask = HC::VMEM;
else if (TII.isSMRD(MI))
Mask = HC::SMEM;
else if (TII.isEXP(MI))
Mask = HC::EXP;
else if (SIInstrInfo::isSALU(MI))
Mask = HC::SALU;
else if (MI.isCopy() && MI.getOperand(0).getReg().isVirtual() &&
Ctx.TRI->hasVGPRs(Ctx.MRI->getRegClass(MI.getOperand(0).getReg())))
Mask = HC::VALU;
if (SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true))
Mask |= HC::RawVALU;
if (MI.mayLoad())
Mask |= HC::Load;
for (const MachineOperand &MO : MI.defs()) {
if (MO.isReg() && MO.getReg().isVirtual() &&
Ctx.TRI->hasVGPRs(Ctx.MRI->getRegClass(MO.getReg()))) {
Mask |= HC::WritesVGPR;
break;
}
}
return Mask;
}
bool isVirtualVGPR(const HazardContext &Ctx, const MachineOperand &MO) {
if (!MO.isReg() || !MO.getReg().isVirtual())
return false;
return Ctx.TRI->hasVGPRs(Ctx.MRI->getRegClass(MO.getReg()));
}
void collectOperandRegs(const MachineInstr &MI, HazardOperand Op,
const HazardContext &Ctx,
SmallVectorImpl<Register> &Out) {
const SIInstrInfo &TII = *Ctx.TII;
switch (Op) {
case HazardOperand::None:
break;
case HazardOperand::Def:
for (const MachineOperand &MO : MI.all_defs()) {
if (isVirtualVGPR(Ctx, MO))
Out.push_back(MO.getReg());
}
break;
case HazardOperand::Src0:
if (const MachineOperand *MO =
TII.getNamedOperand(MI, AMDGPU::OpName::src0)) {
if (isVirtualVGPR(Ctx, *MO))
Out.push_back(MO->getReg());
}
break;
case HazardOperand::Src1:
if (const MachineOperand *MO =
TII.getNamedOperand(MI, AMDGPU::OpName::src1)) {
if (isVirtualVGPR(Ctx, *MO))
Out.push_back(MO->getReg());
}
break;
case HazardOperand::Src2:
if (const MachineOperand *MO =
TII.getNamedOperand(MI, AMDGPU::OpName::src2)) {
if (isVirtualVGPR(Ctx, *MO))
Out.push_back(MO->getReg());
}
break;
case HazardOperand::Src0Src1:
for (AMDGPU::OpName Name : {AMDGPU::OpName::src0, AMDGPU::OpName::src1}) {
if (const MachineOperand *MO = TII.getNamedOperand(MI, Name)) {
if (isVirtualVGPR(Ctx, *MO))
Out.push_back(MO->getReg());
}
}
break;
case HazardOperand::Idx:
if (const MachineOperand *MO =
TII.getNamedOperand(MI, AMDGPU::OpName::idx)) {
if (isVirtualVGPR(Ctx, *MO))
Out.push_back(MO->getReg());
}
break;
case HazardOperand::Vaddr:
if (const MachineOperand *MO =
TII.getNamedOperand(MI, AMDGPU::OpName::vaddr)) {
if (isVirtualVGPR(Ctx, *MO))
Out.push_back(MO->getReg());
}
break;
case HazardOperand::AnySrc:
for (AMDGPU::OpName Name :
{AMDGPU::OpName::src0, AMDGPU::OpName::src1, AMDGPU::OpName::src2}) {
if (const MachineOperand *MO = TII.getNamedOperand(MI, Name)) {
if (isVirtualVGPR(Ctx, *MO))
Out.push_back(MO->getReg());
}
}
break;
case HazardOperand::AnyUse:
for (const MachineOperand &MO : MI.all_uses()) {
if (isVirtualVGPR(Ctx, MO))
Out.push_back(MO.getReg());
}
break;
}
}
enum class MFMAHazardKind { RAW, WAW, WAR };
// MFMA anti-hint wait-state window, mirroring GCNHazardRecognizer.cpp wait
// states.
unsigned mfmaWaitStates(const MachineInstr &MFMA, MFMAHazardKind Kind,
HazardClassMask ReaderClass, const HazardContext &Ctx) {
const SIInstrInfo &TII = *Ctx.TII;
const GCNSubtarget &ST = *Ctx.ST;
const int NumPasses = Ctx.SchedModel->computeInstrLatency(&MFMA);
const bool IsDGEMM = SIInstrInfo::isDGEMM(MFMA.getOpcode());
const bool Mem = ReaderClass & (HC::VMEM | HC::DS | HC::EXP);
auto GFX940NPass = [&]() -> unsigned {
return TII.isXDL(MFMA)
? NumPasses + 3 + (NumPasses != 2 && ST.hasGFX950Insts())
: NumPasses + 2;
};
auto SMFMANPass = [&]() -> unsigned {
switch (NumPasses) {
case 2:
return 5;
case 8:
return 11;
case 16:
return 19;
default:
return 0;
}
};
switch (Kind) {
case MFMAHazardKind::RAW:
if (IsDGEMM) {
switch (NumPasses) {
case 4:
return Mem ? 9 : 6;
case 8:
case 16:
return Mem ? 18 : (ST.hasGFX950Insts() ? 19 : 11);
default:
return 0;
}
}
return ST.hasGFX940Insts() ? GFX940NPass() : SMFMANPass();
case MFMAHazardKind::WAW:
if (IsDGEMM) {
switch (NumPasses) {
case 4:
return 6;
case 8:
case 16:
return 11;
default:
return 0;
}
}
return ST.hasGFX940Insts() ? GFX940NPass() : SMFMANPass();
case MFMAHazardKind::WAR:
switch (NumPasses) {
case 2:
return 1;
case 4:
return 3;
case 8:
return 7;
case 16:
return 15;
default:
return 15;
}
}
return 0;
}
unsigned mfmaWawWindow(const MachineInstr &P, HazardClassMask,
const HazardContext &Ctx) {
return mfmaWaitStates(P, MFMAHazardKind::WAW, HC::None, Ctx);
}
unsigned mfmaWarWindow(const MachineInstr &P, HazardClassMask,
const HazardContext &Ctx) {
return mfmaWaitStates(P, MFMAHazardKind::WAR, HC::None, Ctx);
}
constexpr unsigned NumWMMAHazardCategories = 7;
unsigned wmmaHazardCategory(const MachineInstr &Producer,
const HazardContext &Ctx) {
const bool IsSWMMAC = SIInstrInfo::isSWMMAC(Producer);
const bool LowestRate = Ctx.ST->hasGFX125xLowestRateWMMA();
switch (Ctx.SchedModel->computeInstrLatency(&Producer)) {
case 4:
return 6;
case 8:
return IsSWMMAC ? 2 : 0;
case 16:
return LowestRate ? 4 : (IsSWMMAC ? 3 : 1);
case 32:
return 5;
default:
return NumWMMAHazardCategories;
}
}
// WMMA/SWMMAC co-exec window.
unsigned wmmaCoexecWindow(const MachineInstr &Producer,
HazardClassMask ConsumerClass,
const HazardContext &Ctx) {
const unsigned Category = wmmaHazardCategory(Producer, Ctx);
if (Category >= NumWMMAHazardCategories)
return 0;
static constexpr unsigned WMMAWaitStates[] = {5, 9, 3, 5, 9, 17, 2};
static constexpr unsigned VALUWaitStates[] = {4, 8, 2, 4, 8, 16, 1};
return (ConsumerClass & HC::WMMA) ? WMMAWaitStates[Category]
: VALUWaitStates[Category];
}
unsigned mfmaReaderRawWindow(const MachineInstr &Producer,
HazardClassMask ReaderClass,
const HazardContext &Ctx) {
return mfmaWaitStates(Producer, MFMAHazardKind::RAW, ReaderClass, Ctx);
}
bool hasMFMAHazard(const HazardContext &Ctx) {
return Ctx.ST->hasGFX90AInsts();
}
bool hasWMMACoexecHazard(const HazardContext &Ctx) {
return Ctx.ST->hasWMMACoexecutionHazards();
}
bool hasTransCoexecHazard(const HazardContext &Ctx) {
return Ctx.ST->hasTransCoexecutionHazard();
}
bool hasGFX1250Insts(const HazardContext &Ctx) {
return Ctx.ST->hasGFX1250Insts();
}
bool ruleSelected(AntiHintRule Rule) {
// No -amdgpu-anti-hints-rules on the command line selects every rule.
if (AntiHintRuleSelection.empty() ||
is_contained(AntiHintRuleSelection, AntiHintRule::All))
return true;
return is_contained(AntiHintRuleSelection, Rule);
}
bool isMFMAWAWRuleEnabled(const HazardContext &Ctx) {
return hasMFMAHazard(Ctx) && ruleSelected(AntiHintRule::MFMAWAW);
}
bool isMFMAWARRuleEnabled(const HazardContext &Ctx) {
return hasMFMAHazard(Ctx) && ruleSelected(AntiHintRule::MFMAWAR);
}
bool isWMMAWARABRuleEnabled(const HazardContext &Ctx) {
return hasWMMACoexecHazard(Ctx) && ruleSelected(AntiHintRule::WMMAWARAB);
}
bool isSWMMACWARIndexRuleEnabled(const HazardContext &Ctx) {
return hasWMMACoexecHazard(Ctx) && ruleSelected(AntiHintRule::SWMMACWARIndex);
}
bool isWMMAWAWRuleEnabled(const HazardContext &Ctx) {
return hasWMMACoexecHazard(Ctx) && ruleSelected(AntiHintRule::WMMAWAW);
}
bool isTransWARRuleEnabled(const HazardContext &Ctx) {
return hasTransCoexecHazard(Ctx) && ruleSelected(AntiHintRule::TransWAR);
}
bool isAddrWARRuleEnabled(const HazardContext &Ctx) {
return EnableAntiHintsForAddr && hasGFX1250Insts(Ctx) &&
ruleSelected(AntiHintRule::MemAddrWAR);
}
bool isVAVdstWARRuleEnabled(const HazardContext &Ctx) {
return EnableAntiHintsForVAVdst && hasGFX1250Insts(Ctx) &&
ruleSelected(AntiHintRule::VAVdstWAR);
}
bool isXDLMFMA(const MachineInstr &MI, const HazardContext &Ctx) {
return Ctx.TII->isXDL(MI);
}
bool isXDLWMMA(const MachineInstr &MI, const HazardContext &Ctx) {
return Ctx.TII->isXDLWMMA(MI);
}
bool isXDLSWMMAC(const MachineInstr &MI, const HazardContext &Ctx) {
return Ctx.TII->isXDLWMMA(MI) && SIInstrInfo::isSWMMAC(MI);
}
bool isDSorVMEMLoad(const MachineInstr &MI, const HazardContext &Ctx) {
return MI.mayLoad() && (Ctx.TII->isDS(MI) || Ctx.TII->isVMEM(MI));
}
unsigned resolveWindow(const ConsumerTarget &CT, const MachineInstr &MI,
const HazardContext &Ctx) {
// Explicity given window length overrides the computed one.
if (CT.Window.OptWindowLength)
return *CT.Window.OptWindowLength;
if (CT.Window.Fn)
return CT.Window.Fn(MI, CT.Side.Match.AnyOf, Ctx);
return CT.Window.WindowLength;
}
// Build the anti-hints rules.
class HazardRuleSet {
SmallVector<HazardAntiHintRule, 0> Rules;
public:
class RuleBuilder {
HazardRuleSet &S;
unsigned Idx;
HazardAntiHintRule &rule() const { return S.Rules[Idx]; }
public:
RuleBuilder(HazardRuleSet &S, unsigned Idx) : S(S), Idx(Idx) {}
RuleBuilder &producer(ClassMatch M, HazardOperand Op,
InstPredicate Predicate = nullptr) {
rule().Producer = {M, Op, Predicate};
return *this;
}
RuleBuilder &rawCredit(AdvanceForRawWindowFn AdvanceForRawWindow) {
rule().AdvanceForRawWindow = AdvanceForRawWindow;
return *this;
}
RuleBuilder &
consumer(ClassMatch M, HazardOperand Op, WindowSpec Window,
HazardClassMask CountMask = 0,
AntiHintDirection Direction = AntiHintDirection::OneDirectional,
InstPredicate Predicate = nullptr) {
rule().Consumers.push_back(
{{M, Op, Predicate}, Window, CountMask, Direction});
return *this;
}
RuleBuilder &enabledIf(RulePredicate Predicate) {
rule().Predicate = Predicate;
return *this;
}
RuleBuilder &lifetime(Lifetime Life) {
rule().Life = Life;
return *this;
}
};
RuleBuilder addRule() {
Rules.emplace_back();
return RuleBuilder(*this, Rules.size() - 1);
}
SmallVector<HazardAntiHintRule, 0> buildRules() { return std::move(Rules); }
};
// Here the anti-hints rules are inserted.
SmallVector<HazardAntiHintRule, 0> buildAntiHintsRules() {
using HO = HazardOperand;
HazardRuleSet S;
// MFMA relevent windows and consumers
const WindowSpec MfmaWawWindow{0, nullptr, mfmaWawWindow};
const WindowSpec MfmaWarWindow{0, nullptr, mfmaWarWindow};
const ClassMatch MfmaConsumers = {HC::DS | HC::VALU | HC::VMEM | HC::TRANS |
HC::EXP};
// WMMA relevent windows and consumers
const WindowSpec WmmaCoexecWindow{0, nullptr, wmmaCoexecWindow};
const ClassMatch WmmaCoexecConsumers = {/*AnyOf=*/HC::VALU | HC::TRANS,
/*AllOf=*/HC::None,
/*NoneOf=*/HC::LDSDMA};
const HazardClassMask CoexecCounters = HC::VALU | HC::TRANS | HC::WMMA;
// MFMA WAW rules
S.addRule()
.enabledIf(isMFMAWAWRuleEnabled)
.producer({HC::MFMA}, HO::Def)
.rawCredit(mfmaReaderRawWindow)
.consumer(MfmaConsumers, HO::Def, MfmaWawWindow, HC::None,
AntiHintDirection::OneDirectional);
// MFMA WAR rules
S.addRule()
.enabledIf(isMFMAWARRuleEnabled)
.producer({HC::MFMA}, HO::Src2, isXDLMFMA)
.rawCredit(mfmaReaderRawWindow)
.consumer(MfmaConsumers, HO::Def, MfmaWarWindow, HC::None,
AntiHintDirection::OneDirectional);
// WMMA WAR.
S.addRule()
.enabledIf(isWMMAWARABRuleEnabled)
.producer({HC::WMMA}, HO::Src0Src1, isXDLWMMA)
.consumer(WmmaCoexecConsumers, HO::Def, WmmaCoexecWindow, CoexecCounters,
AntiHintDirection::OneDirectional)
.consumer({HC::VMEM | HC::DS}, HO::Def, WmmaCoexecWindow, CoexecCounters,
AntiHintDirection::OneDirectional);
// SWMMAC WAR.
S.addRule()
.enabledIf(isSWMMACWARIndexRuleEnabled)
.producer({HC::WMMA}, HO::Src2, isXDLSWMMAC)
.consumer(WmmaCoexecConsumers, HO::Def, WmmaCoexecWindow, CoexecCounters,
AntiHintDirection::OneDirectional);
// WMMA WAW.
S.addRule()
.enabledIf(isWMMAWAWRuleEnabled)
.producer({HC::WMMA}, HO::Def, isXDLWMMA)
.consumer(WmmaCoexecConsumers, HO::Def, WmmaCoexecWindow, CoexecCounters,
AntiHintDirection::OneDirectional);
// TRANS WAR.
S.addRule()
.enabledIf(isTransWARRuleEnabled)
.producer({HC::TRANS}, HO::AnyUse)
.consumer({HC::VALU | HC::WMMA}, HO::Def, {/*WindowLength=*/1},
CoexecCounters, AntiHintDirection::OneDirectional);
// Address WAR.
S.addRule()
.enabledIf(isAddrWARRuleEnabled)
.producer({HC::VMEM}, HO::AnyUse)
.consumer({HC::WritesVGPR}, HO::Def, {0, &AddrAntiHintWindow}, HC::VMEM,
AntiHintDirection::Symmetric);
// VA_VDST WAR.
S.addRule()
.enabledIf(isVAVdstWARRuleEnabled)
.lifetime(Lifetime::RegisterCount)
.producer({HC::RawVALU}, HO::AnyUse)
.consumer({HC::Load}, HO::Def, {0, &VAVDSTLookbackWindow}, HC::None,
AntiHintDirection::Symmetric, isDSorVMEMLoad);
return S.buildRules();
}
ArrayRef<HazardAntiHintRule> getAntiHintsRules() {
static const SmallVector<HazardAntiHintRule, 0> Rules = buildAntiHintsRules();
return Rules;
}
struct AntiHintWindow {
SmallVector<Register, 4> Regs;
const MachineInstr *Producer = nullptr;
unsigned Len = 0;
unsigned Elapsed = 0;
};
using ConsumerTracking = SmallVector<AntiHintWindow, 3>;
// One per consumer target of a rule.
using RuleTracking = SmallVector<ConsumerTracking, 3>;
class AntiHintEngine {
const HazardContext &Ctx;
ArrayRef<HazardAntiHintRule> Rules;
SmallVector<bool, 8> RuleApplies;
bool AnyEnabled = false;
public:
AntiHintEngine(const HazardContext &Ctx)
: Ctx(Ctx), Rules(getAntiHintsRules()), RuleApplies(Rules.size()) {
for (unsigned R = 0; R < Rules.size(); ++R) {
const HazardAntiHintRule &Rule = Rules[R];
RuleApplies[R] = !Rule.Predicate || Rule.Predicate(Ctx);
AnyEnabled |= RuleApplies[R];
}
}
void run(MachineFunction &MF) {
if (!AnyEnabled)
return;
SmallVector<RuleTracking, 8> Tracking(Rules.size());
for (unsigned R = 0; R < Rules.size(); ++R)
Tracking[R].resize(Rules[R].Consumers.size());
for (const MachineBasicBlock &MBB : MF) {
for (RuleTracking &RT : Tracking) {
for (ConsumerTracking &Track : RT)
Track.clear();
}
for (const MachineInstr &MI : MBB) {
if (MI.isMetaInstruction())
continue;
const HazardClassMask C = getInstHazardClass(MI, Ctx);
// Wait states this instruction contributes to an open window.
unsigned WaitStates = SIInstrInfo::getNumWaitStates(MI);
addAntiHintsAndExpire(MI, C, WaitStates, Tracking);
addWindows(MI, C, Tracking);
}
}
}
private:
// Check if the right class and predicate matches.
bool sideMatches(const HazardSide &Side, const MachineInstr &MI,
HazardClassMask C) {
return Side.Match.matches(C) &&
(!Side.Predicate || Side.Predicate(MI, Ctx));
}
// Producer phase: open a window for each consumer target.
void addWindows(const MachineInstr &MI, HazardClassMask C,
MutableArrayRef<RuleTracking> Tracking) {
for (unsigned R = 0; R < Rules.size(); ++R) {
const HazardAntiHintRule &Rule = Rules[R];
if (!RuleApplies[R] || !sideMatches(Rule.Producer, MI, C))
continue;
SmallVector<Register, 4> Regs;
// Collect the producer regs.
collectOperandRegs(MI, Rule.Producer.Op, Ctx, Regs);
if (Regs.empty())
continue;
for (unsigned ConsumerIdx = 0, E = Rule.Consumers.size();
ConsumerIdx != E; ++ConsumerIdx)
seedWindow(Rule, Rule.Consumers[ConsumerIdx], MI, Regs,
Tracking[R][ConsumerIdx]);
}
}
void seedWindow(const HazardAntiHintRule &Rule, const ConsumerTarget &CT,
const MachineInstr &MI, ArrayRef<Register> Regs,
ConsumerTracking &Track) {
unsigned Window = resolveWindow(CT, MI, Ctx);
if (!Window)
return;
if (Rule.Life == Lifetime::RegisterCount) {
// Window length caps the register count.
if (Track.empty())
Track.push_back({{}, &MI, Window, 0});
SmallVectorImpl<Register> &Recent = Track.front().Regs;
for (Register Reg : Regs) {
if (llvm::is_contained(Recent, Reg))
continue;
Recent.push_back(Reg);
if (Recent.size() > Window)
Recent.erase(Recent.begin());
}
return;
}
Track.push_back({SmallVector<Register, 4>(Regs), &MI, Window, 0});
}
void advanceByRawWindow(const MachineInstr &MI, HazardClassMask C,
const HazardAntiHintRule &Rule,
const ConsumerTarget &CT, ConsumerTracking &Track) {
if (!Rule.AdvanceForRawWindow || !CT.Side.Match.matches(C))
return;
for (AntiHintWindow &Window : Track) {
// Determine if def generated by producer is read by the MI consumer.
bool ReadsProducerDef = llvm::any_of(
Window.Producer->all_defs(), [&](const MachineOperand &MO) {
return MO.getReg().isVirtual() &&
MI.readsVirtualRegister(MO.getReg());
});
// Advance by RAW window from the producer if that is larger than current
// Window.Elapsed.
if (ReadsProducerDef) {
Window.Elapsed = std::max(
Window.Elapsed, Rule.AdvanceForRawWindow(*Window.Producer, C, Ctx));
}
}
}
// Consumer phase: add anti-hints, then charge and expire open windows.
void addAntiHintsAndExpire(const MachineInstr &MI, HazardClassMask C,
unsigned WaitStates,
MutableArrayRef<RuleTracking> Tracking) {
for (unsigned R = 0; R < Rules.size(); ++R) {
const HazardAntiHintRule &Rule = Rules[R];
if (!RuleApplies[R])
continue;
for (unsigned ConsumerIdx = 0, E = Rule.Consumers.size();
ConsumerIdx != E; ++ConsumerIdx) {
const ConsumerTarget &CT = Rule.Consumers[ConsumerIdx];
ConsumerTracking &Track = Tracking[R][ConsumerIdx];
if (Track.empty())
continue;
// Before adding the anti-hints, see if advancing by RAW window will
// help remove the window.
const bool Counts = Rule.Life == Lifetime::WindowBudget;
if (Counts) {
advanceByRawWindow(MI, C, Rule, CT, Track);
llvm::erase_if(Track, [](const AntiHintWindow &Window) {
return Window.Elapsed >= Window.Len;
});
}
// Add anti-hints if the consumer matches the instruction.
if (sideMatches(CT.Side, MI, C))
addAntiHints(CT, MI, Track);
// This instruction's own wait states count toward the next one.
if (Counts && (!CT.CounterMask || (C & CT.CounterMask))) {
for (AntiHintWindow &Window : Track)
Window.Elapsed += WaitStates;
}
}
}
}
bool isCopyOf(Register Cand, Register HazardReg) const {
const MachineInstr *Def = Ctx.MRI->getUniqueVRegDef(Cand);
return Def && Def->isCopy() && Def->getOperand(1).getReg() == HazardReg;
}
void addAntiHints(const ConsumerTarget &CT, const MachineInstr &MI,
const ConsumerTracking &Track) {
if (Track.empty())
return;
SmallVector<Register, 4> ConsumerRegs;
// Collect the consumer regs.
collectOperandRegs(MI, CT.Side.Op, Ctx, ConsumerRegs);
if (ConsumerRegs.empty())
return;
SlotIndex Slot = Ctx.LIS->getInstructionIndex(MI).getRegSlot();
auto AntiHint = [&](Register ProducerReg) {
if (!Ctx.LIS->hasInterval(ProducerReg))
return;
const LiveInterval &ProducerLI = Ctx.LIS->getInterval(ProducerReg);
// Skip a live producer reg.
if (ProducerLI.liveAt(Slot))
return;
for (Register ConsumerReg : ConsumerRegs) {
if (ConsumerReg == ProducerReg || isCopyOf(ConsumerReg, ProducerReg))
continue;
Ctx.MRI->addRegAllocationAntiHints(ConsumerReg, ProducerReg);
if (CT.Direction == AntiHintDirection::Symmetric)
Ctx.MRI->addRegAllocationAntiHints(ProducerReg, ConsumerReg);
LLVM_DEBUG(
dbgs() << "anti-hint: keep " << printReg(ProducerReg, Ctx.TRI)
<< (CT.Direction == AntiHintDirection::Symmetric ? " <-> "
: " <- ")
<< printReg(ConsumerReg, Ctx.TRI) << " (consumer "
<< Ctx.TII->getName(MI.getOpcode()) << ")\n");
}
};
for (const AntiHintWindow &Window : Track) {
for (Register ProducerReg : Window.Regs)
AntiHint(ProducerReg);
}
}
};
} // namespace
void AMDGPU::applyAntiHintRules(MachineFunction &MF, const HazardContext &Ctx) {
AntiHintEngine(Ctx).run(MF);
}