Skip to content
Merged
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 docs/CURRENT_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path>` 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).
Expand Down
8 changes: 8 additions & 0 deletions docs/VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <fresh-dir> 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 <fresh-dir> 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.
Expand Down
75 changes: 51 additions & 24 deletions recomp/PSPRecomp/tools/codegen_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <fstream>
#include <iostream>
#include <iomanip>
#include <iterator>
#include <map>
#include <regex>
#include <string_view>
Expand Down Expand Up @@ -1498,70 +1499,96 @@ 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
// giant translation units. Dynamic-size matrix helpers remain untouched.
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;
}

Expand Down