From a3bf73bf7a1c1f5d5d15b366b59e1cb3c2dcf2ea Mon Sep 17 00:00:00 2001 From: Brian Degenhardt Date: Fri, 14 Aug 2026 21:25:14 -0700 Subject: [PATCH] GS: AArch64 has no slow unaligned load to compile around MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FAST_UNALIGNED was defined only inside the ARCH_X86 arm, where it records that AVX-and-later cores stopped punishing unaligned vector loads. On ARM64 the macro was therefore undefined, which the preprocessor reads as zero, so every arm64 build compiled the texture-upload path as though the punishment existed. It never did. LDR Q and LD1 take any address, and GSVector4i's load template ignores its own `aligned` parameter and emits the same instruction either way. So the callers were paying for a distinction with no machine behind it: WriteImage tests the source address and the pitch on every call to choose between three template instantiations of WriteImageBlock and WriteImageColumn that, for 8- and 4-bit columns, compile to identical code. For 32- and 16-bit columns the unaligned arm is not identical, but it is the worse one — eight combining 64-bit loads instead of four 128-bit loads and a swizzle. Defining it collapses all of that. GSLocalMemoryMultiISA.cpp.o goes from 80,368 to 62,184 bytes of .text and from 58 emitted functions to 32, which is what an I-cache on a handheld cares about. Only GSBlock.h and GSLocalMemoryMultiISA.cpp read the macro, so nothing else moves. The retained load strategy is not new code: whenever an upload happened to land 32-byte aligned, arm64 already ran exactly this sequence. What goes away is the arm that only ever ran when it did not. --- common/VectorIntrin.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/VectorIntrin.h b/common/VectorIntrin.h index 9451726b24..7f4f24d784 100644 --- a/common/VectorIntrin.h +++ b/common/VectorIntrin.h @@ -38,6 +38,16 @@ #include #elif defined(ARCH_ARM64) + +// AArch64 has no aligned/unaligned load distinction to begin with: LDR Q and LD1 +// take any address, and GSVector4i::load ignores its own parameter and +// emits the same instruction either way. Leaving this undefined made the callers +// pay for a distinction that does not exist — a runtime address-and-pitch test +// per texture upload, dispatching into three template instantiations that +// compile to identical code, and for 32/16-bit columns a worse load strategy +// (eight combining 64-bit loads instead of four 128-bit loads and a swizzle). +#define FAST_UNALIGNED 1 + #include #endif