mirror of
https://github.com/jessicanataliagta/PSPRecomp
synced 2026-09-27 00:53:30 -04:00
otimizações round 13 (severo)
otimizações round 13 (severo)
This commit is contained in:
@@ -277,6 +277,14 @@ struct alignas(16) AllegrexContext {
|
||||
PSPRECOMP_CONTEXT_FORCEINLINE void write_vfpu_vector_with_destination_prefix_ct(const float *source) noexcept {
|
||||
static_assert(Length >= 1u && Length <= 4u);
|
||||
const std::uint32_t destination_prefix = vfpu_ctrl[2];
|
||||
// V8.4: the architectural default has no saturation and no lane mask.
|
||||
// This is by far the common case in geometry code, so bypass the local
|
||||
// copy, four saturation decodes and four mask tests entirely.
|
||||
if (destination_prefix == 0u) {
|
||||
write_vfpu_vector_ct<VectorRegister, Length>(source);
|
||||
eat_vfpu_prefixes();
|
||||
return;
|
||||
}
|
||||
float value[4]{source[0], Length >= 2u ? source[1] : 0.0f,
|
||||
Length >= 3u ? source[2] : 0.0f, Length >= 4u ? source[3] : 0.0f};
|
||||
auto saturate_lane = [&](std::uint32_t i) {
|
||||
@@ -1666,6 +1674,20 @@ struct alignas(16) AllegrexContext {
|
||||
float source[16]{};
|
||||
float target[4]{};
|
||||
float result[16]{};
|
||||
// With S/T/D at their architectural defaults VMSCL is exactly a
|
||||
// matrix-by-scalar multiply. Avoid reading the old destination and all
|
||||
// prefix/saturation plumbing while retaining the same final prefix eat.
|
||||
if (vfpu_ctrl[0] == 0xE4u && vfpu_ctrl[1] == 0xE4u && vfpu_ctrl[2] == 0u) {
|
||||
read_vfpu_matrix_ct<SourceMatrixRegister, Side>(source);
|
||||
read_vfpu_vector_ct<TargetScalarRegister, 1u>(target);
|
||||
const float scalar = target[0];
|
||||
for (std::uint32_t row = 0u; row < Side; ++row)
|
||||
for (std::uint32_t column = 0u; column < Side; ++column)
|
||||
result[row * 4u + column] = source[row * 4u + column] * scalar;
|
||||
write_vfpu_matrix_ct<DestinationMatrixRegister, Side>(result);
|
||||
eat_vfpu_prefixes();
|
||||
return;
|
||||
}
|
||||
float previous_destination[16]{};
|
||||
read_vfpu_matrix_ct<SourceMatrixRegister, Side>(source);
|
||||
read_vfpu_matrix_ct<DestinationMatrixRegister, Side>(previous_destination);
|
||||
|
||||
@@ -68,6 +68,83 @@ public:
|
||||
// GuestMemory and preserve the original validation/watch behavior.
|
||||
class AotFastView {
|
||||
public:
|
||||
// V8.4 production AOT path. VCSNative now treats direct-fastmem as a
|
||||
// launch requirement, so its generated corpus can call these helpers
|
||||
// and remove the per-memory-access `fastmem_base_ != nullptr` branch.
|
||||
// The checked AOT helpers below remain untouched for the generic
|
||||
// PSPRecomp framework, tests, tools and profiles that need fallback
|
||||
// memory semantics.
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint8_t aot_direct_load8(std::uint32_t address) const noexcept {
|
||||
return *fastmem_pointer(fastmem_base_, address);
|
||||
}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint16_t aot_direct_load16(std::uint32_t address) const noexcept {
|
||||
return GuestMemory::read_le16(fastmem_pointer(fastmem_base_, address));
|
||||
}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint32_t aot_direct_load32(std::uint32_t address) const noexcept {
|
||||
return GuestMemory::read_le32(fastmem_pointer(fastmem_base_, address));
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_direct_store8(std::uint32_t address, std::uint8_t value) const noexcept {
|
||||
*fastmem_pointer(fastmem_base_, address) = value;
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_direct_store16(std::uint32_t address, std::uint16_t value) const noexcept {
|
||||
GuestMemory::write_le16(fastmem_pointer(fastmem_base_, address), value);
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_direct_store32(std::uint32_t address, std::uint32_t value) const noexcept {
|
||||
GuestMemory::write_le32(fastmem_pointer(fastmem_base_, address), value);
|
||||
}
|
||||
template <std::size_t N>
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_direct_load32_block(
|
||||
std::uint32_t address, std::uint32_t (&values)[N]) const noexcept {
|
||||
static_assert(N != 0u);
|
||||
if constexpr (std::endian::native == std::endian::little) {
|
||||
std::memcpy(values, fastmem_pointer(fastmem_base_, address), N * sizeof(std::uint32_t));
|
||||
} else {
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
values[i] = GuestMemory::read_le32(fastmem_pointer(
|
||||
fastmem_base_, address + static_cast<std::uint32_t>(i * 4u)));
|
||||
}
|
||||
}
|
||||
template <std::size_t N>
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_direct_store32_block(
|
||||
std::uint32_t address, const std::uint32_t (&values)[N]) const noexcept {
|
||||
static_assert(N != 0u);
|
||||
if constexpr (std::endian::native == std::endian::little) {
|
||||
std::memcpy(fastmem_pointer(fastmem_base_, address), values, N * sizeof(std::uint32_t));
|
||||
} else {
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
GuestMemory::write_le32(fastmem_pointer(
|
||||
fastmem_base_, address + static_cast<std::uint32_t>(i * 4u)), values[i]);
|
||||
}
|
||||
}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint32_t aot_direct_load_word_left(
|
||||
std::uint32_t address, std::uint32_t existing) const noexcept {
|
||||
const std::uint32_t shift = (address & 3u) * 8u;
|
||||
const std::uint32_t memory_word = aot_direct_load32(address & ~3u);
|
||||
return (existing & (0x00FFFFFFu >> shift)) | (memory_word << (24u - shift));
|
||||
}
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint32_t aot_direct_load_word_right(
|
||||
std::uint32_t address, std::uint32_t existing) const noexcept {
|
||||
const std::uint32_t shift = (address & 3u) * 8u;
|
||||
const std::uint32_t memory_word = aot_direct_load32(address & ~3u);
|
||||
return (existing & (0xFFFFFF00u << (24u - shift))) | (memory_word >> shift);
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_direct_store_word_left(
|
||||
std::uint32_t address, std::uint32_t value) const noexcept {
|
||||
const std::uint32_t shift = (address & 3u) * 8u;
|
||||
const std::uint32_t aligned = address & ~3u;
|
||||
const std::uint32_t memory_word = aot_direct_load32(aligned);
|
||||
aot_direct_store32(aligned,
|
||||
(value >> (24u - shift)) | (memory_word & (0xFFFFFF00u << shift)));
|
||||
}
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_direct_store_word_right(
|
||||
std::uint32_t address, std::uint32_t value) const noexcept {
|
||||
const std::uint32_t shift = (address & 3u) * 8u;
|
||||
const std::uint32_t aligned = address & ~3u;
|
||||
const std::uint32_t memory_word = aot_direct_load32(aligned);
|
||||
aot_direct_store32(aligned,
|
||||
(value << shift) | (memory_word & (0x00FFFFFFu >> (24u - shift))));
|
||||
}
|
||||
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH std::uint8_t aot_load8(std::uint32_t address) const {
|
||||
if (fastmem_base_ != nullptr) return *fastmem_pointer(fastmem_base_, address);
|
||||
const std::uint32_t offset = ram_offset_of_fast(address);
|
||||
@@ -446,6 +523,24 @@ public:
|
||||
void aot_store_word_left(std::uint32_t address, std::uint32_t value);
|
||||
void aot_store_word_right(std::uint32_t address, std::uint32_t value);
|
||||
|
||||
// Generic block access used by code generators for LV.Q/SV.Q. Profiles
|
||||
// with a shared AotFastView lower these calls to their cached/direct view;
|
||||
// manual generated fixtures retain the exact checked scalar semantics.
|
||||
template <std::size_t N>
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_load32_block(
|
||||
std::uint32_t address, std::uint32_t (&values)[N]) const {
|
||||
static_assert(N != 0u);
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
values[i] = aot_load32(address + static_cast<std::uint32_t>(i * 4u));
|
||||
}
|
||||
template <std::size_t N>
|
||||
PSPRECOMP_MEMORY_FAST_PATH void aot_store32_block(
|
||||
std::uint32_t address, const std::uint32_t (&values)[N]) {
|
||||
static_assert(N != 0u);
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
aot_store32(address + static_cast<std::uint32_t>(i * 4u), values[i]);
|
||||
}
|
||||
|
||||
// DEFLATE/LZ-style forward overlap copy. Unlike memmove, bytes written at
|
||||
// the destination become immediately available as source bytes, so a short
|
||||
// match (for example distance=1) can expand to a long repeated run.
|
||||
|
||||
Reference in New Issue
Block a user