389 void move_to_phi_nodes(BlockIndex target)
noexcept {
390 if (analyzer.block_has_phis(target)) {
391 move_to_phi_nodes_impl(target);
395 void move_to_phi_nodes_impl(BlockIndex target)
noexcept;
397 bool branch_needs_split(IRBlockRef target)
noexcept {
399 return analyzer.block_has_phis(target);
402 BlockIndex next_block() const noexcept;
404 bool try_force_fixed_assignment(IRValueRef) const noexcept {
return false; }
406 bool hook_post_func_sym_init() noexcept {
return true; }
408 void analysis_start() noexcept {}
410 void analysis_end() noexcept {}
412 void reloc_text(Assembler::SymRef sym,
415 i64 addend = 0) noexcept {
416 this->assembler.reloc_sec(
417 text_writer.get_sec_ref(), sym, type, offset, addend);
420 void label_place(Assembler::Label label)
noexcept {
421 this->assembler.label_place(
422 label, text_writer.get_sec_ref(), text_writer.offset());
426 Assembler::SymRef get_personality_sym() noexcept;
428 bool compile_func(IRFuncRef func, u32 func_idx) noexcept;
430 bool compile_block(IRBlockRef block, u32 block_idx) noexcept;
434#include "GenericValuePart.hpp"
435#include "ScratchReg.hpp"
436#include "ValuePartRef.hpp"
437#include "ValueRef.hpp"
441template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
442template <
typename CBDerived>
443void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
444 CBDerived>::add_arg(ValuePart &&vp, CCAssignment cca)
noexcept {
445 cca.bank = vp.bank();
446 cca.size = vp.part_size();
448 assigner.assign_arg(cca);
451 derived()->add_arg_byval(vp, cca);
453 }
else if (!cca.reg.valid()) {
454 derived()->add_arg_stack(vp, cca);
457 u32 size = vp.part_size();
458 if (vp.is_in_reg(cca.reg)) {
459 if (!vp.can_salvage()) {
460 compiler.evict_reg(cca.reg);
462 vp.salvage(&compiler);
464 if (cca.sext || cca.zext) {
465 compiler.generate_raw_intext(cca.reg, cca.reg, cca.sext, 8 * size, 64);
468 if (compiler.register_file.is_used(cca.reg)) {
469 compiler.evict_reg(cca.reg);
471 if (vp.can_salvage()) {
472 AsmReg vp_reg = vp.salvage(&compiler);
473 if (cca.sext || cca.zext) {
474 compiler.generate_raw_intext(cca.reg, vp_reg, cca.sext, 8 * size, 64);
476 compiler.mov(cca.reg, vp_reg, size);
479 vp.reload_into_specific_fixed(&compiler, cca.reg);
480 if (cca.sext || cca.zext) {
481 compiler.generate_raw_intext(
482 cca.reg, cca.reg, cca.sext, 8 * size, 64);
487 assert(!compiler.register_file.is_used(cca.reg));
488 compiler.register_file.mark_used(
489 cca.reg, CompilerBase::INVALID_VAL_LOCAL_IDX, 0);
490 compiler.register_file.mark_fixed(cca.reg);
491 compiler.register_file.mark_clobbered(cca.reg);
492 arg_regs |= (1ull << cca.reg.id());
496template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
497template <
typename CBDerived>
498void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
499 CBDerived>::add_arg(CallArg &&arg)
noexcept {
500 ValueRef vr = compiler.val_ref(arg.value);
501 const u32 part_count = compiler.val_parts(arg.value).count();
503 if (arg.flag == CallArg::Flag::byval) {
504 assert(part_count == 1);
508 .byval_align = arg.byval_align,
509 .byval_size = arg.byval_size,
515 bool consecutive =
false;
517 if (compiler.arg_is_int128(arg.value)) {
521 }
else if (part_count > 1 &&
522 !compiler.arg_allow_split_reg_stack_passing(arg.value)) {
524 if (part_count > UINT8_MAX) {
531 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
536 u8(consecutive ? part_count - part_idx - 1 : consec_def),
537 .sret = arg.flag == CallArg::Flag::sret,
538 .sext = arg.flag == CallArg::Flag::sext,
539 .zext = arg.flag == CallArg::Flag::zext,
540 .align = u8(part_idx == 0 ? align : 1),
545template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
546template <
typename CBDerived>
547void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<CBDerived>::call(
548 std::variant<typename Assembler::SymRef, ValuePart> target)
noexcept {
549 typename RegisterFile::RegBitSet skip_evict = arg_regs;
550 if (
auto *vp = std::get_if<ValuePart>(&target); vp && vp->can_salvage()) {
552 assert(vp->cur_reg_unlocked().valid() &&
"can_salvage implies register");
553 skip_evict |= (1ull << vp->cur_reg_unlocked().id());
556 auto clobbered = ~assigner.get_ccinfo().callee_saved_regs;
557 for (
auto reg_id : util::BitSetIterator<>{compiler.register_file.used &
558 clobbered & ~skip_evict}) {
559 compiler.evict_reg(AsmReg{reg_id});
560 compiler.register_file.mark_clobbered(Reg{reg_id});
563 derived()->call_impl(std::move(target));
565 for (
auto reg_id : util::BitSetIterator<>{arg_regs}) {
566 compiler.register_file.unmark_fixed(Reg{reg_id});
567 compiler.register_file.unmark_used(Reg{reg_id});
571template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
572template <
typename CBDerived>
573void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
574 CBDerived>::add_ret(ValuePart &vp, CCAssignment cca)
noexcept {
575 cca.bank = vp.bank();
576 cca.size = vp.part_size();
577 assigner.assign_ret(cca);
578 assert(cca.reg.valid() &&
"return value must be in register");
579 vp.set_value_reg(&compiler, cca.reg);
582template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
583template <
typename CBDerived>
584void CompilerBase<Adaptor, Derived, Config>::CallBuilderBase<
585 CBDerived>::add_ret(ValueRef &vr)
noexcept {
586 assert(vr.has_assignment());
587 u32 part_count = vr.assignment()->part_count;
588 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
590 add_ret(vr.part(part_idx), cca);
594template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
595void CompilerBase<Adaptor, Derived, Config>::RetBuilder::add(
596 ValuePart &&vp, CCAssignment cca)
noexcept {
597 cca.bank = vp.bank();
598 u32 size = cca.size = vp.part_size();
599 assigner.assign_ret(cca);
600 assert(cca.reg.valid() &&
"indirect return value must use sret argument");
602 if (vp.is_in_reg(cca.reg)) {
603 if (!vp.can_salvage()) {
604 compiler.evict_reg(cca.reg);
606 vp.salvage(&compiler);
608 if (cca.sext || cca.zext) {
609 compiler.generate_raw_intext(cca.reg, cca.reg, cca.sext, 8 * size, 64);
612 if (compiler.register_file.is_used(cca.reg)) {
613 compiler.evict_reg(cca.reg);
615 if (vp.can_salvage()) {
616 AsmReg vp_reg = vp.salvage(&compiler);
617 if (cca.sext || cca.zext) {
618 compiler.generate_raw_intext(cca.reg, vp_reg, cca.sext, 8 * size, 64);
620 compiler.mov(cca.reg, vp_reg, size);
623 vp.reload_into_specific_fixed(&compiler, cca.reg);
624 if (cca.sext || cca.zext) {
625 compiler.generate_raw_intext(cca.reg, cca.reg, cca.sext, 8 * size, 64);
630 assert(!compiler.register_file.is_used(cca.reg));
631 compiler.register_file.mark_used(
632 cca.reg, CompilerBase::INVALID_VAL_LOCAL_IDX, 0);
633 compiler.register_file.mark_fixed(cca.reg);
634 compiler.register_file.mark_clobbered(cca.reg);
635 ret_regs |= (1ull << cca.reg.id());
638template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
639void CompilerBase<Adaptor, Derived, Config>::RetBuilder::add(
640 IRValueRef val, CallArg::Flag flag)
noexcept {
641 u32 part_count = compiler.val_parts(val).count();
642 ValueRef vr = compiler.val_ref(val);
643 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
644 add(vr.part(part_idx),
646 .sext = flag == CallArg::Flag::sext,
647 .zext = flag == CallArg::Flag::zext,
652template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
653void CompilerBase<Adaptor, Derived, Config>::RetBuilder::ret() noexcept {
654 for (
auto reg_id : util::BitSetIterator<>{ret_regs}) {
655 compiler.register_file.unmark_fixed(Reg{reg_id});
656 compiler.register_file.unmark_used(Reg{reg_id});
659 compiler.gen_func_epilog();
660 compiler.release_regs_after_return();
663template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
666 text_writer.switch_section(
667 assembler.get_section(assembler.get_text_section()));
669 assert(func_syms.empty());
670 for (
const IRFuncRef func : adaptor->funcs()) {
671 auto binding = Assembler::SymBinding::GLOBAL;
672 if (adaptor->func_has_weak_linkage(func)) {
673 binding = Assembler::SymBinding::WEAK;
674 }
else if (adaptor->func_only_local(func)) {
675 binding = Assembler::SymBinding::LOCAL;
677 if (adaptor->func_extern(func)) {
678 func_syms.push_back(derived()->assembler.sym_add_undef(
679 adaptor->func_link_name(func), binding));
681 func_syms.push_back(derived()->assembler.sym_predef_func(
682 adaptor->func_link_name(func), binding));
684 derived()->define_func_idx(func, func_syms.size() - 1);
687 if (!derived()->hook_post_func_sym_init()) {
688 TPDE_LOG_ERR(
"hook_pust_func_sym_init failed");
697 for (
const IRFuncRef func : adaptor->funcs()) {
698 if (adaptor->func_extern(func)) {
699 TPDE_LOG_TRACE(
"Skipping compilation of func {}",
700 adaptor->func_link_name(func));
705 TPDE_LOG_TRACE(
"Compiling func {}", adaptor->func_link_name(func));
706 if (!derived()->compile_func(func, func_idx)) {
707 TPDE_LOG_ERR(
"Failed to compile function {}",
708 adaptor->func_link_name(func));
715 assembler.finalize();
722template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
726 for (
auto &e : stack.fixed_free_lists) {
729 stack.dynamic_free_lists.clear();
733 block_labels.clear();
734 personality_syms.clear();
737template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
739 IRValueRef value, ValLocalIdx local_idx)
noexcept {
740 assert(val_assignment(local_idx) ==
nullptr);
741 TPDE_LOG_TRACE(
"Initializing assignment for value {}",
742 static_cast<u32
>(local_idx));
744 const auto parts = derived()->val_parts(value);
745 const u32 part_count = parts.count();
746 assert(part_count > 0);
747 auto *assignment = assignments.allocator.allocate(part_count);
748 assignments.value_ptrs[
static_cast<u32
>(local_idx)] = assignment;
750 u32 max_part_size = 0;
751 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
752 auto ap = AssignmentPartRef{assignment, part_idx};
754 ap.set_bank(parts.reg_bank(part_idx));
755 const u32 size = parts.size_bytes(part_idx);
757 max_part_size = std::max(max_part_size, size);
758 ap.set_part_size(size);
761 const auto &liveness = analyzer.liveness_info(local_idx);
770 if (part_count == 1) {
771 const auto &cur_loop =
772 analyzer.loop_from_idx(analyzer.block_loop_idx(cur_block_idx));
773 auto ap = AssignmentPartRef{assignment, 0};
776 liveness.last > cur_block_idx &&
777 cur_loop.definitions_in_childs +
778 assignments.cur_fixed_assignment_count[ap.bank().id()] <
779 Derived::NUM_FIXED_ASSIGNMENTS[ap.bank().id()];
780 if (derived()->try_force_fixed_assignment(value)) {
781 try_fixed = assignments.cur_fixed_assignment_count[ap.bank().id()] <
782 Derived::NUM_FIXED_ASSIGNMENTS[ap.bank().id()];
787 AsmReg reg = derived()->select_fixed_assignment_reg(ap.bank(), value);
788 TPDE_LOG_TRACE(
"Trying to assign fixed reg to value {}",
789 static_cast<u32
>(local_idx));
793 if (!reg.invalid() && !register_file.is_used(reg)) {
794 TPDE_LOG_TRACE(
"Assigning fixed assignment to reg {} for value {}",
796 static_cast<u32
>(local_idx));
798 ap.set_register_valid(
true);
799 ap.set_fixed_assignment(
true);
800 register_file.mark_used(reg, local_idx, 0);
801 register_file.inc_lock_count(reg);
802 register_file.mark_clobbered(reg);
803 ++assignments.cur_fixed_assignment_count[ap.bank().id()];
808 const auto last_full = liveness.last_full;
809 const auto ref_count = liveness.ref_count;
811 assert(max_part_size <= 256);
812 assignment->max_part_size = max_part_size;
814 assignment->pending_free =
false;
816 assignment->variable_ref =
false;
817 assignment->stack_variable =
false;
818 assignment->delay_free = last_full;
819 assignment->part_count = part_count;
820 assignment->frame_off = 0;
821 assignment->references_left = ref_count;
824template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
826 const ValLocalIdx local_idx)
noexcept {
827 TPDE_LOG_TRACE(
"Freeing assignment for value {}",
828 static_cast<u32
>(local_idx));
830 ValueAssignment *assignment =
831 assignments.value_ptrs[
static_cast<u32
>(local_idx)];
832 assignments.value_ptrs[
static_cast<u32
>(local_idx)] =
nullptr;
833 const auto is_var_ref = assignment->variable_ref;
834 const u32 part_count = assignment->part_count;
837 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
838 auto ap = AssignmentPartRef{assignment, part_idx};
839 if (ap.fixed_assignment()) [[unlikely]] {
840 const auto reg = ap.get_reg();
841 assert(register_file.is_fixed(reg));
842 assert(register_file.reg_local_idx(reg) == local_idx);
843 assert(register_file.reg_part(reg) == part_idx);
844 --assignments.cur_fixed_assignment_count[ap.bank().id()];
845 register_file.dec_lock_count_must_zero(reg);
846 register_file.unmark_used(reg);
847 }
else if (ap.register_valid()) {
848 const auto reg = ap.get_reg();
849 assert(!register_file.is_fixed(reg));
850 register_file.unmark_used(reg);
855 for (
auto reg_id : register_file.used_regs()) {
856 assert(register_file.reg_local_idx(AsmReg{reg_id}) != local_idx &&
857 "freeing assignment that is still referenced by a register");
862 if (!is_var_ref && assignment->frame_off != 0) {
863 free_stack_slot(assignment->frame_off, assignment->size());
866 assignments.allocator.deallocate(assignment);
869template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
871 ValLocalIdx local_idx, u32 var_ref_data)
noexcept {
872 TPDE_LOG_TRACE(
"Initializing variable-ref assignment for value {}",
873 static_cast<u32
>(local_idx));
875 assert(val_assignment(local_idx) ==
nullptr);
876 auto *assignment = assignments.allocator.allocate_slow(1,
true);
877 assignments.value_ptrs[
static_cast<u32
>(local_idx)] = assignment;
879 assignment->max_part_size = Config::PLATFORM_POINTER_SIZE;
880 assignment->variable_ref =
true;
881 assignment->stack_variable =
false;
882 assignment->part_count = 1;
883 assignment->var_ref_custom_idx = var_ref_data;
884 assignment->next_delayed_free_entry = assignments.variable_ref_list;
886 assignments.variable_ref_list = local_idx;
888 AssignmentPartRef ap{assignment, 0};
890 ap.set_bank(Config::GP_BANK);
891 ap.set_part_size(Config::PLATFORM_POINTER_SIZE);
894template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
897 unsigned align_bits = 4;
900 }
else if (size <= 16) {
902 u32 free_list_idx = size == 1 ? 0 : 32 - util::cnt_lz<u32>(size - 1);
903 assert(size <= 1u << free_list_idx);
904 size = 1 << free_list_idx;
905 align_bits = free_list_idx;
907 if (!stack.fixed_free_lists[free_list_idx].empty()) {
908 auto slot = stack.fixed_free_lists[free_list_idx].back();
909 stack.fixed_free_lists[free_list_idx].pop_back();
913 size = util::align_up(size, 16);
914 auto it = stack.dynamic_free_lists.find(size);
915 if (it != stack.dynamic_free_lists.end() && !it->second.empty()) {
916 const auto slot = it->second.back();
917 it->second.pop_back();
922 assert(stack.frame_size != ~0u &&
923 "cannot allocate stack slot before stack frame is initialized");
926 for (u32 list_idx = util::cnt_tz(stack.frame_size); list_idx < align_bits;
927 list_idx = util::cnt_tz(stack.frame_size)) {
928 i32 slot = stack.frame_size;
929 if constexpr (Config::FRAME_INDEXING_NEGATIVE) {
930 slot = -(slot + (1ull << list_idx));
932 stack.fixed_free_lists[list_idx].push_back(slot);
933 stack.frame_size += 1ull << list_idx;
936 auto slot = stack.frame_size;
937 assert(slot != 0 &&
"stack slot 0 is reserved");
938 stack.frame_size += size;
940 if constexpr (Config::FRAME_INDEXING_NEGATIVE) {
941 slot = -(slot + size);
946template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
947void CompilerBase<Adaptor, Derived, Config>::free_stack_slot(
948 u32 slot, u32 size)
noexcept {
949 if (size == 0) [[unlikely]] {
950 assert(slot == 0 &&
"unexpected slot for zero-sized stack-slot?");
952 }
else if (size <= 16) [[likely]] {
953 u32 free_list_idx = size == 1 ? 0 : 32 - util::cnt_lz<u32>(size - 1);
954 stack.fixed_free_lists[free_list_idx].push_back(slot);
956 size = util::align_up(size, 16);
957 stack.dynamic_free_lists[size].push_back(slot);
961template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
962template <
typename Fn>
963void CompilerBase<Adaptor, Derived, Config>::handle_func_arg(
964 u32 arg_idx, IRValueRef arg, Fn add_arg)
noexcept {
965 ValueRef vr = derived()->result_ref(arg);
966 if (adaptor->cur_arg_is_byval(arg_idx)) {
970 .byval_align = adaptor->cur_arg_byval_align(arg_idx),
971 .byval_size = adaptor->cur_arg_byval_size(arg_idx),
976 if (adaptor->cur_arg_is_sret(arg_idx)) {
977 add_arg(vr.part(0), CCAssignment{.sret = true});
981 const u32 part_count = derived()->val_parts(arg).count();
986 if (derived()->arg_is_int128(arg)) {
990 }
else if (part_count > 1 &&
991 !derived()->arg_allow_split_reg_stack_passing(arg)) {
993 if (part_count > UINT8_MAX) {
1000 for (u32 part_idx = 0; part_idx < part_count; ++part_idx) {
1001 add_arg(vr.part(part_idx),
1004 u8(consecutive ? part_count - part_idx - 1 : consec_def),
1005 .align = u8(part_idx == 0 ? align : 1),
1010template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1011typename CompilerBase<Adaptor, Derived, Config>::ValueRef
1012 CompilerBase<Adaptor, Derived, Config>::val_ref(IRValueRef value)
noexcept {
1013 if (
auto special = derived()->val_ref_special(value); special) {
1014 return ValueRef{
this, std::move(*special)};
1017 const ValLocalIdx local_idx = analyzer.adaptor->val_local_idx(value);
1018 assert(val_assignment(local_idx) !=
nullptr &&
"value use before def");
1019 return ValueRef{
this, local_idx};
1022template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1023std::pair<typename CompilerBase<Adaptor, Derived, Config>::ValueRef,
1024 typename CompilerBase<Adaptor, Derived, Config>::ValuePartRef>
1025 CompilerBase<Adaptor, Derived, Config>::val_ref_single(
1026 IRValueRef value)
noexcept {
1027 std::pair<ValueRef, ValuePartRef> res{val_ref(value),
this};
1028 res.second = res.first.part(0);
1032template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1033typename CompilerBase<Adaptor, Derived, Config>::ValueRef
1035 IRValueRef value)
noexcept {
1036 const ValLocalIdx local_idx = analyzer.adaptor->val_local_idx(value);
1037 if (val_assignment(local_idx) ==
nullptr) {
1038 init_assignment(value, local_idx);
1040 return ValueRef{
this, local_idx};
1043template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1044std::pair<typename CompilerBase<Adaptor, Derived, Config>::ValueRef,
1047 IRValueRef value)
noexcept {
1048 std::pair<ValueRef, ValuePartRef> res{result_ref(value),
this};
1049 res.second = res.first.part(0);
1053template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1054void CompilerBase<Adaptor, Derived, Config>::set_value(
1055 ValuePartRef &val_ref, ScratchReg &scratch)
noexcept {
1056 auto ap = val_ref.assignment();
1057 assert(scratch.has_reg());
1058 auto reg = scratch.cur_reg();
1060 if (ap.fixed_assignment()) {
1061 auto cur_reg = ap.get_reg();
1062 assert(register_file.is_used(cur_reg));
1063 assert(register_file.is_fixed(cur_reg));
1064 assert(register_file.reg_local_idx(cur_reg) == val_ref.local_idx());
1066 if (cur_reg.id() != reg.id()) {
1067 derived()->mov(cur_reg, reg, ap.part_size());
1070 ap.set_register_valid(
true);
1071 ap.set_modified(
true);
1075 if (ap.register_valid()) {
1076 auto cur_reg = ap.get_reg();
1077 if (cur_reg.id() == reg.id()) {
1078 ap.set_modified(
true);
1082 assert(!register_file.is_fixed(cur_reg));
1083 register_file.unmark_used(cur_reg);
1087 assert(register_file.is_used(reg));
1088 assert(register_file.is_fixed(reg));
1089 assert(register_file.is_clobbered(reg));
1090 scratch.force_set_reg(AsmReg::make_invalid());
1091 register_file.unmark_fixed(reg);
1092 register_file.update_reg_assignment(reg, val_ref.local_idx(), val_ref.part());
1094 ap.set_register_valid(
true);
1095 ap.set_modified(
true);
1098template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1099typename CompilerBase<Adaptor, Derived, Config>::AsmReg
1101 GenericValuePart &gv)
noexcept {
1102 if (std::holds_alternative<ScratchReg>(gv.state)) {
1103 return std::get<ScratchReg>(gv.state).cur_reg();
1105 if (std::holds_alternative<ValuePartRef>(gv.state)) {
1106 auto &vpr = std::get<ValuePartRef>(gv.state);
1107 if (vpr.has_reg()) {
1108 return vpr.cur_reg();
1110 return vpr.load_to_reg();
1112 if (
auto *expr = std::get_if<typename GenericValuePart::Expr>(&gv.state)) {
1113 if (expr->has_base() && !expr->has_index() && expr->disp == 0) {
1114 return expr->base_reg();
1116 return derived()->gval_expr_as_reg(gv);
1118 TPDE_UNREACHABLE(
"gval_as_reg on empty GenericValuePart");
1121template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1122typename CompilerBase<Adaptor, Derived, Config>::AsmReg
1124 GenericValuePart &gv, ScratchReg &dst)
noexcept {
1125 AsmReg reg = gval_as_reg(gv);
1126 if (!dst.has_reg()) {
1127 if (
auto *scratch = std::get_if<ScratchReg>(&gv.state)) {
1128 dst = std::move(*scratch);
1129 }
else if (
auto *val_ref = std::get_if<ValuePartRef>(&gv.state)) {
1130 if (val_ref->can_salvage()) {
1131 dst.alloc_specific(val_ref->salvage());
1132 assert(dst.cur_reg() == reg &&
"salvaging unsuccessful");
1139template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1141 AsmReg dst, AssignmentPartRef ap)
noexcept {
1142 if (!ap.variable_ref()) {
1143 assert(ap.stack_valid());
1144 derived()->load_from_stack(dst, ap.frame_off(), ap.part_size());
1145 }
else if (ap.is_stack_variable()) {
1146 derived()->load_address_of_stack_var(dst, ap);
1147 }
else if constexpr (!Config::DEFAULT_VAR_REF_HANDLING) {
1148 derived()->load_address_of_var_reference(dst, ap);
1150 TPDE_UNREACHABLE(
"non-stack-variable needs custom var-ref handling");
1154template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1156 AssignmentPartRef ap)
noexcept {
1157 assert(!ap.variable_ref() &&
"cannot allocate spill slot for variable ref");
1158 if (ap.assignment()->frame_off == 0) {
1159 assert(!ap.stack_valid() &&
"stack-valid set without spill slot");
1160 ap.assignment()->frame_off = allocate_stack_slot(ap.assignment()->size());
1161 assert(ap.assignment()->frame_off != 0);
1165template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1167 AssignmentPartRef ap)
noexcept {
1168 assert(may_change_value_state());
1169 if (!ap.stack_valid() && !ap.variable_ref()) {
1170 assert(ap.register_valid() &&
"cannot spill uninitialized assignment part");
1171 allocate_spill_slot(ap);
1172 derived()->spill_reg(ap.get_reg(), ap.frame_off(), ap.part_size());
1173 ap.set_stack_valid();
1177template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1179 AssignmentPartRef ap)
noexcept {
1180 assert(may_change_value_state());
1181 assert(ap.register_valid());
1182 derived()->spill(ap);
1183 ap.set_register_valid(
false);
1184 register_file.unmark_used(ap.get_reg());
1187template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1189 assert(may_change_value_state());
1190 assert(!register_file.is_fixed(reg));
1191 assert(register_file.reg_local_idx(reg) != INVALID_VAL_LOCAL_IDX);
1193 ValLocalIdx local_idx = register_file.reg_local_idx(reg);
1194 auto part = register_file.reg_part(reg);
1195 AssignmentPartRef evict_part{val_assignment(local_idx), part};
1196 assert(evict_part.register_valid());
1197 assert(evict_part.get_reg() == reg);
1198 derived()->spill(evict_part);
1199 evict_part.set_register_valid(
false);
1200 register_file.unmark_used(reg);
1203template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1205 assert(may_change_value_state());
1206 assert(!register_file.is_fixed(reg));
1207 assert(register_file.reg_local_idx(reg) != INVALID_VAL_LOCAL_IDX);
1209 ValLocalIdx local_idx = register_file.reg_local_idx(reg);
1210 auto part = register_file.reg_part(reg);
1211 AssignmentPartRef ap{val_assignment(local_idx), part};
1212 assert(ap.register_valid());
1213 assert(ap.get_reg() == reg);
1214 assert(!ap.modified() || ap.variable_ref());
1215 ap.set_register_valid(
false);
1216 register_file.unmark_used(reg);
1219template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1220typename CompilerBase<Adaptor, Derived, Config>::RegisterFile::RegBitSet
1222 bool force_spill)
noexcept {
1246 using RegBitSet =
typename RegisterFile::RegBitSet;
1248 assert(may_change_value_state());
1250 const IRBlockRef cur_block_ref = analyzer.block_ref(cur_block_idx);
1252 bool must_spill = force_spill;
1256 auto next_block_is_succ =
false;
1257 auto next_block_has_multiple_incoming =
false;
1259 for (
const IRBlockRef succ : adaptor->block_succs(cur_block_ref)) {
1261 if (
static_cast<u32
>(analyzer.block_idx(succ)) ==
1262 static_cast<u32
>(cur_block_idx) + 1) {
1263 next_block_is_succ =
true;
1264 if (analyzer.block_has_multiple_incoming(succ)) {
1265 next_block_has_multiple_incoming =
true;
1270 must_spill = !next_block_is_succ || next_block_has_multiple_incoming;
1272 if (succ_count == 1 && !must_spill) {
1294 const auto spill_reg_if_needed = [&](
const auto reg) {
1295 auto local_idx = register_file.reg_local_idx(AsmReg{reg});
1296 auto part = register_file.reg_part(AsmReg{reg});
1297 if (local_idx == INVALID_VAL_LOCAL_IDX) {
1301 auto *assignment = val_assignment(local_idx);
1302 auto ap = AssignmentPartRef{assignment, part};
1303 if (ap.fixed_assignment()) {
1308 if (!ap.modified()) {
1313 if (ap.variable_ref()) {
1317 const auto &liveness = analyzer.liveness_info(local_idx);
1318 if (liveness.last <= cur_block_idx) {
1330 for (
const IRBlockRef succ : adaptor->block_succs(cur_block_ref)) {
1331 const auto block_idx = analyzer.block_idx(succ);
1332 if (
static_cast<u32
>(block_idx) ==
static_cast<u32
>(cur_block_idx) + 1) {
1335 if (block_idx >= liveness.first && block_idx <= liveness.last) {
1344 auto spilled = RegBitSet{};
1346 for (
auto reg : register_file.used_regs()) {
1347 const auto reg_fixed = spill_reg_if_needed(reg);
1350 if (!reg_fixed && must_spill) {
1353 spilled |= RegBitSet{1ull} << reg;
1360template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1361void CompilerBase<Adaptor, Derived, Config>::release_spilled_regs(
1362 typename RegisterFile::RegBitSet regs)
noexcept {
1363 assert(may_change_value_state());
1366 for (
auto reg_id : util::BitSetIterator<>{regs}) {
1367 auto reg = AsmReg{reg_id};
1368 if (!register_file.is_used(reg)) {
1371 if (register_file.is_fixed(reg)) {
1374 assert(register_file.reg_local_idx(reg) != INVALID_VAL_LOCAL_IDX);
1382template <IRAdaptor Adaptor,
typename Derived, CompilerConfig Config>
1386 for (
auto reg_id : register_file.used_nonfixed_regs()) {
1387 free_reg(Reg{reg_id});