backend/arm64/reg_alloc: Support multiple locks on a location
This commit is contained in:
parent
6bcfaee1f4
commit
bf55920ce9
3 changed files with 53 additions and 35 deletions
|
@ -152,6 +152,8 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E
|
||||||
ASSERT_FALSE("Invalid opcode: {}", inst->GetOpcode());
|
ASSERT_FALSE("Invalid opcode: {}", inst->GetOpcode());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reg_alloc.AssertAllUnlocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_alloc.AssertNoMoreUses();
|
reg_alloc.AssertNoMoreUses();
|
||||||
|
|
|
@ -91,15 +91,16 @@ bool HostLocInfo::Contains(const IR::Inst* value) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostLocInfo::SetupScratchLocation() {
|
void HostLocInfo::SetupScratchLocation() {
|
||||||
ASSERT(values.empty());
|
ASSERT(IsCompletelyEmpty());
|
||||||
locked = true;
|
locked++;
|
||||||
realized = true;
|
realized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostLocInfo::SetupLocation(const IR::Inst* value) {
|
void HostLocInfo::SetupLocation(const IR::Inst* value) {
|
||||||
|
ASSERT(IsCompletelyEmpty());
|
||||||
values.clear();
|
values.clear();
|
||||||
values.emplace_back(value);
|
values.emplace_back(value);
|
||||||
locked = true;
|
locked++;
|
||||||
realized = true;
|
realized = true;
|
||||||
uses_this_inst = 0;
|
uses_this_inst = 0;
|
||||||
accumulated_uses = 0;
|
accumulated_uses = 0;
|
||||||
|
@ -123,10 +124,9 @@ void HostLocInfo::UpdateUses() {
|
||||||
uses_this_inst = 0;
|
uses_this_inst = 0;
|
||||||
|
|
||||||
if (accumulated_uses == expected_uses) {
|
if (accumulated_uses == expected_uses) {
|
||||||
*this = {};
|
values.clear();
|
||||||
} else {
|
accumulated_uses = 0;
|
||||||
realized = false;
|
expected_uses = 0;
|
||||||
locked = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +187,16 @@ void RegAlloc::DefineAsRegister(IR::Inst* inst, oaknut::Reg reg) {
|
||||||
ASSERT(!ValueLocation(inst));
|
ASSERT(!ValueLocation(inst));
|
||||||
auto& info = reg.is_vector() ? fprs[reg.index()] : gprs[reg.index()];
|
auto& info = reg.is_vector() ? fprs[reg.index()] : gprs[reg.index()];
|
||||||
ASSERT(info.IsCompletelyEmpty());
|
ASSERT(info.IsCompletelyEmpty());
|
||||||
info.SetupLocation(inst);
|
info.values.emplace_back(inst);
|
||||||
|
info.expected_uses += inst->UseCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegAlloc::AssertAllUnlocked() const {
|
||||||
|
const auto is_unlocked = [](const auto& i) { return !i.locked && !i.realized; };
|
||||||
|
ASSERT(std::all_of(gprs.begin(), gprs.end(), is_unlocked));
|
||||||
|
ASSERT(std::all_of(fprs.begin(), fprs.end(), is_unlocked));
|
||||||
|
ASSERT(is_unlocked(flags));
|
||||||
|
ASSERT(std::all_of(spills.begin(), spills.end(), is_unlocked));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::AssertNoMoreUses() const {
|
void RegAlloc::AssertNoMoreUses() const {
|
||||||
|
@ -329,12 +338,6 @@ template int RegAlloc::RealizeWriteImpl<HostLoc::Kind::Gpr>(const IR::Inst* valu
|
||||||
template int RegAlloc::RealizeWriteImpl<HostLoc::Kind::Fpr>(const IR::Inst* value);
|
template int RegAlloc::RealizeWriteImpl<HostLoc::Kind::Fpr>(const IR::Inst* value);
|
||||||
template int RegAlloc::RealizeWriteImpl<HostLoc::Kind::Flags>(const IR::Inst* value);
|
template int RegAlloc::RealizeWriteImpl<HostLoc::Kind::Flags>(const IR::Inst* value);
|
||||||
|
|
||||||
void RegAlloc::Unlock(HostLoc host_loc) {
|
|
||||||
HostLocInfo& info = ValueInfo(host_loc);
|
|
||||||
ASSERT(info.locked && info.realized);
|
|
||||||
info.UpdateUses();
|
|
||||||
}
|
|
||||||
|
|
||||||
int RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs, const std::vector<int>& order) const {
|
int RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs, const std::vector<int>& order) const {
|
||||||
const auto empty = std::find_if(order.begin(), order.end(), [&](int i) { return regs[i].IsImmediatelyAllocatable(); });
|
const auto empty = std::find_if(order.begin(), order.end(), [&](int i) { return regs[i].IsImmediatelyAllocatable(); });
|
||||||
if (empty != order.end()) {
|
if (empty != order.end()) {
|
||||||
|
@ -392,6 +395,8 @@ void RegAlloc::ReadWriteFlags(Argument& read, IR::Inst* write) {
|
||||||
ASSERT_FALSE("Invalid current location for flags");
|
ASSERT_FALSE("Invalid current location for flags");
|
||||||
}
|
}
|
||||||
flags.SetupLocation(write);
|
flags.SetupLocation(write);
|
||||||
|
flags.locked--;
|
||||||
|
flags.realized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::SpillFlags() {
|
void RegAlloc::SpillFlags() {
|
||||||
|
|
|
@ -102,8 +102,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class RegAlloc;
|
friend class RegAlloc;
|
||||||
explicit RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value)
|
explicit RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value);
|
||||||
: reg_alloc{reg_alloc}, write{write}, value{value} {}
|
|
||||||
|
|
||||||
RAReg(const RAReg&) = delete;
|
RAReg(const RAReg&) = delete;
|
||||||
RAReg& operator=(const RAReg&) = delete;
|
RAReg& operator=(const RAReg&) = delete;
|
||||||
|
@ -120,7 +119,7 @@ private:
|
||||||
|
|
||||||
struct HostLocInfo {
|
struct HostLocInfo {
|
||||||
std::vector<const IR::Inst*> values;
|
std::vector<const IR::Inst*> values;
|
||||||
bool locked = false;
|
size_t locked = 0;
|
||||||
bool realized = false;
|
bool realized = false;
|
||||||
size_t uses_this_inst = 0;
|
size_t uses_this_inst = 0;
|
||||||
size_t accumulated_uses = 0;
|
size_t accumulated_uses = 0;
|
||||||
|
@ -145,16 +144,16 @@ public:
|
||||||
ArgumentInfo GetArgumentInfo(IR::Inst* inst);
|
ArgumentInfo GetArgumentInfo(IR::Inst* inst);
|
||||||
bool IsValueLive(IR::Inst* inst) const;
|
bool IsValueLive(IR::Inst* inst) const;
|
||||||
|
|
||||||
auto ReadX(Argument& arg) { return RAReg<oaknut::XReg>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadX(Argument& arg) { return RAReg<oaknut::XReg>{*this, false, arg.value}; }
|
||||||
auto ReadW(Argument& arg) { return RAReg<oaknut::WReg>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadW(Argument& arg) { return RAReg<oaknut::WReg>{*this, false, arg.value}; }
|
||||||
|
|
||||||
auto ReadQ(Argument& arg) { return RAReg<oaknut::QReg>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadQ(Argument& arg) { return RAReg<oaknut::QReg>{*this, false, arg.value}; }
|
||||||
auto ReadD(Argument& arg) { return RAReg<oaknut::DReg>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadD(Argument& arg) { return RAReg<oaknut::DReg>{*this, false, arg.value}; }
|
||||||
auto ReadS(Argument& arg) { return RAReg<oaknut::SReg>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadS(Argument& arg) { return RAReg<oaknut::SReg>{*this, false, arg.value}; }
|
||||||
auto ReadH(Argument& arg) { return RAReg<oaknut::HReg>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadH(Argument& arg) { return RAReg<oaknut::HReg>{*this, false, arg.value}; }
|
||||||
auto ReadB(Argument& arg) { return RAReg<oaknut::BReg>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadB(Argument& arg) { return RAReg<oaknut::BReg>{*this, false, arg.value}; }
|
||||||
|
|
||||||
auto ReadFlags(Argument& arg) { return RAReg<FlagsTag>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadFlags(Argument& arg) { return RAReg<FlagsTag>{*this, false, arg.value}; }
|
||||||
|
|
||||||
template<size_t size>
|
template<size_t size>
|
||||||
auto ReadReg(Argument& arg) {
|
auto ReadReg(Argument& arg) {
|
||||||
|
@ -242,6 +241,7 @@ public:
|
||||||
(rs.Realize(), ...);
|
(rs.Realize(), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssertAllUnlocked() const;
|
||||||
void AssertNoMoreUses() const;
|
void AssertNoMoreUses() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -249,20 +249,12 @@ private:
|
||||||
template<typename>
|
template<typename>
|
||||||
friend struct RAReg;
|
friend struct RAReg;
|
||||||
|
|
||||||
const IR::Value& PreReadImpl(const IR::Value& value) {
|
|
||||||
if (!value.IsImmediate()) {
|
|
||||||
ValueInfo(value.GetInst()).locked = true;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<HostLoc::Kind kind>
|
template<HostLoc::Kind kind>
|
||||||
int GenerateImmediate(const IR::Value& value);
|
int GenerateImmediate(const IR::Value& value);
|
||||||
template<HostLoc::Kind kind>
|
template<HostLoc::Kind kind>
|
||||||
int RealizeReadImpl(const IR::Value& value);
|
int RealizeReadImpl(const IR::Value& value);
|
||||||
template<HostLoc::Kind kind>
|
template<HostLoc::Kind kind>
|
||||||
int RealizeWriteImpl(const IR::Inst* value);
|
int RealizeWriteImpl(const IR::Inst* value);
|
||||||
void Unlock(HostLoc host_loc);
|
|
||||||
|
|
||||||
int AllocateRegister(const std::array<HostLocInfo, 32>& regs, const std::vector<int>& order) const;
|
int AllocateRegister(const std::array<HostLocInfo, 32>& regs, const std::vector<int>& order) const;
|
||||||
void SpillGpr(int index);
|
void SpillGpr(int index);
|
||||||
|
@ -287,10 +279,29 @@ private:
|
||||||
mutable std::mt19937 rand_gen;
|
mutable std::mt19937 rand_gen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
RAReg<T>::RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value)
|
||||||
|
: reg_alloc{reg_alloc}, write{write}, value{value} {
|
||||||
|
if (!write && !value.IsImmediate()) {
|
||||||
|
reg_alloc.ValueInfo(value.GetInst()).locked++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
RAReg<T>::~RAReg() {
|
RAReg<T>::~RAReg() {
|
||||||
if (reg) {
|
if (value.IsImmediate()) {
|
||||||
reg_alloc.Unlock(HostLoc{kind, reg->index()});
|
if (reg) {
|
||||||
|
// Immediate in scratch register
|
||||||
|
HostLocInfo& info = reg_alloc.ValueInfo(HostLoc{kind, reg->index()});
|
||||||
|
info.locked--;
|
||||||
|
info.realized = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HostLocInfo& info = reg_alloc.ValueInfo(value.GetInst());
|
||||||
|
info.locked--;
|
||||||
|
if (reg) {
|
||||||
|
info.realized = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue