shader: implement RDNA2 frexp mantissa

This commit is contained in:
nmzik
2026-08-17 07:58:56 +02:00
parent 1be9568a02
commit 330889e50b
7 changed files with 61 additions and 16 deletions
@@ -567,6 +567,7 @@ std::string OpcodeToString(Opcode opcode) {
case Opcode::VCvtRpiI32F32: return "v_cvt_rpi_i32_f32"; case Opcode::VCvtRpiI32F32: return "v_cvt_rpi_i32_f32";
case Opcode::VCvtFlrI32F32: return "v_cvt_flr_i32_f32"; case Opcode::VCvtFlrI32F32: return "v_cvt_flr_i32_f32";
case Opcode::VFrexpExpI32F32: return "v_frexp_exp_i32_f32"; case Opcode::VFrexpExpI32F32: return "v_frexp_exp_i32_f32";
case Opcode::VFrexpMantF32: return "v_frexp_mant_f32";
case Opcode::VCvtOffF32I4: return "v_cvt_off_f32_i4"; case Opcode::VCvtOffF32I4: return "v_cvt_off_f32_i4";
case Opcode::VCvtF32Ubyte0: return "v_cvt_f32_ubyte0"; case Opcode::VCvtF32Ubyte0: return "v_cvt_f32_ubyte0";
case Opcode::VCvtF32Ubyte1: return "v_cvt_f32_ubyte1"; case Opcode::VCvtF32Ubyte1: return "v_cvt_f32_ubyte1";
@@ -152,6 +152,7 @@ enum class Opcode {
VCvtRpiI32F32, VCvtRpiI32F32,
VCvtFlrI32F32, VCvtFlrI32F32,
VFrexpExpI32F32, VFrexpExpI32F32,
VFrexpMantF32,
VCvtOffF32I4, VCvtOffF32I4,
VCvtF32Ubyte0, VCvtF32Ubyte0,
VCvtF32Ubyte1, VCvtF32Ubyte1,
@@ -121,6 +121,7 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
{0x39u, Opcode::VFfbhU32}, {0x39u, Opcode::VFfbhU32},
{0x3au, Opcode::VFfblB32}, {0x3au, Opcode::VFfblB32},
{0x3fu, Opcode::VFrexpExpI32F32}, {0x3fu, Opcode::VFrexpExpI32F32},
{0x40u, Opcode::VFrexpMantF32},
{0x42u, Opcode::VMovreldB32}, {0x42u, Opcode::VMovreldB32},
{0x43u, Opcode::VMovrelsB32}, {0x43u, Opcode::VMovrelsB32},
{0x50u, Opcode::VCvtF16U16}, {0x50u, Opcode::VCvtF16U16},
@@ -170,6 +171,7 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
{0x39u, Opcode::VFfbhU32}, {0x39u, Opcode::VFfbhU32},
{0x3au, Opcode::VFfblB32}, {0x3au, Opcode::VFfblB32},
{0x3fu, Opcode::VFrexpExpI32F32}, {0x3fu, Opcode::VFrexpExpI32F32},
{0x40u, Opcode::VFrexpMantF32},
{0x42u, Opcode::VMovreldB32}, {0x42u, Opcode::VMovreldB32},
{0x43u, Opcode::VMovrelsB32}, {0x43u, Opcode::VMovrelsB32},
{0x50u, Opcode::VCvtF16U16}, {0x50u, Opcode::VCvtF16U16},
@@ -413,6 +415,7 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) {
case Opcode::VCvtRpiI32F32: case Opcode::VCvtRpiI32F32:
case Opcode::VCvtFlrI32F32: case Opcode::VCvtFlrI32F32:
case Opcode::VFrexpExpI32F32: case Opcode::VFrexpExpI32F32:
case Opcode::VFrexpMantF32:
case Opcode::VCvtF16F32: case Opcode::VCvtF16F32:
case Opcode::VRcpF32: case Opcode::VRcpF32:
case Opcode::VRcpIflagF32: case Opcode::VRcpIflagF32:
@@ -767,6 +770,7 @@ bool IsVop1FloatResultOpcode(Opcode opcode) {
case Opcode::VCeilF32: case Opcode::VCeilF32:
case Opcode::VRndneF32: case Opcode::VRndneF32:
case Opcode::VFloorF32: case Opcode::VFloorF32:
case Opcode::VFrexpMantF32:
case Opcode::VExpF32: case Opcode::VExpF32:
case Opcode::VLogF32: case Opcode::VLogF32:
case Opcode::VRsqF32: case Opcode::VRsqF32:
@@ -93,6 +93,26 @@ bool Translator::TranslateFloat16Operation(const IR::Instruction& inst) {
} }
bool Translator::TranslateFloatOperation(const IR::Instruction& inst) { bool Translator::TranslateFloatOperation(const IR::Instruction& inst) {
if (inst.op == IR::Opcode::FrexpMantF32) {
const auto bits = ReadU32(inst.src[0]);
const auto exponent = IR::U32(
ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(23u), IR::Value(8u)}));
const auto mantissa = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x007fffffu)));
const auto sign = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x80000000u)));
const auto base = ir.BitwiseOr(sign, IR::U32(IR::Value(0x3f000000u)));
const auto normal = ir.BitwiseOr(base, mantissa);
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {mantissa}));
const auto shift = ir.ISub(IR::U32(IR::Value(23u)), msb);
const auto fraction =
ir.BitwiseAnd(ir.ShiftLeftLogical(mantissa, shift), IR::U32(IR::Value(0x007fffffu)));
const auto subnormal = ir.BitwiseOr(base, fraction);
const auto zero = ir.IEqual(mantissa, IR::U32(IR::Value(0u)));
const auto finite = ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal,
ir.Select(zero, bits, subnormal));
const auto result = ir.Select(ir.IEqual(exponent, IR::U32(IR::Value(0xffu))), bits, finite);
WriteOperand(inst.dst, ir.BitCastF32(result));
return true;
}
IR::ValueOpcode opcode {}; IR::ValueOpcode opcode {};
switch (inst.op) { switch (inst.op) {
case IR::Opcode::RcpF32: opcode = IR::ValueOpcode::FPRecip32; break; case IR::Opcode::RcpF32: opcode = IR::ValueOpcode::FPRecip32; break;
@@ -104,6 +104,7 @@ constexpr LowerMap LOWER_OPS[] = {
{Decoder::Opcode::VCvtRpiI32F32, Opcode::ConvertRoundPlusInfF32ToI32}, {Decoder::Opcode::VCvtRpiI32F32, Opcode::ConvertRoundPlusInfF32ToI32},
{Decoder::Opcode::VCvtFlrI32F32, Opcode::ConvertFloorF32ToI32}, {Decoder::Opcode::VCvtFlrI32F32, Opcode::ConvertFloorF32ToI32},
{Decoder::Opcode::VFrexpExpI32F32, Opcode::FrexpExpI32F32}, {Decoder::Opcode::VFrexpExpI32F32, Opcode::FrexpExpI32F32},
{Decoder::Opcode::VFrexpMantF32, Opcode::FrexpMantF32},
{Decoder::Opcode::VCvtOffF32I4, Opcode::ConvertI4ToOffsetF32}, {Decoder::Opcode::VCvtOffF32I4, Opcode::ConvertI4ToOffsetF32},
{Decoder::Opcode::VRcpF32, Opcode::RcpF32}, {Decoder::Opcode::VRcpF32, Opcode::RcpF32},
{Decoder::Opcode::VRcpIflagF32, Opcode::RcpIflagF32}, {Decoder::Opcode::VRcpIflagF32, Opcode::RcpIflagF32},
@@ -214,6 +214,7 @@ IR_OPCODE(ConvertF16ToI16, General, Conversion)
IR_OPCODE(ConvertRoundPlusInfF32ToI32, General, Conversion) IR_OPCODE(ConvertRoundPlusInfF32ToI32, General, Conversion)
IR_OPCODE(ConvertFloorF32ToI32, General, Conversion) IR_OPCODE(ConvertFloorF32ToI32, General, Conversion)
IR_OPCODE(FrexpExpI32F32, General, Conversion) IR_OPCODE(FrexpExpI32F32, General, Conversion)
IR_OPCODE(FrexpMantF32, General, Float)
IR_OPCODE(ConvertI4ToOffsetF32, General, Conversion) IR_OPCODE(ConvertI4ToOffsetF32, General, Conversion)
IR_OPCODE(LdexpF32, General, Float) IR_OPCODE(LdexpF32, General, Float)
IR_OPCODE(PackF32ToF16Rtz, General, Conversion) IR_OPCODE(PackF32ToF16Rtz, General, Conversion)
+33 -16
View File
@@ -13527,13 +13527,13 @@ TestCase VectorFloatConversionOps() {
O::SEndpgm}}; O::SEndpgm}};
} }
TestCase VectorFrexpExpI32F32Edges() { TestCase VectorFrexpF32Edges() {
using O = ShaderOpcode; using O = ShaderOpcode;
const std::vector<u32> inputs = { const std::vector<u32> inputs = {
0x00000000u, 0x80000000u, 0x3f000000u, 0xbf800000u, 0x00000000u, 0x80000000u, 0x3f000000u, 0xbf800000u, 0x40800000u,
0x40800000u, 0xc1000000u, 0x00800000u, 0x007fffffu, 0xc1000000u, 0x00800000u, 0x007fffffu, 0x00000001u, 0x7f800000u,
0x00000001u, 0x7f800000u, 0x7fc00000u, 0x7fc00000u, 0xff800000u, 0x7fa00001u,
}; };
std::vector<u32> code; std::vector<u32> code;
for (u32 i = 0; i < static_cast<u32>(inputs.size()); i++) { for (u32 i = 0; i < static_cast<u32>(inputs.size()); i++) {
@@ -13541,27 +13541,44 @@ TestCase VectorFrexpExpI32F32Edges() {
AppendBufferLoadDword(&code, i == 3u ? 23u : i, 31); AppendBufferLoadDword(&code, i == 3u ? 23u : i, 31);
} }
for (u32 i = 0; i < static_cast<u32>(inputs.size()); i++) { for (u32 i = 0; i < static_cast<u32>(inputs.size()); i++) {
const auto dst = 16u + i; const auto exponent_dst = i == 3u ? 19u : 32u + i;
if (dst == 19u) { const auto mantissa_dst = i == 3u ? 21u : 48u + i;
if (i == 3u) {
code.push_back(0x7e267ef9u); // captured v_frexp_exp_i32_f32 v19, abs(v23) code.push_back(0x7e267ef9u); // captured v_frexp_exp_i32_f32 v19, abs(v23)
code.push_back(0x00260617u); code.push_back(0x00260617u);
} else { } else {
code.push_back(EncodeVop1(0x3f, dst, Vgpr(i))); code.push_back(EncodeVop1(0x3f, exponent_dst, Vgpr(i)));
} }
AppendStoreVgpr(&code, dst, i); AppendStoreVgpr(&code, exponent_dst, i);
if (i == 3u) {
code.push_back(0x7e2a80f9u); // captured v_frexp_mant_f32 v21, abs(v23)
code.push_back(0x00260617u);
} else {
code.push_back(EncodeVop1(0x40, mantissa_dst, Vgpr(i)));
}
AppendStoreVgpr(&code, mantissa_dst, inputs.size() + i);
} }
AppendEnd(&code); AppendEnd(&code);
TestCase test; TestCase test;
test.name = "VectorFrexpExpI32F32Edges"; test.name = "VectorFrexpF32Edges";
test.code = std::move(code); test.code = std::move(code);
test.initial = inputs; test.initial = inputs;
test.expected = {0u, 0u, 0u, 1u, 3u, 4u, test.initial.resize(inputs.size() * 2u);
0xffffff83u, 0xffffff82u, 0xffffff6cu, 0u, 0u}; test.expected = {
test.opcodes = {O::VMovB32, O::BufferLoadDword, O::VFrexpExpI32F32, 0u, 0u, 0u, 1u, 3u,
O::BufferStoreDword, O::SEndpgm}; 4u, 0xffffff83u, 0xffffff82u, 0xffffff6cu, 0u,
test.decoded_counts = {{"v_frexp_exp_i32_f32", inputs.size()}}; 0u, 0u, 0u, 0x00000000u, 0x80000000u,
test.required_spirv = {"FindUMsb", "OpBitFieldUExtract"}; 0x3f000000u, 0x3f000000u, 0x3f000000u, 0xbf000000u, 0x3f000000u,
0x3f7ffffeu, 0x3f000000u, 0x7f800000u, 0x7fc00000u, 0xff800000u,
0x7fa00001u,
};
test.opcodes = {O::VMovB32, O::BufferLoadDword, O::VFrexpExpI32F32,
O::VFrexpMantF32, O::BufferStoreDword, O::SEndpgm};
test.decoded_counts = {{"v_frexp_exp_i32_f32", inputs.size()},
{"v_frexp_mant_f32", inputs.size()}};
test.required_spirv = {"FindUMsb", "OpBitFieldUExtract",
"OpShiftLeftLogical"};
return test; return test;
} }
@@ -17880,7 +17897,7 @@ std::vector<TestCase> MakeCases() {
AddCase(VectorMinMaxF32NanAndSignedZeroEdges); AddCase(VectorMinMaxF32NanAndSignedZeroEdges);
AddCase(VectorMed3F32NanUsesMin3Path); AddCase(VectorMed3F32NanUsesMin3Path);
AddCase(VectorFloatConversionOps); AddCase(VectorFloatConversionOps);
AddCase(VectorFrexpExpI32F32Edges); AddCase(VectorFrexpF32Edges);
AddCase(CvtF32ToIntSaturatesNaNAndOutOfRange); AddCase(CvtF32ToIntSaturatesNaNAndOutOfRange);
AddCase(VectorSpecialF32FlushesDenormalInputs); AddCase(VectorSpecialF32FlushesDenormalInputs);
AddCase(VectorRcpIflagF32IntegerReciprocal); AddCase(VectorRcpIflagF32IntegerReciprocal);