mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
554 lines
26 KiB
C++
554 lines
26 KiB
C++
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
|
|
|
#include <array>
|
|
|
|
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
|
|
|
void Translator::Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
bool arithmetic) {
|
|
const auto value = ReadU16AsU32(inst.src1, arithmetic);
|
|
const auto count = ir.BitwiseAnd(ReadU16AsU32(inst.src0, false), IR::U32(IR::Value(15u)));
|
|
const auto result = IR::U32(ir.Emit(opcode, {value, count}));
|
|
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
|
}
|
|
|
|
void Translator::Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
bool sign) {
|
|
const auto lhs = ReadU16AsU32(inst.src0, sign);
|
|
const auto rhs = ReadU16AsU32(inst.src1, sign);
|
|
const auto result = IR::U32(ir.Emit(opcode, {lhs, rhs}));
|
|
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
|
}
|
|
|
|
void Translator::V_MAD_I16(const Decoder::Instruction& inst) {
|
|
const auto lhs = ReadU16AsU32(inst.src0, true);
|
|
const auto rhs = ReadU16AsU32(inst.src1, true);
|
|
auto result = ir.IAdd(ir.IMul(lhs, rhs), ReadU16AsU32(inst.src2, true));
|
|
if (inst.dst.clamp) {
|
|
result = IR::U32(ir.Emit(IR::ValueOpcode::SMax32, {result, IR::Value(0xffff8000u)}));
|
|
result = IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {result, IR::Value(0x7fffu)}));
|
|
}
|
|
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
|
}
|
|
|
|
void Translator::Integer16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
bool sign) {
|
|
const auto result = IR::U32(ir.Emit(
|
|
opcode, {ReadU16AsU32(inst.src0, sign), ReadU16AsU32(inst.src1, sign),
|
|
ReadU16AsU32(inst.src2, sign)}));
|
|
Write16Bits(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
bool arithmetic) {
|
|
const auto translate_lane = [&](bool high) {
|
|
const auto count =
|
|
ir.BitwiseAnd(ReadU16LaneAsU32(inst.src0, high, false), IR::U32(IR::Value(15u)));
|
|
const auto value = ReadU16LaneAsU32(inst.src1, high, arithmetic);
|
|
return IR::U32(ir.Emit(opcode, {value, count}));
|
|
};
|
|
WriteOperand(DestinationOperand(inst),
|
|
PackU16Lanes(translate_lane(false), translate_lane(true)));
|
|
}
|
|
|
|
void Translator::PackedInteger16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode) {
|
|
const auto translate_lane = [&](bool high) {
|
|
const auto lhs = ReadU16LaneAsU32(inst.src0, high, false);
|
|
const auto rhs = ReadU16LaneAsU32(inst.src1, high, false);
|
|
return IR::U32(ir.Emit(opcode, {lhs, rhs}));
|
|
};
|
|
WriteOperand(DestinationOperand(inst),
|
|
PackU16Lanes(translate_lane(false), translate_lane(true)));
|
|
}
|
|
|
|
void Translator::PackedInteger16Mad(const Decoder::Instruction& inst, bool sign) {
|
|
const auto translate_lane = [&](bool high) {
|
|
const auto lhs = ReadU16LaneAsU32(inst.src0, high, false);
|
|
const auto rhs = ReadU16LaneAsU32(inst.src1, high, false);
|
|
return ir.IAdd(ir.IMul(lhs, rhs), ReadU16LaneAsU32(inst.src2, high, sign));
|
|
};
|
|
WriteOperand(DestinationOperand(inst),
|
|
PackU16Lanes(translate_lane(false), translate_lane(true)));
|
|
}
|
|
|
|
void Translator::PackedInteger16MinMax(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
bool sign) {
|
|
const auto translate_lane = [&](bool high) {
|
|
const auto lhs = ReadU16LaneAsU32(inst.src0, high, sign);
|
|
const auto rhs = ReadU16LaneAsU32(inst.src1, high, sign);
|
|
return IR::U32(ir.Emit(opcode, {lhs, rhs}));
|
|
};
|
|
WriteOperand(DestinationOperand(inst),
|
|
PackU16Lanes(translate_lane(false), translate_lane(true)));
|
|
}
|
|
|
|
IR::U1 Translator::U64MaskBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
bool negate_rhs, bool negate_result) {
|
|
const auto lhs = ReadMask(inst.src0);
|
|
auto rhs = ReadMask(inst.src1);
|
|
if (negate_rhs) {
|
|
rhs = ir.LogicalNot(rhs);
|
|
}
|
|
auto result = IR::U1(ir.Emit(opcode, {lhs, rhs}));
|
|
return negate_result ? ir.LogicalNot(result) : result;
|
|
}
|
|
|
|
void Translator::S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode logical_opcode,
|
|
IR::ValueOpcode bit_opcode, bool negate_rhs, bool negate_result,
|
|
bool unary) {
|
|
const auto invocation_result =
|
|
unary ? ir.LogicalNot(ReadMask(inst.src0))
|
|
: U64MaskBinary(inst, logical_opcode, negate_rhs, negate_result);
|
|
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))) {
|
|
const auto mask = WriteMask(inst.dst, invocation_result, true);
|
|
ir.SetScc(ir.INotEqual(ir.BitwiseOr(mask[0], mask[1]), IR::U32(IR::Value(0u))));
|
|
return;
|
|
}
|
|
|
|
// Descriptor tracking reasons about each scalar word independently.
|
|
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 (unary) {
|
|
result = {ir.BitwiseNot(lhs[0]), ir.BitwiseNot(lhs[1])};
|
|
} else {
|
|
const auto rhs = ReadU32Pair(inst.src1);
|
|
for (uint32_t component = 0; component < 2u; component++) {
|
|
auto value = ir.Emit(
|
|
bit_opcode, {lhs[component], negate_rhs ? IR::Value(ir.BitwiseNot(rhs[component]))
|
|
: IR::Value(rhs[component])});
|
|
result[component] = negate_result ? ir.BitwiseNot(IR::U32(value)) : IR::U32(value);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
ir.SetScc(ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u))));
|
|
}
|
|
|
|
void Translator::SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
IR::Type type, bool reverse, bool mask_shift_count,
|
|
bool update_scc) {
|
|
std::array<IR::Value, 3> args;
|
|
for (uint32_t index = 0; index < inst.src_count; index++) {
|
|
const auto arg_type = IR::ArgTypeOf(opcode, index);
|
|
const auto& operand = SourceAt(inst, reverse && index < 2u ? 1u - index : index);
|
|
args[index] = ReadOperand(operand, arg_type == IR::Type::Void ? type : arg_type);
|
|
if (mask_shift_count && index == 1u) {
|
|
args[index] = ir.BitwiseAnd(IR::U32(args[index]), IR::U32(IR::Value(31u)));
|
|
}
|
|
}
|
|
IR::Value result;
|
|
switch (inst.src_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 simple integer source count: %u", inst.src_count);
|
|
}
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
if (update_scc) {
|
|
if (IR::TypeOf(opcode) == IR::Type::U64) {
|
|
ir.SetScc(
|
|
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
|
} else {
|
|
ir.SetScc(ir.INotEqual(IR::U32(result), IR::U32(IR::Value(0u))));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Translator::S_ASHR_I64(const Decoder::Instruction& inst) {
|
|
// Signed 64-bit sources sign-extend literals; generic B64 operands zero-extend them.
|
|
const auto source =
|
|
inst.src0.kind == Decoder::OperandKind::LiteralConstant
|
|
? ir.ConstructU64(
|
|
ReadU32(inst.src0),
|
|
IR::U32(IR::Value((inst.src0.value & 0x80000000u) ? 0xffffffffu : 0u)))
|
|
: ReadU64(inst.src0);
|
|
const auto result =
|
|
ir.Emit(IR::ValueOpcode::ShiftRightArithmetic64, {source, ReadU32(inst.src1)});
|
|
WriteOperand(inst.dst, result);
|
|
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
|
}
|
|
|
|
void Translator::ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
|
bool negate_rhs, bool negate_result, bool update_scc) {
|
|
const auto lhs = ReadU32(inst.src0);
|
|
auto rhs = ReadU32(inst.src1);
|
|
if (negate_rhs) {
|
|
rhs = ir.BitwiseNot(rhs);
|
|
}
|
|
auto result = IR::U32(ir.Emit(opcode, {lhs, rhs}));
|
|
if (negate_result) {
|
|
result = ir.BitwiseNot(result);
|
|
}
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
if (update_scc) {
|
|
ir.SetScc(ir.INotEqual(result, IR::U32(IR::Value(0u))));
|
|
}
|
|
}
|
|
|
|
void Translator::V_AND_OR_B32(const Decoder::Instruction& inst) {
|
|
const auto result =
|
|
ir.BitwiseOr(ir.BitwiseAnd(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_OR3_B32(const Decoder::Instruction& inst) {
|
|
const auto result =
|
|
ir.BitwiseOr(ir.BitwiseOr(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_XOR3_B32(const Decoder::Instruction& inst) {
|
|
const auto result =
|
|
ir.BitwiseXor(ir.BitwiseXor(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::S_FF1_I32_B64(const Decoder::Instruction& inst) {
|
|
const auto source = ExtractU64(ReadU64(inst.src0));
|
|
const auto low_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[0]}));
|
|
const auto high_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[1]}));
|
|
const auto high_position = ir.IAdd(high_lsb, IR::U32(IR::Value(32u)));
|
|
const auto result = ir.Select(ir.INotEqual(source[0], IR::U32(IR::Value(0u))), low_lsb,
|
|
ir.Select(ir.INotEqual(source[1], IR::U32(IR::Value(0u))),
|
|
high_position, IR::U32(IR::Value(0xffffffffu))));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_FFBH_32(const Decoder::Instruction& inst, bool sign) {
|
|
const auto source = ReadU32(inst.src0);
|
|
const auto value = sign ? ir.BitwiseXor(
|
|
source, ir.ShiftRightArithmetic(source, IR::U32(IR::Value(31u))))
|
|
: source;
|
|
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {value}));
|
|
const auto position = ir.ISub(IR::U32(IR::Value(31u)), msb);
|
|
const auto result = ir.Select(ir.INotEqual(value, IR::U32(IR::Value(0u))), position,
|
|
IR::U32(IR::Value(0xffffffffu)));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::S_FLBIT_I32_B64(const Decoder::Instruction& inst) {
|
|
const auto source = ReadU64(inst.src0);
|
|
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb64, {source}));
|
|
const auto position = ir.ISub(IR::U32(IR::Value(63u)), msb);
|
|
const auto nonzero =
|
|
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64,
|
|
{source, ir.ConstructU64(IR::U32(IR::Value(0u)), IR::U32(IR::Value(0u)))}));
|
|
const auto result = ir.Select(nonzero, position, IR::U32(IR::Value(0xffffffffu)));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::Integer24(const Decoder::Instruction& inst, bool sign, bool addend) {
|
|
const auto extract24 = [&](IR::U32 value) {
|
|
return IR::U32(
|
|
ir.Emit(sign ? IR::ValueOpcode::BitFieldSExtract : IR::ValueOpcode::BitFieldUExtract,
|
|
{value, IR::Value(0u), IR::Value(24u)}));
|
|
};
|
|
const auto lhs = extract24(ReadU32(inst.src0));
|
|
const auto rhs = extract24(ReadU32(inst.src1));
|
|
auto result = ir.IMul(lhs, rhs);
|
|
if (addend) {
|
|
result = ir.IAdd(result, ReadU32(inst.src2));
|
|
}
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_MAD_U64_U32(const Decoder::Instruction& inst) {
|
|
const auto lhs = ReadU32(inst.src0);
|
|
const auto rhs = ReadU32(inst.src1);
|
|
// Keep the 32x32 product in words: native widening IMul64 fails NVIDIA compilation
|
|
// in large vertex shaders (Astro's Playroom). The 64-bit addition stays native.
|
|
const auto product = ir.ConstructU64(
|
|
ir.IMul(lhs, rhs), IR::U32(ir.Emit(IR::ValueOpcode::UMulHi, {lhs, rhs})));
|
|
const auto result = ir.Emit(IR::ValueOpcode::IAdd64, {product, ReadU64(inst.src2)});
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
if (inst.dst2.kind != Decoder::OperandKind::Null &&
|
|
inst.dst2.kind != Decoder::OperandKind::Unknown) {
|
|
// The 32x32 product fits in 64 bits, so only the addition can carry.
|
|
WriteMask(inst.dst2, IR::U1(ir.Emit(IR::ValueOpcode::ULessThan64, {result, product})));
|
|
}
|
|
}
|
|
|
|
void Translator::V_SAD_U32(const Decoder::Instruction& inst) {
|
|
const auto lhs = ReadU32(inst.src0);
|
|
const auto rhs = ReadU32(inst.src1);
|
|
const auto lo = IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {lhs, rhs}));
|
|
const auto hi = IR::U32(ir.Emit(IR::ValueOpcode::UMax32, {lhs, rhs}));
|
|
const auto result = ir.IAdd(ir.ISub(hi, lo), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_ADD3_U32(const Decoder::Instruction& inst) {
|
|
const auto result =
|
|
ir.IAdd(ir.IAdd(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::S_BITSET_B32(const Decoder::Instruction& inst, bool set) {
|
|
const auto offset = ir.BitwiseAnd(ReadU32(inst.src0), IR::U32(IR::Value(31u)));
|
|
const auto bit = ir.ShiftLeftLogical(IR::U32(IR::Value(1u)), offset);
|
|
const auto old = ReadU32(inst.dst);
|
|
const auto result = set ? ir.BitwiseOr(old, bit) : ir.BitwiseAnd(old, ir.BitwiseNot(bit));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::S_BITSET_B64(const Decoder::Instruction& inst, bool set) {
|
|
const auto offset = ir.BitwiseAnd(ReadU32(inst.src0), IR::U32(IR::Value(63u)));
|
|
const auto word_bit = ir.BitwiseAnd(offset, IR::U32(IR::Value(31u)));
|
|
const auto bit = ir.ShiftLeftLogical(IR::U32(IR::Value(1u)), word_bit);
|
|
const auto old = ReadU32Pair(inst.dst);
|
|
const auto low_value = set ? ir.BitwiseOr(old[0], bit)
|
|
: ir.BitwiseAnd(old[0], ir.BitwiseNot(bit));
|
|
const auto high_value = set ? ir.BitwiseOr(old[1], bit)
|
|
: ir.BitwiseAnd(old[1], ir.BitwiseNot(bit));
|
|
const auto high = IR::U1(ir.Emit(IR::ValueOpcode::UGreaterThanEqual32,
|
|
{offset, IR::Value(32u)}));
|
|
WriteU32Pair(inst.dst,
|
|
{ir.Select(high, old[0], low_value), ir.Select(high, high_value, old[1])});
|
|
}
|
|
|
|
void Translator::V_BCNT_U32_B32(const Decoder::Instruction& inst) {
|
|
const auto count = IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {ReadU32(inst.src0)}));
|
|
WriteOperand(DestinationOperand(inst), ir.IAdd(count, ReadU32(inst.src1)));
|
|
}
|
|
|
|
void Translator::V_MBCNT_U32_B32(const Decoder::Instruction& inst, bool low) {
|
|
const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId));
|
|
const auto local = ir.BitwiseAnd(lane, IR::U32(IR::Value(31u)));
|
|
const auto below =
|
|
ir.ISub(ir.ShiftLeftLogical(IR::U32(IR::Value(1u)), local), IR::U32(IR::Value(1u)));
|
|
const auto high_lane =
|
|
IR::U1(ir.Emit(IR::ValueOpcode::UGreaterThanEqual32, {lane, IR::Value(32u)}));
|
|
const auto thread_mask = low ? ir.Select(high_lane, IR::U32(IR::Value(0xffffffffu)), below)
|
|
: ir.Select(high_lane, below, IR::U32(IR::Value(0u)));
|
|
const auto active = ir.BitwiseAnd(ReadU32(inst.src0), thread_mask);
|
|
const auto count = IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {active}));
|
|
WriteOperand(DestinationOperand(inst), ir.IAdd(count, ReadU32(inst.src1)));
|
|
}
|
|
|
|
void Translator::S_BITREPLICATE_B64_B32(const Decoder::Instruction& inst) {
|
|
const auto replicate = [&](IR::U32 value) {
|
|
auto bits = ir.BitwiseOr(value, ir.ShiftLeftLogical(value, IR::U32(IR::Value(8u))));
|
|
bits = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x00ff00ffu)));
|
|
bits = ir.BitwiseOr(bits, ir.ShiftLeftLogical(bits, IR::U32(IR::Value(4u))));
|
|
bits = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x0f0f0f0fu)));
|
|
bits = ir.BitwiseOr(bits, ir.ShiftLeftLogical(bits, IR::U32(IR::Value(2u))));
|
|
bits = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x33333333u)));
|
|
bits = ir.BitwiseOr(bits, ir.ShiftLeftLogical(bits, IR::U32(IR::Value(1u))));
|
|
bits = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x55555555u)));
|
|
return ir.BitwiseOr(bits, ir.ShiftLeftLogical(bits, IR::U32(IR::Value(1u))));
|
|
};
|
|
const auto source = ReadU32(inst.src0);
|
|
const auto low = ir.BitwiseAnd(source, IR::U32(IR::Value(0xffffu)));
|
|
const auto high = ir.ShiftRightLogical(source, IR::U32(IR::Value(16u)));
|
|
WriteOperand(DestinationOperand(inst), ir.ConstructU64(replicate(low), replicate(high)));
|
|
}
|
|
|
|
void Translator::S_QUADMASK_B64(const Decoder::Instruction& inst) {
|
|
const auto compact = [&](IR::U32 value) {
|
|
auto bits = ir.BitwiseOr(value, ir.ShiftRightLogical(value, IR::U32(IR::Value(1u))));
|
|
bits = ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, IR::U32(IR::Value(2u))));
|
|
bits = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x11111111u)));
|
|
bits = ir.BitwiseAnd(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, IR::U32(IR::Value(3u)))),
|
|
IR::U32(IR::Value(0x03030303u)));
|
|
bits = ir.BitwiseAnd(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, IR::U32(IR::Value(6u)))),
|
|
IR::U32(IR::Value(0x000f000fu)));
|
|
return ir.BitwiseAnd(
|
|
ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, IR::U32(IR::Value(12u)))),
|
|
IR::U32(IR::Value(0xffu)));
|
|
};
|
|
const auto source = ReadU32Pair(inst.src0);
|
|
const auto quads = ir.BitwiseOr(
|
|
compact(source[0]), ir.ShiftLeftLogical(compact(source[1]), IR::U32(IR::Value(8u))));
|
|
const auto result = ir.ConstructU64(quads, IR::U32(IR::Value(0u)));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
|
}
|
|
|
|
void Translator::BFM_B32(const Decoder::Instruction& inst) {
|
|
const auto count = ir.BitwiseAnd(ReadU32(inst.src0), IR::U32(IR::Value(31u)));
|
|
const auto offset = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(31u)));
|
|
const auto result = ir.Emit(IR::ValueOpcode::BitFieldInsert,
|
|
{IR::Value(0u), IR::Value(0xffffffffu), offset, count});
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
IR::U32 Translator::RightMask32(IR::U32 count) {
|
|
return IR::U32(ir.Emit(IR::ValueOpcode::BitFieldInsert,
|
|
{IR::Value(0u), IR::Value(0xffffffffu), IR::Value(0u), count}));
|
|
}
|
|
|
|
IR::U64 Translator::RightMask64(IR::U32 count) {
|
|
const auto below32 = IR::U1(ir.Emit(IR::ValueOpcode::ULessThan32, {count, IR::Value(32u)}));
|
|
const auto above32 = IR::U1(ir.Emit(IR::ValueOpcode::UGreaterThan32, {count, IR::Value(32u)}));
|
|
const auto low_count = ir.Select(below32, count, IR::U32(IR::Value(32u)));
|
|
const auto high_count =
|
|
ir.Select(above32, ir.ISub(count, IR::U32(IR::Value(32u))), IR::U32(IR::Value(0u)));
|
|
return ir.ConstructU64(RightMask32(low_count), RightMask32(high_count));
|
|
}
|
|
|
|
void Translator::S_BFM_B64(const Decoder::Instruction& inst) {
|
|
const auto count = ReadU32(inst.src0);
|
|
const auto offset = ReadU32(inst.src1);
|
|
const auto one = IR::Value(uint64_t {1});
|
|
const auto limit = ir.Emit(IR::ValueOpcode::ShiftLeftLogical64, {one, count});
|
|
const auto mask = ir.Emit(IR::ValueOpcode::ISub64, {limit, one});
|
|
const auto result = ir.Emit(IR::ValueOpcode::ShiftLeftLogical64, {mask, offset});
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::S_BFE_U32(const Decoder::Instruction& inst, bool sign) {
|
|
const auto source = ReadU32(inst.src0);
|
|
const auto field = ReadU32(inst.src1);
|
|
const auto offset =
|
|
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {field, IR::Value(0u), IR::Value(5u)}));
|
|
const auto raw_count =
|
|
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {field, IR::Value(16u), IR::Value(7u)}));
|
|
const auto count = IR::U32(
|
|
ir.Emit(IR::ValueOpcode::UMin32, {raw_count, ir.ISub(IR::U32(IR::Value(32u)), offset)}));
|
|
const auto opcode =
|
|
sign ? IR::ValueOpcode::BitFieldSExtract : IR::ValueOpcode::BitFieldUExtract;
|
|
const auto result = IR::U32(ir.Emit(opcode, {source, offset, count}));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
ir.SetScc(ir.INotEqual(result, IR::U32(IR::Value(0u))));
|
|
}
|
|
|
|
void Translator::S_BFE_U64(const Decoder::Instruction& inst) {
|
|
const auto source = ReadU64(inst.src0);
|
|
const auto field = ReadU32(inst.src1);
|
|
const auto offset =
|
|
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {field, IR::Value(0u), IR::Value(6u)}));
|
|
const auto raw_count =
|
|
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {field, IR::Value(16u), IR::Value(7u)}));
|
|
const auto available = ir.ISub(IR::U32(IR::Value(64u)), offset);
|
|
const auto count = IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {raw_count, available}));
|
|
const auto shifted = IR::U64(ir.Emit(IR::ValueOpcode::ShiftRightLogical64, {source, offset}));
|
|
const auto result = ir.Emit(IR::ValueOpcode::BitwiseAnd64, {shifted, RightMask64(count)});
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
|
}
|
|
|
|
void Translator::V_BFE_U32(const Decoder::Instruction& inst, bool sign) {
|
|
const auto source = ReadU32(inst.src0);
|
|
const auto offset = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(31u)));
|
|
const auto raw_count = ir.BitwiseAnd(ReadU32(inst.src2), IR::U32(IR::Value(31u)));
|
|
const auto count = IR::U32(
|
|
ir.Emit(IR::ValueOpcode::UMin32, {raw_count, ir.ISub(IR::U32(IR::Value(32u)), offset)}));
|
|
const auto opcode =
|
|
sign ? IR::ValueOpcode::BitFieldSExtract : IR::ValueOpcode::BitFieldUExtract;
|
|
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {source, offset, count}));
|
|
}
|
|
|
|
void Translator::V_BFI_B32(const Decoder::Instruction& inst) {
|
|
const auto bits = ReadU32(inst.src0);
|
|
const auto insert = ReadU32(inst.src1);
|
|
const auto base = ReadU32(inst.src2);
|
|
const auto result =
|
|
ir.BitwiseOr(ir.BitwiseAnd(bits, insert), ir.BitwiseAnd(ir.BitwiseNot(bits), base));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::S_BITCMP_B32(const Decoder::Instruction& inst, bool expected) {
|
|
const auto value = ReadU32(inst.src0);
|
|
const auto offset = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(31u)));
|
|
const auto bit =
|
|
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {value, offset, IR::Value(1u)}));
|
|
WriteCompareResult(inst.dst, ir.IEqual(bit, IR::U32(IR::Value(expected ? 1u : 0u))));
|
|
}
|
|
|
|
void Translator::S_BITCMP_B64(const Decoder::Instruction& inst, bool expected) {
|
|
const auto value = ReadU32Pair(inst.src0);
|
|
const auto offset = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(63u)));
|
|
const auto word_bit = ir.BitwiseAnd(offset, IR::U32(IR::Value(31u)));
|
|
const auto word =
|
|
ir.Select(ir.ULessThan(offset, IR::U32(IR::Value(32u))), value[0], value[1]);
|
|
const auto bit =
|
|
IR::U32(ir.Emit(IR::ValueOpcode::BitFieldUExtract, {word, word_bit, IR::Value(1u)}));
|
|
WriteCompareResult(inst.dst, ir.IEqual(bit, IR::U32(IR::Value(expected ? 1u : 0u))));
|
|
}
|
|
|
|
void Translator::V_ALIGN_B32(const Decoder::Instruction& inst, bool byte_offset) {
|
|
const auto hi = ReadU32(inst.src0);
|
|
const auto lo = ReadU32(inst.src1);
|
|
auto shift = ir.BitwiseAnd(ReadU32(inst.src2), IR::U32(IR::Value(byte_offset ? 3u : 31u)));
|
|
if (byte_offset) shift = ir.ShiftLeftLogical(shift, IR::U32(IR::Value(3u)));
|
|
const auto lo_part = ir.ShiftRightLogical(lo, shift);
|
|
const auto inverse =
|
|
ir.BitwiseAnd(ir.ISub(IR::U32(IR::Value(32u)), shift), IR::U32(IR::Value(31u)));
|
|
const auto hi_part_raw = ir.ShiftLeftLogical(hi, inverse);
|
|
const auto hi_part =
|
|
ir.Select(ir.INotEqual(shift, IR::U32(IR::Value(0u))), hi_part_raw, IR::U32(IR::Value(0u)));
|
|
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(lo_part, hi_part));
|
|
}
|
|
|
|
void Translator::V_LSHL_ADD_U32(const Decoder::Instruction& inst) {
|
|
const auto shift = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(31u)));
|
|
const auto result = ir.IAdd(ir.ShiftLeftLogical(ReadU32(inst.src0), shift), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_ADD_LSHL_U32(const Decoder::Instruction& inst) {
|
|
const auto shift = ir.BitwiseAnd(ReadU32(inst.src2), IR::U32(IR::Value(31u)));
|
|
const auto result = ir.ShiftLeftLogical(ir.IAdd(ReadU32(inst.src0), ReadU32(inst.src1)), shift);
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_XAD_U32(const Decoder::Instruction& inst) {
|
|
const auto result =
|
|
ir.IAdd(ir.BitwiseXor(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_LSHL_OR_B32(const Decoder::Instruction& inst) {
|
|
const auto shift = ir.BitwiseAnd(ReadU32(inst.src1), IR::U32(IR::Value(31u)));
|
|
const auto result =
|
|
ir.BitwiseOr(ir.ShiftLeftLogical(ReadU32(inst.src0), shift), ReadU32(inst.src2));
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::V_CNDMASK_B32(const Decoder::Instruction& inst) {
|
|
Decoder::Operand mask_operand;
|
|
mask_operand.kind = Decoder::OperandKind::VccLo;
|
|
if (inst.src_count >= 3u) {
|
|
mask_operand = inst.src2;
|
|
}
|
|
const auto condition = ReadMask(mask_operand);
|
|
IR::Value result;
|
|
if (inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute) {
|
|
result =
|
|
ir.Emit(IR::ValueOpcode::SelectF32, {condition, ReadOperand(inst.src1, IR::Type::F32),
|
|
ReadOperand(inst.src0, IR::Type::F32)});
|
|
} else {
|
|
result = ir.Select(condition, ReadU32(inst.src1), ReadU32(inst.src0));
|
|
}
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
void Translator::PackB16(const Decoder::Instruction& inst, bool high0, bool high1) {
|
|
const auto lo = high0 ? ir.ShiftRightLogical(ReadU32(inst.src0), IR::U32(IR::Value(16u)))
|
|
: ReadU32(inst.src0);
|
|
const auto hi = high1 ? ir.ShiftRightLogical(ReadU32(inst.src1), IR::U32(IR::Value(16u)))
|
|
: ReadU32(inst.src1);
|
|
const auto result = PackU16Lanes(lo, hi);
|
|
WriteOperand(DestinationOperand(inst), result);
|
|
}
|
|
|
|
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|