Made member methods const (#34)

* refactor: update function name checks to use contains method

* refactor: update code generation to use static_cast

* refactor: made member methods const

* refactor: made range-based loop

* refactor: make one variable constructor explicit

* refactor: remove redundant else

* refactor: turn includes to forward declarations

* refactor: brace placements turned into allman style
This commit is contained in:
M. Sami Gürpınar
2026-01-31 19:14:56 +03:00
committed by GitHub
parent e8f0c69de6
commit c218c64348
14 changed files with 134 additions and 119 deletions
+28 -19
View File
@@ -1,9 +1,6 @@
#ifndef PS2RECOMP_ELF_ANALYZER_H
#define PS2RECOMP_ELF_ANALYZER_H
#include "ps2recomp/elf_parser.h"
#include "ps2recomp/r5900_decoder.h"
#include "ps2recomp/types.h"
#include <string>
#include <vector>
#include <unordered_set>
@@ -14,10 +11,23 @@
namespace ps2recomp
{
class ElfAnalyzer
struct CFGNode;
struct Instruction;
struct FunctionCall;
struct JumpTable;
struct Relocation;
struct Section;
struct Symbol;
struct Function;
class R5900Decoder;
class ElfParser;
using CFG = std::unordered_map<uint32_t, CFGNode>;
class ElfAnalyzer
{
public:
ElfAnalyzer(const std::string &elfPath);
explicit ElfAnalyzer(const std::string &elfPath);
~ElfAnalyzer();
bool analyze();
@@ -40,7 +50,6 @@ namespace ps2recomp
std::map<uint32_t, uint32_t> m_patches;
std::map<uint32_t, std::string> m_patchReasons;
std::unordered_map<uint32_t, CFG> m_functionCFGs;
std::vector<JumpTable> m_jumpTables;
std::unordered_map<uint32_t, std::vector<FunctionCall>> m_functionCalls;
@@ -52,30 +61,30 @@ namespace ps2recomp
void identifyPotentialPatches();
void analyzeControlFlow();
void detectJumpTables();
void analyzePerformanceCriticalPaths();
void analyzePerformanceCriticalPaths() const;
void identifyRecursiveFunctions();
void analyzeRegisterUsage();
void analyzeFunctionSignatures();
void analyzeRegisterUsage() const;
void analyzeFunctionSignatures() const;
void optimizePatches();
bool identifyMemcpyPattern(const Function &func);
bool identifyMemsetPattern(const Function &func);
bool identifyStringOperationPattern(const Function &func);
bool identifyMathPattern(const Function &func);
bool identifyMemcpyPattern(const Function &func) const;
bool identifyMemsetPattern(const Function &func) const;
bool identifyStringOperationPattern(const Function &func) const;
bool identifyMathPattern(const Function &func) const;
bool isSystemFunction(const std::string &name) const;
bool isLibraryFunction(const std::string &name) const;
std::vector<Instruction> decodeFunction(const Function &function);
CFG buildCFG(const Function &function);
std::vector<Instruction> decodeFunction(const Function &function) const;
CFG buildCFG(const Function &function) const;
std::string formatAddress(uint32_t address) const;
std::string escapeBackslashes(const std::string &path);
bool hasMMIInstructions(const Function &function);
bool hasVUInstructions(const Function &function);
bool hasMMIInstructions(const Function &function) const;
bool hasVUInstructions(const Function &function) const;
bool identifyFunctionType(const Function &function);
void categorizeFunction(Function &function);
uint32_t getSuccessor(const Instruction &inst, uint32_t currentAddr);
bool isSelfModifyingCode(const Function &function);
bool isLoopHeavyFunction(const Function &function);
bool isSelfModifyingCode(const Function &function) const;
bool isLoopHeavyFunction(const Function &function) const;
};
}
+50 -56
View File
@@ -1,4 +1,7 @@
#include "ps2recomp/elf_analyzer.h"
#include "ps2recomp/elf_parser.h"
#include "ps2recomp/r5900_decoder.h"
#include "ps2recomp/types.h"
#include <iostream>
#include <sstream>
#include <algorithm>
@@ -133,18 +136,17 @@ namespace ps2recomp
file << "# Jump tables detected in the program\n";
file << "[jump_tables]\n";
for (size_t i = 0; i < m_jumpTables.size(); ++i)
for (const auto & jt : m_jumpTables)
{
const auto &jt = m_jumpTables[i];
file << "[[jump_tables.table]]\n";
file << "address = \"0x" << std::hex << jt.address << "\"\n"
<< std::dec;
file << "entries = [\n";
for (const auto &entry : jt.entries)
for (const auto & [index, target] : jt.entries)
{
file << " { index = " << entry.index << ", target = \"0x"
<< std::hex << entry.target << "\" },\n"
file << " { index = " << index << ", target = \"0x"
<< std::hex << target << "\" },\n"
<< std::dec;
}
@@ -779,8 +781,8 @@ namespace ps2recomp
}
}
void ElfAnalyzer::analyzePerformanceCriticalPaths()
{
void ElfAnalyzer::analyzePerformanceCriticalPaths() const
{
std::cout << "Analyzing performance-critical paths..." << std::endl;
for (const auto &func : m_functions)
@@ -793,11 +795,9 @@ namespace ps2recomp
std::vector<Instruction> instructions = decodeFunction(func);
for (size_t i = 0; i < instructions.size(); i++)
for (const auto& inst : instructions)
{
const auto &inst = instructions[i];
if (inst.isBranch)
if (inst.isBranch)
{
int32_t offset = static_cast<int16_t>(inst.immediate) << 2;
uint32_t targetAddr = inst.address + 4 + offset;
@@ -814,11 +814,11 @@ namespace ps2recomp
<< " (size: " << loopSize << " instructions)" << std::endl;
bool hasMultimedia = false;
for (size_t j = 0; j < instructions.size(); j++)
for (const auto& instruction : instructions)
{
if (instructions[j].address >= targetAddr && instructions[j].address <= inst.address)
if (instruction.address >= targetAddr && instruction.address <= inst.address)
{
if (instructions[j].isMultimedia)
if (instruction.isMultimedia)
{
hasMultimedia = true;
break;
@@ -901,8 +901,8 @@ namespace ps2recomp
}
}
void ElfAnalyzer::analyzeRegisterUsage()
{
void ElfAnalyzer::analyzeRegisterUsage() const
{
std::cout << "Analyzing register usage patterns..." << std::endl;
for (const auto &func : m_functions)
@@ -1000,8 +1000,8 @@ namespace ps2recomp
}
}
void ElfAnalyzer::analyzeFunctionSignatures()
{
void ElfAnalyzer::analyzeFunctionSignatures() const
{
std::cout << "Analyzing function signatures..." << std::endl;
for (const auto &func : m_functions)
@@ -1160,8 +1160,8 @@ namespace ps2recomp
}
}
bool ElfAnalyzer::identifyMemcpyPattern(const Function &func)
{
bool ElfAnalyzer::identifyMemcpyPattern(const Function &func) const
{
std::vector<Instruction> instructions = decodeFunction(func);
bool hasLoop = false;
@@ -1169,10 +1169,8 @@ namespace ps2recomp
bool storesData = false;
bool incrementsPointers = false;
for (size_t i = 0; i < instructions.size(); i++)
for (const auto & inst : instructions)
{
const auto &inst = instructions[i];
if (inst.isBranch)
{
int32_t offset = static_cast<int16_t>(inst.immediate) << 2;
@@ -1206,8 +1204,8 @@ namespace ps2recomp
return hasLoop && loadsData && storesData && incrementsPointers;
}
bool ElfAnalyzer::identifyMemsetPattern(const Function &func)
{
bool ElfAnalyzer::identifyMemsetPattern(const Function &func) const
{
std::vector<Instruction> instructions = decodeFunction(func);
bool hasLoop = false;
@@ -1215,10 +1213,8 @@ namespace ps2recomp
bool storesData = false;
bool incrementsPointer = false;
for (size_t i = 0; i < instructions.size(); i++)
for (const auto & inst : instructions)
{
const auto &inst = instructions[i];
if (inst.isBranch)
{
int32_t offset = static_cast<int16_t>(inst.immediate) << 2;
@@ -1251,8 +1247,8 @@ namespace ps2recomp
return hasLoop && usesConstant && storesData && incrementsPointer;
}
bool ElfAnalyzer::identifyStringOperationPattern(const Function &func)
{
bool ElfAnalyzer::identifyStringOperationPattern(const Function &func) const
{
std::vector<Instruction> instructions = decodeFunction(func);
bool hasLoop = false;
@@ -1260,10 +1256,8 @@ namespace ps2recomp
bool loadsByte = false;
bool storesByte = false;
for (size_t i = 0; i < instructions.size(); i++)
for (const auto & inst : instructions)
{
const auto &inst = instructions[i];
if (inst.isBranch)
{
int32_t offset = static_cast<int16_t>(inst.immediate) << 2;
@@ -1293,8 +1287,8 @@ namespace ps2recomp
return hasLoop && checksZero && (loadsByte || storesByte);
}
bool ElfAnalyzer::identifyMathPattern(const Function &func)
{
bool ElfAnalyzer::identifyMathPattern(const Function &func) const
{
std::vector<Instruction> instructions = decodeFunction(func);
int mathOps = 0;
@@ -1324,8 +1318,8 @@ namespace ps2recomp
return mathOps > instructions.size() * 0.3 || usesFPU;
}
CFG ElfAnalyzer::buildCFG(const Function &function)
{
CFG ElfAnalyzer::buildCFG(const Function &function) const
{
CFG cfg;
std::vector<Instruction> instructions = decodeFunction(function);
std::map<uint32_t, size_t> addrToIndex;
@@ -1567,8 +1561,8 @@ namespace ps2recomp
return false;
}
std::vector<Instruction> ElfAnalyzer::decodeFunction(const Function &function)
{
std::vector<Instruction> ElfAnalyzer::decodeFunction(const Function &function) const
{
std::vector<Instruction> instructions;
for (uint32_t addr = function.start; addr < function.end; addr += 4)
@@ -1602,8 +1596,8 @@ namespace ps2recomp
return ss.str();
}
bool ElfAnalyzer::hasMMIInstructions(const Function &function)
{
bool ElfAnalyzer::hasMMIInstructions(const Function &function) const
{
std::vector<Instruction> instructions = decodeFunction(function);
for (const auto &inst : instructions)
@@ -1617,8 +1611,8 @@ namespace ps2recomp
return false;
}
bool ElfAnalyzer::hasVUInstructions(const Function &function)
{
bool ElfAnalyzer::hasVUInstructions(const Function &function) const
{
std::vector<Instruction> instructions = decodeFunction(function);
for (const auto &inst : instructions)
@@ -1685,11 +1679,12 @@ namespace ps2recomp
std::cout << "Skipping function " << function.name << " due to hardware I/O" << std::endl;
return true;
}
else if (hasComplexMMI && isVeryLarge)
if (hasComplexMMI && isVeryLarge)
{
m_skipFunctions.insert(function.name);
std::cout << "Skipping large function " << function.name << " with complex MMI" << std::endl;
return true;
m_skipFunctions.insert(function.name);
std::cout << "Skipping large function " << function.name << " with complex MMI" << std::endl;
return true;
}
return false;
@@ -1714,8 +1709,8 @@ namespace ps2recomp
}
}
bool ElfAnalyzer::isSelfModifyingCode(const Function &function)
{
bool ElfAnalyzer::isSelfModifyingCode(const Function &function) const
{
std::vector<Instruction> instructions = decodeFunction(function);
for (size_t i = 0; i < instructions.size(); i++)
@@ -1760,15 +1755,13 @@ namespace ps2recomp
return false;
}
bool ElfAnalyzer::isLoopHeavyFunction(const Function &function)
{
bool ElfAnalyzer::isLoopHeavyFunction(const Function &function) const
{
std::vector<Instruction> instructions = decodeFunction(function);
int loopCount = 0;
for (size_t i = 0; i < instructions.size(); i++)
for (const auto & inst : instructions)
{
const auto &inst = instructions[i];
if (inst.isBranch)
{
int32_t offset = static_cast<int16_t>(inst.immediate) << 2;
@@ -1790,9 +1783,10 @@ namespace ps2recomp
int32_t offset = static_cast<int16_t>(inst.immediate) << 2;
return currentAddr + 4 + offset;
}
else if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL)
if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL)
{
return (currentAddr & 0xF0000000) | (inst.target << 2);
return (currentAddr & 0xF0000000) | (inst.target << 2);
}
return currentAddr + 4;
@@ -1,7 +1,6 @@
#ifndef PS2RECOMP_CODE_GENERATOR_H
#define PS2RECOMP_CODE_GENERATOR_H
#include "ps2recomp/types.h"
#include <string>
#include <vector>
#include <map>
@@ -10,12 +9,17 @@
namespace ps2recomp
{
extern const std::unordered_set<std::string> kKeywords;
struct JumpTableEntry;
struct Instruction;
struct Function;
struct Symbol;
extern const std::unordered_set<std::string> kKeywords;
class CodeGenerator
{
public:
CodeGenerator(const std::vector<Symbol> &symbols);
explicit CodeGenerator(const std::vector<Symbol> &symbols);
~CodeGenerator();
struct BootstrapInfo
@@ -10,11 +10,11 @@ namespace ps2recomp
class ConfigManager
{
public:
ConfigManager(const std::string &configPath);
explicit ConfigManager(const std::string &configPath);
~ConfigManager();
RecompilerConfig loadConfig();
void saveConfig(const RecompilerConfig &config);
RecompilerConfig loadConfig() const;
void saveConfig(const RecompilerConfig &config) const;
private:
std::string m_configPath;
+10 -7
View File
@@ -1,7 +1,6 @@
#ifndef PS2RECOMP_ELF_PARSER_H
#define PS2RECOMP_ELF_PARSER_H
#include "ps2recomp/types.h"
#include <elfio/elfio.hpp>
#include <string>
#include <vector>
@@ -9,16 +8,20 @@
namespace ps2recomp
{
struct Relocation;
struct Section;
struct Function;
struct Symbol;
class ElfParser
class ElfParser
{
public:
ElfParser(const std::string &filePath);
explicit ElfParser(const std::string &filePath);
~ElfParser();
bool parse();
std::vector<Function> extractFunctions();
std::vector<Function> extractFunctions() const;
std::vector<Symbol> extractSymbols();
std::vector<Section> getSections();
std::vector<Relocation> getRelocations();
@@ -26,9 +29,9 @@ namespace ps2recomp
// Helper methods
bool isValidAddress(uint32_t address) const;
uint32_t readWord(uint32_t address) const;
uint8_t *getSectionData(const std::string &sectionName);
uint32_t getSectionAddress(const std::string &sectionName);
uint32_t getSectionSize(const std::string &sectionName);
uint8_t *getSectionData(const std::string &sectionName) const;
uint32_t getSectionAddress(const std::string &sectionName) const;
uint32_t getSectionSize(const std::string &sectionName) const;
uint32_t getEntryPoint() const;
private:
@@ -1,11 +1,8 @@
#ifndef PS2RECOMP_PS2_RECOMPILER_H
#define PS2RECOMP_PS2_RECOMPILER_H
#include "ps2recomp/types.h"
#include "ps2recomp/elf_parser.h"
#include "ps2recomp/r5900_decoder.h"
#include "ps2recomp/code_generator.h"
#include "ps2recomp/config_manager.h"
#include "code_generator.h"
#include "config_manager.h"
#include <string>
#include <vector>
#include <unordered_map>
@@ -14,12 +11,14 @@
namespace ps2recomp
{
class R5900Decoder;
class ElfParser;
class PS2Recompiler
class PS2Recompiler
{
public:
PS2Recompiler(const std::string &configPath);
~PS2Recompiler() = default;
explicit PS2Recompiler(const std::string &configPath);
~PS2Recompiler();
bool initialize();
bool recompile();
@@ -48,7 +47,7 @@ namespace ps2recomp
void discoverAdditionalEntryPoints();
bool shouldSkipFunction(const std::string &name) const;
bool isStubFunction(const std::string &name) const;
std::string generateRuntimeHeader();
std::string generateRuntimeHeader() const;
bool generateFunctionHeader();
bool generateStubHeader();
bool writeToFile(const std::string &path, const std::string &content);
+1 -1
View File
@@ -14,7 +14,7 @@ namespace ps2recomp
R5900Decoder();
~R5900Decoder();
Instruction decodeInstruction(uint32_t address, uint32_t rawInstruction);
Instruction decodeInstruction(uint32_t address, uint32_t rawInstruction) const;
bool isBranchInstruction(const Instruction &inst) const;
bool isJumpInstruction(const Instruction &inst) const;
-2
View File
@@ -152,8 +152,6 @@ namespace ps2recomp
JumpTable jumpTable;
};
using CFG = std::unordered_map<uint32_t, CFGNode>;
// Function call
struct FunctionCall
{
+1
View File
@@ -1,5 +1,6 @@
#include "ps2recomp/code_generator.h"
#include "ps2recomp/instructions.h"
#include "ps2recomp/types.h"
#include <fmt/format.h>
#include <sstream>
#include <algorithm>
+7 -7
View File
@@ -14,8 +14,8 @@ namespace ps2recomp
ConfigManager::~ConfigManager() = default;
RecompilerConfig ConfigManager::loadConfig()
{
RecompilerConfig ConfigManager::loadConfig() const
{
RecompilerConfig config;
try
@@ -58,8 +58,8 @@ namespace ps2recomp
return config;
}
void ConfigManager::saveConfig(const RecompilerConfig &config)
{
void ConfigManager::saveConfig(const RecompilerConfig &config) const
{
toml::value data;
toml::table general;
@@ -77,11 +77,11 @@ namespace ps2recomp
toml::table patches;
toml::array instPatches;
for (const auto &patch : config.patches)
for (const auto & [addr, value] : config.patches)
{
toml::table p;
p["address"] = "0x" + std::to_string(patch.first);
p["value"] = patch.second;
p["address"] = "0x" + std::to_string(addr);
p["value"] = value;
instPatches.push_back(p);
}
patches["instructions"] = instPatches;
+9 -8
View File
@@ -1,4 +1,5 @@
#include "ps2recomp/elf_parser.h"
#include "ps2recomp/types.h"
#include <iostream>
#include <stdexcept>
@@ -21,8 +22,8 @@ namespace ps2recomp
!(section->get_flags() & ELFIO::SHF_EXECINSTR);
}
std::vector<Function> ElfParser::extractFunctions()
{
std::vector<Function> ElfParser::extractFunctions() const
{
std::vector<Function> functions;
for (const auto &symbol : m_symbols)
@@ -92,8 +93,8 @@ namespace ps2recomp
throw std::runtime_error("Invalid address for readWord: " + std::to_string(address));
}
uint8_t *ElfParser::getSectionData(const std::string &sectionName)
{
uint8_t *ElfParser::getSectionData(const std::string &sectionName) const
{
for (const auto &section : m_sections)
{
if (section.name == sectionName)
@@ -105,8 +106,8 @@ namespace ps2recomp
return nullptr;
}
uint32_t ElfParser::getSectionAddress(const std::string &sectionName)
{
uint32_t ElfParser::getSectionAddress(const std::string &sectionName) const
{
for (const auto &section : m_sections)
{
if (section.name == sectionName)
@@ -118,8 +119,8 @@ namespace ps2recomp
return 0;
}
uint32_t ElfParser::getSectionSize(const std::string &sectionName)
{
uint32_t ElfParser::getSectionSize(const std::string &sectionName) const
{
for (const auto &section : m_sections)
{
if (section.name == sectionName)
+7 -2
View File
@@ -1,5 +1,8 @@
#include "ps2recomp/ps2_recompiler.h"
#include "ps2recomp/instructions.h"
#include "ps2recomp/types.h"
#include "ps2recomp/elf_parser.h"
#include "ps2recomp/r5900_decoder.h"
#include "ps2_runtime_calls.h"
#include <iostream>
#include <fstream>
@@ -44,6 +47,8 @@ namespace ps2recomp
{
}
PS2Recompiler::~PS2Recompiler() = default;
bool PS2Recompiler::initialize()
{
try
@@ -672,8 +677,8 @@ namespace ps2recomp
return ps2_runtime_calls::isStubName(name);
}
std::string PS2Recompiler::generateRuntimeHeader()
{
std::string PS2Recompiler::generateRuntimeHeader() const
{
return m_codeGenerator->generateMacroHeader();
}
+2 -2
View File
@@ -12,8 +12,8 @@ namespace ps2recomp
{
}
Instruction R5900Decoder::decodeInstruction(uint32_t address, uint32_t rawInstruction)
{
Instruction R5900Decoder::decodeInstruction(uint32_t address, uint32_t rawInstruction) const
{
Instruction inst;
inst.address = address;
+1
View File
@@ -1,6 +1,7 @@
#include "MiniTest.h"
#include "ps2recomp/code_generator.h"
#include "ps2recomp/instructions.h"
#include "ps2recomp/types.h"
using namespace ps2recomp;