mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-30 18:16:53 -04:00
refactor: refactor VU1 (#191)
* feat: implement fix and changes based on dark cloud report fix: fix GS AFAIL for RGB/alpha/Z, ZMSK fix: fix VU1 flags mask and pipeline fix: small VU1 cache fix feat: __ct__, __sinit_ are not sillent stubs anymore * feat: fix song JP pulling * feat: sound update for lotR * feat: prevent guest execution to be very slow * fix: small gs size bug * feat: refactor VU fix: fix cliping and other issues on gs fix: fix wrong vu0 register on recompiler * fix fix ACC scheduler stall feat: remove unused test fix: .fix overflow e underflow on FMAC * feat: small setting for windows test
This commit is contained in:
@@ -155,6 +155,102 @@ static bool writeMinimalMipsElfWithJalFallbackTarget(const std::filesystem::path
|
||||
return writer.save(elfPath.string());
|
||||
}
|
||||
|
||||
static bool writeMinimalMipsElfWithInitializer(const std::filesystem::path &elfPath,
|
||||
const std::string &functionName,
|
||||
uint32_t initializerTarget)
|
||||
{
|
||||
ELFIO::elfio writer;
|
||||
writer.create(ELFIO::ELFCLASS32, ELFIO::ELFDATA2LSB);
|
||||
writer.set_os_abi(ELFIO::ELFOSABI_NONE);
|
||||
writer.set_type(ELFIO::ET_EXEC);
|
||||
writer.set_machine(ELFIO::EM_MIPS);
|
||||
writer.set_entry(0x00100000u);
|
||||
|
||||
ELFIO::section *text = writer.sections.add(".text");
|
||||
text->set_type(ELFIO::SHT_PROGBITS);
|
||||
text->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR);
|
||||
text->set_addr_align(4);
|
||||
text->set_address(0x00100000u);
|
||||
const std::array<uint32_t, 2> textWords = {
|
||||
0x03E00008u, // jr $ra
|
||||
0x00000000u, // nop
|
||||
};
|
||||
text->set_data(reinterpret_cast<const char *>(textWords.data()),
|
||||
static_cast<ELFIO::Elf_Word>(textWords.size() * sizeof(uint32_t)));
|
||||
|
||||
ELFIO::section *ctors = writer.sections.add(".ctors");
|
||||
ctors->set_type(ELFIO::SHT_PROGBITS);
|
||||
ctors->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_WRITE);
|
||||
ctors->set_addr_align(4);
|
||||
ctors->set_address(0x00200000u);
|
||||
ctors->set_data(reinterpret_cast<const char *>(&initializerTarget),
|
||||
static_cast<ELFIO::Elf_Word>(sizeof(initializerTarget)));
|
||||
|
||||
ELFIO::section *strtab = writer.sections.add(".strtab");
|
||||
strtab->set_type(ELFIO::SHT_STRTAB);
|
||||
strtab->set_addr_align(1);
|
||||
|
||||
ELFIO::section *symtab = writer.sections.add(".symtab");
|
||||
symtab->set_type(ELFIO::SHT_SYMTAB);
|
||||
symtab->set_info(1);
|
||||
symtab->set_link(strtab->get_index());
|
||||
symtab->set_addr_align(4);
|
||||
symtab->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_SYMTAB));
|
||||
|
||||
ELFIO::symbol_section_accessor symbols(writer, symtab);
|
||||
ELFIO::string_section_accessor strings(strtab);
|
||||
symbols.add_symbol(strings, "", 0, 0,
|
||||
ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF);
|
||||
symbols.add_symbol(strings, functionName.c_str(), text->get_address(), text->get_size(),
|
||||
ELFIO::STB_GLOBAL, ELFIO::STT_FUNC, 0, text->get_index());
|
||||
|
||||
ELFIO::segment *textSegment = writer.segments.add();
|
||||
textSegment->set_type(ELFIO::PT_LOAD);
|
||||
textSegment->set_flags(ELFIO::PF_R | ELFIO::PF_X);
|
||||
textSegment->set_align(0x1000);
|
||||
textSegment->add_section_index(text->get_index(), text->get_addr_align());
|
||||
|
||||
ELFIO::segment *dataSegment = writer.segments.add();
|
||||
dataSegment->set_type(ELFIO::PT_LOAD);
|
||||
dataSegment->set_flags(ELFIO::PF_R | ELFIO::PF_W);
|
||||
dataSegment->set_align(0x1000);
|
||||
dataSegment->add_section_index(ctors->get_index(), ctors->get_addr_align());
|
||||
|
||||
return writer.save(elfPath.string());
|
||||
}
|
||||
|
||||
static bool writeRecompilerTestConfig(const std::filesystem::path &configPath,
|
||||
const std::filesystem::path &elfPath,
|
||||
const std::filesystem::path &outputPath,
|
||||
const std::vector<std::string> &skip,
|
||||
const std::vector<std::string> &stubs = {})
|
||||
{
|
||||
std::ofstream config(configPath);
|
||||
if (!config)
|
||||
return false;
|
||||
|
||||
config << "[general]\n";
|
||||
config << "input = \"" << elfPath.generic_string() << "\"\n";
|
||||
config << "output = \"" << outputPath.generic_string() << "\"\n";
|
||||
config << "skip = [";
|
||||
for (size_t i = 0; i < skip.size(); ++i)
|
||||
{
|
||||
if (i != 0u)
|
||||
config << ", ";
|
||||
config << '"' << skip[i] << '"';
|
||||
}
|
||||
config << "]\n";
|
||||
config << "stubs = [";
|
||||
for (size_t i = 0; i < stubs.size(); ++i)
|
||||
{
|
||||
if (i != 0u)
|
||||
config << ", ";
|
||||
config << '"' << stubs[i] << '"';
|
||||
}
|
||||
config << "]\n";
|
||||
return static_cast<bool>(config);
|
||||
}
|
||||
|
||||
void register_ps2_recompiler_tests()
|
||||
{
|
||||
MiniTest::Case("PS2Recompiler", [](TestCase &tc)
|
||||
@@ -890,6 +986,98 @@ void register_ps2_recompiler_tests()
|
||||
"__sbprintf should be left for recompilation");
|
||||
});
|
||||
|
||||
tc.Run("initializer skips fall back to guest recompilation", [](TestCase &t) {
|
||||
const std::string uniqueSuffix =
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
|
||||
const std::filesystem::path tempRoot =
|
||||
std::filesystem::temp_directory_path() / ("ps2recomp-initializer-" + uniqueSuffix);
|
||||
const std::filesystem::path elfPath = tempRoot / "initializer.elf";
|
||||
const std::filesystem::path configPath = tempRoot / "initializer.toml";
|
||||
const std::filesystem::path outputPath = tempRoot / "output";
|
||||
std::filesystem::create_directories(tempRoot);
|
||||
|
||||
const bool elfWritten =
|
||||
writeMinimalMipsElfWithInitializer(elfPath, "__sinit_test.cpp", 0x00100000u);
|
||||
const bool configWritten =
|
||||
writeRecompilerTestConfig(configPath, elfPath, outputPath, {"__sinit_test.cpp"});
|
||||
t.IsTrue(elfWritten && configWritten,
|
||||
"initializer regression inputs should be generated");
|
||||
|
||||
if (elfWritten && configWritten)
|
||||
{
|
||||
PS2Recompiler recompiler(configPath.string());
|
||||
t.IsTrue(recompiler.initialize(),
|
||||
"initializer regression config should initialize");
|
||||
t.IsTrue(recompiler.recompile(),
|
||||
"a decodable skipped initializer should use guest fallback");
|
||||
const RecompilerReporter::Counters &counters = recompiler.reportCounters();
|
||||
t.Equals(counters.correctnessCriticalGuestFallbacks, static_cast<size_t>(1u),
|
||||
"the ignored initializer skip should be reported");
|
||||
t.Equals(counters.correctnessCriticalFailures, static_cast<size_t>(0u),
|
||||
"guest fallback should avoid a correctness-critical failure");
|
||||
t.Equals(counters.functionsSkipped, static_cast<size_t>(0u),
|
||||
"the initializer should not remain skipped");
|
||||
t.Equals(counters.functionsRecompiled, static_cast<size_t>(1u),
|
||||
"the original initializer body should be recompiled");
|
||||
}
|
||||
|
||||
std::error_code removeError;
|
||||
std::filesystem::remove_all(tempRoot, removeError);
|
||||
});
|
||||
|
||||
tc.Run("missing constructor-table targets fail recompilation", [](TestCase &t) {
|
||||
const std::string uniqueSuffix =
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
|
||||
const std::filesystem::path tempRoot =
|
||||
std::filesystem::temp_directory_path() / ("ps2recomp-missing-initializer-" + uniqueSuffix);
|
||||
const std::filesystem::path elfPath = tempRoot / "initializer.elf";
|
||||
const std::filesystem::path configPath = tempRoot / "initializer.toml";
|
||||
const std::filesystem::path outputPath = tempRoot / "output";
|
||||
std::filesystem::create_directories(tempRoot);
|
||||
|
||||
const bool elfWritten =
|
||||
writeMinimalMipsElfWithInitializer(elfPath, "ordinary_entry", 0x00100040u);
|
||||
const bool configWritten =
|
||||
writeRecompilerTestConfig(configPath, elfPath, outputPath, {});
|
||||
t.IsTrue(elfWritten && configWritten,
|
||||
"missing-initializer regression inputs should be generated");
|
||||
|
||||
if (elfWritten && configWritten)
|
||||
{
|
||||
{
|
||||
PS2Recompiler recompiler(configPath.string());
|
||||
t.IsTrue(recompiler.initialize(),
|
||||
"missing-initializer regression config should initialize");
|
||||
t.IsFalse(recompiler.recompile(),
|
||||
"an unresolved .ctors target should be correctness-fatal");
|
||||
t.Equals(recompiler.reportCounters().correctnessCriticalFailures,
|
||||
static_cast<size_t>(1u),
|
||||
"the unresolved constructor target should appear in the report");
|
||||
}
|
||||
|
||||
const bool overrideWritten =
|
||||
writeRecompilerTestConfig(
|
||||
configPath, elfPath, outputPath, {},
|
||||
{"memclr@0x00100040"});
|
||||
t.IsTrue(overrideWritten,
|
||||
"manual initializer override config should be generated");
|
||||
if (overrideWritten)
|
||||
{
|
||||
PS2Recompiler overridden(configPath.string());
|
||||
t.IsTrue(overridden.initialize(),
|
||||
"manual initializer override should initialize");
|
||||
t.IsTrue(overridden.recompile(),
|
||||
"a resolved address-bound handler should satisfy the constructor target");
|
||||
t.Equals(overridden.reportCounters().functionsStubbed,
|
||||
static_cast<size_t>(1u),
|
||||
"the resolved manual initializer should be emitted as a stub binding");
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code removeError;
|
||||
std::filesystem::remove_all(tempRoot, removeError);
|
||||
});
|
||||
|
||||
tc.Run("respect max length for .cpp filenames", [](TestCase& t) {
|
||||
|
||||
t.IsTrue(PS2Recompiler::ClampFilenameLength("ReallyLongFunctionNameReallyLongFunctionNameReallyLongFunctionName_0x12345678",".cpp",50).length() <= 50,"Function name must be max 50 characters");
|
||||
|
||||
Reference in New Issue
Block a user