mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2025-05-14 08:12:19 +00:00
Implemented calling internal functions in live recompiler, added bltzal/bltzall to branch operations
This commit is contained in:
parent
b7abe0ac4e
commit
898de82da7
10 changed files with 246 additions and 80 deletions
|
@ -31,17 +31,78 @@ namespace Registers {
|
|||
constexpr int float_temp = SLJIT_FR0;
|
||||
}
|
||||
|
||||
struct InnerCall {
|
||||
size_t target_func_index;
|
||||
sljit_jump* jump;
|
||||
};
|
||||
|
||||
struct ReferenceSymbolCall {
|
||||
uint16_t reference;
|
||||
};
|
||||
|
||||
struct N64Recomp::LiveGeneratorContext {
|
||||
std::unordered_map<std::string, sljit_label*> labels;
|
||||
std::unordered_map<std::string, std::vector<sljit_jump*>> pending_jumps;
|
||||
std::vector<sljit_label*> func_labels;
|
||||
std::vector<InnerCall> inner_calls;
|
||||
sljit_jump* cur_branch_jump;
|
||||
};
|
||||
|
||||
N64Recomp::LiveGenerator::LiveGenerator(sljit_compiler* compiler) : compiler(compiler) {
|
||||
N64Recomp::LiveGenerator::LiveGenerator(size_t num_funcs) : compiler(compiler) {
|
||||
compiler = sljit_create_compiler(NULL);
|
||||
context = std::make_unique<LiveGeneratorContext>();
|
||||
context->func_labels.resize(num_funcs);
|
||||
}
|
||||
|
||||
N64Recomp::LiveGenerator::~LiveGenerator() {}
|
||||
N64Recomp::LiveGenerator::~LiveGenerator() {
|
||||
if (compiler != nullptr) {
|
||||
sljit_free_compiler(compiler);
|
||||
compiler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
N64Recomp::LiveGeneratorOutput N64Recomp::LiveGenerator::finish() {
|
||||
LiveGeneratorOutput ret{};
|
||||
ret.good = true;
|
||||
|
||||
// Populate all the pending inner function calls.
|
||||
for (const InnerCall& call : context->inner_calls) {
|
||||
sljit_label* target_func_label = context->func_labels[call.target_func_index];
|
||||
|
||||
// Generation isn't valid if the target function wasn't recompiled.
|
||||
if (target_func_label == nullptr) {
|
||||
return { };
|
||||
}
|
||||
|
||||
sljit_set_label(call.jump, target_func_label);
|
||||
}
|
||||
|
||||
// Generate the code.
|
||||
ret.code = sljit_generate_code(compiler, 0, NULL);
|
||||
ret.code_size = sljit_get_generated_code_size(compiler);
|
||||
ret.functions.resize(context->func_labels.size());
|
||||
|
||||
// Get the function addresses.
|
||||
for (size_t func_index = 0; func_index < ret.functions.size(); func_index++) {
|
||||
sljit_label* func_label = context->func_labels[func_index];
|
||||
|
||||
// If the function wasn't recompiled, don't populate its address.
|
||||
if (func_label != nullptr) {
|
||||
ret.functions[func_index] = reinterpret_cast<recomp_func_t*>(sljit_get_label_addr(func_label));
|
||||
}
|
||||
}
|
||||
|
||||
sljit_free_compiler(compiler);
|
||||
compiler = nullptr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
N64Recomp::LiveGeneratorOutput::~LiveGeneratorOutput() {
|
||||
if (code != nullptr) {
|
||||
sljit_free_code(code, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int get_gpr_context_offset(int gpr_index) {
|
||||
return offsetof(recomp_context, r0) + sizeof(recomp_context::r0) * gpr_index;
|
||||
|
@ -638,7 +699,8 @@ void N64Recomp::LiveGenerator::process_store_op(const StoreOp& op, const Instruc
|
|||
}
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_function_start(const std::string& function_name) const {
|
||||
void N64Recomp::LiveGenerator::emit_function_start(const std::string& function_name, size_t func_index) const {
|
||||
context->func_labels[func_index] = sljit_emit_label(compiler);
|
||||
sljit_emit_enter(compiler, 0, SLJIT_ARGS2V(P, P), 4, 5, 0);
|
||||
sljit_emit_op2(compiler, SLJIT_SUB, Registers::rdram, 0, Registers::rdram, 0, SLJIT_IMM, 0xFFFFFFFF80000000);
|
||||
}
|
||||
|
@ -655,10 +717,18 @@ void N64Recomp::LiveGenerator::emit_function_call_by_register(int reg) const {
|
|||
assert(false);
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_function_call_by_name(const std::string& func_name) const {
|
||||
void N64Recomp::LiveGenerator::emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const {
|
||||
const N64Recomp::ReferenceSymbol& sym = context.get_reference_symbol(section_index, symbol_index);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_function_call(const Context& recompiler_context, size_t function_index) const {
|
||||
sljit_emit_op2(compiler, SLJIT_ADD, SLJIT_R0, 0, Registers::rdram, 0, SLJIT_IMM, 0xFFFFFFFF80000000);
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, Registers::ctx, 0);
|
||||
sljit_jump* call_jump = sljit_emit_call(compiler, SLJIT_CALL, SLJIT_ARGS2V(P, P));
|
||||
context->inner_calls.emplace_back(InnerCall{ .target_func_index = function_index, .jump = call_jump });
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_goto(const std::string& target) const {
|
||||
sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_JUMP);
|
||||
// Check if the label already exists.
|
||||
|
@ -996,7 +1066,7 @@ void N64Recomp::LiveGenerator::emit_comment(const std::string& comment) const {
|
|||
// Nothing to do here.
|
||||
}
|
||||
|
||||
bool N64Recomp::recompile_function_live(LiveGenerator& generator, const Context& context, const Function& func, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
return recompile_function_custom(generator, context, func, output_file, static_funcs_out, tag_reference_relocs);
|
||||
bool N64Recomp::recompile_function_live(LiveGenerator& generator, const Context& context, size_t function_index, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
return recompile_function_custom(generator, context, function_index, output_file, static_funcs_out, tag_reference_relocs);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ enum class TestError {
|
|||
Success,
|
||||
FailedToOpenInput,
|
||||
FailedToRecompile,
|
||||
UnknownStructType,
|
||||
DataDifference
|
||||
};
|
||||
|
||||
|
@ -84,6 +85,7 @@ TestStats run_test(const std::filesystem::path& tests_dir, const std::string& te
|
|||
uint32_t data_length = read_u32_swap(file_data, 0x10);
|
||||
uint32_t text_address = read_u32_swap(file_data, 0x14);
|
||||
uint32_t data_address = read_u32_swap(file_data, 0x18);
|
||||
uint32_t next_struct_address = read_u32_swap(file_data, 0x1C);
|
||||
|
||||
recomp_context ctx{};
|
||||
|
||||
|
@ -105,55 +107,107 @@ TestStats run_test(const std::filesystem::path& tests_dir, const std::string& te
|
|||
context.sections[0].executable = true;
|
||||
context.section_functions.resize(context.sections.size());
|
||||
|
||||
// Get the function's instruction words.
|
||||
std::vector<uint32_t> text_words{};
|
||||
text_words.resize(text_length / sizeof(uint32_t));
|
||||
for (size_t i = 0; i < text_words.size(); i++) {
|
||||
text_words[i] = read_u32(context.rom, text_offset + i * sizeof(uint32_t));
|
||||
size_t start_func_index;
|
||||
uint32_t function_desc_address = 0;
|
||||
|
||||
// Read any extra structs.
|
||||
while (next_struct_address != 0) {
|
||||
uint32_t cur_struct_address = next_struct_address;
|
||||
uint32_t struct_type = read_u32_swap(context.rom, next_struct_address + 0x00);
|
||||
next_struct_address = read_u32_swap(context.rom, next_struct_address + 0x04);
|
||||
|
||||
switch (struct_type) {
|
||||
case 1: // Function desc
|
||||
function_desc_address = cur_struct_address;
|
||||
break;
|
||||
default:
|
||||
printf("Unknown struct type %u\n", struct_type);
|
||||
return { TestError::UnknownStructType };
|
||||
}
|
||||
}
|
||||
|
||||
// Add the function to the context.
|
||||
context.functions_by_vram[text_address].emplace_back(context.functions.size());
|
||||
context.section_functions.emplace_back(context.functions.size());
|
||||
context.sections[0].function_addrs.emplace_back(text_address);
|
||||
context.functions.emplace_back(
|
||||
text_address,
|
||||
text_offset,
|
||||
text_words,
|
||||
"test_func",
|
||||
0
|
||||
);
|
||||
// Check if a function description exists.
|
||||
if (function_desc_address == 0) {
|
||||
// No function description, so treat the whole thing as one function.
|
||||
|
||||
// Get the function's instruction words.
|
||||
std::vector<uint32_t> text_words{};
|
||||
text_words.resize(text_length / sizeof(uint32_t));
|
||||
for (size_t i = 0; i < text_words.size(); i++) {
|
||||
text_words[i] = read_u32(context.rom, text_offset + i * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
// Add the function to the context.
|
||||
context.functions_by_vram[text_address].emplace_back(context.functions.size());
|
||||
context.section_functions.emplace_back(context.functions.size());
|
||||
context.sections[0].function_addrs.emplace_back(text_address);
|
||||
context.functions.emplace_back(
|
||||
text_address,
|
||||
text_offset,
|
||||
text_words,
|
||||
"test_func",
|
||||
0
|
||||
);
|
||||
start_func_index = 0;
|
||||
}
|
||||
else {
|
||||
// Use the function description.
|
||||
uint32_t num_funcs = read_u32_swap(context.rom, function_desc_address + 0x08);
|
||||
start_func_index = read_u32_swap(context.rom, function_desc_address + 0x0C);
|
||||
|
||||
for (size_t func_index = 0; func_index < num_funcs; func_index++) {
|
||||
uint32_t cur_func_address = read_u32_swap(context.rom, function_desc_address + 0x10 + 0x00 + 0x08 * func_index);
|
||||
uint32_t cur_func_length = read_u32_swap(context.rom, function_desc_address + 0x10 + 0x04 + 0x08 * func_index);
|
||||
uint32_t cur_func_offset = cur_func_address - text_address + text_offset;
|
||||
|
||||
// Get the function's instruction words.
|
||||
std::vector<uint32_t> text_words{};
|
||||
text_words.resize(cur_func_length / sizeof(uint32_t));
|
||||
for (size_t i = 0; i < text_words.size(); i++) {
|
||||
text_words[i] = read_u32(context.rom, cur_func_offset + i * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
// Add the function to the context.
|
||||
context.functions_by_vram[cur_func_address].emplace_back(context.functions.size());
|
||||
context.section_functions.emplace_back(context.functions.size());
|
||||
context.sections[0].function_addrs.emplace_back(cur_func_address);
|
||||
context.functions.emplace_back(
|
||||
cur_func_address,
|
||||
cur_func_offset,
|
||||
std::move(text_words),
|
||||
"test_func_" + std::to_string(func_index),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint32_t>> dummy_static_funcs{};
|
||||
|
||||
auto before_codegen = std::chrono::system_clock::now();
|
||||
|
||||
// Create the sljit compiler and generator.
|
||||
sljit_compiler* compiler = sljit_create_compiler(NULL);
|
||||
N64Recomp::LiveGenerator generator{ compiler };
|
||||
std::ostringstream dummy_ostream{};
|
||||
// Create the sljit compiler and the generator.
|
||||
N64Recomp::LiveGenerator generator{ context.functions.size() };
|
||||
|
||||
//sljit_emit_op0(compiler, SLJIT_BREAKPOINT);
|
||||
for (size_t func_index = 0; func_index < context.functions.size(); func_index++) {
|
||||
std::ostringstream dummy_ostream{};
|
||||
|
||||
if (!N64Recomp::recompile_function_live(generator, context, context.functions[0], dummy_ostream, dummy_static_funcs, true)) {
|
||||
return { TestError::FailedToRecompile };
|
||||
//sljit_emit_op0(compiler, SLJIT_BREAKPOINT);
|
||||
|
||||
if (!N64Recomp::recompile_function_live(generator, context, func_index, dummy_ostream, dummy_static_funcs, true)) {
|
||||
return { TestError::FailedToRecompile };
|
||||
}
|
||||
}
|
||||
|
||||
// Finish up code generation.
|
||||
void* code = sljit_generate_code(compiler, 0, NULL);
|
||||
size_t code_size = sljit_get_generated_code_size(compiler);
|
||||
|
||||
sljit_free_compiler(compiler);
|
||||
// Generate the code.
|
||||
N64Recomp::LiveGeneratorOutput output = generator.finish();
|
||||
|
||||
auto after_codegen = std::chrono::system_clock::now();
|
||||
|
||||
auto before_execution = std::chrono::system_clock::now();
|
||||
|
||||
// Run the generated code.
|
||||
typedef void (recomp_func_t)(uint8_t* rdram, recomp_context* ctx);
|
||||
((recomp_func_t*)code)(rdram.data(), &ctx);
|
||||
|
||||
sljit_free_code(code, nullptr);
|
||||
ctx.r29 = 0xFFFFFFFF80000000 + rdram.size() - 0x10; // Set the stack pointer.
|
||||
output.functions[start_func_index](rdram.data(), &ctx);
|
||||
|
||||
auto after_execution = std::chrono::system_clock::now();
|
||||
|
||||
|
@ -175,7 +229,7 @@ TestStats run_test(const std::filesystem::path& tests_dir, const std::string& te
|
|||
ret.error = TestError::Success;
|
||||
ret.codegen_microseconds = std::chrono::duration_cast<std::chrono::microseconds>(after_codegen - before_codegen).count();
|
||||
ret.execution_microseconds = std::chrono::duration_cast<std::chrono::microseconds>(after_execution - before_execution).count();
|
||||
ret.code_size = code_size;
|
||||
ret.code_size = output.code_size;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -214,6 +268,9 @@ int main(int argc, const char** argv) {
|
|||
case TestError::FailedToRecompile:
|
||||
printf(" Failed to recompile\n");
|
||||
break;
|
||||
case TestError::UnknownStructType:
|
||||
printf(" Unknown additional data struct type in test data\n");
|
||||
break;
|
||||
case TestError::DataDifference:
|
||||
printf(" Output data did not match, dumped to file\n");
|
||||
break;
|
||||
|
|
|
@ -221,8 +221,7 @@ int main(int argc, const char** argv) {
|
|||
|
||||
// Perform a second pass for recompiling all the functions.
|
||||
for (size_t func_index = 0; func_index < mod_context.functions.size(); func_index++) {
|
||||
auto& func = mod_context.functions[func_index];
|
||||
if (!N64Recomp::recompile_function(mod_context, func, output_file, static_funcs_by_section, true)) {
|
||||
if (!N64Recomp::recompile_function(mod_context, func_index, output_file, static_funcs_by_section, true)) {
|
||||
output_file.close();
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(output_file_path, ec);
|
||||
|
|
|
@ -521,8 +521,8 @@ namespace N64Recomp {
|
|||
};
|
||||
|
||||
class Generator;
|
||||
bool recompile_function(const Context& context, const Function& func, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs, bool tag_reference_relocs);
|
||||
bool recompile_function_custom(Generator& generator, const Context& context, const Function& func, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs);
|
||||
bool recompile_function(const Context& context, size_t function_index, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs, bool tag_reference_relocs);
|
||||
bool recompile_function_custom(Generator& generator, const Context& context, size_t function_index, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs);
|
||||
|
||||
enum class ModSymbolsError {
|
||||
Good,
|
||||
|
|
|
@ -30,11 +30,12 @@ namespace N64Recomp {
|
|||
virtual void process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const = 0;
|
||||
virtual void process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const = 0;
|
||||
virtual void process_store_op(const StoreOp& op, const InstructionContext& ctx) const = 0;
|
||||
virtual void emit_function_start(const std::string& function_name) const = 0;
|
||||
virtual void emit_function_start(const std::string& function_name, size_t func_index) const = 0;
|
||||
virtual void emit_function_end() const = 0;
|
||||
virtual void emit_function_call_lookup(uint32_t addr) const = 0;
|
||||
virtual void emit_function_call_by_register(int reg) const = 0;
|
||||
virtual void emit_function_call_by_name(const std::string& func_name) const = 0;
|
||||
virtual void emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const = 0;
|
||||
virtual void emit_function_call(const Context& context, size_t function_index) const = 0;
|
||||
virtual void emit_goto(const std::string& target) const = 0;
|
||||
virtual void emit_label(const std::string& label_name) const = 0;
|
||||
virtual void emit_variable_declaration(const std::string& var_name, int reg) const = 0;
|
||||
|
@ -65,11 +66,12 @@ namespace N64Recomp {
|
|||
void process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const final;
|
||||
void process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const final;
|
||||
void process_store_op(const StoreOp& op, const InstructionContext& ctx) const final;
|
||||
void emit_function_start(const std::string& function_name) const final;
|
||||
void emit_function_start(const std::string& function_name, size_t func_index) const final;
|
||||
void emit_function_end() const final;
|
||||
void emit_function_call_lookup(uint32_t addr) const final;
|
||||
void emit_function_call_by_register(int reg) const final;
|
||||
void emit_function_call_by_name(const std::string& func_name) const final;
|
||||
void emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const final;
|
||||
void emit_function_call(const Context& context, size_t function_index) const final;
|
||||
void emit_goto(const std::string& target) const final;
|
||||
void emit_label(const std::string& label_name) const final;
|
||||
void emit_variable_declaration(const std::string& var_name, int reg) const final;
|
||||
|
|
|
@ -2,23 +2,49 @@
|
|||
#define __LIVE_RECOMPILER_H__
|
||||
|
||||
#include "recompiler/generator.h"
|
||||
#include "recomp.h"
|
||||
|
||||
struct sljit_compiler;
|
||||
|
||||
namespace N64Recomp {
|
||||
struct LiveGeneratorContext;
|
||||
struct LiveGeneratorOutput {
|
||||
LiveGeneratorOutput() = default;
|
||||
LiveGeneratorOutput(const LiveGeneratorOutput& rhs) = delete;
|
||||
LiveGeneratorOutput(LiveGeneratorOutput&& rhs) { *this = std::move(rhs); }
|
||||
LiveGeneratorOutput& operator=(const LiveGeneratorOutput& rhs) = delete;
|
||||
LiveGeneratorOutput& operator=(LiveGeneratorOutput&& rhs) {
|
||||
good = rhs.good;
|
||||
functions = std::move(rhs.functions);
|
||||
code = rhs.code;
|
||||
code_size = rhs.code_size;
|
||||
|
||||
rhs.good = false;
|
||||
rhs.code = nullptr;
|
||||
rhs.code_size = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
~LiveGeneratorOutput();
|
||||
bool good = false;
|
||||
std::vector<recomp_func_t*> functions;
|
||||
void* code;
|
||||
size_t code_size;
|
||||
};
|
||||
class LiveGenerator final : public Generator {
|
||||
public:
|
||||
LiveGenerator(sljit_compiler* compiler);
|
||||
LiveGenerator(size_t num_funcs);
|
||||
~LiveGenerator();
|
||||
LiveGeneratorOutput finish();
|
||||
void process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const final;
|
||||
void process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const final;
|
||||
void process_store_op(const StoreOp& op, const InstructionContext& ctx) const final;
|
||||
void emit_function_start(const std::string& function_name) const final;
|
||||
void emit_function_start(const std::string& function_name, size_t func_index) const final;
|
||||
void emit_function_end() const final;
|
||||
void emit_function_call_lookup(uint32_t addr) const final;
|
||||
void emit_function_call_by_register(int reg) const final;
|
||||
void emit_function_call_by_name(const std::string& func_name) const final;
|
||||
void emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const final;
|
||||
void emit_function_call(const Context& context, size_t function_index) const final;
|
||||
void emit_goto(const std::string& target) const final;
|
||||
void emit_label(const std::string& label_name) const final;
|
||||
void emit_variable_declaration(const std::string& var_name, int reg) const final;
|
||||
|
@ -50,7 +76,7 @@ namespace N64Recomp {
|
|||
};
|
||||
|
||||
void live_recompiler_init();
|
||||
bool recompile_function_live(LiveGenerator& generator, const Context& context, const Function& func, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs);
|
||||
bool recompile_function_live(LiveGenerator& generator, const Context& context, size_t function_index, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -362,7 +362,7 @@ void N64Recomp::CGenerator::get_binary_expr_string(BinaryOpType type, const Bina
|
|||
}
|
||||
}
|
||||
|
||||
void N64Recomp::CGenerator::emit_function_start(const std::string& function_name) const {
|
||||
void N64Recomp::CGenerator::emit_function_start(const std::string& function_name, size_t func_index) const {
|
||||
fmt::print(output_file,
|
||||
"RECOMP_FUNC 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
|
||||
|
@ -384,8 +384,13 @@ void N64Recomp::CGenerator::emit_function_call_by_register(int reg) const {
|
|||
fmt::print(output_file, "LOOKUP_FUNC({})(rdram, ctx);\n", gpr_to_string(reg));
|
||||
}
|
||||
|
||||
void N64Recomp::CGenerator::emit_function_call_by_name(const std::string& func_name) const {
|
||||
fmt::print(output_file, "{}(rdram, ctx);\n", func_name);
|
||||
void N64Recomp::CGenerator::emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const {
|
||||
const N64Recomp::ReferenceSymbol& sym = context.get_reference_symbol(section_index, symbol_index);
|
||||
fmt::print(output_file, "{}(rdram, ctx);\n", sym.name);
|
||||
}
|
||||
|
||||
void N64Recomp::CGenerator::emit_function_call(const Context& context, size_t function_index) const {
|
||||
fmt::print(output_file, "{}(rdram, ctx);\n", context.functions[function_index].name);
|
||||
}
|
||||
|
||||
void N64Recomp::CGenerator::emit_goto(const std::string& target) const {
|
||||
|
|
29
src/main.cpp
29
src/main.cpp
|
@ -111,7 +111,7 @@ bool compare_files(const std::filesystem::path& file1_path, const std::filesyste
|
|||
return std::equal(begin1, std::istreambuf_iterator<char>(), begin2); //Second argument is end-of-range iterator
|
||||
}
|
||||
|
||||
bool recompile_single_function(const N64Recomp::Context& context, const N64Recomp::Function& func, const std::string& recomp_include, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs_out) {
|
||||
bool recompile_single_function(const N64Recomp::Context& context, size_t func_index, const std::string& recomp_include, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs_out) {
|
||||
// Open the temporary output file
|
||||
std::filesystem::path temp_path = output_path;
|
||||
temp_path.replace_extension(".tmp");
|
||||
|
@ -127,7 +127,7 @@ bool recompile_single_function(const N64Recomp::Context& context, const N64Recom
|
|||
"\n",
|
||||
recomp_include);
|
||||
|
||||
if (!N64Recomp::recompile_function(context, func, output_file, static_funcs_out, false)) {
|
||||
if (!N64Recomp::recompile_function(context, func_index, output_file, static_funcs_out, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -725,7 +725,7 @@ int main(int argc, char** argv) {
|
|||
|
||||
// Recompile the function.
|
||||
if (config.single_file_output || config.functions_per_output_file > 1) {
|
||||
result = N64Recomp::recompile_function(context, func, current_output_file, static_funcs_by_section, false);
|
||||
result = N64Recomp::recompile_function(context, i, current_output_file, static_funcs_by_section, false);
|
||||
if (!config.single_file_output) {
|
||||
cur_file_function_count++;
|
||||
if (cur_file_function_count >= config.functions_per_output_file) {
|
||||
|
@ -734,7 +734,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
result = recompile_single_function(context, func, config.recomp_include, config.output_func_path / (func.name + ".c"), static_funcs_by_section);
|
||||
result = recompile_single_function(context, i, config.recomp_include, config.output_func_path / (func.name + ".c"), static_funcs_by_section);
|
||||
}
|
||||
if (result == false) {
|
||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||
|
@ -797,22 +797,25 @@ int main(int argc, char** argv) {
|
|||
std::vector<uint32_t> insn_words((cur_func_end - static_func_addr) / sizeof(uint32_t));
|
||||
insn_words.assign(func_rom_start, func_rom_start + insn_words.size());
|
||||
|
||||
N64Recomp::Function func {
|
||||
// Create the new function and add it to the context.
|
||||
size_t new_func_index = context.functions.size();
|
||||
context.functions.emplace_back(
|
||||
static_func_addr,
|
||||
rom_addr,
|
||||
std::move(insn_words),
|
||||
fmt::format("static_{}_{:08X}", section_index, static_func_addr),
|
||||
static_cast<uint16_t>(section_index),
|
||||
false
|
||||
};
|
||||
);
|
||||
const N64Recomp::Function& new_func = context.functions[new_func_index];
|
||||
|
||||
fmt::print(func_header_file,
|
||||
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
||||
"void {}(uint8_t* rdram, recomp_context* ctx);\n", new_func.name);
|
||||
|
||||
bool result;
|
||||
size_t prev_num_statics = static_funcs_by_section[func.section_index].size();
|
||||
size_t prev_num_statics = static_funcs_by_section[new_func.section_index].size();
|
||||
if (config.single_file_output || config.functions_per_output_file > 1) {
|
||||
result = N64Recomp::recompile_function(context, func, current_output_file, static_funcs_by_section, false);
|
||||
result = N64Recomp::recompile_function(context, new_func_index, current_output_file, static_funcs_by_section, false);
|
||||
if (!config.single_file_output) {
|
||||
cur_file_function_count++;
|
||||
if (cur_file_function_count >= config.functions_per_output_file) {
|
||||
|
@ -821,14 +824,14 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
result = recompile_single_function(context, func, config.recomp_include, config.output_func_path / (func.name + ".c"), static_funcs_by_section);
|
||||
result = recompile_single_function(context, new_func_index, config.recomp_include, config.output_func_path / (new_func.name + ".c"), static_funcs_by_section);
|
||||
}
|
||||
|
||||
// Add any new static functions that were found while recompiling this one.
|
||||
size_t cur_num_statics = static_funcs_by_section[func.section_index].size();
|
||||
size_t cur_num_statics = static_funcs_by_section[new_func.section_index].size();
|
||||
if (cur_num_statics != prev_num_statics) {
|
||||
for (size_t new_static_index = prev_num_statics; new_static_index < cur_num_statics; new_static_index++) {
|
||||
uint32_t new_static_vram = static_funcs_by_section[func.section_index][new_static_index];
|
||||
uint32_t new_static_vram = static_funcs_by_section[new_func.section_index][new_static_index];
|
||||
|
||||
if (!statics_set.contains(new_static_vram)) {
|
||||
statics_set.emplace(new_static_vram);
|
||||
|
@ -838,7 +841,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
if (result == false) {
|
||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||
fmt::print(stderr, "Error recompiling {}\n", new_func.name);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,6 +157,8 @@ namespace N64Recomp {
|
|||
{ InstrId::cpu_bltzl, { BinaryOpType::Less, {{ UnaryOpType::ToS64, UnaryOpType::None }, { Operand::Rs, Operand::Zero }}, false, true }},
|
||||
{ InstrId::cpu_bgezal, { BinaryOpType::GreaterEq, {{ UnaryOpType::ToS64, UnaryOpType::None }, { Operand::Rs, Operand::Zero }}, true, false }},
|
||||
{ InstrId::cpu_bgezall, { BinaryOpType::GreaterEq, {{ UnaryOpType::ToS64, UnaryOpType::None }, { Operand::Rs, Operand::Zero }}, true, true }},
|
||||
{ InstrId::cpu_bltzal, { BinaryOpType::Less, {{ UnaryOpType::ToS64, UnaryOpType::None }, { Operand::Rs, Operand::Zero }}, true, false }},
|
||||
{ InstrId::cpu_bltzall, { BinaryOpType::Less, {{ UnaryOpType::ToS64, UnaryOpType::None }, { Operand::Rs, Operand::Zero }}, true, true }},
|
||||
{ InstrId::cpu_bc1f, { BinaryOpType::Equal, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Cop1cs, Operand::Zero }}, false, false }},
|
||||
{ InstrId::cpu_bc1fl, { BinaryOpType::Equal, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Cop1cs, Operand::Zero }}, false, true }},
|
||||
{ InstrId::cpu_bc1t, { BinaryOpType::NotEqual, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Cop1cs, Operand::Zero }}, false, false }},
|
||||
|
|
|
@ -280,6 +280,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
// Normal symbol or reference symbol,
|
||||
else {
|
||||
std::string jal_target_name{};
|
||||
size_t matched_func_index = (size_t)-1;
|
||||
if (reloc_reference_symbol != (size_t)-1) {
|
||||
const auto& ref_symbol = context.get_reference_symbol(reloc_section, reloc_reference_symbol);
|
||||
|
||||
|
@ -292,11 +293,8 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
fmt::print(stderr, "Function {} uses a MIPS_R_26 addend, which is not supported yet\n", func.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
jal_target_name = ref_symbol.name;
|
||||
}
|
||||
else {
|
||||
size_t matched_func_index = 0;
|
||||
JalResolutionResult jal_result = resolve_jal(context, func.section_index, target_func_vram, matched_func_index);
|
||||
|
||||
switch (jal_result) {
|
||||
|
@ -331,11 +329,14 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
return false;
|
||||
}
|
||||
print_indent();
|
||||
if (call_by_lookup) {
|
||||
if (reloc_reference_symbol != (size_t)-1) {
|
||||
generator.emit_function_call_reference_symbol(context, reloc_section, reloc_reference_symbol);
|
||||
}
|
||||
else if (call_by_lookup) {
|
||||
generator.emit_function_call_lookup(target_func_vram);
|
||||
}
|
||||
else {
|
||||
generator.emit_function_call_by_name(jal_target_name);
|
||||
generator.emit_function_call(context, matched_func_index);
|
||||
}
|
||||
print_link_branch();
|
||||
}
|
||||
|
@ -355,8 +356,8 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
print_indent();
|
||||
generator.emit_return();
|
||||
// TODO check if this branch close should exist.
|
||||
print_indent();
|
||||
generator.emit_branch_close();
|
||||
// print_indent();
|
||||
// generator.emit_branch_close();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -747,11 +748,12 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
}
|
||||
|
||||
template <typename GeneratorType>
|
||||
bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context& context, const N64Recomp::Function& func, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context& context, size_t func_index, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
const N64Recomp::Function& func = context.functions[func_index];
|
||||
//fmt::print("Recompiling {}\n", func.name);
|
||||
std::vector<rabbitizer::InstructionCpu> instructions;
|
||||
|
||||
generator.emit_function_start(func.name);
|
||||
generator.emit_function_start(func.name, func_index);
|
||||
|
||||
if (context.trace_mode) {
|
||||
fmt::print(output_file,
|
||||
|
@ -859,11 +861,11 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
|||
}
|
||||
|
||||
// Wrap the templated function with CGenerator as the template parameter.
|
||||
bool N64Recomp::recompile_function(const N64Recomp::Context& context, const N64Recomp::Function& func, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
bool N64Recomp::recompile_function(const N64Recomp::Context& context, size_t function_index, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
CGenerator generator{output_file};
|
||||
return recompile_function_impl(generator, context, func, output_file, static_funcs_out, tag_reference_relocs);
|
||||
return recompile_function_impl(generator, context, function_index, output_file, static_funcs_out, tag_reference_relocs);
|
||||
}
|
||||
|
||||
bool N64Recomp::recompile_function_custom(Generator& generator, const Context& context, const Function& func, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
return recompile_function_impl(generator, context, func, output_file, static_funcs_out, tag_reference_relocs);
|
||||
bool N64Recomp::recompile_function_custom(Generator& generator, const Context& context, size_t function_index, std::ostream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||
return recompile_function_impl(generator, context, function_index, output_file, static_funcs_out, tag_reference_relocs);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue