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);
281 ScratchReg into_scratch(
CompilerBase *compiler) &&
noexcept {
283 assert((has_assignment() || state.c.owned) &&
284 "into_scratch from unowned ValuePart not implemented");
285 ScratchReg res{compiler};
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;
334 void set_value(
CompilerBase *compiler, ScratchReg &&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()) {
630 AsmReg cur_reg = alloc_reg(compiler);
631 other.reload_into_specific_fixed(compiler, cur_reg, ap.part_size());
632 other.reset(compiler);
634 ap.set_register_valid(
true);
635 ap.set_modified(
true);
640 if (ap.register_valid()) {
643 auto cur_reg = ap.get_reg();
644 assert(!reg_file.is_fixed(cur_reg));
645 reg_file.unmark_used(cur_reg);
648 AsmReg new_reg = other.salvage_keep_used(compiler);
649 reg_file.update_reg_assignment(new_reg, local_idx(), part());
651 ap.set_register_valid(
true);
652 ap.set_modified(
true);
655template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
656void CompilerBase<Adaptor, Derived, Config>::ValuePart::set_value(
658 assert(compiler->may_change_value_state());
660 auto ®_file = compiler->register_file;
663 assert(other.has_reg() &&
"cannot initialize with invalid register");
664 Reg value_reg = other.cur_reg();
665 assert(reg_file.is_fixed(value_reg));
666 assert(reg_file.is_used(value_reg));
667 assert(reg_file.is_clobbered(value_reg));
668 assert(!state.c.reg.valid() &&
669 "attempted to overwrite already initialized and locked ValuePartRef");
671 if (!has_assignment()) {
672 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
673 state.c.reg = value_reg;
674 state.c.owned =
true;
675 assert(reg_file.reg_local_idx(value_reg) == INVALID_VAL_LOCAL_IDX);
676 assert(reg_file.reg_part(value_reg) == 0);
677 other.force_set_reg(AsmReg::make_invalid());
682 auto ap = assignment();
683 assert(!ap.variable_ref() &&
"cannot update variable ref");
685 if (ap.fixed_assignment()) {
687 auto cur_reg = ap.get_reg();
688 assert(reg_file.is_used(cur_reg));
689 assert(reg_file.is_fixed(cur_reg));
690 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
691 assert(ap.register_valid() && !ap.stack_valid() &&
692 "invalid state for fixed assignment");
693 assert(cur_reg != value_reg);
694 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
700 assert(!ap.register_valid() && !ap.stack_valid() &&
701 "attempted to overwrite already initialized ValuePartRef");
704 reg_file.unmark_fixed(value_reg);
705 reg_file.update_reg_assignment(value_reg, local_idx(), part());
706 ap.set_reg(value_reg);
707 ap.set_register_valid(
true);
708 other.force_set_reg(AsmReg::make_invalid());
711template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
712void CompilerBase<Adaptor, Derived, Config>::ValuePart::set_value_reg(
714 assert(compiler->may_change_value_state());
716 auto ®_file = compiler->register_file;
719 assert(value_reg.valid() &&
"cannot initialize with invalid register");
720 assert(!state.c.reg.valid() &&
721 "attempted to overwrite already initialized and locked ValuePartRef");
723 if (!has_assignment()) {
724 assert(!is_const() &&
"cannot mutate constant ValuePartRef");
725 state.c.reg = value_reg;
726 state.c.owned =
true;
727 reg_file.mark_used(state.c.reg, INVALID_VAL_LOCAL_IDX, 0);
728 reg_file.mark_fixed(state.c.reg);
733 auto ap = assignment();
734 assert(!ap.variable_ref() &&
"cannot update variable ref");
736 if (ap.fixed_assignment()) {
738 auto cur_reg = ap.get_reg();
739 assert(reg_file.is_used(cur_reg));
740 assert(reg_file.is_fixed(cur_reg));
741 assert(reg_file.reg_local_idx(cur_reg) == local_idx());
743 assert(cur_reg != value_reg);
744 compiler->derived()->mov(cur_reg, value_reg, ap.part_size());
745 ap.set_register_valid(
true);
746 ap.set_modified(
true);
751 assert(!ap.register_valid() && !ap.stack_valid() &&
752 "attempted to overwrite already initialized ValuePartRef");
754 reg_file.mark_used(value_reg, local_idx(), part());
755 reg_file.mark_clobbered(value_reg);
756 ap.set_reg(value_reg);
757 ap.set_register_valid(
true);
758 ap.set_modified(
true);
761template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
762typename CompilerBase<Adaptor, Derived, Config>::AsmReg
763 CompilerBase<Adaptor, Derived, Config>::ValuePart::salvage_keep_used(
765 assert(compiler->may_change_value_state());
766 assert(can_salvage());
767 if (!has_assignment()) {
768 AsmReg reg = state.c.reg;
769 compiler->register_file.unmark_fixed(reg);
770 state.c.reg = AsmReg::make_invalid();
774 auto ap = assignment();
775 assert(ap.register_valid());
776 auto cur_reg = ap.get_reg();
779 assert(ap.fixed_assignment() || !compiler->register_file.is_fixed(cur_reg));
780 if (ap.fixed_assignment()) {
781 compiler->register_file.dec_lock_count(cur_reg);
784 ap.set_register_valid(
false);
785 ap.set_fixed_assignment(
false);
789template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
790void CompilerBase<Adaptor, Derived, Config>::ValuePart::reset(
792 AsmReg reg = state.c.reg;
800 assert(!has_assignment() || assignment().modified() ||
true);
804 if (has_assignment()) {
805 AssignmentPartRef ap = assignment();
806 bool fixed = ap.fixed_assignment();
807 ap.set_register_valid(
false);
808 ap.set_fixed_assignment(
false);
809 compiler->register_file.dec_lock_count_must_zero(reg, fixed ? 2 : 1);
811 compiler->register_file.unmark_fixed(reg);
813 compiler->register_file.unmark_used(reg);
814 }
else if (has_assignment()) {
815 compiler->register_file.dec_lock_count(reg);
818 state.c.reg = AsmReg::make_invalid();
821template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
822struct CompilerBase<Adaptor, Derived, Config>::ValuePartRef : ValuePart {
825 template <
typename... Args>
826 ValuePartRef(
CompilerBase *compiler, Args &&...args) noexcept
827 : ValuePart(std::forward<Args>(args)...), compiler(compiler) {}
829 explicit ValuePartRef(
const ValuePartRef &) =
delete;
831 ValuePartRef(ValuePartRef &&other) noexcept
832 : ValuePart(std::move(other)), compiler(other.compiler) {}
834 ~ValuePartRef() noexcept {
reset(); }
836 ValuePartRef &operator=(
const ValuePartRef &) =
delete;
838 ValuePartRef &operator=(ValuePartRef &&other)
noexcept {
839 if (
this == &other) {
843 ValuePart::operator=(std::move(other));
847 AsmReg alloc_reg(u64 exclusion_mask = 0) noexcept {
848 return ValuePart::alloc_reg(compiler, exclusion_mask);
851 AsmReg alloc_try_reuse(ValuePart &ref)
noexcept {
852 return ValuePart::alloc_try_reuse(compiler, ref);
855 void alloc_specific(AsmReg reg)
noexcept {
856 ValuePart::alloc_specific(compiler, reg);
859 AsmReg load_to_reg() noexcept {
return ValuePart::load_to_reg(compiler); }
861 void load_to_specific(AsmReg reg)
noexcept {
862 ValuePart::load_to_specific(compiler, reg);
865 AsmReg reload_into_specific_fixed(AsmReg reg,
unsigned size = 0) noexcept {
866 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
869 AsmReg reload_into_specific_fixed(
CompilerBase *compiler,
871 unsigned size = 0) noexcept {
872 return ValuePart::reload_into_specific_fixed(compiler, reg, size);
875 ValuePartRef get_unowned_ref() noexcept {
876 return ValuePartRef{compiler, ValuePart::get_unowned()};
879 ValuePartRef into_temporary() &&
noexcept {
882 std::move(*
static_cast<ValuePart *
>(
this)).into_temporary(compiler)};
885 ValuePartRef into_extended(
bool sign, u32 from, u32 to) &&
noexcept {
886 return ValuePartRef{compiler,
887 std::move(*
static_cast<ValuePart *
>(
this))
888 .into_extended(compiler, sign, from, to)};
891 void lock() noexcept { ValuePart::lock(compiler); }
892 void unlock() noexcept { ValuePart::unlock(compiler); }
894 void set_value(ValuePart &&other)
noexcept {
895 ValuePart::set_value(compiler, std::move(other));
898 void set_value(ScratchReg &&other)
noexcept {
899 ValuePart::set_value(compiler, std::move(other));
902 void set_value_reg(AsmReg value_reg)
noexcept {
903 ValuePart::set_value_reg(compiler, value_reg);
906 AsmReg salvage() noexcept {
return ValuePart::salvage(compiler); }
908 void reset() noexcept { ValuePart::reset(compiler); }
CompilerBase(Adaptor *adaptor)
Initialize a CompilerBase, should be called by the derived classes.