#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegAllocRegistry.h"
#include "llvm/CodeGen/RegisterClassInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/SlotIndexes.h"
using namespace llvm;
#define DEBUG_TYPE "regalloc"
STATISTIC(NumStores, "Number of stores (spills) added");
STATISTIC(NumLoads , "Number of loads (reloads) added");
namespace {
class RegAllocSimple : public MachineFunctionPass {
public:
static char ID;
RegAllocSimple() : MachineFunctionPass(ID) {}
private:
MachineFrameInfo *MFI;
MachineRegisterInfo *MRI;
TargetRegisterInfo const *TRI;
TargetInstrInfo const *TII;
RegisterClassInfo RegClassInfo;
DenseMap<MachineOperand, MCPhysReg> LiveVirtRegs;
DenseMap<MCPhysReg, MachineOperand> LivePhysRegs;
DenseMap<MachineOperand, int> LiveSpills;
SmallSet<MCPhysReg, 8> UsedInInstr;
public:
StringRef getPassName() const override { return "Simple Register Allocator"; }
void getAnalysisUsage(AnalysisUsage &AU) const override
{
AU.setPreservesCFG();
AU.addRequired<LiveIntervals>();
AU.addPreserved<LiveIntervals>();
AU.addRequired<SlotIndexes>();
AU.addPreserved<SlotIndexes>();
MachineFunctionPass::getAnalysisUsage(AU);
}
MachineFunctionProperties getRequiredProperties() const override
{
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoPHIs);
}
MachineFunctionProperties getSetProperties() const override
{
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
MachineFunctionProperties getClearedProperties() const override
{
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::IsSSA);
}
private:
void allocateOperand(MachineOperand &MO, bool const isUse)
{
auto it = LiveVirtRegs.find(MO);
if (it != LiveVirtRegs.end()) {
return;
}
assert(MO.getReg().isVirtual() && "Can only allocate a virtual register");
auto* RC = MRI->getRegClass(MO.getReg());
auto AllocOrder = RegClassInfo.getOrder(RC);
MCPhysReg Reg = [AllocOrder, this]() {
auto const regIsUsed = [this](MCPhysReg const& Reg){ return this->LivePhysRegs.find(Reg) != this->LivePhysRegs.end(); };
auto const regIsSpillable = [this](MCPhysReg const& Reg){ return this->UsedInInstr.contains(Reg); };
auto unusedPhysRegs = AllocOrder.drop_while(regIsUsed);
if (unusedPhysRegs.empty()) {
auto spillablePhysRegs = AllocOrder.drop_while(regIsSpillable);
MCPhysReg spilled = spillablePhysRegs.front();
auto virt_spilled = this->LivePhysRegs.find(spilled);
assert(virt_spilled != this->LivePhysRegs.end() && "Attempting to spill unbound physical register");
this->spill(virt_spilled->second);
return spilled;
} else {
return unusedPhysRegs.front();
}
}();
if (isUse) {
reload(MO, Reg);
}
bindPhysReg(MO, Reg);
UsedInInstr.insert(Reg);
LiveVirtRegs.insert({MO, Reg});
LivePhysRegs.insert({Reg, MO});
}
void allocateInstruction(MachineInstr &MI)
{
static SmallVector<MachineOperand, 8> Uses;
static SmallVector<MachineOperand, 1> RegMask;
static SmallVector<MachineOperand, 8> Defs;
UsedInInstr.clear();
Uses.clear();
RegMask.clear();
Defs.clear();
for (auto& MO: MI.operands()) {
if (MO.isUse())
Uses.emplace_back(MO);
if (MO.isRegMask())
RegMask.emplace_back(MO);
if (MO.isDef())
Defs.emplace_back(MO);
}
auto const allocUse = [this](MachineOperand &MO){
assert(MO.isUse());
LLVM_DEBUG(dbgs() << "Regalloc: Allocating use " << MO << "\n");
this->allocateOperand(MO, true);
};
auto const allocDef = [this](MachineOperand &MO){
assert(MO.isDef());
LLVM_DEBUG(dbgs() << "Regalloc: Allocating def " << MO << "\n");
this->allocateOperand(MO, false);
};
for_each(Uses, allocUse);
for(auto& MO: RegMask) {
for (auto &[O, R]: LiveVirtRegs)
if (MO.clobbersPhysReg(R)) {
LLVM_DEBUG(dbgs() << "Regalloc: Spilling clobbered reg " << R << "\n");
spill(O);
}
}
for_each(Defs, allocDef);
}
void allocateBasicBlock(MachineBasicBlock &MBB)
{
LiveVirtRegs.clear();
for (auto &MI: MBB) {
allocateInstruction(MI);
if (MI.isReturn())
return; }
for (auto &[O, _]: LiveVirtRegs) {
spill(O);
}
}
bool runOnMachineFunction(MachineFunction &MF) override
{
MRI = &MF.getRegInfo();
const TargetSubtargetInfo &STI = MF.getSubtarget();
TRI = STI.getRegisterInfo();
TII = STI.getInstrInfo();
MFI = &MF.getFrameInfo();
MRI->freezeReservedRegs(MF);
RegClassInfo.runOnMachineFunction(MF);
for (MachineBasicBlock &MBB : MF) {
allocateBasicBlock(MBB);
}
MRI->clearVirtRegs();
SlotIndexes& SI = getAnalysis<SlotIndexes>();
SI.releaseMemory();
SI.runOnMachineFunction(MF);
LiveIntervals& LI = getAnalysis<LiveIntervals>();
LI.releaseMemory();
LI.runOnMachineFunction(MF);
return true;
}
void bindPhysReg(MachineOperand &MO, MCPhysReg Reg)
{
assert(MO.getReg().isVirtual() && "Cannot bind a physical register to a non-virtual register");
auto idx = MO.getSubReg();
if (idx) {
Reg = TRI->getSubReg(Reg, idx);
MO.setSubReg(0);
}
MO.setReg(Reg);
if (MO.isKill())
MO.setIsKill(false);
else if (MO.isDead())
MO.setIsDead(false);
MO.setIsRenamable();
}
void spill(MachineOperand &MO)
{
assert(MO.getReg().isPhysical() && "Cannot spill something that is not a physical register");
++NumStores;
LiveVirtRegs.erase(MO);
LivePhysRegs.erase(MO.getReg());
UsedInInstr.erase(MO.getReg());
auto* RC = MRI->getRegClass(MO.getReg());
auto slot = MFI->CreateSpillStackObject(TRI->getSpillSize(*RC), TRI->getSpillAlign(*RC));
TII->storeRegToStackSlot(*MO.getMBB(), MO.getMBB()->begin(), MO.getReg(), MO.isKill(), slot, RC, TRI);
LiveSpills.insert({MO, slot});
}
void reload(MachineOperand &MO, MCPhysReg Dest)
{
assert(MO.getReg().isStack() && "Cannot reload something that is not a stack slot");
++NumLoads;
auto* RC = MRI->getRegClass(MO.getReg());
auto slot = LiveSpills.find(MO);
assert(slot != LiveSpills.end() && "Did not find spill slot to reload");
TII->loadRegFromStackSlot(*MO.getMBB(), MO.getMBB()->begin(), Dest, slot->second, RC, TRI);
LiveSpills.erase(MO);
}
};
}
char RegAllocSimple::ID = 0;
FunctionPass *llvm::createSimpleRegisterAllocator() { return new RegAllocSimple(); }
INITIALIZE_PASS(RegAllocSimple, "regallocsimple", "Simple Register Allocator", false, false)
static RegisterRegAlloc simpleRegAlloc("simple", "simple register allocator", createSimpleRegisterAllocator);