Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ps2xRecomp/include/ps2recomp/code_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace ps2recomp
struct Function;
struct Symbol;
struct Section;
class RecompilerReporter;

extern const std::unordered_set<std::string> kKeywords;

Expand Down Expand Up @@ -54,6 +55,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,
Expand All @@ -68,8 +70,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);
Expand Down
4 changes: 4 additions & 0 deletions ps2xRecomp/include/ps2recomp/config_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

namespace ps2recomp
{
class RecompilerReporter;

class ConfigManager
{
public:
Expand All @@ -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;
};

}
Expand Down
84 changes: 84 additions & 0 deletions ps2xRecomp/include/ps2recomp/control_flow_emitter.h
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions ps2xRecomp/include/ps2recomp/control_flow_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#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
{
if (inst.raw == 0u)
{
return true;
}

// Some tests and decoded streams represent a no-op as addiu $zero, $zero, 0.
return inst.opcode == OPCODE_ADDIU &&
inst.rs == 0u &&
inst.rt == 0u &&
static_cast<int16_t>(inst.simmediate) == 0;
}
}

#endif // PS2RECOMP_CONTROL_FLOW_UTILS_H
3 changes: 3 additions & 0 deletions ps2xRecomp/include/ps2recomp/elf_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace ps2recomp
struct Section;
struct Function;
struct Symbol;
class RecompilerReporter;

class ElfParser
{
Expand All @@ -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:
Expand All @@ -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();
Expand Down
5 changes: 5 additions & 0 deletions ps2xRecomp/include/ps2recomp/ps2_recompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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(
Expand All @@ -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;
Expand Down
93 changes: 93 additions & 0 deletions ps2xRecomp/include/ps2recomp/recompiler_reporter.h
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading