6#include "tpde/ValLocalIdx.hpp"
7#include "tpde/base.hpp"
8#include "tpde/util/misc.hpp"
17 explicit constexpr Reg(
const u64
id) : reg_id(static_cast<u8>(id)) {
21 constexpr u8 id()
const {
return reg_id; }
23 constexpr bool invalid()
const {
return reg_id == 0xFF; }
25 constexpr bool valid()
const {
return reg_id != 0xFF; }
27 constexpr static Reg make_invalid() {
return Reg{(u8)0xFF}; }
29 constexpr bool operator==(
const Reg &other)
const {
30 return reg_id == other.reg_id;
39 constexpr RegBank() : bank(u8(-1)) {}
41 constexpr explicit RegBank(u8 bank) : bank(bank) {}
43 constexpr u8 id()
const {
return bank; }
45 constexpr bool operator==(
const RegBank &other)
const {
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{};
89 [[nodiscard]]
bool is_used(
const Reg reg)
const {
90 assert(reg.id() < NumRegs);
91 return (used & 1ull << reg.id()) != 0;
94 [[nodiscard]]
bool is_fixed(
const Reg reg)
const {
95 assert(reg.id() < NumRegs);
96 return lock_counts[reg.id()] > 0;
99 [[nodiscard]]
bool is_clobbered(
const Reg reg)
const {
100 assert(reg.id() < NumRegs);
101 return (clobbered & 1ull << reg.id()) != 0;
104 void mark_used(
const Reg reg,
const ValLocalIdx local_idx,
const u32 part) {
105 assert(reg.id() < NumRegs);
106 assert(!is_used(reg));
107 assert(!is_fixed(reg));
108 assert(lock_counts[reg.id()] == 0);
109 used |= (1ull << reg.id());
110 assignments[reg.id()] = Assignment{.local_idx = local_idx, .part = part};
113 void update_reg_assignment(
const Reg reg,
114 const ValLocalIdx local_idx,
116 assert(is_used(reg));
117 assignments[reg.id()].local_idx = local_idx;
118 assignments[reg.id()].part = part;
121 void unmark_used(
const Reg reg) {
122 assert(reg.id() < NumRegs);
123 assert(is_used(reg));
124 assert(!is_fixed(reg));
125 assert(lock_counts[reg.id()] == 0);
126 used &= ~(1ull << reg.id());
129 void mark_fixed(
const Reg reg) {
130 assert(reg.id() < NumRegs);
131 assert(is_used(reg));
132 assert(lock_counts[reg.id()] == 0);
133 lock_counts[reg.id()] = 1;
136 void unmark_fixed(
const Reg reg) {
137 assert(reg.id() < NumRegs);
138 assert(is_used(reg));
139 assert(is_fixed(reg));
140 assert(lock_counts[reg.id()] == 1);
141 lock_counts[reg.id()] = 0;
144 void inc_lock_count(
const Reg reg) {
145 assert(reg.id() < NumRegs);
146 assert(is_used(reg));
147 ++lock_counts[reg.id()];
151 bool dec_lock_count(
const Reg reg) {
152 assert(reg.id() < NumRegs);
153 assert(is_used(reg));
154 assert(lock_counts[reg.id()] > 0);
155 if (--lock_counts[reg.id()] == 0) {
162 void dec_lock_count_must_zero(
const Reg reg, [[maybe_unused]] u8 sub = 1) {
163 assert(reg.id() < NumRegs);
164 assert(is_used(reg));
165 assert(lock_counts[reg.id()] == sub);
166 lock_counts[reg.id()] = 0;
169 void mark_clobbered(
const Reg reg) {
170 assert(reg.id() < NumRegs);
171 clobbered |= (1ull << reg.id());
174 [[nodiscard]] ValLocalIdx reg_local_idx(
const Reg reg)
const {
175 assert(is_used(reg));
176 return assignments[reg.id()].local_idx;
179 [[nodiscard]] u32 reg_part(
const Reg reg)
const {
180 assert(is_used(reg));
181 return assignments[reg.id()].part;
184 [[nodiscard]] util::BitSetIterator<> used_regs()
const {
185 return util::BitSetIterator<>{used};
188 [[nodiscard]] Reg find_first_free_excluding(
const RegBank bank,
189 const u64 exclusion_mask)
const {
191 const RegBitSet free_bank = allocatable & ~used & bank_regs(bank);
192 const RegBitSet selectable = free_bank & ~exclusion_mask;
193 if (selectable == 0) {
194 return Reg::make_invalid();
196 return Reg{
static_cast<u8
>(util::cnt_tz(selectable))};
200 find_first_nonfixed_excluding(
const RegBank bank,
201 const u64 exclusion_mask)
const {
203 for (
auto reg_id : util::BitSetIterator<>{used & bank_regs(bank)}) {
204 if (!is_fixed(Reg{reg_id}) && !((u64{1} << reg_id) & exclusion_mask)) {
208 return Reg::make_invalid();
211 [[nodiscard]]
static RegBank reg_bank(
const Reg reg) {
212 return RegBank(reg.id() / RegsPerBank);
215 [[nodiscard]]
static RegBitSet bank_regs(
const RegBank bank) {
216 assert(bank.id() <= 1);
217 return ((1ull << RegsPerBank) - 1) << (bank.id() * RegsPerBank);