shader: remove recompiler error-string propagation

This commit is contained in:
nmzik committed 2026-08-31 05:31:41 +04:00
1 parent 13533bd053
commit af8aecfdde
47 files changed
+2040 -3253

No files matched your search

@@ -1,5 +1,7 @@
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
#include "common/assert.h"
#include <algorithm>
#include <fmt/format.h>
#include <iterator>
@@ -24,19 +26,18 @@ struct SetpcTargetInfo {
std::vector<uint32_t> selector_target_pcs;
};
void SetError(std::string* error, const std::string& message) {
if (error != nullptr) {
*error = message;
}
}
void SetFailure(Graph& graph, FailureKind kind, uint32_t block_id, const std::string& message,
std::string* error) {
void SetFailure(Graph& graph, FailureKind kind, uint32_t block_id, const std::string& message) {
graph.unsupported = true;
graph.failure_kind = kind;
graph.failure_block = block_id;
graph.unsupported_reason = message;
SetError(error, message);
}
[[noreturn]] void ExitBuildFailure(Graph& graph, FailureKind kind, uint32_t block_id,
const std::string& message) {
SetFailure(graph, kind, block_id, message);
EXIT("shader CFG build failed: %s", message.c_str());
std::abort();
}
uint32_t InstructionEndPc(const Instruction& inst) {
@@ -1267,7 +1268,7 @@ bool MergeLeavesContainingLoop(const Graph& graph, uint32_t header, uint32_t mer
return false;
}
bool CanonicalizeNaturalLoops(Graph& graph, std::string* error) {
bool CanonicalizeNaturalLoops(Graph& graph) {
const auto rewrite_budget = graph.blocks.size() * 2u + 16u;
for (size_t rewrite = 0; rewrite < rewrite_budget; rewrite++) {
bool changed = false;
@@ -1322,19 +1323,18 @@ bool CanonicalizeNaturalLoops(Graph& graph, std::string* error) {
}
SetFailure(graph, FailureKind::StructuredControlFlow, graph.entry_block,
"CFG loop canonicalization exceeded rewrite budget", error);
"CFG loop canonicalization exceeded rewrite budget");
return false;
}
bool IsolateSemanticLoopHeaders(Graph& graph, std::string* error) {
bool IsolateSemanticLoopHeaders(Graph& graph) {
const auto isolation_budget = graph.natural_loops.size() + 1u;
for (size_t isolation = 0; isolation < isolation_budget; isolation++) {
const auto loop = std::find_if(graph.natural_loops.begin(), graph.natural_loops.end(),
[&](const auto& value) {
const auto* header = graph.FindBlock(value.header);
return header != nullptr &&
header->inst_begin != header->inst_end;
});
const auto loop = std::find_if(
graph.natural_loops.begin(), graph.natural_loops.end(), [&](const auto& value) {
const auto* header = graph.FindBlock(value.header);
return header != nullptr && header->inst_begin != header->inst_end;
});
if (loop == graph.natural_loops.end()) {
return true;
}
@@ -1346,14 +1346,13 @@ bool IsolateSemanticLoopHeaders(Graph& graph, std::string* error) {
// displacing OpLoopMerge into a helper-created block.
if (!IsolateSemanticLoopHeader(graph, loop->header)) {
SetFailure(graph, FailureKind::StructuredControlFlow, loop->header,
fmt::format("failed to isolate semantic loop header {}", loop->header),
error);
fmt::format("failed to isolate semantic loop header {}", loop->header));
return false;
}
}
SetFailure(graph, FailureKind::StructuredControlFlow, graph.entry_block,
"CFG semantic loop-header isolation exceeded rewrite budget", error);
"CFG semantic loop-header isolation exceeded rewrite budget");
return false;
}
@@ -1453,7 +1452,7 @@ std::vector<uint32_t> SelectionRegion(const Graph& graph, const BasicBlock& head
return region;
}
bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
bool SplitOneSelectionMerge(Graph& graph) {
std::vector<uint32_t> loop_headers;
loop_headers.reserve(graph.natural_loops.size());
for (const auto& loop: graph.natural_loops) {
@@ -1501,8 +1500,7 @@ bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
graph, FailureKind::StructuredControlFlow, block_id,
fmt::format("selection header block {} has externally entered region block {}; "
"semantic block cloning is disabled",
block_id, *external),
error);
block_id, *external));
return false;
}
const auto construct_blocks = DominatedBlocks(graph, block_id, merge);
@@ -1514,11 +1512,11 @@ bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
return false;
}
bool SplitSharedMergeBlocks(Graph& graph, std::string* error) {
bool SplitSharedMergeBlocks(Graph& graph) {
const auto original_block_count = static_cast<uint32_t>(graph.blocks.size());
const auto split_budget = std::max<uint32_t>(16u, original_block_count * 4u);
for (uint32_t splits = 0; splits < split_budget; splits++) {
if (!SplitOneLoopMerge(graph) && !SplitOneSelectionMerge(graph, error)) {
if (!SplitOneLoopMerge(graph) && !SplitOneSelectionMerge(graph)) {
return !graph.unsupported;
}
RebuildPredecessors(graph);
@@ -1528,8 +1526,7 @@ bool SplitSharedMergeBlocks(Graph& graph, std::string* error) {
fmt::format("CFG shared merge splitting exceeded budget: original_blocks={} "
"current_blocks={} split_budget={}",
original_block_count, static_cast<uint64_t>(graph.blocks.size()),
split_budget),
error);
split_budget));
return false;
}
@@ -1890,12 +1887,11 @@ bool RouteSharedSelectionArm(Graph& graph, uint32_t route_variable) {
} // namespace
bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* error) {
graph = {};
Graph BuildGraph(const Decoder::Program& program) {
Graph graph;
if (program.instructions.empty()) {
SetError(error, "cannot build CFG for empty shader");
return false;
ExitBuildFailure(graph, FailureKind::InvalidLabel, UINT32_MAX,
"cannot build CFG for empty shader");
}
const auto first_pc = program.instructions.front().pc;
@@ -1905,11 +1901,10 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
for (const auto& inst: program.instructions) {
instruction_pcs.insert(inst.pc);
if (inst.opcode == Opcode::UNSUPPORTED) {
SetFailure(graph, FailureKind::UnsupportedInstruction, UINT32_MAX,
fmt::format("unsupported decoded instruction in CFG at pc 0x{:08x}: {}",
inst.pc, Decoder::InstructionToString(inst).c_str()),
error);
return false;
ExitBuildFailure(
graph, FailureKind::UnsupportedInstruction, UINT32_MAX,
fmt::format("unsupported decoded instruction in CFG at pc 0x{:08x}: {}", inst.pc,
Decoder::InstructionToString(inst).c_str()));
}
}
@@ -1923,11 +1918,9 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
const auto next_pc = InstructionEndPc(inst);
if (IsBranch(inst.opcode)) {
if (!IsValidTarget(inst.branch_target, instruction_pcs, first_pc, end_pc)) {
SetFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}", inst.pc,
inst.branch_target),
error);
return false;
ExitBuildFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}",
inst.pc, inst.branch_target));
}
labels.insert(inst.branch_target);
if (next_pc <= end_pc) {
@@ -1936,21 +1929,19 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
} else if (inst.opcode == Opcode::S_SETPC_B64) {
SetpcTargetInfo target_info;
if (!ResolveSetpcTargets(program, i, target_info)) {
SetFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
fmt::format("unsupported dynamic S_SETPC_B64 at pc 0x{:08x}", inst.pc),
error);
return false;
ExitBuildFailure(
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
fmt::format("unsupported dynamic S_SETPC_B64 at pc 0x{:08x}", inst.pc));
}
const auto& target_pcs = target_info.indirect
? target_info.target_pcs
: std::vector<uint32_t> {target_info.target};
for (const auto target: target_pcs) {
if (!IsValidTarget(target, instruction_pcs, first_pc, end_pc)) {
SetFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
fmt::format("S_SETPC_B64 at pc 0x{:08x} targets invalid pc 0x{:08x}",
inst.pc, target),
error);
return false;
ExitBuildFailure(
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
fmt::format("S_SETPC_B64 at pc 0x{:08x} targets invalid pc 0x{:08x}",
inst.pc, target));
}
labels.insert(target);
}
@@ -1971,10 +1962,9 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
continue;
}
if (start != end_pc && !instruction_pcs.contains(start)) {
SetFailure(graph, FailureKind::InvalidLabel, UINT32_MAX,
fmt::format("CFG label does not start on an instruction: 0x{:08x}", start),
error);
return false;
ExitBuildFailure(
graph, FailureKind::InvalidLabel, UINT32_MAX,
fmt::format("CFG label does not start on an instruction: 0x{:08x}", start));
}
BasicBlock block;
@@ -2046,11 +2036,10 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
block.terminator.true_block = pc_to_block.at(last.branch_target);
const auto fallthrough = pc_to_block.find(next_pc);
if (fallthrough == pc_to_block.end()) {
SetFailure(graph, FailureKind::MissingFallthrough, block.id,
fmt::format("conditional branch at pc 0x{:08x} has no fallthrough block",
last.pc),
error);
return false;
ExitBuildFailure(
graph, FailureKind::MissingFallthrough, block.id,
fmt::format("conditional branch at pc 0x{:08x} has no fallthrough block",
last.pc));
}
block.terminator.false_block = fallthrough->second;
} else {
@@ -2131,27 +2120,26 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
graph.unsupported = false;
}
return true;
return graph;
}
namespace {
bool StructurizeImpl(Graph& graph, std::string* error) {
bool StructurizeImpl(Graph& graph) {
if (graph.unsupported || graph.irreducible) {
if (graph.unsupported_reason.empty()) {
graph.unsupported_reason = "unsupported CFG";
}
SetError(error, graph.unsupported_reason);
return false;
}
if (!CanonicalizeNaturalLoops(graph, error)) {
if (!CanonicalizeNaturalLoops(graph)) {
return false;
}
if (!SplitSharedMergeBlocks(graph, error)) {
if (!SplitSharedMergeBlocks(graph)) {
return false;
}
if (!IsolateSemanticLoopHeaders(graph, error)) {
if (!IsolateSemanticLoopHeaders(graph)) {
return false;
}
ClearStructuredTerminators(graph);
@@ -2164,8 +2152,7 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
graph, FailureKind::StructuredControlFlow, header,
fmt::format(
"duplicate structured merge block {} for header {} (already used by header {})",
merge, header, it->second),
error);
merge, header, it->second));
return false;
}
return true;
@@ -2176,16 +2163,14 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
if (header == nullptr || loop.merge == UINT32_MAX || loop.continue_block == UINT32_MAX) {
SetFailure(
graph, FailureKind::StructuredControlFlow, loop.header,
fmt::format("loop at block {} has no structured merge/continue", loop.header),
error);
fmt::format("loop at block {} has no structured merge/continue", loop.header));
return false;
}
if (header->inst_begin != header->inst_end ||
header->terminator.kind != TerminatorKind::Branch) {
SetFailure(graph, FailureKind::StructuredControlFlow, loop.header,
fmt::format("loop header {} is not a dedicated empty control block",
loop.header),
error);
SetFailure(
graph, FailureKind::StructuredControlFlow, loop.header,
fmt::format("loop header {} is not a dedicated empty control block", loop.header));
return false;
}
if (!reserve_merge_block(loop.header, loop.merge)) {
@@ -2208,8 +2193,7 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
const auto merge = FindSelectionMerge(graph, block);
if (merge == UINT32_MAX) {
SetFailure(graph, FailureKind::StructuredControlFlow, block.id,
fmt::format("conditional block {} has no structured merge", block.id),
error);
fmt::format("conditional block {} has no structured merge", block.id));
return false;
}
@@ -2224,16 +2208,15 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
} // namespace
bool Structurize(Graph& graph, std::string* error) {
bool Structurize(Graph& graph) {
Graph original = graph;
if (StructurizeImpl(graph, error)) {
if (StructurizeImpl(graph)) {
return true;
}
Graph failed_graph = std::move(graph);
std::string failed_error = error != nullptr ? *error : std::string {};
graph = std::move(original);
const auto route_budget = static_cast<uint32_t>(graph.blocks.size());
Graph failed_graph = std::move(graph);
graph = std::move(original);
const auto route_budget = static_cast<uint32_t>(graph.blocks.size());
// Apply one route at a time and retry. Eagerly routing every matching diamond can
// rewrite unrelated selections that were already structurally valid.
for (uint32_t route_variable = 0; route_variable < route_budget; route_variable++) {
@@ -2241,18 +2224,12 @@ bool Structurize(Graph& graph, std::string* error) {
break;
}
Graph routed = graph;
if (error != nullptr) {
error->clear();
}
if (StructurizeImpl(routed, error)) {
if (StructurizeImpl(routed)) {
graph = std::move(routed);
return true;
}
}
graph = std::move(failed_graph);
if (error != nullptr) {
*error = std::move(failed_error);
}
return false;
}
@@ -108,8 +108,8 @@ struct Graph {
uint32_t FindNearestCommonPostDominator(uint32_t block_a, uint32_t block_b) const;
};
bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* error);
bool Structurize(Graph& graph, std::string* error);
Graph BuildGraph(const Decoder::Program& program);
bool Structurize(Graph& graph);
std::string BranchConditionToString(BranchCondition condition);
std::string FailureKindToString(FailureKind kind);
std::string GraphToString(const Graph& graph);
@@ -357,8 +357,8 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
DecodeVectorGpr(vdata, inst.dst);
DecodeVectorGpr(vaddr, inst.src0);
DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr);
DecodeScalarSource(ssamp * 4u, pc, inst.src2, nullptr);
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
DecodeScalarSource(ssamp * 4u, pc, inst.src2);
inst.src_count = 3;
}
@@ -241,11 +241,11 @@ void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
MarkMemoryUnsupported(inst, Family::SMEM, opcode, "SMEM opcode is not implemented");
}
DecodeScalarDestination(sdst, pc, inst.dst, nullptr);
DecodeScalarDestination(sdst, pc, inst.dst);
// SMEM encodes SBASE in SGPR pairs. Scalar-buffer loads still use the same
// pair index; their descriptor operand consumes four SGPRs from that base.
DecodeScalarSource(sbase * 2u, pc, inst.src0, nullptr);
DecodeScalarSource(soffset, pc, inst.src1, nullptr);
DecodeScalarSource(sbase * 2u, pc, inst.src0);
DecodeScalarSource(soffset, pc, inst.src1);
inst.src_count = 2;
}
@@ -278,8 +278,8 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
DecodeVectorGpr(vdata, inst.dst);
DecodeVectorGpr(vaddr, inst.src0);
DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr);
DecodeScalarSource(soffset, pc, inst.src2, nullptr);
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
DecodeScalarSource(soffset, pc, inst.src2);
inst.src_count = 3;
}
@@ -316,8 +316,8 @@ void DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
DecodeVectorGpr(vdata, inst.dst);
DecodeVectorGpr(vaddr, inst.src0);
DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr);
DecodeScalarSource(soffset, pc, inst.src2, nullptr);
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
DecodeScalarSource(soffset, pc, inst.src2);
inst.src_count = 3;
}
@@ -364,7 +364,7 @@ void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
DecodeVectorGpr(addr + 1u, inst.src1);
inst.src_count = 2;
} else {
DecodeScalarSource(saddr, pc, inst.src1, nullptr);
DecodeScalarSource(saddr, pc, inst.src1);
inst.src_count = 2;
}
}
@@ -105,21 +105,18 @@ constexpr auto SOPK_OPS = Detail::MakeOpcodeTable<0x20>(SOPK_OPCODE_LIST);
constexpr auto SOPC_OPS = Detail::MakeOpcodeTable<0x80>(SOPC_OPCODE_LIST);
constexpr auto SOPP_OPS = Detail::MakeOpcodeTable<0x80>(SOPP_OPCODE_LIST);
bool DecodeBinarySources(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, uint32_t ssrc0, uint32_t ssrc1, std::string* error) {
if (!DecodeScalarSource(ssrc0, pc, inst.src0, error) ||
!DecodeScalarSource(ssrc1, pc, inst.src1, error)) {
return false;
}
void DecodeBinarySources(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, uint32_t ssrc0, uint32_t ssrc1) {
DecodeScalarSource(ssrc0, pc, inst.src0);
DecodeScalarSource(ssrc1, pc, inst.src1);
inst.src_count = 2;
ReadLiteralOperands(code, word_index, inst);
return true;
}
} // namespace
bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word = code[word_index];
const uint32_t opcode = (word >> 8u) & 0xffu;
const uint32_t ssrc0 = word & 0xffu;
@@ -134,35 +131,31 @@ bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::SOP1, opcode, "SOP1 opcode is not implemented");
return true;
return;
}
switch (inst.opcode) {
case Opcode::S_GETPC_B64:
inst.src_count = 0;
return DecodeScalarDestination(sdst, pc, inst.dst, error);
DecodeScalarDestination(sdst, pc, inst.dst);
return;
case Opcode::S_SETPC_B64:
inst.src_count = 1;
inst.dst.kind = OperandKind::Null;
if (!DecodeScalarSource(ssrc0, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(ssrc0, pc, inst.src0);
ReadLiteralOperands(code, word_index, inst);
return true;
return;
default: break;
}
if (!DecodeScalarSource(ssrc0, pc, inst.src0, error) ||
!DecodeScalarDestination(sdst, pc, inst.dst, error)) {
return false;
}
DecodeScalarSource(ssrc0, pc, inst.src0);
DecodeScalarDestination(sdst, pc, inst.dst);
inst.src_count = 1;
ReadLiteralOperands(code, word_index, inst);
return true;
}
bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word = code[word_index];
const uint32_t opcode = (word >> 23u) & 0x7fu;
const uint32_t ssrc1 = (word >> 8u) & 0xffu;
@@ -178,17 +171,15 @@ bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::SOP2, opcode, "SOP2 opcode is not implemented");
return true;
return;
}
if (!DecodeScalarDestination(sdst, pc, inst.dst, error)) {
return false;
}
return DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1, error);
DecodeScalarDestination(sdst, pc, inst.dst);
DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1);
}
bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word = code[word_index];
const uint32_t opcode = (word >> 23u) & 0x1fu;
const uint32_t sdst = (word >> 16u) & 0x7fu;
@@ -207,18 +198,18 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::SOPK, opcode, "SOPK opcode is not implemented");
return true;
return;
}
switch (inst.opcode) {
case Opcode::S_MOVK_I32: return DecodeScalarDestination(sdst, pc, inst.dst, error);
case Opcode::S_MOVK_I32: DecodeScalarDestination(sdst, pc, inst.dst); return;
case Opcode::S_WAITCNT: {
const uint32_t waitcnt = word & 0xffffu;
inst.dst.kind = OperandKind::Null;
inst.src0.signed_val = static_cast<int32_t>(waitcnt);
inst.src0.value = waitcnt;
inst.src_count = 1;
return true;
return;
}
case Opcode::S_SETREG_B32:
inst.dst.kind = OperandKind::Null;
@@ -226,26 +217,25 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
inst.src1.value = word & 0xffffu;
inst.src1.signed_val = static_cast<int32_t>(imm);
inst.src_count = 2;
return DecodeScalarSource(sdst, pc, inst.src0, error);
DecodeScalarSource(sdst, pc, inst.src0);
return;
default: break;
}
inst.src1 = inst.src0;
if (!DecodeScalarSource(sdst, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(sdst, pc, inst.src0);
if (inst.opcode == Opcode::S_ADD_I32 || inst.opcode == Opcode::S_MULK_I32) {
inst.src_count = 2;
return DecodeScalarDestination(sdst, pc, inst.dst, error);
DecodeScalarDestination(sdst, pc, inst.dst);
return;
}
inst.dst.kind = OperandKind::Scc;
inst.src_count = 2;
return true;
}
bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word = code[word_index];
const uint32_t ssrc1 = (word >> 8u) & 0xffu;
const uint32_t ssrc0 = word & 0xffu;
@@ -261,10 +251,10 @@ bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::SOPC, opcode, "SOPC opcode is not implemented");
return true;
return;
}
return DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1, error);
DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1);
}
void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
@@ -5,14 +5,14 @@
namespace Libs::Graphics::ShaderRecompiler::Decoder {
bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
void DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
@@ -1,5 +1,6 @@
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "common/assert.h"
#include "graphics/shader/recompiler/frontend/decode/ExportOps.h"
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
#include "graphics/shader/recompiler/frontend/decode/MemoryOps.h"
@@ -17,12 +18,6 @@ uint32_t FloatBits(float value) {
return std::bit_cast<uint32_t>(value);
}
void SetError(std::string* error, const char* message) {
if (error != nullptr) {
*error = message;
}
}
bool HasLiteral(const Instruction& inst) {
return inst.src0.kind == OperandKind::LiteralConstant ||
inst.src1.kind == OperandKind::LiteralConstant ||
@@ -216,86 +211,76 @@ const char* ImageDimensionToString(ImageDimension dimension) {
}
}
bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error) {
void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
operand = {};
if (code <= 105u) {
operand.kind = OperandKind::Sgpr;
operand.reg = code;
return true;
return;
}
if (code >= 128u && code <= 192u) {
operand.kind = OperandKind::IntegerInlineConstant;
operand.signed_val = static_cast<int32_t>(code - 128u);
operand.value = static_cast<uint32_t>(operand.signed_val);
return true;
return;
}
if (code >= 193u && code <= 208u) {
operand.kind = OperandKind::IntegerInlineConstant;
operand.signed_val = 192 - static_cast<int32_t>(code);
operand.value = static_cast<uint32_t>(operand.signed_val);
return true;
return;
}
if (code >= 240u && code <= 247u) {
constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f};
operand.kind = OperandKind::FloatInlineConstant;
operand.float_val = values[code - 240u];
operand.value = FloatBits(operand.float_val);
return true;
return;
}
if (code >= 256u && code <= 511u) {
DecodeVectorGpr(code - 256u, operand);
return true;
return;
}
switch (code) {
case 106u: operand.kind = OperandKind::VccLo; return true;
case 107u: operand.kind = OperandKind::VccHi; return true;
case 124u: operand.kind = OperandKind::M0; return true;
case 125u: operand.kind = OperandKind::Null; return true;
case 126u: operand.kind = OperandKind::ExecLo; return true;
case 127u: operand.kind = OperandKind::ExecHi; return true;
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return true;
case 106u: operand.kind = OperandKind::VccLo; return;
case 107u: operand.kind = OperandKind::VccHi; return;
case 124u: operand.kind = OperandKind::M0; return;
case 125u: operand.kind = OperandKind::Null; return;
case 126u: operand.kind = OperandKind::ExecLo; return;
case 127u: operand.kind = OperandKind::ExecHi; return;
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return;
case 248u:
operand.kind = OperandKind::FloatInlineConstant;
operand.float_val = 0.15915494309189535f;
operand.value = FloatBits(operand.float_val);
return true;
case 251u: operand.kind = OperandKind::VccZ; return true;
case 252u: operand.kind = OperandKind::ExecZ; return true;
case 253u: operand.kind = OperandKind::Scc; return true;
case 255u: operand.kind = OperandKind::LiteralConstant; return true;
default:
if (error != nullptr) {
*error = fmt::format("unsupported scalar source operand 0x{:08x} at pc 0x{:08x}",
code, pc);
}
return false;
return;
case 251u: operand.kind = OperandKind::VccZ; return;
case 252u: operand.kind = OperandKind::ExecZ; return;
case 253u: operand.kind = OperandKind::Scc; return;
case 255u: operand.kind = OperandKind::LiteralConstant; return;
default: EXIT("unsupported scalar source operand 0x%08x at pc 0x%08x", code, pc);
}
}
bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std::string* error) {
void DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand) {
operand = {};
if (code <= 105u) {
operand.kind = OperandKind::Sgpr;
operand.reg = code;
return true;
return;
}
switch (code) {
case 106u: operand.kind = OperandKind::VccLo; return true;
case 107u: operand.kind = OperandKind::VccHi; return true;
case 124u: operand.kind = OperandKind::M0; return true;
case 125u: operand.kind = OperandKind::Null; return true;
case 126u: operand.kind = OperandKind::ExecLo; return true;
case 127u: operand.kind = OperandKind::ExecHi; return true;
default:
if (error != nullptr) {
*error = fmt::format(
"unsupported scalar destination operand 0x{:08x} at pc 0x{:08x}", code, pc);
}
return false;
case 106u: operand.kind = OperandKind::VccLo; return;
case 107u: operand.kind = OperandKind::VccHi; return;
case 124u: operand.kind = OperandKind::M0; return;
case 125u: operand.kind = OperandKind::Null; return;
case 126u: operand.kind = OperandKind::ExecLo; return;
case 127u: operand.kind = OperandKind::ExecHi; return;
default: EXIT("unsupported scalar destination operand 0x%08x at pc 0x%08x", code, pc);
}
}
@@ -368,39 +353,33 @@ Family GetInstructionFamily(uint32_t word) {
}
}
bool DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst) {
const uint32_t pc = word_index * sizeof(uint32_t);
switch (GetInstructionFamily(code[word_index])) {
case Family::SOP1: return DecodeSop1(pc, code, word_index, inst, error);
case Family::SOP2: return DecodeSop2(pc, code, word_index, inst, error);
case Family::SOPK: return DecodeSopk(pc, code, word_index, inst, error);
case Family::SOPC: return DecodeSopc(pc, code, word_index, inst, error);
case Family::SOPP: DecodeSopp(pc, code, word_index, inst); return true;
case Family::VOP1: return DecodeVop1(pc, code, word_index, inst, error);
case Family::VOP2: return DecodeVop2(pc, code, word_index, inst, error);
case Family::VOP3: return DecodeVop3(pc, code, word_index, inst, error);
case Family::VOP3P: return DecodeVop3p(pc, code, word_index, inst, error);
case Family::VOPC: return DecodeVopc(pc, code, word_index, inst, error);
case Family::VINTRP: DecodeVintrp(pc, code, word_index, inst); return true;
case Family::SMEM: DecodeSmem(pc, code, word_index, inst); return true;
case Family::MUBUF: DecodeMubuf(pc, code, word_index, inst); return true;
case Family::MTBUF: DecodeMtbuf(pc, code, word_index, inst); return true;
case Family::FLAT: DecodeFlat(pc, code, word_index, inst); return true;
case Family::DS: DecodeDs(pc, code, word_index, inst); return true;
case Family::MIMG: DecodeMimg(pc, code, word_index, inst); return true;
case Family::EXP: DecodeExp(pc, code, word_index, inst); return true;
case Family::SOP1: DecodeSop1(pc, code, word_index, inst); return;
case Family::SOP2: DecodeSop2(pc, code, word_index, inst); return;
case Family::SOPK: DecodeSopk(pc, code, word_index, inst); return;
case Family::SOPC: DecodeSopc(pc, code, word_index, inst); return;
case Family::SOPP: DecodeSopp(pc, code, word_index, inst); return;
case Family::VOP1: DecodeVop1(pc, code, word_index, inst); return;
case Family::VOP2: DecodeVop2(pc, code, word_index, inst); return;
case Family::VOP3: DecodeVop3(pc, code, word_index, inst); return;
case Family::VOP3P: DecodeVop3p(pc, code, word_index, inst); return;
case Family::VOPC: DecodeVopc(pc, code, word_index, inst); return;
case Family::VINTRP: DecodeVintrp(pc, code, word_index, inst); return;
case Family::SMEM: DecodeSmem(pc, code, word_index, inst); return;
case Family::MUBUF: DecodeMubuf(pc, code, word_index, inst); return;
case Family::MTBUF: DecodeMtbuf(pc, code, word_index, inst); return;
case Family::FLAT: DecodeFlat(pc, code, word_index, inst); return;
case Family::DS: DecodeDs(pc, code, word_index, inst); return;
case Family::MIMG: DecodeMimg(pc, code, word_index, inst); return;
case Family::EXP: DecodeExp(pc, code, word_index, inst); return;
default:
if (error != nullptr) {
*error =
fmt::format("unknown RDNA2 instruction family at pc 0x{:08x}, raw=0x{:08x}", pc,
code[word_index]);
}
return false;
EXIT("unknown RDNA2 instruction family at pc 0x%08x, raw=0x%08x", pc, code[word_index]);
}
}
bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string* error) {
void DecodeProgram(std::span<const uint32_t> code, Program& program) {
program.instructions.clear();
program.instructions.reserve(code.size());
program.code = code;
@@ -408,10 +387,7 @@ bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string
std::vector<bool> branch_targets;
for (uint32_t word_index = 0; word_index < code.size();) {
program.instructions.emplace_back();
if (!DecodeInstruction(code, word_index, program.instructions.back(), error)) {
program.instructions.pop_back();
return false;
}
DecodeInstruction(code, word_index, program.instructions.back());
const auto& inst = program.instructions.back();
word_index += inst.word_count;
@@ -425,12 +401,11 @@ bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string
}
if (inst.opcode == Opcode::S_ENDPGM &&
(word_index >= code.size() || branch_targets.empty() || !branch_targets[word_index])) {
return true;
return;
}
}
SetError(error, "shader decode reached the code boundary before S_ENDPGM");
return false;
EXIT("shader decode reached the code boundary before S_ENDPGM");
}
std::string OperandToString(const Operand& operand) {
@@ -704,12 +704,11 @@ struct Program {
// Code spans are trusted to contain complete instructions, valid branch targets, and 32-bit PCs.
Family GetInstructionFamily(uint32_t word);
// The output object must be freshly initialized.
bool DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string* error);
void DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
void DecodeProgram(std::span<const uint32_t> code, Program& program);
bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error);
bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std::string* error);
void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand);
void DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand);
void DecodeVectorGpr(uint32_t reg, Operand& operand);
void ReadLiteralOperands(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
void SetRawWords(Instruction& inst, std::span<const uint32_t> code, uint32_t word_index,
@@ -204,55 +204,52 @@ struct VopcOpcodeInfo {
};
constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = {
{0x00u, Opcode::V_CMP_F_F32}, {0x01u, Opcode::V_CMP_LT_F32},
{0x02u, Opcode::V_CMP_EQ_F32}, {0x03u, Opcode::V_CMP_LE_F32},
{0x04u, Opcode::V_CMP_GT_F32}, {0x05u, Opcode::V_CMP_LG_F32},
{0x06u, Opcode::V_CMP_GE_F32}, {0x07u, Opcode::V_CMP_O_F32},
{0x08u, Opcode::V_CMP_U_F32}, {0x09u, Opcode::V_CMP_NGE_F32},
{0x0au, Opcode::V_CMP_NLG_F32}, {0x0bu, Opcode::V_CMP_NGT_F32},
{0x0cu, Opcode::V_CMP_NLE_F32}, {0x0du, Opcode::V_CMP_NEQ_F32},
{0x0eu, Opcode::V_CMP_NLT_F32}, {0x0fu, Opcode::V_CMP_TRU_F32},
{0x11u, Opcode::V_CMPX_LT_F32}, {0x12u, Opcode::V_CMPX_EQ_F32},
{0x13u, Opcode::V_CMPX_LE_F32}, {0x14u, Opcode::V_CMPX_GT_F32},
{0x15u, Opcode::V_CMPX_LG_F32}, {0x16u, Opcode::V_CMPX_GE_F32},
{0x19u, Opcode::V_CMPX_NGE_F32}, {0x1au, Opcode::V_CMPX_NLG_F32},
{0x1bu, Opcode::V_CMPX_NGT_F32}, {0x1cu, Opcode::V_CMPX_NLE_F32},
{0x1du, Opcode::V_CMPX_NEQ_F32}, {0x1eu, Opcode::V_CMPX_NLT_F32},
{0x80u, Opcode::V_CMP_F_I32}, {0x81u, Opcode::V_CMP_LT_I32},
{0x82u, Opcode::V_CMP_EQ_I32}, {0x83u, Opcode::V_CMP_LE_I32},
{0x84u, Opcode::V_CMP_GT_I32}, {0x85u, Opcode::V_CMP_NE_I32},
{0x86u, Opcode::V_CMP_GE_I32}, {0x87u, Opcode::V_CMP_T_I32},
{0x88u, Opcode::V_CMP_CLASS_F32}, {0x89u, Opcode::V_CMP_LT_I16},
{0x8au, Opcode::V_CMP_EQ_I16}, {0x8bu, Opcode::V_CMP_LE_I16},
{0x8cu, Opcode::V_CMP_GT_I16}, {0x8du, Opcode::V_CMP_NE_I16},
{0x8eu, Opcode::V_CMP_GE_I16}, {0x91u, Opcode::V_CMPX_LT_I32},
{0x92u, Opcode::V_CMPX_EQ_I32}, {0x93u, Opcode::V_CMPX_LE_I32},
{0x94u, Opcode::V_CMPX_GT_I32}, {0x95u, Opcode::V_CMPX_NE_I32},
{0x96u, Opcode::V_CMPX_GE_I32}, {0x98u, Opcode::V_CMPX_CLASS_F32},
{0xa9u, Opcode::V_CMP_LT_U16},
{0xaau, Opcode::V_CMP_EQ_U16}, {0xabu, Opcode::V_CMP_LE_U16},
{0xacu, Opcode::V_CMP_GT_U16}, {0xadu, Opcode::V_CMP_NE_U16},
{0xaeu, Opcode::V_CMP_GE_U16}, {0xbcu, Opcode::V_CMPX_GT_U16},
{0xc0u, Opcode::V_CMP_F_U32},
{0xc1u, Opcode::V_CMP_LT_U32}, {0xc2u, Opcode::V_CMP_EQ_U32},
{0xc3u, Opcode::V_CMP_LE_U32}, {0xc4u, Opcode::V_CMP_GT_U32},
{0xc5u, Opcode::V_CMP_NE_U32}, {0xc6u, Opcode::V_CMP_GE_U32},
{0xc7u, Opcode::V_CMP_T_U32}, {0xa2u, Opcode::V_CMP_EQ_I64, false},
{0xb5u, Opcode::V_CMPX_NE_I64, false},
{0xd1u, Opcode::V_CMPX_LT_U32}, {0xd2u, Opcode::V_CMPX_EQ_U32},
{0xd3u, Opcode::V_CMPX_LE_U32}, {0xd4u, Opcode::V_CMPX_GT_U32},
{0xd5u, Opcode::V_CMPX_NE_U32}, {0xd6u, Opcode::V_CMPX_GE_U32},
{0xe2u, Opcode::V_CMP_EQ_U64, false}, {0xe4u, Opcode::V_CMP_GT_U64, false},
{0xe5u, Opcode::V_CMP_NE_U64, false},
{0xf5u, Opcode::V_CMPX_NE_U64, false},
{0xc9u, Opcode::V_CMP_LT_F16}, {0xcau, Opcode::V_CMP_EQ_F16},
{0xcbu, Opcode::V_CMP_LE_F16}, {0xccu, Opcode::V_CMP_GT_F16},
{0xcdu, Opcode::V_CMP_LG_F16}, {0xceu, Opcode::V_CMP_GE_F16},
{0xedu, Opcode::V_CMP_NEQ_F16}, {0xd9u, Opcode::V_CMPX_LT_F16},
{0xdau, Opcode::V_CMPX_EQ_F16}, {0xdbu, Opcode::V_CMPX_LE_F16},
{0xdcu, Opcode::V_CMPX_GT_F16}, {0xdeu, Opcode::V_CMPX_GE_F16},
{0xfbu, Opcode::V_CMPX_NGT_F16}, {0xfdu, Opcode::V_CMPX_NEQ_F16},
{0xfeu, Opcode::V_CMPX_NLT_F16},
{0x00u, Opcode::V_CMP_F_F32}, {0x01u, Opcode::V_CMP_LT_F32},
{0x02u, Opcode::V_CMP_EQ_F32}, {0x03u, Opcode::V_CMP_LE_F32},
{0x04u, Opcode::V_CMP_GT_F32}, {0x05u, Opcode::V_CMP_LG_F32},
{0x06u, Opcode::V_CMP_GE_F32}, {0x07u, Opcode::V_CMP_O_F32},
{0x08u, Opcode::V_CMP_U_F32}, {0x09u, Opcode::V_CMP_NGE_F32},
{0x0au, Opcode::V_CMP_NLG_F32}, {0x0bu, Opcode::V_CMP_NGT_F32},
{0x0cu, Opcode::V_CMP_NLE_F32}, {0x0du, Opcode::V_CMP_NEQ_F32},
{0x0eu, Opcode::V_CMP_NLT_F32}, {0x0fu, Opcode::V_CMP_TRU_F32},
{0x11u, Opcode::V_CMPX_LT_F32}, {0x12u, Opcode::V_CMPX_EQ_F32},
{0x13u, Opcode::V_CMPX_LE_F32}, {0x14u, Opcode::V_CMPX_GT_F32},
{0x15u, Opcode::V_CMPX_LG_F32}, {0x16u, Opcode::V_CMPX_GE_F32},
{0x19u, Opcode::V_CMPX_NGE_F32}, {0x1au, Opcode::V_CMPX_NLG_F32},
{0x1bu, Opcode::V_CMPX_NGT_F32}, {0x1cu, Opcode::V_CMPX_NLE_F32},
{0x1du, Opcode::V_CMPX_NEQ_F32}, {0x1eu, Opcode::V_CMPX_NLT_F32},
{0x80u, Opcode::V_CMP_F_I32}, {0x81u, Opcode::V_CMP_LT_I32},
{0x82u, Opcode::V_CMP_EQ_I32}, {0x83u, Opcode::V_CMP_LE_I32},
{0x84u, Opcode::V_CMP_GT_I32}, {0x85u, Opcode::V_CMP_NE_I32},
{0x86u, Opcode::V_CMP_GE_I32}, {0x87u, Opcode::V_CMP_T_I32},
{0x88u, Opcode::V_CMP_CLASS_F32}, {0x89u, Opcode::V_CMP_LT_I16},
{0x8au, Opcode::V_CMP_EQ_I16}, {0x8bu, Opcode::V_CMP_LE_I16},
{0x8cu, Opcode::V_CMP_GT_I16}, {0x8du, Opcode::V_CMP_NE_I16},
{0x8eu, Opcode::V_CMP_GE_I16}, {0x91u, Opcode::V_CMPX_LT_I32},
{0x92u, Opcode::V_CMPX_EQ_I32}, {0x93u, Opcode::V_CMPX_LE_I32},
{0x94u, Opcode::V_CMPX_GT_I32}, {0x95u, Opcode::V_CMPX_NE_I32},
{0x96u, Opcode::V_CMPX_GE_I32}, {0x98u, Opcode::V_CMPX_CLASS_F32},
{0xa9u, Opcode::V_CMP_LT_U16}, {0xaau, Opcode::V_CMP_EQ_U16},
{0xabu, Opcode::V_CMP_LE_U16}, {0xacu, Opcode::V_CMP_GT_U16},
{0xadu, Opcode::V_CMP_NE_U16}, {0xaeu, Opcode::V_CMP_GE_U16},
{0xbcu, Opcode::V_CMPX_GT_U16}, {0xc0u, Opcode::V_CMP_F_U32},
{0xc1u, Opcode::V_CMP_LT_U32}, {0xc2u, Opcode::V_CMP_EQ_U32},
{0xc3u, Opcode::V_CMP_LE_U32}, {0xc4u, Opcode::V_CMP_GT_U32},
{0xc5u, Opcode::V_CMP_NE_U32}, {0xc6u, Opcode::V_CMP_GE_U32},
{0xc7u, Opcode::V_CMP_T_U32}, {0xa2u, Opcode::V_CMP_EQ_I64, false},
{0xb5u, Opcode::V_CMPX_NE_I64, false}, {0xd1u, Opcode::V_CMPX_LT_U32},
{0xd2u, Opcode::V_CMPX_EQ_U32}, {0xd3u, Opcode::V_CMPX_LE_U32},
{0xd4u, Opcode::V_CMPX_GT_U32}, {0xd5u, Opcode::V_CMPX_NE_U32},
{0xd6u, Opcode::V_CMPX_GE_U32}, {0xe2u, Opcode::V_CMP_EQ_U64, false},
{0xe4u, Opcode::V_CMP_GT_U64, false}, {0xe5u, Opcode::V_CMP_NE_U64, false},
{0xf5u, Opcode::V_CMPX_NE_U64, false}, {0xc9u, Opcode::V_CMP_LT_F16},
{0xcau, Opcode::V_CMP_EQ_F16}, {0xcbu, Opcode::V_CMP_LE_F16},
{0xccu, Opcode::V_CMP_GT_F16}, {0xcdu, Opcode::V_CMP_LG_F16},
{0xceu, Opcode::V_CMP_GE_F16}, {0xedu, Opcode::V_CMP_NEQ_F16},
{0xd9u, Opcode::V_CMPX_LT_F16}, {0xdau, Opcode::V_CMPX_EQ_F16},
{0xdbu, Opcode::V_CMPX_LE_F16}, {0xdcu, Opcode::V_CMPX_GT_F16},
{0xdeu, Opcode::V_CMPX_GE_F16}, {0xfbu, Opcode::V_CMPX_NGT_F16},
{0xfdu, Opcode::V_CMPX_NEQ_F16}, {0xfeu, Opcode::V_CMPX_NLT_F16},
};
constexpr auto VOPC_OPS = Detail::MakeOpcodeTable<0x100>(VOPC_OPCODE_LIST);
@@ -641,8 +638,8 @@ bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) {
return true;
}
bool DecodeVop1Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, Instruction& inst, std::string* error) {
void DecodeVop1Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, Instruction& inst) {
const auto modifier = code[word_index + 1u];
const auto src0 = modifier & 0xffu;
const auto dst_sel = (modifier >> 8u) & 0x7u;
@@ -657,32 +654,27 @@ bool DecodeVop1Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_i
SetRawWords(inst, code, word_index, 2);
if (!ValidateVop1Sdwa(inst, opcode, modifier)) {
return true;
return;
}
const bool scalar_dst = UsesScalarDestination(inst.opcode);
if (scalar_dst) {
if (!DecodeScalarDestination(vdst, pc, inst.dst, error)) {
return false;
}
DecodeScalarDestination(vdst, pc, inst.dst);
} else {
DecodeVectorGpr(vdst, inst.dst);
}
if (!DecodeScalarSource(src0 + (s0 == 0u ? 256u : 0u), pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(src0 + (s0 == 0u ? 256u : 0u), pc, inst.src0);
inst.dst.sdwa_sel = dst_sel;
inst.dst.sdwa_dst_unused = dst_u;
inst.dst.explicit_sdwa_dst = true;
inst.dst.clamp = clamp != 0u;
inst.dst.omod = omod;
inst.src0.sdwa_sel = src0_sel;
inst.src0.sdwa_sext = src0_sext != 0u;
inst.src0.negate = src0_neg != 0u;
inst.src0.absolute = src0_abs != 0u;
inst.src_count = 1;
inst.src0.sdwa_sel = src0_sel;
inst.src0.sdwa_sext = src0_sext != 0u;
inst.src0.negate = src0_neg != 0u;
inst.src0.absolute = src0_abs != 0u;
inst.src_count = 1;
ReadLiteralOperands(code, word_index, inst);
return true;
}
void ApplyDppModifier(Operand& operand, uint32_t modifier) {
@@ -696,38 +688,33 @@ void ApplyDppModifier(Operand& operand, uint32_t modifier) {
operand.dpp_row_mask = (modifier >> 28u) & 0xfu;
}
bool DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, Instruction& inst, std::string* error) {
void DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, Instruction& inst) {
const auto modifier = code[word_index + 1u];
const auto src0 = modifier & 0xffu;
SetRawWords(inst, code, word_index, 2);
const bool scalar_dst = UsesScalarDestination(inst.opcode);
if (scalar_dst) {
if (!DecodeScalarDestination(vdst, pc, inst.dst, error)) {
return false;
}
DecodeScalarDestination(vdst, pc, inst.dst);
} else {
DecodeVectorGpr(vdst, inst.dst);
}
if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(src0 + 256u, pc, inst.src0);
ApplyDppModifier(inst.src0, modifier);
inst.src_count = 1;
if (!IsVop1FloatSourceOpcode(inst.opcode) && (inst.src0.negate || inst.src0.absolute)) {
SetUnsupported(inst, Family::VOP1, opcode,
"VOP1 DPP integer source modifiers are not supported");
return true;
return;
}
ReadLiteralOperands(code, word_index, inst);
return true;
}
using Vop1ModifierDecodeFn = bool (*)(uint32_t pc, std::span<const uint32_t> code,
using Vop1ModifierDecodeFn = void (*)(uint32_t pc, std::span<const uint32_t> code,
uint32_t word_index, uint32_t opcode, uint32_t vdst,
Instruction& inst, std::string* error);
Instruction& inst);
struct Vop1ModifierDecoder {
uint32_t escape = 0;
@@ -740,20 +727,14 @@ constexpr Vop1ModifierDecoder VOP1_MODIFIER_DECODERS[] = {
};
bool TryDecodeVop1Modifier(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t src0, uint32_t opcode, uint32_t vdst, Instruction& inst,
std::string* error, bool* handled) {
if (handled != nullptr) {
*handled = false;
}
uint32_t src0, uint32_t opcode, uint32_t vdst, Instruction& inst) {
for (const auto& decoder: VOP1_MODIFIER_DECODERS) {
if (decoder.escape == src0) {
if (handled != nullptr) {
*handled = true;
}
return decoder.decode(pc, code, word_index, opcode, vdst, inst, error);
decoder.decode(pc, code, word_index, opcode, vdst, inst);
return true;
}
}
return true;
return false;
}
bool IsVop2FloatSourceOpcode(Opcode opcode) {
@@ -1034,9 +1015,8 @@ bool ValidateVop2Sdwa(Instruction& inst, uint32_t opcode, const Vop2SdwaFields&
return true;
}
bool FinalizeVop2Instruction(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, std::string* error) {
(void)pc;
void FinalizeVop2Instruction(std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
switch (inst.opcode) {
case Opcode::V_MADMK_F32:
case Opcode::V_FMAMK_F16:
@@ -1067,55 +1047,48 @@ bool FinalizeVop2Instruction(uint32_t pc, std::span<const uint32_t> code, uint32
default: inst.src_count = 2; break;
}
ReadLiteralOperands(code, word_index, inst);
return true;
}
bool DecodeVop2Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, uint32_t vsrc1, Instruction& inst,
std::string* error) {
void DecodeVop2Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, uint32_t vsrc1, Instruction& inst) {
const auto modifier = code[word_index + 1u];
const auto fields = DecodeVop2SdwaFields(modifier);
SetRawWords(inst, code, word_index, 2);
if (!ValidateVop2Sdwa(inst, opcode, fields)) {
return true;
return;
}
DecodeVectorGpr(vdst, inst.dst);
if (!DecodeScalarSource(fields.src0 + (fields.s0 == 0u ? 256u : 0u), pc, inst.src0, error) ||
!DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, inst.src1, error)) {
return false;
}
DecodeScalarSource(fields.src0 + (fields.s0 == 0u ? 256u : 0u), pc, inst.src0);
DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, inst.src1);
inst.dst.sdwa_sel = fields.dst_sel;
inst.dst.sdwa_dst_unused = fields.dst_u;
inst.dst.explicit_sdwa_dst = true;
inst.dst.clamp = fields.clamp != 0u;
inst.dst.omod = fields.omod;
inst.src0.sdwa_sel = fields.src0_sel;
inst.src0.sdwa_sext = fields.src0_sext != 0u;
inst.src0.negate = fields.src0_neg != 0u;
inst.src0.absolute = fields.src0_abs != 0u;
inst.src1.sdwa_sel = fields.src1_sel;
inst.src1.sdwa_sext = fields.src1_sext != 0u;
inst.src1.negate = fields.src1_neg != 0u;
inst.src1.absolute = fields.src1_abs != 0u;
return FinalizeVop2Instruction(pc, code, word_index, inst, error);
inst.src0.sdwa_sel = fields.src0_sel;
inst.src0.sdwa_sext = fields.src0_sext != 0u;
inst.src0.negate = fields.src0_neg != 0u;
inst.src0.absolute = fields.src0_abs != 0u;
inst.src1.sdwa_sel = fields.src1_sel;
inst.src1.sdwa_sext = fields.src1_sext != 0u;
inst.src1.negate = fields.src1_neg != 0u;
inst.src1.absolute = fields.src1_abs != 0u;
FinalizeVop2Instruction(code, word_index, inst);
}
bool DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, uint32_t vsrc1, Instruction& inst,
std::string* error) {
void DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, uint32_t vsrc1, Instruction& inst) {
const auto modifier = code[word_index + 1u];
const auto src0 = modifier & 0xffu;
SetRawWords(inst, code, word_index, 2);
DecodeVectorGpr(vdst, inst.dst);
DecodeVectorGpr(vsrc1, inst.src1);
if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(src0 + 256u, pc, inst.src0);
ApplyDppModifier(inst.src0, modifier);
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
const bool packed_fmac = inst.opcode == Opcode::V_PK_FMAC_F16;
if (packed_fmac) {
inst.src0.negate_hi = inst.src0.negate;
@@ -1126,14 +1099,14 @@ bool DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
(inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute)) {
SetUnsupported(inst, Family::VOP2, opcode,
"VOP2 DPP integer source modifiers are not supported");
return true;
return;
}
return FinalizeVop2Instruction(pc, code, word_index, inst, error);
FinalizeVop2Instruction(code, word_index, inst);
}
using Vop2ModifierDecodeFn = bool (*)(uint32_t pc, std::span<const uint32_t> code,
using Vop2ModifierDecodeFn = void (*)(uint32_t pc, std::span<const uint32_t> code,
uint32_t word_index, uint32_t opcode, uint32_t vdst,
uint32_t vsrc1, Instruction& inst, std::string* error);
uint32_t vsrc1, Instruction& inst);
struct Vop2ModifierDecoder {
uint32_t escape = 0;
@@ -1147,19 +1120,14 @@ constexpr Vop2ModifierDecoder VOP2_MODIFIER_DECODERS[] = {
bool TryDecodeVop2Modifier(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t src0, uint32_t opcode, uint32_t vdst, uint32_t vsrc1,
Instruction& inst, std::string* error, bool* handled) {
if (handled != nullptr) {
*handled = false;
}
Instruction& inst) {
for (const auto& decoder: VOP2_MODIFIER_DECODERS) {
if (decoder.escape == src0) {
if (handled != nullptr) {
*handled = true;
}
return decoder.decode(pc, code, word_index, opcode, vdst, vsrc1, inst, error);
decoder.decode(pc, code, word_index, opcode, vdst, vsrc1, inst);
return true;
}
}
return true;
return false;
}
struct VopcSdwaFields {
@@ -1200,31 +1168,29 @@ bool SupportsVopcSdwa(Opcode opcode) {
return opcode != Opcode::UNSUPPORTED;
}
bool DecodeVopcSdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vsrc1, Instruction& inst, std::string* error) {
void DecodeVopcSdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vsrc1, Instruction& inst) {
const auto modifier = code[word_index + 1u];
const auto fields = DecodeVopcSdwaFields(modifier);
SetRawWords(inst, code, word_index, 2);
if (fields.src0_sel > 6u || fields.src1_sel > 6u) {
SetUnsupported(inst, Family::VOPC, opcode, "VOPC SDWA selector is invalid");
return true;
return;
}
if (!SupportsVopcSdwa(inst.opcode)) {
SetUnsupported(inst, Family::VOPC, opcode,
"VOPC SDWA modifier is not supported for opcode");
return true;
return;
}
if (!DecodeScalarSource(fields.src0 + (fields.s0 == 0u ? 256u : 0u), pc, inst.src0, error) ||
!DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, inst.src1, error)) {
return false;
}
DecodeScalarSource(fields.src0 + (fields.s0 == 0u ? 256u : 0u), pc, inst.src0);
DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, inst.src1);
if (IsVopcCompareExec(inst.opcode)) {
inst.dst.kind = OperandKind::ExecLo;
} else if (fields.sd == 0u) {
inst.dst.kind = OperandKind::VccLo;
} else if (!DecodeScalarDestination(fields.sdst, pc, inst.dst, error)) {
return false;
} else {
DecodeScalarDestination(fields.sdst, pc, inst.dst);
}
inst.src0.sdwa_sel = fields.src0_sel;
inst.src0.sdwa_sext = fields.src0_sext != 0u;
@@ -1236,34 +1202,30 @@ bool DecodeVopcSdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_i
inst.src1.absolute = fields.src1_abs != 0u;
inst.src_count = 2;
ReadLiteralOperands(code, word_index, inst);
return true;
}
bool DecodeVopcDpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vsrc1, Instruction& inst, std::string* error) {
void DecodeVopcDpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vsrc1, Instruction& inst) {
const auto modifier = code[word_index + 1u];
const auto src0 = modifier & 0xffu;
SetRawWords(inst, code, word_index, 2);
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::VOPC, opcode, "VOPC opcode is not implemented");
return true;
return;
}
const auto* info = Detail::FindOpcode(VOPC_OPS, opcode);
if (info == nullptr || !info->supports_dpp) {
SetUnsupported(inst, Family::VOPC, opcode, "VOPC DPP modifier is not supported for opcode");
return true;
return;
}
DecodeVectorGpr(vsrc1, inst.src1);
if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(src0 + 256u, pc, inst.src0);
inst.dst.kind = IsVopcCompareExec(inst.opcode) ? OperandKind::ExecLo : OperandKind::VccLo;
ApplyDppModifier(inst.src0, modifier);
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
inst.src_count = 2;
ReadLiteralOperands(code, word_index, inst);
return true;
}
uint32_t NativeVop3SourceCount(Opcode opcode) {
@@ -1560,8 +1522,8 @@ bool IsVopcCompareExec(Opcode opcode) {
} // namespace
bool DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word = code[word_index];
const uint32_t opcode = (word >> 25u) & 0x3fu;
const uint32_t vdst = (word >> 17u) & 0xffu;
@@ -1577,27 +1539,20 @@ bool DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::VOP2, opcode, "VOP2 opcode is not implemented");
return true;
return;
}
bool modifier_handled = false;
if (!TryDecodeVop2Modifier(pc, code, word_index, src0, opcode, vdst, vsrc1, inst, error,
&modifier_handled)) {
return false;
}
if (modifier_handled) {
return true;
if (TryDecodeVop2Modifier(pc, code, word_index, src0, opcode, vdst, vsrc1, inst)) {
return;
}
DecodeVectorGpr(vdst, inst.dst);
DecodeVectorGpr(vsrc1, inst.src1);
if (!DecodeScalarSource(src0, pc, inst.src0, error)) {
return false;
}
return FinalizeVop2Instruction(pc, code, word_index, inst, error);
DecodeScalarSource(src0, pc, inst.src0);
FinalizeVop2Instruction(code, word_index, inst);
}
bool DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word = code[word_index];
const uint32_t opcode = (word >> 9u) & 0xffu;
const uint32_t src0 = word & 0x1ffu;
@@ -1612,39 +1567,29 @@ bool DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::VOP1, opcode, "VOP1 opcode is not implemented");
return true;
return;
}
if (inst.opcode == Opcode::V_NOP) {
inst.dst.kind = OperandKind::Null;
inst.src_count = 0;
return true;
return;
}
bool modifier_handled = false;
if (!TryDecodeVop1Modifier(pc, code, word_index, src0, opcode, vdst, inst, error,
&modifier_handled)) {
return false;
}
if (modifier_handled) {
return true;
if (TryDecodeVop1Modifier(pc, code, word_index, src0, opcode, vdst, inst)) {
return;
}
const bool scalar_dst = UsesScalarDestination(inst.opcode);
if (scalar_dst) {
if (!DecodeScalarDestination(vdst, pc, inst.dst, error)) {
return false;
}
DecodeScalarDestination(vdst, pc, inst.dst);
} else {
DecodeVectorGpr(vdst, inst.dst);
}
if (!DecodeScalarSource(src0, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(src0, pc, inst.src0);
inst.src_count = 1;
ReadLiteralOperands(code, word_index, inst);
return true;
}
bool DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word = code[word_index];
const uint32_t opcode = (word >> 17u) & 0xffu;
const uint32_t src0 = word & 0x1ffu;
@@ -1659,25 +1604,22 @@ bool DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
SetRawWords(inst, code, word_index, 1);
switch (src0) {
case 249u: return DecodeVopcSdwa(pc, code, word_index, opcode, vsrc1, inst, error);
case 250u: return DecodeVopcDpp(pc, code, word_index, opcode, vsrc1, inst, error);
case 249u: DecodeVopcSdwa(pc, code, word_index, opcode, vsrc1, inst); return;
case 250u: DecodeVopcDpp(pc, code, word_index, opcode, vsrc1, inst); return;
default: break;
}
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::VOPC, opcode, "VOPC opcode is not implemented");
return true;
return;
}
DecodeVectorGpr(vsrc1, inst.src1);
if (!DecodeScalarSource(src0, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(src0, pc, inst.src0);
inst.src_count = 2;
ReadLiteralOperands(code, word_index, inst);
return true;
}
bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word0 = code[word_index];
const uint32_t word1 = code[word_index + 1u];
const uint32_t opcode = (word0 >> 16u) & 0x3ffu;
@@ -1728,29 +1670,26 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
reason = "VOP3 mad-mix clamp/omod is not implemented";
}
SetUnsupported(inst, Family::VOP3, opcode, reason);
return true;
return;
}
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::VOP3, opcode, "VOP3 opcode is not implemented");
return true;
return;
}
if (inst.opcode == Opcode::V_NOP) {
inst.dst.kind = OperandKind::Null;
inst.src_count = 0;
return true;
return;
}
bool dst_ok = true;
if (compare_exec) {
inst.dst.kind = OperandKind::ExecLo;
} else if (scalar_dst) {
// VOP3A uses VDST for VOPC and the scalar-destination lane-read opcodes.
dst_ok = DecodeScalarDestination(vdst, pc, inst.dst, error);
DecodeScalarDestination(vdst, pc, inst.dst);
} else {
DecodeVectorGpr(vdst, inst.dst);
}
if (!dst_ok || !DecodeScalarSource(src0, pc, inst.src0, error)) {
return false;
}
DecodeScalarSource(src0, pc, inst.src0);
inst.dst.clamp = native_clamp_modifier && clamp != 0u;
inst.dst.omod = native_result_modifiers ? omod : 0u;
if (permlane) {
@@ -1758,15 +1697,13 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
inst.dst.op_sel_hi = (op_sel & 0x2u) != 0u;
}
if (vop3_vopc) {
if (!DecodeScalarSource(src1, pc, inst.src1, error)) {
return false;
}
DecodeScalarSource(src1, pc, inst.src1);
inst.src_count = 2;
if (native_source_modifiers) {
ApplyNativeVop3SourceModifiers(inst, abs, neg);
}
ReadLiteralOperands(code, word_index, inst);
return true;
return;
}
if (IsVop3EncodedVop1(opcode)) {
inst.src_count = 1;
@@ -1774,45 +1711,35 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
ApplyNativeVop3SourceModifiers(inst, abs, neg);
}
ReadLiteralOperands(code, word_index, inst);
return true;
return;
}
if (carry_in_out) {
if (!DecodeScalarDestination(sdst, pc, inst.dst2, error) ||
!DecodeScalarSource(src1, pc, inst.src1, error) ||
!DecodeScalarSource(src2, pc, inst.src2, error)) {
return false;
}
DecodeScalarDestination(sdst, pc, inst.dst2);
DecodeScalarSource(src1, pc, inst.src1);
DecodeScalarSource(src2, pc, inst.src2);
inst.src_count = 3;
ReadLiteralOperands(code, word_index, inst);
return true;
return;
}
if (vop3b_carry_out) {
if (!DecodeScalarDestination(sdst, pc, inst.dst2, error) ||
!DecodeScalarSource(src1, pc, inst.src1, error)) {
return false;
}
DecodeScalarDestination(sdst, pc, inst.dst2);
DecodeScalarSource(src1, pc, inst.src1);
inst.src_count = 2;
ReadLiteralOperands(code, word_index, inst);
return true;
return;
}
if (vop3b_mad_u64) {
if (!DecodeScalarDestination(sdst, pc, inst.dst2, error) ||
!DecodeScalarSource(src1, pc, inst.src1, error) ||
!DecodeScalarSource(src2, pc, inst.src2, error)) {
return false;
}
DecodeScalarDestination(sdst, pc, inst.dst2);
DecodeScalarSource(src1, pc, inst.src1);
DecodeScalarSource(src2, pc, inst.src2);
inst.src_count = 3;
ReadLiteralOperands(code, word_index, inst);
return true;
return;
}
if (IsVop3EncodedVop2(opcode)) {
if (!DecodeScalarSource(src1, pc, inst.src1, error)) {
return false;
}
DecodeScalarSource(src1, pc, inst.src1);
if (inst.opcode == Opcode::V_CNDMASK_B32) {
if (!DecodeScalarSource(src2, pc, inst.src2, error)) {
return false;
}
DecodeScalarSource(src2, pc, inst.src2);
inst.src_count = 3;
} else {
inst.src_count = 2;
@@ -1821,14 +1748,12 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
ApplyNativeVop3SourceModifiers(inst, abs, neg);
}
ReadLiteralOperands(code, word_index, inst);
return true;
}
if (!DecodeScalarSource(src1, pc, inst.src1, error)) {
return false;
return;
}
DecodeScalarSource(src1, pc, inst.src1);
inst.src_count = NativeVop3SourceCount(inst.opcode);
if (inst.src_count > 2u && !DecodeScalarSource(src2, pc, inst.src2, error)) {
return false;
if (inst.src_count > 2u) {
DecodeScalarSource(src2, pc, inst.src2);
}
if (mad_mix) {
ApplyNativeVop3TernaryModifiers(inst, op_sel, abs, neg);
@@ -1844,11 +1769,10 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
ApplyNativeVop3SourceModifiers(inst, abs, neg);
}
ReadLiteralOperands(code, word_index, inst);
return true;
}
bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, std::string* error) {
void DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst) {
const uint32_t word0 = code[word_index];
const uint32_t word1 = code[word_index + 1u];
const uint32_t opcode = (word0 >> 16u) & 0x7fu;
@@ -1872,16 +1796,14 @@ bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
if (inst.opcode == Opcode::UNSUPPORTED) {
SetUnsupported(inst, Family::VOP3P, opcode, "VOP3P opcode is not implemented");
return true;
return;
}
inst.src_count = Vop3pSourceCount(inst.opcode);
DecodeVectorGpr(vdst, inst.dst);
if (!DecodeScalarSource(src0, pc, inst.src0, error) ||
!DecodeScalarSource(src1, pc, inst.src1, error)) {
return false;
}
if (inst.src_count > 2u && !DecodeScalarSource(src2, pc, inst.src2, error)) {
return false;
DecodeScalarSource(src0, pc, inst.src0);
DecodeScalarSource(src1, pc, inst.src1);
if (inst.src_count > 2u) {
DecodeScalarSource(src2, pc, inst.src2);
}
if (inst.opcode == Opcode::V_FMA_F32) {
inst.dst.clamp = clamp != 0u;
@@ -1891,7 +1813,7 @@ bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
inst.dst.clamp = clamp != 0u;
} else if (clamp != 0u) {
SetUnsupported(inst, Family::VOP3P, opcode, "VOP3P integer clamp is not implemented");
return true;
return;
}
ApplyVop3pSourceModifiers(inst, op_sel, op_sel_hi, neg, neg_hi);
if (inst.opcode == Opcode::V_MAD_MIXLO_F16) {
@@ -1904,7 +1826,6 @@ bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
ApplyVop3pMixAbsModifiers(inst);
}
ReadLiteralOperands(code, word_index, inst);
return true;
}
void DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
@@ -5,16 +5,16 @@
namespace Libs::Graphics::ShaderRecompiler::Decoder {
bool DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, std::string* error);
void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
@@ -1,9 +1,8 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
#include "graphics/shader/shader.h"
#include <algorithm>
#include <array>
#include <fmt/format.h>
namespace Libs::Graphics::ShaderRecompiler::Frontend {
@@ -47,14 +46,13 @@ IR::ExportFlags Translator::AddExportInfo(const Decoder::Instruction& inst) {
return {.index = index, .pc = inst.pc};
}
bool Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
void Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
uint32_t component_count) {
for (uint32_t component = 0; component < component_count; component++) {
const auto value =
ir.Emit(IR::ValueOpcode::GetAttribute, {IR::Value(attribute), IR::Value(component)});
WriteOperand(OffsetOperand(inst.dst, component), value);
}
return true;
}
void Translator::V_INTERP_P1_F32() {}
@@ -65,25 +63,20 @@ void Translator::V_INTERP_P2_F32(const Decoder::Instruction& inst) {
WriteOperand(inst.dst, value);
}
bool Translator::V_INTERP_MOV_F32(const Decoder::Instruction& inst, std::string* error) {
void Translator::V_INTERP_MOV_F32(const Decoder::Instruction& inst) {
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));
EXIT("v_interp_mov_f32 mode %u 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) {
void Translator::EXP(const Decoder::Instruction& inst) {
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));
EXIT("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)};
@@ -93,14 +86,13 @@ bool Translator::EXP(const Decoder::Instruction& inst, std::string* error) {
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) {
bool Translator::EmitInterpolation(const Decoder::Instruction& inst) {
switch (inst.opcode) {
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);
case Decoder::Opcode::V_INTERP_MOV_F32: V_INTERP_MOV_F32(inst); return true;
default: return false;
}
}
@@ -1,3 +1,4 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend {
@@ -163,12 +164,12 @@ void Translator::S_INST_PREFETCH() {
ir.Emit(IR::ValueOpcode::InstPrefetch);
}
bool Translator::S_GETPC_B64(const Decoder::Instruction& inst, std::string* error) {
void Translator::S_GETPC_B64(const Decoder::Instruction& inst) {
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;
return;
}
auto high = PlainOperand(inst.dst);
switch (inst.dst.kind) {
@@ -179,25 +180,19 @@ bool Translator::S_GETPC_B64(const Decoder::Instruction& inst, std::string* erro
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;
EXIT("S_GETPC_B64 destination does not name a valid scalar pair at pc 0x%08x",
inst.pc);
}
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;
EXIT("S_GETPC_B64 destination does not name a valid scalar pair at pc 0x%08x", inst.pc);
}
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) {
@@ -213,8 +208,8 @@ void Translator::S_CSELECT_B64(const Decoder::Instruction& inst) {
}
void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers) {
if (inst.src0.kind == Decoder::OperandKind::Sgpr && inst.dst.kind == Decoder::OperandKind::Sgpr &&
inst.src0.reg == inst.dst.reg) {
if (inst.src0.kind == Decoder::OperandKind::Sgpr &&
inst.dst.kind == Decoder::OperandKind::Sgpr && inst.src0.reg == inst.dst.reg) {
return;
}
if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
@@ -242,21 +237,15 @@ void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
ir.SetScc(ScalarU64NonZero(result));
}
bool Translator::V_MOVRELS_B32(const Decoder::Instruction& inst, std::string* error) {
void Translator::V_MOVRELS_B32(const Decoder::Instruction& inst) {
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;
EXIT("V_MOVRELS_B32 requires VGPR source and destination at pc 0x%08x", inst.pc);
}
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;
EXIT("V_MOVRELS_B32 modifiers are not implemented at pc 0x%08x", inst.pc);
}
const auto base = inst.src0.reg;
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
@@ -267,23 +256,16 @@ bool Translator::V_MOVRELS_B32(const Decoder::Instruction& inst, std::string* er
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) {
void Translator::V_MOVRELD_B32(const Decoder::Instruction& inst) {
if (inst.dst.kind != Decoder::OperandKind::Vgpr) {
if (error != nullptr) {
*error = "V_MOVRELD_B32 requires VGPR destination";
}
return false;
EXIT("V_MOVRELD_B32 requires VGPR destination at pc 0x%08x", inst.pc);
}
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;
EXIT("V_MOVRELD_B32 modifiers are not implemented at pc 0x%08x", inst.pc);
}
const auto base = inst.dst.reg;
const auto value = ReadU32(inst.src0);
@@ -295,7 +277,6 @@ bool Translator::V_MOVRELD_B32(const Decoder::Instruction& inst, std::string* er
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) {
@@ -1,23 +1,18 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::string* error) {
void Translator::TranslateInstruction(const Decoder::Instruction& inst) {
current_opcode = inst.opcode;
current_pc = inst.pc;
switch (inst.opcode) {
case Decoder::Opcode::UNKNOWN:
case Decoder::Opcode::COUNT:
if (error != nullptr) {
*error = "decoded opcode has no IR translation";
}
return false;
EXIT("decoded opcode has no IR translation at pc 0x%08x", inst.pc);
case Decoder::Opcode::UNSUPPORTED:
if (error != nullptr) {
*error = "unsupported decoded instruction: " + Decoder::InstructionToString(inst);
}
return false;
EXIT("unsupported decoded instruction: %s", Decoder::InstructionToString(inst).c_str());
default: break;
}
@@ -27,27 +22,30 @@ bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::str
case Decoder::Family::SOP2:
case Decoder::Family::SOPK:
case Decoder::Family::SOPC:
case Decoder::Family::SOPP: translated = EmitScalar(inst, error); break;
case Decoder::Family::SOPP: translated = EmitScalar(inst); 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::VOPC: translated = EmitVector(inst); 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: translated = EmitMemory(inst, error); break;
case Decoder::Family::VINTRP: translated = EmitInterpolation(inst, error); break;
case Decoder::Family::EXP: translated = EXP(inst, error); break;
case Decoder::Family::MIMG: translated = EmitMemory(inst); break;
case Decoder::Family::VINTRP: translated = EmitInterpolation(inst); break;
case Decoder::Family::EXP:
EXP(inst);
translated = true;
break;
default: break;
}
if (!translated && error != nullptr && error->empty()) {
*error = "decoded opcode has no IR translation";
if (!translated) {
EXIT("opcode %s at pc 0x%08x has no IR translation",
Decoder::InstructionToString(inst).c_str(), inst.pc);
}
return translated;
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -1,9 +1,7 @@
#include "common/magicEnum.h"
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
#include <algorithm>
#include <array>
#include <fmt/format.h>
namespace Libs::Graphics::ShaderRecompiler::Frontend {
@@ -890,7 +888,7 @@ bool Translator::DS_BPERMUTE_B32(const Decoder::Instruction& inst) {
return true;
}
bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error) {
bool Translator::EmitMemory(const Decoder::Instruction& inst) {
switch (inst.opcode) {
case Decoder::Opcode::S_LOAD_DWORD:
case Decoder::Opcode::S_LOAD_DWORDX2:
@@ -1080,12 +1078,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error
case Decoder::Opcode::DS_WRITE_B64:
case Decoder::Opcode::DS_WRITE_B96:
case Decoder::Opcode::DS_WRITE_B128: return DS_WRITE(inst);
default:
if (error != nullptr) {
*error = fmt::format("memory-family opcode has no specialized IR translation: {}",
magic_enum::enum_name(inst.opcode));
}
return false;
default: return false;
}
}
@@ -2,14 +2,14 @@
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::EmitScalar(const Decoder::Instruction& inst, std::string* error) {
bool Translator::EmitScalar(const Decoder::Instruction& inst) {
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_GETPC_B64: S_GETPC_B64(inst); return true;
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;
@@ -1,16 +1,14 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
#include "graphics/shader/shader.h"
#include <algorithm>
#include <array>
#include <fmt/format.h>
#include <unordered_map>
#include <utility>
namespace Libs::Graphics::ShaderRecompiler::Frontend {
using ShaderError::Fail;
Decoder::Operand Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
switch (index) {
case 0: return inst.src0;
@@ -396,8 +394,7 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value)
}
}
if (type == IR::Type::U16) {
Write16Bits(operand,
IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)})));
Write16Bits(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)})));
return;
}
if (type == IR::Type::F16) {
@@ -811,11 +808,10 @@ void Translator::WriteCompareResult(const Decoder::Operand& operand, IR::U1 valu
WriteMask(operand, ir.LogicalAnd(ir.GetExec(), value));
}
bool Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info,
std::string* error) {
void Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info) {
if (source.terminator.goto_value >= 0) {
if (source.terminator.goto_variable == UINT32_MAX) {
return Fail(error, fmt::format("block {} sets an invalid goto variable", source.id));
EXIT("block %u sets an invalid goto variable", source.id);
}
ir.SetGotoVariable(source.terminator.goto_variable,
IR::U1(IR::Value(source.terminator.goto_value != 0)));
@@ -827,13 +823,13 @@ bool Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo
info.indirect_target =
ir.GetScalarReg(static_cast<IR::ScalarReg>(source.terminator.indirect_pc_sgpr));
} else {
return Fail(error, fmt::format("block {} has no indirect branch selector", source.id));
EXIT("block %u has no indirect branch selector", source.id);
}
ir.Emit(IR::ValueOpcode::ReferenceU32, {info.indirect_target});
return true;
return;
}
if (source.terminator.kind != CFG::TerminatorKind::ConditionalBranch) {
return true;
return;
}
// EXEC and VCC are invocation-local Boolean masks. Branching on that Boolean lets inactive
// invocations leave the region without reconstructing a host-subgroup mask.
@@ -848,17 +844,15 @@ bool Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo
case CFG::BranchCondition::ExecNonZero: condition = ir.GetExec(); break;
case CFG::BranchCondition::GotoVariable:
if (source.terminator.goto_variable == UINT32_MAX) {
return Fail(error,
fmt::format("block {} reads an invalid goto variable", source.id));
EXIT("block %u reads an invalid goto variable", source.id);
}
condition = ir.GetGotoVariable(source.terminator.goto_variable);
break;
case CFG::BranchCondition::Unknown:
return Fail(error, fmt::format("block {} has an unknown branch condition", source.id));
EXIT("block %u has an unknown branch condition", source.id);
}
info.condition = condition;
ir.Emit(IR::ValueOpcode::Reference, {condition});
return true;
}
namespace {
@@ -980,47 +974,42 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
}
}
bool ValidateTranslateOptions(const TranslateOptions& options, std::string* error) {
void ValidateTranslateOptions(const TranslateOptions& options) {
if (options.wave_size != 32u && options.wave_size != 64u) {
return Fail(error, "shader translation requires wave32 or wave64");
EXIT("shader translation requires wave32 or wave64, got %u", options.wave_size);
}
switch (options.stage) {
case ShaderType::Vertex:
return options.vertex != nullptr ||
Fail(error, "vertex shader translation has no vertex input metadata");
if (options.vertex == nullptr) {
EXIT("vertex shader translation has no vertex input metadata");
}
return;
case ShaderType::Pixel:
return options.pixel != nullptr ||
Fail(error, "pixel shader translation has no pixel input metadata");
if (options.pixel == nullptr) {
EXIT("pixel shader translation has no pixel input metadata");
}
return;
case ShaderType::Compute:
return options.compute != nullptr ||
Fail(error, "compute shader translation has no compute input metadata");
default: return Fail(error, "shader translation has an unsupported stage");
if (options.compute == nullptr) {
EXIT("compute shader translation has no compute input metadata");
}
return;
default:
EXIT("shader translation has unsupported stage %u",
static_cast<uint32_t>(options.stage));
}
}
} // namespace
bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
const TranslateOptions& options, IR::Program& result, std::string* error) {
result = {};
struct ResetOnFailure {
IR::Program& program;
bool complete = false;
~ResetOnFailure() {
if (!complete) {
program = {};
}
}
} reset_on_failure {result};
if (!ValidateTranslateOptions(options, error)) {
return false;
}
IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
const TranslateOptions& options) {
ValidateTranslateOptions(options);
if (cfg.blocks.empty()) {
return Fail(error, "cannot translate an empty CFG");
EXIT("cannot translate an empty CFG");
}
IR::Program result;
result.stage = options.stage;
result.wave_size = options.wave_size;
result.shader_hash = options.shader_hash;
@@ -1038,8 +1027,8 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
for (const auto& cfg_block: cfg.blocks) {
for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) {
if (index >= decoded.instructions.size()) {
return Fail(error,
"CFG block references an instruction outside the decoded program");
EXIT("CFG block %u references instruction %u outside decoded program of size %zu",
cfg_block.id, index, decoded.instructions.size());
}
const auto& instruction = decoded.instructions[index];
if (IsCodeTableLoad(cfg, instruction.pc)) {
@@ -1056,7 +1045,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
return block.id;
})->id;
if (max_id == UINT32_MAX) {
return Fail(error, "cannot allocate a typed entry block id");
EXIT("cannot allocate a typed entry block id");
}
CFG::Terminator terminator;
terminator.kind = CFG::TerminatorKind::Branch;
@@ -1070,7 +1059,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
block_indices.reserve(cfg.blocks.size());
for (const auto& source_block: cfg.blocks) {
if (!block_indices.emplace(source_block.id, result.blocks.size()).second) {
return Fail(error, fmt::format("CFG contains duplicate block id {}", source_block.id));
EXIT("CFG contains duplicate block id %u", source_block.id);
}
result.block_storage.push_back(std::make_unique<IR::Block>());
result.blocks.push_back(result.block_storage.back().get());
@@ -1082,8 +1071,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
for (const auto successor: source_block.successors) {
const auto target = block_indices.find(successor);
if (target == block_indices.end()) {
return Fail(error, fmt::format("CFG block {} has unknown successor {}",
source_block.id, successor));
EXIT("CFG block %u has unknown successor %u", source_block.id, successor);
}
result.blocks[source_index]->AddBranch(result.blocks[target->second]);
}
@@ -1182,7 +1170,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
}
}
for (const auto& cfg_block: cfg.blocks) {
const auto typed_index = block_indices.at(cfg_block.id);
const auto typed_index = block_indices.at(cfg_block.id);
Translator translator(result, result.blocks[typed_index], vector_limit, options.wave_size);
for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) {
const auto& instruction = decoded.instructions[index];
@@ -1198,44 +1186,27 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
instruction.data_dwords == embedded->components &&
instruction.dst.kind == Decoder::OperandKind::Vgpr) {
if (options.vertex == nullptr) {
return Fail(error, "embedded vertex fetch plan has no vertex input metadata");
EXIT("embedded vertex fetch plan has no vertex input metadata");
}
const auto resource = ResolveEmbeddedFetchResource(*options.vertex, *embedded);
if (resource < 0 || resource >= options.vertex->resources_num) {
return Fail(
error,
fmt::format(
"embedded vertex fetch at 0x{:08x} has no resource for attribute {}",
instruction.pc, embedded->attrib_id));
EXIT("embedded vertex fetch at 0x%08x has no resource for attribute %d",
instruction.pc, embedded->attrib_id);
}
result.info.vertex_fetch_components[static_cast<size_t>(resource)] =
static_cast<uint8_t>(std::max<uint32_t>(
result.info.vertex_fetch_components[static_cast<size_t>(resource)],
embedded->components));
if (!translator.TranslateEmbeddedFetch(instruction, static_cast<uint32_t>(resource),
embedded->components)) {
return false;
}
translator.TranslateEmbeddedFetch(instruction, static_cast<uint32_t>(resource),
embedded->components);
continue;
}
if (!translator.TranslateInstruction(instruction, error)) {
if (error == nullptr || error->empty()) {
return Fail(error, fmt::format("opcode {} at 0x{:08x} has no IR translation",
magic_enum::enum_name(instruction.opcode),
instruction.pc));
}
return false;
}
}
if (!translator.AddBranchCondition(cfg_block, result.block_info[typed_index], error)) {
return false;
translator.TranslateInstruction(instruction);
}
translator.AddBranchCondition(cfg_block, result.block_info[typed_index]);
}
if (!IR::ValidateProgram(result, false, error)) {
return false;
}
reset_on_failure.complete = true;
return true;
IR::ValidateProgram(result, false);
return result;
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -38,7 +38,7 @@ struct TranslateOptions {
const EmbeddedFetchPlan* embedded_fetch = nullptr;
};
bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
const TranslateOptions& options, IR::Program& result, std::string* error);
IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
const TranslateOptions& options);
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -13,10 +13,10 @@ public:
: program(program), ir(block), current_vector_limit(vector_limit),
current_wave_size(wave_size) {}
bool TranslateInstruction(const Decoder::Instruction& inst, std::string* error);
bool TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
void TranslateInstruction(const Decoder::Instruction& inst);
void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
uint32_t component_count);
bool AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info, std::string* error);
void AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info);
private:
struct ScalarU64 {
@@ -194,26 +194,26 @@ private:
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);
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_32(const Decoder::Instruction& inst, bool sign);
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);
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_32(const Decoder::Instruction& inst, bool sign);
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);
@@ -246,27 +246,27 @@ private:
void S_SENDMSG();
void S_TTRACEDATA();
void S_INST_PREFETCH();
bool S_GETPC_B64(const Decoder::Instruction& inst, std::string* error);
void S_GETPC_B64(const Decoder::Instruction& inst);
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_MOVRELS_B32(const Decoder::Instruction& inst);
void V_MOVRELD_B32(const Decoder::Instruction& inst);
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);
void V_INTERP_MOV_F32(const Decoder::Instruction& inst);
void EXP(const Decoder::Instruction& inst);
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);
bool EmitScalar(const Decoder::Instruction& inst);
bool EmitVector(const Decoder::Instruction& inst);
bool EmitInterpolation(const Decoder::Instruction& inst);
bool EmitMemory(const Decoder::Instruction& inst);
IR::Program& program;
IR::IREmitter ir;
@@ -2,7 +2,7 @@
namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error) {
bool Translator::EmitVector(const Decoder::Instruction& inst) {
using O = Decoder::Opcode;
switch (inst.opcode) {
case O::V_NOP: return true;
@@ -13,8 +13,8 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error
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_MOVRELS_B32: V_MOVRELS_B32(inst); return true;
case O::V_MOVRELD_B32: V_MOVRELD_B32(inst); return true;
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;