mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-04-27 12:32:00 +00:00
Implement tail calls handled by conditional branches
This commit is contained in:
parent
268777c6fd
commit
b94a6cf695
2 changed files with 42 additions and 28 deletions
|
|
@ -501,10 +501,6 @@ std::unordered_set<std::string> ignored_funcs {
|
||||||
"kdebugserver",
|
"kdebugserver",
|
||||||
"send",
|
"send",
|
||||||
|
|
||||||
// libgcc math routines (these throw off the recompiler)
|
|
||||||
"__divdi3",
|
|
||||||
"__moddi3",
|
|
||||||
|
|
||||||
// ido math routines
|
// ido math routines
|
||||||
"__ll_div",
|
"__ll_div",
|
||||||
"__ll_lshift",
|
"__ll_lshift",
|
||||||
|
|
@ -624,11 +620,14 @@ std::unordered_set<std::string> renamed_funcs{
|
||||||
"fill_inbuf",
|
"fill_inbuf",
|
||||||
"flush_window",
|
"flush_window",
|
||||||
|
|
||||||
|
// libgcc math routines
|
||||||
"__muldi3",
|
"__muldi3",
|
||||||
|
"__divdi3",
|
||||||
"__udivdi3",
|
"__udivdi3",
|
||||||
"__umoddi3",
|
"__umoddi3",
|
||||||
"div64_64",
|
"div64_64",
|
||||||
"div64_32",
|
"div64_32",
|
||||||
|
"__moddi3",
|
||||||
};
|
};
|
||||||
|
|
||||||
bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, ELFIO::section* symtab_section, uint32_t entrypoint, bool has_entrypoint, bool use_absolute_symbols) {
|
bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, ELFIO::section* symtab_section, uint32_t entrypoint, bool has_entrypoint, bool use_absolute_symbols) {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
||||||
uint32_t reloc_section = 0;
|
uint32_t reloc_section = 0;
|
||||||
uint32_t reloc_target_section_offset = 0;
|
uint32_t reloc_target_section_offset = 0;
|
||||||
|
|
||||||
|
uint32_t func_vram_end = func.vram + func.words.size() * sizeof(func.words[0]);
|
||||||
|
|
||||||
// Check if this instruction has a reloc.
|
// Check if this instruction has a reloc.
|
||||||
if (section.relocatable && section.relocs.size() > 0 && section.relocs[reloc_index].address == instr_vram) {
|
if (section.relocatable && section.relocs.size() > 0 && section.relocs[reloc_index].address == instr_vram) {
|
||||||
// Get the reloc data for this instruction
|
// Get the reloc data for this instruction
|
||||||
|
|
@ -104,26 +106,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto print_branch = [&](uint32_t target_vram) {
|
|
||||||
fmt::print(output_file, "{{\n ");
|
|
||||||
if (instr_index < instructions.size() - 1) {
|
|
||||||
bool dummy_needs_link_branch;
|
|
||||||
bool dummy_is_branch_likely;
|
|
||||||
size_t next_reloc_index = reloc_index;
|
|
||||||
uint32_t next_vram = instr_vram + 4;
|
|
||||||
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
|
||||||
next_reloc_index++;
|
|
||||||
}
|
|
||||||
process_instruction(context, config, func, stats, skipped_insns, instr_index + 1, instructions, output_file, true, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
|
||||||
}
|
|
||||||
fmt::print(output_file, " ");
|
|
||||||
fmt::print(output_file, "goto L_{:08X}", target_vram);
|
|
||||||
if (needs_link_branch) {
|
|
||||||
fmt::print(output_file, ";\n goto after_{}", link_branch_index);
|
|
||||||
}
|
|
||||||
fmt::print(output_file, ";\n }}\n");
|
|
||||||
};
|
|
||||||
|
|
||||||
auto print_func_call = [&](uint32_t target_func_vram, bool link_branch = true) {
|
auto print_func_call = [&](uint32_t target_func_vram, bool link_branch = true) {
|
||||||
const auto matching_funcs_find = context.functions_by_vram.find(target_func_vram);
|
const auto matching_funcs_find = context.functions_by_vram.find(target_func_vram);
|
||||||
std::string jal_target_name;
|
std::string jal_target_name;
|
||||||
|
|
@ -195,6 +177,41 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto print_branch = [&](uint32_t branch_target) {
|
||||||
|
if (branch_target < func.vram || branch_target >= func_vram_end) {
|
||||||
|
// FIXME: how to deal with static functions?
|
||||||
|
if (context.functions_by_vram.find(branch_target) != context.functions_by_vram.end()) {
|
||||||
|
fmt::print(output_file, "{{\n ");
|
||||||
|
fmt::print("Tail call in {} to 0x{:08X}\n", func.name, branch_target);
|
||||||
|
print_func_call(branch_target, false);
|
||||||
|
print_line("return");
|
||||||
|
fmt::print(output_file, ";\n }}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print(stderr, "[Warn] Function {} is branching outside of the function (to 0x{:08X})\n", func.name, branch_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print(output_file, "{{\n ");
|
||||||
|
if (instr_index < instructions.size() - 1) {
|
||||||
|
bool dummy_needs_link_branch;
|
||||||
|
bool dummy_is_branch_likely;
|
||||||
|
size_t next_reloc_index = reloc_index;
|
||||||
|
uint32_t next_vram = instr_vram + 4;
|
||||||
|
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
||||||
|
next_reloc_index++;
|
||||||
|
}
|
||||||
|
process_instruction(context, config, func, stats, skipped_insns, instr_index + 1, instructions, output_file, true, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print(output_file, " ");
|
||||||
|
fmt::print(output_file, "goto L_{:08X}", branch_target);
|
||||||
|
if (needs_link_branch) {
|
||||||
|
fmt::print(output_file, ";\n goto after_{}", link_branch_index);
|
||||||
|
}
|
||||||
|
fmt::print(output_file, ";\n }}\n");
|
||||||
|
};
|
||||||
|
|
||||||
if (indent) {
|
if (indent) {
|
||||||
print_indent();
|
print_indent();
|
||||||
}
|
}
|
||||||
|
|
@ -216,8 +233,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
||||||
std::string unsigned_imm_string;
|
std::string unsigned_imm_string;
|
||||||
std::string signed_imm_string;
|
std::string signed_imm_string;
|
||||||
|
|
||||||
uint32_t func_vram_end = func.vram + func.words.size() * sizeof(func.words[0]);
|
|
||||||
|
|
||||||
if (!at_reloc) {
|
if (!at_reloc) {
|
||||||
unsigned_imm_string = fmt::format("{:#X}", imm);
|
unsigned_imm_string = fmt::format("{:#X}", imm);
|
||||||
signed_imm_string = fmt::format("{:#X}", (int16_t)imm);
|
signed_imm_string = fmt::format("{:#X}", (int16_t)imm);
|
||||||
|
|
@ -519,7 +534,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
||||||
}
|
}
|
||||||
// Otherwise, check if it's a tail call
|
// Otherwise, check if it's a tail call
|
||||||
else if (instr_vram == func_vram_end - 2 * sizeof(func.words[0])) {
|
else if (instr_vram == func_vram_end - 2 * sizeof(func.words[0])) {
|
||||||
fmt::print("Tail call in {}\n", func.name);
|
fmt::print("Tail call in {} to 0x{:08X}\n", func.name, branch_target);
|
||||||
print_func_call(branch_target);
|
print_func_call(branch_target);
|
||||||
}
|
}
|
||||||
// This may be a tail call in the middle of the control flow due to a previous check
|
// This may be a tail call in the middle of the control flow due to a previous check
|
||||||
|
|
@ -535,7 +550,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
||||||
// ```
|
// ```
|
||||||
// FIXME: how to deal with static functions?
|
// FIXME: how to deal with static functions?
|
||||||
else if (context.functions_by_vram.find(branch_target) != context.functions_by_vram.end()) {
|
else if (context.functions_by_vram.find(branch_target) != context.functions_by_vram.end()) {
|
||||||
fmt::print("Tail call in {}\n", func.name);
|
fmt::print("Tail call in {} to 0x{:08X}\n", func.name, branch_target);
|
||||||
print_func_call(branch_target, false);
|
print_func_call(branch_target, false);
|
||||||
print_line("return");
|
print_line("return");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue