shader: remove second IR layer

This commit is contained in:
nmzik committed 2026-08-26 08:49:39 +02:00
1 parent e2c840cb23
commit ffa69d48de
49 files changed
+3691 -6001

No files matched your search

+1
View File
@@ -430,6 +430,7 @@ add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
"${KYTY_TESTS_DIR}/ResourceTrackingTests.cpp" "${KYTY_TESTS_DIR}/ResourceTrackingTests.cpp"
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.cpp" "${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.cpp"
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp" "${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
"${KYTY_SOURCE_DIR}/graphics/shader/shaderPixelParameter.cpp"
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp" "${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp"
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp" "${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp"
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp" "${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
@@ -548,9 +548,10 @@ void CreatePipelineInternal(
uint32_t attr_size = 4; uint32_t attr_size = 4;
auto registers_num = vs_input_info.resources_dst[index].registers_num; auto registers_num = vs_input_info.resources_dst[index].registers_num;
auto used_components = (vs_input_info.resource_fetch_components[index] > 0 const auto compiled_components =
? vs_input_info.resource_fetch_components[index] vs_input_info.stage.program->info.vertex_fetch_components[index];
: registers_num); auto used_components =
compiled_components > 0 ? static_cast<int>(compiled_components) : registers_num;
GetInputFormat(vs_input_info.resources[index], input_attr[index].format, attr_size, GetInputFormat(vs_input_info.resources[index], input_attr[index].format, attr_size,
static_cast<uint32_t>(used_components)); static_cast<uint32_t>(used_components));
@@ -7,7 +7,6 @@
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "graphics/shader/recompiler/frontend/translate/Translate.h" #include "graphics/shader/recompiler/frontend/translate/Translate.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h"
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h" #include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h" #include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h"
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h" #include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h"
@@ -49,7 +48,7 @@ std::string MakeIrDump(const CFG::Graph& cfg, const IR::Program& ir) {
dump += "\nIR:\n"; dump += "\nIR:\n";
dump += fmt::format("mode={} scratch_dwords={}\n", dump += fmt::format("mode={} scratch_dwords={}\n",
ir.dispatcher_fallback ? "dispatcher" : "structured", ir.scratch_dwords); ir.dispatcher_fallback ? "dispatcher" : "structured", ir.scratch_dwords);
dump += ir.values != nullptr ? IR::ValueProgramToString(*ir.values) : IR::ProgramToString(ir); dump += IR::ProgramToString(ir);
return dump; return dump;
} }
@@ -117,17 +116,8 @@ void ClearEmbeddedFetchVectorLanes(EmbeddedFetchVectorLanes* lanes, uint32_t reg
lanes->erase(first, last); lanes->erase(first, last);
} }
struct EmbeddedFetchLoad { using EmbeddedFetchLoad = Frontend::EmbeddedFetchLoad;
uint32_t pc = 0; using EmbeddedFetchData = Frontend::EmbeddedFetchPlan;
int attrib_id = -1;
uint32_t components = 0;
std::vector<uint32_t> prolog_loads;
};
struct EmbeddedFetchData {
std::vector<EmbeddedFetchLoad> loads;
int32_t vertex_offset_sgpr = -1;
};
bool IsDecodedSgpr(const Decoder::Operand& op) { bool IsDecodedSgpr(const Decoder::Operand& op) {
return op.kind == Decoder::OperandKind::Sgpr || op.kind == Decoder::OperandKind::VccLo || return op.kind == Decoder::OperandKind::Sgpr || op.kind == Decoder::OperandKind::VccLo ||
@@ -246,15 +236,6 @@ int BufferTableAttribFromOffset(uint32_t raw_offset, int dword) {
return static_cast<int>((raw_offset + static_cast<uint32_t>(dword) * 4u) / 16u); return static_cast<int>((raw_offset + static_cast<uint32_t>(dword) * 4u) / 16u);
} }
void AppendUniquePcs(std::vector<uint32_t>& dst, const std::vector<uint32_t>& src) {
dst.reserve(dst.size() + src.size());
for (auto pc: src) {
if (std::find(dst.begin(), dst.end(), pc) == dst.end()) {
dst.push_back(pc);
}
}
}
EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded, EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded,
const ShaderVertexInputInfo* input_info, const ShaderVertexInputInfo* input_info,
uint32_t user_data_base, uint32_t user_data_count, uint32_t user_data_base, uint32_t user_data_count,
@@ -489,103 +470,6 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
return data; return data;
} }
const EmbeddedFetchLoad* FindEmbeddedFetchLoad(const std::vector<EmbeddedFetchLoad>& loads,
uint32_t pc) {
for (const auto& load: loads) {
if (load.pc == pc) {
return &load;
}
}
return nullptr;
}
bool EmbeddedFetchPcInList(const std::vector<uint32_t>& pcs, uint32_t pc) {
return std::find(pcs.begin(), pcs.end(), pc) != pcs.end();
}
bool IsIrFetchPrologLoad(const IR::Instruction& inst) {
return inst.op == IR::Opcode::SLoadDword || inst.op == IR::Opcode::SBufferLoadDword;
}
int ResolveEmbeddedFetchResource(const ShaderVertexInputInfo* input_info,
const EmbeddedFetchLoad& load) {
if (load.attrib_id >= 0 && load.attrib_id < input_info->resources_num &&
input_info->resources_dst[load.attrib_id].attr_id == load.attrib_id) {
return load.attrib_id;
}
for (int i = 0; i < input_info->resources_num; i++) {
const auto& dst = input_info->resources_dst[i];
if (dst.attr_id == load.attrib_id &&
load.components <= static_cast<uint32_t>(std::max(dst.registers_num, 1))) {
return i;
}
}
for (int i = 0; i < input_info->resources_num; i++) {
if (input_info->resources_dst[i].attr_id == load.attrib_id) {
return i;
}
}
return -1;
}
uint32_t RewriteEmbeddedVertexFetches(IR::Program& ir, const ShaderVertexInputInfo* input_info,
const std::vector<EmbeddedFetchLoad>& loads) {
if (loads.empty()) {
return 0;
}
std::vector<uint32_t> prolog_pcs;
prolog_pcs.reserve(loads.size());
for (const auto& load: loads) {
AppendUniquePcs(prolog_pcs, load.prolog_loads);
}
auto* mutable_input_info = const_cast<ShaderVertexInputInfo*>(input_info);
uint32_t rewritten = 0;
for (auto& block: ir.blocks) {
for (auto& inst: block.instructions) {
if (IsIrFetchPrologLoad(inst) && EmbeddedFetchPcInList(prolog_pcs, inst.pc)) {
auto pc = inst.pc;
inst = {};
inst.pc = pc;
inst.op = IR::Opcode::ControlNop;
continue;
}
if (inst.op != IR::Opcode::BufferLoadDword) {
continue;
}
const auto* load = FindEmbeddedFetchLoad(loads, inst.pc);
if (load == nullptr || inst.memory.data_dwords != load->components ||
inst.dst.kind != IR::OperandKind::Register ||
inst.dst.reg.file != IR::RegisterFile::Vector) {
continue;
}
const auto resource_id = ResolveEmbeddedFetchResource(input_info, *load);
if (resource_id < 0 || resource_id >= input_info->resources_num) {
LOGF("ShaderRecompiler VS embedded fetch remap failed: pc=0x%08" PRIx32
" attrib=%d resources=%d\n",
inst.pc, load->attrib_id, input_info->resources_num);
continue;
}
inst.op = IR::Opcode::LoadInputF32;
inst.input_info.attr = static_cast<uint32_t>(resource_id);
inst.input_info.chan = 0;
inst.input_info.component_count = load->components;
inst.memory = {};
inst.src_count = 0;
mutable_input_info->resource_fetch_components[resource_id] =
std::max(mutable_input_info->resource_fetch_components[resource_id],
static_cast<int>(load->components));
rewritten += load->components;
}
}
return rewritten;
}
} // namespace } // namespace
bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options, bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
@@ -677,27 +561,6 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
} }
IR::Program ir; IR::Program ir;
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " IR LowerProgram\n", GetDumpLabel(options),
StageName(options.stage), options.shader_hash);
const auto lower_program = [&](IR::Program& output) {
output = {};
if (!IR::LowerProgram(decoded, cfg, options.stage, options.wave_size, output, error)) {
return false;
}
output.shader_hash = options.shader_hash;
output.user_data_base = options.user_data_base;
output.user_data_count = options.user_data_count;
output.scratch_dwords = options.scratch_dwords;
return true;
};
if (!lower_program(ir)) {
return false;
}
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " IR LowerProgram blocks=%" PRIu64
" elapsed_ms=%" PRIu64 "\n",
GetDumpLabel(options), StageName(options.stage), options.shader_hash,
static_cast<uint64_t>(ir.blocks.size()), phase_ms());
const ShaderVertexInputInfo* vertex = nullptr; const ShaderVertexInputInfo* vertex = nullptr;
const ShaderPixelInputInfo* pixel = nullptr; const ShaderPixelInputInfo* pixel = nullptr;
const ShaderComputeInputInfo* compute = nullptr; const ShaderComputeInputInfo* compute = nullptr;
@@ -709,42 +572,55 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
} }
EmbeddedFetchData embedded_fetch; EmbeddedFetchData embedded_fetch;
if (options.stage == ShaderType::Vertex && vertex->fetch_embedded) { if (options.stage == ShaderType::Vertex && vertex->fetch_embedded) {
embedded_fetch = DetectEmbeddedVertexFetch(decoded, vertex, ir.user_data_base, embedded_fetch = DetectEmbeddedVertexFetch(decoded, vertex, options.user_data_base,
ir.user_data_count, options.wave_size); options.user_data_count, options.wave_size);
auto rewritten = RewriteEmbeddedVertexFetches(ir, vertex, embedded_fetch.loads); if (!embedded_fetch.loads.empty()) {
if (rewritten > 0 || !embedded_fetch.loads.empty()) { LOGF("%s embedded vertex fetch plan: detected=%" PRIu64 "\n", GetDumpLabel(options),
LOGF("%s embedded vertex fetch rewrite: detected=%" PRIu64 " rewritten=%" PRIu32 "\n", static_cast<uint64_t>(embedded_fetch.loads.size()));
GetDumpLabel(options), static_cast<uint64_t>(embedded_fetch.loads.size()),
rewritten);
} }
} }
ir.dispatcher_fallback = dispatcher_fallback; Frontend::TranslateOptions translate_options {
if (!dispatcher_reason.empty()) { .stage = options.stage,
ir.fallback_reason = dispatcher_reason; .wave_size = options.wave_size,
} .shader_hash = options.shader_hash,
.user_data_base = options.user_data_base,
ir.values = std::make_shared<IR::ValueProgram>(); .user_data_count = options.user_data_count,
if (!Frontend::TranslateProgram(ir, *ir.values, vertex, pixel, compute, error)) { .scratch_dwords = options.scratch_dwords,
.dispatcher_fallback = dispatcher_fallback,
.cfg_failure_kind = cfg.failure_kind,
.fallback_reason = dispatcher_reason.empty() ? cfg.unsupported_reason
: dispatcher_reason,
.vertex = vertex,
.pixel = pixel,
.compute = compute,
.embedded_fetch = embedded_fetch.loads.empty() ? nullptr : &embedded_fetch,
};
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " IR TranslateProgram\n",
GetDumpLabel(options), StageName(options.stage), options.shader_hash);
if (!Frontend::TranslateProgram(decoded, cfg, translate_options, ir, error)) {
return false; return false;
} }
ir.blocks.clear(); LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " IR TranslateProgram blocks=%" PRIu64
IR::RewriteToSsa(ir.values->blocks); " elapsed_ms=%" PRIu64 "\n",
IR::ConstantPropagationPass(ir.values->blocks); GetDumpLabel(options), StageName(options.stage), options.shader_hash,
IR::ResolveControlFlowIdentities(*ir.values); static_cast<uint64_t>(ir.blocks.size()), phase_ms());
IR::RemoveIdentities(ir.values->blocks); IR::RewriteToSsa(ir.blocks);
IR::EliminateDeadCode(ir.values->blocks); IR::ConstantPropagationPass(ir.blocks);
const auto read_lane_stats = IR::EliminateReadLane(*ir.values, ir.wave_size); IR::ResolveControlFlowIdentities(ir);
IR::RemoveIdentities(ir.blocks);
IR::EliminateDeadCode(ir.blocks);
const auto read_lane_stats = IR::EliminateReadLane(ir, ir.wave_size);
if (read_lane_stats.rewritten_reads != 0) { if (read_lane_stats.rewritten_reads != 0) {
LOGF("%s read-lane elimination: reads=%" PRIu32 "\n", GetDumpLabel(options), LOGF("%s read-lane elimination: reads=%" PRIu32 "\n", GetDumpLabel(options),
read_lane_stats.rewritten_reads); read_lane_stats.rewritten_reads);
IR::ConstantPropagationPass(ir.values->blocks); IR::ConstantPropagationPass(ir.blocks);
IR::ResolveControlFlowIdentities(*ir.values); IR::ResolveControlFlowIdentities(ir);
IR::RemoveIdentities(ir.values->blocks); IR::RemoveIdentities(ir.blocks);
IR::EliminateDeadCode(ir.values->blocks); IR::EliminateDeadCode(ir.blocks);
} }
if (options.stage == ShaderType::Compute) { if (options.stage == ShaderType::Compute) {
const auto lds_barriers = const auto lds_barriers =
IR::InsertSharedMemoryBarriers(*ir.values, ir.wave_size, *compute); IR::InsertSharedMemoryBarriers(ir, ir.wave_size, *compute);
if (lds_barriers.inserted_barriers != 0) { if (lds_barriers.inserted_barriers != 0) {
LOGF("%s wave64 LDS synchronization: barriers=%" PRIu32 "\n", GetDumpLabel(options), LOGF("%s wave64 LDS synchronization: barriers=%" PRIu32 "\n", GetDumpLabel(options),
lds_barriers.inserted_barriers); lds_barriers.inserted_barriers);
@@ -758,11 +634,11 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
} }
return false; return false;
} }
IR::EliminateDeadCode(ir.values->blocks); IR::EliminateDeadCode(ir.blocks);
if (!IR::TrackResources(ir, error)) { if (!IR::TrackResources(ir, error)) {
return false; return false;
} }
IR::EliminateDeadCode(ir.values->blocks); IR::EliminateDeadCode(ir.blocks);
if (options.stage == ShaderType::Vertex) { if (options.stage == ShaderType::Vertex) {
ir.info.vertex_offset_sgpr = embedded_fetch.vertex_offset_sgpr; ir.info.vertex_offset_sgpr = embedded_fetch.vertex_offset_sgpr;
} }
@@ -1,7 +1,7 @@
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h" #include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" #include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
#include <algorithm> #include <algorithm>
@@ -51,22 +51,20 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
Expect(Kind::Samplers, Dense(program.info.samplers.size())); Expect(Kind::Samplers, Dense(program.info.samplers.size()));
} }
bool uses_gds = false; bool uses_gds = false;
if (program.values != nullptr) { for (const auto* block: program.blocks) {
for (const auto* block: program.values->blocks) { for (const auto& inst: *block) {
for (const auto& inst: *block) { if (IR::SharedAccessOf(inst.GetOpcode()) == IR::SharedAccess::None) {
if (IR::SharedAccessOf(inst.GetOpcode()) == IR::SharedAccess::None) { continue;
continue;
}
const auto index = inst.Flags<IR::MemoryFlags>().index;
if (index >= program.values->memory_info.size()) {
return Fail(error, "shared operation has invalid memory metadata");
}
const auto kind = program.values->memory_info[index].kind;
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
return Fail(error, "shared operation has invalid resource kind");
}
uses_gds |= kind == IR::ResourceKind::Gds;
} }
const auto index = inst.Flags<IR::MemoryFlags>().index;
if (index >= program.memory_info.size()) {
return Fail(error, "shared operation has invalid memory metadata");
}
const auto kind = program.memory_info[index].kind;
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
return Fail(error, "shared operation has invalid resource kind");
}
uses_gds |= kind == IR::ResourceKind::Gds;
} }
} }
if (uses_gds) { if (uses_gds) {
@@ -76,11 +74,10 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
Expect(Kind::AddressMemory, Dense(program.info.addresses.size())); Expect(Kind::AddressMemory, Dense(program.info.addresses.size()));
} }
const bool uses_flattened_runtime = const bool uses_flattened_runtime =
program.values != nullptr && !program.srt_reads.empty() ||
(!program.values->srt_reads.empty() ||
std::ranges::any_of(program.info.images, [](const IR::ImageResource& image) { std::ranges::any_of(program.info.images, [](const IR::ImageResource& image) {
return image.indirect_mapping_capacity != 0u; return image.indirect_mapping_capacity != 0u;
})); });
if (uses_flattened_runtime) { if (uses_flattened_runtime) {
Expect(Kind::FlattenedSrt); Expect(Kind::FlattenedSrt);
} }
@@ -120,9 +117,6 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
return Fail(error, "native user-data layout is inconsistent"); return Fail(error, "native user-data layout is inconsistent");
} }
if (program.values == nullptr) {
return Fail(error, "native shader plan has no typed SSA");
}
const auto planning_only_handle = [&](const IR::Inst& handle) { const auto planning_only_handle = [&](const IR::Inst& handle) {
return !handle.Uses().empty() && return !handle.Uses().empty() &&
std::ranges::all_of(handle.Uses(), [&](const IR::Use& use) { std::ranges::all_of(handle.Uses(), [&](const IR::Use& use) {
@@ -132,11 +126,11 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
return false; return false;
} }
const auto index = use.user->Flags<IR::MemoryFlags>().index; const auto index = use.user->Flags<IR::MemoryFlags>().index;
return index < program.values->memory_info.size() && return index < program.memory_info.size() &&
program.values->memory_info[index].planning_only; program.memory_info[index].planning_only;
}); });
}; };
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
const auto dense = inst.Flags<uint32_t>(); const auto dense = inst.Flags<uint32_t>();
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
@@ -174,7 +168,7 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
case IR::ValueOpcode::ReadConst: { case IR::ValueOpcode::ReadConst: {
const auto slot = inst.Arg(1).Resolve(); const auto slot = inst.Arg(1).Resolve();
if (!slot.IsImmediate() || slot.GetType() != IR::Type::U32 || if (!slot.IsImmediate() || slot.GetType() != IR::Type::U32 ||
slot.U32() >= program.values->srt_reads.size()) { slot.U32() >= program.srt_reads.size()) {
return Fail(error, "flattened SRT read has an invalid dense slot"); return Fail(error, "flattened SRT read has an invalid dense slot");
} }
break; break;
@@ -191,19 +185,15 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) { bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
program.spirv_requirements.reset(); program.spirv_requirements.reset();
IR::SpirvRequirements requirements {}; IR::SpirvRequirements requirements {};
if (program.values == nullptr) {
program.spirv_requirements.emplace(requirements);
return true;
}
const auto MarkBallot = [&] { requirements.subgroup_ballot = true; }; const auto MarkBallot = [&] { requirements.subgroup_ballot = true; };
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
if (IR::AddressOpcodeInfoOf(inst.GetOpcode()).access != IR::AddressAccess::None) { if (IR::AddressOpcodeInfoOf(inst.GetOpcode()).access != IR::AddressAccess::None) {
const auto memory_index = inst.Flags<IR::MemoryFlags>().index; const auto memory_index = inst.Flags<IR::MemoryFlags>().index;
if (memory_index >= program.values->memory_info.size()) { if (memory_index >= program.memory_info.size()) {
return Fail(error, "address operation has invalid memory metadata"); return Fail(error, "address operation has invalid memory metadata");
} }
if (program.values->memory_info[memory_index].kind == IR::ResourceKind::Scratch) { if (program.memory_info[memory_index].kind == IR::ResourceKind::Scratch) {
if (program.scratch_dwords == 0) { if (program.scratch_dwords == 0) {
return Fail(error, "scratch operation has no per-thread storage"); return Fail(error, "scratch operation has no per-thread storage");
} }
@@ -212,10 +202,10 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
} }
if (IR::BufferAccessOf(inst.GetOpcode()) != IR::BufferAccess::None) { if (IR::BufferAccessOf(inst.GetOpcode()) != IR::BufferAccess::None) {
const auto memory_index = inst.Flags<IR::MemoryFlags>().index; const auto memory_index = inst.Flags<IR::MemoryFlags>().index;
if (memory_index >= program.values->memory_info.size()) { if (memory_index >= program.memory_info.size()) {
return Fail(error, "buffer operation has invalid memory metadata"); return Fail(error, "buffer operation has invalid memory metadata");
} }
const auto& memory = program.values->memory_info[memory_index]; const auto& memory = program.memory_info[memory_index];
if (memory.kind == IR::ResourceKind::Buffer) { if (memory.kind == IR::ResourceKind::Buffer) {
if (memory.resource >= program.info.buffers.size()) { if (memory.resource >= program.info.buffers.size()) {
return Fail(error, "buffer operation has invalid resource metadata"); return Fail(error, "buffer operation has invalid resource metadata");
@@ -231,10 +221,10 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
const auto shared_access = IR::SharedAccessOf(inst.GetOpcode()); const auto shared_access = IR::SharedAccessOf(inst.GetOpcode());
if (shared_access != IR::SharedAccess::None) { if (shared_access != IR::SharedAccess::None) {
const auto index = inst.Flags<IR::MemoryFlags>().index; const auto index = inst.Flags<IR::MemoryFlags>().index;
if (index >= program.values->memory_info.size()) { if (index >= program.memory_info.size()) {
return Fail(error, "shared operation has invalid memory metadata"); return Fail(error, "shared operation has invalid memory metadata");
} }
const auto kind = program.values->memory_info[index].kind; const auto kind = program.memory_info[index].kind;
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) { if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
return Fail(error, "shared operation has invalid resource kind"); return Fail(error, "shared operation has invalid resource kind");
} }
@@ -288,11 +278,11 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
break; break;
case IR::ValueOpcode::SetAttribute: { case IR::ValueOpcode::SetAttribute: {
const auto index = inst.Flags<IR::ExportFlags>().index; const auto index = inst.Flags<IR::ExportFlags>().index;
if (index >= program.values->export_info.size()) { if (index >= program.export_info.size()) {
return Fail(error, "attribute export has invalid metadata"); return Fail(error, "attribute export has invalid metadata");
} }
if (program.stage == ShaderType::Pixel && if (program.stage == ShaderType::Pixel &&
program.values->export_info[index].vm) { program.export_info[index].vm) {
requirements.pixel_valid_mask = true; requirements.pixel_valid_mask = true;
} }
break; break;
@@ -330,12 +320,7 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
if (!ValidateNativeProgram(program, error)) { if (!ValidateNativeProgram(program, error)) {
return false; return false;
} }
if (program.values == nullptr) { if (!IR::ValidateProgram(program, true, error)) {
SetError(error, "SPIR-V emitter requires planned typed SSA");
return false;
}
const auto& value_program = *program.values;
if (!IR::ValidateValueProgram(value_program, true, error)) {
return false; return false;
} }
EmitterState state(program, resources, input_info); EmitterState state(program, resources, input_info);
@@ -348,7 +333,7 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
AllocateInputVariables(state); AllocateInputVariables(state);
AllocateOutputVariables(state); AllocateOutputVariables(state);
DefineModule(state); DefineModule(state);
if (!EmitValueProgram(state, value_program, error)) { if (!EmitProgram(state, program, error)) {
return false; return false;
} }
@@ -2,7 +2,7 @@
#include "graphics/guest_gpu/gpu_defs.h" #include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h" #include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" #include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
@@ -5,13 +5,11 @@
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
namespace { namespace {
bool UserDataDwordIndex(const EmitterState& state, IR::Register reg, uint32_t& dword_index) { bool UserDataDwordIndex(const EmitterState& state, IR::ScalarReg reg, uint32_t& dword_index) {
if (reg.file != IR::RegisterFile::Scalar) { const auto register_index = IR::RegIndex(reg);
return false;
}
const auto& registers = state.program.bindings.user_data_registers; const auto& registers = state.program.bindings.user_data_registers;
const auto found = std::lower_bound(registers.begin(), registers.end(), reg.index); const auto found = std::lower_bound(registers.begin(), registers.end(), register_index);
if (found == registers.end() || *found != reg.index) { if (found == registers.end() || *found != register_index) {
return false; return false;
} }
dword_index = static_cast<uint32_t>(found - registers.begin()); dword_index = static_cast<uint32_t>(found - registers.begin());
@@ -504,7 +502,7 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::GetUserData: { case IR::ValueOpcode::GetUserData: {
const auto reg = inst.Arg(0).ScalarRegister(); const auto reg = inst.Arg(0).ScalarRegister();
uint32_t dword = 0; uint32_t dword = 0;
if (!UserDataDwordIndex(state, {IR::RegisterFile::Scalar, IR::RegIndex(reg)}, dword)) { if (!UserDataDwordIndex(state, reg, dword)) {
ctx.Define(inst, ConstantU32(state, 0)); ctx.Define(inst, ConstantU32(state, 0));
} else { } else {
ctx.Define(inst, EmitShaderDataDwordLoad(state, dword)); ctx.Define(inst, EmitShaderDataDwordLoad(state, dword));
@@ -6,7 +6,7 @@
#include "graphics/shader/recompiler/BufferFormat.h" #include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h" #include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h" #include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h" #include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
@@ -407,7 +407,7 @@ inline void EmitLabel(EmitterState& state, uint32_t label) {
} }
struct ValueEmitContext { struct ValueEmitContext {
ValueEmitContext(EmitterState& state_, const IR::ValueProgram& program_) ValueEmitContext(EmitterState& state_, const IR::Program& program_)
: state(state_), program(program_) {} : state(state_), program(program_) {}
uint32_t Def(IR::Value value); uint32_t Def(IR::Value value);
@@ -425,7 +425,7 @@ struct ValueEmitContext {
void Fail(const IR::Inst& inst, const char* reason); void Fail(const IR::Inst& inst, const char* reason);
EmitterState& state; EmitterState& state;
const IR::ValueProgram& program; const IR::Program& program;
std::unordered_map<const IR::Inst*, uint32_t> definitions; std::unordered_map<const IR::Inst*, uint32_t> definitions;
std::unordered_map<const IR::Block*, uint32_t> labels; std::unordered_map<const IR::Block*, uint32_t> labels;
const std::unordered_map<const IR::Inst*, uint32_t>* dispatcher_spills = nullptr; const std::unordered_map<const IR::Inst*, uint32_t>* dispatcher_spills = nullptr;
@@ -847,7 +847,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst);
bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst); bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst);
bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std::string* error); bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* error);
// These templates accept local lambdas from several emitter translation units. // These templates accept local lambdas from several emitter translation units.
template <typename Fn> template <typename Fn>
@@ -79,9 +79,9 @@ void StoreDispatcherPhiEdge(ValueEmitContext& ctx, const DispatcherFunctionState
} }
} }
const IR::Block* TargetBlock(const IR::ValueProgram& program, uint32_t id) { const IR::Block* TargetBlock(const IR::Program& program, uint32_t id) {
const auto found = std::ranges::find_if( const auto found = std::ranges::find_if(
program.block_info, [&](const IR::ValueBlockInfo& info) { return info.id == id; }); program.block_info, [&](const IR::BlockInfo& info) { return info.id == id; });
if (found == program.block_info.end()) { if (found == program.block_info.end()) {
return nullptr; return nullptr;
} }
@@ -94,7 +94,7 @@ void EmitReturn(ValueEmitContext& ctx) {
} }
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block, void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
const IR::ValueBlockInfo& info) { const IR::BlockInfo& info) {
const auto& term = info.terminator; const auto& term = info.terminator;
const auto emit_merge = [&]() { const auto emit_merge = [&]() {
if (term.loop_header) { if (term.loop_header) {
@@ -150,7 +150,7 @@ void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState&
} }
uint32_t EmitDispatcherNextPc(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher, uint32_t EmitDispatcherNextPc(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
const IR::Block* block, const IR::ValueBlockInfo& info) { const IR::Block* block, const IR::BlockInfo& info) {
const auto& term = info.terminator; const auto& term = info.terminator;
switch (term.kind) { switch (term.kind) {
case CFG::TerminatorKind::Branch: case CFG::TerminatorKind::Branch:
@@ -503,7 +503,7 @@ void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) {
error = fmt::format("typed opcode {} {}", IR::ValueOpcodeName(inst.GetOpcode()), reason); error = fmt::format("typed opcode {} {}", IR::ValueOpcodeName(inst.GetOpcode()), reason);
} }
bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std::string* error) { bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* error) {
ValueEmitContext ctx(state, program); ValueEmitContext ctx(state, program);
std::optional<DispatcherFunctionState> dispatcher; std::optional<DispatcherFunctionState> dispatcher;
if (state.stage == ShaderType::Pixel && state.requirements.pixel_valid_mask) { if (state.stage == ShaderType::Pixel && state.requirements.pixel_valid_mask) {
@@ -1342,7 +1342,7 @@ bool IsolateSemanticLoopHeaders(Graph& graph, std::string* error) {
// SPIR-V requires OpLoopMerge and its branch to remain in the loop header's // SPIR-V requires OpLoopMerge and its branch to remain in the loop header's
// physical block. Keep that header as a dedicated control node, exactly like // physical block. Keep that header as a dedicated control node, exactly like
// the separate Loop node in shadPS4's structured AST. Guest instructions live // the separate Loop node in shadPS4's structured AST. Guest instructions live
// in the body so later lowering may introduce bounds/EXEC control flow without // in the body so later translation may introduce bounds/EXEC control flow without
// displacing OpLoopMerge into a helper-created block. // displacing OpLoopMerge into a helper-created block.
if (!IsolateSemanticLoopHeader(graph, loop->header)) { if (!IsolateSemanticLoopHeader(graph, loop->header)) {
SetFailure(graph, FailureKind::StructuredControlFlow, loop->header, SetFailure(graph, FailureKind::StructuredControlFlow, loop->header,
@@ -423,7 +423,7 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
DecodeVectorGpr(vdst, inst.dst); DecodeVectorGpr(vdst, inst.dst);
if (inst.opcode == Opcode::DS_READ_U16_D16) { if (inst.opcode == Opcode::DS_READ_U16_D16) {
// Native D16 reads update only the selected destination half. Reuse the partial // Native D16 reads update only the selected destination half. Reuse the partial
// destination representation so the typed lowering preserves the upper word. // destination representation so typed IR translation preserves the upper word.
inst.dst.sdwa_sel = 4u; inst.dst.sdwa_sel = 4u;
} }
DecodeVectorGpr(addr, inst.src0); DecodeVectorGpr(addr, inst.src0);
@@ -7,7 +7,7 @@ namespace {
using Detail::OpcodeMap; using Detail::OpcodeMap;
// These profiles describe the selectors that the current lowering implements. They are not // These profiles describe the selectors that the current translator implements. They are not
// architectural SDWA legality classes. // architectural SDWA legality classes.
enum class Vop2SdwaProfile { enum class Vop2SdwaProfile {
None, None,
@@ -1,42 +1,104 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h" #include "graphics/shader/recompiler/frontend/translate/Translator.h"
#include "graphics/shader/shader.h"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <fmt/format.h>
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
IR::ExportFlags Translator::AddExportInfo(const IR::Instruction& inst) { namespace {
const auto index = static_cast<uint32_t>(value_program.export_info.size());
value_program.export_info.push_back(inst.export_info); IR::ExportTargetKind ExportTargetKindFromTarget(uint32_t target, uint32_t& index) {
index = 0;
switch (target) {
case 0x08u: return IR::ExportTargetKind::MrtZ;
case 0x09u: return IR::ExportTargetKind::Null;
case 0x14u: return IR::ExportTargetKind::Primitive;
default: break;
}
if (target <= 0x07u) {
index = target;
return IR::ExportTargetKind::Mrt;
}
if (target >= 0x0cu && target <= 0x0fu) {
index = target - 0x0cu;
return IR::ExportTargetKind::Position;
}
if (target >= 0x20u && target <= 0x3fu) {
index = target - 0x20u;
return IR::ExportTargetKind::Parameter;
}
return IR::ExportTargetKind::Unknown;
}
} // namespace
IR::ExportFlags Translator::AddExportInfo(const Decoder::Instruction& inst) {
IR::ExportInfo info;
info.kind = ExportTargetKindFromTarget(inst.exp.target, info.index);
info.target = inst.exp.target;
info.en = inst.exp.en;
info.done = inst.exp.done;
info.compr = inst.exp.compr;
info.vm = inst.exp.vm;
const auto index = static_cast<uint32_t>(program.export_info.size());
program.export_info.push_back(info);
return {.index = index, .pc = inst.pc}; return {.index = index, .pc = inst.pc};
} }
bool Translator::TranslateAttributeOperation(const IR::Instruction& inst) { bool Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
if (inst.op == IR::Opcode::LoadInputF32) { uint32_t component_count) {
for (uint32_t component = 0; component < inst.input_info.component_count; component++) { for (uint32_t component = 0; component < component_count; component++) {
const auto chan = inst.input_info.chan + component; const auto value =
const auto value = inst.input_info.interpolation_mode < 3u ir.Emit(IR::ValueOpcode::GetAttribute, {IR::Value(attribute), IR::Value(component)});
? ir.Emit(IR::ValueOpcode::GetInterpolationParameter, WriteOperand(OffsetOperand(inst.dst, component), value);
{IR::Value(inst.input_info.attr), IR::Value(chan),
IR::Value(inst.input_info.interpolation_mode)})
: ir.Emit(IR::ValueOpcode::GetAttribute,
{IR::Value(inst.input_info.attr), IR::Value(chan)});
WriteOperand(OffsetOperand(inst.dst, component), value);
}
return true;
} }
if (inst.op == IR::Opcode::Export) { return true;
std::array<IR::Value, 4> components {IR::Value(0u), IR::Value(0u), IR::Value(0u),
IR::Value(0u)};
for (uint32_t index = 0; index < std::min(inst.src_count, 4u); index++) {
components[index] = ReadRawU32(PlainOperand(inst.src[index]));
}
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;
}
return false;
} }
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail bool Translator::TranslateAttributeOperation(const Decoder::Instruction& inst, std::string* error) {
switch (inst.opcode) {
case Decoder::Opcode::V_INTERP_P1_F32: return true;
case Decoder::Opcode::V_INTERP_P2_F32:
case Decoder::Opcode::V_INTERP_MOV_F32: {
if (inst.opcode == Decoder::Opcode::V_INTERP_MOV_F32 && inst.src0.value >= 3u) {
return ShaderError::Fail(
error, fmt::format("v_interp_mov_f32 mode {} is reserved at pc 0x{:08x}",
inst.src0.value, inst.pc));
}
const auto interpolation_mode =
inst.opcode == Decoder::Opcode::V_INTERP_MOV_F32 ? inst.src0.value : 3u;
const auto value =
interpolation_mode < 3u
? ir.Emit(IR::ValueOpcode::GetInterpolationParameter,
{IR::Value(inst.src1.value), IR::Value(inst.src2.value),
IR::Value(interpolation_mode)})
: ir.Emit(IR::ValueOpcode::GetAttribute,
{IR::Value(inst.src1.value), IR::Value(inst.src2.value)});
WriteOperand(inst.dst, value);
return true;
}
case Decoder::Opcode::EXP: {
uint32_t index = 0;
if (ExportTargetKindFromTarget(inst.exp.target, index) ==
IR::ExportTargetKind::Unknown) {
return ShaderError::Fail(
error, fmt::format("unsupported EXP target 0x{:02x} at pc 0x{:08x}",
inst.exp.target, inst.pc));
}
std::array<IR::Value, 4> components {IR::Value(0u), IR::Value(0u), IR::Value(0u),
IR::Value(0u)};
for (uint32_t source = 0; source < std::min(inst.src_count, 4u); source++) {
components[source] = ReadRawU32(PlainOperand(SourceAt(inst, source)));
}
const auto data = ir.Emit(IR::ValueOpcode::CompositeConstructU32x4,
{components[0], components[1], components[2], components[3]});
ir.Emit(IR::ValueOpcode::SetAttribute, {data, ir.GetExec()}, AddExportInfo(inst));
return true;
}
default: return false;
}
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -1,187 +1,210 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h" #include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslateIntegerCompare(const IR::Instruction& inst) { bool Translator::TranslateIntegerCompare(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode {}; IR::ValueOpcode opcode {};
IR::Type type = IR::Type::U32; IR::Type type = IR::Type::U32;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::CompareFalse: case Decoder::Opcode::V_CMP_F_I32:
case Decoder::Opcode::V_CMP_F_U32:
WriteCompareResult(inst.dst, IR::U1(IR::Value(false))); WriteCompareResult(inst.dst, IR::U1(IR::Value(false)));
return true; return true;
case IR::Opcode::CompareTrue: case Decoder::Opcode::V_CMP_T_I32:
case Decoder::Opcode::V_CMP_T_U32:
WriteCompareResult(inst.dst, IR::U1(IR::Value(true))); WriteCompareResult(inst.dst, IR::U1(IR::Value(true)));
return true; return true;
case IR::Opcode::CompareEqU32: case Decoder::Opcode::S_CMP_EQ_U32:
case IR::Opcode::CompareMaskEqU32: case Decoder::Opcode::V_CMP_EQ_U32:
case IR::Opcode::CompareEqI32: case Decoder::Opcode::V_CMPX_EQ_U32:
case IR::Opcode::CompareMaskEqI32: opcode = IR::ValueOpcode::IEqual32; break; case Decoder::Opcode::S_CMP_EQ_I32:
case IR::Opcode::CompareNeU32: case Decoder::Opcode::V_CMP_EQ_I32:
case IR::Opcode::CompareMaskNeU32: case Decoder::Opcode::V_CMPX_EQ_I32: opcode = IR::ValueOpcode::IEqual32; break;
case IR::Opcode::CompareNeI32: case Decoder::Opcode::S_CMP_LG_U32:
case IR::Opcode::CompareMaskNeI32: opcode = IR::ValueOpcode::INotEqual32; break; case Decoder::Opcode::V_CMP_NE_U32:
case IR::Opcode::CompareGtU32: case Decoder::Opcode::V_CMPX_NE_U32:
case IR::Opcode::CompareMaskGtU32: opcode = IR::ValueOpcode::UGreaterThan32; break; case Decoder::Opcode::S_CMP_LG_I32:
case IR::Opcode::CompareGeU32: case Decoder::Opcode::V_CMP_NE_I32:
case IR::Opcode::CompareMaskGeU32: opcode = IR::ValueOpcode::UGreaterThanEqual32; break; case Decoder::Opcode::V_CMPX_NE_I32: opcode = IR::ValueOpcode::INotEqual32; break;
case IR::Opcode::CompareLtU32: case Decoder::Opcode::S_CMP_GT_U32:
case IR::Opcode::CompareMaskLtU32: opcode = IR::ValueOpcode::ULessThan32; break; case Decoder::Opcode::V_CMP_GT_U32:
case IR::Opcode::CompareLeU32: case Decoder::Opcode::V_CMPX_GT_U32: opcode = IR::ValueOpcode::UGreaterThan32; break;
case IR::Opcode::CompareMaskLeU32: opcode = IR::ValueOpcode::ULessThanEqual32; break; case Decoder::Opcode::S_CMP_GE_U32:
case IR::Opcode::CompareGtI32: case Decoder::Opcode::V_CMP_GE_U32:
case IR::Opcode::CompareMaskGtI32: opcode = IR::ValueOpcode::SGreaterThan32; break; case Decoder::Opcode::V_CMPX_GE_U32: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
case IR::Opcode::CompareGeI32: case Decoder::Opcode::S_CMP_LT_U32:
case IR::Opcode::CompareMaskGeI32: opcode = IR::ValueOpcode::SGreaterThanEqual32; break; case Decoder::Opcode::V_CMP_LT_U32:
case IR::Opcode::CompareLtI32: case Decoder::Opcode::V_CMPX_LT_U32: opcode = IR::ValueOpcode::ULessThan32; break;
case IR::Opcode::CompareMaskLtI32: opcode = IR::ValueOpcode::SLessThan32; break; case Decoder::Opcode::S_CMP_LE_U32:
case IR::Opcode::CompareLeI32: case Decoder::Opcode::V_CMP_LE_U32:
case IR::Opcode::CompareMaskLeI32: opcode = IR::ValueOpcode::SLessThanEqual32; break; case Decoder::Opcode::V_CMPX_LE_U32: opcode = IR::ValueOpcode::ULessThanEqual32; break;
case IR::Opcode::CompareEqU64: case Decoder::Opcode::S_CMP_GT_I32:
case Decoder::Opcode::V_CMP_GT_I32:
case Decoder::Opcode::V_CMPX_GT_I32: opcode = IR::ValueOpcode::SGreaterThan32; break;
case Decoder::Opcode::S_CMP_GE_I32:
case Decoder::Opcode::V_CMP_GE_I32:
case Decoder::Opcode::V_CMPX_GE_I32: opcode = IR::ValueOpcode::SGreaterThanEqual32; break;
case Decoder::Opcode::S_CMP_LT_I32:
case Decoder::Opcode::V_CMP_LT_I32:
case Decoder::Opcode::V_CMPX_LT_I32: opcode = IR::ValueOpcode::SLessThan32; break;
case Decoder::Opcode::S_CMP_LE_I32:
case Decoder::Opcode::V_CMP_LE_I32:
case Decoder::Opcode::V_CMPX_LE_I32: opcode = IR::ValueOpcode::SLessThanEqual32; break;
case Decoder::Opcode::S_CMP_EQ_U64:
case Decoder::Opcode::V_CMP_EQ_I64:
opcode = IR::ValueOpcode::IEqual64; opcode = IR::ValueOpcode::IEqual64;
type = IR::Type::U64; type = IR::Type::U64;
break; break;
case IR::Opcode::CompareGtU64: case Decoder::Opcode::V_CMP_GT_U64:
opcode = IR::ValueOpcode::UGreaterThan64; opcode = IR::ValueOpcode::UGreaterThan64;
type = IR::Type::U64; type = IR::Type::U64;
break; break;
case IR::Opcode::CompareNeU64: case Decoder::Opcode::S_CMP_LG_U64:
case Decoder::Opcode::V_CMP_NE_U64:
case Decoder::Opcode::V_CMPX_NE_I64:
case Decoder::Opcode::V_CMPX_NE_U64:
opcode = IR::ValueOpcode::INotEqual64; opcode = IR::ValueOpcode::INotEqual64;
type = IR::Type::U64; type = IR::Type::U64;
break; break;
default: return false; default: return false;
} }
const auto lhs = ReadOperand(inst.src[0], type); const auto lhs = ReadOperand(inst.src0, type);
const auto rhs = ReadOperand(inst.src[1], type); const auto rhs = ReadOperand(inst.src1, type);
WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {lhs, rhs}))); WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {lhs, rhs})));
return true; return true;
} }
bool Translator::TranslateInteger16Compare(const IR::Instruction& inst) { bool Translator::TranslateInteger16Compare(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode {}; IR::ValueOpcode opcode {};
bool signed_value = false; bool signed_value = false;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::CompareEqU16: case Decoder::Opcode::V_CMP_EQ_U16:
case IR::Opcode::CompareEqI16: opcode = IR::ValueOpcode::IEqual32; break; case Decoder::Opcode::V_CMP_EQ_I16: opcode = IR::ValueOpcode::IEqual32; break;
case IR::Opcode::CompareNeU16: case Decoder::Opcode::V_CMP_NE_U16:
case IR::Opcode::CompareNeI16: opcode = IR::ValueOpcode::INotEqual32; break; case Decoder::Opcode::V_CMP_NE_I16: opcode = IR::ValueOpcode::INotEqual32; break;
case IR::Opcode::CompareGtU16: opcode = IR::ValueOpcode::UGreaterThan32; break; case Decoder::Opcode::V_CMP_GT_U16: opcode = IR::ValueOpcode::UGreaterThan32; break;
case IR::Opcode::CompareGeU16: opcode = IR::ValueOpcode::UGreaterThanEqual32; break; case Decoder::Opcode::V_CMP_GE_U16: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
case IR::Opcode::CompareLtU16: opcode = IR::ValueOpcode::ULessThan32; break; case Decoder::Opcode::V_CMP_LT_U16: opcode = IR::ValueOpcode::ULessThan32; break;
case IR::Opcode::CompareLeU16: opcode = IR::ValueOpcode::ULessThanEqual32; break; case Decoder::Opcode::V_CMP_LE_U16: opcode = IR::ValueOpcode::ULessThanEqual32; break;
case IR::Opcode::CompareGtI16: case Decoder::Opcode::V_CMP_GT_I16:
opcode = IR::ValueOpcode::SGreaterThan32; opcode = IR::ValueOpcode::SGreaterThan32;
signed_value = true; signed_value = true;
break; break;
case IR::Opcode::CompareGeI16: case Decoder::Opcode::V_CMP_GE_I16:
opcode = IR::ValueOpcode::SGreaterThanEqual32; opcode = IR::ValueOpcode::SGreaterThanEqual32;
signed_value = true; signed_value = true;
break; break;
case IR::Opcode::CompareLtI16: case Decoder::Opcode::V_CMP_LT_I16:
opcode = IR::ValueOpcode::SLessThan32; opcode = IR::ValueOpcode::SLessThan32;
signed_value = true; signed_value = true;
break; break;
case IR::Opcode::CompareLeI16: case Decoder::Opcode::V_CMP_LE_I16:
opcode = IR::ValueOpcode::SLessThanEqual32; opcode = IR::ValueOpcode::SLessThanEqual32;
signed_value = true; signed_value = true;
break; break;
default: return false; default: return false;
} }
if (inst.op == IR::Opcode::CompareEqI16 || inst.op == IR::Opcode::CompareNeI16) { if (inst.opcode == Decoder::Opcode::V_CMP_EQ_I16 ||
inst.opcode == Decoder::Opcode::V_CMP_NE_I16) {
signed_value = true; signed_value = true;
} }
WriteCompareResult(inst.dst, WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {ReadU16AsU32(inst.src0, signed_value),
IR::U1(ir.Emit(opcode, {ReadU16AsU32(inst.src[0], signed_value), ReadU16AsU32(inst.src1, signed_value)})));
ReadU16AsU32(inst.src[1], signed_value)})));
return true; return true;
} }
bool Translator::TranslateFloatCompare(const IR::Instruction& inst) { bool Translator::TranslateFloatCompare(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode {}; IR::ValueOpcode opcode {};
bool half = false; bool half = false;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::CompareEqF32: case Decoder::Opcode::V_CMP_F_F32:
case IR::Opcode::CompareMaskEqF32: opcode = IR::ValueOpcode::FPOrdEqual32; break; WriteCompareResult(inst.dst, IR::U1(IR::Value(false)));
case IR::Opcode::CompareNeF32: return true;
case IR::Opcode::CompareMaskNeF32: opcode = IR::ValueOpcode::FPOrdNotEqual32; break; case Decoder::Opcode::V_CMP_TRU_F32:
case IR::Opcode::CompareGtF32: WriteCompareResult(inst.dst, IR::U1(IR::Value(true)));
case IR::Opcode::CompareMaskGtF32: opcode = IR::ValueOpcode::FPOrdGreaterThan32; break; return true;
case IR::Opcode::CompareGeF32: case Decoder::Opcode::V_CMP_EQ_F32:
case IR::Opcode::CompareMaskGeF32: opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32; break; case Decoder::Opcode::V_CMPX_EQ_F32: opcode = IR::ValueOpcode::FPOrdEqual32; break;
case IR::Opcode::CompareLtF32: case Decoder::Opcode::V_CMP_LG_F32:
case IR::Opcode::CompareMaskLtF32: opcode = IR::ValueOpcode::FPOrdLessThan32; break; case Decoder::Opcode::V_CMPX_LG_F32: opcode = IR::ValueOpcode::FPOrdNotEqual32; break;
case IR::Opcode::CompareLeF32: case Decoder::Opcode::V_CMP_GT_F32:
case IR::Opcode::CompareMaskLeF32: opcode = IR::ValueOpcode::FPOrdLessThanEqual32; break; case Decoder::Opcode::V_CMPX_GT_F32: opcode = IR::ValueOpcode::FPOrdGreaterThan32; break;
case IR::Opcode::CompareUnordEqF32: case Decoder::Opcode::V_CMP_GE_F32:
case IR::Opcode::CompareMaskUnordEqF32: opcode = IR::ValueOpcode::FPUnordEqual32; break; case Decoder::Opcode::V_CMPX_GE_F32:
case IR::Opcode::CompareUnordNeF32: opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32;
case IR::Opcode::CompareMaskUnordNeF32: opcode = IR::ValueOpcode::FPUnordNotEqual32; break;
case IR::Opcode::CompareUnordGtF32:
case IR::Opcode::CompareMaskUnordGtF32:
opcode = IR::ValueOpcode::FPUnordGreaterThan32;
break; break;
case IR::Opcode::CompareUnordGeF32: case Decoder::Opcode::V_CMP_LT_F32:
case IR::Opcode::CompareMaskUnordGeF32: case Decoder::Opcode::V_CMPX_LT_F32: opcode = IR::ValueOpcode::FPOrdLessThan32; break;
case Decoder::Opcode::V_CMP_LE_F32:
case Decoder::Opcode::V_CMPX_LE_F32: opcode = IR::ValueOpcode::FPOrdLessThanEqual32; break;
case Decoder::Opcode::V_CMP_NLG_F32:
case Decoder::Opcode::V_CMPX_NLG_F32: opcode = IR::ValueOpcode::FPUnordEqual32; break;
case Decoder::Opcode::V_CMP_NEQ_F32:
case Decoder::Opcode::V_CMPX_NEQ_F32: opcode = IR::ValueOpcode::FPUnordNotEqual32; break;
case Decoder::Opcode::V_CMP_NLE_F32:
case Decoder::Opcode::V_CMPX_NLE_F32: opcode = IR::ValueOpcode::FPUnordGreaterThan32; break;
case Decoder::Opcode::V_CMP_NLT_F32:
case Decoder::Opcode::V_CMPX_NLT_F32:
opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32; opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32;
break; break;
case IR::Opcode::CompareUnordLtF32: case Decoder::Opcode::V_CMP_NGE_F32:
case IR::Opcode::CompareMaskUnordLtF32: opcode = IR::ValueOpcode::FPUnordLessThan32; break; case Decoder::Opcode::V_CMPX_NGE_F32: opcode = IR::ValueOpcode::FPUnordLessThan32; break;
case IR::Opcode::CompareUnordLeF32: case Decoder::Opcode::V_CMP_NGT_F32:
case IR::Opcode::CompareMaskUnordLeF32: case Decoder::Opcode::V_CMPX_NGT_F32:
opcode = IR::ValueOpcode::FPUnordLessThanEqual32; opcode = IR::ValueOpcode::FPUnordLessThanEqual32;
break; break;
case IR::Opcode::CompareEqF16: case Decoder::Opcode::V_CMP_EQ_F16:
case IR::Opcode::CompareMaskEqF16: case Decoder::Opcode::V_CMPX_EQ_F16:
opcode = IR::ValueOpcode::FPOrdEqual32; opcode = IR::ValueOpcode::FPOrdEqual32;
half = true; half = true;
break; break;
case IR::Opcode::CompareNeF16: case Decoder::Opcode::V_CMP_LG_F16:
case IR::Opcode::CompareMaskNeF16:
opcode = IR::ValueOpcode::FPOrdNotEqual32; opcode = IR::ValueOpcode::FPOrdNotEqual32;
half = true; half = true;
break; break;
case IR::Opcode::CompareGtF16: case Decoder::Opcode::V_CMP_GT_F16:
case IR::Opcode::CompareMaskGtF16: case Decoder::Opcode::V_CMPX_GT_F16:
opcode = IR::ValueOpcode::FPOrdGreaterThan32; opcode = IR::ValueOpcode::FPOrdGreaterThan32;
half = true; half = true;
break; break;
case IR::Opcode::CompareGeF16: case Decoder::Opcode::V_CMP_GE_F16:
case IR::Opcode::CompareMaskGeF16: case Decoder::Opcode::V_CMPX_GE_F16:
opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32; opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32;
half = true; half = true;
break; break;
case IR::Opcode::CompareLtF16: case Decoder::Opcode::V_CMP_LT_F16:
case IR::Opcode::CompareMaskLtF16: case Decoder::Opcode::V_CMPX_LT_F16:
opcode = IR::ValueOpcode::FPOrdLessThan32; opcode = IR::ValueOpcode::FPOrdLessThan32;
half = true; half = true;
break; break;
case IR::Opcode::CompareLeF16: case Decoder::Opcode::V_CMP_LE_F16:
case IR::Opcode::CompareMaskLeF16: case Decoder::Opcode::V_CMPX_LE_F16:
opcode = IR::ValueOpcode::FPOrdLessThanEqual32; opcode = IR::ValueOpcode::FPOrdLessThanEqual32;
half = true; half = true;
break; break;
case IR::Opcode::CompareUnordNeF16: case Decoder::Opcode::V_CMP_NEQ_F16:
case IR::Opcode::CompareMaskUnordNeF16: case Decoder::Opcode::V_CMPX_NEQ_F16:
opcode = IR::ValueOpcode::FPUnordNotEqual32; opcode = IR::ValueOpcode::FPUnordNotEqual32;
half = true; half = true;
break; break;
case IR::Opcode::CompareMaskUnordGeF16: case Decoder::Opcode::V_CMPX_NLT_F16:
opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32; opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32;
half = true; half = true;
break; break;
case IR::Opcode::CompareOrderedF32: case Decoder::Opcode::V_CMP_O_F32:
case IR::Opcode::CompareUnorderedF32: { case Decoder::Opcode::V_CMP_U_F32: {
const auto lhs = IR::F32(ReadOperand(inst.src[0], IR::Type::F32)); const auto lhs = IR::F32(ReadOperand(inst.src0, IR::Type::F32));
const auto rhs = IR::F32(ReadOperand(inst.src[1], IR::Type::F32)); const auto rhs = IR::F32(ReadOperand(inst.src1, IR::Type::F32));
const auto unordered = ir.LogicalOr(IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {lhs})), const auto unordered = ir.LogicalOr(IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {lhs})),
IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {rhs}))); IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {rhs})));
const auto result = const auto result =
inst.op == IR::Opcode::CompareUnorderedF32 ? unordered : ir.LogicalNot(unordered); inst.opcode == Decoder::Opcode::V_CMP_U_F32 ? unordered : ir.LogicalNot(unordered);
WriteCompareResult(inst.dst, result); WriteCompareResult(inst.dst, result);
return true; return true;
} }
case IR::Opcode::CompareClassF32: { case Decoder::Opcode::V_CMP_CLASS_F32: {
const auto value = ReadOperand(inst.src[0], IR::Type::F32); const auto value = ReadOperand(inst.src0, IR::Type::F32);
const auto mask = ReadOperand(inst.src[1], IR::Type::U32); const auto mask = ReadOperand(inst.src1, IR::Type::U32);
WriteCompareResult(inst.dst, WriteCompareResult(inst.dst,
IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask}))); IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask})));
return true; return true;
@@ -189,11 +212,11 @@ bool Translator::TranslateFloatCompare(const IR::Instruction& inst) {
default: return false; default: return false;
} }
const auto lhs = const auto lhs =
half ? IR::Value(ReadF16AsF32(inst.src[0])) : ReadOperand(inst.src[0], IR::Type::F32); half ? IR::Value(ReadF16AsF32(inst.src0)) : ReadOperand(inst.src0, IR::Type::F32);
const auto rhs = const auto rhs =
half ? IR::Value(ReadF16AsF32(inst.src[1])) : ReadOperand(inst.src[1], IR::Type::F32); half ? IR::Value(ReadF16AsF32(inst.src1)) : ReadOperand(inst.src1, IR::Type::F32);
WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {lhs, rhs}))); WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {lhs, rhs})));
return true; return true;
} }
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail } // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -1,23 +1,52 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h" #include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
namespace {
bool Translator::TranslateStateOperation(const IR::Instruction& inst) { bool IsExecOrVcc(const Decoder::Operand& operand) {
switch (operand.kind) {
case Decoder::OperandKind::ExecLo:
case Decoder::OperandKind::ExecHi:
case Decoder::OperandKind::VccLo:
case Decoder::OperandKind::VccHi: return true;
default: return false;
}
}
Decoder::Operand ConditionOperand(Decoder::OperandKind kind) {
Decoder::Operand operand;
operand.kind = kind;
return operand;
}
} // namespace
bool Translator::TranslateStateOperation(const Decoder::Instruction& inst) {
const auto zero = IR::U32(IR::Value(0u)); const auto zero = IR::U32(IR::Value(0u));
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::SaveexecB32: case Decoder::Opcode::S_AND_SAVEEXEC_B32:
case IR::Opcode::SaveexecB64: { case Decoder::Opcode::S_ANDN1_SAVEEXEC_B32:
case Decoder::Opcode::S_AND_SAVEEXEC_B64:
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B64:
case Decoder::Opcode::S_ORN2_SAVEEXEC_B64: {
const auto old = ir.GetExec(); const auto old = ir.GetExec();
const auto src = ReadMask(inst.src[0]); const auto src = ReadMask(inst.src0);
IR::U1 result; IR::U1 result;
switch (inst.saveexec_mode) { switch (inst.opcode) {
case IR::SaveexecMode::And: result = ir.LogicalAnd(old, src); break; case Decoder::Opcode::S_AND_SAVEEXEC_B32:
case IR::SaveexecMode::Andn1: case Decoder::Opcode::S_AND_SAVEEXEC_B64: result = ir.LogicalAnd(old, src); break;
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B32:
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B64:
result = ir.LogicalAnd(old, ir.LogicalNot(src)); result = ir.LogicalAnd(old, ir.LogicalNot(src));
break; break;
case IR::SaveexecMode::Orn2: result = ir.LogicalOr(ir.LogicalNot(old), src); break; case Decoder::Opcode::S_ORN2_SAVEEXEC_B64:
result = ir.LogicalOr(ir.LogicalNot(old), src);
break;
default: EXIT("invalid saveexec opcode");
} }
if (inst.op == IR::Opcode::SaveexecB64) { if (inst.opcode == Decoder::Opcode::S_AND_SAVEEXEC_B64 ||
inst.opcode == Decoder::Opcode::S_ANDN1_SAVEEXEC_B64 ||
inst.opcode == Decoder::Opcode::S_ORN2_SAVEEXEC_B64) {
WriteMask64(inst.dst, old); WriteMask64(inst.dst, old);
} else { } else {
WriteMask(inst.dst, old); WriteMask(inst.dst, old);
@@ -29,63 +58,75 @@ bool Translator::TranslateStateOperation(const IR::Instruction& inst) {
ir.SetScc(result); ir.SetScc(result);
return true; return true;
} }
case IR::Opcode::ScalarAddCarryU32: case Decoder::Opcode::S_ADD_U32:
case IR::Opcode::IAddCarryU32: { case Decoder::Opcode::S_ADDC_U32:
const auto lhs = ReadU32(inst.src[0]); case Decoder::Opcode::V_ADD_I32:
const auto rhs = ReadU32(inst.src[1]); case Decoder::Opcode::V_ADDC_U32: {
const auto carry_in = ConditionBit(inst.src[2]); const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src1);
const auto carry_in = inst.opcode == Decoder::Opcode::S_ADDC_U32
? ConditionBit(ConditionOperand(Decoder::OperandKind::Scc))
: inst.opcode == Decoder::Opcode::V_ADDC_U32
? ConditionBit(inst.src2)
: zero;
const auto add0 = ir.Emit(IR::ValueOpcode::IAddCarry32, {lhs, rhs}); const auto add0 = ir.Emit(IR::ValueOpcode::IAddCarry32, {lhs, rhs});
const auto partial = ir.CompositeExtract(add0, 0); const auto partial = ir.CompositeExtract(add0, 0);
const auto carry0 = ir.CompositeExtract(add0, 1); const auto carry0 = ir.CompositeExtract(add0, 1);
const auto add1 = ir.Emit(IR::ValueOpcode::IAddCarry32, {partial, carry_in}); const auto add1 = ir.Emit(IR::ValueOpcode::IAddCarry32, {partial, carry_in});
const auto result = ir.CompositeExtract(add1, 0); const auto result = ir.CompositeExtract(add1, 0);
const auto carry1 = ir.CompositeExtract(add1, 1); const auto carry1 = ir.CompositeExtract(add1, 1);
const auto carry = ir.INotEqual(ir.BitwiseOr(carry0, carry1), IR::U32(IR::Value(0u))); const auto carry = ir.INotEqual(ir.BitwiseOr(carry0, carry1), zero);
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
if (inst.op == IR::Opcode::ScalarAddCarryU32) { if (inst.opcode == Decoder::Opcode::S_ADD_U32 ||
inst.opcode == Decoder::Opcode::S_ADDC_U32) {
ir.SetScc(carry); ir.SetScc(carry);
} else { } else {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), carry)); WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), carry));
} }
return true; return true;
} }
case IR::Opcode::ScalarSubBorrowU32: case Decoder::Opcode::S_SUB_U32:
case IR::Opcode::ISubBorrowU32: { case Decoder::Opcode::V_SUB_I32:
const auto lhs = ReadU32(inst.src[0]); case Decoder::Opcode::V_SUBREV_I32: {
const auto rhs = ReadU32(inst.src[1]); const bool reverse = inst.opcode == Decoder::Opcode::V_SUBREV_I32;
const auto result = ir.ISub(lhs, rhs); const auto lhs = ReadU32(reverse ? inst.src1 : inst.src0);
const auto borrow = ir.UGreaterThan(rhs, lhs); const auto rhs = ReadU32(reverse ? inst.src0 : inst.src1);
WriteOperand(inst.dst, result); const auto result = ir.ISub(lhs, rhs);
if (inst.op == IR::Opcode::ScalarSubBorrowU32) { const auto borrow = ir.UGreaterThan(rhs, lhs);
WriteOperand(DestinationOperand(inst), result);
if (inst.opcode == Decoder::Opcode::S_SUB_U32) {
ir.SetScc(borrow); ir.SetScc(borrow);
} else { } else {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow)); WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
} }
return true; return true;
} }
case IR::Opcode::ScalarSubBorrowCarryU32: case Decoder::Opcode::S_SUBB_U32:
case IR::Opcode::ISubBorrowCarryU32: { case Decoder::Opcode::V_SUBREV_CO_CI_U32: {
const auto lhs = ReadU32(inst.src[0]); const bool vector = inst.opcode == Decoder::Opcode::V_SUBREV_CO_CI_U32;
const auto rhs = ReadU32(inst.src[1]); const auto lhs = ReadU32(vector ? inst.src1 : inst.src0);
const auto borrow_in = ConditionBit(inst.src[2]); const auto rhs = ReadU32(vector ? inst.src0 : inst.src1);
const auto borrow_in = vector
? ConditionBit(inst.src2)
: ConditionBit(ConditionOperand(Decoder::OperandKind::Scc));
const auto partial = ir.ISub(lhs, rhs); const auto partial = ir.ISub(lhs, rhs);
const auto result = ir.ISub(partial, borrow_in); const auto result = ir.ISub(partial, borrow_in);
const auto borrow0 = ir.UGreaterThan(rhs, lhs); const auto borrow0 = ir.UGreaterThan(rhs, lhs);
const auto borrow1 = ir.UGreaterThan(borrow_in, partial); const auto borrow1 = ir.UGreaterThan(borrow_in, partial);
WriteOperand(inst.dst, result); const auto borrow = ir.LogicalOr(borrow0, borrow1);
const auto borrow = ir.LogicalOr(borrow0, borrow1); WriteOperand(DestinationOperand(inst), result);
if (inst.op == IR::Opcode::ScalarSubBorrowCarryU32) { if (vector) {
ir.SetScc(borrow);
} else {
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow)); WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
} else {
ir.SetScc(borrow);
} }
return true; return true;
} }
case IR::Opcode::ScalarSignedAddOverflowI32: case Decoder::Opcode::S_ADD_I32:
case IR::Opcode::ScalarSignedSubOverflowI32: { case Decoder::Opcode::S_SUB_I32: {
const auto lhs = ReadU32(inst.src[0]); const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src[1]); const auto rhs = ReadU32(inst.src1);
const bool subtract = inst.op == IR::Opcode::ScalarSignedSubOverflowI32; const bool subtract = inst.opcode == Decoder::Opcode::S_SUB_I32;
const auto result = subtract ? ir.ISub(lhs, rhs) : ir.IAdd(lhs, rhs); const auto result = subtract ? ir.ISub(lhs, rhs) : ir.IAdd(lhs, rhs);
const auto shift = IR::U32(IR::Value(31u)); const auto shift = IR::U32(IR::Value(31u));
const auto lhs_sign = ir.ShiftRightLogical(lhs, shift); const auto lhs_sign = ir.ShiftRightLogical(lhs, shift);
@@ -94,116 +135,201 @@ bool Translator::TranslateStateOperation(const IR::Instruction& inst) {
const auto inputs = const auto inputs =
subtract ? ir.INotEqual(lhs_sign, rhs_sign) : ir.IEqual(lhs_sign, rhs_sign); subtract ? ir.INotEqual(lhs_sign, rhs_sign) : ir.IEqual(lhs_sign, rhs_sign);
const auto changed = ir.INotEqual(lhs_sign, out_sign); const auto changed = ir.INotEqual(lhs_sign, out_sign);
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
ir.SetScc(ir.LogicalAnd(inputs, changed)); ir.SetScc(ir.LogicalAnd(inputs, changed));
return true; return true;
} }
case IR::Opcode::ScalarShiftLeftAddCarryU32: { case Decoder::Opcode::S_LSHL1_ADD_U32:
const auto lhs = ReadU32(inst.src[0]); case Decoder::Opcode::S_LSHL2_ADD_U32:
const auto shift = ReadU32(inst.src[1]); case Decoder::Opcode::S_LSHL3_ADD_U32:
const auto rhs = ReadU32(inst.src[2]); case Decoder::Opcode::S_LSHL4_ADD_U32: {
const auto shifted = ir.ShiftLeftLogical(lhs, shift); const uint32_t shift_amount = 1u + static_cast<uint32_t>(inst.opcode) -
const auto result = ir.IAdd(shifted, rhs); static_cast<uint32_t>(Decoder::Opcode::S_LSHL1_ADD_U32);
const auto add_carry = ir.ULessThan(result, shifted); const auto lhs = ReadU32(inst.src0);
const auto inverse_shift = ir.ISub(IR::U32(IR::Value(32u)), shift); const auto shift = IR::U32(IR::Value(shift_amount));
const auto shifted_out = ir.ShiftRightLogical(lhs, inverse_shift); const auto rhs = ReadU32(inst.src1);
const auto shift_carry = ir.INotEqual(shifted_out, zero); const auto shifted = ir.ShiftLeftLogical(lhs, shift);
WriteOperand(inst.dst, result); const auto result = ir.IAdd(shifted, rhs);
const auto add_carry = ir.ULessThan(result, shifted);
const auto inverse_shift = ir.ISub(IR::U32(IR::Value(32u)), shift);
const auto shifted_out = ir.ShiftRightLogical(lhs, inverse_shift);
const auto shift_carry = ir.INotEqual(shifted_out, zero);
WriteOperand(DestinationOperand(inst), result);
ir.SetScc(ir.LogicalOr(add_carry, shift_carry)); ir.SetScc(ir.LogicalOr(add_carry, shift_carry));
return true; return true;
} }
default: return false; case Decoder::Opcode::S_MIN_I32:
} case Decoder::Opcode::S_MAX_I32:
} case Decoder::Opcode::S_MIN_U32:
case Decoder::Opcode::S_MAX_U32: {
bool Translator::TranslateControlOperation(const IR::Instruction& inst) { const auto lhs = ReadU32(inst.src0);
switch (inst.op) { const auto rhs = ReadU32(inst.src1);
case IR::Opcode::ControlNop: ir.Emit(IR::ValueOpcode::ControlNop); return true; IR::ValueOpcode value_opcode;
case IR::Opcode::Waitcnt: ir.Emit(IR::ValueOpcode::Waitcnt); return true; IR::ValueOpcode compare_opcode;
case IR::Opcode::Barrier: ir.Emit(IR::ValueOpcode::Barrier); return true; switch (inst.opcode) {
case IR::Opcode::Sendmsg: ir.Emit(IR::ValueOpcode::Sendmsg); return true; case Decoder::Opcode::S_MIN_I32:
case IR::Opcode::TtraceData: ir.Emit(IR::ValueOpcode::TtraceData); return true; value_opcode = IR::ValueOpcode::SMin32;
case IR::Opcode::InstPrefetch: ir.Emit(IR::ValueOpcode::InstPrefetch); return true; compare_opcode = IR::ValueOpcode::SLessThan32;
default: return false; break;
} case Decoder::Opcode::S_MAX_I32:
} value_opcode = IR::ValueOpcode::SMax32;
compare_opcode = IR::ValueOpcode::SGreaterThan32;
bool Translator::TranslateMove(const IR::Instruction& inst) { break;
switch (inst.op) { case Decoder::Opcode::S_MIN_U32:
case IR::Opcode::MoveU32: value_opcode = IR::ValueOpcode::UMin32;
if (inst.src[0].kind == IR::OperandKind::Register && compare_opcode = IR::ValueOpcode::ULessThan32;
(inst.src[0].reg.file == IR::RegisterFile::Exec || break;
inst.src[0].reg.file == IR::RegisterFile::Vcc) && case Decoder::Opcode::S_MAX_U32:
inst.dst.kind == IR::OperandKind::Register && value_opcode = IR::ValueOpcode::UMax32;
(inst.dst.reg.file == IR::RegisterFile::Exec || compare_opcode = IR::ValueOpcode::UGreaterThan32;
inst.dst.reg.file == IR::RegisterFile::Vcc)) { break;
WriteMask(inst.dst, ReadMask(inst.src[0])); default: EXIT("invalid scalar min/max opcode");
} else {
WriteOperand(inst.dst, ReadOperand(inst.src[0], IR::Type::U32));
} }
WriteOperand(DestinationOperand(inst), ir.Emit(value_opcode, {lhs, rhs}));
ir.SetScc(IR::U1(ir.Emit(compare_opcode, {lhs, rhs})));
return true; return true;
case IR::Opcode::MoveF32Bits: }
WriteOperand(inst.dst, ReadOperand(inst.src[0], IR::Type::F32)); default: return false;
return true; }
case IR::Opcode::MoveU64: { }
const auto is_exec_or_vcc = [](const IR::Operand& operand) {
return operand.kind == IR::OperandKind::Register && bool Translator::TranslateControlOperation(const Decoder::Instruction& inst) {
(operand.reg.file == IR::RegisterFile::Exec || switch (inst.opcode) {
operand.reg.file == IR::RegisterFile::Vcc); case Decoder::Opcode::V_NOP: return true;
}; case Decoder::Opcode::S_NOP:
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src[0])) { case Decoder::Opcode::S_SETREG_B32:
WriteMask64(inst.dst, ReadMask(inst.src[0])); case Decoder::Opcode::S_SLEEP:
case Decoder::Opcode::S_TRAP: ir.Emit(IR::ValueOpcode::ControlNop); return true;
case Decoder::Opcode::S_WAITCNT:
case Decoder::Opcode::S_WAITCNT_DEPCTR: ir.Emit(IR::ValueOpcode::Waitcnt); return true;
case Decoder::Opcode::S_BARRIER: ir.Emit(IR::ValueOpcode::Barrier); return true;
case Decoder::Opcode::S_SENDMSG: ir.Emit(IR::ValueOpcode::Sendmsg); return true;
case Decoder::Opcode::S_TTRACEDATA: ir.Emit(IR::ValueOpcode::TtraceData); return true;
case Decoder::Opcode::S_INST_PREFETCH: ir.Emit(IR::ValueOpcode::InstPrefetch); return true;
default: return false;
}
}
bool Translator::TranslateMove(const Decoder::Instruction& inst, std::string* error) {
switch (inst.opcode) {
case Decoder::Opcode::S_GETPC_B64: {
const auto base = IR::U64(ir.Emit(IR::ValueOpcode::GetShaderBase));
const auto address = IR::U64(ir.Emit(
IR::ValueOpcode::IAdd64, {base, IR::Value(static_cast<uint64_t>(inst.pc) + 4u)}));
if (inst.dst.kind == Decoder::OperandKind::Null) {
return true; return true;
} }
const bool scalar_copy = inst.dst.kind == IR::OperandKind::Register && auto high = PlainOperand(inst.dst);
inst.src[0].kind == IR::OperandKind::Register && switch (inst.dst.kind) {
inst.dst.reg.file == IR::RegisterFile::Scalar && case Decoder::OperandKind::Sgpr:
inst.src[0].reg.file == IR::RegisterFile::Scalar; if (inst.dst.reg < 105u) {
IR::U1 source_mask; high.reg++;
IR::U1 source_mask_valid; } else if (inst.dst.reg == 105u) {
if (scalar_copy) { high.kind = Decoder::OperandKind::VccLo;
const auto src = static_cast<IR::ScalarReg>(inst.src[0].reg.index); high.reg = 0u;
source_mask = ir.GetThreadBitScalarReg(src); } else {
source_mask_valid = ir.GetScalarMaskTag(src); if (error != nullptr) {
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
}
return false;
}
break;
case Decoder::OperandKind::VccLo: high.kind = Decoder::OperandKind::VccHi; break;
case Decoder::OperandKind::M0: high.kind = Decoder::OperandKind::Null; break;
case Decoder::OperandKind::ExecLo: high.kind = Decoder::OperandKind::ExecHi; break;
default:
if (error != nullptr) {
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
}
return false;
} }
WriteU32Pair(inst.dst, ReadU32Pair(inst.src[0])); const auto words = ExtractU64(address);
if (scalar_copy) { WriteOperand(inst.dst, words[0]);
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg.index), WriteOperand(high, words[1]);
source_mask); return true;
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg.index), }
source_mask_valid); case Decoder::Opcode::S_CSELECT_B32: {
const auto result = ir.Select(ir.GetScc(), ReadU32(inst.src0), ReadU32(inst.src1));
WriteOperand(DestinationOperand(inst), result);
return true;
}
case Decoder::Opcode::S_CSELECT_B64: {
const auto condition = ir.GetScc();
const auto lhs = ReadU32Pair(inst.src0);
const auto rhs = ReadU32Pair(inst.src1);
const auto selected_mask = IR::U1(ir.Emit(
IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(inst.src1)}));
const auto selected_mask_valid =
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
{condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)}));
if (IsExecOrVcc(inst.dst)) {
WriteMask64(inst.dst, selected_mask);
return true;
}
WriteU32Pair(inst.dst, {ir.Select(condition, lhs[0], rhs[0]),
ir.Select(condition, lhs[1], rhs[1])});
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, selected_mask);
ir.SetScalarMaskTag(dst, selected_mask_valid);
} }
return true; return true;
} }
case IR::Opcode::WqmB64: { case Decoder::Opcode::S_MOV_B32:
const auto is_exec_or_vcc = [](const IR::Operand& operand) { case Decoder::Opcode::S_MOVK_I32:
return operand.kind == IR::OperandKind::Register && case Decoder::Opcode::V_MOV_B32:
(operand.reg.file == IR::RegisterFile::Exec || if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
operand.reg.file == IR::RegisterFile::Vcc); WriteMask(inst.dst, ReadMask(inst.src0));
}; } else if (inst.opcode == Decoder::Opcode::V_MOV_B32 &&
if (!is_exec_or_vcc(inst.dst) && !is_exec_or_vcc(inst.src[0])) { (inst.src0.negate || inst.src0.absolute)) {
const auto mask_valid = ReadMaskValid(inst.src[0]); WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::F32));
} else {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::U32));
}
return true;
case Decoder::Opcode::S_MOV_B64: {
if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) {
WriteMask64(inst.dst, ReadMask(inst.src0));
return true;
}
const bool scalar_copy = inst.dst.kind == Decoder::OperandKind::Sgpr &&
inst.src0.kind == Decoder::OperandKind::Sgpr;
IR::U1 source_mask;
IR::U1 source_mask_valid;
if (scalar_copy) {
source_mask = ir.GetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.src0.reg));
source_mask_valid = ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(inst.src0.reg));
}
WriteU32Pair(inst.dst, ReadU32Pair(inst.src0));
if (scalar_copy) {
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask);
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask_valid);
}
return true;
}
case Decoder::Opcode::S_WQM_B64: {
if (!IsExecOrVcc(inst.dst) && !IsExecOrVcc(inst.src0)) {
const auto mask_valid = ReadMaskValid(inst.src0);
const auto invocation_result = const auto invocation_result =
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src[0])})); IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
const auto result = IR::U64( const auto result = IR::U64(
ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src[0], IR::Type::U64)})); ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
if (inst.dst.kind == IR::OperandKind::Register && if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
inst.dst.reg.file == IR::RegisterFile::Scalar) { const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg.index);
ir.SetThreadBitScalarReg(dst, invocation_result); ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid); ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero = IR::U1( const auto raw_nonzero = IR::U1(
ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})); ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}));
ir.SetScc(IR::U1(ir.Emit( ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero}))); {mask_valid, invocation_result, raw_nonzero})));
} else { } else {
ir.SetScc(IR::U1( ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}))); ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
} }
return true; return true;
} }
const auto result = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src[0])})); const auto result = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
WriteMask64(inst.dst, result); WriteMask64(inst.dst, result);
ir.SetScc(result); ir.SetScc(result);
return true; return true;
@@ -212,29 +338,56 @@ bool Translator::TranslateMove(const IR::Instruction& inst) {
} }
} }
bool Translator::TranslateLaneOperation(const IR::Instruction& inst) { bool Translator::TranslateLaneOperation(const Decoder::Instruction& inst, std::string* error) {
const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u)); const auto lane_mask = IR::U32(IR::Value(current_wave_size == 32u ? 31u : 63u));
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::MoveRelSourceU32: { case Decoder::Opcode::V_MOVRELS_B32: {
EXIT_IF(inst.src[0].kind != IR::OperandKind::Register || if (inst.dst.kind != Decoder::OperandKind::Vgpr ||
inst.src[0].reg.file != IR::RegisterFile::Vector); inst.src0.kind != Decoder::OperandKind::Vgpr) {
const auto base = inst.src[0].reg.index; if (error != nullptr) {
const auto m0 = ir.BitwiseAnd(ReadU32(inst.src[1]), IR::U32(IR::Value(0xffu))); *error = "V_MOVRELS_B32 requires VGPR source and destination";
}
return false;
}
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate ||
inst.src0.absolute || inst.src0.dpp) {
if (error != nullptr) {
*error = "V_MOVRELS_B32 modifiers are not implemented";
}
return false;
}
const auto base = inst.src0.reg;
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
IR::U32(IR::Value(0xffu)));
auto selected = ir.GetVectorReg(static_cast<IR::VectorReg>(base)); auto selected = ir.GetVectorReg(static_cast<IR::VectorReg>(base));
for (uint32_t index = base + 1u; index < current_vector_limit; index++) { for (uint32_t index = base + 1u; index < current_vector_limit; index++) {
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base))); const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
selected = selected =
ir.Select(match, ir.GetVectorReg(static_cast<IR::VectorReg>(index)), selected); ir.Select(match, ir.GetVectorReg(static_cast<IR::VectorReg>(index)), selected);
} }
WriteOperand(inst.dst, selected); WriteOperand(DestinationOperand(inst), selected);
return true; return true;
} }
case IR::Opcode::MoveRelDestU32: { case Decoder::Opcode::V_MOVRELD_B32: {
EXIT_IF(inst.dst.kind != IR::OperandKind::Register || if (inst.dst.kind != Decoder::OperandKind::Vgpr) {
inst.dst.reg.file != IR::RegisterFile::Vector); if (error != nullptr) {
const auto base = inst.dst.reg.index; *error = "V_MOVRELD_B32 requires VGPR destination";
const auto value = ReadU32(inst.src[0]); }
const auto m0 = ir.BitwiseAnd(ReadU32(inst.src[1]), IR::U32(IR::Value(0xffu))); return false;
}
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate ||
inst.src0.absolute || inst.src0.dpp) {
if (error != nullptr) {
*error = "V_MOVRELD_B32 modifiers are not implemented";
}
return false;
}
const auto base = inst.dst.reg;
const auto value = ReadU32(inst.src0);
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
IR::U32(IR::Value(0xffu)));
for (uint32_t index = base; index < current_vector_limit; index++) { for (uint32_t index = base; index < current_vector_limit; index++) {
const auto reg = static_cast<IR::VectorReg>(index); const auto reg = static_cast<IR::VectorReg>(index);
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base))); const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
@@ -243,41 +396,38 @@ bool Translator::TranslateLaneOperation(const IR::Instruction& inst) {
} }
return true; return true;
} }
case IR::Opcode::ReadFirstLaneU32: { case Decoder::Opcode::V_READFIRSTLANE_B32: {
const auto result = const auto result =
ir.Emit(IR::ValueOpcode::ReadFirstLane, {ReadU32(inst.src[0]), ir.GetExec()}); ir.Emit(IR::ValueOpcode::ReadFirstLane, {ReadU32(inst.src0), ir.GetExec()});
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
return true; return true;
} }
case IR::Opcode::ReadLaneU32: { case Decoder::Opcode::V_READLANE_B32: {
const auto lane = ir.BitwiseAnd(ReadU32(inst.src[1]), lane_mask); const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
WriteOperand(inst.dst, WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src[0]), lane})); ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src0), lane}));
return true; return true;
} }
case IR::Opcode::WriteLaneU32: { case Decoder::Opcode::V_WRITELANE_B32: {
EXIT_IF(inst.dst.kind != IR::OperandKind::Register || EXIT_IF(inst.dst.kind != Decoder::OperandKind::Vgpr);
inst.dst.reg.file != IR::RegisterFile::Vector); const auto reg = static_cast<IR::VectorReg>(inst.dst.reg);
const auto reg = static_cast<IR::VectorReg>(inst.dst.reg.index); const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
const auto lane = ir.BitwiseAnd(ReadU32(inst.src[1]), lane_mask); const auto result = IR::U32(ir.Emit(IR::ValueOpcode::WriteLane,
const auto result = IR::U32(ir.Emit( {ir.GetVectorReg(reg), ReadU32(inst.src0), lane}));
IR::ValueOpcode::WriteLane, {ir.GetVectorReg(reg), ReadU32(inst.src[0]), lane}));
// V_WRITELANE_B32 ignores EXEC; bypass the ordinary VGPR destination path.
ir.SetVectorReg(reg, result); ir.SetVectorReg(reg, result);
return true; return true;
} }
case IR::Opcode::Permlane16B32: case Decoder::Opcode::V_PERMLANE16_B32:
case IR::Opcode::Permlanex16B32: { case Decoder::Opcode::V_PERMLANEX16_B32: {
const IR::PermlaneFlags flags { const IR::PermlaneFlags flags {
.x16 = inst.op == IR::Opcode::Permlanex16B32, .x16 = inst.opcode == Decoder::Opcode::V_PERMLANEX16_B32,
.fetch_inactive = inst.dst.op_sel, .fetch_inactive = inst.dst.op_sel,
.bound_control = inst.dst.op_sel_hi, .bound_control = inst.dst.op_sel_hi,
}; };
const auto result = ir.Emit( const auto result = ir.Emit(
IR::ValueOpcode::Permlane16U32, IR::ValueOpcode::Permlane16U32,
{ReadU32(inst.src[0]), ReadU32(inst.src[1]), ReadU32(inst.src[2]), ir.GetExec()}, {ReadU32(inst.src0), ReadU32(inst.src1), ReadU32(inst.src2), ir.GetExec()}, flags);
flags); auto dst = DestinationOperand(inst);
auto dst = inst.dst;
dst.op_sel = false; dst.op_sel = false;
dst.op_sel_hi = false; dst.op_sel_hi = false;
WriteOperand(dst, result); WriteOperand(dst, result);
@@ -287,4 +437,4 @@ bool Translator::TranslateLaneOperation(const IR::Instruction& inst) {
} }
} }
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail } // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h" #include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
IR::F32 Translator::SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value) { IR::F32 Translator::SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value) {
return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value})); return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value}));
@@ -38,11 +38,11 @@ IR::U32 Translator::ConvertF32ToI32Saturated(IR::F32 value, float lower_bound, f
return ir.Select(nan, IR::U32(IR::Value(0u)), clamped); return ir.Select(nan, IR::U32(IR::Value(0u)), clamped);
} }
bool Translator::TranslateConversion(const IR::Instruction& inst) { bool Translator::TranslateConversion(const Decoder::Instruction& inst) {
const auto f32 = [&](uint32_t index) { const auto f32 = [&](uint32_t index) {
return IR::F32(ReadOperand(inst.src[index], IR::Type::F32)); return IR::F32(ReadOperand(SourceAt(inst, index), IR::Type::F32));
}; };
const auto u32 = [&](uint32_t index) { return ReadU32(inst.src[index]); }; const auto u32 = [&](uint32_t index) { return ReadU32(SourceAt(inst, index)); };
const auto convert_u32 = [&](IR::F32 value) { const auto convert_u32 = [&](IR::F32 value) {
return ConvertF32ToU32Saturated(value, 4294967296.0f, 4294967040.0f, 0xffffffffu); return ConvertF32ToU32Saturated(value, 4294967296.0f, 4294967040.0f, 0xffffffffu);
}; };
@@ -51,65 +51,77 @@ bool Translator::TranslateConversion(const IR::Instruction& inst) {
0x80000000u, 0x7fffffffu); 0x80000000u, 0x7fffffffu);
}; };
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::ConvertByteU32ToF32: { case Decoder::Opcode::V_CVT_F32_UBYTE0:
const auto index = case Decoder::Opcode::V_CVT_F32_UBYTE1:
inst.src[1].kind == IR::OperandKind::ImmediateU32 ? inst.src[1].imm & 3u : 0u; case Decoder::Opcode::V_CVT_F32_UBYTE2:
case Decoder::Opcode::V_CVT_F32_UBYTE3: {
const auto index = static_cast<uint32_t>(inst.opcode) -
static_cast<uint32_t>(Decoder::Opcode::V_CVT_F32_UBYTE0);
const auto byte = const auto byte =
ir.BitwiseAnd(ir.ShiftRightLogical(u32(0), IR::U32(IR::Value(index * 8u))), ir.BitwiseAnd(ir.ShiftRightLogical(u32(0), IR::U32(IR::Value(index * 8u))),
IR::U32(IR::Value(0xffu))); IR::U32(IR::Value(0xffu)));
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::ConvertF32U32, {byte})); WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::ConvertF32U32, {byte}));
return true; return true;
} }
case IR::Opcode::ConvertU32ToF32: case Decoder::Opcode::V_CVT_F32_U32:
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::ConvertF32U32, {u32(0)})); WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ConvertF32U32, {u32(0)}));
return true; return true;
case IR::Opcode::ConvertI32ToF32: case Decoder::Opcode::V_CVT_F32_I32:
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::ConvertF32S32, {u32(0)})); WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::ConvertF32S32, {u32(0)}));
return true; return true;
case IR::Opcode::ConvertF32ToU32: WriteOperand(inst.dst, convert_u32(f32(0))); return true; case Decoder::Opcode::V_CVT_U32_F32:
case IR::Opcode::ConvertF32ToI32: WriteOperand(inst.dst, convert_i32(f32(0))); return true; WriteOperand(DestinationOperand(inst), convert_u32(f32(0)));
case IR::Opcode::ConvertF32ToF16: WriteF16(inst.dst, f32(0)); return true;
case IR::Opcode::ConvertF16ToF32:
WriteOperand(inst.dst, ReadF16AsF32(inst.src[0]));
return true; return true;
case IR::Opcode::ConvertU16ToF16: { case Decoder::Opcode::V_CVT_I32_F32:
const auto value = IR::F32( WriteOperand(DestinationOperand(inst), convert_i32(f32(0)));
ir.Emit(IR::ValueOpcode::ConvertF32U32, {ReadU16AsU32(inst.src[0], false)}));
WriteF16(inst.dst, value);
return true; return true;
} case Decoder::Opcode::V_CVT_F16_F32:
case IR::Opcode::ConvertI16ToF16: { WriteF16(DestinationOperand(inst), f32(0));
return true;
case Decoder::Opcode::V_CVT_F32_F16:
WriteOperand(DestinationOperand(inst), ReadF16AsF32(inst.src0));
return true;
case Decoder::Opcode::V_CVT_F16_U16: {
const auto value = const auto value =
IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {ReadU16AsU32(inst.src[0], true)})); IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32U32, {ReadU16AsU32(inst.src0, false)}));
WriteF16(inst.dst, value); WriteF16(DestinationOperand(inst), value);
return true; return true;
} }
case IR::Opcode::ConvertF16ToU16: { case Decoder::Opcode::V_CVT_F16_I16: {
const auto value =
IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {ReadU16AsU32(inst.src0, true)}));
WriteF16(DestinationOperand(inst), value);
return true;
}
case Decoder::Opcode::V_CVT_U16_F16: {
const auto converted = const auto converted =
ConvertF32ToU32Saturated(ReadF16AsF32(inst.src[0]), 65536.0f, 65535.0f, 0xffffu); ConvertF32ToU32Saturated(ReadF16AsF32(inst.src0), 65536.0f, 65535.0f, 0xffffu);
WriteU16(inst.dst, converted); WriteU16(DestinationOperand(inst), converted);
return true; return true;
} }
case IR::Opcode::ConvertF16ToI16: { case Decoder::Opcode::V_CVT_I16_F16: {
const auto converted = ConvertF32ToI32Saturated( const auto converted = ConvertF32ToI32Saturated(
ReadF16AsF32(inst.src[0]), -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu); ReadF16AsF32(inst.src0), -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu);
WriteU16(inst.dst, ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu)))); WriteU16(DestinationOperand(inst),
ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
return true; return true;
} }
case IR::Opcode::ConvertRoundPlusInfF32ToI32: { case Decoder::Opcode::V_CVT_RPI_I32_F32: {
const auto biased = const auto biased =
IR::F32(ir.Emit(IR::ValueOpcode::FPAdd32, {f32(0), IR::Value::F32(0.5f)})); IR::F32(ir.Emit(IR::ValueOpcode::FPAdd32, {f32(0), IR::Value::F32(0.5f)}));
const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {biased})); const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {biased}));
WriteOperand(inst.dst, convert_i32(rounded)); WriteOperand(DestinationOperand(inst), convert_i32(rounded));
return true; return true;
} }
case IR::Opcode::ConvertFloorF32ToI32: { case Decoder::Opcode::V_CVT_FLR_I32_F32: {
const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {f32(0)})); const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {f32(0)}));
WriteOperand(inst.dst, convert_i32(rounded)); WriteOperand(DestinationOperand(inst), convert_i32(rounded));
return true; return true;
} }
case IR::Opcode::FrexpExpI32F32: { case Decoder::Opcode::V_FREXP_EXP_I32_F32: {
const auto bits = ir.BitCastU32(f32(0)); const auto bits = ir.BitCastU32(f32(0));
const auto exponent = IR::U32( const auto exponent = IR::U32(
ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(23u), IR::Value(8u)})); ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(23u), IR::Value(8u)}));
@@ -123,43 +135,45 @@ bool Translator::TranslateConversion(const IR::Instruction& inst) {
ir.INotEqual(exponent, IR::U32(IR::Value(0xffu))), ir.INotEqual(exponent, IR::U32(IR::Value(0xffu))),
ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal, denormal), ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal, denormal),
IR::U32(IR::Value(0u))); IR::U32(IR::Value(0u)));
WriteOperand(inst.dst, finite); WriteOperand(DestinationOperand(inst), finite);
return true; return true;
} }
case IR::Opcode::ConvertI4ToOffsetF32: { case Decoder::Opcode::V_CVT_OFF_F32_I4: {
const auto nibble = IR::U32( const auto nibble = IR::U32(
ir.Emit(IR::ValueOpcode::BitFieldSExtract, {u32(0), IR::Value(0u), IR::Value(4u)})); ir.Emit(IR::ValueOpcode::BitFieldSExtract, {u32(0), IR::Value(0u), IR::Value(4u)}));
const auto value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {nibble})); const auto value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {nibble}));
WriteOperand(inst.dst, WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::FPMul32, {value, IR::Value::F32(1.0f / 16.0f)})); ir.Emit(IR::ValueOpcode::FPMul32, {value, IR::Value::F32(1.0f / 16.0f)}));
return true; return true;
} }
case IR::Opcode::PackF32ToF16Rtz: case Decoder::Opcode::V_CVT_PKRTZ_F16_F32:
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {f32(0), f32(1)})); WriteOperand(DestinationOperand(inst),
ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {f32(0), f32(1)}));
return true; return true;
case IR::Opcode::PackSnorm2x16F32: case Decoder::Opcode::V_CVT_PKNORM_I16_F32:
case IR::Opcode::PackUnorm2x16F32: { case Decoder::Opcode::V_CVT_PKNORM_U16_F32: {
const auto pair = ir.Emit(IR::ValueOpcode::CompositeConstructF32x2, {f32(0), f32(1)}); const auto pair = ir.Emit(IR::ValueOpcode::CompositeConstructF32x2, {f32(0), f32(1)});
const auto opcode = inst.op == IR::Opcode::PackSnorm2x16F32 const auto opcode = inst.opcode == Decoder::Opcode::V_CVT_PKNORM_I16_F32
? IR::ValueOpcode::PackSnorm2x16 ? IR::ValueOpcode::PackSnorm2x16
: IR::ValueOpcode::PackUnorm2x16; : IR::ValueOpcode::PackUnorm2x16;
WriteOperand(inst.dst, ir.Emit(opcode, {pair})); WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {pair}));
return true; return true;
} }
case IR::Opcode::PackU8F32: { case Decoder::Opcode::V_CVT_PK_U8_F32: {
const auto byte = ConvertF32ToU32Saturated(f32(0), 255.0f, 255.0f, 255u); const auto byte = ConvertF32ToU32Saturated(f32(0), 255.0f, 255.0f, 255u);
const auto index = ir.BitwiseAnd(u32(1), IR::U32(IR::Value(3u))); const auto index = ir.BitwiseAnd(u32(1), IR::U32(IR::Value(3u)));
const auto shift = ir.ShiftLeftLogical(index, IR::U32(IR::Value(3u))); const auto shift = ir.ShiftLeftLogical(index, IR::U32(IR::Value(3u)));
const auto mask = ir.ShiftLeftLogical(IR::U32(IR::Value(0xffu)), shift); const auto mask = ir.ShiftLeftLogical(IR::U32(IR::Value(0xffu)), shift);
const auto base = ir.BitwiseAnd(u32(2), ir.BitwiseNot(mask)); const auto base = ir.BitwiseAnd(u32(2), ir.BitwiseNot(mask));
WriteOperand(inst.dst, ir.BitwiseOr(base, ir.ShiftLeftLogical(byte, shift))); WriteOperand(DestinationOperand(inst),
ir.BitwiseOr(base, ir.ShiftLeftLogical(byte, shift)));
return true; return true;
} }
case IR::Opcode::PackB32F16: { case Decoder::Opcode::V_PACK_B32_F16: {
const auto low = ReadF16LaneBits(inst.src[0], false); const auto low = ReadF16LaneBits(inst.src0, false);
const auto high = const auto high =
ir.ShiftLeftLogical(ReadF16LaneBits(inst.src[1], false), IR::U32(IR::Value(16u))); ir.ShiftLeftLogical(ReadF16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
WriteOperand(inst.dst, ir.BitwiseOr(low, high)); WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low, high));
return true; return true;
} }
default: return false; default: return false;
@@ -172,4 +186,4 @@ IR::U32 Translator::PackU16Lanes(IR::U32 low, IR::U32 high) {
ir.ShiftLeftLogical(ir.BitwiseAnd(high, mask), IR::U32(IR::Value(16u)))); ir.ShiftLeftLogical(ir.BitwiseAnd(high, mask), IR::U32(IR::Value(16u))));
} }
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail } // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -1,57 +1,73 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h" #include "graphics/shader/recompiler/frontend/translate/Translator.h"
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslateU64MaskOperation(const IR::Instruction& inst) { bool Translator::TranslateU64MaskOperation(const Decoder::Instruction& inst) {
switch (inst.opcode) {
case Decoder::Opcode::S_AND_B64:
case Decoder::Opcode::S_ANDN2_B64:
case Decoder::Opcode::S_OR_B64:
case Decoder::Opcode::S_ORN2_B64:
case Decoder::Opcode::S_XOR_B64:
case Decoder::Opcode::S_NAND_B64:
case Decoder::Opcode::S_NOR_B64:
case Decoder::Opcode::S_XNOR_B64:
case Decoder::Opcode::S_NOT_B64: break;
default: return false;
}
const auto invocation_result = EvaluateU64Mask(inst); const auto invocation_result = EvaluateU64Mask(inst);
const auto is_exec_or_vcc = [](const IR::Operand& operand) { const auto is_exec_or_vcc = [](const Decoder::Operand& operand) {
return operand.kind == IR::OperandKind::Register && switch (operand.kind) {
(operand.reg.file == IR::RegisterFile::Exec || case Decoder::OperandKind::ExecLo:
operand.reg.file == IR::RegisterFile::Vcc); case Decoder::OperandKind::ExecHi:
case Decoder::OperandKind::VccLo:
case Decoder::OperandKind::VccHi: return true;
default: return false;
}
}; };
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src[0]) || if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src0) ||
(inst.src_count > 1u && is_exec_or_vcc(inst.src[1]))) { (inst.src_count > 1u && is_exec_or_vcc(inst.src1))) {
WriteMask64(inst.dst, invocation_result); WriteMask64(inst.dst, invocation_result);
ir.SetScc(invocation_result); ir.SetScc(invocation_result);
return true; return true;
} }
const auto lhs = ReadU32Pair(inst.src[0]); const auto lhs = ReadU32Pair(inst.src0);
auto mask_valid = ReadMaskValid(inst.src[0]); auto mask_valid = ReadMaskValid(inst.src0);
if (inst.src_count > 1u) { if (inst.src_count > 1u) {
mask_valid = ir.LogicalAnd(mask_valid, ReadMaskValid(inst.src[1])); mask_valid = ir.LogicalAnd(mask_valid, ReadMaskValid(inst.src1));
} }
std::array<IR::U32, 2> result; std::array<IR::U32, 2> result;
if (inst.op == IR::Opcode::BitwiseNotU64) { if (inst.opcode == Decoder::Opcode::S_NOT_B64) {
result = {ir.BitwiseNot(lhs[0]), ir.BitwiseNot(lhs[1])}; result = {ir.BitwiseNot(lhs[0]), ir.BitwiseNot(lhs[1])};
} else { } else {
const auto rhs = ReadU32Pair(inst.src[1]); const auto rhs = ReadU32Pair(inst.src1);
for (uint32_t component = 0; component < 2u; component++) { for (uint32_t component = 0; component < 2u; component++) {
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::BitwiseAndU64: case Decoder::Opcode::S_AND_B64:
result[component] = ir.BitwiseAnd(lhs[component], rhs[component]); result[component] = ir.BitwiseAnd(lhs[component], rhs[component]);
break; break;
case IR::Opcode::BitwiseAndNotU64: case Decoder::Opcode::S_ANDN2_B64:
result[component] = result[component] =
ir.BitwiseAnd(lhs[component], ir.BitwiseNot(rhs[component])); ir.BitwiseAnd(lhs[component], ir.BitwiseNot(rhs[component]));
break; break;
case IR::Opcode::BitwiseOrU64: case Decoder::Opcode::S_OR_B64:
result[component] = ir.BitwiseOr(lhs[component], rhs[component]); result[component] = ir.BitwiseOr(lhs[component], rhs[component]);
break; break;
case IR::Opcode::BitwiseOrNotU64: case Decoder::Opcode::S_ORN2_B64:
result[component] = ir.BitwiseOr(lhs[component], ir.BitwiseNot(rhs[component])); result[component] = ir.BitwiseOr(lhs[component], ir.BitwiseNot(rhs[component]));
break; break;
case IR::Opcode::BitwiseXorU64: case Decoder::Opcode::S_XOR_B64:
result[component] = ir.BitwiseXor(lhs[component], rhs[component]); result[component] = ir.BitwiseXor(lhs[component], rhs[component]);
break; break;
case IR::Opcode::BitwiseNandU64: case Decoder::Opcode::S_NAND_B64:
result[component] = result[component] =
ir.BitwiseNot(ir.BitwiseAnd(lhs[component], rhs[component])); ir.BitwiseNot(ir.BitwiseAnd(lhs[component], rhs[component]));
break; break;
case IR::Opcode::BitwiseNorU64: case Decoder::Opcode::S_NOR_B64:
result[component] = ir.BitwiseNot(ir.BitwiseOr(lhs[component], rhs[component])); result[component] = ir.BitwiseNot(ir.BitwiseOr(lhs[component], rhs[component]));
break; break;
case IR::Opcode::BitwiseXnorU64: case Decoder::Opcode::S_XNOR_B64:
result[component] = result[component] =
ir.BitwiseNot(ir.BitwiseXor(lhs[component], rhs[component])); ir.BitwiseNot(ir.BitwiseXor(lhs[component], rhs[component]));
break; break;
@@ -60,9 +76,8 @@ bool Translator::TranslateU64MaskOperation(const IR::Instruction& inst) {
} }
} }
WriteU32Pair(inst.dst, result); WriteU32Pair(inst.dst, result);
if (inst.dst.kind == IR::OperandKind::Register && if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
inst.dst.reg.file == IR::RegisterFile::Scalar) { const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg.index);
ir.SetThreadBitScalarReg(dst, invocation_result); ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid); ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero = const auto raw_nonzero =
@@ -75,29 +90,68 @@ bool Translator::TranslateU64MaskOperation(const IR::Instruction& inst) {
return true; return true;
} }
bool Translator::TranslateInstruction(const IR::Instruction& inst) { bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::string* error) {
switch (IR::GetOpcodeInfo(inst.op).lowering_class) { current_opcode = inst.opcode;
case IR::LoweringClass::Control: return TranslateControlOperation(inst); current_pc = inst.pc;
case IR::LoweringClass::Move: return TranslateMove(inst);
case IR::LoweringClass::Lane: return TranslateLaneOperation(inst); switch (inst.opcode) {
case IR::LoweringClass::State: return TranslateStateOperation(inst); case Decoder::Opcode::UNKNOWN:
case IR::LoweringClass::Memory: return TranslateMemoryOperation(inst); case Decoder::Opcode::COUNT:
case IR::LoweringClass::Attribute: return TranslateAttributeOperation(inst); if (error != nullptr) {
case IR::LoweringClass::IntegerCompare: return TranslateIntegerCompare(inst); *error = "decoded opcode has no IR translation";
case IR::LoweringClass::Integer16Compare: return TranslateInteger16Compare(inst); }
case IR::LoweringClass::FloatCompare: return TranslateFloatCompare(inst); return false;
case IR::LoweringClass::Conversion: return TranslateConversion(inst); case Decoder::Opcode::UNSUPPORTED:
case IR::LoweringClass::Integer16: return TranslateInteger16Operation(inst); if (error != nullptr) {
case IR::LoweringClass::PackedInteger16: return TranslatePackedInteger16(inst); *error = "unsupported decoded instruction: " + Decoder::InstructionToString(inst);
case IR::LoweringClass::PackedFloat16: return TranslatePackedFloat16(inst); }
case IR::LoweringClass::Float16: return TranslateFloat16Operation(inst); return false;
case IR::LoweringClass::Float: return TranslateFloatOperation(inst); case Decoder::Opcode::S_GETPC_B64: return TranslateMove(inst, error);
case IR::LoweringClass::U64Mask: return TranslateU64MaskOperation(inst); case Decoder::Opcode::V_MOVRELS_B32:
case IR::LoweringClass::SimpleInteger: return TranslateSimpleInteger(inst); case Decoder::Opcode::V_MOVRELD_B32: return TranslateLaneOperation(inst, error);
case IR::LoweringClass::ComposedInteger: return TranslateComposedInteger(inst); default: break;
case IR::LoweringClass::ExtendedInteger: return TranslateExtendedInteger(inst);
} }
return false;
switch (inst.family) {
case Decoder::Family::SMEM:
case Decoder::Family::MUBUF:
case Decoder::Family::MTBUF:
case Decoder::Family::FLAT:
case Decoder::Family::DS:
case Decoder::Family::MIMG: return TranslateMemoryOperation(inst, error);
case Decoder::Family::EXP: return TranslateAttributeOperation(inst, error);
default: break;
}
switch (inst.opcode) {
case Decoder::Opcode::V_INTERP_P1_F32:
case Decoder::Opcode::V_INTERP_P2_F32:
case Decoder::Opcode::V_INTERP_MOV_F32: return TranslateAttributeOperation(inst, error);
case Decoder::Opcode::S_BRANCH:
case Decoder::Opcode::S_CBRANCH_SCC0:
case Decoder::Opcode::S_CBRANCH_SCC1:
case Decoder::Opcode::S_CBRANCH_VCCZ:
case Decoder::Opcode::S_CBRANCH_VCCNZ:
case Decoder::Opcode::S_CBRANCH_EXECZ:
case Decoder::Opcode::S_CBRANCH_EXECNZ:
case Decoder::Opcode::S_SETPC_B64:
case Decoder::Opcode::S_ENDPGM: return true;
default: break;
}
const bool translated = TranslateControlOperation(inst) || TranslateStateOperation(inst) ||
TranslateMove(inst, error) || TranslateLaneOperation(inst, error) ||
TranslateIntegerCompare(inst) || TranslateInteger16Compare(inst) ||
TranslateFloatCompare(inst) || TranslateConversion(inst) ||
TranslateInteger16Operation(inst) || TranslatePackedInteger16(inst) ||
TranslatePackedFloat16(inst) || TranslateFloat16Operation(inst) ||
TranslateFloatOperation(inst) || TranslateU64MaskOperation(inst) ||
TranslateSimpleInteger(inst) || TranslateComposedInteger(inst) ||
TranslateExtendedInteger(inst);
if (!translated && error != nullptr) {
*error = "decoded opcode has no IR translation";
}
return translated;
} }
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail } // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -3,36 +3,39 @@
#include <array> #include <array>
#include <utility> #include <utility>
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslatePackedFloat16(const IR::Instruction& inst) { bool Translator::TranslatePackedFloat16(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode; IR::ValueOpcode opcode;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::PackedAddF16: opcode = IR::ValueOpcode::FPAdd32; break; case Decoder::Opcode::V_PK_ADD_F16: opcode = IR::ValueOpcode::FPAdd32; break;
case IR::Opcode::PackedMulF16: opcode = IR::ValueOpcode::FPMul32; break; case Decoder::Opcode::V_PK_MUL_F16: opcode = IR::ValueOpcode::FPMul32; break;
case IR::Opcode::PackedMinF16: opcode = IR::ValueOpcode::FPMin32; break; case Decoder::Opcode::V_PK_MIN_F16: opcode = IR::ValueOpcode::FPMin32; break;
case IR::Opcode::PackedMaxF16: opcode = IR::ValueOpcode::FPMax32; break; case Decoder::Opcode::V_PK_MAX_F16: opcode = IR::ValueOpcode::FPMax32; break;
case IR::Opcode::PackedFmaF16: opcode = IR::ValueOpcode::FPFma32; break; case Decoder::Opcode::V_PK_FMA_F16: opcode = IR::ValueOpcode::FPFma32; break;
case Decoder::Opcode::V_PK_FMAC_F16: opcode = IR::ValueOpcode::FPFma32; break;
default: return false; default: return false;
} }
const auto translate_lane = [&](bool high) { const auto translate_lane = [&](bool high) {
const auto lhs = ReadF16LaneAsF32(inst.src[0], high, true); const auto lhs = ReadF16LaneAsF32(inst.src0, high, true);
const auto rhs = ReadF16LaneAsF32(inst.src[1], high, true); const auto rhs = ReadF16LaneAsF32(inst.src1, high, true);
IR::F32 result; IR::F32 result;
if (inst.src_count == 3u) { if (inst.opcode == Decoder::Opcode::V_PK_FMAC_F16) {
result = IR::F32( result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.dst, high, true)}));
ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src[2], high, true)})); } else if (inst.src_count == 3u) {
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src2, high, true)}));
} else { } else {
result = IR::F32(ir.Emit(opcode, {lhs, rhs})); result = IR::F32(ir.Emit(opcode, {lhs, rhs}));
} }
return ApplyF32ResultModifiers(inst.dst, result); return ApplyF32ResultModifiers(inst.dst, result);
}; };
auto raw = inst.dst; auto raw = DestinationOperand(inst);
raw.omod = 0u; raw.omod = 0u;
raw.clamp = false; raw.clamp = false;
auto result = PackHalf2x16(translate_lane(false), translate_lane(true)); auto result = PackHalf2x16(translate_lane(false), translate_lane(true));
if (inst.op == IR::Opcode::PackedMinF16 || inst.op == IR::Opcode::PackedMaxF16) { if (inst.opcode == Decoder::Opcode::V_PK_MIN_F16 ||
const auto quiet_snan = [&](const IR::Operand& operand, bool high) { inst.opcode == Decoder::Opcode::V_PK_MAX_F16) {
const auto quiet_snan = [&](const Decoder::Operand& operand, bool high) {
const auto bits = ReadU16LaneAsU32(operand, high, false); const auto bits = ReadU16LaneAsU32(operand, high, false);
const auto exponent = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x7c00u))); const auto exponent = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x7c00u)));
const auto payload = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x01ffu))); const auto payload = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x01ffu)));
@@ -41,8 +44,8 @@ bool Translator::TranslatePackedFloat16(const IR::Instruction& inst) {
return std::pair {snan, ir.BitwiseOr(bits, IR::U32(IR::Value(0x0200u)))}; return std::pair {snan, ir.BitwiseOr(bits, IR::U32(IR::Value(0x0200u)))};
}; };
const auto override_lane = [&](bool high) { const auto override_lane = [&](bool high) {
const auto [lhs_snan, lhs_quiet] = quiet_snan(inst.src[0], high); const auto [lhs_snan, lhs_quiet] = quiet_snan(inst.src0, high);
const auto [rhs_snan, rhs_quiet] = quiet_snan(inst.src[1], high); const auto [rhs_snan, rhs_quiet] = quiet_snan(inst.src1, high);
const auto normal = high ? ir.ShiftRightLogical(result, IR::U32(IR::Value(16u))) const auto normal = high ? ir.ShiftRightLogical(result, IR::U32(IR::Value(16u)))
: ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))); : ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu)));
return ir.Select(lhs_snan, lhs_quiet, ir.Select(rhs_snan, rhs_quiet, normal)); return ir.Select(lhs_snan, lhs_quiet, ir.Select(rhs_snan, rhs_quiet, normal));
@@ -53,59 +56,71 @@ bool Translator::TranslatePackedFloat16(const IR::Instruction& inst) {
return true; return true;
} }
bool Translator::TranslateFloat16Operation(const IR::Instruction& inst) { bool Translator::TranslateFloat16Operation(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode; IR::ValueOpcode opcode;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::AddF16: opcode = IR::ValueOpcode::FPAdd32; break; case Decoder::Opcode::V_ADD_F16: opcode = IR::ValueOpcode::FPAdd32; break;
case IR::Opcode::SubF16: opcode = IR::ValueOpcode::FPSub32; break; case Decoder::Opcode::V_SUB_F16: opcode = IR::ValueOpcode::FPSub32; break;
case IR::Opcode::MulF16: opcode = IR::ValueOpcode::FPMul32; break; case Decoder::Opcode::V_SUBREV_F16: opcode = IR::ValueOpcode::FPSub32; break;
case IR::Opcode::MinF16: opcode = IR::ValueOpcode::FPMin32; break; case Decoder::Opcode::V_MUL_F16: opcode = IR::ValueOpcode::FPMul32; break;
case IR::Opcode::MaxF16: opcode = IR::ValueOpcode::FPMax32; break; case Decoder::Opcode::V_MIN_F16: opcode = IR::ValueOpcode::FPMin32; break;
case IR::Opcode::FmaF16: case Decoder::Opcode::V_MAX_F16: opcode = IR::ValueOpcode::FPMax32; break;
case IR::Opcode::MadMixF16: opcode = IR::ValueOpcode::FPFma32; break; case Decoder::Opcode::V_FMAC_F16:
case IR::Opcode::RcpF16: opcode = IR::ValueOpcode::FPRecip32; break; case Decoder::Opcode::V_FMAMK_F16:
case IR::Opcode::SqrtF16: opcode = IR::ValueOpcode::FPSqrt; break; case Decoder::Opcode::V_FMAAK_F16:
case IR::Opcode::InverseSqrtF16: opcode = IR::ValueOpcode::FPRecipSqrt32; break; case Decoder::Opcode::V_FMA_F16:
case IR::Opcode::Log2F16: opcode = IR::ValueOpcode::FPLog2; break; case Decoder::Opcode::V_MAD_MIXLO_F16: opcode = IR::ValueOpcode::FPFma32; break;
case IR::Opcode::Exp2F16: opcode = IR::ValueOpcode::FPExp2; break; case Decoder::Opcode::V_MAD_MIXHI_F16: opcode = IR::ValueOpcode::FPFma32; break;
case IR::Opcode::FloorF16: opcode = IR::ValueOpcode::FPFloor32; break; case Decoder::Opcode::V_RCP_F16: opcode = IR::ValueOpcode::FPRecip32; break;
case IR::Opcode::CeilF16: opcode = IR::ValueOpcode::FPCeil32; break; case Decoder::Opcode::V_SQRT_F16: opcode = IR::ValueOpcode::FPSqrt; break;
case IR::Opcode::TruncF16: opcode = IR::ValueOpcode::FPTrunc32; break; case Decoder::Opcode::V_RSQ_F16: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
case IR::Opcode::RoundEvenF16: opcode = IR::ValueOpcode::FPRoundEven32; break; case Decoder::Opcode::V_LOG_F16: opcode = IR::ValueOpcode::FPLog2; break;
case IR::Opcode::Min3F16: opcode = IR::ValueOpcode::FPMinTri32; break; case Decoder::Opcode::V_EXP_F16: opcode = IR::ValueOpcode::FPExp2; break;
case IR::Opcode::Max3F16: opcode = IR::ValueOpcode::FPMaxTri32; break; case Decoder::Opcode::V_FLOOR_F16: opcode = IR::ValueOpcode::FPFloor32; break;
case IR::Opcode::Med3F16: opcode = IR::ValueOpcode::FPMedTri32; break; case Decoder::Opcode::V_CEIL_F16: opcode = IR::ValueOpcode::FPCeil32; break;
case Decoder::Opcode::V_TRUNC_F16: opcode = IR::ValueOpcode::FPTrunc32; break;
case Decoder::Opcode::V_RNDNE_F16: opcode = IR::ValueOpcode::FPRoundEven32; break;
case Decoder::Opcode::V_MIN3_F16: opcode = IR::ValueOpcode::FPMinTri32; break;
case Decoder::Opcode::V_MAX3_F16: opcode = IR::ValueOpcode::FPMaxTri32; break;
case Decoder::Opcode::V_MED3_F16: opcode = IR::ValueOpcode::FPMedTri32; break;
default: return false; default: return false;
} }
const bool accumulator = inst.opcode == Decoder::Opcode::V_FMAC_F16;
const uint32_t source_count = accumulator ? 3u : inst.src_count;
std::array<IR::Value, 3> args; std::array<IR::Value, 3> args;
for (uint32_t index = 0; index < inst.src_count; index++) { for (uint32_t index = 0; index < source_count; index++) {
args[index] = inst.op == IR::Opcode::MadMixF16 ? IR::Value(ReadMixF32(inst.src[index])) auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
: IR::Value(ReadF16AsF32(inst.src[index])); if (inst.opcode == Decoder::Opcode::V_SUBREV_F16 && index < 2u) {
operand = SourceAt(inst, 1u - index);
}
const bool mix = inst.opcode == Decoder::Opcode::V_MAD_MIXLO_F16 ||
inst.opcode == Decoder::Opcode::V_MAD_MIXHI_F16;
args[index] = mix ? IR::Value(ReadMixF32(operand)) : IR::Value(ReadF16AsF32(operand));
} }
IR::F32 result; IR::F32 result;
switch (inst.src_count) { switch (source_count) {
case 1: result = IR::F32(ir.Emit(opcode, {args[0]})); break; case 1: result = IR::F32(ir.Emit(opcode, {args[0]})); break;
case 2: result = IR::F32(ir.Emit(opcode, {args[0], args[1]})); break; case 2: result = IR::F32(ir.Emit(opcode, {args[0], args[1]})); break;
case 3: result = IR::F32(ir.Emit(opcode, {args[0], args[1], args[2]})); break; case 3: result = IR::F32(ir.Emit(opcode, {args[0], args[1], args[2]})); break;
default: EXIT("invalid half-float source count: %u", inst.src_count); default: EXIT("invalid half-float source count: %u", inst.src_count);
} }
if (inst.op == IR::Opcode::SqrtF16 || inst.op == IR::Opcode::InverseSqrtF16 || if (inst.opcode == Decoder::Opcode::V_SQRT_F16 || inst.opcode == Decoder::Opcode::V_RSQ_F16 ||
inst.op == IR::Opcode::Log2F16) { inst.opcode == Decoder::Opcode::V_LOG_F16) {
const auto negative = IR::U1( const auto negative =
ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {args[0], IR::Value::F32(0.0f)})); IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {args[0], IR::Value::F32(0.0f)}));
result = ApplyF32ResultModifiers(inst.dst, result); result = ApplyF32ResultModifiers(inst.dst, result);
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f))); const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u)); const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
WriteU16(inst.dst, ir.Select(negative, invalid, bits)); WriteU16(DestinationOperand(inst), ir.Select(negative, invalid, bits));
return true; return true;
} }
WriteF16(inst.dst, result); WriteF16(DestinationOperand(inst), result);
return true; return true;
} }
bool Translator::TranslateFloatOperation(const IR::Instruction& inst) { bool Translator::TranslateFloatOperation(const Decoder::Instruction& inst) {
if (inst.op == IR::Opcode::FrexpMantF32) { if (inst.opcode == Decoder::Opcode::V_FREXP_MANT_F32) {
const auto bits = ReadU32(inst.src[0]); const auto bits = ReadU32(inst.src0);
const auto exponent = IR::U32( const auto exponent = IR::U32(
ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(23u), IR::Value(8u)})); ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(23u), IR::Value(8u)}));
const auto mantissa = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x007fffffu))); const auto mantissa = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x007fffffu)));
@@ -121,84 +136,99 @@ bool Translator::TranslateFloatOperation(const IR::Instruction& inst) {
const auto finite = ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal, const auto finite = ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal,
ir.Select(zero, bits, subnormal)); ir.Select(zero, bits, subnormal));
const auto result = ir.Select(ir.IEqual(exponent, IR::U32(IR::Value(0xffu))), bits, finite); const auto result = ir.Select(ir.IEqual(exponent, IR::U32(IR::Value(0xffu))), bits, finite);
WriteOperand(inst.dst, ir.BitCastF32(result)); WriteOperand(DestinationOperand(inst), ir.BitCastF32(result));
return true; return true;
} }
IR::ValueOpcode opcode {}; IR::ValueOpcode opcode {};
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::RcpF32: opcode = IR::ValueOpcode::FPRecip32; break; case Decoder::Opcode::V_RCP_F32: opcode = IR::ValueOpcode::FPRecip32; break;
case IR::Opcode::RcpIflagF32: opcode = IR::ValueOpcode::FPRecipIFlag32; break; case Decoder::Opcode::V_RCP_IFLAG_F32: opcode = IR::ValueOpcode::FPRecipIFlag32; break;
case IR::Opcode::FractF32: opcode = IR::ValueOpcode::FPFract32; break; case Decoder::Opcode::V_FRACT_F32: opcode = IR::ValueOpcode::FPFract32; break;
case IR::Opcode::TruncF32: opcode = IR::ValueOpcode::FPTrunc32; break; case Decoder::Opcode::V_TRUNC_F32: opcode = IR::ValueOpcode::FPTrunc32; break;
case IR::Opcode::CeilF32: opcode = IR::ValueOpcode::FPCeil32; break; case Decoder::Opcode::V_CEIL_F32: opcode = IR::ValueOpcode::FPCeil32; break;
case IR::Opcode::RoundEvenF32: opcode = IR::ValueOpcode::FPRoundEven32; break; case Decoder::Opcode::V_RNDNE_F32: opcode = IR::ValueOpcode::FPRoundEven32; break;
case IR::Opcode::FloorF32: opcode = IR::ValueOpcode::FPFloor32; break; case Decoder::Opcode::V_FLOOR_F32: opcode = IR::ValueOpcode::FPFloor32; break;
case IR::Opcode::Exp2F32: opcode = IR::ValueOpcode::FPExp2; break; case Decoder::Opcode::V_EXP_F32: opcode = IR::ValueOpcode::FPExp2; break;
case IR::Opcode::Log2F32: opcode = IR::ValueOpcode::FPLog2; break; case Decoder::Opcode::V_LOG_F32: opcode = IR::ValueOpcode::FPLog2; break;
case IR::Opcode::InverseSqrtF32: opcode = IR::ValueOpcode::FPRecipSqrt32; break; case Decoder::Opcode::V_RSQ_F32: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
case IR::Opcode::SqrtF32: opcode = IR::ValueOpcode::FPSqrt; break; case Decoder::Opcode::V_SQRT_F32: opcode = IR::ValueOpcode::FPSqrt; break;
case IR::Opcode::SinF32: opcode = IR::ValueOpcode::FPSin; break; case Decoder::Opcode::V_SIN_F32: opcode = IR::ValueOpcode::FPSin; break;
case IR::Opcode::CosF32: opcode = IR::ValueOpcode::FPCos; break; case Decoder::Opcode::V_COS_F32: opcode = IR::ValueOpcode::FPCos; break;
case IR::Opcode::FAddF32: opcode = IR::ValueOpcode::FPAdd32; break; case Decoder::Opcode::V_ADD_F32: opcode = IR::ValueOpcode::FPAdd32; break;
case IR::Opcode::FSubF32: opcode = IR::ValueOpcode::FPSub32; break; case Decoder::Opcode::V_SUB_F32: opcode = IR::ValueOpcode::FPSub32; break;
case IR::Opcode::FMulF32: opcode = IR::ValueOpcode::FPMul32; break; case Decoder::Opcode::V_SUBREV_F32: opcode = IR::ValueOpcode::FPSub32; break;
case IR::Opcode::FMinF32: opcode = IR::ValueOpcode::FPMin32; break; case Decoder::Opcode::V_MUL_F32: opcode = IR::ValueOpcode::FPMul32; break;
case IR::Opcode::FMaxF32: opcode = IR::ValueOpcode::FPMax32; break; case Decoder::Opcode::V_MIN_F32: opcode = IR::ValueOpcode::FPMin32; break;
case IR::Opcode::FMadF32: opcode = IR::ValueOpcode::FPFma32; break; case Decoder::Opcode::V_MAX_F32: opcode = IR::ValueOpcode::FPMax32; break;
case IR::Opcode::FMin3F32: opcode = IR::ValueOpcode::FPMinTri32; break; case Decoder::Opcode::V_MAC_F32: opcode = IR::ValueOpcode::FPFma32; break;
case IR::Opcode::FMax3F32: opcode = IR::ValueOpcode::FPMaxTri32; break; case Decoder::Opcode::V_MADMK_F32:
case IR::Opcode::FMed3F32: opcode = IR::ValueOpcode::FPMedTri32; break; case Decoder::Opcode::V_MADAK_F32:
case IR::Opcode::LdexpF32: opcode = IR::ValueOpcode::FPLdexp; break; case Decoder::Opcode::V_MAD_F32:
case Decoder::Opcode::V_FMA_F32: opcode = IR::ValueOpcode::FPFma32; break;
case Decoder::Opcode::V_MIN3_F32: opcode = IR::ValueOpcode::FPMinTri32; break;
case Decoder::Opcode::V_MAX3_F32: opcode = IR::ValueOpcode::FPMaxTri32; break;
case Decoder::Opcode::V_MED3_F32: opcode = IR::ValueOpcode::FPMedTri32; break;
case Decoder::Opcode::V_LDEXP_F32: opcode = IR::ValueOpcode::FPLdexp; break;
default: break; default: break;
} }
if (opcode != IR::ValueOpcode {}) { if (opcode != IR::ValueOpcode {}) {
const bool accumulator = inst.opcode == Decoder::Opcode::V_MAC_F32;
const bool fused = accumulator || inst.opcode == Decoder::Opcode::V_MADMK_F32 ||
inst.opcode == Decoder::Opcode::V_MADAK_F32 ||
inst.opcode == Decoder::Opcode::V_MAD_F32 ||
inst.opcode == Decoder::Opcode::V_FMA_F32;
const uint32_t source_count = accumulator ? 3u : inst.src_count;
std::array<IR::Value, 3> args; std::array<IR::Value, 3> args;
for (uint32_t index = 0; index < inst.src_count; index++) { for (uint32_t index = 0; index < source_count; index++) {
const auto arg_type = IR::ArgTypeOf(opcode, index); const auto arg_type = IR::ArgTypeOf(opcode, index);
args[index] = auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
arg_type == IR::Type::F32 if (inst.opcode == Decoder::Opcode::V_SUBREV_F32 && index < 2u) {
? (inst.op == IR::Opcode::FMadF32 ? IR::Value(ReadMixF32(inst.src[index])) operand = SourceAt(inst, 1u - index);
: ReadOperand(inst.src[index], IR::Type::F32)) }
: ReadOperand(inst.src[index], arg_type); args[index] = arg_type == IR::Type::F32 ? (fused ? IR::Value(ReadMixF32(operand))
: ReadOperand(operand, IR::Type::F32))
: ReadOperand(operand, arg_type);
} }
IR::Value result; IR::Value result;
switch (inst.src_count) { switch (source_count) {
case 1: result = ir.Emit(opcode, {args[0]}); break; case 1: result = ir.Emit(opcode, {args[0]}); break;
case 2: result = ir.Emit(opcode, {args[0], args[1]}); break; case 2: result = ir.Emit(opcode, {args[0], args[1]}); break;
case 3: result = ir.Emit(opcode, {args[0], args[1], args[2]}); break; case 3: result = ir.Emit(opcode, {args[0], args[1], args[2]}); break;
default: EXIT("invalid floating-point source count: %u", inst.src_count); default: EXIT("invalid floating-point source count: %u", inst.src_count);
} }
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
return true; return true;
} }
const auto select_f32 = [&](IR::U1 condition, IR::F32 true_value, IR::F32 false_value) { const auto select_f32 = [&](IR::U1 condition, IR::F32 true_value, IR::F32 false_value) {
return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value})); return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value}));
}; };
if (inst.op == IR::Opcode::Dot2AccF32F16) { if (inst.opcode == Decoder::Opcode::V_DOT2C_F32_F16) {
auto a = inst.src[0]; auto a = inst.src0;
a.op_sel = false; a.op_sel = false;
a.op_sel_hi = true; a.op_sel_hi = true;
auto b = inst.src[1]; auto b = inst.src1;
b.op_sel = false; b.op_sel = false;
b.op_sel_hi = true; b.op_sel_hi = true;
const auto a_lo = ReadF16LaneAsF32(a, false); const auto a_lo = ReadF16LaneAsF32(a, false);
const auto a_hi = ReadF16LaneAsF32(a, true); const auto a_hi = ReadF16LaneAsF32(a, true);
const auto b_lo = ReadF16LaneAsF32(b, false); const auto b_lo = ReadF16LaneAsF32(b, false);
const auto b_hi = ReadF16LaneAsF32(b, true); const auto b_hi = ReadF16LaneAsF32(b, true);
const auto acc = ReadMixF32(inst.src[2]); const auto acc = ReadMixF32(inst.dst);
const auto lo = IR::F32(ir.Emit(IR::ValueOpcode::FPFma32, {a_lo, b_lo, acc})); const auto lo = IR::F32(ir.Emit(IR::ValueOpcode::FPFma32, {a_lo, b_lo, acc}));
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::FPFma32, {a_hi, b_hi, lo})); WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::FPFma32, {a_hi, b_hi, lo}));
return true; return true;
} }
if (inst.op != IR::Opcode::CubeIdF32 && inst.op != IR::Opcode::CubeScF32 && if (inst.opcode != Decoder::Opcode::V_CUBEID_F32 &&
inst.op != IR::Opcode::CubeTcF32 && inst.op != IR::Opcode::CubeMaF32) { inst.opcode != Decoder::Opcode::V_CUBESC_F32 &&
inst.opcode != Decoder::Opcode::V_CUBETC_F32 &&
inst.opcode != Decoder::Opcode::V_CUBEMA_F32) {
return false; return false;
} }
const auto x = ReadMixF32(inst.src[0]); const auto x = ReadMixF32(inst.src0);
const auto y = ReadMixF32(inst.src[1]); const auto y = ReadMixF32(inst.src1);
const auto z = ReadMixF32(inst.src[2]); const auto z = ReadMixF32(inst.src2);
const auto nx = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {x})); const auto nx = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {x}));
const auto ny = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {y})); const auto ny = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {y}));
const auto nz = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {z})); const auto nz = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {z}));
@@ -216,18 +246,20 @@ bool Translator::TranslateFloatOperation(const IR::Instruction& inst) {
return select_f32(z_face, z_value, select_f32(y_face, y_value, x_value)); return select_f32(z_face, z_value, select_f32(y_face, y_value, x_value));
}; };
IR::F32 result; IR::F32 result;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::CubeIdF32: case Decoder::Opcode::V_CUBEID_F32:
result = select_face( result = select_face(
select_f32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))), select_f32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))),
select_f32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))), select_f32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))),
select_f32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f)))); select_f32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f))));
break; break;
case IR::Opcode::CubeScF32: case Decoder::Opcode::V_CUBESC_F32:
result = select_face(select_f32(x_neg, z, nz), x, select_f32(z_neg, nx, x)); result = select_face(select_f32(x_neg, z, nz), x, select_f32(z_neg, nx, x));
break; break;
case IR::Opcode::CubeTcF32: result = select_face(ny, select_f32(y_neg, nz, z), ny); break; case Decoder::Opcode::V_CUBETC_F32:
case IR::Opcode::CubeMaF32: { result = select_face(ny, select_f32(y_neg, nz, z), ny);
break;
case Decoder::Opcode::V_CUBEMA_F32: {
const auto two = IR::F32(IR::Value::F32(2.0f)); const auto two = IR::F32(IR::Value::F32(2.0f));
result = select_face(IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {x, two})), result = select_face(IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {x, two})),
IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {y, two})), IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {y, two})),
@@ -236,8 +268,8 @@ bool Translator::TranslateFloatOperation(const IR::Instruction& inst) {
} }
default: return false; default: return false;
} }
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
return true; return true;
} }
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail } // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -2,22 +2,22 @@
#include <array> #include <array>
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool Translator::TranslateInteger16Operation(const IR::Instruction& inst) { bool Translator::TranslateInteger16Operation(const Decoder::Instruction& inst) {
const auto mask16 = [&](IR::U32 value) { const auto mask16 = [&](IR::U32 value) {
return ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu))); return ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu)));
}; };
IR::Value result; IR::Value result;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::ShiftLeftLogicalU16: case Decoder::Opcode::V_LSHLREV_B16:
case IR::Opcode::ShiftRightLogicalU16: case Decoder::Opcode::V_LSHRREV_B16:
case IR::Opcode::ShiftRightArithmeticI16: { case Decoder::Opcode::V_ASHRREV_I16: {
const bool arithmetic = inst.op == IR::Opcode::ShiftRightArithmeticI16; const bool arithmetic = inst.opcode == Decoder::Opcode::V_ASHRREV_I16;
const auto value = ReadU16AsU32(inst.src[0], arithmetic); const auto value = ReadU16AsU32(inst.src1, arithmetic);
const auto count = const auto count =
ir.BitwiseAnd(ReadU16AsU32(inst.src[1], false), IR::U32(IR::Value(15u))); ir.BitwiseAnd(ReadU16AsU32(inst.src0, false), IR::U32(IR::Value(15u)));
if (inst.op == IR::Opcode::ShiftLeftLogicalU16) { if (inst.opcode == Decoder::Opcode::V_LSHLREV_B16) {
result = ir.ShiftLeftLogical(value, count); result = ir.ShiftLeftLogical(value, count);
} else if (arithmetic) { } else if (arithmetic) {
result = ir.ShiftRightArithmetic(value, count); result = ir.ShiftRightArithmetic(value, count);
@@ -26,176 +26,207 @@ bool Translator::TranslateInteger16Operation(const IR::Instruction& inst) {
} }
break; break;
} }
case IR::Opcode::IAddU16: case Decoder::Opcode::V_ADD_NC_U16:
result = ir.IAdd(ReadU16AsU32(inst.src[0], false), ReadU16AsU32(inst.src[1], false)); case Decoder::Opcode::V_ADD_NC_I16:
result = ir.IAdd(ReadU16AsU32(inst.src0, false), ReadU16AsU32(inst.src1, false));
break; break;
case IR::Opcode::ISubI16: case Decoder::Opcode::V_SUB_NC_U16:
result = ir.ISub(ReadU16AsU32(inst.src[0], false), ReadU16AsU32(inst.src[1], false)); case Decoder::Opcode::V_SUB_NC_I16:
result = ir.ISub(ReadU16AsU32(inst.src0, false), ReadU16AsU32(inst.src1, false));
break; break;
case IR::Opcode::IMed3I16: case Decoder::Opcode::V_MED3_I16:
result = ir.Emit(IR::ValueOpcode::SMedTri32, result = ir.Emit(IR::ValueOpcode::SMedTri32,
{ReadU16AsU32(inst.src[0], true), ReadU16AsU32(inst.src[1], true), {ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
ReadU16AsU32(inst.src[2], true)}); ReadU16AsU32(inst.src2, true)});
break; break;
case IR::Opcode::IMinI16: case Decoder::Opcode::V_MIN_I16:
case IR::Opcode::IMaxI16: case Decoder::Opcode::V_MAX_I16:
case IR::Opcode::UMinU16: case Decoder::Opcode::V_MIN_U16:
case IR::Opcode::UMaxU16: { case Decoder::Opcode::V_MAX_U16: {
const bool sign = inst.op == IR::Opcode::IMinI16 || inst.op == IR::Opcode::IMaxI16; const bool sign = inst.opcode == Decoder::Opcode::V_MIN_I16 ||
inst.opcode == Decoder::Opcode::V_MAX_I16;
IR::ValueOpcode opcode; IR::ValueOpcode opcode;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::IMinI16: opcode = IR::ValueOpcode::SMin32; break; case Decoder::Opcode::V_MIN_I16: opcode = IR::ValueOpcode::SMin32; break;
case IR::Opcode::IMaxI16: opcode = IR::ValueOpcode::SMax32; break; case Decoder::Opcode::V_MAX_I16: opcode = IR::ValueOpcode::SMax32; break;
case IR::Opcode::UMinU16: opcode = IR::ValueOpcode::UMin32; break; case Decoder::Opcode::V_MIN_U16: opcode = IR::ValueOpcode::UMin32; break;
default: opcode = IR::ValueOpcode::UMax32; break; default: opcode = IR::ValueOpcode::UMax32; break;
} }
result = result =
ir.Emit(opcode, {ReadU16AsU32(inst.src[0], sign), ReadU16AsU32(inst.src[1], sign)}); ir.Emit(opcode, {ReadU16AsU32(inst.src0, sign), ReadU16AsU32(inst.src1, sign)});
break; break;
} }
default: return false; default: return false;
} }
WriteU16(inst.dst, mask16(IR::U32(result))); WriteU16(DestinationOperand(inst), mask16(IR::U32(result)));
return true; return true;
} }
bool Translator::TranslatePackedInteger16(const IR::Instruction& inst) { bool Translator::TranslatePackedInteger16(const Decoder::Instruction& inst) {
const auto lane = [&](const IR::Operand& operand, bool high, bool sign) { const auto lane = [&](const Decoder::Operand& operand, bool high, bool sign) {
return ReadU16LaneAsU32(operand, high, sign); return ReadU16LaneAsU32(operand, high, sign);
}; };
const auto mask_count = [&](IR::U32 value) { const auto mask_count = [&](IR::U32 value) {
return ir.BitwiseAnd(value, IR::U32(IR::Value(15u))); return ir.BitwiseAnd(value, IR::U32(IR::Value(15u)));
}; };
const auto translate_lane = [&](bool high) -> IR::U32 { const auto translate_lane = [&](bool high) -> IR::U32 {
if (inst.op == IR::Opcode::PackedLshlrevB16 || inst.op == IR::Opcode::PackedLshrrevB16 || if (inst.opcode == Decoder::Opcode::V_PK_LSHLREV_B16 ||
inst.op == IR::Opcode::PackedAshrrevI16) { inst.opcode == Decoder::Opcode::V_PK_LSHRREV_B16 ||
const auto count = mask_count(lane(inst.src[0], high, false)); inst.opcode == Decoder::Opcode::V_PK_ASHRREV_I16) {
const bool arithmetic = inst.op == IR::Opcode::PackedAshrrevI16; const auto count = mask_count(lane(inst.src0, high, false));
const auto value = lane(inst.src[1], high, arithmetic); const bool arithmetic = inst.opcode == Decoder::Opcode::V_PK_ASHRREV_I16;
if (inst.op == IR::Opcode::PackedLshlrevB16) { const auto value = lane(inst.src1, high, arithmetic);
if (inst.opcode == Decoder::Opcode::V_PK_LSHLREV_B16) {
return ir.ShiftLeftLogical(value, count); return ir.ShiftLeftLogical(value, count);
} }
return arithmetic ? ir.ShiftRightArithmetic(value, count) return arithmetic ? ir.ShiftRightArithmetic(value, count)
: ir.ShiftRightLogical(value, count); : ir.ShiftRightLogical(value, count);
} }
const bool signed_minmax = const bool signed_minmax = inst.opcode == Decoder::Opcode::V_PK_MAX_I16 ||
inst.op == IR::Opcode::PackedMaxI16 || inst.op == IR::Opcode::PackedMinI16; inst.opcode == Decoder::Opcode::V_PK_MIN_I16;
const auto lhs = lane(inst.src[0], high, signed_minmax); const auto lhs = lane(inst.src0, high, signed_minmax);
const auto rhs = lane(inst.src[1], high, signed_minmax); const auto rhs = lane(inst.src1, high, signed_minmax);
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::PackedMadI16: case Decoder::Opcode::V_PK_MAD_I16:
case IR::Opcode::PackedMadU16: case Decoder::Opcode::V_PK_MAD_U16:
return ir.IAdd(ir.IMul(lhs, rhs), return ir.IAdd(ir.IMul(lhs, rhs),
lane(inst.src[2], high, inst.op == IR::Opcode::PackedMadI16)); lane(inst.src2, high, inst.opcode == Decoder::Opcode::V_PK_MAD_I16));
case IR::Opcode::PackedMulLoU16: return ir.IMul(lhs, rhs); case Decoder::Opcode::V_PK_MUL_LO_U16: return ir.IMul(lhs, rhs);
case IR::Opcode::PackedAddI16: case Decoder::Opcode::V_PK_ADD_I16:
case IR::Opcode::PackedAddU16: return ir.IAdd(lhs, rhs); case Decoder::Opcode::V_PK_ADD_U16: return ir.IAdd(lhs, rhs);
case IR::Opcode::PackedSubI16: case Decoder::Opcode::V_PK_SUB_I16:
case IR::Opcode::PackedSubU16: return ir.ISub(lhs, rhs); case Decoder::Opcode::V_PK_SUB_U16: return ir.ISub(lhs, rhs);
case IR::Opcode::PackedMaxI16: case Decoder::Opcode::V_PK_MAX_I16:
return IR::U32(ir.Emit(IR::ValueOpcode::SMax32, {lhs, rhs})); return IR::U32(ir.Emit(IR::ValueOpcode::SMax32, {lhs, rhs}));
case IR::Opcode::PackedMinI16: case Decoder::Opcode::V_PK_MIN_I16:
return IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {lhs, rhs})); return IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {lhs, rhs}));
case IR::Opcode::PackedMaxU16: case Decoder::Opcode::V_PK_MAX_U16:
return IR::U32(ir.Emit(IR::ValueOpcode::UMax32, {lhs, rhs})); return IR::U32(ir.Emit(IR::ValueOpcode::UMax32, {lhs, rhs}));
case IR::Opcode::PackedMinU16: case Decoder::Opcode::V_PK_MIN_U16:
return IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {lhs, rhs})); return IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {lhs, rhs}));
default: EXIT("invalid packed integer opcode"); default: EXIT("invalid packed integer opcode");
} }
}; };
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::PackedMadI16: case Decoder::Opcode::V_PK_MAD_I16:
case IR::Opcode::PackedMulLoU16: case Decoder::Opcode::V_PK_MUL_LO_U16:
case IR::Opcode::PackedAddI16: case Decoder::Opcode::V_PK_ADD_I16:
case IR::Opcode::PackedSubI16: case Decoder::Opcode::V_PK_SUB_I16:
case IR::Opcode::PackedLshlrevB16: case Decoder::Opcode::V_PK_LSHLREV_B16:
case IR::Opcode::PackedLshrrevB16: case Decoder::Opcode::V_PK_LSHRREV_B16:
case IR::Opcode::PackedAshrrevI16: case Decoder::Opcode::V_PK_ASHRREV_I16:
case IR::Opcode::PackedMaxI16: case Decoder::Opcode::V_PK_MAX_I16:
case IR::Opcode::PackedMinI16: case Decoder::Opcode::V_PK_MIN_I16:
case IR::Opcode::PackedMadU16: case Decoder::Opcode::V_PK_MAD_U16:
case IR::Opcode::PackedAddU16: case Decoder::Opcode::V_PK_ADD_U16:
case IR::Opcode::PackedSubU16: case Decoder::Opcode::V_PK_SUB_U16:
case IR::Opcode::PackedMaxU16: case Decoder::Opcode::V_PK_MAX_U16:
case IR::Opcode::PackedMinU16: case Decoder::Opcode::V_PK_MIN_U16:
WriteOperand(inst.dst, PackU16Lanes(translate_lane(false), translate_lane(true))); WriteOperand(DestinationOperand(inst),
PackU16Lanes(translate_lane(false), translate_lane(true)));
return true; return true;
default: return false; default: return false;
} }
} }
IR::U1 Translator::EvaluateU64Mask(const IR::Instruction& inst) { IR::U1 Translator::EvaluateU64Mask(const Decoder::Instruction& inst) {
const auto lhs = [&] { return ReadMask(inst.src[0]); }; const auto lhs = [&] { return ReadMask(inst.src0); };
const auto rhs = [&] { return ReadMask(inst.src[1]); }; const auto rhs = [&] { return ReadMask(inst.src1); };
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::BitwiseAndU64: return ir.LogicalAnd(lhs(), rhs()); case Decoder::Opcode::S_AND_B64: return ir.LogicalAnd(lhs(), rhs());
case IR::Opcode::BitwiseOrU64: return ir.LogicalOr(lhs(), rhs()); case Decoder::Opcode::S_OR_B64: return ir.LogicalOr(lhs(), rhs());
case IR::Opcode::BitwiseXorU64: case Decoder::Opcode::S_XOR_B64:
return IR::U1(ir.Emit(IR::ValueOpcode::LogicalXor, {lhs(), rhs()})); return IR::U1(ir.Emit(IR::ValueOpcode::LogicalXor, {lhs(), rhs()}));
case IR::Opcode::BitwiseAndNotU64: return ir.LogicalAnd(lhs(), ir.LogicalNot(rhs())); case Decoder::Opcode::S_ANDN2_B64: return ir.LogicalAnd(lhs(), ir.LogicalNot(rhs()));
case IR::Opcode::BitwiseOrNotU64: return ir.LogicalOr(lhs(), ir.LogicalNot(rhs())); case Decoder::Opcode::S_ORN2_B64: return ir.LogicalOr(lhs(), ir.LogicalNot(rhs()));
case IR::Opcode::BitwiseNandU64: return ir.LogicalNot(ir.LogicalAnd(lhs(), rhs())); case Decoder::Opcode::S_NAND_B64: return ir.LogicalNot(ir.LogicalAnd(lhs(), rhs()));
case IR::Opcode::BitwiseNorU64: return ir.LogicalNot(ir.LogicalOr(lhs(), rhs())); case Decoder::Opcode::S_NOR_B64: return ir.LogicalNot(ir.LogicalOr(lhs(), rhs()));
case IR::Opcode::BitwiseXnorU64: case Decoder::Opcode::S_XNOR_B64:
return ir.LogicalNot(IR::U1(ir.Emit(IR::ValueOpcode::LogicalXor, {lhs(), rhs()}))); return ir.LogicalNot(IR::U1(ir.Emit(IR::ValueOpcode::LogicalXor, {lhs(), rhs()})));
case IR::Opcode::BitwiseNotU64: return ir.LogicalNot(lhs()); case Decoder::Opcode::S_NOT_B64: return ir.LogicalNot(lhs());
default: EXIT("invalid 64-bit mask opcode"); default: EXIT("invalid 64-bit mask opcode");
} }
} }
bool Translator::TranslateSimpleInteger(const IR::Instruction& inst) { bool Translator::TranslateSimpleInteger(const Decoder::Instruction& inst) {
IR::ValueOpcode opcode {}; IR::ValueOpcode opcode {};
IR::Type type = IR::Type::U32; IR::Type type = IR::Type::U32;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::AbsI32: opcode = IR::ValueOpcode::IAbs32; break; case Decoder::Opcode::S_ABS_I32: opcode = IR::ValueOpcode::IAbs32; break;
case IR::Opcode::IAddU32: opcode = IR::ValueOpcode::IAdd32; break; case Decoder::Opcode::S_MUL_I32:
case IR::Opcode::ISubU32: opcode = IR::ValueOpcode::ISub32; break; case Decoder::Opcode::S_MULK_I32:
case IR::Opcode::IMulU32: opcode = IR::ValueOpcode::IMul32; break; case Decoder::Opcode::V_MUL_LO_U32:
case IR::Opcode::UMulHighU32: opcode = IR::ValueOpcode::UMulHi; break; case Decoder::Opcode::V_MUL_LO_I32: opcode = IR::ValueOpcode::IMul32; break;
case IR::Opcode::SMulHighI32: opcode = IR::ValueOpcode::SMulHi; break; case Decoder::Opcode::S_MUL_HI_U32:
case IR::Opcode::IMinI32: opcode = IR::ValueOpcode::SMin32; break; case Decoder::Opcode::V_MUL_HI_U32: opcode = IR::ValueOpcode::UMulHi; break;
case IR::Opcode::IMaxI32: opcode = IR::ValueOpcode::SMax32; break; case Decoder::Opcode::V_MUL_HI_I32: opcode = IR::ValueOpcode::SMulHi; break;
case IR::Opcode::UMinU32: opcode = IR::ValueOpcode::UMin32; break; case Decoder::Opcode::V_ADD_NC_U32: opcode = IR::ValueOpcode::IAdd32; break;
case IR::Opcode::UMaxU32: opcode = IR::ValueOpcode::UMax32; break; case Decoder::Opcode::V_SUB_NC_U32:
case IR::Opcode::IMin3I32: opcode = IR::ValueOpcode::SMinTri32; break; case Decoder::Opcode::V_SUBREV_NC_U32: opcode = IR::ValueOpcode::ISub32; break;
case IR::Opcode::IMax3I32: opcode = IR::ValueOpcode::SMaxTri32; break; case Decoder::Opcode::V_MIN_I32: opcode = IR::ValueOpcode::SMin32; break;
case IR::Opcode::IMed3I32: opcode = IR::ValueOpcode::SMedTri32; break; case Decoder::Opcode::V_MAX_I32: opcode = IR::ValueOpcode::SMax32; break;
case IR::Opcode::UMin3U32: opcode = IR::ValueOpcode::UMinTri32; break; case Decoder::Opcode::V_MIN_U32: opcode = IR::ValueOpcode::UMin32; break;
case IR::Opcode::UMax3U32: opcode = IR::ValueOpcode::UMaxTri32; break; case Decoder::Opcode::V_MAX_U32: opcode = IR::ValueOpcode::UMax32; break;
case IR::Opcode::UMed3U32: opcode = IR::ValueOpcode::UMedTri32; break; case Decoder::Opcode::V_MIN3_I32: opcode = IR::ValueOpcode::SMinTri32; break;
case IR::Opcode::BitwiseAndU32: opcode = IR::ValueOpcode::BitwiseAnd32; break; case Decoder::Opcode::V_MAX3_I32: opcode = IR::ValueOpcode::SMaxTri32; break;
case IR::Opcode::BitwiseOrU32: opcode = IR::ValueOpcode::BitwiseOr32; break; case Decoder::Opcode::V_MED3_I32: opcode = IR::ValueOpcode::SMedTri32; break;
case IR::Opcode::BitwiseXorU32: opcode = IR::ValueOpcode::BitwiseXor32; break; case Decoder::Opcode::V_MIN3_U32: opcode = IR::ValueOpcode::UMinTri32; break;
case IR::Opcode::BitwiseNotU32: opcode = IR::ValueOpcode::BitwiseNot32; break; case Decoder::Opcode::V_MAX3_U32: opcode = IR::ValueOpcode::UMaxTri32; break;
case IR::Opcode::BitReverseU32: opcode = IR::ValueOpcode::BitReverse32; break; case Decoder::Opcode::V_MED3_U32: opcode = IR::ValueOpcode::UMedTri32; break;
case IR::Opcode::BitCountU32: opcode = IR::ValueOpcode::BitCount32; break; case Decoder::Opcode::S_AND_B32:
case IR::Opcode::BitCountU64: case Decoder::Opcode::V_AND_B32: opcode = IR::ValueOpcode::BitwiseAnd32; break;
case Decoder::Opcode::S_OR_B32:
case Decoder::Opcode::V_OR_B32: opcode = IR::ValueOpcode::BitwiseOr32; break;
case Decoder::Opcode::S_XOR_B32:
case Decoder::Opcode::V_XOR_B32: opcode = IR::ValueOpcode::BitwiseXor32; break;
case Decoder::Opcode::S_NOT_B32:
case Decoder::Opcode::V_NOT_B32: opcode = IR::ValueOpcode::BitwiseNot32; break;
case Decoder::Opcode::S_BREV_B32:
case Decoder::Opcode::V_BFREV_B32: opcode = IR::ValueOpcode::BitReverse32; break;
case Decoder::Opcode::S_BCNT1_I32_B32: opcode = IR::ValueOpcode::BitCount32; break;
case Decoder::Opcode::S_BCNT1_I32_B64:
opcode = IR::ValueOpcode::BitCount64; opcode = IR::ValueOpcode::BitCount64;
type = IR::Type::U64; type = IR::Type::U64;
break; break;
case IR::Opcode::FindLsbU32: opcode = IR::ValueOpcode::FindILsb32; break; case Decoder::Opcode::S_FF1_I32_B32:
case IR::Opcode::ShiftLeftLogicalU32: opcode = IR::ValueOpcode::ShiftLeftLogical32; break; case Decoder::Opcode::V_FFBL_B32: opcode = IR::ValueOpcode::FindILsb32; break;
case IR::Opcode::ShiftRightLogicalU32: opcode = IR::ValueOpcode::ShiftRightLogical32; break; case Decoder::Opcode::S_LSHL_B32:
case IR::Opcode::ShiftRightArithmeticI32: case Decoder::Opcode::V_LSHL_B32:
case Decoder::Opcode::V_LSHLREV_B32: opcode = IR::ValueOpcode::ShiftLeftLogical32; break;
case Decoder::Opcode::S_LSHR_B32:
case Decoder::Opcode::V_LSHR_B32:
case Decoder::Opcode::V_LSHRREV_B32: opcode = IR::ValueOpcode::ShiftRightLogical32; break;
case Decoder::Opcode::S_ASHR_I32:
case Decoder::Opcode::V_ASHR_I32:
case Decoder::Opcode::V_ASHRREV_I32:
opcode = IR::ValueOpcode::ShiftRightArithmetic32; opcode = IR::ValueOpcode::ShiftRightArithmetic32;
break; break;
case IR::Opcode::ShiftLeftLogicalU64: case Decoder::Opcode::S_LSHL_B64:
case Decoder::Opcode::V_LSHLREV_B64:
opcode = IR::ValueOpcode::ShiftLeftLogical64; opcode = IR::ValueOpcode::ShiftLeftLogical64;
type = IR::Type::U64; type = IR::Type::U64;
break; break;
case IR::Opcode::ShiftRightLogicalU64: case Decoder::Opcode::S_LSHR_B64:
case Decoder::Opcode::V_LSHRREV_B64:
opcode = IR::ValueOpcode::ShiftRightLogical64; opcode = IR::ValueOpcode::ShiftRightLogical64;
type = IR::Type::U64; type = IR::Type::U64;
break; break;
default: return false; default: return false;
} }
const bool reverse = inst.opcode == Decoder::Opcode::V_SUBREV_NC_U32 ||
inst.opcode == Decoder::Opcode::V_LSHLREV_B32 ||
inst.opcode == Decoder::Opcode::V_LSHRREV_B32 ||
inst.opcode == Decoder::Opcode::V_ASHRREV_I32 ||
inst.opcode == Decoder::Opcode::V_LSHLREV_B64 ||
inst.opcode == Decoder::Opcode::V_LSHRREV_B64;
std::array<IR::Value, 3> args; std::array<IR::Value, 3> args;
for (uint32_t index = 0; index < inst.src_count; index++) { for (uint32_t index = 0; index < inst.src_count; index++) {
const auto arg_type = IR::ArgTypeOf(opcode, index); const auto arg_type = IR::ArgTypeOf(opcode, index);
args[index] = ReadOperand(inst.src[index], arg_type == IR::Type::Void ? type : arg_type); const auto operand = SourceAt(inst, reverse && index < 2u ? 1u - index : index);
args[index] = ReadOperand(operand, arg_type == IR::Type::Void ? type : arg_type);
if (index == 1u && (opcode == IR::ValueOpcode::ShiftLeftLogical32 || if (index == 1u && (opcode == IR::ValueOpcode::ShiftLeftLogical32 ||
opcode == IR::ValueOpcode::ShiftRightLogical32 || opcode == IR::ValueOpcode::ShiftRightLogical32 ||
opcode == IR::ValueOpcode::ShiftRightArithmetic32)) { opcode == IR::ValueOpcode::ShiftRightArithmetic32)) {
@@ -209,56 +240,77 @@ bool Translator::TranslateSimpleInteger(const IR::Instruction& inst) {
case 3: result = ir.Emit(opcode, {args[0], args[1], args[2]}); break; case 3: result = ir.Emit(opcode, {args[0], args[1], args[2]}); break;
default: EXIT("invalid simple integer source count: %u", inst.src_count); default: EXIT("invalid simple integer source count: %u", inst.src_count);
} }
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
switch (inst.opcode) {
case Decoder::Opcode::S_ABS_I32:
case Decoder::Opcode::S_BCNT1_I32_B32:
case Decoder::Opcode::S_BCNT1_I32_B64:
case Decoder::Opcode::S_AND_B32:
case Decoder::Opcode::S_OR_B32:
case Decoder::Opcode::S_XOR_B32:
case Decoder::Opcode::S_NOT_B32:
case Decoder::Opcode::S_LSHL_B32:
case Decoder::Opcode::S_LSHR_B32:
case Decoder::Opcode::S_ASHR_I32:
ir.SetScc(ir.INotEqual(IR::U32(result), IR::U32(IR::Value(0u))));
break;
case Decoder::Opcode::S_LSHL_B64:
case Decoder::Opcode::S_LSHR_B64:
ir.SetScc(
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
break;
default: break;
}
return true; return true;
} }
bool Translator::TranslateComposedInteger(const IR::Instruction& inst) { bool Translator::TranslateComposedInteger(const Decoder::Instruction& inst) {
const auto binary_u32 = [&](auto operation) { const auto binary_u32 = [&](auto operation) {
const auto lhs = ReadU32(inst.src[0]); const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src[1]); const auto rhs = ReadU32(inst.src1);
return operation(lhs, rhs); return operation(lhs, rhs);
}; };
IR::Value result; IR::Value result;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::BitwiseAndNotU32: case Decoder::Opcode::S_ANDN2_B32:
result = binary_u32( result = binary_u32(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseAnd(lhs, ir.BitwiseNot(rhs)); }); [&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseAnd(lhs, ir.BitwiseNot(rhs)); });
break; break;
case IR::Opcode::BitwiseOrNotU32: case Decoder::Opcode::S_ORN2_B32:
result = binary_u32( result = binary_u32(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseOr(lhs, ir.BitwiseNot(rhs)); }); [&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseOr(lhs, ir.BitwiseNot(rhs)); });
break; break;
case IR::Opcode::BitwiseNandU32: case Decoder::Opcode::S_NAND_B32:
result = binary_u32( result = binary_u32(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseAnd(lhs, rhs)); }); [&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseAnd(lhs, rhs)); });
break; break;
case IR::Opcode::BitwiseNorU32: case Decoder::Opcode::S_NOR_B32:
result = binary_u32( result = binary_u32(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseOr(lhs, rhs)); }); [&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseOr(lhs, rhs)); });
break; break;
case IR::Opcode::BitwiseXnorU32: case Decoder::Opcode::S_XNOR_B32:
case Decoder::Opcode::V_XNOR_B32:
result = binary_u32( result = binary_u32(
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseXor(lhs, rhs)); }); [&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseXor(lhs, rhs)); });
break; break;
case IR::Opcode::BitwiseAndOrU32: { case Decoder::Opcode::V_AND_OR_B32: {
const auto lhs = ReadU32(inst.src[0]); const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src[1]); const auto rhs = ReadU32(inst.src1);
const auto add = ReadU32(inst.src[2]); const auto add = ReadU32(inst.src2);
result = ir.BitwiseOr(ir.BitwiseAnd(lhs, rhs), add); result = ir.BitwiseOr(ir.BitwiseAnd(lhs, rhs), add);
break; break;
} }
case IR::Opcode::BitwiseOr3U32: case Decoder::Opcode::V_OR3_B32:
result = ir.BitwiseOr(ir.BitwiseOr(ReadU32(inst.src[0]), ReadU32(inst.src[1])), result = ir.BitwiseOr(ir.BitwiseOr(ReadU32(inst.src0), ReadU32(inst.src1)),
ReadU32(inst.src[2])); ReadU32(inst.src2));
break; break;
case IR::Opcode::BitwiseXor3U32: case Decoder::Opcode::V_XOR3_B32:
result = ir.BitwiseXor(ir.BitwiseXor(ReadU32(inst.src[0]), ReadU32(inst.src[1])), result = ir.BitwiseXor(ir.BitwiseXor(ReadU32(inst.src0), ReadU32(inst.src1)),
ReadU32(inst.src[2])); ReadU32(inst.src2));
break; break;
case IR::Opcode::FindLsbU64: { case Decoder::Opcode::S_FF1_I32_B64: {
const auto source = ExtractU64(ReadU64(inst.src[0])); const auto source = ExtractU64(ReadU64(inst.src0));
const auto low_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[0]})); const auto low_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[0]}));
const auto high_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[1]})); const auto high_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[1]}));
const auto high_position = ir.IAdd(high_lsb, IR::U32(IR::Value(32u))); const auto high_position = ir.IAdd(high_lsb, IR::U32(IR::Value(32u)));
@@ -267,16 +319,16 @@ bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
high_position, IR::U32(IR::Value(0xffffffffu)))); high_position, IR::U32(IR::Value(0xffffffffu))));
break; break;
} }
case IR::Opcode::FindMsbFromHighU32: { case Decoder::Opcode::V_FFBH_U32: {
const auto source = ReadU32(inst.src[0]); const auto source = ReadU32(inst.src0);
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {source})); const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {source}));
const auto position = ir.ISub(IR::U32(IR::Value(31u)), msb); const auto position = ir.ISub(IR::U32(IR::Value(31u)), msb);
result = ir.Select(ir.INotEqual(source, IR::U32(IR::Value(0u))), position, result = ir.Select(ir.INotEqual(source, IR::U32(IR::Value(0u))), position,
IR::U32(IR::Value(0xffffffffu))); IR::U32(IR::Value(0xffffffffu)));
break; break;
} }
case IR::Opcode::FindMsbFromHighU64: { case Decoder::Opcode::S_FLBIT_I32_B64: {
const auto source = ReadU64(inst.src[0]); const auto source = ReadU64(inst.src0);
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb64, {source})); const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb64, {source}));
const auto position = ir.ISub(IR::U32(IR::Value(63u)), msb); const auto position = ir.ISub(IR::U32(IR::Value(63u)), msb);
const auto nonzero = IR::U1( const auto nonzero = IR::U1(
@@ -287,11 +339,21 @@ bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
} }
default: return false; default: return false;
} }
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
switch (inst.opcode) {
case Decoder::Opcode::S_ANDN2_B32:
case Decoder::Opcode::S_ORN2_B32:
case Decoder::Opcode::S_NAND_B32:
case Decoder::Opcode::S_NOR_B32:
case Decoder::Opcode::S_XNOR_B32:
ir.SetScc(ir.INotEqual(IR::U32(result), IR::U32(IR::Value(0u))));
break;
default: break;
}
return true; return true;
} }
bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) { bool Translator::TranslateExtendedInteger(const Decoder::Instruction& inst) {
const auto imm = [](uint32_t value) { return IR::U32(IR::Value(value)); }; const auto imm = [](uint32_t value) { return IR::U32(IR::Value(value)); };
const auto mask = [&](IR::U32 value, uint32_t bits) { return ir.BitwiseAnd(value, imm(bits)); }; const auto mask = [&](IR::U32 value, uint32_t bits) { return ir.BitwiseAnd(value, imm(bits)); };
const auto extract = [&](IR::U32 value, IR::U32 offset, IR::U32 width, bool sign) { const auto extract = [&](IR::U32 value, IR::U32 offset, IR::U32 width, bool sign) {
@@ -312,26 +374,26 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
}; };
IR::Value result; IR::Value result;
switch (inst.op) { switch (inst.opcode) {
case IR::Opcode::IMadI24U32: case Decoder::Opcode::V_MAD_I32_I24:
case IR::Opcode::UMadU24U32: case Decoder::Opcode::V_MAD_U32_U24:
case IR::Opcode::IMulI24U32: case Decoder::Opcode::V_MUL_I32_I24:
case IR::Opcode::UMulU24U32: { case Decoder::Opcode::V_MUL_U32_U24: {
const bool sign = const bool sign = inst.opcode == Decoder::Opcode::V_MAD_I32_I24 ||
inst.op == IR::Opcode::IMadI24U32 || inst.op == IR::Opcode::IMulI24U32; inst.opcode == Decoder::Opcode::V_MUL_I32_I24;
const auto lhs = extract(ReadU32(inst.src[0]), imm(0), imm(24), sign); const auto lhs = extract(ReadU32(inst.src0), imm(0), imm(24), sign);
const auto rhs = extract(ReadU32(inst.src[1]), imm(0), imm(24), sign); const auto rhs = extract(ReadU32(inst.src1), imm(0), imm(24), sign);
auto value = ir.IMul(lhs, rhs); auto value = ir.IMul(lhs, rhs);
if (inst.src_count == 3) { if (inst.src_count == 3) {
value = ir.IAdd(value, ReadU32(inst.src[2])); value = ir.IAdd(value, ReadU32(inst.src2));
} }
result = value; result = value;
break; break;
} }
case IR::Opcode::UMadU64U32: { case Decoder::Opcode::V_MAD_U64_U32: {
const auto lhs = ReadU32(inst.src[0]); const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src[1]); const auto rhs = ReadU32(inst.src1);
const auto add = ExtractU64(ReadU64(inst.src[2])); const auto add = ExtractU64(ReadU64(inst.src2));
const auto mul_low = ir.IMul(lhs, rhs); const auto mul_low = ir.IMul(lhs, rhs);
const auto mul_high = IR::U32(ir.Emit(IR::ValueOpcode::UMulHi, {lhs, rhs})); const auto mul_high = IR::U32(ir.Emit(IR::ValueOpcode::UMulHi, {lhs, rhs}));
const auto low = ir.IAdd(mul_low, add[0]); const auto low = ir.IAdd(mul_low, add[0]);
@@ -340,52 +402,52 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
const auto carry0 = ir.ULessThan(high0, mul_high); const auto carry0 = ir.ULessThan(high0, mul_high);
const auto high = ir.IAdd(high0, ir.Select(carry_low, imm(1), imm(0))); const auto high = ir.IAdd(high0, ir.Select(carry_low, imm(1), imm(0)));
const auto carry1 = ir.ULessThan(high, high0); const auto carry1 = ir.ULessThan(high, high0);
WriteOperand(inst.dst, ir.ConstructU64(low, high)); WriteOperand(DestinationOperand(inst), ir.ConstructU64(low, high));
if (inst.dst2.kind != IR::OperandKind::Null) { if (inst.dst2.kind != Decoder::OperandKind::Null &&
inst.dst2.kind != Decoder::OperandKind::Unknown) {
WriteMask(inst.dst2, ir.LogicalOr(carry0, carry1)); WriteMask(inst.dst2, ir.LogicalOr(carry0, carry1));
} }
return true; return true;
} }
case IR::Opcode::SadU32: { case Decoder::Opcode::V_SAD_U32: {
const auto lhs = ReadU32(inst.src[0]); const auto lhs = ReadU32(inst.src0);
const auto rhs = ReadU32(inst.src[1]); const auto rhs = ReadU32(inst.src1);
const auto lo = IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {lhs, rhs})); const auto lo = IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {lhs, rhs}));
const auto hi = IR::U32(ir.Emit(IR::ValueOpcode::UMax32, {lhs, rhs})); const auto hi = IR::U32(ir.Emit(IR::ValueOpcode::UMax32, {lhs, rhs}));
result = ir.IAdd(ir.ISub(hi, lo), ReadU32(inst.src[2])); result = ir.IAdd(ir.ISub(hi, lo), ReadU32(inst.src2));
break; break;
} }
case IR::Opcode::IAdd3U32: case Decoder::Opcode::V_ADD3_U32:
result = result = ir.IAdd(ir.IAdd(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
ir.IAdd(ir.IAdd(ReadU32(inst.src[0]), ReadU32(inst.src[1])), ReadU32(inst.src[2]));
break; break;
case IR::Opcode::BitClearU32: case Decoder::Opcode::S_BITSET0_B32:
case IR::Opcode::BitSetU32: { case Decoder::Opcode::S_BITSET1_B32: {
const auto bit = ir.ShiftLeftLogical(imm(1), mask(ReadU32(inst.src[1]), 31u)); const auto bit = ir.ShiftLeftLogical(imm(1), mask(ReadU32(inst.src0), 31u));
result = inst.op == IR::Opcode::BitClearU32 result = inst.opcode == Decoder::Opcode::S_BITSET0_B32
? IR::Value(ir.BitwiseAnd(ReadU32(inst.src[0]), ir.BitwiseNot(bit))) ? IR::Value(ir.BitwiseAnd(ReadU32(inst.dst), ir.BitwiseNot(bit)))
: IR::Value(ir.BitwiseOr(ReadU32(inst.src[0]), bit)); : IR::Value(ir.BitwiseOr(ReadU32(inst.dst), bit));
break; break;
} }
case IR::Opcode::BitCountAddU32: case Decoder::Opcode::V_BCNT_U32_B32:
result = ir.IAdd(IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {ReadU32(inst.src[0])})), result = ir.IAdd(IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {ReadU32(inst.src0)})),
ReadU32(inst.src[1])); ReadU32(inst.src1));
break; break;
case IR::Opcode::MaskedBitCountLowU32: case Decoder::Opcode::V_MBCNT_LO_U32_B32:
case IR::Opcode::MaskedBitCountHighU32: { case Decoder::Opcode::V_MBCNT_HI_U32_B32: {
const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId)); const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId));
const auto local = mask(lane, 31u); const auto local = mask(lane, 31u);
const auto below = ir.ISub(ir.ShiftLeftLogical(imm(1), local), imm(1)); const auto below = ir.ISub(ir.ShiftLeftLogical(imm(1), local), imm(1));
const auto high_lane = const auto high_lane =
IR::U1(ir.Emit(IR::ValueOpcode::UGreaterThanEqual32, {lane, imm(32)})); IR::U1(ir.Emit(IR::ValueOpcode::UGreaterThanEqual32, {lane, imm(32)}));
const auto thread_mask = inst.op == IR::Opcode::MaskedBitCountLowU32 const auto thread_mask = inst.opcode == Decoder::Opcode::V_MBCNT_LO_U32_B32
? ir.Select(high_lane, imm(0xffffffffu), below) ? ir.Select(high_lane, imm(0xffffffffu), below)
: ir.Select(high_lane, below, imm(0)); : ir.Select(high_lane, below, imm(0));
const auto active = ir.BitwiseAnd(ReadU32(inst.src[0]), thread_mask); const auto active = ir.BitwiseAnd(ReadU32(inst.src0), thread_mask);
result = ir.IAdd(IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {active})), result = ir.IAdd(IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {active})),
ReadU32(inst.src[1])); ReadU32(inst.src1));
break; break;
} }
case IR::Opcode::BitReplicateB64B32: { case Decoder::Opcode::S_BITREPLICATE_B64_B32: {
const auto replicate = [&](IR::U32 value) { const auto replicate = [&](IR::U32 value) {
auto bits = ir.BitwiseOr(value, ir.ShiftLeftLogical(value, imm(8))); auto bits = ir.BitwiseOr(value, ir.ShiftLeftLogical(value, imm(8)));
bits = ir.BitwiseAnd(bits, imm(0x00ff00ffu)); bits = ir.BitwiseAnd(bits, imm(0x00ff00ffu));
@@ -397,12 +459,12 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
bits = ir.BitwiseAnd(bits, imm(0x55555555u)); bits = ir.BitwiseAnd(bits, imm(0x55555555u));
return ir.BitwiseOr(bits, ir.ShiftLeftLogical(bits, imm(1))); return ir.BitwiseOr(bits, ir.ShiftLeftLogical(bits, imm(1)));
}; };
const auto source = ReadU32(inst.src[0]); const auto source = ReadU32(inst.src0);
result = ir.ConstructU64(replicate(mask(source, 0xffffu)), result = ir.ConstructU64(replicate(mask(source, 0xffffu)),
replicate(ir.ShiftRightLogical(source, imm(16)))); replicate(ir.ShiftRightLogical(source, imm(16))));
break; break;
} }
case IR::Opcode::QuadmaskB64: { case Decoder::Opcode::S_QUADMASK_B64: {
const auto compact = [&](IR::U32 value) { const auto compact = [&](IR::U32 value) {
auto bits = ir.BitwiseOr(value, ir.ShiftRightLogical(value, imm(1))); auto bits = ir.BitwiseOr(value, ir.ShiftRightLogical(value, imm(1)));
bits = ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(2))); bits = ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(2)));
@@ -411,42 +473,43 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
bits = mask(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(6))), 0x000f000fu); bits = mask(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(6))), 0x000f000fu);
return mask(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(12))), 0xffu); return mask(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(12))), 0xffu);
}; };
const auto source = ReadU32Pair(inst.src[0]); const auto source = ReadU32Pair(inst.src0);
const auto quads = const auto quads =
ir.BitwiseOr(compact(source[0]), ir.ShiftLeftLogical(compact(source[1]), imm(8))); ir.BitwiseOr(compact(source[0]), ir.ShiftLeftLogical(compact(source[1]), imm(8)));
result = ir.ConstructU64(quads, imm(0)); result = ir.ConstructU64(quads, imm(0));
break; break;
} }
case IR::Opcode::BitFieldMaskU32: { case Decoder::Opcode::S_BFM_B32:
const auto count = mask(ReadU32(inst.src[0]), 31u); case Decoder::Opcode::V_BFM_B32: {
const auto offset = mask(ReadU32(inst.src[1]), 31u); const auto count = mask(ReadU32(inst.src0), 31u);
const auto offset = mask(ReadU32(inst.src1), 31u);
result = result =
ir.Emit(IR::ValueOpcode::BitFieldInsert, {imm(0), imm(0xffffffffu), offset, count}); ir.Emit(IR::ValueOpcode::BitFieldInsert, {imm(0), imm(0xffffffffu), offset, count});
break; break;
} }
case IR::Opcode::BitFieldMaskU64: { case Decoder::Opcode::S_BFM_B64: {
const auto count = mask(ReadU32(inst.src[0]), 63u); const auto count = mask(ReadU32(inst.src0), 63u);
const auto offset = mask(ReadU32(inst.src[1]), 63u); const auto offset = mask(ReadU32(inst.src1), 63u);
result = ir.Emit(IR::ValueOpcode::ShiftLeftLogical64, {right_mask64(count), offset}); result = ir.Emit(IR::ValueOpcode::ShiftLeftLogical64, {right_mask64(count), offset});
break; break;
} }
case IR::Opcode::BitFieldExtractU32: case Decoder::Opcode::S_BFE_U32:
case IR::Opcode::BitFieldExtractI32: { case Decoder::Opcode::S_BFE_I32: {
const auto source = ReadU32(inst.src[0]); const auto source = ReadU32(inst.src0);
const auto field = ReadU32(inst.src[1]); const auto field = ReadU32(inst.src1);
const auto offset = extract(field, imm(0), imm(5), false); const auto offset = extract(field, imm(0), imm(5), false);
const auto count = const auto count =
IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {extract(field, imm(16), imm(7), false), IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {extract(field, imm(16), imm(7), false),
ir.ISub(imm(32), offset)})); ir.ISub(imm(32), offset)}));
const auto opcode = inst.op == IR::Opcode::BitFieldExtractI32 const auto opcode = inst.opcode == Decoder::Opcode::S_BFE_I32
? IR::ValueOpcode::BitFieldSExtract ? IR::ValueOpcode::BitFieldSExtract
: IR::ValueOpcode::BitFieldUExtract; : IR::ValueOpcode::BitFieldUExtract;
result = ir.Emit(opcode, {source, offset, count}); result = ir.Emit(opcode, {source, offset, count});
break; break;
} }
case IR::Opcode::BitFieldExtractU64: { case Decoder::Opcode::S_BFE_U64: {
const auto source = ReadU64(inst.src[0]); const auto source = ReadU64(inst.src0);
const auto field = ReadU32(inst.src[1]); const auto field = ReadU32(inst.src1);
const auto offset = extract(field, imm(0), imm(6), false); const auto offset = extract(field, imm(0), imm(6), false);
const auto raw_count = extract(field, imm(16), imm(7), false); const auto raw_count = extract(field, imm(16), imm(7), false);
const auto available = ir.ISub(imm(64), offset); const auto available = ir.ISub(imm(64), offset);
@@ -456,49 +519,49 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
result = ir.Emit(IR::ValueOpcode::BitwiseAnd64, {shifted, right_mask64(count)}); result = ir.Emit(IR::ValueOpcode::BitwiseAnd64, {shifted, right_mask64(count)});
break; break;
} }
case IR::Opcode::BitFieldExtract3U32: case Decoder::Opcode::V_BFE_U32:
case IR::Opcode::BitFieldExtract3I32: { case Decoder::Opcode::V_BFE_I32: {
const auto source = ReadU32(inst.src[0]); const auto source = ReadU32(inst.src0);
const auto offset = mask(ReadU32(inst.src[1]), 31u); const auto offset = mask(ReadU32(inst.src1), 31u);
const auto count = const auto count =
IR::U32(ir.Emit(IR::ValueOpcode::UMin32, IR::U32(ir.Emit(IR::ValueOpcode::UMin32,
{mask(ReadU32(inst.src[2]), 31u), ir.ISub(imm(32), offset)})); {mask(ReadU32(inst.src2), 31u), ir.ISub(imm(32), offset)}));
const auto opcode = inst.op == IR::Opcode::BitFieldExtract3I32 const auto opcode = inst.opcode == Decoder::Opcode::V_BFE_I32
? IR::ValueOpcode::BitFieldSExtract ? IR::ValueOpcode::BitFieldSExtract
: IR::ValueOpcode::BitFieldUExtract; : IR::ValueOpcode::BitFieldUExtract;
result = ir.Emit(opcode, {source, offset, count}); result = ir.Emit(opcode, {source, offset, count});
break; break;
} }
case IR::Opcode::BitFieldInsertSelectU32: { case Decoder::Opcode::V_BFI_B32: {
const auto bits = ReadU32(inst.src[0]); const auto bits = ReadU32(inst.src0);
const auto insert = ReadU32(inst.src[1]); const auto insert = ReadU32(inst.src1);
const auto base = ReadU32(inst.src[2]); const auto base = ReadU32(inst.src2);
result = result =
ir.BitwiseOr(ir.BitwiseAnd(bits, insert), ir.BitwiseAnd(ir.BitwiseNot(bits), base)); ir.BitwiseOr(ir.BitwiseAnd(bits, insert), ir.BitwiseAnd(ir.BitwiseNot(bits), base));
break; break;
} }
case IR::Opcode::BitCompare0B32: case Decoder::Opcode::S_BITCMP0_B32:
case IR::Opcode::BitCompare1B32: { case Decoder::Opcode::S_BITCMP1_B32: {
const auto value = ReadU32(inst.src[0]); const auto value = ReadU32(inst.src0);
const auto bit = extract(value, mask(ReadU32(inst.src[1]), 31u), imm(1), false); const auto bit = extract(value, mask(ReadU32(inst.src1), 31u), imm(1), false);
const auto expected = imm(inst.op == IR::Opcode::BitCompare1B32 ? 1u : 0u); const auto expected = imm(inst.opcode == Decoder::Opcode::S_BITCMP1_B32 ? 1u : 0u);
WriteOperand(inst.dst, ir.IEqual(bit, expected)); WriteCompareResult(inst.dst, ir.IEqual(bit, expected));
return true; return true;
} }
case IR::Opcode::AlignBitU32: { case Decoder::Opcode::V_ALIGNBIT_B32: {
const auto hi = ReadU32(inst.src[0]); const auto hi = ReadU32(inst.src0);
const auto lo = ReadU32(inst.src[1]); const auto lo = ReadU32(inst.src1);
const auto shift = mask(ReadU32(inst.src[2]), 31u); const auto shift = mask(ReadU32(inst.src2), 31u);
const auto lo_part = ir.ShiftRightLogical(lo, shift); const auto lo_part = ir.ShiftRightLogical(lo, shift);
const auto hi_part_raw = ir.ShiftLeftLogical(hi, mask(ir.ISub(imm(32), shift), 31u)); const auto hi_part_raw = ir.ShiftLeftLogical(hi, mask(ir.ISub(imm(32), shift), 31u));
const auto hi_part = ir.Select(ir.INotEqual(shift, imm(0)), hi_part_raw, imm(0)); const auto hi_part = ir.Select(ir.INotEqual(shift, imm(0)), hi_part_raw, imm(0));
result = ir.BitwiseOr(lo_part, hi_part); result = ir.BitwiseOr(lo_part, hi_part);
break; break;
} }
case IR::Opcode::AlignByteU32: { case Decoder::Opcode::V_ALIGNBYTE_B32: {
const auto hi = ReadU32(inst.src[0]); const auto hi = ReadU32(inst.src0);
const auto lo = ReadU32(inst.src[1]); const auto lo = ReadU32(inst.src1);
const auto byte_offset = mask(ReadU32(inst.src[2]), 31u); const auto byte_offset = mask(ReadU32(inst.src2), 31u);
const auto bit_offset = ir.ShiftLeftLogical(byte_offset, imm(3)); const auto bit_offset = ir.ShiftLeftLogical(byte_offset, imm(3));
const auto concatenated = ir.ConstructU64(lo, hi); const auto concatenated = ir.ConstructU64(lo, hi);
const auto shifted = IR::U64(ir.Emit(IR::ValueOpcode::ShiftRightLogical64, const auto shifted = IR::U64(ir.Emit(IR::ValueOpcode::ShiftRightLogical64,
@@ -508,85 +571,68 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
result = ir.Select(in_range, ExtractU64(shifted)[0], imm(0)); result = ir.Select(in_range, ExtractU64(shifted)[0], imm(0));
break; break;
} }
case IR::Opcode::ShiftLeftAddU32: case Decoder::Opcode::V_LSHL_ADD_U32:
result = ir.IAdd(ir.ShiftLeftLogical(ReadU32(inst.src0), mask(ReadU32(inst.src1), 31u)),
ReadU32(inst.src2));
break;
case Decoder::Opcode::V_ADD_LSHL_U32:
result = ir.ShiftLeftLogical(ir.IAdd(ReadU32(inst.src0), ReadU32(inst.src1)),
mask(ReadU32(inst.src2), 31u));
break;
case Decoder::Opcode::V_XAD_U32:
result = result =
ir.IAdd(ir.ShiftLeftLogical(ReadU32(inst.src[0]), mask(ReadU32(inst.src[1]), 31u)), ir.IAdd(ir.BitwiseXor(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
ReadU32(inst.src[2]));
break; break;
case IR::Opcode::AddShiftLeftU32: case Decoder::Opcode::V_LSHL_OR_B32:
result = ir.ShiftLeftLogical(ir.IAdd(ReadU32(inst.src[0]), ReadU32(inst.src[1])),
mask(ReadU32(inst.src[2]), 31u));
break;
case IR::Opcode::XorAddU32:
result = ir.IAdd(ir.BitwiseXor(ReadU32(inst.src[0]), ReadU32(inst.src[1])),
ReadU32(inst.src[2]));
break;
case IR::Opcode::ShiftLeftOrU32:
result = ir.BitwiseOr(
ir.ShiftLeftLogical(ReadU32(inst.src[0]), mask(ReadU32(inst.src[1]), 31u)),
ReadU32(inst.src[2]));
break;
case IR::Opcode::SelectU32:
result = result =
ir.Select(ReadCondition(inst.src[0]), ReadU32(inst.src[1]), ReadU32(inst.src[2])); ir.BitwiseOr(ir.ShiftLeftLogical(ReadU32(inst.src0), mask(ReadU32(inst.src1), 31u)),
ReadU32(inst.src2));
break; break;
case IR::Opcode::SelectMaskU32: case Decoder::Opcode::V_CNDMASK_B32: {
result = ir.Select(ReadMask(inst.src[0]), ReadU32(inst.src[1]), ReadU32(inst.src[2])); Decoder::Operand mask_operand;
break; mask_operand.kind = Decoder::OperandKind::VccLo;
case IR::Opcode::SelectF32Bits: if (inst.src_count >= 3u) {
case IR::Opcode::SelectMaskF32Bits: { mask_operand = inst.src2;
const auto condition = inst.op == IR::Opcode::SelectMaskF32Bits }
? ReadMask(inst.src[0]) const auto condition = ReadMask(mask_operand);
: ReadCondition(inst.src[0]); if (inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute) {
result = ir.Emit(IR::ValueOpcode::SelectF32, result = ir.Emit(IR::ValueOpcode::SelectF32,
{condition, ReadOperand(inst.src[1], IR::Type::F32), {condition, ReadOperand(inst.src1, IR::Type::F32),
ReadOperand(inst.src[2], IR::Type::F32)}); ReadOperand(inst.src0, IR::Type::F32)});
} else {
result = ir.Select(condition, ReadU32(inst.src1), ReadU32(inst.src0));
}
break; break;
} }
case IR::Opcode::SelectU64: { case Decoder::Opcode::S_PACK_LL_B32_B16:
const auto condition = ReadCondition(inst.src[0]); case Decoder::Opcode::S_PACK_LH_B32_B16:
const auto selected_mask = case Decoder::Opcode::S_PACK_HH_B32_B16:
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, case Decoder::Opcode::V_CVT_PK_U16_U32:
{condition, ReadMask(inst.src[1]), ReadMask(inst.src[2])})); case Decoder::Opcode::V_CVT_PK_I16_I32: {
const auto selected_mask_valid = const bool high0 = inst.opcode == Decoder::Opcode::S_PACK_HH_B32_B16;
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMaskValid(inst.src[1]), const bool high1 = inst.opcode == Decoder::Opcode::S_PACK_LH_B32_B16 || high0;
ReadMaskValid(inst.src[2])}));
if (inst.dst.kind == IR::OperandKind::Register &&
(inst.dst.reg.file == IR::RegisterFile::Exec ||
inst.dst.reg.file == IR::RegisterFile::Vcc)) {
WriteMask64(inst.dst, selected_mask);
return true;
}
const auto lhs = ReadU32Pair(inst.src[1]);
const auto rhs = ReadU32Pair(inst.src[2]);
WriteU32Pair(inst.dst, {ir.Select(condition, lhs[0], rhs[0]),
ir.Select(condition, lhs[1], rhs[1])});
if (inst.dst.kind == IR::OperandKind::Register &&
inst.dst.reg.file == IR::RegisterFile::Scalar) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg.index);
ir.SetThreadBitScalarReg(dst, selected_mask);
ir.SetScalarMaskTag(dst, selected_mask_valid);
}
return true;
}
case IR::Opcode::PackLowLowU16:
case IR::Opcode::PackLowHighU16:
case IR::Opcode::PackHighHighU16:
case IR::Opcode::PackU16U32: {
const bool high0 = inst.op == IR::Opcode::PackHighHighU16;
const bool high1 = inst.op == IR::Opcode::PackLowHighU16 || high0;
const auto lo = const auto lo =
high0 ? ir.ShiftRightLogical(ReadU32(inst.src[0]), imm(16)) : ReadU32(inst.src[0]); high0 ? ir.ShiftRightLogical(ReadU32(inst.src0), imm(16)) : ReadU32(inst.src0);
const auto hi = const auto hi =
high1 ? ir.ShiftRightLogical(ReadU32(inst.src[1]), imm(16)) : ReadU32(inst.src[1]); high1 ? ir.ShiftRightLogical(ReadU32(inst.src1), imm(16)) : ReadU32(inst.src1);
result = result =
ir.BitwiseOr(mask(lo, 0xffffu), ir.ShiftLeftLogical(mask(hi, 0xffffu), imm(16))); ir.BitwiseOr(mask(lo, 0xffffu), ir.ShiftLeftLogical(mask(hi, 0xffffu), imm(16)));
break; break;
} }
default: return false; default: return false;
} }
WriteOperand(inst.dst, result); WriteOperand(DestinationOperand(inst), result);
switch (inst.opcode) {
case Decoder::Opcode::S_BFE_U32:
case Decoder::Opcode::S_BFE_I32: ir.SetScc(ir.INotEqual(IR::U32(result), imm(0u))); break;
case Decoder::Opcode::S_BFE_U64:
case Decoder::Opcode::S_QUADMASK_B64:
ir.SetScc(
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
break;
default: break;
}
return true; return true;
} }
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail } // namespace Libs::Graphics::ShaderRecompiler::Frontend
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,12 +1,44 @@
#pragma once #pragma once
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include <cstdint>
#include <string_view>
#include <vector>
namespace Libs::Graphics::ShaderRecompiler::Frontend { namespace Libs::Graphics::ShaderRecompiler::Frontend {
bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result, struct EmbeddedFetchLoad {
const ShaderVertexInputInfo* vertex_input_info, uint32_t pc = 0;
const ShaderPixelInputInfo* pixel_input_info, int attrib_id = -1;
const ShaderComputeInputInfo* compute_input_info, std::string* error); uint32_t components = 0;
std::vector<uint32_t> prolog_loads;
};
struct EmbeddedFetchPlan {
std::vector<EmbeddedFetchLoad> loads;
int32_t vertex_offset_sgpr = -1;
};
struct TranslateOptions {
ShaderType stage = ShaderType::Unknown;
uint32_t wave_size = 64;
uint64_t shader_hash = 0;
uint32_t user_data_base = 0;
uint32_t user_data_count = 64;
uint32_t scratch_dwords = 0;
bool dispatcher_fallback = false;
CFG::FailureKind cfg_failure_kind = CFG::FailureKind::None;
std::string_view fallback_reason;
const ShaderVertexInputInfo* vertex = nullptr;
const ShaderPixelInputInfo* pixel = nullptr;
const ShaderComputeInputInfo* compute = nullptr;
const EmbeddedFetchPlan* embedded_fetch = nullptr;
};
bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
const TranslateOptions& options, IR::Program& result, std::string* error);
} // namespace Libs::Graphics::ShaderRecompiler::Frontend } // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -5,58 +5,59 @@
#include <array> #include <array>
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail { namespace Libs::Graphics::ShaderRecompiler::Frontend {
class Translator { class Translator {
public: public:
Translator(IR::ValueProgram& program, IR::Block* block, uint32_t vector_limit, Translator(IR::Program& program, IR::Block* block, uint32_t vector_limit, uint32_t wave_size)
uint32_t wave_size) : program(program), ir(block), current_vector_limit(vector_limit),
: value_program(program), ir(block), current_vector_limit(vector_limit),
current_wave_size(wave_size) {} current_wave_size(wave_size) {}
bool TranslateBlock(const IR::BasicBlock& source, std::string* error); bool TranslateInstruction(const Decoder::Instruction& inst, std::string* error);
bool AddBranchCondition(const IR::BasicBlock& source, IR::ValueBlockInfo& info, bool TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
std::string* error); uint32_t component_count);
bool AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info, std::string* error);
private: private:
IR::Operand OffsetOperand(const IR::Operand& operand, uint32_t offset); Decoder::Operand SourceAt(const Decoder::Instruction& inst, uint32_t index);
IR::Operand ScalarDestinationOperand(const IR::Operand& operand, uint32_t offset); Decoder::Operand DestinationOperand(const Decoder::Instruction& inst);
IR::Operand PlainOperand(const IR::Operand& operand); Decoder::Operand OffsetOperand(const Decoder::Operand& operand, uint32_t offset);
IR::U32 ReadPcRelativeU32(const IR::Operand& operand); Decoder::Operand ScalarDestinationOperand(const Decoder::Operand& operand, uint32_t offset);
Decoder::Operand PlainOperand(const Decoder::Operand& operand);
std::array<IR::U32, 2> BallotMask(IR::U1 value); std::array<IR::U32, 2> BallotMask(IR::U1 value);
IR::U32 ReadRawU32(const IR::Operand& operand); IR::U32 ReadRawU32(const Decoder::Operand& operand);
IR::U32 ReadScalarCode(uint32_t code); IR::U32 ReadScalarCode(uint32_t code);
IR::U32 ApplyBitSourceModifiers(const IR::Operand& operand, IR::U32 value); IR::U32 ApplyBitSourceModifiers(const Decoder::Operand& operand, IR::U32 value);
IR::Value ReadOperand(const IR::Operand& operand, IR::Type type); IR::Value ReadOperand(const Decoder::Operand& operand, IR::Type type);
IR::U1 ThreadBit(IR::U32 low); IR::U1 ThreadBit(IR::U32 low);
void WriteRawU32(const IR::Operand& operand, IR::U32 value); void WriteRawU32(const Decoder::Operand& operand, IR::U32 value);
IR::F32 ApplyF32ResultModifiers(const IR::Operand& operand, IR::F32 value); IR::F32 ApplyF32ResultModifiers(const Decoder::Operand& operand, IR::F32 value);
void WriteOperand(const IR::Operand& operand, IR::Value value); void WriteOperand(const Decoder::Operand& operand, IR::Value value);
IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high); IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high);
void WriteF16(const IR::Operand& operand, IR::F32 value); void WriteF16(const Decoder::Operand& operand, IR::F32 value);
void WriteU16(const IR::Operand& operand, IR::U32 value); void WriteU16(const Decoder::Operand& operand, IR::U32 value);
IR::U32 ReadU32(const IR::Operand& operand); IR::U32 ReadU32(const Decoder::Operand& operand);
std::array<IR::U32, 2> ReadU32Pair(const IR::Operand& operand); std::array<IR::U32, 2> ReadU32Pair(const Decoder::Operand& operand);
IR::U64 ReadU64(const IR::Operand& operand); IR::U64 ReadU64(const Decoder::Operand& operand);
IR::F32 ReadF16LaneAsF32(const IR::Operand& operand, bool high_lane, bool packed = false); IR::F32 ReadF16LaneAsF32(const Decoder::Operand& operand, bool high_lane, bool packed = false);
IR::F32 ReadF16AsF32(const IR::Operand& operand); IR::F32 ReadF16AsF32(const Decoder::Operand& operand);
IR::F32 ReadMixF32(const IR::Operand& operand); IR::F32 ReadMixF32(const Decoder::Operand& operand);
IR::U32 ReadU16LaneRaw(const IR::Operand& operand, bool high_lane); IR::U32 ReadU16LaneRaw(const Decoder::Operand& operand, bool high_lane);
IR::U32 ReadU16LaneAsU32(const IR::Operand& operand, bool high_lane, bool sign_extend); IR::U32 ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane, bool sign_extend);
IR::U32 ReadU16AsU32(const IR::Operand& operand, bool sign_extend); IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend);
IR::U32 ReadF16LaneBits(const IR::Operand& operand, bool high_lane); IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
std::array<IR::U32, 2> ExtractU64(IR::U64 value); std::array<IR::U32, 2> ExtractU64(IR::U64 value);
void WriteU32Pair(const IR::Operand& operand, const std::array<IR::U32, 2>& value); void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
IR::U1 ReadCondition(const IR::Operand& operand); IR::U1 ReadCondition(const Decoder::Operand& operand);
IR::U32 ConditionBit(const IR::Operand& operand); IR::U32 ConditionBit(const Decoder::Operand& operand);
IR::U1 ReadMask(const IR::Operand& operand); IR::U1 ReadMask(const Decoder::Operand& operand);
IR::U1 ReadMaskValid(const IR::Operand& operand); IR::U1 ReadMaskValid(const Decoder::Operand& operand);
void WriteMask(const IR::Operand& operand, IR::U1 value); void WriteMask(const Decoder::Operand& operand, IR::U1 value);
void WriteMask64(const IR::Operand& operand, IR::U1 value); void WriteMask64(const Decoder::Operand& operand, IR::U1 value);
void WriteCompareResult(const IR::Operand& operand, IR::U1 value); void WriteCompareResult(const Decoder::Operand& operand, IR::U1 value);
IR::MemoryFlags AddMemoryInfo(const IR::Instruction& inst); IR::MemoryFlags AddMemoryInfo(const IR::MemoryInfo& memory, uint32_t pc);
IR::ExportFlags AddExportInfo(const IR::Instruction& inst); IR::ExportFlags AddExportInfo(const Decoder::Instruction& inst);
struct AddressOperands { struct AddressOperands {
IR::Value resource; IR::Value resource;
IR::Value low; IR::Value low;
@@ -67,31 +68,31 @@ private:
IR::U32 offset; IR::U32 offset;
IR::U32 soffset; IR::U32 soffset;
}; };
AddressOperands ReadAddressOperands(const IR::Instruction& inst, uint32_t first_source); AddressOperands ReadAddressOperands(const Decoder::Instruction& inst, uint32_t first_source);
IR::U32 GetResourceDword(uint32_t index, uint32_t dword); IR::U32 GetResourceDword(uint32_t index, uint32_t dword);
IR::Value GetBufferResource(const IR::MemoryInfo& memory); IR::Value GetBufferResource(const IR::MemoryInfo& memory);
IR::Value GetAddressResource(IR::Value low, IR::Value high); IR::Value GetAddressResource(IR::Value low, IR::Value high);
IR::Value GetScalarAddressResource(uint32_t base); IR::Value GetScalarAddressResource(uint32_t base);
IR::Value GetImageResource(const IR::MemoryInfo& memory); IR::Value GetImageResource(const IR::MemoryInfo& memory);
IR::Value GetSamplerResource(const IR::MemoryInfo& memory); IR::Value GetSamplerResource(const IR::MemoryInfo& memory);
IR::Value MakeImageAddress(const IR::Instruction& inst, const IR::Operand& base); IR::Value MakeImageAddress(const Decoder::Instruction& inst, const Decoder::Operand& base);
IR::Value ConstructU32x4(const IR::Operand& base, uint32_t count); IR::Value ConstructU32x4(const Decoder::Operand& base, uint32_t count);
void WriteImageComponents(const IR::Operand& dst, IR::Value value, const IR::MemoryInfo& memory, void WriteImageComponents(const Decoder::Operand& dst, IR::Value value,
uint32_t component_limit); const IR::MemoryInfo& memory, uint32_t component_limit);
IR::ValueOpcode ImageAtomicOpcode(IR::Opcode opcode); IR::ValueOpcode ImageAtomicOpcode(Decoder::Opcode opcode);
BufferAddress ReadBufferAddress(const IR::Instruction& inst, uint32_t source_offset); BufferAddress ReadBufferAddress(const Decoder::Instruction& inst, uint32_t source_offset);
IR::U32 WidenSubdword(IR::Value value, uint32_t bits, bool sign); IR::U32 WidenSubdword(IR::Value value, uint32_t bits, bool sign);
IR::Value NarrowSubdword(IR::U32 value, uint32_t bits); IR::Value NarrowSubdword(IR::U32 value, uint32_t bits);
IR::ValueOpcode BufferAtomicOpcode(IR::Opcode opcode); IR::ValueOpcode BufferAtomicOpcode(Decoder::Opcode opcode);
IR::ValueOpcode SharedAtomicOpcode(IR::Opcode opcode); IR::ValueOpcode SharedAtomicOpcode(Decoder::Opcode opcode);
bool TranslateScalarMemory(const IR::Instruction& inst); bool TranslateScalarMemory(const Decoder::Instruction& inst, std::string* error);
bool TranslateBufferLoad(const IR::Instruction& inst); bool TranslateBufferLoad(const Decoder::Instruction& inst, std::string* error);
bool TranslateBufferStore(const IR::Instruction& inst); bool TranslateBufferStore(const Decoder::Instruction& inst, std::string* error);
bool TranslateAtomicMemory(const IR::Instruction& inst); bool TranslateAtomicMemory(const Decoder::Instruction& inst, std::string* error);
bool TranslateFlatLoad(const IR::Instruction& inst); bool TranslateFlatLoad(const Decoder::Instruction& inst, std::string* error);
bool TranslateFlatStore(const IR::Instruction& inst); bool TranslateFlatStore(const Decoder::Instruction& inst, std::string* error);
bool TranslateImageMemory(const IR::Instruction& inst); bool TranslateImageMemory(const Decoder::Instruction& inst, std::string* error);
bool TranslateSharedMemory(const IR::Instruction& inst); bool TranslateSharedMemory(const Decoder::Instruction& inst, std::string* error);
IR::F32 SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value); IR::F32 SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value);
IR::U32 ConvertF32ToU32Saturated(IR::F32 value, float upper_bound, float safe_upper, IR::U32 ConvertF32ToU32Saturated(IR::F32 value, float upper_bound, float safe_upper,
@@ -101,34 +102,33 @@ private:
uint32_t upper_result); uint32_t upper_result);
IR::U32 PackU16Lanes(IR::U32 low, IR::U32 high); IR::U32 PackU16Lanes(IR::U32 low, IR::U32 high);
bool TranslateInstruction(const IR::Instruction& inst); bool TranslateStateOperation(const Decoder::Instruction& inst);
bool TranslateStateOperation(const IR::Instruction& inst); bool TranslateControlOperation(const Decoder::Instruction& inst);
bool TranslateControlOperation(const IR::Instruction& inst); bool TranslateMove(const Decoder::Instruction& inst, std::string* error);
bool TranslateMove(const IR::Instruction& inst); bool TranslateLaneOperation(const Decoder::Instruction& inst, std::string* error);
bool TranslateLaneOperation(const IR::Instruction& inst); bool TranslateAttributeOperation(const Decoder::Instruction& inst, std::string* error);
bool TranslateAttributeOperation(const IR::Instruction& inst); bool TranslateMemoryOperation(const Decoder::Instruction& inst, std::string* error);
bool TranslateMemoryOperation(const IR::Instruction& inst); bool TranslateIntegerCompare(const Decoder::Instruction& inst);
bool TranslateIntegerCompare(const IR::Instruction& inst); bool TranslateInteger16Compare(const Decoder::Instruction& inst);
bool TranslateInteger16Compare(const IR::Instruction& inst); bool TranslateFloatCompare(const Decoder::Instruction& inst);
bool TranslateFloatCompare(const IR::Instruction& inst); bool TranslateConversion(const Decoder::Instruction& inst);
bool TranslateConversion(const IR::Instruction& inst); bool TranslateInteger16Operation(const Decoder::Instruction& inst);
bool TranslateInteger16Operation(const IR::Instruction& inst); bool TranslatePackedInteger16(const Decoder::Instruction& inst);
bool TranslatePackedInteger16(const IR::Instruction& inst); bool TranslatePackedFloat16(const Decoder::Instruction& inst);
bool TranslatePackedFloat16(const IR::Instruction& inst); bool TranslateFloat16Operation(const Decoder::Instruction& inst);
bool TranslateFloat16Operation(const IR::Instruction& inst); bool TranslateFloatOperation(const Decoder::Instruction& inst);
bool TranslateFloatOperation(const IR::Instruction& inst); IR::U1 EvaluateU64Mask(const Decoder::Instruction& inst);
IR::U1 EvaluateU64Mask(const IR::Instruction& inst); bool TranslateU64MaskOperation(const Decoder::Instruction& inst);
bool TranslateU64MaskOperation(const IR::Instruction& inst); bool TranslateSimpleInteger(const Decoder::Instruction& inst);
bool TranslateSimpleInteger(const IR::Instruction& inst); bool TranslateComposedInteger(const Decoder::Instruction& inst);
bool TranslateComposedInteger(const IR::Instruction& inst); bool TranslateExtendedInteger(const Decoder::Instruction& inst);
bool TranslateExtendedInteger(const IR::Instruction& inst);
IR::ValueProgram& value_program; IR::Program& program;
IR::IREmitter ir; IR::IREmitter ir;
IR::Opcode current_opcode = IR::Opcode::ControlNop; Decoder::Opcode current_opcode = Decoder::Opcode::UNKNOWN;
uint32_t current_pc = 0; uint32_t current_pc = 0;
uint32_t current_vector_limit = 1; uint32_t current_vector_limit = 1;
uint32_t current_wave_size = 64; uint32_t current_wave_size = 64;
}; };
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail } // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -1,11 +1,13 @@
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
#include <fmt/format.h> #include <fmt/format.h>
#include <map> #include <map>
#include <new>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <utility>
namespace Libs::Graphics::ShaderRecompiler::IR { namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -50,7 +52,7 @@ bool IsRuntimeRead(ValueOpcode opcode) {
AddressOpcodeInfoOf(opcode).access == AddressAccess::Read; AddressOpcodeInfoOf(opcode).access == AddressAccess::Read;
} }
bool EquivalentValue(const ValueProgram& program, Value left, Value right, bool EquivalentValue(const Program& program, Value left, Value right,
std::vector<std::pair<const Inst*, const Inst*>>& visited) { std::vector<std::pair<const Inst*, const Inst*>>& visited) {
left = left.Resolve(); left = left.Resolve();
right = right.Resolve(); right = right.Resolve();
@@ -93,7 +95,7 @@ bool EquivalentValue(const ValueProgram& program, Value left, Value right,
} // namespace } // namespace
ValueProgram::~ValueProgram() { Program::~Program() {
// Values may cross block boundaries. Detach all arguments before any block starts destroying // Values may cross block boundaries. Detach all arguments before any block starts destroying
// its instruction storage so reverse-use links always point to live definitions. // its instruction storage so reverse-use links always point to live definitions.
for (auto* block: blocks) { for (auto* block: blocks) {
@@ -103,12 +105,20 @@ ValueProgram::~ValueProgram() {
} }
} }
bool EquivalentValue(const ValueProgram& program, Value left, Value right) { Program& Program::operator=(Program&& other) noexcept {
if (this != &other) {
this->~Program();
new (this) Program(std::move(other));
}
return *this;
}
bool EquivalentValue(const Program& program, Value left, Value right) {
std::vector<std::pair<const Inst*, const Inst*>> visited; std::vector<std::pair<const Inst*, const Inst*>> visited;
return EquivalentValue(program, left, right, visited); return EquivalentValue(program, left, right, visited);
} }
Value ResolveInvariantPhi(const ValueProgram& program, Value value) { Value ResolveInvariantPhi(const Program& program, Value value) {
value = value.Resolve(); value = value.Resolve();
const auto* root = value.TryInstruction(); const auto* root = value.TryInstruction();
if (root == nullptr || root->GetOpcode() != ValueOpcode::Phi) { if (root == nullptr || root->GetOpcode() != ValueOpcode::Phi) {
@@ -139,7 +149,7 @@ Value ResolveInvariantPhi(const ValueProgram& program, Value value) {
return invariant; return invariant;
} }
bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::string* error) { bool ValidateProgram(const Program& program, bool require_ssa, std::string* error) {
if (program.blocks.size() != program.block_info.size() || if (program.blocks.size() != program.block_info.size() ||
program.blocks.size() != program.block_storage.size()) { program.blocks.size() != program.block_storage.size()) {
return Fail(error, "value IR block storage is inconsistent"); return Fail(error, "value IR block storage is inconsistent");
@@ -591,14 +601,14 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
return true; return true;
} }
void ResolveControlFlowIdentities(ValueProgram& program) { void ResolveControlFlowIdentities(Program& program) {
for (auto& info: program.block_info) { for (auto& info: program.block_info) {
info.condition = info.condition.Resolve(); info.condition = info.condition.Resolve();
info.indirect_target = info.indirect_target.Resolve(); info.indirect_target = info.indirect_target.Resolve();
} }
} }
std::string ValueProgramToString(const ValueProgram& program) { std::string ProgramToString(const Program& program) {
std::map<const Inst*, size_t> ids; std::map<const Inst*, size_t> ids;
size_t next_id = 1; size_t next_id = 1;
for (const auto* block: program.blocks) { for (const auto* block: program.blocks) {
File diff suppressed because it is too large. Load diff
+61 -89
View File
@@ -6,7 +6,7 @@
#include "graphics/guest_gpu/gpu_defs.h" #include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h" #include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "graphics/shader/recompiler/ir/opcodes/Opcodes.h" #include "graphics/shader/recompiler/ir/Block.h"
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.h" #include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
@@ -19,47 +19,6 @@
namespace Libs::Graphics::ShaderRecompiler::IR { namespace Libs::Graphics::ShaderRecompiler::IR {
struct ValueProgram;
enum class RegisterFile { Scalar, Vector, Vcc, Exec, Scc, M0 };
struct Register {
RegisterFile file = RegisterFile::Scalar;
uint32_t index = 0;
bool operator==(const Register& other) const {
return file == other.file && index == other.index;
}
};
enum class OperandKind { Register, ImmediateU32, PcRelativeU32, PcRelativeHighU32, Null };
struct Operand {
OperandKind kind = OperandKind::Null;
Register reg;
uint32_t imm = 0;
bool float_inline = false;
bool sext_64 = false;
uint32_t sdwa_sel = 6;
uint32_t sdwa_dst_unused = 2;
uint32_t omod = 0;
bool sdwa_sext = false;
bool op_sel = false;
bool op_sel_hi = false;
bool negate = false;
bool negate_hi = false;
bool absolute = false;
bool clamp = false;
uint32_t dpp_ctrl = 0;
uint32_t dpp_row_mask = 0xf;
uint32_t dpp_bank_mask = 0xf;
bool dpp_fetch_inactive = false;
bool dpp_bound_ctrl = false;
bool dpp = false;
bool operator==(const Operand& other) const = default;
};
enum class ResourceKind { enum class ResourceKind {
None, None,
ScalarBuffer, ScalarBuffer,
@@ -144,44 +103,6 @@ struct ExportInfo {
bool operator==(const ExportInfo& other) const = default; bool operator==(const ExportInfo& other) const = default;
}; };
struct InputInfo {
uint32_t attr = 0;
uint32_t chan = 0;
uint32_t component_count = 1;
uint32_t interpolation_mode = 3;
bool operator==(const InputInfo& other) const = default;
};
enum class SaveexecMode { And, Orn2, Andn1 };
struct Instruction {
uint32_t pc = 0;
Opcode op = Opcode::MoveU32;
Operand dst;
Operand dst2;
Operand src[4];
uint32_t src_count = 0;
MemoryInfo memory;
ExportInfo export_info;
InputInfo input_info;
SaveexecMode saveexec_mode = SaveexecMode::And;
bool operator==(const Instruction& other) const = default;
};
struct BasicBlock {
uint32_t id = 0;
uint32_t start_pc = 0;
uint32_t end_pc = 0;
uint32_t inst_begin = 0;
uint32_t inst_end = 0;
std::vector<uint32_t> predecessors;
std::vector<uint32_t> successors;
CFG::Terminator terminator;
std::vector<Instruction> instructions;
};
struct DescriptorValue { struct DescriptorValue {
std::array<uint32_t, 8> dwords = {}; std::array<uint32_t, 8> dwords = {};
uint32_t dword_count = 0; uint32_t dword_count = 0;
@@ -522,6 +443,7 @@ struct ShaderInfo {
std::vector<SampledResourcePair> sampled_pairs; std::vector<SampledResourcePair> sampled_pairs;
std::vector<StageInput> inputs; std::vector<StageInput> inputs;
std::vector<StageOutput> outputs; std::vector<StageOutput> outputs;
std::array<uint8_t, 32> vertex_fetch_components {};
int32_t vertex_offset_sgpr = -1; int32_t vertex_offset_sgpr = -1;
bool has_bitwise_xor = false; bool has_bitwise_xor = false;
@@ -539,7 +461,50 @@ struct SpirvRequirements {
bool pixel_valid_mask = false; bool pixel_valid_mask = false;
}; };
struct BlockInfo {
uint32_t id = 0;
uint32_t start_pc = 0;
uint32_t end_pc = 0;
CFG::Terminator terminator;
Value condition;
Value indirect_target;
};
struct DescriptorSource {
struct IndirectImage {
uint32_t material_source = 0;
uint32_t heap_source = 0;
uint32_t selector_stride = 0;
uint32_t selector_offset = 0;
uint32_t key_arg = 0;
bool operator==(const IndirectImage& other) const = default;
};
std::array<Value, 8> dwords {};
uint32_t dword_count = 0;
std::optional<IndirectImage> indirect_image;
bool operator==(const DescriptorSource& other) const = default;
};
struct SrtRead {
Value value;
uint32_t flat_offset = 0;
uint32_t use_pc = 0;
bool operator==(const SrtRead& other) const = default;
};
struct Program { struct Program {
Program() = default;
~Program();
Program(const Program&) = delete;
Program& operator=(const Program&) = delete;
Program(Program&&) noexcept = default;
Program& operator=(Program&& other) noexcept;
ShaderType stage = ShaderType::Unknown; ShaderType stage = ShaderType::Unknown;
uint64_t shader_hash = 0; uint64_t shader_hash = 0;
uint32_t wave_size = 64; uint32_t wave_size = 64;
@@ -549,8 +514,17 @@ struct Program {
bool dispatcher_fallback = false; bool dispatcher_fallback = false;
CFG::FailureKind cfg_failure_kind = CFG::FailureKind::None; CFG::FailureKind cfg_failure_kind = CFG::FailureKind::None;
std::string fallback_reason; std::string fallback_reason;
std::vector<BasicBlock> blocks; std::vector<std::unique_ptr<Block>> block_storage;
std::shared_ptr<ValueProgram> values; BlockList blocks;
std::vector<BlockInfo> block_info;
// Decoded MIMG/VMEM metadata carries details such as RDNA2 NSA address registers and
// storage-image swizzles. Typed memory instructions carry a dense index into these shader-local
// tables until those fields are consumed by emission.
std::vector<MemoryInfo> memory_info;
std::vector<ExportInfo> export_info;
std::vector<DescriptorSource> descriptor_sources;
std::vector<SrtRead> srt_reads;
std::vector<Value> dynamic_reads;
bool srt_plan_complete = false; bool srt_plan_complete = false;
ShaderInfo info; ShaderInfo info;
bool resource_tracking_complete = false; bool resource_tracking_complete = false;
@@ -561,15 +535,13 @@ struct Program {
std::optional<SpirvRequirements> spirv_requirements; std::optional<SpirvRequirements> spirv_requirements;
}; };
bool LowerProgram(const Decoder::Program& decoded, const CFG::Graph& cfg, ShaderType stage,
uint32_t wave_size, Program& program, std::string* error);
std::string RegisterToString(Register reg);
std::string OperandToString(const Operand& operand);
std::string ExportTargetKindToString(ExportTargetKind kind);
std::string InstructionToString(const Instruction& inst);
std::string ProgramToString(const Program& program); std::string ProgramToString(const Program& program);
bool ValidateProgram(const Program& program, bool require_ssa, std::string* error);
void ResolveControlFlowIdentities(Program& program);
bool EquivalentValue(const Program& program, Value left, Value right);
Value ResolveInvariantPhi(const Program& program, Value value);
} // namespace Libs::Graphics::ShaderRecompiler::IR } // namespace Libs::Graphics::ShaderRecompiler::IR
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERIR_H_ */ #endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERIR_H_ */
@@ -1,51 +0,0 @@
#ifndef EMULATOR_SRC_GRAPHICS_SHADER_RECOMPILER_SHADERIR_SHADERIRINTERNAL_H_
#define EMULATOR_SRC_GRAPHICS_SHADER_RECOMPILER_SHADERIR_SHADERIRINTERNAL_H_
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include <optional>
namespace Libs::Graphics::ShaderRecompiler::IR {
void SetError(std::string* error, const char* message);
std::optional<Opcode> LookupIrOpcode(Decoder::Opcode opcode);
bool IsReversedBinary(Decoder::Opcode opcode);
bool IsVectorCarryOutOpcode(Decoder::Opcode opcode);
bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode);
bool ScalarResultIs64Bit(Decoder::Opcode opcode);
Operand MakeSccOperand();
Operand MakeImmediateU32(uint32_t value);
Operand MakePcRelativeU32(uint32_t value);
Operand MakePcRelativeHighU32(uint32_t value);
bool AppendScalarResultSccNonZero(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error);
uint32_t VectorByteConvertIndex(Decoder::Opcode opcode);
void CopyOperandModifiers(const Decoder::Operand& decoded, Operand& operand);
bool LowerRegisterOperand(const Decoder::Operand& decoded, Operand& operand, std::string* error);
bool LowerSourceOperand(const Decoder::Operand& decoded, Operand& operand, std::string* error);
void ApplyDppDestinationMask(const Decoder::Instruction& decoded, Operand& dst);
uint32_t ResourceIndexFromOperand(const Decoder::Operand& operand);
MemoryInfo MemoryInfoFromDecoded(const Decoder::Instruction& decoded, ResourceKind kind);
uint32_t RawScalarLoadBase(const Decoder::Operand& operand);
void ClearRegisterOffsetModifiers(Decoder::Operand& operand);
bool TryGetEncodedScalarCode(const Decoder::Operand& operand, uint32_t& code);
bool TryOffsetScalarDestination(const Decoder::Operand& operand, uint32_t index,
Decoder::Operand& result);
Decoder::Operand OffsetDecodedRegister(const Decoder::Operand& operand, uint32_t index);
MemoryInfo OffsetMemoryInfo(const Decoder::Instruction& decoded, ResourceKind kind,
uint32_t dword_index);
MemoryInfo ByteOffsetMemoryInfo(const Decoder::Instruction& decoded, ResourceKind kind,
uint32_t byte_offset);
ResourceKind FlatSegmentResourceKind(uint32_t segment);
const Decoder::Operand& DecodedSourceAt(const Decoder::Instruction& decoded, uint32_t index);
Decoder::Operand M0Operand();
bool IsMemoryOpcode(Decoder::Opcode opcode);
bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error);
} // namespace Libs::Graphics::ShaderRecompiler::IR
#endif // EMULATOR_SRC_GRAPHICS_SHADER_RECOMPILER_SHADERIR_SHADERIRINTERNAL_H_
@@ -1,214 +0,0 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIRInternal.h"
#include <algorithm>
#include <fmt/format.h>
#include <utility>
namespace Libs::Graphics::ShaderRecompiler::IR {
std::string RegisterToString(Register reg) {
switch (reg.file) {
case RegisterFile::Scalar: return fmt::format("s{}", reg.index);
case RegisterFile::Vector: return fmt::format("v{}", reg.index);
case RegisterFile::Vcc:
return reg.index == 0 ? std::string("vcc_lo") : std::string("vcc_hi");
case RegisterFile::Exec:
return reg.index == 0 ? std::string("exec_lo") : std::string("exec_hi");
case RegisterFile::Scc:
if (reg.index == static_cast<uint32_t>(Decoder::OperandKind::Scc)) {
return "scc";
}
if (reg.index == static_cast<uint32_t>(Decoder::OperandKind::VccZ)) {
return "vccz";
}
if (reg.index == static_cast<uint32_t>(Decoder::OperandKind::ExecZ)) {
return "execz";
}
return fmt::format("scc_like_{}", reg.index);
case RegisterFile::M0: return "m0";
default: return "unknown_reg";
}
}
std::string OperandToString(const Operand& operand) {
std::string text;
switch (operand.kind) {
case OperandKind::Register: text = RegisterToString(operand.reg); break;
case OperandKind::ImmediateU32: text = fmt::format("0x{:08x}", operand.imm); break;
case OperandKind::PcRelativeU32: text = fmt::format("pc_rel(0x{:08x})", operand.imm); break;
case OperandKind::PcRelativeHighU32:
text = fmt::format("pc_rel_hi(0x{:08x})", operand.imm);
break;
case OperandKind::Null: text = "null"; break;
default: text = "unknown"; break;
}
if (operand.sdwa_sel != 6 || operand.sdwa_sext) {
text += fmt::format(".sdwa(sel={},sext={})", operand.sdwa_sel, operand.sdwa_sext ? 1u : 0u);
}
if (operand.absolute) {
text += ".abs";
}
if (operand.negate) {
text += ".neg";
}
if (operand.op_sel || operand.op_sel_hi || operand.negate_hi) {
text += fmt::format(".opsel(lo={},hi={},neghi={})", operand.op_sel ? 1u : 0u,
operand.op_sel_hi ? 1u : 0u, operand.negate_hi ? 1u : 0u);
}
if (operand.omod != 0) {
text += fmt::format(".omod({})", operand.omod);
}
if (operand.clamp) {
text += ".clamp";
}
if (operand.dpp) {
text += fmt::format(".dpp(ctrl=0x{:x},fi={},bc={})", operand.dpp_ctrl,
operand.dpp_fetch_inactive ? 1u : 0u, operand.dpp_bound_ctrl ? 1u : 0u);
}
return text;
}
std::string ResourceKindToString(ResourceKind kind) {
switch (kind) {
case ResourceKind::ScalarBuffer: return "scalar_buffer";
case ResourceKind::ScalarAddress: return "scalar_address";
case ResourceKind::Buffer: return "buffer";
case ResourceKind::Flat: return "flat";
case ResourceKind::Global: return "global";
case ResourceKind::Scratch: return "scratch";
case ResourceKind::Lds: return "lds";
case ResourceKind::Gds: return "gds";
case ResourceKind::Image: return "image";
case ResourceKind::ImageUint: return "image_uint";
case ResourceKind::StorageImage: return "storage_image";
case ResourceKind::StorageImageUint: return "storage_image_uint";
case ResourceKind::Sampler: return "sampler";
default: return "none";
}
}
std::string MemoryInfoToString(const MemoryInfo& mem) {
if (mem.kind == ResourceKind::None) {
return "";
}
std::string text = fmt::format(
" ; {} resource={} sampler={} offset={} offset2={} dmask=0x{:x} data_dwords={} "
"data_bits={} component={}/{} dfmt={} nfmt={} signed={} typed={} formatted={} segment={} "
"glc={} slc={} idxen={} offen={} image_flags=0x{:x} image_dim={} image_addr={} "
"image_mip={} image_r128={}",
ResourceKindToString(mem.kind).c_str(), mem.resource, mem.sampler, mem.offset,
mem.secondary_offset, mem.dmask, mem.data_dwords, mem.data_bits, mem.component_index,
mem.component_count, mem.data_format, mem.number_format, mem.data_signed ? 1u : 0u,
mem.typed ? 1u : 0u, mem.formatted ? 1u : 0u, mem.memory_segment, mem.glc ? 1u : 0u,
mem.slc ? 1u : 0u, mem.idxen ? 1u : 0u, mem.offen ? 1u : 0u, mem.image_sample_flags,
Decoder::ImageDimensionToString(mem.image_dimension), mem.image_address_components,
mem.image_has_mip ? 1u : 0u, mem.image_r128 ? 1u : 0u);
if (mem.image_nsa_dwords != 0) {
text += fmt::format(" image_nsa_dwords={} image_nsa_addr=", mem.image_nsa_dwords);
const auto count =
std::min<uint32_t>(mem.image_nsa_dwords * 4u, Decoder::MaxImageNsaAddressComponents);
for (uint32_t i = 0; i < count; i++) {
if (i != 0) {
text += ",";
}
text += fmt::format("{}", mem.image_nsa_addr[i]);
}
}
return text;
}
std::string ExportInfoToString(const ExportInfo& exp) {
return fmt::format(" ; target={} index={} raw=0x{:02x} en=0x{:x} done={} compr={} vm={}",
ExportTargetKindToString(exp.kind).c_str(), exp.index, exp.target, exp.en,
exp.done ? 1u : 0u, exp.compr ? 1u : 0u, exp.vm ? 1u : 0u);
}
std::string InputInfoToString(Opcode op, const InputInfo& input) {
if (op != Opcode::LoadInputF32) {
return "";
}
return fmt::format(" ; input_attr={} input_chan={} input_components={}", input.attr, input.chan,
input.component_count);
}
std::string TerminatorToString(const CFG::Terminator& term) {
return fmt::format(
"term={} cond={} true={} false={} merge={} continue={} loop={} indirect_sgpr={} "
"indirect_selector={} indirect_targets={} selector_values={}",
static_cast<uint32_t>(term.kind), CFG::BranchConditionToString(term.condition).c_str(),
term.true_block, term.false_block, term.merge_block, term.continue_block,
term.loop_header ? 1u : 0u, term.indirect_pc_sgpr, term.indirect_selector_code,
static_cast<uint32_t>(term.indirect_targets.size()),
static_cast<uint32_t>(term.indirect_selector_values.size()));
}
std::string ExportSourcesToString(const Instruction& inst) {
std::string text;
for (uint32_t i = 0; i < inst.src_count && i < 4u; i++) {
text += i == 0 ? " " : ", ";
text += OperandToString(inst.src[i]);
}
return text;
}
std::string InstructionToString(const Instruction& inst) {
std::string text = fmt::format("0x{:08x}: ", inst.pc);
if (inst.op == Opcode::Export) {
text += "Export";
text += ExportSourcesToString(inst);
text += ExportInfoToString(inst.export_info);
return text;
}
text += OpcodeName(inst.op);
text += " ";
text += OperandToString(inst.dst);
if (inst.dst2.kind != OperandKind::Null) {
text += ", ";
text += OperandToString(inst.dst2);
}
for (uint32_t i = 0; i < inst.src_count; i++) {
text += i == 0 ? ", " : ", ";
text += OperandToString(inst.src[i]);
}
text += MemoryInfoToString(inst.memory);
text += InputInfoToString(inst.op, inst.input_info);
return text;
}
std::string ExportTargetKindToString(ExportTargetKind kind) {
switch (kind) {
case ExportTargetKind::Null: return "null";
case ExportTargetKind::Position: return "position";
case ExportTargetKind::Primitive: return "primitive";
case ExportTargetKind::Parameter: return "parameter";
case ExportTargetKind::Mrt: return "mrt";
case ExportTargetKind::MrtZ: return "mrtz";
default: return "unknown";
}
}
std::string ProgramToString(const Program& program) {
std::string text;
text += fmt::format("mode={} fallback={} reason={}\n",
program.dispatcher_fallback ? "dispatcher" : "structured",
CFG::FailureKindToString(program.cfg_failure_kind).c_str(),
program.fallback_reason.c_str());
for (const auto& block: program.blocks) {
text += fmt::format("block_{} pc=0x{:08x} end=0x{:08x} preds={} succs={} {}\n", block.id,
block.start_pc, block.end_pc,
static_cast<uint32_t>(block.predecessors.size()),
static_cast<uint32_t>(block.successors.size()),
TerminatorToString(block.terminator).c_str());
for (const auto& inst: block.instructions) {
text += " ";
text += InstructionToString(inst);
text += "\n";
}
}
return text;
}
} // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,812 +0,0 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIRInternal.h"
#include <algorithm>
#include <fmt/format.h>
#include <utility>
namespace Libs::Graphics::ShaderRecompiler::IR {
namespace {
Opcode BufferLoadOpcode(const Decoder::Instruction& decoded) {
switch (decoded.data_bits) {
case 8u: return decoded.data_signed ? Opcode::BufferLoadSbyte : Opcode::BufferLoadUbyte;
case 16u: return decoded.data_signed ? Opcode::BufferLoadSshort : Opcode::BufferLoadUshort;
default: return Opcode::BufferLoadDword;
}
}
Opcode BufferStoreOpcode(uint32_t data_bits) {
switch (data_bits) {
case 8u: return Opcode::BufferStoreByte;
case 16u: return Opcode::BufferStoreShort;
default: return Opcode::BufferStoreDword;
}
}
Opcode DsReadOpcode(const Decoder::Instruction& decoded) {
switch (decoded.data_bits) {
case 8u: return decoded.data_signed ? Opcode::DsReadSbyte : Opcode::DsReadUbyte;
case 16u: return decoded.data_signed ? Opcode::DsReadSshort : Opcode::DsReadUshort;
default: return Opcode::DsReadB32;
}
}
Opcode FlatLoadOpcode(const Decoder::Instruction& decoded) {
switch (decoded.data_bits) {
case 8u: return decoded.data_signed ? Opcode::FlatLoadSbyte : Opcode::FlatLoadUbyte;
case 16u: return decoded.data_signed ? Opcode::FlatLoadSshort : Opcode::FlatLoadUshort;
default: return Opcode::FlatLoadDword;
}
}
Opcode FlatStoreOpcode(uint32_t data_bits) {
switch (data_bits) {
case 8u: return Opcode::FlatStoreByte;
case 16u: return Opcode::FlatStoreShort;
default: return Opcode::FlatStoreDword;
}
}
Opcode DsWriteOpcode(uint32_t data_bits) {
switch (data_bits) {
case 8u: return Opcode::DsWriteByte;
case 16u: return Opcode::DsWriteShort;
default: return Opcode::DsWriteB32;
}
}
Opcode LoweredImageOpcode(Decoder::Opcode opcode) {
switch (opcode) {
case Decoder::Opcode::IMAGE_GET_RESINFO: return Opcode::ImageGetResinfo;
case Decoder::Opcode::IMAGE_GET_LOD: return Opcode::ImageGetLod;
case Decoder::Opcode::IMAGE_LOAD:
case Decoder::Opcode::IMAGE_LOAD_MIP: return Opcode::ImageLoad;
case Decoder::Opcode::IMAGE_GATHER4_LZ:
case Decoder::Opcode::IMAGE_GATHER4_C:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ:
case Decoder::Opcode::IMAGE_GATHER4_LZ_O:
case Decoder::Opcode::IMAGE_GATHER4_C_O:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ_O:
case Decoder::Opcode::IMAGE_GATHER4H: return Opcode::ImageGather4;
default: return Opcode::ImageSample;
}
}
bool LowerScalarMemoryLoadDword(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
std::string* error) {
uint32_t destination_code = 0;
const auto alignment = decoded.data_dwords == 1u ? 1u
: decoded.data_dwords == 2u ? 2u
: 4u;
if (!TryGetEncodedScalarCode(decoded.dst, destination_code) ||
destination_code % alignment != 0u ||
destination_code + decoded.data_dwords > 108u) {
SetError(error, "scalar-memory destination has an invalid aligned SGPR span");
return false;
}
uint32_t base_code = 0;
const auto base_alignment = op == Opcode::SLoadDword ? 2u : 4u;
const auto base_width = op == Opcode::SLoadDword ? 2u : 4u;
if (!TryGetEncodedScalarCode(decoded.src0, base_code) ||
base_code % base_alignment != 0u || base_code + base_width > 108u) {
SetError(error, "scalar-memory base has an invalid aligned SGPR span");
return false;
}
Instruction inst;
inst.pc = decoded.pc;
inst.op = op;
inst.src_count = 1;
const auto kind =
op == Opcode::SLoadDword ? ResourceKind::ScalarAddress : ResourceKind::ScalarBuffer;
inst.memory = MemoryInfoFromDecoded(decoded, kind);
// Raw s_load uses a two-SGPR pointer base; s_buffer_load uses a four-SGPR
// descriptor resource index.
inst.memory.resource = op == Opcode::SLoadDword ? RawScalarLoadBase(decoded.src0)
: ResourceIndexFromOperand(decoded.src0);
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerSourceOperand(decoded.src1, inst.src[0], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerBufferAddressSources(const Decoder::Instruction& decoded, Instruction& inst,
uint32_t first_src, std::string* error) {
uint32_t src = first_src;
if (decoded.idxen) {
if (!LowerSourceOperand(decoded.src0, inst.src[src++], error)) {
return false;
}
}
if (decoded.offen) {
const auto offset_source =
decoded.idxen ? OffsetDecodedRegister(decoded.src0, 1) : decoded.src0;
if (!LowerSourceOperand(offset_source, inst.src[src++], error)) {
return false;
}
}
if (!LowerSourceOperand(decoded.src2, inst.src[src++], error)) {
return false;
}
inst.src_count = src;
return true;
}
bool LowerBufferLoad(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = BufferLoadOpcode(decoded);
inst.memory = MemoryInfoFromDecoded(decoded, ResourceKind::Buffer);
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerBufferAddressSources(decoded, inst, 0, error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerBufferStore(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = BufferStoreOpcode(decoded.data_bits);
inst.memory = MemoryInfoFromDecoded(decoded, ResourceKind::Buffer);
inst.dst.kind = OperandKind::Null;
if (!LowerSourceOperand(decoded.dst, inst.src[0], error) ||
!LowerBufferAddressSources(decoded, inst, 1, error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerBufferAtomicDword(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = op;
inst.memory = MemoryInfoFromDecoded(decoded, ResourceKind::Buffer);
inst.dst.kind = OperandKind::Null;
if ((decoded.glc && !LowerRegisterOperand(decoded.dst, inst.dst, error)) ||
!LowerSourceOperand(decoded.dst, inst.src[0], error) ||
!LowerBufferAddressSources(decoded, inst, 1, error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
ResourceKind DsMemoryKind(const Decoder::Instruction& decoded) {
return decoded.gds ? ResourceKind::Gds : ResourceKind::Lds;
}
bool LowerDsRead(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
const auto ir_op = DsReadOpcode(decoded);
Instruction inst;
inst.pc = decoded.pc;
inst.op = ir_op;
inst.src_count = 1;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerSourceOperand(decoded.src0, inst.src[0], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerDsRead2(const Decoder::Instruction& decoded, BasicBlock& block, uint32_t dwords_per_read,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = Opcode::DsRead2B32;
inst.src_count = 1;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
inst.memory.data_dwords = dwords_per_read * 2u;
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerSourceOperand(decoded.src0, inst.src[0], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerDsAtomicU32(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
bool return_old, std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = op;
inst.src_count = 2;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
inst.dst.kind = return_old ? OperandKind::Register : OperandKind::Null;
if ((return_old && !LowerRegisterOperand(decoded.dst, inst.dst, error)) ||
!LowerSourceOperand(decoded.src1, inst.src[0], error) ||
!LowerSourceOperand(decoded.src0, inst.src[1], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerDsSwizzleB32(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = Opcode::DsSwizzleB32;
inst.src_count = 2;
inst.src[1].kind = OperandKind::ImmediateU32;
inst.src[1].imm = decoded.offset & 0xffffu;
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerSourceOperand(decoded.src0, inst.src[0], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerDsFloatMinMaxF32(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = op;
inst.src_count = 2;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
inst.memory.resource = 0;
inst.dst.kind = OperandKind::Null;
if (!LowerSourceOperand(decoded.src1, inst.src[0], error) ||
!LowerSourceOperand(decoded.src0, inst.src[1], error) ||
!LowerSourceOperand(decoded.src2, inst.src[2], error)) {
return false;
}
inst.src_count = 3;
block.instructions.push_back(inst);
return true;
}
bool LowerDsWriteAddtidB32(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = Opcode::DsWriteAddtidB32;
inst.src_count = 2;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
inst.memory.resource = 0;
inst.dst.kind = OperandKind::Null;
const auto m0 = M0Operand();
if (!LowerSourceOperand(decoded.src1, inst.src[0], error) ||
!LowerSourceOperand(m0, inst.src[1], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerDsReadAddtidB32(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = Opcode::DsReadAddtidB32;
inst.src_count = 1;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
inst.memory.resource = 0;
const auto m0 = M0Operand();
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerSourceOperand(m0, inst.src[0], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerDsAppendConsume(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = op;
inst.src_count = 1;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
const auto m0 = M0Operand();
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerSourceOperand(m0, inst.src[0], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
Opcode ImageAtomicIrOpcode(Decoder::Opcode opcode) {
switch (opcode) {
case Decoder::Opcode::IMAGE_ATOMIC_ADD: return Opcode::AtomicAddU32;
case Decoder::Opcode::IMAGE_ATOMIC_UMIN: return Opcode::AtomicUMinU32;
case Decoder::Opcode::IMAGE_ATOMIC_UMAX: return Opcode::AtomicUMaxU32;
case Decoder::Opcode::IMAGE_ATOMIC_AND: return Opcode::AtomicAndU32;
case Decoder::Opcode::IMAGE_ATOMIC_OR: return Opcode::AtomicOrU32;
case Decoder::Opcode::IMAGE_ATOMIC_XOR: return Opcode::AtomicXorU32;
default: return Opcode::AtomicAddU32;
}
}
bool LowerImageAtomicU32(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = ImageAtomicIrOpcode(decoded.opcode);
inst.src_count = 2;
inst.memory = MemoryInfoFromDecoded(decoded, ResourceKind::StorageImageUint);
inst.memory.sampler = 0;
inst.dst.kind = OperandKind::Null;
if ((decoded.glc && !LowerRegisterOperand(decoded.dst, inst.dst, error)) ||
!LowerSourceOperand(decoded.dst, inst.src[0], error) ||
!LowerSourceOperand(decoded.src0, inst.src[1], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerFlatLoad(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
const auto kind = FlatSegmentResourceKind(decoded.memory_segment);
const auto ir_op = FlatLoadOpcode(decoded);
const auto count = decoded.data_bits == 32u ? decoded.data_dwords : 1u;
if (decoded.data_bits == 32u && count > 1u) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = ir_op;
inst.src_count = decoded.src_count;
inst.memory = MemoryInfoFromDecoded(decoded, kind);
inst.memory.resource = 0;
if (!LowerRegisterOperand(decoded.dst, inst.dst, error)) {
return false;
}
for (uint32_t i = 0; i < decoded.src_count && i < 3u; i++) {
if (!LowerSourceOperand(DecodedSourceAt(decoded, i), inst.src[i], error)) {
return false;
}
}
block.instructions.push_back(inst);
return true;
}
for (uint32_t dword = 0; dword < count; dword++) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = ir_op;
inst.src_count = decoded.src_count;
inst.memory = OffsetMemoryInfo(decoded, kind, dword);
inst.memory.resource = 0;
if (!LowerRegisterOperand(OffsetDecodedRegister(decoded.dst, dword), inst.dst, error)) {
return false;
}
for (uint32_t i = 0; i < decoded.src_count && i < 3u; i++) {
if (!LowerSourceOperand(DecodedSourceAt(decoded, i), inst.src[i], error)) {
return false;
}
}
block.instructions.push_back(inst);
}
return true;
}
bool LowerFlatStore(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
const auto kind = FlatSegmentResourceKind(decoded.memory_segment);
const auto ir_op = FlatStoreOpcode(decoded.data_bits);
const auto count = decoded.data_bits == 32u ? decoded.data_dwords : 1u;
for (uint32_t dword = 0; dword < count; dword++) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = ir_op;
inst.src_count = decoded.src_count + 1u;
inst.memory = OffsetMemoryInfo(decoded, kind, dword);
inst.memory.resource = 0;
inst.dst.kind = OperandKind::Null;
if (!LowerSourceOperand(OffsetDecodedRegister(decoded.dst, dword), inst.src[0], error)) {
return false;
}
for (uint32_t i = 0; i < decoded.src_count && i + 1u < 3u; i++) {
if (!LowerSourceOperand(DecodedSourceAt(decoded, i), inst.src[i + 1u], error)) {
return false;
}
}
block.instructions.push_back(inst);
}
return true;
}
bool LowerDsWrite(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
const auto ir_op = DsWriteOpcode(decoded.data_bits);
Instruction inst;
inst.pc = decoded.pc;
inst.op = ir_op;
inst.src_count = 2;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
inst.dst.kind = OperandKind::Null;
if (!LowerSourceOperand(decoded.src1, inst.src[0], error) ||
!LowerSourceOperand(decoded.src0, inst.src[1], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerDsWrite2(const Decoder::Instruction& decoded, BasicBlock& block,
uint32_t dwords_per_write, std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
inst.op = Opcode::DsWrite2B32;
inst.src_count = 3;
inst.memory = MemoryInfoFromDecoded(decoded, DsMemoryKind(decoded));
inst.memory.data_dwords = dwords_per_write * 2u;
inst.dst.kind = OperandKind::Null;
if (!LowerSourceOperand(decoded.src1, inst.src[0], error) ||
!LowerSourceOperand(decoded.src0, inst.src[1], error) ||
!LowerSourceOperand(decoded.src2, inst.src[2], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
bool LowerImageOperation(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error) {
Instruction inst;
inst.pc = decoded.pc;
if (decoded.opcode == Decoder::Opcode::IMAGE_STORE ||
decoded.opcode == Decoder::Opcode::IMAGE_STORE_MIP) {
inst.op = Opcode::ImageStore;
inst.src_count = 2;
inst.memory = MemoryInfoFromDecoded(decoded, ResourceKind::StorageImage);
inst.memory.sampler = 0;
inst.dst.kind = OperandKind::Null;
if (!LowerSourceOperand(decoded.dst, inst.src[0], error) ||
!LowerSourceOperand(decoded.src0, inst.src[1], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
inst.op = LoweredImageOpcode(decoded.opcode);
inst.src_count = 1;
inst.memory = MemoryInfoFromDecoded(decoded, ResourceKind::Image);
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
!LowerSourceOperand(decoded.src0, inst.src[0], error)) {
return false;
}
block.instructions.push_back(inst);
return true;
}
} // namespace
bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error) {
switch (decoded.opcode) {
case Decoder::Opcode::S_LOAD_DWORD:
case Decoder::Opcode::S_LOAD_DWORDX2:
case Decoder::Opcode::S_LOAD_DWORDX4:
case Decoder::Opcode::S_LOAD_DWORDX8:
case Decoder::Opcode::S_LOAD_DWORDX16:
return LowerScalarMemoryLoadDword(decoded, block, Opcode::SLoadDword, error);
case Decoder::Opcode::S_BUFFER_LOAD_DWORD:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX2:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX4:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX8:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX16:
return LowerScalarMemoryLoadDword(decoded, block, Opcode::SBufferLoadDword, error);
case Decoder::Opcode::BUFFER_LOAD_UBYTE:
case Decoder::Opcode::BUFFER_LOAD_SBYTE:
case Decoder::Opcode::BUFFER_LOAD_USHORT:
case Decoder::Opcode::BUFFER_LOAD_SSHORT:
case Decoder::Opcode::BUFFER_LOAD_DWORD:
case Decoder::Opcode::BUFFER_LOAD_DWORDX2:
case Decoder::Opcode::BUFFER_LOAD_DWORDX3:
case Decoder::Opcode::BUFFER_LOAD_DWORDX4:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_X:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XY:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZ:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZW:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_X:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XY:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XYZ:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XYZW:
return LowerBufferLoad(decoded, block, error);
case Decoder::Opcode::BUFFER_STORE_DWORD:
case Decoder::Opcode::BUFFER_STORE_DWORDX2:
case Decoder::Opcode::BUFFER_STORE_DWORDX3:
case Decoder::Opcode::BUFFER_STORE_DWORDX4:
case Decoder::Opcode::BUFFER_STORE_BYTE:
case Decoder::Opcode::BUFFER_STORE_SHORT:
case Decoder::Opcode::BUFFER_STORE_FORMAT_X:
case Decoder::Opcode::BUFFER_STORE_FORMAT_XY:
case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZ:
case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZW:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_X:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XY:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XYZ:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XYZW:
return LowerBufferStore(decoded, block, error);
case Decoder::Opcode::BUFFER_ATOMIC_SWAP:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicSwapU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_ADD:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicAddU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_SUB:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicSubU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_SMIN:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicSMinI32, error);
case Decoder::Opcode::BUFFER_ATOMIC_UMIN:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicUMinU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_SMAX:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicSMaxI32, error);
case Decoder::Opcode::BUFFER_ATOMIC_UMAX:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicUMaxU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_AND:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicAndU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_OR:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicOrU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_XOR:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicXorU32, error);
case Decoder::Opcode::BUFFER_ATOMIC_FMIN:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicFMinF32, error);
case Decoder::Opcode::BUFFER_ATOMIC_FMAX:
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicFMaxF32, error);
case Decoder::Opcode::FLAT_LOAD_UBYTE:
case Decoder::Opcode::FLAT_LOAD_SBYTE:
case Decoder::Opcode::FLAT_LOAD_USHORT:
case Decoder::Opcode::FLAT_LOAD_SSHORT:
case Decoder::Opcode::FLAT_LOAD_DWORD:
case Decoder::Opcode::FLAT_LOAD_DWORDX2:
case Decoder::Opcode::FLAT_LOAD_DWORDX3:
case Decoder::Opcode::FLAT_LOAD_DWORDX4: return LowerFlatLoad(decoded, block, error);
case Decoder::Opcode::FLAT_STORE_BYTE:
case Decoder::Opcode::FLAT_STORE_SHORT:
case Decoder::Opcode::FLAT_STORE_DWORD:
case Decoder::Opcode::FLAT_STORE_DWORDX2:
case Decoder::Opcode::FLAT_STORE_DWORDX3:
case Decoder::Opcode::FLAT_STORE_DWORDX4: return LowerFlatStore(decoded, block, error);
case Decoder::Opcode::DS_ADD_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicAddU32, false, error);
case Decoder::Opcode::DS_ADD_RTN_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicAddU32, true, error);
case Decoder::Opcode::DS_SUB_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicSubU32, false, error);
case Decoder::Opcode::DS_SUB_RTN_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicSubU32, true, error);
case Decoder::Opcode::DS_MIN_I32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicSMinI32, false, error);
case Decoder::Opcode::DS_MIN_RTN_I32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicSMinI32, true, error);
case Decoder::Opcode::DS_MAX_I32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicSMaxI32, false, error);
case Decoder::Opcode::DS_MAX_RTN_I32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicSMaxI32, true, error);
case Decoder::Opcode::DS_MIN_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicUMinU32, false, error);
case Decoder::Opcode::DS_MIN_RTN_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicUMinU32, true, error);
case Decoder::Opcode::DS_MAX_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicUMaxU32, false, error);
case Decoder::Opcode::DS_MAX_RTN_U32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicUMaxU32, true, error);
case Decoder::Opcode::DS_AND_B32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicAndU32, false, error);
case Decoder::Opcode::DS_AND_RTN_B32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicAndU32, true, error);
case Decoder::Opcode::DS_OR_B32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicOrU32, false, error);
case Decoder::Opcode::DS_OR_RTN_B32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicOrU32, true, error);
case Decoder::Opcode::DS_XOR_B32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicXorU32, false, error);
case Decoder::Opcode::DS_XOR_RTN_B32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicXorU32, true, error);
case Decoder::Opcode::DS_WRXCHG_RTN_B32:
return LowerDsAtomicU32(decoded, block, Opcode::AtomicSwapU32, true, error);
case Decoder::Opcode::DS_MIN_F32:
return LowerDsFloatMinMaxF32(decoded, block, Opcode::DsMinF32, error);
case Decoder::Opcode::DS_MAX_F32:
return LowerDsFloatMinMaxF32(decoded, block, Opcode::DsMaxF32, error);
case Decoder::Opcode::DS_SWIZZLE_B32: return LowerDsSwizzleB32(decoded, block, error);
case Decoder::Opcode::DS_CONSUME:
return LowerDsAppendConsume(decoded, block, Opcode::DsConsume, error);
case Decoder::Opcode::DS_APPEND:
return LowerDsAppendConsume(decoded, block, Opcode::DsAppend, error);
case Decoder::Opcode::DS_WRITE_ADDTID_B32:
return LowerDsWriteAddtidB32(decoded, block, error);
case Decoder::Opcode::DS_READ_ADDTID_B32:
return LowerDsReadAddtidB32(decoded, block, error);
case Decoder::Opcode::DS_READ2_B32:
case Decoder::Opcode::DS_READ2ST64_B32: return LowerDsRead2(decoded, block, 1, error);
case Decoder::Opcode::DS_READ2_B64:
case Decoder::Opcode::DS_READ2ST64_B64: return LowerDsRead2(decoded, block, 2, error);
case Decoder::Opcode::DS_READ_I8:
case Decoder::Opcode::DS_READ_U8:
case Decoder::Opcode::DS_READ_I16:
case Decoder::Opcode::DS_READ_U16:
case Decoder::Opcode::DS_READ_U16_D16:
case Decoder::Opcode::DS_READ_B32:
case Decoder::Opcode::DS_READ_B64:
case Decoder::Opcode::DS_READ_B96:
case Decoder::Opcode::DS_READ_B128: return LowerDsRead(decoded, block, error);
case Decoder::Opcode::DS_WRITE2_B32:
case Decoder::Opcode::DS_WRITE2ST64_B32: return LowerDsWrite2(decoded, block, 1, error);
case Decoder::Opcode::DS_WRITE2_B64:
case Decoder::Opcode::DS_WRITE2ST64_B64: return LowerDsWrite2(decoded, block, 2, error);
case Decoder::Opcode::DS_WRITE_B8:
case Decoder::Opcode::DS_WRITE_B16:
case Decoder::Opcode::DS_WRITE_B32:
case Decoder::Opcode::DS_WRITE_B64:
case Decoder::Opcode::DS_WRITE_B96:
case Decoder::Opcode::DS_WRITE_B128: return LowerDsWrite(decoded, block, error);
case Decoder::Opcode::IMAGE_GET_RESINFO:
case Decoder::Opcode::IMAGE_GET_LOD:
case Decoder::Opcode::IMAGE_LOAD:
case Decoder::Opcode::IMAGE_LOAD_MIP:
case Decoder::Opcode::IMAGE_STORE:
case Decoder::Opcode::IMAGE_STORE_MIP:
case Decoder::Opcode::IMAGE_GATHER4_LZ:
case Decoder::Opcode::IMAGE_GATHER4_C:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ:
case Decoder::Opcode::IMAGE_GATHER4_LZ_O:
case Decoder::Opcode::IMAGE_GATHER4_C_O:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ_O:
case Decoder::Opcode::IMAGE_GATHER4H:
case Decoder::Opcode::IMAGE_SAMPLE: return LowerImageOperation(decoded, block, error);
case Decoder::Opcode::IMAGE_ATOMIC_ADD:
case Decoder::Opcode::IMAGE_ATOMIC_UMIN:
case Decoder::Opcode::IMAGE_ATOMIC_UMAX:
case Decoder::Opcode::IMAGE_ATOMIC_AND:
case Decoder::Opcode::IMAGE_ATOMIC_OR:
case Decoder::Opcode::IMAGE_ATOMIC_XOR: return LowerImageAtomicU32(decoded, block, error);
default:
if (error != nullptr) {
*error = fmt::format("memory opcode has no specialized IR lowering: {}",
magic_enum::enum_name(decoded.opcode));
}
return false;
}
}
bool IsMemoryOpcode(Decoder::Opcode opcode) {
switch (opcode) {
case Decoder::Opcode::S_LOAD_DWORD:
case Decoder::Opcode::S_LOAD_DWORDX2:
case Decoder::Opcode::S_LOAD_DWORDX4:
case Decoder::Opcode::S_LOAD_DWORDX8:
case Decoder::Opcode::S_LOAD_DWORDX16:
case Decoder::Opcode::S_BUFFER_LOAD_DWORD:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX2:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX4:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX8:
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX16:
case Decoder::Opcode::BUFFER_LOAD_UBYTE:
case Decoder::Opcode::BUFFER_LOAD_SBYTE:
case Decoder::Opcode::BUFFER_LOAD_USHORT:
case Decoder::Opcode::BUFFER_LOAD_SSHORT:
case Decoder::Opcode::BUFFER_LOAD_DWORD:
case Decoder::Opcode::BUFFER_LOAD_DWORDX2:
case Decoder::Opcode::BUFFER_LOAD_DWORDX3:
case Decoder::Opcode::BUFFER_LOAD_DWORDX4:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_X:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XY:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZ:
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZW:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_X:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XY:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XYZ:
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XYZW:
case Decoder::Opcode::BUFFER_STORE_DWORD:
case Decoder::Opcode::BUFFER_STORE_DWORDX2:
case Decoder::Opcode::BUFFER_STORE_DWORDX3:
case Decoder::Opcode::BUFFER_STORE_DWORDX4:
case Decoder::Opcode::BUFFER_STORE_BYTE:
case Decoder::Opcode::BUFFER_STORE_SHORT:
case Decoder::Opcode::BUFFER_STORE_FORMAT_X:
case Decoder::Opcode::BUFFER_STORE_FORMAT_XY:
case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZ:
case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZW:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_X:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XY:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XYZ:
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XYZW:
case Decoder::Opcode::BUFFER_ATOMIC_SWAP:
case Decoder::Opcode::BUFFER_ATOMIC_ADD:
case Decoder::Opcode::BUFFER_ATOMIC_SUB:
case Decoder::Opcode::BUFFER_ATOMIC_SMIN:
case Decoder::Opcode::BUFFER_ATOMIC_UMIN:
case Decoder::Opcode::BUFFER_ATOMIC_SMAX:
case Decoder::Opcode::BUFFER_ATOMIC_UMAX:
case Decoder::Opcode::BUFFER_ATOMIC_AND:
case Decoder::Opcode::BUFFER_ATOMIC_OR:
case Decoder::Opcode::BUFFER_ATOMIC_XOR:
case Decoder::Opcode::BUFFER_ATOMIC_FMIN:
case Decoder::Opcode::BUFFER_ATOMIC_FMAX:
case Decoder::Opcode::FLAT_LOAD_UBYTE:
case Decoder::Opcode::FLAT_LOAD_SBYTE:
case Decoder::Opcode::FLAT_LOAD_USHORT:
case Decoder::Opcode::FLAT_LOAD_SSHORT:
case Decoder::Opcode::FLAT_LOAD_DWORD:
case Decoder::Opcode::FLAT_LOAD_DWORDX2:
case Decoder::Opcode::FLAT_LOAD_DWORDX3:
case Decoder::Opcode::FLAT_LOAD_DWORDX4:
case Decoder::Opcode::FLAT_STORE_BYTE:
case Decoder::Opcode::FLAT_STORE_SHORT:
case Decoder::Opcode::FLAT_STORE_DWORD:
case Decoder::Opcode::FLAT_STORE_DWORDX2:
case Decoder::Opcode::FLAT_STORE_DWORDX3:
case Decoder::Opcode::FLAT_STORE_DWORDX4:
case Decoder::Opcode::DS_ADD_U32:
case Decoder::Opcode::DS_ADD_RTN_U32:
case Decoder::Opcode::DS_SUB_U32:
case Decoder::Opcode::DS_SUB_RTN_U32:
case Decoder::Opcode::DS_MIN_I32:
case Decoder::Opcode::DS_MIN_RTN_I32:
case Decoder::Opcode::DS_MAX_I32:
case Decoder::Opcode::DS_MAX_RTN_I32:
case Decoder::Opcode::DS_MIN_U32:
case Decoder::Opcode::DS_MIN_RTN_U32:
case Decoder::Opcode::DS_MAX_U32:
case Decoder::Opcode::DS_MAX_RTN_U32:
case Decoder::Opcode::DS_AND_B32:
case Decoder::Opcode::DS_AND_RTN_B32:
case Decoder::Opcode::DS_OR_B32:
case Decoder::Opcode::DS_OR_RTN_B32:
case Decoder::Opcode::DS_XOR_B32:
case Decoder::Opcode::DS_XOR_RTN_B32:
case Decoder::Opcode::DS_WRXCHG_RTN_B32:
case Decoder::Opcode::DS_MIN_F32:
case Decoder::Opcode::DS_MAX_F32:
case Decoder::Opcode::DS_SWIZZLE_B32:
case Decoder::Opcode::DS_CONSUME:
case Decoder::Opcode::DS_APPEND:
case Decoder::Opcode::DS_READ_I8:
case Decoder::Opcode::DS_READ_U8:
case Decoder::Opcode::DS_READ_I16:
case Decoder::Opcode::DS_READ_U16:
case Decoder::Opcode::DS_READ_U16_D16:
case Decoder::Opcode::DS_READ2_B32:
case Decoder::Opcode::DS_READ2ST64_B32:
case Decoder::Opcode::DS_READ_B32:
case Decoder::Opcode::DS_READ_B64:
case Decoder::Opcode::DS_READ2_B64:
case Decoder::Opcode::DS_READ2ST64_B64:
case Decoder::Opcode::DS_READ_B96:
case Decoder::Opcode::DS_READ_B128:
case Decoder::Opcode::DS_WRITE_B8:
case Decoder::Opcode::DS_WRITE_B16:
case Decoder::Opcode::DS_WRITE2_B32:
case Decoder::Opcode::DS_WRITE2ST64_B32:
case Decoder::Opcode::DS_WRITE2_B64:
case Decoder::Opcode::DS_WRITE2ST64_B64:
case Decoder::Opcode::DS_WRITE_B32:
case Decoder::Opcode::DS_WRITE_B64:
case Decoder::Opcode::DS_WRITE_B96:
case Decoder::Opcode::DS_WRITE_B128:
case Decoder::Opcode::DS_WRITE_ADDTID_B32:
case Decoder::Opcode::DS_READ_ADDTID_B32:
case Decoder::Opcode::IMAGE_GET_RESINFO:
case Decoder::Opcode::IMAGE_GET_LOD:
case Decoder::Opcode::IMAGE_LOAD:
case Decoder::Opcode::IMAGE_LOAD_MIP:
case Decoder::Opcode::IMAGE_STORE:
case Decoder::Opcode::IMAGE_STORE_MIP:
case Decoder::Opcode::IMAGE_ATOMIC_ADD:
case Decoder::Opcode::IMAGE_ATOMIC_UMIN:
case Decoder::Opcode::IMAGE_ATOMIC_UMAX:
case Decoder::Opcode::IMAGE_ATOMIC_AND:
case Decoder::Opcode::IMAGE_ATOMIC_OR:
case Decoder::Opcode::IMAGE_ATOMIC_XOR:
case Decoder::Opcode::IMAGE_GATHER4_LZ:
case Decoder::Opcode::IMAGE_GATHER4_C:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ:
case Decoder::Opcode::IMAGE_GATHER4_LZ_O:
case Decoder::Opcode::IMAGE_GATHER4_C_O:
case Decoder::Opcode::IMAGE_GATHER4_C_LZ_O:
case Decoder::Opcode::IMAGE_GATHER4H:
case Decoder::Opcode::IMAGE_SAMPLE: return true;
default: return false;
}
}
} // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,76 +0,0 @@
#pragma once
#include "graphics/shader/recompiler/ir/Block.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include <memory>
#include <optional>
#include <string>
#include <vector>
namespace Libs::Graphics::ShaderRecompiler::IR {
struct ValueBlockInfo {
uint32_t id = 0;
uint32_t start_pc = 0;
uint32_t end_pc = 0;
CFG::Terminator terminator;
Value condition;
Value indirect_target;
};
struct DescriptorSource {
struct IndirectImage {
uint32_t material_source = 0;
uint32_t heap_source = 0;
uint32_t selector_stride = 0;
uint32_t selector_offset = 0;
uint32_t key_arg = 0;
bool operator==(const IndirectImage& other) const = default;
};
std::array<Value, 8> dwords {};
uint32_t dword_count = 0;
std::optional<IndirectImage> indirect_image;
bool operator==(const DescriptorSource& other) const = default;
};
struct SrtRead {
Value value;
uint32_t flat_offset = 0;
uint32_t use_pc = 0;
bool operator==(const SrtRead& other) const = default;
};
struct ValueProgram {
ValueProgram() = default;
~ValueProgram();
ValueProgram(const ValueProgram&) = delete;
ValueProgram& operator=(const ValueProgram&) = delete;
ValueProgram(ValueProgram&&) = default;
ValueProgram& operator=(ValueProgram&&) = default;
std::vector<std::unique_ptr<Block>> block_storage;
BlockList blocks;
std::vector<ValueBlockInfo> block_info;
// Decoded MIMG/VMEM metadata carries details such as RDNA2 NSA address registers and
// storage-image swizzles. Typed memory instructions carry a dense index into these shader-local
// tables until those fields are consumed by emission.
std::vector<MemoryInfo> memory_info;
std::vector<ExportInfo> export_info;
std::vector<DescriptorSource> descriptor_sources;
std::vector<SrtRead> srt_reads;
std::vector<Value> dynamic_reads;
};
bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::string* error);
void ResolveControlFlowIdentities(ValueProgram& program);
bool EquivalentValue(const ValueProgram& program, Value left, Value right);
Value ResolveInvariantPhi(const ValueProgram& program, Value value);
std::string ValueProgramToString(const ValueProgram& program);
} // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,71 +0,0 @@
#pragma once
#include "graphics/shader/recompiler/ir/Type.h"
#include <array>
#include <cstddef>
#include <string_view>
namespace Libs::Graphics::ShaderRecompiler::IR {
enum class Opcode {
#define IR_OPCODE(name, category, lowering, ...) name,
#include "graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc"
#undef IR_OPCODE
Count,
};
enum class OpcodeClass { General, Compare, Compare64 };
enum class LoweringClass {
Control,
Move,
Lane,
State,
Memory,
Attribute,
IntegerCompare,
Integer16Compare,
FloatCompare,
Conversion,
Integer16,
PackedInteger16,
PackedFloat16,
Float16,
Float,
U64Mask,
SimpleInteger,
ComposedInteger,
ExtendedInteger,
};
struct OpcodeInfo {
std::string_view name;
OpcodeClass opcode_class = OpcodeClass::General;
LoweringClass lowering_class = LoweringClass::Control;
};
inline constexpr OpcodeInfo OPCODE_INFO[] = {
#define IR_OPCODE(name, category, lowering, ...) \
{#name, OpcodeClass::category, LoweringClass::lowering},
#include "graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc"
#undef IR_OPCODE
};
static_assert(std::size(OPCODE_INFO) == static_cast<size_t>(Opcode::Count));
constexpr const OpcodeInfo& GetOpcodeInfo(Opcode opcode) {
return OPCODE_INFO[static_cast<size_t>(opcode)];
}
constexpr std::string_view OpcodeName(Opcode opcode) {
return GetOpcodeInfo(opcode).name;
}
constexpr bool IsCompareOpcode(Opcode opcode) {
return GetOpcodeInfo(opcode).opcode_class != OpcodeClass::General;
}
constexpr bool IsCompare64Opcode(Opcode opcode) {
return GetOpcodeInfo(opcode).opcode_class == OpcodeClass::Compare64;
}
} // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,548 +0,0 @@
#include "common/assert.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/ShaderIRInternal.h"
#include <algorithm>
#include <fmt/format.h>
#include <utility>
namespace Libs::Graphics::ShaderRecompiler::IR {
namespace {
struct LowerMap {
Decoder::Opcode decoded = Decoder::Opcode::UNKNOWN;
Opcode ir = Opcode::MoveU32;
};
constexpr LowerMap LOWER_OPS[] = {
{Decoder::Opcode::S_MOV_B32, Opcode::MoveU32},
{Decoder::Opcode::S_MOV_B64, Opcode::MoveU64},
{Decoder::Opcode::S_WQM_B64, Opcode::WqmB64},
{Decoder::Opcode::S_MOVK_I32, Opcode::MoveU32},
{Decoder::Opcode::S_ABS_I32, Opcode::AbsI32},
{Decoder::Opcode::S_BREV_B32, Opcode::BitReverseU32},
{Decoder::Opcode::S_BCNT1_I32_B32, Opcode::BitCountU32},
{Decoder::Opcode::S_BCNT1_I32_B64, Opcode::BitCountU64},
{Decoder::Opcode::S_FF1_I32_B32, Opcode::FindLsbU32},
{Decoder::Opcode::S_FF1_I32_B64, Opcode::FindLsbU64},
{Decoder::Opcode::S_FLBIT_I32_B64, Opcode::FindMsbFromHighU64},
{Decoder::Opcode::S_BITREPLICATE_B64_B32, Opcode::BitReplicateB64B32},
{Decoder::Opcode::S_QUADMASK_B64, Opcode::QuadmaskB64},
{Decoder::Opcode::S_NOT_B32, Opcode::BitwiseNotU32},
{Decoder::Opcode::S_NOT_B64, Opcode::BitwiseNotU64},
{Decoder::Opcode::S_ADD_U32, Opcode::IAddU32},
{Decoder::Opcode::S_SUB_U32, Opcode::ISubU32},
{Decoder::Opcode::S_ADD_I32, Opcode::IAddU32},
{Decoder::Opcode::S_SUB_I32, Opcode::ISubU32},
{Decoder::Opcode::S_MIN_I32, Opcode::IMinI32},
{Decoder::Opcode::S_MAX_I32, Opcode::IMaxI32},
{Decoder::Opcode::S_MIN_U32, Opcode::UMinU32},
{Decoder::Opcode::S_MAX_U32, Opcode::UMaxU32},
{Decoder::Opcode::S_AND_B32, Opcode::BitwiseAndU32},
{Decoder::Opcode::S_AND_B64, Opcode::BitwiseAndU64},
{Decoder::Opcode::S_ANDN2_B32, Opcode::BitwiseAndNotU32},
{Decoder::Opcode::S_ANDN2_B64, Opcode::BitwiseAndNotU64},
{Decoder::Opcode::S_OR_B32, Opcode::BitwiseOrU32},
{Decoder::Opcode::S_OR_B64, Opcode::BitwiseOrU64},
{Decoder::Opcode::S_ORN2_B32, Opcode::BitwiseOrNotU32},
{Decoder::Opcode::S_ORN2_B64, Opcode::BitwiseOrNotU64},
{Decoder::Opcode::S_XOR_B32, Opcode::BitwiseXorU32},
{Decoder::Opcode::S_XOR_B64, Opcode::BitwiseXorU64},
{Decoder::Opcode::S_NAND_B32, Opcode::BitwiseNandU32},
{Decoder::Opcode::S_NAND_B64, Opcode::BitwiseNandU64},
{Decoder::Opcode::S_NOR_B32, Opcode::BitwiseNorU32},
{Decoder::Opcode::S_NOR_B64, Opcode::BitwiseNorU64},
{Decoder::Opcode::S_XNOR_B32, Opcode::BitwiseXnorU32},
{Decoder::Opcode::S_XNOR_B64, Opcode::BitwiseXnorU64},
{Decoder::Opcode::S_LSHL_B32, Opcode::ShiftLeftLogicalU32},
{Decoder::Opcode::S_LSHL_B64, Opcode::ShiftLeftLogicalU64},
{Decoder::Opcode::S_LSHR_B32, Opcode::ShiftRightLogicalU32},
{Decoder::Opcode::S_LSHR_B64, Opcode::ShiftRightLogicalU64},
{Decoder::Opcode::S_ASHR_I32, Opcode::ShiftRightArithmeticI32},
{Decoder::Opcode::S_MUL_I32, Opcode::IMulU32},
{Decoder::Opcode::S_MUL_HI_U32, Opcode::UMulHighU32},
{Decoder::Opcode::S_MULK_I32, Opcode::IMulU32},
{Decoder::Opcode::S_BFE_U32, Opcode::BitFieldExtractU32},
{Decoder::Opcode::S_BFE_I32, Opcode::BitFieldExtractI32},
{Decoder::Opcode::S_BFE_U64, Opcode::BitFieldExtractU64},
{Decoder::Opcode::S_BFM_B32, Opcode::BitFieldMaskU32},
{Decoder::Opcode::S_BFM_B64, Opcode::BitFieldMaskU64},
{Decoder::Opcode::S_BITCMP0_B32, Opcode::BitCompare0B32},
{Decoder::Opcode::S_BITCMP1_B32, Opcode::BitCompare1B32},
{Decoder::Opcode::S_PACK_LL_B32_B16, Opcode::PackLowLowU16},
{Decoder::Opcode::S_PACK_LH_B32_B16, Opcode::PackLowHighU16},
{Decoder::Opcode::S_PACK_HH_B32_B16, Opcode::PackHighHighU16},
{Decoder::Opcode::S_CMP_EQ_I32, Opcode::CompareEqI32},
{Decoder::Opcode::S_CMP_LG_I32, Opcode::CompareNeI32},
{Decoder::Opcode::S_CMP_GT_I32, Opcode::CompareGtI32},
{Decoder::Opcode::S_CMP_GE_I32, Opcode::CompareGeI32},
{Decoder::Opcode::S_CMP_LT_I32, Opcode::CompareLtI32},
{Decoder::Opcode::S_CMP_LE_I32, Opcode::CompareLeI32},
{Decoder::Opcode::S_CMP_EQ_U32, Opcode::CompareEqU32},
{Decoder::Opcode::S_CMP_LG_U32, Opcode::CompareNeU32},
{Decoder::Opcode::S_CMP_GT_U32, Opcode::CompareGtU32},
{Decoder::Opcode::S_CMP_GE_U32, Opcode::CompareGeU32},
{Decoder::Opcode::S_CMP_LT_U32, Opcode::CompareLtU32},
{Decoder::Opcode::S_CMP_LE_U32, Opcode::CompareLeU32},
{Decoder::Opcode::S_CMP_EQ_U64, Opcode::CompareEqU64},
{Decoder::Opcode::S_CMP_LG_U64, Opcode::CompareNeU64},
{Decoder::Opcode::V_READFIRSTLANE_B32, Opcode::ReadFirstLaneU32},
{Decoder::Opcode::V_READLANE_B32, Opcode::ReadLaneU32},
{Decoder::Opcode::V_WRITELANE_B32, Opcode::WriteLaneU32},
{Decoder::Opcode::V_PERMLANE16_B32, Opcode::Permlane16B32},
{Decoder::Opcode::V_PERMLANEX16_B32, Opcode::Permlanex16B32},
{Decoder::Opcode::V_CVT_F32_I32, Opcode::ConvertI32ToF32},
{Decoder::Opcode::V_CVT_F32_U32, Opcode::ConvertU32ToF32},
{Decoder::Opcode::V_CVT_U32_F32, Opcode::ConvertF32ToU32},
{Decoder::Opcode::V_CVT_I32_F32, Opcode::ConvertF32ToI32},
{Decoder::Opcode::V_CVT_F16_F32, Opcode::ConvertF32ToF16},
{Decoder::Opcode::V_CVT_F32_F16, Opcode::ConvertF16ToF32},
{Decoder::Opcode::V_CVT_F16_U16, Opcode::ConvertU16ToF16},
{Decoder::Opcode::V_CVT_U16_F16, Opcode::ConvertF16ToU16},
{Decoder::Opcode::V_CVT_F16_I16, Opcode::ConvertI16ToF16},
{Decoder::Opcode::V_CVT_I16_F16, Opcode::ConvertF16ToI16},
{Decoder::Opcode::V_CVT_RPI_I32_F32, Opcode::ConvertRoundPlusInfF32ToI32},
{Decoder::Opcode::V_CVT_FLR_I32_F32, Opcode::ConvertFloorF32ToI32},
{Decoder::Opcode::V_FREXP_EXP_I32_F32, Opcode::FrexpExpI32F32},
{Decoder::Opcode::V_FREXP_MANT_F32, Opcode::FrexpMantF32},
{Decoder::Opcode::V_CVT_OFF_F32_I4, Opcode::ConvertI4ToOffsetF32},
{Decoder::Opcode::V_RCP_F32, Opcode::RcpF32},
{Decoder::Opcode::V_RCP_IFLAG_F32, Opcode::RcpIflagF32},
{Decoder::Opcode::V_FRACT_F32, Opcode::FractF32},
{Decoder::Opcode::V_TRUNC_F32, Opcode::TruncF32},
{Decoder::Opcode::V_CEIL_F32, Opcode::CeilF32},
{Decoder::Opcode::V_RNDNE_F32, Opcode::RoundEvenF32},
{Decoder::Opcode::V_FLOOR_F32, Opcode::FloorF32},
{Decoder::Opcode::V_EXP_F32, Opcode::Exp2F32},
{Decoder::Opcode::V_LOG_F32, Opcode::Log2F32},
{Decoder::Opcode::V_RSQ_F32, Opcode::InverseSqrtF32},
{Decoder::Opcode::V_SQRT_F32, Opcode::SqrtF32},
{Decoder::Opcode::V_RCP_F16, Opcode::RcpF16},
{Decoder::Opcode::V_SQRT_F16, Opcode::SqrtF16},
{Decoder::Opcode::V_RSQ_F16, Opcode::InverseSqrtF16},
{Decoder::Opcode::V_LOG_F16, Opcode::Log2F16},
{Decoder::Opcode::V_EXP_F16, Opcode::Exp2F16},
{Decoder::Opcode::V_FLOOR_F16, Opcode::FloorF16},
{Decoder::Opcode::V_CEIL_F16, Opcode::CeilF16},
{Decoder::Opcode::V_TRUNC_F16, Opcode::TruncF16},
{Decoder::Opcode::V_RNDNE_F16, Opcode::RoundEvenF16},
{Decoder::Opcode::V_SIN_F32, Opcode::SinF32},
{Decoder::Opcode::V_COS_F32, Opcode::CosF32},
{Decoder::Opcode::V_NOT_B32, Opcode::BitwiseNotU32},
{Decoder::Opcode::V_BFREV_B32, Opcode::BitReverseU32},
{Decoder::Opcode::V_FFBL_B32, Opcode::FindLsbU32},
{Decoder::Opcode::V_FFBH_U32, Opcode::FindMsbFromHighU32},
{Decoder::Opcode::V_ADD_F32, Opcode::FAddF32},
{Decoder::Opcode::V_SUB_F32, Opcode::FSubF32},
{Decoder::Opcode::V_SUBREV_F32, Opcode::FSubF32},
{Decoder::Opcode::V_MUL_F32, Opcode::FMulF32},
{Decoder::Opcode::V_MAC_F32, Opcode::FMadF32},
{Decoder::Opcode::V_MADMK_F32, Opcode::FMadF32},
{Decoder::Opcode::V_MADAK_F32, Opcode::FMadF32},
{Decoder::Opcode::V_MIN_F32, Opcode::FMinF32},
{Decoder::Opcode::V_MAX_F32, Opcode::FMaxF32},
{Decoder::Opcode::V_MIN_I32, Opcode::IMinI32},
{Decoder::Opcode::V_MAX_I32, Opcode::IMaxI32},
{Decoder::Opcode::V_MAD_F32, Opcode::FMadF32},
{Decoder::Opcode::V_MAD_I32_I24, Opcode::IMadI24U32},
{Decoder::Opcode::V_MAD_U32_U24, Opcode::UMadU24U32},
{Decoder::Opcode::V_CUBEID_F32, Opcode::CubeIdF32},
{Decoder::Opcode::V_CUBESC_F32, Opcode::CubeScF32},
{Decoder::Opcode::V_CUBETC_F32, Opcode::CubeTcF32},
{Decoder::Opcode::V_CUBEMA_F32, Opcode::CubeMaF32},
{Decoder::Opcode::V_FMA_F32, Opcode::FMadF32},
{Decoder::Opcode::V_BFE_U32, Opcode::BitFieldExtract3U32},
{Decoder::Opcode::V_BFE_I32, Opcode::BitFieldExtract3I32},
{Decoder::Opcode::V_BFI_B32, Opcode::BitFieldInsertSelectU32},
{Decoder::Opcode::V_ALIGNBIT_B32, Opcode::AlignBitU32},
{Decoder::Opcode::V_ALIGNBYTE_B32, Opcode::AlignByteU32},
{Decoder::Opcode::V_MIN3_F32, Opcode::FMin3F32},
{Decoder::Opcode::V_MIN3_I32, Opcode::IMin3I32},
{Decoder::Opcode::V_MIN3_U32, Opcode::UMin3U32},
{Decoder::Opcode::V_MIN3_F16, Opcode::Min3F16},
{Decoder::Opcode::V_MAX3_F32, Opcode::FMax3F32},
{Decoder::Opcode::V_MAX3_I32, Opcode::IMax3I32},
{Decoder::Opcode::V_MAX3_U32, Opcode::UMax3U32},
{Decoder::Opcode::V_MAX3_F16, Opcode::Max3F16},
{Decoder::Opcode::V_MED3_F32, Opcode::FMed3F32},
{Decoder::Opcode::V_MED3_I32, Opcode::IMed3I32},
{Decoder::Opcode::V_MED3_U32, Opcode::UMed3U32},
{Decoder::Opcode::V_MED3_F16, Opcode::Med3F16},
{Decoder::Opcode::V_MED3_I16, Opcode::IMed3I16},
{Decoder::Opcode::V_SAD_U32, Opcode::SadU32},
{Decoder::Opcode::V_ADD3_U32, Opcode::IAdd3U32},
{Decoder::Opcode::V_LSHL_ADD_U32, Opcode::ShiftLeftAddU32},
{Decoder::Opcode::V_ADD_LSHL_U32, Opcode::AddShiftLeftU32},
{Decoder::Opcode::V_XAD_U32, Opcode::XorAddU32},
{Decoder::Opcode::V_LSHL_OR_B32, Opcode::ShiftLeftOrU32},
{Decoder::Opcode::V_AND_OR_B32, Opcode::BitwiseAndOrU32},
{Decoder::Opcode::V_OR3_B32, Opcode::BitwiseOr3U32},
{Decoder::Opcode::V_XOR3_B32, Opcode::BitwiseXor3U32},
{Decoder::Opcode::V_ADD_NC_U32, Opcode::IAddU32},
{Decoder::Opcode::V_SUB_NC_U32, Opcode::ISubU32},
{Decoder::Opcode::V_SUBREV_NC_U32, Opcode::ISubU32},
{Decoder::Opcode::V_ADD_NC_U16, Opcode::IAddU16},
{Decoder::Opcode::V_SUB_NC_U16, Opcode::ISubI16},
{Decoder::Opcode::V_MAX_U16, Opcode::UMaxU16},
{Decoder::Opcode::V_MAX_I16, Opcode::IMaxI16},
{Decoder::Opcode::V_MIN_U16, Opcode::UMinU16},
{Decoder::Opcode::V_MIN_I16, Opcode::IMinI16},
{Decoder::Opcode::V_ADD_NC_I16, Opcode::IAddU16},
{Decoder::Opcode::V_SUB_NC_I16, Opcode::ISubI16},
{Decoder::Opcode::V_MUL_I32_I24, Opcode::IMulI24U32},
{Decoder::Opcode::V_MUL_U32_U24, Opcode::UMulU24U32},
{Decoder::Opcode::V_MUL_LO_U32, Opcode::IMulU32},
{Decoder::Opcode::V_MUL_HI_U32, Opcode::UMulHighU32},
{Decoder::Opcode::V_MUL_LO_I32, Opcode::IMulU32},
{Decoder::Opcode::V_MUL_HI_I32, Opcode::SMulHighI32},
{Decoder::Opcode::V_ADD_I32, Opcode::IAddU32},
{Decoder::Opcode::V_SUB_I32, Opcode::ISubU32},
{Decoder::Opcode::V_SUBREV_I32, Opcode::ISubU32},
{Decoder::Opcode::V_BFM_B32, Opcode::BitFieldMaskU32},
{Decoder::Opcode::V_CVT_PKRTZ_F16_F32, Opcode::PackF32ToF16Rtz},
{Decoder::Opcode::V_LDEXP_F32, Opcode::LdexpF32},
{Decoder::Opcode::V_CVT_PKNORM_I16_F32, Opcode::PackSnorm2x16F32},
{Decoder::Opcode::V_CVT_PKNORM_U16_F32, Opcode::PackUnorm2x16F32},
{Decoder::Opcode::V_CVT_PK_U16_U32, Opcode::PackU16U32},
{Decoder::Opcode::V_CVT_PK_I16_I32, Opcode::PackU16U32},
{Decoder::Opcode::V_CVT_PK_U8_F32, Opcode::PackU8F32},
{Decoder::Opcode::V_PACK_B32_F16, Opcode::PackB32F16},
{Decoder::Opcode::V_PK_MAD_I16, Opcode::PackedMadI16},
{Decoder::Opcode::V_PK_MUL_LO_U16, Opcode::PackedMulLoU16},
{Decoder::Opcode::V_PK_ADD_I16, Opcode::PackedAddI16},
{Decoder::Opcode::V_PK_SUB_I16, Opcode::PackedSubI16},
{Decoder::Opcode::V_PK_LSHLREV_B16, Opcode::PackedLshlrevB16},
{Decoder::Opcode::V_PK_LSHRREV_B16, Opcode::PackedLshrrevB16},
{Decoder::Opcode::V_PK_ASHRREV_I16, Opcode::PackedAshrrevI16},
{Decoder::Opcode::V_PK_MAX_I16, Opcode::PackedMaxI16},
{Decoder::Opcode::V_PK_MIN_I16, Opcode::PackedMinI16},
{Decoder::Opcode::V_PK_MAD_U16, Opcode::PackedMadU16},
{Decoder::Opcode::V_PK_ADD_U16, Opcode::PackedAddU16},
{Decoder::Opcode::V_PK_SUB_U16, Opcode::PackedSubU16},
{Decoder::Opcode::V_PK_MAX_U16, Opcode::PackedMaxU16},
{Decoder::Opcode::V_PK_MIN_U16, Opcode::PackedMinU16},
{Decoder::Opcode::V_PK_ADD_F16, Opcode::PackedAddF16},
{Decoder::Opcode::V_PK_MUL_F16, Opcode::PackedMulF16},
{Decoder::Opcode::V_PK_MIN_F16, Opcode::PackedMinF16},
{Decoder::Opcode::V_PK_MAX_F16, Opcode::PackedMaxF16},
{Decoder::Opcode::V_PK_FMA_F16, Opcode::PackedFmaF16},
{Decoder::Opcode::V_PK_FMAC_F16, Opcode::PackedFmaF16},
{Decoder::Opcode::V_ADD_F16, Opcode::AddF16},
{Decoder::Opcode::V_SUB_F16, Opcode::SubF16},
{Decoder::Opcode::V_SUBREV_F16, Opcode::SubF16},
{Decoder::Opcode::V_MUL_F16, Opcode::MulF16},
{Decoder::Opcode::V_FMAC_F16, Opcode::FmaF16},
{Decoder::Opcode::V_FMAMK_F16, Opcode::FmaF16},
{Decoder::Opcode::V_FMAAK_F16, Opcode::FmaF16},
{Decoder::Opcode::V_FMA_F16, Opcode::FmaF16},
{Decoder::Opcode::V_MAD_MIXLO_F16, Opcode::MadMixF16},
{Decoder::Opcode::V_MAD_MIXHI_F16, Opcode::MadMixF16},
{Decoder::Opcode::V_AND_B32, Opcode::BitwiseAndU32},
{Decoder::Opcode::V_OR_B32, Opcode::BitwiseOrU32},
{Decoder::Opcode::V_XOR_B32, Opcode::BitwiseXorU32},
{Decoder::Opcode::V_XNOR_B32, Opcode::BitwiseXnorU32},
{Decoder::Opcode::V_BCNT_U32_B32, Opcode::BitCountAddU32},
{Decoder::Opcode::V_MBCNT_LO_U32_B32, Opcode::MaskedBitCountLowU32},
{Decoder::Opcode::V_MBCNT_HI_U32_B32, Opcode::MaskedBitCountHighU32},
{Decoder::Opcode::V_LSHL_B32, Opcode::ShiftLeftLogicalU32},
{Decoder::Opcode::V_LSHLREV_B32, Opcode::ShiftLeftLogicalU32},
{Decoder::Opcode::V_LSHLREV_B16, Opcode::ShiftLeftLogicalU16},
{Decoder::Opcode::V_LSHR_B32, Opcode::ShiftRightLogicalU32},
{Decoder::Opcode::V_LSHRREV_B32, Opcode::ShiftRightLogicalU32},
{Decoder::Opcode::V_LSHLREV_B64, Opcode::ShiftLeftLogicalU64},
{Decoder::Opcode::V_LSHRREV_B64, Opcode::ShiftRightLogicalU64},
{Decoder::Opcode::V_LSHRREV_B16, Opcode::ShiftRightLogicalU16},
{Decoder::Opcode::V_ASHR_I32, Opcode::ShiftRightArithmeticI32},
{Decoder::Opcode::V_ASHRREV_I32, Opcode::ShiftRightArithmeticI32},
{Decoder::Opcode::V_ASHRREV_I16, Opcode::ShiftRightArithmeticI16},
{Decoder::Opcode::V_MIN_U32, Opcode::UMinU32},
{Decoder::Opcode::V_MAX_U32, Opcode::UMaxU32},
{Decoder::Opcode::V_MAX_F16, Opcode::MaxF16},
{Decoder::Opcode::V_MIN_F16, Opcode::MinF16},
{Decoder::Opcode::V_CMP_F_F32, Opcode::CompareFalse},
{Decoder::Opcode::V_CMP_LT_F32, Opcode::CompareLtF32},
{Decoder::Opcode::V_CMP_EQ_F32, Opcode::CompareEqF32},
{Decoder::Opcode::V_CMP_LE_F32, Opcode::CompareLeF32},
{Decoder::Opcode::V_CMP_GT_F32, Opcode::CompareGtF32},
{Decoder::Opcode::V_CMP_LG_F32, Opcode::CompareNeF32},
{Decoder::Opcode::V_CMP_GE_F32, Opcode::CompareGeF32},
{Decoder::Opcode::V_CMP_O_F32, Opcode::CompareOrderedF32},
{Decoder::Opcode::V_CMP_U_F32, Opcode::CompareUnorderedF32},
{Decoder::Opcode::V_CMP_NGE_F32, Opcode::CompareUnordLtF32},
{Decoder::Opcode::V_CMP_NLG_F32, Opcode::CompareUnordEqF32},
{Decoder::Opcode::V_CMP_NGT_F32, Opcode::CompareUnordLeF32},
{Decoder::Opcode::V_CMP_NLE_F32, Opcode::CompareUnordGtF32},
{Decoder::Opcode::V_CMP_NEQ_F32, Opcode::CompareUnordNeF32},
{Decoder::Opcode::V_CMP_NLT_F32, Opcode::CompareUnordGeF32},
{Decoder::Opcode::V_CMP_TRU_F32, Opcode::CompareTrue},
{Decoder::Opcode::V_CMP_CLASS_F32, Opcode::CompareClassF32},
{Decoder::Opcode::V_CMP_LT_F16, Opcode::CompareLtF16},
{Decoder::Opcode::V_CMP_EQ_F16, Opcode::CompareEqF16},
{Decoder::Opcode::V_CMP_LE_F16, Opcode::CompareLeF16},
{Decoder::Opcode::V_CMP_GT_F16, Opcode::CompareGtF16},
{Decoder::Opcode::V_CMP_LG_F16, Opcode::CompareNeF16},
{Decoder::Opcode::V_CMP_GE_F16, Opcode::CompareGeF16},
{Decoder::Opcode::V_CMP_NEQ_F16, Opcode::CompareUnordNeF16},
{Decoder::Opcode::V_CMPX_LT_F16, Opcode::CompareMaskLtF16},
{Decoder::Opcode::V_CMPX_EQ_F16, Opcode::CompareMaskEqF16},
{Decoder::Opcode::V_CMPX_LE_F16, Opcode::CompareMaskLeF16},
{Decoder::Opcode::V_CMPX_GT_F16, Opcode::CompareMaskGtF16},
{Decoder::Opcode::V_CMPX_GE_F16, Opcode::CompareMaskGeF16},
{Decoder::Opcode::V_CMPX_NEQ_F16, Opcode::CompareMaskUnordNeF16},
{Decoder::Opcode::V_CMPX_NLT_F16, Opcode::CompareMaskUnordGeF16},
{Decoder::Opcode::V_CMPX_LT_F32, Opcode::CompareMaskLtF32},
{Decoder::Opcode::V_CMPX_EQ_F32, Opcode::CompareMaskEqF32},
{Decoder::Opcode::V_CMPX_LE_F32, Opcode::CompareMaskLeF32},
{Decoder::Opcode::V_CMPX_GT_F32, Opcode::CompareMaskGtF32},
{Decoder::Opcode::V_CMPX_LG_F32, Opcode::CompareMaskNeF32},
{Decoder::Opcode::V_CMPX_GE_F32, Opcode::CompareMaskGeF32},
{Decoder::Opcode::V_CMPX_NGE_F32, Opcode::CompareMaskUnordLtF32},
{Decoder::Opcode::V_CMPX_NLG_F32, Opcode::CompareMaskUnordEqF32},
{Decoder::Opcode::V_CMPX_NGT_F32, Opcode::CompareMaskUnordLeF32},
{Decoder::Opcode::V_CMPX_NLE_F32, Opcode::CompareMaskUnordGtF32},
{Decoder::Opcode::V_CMPX_NEQ_F32, Opcode::CompareMaskUnordNeF32},
{Decoder::Opcode::V_CMPX_NLT_F32, Opcode::CompareMaskUnordGeF32},
{Decoder::Opcode::V_CMP_F_I32, Opcode::CompareFalse},
{Decoder::Opcode::V_CMP_LT_I32, Opcode::CompareLtI32},
{Decoder::Opcode::V_CMP_EQ_I32, Opcode::CompareEqI32},
{Decoder::Opcode::V_CMP_LE_I32, Opcode::CompareLeI32},
{Decoder::Opcode::V_CMP_GT_I32, Opcode::CompareGtI32},
{Decoder::Opcode::V_CMP_NE_I32, Opcode::CompareNeI32},
{Decoder::Opcode::V_CMP_GE_I32, Opcode::CompareGeI32},
{Decoder::Opcode::V_CMP_T_I32, Opcode::CompareTrue},
{Decoder::Opcode::V_CMP_LT_I16, Opcode::CompareLtI16},
{Decoder::Opcode::V_CMP_EQ_I16, Opcode::CompareEqI16},
{Decoder::Opcode::V_CMP_LE_I16, Opcode::CompareLeI16},
{Decoder::Opcode::V_CMP_GT_I16, Opcode::CompareGtI16},
{Decoder::Opcode::V_CMP_NE_I16, Opcode::CompareNeI16},
{Decoder::Opcode::V_CMP_GE_I16, Opcode::CompareGeI16},
{Decoder::Opcode::V_CMPX_LT_I32, Opcode::CompareMaskLtI32},
{Decoder::Opcode::V_CMPX_EQ_I32, Opcode::CompareMaskEqI32},
{Decoder::Opcode::V_CMPX_LE_I32, Opcode::CompareMaskLeI32},
{Decoder::Opcode::V_CMPX_GT_I32, Opcode::CompareMaskGtI32},
{Decoder::Opcode::V_CMPX_NE_I32, Opcode::CompareMaskNeI32},
{Decoder::Opcode::V_CMPX_GE_I32, Opcode::CompareMaskGeI32},
{Decoder::Opcode::V_CMP_LT_U16, Opcode::CompareLtU16},
{Decoder::Opcode::V_CMP_EQ_U16, Opcode::CompareEqU16},
{Decoder::Opcode::V_CMP_LE_U16, Opcode::CompareLeU16},
{Decoder::Opcode::V_CMP_GT_U16, Opcode::CompareGtU16},
{Decoder::Opcode::V_CMP_NE_U16, Opcode::CompareNeU16},
{Decoder::Opcode::V_CMP_GE_U16, Opcode::CompareGeU16},
{Decoder::Opcode::V_CMP_F_U32, Opcode::CompareFalse},
{Decoder::Opcode::V_CMP_LT_U32, Opcode::CompareLtU32},
{Decoder::Opcode::V_CMP_EQ_U32, Opcode::CompareEqU32},
{Decoder::Opcode::V_CMP_LE_U32, Opcode::CompareLeU32},
{Decoder::Opcode::V_CMP_GT_U32, Opcode::CompareGtU32},
{Decoder::Opcode::V_CMP_NE_U32, Opcode::CompareNeU32},
{Decoder::Opcode::V_CMP_GE_U32, Opcode::CompareGeU32},
{Decoder::Opcode::V_CMP_T_U32, Opcode::CompareTrue},
{Decoder::Opcode::V_CMP_EQ_I64, Opcode::CompareEqU64},
{Decoder::Opcode::V_CMP_GT_U64, Opcode::CompareGtU64},
{Decoder::Opcode::V_CMP_NE_U64, Opcode::CompareNeU64},
{Decoder::Opcode::V_CMPX_NE_I64, Opcode::CompareNeU64},
{Decoder::Opcode::V_CMPX_NE_U64, Opcode::CompareNeU64},
{Decoder::Opcode::V_CMPX_LT_U32, Opcode::CompareMaskLtU32},
{Decoder::Opcode::V_CMPX_EQ_U32, Opcode::CompareMaskEqU32},
{Decoder::Opcode::V_CMPX_LE_U32, Opcode::CompareMaskLeU32},
{Decoder::Opcode::V_CMPX_GT_U32, Opcode::CompareMaskGtU32},
{Decoder::Opcode::V_CMPX_NE_U32, Opcode::CompareMaskNeU32},
{Decoder::Opcode::V_CMPX_GE_U32, Opcode::CompareMaskGeU32},
{Decoder::Opcode::S_LOAD_DWORD, Opcode::SLoadDword},
{Decoder::Opcode::S_BUFFER_LOAD_DWORD, Opcode::SBufferLoadDword},
{Decoder::Opcode::BUFFER_LOAD_DWORD, Opcode::BufferLoadDword},
{Decoder::Opcode::BUFFER_LOAD_SBYTE, Opcode::BufferLoadSbyte},
{Decoder::Opcode::BUFFER_LOAD_SSHORT, Opcode::BufferLoadSshort},
{Decoder::Opcode::BUFFER_STORE_BYTE, Opcode::BufferStoreByte},
{Decoder::Opcode::BUFFER_STORE_SHORT, Opcode::BufferStoreShort},
{Decoder::Opcode::BUFFER_STORE_DWORD, Opcode::BufferStoreDword},
{Decoder::Opcode::BUFFER_ATOMIC_SWAP, Opcode::AtomicSwapU32},
{Decoder::Opcode::BUFFER_ATOMIC_ADD, Opcode::AtomicAddU32},
{Decoder::Opcode::BUFFER_ATOMIC_SUB, Opcode::AtomicSubU32},
{Decoder::Opcode::BUFFER_ATOMIC_SMIN, Opcode::AtomicSMinI32},
{Decoder::Opcode::BUFFER_ATOMIC_UMIN, Opcode::AtomicUMinU32},
{Decoder::Opcode::BUFFER_ATOMIC_SMAX, Opcode::AtomicSMaxI32},
{Decoder::Opcode::BUFFER_ATOMIC_UMAX, Opcode::AtomicUMaxU32},
{Decoder::Opcode::BUFFER_ATOMIC_AND, Opcode::AtomicAndU32},
{Decoder::Opcode::BUFFER_ATOMIC_OR, Opcode::AtomicOrU32},
{Decoder::Opcode::BUFFER_ATOMIC_XOR, Opcode::AtomicXorU32},
{Decoder::Opcode::BUFFER_ATOMIC_FMIN, Opcode::AtomicFMinF32},
{Decoder::Opcode::BUFFER_ATOMIC_FMAX, Opcode::AtomicFMaxF32},
{Decoder::Opcode::FLAT_LOAD_UBYTE, Opcode::FlatLoadUbyte},
{Decoder::Opcode::FLAT_LOAD_SBYTE, Opcode::FlatLoadSbyte},
{Decoder::Opcode::FLAT_LOAD_SSHORT, Opcode::FlatLoadSshort},
{Decoder::Opcode::FLAT_STORE_BYTE, Opcode::FlatStoreByte},
{Decoder::Opcode::FLAT_STORE_SHORT, Opcode::FlatStoreShort},
{Decoder::Opcode::FLAT_STORE_DWORD, Opcode::FlatStoreDword},
{Decoder::Opcode::DS_ADD_U32, Opcode::AtomicAddU32},
{Decoder::Opcode::DS_ADD_RTN_U32, Opcode::AtomicAddU32},
{Decoder::Opcode::DS_SUB_U32, Opcode::AtomicSubU32},
{Decoder::Opcode::DS_SUB_RTN_U32, Opcode::AtomicSubU32},
{Decoder::Opcode::DS_MIN_I32, Opcode::AtomicSMinI32},
{Decoder::Opcode::DS_MIN_RTN_I32, Opcode::AtomicSMinI32},
{Decoder::Opcode::DS_MAX_I32, Opcode::AtomicSMaxI32},
{Decoder::Opcode::DS_MAX_RTN_I32, Opcode::AtomicSMaxI32},
{Decoder::Opcode::DS_MIN_U32, Opcode::AtomicUMinU32},
{Decoder::Opcode::DS_MIN_RTN_U32, Opcode::AtomicUMinU32},
{Decoder::Opcode::DS_MAX_U32, Opcode::AtomicUMaxU32},
{Decoder::Opcode::DS_MAX_RTN_U32, Opcode::AtomicUMaxU32},
{Decoder::Opcode::DS_AND_B32, Opcode::AtomicAndU32},
{Decoder::Opcode::DS_AND_RTN_B32, Opcode::AtomicAndU32},
{Decoder::Opcode::DS_OR_B32, Opcode::AtomicOrU32},
{Decoder::Opcode::DS_OR_RTN_B32, Opcode::AtomicOrU32},
{Decoder::Opcode::DS_XOR_B32, Opcode::AtomicXorU32},
{Decoder::Opcode::DS_XOR_RTN_B32, Opcode::AtomicXorU32},
{Decoder::Opcode::DS_WRXCHG_RTN_B32, Opcode::AtomicSwapU32},
{Decoder::Opcode::DS_READ_I8, Opcode::DsReadSbyte},
{Decoder::Opcode::DS_READ_U8, Opcode::DsReadUbyte},
{Decoder::Opcode::DS_READ_I16, Opcode::DsReadSshort},
{Decoder::Opcode::DS_READ_U16, Opcode::DsReadUshort},
{Decoder::Opcode::DS_READ_U16_D16, Opcode::DsReadUshort},
{Decoder::Opcode::DS_READ2_B32, Opcode::DsRead2B32},
{Decoder::Opcode::DS_READ2ST64_B32, Opcode::DsRead2B32},
{Decoder::Opcode::DS_READ_B32, Opcode::DsReadB32},
{Decoder::Opcode::DS_READ_B64, Opcode::DsReadB32},
{Decoder::Opcode::DS_READ2_B64, Opcode::DsRead2B32},
{Decoder::Opcode::DS_READ2ST64_B64, Opcode::DsRead2B32},
{Decoder::Opcode::DS_READ_B96, Opcode::DsReadB32},
{Decoder::Opcode::DS_READ_B128, Opcode::DsReadB32},
{Decoder::Opcode::DS_WRITE_B8, Opcode::DsWriteByte},
{Decoder::Opcode::DS_WRITE_B16, Opcode::DsWriteShort},
{Decoder::Opcode::DS_WRITE2_B32, Opcode::DsWrite2B32},
{Decoder::Opcode::DS_WRITE2ST64_B32, Opcode::DsWrite2B32},
{Decoder::Opcode::DS_WRITE2_B64, Opcode::DsWrite2B32},
{Decoder::Opcode::DS_WRITE2ST64_B64, Opcode::DsWrite2B32},
{Decoder::Opcode::DS_WRITE_B32, Opcode::DsWriteB32},
{Decoder::Opcode::DS_WRITE_B64, Opcode::DsWriteB32},
{Decoder::Opcode::DS_WRITE_B96, Opcode::DsWriteB32},
{Decoder::Opcode::DS_WRITE_B128, Opcode::DsWriteB32},
{Decoder::Opcode::DS_MIN_F32, Opcode::DsMinF32},
{Decoder::Opcode::DS_MAX_F32, Opcode::DsMaxF32},
{Decoder::Opcode::DS_CONSUME, Opcode::DsConsume},
{Decoder::Opcode::DS_APPEND, Opcode::DsAppend},
{Decoder::Opcode::DS_WRITE_ADDTID_B32, Opcode::DsWriteAddtidB32},
{Decoder::Opcode::DS_READ_ADDTID_B32, Opcode::DsReadAddtidB32},
{Decoder::Opcode::IMAGE_GET_RESINFO, Opcode::ImageGetResinfo},
{Decoder::Opcode::IMAGE_GET_LOD, Opcode::ImageGetLod},
{Decoder::Opcode::IMAGE_LOAD, Opcode::ImageLoad},
{Decoder::Opcode::IMAGE_LOAD_MIP, Opcode::ImageLoad},
{Decoder::Opcode::IMAGE_STORE, Opcode::ImageStore},
{Decoder::Opcode::IMAGE_STORE_MIP, Opcode::ImageStore},
{Decoder::Opcode::IMAGE_ATOMIC_ADD, Opcode::AtomicAddU32},
{Decoder::Opcode::IMAGE_ATOMIC_UMIN, Opcode::AtomicUMinU32},
{Decoder::Opcode::IMAGE_ATOMIC_UMAX, Opcode::AtomicUMaxU32},
{Decoder::Opcode::IMAGE_ATOMIC_AND, Opcode::AtomicAndU32},
{Decoder::Opcode::IMAGE_ATOMIC_OR, Opcode::AtomicOrU32},
{Decoder::Opcode::IMAGE_ATOMIC_XOR, Opcode::AtomicXorU32},
{Decoder::Opcode::IMAGE_SAMPLE, Opcode::ImageSample},
{Decoder::Opcode::IMAGE_GATHER4_LZ, Opcode::ImageGather4},
{Decoder::Opcode::IMAGE_GATHER4_C, Opcode::ImageGather4},
{Decoder::Opcode::IMAGE_GATHER4_C_LZ, Opcode::ImageGather4},
{Decoder::Opcode::IMAGE_GATHER4_LZ_O, Opcode::ImageGather4},
{Decoder::Opcode::IMAGE_GATHER4_C_O, Opcode::ImageGather4},
{Decoder::Opcode::IMAGE_GATHER4_C_LZ_O, Opcode::ImageGather4},
{Decoder::Opcode::IMAGE_GATHER4H, Opcode::ImageGather4},
};
} // namespace
void SetError(std::string* error, const char* message) {
if (error != nullptr) {
*error = message;
}
}
std::optional<Opcode> LookupIrOpcode(Decoder::Opcode opcode) {
for (const auto& op: LOWER_OPS) {
if (op.decoded == opcode) {
return op.ir;
}
}
return std::nullopt;
}
bool IsReversedBinary(Decoder::Opcode opcode) {
switch (opcode) {
case Decoder::Opcode::V_SUBREV_F32:
case Decoder::Opcode::V_SUBREV_F16:
case Decoder::Opcode::V_SUBREV_NC_U32:
case Decoder::Opcode::V_LSHLREV_B32:
case Decoder::Opcode::V_LSHRREV_B32:
case Decoder::Opcode::V_ASHRREV_I32:
case Decoder::Opcode::V_LSHLREV_B64:
case Decoder::Opcode::V_LSHRREV_B64:
case Decoder::Opcode::V_LSHLREV_B16:
case Decoder::Opcode::V_LSHRREV_B16:
case Decoder::Opcode::V_ASHRREV_I16:
case Decoder::Opcode::V_SUBREV_I32: return true;
default: return false;
}
}
bool IsVectorCarryOutOpcode(Decoder::Opcode opcode) {
switch (opcode) {
case Decoder::Opcode::V_ADD_I32:
case Decoder::Opcode::V_SUB_I32:
case Decoder::Opcode::V_SUBREV_I32: return true;
default: return false;
}
}
bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode) {
switch (opcode) {
case Decoder::Opcode::S_ABS_I32:
case Decoder::Opcode::S_BCNT1_I32_B32:
case Decoder::Opcode::S_BCNT1_I32_B64:
case Decoder::Opcode::S_AND_B32:
case Decoder::Opcode::S_ANDN2_B32:
case Decoder::Opcode::S_OR_B32:
case Decoder::Opcode::S_ORN2_B32:
case Decoder::Opcode::S_XOR_B32:
case Decoder::Opcode::S_NAND_B32:
case Decoder::Opcode::S_NOR_B32:
case Decoder::Opcode::S_XNOR_B32:
case Decoder::Opcode::S_NOT_B32:
case Decoder::Opcode::S_LSHL_B32:
case Decoder::Opcode::S_LSHR_B32:
case Decoder::Opcode::S_ASHR_I32:
case Decoder::Opcode::S_BFE_U32:
case Decoder::Opcode::S_BFE_I32:
case Decoder::Opcode::S_AND_B64:
case Decoder::Opcode::S_ANDN2_B64:
case Decoder::Opcode::S_NOT_B64:
case Decoder::Opcode::S_OR_B64:
case Decoder::Opcode::S_ORN2_B64:
case Decoder::Opcode::S_XOR_B64:
case Decoder::Opcode::S_NAND_B64:
case Decoder::Opcode::S_NOR_B64:
case Decoder::Opcode::S_XNOR_B64:
case Decoder::Opcode::S_LSHL_B64:
case Decoder::Opcode::S_LSHR_B64:
case Decoder::Opcode::S_BFE_U64:
case Decoder::Opcode::S_WQM_B64:
case Decoder::Opcode::S_QUADMASK_B64: return true;
default: return false;
}
}
bool ScalarResultIs64Bit(Decoder::Opcode opcode) {
switch (opcode) {
case Decoder::Opcode::S_AND_B64:
case Decoder::Opcode::S_ANDN2_B64:
case Decoder::Opcode::S_NOT_B64:
case Decoder::Opcode::S_OR_B64:
case Decoder::Opcode::S_ORN2_B64:
case Decoder::Opcode::S_XOR_B64:
case Decoder::Opcode::S_NAND_B64:
case Decoder::Opcode::S_NOR_B64:
case Decoder::Opcode::S_XNOR_B64:
case Decoder::Opcode::S_LSHL_B64:
case Decoder::Opcode::S_LSHR_B64:
case Decoder::Opcode::S_BFE_U64:
case Decoder::Opcode::S_WQM_B64:
case Decoder::Opcode::S_QUADMASK_B64: return true;
default: return false;
}
}
} // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,354 +0,0 @@
// Semantic operations produced by RDNA2 instruction lowering. The typed frontend consumes this
// baseline set before optimization and resource planning; it must not grow without decoder support.
IR_OPCODE(ControlNop, General, Control, Void)
IR_OPCODE(Waitcnt, General, Control, Void)
IR_OPCODE(Barrier, General, Control, Void)
IR_OPCODE(Sendmsg, General, Control, Void)
IR_OPCODE(TtraceData, General, Control, Void)
IR_OPCODE(InstPrefetch, General, Control, Void)
IR_OPCODE(MoveU32, General, Move)
IR_OPCODE(MoveF32Bits, General, Move)
IR_OPCODE(MoveRelDestU32, General, Lane)
IR_OPCODE(MoveRelSourceU32, General, Lane)
IR_OPCODE(MoveU64, General, Move)
IR_OPCODE(WqmB64, General, Move)
IR_OPCODE(SaveexecB32, General, State)
IR_OPCODE(SaveexecB64, General, State)
IR_OPCODE(ReadFirstLaneU32, General, Lane)
IR_OPCODE(ReadLaneU32, General, Lane)
IR_OPCODE(WriteLaneU32, General, Lane)
IR_OPCODE(Permlane16B32, General, Lane)
IR_OPCODE(Permlanex16B32, General, Lane)
IR_OPCODE(AbsI32, General, SimpleInteger)
IR_OPCODE(IAddU32, General, SimpleInteger)
IR_OPCODE(IAddCarryU32, General, State)
IR_OPCODE(ISubBorrowCarryU32, General, State)
IR_OPCODE(ISubBorrowU32, General, State)
IR_OPCODE(ScalarAddCarryU32, General, State)
IR_OPCODE(ScalarSubBorrowU32, General, State)
IR_OPCODE(ScalarSubBorrowCarryU32, General, State)
IR_OPCODE(ScalarSignedAddOverflowI32, General, State)
IR_OPCODE(ScalarSignedSubOverflowI32, General, State)
IR_OPCODE(ScalarShiftLeftAddCarryU32, General, State)
IR_OPCODE(ISubU32, General, SimpleInteger)
IR_OPCODE(IMulU32, General, SimpleInteger)
IR_OPCODE(UMulHighU32, General, SimpleInteger)
IR_OPCODE(SMulHighI32, General, SimpleInteger)
IR_OPCODE(IMadI24U32, General, ExtendedInteger)
IR_OPCODE(UMadU24U32, General, ExtendedInteger)
IR_OPCODE(UMadU64U32, General, ExtendedInteger)
IR_OPCODE(SadU32, General, ExtendedInteger)
IR_OPCODE(IAdd3U32, General, ExtendedInteger)
IR_OPCODE(IMulI24U32, General, ExtendedInteger)
IR_OPCODE(UMulU24U32, General, ExtendedInteger)
IR_OPCODE(IMinI32, General, SimpleInteger)
IR_OPCODE(IMaxI32, General, SimpleInteger)
IR_OPCODE(IMin3I32, General, SimpleInteger)
IR_OPCODE(IMax3I32, General, SimpleInteger)
IR_OPCODE(IMed3I32, General, SimpleInteger)
IR_OPCODE(UMinU32, General, SimpleInteger)
IR_OPCODE(UMaxU32, General, SimpleInteger)
IR_OPCODE(UMin3U32, General, SimpleInteger)
IR_OPCODE(UMax3U32, General, SimpleInteger)
IR_OPCODE(UMed3U32, General, SimpleInteger)
IR_OPCODE(BitwiseAndU32, General, SimpleInteger)
IR_OPCODE(BitwiseAndU64, General, U64Mask)
IR_OPCODE(BitwiseAndNotU32, General, ComposedInteger)
IR_OPCODE(BitwiseAndNotU64, General, U64Mask)
IR_OPCODE(BitwiseOrU32, General, SimpleInteger)
IR_OPCODE(BitwiseOrU64, General, U64Mask)
IR_OPCODE(BitwiseOrNotU32, General, ComposedInteger)
IR_OPCODE(BitwiseOrNotU64, General, U64Mask)
IR_OPCODE(BitwiseAndOrU32, General, ComposedInteger)
IR_OPCODE(BitwiseOr3U32, General, ComposedInteger)
IR_OPCODE(BitwiseXorU32, General, SimpleInteger)
IR_OPCODE(BitwiseXorU64, General, U64Mask)
IR_OPCODE(BitwiseXor3U32, General, ComposedInteger)
IR_OPCODE(BitwiseNandU32, General, ComposedInteger)
IR_OPCODE(BitwiseNandU64, General, U64Mask)
IR_OPCODE(BitwiseNorU32, General, ComposedInteger)
IR_OPCODE(BitwiseNorU64, General, U64Mask)
IR_OPCODE(BitwiseXnorU32, General, ComposedInteger)
IR_OPCODE(BitwiseXnorU64, General, U64Mask)
IR_OPCODE(BitwiseNotU32, General, SimpleInteger)
IR_OPCODE(BitwiseNotU64, General, U64Mask)
IR_OPCODE(BitClearU32, General, ExtendedInteger)
IR_OPCODE(BitSetU32, General, ExtendedInteger)
IR_OPCODE(BitReverseU32, General, SimpleInteger)
IR_OPCODE(BitCountU32, General, SimpleInteger)
IR_OPCODE(BitCountU64, General, SimpleInteger)
IR_OPCODE(BitReplicateB64B32, General, ExtendedInteger)
IR_OPCODE(QuadmaskB64, General, ExtendedInteger)
IR_OPCODE(BitCountAddU32, General, ExtendedInteger)
IR_OPCODE(MaskedBitCountLowU32, General, ExtendedInteger)
IR_OPCODE(MaskedBitCountHighU32, General, ExtendedInteger)
IR_OPCODE(FindLsbU32, General, SimpleInteger)
IR_OPCODE(FindLsbU64, General, ComposedInteger)
IR_OPCODE(FindMsbFromHighU32, General, ComposedInteger)
IR_OPCODE(FindMsbFromHighU64, General, ComposedInteger)
IR_OPCODE(BitFieldMaskU32, General, ExtendedInteger)
IR_OPCODE(BitFieldMaskU64, General, ExtendedInteger)
IR_OPCODE(BitFieldExtractU32, General, ExtendedInteger)
IR_OPCODE(BitFieldExtractI32, General, ExtendedInteger)
IR_OPCODE(BitFieldExtractU64, General, ExtendedInteger)
IR_OPCODE(BitFieldExtract3U32, General, ExtendedInteger)
IR_OPCODE(BitFieldExtract3I32, General, ExtendedInteger)
IR_OPCODE(BitFieldInsertSelectU32, General, ExtendedInteger)
IR_OPCODE(BitCompare0B32, General, ExtendedInteger)
IR_OPCODE(BitCompare1B32, General, ExtendedInteger)
IR_OPCODE(AlignBitU32, General, ExtendedInteger)
IR_OPCODE(AlignByteU32, General, ExtendedInteger)
IR_OPCODE(ShiftLeftAddU32, General, ExtendedInteger)
IR_OPCODE(AddShiftLeftU32, General, ExtendedInteger)
IR_OPCODE(XorAddU32, General, ExtendedInteger)
IR_OPCODE(ShiftLeftOrU32, General, ExtendedInteger)
IR_OPCODE(ShiftLeftLogicalU32, General, SimpleInteger)
IR_OPCODE(ShiftLeftLogicalU64, General, SimpleInteger)
IR_OPCODE(ShiftLeftLogicalU16, General, Integer16)
IR_OPCODE(ShiftRightLogicalU32, General, SimpleInteger)
IR_OPCODE(ShiftRightLogicalU64, General, SimpleInteger)
IR_OPCODE(ShiftRightLogicalU16, General, Integer16)
IR_OPCODE(ShiftRightArithmeticI32, General, SimpleInteger)
IR_OPCODE(ShiftRightArithmeticI16, General, Integer16)
IR_OPCODE(SelectU32, General, ExtendedInteger, U32, U1, U32, U32)
IR_OPCODE(SelectMaskU32, General, ExtendedInteger)
IR_OPCODE(SelectF32Bits, General, ExtendedInteger)
IR_OPCODE(SelectMaskF32Bits, General, ExtendedInteger)
IR_OPCODE(SelectU64, General, ExtendedInteger)
IR_OPCODE(PackLowLowU16, General, ExtendedInteger)
IR_OPCODE(PackLowHighU16, General, ExtendedInteger)
IR_OPCODE(PackHighHighU16, General, ExtendedInteger)
IR_OPCODE(CompareFalse, Compare, IntegerCompare)
IR_OPCODE(CompareTrue, Compare, IntegerCompare)
IR_OPCODE(CompareEqU32, Compare, IntegerCompare)
IR_OPCODE(CompareNeU32, Compare, IntegerCompare)
IR_OPCODE(CompareGtU32, Compare, IntegerCompare)
IR_OPCODE(CompareGeU32, Compare, IntegerCompare)
IR_OPCODE(CompareLtU32, Compare, IntegerCompare)
IR_OPCODE(CompareLeU32, Compare, IntegerCompare)
IR_OPCODE(CompareEqU64, Compare64, IntegerCompare)
IR_OPCODE(CompareGtU64, Compare64, IntegerCompare)
IR_OPCODE(CompareNeU64, Compare64, IntegerCompare)
IR_OPCODE(CompareMaskEqU32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskNeU32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskGtU32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskGeU32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskLtU32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskLeU32, Compare, IntegerCompare)
IR_OPCODE(CompareEqI32, Compare, IntegerCompare)
IR_OPCODE(CompareNeI32, Compare, IntegerCompare)
IR_OPCODE(CompareGtI32, Compare, IntegerCompare)
IR_OPCODE(CompareGeI32, Compare, IntegerCompare)
IR_OPCODE(CompareLtI32, Compare, IntegerCompare)
IR_OPCODE(CompareLeI32, Compare, IntegerCompare)
IR_OPCODE(CompareEqI16, Compare, Integer16Compare)
IR_OPCODE(CompareNeI16, Compare, Integer16Compare)
IR_OPCODE(CompareGtI16, Compare, Integer16Compare)
IR_OPCODE(CompareGeI16, Compare, Integer16Compare)
IR_OPCODE(CompareLtI16, Compare, Integer16Compare)
IR_OPCODE(CompareLeI16, Compare, Integer16Compare)
IR_OPCODE(CompareMaskEqI32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskNeI32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskGtI32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskGeI32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskLtI32, Compare, IntegerCompare)
IR_OPCODE(CompareMaskLeI32, Compare, IntegerCompare)
IR_OPCODE(CompareEqU16, Compare, Integer16Compare)
IR_OPCODE(CompareNeU16, Compare, Integer16Compare)
IR_OPCODE(CompareGtU16, Compare, Integer16Compare)
IR_OPCODE(CompareGeU16, Compare, Integer16Compare)
IR_OPCODE(CompareLtU16, Compare, Integer16Compare)
IR_OPCODE(CompareLeU16, Compare, Integer16Compare)
IR_OPCODE(CompareEqF32, Compare, FloatCompare)
IR_OPCODE(CompareNeF32, Compare, FloatCompare)
IR_OPCODE(CompareGtF32, Compare, FloatCompare)
IR_OPCODE(CompareGeF32, Compare, FloatCompare)
IR_OPCODE(CompareLtF32, Compare, FloatCompare)
IR_OPCODE(CompareLeF32, Compare, FloatCompare)
IR_OPCODE(CompareOrderedF32, Compare, FloatCompare)
IR_OPCODE(CompareUnorderedF32, Compare, FloatCompare)
IR_OPCODE(CompareUnordEqF32, Compare, FloatCompare)
IR_OPCODE(CompareUnordNeF32, Compare, FloatCompare)
IR_OPCODE(CompareUnordGtF32, Compare, FloatCompare)
IR_OPCODE(CompareUnordGeF32, Compare, FloatCompare)
IR_OPCODE(CompareUnordLtF32, Compare, FloatCompare)
IR_OPCODE(CompareUnordLeF32, Compare, FloatCompare)
IR_OPCODE(CompareClassF32, Compare, FloatCompare)
IR_OPCODE(CompareEqF16, Compare, FloatCompare)
IR_OPCODE(CompareNeF16, Compare, FloatCompare)
IR_OPCODE(CompareGtF16, Compare, FloatCompare)
IR_OPCODE(CompareGeF16, Compare, FloatCompare)
IR_OPCODE(CompareLtF16, Compare, FloatCompare)
IR_OPCODE(CompareLeF16, Compare, FloatCompare)
IR_OPCODE(CompareUnordNeF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskEqF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskNeF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskGtF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskGeF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskLtF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskLeF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordNeF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordGeF16, Compare, FloatCompare)
IR_OPCODE(CompareMaskEqF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskNeF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskGtF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskGeF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskLtF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskLeF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordEqF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordNeF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordGtF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordGeF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordLtF32, Compare, FloatCompare)
IR_OPCODE(CompareMaskUnordLeF32, Compare, FloatCompare)
IR_OPCODE(ConvertByteU32ToF32, General, Conversion)
IR_OPCODE(ConvertU32ToF32, General, Conversion)
IR_OPCODE(ConvertI32ToF32, General, Conversion)
IR_OPCODE(ConvertF32ToU32, General, Conversion)
IR_OPCODE(ConvertF32ToI32, General, Conversion)
IR_OPCODE(ConvertF32ToF16, General, Conversion)
IR_OPCODE(ConvertF16ToF32, General, Conversion)
IR_OPCODE(ConvertU16ToF16, General, Conversion)
IR_OPCODE(ConvertF16ToU16, General, Conversion)
IR_OPCODE(ConvertI16ToF16, General, Conversion)
IR_OPCODE(ConvertF16ToI16, General, Conversion)
IR_OPCODE(ConvertRoundPlusInfF32ToI32, General, Conversion)
IR_OPCODE(ConvertFloorF32ToI32, General, Conversion)
IR_OPCODE(FrexpExpI32F32, General, Conversion)
IR_OPCODE(FrexpMantF32, General, Float)
IR_OPCODE(ConvertI4ToOffsetF32, General, Conversion)
IR_OPCODE(LdexpF32, General, Float)
IR_OPCODE(PackF32ToF16Rtz, General, Conversion)
IR_OPCODE(PackSnorm2x16F32, General, Conversion)
IR_OPCODE(PackUnorm2x16F32, General, Conversion)
IR_OPCODE(PackU16U32, General, ExtendedInteger)
IR_OPCODE(PackU8F32, General, Conversion)
IR_OPCODE(PackB32F16, General, Conversion)
IR_OPCODE(PackedMadI16, General, PackedInteger16)
IR_OPCODE(PackedMulLoU16, General, PackedInteger16)
IR_OPCODE(PackedAddI16, General, PackedInteger16)
IR_OPCODE(PackedSubI16, General, PackedInteger16)
IR_OPCODE(PackedLshlrevB16, General, PackedInteger16)
IR_OPCODE(PackedLshrrevB16, General, PackedInteger16)
IR_OPCODE(PackedAshrrevI16, General, PackedInteger16)
IR_OPCODE(PackedMaxI16, General, PackedInteger16)
IR_OPCODE(PackedMinI16, General, PackedInteger16)
IR_OPCODE(PackedMadU16, General, PackedInteger16)
IR_OPCODE(PackedAddU16, General, PackedInteger16)
IR_OPCODE(PackedSubU16, General, PackedInteger16)
IR_OPCODE(PackedMaxU16, General, PackedInteger16)
IR_OPCODE(PackedMinU16, General, PackedInteger16)
IR_OPCODE(PackedAddF16, General, PackedFloat16)
IR_OPCODE(PackedMulF16, General, PackedFloat16)
IR_OPCODE(PackedMinF16, General, PackedFloat16)
IR_OPCODE(PackedMaxF16, General, PackedFloat16)
IR_OPCODE(PackedFmaF16, General, PackedFloat16)
IR_OPCODE(AddF16, General, Float16)
IR_OPCODE(SubF16, General, Float16)
IR_OPCODE(MulF16, General, Float16)
IR_OPCODE(MinF16, General, Float16)
IR_OPCODE(MaxF16, General, Float16)
IR_OPCODE(FmaF16, General, Float16)
IR_OPCODE(MadMixF16, General, Float16)
IR_OPCODE(IAddU16, General, Integer16)
IR_OPCODE(ISubI16, General, Integer16)
IR_OPCODE(IMinI16, General, Integer16)
IR_OPCODE(IMaxI16, General, Integer16)
IR_OPCODE(IMed3I16, General, Integer16)
IR_OPCODE(UMinU16, General, Integer16)
IR_OPCODE(UMaxU16, General, Integer16)
IR_OPCODE(RcpF32, General, Float)
IR_OPCODE(RcpIflagF32, General, Float)
IR_OPCODE(FractF32, General, Float)
IR_OPCODE(TruncF32, General, Float)
IR_OPCODE(CeilF32, General, Float)
IR_OPCODE(RoundEvenF32, General, Float)
IR_OPCODE(FloorF32, General, Float)
IR_OPCODE(Exp2F32, General, Float)
IR_OPCODE(Log2F32, General, Float)
IR_OPCODE(InverseSqrtF32, General, Float)
IR_OPCODE(SqrtF32, General, Float)
IR_OPCODE(RcpF16, General, Float16)
IR_OPCODE(SqrtF16, General, Float16)
IR_OPCODE(InverseSqrtF16, General, Float16)
IR_OPCODE(Log2F16, General, Float16)
IR_OPCODE(Exp2F16, General, Float16)
IR_OPCODE(FloorF16, General, Float16)
IR_OPCODE(CeilF16, General, Float16)
IR_OPCODE(TruncF16, General, Float16)
IR_OPCODE(RoundEvenF16, General, Float16)
IR_OPCODE(SinF32, General, Float)
IR_OPCODE(CosF32, General, Float)
IR_OPCODE(CubeIdF32, General, Float)
IR_OPCODE(CubeScF32, General, Float)
IR_OPCODE(CubeTcF32, General, Float)
IR_OPCODE(CubeMaF32, General, Float)
IR_OPCODE(FAddF32, General, Float)
IR_OPCODE(FSubF32, General, Float)
IR_OPCODE(FMulF32, General, Float)
IR_OPCODE(FMinF32, General, Float)
IR_OPCODE(FMaxF32, General, Float)
IR_OPCODE(FMadF32, General, Float)
IR_OPCODE(Dot2AccF32F16, General, Float)
IR_OPCODE(FMin3F32, General, Float)
IR_OPCODE(FMax3F32, General, Float)
IR_OPCODE(FMed3F32, General, Float)
IR_OPCODE(Min3F16, General, Float16)
IR_OPCODE(Max3F16, General, Float16)
IR_OPCODE(Med3F16, General, Float16)
IR_OPCODE(LoadSrtDword, General, Memory)
IR_OPCODE(SLoadDword, General, Memory)
IR_OPCODE(SBufferLoadDword, General, Memory)
IR_OPCODE(BufferLoadUbyte, General, Memory)
IR_OPCODE(BufferLoadSbyte, General, Memory)
IR_OPCODE(BufferLoadUshort, General, Memory)
IR_OPCODE(BufferLoadSshort, General, Memory)
IR_OPCODE(BufferLoadDword, General, Memory)
IR_OPCODE(BufferStoreByte, General, Memory)
IR_OPCODE(BufferStoreShort, General, Memory)
IR_OPCODE(BufferStoreDword, General, Memory)
IR_OPCODE(AtomicSwapU32, General, Memory)
IR_OPCODE(AtomicAddU32, General, Memory)
IR_OPCODE(AtomicSubU32, General, Memory)
IR_OPCODE(AtomicSMinI32, General, Memory)
IR_OPCODE(AtomicUMinU32, General, Memory)
IR_OPCODE(AtomicSMaxI32, General, Memory)
IR_OPCODE(AtomicUMaxU32, General, Memory)
IR_OPCODE(AtomicAndU32, General, Memory)
IR_OPCODE(AtomicOrU32, General, Memory)
IR_OPCODE(AtomicXorU32, General, Memory)
IR_OPCODE(AtomicFMinF32, General, Memory)
IR_OPCODE(AtomicFMaxF32, General, Memory)
IR_OPCODE(FlatLoadUbyte, General, Memory)
IR_OPCODE(FlatLoadSbyte, General, Memory)
IR_OPCODE(FlatLoadUshort, General, Memory)
IR_OPCODE(FlatLoadSshort, General, Memory)
IR_OPCODE(FlatLoadDword, General, Memory)
IR_OPCODE(FlatStoreByte, General, Memory)
IR_OPCODE(FlatStoreShort, General, Memory)
IR_OPCODE(FlatStoreDword, General, Memory)
IR_OPCODE(DsReadUbyte, General, Memory)
IR_OPCODE(DsReadSbyte, General, Memory)
IR_OPCODE(DsReadUshort, General, Memory)
IR_OPCODE(DsReadSshort, General, Memory)
IR_OPCODE(DsReadB32, General, Memory)
IR_OPCODE(DsRead2B32, General, Memory)
IR_OPCODE(DsWriteByte, General, Memory)
IR_OPCODE(DsWriteShort, General, Memory)
IR_OPCODE(DsWriteB32, General, Memory)
IR_OPCODE(DsWrite2B32, General, Memory)
IR_OPCODE(DsMinF32, General, Memory)
IR_OPCODE(DsMaxF32, General, Memory)
IR_OPCODE(DsSwizzleB32, General, Memory)
IR_OPCODE(DsConsume, General, Memory)
IR_OPCODE(DsAppend, General, Memory)
IR_OPCODE(DsWriteAddtidB32, General, Memory)
IR_OPCODE(DsReadAddtidB32, General, Memory)
IR_OPCODE(ImageGetResinfo, General, Memory)
IR_OPCODE(ImageGetLod, General, Memory)
IR_OPCODE(ImageLoad, General, Memory)
IR_OPCODE(ImageStore, General, Memory)
IR_OPCODE(ImageSample, General, Memory)
IR_OPCODE(ImageGather4, General, Memory)
IR_OPCODE(LoadInputF32, General, Attribute)
IR_OPCODE(Export, General, Attribute)
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h" #include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@@ -16,7 +16,7 @@ constexpr uint32_t ImageBindingCount =
bool CollectUserData(const Program& program, std::vector<uint32_t>& result) { bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
std::array<bool, NumScalarRegs> registers {}; std::array<bool, NumScalarRegs> registers {};
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
if (inst.GetOpcode() != ValueOpcode::GetUserData || !inst.HasUses()) { if (inst.GetOpcode() != ValueOpcode::GetUserData || !inst.HasUses()) {
continue; continue;
@@ -47,16 +47,16 @@ void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
bool UsesGds(const Program& program, bool& uses_gds) { bool UsesGds(const Program& program, bool& uses_gds) {
uses_gds = false; uses_gds = false;
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
if (SharedAccessOf(inst.GetOpcode()) == SharedAccess::None) { if (SharedAccessOf(inst.GetOpcode()) == SharedAccess::None) {
continue; continue;
} }
const auto index = inst.Flags<MemoryFlags>().index; const auto index = inst.Flags<MemoryFlags>().index;
if (index >= program.values->memory_info.size()) { if (index >= program.memory_info.size()) {
return false; return false;
} }
const auto kind = program.values->memory_info[index].kind; const auto kind = program.memory_info[index].kind;
if (kind != ResourceKind::Lds && kind != ResourceKind::Gds) { if (kind != ResourceKind::Lds && kind != ResourceKind::Gds) {
return false; return false;
} }
@@ -76,12 +76,6 @@ bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::stri
} }
return false; return false;
} }
if (program.values == nullptr) {
if (error != nullptr) {
*error = "typed value program is not available";
}
return false;
}
if (push_constant_offset > NativePushConstantSize || if (push_constant_offset > NativePushConstantSize ||
push_constant_offset % sizeof(uint32_t) != 0) { push_constant_offset % sizeof(uint32_t) != 0) {
if (error != nullptr) { if (error != nullptr) {
@@ -171,7 +165,7 @@ bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::stri
AddBinding(next, DescriptorBindingKind::AddressMemory, std::move(resources)); AddBinding(next, DescriptorBindingKind::AddressMemory, std::move(resources));
} }
const bool uses_flattened_runtime = const bool uses_flattened_runtime =
!program.values->srt_reads.empty() || !program.srt_reads.empty() ||
std::ranges::any_of(program.info.images, [](const ImageResource& image) { std::ranges::any_of(program.info.images, [](const ImageResource& image) {
return image.indirect_mapping_capacity != 0u; return image.indirect_mapping_capacity != 0u;
}); });
@@ -101,7 +101,7 @@ Value GetRealValue(PhiMap& phi_map, Value source, uint32_t lane, uint32_t wave_s
} // namespace } // namespace
ReadLaneStats EliminateReadLane(ValueProgram& program, uint32_t wave_size) { ReadLaneStats EliminateReadLane(Program& program, uint32_t wave_size) {
ReadLaneStats stats; ReadLaneStats stats;
if (wave_size != 32u && wave_size != 64u) { if (wave_size != 32u && wave_size != 64u) {
return stats; return stats;
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
namespace Libs::Graphics::ShaderRecompiler::IR { namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -8,6 +8,6 @@ struct ReadLaneStats {
uint32_t rewritten_reads = 0; uint32_t rewritten_reads = 0;
}; };
[[nodiscard]] ReadLaneStats EliminateReadLane(ValueProgram& program, uint32_t wave_size); [[nodiscard]] ReadLaneStats EliminateReadLane(Program& program, uint32_t wave_size);
} // namespace Libs::Graphics::ShaderRecompiler::IR } // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,7 +1,7 @@
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h" #include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
#include "graphics/guest_gpu/gpu_format.h" #include "graphics/guest_gpu/gpu_format.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shaderBindings.h" #include "graphics/shader/shaderBindings.h"
#include <algorithm> #include <algorithm>
@@ -105,10 +105,10 @@ bool DecodeBufferDescriptor(const DescriptorValue& descriptor, ShaderBufferResou
} }
const DescriptorSource* Source(const Program& program, uint32_t source) { const DescriptorSource* Source(const Program& program, uint32_t source) {
if (program.values == nullptr || source >= program.values->descriptor_sources.size()) { if (source >= program.descriptor_sources.size()) {
return nullptr; return nullptr;
} }
return &program.values->descriptor_sources[source]; return &program.descriptor_sources[source];
} }
void MarkCleanFlatSlots(const Program& program, const DescriptorSource* source, void MarkCleanFlatSlots(const Program& program, const DescriptorSource* source,
@@ -131,7 +131,7 @@ void MarkCleanFlatSlots(const Program& program, const DescriptorSource* source,
const auto slot = inst->Arg(1).Resolve(); const auto slot = inst->Arg(1).Resolve();
if (slot.IsImmediate() && slot.GetType() == Type::U32 && slot.U32() < slots.size()) { if (slot.IsImmediate() && slot.GetType() == Type::U32 && slot.U32() < slots.size()) {
slots[slot.U32()] = 1u; slots[slot.U32()] = 1u;
pending.push_back(program.values->srt_reads[slot.U32()].value); pending.push_back(program.srt_reads[slot.U32()].value);
} }
continue; continue;
} }
@@ -275,7 +275,7 @@ uint64_t AddressSpecialization(const AddressResource& resource,
} }
size_t FlattenedRuntimeDwords(const Program& program) { size_t FlattenedRuntimeDwords(const Program& program) {
size_t size = program.values != nullptr ? program.values->srt_reads.size() : 0u; size_t size = program.srt_reads.size();
for (uint32_t resource = 0; resource < program.info.images.size(); resource++) { for (uint32_t resource = 0; resource < program.info.images.size(); resource++) {
const auto& image = program.info.images[resource]; const auto& image = program.info.images[resource];
if (image.indirect_root == resource) { if (image.indirect_root == resource) {
@@ -305,8 +305,7 @@ bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& sn
} }
return false; return false;
} }
if (program.values == nullptr || if (snapshot.flattened_srt.size() != FlattenedRuntimeDwords(program)) {
snapshot.flattened_srt.size() != FlattenedRuntimeDwords(program)) {
if (error != nullptr) { if (error != nullptr) {
*error = "flattened SRT snapshot does not match the shader plan"; *error = "flattened SRT snapshot does not match the shader plan";
} }
@@ -549,7 +548,7 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
} }
std::vector<DescriptorSourceRequest> requests; std::vector<DescriptorSourceRequest> requests;
std::vector<uint8_t> clean_flat_slots(program.values->srt_reads.size()); std::vector<uint8_t> clean_flat_slots(program.srt_reads.size());
requests.reserve(program.info.buffers.size() + program.info.images.size() * 2u + requests.reserve(program.info.buffers.size() + program.info.images.size() * 2u +
program.info.samplers.size() + program.info.addresses.size()); program.info.samplers.size() + program.info.addresses.size());
for (const auto& buffer: program.info.buffers) { for (const auto& buffer: program.info.buffers) {
@@ -925,13 +924,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
} }
} }
} }
if (program.values == nullptr) { auto memory_info = program.memory_info;
if (error != nullptr) {
*error = "typed SSA is not ready";
}
return false;
}
auto memory_info = program.values->memory_info;
struct SamplerUsage { struct SamplerUsage {
bool native = false; bool native = false;
@@ -980,7 +973,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
pair.sampler = point_sampler[pair.sampler]; pair.sampler = point_sampler[pair.sampler];
} }
} }
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
const auto image_opcode = ImageOpcodeInfoOf(inst.GetOpcode()); const auto image_opcode = ImageOpcodeInfoOf(inst.GetOpcode());
if (image_opcode.access == ImageAccess::None) { if (image_opcode.access == ImageAccess::None) {
@@ -1023,7 +1016,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
} }
} }
program.info = std::move(next); program.info = std::move(next);
program.values->memory_info = std::move(memory_info); program.memory_info = std::move(memory_info);
snapshot = std::move(next_snapshot); snapshot = std::move(next_snapshot);
return true; return true;
} }
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/ir/passes/ResourceTracking.h" #include "graphics/shader/recompiler/ir/passes/ResourceTracking.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h" #include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
@@ -94,8 +94,7 @@ ImageMipMode MipMode(const MemoryInfo& memory) {
class Tracker { class Tracker {
public: public:
Tracker(Program& program, ValueProgram& values) explicit Tracker(Program& program): m_program(program), m_info(program.info) {
: m_program(program), m_values(values), m_info(program.info) {
m_info.buffers.clear(); m_info.buffers.clear();
m_info.addresses.clear(); m_info.addresses.clear();
m_info.images.clear(); m_info.images.clear();
@@ -111,7 +110,7 @@ public:
return Fail(0, error, "SRT plan is not ready"); return Fail(0, error, "SRT plan is not ready");
} }
PlanIndirectImages(); PlanIndirectImages();
for (auto* block: m_values.blocks) { for (auto* block: m_program.blocks) {
for (auto& inst: *block) { for (auto& inst: *block) {
if (!Collect(inst, error)) { if (!Collect(inst, error)) {
return false; return false;
@@ -123,7 +122,7 @@ public:
patch.handle->SetFlags<uint32_t>(patch.resource); patch.handle->SetFlags<uint32_t>(patch.resource);
} }
for (const auto& patch: m_memory_patches) { for (const auto& patch: m_memory_patches) {
auto& memory = m_values.memory_info[patch.index]; auto& memory = m_program.memory_info[patch.index];
memory.resource = patch.resource; memory.resource = patch.resource;
if (patch.has_sampler) { if (patch.has_sampler) {
memory.sampler = patch.sampler; memory.sampler = patch.sampler;
@@ -138,16 +137,16 @@ public:
plan.handle->SetArg(dword, plan.key); plan.handle->SetArg(dword, plan.key);
} }
for (const auto index: plan.memory) { for (const auto index: plan.memory) {
m_values.memory_info[index].planning_only = true; m_program.memory_info[index].planning_only = true;
} }
} }
std::erase_if(m_values.dynamic_reads, [&](Value value) { std::erase_if(m_program.dynamic_reads, [&](Value value) {
const auto* inst = value.Resolve().TryInstruction(); const auto* inst = value.Resolve().TryInstruction();
return std::ranges::any_of(m_indirect_images, [&](const IndirectImagePlan& plan) { return std::ranges::any_of(m_indirect_images, [&](const IndirectImagePlan& plan) {
return std::ranges::find(plan.reads, inst) != plan.reads.end(); return std::ranges::find(plan.reads, inst) != plan.reads.end();
}); });
}); });
m_values.descriptor_sources = std::move(m_sources); m_program.descriptor_sources = std::move(m_sources);
m_program.info = std::move(m_info); m_program.info = std::move(m_info);
m_program.resource_tracking_complete = true; m_program.resource_tracking_complete = true;
return true; return true;
@@ -208,19 +207,19 @@ private:
if (left.value.IsEmpty() || right.value.IsEmpty()) { if (left.value.IsEmpty() || right.value.IsEmpty()) {
return require_both ? AddressPart {} : (left.value.IsEmpty() ? right : left); return require_both ? AddressPart {} : (left.value.IsEmpty() ? right : left);
} }
return EquivalentValue(m_values, left.value, right.value) return EquivalentValue(m_program, left.value, right.value)
? AddressPart {left.value, left.rooted || right.rooted} ? AddressPart {left.value, left.rooted || right.rooted}
: AddressPart {}; : AddressPart {};
}; };
switch (inst->GetOpcode()) { switch (inst->GetOpcode()) {
case ValueOpcode::SelectU32: case ValueOpcode::SelectU32:
if (EquivalentValue(m_values, inst->Arg(0), active)) { if (EquivalentValue(m_program, inst->Arg(0), active)) {
return finish(FindAddressPart(inst->Arg(1), active, visiting)); return finish(FindAddressPart(inst->Arg(1), active, visiting));
} }
return finish(merge(FindAddressPart(inst->Arg(1), active, visiting), return finish(merge(FindAddressPart(inst->Arg(1), active, visiting),
FindAddressPart(inst->Arg(2), active, visiting), true)); FindAddressPart(inst->Arg(2), active, visiting), true));
case ValueOpcode::Phi: { case ValueOpcode::Phi: {
const auto invariant = ResolveInvariantPhi(m_values, value); const auto invariant = ResolveInvariantPhi(m_program, value);
return finish(invariant.IsEmpty() ? AddressPart {} return finish(invariant.IsEmpty() ? AddressPart {}
: FindAddressPart(invariant, active, visiting)); : FindAddressPart(invariant, active, visiting));
} }
@@ -312,7 +311,7 @@ private:
} }
bool same = true; bool same = true;
for (uint32_t i = 0; i < descriptor.dword_count; i++) { for (uint32_t i = 0; i < descriptor.dword_count; i++) {
same = same && EquivalentValue(m_values, current.dwords[i], descriptor.dwords[i]); same = same && EquivalentValue(m_program, current.dwords[i], descriptor.dwords[i]);
} }
if (same) { if (same) {
return candidate; return candidate;
@@ -342,10 +341,10 @@ private:
return nullptr; return nullptr;
} }
index = read.Flags<MemoryFlags>().index; index = read.Flags<MemoryFlags>().index;
if (index >= m_values.memory_info.size()) { if (index >= m_program.memory_info.size()) {
return nullptr; return nullptr;
} }
const auto& memory = m_values.memory_info[index]; const auto& memory = m_program.memory_info[index];
return memory.kind == ResourceKind::ScalarBuffer && memory.data_bits == 32u && return memory.kind == ResourceKind::ScalarBuffer && memory.data_bits == 32u &&
memory.data_dwords == 1u memory.data_dwords == 1u
? &memory ? &memory
@@ -353,7 +352,7 @@ private:
} }
bool MemoryIndexBelongsTo(uint32_t index, const Inst& owner) const { bool MemoryIndexBelongsTo(uint32_t index, const Inst& owner) const {
for (const auto* block: m_values.blocks) { for (const auto* block: m_program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
const auto op = inst.GetOpcode(); const auto op = inst.GetOpcode();
if ((BufferAccessOf(op) == BufferAccess::None && if ((BufferAccessOf(op) == BufferAccess::None &&
@@ -446,7 +445,7 @@ private:
heap_handle = current_handle; heap_handle = current_handle;
if (dword == 0u) { if (dword == 0u) {
heap_offset = heap_reads[dword]->Arg(1).Resolve(); heap_offset = heap_reads[dword]->Arg(1).Resolve();
} else if (!EquivalentValue(m_values, heap_offset, heap_reads[dword]->Arg(1))) { } else if (!EquivalentValue(m_program, heap_offset, heap_reads[dword]->Arg(1))) {
return false; return false;
} }
plan.memory[dword] = memory_index; plan.memory[dword] = memory_index;
@@ -537,7 +536,7 @@ private:
} }
void PlanIndirectImages() { void PlanIndirectImages() {
for (auto* block: m_values.blocks) { for (auto* block: m_program.blocks) {
for (auto& inst: *block) { for (auto& inst: *block) {
if (ImageOpcodeInfoOf(inst.GetOpcode()).access == ImageAccess::None || if (ImageOpcodeInfoOf(inst.GetOpcode()).access == ImageAccess::None ||
inst.NumArgs() == 0u) { inst.NumArgs() == 0u) {
@@ -588,7 +587,7 @@ private:
return Fail(pc, error, "GetAddressResource must have two address dwords"); return Fail(pc, error, "GetAddressResource must have two address dwords");
} }
DescriptorSource descriptor; DescriptorSource descriptor;
const auto& memory = m_values.memory_info[memory_inst.Flags<MemoryFlags>().index]; const auto& memory = m_program.memory_info[memory_inst.Flags<MemoryFlags>().index];
if (memory.address_is_full) { if (memory.address_is_full) {
const auto active = memory_inst.Arg(memory_inst.NumArgs() - 1u); const auto active = memory_inst.Arg(memory_inst.NumArgs() - 1u);
if (memory.kind != ResourceKind::Flat || if (memory.kind != ResourceKind::Flat ||
@@ -797,14 +796,14 @@ private:
return true; return true;
} }
const auto flags = inst.Flags<MemoryFlags>(); const auto flags = inst.Flags<MemoryFlags>();
if (flags.index >= m_values.memory_info.size()) { if (flags.index >= m_program.memory_info.size()) {
return Fail(flags.pc, error, return Fail(flags.pc, error,
fmt::format("memory metadata index {} is out of range", flags.index)); fmt::format("memory metadata index {} is out of range", flags.index));
} }
if (inst.NumArgs() == 0) { if (inst.NumArgs() == 0) {
return Fail(flags.pc, error, "memory operation has no resource handle"); return Fail(flags.pc, error, "memory operation has no resource handle");
} }
const auto& memory = m_values.memory_info[flags.index]; const auto& memory = m_program.memory_info[flags.index];
if (memory.planning_only || IsIndirectPlanningMemory(flags.index)) { if (memory.planning_only || IsIndirectPlanningMemory(flags.index)) {
return true; return true;
} }
@@ -914,7 +913,7 @@ private:
} }
bool alias = true; bool alias = true;
for (uint32_t dword = 0; dword < 4; dword++) { for (uint32_t dword = 0; dword < 4; dword++) {
alias = alias && EquivalentValue(m_values, buffer_source->dwords[dword], alias = alias && EquivalentValue(m_program, buffer_source->dwords[dword],
image_source->dwords[dword]); image_source->dwords[dword]);
} }
if (alias) { if (alias) {
@@ -926,7 +925,6 @@ private:
} }
Program& m_program; Program& m_program;
ValueProgram& m_values;
ShaderInfo m_info; ShaderInfo m_info;
std::vector<DescriptorSource> m_sources; std::vector<DescriptorSource> m_sources;
std::vector<HandlePatch> m_handle_patches; std::vector<HandlePatch> m_handle_patches;
@@ -937,13 +935,7 @@ private:
} // namespace } // namespace
bool TrackResources(Program& program, std::string* error) { bool TrackResources(Program& program, std::string* error) {
if (program.values == nullptr) { return Tracker(program).Run(error);
if (error != nullptr) {
*error = "shader resource tracking requires typed IR";
}
return false;
}
return Tracker(program, *program.values).Run(error);
} }
} // namespace Libs::Graphics::ShaderRecompiler::IR } // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/ir/passes/ShaderInfoCollection.h" #include "graphics/shader/recompiler/ir/passes/ShaderInfoCollection.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include <algorithm> #include <algorithm>
#include <fmt/format.h> #include <fmt/format.h>
@@ -66,7 +66,7 @@ bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
} }
return false; return false;
}; };
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case ValueOpcode::GetAttribute: { case ValueOpcode::GetAttribute: {
@@ -140,7 +140,7 @@ bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
break; break;
} }
case ValueOpcode::SetAttribute: case ValueOpcode::SetAttribute:
if (inst.Flags<ExportFlags>().index >= program.values->export_info.size()) { if (inst.Flags<ExportFlags>().index >= program.export_info.size()) {
return Fail("typed export metadata index is out of range"); return Fail("typed export metadata index is out of range");
} }
break; break;
@@ -156,7 +156,7 @@ void CollectVertexInputs(const Program& program, const ShaderVertexInputInfo* ve
AddInput(info, StageInputKind::VertexIndex, 0, 1, "gl_VertexIndex"); AddInput(info, StageInputKind::VertexIndex, 0, 1, "gl_VertexIndex");
AddInput(info, StageInputKind::InstanceIndex, 0, 1, "gl_InstanceIndex"); AddInput(info, StageInputKind::InstanceIndex, 0, 1, "gl_InstanceIndex");
uint32_t used_components[ShaderVertexInputInfo::RES_MAX] = {}; uint32_t used_components[ShaderVertexInputInfo::RES_MAX] = {};
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
if (inst.GetOpcode() == ValueOpcode::GetAttribute) { if (inst.GetOpcode() == ValueOpcode::GetAttribute) {
const auto attr = inst.Arg(0).U32(); const auto attr = inst.Arg(0).U32();
@@ -185,7 +185,7 @@ void CollectPixelInputs(const Program& program, const ShaderPixelInputInfo* pixe
} }
std::array<bool, 32> per_vertex {}; std::array<bool, 32> per_vertex {};
std::array<bool, 32> interpolated {}; std::array<bool, 32> interpolated {};
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
if (inst.GetOpcode() == ValueOpcode::GetAttribute) { if (inst.GetOpcode() == ValueOpcode::GetAttribute) {
interpolated[inst.Arg(0).U32()] = true; interpolated[inst.Arg(0).U32()] = true;
@@ -228,7 +228,7 @@ void CollectComputeInputs(const ShaderComputeInputInfo* compute, ShaderInfo& inf
} }
void CollectBuiltinInputs(const Program& program, ShaderInfo& info) { void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
if (inst.GetOpcode() != ValueOpcode::GetBuiltin) { if (inst.GetOpcode() != ValueOpcode::GetBuiltin) {
continue; continue;
@@ -277,12 +277,12 @@ bool CollectOutputs(const Program& program, const ShaderVertexInputInfo* vertex,
} }
return false; return false;
}; };
for (const auto* block: program.values->blocks) { for (const auto* block: program.blocks) {
for (const auto& inst: *block) { for (const auto& inst: *block) {
if (inst.GetOpcode() != ValueOpcode::SetAttribute) { if (inst.GetOpcode() != ValueOpcode::SetAttribute) {
continue; continue;
} }
const auto& export_info = program.values->export_info[inst.Flags<ExportFlags>().index]; const auto& export_info = program.export_info[inst.Flags<ExportFlags>().index];
if (export_info.kind == ExportTargetKind::MrtZ) { if (export_info.kind == ExportTargetKind::MrtZ) {
if (program.stage == ShaderType::Pixel && (export_info.en & 0x1u) != 0 && if (program.stage == ShaderType::Pixel && (export_info.en & 0x1u) != 0 &&
pixel->ps_depth_export_enable) { pixel->ps_depth_export_enable) {
@@ -360,12 +360,6 @@ bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::
} }
return false; return false;
} }
if (program.values == nullptr) {
if (error != nullptr) {
*error = "typed value program is not available";
}
return false;
}
if (!ValidateOptions(program, options, error)) { if (!ValidateOptions(program, options, error)) {
return false; return false;
} }
@@ -377,7 +371,7 @@ bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::
next.inputs.clear(); next.inputs.clear();
next.outputs.clear(); next.outputs.clear();
next.has_bitwise_xor = std::any_of( next.has_bitwise_xor = std::any_of(
program.values->blocks.begin(), program.values->blocks.end(), [](const auto* block) { program.blocks.begin(), program.blocks.end(), [](const auto* block) {
return std::any_of(block->begin(), block->end(), [](const auto& inst) { return std::any_of(block->begin(), block->end(), [](const auto& inst) {
return inst.GetOpcode() == ValueOpcode::BitwiseXor32; return inst.GetOpcode() == ValueOpcode::BitwiseXor32;
}); });
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/ir/passes/SharedMemoryBarrier.h" #include "graphics/shader/recompiler/ir/passes/SharedMemoryBarrier.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include <algorithm> #include <algorithm>
#include <queue> #include <queue>
@@ -12,7 +12,7 @@ namespace {
enum class LdsPhase { None, Read, Write }; enum class LdsPhase { None, Read, Write };
LdsPhase GetLdsPhase(const ValueProgram& program, const Inst& inst) { LdsPhase GetLdsPhase(const Program& program, const Inst& inst) {
const auto access = SharedAccessOf(inst.GetOpcode()); const auto access = SharedAccessOf(inst.GetOpcode());
if (access != SharedAccess::Read && access != SharedAccess::Write) { if (access != SharedAccess::Read && access != SharedAccess::Write) {
return LdsPhase::None; return LdsPhase::None;
@@ -59,7 +59,7 @@ uint32_t InsertBarrier(Block& block, Block::iterator position) {
return 1; return 1;
} }
uint32_t InsertBlockBarriers(ValueProgram& program, Block& block) { uint32_t InsertBlockBarriers(Program& program, Block& block) {
uint32_t inserted = 0; uint32_t inserted = 0;
LdsPhase phase = LdsPhase::None; LdsPhase phase = LdsPhase::None;
for (auto inst = block.begin(); inst != block.end(); ++inst) { for (auto inst = block.begin(); inst != block.end(); ++inst) {
@@ -94,7 +94,7 @@ uint32_t InsertMergeBarrier(Block& block) {
} // namespace } // namespace
SharedMemoryBarrierStats InsertSharedMemoryBarriers(ValueProgram& program, uint32_t wave_size, SharedMemoryBarrierStats InsertSharedMemoryBarriers(Program& program, uint32_t wave_size,
const ShaderComputeInputInfo& compute_info) { const ShaderComputeInputInfo& compute_info) {
SharedMemoryBarrierStats stats; SharedMemoryBarrierStats stats;
const auto threadgroup_size = compute_info.threads_num[0] * compute_info.threads_num[1] * const auto threadgroup_size = compute_info.threads_num[0] * compute_info.threads_num[1] *
@@ -9,7 +9,7 @@ struct SharedMemoryBarrierStats {
}; };
[[nodiscard]] SharedMemoryBarrierStats [[nodiscard]] SharedMemoryBarrierStats
InsertSharedMemoryBarriers(ValueProgram& program, uint32_t wave_size, InsertSharedMemoryBarriers(Program& program, uint32_t wave_size,
const ShaderComputeInputInfo& compute_info); const ShaderComputeInputInfo& compute_info);
} // namespace Libs::Graphics::ShaderRecompiler::IR } // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -1,6 +1,6 @@
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h" #include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
#include <algorithm> #include <algorithm>
@@ -50,7 +50,7 @@ bool AddSignedAddress(uint64_t base, int64_t offset, uint64_t& result) {
return true; return true;
} }
bool IsRawRead(const ValueProgram& values, const Inst& inst) { bool IsRawRead(const Program& values, const Inst& inst) {
const auto op = inst.GetOpcode(); const auto op = inst.GetOpcode();
if (op != ValueOpcode::LoadAddressU32 && op != ValueOpcode::ReadConstBuffer) { if (op != ValueOpcode::LoadAddressU32 && op != ValueOpcode::ReadConstBuffer) {
return false; return false;
@@ -118,8 +118,7 @@ bool IsRuntimeIntegerOp(ValueOpcode op) {
class RuntimeValidator { class RuntimeValidator {
public: public:
RuntimeValidator(const Program& program, const ValueProgram& values) explicit RuntimeValidator(const Program& program): m_program(program) {}
: m_program(program), m_values(values) {}
bool Run(Value value, std::string& reason) { return Validate(value, reason); } bool Run(Value value, std::string& reason) { return Validate(value, reason); }
@@ -176,7 +175,7 @@ private:
return finish(true); return finish(true);
} }
if (op == ValueOpcode::Phi) { if (op == ValueOpcode::Phi) {
const auto invariant = ResolveInvariantPhi(m_values, value); const auto invariant = ResolveInvariantPhi(m_program, value);
if (invariant.IsEmpty()) { if (invariant.IsEmpty()) {
reason = "contains a control-dependent phi"; reason = "contains a control-dependent phi";
return finish(false); return finish(false);
@@ -196,7 +195,7 @@ private:
inst->Arg(0).Resolve().TryInstruction()->GetOpcode() != inst->Arg(0).Resolve().TryInstruction()->GetOpcode() !=
ValueOpcode::GetSrtResource || ValueOpcode::GetSrtResource ||
!slot.IsImmediate() || slot.GetType() != Type::U32 || !slot.IsImmediate() || slot.GetType() != Type::U32 ||
slot.U32() >= m_values.srt_reads.size()) { slot.U32() >= m_program.srt_reads.size()) {
reason = "contains a malformed flattened SRT read"; reason = "contains a malformed flattened SRT read";
return finish(false); return finish(false);
} }
@@ -205,7 +204,7 @@ private:
? ValueOpcode::GetAddressResource ? ValueOpcode::GetAddressResource
: ValueOpcode::GetBufferResource; : ValueOpcode::GetBufferResource;
const auto* handle = inst->NumArgs() != 0 ? inst->Arg(0).ResolveInstruction() : nullptr; const auto* handle = inst->NumArgs() != 0 ? inst->Arg(0).ResolveInstruction() : nullptr;
if (!IsRawRead(m_values, *inst) || handle == nullptr || if (!IsRawRead(m_program, *inst) || handle == nullptr ||
handle->GetOpcode() != expected) { handle->GetOpcode() != expected) {
reason = fmt::format("contains a non-scalar {}", ValueOpcodeName(op)); reason = fmt::format("contains a non-scalar {}", ValueOpcodeName(op));
return finish(false); return finish(false);
@@ -252,24 +251,23 @@ private:
} }
const Program& m_program; const Program& m_program;
const ValueProgram& m_values;
std::unordered_set<const Inst*> m_visiting; std::unordered_set<const Inst*> m_visiting;
}; };
class PlanBuilder { class PlanBuilder {
public: public:
PlanBuilder(Program& program, ValueProgram& values): m_program(program), m_values(values) {} explicit PlanBuilder(Program& program): m_program(program) {}
bool Run(std::string* error) { bool Run(std::string* error) {
m_values.srt_reads.clear(); m_program.srt_reads.clear();
m_values.dynamic_reads.clear(); m_program.dynamic_reads.clear();
for (auto* block: m_values.blocks) { for (auto* block: m_program.blocks) {
for (auto& inst: *block) { for (auto& inst: *block) {
const auto op = inst.GetOpcode(); const auto op = inst.GetOpcode();
if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) { if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) {
const auto flags = inst.Flags<MemoryFlags>(); const auto flags = inst.Flags<MemoryFlags>();
if (flags.index < m_values.memory_info.size()) { if (flags.index < m_program.memory_info.size()) {
const auto kind = m_values.memory_info[flags.index].kind; const auto kind = m_program.memory_info[flags.index].kind;
const bool crosswired = (op == ValueOpcode::LoadAddressU32 && const bool crosswired = (op == ValueOpcode::LoadAddressU32 &&
kind == ResourceKind::ScalarBuffer) || kind == ResourceKind::ScalarBuffer) ||
(op == ValueOpcode::ReadConstBuffer && (op == ValueOpcode::ReadConstBuffer &&
@@ -290,10 +288,10 @@ public:
} }
} }
} }
for (auto* block: m_values.blocks) { for (auto* block: m_program.blocks) {
for (auto& inst: *block) { for (auto& inst: *block) {
std::string reason; std::string reason;
if (inst.GetOpcode() == ValueOpcode::LoadAddressU32 && IsRawRead(m_values, inst) && if (inst.GetOpcode() == ValueOpcode::LoadAddressU32 && IsRawRead(m_program, inst) &&
inst.Arg(1).Resolve().IsImmediate() && inst.Arg(1).Resolve().IsImmediate() &&
ValidateRuntimeValue(m_program, Value(&inst), reason) && ValidateRuntimeValue(m_program, Value(&inst), reason) &&
!Collect(Value(&inst), inst.Flags<MemoryFlags>().pc, error)) { !Collect(Value(&inst), inst.Flags<MemoryFlags>().pc, error)) {
@@ -348,25 +346,25 @@ private:
} }
m_visiting.pop_back(); m_visiting.pop_back();
m_visited.push_back(inst); m_visited.push_back(inst);
if (!IsRawRead(m_values, *inst)) { if (!IsRawRead(m_program, *inst)) {
return true; return true;
} }
const auto offset = inst->Arg(1).Resolve(); const auto offset = inst->Arg(1).Resolve();
if (!offset.IsImmediate() || offset.GetType() != Type::U32) { if (!offset.IsImmediate() || offset.GetType() != Type::U32) {
if (std::ranges::find(m_values.dynamic_reads, value) == m_values.dynamic_reads.end()) { if (std::ranges::find(m_program.dynamic_reads, value) == m_program.dynamic_reads.end()) {
m_values.dynamic_reads.push_back(value); m_program.dynamic_reads.push_back(value);
} }
return true; return true;
} }
use_pc = inst->Flags<MemoryFlags>().pc; use_pc = inst->Flags<MemoryFlags>().pc;
for (uint32_t slot = 0; slot < m_values.srt_reads.size(); slot++) { for (uint32_t slot = 0; slot < m_program.srt_reads.size(); slot++) {
if (EquivalentValue(m_values, value, m_values.srt_reads[slot].value)) { if (EquivalentValue(m_program, value, m_program.srt_reads[slot].value)) {
m_patches.push_back({inst, slot, false}); m_patches.push_back({inst, slot, false});
return true; return true;
} }
} }
const auto slot = static_cast<uint32_t>(m_values.srt_reads.size()); const auto slot = static_cast<uint32_t>(m_program.srt_reads.size());
m_values.srt_reads.push_back({value, slot, use_pc}); m_program.srt_reads.push_back({value, slot, use_pc});
m_patches.push_back({inst, slot, true}); m_patches.push_back({inst, slot, true});
return true; return true;
} }
@@ -385,7 +383,7 @@ private:
for (const auto& use: uses) { for (const auto& use: uses) {
use.user->SetArg(use.operand, flat); use.user->SetArg(use.operand, flat);
} }
for (auto& info: m_values.block_info) { for (auto& info: m_program.block_info) {
if (info.condition.Resolve() == Value(patch.inst)) { if (info.condition.Resolve() == Value(patch.inst)) {
info.condition = flat; info.condition = flat;
} }
@@ -395,8 +393,8 @@ private:
} }
if (patch.keep) { if (patch.keep) {
const auto memory = patch.inst->Flags<MemoryFlags>().index; const auto memory = patch.inst->Flags<MemoryFlags>().index;
if (memory < m_values.memory_info.size()) { if (memory < m_program.memory_info.size()) {
m_values.memory_info[memory].planning_only = true; m_program.memory_info[memory].planning_only = true;
} }
block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(patch.inst)}); block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(patch.inst)});
} }
@@ -404,7 +402,6 @@ private:
} }
Program& m_program; Program& m_program;
ValueProgram& m_values;
std::vector<Inst*> m_visiting; std::vector<Inst*> m_visiting;
std::vector<Inst*> m_visited; std::vector<Inst*> m_visited;
std::vector<Patch> m_patches; std::vector<Patch> m_patches;
@@ -414,7 +411,7 @@ class Evaluator {
public: public:
Evaluator(const Program& program, const SrtRuntime& runtime, Evaluator(const Program& program, const SrtRuntime& runtime,
std::span<const uint8_t> clean_flat_slots = {}, Evaluator* clean_evaluator = nullptr) std::span<const uint8_t> clean_flat_slots = {}, Evaluator* clean_evaluator = nullptr)
: m_program(program), m_values(*program.values), m_runtime(runtime), : m_program(program), m_runtime(runtime),
m_clean_flat_slots(clean_flat_slots), m_clean_evaluator(clean_evaluator) {} m_clean_flat_slots(clean_flat_slots), m_clean_evaluator(clean_evaluator) {}
void SetUsePc(uint32_t pc) { m_use_pc = pc; } void SetUsePc(uint32_t pc) { m_use_pc = pc; }
@@ -472,7 +469,7 @@ private:
} }
bool EvaluatePhi(const Inst& inst, uint64_t& result, std::string* error) { bool EvaluatePhi(const Inst& inst, uint64_t& result, std::string* error) {
const auto value = ResolveInvariantPhi(m_values, Value(const_cast<Inst*>(&inst))); const auto value = ResolveInvariantPhi(m_program, Value(const_cast<Inst*>(&inst)));
return !value.IsEmpty() ? EvaluateWide(value, result, error) return !value.IsEmpty() ? EvaluateWide(value, result, error)
: Fail(error, "typed phi has runtime-dependent values"); : Fail(error, "typed phi has runtime-dependent values");
} }
@@ -518,10 +515,10 @@ private:
bool EvaluateRawRead(const Inst& inst, uint64_t& result, std::string* error) { bool EvaluateRawRead(const Inst& inst, uint64_t& result, std::string* error) {
const auto flags = inst.Flags<MemoryFlags>(); const auto flags = inst.Flags<MemoryFlags>();
if (flags.index >= m_values.memory_info.size()) { if (flags.index >= m_program.memory_info.size()) {
return Fail(error, "raw scalar read has invalid metadata"); return Fail(error, "raw scalar read has invalid metadata");
} }
const auto& mem = m_values.memory_info[flags.index]; const auto& mem = m_program.memory_info[flags.index];
const auto* handle = inst.Arg(0).ResolveInstruction(); const auto* handle = inst.Arg(0).ResolveInstruction();
if (handle == nullptr) { if (handle == nullptr) {
return Fail(error, "raw scalar read has no descriptor handle"); return Fail(error, "raw scalar read has no descriptor handle");
@@ -609,20 +606,20 @@ private:
case ValueOpcode::ReadConst: { case ValueOpcode::ReadConst: {
const auto slot = inst.Arg(1).Resolve(); const auto slot = inst.Arg(1).Resolve();
if (!slot.IsImmediate() || slot.GetType() != Type::U32 || if (!slot.IsImmediate() || slot.GetType() != Type::U32 ||
slot.U32() >= m_values.srt_reads.size()) { slot.U32() >= m_program.srt_reads.size()) {
return Fail(error, "invalid flattened SRT index"); return Fail(error, "invalid flattened SRT index");
} }
if (slot.U32() < m_clean_flat_slots.size() && if (slot.U32() < m_clean_flat_slots.size() &&
m_clean_flat_slots[slot.U32()] != 0u && m_clean_evaluator != nullptr) { m_clean_flat_slots[slot.U32()] != 0u && m_clean_evaluator != nullptr) {
m_clean_evaluator->SetUsePc(m_use_pc); m_clean_evaluator->SetUsePc(m_use_pc);
return m_clean_evaluator->EvaluateWide(m_values.srt_reads[slot.U32()].value, return m_clean_evaluator->EvaluateWide(m_program.srt_reads[slot.U32()].value,
result, error); result, error);
} }
return EvaluateWide(m_values.srt_reads[slot.U32()].value, result, error); return EvaluateWide(m_program.srt_reads[slot.U32()].value, result, error);
} }
case ValueOpcode::LoadAddressU32: case ValueOpcode::LoadAddressU32:
case ValueOpcode::ReadConstBuffer: case ValueOpcode::ReadConstBuffer:
if (IsRawRead(m_values, inst)) { if (IsRawRead(m_program, inst)) {
return EvaluateRawRead(inst, result, error); return EvaluateRawRead(inst, result, error);
} }
break; break;
@@ -856,7 +853,6 @@ private:
} }
const Program& m_program; const Program& m_program;
const ValueProgram& m_values;
const SrtRuntime& m_runtime; const SrtRuntime& m_runtime;
std::span<const uint8_t> m_clean_flat_slots; std::span<const uint8_t> m_clean_flat_slots;
Evaluator* m_clean_evaluator = nullptr; Evaluator* m_clean_evaluator = nullptr;
@@ -866,10 +862,10 @@ private:
}; };
const DescriptorSource* Source(const Program& program, uint32_t source) { const DescriptorSource* Source(const Program& program, uint32_t source) {
if (program.values == nullptr || source >= program.values->descriptor_sources.size()) { if (source >= program.descriptor_sources.size()) {
return nullptr; return nullptr;
} }
return &program.values->descriptor_sources[source]; return &program.descriptor_sources[source];
} }
bool EvaluateRuntimeSourcesImpl(const Program& program, bool EvaluateRuntimeSourcesImpl(const Program& program,
@@ -877,7 +873,7 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
const SrtRuntime& runtime, std::vector<DescriptorValue>& results, const SrtRuntime& runtime, std::vector<DescriptorValue>& results,
std::vector<uint32_t>& flat, bool evaluate_flat, std::vector<uint32_t>& flat, bool evaluate_flat,
std::span<const uint8_t> clean_flat_slots, std::string* error) { std::span<const uint8_t> clean_flat_slots, std::string* error) {
if (program.values == nullptr || !program.srt_plan_complete) { if (!program.srt_plan_complete) {
if (error != nullptr) { if (error != nullptr) {
*error = Diagnostic(program, 0, "typed SRT plan is not ready"); *error = Diagnostic(program, 0, "typed SRT plan is not ready");
} }
@@ -917,8 +913,8 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
} }
std::vector<uint32_t> flattened; std::vector<uint32_t> flattened;
if (evaluate_flat) { if (evaluate_flat) {
flattened.resize(program.values->srt_reads.size()); flattened.resize(program.srt_reads.size());
for (const auto& read: program.values->srt_reads) { for (const auto& read: program.srt_reads) {
const bool clean = read.flat_offset < clean_flat_slots.size() && const bool clean = read.flat_offset < clean_flat_slots.size() &&
clean_flat_slots[read.flat_offset] != 0u; clean_flat_slots[read.flat_offset] != 0u;
auto& selected = clean ? clean_evaluator : evaluator; auto& selected = clean ? clean_evaluator : evaluator;
@@ -939,23 +935,18 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
} // namespace } // namespace
bool ValidateRuntimeValue(const Program& program, Value value, std::string& reason) { bool ValidateRuntimeValue(const Program& program, Value value, std::string& reason) {
if (program.values == nullptr) { return RuntimeValidator(program).Run(value, reason);
reason = "typed SSA is not ready";
return false;
}
return RuntimeValidator(program, *program.values).Run(value, reason);
} }
bool BuildSrtPlan(Program& program, std::string* error) { bool BuildSrtPlan(Program& program, std::string* error) {
if (program.values == nullptr || program.resource_tracking_complete) { if (program.resource_tracking_complete) {
if (error != nullptr) { if (error != nullptr) {
*error = program.values == nullptr ? "typed SSA is not ready" *error = "cannot rebuild SRT after resource tracking";
: "cannot rebuild SRT after resource tracking";
} }
return false; return false;
} }
program.srt_plan_complete = false; program.srt_plan_complete = false;
if (!PlanBuilder(program, *program.values).Run(error)) { if (!PlanBuilder(program).Run(error)) {
return false; return false;
} }
program.srt_plan_complete = true; program.srt_plan_complete = true;
+2 -39
View File
@@ -15,7 +15,7 @@
#include "graphics/host_gpu/renderer/renderContext.h" #include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/shader/recompiler/ShaderRecompiler.h" #include "graphics/shader/recompiler/ShaderRecompiler.h"
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/shaderVertexMetadata.h" #include "graphics/shader/shaderVertexMetadata.h"
#include "kernel/memory.h" #include "kernel/memory.h"
#include "libs/errno.h" #include "libs/errno.h"
@@ -49,9 +49,6 @@ namespace Libs::Graphics {
namespace { namespace {
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
constexpr uint32_t PsInputFlatShade = 0x00000400u;
bool ReadShaderGuestMemory(void*, uint64_t address, uint32_t* value) { bool ReadShaderGuestMemory(void*, uint64_t address, uint32_t* value) {
return value != nullptr && return value != nullptr &&
Libs::LibKernel::Memory::TryReadGpuCleanBacking(address, value, sizeof(*value)); Libs::LibKernel::Memory::TryReadGpuCleanBacking(address, value, sizeof(*value));
@@ -59,40 +56,6 @@ bool ReadShaderGuestMemory(void*, uint64_t address, uint32_t* value) {
} // namespace } // namespace
uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, uint32_t input) {
return input < info.input_num ? info.interpolator_settings[input] & PsInputOffsetMask : input;
}
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
std::span<const uint32_t> active_inputs, uint32_t input) {
std::array<bool, 32> used_locations {};
for (const auto active_input: active_inputs) {
auto location = ShaderPixelParameterMappedLocation(info, active_input);
if (location < used_locations.size() && used_locations[location]) {
location = active_input;
while (location < used_locations.size() && used_locations[location]) {
location++;
}
EXIT_NOT_IMPLEMENTED(location >= used_locations.size());
}
if (active_input == input) {
return location;
}
used_locations[location] = true;
}
return ShaderPixelParameterMappedLocation(info, input);
}
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input) {
return input < info.input_num && (info.interpolator_settings[input] & PsInputFlatShade) != 0 &&
!ShaderPixelParameterIsCustom(info, input);
}
bool ShaderPixelParameterIsCustom(const ShaderPixelInputInfo& info, uint32_t input) {
return input < 32u && (info.custom_interpolation_mask & (1u << input)) != 0;
}
struct ShaderBinaryInfo { struct ShaderBinaryInfo {
uint8_t signature[7]; uint8_t signature[7];
uint8_t version; uint8_t version;
@@ -1035,7 +998,7 @@ static std::string ShaderDescribeSpecialization(const ShaderRecompiler::IR::Prog
program.bindings.push_constant_size, program.bindings.descriptors.size(), program.bindings.push_constant_size, program.bindings.descriptors.size(),
program.bindings.user_data_registers.size(), program.info.buffers.size(), program.bindings.user_data_registers.size(), program.info.buffers.size(),
program.info.images.size(), program.info.samplers.size(), program.info.addresses.size(), program.info.images.size(), program.info.samplers.size(), program.info.addresses.size(),
program.values != nullptr ? program.values->srt_reads.size() : 0u); program.srt_reads.size());
for (uint32_t i = 0; i < program.info.buffers.size(); i++) { for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
const auto& buffer = program.info.buffers[i]; const auto& buffer = program.info.buffers[i];
ret += ret +=
-1
View File
@@ -84,7 +84,6 @@ struct ShaderVertexInputInfo {
ShaderBufferResource resources[RES_MAX]; ShaderBufferResource resources[RES_MAX];
ShaderVertexDestination resources_dst[RES_MAX]; ShaderVertexDestination resources_dst[RES_MAX];
int resource_fetch_components[RES_MAX] = {};
ShaderVertexInputBuffer buffers[RES_MAX]; ShaderVertexInputBuffer buffers[RES_MAX];
ShaderStageRuntime stage; ShaderStageRuntime stage;
int resources_num = 0; int resources_num = 0;
@@ -0,0 +1,48 @@
#include "graphics/shader/shader.h"
#include "common/assert.h"
namespace Libs::Graphics {
namespace {
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
constexpr uint32_t PsInputFlatShade = 0x00000400u;
} // namespace
uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, uint32_t input) {
return input < info.input_num ? info.interpolator_settings[input] & PsInputOffsetMask : input;
}
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
std::span<const uint32_t> active_inputs, uint32_t input) {
std::array<bool, 32> used_locations {};
for (const auto active_input: active_inputs) {
auto location = ShaderPixelParameterMappedLocation(info, active_input);
if (location < used_locations.size() && used_locations[location]) {
location = active_input;
while (location < used_locations.size() && used_locations[location]) {
location++;
}
EXIT_NOT_IMPLEMENTED(location >= used_locations.size());
}
if (active_input == input) {
return location;
}
used_locations[location] = true;
}
return ShaderPixelParameterMappedLocation(info, input);
}
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input) {
return input < info.input_num && (info.interpolator_settings[input] & PsInputFlatShade) != 0 &&
!ShaderPixelParameterIsCustom(info, input);
}
bool ShaderPixelParameterIsCustom(const ShaderPixelInputInfo& info, uint32_t input) {
return input < 32u && (info.custom_interpolation_mask & (1u << input)) != 0;
}
} // namespace Libs::Graphics
+39 -36
View File
@@ -1,5 +1,5 @@
#include "graphics/guest_gpu/gpu_defs.h" #include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h" #include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h" #include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h"
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h" #include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
@@ -35,7 +35,6 @@ struct Fixture {
explicit Fixture(ShaderType stage = ShaderType::Compute) { explicit Fixture(ShaderType stage = ShaderType::Compute) {
program.stage = stage; program.stage = stage;
program.values = std::make_shared<ValueProgram>();
program.user_data_count = 64; program.user_data_count = 64;
block = AddBlock(); block = AddBlock();
} }
@@ -43,10 +42,10 @@ struct Fixture {
Block *AddBlock() { Block *AddBlock() {
auto storage = std::make_unique<Block>(); auto storage = std::make_unique<Block>();
auto *result = storage.get(); auto *result = storage.get();
program.values->block_storage.push_back(std::move(storage)); program.block_storage.push_back(std::move(storage));
program.values->blocks.push_back(result); program.blocks.push_back(result);
program.values->block_info.push_back( program.block_info.push_back(
{.id = static_cast<uint32_t>(program.values->block_info.size())}); {.id = static_cast<uint32_t>(program.block_info.size())});
return result; return result;
} }
@@ -77,8 +76,8 @@ struct Fixture {
MemoryFlags AddMemory(MemoryInfo memory, uint32_t pc) { MemoryFlags AddMemory(MemoryInfo memory, uint32_t pc) {
const auto index = const auto index =
static_cast<uint32_t>(program.values->memory_info.size()); static_cast<uint32_t>(program.memory_info.size());
program.values->memory_info.push_back(memory); program.memory_info.push_back(memory);
return {index, pc}; return {index, pc};
} }
@@ -241,19 +240,19 @@ MakeIndirectImageFixture(bool malformed, uint32_t material_immediate = 0,
void TestInvariantIndirectImageMaterialization() { void TestInvariantIndirectImageMaterialization() {
auto fixture = MakeIndirectImageFixture(false); auto fixture = MakeIndirectImageFixture(false);
fixture->PlanAndTrack(); fixture->PlanAndTrack();
EliminateDeadCode(fixture->program.values->blocks); EliminateDeadCode(fixture->program.blocks);
std::string validation_error; std::string validation_error;
Check( Check(
ValidateValueProgram(*fixture->program.values, true, &validation_error), ValidateProgram(fixture->program, true, &validation_error),
"post-tracking dead-code elimination invalidated descriptor provenance"); "post-tracking dead-code elimination invalidated descriptor provenance");
Check(fixture->program.info.buffers.size() == 1 && Check(fixture->program.info.buffers.size() == 1 &&
fixture->program.info.images.size() == 1 && fixture->program.info.images.size() == 1 &&
fixture->program.values->dynamic_reads.size() == 1, fixture->program.dynamic_reads.size() == 1,
"indirect image key was not retained as a scalar-buffer read"); "indirect image key was not retained as a scalar-buffer read");
const auto source = fixture->program.info.images[0].source; const auto source = fixture->program.info.images[0].source;
Check(source < fixture->program.values->descriptor_sources.size() && Check(source < fixture->program.descriptor_sources.size() &&
fixture->program.values->descriptor_sources[source] fixture->program.descriptor_sources[source]
.indirect_image.has_value(), .indirect_image.has_value(),
"indirect image source was not retained for runtime proof"); "indirect image source was not retained for runtime proof");
const auto image_handle = const auto image_handle =
@@ -407,7 +406,7 @@ void TestInvariantIndirectImageMaterialization() {
auto memory_backed = MakeIndirectImageFixture(false, 0u, true); auto memory_backed = MakeIndirectImageFixture(false, 0u, true);
memory_backed->PlanAndTrack(); memory_backed->PlanAndTrack();
EliminateDeadCode(memory_backed->program.values->blocks); EliminateDeadCode(memory_backed->program.blocks);
std::array<uint32_t, 11> memory_backed_user_data{0x1000u, 224u << 16u, 2u, 0u, std::array<uint32_t, 11> memory_backed_user_data{0x1000u, 224u << 16u, 2u, 0u,
0x2000u, 16u << 16u, 4u, 0u, 0x2000u, 16u << 16u, 4u, 0u,
7u, 0x3100u, 0u}; 7u, 0x3100u, 0u};
@@ -431,7 +430,7 @@ void TestInvariantIndirectImageMaterialization() {
error.find("ReadFirstLane") != std::string::npos && error.find("ReadFirstLane") != std::string::npos &&
!malformed->program.resource_tracking_complete && !malformed->program.resource_tracking_complete &&
malformed->program.info.images.empty() && malformed->program.info.images.empty() &&
malformed->program.values->descriptor_sources.empty(), malformed->program.descriptor_sources.empty(),
"malformed indirect image pattern was partially accepted"); "malformed indirect image pattern was partially accepted");
auto wrapped_immediate = MakeIndirectImageFixture(false, 4u); auto wrapped_immediate = MakeIndirectImageFixture(false, 4u);
@@ -484,7 +483,7 @@ void TestDenseBufferTracking() {
Check(fixture.program.info.buffers.size() == 2, Check(fixture.program.info.buffers.size() == 2,
"typed buffer sources were not densely interned"); "typed buffer sources were not densely interned");
Check(fixture.program.values->descriptor_sources.size() == 2, Check(fixture.program.descriptor_sources.size() == 2,
"descriptor source table did not match dense topology"); "descriptor source table did not match dense topology");
const auto &resource = fixture.program.info.buffers[0]; const auto &resource = fixture.program.info.buffers[0];
Check(resource.read && resource.written && resource.atomic && Check(resource.read && resource.written && resource.atomic &&
@@ -494,10 +493,10 @@ void TestDenseBufferTracking() {
Check(first.Instruction()->Flags<uint32_t>() == 0 && Check(first.Instruction()->Flags<uint32_t>() == 0 &&
second.Instruction()->Flags<uint32_t>() == 1, second.Instruction()->Flags<uint32_t>() == 1,
"typed handles were not assigned dense indices"); "typed handles were not assigned dense indices");
Check(fixture.program.values->memory_info[load_flags.index].resource == 0 && Check(fixture.program.memory_info[load_flags.index].resource == 0 &&
fixture.program.values->memory_info[store_flags.index].resource == fixture.program.memory_info[store_flags.index].resource ==
0 && 0 &&
fixture.program.values->memory_info[other_flags.index].resource == fixture.program.memory_info[other_flags.index].resource ==
1, 1,
"typed memory metadata was not patched to dense indices"); "typed memory metadata was not patched to dense indices");
@@ -531,8 +530,8 @@ void TestScalarAndVectorBufferAlias() {
Check(fixture.program.info.buffers.size() == 1 && Check(fixture.program.info.buffers.size() == 1 &&
fixture.program.info.buffers[0].scalar, fixture.program.info.buffers[0].scalar,
"typed scalar and vector uses of one descriptor were split"); "typed scalar and vector uses of one descriptor were split");
Check(fixture.program.values->memory_info[scalar_flags.index].resource == 0 && Check(fixture.program.memory_info[scalar_flags.index].resource == 0 &&
fixture.program.values->memory_info[vector_flags.index].resource == fixture.program.memory_info[vector_flags.index].resource ==
0, 0,
"scalar/vector alias did not share a dense index"); "scalar/vector alias did not share a dense index");
} }
@@ -625,7 +624,7 @@ void TestImagesSamplersAndAliases() {
repeated.second.Instruction()->Flags<uint32_t>() == 0, repeated.second.Instruction()->Flags<uint32_t>() == 0,
"unused sampler border colors prevented source interning"); "unused sampler border colors prevented source interning");
const auto sampler_source = fixture.program.info.samplers[0].source; const auto sampler_source = fixture.program.info.samplers[0].source;
Check(fixture.program.values->descriptor_sources[sampler_source] Check(fixture.program.descriptor_sources[sampler_source]
.dwords[3] .dwords[3]
.U32() == 0, .U32() == 0,
"unused sampler border color was not canonicalized"); "unused sampler border color was not canonicalized");
@@ -665,7 +664,7 @@ void TestSampleAdjustSamplerScratch() {
fixture.PlanAndTrack(); fixture.PlanAndTrack();
const auto source = fixture.program.info.samplers[0].source; const auto source = fixture.program.info.samplers[0].source;
const auto stored = fixture.program.values->descriptor_sources[source] const auto stored = fixture.program.descriptor_sources[source]
.dwords[3] .dwords[3]
.Resolve() .Resolve()
.TryInstruction(); .TryInstruction();
@@ -758,10 +757,10 @@ void TestDynamicStorageMipTracking() {
mip1.first.Instruction()->Flags<uint32_t>() == 1 && mip1.first.Instruction()->Flags<uint32_t>() == 1 &&
mip2.first.Instruction()->Flags<uint32_t>() == 1 && mip2.first.Instruction()->Flags<uint32_t>() == 1 &&
dynamic.first.Instruction()->Flags<uint32_t>() == 1 && dynamic.first.Instruction()->Flags<uint32_t>() == 1 &&
fixture.program.values->memory_info[plain.second].resource == 0 && fixture.program.memory_info[plain.second].resource == 0 &&
fixture.program.values->memory_info[mip1.second].resource == 1 && fixture.program.memory_info[mip1.second].resource == 1 &&
fixture.program.values->memory_info[mip2.second].resource == 1 && fixture.program.memory_info[mip2.second].resource == 1 &&
fixture.program.values->memory_info[dynamic.second].resource == 1, fixture.program.memory_info[dynamic.second].resource == 1,
"dynamic storage mip handles and memory metadata were not patched"); "dynamic storage mip handles and memory metadata were not patched");
DescriptorValue descriptor{}; DescriptorValue descriptor{};
@@ -795,7 +794,6 @@ void TestDynamicStorageMipTracking() {
"dynamic storage mip descriptors were not expanded consecutively"); "dynamic storage mip descriptors were not expanded consecutively");
Program null_program; Program null_program;
null_program.values = std::make_shared<ValueProgram>();
null_program.resource_tracking_complete = true; null_program.resource_tracking_complete = true;
ImageResource null_image; ImageResource null_image;
null_image.kind = ResourceKind::StorageImage; null_image.kind = ResourceKind::StorageImage;
@@ -847,12 +845,12 @@ void TestSrtFlatteningAndRuntimeMemoization() {
fixture.AddMemory(buffer, 16)); fixture.AddMemory(buffer, 16));
fixture.PlanAndTrack(); fixture.PlanAndTrack();
Check(fixture.program.values->srt_reads.size() == 1, Check(fixture.program.srt_reads.size() == 1,
"shared typed scalar read did not receive one flat SRT slot"); "shared typed scalar read did not receive one flat SRT slot");
Check(fixture.program.info.buffers.size() == 1 && Check(fixture.program.info.buffers.size() == 1 &&
fixture.program.info.addresses.empty(), fixture.program.info.addresses.empty(),
"planning-only scalar reads leaked into resource topology"); "planning-only scalar reads leaked into resource topology");
Check(fixture.program.values->memory_info[0].planning_only, Check(fixture.program.memory_info[0].planning_only,
"canonical runtime scalar read was not marked planning-only"); "canonical runtime scalar read was not marked planning-only");
std::array<uint32_t, 2> user_data{0x1000u, 0u}; std::array<uint32_t, 2> user_data{0x1000u, 0u};
@@ -910,8 +908,8 @@ void TestDynamicSrtReadRemainsExplicit() {
fixture.AddMemory(buffer, 8)); fixture.AddMemory(buffer, 8));
fixture.PlanAndTrack(); fixture.PlanAndTrack();
Check(fixture.program.values->srt_reads.empty() && Check(fixture.program.srt_reads.empty() &&
fixture.program.values->dynamic_reads.size() == 1 && fixture.program.dynamic_reads.size() == 1 &&
fixture.program.info.addresses.size() == 1, fixture.program.info.addresses.size() == 1,
"dynamic scalar read was incorrectly flattened or lost"); "dynamic scalar read was incorrectly flattened or lost");
std::array<uint32_t, 3> user_data{0x1000u, 0u, 4u}; std::array<uint32_t, 3> user_data{0x1000u, 0u, 4u};
@@ -973,7 +971,7 @@ void TestPhiValidation() {
error.find("control-dependent phi") != std::string::npos && error.find("control-dependent phi") != std::string::npos &&
!fixture.program.resource_tracking_complete && !fixture.program.resource_tracking_complete &&
fixture.program.info.buffers.empty() && fixture.program.info.buffers.empty() &&
fixture.program.values->descriptor_sources.empty(), fixture.program.descriptor_sources.empty(),
"control-dependent descriptor phi was not rejected transactionally"); "control-dependent descriptor phi was not rejected transactionally");
} }
@@ -1295,7 +1293,7 @@ void TestResourceLimitIsTransactional() {
error.find("buffer resource limit exceeded") != std::string::npos && error.find("buffer resource limit exceeded") != std::string::npos &&
!fixture.program.resource_tracking_complete && !fixture.program.resource_tracking_complete &&
fixture.program.info.buffers.empty() && fixture.program.info.buffers.empty() &&
fixture.program.values->descriptor_sources.empty(), fixture.program.descriptor_sources.empty(),
"resource-limit failure partially mutated typed resource state"); "resource-limit failure partially mutated typed resource state");
} }
@@ -1412,13 +1410,18 @@ int main() {
// The full emulator supplies these assertion hooks through common. This focused // The full emulator supplies these assertion hooks through common. This focused
// target links only fmt; keep assertion failures observable without widening // target links only fmt; keep assertion failures observable without widening
// its legacy build manifest. // its focused build manifest.
namespace Common { namespace Common {
int DbgExitIfHandler(const char *expression, const char *file, int line) { int DbgExitIfHandler(const char *expression, const char *file, int line) {
throw std::runtime_error(std::string("typed IR assertion: ") + expression + throw std::runtime_error(std::string("typed IR assertion: ") + expression +
" at " + file + ':' + std::to_string(line)); " at " + file + ':' + std::to_string(line));
} }
int DbgNotImplementedHandler(const char *expression, const char *file, int line) {
throw std::runtime_error(std::string("typed IR not implemented: ") + expression +
" at " + file + ':' + std::to_string(line));
}
void DbgExit(int) { throw std::runtime_error("typed IR assertion failed"); } void DbgExit(int) { throw std::runtime_error("typed IR assertion failed"); }
} // namespace Common } // namespace Common
@@ -1427,6 +1430,6 @@ void DbgExit(int) { throw std::runtime_error("typed IR assertion failed"); }
#include "graphics/shader/recompiler/ir/Block.cpp" #include "graphics/shader/recompiler/ir/Block.cpp"
#include "graphics/shader/recompiler/ir/Type.cpp" #include "graphics/shader/recompiler/ir/Type.cpp"
#include "graphics/shader/recompiler/ir/Value.cpp" #include "graphics/shader/recompiler/ir/Value.cpp"
#include "graphics/shader/recompiler/ir/ValueProgram.cpp" #include "graphics/shader/recompiler/ir/Program.cpp"
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp" #include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp" #include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp"
+39 -40
View File
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h" #include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h"
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h" #include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h"
#include "graphics/shader/recompiler/ir/passes/ReadLaneElimination.h" #include "graphics/shader/recompiler/ir/passes/ReadLaneElimination.h"
@@ -27,16 +27,15 @@ struct Fixture {
program.stage = ShaderType::Compute; program.stage = ShaderType::Compute;
program.shader_hash = 0x12345678u; program.shader_hash = 0x12345678u;
program.user_data_base = 2; program.user_data_base = 2;
program.values = std::make_shared<ValueProgram>();
for (uint32_t index = 0; index < block_count; index++) { for (uint32_t index = 0; index < block_count; index++) {
program.values->block_storage.push_back(std::make_unique<Block>()); program.block_storage.push_back(std::make_unique<Block>());
auto *block = program.values->block_storage.back().get(); auto *block = program.block_storage.back().get();
program.values->blocks.push_back(block); program.blocks.push_back(block);
program.values->block_info.push_back({.id = index}); program.block_info.push_back({.id = index});
} }
} }
Block &BlockAt(uint32_t index = 0) { return *program.values->blocks[index]; } Block &BlockAt(uint32_t index = 0) { return *program.blocks[index]; }
Value Emit(ValueOpcode opcode, std::initializer_list<Value> args = {}, Value Emit(ValueOpcode opcode, std::initializer_list<Value> args = {},
uint64_t flags = 0, uint32_t block = 0) { uint64_t flags = 0, uint32_t block = 0) {
@@ -55,8 +54,8 @@ struct Fixture {
MemoryInfo info; MemoryInfo info;
info.kind = kind; info.kind = kind;
info.offset = static_cast<uint32_t>(offset); info.offset = static_cast<uint32_t>(offset);
program.values->memory_info.push_back(info); program.memory_info.push_back(info);
return static_cast<uint32_t>(program.values->memory_info.size() - 1u); return static_cast<uint32_t>(program.memory_info.size() - 1u);
} }
void Plan() { void Plan() {
@@ -103,11 +102,11 @@ void TestImmediateFlatteningAndGvn() {
{first, second, Value(16u), Value(0u)}); {first, second, Value(16u), Value(0u)});
fixture.Plan(); fixture.Plan();
Check(fixture.program.values->srt_reads.size() == 1, Check(fixture.program.srt_reads.size() == 1,
"equivalent typed scalar reads were not coalesced"); "equivalent typed scalar reads were not coalesced");
Check(fixture.program.values->dynamic_reads.empty(), Check(fixture.program.dynamic_reads.empty(),
"immediate scalar read was classified as dynamic"); "immediate scalar read was classified as dynamic");
Check(fixture.program.values->memory_info[memory].planning_only, Check(fixture.program.memory_info[memory].planning_only,
"flattened raw read was not kept as a planning-only root"); "flattened raw read was not kept as a planning-only root");
TestMemory memory_image{{{0x1020u, 0xfeedbeefu}}}; TestMemory memory_image{{{0x1020u, 0xfeedbeefu}}};
@@ -173,8 +172,8 @@ void TestDynamicReadRemainsTyped() {
{read, Value(0u), Value(16u), Value(0u)}); {read, Value(0u), Value(16u), Value(0u)});
fixture.Plan(); fixture.Plan();
Check(fixture.program.values->srt_reads.empty() && Check(fixture.program.srt_reads.empty() &&
fixture.program.values->dynamic_reads == std::vector<Value>{read}, fixture.program.dynamic_reads == std::vector<Value>{read},
"dynamic scalar read received a fake flattened slot"); "dynamic scalar read received a fake flattened slot");
} }
@@ -209,7 +208,7 @@ void TestShaderBaseAndUserData() {
{Value(static_cast<ScalarReg>(2))}); {Value(static_cast<ScalarReg>(2))});
const auto sum = fixture.Emit(ValueOpcode::IAdd32, {user, Value(4u)}); const auto sum = fixture.Emit(ValueOpcode::IAdd32, {user, Value(4u)});
fixture.Plan(); fixture.Plan();
fixture.program.values->descriptor_sources.push_back( fixture.program.descriptor_sources.push_back(
{.dwords = {low, high, sum}, .dword_count = 3}); {.dwords = {low, high, sum}, .dword_count = 3});
const std::array user_data{0x20u}; const std::array user_data{0x20u};
@@ -239,7 +238,7 @@ void TestCarryAndBitFields() {
const auto sign = fixture.Emit(ValueOpcode::BitFieldSExtract, const auto sign = fixture.Emit(ValueOpcode::BitFieldSExtract,
{Value(0x000000f0u), Value(4u), Value(4u)}); {Value(0x000000f0u), Value(4u), Value(4u)});
fixture.Plan(); fixture.Plan();
fixture.program.values->descriptor_sources.push_back( fixture.program.descriptor_sources.push_back(
{.dwords = {low, high, inserted, sign}, .dword_count = 4}); {.dwords = {low, high, inserted, sign}, .dword_count = 4});
DescriptorValue result; DescriptorValue result;
@@ -262,9 +261,9 @@ void TestInvariantAndDivergentPhi() {
divergent.AddPhiOperand(&fixture.BlockAt(0), Value(7u)); divergent.AddPhiOperand(&fixture.BlockAt(0), Value(7u));
divergent.AddPhiOperand(&fixture.BlockAt(1), Value(9u)); divergent.AddPhiOperand(&fixture.BlockAt(1), Value(9u));
fixture.Plan(); fixture.Plan();
fixture.program.values->descriptor_sources.push_back( fixture.program.descriptor_sources.push_back(
{.dwords = {Value(&invariant)}, .dword_count = 1}); {.dwords = {Value(&invariant)}, .dword_count = 1});
fixture.program.values->descriptor_sources.push_back( fixture.program.descriptor_sources.push_back(
{.dwords = {Value(&divergent)}, .dword_count = 1}); {.dwords = {Value(&divergent)}, .dword_count = 1});
DescriptorValue result; DescriptorValue result;
@@ -293,10 +292,10 @@ void TestControlDependentStandaloneLoadStaysTyped() {
RawRead(fixture, Address(fixture, Value(&base), Value(0u), 2), Value(0u), RawRead(fixture, Address(fixture, Value(&base), Value(0u), 2), Value(0u),
memory, 2); memory, 2);
fixture.Plan(); fixture.Plan();
Check(fixture.program.values->srt_reads.empty() && Check(fixture.program.srt_reads.empty() &&
read.ResolveInstruction()->GetOpcode() == read.ResolveInstruction()->GetOpcode() ==
ValueOpcode::LoadAddressU32 && ValueOpcode::LoadAddressU32 &&
!fixture.program.values->memory_info[memory].planning_only, !fixture.program.memory_info[memory].planning_only,
"control-dependent standalone scalar load was flattened into a host " "control-dependent standalone scalar load was flattened into a host "
"snapshot"); "snapshot");
} }
@@ -315,7 +314,7 @@ void TestRuntime64BitDescriptorOps() {
const auto high = const auto high =
fixture.Emit(ValueOpcode::CompositeExtractU64, {combined, Value(1u)}); fixture.Emit(ValueOpcode::CompositeExtractU64, {combined, Value(1u)});
fixture.Plan(); fixture.Plan();
fixture.program.values->descriptor_sources.push_back( fixture.program.descriptor_sources.push_back(
{.dwords = {low, high}, .dword_count = 2}); {.dwords = {low, high}, .dword_count = 2});
DescriptorValue result; DescriptorValue result;
@@ -370,7 +369,7 @@ void TestReadLaneElimination() {
{undef, Value(0xdeadbeefu), Value(5u)}); {undef, Value(0xdeadbeefu), Value(5u)});
const auto read = fixture.Emit(ValueOpcode::ReadLane, {write, Value(5u)}); const auto read = fixture.Emit(ValueOpcode::ReadLane, {write, Value(5u)});
const auto use = fixture.Emit(ValueOpcode::IAdd32, {read, Value(1u)}); const auto use = fixture.Emit(ValueOpcode::IAdd32, {read, Value(1u)});
const auto stats = EliminateReadLane(*fixture.program.values, 64); const auto stats = EliminateReadLane(fixture.program, 64);
Check(stats.rewritten_reads == 1 && Check(stats.rewritten_reads == 1 &&
use.ResolveInstruction()->Arg(0).Resolve() == Value(0xdeadbeefu), use.ResolveInstruction()->Arg(0).Resolve() == Value(0xdeadbeefu),
"fixed-lane typed read was not rewritten from its SSA write chain"); "fixed-lane typed read was not rewritten from its SSA write chain");
@@ -379,7 +378,7 @@ void TestReadLaneElimination() {
{Value(static_cast<ScalarReg>(2))}); {Value(static_cast<ScalarReg>(2))});
const auto dynamic = fixture.Emit(ValueOpcode::ReadLane, {write, selector}); const auto dynamic = fixture.Emit(ValueOpcode::ReadLane, {write, selector});
fixture.Emit(ValueOpcode::IAdd32, {dynamic, Value(1u)}); fixture.Emit(ValueOpcode::IAdd32, {dynamic, Value(1u)});
Check(EliminateReadLane(*fixture.program.values, 64).rewritten_reads == 0, Check(EliminateReadLane(fixture.program, 64).rewritten_reads == 0,
"dynamic-lane read was rewritten unsafely"); "dynamic-lane read was rewritten unsafely");
} }
@@ -390,9 +389,9 @@ void TestOptimizationPipeline() {
fixture.Emit(ValueOpcode::ReferenceU32, {kept}); fixture.Emit(ValueOpcode::ReferenceU32, {kept});
fixture.Emit(ValueOpcode::IMul32, {Value(6u), Value(7u)}); fixture.Emit(ValueOpcode::IMul32, {Value(6u), Value(7u)});
ConstantPropagationPass(fixture.program.values->blocks); ConstantPropagationPass(fixture.program.blocks);
RemoveIdentities(fixture.program.values->blocks); RemoveIdentities(fixture.program.blocks);
EliminateDeadCode(fixture.program.values->blocks); EliminateDeadCode(fixture.program.blocks);
const auto &instructions = fixture.BlockAt().Instructions(); const auto &instructions = fixture.BlockAt().Instructions();
Check(instructions.size() == 1 && Check(instructions.size() == 1 &&
@@ -404,20 +403,20 @@ void TestOptimizationPipeline() {
void TestControlFlowValueSurvivesReadLaneFolding() { void TestControlFlowValueSurvivesReadLaneFolding() {
Fixture fixture(3); Fixture fixture(3);
auto *entry = fixture.program.values->blocks[0]; auto *entry = fixture.program.blocks[0];
auto *taken = fixture.program.values->blocks[1]; auto *taken = fixture.program.blocks[1];
auto *other = fixture.program.values->blocks[2]; auto *other = fixture.program.blocks[2];
entry->AddBranch(taken); entry->AddBranch(taken);
entry->AddBranch(other); entry->AddBranch(other);
auto &entry_info = fixture.program.values->block_info[0]; auto &entry_info = fixture.program.block_info[0];
entry_info.terminator.kind = entry_info.terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
entry_info.terminator.true_block = 1; entry_info.terminator.true_block = 1;
entry_info.terminator.false_block = 2; entry_info.terminator.false_block = 2;
fixture.program.values->block_info[1].terminator.kind = fixture.program.block_info[1].terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return; Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
fixture.program.values->block_info[2].terminator.kind = fixture.program.block_info[2].terminator.kind =
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return; Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
const auto undef = fixture.Emit(ValueOpcode::UndefU32); const auto undef = fixture.Emit(ValueOpcode::UndefU32);
@@ -429,16 +428,16 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
fixture.Emit(ValueOpcode::IEqual32, {read, Value(42u)}); fixture.Emit(ValueOpcode::IEqual32, {read, Value(42u)});
fixture.Emit(ValueOpcode::Reference, {entry_info.condition}); fixture.Emit(ValueOpcode::Reference, {entry_info.condition});
Check(EliminateReadLane(*fixture.program.values, 64).rewritten_reads == 1, Check(EliminateReadLane(fixture.program, 64).rewritten_reads == 1,
"control-flow fixture did not eliminate its fixed-lane read"); "control-flow fixture did not eliminate its fixed-lane read");
ConstantPropagationPass(fixture.program.values->blocks); ConstantPropagationPass(fixture.program.blocks);
ResolveControlFlowIdentities(*fixture.program.values); ResolveControlFlowIdentities(fixture.program);
RemoveIdentities(fixture.program.values->blocks); RemoveIdentities(fixture.program.blocks);
EliminateDeadCode(fixture.program.values->blocks); EliminateDeadCode(fixture.program.blocks);
std::string error; std::string error;
Check(entry_info.condition == Value(true) && Check(entry_info.condition == Value(true) &&
ValidateValueProgram(*fixture.program.values, true, &error), ValidateProgram(fixture.program, true, &error),
error.empty() ? "folded branch condition did not survive identity removal" error.empty() ? "folded branch condition did not survive identity removal"
: error.c_str()); : error.c_str());
} }
@@ -447,7 +446,7 @@ void TestUndefinedRuntimeValueFails() {
Fixture fixture; Fixture fixture;
const auto undef = fixture.Emit(ValueOpcode::UndefU32); const auto undef = fixture.Emit(ValueOpcode::UndefU32);
fixture.Plan(); fixture.Plan();
fixture.program.values->descriptor_sources.push_back( fixture.program.descriptor_sources.push_back(
{.dwords = {undef}, .dword_count = 1}); {.dwords = {undef}, .dword_count = 1});
DescriptorValue result; DescriptorValue result;
result.dword_count = 3; result.dword_count = 3;
@@ -499,7 +498,7 @@ int main() {
#include "../src/graphics/shader/recompiler/ir/Block.cpp" #include "../src/graphics/shader/recompiler/ir/Block.cpp"
#include "../src/graphics/shader/recompiler/ir/Type.cpp" #include "../src/graphics/shader/recompiler/ir/Type.cpp"
#include "../src/graphics/shader/recompiler/ir/Value.cpp" #include "../src/graphics/shader/recompiler/ir/Value.cpp"
#include "../src/graphics/shader/recompiler/ir/ValueProgram.cpp" #include "../src/graphics/shader/recompiler/ir/Program.cpp"
#include "../src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp" #include "../src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
#include "../src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp" #include "../src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp"
#include "../src/graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp" #include "../src/graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp"
+88 -117
View File
@@ -38,7 +38,7 @@
#include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h" #include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h"
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h" #include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h" #include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
#include "graphics/shader/rectListShader.h" #include "graphics/shader/rectListShader.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
@@ -967,7 +967,6 @@ struct TestCase {
std::vector<u32> expected_gds; std::vector<u32> expected_gds;
std::vector<std::pair<std::string, size_t>> decoded_counts; std::vector<std::pair<std::string, size_t>> decoded_counts;
std::vector<std::pair<std::string, size_t>> ir_counts; std::vector<std::pair<std::string, size_t>> ir_counts;
std::vector<std::pair<std::string, size_t>> native_ir_counts;
u32 expected_storage_mip_descriptors = 0; u32 expected_storage_mip_descriptors = 0;
}; };
@@ -1192,32 +1191,6 @@ CompiledShader CompileCase(const TestCase &test) {
text + " count=" + std::to_string(actual) + text + " count=" + std::to_string(actual) +
", expected=" + std::to_string(expected)); ", expected=" + std::to_string(expected));
} }
if (!test.native_ir_counts.empty()) {
ShaderRecompiler::Decoder::Program decoded;
ShaderRecompiler::CFG::Graph cfg;
ShaderRecompiler::IR::Program native;
std::string native_error;
Require(test.name, "native decode",
ShaderRecompiler::Decoder::DecodeProgram(test.code, decoded,
&native_error),
native_error);
Require(test.name, "native CFG",
ShaderRecompiler::CFG::BuildGraph(decoded, cfg, &native_error) &&
ShaderRecompiler::CFG::Structurize(cfg, &native_error),
native_error);
Require(test.name, "native lowering",
ShaderRecompiler::IR::LowerProgram(decoded, cfg, options.stage,
options.wave_size, native,
&native_error),
native_error);
const auto native_ir = ShaderRecompiler::IR::ProgramToString(native);
for (const auto &[text, expected] : test.native_ir_counts) {
const auto actual = CountText(native_ir, text);
Require(test.name, "native IR", actual == expected,
text + " count=" + std::to_string(actual) +
", expected=" + std::to_string(expected));
}
}
if (test.expected_storage_mip_descriptors != 0) { if (test.expected_storage_mip_descriptors != 0) {
const auto image = std::ranges::find_if( const auto image = std::ranges::find_if(
result.program.info.images, [](const auto &resource) { result.program.info.images, [](const auto &resource) {
@@ -1242,7 +1215,7 @@ CompiledShader CompileCase(const TestCase &test) {
"dynamic storage image did not receive one descriptor per mip"); "dynamic storage image did not receive one descriptor per mip");
} }
if (test.expand_shader_data_storage) { if (test.expand_shader_data_storage) {
auto &block = *result.program.values->blocks.front(); auto &block = *result.program.blocks.front();
for (u32 reg = 0; reg < 33; reg++) { for (u32 reg = 0; reg < 33; reg++) {
auto &user_data = block.AppendNewInst( auto &user_data = block.AppendNewInst(
ShaderRecompiler::IR::ValueOpcode::GetUserData, ShaderRecompiler::IR::ValueOpcode::GetUserData,
@@ -7519,8 +7492,6 @@ public:
ShaderRecompiler::IR::Program null_program{}; ShaderRecompiler::IR::Program null_program{};
null_program.stage = ShaderType::Compute; null_program.stage = ShaderType::Compute;
null_program.values =
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
null_program.resource_tracking_complete = true; null_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource null_resource{}; ShaderRecompiler::IR::ImageResource null_resource{};
null_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; null_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
@@ -7630,8 +7601,6 @@ public:
storage.fields[5] = 0x00700000u; storage.fields[5] = 0x00700000u;
ShaderRecompiler::IR::Program storage_program{}; ShaderRecompiler::IR::Program storage_program{};
storage_program.stage = ShaderType::Vertex; storage_program.stage = ShaderType::Vertex;
storage_program.values =
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
storage_program.resource_tracking_complete = true; storage_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource storage_resource{}; ShaderRecompiler::IR::ImageResource storage_resource{};
storage_resource.kind = storage_resource.kind =
@@ -7911,8 +7880,6 @@ public:
ShaderRecompiler::IR::Program sampled_program{}; ShaderRecompiler::IR::Program sampled_program{};
sampled_program.stage = ShaderType::Pixel; sampled_program.stage = ShaderType::Pixel;
sampled_program.values =
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
sampled_program.resource_tracking_complete = true; sampled_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource sampled_resource{}; ShaderRecompiler::IR::ImageResource sampled_resource{};
sampled_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; sampled_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
@@ -8003,15 +7970,19 @@ public:
RenderExecutorTestAccess::ResetBindings(executor); RenderExecutorTestAccess::ResetBindings(executor);
auto vertex_sampled_program = auto vertex_sampled_program =
std::make_shared<ShaderRecompiler::IR::Program>( std::make_shared<ShaderRecompiler::IR::Program>();
*sampled_runtime.program);
vertex_sampled_program->stage = ShaderType::Vertex; vertex_sampled_program->stage = ShaderType::Vertex;
vertex_sampled_program->resource_tracking_complete = true;
vertex_sampled_program->info.images.push_back(sampled_resource);
allocate_bindings(*vertex_sampled_program);
ShaderStageRuntime vertex_sampled_runtime{ ShaderStageRuntime vertex_sampled_runtime{
std::move(vertex_sampled_program), sampled_runtime.resources}; std::move(vertex_sampled_program), sampled_runtime.resources};
auto pixel_storage_program = auto pixel_storage_program =
std::make_shared<ShaderRecompiler::IR::Program>( std::make_shared<ShaderRecompiler::IR::Program>();
*storage_runtime.program);
pixel_storage_program->stage = ShaderType::Pixel; pixel_storage_program->stage = ShaderType::Pixel;
pixel_storage_program->resource_tracking_complete = true;
pixel_storage_program->info.images.push_back(storage_resource);
allocate_bindings(*pixel_storage_program);
ShaderStageRuntime pixel_storage_runtime{std::move(pixel_storage_program), ShaderStageRuntime pixel_storage_runtime{std::move(pixel_storage_program),
storage_runtime.resources}; storage_runtime.resources};
auto writable_alias_bindings = auto writable_alias_bindings =
@@ -8083,8 +8054,6 @@ public:
auto split_program = std::make_shared<ShaderRecompiler::IR::Program>(); auto split_program = std::make_shared<ShaderRecompiler::IR::Program>();
split_program->stage = ShaderType::Vertex; split_program->stage = ShaderType::Vertex;
split_program->values =
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
split_program->resource_tracking_complete = true; split_program->resource_tracking_complete = true;
split_program->info.images = {storage_resource, sampled_resource}; split_program->info.images = {storage_resource, sampled_resource};
allocate_bindings(*split_program); allocate_bindings(*split_program);
@@ -8459,8 +8428,6 @@ public:
ShaderRecompiler::IR::Program array_program{}; ShaderRecompiler::IR::Program array_program{};
array_program.stage = ShaderType::Compute; array_program.stage = ShaderType::Compute;
array_program.values =
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
array_program.resource_tracking_complete = true; array_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource array_resource{}; ShaderRecompiler::IR::ImageResource array_resource{};
array_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; array_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
@@ -8544,10 +8511,13 @@ public:
colliding_msaa_descriptor.dwords.begin()); colliding_msaa_descriptor.dwords.begin());
colliding_msaa_descriptor.dword_count = 8; colliding_msaa_descriptor.dword_count = 8;
auto colliding_msaa_program = auto colliding_msaa_program =
std::make_shared<ShaderRecompiler::IR::Program>( std::make_shared<ShaderRecompiler::IR::Program>();
*array_runtime.program); colliding_msaa_program->stage = ShaderType::Compute;
colliding_msaa_program->info.images[0].dimension = colliding_msaa_program->resource_tracking_complete = true;
auto colliding_msaa_resource = array_resource;
colliding_msaa_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa; ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa;
colliding_msaa_program->info.images.push_back(colliding_msaa_resource);
auto colliding_msaa_snapshot = auto colliding_msaa_snapshot =
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>(); std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
colliding_msaa_snapshot->images.push_back(colliding_msaa_descriptor); colliding_msaa_snapshot->images.push_back(colliding_msaa_descriptor);
@@ -8604,10 +8574,13 @@ public:
std::copy(std::begin(msaa_texture.fields), std::end(msaa_texture.fields), std::copy(std::begin(msaa_texture.fields), std::end(msaa_texture.fields),
msaa_descriptor.dwords.begin()); msaa_descriptor.dwords.begin());
msaa_descriptor.dword_count = 8; msaa_descriptor.dword_count = 8;
auto msaa_program = std::make_shared<ShaderRecompiler::IR::Program>( auto msaa_program = std::make_shared<ShaderRecompiler::IR::Program>();
*array_runtime.program); msaa_program->stage = ShaderType::Compute;
msaa_program->info.images[0].dimension = msaa_program->resource_tracking_complete = true;
auto msaa_resource = array_resource;
msaa_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa; ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa;
msaa_program->info.images.push_back(msaa_resource);
auto msaa_snapshot = auto msaa_snapshot =
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>(); std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
msaa_snapshot->images.push_back(msaa_descriptor); msaa_snapshot->images.push_back(msaa_descriptor);
@@ -8643,10 +8616,14 @@ public:
std::end(msaa_array_texture.fields), std::end(msaa_array_texture.fields),
msaa_array_descriptor.dwords.begin()); msaa_array_descriptor.dwords.begin());
msaa_array_descriptor.dword_count = 8; msaa_array_descriptor.dword_count = 8;
auto msaa_array_program = std::make_shared<ShaderRecompiler::IR::Program>( auto msaa_array_program =
*msaa_runtime.program); std::make_shared<ShaderRecompiler::IR::Program>();
msaa_array_program->info.images[0].dimension = msaa_array_program->stage = ShaderType::Compute;
msaa_array_program->resource_tracking_complete = true;
auto msaa_array_resource = array_resource;
msaa_array_resource.dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray; ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray;
msaa_array_program->info.images.push_back(msaa_array_resource);
auto msaa_array_snapshot = auto msaa_array_snapshot =
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>(); std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
msaa_array_snapshot->images.push_back(msaa_array_descriptor); msaa_array_snapshot->images.push_back(msaa_array_descriptor);
@@ -8868,7 +8845,6 @@ public:
ShaderRecompiler::IR::Program program{}; ShaderRecompiler::IR::Program program{};
program.stage = ShaderType::Compute; program.stage = ShaderType::Compute;
program.values = std::make_shared<ShaderRecompiler::IR::ValueProgram>();
program.resource_tracking_complete = true; program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource stencil_resource{}; ShaderRecompiler::IR::ImageResource stencil_resource{};
stencil_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint; stencil_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
@@ -8940,11 +8916,15 @@ public:
RenderExecutorTestAccess::ResetBindings(executor); RenderExecutorTestAccess::ResetBindings(executor);
auto stencil_storage_program = auto stencil_storage_program =
std::make_shared<ShaderRecompiler::IR::Program>(*runtime.program); std::make_shared<ShaderRecompiler::IR::Program>();
stencil_storage_program->info.images[0].kind = stencil_storage_program->stage = ShaderType::Compute;
stencil_storage_program->resource_tracking_complete = true;
auto stencil_storage_resource = stencil_resource;
stencil_storage_resource.kind =
ShaderRecompiler::IR::ResourceKind::StorageImageUint; ShaderRecompiler::IR::ResourceKind::StorageImageUint;
stencil_storage_program->info.images[0].read = false; stencil_storage_resource.read = false;
stencil_storage_program->info.images[0].written = true; stencil_storage_resource.written = true;
stencil_storage_program->info.images.push_back(stencil_storage_resource);
auto storage_stencil = stencil; auto storage_stencil = stencil;
storage_stencil.fields[5] = 0x00700000u; storage_stencil.fields[5] = 0x00700000u;
ShaderRecompiler::IR::DescriptorValue stencil_storage_descriptor{}; ShaderRecompiler::IR::DescriptorValue stencil_storage_descriptor{};
@@ -12132,7 +12112,6 @@ TestCase ScalarWaitcntDepctrCapturedVmVsrc() {
test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_WAITCNT_DEPCTR, test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_WAITCNT_DEPCTR,
O::S_ENDPGM}; O::S_ENDPGM};
test.decoded_counts = {{"0x00000010: S_WAITCNT_DEPCTR 0x0000ffe3\n", 1}}; test.decoded_counts = {{"0x00000010: S_WAITCNT_DEPCTR 0x0000ffe3\n", 1}};
test.native_ir_counts = {{"0x00000010: Waitcnt null, 0x0000ffe3", 1}};
test.forbidden_spirv = {"OpControlBarrier", "OpMemoryBarrier"}; test.forbidden_spirv = {"OpControlBarrier", "OpMemoryBarrier"};
return test; return test;
} }
@@ -13289,7 +13268,6 @@ TestCase Vop3Med3I16Captured() {
test.decoded_counts = {{"0x00000020: V_MED3_I16 v3.sdwa(sel=5,sext=0), " test.decoded_counts = {{"0x00000020: V_MED3_I16 v3.sdwa(sel=5,sext=0), "
"v5.opsel(lo=1,hi=0,neghi=0), s25, s27\n", "v5.opsel(lo=1,hi=0,neghi=0), s25, s27\n",
1}}; 1}};
test.native_ir_counts = {{"IMed3I16", 1}};
test.required_spirv = {"OpSLessThan"}; test.required_spirv = {"OpSLessThan"};
return test; return test;
} }
@@ -15227,8 +15205,6 @@ TestCase VectorVopcCmpxNeU64CapturedExecMask() {
O::S_MOV_B32, O::S_ENDPGM}}; O::S_MOV_B32, O::S_ENDPGM}};
test.decoded_counts = {{"V_CMPX_NE_U64 exec_lo, 0, v7", 2}, test.decoded_counts = {{"V_CMPX_NE_U64 exec_lo, 0, v7", 2},
{"V_CMPX_NE_U64 exec_lo, 0, v2", 2}}; {"V_CMPX_NE_U64 exec_lo, 0, v2", 2}};
test.native_ir_counts = {{"CompareNeU64 exec_lo, 0x00000000, v7", 2},
{"CompareNeU64 exec_lo, 0x00000000, v2", 2}};
return test; return test;
} }
@@ -15258,7 +15234,6 @@ TestCase VectorVop3CmpxNeI64CapturedExecMask() {
{O::V_MOV_B32, O::V_CMPX_NE_I64, O::BUFFER_STORE_DWORD, {O::V_MOV_B32, O::V_CMPX_NE_I64, O::BUFFER_STORE_DWORD,
O::S_MOV_B32, O::S_ENDPGM}}; O::S_MOV_B32, O::S_ENDPGM}};
test.decoded_counts = {{"V_CMPX_NE_I64 exec_lo, 0, v7", 2}}; test.decoded_counts = {{"V_CMPX_NE_I64 exec_lo, 0, v7", 2}};
test.native_ir_counts = {{"CompareNeU64 exec_lo, 0x00000000, v7", 2}};
return test; return test;
} }
@@ -17738,7 +17713,6 @@ TestCase DsReadU16D16CapturedPreservesHighHalf() {
{O::V_MOV_B32, O::DS_WRITE_B32, O::DS_READ_U16_D16, {O::V_MOV_B32, O::DS_WRITE_B32, O::DS_READ_U16_D16,
O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
test.decoded_counts = {{"DS_READ_U16_D16", 2}}; test.decoded_counts = {{"DS_READ_U16_D16", 2}};
test.native_ir_counts = {{"DsReadUshort", 2}};
return test; return test;
} }
@@ -18652,7 +18626,6 @@ TestCase ImageLoadR128IgnoresAdjacentMaskSgprs() {
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt); test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt);
test.has_user_data = true; test.has_user_data = true;
test.decoded_counts = {{"r128=1", 1}}; test.decoded_counts = {{"r128=1", 1}};
test.native_ir_counts = {{"image_r128=1", 1}};
return test; return test;
} }
@@ -18974,8 +18947,6 @@ TestCase ImageD16GatherPacksHalfPairs() {
O::S_ENDPGM}; O::S_ENDPGM};
test.sampled_image_rgba = image; test.sampled_image_rgba = image;
test.decoded_counts = {{"d16=1", 1}}; test.decoded_counts = {{"d16=1", 1}};
test.native_ir_counts = {
{"dmask=0x1 data_dwords=2 data_bits=16 component=0/4", 1}};
test.required_spirv = {"OpImageGather", "PackHalf2x16"}; test.required_spirv = {"OpImageGather", "PackHalf2x16"};
return test; return test;
} }
@@ -19196,8 +19167,6 @@ TestCase ImageD16StoreUnpacksHalfPairs() {
test.storage_image_rgba = MakeRgbaImage(4, 4); test.storage_image_rgba = MakeRgbaImage(4, 4);
test.expected_storage_image_rgba = expected_image; test.expected_storage_image_rgba = expected_image;
test.decoded_counts = {{"d16=1", 1}}; test.decoded_counts = {{"d16=1", 1}};
test.native_ir_counts = {
{"dmask=0xf data_dwords=2 data_bits=16 component=0/4", 1}};
test.required_spirv = {"UnpackHalf2x16", "OpImageWrite"}; test.required_spirv = {"UnpackHalf2x16", "OpImageWrite"};
return test; return test;
} }
@@ -19213,11 +19182,10 @@ void CheckIndirectImageKeySwitch() {
program.srt_plan_complete = true; program.srt_plan_complete = true;
program.resource_tracking_complete = true; program.resource_tracking_complete = true;
program.shader_info_complete = true; program.shader_info_complete = true;
program.values = std::make_shared<ValueProgram>(); program.block_storage.push_back(std::make_unique<Block>());
program.values->block_storage.push_back(std::make_unique<Block>()); auto *block = program.block_storage.back().get();
auto *block = program.values->block_storage.back().get(); program.blocks.push_back(block);
program.values->blocks.push_back(block); program.block_info.push_back({.id = 0});
program.values->block_info.push_back({.id = 0});
auto &key = block->AppendNewInst(ValueOpcode::LaneId); auto &key = block->AppendNewInst(ValueOpcode::LaneId);
auto &image = auto &image =
@@ -19242,7 +19210,7 @@ void CheckIndirectImageKeySwitch() {
memory.image_sample_flags = ShaderRecompiler::Decoder::ImageSampleFlagLod; memory.image_sample_flags = ShaderRecompiler::Decoder::ImageSampleFlagLod;
memory.image_dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; memory.image_dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
memory.image_address_components = 3; memory.image_address_components = 3;
program.values->memory_info.push_back(memory); program.memory_info.push_back(memory);
const MemoryFlags memory_flags{0u, 0x10f0u}; const MemoryFlags memory_flags{0u, 0x10f0u};
uint64_t memory_flag_bits = 0; uint64_t memory_flag_bits = 0;
std::memcpy(&memory_flag_bits, &memory_flags, sizeof(memory_flags)); std::memcpy(&memory_flag_bits, &memory_flags, sizeof(memory_flags));
@@ -19253,11 +19221,11 @@ void CheckIndirectImageKeySwitch() {
{Value(&sample), Value(0u)}); {Value(&sample), Value(0u)});
block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(&sample_x)}); block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(&sample_x)});
program.values->descriptor_sources.resize(2); program.descriptor_sources.resize(2);
program.values->descriptor_sources[0].dword_count = 8; program.descriptor_sources[0].dword_count = 8;
program.values->descriptor_sources[0].indirect_image = program.descriptor_sources[0].indirect_image =
DescriptorSource::IndirectImage{0u, 0u, 224u, 12u, 0u}; DescriptorSource::IndirectImage{0u, 0u, 224u, 12u, 0u};
program.values->descriptor_sources[1].dword_count = 4; program.descriptor_sources[1].dword_count = 4;
ImageResource root{}; ImageResource root{};
root.source = 0; root.source = 0;
@@ -20394,8 +20362,8 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
const auto code = MakeCode(); const auto code = MakeCode();
auto positive = Compile("exact Prospero kernel", code); auto positive = Compile("exact Prospero kernel", code);
compute.stage.program = compute.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
std::make_shared<const ShaderRecompiler::IR::Program>(positive.program); std::move(positive.program));
compute.stage.resources = compute.stage.resources =
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>( std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
positive.resources); positive.resources);
@@ -20488,7 +20456,8 @@ void CheckEmbeddedFetchVertexOffset() {
Require(name, "compile", Require(name, "compile",
ShaderRecompiler::TryRecompile(code, options, result, &error), ShaderRecompiler::TryRecompile(code, options, result, &error),
error); error);
Require(name, "fetch rewrite", vertex.resource_fetch_components[0] == 4, Require(name, "fetch rewrite",
result.program.info.vertex_fetch_components[0] == 4,
"encoded fetch sequence was not recognized and rewritten"); "encoded fetch sequence was not recognized and rewritten");
return result; return result;
}; };
@@ -20497,8 +20466,11 @@ void CheckEmbeddedFetchVertexOffset() {
u32 index_offset) { u32 index_offset) {
ShaderVertexInputInfo vertex; ShaderVertexInputInfo vertex;
vertex.fetch_embedded = true; vertex.fetch_embedded = true;
vertex.stage.program = auto program = std::make_shared<ShaderRecompiler::IR::Program>();
std::make_shared<const ShaderRecompiler::IR::Program>(result.program); program->user_data_base = result.program.user_data_base;
program->info.vertex_offset_sgpr =
result.program.info.vertex_offset_sgpr;
vertex.stage.program = std::move(program);
vertex.stage.resources = vertex.stage.resources =
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>( std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
result.resources); result.resources);
@@ -22366,52 +22338,55 @@ void CheckImageSpecializationId() {
HW::ComputeShaderInfo regs{}; HW::ComputeShaderInfo regs{};
regs.cs_regs.data_addr = reinterpret_cast<uint64_t>(code.data()); regs.cs_regs.data_addr = reinterpret_cast<uint64_t>(code.data());
ShaderRecompiler::IR::Program identity_program;
identity_program.binding_layout_complete = true;
ShaderRecompiler::IR::ImageResource image; ShaderRecompiler::IR::ImageResource image;
image.kind = ShaderRecompiler::IR::ResourceKind::StorageImage; image.kind = ShaderRecompiler::IR::ResourceKind::StorageImage;
image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
identity_program.info.images.push_back(image); const auto make_program = [](ShaderRecompiler::IR::ImageResource resource) {
auto rgb1_program = identity_program; ShaderRecompiler::IR::Program program;
rgb1_program.info.images[0].shader_swizzle = DstSel(4, 5, 6, 1); program.binding_layout_complete = true;
auto converted_program = identity_program; program.info.images.push_back(std::move(resource));
converted_program.info.images[0].conversion_format = return program;
};
auto rgb1_image = image;
rgb1_image.shader_swizzle = DstSel(4, 5, 6, 1);
auto converted_image = image;
converted_image.conversion_format =
Prospero::BufferFormat::k11_11_10UInt; Prospero::BufferFormat::k11_11_10UInt;
auto mip1_program = identity_program; auto mip1_image = image;
mip1_program.info.images[0].mip_mode = mip1_image.mip_mode = ShaderRecompiler::IR::ImageMipMode::DynamicStorage;
ShaderRecompiler::IR::ImageMipMode::DynamicStorage; auto mip3_image = mip1_image;
auto mip3_program = mip1_program; mip3_image.mip_count = 3;
mip3_program.info.images[0].mip_count = 3; auto array_image = image;
auto array_program = identity_program; array_image.dimension =
array_program.info.images[0].dimension =
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray; ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
auto cube_program = array_program; auto cube_image = array_image;
cube_program.info.images[0].cube = true; cube_image.cube = true;
const auto resources = const auto resources =
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(); std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>();
ShaderComputeInputInfo identity_info; ShaderComputeInputInfo identity_info;
identity_info.stage.program = identity_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
std::make_shared<const ShaderRecompiler::IR::Program>(identity_program); make_program(image));
identity_info.stage.resources = resources; identity_info.stage.resources = resources;
auto rgb1_info = identity_info; auto rgb1_info = identity_info;
rgb1_info.stage.program = rgb1_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
std::make_shared<const ShaderRecompiler::IR::Program>(rgb1_program); make_program(rgb1_image));
auto converted_info = identity_info; auto converted_info = identity_info;
converted_info.stage.program = converted_info.stage.program =
std::make_shared<const ShaderRecompiler::IR::Program>(converted_program); std::make_shared<const ShaderRecompiler::IR::Program>(
make_program(converted_image));
auto mip1_info = identity_info; auto mip1_info = identity_info;
mip1_info.stage.program = mip1_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
std::make_shared<const ShaderRecompiler::IR::Program>(mip1_program); make_program(mip1_image));
auto mip3_info = identity_info; auto mip3_info = identity_info;
mip3_info.stage.program = mip3_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
std::make_shared<const ShaderRecompiler::IR::Program>(mip3_program); make_program(mip3_image));
auto array_info = identity_info; auto array_info = identity_info;
array_info.stage.program = array_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
std::make_shared<const ShaderRecompiler::IR::Program>(array_program); make_program(array_image));
auto cube_info = identity_info; auto cube_info = identity_info;
cube_info.stage.program = cube_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
std::make_shared<const ShaderRecompiler::IR::Program>(cube_program); make_program(cube_image));
const auto identity_id = ShaderGetIdCS(regs, identity_info, true); const auto identity_id = ShaderGetIdCS(regs, identity_info, true);
const auto rgb1_id = ShaderGetIdCS(regs, rgb1_info, true); const auto rgb1_id = ShaderGetIdCS(regs, rgb1_info, true);
@@ -22452,8 +22427,6 @@ void CheckImageSpecializationId() {
"forcing point filtering enabled mipmapping on a base-mip sampler"); "forcing point filtering enabled mipmapping on a base-mip sampler");
ShaderRecompiler::IR::Program mixed_sampler_program; ShaderRecompiler::IR::Program mixed_sampler_program;
mixed_sampler_program.values =
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
mixed_sampler_program.resource_tracking_complete = true; mixed_sampler_program.resource_tracking_complete = true;
ShaderRecompiler::IR::ImageResource sampled_image; ShaderRecompiler::IR::ImageResource sampled_image;
sampled_image.kind = ShaderRecompiler::IR::ResourceKind::Image; sampled_image.kind = ShaderRecompiler::IR::ResourceKind::Image;
@@ -22500,8 +22473,6 @@ void CheckImageSpecializationId() {
"native variants"); "native variants");
ShaderRecompiler::IR::Program packed_atomic_program; ShaderRecompiler::IR::Program packed_atomic_program;
packed_atomic_program.values =
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
packed_atomic_program.resource_tracking_complete = true; packed_atomic_program.resource_tracking_complete = true;
auto packed_atomic_image = sampled_image; auto packed_atomic_image = sampled_image;
packed_atomic_image.kind = packed_atomic_image.kind =
@@ -23245,7 +23216,7 @@ void CheckEmbeddedFetchLaneSpill() {
Require("EmbeddedFetchLaneSpill", "compile", Require("EmbeddedFetchLaneSpill", "compile",
ShaderRecompiler::TryRecompile(code, options, result, &error), error); ShaderRecompiler::TryRecompile(code, options, result, &error), error);
Require("EmbeddedFetchLaneSpill", "fetch rewrite", Require("EmbeddedFetchLaneSpill", "fetch rewrite",
vertex.resource_fetch_components[0] == 4, result.program.info.vertex_fetch_components[0] == 4,
"lane-spilled fetch descriptor was not recognized and rewritten"); "lane-spilled fetch descriptor was not recognized and rewritten");
std::printf("[host] %-32s ok\n", "EmbeddedFetchLaneSpill"); std::printf("[host] %-32s ok\n", "EmbeddedFetchLaneSpill");
} }
+8 -9
View File
@@ -1,4 +1,4 @@
#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h" #include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
#include "graphics/shader/shader.h" #include "graphics/shader/shader.h"
@@ -23,12 +23,11 @@ bool RejectSpecializationRead(void *userdata, uint64_t, uint32_t *) {
Libs::Graphics::ShaderRecompiler::IR::Block & Libs::Graphics::ShaderRecompiler::IR::Block &
AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) { AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) {
using namespace Libs::Graphics::ShaderRecompiler::IR; using namespace Libs::Graphics::ShaderRecompiler::IR;
program.values = std::make_shared<ValueProgram>();
auto block = std::make_unique<Block>(); auto block = std::make_unique<Block>();
auto *result = block.get(); auto *result = block.get();
program.values->blocks.push_back(result); program.blocks.push_back(result);
program.values->block_info.push_back({.id = 0}); program.block_info.push_back({.id = 0});
program.values->block_storage.push_back(std::move(block)); program.block_storage.push_back(std::move(block));
return *result; return *result;
} }
@@ -44,7 +43,7 @@ SrtProgram(uint64_t address) {
MemoryInfo memory; MemoryInfo memory;
memory.kind = ResourceKind::ScalarAddress; memory.kind = ResourceKind::ScalarAddress;
memory.planning_only = true; memory.planning_only = true;
program.values->memory_info.push_back(memory); program.memory_info.push_back(memory);
const auto low = Value(static_cast<uint32_t>(address)); const auto low = Value(static_cast<uint32_t>(address));
const auto high = Value(static_cast<uint32_t>(address >> 32u)); const auto high = Value(static_cast<uint32_t>(address >> 32u));
auto &handle = auto &handle =
@@ -53,7 +52,7 @@ SrtProgram(uint64_t address) {
ValueOpcode::LoadAddressU32, ValueOpcode::LoadAddressU32,
{Value(&handle), Value(0u), Value(0u), Value(true)}); {Value(&handle), Value(0u), Value(0u), Value(true)});
raw.SetFlags(MemoryFlags{.index = 0, .pc = 0x40}); raw.SetFlags(MemoryFlags{.index = 0, .pc = 0x40});
program.values->srt_reads.push_back({Value(&raw), 0, 0x40}); program.srt_reads.push_back({Value(&raw), 0, 0x40});
auto &srt = value_block.AppendNewInst(ValueOpcode::GetSrtResource); auto &srt = value_block.AppendNewInst(ValueOpcode::GetSrtResource);
auto &flat = value_block.AppendNewInst(ValueOpcode::ReadConst, auto &flat = value_block.AppendNewInst(ValueOpcode::ReadConst,
@@ -62,7 +61,7 @@ SrtProgram(uint64_t address) {
source.dwords[0] = Value(&flat); source.dwords[0] = Value(&flat);
source.dwords[1] = Value(0u); source.dwords[1] = Value(0u);
source.dword_count = 2; source.dword_count = 2;
program.values->descriptor_sources.push_back(source); program.descriptor_sources.push_back(source);
return std::make_shared<const Program>(std::move(program)); return std::make_shared<const Program>(std::move(program));
} }
@@ -141,5 +140,5 @@ int main() {
#include "graphics/shader/recompiler/ir/Block.cpp" #include "graphics/shader/recompiler/ir/Block.cpp"
#include "graphics/shader/recompiler/ir/Type.cpp" #include "graphics/shader/recompiler/ir/Type.cpp"
#include "graphics/shader/recompiler/ir/Value.cpp" #include "graphics/shader/recompiler/ir/Value.cpp"
#include "graphics/shader/recompiler/ir/ValueProgram.cpp" #include "graphics/shader/recompiler/ir/Program.cpp"
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp" #include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
+467 -388
View File
File diff suppressed because it is too large. Load diff