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() noexcept : state{ConstantData{.is_const =
false}} {}
47 ValuePart(RegBank bank) noexcept
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) noexcept
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) noexcept
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) noexcept : state{other.state} {
98 other.state.c = ConstantData{.is_const =
false, .bank = bank()};
101 ~ValuePart() noexcept {
102 assert(!state.c.reg.valid() &&
"must call reset() on ValuePart explicitly");
105 ValuePart &operator=(
const ValuePart &) =
delete;
107 ValuePart &operator=(ValuePart &&other)
noexcept {
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 noexcept {
return state.v.has_assignment; }
119 bool is_const() const noexcept {
120 return !state.c.has_assignment && state.c.is_const;
123 bool is_owned() const noexcept {
124 assert(has_assignment());
125 return state.c.owned;
128 [[nodiscard]] AssignmentPartRef assignment() const noexcept {
129 assert(has_assignment());
130 return AssignmentPartRef{state.v.assignment, state.v.part};
135 AsmReg cur_reg() const noexcept {
136 assert(state.v.reg.valid());
142 AsmReg cur_reg_unlocked() const noexcept {
143 if (state.v.reg.valid()) {
146 if (has_assignment()) {
147 if (
auto ap = assignment(); ap.register_valid()) {
151 return AsmReg::make_invalid();
155 bool is_in_reg(AsmReg reg)
const noexcept {
157 return cur_reg() == reg;
159 if (has_assignment()) {
160 auto ap = assignment();
161 return ap.register_valid() && ap.get_reg() == reg;
166 bool has_reg() const noexcept {
return state.v.reg.valid(); }
171 bool reload)
noexcept;
174 bool reload)
noexcept;
179 AsmReg alloc_reg(
CompilerBase *compiler, u64 exclusion_mask = 0) noexcept {
180 return alloc_reg_impl(compiler, exclusion_mask,
false);
198 AsmReg alloc_try_reuse(
CompilerBase *compiler, ValuePart &ref)
noexcept {
199 assert(ref.has_reg());
200 if (!has_assignment() || !assignment().register_valid()) {
201 assert(!has_assignment() || !assignment().fixed_assignment());
202 if (ref.can_salvage()) {
203 set_value(compiler, std::move(ref));
204 if (has_assignment()) {
210 return alloc_reg(compiler);
218 void alloc_specific(
CompilerBase *compiler, AsmReg reg)
noexcept {
219 alloc_specific_impl(compiler, reg,
false);
226 return alloc_reg_impl(compiler, 0,
true);
236 void load_to_specific(
CompilerBase *compiler, AsmReg reg)
noexcept {
237 alloc_specific_impl(compiler, reg,
true);
241 AsmReg reload_into_specific_fixed(
CompilerBase *compiler,
243 unsigned size = 0) noexcept;
246 ValuePart get_unowned() noexcept {
248 ValuePart res{bank()};
250 ConstantData{.reg = cur_reg(), .owned =
false, .is_const =
false};
255 ValuePart into_temporary(
CompilerBase *compiler) &&
noexcept {
257 if (state.c.const_inline) {
258 ValuePart res{state.c.inline_data, state.c.size, state.c.bank};
259 res.load_to_reg(compiler);
262 ValuePart res{state.c.data, state.c.size, state.c.bank};
263 res.load_to_reg(compiler);
269 assert((has_assignment() || state.c.owned) &&
270 "into_temporary from unowned ValuePart not implemented");
271 ValuePart res{bank()};
272 res.set_value(compiler, std::move(*
this));
273 if (!res.has_reg()) [[unlikely]] {
274 assert(res.is_const());
275 res.load_to_reg(compiler);
283 assert((has_assignment() || state.c.owned || state.c.is_const) &&
284 "into_scratch from unowned ValuePart not implemented");
287 res.alloc_specific(salvage(compiler));
289 reload_into_specific_fixed(compiler, res.alloc(bank()));
299 u32 to) &&
noexcept {
300 assert(from < to &&
"invalid integer extension sizes");
301 if (is_const() && to <= 64) {
302 u64 val = const_data()[0];
303 u64 extended = sign ? util::sext(val, from) : util::zext(val, from);
304 return ValuePart{extended, (to + 7) / 8, state.c.bank};
306 ValuePart res{bank()};
307 Reg src_reg = has_reg() ? cur_reg() : load_to_reg(compiler);
309 res.set_value(compiler, std::move(*
this));
310 assert(src_reg == res.cur_reg());
312 res.alloc_reg(compiler);
314 compiler->derived()->generate_raw_intext(
315 res.cur_reg(), src_reg, sign, from, to);
322 void set_modified() noexcept {
323 assert(has_reg() && has_assignment());
324 assignment().set_modified(
true);
330 void set_value(
CompilerBase *compiler, ValuePart &&other)
noexcept;
340 void set_value_reg(
CompilerBase *compiler, AsmReg reg)
noexcept;
342 bool can_salvage() const noexcept {
343 if (!has_assignment()) {
344 return state.c.owned && state.c.reg.valid();
347 return state.v.owned && assignment().register_valid();
351 AsmReg salvage_keep_used(
CompilerBase *compiler)
noexcept;
357 AsmReg reg = salvage_keep_used(compiler);
358 compiler->register_file.unmark_used(reg);
362 ValLocalIdx local_idx() const noexcept {
363 assert(has_assignment());
364 return state.v.local_idx;
367 u32 part() const noexcept {
368 assert(has_assignment());
372 RegBank bank() const noexcept {
373 return !has_assignment() ? state.c.bank : assignment().bank();
376 u32 part_size() const noexcept {
377 return !has_assignment() ? state.c.size : assignment().part_size();
380 std::span<const u64> const_data() const noexcept {
382 if (state.c.const_inline) {
383 return {&state.c.inline_data, 1};
385 return {state.c.data, (state.c.size + 7) / 8};
392template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
393typename CompilerBase<Adaptor, Derived, Config>::AsmReg
394 CompilerBase<Adaptor, Derived, Config>::ValuePart::alloc_reg_impl(
397 const bool reload)
noexcept {
401 assert(compiler->may_change_value_state());
402 assert(!state.c.reg.valid());
405 if (has_assignment()) {
406 auto ap = assignment();
407 if (ap.register_valid()) {
410 assert((exclusion_mask & (1ull << state.v.reg.id())) == 0 &&
411 "moving registers in alloc_reg is unsupported");
420 Reg reg = compiler->select_reg(bank, exclusion_mask);
421 auto ®_file = compiler->register_file;
422 reg_file.mark_clobbered(reg);
423 if (has_assignment()) {
424 reg_file.mark_used(reg, state.v.local_idx, state.v.part);
425 auto ap = assignment();
427 ap.set_register_valid(
true);
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;
445 assert(is_const() &&
"cannot reload temporary value");
446 compiler->derived()->materialize_constant(
447 const_data().data(), state.c.bank, state.c.size, reg);
454template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
455typename CompilerBase<Adaptor, Derived, Config>::AsmReg
456 CompilerBase<Adaptor, Derived, Config>::ValuePart::alloc_specific_impl(
457 CompilerBase *compiler, AsmReg reg,
const bool reload)
noexcept {
458 assert(!state.c.reg.valid());
460 if (has_assignment()) {
461 auto ap = assignment();
462 assert(!ap.fixed_assignment());
464 if (ap.register_valid() && ap.get_reg() == reg) {
470 auto ®_file = compiler->register_file;
471 if (reg_file.is_used(reg)) {
472 compiler->evict_reg(reg);
475 reg_file.mark_clobbered(reg);
476 if (has_assignment()) {
477 assert(compiler->may_change_value_state());
479 reg_file.mark_used(reg, state.v.local_idx, state.v.part);
480 auto ap = assignment();
481 auto old_reg = AsmReg::make_invalid();
482 if (ap.register_valid()) {
483 old_reg = ap.get_reg();
487 ap.set_register_valid(
true);
494 if (old_reg.valid()) {
495 compiler->derived()->mov(reg, old_reg, ap.part_size());
496 reg_file.unmark_used(old_reg);
498 compiler->derived()->reload_to_reg(reg, ap);
501 assert(!ap.stack_valid() &&
"alloc_reg with valid stack slot");
504 reg_file.mark_used(reg, INVALID_VAL_LOCAL_IDX, 0);
505 reg_file.mark_fixed(reg);
508 if (state.c.reg.valid()) {
510 compiler->derived()->mov(reg, state.c.reg, 8);
511 reg_file.unmark_fixed(state.c.reg);
512 reg_file.unmark_used(state.c.reg);
514 assert(is_const() &&
"cannot reload temporary value");
515 compiler->derived()->materialize_constant(
516 const_data().data(), state.c.bank, state.c.size, reg);
521 state.c.owned =
true;
527template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
528typename CompilerBase<Adaptor, Derived, Config>::AsmReg
532 unsigned size)
noexcept {
534 compiler->derived()->materialize_constant(
535 const_data().data(), state.c.bank, state.c.size, reg);
538 if (!has_assignment()) {
540 assert(reg != cur_reg());
543 compiler->derived()->mov(reg, cur_reg(), size);
547 auto ap = assignment();
549 assert(cur_reg() != reg);
550 compiler->derived()->mov(reg, cur_reg(), ap.part_size());
551 }
else if (ap.register_valid()) {
552 assert(ap.get_reg() != reg);
554 compiler->derived()->mov(reg, ap.get_reg(), ap.part_size());
556 assert(!ap.fixed_assignment());
557 compiler->derived()->reload_to_reg(reg, ap);
560 compiler->register_file.mark_clobbered(reg);
564template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
565void CompilerBase<Adaptor, Derived, Config>::ValuePart::lock(
567 assert(has_assignment());
569 auto ap = assignment();
570 assert(ap.register_valid());
572 const auto reg = ap.get_reg();
573 compiler->register_file.inc_lock_count(reg);
577template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
578void CompilerBase<Adaptor, Derived, Config>::ValuePart::unlock(
580 assert(has_assignment());
581 if (!state.v.reg.valid()) {
585 compiler->register_file.dec_lock_count(state.v.reg);
586 state.v.reg = AsmReg::make_invalid();
589template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
590void CompilerBase<Adaptor, Derived, Config>::ValuePart::set_value(
592 auto ®_file = compiler->register_file;
593 if (!has_assignment()) {
598 if (!other.has_assignment()) {
602 *
this = std::move(other);
606 if (!other.can_salvage()) {
608 AsmReg cur_reg = alloc_reg(compiler);
609 other.reload_into_specific_fixed(compiler, cur_reg);
610 other.reset(compiler);
617 state.c.reg = other.salvage_keep_used(compiler);
618 state.c.owned =
true;
619 reg_file.mark_fixed(state.c.reg);
620 reg_file.update_reg_assignment(state.c.reg, INVALID_VAL_LOCAL_IDX, 0);
625 auto ap = assignment();
626 assert(!ap.variable_ref() &&
"cannot update variable ref");
628 if (ap.fixed_assignment() || !other.can_salvage()) {
634 ap.set_modified(
true);
637 AsmReg cur_reg = alloc_reg(compiler);
638 other.reload_into_specific_fixed(compiler, cur_reg, ap.part_size());
639 other.reset(compiler);
641 ap.set_register_valid(
true);
642 ap.set_modified(
true);
647 if (ap.register_valid()) {
650 auto cur_reg = ap.get_reg();
651 assert(!reg_file.is_fixed(cur_reg));
652 reg_file.unmark_used(cur_reg);
655 AsmReg new_reg = other.salvage_keep_used(compiler);
656 reg_file.update_reg_assignment(new_reg, local_idx(), part());
658 ap.set_register_valid(
true);
659 ap.set_modified(
true);
662template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
663void CompilerBase<Adaptor, Derived, Config>::ValuePart::set_value(
665 assert(compiler->may_change_value_state());
667 auto ®_file = compiler->register_file;
670 assert(other.has_reg() &&
"cannot initialize with invalid register");
671 Reg value_reg = other.cur_reg();
672 assert(reg_file.is_fixed(value_reg));
673 assert(reg_file.is_used(value_reg));
674 assert(reg_file.is_clobbered(value_reg));
675 assert(!state.c.reg.valid() &&
676 "attempted to overwrite already initialized and locked ValuePartRef");
678 if (!has_assignment()) {
679 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
680 state.c.reg = value_reg;
681 state.c.owned =
true;
682 assert(reg_file.reg_local_idx(value_reg) == INVALID_VAL_LOCAL_IDX);
683 assert(reg_file.reg_part(value_reg) == 0);
684 other.force_set_reg(AsmReg::make_invalid());
689 auto ap = assignment();
690 assert(!ap.variable_ref() &&
"cannot update variable ref");
692 if (ap.fixed_assignment()) {
694 auto cur_reg = ap.get_reg();
695 assert(reg_file.is_used(cur_reg));
696 assert(reg_file.is_fixed(cur_reg));
697 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
698 assert(ap.register_valid() && !ap.stack_valid() &&
699 "invalid state for fixed assignment");
700 assert(cur_reg != value_reg);
701 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
707 assert(!ap.register_valid() && !ap.stack_valid() &&
708 "attempted to overwrite already initialized ValuePartRef");
711 reg_file.unmark_fixed(value_reg);
712 reg_file.update_reg_assignment(value_reg, local_idx(), part());
713 ap.set_reg(value_reg);
714 ap.set_register_valid(
true);
715 ap.set_modified(
true);
716 other.force_set_reg(AsmReg::make_invalid());
719template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
720void CompilerBase<Adaptor, Derived, Config>::ValuePart::set_value_reg(
722 assert(compiler->may_change_value_state());
724 auto ®_file = compiler->register_file;
727 assert(value_reg.valid() &&
"cannot initialize with invalid register");
728 assert(!state.c.reg.valid() &&
729 "attempted to overwrite already initialized and locked ValuePartRef");
731 if (!has_assignment()) {
732 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
733 state.c.reg = value_reg;
734 state.c.owned =
true;
735 reg_file.mark_used(state.c.reg, INVALID_VAL_LOCAL_IDX, 0);
736 reg_file.mark_fixed(state.c.reg);
741 auto ap = assignment();
742 assert(!ap.variable_ref() &&
"cannot update variable ref");
744 if (ap.fixed_assignment()) {
746 auto cur_reg = ap.get_reg();
747 assert(reg_file.is_used(cur_reg));
748 assert(reg_file.is_fixed(cur_reg));
749 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
751 assert(cur_reg != value_reg);
752 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
753 ap.set_register_valid(
true);
754 ap.set_modified(
true);
759 assert(!ap.register_valid() && !ap.stack_valid() &&
760 "attempted to overwrite already initialized ValuePartRef");
762 reg_file.mark_used(value_reg, local_idx(), part());
763 reg_file.mark_clobbered(value_reg);
764 ap.set_reg(value_reg);
765 ap.set_register_valid(
true);
766 ap.set_modified(
true);
769template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
770typename CompilerBase<Adaptor, Derived, Config>::AsmReg
771 CompilerBase<Adaptor, Derived, Config>::ValuePart::salvage_keep_used(
773 assert(compiler->may_change_value_state());
774 assert(can_salvage());
775 if (!has_assignment()) {
776 AsmReg reg = state.c.reg;
777 compiler->register_file.unmark_fixed(reg);
778 state.c.reg = AsmReg::make_invalid();
782 auto ap = assignment();
783 assert(ap.register_valid());
784 auto cur_reg = ap.get_reg();
787 assert(ap.fixed_assignment() || !compiler->register_file.is_fixed(cur_reg));
788 if (ap.fixed_assignment()) {
789 compiler->register_file.dec_lock_count(cur_reg);
790 --compiler->assignments.cur_fixed_assignment_count[ap.bank().id()];
793 ap.set_register_valid(
false);
794 ap.set_fixed_assignment(
false);
798template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
799void CompilerBase<Adaptor, Derived, Config>::ValuePart::reset(
801 AsmReg reg = state.c.reg;
809 assert(!has_assignment() || assignment().modified() ||
true);
813 if (has_assignment()) {
814 AssignmentPartRef ap = assignment();
815 bool fixed = ap.fixed_assignment();
816 ap.set_register_valid(
false);
817 ap.set_fixed_assignment(
false);
818 compiler->register_file.dec_lock_count_must_zero(reg, fixed ? 2 : 1);
820 --compiler->assignments.cur_fixed_assignment_count[ap.bank().id()];
823 compiler->register_file.unmark_fixed(reg);
825 compiler->register_file.unmark_used(reg);
826 }
else if (has_assignment()) {
827 compiler->register_file.dec_lock_count(reg);
830 state.c.reg = AsmReg::make_invalid();
833template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
834struct CompilerBase<Adaptor, Derived, Config>::ValuePartRef : ValuePart {
837 template <
typename... Args>
838 ValuePartRef(
CompilerBase *compiler, Args &&...args) noexcept
839 : ValuePart(std::forward<Args>(args)...), compiler(compiler) {}
841 explicit ValuePartRef(
const ValuePartRef &) =
delete;
843 ValuePartRef(ValuePartRef &&other) noexcept
844 : ValuePart(std::move(other)), compiler(other.compiler) {}
846 ~ValuePartRef() noexcept {
reset(); }
848 ValuePartRef &operator=(
const ValuePartRef &) =
delete;
850 ValuePartRef &operator=(ValuePartRef &&other)
noexcept {
851 if (
this == &other) {
855 ValuePart::operator=(std::move(other));
859 ValuePartRef &operator=(ValuePart &&other)
noexcept {
861 ValuePart::operator=(std::move(other));
865 AsmReg alloc_reg(u64 exclusion_mask = 0) noexcept {
866 return ValuePart::alloc_reg(compiler, exclusion_mask);
869 AsmReg alloc_try_reuse(ValuePart &ref)
noexcept {
870 return ValuePart::alloc_try_reuse(compiler, ref);
873 void alloc_specific(AsmReg reg)
noexcept {
874 ValuePart::alloc_specific(compiler, reg);
877 AsmReg load_to_reg() noexcept {
return ValuePart::load_to_reg(compiler); }
879 void load_to_specific(AsmReg reg)
noexcept {
880 ValuePart::load_to_specific(compiler, reg);
883 AsmReg reload_into_specific_fixed(AsmReg reg,
unsigned size = 0) noexcept {
884 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
887 AsmReg reload_into_specific_fixed(
CompilerBase *compiler,
889 unsigned size = 0) noexcept {
890 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
893 ValuePartRef get_unowned_ref() noexcept {
894 return ValuePartRef{compiler, ValuePart::get_unowned()};
897 ValuePartRef into_temporary() &&
noexcept {
900 std::move(*
static_cast<ValuePart *
>(
this)).into_temporary(compiler)};
904 return std::move(*
static_cast<ValuePart *
>(
this)).into_scratch(compiler);
907 ValuePartRef into_extended(
bool sign, u32 from, u32 to) &&
noexcept {
908 return ValuePartRef{compiler,
909 std::move(*
static_cast<ValuePart *
>(
this))
910 .into_extended(compiler, sign, from, to)};
913 void lock() noexcept { ValuePart::lock(compiler); }
914 void unlock() noexcept { ValuePart::unlock(compiler); }
916 void set_value(ValuePart &&other)
noexcept {
917 ValuePart::set_value(compiler, std::move(other));
921 ValuePart::set_value(compiler, std::move(other));
924 void set_value_reg(AsmReg value_reg)
noexcept {
925 ValuePart::set_value_reg(compiler, value_reg);
928 AsmReg salvage() noexcept {
return ValuePart::salvage(compiler); }
930 void reset() noexcept { 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.