From 4b7df8623a04c2b4b0162fe446a4c98fe92f577e Mon Sep 17 00:00:00 2001 From: Deeptanshu Lal <143280861+Deeptanshuu@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:50:12 +0530 Subject: [PATCH] [AGC] Fix v_fmac_f32 family decoding in Gen5 VOP2 table (#103) VOP2 opcode 0x2B was mapped to v_ldexp_f32, which is its gfx6/gfx7 assignment. On gfx10-class hardware 0x2B is v_fmac_f32, so any shader using it silently computed ldexp(a, b) instead of dst += a * b. v_ldexp_f32 on gfx10 only exists as VOP3 0x362, which the VOP3 table already maps correctly. Also add the remaining members of the fmac family: - v_fmamk_f32 (0x2C) and v_fmaak_f32 (0x2D), including their mandatory literal dword in instruction sizing and operand construction, reusing the existing v_madmk/v_madak handling. - The VOP3-encoded form of v_fmac_f32 (0x12B), emitted when source modifiers are present. SPIR-V emission reuses the existing v_mac_f32 body (fma with the destination register as addend) and the v_mad/v_fma case group. Opcode assignments verified against LLVM's AMDGPU backend (VOP2Instructions.td): V_FMAC_F32 gfx10 = 0x02b, V_FMAMK_F32 = 0x02c, V_FMAAK_F32 = 0x02d; V_LDEXP_F32 is 0x02b only on gfx6/gfx7 and is VOP3-only 0x362 on gfx10. Decode verified by feeding hand-assembled gfx1013 words through Gen5ShaderTranslator: 0x560A0501 previously decoded as VLdexpF32 and a v_fmamk_f32 program failed with unknown-vop2 op=0x2C; both now decode correctly, and VOP3 0x362 still decodes as VLdexpF32. Co-authored-by: Claude Fable 5 --- src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs | 11 +++++++---- src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs b/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs index d52cad2..cfb4c7e 100644 --- a/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs +++ b/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs @@ -718,7 +718,7 @@ internal static class Gen5ShaderTranslator } var src0 = word & 0x1FF; - sizeDwords = opcode is 0x20 or 0x21 || src0 is 0xF9 or 0xFA or 0xFF ? 2u : 1u; + sizeDwords = opcode is 0x20 or 0x21 or 0x2C or 0x2D || src0 is 0xF9 or 0xFA or 0xFF ? 2u : 1u; error = string.Empty; name = opcode switch { @@ -757,7 +757,9 @@ internal static class Gen5ShaderTranslator 0x28 => "VAddcU32", 0x29 => "VSubbU32", 0x2A => "VSubbrevU32", - 0x2B => "VLdexpF32", + 0x2B => "VFmacF32", + 0x2C => "VFmamkF32", + 0x2D => "VFmaakF32", 0x2F => "VCvtPkrtzF16F32", 0x30 => "VCvtPkU16U32", 0x31 => "VCvtPkI16I32", @@ -870,6 +872,7 @@ internal static class Gen5ShaderTranslator 0x10F => "VMinF32", 0x110 => "VMaxF32", 0x11F => "VMacF32", + 0x12B => "VFmacF32", 0x12F => "VCvtPkrtzF16F32", 0x141 => "VMadF32", 0x143 => "VMadU32U24", @@ -1379,7 +1382,7 @@ internal static class Gen5ShaderTranslator Gen5Operand.Source(word & 0x1FF, literal), Gen5Operand.Vector((word >> 9) & 0xFF), ]; - if (opcode == "VMadMkF32" && literal.HasValue) + if (opcode is "VMadMkF32" or "VFmamkF32" && literal.HasValue) { sources = [ @@ -1388,7 +1391,7 @@ internal static class Gen5ShaderTranslator sources[1], ]; } - else if (opcode == "VMadAkF32" && literal.HasValue) + else if (opcode is "VMadAkF32" or "VFmaakF32" && literal.HasValue) { sources = [ diff --git a/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs b/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs index 5c01072..9df8d40 100644 --- a/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs +++ b/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs @@ -255,6 +255,8 @@ internal static partial class Gen5SpirvTranslator case "VFmaF32": case "VMadMkF32": case "VMadAkF32": + case "VFmamkF32": + case "VFmaakF32": result = EmitFloatResult( instruction, Ext( @@ -265,6 +267,7 @@ internal static partial class Gen5SpirvTranslator GetFloatSource(instruction, 2))); break; case "VMacF32": + case "VFmacF32": { var addend = Bitcast(_floatType, LoadV(destination)); result = EmitFloatResult(