423 void move_to_phi_nodes(BlockIndex target)
noexcept {
424 if (analyzer.block_has_phis(target)) {
425 move_to_phi_nodes_impl(target);
429 void move_to_phi_nodes_impl(BlockIndex target)
noexcept;
431 bool branch_needs_split(IRBlockRef target)
noexcept {
433 return analyzer.block_has_phis(target);
436 BlockIndex next_block() const noexcept;
438 bool try_force_fixed_assignment(IRValueRef) const noexcept {
return false; }
440 bool hook_post_func_sym_init() noexcept {
return true; }
442 void analysis_start() noexcept {}
444 void analysis_end() noexcept {}
446 void reloc_text(SymRef sym, u32 type, u64 offset, i64 addend = 0) noexcept {
447 this->assembler.reloc_sec(
448 text_writer.get_sec_ref(), sym, type, offset, addend);
451 void label_place(Label label)
noexcept {
452 this->text_writer.label_place(label, text_writer.offset());
456 SymRef get_personality_sym() noexcept;
458 bool compile_func(IRFuncRef func, u32 func_idx) noexcept;
460 bool compile_block(IRBlockRef block, u32 block_idx) noexcept;
464#include "GenericValuePart.hpp"
465#include "ScratchReg.hpp"
466#include "ValuePartRef.hpp"
467#include "ValueRef.hpp"
471template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
472template <
typename CBDerived>
473void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
474 CBDerived>::add_arg(ValuePart &&vp, CCAssignment cca)
noexcept {
476 cca.bank = vp.bank();
477 cca.size = vp.part_size();
480 assigner.assign_arg(cca);
481 bool needs_ext = cca.int_ext != 0;
482 bool ext_sign = cca.int_ext >> 7;
483 unsigned ext_bits = cca.int_ext & 0x3f;
486 derived()->add_arg_byval(vp, cca);
488 }
else if (!cca.reg.valid()) {
490 auto ext = std::move(vp).into_extended(&compiler, ext_sign, ext_bits, 64);
491 derived()->add_arg_stack(ext, cca);
492 ext.reset(&compiler);
494 derived()->add_arg_stack(vp, cca);
498 u32 size = vp.part_size();
499 if (vp.is_in_reg(cca.reg)) {
500 if (!vp.can_salvage()) {
501 compiler.evict_reg(cca.reg);
503 vp.salvage(&compiler);
506 compiler.generate_raw_intext(cca.reg, cca.reg, ext_sign, ext_bits, 64);
509 if (compiler.register_file.is_used(cca.reg)) {
510 compiler.evict_reg(cca.reg);
512 if (vp.can_salvage()) {
513 AsmReg vp_reg = vp.salvage(&compiler);
515 compiler.generate_raw_intext(cca.reg, vp_reg, ext_sign, ext_bits, 64);
517 compiler.mov(cca.reg, vp_reg, size);
520 vp.reload_into_specific_fixed(&compiler, cca.reg);
522 compiler.generate_raw_intext(
523 cca.reg, cca.reg, ext_sign, ext_bits, 64);
528 assert(!compiler.register_file.is_used(cca.reg));
529 compiler.register_file.mark_clobbered(cca.reg);
530 compiler.register_file.allocatable &= ~(u64{1} << cca.reg.id());
531 arg_regs |= (1ull << cca.reg.id());
535template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
536template <
typename CBDerived>
537void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
538 CBDerived>::add_arg(
const CallArg &arg, u32 part_count)
noexcept {
539 ValueRef vr = compiler.val_ref(arg.value);
541 if (arg.flag == CallArg::Flag::byval) {
542 assert(part_count == 1);
546 .align = arg.byval_align,
547 .size = arg.byval_size,
553 bool consecutive =
false;
555 if (compiler.arg_is_int128(arg.value)) {
559 }
else if (part_count > 1 &&
560 !compiler.arg_allow_split_reg_stack_passing(arg.value)) {
562 if (part_count > UINT8_MAX) {
569 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
571 if (arg.flag == CallArg::Flag::sext || arg.flag == CallArg::Flag::zext) {
572 assert(arg.ext_bits != 0 &&
"cannot extend zero-bit integer");
573 int_ext = arg.ext_bits | (arg.flag == CallArg::Flag::sext ? 0x80 : 0);
579 u8(consecutive ? part_count - part_idx - 1 : consec_def),
580 .sret = arg.flag == CallArg::Flag::sret,
582 .align = u8(part_idx == 0 ? align : 1),
587template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
588template <
typename CBDerived>
589void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<CBDerived>::call(
590 std::variant<SymRef, ValuePart> target)
noexcept {
591 typename RegisterFile::RegBitSet skip_evict = arg_regs;
592 if (
auto *vp = std::get_if<ValuePart>(&target); vp && vp->can_salvage()) {
594 assert(vp->cur_reg_unlocked().valid() &&
"can_salvage implies register");
595 skip_evict |= (1ull << vp->cur_reg_unlocked().
id());
598 auto clobbered = ~assigner.get_ccinfo().callee_saved_regs;
599 for (
auto reg_id : util::BitSetIterator<>{compiler.register_file.used &
600 clobbered & ~skip_evict}) {
601 compiler.evict_reg(AsmReg{reg_id});
602 compiler.register_file.mark_clobbered(Reg{reg_id});
605 derived()->call_impl(std::move(target));
607 assert((compiler.register_file.allocatable & arg_regs) == 0);
608 compiler.register_file.allocatable |= arg_regs;
611template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
612template <
typename CBDerived>
613void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
614 CBDerived>::add_ret(ValuePart &vp, CCAssignment cca)
noexcept {
615 cca.bank = vp.bank();
616 cca.size = vp.part_size();
617 assigner.assign_ret(cca);
618 assert(cca.reg.valid() &&
"return value must be in register");
619 vp.set_value_reg(&compiler, cca.reg);
622template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
623template <
typename CBDerived>
624void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
625 CBDerived>::add_ret(ValueRef &vr)
noexcept {
626 assert(vr.has_assignment());
627 u32 part_count = vr.assignment()->part_count;
628 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
630 add_ret(vr.part(part_idx), cca);
634template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
635void CompilerBase<Adaptor, Derived, Config>::RetBuilder::add(
636 ValuePart &&vp, CCAssignment cca)
noexcept {
637 cca.bank = vp.bank();
638 u32 size = cca.size = vp.part_size();
639 assigner.assign_ret(cca);
640 assert(cca.reg.valid() &&
"indirect return value must use sret argument");
642 bool needs_ext = cca.int_ext != 0;
643 bool ext_sign = cca.int_ext >> 7;
644 unsigned ext_bits = cca.int_ext & 0x3f;
646 if (vp.is_in_reg(cca.reg)) {
647 if (!vp.can_salvage()) {
648 compiler.evict_reg(cca.reg);
650 vp.salvage(&compiler);
653 compiler.generate_raw_intext(cca.reg, cca.reg, ext_sign, ext_bits, 64);
656 if (compiler.register_file.is_used(cca.reg)) {
657 compiler.evict_reg(cca.reg);
659 if (vp.can_salvage()) {
660 AsmReg vp_reg = vp.salvage(&compiler);
662 compiler.generate_raw_intext(cca.reg, vp_reg, ext_sign, ext_bits, 64);
664 compiler.mov(cca.reg, vp_reg, size);
667 vp.reload_into_specific_fixed(&compiler, cca.reg);
669 compiler.generate_raw_intext(cca.reg, cca.reg, ext_sign, ext_bits, 64);
674 assert(!compiler.register_file.is_used(cca.reg));
675 compiler.register_file.mark_clobbered(cca.reg);
676 compiler.register_file.allocatable &= ~(u64{1} << cca.reg.id());
677 ret_regs |= (1ull << cca.reg.id());
680template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
681void CompilerBase<Adaptor, Derived, Config>::RetBuilder::add(
682 IRValueRef val)
noexcept {
683 u32 part_count = compiler.val_parts(val).count();
684 ValueRef vr = compiler.val_ref(val);
685 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
686 add(vr.part(part_idx), CCAssignment{});
690template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
691void CompilerBase<Adaptor, Derived, Config>::RetBuilder::ret() noexcept {
692 assert((compiler.register_file.allocatable & ret_regs) == 0);
693 compiler.register_file.allocatable |= ret_regs;
695 compiler.gen_func_epilog();
696 compiler.release_regs_after_return();
699template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
702 text_writer.switch_section(
703 assembler.get_section(assembler.get_text_section()));
705 assert(func_syms.empty());
706 for (
const IRFuncRef func : adaptor->funcs()) {
707 auto binding = Assembler::SymBinding::GLOBAL;
708 if (adaptor->func_has_weak_linkage(func)) {
709 binding = Assembler::SymBinding::WEAK;
710 }
else if (adaptor->func_only_local(func)) {
711 binding = Assembler::SymBinding::LOCAL;
713 if (adaptor->func_extern(func)) {
714 func_syms.push_back(
derived()->assembler.sym_add_undef(
715 adaptor->func_link_name(func), binding));
717 func_syms.push_back(
derived()->assembler.sym_predef_func(
718 adaptor->func_link_name(func), binding));
720 derived()->define_func_idx(func, func_syms.size() - 1);
723 if (!
derived()->hook_post_func_sym_init()) {
724 TPDE_LOG_ERR(
"hook_pust_func_sym_init failed");
733 for (
const IRFuncRef func : adaptor->funcs()) {
734 if (adaptor->func_extern(func)) {
735 TPDE_LOG_TRACE(
"Skipping compilation of func {}",
736 adaptor->func_link_name(func));
741 TPDE_LOG_TRACE(
"Compiling func {}", adaptor->func_link_name(func));
742 if (!
derived()->compile_func(func, func_idx)) {
743 TPDE_LOG_ERR(
"Failed to compile function {}",
744 adaptor->func_link_name(func));
751 assembler.finalize();
758template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
762 for (
auto &e : stack.fixed_free_lists) {
765 stack.dynamic_free_lists.clear();
769 block_labels.clear();
770 personality_syms.clear();
773template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
774void CompilerBase<Adaptor, Derived, Config>::init_assignment(
775 IRValueRef value, ValLocalIdx local_idx)
noexcept {
776 assert(val_assignment(local_idx) ==
nullptr);
777 TPDE_LOG_TRACE(
"Initializing assignment for value {}",
778 static_cast<u32
>(local_idx));
780 const auto parts =
derived()->val_parts(value);
781 const u32 part_count = parts.count();
782 assert(part_count > 0);
783 auto *assignment = assignments.allocator.allocate(part_count);
784 assignments.value_ptrs[
static_cast<u32
>(local_idx)] = assignment;
786 u32 max_part_size = 0;
787 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
788 auto ap = AssignmentPartRef{assignment, part_idx};
790 ap.set_bank(parts.reg_bank(part_idx));
791 const u32 size = parts.size_bytes(part_idx);
793 max_part_size = std::max(max_part_size, size);
794 ap.set_part_size(size);
797 const auto &liveness = analyzer.liveness_info(local_idx);
806 if (part_count == 1) {
807 const auto &cur_loop =
808 analyzer.loop_from_idx(analyzer.block_loop_idx(cur_block_idx));
809 auto ap = AssignmentPartRef{assignment, 0};
812 liveness.last > cur_block_idx &&
813 cur_loop.definitions_in_childs +
814 assignments.cur_fixed_assignment_count[ap.bank().id()] <
815 Derived::NUM_FIXED_ASSIGNMENTS[ap.bank().id()];
816 if (
derived()->try_force_fixed_assignment(value)) {
817 try_fixed = assignments.cur_fixed_assignment_count[ap.bank().id()] <
818 Derived::NUM_FIXED_ASSIGNMENTS[ap.bank().id()];
823 AsmReg reg =
derived()->select_fixed_assignment_reg(ap, value);
824 TPDE_LOG_TRACE(
"Trying to assign fixed reg to value {}",
825 static_cast<u32
>(local_idx));
829 if (!reg.invalid() && !register_file.is_used(reg)) {
830 TPDE_LOG_TRACE(
"Assigning fixed assignment to reg {} for value {}",
832 static_cast<u32
>(local_idx));
834 ap.set_register_valid(
true);
835 ap.set_fixed_assignment(
true);
836 register_file.mark_used(reg, local_idx, 0);
837 register_file.inc_lock_count(reg);
838 register_file.mark_clobbered(reg);
839 ++assignments.cur_fixed_assignment_count[ap.bank().id()];
844 const auto last_full = liveness.last_full;
845 const auto ref_count = liveness.ref_count;
847 assert(max_part_size <= 256);
848 assignment->max_part_size = max_part_size;
849 assignment->pending_free =
false;
850 assignment->variable_ref =
false;
851 assignment->stack_variable =
false;
852 assignment->delay_free = last_full;
853 assignment->part_count = part_count;
854 assignment->frame_off = 0;
855 assignment->references_left = ref_count;
858template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
860 ValLocalIdx local_idx, ValueAssignment *assignment)
noexcept {
861 TPDE_LOG_TRACE(
"Freeing assignment for value {}",
862 static_cast<u32
>(local_idx));
864 assert(assignments.value_ptrs[
static_cast<u32
>(local_idx)] == assignment);
865 assignments.value_ptrs[
static_cast<u32
>(local_idx)] =
nullptr;
866 const auto is_var_ref = assignment->variable_ref;
867 const u32 part_count = assignment->part_count;
870 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
871 auto ap = AssignmentPartRef{assignment, part_idx};
872 if (ap.fixed_assignment()) [[unlikely]] {
873 const auto reg = ap.get_reg();
874 assert(register_file.is_fixed(reg));
875 assert(register_file.reg_local_idx(reg) == local_idx);
876 assert(register_file.reg_part(reg) == part_idx);
877 --assignments.cur_fixed_assignment_count[ap.bank().id()];
878 register_file.dec_lock_count_must_zero(reg);
879 register_file.unmark_used(reg);
880 }
else if (ap.register_valid()) {
881 const auto reg = ap.get_reg();
882 assert(!register_file.is_fixed(reg));
883 register_file.unmark_used(reg);
888 for (
auto reg_id : register_file.used_regs()) {
889 assert(register_file.reg_local_idx(AsmReg{reg_id}) != local_idx &&
890 "freeing assignment that is still referenced by a register");
895 if (!is_var_ref && assignment->frame_off != 0) {
896 free_stack_slot(assignment->frame_off, assignment->size());
899 assignments.allocator.deallocate(assignment);
902template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
903[[gnu::noinline]]
void
905 ValLocalIdx local_idx, ValueAssignment *assignment)
noexcept {
906 if (!assignment->delay_free) {
907 free_assignment(local_idx, assignment);
912 TPDE_LOG_TRACE(
"Delay freeing assignment for value {}",
913 static_cast<u32
>(local_idx));
914 const auto &liveness = analyzer.liveness_info(local_idx);
915 auto &free_list_head = assignments.delayed_free_lists[u32(liveness.last)];
916 assignment->next_delayed_free_entry = free_list_head;
917 assignment->pending_free =
true;
918 free_list_head = local_idx;
921template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
923 ValLocalIdx local_idx, u32 var_ref_data)
noexcept {
924 TPDE_LOG_TRACE(
"Initializing variable-ref assignment for value {}",
925 static_cast<u32
>(local_idx));
927 assert(val_assignment(local_idx) ==
nullptr);
928 auto *assignment = assignments.allocator.allocate_slow(1,
true);
929 assignments.value_ptrs[
static_cast<u32
>(local_idx)] = assignment;
931 assignment->max_part_size = Config::PLATFORM_POINTER_SIZE;
932 assignment->variable_ref =
true;
933 assignment->stack_variable =
false;
934 assignment->part_count = 1;
935 assignment->var_ref_custom_idx = var_ref_data;
936 assignment->next_delayed_free_entry = assignments.variable_ref_list;
938 assignments.variable_ref_list = local_idx;
940 AssignmentPartRef ap{assignment, 0};
942 ap.set_bank(Config::GP_BANK);
943 ap.set_part_size(Config::PLATFORM_POINTER_SIZE);
946template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
947i32 CompilerBase<Adaptor, Derived, Config>::allocate_stack_slot(
949 unsigned align_bits = 4;
952 }
else if (size <= 16) {
954 u32 free_list_idx = size == 1 ? 0 : 32 - util::cnt_lz<u32>(size - 1);
955 assert(size <= 1u << free_list_idx);
956 size = 1 << free_list_idx;
957 align_bits = free_list_idx;
959 if (!stack.fixed_free_lists[free_list_idx].empty()) {
960 auto slot = stack.fixed_free_lists[free_list_idx].back();
961 stack.fixed_free_lists[free_list_idx].pop_back();
965 size = util::align_up(size, 16);
966 auto it = stack.dynamic_free_lists.find(size);
967 if (it != stack.dynamic_free_lists.end() && !it->second.empty()) {
968 const auto slot = it->second.back();
969 it->second.pop_back();
974 assert(stack.frame_size != ~0u &&
975 "cannot allocate stack slot before stack frame is initialized");
978 for (u32 list_idx = util::cnt_tz(stack.frame_size); list_idx < align_bits;
979 list_idx = util::cnt_tz(stack.frame_size)) {
980 i32 slot = stack.frame_size;
981 if constexpr (Config::FRAME_INDEXING_NEGATIVE) {
982 slot = -(slot + (1ull << list_idx));
984 stack.fixed_free_lists[list_idx].push_back(slot);
985 stack.frame_size += 1ull << list_idx;
988 auto slot = stack.frame_size;
989 assert(slot != 0 &&
"stack slot 0 is reserved");
990 stack.frame_size += size;
992 if constexpr (Config::FRAME_INDEXING_NEGATIVE) {
993 slot = -(slot + size);
998template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1000 u32 slot, u32 size)
noexcept {
1001 if (size == 0) [[unlikely]] {
1002 assert(slot == 0 &&
"unexpected slot for zero-sized stack-slot?");
1004 }
else if (size <= 16) [[likely]] {
1005 u32 free_list_idx = size == 1 ? 0 : 32 - util::cnt_lz<u32>(size - 1);
1006 stack.fixed_free_lists[free_list_idx].push_back(slot);
1008 size = util::align_up(size, 16);
1009 stack.dynamic_free_lists[size].push_back(slot);
1013template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1014template <
typename Fn>
1016 u32 arg_idx, IRValueRef arg, Fn add_arg)
noexcept {
1017 ValueRef vr =
derived()->result_ref(arg);
1018 if (adaptor->cur_arg_is_byval(arg_idx)) {
1019 std::optional<i32> byval_frame_off =
1023 .align = u8(adaptor->cur_arg_byval_align(arg_idx)),
1024 .size = adaptor->cur_arg_byval_size(arg_idx),
1027 if (byval_frame_off) {
1029 ValLocalIdx local_idx = val_idx(arg);
1035 if (ValueAssignment *assignment = val_assignment(local_idx)) {
1036 free_assignment(local_idx, assignment);
1039 ValueAssignment *assignment = this->val_assignment(local_idx);
1040 assignment->stack_variable =
true;
1041 assignment->frame_off = *byval_frame_off;
1046 if (adaptor->cur_arg_is_sret(arg_idx)) {
1047 add_arg(vr.part(0), CCAssignment{.sret = true});
1051 const u32 part_count = vr.assignment()->part_count;
1054 u32 consecutive = 0;
1056 if (
derived()->arg_is_int128(arg)) {
1060 }
else if (part_count > 1 &&
1061 !
derived()->arg_allow_split_reg_stack_passing(arg)) {
1063 if (part_count > UINT8_MAX) {
1070 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
1071 add_arg(vr.part(part_idx),
1074 u8(consecutive ? part_count - part_idx - 1 : consec_def),
1075 .align = u8(part_idx == 0 ? align : 1),
1080template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1083 if (
auto special =
derived()->val_ref_special(value); special) {
1084 return ValueRef{
this, std::move(*special)};
1087 const ValLocalIdx local_idx = analyzer.adaptor->val_local_idx(value);
1088 assert(val_assignment(local_idx) !=
nullptr &&
"value use before def");
1089 return ValueRef{
this, local_idx};
1092template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1093std::pair<typename CompilerBase<Adaptor, Derived, Config>::ValueRef,
1096 IRValueRef value)
noexcept {
1097 std::pair<ValueRef, ValuePartRef> res{val_ref(value),
this};
1098 res.second = res.first.part(0);
1102template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1105 IRValueRef value)
noexcept {
1106 const ValLocalIdx local_idx = analyzer.adaptor->val_local_idx(value);
1107 if (val_assignment(local_idx) ==
nullptr) {
1108 init_assignment(value, local_idx);
1110 return ValueRef{
this, local_idx};
1113template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1114std::pair<typename CompilerBase<Adaptor, Derived, Config>::ValueRef,
1115 typename CompilerBase<Adaptor, Derived, Config>::ValuePartRef>
1116 CompilerBase<Adaptor, Derived, Config>::result_ref_single(
1117 IRValueRef value)
noexcept {
1118 std::pair<ValueRef, ValuePartRef> res{
result_ref(value),
this};
1119 res.second = res.first.part(0);
1123template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1125 ValuePartRef &val_ref, ScratchReg &scratch)
noexcept {
1126 val_ref.set_value(std::move(scratch));
1129template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1132 GenericValuePart &gv)
noexcept {
1133 if (std::holds_alternative<ScratchReg>(gv.state)) {
1134 return std::get<ScratchReg>(gv.state).cur_reg();
1136 if (std::holds_alternative<ValuePartRef>(gv.state)) {
1137 auto &vpr = std::get<ValuePartRef>(gv.state);
1138 if (vpr.has_reg()) {
1139 return vpr.cur_reg();
1141 return vpr.load_to_reg();
1143 if (
auto *expr = std::get_if<typename GenericValuePart::Expr>(&gv.state)) {
1144 if (expr->has_base() && !expr->has_index() && expr->disp == 0) {
1145 return expr->base_reg();
1147 return derived()->gval_expr_as_reg(gv);
1149 TPDE_UNREACHABLE(
"gval_as_reg on empty GenericValuePart");
1152template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1153typename CompilerBase<Adaptor, Derived, Config>::AsmReg
1155 GenericValuePart &gv, ScratchReg &dst)
noexcept {
1157 if (!dst.has_reg()) {
1158 if (
auto *scratch = std::get_if<ScratchReg>(&gv.state)) {
1159 dst = std::move(*scratch);
1160 }
else if (
auto *val_ref = std::get_if<ValuePartRef>(&gv.state)) {
1161 if (val_ref->can_salvage()) {
1162 dst.alloc_specific(val_ref->salvage());
1163 assert(dst.cur_reg() == reg &&
"salvaging unsuccessful");
1170template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1171Reg CompilerBase<Adaptor, Derived, Config>::select_reg_evict(
1172 RegBank bank, u64 exclusion_mask)
noexcept {
1173 TPDE_LOG_DBG(
"select_reg_evict for bank {}", bank.id());
1175 register_file.used & register_file.bank_regs(bank) & ~exclusion_mask;
1177 Reg candidate = Reg::make_invalid();
1179 for (
auto reg_id : util::BitSetIterator<>(candidates)) {
1181 if (register_file.is_fixed(reg)) {
1186 auto local_idx = register_file.reg_local_idx(reg);
1187 u32 part = register_file.reg_part(Reg{reg});
1188 assert(local_idx != INVALID_VAL_LOCAL_IDX);
1189 ValueAssignment *va = val_assignment(local_idx);
1190 AssignmentPartRef ap{va, part};
1203 if (ap.variable_ref()) {
1204 TPDE_LOG_DBG(
" r{} ({}) is variable-ref", reg_id, u32(local_idx));
1210 if (ap.stack_valid()) {
1211 score |= u32{1} << 31;
1214 const auto &liveness = analyzer.liveness_info(local_idx);
1215 u32 last_use_dist = u32(liveness.last) - u32(cur_block_idx);
1216 score |= (last_use_dist < 0x8000 ? 0x8000 - last_use_dist : 0) << 16;
1218 u32 refs_left = va->pending_free ? 0 : va->references_left;
1219 score |= (refs_left < 0xffff ? 0x10000 - refs_left : 1);
1221 TPDE_LOG_DBG(
" r{} ({}:{}) rc={}/{} live={}-{}{} spilled={} score={:#x}",
1227 u32(liveness.first),
1229 &
"*"[!liveness.last_full],
1234 if (score > max_score) {
1239 if (candidate.invalid()) [[unlikely]] {
1240 TPDE_FATAL(
"ran out of registers for scratch registers");
1242 TPDE_LOG_DBG(
" selected r{}", candidate.id());
1247template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1249 AsmReg dst, AssignmentPartRef ap)
noexcept {
1250 if (!ap.variable_ref()) {
1251 assert(ap.stack_valid());
1252 derived()->load_from_stack(dst, ap.frame_off(), ap.part_size());
1253 }
else if (ap.is_stack_variable()) {
1254 derived()->load_address_of_stack_var(dst, ap);
1255 }
else if constexpr (!Config::DEFAULT_VAR_REF_HANDLING) {
1256 derived()->load_address_of_var_reference(dst, ap);
1258 TPDE_UNREACHABLE(
"non-stack-variable needs custom var-ref handling");
1262template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1263void CompilerBase<Adaptor, Derived, Config>::allocate_spill_slot(
1264 AssignmentPartRef ap)
noexcept {
1265 assert(!ap.variable_ref() &&
"cannot allocate spill slot for variable ref");
1266 if (ap.assignment()->frame_off == 0) {
1267 assert(!ap.stack_valid() &&
"stack-valid set without spill slot");
1268 ap.assignment()->frame_off = allocate_stack_slot(ap.assignment()->size());
1269 assert(ap.assignment()->frame_off != 0);
1273template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1275 AssignmentPartRef ap)
noexcept {
1276 assert(may_change_value_state());
1277 if (!ap.stack_valid() && !ap.variable_ref()) {
1278 assert(ap.register_valid() &&
"cannot spill uninitialized assignment part");
1279 allocate_spill_slot(ap);
1280 derived()->spill_reg(ap.get_reg(), ap.frame_off(), ap.part_size());
1281 ap.set_stack_valid();
1285template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1287 AssignmentPartRef ap)
noexcept {
1288 assert(may_change_value_state());
1289 assert(ap.register_valid());
1291 ap.set_register_valid(
false);
1292 register_file.unmark_used(ap.get_reg());
1295template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1297 assert(may_change_value_state());
1298 assert(!register_file.is_fixed(reg));
1299 assert(register_file.reg_local_idx(reg) != INVALID_VAL_LOCAL_IDX);
1301 ValLocalIdx local_idx = register_file.reg_local_idx(reg);
1302 auto part = register_file.reg_part(reg);
1303 AssignmentPartRef evict_part{val_assignment(local_idx), part};
1304 assert(evict_part.register_valid());
1305 assert(evict_part.get_reg() == reg);
1307 evict_part.set_register_valid(
false);
1308 register_file.unmark_used(reg);
1311template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1313 assert(may_change_value_state());
1314 assert(!register_file.is_fixed(reg));
1315 assert(register_file.reg_local_idx(reg) != INVALID_VAL_LOCAL_IDX);
1317 ValLocalIdx local_idx = register_file.reg_local_idx(reg);
1318 auto part = register_file.reg_part(reg);
1319 AssignmentPartRef ap{val_assignment(local_idx), part};
1320 assert(ap.register_valid());
1321 assert(ap.get_reg() == reg);
1322 assert(!ap.modified() || ap.variable_ref());
1323 ap.set_register_valid(
false);
1324 register_file.unmark_used(reg);
1327template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1328typename CompilerBase<Adaptor, Derived, Config>::RegisterFile::RegBitSet
1329 CompilerBase<Adaptor, Derived, Config>::spill_before_branch(
1330 bool force_spill)
noexcept {
1354 using RegBitSet =
typename RegisterFile::RegBitSet;
1356 assert(may_change_value_state());
1358 const IRBlockRef cur_block_ref = analyzer.block_ref(cur_block_idx);
1362 BlockIndex earliest_next_succ = Analyzer<Adaptor>::INVALID_BLOCK_IDX;
1364 bool must_spill = force_spill;
1368 auto next_block_is_succ =
false;
1369 auto next_block_has_multiple_incoming =
false;
1371 for (
const IRBlockRef succ : adaptor->block_succs(cur_block_ref)) {
1373 BlockIndex succ_idx = analyzer.block_idx(succ);
1374 if (u32(succ_idx) == u32(cur_block_idx) + 1) {
1375 next_block_is_succ =
true;
1376 if (analyzer.block_has_multiple_incoming(succ)) {
1377 next_block_has_multiple_incoming =
true;
1379 }
else if (succ_idx > cur_block_idx && succ_idx < earliest_next_succ) {
1380 earliest_next_succ = succ_idx;
1384 must_spill = !next_block_is_succ || next_block_has_multiple_incoming;
1386 if (succ_count == 1 && !must_spill) {
1391 auto release_regs = RegBitSet{};
1393 for (
auto reg : register_file.used_regs()) {
1394 auto local_idx = register_file.reg_local_idx(Reg{reg});
1395 auto part = register_file.reg_part(Reg{reg});
1396 if (local_idx == INVALID_VAL_LOCAL_IDX) {
1400 AssignmentPartRef ap{val_assignment(local_idx), part};
1401 if (ap.fixed_assignment()) {
1409 release_regs |= RegBitSet{1ull} << reg;
1412 if (!ap.modified() || ap.variable_ref()) {
1417 const auto &liveness = analyzer.liveness_info(local_idx);
1418 if (liveness.last <= cur_block_idx) {
1431 if (must_spill || earliest_next_succ <= liveness.last) {
1436 return release_regs;
1439template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1441 typename RegisterFile::RegBitSet regs)
noexcept {
1442 assert(may_change_value_state());
1445 for (
auto reg_id : util::BitSetIterator<>{regs & register_file.used}) {
1446 if (!register_file.is_fixed(Reg{reg_id})) {
1452template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1456 for (
auto reg_id : register_file.used_regs()) {
1457 if (!register_file.is_fixed(Reg{reg_id})) {