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
@@ -57,46 +57,50 @@ bool Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32
return true;
}
bool Translator::TranslateAttributeOperation(const Decoder::Instruction& inst, std::string* error) {
void Translator::V_INTERP_P1_F32() {}
void Translator::V_INTERP_P2_F32(const Decoder::Instruction& inst) {
const auto value = ir.Emit(IR::ValueOpcode::GetAttribute,
{IR::Value(inst.src1.value), IR::Value(inst.src2.value)});
WriteOperand(inst.dst, value);
}
bool Translator::V_INTERP_MOV_F32(const Decoder::Instruction& inst, std::string* error) {
if (inst.src0.value >= 3u) {
return ShaderError::Fail(error,
fmt::format("v_interp_mov_f32 mode {} is reserved at pc 0x{:08x}",
inst.src0.value, inst.pc));
}
const auto value = ir.Emit(
IR::ValueOpcode::GetInterpolationParameter,
{IR::Value(inst.src1.value), IR::Value(inst.src2.value), IR::Value(inst.src0.value)});
WriteOperand(inst.dst, value);
return true;
}
bool Translator::EXP(const Decoder::Instruction& inst, std::string* error) {
uint32_t index = 0;
if (ExportTargetKindFromTarget(inst.exp.target, index) == IR::ExportTargetKind::Unknown) {
return ShaderError::Fail(error,
fmt::format("unsupported EXP target 0x{:02x} at pc 0x{:08x}",
inst.exp.target, inst.pc));
}
std::array<IR::Value, 4> components {IR::Value(0u), IR::Value(0u), IR::Value(0u),
IR::Value(0u)};
for (uint32_t source = 0; source < std::min(inst.src_count, 4u); source++) {
components[source] = ReadRawU32(PlainOperand(SourceAt(inst, source)));
}
const auto data = ir.Emit(IR::ValueOpcode::CompositeConstructU32x4,
{components[0], components[1], components[2], components[3]});
ir.Emit(IR::ValueOpcode::SetAttribute, {data, ir.GetExec()}, AddExportInfo(inst));
return true;
}
bool Translator::EmitInterpolation(const Decoder::Instruction& inst, std::string* error) {
switch (inst.opcode) {
case Decoder::Opcode::V_INTERP_P1_F32: return true;
case Decoder::Opcode::V_INTERP_P2_F32:
case Decoder::Opcode::V_INTERP_MOV_F32: {
if (inst.opcode == Decoder::Opcode::V_INTERP_MOV_F32 && inst.src0.value >= 3u) {
return ShaderError::Fail(
error, fmt::format("v_interp_mov_f32 mode {} is reserved at pc 0x{:08x}",
inst.src0.value, inst.pc));
}
const auto interpolation_mode =
inst.opcode == Decoder::Opcode::V_INTERP_MOV_F32 ? inst.src0.value : 3u;
const auto value =
interpolation_mode < 3u
? ir.Emit(IR::ValueOpcode::GetInterpolationParameter,
{IR::Value(inst.src1.value), IR::Value(inst.src2.value),
IR::Value(interpolation_mode)})
: ir.Emit(IR::ValueOpcode::GetAttribute,
{IR::Value(inst.src1.value), IR::Value(inst.src2.value)});
WriteOperand(inst.dst, value);
return true;
}
case Decoder::Opcode::EXP: {
uint32_t index = 0;
if (ExportTargetKindFromTarget(inst.exp.target, index) ==
IR::ExportTargetKind::Unknown) {
return ShaderError::Fail(
error, fmt::format("unsupported EXP target 0x{:02x} at pc 0x{:08x}",
inst.exp.target, inst.pc));
}
std::array<IR::Value, 4> components {IR::Value(0u), IR::Value(0u), IR::Value(0u),
IR::Value(0u)};
for (uint32_t source = 0; source < std::min(inst.src_count, 4u); source++) {
components[source] = ReadRawU32(PlainOperand(SourceAt(inst, source)));
}
const auto data = ir.Emit(IR::ValueOpcode::CompositeConstructU32x4,
{components[0], components[1], components[2], components[3]});
ir.Emit(IR::ValueOpcode::SetAttribute, {data, ir.GetExec()}, AddExportInfo(inst));
return true;
}
case Decoder::Opcode::V_INTERP_P1_F32: V_INTERP_P1_F32(); return true;
case Decoder::Opcode::V_INTERP_P2_F32: V_INTERP_P2_F32(inst); return true;
case Decoder::Opcode::V_INTERP_MOV_F32: return V_INTERP_MOV_F32(inst, error);
default: return false;
}
}
@@ -2,221 +2,64 @@
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslateIntegerCompare(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode {};
IR::Type type = IR::Type::U32;
switch (inst.opcode) {
case Decoder::Opcode::V_CMP_F_I32:
case Decoder::Opcode::V_CMP_F_U32:
WriteCompareResult(inst.dst, IR::U1(IR::Value(false)));
return true;
case Decoder::Opcode::V_CMP_T_I32:
case Decoder::Opcode::V_CMP_T_U32:
WriteCompareResult(inst.dst, IR::U1(IR::Value(true)));
return true;
case Decoder::Opcode::S_CMP_EQ_U32:
case Decoder::Opcode::V_CMP_EQ_U32:
case Decoder::Opcode::V_CMPX_EQ_U32:
case Decoder::Opcode::S_CMP_EQ_I32:
case Decoder::Opcode::V_CMP_EQ_I32:
case Decoder::Opcode::V_CMPX_EQ_I32: opcode = IR::ValueOpcode::IEqual32; break;
case Decoder::Opcode::S_CMP_LG_U32:
case Decoder::Opcode::V_CMP_NE_U32:
case Decoder::Opcode::V_CMPX_NE_U32:
case Decoder::Opcode::S_CMP_LG_I32:
case Decoder::Opcode::V_CMP_NE_I32:
case Decoder::Opcode::V_CMPX_NE_I32: opcode = IR::ValueOpcode::INotEqual32; break;
case Decoder::Opcode::S_CMP_GT_U32:
case Decoder::Opcode::V_CMP_GT_U32:
case Decoder::Opcode::V_CMPX_GT_U32: opcode = IR::ValueOpcode::UGreaterThan32; break;
case Decoder::Opcode::S_CMP_GE_U32:
case Decoder::Opcode::V_CMP_GE_U32:
case Decoder::Opcode::V_CMPX_GE_U32: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
case Decoder::Opcode::S_CMP_LT_U32:
case Decoder::Opcode::V_CMP_LT_U32:
case Decoder::Opcode::V_CMPX_LT_U32: opcode = IR::ValueOpcode::ULessThan32; break;
case Decoder::Opcode::S_CMP_LE_U32:
case Decoder::Opcode::V_CMP_LE_U32:
case Decoder::Opcode::V_CMPX_LE_U32: opcode = IR::ValueOpcode::ULessThanEqual32; break;
case Decoder::Opcode::S_CMP_GT_I32:
case Decoder::Opcode::V_CMP_GT_I32:
case Decoder::Opcode::V_CMPX_GT_I32: opcode = IR::ValueOpcode::SGreaterThan32; break;
case Decoder::Opcode::S_CMP_GE_I32:
case Decoder::Opcode::V_CMP_GE_I32:
case Decoder::Opcode::V_CMPX_GE_I32: opcode = IR::ValueOpcode::SGreaterThanEqual32; break;
case Decoder::Opcode::S_CMP_LT_I32:
case Decoder::Opcode::V_CMP_LT_I32:
case Decoder::Opcode::V_CMPX_LT_I32: opcode = IR::ValueOpcode::SLessThan32; break;
case Decoder::Opcode::S_CMP_LE_I32:
case Decoder::Opcode::V_CMP_LE_I32:
case Decoder::Opcode::V_CMPX_LE_I32: opcode = IR::ValueOpcode::SLessThanEqual32; break;
case Decoder::Opcode::S_CMP_EQ_U64:
case Decoder::Opcode::V_CMP_EQ_I64:
opcode = IR::ValueOpcode::IEqual64;
type = IR::Type::U64;
break;
case Decoder::Opcode::V_CMP_GT_U64:
opcode = IR::ValueOpcode::UGreaterThan64;
type = IR::Type::U64;
break;
case Decoder::Opcode::S_CMP_LG_U64:
case Decoder::Opcode::V_CMP_NE_U64:
case Decoder::Opcode::V_CMPX_NE_I64:
case Decoder::Opcode::V_CMPX_NE_U64:
opcode = IR::ValueOpcode::INotEqual64;
type = IR::Type::U64;
break;
default: return false;
void Translator::EmitCompareResult(const Decoder::Instruction& inst, IR::U1 value, bool scalar,
bool cmpx) {
if (scalar) {
ir.SetScc(value);
return;
}
const auto masked = ir.LogicalAnd(ir.GetExec(), value);
if (cmpx) {
const auto mask = BallotMask(masked);
ir.SetExec(masked);
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
return;
}
WriteMask(inst.dst, masked);
}
void Translator::EmitCompareConstant(const Decoder::Instruction& inst, bool value, bool scalar,
bool cmpx) {
EmitCompareResult(inst, IR::U1(IR::Value(value)), scalar, cmpx);
}
void Translator::EmitIntegerCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
IR::Type type, bool scalar, bool cmpx) {
const auto lhs = ReadOperand(inst.src0, type);
const auto rhs = ReadOperand(inst.src1, type);
WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {lhs, rhs})));
return true;
EmitCompareResult(inst, IR::U1(ir.Emit(opcode, {lhs, rhs})), scalar, cmpx);
}
bool Translator::TranslateInteger16Compare(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode {};
bool signed_value = false;
switch (inst.opcode) {
case Decoder::Opcode::V_CMP_EQ_U16:
case Decoder::Opcode::V_CMP_EQ_I16: opcode = IR::ValueOpcode::IEqual32; break;
case Decoder::Opcode::V_CMP_NE_U16:
case Decoder::Opcode::V_CMP_NE_I16: opcode = IR::ValueOpcode::INotEqual32; break;
case Decoder::Opcode::V_CMP_GT_U16: opcode = IR::ValueOpcode::UGreaterThan32; break;
case Decoder::Opcode::V_CMP_GE_U16: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
case Decoder::Opcode::V_CMP_LT_U16: opcode = IR::ValueOpcode::ULessThan32; break;
case Decoder::Opcode::V_CMP_LE_U16: opcode = IR::ValueOpcode::ULessThanEqual32; break;
case Decoder::Opcode::V_CMP_GT_I16:
opcode = IR::ValueOpcode::SGreaterThan32;
signed_value = true;
break;
case Decoder::Opcode::V_CMP_GE_I16:
opcode = IR::ValueOpcode::SGreaterThanEqual32;
signed_value = true;
break;
case Decoder::Opcode::V_CMP_LT_I16:
opcode = IR::ValueOpcode::SLessThan32;
signed_value = true;
break;
case Decoder::Opcode::V_CMP_LE_I16:
opcode = IR::ValueOpcode::SLessThanEqual32;
signed_value = true;
break;
default: return false;
}
if (inst.opcode == Decoder::Opcode::V_CMP_EQ_I16 ||
inst.opcode == Decoder::Opcode::V_CMP_NE_I16) {
signed_value = true;
}
WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {ReadU16AsU32(inst.src0, signed_value),
ReadU16AsU32(inst.src1, signed_value)})));
return true;
void Translator::EmitInteger16Compare(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool signed_value, bool cmpx) {
const auto lhs = ReadU16AsU32(inst.src0, signed_value);
const auto rhs = ReadU16AsU32(inst.src1, signed_value);
EmitCompareResult(inst, IR::U1(ir.Emit(opcode, {lhs, rhs})), false, cmpx);
}
bool Translator::TranslateFloatCompare(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode {};
bool half = false;
switch (inst.opcode) {
case Decoder::Opcode::V_CMP_F_F32:
WriteCompareResult(inst.dst, IR::U1(IR::Value(false)));
return true;
case Decoder::Opcode::V_CMP_TRU_F32:
WriteCompareResult(inst.dst, IR::U1(IR::Value(true)));
return true;
case Decoder::Opcode::V_CMP_EQ_F32:
case Decoder::Opcode::V_CMPX_EQ_F32: opcode = IR::ValueOpcode::FPOrdEqual32; break;
case Decoder::Opcode::V_CMP_LG_F32:
case Decoder::Opcode::V_CMPX_LG_F32: opcode = IR::ValueOpcode::FPOrdNotEqual32; break;
case Decoder::Opcode::V_CMP_GT_F32:
case Decoder::Opcode::V_CMPX_GT_F32: opcode = IR::ValueOpcode::FPOrdGreaterThan32; break;
case Decoder::Opcode::V_CMP_GE_F32:
case Decoder::Opcode::V_CMPX_GE_F32:
opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32;
break;
case Decoder::Opcode::V_CMP_LT_F32:
case Decoder::Opcode::V_CMPX_LT_F32: opcode = IR::ValueOpcode::FPOrdLessThan32; break;
case Decoder::Opcode::V_CMP_LE_F32:
case Decoder::Opcode::V_CMPX_LE_F32: opcode = IR::ValueOpcode::FPOrdLessThanEqual32; break;
case Decoder::Opcode::V_CMP_NLG_F32:
case Decoder::Opcode::V_CMPX_NLG_F32: opcode = IR::ValueOpcode::FPUnordEqual32; break;
case Decoder::Opcode::V_CMP_NEQ_F32:
case Decoder::Opcode::V_CMPX_NEQ_F32: opcode = IR::ValueOpcode::FPUnordNotEqual32; break;
case Decoder::Opcode::V_CMP_NLE_F32:
case Decoder::Opcode::V_CMPX_NLE_F32: opcode = IR::ValueOpcode::FPUnordGreaterThan32; break;
case Decoder::Opcode::V_CMP_NLT_F32:
case Decoder::Opcode::V_CMPX_NLT_F32:
opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32;
break;
case Decoder::Opcode::V_CMP_NGE_F32:
case Decoder::Opcode::V_CMPX_NGE_F32: opcode = IR::ValueOpcode::FPUnordLessThan32; break;
case Decoder::Opcode::V_CMP_NGT_F32:
case Decoder::Opcode::V_CMPX_NGT_F32:
opcode = IR::ValueOpcode::FPUnordLessThanEqual32;
break;
case Decoder::Opcode::V_CMP_EQ_F16:
case Decoder::Opcode::V_CMPX_EQ_F16:
opcode = IR::ValueOpcode::FPOrdEqual32;
half = true;
break;
case Decoder::Opcode::V_CMP_LG_F16:
opcode = IR::ValueOpcode::FPOrdNotEqual32;
half = true;
break;
case Decoder::Opcode::V_CMP_GT_F16:
case Decoder::Opcode::V_CMPX_GT_F16:
opcode = IR::ValueOpcode::FPOrdGreaterThan32;
half = true;
break;
case Decoder::Opcode::V_CMP_GE_F16:
case Decoder::Opcode::V_CMPX_GE_F16:
opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32;
half = true;
break;
case Decoder::Opcode::V_CMP_LT_F16:
case Decoder::Opcode::V_CMPX_LT_F16:
opcode = IR::ValueOpcode::FPOrdLessThan32;
half = true;
break;
case Decoder::Opcode::V_CMP_LE_F16:
case Decoder::Opcode::V_CMPX_LE_F16:
opcode = IR::ValueOpcode::FPOrdLessThanEqual32;
half = true;
break;
case Decoder::Opcode::V_CMP_NEQ_F16:
case Decoder::Opcode::V_CMPX_NEQ_F16:
opcode = IR::ValueOpcode::FPUnordNotEqual32;
half = true;
break;
case Decoder::Opcode::V_CMPX_NLT_F16:
opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32;
half = true;
break;
case Decoder::Opcode::V_CMP_O_F32:
case Decoder::Opcode::V_CMP_U_F32: {
const auto lhs = IR::F32(ReadOperand(inst.src0, IR::Type::F32));
const auto rhs = IR::F32(ReadOperand(inst.src1, IR::Type::F32));
const auto unordered = ir.LogicalOr(IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {lhs})),
IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {rhs})));
const auto result =
inst.opcode == Decoder::Opcode::V_CMP_U_F32 ? unordered : ir.LogicalNot(unordered);
WriteCompareResult(inst.dst, result);
return true;
}
case Decoder::Opcode::V_CMP_CLASS_F32: {
const auto value = ReadOperand(inst.src0, IR::Type::F32);
const auto mask = ReadOperand(inst.src1, IR::Type::U32);
WriteCompareResult(inst.dst,
IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask})));
return true;
}
default: return false;
}
void Translator::EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool half, bool cmpx) {
const auto lhs =
half ? IR::Value(ReadF16AsF32(inst.src0)) : ReadOperand(inst.src0, IR::Type::F32);
const auto rhs =
half ? IR::Value(ReadF16AsF32(inst.src1)) : ReadOperand(inst.src1, IR::Type::F32);
WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {lhs, rhs})));
return true;
EmitCompareResult(inst, IR::U1(ir.Emit(opcode, {lhs, rhs})), false, cmpx);
}
void Translator::EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered) {
const auto lhs = IR::F32(ReadOperand(inst.src0, IR::Type::F32));
const auto rhs = IR::F32(ReadOperand(inst.src1, IR::Type::F32));
const auto unordered = ir.LogicalOr(IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {lhs})),
IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {rhs})));
EmitCompareResult(inst, ordered ? ir.LogicalNot(unordered) : unordered, false, false);
}
void Translator::V_CMP_CLASS_F32(const Decoder::Instruction& inst) {
const auto value = ReadOperand(inst.src0, IR::Type::F32);
const auto mask = ReadOperand(inst.src1, IR::Type::U32);
EmitCompareResult(inst, IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask})), false,
false);
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -21,420 +21,350 @@ Decoder::Operand ConditionOperand(Decoder::OperandKind kind) {
} // namespace
bool Translator::TranslateStateOperation(const Decoder::Instruction& inst) {
const auto zero = IR::U32(IR::Value(0u));
switch (inst.opcode) {
case Decoder::Opcode::S_AND_SAVEEXEC_B32:
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B32:
case Decoder::Opcode::S_AND_SAVEEXEC_B64:
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B64:
case Decoder::Opcode::S_ORN2_SAVEEXEC_B64: {
const auto old = ir.GetExec();
const auto src = ReadMask(inst.src0);
IR::U1 result;
switch (inst.opcode) {
case Decoder::Opcode::S_AND_SAVEEXEC_B32:
case Decoder::Opcode::S_AND_SAVEEXEC_B64: result = ir.LogicalAnd(old, src); break;
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B32:
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B64:
result = ir.LogicalAnd(old, ir.LogicalNot(src));
break;
case Decoder::Opcode::S_ORN2_SAVEEXEC_B64:
result = ir.LogicalOr(ir.LogicalNot(old), src);
break;
default: EXIT("invalid saveexec opcode");
}
if (inst.opcode == Decoder::Opcode::S_AND_SAVEEXEC_B64 ||
inst.opcode == Decoder::Opcode::S_ANDN1_SAVEEXEC_B64 ||
inst.opcode == Decoder::Opcode::S_ORN2_SAVEEXEC_B64) {
WriteMask64(inst.dst, old);
} else {
WriteMask(inst.dst, old);
}
const auto mask = BallotMask(result);
ir.SetExec(result);
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
ir.SetScc(result);
return true;
}
case Decoder::Opcode::S_ADD_U32:
case Decoder::Opcode::S_ADDC_U32:
case Decoder::Opcode::V_ADD_I32:
case Decoder::Opcode::V_ADDC_U32: {
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
const auto carry_in = inst.opcode == Decoder::Opcode::S_ADDC_U32
? ConditionBit(ConditionOperand(Decoder::OperandKind::Scc))
: inst.opcode == Decoder::Opcode::V_ADDC_U32
? ConditionBit(inst.src2)
: zero;
const auto add0 = ir.Emit(IR::ValueOpcode::IAddCarry32, {lhs, rhs});
const auto partial = ir.CompositeExtract(add0, 0);
const auto carry0 = ir.CompositeExtract(add0, 1);
const auto add1 = ir.Emit(IR::ValueOpcode::IAddCarry32, {partial, carry_in});
const auto result = ir.CompositeExtract(add1, 0);
const auto carry1 = ir.CompositeExtract(add1, 1);
const auto carry = ir.INotEqual(ir.BitwiseOr(carry0, carry1), zero);
WriteOperand(DestinationOperand(inst), result);
if (inst.opcode == Decoder::Opcode::S_ADD_U32 ||
inst.opcode == Decoder::Opcode::S_ADDC_U32) {
ir.SetScc(carry);
} else {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), carry));
}
return true;
}
case Decoder::Opcode::S_SUB_U32:
case Decoder::Opcode::V_SUB_I32:
case Decoder::Opcode::V_SUBREV_I32: {
const bool reverse = inst.opcode == Decoder::Opcode::V_SUBREV_I32;
const auto lhs = ReadU32(reverse ? inst.src1 : inst.src0);
const auto rhs = ReadU32(reverse ? inst.src0 : inst.src1);
const auto result = ir.ISub(lhs, rhs);
const auto borrow = ir.UGreaterThan(rhs, lhs);
WriteOperand(DestinationOperand(inst), result);
if (inst.opcode == Decoder::Opcode::S_SUB_U32) {
ir.SetScc(borrow);
} else {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
}
return true;
}
case Decoder::Opcode::S_SUBB_U32:
case Decoder::Opcode::V_SUBREV_CO_CI_U32: {
const bool vector = inst.opcode == Decoder::Opcode::V_SUBREV_CO_CI_U32;
const auto lhs = ReadU32(vector ? inst.src1 : inst.src0);
const auto rhs = ReadU32(vector ? inst.src0 : inst.src1);
const auto borrow_in = vector
? ConditionBit(inst.src2)
: ConditionBit(ConditionOperand(Decoder::OperandKind::Scc));
const auto partial = ir.ISub(lhs, rhs);
const auto result = ir.ISub(partial, borrow_in);
const auto borrow0 = ir.UGreaterThan(rhs, lhs);
const auto borrow1 = ir.UGreaterThan(borrow_in, partial);
const auto borrow = ir.LogicalOr(borrow0, borrow1);
WriteOperand(DestinationOperand(inst), result);
if (vector) {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
} else {
ir.SetScc(borrow);
}
return true;
}
case Decoder::Opcode::S_ADD_I32:
case Decoder::Opcode::S_SUB_I32: {
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
const bool subtract = inst.opcode == Decoder::Opcode::S_SUB_I32;
const auto result = subtract ? ir.ISub(lhs, rhs) : ir.IAdd(lhs, rhs);
const auto shift = IR::U32(IR::Value(31u));
const auto lhs_sign = ir.ShiftRightLogical(lhs, shift);
const auto rhs_sign = ir.ShiftRightLogical(rhs, shift);
const auto out_sign = ir.ShiftRightLogical(result, shift);
const auto inputs =
subtract ? ir.INotEqual(lhs_sign, rhs_sign) : ir.IEqual(lhs_sign, rhs_sign);
const auto changed = ir.INotEqual(lhs_sign, out_sign);
WriteOperand(DestinationOperand(inst), result);
ir.SetScc(ir.LogicalAnd(inputs, changed));
return true;
}
case Decoder::Opcode::S_LSHL1_ADD_U32:
case Decoder::Opcode::S_LSHL2_ADD_U32:
case Decoder::Opcode::S_LSHL3_ADD_U32:
case Decoder::Opcode::S_LSHL4_ADD_U32: {
const uint32_t shift_amount = 1u + static_cast<uint32_t>(inst.opcode) -
static_cast<uint32_t>(Decoder::Opcode::S_LSHL1_ADD_U32);
const auto lhs = ReadU32(inst.src0);
const auto shift = IR::U32(IR::Value(shift_amount));
const auto rhs = ReadU32(inst.src1);
const auto shifted = ir.ShiftLeftLogical(lhs, shift);
const auto result = ir.IAdd(shifted, rhs);
const auto add_carry = ir.ULessThan(result, shifted);
const auto inverse_shift = ir.ISub(IR::U32(IR::Value(32u)), shift);
const auto shifted_out = ir.ShiftRightLogical(lhs, inverse_shift);
const auto shift_carry = ir.INotEqual(shifted_out, zero);
WriteOperand(DestinationOperand(inst), result);
ir.SetScc(ir.LogicalOr(add_carry, shift_carry));
return true;
}
case Decoder::Opcode::S_MIN_I32:
case Decoder::Opcode::S_MAX_I32:
case Decoder::Opcode::S_MIN_U32:
case Decoder::Opcode::S_MAX_U32: {
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
IR::ValueOpcode value_opcode;
IR::ValueOpcode compare_opcode;
switch (inst.opcode) {
case Decoder::Opcode::S_MIN_I32:
value_opcode = IR::ValueOpcode::SMin32;
compare_opcode = IR::ValueOpcode::SLessThan32;
break;
case Decoder::Opcode::S_MAX_I32:
value_opcode = IR::ValueOpcode::SMax32;
compare_opcode = IR::ValueOpcode::SGreaterThan32;
break;
case Decoder::Opcode::S_MIN_U32:
value_opcode = IR::ValueOpcode::UMin32;
compare_opcode = IR::ValueOpcode::ULessThan32;
break;
case Decoder::Opcode::S_MAX_U32:
value_opcode = IR::ValueOpcode::UMax32;
compare_opcode = IR::ValueOpcode::UGreaterThan32;
break;
default: EXIT("invalid scalar min/max opcode");
}
WriteOperand(DestinationOperand(inst), ir.Emit(value_opcode, {lhs, rhs}));
ir.SetScc(IR::U1(ir.Emit(compare_opcode, {lhs, rhs})));
return true;
}
default: return false;
void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation,
bool negate_exec, bool negate_source, bool write_64) {
const auto old = ir.GetExec();
const auto src = ReadMask(inst.src0);
const auto lhs = negate_exec ? ir.LogicalNot(old) : old;
const auto rhs = negate_source ? ir.LogicalNot(src) : src;
const auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
if (write_64) {
WriteMask64(inst.dst, old);
} else {
WriteMask(inst.dst, old);
}
const auto mask = BallotMask(result);
ir.SetExec(result);
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
ir.SetScc(result);
}
void Translator::ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in) {
const auto zero = IR::U32(IR::Value(0u));
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
const auto carry_in = !use_carry_in ? zero
: vector ? ConditionBit(inst.src2)
: ConditionBit(ConditionOperand(Decoder::OperandKind::Scc));
const auto add0 = ir.Emit(IR::ValueOpcode::IAddCarry32, {lhs, rhs});
const auto partial = ir.CompositeExtract(add0, 0);
const auto carry0 = ir.CompositeExtract(add0, 1);
const auto add1 = ir.Emit(IR::ValueOpcode::IAddCarry32, {partial, carry_in});
const auto result = ir.CompositeExtract(add1, 0);
const auto carry1 = ir.CompositeExtract(add1, 1);
const auto carry = ir.INotEqual(ir.BitwiseOr(carry0, carry1), zero);
WriteOperand(DestinationOperand(inst), result);
if (vector) {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), carry));
} else {
ir.SetScc(carry);
}
}
bool Translator::TranslateControlOperation(const Decoder::Instruction& inst) {
switch (inst.opcode) {
case Decoder::Opcode::V_NOP: return true;
case Decoder::Opcode::S_NOP:
case Decoder::Opcode::S_SETREG_B32:
case Decoder::Opcode::S_SLEEP:
case Decoder::Opcode::S_TRAP: ir.Emit(IR::ValueOpcode::ControlNop); return true;
case Decoder::Opcode::S_WAITCNT:
case Decoder::Opcode::S_WAITCNT_DEPCTR: ir.Emit(IR::ValueOpcode::Waitcnt); return true;
case Decoder::Opcode::S_BARRIER: ir.Emit(IR::ValueOpcode::Barrier); return true;
case Decoder::Opcode::S_SENDMSG: ir.Emit(IR::ValueOpcode::Sendmsg); return true;
case Decoder::Opcode::S_TTRACEDATA: ir.Emit(IR::ValueOpcode::TtraceData); return true;
case Decoder::Opcode::S_INST_PREFETCH: ir.Emit(IR::ValueOpcode::InstPrefetch); return true;
default: return false;
void Translator::SUB_U32(const Decoder::Instruction& inst, bool vector, bool reverse) {
const auto lhs = ReadU32(reverse ? inst.src1 : inst.src0);
const auto rhs = ReadU32(reverse ? inst.src0 : inst.src1);
const auto result = ir.ISub(lhs, rhs);
const auto borrow = ir.UGreaterThan(rhs, lhs);
WriteOperand(DestinationOperand(inst), result);
if (vector) {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
} else {
ir.SetScc(borrow);
}
}
bool Translator::TranslateMove(const Decoder::Instruction& inst, std::string* error) {
switch (inst.opcode) {
case Decoder::Opcode::S_GETPC_B64: {
const auto base = IR::U64(ir.Emit(IR::ValueOpcode::GetShaderBase));
const auto address = IR::U64(ir.Emit(
IR::ValueOpcode::IAdd64, {base, IR::Value(static_cast<uint64_t>(inst.pc) + 4u)}));
if (inst.dst.kind == Decoder::OperandKind::Null) {
return true;
}
auto high = PlainOperand(inst.dst);
switch (inst.dst.kind) {
case Decoder::OperandKind::Sgpr:
if (inst.dst.reg < 105u) {
high.reg++;
} else if (inst.dst.reg == 105u) {
high.kind = Decoder::OperandKind::VccLo;
high.reg = 0u;
} else {
if (error != nullptr) {
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
}
return false;
}
break;
case Decoder::OperandKind::VccLo: high.kind = Decoder::OperandKind::VccHi; break;
case Decoder::OperandKind::M0: high.kind = Decoder::OperandKind::Null; break;
case Decoder::OperandKind::ExecLo: high.kind = Decoder::OperandKind::ExecHi; break;
default:
if (error != nullptr) {
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
}
return false;
}
const auto words = ExtractU64(address);
WriteOperand(inst.dst, words[0]);
WriteOperand(high, words[1]);
return true;
}
case Decoder::Opcode::S_CSELECT_B32: {
const auto result = ir.Select(ir.GetScc(), ReadU32(inst.src0), ReadU32(inst.src1));
WriteOperand(DestinationOperand(inst), result);
return true;
}
case Decoder::Opcode::S_CSELECT_B64: {
const auto condition = ir.GetScc();
const auto lhs = ReadU32Pair(inst.src0);
const auto rhs = ReadU32Pair(inst.src1);
const auto selected_mask = IR::U1(ir.Emit(
IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(inst.src1)}));
const auto selected_mask_valid =
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
{condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)}));
if (IsExecOrVcc(inst.dst)) {
WriteMask64(inst.dst, selected_mask);
return true;
}
WriteU32Pair(inst.dst, {ir.Select(condition, lhs[0], rhs[0]),
ir.Select(condition, lhs[1], rhs[1])});
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, selected_mask);
ir.SetScalarMaskTag(dst, selected_mask_valid);
}
return true;
}
case Decoder::Opcode::S_MOV_B32:
case Decoder::Opcode::S_MOVK_I32:
case Decoder::Opcode::V_MOV_B32:
if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
WriteMask(inst.dst, ReadMask(inst.src0));
} else if (inst.opcode == Decoder::Opcode::V_MOV_B32 &&
(inst.src0.negate || inst.src0.absolute)) {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::F32));
void Translator::SUBB_U32(const Decoder::Instruction& inst, bool vector) {
const auto lhs = ReadU32(vector ? inst.src1 : inst.src0);
const auto rhs = ReadU32(vector ? inst.src0 : inst.src1);
const auto borrow_in = vector ? ConditionBit(inst.src2)
: ConditionBit(ConditionOperand(Decoder::OperandKind::Scc));
const auto partial = ir.ISub(lhs, rhs);
const auto result = ir.ISub(partial, borrow_in);
const auto borrow0 = ir.UGreaterThan(rhs, lhs);
const auto borrow1 = ir.UGreaterThan(borrow_in, partial);
const auto borrow = ir.LogicalOr(borrow0, borrow1);
WriteOperand(DestinationOperand(inst), result);
if (vector) {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
} else {
ir.SetScc(borrow);
}
}
void Translator::S_ADD_SUB_I32(const Decoder::Instruction& inst, bool subtract) {
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
const auto result = subtract ? ir.ISub(lhs, rhs) : ir.IAdd(lhs, rhs);
const auto shift = IR::U32(IR::Value(31u));
const auto lhs_sign = ir.ShiftRightLogical(lhs, shift);
const auto rhs_sign = ir.ShiftRightLogical(rhs, shift);
const auto out_sign = ir.ShiftRightLogical(result, shift);
const auto inputs = subtract ? ir.INotEqual(lhs_sign, rhs_sign) : ir.IEqual(lhs_sign, rhs_sign);
const auto changed = ir.INotEqual(lhs_sign, out_sign);
WriteOperand(DestinationOperand(inst), result);
ir.SetScc(ir.LogicalAnd(inputs, changed));
}
void Translator::S_LSHL_ADD_U32(const Decoder::Instruction& inst, uint32_t shift_amount) {
const auto lhs = ReadU32(inst.src0);
const auto shift = IR::U32(IR::Value(shift_amount));
const auto rhs = ReadU32(inst.src1);
const auto shifted = ir.ShiftLeftLogical(lhs, shift);
const auto result = ir.IAdd(shifted, rhs);
const auto add_carry = ir.ULessThan(result, shifted);
const auto inverse_shift = ir.ISub(IR::U32(IR::Value(32u)), shift);
const auto shifted_out = ir.ShiftRightLogical(lhs, inverse_shift);
const auto shift_carry = ir.INotEqual(shifted_out, IR::U32(IR::Value(0u)));
WriteOperand(DestinationOperand(inst), result);
ir.SetScc(ir.LogicalOr(add_carry, shift_carry));
}
void Translator::ScalarMinMax32(const Decoder::Instruction& inst, IR::ValueOpcode value_opcode,
IR::ValueOpcode compare_opcode) {
const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
WriteOperand(DestinationOperand(inst), ir.Emit(value_opcode, {lhs, rhs}));
ir.SetScc(IR::U1(ir.Emit(compare_opcode, {lhs, rhs})));
}
void Translator::EmitControlNop() {
ir.Emit(IR::ValueOpcode::ControlNop);
}
void Translator::EmitWaitcnt() {
ir.Emit(IR::ValueOpcode::Waitcnt);
}
void Translator::S_BARRIER() {
ir.Emit(IR::ValueOpcode::Barrier);
}
void Translator::S_SENDMSG() {
ir.Emit(IR::ValueOpcode::Sendmsg);
}
void Translator::S_TTRACEDATA() {
ir.Emit(IR::ValueOpcode::TtraceData);
}
void Translator::S_INST_PREFETCH() {
ir.Emit(IR::ValueOpcode::InstPrefetch);
}
bool Translator::S_GETPC_B64(const Decoder::Instruction& inst, std::string* error) {
const auto base = IR::U64(ir.Emit(IR::ValueOpcode::GetShaderBase));
const auto address = IR::U64(
ir.Emit(IR::ValueOpcode::IAdd64, {base, IR::Value(static_cast<uint64_t>(inst.pc) + 4u)}));
if (inst.dst.kind == Decoder::OperandKind::Null) {
return true;
}
auto high = PlainOperand(inst.dst);
switch (inst.dst.kind) {
case Decoder::OperandKind::Sgpr:
if (inst.dst.reg < 105u) {
high.reg++;
} else if (inst.dst.reg == 105u) {
high.kind = Decoder::OperandKind::VccLo;
high.reg = 0u;
} else {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::U32));
}
return true;
case Decoder::Opcode::S_MOV_B64: {
if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) {
WriteMask64(inst.dst, ReadMask(inst.src0));
return true;
}
const bool scalar_copy = inst.dst.kind == Decoder::OperandKind::Sgpr &&
inst.src0.kind == Decoder::OperandKind::Sgpr;
IR::U1 source_mask;
IR::U1 source_mask_valid;
if (scalar_copy) {
source_mask = ir.GetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.src0.reg));
source_mask_valid = ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(inst.src0.reg));
}
WriteU32Pair(inst.dst, ReadU32Pair(inst.src0));
if (scalar_copy) {
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask);
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask_valid);
}
return true;
}
case Decoder::Opcode::S_WQM_B64: {
if (!IsExecOrVcc(inst.dst) && !IsExecOrVcc(inst.src0)) {
const auto mask_valid = ReadMaskValid(inst.src0);
const auto invocation_result =
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
const auto result = IR::U64(
ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
WriteOperand(DestinationOperand(inst), result);
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero = IR::U1(
ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}));
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
{mask_valid, invocation_result, raw_nonzero})));
} else {
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
if (error != nullptr) {
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
}
return true;
return false;
}
const auto result = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
WriteMask64(inst.dst, result);
ir.SetScc(result);
return true;
}
default: return false;
break;
case Decoder::OperandKind::VccLo: high.kind = Decoder::OperandKind::VccHi; break;
case Decoder::OperandKind::M0: high.kind = Decoder::OperandKind::Null; break;
case Decoder::OperandKind::ExecLo: high.kind = Decoder::OperandKind::ExecHi; break;
default:
if (error != nullptr) {
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
}
return false;
}
const auto words = ExtractU64(address);
WriteOperand(inst.dst, words[0]);
WriteOperand(high, words[1]);
return true;
}
void Translator::S_CSELECT_B32(const Decoder::Instruction& inst) {
const auto result = ir.Select(ir.GetScc(), ReadU32(inst.src0), ReadU32(inst.src1));
WriteOperand(DestinationOperand(inst), result);
}
void Translator::S_CSELECT_B64(const Decoder::Instruction& inst) {
const auto condition = ir.GetScc();
const auto lhs = ReadU32Pair(inst.src0);
const auto rhs = ReadU32Pair(inst.src1);
const auto selected_mask = IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(inst.src1)}));
const auto selected_mask_valid =
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
{condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)}));
if (IsExecOrVcc(inst.dst)) {
WriteMask64(inst.dst, selected_mask);
return;
}
WriteU32Pair(inst.dst,
{ir.Select(condition, lhs[0], rhs[0]), ir.Select(condition, lhs[1], rhs[1])});
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, selected_mask);
ir.SetScalarMaskTag(dst, selected_mask_valid);
}
}
bool Translator::TranslateLaneOperation(const Decoder::Instruction& inst, std::string* error) {
void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers) {
if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
WriteMask(inst.dst, ReadMask(inst.src0));
} else if (apply_float_modifiers && (inst.src0.negate || inst.src0.absolute)) {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::F32));
} else {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::U32));
}
}
void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) {
WriteMask64(inst.dst, ReadMask(inst.src0));
return;
}
const bool scalar_copy =
inst.dst.kind == Decoder::OperandKind::Sgpr && inst.src0.kind == Decoder::OperandKind::Sgpr;
IR::U1 source_mask;
IR::U1 source_mask_valid;
if (scalar_copy) {
source_mask = ir.GetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.src0.reg));
source_mask_valid = ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(inst.src0.reg));
}
WriteU32Pair(inst.dst, ReadU32Pair(inst.src0));
if (scalar_copy) {
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask);
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask_valid);
}
}
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
if (!IsExecOrVcc(inst.dst) && !IsExecOrVcc(inst.src0)) {
const auto mask_valid = ReadMaskValid(inst.src0);
const auto invocation_result =
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
const auto result =
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
WriteOperand(DestinationOperand(inst), result);
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero =
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}));
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
} else {
ir.SetScc(
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
}
return;
}
const auto result = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
WriteMask64(inst.dst, result);
ir.SetScc(result);
}
bool Translator::V_MOVRELS_B32(const Decoder::Instruction& inst, std::string* error) {
if (inst.dst.kind != Decoder::OperandKind::Vgpr ||
inst.src0.kind != Decoder::OperandKind::Vgpr) {
if (error != nullptr) {
*error = "V_MOVRELS_B32 requires VGPR source and destination";
}
return false;
}
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate || inst.src0.absolute ||
inst.src0.dpp) {
if (error != nullptr) {
*error = "V_MOVRELS_B32 modifiers are not implemented";
}
return false;
}
const auto base = inst.src0.reg;
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
IR::U32(IR::Value(0xffu)));
auto selected = ir.GetVectorReg(static_cast<IR::VectorReg>(base));
for (uint32_t index = base + 1u; index < current_vector_limit; index++) {
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
selected = ir.Select(match, ir.GetVectorReg(static_cast<IR::VectorReg>(index)), selected);
}
WriteOperand(DestinationOperand(inst), selected);
return true;
}
bool Translator::V_MOVRELD_B32(const Decoder::Instruction& inst, std::string* error) {
if (inst.dst.kind != Decoder::OperandKind::Vgpr) {
if (error != nullptr) {
*error = "V_MOVRELD_B32 requires VGPR destination";
}
return false;
}
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate || inst.src0.absolute ||
inst.src0.dpp) {
if (error != nullptr) {
*error = "V_MOVRELD_B32 modifiers are not implemented";
}
return false;
}
const auto base = inst.dst.reg;
const auto value = ReadU32(inst.src0);
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
IR::U32(IR::Value(0xffu)));
for (uint32_t index = base; index < current_vector_limit; index++) {
const auto reg = static_cast<IR::VectorReg>(index);
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
const auto write = ir.LogicalAnd(ir.GetExec(), match);
ir.SetVectorReg(reg, ir.Select(write, value, ir.GetVectorReg(reg)));
}
return true;
}
void Translator::V_READFIRSTLANE_B32(const Decoder::Instruction& inst) {
const auto result = ir.Emit(IR::ValueOpcode::ReadFirstLane, {ReadU32(inst.src0), ir.GetExec()});
WriteOperand(DestinationOperand(inst), result);
}
void Translator::V_READLANE_B32(const Decoder::Instruction& inst) {
const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u));
switch (inst.opcode) {
case Decoder::Opcode::V_MOVRELS_B32: {
if (inst.dst.kind != Decoder::OperandKind::Vgpr ||
inst.src0.kind != Decoder::OperandKind::Vgpr) {
if (error != nullptr) {
*error = "V_MOVRELS_B32 requires VGPR source and destination";
}
return false;
}
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate ||
inst.src0.absolute || inst.src0.dpp) {
if (error != nullptr) {
*error = "V_MOVRELS_B32 modifiers are not implemented";
}
return false;
}
const auto base = inst.src0.reg;
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
IR::U32(IR::Value(0xffu)));
auto selected = ir.GetVectorReg(static_cast<IR::VectorReg>(base));
for (uint32_t index = base + 1u; index < current_vector_limit; index++) {
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
selected =
ir.Select(match, ir.GetVectorReg(static_cast<IR::VectorReg>(index)), selected);
}
WriteOperand(DestinationOperand(inst), selected);
return true;
}
case Decoder::Opcode::V_MOVRELD_B32: {
if (inst.dst.kind != Decoder::OperandKind::Vgpr) {
if (error != nullptr) {
*error = "V_MOVRELD_B32 requires VGPR destination";
}
return false;
}
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate ||
inst.src0.absolute || inst.src0.dpp) {
if (error != nullptr) {
*error = "V_MOVRELD_B32 modifiers are not implemented";
}
return false;
}
const auto base = inst.dst.reg;
const auto value = ReadU32(inst.src0);
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
IR::U32(IR::Value(0xffu)));
for (uint32_t index = base; index < current_vector_limit; index++) {
const auto reg = static_cast<IR::VectorReg>(index);
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
const auto write = ir.LogicalAnd(ir.GetExec(), match);
ir.SetVectorReg(reg, ir.Select(write, value, ir.GetVectorReg(reg)));
}
return true;
}
case Decoder::Opcode::V_READFIRSTLANE_B32: {
const auto result =
ir.Emit(IR::ValueOpcode::ReadFirstLane, {ReadU32(inst.src0), ir.GetExec()});
WriteOperand(DestinationOperand(inst), result);
return true;
}
case Decoder::Opcode::V_READLANE_B32: {
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src0), lane}));
return true;
}
case Decoder::Opcode::V_WRITELANE_B32: {
EXIT_IF(inst.dst.kind != Decoder::OperandKind::Vgpr);
const auto reg = static_cast<IR::VectorReg>(inst.dst.reg);
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
const auto result = IR::U32(ir.Emit(IR::ValueOpcode::WriteLane,
{ir.GetVectorReg(reg), ReadU32(inst.src0), lane}));
ir.SetVectorReg(reg, result);
return true;
}
case Decoder::Opcode::V_PERMLANE16_B32:
case Decoder::Opcode::V_PERMLANEX16_B32: {
const IR::PermlaneFlags flags {
.x16 = inst.opcode == Decoder::Opcode::V_PERMLANEX16_B32,
.fetch_inactive = inst.dst.op_sel,
.bound_control = inst.dst.op_sel_hi,
};
const auto result = ir.Emit(
IR::ValueOpcode::Permlane16U32,
{ReadU32(inst.src0), ReadU32(inst.src1), ReadU32(inst.src2), ir.GetExec()}, flags);
auto dst = DestinationOperand(inst);
dst.op_sel = false;
dst.op_sel_hi = false;
WriteOperand(dst, result);
return true;
}
default: return false;
}
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src0), lane}));
}
void Translator::V_WRITELANE_B32(const Decoder::Instruction& inst) {
EXIT_IF(inst.dst.kind != Decoder::OperandKind::Vgpr);
const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u));
const auto reg = static_cast<IR::VectorReg>(inst.dst.reg);
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
const auto result = IR::U32(
ir.Emit(IR::ValueOpcode::WriteLane, {ir.GetVectorReg(reg), ReadU32(inst.src0), lane}));
ir.SetVectorReg(reg, result);
}
void Translator::V_PERMLANE16_B32(const Decoder::Instruction& inst, bool x16) {
const IR::PermlaneFlags flags {
.x16 = x16,
.fetch_inactive = inst.dst.op_sel,
.bound_control = inst.dst.op_sel_hi,
};
const auto result =
ir.Emit(IR::ValueOpcode::Permlane16U32,
{ReadU32(inst.src0), ReadU32(inst.src1), ReadU32(inst.src2), ir.GetExec()}, flags);
auto dst = DestinationOperand(inst);
dst.op_sel = false;
dst.op_sel_hi = false;
WriteOperand(dst, result);
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -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) {
@@ -2,94 +2,6 @@
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslateU64MaskOperation(const Decoder::Instruction& inst) {
switch (inst.opcode) {
case Decoder::Opcode::S_AND_B64:
case Decoder::Opcode::S_ANDN2_B64:
case Decoder::Opcode::S_OR_B64:
case Decoder::Opcode::S_ORN2_B64:
case Decoder::Opcode::S_XOR_B64:
case Decoder::Opcode::S_NAND_B64:
case Decoder::Opcode::S_NOR_B64:
case Decoder::Opcode::S_XNOR_B64:
case Decoder::Opcode::S_NOT_B64: break;
default: return false;
}
const auto invocation_result = EvaluateU64Mask(inst);
const auto is_exec_or_vcc = [](const Decoder::Operand& operand) {
switch (operand.kind) {
case Decoder::OperandKind::ExecLo:
case Decoder::OperandKind::ExecHi:
case Decoder::OperandKind::VccLo:
case Decoder::OperandKind::VccHi: return true;
default: return false;
}
};
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src0) ||
(inst.src_count > 1u && is_exec_or_vcc(inst.src1))) {
WriteMask64(inst.dst, invocation_result);
ir.SetScc(invocation_result);
return true;
}
const auto lhs = ReadU32Pair(inst.src0);
auto mask_valid = ReadMaskValid(inst.src0);
if (inst.src_count > 1u) {
mask_valid = ir.LogicalAnd(mask_valid, ReadMaskValid(inst.src1));
}
std::array<IR::U32, 2> result;
if (inst.opcode == Decoder::Opcode::S_NOT_B64) {
result = {ir.BitwiseNot(lhs[0]), ir.BitwiseNot(lhs[1])};
} else {
const auto rhs = ReadU32Pair(inst.src1);
for (uint32_t component = 0; component < 2u; component++) {
switch (inst.opcode) {
case Decoder::Opcode::S_AND_B64:
result[component] = ir.BitwiseAnd(lhs[component], rhs[component]);
break;
case Decoder::Opcode::S_ANDN2_B64:
result[component] =
ir.BitwiseAnd(lhs[component], ir.BitwiseNot(rhs[component]));
break;
case Decoder::Opcode::S_OR_B64:
result[component] = ir.BitwiseOr(lhs[component], rhs[component]);
break;
case Decoder::Opcode::S_ORN2_B64:
result[component] = ir.BitwiseOr(lhs[component], ir.BitwiseNot(rhs[component]));
break;
case Decoder::Opcode::S_XOR_B64:
result[component] = ir.BitwiseXor(lhs[component], rhs[component]);
break;
case Decoder::Opcode::S_NAND_B64:
result[component] =
ir.BitwiseNot(ir.BitwiseAnd(lhs[component], rhs[component]));
break;
case Decoder::Opcode::S_NOR_B64:
result[component] = ir.BitwiseNot(ir.BitwiseOr(lhs[component], rhs[component]));
break;
case Decoder::Opcode::S_XNOR_B64:
result[component] =
ir.BitwiseNot(ir.BitwiseXor(lhs[component], rhs[component]));
break;
default: return false;
}
}
}
WriteU32Pair(inst.dst, result);
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero =
ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u)));
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
} else {
ir.SetScc(ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u))));
}
return true;
}
bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::string* error) {
current_opcode = inst.opcode;
current_pc = inst.pc;
@@ -106,49 +18,33 @@ bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::str
*error = "unsupported decoded instruction: " + Decoder::InstructionToString(inst);
}
return false;
case Decoder::Opcode::S_GETPC_B64: return TranslateMove(inst, error);
case Decoder::Opcode::V_MOVRELS_B32:
case Decoder::Opcode::V_MOVRELD_B32: return TranslateLaneOperation(inst, error);
default: break;
}
bool translated = false;
switch (inst.family) {
case Decoder::Family::SOP1:
case Decoder::Family::SOP2:
case Decoder::Family::SOPK:
case Decoder::Family::SOPC:
case Decoder::Family::SOPP: translated = EmitScalar(inst, error); break;
case Decoder::Family::VOP1:
case Decoder::Family::VOP2:
case Decoder::Family::VOP3:
case Decoder::Family::VOP3P:
case Decoder::Family::VOPC: translated = EmitVector(inst, error); break;
case Decoder::Family::SMEM:
case Decoder::Family::MUBUF:
case Decoder::Family::MTBUF:
case Decoder::Family::FLAT:
case Decoder::Family::DS:
case Decoder::Family::MIMG: return TranslateMemoryOperation(inst, error);
case Decoder::Family::EXP: return TranslateAttributeOperation(inst, error);
case Decoder::Family::MIMG: translated = EmitMemory(inst, error); break;
case Decoder::Family::VINTRP: translated = EmitInterpolation(inst, error); break;
case Decoder::Family::EXP: translated = EXP(inst, error); break;
default: break;
}
switch (inst.opcode) {
case Decoder::Opcode::V_INTERP_P1_F32:
case Decoder::Opcode::V_INTERP_P2_F32:
case Decoder::Opcode::V_INTERP_MOV_F32: return TranslateAttributeOperation(inst, error);
case Decoder::Opcode::S_BRANCH:
case Decoder::Opcode::S_CBRANCH_SCC0:
case Decoder::Opcode::S_CBRANCH_SCC1:
case Decoder::Opcode::S_CBRANCH_VCCZ:
case Decoder::Opcode::S_CBRANCH_VCCNZ:
case Decoder::Opcode::S_CBRANCH_EXECZ:
case Decoder::Opcode::S_CBRANCH_EXECNZ:
case Decoder::Opcode::S_SETPC_B64:
case Decoder::Opcode::S_ENDPGM: return true;
default: break;
}
const bool translated = TranslateControlOperation(inst) || TranslateStateOperation(inst) ||
TranslateMove(inst, error) || TranslateLaneOperation(inst, error) ||
TranslateIntegerCompare(inst) || TranslateInteger16Compare(inst) ||
TranslateFloatCompare(inst) || TranslateConversion(inst) ||
TranslateInteger16Operation(inst) || TranslatePackedInteger16(inst) ||
TranslatePackedFloat16(inst) || TranslateFloat16Operation(inst) ||
TranslateFloatOperation(inst) || TranslateU64MaskOperation(inst) ||
TranslateSimpleInteger(inst) || TranslateComposedInteger(inst) ||
TranslateExtendedInteger(inst);
if (!translated && error != nullptr) {
if (!translated && error != nullptr && error->empty()) {
*error = "decoded opcode has no IR translation";
}
return translated;
@@ -5,22 +5,13 @@
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslatePackedFloat16(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode;
switch (inst.opcode) {
case Decoder::Opcode::V_PK_ADD_F16: opcode = IR::ValueOpcode::FPAdd32; break;
case Decoder::Opcode::V_PK_MUL_F16: opcode = IR::ValueOpcode::FPMul32; break;
case Decoder::Opcode::V_PK_MIN_F16: opcode = IR::ValueOpcode::FPMin32; break;
case Decoder::Opcode::V_PK_MAX_F16: opcode = IR::ValueOpcode::FPMax32; break;
case Decoder::Opcode::V_PK_FMA_F16: opcode = IR::ValueOpcode::FPFma32; break;
case Decoder::Opcode::V_PK_FMAC_F16: opcode = IR::ValueOpcode::FPFma32; break;
default: return false;
}
bool Translator::PackedFloat16(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool accumulator, bool quiet_snan) {
const auto translate_lane = [&](bool high) {
const auto lhs = ReadF16LaneAsF32(inst.src0, high, true);
const auto rhs = ReadF16LaneAsF32(inst.src1, high, true);
IR::F32 result;
if (inst.opcode == Decoder::Opcode::V_PK_FMAC_F16) {
if (accumulator) {
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.dst, high, true)}));
} else if (inst.src_count == 3u) {
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src2, high, true)}));
@@ -33,9 +24,8 @@ bool Translator::TranslatePackedFloat16(const Decoder::Instruction& inst) {
raw.omod = 0u;
raw.clamp = false;
auto result = PackHalf2x16(translate_lane(false), translate_lane(true));
if (inst.opcode == Decoder::Opcode::V_PK_MIN_F16 ||
inst.opcode == Decoder::Opcode::V_PK_MAX_F16) {
const auto quiet_snan = [&](const Decoder::Operand& operand, bool high) {
if (quiet_snan) {
const auto quiet_snan_lane = [&](const Decoder::Operand& operand, bool high) {
const auto bits = ReadU16LaneAsU32(operand, high, false);
const auto exponent = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x7c00u)));
const auto payload = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x01ffu)));
@@ -44,8 +34,8 @@ bool Translator::TranslatePackedFloat16(const Decoder::Instruction& inst) {
return std::pair {snan, ir.BitwiseOr(bits, IR::U32(IR::Value(0x0200u)))};
};
const auto override_lane = [&](bool high) {
const auto [lhs_snan, lhs_quiet] = quiet_snan(inst.src0, high);
const auto [rhs_snan, rhs_quiet] = quiet_snan(inst.src1, high);
const auto [lhs_snan, lhs_quiet] = quiet_snan_lane(inst.src0, high);
const auto [rhs_snan, rhs_quiet] = quiet_snan_lane(inst.src1, high);
const auto normal = high ? ir.ShiftRightLogical(result, IR::U32(IR::Value(16u)))
: ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu)));
return ir.Select(lhs_snan, lhs_quiet, ir.Select(rhs_snan, rhs_quiet, normal));
@@ -56,176 +46,130 @@ bool Translator::TranslatePackedFloat16(const Decoder::Instruction& inst) {
return true;
}
bool Translator::TranslateFloat16Operation(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode;
switch (inst.opcode) {
case Decoder::Opcode::V_ADD_F16: opcode = IR::ValueOpcode::FPAdd32; break;
case Decoder::Opcode::V_SUB_F16: opcode = IR::ValueOpcode::FPSub32; break;
case Decoder::Opcode::V_SUBREV_F16: opcode = IR::ValueOpcode::FPSub32; break;
case Decoder::Opcode::V_MUL_F16: opcode = IR::ValueOpcode::FPMul32; break;
case Decoder::Opcode::V_MIN_F16: opcode = IR::ValueOpcode::FPMin32; break;
case Decoder::Opcode::V_MAX_F16: opcode = IR::ValueOpcode::FPMax32; break;
case Decoder::Opcode::V_FMAC_F16:
case Decoder::Opcode::V_FMAMK_F16:
case Decoder::Opcode::V_FMAAK_F16:
case Decoder::Opcode::V_FMA_F16:
case Decoder::Opcode::V_MAD_MIXLO_F16: opcode = IR::ValueOpcode::FPFma32; break;
case Decoder::Opcode::V_MAD_MIXHI_F16: opcode = IR::ValueOpcode::FPFma32; break;
case Decoder::Opcode::V_RCP_F16: opcode = IR::ValueOpcode::FPRecip32; break;
case Decoder::Opcode::V_SQRT_F16: opcode = IR::ValueOpcode::FPSqrt; break;
case Decoder::Opcode::V_RSQ_F16: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
case Decoder::Opcode::V_LOG_F16: opcode = IR::ValueOpcode::FPLog2; break;
case Decoder::Opcode::V_EXP_F16: opcode = IR::ValueOpcode::FPExp2; break;
case Decoder::Opcode::V_FLOOR_F16: opcode = IR::ValueOpcode::FPFloor32; break;
case Decoder::Opcode::V_CEIL_F16: opcode = IR::ValueOpcode::FPCeil32; break;
case Decoder::Opcode::V_TRUNC_F16: opcode = IR::ValueOpcode::FPTrunc32; break;
case Decoder::Opcode::V_RNDNE_F16: opcode = IR::ValueOpcode::FPRoundEven32; break;
case Decoder::Opcode::V_MIN3_F16: opcode = IR::ValueOpcode::FPMinTri32; break;
case Decoder::Opcode::V_MAX3_F16: opcode = IR::ValueOpcode::FPMaxTri32; break;
case Decoder::Opcode::V_MED3_F16: opcode = IR::ValueOpcode::FPMedTri32; break;
default: return false;
}
const bool accumulator = inst.opcode == Decoder::Opcode::V_FMAC_F16;
const uint32_t source_count = accumulator ? 3u : inst.src_count;
std::array<IR::Value, 3> args;
for (uint32_t index = 0; index < source_count; index++) {
auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
if (inst.opcode == Decoder::Opcode::V_SUBREV_F16 && index < 2u) {
operand = SourceAt(inst, 1u - index);
}
const bool mix = inst.opcode == Decoder::Opcode::V_MAD_MIXLO_F16 ||
inst.opcode == Decoder::Opcode::V_MAD_MIXHI_F16;
args[index] = mix ? IR::Value(ReadMixF32(operand)) : IR::Value(ReadF16AsF32(operand));
}
IR::F32 result;
switch (source_count) {
case 1: result = IR::F32(ir.Emit(opcode, {args[0]})); break;
case 2: result = IR::F32(ir.Emit(opcode, {args[0], args[1]})); break;
case 3: result = IR::F32(ir.Emit(opcode, {args[0], args[1], args[2]})); break;
default: EXIT("invalid half-float source count: %u", inst.src_count);
}
if (inst.opcode == Decoder::Opcode::V_SQRT_F16 || inst.opcode == Decoder::Opcode::V_RSQ_F16 ||
inst.opcode == Decoder::Opcode::V_LOG_F16) {
const auto negative =
IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {args[0], IR::Value::F32(0.0f)}));
result = ApplyF32ResultModifiers(inst.dst, result);
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
WriteU16(DestinationOperand(inst), ir.Select(negative, invalid, bits));
bool Translator::Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool invalid_negative) {
const auto argument = ReadF16AsF32(inst.src0);
auto result = IR::F32(ir.Emit(opcode, {argument}));
if (!invalid_negative) {
WriteF16(DestinationOperand(inst), result);
return true;
}
WriteF16(DestinationOperand(inst), result);
const auto negative =
IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {argument, IR::Value::F32(0.0f)}));
result = ApplyF32ResultModifiers(inst.dst, result);
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
WriteU16(DestinationOperand(inst), ir.Select(negative, invalid, bits));
return true;
}
bool Translator::TranslateFloatOperation(const Decoder::Instruction& inst) {
if (inst.opcode == Decoder::Opcode::V_FREXP_MANT_F32) {
const auto bits = ReadU32(inst.src0);
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(DestinationOperand(inst), ir.BitCastF32(result));
return true;
}
IR::ValueOpcode opcode {};
switch (inst.opcode) {
case Decoder::Opcode::V_RCP_F32: opcode = IR::ValueOpcode::FPRecip32; break;
case Decoder::Opcode::V_RCP_IFLAG_F32: opcode = IR::ValueOpcode::FPRecipIFlag32; break;
case Decoder::Opcode::V_FRACT_F32: opcode = IR::ValueOpcode::FPFract32; break;
case Decoder::Opcode::V_TRUNC_F32: opcode = IR::ValueOpcode::FPTrunc32; break;
case Decoder::Opcode::V_CEIL_F32: opcode = IR::ValueOpcode::FPCeil32; break;
case Decoder::Opcode::V_RNDNE_F32: opcode = IR::ValueOpcode::FPRoundEven32; break;
case Decoder::Opcode::V_FLOOR_F32: opcode = IR::ValueOpcode::FPFloor32; break;
case Decoder::Opcode::V_EXP_F32: opcode = IR::ValueOpcode::FPExp2; break;
case Decoder::Opcode::V_LOG_F32: opcode = IR::ValueOpcode::FPLog2; break;
case Decoder::Opcode::V_RSQ_F32: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
case Decoder::Opcode::V_SQRT_F32: opcode = IR::ValueOpcode::FPSqrt; break;
case Decoder::Opcode::V_SIN_F32: opcode = IR::ValueOpcode::FPSin; break;
case Decoder::Opcode::V_COS_F32: opcode = IR::ValueOpcode::FPCos; break;
case Decoder::Opcode::V_ADD_F32: opcode = IR::ValueOpcode::FPAdd32; break;
case Decoder::Opcode::V_SUB_F32: opcode = IR::ValueOpcode::FPSub32; break;
case Decoder::Opcode::V_SUBREV_F32: opcode = IR::ValueOpcode::FPSub32; break;
case Decoder::Opcode::V_MUL_F32: opcode = IR::ValueOpcode::FPMul32; break;
case Decoder::Opcode::V_MIN_F32: opcode = IR::ValueOpcode::FPMin32; break;
case Decoder::Opcode::V_MAX_F32: opcode = IR::ValueOpcode::FPMax32; break;
case Decoder::Opcode::V_MAC_F32: opcode = IR::ValueOpcode::FPFma32; break;
case Decoder::Opcode::V_MADMK_F32:
case Decoder::Opcode::V_MADAK_F32:
case Decoder::Opcode::V_MAD_F32:
case Decoder::Opcode::V_FMA_F32: opcode = IR::ValueOpcode::FPFma32; break;
case Decoder::Opcode::V_MIN3_F32: opcode = IR::ValueOpcode::FPMinTri32; break;
case Decoder::Opcode::V_MAX3_F32: opcode = IR::ValueOpcode::FPMaxTri32; break;
case Decoder::Opcode::V_MED3_F32: opcode = IR::ValueOpcode::FPMedTri32; break;
case Decoder::Opcode::V_LDEXP_F32: opcode = IR::ValueOpcode::FPLdexp; break;
default: break;
}
if (opcode != IR::ValueOpcode {}) {
const bool accumulator = inst.opcode == Decoder::Opcode::V_MAC_F32;
const bool fused = accumulator || inst.opcode == Decoder::Opcode::V_MADMK_F32 ||
inst.opcode == Decoder::Opcode::V_MADAK_F32 ||
inst.opcode == Decoder::Opcode::V_MAD_F32 ||
inst.opcode == Decoder::Opcode::V_FMA_F32;
const uint32_t source_count = accumulator ? 3u : inst.src_count;
std::array<IR::Value, 3> args;
for (uint32_t index = 0; index < source_count; index++) {
const auto arg_type = IR::ArgTypeOf(opcode, index);
auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
if (inst.opcode == Decoder::Opcode::V_SUBREV_F32 && index < 2u) {
operand = SourceAt(inst, 1u - index);
}
args[index] = arg_type == IR::Type::F32 ? (fused ? IR::Value(ReadMixF32(operand))
: ReadOperand(operand, IR::Type::F32))
: ReadOperand(operand, arg_type);
}
IR::Value result;
switch (source_count) {
case 1: result = ir.Emit(opcode, {args[0]}); break;
case 2: result = ir.Emit(opcode, {args[0], args[1]}); break;
case 3: result = ir.Emit(opcode, {args[0], args[1], args[2]}); break;
default: EXIT("invalid floating-point source count: %u", inst.src_count);
}
WriteOperand(DestinationOperand(inst), result);
return true;
}
bool Translator::Float16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool reverse) {
const auto lhs = ReadF16AsF32(reverse ? inst.src1 : inst.src0);
const auto rhs = ReadF16AsF32(reverse ? inst.src0 : inst.src1);
WriteF16(DestinationOperand(inst), IR::F32(ir.Emit(opcode, {lhs, rhs})));
return true;
}
bool Translator::Float16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool accumulator, bool mix) {
std::array<IR::Value, 3> args;
for (uint32_t index = 0; index < args.size(); index++) {
const auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
args[index] = mix ? IR::Value(ReadMixF32(operand)) : IR::Value(ReadF16AsF32(operand));
}
WriteF16(DestinationOperand(inst), IR::F32(ir.Emit(opcode, {args[0], args[1], args[2]})));
return true;
}
bool Translator::FloatUnary(const Decoder::Instruction& inst, IR::ValueOpcode opcode) {
const auto type = IR::ArgTypeOf(opcode, 0);
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {ReadOperand(inst.src0, type)}));
return true;
}
bool Translator::FloatBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool reverse) {
std::array<IR::Value, 2> args;
for (uint32_t index = 0; index < args.size(); index++) {
const auto operand = SourceAt(inst, reverse ? 1u - index : index);
args[index] = ReadOperand(operand, IR::ArgTypeOf(opcode, index));
}
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {args[0], args[1]}));
return true;
}
bool Translator::FloatTernary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool accumulator, bool mix) {
std::array<IR::Value, 3> args;
for (uint32_t index = 0; index < args.size(); index++) {
const auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
const auto type = IR::ArgTypeOf(opcode, index);
args[index] = type == IR::Type::F32 && mix ? IR::Value(ReadMixF32(operand))
: ReadOperand(operand, type);
}
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {args[0], args[1], args[2]}));
return true;
}
bool Translator::V_FREXP_MANT_F32(const Decoder::Instruction& inst) {
const auto bits = ReadU32(inst.src0);
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(DestinationOperand(inst), ir.BitCastF32(result));
return true;
}
bool Translator::V_DOT2C_F32_F16(const Decoder::Instruction& inst) {
auto a = inst.src0;
a.op_sel = false;
a.op_sel_hi = true;
auto b = inst.src1;
b.op_sel = false;
b.op_sel_hi = true;
const auto a_lo = ReadF16LaneAsF32(a, false);
const auto a_hi = ReadF16LaneAsF32(a, true);
const auto b_lo = ReadF16LaneAsF32(b, false);
const auto b_hi = ReadF16LaneAsF32(b, true);
const auto acc = ReadMixF32(inst.dst);
const auto lo = IR::F32(ir.Emit(IR::ValueOpcode::FPFma32, {a_lo, b_lo, acc}));
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::FPFma32, {a_hi, b_hi, lo}));
return true;
}
bool Translator::V_CUBEID_F32(const Decoder::Instruction& inst) {
return FloatCube(inst, 0u);
}
bool Translator::V_CUBESC_F32(const Decoder::Instruction& inst) {
return FloatCube(inst, 1u);
}
bool Translator::V_CUBETC_F32(const Decoder::Instruction& inst) {
return FloatCube(inst, 2u);
}
bool Translator::V_CUBEMA_F32(const Decoder::Instruction& inst) {
return FloatCube(inst, 3u);
}
bool Translator::FloatCube(const Decoder::Instruction& inst, uint32_t result_kind) {
const auto select_f32 = [&](IR::U1 condition, IR::F32 true_value, IR::F32 false_value) {
return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value}));
};
if (inst.opcode == Decoder::Opcode::V_DOT2C_F32_F16) {
auto a = inst.src0;
a.op_sel = false;
a.op_sel_hi = true;
auto b = inst.src1;
b.op_sel = false;
b.op_sel_hi = true;
const auto a_lo = ReadF16LaneAsF32(a, false);
const auto a_hi = ReadF16LaneAsF32(a, true);
const auto b_lo = ReadF16LaneAsF32(b, false);
const auto b_hi = ReadF16LaneAsF32(b, true);
const auto acc = ReadMixF32(inst.dst);
const auto lo = IR::F32(ir.Emit(IR::ValueOpcode::FPFma32, {a_lo, b_lo, acc}));
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::FPFma32, {a_hi, b_hi, lo}));
return true;
}
if (inst.opcode != Decoder::Opcode::V_CUBEID_F32 &&
inst.opcode != Decoder::Opcode::V_CUBESC_F32 &&
inst.opcode != Decoder::Opcode::V_CUBETC_F32 &&
inst.opcode != Decoder::Opcode::V_CUBEMA_F32) {
return false;
}
const auto x = ReadMixF32(inst.src0);
const auto y = ReadMixF32(inst.src1);
const auto z = ReadMixF32(inst.src2);
@@ -246,27 +190,23 @@ bool Translator::TranslateFloatOperation(const Decoder::Instruction& inst) {
return select_f32(z_face, z_value, select_f32(y_face, y_value, x_value));
};
IR::F32 result;
switch (inst.opcode) {
case Decoder::Opcode::V_CUBEID_F32:
switch (result_kind) {
case 0u:
result = select_face(
select_f32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))),
select_f32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))),
select_f32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f))));
break;
case Decoder::Opcode::V_CUBESC_F32:
result = select_face(select_f32(x_neg, z, nz), x, select_f32(z_neg, nx, x));
break;
case Decoder::Opcode::V_CUBETC_F32:
result = select_face(ny, select_f32(y_neg, nz, z), ny);
break;
case Decoder::Opcode::V_CUBEMA_F32: {
case 1u: result = select_face(select_f32(x_neg, z, nz), x, select_f32(z_neg, nx, x)); break;
case 2u: result = select_face(ny, select_f32(y_neg, nz, z), ny); break;
case 3u: {
const auto two = IR::F32(IR::Value::F32(2.0f));
result = select_face(IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {x, two})),
IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {y, two})),
IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {z, two})));
break;
}
default: return false;
default: EXIT("invalid cube result kind");
}
WriteOperand(DestinationOperand(inst), result);
return true;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,221 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::EmitScalar(const Decoder::Instruction& inst, std::string* error) {
using O = Decoder::Opcode;
switch (inst.opcode) {
case O::S_MOV_B32:
case O::S_MOVK_I32: MOV_B32(inst, false); return true;
case O::S_MOV_B64: S_MOV_B64(inst); return true;
case O::S_WQM_B64: S_WQM_B64(inst); return true;
case O::S_GETPC_B64: return S_GETPC_B64(inst, error);
case O::S_SETPC_B64: return true;
case O::S_CSELECT_B32: S_CSELECT_B32(inst); return true;
case O::S_CSELECT_B64: S_CSELECT_B64(inst); return true;
case O::S_SETREG_B32: EmitControlNop(); return true;
case O::S_WAITCNT: EmitWaitcnt(); return true;
case O::S_AND_SAVEEXEC_B32:
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, false, false);
return true;
case O::S_ANDN1_SAVEEXEC_B32:
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, true, false);
return true;
case O::S_AND_SAVEEXEC_B64:
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, false, true);
return true;
case O::S_ANDN1_SAVEEXEC_B64:
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, true, true);
return true;
case O::S_ORN2_SAVEEXEC_B64:
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalOr, true, false, true);
return true;
case O::S_ADD_U32: ADD_U32(inst, false, false); return true;
case O::S_ADDC_U32: ADD_U32(inst, false, true); return true;
case O::S_SUB_U32: SUB_U32(inst, false, false); return true;
case O::S_SUBB_U32: SUBB_U32(inst, false); return true;
case O::S_ADD_I32: S_ADD_SUB_I32(inst, false); return true;
case O::S_SUB_I32: S_ADD_SUB_I32(inst, true); return true;
case O::S_LSHL1_ADD_U32: S_LSHL_ADD_U32(inst, 1u); return true;
case O::S_LSHL2_ADD_U32: S_LSHL_ADD_U32(inst, 2u); return true;
case O::S_LSHL3_ADD_U32: S_LSHL_ADD_U32(inst, 3u); return true;
case O::S_LSHL4_ADD_U32: S_LSHL_ADD_U32(inst, 4u); return true;
case O::S_MIN_I32:
ScalarMinMax32(inst, IR::ValueOpcode::SMin32, IR::ValueOpcode::SLessThan32);
return true;
case O::S_MAX_I32:
ScalarMinMax32(inst, IR::ValueOpcode::SMax32, IR::ValueOpcode::SGreaterThan32);
return true;
case O::S_MIN_U32:
ScalarMinMax32(inst, IR::ValueOpcode::UMin32, IR::ValueOpcode::ULessThan32);
return true;
case O::S_MAX_U32:
ScalarMinMax32(inst, IR::ValueOpcode::UMax32, IR::ValueOpcode::UGreaterThan32);
return true;
case O::S_CMP_EQ_U32:
case O::S_CMP_EQ_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual32, IR::Type::U32, true, false);
return true;
case O::S_CMP_LG_U32:
case O::S_CMP_LG_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual32, IR::Type::U32, true, false);
return true;
case O::S_CMP_GT_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan32, IR::Type::U32, true, false);
return true;
case O::S_CMP_GE_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThanEqual32, IR::Type::U32, true,
false);
return true;
case O::S_CMP_LT_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan32, IR::Type::U32, true, false);
return true;
case O::S_CMP_LE_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThanEqual32, IR::Type::U32, true, false);
return true;
case O::S_CMP_GT_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SGreaterThan32, IR::Type::U32, true, false);
return true;
case O::S_CMP_GE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SGreaterThanEqual32, IR::Type::U32, true,
false);
return true;
case O::S_CMP_LT_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThan32, IR::Type::U32, true, false);
return true;
case O::S_CMP_LE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, true, false);
return true;
case O::S_CMP_EQ_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, true, false);
return true;
case O::S_CMP_LG_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual64, IR::Type::U64, true, false);
return true;
case O::S_AND_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalAnd, IR::ValueOpcode::BitwiseAnd32,
false, false, false);
case O::S_ANDN2_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalAnd, IR::ValueOpcode::BitwiseAnd32,
true, false, false);
case O::S_OR_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalOr, IR::ValueOpcode::BitwiseOr32, false,
false, false);
case O::S_ORN2_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalOr, IR::ValueOpcode::BitwiseOr32, true,
false, false);
case O::S_XOR_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalXor, IR::ValueOpcode::BitwiseXor32,
false, false, false);
case O::S_NAND_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalAnd, IR::ValueOpcode::BitwiseAnd32,
false, true, false);
case O::S_NOR_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalOr, IR::ValueOpcode::BitwiseOr32, false,
true, false);
case O::S_XNOR_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalXor, IR::ValueOpcode::BitwiseXor32,
false, true, false);
case O::S_NOT_B64:
return S_U64_MASK(inst, IR::ValueOpcode::LogicalAnd, IR::ValueOpcode::BitwiseAnd32,
false, false, true);
case O::S_ABS_I32:
return SimpleInteger(inst, IR::ValueOpcode::IAbs32, IR::Type::U32, false, false, true);
case O::S_MUL_I32:
case O::S_MULK_I32:
return SimpleInteger(inst, IR::ValueOpcode::IMul32, IR::Type::U32, false, false, false);
case O::S_MUL_HI_U32:
return SimpleInteger(inst, IR::ValueOpcode::UMulHi, IR::Type::U32, false, false, false);
case O::S_AND_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseAnd32, IR::Type::U32, false, false,
true);
case O::S_OR_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseOr32, IR::Type::U32, false, false,
true);
case O::S_XOR_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseXor32, IR::Type::U32, false, false,
true);
case O::S_NOT_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseNot32, IR::Type::U32, false, false,
true);
case O::S_BREV_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitReverse32, IR::Type::U32, false, false,
false);
case O::S_BCNT1_I32_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitCount32, IR::Type::U32, false, false,
true);
case O::S_BCNT1_I32_B64:
return SimpleInteger(inst, IR::ValueOpcode::BitCount64, IR::Type::U64, false, false,
true);
case O::S_FF1_I32_B32:
return SimpleInteger(inst, IR::ValueOpcode::FindILsb32, IR::Type::U32, false, false,
false);
case O::S_LSHL_B32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftLeftLogical32, IR::Type::U32, false,
true, true);
case O::S_LSHR_B32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical32, IR::Type::U32, false,
true, true);
case O::S_ASHR_I32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightArithmetic32, IR::Type::U32,
false, true, true);
case O::S_LSHL_B64:
return SimpleInteger(inst, IR::ValueOpcode::ShiftLeftLogical64, IR::Type::U64, false,
false, true);
case O::S_LSHR_B64:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical64, IR::Type::U64, false,
false, true);
case O::S_ANDN2_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseAnd32, true, false, true);
case O::S_ORN2_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseOr32, true, false, true);
case O::S_NAND_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseAnd32, false, true, true);
case O::S_NOR_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseOr32, false, true, true);
case O::S_XNOR_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseXor32, false, true, true);
case O::S_FF1_I32_B64: return S_FF1_I32_B64(inst);
case O::S_FLBIT_I32_B64: return S_FLBIT_I32_B64(inst);
case O::S_BITSET0_B32: return S_BITSET_B32(inst, false);
case O::S_BITSET1_B32: return S_BITSET_B32(inst, true);
case O::S_BITREPLICATE_B64_B32: return S_BITREPLICATE_B64_B32(inst);
case O::S_QUADMASK_B64: return S_QUADMASK_B64(inst);
case O::S_BFM_B32: return BFM_B32(inst);
case O::S_BFM_B64: return S_BFM_B64(inst);
case O::S_BFE_U32: return S_BFE_U32(inst, false);
case O::S_BFE_I32: return S_BFE_U32(inst, true);
case O::S_BFE_U64: return S_BFE_U64(inst);
case O::S_BITCMP0_B32: return S_BITCMP_B32(inst, false);
case O::S_BITCMP1_B32: return S_BITCMP_B32(inst, true);
case O::S_PACK_LL_B32_B16: return PackB16(inst, false, false);
case O::S_PACK_LH_B32_B16: return PackB16(inst, false, true);
case O::S_PACK_HH_B32_B16: return PackB16(inst, true, true);
case O::S_NOP:
case O::S_SLEEP:
case O::S_TRAP: EmitControlNop(); return true;
case O::S_WAITCNT_DEPCTR: EmitWaitcnt(); return true;
case O::S_BARRIER: S_BARRIER(); return true;
case O::S_SENDMSG: S_SENDMSG(); return true;
case O::S_TTRACEDATA: S_TTRACEDATA(); return true;
case O::S_INST_PREFETCH: S_INST_PREFETCH(); return true;
case O::S_BRANCH:
case O::S_CBRANCH_SCC0:
case O::S_CBRANCH_SCC1:
case O::S_CBRANCH_VCCZ:
case O::S_CBRANCH_VCCNZ:
case O::S_CBRANCH_EXECZ:
case O::S_CBRANCH_EXECNZ:
case O::S_ENDPGM: return true;
default: return false;
}
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -75,24 +75,40 @@ private:
IR::Value GetScalarAddressResource(uint32_t base);
IR::Value GetImageResource(const IR::MemoryInfo& memory);
IR::Value GetSamplerResource(const IR::MemoryInfo& memory);
IR::Value MakeImageAddress(const Decoder::Instruction& inst, const Decoder::Operand& base);
IR::Value ConstructU32x4(const Decoder::Operand& base, uint32_t count);
void WriteImageComponents(const Decoder::Operand& dst, IR::Value value,
const IR::MemoryInfo& memory, uint32_t component_limit);
IR::ValueOpcode ImageAtomicOpcode(Decoder::Opcode opcode);
BufferAddress ReadBufferAddress(const Decoder::Instruction& inst, uint32_t source_offset);
IR::U32 WidenSubdword(IR::Value value, uint32_t bits, bool sign);
IR::Value NarrowSubdword(IR::U32 value, uint32_t bits);
IR::ValueOpcode BufferAtomicOpcode(Decoder::Opcode opcode);
IR::ValueOpcode SharedAtomicOpcode(Decoder::Opcode opcode);
bool TranslateScalarMemory(const Decoder::Instruction& inst, std::string* error);
bool TranslateBufferLoad(const Decoder::Instruction& inst, std::string* error);
bool TranslateBufferStore(const Decoder::Instruction& inst, std::string* error);
bool TranslateAtomicMemory(const Decoder::Instruction& inst, std::string* error);
bool TranslateFlatLoad(const Decoder::Instruction& inst, std::string* error);
bool TranslateFlatStore(const Decoder::Instruction& inst, std::string* error);
bool TranslateImageMemory(const Decoder::Instruction& inst, std::string* error);
bool TranslateSharedMemory(const Decoder::Instruction& inst, std::string* error);
IR::Value MakeImageAddress(const Decoder::Instruction& inst, const Decoder::Operand& base);
IR::Value ConstructU32x4(const Decoder::Operand& base, uint32_t count);
void WriteImageComponents(const Decoder::Operand& dst, IR::Value value,
const IR::MemoryInfo& memory, uint32_t component_limit);
BufferAddress ReadBufferAddress(const Decoder::Instruction& inst, uint32_t source_offset);
IR::U32 WidenSubdword(IR::Value value, uint32_t bits, bool sign);
IR::Value NarrowSubdword(IR::U32 value, uint32_t bits);
bool S_LOAD(const Decoder::Instruction& inst, bool raw);
bool BUFFER_LOAD(const Decoder::Instruction& inst);
bool BUFFER_STORE(const Decoder::Instruction& inst);
bool BUFFER_ATOMIC(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
bool IMAGE_ATOMIC(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
bool DS_ATOMIC(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool returns_value);
bool FLAT_LOAD(const Decoder::Instruction& inst);
bool FLAT_STORE(const Decoder::Instruction& inst);
bool IMAGE_GET_RESINFO(const Decoder::Instruction& inst);
bool IMAGE_GET_LOD(const Decoder::Instruction& inst);
bool IMAGE_LOAD(const Decoder::Instruction& inst);
bool IMAGE_STORE(const Decoder::Instruction& inst);
bool IMAGE_SAMPLE(const Decoder::Instruction& inst);
bool IMAGE_GATHER(const Decoder::Instruction& inst);
IR::Value LoadSharedU32(uint32_t width, IR::U32 address, const IR::MemoryInfo& memory,
uint32_t pc);
IR::Value ExtractSharedU32(IR::Value value, uint32_t width, uint32_t index);
void WriteSharedU32(uint32_t width, IR::U32 address, const std::array<IR::Value, 4>& values,
const IR::MemoryInfo& memory, uint32_t pc);
bool DS_READ(const Decoder::Instruction& inst);
bool DS_READ2(const Decoder::Instruction& inst);
bool DS_WRITE(const Decoder::Instruction& inst);
bool DS_WRITE2(const Decoder::Instruction& inst);
bool DS_MINMAX_F32(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
bool DS_APPEND_CONSUME(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
bool DS_ADDTID(const Decoder::Instruction& inst, bool write);
bool DS_SWIZZLE_B32(const Decoder::Instruction& inst);
IR::F32 SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value);
IR::U32 ConvertF32ToU32Saturated(IR::F32 value, float upper_bound, float safe_upper,
@@ -101,27 +117,136 @@ private:
float safe_upper, uint32_t lower_result,
uint32_t upper_result);
IR::U32 PackU16Lanes(IR::U32 low, IR::U32 high);
void EmitCompareResult(const Decoder::Instruction& inst, IR::U1 value, bool scalar, bool cmpx);
void EmitCompareConstant(const Decoder::Instruction& inst, bool value, bool scalar, bool cmpx);
void EmitIntegerCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
bool scalar, bool cmpx);
void EmitInteger16Compare(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool signed_value, bool cmpx);
void EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool half,
bool cmpx);
void EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered);
void V_CMP_CLASS_F32(const Decoder::Instruction& inst);
void V_CVT_F32_UBYTE(const Decoder::Instruction& inst, uint32_t byte_index);
void V_CVT_F32_U32(const Decoder::Instruction& inst);
void V_CVT_F32_I32(const Decoder::Instruction& inst);
void V_CVT_U32_F32(const Decoder::Instruction& inst);
void V_CVT_I32_F32(const Decoder::Instruction& inst);
void V_CVT_F16_F32(const Decoder::Instruction& inst);
void V_CVT_F32_F16(const Decoder::Instruction& inst);
void V_CVT_F16_16(const Decoder::Instruction& inst, bool signed_value);
void V_CVT_16_F16(const Decoder::Instruction& inst, bool signed_value);
void V_CVT_RPI_I32_F32(const Decoder::Instruction& inst);
void V_CVT_FLR_I32_F32(const Decoder::Instruction& inst);
void V_FREXP_EXP_I32_F32(const Decoder::Instruction& inst);
void V_CVT_OFF_F32_I4(const Decoder::Instruction& inst);
void V_CVT_PKRTZ_F16_F32(const Decoder::Instruction& inst);
void V_CVT_PKNORM_F32(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
void V_CVT_PK_U8_F32(const Decoder::Instruction& inst);
void V_PACK_B32_F16(const Decoder::Instruction& inst);
bool PackedFloat16(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
bool quiet_snan);
bool Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool invalid_negative);
bool Float16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool reverse);
bool Float16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
bool mix);
bool FloatUnary(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
bool FloatBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool reverse);
bool FloatTernary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
bool mix);
bool V_FREXP_MANT_F32(const Decoder::Instruction& inst);
bool V_DOT2C_F32_F16(const Decoder::Instruction& inst);
bool V_CUBEID_F32(const Decoder::Instruction& inst);
bool V_CUBESC_F32(const Decoder::Instruction& inst);
bool V_CUBETC_F32(const Decoder::Instruction& inst);
bool V_CUBEMA_F32(const Decoder::Instruction& inst);
bool FloatCube(const Decoder::Instruction& inst, uint32_t result_kind);
bool Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool arithmetic);
bool Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
bool V_MED3_I16(const Decoder::Instruction& inst);
bool PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool arithmetic);
bool PackedInteger16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
bool PackedInteger16Mad(const Decoder::Instruction& inst, bool sign);
bool PackedInteger16MinMax(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
bool S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode logical_opcode,
IR::ValueOpcode bit_opcode, bool negate_rhs, bool negate_result, bool unary);
IR::U1 U64MaskBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool negate_rhs,
bool negate_result);
bool SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
bool reverse, bool mask_shift_count, bool update_scc);
bool ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool negate_rhs, bool negate_result, bool update_scc);
bool V_AND_OR_B32(const Decoder::Instruction& inst);
bool V_OR3_B32(const Decoder::Instruction& inst);
bool V_XOR3_B32(const Decoder::Instruction& inst);
bool S_FF1_I32_B64(const Decoder::Instruction& inst);
bool V_FFBH_U32(const Decoder::Instruction& inst);
bool S_FLBIT_I32_B64(const Decoder::Instruction& inst);
bool Integer24(const Decoder::Instruction& inst, bool sign, bool addend);
bool V_MAD_U64_U32(const Decoder::Instruction& inst);
bool V_SAD_U32(const Decoder::Instruction& inst);
bool V_ADD3_U32(const Decoder::Instruction& inst);
bool S_BITSET_B32(const Decoder::Instruction& inst, bool set);
bool V_BCNT_U32_B32(const Decoder::Instruction& inst);
bool V_MBCNT_U32_B32(const Decoder::Instruction& inst, bool low);
bool S_BITREPLICATE_B64_B32(const Decoder::Instruction& inst);
bool S_QUADMASK_B64(const Decoder::Instruction& inst);
bool BFM_B32(const Decoder::Instruction& inst);
IR::U32 RightMask32(IR::U32 count);
IR::U64 RightMask64(IR::U32 count);
bool S_BFM_B64(const Decoder::Instruction& inst);
bool S_BFE_U32(const Decoder::Instruction& inst, bool sign);
bool S_BFE_U64(const Decoder::Instruction& inst);
bool V_BFE_U32(const Decoder::Instruction& inst, bool sign);
bool V_BFI_B32(const Decoder::Instruction& inst);
bool S_BITCMP_B32(const Decoder::Instruction& inst, bool expected);
bool V_ALIGNBIT_B32(const Decoder::Instruction& inst);
bool V_ALIGNBYTE_B32(const Decoder::Instruction& inst);
bool V_LSHL_ADD_U32(const Decoder::Instruction& inst);
bool V_ADD_LSHL_U32(const Decoder::Instruction& inst);
bool V_XAD_U32(const Decoder::Instruction& inst);
bool V_LSHL_OR_B32(const Decoder::Instruction& inst);
bool V_CNDMASK_B32(const Decoder::Instruction& inst);
bool PackB16(const Decoder::Instruction& inst, bool high0, bool high1);
bool TranslateStateOperation(const Decoder::Instruction& inst);
bool TranslateControlOperation(const Decoder::Instruction& inst);
bool TranslateMove(const Decoder::Instruction& inst, std::string* error);
bool TranslateLaneOperation(const Decoder::Instruction& inst, std::string* error);
bool TranslateAttributeOperation(const Decoder::Instruction& inst, std::string* error);
bool TranslateMemoryOperation(const Decoder::Instruction& inst, std::string* error);
bool TranslateIntegerCompare(const Decoder::Instruction& inst);
bool TranslateInteger16Compare(const Decoder::Instruction& inst);
bool TranslateFloatCompare(const Decoder::Instruction& inst);
bool TranslateConversion(const Decoder::Instruction& inst);
bool TranslateInteger16Operation(const Decoder::Instruction& inst);
bool TranslatePackedInteger16(const Decoder::Instruction& inst);
bool TranslatePackedFloat16(const Decoder::Instruction& inst);
bool TranslateFloat16Operation(const Decoder::Instruction& inst);
bool TranslateFloatOperation(const Decoder::Instruction& inst);
IR::U1 EvaluateU64Mask(const Decoder::Instruction& inst);
bool TranslateU64MaskOperation(const Decoder::Instruction& inst);
bool TranslateSimpleInteger(const Decoder::Instruction& inst);
bool TranslateComposedInteger(const Decoder::Instruction& inst);
bool TranslateExtendedInteger(const Decoder::Instruction& inst);
void S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation, bool negate_exec,
bool negate_source, bool write_64);
void ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in);
void SUB_U32(const Decoder::Instruction& inst, bool vector, bool reverse);
void SUBB_U32(const Decoder::Instruction& inst, bool vector);
void S_ADD_SUB_I32(const Decoder::Instruction& inst, bool subtract);
void S_LSHL_ADD_U32(const Decoder::Instruction& inst, uint32_t shift_amount);
void ScalarMinMax32(const Decoder::Instruction& inst, IR::ValueOpcode value_opcode,
IR::ValueOpcode compare_opcode);
void EmitControlNop();
void EmitWaitcnt();
void S_BARRIER();
void S_SENDMSG();
void S_TTRACEDATA();
void S_INST_PREFETCH();
bool S_GETPC_B64(const Decoder::Instruction& inst, std::string* error);
void S_CSELECT_B32(const Decoder::Instruction& inst);
void S_CSELECT_B64(const Decoder::Instruction& inst);
void MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers);
void S_MOV_B64(const Decoder::Instruction& inst);
void S_WQM_B64(const Decoder::Instruction& inst);
bool V_MOVRELS_B32(const Decoder::Instruction& inst, std::string* error);
bool V_MOVRELD_B32(const Decoder::Instruction& inst, std::string* error);
void V_READFIRSTLANE_B32(const Decoder::Instruction& inst);
void V_READLANE_B32(const Decoder::Instruction& inst);
void V_WRITELANE_B32(const Decoder::Instruction& inst);
void V_PERMLANE16_B32(const Decoder::Instruction& inst, bool x16);
void V_INTERP_P1_F32();
void V_INTERP_P2_F32(const Decoder::Instruction& inst);
bool V_INTERP_MOV_F32(const Decoder::Instruction& inst, std::string* error);
bool EXP(const Decoder::Instruction& inst, std::string* error);
bool EmitScalar(const Decoder::Instruction& inst, std::string* error);
bool EmitVector(const Decoder::Instruction& inst, std::string* error);
bool EmitInterpolation(const Decoder::Instruction& inst, std::string* error);
bool EmitMemory(const Decoder::Instruction& inst, std::string* error);
IR::Program& program;
IR::IREmitter ir;
@@ -0,0 +1,514 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error) {
using O = Decoder::Opcode;
switch (inst.opcode) {
case O::V_NOP: return true;
case O::V_ADD_I32: ADD_U32(inst, true, false); return true;
case O::V_ADDC_U32: ADD_U32(inst, true, true); return true;
case O::V_SUB_I32: SUB_U32(inst, true, false); return true;
case O::V_SUBREV_I32: SUB_U32(inst, true, true); return true;
case O::V_SUBREV_CO_CI_U32: SUBB_U32(inst, true); return true;
case O::V_MOV_B32: MOV_B32(inst, true); return true;
case O::V_MOVRELS_B32: return V_MOVRELS_B32(inst, error);
case O::V_MOVRELD_B32: return V_MOVRELD_B32(inst, error);
case O::V_READFIRSTLANE_B32: V_READFIRSTLANE_B32(inst); return true;
case O::V_READLANE_B32: V_READLANE_B32(inst); return true;
case O::V_WRITELANE_B32: V_WRITELANE_B32(inst); return true;
case O::V_PERMLANE16_B32: V_PERMLANE16_B32(inst, false); return true;
case O::V_PERMLANEX16_B32: V_PERMLANE16_B32(inst, true); return true;
case O::V_CMP_F_I32:
case O::V_CMP_F_U32: EmitCompareConstant(inst, false, false, false); return true;
case O::V_CMP_T_I32:
case O::V_CMP_T_U32: EmitCompareConstant(inst, true, false, false); return true;
case O::V_CMP_EQ_U32:
case O::V_CMP_EQ_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual32, IR::Type::U32, false, false);
return true;
case O::V_CMPX_EQ_U32:
case O::V_CMPX_EQ_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual32, IR::Type::U32, false, true);
return true;
case O::V_CMP_NE_U32:
case O::V_CMP_NE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual32, IR::Type::U32, false, false);
return true;
case O::V_CMPX_NE_U32:
case O::V_CMPX_NE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual32, IR::Type::U32, false, true);
return true;
case O::V_CMP_GT_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan32, IR::Type::U32, false, false);
return true;
case O::V_CMPX_GT_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan32, IR::Type::U32, false, true);
return true;
case O::V_CMP_GE_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThanEqual32, IR::Type::U32, false,
false);
return true;
case O::V_CMPX_GE_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThanEqual32, IR::Type::U32, false,
true);
return true;
case O::V_CMP_LT_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan32, IR::Type::U32, false, false);
return true;
case O::V_CMPX_LT_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan32, IR::Type::U32, false, true);
return true;
case O::V_CMP_LE_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThanEqual32, IR::Type::U32, false,
false);
return true;
case O::V_CMPX_LE_U32:
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThanEqual32, IR::Type::U32, false, true);
return true;
case O::V_CMP_GT_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SGreaterThan32, IR::Type::U32, false, false);
return true;
case O::V_CMPX_GT_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SGreaterThan32, IR::Type::U32, false, true);
return true;
case O::V_CMP_GE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SGreaterThanEqual32, IR::Type::U32, false,
false);
return true;
case O::V_CMPX_GE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SGreaterThanEqual32, IR::Type::U32, false,
true);
return true;
case O::V_CMP_LT_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThan32, IR::Type::U32, false, false);
return true;
case O::V_CMPX_LT_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThan32, IR::Type::U32, false, true);
return true;
case O::V_CMP_LE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, false,
false);
return true;
case O::V_CMPX_LE_I32:
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, false, true);
return true;
case O::V_CMP_EQ_I64:
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, false, false);
return true;
case O::V_CMP_GT_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan64, IR::Type::U64, false, false);
return true;
case O::V_CMP_NE_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual64, IR::Type::U64, false, false);
return true;
case O::V_CMPX_NE_I64:
case O::V_CMPX_NE_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual64, IR::Type::U64, false, true);
return true;
case O::V_CMP_EQ_U16:
EmitInteger16Compare(inst, IR::ValueOpcode::IEqual32, false, false);
return true;
case O::V_CMP_EQ_I16:
EmitInteger16Compare(inst, IR::ValueOpcode::IEqual32, true, false);
return true;
case O::V_CMP_NE_U16:
EmitInteger16Compare(inst, IR::ValueOpcode::INotEqual32, false, false);
return true;
case O::V_CMP_NE_I16:
EmitInteger16Compare(inst, IR::ValueOpcode::INotEqual32, true, false);
return true;
case O::V_CMP_GT_U16:
EmitInteger16Compare(inst, IR::ValueOpcode::UGreaterThan32, false, false);
return true;
case O::V_CMP_GE_U16:
EmitInteger16Compare(inst, IR::ValueOpcode::UGreaterThanEqual32, false, false);
return true;
case O::V_CMP_LT_U16:
EmitInteger16Compare(inst, IR::ValueOpcode::ULessThan32, false, false);
return true;
case O::V_CMP_LE_U16:
EmitInteger16Compare(inst, IR::ValueOpcode::ULessThanEqual32, false, false);
return true;
case O::V_CMP_GT_I16:
EmitInteger16Compare(inst, IR::ValueOpcode::SGreaterThan32, true, false);
return true;
case O::V_CMP_GE_I16:
EmitInteger16Compare(inst, IR::ValueOpcode::SGreaterThanEqual32, true, false);
return true;
case O::V_CMP_LT_I16:
EmitInteger16Compare(inst, IR::ValueOpcode::SLessThan32, true, false);
return true;
case O::V_CMP_LE_I16:
EmitInteger16Compare(inst, IR::ValueOpcode::SLessThanEqual32, true, false);
return true;
case O::V_CMP_F_F32: EmitCompareConstant(inst, false, false, false); return true;
case O::V_CMP_TRU_F32: EmitCompareConstant(inst, true, false, false); return true;
case O::V_CMP_EQ_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdEqual32, false, false);
return true;
case O::V_CMPX_EQ_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdEqual32, false, true);
return true;
case O::V_CMP_LG_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdNotEqual32, false, false);
return true;
case O::V_CMPX_LG_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdNotEqual32, false, true);
return true;
case O::V_CMP_GT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThan32, false, false);
return true;
case O::V_CMPX_GT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThan32, false, true);
return true;
case O::V_CMP_GE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThanEqual32, false, false);
return true;
case O::V_CMPX_GE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThanEqual32, false, true);
return true;
case O::V_CMP_LT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThan32, false, false);
return true;
case O::V_CMPX_LT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThan32, false, true);
return true;
case O::V_CMP_LE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThanEqual32, false, false);
return true;
case O::V_CMPX_LE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThanEqual32, false, true);
return true;
case O::V_CMP_NLG_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordEqual32, false, false);
return true;
case O::V_CMPX_NLG_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordEqual32, false, true);
return true;
case O::V_CMP_NEQ_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual32, false, false);
return true;
case O::V_CMPX_NEQ_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual32, false, true);
return true;
case O::V_CMP_NLE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThan32, false, false);
return true;
case O::V_CMPX_NLE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThan32, false, true);
return true;
case O::V_CMP_NLT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, false, false);
return true;
case O::V_CMPX_NLT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, false, true);
return true;
case O::V_CMP_NGE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThan32, false, false);
return true;
case O::V_CMPX_NGE_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThan32, false, true);
return true;
case O::V_CMP_NGT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, false, false);
return true;
case O::V_CMPX_NGT_F32:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, false, true);
return true;
case O::V_CMP_EQ_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdEqual32, true, false);
return true;
case O::V_CMPX_EQ_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdEqual32, true, true);
return true;
case O::V_CMP_LG_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdNotEqual32, true, false);
return true;
case O::V_CMP_GT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThan32, true, false);
return true;
case O::V_CMPX_GT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThan32, true, true);
return true;
case O::V_CMP_GE_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThanEqual32, true, false);
return true;
case O::V_CMPX_GE_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThanEqual32, true, true);
return true;
case O::V_CMP_LT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThan32, true, false);
return true;
case O::V_CMPX_LT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThan32, true, true);
return true;
case O::V_CMP_LE_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThanEqual32, true, false);
return true;
case O::V_CMPX_LE_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThanEqual32, true, true);
return true;
case O::V_CMP_NEQ_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual32, true, false);
return true;
case O::V_CMPX_NEQ_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual32, true, true);
return true;
case O::V_CMPX_NLT_F16:
EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, true);
return true;
case O::V_CMP_O_F32: EmitFloatOrderedCompare(inst, true); return true;
case O::V_CMP_U_F32: EmitFloatOrderedCompare(inst, false); return true;
case O::V_CMP_CLASS_F32: V_CMP_CLASS_F32(inst); return true;
case O::V_CVT_F32_UBYTE0: V_CVT_F32_UBYTE(inst, 0); return true;
case O::V_CVT_F32_UBYTE1: V_CVT_F32_UBYTE(inst, 1); return true;
case O::V_CVT_F32_UBYTE2: V_CVT_F32_UBYTE(inst, 2); return true;
case O::V_CVT_F32_UBYTE3: V_CVT_F32_UBYTE(inst, 3); return true;
case O::V_CVT_F32_U32: V_CVT_F32_U32(inst); return true;
case O::V_CVT_F32_I32: V_CVT_F32_I32(inst); return true;
case O::V_CVT_U32_F32: V_CVT_U32_F32(inst); return true;
case O::V_CVT_I32_F32: V_CVT_I32_F32(inst); return true;
case O::V_CVT_F16_F32: V_CVT_F16_F32(inst); return true;
case O::V_CVT_F32_F16: V_CVT_F32_F16(inst); return true;
case O::V_CVT_F16_U16: V_CVT_F16_16(inst, false); return true;
case O::V_CVT_F16_I16: V_CVT_F16_16(inst, true); return true;
case O::V_CVT_U16_F16: V_CVT_16_F16(inst, false); return true;
case O::V_CVT_I16_F16: V_CVT_16_F16(inst, true); return true;
case O::V_CVT_RPI_I32_F32: V_CVT_RPI_I32_F32(inst); return true;
case O::V_CVT_FLR_I32_F32: V_CVT_FLR_I32_F32(inst); return true;
case O::V_FREXP_EXP_I32_F32: V_FREXP_EXP_I32_F32(inst); return true;
case O::V_CVT_OFF_F32_I4: V_CVT_OFF_F32_I4(inst); return true;
case O::V_CVT_PKRTZ_F16_F32: V_CVT_PKRTZ_F16_F32(inst); return true;
case O::V_CVT_PKNORM_I16_F32:
V_CVT_PKNORM_F32(inst, IR::ValueOpcode::PackSnorm2x16);
return true;
case O::V_CVT_PKNORM_U16_F32:
V_CVT_PKNORM_F32(inst, IR::ValueOpcode::PackUnorm2x16);
return true;
case O::V_CVT_PK_U8_F32: V_CVT_PK_U8_F32(inst); return true;
case O::V_PACK_B32_F16: V_PACK_B32_F16(inst); return true;
case O::V_CVT_PK_U16_U32:
case O::V_CVT_PK_I16_I32: return PackB16(inst, false, false);
case O::V_LSHLREV_B16:
return Integer16Shift(inst, IR::ValueOpcode::ShiftLeftLogical32, false);
case O::V_LSHRREV_B16:
return Integer16Shift(inst, IR::ValueOpcode::ShiftRightLogical32, false);
case O::V_ASHRREV_I16:
return Integer16Shift(inst, IR::ValueOpcode::ShiftRightArithmetic32, true);
case O::V_ADD_NC_U16:
case O::V_ADD_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::IAdd32, false);
case O::V_SUB_NC_U16:
case O::V_SUB_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::ISub32, false);
case O::V_MED3_I16: return V_MED3_I16(inst);
case O::V_MIN_I16: return Integer16Binary(inst, IR::ValueOpcode::SMin32, true);
case O::V_MAX_I16: return Integer16Binary(inst, IR::ValueOpcode::SMax32, true);
case O::V_MIN_U16: return Integer16Binary(inst, IR::ValueOpcode::UMin32, false);
case O::V_MAX_U16: return Integer16Binary(inst, IR::ValueOpcode::UMax32, false);
case O::V_PK_LSHLREV_B16:
return PackedInteger16Shift(inst, IR::ValueOpcode::ShiftLeftLogical32, false);
case O::V_PK_LSHRREV_B16:
return PackedInteger16Shift(inst, IR::ValueOpcode::ShiftRightLogical32, false);
case O::V_PK_ASHRREV_I16:
return PackedInteger16Shift(inst, IR::ValueOpcode::ShiftRightArithmetic32, true);
case O::V_PK_MAD_I16: return PackedInteger16Mad(inst, true);
case O::V_PK_MAD_U16: return PackedInteger16Mad(inst, false);
case O::V_PK_MUL_LO_U16: return PackedInteger16Binary(inst, IR::ValueOpcode::IMul32);
case O::V_PK_ADD_I16:
case O::V_PK_ADD_U16: return PackedInteger16Binary(inst, IR::ValueOpcode::IAdd32);
case O::V_PK_SUB_I16:
case O::V_PK_SUB_U16: return PackedInteger16Binary(inst, IR::ValueOpcode::ISub32);
case O::V_PK_MAX_I16: return PackedInteger16MinMax(inst, IR::ValueOpcode::SMax32, true);
case O::V_PK_MIN_I16: return PackedInteger16MinMax(inst, IR::ValueOpcode::SMin32, true);
case O::V_PK_MAX_U16: return PackedInteger16MinMax(inst, IR::ValueOpcode::UMax32, false);
case O::V_PK_MIN_U16: return PackedInteger16MinMax(inst, IR::ValueOpcode::UMin32, false);
case O::V_PK_ADD_F16: return PackedFloat16(inst, IR::ValueOpcode::FPAdd32, false, false);
case O::V_PK_MUL_F16: return PackedFloat16(inst, IR::ValueOpcode::FPMul32, false, false);
case O::V_PK_MIN_F16: return PackedFloat16(inst, IR::ValueOpcode::FPMin32, false, true);
case O::V_PK_MAX_F16: return PackedFloat16(inst, IR::ValueOpcode::FPMax32, false, true);
case O::V_PK_FMA_F16: return PackedFloat16(inst, IR::ValueOpcode::FPFma32, false, false);
case O::V_PK_FMAC_F16: return PackedFloat16(inst, IR::ValueOpcode::FPFma32, true, false);
case O::V_ADD_F16: return Float16Binary(inst, IR::ValueOpcode::FPAdd32, false);
case O::V_SUB_F16: return Float16Binary(inst, IR::ValueOpcode::FPSub32, false);
case O::V_SUBREV_F16: return Float16Binary(inst, IR::ValueOpcode::FPSub32, true);
case O::V_MUL_F16: return Float16Binary(inst, IR::ValueOpcode::FPMul32, false);
case O::V_MIN_F16: return Float16Binary(inst, IR::ValueOpcode::FPMin32, false);
case O::V_MAX_F16: return Float16Binary(inst, IR::ValueOpcode::FPMax32, false);
case O::V_FMAC_F16: return Float16Ternary(inst, IR::ValueOpcode::FPFma32, true, false);
case O::V_FMAMK_F16:
case O::V_FMAAK_F16:
case O::V_FMA_F16: return Float16Ternary(inst, IR::ValueOpcode::FPFma32, false, false);
case O::V_MAD_MIXLO_F16:
case O::V_MAD_MIXHI_F16: return Float16Ternary(inst, IR::ValueOpcode::FPFma32, false, true);
case O::V_RCP_F16: return Float16Unary(inst, IR::ValueOpcode::FPRecip32, false);
case O::V_SQRT_F16: return Float16Unary(inst, IR::ValueOpcode::FPSqrt, true);
case O::V_RSQ_F16: return Float16Unary(inst, IR::ValueOpcode::FPRecipSqrt32, true);
case O::V_LOG_F16: return Float16Unary(inst, IR::ValueOpcode::FPLog2, true);
case O::V_EXP_F16: return Float16Unary(inst, IR::ValueOpcode::FPExp2, false);
case O::V_FLOOR_F16: return Float16Unary(inst, IR::ValueOpcode::FPFloor32, false);
case O::V_CEIL_F16: return Float16Unary(inst, IR::ValueOpcode::FPCeil32, false);
case O::V_TRUNC_F16: return Float16Unary(inst, IR::ValueOpcode::FPTrunc32, false);
case O::V_RNDNE_F16: return Float16Unary(inst, IR::ValueOpcode::FPRoundEven32, false);
case O::V_MIN3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMinTri32, false, false);
case O::V_MAX3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMaxTri32, false, false);
case O::V_MED3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMedTri32, false, false);
case O::V_FREXP_MANT_F32: return V_FREXP_MANT_F32(inst);
case O::V_RCP_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecip32);
case O::V_RCP_IFLAG_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecipIFlag32);
case O::V_FRACT_F32: return FloatUnary(inst, IR::ValueOpcode::FPFract32);
case O::V_TRUNC_F32: return FloatUnary(inst, IR::ValueOpcode::FPTrunc32);
case O::V_CEIL_F32: return FloatUnary(inst, IR::ValueOpcode::FPCeil32);
case O::V_RNDNE_F32: return FloatUnary(inst, IR::ValueOpcode::FPRoundEven32);
case O::V_FLOOR_F32: return FloatUnary(inst, IR::ValueOpcode::FPFloor32);
case O::V_EXP_F32: return FloatUnary(inst, IR::ValueOpcode::FPExp2);
case O::V_LOG_F32: return FloatUnary(inst, IR::ValueOpcode::FPLog2);
case O::V_RSQ_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecipSqrt32);
case O::V_SQRT_F32: return FloatUnary(inst, IR::ValueOpcode::FPSqrt);
case O::V_SIN_F32: return FloatUnary(inst, IR::ValueOpcode::FPSin);
case O::V_COS_F32: return FloatUnary(inst, IR::ValueOpcode::FPCos);
case O::V_ADD_F32: return FloatBinary(inst, IR::ValueOpcode::FPAdd32, false);
case O::V_SUB_F32: return FloatBinary(inst, IR::ValueOpcode::FPSub32, false);
case O::V_SUBREV_F32: return FloatBinary(inst, IR::ValueOpcode::FPSub32, true);
case O::V_MUL_F32: return FloatBinary(inst, IR::ValueOpcode::FPMul32, false);
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_MADMK_F32:
case O::V_MADAK_F32:
case O::V_MAD_F32:
case O::V_FMA_F32: return FloatTernary(inst, IR::ValueOpcode::FPFma32, false, true);
case O::V_MIN3_F32: return FloatTernary(inst, IR::ValueOpcode::FPMinTri32, false, false);
case O::V_MAX3_F32: return FloatTernary(inst, IR::ValueOpcode::FPMaxTri32, false, false);
case O::V_MED3_F32: return FloatTernary(inst, IR::ValueOpcode::FPMedTri32, false, false);
case O::V_DOT2C_F32_F16: return V_DOT2C_F32_F16(inst);
case O::V_CUBEID_F32: return V_CUBEID_F32(inst);
case O::V_CUBESC_F32: return V_CUBESC_F32(inst);
case O::V_CUBETC_F32: return V_CUBETC_F32(inst);
case O::V_CUBEMA_F32: return V_CUBEMA_F32(inst);
case O::V_MUL_LO_U32:
case O::V_MUL_LO_I32:
return SimpleInteger(inst, IR::ValueOpcode::IMul32, IR::Type::U32, false, false, false);
case O::V_MUL_HI_U32:
return SimpleInteger(inst, IR::ValueOpcode::UMulHi, IR::Type::U32, false, false, false);
case O::V_MUL_HI_I32:
return SimpleInteger(inst, IR::ValueOpcode::SMulHi, IR::Type::U32, false, false, false);
case O::V_ADD_NC_U32:
return SimpleInteger(inst, IR::ValueOpcode::IAdd32, IR::Type::U32, false, false, false);
case O::V_SUB_NC_U32:
return SimpleInteger(inst, IR::ValueOpcode::ISub32, IR::Type::U32, false, false, false);
case O::V_SUBREV_NC_U32:
return SimpleInteger(inst, IR::ValueOpcode::ISub32, IR::Type::U32, true, false, false);
case O::V_MIN_I32:
return SimpleInteger(inst, IR::ValueOpcode::SMin32, IR::Type::U32, false, false, false);
case O::V_MAX_I32:
return SimpleInteger(inst, IR::ValueOpcode::SMax32, IR::Type::U32, false, false, false);
case O::V_MIN_U32:
return SimpleInteger(inst, IR::ValueOpcode::UMin32, IR::Type::U32, false, false, false);
case O::V_MAX_U32:
return SimpleInteger(inst, IR::ValueOpcode::UMax32, IR::Type::U32, false, false, false);
case O::V_MIN3_I32:
return SimpleInteger(inst, IR::ValueOpcode::SMinTri32, IR::Type::U32, false, false,
false);
case O::V_MAX3_I32:
return SimpleInteger(inst, IR::ValueOpcode::SMaxTri32, IR::Type::U32, false, false,
false);
case O::V_MED3_I32:
return SimpleInteger(inst, IR::ValueOpcode::SMedTri32, IR::Type::U32, false, false,
false);
case O::V_MIN3_U32:
return SimpleInteger(inst, IR::ValueOpcode::UMinTri32, IR::Type::U32, false, false,
false);
case O::V_MAX3_U32:
return SimpleInteger(inst, IR::ValueOpcode::UMaxTri32, IR::Type::U32, false, false,
false);
case O::V_MED3_U32:
return SimpleInteger(inst, IR::ValueOpcode::UMedTri32, IR::Type::U32, false, false,
false);
case O::V_AND_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseAnd32, IR::Type::U32, false, false,
false);
case O::V_OR_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseOr32, IR::Type::U32, false, false,
false);
case O::V_XOR_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseXor32, IR::Type::U32, false, false,
false);
case O::V_NOT_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitwiseNot32, IR::Type::U32, false, false,
false);
case O::V_BFREV_B32:
return SimpleInteger(inst, IR::ValueOpcode::BitReverse32, IR::Type::U32, false, false,
false);
case O::V_FFBL_B32:
return SimpleInteger(inst, IR::ValueOpcode::FindILsb32, IR::Type::U32, false, false,
false);
case O::V_LSHL_B32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftLeftLogical32, IR::Type::U32, false,
true, false);
case O::V_LSHLREV_B32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftLeftLogical32, IR::Type::U32, true,
true, false);
case O::V_LSHR_B32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical32, IR::Type::U32, false,
true, false);
case O::V_LSHRREV_B32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical32, IR::Type::U32, true,
true, false);
case O::V_ASHR_I32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightArithmetic32, IR::Type::U32,
false, true, false);
case O::V_ASHRREV_I32:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightArithmetic32, IR::Type::U32, true,
true, false);
case O::V_LSHLREV_B64:
return SimpleInteger(inst, IR::ValueOpcode::ShiftLeftLogical64, IR::Type::U64, true,
false, false);
case O::V_LSHRREV_B64:
return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical64, IR::Type::U64, true,
false, false);
case O::V_XNOR_B32:
return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseXor32, false, true, false);
case O::V_AND_OR_B32: return V_AND_OR_B32(inst);
case O::V_OR3_B32: return V_OR3_B32(inst);
case O::V_XOR3_B32: return V_XOR3_B32(inst);
case O::V_FFBH_U32: return V_FFBH_U32(inst);
case O::V_MAD_I32_I24: return Integer24(inst, true, true);
case O::V_MAD_U32_U24: return Integer24(inst, false, true);
case O::V_MUL_I32_I24: return Integer24(inst, true, false);
case O::V_MUL_U32_U24: return Integer24(inst, false, false);
case O::V_MAD_U64_U32: return V_MAD_U64_U32(inst);
case O::V_SAD_U32: return V_SAD_U32(inst);
case O::V_ADD3_U32: return V_ADD3_U32(inst);
case O::V_BCNT_U32_B32: return V_BCNT_U32_B32(inst);
case O::V_MBCNT_LO_U32_B32: return V_MBCNT_U32_B32(inst, true);
case O::V_MBCNT_HI_U32_B32: return V_MBCNT_U32_B32(inst, false);
case O::V_BFM_B32: return BFM_B32(inst);
case O::V_BFE_U32: return V_BFE_U32(inst, false);
case O::V_BFE_I32: return V_BFE_U32(inst, true);
case O::V_BFI_B32: return V_BFI_B32(inst);
case O::V_ALIGNBIT_B32: return V_ALIGNBIT_B32(inst);
case O::V_ALIGNBYTE_B32: return V_ALIGNBYTE_B32(inst);
case O::V_LSHL_ADD_U32: return V_LSHL_ADD_U32(inst);
case O::V_ADD_LSHL_U32: return V_ADD_LSHL_U32(inst);
case O::V_XAD_U32: return V_XAD_U32(inst);
case O::V_LSHL_OR_B32: return V_LSHL_OR_B32(inst);
case O::V_CNDMASK_B32: return V_CNDMASK_B32(inst);
default: return false;
}
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend