2021-12-14 14:49:57 +08:00
|
|
|
//===- RegAllocEvictionAdvisor.cpp - eviction advisor ---------------------===//
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// Implementation of the default eviction advisor and of the Analysis pass.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "RegAllocEvictionAdvisor.h"
|
2022-03-15 17:54:19 +08:00
|
|
|
#include "AllocationOrder.h"
|
2022-01-06 01:22:02 +08:00
|
|
|
#include "RegAllocGreedy.h"
|
2022-03-15 17:54:19 +08:00
|
|
|
#include "llvm/CodeGen/LiveRegMatrix.h"
|
2021-12-14 14:49:57 +08:00
|
|
|
#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(
|
2022-01-30 00:59:31 +08:00
|
|
|
"regalloc-enable-advisor", cl::Hidden, cl::ZeroOrMore,
|
2021-12-14 14:49:57 +08:00
|
|
|
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));
|
|
|
|
|
2022-02-02 00:21:30 +08:00
|
|
|
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));
|
|
|
|
|
2021-12-14 14:49:57 +08:00
|
|
|
#define DEBUG_TYPE "regalloc"
|
2022-01-20 13:19:53 +08:00
|
|
|
#ifdef LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL
|
|
|
|
#define LLVM_HAVE_TF_AOT
|
|
|
|
#endif
|
2021-12-14 14:49:57 +08:00
|
|
|
|
|
|
|
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) {}
|
|
|
|
|
|
|
|
// support for isa<> and dyn_cast.
|
|
|
|
static bool classof(const RegAllocEvictionAdvisorAnalysis *R) {
|
|
|
|
return R->getAdvisorMode() == AdvisorMode::Default;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::unique_ptr<RegAllocEvictionAdvisor>
|
2022-02-04 01:10:06 +08:00
|
|
|
getAdvisor(const MachineFunction &MF, const RAGreedy &RA) override {
|
2022-01-06 01:22:02 +08:00
|
|
|
return std::make_unique<DefaultEvictionAdvisor>(MF, RA);
|
2021-12-14 14:49:57 +08:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
template <> Pass *llvm::callDefaultCtor<RegAllocEvictionAdvisorAnalysis>() {
|
|
|
|
Pass *Ret = nullptr;
|
|
|
|
switch (Mode) {
|
|
|
|
case RegAllocEvictionAdvisorAnalysis::AdvisorMode::Default:
|
|
|
|
Ret = new DefaultEvictionAdvisorAnalysis(/*NotAsRequested*/ false);
|
|
|
|
break;
|
|
|
|
case RegAllocEvictionAdvisorAnalysis::AdvisorMode::Development:
|
2021-12-23 04:46:06 +08:00
|
|
|
#if defined(LLVM_HAVE_TF_API)
|
|
|
|
Ret = createDevelopmentModeAdvisor();
|
|
|
|
#endif
|
2021-12-14 14:49:57 +08:00
|
|
|
break;
|
|
|
|
case RegAllocEvictionAdvisorAnalysis::AdvisorMode::Release:
|
2021-12-23 04:46:06 +08:00
|
|
|
#if defined(LLVM_HAVE_TF_AOT)
|
|
|
|
Ret = createReleaseModeAdvisor();
|
|
|
|
#endif
|
2021-12-14 14:49:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (Ret)
|
|
|
|
return Ret;
|
|
|
|
return new DefaultEvictionAdvisorAnalysis(/*NotAsRequested*/ 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");
|
|
|
|
}
|
|
|
|
|
2022-02-04 01:10:06 +08:00
|
|
|
RegAllocEvictionAdvisor::RegAllocEvictionAdvisor(const MachineFunction &MF,
|
2022-01-06 01:22:02 +08:00
|
|
|
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)),
|
2021-12-14 14:49:57 +08:00
|
|
|
EnableLocalReassign(EnableLocalReassignment ||
|
|
|
|
MF.getSubtarget().enableRALocalReassignment(
|
|
|
|
MF.getTarget().getOptLevel())) {}
|
2022-02-01 23:58:00 +08:00
|
|
|
|
|
|
|
/// shouldEvict - determine if A should evict the assigned live range B. The
|
|
|
|
/// eviction policy defined by this function together with the allocation order
|
|
|
|
/// defined by enqueue() decides which registers ultimately end up being split
|
|
|
|
/// and spilled.
|
|
|
|
///
|
|
|
|
/// Cascade numbers are used to prevent infinite loops if this function is a
|
|
|
|
/// cyclic relation.
|
|
|
|
///
|
|
|
|
/// @param A The live range to be assigned.
|
|
|
|
/// @param IsHint True when A is about to be assigned to its preferred
|
|
|
|
/// register.
|
|
|
|
/// @param B The live range to be evicted.
|
|
|
|
/// @param BreaksHint True when B is already assigned to its preferred register.
|
2022-02-04 01:07:42 +08:00
|
|
|
bool DefaultEvictionAdvisor::shouldEvict(const LiveInterval &A, bool IsHint,
|
|
|
|
const LiveInterval &B,
|
2022-02-01 23:58:00 +08:00
|
|
|
bool BreaksHint) const {
|
|
|
|
bool CanSplit = RA.getExtraInfo().getStage(B) < RS_Spill;
|
|
|
|
|
|
|
|
// Be fairly aggressive about following hints as long as the evictee can be
|
|
|
|
// split.
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// canEvictHintInterference - return true if the interference for VirtReg
|
|
|
|
/// on the PhysReg, which is VirtReg's hint, can be evicted in favor of VirtReg.
|
|
|
|
bool DefaultEvictionAdvisor::canEvictHintInterference(
|
2022-02-04 01:07:42 +08:00
|
|
|
const LiveInterval &VirtReg, MCRegister PhysReg,
|
2022-02-01 23:58:00 +08:00
|
|
|
const SmallVirtRegSet &FixedRegisters) const {
|
|
|
|
EvictionCost MaxCost;
|
|
|
|
MaxCost.setBrokenHints(1);
|
|
|
|
return canEvictInterferenceBasedOnCost(VirtReg, PhysReg, true, MaxCost,
|
|
|
|
FixedRegisters);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// canEvictInterferenceBasedOnCost - Return true if all interferences between
|
|
|
|
/// VirtReg and PhysReg can be evicted.
|
|
|
|
///
|
|
|
|
/// @param VirtReg Live range that is about to be assigned.
|
|
|
|
/// @param PhysReg Desired register for assignment.
|
|
|
|
/// @param IsHint True when PhysReg is VirtReg's preferred register.
|
|
|
|
/// @param MaxCost Only look for cheaper candidates and update with new cost
|
|
|
|
/// when returning true.
|
|
|
|
/// @returns True when interference can be evicted cheaper than MaxCost.
|
|
|
|
bool DefaultEvictionAdvisor::canEvictInterferenceBasedOnCost(
|
2022-02-04 01:07:42 +08:00
|
|
|
const LiveInterval &VirtReg, MCRegister PhysReg, bool IsHint,
|
2022-02-01 23:58:00 +08:00
|
|
|
EvictionCost &MaxCost, const SmallVirtRegSet &FixedRegisters) const {
|
|
|
|
// It is only possible to evict virtual register interference.
|
|
|
|
if (Matrix->checkInterference(VirtReg, PhysReg) > LiveRegMatrix::IK_VirtReg)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool IsLocal = VirtReg.empty() || LIS->intervalIsInOneMBB(VirtReg);
|
|
|
|
|
|
|
|
// Find VirtReg's cascade number. This will be unassigned if VirtReg was never
|
|
|
|
// involved in an eviction before. If a cascade number was assigned, deny
|
|
|
|
// evicting anything with the same or a newer cascade number. This prevents
|
|
|
|
// infinite eviction loops.
|
|
|
|
//
|
|
|
|
// This works out so a register without a cascade number is allowed to evict
|
|
|
|
// anything, and it can be evicted by anything.
|
|
|
|
unsigned Cascade = RA.getExtraInfo().getCascadeOrCurrentNext(VirtReg.reg());
|
|
|
|
|
|
|
|
EvictionCost Cost;
|
|
|
|
for (MCRegUnitIterator Units(PhysReg, TRI); Units.isValid(); ++Units) {
|
|
|
|
LiveIntervalUnion::Query &Q = Matrix->query(VirtReg, *Units);
|
|
|
|
// If there is 10 or more interferences, chances are one is heavier.
|
2022-02-02 00:21:30 +08:00
|
|
|
const auto &Interferences = Q.interferingVRegs(EvictInterferenceCutoff);
|
|
|
|
if (Interferences.size() >= EvictInterferenceCutoff)
|
2022-02-01 23:58:00 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// Check if any interfering live range is heavier than MaxWeight.
|
2022-02-04 01:07:42 +08:00
|
|
|
for (const LiveInterval *Intf : reverse(Interferences)) {
|
2022-02-01 23:58:00 +08:00
|
|
|
assert(Register::isVirtualRegister(Intf->reg()) &&
|
|
|
|
"Only expecting virtual register interference from query");
|
|
|
|
|
|
|
|
// Do not allow eviction of a virtual register if we are in the middle
|
|
|
|
// of last-chance recoloring and this virtual register is one that we
|
|
|
|
// have scavenged a physical register for.
|
|
|
|
if (FixedRegisters.count(Intf->reg()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Never evict spill products. They cannot split or spill.
|
|
|
|
if (RA.getExtraInfo().getStage(*Intf) == RS_Done)
|
|
|
|
return false;
|
|
|
|
// Once a live range becomes small enough, it is urgent that we find a
|
|
|
|
// register for it. This is indicated by an infinite spill weight. These
|
|
|
|
// urgent live ranges get to evict almost anything.
|
|
|
|
//
|
|
|
|
// Also allow urgent evictions of unspillable ranges from a strictly
|
|
|
|
// larger allocation order.
|
|
|
|
bool Urgent =
|
|
|
|
!VirtReg.isSpillable() &&
|
|
|
|
(Intf->isSpillable() ||
|
|
|
|
RegClassInfo.getNumAllocatableRegs(MRI->getRegClass(VirtReg.reg())) <
|
|
|
|
RegClassInfo.getNumAllocatableRegs(
|
|
|
|
MRI->getRegClass(Intf->reg())));
|
|
|
|
// Only evict older cascades or live ranges without a cascade.
|
|
|
|
unsigned IntfCascade = RA.getExtraInfo().getCascade(Intf->reg());
|
|
|
|
if (Cascade <= IntfCascade) {
|
|
|
|
if (!Urgent)
|
|
|
|
return false;
|
|
|
|
// We permit breaking cascades for urgent evictions. It should be the
|
|
|
|
// last resort, though, so make it really expensive.
|
|
|
|
Cost.BrokenHints += 10;
|
|
|
|
}
|
|
|
|
// Would this break a satisfied hint?
|
|
|
|
bool BreaksHint = VRM->hasPreferredPhys(Intf->reg());
|
|
|
|
// Update eviction cost.
|
|
|
|
Cost.BrokenHints += BreaksHint;
|
|
|
|
Cost.MaxWeight = std::max(Cost.MaxWeight, Intf->weight());
|
|
|
|
// Abort if this would be too expensive.
|
|
|
|
if (!(Cost < MaxCost))
|
|
|
|
return false;
|
|
|
|
if (Urgent)
|
|
|
|
continue;
|
|
|
|
// Apply the eviction policy for non-urgent evictions.
|
|
|
|
if (!shouldEvict(VirtReg, IsHint, *Intf, BreaksHint))
|
|
|
|
return false;
|
|
|
|
// If !MaxCost.isMax(), then we're just looking for a cheap register.
|
|
|
|
// Evicting another local live range in this case could lead to suboptimal
|
|
|
|
// coloring.
|
|
|
|
if (!MaxCost.isMax() && IsLocal && LIS->intervalIsInOneMBB(*Intf) &&
|
|
|
|
(!EnableLocalReassign || !canReassign(*Intf, PhysReg))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
MaxCost = Cost;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
MCRegister DefaultEvictionAdvisor::tryFindEvictionCandidate(
|
2022-02-04 01:07:42 +08:00
|
|
|
const LiveInterval &VirtReg, const AllocationOrder &Order,
|
2022-02-01 23:58:00 +08:00
|
|
|
uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const {
|
|
|
|
// Keep track of the cheapest interference seen so far.
|
|
|
|
EvictionCost BestCost;
|
|
|
|
BestCost.setMax();
|
|
|
|
MCRegister BestPhys;
|
|
|
|
auto MaybeOrderLimit = getOrderLimit(VirtReg, Order, CostPerUseLimit);
|
|
|
|
if (!MaybeOrderLimit)
|
|
|
|
return MCRegister::NoRegister;
|
|
|
|
unsigned OrderLimit = *MaybeOrderLimit;
|
|
|
|
|
|
|
|
// When we are just looking for a reduced cost per use, don't break any
|
|
|
|
// hints, and only evict smaller spill weights.
|
|
|
|
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;
|
|
|
|
|
|
|
|
// Best so far.
|
|
|
|
BestPhys = PhysReg;
|
|
|
|
|
|
|
|
// Stop if the hint can be used.
|
|
|
|
if (I.isHint())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return BestPhys;
|
|
|
|
}
|