A select of exec and zero gives a mask that is the same for every lane. Reading
it back went through a lane number, which Metal does not have in a vertex
shader, so MoltenVK rejected the shader. The select now stores the lane bit and
marks the pair as a mask, a constant mask word reads as a constant, and the
constant folder removes the bit test on an all-zero or all-one word.
agc_scalar_select_mask runs the translated IR over a wave and pins the lane
values for scc set and clear, a source that differs per lane, the two wave64
halves differing, a destination that is also a source, and constant and
literal sources.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reuse the prerequisite call-region analysis for signed relative calls, including callees after s_endpgm. Lower the saved link as shader base plus PC+4 and reject unsupported targets and call shapes.
Add decoder, IR, CFG and Vulkan execution regressions. Seven focused CMake tests pass; five layouts cover 15 dispatches and 3,200 output comparisons on lavapipe. Record ISA sources and native RDNA2/PS5 limits in TechnicalDebt.
Decodes SOP1 0x21 s_swappc_b64 (link pair = pc + 4) and resolves its call/return pairing through the existing s_setpc_b64 path: static call regions in GraphBuilder and the vertex-entry fetch-shader call inlined from the parsed fetch-shader plan. s_call_b64 (SOPK 0x16) and s_rfe_b64 (SOP1 0x22) throw at decode. Unmodeled cases throw; see docs/dev/TechnicalDebt.md.
s_sethalt with SIMM16[0] clear is a no-op; s_sethalt 1, s_sendmsghalt, a reached s_code_end, s_rfe_b64, the ds_gws_* instructions and ds_ordered_count throw with the reason at translation. Linear decoding stops at s_code_end like at s_endpgm, so code padded with s_code_end after a final backward branch decodes. Encodings from LLVM SOPInstructions.td and DSInstructions.td, checked against the gfx10 MC tests; semantics from the RDNA 2 ISA guide.
s_endpgm_saved and s_endpgm_ordered_ps_done end the wave like s_endpgm
(checked on an RDNA2 GPU: nothing after them runs), so they decode to
SEndpgm and the control flow graph treats them as terminators.
s_atc_probe, s_atc_probe_buffer and s_dcache_discard(_x2) have no visible
effect and translate to no-ops like the other scalar cache controls.
s_round_mode is ignored like s_setreg MODE writes; listed in TechnicalDebt.
Co-Authored-By: Claude <noreply@anthropic.com>
SOPK 0x12 threw in the decoder. The recompiler emits round to nearest
even, which s_setreg_imm32_b32 already requires, so reads that stay
within MODE bits 0..3 return 0. Other hardware registers and fields
throw with the register and bits read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Any other field or value throws. The existing s_setreg_b32 no-op is
recorded under Functional technical debt instead of silent stubs.
Co-Authored-By: Claude <noreply@anthropic.com>
s_cmovk_i32 writes the sign-extended 16-bit immediate when SCC is set.
s_version is a no-op marker. s_setreg_imm32_b32 takes its value from a
literal dword and is ignored like s_setreg_b32; both are now listed as
silent stubs in TechnicalDebt.
Co-Authored-By: Claude <noreply@anthropic.com>
s_wakeup (SOPP 0x03) threw "unsupported SOPP opcode 3" in the decoder. It
pings the other waves of the thread group so they leave s_sleep early.
s_sleep already translates to nothing, so no wave is ever asleep and the
ping has nothing to wake: s_wakeup translates like s_sleep. It carries no
operand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
s_movrels reads SGPR[src + M0] and s_movreld writes SGPR[dst + M0].
The 64-bit forms align the offset down to even (M0 & ~1), and
s_movrelsd_2_b32 takes the source offset from M0[9:0] and the
destination offset from M0[25:16], as measured on hardware. They use
the same select-over-registers lowering as the vector forms, over the
106 SGPRs. The embedded vertex-fetch scan forgets every tracked SGPR on
a relative write.
Co-Authored-By: Claude <noreply@anthropic.com>
The decoder threw for these SOP1 opcodes. The 64-bit find-first-zero and
signed leading-bit counts reuse the existing 64-bit helpers with a flag;
s_quadmask_b32 is the 32-bit half of s_quadmask_b64. s_bcnt0 and s_quadmask
set SCC to result != 0; the find and leading-bit forms leave it.
Co-Authored-By: Claude <noreply@anthropic.com>
s_icache_inv, s_incperflevel, s_decperflevel, s_ttracedata_imm, s_wait_idle,
s_gl1_inv, s_dcache_inv, s_dcache_wb, buffer_gl0_inv and buffer_gl1_inv threw
in the decoder. SPIR-V has no cache control, so they translate like s_clause
and s_waitcnt, which already emit nothing. The SMEM and MUBUF forms carry no
operands, so no pass sees a phantom base or destination register.
Co-Authored-By: Claude <noreply@anthropic.com>
S_CBRANCH_CDBGSYS, S_CBRANCH_CDBGUSER, S_CBRANCH_CDBGSYS_OR_USER and S_CBRANCH_CDBGSYS_AND_USER
(SOPP 0x17-0x1a) branch only while a debugger sets the conditional debug bits in the wave's
status, which never happens on a retail console, so they decode to one no-op opcode.