gs_vector gains adds16, hadds16 and mul16hrs, the three the resampler is
made of. The last two are SSSE3 instructions, so the SSE2 tier builds
them from mullo/mulhi and a lane gather, and NEON gets its own forms;
the oracle checks all three against a written-out scalar model at every
tier and the equivalence lane checks them against GSVector4i.
The resampler then drops GSVector4i/GSVector8i. It builds at SSE2 now,
which it could not before -- GSVector4i reaches for pminsd with no
fallback -- and the 256-bit path is spelled directly, since AVX2 is the
only place a 16-lane form exists.
Taps stay written out rather than looped: the accumulator saturates, so
the order has to stand, and a loop costs the multiplies their overlap.
Hoisting the buffer base out of them is what pays: ReverbDownsample goes
39 instructions and 15 memory ops to 26 and 13, the unit's text 781
bytes to 669 at SSE4.1 and 1053 to 957 at AVX2, and a six-million-call
microbenchmark 69 ms to 65. The AVX2 path is unchanged at 66 ms.
tests/spu2/build.sh now sweeps -msse2, -msse4.1 and -mavx2 under both
compilers; all six builds agree on all eight hashes.
First caller converted, and the one that shows where the difference lies.
The rect alignment now takes the block size as the packed pair callers
already hold, so it reads with one 8-byte load rather than rebuilding a
vector from two ints; GSDirtyRect.cpp comes out 16 bytes smaller under
gcc at SSE4.1 and 32 at AVX2, with clang level.
The operations themselves are identical either way, as the codegen lane
records. What moves the number is how a value is read on the way in.
Most of what the GS does with a vector is read or write one lane by name
rather than operate on all four, and a bare machine vector cannot spell
that. gs_v4i_view carries the same names the class does -- rect edges,
lanes, and the 8- and 16-bit arrays -- over the vector itself. It costs
nothing on gcc, and on clang a rect's width-by-height comes out two
instructions shorter, because the union form vectorises where reading the
class's members does not.
Adds ralign for the outside and negative-infinity modes, the high-half
load and four-lane construction, which is what a rect-shaped caller needs
beyond the arithmetic already here.
gs_vec4i is the machine vector itself rather than a class holding one, so
an operation is its intrinsic: operators become named functions, and the
template parameters carrying an immediate become macros, which the
instructions require since pslld and pshufd take a literal.
Carries a tier GSVector4i cannot express. The class reaches for pminsd,
pmaxsd, pminud, pmaxud, pblendvb, pblendw, packusdw, ptest, pmovzx and the
pinsr/pextr family with no fallback, which is what makes SSE4.1 a hard
floor for the whole GS; the SSE2 forms are here, alongside SSE4.1, AVX and
NEON.
tests/gsvec checks every entry point against a written-out scalar model of
what the operation means -- the only reference available on SSE2, where
the class will not build -- and against the class itself from SSE4.1 up,
at both compilers and every tier. A third lane counts instructions per
operation: the two are identical everywhere, values in and value out, so
nothing is paid for the shape.