#include "RegAllocEvictionAdvisor.h"
#include "AllocationOrder.h"
#include "RegAllocGreedy.h"
#include "llvm/CodeGen/LiveRegMatrix.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/RegisterClassInfo.h"
#include "llvm/CodeGen/VirtRegMap.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
static cl::opt<RegAllocEvictionAdvisorAnalysis::AdvisorMode> Mode(
"regalloc-enable-advisor", cl::Hidden,
cl::init(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Default),
cl::desc("Enable regalloc advisor mode"),
cl::values(
clEnumValN(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Default,
"default", "Default"),
clEnumValN(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Release,
"release", "precompiled"),
clEnumValN(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Development,
"development", "for training")));
static cl::opt<bool> EnableLocalReassignment(
"enable-local-reassign", cl::Hidden,
cl::desc("Local reassignment can yield better allocation decisions, but "
"may be compile time intensive"),
cl::init(false));
cl::opt<unsigned> EvictInterferenceCutoff(
"regalloc-eviction-max-interference-cutoff", cl::Hidden,
cl::desc("Number of interferences after which we declare "
"an interference unevictable and bail out. This "
"is a compilation cost-saving consideration. To "
"disable, pass a very large number."),
cl::init(10));
#define DEBUG_TYPE "regalloc"
#ifdef LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL
#define LLVM_HAVE_TF_AOT
#endif
char RegAllocEvictionAdvisorAnalysis::ID = 0;
INITIALIZE_PASS(RegAllocEvictionAdvisorAnalysis, "regalloc-evict",
"Regalloc eviction policy", false, true)
namespace {
class DefaultEvictionAdvisorAnalysis final
: public RegAllocEvictionAdvisorAnalysis {
public:
DefaultEvictionAdvisorAnalysis(bool NotAsRequested)
: RegAllocEvictionAdvisorAnalysis(AdvisorMode::Default),
NotAsRequested(NotAsRequested) {}
static bool classof(const RegAllocEvictionAdvisorAnalysis *R) {
return R->getAdvisorMode() == AdvisorMode::Default;
}
private:
std::unique_ptr<RegAllocEvictionAdvisor>
getAdvisor(const MachineFunction &MF, const RAGreedy &RA) override {
return std::make_unique<DefaultEvictionAdvisor>(MF, RA);
}
bool doInitialization(Module &M) override {
if (NotAsRequested)
M.getContext().emitError("Requested regalloc eviction advisor analysis "
"could be created. Using default");
return RegAllocEvictionAdvisorAnalysis::doInitialization(M);
}
const bool NotAsRequested;
};
}
template <> Pass *llvm::callDefaultCtor<RegAllocEvictionAdvisorAnalysis>() {
Pass *Ret = nullptr;
switch (Mode) {
case RegAllocEvictionAdvisorAnalysis::AdvisorMode::Default:
Ret = new DefaultEvictionAdvisorAnalysis( false);
break;
case RegAllocEvictionAdvisorAnalysis::AdvisorMode::Development:
#if defined(LLVM_HAVE_TF_API)
Ret = createDevelopmentModeAdvisor();
#endif
break;
case RegAllocEvictionAdvisorAnalysis::AdvisorMode::Release:
#if defined(LLVM_HAVE_TF_AOT)
Ret = createReleaseModeAdvisor();
#endif
break;
}
if (Ret)
return Ret;
return new DefaultEvictionAdvisorAnalysis( true);
}
StringRef RegAllocEvictionAdvisorAnalysis::getPassName() const {
switch (getAdvisorMode()) {
case AdvisorMode::Default:
return "Default Regalloc Eviction Advisor";
case AdvisorMode::Release:
return "Release mode Regalloc Eviction Advisor";
case AdvisorMode::Development:
return "Development mode Regalloc Eviction Advisor";
}
llvm_unreachable("Unknown advisor kind");
}
RegAllocEvictionAdvisor::RegAllocEvictionAdvisor(const MachineFunction &MF,
const RAGreedy &RA)
: MF(MF), RA(RA), Matrix(RA.getInterferenceMatrix()),
LIS(RA.getLiveIntervals()), VRM(RA.getVirtRegMap()),
MRI(&VRM->getRegInfo()), TRI(MF.getSubtarget().getRegisterInfo()),
RegClassInfo(RA.getRegClassInfo()), RegCosts(TRI->getRegisterCosts(MF)),
EnableLocalReassign(EnableLocalReassignment ||
MF.getSubtarget().enableRALocalReassignment(
MF.getTarget().getOptLevel())) {}
bool DefaultEvictionAdvisor::shouldEvict(const LiveInterval &A, bool IsHint,
const LiveInterval &B,
bool BreaksHint) const {
bool CanSplit = RA.getExtraInfo().getStage(B) < RS_Spill;
if (CanSplit && IsHint && !BreaksHint)
return true;
if (A.weight() > B.weight()) {
LLVM_DEBUG(dbgs() << "should evict: " << B << " w= " << B.weight() << '\n');
return true;
}
return false;
}
bool DefaultEvictionAdvisor::canEvictHintInterference(
const LiveInterval &VirtReg, MCRegister PhysReg,
const SmallVirtRegSet &FixedRegisters) const {
EvictionCost MaxCost;
MaxCost.setBrokenHints(1);
return canEvictInterferenceBasedOnCost(VirtReg, PhysReg, true, MaxCost,
FixedRegisters);
}
bool DefaultEvictionAdvisor::canEvictInterferenceBasedOnCost(
const LiveInterval &VirtReg, MCRegister PhysReg, bool IsHint,
EvictionCost &MaxCost, const SmallVirtRegSet &FixedRegisters) const {
if (Matrix->checkInterference(VirtReg, PhysReg) > LiveRegMatrix::IK_VirtReg)
return false;
bool IsLocal = VirtReg.empty() || LIS->intervalIsInOneMBB(VirtReg);
unsigned Cascade = RA.getExtraInfo().getCascadeOrCurrentNext(VirtReg.reg());
EvictionCost Cost;
for (MCRegUnitIterator Units(PhysReg, TRI); Units.isValid(); ++Units) {
LiveIntervalUnion::Query &Q = Matrix->query(VirtReg, *Units);
const auto &Interferences = Q.interferingVRegs(EvictInterferenceCutoff);
if (Interferences.size() >= EvictInterferenceCutoff)
return false;
for (const LiveInterval *Intf : reverse(Interferences)) {
assert(Register::isVirtualRegister(Intf->reg()) &&
"Only expecting virtual register interference from query");
if (FixedRegisters.count(Intf->reg()))
return false;
if (RA.getExtraInfo().getStage(*Intf) == RS_Done)
return false;
bool Urgent =
!VirtReg.isSpillable() &&
(Intf->isSpillable() ||
RegClassInfo.getNumAllocatableRegs(MRI->getRegClass(VirtReg.reg())) <
RegClassInfo.getNumAllocatableRegs(
MRI->getRegClass(Intf->reg())));
unsigned IntfCascade = RA.getExtraInfo().getCascade(Intf->reg());
if (Cascade == IntfCascade)
return false;
if (Cascade < IntfCascade) {
if (!Urgent)
return false;
Cost.BrokenHints += 10;
}
bool BreaksHint = VRM->hasPreferredPhys(Intf->reg());
Cost.BrokenHints += BreaksHint;
Cost.MaxWeight = std::max(Cost.MaxWeight, Intf->weight());
if (!(Cost < MaxCost))
return false;
if (Urgent)
continue;
if (!shouldEvict(VirtReg, IsHint, *Intf, BreaksHint))
return false;
if (!MaxCost.isMax() && IsLocal && LIS->intervalIsInOneMBB(*Intf) &&
(!EnableLocalReassign || !canReassign(*Intf, PhysReg))) {
return false;
}
}
}
MaxCost = Cost;
return true;
}
MCRegister DefaultEvictionAdvisor::tryFindEvictionCandidate(
const LiveInterval &VirtReg, const AllocationOrder &Order,
uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const {
EvictionCost BestCost;
BestCost.setMax();
MCRegister BestPhys;
auto MaybeOrderLimit = getOrderLimit(VirtReg, Order, CostPerUseLimit);
if (!MaybeOrderLimit)
return MCRegister::NoRegister;
unsigned OrderLimit = *MaybeOrderLimit;
if (CostPerUseLimit < uint8_t(~0u)) {
BestCost.BrokenHints = 0;
BestCost.MaxWeight = VirtReg.weight();
}
for (auto I = Order.begin(), E = Order.getOrderLimitEnd(OrderLimit); I != E;
++I) {
MCRegister PhysReg = *I;
assert(PhysReg);
if (!canAllocatePhysReg(CostPerUseLimit, PhysReg) ||
!canEvictInterferenceBasedOnCost(VirtReg, PhysReg, false, BestCost,
FixedRegisters))
continue;
BestPhys = PhysReg;
if (I.isHint())
break;
}
return BestPhys;
}