2021-11-16 03:42:04 +08:00
|
|
|
//===- RegAllocEvictionAdvisor.h - Interference resolution ------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#ifndef LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H
|
|
|
|
#define LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H
|
|
|
|
|
|
|
|
#include "AllocationOrder.h"
|
|
|
|
#include "llvm/ADT/IndexedMap.h"
|
|
|
|
#include "llvm/ADT/SmallSet.h"
|
|
|
|
#include "llvm/CodeGen/LiveInterval.h"
|
|
|
|
#include "llvm/CodeGen/LiveIntervals.h"
|
|
|
|
#include "llvm/CodeGen/LiveRegMatrix.h"
|
|
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
|
|
#include "llvm/CodeGen/Register.h"
|
|
|
|
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
2021-12-14 14:49:57 +08:00
|
|
|
#include "llvm/Config/llvm-config.h"
|
2021-11-16 03:42:04 +08:00
|
|
|
#include "llvm/Pass.h"
|
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
|
|
|
|
using SmallVirtRegSet = SmallSet<Register, 16>;
|
|
|
|
|
|
|
|
// Live ranges pass through a number of stages as we try to allocate them.
|
|
|
|
// Some of the stages may also create new live ranges:
|
|
|
|
//
|
|
|
|
// - Region splitting.
|
|
|
|
// - Per-block splitting.
|
|
|
|
// - Local splitting.
|
|
|
|
// - Spilling.
|
|
|
|
//
|
|
|
|
// Ranges produced by one of the stages skip the previous stages when they are
|
|
|
|
// dequeued. This improves performance because we can skip interference checks
|
|
|
|
// that are unlikely to give any results. It also guarantees that the live
|
|
|
|
// range splitting algorithm terminates, something that is otherwise hard to
|
|
|
|
// ensure.
|
|
|
|
enum LiveRangeStage {
|
|
|
|
/// Newly created live range that has never been queued.
|
|
|
|
RS_New,
|
|
|
|
|
|
|
|
/// Only attempt assignment and eviction. Then requeue as RS_Split.
|
|
|
|
RS_Assign,
|
|
|
|
|
|
|
|
/// Attempt live range splitting if assignment is impossible.
|
|
|
|
RS_Split,
|
|
|
|
|
|
|
|
/// Attempt more aggressive live range splitting that is guaranteed to make
|
|
|
|
/// progress. This is used for split products that may not be making
|
|
|
|
/// progress.
|
|
|
|
RS_Split2,
|
|
|
|
|
|
|
|
/// Live range will be spilled. No more splitting will be attempted.
|
|
|
|
RS_Spill,
|
|
|
|
|
|
|
|
/// Live range is in memory. Because of other evictions, it might get moved
|
|
|
|
/// in a register in the end.
|
|
|
|
RS_Memory,
|
|
|
|
|
|
|
|
/// There is nothing more we can do to this live range. Abort compilation
|
|
|
|
/// if it can't be assigned.
|
|
|
|
RS_Done
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Cost of evicting interference - used by default advisor, and the eviction
|
|
|
|
/// chain heuristic in RegAllocGreedy.
|
|
|
|
// FIXME: this can be probably made an implementation detail of the default
|
|
|
|
// advisor, if the eviction chain logic can be refactored.
|
|
|
|
struct EvictionCost {
|
|
|
|
unsigned BrokenHints = 0; ///< Total number of broken hints.
|
|
|
|
float MaxWeight = 0; ///< Maximum spill weight evicted.
|
|
|
|
|
|
|
|
EvictionCost() = default;
|
|
|
|
|
|
|
|
bool isMax() const { return BrokenHints == ~0u; }
|
|
|
|
|
|
|
|
void setMax() { BrokenHints = ~0u; }
|
|
|
|
|
|
|
|
void setBrokenHints(unsigned NHints) { BrokenHints = NHints; }
|
|
|
|
|
|
|
|
bool operator<(const EvictionCost &O) const {
|
|
|
|
return std::tie(BrokenHints, MaxWeight) <
|
|
|
|
std::tie(O.BrokenHints, O.MaxWeight);
|
|
|
|
}
|
|
|
|
};
|
2021-12-01 08:30:16 +08:00
|
|
|
|
|
|
|
/// Track allocation stage and eviction loop prevention during allocation.
|
|
|
|
// TODO(mtrofin): Consider exposing RAGreedy in a header instead, and folding
|
|
|
|
// this back into it.
|
|
|
|
class ExtraRegInfo final {
|
|
|
|
// RegInfo - Keep additional information about each live range.
|
|
|
|
struct RegInfo {
|
|
|
|
LiveRangeStage Stage = RS_New;
|
|
|
|
|
|
|
|
// Cascade - Eviction loop prevention. See
|
|
|
|
// canEvictInterferenceBasedOnCost().
|
|
|
|
unsigned Cascade = 0;
|
|
|
|
|
|
|
|
RegInfo() = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
IndexedMap<RegInfo, VirtReg2IndexFunctor> Info;
|
|
|
|
unsigned NextCascade = 1;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ExtraRegInfo() = default;
|
|
|
|
ExtraRegInfo(const ExtraRegInfo &) = delete;
|
|
|
|
|
|
|
|
LiveRangeStage getStage(Register Reg) const { return Info[Reg].Stage; }
|
|
|
|
|
|
|
|
LiveRangeStage getStage(const LiveInterval &VirtReg) const {
|
|
|
|
return getStage(VirtReg.reg());
|
|
|
|
}
|
|
|
|
|
|
|
|
void setStage(Register Reg, LiveRangeStage Stage) {
|
|
|
|
Info.grow(Reg.id());
|
|
|
|
Info[Reg].Stage = Stage;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setStage(const LiveInterval &VirtReg, LiveRangeStage Stage) {
|
|
|
|
setStage(VirtReg.reg(), Stage);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the current stage of the register, if present, otherwise initialize
|
|
|
|
/// it and return that.
|
|
|
|
LiveRangeStage getOrInitStage(Register Reg) {
|
|
|
|
Info.grow(Reg.id());
|
|
|
|
return getStage(Reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned getCascade(Register Reg) const { return Info[Reg].Cascade; }
|
|
|
|
|
|
|
|
void setCascade(Register Reg, unsigned Cascade) {
|
|
|
|
Info.grow(Reg.id());
|
|
|
|
Info[Reg].Cascade = Cascade;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned getOrAssignNewCascade(Register Reg) {
|
|
|
|
unsigned Cascade = getCascade(Reg);
|
|
|
|
if (!Cascade) {
|
|
|
|
Cascade = NextCascade++;
|
|
|
|
setCascade(Reg, Cascade);
|
|
|
|
}
|
|
|
|
return Cascade;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned getCascadeOrCurrentNext(Register Reg) const {
|
|
|
|
unsigned Cascade = getCascade(Reg);
|
|
|
|
if (!Cascade)
|
|
|
|
Cascade = NextCascade;
|
|
|
|
return Cascade;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Iterator>
|
|
|
|
void setStage(Iterator Begin, Iterator End, LiveRangeStage NewStage) {
|
|
|
|
for (; Begin != End; ++Begin) {
|
|
|
|
Register Reg = *Begin;
|
|
|
|
Info.grow(Reg.id());
|
|
|
|
if (Info[Reg].Stage == RS_New)
|
|
|
|
Info[Reg].Stage = NewStage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void LRE_DidCloneVirtReg(Register New, Register Old);
|
|
|
|
};
|
2021-12-14 14:49:57 +08:00
|
|
|
|
|
|
|
/// Interface to the eviction advisor, which is responsible for making a
|
|
|
|
/// decision as to which live ranges should be evicted (if any).
|
|
|
|
class RegAllocEvictionAdvisor {
|
|
|
|
public:
|
|
|
|
RegAllocEvictionAdvisor(const RegAllocEvictionAdvisor &) = delete;
|
|
|
|
RegAllocEvictionAdvisor(RegAllocEvictionAdvisor &&) = delete;
|
|
|
|
virtual ~RegAllocEvictionAdvisor() = default;
|
|
|
|
|
|
|
|
/// Find a physical register that can be freed by evicting the FixedRegisters,
|
|
|
|
/// or return NoRegister. The eviction decision is assumed to be correct (i.e.
|
|
|
|
/// no fixed live ranges are evicted) and profitable.
|
|
|
|
virtual MCRegister
|
|
|
|
tryFindEvictionCandidate(LiveInterval &VirtReg, const AllocationOrder &Order,
|
|
|
|
uint8_t CostPerUseLimit,
|
|
|
|
const SmallVirtRegSet &FixedRegisters) const = 0;
|
|
|
|
|
|
|
|
/// Find out if we can evict the live ranges occupying the given PhysReg,
|
|
|
|
/// which is a hint (preferred register) for VirtReg.
|
|
|
|
virtual bool
|
|
|
|
canEvictHintInterference(LiveInterval &VirtReg, MCRegister PhysReg,
|
|
|
|
const SmallVirtRegSet &FixedRegisters) const = 0;
|
|
|
|
|
|
|
|
/// Returns true if the given \p PhysReg is a callee saved register and has
|
|
|
|
/// not been used for allocation yet.
|
|
|
|
bool isUnusedCalleeSavedReg(MCRegister PhysReg) const;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
RegAllocEvictionAdvisor(const MachineFunction &MF, LiveRegMatrix *Matrix,
|
|
|
|
LiveIntervals *LIS, VirtRegMap *VRM,
|
|
|
|
const RegisterClassInfo &RegClassInfo,
|
|
|
|
ExtraRegInfo *ExtraInfo);
|
|
|
|
|
|
|
|
Register canReassign(LiveInterval &VirtReg, Register PrevReg) const;
|
|
|
|
|
|
|
|
const MachineFunction &MF;
|
|
|
|
LiveRegMatrix *const Matrix;
|
|
|
|
LiveIntervals *const LIS;
|
|
|
|
VirtRegMap *const VRM;
|
|
|
|
MachineRegisterInfo *const MRI;
|
|
|
|
const TargetRegisterInfo *const TRI;
|
|
|
|
const RegisterClassInfo &RegClassInfo;
|
|
|
|
const ArrayRef<uint8_t> RegCosts;
|
|
|
|
ExtraRegInfo *const ExtraInfo;
|
|
|
|
|
|
|
|
/// Run or not the local reassignment heuristic. This information is
|
|
|
|
/// obtained from the TargetSubtargetInfo.
|
|
|
|
const bool EnableLocalReassign;
|
|
|
|
|
|
|
|
private:
|
|
|
|
unsigned NextCascade = 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// ImmutableAnalysis abstraction for fetching the Eviction Advisor. We model it
|
|
|
|
/// as an analysis to decouple the user from the implementation insofar as
|
|
|
|
/// dependencies on other analyses goes. The motivation for it being an
|
|
|
|
/// immutable pass is twofold:
|
|
|
|
/// - in the ML implementation case, the evaluator is stateless but (especially
|
|
|
|
/// in the development mode) expensive to set up. With an immutable pass, we set
|
|
|
|
/// it up once.
|
|
|
|
/// - in the 'development' mode ML case, we want to capture the training log
|
|
|
|
/// during allocation (this is a log of features encountered and decisions
|
|
|
|
/// made), and then measure a score, potentially a few steps after allocation
|
|
|
|
/// completes. So we need the properties of an immutable pass to keep the logger
|
|
|
|
/// state around until we can make that measurement.
|
|
|
|
///
|
|
|
|
/// Because we need to offer additional services in 'development' mode, the
|
|
|
|
/// implementations of this analysis need to implement RTTI support.
|
|
|
|
class RegAllocEvictionAdvisorAnalysis : public ImmutablePass {
|
|
|
|
public:
|
|
|
|
enum class AdvisorMode : int { Default, Release, Development };
|
|
|
|
|
|
|
|
RegAllocEvictionAdvisorAnalysis(AdvisorMode Mode)
|
|
|
|
: ImmutablePass(ID), Mode(Mode){};
|
|
|
|
static char ID;
|
|
|
|
|
|
|
|
/// Get an advisor for the given context (i.e. machine function, etc)
|
|
|
|
virtual std::unique_ptr<RegAllocEvictionAdvisor>
|
|
|
|
getAdvisor(const MachineFunction &MF, LiveRegMatrix *Matrix,
|
|
|
|
LiveIntervals *LIS, VirtRegMap *VRM,
|
|
|
|
const RegisterClassInfo &RegClassInfo,
|
|
|
|
ExtraRegInfo *ExtraInfo) = 0;
|
|
|
|
AdvisorMode getAdvisorMode() const { return Mode; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
// This analysis preserves everything, and subclasses may have additional
|
|
|
|
// requirements.
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
|
|
AU.setPreservesAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef getPassName() const override;
|
|
|
|
const AdvisorMode Mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Specialization for the API used by the analysis infrastructure to create
|
|
|
|
/// an instance of the eviction advisor.
|
|
|
|
template <> Pass *callDefaultCtor<RegAllocEvictionAdvisorAnalysis>();
|
|
|
|
|
|
|
|
// TODO(mtrofin): implement these.
|
|
|
|
#ifdef LLVM_HAVE_TF_AOT
|
|
|
|
RegAllocEvictionAdvisorAnalysis *createReleaseModeAdvisor();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef LLVM_HAVE_TF_API
|
|
|
|
RegAllocEvictionAdvisorAnalysis *createDevelopmentModeAdvisor();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// TODO: move to RegAllocEvictionAdvisor.cpp when we move implementation
|
|
|
|
// out of RegAllocGreedy.cpp
|
|
|
|
class DefaultEvictionAdvisor : public RegAllocEvictionAdvisor {
|
|
|
|
public:
|
|
|
|
DefaultEvictionAdvisor(const MachineFunction &MF, LiveRegMatrix *Matrix,
|
|
|
|
LiveIntervals *LIS, VirtRegMap *VRM,
|
|
|
|
const RegisterClassInfo &RegClassInfo,
|
|
|
|
ExtraRegInfo *ExtraInfo)
|
|
|
|
: RegAllocEvictionAdvisor(MF, Matrix, LIS, VRM, RegClassInfo, ExtraInfo) {
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
MCRegister tryFindEvictionCandidate(LiveInterval &, const AllocationOrder &,
|
|
|
|
uint8_t,
|
|
|
|
const SmallVirtRegSet &) const override;
|
|
|
|
bool canEvictHintInterference(LiveInterval &, MCRegister,
|
|
|
|
const SmallVirtRegSet &) const override;
|
|
|
|
bool canEvictInterferenceBasedOnCost(LiveInterval &, MCRegister, bool,
|
|
|
|
EvictionCost &,
|
|
|
|
const SmallVirtRegSet &) const;
|
|
|
|
bool shouldEvict(LiveInterval &A, bool, LiveInterval &B, bool) const;
|
|
|
|
};
|
2021-11-16 03:42:04 +08:00
|
|
|
} // namespace llvm
|
|
|
|
|
|
|
|
#endif // LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H
|