From cd16c0a993a21d14eaf77675498f404e08aba777 Mon Sep 17 00:00:00 2001 From: Utku Halis Date: Sun, 11 Oct 2026 07:32:47 +0300 Subject: [PATCH] fix(shader): take the fused result when an f16 fma's sum is exact fmaF16RoundedToOdd adds the f32 product and the addend separately and uses TwoSum to round to odd when the sum is inexact. When the error is zero it returned the separate sum. On MoltenVK (Apple M3 Pro) the separate multiply and add produce +0 where the IEEE result is -0: agc_driver_dpp16_operands got 0x0000 instead of 0x8000 in the high half of lane 1's row_shr v_pk_fmac_f16. The exact case now returns an f32 fma of the same operands. The product of two f16 values is exact in f32 (11-bit significands, exponents well inside the f32 range), so when TwoSum's error is zero the separate sum and the fused result are the same IEEE value, including the sign of zero; the change only matters on devices whose separate multiply and add do not keep it. The inexact path is unchanged. --- core/shader/recompiler/Translation/src/FloatInstructions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/shader/recompiler/Translation/src/FloatInstructions.cpp b/core/shader/recompiler/Translation/src/FloatInstructions.cpp index 2ca7925c1f..0aef745417 100644 --- a/core/shader/recompiler/Translation/src/FloatInstructions.cpp +++ b/core/shader/recompiler/Translation/src/FloatInstructions.cpp @@ -262,7 +262,8 @@ IrF32 TranslationContext::fmaF16RoundedToOdd(IrF32 lhs, IrF32 rhs, IrF32 addend) IrValue& sumBits = ir.BitCastU32(sum); IrValue& sameSign = ir.IEqual(ir.BitwiseAnd(ir.BitwiseXor(sumBits, ir.BitCastU32(error)), ir.Constant(0x80000000u)), ir.Constant(0u)); IrValue& towardZero = ir.Select(sameSign, sumBits, ir.ISub(sumBits, ir.Constant(1u))); - return IrF32(ir.BitCastF32(ir.Select(inexact, ir.BitwiseOr(towardZero, ir.Constant(1u)), sumBits))); + IrValue& fused = ir.BitCastU32(ir.Emit(IrOpcode::FPFma32, IrType::F32, {&lhs.Value(), &rhs.Value(), &addend.Value()})); + return IrF32(ir.BitCastF32(ir.Select(inexact, ir.BitwiseOr(towardZero, ir.Constant(1u)), fused))); } IrU32 TranslationContext::readF16Bits(const RdnaOperand& operand) {