Decode FLAT opcode 0x16 in the global segment and load one dword per
active lane from the SGPR pair base plus the signed offset plus four
times the lane id.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The decoder stopped at an s_endpgm unless the next word was a known branch
target. When an earlier branch jumps further than that word, the block right
after the s_endpgm is entered only from later code, so the program was cut
short and the graph builder rejected the branch as targeting an invalid
program counter. Stop only when no decoded branch targets a word at or past
the one after the s_endpgm.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
v_add_co_u32, v_sub_co_u32 and v_subrev_co_u32 (VOP3 0x30f, 0x310, 0x319)
decode to VAddI32, VSubI32 and VSubrevI32 and had no execution test.
tools/progress.py listed the first and the last as variants of those
opcodes but not v_sub_co_u32, which it reported as missing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On LDS the counter is at the instruction offset. The emitter added
M0[31:16] to it and required the offset to be below M0[15:0], which is
the GDS base and a size no other LDS access reads. The GDS path is
unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
VOP2 0x2b, 0x2c and 0x2d decode to the opcodes of v_mac_f32, v_madmk_f32
and v_madak_f32 and differ only by the encoding, which keeps them fused.
The progress map derives names from the opcode enum, so it listed the three
fused forms as missing.
The new test decodes the six encodings and checks that the translation
rounds the product separately for 0x1f-0x21, keeps 0x2b-0x2d fused, and
reads the accumulator and the literal from the right operand.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
OpConstantTrue and OpConstantFalse carry no operand word, so they reached the
declaration cache through the variadic Constant template, which builds the key
vector, and paid an allocation on every call. ConstantBool emits both, so a
shader paid it twice per boolean constant. Add the two-word overload; the
compile-shaped benchmark drops from 10413 allocations to 415.
Add agc_spirv_module_tests covering the interning contract: a repeated
declaration returns one id and is emitted once, operand shapes that share a
leading key word stay distinct, integral and enum operands agree, decorated
types stay separate from plain ones, memoized ids survive AllocateId, modules
do not share ids, and ids stay unique and contiguous past the cache capacity.
The test passes against both the previous header and this one, so it pins the
contract rather than the implementation.
Benchmark, pre-optimization header against this one, best of five:
hot loop x 2M 613.6 ms -> 46.3 ms 13.2x
compile-shaped 5000 instr 4.2 ms -> 0.4 ms 10.8x
64 compile-shaped modules 286.0 ms -> 25.4 ms 11.3x
Allocations for the compile-shaped case fall from 55217 to 415. Both builds
compute the same value over the same call sequence.
Co-Authored-By: Claude <[email protected]>
Every SpirvModule::Type and Constant call built a heap std::vector key and
looked it up in a std::map keyed on std::vector, so the comparison walked the
key bytes. At the one to three such calls per emitted instruction a shader
issues thousands of them per compile.
Add a fixed-size, linearly probed declaration cache that holds the key inline
and is consulted before the key vector is built, making a repeated declaration
a hash and a compare. The cacheable shapes are those whose operands are a
word: OpTypeVoid, OpTypeBool, OpTypeInt, OpTypeFloat, OpTypeVector,
OpTypePointer, OpTypeStruct, OpTypeArray, OpTypeFunction and the scalar
OpConstant family. The overloads are constrained on word-sized operands so
that enum operands such as spv::StorageClassFunction reach the cache instead of
binding the variadic template by reference.
Measured with a harness driving the module through the emitters' call shapes:
80000 hot type and constant calls drop from 80074 heap allocations to 140. The
assigned ids and the emitted SPIR-V word stream are byte-identical.
Co-Authored-By: Claude <[email protected]>
The PS5 libthr (FreeBSD) creates error-checking mutexes by default and has
PTHREAD_MUTEX_ADAPTIVE_NP (type 4); relocking an error-checking or adaptive
mutex returns EDEADLK. Default mutexes were Normal and type 4 was mapped to
Normal, so a relock threw instead of returning EDEADLK, which aborted an
Unreal Engine title's recursive directory scan.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The unsupported-instruction error now lists the instruction bytes, and the relink failure names the input file, the main executable or the guest module, on an extra Input: line.
op_sel[1:0] on v_alignbit_b32/v_alignbyte_b32 selects the byte of src2 that
holds the shift, op_sel[2] is ignored and op_sel[3] writes the low 16 result
bits to the high half with the low half zeroed (measured on gfx1035).
v_alignbyte_b32 now uses src2[1:0] only, as the ISA says; values >= 4 gave
a wrong result before. v_max3/min3_{i16,u16} and v_med3_u16 take op_sel like
v_med3_i16 already does.
Co-Authored-By: Claude <[email protected]>
Color targets with the SW_4KB_S swizzle (CB_COLOR_ATTRIB3 tile mode 5) are laid out in 4 KiB
blocks with the standard element order the texture detiler uses, and are resident through the
same storage images as 64 KiB targets. A CB_COLOR_VIEW selecting one slice (SLICE_START equal to
SLICE_MAX) renders into that slice: the target starts one slice size per slice after the surface,
the slice size being the surface's mip chain as a texture of the same tile mode lays it out. Views
of several slices are still rejected.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
v_div_scale_f32 returns a NaN source unchanged instead of scaling it,
and v_div_fmas_f32 quiets the first NaN input or returns the default
NaN 0xffc00000, so results no longer depend on the driver's NaNs. The
test compares NaN results bit for bit. s_denorm_mode throws: the
recompiler does not pin a denormal mode, so no value matches what it
models.
Co-Authored-By: Claude <[email protected]>
These make up the precise f32 division macro compilers emit for a / b. No
reference emulator implements them and the ISA pseudocode disagrees with the
hardware, so the rules were measured on an RDNA2 GPU:
- div_scale decides from the exponent fields only: 2^64 or 2^-64 scaling
and VCC for differences >= 96 or <= -96, a denormal or huge (>= 253)
denominator and a numerator exponent <= 24; a zero input gives 0xffc00000.
- div_fmas scales by 2^64 when the exponent of S2 is >= 128, else 2^-64,
before the single rounding (the ISA says a fixed 2^32).
- div_fixup follows the ISA, and a NaN quotient with finite inputs becomes
+-inf.
s_denorm_mode is accepted and ignored; listed in TechnicalDebt.
Co-Authored-By: Claude <[email protected]>
buffer_atomic_fcmpswap, fcmpswap_x2, fmin_x2, fmax_x2, inc_x2 and
dec_x2 are compare-and-swap loops on the dword or qword. fcmpswap
compares as floats (-0 equals +0, NaN equals nothing) and fmin/fmax are
IEEE minNum/maxNum with -0 < +0, a quieted result for a signalling NaN
and the other operand for a quiet one, all with integer ops so the
driver's float controls do not matter. The existing 32-bit fmin/fmax
kept the old value whenever a NaN or two zeros were involved; it now
uses the same rule.
Co-Authored-By: Claude <[email protected]>
buffer_atomic_inc, buffer_atomic_dec, buffer_atomic_cmpswap_x2 and
buffer_atomic_{add,sub,smin,umin,smax,umax,and,xor}_x2 threw in the decoder.
The 64-bit ones use native 64-bit SPIR-V atomics like the existing
swap_x2/or_x2; inc and dec wrap at the data operand through the existing
compare-exchange helpers.
Co-Authored-By: Claude <[email protected]>