6#include "tpde/ValueAssignment.hpp"
13template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
17 AsmReg reg = AsmReg::make_invalid();
18 bool has_assignment =
false;
21 bool const_inline : 1;
31 AsmReg reg = AsmReg::make_invalid();
32 bool has_assignment =
true;
34 ValLocalIdx local_idx;
36 ValueAssignment *assignment;
45 ValuePart() : state{ConstantData{.is_const = false}} {}
47 ValuePart(RegBank bank)
49 ConstantData{.is_const = false, .bank = bank}
51 assert(bank.id() < Config::NUM_BANKS);
54 ValuePart(ValLocalIdx local_idx,
55 ValueAssignment *assignment,
61 .local_idx = local_idx,
63 .assignment = assignment,
66 assert(this->assignment().variable_ref() ||
67 state.v.assignment->references_left);
68 assert(!owned || state.v.assignment->references_left == 1);
71 ValuePart(
const u64 *data, u32 size, RegBank bank)
73 .c = ConstantData{.is_const = true,
74 .const_inline = false,
79 assert(data &&
"constant data must not be null");
80 assert(bank.id() < Config::NUM_BANKS);
83 ValuePart(
const u64 val, u32 size, RegBank bank)
85 .c = ConstantData{.is_const = true,
91 assert(size <=
sizeof(val));
92 assert(bank.id() < Config::NUM_BANKS);
95 explicit ValuePart(
const ValuePart &) =
delete;
97 ValuePart(ValuePart &&other) : state{other.state} {
98 other.state.c = ConstantData{.is_const =
false, .bank = bank()};
102 assert(!state.c.reg.valid() &&
"must call reset() on ValuePart explicitly");
105 ValuePart &operator=(
const ValuePart &) =
delete;
107 ValuePart &operator=(ValuePart &&other) {
108 if (
this == &other) {
111 assert(!state.c.reg.valid() &&
"must call reset() on ValuePart explicitly");
112 this->state = other.state;
113 other.state.c = ConstantData{.is_const =
false, .bank = bank()};
117 bool has_assignment()
const {
return state.v.has_assignment; }
119 bool is_const()
const {
return !state.c.has_assignment && state.c.is_const; }
121 bool is_owned()
const {
122 assert(has_assignment());
123 return state.c.owned;
126 [[nodiscard]] AssignmentPartRef assignment()
const {
127 assert(has_assignment());
128 return AssignmentPartRef{state.v.assignment, state.v.part};
133 AsmReg cur_reg()
const {
134 assert(state.v.reg.valid());
140 AsmReg cur_reg_unlocked()
const {
141 if (state.v.reg.valid()) {
144 if (has_assignment()) {
145 if (
auto ap = assignment(); ap.register_valid()) {
149 return AsmReg::make_invalid();
153 bool is_in_reg(AsmReg reg)
const {
155 return cur_reg() == reg;
157 if (has_assignment()) {
158 auto ap = assignment();
159 return ap.register_valid() && ap.get_reg() == reg;
164 bool has_reg()
const {
return state.v.reg.valid(); }
167 template <
bool Reload>
169 AsmReg alloc_specific_impl(
CompilerBase *compiler, AsmReg reg,
bool reload);
175 alloc_reg_impl<
false>(compiler);
183 alloc_reg_impl<
false>(compiler);
203 AsmReg alloc_try_reuse(
CompilerBase *compiler, ValuePart &ref) {
204 assert(ref.has_reg());
205 if (!has_assignment() || !assignment().register_valid()) {
206 assert(!has_assignment() || !assignment().fixed_assignment());
207 if (ref.can_salvage()) {
208 set_value(compiler, std::move(ref));
209 if (has_assignment()) {
215 return alloc_reg(compiler);
223 void alloc_specific(
CompilerBase *compiler, AsmReg reg) {
224 alloc_specific_impl(compiler, reg,
false);
231 alloc_reg_impl<
true>(compiler);
242 void load_to_specific(
CompilerBase *compiler, AsmReg reg) {
243 alloc_specific_impl(compiler, reg,
true);
247 AsmReg reload_into_specific_fixed(
CompilerBase *compiler,
252 ValuePart get_unowned() {
254 ValuePart res{bank()};
256 ConstantData{.reg = cur_reg(), .owned =
false, .is_const =
false};
263 if (state.c.const_inline) {
264 ValuePart res{state.c.inline_data, state.c.size, state.c.bank};
265 res.load_to_reg(compiler);
268 ValuePart res{state.c.data, state.c.size, state.c.bank};
269 res.load_to_reg(compiler);
275 assert((has_assignment() || state.c.owned) &&
276 "into_temporary from unowned ValuePart not implemented");
277 ValuePart res{bank()};
278 res.set_value(compiler, std::move(*
this));
279 if (!res.has_reg()) [[unlikely]] {
280 assert(res.is_const());
281 res.load_to_reg(compiler);
289 assert((has_assignment() || state.c.owned || state.c.is_const) &&
290 "into_scratch from unowned ValuePart not implemented");
293 res.alloc_specific(salvage(compiler));
295 reload_into_specific_fixed(compiler, res.alloc(bank()));
303 into_extended(
CompilerBase *compiler,
bool sign, u32 from, u32 to) && {
304 assert(from < to &&
"invalid integer extension sizes");
305 if (is_const() && to <= 64) {
306 u64 val = const_data()[0];
307 u64 extended = sign ? util::sext(val, from) : util::zext(val, from);
308 return ValuePart{extended, (to + 7) / 8, state.c.bank};
310 ValuePart res{bank()};
311 Reg src_reg = has_reg() ? cur_reg() : load_to_reg(compiler);
313 res.set_value(compiler, std::move(*
this));
314 assert(src_reg == res.cur_reg());
316 res.alloc_reg(compiler);
318 compiler->derived()->generate_raw_intext(
319 res.cur_reg(), src_reg, sign, from, to);
326 void set_modified() {
327 assert(has_reg() && has_assignment());
328 assignment().set_modified(
true);
334 void set_value(
CompilerBase *compiler, ValuePart &&other);
346 bool can_salvage()
const {
347 if (!has_assignment()) {
348 return state.c.owned && state.c.reg.valid();
351 return state.v.owned && assignment().register_valid();
361 AsmReg reg = salvage_keep_used(compiler);
362 compiler->register_file.unmark_used(reg);
366 ValLocalIdx local_idx()
const {
367 assert(has_assignment());
368 return state.v.local_idx;
372 assert(has_assignment());
376 RegBank bank()
const {
377 return !has_assignment() ? state.c.bank : assignment().bank();
380 u32 part_size()
const {
381 return !has_assignment() ? state.c.size : assignment().part_size();
384 std::span<const u64> const_data()
const {
386 if (state.c.const_inline) {
387 return {&state.c.inline_data, 1};
389 return {state.c.data, (state.c.size + 7) / 8};
396template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
397template <
bool Reload>
398void CompilerBase<Adaptor, Derived, Config>::ValuePart::alloc_reg_impl(
403 assert(compiler->may_change_value_state());
404 assert(!state.c.reg.valid());
407 if (has_assignment()) {
408 auto ap = assignment();
409 if (ap.register_valid()) {
419 Reg reg = compiler->select_reg(bank);
420 auto ®_file = compiler->register_file;
421 reg_file.mark_clobbered(reg);
422 if (has_assignment()) {
423 reg_file.mark_used(reg, state.v.local_idx, state.v.part);
427 reg_file.mark_fixed(reg);
429 auto ap = assignment();
431 ap.set_register_valid(
true);
433 if constexpr (Reload) {
434 compiler->derived()->reload_to_reg(reg, ap);
436 assert(!ap.stack_valid() &&
"alloc_reg called on initialized value");
439 reg_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
440 reg_file.mark_fixed(reg);
442 state.c.owned =
true;
444 if constexpr (Reload) {
445 assert(is_const() &&
"cannot reload temporary value");
446 compiler->derived()->materialize_constant(
447 const_data().data(), state.c.bank, state.c.size, reg);
452template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
455 CompilerBase *compiler, AsmReg reg,
const bool reload) {
456 assert(!state.c.reg.valid());
458 if (has_assignment()) {
459 auto ap = assignment();
460 assert(!ap.fixed_assignment());
462 if (ap.register_valid() && ap.get_reg() == reg) {
468 auto ®_file = compiler->register_file;
469 if (reg_file.is_used(reg)) {
470 compiler->evict_reg(reg);
473 reg_file.mark_clobbered(reg);
474 if (has_assignment()) {
475 assert(compiler->may_change_value_state());
477 reg_file.mark_used(reg, state.v.local_idx, state.v.part);
478 auto ap = assignment();
479 auto old_reg = AsmReg::make_invalid();
480 if (ap.register_valid()) {
481 old_reg = ap.get_reg();
485 ap.set_register_valid(
true);
492 if (old_reg.valid()) {
493 compiler->derived()->mov(reg, old_reg, ap.part_size());
494 reg_file.unmark_used(old_reg);
496 compiler->derived()->reload_to_reg(reg, ap);
499 assert(!ap.stack_valid() &&
"alloc_reg with valid stack slot");
502 reg_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
503 reg_file.mark_fixed(reg);
506 if (state.c.reg.valid()) {
508 compiler->derived()->mov(reg, state.c.reg, 8);
509 reg_file.unmark_fixed(state.c.reg);
510 reg_file.unmark_used(state.c.reg);
512 assert(is_const() &&
"cannot reload temporary value");
513 compiler->derived()->materialize_constant(
514 const_data().data(), state.c.bank, state.c.size, reg);
519 state.c.owned =
true;
525template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
532 compiler->derived()->materialize_constant(
533 const_data().data(), state.c.bank, state.c.size, reg);
536 if (!has_assignment()) {
538 assert(reg != cur_reg());
541 compiler->derived()->mov(reg, cur_reg(), size);
545 auto ap = assignment();
547 assert(cur_reg() != reg);
548 compiler->derived()->mov(reg, cur_reg(), ap.part_size());
549 }
else if (ap.register_valid()) {
550 assert(ap.get_reg() != reg);
552 compiler->derived()->mov(reg, ap.get_reg(), ap.part_size());
554 assert(!ap.fixed_assignment());
555 compiler->derived()->reload_to_reg(reg, ap);
558 compiler->register_file.mark_clobbered(reg);
562template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
565 assert(has_assignment());
567 auto ap = assignment();
568 assert(ap.register_valid());
570 const auto reg = ap.get_reg();
571 compiler->register_file.inc_lock_count(reg);
575template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
578 assert(has_assignment());
579 if (!state.v.reg.valid()) {
583 compiler->register_file.dec_lock_count(state.v.reg);
584 state.v.reg = AsmReg::make_invalid();
587template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
590 assert(
this != &other &&
"cannot assign ValuePart to itself");
591 auto ®_file = compiler->register_file;
592 if (!has_assignment()) {
597 if (!other.has_assignment()) {
601 *
this = std::move(other);
605 if (!other.can_salvage()) {
607 AsmReg cur_reg = alloc_reg(compiler);
608 other.reload_into_specific_fixed(compiler, cur_reg);
609 other.reset(compiler);
616 state.c.reg = other.salvage_keep_used(compiler);
617 state.c.owned =
true;
618 reg_file.mark_fixed(state.c.reg);
619 reg_file.update_reg_assignment(state.c.reg, INVALID_VAL_LOCAL_IDX, 0);
624 auto ap = assignment();
625 assert(!ap.variable_ref() &&
"cannot update variable ref");
627 if (ap.fixed_assignment() || !other.can_salvage()) {
628 if constexpr (WithAsserts) {
633 ap.set_modified(
true);
636 AsmReg cur_reg = alloc_reg(compiler);
637 other.reload_into_specific_fixed(compiler, cur_reg, ap.part_size());
638 other.reset(compiler);
640 ap.set_register_valid(
true);
641 ap.set_modified(
true);
646 if (ap.register_valid()) {
649 auto cur_reg = ap.get_reg();
650 assert(!reg_file.is_fixed(cur_reg));
651 reg_file.unmark_used(cur_reg);
654 AsmReg new_reg = other.salvage_keep_used(compiler);
655 reg_file.update_reg_assignment(new_reg, local_idx(), part());
657 ap.set_register_valid(
true);
658 ap.set_modified(
true);
661template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
664 assert(compiler->may_change_value_state());
666 auto ®_file = compiler->register_file;
669 assert(other.has_reg() &&
"cannot initialize with invalid register");
670 Reg value_reg = other.cur_reg();
671 assert(reg_file.is_fixed(value_reg));
672 assert(reg_file.is_used(value_reg));
673 assert(reg_file.is_clobbered(value_reg));
674 assert(!state.c.reg.valid() &&
675 "attempted to overwrite already initialized and locked ValuePartRef");
677 if (!has_assignment()) {
678 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
679 state.c.reg = value_reg;
680 state.c.owned =
true;
681 assert(reg_file.reg_local_idx(value_reg) == INVALID_VAL_LOCAL_IDX);
682 assert(reg_file.reg_part(value_reg) == 0);
683 other.force_set_reg(AsmReg::make_invalid());
688 auto ap = assignment();
689 assert(!ap.variable_ref() &&
"cannot update variable ref");
691 if (ap.fixed_assignment()) {
693 auto cur_reg = ap.get_reg();
694 assert(reg_file.is_used(cur_reg));
695 assert(reg_file.is_fixed(cur_reg));
696 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
697 assert(ap.register_valid() && !ap.stack_valid() &&
698 "invalid state for fixed assignment");
699 assert(cur_reg != value_reg);
700 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
706 assert(!ap.register_valid() && !ap.stack_valid() &&
707 "attempted to overwrite already initialized ValuePartRef");
710 reg_file.unmark_fixed(value_reg);
711 reg_file.update_reg_assignment(value_reg, local_idx(), part());
712 ap.set_reg(value_reg);
713 ap.set_register_valid(
true);
714 ap.set_modified(
true);
715 other.force_set_reg(AsmReg::make_invalid());
718template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
721 assert(compiler->may_change_value_state());
723 auto ®_file = compiler->register_file;
726 assert(value_reg.valid() &&
"cannot initialize with invalid register");
727 assert(!state.c.reg.valid() &&
728 "attempted to overwrite already initialized and locked ValuePartRef");
730 if (!has_assignment()) {
731 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
732 state.c.reg = value_reg;
733 state.c.owned =
true;
734 reg_file.mark_used(state.c.reg, INVALID_VAL_LOCAL_IDX, 0);
735 reg_file.mark_fixed(state.c.reg);
740 auto ap = assignment();
741 assert(!ap.variable_ref() &&
"cannot update variable ref");
743 if (ap.fixed_assignment()) {
745 auto cur_reg = ap.get_reg();
746 assert(reg_file.is_used(cur_reg));
747 assert(reg_file.is_fixed(cur_reg));
748 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
750 assert(cur_reg != value_reg);
751 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
752 ap.set_register_valid(
true);
753 ap.set_modified(
true);
758 assert(!ap.register_valid() && !ap.stack_valid() &&
759 "attempted to overwrite already initialized ValuePartRef");
761 reg_file.mark_used(value_reg, local_idx(), part());
762 reg_file.mark_clobbered(value_reg);
763 ap.set_reg(value_reg);
764 ap.set_register_valid(
true);
765 ap.set_modified(
true);
768template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
772 assert(compiler->may_change_value_state());
773 assert(can_salvage());
774 if (!has_assignment()) {
775 AsmReg reg = state.c.reg;
776 compiler->register_file.unmark_fixed(reg);
777 state.c.reg = AsmReg::make_invalid();
781 auto ap = assignment();
782 assert(ap.register_valid());
783 auto cur_reg = ap.get_reg();
786 assert(ap.fixed_assignment() || !compiler->register_file.is_fixed(cur_reg));
787 if (ap.fixed_assignment()) {
788 compiler->register_file.dec_lock_count(cur_reg);
789 --compiler->assignments.cur_fixed_assignment_count[ap.bank().id()];
792 ap.set_register_valid(
false);
793 ap.set_fixed_assignment(
false);
797template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
800 AsmReg reg = state.c.reg;
807 assert(!has_assignment() || assignment().modified() ||
true);
810 if (has_assignment()) {
811 AssignmentPartRef ap = assignment();
812 bool fixed = ap.fixed_assignment();
813 ap.set_register_valid(
false);
814 ap.set_fixed_assignment(
false);
815 compiler->register_file.dec_lock_count_must_zero(reg, fixed ? 2 : 1);
817 --compiler->assignments.cur_fixed_assignment_count[ap.bank().id()];
820 compiler->register_file.unmark_fixed(reg);
822 compiler->register_file.unmark_used(reg);
823 }
else if (has_assignment()) {
824 compiler->register_file.dec_lock_count(reg);
827 state.c.reg = AsmReg::make_invalid();
830template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
831struct CompilerBase<Adaptor, Derived, Config>::ValuePartRef : ValuePart {
834 template <
typename... Args>
836 : ValuePart(std::forward<Args>(args)...), compiler(compiler) {}
838 explicit ValuePartRef(
const ValuePartRef &) =
delete;
840 ValuePartRef(ValuePartRef &&other)
841 : ValuePart(std::move(other)), compiler(other.compiler) {}
843 ~ValuePartRef() {
reset(); }
845 ValuePartRef &operator=(
const ValuePartRef &) =
delete;
847 ValuePartRef &operator=(ValuePartRef &&other) {
848 if (
this == &other) {
852 ValuePart::operator=(std::move(other));
856 ValuePartRef &operator=(ValuePart &&other) {
858 ValuePart::operator=(std::move(other));
862 AsmReg alloc_reg() {
return ValuePart::alloc_reg(compiler); }
864 AsmReg cur_reg_or_alloc() {
return ValuePart::cur_reg_or_alloc(compiler); }
866 AsmReg alloc_try_reuse(ValuePart &ref) {
867 return ValuePart::alloc_try_reuse(compiler, ref);
870 void alloc_specific(AsmReg reg) { ValuePart::alloc_specific(compiler, reg); }
872 AsmReg load_to_reg() {
return ValuePart::load_to_reg(compiler); }
874 void load_to_specific(AsmReg reg) {
875 ValuePart::load_to_specific(compiler, reg);
878 AsmReg reload_into_specific_fixed(AsmReg reg,
unsigned size = 0) {
879 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
882 AsmReg reload_into_specific_fixed(
CompilerBase *compiler,
885 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
888 ValuePartRef get_unowned_ref() {
889 return ValuePartRef{compiler, ValuePart::get_unowned()};
892 ValuePartRef into_temporary() && {
895 std::move(*
static_cast<ValuePart *
>(
this)).into_temporary(compiler)};
898 ScratchReg into_scratch() && {
899 return std::move(*
static_cast<ValuePart *
>(
this)).into_scratch(compiler);
902 ValuePartRef into_extended(
bool sign, u32 from, u32 to) && {
903 return ValuePartRef{compiler,
904 std::move(*
static_cast<ValuePart *
>(
this))
905 .into_extended(compiler, sign, from, to)};
908 void lock() { ValuePart::lock(compiler); }
909 void unlock() { ValuePart::unlock(compiler); }
911 void set_value(ValuePart &&other) {
912 ValuePart::set_value(compiler, std::move(other));
915 void set_value(ScratchReg &&other) {
916 ValuePart::set_value(compiler, std::move(other));
919 void set_value_reg(AsmReg value_reg) {
920 ValuePart::set_value_reg(compiler, value_reg);
923 AsmReg salvage() {
return ValuePart::salvage(compiler); }
925 void reset() { ValuePart::reset(compiler); }
Owned unspillable and unevictable temporary register with RAII semantics.
The base class for the compiler.
void reset()
Reset any leftover data from the previous compilation such that it will not affect the next compilati...
CompilerBase(Adaptor *adaptor)
Initialize a CompilerBase, should be called by the derived classes.