From d5410a93c8d6790242995b5022791d75e38b8831 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 20 Aug 2026 18:39:17 +0200 Subject: [PATCH] graphics: preserve FP16 inline constants --- .../recompiler/frontend/translate/Float.cpp | 7 +++-- .../frontend/translate/Translate.cpp | 16 +++++++++- .../frontend/translate/Translator.h | 3 +- .../shader/recompiler/ir/ShaderIR.cpp | 9 +++--- src/graphics/shader/recompiler/ir/ShaderIR.h | 1 + tests/ShaderRecompilerComputeTests.cpp | 29 +++++++++++++++++++ 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/translate/Float.cpp b/src/graphics/shader/recompiler/frontend/translate/Float.cpp index 76f7d1a42..e6e3e826c 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Float.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Float.cpp @@ -16,11 +16,12 @@ bool Translator::TranslatePackedFloat16(const IR::Instruction& inst) { default: return false; } const auto translate_lane = [&](bool high) { - const auto lhs = ReadF16LaneAsF32(inst.src[0], high); - const auto rhs = ReadF16LaneAsF32(inst.src[1], high); + const auto lhs = ReadF16LaneAsF32(inst.src[0], high, true); + const auto rhs = ReadF16LaneAsF32(inst.src[1], high, true); IR::F32 result; if (inst.src_count == 3u) { - result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src[2], high)})); + result = IR::F32( + ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src[2], high, true)})); } else { result = IR::F32(ir.Emit(opcode, {lhs, rhs})); } diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index 4efd3b23a..e1fdaf2c6 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -456,7 +456,21 @@ IR::U64 Translator::ReadU64(const IR::Operand& operand) { return IR::U64(ReadOperand(operand, IR::Type::U64)); } -IR::F32 Translator::ReadF16LaneAsF32(const IR::Operand& operand, bool high_lane) { +IR::F32 Translator::ReadF16LaneAsF32(const IR::Operand& operand, bool high_lane, bool packed) { + if (operand.float_inline) { + const bool use_zero = packed && (high_lane ? operand.op_sel_hi : operand.op_sel); + auto value = use_zero ? IR::F32(IR::Value::F32(0.0f)) + : ir.BitCastF32(IR::U32(IR::Value(operand.imm))); + const auto half = IR::F16(ir.Emit(IR::ValueOpcode::ConvertF16F32, {value})); + value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32F16, {half})); + if (operand.absolute) { + value = IR::F32(ir.Emit(IR::ValueOpcode::FPAbs32, {value})); + } + if (high_lane ? operand.negate_hi : operand.negate) { + value = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {value})); + } + return value; + } auto raw_operand = operand; raw_operand.sdwa_sel = 6; raw_operand.sdwa_sext = false; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 72b363f20..7b88919fa 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -38,7 +38,8 @@ private: IR::U32 ReadU32(const IR::Operand& operand); std::array ReadU32Pair(const IR::Operand& operand); IR::U64 ReadU64(const IR::Operand& operand); - IR::F32 ReadF16LaneAsF32(const IR::Operand& operand, bool high_lane); + IR::F32 ReadF16LaneAsF32(const IR::Operand& operand, bool high_lane, + bool packed = false); IR::F32 ReadF16AsF32(const IR::Operand& operand); IR::F32 ReadMixF32(const IR::Operand& operand); IR::U32 ReadU16LaneRaw(const IR::Operand& operand, bool high_lane); diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.cpp b/src/graphics/shader/recompiler/ir/ShaderIR.cpp index 6dffea760..6c3c20cc2 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.cpp +++ b/src/graphics/shader/recompiler/ir/ShaderIR.cpp @@ -157,10 +157,11 @@ bool LowerSourceOperand(const Decoder::Operand& decoded, Operand& operand, std:: case Decoder::OperandKind::FloatInlineConstant: operand = {}; CopyOperandModifiers(decoded, operand); - operand.kind = OperandKind::ImmediateU32; - operand.imm = decoded.value; - operand.sext_64 = decoded.kind == Decoder::OperandKind::IntegerInlineConstant && - decoded.signed_val < 0; + operand.kind = OperandKind::ImmediateU32; + operand.imm = decoded.value; + operand.float_inline = decoded.kind == Decoder::OperandKind::FloatInlineConstant; + operand.sext_64 = decoded.kind == Decoder::OperandKind::IntegerInlineConstant && + decoded.signed_val < 0; return true; case Decoder::OperandKind::Null: case Decoder::OperandKind::PopsExitingWaveId: diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index a705750b8..e70345022 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -37,6 +37,7 @@ struct Operand { OperandKind kind = OperandKind::Null; Register reg; uint32_t imm = 0; + bool float_inline = false; bool sext_64 = false; uint32_t sdwa_sel = 6; uint32_t sdwa_dst_unused = 2; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 4503ae6f5..63d48e024 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -13867,6 +13867,34 @@ TestCase VectorVop3FmaF16UsesRdna2Opcode34b() { {O::V_MOV_B32, O::V_FMA_F16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase FloatInlineConstantF16UsesNumericValue() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 1, 0x38003800u); // 0.5h, 0.5h + AppendVMovLiteral(&code, 2, 0x3c003c00u); // 1.0h, 1.0h + AppendVMovLiteral(&code, 10, 0xaaaa0000u); + AppendVMovLiteral(&code, 12, 0xbbbb0000u); + + AppendVop3(&code, 0x34bu, 10, 246u, Vgpr(1), Vgpr(2)); + AppendVop3p(&code, 0x0eu, 11, 246u, Vgpr(1), Vgpr(2), 0x1u); + AppendVop3(&code, 0x34bu, 12, 248u, 242u, 128u); + + AppendStoreVgpr(&code, 10, 0); + AppendStoreVgpr(&code, 11, 1); + AppendStoreVgpr(&code, 12, 2); + AppendEnd(&code); + + TestCase test; + test.name = "FloatInlineConstantF16UsesNumericValue"; + test.code = std::move(code); + test.expected = {0xaaaa4200u, 0x3c004200u, 0xbbbb3118u}; + test.opcodes = {O::V_MOV_B32, O::V_FMA_F16, O::V_PK_FMA_F16, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.decoded_counts = {{"4.000000", 2}, {"0.159155", 1}}; + return test; +} + TestCase VectorFloatControlContractPreservesInfNan() { using O = ShaderOpcode; @@ -18941,6 +18969,7 @@ std::vector MakeCases() { AddCase(MadMixF16LiteralHalfSourceUsesOpsel); AddCase(MadMixF16NegHiIsAbsAndNegIsIndependent); AddCase(VectorVop3FmaF16UsesRdna2Opcode34b); + AddCase(FloatInlineConstantF16UsesNumericValue); AddCase(VectorFloatControlContractPreservesInfNan); AddCase(VectorFloatArithmeticOps); AddCase(VectorMinMaxF32NanAndSignedZeroEdges);