mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
shader: implement S_ORN2_SAVEEXEC_B32
This commit is contained in:
@@ -62,6 +62,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = {
|
|||||||
{0x37u, Opcode::S_ANDN1_SAVEEXEC_B64},
|
{0x37u, Opcode::S_ANDN1_SAVEEXEC_B64},
|
||||||
{0x3bu, Opcode::S_BITREPLICATE_B64_B32},
|
{0x3bu, Opcode::S_BITREPLICATE_B64_B32},
|
||||||
{0x3cu, Opcode::S_AND_SAVEEXEC_B32},
|
{0x3cu, Opcode::S_AND_SAVEEXEC_B32},
|
||||||
|
{0x40u, Opcode::S_ORN2_SAVEEXEC_B32},
|
||||||
{0x44u, Opcode::S_ANDN1_SAVEEXEC_B32},
|
{0x44u, Opcode::S_ANDN1_SAVEEXEC_B32},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -485,6 +485,7 @@ std::string InstructionToString(const Instruction& inst) {
|
|||||||
case Opcode::S_WQM_B64:
|
case Opcode::S_WQM_B64:
|
||||||
case Opcode::S_QUADMASK_B64:
|
case Opcode::S_QUADMASK_B64:
|
||||||
case Opcode::S_AND_SAVEEXEC_B32:
|
case Opcode::S_AND_SAVEEXEC_B32:
|
||||||
|
case Opcode::S_ORN2_SAVEEXEC_B32:
|
||||||
case Opcode::S_ANDN1_SAVEEXEC_B32:
|
case Opcode::S_ANDN1_SAVEEXEC_B32:
|
||||||
case Opcode::S_AND_SAVEEXEC_B64:
|
case Opcode::S_AND_SAVEEXEC_B64:
|
||||||
case Opcode::S_ORN2_SAVEEXEC_B64:
|
case Opcode::S_ORN2_SAVEEXEC_B64:
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ enum class Opcode {
|
|||||||
S_GETPC_B64,
|
S_GETPC_B64,
|
||||||
S_SETPC_B64,
|
S_SETPC_B64,
|
||||||
S_AND_SAVEEXEC_B32,
|
S_AND_SAVEEXEC_B32,
|
||||||
|
S_ORN2_SAVEEXEC_B32,
|
||||||
S_ANDN1_SAVEEXEC_B32,
|
S_ANDN1_SAVEEXEC_B32,
|
||||||
S_AND_SAVEEXEC_B64,
|
S_AND_SAVEEXEC_B64,
|
||||||
S_ORN2_SAVEEXEC_B64,
|
S_ORN2_SAVEEXEC_B64,
|
||||||
|
|||||||
@@ -24,27 +24,34 @@ Decoder::Operand ConditionOperand(Decoder::OperandKind kind) {
|
|||||||
|
|
||||||
void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation,
|
void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation,
|
||||||
bool negate_exec, bool negate_source, bool write_64) {
|
bool negate_exec, bool negate_source, bool write_64) {
|
||||||
|
if (!write_64) {
|
||||||
|
// Read the encoded scalar word and preserve EXEC_HI, including in wave32.
|
||||||
|
const auto old = ir.GetExecLo();
|
||||||
|
const auto src = ReadU32(inst.src0);
|
||||||
|
const auto lhs = negate_exec ? ir.BitwiseNot(old) : old;
|
||||||
|
const auto rhs = negate_source ? ir.BitwiseNot(src) : src;
|
||||||
|
IR::U32 result;
|
||||||
|
switch (operation) {
|
||||||
|
case IR::ValueOpcode::LogicalAnd: result = ir.BitwiseAnd(lhs, rhs); break;
|
||||||
|
case IR::ValueOpcode::LogicalOr: result = ir.BitwiseOr(lhs, rhs); break;
|
||||||
|
default: EXIT("unsupported SAVEEXEC operation");
|
||||||
|
}
|
||||||
|
WriteRawU32(inst.dst, old);
|
||||||
|
WriteRawU32(ConditionOperand(Decoder::OperandKind::ExecLo), result);
|
||||||
|
ir.SetScc(ir.INotEqual(result, IR::U32(IR::Value(0u))));
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto old = ir.GetExec();
|
const auto old = ir.GetExec();
|
||||||
const auto src = ReadMask(inst.src0);
|
const auto src = ReadMask(inst.src0);
|
||||||
const auto lhs = negate_exec ? ir.LogicalNot(old) : old;
|
const auto lhs = negate_exec ? ir.LogicalNot(old) : old;
|
||||||
const auto rhs = negate_source ? ir.LogicalNot(src) : src;
|
const auto rhs = negate_source ? ir.LogicalNot(src) : src;
|
||||||
auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
|
const auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
|
||||||
if (write_64) {
|
WriteMask(inst.dst, old, true);
|
||||||
WriteMask(inst.dst, old, true);
|
|
||||||
} else {
|
|
||||||
WriteRawU32(inst.dst, ir.GetExecLo());
|
|
||||||
if (program.wave_size == 64u) {
|
|
||||||
const auto low_half =
|
|
||||||
ir.ULessThan(IR::U32(ir.Emit(IR::ValueOpcode::LaneId)), IR::U32(IR::Value(32u)));
|
|
||||||
result = IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {low_half, result, old}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto mask = BallotMask(result);
|
const auto mask = BallotMask(result);
|
||||||
ir.SetExec(result);
|
ir.SetExec(result);
|
||||||
ir.SetExecLo(mask[0]);
|
ir.SetExecLo(mask[0]);
|
||||||
ir.SetExecHi(mask[1]);
|
ir.SetExecHi(mask[1]);
|
||||||
ir.SetScc(
|
ir.SetScc(ir.INotEqual(ir.BitwiseOr(mask[0], mask[1]), IR::U32(IR::Value(0u))));
|
||||||
ir.INotEqual(write_64 ? ir.BitwiseOr(mask[0], mask[1]) : mask[0], IR::U32(IR::Value(0u))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Translator::ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in) {
|
void Translator::ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in) {
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
|
|||||||
case O::S_ANDN1_SAVEEXEC_B32:
|
case O::S_ANDN1_SAVEEXEC_B32:
|
||||||
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, true, false);
|
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, true, false);
|
||||||
return true;
|
return true;
|
||||||
|
case O::S_ORN2_SAVEEXEC_B32:
|
||||||
|
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalOr, true, false, false);
|
||||||
|
return true;
|
||||||
case O::S_AND_SAVEEXEC_B64:
|
case O::S_AND_SAVEEXEC_B64:
|
||||||
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, false, true);
|
S_SAVEEXEC(inst, IR::ValueOpcode::LogicalAnd, false, false, true);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -14968,6 +14968,69 @@ TestCase ScalarOrn2SaveexecUsesSourceOrNotExec() {
|
|||||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase ScalarOrn2SaveexecB32(u32 wave_size, u32 threads) {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
constexpr u32 exec_hi = 0xa5a55a5au;
|
||||||
|
constexpr u32 vcc_hi = 0x13579bdfu;
|
||||||
|
struct Case {
|
||||||
|
u32 exec_lo, source, destination, result;
|
||||||
|
};
|
||||||
|
constexpr Case cases[] = {
|
||||||
|
{0x8000000cu, 126, 106, 0xffffffffu}, // Captured vcc_lo, exec_lo.
|
||||||
|
{0x0000000fu, 6, 6, 0xfffffff3u}, // Aliased SGPR source/destination.
|
||||||
|
{0xffffffffu, 128, 8, 0}, // SCC ignores the nonzero high half.
|
||||||
|
{0xffffffffu, 107, 8, vcc_hi},
|
||||||
|
{0xffffffffu, 127, 8, exec_hi},
|
||||||
|
{0xffffffffu, 253, 8, 1}, // SCC is a scalar value, not a mask.
|
||||||
|
};
|
||||||
|
TestCase test;
|
||||||
|
test.name = wave_size == 64 ? "ScalarOrn2SaveexecB32Wave64"
|
||||||
|
: threads == 32 ? "ScalarOrn2SaveexecB32Wave32"
|
||||||
|
: "ScalarOrn2SaveexecB32PartialWave";
|
||||||
|
auto &code = test.code;
|
||||||
|
for (const auto &item : cases) {
|
||||||
|
AppendVMovU32(&code, 2, 0);
|
||||||
|
AppendSMovLiteral(&code, 126, item.exec_lo);
|
||||||
|
AppendSMovLiteral(&code, 127, exec_hi);
|
||||||
|
AppendSMovLiteral(&code, 107, vcc_hi);
|
||||||
|
AppendSMovLiteral(&code, 6, 3);
|
||||||
|
code.push_back(EncodeSopc(0x06, InlineU32(0), InlineU32(0)));
|
||||||
|
code.push_back(item.source == 126 ? 0xbeea407eu
|
||||||
|
: EncodeSop1(0x40, item.destination, item.source));
|
||||||
|
const u32 snapshots[] = {item.destination, 126, 127, 107, 253};
|
||||||
|
for (u32 i = 0; i < std::size(snapshots); ++i) {
|
||||||
|
code.push_back(EncodeSMovB32(20 + i, snapshots[i]));
|
||||||
|
}
|
||||||
|
AppendVMovU32(&code, 2, 1);
|
||||||
|
code.push_back(EncodeSop1(0x04, 126, 193u)); // Restore EXEC for readback.
|
||||||
|
const u32 expected[] = {item.exec_lo, item.result, exec_hi, vcc_hi,
|
||||||
|
item.result != 0 ? 1u : 0u};
|
||||||
|
for (u32 i = 0; i < std::size(expected); ++i) {
|
||||||
|
AppendStoreSgprAtLaneDwordOffset(&code, 20 + i, 0,
|
||||||
|
static_cast<u32>(test.expected.size()));
|
||||||
|
test.expected.insert(test.expected.end(), threads, expected[i]);
|
||||||
|
}
|
||||||
|
AppendStoreVgprAtLaneDwordOffset(&code, 2, 0,
|
||||||
|
static_cast<u32>(test.expected.size()));
|
||||||
|
for (u32 lane = 0; lane < threads; ++lane) {
|
||||||
|
const u32 mask = lane < 32 ? item.result : exec_hi;
|
||||||
|
test.expected.push_back((mask >> (lane % 32)) & 1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppendEnd(&code);
|
||||||
|
test.initial.resize(test.expected.size());
|
||||||
|
test.opcodes = {O::S_MOV_B32, O::S_MOV_B64, O::S_CMP_EQ_U32,
|
||||||
|
O::S_ORN2_SAVEEXEC_B32, O::V_MOV_B32, O::V_LSHLREV_B32,
|
||||||
|
O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.decoded_counts = {{"S_ORN2_SAVEEXEC_B32 vcc_lo, exec_lo", 1}};
|
||||||
|
test.compute_info.threads_num[0] = threads;
|
||||||
|
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1;
|
||||||
|
test.compute_info.thread_ids_num = 1;
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.has_compute_info = true;
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase ScalarGetpcWritesNextInstructionPc() {
|
TestCase ScalarGetpcWritesNextInstructionPc() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -23993,6 +24056,9 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(ScalarFlbitI32B32Gpu);
|
AddCase(ScalarFlbitI32B32Gpu);
|
||||||
AddCase(ScalarSaveExecOps);
|
AddCase(ScalarSaveExecOps);
|
||||||
AddCase(ScalarOrn2SaveexecUsesSourceOrNotExec);
|
AddCase(ScalarOrn2SaveexecUsesSourceOrNotExec);
|
||||||
|
cases.push_back(ScalarOrn2SaveexecB32(32, 32));
|
||||||
|
cases.push_back(ScalarOrn2SaveexecB32(64, 64));
|
||||||
|
cases.push_back(ScalarOrn2SaveexecB32(32, 4));
|
||||||
AddCase(ScalarGetpcWritesNextInstructionPc);
|
AddCase(ScalarGetpcWritesNextInstructionPc);
|
||||||
AddCase(ScalarBitfieldPack);
|
AddCase(ScalarBitfieldPack);
|
||||||
AddCase(ScalarBrevB32PreservesScc);
|
AddCase(ScalarBrevB32PreservesScc);
|
||||||
@@ -28448,6 +28514,9 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, DispatcherIrreducibleControlFlow());
|
RunCase(&vulkan, DispatcherIrreducibleControlFlow());
|
||||||
RunCase(&vulkan, ScalarSaveExecOps());
|
RunCase(&vulkan, ScalarSaveExecOps());
|
||||||
RunCase(&vulkan, ScalarOrn2SaveexecUsesSourceOrNotExec());
|
RunCase(&vulkan, ScalarOrn2SaveexecUsesSourceOrNotExec());
|
||||||
|
RunCase(&vulkan, ScalarOrn2SaveexecB32(32, 32));
|
||||||
|
RunCase(&vulkan, ScalarOrn2SaveexecB32(64, 64));
|
||||||
|
RunCase(&vulkan, ScalarOrn2SaveexecB32(32, 4));
|
||||||
RunCase(&vulkan, ScalarNotB64UpdatesScc());
|
RunCase(&vulkan, ScalarNotB64UpdatesScc());
|
||||||
RunCase(&vulkan, ScalarSelectB64PreservesMaskProvenance());
|
RunCase(&vulkan, ScalarSelectB64PreservesMaskProvenance());
|
||||||
RunCase(&vulkan, ScalarConditionalMoveB64());
|
RunCase(&vulkan, ScalarConditionalMoveB64());
|
||||||
|
|||||||
@@ -4033,6 +4033,16 @@ void TestNewShaderDecoderArchitecture() {
|
|||||||
Check(GetInstructionFamily(EncodeDs0(0x36)) == Family::DS,
|
Check(GetInstructionFamily(EncodeDs0(0x36)) == Family::DS,
|
||||||
"decoder did not classify DS directly");
|
"decoder did not classify DS directly");
|
||||||
|
|
||||||
|
const uint32_t orn2_saveexec_code[] = {0xbeea407eu};
|
||||||
|
Instruction orn2_saveexec;
|
||||||
|
ShaderRecompiler::Decoder::DecodeInstruction(orn2_saveexec_code, 0u, orn2_saveexec);
|
||||||
|
Check(orn2_saveexec.family == Family::SOP1 &&
|
||||||
|
orn2_saveexec.opcode == Opcode::S_ORN2_SAVEEXEC_B32 &&
|
||||||
|
orn2_saveexec.word_count == 1u && orn2_saveexec.src_count == 1u &&
|
||||||
|
orn2_saveexec.dst.kind == OperandKind::VccLo &&
|
||||||
|
orn2_saveexec.src0.kind == OperandKind::ExecLo,
|
||||||
|
"decoder rejected or misdecoded captured S_ORN2_SAVEEXEC_B32");
|
||||||
|
|
||||||
const uint32_t offset_code[] = {0u, EncodeVop1(0x01, 2, 3)};
|
const uint32_t offset_code[] = {0u, EncodeVop1(0x01, 2, 3)};
|
||||||
Instruction direct;
|
Instruction direct;
|
||||||
ShaderRecompiler::Decoder::DecodeInstruction(offset_code, 1u, direct);
|
ShaderRecompiler::Decoder::DecodeInstruction(offset_code, 1u, direct);
|
||||||
|
|||||||
Reference in New Issue
Block a user