blob: 64f1bd5cc5da487f6cdf334d5e8f4de6b846515a [file] [log] [blame]
//===- AMDGPUSetWavePriority.cpp - Set wave priority ----------------------===//
//
// 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
/// Pass to temporarily raise the wave priority beginning the start of
/// the shader function until its last VMEM instructions to allow younger
/// waves to issue their VMEM instructions as well.
//
//===----------------------------------------------------------------------===//
#include "AMDGPU.h"
#include "GCNSubtarget.h"
#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "SIInstrInfo.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/Allocator.h"
using namespace llvm;
#define DEBUG_TYPE "amdgpu-set-wave-priority"
static cl::opt<unsigned> DefaultVALUInstsThreshold(
"amdgpu-set-wave-priority-valu-insts-threshold",
cl::desc("VALU instruction count threshold for adjusting wave priority"),
cl::init(100), cl::Hidden);
namespace {
struct MBBInfo {
MBBInfo() = default;
unsigned NumVALUInstsAtStart = 0;
bool MayReachVMEMLoad = false;
MachineInstr *LastVMEMLoad = nullptr;
};
using MBBInfoSet = DenseMap<const MachineBasicBlock *, MBBInfo>;
class AMDGPUSetWavePriority : public MachineFunctionPass {
public:
static char ID;
AMDGPUSetWavePriority() : MachineFunctionPass(ID) {}
StringRef getPassName() const override { return "Set wave priority"; }
bool runOnMachineFunction(MachineFunction &MF) override;
private:
MachineInstr *BuildSetprioMI(MachineBasicBlock &MBB,
MachineBasicBlock::iterator I,
unsigned priority) const;
const SIInstrInfo *TII;
};
} // End anonymous namespace.
INITIALIZE_PASS(AMDGPUSetWavePriority, DEBUG_TYPE, "Set wave priority", false,
false)
char AMDGPUSetWavePriority::ID = 0;
FunctionPass *llvm::createAMDGPUSetWavePriorityPass() {
return new AMDGPUSetWavePriority();
}
MachineInstr *
AMDGPUSetWavePriority::BuildSetprioMI(MachineBasicBlock &MBB,
MachineBasicBlock::iterator I,
unsigned priority) const {
return BuildMI(MBB, I, DebugLoc(), TII->get(AMDGPU::S_SETPRIO))
.addImm(priority);
}
// Checks that for every predecessor Pred that can reach a VMEM load,
// none of Pred's successors can reach a VMEM load.
static bool CanLowerPriorityDirectlyInPredecessors(const MachineBasicBlock &MBB,
MBBInfoSet &MBBInfos) {
for (const MachineBasicBlock *Pred : MBB.predecessors()) {
if (!MBBInfos[Pred].MayReachVMEMLoad)
continue;
for (const MachineBasicBlock *Succ : Pred->successors()) {
if (MBBInfos[Succ].MayReachVMEMLoad)
return false;
}
}
return true;
}
static bool isVMEMLoad(const MachineInstr &MI) {
return SIInstrInfo::isVMEM(MI) && MI.mayLoad();
}
bool AMDGPUSetWavePriority::runOnMachineFunction(MachineFunction &MF) {
const unsigned HighPriority = 3;
const unsigned LowPriority = 0;
Function &F = MF.getFunction();
if (skipFunction(F) || !AMDGPU::isEntryFunctionCC(F.getCallingConv()))
return false;
const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
TII = ST.getInstrInfo();
unsigned VALUInstsThreshold = DefaultVALUInstsThreshold;
Attribute A = F.getFnAttribute("amdgpu-wave-priority-threshold");
if (A.isValid())
A.getValueAsString().getAsInteger(0, VALUInstsThreshold);
// Find VMEM loads that may be executed before long-enough sequences of
// VALU instructions. We currently assume that backedges/loops, branch
// probabilities and other details can be ignored, so we essentially
// determine the largest number of VALU instructions along every
// possible path from the start of the function that may potentially be
// executed provided no backedge is ever taken.
MBBInfoSet MBBInfos;
for (MachineBasicBlock *MBB : post_order(&MF)) {
bool AtStart = true;
unsigned MaxNumVALUInstsInMiddle = 0;
unsigned NumVALUInstsAtEnd = 0;
for (MachineInstr &MI : *MBB) {
if (isVMEMLoad(MI)) {
AtStart = false;
MBBInfo &Info = MBBInfos[MBB];
Info.NumVALUInstsAtStart = 0;
MaxNumVALUInstsInMiddle = 0;
NumVALUInstsAtEnd = 0;
Info.LastVMEMLoad = &MI;
} else if (SIInstrInfo::isDS(MI)) {
AtStart = false;
MaxNumVALUInstsInMiddle =
std::max(MaxNumVALUInstsInMiddle, NumVALUInstsAtEnd);
NumVALUInstsAtEnd = 0;
} else if (SIInstrInfo::isVALU(MI)) {
if (AtStart)
++MBBInfos[MBB].NumVALUInstsAtStart;
++NumVALUInstsAtEnd;
}
}
bool SuccsMayReachVMEMLoad = false;
unsigned NumFollowingVALUInsts = 0;
for (const MachineBasicBlock *Succ : MBB->successors()) {
SuccsMayReachVMEMLoad |= MBBInfos[Succ].MayReachVMEMLoad;
NumFollowingVALUInsts =
std::max(NumFollowingVALUInsts, MBBInfos[Succ].NumVALUInstsAtStart);
}
MBBInfo &Info = MBBInfos[MBB];
if (AtStart)
Info.NumVALUInstsAtStart += NumFollowingVALUInsts;
NumVALUInstsAtEnd += NumFollowingVALUInsts;
unsigned MaxNumVALUInsts =
std::max(MaxNumVALUInstsInMiddle, NumVALUInstsAtEnd);
Info.MayReachVMEMLoad =
SuccsMayReachVMEMLoad ||
(Info.LastVMEMLoad && MaxNumVALUInsts >= VALUInstsThreshold);
}
MachineBasicBlock &Entry = MF.front();
if (!MBBInfos[&Entry].MayReachVMEMLoad)
return false;
// Raise the priority at the beginning of the shader.
MachineBasicBlock::iterator I = Entry.begin(), E = Entry.end();
while (I != E && !SIInstrInfo::isVALU(*I) && !I->isTerminator())
++I;
BuildSetprioMI(Entry, I, HighPriority);
// Lower the priority on edges where control leaves blocks from which
// the VMEM loads are reachable.
SmallSet<MachineBasicBlock *, 16> PriorityLoweringBlocks;
for (MachineBasicBlock &MBB : MF) {
if (MBBInfos[&MBB].MayReachVMEMLoad) {
if (MBB.succ_empty())
PriorityLoweringBlocks.insert(&MBB);
continue;
}
if (CanLowerPriorityDirectlyInPredecessors(MBB, MBBInfos)) {
for (MachineBasicBlock *Pred : MBB.predecessors()) {
if (MBBInfos[Pred].MayReachVMEMLoad)
PriorityLoweringBlocks.insert(Pred);
}
continue;
}
// Where lowering the priority in predecessors is not possible, the
// block receiving control either was not part of a loop in the first
// place or the loop simplification/canonicalization pass should have
// already tried to split the edge and insert a preheader, and if for
// whatever reason it failed to do so, then this leaves us with the
// only option of lowering the priority within the loop.
PriorityLoweringBlocks.insert(&MBB);
}
for (MachineBasicBlock *MBB : PriorityLoweringBlocks) {
BuildSetprioMI(
*MBB,
MBBInfos[MBB].LastVMEMLoad
? std::next(MachineBasicBlock::iterator(MBBInfos[MBB].LastVMEMLoad))
: MBB->begin(),
LowPriority);
}
return true;
}