mirror of
				https://github.com/N64Recomp/N64Recomp.git
				synced 2025-10-30 08:02:11 +00:00 
			
		
		
		
	Implement tail calls in the middle of functions and allow recomping libgcc math routines (#43)
This commit is contained in:
		
							parent
							
								
									5d46de6de0
								
							
						
					
					
						commit
						26c5c2cbb8
					
				
					 3 changed files with 77 additions and 47 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,5 +1,6 @@ | ||||||
| # VSCode file settings | # VSCode file settings | ||||||
| .vscode/settings.json | .vscode/settings.json | ||||||
|  | .vscode/c_cpp_properties.json | ||||||
| 
 | 
 | ||||||
| # Input elf and rom files | # Input elf and rom files | ||||||
| *.elf | *.elf | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								src/main.cpp
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								src/main.cpp
									
										
									
									
									
								
							|  | @ -500,14 +500,7 @@ std::unordered_set<std::string> ignored_funcs { | ||||||
|     "rmonGetRcpRegister", |     "rmonGetRcpRegister", | ||||||
|     "kdebugserver", |     "kdebugserver", | ||||||
|     "send", |     "send", | ||||||
|     // libgcc math routines (these throw off the recompiler)
 | 
 | ||||||
|     "__muldi3", |  | ||||||
|     "__divdi3", |  | ||||||
|     "__udivdi3", |  | ||||||
|     "__umoddi3", |  | ||||||
|     "div64_64", |  | ||||||
|     "div64_32", |  | ||||||
|     "__moddi3", |  | ||||||
|     // ido math routines
 |     // ido math routines
 | ||||||
|     "__ll_div", |     "__ll_div", | ||||||
|     "__ll_lshift", |     "__ll_lshift", | ||||||
|  | @ -626,6 +619,15 @@ std::unordered_set<std::string> renamed_funcs{ | ||||||
|     "clear_bufs", |     "clear_bufs", | ||||||
|     "fill_inbuf", |     "fill_inbuf", | ||||||
|     "flush_window", |     "flush_window", | ||||||
|  | 
 | ||||||
|  |     // libgcc math routines
 | ||||||
|  |     "__muldi3", | ||||||
|  |     "__divdi3", | ||||||
|  |     "__udivdi3", | ||||||
|  |     "__umoddi3", | ||||||
|  |     "div64_64", | ||||||
|  |     "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,27 +106,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     auto print_branch = [&]<typename... Ts>(fmt::format_string<Ts...> fmt_str, Ts ...args) { |     auto print_func_call = [&](uint32_t target_func_vram, bool link_branch = true) { | ||||||
|         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::vprint(output_file, fmt_str, fmt::make_format_args(args...)); |  | ||||||
|         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) { |  | ||||||
|         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; | ||||||
|         uint32_t section_vram_start = section.ram_addr; |         uint32_t section_vram_start = section.ram_addr; | ||||||
|  | @ -190,11 +172,46 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         needs_link_branch = true; |         needs_link_branch = link_branch; | ||||||
|         print_unconditional_branch("{}(rdram, ctx)", jal_target_name); |         print_unconditional_branch("{}(rdram, ctx)", jal_target_name); | ||||||
|         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); | ||||||
|  | @ -511,16 +526,28 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|         { |         { | ||||||
|             uint32_t branch_target = instr.getBranchVramGeneric(); |             uint32_t branch_target = instr.getBranchVramGeneric(); | ||||||
|             if (branch_target == instr_vram) { |             if (branch_target == instr_vram) { | ||||||
|                 print_line("void pause_self(uint8_t *rdram); pause_self(rdram)"); |                 print_line("pause_self(rdram)"); | ||||||
|             } |             } | ||||||
|             // Check if the branch is within this function
 |             // Check if the branch is within this function
 | ||||||
|             else if (branch_target >= func.vram && branch_target < func_vram_end) { |             else if (branch_target >= func.vram && branch_target < func_vram_end) { | ||||||
|                 print_unconditional_branch("goto L_{:08X}", branch_target); |                 print_unconditional_branch("goto L_{:08X}", branch_target); | ||||||
|             } |             } | ||||||
|             // Otherwise, check if it's a tail call
 |             // This may be a tail call in the middle of the control flow due to a previous check
 | ||||||
|             else if (instr_vram == func_vram_end - 2 * sizeof(func.words[0])) { |             // For example:
 | ||||||
|                 fmt::print("Tail call in {}\n", func.name); |             // ```c
 | ||||||
|                 print_func_call(branch_target); |             // void test() {
 | ||||||
|  |             //     if (SOME_CONDITION) {
 | ||||||
|  |             //         do_a();
 | ||||||
|  |             //     } else {
 | ||||||
|  |             //         do_b();
 | ||||||
|  |             //     }
 | ||||||
|  |             // }
 | ||||||
|  |             // ```
 | ||||||
|  |             // FIXME: how to deal with static functions?
 | ||||||
|  |             else if (context.functions_by_vram.find(branch_target) != context.functions_by_vram.end()) { | ||||||
|  |                 fmt::print("Tail call in {} to 0x{:08X}\n", func.name, branch_target); | ||||||
|  |                 print_func_call(branch_target, false); | ||||||
|  |                 print_line("return"); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 fmt::print(stderr, "Unhandled branch in {} at 0x{:08X} to 0x{:08X}\n", func.name, instr_vram, branch_target); |                 fmt::print(stderr, "Unhandled branch in {} at 0x{:08X} to 0x{:08X}\n", func.name, instr_vram, branch_target); | ||||||
|  | @ -593,7 +620,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_bne: |     case InstrId::cpu_bne: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if ({}{} != {}{})", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); |         print_branch_condition("if ({}{} != {}{})", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
|     case InstrId::cpu_beql: |     case InstrId::cpu_beql: | ||||||
|         is_branch_likely = true; |         is_branch_likely = true; | ||||||
|  | @ -601,7 +628,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_beq: |     case InstrId::cpu_beq: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if ({}{} == {}{})", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); |         print_branch_condition("if ({}{} == {}{})", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
|     case InstrId::cpu_bgezl: |     case InstrId::cpu_bgezl: | ||||||
|         is_branch_likely = true; |         is_branch_likely = true; | ||||||
|  | @ -609,7 +636,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_bgez: |     case InstrId::cpu_bgez: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if (SIGNED({}{}) >= 0)", ctx_gpr_prefix(rs), rs); |         print_branch_condition("if (SIGNED({}{}) >= 0)", ctx_gpr_prefix(rs), rs); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
|     case InstrId::cpu_bgtzl: |     case InstrId::cpu_bgtzl: | ||||||
|         is_branch_likely = true; |         is_branch_likely = true; | ||||||
|  | @ -617,7 +644,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_bgtz: |     case InstrId::cpu_bgtz: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if (SIGNED({}{}) > 0)", ctx_gpr_prefix(rs), rs); |         print_branch_condition("if (SIGNED({}{}) > 0)", ctx_gpr_prefix(rs), rs); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
|     case InstrId::cpu_blezl: |     case InstrId::cpu_blezl: | ||||||
|         is_branch_likely = true; |         is_branch_likely = true; | ||||||
|  | @ -625,7 +652,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_blez: |     case InstrId::cpu_blez: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if (SIGNED({}{}) <= 0)", ctx_gpr_prefix(rs), rs); |         print_branch_condition("if (SIGNED({}{}) <= 0)", ctx_gpr_prefix(rs), rs); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
|     case InstrId::cpu_bltzl: |     case InstrId::cpu_bltzl: | ||||||
|         is_branch_likely = true; |         is_branch_likely = true; | ||||||
|  | @ -633,7 +660,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_bltz: |     case InstrId::cpu_bltz: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if (SIGNED({}{}) < 0)", ctx_gpr_prefix(rs), rs); |         print_branch_condition("if (SIGNED({}{}) < 0)", ctx_gpr_prefix(rs), rs); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
|     case InstrId::cpu_break: |     case InstrId::cpu_break: | ||||||
|         print_line("do_break({})", instr_vram); |         print_line("do_break({})", instr_vram); | ||||||
|  | @ -814,7 +841,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_bc1t: |     case InstrId::cpu_bc1t: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if (c1cs)", ctx_gpr_prefix(rs), rs); |         print_branch_condition("if (c1cs)", ctx_gpr_prefix(rs), rs); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
|     case InstrId::cpu_bc1fl: |     case InstrId::cpu_bc1fl: | ||||||
|         is_branch_likely = true; |         is_branch_likely = true; | ||||||
|  | @ -822,7 +849,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C | ||||||
|     case InstrId::cpu_bc1f: |     case InstrId::cpu_bc1f: | ||||||
|         print_indent(); |         print_indent(); | ||||||
|         print_branch_condition("if (!c1cs)", ctx_gpr_prefix(rs), rs); |         print_branch_condition("if (!c1cs)", ctx_gpr_prefix(rs), rs); | ||||||
|         print_branch("goto L_{:08X}", (uint32_t)instr.getBranchVramGeneric()); |         print_branch((uint32_t)instr.getBranchVramGeneric()); | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     // Cop1 arithmetic
 |     // Cop1 arithmetic
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Anghelo Carvajal
						Anghelo Carvajal