shader: separate legacy FP32 MAD rounding from fused FMA

Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
nmzik
2026-10-05 03:48:35 +04:00
parent bbb98bd490
commit d8be245c22
7 changed files with 134 additions and 8 deletions
@@ -223,6 +223,25 @@ uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) {
}
} // namespace
uint32_t EmitFPFma32(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
const auto result = EmitGlsl<GLSLstd450Fma, IR::Type::F32>(state, a, b, c);
state.builder.AddAnnotation(spv::OpDecorate, result, spv::DecorationNoContraction);
return result;
}
uint32_t EmitFPMad32(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
// Legacy MAD/MAC round and flush between multiply and add, irrespective of SP_DENORM.
a = EmitFlushF32DenormToSignedZero(state, a);
b = EmitFlushF32DenormToSignedZero(state, b);
c = EmitFlushF32DenormToSignedZero(state, c);
const auto product = EmitFPMul32(state, a, b);
state.builder.AddAnnotation(spv::OpDecorate, product, spv::DecorationNoContraction);
const auto sum = EmitFPAdd32(state, EmitFlushF32DenormToSignedZero(state, product), c);
state.builder.AddAnnotation(spv::OpDecorate, sum, spv::DecorationNoContraction);
return EmitFlushF32DenormToSignedZero(state, sum);
}
uint32_t EmitFPMedTri32(EmitterState& state, uint32_t a, uint32_t b, uint32_t c) {
const auto min_ab = EmitMinMaxF32Value(state, a, b, false);
const auto min3 = EmitMinMaxF32Value(state, min_ab, c, false);
@@ -160,8 +160,8 @@ EMIT_NATIVE(FPMul64, OpFMul, F64, uint32_t, uint32_t)
inline constexpr auto EmitFPFma64 =
EmitGlsl<GLSLstd450Fma, IR::Type::F64, uint32_t, uint32_t, uint32_t>;
uint32_t EmitFPRecip64(EmitterState& state, uint32_t arg0);
inline constexpr auto EmitFPFma32 =
EmitGlsl<GLSLstd450Fma, IR::Type::F32, uint32_t, uint32_t, uint32_t>;
uint32_t EmitFPFma32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
uint32_t EmitFPMad32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
uint32_t EmitFPMin32(EmitterState& state, uint32_t arg0, uint32_t arg1);
uint32_t EmitFPMax32(EmitterState& state, uint32_t arg0, uint32_t arg1);
uint32_t EmitFPMin64(EmitterState& state, uint32_t arg0, uint32_t arg1);
@@ -224,6 +224,9 @@ enum class Opcode {
V_MAC_F32,
V_MADMK_F32,
V_MADAK_F32,
V_FMAC_F32,
V_FMAMK_F32,
V_FMAAK_F32,
V_MIN_F32,
V_MAX_F32,
V_ADD_F16,
@@ -65,9 +65,9 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = {
{0x2au, Opcode::V_SUBREV_CO_CI_U32},
{0x26u, Opcode::V_SUB_NC_U32, Vop2SdwaProfile::IntegerPartialDestination},
{0x27u, Opcode::V_SUBREV_NC_U32, Vop2SdwaProfile::IntegerFullDestination},
{0x2bu, Opcode::V_MAC_F32},
{0x2cu, Opcode::V_MADMK_F32},
{0x2du, Opcode::V_MADAK_F32},
{0x2bu, Opcode::V_FMAC_F32},
{0x2cu, Opcode::V_FMAMK_F32},
{0x2du, Opcode::V_FMAAK_F32},
{0x2fu, Opcode::V_CVT_PKRTZ_F16_F32, Vop2SdwaProfile::PackedFloat16},
{0x32u, Opcode::V_ADD_F16, Vop2SdwaProfile::Float16},
{0x33u, Opcode::V_SUB_F16, Vop2SdwaProfile::Float16},
@@ -800,6 +800,9 @@ bool IsVop2FloatOpcode(Opcode opcode) {
case Opcode::V_MAC_F32:
case Opcode::V_MADMK_F32:
case Opcode::V_MADAK_F32:
case Opcode::V_FMAC_F32:
case Opcode::V_FMAMK_F32:
case Opcode::V_FMAAK_F32:
case Opcode::V_CVT_PKRTZ_F16_F32:
case Opcode::V_ADD_F16:
case Opcode::V_SUB_F16:
@@ -1057,6 +1060,7 @@ void FinalizeVop2Instruction(std::span<const uint32_t> code, uint32_t word_index
Instruction& inst) {
switch (inst.opcode) {
case Opcode::V_MADMK_F32:
case Opcode::V_FMAMK_F32:
case Opcode::V_FMAMK_F16:
inst.src2 = inst.src1;
inst.src1 = {};
@@ -1064,6 +1068,7 @@ void FinalizeVop2Instruction(std::span<const uint32_t> code, uint32_t word_index
inst.src_count = 3;
break;
case Opcode::V_MADAK_F32:
case Opcode::V_FMAAK_F32:
case Opcode::V_FMAAK_F16:
inst.src2 = {};
inst.src2.kind = OperandKind::LiteralConstant;
@@ -1401,6 +1406,7 @@ bool SupportsNativeVop3SourceModifiers(Opcode opcode) {
case Opcode::V_MIN_F32:
case Opcode::V_MAX_F32:
case Opcode::V_MAC_F32:
case Opcode::V_FMAC_F32:
case Opcode::V_MAD_F32:
case Opcode::V_ADD_F64:
case Opcode::V_MUL_F64:
@@ -1436,6 +1442,7 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) {
case Opcode::V_MIN_F32:
case Opcode::V_MAX_F32:
case Opcode::V_MAC_F32:
case Opcode::V_FMAC_F32:
case Opcode::V_MAD_F32:
case Opcode::V_FMA_F32:
case Opcode::V_FMA_F16:
@@ -479,10 +479,13 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_MIN_F32: return FloatBinary(inst, IR::ValueOpcode::FPMin32, false);
case O::V_MAX_F32: return FloatBinary(inst, IR::ValueOpcode::FPMax32, false);
case O::V_LDEXP_F32: return FloatBinary(inst, IR::ValueOpcode::FPLdexp, false);
case O::V_MAC_F32: return FloatTernary(inst, IR::ValueOpcode::FPFma32, true, true);
case O::V_MAC_F32: return FloatTernary(inst, IR::ValueOpcode::FPMad32, true, true);
case O::V_MADMK_F32:
case O::V_MADAK_F32:
case O::V_MAD_F32:
case O::V_MAD_F32: return FloatTernary(inst, IR::ValueOpcode::FPMad32, false, true);
case O::V_FMAC_F32: return FloatTernary(inst, IR::ValueOpcode::FPFma32, true, true);
case O::V_FMAMK_F32:
case O::V_FMAAK_F32:
case O::V_FMA_F32: return FloatTernary(inst, IR::ValueOpcode::FPFma32, false, true);
case O::V_FMA_F64: return FloatTernary(inst, IR::ValueOpcode::FPFma64, false, false);
case O::V_MIN3_F32: return FloatTernary(inst, IR::ValueOpcode::FPMinTri32, false, false);
@@ -179,6 +179,7 @@ VALUE_OPCODE(FPCmpClass32, U1, F32, U32)
VALUE_OPCODE(FPAdd32, F32, F32, F32)
VALUE_OPCODE(FPSub32, F32, F32, F32)
VALUE_OPCODE(FPFma32, F32, F32, F32, F32)
VALUE_OPCODE(FPMad32, F32, F32, F32, F32)
VALUE_OPCODE(FPMul32, F32, F32, F32)
VALUE_OPCODE(FPAdd64, F64, F64, F64)
VALUE_OPCODE(FPMul64, F64, F64, F64)