403 void move_to_phi_nodes(BlockIndex target)
noexcept {
404 if (analyzer.block_has_phis(target)) {
405 move_to_phi_nodes_impl(target);
409 void move_to_phi_nodes_impl(BlockIndex target)
noexcept;
411 bool branch_needs_split(IRBlockRef target)
noexcept {
413 return analyzer.block_has_phis(target);
416 BlockIndex next_block() const noexcept;
418 bool try_force_fixed_assignment(IRValueRef) const noexcept {
return false; }
420 bool hook_post_func_sym_init() noexcept {
return true; }
422 void analysis_start() noexcept {}
424 void analysis_end() noexcept {}
426 void reloc_text(Assembler::SymRef sym,
429 i64 addend = 0) noexcept {
430 this->assembler.reloc_sec(
431 text_writer.get_sec_ref(), sym, type, offset, addend);
434 void label_place(Assembler::Label label)
noexcept {
435 this->assembler.label_place(
436 label, text_writer.get_sec_ref(), text_writer.offset());
440 Assembler::SymRef get_personality_sym() noexcept;
442 bool compile_func(IRFuncRef func, u32 func_idx) noexcept;
444 bool compile_block(IRBlockRef block, u32 block_idx) noexcept;
448#include "GenericValuePart.hpp"
449#include "ScratchReg.hpp"
450#include "ValuePartRef.hpp"
451#include "ValueRef.hpp"
455template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
456template <
typename CBDerived>
457void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
458 CBDerived>::add_arg(ValuePart &&vp, CCAssignment cca)
noexcept {
459 cca.bank = vp.bank();
460 cca.size = vp.part_size();
462 assigner.assign_arg(cca);
465 derived()->add_arg_byval(vp, cca);
467 }
else if (!cca.reg.valid()) {
468 derived()->add_arg_stack(vp, cca);
471 u32 size = vp.part_size();
472 if (vp.is_in_reg(cca.reg)) {
473 if (!vp.can_salvage()) {
474 compiler.evict_reg(cca.reg);
476 vp.salvage(&compiler);
478 if (cca.sext || cca.zext) {
479 compiler.generate_raw_intext(cca.reg, cca.reg, cca.sext, 8 * size, 64);
482 if (compiler.register_file.is_used(cca.reg)) {
483 compiler.evict_reg(cca.reg);
485 if (vp.can_salvage()) {
486 AsmReg vp_reg = vp.salvage(&compiler);
487 if (cca.sext || cca.zext) {
488 compiler.generate_raw_intext(cca.reg, vp_reg, cca.sext, 8 * size, 64);
490 compiler.mov(cca.reg, vp_reg, size);
493 vp.reload_into_specific_fixed(&compiler, cca.reg);
494 if (cca.sext || cca.zext) {
495 compiler.generate_raw_intext(
496 cca.reg, cca.reg, cca.sext, 8 * size, 64);
501 assert(!compiler.register_file.is_used(cca.reg));
502 compiler.register_file.mark_used(
503 cca.reg, CompilerBase::INVALID_VAL_LOCAL_IDX, 0);
504 compiler.register_file.mark_fixed(cca.reg);
505 compiler.register_file.mark_clobbered(cca.reg);
506 arg_regs |= (1ull << cca.reg.id());
510template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
511template <
typename CBDerived>
512void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
513 CBDerived>::add_arg(CallArg &&arg)
noexcept {
514 ValueRef vr = compiler.val_ref(arg.value);
515 const u32 part_count = compiler.val_parts(arg.value).count();
517 if (arg.flag == CallArg::Flag::byval) {
518 assert(part_count == 1);
522 .byval_align = arg.byval_align,
523 .byval_size = arg.byval_size,
529 bool consecutive =
false;
531 if (compiler.arg_is_int128(arg.value)) {
535 }
else if (part_count > 1 &&
536 !compiler.arg_allow_split_reg_stack_passing(arg.value)) {
538 if (part_count > UINT8_MAX) {
545 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
550 u8(consecutive ? part_count - part_idx - 1 : consec_def),
551 .sret = arg.flag == CallArg::Flag::sret,
552 .sext = arg.flag == CallArg::Flag::sext,
553 .zext = arg.flag == CallArg::Flag::zext,
554 .align = u8(part_idx == 0 ? align : 1),
559template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
560template <
typename CBDerived>
561void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<CBDerived>::call(
562 std::variant<typename Assembler::SymRef, ValuePart> target)
noexcept {
563 typename RegisterFile::RegBitSet skip_evict = arg_regs;
564 if (
auto *vp = std::get_if<ValuePart>(&target); vp && vp->can_salvage()) {
566 assert(vp->cur_reg_unlocked().valid() &&
"can_salvage implies register");
567 skip_evict |= (1ull << vp->cur_reg_unlocked().
id());
570 auto clobbered = ~assigner.get_ccinfo().callee_saved_regs;
571 for (
auto reg_id : util::BitSetIterator<>{compiler.register_file.used &
572 clobbered & ~skip_evict}) {
573 compiler.evict_reg(AsmReg{reg_id});
574 compiler.register_file.mark_clobbered(Reg{reg_id});
577 derived()->call_impl(std::move(target));
579 assert((compiler.register_file.fixed & arg_regs) == arg_regs);
580 assert((compiler.register_file.used & arg_regs) == arg_regs);
581 compiler.register_file.fixed &= ~arg_regs;
582 compiler.register_file.used &= ~arg_regs;
585template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
586template <
typename CBDerived>
587void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
588 CBDerived>::add_ret(ValuePart &vp, CCAssignment cca)
noexcept {
589 cca.bank = vp.bank();
590 cca.size = vp.part_size();
591 assigner.assign_ret(cca);
592 assert(cca.reg.valid() &&
"return value must be in register");
593 vp.set_value_reg(&compiler, cca.reg);
596template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
597template <
typename CBDerived>
598void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
599 CBDerived>::add_ret(ValueRef &vr)
noexcept {
600 assert(vr.has_assignment());
601 u32 part_count = vr.assignment()->part_count;
602 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
604 add_ret(vr.part(part_idx), cca);
608template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
609void CompilerBase<Adaptor, Derived, Config>::RetBuilder::add(
610 ValuePart &&vp, CCAssignment cca)
noexcept {
611 cca.bank = vp.bank();
612 u32 size = cca.size = vp.part_size();
613 assigner.assign_ret(cca);
614 assert(cca.reg.valid() &&
"indirect return value must use sret argument");
616 if (vp.is_in_reg(cca.reg)) {
617 if (!vp.can_salvage()) {
618 compiler.evict_reg(cca.reg);
620 vp.salvage(&compiler);
622 if (cca.sext || cca.zext) {
623 compiler.generate_raw_intext(cca.reg, cca.reg, cca.sext, 8 * size, 64);
626 if (compiler.register_file.is_used(cca.reg)) {
627 compiler.evict_reg(cca.reg);
629 if (vp.can_salvage()) {
630 AsmReg vp_reg = vp.salvage(&compiler);
631 if (cca.sext || cca.zext) {
632 compiler.generate_raw_intext(cca.reg, vp_reg, cca.sext, 8 * size, 64);
634 compiler.mov(cca.reg, vp_reg, size);
637 vp.reload_into_specific_fixed(&compiler, cca.reg);
638 if (cca.sext || cca.zext) {
639 compiler.generate_raw_intext(cca.reg, cca.reg, cca.sext, 8 * size, 64);
644 assert(!compiler.register_file.is_used(cca.reg));
645 compiler.register_file.mark_used(
646 cca.reg, CompilerBase::INVALID_VAL_LOCAL_IDX, 0);
647 compiler.register_file.mark_fixed(cca.reg);
648 compiler.register_file.mark_clobbered(cca.reg);
649 ret_regs |= (1ull << cca.reg.id());
652template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
653void CompilerBase<Adaptor, Derived, Config>::RetBuilder::add(
654 IRValueRef val, CallArg::Flag flag)
noexcept {
655 u32 part_count = compiler.val_parts(val).count();
656 ValueRef vr = compiler.val_ref(val);
657 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
658 add(vr.part(part_idx),
660 .sext = flag == CallArg::Flag::sext,
661 .zext = flag == CallArg::Flag::zext,
666template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
667void CompilerBase<Adaptor, Derived, Config>::RetBuilder::ret() noexcept {
668 assert((compiler.register_file.fixed & ret_regs) == ret_regs);
669 assert((compiler.register_file.used & ret_regs) == ret_regs);
670 compiler.register_file.fixed &= ~ret_regs;
671 compiler.register_file.used &= ~ret_regs;
673 compiler.gen_func_epilog();
674 compiler.release_regs_after_return();
677template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
680 text_writer.switch_section(
681 assembler.get_section(assembler.get_text_section()));
683 assert(func_syms.empty());
684 for (
const IRFuncRef func : adaptor->funcs()) {
685 auto binding = Assembler::SymBinding::GLOBAL;
686 if (adaptor->func_has_weak_linkage(func)) {
687 binding = Assembler::SymBinding::WEAK;
688 }
else if (adaptor->func_only_local(func)) {
689 binding = Assembler::SymBinding::LOCAL;
691 if (adaptor->func_extern(func)) {
692 func_syms.push_back(
derived()->assembler.sym_add_undef(
693 adaptor->func_link_name(func), binding));
695 func_syms.push_back(
derived()->assembler.sym_predef_func(
696 adaptor->func_link_name(func), binding));
698 derived()->define_func_idx(func, func_syms.size() - 1);
701 if (!
derived()->hook_post_func_sym_init()) {
702 TPDE_LOG_ERR(
"hook_pust_func_sym_init failed");
711 for (
const IRFuncRef func : adaptor->funcs()) {
712 if (adaptor->func_extern(func)) {
713 TPDE_LOG_TRACE(
"Skipping compilation of func {}",
714 adaptor->func_link_name(func));
719 TPDE_LOG_TRACE(
"Compiling func {}", adaptor->func_link_name(func));
720 if (!
derived()->compile_func(func, func_idx)) {
721 TPDE_LOG_ERR(
"Failed to compile function {}",
722 adaptor->func_link_name(func));
729 assembler.finalize();
736template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
740 for (
auto &e : stack.fixed_free_lists) {
743 stack.dynamic_free_lists.clear();
747 block_labels.clear();
748 personality_syms.clear();
751template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
752void CompilerBase<Adaptor, Derived, Config>::init_assignment(
753 IRValueRef value, ValLocalIdx local_idx)
noexcept {
754 assert(val_assignment(local_idx) ==
nullptr);
755 TPDE_LOG_TRACE(
"Initializing assignment for value {}",
756 static_cast<u32
>(local_idx));
758 const auto parts =
derived()->val_parts(value);
759 const u32 part_count = parts.count();
760 assert(part_count > 0);
761 auto *assignment = assignments.allocator.allocate(part_count);
762 assignments.value_ptrs[
static_cast<u32
>(local_idx)] = assignment;
764 u32 max_part_size = 0;
765 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
766 auto ap = AssignmentPartRef{assignment, part_idx};
768 ap.set_bank(parts.reg_bank(part_idx));
769 const u32 size = parts.size_bytes(part_idx);
771 max_part_size = std::max(max_part_size, size);
772 ap.set_part_size(size);
775 const auto &liveness = analyzer.liveness_info(local_idx);
784 if (part_count == 1) {
785 const auto &cur_loop =
786 analyzer.loop_from_idx(analyzer.block_loop_idx(cur_block_idx));
787 auto ap = AssignmentPartRef{assignment, 0};
790 liveness.last > cur_block_idx &&
791 cur_loop.definitions_in_childs +
792 assignments.cur_fixed_assignment_count[ap.bank().id()] <
793 Derived::NUM_FIXED_ASSIGNMENTS[ap.bank().id()];
794 if (
derived()->try_force_fixed_assignment(value)) {
795 try_fixed = assignments.cur_fixed_assignment_count[ap.bank().id()] <
796 Derived::NUM_FIXED_ASSIGNMENTS[ap.bank().id()];
801 AsmReg reg =
derived()->select_fixed_assignment_reg(ap.bank(), value);
802 TPDE_LOG_TRACE(
"Trying to assign fixed reg to value {}",
803 static_cast<u32
>(local_idx));
807 if (!reg.invalid() && !register_file.is_used(reg)) {
808 TPDE_LOG_TRACE(
"Assigning fixed assignment to reg {} for value {}",
810 static_cast<u32
>(local_idx));
812 ap.set_register_valid(
true);
813 ap.set_fixed_assignment(
true);
814 register_file.mark_used(reg, local_idx, 0);
815 register_file.inc_lock_count(reg);
816 register_file.mark_clobbered(reg);
817 ++assignments.cur_fixed_assignment_count[ap.bank().id()];
822 const auto last_full = liveness.last_full;
823 const auto ref_count = liveness.ref_count;
825 assert(max_part_size <= 256);
826 assignment->max_part_size = max_part_size;
827 assignment->pending_free =
false;
828 assignment->variable_ref =
false;
829 assignment->stack_variable =
false;
830 assignment->delay_free = last_full;
831 assignment->part_count = part_count;
832 assignment->frame_off = 0;
833 assignment->references_left = ref_count;
836template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
838 const ValLocalIdx local_idx)
noexcept {
839 TPDE_LOG_TRACE(
"Freeing assignment for value {}",
840 static_cast<u32
>(local_idx));
842 ValueAssignment *assignment =
843 assignments.value_ptrs[
static_cast<u32
>(local_idx)];
844 assignments.value_ptrs[
static_cast<u32
>(local_idx)] =
nullptr;
845 const auto is_var_ref = assignment->variable_ref;
846 const u32 part_count = assignment->part_count;
849 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
850 auto ap = AssignmentPartRef{assignment, part_idx};
851 if (ap.fixed_assignment()) [[unlikely]] {
852 const auto reg = ap.get_reg();
853 assert(register_file.is_fixed(reg));
854 assert(register_file.reg_local_idx(reg) == local_idx);
855 assert(register_file.reg_part(reg) == part_idx);
856 --assignments.cur_fixed_assignment_count[ap.bank().id()];
857 register_file.dec_lock_count_must_zero(reg);
858 register_file.unmark_used(reg);
859 }
else if (ap.register_valid()) {
860 const auto reg = ap.get_reg();
861 assert(!register_file.is_fixed(reg));
862 register_file.unmark_used(reg);
867 for (
auto reg_id : register_file.used_regs()) {
868 assert(register_file.reg_local_idx(AsmReg{reg_id}) != local_idx &&
869 "freeing assignment that is still referenced by a register");
874 if (!is_var_ref && assignment->frame_off != 0) {
875 free_stack_slot(assignment->frame_off, assignment->size());
878 assignments.allocator.deallocate(assignment);
881template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
883 ValLocalIdx local_idx, u32 var_ref_data)
noexcept {
884 TPDE_LOG_TRACE(
"Initializing variable-ref assignment for value {}",
885 static_cast<u32
>(local_idx));
887 assert(val_assignment(local_idx) ==
nullptr);
888 auto *assignment = assignments.allocator.allocate_slow(1,
true);
889 assignments.value_ptrs[
static_cast<u32
>(local_idx)] = assignment;
891 assignment->max_part_size = Config::PLATFORM_POINTER_SIZE;
892 assignment->variable_ref =
true;
893 assignment->stack_variable =
false;
894 assignment->part_count = 1;
895 assignment->var_ref_custom_idx = var_ref_data;
896 assignment->next_delayed_free_entry = assignments.variable_ref_list;
898 assignments.variable_ref_list = local_idx;
900 AssignmentPartRef ap{assignment, 0};
902 ap.set_bank(Config::GP_BANK);
903 ap.set_part_size(Config::PLATFORM_POINTER_SIZE);
906template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
907i32 CompilerBase<Adaptor, Derived, Config>::allocate_stack_slot(
909 unsigned align_bits = 4;
912 }
else if (size <= 16) {
914 u32 free_list_idx = size == 1 ? 0 : 32 - util::cnt_lz<u32>(size - 1);
915 assert(size <= 1u << free_list_idx);
916 size = 1 << free_list_idx;
917 align_bits = free_list_idx;
919 if (!stack.fixed_free_lists[free_list_idx].empty()) {
920 auto slot = stack.fixed_free_lists[free_list_idx].back();
921 stack.fixed_free_lists[free_list_idx].pop_back();
925 size = util::align_up(size, 16);
926 auto it = stack.dynamic_free_lists.find(size);
927 if (it != stack.dynamic_free_lists.end() && !it->second.empty()) {
928 const auto slot = it->second.back();
929 it->second.pop_back();
934 assert(stack.frame_size != ~0u &&
935 "cannot allocate stack slot before stack frame is initialized");
938 for (u32 list_idx = util::cnt_tz(stack.frame_size); list_idx < align_bits;
939 list_idx = util::cnt_tz(stack.frame_size)) {
940 i32 slot = stack.frame_size;
941 if constexpr (Config::FRAME_INDEXING_NEGATIVE) {
942 slot = -(slot + (1ull << list_idx));
944 stack.fixed_free_lists[list_idx].push_back(slot);
945 stack.frame_size += 1ull << list_idx;
948 auto slot = stack.frame_size;
949 assert(slot != 0 &&
"stack slot 0 is reserved");
950 stack.frame_size += size;
952 if constexpr (Config::FRAME_INDEXING_NEGATIVE) {
953 slot = -(slot + size);
958template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
960 u32 slot, u32 size)
noexcept {
961 if (size == 0) [[unlikely]] {
962 assert(slot == 0 &&
"unexpected slot for zero-sized stack-slot?");
964 }
else if (size <= 16) [[likely]] {
965 u32 free_list_idx = size == 1 ? 0 : 32 - util::cnt_lz<u32>(size - 1);
966 stack.fixed_free_lists[free_list_idx].push_back(slot);
968 size = util::align_up(size, 16);
969 stack.dynamic_free_lists[size].push_back(slot);
973template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
974template <
typename Fn>
976 u32 arg_idx, IRValueRef arg, Fn add_arg)
noexcept {
977 ValueRef vr =
derived()->result_ref(arg);
978 if (adaptor->cur_arg_is_byval(arg_idx)) {
979 std::optional<i32> byval_frame_off =
983 .byval_align = adaptor->cur_arg_byval_align(arg_idx),
984 .byval_size = adaptor->cur_arg_byval_size(arg_idx),
987 if (byval_frame_off) {
989 ValLocalIdx local_idx = val_idx(arg);
995 if (val_assignment(local_idx)) {
999 ValueAssignment *assignment = this->val_assignment(local_idx);
1000 assignment->stack_variable =
true;
1001 assignment->frame_off = *byval_frame_off;
1006 if (adaptor->cur_arg_is_sret(arg_idx)) {
1007 add_arg(vr.part(0), CCAssignment{.sret = true});
1011 const u32 part_count = vr.assignment()->part_count;
1014 u32 consecutive = 0;
1016 if (
derived()->arg_is_int128(arg)) {
1020 }
else if (part_count > 1 &&
1021 !
derived()->arg_allow_split_reg_stack_passing(arg)) {
1023 if (part_count > UINT8_MAX) {
1030 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
1031 add_arg(vr.part(part_idx),
1034 u8(consecutive ? part_count - part_idx - 1 : consec_def),
1035 .align = u8(part_idx == 0 ? align : 1),
1040template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1043 if (
auto special =
derived()->val_ref_special(value); special) {
1044 return ValueRef{
this, std::move(*special)};
1047 const ValLocalIdx local_idx = analyzer.adaptor->val_local_idx(value);
1048 assert(val_assignment(local_idx) !=
nullptr &&
"value use before def");
1049 return ValueRef{
this, local_idx};
1052template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1053std::pair<typename CompilerBase<Adaptor, Derived, Config>::ValueRef,
1056 IRValueRef value)
noexcept {
1057 std::pair<ValueRef, ValuePartRef> res{val_ref(value),
this};
1058 res.second = res.first.part(0);
1062template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1065 IRValueRef value)
noexcept {
1066 const ValLocalIdx local_idx = analyzer.adaptor->val_local_idx(value);
1067 if (val_assignment(local_idx) ==
nullptr) {
1068 init_assignment(value, local_idx);
1070 return ValueRef{
this, local_idx};
1073template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1074std::pair<typename CompilerBase<Adaptor, Derived, Config>::ValueRef,
1075 typename CompilerBase<Adaptor, Derived, Config>::ValuePartRef>
1076 CompilerBase<Adaptor, Derived, Config>::result_ref_single(
1077 IRValueRef value)
noexcept {
1078 std::pair<ValueRef, ValuePartRef> res{
result_ref(value),
this};
1079 res.second = res.first.part(0);
1083template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1085 ValuePartRef &val_ref, ScratchReg &scratch)
noexcept {
1086 auto ap = val_ref.assignment();
1087 assert(scratch.has_reg());
1088 auto reg = scratch.cur_reg();
1090 if (ap.fixed_assignment()) {
1091 auto cur_reg = ap.get_reg();
1092 assert(register_file.is_used(cur_reg));
1093 assert(register_file.is_fixed(cur_reg));
1094 assert(register_file.reg_local_idx(cur_reg) == val_ref.local_idx());
1096 if (cur_reg.id() != reg.id()) {
1097 derived()->mov(cur_reg, reg, ap.part_size());
1100 ap.set_register_valid(
true);
1101 ap.set_modified(
true);
1105 if (ap.register_valid()) {
1106 auto cur_reg = ap.get_reg();
1107 if (cur_reg.id() == reg.id()) {
1108 ap.set_modified(
true);
1112 assert(!register_file.is_fixed(cur_reg));
1113 register_file.unmark_used(cur_reg);
1117 assert(register_file.is_used(reg));
1118 assert(register_file.is_fixed(reg));
1119 assert(register_file.is_clobbered(reg));
1120 scratch.force_set_reg(AsmReg::make_invalid());
1121 register_file.unmark_fixed(reg);
1122 register_file.update_reg_assignment(reg, val_ref.local_idx(), val_ref.part());
1124 ap.set_register_valid(
true);
1125 ap.set_modified(
true);
1128template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1131 GenericValuePart &gv)
noexcept {
1132 if (std::holds_alternative<ScratchReg>(gv.state)) {
1133 return std::get<ScratchReg>(gv.state).cur_reg();
1135 if (std::holds_alternative<ValuePartRef>(gv.state)) {
1136 auto &vpr = std::get<ValuePartRef>(gv.state);
1137 if (vpr.has_reg()) {
1138 return vpr.cur_reg();
1140 return vpr.load_to_reg();
1142 if (
auto *expr = std::get_if<typename GenericValuePart::Expr>(&gv.state)) {
1143 if (expr->has_base() && !expr->has_index() && expr->disp == 0) {
1144 return expr->base_reg();
1146 return derived()->gval_expr_as_reg(gv);
1148 TPDE_UNREACHABLE(
"gval_as_reg on empty GenericValuePart");
1151template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1152typename CompilerBase<Adaptor, Derived, Config>::AsmReg
1154 GenericValuePart &gv, ScratchReg &dst)
noexcept {
1156 if (!dst.has_reg()) {
1157 if (
auto *scratch = std::get_if<ScratchReg>(&gv.state)) {
1158 dst = std::move(*scratch);
1159 }
else if (
auto *val_ref = std::get_if<ValuePartRef>(&gv.state)) {
1160 if (val_ref->can_salvage()) {
1161 dst.alloc_specific(val_ref->salvage());
1162 assert(dst.cur_reg() == reg &&
"salvaging unsuccessful");
1169template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1170Reg CompilerBase<Adaptor, Derived, Config>::select_reg_evict(
1171 RegBank bank, u64 exclusion_mask)
noexcept {
1172 TPDE_LOG_DBG(
"select_reg_evict for bank {}", bank.id());
1173 auto candidates = register_file.allocatable & ~register_file.fixed &
1174 register_file.bank_regs(bank) & ~exclusion_mask;
1176 Reg candidate = Reg::make_invalid();
1178 for (
auto reg_id : util::BitSetIterator<>(candidates)) {
1182 auto local_idx = register_file.reg_local_idx(reg);
1183 u32 part = register_file.reg_part(Reg{reg});
1184 assert(local_idx != INVALID_VAL_LOCAL_IDX);
1185 ValueAssignment *va = val_assignment(local_idx);
1186 AssignmentPartRef ap{va, part};
1199 if (ap.variable_ref()) {
1200 TPDE_LOG_DBG(
" r{} ({}) is variable-ref", reg_id, u32(local_idx));
1206 if (ap.stack_valid()) {
1207 score |= u32{1} << 31;
1210 const auto &liveness = analyzer.liveness_info(local_idx);
1211 u32 last_use_dist = u32(liveness.last) - u32(cur_block_idx);
1212 score |= (last_use_dist < 0x8000 ? 0x8000 - last_use_dist : 0) << 16;
1214 u32 refs_left = va->pending_free ? 0 : va->references_left;
1215 score |= (refs_left < 0xffff ? 0x10000 - refs_left : 1);
1217 TPDE_LOG_DBG(
" r{} ({}:{}) rc={}/{} live={}-{}{} spilled={} score={:#x}",
1223 u32(liveness.first),
1225 &
"*"[!liveness.last_full],
1230 if (score > max_score) {
1235 if (candidate.invalid()) [[unlikely]] {
1236 TPDE_FATAL(
"ran out of registers for scratch registers");
1238 TPDE_LOG_DBG(
" selected r{}", candidate.id());
1243template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1245 AsmReg dst, AssignmentPartRef ap)
noexcept {
1246 if (!ap.variable_ref()) {
1247 assert(ap.stack_valid());
1248 derived()->load_from_stack(dst, ap.frame_off(), ap.part_size());
1249 }
else if (ap.is_stack_variable()) {
1250 derived()->load_address_of_stack_var(dst, ap);
1251 }
else if constexpr (!Config::DEFAULT_VAR_REF_HANDLING) {
1252 derived()->load_address_of_var_reference(dst, ap);
1254 TPDE_UNREACHABLE(
"non-stack-variable needs custom var-ref handling");
1258template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1259void CompilerBase<Adaptor, Derived, Config>::allocate_spill_slot(
1260 AssignmentPartRef ap)
noexcept {
1261 assert(!ap.variable_ref() &&
"cannot allocate spill slot for variable ref");
1262 if (ap.assignment()->frame_off == 0) {
1263 assert(!ap.stack_valid() &&
"stack-valid set without spill slot");
1264 ap.assignment()->frame_off = allocate_stack_slot(ap.assignment()->size());
1265 assert(ap.assignment()->frame_off != 0);
1269template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1271 AssignmentPartRef ap)
noexcept {
1272 assert(may_change_value_state());
1273 if (!ap.stack_valid() && !ap.variable_ref()) {
1274 assert(ap.register_valid() &&
"cannot spill uninitialized assignment part");
1275 allocate_spill_slot(ap);
1276 derived()->spill_reg(ap.get_reg(), ap.frame_off(), ap.part_size());
1277 ap.set_stack_valid();
1281template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1283 AssignmentPartRef ap)
noexcept {
1284 assert(may_change_value_state());
1285 assert(ap.register_valid());
1287 ap.set_register_valid(
false);
1288 register_file.unmark_used(ap.get_reg());
1291template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1293 assert(may_change_value_state());
1294 assert(!register_file.is_fixed(reg));
1295 assert(register_file.reg_local_idx(reg) != INVALID_VAL_LOCAL_IDX);
1297 ValLocalIdx local_idx = register_file.reg_local_idx(reg);
1298 auto part = register_file.reg_part(reg);
1299 AssignmentPartRef evict_part{val_assignment(local_idx), part};
1300 assert(evict_part.register_valid());
1301 assert(evict_part.get_reg() == reg);
1303 evict_part.set_register_valid(
false);
1304 register_file.unmark_used(reg);
1307template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1309 assert(may_change_value_state());
1310 assert(!register_file.is_fixed(reg));
1311 assert(register_file.reg_local_idx(reg) != INVALID_VAL_LOCAL_IDX);
1313 ValLocalIdx local_idx = register_file.reg_local_idx(reg);
1314 auto part = register_file.reg_part(reg);
1315 AssignmentPartRef ap{val_assignment(local_idx), part};
1316 assert(ap.register_valid());
1317 assert(ap.get_reg() == reg);
1318 assert(!ap.modified() || ap.variable_ref());
1319 ap.set_register_valid(
false);
1320 register_file.unmark_used(reg);
1323template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1324typename CompilerBase<Adaptor, Derived, Config>::RegisterFile::RegBitSet
1325 CompilerBase<Adaptor, Derived, Config>::spill_before_branch(
1326 bool force_spill)
noexcept {
1350 using RegBitSet =
typename RegisterFile::RegBitSet;
1352 assert(may_change_value_state());
1354 const IRBlockRef cur_block_ref = analyzer.block_ref(cur_block_idx);
1356 bool must_spill = force_spill;
1360 auto next_block_is_succ =
false;
1361 auto next_block_has_multiple_incoming =
false;
1363 for (
const IRBlockRef succ : adaptor->block_succs(cur_block_ref)) {
1365 if (
static_cast<u32
>(analyzer.block_idx(succ)) ==
1366 static_cast<u32
>(cur_block_idx) + 1) {
1367 next_block_is_succ =
true;
1368 if (analyzer.block_has_multiple_incoming(succ)) {
1369 next_block_has_multiple_incoming =
true;
1374 must_spill = !next_block_is_succ || next_block_has_multiple_incoming;
1376 if (succ_count == 1 && !must_spill) {
1398 const auto spill_reg_if_needed = [&](
const auto reg) {
1399 auto local_idx = register_file.reg_local_idx(AsmReg{reg});
1400 auto part = register_file.reg_part(AsmReg{reg});
1401 if (local_idx == INVALID_VAL_LOCAL_IDX) {
1405 auto *assignment = val_assignment(local_idx);
1406 auto ap = AssignmentPartRef{assignment, part};
1407 if (ap.fixed_assignment()) {
1412 if (!ap.modified()) {
1417 if (ap.variable_ref()) {
1421 const auto &liveness = analyzer.liveness_info(local_idx);
1422 if (liveness.last <= cur_block_idx) {
1434 for (
const IRBlockRef succ : adaptor->block_succs(cur_block_ref)) {
1435 const auto block_idx = analyzer.block_idx(succ);
1436 if (
static_cast<u32
>(block_idx) ==
static_cast<u32
>(cur_block_idx) + 1) {
1439 if (block_idx >= liveness.first && block_idx <= liveness.last) {
1448 auto spilled = RegBitSet{};
1450 for (
auto reg : register_file.used_regs()) {
1451 const auto reg_fixed = spill_reg_if_needed(reg);
1454 if (!reg_fixed && must_spill) {
1457 spilled |= RegBitSet{1ull} << reg;
1464template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1466 typename RegisterFile::RegBitSet regs)
noexcept {
1467 assert(may_change_value_state());
1470 auto used_non_fixed_regs = register_file.used & ~register_file.fixed;
1471 for (
auto reg_id : util::BitSetIterator<>{regs & used_non_fixed_regs}) {
1476template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1480 for (
auto reg_id : register_file.used_nonfixed_regs()) {