Implemented calling internal functions in live recompiler, added bltzal/bltzall to branch operations

This commit is contained in:
Mr-Wiseguy 2024-10-07 00:15:11 -04:00
parent b7abe0ac4e
commit 898de82da7
10 changed files with 246 additions and 80 deletions

View file

@ -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);
}

View file

@ -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;