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);
239 alloc_reg_impl<
true>(compiler);
251 void load_to_specific(
CompilerBase *compiler, AsmReg reg) {
252 alloc_specific_impl(compiler, reg,
true);
256 AsmReg reload_into_specific_fixed(
CompilerBase *compiler,
261 ValuePart get_unowned() {
263 ValuePart res{bank()};
265 ConstantData{.reg = cur_reg(), .owned =
false, .is_const =
false};
272 if (state.c.const_inline) {
273 ValuePart res{state.c.inline_data, state.c.size, state.c.bank};
274 res.load_to_reg(compiler);
277 ValuePart res{state.c.data, state.c.size, state.c.bank};
278 res.load_to_reg(compiler);
284 assert((has_assignment() || state.c.owned) &&
285 "into_temporary from unowned ValuePart not implemented");
286 ValuePart res{bank()};
287 res.set_value(compiler, std::move(*
this));
288 if (!res.has_reg()) [[unlikely]] {
289 assert(res.is_const());
290 res.load_to_reg(compiler);
298 assert((has_assignment() || state.c.owned || state.c.is_const) &&
299 "into_scratch from unowned ValuePart not implemented");
302 res.alloc_specific(salvage(compiler));
304 reload_into_specific_fixed(compiler, res.alloc(bank()));
312 into_extended(
CompilerBase *compiler,
bool sign, u32 from, u32 to) && {
313 assert(from < to &&
"invalid integer extension sizes");
314 if (is_const() && to <= 64) {
315 u64 val = const_data()[0];
316 u64 extended = sign ? util::sext(val, from) : util::zext(val, from);
317 return ValuePart{extended, (to + 7) / 8, state.c.bank};
319 ValuePart res{bank()};
320 Reg src_reg = has_reg() ? cur_reg() : load_to_reg(compiler);
322 res.set_value(compiler, std::move(*
this));
323 assert(src_reg == res.cur_reg());
325 res.alloc_reg(compiler);
327 compiler->derived()->generate_raw_intext(
328 res.cur_reg(), src_reg, sign, from, to);
335 void set_modified() {
336 assert(has_reg() && has_assignment());
337 assignment().set_modified(
true);
343 void set_value(
CompilerBase *compiler, ValuePart &&other);
355 bool can_salvage()
const {
356 if (!has_assignment()) {
357 return state.c.owned && state.c.reg.valid();
360 return state.v.owned && assignment().register_valid();
370 AsmReg reg = salvage_keep_used(compiler);
371 compiler->register_file.unmark_used(reg);
375 ValLocalIdx local_idx()
const {
376 assert(has_assignment());
377 return state.v.local_idx;
381 assert(has_assignment());
385 RegBank bank()
const {
386 return !has_assignment() ? state.c.bank : assignment().bank();
389 u32 part_size()
const {
390 return !has_assignment() ? state.c.size : assignment().part_size();
393 std::span<const u64> const_data()
const {
395 if (state.c.const_inline) {
396 return {&state.c.inline_data, 1};
398 return {state.c.data, (state.c.size + 7) / 8};
405template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
406template <
bool Reload>
407void CompilerBase<Adaptor, Derived, Config>::ValuePart::alloc_reg_impl(
412 assert(compiler->may_change_value_state());
413 assert(!state.c.reg.valid());
416 if (has_assignment()) {
417 auto ap = assignment();
418 if (ap.register_valid()) {
428 Reg reg = compiler->select_reg(bank);
429 auto ®_file = compiler->register_file;
430 reg_file.mark_clobbered(reg);
431 if (has_assignment()) {
432 reg_file.mark_used(reg, state.v.local_idx, state.v.part);
436 reg_file.mark_fixed(reg);
438 auto ap = assignment();
440 ap.set_register_valid(
true);
442 if constexpr (Reload) {
443 compiler->derived()->reload_to_reg(reg, ap);
445 assert(!ap.stack_valid() &&
"alloc_reg called on initialized value");
448 reg_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
449 reg_file.mark_fixed(reg);
451 state.c.owned =
true;
453 if constexpr (Reload) {
454 assert(is_const() &&
"cannot reload temporary value");
455 compiler->derived()->materialize_constant(
456 const_data().data(), state.c.bank, state.c.size, reg);
461template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
464 CompilerBase *compiler, AsmReg reg,
const bool reload) {
465 assert(!state.c.reg.valid());
467 if (has_assignment()) {
468 auto ap = assignment();
469 assert(!ap.fixed_assignment());
471 if (ap.register_valid() && ap.get_reg() == reg) {
477 auto ®_file = compiler->register_file;
478 if (reg_file.is_used(reg)) {
479 compiler->evict_reg(reg);
482 reg_file.mark_clobbered(reg);
483 if (has_assignment()) {
484 assert(compiler->may_change_value_state());
486 reg_file.mark_used(reg, state.v.local_idx, state.v.part);
487 auto ap = assignment();
488 auto old_reg = AsmReg::make_invalid();
489 if (ap.register_valid()) {
490 old_reg = ap.get_reg();
494 ap.set_register_valid(
true);
501 if (old_reg.valid()) {
502 compiler->derived()->mov(reg, old_reg, ap.part_size());
503 reg_file.unmark_used(old_reg);
505 compiler->derived()->reload_to_reg(reg, ap);
508 assert(!ap.stack_valid() &&
"alloc_reg with valid stack slot");
511 reg_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
512 reg_file.mark_fixed(reg);
515 if (state.c.reg.valid()) {
517 compiler->derived()->mov(reg, state.c.reg, 8);
518 reg_file.unmark_fixed(state.c.reg);
519 reg_file.unmark_used(state.c.reg);
521 assert(is_const() &&
"cannot reload temporary value");
522 compiler->derived()->materialize_constant(
523 const_data().data(), state.c.bank, state.c.size, reg);
528 state.c.owned =
true;
534template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
541 compiler->derived()->materialize_constant(
542 const_data().data(), state.c.bank, state.c.size, reg);
545 if (!has_assignment()) {
547 assert(reg != cur_reg());
550 compiler->derived()->mov(reg, cur_reg(), size);
554 auto ap = assignment();
556 assert(cur_reg() != reg);
557 compiler->derived()->mov(reg, cur_reg(), ap.part_size());
558 }
else if (ap.register_valid()) {
559 assert(ap.get_reg() != reg);
561 compiler->derived()->mov(reg, ap.get_reg(), ap.part_size());
563 assert(!ap.fixed_assignment());
564 compiler->derived()->reload_to_reg(reg, ap);
567 compiler->register_file.mark_clobbered(reg);
571template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
574 assert(has_assignment());
576 auto ap = assignment();
577 assert(ap.register_valid());
579 const auto reg = ap.get_reg();
580 compiler->register_file.inc_lock_count(reg);
584template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
587 assert(has_assignment());
588 if (!state.v.reg.valid()) {
592 compiler->register_file.dec_lock_count(state.v.reg);
593 state.v.reg = AsmReg::make_invalid();
596template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
599 assert(
this != &other &&
"cannot assign ValuePart to itself");
600 auto ®_file = compiler->register_file;
601 if (!has_assignment()) {
606 if (!other.has_assignment()) {
610 *
this = std::move(other);
614 if (!other.can_salvage()) {
616 AsmReg cur_reg = alloc_reg(compiler);
617 other.reload_into_specific_fixed(compiler, cur_reg);
618 other.reset(compiler);
625 state.c.reg = other.salvage_keep_used(compiler);
626 state.c.owned =
true;
627 reg_file.mark_fixed(state.c.reg);
628 reg_file.update_reg_assignment(state.c.reg, INVALID_VAL_LOCAL_IDX, 0);
633 auto ap = assignment();
634 assert(!ap.variable_ref() &&
"cannot update variable ref");
636 if (ap.fixed_assignment() || !other.can_salvage()) {
637 if constexpr (WithAsserts) {
642 ap.set_modified(
true);
645 AsmReg cur_reg = alloc_reg(compiler);
646 other.reload_into_specific_fixed(compiler, cur_reg, ap.part_size());
647 other.reset(compiler);
649 ap.set_register_valid(
true);
650 ap.set_modified(
true);
655 if (ap.register_valid()) {
658 auto cur_reg = ap.get_reg();
659 assert(!reg_file.is_fixed(cur_reg));
660 reg_file.unmark_used(cur_reg);
663 AsmReg new_reg = other.salvage_keep_used(compiler);
664 reg_file.update_reg_assignment(new_reg, local_idx(), part());
666 ap.set_register_valid(
true);
667 ap.set_modified(
true);
670template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
673 assert(compiler->may_change_value_state());
675 auto ®_file = compiler->register_file;
678 assert(other.has_reg() &&
"cannot initialize with invalid register");
679 Reg value_reg = other.cur_reg();
680 assert(reg_file.is_fixed(value_reg));
681 assert(reg_file.is_used(value_reg));
682 assert(reg_file.is_clobbered(value_reg));
683 assert(!state.c.reg.valid() &&
684 "attempted to overwrite already initialized and locked ValuePartRef");
686 if (!has_assignment()) {
687 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
688 state.c.reg = value_reg;
689 state.c.owned =
true;
690 assert(reg_file.reg_local_idx(value_reg) == INVALID_VAL_LOCAL_IDX);
691 assert(reg_file.reg_part(value_reg) == 0);
692 other.force_set_reg(AsmReg::make_invalid());
697 auto ap = assignment();
698 assert(!ap.variable_ref() &&
"cannot update variable ref");
700 if (ap.fixed_assignment()) {
702 auto cur_reg = ap.get_reg();
703 assert(reg_file.is_used(cur_reg));
704 assert(reg_file.is_fixed(cur_reg));
705 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
706 assert(ap.register_valid() && !ap.stack_valid() &&
707 "invalid state for fixed assignment");
708 assert(cur_reg != value_reg);
709 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
715 assert(!ap.register_valid() && !ap.stack_valid() &&
716 "attempted to overwrite already initialized ValuePartRef");
719 reg_file.unmark_fixed(value_reg);
720 reg_file.update_reg_assignment(value_reg, local_idx(), part());
721 ap.set_reg(value_reg);
722 ap.set_register_valid(
true);
723 ap.set_modified(
true);
724 other.force_set_reg(AsmReg::make_invalid());
727template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
730 assert(compiler->may_change_value_state());
732 auto ®_file = compiler->register_file;
735 assert(value_reg.valid() &&
"cannot initialize with invalid register");
736 assert(!state.c.reg.valid() &&
737 "attempted to overwrite already initialized and locked ValuePartRef");
739 if (!has_assignment()) {
740 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
741 state.c.reg = value_reg;
742 state.c.owned =
true;
743 reg_file.mark_used(state.c.reg, INVALID_VAL_LOCAL_IDX, 0);
744 reg_file.mark_fixed(state.c.reg);
749 auto ap = assignment();
750 assert(!ap.variable_ref() &&
"cannot update variable ref");
752 if (ap.fixed_assignment()) {
754 auto cur_reg = ap.get_reg();
755 assert(reg_file.is_used(cur_reg));
756 assert(reg_file.is_fixed(cur_reg));
757 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
759 assert(cur_reg != value_reg);
760 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
761 ap.set_register_valid(
true);
762 ap.set_modified(
true);
767 assert(!ap.register_valid() && !ap.stack_valid() &&
768 "attempted to overwrite already initialized ValuePartRef");
770 reg_file.mark_used(value_reg, local_idx(), part());
771 reg_file.mark_clobbered(value_reg);
772 ap.set_reg(value_reg);
773 ap.set_register_valid(
true);
774 ap.set_modified(
true);
777template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
781 assert(compiler->may_change_value_state());
782 assert(can_salvage());
783 if (!has_assignment()) {
784 AsmReg reg = state.c.reg;
785 compiler->register_file.unmark_fixed(reg);
786 state.c.reg = AsmReg::make_invalid();
790 auto ap = assignment();
791 assert(ap.register_valid());
792 auto cur_reg = ap.get_reg();
795 assert(ap.fixed_assignment() || !compiler->register_file.is_fixed(cur_reg));
796 if (ap.fixed_assignment()) {
797 compiler->register_file.dec_lock_count(cur_reg);
798 --compiler->assignments.cur_fixed_assignment_count[ap.bank().id()];
801 ap.set_register_valid(
false);
802 ap.set_fixed_assignment(
false);
806template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
809 AsmReg reg = state.c.reg;
816 assert(!has_assignment() || assignment().modified() ||
true);
819 if (has_assignment()) {
820 AssignmentPartRef ap = assignment();
821 bool fixed = ap.fixed_assignment();
822 ap.set_register_valid(
false);
823 ap.set_fixed_assignment(
false);
824 compiler->register_file.dec_lock_count_must_zero(reg, fixed ? 2 : 1);
826 --compiler->assignments.cur_fixed_assignment_count[ap.bank().id()];
829 compiler->register_file.unmark_fixed(reg);
831 compiler->register_file.unmark_used(reg);
832 }
else if (has_assignment()) {
833 compiler->register_file.dec_lock_count(reg);
836 state.c.reg = AsmReg::make_invalid();
839template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
840struct CompilerBase<Adaptor, Derived, Config>::ValuePartRef : ValuePart {
843 template <
typename... Args>
845 : ValuePart(std::forward<Args>(args)...), compiler(compiler) {}
847 explicit ValuePartRef(
const ValuePartRef &) =
delete;
849 ValuePartRef(ValuePartRef &&other)
850 : ValuePart(std::move(other)), compiler(other.compiler) {}
852 ~ValuePartRef() {
reset(); }
854 ValuePartRef &operator=(
const ValuePartRef &) =
delete;
856 ValuePartRef &operator=(ValuePartRef &&other) {
857 if (
this == &other) {
861 ValuePart::operator=(std::move(other));
865 ValuePartRef &operator=(ValuePart &&other) {
867 ValuePart::operator=(std::move(other));
871 AsmReg alloc_reg() {
return ValuePart::alloc_reg(compiler); }
873 AsmReg cur_reg_or_alloc() {
return ValuePart::cur_reg_or_alloc(compiler); }
875 AsmReg alloc_try_reuse(ValuePart &ref) {
876 return ValuePart::alloc_try_reuse(compiler, ref);
879 void alloc_specific(AsmReg reg) { ValuePart::alloc_specific(compiler, reg); }
881 AsmReg load_to_reg() {
return ValuePart::load_to_reg(compiler); }
883 AsmReg cur_reg_or_load() {
return ValuePart::cur_reg_or_load(compiler); }
885 void load_to_specific(AsmReg reg) {
886 ValuePart::load_to_specific(compiler, reg);
889 AsmReg reload_into_specific_fixed(AsmReg reg,
unsigned size = 0) {
890 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
893 AsmReg reload_into_specific_fixed(
CompilerBase *compiler,
896 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
899 ValuePartRef get_unowned_ref() {
900 return ValuePartRef{compiler, ValuePart::get_unowned()};
903 ValuePartRef into_temporary() && {
906 std::move(*
static_cast<ValuePart *
>(
this)).into_temporary(compiler)};
909 ScratchReg into_scratch() && {
910 return std::move(*
static_cast<ValuePart *
>(
this)).into_scratch(compiler);
913 ValuePartRef into_extended(
bool sign, u32 from, u32 to) && {
914 return ValuePartRef{compiler,
915 std::move(*
static_cast<ValuePart *
>(
this))
916 .into_extended(compiler, sign, from, to)};
919 void lock() { ValuePart::lock(compiler); }
920 void unlock() { ValuePart::unlock(compiler); }
922 void set_value(ValuePart &&other) {
923 ValuePart::set_value(compiler, std::move(other));
926 void set_value(ScratchReg &&other) {
927 ValuePart::set_value(compiler, std::move(other));
930 void set_value_reg(AsmReg value_reg) {
931 ValuePart::set_value_reg(compiler, value_reg);
934 AsmReg salvage() {
return ValuePart::salvage(compiler); }
936 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.