| //===- 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" |
| #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) {} |
| |
| // support for isa<> and dyn_cast. |
| 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; |
| }; |
| } // 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: |
| #if defined(LLVM_HAVE_TFLITE) |
| 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(/*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"); |
| } |
| |
| 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())) {} |
| |
| /// 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. |
| bool DefaultEvictionAdvisor::shouldEvict(const LiveInterval &A, bool IsHint, |
| const LiveInterval &B, |
| 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( |
| const LiveInterval &VirtReg, MCRegister PhysReg, |
| 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( |
| const LiveInterval &VirtReg, MCRegister PhysReg, bool IsHint, |
| 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. |
| const auto &Interferences = Q.interferingVRegs(EvictInterferenceCutoff); |
| if (Interferences.size() >= EvictInterferenceCutoff) |
| return false; |
| |
| // Check if any interfering live range is heavier than MaxWeight. |
| for (const LiveInterval *Intf : reverse(Interferences)) { |
| assert(Intf->reg().isVirtual() && |
| "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) |
| return false; |
| |
| 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( |
| const LiveInterval &VirtReg, const AllocationOrder &Order, |
| 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; |
| } |