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
30 changes: 30 additions & 0 deletions src/backend/codegen/machine_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ namespace rat {
return o;
}

MachineOperand MachineOperand::none(U32 w) { return make(Kind::None, w, 0); }

MachineOperand MachineOperand::symbol(const String& s) {
MachineOperand o = make(Kind::Sym, 8, 0);
o.name = &s;
return o;
}

U32& MachineBlock::dbgBefore(U32 k) { return k < insts.size() ? insts[k].dbg : dbgEnd; }

void MachineFunc::reset() {
for(MachineBlock& b : blocks) {
b.id = -1;
Expand All @@ -37,7 +41,9 @@ namespace rat {
b.preds.clear();
b.succs.clear();
b.insts.clear();
b.dbgEnd = 0;
}
dbg.clear();
nextVReg = 1;
vregClass.clear();
frameBytes = 0;
Expand All @@ -52,4 +58,28 @@ namespace rat {
vregClass[v] = cls;
return v;
}

void MachineFunc::addDbg(U32& chain, U32 inst, MachineOperand loc) {
if(dbg.empty())
dbg.emplace_back();
dbg.push_back({inst, loc, 0});
chain = joinDbg(chain, (U32)dbg.size() - 1);
}

U32 MachineFunc::joinDbg(U32 first, U32 second) {
if(!first || !second)
return first ? first : second;
U32 tail = first;
while(dbg[tail].next)
tail = dbg[tail].next;
dbg[tail].next = second;
return first;
}

U32 MachineFunc::copyDbg(U32 chain) {
U32 head = 0;
for(U32 r = chain; r; r = dbg[r].next)
addDbg(head, dbg[r].inst, dbg[r].loc);
return head;
}
} // namespace rat
17 changes: 17 additions & 0 deletions src/backend/codegen/machine_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,20 @@ namespace rat {
static MachineOperand frameSlot(I32 s, U32 w = 8);
static MachineOperand symbol(const String& s);
static MachineOperand blockRef(I32 b);
static MachineOperand none(U32 w = 8);
static MachineOperand make(Kind k, U32 w, U32 v);

B32 isVReg() const { return kind == Kind::VReg; }
B32 isPhys() const { return kind == Kind::Phys; }
};

// a debug var inst lives in loc from here on, a None loc is optimized out
struct DbgRec {
U32 inst;
MachineOperand loc;
U32 next; // in MachineFunc::dbg, 0 ends the chain
};

using MachineDefs = SmallList<MachineOperand, 2>;
using MachineOperands = SmallList<MachineOperand, 3>;

Expand All @@ -65,6 +73,7 @@ namespace rat {
I64 imm = 0; // backend-defined small immediate
I64 imm2 = 0; // second backend-defined immediate
U32 loc = 0; // DebugBody loc id
U32 dbg = 0; // DbgRec chain applied before this instr
};

struct MachineBlock {
Expand All @@ -74,6 +83,9 @@ namespace rat {
SmallList<I32, 2> preds;
SmallList<I32, 2> succs;
List<MachineInstr> insts;
U32 dbgEnd = 0; // DbgRec chain applied past the last instr

U32& dbgBefore(U32 k); // the chain applied before instr k, dbgEnd past the last
};

struct MachineFuncAux {
Expand All @@ -87,8 +99,13 @@ namespace rat {
U32 frameBytes = 0;
List<PhysReg> usedCalleeSaved;
UniquePtr<MachineFuncAux> aux;
List<DbgRec> dbg; // chain storage, 0 unused

VReg newVReg(U32 cls);
// a chain is its head index in dbg, 0 empty. addDbg appends in place, joinDbg returns first then second
void addDbg(U32& chain, U32 inst, MachineOperand loc);
U32 joinDbg(U32 first, U32 second);
U32 copyDbg(U32 chain);
void reset(); // keeps the block storage
};
} // namespace rat
Expand Down
71 changes: 62 additions & 9 deletions src/backend/codegen/reg_alloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,13 @@ namespace rat {
number();
liveness();
buildIntervals();
B32 dbg = f.dbg.size() > 1;
if(dbg)
checkDebugLive();
coalesce();
assignRegs();
if(dbg)
rewriteDebug();
rewrite();
fn->usedCalleeSaved.clear();
for(U64 m = usedCallee; m; m &= m - 1)
Expand Down Expand Up @@ -251,6 +256,46 @@ namespace rat {
chunk[s >> 6] |= busy[s];
}

B32 RegAllocPass::covers(VReg v, I32 pos) const {
const List<Seg>& segs = iv[v].segs;
auto it = std::upper_bound(segs.begin(), segs.end(), Seg{pos, INT32_MAX});
return it != segs.begin() && (it - 1)->second >= pos;
}

// a record holds when its vreg is live right before the instr, or was just written by the one ahead,
// before bundles merge. stack resident vregs keep their slot
B32 RegAllocPass::liveAt(const MachineOperand& o, U32 b, U32 k) const {
I32 u = 2 * (I32)(blockFirst[b] + k);
B32 last = k == fn->blocks[b].insts.size();
return cross[o.vreg] || (!last && covers(o.vreg, u)) || (k > 0 && covers(o.vreg, u - 1));
}

void RegAllocPass::checkDebugLive() {
for(U32 b = 0; b < fn->blocks.size(); ++b) {
MachineBlock& blk = fn->blocks[b];
for(U32 k = 0; k <= blk.insts.size(); ++k) {
for(U32 r = blk.dbgBefore(k); r; r = fn->dbg[r].next) {
MachineOperand& o = fn->dbg[r].loc;
if(o.isVReg() && !liveAt(o, b, k))
o = MachineOperand::none(o.width);
}
}
}
}

// spilled bundles are found in their slot
void RegAllocPass::rewriteDebug() {
for(DbgRec& r : fn->dbg) {
if(!r.loc.isVReg())
continue;
const Interval& t = bundle(r.loc.vreg);
if(t.reg != kNoReg)
r.loc = MachineOperand::fixed(t.reg, r.loc.width);
else
r.loc = MachineOperand::frameSlot(t.slot, r.loc.width);
}
}

// path halving
VReg RegAllocPass::find(VReg v) {
while(iv[v].root != v)
Expand Down Expand Up @@ -485,21 +530,29 @@ namespace rat {
return bundle(o.vreg).reg;
}

// copies inside a bundle vanish
// copies inside a bundle vanish, debug records move to the first instr standing for theirs
void RegAllocPass::rewrite() {
List<MachineInstr> out;
for(U32 b = 0; b < fn->blocks.size(); ++b) {
List<MachineInstr>& insts = fn->blocks[b].insts;
MachineBlock& blk = fn->blocks[b];
out.clear();
out.reserve(insts.size());
for(U32 k = 0; k < insts.size(); ++k) {
MachineInstr& in = insts[k];
out.reserve(blk.insts.size());
U32 dbg = 0;
for(U32 k = 0; k < blk.insts.size(); ++k) {
MachineInstr& in = blk.insts[k];
U32 i = blockFirst[b] + k;
if(copyAt[i] && (sameBundle(in.defs[0], in.uses[0]) || rewriteCopy(out, in, i)))
continue;
rewriteInstr(out, in, i);
U32 first = (U32)out.size();
dbg = fn->joinDbg(dbg, in.dbg);
in.dbg = 0;
if(!copyAt[i] || !(sameBundle(in.defs[0], in.uses[0]) || rewriteCopy(out, in, i)))
rewriteInstr(out, in, i);
if(out.size() > first) {
out[first].dbg = dbg;
dbg = 0;
}
}
insts.swap(out);
blk.dbgEnd = fn->joinDbg(dbg, blk.dbgEnd);
blk.insts.swap(out);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/backend/codegen/reg_alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ namespace rat {
void assignSlots(List<Pair<I32, VReg>>& spilled);
I32 newSlot(U32 cls);
PhysReg pick(VReg v) const;
// debug records
B32 covers(VReg v, I32 pos) const;
B32 liveAt(const MachineOperand& o, U32 b, U32 k) const;
void checkDebugLive();
void rewriteDebug();
// rewrite
void rewrite();
B32 rewriteCopy(List<MachineInstr>& out, const MachineInstr& in, U32 i);
Expand Down
8 changes: 8 additions & 0 deletions src/backend/codegen/schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,14 @@ namespace rat {

I32 Schedule::blockOf(const Node* n) const { return n ? nodeBlock[n->getId()] : -1; }

// a head starts its block, a call or asm control proj follows its producer
I32 Schedule::anchorBlock(const Node* ctrl) const {
if(I32 b = blockOfHead(ctrl); b >= 0)
return b;
const ProjNode* p = dyn_cast<ProjNode>(ctrl);
return p ? blockOf(p->getProducer()) : -1;
}

NodeSpan Schedule::phis(I32 b) const {
return NodeSpan{phiFlat.data() + phiStart[b], (U32)(phiStart[b + 1] - phiStart[b])};
}
Expand Down
1 change: 1 addition & 0 deletions src/backend/codegen/schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ namespace rat {
const List<Node*>& allocNodes() const;

I32 blockOf(const Node* n) const;
I32 anchorBlock(const Node* ctrl) const;

U32 succCount(I32 b) const;
I32 succAt(I32 b, U32 i) const;
Expand Down
48 changes: 48 additions & 0 deletions src/backend/ir/debug_info.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "ir/debug_info.h"

#include "ir/node.h"

namespace rat {
DebugType* DebugInfo::makeType(DebugType::Kind kind, String name, U32 size) {
types.push_back(std::make_unique<DebugType>());
Expand All @@ -19,4 +21,50 @@ namespace rat {
locs.push_back({src, scope});
return (U32)locs.size() - 1;
}
// a constant keeps its value wherever it sits, so it needs no link
void DebugBody::link(Node* n, U32 binding) {
if(!n || isa<ConstantNode>(n))
return;
U32 id = n->getId();
if(id >= refHead.size())
refHead.resize(std::max(id + 1, (U32)refHead.size() * 2), 0);
refs.push_back({binding, refHead[id]});
refHead[id] = (U32)refs.size();
}

void DebugBody::bind(U32 inst, Node* value, Node* anchor, Node* after) {
U32 id = (U32)bindings.size();
bindings.push_back({inst, value, anchor, after});
link(value, id);
link(anchor, id);
link(after, id);
}

void DebugBody::mergeArm(Node* arm, Node* to) {
U32 id = arm->getId();
for(U32 r = id < refHead.size() ? refHead[id] : 0; r; r = refs[r - 1].next)
if(bindings[refs[r - 1].binding].anchor == arm)
bindings[refs[r - 1].binding].value = nullptr;
retarget(arm, to);
}

void DebugBody::retarget(Node* from, Node* to) {
U32 id = from->getId();
if(id >= refHead.size())
return;
U32 r = refHead[id];
refHead[id] = 0;
for(; r; r = refs[r - 1].next) {
U32 binding = refs[r - 1].binding;
DebugBinding& b = bindings[binding];
B32 moved = false;
for(Node** slot : {&b.value, &b.anchor, &b.after})
if(*slot == from) {
*slot = to;
moved = true;
}
if(moved)
link(to, binding);
}
}
} // namespace rat
25 changes: 25 additions & 0 deletions src/backend/ir/debug_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace rat {
struct AllocNode;
struct Node;

struct SrcLoc {
U32 file = 0; // DebugInfo::files
Expand Down Expand Up @@ -89,6 +90,16 @@ namespace rat {
const DebugVar* var;
U32 scope = 0;
AllocNode* slot = nullptr; // frame storage
B32 indirect = false; // bound values are its address
};

// var inst holds value from where control reaches anchor on, past the store or call after, a null
// value is optimized out
struct DebugBinding {
U32 inst;
Node* value;
Node* anchor;
Node* after; // the last effect made before it, null = none
};

// debug state of one IR function, inlining copies it into the caller
Expand All @@ -97,8 +108,22 @@ namespace rat {
List<DebugLoc> locs = List<DebugLoc>(1); // 0 = no location
List<DebugScope> scopes = List<DebugScope>(1); // 0 = the body
List<DebugVarInst> vars;
List<DebugBinding> bindings;
// bindings naming a node, linked per node id, constants are never relinked
struct Ref {
U32 binding;
U32 next; // + 1, 0 ends
};
List<U32> refHead; // node id -> first ref + 1
List<Ref> refs;

U32 addLoc(SrcLoc src, U32 scope);
void bind(U32 inst, Node* value, Node* anchor, Node* after);
// moves the bindings of from onto to, null drops them
void retarget(Node* from, Node* to);
// bindings anchored at a branch arm merged into to hold on one path only, they lose their value
void mergeArm(Node* arm, Node* to);
void link(Node* n, U32 binding);
};

struct DebugInfo {
Expand Down
Loading
Loading