#include "SpillPlacement.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/CodeGen/EdgeBundles.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <utility>
using namespace llvm;
#define DEBUG_TYPE "spill-code-placement"
char SpillPlacement::ID = 0;
char &llvm::SpillPlacementID = SpillPlacement::ID;
INITIALIZE_PASS_BEGIN(SpillPlacement, DEBUG_TYPE,
"Spill Code Placement Analysis", true, true)
INITIALIZE_PASS_DEPENDENCY(EdgeBundles)
INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
INITIALIZE_PASS_END(SpillPlacement, DEBUG_TYPE,
"Spill Code Placement Analysis", true, true)
void SpillPlacement::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequired<MachineBlockFrequencyInfo>();
AU.addRequiredTransitive<EdgeBundles>();
AU.addRequiredTransitive<MachineLoopInfo>();
MachineFunctionPass::getAnalysisUsage(AU);
}
struct SpillPlacement::Node {
BlockFrequency BiasN;
BlockFrequency BiasP;
int Value;
using LinkVector = SmallVector<std::pair<BlockFrequency, unsigned>, 4>;
LinkVector Links;
BlockFrequency SumLinkWeights;
bool preferReg() const {
return Value > 0;
}
bool mustSpill() const {
return BiasN >= BiasP + SumLinkWeights;
}
void clear(const BlockFrequency &Threshold) {
BiasN = BiasP = Value = 0;
SumLinkWeights = Threshold;
Links.clear();
}
void addLink(unsigned b, BlockFrequency w) {
SumLinkWeights += w;
for (std::pair<BlockFrequency, unsigned> &L : Links)
if (L.second == b) {
L.first += w;
return;
}
Links.push_back(std::make_pair(w, b));
}
void addBias(BlockFrequency freq, BorderConstraint direction) {
switch (direction) {
default:
break;
case PrefReg:
BiasP += freq;
break;
case PrefSpill:
BiasN += freq;
break;
case MustSpill:
BiasN = BlockFrequency::getMaxFrequency();
break;
}
}
bool update(const Node nodes[], const BlockFrequency &Threshold) {
BlockFrequency SumN = BiasN;
BlockFrequency SumP = BiasP;
for (std::pair<BlockFrequency, unsigned> &L : Links) {
if (nodes[L.second].Value == -1)
SumN += L.first;
else if (nodes[L.second].Value == 1)
SumP += L.first;
}
bool Before = preferReg();
if (SumN >= SumP + Threshold)
Value = -1;
else if (SumP >= SumN + Threshold)
Value = 1;
else
Value = 0;
return Before != preferReg();
}
void getDissentingNeighbors(SparseSet<unsigned> &List,
const Node nodes[]) const {
for (const auto &Elt : Links) {
unsigned n = Elt.second;
if (Value != nodes[n].Value)
List.insert(n);
}
}
};
bool SpillPlacement::runOnMachineFunction(MachineFunction &mf) {
MF = &mf;
bundles = &getAnalysis<EdgeBundles>();
loops = &getAnalysis<MachineLoopInfo>();
assert(!nodes && "Leaking node array");
nodes = new Node[bundles->getNumBundles()];
TodoList.clear();
TodoList.setUniverse(bundles->getNumBundles());
BlockFrequencies.resize(mf.getNumBlockIDs());
MBFI = &getAnalysis<MachineBlockFrequencyInfo>();
setThreshold(MBFI->getEntryFreq());
for (auto &I : mf) {
unsigned Num = I.getNumber();
BlockFrequencies[Num] = MBFI->getBlockFreq(&I);
}
return false;
}
void SpillPlacement::releaseMemory() {
delete[] nodes;
nodes = nullptr;
TodoList.clear();
}
void SpillPlacement::activate(unsigned n) {
TodoList.insert(n);
if (ActiveNodes->test(n))
return;
ActiveNodes->set(n);
nodes[n].clear(Threshold);
if (bundles->getBlocks(n).size() > 100) {
nodes[n].BiasP = 0;
nodes[n].BiasN = (MBFI->getEntryFreq() / 16);
}
}
void SpillPlacement::setThreshold(const BlockFrequency &Entry) {
uint64_t Freq = Entry.getFrequency();
uint64_t Scaled = (Freq >> 13) + bool(Freq & (1 << 12));
Threshold = std::max(UINT64_C(1), Scaled);
}
void SpillPlacement::addConstraints(ArrayRef<BlockConstraint> LiveBlocks) {
for (const BlockConstraint &LB : LiveBlocks) {
BlockFrequency Freq = BlockFrequencies[LB.Number];
if (LB.Entry != DontCare) {
unsigned ib = bundles->getBundle(LB.Number, false);
activate(ib);
nodes[ib].addBias(Freq, LB.Entry);
}
if (LB.Exit != DontCare) {
unsigned ob = bundles->getBundle(LB.Number, true);
activate(ob);
nodes[ob].addBias(Freq, LB.Exit);
}
}
}
void SpillPlacement::addPrefSpill(ArrayRef<unsigned> Blocks, bool Strong) {
for (unsigned B : Blocks) {
BlockFrequency Freq = BlockFrequencies[B];
if (Strong)
Freq += Freq;
unsigned ib = bundles->getBundle(B, false);
unsigned ob = bundles->getBundle(B, true);
activate(ib);
activate(ob);
nodes[ib].addBias(Freq, PrefSpill);
nodes[ob].addBias(Freq, PrefSpill);
}
}
void SpillPlacement::addLinks(ArrayRef<unsigned> Links) {
for (unsigned Number : Links) {
unsigned ib = bundles->getBundle(Number, false);
unsigned ob = bundles->getBundle(Number, true);
if (ib == ob)
continue;
activate(ib);
activate(ob);
BlockFrequency Freq = BlockFrequencies[Number];
nodes[ib].addLink(ob, Freq);
nodes[ob].addLink(ib, Freq);
}
}
bool SpillPlacement::scanActiveBundles() {
RecentPositive.clear();
for (unsigned n : ActiveNodes->set_bits()) {
update(n);
if (nodes[n].mustSpill())
continue;
if (nodes[n].preferReg())
RecentPositive.push_back(n);
}
return !RecentPositive.empty();
}
bool SpillPlacement::update(unsigned n) {
if (!nodes[n].update(nodes, Threshold))
return false;
nodes[n].getDissentingNeighbors(TodoList, nodes);
return true;
}
void SpillPlacement::iterate() {
RecentPositive.clear();
unsigned Limit = bundles->getNumBundles() * 10;
while(Limit-- > 0 && !TodoList.empty()) {
unsigned n = TodoList.pop_back_val();
if (!update(n))
continue;
if (nodes[n].preferReg())
RecentPositive.push_back(n);
}
}
void SpillPlacement::prepare(BitVector &RegBundles) {
RecentPositive.clear();
TodoList.clear();
ActiveNodes = &RegBundles;
ActiveNodes->clear();
ActiveNodes->resize(bundles->getNumBundles());
}
bool
SpillPlacement::finish() {
assert(ActiveNodes && "Call prepare() first");
bool Perfect = true;
for (unsigned n : ActiveNodes->set_bits())
if (!nodes[n].preferReg()) {
ActiveNodes->reset(n);
Perfect = false;
}
ActiveNodes = nullptr;
return Perfect;
}
void SpillPlacement::BlockConstraint::print(raw_ostream &OS) const {
auto toString = [](BorderConstraint C) -> StringRef {
switch(C) {
case DontCare: return "DontCare";
case PrefReg: return "PrefReg";
case PrefSpill: return "PrefSpill";
case PrefBoth: return "PrefBoth";
case MustSpill: return "MustSpill";
};
llvm_unreachable("uncovered switch");
};
dbgs() << "{" << Number << ", "
<< toString(Entry) << ", "
<< toString(Exit) << ", "
<< (ChangesValue ? "changes" : "no change") << "}";
}
void SpillPlacement::BlockConstraint::dump() const {
print(dbgs());
dbgs() << "\n";
}