graphics: split shader translation into explicit opcode dispatchers

This commit is contained in:
nmzik
2026-08-26 08:48:17 +02:00
parent ffa69d48de
commit ab50c10d08
12 changed files with 2446 additions and 2358 deletions
@@ -38,146 +38,136 @@ IR::U32 Translator::ConvertF32ToI32Saturated(IR::F32 value, float lower_bound, f
return ir.Select(nan, IR::U32(IR::Value(0u)), clamped);
}
bool Translator::TranslateConversion(const Decoder::Instruction& inst) {
const auto f32 = [&](uint32_t index) {
return IR::F32(ReadOperand(SourceAt(inst, index), IR::Type::F32));
};
const auto u32 = [&](uint32_t index) { return ReadU32(SourceAt(inst, index)); };
const auto convert_u32 = [&](IR::F32 value) {
return ConvertF32ToU32Saturated(value, 4294967296.0f, 4294967040.0f, 0xffffffffu);
};
const auto convert_i32 = [&](IR::F32 value) {
return ConvertF32ToI32Saturated(value, -2147483648.0f, 2147483648.0f, 2147483520.0f,
0x80000000u, 0x7fffffffu);
};
void Translator::V_CVT_F32_UBYTE(const Decoder::Instruction& inst, uint32_t byte_index) {
const auto source = ReadU32(SourceAt(inst, 0));
const auto byte =
ir.BitwiseAnd(ir.ShiftRightLogical(source, IR::U32(IR::Value(byte_index * 8u))),
IR::U32(IR::Value(0xffu)));
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::ConvertF32U32, {byte}));
}
switch (inst.opcode) {
case Decoder::Opcode::V_CVT_F32_UBYTE0:
case Decoder::Opcode::V_CVT_F32_UBYTE1:
case Decoder::Opcode::V_CVT_F32_UBYTE2:
case Decoder::Opcode::V_CVT_F32_UBYTE3: {
const auto index = static_cast<uint32_t>(inst.opcode) -
static_cast<uint32_t>(Decoder::Opcode::V_CVT_F32_UBYTE0);
const auto byte =
ir.BitwiseAnd(ir.ShiftRightLogical(u32(0), IR::U32(IR::Value(index * 8u))),
IR::U32(IR::Value(0xffu)));
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::ConvertF32U32, {byte}));
return true;
}
case Decoder::Opcode::V_CVT_F32_U32:
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ConvertF32U32, {u32(0)}));
return true;
case Decoder::Opcode::V_CVT_F32_I32:
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ConvertF32S32, {u32(0)}));
return true;
case Decoder::Opcode::V_CVT_U32_F32:
WriteOperand(DestinationOperand(inst), convert_u32(f32(0)));
return true;
case Decoder::Opcode::V_CVT_I32_F32:
WriteOperand(DestinationOperand(inst), convert_i32(f32(0)));
return true;
case Decoder::Opcode::V_CVT_F16_F32:
WriteF16(DestinationOperand(inst), f32(0));
return true;
case Decoder::Opcode::V_CVT_F32_F16:
WriteOperand(DestinationOperand(inst), ReadF16AsF32(inst.src0));
return true;
case Decoder::Opcode::V_CVT_F16_U16: {
const auto value =
IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32U32, {ReadU16AsU32(inst.src0, false)}));
WriteF16(DestinationOperand(inst), value);
return true;
}
case Decoder::Opcode::V_CVT_F16_I16: {
const auto value =
IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {ReadU16AsU32(inst.src0, true)}));
WriteF16(DestinationOperand(inst), value);
return true;
}
case Decoder::Opcode::V_CVT_U16_F16: {
const auto converted =
ConvertF32ToU32Saturated(ReadF16AsF32(inst.src0), 65536.0f, 65535.0f, 0xffffu);
WriteU16(DestinationOperand(inst), converted);
return true;
}
case Decoder::Opcode::V_CVT_I16_F16: {
const auto converted = ConvertF32ToI32Saturated(
ReadF16AsF32(inst.src0), -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu);
WriteU16(DestinationOperand(inst),
ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
return true;
}
case Decoder::Opcode::V_CVT_RPI_I32_F32: {
const auto biased =
IR::F32(ir.Emit(IR::ValueOpcode::FPAdd32, {f32(0), IR::Value::F32(0.5f)}));
const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {biased}));
WriteOperand(DestinationOperand(inst), convert_i32(rounded));
return true;
}
case Decoder::Opcode::V_CVT_FLR_I32_F32: {
const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {f32(0)}));
WriteOperand(DestinationOperand(inst), convert_i32(rounded));
return true;
}
case Decoder::Opcode::V_FREXP_EXP_I32_F32: {
const auto bits = ir.BitCastU32(f32(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 normal = ir.ISub(exponent, IR::U32(IR::Value(126u)));
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {mantissa}));
const auto subnormal = ir.ISub(msb, IR::U32(IR::Value(148u)));
const auto denormal = ir.Select(ir.INotEqual(mantissa, IR::U32(IR::Value(0u))),
subnormal, IR::U32(IR::Value(0u)));
const auto finite = ir.Select(
ir.INotEqual(exponent, IR::U32(IR::Value(0xffu))),
ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal, denormal),
IR::U32(IR::Value(0u)));
WriteOperand(DestinationOperand(inst), finite);
return true;
}
case Decoder::Opcode::V_CVT_OFF_F32_I4: {
const auto nibble = IR::U32(
ir.Emit(IR::ValueOpcode::BitFieldSExtract, {u32(0), IR::Value(0u), IR::Value(4u)}));
const auto value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {nibble}));
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::FPMul32, {value, IR::Value::F32(1.0f / 16.0f)}));
return true;
}
case Decoder::Opcode::V_CVT_PKRTZ_F16_F32:
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {f32(0), f32(1)}));
return true;
case Decoder::Opcode::V_CVT_PKNORM_I16_F32:
case Decoder::Opcode::V_CVT_PKNORM_U16_F32: {
const auto pair = ir.Emit(IR::ValueOpcode::CompositeConstructF32x2, {f32(0), f32(1)});
const auto opcode = inst.opcode == Decoder::Opcode::V_CVT_PKNORM_I16_F32
? IR::ValueOpcode::PackSnorm2x16
: IR::ValueOpcode::PackUnorm2x16;
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {pair}));
return true;
}
case Decoder::Opcode::V_CVT_PK_U8_F32: {
const auto byte = ConvertF32ToU32Saturated(f32(0), 255.0f, 255.0f, 255u);
const auto index = ir.BitwiseAnd(u32(1), IR::U32(IR::Value(3u)));
const auto shift = ir.ShiftLeftLogical(index, IR::U32(IR::Value(3u)));
const auto mask = ir.ShiftLeftLogical(IR::U32(IR::Value(0xffu)), shift);
const auto base = ir.BitwiseAnd(u32(2), ir.BitwiseNot(mask));
WriteOperand(DestinationOperand(inst),
ir.BitwiseOr(base, ir.ShiftLeftLogical(byte, shift)));
return true;
}
case Decoder::Opcode::V_PACK_B32_F16: {
const auto low = ReadF16LaneBits(inst.src0, false);
const auto high =
ir.ShiftLeftLogical(ReadF16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low, high));
return true;
}
default: return false;
void Translator::V_CVT_F32_U32(const Decoder::Instruction& inst) {
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ConvertF32U32, {ReadU32(SourceAt(inst, 0))}));
}
void Translator::V_CVT_F32_I32(const Decoder::Instruction& inst) {
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ConvertF32S32, {ReadU32(SourceAt(inst, 0))}));
}
void Translator::V_CVT_U32_F32(const Decoder::Instruction& inst) {
const auto value = IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32));
WriteOperand(DestinationOperand(inst),
ConvertF32ToU32Saturated(value, 4294967296.0f, 4294967040.0f, 0xffffffffu));
}
void Translator::V_CVT_I32_F32(const Decoder::Instruction& inst) {
const auto value = IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32));
WriteOperand(DestinationOperand(inst),
ConvertF32ToI32Saturated(value, -2147483648.0f, 2147483648.0f, 2147483520.0f,
0x80000000u, 0x7fffffffu));
}
void Translator::V_CVT_F16_F32(const Decoder::Instruction& inst) {
WriteF16(DestinationOperand(inst), IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32)));
}
void Translator::V_CVT_F32_F16(const Decoder::Instruction& inst) {
WriteOperand(DestinationOperand(inst), ReadF16AsF32(inst.src0));
}
void Translator::V_CVT_F16_16(const Decoder::Instruction& inst, bool signed_value) {
const auto value = IR::F32(
ir.Emit(signed_value ? IR::ValueOpcode::ConvertF32S32 : IR::ValueOpcode::ConvertF32U32,
{ReadU16AsU32(inst.src0, signed_value)}));
WriteF16(DestinationOperand(inst), value);
}
void Translator::V_CVT_16_F16(const Decoder::Instruction& inst, bool signed_value) {
const auto value = ReadF16AsF32(inst.src0);
if (signed_value) {
const auto converted =
ConvertF32ToI32Saturated(value, -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu);
WriteU16(DestinationOperand(inst), ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
return;
}
WriteU16(DestinationOperand(inst),
ConvertF32ToU32Saturated(value, 65536.0f, 65535.0f, 0xffffu));
}
void Translator::V_CVT_RPI_I32_F32(const Decoder::Instruction& inst) {
const auto source = IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32));
const auto biased = IR::F32(ir.Emit(IR::ValueOpcode::FPAdd32, {source, IR::Value::F32(0.5f)}));
const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {biased}));
WriteOperand(DestinationOperand(inst),
ConvertF32ToI32Saturated(rounded, -2147483648.0f, 2147483648.0f, 2147483520.0f,
0x80000000u, 0x7fffffffu));
}
void Translator::V_CVT_FLR_I32_F32(const Decoder::Instruction& inst) {
const auto source = IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32));
const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {source}));
WriteOperand(DestinationOperand(inst),
ConvertF32ToI32Saturated(rounded, -2147483648.0f, 2147483648.0f, 2147483520.0f,
0x80000000u, 0x7fffffffu));
}
void Translator::V_FREXP_EXP_I32_F32(const Decoder::Instruction& inst) {
const auto source = IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32));
const auto bits = ir.BitCastU32(source);
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 normal = ir.ISub(exponent, IR::U32(IR::Value(126u)));
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {mantissa}));
const auto subnormal = ir.ISub(msb, IR::U32(IR::Value(148u)));
const auto denormal = ir.Select(ir.INotEqual(mantissa, IR::U32(IR::Value(0u))), subnormal,
IR::U32(IR::Value(0u)));
const auto finite =
ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0xffu))),
ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal, denormal),
IR::U32(IR::Value(0u)));
WriteOperand(DestinationOperand(inst), finite);
}
void Translator::V_CVT_OFF_F32_I4(const Decoder::Instruction& inst) {
const auto nibble =
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldSExtract,
{ReadU32(SourceAt(inst, 0)), IR::Value(0u), IR::Value(4u)}));
const auto value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {nibble}));
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::FPMul32, {value, IR::Value::F32(1.0f / 16.0f)}));
}
void Translator::V_CVT_PKRTZ_F16_F32(const Decoder::Instruction& inst) {
const auto lhs = ReadOperand(SourceAt(inst, 0), IR::Type::F32);
const auto rhs = ReadOperand(SourceAt(inst, 1), IR::Type::F32);
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {lhs, rhs}));
}
void Translator::V_CVT_PKNORM_F32(const Decoder::Instruction& inst, IR::ValueOpcode opcode) {
const auto lhs = ReadOperand(SourceAt(inst, 0), IR::Type::F32);
const auto rhs = ReadOperand(SourceAt(inst, 1), IR::Type::F32);
const auto pair = ir.Emit(IR::ValueOpcode::CompositeConstructF32x2, {lhs, rhs});
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {pair}));
}
void Translator::V_CVT_PK_U8_F32(const Decoder::Instruction& inst) {
const auto source = IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32));
const auto byte = ConvertF32ToU32Saturated(source, 255.0f, 255.0f, 255u);
const auto index = ir.BitwiseAnd(ReadU32(SourceAt(inst, 1)), IR::U32(IR::Value(3u)));
const auto shift = ir.ShiftLeftLogical(index, IR::U32(IR::Value(3u)));
const auto mask = ir.ShiftLeftLogical(IR::U32(IR::Value(0xffu)), shift);
const auto base = ir.BitwiseAnd(ReadU32(SourceAt(inst, 2)), ir.BitwiseNot(mask));
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(base, ir.ShiftLeftLogical(byte, shift)));
}
void Translator::V_PACK_B32_F16(const Decoder::Instruction& inst) {
const auto low = ReadF16LaneBits(inst.src0, false);
const auto high =
ir.ShiftLeftLogical(ReadF16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low, high));
}
IR::U32 Translator::PackU16Lanes(IR::U32 low, IR::U32 high) {