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;
72 RegBitSet clobbered = 0;
73 std::array<u8, NumBanks> clocks{};
76 ValLocalIdx local_idx;
80 std::array<Assignment, NumRegs> assignments;
82 std::array<u8, NumRegs> lock_counts{};
84 void reset() noexcept {
92 [[nodiscard]]
bool is_used(
const Reg reg)
const noexcept {
93 assert(reg.id() < NumRegs);
94 return (used & 1ull << reg.id()) != 0;
97 [[nodiscard]]
bool is_fixed(
const Reg reg)
const noexcept {
98 assert(reg.id() < NumRegs);
99 return (fixed & 1ull << reg.id()) != 0;
102 [[nodiscard]]
bool is_clobbered(
const Reg reg)
const noexcept {
103 assert(reg.id() < NumRegs);
104 return (clobbered & 1ull << reg.id()) != 0;
107 void mark_used(
const Reg reg,
108 const ValLocalIdx local_idx,
109 const u32 part)
noexcept {
110 assert(reg.id() < NumRegs);
111 assert(!is_used(reg));
112 assert(!is_fixed(reg));
113 assert(lock_counts[reg.id()] == 0);
114 used |= (1ull << reg.id());
115 assignments[reg.id()] = Assignment{.local_idx = local_idx, .part = part};
118 void update_reg_assignment(
const Reg reg,
119 const ValLocalIdx local_idx,
120 const u32 part)
noexcept {
121 assert(is_used(reg));
122 assignments[reg.id()].local_idx = local_idx;
123 assignments[reg.id()].part = part;
126 void unmark_used(
const Reg reg)
noexcept {
127 assert(reg.id() < NumRegs);
128 assert(is_used(reg));
129 assert(!is_fixed(reg));
130 assert(lock_counts[reg.id()] == 0);
131 used &= ~(1ull << reg.id());
134 void mark_fixed(
const Reg reg)
noexcept {
135 assert(reg.id() < NumRegs);
136 assert(is_used(reg));
137 fixed |= (1ull << reg.id());
140 void unmark_fixed(
const Reg reg)
noexcept {
141 assert(reg.id() < NumRegs);
142 assert(is_used(reg));
143 assert(is_fixed(reg));
144 fixed &= ~(1ull << reg.id());
147 void inc_lock_count(
const Reg reg)
noexcept {
148 assert(reg.id() < NumRegs);
149 assert(is_used(reg));
151 ++lock_counts[reg.id()];
155 bool dec_lock_count(
const Reg reg)
noexcept {
156 assert(reg.id() < NumRegs);
157 assert(is_used(reg));
158 assert(lock_counts[reg.id()] > 0);
159 if (--lock_counts[reg.id()] == 0) {
167 void dec_lock_count_must_zero(
const Reg reg)
noexcept {
168 assert(reg.id() < NumRegs);
169 assert(is_used(reg));
170 assert(lock_counts[reg.id()] == 1);
171 lock_counts[reg.id()] = 0;
175 void mark_clobbered(
const Reg reg)
noexcept {
176 assert(reg.id() < NumRegs);
177 clobbered |= (1ull << reg.id());
180 [[nodiscard]] ValLocalIdx reg_local_idx(
const Reg reg)
const noexcept {
181 assert(is_used(reg));
182 return assignments[reg.id()].local_idx;
185 [[nodiscard]] u32 reg_part(
const Reg reg)
const noexcept {
186 assert(is_used(reg));
187 return assignments[reg.id()].part;
190 [[nodiscard]] util::BitSetIterator<> used_regs() const noexcept {
191 return util::BitSetIterator<>{used};
194 [[nodiscard]] util::BitSetIterator<> used_nonfixed_regs() const noexcept {
195 return util::BitSetIterator<>{used & ~fixed};
199 find_first_free_excluding(
const RegBank bank,
200 const u64 exclusion_mask)
const noexcept {
202 const RegBitSet free_bank = allocatable & ~used & bank_regs(bank);
203 const RegBitSet selectable = free_bank & ~exclusion_mask;
204 if (selectable == 0) {
205 return Reg::make_invalid();
207 return Reg{
static_cast<u8
>(util::cnt_tz(selectable))};
211 find_first_nonfixed_excluding(
const RegBank bank,
212 const u64 exclusion_mask)
const noexcept {
214 const RegBitSet allocatable_bank = allocatable & ~fixed & bank_regs(bank);
215 const RegBitSet selectable = allocatable_bank & ~exclusion_mask;
216 if (selectable == 0) {
217 return Reg::make_invalid();
219 return Reg{util::cnt_tz(selectable)};
223 find_clocked_nonfixed_excluding(
const RegBank bank,
224 const u64 exclusion_mask)
noexcept {
225 const RegBitSet allocatable_bank = allocatable & ~fixed & bank_regs(bank);
226 const RegBitSet selectable = allocatable_bank & ~exclusion_mask;
227 if (selectable == 0) {
228 return Reg::make_invalid();
231 auto clock = clocks[bank.id()];
232 const u64 reg_id = (selectable >> clock)
233 ? util::cnt_tz(selectable >> clock) + clock
234 : util::cnt_tz(selectable);
238 if (clock >= RegsPerBank) {
241 clocks[bank.id()] = clock;
243 return Reg{
static_cast<u8
>(reg_id)};
246 [[nodiscard]]
static RegBank reg_bank(
const Reg reg)
noexcept {
247 return RegBank(reg.id() / RegsPerBank);
250 [[nodiscard]]
static RegBitSet bank_regs(
const RegBank bank)
noexcept {
251 assert(bank.id() <= 1);
252 return ((1ull << RegsPerBank) - 1) << (bank.id() * RegsPerBank);