Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions src/backend/codegen/linear_scan_reg_alloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace rat {
}

void LinearScanRegAllocPass::buildIntervals() {
liveness(liveIn, liveOut);
liveness();

if(intervals.size() < fn->nextVReg)
intervals.resize(fn->nextVReg);
Expand All @@ -39,14 +39,22 @@ namespace rat {

// backward walk per block
segEnd.assign(fn->nextVReg, 0);
live.resetAll(fn->nextVReg);
for(U32 b = 0; b < fn->blocks.size(); ++b) {
if(blkPts[b].empty())
continue;
I32 first = (I32)blkPts[b].front();
I32 last = (I32)blkPts[b].back();

live.copyFrom(liveOut[b]);
live.forEach([&](VReg v) { segEnd[v] = last; });
if(liveIsDense) {
live.copyFrom(denseLive.out[b]);
live.forEach([&](VReg v) { segEnd[v] = last; });
} else {
for(VReg v : sparseLive.out[b]) {
live.set(v);
segEnd[v] = last;
}
}

// an inner-loop reference costs more than a straight-line one
U32 weight = 1;
Expand Down Expand Up @@ -75,7 +83,13 @@ namespace rat {
}
}
}
live.forEach([&](VReg v) { ivFor(v).segs.push_back({first, segEnd[v]}); });
if(liveIsDense)
live.forEach([&](VReg v) { ivFor(v).segs.push_back({first, segEnd[v]}); });
else
for(VReg v : sparseLive.in[b]) {
ivFor(v).segs.push_back({first, segEnd[v]});
live.reset(v); // leaves the set empty for the next block
}
}

