diff --git a/include/recomp_port.h b/include/recomp_port.h index ccd3a9d..1cdae72 100644 --- a/include/recomp_port.h +++ b/include/recomp_port.h @@ -46,7 +46,10 @@ namespace RecompPort { struct Config { int32_t entrypoint; + bool has_entrypoint; bool uses_mips3_float_mode; + bool single_file_output; + bool use_absolute_symbols; std::filesystem::path elf_path; std::filesystem::path output_func_path; std::filesystem::path relocatable_sections_path; @@ -154,7 +157,7 @@ namespace RecompPort { }; bool analyze_function(const Context& context, const Function& function, const std::vector& instructions, FunctionStats& stats); - bool recompile_function(const Context& context, const Config& config, const Function& func, const std::filesystem::path& output_path, std::span> static_funcs); + bool recompile_function(const Context& context, const Config& config, const Function& func, std::ofstream& output_file, std::span> static_funcs, bool write_header); } #endif diff --git a/lib/Rabbitizer.vcxproj b/lib/Rabbitizer.vcxproj index 38b791e..c725806 100644 --- a/lib/Rabbitizer.vcxproj +++ b/lib/Rabbitizer.vcxproj @@ -22,8 +22,10 @@ + + @@ -35,8 +37,12 @@ + + + + @@ -46,8 +52,10 @@ + + @@ -158,7 +166,7 @@ true - $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;%(AdditionalIncludeDirectories) + $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;$(ProjectDir)rabbitizer\tables stdcpp17 @@ -171,7 +179,7 @@ true - $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;%(AdditionalIncludeDirectories) + $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;$(ProjectDir)rabbitizer\tables stdcpp17 @@ -182,7 +190,7 @@ true - $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;%(AdditionalIncludeDirectories) + $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;$(ProjectDir)rabbitizer\tables stdcpp17 @@ -195,7 +203,7 @@ true - $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;%(AdditionalIncludeDirectories) + $(ProjectDir)rabbitizer\include;$(ProjectDir)rabbitizer\cplusplus\include;$(ProjectDir)rabbitizer\tables stdcpp17 diff --git a/lib/Rabbitizer.vcxproj.filters b/lib/Rabbitizer.vcxproj.filters index 7d040d7..7d59c9f 100644 --- a/lib/Rabbitizer.vcxproj.filters +++ b/lib/Rabbitizer.vcxproj.filters @@ -102,6 +102,30 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/lib/rabbitizer b/lib/rabbitizer index b9a39f6..e0d8003 160000 --- a/lib/rabbitizer +++ b/lib/rabbitizer @@ -1 +1 @@ -Subproject commit b9a39f6ec0a3ff6690ef2925e6275cf6578602cc +Subproject commit e0d8003047938e2ec3697eaf8d61a84d11d17b43 diff --git a/src/config.cpp b/src/config.cpp index 446de3d..d7422b1 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -211,12 +211,20 @@ RecompPort::Config::Config(const char* path) { // Input section (required) const toml::value& input_data = toml::find(config_data, "input"); - entrypoint = toml::find(input_data, "entrypoint"); + if (input_data.contains("entrypoint")) { + entrypoint = toml::find(input_data, "entrypoint"); + has_entrypoint = true; + } + else { + has_entrypoint = false; + } elf_path = concat_if_not_empty(basedir, toml::find(input_data, "elf_path")); output_func_path = concat_if_not_empty(basedir, toml::find(input_data, "output_func_path")); relocatable_sections_path = concat_if_not_empty(basedir, toml::find_or(input_data, "relocatable_sections_path", "")); uses_mips3_float_mode = toml::find_or(input_data, "uses_mips3_float_mode", false); bss_section_suffix = toml::find_or(input_data, "bss_section_suffix", ".bss"); + single_file_output = toml::find_or(input_data, "single_file_output", false); + use_absolute_symbols = toml::find_or(input_data, "use_absolute_symbols", false); // Patches section (optional) const toml::value& patches_data = toml::find_or(config_data, "patches", toml::value{}); diff --git a/src/main.cpp b/src/main.cpp index afb4075..37086b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -573,7 +573,7 @@ std::unordered_set renamed_funcs{ "__assert", }; -bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, ELFIO::section* symtab_section, uint32_t entrypoint) { +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 found_entrypoint_func = false; ELFIO::symbol_section_accessor symbols{ elf_file, symtab_section }; fmt::print("Num symbols: {}\n", symbols.get_symbols_num()); @@ -593,12 +593,28 @@ bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, EL symbols.get_symbol(sym_index, name, value, size, bind, type, section_index, other); + if (section_index == ELFIO::SHN_ABS && use_absolute_symbols) { + uint32_t vram = static_cast(value); + context.functions_by_vram[vram].push_back(context.functions.size()); + + context.functions.emplace_back( + vram, + 0, + std::vector{}, + std::move(name), + 0, + true, + reimplemented + ); + continue; + } + if (section_index >= context.sections.size()) { continue; } // Check if this symbol is the entrypoint - if (value == entrypoint && type == ELFIO::STT_FUNC) { + if (has_entrypoint && value == entrypoint && type == ELFIO::STT_FUNC) { if (found_entrypoint_func) { fmt::print(stderr, "Ambiguous entrypoint: {}\n", name); return false; @@ -996,6 +1012,59 @@ bool read_list_file(const std::filesystem::path& filename, std::vector file1_buf(65536); + static std::vector file2_buf(65536); + + std::ifstream file1(file1_path, std::ifstream::ate | std::ifstream::binary); //open file at the end + std::ifstream file2(file2_path, std::ifstream::ate | std::ifstream::binary); //open file at the end + const std::ifstream::pos_type fileSize = file1.tellg(); + + file1.rdbuf()->pubsetbuf(file1_buf.data(), file1_buf.size()); + file2.rdbuf()->pubsetbuf(file2_buf.data(), file2_buf.size()); + + if (fileSize != file2.tellg()) { + return false; //different file size + } + + file1.seekg(0); //rewind + file2.seekg(0); //rewind + + std::istreambuf_iterator begin1(file1); + std::istreambuf_iterator begin2(file2); + + return std::equal(begin1, std::istreambuf_iterator(), begin2); //Second argument is end-of-range iterator +} + +bool recompile_single_function(const RecompPort::Context& context, const RecompPort::Config& config, const RecompPort::Function& func, const std::filesystem::path& output_path, std::span> static_funcs_out) { + // Open the temporary output file + std::filesystem::path temp_path = output_path; + temp_path.replace_extension(".tmp"); + std::ofstream output_file{ temp_path }; + if (!output_file.good()) { + fmt::print(stderr, "Failed to open file for writing: {}\n", temp_path.string() ); + return false; + } + + if (!RecompPort::recompile_function(context, config, func, output_file, static_funcs_out, true)) { + return false; + } + + output_file.close(); + + // If a file of the target name exists and it's identical to the output file, delete the output file. + // This prevents updating the existing file so that it doesn't need to be rebuilt. + if (std::filesystem::exists(output_path) && compare_files(output_path, temp_path)) { + std::filesystem::remove(temp_path); + } + // Otherwise, rename the new file to the target path. + else { + std::filesystem::rename(temp_path, output_path); + } + + return true; +} + int main(int argc, char** argv) { auto exit_failure = [] (const std::string& error_str) { fmt::vprint(stderr, error_str, fmt::make_format_args()); @@ -1069,9 +1138,9 @@ int main(int argc, char** argv) { } // Read all of the symbols in the elf and look for the entrypoint function - bool found_entrypoint_func = read_symbols(context, elf_file, symtab_section, config.entrypoint); + bool found_entrypoint_func = read_symbols(context, elf_file, symtab_section, config.entrypoint, config.has_entrypoint, config.use_absolute_symbols); - if (!found_entrypoint_func) { + if (config.has_entrypoint && !found_entrypoint_func) { exit_failure("Could not find entrypoint function\n"); } @@ -1079,17 +1148,8 @@ int main(int argc, char** argv) { std::filesystem::create_directories(config.output_func_path); - std::ofstream lookup_file{ config.output_func_path / "lookup.cpp" }; std::ofstream func_header_file{ config.output_func_path / "funcs.h" }; - fmt::print(lookup_file, - //"#include \n" - "#include \"recomp.h\"\n" - //"#include \"funcs.h\"\n" - "\n" - //"std::pair funcs[] {{\n" - ); - fmt::print(func_header_file, "#include \"recomp.h\"\n" "\n" @@ -1152,6 +1212,13 @@ int main(int argc, char** argv) { func.words[instruction_index] = byteswap(patch.value); } + std::ofstream single_output_file; + bool header_written = false; + + if (config.single_file_output) { + single_output_file.open(config.output_func_path / config.elf_path.stem().replace_extension(".c")); + } + //#pragma omp parallel for for (size_t i = 0; i < context.functions.size(); i++) { const auto& func = context.functions[i]; @@ -1159,18 +1226,21 @@ int main(int argc, char** argv) { if (!func.ignored && func.words.size() != 0) { fmt::print(func_header_file, "void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name); - //fmt::print(lookup_file, - // " {{ 0x{:08X}u, {} }},\n", func.vram, func.name); - if (RecompPort::recompile_function(context, config, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) { - //lookup_file.clear(); + bool result; + if (config.single_file_output) { + result = RecompPort::recompile_function(context, config, func, single_output_file, static_funcs_by_section, !header_written); + header_written = true; + } + else { + result = recompile_single_function(context, config, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section); + } + if (result == false) { fmt::print(stderr, "Error recompiling {}\n", func.name); std::exit(EXIT_FAILURE); } } else if (func.reimplemented) { fmt::print(func_header_file, "void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name); - //fmt::print(lookup_file, - // " {{ 0x{:08X}u, {} }},\n", func.vram, func.name); } } @@ -1228,27 +1298,40 @@ int main(int argc, char** argv) { fmt::print(func_header_file, "void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name); - //fmt::print(lookup_file, - // " {{ 0x{:08X}u, {} }},\n", func.vram, func.name); - if (RecompPort::recompile_function(context, config, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) { - //lookup_file.clear(); + + bool result; + if (config.single_file_output) { + result = RecompPort::recompile_function(context, config, func, single_output_file, static_funcs_by_section, !header_written); + header_written = true; + } + else { + result = recompile_single_function(context, config, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section); + } + + if (result == false) { fmt::print(stderr, "Error recompiling {}\n", func.name); std::exit(EXIT_FAILURE); } } } - fmt::print(lookup_file, - //"}};\n" - //"extern const size_t num_funcs = sizeof(funcs) / sizeof(funcs[0]);\n" - //"\n" - "gpr get_entrypoint_address() {{ return (gpr)(int32_t)0x{:08X}u; }}\n" - "\n" - "const char* get_rom_name() {{ return \"{}\"; }}\n" - "\n", - static_cast(config.entrypoint), - config.elf_path.filename().replace_extension(".z64").string() - ); + if (config.has_entrypoint) { + std::ofstream lookup_file{ config.output_func_path / "lookup.cpp" }; + + fmt::print(lookup_file, + "#include \"recomp.h\"\n" + "\n" + ); + + fmt::print(lookup_file, + "gpr get_entrypoint_address() {{ return (gpr)(int32_t)0x{:08X}u; }}\n" + "\n" + "const char* get_rom_name() {{ return \"{}\"; }}\n" + "\n", + static_cast(config.entrypoint), + config.elf_path.filename().replace_extension(".z64").string() + ); + } fmt::print(func_header_file, "\n" diff --git a/src/recompilation.cpp b/src/recompilation.cpp index 4c484d5..0f67e16 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -191,6 +191,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C } needs_link_branch = true; print_unconditional_branch("{}(rdram, ctx)", jal_target_name); + return true; }; if (indent) { @@ -674,6 +675,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C break; // Cop1 compares + // TODO allow NaN in ordered and unordered float comparisons, default to a compare result of 1 for ordered and 0 for unordered if a NaN is present case InstrId::cpu_c_lt_s: print_line("CHECK_FR(ctx, {})", fs); print_line("CHECK_FR(ctx, {})", ft); @@ -701,6 +703,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C //print_line("*(volatile int*)0 = 0;"); print_line("c1cs = ctx->f{}.fl <= ctx->f{}.fl", fs, ft); break; + case InstrId::cpu_c_ule_s: + print_line("CHECK_FR(ctx, {})", fs); + print_line("CHECK_FR(ctx, {})", ft); + //print_line("*(volatile int*)0 = 0;"); + print_line("c1cs = ctx->f{}.fl <= ctx->f{}.fl", fs, ft); + break; case InstrId::cpu_c_le_d: print_line("CHECK_FR(ctx, {})", fs); print_line("CHECK_FR(ctx, {})", ft); @@ -947,47 +955,19 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C return true; } -bool compare_files(const std::filesystem::path& file1_path, const std::filesystem::path& file2_path) { - static std::vector file1_buf(65536); - static std::vector file2_buf(65536); - - std::ifstream file1(file1_path, std::ifstream::ate | std::ifstream::binary); //open file at the end - std::ifstream file2(file2_path, std::ifstream::ate | std::ifstream::binary); //open file at the end - const std::ifstream::pos_type fileSize = file1.tellg(); - - file1.rdbuf()->pubsetbuf(file1_buf.data(), file1_buf.size()); - file2.rdbuf()->pubsetbuf(file2_buf.data(), file2_buf.size()); - - if (fileSize != file2.tellg()) { - return false; //different file size - } - - file1.seekg(0); //rewind - file2.seekg(0); //rewind - - std::istreambuf_iterator begin1(file1); - std::istreambuf_iterator begin2(file2); - - return std::equal(begin1, std::istreambuf_iterator(), begin2); //Second argument is end-of-range iterator -} - -bool RecompPort::recompile_function(const RecompPort::Context& context, const RecompPort::Config& config, const RecompPort::Function& func, const std::filesystem::path& output_path, std::span> static_funcs_out) { +bool RecompPort::recompile_function(const RecompPort::Context& context, const RecompPort::Config& config, const RecompPort::Function& func, std::ofstream& output_file, std::span> static_funcs_out, bool write_header) { //fmt::print("Recompiling {}\n", func.name); std::vector instructions; - // Open the output file and write the file header - std::filesystem::path temp_path = output_path; - temp_path.replace_extension(".tmp"); - std::ofstream output_file{ temp_path }; - if (!output_file.good()) { - fmt::print(stderr, "Failed to open file for writing: {}\n", temp_path.string() ); - return false; + if (write_header) { + // Write the file header + fmt::print(output_file, + "#include \"recomp.h\"\n" + "#include \"disable_warnings.h\"\n" + "\n"); } fmt::print(output_file, - "#include \"recomp.h\"\n" - "#include \"disable_warnings.h\"\n" - "\n" "void {}(uint8_t* rdram, recomp_context* ctx) {{\n" // these variables shouldn't need to be preserved across function boundaries, so make them local for more efficient output " uint64_t hi = 0, lo = 0, result = 0;\n" @@ -1070,7 +1050,7 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re // Process the current instruction and check for errors if (process_instruction(context, config, func, stats, skipped_insns, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, static_funcs_out) == false) { - fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path.string()); + fmt::print(stderr, "Error in recompiling {}, clearing output file\n", func.name); output_file.clear(); return false; } @@ -1092,18 +1072,6 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re // Terminate the function fmt::print(output_file, ";}}\n"); - - output_file.close(); - - // If a file of the target name exists and it's identical to the output file, delete the output file. - // This prevents updating the existing file so that it doesn't need to be rebuilt. - if (std::filesystem::exists(output_path) && compare_files(output_path, temp_path)) { - std::filesystem::remove(temp_path); - } - // Otherwise, rename the new file to the target path. - else { - std::filesystem::rename(temp_path, output_path); - } - + return true; }