Refactor runtime for move speed and better code style (#140)

* feat: added guestBranchKind enum to categorize branch types
feat: added missingFunctionPolicy enum to define behaviors for missing function scenarios
refactor: added handle guest branches and report missing functions
feat lookupFunction to utilize new dispatch logic and improve error handling for unregistered functions

* fix: fix test conflict

* feat: added debug sound driver logs

* feat: emmiter for return

* feat: added recompiler reporter
feat: added strict diagnostics flag for heavy debug calls

* feat: staticc table insted of hashmap for runtime

* feat: back file to ignore

* feat: explode code across helpers and classes

* feat: update codegen test
feat: better guest nop check

* feat: fix link problem on linux

* feat: fix Segmentation fault
This commit is contained in:
Ranieri
2026-07-04 00:43:22 -03:00
committed by GitHub
parent 3a2e0d69fd
commit 5196a6672a
53 changed files with 5879 additions and 4353 deletions
@@ -0,0 +1,84 @@
#ifndef PS2RECOMP_CONTROL_FLOW_EMITTER_H
#define PS2RECOMP_CONTROL_FLOW_EMITTER_H
#include "ps2recomp/code_generator.h"
#include "ps2recomp/types.h"
#include <cstdint>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
namespace ps2recomp
{
class ControlFlowEmitter
{
public:
ControlFlowEmitter(CodeGenerator &generator,
const Instruction &branchInst,
const Instruction &delaySlot,
const Function &function,
const CodeGenerator::AnalysisResult &analysisResult);
std::string emit();
private:
enum class StaticBranchKind
{
Jump,
Call,
};
enum class RegisterBranchKind
{
Jump,
Call,
};
CodeGenerator &m_gen;
const Instruction &m_branchInst;
const Instruction &m_delaySlot;
const Function &m_function;
const CodeGenerator::AnalysisResult &m_analysisResult;
std::stringstream m_ss;
uint32_t branchPc() const;
uint32_t delayPc() const;
uint32_t fallthroughPc() const;
bool hasRealDelaySlot() const;
bool isLikelyBranch() const;
bool isCallLikeEdge() const;
bool isInternalTarget(uint32_t target) const;
std::vector<uint32_t> resolvedLocalIndirectTargets() const;
std::string delaySlotCode() const;
void emitDelaySlot(std::string_view indent);
void emitResumeFromDelaySlotEntry();
void emitInternalTarget(uint32_t target, uint32_t sourcePc, std::string_view indent);
void emitFallthroughLabelIfNeeded();
void emitFinalFallthrough();
void emitStaticJump(StaticBranchKind kind);
void emitRegisterJump(RegisterBranchKind kind);
void emitConditionalBranch();
void emitFallbackInstruction();
bool emitDirectFunctionJumpIfAvailable(uint32_t target, StaticBranchKind kind, std::string_view indent);
void emitExternalJumpDispatch(uint32_t target, StaticBranchKind kind, std::string_view indent);
void emitExternalRegisterCallDispatch(std::string_view jumpTargetExpression, std::string_view indent);
void emitExternalRegisterJumpDispatch(std::string_view jumpTargetExpression, RegisterBranchKind kind, uint8_t rsReg, std::string_view indent);
void emitRuntimeBranchDispatch(std::string_view targetExpression,
uint32_t sourcePc,
uint32_t returnPc,
std::string_view runtimeKind,
std::string_view debugName,
std::string_view indent,
bool returnOnTransfer);
bool emitRelocationCallIfAvailable(StaticBranchKind kind, std::string_view indent);
std::string conditionalBranchExpression() const;
uint32_t conditionalBranchTarget() const;
};
}
#endif // PS2RECOMP_CONTROL_FLOW_EMITTER_H
@@ -0,0 +1,25 @@
#ifndef PS2RECOMP_FUNCTION_EMITTER_H
#define PS2RECOMP_FUNCTION_EMITTER_H
#include <string>
#include <vector>
namespace ps2recomp
{
struct Function;
struct Instruction;
class CodeGenerator;
class FunctionEmitter
{
public:
explicit FunctionEmitter(CodeGenerator &codeGenerator);
std::string emit(const Function &function, const std::vector<Instruction> &instructions, bool useHeaders);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_FUNCTION_EMITTER_H
@@ -0,0 +1,26 @@
#ifndef PS2RECOMP_FUNCTION_TABLE_EMITTER_H
#define PS2RECOMP_FUNCTION_TABLE_EMITTER_H
#include <cstdint>
#include <map>
#include <string>
#include <vector>
namespace ps2recomp
{
struct Function;
class CodeGenerator;
class FunctionTableEmitter
{
public:
explicit FunctionTableEmitter(CodeGenerator &codeGenerator);
std::string emit(const std::vector<Function> &functions, const std::map<uint32_t, std::string> &stubs);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_FUNCTION_TABLE_EMITTER_H
@@ -0,0 +1,22 @@
#ifndef PS2RECOMP_COP0_TRANSLATOR_H
#define PS2RECOMP_COP0_TRANSLATOR_H
#include <string>
namespace ps2recomp
{
struct Instruction;
class CodeGenerator;
class Cop0Translator
{
public:
explicit Cop0Translator(CodeGenerator &codeGenerator);
std::string translate(const Instruction &inst);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_COP0_TRANSLATOR_H
@@ -0,0 +1,22 @@
#ifndef PS2RECOMP_FPU_TRANSLATOR_H
#define PS2RECOMP_FPU_TRANSLATOR_H
#include <string>
namespace ps2recomp
{
struct Instruction;
class CodeGenerator;
class FpuTranslator
{
public:
explicit FpuTranslator(CodeGenerator &codeGenerator);
std::string translate(const Instruction &inst);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_FPU_TRANSLATOR_H
@@ -0,0 +1,22 @@
#ifndef PS2RECOMP_INSTRUCTION_TRANSLATOR_H
#define PS2RECOMP_INSTRUCTION_TRANSLATOR_H
#include <string>
namespace ps2recomp
{
struct Instruction;
class CodeGenerator;
class InstructionTranslator
{
public:
explicit InstructionTranslator(CodeGenerator &codeGenerator);
std::string translate(const Instruction &inst);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_INSTRUCTION_TRANSLATOR_H
@@ -0,0 +1,22 @@
#ifndef PS2RECOMP_MMI_TRANSLATOR_H
#define PS2RECOMP_MMI_TRANSLATOR_H
#include <string>
namespace ps2recomp
{
struct Instruction;
class CodeGenerator;
class MmiTranslator
{
public:
explicit MmiTranslator(CodeGenerator &codeGenerator);
std::string translate(const Instruction &inst);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_MMI_TRANSLATOR_H
@@ -0,0 +1,22 @@
#ifndef PS2RECOMP_REGIMM_TRANSLATOR_H
#define PS2RECOMP_REGIMM_TRANSLATOR_H
#include <string>
namespace ps2recomp
{
struct Instruction;
class CodeGenerator;
class RegimmTranslator
{
public:
explicit RegimmTranslator(CodeGenerator &codeGenerator);
std::string translate(const Instruction &inst);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_REGIMM_TRANSLATOR_H
@@ -0,0 +1,22 @@
#ifndef PS2RECOMP_SPECIAL_TRANSLATOR_H
#define PS2RECOMP_SPECIAL_TRANSLATOR_H
#include <string>
namespace ps2recomp
{
struct Instruction;
class CodeGenerator;
class SpecialTranslator
{
public:
explicit SpecialTranslator(CodeGenerator &codeGenerator);
std::string translate(const Instruction &inst);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_SPECIAL_TRANSLATOR_H
@@ -0,0 +1,22 @@
#ifndef PS2RECOMP_VU_TRANSLATOR_H
#define PS2RECOMP_VU_TRANSLATOR_H
#include <string>
namespace ps2recomp
{
struct Instruction;
class CodeGenerator;
class VuTranslator
{
public:
explicit VuTranslator(CodeGenerator &codeGenerator);
std::string translate(const Instruction &inst);
private:
CodeGenerator &m_codeGenerator;
};
}
#endif // PS2RECOMP_VU_TRANSLATOR_H
@@ -7,6 +7,7 @@
#include <map>
#include <unordered_map>
#include <unordered_set>
#include "ps2recomp/control_flow_analyzer.h"
namespace ps2recomp
{
@@ -16,6 +17,7 @@ namespace ps2recomp
struct Function;
struct Symbol;
struct Section;
class RecompilerReporter;
extern const std::unordered_set<std::string> kKeywords;
@@ -35,13 +37,7 @@ namespace ps2recomp
std::string entryName;
};
struct AnalysisResult {
std::unordered_set<uint32_t> entryPoints;
std::unordered_set<uint32_t> externalEntryPoints;
std::unordered_set<uint32_t> resumeEntryPoints;
std::unordered_set<uint32_t> indirectFallbackEntryPoints;
std::unordered_map<uint32_t, std::vector<uint32_t>> jumpTableTargets;
};
using AnalysisResult = ControlFlowAnalysisResult;
std::string generateFunction(const Function &function, const std::vector<Instruction> &instructions, const bool &useHeaders);
std::string generateFunctionRegistration(const std::vector<Function> &functions, const std::map<uint32_t, std::string> &stubs);
@@ -54,6 +50,7 @@ namespace ps2recomp
void setConfiguredJumpTables(const std::vector<JumpTable> &jumpTables);
void setResumeEntryTargets(const std::unordered_map<uint32_t, std::vector<uint32_t>> &resumeTargetsByOwner);
void setEmitInstructionComments(bool emitInstructionComments);
void setReporter(RecompilerReporter *reporter);
AnalysisResult collectInternalBranchTargets(const Function &function,
const std::vector<Instruction> &instructions,
@@ -68,8 +65,11 @@ namespace ps2recomp
const std::vector<Section>& m_sections;
BootstrapInfo m_bootstrapInfo;
bool m_emitInstructionComments = true;
RecompilerReporter *m_reporter = nullptr;
std::string m_currentFunctionName;
std::string translateInstruction(const Instruction &inst);
std::string emitUnhandledInstruction(const Instruction &inst, const std::string &message);
std::string translateMMIInstruction(const Instruction &inst);
std::string translateVUInstruction(const Instruction &inst);
std::string translateFPUInstruction(const Instruction &inst);
@@ -0,0 +1,37 @@
#ifndef PS2RECOMP_CODEGEN_HELPERS_H
#define PS2RECOMP_CODEGEN_HELPERS_H
#include <cmath>
#include <cstdint>
#include <string>
#include <fmt/format.h>
namespace ps2recomp::codegen
{
inline std::string formatFloatLiteral(float value)
{
if (!std::isfinite(value))
{
return (value < 0.0f) ? "-INFINITY" : "INFINITY";
}
std::string literal = fmt::format("{:.9g}", value);
if (literal.find_first_of(".eE") == std::string::npos)
{
literal += ".0";
}
literal += 'f';
return literal;
}
inline std::string vuMaskExpr(uint8_t dest_mask)
{
return fmt::format("_mm_castsi128_ps(_mm_set_epi32({}, {}, {}, {}))",
(dest_mask & 0x1) ? -1 : 0,
(dest_mask & 0x2) ? -1 : 0,
(dest_mask & 0x4) ? -1 : 0,
(dest_mask & 0x8) ? -1 : 0);
}
}
#endif // PS2RECOMP_CODEGEN_HELPERS_H
@@ -6,6 +6,8 @@
namespace ps2recomp
{
class RecompilerReporter;
class ConfigManager
{
public:
@@ -14,9 +16,11 @@ namespace ps2recomp
RecompilerConfig loadConfig() const;
void saveConfig(const RecompilerConfig &config) const;
void setReporter(RecompilerReporter *reporter);
private:
std::string m_configPath;
RecompilerReporter *m_reporter = nullptr;
};
}
@@ -0,0 +1,43 @@
#ifndef PS2RECOMP_CONTROL_FLOW_ANALYZER_H
#define PS2RECOMP_CONTROL_FLOW_ANALYZER_H
#include <cstdint>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace ps2recomp
{
struct Function;
struct Instruction;
struct Section;
class RecompilerReporter;
struct ControlFlowAnalysisResult
{
std::unordered_set<uint32_t> entryPoints;
std::unordered_set<uint32_t> externalEntryPoints;
std::unordered_set<uint32_t> resumeEntryPoints;
std::unordered_set<uint32_t> indirectFallbackEntryPoints;
std::unordered_map<uint32_t, std::vector<uint32_t>> jumpTableTargets;
};
class ControlFlowAnalyzer
{
public:
ControlFlowAnalyzer(const std::vector<Section> &sections,
const std::unordered_map<uint32_t, std::vector<uint32_t>> &configuredJumpTableTargetsByAddress,
RecompilerReporter *reporter);
ControlFlowAnalysisResult analyze(const Function &function,
const std::vector<Instruction> &instructions,
const std::vector<Function> *allFunctions = nullptr) const;
private:
const std::vector<Section> &m_sections;
const std::unordered_map<uint32_t, std::vector<uint32_t>> &m_configJumpTableTargetsByAddress;
RecompilerReporter *m_reporter = nullptr;
};
}
#endif // PS2RECOMP_CONTROL_FLOW_ANALYZER_H
@@ -0,0 +1,35 @@
#ifndef PS2RECOMP_CONTROL_FLOW_UTILS_H
#define PS2RECOMP_CONTROL_FLOW_UTILS_H
#include "ps2recomp/instructions.h"
#include <cstdint>
namespace ps2recomp
{
inline uint32_t buildAbsoluteJumpTarget(uint32_t address, uint32_t target) noexcept
{
return ((address + 4u) & 0xF0000000u) | (target << 2);
}
inline bool isGuestNop(const Instruction &inst) noexcept
{
const bool sllZero =
inst.opcode == OPCODE_SPECIAL &&
inst.function == SPECIAL_SLL &&
inst.rd == 0u &&
inst.rt == 0u &&
inst.sa == 0u;
// Some tests and decoded streams represent a no-op as addiu $zero, $zero, 0.
const bool addiuZero =
inst.opcode == OPCODE_ADDIU &&
inst.rs == 0u &&
inst.rt == 0u &&
static_cast<int16_t>(inst.simmediate) == 0;
return sllZero || addiuZero;
}
}
#endif // PS2RECOMP_CONTROL_FLOW_UTILS_H
@@ -13,6 +13,7 @@ namespace ps2recomp
struct Section;
struct Function;
struct Symbol;
class RecompilerReporter;
class ElfParser
{
@@ -36,6 +37,7 @@ namespace ps2recomp
uint32_t getSectionAddress(const std::string &sectionName) const;
uint32_t getSectionSize(const std::string &sectionName) const;
uint32_t getEntryPoint() const;
void setReporter(RecompilerReporter *reporter);
void debugAddress(uint32_t address) const;
private:
@@ -47,6 +49,7 @@ namespace ps2recomp
std::vector<Relocation> m_relocations;
std::vector<Function> m_extraFunctions;
bool m_hasLoadedGhidraMap = false;
RecompilerReporter *m_reporter = nullptr;
std::unordered_set<uint32_t> m_ghidraMapStarts;
void loadSections();
@@ -3,11 +3,14 @@
#include "code_generator.h"
#include "config_manager.h"
#include "recompiler_reporter.h"
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <filesystem>
#include <memory>
#include <map>
namespace ps2recomp
{
@@ -30,6 +33,7 @@ namespace ps2recomp
bool initialize();
bool recompile();
void generateOutput();
void printReport() const;
static StubTarget resolveStubTarget(const std::string& name);
static size_t DiscoverAdditionalEntryPoints(
@@ -48,6 +52,7 @@ namespace ps2recomp
std::unique_ptr<R5900Decoder> m_decoder;
std::unique_ptr<CodeGenerator> m_codeGenerator;
RecompilerConfig m_config;
RecompilerReporter m_reporter;
std::vector<Function> m_functions;
std::vector<Symbol> m_symbols;
@@ -0,0 +1,93 @@
#ifndef PS2RECOMP_RECOMPILER_REPORTER_H
#define PS2RECOMP_RECOMPILER_REPORTER_H
#include <cstddef>
#include <cstdint>
#include <iosfwd>
#include <mutex>
#include <string>
#include <vector>
namespace ps2recomp
{
class RecompilerReporter
{
public:
enum class Severity
{
Info,
Warning,
Error
};
struct Event
{
Severity severity = Severity::Info;
std::string category;
std::string message;
std::string functionName;
uint32_t address = 0;
bool hasAddress = false;
};
struct Counters
{
size_t functionsDiscovered = 0;
size_t symbolsDiscovered = 0;
size_t sectionsDiscovered = 0;
size_t relocationsDiscovered = 0;
size_t functionsProcessed = 0;
size_t functionsRecompiled = 0;
size_t functionsStubbed = 0;
size_t functionsSkipped = 0;
size_t decodeFailures = 0;
size_t additionalEntryPoints = 0;
size_t generatedFunctions = 0;
size_t unhandledInstructions = 0;
size_t indirectFallbackPromotions = 0;
size_t indirectFallbackEntries = 0;
};
void progress(const std::string &message);
void info(const std::string &category, const std::string &message);
void warning(const std::string &category, const std::string &message);
void error(const std::string &category, const std::string &message);
void warningAt(const std::string &category, const std::string &functionName, uint32_t address, const std::string &message);
void errorAt(const std::string &category, const std::string &functionName, uint32_t address, const std::string &message);
void recordDiscovered(size_t functions, size_t symbols, size_t sections, size_t relocations);
void recordFunctionProcessed();
void recordFunctionRecompiled();
void recordFunctionStubbed();
void recordFunctionSkipped();
void recordDecodeFailure();
void recordAdditionalEntryPoints(size_t count);
void recordGeneratedFunctions(size_t count);
void recordIndirectFallbackPromotion(const std::string &functionName,
const std::vector<uint32_t> &jumpAddresses,
size_t promotedEntryCount);
void recordUnhandledInstruction(const std::string &functionName,
uint32_t address,
uint32_t raw,
const std::string &message);
const Counters &counters() const;
bool hasErrors() const;
bool hasWarnings() const;
void printSummary(std::ostream &os) const;
private:
void addEvent(Severity severity,
const std::string &category,
const std::string &message,
const std::string &functionName = {},
uint32_t address = 0,
bool hasAddress = false);
mutable std::mutex m_mutex;
Counters m_counters;
std::vector<Event> m_events;
};
}
#endif