for(U32 v = 1; v < fn->nextVReg; ++v) {
Expand Down
1 change: 0 additions & 1 deletion src/backend/codegen/linear_scan_reg_alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ namespace rat {
List<std::pair<I32, U64>> pinsByPoint;
List<Interval*> expiredBuf;
List<I32> segEnd;
List<VRegSet> liveIn, liveOut;
VRegSet live;
List<U64> freeRegs;
};
Expand Down
174 changes: 150 additions & 24 deletions src/backend/codegen/reg_alloc_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,31 +117,136 @@ namespace rat {
return slot;
}

void RegAllocBase::liveness(List<VRegSet>& liveIn, List<VRegSet>& liveOut) {
U32 nb = (U32)fn->blocks.size();
U32 nv = fn->nextVReg;
auto prep = [&](List<VRegSet>& v) {
if(v.size() < nb)
v.resize(nb);
void DenseLive::prep(U32 nb, U32 nv) {
for(List<VRegSet>* v : {&in, &out, &use, &def}) {
if(v->size() < nb)
v->resize(nb);
for(U32 i = 0; i < nb; ++i)
v[i].resetAll(nv);
};
prep(liveUseScratch);
prep(liveDefScratch);
List<VRegSet>& useSet = liveUseScratch;
List<VRegSet>& defSet = liveDefScratch;
for(U32 b = 0; b < nb; ++b) {
(*v)[i].resetAll(nv);
}
}

void SparseLive::prep(U32 nb) {
for(List<VRegList>* v : {&in, &out, &use, &def}) {
if(v->size() < nb)
v->resize(nb);
for(U32 i = 0; i < nb; ++i)
(*v)[i].clear();
}
}

void detail::vregUnion(const VRegList& a, const VRegList& b, VRegList& dst) {
dst.clear();
U32 i = 0, j = 0;
while(i < a.size() && j < b.size()) {
if(a[i] < b[j])
dst.push_back(a[i++]);
else if(b[j] < a[i])
dst.push_back(b[j++]);
else {
dst.push_back(a[i++]);
++j;
}
}
while(i < a.size())
dst.push_back(a[i++]);
while(j < b.size())
dst.push_back(b[j++]);
}

void detail::vregUnionMasked(const VRegList& a,
const VRegList& b,
const VRegList& mask,
VRegList& dst) {
dst.clear();
U32 i = 0, m = 0;
for(U32 j = 0; j < b.size(); ++j) {
while(m < mask.size() && mask[m] < b[j])
++m;
if(m < mask.size() && mask[m] == b[j]) // killed by a def in this block
continue;
while(i < a.size() && a[i] < b[j])
dst.push_back(a[i++]);
if(i < a.size() && a[i] == b[j])
++i;
dst.push_back(b[j]);
}
while(i < a.size())
dst.push_back(a[i++]);
}

void RegAllocBase::blockUseDefsSparse() {
VRegSet used(fn->nextVReg), defd(fn->nextVReg);
for(U32 b = 0; b < (U32)fn->blocks.size(); ++b) {
VRegList& use = sparseLive.use[b];
VRegList& def = sparseLive.def[b];
for(const MachineInstr& in : fn->blocks[b].insts) {
for(const MachineOperand& u : in.uses)
if(u.isVReg() && !defSet[b].test(u.vreg))
useSet[b].set(u.vreg);
if(u.isVReg() && !defd.test(u.vreg) && !used.test(u.vreg)) {
used.set(u.vreg);
use.push_back(u.vreg);
}
for(const MachineOperand& d : in.defs)
if(d.isVReg())
defSet[b].set(d.vreg);
if(d.isVReg() && !defd.test(d.vreg)) {
defd.set(d.vreg);
def.push_back(d.vreg);
}
}
for(VReg v : use)
used.reset(v);
for(VReg v : def)
defd.reset(v);
std::sort(use.begin(), use.end());
std::sort(def.begin(), def.end());
}
prep(liveIn);
prep(liveOut);
}

void RegAllocBase::liveOutOf(U32 b, VRegList& out, VRegList& tmp) {
const List<I32>& succs = fn->blocks[b].succs;
if(succs.empty()) {
out.clear();
return;
}
out = sparseLive.in[(U32)succs[0]];
for(U32 i = 1; i < (U32)succs.size(); ++i) {
detail::vregUnion(out, sparseLive.in[(U32)succs[i]], tmp);
out.swap(tmp);
}
}

constexpr U64 kDenseLivenessBytes = 64ull << 20;

B32 RegAllocBase::denseLivenessFits() const {
U64 words = (fn->nextVReg + 63) / 64;
U64 perBlock = words * 8 * 4; // in, out, use and def, eight bytes to the word
return (U64)fn->blocks.size() * perBlock <= kDenseLivenessBytes;
}

void RegAllocBase::liveness() {
liveIsDense = denseLivenessFits();
if(liveIsDense)
livenessDense();
else
livenessSparse();
}

void RegAllocBase::blockUseDefsDense() {
for(U32 b = 0; b < (U32)fn->blocks.size(); ++b)
for(const MachineInstr& in : fn->blocks[b].insts) {
for(const MachineOperand& u : in.uses)
if(u.isVReg() && !denseLive.def[b].test(u.vreg))
denseLive.use[b].set(u.vreg);
for(const MachineOperand& d : in.defs)
if(d.isVReg())
denseLive.def[b].set(d.vreg);
}
}

void RegAllocBase::livenessDense() {
U32 nb = (U32)fn->blocks.size();
U32 nv = fn->nextVReg;
denseLive.prep(nb, nv);
blockUseDefsDense();
B32 changed = true;
VRegSet out, in;
out.resetAll(nv);
Expand All @@ -151,12 +256,33 @@ namespace rat {
for(I32 b = (I32)nb - 1; b >= 0; --b) {
out.resetAll(nv);
for(I32 s : fn->blocks[b].succs)
out.orWith(liveIn[s]);
in.assignUnionMasked(useSet[b], out, defSet[b]); // use | (out & ~def)
if(!(in == liveIn[b]) || !(out == liveOut[b])) {
out.orWith(denseLive.in[s]);
in.assignUnionMasked(denseLive.use[b], out, denseLive.def[b]); // use | (out & ~def)
if(!(in == denseLive.in[b]) || !(out == denseLive.out[b])) {
changed = true;
denseLive.in[b].copyFrom(in);
denseLive.out[b].copyFrom(out);
}
}
}
}

void RegAllocBase::livenessSparse() {
U32 nb = (U32)fn->blocks.size();
sparseLive.prep(nb);
blockUseDefsSparse();
B32 changed = true;
VRegList out, in, tmp;
while(changed) {
changed = false;
for(I32 b = (I32)nb - 1; b >= 0; --b) {
liveOutOf((U32)b, out, tmp);
// use | (out & ~def)
detail::vregUnionMasked(sparseLive.use[b], out, sparseLive.def[b], in);
if(in != sparseLive.in[b] || out != sparseLive.out[b]) {
changed = true;
liveIn[b].copyFrom(in);
liveOut[b].copyFrom(out);
sparseLive.in[b] = in;
sparseLive.out[b] = out;
}
}
}
Expand Down
34 changes: 31 additions & 3 deletions src/backend/codegen/reg_alloc_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ namespace rat {
List<U64> words;
};

using VRegList = List<VReg>;

namespace detail {
void vregUnion(const VRegList& a, const VRegList& b, VRegList& dst); // dst = a | b
void vregUnionMasked(const VRegList& a,
const VRegList& b,
const VRegList& mask,
VRegList& dst); // dst = a | (b & ~mask)
} // namespace detail

struct DenseLive {
List<VRegSet> in, out, use, def;
void prep(U32 nb, U32 nv);
};

struct SparseLive {
List<VRegList> in, out, use, def;
void prep(U32 nb);
};

struct RegAllocBase : MachinePass {
B32 run(Module& module, MachineModule& mm, const TargetInfo& target) override;
protected:
Expand Down Expand Up @@ -86,7 +106,7 @@ namespace rat {
void pinFixedArgWindows();
void collectCopyHints();
void collectRematDefs();
void liveness(List<VRegSet>& liveIn, List<VRegSet>& liveOut);
void liveness();

// allocation preferences derived from copies
List<PhysReg> hintedRegs(VReg v, const Delegate<PhysReg(VReg)>& colorOf) const;
Expand Down Expand Up @@ -135,9 +155,17 @@ namespace rat {
B32 ok = true;
Memo memo;
Set<VReg> slotReadByCall;
List<VRegSet> liveUseScratch;
List<VRegSet> liveDefScratch;
B32 liveIsDense = false;
DenseLive denseLive;
SparseLive sparseLive;
private:
B32 denseLivenessFits() const;
void livenessDense();
void livenessSparse();
void blockUseDefsDense();
void blockUseDefsSparse();
void liveOutOf(U32 b, VRegList& out, VRegList& tmp);

struct PooledSlot {
I32 slot;
I32 freeEnd; // last point of the current occupant's live range
Expand Down
14 changes: 14 additions & 0 deletions src/backend/codegen/schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace rat {
buildCFG();
computeDominators();
computeLoops();
computeHoistBounds();
List<Node*> work;
for(Node* n : fn)
if(isFloating(n))
Expand Down Expand Up @@ -372,6 +373,17 @@ namespace rat {
}
}

// a hoist only ever moves a node to a shallower loop, so record how shallow the
// idom path gets: straight-line code then never has to walk it
void Schedule::computeHoistBounds() {
for(I32 b : rpoOrder) {
Block& bl = blocks[b];
bl.minDepthAbove = bl.loopDepth;
if(b != entryBlock)
bl.minDepthAbove = std::min(bl.loopDepth, blocks[bl.idom].minDepthAbove);
}
}

B32 Schedule::isFloating(const Node* n) {
Opcode op = n->getOpcode();
if(op == Opcode::Alloc)
Expand All @@ -393,6 +405,8 @@ namespace rat {
I32 Schedule::homeBlock(Node* n) const { return headBlock(headOf(n->getControlInput())); }

I32 Schedule::hoistTarget(const Node* n, I32 late, I32 early) const {
if(blocks[late].minDepthAbove >= blocks[late].loopDepth)
return late; // nothing above is shallower, so the walk cannot move it
Opcode op = n->getOpcode();
B32 remat = op == Opcode::Constant || op == Opcode::Global;
B32 trapping = mayTrap(n);
Expand Down
8 changes: 5 additions & 3 deletions src/backend/codegen/schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ namespace rat {
List<I32> caseB; // switch successors, slot order
List<I32> preds; // predecessor block indices

I32 idom = -1; // immediate dominator (entry dominates itself)
I32 domDepth = 0; // depth in the dominator tree
I32 loopDepth = 0; // number of natural loops containing this block
I32 idom = -1; // immediate dominator (entry dominates itself)
I32 domDepth = 0; // depth in the dominator tree
I32 loopDepth = 0; // number of natural loops containing this block
I32 minDepthAbove = 0; // least loopDepth on the idom path up to entry

List<PhiNode*> phis; // data phis merged at this block (region only)
List<Node*> nodes; // scheduled compute nodes, in emit order
Expand Down Expand Up @@ -81,6 +82,7 @@ namespace rat {
void buildCFG();
void computeDominators();
void computeLoops();
void computeHoistBounds();
void scheduleEarly(const List<Node*>& work, List<I32>& early);
void scheduleLate(const List<Node*>& work, const List<I32>& early);
void placeLoads(const List<Node*>& work, const List<I32>& early);
Expand Down
Loading
Loading