otimizações round 10

otimizações round 10
This commit is contained in:
Jessica_Natalia
2026-08-17 17:07:10 -03:00
parent b5a9108373
commit c471e96844
6 changed files with 403 additions and 76 deletions
+250 -59
View File
@@ -7,6 +7,15 @@
#include <cstring>
#include <iostream>
#include <limits>
#include <string_view>
#if defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
namespace psprecomp {
@@ -55,6 +64,14 @@ bool overlaps_watch(std::uint32_t address, std::size_t length) {
static_cast<std::uint64_t>(canonical_watch) < first_end;
}
bool environment_enabled_default_on(const char *name) noexcept {
const char *value = std::getenv(name);
if (value == nullptr || *value == '\0') return true;
const std::string_view text(value);
return !(text == "0" || text == "off" || text == "OFF" ||
text == "false" || text == "FALSE" || text == "no" || text == "NO");
}
void log_write_watch(std::uint32_t address, std::size_t length, const char *operation,
std::uint64_t old_value, std::uint64_t new_value) {
if (!overlaps_watch(address, length)) return;
@@ -69,21 +86,182 @@ void log_write_watch(std::uint32_t address, std::size_t length, const char *oper
}
}
bool GuestMemory::initialize_direct_fastmem(std::uint32_t size_bytes) noexcept {
direct_fastmem_base_ = nullptr;
fastmem_view_count_ = 0u;
fastmem_views_.fill(nullptr);
fastmem_ram_mapping_ = nullptr;
fastmem_vram_mapping_ = nullptr;
if (!environment_enabled_default_on("PSPRECOMP_AOT_DIRECT_FASTMEM"))
return false;
#if defined(_WIN32) && INTPTR_MAX > INT32_MAX
HANDLE ram_mapping = CreateFileMappingW(
INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0u,
static_cast<DWORD>(size_bytes), nullptr);
if (ram_mapping == nullptr) return false;
HANDLE vram_mapping = CreateFileMappingW(
INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0u,
static_cast<DWORD>(kVramSize), nullptr);
if (vram_mapping == nullptr) {
CloseHandle(ram_mapping);
return false;
}
// Map the exact alias model used by canonical(address): the top three bits
// are ignored, so every 0x20000000 mirror must resolve to the same physical
// bytes. VRAM additionally has four 2 MiB mirrors inside its 8 MiB window.
// No 4 GiB reservation is needed; only the 40 live sparse views consume VA.
const auto clear_attempt = [&]() noexcept {
for (std::size_t i = 0u; i < fastmem_view_count_; ++i) {
if (fastmem_views_[i] != nullptr) UnmapViewOfFile(fastmem_views_[i]);
fastmem_views_[i] = nullptr;
}
fastmem_view_count_ = 0u;
};
const auto map_exact = [&](HANDLE mapping, std::uintptr_t host_address,
std::size_t bytes) noexcept -> bool {
void *const requested = reinterpret_cast<void *>(host_address);
void *const view = MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0u, 0u,
bytes, requested);
if (view != requested) {
if (view != nullptr) UnmapViewOfFile(view);
return false;
}
if (fastmem_view_count_ >= fastmem_views_.size()) {
UnmapViewOfFile(view);
return false;
}
fastmem_views_[fastmem_view_count_++] = view;
return true;
};
// High, 64 KiB-aligned bases keep the sparse PSP 4 GiB window away from
// ordinary executable/heap allocations. Try several independent 1 TiB
// slots so ASLR or another mapping cannot make fastmem boot-critical.
constexpr std::uintptr_t kFirstCandidate = UINT64_C(0x0000040000000000);
constexpr std::uintptr_t kCandidateStep = UINT64_C(0x0000010000000000);
constexpr std::size_t kCandidateCount = 24u;
bool mapped = false;
for (std::size_t attempt = 0u; attempt < kCandidateCount && !mapped; ++attempt) {
clear_attempt();
const std::uintptr_t base = kFirstCandidate + kCandidateStep * attempt;
bool ok = true;
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
const std::uint32_t guest = kPhysicalBase + alias * 0x20000000u;
ok = map_exact(ram_mapping, base + guest, size_bytes);
}
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
for (std::uint32_t mirror = 0u; mirror < kVramMirrorCount && ok; ++mirror) {
const std::uint32_t guest = kVramPhysicalBase + mirror * kVramSize +
alias * 0x20000000u;
ok = map_exact(vram_mapping, base + guest, kVramSize);
}
}
if (ok) {
// Verify that the OS really gave us coherent aliases before any
// guest data is loaded. This turns a broken/partial mapping into a
// clean fallback rather than latent guest-memory corruption.
auto *const probe_base = reinterpret_cast<std::uint8_t *>(base);
const std::uint32_t ram_probe_offset = size_bytes - 1u;
probe_base[kPhysicalBase + ram_probe_offset] = 0x5Au;
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
const std::uint32_t guest = kPhysicalBase + alias * 0x20000000u;
ok = probe_base[guest + ram_probe_offset] == 0x5Au;
}
probe_base[kPhysicalBase + ram_probe_offset] = 0u;
const std::uint32_t vram_probe_offset = kVramSize - 1u;
probe_base[kVramPhysicalBase + vram_probe_offset] = 0xA5u;
for (std::uint32_t alias = 0u; alias < 8u && ok; ++alias) {
for (std::uint32_t mirror = 0u; mirror < kVramMirrorCount && ok; ++mirror) {
const std::uint32_t guest = kVramPhysicalBase + mirror * kVramSize +
alias * 0x20000000u;
ok = probe_base[guest + vram_probe_offset] == 0xA5u;
}
}
probe_base[kVramPhysicalBase + vram_probe_offset] = 0u;
}
if (ok) {
direct_fastmem_base_ = reinterpret_cast<std::uint8_t *>(base);
mapped = true;
}
}
if (!mapped) {
clear_attempt();
CloseHandle(vram_mapping);
CloseHandle(ram_mapping);
return false;
}
fastmem_ram_mapping_ = ram_mapping;
fastmem_vram_mapping_ = vram_mapping;
return true;
#else
(void)size_bytes;
return false;
#endif
}
void GuestMemory::shutdown_direct_fastmem() noexcept {
#if defined(_WIN32) && INTPTR_MAX > INT32_MAX
for (std::size_t i = 0u; i < fastmem_view_count_; ++i) {
if (fastmem_views_[i] != nullptr) UnmapViewOfFile(fastmem_views_[i]);
fastmem_views_[i] = nullptr;
}
fastmem_view_count_ = 0u;
if (fastmem_vram_mapping_ != nullptr) {
CloseHandle(static_cast<HANDLE>(fastmem_vram_mapping_));
fastmem_vram_mapping_ = nullptr;
}
if (fastmem_ram_mapping_ != nullptr) {
CloseHandle(static_cast<HANDLE>(fastmem_ram_mapping_));
fastmem_ram_mapping_ = nullptr;
}
#endif
direct_fastmem_base_ = nullptr;
}
GuestMemory::GuestMemory(std::uint32_t size_bytes)
: vram_(kVramSize, 0u), bytes_(size_bytes, 0u), write_watch_enabled_(std::getenv("PSPRECOMP_WATCH_WRITE") != nullptr) {
: ram_size_(size_bytes),
write_watch_enabled_(std::getenv("PSPRECOMP_WATCH_WRITE") != nullptr) {
if (size_bytes != 32u * 1024u * 1024u && size_bytes != 64u * 1024u * 1024u) {
throw Error("PSP RAM size must be 32 MiB or 64 MiB");
}
// Bind the inline AOT fast paths to main RAM. bytes_ is never resized
// afterwards, and the instance is non-copyable, so this stays valid.
ram_data_ = bytes_.data();
if (initialize_direct_fastmem(size_bytes)) {
// These two aliases are backed by the same page-file sections as every
// other PSP mirror in the fastmem arena. Keeping the ordinary pointers
// on those mappings makes HLE/raw_pointer/ELF loading coherent with the
// generated AOT direct-address path without a shadow copy.
vram_data_ = direct_fastmem_base_ + kVramPhysicalBase;
ram_data_ = direct_fastmem_base_ + kPhysicalBase;
} else {
fallback_vram_.assign(kVramSize, 0u);
fallback_ram_.assign(size_bytes, 0u);
vram_data_ = fallback_vram_.data();
ram_data_ = fallback_ram_.data();
}
ram_limit8_ = size_bytes - 1u;
ram_limit16_ = size_bytes - 2u;
ram_limit32_ = size_bytes - 4u;
}
std::uint32_t GuestMemory::size() const noexcept { return static_cast<std::uint32_t>(bytes_.size()); }
std::uint32_t GuestMemory::vram_size() const noexcept { return static_cast<std::uint32_t>(vram_.size()); }
GuestMemory::~GuestMemory() {
shutdown_direct_fastmem();
}
std::uint32_t GuestMemory::size() const noexcept { return ram_size_; }
std::uint32_t GuestMemory::vram_size() const noexcept { return kVramSize; }
bool GuestMemory::is_vram_window(std::uint32_t canonical_address) const noexcept {
return canonical_address >= kVramPhysicalBase &&
@@ -99,7 +277,7 @@ bool GuestMemory::contains(std::uint32_t address, std::size_t length) const noex
const std::uint64_t end = static_cast<std::uint64_t>(c) + static_cast<std::uint64_t>(length);
if (is_vram_window(c) && end <= static_cast<std::uint64_t>(kVramPhysicalBase) + kVramAddressSpan)
return true;
if (c >= kPhysicalBase && end <= static_cast<std::uint64_t>(kPhysicalBase) + bytes_.size())
if (c >= kPhysicalBase && end <= static_cast<std::uint64_t>(kPhysicalBase) + ram_size_)
return true;
return false;
}
@@ -114,11 +292,15 @@ GuestMemory::ResolvedAddress GuestMemory::resolve(std::uint32_t address, std::si
return {Region::Ram, static_cast<std::size_t>(c - kPhysicalBase)};
}
const std::vector<std::uint8_t> &GuestMemory::region_bytes(Region region) const noexcept {
return region == Region::Vram ? vram_ : bytes_;
std::span<const std::uint8_t> GuestMemory::region_bytes(Region region) const noexcept {
return region == Region::Vram
? std::span<const std::uint8_t>(vram_data_, kVramSize)
: std::span<const std::uint8_t>(ram_data_, ram_size_);
}
std::vector<std::uint8_t> &GuestMemory::region_bytes(Region region) noexcept {
return region == Region::Vram ? vram_ : bytes_;
std::span<std::uint8_t> GuestMemory::region_bytes(Region region) noexcept {
return region == Region::Vram
? std::span<std::uint8_t>(vram_data_, kVramSize)
: std::span<std::uint8_t>(ram_data_, ram_size_);
}
// The `_slow` bodies below are the original aot_* implementations, reached only
@@ -126,9 +308,9 @@ std::vector<std::uint8_t> &GuestMemory::region_bytes(Region region) noexcept {
// an out-of-range address, a region-crossing width, or an armed write watch.
std::uint8_t GuestMemory::aot_load8_slow(std::uint32_t address) const {
const std::uint32_t c = canonical(address);
if (is_vram_window(c)) return vram_[vram_offset(c)];
if (c >= kPhysicalBase && c - kPhysicalBase < bytes_.size())
return bytes_[static_cast<std::size_t>(c - kPhysicalBase)];
if (is_vram_window(c)) return vram_data_[vram_offset(c)];
if (c >= kPhysicalBase && c - kPhysicalBase < ram_size_)
return ram_data_[static_cast<std::size_t>(c - kPhysicalBase)];
return load8(address);
}
@@ -136,34 +318,37 @@ std::uint16_t GuestMemory::aot_load16_slow(std::uint32_t address) const {
const std::uint32_t c = canonical(address);
if (is_vram_window(c)) {
const std::size_t offset = vram_offset(c);
if (offset + 2u <= vram_.size())
return static_cast<std::uint16_t>(vram_[offset]) |
static_cast<std::uint16_t>(static_cast<std::uint16_t>(vram_[offset + 1u]) << 8u);
if (offset + 2u <= static_cast<std::size_t>(kVramSize))
return static_cast<std::uint16_t>(vram_data_[offset]) |
static_cast<std::uint16_t>(static_cast<std::uint16_t>(vram_data_[offset + 1u]) << 8u);
} else if (c >= kPhysicalBase) {
const std::size_t offset = static_cast<std::size_t>(c - kPhysicalBase);
if (offset + 2u <= bytes_.size())
return static_cast<std::uint16_t>(bytes_[offset]) |
static_cast<std::uint16_t>(static_cast<std::uint16_t>(bytes_[offset + 1u]) << 8u);
if (offset + 2u <= ram_size_)
return static_cast<std::uint16_t>(ram_data_[offset]) |
static_cast<std::uint16_t>(static_cast<std::uint16_t>(ram_data_[offset + 1u]) << 8u);
}
return load16(address);
}
std::uint32_t GuestMemory::aot_load32_slow(std::uint32_t address) const {
const std::uint32_t c = canonical(address);
const std::vector<std::uint8_t> *data = nullptr;
const std::uint8_t *data = nullptr;
std::size_t data_size = 0u;
std::size_t offset = 0u;
if (is_vram_window(c)) {
data = &vram_;
data = vram_data_;
data_size = kVramSize;
offset = vram_offset(c);
} else if (c >= kPhysicalBase) {
data = &bytes_;
data = ram_data_;
data_size = ram_size_;
offset = static_cast<std::size_t>(c - kPhysicalBase);
}
if (data != nullptr && offset + 4u <= data->size()) {
return static_cast<std::uint32_t>((*data)[offset]) |
(static_cast<std::uint32_t>((*data)[offset + 1u]) << 8u) |
(static_cast<std::uint32_t>((*data)[offset + 2u]) << 16u) |
(static_cast<std::uint32_t>((*data)[offset + 3u]) << 24u);
if (data != nullptr && offset + 4u <= data_size) {
return static_cast<std::uint32_t>(data[offset]) |
(static_cast<std::uint32_t>(data[offset + 1u]) << 8u) |
(static_cast<std::uint32_t>(data[offset + 2u]) << 16u) |
(static_cast<std::uint32_t>(data[offset + 3u]) << 24u);
}
return load32(address);
}
@@ -182,9 +367,9 @@ std::uint32_t GuestMemory::aot_load_word_right(std::uint32_t address, std::uint3
void GuestMemory::aot_store8_slow(std::uint32_t address, std::uint8_t value) {
if (write_watch_enabled_) { store8(address, value); return; }
const std::uint32_t c = canonical(address);
if (is_vram_window(c)) { vram_[vram_offset(c)] = value; return; }
if (c >= kPhysicalBase && c - kPhysicalBase < bytes_.size()) {
bytes_[static_cast<std::size_t>(c - kPhysicalBase)] = value;
if (is_vram_window(c)) { vram_data_[vram_offset(c)] = value; return; }
if (c >= kPhysicalBase && c - kPhysicalBase < ram_size_) {
ram_data_[static_cast<std::size_t>(c - kPhysicalBase)] = value;
return;
}
store8(address, value);
@@ -192,13 +377,14 @@ void GuestMemory::aot_store8_slow(std::uint32_t address, std::uint8_t value) {
void GuestMemory::aot_store16_slow(std::uint32_t address, std::uint16_t value) {
if (write_watch_enabled_) { store16(address, value); return; }
const std::uint32_t c = canonical(address);
std::vector<std::uint8_t> *data = nullptr;
std::uint8_t *data = nullptr;
std::size_t data_size = 0u;
std::size_t offset = 0u;
if (is_vram_window(c)) { data = &vram_; offset = vram_offset(c); }
else if (c >= kPhysicalBase) { data = &bytes_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
if (data != nullptr && offset + 2u <= data->size()) {
(*data)[offset] = static_cast<std::uint8_t>(value & 0xFFu);
(*data)[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
if (is_vram_window(c)) { data = vram_data_; data_size = kVramSize; offset = vram_offset(c); }
else if (c >= kPhysicalBase) { data = ram_data_; data_size = ram_size_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
if (data != nullptr && offset + 2u <= data_size) {
data[offset] = static_cast<std::uint8_t>(value & 0xFFu);
data[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
return;
}
store16(address, value);
@@ -206,15 +392,16 @@ void GuestMemory::aot_store16_slow(std::uint32_t address, std::uint16_t value) {
void GuestMemory::aot_store32_slow(std::uint32_t address, std::uint32_t value) {
if (write_watch_enabled_) { store32(address, value); return; }
const std::uint32_t c = canonical(address);
std::vector<std::uint8_t> *data = nullptr;
std::uint8_t *data = nullptr;
std::size_t data_size = 0u;
std::size_t offset = 0u;
if (is_vram_window(c)) { data = &vram_; offset = vram_offset(c); }
else if (c >= kPhysicalBase) { data = &bytes_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
if (data != nullptr && offset + 4u <= data->size()) {
(*data)[offset] = static_cast<std::uint8_t>(value & 0xFFu);
(*data)[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
(*data)[offset + 2u] = static_cast<std::uint8_t>((value >> 16u) & 0xFFu);
(*data)[offset + 3u] = static_cast<std::uint8_t>((value >> 24u) & 0xFFu);
if (is_vram_window(c)) { data = vram_data_; data_size = kVramSize; offset = vram_offset(c); }
else if (c >= kPhysicalBase) { data = ram_data_; data_size = ram_size_; offset = static_cast<std::size_t>(c - kPhysicalBase); }
if (data != nullptr && offset + 4u <= data_size) {
data[offset] = static_cast<std::uint8_t>(value & 0xFFu);
data[offset + 1u] = static_cast<std::uint8_t>((value >> 8u) & 0xFFu);
data[offset + 2u] = static_cast<std::uint8_t>((value >> 16u) & 0xFFu);
data[offset + 3u] = static_cast<std::uint8_t>((value >> 24u) & 0xFFu);
return;
}
store32(address, value);
@@ -259,7 +446,7 @@ void GuestMemory::aot_copy_lz_match(std::uint32_t destination, std::uint32_t sou
return;
}
auto &data = region_bytes(destination_resolved.region);
auto data = region_bytes(destination_resolved.region);
if (destination_resolved.offset + length > data.size() ||
source_resolved.offset + length > data.size() ||
source_resolved.offset >= destination_resolved.offset) {
@@ -276,10 +463,10 @@ void GuestMemory::aot_copy_lz_match(std::uint32_t destination, std::uint32_t sou
// the already produced prefix in geometrically growing non-overlapping
// chunks. This is equivalent to the guest's forward byte loop, including
// distance=1 runs, but completes in O(log(length)) host copies.
std::size_t copied = std::min(distance, total);
std::size_t copied = (std::min)(distance, total);
std::memcpy(data.data() + destination_offset, data.data() + source_offset, copied);
while (copied < total) {
const std::size_t chunk = std::min(copied, total - copied);
const std::size_t chunk = (std::min)(copied, total - copied);
std::memcpy(data.data() + destination_offset + copied, data.data() + destination_offset, chunk);
copied += chunk;
}
@@ -296,12 +483,12 @@ const std::uint8_t *GuestMemory::raw_pointer(std::uint32_t address, std::size_t
const std::size_t offset = vram_offset(c);
// A run that would wrap past the end of the 2 MiB EDRAM image is not
// contiguous in host memory even though it is legal in guest space.
if (offset + length <= vram_.size()) return vram_.data() + offset;
if (offset + length <= static_cast<std::size_t>(kVramSize)) return vram_data_ + offset;
return nullptr;
}
if (c < kPhysicalBase) return nullptr;
const std::size_t offset = static_cast<std::size_t>(c - kPhysicalBase);
if (offset + length <= bytes_.size()) return bytes_.data() + offset;
if (offset + length <= ram_size_) return ram_data_ + offset;
return nullptr;
}
@@ -331,7 +518,7 @@ std::uint32_t GuestMemory::load_word_right(std::uint32_t address, std::uint32_t
}
void GuestMemory::store8(std::uint32_t address, std::uint8_t value) {
const auto r = resolve(address, 1u);
auto &data = region_bytes(r.region);
auto data = region_bytes(r.region);
const std::uint8_t old = data[r.offset];
log_write_watch(address, 1u, "store8", old, value);
data[r.offset] = value;
@@ -381,8 +568,8 @@ void GuestMemory::copy_in(std::uint32_t address, std::span<const std::uint8_t> s
while (copied < source.size()) {
const std::uint32_t current = address + static_cast<std::uint32_t>(copied);
const auto r = resolve(current, 1u);
auto &data = region_bytes(r.region);
const std::size_t chunk = std::min(source.size() - copied, data.size() - r.offset);
auto data = region_bytes(r.region);
const std::size_t chunk = (std::min)(source.size() - copied, data.size() - r.offset);
std::copy_n(source.begin() + static_cast<std::ptrdiff_t>(copied), chunk,
data.begin() + static_cast<std::ptrdiff_t>(r.offset));
copied += chunk;
@@ -395,8 +582,8 @@ void GuestMemory::copy_out(std::uint32_t address, std::span<std::uint8_t> destin
while (copied < destination.size()) {
const std::uint32_t current = address + static_cast<std::uint32_t>(copied);
const auto r = resolve(current, 1u);
const auto &data = region_bytes(r.region);
const std::size_t chunk = std::min(destination.size() - copied, data.size() - r.offset);
const auto data = region_bytes(r.region);
const std::size_t chunk = (std::min)(destination.size() - copied, data.size() - r.offset);
std::copy_n(data.begin() + static_cast<std::ptrdiff_t>(r.offset), chunk,
destination.begin() + static_cast<std::ptrdiff_t>(copied));
copied += chunk;
@@ -410,8 +597,8 @@ void GuestMemory::zero(std::uint32_t address, std::size_t length) {
while (cleared < length) {
const std::uint32_t current = address + static_cast<std::uint32_t>(cleared);
const auto r = resolve(current, 1u);
auto &data = region_bytes(r.region);
const std::size_t chunk = std::min(length - cleared, data.size() - r.offset);
auto data = region_bytes(r.region);
const std::size_t chunk = (std::min)(length - cleared, data.size() - r.offset);
std::fill_n(data.begin() + static_cast<std::ptrdiff_t>(r.offset), chunk, 0u);
cleared += chunk;
}
@@ -426,7 +613,11 @@ std::string GuestMemory::read_c_string(std::uint32_t address, std::size_t max_le
}
throw Error("Unterminated guest string at " + hex32(address));
}
const std::vector<std::uint8_t> &GuestMemory::bytes() const noexcept { return bytes_; }
const std::vector<std::uint8_t> &GuestMemory::vram_bytes() const noexcept { return vram_; }
std::span<const std::uint8_t> GuestMemory::bytes() const noexcept {
return {ram_data_, ram_size_};
}
std::span<const std::uint8_t> GuestMemory::vram_bytes() const noexcept {
return {vram_data_, kVramSize};
}
} // namespace psprecomp