From 096c72daf86617ecc4de78d35da3d1b713cc3654 Mon Sep 17 00:00:00 2001 From: endercodezz Date: Thu, 5 Nov 2026 00:38:12 +0200 Subject: [PATCH] perf(recomp): prefilter VFPU rewrite candidates Search for the literal helper prefix before applying each ordered regex at a candidate offset. Verified: 240 output hashes matched per mode, Release CTest 27/27, stable bootstrap and two matching baseline replays at PC 0x08A87150. --- docs/CURRENT_STATE.md | 5 ++ docs/VERIFICATION.md | 8 +++ recomp/PSPRecomp/tools/codegen_main.cpp | 75 +++++++++++++++++-------- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/docs/CURRENT_STATE.md b/docs/CURRENT_STATE.md index 776be01d..6c66064d 100644 --- a/docs/CURRENT_STATE.md +++ b/docs/CURRENT_STATE.md @@ -2,6 +2,11 @@ This is the current source of truth, newest entries first. Evidence concerns ULUS-10512; each section says whether it was measured on the PC runner, in Azahar or on a real New 3DS. +## Production AUTO generator filters VFPU regex candidates (2026-11-05) + +- [VERIFIED] On Windows Release, fresh diagnostic generation took 9.26 s with the C++ literal filter and 40.31 s with the pre-change generator (one run each). The release-mode output also matched. In each mode all 240 files matched the original by filename and SHA-256. The incremental build rewrote 0 of 237 generated units. See the [verification record](VERIFICATION.md#production-vfpu-prefilter-38-2026-11-05). +- [VERIFIED] Release build, `ctest --test-dir build -j6 --output-on-failure` (27/27) and `--verify-bootstrap --max-dispatches 100000` passed. Two runs with `--run-until-blocker --max-dispatches 20000000 --ms0 .tmp/issue27/ms0 --mods .tmp/issue27/mods --dump-events ` reached the dispatch budget at `0x08A87150`. Both event dumps match the pre-change replay at SHA-256 `1a5f566002148933522c869ed3febb9eae3d652764501d20e8437b3b1898e605`. Logs and dumps: `.tmp/vfpu-production/`. + ## Host recompiler experiment: typed aligned-memory lowering (2026-11-04) - [VERIFIED] On Windows, three runs each of the instrumented baseline, direct C++ emission and Rust emission produced the same 240 files as the unmodified PSPRecomp. The baseline spent a median 2.31 s on the aligned-memory pass and 28.41 s on VFPU lowering out of 37.26 s wall. See the [verification record](VERIFICATION.md#psprecomp-auto-lowering-benchmark-34-2026-11-04). diff --git a/docs/VERIFICATION.md b/docs/VERIFICATION.md index d0a9a5ce..4edad987 100644 --- a/docs/VERIFICATION.md +++ b/docs/VERIFICATION.md @@ -16,6 +16,14 @@ Current execution and coverage measurements: [CURRENT_STATE.md](CURRENT_STATE.md --- +## Production VFPU prefilter (#38, 2026-11-05) + +- [VERIFIED] `recomp/PSPRecomp/tools/codegen_main.cpp::replace_vfpu_calls` uses a literal search before matching the existing regex at each candidate. `lower_constant_vfpu_accesses` retains the order, regex patterns and replacement formats from `b35d2cc`. +- [VERIFIED] Windows Release build (`cmake --build build --config Release --parallel 6`) generated 237 units and rewrote zero in its existing output directory. Independently, `psp_recomp profiles/p3p/game/eboot.elf --auto 0x08804000 16384 build/generated/patches.txt` took 9.26 s with the filter and 40.31 s before it. A fresh run with `--no-transfer-records` took 7.50 s. Each of the two modes matched its own original 240-file manifest by filename and SHA-256. `tools/verify_aot_layout.py 237 16384` passed for both modes. Timings, original diagnostic output, fresh outputs, manifests and build log are in `.tmp/vfpu-production/`. The earlier unmodified release output used for comparison is `.tmp/host-language-research/a`. +- [VERIFIED] SHA-256 of the diagnostic manifest (`diagnostic-sha256.txt`) is `0f3b7406d056ad0e6e268f46bacca6f12c064c62a08a6acaadc4956a499b6ff1`. SHA-256 of the release manifest (`release-sha256.txt`) is `6ee69504bda0cb7f4ba46326564092ac108b0db0e9ebb91248e205344ee40861`. + +--- + ## Thread-entry frontier provenance micro-sprint - [VERIFIED] The `SceWaveMain` UID 5 initial context switch installs its stored entry `0x08B64FE0` without a guest JAL to that address. Evidence: `core/src/hle/threadman.cpp::start_thread`, `platform/pc/telemetry.hpp::blocker_type`, `.tmp/thread-entry-before.json` and `.tmp/thread-entry-check/report.json`. The new `thread_entry_transfer` record is one-shot and is invalidated by guest execution/transfer or another switch. diff --git a/recomp/PSPRecomp/tools/codegen_main.cpp b/recomp/PSPRecomp/tools/codegen_main.cpp index 186f56d7..e170632b 100644 --- a/recomp/PSPRecomp/tools/codegen_main.cpp +++ b/recomp/PSPRecomp/tools/codegen_main.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1498,6 +1499,32 @@ std::string lower_constant_fpr_accesses(std::string text) { return lowered; } +// P3P3DS: find the literal helper name first so each regex only examines candidate +// calls instead of searching the full generated unit at every position. +std::string replace_vfpu_calls(std::string text, std::string_view prefix, + const std::regex &pattern, std::string_view replacement) { + std::string result; + std::smatch match; + std::size_t copied = 0; + std::size_t search = 0; + while ((search = text.find(prefix, search)) != std::string::npos) { + if (!std::regex_search(text.cbegin() + search, text.cend(), match, pattern, + std::regex_constants::match_continuous)) { + search += prefix.size(); + continue; + } + if (copied == 0) result.reserve(text.size()); + result.append(text, copied, search - copied); + match.format(std::back_inserter(result), replacement.data(), + replacement.data() + replacement.size()); + search += match.length(); + copied = search; + } + if (copied == 0) return text; + result.append(text, copied, std::string::npos); + return result; +} + // VFPU operands in automatic AOT are encoded literals. Convert the // generic lane/prefix helpers into compile-time templates so row/column mapping, // scalar-vs-control selection and 1..4-lane loops disappear before MSVC sees the @@ -1505,63 +1532,63 @@ std::string lower_constant_fpr_accesses(std::string text) { std::string lower_constant_vfpu_accesses(std::string text) { const std::regex read_prefix( R"(ctx\.read_vfpu_vector_with_source_prefix\(([A-Za-z0-9_]+), ([0-9]+)u, ([1-4])u, ([01])u\);)" ); - text = std::regex_replace(text, read_prefix, - "ctx.read_vfpu_vector_with_source_prefix_ct<$2u, $3u, $4u>($1);"); + text = replace_vfpu_calls(std::move(text), "ctx.read_vfpu_vector_with_source_prefix(", + read_prefix, "ctx.read_vfpu_vector_with_source_prefix_ct<$2u, $3u, $4u>($1);"); const std::regex read_plain( R"(ctx\.read_vfpu_vector\(([A-Za-z0-9_]+), ([0-9]+)u, ([1-4])u\);)" ); - text = std::regex_replace(text, read_plain, - "ctx.read_vfpu_vector_ct<$2u, $3u>($1);"); + text = replace_vfpu_calls(std::move(text), "ctx.read_vfpu_vector(", + read_plain, "ctx.read_vfpu_vector_ct<$2u, $3u>($1);"); const std::regex write_prefix( R"(ctx\.write_vfpu_vector_with_destination_prefix\(([A-Za-z0-9_]+), ([0-9]+)u, ([1-4])u\);)" ); - text = std::regex_replace(text, write_prefix, - "ctx.write_vfpu_vector_with_destination_prefix_ct<$2u, $3u>($1);"); + text = replace_vfpu_calls(std::move(text), "ctx.write_vfpu_vector_with_destination_prefix(", + write_prefix, "ctx.write_vfpu_vector_with_destination_prefix_ct<$2u, $3u>($1);"); const std::regex write_plain( R"(ctx\.write_vfpu_vector\(([A-Za-z0-9_]+), ([0-9]+)u, ([1-4])u\);)" ); - text = std::regex_replace(text, write_plain, - "ctx.write_vfpu_vector_ct<$2u, $3u>($1);"); + text = replace_vfpu_calls(std::move(text), "ctx.write_vfpu_vector(", + write_plain, "ctx.write_vfpu_vector_ct<$2u, $3u>($1);"); const std::regex source_prefix_plain( R"(ctx\.apply_vfpu_source_prefix\(([A-Za-z0-9_]+), ([1-4])u, ([01])u\);)" ); - text = std::regex_replace(text, source_prefix_plain, - "ctx.apply_vfpu_source_prefix_ct<$2u, $3u>($1);"); + text = replace_vfpu_calls(std::move(text), "ctx.apply_vfpu_source_prefix(", + source_prefix_plain, "ctx.apply_vfpu_source_prefix_ct<$2u, $3u>($1);"); const std::regex fpu_to_word( R"(ctx\.fpu_float_to_word\(([^,\n]+), ([0-3])u\))" ); - text = std::regex_replace(text, fpu_to_word, - "ctx.fpu_float_to_word_ct<$2u>($1)"); + text = replace_vfpu_calls(std::move(text), "ctx.fpu_float_to_word(", + fpu_to_word, "ctx.fpu_float_to_word_ct<$2u>($1)"); const std::regex vcmp( R"(ctx\.execute_vfpu_vcmp\(([0-9]+)u, ([0-9]+)u, ([1-4])u, ([0-9]+)u\);)" ); - text = std::regex_replace(text, vcmp, - "ctx.execute_vfpu_vcmp_ct<$1u, $2u, $3u, $4u>();"); + text = replace_vfpu_calls(std::move(text), "ctx.execute_vfpu_vcmp(", + vcmp, "ctx.execute_vfpu_vcmp_ct<$1u, $2u, $3u, $4u>();"); const std::regex vcmov( R"(ctx\.execute_vfpu_vcmov\(([0-9]+)u, ([0-9]+)u, ([1-4])u, ([0-7])u, (true|false)\);)" ); - text = std::regex_replace(text, vcmov, - "ctx.execute_vfpu_vcmov_ct<$1u, $2u, $3u, $4u, $5>();"); + text = replace_vfpu_calls(std::move(text), "ctx.execute_vfpu_vcmov(", + vcmov, "ctx.execute_vfpu_vcmov_ct<$1u, $2u, $3u, $4u, $5>();"); const std::regex vdot( R"(ctx\.execute_vfpu_vdot\(([0-9]+)u, ([0-9]+)u, ([0-9]+)u, ([1-4])u\);)" ); - text = std::regex_replace(text, vdot, - "ctx.execute_vfpu_vdot_ct<$1u, $2u, $3u, $4u>();"); + text = replace_vfpu_calls(std::move(text), "ctx.execute_vfpu_vdot(", + vdot, "ctx.execute_vfpu_vdot_ct<$1u, $2u, $3u, $4u>();"); const std::regex vscl( R"(ctx\.execute_vfpu_vscl\(([0-9]+)u, ([0-9]+)u, ([0-9]+)u, ([1-4])u\);)" ); - text = std::regex_replace(text, vscl, - "ctx.execute_vfpu_vscl_ct<$1u, $2u, $3u, $4u>();"); + text = replace_vfpu_calls(std::move(text), "ctx.execute_vfpu_vscl(", + vscl, "ctx.execute_vfpu_vscl_ct<$1u, $2u, $3u, $4u>();"); const std::regex scalar_read(R"(ctx\.vfpu_scalar_bits\(([0-9]+)u\))"); - text = std::regex_replace(text, scalar_read, - "ctx.vfpu_scalar_bits_ct<$1u>()"); + text = replace_vfpu_calls(std::move(text), "ctx.vfpu_scalar_bits(", + scalar_read, "ctx.vfpu_scalar_bits_ct<$1u>()"); // set_vfpu_scalar_bits second arguments in generated code stay on one line. const std::regex scalar_write( R"(ctx\.set_vfpu_scalar_bits\(([0-9]+)u, ([^;\n]+)\);)" ); - text = std::regex_replace(text, scalar_write, - "ctx.set_vfpu_scalar_bits_ct<$1u>($2);"); + text = replace_vfpu_calls(std::move(text), "ctx.set_vfpu_scalar_bits(", + scalar_write, "ctx.set_vfpu_scalar_bits_ct<$1u>($2);"); return text; }