6#include "tpde/ValLocalIdx.hpp"
7#include "tpde/base.hpp"
8#include "tpde/util/misc.hpp"
17 explicit constexpr Reg(
const u64
id) noexcept : reg_id(
static_cast<u8
>(
id)) {
21 constexpr u8 id() const noexcept {
return reg_id; }
23 constexpr bool invalid() const noexcept {
return reg_id == 0xFF; }
25 constexpr bool valid() const noexcept {
return reg_id != 0xFF; }
27 constexpr static Reg make_invalid() noexcept {
return Reg{(u8)0xFF}; }
29 constexpr bool operator==(
const Reg &other)
const noexcept {
30 return reg_id == other.reg_id;
39 constexpr RegBank() noexcept : bank(u8(-1)) {}
41 constexpr explicit RegBank(u8 bank) noexcept : bank(bank) {}
43 constexpr u8 id() const noexcept {
return bank; }
45 constexpr bool operator==(
const RegBank &other)
const noexcept {
46 return bank == other.bank;
50template <
unsigned NumBanks,
unsigned RegsPerBank>
53 static constexpr unsigned NumRegs = NumBanks * RegsPerBank;
55 static_assert(RegsPerBank > 0 && (RegsPerBank & (RegsPerBank - 1)) == 0,
56 "RegsPerBank must be a power of two");
57 static_assert(NumRegs < Reg::make_invalid().id());
58 static_assert(NumRegs <= 64);
62 using RegBitSet = u64;
65 RegBitSet allocatable = 0;
70 RegBitSet clobbered = 0;
71 std::array<u8, NumBanks> clocks{};
74 ValLocalIdx local_idx;
78 std::array<Assignment, NumRegs> assignments;
80 std::array<u8, NumRegs> lock_counts{};
82 void reset() noexcept {
89 [[nodiscard]]
bool is_used(
const Reg reg)
const noexcept {
90 assert(reg.id() < NumRegs);
91 return (used & 1ull << reg.id()) != 0;
94 [[nodiscard]]
bool is_fixed(
const Reg reg)
const noexcept {
95 assert(reg.id() < NumRegs);
96 return lock_counts[reg.id()] > 0;
99 [[nodiscard]]
bool is_clobbered(
const Reg reg)
const noexcept {
100 assert(reg.id() < NumRegs);
101 return (clobbered & 1ull << reg.id()) != 0;
104 void mark_used(
const Reg reg,
105 const ValLocalIdx local_idx,
106 const u32 part)
noexcept {
107 assert(reg.id() < NumRegs);
108 assert(!is_used(reg));
109 assert(!is_fixed(reg));
110 assert(lock_counts[reg.id()] == 0);
111 used |= (1ull << reg.id());
112 assignments[reg.id()] = Assignment{.local_idx = local_idx, .part = part};
115 void update_reg_assignment(
const Reg reg,
116 const ValLocalIdx local_idx,
117 const u32 part)
noexcept {
118 assert(is_used(reg));
119 assignments[reg.id()].local_idx = local_idx;
120 assignments[reg.id()].part = part;
123 void unmark_used(
const Reg reg)
noexcept {
124 assert(reg.id() < NumRegs);
125 assert(is_used(reg));
126 assert(!is_fixed(reg));
127 assert(lock_counts[reg.id()] == 0);
128 used &= ~(1ull << reg.id());
131 void mark_fixed(
const Reg reg)
noexcept {
132 assert(reg.id() < NumRegs);
133 assert(is_used(reg));
134 assert(lock_counts[reg.id()] == 0);
135 lock_counts[reg.id()] = 1;
138 void unmark_fixed(
const Reg reg)
noexcept {
139 assert(reg.id() < NumRegs);
140 assert(is_used(reg));
141 assert(is_fixed(reg));
142 assert(lock_counts[reg.id()] == 1);
143 lock_counts[reg.id()] = 0;
146 void inc_lock_count(
const Reg reg)
noexcept {
147 assert(reg.id() < NumRegs);
148 assert(is_used(reg));
149 ++lock_counts[reg.id()];
153 bool dec_lock_count(
const Reg reg)
noexcept {
154 assert(reg.id() < NumRegs);
155 assert(is_used(reg));
156 assert(lock_counts[reg.id()] > 0);
157 if (--lock_counts[reg.id()] == 0) {
164 void dec_lock_count_must_zero(
const Reg reg,
165 [[maybe_unused]] u8 sub = 1) noexcept {
166 assert(reg.id() < NumRegs);
167 assert(is_used(reg));
168 assert(lock_counts[reg.id()] == sub);
169 lock_counts[reg.id()] = 0;
172 void mark_clobbered(
const Reg reg)
noexcept {
173 assert(reg.id() < NumRegs);
174 clobbered |= (1ull << reg.id());
177 [[nodiscard]] ValLocalIdx reg_local_idx(
const Reg reg)
const noexcept {
178 assert(is_used(reg));
179 return assignments[reg.id()].local_idx;
182 [[nodiscard]] u32 reg_part(
const Reg reg)
const noexcept {
183 assert(is_used(reg));
184 return assignments[reg.id()].part;
187 [[nodiscard]] util::BitSetIterator<> used_regs() const noexcept {
188 return util::BitSetIterator<>{used};
192 find_first_free_excluding(
const RegBank bank,
193 const u64 exclusion_mask)
const noexcept {
195 const RegBitSet free_bank = allocatable & ~used & bank_regs(bank);
196 const RegBitSet selectable = free_bank & ~exclusion_mask;
197 if (selectable == 0) {
198 return Reg::make_invalid();
200 return Reg{
static_cast<u8
>(util::cnt_tz(selectable))};
204 find_first_nonfixed_excluding(
const RegBank bank,
205 const u64 exclusion_mask)
const noexcept {
207 for (
auto reg_id : util::BitSetIterator<>{used & bank_regs(bank)}) {
208 if (!is_fixed(Reg{reg_id}) && !((u64{1} << reg_id) & exclusion_mask)) {
212 return Reg::make_invalid();
215 [[nodiscard]]
static RegBank reg_bank(
const Reg reg)
noexcept {
216 return RegBank(reg.id() / RegsPerBank);
219 [[nodiscard]]
static RegBitSet bank_regs(
const RegBank bank)
noexcept {
220 assert(bank.id() <= 1);
221 return ((1ull << RegsPerBank) - 1) << (bank.id() * RegsPerBank);