mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-02 16:09:47 +08:00
[AGC] Decode VOP3P and emit packed f16 arithmetic (first slice) (#145)
* [AGC] Decode VOP3P and emit packed f16 arithmetic (first slice) On gfx10 the VOP3P family lives under its own 0b110011000 prefix (word0 top byte 0xCC), which the major-opcode switch currently routes to the SMEM branch, so packed instructions were decoded as scalar memory ops and emitted as silent no-ops. Intercept the exact 9-bit prefix ahead of the switch, decode the five packed-f16 arithmetic opcodes with their op_sel/op_sel_hi/neg_lo/neg_hi/clamp modifiers, and emit them as UnpackHalf2x16 -> component-wise f32 vec2 ops -> PackHalf2x16 so no Float16 capability is needed. Bit layout and opcode numbers pinned to LLVM MC test encodings (vop3p.s, gfx10_vop3p_literalv216.txt) and VOP3PInstructions.td. Unsupported modifiers, packed constants and out-of-scope packed opcodes fail with a clear error instead of emitting wrong results. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AGC] Make packed f16 exact and drop v_pk_fma_f16 (review response) Address the FP16 correctness review on the VOP3P slice. Replace GLSL UnpackHalf2x16/PackHalf2x16 with explicit integer f16<->f32 conversions (EmitHalfToFloat/EmitFloatToHalf): exact widening with subnormal normalisation, and narrowing with round-to-nearest-even, overflow-to-Inf and NaN/Inf handling. Their subnormal and rounding behaviour no longer depends on implementation-defined float-controls modes. With exact conversions, v_pk_add_f16 and v_pk_mul_f16 are bit-exact to a true f16 op (f32 result rounds losslessly to f16; a f16 product fits in f32). Emit v_pk_min_f16/v_pk_max_f16 as fminnum_like/fmaxnum_like (NaN operand returns the other; ordered numeric compare) instead of GLSL FMin/FMax. v_pk_fma_f16 now fails emission loudly: a fused f16 FMA rounds once, an f32 multiply-add then pack double-rounds (fma(0x4100,0x7522,0x04EA) is 0x7A6B fused vs 0x7A6A via f32). Exact fused emulation is a planned follow-up slice. ShaderDump gains an Expect model (Translates/DecodeFails/EmitFails) and packed regressions: arith, non-default modifiers, and loud-failure pins for the fma case above and for clamp. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: tensorcrush <tensorcrush@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Antigravity AI <antigravity@gemini.com>
This commit is contained in:
@@ -237,6 +237,17 @@ public sealed record Gen5SdwaControl(
|
||||
bool Clamp,
|
||||
uint? ScalarDestination) : Gen5InstructionControl;
|
||||
|
||||
// Packed (VOP3P) source and destination modifiers. Each mask holds one bit per
|
||||
// source operand. OpSel/OpSelHi pick which 16-bit half of a source feeds the low
|
||||
// and high result lanes respectively; NegLo/NegHi negate the value routed to each
|
||||
// lane. Clamp saturates each output half to [0, 1].
|
||||
public sealed record Gen5Vop3pControl(
|
||||
uint OpSelMask,
|
||||
uint OpSelHiMask,
|
||||
uint NegLoMask,
|
||||
uint NegHiMask,
|
||||
bool Clamp) : Gen5InstructionControl;
|
||||
|
||||
public sealed record Gen5DppControl(
|
||||
uint Control,
|
||||
bool FetchInactive,
|
||||
|
||||
@@ -562,6 +562,22 @@ public static class Gen5ShaderTranslator
|
||||
return DecodeSop(word, out name, out sizeDwords, out error);
|
||||
}
|
||||
|
||||
// gfx10 moved VOP3P (packed 16-bit math) to its own 0b110011000 prefix
|
||||
// (word0 top byte 0xCC), separate from the VOP3 block. Match the full
|
||||
// 9-bit prefix here, before the coarse major-opcode switch, so packed
|
||||
// instructions are not misread as one of the neighbouring encodings.
|
||||
if ((word & 0xFF800000u) == 0xCC000000u)
|
||||
{
|
||||
encoding = Gen5ShaderEncoding.Vop3p;
|
||||
if (!ctx.TryReadUInt32(baseAddress + pc + sizeof(uint), out var vop3pExtra))
|
||||
{
|
||||
error = $"vop3p-extra-read-failed pc=0x{pc:X}";
|
||||
return false;
|
||||
}
|
||||
|
||||
return DecodeVop3p(word, vop3pExtra, out name, out sizeDwords, out error);
|
||||
}
|
||||
|
||||
switch (word >> 26)
|
||||
{
|
||||
case 0x33:
|
||||
@@ -1160,6 +1176,37 @@ public static class Gen5ShaderTranslator
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DecodeVop3p(
|
||||
uint word,
|
||||
uint extra,
|
||||
out string name,
|
||||
out uint sizeDwords,
|
||||
out string error)
|
||||
{
|
||||
var opcode = (word >> 16) & 0x7F;
|
||||
var src0 = extra & 0x1FF;
|
||||
var src1 = (extra >> 9) & 0x1FF;
|
||||
var src2 = (extra >> 18) & 0x1FF;
|
||||
sizeDwords = src0 == 0xFF || src1 == 0xFF || src2 == 0xFF ? 3u : 2u;
|
||||
error = string.Empty;
|
||||
|
||||
// Opcode numbers taken from LLVM's AMDGPU VOP3PInstructions.td and the
|
||||
// gfx9/gfx10 MC test encodings; they are unchanged across gfx9 and gfx10.
|
||||
// Unhandled packed opcodes (integer, fma_mix, ...) stay opaque here and
|
||||
// fail loudly at emission rather than being silently mis-emitted.
|
||||
name = opcode switch
|
||||
{
|
||||
0x0E => "VPkFmaF16",
|
||||
0x0F => "VPkAddF16",
|
||||
0x10 => "VPkMulF16",
|
||||
0x11 => "VPkMinF16",
|
||||
0x12 => "VPkMaxF16",
|
||||
_ => $"Vop3pRaw{opcode:X2}",
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DecodeDs(
|
||||
uint word,
|
||||
out string name,
|
||||
@@ -1839,6 +1886,28 @@ public static class Gen5ShaderTranslator
|
||||
isVop3B ? (word >> 8) & 0x7F : null);
|
||||
break;
|
||||
}
|
||||
case Gen5ShaderEncoding.Vop3p:
|
||||
{
|
||||
var extra = words[1];
|
||||
sources =
|
||||
[
|
||||
Gen5Operand.Source(extra & 0x1FF, literal),
|
||||
Gen5Operand.Source((extra >> 9) & 0x1FF, literal),
|
||||
Gen5Operand.Source((extra >> 18) & 0x1FF, literal),
|
||||
];
|
||||
destinations = [Gen5Operand.Vector(word & 0xFF)];
|
||||
|
||||
// op_sel_hi is split across both dwords: bits [1:0] live in word1
|
||||
// [28:27], bit [2] in word0 [14].
|
||||
var opSelHi = ((extra >> 27) & 0x3) | (((word >> 14) & 0x1) << 2);
|
||||
control = new Gen5Vop3pControl(
|
||||
(word >> 11) & 0x7,
|
||||
opSelHi,
|
||||
(extra >> 29) & 0x7,
|
||||
(word >> 8) & 0x7,
|
||||
((word >> 15) & 1) != 0);
|
||||
break;
|
||||
}
|
||||
case Gen5ShaderEncoding.Ds:
|
||||
{
|
||||
var extra = words[1];
|
||||
|
||||
Reference in New Issue
Block a user