(Live recompiler) Add dedicated float compare operations, fix switch case handling with multiple functions, add reference symbol handling, other misc fixes

This commit is contained in:
Mr-Wiseguy 2024-12-29 17:44:44 -05:00
parent 3672805121
commit 75f2abdb7d
8 changed files with 275 additions and 100 deletions

View file

@ -188,6 +188,8 @@ namespace N64Recomp {
std::vector<ReferenceSymbol> reference_symbols;
// Mapping of symbol name to reference symbol index.
std::unordered_map<std::string, SymbolReference> reference_symbols_by_name;
// Whether all reference sections should be treated as relocatable (used in live recompilation).
bool all_reference_sections_relocatable = false;
public:
std::vector<Section> sections;
std::vector<Function> functions;
@ -200,6 +202,8 @@ namespace N64Recomp {
// The target ROM being recompiled, TODO move this outside of the context to avoid making a copy for mod contexts.
// Used for reading relocations and for the output binary feature.
std::vector<uint8_t> rom;
// Whether reference symbols should be validated when emitting function calls during recompilation.
bool skip_validating_reference_symbols = true;
//// Only used by the CLI, TODO move this to a struct in the internal headers.
// A mapping of function name to index in the functions vector
@ -372,6 +376,9 @@ namespace N64Recomp {
}
bool is_reference_section_relocatable(uint16_t section_index) const {
if (all_reference_sections_relocatable) {
return true;
}
if (section_index == SectionAbsolute) {
return false;
}
@ -531,6 +538,10 @@ namespace N64Recomp {
void copy_reference_sections_from(const Context& rhs) {
reference_sections = rhs.reference_sections;
}
void set_all_reference_sections_relocatable() {
all_reference_sections_relocatable = true;
}
};
class Generator;

View file

@ -34,7 +34,9 @@ namespace N64Recomp {
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_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const = 0;
// target_section_offset can each be deduced from symbol_index if the full context is available,
// but for live recompilation the reference symbol list is unavailable so it's still provided.
virtual void emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index, uint32_t target_section_offset) 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;
@ -70,7 +72,7 @@ namespace N64Recomp {
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_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const final;
void emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index, uint32_t target_section_offset) 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;

View file

@ -1,6 +1,7 @@
#ifndef __LIVE_RECOMPILER_H__
#define __LIVE_RECOMPILER_H__
#include <unordered_map>
#include "recompiler/generator.h"
#include "recomp.h"
@ -8,6 +9,10 @@ struct sljit_compiler;
namespace N64Recomp {
struct LiveGeneratorContext;
struct ReferenceJumpDetails {
uint16_t section;
uint32_t section_offset;
};
struct LiveGeneratorOutput {
LiveGeneratorOutput() = default;
LiveGeneratorOutput(const LiveGeneratorOutput& rhs) = delete;
@ -20,29 +25,46 @@ namespace N64Recomp {
code = rhs.code;
code_size = rhs.code_size;
functions = std::move(rhs.functions);
reference_symbol_jumps = std::move(rhs.reference_symbol_jumps);
import_jumps_by_index = std::move(rhs.import_jumps_by_index);
executable_offset = rhs.executable_offset;
rhs.good = false;
rhs.string_literals.clear();
rhs.jump_tables.clear();
rhs.code = nullptr;
rhs.code_size = 0;
rhs.reference_symbol_jumps.clear();
rhs.executable_offset = 0;
return *this;
}
~LiveGeneratorOutput();
size_t num_reference_symbol_jumps() const;
void set_reference_symbol_jump(size_t jump_index, recomp_func_t* func);
ReferenceJumpDetails get_reference_symbol_jump_details(size_t jump_index);
void populate_import_symbol_jumps(size_t import_index, recomp_func_t* func);
bool good = false;
// Storage for string literals referenced by recompiled code. These must be manually allocated to prevent
// them from moving, as the referenced address is baked into the recompiled code.
std::vector<const char*> string_literals;
// Storage for jump tables referenced by recompiled code (vector of arrays of pointers). These must also be manually allocated
// for the same reason as strings.
std::vector<void**> jump_tables;
// Storage for string literals referenced by recompiled code. These are allocated as unique_ptr arrays
// to prevent them from moving, as the referenced address is baked into the recompiled code.
std::vector<std::unique_ptr<char[]>> string_literals;
// Storage for jump tables referenced by recompiled code (vector of arrays of pointers). These are also
// allocated as unique_ptr arrays for the same reason as strings.
std::vector<std::unique_ptr<void*[]>> jump_tables;
// Recompiled code.
void* code;
// Size of the recompiled code.
size_t code_size;
// Pointers to each individual function within the recompiled code.
std::vector<recomp_func_t*> functions;
private:
// List of jump details and the corresponding jump instruction address. These jumps get populated after recompilation is complete
// during dependency resolution.
std::vector<std::pair<ReferenceJumpDetails, void*>> reference_symbol_jumps;
// Mapping of import symbol index to any jumps to that import symbol.
std::unordered_multimap<size_t, void*> import_jumps_by_index;
// sljit executable offset.
int64_t executable_offset;
friend class LiveGenerator;
};
struct LiveGeneratorInputs {
uint32_t base_event_index;
@ -75,7 +97,7 @@ namespace N64Recomp {
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_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index) const final;
void emit_function_call_reference_symbol(const Context& context, uint16_t section_index, size_t symbol_index, uint32_t target_section_offset) 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;
@ -103,7 +125,7 @@ namespace N64Recomp {
void get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string) const;
void get_binary_expr_string(BinaryOpType type, const BinaryOperands& operands, const InstructionContext& ctx, const std::string& output, std::string& expr_string) const;
void get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string) const;
// Loads the relocated address specified by the instruction context into R0.
// Loads the relocated address specified by the instruction context into the target register.
void load_relocated_address(const InstructionContext& ctx, int reg) const;
sljit_compiler* compiler;
LiveGeneratorInputs inputs;

View file

@ -99,6 +99,12 @@ namespace N64Recomp {
LessEq,
Greater,
GreaterEq,
EqualF32,
LessF32,
LessEqF32,
EqualF64,
LessF64,
LessEqF64,
// Loads
LD,
LW,