clamp (and omod, ignored while f16 denormals are enabled) were refused or
dropped per op: v_{add,sub,subrev,mul,min,max}_f16, v_ldexp_f16,
v_frexp_mant_f16, v_cvt_norm_{i,u}16_f16, v_{max3,min3,med3,div_fixup}_f16.
They now share one clamp on the f16 bits (clampF16Bits).
Co-Authored-By: Claude <noreply@anthropic.com>
Single rounding of the whole sum, f32 accumulator denormals flushed, NaN
picked on the bits, clamp ignored and the v_dot2c accumulator read from
the lane itself under DPP, all as measured on hardware.
Co-Authored-By: Claude <noreply@anthropic.com>
- v_cndmask_b32: neg/abs on DPP; neg/abs applied on the bits, as the hardware does
- v_mul_legacy_f32: neg/abs on DPP and SDWA, clamp/omod on SDWA and VOP3
- v_ldexp_f16: neg/abs on src0 for VOP3, DPP and SDWA; NaN result chosen on the bits
Co-Authored-By: Claude <noreply@anthropic.com>
v_pack_b32_f16 applied neg/abs to bit 31 of the source dword instead of the
selected half. v_cvt_pknorm_{i16,u16}_f32 and v_ldexp_f32 used GLSL
PackSnorm2x16/PackUnorm2x16/Ldexp, which leave NaN, rounding ties and
denormals to the driver. Compute them on the bits as measured on RDNA2.
Co-Authored-By: Claude <noreply@anthropic.com>
v_ceil/floor/trunc/rndne/fract/sqrt/rsq/rcp/rcp_iflag/log/exp/sin/cos
(f32 and f16) and v_frexp_mant_f32 left NaN, invalid, zero and infinite
results to the driver: signalling NaNs came back unquieted, lavapipe lost
the NaN sign and payload, invalid results were +NaN instead of -NaN, f32
sin/cos at whole and half cycles returned tiny values instead of +0, and
v_fract_f16 of a tiny negative rounded up to 1.0.
Adds an execution test for the 27 opcodes with expected values from
hardware; approximate ops are compared with tolerances from the Vulkan
precision table.
Co-Authored-By: Claude <noreply@anthropic.com>
writeF16 dropped the clamp bit, so VOP3/SDWA f16 results with clamp
(v_rcp_f16, v_fma_f16, v_cvt_f16_f32, v_add_f16_sdwa, ...) were not
saturated. Clamp now limits to [0, 1] with NaN and -0 going to +0.
The f16 sqrt/rsq/log and sin/cos paths applied omod, which the
hardware ignores on f16 results in the default float mode, and
v_cvt_pkrtz_f16_f32 applied clamp and omod to its inputs, which the
hardware ignores as well.
Co-Authored-By: Claude <noreply@anthropic.com>
VOP3 0x140 was translated as a plain fma, so a zero source times an
infinity or a NaN gave NaN instead of the addend. A zero source now
gives +0 + src2, the same rule v_mul_legacy_f32 and v_mac_legacy_f32
already follow. The opcode also takes neg and abs on its sources.
VOP3P 0x13 threw "VOP3P opcode is not implemented". It computes
src0.lo * src1.lo + src0.hi * src1.hi + src2 like v_dot2c_f32_f16, with
the VOP3P op_sel/op_sel_hi and neg_lo/neg_hi modifiers on the packed f16
sources, neg on the f32 accumulator and clamp. op_sel and neg_hi on the
accumulator throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v_pk_fmac_f16 read its accumulator through the permuted destination and
wrote without the DPP write mask. v_movrels/movrelsd/movrelsd_2 dropped
the source permute, and the relative writes dropped the DPP write mask.
Co-Authored-By: Claude <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>
Decode VOP3 opcode 0x35f and translate it on the raw half bits, matching
the RDNA2 hardware: NaN numerator or denominator is returned quieted,
0/0 and inf/inf give 0xfe00, a NaN quotient becomes a signed infinity,
and op_sel, abs and neg apply as for the other f16 ternary ops.
Co-Authored-By: Claude <noreply@anthropic.com>
v_ldexp_f16, v_frexp_mant_f16, v_frexp_exp_i16_f16, v_cvt_norm_i16/u16_f16,
v_cvt_pknorm_i16/u16_f16, v_cmp_class_f16, v_cmpx_class_f16,
v_sat_pk_u8_i16 and v_mul/mac_legacy_f32 threw in the decoder. The
behaviour was measured on an RDNA2 GPU: ldexp takes the exponent as an i16,
the norm conversions round to nearest even (done in integers, as x * 32767
does not fit f32 exactly), and a legacy multiply with a zero operand gives
+0 even for an infinite or NaN other operand. v_mac_legacy_f32 adds the
rounded product, like v_mac_f32.
Co-Authored-By: Claude <noreply@anthropic.com>
The decoder puts an instruction's DPP fields on its source, and only a
destination carrying them has writeRawU32 keep the lanes the masks leave out
(and, without bound_ctrl, the lanes with no source lane). destinationOperand
copied them over, but no write used it, so every row was written: a wave64
scan adding the other row's total into the odd rows only (row_mask:0xa) added
it into every row, the prefix sums a key pass indexes primitives by came out
wrong, and the tree built from the keys held cycles its walkers looped on.
As in the Kyty recompiler (writes through DestinationOperand):
TranslateInstruction gives the destination its source's DPP fields once, and
a mac accumulator reads the destination without them (accumulatorOperand).
agc_dpp_write_tests checks a masked DPP add writes through DppUpdateU32.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>