From 1f09de889688bdef0d718d3c2cc4e4f0a2e5de81 Mon Sep 17 00:00:00 2001 From: tensorcrush Date: Tue, 14 Jul 2026 17:05:33 +0200 Subject: [PATCH] [AGC] Complete gfx10 v_cmpx_f32 decode and emit ordered/unordered float compares (#122) * [AGC] Complete gfx10 v_cmpx_f32 decode and emit ordered/unordered float compares Add the missing v_cmpx_*_f32 VOPC decode entries (0x17-0x1C, 0x1F) and emission for the ordered/unordered predicates: nlg maps to OpFUnordEqual, while o/u are lowered from OpIsNan (unordered = isnan(a) || isnan(b), ordered = !unordered) because SPIR-V's OpOrdered/OpUnordered require the Kernel capability and are invalid in Vulkan shader modules. Opcode numbers cross-checked against LLVM's llvm-mc regression tests (llvm/test/MC/AMDGPU/gfx10_asm_vopc.s, gfx10_asm_vopcx.s); emitted lowering validated with spirv-val --target-env vulkan1.1. Co-Authored-By: Claude Fable 5 * [AGC] Write VCC only for non-X vector compares On gfx10 the VCmpx encodings have no sdst and define EXEC only, so the unconditional VCC store clobbered VCC on every VCmpx. Move the VCC store to the non-X path; EXEC keeps the existing old-EXEC & condition update. Addresses review feedback on #122. Co-Authored-By: Claude Fable 5 --------- Co-authored-by: tensorcrush Co-authored-by: Claude Fable 5 --- src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs | 7 +++++ .../Agc/Gen5SpirvTranslator.Alu.cs | 27 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs b/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs index f044ca3..e6db37d 100644 --- a/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs +++ b/src/SharpEmu.Libs/Agc/Gen5ShaderTranslator.cs @@ -802,8 +802,15 @@ internal static class Gen5ShaderTranslator 0x14 => "VCmpxGtF32", 0x15 => "VCmpxLgF32", 0x16 => "VCmpxGeF32", + 0x17 => "VCmpxOF32", + 0x18 => "VCmpxUF32", + 0x19 => "VCmpxNgeF32", + 0x1A => "VCmpxNlgF32", + 0x1B => "VCmpxNgtF32", + 0x1C => "VCmpxNleF32", 0x1D => "VCmpxNeqF32", 0x1E => "VCmpxNltF32", + 0x1F => "VCmpxTruF32", 0x80 => "VCmpFI32", 0x81 => "VCmpLtI32", 0x82 => "VCmpEqI32", diff --git a/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs b/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs index d95ddc3..46d36a5 100644 --- a/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs +++ b/src/SharpEmu.Libs/Agc/Gen5SpirvTranslator.Alu.cs @@ -861,6 +861,25 @@ internal static partial class Gen5SpirvTranslator { condition = _module.ConstantBool(true); } + else if (opcode is "VCmpOF32" or "VCmpxOF32" or "VCmpUF32" or "VCmpxUF32") + { + // The ordered/unordered predicates only test whether either + // operand is NaN. SPIR-V's OpOrdered/OpUnordered are Kernel-only, + // so build the same result from OpIsNan, which needs no extra + // capability: unordered = isnan(a) || isnan(b), ordered = !that. + var left = GetFloatSource(instruction, 0); + var right = GetFloatSource(instruction, 1); + var nanLeft = _module.AddInstruction(SpirvOp.IsNan, _boolType, left); + var nanRight = _module.AddInstruction(SpirvOp.IsNan, _boolType, right); + var unordered = _module.AddInstruction( + SpirvOp.LogicalOr, + _boolType, + nanLeft, + nanRight); + condition = opcode is "VCmpUF32" or "VCmpxUF32" + ? unordered + : _module.AddInstruction(SpirvOp.LogicalNot, _boolType, unordered); + } else if (opcode is not ("VCmpClassF32" or "VCmpxClassF32") && opcode.EndsWith("F32", StringComparison.Ordinal)) { @@ -875,6 +894,7 @@ internal static partial class Gen5SpirvTranslator "VCmpLgF32" or "VCmpxLgF32" => SpirvOp.FOrdNotEqual, "VCmpGeF32" or "VCmpxGeF32" => SpirvOp.FOrdGreaterThanEqual, "VCmpNeqF32" or "VCmpxNeqF32" => SpirvOp.FUnordNotEqual, + "VCmpNlgF32" or "VCmpxNlgF32" => SpirvOp.FUnordEqual, "VCmpNltF32" or "VCmpxNltF32" => SpirvOp.FUnordGreaterThanEqual, "VCmpNleF32" or "VCmpxNleF32" => SpirvOp.FUnordGreaterThan, "VCmpNgtF32" or "VCmpxNgtF32" => SpirvOp.FUnordLessThanEqual, @@ -925,7 +945,8 @@ internal static partial class Gen5SpirvTranslator condition = _module.AddInstruction(operation, _boolType, left, right); } - StoreWaveMask(106, condition); + // On gfx10, VCmpx writes EXEC only and preserves VCC; the sdst + // operand was removed from the cmpx encodings on this generation. if (opcode.StartsWith("VCmpx", StringComparison.Ordinal)) { var active = _module.AddInstruction( @@ -935,6 +956,10 @@ internal static partial class Gen5SpirvTranslator condition); StoreWaveMask(126, active); } + else + { + StoreWaveMask(106, condition); + } return true; }