mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
shader: remove second IR layer
This commit is contained in:
1 parent
e2c840cb23
commit
ffa69d48de
49 files changed
+3691
-6001
No files matched your search
@@ -430,6 +430,7 @@ add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ResourceTrackingTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.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/ResourceTracking.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
|
||||
|
||||
@@ -548,9 +548,10 @@ void CreatePipelineInternal(
|
||||
|
||||
uint32_t attr_size = 4;
|
||||
auto registers_num = vs_input_info.resources_dst[index].registers_num;
|
||||
auto used_components = (vs_input_info.resource_fetch_components[index] > 0
|
||||
? vs_input_info.resource_fetch_components[index]
|
||||
: registers_num);
|
||||
const auto compiled_components =
|
||||
vs_input_info.stage.program->info.vertex_fetch_components[index];
|
||||
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,
|
||||
static_cast<uint32_t>(used_components));
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translate.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/ConstantPropagation.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 += fmt::format("mode={} scratch_dwords={}\n",
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -117,17 +116,8 @@ void ClearEmbeddedFetchVectorLanes(EmbeddedFetchVectorLanes* lanes, uint32_t reg
|
||||
lanes->erase(first, last);
|
||||
}
|
||||
|
||||
struct EmbeddedFetchLoad {
|
||||
uint32_t pc = 0;
|
||||
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;
|
||||
};
|
||||
using EmbeddedFetchLoad = Frontend::EmbeddedFetchLoad;
|
||||
using EmbeddedFetchData = Frontend::EmbeddedFetchPlan;
|
||||
|
||||
bool IsDecodedSgpr(const Decoder::Operand& op) {
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
const ShaderVertexInputInfo* input_info,
|
||||
uint32_t user_data_base, uint32_t user_data_count,
|
||||
@@ -489,103 +470,6 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
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
|
||||
|
||||
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;
|
||||
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 ShaderPixelInputInfo* pixel = nullptr;
|
||||
const ShaderComputeInputInfo* compute = nullptr;
|
||||
@@ -709,42 +572,55 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
}
|
||||
EmbeddedFetchData embedded_fetch;
|
||||
if (options.stage == ShaderType::Vertex && vertex->fetch_embedded) {
|
||||
embedded_fetch = DetectEmbeddedVertexFetch(decoded, vertex, ir.user_data_base,
|
||||
ir.user_data_count, options.wave_size);
|
||||
auto rewritten = RewriteEmbeddedVertexFetches(ir, vertex, embedded_fetch.loads);
|
||||
if (rewritten > 0 || !embedded_fetch.loads.empty()) {
|
||||
LOGF("%s embedded vertex fetch rewrite: detected=%" PRIu64 " rewritten=%" PRIu32 "\n",
|
||||
GetDumpLabel(options), static_cast<uint64_t>(embedded_fetch.loads.size()),
|
||||
rewritten);
|
||||
embedded_fetch = DetectEmbeddedVertexFetch(decoded, vertex, options.user_data_base,
|
||||
options.user_data_count, options.wave_size);
|
||||
if (!embedded_fetch.loads.empty()) {
|
||||
LOGF("%s embedded vertex fetch plan: detected=%" PRIu64 "\n", GetDumpLabel(options),
|
||||
static_cast<uint64_t>(embedded_fetch.loads.size()));
|
||||
}
|
||||
}
|
||||
ir.dispatcher_fallback = dispatcher_fallback;
|
||||
if (!dispatcher_reason.empty()) {
|
||||
ir.fallback_reason = dispatcher_reason;
|
||||
}
|
||||
|
||||
ir.values = std::make_shared<IR::ValueProgram>();
|
||||
if (!Frontend::TranslateProgram(ir, *ir.values, vertex, pixel, compute, error)) {
|
||||
Frontend::TranslateOptions translate_options {
|
||||
.stage = options.stage,
|
||||
.wave_size = options.wave_size,
|
||||
.shader_hash = options.shader_hash,
|
||||
.user_data_base = options.user_data_base,
|
||||
.user_data_count = options.user_data_count,
|
||||
.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;
|
||||
}
|
||||
ir.blocks.clear();
|
||||
IR::RewriteToSsa(ir.values->blocks);
|
||||
IR::ConstantPropagationPass(ir.values->blocks);
|
||||
IR::ResolveControlFlowIdentities(*ir.values);
|
||||
IR::RemoveIdentities(ir.values->blocks);
|
||||
IR::EliminateDeadCode(ir.values->blocks);
|
||||
const auto read_lane_stats = IR::EliminateReadLane(*ir.values, ir.wave_size);
|
||||
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " IR TranslateProgram blocks=%" PRIu64
|
||||
" elapsed_ms=%" PRIu64 "\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash,
|
||||
static_cast<uint64_t>(ir.blocks.size()), phase_ms());
|
||||
IR::RewriteToSsa(ir.blocks);
|
||||
IR::ConstantPropagationPass(ir.blocks);
|
||||
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) {
|
||||
LOGF("%s read-lane elimination: reads=%" PRIu32 "\n", GetDumpLabel(options),
|
||||
read_lane_stats.rewritten_reads);
|
||||
IR::ConstantPropagationPass(ir.values->blocks);
|
||||
IR::ResolveControlFlowIdentities(*ir.values);
|
||||
IR::RemoveIdentities(ir.values->blocks);
|
||||
IR::EliminateDeadCode(ir.values->blocks);
|
||||
IR::ConstantPropagationPass(ir.blocks);
|
||||
IR::ResolveControlFlowIdentities(ir);
|
||||
IR::RemoveIdentities(ir.blocks);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
}
|
||||
if (options.stage == ShaderType::Compute) {
|
||||
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) {
|
||||
LOGF("%s wave64 LDS synchronization: barriers=%" PRIu32 "\n", GetDumpLabel(options),
|
||||
lds_barriers.inserted_barriers);
|
||||
@@ -758,11 +634,11 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
IR::EliminateDeadCode(ir.values->blocks);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
if (!IR::TrackResources(ir, error)) {
|
||||
return false;
|
||||
}
|
||||
IR::EliminateDeadCode(ir.values->blocks);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
if (options.stage == ShaderType::Vertex) {
|
||||
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/spirvEmitterInternal.h"
|
||||
#include "graphics/shader/recompiler/ir/ValueProgram.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -51,22 +51,20 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
Expect(Kind::Samplers, Dense(program.info.samplers.size()));
|
||||
}
|
||||
bool uses_gds = false;
|
||||
if (program.values != nullptr) {
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (IR::SharedAccessOf(inst.GetOpcode()) == IR::SharedAccess::None) {
|
||||
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;
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (IR::SharedAccessOf(inst.GetOpcode()) == IR::SharedAccess::None) {
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
@@ -76,11 +74,10 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
Expect(Kind::AddressMemory, Dense(program.info.addresses.size()));
|
||||
}
|
||||
const bool uses_flattened_runtime =
|
||||
program.values != nullptr &&
|
||||
(!program.values->srt_reads.empty() ||
|
||||
!program.srt_reads.empty() ||
|
||||
std::ranges::any_of(program.info.images, [](const IR::ImageResource& image) {
|
||||
return image.indirect_mapping_capacity != 0u;
|
||||
}));
|
||||
});
|
||||
if (uses_flattened_runtime) {
|
||||
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");
|
||||
}
|
||||
|
||||
if (program.values == nullptr) {
|
||||
return Fail(error, "native shader plan has no typed SSA");
|
||||
}
|
||||
const auto planning_only_handle = [&](const IR::Inst& handle) {
|
||||
return !handle.Uses().empty() &&
|
||||
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;
|
||||
}
|
||||
const auto index = use.user->Flags<IR::MemoryFlags>().index;
|
||||
return index < program.values->memory_info.size() &&
|
||||
program.values->memory_info[index].planning_only;
|
||||
return index < program.memory_info.size() &&
|
||||
program.memory_info[index].planning_only;
|
||||
});
|
||||
};
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
const auto dense = inst.Flags<uint32_t>();
|
||||
switch (inst.GetOpcode()) {
|
||||
@@ -174,7 +168,7 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
case IR::ValueOpcode::ReadConst: {
|
||||
const auto slot = inst.Arg(1).Resolve();
|
||||
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");
|
||||
}
|
||||
break;
|
||||
@@ -191,19 +185,15 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
program.spirv_requirements.reset();
|
||||
IR::SpirvRequirements requirements {};
|
||||
if (program.values == nullptr) {
|
||||
program.spirv_requirements.emplace(requirements);
|
||||
return 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) {
|
||||
if (IR::AddressOpcodeInfoOf(inst.GetOpcode()).access != IR::AddressAccess::None) {
|
||||
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");
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
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");
|
||||
}
|
||||
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.resource >= program.info.buffers.size()) {
|
||||
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());
|
||||
if (shared_access != IR::SharedAccess::None) {
|
||||
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");
|
||||
}
|
||||
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) {
|
||||
return Fail(error, "shared operation has invalid resource kind");
|
||||
}
|
||||
@@ -288,11 +278,11 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
break;
|
||||
case IR::ValueOpcode::SetAttribute: {
|
||||
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");
|
||||
}
|
||||
if (program.stage == ShaderType::Pixel &&
|
||||
program.values->export_info[index].vm) {
|
||||
program.export_info[index].vm) {
|
||||
requirements.pixel_valid_mask = true;
|
||||
}
|
||||
break;
|
||||
@@ -330,12 +320,7 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
if (!ValidateNativeProgram(program, error)) {
|
||||
return false;
|
||||
}
|
||||
if (program.values == nullptr) {
|
||||
SetError(error, "SPIR-V emitter requires planned typed SSA");
|
||||
return false;
|
||||
}
|
||||
const auto& value_program = *program.values;
|
||||
if (!IR::ValidateValueProgram(value_program, true, error)) {
|
||||
if (!IR::ValidateProgram(program, true, error)) {
|
||||
return false;
|
||||
}
|
||||
EmitterState state(program, resources, input_info);
|
||||
@@ -348,7 +333,7 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
AllocateInputVariables(state);
|
||||
AllocateOutputVariables(state);
|
||||
DefineModule(state);
|
||||
if (!EmitValueProgram(state, value_program, error)) {
|
||||
if (!EmitProgram(state, program, error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.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 {
|
||||
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
namespace {
|
||||
|
||||
bool UserDataDwordIndex(const EmitterState& state, IR::Register reg, uint32_t& dword_index) {
|
||||
if (reg.file != IR::RegisterFile::Scalar) {
|
||||
return false;
|
||||
}
|
||||
bool UserDataDwordIndex(const EmitterState& state, IR::ScalarReg reg, uint32_t& dword_index) {
|
||||
const auto register_index = IR::RegIndex(reg);
|
||||
const auto& registers = state.program.bindings.user_data_registers;
|
||||
const auto found = std::lower_bound(registers.begin(), registers.end(), reg.index);
|
||||
if (found == registers.end() || *found != reg.index) {
|
||||
const auto found = std::lower_bound(registers.begin(), registers.end(), register_index);
|
||||
if (found == registers.end() || *found != register_index) {
|
||||
return false;
|
||||
}
|
||||
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: {
|
||||
const auto reg = inst.Arg(0).ScalarRegister();
|
||||
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));
|
||||
} else {
|
||||
ctx.Define(inst, EmitShaderDataDwordLoad(state, dword));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "graphics/shader/recompiler/BufferFormat.h"
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.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/ResourceMaterialization.h"
|
||||
|
||||
@@ -407,7 +407,7 @@ inline void EmitLabel(EmitterState& state, uint32_t label) {
|
||||
}
|
||||
|
||||
struct ValueEmitContext {
|
||||
ValueEmitContext(EmitterState& state_, const IR::ValueProgram& program_)
|
||||
ValueEmitContext(EmitterState& state_, const IR::Program& program_)
|
||||
: state(state_), program(program_) {}
|
||||
|
||||
uint32_t Def(IR::Value value);
|
||||
@@ -425,7 +425,7 @@ struct ValueEmitContext {
|
||||
void Fail(const IR::Inst& inst, const char* reason);
|
||||
|
||||
EmitterState& state;
|
||||
const IR::ValueProgram& program;
|
||||
const IR::Program& program;
|
||||
std::unordered_map<const IR::Inst*, uint32_t> definitions;
|
||||
std::unordered_map<const IR::Block*, uint32_t> labels;
|
||||
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 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.
|
||||
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(
|
||||
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()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ void EmitReturn(ValueEmitContext& ctx) {
|
||||
}
|
||||
|
||||
void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block,
|
||||
const IR::ValueBlockInfo& info) {
|
||||
const IR::BlockInfo& info) {
|
||||
const auto& term = info.terminator;
|
||||
const auto emit_merge = [&]() {
|
||||
if (term.loop_header) {
|
||||
@@ -150,7 +150,7 @@ void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState&
|
||||
}
|
||||
|
||||
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;
|
||||
switch (term.kind) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
std::optional<DispatcherFunctionState> dispatcher;
|
||||
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
|
||||
// physical block. Keep that header as a dedicated control node, exactly like
|
||||
// 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.
|
||||
if (!IsolateSemanticLoopHeader(graph, 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);
|
||||
if (inst.opcode == Opcode::DS_READ_U16_D16) {
|
||||
// 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;
|
||||
}
|
||||
DecodeVectorGpr(addr, inst.src0);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace {
|
||||
|
||||
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.
|
||||
enum class Vop2SdwaProfile {
|
||||
None,
|
||||
|
||||
@@ -1,42 +1,104 @@
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail {
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
IR::ExportFlags Translator::AddExportInfo(const IR::Instruction& inst) {
|
||||
const auto index = static_cast<uint32_t>(value_program.export_info.size());
|
||||
value_program.export_info.push_back(inst.export_info);
|
||||
namespace {
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
bool Translator::TranslateAttributeOperation(const IR::Instruction& inst) {
|
||||
if (inst.op == IR::Opcode::LoadInputF32) {
|
||||
for (uint32_t component = 0; component < inst.input_info.component_count; component++) {
|
||||
const auto chan = inst.input_info.chan + component;
|
||||
const auto value = inst.input_info.interpolation_mode < 3u
|
||||
? ir.Emit(IR::ValueOpcode::GetInterpolationParameter,
|
||||
{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;
|
||||
bool Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
uint32_t component_count) {
|
||||
for (uint32_t component = 0; component < component_count; component++) {
|
||||
const auto value =
|
||||
ir.Emit(IR::ValueOpcode::GetAttribute, {IR::Value(attribute), IR::Value(component)});
|
||||
WriteOperand(OffsetOperand(inst.dst, component), value);
|
||||
}
|
||||
if (inst.op == IR::Opcode::Export) {
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // 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"
|
||||
|
||||
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::Type type = IR::Type::U32;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::CompareFalse:
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_CMP_F_I32:
|
||||
case Decoder::Opcode::V_CMP_F_U32:
|
||||
WriteCompareResult(inst.dst, IR::U1(IR::Value(false)));
|
||||
return true;
|
||||
case 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)));
|
||||
return true;
|
||||
case IR::Opcode::CompareEqU32:
|
||||
case IR::Opcode::CompareMaskEqU32:
|
||||
case IR::Opcode::CompareEqI32:
|
||||
case IR::Opcode::CompareMaskEqI32: opcode = IR::ValueOpcode::IEqual32; break;
|
||||
case IR::Opcode::CompareNeU32:
|
||||
case IR::Opcode::CompareMaskNeU32:
|
||||
case IR::Opcode::CompareNeI32:
|
||||
case IR::Opcode::CompareMaskNeI32: opcode = IR::ValueOpcode::INotEqual32; break;
|
||||
case IR::Opcode::CompareGtU32:
|
||||
case IR::Opcode::CompareMaskGtU32: opcode = IR::ValueOpcode::UGreaterThan32; break;
|
||||
case IR::Opcode::CompareGeU32:
|
||||
case IR::Opcode::CompareMaskGeU32: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
|
||||
case IR::Opcode::CompareLtU32:
|
||||
case IR::Opcode::CompareMaskLtU32: opcode = IR::ValueOpcode::ULessThan32; break;
|
||||
case IR::Opcode::CompareLeU32:
|
||||
case IR::Opcode::CompareMaskLeU32: opcode = IR::ValueOpcode::ULessThanEqual32; break;
|
||||
case IR::Opcode::CompareGtI32:
|
||||
case IR::Opcode::CompareMaskGtI32: opcode = IR::ValueOpcode::SGreaterThan32; break;
|
||||
case IR::Opcode::CompareGeI32:
|
||||
case IR::Opcode::CompareMaskGeI32: opcode = IR::ValueOpcode::SGreaterThanEqual32; break;
|
||||
case IR::Opcode::CompareLtI32:
|
||||
case IR::Opcode::CompareMaskLtI32: opcode = IR::ValueOpcode::SLessThan32; break;
|
||||
case IR::Opcode::CompareLeI32:
|
||||
case IR::Opcode::CompareMaskLeI32: opcode = IR::ValueOpcode::SLessThanEqual32; break;
|
||||
case IR::Opcode::CompareEqU64:
|
||||
case Decoder::Opcode::S_CMP_EQ_U32:
|
||||
case Decoder::Opcode::V_CMP_EQ_U32:
|
||||
case Decoder::Opcode::V_CMPX_EQ_U32:
|
||||
case Decoder::Opcode::S_CMP_EQ_I32:
|
||||
case Decoder::Opcode::V_CMP_EQ_I32:
|
||||
case Decoder::Opcode::V_CMPX_EQ_I32: opcode = IR::ValueOpcode::IEqual32; break;
|
||||
case Decoder::Opcode::S_CMP_LG_U32:
|
||||
case Decoder::Opcode::V_CMP_NE_U32:
|
||||
case Decoder::Opcode::V_CMPX_NE_U32:
|
||||
case Decoder::Opcode::S_CMP_LG_I32:
|
||||
case Decoder::Opcode::V_CMP_NE_I32:
|
||||
case Decoder::Opcode::V_CMPX_NE_I32: opcode = IR::ValueOpcode::INotEqual32; break;
|
||||
case Decoder::Opcode::S_CMP_GT_U32:
|
||||
case Decoder::Opcode::V_CMP_GT_U32:
|
||||
case Decoder::Opcode::V_CMPX_GT_U32: opcode = IR::ValueOpcode::UGreaterThan32; break;
|
||||
case Decoder::Opcode::S_CMP_GE_U32:
|
||||
case Decoder::Opcode::V_CMP_GE_U32:
|
||||
case Decoder::Opcode::V_CMPX_GE_U32: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
|
||||
case Decoder::Opcode::S_CMP_LT_U32:
|
||||
case Decoder::Opcode::V_CMP_LT_U32:
|
||||
case Decoder::Opcode::V_CMPX_LT_U32: opcode = IR::ValueOpcode::ULessThan32; break;
|
||||
case Decoder::Opcode::S_CMP_LE_U32:
|
||||
case Decoder::Opcode::V_CMP_LE_U32:
|
||||
case Decoder::Opcode::V_CMPX_LE_U32: opcode = IR::ValueOpcode::ULessThanEqual32; break;
|
||||
case Decoder::Opcode::S_CMP_GT_I32:
|
||||
case Decoder::Opcode::V_CMP_GT_I32:
|
||||
case Decoder::Opcode::V_CMPX_GT_I32: opcode = IR::ValueOpcode::SGreaterThan32; break;
|
||||
case Decoder::Opcode::S_CMP_GE_I32:
|
||||
case Decoder::Opcode::V_CMP_GE_I32:
|
||||
case Decoder::Opcode::V_CMPX_GE_I32: opcode = IR::ValueOpcode::SGreaterThanEqual32; break;
|
||||
case Decoder::Opcode::S_CMP_LT_I32:
|
||||
case Decoder::Opcode::V_CMP_LT_I32:
|
||||
case Decoder::Opcode::V_CMPX_LT_I32: opcode = IR::ValueOpcode::SLessThan32; break;
|
||||
case Decoder::Opcode::S_CMP_LE_I32:
|
||||
case Decoder::Opcode::V_CMP_LE_I32:
|
||||
case Decoder::Opcode::V_CMPX_LE_I32: opcode = IR::ValueOpcode::SLessThanEqual32; break;
|
||||
case Decoder::Opcode::S_CMP_EQ_U64:
|
||||
case Decoder::Opcode::V_CMP_EQ_I64:
|
||||
opcode = IR::ValueOpcode::IEqual64;
|
||||
type = IR::Type::U64;
|
||||
break;
|
||||
case IR::Opcode::CompareGtU64:
|
||||
case Decoder::Opcode::V_CMP_GT_U64:
|
||||
opcode = IR::ValueOpcode::UGreaterThan64;
|
||||
type = IR::Type::U64;
|
||||
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;
|
||||
type = IR::Type::U64;
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
const auto lhs = ReadOperand(inst.src[0], type);
|
||||
const auto rhs = ReadOperand(inst.src[1], type);
|
||||
const auto lhs = ReadOperand(inst.src0, type);
|
||||
const auto rhs = ReadOperand(inst.src1, type);
|
||||
WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {lhs, rhs})));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::TranslateInteger16Compare(const IR::Instruction& inst) {
|
||||
bool Translator::TranslateInteger16Compare(const Decoder::Instruction& inst) {
|
||||
IR::ValueOpcode opcode {};
|
||||
bool signed_value = false;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::CompareEqU16:
|
||||
case IR::Opcode::CompareEqI16: opcode = IR::ValueOpcode::IEqual32; break;
|
||||
case IR::Opcode::CompareNeU16:
|
||||
case IR::Opcode::CompareNeI16: opcode = IR::ValueOpcode::INotEqual32; break;
|
||||
case IR::Opcode::CompareGtU16: opcode = IR::ValueOpcode::UGreaterThan32; break;
|
||||
case IR::Opcode::CompareGeU16: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
|
||||
case IR::Opcode::CompareLtU16: opcode = IR::ValueOpcode::ULessThan32; break;
|
||||
case IR::Opcode::CompareLeU16: opcode = IR::ValueOpcode::ULessThanEqual32; break;
|
||||
case IR::Opcode::CompareGtI16:
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_CMP_EQ_U16:
|
||||
case Decoder::Opcode::V_CMP_EQ_I16: opcode = IR::ValueOpcode::IEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_NE_U16:
|
||||
case Decoder::Opcode::V_CMP_NE_I16: opcode = IR::ValueOpcode::INotEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_GT_U16: opcode = IR::ValueOpcode::UGreaterThan32; break;
|
||||
case Decoder::Opcode::V_CMP_GE_U16: opcode = IR::ValueOpcode::UGreaterThanEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_LT_U16: opcode = IR::ValueOpcode::ULessThan32; break;
|
||||
case Decoder::Opcode::V_CMP_LE_U16: opcode = IR::ValueOpcode::ULessThanEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_GT_I16:
|
||||
opcode = IR::ValueOpcode::SGreaterThan32;
|
||||
signed_value = true;
|
||||
break;
|
||||
case IR::Opcode::CompareGeI16:
|
||||
case Decoder::Opcode::V_CMP_GE_I16:
|
||||
opcode = IR::ValueOpcode::SGreaterThanEqual32;
|
||||
signed_value = true;
|
||||
break;
|
||||
case IR::Opcode::CompareLtI16:
|
||||
case Decoder::Opcode::V_CMP_LT_I16:
|
||||
opcode = IR::ValueOpcode::SLessThan32;
|
||||
signed_value = true;
|
||||
break;
|
||||
case IR::Opcode::CompareLeI16:
|
||||
case Decoder::Opcode::V_CMP_LE_I16:
|
||||
opcode = IR::ValueOpcode::SLessThanEqual32;
|
||||
signed_value = true;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
WriteCompareResult(inst.dst,
|
||||
IR::U1(ir.Emit(opcode, {ReadU16AsU32(inst.src[0], signed_value),
|
||||
ReadU16AsU32(inst.src[1], signed_value)})));
|
||||
WriteCompareResult(inst.dst, IR::U1(ir.Emit(opcode, {ReadU16AsU32(inst.src0, signed_value),
|
||||
ReadU16AsU32(inst.src1, signed_value)})));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::TranslateFloatCompare(const IR::Instruction& inst) {
|
||||
bool Translator::TranslateFloatCompare(const Decoder::Instruction& inst) {
|
||||
IR::ValueOpcode opcode {};
|
||||
bool half = false;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::CompareEqF32:
|
||||
case IR::Opcode::CompareMaskEqF32: opcode = IR::ValueOpcode::FPOrdEqual32; break;
|
||||
case IR::Opcode::CompareNeF32:
|
||||
case IR::Opcode::CompareMaskNeF32: opcode = IR::ValueOpcode::FPOrdNotEqual32; break;
|
||||
case IR::Opcode::CompareGtF32:
|
||||
case IR::Opcode::CompareMaskGtF32: opcode = IR::ValueOpcode::FPOrdGreaterThan32; break;
|
||||
case IR::Opcode::CompareGeF32:
|
||||
case IR::Opcode::CompareMaskGeF32: opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32; break;
|
||||
case IR::Opcode::CompareLtF32:
|
||||
case IR::Opcode::CompareMaskLtF32: opcode = IR::ValueOpcode::FPOrdLessThan32; break;
|
||||
case IR::Opcode::CompareLeF32:
|
||||
case IR::Opcode::CompareMaskLeF32: opcode = IR::ValueOpcode::FPOrdLessThanEqual32; break;
|
||||
case IR::Opcode::CompareUnordEqF32:
|
||||
case IR::Opcode::CompareMaskUnordEqF32: opcode = IR::ValueOpcode::FPUnordEqual32; break;
|
||||
case IR::Opcode::CompareUnordNeF32:
|
||||
case IR::Opcode::CompareMaskUnordNeF32: opcode = IR::ValueOpcode::FPUnordNotEqual32; break;
|
||||
case IR::Opcode::CompareUnordGtF32:
|
||||
case IR::Opcode::CompareMaskUnordGtF32:
|
||||
opcode = IR::ValueOpcode::FPUnordGreaterThan32;
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_CMP_F_F32:
|
||||
WriteCompareResult(inst.dst, IR::U1(IR::Value(false)));
|
||||
return true;
|
||||
case Decoder::Opcode::V_CMP_TRU_F32:
|
||||
WriteCompareResult(inst.dst, IR::U1(IR::Value(true)));
|
||||
return true;
|
||||
case Decoder::Opcode::V_CMP_EQ_F32:
|
||||
case Decoder::Opcode::V_CMPX_EQ_F32: opcode = IR::ValueOpcode::FPOrdEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_LG_F32:
|
||||
case Decoder::Opcode::V_CMPX_LG_F32: opcode = IR::ValueOpcode::FPOrdNotEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_GT_F32:
|
||||
case Decoder::Opcode::V_CMPX_GT_F32: opcode = IR::ValueOpcode::FPOrdGreaterThan32; break;
|
||||
case Decoder::Opcode::V_CMP_GE_F32:
|
||||
case Decoder::Opcode::V_CMPX_GE_F32:
|
||||
opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32;
|
||||
break;
|
||||
case IR::Opcode::CompareUnordGeF32:
|
||||
case IR::Opcode::CompareMaskUnordGeF32:
|
||||
case Decoder::Opcode::V_CMP_LT_F32:
|
||||
case Decoder::Opcode::V_CMPX_LT_F32: opcode = IR::ValueOpcode::FPOrdLessThan32; break;
|
||||
case Decoder::Opcode::V_CMP_LE_F32:
|
||||
case Decoder::Opcode::V_CMPX_LE_F32: opcode = IR::ValueOpcode::FPOrdLessThanEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_NLG_F32:
|
||||
case Decoder::Opcode::V_CMPX_NLG_F32: opcode = IR::ValueOpcode::FPUnordEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_NEQ_F32:
|
||||
case Decoder::Opcode::V_CMPX_NEQ_F32: opcode = IR::ValueOpcode::FPUnordNotEqual32; break;
|
||||
case Decoder::Opcode::V_CMP_NLE_F32:
|
||||
case Decoder::Opcode::V_CMPX_NLE_F32: opcode = IR::ValueOpcode::FPUnordGreaterThan32; break;
|
||||
case Decoder::Opcode::V_CMP_NLT_F32:
|
||||
case Decoder::Opcode::V_CMPX_NLT_F32:
|
||||
opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32;
|
||||
break;
|
||||
case IR::Opcode::CompareUnordLtF32:
|
||||
case IR::Opcode::CompareMaskUnordLtF32: opcode = IR::ValueOpcode::FPUnordLessThan32; break;
|
||||
case IR::Opcode::CompareUnordLeF32:
|
||||
case IR::Opcode::CompareMaskUnordLeF32:
|
||||
case Decoder::Opcode::V_CMP_NGE_F32:
|
||||
case Decoder::Opcode::V_CMPX_NGE_F32: opcode = IR::ValueOpcode::FPUnordLessThan32; break;
|
||||
case Decoder::Opcode::V_CMP_NGT_F32:
|
||||
case Decoder::Opcode::V_CMPX_NGT_F32:
|
||||
opcode = IR::ValueOpcode::FPUnordLessThanEqual32;
|
||||
break;
|
||||
case IR::Opcode::CompareEqF16:
|
||||
case IR::Opcode::CompareMaskEqF16:
|
||||
case Decoder::Opcode::V_CMP_EQ_F16:
|
||||
case Decoder::Opcode::V_CMPX_EQ_F16:
|
||||
opcode = IR::ValueOpcode::FPOrdEqual32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareNeF16:
|
||||
case IR::Opcode::CompareMaskNeF16:
|
||||
case Decoder::Opcode::V_CMP_LG_F16:
|
||||
opcode = IR::ValueOpcode::FPOrdNotEqual32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareGtF16:
|
||||
case IR::Opcode::CompareMaskGtF16:
|
||||
case Decoder::Opcode::V_CMP_GT_F16:
|
||||
case Decoder::Opcode::V_CMPX_GT_F16:
|
||||
opcode = IR::ValueOpcode::FPOrdGreaterThan32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareGeF16:
|
||||
case IR::Opcode::CompareMaskGeF16:
|
||||
case Decoder::Opcode::V_CMP_GE_F16:
|
||||
case Decoder::Opcode::V_CMPX_GE_F16:
|
||||
opcode = IR::ValueOpcode::FPOrdGreaterThanEqual32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareLtF16:
|
||||
case IR::Opcode::CompareMaskLtF16:
|
||||
case Decoder::Opcode::V_CMP_LT_F16:
|
||||
case Decoder::Opcode::V_CMPX_LT_F16:
|
||||
opcode = IR::ValueOpcode::FPOrdLessThan32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareLeF16:
|
||||
case IR::Opcode::CompareMaskLeF16:
|
||||
case Decoder::Opcode::V_CMP_LE_F16:
|
||||
case Decoder::Opcode::V_CMPX_LE_F16:
|
||||
opcode = IR::ValueOpcode::FPOrdLessThanEqual32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareUnordNeF16:
|
||||
case IR::Opcode::CompareMaskUnordNeF16:
|
||||
case Decoder::Opcode::V_CMP_NEQ_F16:
|
||||
case Decoder::Opcode::V_CMPX_NEQ_F16:
|
||||
opcode = IR::ValueOpcode::FPUnordNotEqual32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareMaskUnordGeF16:
|
||||
case Decoder::Opcode::V_CMPX_NLT_F16:
|
||||
opcode = IR::ValueOpcode::FPUnordGreaterThanEqual32;
|
||||
half = true;
|
||||
break;
|
||||
case IR::Opcode::CompareOrderedF32:
|
||||
case IR::Opcode::CompareUnorderedF32: {
|
||||
const auto lhs = IR::F32(ReadOperand(inst.src[0], IR::Type::F32));
|
||||
const auto rhs = IR::F32(ReadOperand(inst.src[1], IR::Type::F32));
|
||||
case Decoder::Opcode::V_CMP_O_F32:
|
||||
case Decoder::Opcode::V_CMP_U_F32: {
|
||||
const auto lhs = IR::F32(ReadOperand(inst.src0, IR::Type::F32));
|
||||
const auto rhs = IR::F32(ReadOperand(inst.src1, IR::Type::F32));
|
||||
const auto unordered = ir.LogicalOr(IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {lhs})),
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {rhs})));
|
||||
const auto result =
|
||||
inst.op == IR::Opcode::CompareUnorderedF32 ? unordered : ir.LogicalNot(unordered);
|
||||
inst.opcode == Decoder::Opcode::V_CMP_U_F32 ? unordered : ir.LogicalNot(unordered);
|
||||
WriteCompareResult(inst.dst, result);
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::CompareClassF32: {
|
||||
const auto value = ReadOperand(inst.src[0], IR::Type::F32);
|
||||
const auto mask = ReadOperand(inst.src[1], IR::Type::U32);
|
||||
case Decoder::Opcode::V_CMP_CLASS_F32: {
|
||||
const auto value = ReadOperand(inst.src0, IR::Type::F32);
|
||||
const auto mask = ReadOperand(inst.src1, IR::Type::U32);
|
||||
WriteCompareResult(inst.dst,
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask})));
|
||||
return true;
|
||||
@@ -189,11 +212,11 @@ bool Translator::TranslateFloatCompare(const IR::Instruction& inst) {
|
||||
default: return false;
|
||||
}
|
||||
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 =
|
||||
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})));
|
||||
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"
|
||||
|
||||
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));
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::SaveexecB32:
|
||||
case IR::Opcode::SaveexecB64: {
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_AND_SAVEEXEC_B32:
|
||||
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B32:
|
||||
case Decoder::Opcode::S_AND_SAVEEXEC_B64:
|
||||
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B64:
|
||||
case Decoder::Opcode::S_ORN2_SAVEEXEC_B64: {
|
||||
const auto old = ir.GetExec();
|
||||
const auto src = ReadMask(inst.src[0]);
|
||||
const auto src = ReadMask(inst.src0);
|
||||
IR::U1 result;
|
||||
switch (inst.saveexec_mode) {
|
||||
case IR::SaveexecMode::And: result = ir.LogicalAnd(old, src); break;
|
||||
case IR::SaveexecMode::Andn1:
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_AND_SAVEEXEC_B32:
|
||||
case Decoder::Opcode::S_AND_SAVEEXEC_B64: result = ir.LogicalAnd(old, src); break;
|
||||
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B32:
|
||||
case Decoder::Opcode::S_ANDN1_SAVEEXEC_B64:
|
||||
result = ir.LogicalAnd(old, ir.LogicalNot(src));
|
||||
break;
|
||||
case 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);
|
||||
} else {
|
||||
WriteMask(inst.dst, old);
|
||||
@@ -29,63 +58,75 @@ bool Translator::TranslateStateOperation(const IR::Instruction& inst) {
|
||||
ir.SetScc(result);
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ScalarAddCarryU32:
|
||||
case IR::Opcode::IAddCarryU32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
const auto carry_in = ConditionBit(inst.src[2]);
|
||||
case Decoder::Opcode::S_ADD_U32:
|
||||
case Decoder::Opcode::S_ADDC_U32:
|
||||
case Decoder::Opcode::V_ADD_I32:
|
||||
case Decoder::Opcode::V_ADDC_U32: {
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
const auto carry_in = inst.opcode == Decoder::Opcode::S_ADDC_U32
|
||||
? ConditionBit(ConditionOperand(Decoder::OperandKind::Scc))
|
||||
: inst.opcode == Decoder::Opcode::V_ADDC_U32
|
||||
? ConditionBit(inst.src2)
|
||||
: zero;
|
||||
const auto add0 = ir.Emit(IR::ValueOpcode::IAddCarry32, {lhs, rhs});
|
||||
const auto partial = ir.CompositeExtract(add0, 0);
|
||||
const auto carry0 = ir.CompositeExtract(add0, 1);
|
||||
const auto add1 = ir.Emit(IR::ValueOpcode::IAddCarry32, {partial, carry_in});
|
||||
const auto result = ir.CompositeExtract(add1, 0);
|
||||
const auto carry1 = ir.CompositeExtract(add1, 1);
|
||||
const auto carry = ir.INotEqual(ir.BitwiseOr(carry0, carry1), IR::U32(IR::Value(0u)));
|
||||
WriteOperand(inst.dst, result);
|
||||
if (inst.op == IR::Opcode::ScalarAddCarryU32) {
|
||||
const auto carry = ir.INotEqual(ir.BitwiseOr(carry0, carry1), zero);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
if (inst.opcode == Decoder::Opcode::S_ADD_U32 ||
|
||||
inst.opcode == Decoder::Opcode::S_ADDC_U32) {
|
||||
ir.SetScc(carry);
|
||||
} else {
|
||||
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), carry));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ScalarSubBorrowU32:
|
||||
case IR::Opcode::ISubBorrowU32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
const auto result = ir.ISub(lhs, rhs);
|
||||
const auto borrow = ir.UGreaterThan(rhs, lhs);
|
||||
WriteOperand(inst.dst, result);
|
||||
if (inst.op == IR::Opcode::ScalarSubBorrowU32) {
|
||||
case Decoder::Opcode::S_SUB_U32:
|
||||
case Decoder::Opcode::V_SUB_I32:
|
||||
case Decoder::Opcode::V_SUBREV_I32: {
|
||||
const bool reverse = inst.opcode == Decoder::Opcode::V_SUBREV_I32;
|
||||
const auto lhs = ReadU32(reverse ? inst.src1 : inst.src0);
|
||||
const auto rhs = ReadU32(reverse ? inst.src0 : inst.src1);
|
||||
const auto result = ir.ISub(lhs, rhs);
|
||||
const auto borrow = ir.UGreaterThan(rhs, lhs);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
if (inst.opcode == Decoder::Opcode::S_SUB_U32) {
|
||||
ir.SetScc(borrow);
|
||||
} else {
|
||||
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ScalarSubBorrowCarryU32:
|
||||
case IR::Opcode::ISubBorrowCarryU32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
const auto borrow_in = ConditionBit(inst.src[2]);
|
||||
case Decoder::Opcode::S_SUBB_U32:
|
||||
case Decoder::Opcode::V_SUBREV_CO_CI_U32: {
|
||||
const bool vector = inst.opcode == Decoder::Opcode::V_SUBREV_CO_CI_U32;
|
||||
const auto lhs = ReadU32(vector ? inst.src1 : inst.src0);
|
||||
const auto rhs = ReadU32(vector ? inst.src0 : inst.src1);
|
||||
const auto borrow_in = vector
|
||||
? ConditionBit(inst.src2)
|
||||
: ConditionBit(ConditionOperand(Decoder::OperandKind::Scc));
|
||||
const auto partial = ir.ISub(lhs, rhs);
|
||||
const auto result = ir.ISub(partial, borrow_in);
|
||||
const auto borrow0 = ir.UGreaterThan(rhs, lhs);
|
||||
const auto borrow1 = ir.UGreaterThan(borrow_in, partial);
|
||||
WriteOperand(inst.dst, result);
|
||||
const auto borrow = ir.LogicalOr(borrow0, borrow1);
|
||||
if (inst.op == IR::Opcode::ScalarSubBorrowCarryU32) {
|
||||
ir.SetScc(borrow);
|
||||
} else {
|
||||
const auto borrow = ir.LogicalOr(borrow0, borrow1);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
if (vector) {
|
||||
WriteMask(inst.dst2, ir.LogicalAnd(ir.GetExec(), borrow));
|
||||
} else {
|
||||
ir.SetScc(borrow);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ScalarSignedAddOverflowI32:
|
||||
case IR::Opcode::ScalarSignedSubOverflowI32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
const bool subtract = inst.op == IR::Opcode::ScalarSignedSubOverflowI32;
|
||||
case Decoder::Opcode::S_ADD_I32:
|
||||
case Decoder::Opcode::S_SUB_I32: {
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
const bool subtract = inst.opcode == Decoder::Opcode::S_SUB_I32;
|
||||
const auto result = subtract ? ir.ISub(lhs, rhs) : ir.IAdd(lhs, rhs);
|
||||
const auto shift = IR::U32(IR::Value(31u));
|
||||
const auto lhs_sign = ir.ShiftRightLogical(lhs, shift);
|
||||
@@ -94,116 +135,201 @@ bool Translator::TranslateStateOperation(const IR::Instruction& inst) {
|
||||
const auto inputs =
|
||||
subtract ? ir.INotEqual(lhs_sign, rhs_sign) : ir.IEqual(lhs_sign, rhs_sign);
|
||||
const auto changed = ir.INotEqual(lhs_sign, out_sign);
|
||||
WriteOperand(inst.dst, result);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
ir.SetScc(ir.LogicalAnd(inputs, changed));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ScalarShiftLeftAddCarryU32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto shift = ReadU32(inst.src[1]);
|
||||
const auto rhs = ReadU32(inst.src[2]);
|
||||
const auto shifted = ir.ShiftLeftLogical(lhs, shift);
|
||||
const auto result = ir.IAdd(shifted, rhs);
|
||||
const auto add_carry = ir.ULessThan(result, shifted);
|
||||
const auto inverse_shift = ir.ISub(IR::U32(IR::Value(32u)), shift);
|
||||
const auto shifted_out = ir.ShiftRightLogical(lhs, inverse_shift);
|
||||
const auto shift_carry = ir.INotEqual(shifted_out, zero);
|
||||
WriteOperand(inst.dst, result);
|
||||
case Decoder::Opcode::S_LSHL1_ADD_U32:
|
||||
case Decoder::Opcode::S_LSHL2_ADD_U32:
|
||||
case Decoder::Opcode::S_LSHL3_ADD_U32:
|
||||
case Decoder::Opcode::S_LSHL4_ADD_U32: {
|
||||
const uint32_t shift_amount = 1u + static_cast<uint32_t>(inst.opcode) -
|
||||
static_cast<uint32_t>(Decoder::Opcode::S_LSHL1_ADD_U32);
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto shift = IR::U32(IR::Value(shift_amount));
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
const auto shifted = ir.ShiftLeftLogical(lhs, shift);
|
||||
const auto result = ir.IAdd(shifted, rhs);
|
||||
const auto add_carry = ir.ULessThan(result, shifted);
|
||||
const auto inverse_shift = ir.ISub(IR::U32(IR::Value(32u)), shift);
|
||||
const auto shifted_out = ir.ShiftRightLogical(lhs, inverse_shift);
|
||||
const auto shift_carry = ir.INotEqual(shifted_out, zero);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
ir.SetScc(ir.LogicalOr(add_carry, shift_carry));
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Translator::TranslateControlOperation(const IR::Instruction& inst) {
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::ControlNop: ir.Emit(IR::ValueOpcode::ControlNop); return true;
|
||||
case IR::Opcode::Waitcnt: ir.Emit(IR::ValueOpcode::Waitcnt); return true;
|
||||
case IR::Opcode::Barrier: ir.Emit(IR::ValueOpcode::Barrier); return true;
|
||||
case IR::Opcode::Sendmsg: ir.Emit(IR::ValueOpcode::Sendmsg); return true;
|
||||
case IR::Opcode::TtraceData: ir.Emit(IR::ValueOpcode::TtraceData); return true;
|
||||
case IR::Opcode::InstPrefetch: ir.Emit(IR::ValueOpcode::InstPrefetch); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Translator::TranslateMove(const IR::Instruction& inst) {
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::MoveU32:
|
||||
if (inst.src[0].kind == IR::OperandKind::Register &&
|
||||
(inst.src[0].reg.file == IR::RegisterFile::Exec ||
|
||||
inst.src[0].reg.file == IR::RegisterFile::Vcc) &&
|
||||
inst.dst.kind == IR::OperandKind::Register &&
|
||||
(inst.dst.reg.file == IR::RegisterFile::Exec ||
|
||||
inst.dst.reg.file == IR::RegisterFile::Vcc)) {
|
||||
WriteMask(inst.dst, ReadMask(inst.src[0]));
|
||||
} else {
|
||||
WriteOperand(inst.dst, ReadOperand(inst.src[0], IR::Type::U32));
|
||||
case Decoder::Opcode::S_MIN_I32:
|
||||
case Decoder::Opcode::S_MAX_I32:
|
||||
case Decoder::Opcode::S_MIN_U32:
|
||||
case Decoder::Opcode::S_MAX_U32: {
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
IR::ValueOpcode value_opcode;
|
||||
IR::ValueOpcode compare_opcode;
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_MIN_I32:
|
||||
value_opcode = IR::ValueOpcode::SMin32;
|
||||
compare_opcode = IR::ValueOpcode::SLessThan32;
|
||||
break;
|
||||
case Decoder::Opcode::S_MAX_I32:
|
||||
value_opcode = IR::ValueOpcode::SMax32;
|
||||
compare_opcode = IR::ValueOpcode::SGreaterThan32;
|
||||
break;
|
||||
case Decoder::Opcode::S_MIN_U32:
|
||||
value_opcode = IR::ValueOpcode::UMin32;
|
||||
compare_opcode = IR::ValueOpcode::ULessThan32;
|
||||
break;
|
||||
case Decoder::Opcode::S_MAX_U32:
|
||||
value_opcode = IR::ValueOpcode::UMax32;
|
||||
compare_opcode = IR::ValueOpcode::UGreaterThan32;
|
||||
break;
|
||||
default: EXIT("invalid scalar min/max opcode");
|
||||
}
|
||||
WriteOperand(DestinationOperand(inst), ir.Emit(value_opcode, {lhs, rhs}));
|
||||
ir.SetScc(IR::U1(ir.Emit(compare_opcode, {lhs, rhs})));
|
||||
return true;
|
||||
case IR::Opcode::MoveF32Bits:
|
||||
WriteOperand(inst.dst, ReadOperand(inst.src[0], IR::Type::F32));
|
||||
return true;
|
||||
case IR::Opcode::MoveU64: {
|
||||
const auto is_exec_or_vcc = [](const IR::Operand& operand) {
|
||||
return operand.kind == IR::OperandKind::Register &&
|
||||
(operand.reg.file == IR::RegisterFile::Exec ||
|
||||
operand.reg.file == IR::RegisterFile::Vcc);
|
||||
};
|
||||
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src[0])) {
|
||||
WriteMask64(inst.dst, ReadMask(inst.src[0]));
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Translator::TranslateControlOperation(const Decoder::Instruction& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_NOP: return true;
|
||||
case Decoder::Opcode::S_NOP:
|
||||
case Decoder::Opcode::S_SETREG_B32:
|
||||
case Decoder::Opcode::S_SLEEP:
|
||||
case Decoder::Opcode::S_TRAP: ir.Emit(IR::ValueOpcode::ControlNop); return true;
|
||||
case Decoder::Opcode::S_WAITCNT:
|
||||
case Decoder::Opcode::S_WAITCNT_DEPCTR: ir.Emit(IR::ValueOpcode::Waitcnt); return true;
|
||||
case Decoder::Opcode::S_BARRIER: ir.Emit(IR::ValueOpcode::Barrier); return true;
|
||||
case Decoder::Opcode::S_SENDMSG: ir.Emit(IR::ValueOpcode::Sendmsg); return true;
|
||||
case Decoder::Opcode::S_TTRACEDATA: ir.Emit(IR::ValueOpcode::TtraceData); return true;
|
||||
case Decoder::Opcode::S_INST_PREFETCH: ir.Emit(IR::ValueOpcode::InstPrefetch); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
const bool scalar_copy = inst.dst.kind == IR::OperandKind::Register &&
|
||||
inst.src[0].kind == IR::OperandKind::Register &&
|
||||
inst.dst.reg.file == IR::RegisterFile::Scalar &&
|
||||
inst.src[0].reg.file == IR::RegisterFile::Scalar;
|
||||
IR::U1 source_mask;
|
||||
IR::U1 source_mask_valid;
|
||||
if (scalar_copy) {
|
||||
const auto src = static_cast<IR::ScalarReg>(inst.src[0].reg.index);
|
||||
source_mask = ir.GetThreadBitScalarReg(src);
|
||||
source_mask_valid = ir.GetScalarMaskTag(src);
|
||||
auto high = PlainOperand(inst.dst);
|
||||
switch (inst.dst.kind) {
|
||||
case Decoder::OperandKind::Sgpr:
|
||||
if (inst.dst.reg < 105u) {
|
||||
high.reg++;
|
||||
} else if (inst.dst.reg == 105u) {
|
||||
high.kind = Decoder::OperandKind::VccLo;
|
||||
high.reg = 0u;
|
||||
} else {
|
||||
if (error != nullptr) {
|
||||
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Decoder::OperandKind::VccLo: high.kind = Decoder::OperandKind::VccHi; break;
|
||||
case Decoder::OperandKind::M0: high.kind = Decoder::OperandKind::Null; break;
|
||||
case Decoder::OperandKind::ExecLo: high.kind = Decoder::OperandKind::ExecHi; break;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
WriteU32Pair(inst.dst, ReadU32Pair(inst.src[0]));
|
||||
if (scalar_copy) {
|
||||
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg.index),
|
||||
source_mask);
|
||||
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg.index),
|
||||
source_mask_valid);
|
||||
const auto words = ExtractU64(address);
|
||||
WriteOperand(inst.dst, words[0]);
|
||||
WriteOperand(high, words[1]);
|
||||
return true;
|
||||
}
|
||||
case Decoder::Opcode::S_CSELECT_B32: {
|
||||
const auto result = ir.Select(ir.GetScc(), ReadU32(inst.src0), ReadU32(inst.src1));
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
return true;
|
||||
}
|
||||
case Decoder::Opcode::S_CSELECT_B64: {
|
||||
const auto condition = ir.GetScc();
|
||||
const auto lhs = ReadU32Pair(inst.src0);
|
||||
const auto rhs = ReadU32Pair(inst.src1);
|
||||
const auto selected_mask = IR::U1(ir.Emit(
|
||||
IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(inst.src1)}));
|
||||
const auto selected_mask_valid =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
|
||||
{condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)}));
|
||||
if (IsExecOrVcc(inst.dst)) {
|
||||
WriteMask64(inst.dst, selected_mask);
|
||||
return true;
|
||||
}
|
||||
WriteU32Pair(inst.dst, {ir.Select(condition, lhs[0], rhs[0]),
|
||||
ir.Select(condition, lhs[1], rhs[1])});
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
ir.SetThreadBitScalarReg(dst, selected_mask);
|
||||
ir.SetScalarMaskTag(dst, selected_mask_valid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::WqmB64: {
|
||||
const auto is_exec_or_vcc = [](const IR::Operand& operand) {
|
||||
return operand.kind == IR::OperandKind::Register &&
|
||||
(operand.reg.file == IR::RegisterFile::Exec ||
|
||||
operand.reg.file == IR::RegisterFile::Vcc);
|
||||
};
|
||||
if (!is_exec_or_vcc(inst.dst) && !is_exec_or_vcc(inst.src[0])) {
|
||||
const auto mask_valid = ReadMaskValid(inst.src[0]);
|
||||
case Decoder::Opcode::S_MOV_B32:
|
||||
case Decoder::Opcode::S_MOVK_I32:
|
||||
case Decoder::Opcode::V_MOV_B32:
|
||||
if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
|
||||
WriteMask(inst.dst, ReadMask(inst.src0));
|
||||
} else if (inst.opcode == Decoder::Opcode::V_MOV_B32 &&
|
||||
(inst.src0.negate || inst.src0.absolute)) {
|
||||
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::F32));
|
||||
} else {
|
||||
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::U32));
|
||||
}
|
||||
return true;
|
||||
case Decoder::Opcode::S_MOV_B64: {
|
||||
if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) {
|
||||
WriteMask64(inst.dst, ReadMask(inst.src0));
|
||||
return true;
|
||||
}
|
||||
const bool scalar_copy = inst.dst.kind == Decoder::OperandKind::Sgpr &&
|
||||
inst.src0.kind == Decoder::OperandKind::Sgpr;
|
||||
IR::U1 source_mask;
|
||||
IR::U1 source_mask_valid;
|
||||
if (scalar_copy) {
|
||||
source_mask = ir.GetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.src0.reg));
|
||||
source_mask_valid = ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(inst.src0.reg));
|
||||
}
|
||||
WriteU32Pair(inst.dst, ReadU32Pair(inst.src0));
|
||||
if (scalar_copy) {
|
||||
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask);
|
||||
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask_valid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Decoder::Opcode::S_WQM_B64: {
|
||||
if (!IsExecOrVcc(inst.dst) && !IsExecOrVcc(inst.src0)) {
|
||||
const auto mask_valid = ReadMaskValid(inst.src0);
|
||||
const auto invocation_result =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src[0])}));
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
|
||||
const auto result = IR::U64(
|
||||
ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src[0], IR::Type::U64)}));
|
||||
WriteOperand(inst.dst, result);
|
||||
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.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
ir.SetThreadBitScalarReg(dst, invocation_result);
|
||||
ir.SetScalarMaskTag(dst, mask_valid);
|
||||
const auto raw_nonzero = IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}));
|
||||
ir.SetScc(IR::U1(ir.Emit(
|
||||
IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
|
||||
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
|
||||
{mask_valid, invocation_result, raw_nonzero})));
|
||||
} else {
|
||||
ir.SetScc(IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
||||
}
|
||||
return 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);
|
||||
ir.SetScc(result);
|
||||
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));
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::MoveRelSourceU32: {
|
||||
EXIT_IF(inst.src[0].kind != IR::OperandKind::Register ||
|
||||
inst.src[0].reg.file != IR::RegisterFile::Vector);
|
||||
const auto base = inst.src[0].reg.index;
|
||||
const auto m0 = ir.BitwiseAnd(ReadU32(inst.src[1]), IR::U32(IR::Value(0xffu)));
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_MOVRELS_B32: {
|
||||
if (inst.dst.kind != Decoder::OperandKind::Vgpr ||
|
||||
inst.src0.kind != Decoder::OperandKind::Vgpr) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELS_B32 requires VGPR source and destination";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
|
||||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate ||
|
||||
inst.src0.absolute || inst.src0.dpp) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELS_B32 modifiers are not implemented";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const auto base = inst.src0.reg;
|
||||
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
|
||||
IR::U32(IR::Value(0xffu)));
|
||||
auto selected = ir.GetVectorReg(static_cast<IR::VectorReg>(base));
|
||||
for (uint32_t index = base + 1u; index < current_vector_limit; index++) {
|
||||
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
|
||||
selected =
|
||||
ir.Select(match, ir.GetVectorReg(static_cast<IR::VectorReg>(index)), selected);
|
||||
}
|
||||
WriteOperand(inst.dst, selected);
|
||||
WriteOperand(DestinationOperand(inst), selected);
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::MoveRelDestU32: {
|
||||
EXIT_IF(inst.dst.kind != IR::OperandKind::Register ||
|
||||
inst.dst.reg.file != IR::RegisterFile::Vector);
|
||||
const auto base = inst.dst.reg.index;
|
||||
const auto value = ReadU32(inst.src[0]);
|
||||
const auto m0 = ir.BitwiseAnd(ReadU32(inst.src[1]), IR::U32(IR::Value(0xffu)));
|
||||
case Decoder::Opcode::V_MOVRELD_B32: {
|
||||
if (inst.dst.kind != Decoder::OperandKind::Vgpr) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELD_B32 requires VGPR destination";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
|
||||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate ||
|
||||
inst.src0.absolute || inst.src0.dpp) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELD_B32 modifiers are not implemented";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const auto base = inst.dst.reg;
|
||||
const auto value = ReadU32(inst.src0);
|
||||
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
|
||||
IR::U32(IR::Value(0xffu)));
|
||||
for (uint32_t index = base; index < current_vector_limit; index++) {
|
||||
const auto reg = static_cast<IR::VectorReg>(index);
|
||||
const auto match = ir.IEqual(m0, IR::U32(IR::Value(index - base)));
|
||||
@@ -243,41 +396,38 @@ bool Translator::TranslateLaneOperation(const IR::Instruction& inst) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ReadFirstLaneU32: {
|
||||
case Decoder::Opcode::V_READFIRSTLANE_B32: {
|
||||
const auto result =
|
||||
ir.Emit(IR::ValueOpcode::ReadFirstLane, {ReadU32(inst.src[0]), ir.GetExec()});
|
||||
WriteOperand(inst.dst, result);
|
||||
ir.Emit(IR::ValueOpcode::ReadFirstLane, {ReadU32(inst.src0), ir.GetExec()});
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ReadLaneU32: {
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src[1]), lane_mask);
|
||||
WriteOperand(inst.dst,
|
||||
ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src[0]), lane}));
|
||||
case Decoder::Opcode::V_READLANE_B32: {
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.Emit(IR::ValueOpcode::ReadLane, {ReadU32(inst.src0), lane}));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::WriteLaneU32: {
|
||||
EXIT_IF(inst.dst.kind != IR::OperandKind::Register ||
|
||||
inst.dst.reg.file != IR::RegisterFile::Vector);
|
||||
const auto reg = static_cast<IR::VectorReg>(inst.dst.reg.index);
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src[1]), lane_mask);
|
||||
const auto result = IR::U32(ir.Emit(
|
||||
IR::ValueOpcode::WriteLane, {ir.GetVectorReg(reg), ReadU32(inst.src[0]), lane}));
|
||||
// V_WRITELANE_B32 ignores EXEC; bypass the ordinary VGPR destination path.
|
||||
case Decoder::Opcode::V_WRITELANE_B32: {
|
||||
EXIT_IF(inst.dst.kind != Decoder::OperandKind::Vgpr);
|
||||
const auto reg = static_cast<IR::VectorReg>(inst.dst.reg);
|
||||
const auto lane = ir.BitwiseAnd(ReadU32(inst.src1), lane_mask);
|
||||
const auto result = IR::U32(ir.Emit(IR::ValueOpcode::WriteLane,
|
||||
{ir.GetVectorReg(reg), ReadU32(inst.src0), lane}));
|
||||
ir.SetVectorReg(reg, result);
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::Permlane16B32:
|
||||
case IR::Opcode::Permlanex16B32: {
|
||||
case Decoder::Opcode::V_PERMLANE16_B32:
|
||||
case Decoder::Opcode::V_PERMLANEX16_B32: {
|
||||
const IR::PermlaneFlags flags {
|
||||
.x16 = inst.op == IR::Opcode::Permlanex16B32,
|
||||
.x16 = inst.opcode == Decoder::Opcode::V_PERMLANEX16_B32,
|
||||
.fetch_inactive = inst.dst.op_sel,
|
||||
.bound_control = inst.dst.op_sel_hi,
|
||||
};
|
||||
const auto result = ir.Emit(
|
||||
IR::ValueOpcode::Permlane16U32,
|
||||
{ReadU32(inst.src[0]), ReadU32(inst.src[1]), ReadU32(inst.src[2]), ir.GetExec()},
|
||||
flags);
|
||||
auto dst = inst.dst;
|
||||
{ReadU32(inst.src0), ReadU32(inst.src1), ReadU32(inst.src2), ir.GetExec()}, flags);
|
||||
auto dst = DestinationOperand(inst);
|
||||
dst.op_sel = false;
|
||||
dst.op_sel_hi = false;
|
||||
WriteOperand(dst, result);
|
||||
@@ -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"
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
bool Translator::TranslateConversion(const IR::Instruction& inst) {
|
||||
bool Translator::TranslateConversion(const Decoder::Instruction& inst) {
|
||||
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) {
|
||||
return ConvertF32ToU32Saturated(value, 4294967296.0f, 4294967040.0f, 0xffffffffu);
|
||||
};
|
||||
@@ -51,65 +51,77 @@ bool Translator::TranslateConversion(const IR::Instruction& inst) {
|
||||
0x80000000u, 0x7fffffffu);
|
||||
};
|
||||
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::ConvertByteU32ToF32: {
|
||||
const auto index =
|
||||
inst.src[1].kind == IR::OperandKind::ImmediateU32 ? inst.src[1].imm & 3u : 0u;
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_CVT_F32_UBYTE0:
|
||||
case Decoder::Opcode::V_CVT_F32_UBYTE1:
|
||||
case Decoder::Opcode::V_CVT_F32_UBYTE2:
|
||||
case Decoder::Opcode::V_CVT_F32_UBYTE3: {
|
||||
const auto index = static_cast<uint32_t>(inst.opcode) -
|
||||
static_cast<uint32_t>(Decoder::Opcode::V_CVT_F32_UBYTE0);
|
||||
const auto byte =
|
||||
ir.BitwiseAnd(ir.ShiftRightLogical(u32(0), IR::U32(IR::Value(index * 8u))),
|
||||
IR::U32(IR::Value(0xffu)));
|
||||
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::ConvertF32U32, {byte}));
|
||||
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::ConvertF32U32, {byte}));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ConvertU32ToF32:
|
||||
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::ConvertF32U32, {u32(0)}));
|
||||
case Decoder::Opcode::V_CVT_F32_U32:
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.Emit(IR::ValueOpcode::ConvertF32U32, {u32(0)}));
|
||||
return true;
|
||||
case IR::Opcode::ConvertI32ToF32:
|
||||
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::ConvertF32S32, {u32(0)}));
|
||||
case Decoder::Opcode::V_CVT_F32_I32:
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.Emit(IR::ValueOpcode::ConvertF32S32, {u32(0)}));
|
||||
return true;
|
||||
case IR::Opcode::ConvertF32ToU32: WriteOperand(inst.dst, convert_u32(f32(0))); return true;
|
||||
case IR::Opcode::ConvertF32ToI32: WriteOperand(inst.dst, convert_i32(f32(0))); return true;
|
||||
case IR::Opcode::ConvertF32ToF16: WriteF16(inst.dst, f32(0)); return true;
|
||||
case IR::Opcode::ConvertF16ToF32:
|
||||
WriteOperand(inst.dst, ReadF16AsF32(inst.src[0]));
|
||||
case Decoder::Opcode::V_CVT_U32_F32:
|
||||
WriteOperand(DestinationOperand(inst), convert_u32(f32(0)));
|
||||
return true;
|
||||
case IR::Opcode::ConvertU16ToF16: {
|
||||
const auto value = IR::F32(
|
||||
ir.Emit(IR::ValueOpcode::ConvertF32U32, {ReadU16AsU32(inst.src[0], false)}));
|
||||
WriteF16(inst.dst, value);
|
||||
case Decoder::Opcode::V_CVT_I32_F32:
|
||||
WriteOperand(DestinationOperand(inst), convert_i32(f32(0)));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ConvertI16ToF16: {
|
||||
case Decoder::Opcode::V_CVT_F16_F32:
|
||||
WriteF16(DestinationOperand(inst), f32(0));
|
||||
return true;
|
||||
case Decoder::Opcode::V_CVT_F32_F16:
|
||||
WriteOperand(DestinationOperand(inst), ReadF16AsF32(inst.src0));
|
||||
return true;
|
||||
case Decoder::Opcode::V_CVT_F16_U16: {
|
||||
const auto value =
|
||||
IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {ReadU16AsU32(inst.src[0], true)}));
|
||||
WriteF16(inst.dst, value);
|
||||
IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32U32, {ReadU16AsU32(inst.src0, false)}));
|
||||
WriteF16(DestinationOperand(inst), value);
|
||||
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 =
|
||||
ConvertF32ToU32Saturated(ReadF16AsF32(inst.src[0]), 65536.0f, 65535.0f, 0xffffu);
|
||||
WriteU16(inst.dst, converted);
|
||||
ConvertF32ToU32Saturated(ReadF16AsF32(inst.src0), 65536.0f, 65535.0f, 0xffffu);
|
||||
WriteU16(DestinationOperand(inst), converted);
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ConvertF16ToI16: {
|
||||
case Decoder::Opcode::V_CVT_I16_F16: {
|
||||
const auto converted = ConvertF32ToI32Saturated(
|
||||
ReadF16AsF32(inst.src[0]), -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu);
|
||||
WriteU16(inst.dst, ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
|
||||
ReadF16AsF32(inst.src0), -32768.0f, 32768.0f, 32767.0f, 0xffff8000u, 0x7fffu);
|
||||
WriteU16(DestinationOperand(inst),
|
||||
ir.BitwiseAnd(converted, IR::U32(IR::Value(0xffffu))));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ConvertRoundPlusInfF32ToI32: {
|
||||
case Decoder::Opcode::V_CVT_RPI_I32_F32: {
|
||||
const auto biased =
|
||||
IR::F32(ir.Emit(IR::ValueOpcode::FPAdd32, {f32(0), IR::Value::F32(0.5f)}));
|
||||
const auto rounded = IR::F32(ir.Emit(IR::ValueOpcode::FPFloor32, {biased}));
|
||||
WriteOperand(inst.dst, convert_i32(rounded));
|
||||
WriteOperand(DestinationOperand(inst), convert_i32(rounded));
|
||||
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)}));
|
||||
WriteOperand(inst.dst, convert_i32(rounded));
|
||||
WriteOperand(DestinationOperand(inst), convert_i32(rounded));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::FrexpExpI32F32: {
|
||||
case Decoder::Opcode::V_FREXP_EXP_I32_F32: {
|
||||
const auto bits = ir.BitCastU32(f32(0));
|
||||
const auto exponent = IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(23u), IR::Value(8u)}));
|
||||
@@ -123,43 +135,45 @@ bool Translator::TranslateConversion(const IR::Instruction& inst) {
|
||||
ir.INotEqual(exponent, IR::U32(IR::Value(0xffu))),
|
||||
ir.Select(ir.INotEqual(exponent, IR::U32(IR::Value(0u))), normal, denormal),
|
||||
IR::U32(IR::Value(0u)));
|
||||
WriteOperand(inst.dst, finite);
|
||||
WriteOperand(DestinationOperand(inst), finite);
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::ConvertI4ToOffsetF32: {
|
||||
case Decoder::Opcode::V_CVT_OFF_F32_I4: {
|
||||
const auto nibble = IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::BitFieldSExtract, {u32(0), IR::Value(0u), IR::Value(4u)}));
|
||||
const auto value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32S32, {nibble}));
|
||||
WriteOperand(inst.dst,
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.Emit(IR::ValueOpcode::FPMul32, {value, IR::Value::F32(1.0f / 16.0f)}));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::PackF32ToF16Rtz:
|
||||
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {f32(0), f32(1)}));
|
||||
case Decoder::Opcode::V_CVT_PKRTZ_F16_F32:
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {f32(0), f32(1)}));
|
||||
return true;
|
||||
case IR::Opcode::PackSnorm2x16F32:
|
||||
case IR::Opcode::PackUnorm2x16F32: {
|
||||
case Decoder::Opcode::V_CVT_PKNORM_I16_F32:
|
||||
case Decoder::Opcode::V_CVT_PKNORM_U16_F32: {
|
||||
const auto pair = ir.Emit(IR::ValueOpcode::CompositeConstructF32x2, {f32(0), f32(1)});
|
||||
const auto opcode = inst.op == IR::Opcode::PackSnorm2x16F32
|
||||
const auto opcode = inst.opcode == Decoder::Opcode::V_CVT_PKNORM_I16_F32
|
||||
? IR::ValueOpcode::PackSnorm2x16
|
||||
: IR::ValueOpcode::PackUnorm2x16;
|
||||
WriteOperand(inst.dst, ir.Emit(opcode, {pair}));
|
||||
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {pair}));
|
||||
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 index = ir.BitwiseAnd(u32(1), IR::U32(IR::Value(3u)));
|
||||
const auto shift = ir.ShiftLeftLogical(index, IR::U32(IR::Value(3u)));
|
||||
const auto mask = ir.ShiftLeftLogical(IR::U32(IR::Value(0xffu)), shift);
|
||||
const auto base = ir.BitwiseAnd(u32(2), ir.BitwiseNot(mask));
|
||||
WriteOperand(inst.dst, ir.BitwiseOr(base, ir.ShiftLeftLogical(byte, shift)));
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
ir.BitwiseOr(base, ir.ShiftLeftLogical(byte, shift)));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::PackB32F16: {
|
||||
const auto low = ReadF16LaneBits(inst.src[0], false);
|
||||
case Decoder::Opcode::V_PACK_B32_F16: {
|
||||
const auto low = ReadF16LaneBits(inst.src0, false);
|
||||
const auto high =
|
||||
ir.ShiftLeftLogical(ReadF16LaneBits(inst.src[1], false), IR::U32(IR::Value(16u)));
|
||||
WriteOperand(inst.dst, ir.BitwiseOr(low, high));
|
||||
ir.ShiftLeftLogical(ReadF16LaneBits(inst.src1, false), IR::U32(IR::Value(16u)));
|
||||
WriteOperand(DestinationOperand(inst), ir.BitwiseOr(low, high));
|
||||
return true;
|
||||
}
|
||||
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))));
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -1,57 +1,73 @@
|
||||
#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 is_exec_or_vcc = [](const IR::Operand& operand) {
|
||||
return operand.kind == IR::OperandKind::Register &&
|
||||
(operand.reg.file == IR::RegisterFile::Exec ||
|
||||
operand.reg.file == IR::RegisterFile::Vcc);
|
||||
const auto is_exec_or_vcc = [](const Decoder::Operand& operand) {
|
||||
switch (operand.kind) {
|
||||
case Decoder::OperandKind::ExecLo:
|
||||
case Decoder::OperandKind::ExecHi:
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi: return true;
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src[0]) ||
|
||||
(inst.src_count > 1u && is_exec_or_vcc(inst.src[1]))) {
|
||||
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src0) ||
|
||||
(inst.src_count > 1u && is_exec_or_vcc(inst.src1))) {
|
||||
WriteMask64(inst.dst, invocation_result);
|
||||
ir.SetScc(invocation_result);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto lhs = ReadU32Pair(inst.src[0]);
|
||||
auto mask_valid = ReadMaskValid(inst.src[0]);
|
||||
const auto lhs = ReadU32Pair(inst.src0);
|
||||
auto mask_valid = ReadMaskValid(inst.src0);
|
||||
if (inst.src_count > 1u) {
|
||||
mask_valid = ir.LogicalAnd(mask_valid, ReadMaskValid(inst.src[1]));
|
||||
mask_valid = ir.LogicalAnd(mask_valid, ReadMaskValid(inst.src1));
|
||||
}
|
||||
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])};
|
||||
} else {
|
||||
const auto rhs = ReadU32Pair(inst.src[1]);
|
||||
const auto rhs = ReadU32Pair(inst.src1);
|
||||
for (uint32_t component = 0; component < 2u; component++) {
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::BitwiseAndU64:
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_AND_B64:
|
||||
result[component] = ir.BitwiseAnd(lhs[component], rhs[component]);
|
||||
break;
|
||||
case IR::Opcode::BitwiseAndNotU64:
|
||||
case Decoder::Opcode::S_ANDN2_B64:
|
||||
result[component] =
|
||||
ir.BitwiseAnd(lhs[component], ir.BitwiseNot(rhs[component]));
|
||||
break;
|
||||
case IR::Opcode::BitwiseOrU64:
|
||||
case Decoder::Opcode::S_OR_B64:
|
||||
result[component] = ir.BitwiseOr(lhs[component], rhs[component]);
|
||||
break;
|
||||
case IR::Opcode::BitwiseOrNotU64:
|
||||
case Decoder::Opcode::S_ORN2_B64:
|
||||
result[component] = ir.BitwiseOr(lhs[component], ir.BitwiseNot(rhs[component]));
|
||||
break;
|
||||
case IR::Opcode::BitwiseXorU64:
|
||||
case Decoder::Opcode::S_XOR_B64:
|
||||
result[component] = ir.BitwiseXor(lhs[component], rhs[component]);
|
||||
break;
|
||||
case IR::Opcode::BitwiseNandU64:
|
||||
case Decoder::Opcode::S_NAND_B64:
|
||||
result[component] =
|
||||
ir.BitwiseNot(ir.BitwiseAnd(lhs[component], rhs[component]));
|
||||
break;
|
||||
case IR::Opcode::BitwiseNorU64:
|
||||
case Decoder::Opcode::S_NOR_B64:
|
||||
result[component] = ir.BitwiseNot(ir.BitwiseOr(lhs[component], rhs[component]));
|
||||
break;
|
||||
case IR::Opcode::BitwiseXnorU64:
|
||||
case Decoder::Opcode::S_XNOR_B64:
|
||||
result[component] =
|
||||
ir.BitwiseNot(ir.BitwiseXor(lhs[component], rhs[component]));
|
||||
break;
|
||||
@@ -60,9 +76,8 @@ bool Translator::TranslateU64MaskOperation(const IR::Instruction& inst) {
|
||||
}
|
||||
}
|
||||
WriteU32Pair(inst.dst, result);
|
||||
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);
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
ir.SetThreadBitScalarReg(dst, invocation_result);
|
||||
ir.SetScalarMaskTag(dst, mask_valid);
|
||||
const auto raw_nonzero =
|
||||
@@ -75,29 +90,68 @@ bool Translator::TranslateU64MaskOperation(const IR::Instruction& inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::TranslateInstruction(const IR::Instruction& inst) {
|
||||
switch (IR::GetOpcodeInfo(inst.op).lowering_class) {
|
||||
case IR::LoweringClass::Control: return TranslateControlOperation(inst);
|
||||
case IR::LoweringClass::Move: return TranslateMove(inst);
|
||||
case IR::LoweringClass::Lane: return TranslateLaneOperation(inst);
|
||||
case IR::LoweringClass::State: return TranslateStateOperation(inst);
|
||||
case IR::LoweringClass::Memory: return TranslateMemoryOperation(inst);
|
||||
case IR::LoweringClass::Attribute: return TranslateAttributeOperation(inst);
|
||||
case IR::LoweringClass::IntegerCompare: return TranslateIntegerCompare(inst);
|
||||
case IR::LoweringClass::Integer16Compare: return TranslateInteger16Compare(inst);
|
||||
case IR::LoweringClass::FloatCompare: return TranslateFloatCompare(inst);
|
||||
case IR::LoweringClass::Conversion: return TranslateConversion(inst);
|
||||
case IR::LoweringClass::Integer16: return TranslateInteger16Operation(inst);
|
||||
case IR::LoweringClass::PackedInteger16: return TranslatePackedInteger16(inst);
|
||||
case IR::LoweringClass::PackedFloat16: return TranslatePackedFloat16(inst);
|
||||
case IR::LoweringClass::Float16: return TranslateFloat16Operation(inst);
|
||||
case IR::LoweringClass::Float: return TranslateFloatOperation(inst);
|
||||
case IR::LoweringClass::U64Mask: return TranslateU64MaskOperation(inst);
|
||||
case IR::LoweringClass::SimpleInteger: return TranslateSimpleInteger(inst);
|
||||
case IR::LoweringClass::ComposedInteger: return TranslateComposedInteger(inst);
|
||||
case IR::LoweringClass::ExtendedInteger: return TranslateExtendedInteger(inst);
|
||||
bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::string* error) {
|
||||
current_opcode = inst.opcode;
|
||||
current_pc = inst.pc;
|
||||
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::UNKNOWN:
|
||||
case Decoder::Opcode::COUNT:
|
||||
if (error != nullptr) {
|
||||
*error = "decoded opcode has no IR translation";
|
||||
}
|
||||
return false;
|
||||
case Decoder::Opcode::UNSUPPORTED:
|
||||
if (error != nullptr) {
|
||||
*error = "unsupported decoded instruction: " + Decoder::InstructionToString(inst);
|
||||
}
|
||||
return false;
|
||||
case Decoder::Opcode::S_GETPC_B64: return TranslateMove(inst, error);
|
||||
case Decoder::Opcode::V_MOVRELS_B32:
|
||||
case Decoder::Opcode::V_MOVRELD_B32: return TranslateLaneOperation(inst, error);
|
||||
default: break;
|
||||
}
|
||||
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 <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;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::PackedAddF16: opcode = IR::ValueOpcode::FPAdd32; break;
|
||||
case IR::Opcode::PackedMulF16: opcode = IR::ValueOpcode::FPMul32; break;
|
||||
case IR::Opcode::PackedMinF16: opcode = IR::ValueOpcode::FPMin32; break;
|
||||
case IR::Opcode::PackedMaxF16: opcode = IR::ValueOpcode::FPMax32; break;
|
||||
case IR::Opcode::PackedFmaF16: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_PK_ADD_F16: opcode = IR::ValueOpcode::FPAdd32; break;
|
||||
case Decoder::Opcode::V_PK_MUL_F16: opcode = IR::ValueOpcode::FPMul32; break;
|
||||
case Decoder::Opcode::V_PK_MIN_F16: opcode = IR::ValueOpcode::FPMin32; break;
|
||||
case Decoder::Opcode::V_PK_MAX_F16: opcode = IR::ValueOpcode::FPMax32; break;
|
||||
case Decoder::Opcode::V_PK_FMA_F16: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
case Decoder::Opcode::V_PK_FMAC_F16: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
default: return false;
|
||||
}
|
||||
const auto translate_lane = [&](bool high) {
|
||||
const auto lhs = ReadF16LaneAsF32(inst.src[0], high, true);
|
||||
const auto rhs = ReadF16LaneAsF32(inst.src[1], high, true);
|
||||
const auto lhs = ReadF16LaneAsF32(inst.src0, high, true);
|
||||
const auto rhs = ReadF16LaneAsF32(inst.src1, high, true);
|
||||
IR::F32 result;
|
||||
if (inst.src_count == 3u) {
|
||||
result = IR::F32(
|
||||
ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src[2], high, true)}));
|
||||
if (inst.opcode == Decoder::Opcode::V_PK_FMAC_F16) {
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.dst, high, true)}));
|
||||
} else if (inst.src_count == 3u) {
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src2, high, true)}));
|
||||
} else {
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs}));
|
||||
}
|
||||
return ApplyF32ResultModifiers(inst.dst, result);
|
||||
};
|
||||
auto raw = inst.dst;
|
||||
auto raw = DestinationOperand(inst);
|
||||
raw.omod = 0u;
|
||||
raw.clamp = false;
|
||||
auto result = PackHalf2x16(translate_lane(false), translate_lane(true));
|
||||
if (inst.op == IR::Opcode::PackedMinF16 || inst.op == IR::Opcode::PackedMaxF16) {
|
||||
const auto quiet_snan = [&](const IR::Operand& operand, bool high) {
|
||||
if (inst.opcode == Decoder::Opcode::V_PK_MIN_F16 ||
|
||||
inst.opcode == Decoder::Opcode::V_PK_MAX_F16) {
|
||||
const auto quiet_snan = [&](const Decoder::Operand& operand, bool high) {
|
||||
const auto bits = ReadU16LaneAsU32(operand, high, false);
|
||||
const auto exponent = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x7c00u)));
|
||||
const auto payload = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x01ffu)));
|
||||
@@ -41,8 +44,8 @@ bool Translator::TranslatePackedFloat16(const IR::Instruction& inst) {
|
||||
return std::pair {snan, ir.BitwiseOr(bits, IR::U32(IR::Value(0x0200u)))};
|
||||
};
|
||||
const auto override_lane = [&](bool high) {
|
||||
const auto [lhs_snan, lhs_quiet] = quiet_snan(inst.src[0], high);
|
||||
const auto [rhs_snan, rhs_quiet] = quiet_snan(inst.src[1], high);
|
||||
const auto [lhs_snan, lhs_quiet] = quiet_snan(inst.src0, high);
|
||||
const auto [rhs_snan, rhs_quiet] = quiet_snan(inst.src1, high);
|
||||
const auto normal = high ? ir.ShiftRightLogical(result, IR::U32(IR::Value(16u)))
|
||||
: ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu)));
|
||||
return ir.Select(lhs_snan, lhs_quiet, ir.Select(rhs_snan, rhs_quiet, normal));
|
||||
@@ -53,59 +56,71 @@ bool Translator::TranslatePackedFloat16(const IR::Instruction& inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::TranslateFloat16Operation(const IR::Instruction& inst) {
|
||||
bool Translator::TranslateFloat16Operation(const Decoder::Instruction& inst) {
|
||||
IR::ValueOpcode opcode;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::AddF16: opcode = IR::ValueOpcode::FPAdd32; break;
|
||||
case IR::Opcode::SubF16: opcode = IR::ValueOpcode::FPSub32; break;
|
||||
case IR::Opcode::MulF16: opcode = IR::ValueOpcode::FPMul32; break;
|
||||
case IR::Opcode::MinF16: opcode = IR::ValueOpcode::FPMin32; break;
|
||||
case IR::Opcode::MaxF16: opcode = IR::ValueOpcode::FPMax32; break;
|
||||
case IR::Opcode::FmaF16:
|
||||
case IR::Opcode::MadMixF16: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
case IR::Opcode::RcpF16: opcode = IR::ValueOpcode::FPRecip32; break;
|
||||
case IR::Opcode::SqrtF16: opcode = IR::ValueOpcode::FPSqrt; break;
|
||||
case IR::Opcode::InverseSqrtF16: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
|
||||
case IR::Opcode::Log2F16: opcode = IR::ValueOpcode::FPLog2; break;
|
||||
case IR::Opcode::Exp2F16: opcode = IR::ValueOpcode::FPExp2; break;
|
||||
case IR::Opcode::FloorF16: opcode = IR::ValueOpcode::FPFloor32; break;
|
||||
case IR::Opcode::CeilF16: opcode = IR::ValueOpcode::FPCeil32; break;
|
||||
case IR::Opcode::TruncF16: opcode = IR::ValueOpcode::FPTrunc32; break;
|
||||
case IR::Opcode::RoundEvenF16: opcode = IR::ValueOpcode::FPRoundEven32; break;
|
||||
case IR::Opcode::Min3F16: opcode = IR::ValueOpcode::FPMinTri32; break;
|
||||
case IR::Opcode::Max3F16: opcode = IR::ValueOpcode::FPMaxTri32; break;
|
||||
case IR::Opcode::Med3F16: opcode = IR::ValueOpcode::FPMedTri32; break;
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_ADD_F16: opcode = IR::ValueOpcode::FPAdd32; break;
|
||||
case Decoder::Opcode::V_SUB_F16: opcode = IR::ValueOpcode::FPSub32; break;
|
||||
case Decoder::Opcode::V_SUBREV_F16: opcode = IR::ValueOpcode::FPSub32; break;
|
||||
case Decoder::Opcode::V_MUL_F16: opcode = IR::ValueOpcode::FPMul32; break;
|
||||
case Decoder::Opcode::V_MIN_F16: opcode = IR::ValueOpcode::FPMin32; break;
|
||||
case Decoder::Opcode::V_MAX_F16: opcode = IR::ValueOpcode::FPMax32; break;
|
||||
case Decoder::Opcode::V_FMAC_F16:
|
||||
case Decoder::Opcode::V_FMAMK_F16:
|
||||
case Decoder::Opcode::V_FMAAK_F16:
|
||||
case Decoder::Opcode::V_FMA_F16:
|
||||
case Decoder::Opcode::V_MAD_MIXLO_F16: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
case Decoder::Opcode::V_MAD_MIXHI_F16: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
case Decoder::Opcode::V_RCP_F16: opcode = IR::ValueOpcode::FPRecip32; break;
|
||||
case Decoder::Opcode::V_SQRT_F16: opcode = IR::ValueOpcode::FPSqrt; break;
|
||||
case Decoder::Opcode::V_RSQ_F16: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
|
||||
case Decoder::Opcode::V_LOG_F16: opcode = IR::ValueOpcode::FPLog2; break;
|
||||
case Decoder::Opcode::V_EXP_F16: opcode = IR::ValueOpcode::FPExp2; break;
|
||||
case Decoder::Opcode::V_FLOOR_F16: opcode = IR::ValueOpcode::FPFloor32; break;
|
||||
case Decoder::Opcode::V_CEIL_F16: opcode = IR::ValueOpcode::FPCeil32; break;
|
||||
case Decoder::Opcode::V_TRUNC_F16: opcode = IR::ValueOpcode::FPTrunc32; break;
|
||||
case Decoder::Opcode::V_RNDNE_F16: opcode = IR::ValueOpcode::FPRoundEven32; break;
|
||||
case Decoder::Opcode::V_MIN3_F16: opcode = IR::ValueOpcode::FPMinTri32; break;
|
||||
case Decoder::Opcode::V_MAX3_F16: opcode = IR::ValueOpcode::FPMaxTri32; break;
|
||||
case Decoder::Opcode::V_MED3_F16: opcode = IR::ValueOpcode::FPMedTri32; break;
|
||||
default: return false;
|
||||
}
|
||||
const bool accumulator = inst.opcode == Decoder::Opcode::V_FMAC_F16;
|
||||
const uint32_t source_count = accumulator ? 3u : inst.src_count;
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < inst.src_count; index++) {
|
||||
args[index] = inst.op == IR::Opcode::MadMixF16 ? IR::Value(ReadMixF32(inst.src[index]))
|
||||
: IR::Value(ReadF16AsF32(inst.src[index]));
|
||||
for (uint32_t index = 0; index < source_count; index++) {
|
||||
auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
if (inst.opcode == Decoder::Opcode::V_SUBREV_F16 && index < 2u) {
|
||||
operand = SourceAt(inst, 1u - index);
|
||||
}
|
||||
const bool mix = inst.opcode == Decoder::Opcode::V_MAD_MIXLO_F16 ||
|
||||
inst.opcode == Decoder::Opcode::V_MAD_MIXHI_F16;
|
||||
args[index] = mix ? IR::Value(ReadMixF32(operand)) : IR::Value(ReadF16AsF32(operand));
|
||||
}
|
||||
IR::F32 result;
|
||||
switch (inst.src_count) {
|
||||
switch (source_count) {
|
||||
case 1: result = IR::F32(ir.Emit(opcode, {args[0]})); break;
|
||||
case 2: result = IR::F32(ir.Emit(opcode, {args[0], args[1]})); break;
|
||||
case 3: result = IR::F32(ir.Emit(opcode, {args[0], args[1], args[2]})); break;
|
||||
default: EXIT("invalid half-float source count: %u", inst.src_count);
|
||||
}
|
||||
if (inst.op == IR::Opcode::SqrtF16 || inst.op == IR::Opcode::InverseSqrtF16 ||
|
||||
inst.op == IR::Opcode::Log2F16) {
|
||||
const auto negative = IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {args[0], IR::Value::F32(0.0f)}));
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
if (inst.opcode == Decoder::Opcode::V_SQRT_F16 || inst.opcode == Decoder::Opcode::V_RSQ_F16 ||
|
||||
inst.opcode == Decoder::Opcode::V_LOG_F16) {
|
||||
const auto negative =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::FPOrdLessThan32, {args[0], IR::Value::F32(0.0f)}));
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
|
||||
WriteU16(inst.dst, ir.Select(negative, invalid, bits));
|
||||
WriteU16(DestinationOperand(inst), ir.Select(negative, invalid, bits));
|
||||
return true;
|
||||
}
|
||||
WriteF16(inst.dst, result);
|
||||
WriteF16(DestinationOperand(inst), result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::TranslateFloatOperation(const IR::Instruction& inst) {
|
||||
if (inst.op == IR::Opcode::FrexpMantF32) {
|
||||
const auto bits = ReadU32(inst.src[0]);
|
||||
bool Translator::TranslateFloatOperation(const Decoder::Instruction& inst) {
|
||||
if (inst.opcode == Decoder::Opcode::V_FREXP_MANT_F32) {
|
||||
const auto bits = ReadU32(inst.src0);
|
||||
const auto exponent = IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(23u), IR::Value(8u)}));
|
||||
const auto mantissa = ir.BitwiseAnd(bits, IR::U32(IR::Value(0x007fffffu)));
|
||||
@@ -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,
|
||||
ir.Select(zero, bits, subnormal));
|
||||
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;
|
||||
}
|
||||
IR::ValueOpcode opcode {};
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::RcpF32: opcode = IR::ValueOpcode::FPRecip32; break;
|
||||
case IR::Opcode::RcpIflagF32: opcode = IR::ValueOpcode::FPRecipIFlag32; break;
|
||||
case IR::Opcode::FractF32: opcode = IR::ValueOpcode::FPFract32; break;
|
||||
case IR::Opcode::TruncF32: opcode = IR::ValueOpcode::FPTrunc32; break;
|
||||
case IR::Opcode::CeilF32: opcode = IR::ValueOpcode::FPCeil32; break;
|
||||
case IR::Opcode::RoundEvenF32: opcode = IR::ValueOpcode::FPRoundEven32; break;
|
||||
case IR::Opcode::FloorF32: opcode = IR::ValueOpcode::FPFloor32; break;
|
||||
case IR::Opcode::Exp2F32: opcode = IR::ValueOpcode::FPExp2; break;
|
||||
case IR::Opcode::Log2F32: opcode = IR::ValueOpcode::FPLog2; break;
|
||||
case IR::Opcode::InverseSqrtF32: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
|
||||
case IR::Opcode::SqrtF32: opcode = IR::ValueOpcode::FPSqrt; break;
|
||||
case IR::Opcode::SinF32: opcode = IR::ValueOpcode::FPSin; break;
|
||||
case IR::Opcode::CosF32: opcode = IR::ValueOpcode::FPCos; break;
|
||||
case IR::Opcode::FAddF32: opcode = IR::ValueOpcode::FPAdd32; break;
|
||||
case IR::Opcode::FSubF32: opcode = IR::ValueOpcode::FPSub32; break;
|
||||
case IR::Opcode::FMulF32: opcode = IR::ValueOpcode::FPMul32; break;
|
||||
case IR::Opcode::FMinF32: opcode = IR::ValueOpcode::FPMin32; break;
|
||||
case IR::Opcode::FMaxF32: opcode = IR::ValueOpcode::FPMax32; break;
|
||||
case IR::Opcode::FMadF32: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
case IR::Opcode::FMin3F32: opcode = IR::ValueOpcode::FPMinTri32; break;
|
||||
case IR::Opcode::FMax3F32: opcode = IR::ValueOpcode::FPMaxTri32; break;
|
||||
case IR::Opcode::FMed3F32: opcode = IR::ValueOpcode::FPMedTri32; break;
|
||||
case IR::Opcode::LdexpF32: opcode = IR::ValueOpcode::FPLdexp; break;
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_RCP_F32: opcode = IR::ValueOpcode::FPRecip32; break;
|
||||
case Decoder::Opcode::V_RCP_IFLAG_F32: opcode = IR::ValueOpcode::FPRecipIFlag32; break;
|
||||
case Decoder::Opcode::V_FRACT_F32: opcode = IR::ValueOpcode::FPFract32; break;
|
||||
case Decoder::Opcode::V_TRUNC_F32: opcode = IR::ValueOpcode::FPTrunc32; break;
|
||||
case Decoder::Opcode::V_CEIL_F32: opcode = IR::ValueOpcode::FPCeil32; break;
|
||||
case Decoder::Opcode::V_RNDNE_F32: opcode = IR::ValueOpcode::FPRoundEven32; break;
|
||||
case Decoder::Opcode::V_FLOOR_F32: opcode = IR::ValueOpcode::FPFloor32; break;
|
||||
case Decoder::Opcode::V_EXP_F32: opcode = IR::ValueOpcode::FPExp2; break;
|
||||
case Decoder::Opcode::V_LOG_F32: opcode = IR::ValueOpcode::FPLog2; break;
|
||||
case Decoder::Opcode::V_RSQ_F32: opcode = IR::ValueOpcode::FPRecipSqrt32; break;
|
||||
case Decoder::Opcode::V_SQRT_F32: opcode = IR::ValueOpcode::FPSqrt; break;
|
||||
case Decoder::Opcode::V_SIN_F32: opcode = IR::ValueOpcode::FPSin; break;
|
||||
case Decoder::Opcode::V_COS_F32: opcode = IR::ValueOpcode::FPCos; break;
|
||||
case Decoder::Opcode::V_ADD_F32: opcode = IR::ValueOpcode::FPAdd32; break;
|
||||
case Decoder::Opcode::V_SUB_F32: opcode = IR::ValueOpcode::FPSub32; break;
|
||||
case Decoder::Opcode::V_SUBREV_F32: opcode = IR::ValueOpcode::FPSub32; break;
|
||||
case Decoder::Opcode::V_MUL_F32: opcode = IR::ValueOpcode::FPMul32; break;
|
||||
case Decoder::Opcode::V_MIN_F32: opcode = IR::ValueOpcode::FPMin32; break;
|
||||
case Decoder::Opcode::V_MAX_F32: opcode = IR::ValueOpcode::FPMax32; break;
|
||||
case Decoder::Opcode::V_MAC_F32: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
case Decoder::Opcode::V_MADMK_F32:
|
||||
case Decoder::Opcode::V_MADAK_F32:
|
||||
case Decoder::Opcode::V_MAD_F32:
|
||||
case Decoder::Opcode::V_FMA_F32: opcode = IR::ValueOpcode::FPFma32; break;
|
||||
case Decoder::Opcode::V_MIN3_F32: opcode = IR::ValueOpcode::FPMinTri32; break;
|
||||
case Decoder::Opcode::V_MAX3_F32: opcode = IR::ValueOpcode::FPMaxTri32; break;
|
||||
case Decoder::Opcode::V_MED3_F32: opcode = IR::ValueOpcode::FPMedTri32; break;
|
||||
case Decoder::Opcode::V_LDEXP_F32: opcode = IR::ValueOpcode::FPLdexp; break;
|
||||
default: break;
|
||||
}
|
||||
if (opcode != IR::ValueOpcode {}) {
|
||||
const bool accumulator = inst.opcode == Decoder::Opcode::V_MAC_F32;
|
||||
const bool fused = accumulator || inst.opcode == Decoder::Opcode::V_MADMK_F32 ||
|
||||
inst.opcode == Decoder::Opcode::V_MADAK_F32 ||
|
||||
inst.opcode == Decoder::Opcode::V_MAD_F32 ||
|
||||
inst.opcode == Decoder::Opcode::V_FMA_F32;
|
||||
const uint32_t source_count = accumulator ? 3u : inst.src_count;
|
||||
std::array<IR::Value, 3> args;
|
||||
for (uint32_t index = 0; index < inst.src_count; index++) {
|
||||
for (uint32_t index = 0; index < source_count; index++) {
|
||||
const auto arg_type = IR::ArgTypeOf(opcode, index);
|
||||
args[index] =
|
||||
arg_type == IR::Type::F32
|
||||
? (inst.op == IR::Opcode::FMadF32 ? IR::Value(ReadMixF32(inst.src[index]))
|
||||
: ReadOperand(inst.src[index], IR::Type::F32))
|
||||
: ReadOperand(inst.src[index], arg_type);
|
||||
auto operand = accumulator && index == 2u ? inst.dst : SourceAt(inst, index);
|
||||
if (inst.opcode == Decoder::Opcode::V_SUBREV_F32 && index < 2u) {
|
||||
operand = SourceAt(inst, 1u - index);
|
||||
}
|
||||
args[index] = arg_type == IR::Type::F32 ? (fused ? IR::Value(ReadMixF32(operand))
|
||||
: ReadOperand(operand, IR::Type::F32))
|
||||
: ReadOperand(operand, arg_type);
|
||||
}
|
||||
IR::Value result;
|
||||
switch (inst.src_count) {
|
||||
switch (source_count) {
|
||||
case 1: result = ir.Emit(opcode, {args[0]}); break;
|
||||
case 2: result = ir.Emit(opcode, {args[0], args[1]}); break;
|
||||
case 3: result = ir.Emit(opcode, {args[0], args[1], args[2]}); break;
|
||||
default: EXIT("invalid floating-point source count: %u", inst.src_count);
|
||||
}
|
||||
WriteOperand(inst.dst, result);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto select_f32 = [&](IR::U1 condition, IR::F32 true_value, IR::F32 false_value) {
|
||||
return IR::F32(ir.Emit(IR::ValueOpcode::SelectF32, {condition, true_value, false_value}));
|
||||
};
|
||||
if (inst.op == IR::Opcode::Dot2AccF32F16) {
|
||||
auto a = inst.src[0];
|
||||
if (inst.opcode == Decoder::Opcode::V_DOT2C_F32_F16) {
|
||||
auto a = inst.src0;
|
||||
a.op_sel = false;
|
||||
a.op_sel_hi = true;
|
||||
auto b = inst.src[1];
|
||||
auto b = inst.src1;
|
||||
b.op_sel = false;
|
||||
b.op_sel_hi = true;
|
||||
const auto a_lo = ReadF16LaneAsF32(a, false);
|
||||
const auto a_hi = ReadF16LaneAsF32(a, true);
|
||||
const auto b_lo = ReadF16LaneAsF32(b, false);
|
||||
const auto b_hi = ReadF16LaneAsF32(b, true);
|
||||
const auto acc = ReadMixF32(inst.src[2]);
|
||||
const auto acc = ReadMixF32(inst.dst);
|
||||
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;
|
||||
}
|
||||
if (inst.op != IR::Opcode::CubeIdF32 && inst.op != IR::Opcode::CubeScF32 &&
|
||||
inst.op != IR::Opcode::CubeTcF32 && inst.op != IR::Opcode::CubeMaF32) {
|
||||
if (inst.opcode != Decoder::Opcode::V_CUBEID_F32 &&
|
||||
inst.opcode != Decoder::Opcode::V_CUBESC_F32 &&
|
||||
inst.opcode != Decoder::Opcode::V_CUBETC_F32 &&
|
||||
inst.opcode != Decoder::Opcode::V_CUBEMA_F32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto x = ReadMixF32(inst.src[0]);
|
||||
const auto y = ReadMixF32(inst.src[1]);
|
||||
const auto z = ReadMixF32(inst.src[2]);
|
||||
const auto x = ReadMixF32(inst.src0);
|
||||
const auto y = ReadMixF32(inst.src1);
|
||||
const auto z = ReadMixF32(inst.src2);
|
||||
const auto nx = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {x}));
|
||||
const auto ny = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {y}));
|
||||
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));
|
||||
};
|
||||
IR::F32 result;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::CubeIdF32:
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_CUBEID_F32:
|
||||
result = select_face(
|
||||
select_f32(x_neg, IR::F32(IR::Value::F32(1.0f)), IR::F32(IR::Value::F32(0.0f))),
|
||||
select_f32(y_neg, IR::F32(IR::Value::F32(3.0f)), IR::F32(IR::Value::F32(2.0f))),
|
||||
select_f32(z_neg, IR::F32(IR::Value::F32(5.0f)), IR::F32(IR::Value::F32(4.0f))));
|
||||
break;
|
||||
case 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));
|
||||
break;
|
||||
case IR::Opcode::CubeTcF32: result = select_face(ny, select_f32(y_neg, nz, z), ny); break;
|
||||
case IR::Opcode::CubeMaF32: {
|
||||
case Decoder::Opcode::V_CUBETC_F32:
|
||||
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));
|
||||
result = select_face(IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {x, two})),
|
||||
IR::F32(ir.Emit(IR::ValueOpcode::FPMul32, {y, two})),
|
||||
@@ -236,8 +268,8 @@ bool Translator::TranslateFloatOperation(const IR::Instruction& inst) {
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
WriteOperand(inst.dst, result);
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -2,22 +2,22 @@
|
||||
|
||||
#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) {
|
||||
return ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu)));
|
||||
};
|
||||
IR::Value result;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::ShiftLeftLogicalU16:
|
||||
case IR::Opcode::ShiftRightLogicalU16:
|
||||
case IR::Opcode::ShiftRightArithmeticI16: {
|
||||
const bool arithmetic = inst.op == IR::Opcode::ShiftRightArithmeticI16;
|
||||
const auto value = ReadU16AsU32(inst.src[0], arithmetic);
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_LSHLREV_B16:
|
||||
case Decoder::Opcode::V_LSHRREV_B16:
|
||||
case Decoder::Opcode::V_ASHRREV_I16: {
|
||||
const bool arithmetic = inst.opcode == Decoder::Opcode::V_ASHRREV_I16;
|
||||
const auto value = ReadU16AsU32(inst.src1, arithmetic);
|
||||
const auto count =
|
||||
ir.BitwiseAnd(ReadU16AsU32(inst.src[1], false), IR::U32(IR::Value(15u)));
|
||||
if (inst.op == IR::Opcode::ShiftLeftLogicalU16) {
|
||||
ir.BitwiseAnd(ReadU16AsU32(inst.src0, false), IR::U32(IR::Value(15u)));
|
||||
if (inst.opcode == Decoder::Opcode::V_LSHLREV_B16) {
|
||||
result = ir.ShiftLeftLogical(value, count);
|
||||
} else if (arithmetic) {
|
||||
result = ir.ShiftRightArithmetic(value, count);
|
||||
@@ -26,176 +26,207 @@ bool Translator::TranslateInteger16Operation(const IR::Instruction& inst) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::IAddU16:
|
||||
result = ir.IAdd(ReadU16AsU32(inst.src[0], false), ReadU16AsU32(inst.src[1], false));
|
||||
case Decoder::Opcode::V_ADD_NC_U16:
|
||||
case Decoder::Opcode::V_ADD_NC_I16:
|
||||
result = ir.IAdd(ReadU16AsU32(inst.src0, false), ReadU16AsU32(inst.src1, false));
|
||||
break;
|
||||
case IR::Opcode::ISubI16:
|
||||
result = ir.ISub(ReadU16AsU32(inst.src[0], false), ReadU16AsU32(inst.src[1], false));
|
||||
case Decoder::Opcode::V_SUB_NC_U16:
|
||||
case Decoder::Opcode::V_SUB_NC_I16:
|
||||
result = ir.ISub(ReadU16AsU32(inst.src0, false), ReadU16AsU32(inst.src1, false));
|
||||
break;
|
||||
case IR::Opcode::IMed3I16:
|
||||
case Decoder::Opcode::V_MED3_I16:
|
||||
result = ir.Emit(IR::ValueOpcode::SMedTri32,
|
||||
{ReadU16AsU32(inst.src[0], true), ReadU16AsU32(inst.src[1], true),
|
||||
ReadU16AsU32(inst.src[2], true)});
|
||||
{ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
|
||||
ReadU16AsU32(inst.src2, true)});
|
||||
break;
|
||||
case IR::Opcode::IMinI16:
|
||||
case IR::Opcode::IMaxI16:
|
||||
case IR::Opcode::UMinU16:
|
||||
case IR::Opcode::UMaxU16: {
|
||||
const bool sign = inst.op == IR::Opcode::IMinI16 || inst.op == IR::Opcode::IMaxI16;
|
||||
case Decoder::Opcode::V_MIN_I16:
|
||||
case Decoder::Opcode::V_MAX_I16:
|
||||
case Decoder::Opcode::V_MIN_U16:
|
||||
case Decoder::Opcode::V_MAX_U16: {
|
||||
const bool sign = inst.opcode == Decoder::Opcode::V_MIN_I16 ||
|
||||
inst.opcode == Decoder::Opcode::V_MAX_I16;
|
||||
IR::ValueOpcode opcode;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::IMinI16: opcode = IR::ValueOpcode::SMin32; break;
|
||||
case IR::Opcode::IMaxI16: opcode = IR::ValueOpcode::SMax32; break;
|
||||
case IR::Opcode::UMinU16: opcode = IR::ValueOpcode::UMin32; break;
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_MIN_I16: opcode = IR::ValueOpcode::SMin32; break;
|
||||
case Decoder::Opcode::V_MAX_I16: opcode = IR::ValueOpcode::SMax32; break;
|
||||
case Decoder::Opcode::V_MIN_U16: opcode = IR::ValueOpcode::UMin32; break;
|
||||
default: opcode = IR::ValueOpcode::UMax32; break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
WriteU16(inst.dst, mask16(IR::U32(result)));
|
||||
WriteU16(DestinationOperand(inst), mask16(IR::U32(result)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::TranslatePackedInteger16(const IR::Instruction& inst) {
|
||||
const auto lane = [&](const IR::Operand& operand, bool high, bool sign) {
|
||||
bool Translator::TranslatePackedInteger16(const Decoder::Instruction& inst) {
|
||||
const auto lane = [&](const Decoder::Operand& operand, bool high, bool sign) {
|
||||
return ReadU16LaneAsU32(operand, high, sign);
|
||||
};
|
||||
const auto mask_count = [&](IR::U32 value) {
|
||||
return ir.BitwiseAnd(value, IR::U32(IR::Value(15u)));
|
||||
};
|
||||
const auto translate_lane = [&](bool high) -> IR::U32 {
|
||||
if (inst.op == IR::Opcode::PackedLshlrevB16 || inst.op == IR::Opcode::PackedLshrrevB16 ||
|
||||
inst.op == IR::Opcode::PackedAshrrevI16) {
|
||||
const auto count = mask_count(lane(inst.src[0], high, false));
|
||||
const bool arithmetic = inst.op == IR::Opcode::PackedAshrrevI16;
|
||||
const auto value = lane(inst.src[1], high, arithmetic);
|
||||
if (inst.op == IR::Opcode::PackedLshlrevB16) {
|
||||
if (inst.opcode == Decoder::Opcode::V_PK_LSHLREV_B16 ||
|
||||
inst.opcode == Decoder::Opcode::V_PK_LSHRREV_B16 ||
|
||||
inst.opcode == Decoder::Opcode::V_PK_ASHRREV_I16) {
|
||||
const auto count = mask_count(lane(inst.src0, high, false));
|
||||
const bool arithmetic = inst.opcode == Decoder::Opcode::V_PK_ASHRREV_I16;
|
||||
const auto value = lane(inst.src1, high, arithmetic);
|
||||
if (inst.opcode == Decoder::Opcode::V_PK_LSHLREV_B16) {
|
||||
return ir.ShiftLeftLogical(value, count);
|
||||
}
|
||||
return arithmetic ? ir.ShiftRightArithmetic(value, count)
|
||||
: ir.ShiftRightLogical(value, count);
|
||||
}
|
||||
|
||||
const bool signed_minmax =
|
||||
inst.op == IR::Opcode::PackedMaxI16 || inst.op == IR::Opcode::PackedMinI16;
|
||||
const auto lhs = lane(inst.src[0], high, signed_minmax);
|
||||
const auto rhs = lane(inst.src[1], high, signed_minmax);
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::PackedMadI16:
|
||||
case IR::Opcode::PackedMadU16:
|
||||
const bool signed_minmax = inst.opcode == Decoder::Opcode::V_PK_MAX_I16 ||
|
||||
inst.opcode == Decoder::Opcode::V_PK_MIN_I16;
|
||||
const auto lhs = lane(inst.src0, high, signed_minmax);
|
||||
const auto rhs = lane(inst.src1, high, signed_minmax);
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_PK_MAD_I16:
|
||||
case Decoder::Opcode::V_PK_MAD_U16:
|
||||
return ir.IAdd(ir.IMul(lhs, rhs),
|
||||
lane(inst.src[2], high, inst.op == IR::Opcode::PackedMadI16));
|
||||
case IR::Opcode::PackedMulLoU16: return ir.IMul(lhs, rhs);
|
||||
case IR::Opcode::PackedAddI16:
|
||||
case IR::Opcode::PackedAddU16: return ir.IAdd(lhs, rhs);
|
||||
case IR::Opcode::PackedSubI16:
|
||||
case IR::Opcode::PackedSubU16: return ir.ISub(lhs, rhs);
|
||||
case IR::Opcode::PackedMaxI16:
|
||||
lane(inst.src2, high, inst.opcode == Decoder::Opcode::V_PK_MAD_I16));
|
||||
case Decoder::Opcode::V_PK_MUL_LO_U16: return ir.IMul(lhs, rhs);
|
||||
case Decoder::Opcode::V_PK_ADD_I16:
|
||||
case Decoder::Opcode::V_PK_ADD_U16: return ir.IAdd(lhs, rhs);
|
||||
case Decoder::Opcode::V_PK_SUB_I16:
|
||||
case Decoder::Opcode::V_PK_SUB_U16: return ir.ISub(lhs, rhs);
|
||||
case Decoder::Opcode::V_PK_MAX_I16:
|
||||
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}));
|
||||
case IR::Opcode::PackedMaxU16:
|
||||
case Decoder::Opcode::V_PK_MAX_U16:
|
||||
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}));
|
||||
default: EXIT("invalid packed integer opcode");
|
||||
}
|
||||
};
|
||||
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::PackedMadI16:
|
||||
case IR::Opcode::PackedMulLoU16:
|
||||
case IR::Opcode::PackedAddI16:
|
||||
case IR::Opcode::PackedSubI16:
|
||||
case IR::Opcode::PackedLshlrevB16:
|
||||
case IR::Opcode::PackedLshrrevB16:
|
||||
case IR::Opcode::PackedAshrrevI16:
|
||||
case IR::Opcode::PackedMaxI16:
|
||||
case IR::Opcode::PackedMinI16:
|
||||
case IR::Opcode::PackedMadU16:
|
||||
case IR::Opcode::PackedAddU16:
|
||||
case IR::Opcode::PackedSubU16:
|
||||
case IR::Opcode::PackedMaxU16:
|
||||
case IR::Opcode::PackedMinU16:
|
||||
WriteOperand(inst.dst, PackU16Lanes(translate_lane(false), translate_lane(true)));
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_PK_MAD_I16:
|
||||
case Decoder::Opcode::V_PK_MUL_LO_U16:
|
||||
case Decoder::Opcode::V_PK_ADD_I16:
|
||||
case Decoder::Opcode::V_PK_SUB_I16:
|
||||
case Decoder::Opcode::V_PK_LSHLREV_B16:
|
||||
case Decoder::Opcode::V_PK_LSHRREV_B16:
|
||||
case Decoder::Opcode::V_PK_ASHRREV_I16:
|
||||
case Decoder::Opcode::V_PK_MAX_I16:
|
||||
case Decoder::Opcode::V_PK_MIN_I16:
|
||||
case Decoder::Opcode::V_PK_MAD_U16:
|
||||
case Decoder::Opcode::V_PK_ADD_U16:
|
||||
case Decoder::Opcode::V_PK_SUB_U16:
|
||||
case Decoder::Opcode::V_PK_MAX_U16:
|
||||
case Decoder::Opcode::V_PK_MIN_U16:
|
||||
WriteOperand(DestinationOperand(inst),
|
||||
PackU16Lanes(translate_lane(false), translate_lane(true)));
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
IR::U1 Translator::EvaluateU64Mask(const IR::Instruction& inst) {
|
||||
const auto lhs = [&] { return ReadMask(inst.src[0]); };
|
||||
const auto rhs = [&] { return ReadMask(inst.src[1]); };
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::BitwiseAndU64: return ir.LogicalAnd(lhs(), rhs());
|
||||
case IR::Opcode::BitwiseOrU64: return ir.LogicalOr(lhs(), rhs());
|
||||
case IR::Opcode::BitwiseXorU64:
|
||||
IR::U1 Translator::EvaluateU64Mask(const Decoder::Instruction& inst) {
|
||||
const auto lhs = [&] { return ReadMask(inst.src0); };
|
||||
const auto rhs = [&] { return ReadMask(inst.src1); };
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_AND_B64: return ir.LogicalAnd(lhs(), rhs());
|
||||
case Decoder::Opcode::S_OR_B64: return ir.LogicalOr(lhs(), rhs());
|
||||
case Decoder::Opcode::S_XOR_B64:
|
||||
return IR::U1(ir.Emit(IR::ValueOpcode::LogicalXor, {lhs(), rhs()}));
|
||||
case IR::Opcode::BitwiseAndNotU64: return ir.LogicalAnd(lhs(), ir.LogicalNot(rhs()));
|
||||
case IR::Opcode::BitwiseOrNotU64: return ir.LogicalOr(lhs(), ir.LogicalNot(rhs()));
|
||||
case IR::Opcode::BitwiseNandU64: return ir.LogicalNot(ir.LogicalAnd(lhs(), rhs()));
|
||||
case IR::Opcode::BitwiseNorU64: return ir.LogicalNot(ir.LogicalOr(lhs(), rhs()));
|
||||
case IR::Opcode::BitwiseXnorU64:
|
||||
case Decoder::Opcode::S_ANDN2_B64: return ir.LogicalAnd(lhs(), ir.LogicalNot(rhs()));
|
||||
case Decoder::Opcode::S_ORN2_B64: return ir.LogicalOr(lhs(), ir.LogicalNot(rhs()));
|
||||
case Decoder::Opcode::S_NAND_B64: return ir.LogicalNot(ir.LogicalAnd(lhs(), rhs()));
|
||||
case Decoder::Opcode::S_NOR_B64: return ir.LogicalNot(ir.LogicalOr(lhs(), rhs()));
|
||||
case Decoder::Opcode::S_XNOR_B64:
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
bool Translator::TranslateSimpleInteger(const IR::Instruction& inst) {
|
||||
bool Translator::TranslateSimpleInteger(const Decoder::Instruction& inst) {
|
||||
IR::ValueOpcode opcode {};
|
||||
IR::Type type = IR::Type::U32;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::AbsI32: opcode = IR::ValueOpcode::IAbs32; break;
|
||||
case IR::Opcode::IAddU32: opcode = IR::ValueOpcode::IAdd32; break;
|
||||
case IR::Opcode::ISubU32: opcode = IR::ValueOpcode::ISub32; break;
|
||||
case IR::Opcode::IMulU32: opcode = IR::ValueOpcode::IMul32; break;
|
||||
case IR::Opcode::UMulHighU32: opcode = IR::ValueOpcode::UMulHi; break;
|
||||
case IR::Opcode::SMulHighI32: opcode = IR::ValueOpcode::SMulHi; break;
|
||||
case IR::Opcode::IMinI32: opcode = IR::ValueOpcode::SMin32; break;
|
||||
case IR::Opcode::IMaxI32: opcode = IR::ValueOpcode::SMax32; break;
|
||||
case IR::Opcode::UMinU32: opcode = IR::ValueOpcode::UMin32; break;
|
||||
case IR::Opcode::UMaxU32: opcode = IR::ValueOpcode::UMax32; break;
|
||||
case IR::Opcode::IMin3I32: opcode = IR::ValueOpcode::SMinTri32; break;
|
||||
case IR::Opcode::IMax3I32: opcode = IR::ValueOpcode::SMaxTri32; break;
|
||||
case IR::Opcode::IMed3I32: opcode = IR::ValueOpcode::SMedTri32; break;
|
||||
case IR::Opcode::UMin3U32: opcode = IR::ValueOpcode::UMinTri32; break;
|
||||
case IR::Opcode::UMax3U32: opcode = IR::ValueOpcode::UMaxTri32; break;
|
||||
case IR::Opcode::UMed3U32: opcode = IR::ValueOpcode::UMedTri32; break;
|
||||
case IR::Opcode::BitwiseAndU32: opcode = IR::ValueOpcode::BitwiseAnd32; break;
|
||||
case IR::Opcode::BitwiseOrU32: opcode = IR::ValueOpcode::BitwiseOr32; break;
|
||||
case IR::Opcode::BitwiseXorU32: opcode = IR::ValueOpcode::BitwiseXor32; break;
|
||||
case IR::Opcode::BitwiseNotU32: opcode = IR::ValueOpcode::BitwiseNot32; break;
|
||||
case IR::Opcode::BitReverseU32: opcode = IR::ValueOpcode::BitReverse32; break;
|
||||
case IR::Opcode::BitCountU32: opcode = IR::ValueOpcode::BitCount32; break;
|
||||
case IR::Opcode::BitCountU64:
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_ABS_I32: opcode = IR::ValueOpcode::IAbs32; break;
|
||||
case Decoder::Opcode::S_MUL_I32:
|
||||
case Decoder::Opcode::S_MULK_I32:
|
||||
case Decoder::Opcode::V_MUL_LO_U32:
|
||||
case Decoder::Opcode::V_MUL_LO_I32: opcode = IR::ValueOpcode::IMul32; break;
|
||||
case Decoder::Opcode::S_MUL_HI_U32:
|
||||
case Decoder::Opcode::V_MUL_HI_U32: opcode = IR::ValueOpcode::UMulHi; break;
|
||||
case Decoder::Opcode::V_MUL_HI_I32: opcode = IR::ValueOpcode::SMulHi; break;
|
||||
case Decoder::Opcode::V_ADD_NC_U32: opcode = IR::ValueOpcode::IAdd32; break;
|
||||
case Decoder::Opcode::V_SUB_NC_U32:
|
||||
case Decoder::Opcode::V_SUBREV_NC_U32: opcode = IR::ValueOpcode::ISub32; break;
|
||||
case Decoder::Opcode::V_MIN_I32: opcode = IR::ValueOpcode::SMin32; break;
|
||||
case Decoder::Opcode::V_MAX_I32: opcode = IR::ValueOpcode::SMax32; break;
|
||||
case Decoder::Opcode::V_MIN_U32: opcode = IR::ValueOpcode::UMin32; break;
|
||||
case Decoder::Opcode::V_MAX_U32: opcode = IR::ValueOpcode::UMax32; break;
|
||||
case Decoder::Opcode::V_MIN3_I32: opcode = IR::ValueOpcode::SMinTri32; break;
|
||||
case Decoder::Opcode::V_MAX3_I32: opcode = IR::ValueOpcode::SMaxTri32; break;
|
||||
case Decoder::Opcode::V_MED3_I32: opcode = IR::ValueOpcode::SMedTri32; break;
|
||||
case Decoder::Opcode::V_MIN3_U32: opcode = IR::ValueOpcode::UMinTri32; break;
|
||||
case Decoder::Opcode::V_MAX3_U32: opcode = IR::ValueOpcode::UMaxTri32; break;
|
||||
case Decoder::Opcode::V_MED3_U32: opcode = IR::ValueOpcode::UMedTri32; break;
|
||||
case Decoder::Opcode::S_AND_B32:
|
||||
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;
|
||||
type = IR::Type::U64;
|
||||
break;
|
||||
case IR::Opcode::FindLsbU32: opcode = IR::ValueOpcode::FindILsb32; break;
|
||||
case IR::Opcode::ShiftLeftLogicalU32: opcode = IR::ValueOpcode::ShiftLeftLogical32; break;
|
||||
case IR::Opcode::ShiftRightLogicalU32: opcode = IR::ValueOpcode::ShiftRightLogical32; break;
|
||||
case IR::Opcode::ShiftRightArithmeticI32:
|
||||
case Decoder::Opcode::S_FF1_I32_B32:
|
||||
case Decoder::Opcode::V_FFBL_B32: opcode = IR::ValueOpcode::FindILsb32; break;
|
||||
case Decoder::Opcode::S_LSHL_B32:
|
||||
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;
|
||||
break;
|
||||
case IR::Opcode::ShiftLeftLogicalU64:
|
||||
case Decoder::Opcode::S_LSHL_B64:
|
||||
case Decoder::Opcode::V_LSHLREV_B64:
|
||||
opcode = IR::ValueOpcode::ShiftLeftLogical64;
|
||||
type = IR::Type::U64;
|
||||
break;
|
||||
case IR::Opcode::ShiftRightLogicalU64:
|
||||
case Decoder::Opcode::S_LSHR_B64:
|
||||
case Decoder::Opcode::V_LSHRREV_B64:
|
||||
opcode = IR::ValueOpcode::ShiftRightLogical64;
|
||||
type = IR::Type::U64;
|
||||
break;
|
||||
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;
|
||||
for (uint32_t index = 0; index < inst.src_count; 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 ||
|
||||
opcode == IR::ValueOpcode::ShiftRightLogical32 ||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
|
||||
bool Translator::TranslateComposedInteger(const Decoder::Instruction& inst) {
|
||||
const auto binary_u32 = [&](auto operation) {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
return operation(lhs, rhs);
|
||||
};
|
||||
|
||||
IR::Value result;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::BitwiseAndNotU32:
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_ANDN2_B32:
|
||||
result = binary_u32(
|
||||
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseAnd(lhs, ir.BitwiseNot(rhs)); });
|
||||
break;
|
||||
case IR::Opcode::BitwiseOrNotU32:
|
||||
case Decoder::Opcode::S_ORN2_B32:
|
||||
result = binary_u32(
|
||||
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseOr(lhs, ir.BitwiseNot(rhs)); });
|
||||
break;
|
||||
case IR::Opcode::BitwiseNandU32:
|
||||
case Decoder::Opcode::S_NAND_B32:
|
||||
result = binary_u32(
|
||||
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseAnd(lhs, rhs)); });
|
||||
break;
|
||||
case IR::Opcode::BitwiseNorU32:
|
||||
case Decoder::Opcode::S_NOR_B32:
|
||||
result = binary_u32(
|
||||
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseOr(lhs, rhs)); });
|
||||
break;
|
||||
case IR::Opcode::BitwiseXnorU32:
|
||||
case Decoder::Opcode::S_XNOR_B32:
|
||||
case Decoder::Opcode::V_XNOR_B32:
|
||||
result = binary_u32(
|
||||
[&](IR::U32 lhs, IR::U32 rhs) { return ir.BitwiseNot(ir.BitwiseXor(lhs, rhs)); });
|
||||
break;
|
||||
case IR::Opcode::BitwiseAndOrU32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
const auto add = ReadU32(inst.src[2]);
|
||||
case Decoder::Opcode::V_AND_OR_B32: {
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
const auto add = ReadU32(inst.src2);
|
||||
result = ir.BitwiseOr(ir.BitwiseAnd(lhs, rhs), add);
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitwiseOr3U32:
|
||||
result = ir.BitwiseOr(ir.BitwiseOr(ReadU32(inst.src[0]), ReadU32(inst.src[1])),
|
||||
ReadU32(inst.src[2]));
|
||||
case Decoder::Opcode::V_OR3_B32:
|
||||
result = ir.BitwiseOr(ir.BitwiseOr(ReadU32(inst.src0), ReadU32(inst.src1)),
|
||||
ReadU32(inst.src2));
|
||||
break;
|
||||
case IR::Opcode::BitwiseXor3U32:
|
||||
result = ir.BitwiseXor(ir.BitwiseXor(ReadU32(inst.src[0]), ReadU32(inst.src[1])),
|
||||
ReadU32(inst.src[2]));
|
||||
case Decoder::Opcode::V_XOR3_B32:
|
||||
result = ir.BitwiseXor(ir.BitwiseXor(ReadU32(inst.src0), ReadU32(inst.src1)),
|
||||
ReadU32(inst.src2));
|
||||
break;
|
||||
case IR::Opcode::FindLsbU64: {
|
||||
const auto source = ExtractU64(ReadU64(inst.src[0]));
|
||||
case Decoder::Opcode::S_FF1_I32_B64: {
|
||||
const auto source = ExtractU64(ReadU64(inst.src0));
|
||||
const auto low_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[0]}));
|
||||
const auto high_lsb = IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {source[1]}));
|
||||
const auto high_position = ir.IAdd(high_lsb, IR::U32(IR::Value(32u)));
|
||||
@@ -267,16 +319,16 @@ bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
|
||||
high_position, IR::U32(IR::Value(0xffffffffu))));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::FindMsbFromHighU32: {
|
||||
const auto source = ReadU32(inst.src[0]);
|
||||
case Decoder::Opcode::V_FFBH_U32: {
|
||||
const auto source = ReadU32(inst.src0);
|
||||
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {source}));
|
||||
const auto position = ir.ISub(IR::U32(IR::Value(31u)), msb);
|
||||
result = ir.Select(ir.INotEqual(source, IR::U32(IR::Value(0u))), position,
|
||||
IR::U32(IR::Value(0xffffffffu)));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::FindMsbFromHighU64: {
|
||||
const auto source = ReadU64(inst.src[0]);
|
||||
case Decoder::Opcode::S_FLBIT_I32_B64: {
|
||||
const auto source = ReadU64(inst.src0);
|
||||
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb64, {source}));
|
||||
const auto position = ir.ISub(IR::U32(IR::Value(63u)), msb);
|
||||
const auto nonzero = IR::U1(
|
||||
@@ -287,11 +339,21 @@ bool Translator::TranslateComposedInteger(const IR::Instruction& inst) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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 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) {
|
||||
@@ -312,26 +374,26 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
|
||||
};
|
||||
|
||||
IR::Value result;
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::IMadI24U32:
|
||||
case IR::Opcode::UMadU24U32:
|
||||
case IR::Opcode::IMulI24U32:
|
||||
case IR::Opcode::UMulU24U32: {
|
||||
const bool sign =
|
||||
inst.op == IR::Opcode::IMadI24U32 || inst.op == IR::Opcode::IMulI24U32;
|
||||
const auto lhs = extract(ReadU32(inst.src[0]), imm(0), imm(24), sign);
|
||||
const auto rhs = extract(ReadU32(inst.src[1]), imm(0), imm(24), sign);
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_MAD_I32_I24:
|
||||
case Decoder::Opcode::V_MAD_U32_U24:
|
||||
case Decoder::Opcode::V_MUL_I32_I24:
|
||||
case Decoder::Opcode::V_MUL_U32_U24: {
|
||||
const bool sign = inst.opcode == Decoder::Opcode::V_MAD_I32_I24 ||
|
||||
inst.opcode == Decoder::Opcode::V_MUL_I32_I24;
|
||||
const auto lhs = extract(ReadU32(inst.src0), imm(0), imm(24), sign);
|
||||
const auto rhs = extract(ReadU32(inst.src1), imm(0), imm(24), sign);
|
||||
auto value = ir.IMul(lhs, rhs);
|
||||
if (inst.src_count == 3) {
|
||||
value = ir.IAdd(value, ReadU32(inst.src[2]));
|
||||
value = ir.IAdd(value, ReadU32(inst.src2));
|
||||
}
|
||||
result = value;
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::UMadU64U32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
const auto add = ExtractU64(ReadU64(inst.src[2]));
|
||||
case Decoder::Opcode::V_MAD_U64_U32: {
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
const auto add = ExtractU64(ReadU64(inst.src2));
|
||||
const auto mul_low = ir.IMul(lhs, rhs);
|
||||
const auto mul_high = IR::U32(ir.Emit(IR::ValueOpcode::UMulHi, {lhs, rhs}));
|
||||
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 high = ir.IAdd(high0, ir.Select(carry_low, imm(1), imm(0)));
|
||||
const auto carry1 = ir.ULessThan(high, high0);
|
||||
WriteOperand(inst.dst, ir.ConstructU64(low, high));
|
||||
if (inst.dst2.kind != IR::OperandKind::Null) {
|
||||
WriteOperand(DestinationOperand(inst), ir.ConstructU64(low, high));
|
||||
if (inst.dst2.kind != Decoder::OperandKind::Null &&
|
||||
inst.dst2.kind != Decoder::OperandKind::Unknown) {
|
||||
WriteMask(inst.dst2, ir.LogicalOr(carry0, carry1));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::SadU32: {
|
||||
const auto lhs = ReadU32(inst.src[0]);
|
||||
const auto rhs = ReadU32(inst.src[1]);
|
||||
case Decoder::Opcode::V_SAD_U32: {
|
||||
const auto lhs = ReadU32(inst.src0);
|
||||
const auto rhs = ReadU32(inst.src1);
|
||||
const auto lo = IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {lhs, rhs}));
|
||||
const auto hi = IR::U32(ir.Emit(IR::ValueOpcode::UMax32, {lhs, rhs}));
|
||||
result = ir.IAdd(ir.ISub(hi, lo), ReadU32(inst.src[2]));
|
||||
result = ir.IAdd(ir.ISub(hi, lo), ReadU32(inst.src2));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::IAdd3U32:
|
||||
result =
|
||||
ir.IAdd(ir.IAdd(ReadU32(inst.src[0]), ReadU32(inst.src[1])), ReadU32(inst.src[2]));
|
||||
case Decoder::Opcode::V_ADD3_U32:
|
||||
result = ir.IAdd(ir.IAdd(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
|
||||
break;
|
||||
case IR::Opcode::BitClearU32:
|
||||
case IR::Opcode::BitSetU32: {
|
||||
const auto bit = ir.ShiftLeftLogical(imm(1), mask(ReadU32(inst.src[1]), 31u));
|
||||
result = inst.op == IR::Opcode::BitClearU32
|
||||
? IR::Value(ir.BitwiseAnd(ReadU32(inst.src[0]), ir.BitwiseNot(bit)))
|
||||
: IR::Value(ir.BitwiseOr(ReadU32(inst.src[0]), bit));
|
||||
case Decoder::Opcode::S_BITSET0_B32:
|
||||
case Decoder::Opcode::S_BITSET1_B32: {
|
||||
const auto bit = ir.ShiftLeftLogical(imm(1), mask(ReadU32(inst.src0), 31u));
|
||||
result = inst.opcode == Decoder::Opcode::S_BITSET0_B32
|
||||
? IR::Value(ir.BitwiseAnd(ReadU32(inst.dst), ir.BitwiseNot(bit)))
|
||||
: IR::Value(ir.BitwiseOr(ReadU32(inst.dst), bit));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitCountAddU32:
|
||||
result = ir.IAdd(IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {ReadU32(inst.src[0])})),
|
||||
ReadU32(inst.src[1]));
|
||||
case Decoder::Opcode::V_BCNT_U32_B32:
|
||||
result = ir.IAdd(IR::U32(ir.Emit(IR::ValueOpcode::BitCount32, {ReadU32(inst.src0)})),
|
||||
ReadU32(inst.src1));
|
||||
break;
|
||||
case IR::Opcode::MaskedBitCountLowU32:
|
||||
case IR::Opcode::MaskedBitCountHighU32: {
|
||||
case Decoder::Opcode::V_MBCNT_LO_U32_B32:
|
||||
case Decoder::Opcode::V_MBCNT_HI_U32_B32: {
|
||||
const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId));
|
||||
const auto local = mask(lane, 31u);
|
||||
const auto below = ir.ISub(ir.ShiftLeftLogical(imm(1), local), imm(1));
|
||||
const auto high_lane =
|
||||
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, 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})),
|
||||
ReadU32(inst.src[1]));
|
||||
ReadU32(inst.src1));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitReplicateB64B32: {
|
||||
case Decoder::Opcode::S_BITREPLICATE_B64_B32: {
|
||||
const auto replicate = [&](IR::U32 value) {
|
||||
auto bits = ir.BitwiseOr(value, ir.ShiftLeftLogical(value, imm(8)));
|
||||
bits = ir.BitwiseAnd(bits, imm(0x00ff00ffu));
|
||||
@@ -397,12 +459,12 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) {
|
||||
bits = ir.BitwiseAnd(bits, imm(0x55555555u));
|
||||
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)),
|
||||
replicate(ir.ShiftRightLogical(source, imm(16))));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::QuadmaskB64: {
|
||||
case Decoder::Opcode::S_QUADMASK_B64: {
|
||||
const auto compact = [&](IR::U32 value) {
|
||||
auto bits = ir.BitwiseOr(value, ir.ShiftRightLogical(value, imm(1)));
|
||||
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);
|
||||
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 =
|
||||
ir.BitwiseOr(compact(source[0]), ir.ShiftLeftLogical(compact(source[1]), imm(8)));
|
||||
result = ir.ConstructU64(quads, imm(0));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitFieldMaskU32: {
|
||||
const auto count = mask(ReadU32(inst.src[0]), 31u);
|
||||
const auto offset = mask(ReadU32(inst.src[1]), 31u);
|
||||
case Decoder::Opcode::S_BFM_B32:
|
||||
case Decoder::Opcode::V_BFM_B32: {
|
||||
const auto count = mask(ReadU32(inst.src0), 31u);
|
||||
const auto offset = mask(ReadU32(inst.src1), 31u);
|
||||
result =
|
||||
ir.Emit(IR::ValueOpcode::BitFieldInsert, {imm(0), imm(0xffffffffu), offset, count});
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitFieldMaskU64: {
|
||||
const auto count = mask(ReadU32(inst.src[0]), 63u);
|
||||
const auto offset = mask(ReadU32(inst.src[1]), 63u);
|
||||
case Decoder::Opcode::S_BFM_B64: {
|
||||
const auto count = mask(ReadU32(inst.src0), 63u);
|
||||
const auto offset = mask(ReadU32(inst.src1), 63u);
|
||||
result = ir.Emit(IR::ValueOpcode::ShiftLeftLogical64, {right_mask64(count), offset});
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitFieldExtractU32:
|
||||
case IR::Opcode::BitFieldExtractI32: {
|
||||
const auto source = ReadU32(inst.src[0]);
|
||||
const auto field = ReadU32(inst.src[1]);
|
||||
case Decoder::Opcode::S_BFE_U32:
|
||||
case Decoder::Opcode::S_BFE_I32: {
|
||||
const auto source = ReadU32(inst.src0);
|
||||
const auto field = ReadU32(inst.src1);
|
||||
const auto offset = extract(field, imm(0), imm(5), false);
|
||||
const auto count =
|
||||
IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {extract(field, imm(16), imm(7), false),
|
||||
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::BitFieldUExtract;
|
||||
result = ir.Emit(opcode, {source, offset, count});
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitFieldExtractU64: {
|
||||
const auto source = ReadU64(inst.src[0]);
|
||||
const auto field = ReadU32(inst.src[1]);
|
||||
case Decoder::Opcode::S_BFE_U64: {
|
||||
const auto source = ReadU64(inst.src0);
|
||||
const auto field = ReadU32(inst.src1);
|
||||
const auto offset = extract(field, imm(0), imm(6), false);
|
||||
const auto raw_count = extract(field, imm(16), imm(7), false);
|
||||
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)});
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitFieldExtract3U32:
|
||||
case IR::Opcode::BitFieldExtract3I32: {
|
||||
const auto source = ReadU32(inst.src[0]);
|
||||
const auto offset = mask(ReadU32(inst.src[1]), 31u);
|
||||
case Decoder::Opcode::V_BFE_U32:
|
||||
case Decoder::Opcode::V_BFE_I32: {
|
||||
const auto source = ReadU32(inst.src0);
|
||||
const auto offset = mask(ReadU32(inst.src1), 31u);
|
||||
const auto count =
|
||||
IR::U32(ir.Emit(IR::ValueOpcode::UMin32,
|
||||
{mask(ReadU32(inst.src[2]), 31u), ir.ISub(imm(32), offset)}));
|
||||
const auto opcode = inst.op == IR::Opcode::BitFieldExtract3I32
|
||||
{mask(ReadU32(inst.src2), 31u), ir.ISub(imm(32), offset)}));
|
||||
const auto opcode = inst.opcode == Decoder::Opcode::V_BFE_I32
|
||||
? IR::ValueOpcode::BitFieldSExtract
|
||||
: IR::ValueOpcode::BitFieldUExtract;
|
||||
result = ir.Emit(opcode, {source, offset, count});
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitFieldInsertSelectU32: {
|
||||
const auto bits = ReadU32(inst.src[0]);
|
||||
const auto insert = ReadU32(inst.src[1]);
|
||||
const auto base = ReadU32(inst.src[2]);
|
||||
case Decoder::Opcode::V_BFI_B32: {
|
||||
const auto bits = ReadU32(inst.src0);
|
||||
const auto insert = ReadU32(inst.src1);
|
||||
const auto base = ReadU32(inst.src2);
|
||||
result =
|
||||
ir.BitwiseOr(ir.BitwiseAnd(bits, insert), ir.BitwiseAnd(ir.BitwiseNot(bits), base));
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitCompare0B32:
|
||||
case IR::Opcode::BitCompare1B32: {
|
||||
const auto value = ReadU32(inst.src[0]);
|
||||
const auto bit = extract(value, mask(ReadU32(inst.src[1]), 31u), imm(1), false);
|
||||
const auto expected = imm(inst.op == IR::Opcode::BitCompare1B32 ? 1u : 0u);
|
||||
WriteOperand(inst.dst, ir.IEqual(bit, expected));
|
||||
case Decoder::Opcode::S_BITCMP0_B32:
|
||||
case Decoder::Opcode::S_BITCMP1_B32: {
|
||||
const auto value = ReadU32(inst.src0);
|
||||
const auto bit = extract(value, mask(ReadU32(inst.src1), 31u), imm(1), false);
|
||||
const auto expected = imm(inst.opcode == Decoder::Opcode::S_BITCMP1_B32 ? 1u : 0u);
|
||||
WriteCompareResult(inst.dst, ir.IEqual(bit, expected));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::AlignBitU32: {
|
||||
const auto hi = ReadU32(inst.src[0]);
|
||||
const auto lo = ReadU32(inst.src[1]);
|
||||
const auto shift = mask(ReadU32(inst.src[2]), 31u);
|
||||
case Decoder::Opcode::V_ALIGNBIT_B32: {
|
||||
const auto hi = ReadU32(inst.src0);
|
||||
const auto lo = ReadU32(inst.src1);
|
||||
const auto shift = mask(ReadU32(inst.src2), 31u);
|
||||
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 = ir.Select(ir.INotEqual(shift, imm(0)), hi_part_raw, imm(0));
|
||||
result = ir.BitwiseOr(lo_part, hi_part);
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::AlignByteU32: {
|
||||
const auto hi = ReadU32(inst.src[0]);
|
||||
const auto lo = ReadU32(inst.src[1]);
|
||||
const auto byte_offset = mask(ReadU32(inst.src[2]), 31u);
|
||||
case Decoder::Opcode::V_ALIGNBYTE_B32: {
|
||||
const auto hi = ReadU32(inst.src0);
|
||||
const auto lo = ReadU32(inst.src1);
|
||||
const auto byte_offset = mask(ReadU32(inst.src2), 31u);
|
||||
const auto bit_offset = ir.ShiftLeftLogical(byte_offset, imm(3));
|
||||
const auto concatenated = ir.ConstructU64(lo, hi);
|
||||
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));
|
||||
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 =
|
||||
ir.IAdd(ir.ShiftLeftLogical(ReadU32(inst.src[0]), mask(ReadU32(inst.src[1]), 31u)),
|
||||
ReadU32(inst.src[2]));
|
||||
ir.IAdd(ir.BitwiseXor(ReadU32(inst.src0), ReadU32(inst.src1)), ReadU32(inst.src2));
|
||||
break;
|
||||
case IR::Opcode::AddShiftLeftU32:
|
||||
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:
|
||||
case Decoder::Opcode::V_LSHL_OR_B32:
|
||||
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;
|
||||
case IR::Opcode::SelectMaskU32:
|
||||
result = ir.Select(ReadMask(inst.src[0]), ReadU32(inst.src[1]), ReadU32(inst.src[2]));
|
||||
break;
|
||||
case IR::Opcode::SelectF32Bits:
|
||||
case IR::Opcode::SelectMaskF32Bits: {
|
||||
const auto condition = inst.op == IR::Opcode::SelectMaskF32Bits
|
||||
? ReadMask(inst.src[0])
|
||||
: ReadCondition(inst.src[0]);
|
||||
result = ir.Emit(IR::ValueOpcode::SelectF32,
|
||||
{condition, ReadOperand(inst.src[1], IR::Type::F32),
|
||||
ReadOperand(inst.src[2], IR::Type::F32)});
|
||||
case Decoder::Opcode::V_CNDMASK_B32: {
|
||||
Decoder::Operand mask_operand;
|
||||
mask_operand.kind = Decoder::OperandKind::VccLo;
|
||||
if (inst.src_count >= 3u) {
|
||||
mask_operand = inst.src2;
|
||||
}
|
||||
const auto condition = ReadMask(mask_operand);
|
||||
if (inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute) {
|
||||
result = ir.Emit(IR::ValueOpcode::SelectF32,
|
||||
{condition, ReadOperand(inst.src1, IR::Type::F32),
|
||||
ReadOperand(inst.src0, IR::Type::F32)});
|
||||
} else {
|
||||
result = ir.Select(condition, ReadU32(inst.src1), ReadU32(inst.src0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::SelectU64: {
|
||||
const auto condition = ReadCondition(inst.src[0]);
|
||||
const auto selected_mask =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
|
||||
{condition, ReadMask(inst.src[1]), ReadMask(inst.src[2])}));
|
||||
const auto selected_mask_valid =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMaskValid(inst.src[1]),
|
||||
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;
|
||||
case Decoder::Opcode::S_PACK_LL_B32_B16:
|
||||
case Decoder::Opcode::S_PACK_LH_B32_B16:
|
||||
case Decoder::Opcode::S_PACK_HH_B32_B16:
|
||||
case Decoder::Opcode::V_CVT_PK_U16_U32:
|
||||
case Decoder::Opcode::V_CVT_PK_I16_I32: {
|
||||
const bool high0 = inst.opcode == Decoder::Opcode::S_PACK_HH_B32_B16;
|
||||
const bool high1 = inst.opcode == Decoder::Opcode::S_PACK_LH_B32_B16 || high0;
|
||||
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 =
|
||||
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 =
|
||||
ir.BitwiseOr(mask(lo, 0xffffu), ir.ShiftLeftLogical(mask(hi, 0xffffu), imm(16)));
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
} // 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
|
||||
|
||||
#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 {
|
||||
|
||||
bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result,
|
||||
const ShaderVertexInputInfo* vertex_input_info,
|
||||
const ShaderPixelInputInfo* pixel_input_info,
|
||||
const ShaderComputeInputInfo* compute_input_info, std::string* error);
|
||||
struct EmbeddedFetchLoad {
|
||||
uint32_t pc = 0;
|
||||
int attrib_id = -1;
|
||||
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
|
||||
@@ -5,58 +5,59 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail {
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
class Translator {
|
||||
public:
|
||||
Translator(IR::ValueProgram& program, IR::Block* block, uint32_t vector_limit,
|
||||
uint32_t wave_size)
|
||||
: value_program(program), ir(block), current_vector_limit(vector_limit),
|
||||
Translator(IR::Program& program, IR::Block* block, uint32_t vector_limit, uint32_t wave_size)
|
||||
: program(program), ir(block), current_vector_limit(vector_limit),
|
||||
current_wave_size(wave_size) {}
|
||||
|
||||
bool TranslateBlock(const IR::BasicBlock& source, std::string* error);
|
||||
bool AddBranchCondition(const IR::BasicBlock& source, IR::ValueBlockInfo& info,
|
||||
std::string* error);
|
||||
bool TranslateInstruction(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
uint32_t component_count);
|
||||
bool AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info, std::string* error);
|
||||
|
||||
private:
|
||||
IR::Operand OffsetOperand(const IR::Operand& operand, uint32_t offset);
|
||||
IR::Operand ScalarDestinationOperand(const IR::Operand& operand, uint32_t offset);
|
||||
IR::Operand PlainOperand(const IR::Operand& operand);
|
||||
IR::U32 ReadPcRelativeU32(const IR::Operand& operand);
|
||||
Decoder::Operand SourceAt(const Decoder::Instruction& inst, uint32_t index);
|
||||
Decoder::Operand DestinationOperand(const Decoder::Instruction& inst);
|
||||
Decoder::Operand OffsetOperand(const Decoder::Operand& operand, uint32_t offset);
|
||||
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);
|
||||
IR::U32 ReadRawU32(const IR::Operand& operand);
|
||||
IR::U32 ReadRawU32(const Decoder::Operand& operand);
|
||||
IR::U32 ReadScalarCode(uint32_t code);
|
||||
IR::U32 ApplyBitSourceModifiers(const IR::Operand& operand, IR::U32 value);
|
||||
IR::Value ReadOperand(const IR::Operand& operand, IR::Type type);
|
||||
IR::U32 ApplyBitSourceModifiers(const Decoder::Operand& operand, IR::U32 value);
|
||||
IR::Value ReadOperand(const Decoder::Operand& operand, IR::Type type);
|
||||
IR::U1 ThreadBit(IR::U32 low);
|
||||
void WriteRawU32(const IR::Operand& operand, IR::U32 value);
|
||||
IR::F32 ApplyF32ResultModifiers(const IR::Operand& operand, IR::F32 value);
|
||||
void WriteOperand(const IR::Operand& operand, IR::Value value);
|
||||
void WriteRawU32(const Decoder::Operand& operand, IR::U32 value);
|
||||
IR::F32 ApplyF32ResultModifiers(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteOperand(const Decoder::Operand& operand, IR::Value value);
|
||||
IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high);
|
||||
void WriteF16(const IR::Operand& operand, IR::F32 value);
|
||||
void WriteU16(const IR::Operand& operand, IR::U32 value);
|
||||
IR::U32 ReadU32(const IR::Operand& operand);
|
||||
std::array<IR::U32, 2> ReadU32Pair(const IR::Operand& operand);
|
||||
IR::U64 ReadU64(const IR::Operand& operand);
|
||||
IR::F32 ReadF16LaneAsF32(const IR::Operand& operand, bool high_lane, bool packed = false);
|
||||
IR::F32 ReadF16AsF32(const IR::Operand& operand);
|
||||
IR::F32 ReadMixF32(const IR::Operand& operand);
|
||||
IR::U32 ReadU16LaneRaw(const IR::Operand& operand, bool high_lane);
|
||||
IR::U32 ReadU16LaneAsU32(const IR::Operand& operand, bool high_lane, bool sign_extend);
|
||||
IR::U32 ReadU16AsU32(const IR::Operand& operand, bool sign_extend);
|
||||
IR::U32 ReadF16LaneBits(const IR::Operand& operand, bool high_lane);
|
||||
void WriteF16(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteU16(const Decoder::Operand& operand, IR::U32 value);
|
||||
IR::U32 ReadU32(const Decoder::Operand& operand);
|
||||
std::array<IR::U32, 2> ReadU32Pair(const Decoder::Operand& operand);
|
||||
IR::U64 ReadU64(const Decoder::Operand& operand);
|
||||
IR::F32 ReadF16LaneAsF32(const Decoder::Operand& operand, bool high_lane, bool packed = false);
|
||||
IR::F32 ReadF16AsF32(const Decoder::Operand& operand);
|
||||
IR::F32 ReadMixF32(const Decoder::Operand& operand);
|
||||
IR::U32 ReadU16LaneRaw(const Decoder::Operand& operand, bool high_lane);
|
||||
IR::U32 ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane, bool sign_extend);
|
||||
IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend);
|
||||
IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
|
||||
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
|
||||
void WriteU32Pair(const IR::Operand& operand, const std::array<IR::U32, 2>& value);
|
||||
IR::U1 ReadCondition(const IR::Operand& operand);
|
||||
IR::U32 ConditionBit(const IR::Operand& operand);
|
||||
IR::U1 ReadMask(const IR::Operand& operand);
|
||||
IR::U1 ReadMaskValid(const IR::Operand& operand);
|
||||
void WriteMask(const IR::Operand& operand, IR::U1 value);
|
||||
void WriteMask64(const IR::Operand& operand, IR::U1 value);
|
||||
void WriteCompareResult(const IR::Operand& operand, IR::U1 value);
|
||||
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
|
||||
IR::U1 ReadCondition(const Decoder::Operand& operand);
|
||||
IR::U32 ConditionBit(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMask(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
|
||||
void WriteMask(const Decoder::Operand& operand, IR::U1 value);
|
||||
void WriteMask64(const Decoder::Operand& operand, IR::U1 value);
|
||||
void WriteCompareResult(const Decoder::Operand& operand, IR::U1 value);
|
||||
|
||||
IR::MemoryFlags AddMemoryInfo(const IR::Instruction& inst);
|
||||
IR::ExportFlags AddExportInfo(const IR::Instruction& inst);
|
||||
IR::MemoryFlags AddMemoryInfo(const IR::MemoryInfo& memory, uint32_t pc);
|
||||
IR::ExportFlags AddExportInfo(const Decoder::Instruction& inst);
|
||||
struct AddressOperands {
|
||||
IR::Value resource;
|
||||
IR::Value low;
|
||||
@@ -67,31 +68,31 @@ private:
|
||||
IR::U32 offset;
|
||||
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::Value GetBufferResource(const IR::MemoryInfo& memory);
|
||||
IR::Value GetAddressResource(IR::Value low, IR::Value high);
|
||||
IR::Value GetScalarAddressResource(uint32_t base);
|
||||
IR::Value GetImageResource(const IR::MemoryInfo& memory);
|
||||
IR::Value GetSamplerResource(const IR::MemoryInfo& memory);
|
||||
IR::Value MakeImageAddress(const IR::Instruction& inst, const IR::Operand& base);
|
||||
IR::Value ConstructU32x4(const IR::Operand& base, uint32_t count);
|
||||
void WriteImageComponents(const IR::Operand& dst, IR::Value value, const IR::MemoryInfo& memory,
|
||||
uint32_t component_limit);
|
||||
IR::ValueOpcode ImageAtomicOpcode(IR::Opcode opcode);
|
||||
BufferAddress ReadBufferAddress(const IR::Instruction& inst, uint32_t source_offset);
|
||||
IR::Value MakeImageAddress(const Decoder::Instruction& inst, const Decoder::Operand& base);
|
||||
IR::Value ConstructU32x4(const Decoder::Operand& base, uint32_t count);
|
||||
void WriteImageComponents(const Decoder::Operand& dst, IR::Value value,
|
||||
const IR::MemoryInfo& memory, uint32_t component_limit);
|
||||
IR::ValueOpcode ImageAtomicOpcode(Decoder::Opcode opcode);
|
||||
BufferAddress ReadBufferAddress(const Decoder::Instruction& inst, uint32_t source_offset);
|
||||
IR::U32 WidenSubdword(IR::Value value, uint32_t bits, bool sign);
|
||||
IR::Value NarrowSubdword(IR::U32 value, uint32_t bits);
|
||||
IR::ValueOpcode BufferAtomicOpcode(IR::Opcode opcode);
|
||||
IR::ValueOpcode SharedAtomicOpcode(IR::Opcode opcode);
|
||||
bool TranslateScalarMemory(const IR::Instruction& inst);
|
||||
bool TranslateBufferLoad(const IR::Instruction& inst);
|
||||
bool TranslateBufferStore(const IR::Instruction& inst);
|
||||
bool TranslateAtomicMemory(const IR::Instruction& inst);
|
||||
bool TranslateFlatLoad(const IR::Instruction& inst);
|
||||
bool TranslateFlatStore(const IR::Instruction& inst);
|
||||
bool TranslateImageMemory(const IR::Instruction& inst);
|
||||
bool TranslateSharedMemory(const IR::Instruction& inst);
|
||||
IR::ValueOpcode BufferAtomicOpcode(Decoder::Opcode opcode);
|
||||
IR::ValueOpcode SharedAtomicOpcode(Decoder::Opcode opcode);
|
||||
bool TranslateScalarMemory(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateBufferLoad(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateBufferStore(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateAtomicMemory(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateFlatLoad(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateFlatStore(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateImageMemory(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateSharedMemory(const Decoder::Instruction& inst, std::string* error);
|
||||
|
||||
IR::F32 SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value);
|
||||
IR::U32 ConvertF32ToU32Saturated(IR::F32 value, float upper_bound, float safe_upper,
|
||||
@@ -101,34 +102,33 @@ private:
|
||||
uint32_t upper_result);
|
||||
IR::U32 PackU16Lanes(IR::U32 low, IR::U32 high);
|
||||
|
||||
bool TranslateInstruction(const IR::Instruction& inst);
|
||||
bool TranslateStateOperation(const IR::Instruction& inst);
|
||||
bool TranslateControlOperation(const IR::Instruction& inst);
|
||||
bool TranslateMove(const IR::Instruction& inst);
|
||||
bool TranslateLaneOperation(const IR::Instruction& inst);
|
||||
bool TranslateAttributeOperation(const IR::Instruction& inst);
|
||||
bool TranslateMemoryOperation(const IR::Instruction& inst);
|
||||
bool TranslateIntegerCompare(const IR::Instruction& inst);
|
||||
bool TranslateInteger16Compare(const IR::Instruction& inst);
|
||||
bool TranslateFloatCompare(const IR::Instruction& inst);
|
||||
bool TranslateConversion(const IR::Instruction& inst);
|
||||
bool TranslateInteger16Operation(const IR::Instruction& inst);
|
||||
bool TranslatePackedInteger16(const IR::Instruction& inst);
|
||||
bool TranslatePackedFloat16(const IR::Instruction& inst);
|
||||
bool TranslateFloat16Operation(const IR::Instruction& inst);
|
||||
bool TranslateFloatOperation(const IR::Instruction& inst);
|
||||
IR::U1 EvaluateU64Mask(const IR::Instruction& inst);
|
||||
bool TranslateU64MaskOperation(const IR::Instruction& inst);
|
||||
bool TranslateSimpleInteger(const IR::Instruction& inst);
|
||||
bool TranslateComposedInteger(const IR::Instruction& inst);
|
||||
bool TranslateExtendedInteger(const IR::Instruction& inst);
|
||||
bool TranslateStateOperation(const Decoder::Instruction& inst);
|
||||
bool TranslateControlOperation(const Decoder::Instruction& inst);
|
||||
bool TranslateMove(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateLaneOperation(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateAttributeOperation(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateMemoryOperation(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateIntegerCompare(const Decoder::Instruction& inst);
|
||||
bool TranslateInteger16Compare(const Decoder::Instruction& inst);
|
||||
bool TranslateFloatCompare(const Decoder::Instruction& inst);
|
||||
bool TranslateConversion(const Decoder::Instruction& inst);
|
||||
bool TranslateInteger16Operation(const Decoder::Instruction& inst);
|
||||
bool TranslatePackedInteger16(const Decoder::Instruction& inst);
|
||||
bool TranslatePackedFloat16(const Decoder::Instruction& inst);
|
||||
bool TranslateFloat16Operation(const Decoder::Instruction& inst);
|
||||
bool TranslateFloatOperation(const Decoder::Instruction& inst);
|
||||
IR::U1 EvaluateU64Mask(const Decoder::Instruction& inst);
|
||||
bool TranslateU64MaskOperation(const Decoder::Instruction& inst);
|
||||
bool TranslateSimpleInteger(const Decoder::Instruction& inst);
|
||||
bool TranslateComposedInteger(const Decoder::Instruction& inst);
|
||||
bool TranslateExtendedInteger(const Decoder::Instruction& inst);
|
||||
|
||||
IR::ValueProgram& value_program;
|
||||
IR::IREmitter ir;
|
||||
IR::Opcode current_opcode = IR::Opcode::ControlNop;
|
||||
uint32_t current_pc = 0;
|
||||
uint32_t current_vector_limit = 1;
|
||||
uint32_t current_wave_size = 64;
|
||||
IR::Program& program;
|
||||
IR::IREmitter ir;
|
||||
Decoder::Opcode current_opcode = Decoder::Opcode::UNKNOWN;
|
||||
uint32_t current_pc = 0;
|
||||
uint32_t current_vector_limit = 1;
|
||||
uint32_t current_wave_size = 64;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
+18
-8
@@ -1,11 +1,13 @@
|
||||
#include "graphics/shader/recompiler/ir/ValueProgram.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <map>
|
||||
#include <new>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
@@ -50,7 +52,7 @@ bool IsRuntimeRead(ValueOpcode opcode) {
|
||||
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) {
|
||||
left = left.Resolve();
|
||||
right = right.Resolve();
|
||||
@@ -93,7 +95,7 @@ bool EquivalentValue(const ValueProgram& program, Value left, Value right,
|
||||
|
||||
} // namespace
|
||||
|
||||
ValueProgram::~ValueProgram() {
|
||||
Program::~Program() {
|
||||
// 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.
|
||||
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;
|
||||
return EquivalentValue(program, left, right, visited);
|
||||
}
|
||||
|
||||
Value ResolveInvariantPhi(const ValueProgram& program, Value value) {
|
||||
Value ResolveInvariantPhi(const Program& program, Value value) {
|
||||
value = value.Resolve();
|
||||
const auto* root = value.TryInstruction();
|
||||
if (root == nullptr || root->GetOpcode() != ValueOpcode::Phi) {
|
||||
@@ -139,7 +149,7 @@ Value ResolveInvariantPhi(const ValueProgram& program, Value value) {
|
||||
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() ||
|
||||
program.blocks.size() != program.block_storage.size()) {
|
||||
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;
|
||||
}
|
||||
|
||||
void ResolveControlFlowIdentities(ValueProgram& program) {
|
||||
void ResolveControlFlowIdentities(Program& program) {
|
||||
for (auto& info: program.block_info) {
|
||||
info.condition = info.condition.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;
|
||||
size_t next_id = 1;
|
||||
for (const auto* block: program.blocks) {
|
||||
File diff suppressed because it is too large.
Load diff
@@ -6,7 +6,7 @@
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.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/shader.h"
|
||||
|
||||
@@ -19,47 +19,6 @@
|
||||
|
||||
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 {
|
||||
None,
|
||||
ScalarBuffer,
|
||||
@@ -144,44 +103,6 @@ struct ExportInfo {
|
||||
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 {
|
||||
std::array<uint32_t, 8> dwords = {};
|
||||
uint32_t dword_count = 0;
|
||||
@@ -522,6 +443,7 @@ struct ShaderInfo {
|
||||
std::vector<SampledResourcePair> sampled_pairs;
|
||||
std::vector<StageInput> inputs;
|
||||
std::vector<StageOutput> outputs;
|
||||
std::array<uint8_t, 32> vertex_fetch_components {};
|
||||
int32_t vertex_offset_sgpr = -1;
|
||||
bool has_bitwise_xor = false;
|
||||
|
||||
@@ -539,7 +461,50 @@ struct SpirvRequirements {
|
||||
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 {
|
||||
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;
|
||||
uint64_t shader_hash = 0;
|
||||
uint32_t wave_size = 64;
|
||||
@@ -549,8 +514,17 @@ struct Program {
|
||||
bool dispatcher_fallback = false;
|
||||
CFG::FailureKind cfg_failure_kind = CFG::FailureKind::None;
|
||||
std::string fallback_reason;
|
||||
std::vector<BasicBlock> blocks;
|
||||
std::shared_ptr<ValueProgram> values;
|
||||
std::vector<std::unique_ptr<Block>> block_storage;
|
||||
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;
|
||||
ShaderInfo info;
|
||||
bool resource_tracking_complete = false;
|
||||
@@ -561,15 +535,13 @@ struct Program {
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
#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/ValueProgram.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -16,7 +16,7 @@ constexpr uint32_t ImageBindingCount =
|
||||
|
||||
bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
|
||||
std::array<bool, NumScalarRegs> registers {};
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::GetUserData || !inst.HasUses()) {
|
||||
continue;
|
||||
@@ -47,16 +47,16 @@ void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
||||
|
||||
bool UsesGds(const Program& program, bool& uses_gds) {
|
||||
uses_gds = false;
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (SharedAccessOf(inst.GetOpcode()) == SharedAccess::None) {
|
||||
continue;
|
||||
}
|
||||
const auto index = inst.Flags<MemoryFlags>().index;
|
||||
if (index >= program.values->memory_info.size()) {
|
||||
if (index >= program.memory_info.size()) {
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
@@ -76,12 +76,6 @@ bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::stri
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (program.values == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = "typed value program is not available";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (push_constant_offset > NativePushConstantSize ||
|
||||
push_constant_offset % sizeof(uint32_t) != 0) {
|
||||
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));
|
||||
}
|
||||
const bool uses_flattened_runtime =
|
||||
!program.values->srt_reads.empty() ||
|
||||
!program.srt_reads.empty() ||
|
||||
std::ranges::any_of(program.info.images, [](const ImageResource& image) {
|
||||
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
|
||||
|
||||
ReadLaneStats EliminateReadLane(ValueProgram& program, uint32_t wave_size) {
|
||||
ReadLaneStats EliminateReadLane(Program& program, uint32_t wave_size) {
|
||||
ReadLaneStats stats;
|
||||
if (wave_size != 32u && wave_size != 64u) {
|
||||
return stats;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics/shader/recompiler/ir/ValueProgram.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
@@ -8,6 +8,6 @@ struct ReadLaneStats {
|
||||
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
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.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 <algorithm>
|
||||
@@ -105,10 +105,10 @@ bool DecodeBufferDescriptor(const DescriptorValue& descriptor, ShaderBufferResou
|
||||
}
|
||||
|
||||
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 &program.values->descriptor_sources[source];
|
||||
return &program.descriptor_sources[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();
|
||||
if (slot.IsImmediate() && slot.GetType() == Type::U32 && slot.U32() < slots.size()) {
|
||||
slots[slot.U32()] = 1u;
|
||||
pending.push_back(program.values->srt_reads[slot.U32()].value);
|
||||
pending.push_back(program.srt_reads[slot.U32()].value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -275,7 +275,7 @@ uint64_t AddressSpecialization(const AddressResource& resource,
|
||||
}
|
||||
|
||||
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++) {
|
||||
const auto& image = program.info.images[resource];
|
||||
if (image.indirect_root == resource) {
|
||||
@@ -305,8 +305,7 @@ bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& sn
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (program.values == nullptr ||
|
||||
snapshot.flattened_srt.size() != FlattenedRuntimeDwords(program)) {
|
||||
if (snapshot.flattened_srt.size() != FlattenedRuntimeDwords(program)) {
|
||||
if (error != nullptr) {
|
||||
*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<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 +
|
||||
program.info.samplers.size() + program.info.addresses.size());
|
||||
for (const auto& buffer: program.info.buffers) {
|
||||
@@ -925,13 +924,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
}
|
||||
}
|
||||
}
|
||||
if (program.values == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = "typed SSA is not ready";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
auto memory_info = program.values->memory_info;
|
||||
auto memory_info = program.memory_info;
|
||||
|
||||
struct SamplerUsage {
|
||||
bool native = false;
|
||||
@@ -980,7 +973,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
pair.sampler = point_sampler[pair.sampler];
|
||||
}
|
||||
}
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
const auto image_opcode = ImageOpcodeInfoOf(inst.GetOpcode());
|
||||
if (image_opcode.access == ImageAccess::None) {
|
||||
@@ -1023,7 +1016,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
}
|
||||
}
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#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/shader.h"
|
||||
|
||||
@@ -94,8 +94,7 @@ ImageMipMode MipMode(const MemoryInfo& memory) {
|
||||
|
||||
class Tracker {
|
||||
public:
|
||||
Tracker(Program& program, ValueProgram& values)
|
||||
: m_program(program), m_values(values), m_info(program.info) {
|
||||
explicit Tracker(Program& program): m_program(program), m_info(program.info) {
|
||||
m_info.buffers.clear();
|
||||
m_info.addresses.clear();
|
||||
m_info.images.clear();
|
||||
@@ -111,7 +110,7 @@ public:
|
||||
return Fail(0, error, "SRT plan is not ready");
|
||||
}
|
||||
PlanIndirectImages();
|
||||
for (auto* block: m_values.blocks) {
|
||||
for (auto* block: m_program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
if (!Collect(inst, error)) {
|
||||
return false;
|
||||
@@ -123,7 +122,7 @@ public:
|
||||
patch.handle->SetFlags<uint32_t>(patch.resource);
|
||||
}
|
||||
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;
|
||||
if (patch.has_sampler) {
|
||||
memory.sampler = patch.sampler;
|
||||
@@ -138,16 +137,16 @@ public:
|
||||
plan.handle->SetArg(dword, plan.key);
|
||||
}
|
||||
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();
|
||||
return std::ranges::any_of(m_indirect_images, [&](const IndirectImagePlan& plan) {
|
||||
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.resource_tracking_complete = true;
|
||||
return true;
|
||||
@@ -208,19 +207,19 @@ private:
|
||||
if (left.value.IsEmpty() || right.value.IsEmpty()) {
|
||||
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 {};
|
||||
};
|
||||
switch (inst->GetOpcode()) {
|
||||
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(merge(FindAddressPart(inst->Arg(1), active, visiting),
|
||||
FindAddressPart(inst->Arg(2), active, visiting), true));
|
||||
case ValueOpcode::Phi: {
|
||||
const auto invariant = ResolveInvariantPhi(m_values, value);
|
||||
const auto invariant = ResolveInvariantPhi(m_program, value);
|
||||
return finish(invariant.IsEmpty() ? AddressPart {}
|
||||
: FindAddressPart(invariant, active, visiting));
|
||||
}
|
||||
@@ -312,7 +311,7 @@ private:
|
||||
}
|
||||
bool same = true;
|
||||
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) {
|
||||
return candidate;
|
||||
@@ -342,10 +341,10 @@ private:
|
||||
return nullptr;
|
||||
}
|
||||
index = read.Flags<MemoryFlags>().index;
|
||||
if (index >= m_values.memory_info.size()) {
|
||||
if (index >= m_program.memory_info.size()) {
|
||||
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 &&
|
||||
memory.data_dwords == 1u
|
||||
? &memory
|
||||
@@ -353,7 +352,7 @@ private:
|
||||
}
|
||||
|
||||
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) {
|
||||
const auto op = inst.GetOpcode();
|
||||
if ((BufferAccessOf(op) == BufferAccess::None &&
|
||||
@@ -446,7 +445,7 @@ private:
|
||||
heap_handle = current_handle;
|
||||
if (dword == 0u) {
|
||||
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;
|
||||
}
|
||||
plan.memory[dword] = memory_index;
|
||||
@@ -537,7 +536,7 @@ private:
|
||||
}
|
||||
|
||||
void PlanIndirectImages() {
|
||||
for (auto* block: m_values.blocks) {
|
||||
for (auto* block: m_program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
if (ImageOpcodeInfoOf(inst.GetOpcode()).access == ImageAccess::None ||
|
||||
inst.NumArgs() == 0u) {
|
||||
@@ -588,7 +587,7 @@ private:
|
||||
return Fail(pc, error, "GetAddressResource must have two address dwords");
|
||||
}
|
||||
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) {
|
||||
const auto active = memory_inst.Arg(memory_inst.NumArgs() - 1u);
|
||||
if (memory.kind != ResourceKind::Flat ||
|
||||
@@ -797,14 +796,14 @@ private:
|
||||
return true;
|
||||
}
|
||||
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,
|
||||
fmt::format("memory metadata index {} is out of range", flags.index));
|
||||
}
|
||||
if (inst.NumArgs() == 0) {
|
||||
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)) {
|
||||
return true;
|
||||
}
|
||||
@@ -914,7 +913,7 @@ private:
|
||||
}
|
||||
bool alias = true;
|
||||
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]);
|
||||
}
|
||||
if (alias) {
|
||||
@@ -926,7 +925,6 @@ private:
|
||||
}
|
||||
|
||||
Program& m_program;
|
||||
ValueProgram& m_values;
|
||||
ShaderInfo m_info;
|
||||
std::vector<DescriptorSource> m_sources;
|
||||
std::vector<HandlePatch> m_handle_patches;
|
||||
@@ -937,13 +935,7 @@ private:
|
||||
} // namespace
|
||||
|
||||
bool TrackResources(Program& program, std::string* error) {
|
||||
if (program.values == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = "shader resource tracking requires typed IR";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return Tracker(program, *program.values).Run(error);
|
||||
return Tracker(program).Run(error);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
@@ -1,6 +1,6 @@
|
||||
#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 <fmt/format.h>
|
||||
@@ -66,7 +66,7 @@ bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case ValueOpcode::GetAttribute: {
|
||||
@@ -140,7 +140,7 @@ bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
|
||||
break;
|
||||
}
|
||||
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");
|
||||
}
|
||||
break;
|
||||
@@ -156,7 +156,7 @@ void CollectVertexInputs(const Program& program, const ShaderVertexInputInfo* ve
|
||||
AddInput(info, StageInputKind::VertexIndex, 0, 1, "gl_VertexIndex");
|
||||
AddInput(info, StageInputKind::InstanceIndex, 0, 1, "gl_InstanceIndex");
|
||||
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) {
|
||||
if (inst.GetOpcode() == ValueOpcode::GetAttribute) {
|
||||
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> interpolated {};
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (inst.GetOpcode() == ValueOpcode::GetAttribute) {
|
||||
interpolated[inst.Arg(0).U32()] = true;
|
||||
@@ -228,7 +228,7 @@ void CollectComputeInputs(const ShaderComputeInputInfo* compute, ShaderInfo& inf
|
||||
}
|
||||
|
||||
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) {
|
||||
if (inst.GetOpcode() != ValueOpcode::GetBuiltin) {
|
||||
continue;
|
||||
@@ -277,12 +277,12 @@ bool CollectOutputs(const Program& program, const ShaderVertexInputInfo* vertex,
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (const auto* block: program.values->blocks) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::SetAttribute) {
|
||||
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 (program.stage == ShaderType::Pixel && (export_info.en & 0x1u) != 0 &&
|
||||
pixel->ps_depth_export_enable) {
|
||||
@@ -360,12 +360,6 @@ bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (program.values == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = "typed value program is not available";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!ValidateOptions(program, options, error)) {
|
||||
return false;
|
||||
}
|
||||
@@ -377,7 +371,7 @@ bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::
|
||||
next.inputs.clear();
|
||||
next.outputs.clear();
|
||||
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 inst.GetOpcode() == ValueOpcode::BitwiseXor32;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#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 <queue>
|
||||
@@ -12,7 +12,7 @@ namespace {
|
||||
|
||||
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());
|
||||
if (access != SharedAccess::Read && access != SharedAccess::Write) {
|
||||
return LdsPhase::None;
|
||||
@@ -59,7 +59,7 @@ uint32_t InsertBarrier(Block& block, Block::iterator position) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t InsertBlockBarriers(ValueProgram& program, Block& block) {
|
||||
uint32_t InsertBlockBarriers(Program& program, Block& block) {
|
||||
uint32_t inserted = 0;
|
||||
LdsPhase phase = LdsPhase::None;
|
||||
for (auto inst = block.begin(); inst != block.end(); ++inst) {
|
||||
@@ -94,7 +94,7 @@ uint32_t InsertMergeBarrier(Block& block) {
|
||||
|
||||
} // namespace
|
||||
|
||||
SharedMemoryBarrierStats InsertSharedMemoryBarriers(ValueProgram& program, uint32_t wave_size,
|
||||
SharedMemoryBarrierStats InsertSharedMemoryBarriers(Program& program, uint32_t wave_size,
|
||||
const ShaderComputeInputInfo& compute_info) {
|
||||
SharedMemoryBarrierStats stats;
|
||||
const auto threadgroup_size = compute_info.threads_num[0] * compute_info.threads_num[1] *
|
||||
|
||||
@@ -9,7 +9,7 @@ struct SharedMemoryBarrierStats {
|
||||
};
|
||||
|
||||
[[nodiscard]] SharedMemoryBarrierStats
|
||||
InsertSharedMemoryBarriers(ValueProgram& program, uint32_t wave_size,
|
||||
InsertSharedMemoryBarriers(Program& program, uint32_t wave_size,
|
||||
const ShaderComputeInputInfo& compute_info);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
@@ -1,6 +1,6 @@
|
||||
#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 <algorithm>
|
||||
@@ -50,7 +50,7 @@ bool AddSignedAddress(uint64_t base, int64_t offset, uint64_t& result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsRawRead(const ValueProgram& values, const Inst& inst) {
|
||||
bool IsRawRead(const Program& values, const Inst& inst) {
|
||||
const auto op = inst.GetOpcode();
|
||||
if (op != ValueOpcode::LoadAddressU32 && op != ValueOpcode::ReadConstBuffer) {
|
||||
return false;
|
||||
@@ -118,8 +118,7 @@ bool IsRuntimeIntegerOp(ValueOpcode op) {
|
||||
|
||||
class RuntimeValidator {
|
||||
public:
|
||||
RuntimeValidator(const Program& program, const ValueProgram& values)
|
||||
: m_program(program), m_values(values) {}
|
||||
explicit RuntimeValidator(const Program& program): m_program(program) {}
|
||||
|
||||
bool Run(Value value, std::string& reason) { return Validate(value, reason); }
|
||||
|
||||
@@ -176,7 +175,7 @@ private:
|
||||
return finish(true);
|
||||
}
|
||||
if (op == ValueOpcode::Phi) {
|
||||
const auto invariant = ResolveInvariantPhi(m_values, value);
|
||||
const auto invariant = ResolveInvariantPhi(m_program, value);
|
||||
if (invariant.IsEmpty()) {
|
||||
reason = "contains a control-dependent phi";
|
||||
return finish(false);
|
||||
@@ -196,7 +195,7 @@ private:
|
||||
inst->Arg(0).Resolve().TryInstruction()->GetOpcode() !=
|
||||
ValueOpcode::GetSrtResource ||
|
||||
!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";
|
||||
return finish(false);
|
||||
}
|
||||
@@ -205,7 +204,7 @@ private:
|
||||
? ValueOpcode::GetAddressResource
|
||||
: ValueOpcode::GetBufferResource;
|
||||
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) {
|
||||
reason = fmt::format("contains a non-scalar {}", ValueOpcodeName(op));
|
||||
return finish(false);
|
||||
@@ -252,24 +251,23 @@ private:
|
||||
}
|
||||
|
||||
const Program& m_program;
|
||||
const ValueProgram& m_values;
|
||||
std::unordered_set<const Inst*> m_visiting;
|
||||
};
|
||||
|
||||
class PlanBuilder {
|
||||
public:
|
||||
PlanBuilder(Program& program, ValueProgram& values): m_program(program), m_values(values) {}
|
||||
explicit PlanBuilder(Program& program): m_program(program) {}
|
||||
|
||||
bool Run(std::string* error) {
|
||||
m_values.srt_reads.clear();
|
||||
m_values.dynamic_reads.clear();
|
||||
for (auto* block: m_values.blocks) {
|
||||
m_program.srt_reads.clear();
|
||||
m_program.dynamic_reads.clear();
|
||||
for (auto* block: m_program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
const auto op = inst.GetOpcode();
|
||||
if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) {
|
||||
const auto flags = inst.Flags<MemoryFlags>();
|
||||
if (flags.index < m_values.memory_info.size()) {
|
||||
const auto kind = m_values.memory_info[flags.index].kind;
|
||||
if (flags.index < m_program.memory_info.size()) {
|
||||
const auto kind = m_program.memory_info[flags.index].kind;
|
||||
const bool crosswired = (op == ValueOpcode::LoadAddressU32 &&
|
||||
kind == ResourceKind::ScalarBuffer) ||
|
||||
(op == ValueOpcode::ReadConstBuffer &&
|
||||
@@ -290,10 +288,10 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto* block: m_values.blocks) {
|
||||
for (auto* block: m_program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
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() &&
|
||||
ValidateRuntimeValue(m_program, Value(&inst), reason) &&
|
||||
!Collect(Value(&inst), inst.Flags<MemoryFlags>().pc, error)) {
|
||||
@@ -348,25 +346,25 @@ private:
|
||||
}
|
||||
m_visiting.pop_back();
|
||||
m_visited.push_back(inst);
|
||||
if (!IsRawRead(m_values, *inst)) {
|
||||
if (!IsRawRead(m_program, *inst)) {
|
||||
return true;
|
||||
}
|
||||
const auto offset = inst->Arg(1).Resolve();
|
||||
if (!offset.IsImmediate() || offset.GetType() != Type::U32) {
|
||||
if (std::ranges::find(m_values.dynamic_reads, value) == m_values.dynamic_reads.end()) {
|
||||
m_values.dynamic_reads.push_back(value);
|
||||
if (std::ranges::find(m_program.dynamic_reads, value) == m_program.dynamic_reads.end()) {
|
||||
m_program.dynamic_reads.push_back(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
use_pc = inst->Flags<MemoryFlags>().pc;
|
||||
for (uint32_t slot = 0; slot < m_values.srt_reads.size(); slot++) {
|
||||
if (EquivalentValue(m_values, value, m_values.srt_reads[slot].value)) {
|
||||
for (uint32_t slot = 0; slot < m_program.srt_reads.size(); slot++) {
|
||||
if (EquivalentValue(m_program, value, m_program.srt_reads[slot].value)) {
|
||||
m_patches.push_back({inst, slot, false});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const auto slot = static_cast<uint32_t>(m_values.srt_reads.size());
|
||||
m_values.srt_reads.push_back({value, slot, use_pc});
|
||||
const auto slot = static_cast<uint32_t>(m_program.srt_reads.size());
|
||||
m_program.srt_reads.push_back({value, slot, use_pc});
|
||||
m_patches.push_back({inst, slot, true});
|
||||
return true;
|
||||
}
|
||||
@@ -385,7 +383,7 @@ private:
|
||||
for (const auto& use: uses) {
|
||||
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)) {
|
||||
info.condition = flat;
|
||||
}
|
||||
@@ -395,8 +393,8 @@ private:
|
||||
}
|
||||
if (patch.keep) {
|
||||
const auto memory = patch.inst->Flags<MemoryFlags>().index;
|
||||
if (memory < m_values.memory_info.size()) {
|
||||
m_values.memory_info[memory].planning_only = true;
|
||||
if (memory < m_program.memory_info.size()) {
|
||||
m_program.memory_info[memory].planning_only = true;
|
||||
}
|
||||
block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(patch.inst)});
|
||||
}
|
||||
@@ -404,7 +402,6 @@ private:
|
||||
}
|
||||
|
||||
Program& m_program;
|
||||
ValueProgram& m_values;
|
||||
std::vector<Inst*> m_visiting;
|
||||
std::vector<Inst*> m_visited;
|
||||
std::vector<Patch> m_patches;
|
||||
@@ -414,7 +411,7 @@ class Evaluator {
|
||||
public:
|
||||
Evaluator(const Program& program, const SrtRuntime& runtime,
|
||||
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) {}
|
||||
|
||||
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) {
|
||||
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)
|
||||
: Fail(error, "typed phi has runtime-dependent values");
|
||||
}
|
||||
@@ -518,10 +515,10 @@ private:
|
||||
|
||||
bool EvaluateRawRead(const Inst& inst, uint64_t& result, std::string* error) {
|
||||
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");
|
||||
}
|
||||
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();
|
||||
if (handle == nullptr) {
|
||||
return Fail(error, "raw scalar read has no descriptor handle");
|
||||
@@ -609,20 +606,20 @@ private:
|
||||
case ValueOpcode::ReadConst: {
|
||||
const auto slot = inst.Arg(1).Resolve();
|
||||
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");
|
||||
}
|
||||
if (slot.U32() < m_clean_flat_slots.size() &&
|
||||
m_clean_flat_slots[slot.U32()] != 0u && m_clean_evaluator != nullptr) {
|
||||
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);
|
||||
}
|
||||
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::ReadConstBuffer:
|
||||
if (IsRawRead(m_values, inst)) {
|
||||
if (IsRawRead(m_program, inst)) {
|
||||
return EvaluateRawRead(inst, result, error);
|
||||
}
|
||||
break;
|
||||
@@ -856,7 +853,6 @@ private:
|
||||
}
|
||||
|
||||
const Program& m_program;
|
||||
const ValueProgram& m_values;
|
||||
const SrtRuntime& m_runtime;
|
||||
std::span<const uint8_t> m_clean_flat_slots;
|
||||
Evaluator* m_clean_evaluator = nullptr;
|
||||
@@ -866,10 +862,10 @@ private:
|
||||
};
|
||||
|
||||
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 &program.values->descriptor_sources[source];
|
||||
return &program.descriptor_sources[source];
|
||||
}
|
||||
|
||||
bool EvaluateRuntimeSourcesImpl(const Program& program,
|
||||
@@ -877,7 +873,7 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results,
|
||||
std::vector<uint32_t>& flat, bool evaluate_flat,
|
||||
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) {
|
||||
*error = Diagnostic(program, 0, "typed SRT plan is not ready");
|
||||
}
|
||||
@@ -917,8 +913,8 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
||||
}
|
||||
std::vector<uint32_t> flattened;
|
||||
if (evaluate_flat) {
|
||||
flattened.resize(program.values->srt_reads.size());
|
||||
for (const auto& read: program.values->srt_reads) {
|
||||
flattened.resize(program.srt_reads.size());
|
||||
for (const auto& read: program.srt_reads) {
|
||||
const bool clean = read.flat_offset < clean_flat_slots.size() &&
|
||||
clean_flat_slots[read.flat_offset] != 0u;
|
||||
auto& selected = clean ? clean_evaluator : evaluator;
|
||||
@@ -939,23 +935,18 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
||||
} // namespace
|
||||
|
||||
bool ValidateRuntimeValue(const Program& program, Value value, std::string& reason) {
|
||||
if (program.values == nullptr) {
|
||||
reason = "typed SSA is not ready";
|
||||
return false;
|
||||
}
|
||||
return RuntimeValidator(program, *program.values).Run(value, reason);
|
||||
return RuntimeValidator(program).Run(value, reason);
|
||||
}
|
||||
|
||||
bool BuildSrtPlan(Program& program, std::string* error) {
|
||||
if (program.values == nullptr || program.resource_tracking_complete) {
|
||||
if (program.resource_tracking_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = program.values == nullptr ? "typed SSA is not ready"
|
||||
: "cannot rebuild SRT after resource tracking";
|
||||
*error = "cannot rebuild SRT after resource tracking";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
program.srt_plan_complete = false;
|
||||
if (!PlanBuilder(program, *program.values).Run(error)) {
|
||||
if (!PlanBuilder(program).Run(error)) {
|
||||
return false;
|
||||
}
|
||||
program.srt_plan_complete = true;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/shader/recompiler/ShaderRecompiler.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 "kernel/memory.h"
|
||||
#include "libs/errno.h"
|
||||
@@ -49,9 +49,6 @@ namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t PsInputOffsetMask = 0x0000001fu;
|
||||
constexpr uint32_t PsInputFlatShade = 0x00000400u;
|
||||
|
||||
bool ReadShaderGuestMemory(void*, uint64_t address, uint32_t* value) {
|
||||
return value != nullptr &&
|
||||
Libs::LibKernel::Memory::TryReadGpuCleanBacking(address, value, sizeof(*value));
|
||||
@@ -59,40 +56,6 @@ bool ReadShaderGuestMemory(void*, uint64_t address, uint32_t* value) {
|
||||
|
||||
} // 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 {
|
||||
uint8_t signature[7];
|
||||
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.user_data_registers.size(), program.info.buffers.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++) {
|
||||
const auto& buffer = program.info.buffers[i];
|
||||
ret +=
|
||||
|
||||
@@ -84,7 +84,6 @@ struct ShaderVertexInputInfo {
|
||||
|
||||
ShaderBufferResource resources[RES_MAX];
|
||||
ShaderVertexDestination resources_dst[RES_MAX];
|
||||
int resource_fetch_components[RES_MAX] = {};
|
||||
ShaderVertexInputBuffer buffers[RES_MAX];
|
||||
ShaderStageRuntime stage;
|
||||
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
|
||||
@@ -1,5 +1,5 @@
|
||||
#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/DeadCodeElimination.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||
@@ -35,7 +35,6 @@ struct Fixture {
|
||||
|
||||
explicit Fixture(ShaderType stage = ShaderType::Compute) {
|
||||
program.stage = stage;
|
||||
program.values = std::make_shared<ValueProgram>();
|
||||
program.user_data_count = 64;
|
||||
block = AddBlock();
|
||||
}
|
||||
@@ -43,10 +42,10 @@ struct Fixture {
|
||||
Block *AddBlock() {
|
||||
auto storage = std::make_unique<Block>();
|
||||
auto *result = storage.get();
|
||||
program.values->block_storage.push_back(std::move(storage));
|
||||
program.values->blocks.push_back(result);
|
||||
program.values->block_info.push_back(
|
||||
{.id = static_cast<uint32_t>(program.values->block_info.size())});
|
||||
program.block_storage.push_back(std::move(storage));
|
||||
program.blocks.push_back(result);
|
||||
program.block_info.push_back(
|
||||
{.id = static_cast<uint32_t>(program.block_info.size())});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -77,8 +76,8 @@ struct Fixture {
|
||||
|
||||
MemoryFlags AddMemory(MemoryInfo memory, uint32_t pc) {
|
||||
const auto index =
|
||||
static_cast<uint32_t>(program.values->memory_info.size());
|
||||
program.values->memory_info.push_back(memory);
|
||||
static_cast<uint32_t>(program.memory_info.size());
|
||||
program.memory_info.push_back(memory);
|
||||
return {index, pc};
|
||||
}
|
||||
|
||||
@@ -241,19 +240,19 @@ MakeIndirectImageFixture(bool malformed, uint32_t material_immediate = 0,
|
||||
void TestInvariantIndirectImageMaterialization() {
|
||||
auto fixture = MakeIndirectImageFixture(false);
|
||||
fixture->PlanAndTrack();
|
||||
EliminateDeadCode(fixture->program.values->blocks);
|
||||
EliminateDeadCode(fixture->program.blocks);
|
||||
std::string validation_error;
|
||||
Check(
|
||||
ValidateValueProgram(*fixture->program.values, true, &validation_error),
|
||||
ValidateProgram(fixture->program, true, &validation_error),
|
||||
"post-tracking dead-code elimination invalidated descriptor provenance");
|
||||
|
||||
Check(fixture->program.info.buffers.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");
|
||||
const auto source = fixture->program.info.images[0].source;
|
||||
Check(source < fixture->program.values->descriptor_sources.size() &&
|
||||
fixture->program.values->descriptor_sources[source]
|
||||
Check(source < fixture->program.descriptor_sources.size() &&
|
||||
fixture->program.descriptor_sources[source]
|
||||
.indirect_image.has_value(),
|
||||
"indirect image source was not retained for runtime proof");
|
||||
const auto image_handle =
|
||||
@@ -407,7 +406,7 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
|
||||
auto memory_backed = MakeIndirectImageFixture(false, 0u, true);
|
||||
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,
|
||||
0x2000u, 16u << 16u, 4u, 0u,
|
||||
7u, 0x3100u, 0u};
|
||||
@@ -431,7 +430,7 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
error.find("ReadFirstLane") != std::string::npos &&
|
||||
!malformed->program.resource_tracking_complete &&
|
||||
malformed->program.info.images.empty() &&
|
||||
malformed->program.values->descriptor_sources.empty(),
|
||||
malformed->program.descriptor_sources.empty(),
|
||||
"malformed indirect image pattern was partially accepted");
|
||||
|
||||
auto wrapped_immediate = MakeIndirectImageFixture(false, 4u);
|
||||
@@ -484,7 +483,7 @@ void TestDenseBufferTracking() {
|
||||
|
||||
Check(fixture.program.info.buffers.size() == 2,
|
||||
"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");
|
||||
const auto &resource = fixture.program.info.buffers[0];
|
||||
Check(resource.read && resource.written && resource.atomic &&
|
||||
@@ -494,10 +493,10 @@ void TestDenseBufferTracking() {
|
||||
Check(first.Instruction()->Flags<uint32_t>() == 0 &&
|
||||
second.Instruction()->Flags<uint32_t>() == 1,
|
||||
"typed handles were not assigned dense indices");
|
||||
Check(fixture.program.values->memory_info[load_flags.index].resource == 0 &&
|
||||
fixture.program.values->memory_info[store_flags.index].resource ==
|
||||
Check(fixture.program.memory_info[load_flags.index].resource == 0 &&
|
||||
fixture.program.memory_info[store_flags.index].resource ==
|
||||
0 &&
|
||||
fixture.program.values->memory_info[other_flags.index].resource ==
|
||||
fixture.program.memory_info[other_flags.index].resource ==
|
||||
1,
|
||||
"typed memory metadata was not patched to dense indices");
|
||||
|
||||
@@ -531,8 +530,8 @@ void TestScalarAndVectorBufferAlias() {
|
||||
Check(fixture.program.info.buffers.size() == 1 &&
|
||||
fixture.program.info.buffers[0].scalar,
|
||||
"typed scalar and vector uses of one descriptor were split");
|
||||
Check(fixture.program.values->memory_info[scalar_flags.index].resource == 0 &&
|
||||
fixture.program.values->memory_info[vector_flags.index].resource ==
|
||||
Check(fixture.program.memory_info[scalar_flags.index].resource == 0 &&
|
||||
fixture.program.memory_info[vector_flags.index].resource ==
|
||||
0,
|
||||
"scalar/vector alias did not share a dense index");
|
||||
}
|
||||
@@ -625,7 +624,7 @@ void TestImagesSamplersAndAliases() {
|
||||
repeated.second.Instruction()->Flags<uint32_t>() == 0,
|
||||
"unused sampler border colors prevented source interning");
|
||||
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]
|
||||
.U32() == 0,
|
||||
"unused sampler border color was not canonicalized");
|
||||
@@ -665,7 +664,7 @@ void TestSampleAdjustSamplerScratch() {
|
||||
fixture.PlanAndTrack();
|
||||
|
||||
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]
|
||||
.Resolve()
|
||||
.TryInstruction();
|
||||
@@ -758,10 +757,10 @@ void TestDynamicStorageMipTracking() {
|
||||
mip1.first.Instruction()->Flags<uint32_t>() == 1 &&
|
||||
mip2.first.Instruction()->Flags<uint32_t>() == 1 &&
|
||||
dynamic.first.Instruction()->Flags<uint32_t>() == 1 &&
|
||||
fixture.program.values->memory_info[plain.second].resource == 0 &&
|
||||
fixture.program.values->memory_info[mip1.second].resource == 1 &&
|
||||
fixture.program.values->memory_info[mip2.second].resource == 1 &&
|
||||
fixture.program.values->memory_info[dynamic.second].resource == 1,
|
||||
fixture.program.memory_info[plain.second].resource == 0 &&
|
||||
fixture.program.memory_info[mip1.second].resource == 1 &&
|
||||
fixture.program.memory_info[mip2.second].resource == 1 &&
|
||||
fixture.program.memory_info[dynamic.second].resource == 1,
|
||||
"dynamic storage mip handles and memory metadata were not patched");
|
||||
|
||||
DescriptorValue descriptor{};
|
||||
@@ -795,7 +794,6 @@ void TestDynamicStorageMipTracking() {
|
||||
"dynamic storage mip descriptors were not expanded consecutively");
|
||||
|
||||
Program null_program;
|
||||
null_program.values = std::make_shared<ValueProgram>();
|
||||
null_program.resource_tracking_complete = true;
|
||||
ImageResource null_image;
|
||||
null_image.kind = ResourceKind::StorageImage;
|
||||
@@ -847,12 +845,12 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
||||
fixture.AddMemory(buffer, 16));
|
||||
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");
|
||||
Check(fixture.program.info.buffers.size() == 1 &&
|
||||
fixture.program.info.addresses.empty(),
|
||||
"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");
|
||||
|
||||
std::array<uint32_t, 2> user_data{0x1000u, 0u};
|
||||
@@ -910,8 +908,8 @@ void TestDynamicSrtReadRemainsExplicit() {
|
||||
fixture.AddMemory(buffer, 8));
|
||||
fixture.PlanAndTrack();
|
||||
|
||||
Check(fixture.program.values->srt_reads.empty() &&
|
||||
fixture.program.values->dynamic_reads.size() == 1 &&
|
||||
Check(fixture.program.srt_reads.empty() &&
|
||||
fixture.program.dynamic_reads.size() == 1 &&
|
||||
fixture.program.info.addresses.size() == 1,
|
||||
"dynamic scalar read was incorrectly flattened or lost");
|
||||
std::array<uint32_t, 3> user_data{0x1000u, 0u, 4u};
|
||||
@@ -973,7 +971,7 @@ void TestPhiValidation() {
|
||||
error.find("control-dependent phi") != std::string::npos &&
|
||||
!fixture.program.resource_tracking_complete &&
|
||||
fixture.program.info.buffers.empty() &&
|
||||
fixture.program.values->descriptor_sources.empty(),
|
||||
fixture.program.descriptor_sources.empty(),
|
||||
"control-dependent descriptor phi was not rejected transactionally");
|
||||
}
|
||||
|
||||
@@ -1295,7 +1293,7 @@ void TestResourceLimitIsTransactional() {
|
||||
error.find("buffer resource limit exceeded") != std::string::npos &&
|
||||
!fixture.program.resource_tracking_complete &&
|
||||
fixture.program.info.buffers.empty() &&
|
||||
fixture.program.values->descriptor_sources.empty(),
|
||||
fixture.program.descriptor_sources.empty(),
|
||||
"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
|
||||
// target links only fmt; keep assertion failures observable without widening
|
||||
// its legacy build manifest.
|
||||
// its focused build manifest.
|
||||
namespace Common {
|
||||
int DbgExitIfHandler(const char *expression, const char *file, int line) {
|
||||
throw std::runtime_error(std::string("typed IR assertion: ") + expression +
|
||||
" 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"); }
|
||||
} // 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/Type.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/passes/DeadCodeElimination.cpp"
|
||||
@@ -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/DeadCodeElimination.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ReadLaneElimination.h"
|
||||
@@ -27,16 +27,15 @@ struct Fixture {
|
||||
program.stage = ShaderType::Compute;
|
||||
program.shader_hash = 0x12345678u;
|
||||
program.user_data_base = 2;
|
||||
program.values = std::make_shared<ValueProgram>();
|
||||
for (uint32_t index = 0; index < block_count; index++) {
|
||||
program.values->block_storage.push_back(std::make_unique<Block>());
|
||||
auto *block = program.values->block_storage.back().get();
|
||||
program.values->blocks.push_back(block);
|
||||
program.values->block_info.push_back({.id = index});
|
||||
program.block_storage.push_back(std::make_unique<Block>());
|
||||
auto *block = program.block_storage.back().get();
|
||||
program.blocks.push_back(block);
|
||||
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 = {},
|
||||
uint64_t flags = 0, uint32_t block = 0) {
|
||||
@@ -55,8 +54,8 @@ struct Fixture {
|
||||
MemoryInfo info;
|
||||
info.kind = kind;
|
||||
info.offset = static_cast<uint32_t>(offset);
|
||||
program.values->memory_info.push_back(info);
|
||||
return static_cast<uint32_t>(program.values->memory_info.size() - 1u);
|
||||
program.memory_info.push_back(info);
|
||||
return static_cast<uint32_t>(program.memory_info.size() - 1u);
|
||||
}
|
||||
|
||||
void Plan() {
|
||||
@@ -103,11 +102,11 @@ void TestImmediateFlatteningAndGvn() {
|
||||
{first, second, Value(16u), Value(0u)});
|
||||
|
||||
fixture.Plan();
|
||||
Check(fixture.program.values->srt_reads.size() == 1,
|
||||
Check(fixture.program.srt_reads.size() == 1,
|
||||
"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");
|
||||
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");
|
||||
|
||||
TestMemory memory_image{{{0x1020u, 0xfeedbeefu}}};
|
||||
@@ -173,8 +172,8 @@ void TestDynamicReadRemainsTyped() {
|
||||
{read, Value(0u), Value(16u), Value(0u)});
|
||||
|
||||
fixture.Plan();
|
||||
Check(fixture.program.values->srt_reads.empty() &&
|
||||
fixture.program.values->dynamic_reads == std::vector<Value>{read},
|
||||
Check(fixture.program.srt_reads.empty() &&
|
||||
fixture.program.dynamic_reads == std::vector<Value>{read},
|
||||
"dynamic scalar read received a fake flattened slot");
|
||||
}
|
||||
|
||||
@@ -209,7 +208,7 @@ void TestShaderBaseAndUserData() {
|
||||
{Value(static_cast<ScalarReg>(2))});
|
||||
const auto sum = fixture.Emit(ValueOpcode::IAdd32, {user, Value(4u)});
|
||||
fixture.Plan();
|
||||
fixture.program.values->descriptor_sources.push_back(
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {low, high, sum}, .dword_count = 3});
|
||||
|
||||
const std::array user_data{0x20u};
|
||||
@@ -239,7 +238,7 @@ void TestCarryAndBitFields() {
|
||||
const auto sign = fixture.Emit(ValueOpcode::BitFieldSExtract,
|
||||
{Value(0x000000f0u), Value(4u), Value(4u)});
|
||||
fixture.Plan();
|
||||
fixture.program.values->descriptor_sources.push_back(
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {low, high, inserted, sign}, .dword_count = 4});
|
||||
|
||||
DescriptorValue result;
|
||||
@@ -262,9 +261,9 @@ void TestInvariantAndDivergentPhi() {
|
||||
divergent.AddPhiOperand(&fixture.BlockAt(0), Value(7u));
|
||||
divergent.AddPhiOperand(&fixture.BlockAt(1), Value(9u));
|
||||
fixture.Plan();
|
||||
fixture.program.values->descriptor_sources.push_back(
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.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});
|
||||
|
||||
DescriptorValue result;
|
||||
@@ -293,10 +292,10 @@ void TestControlDependentStandaloneLoadStaysTyped() {
|
||||
RawRead(fixture, Address(fixture, Value(&base), Value(0u), 2), Value(0u),
|
||||
memory, 2);
|
||||
fixture.Plan();
|
||||
Check(fixture.program.values->srt_reads.empty() &&
|
||||
Check(fixture.program.srt_reads.empty() &&
|
||||
read.ResolveInstruction()->GetOpcode() ==
|
||||
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 "
|
||||
"snapshot");
|
||||
}
|
||||
@@ -315,7 +314,7 @@ void TestRuntime64BitDescriptorOps() {
|
||||
const auto high =
|
||||
fixture.Emit(ValueOpcode::CompositeExtractU64, {combined, Value(1u)});
|
||||
fixture.Plan();
|
||||
fixture.program.values->descriptor_sources.push_back(
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {low, high}, .dword_count = 2});
|
||||
|
||||
DescriptorValue result;
|
||||
@@ -370,7 +369,7 @@ void TestReadLaneElimination() {
|
||||
{undef, Value(0xdeadbeefu), Value(5u)});
|
||||
const auto read = fixture.Emit(ValueOpcode::ReadLane, {write, Value(5u)});
|
||||
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 &&
|
||||
use.ResolveInstruction()->Arg(0).Resolve() == Value(0xdeadbeefu),
|
||||
"fixed-lane typed read was not rewritten from its SSA write chain");
|
||||
@@ -379,7 +378,7 @@ void TestReadLaneElimination() {
|
||||
{Value(static_cast<ScalarReg>(2))});
|
||||
const auto dynamic = fixture.Emit(ValueOpcode::ReadLane, {write, selector});
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -390,9 +389,9 @@ void TestOptimizationPipeline() {
|
||||
fixture.Emit(ValueOpcode::ReferenceU32, {kept});
|
||||
fixture.Emit(ValueOpcode::IMul32, {Value(6u), Value(7u)});
|
||||
|
||||
ConstantPropagationPass(fixture.program.values->blocks);
|
||||
RemoveIdentities(fixture.program.values->blocks);
|
||||
EliminateDeadCode(fixture.program.values->blocks);
|
||||
ConstantPropagationPass(fixture.program.blocks);
|
||||
RemoveIdentities(fixture.program.blocks);
|
||||
EliminateDeadCode(fixture.program.blocks);
|
||||
|
||||
const auto &instructions = fixture.BlockAt().Instructions();
|
||||
Check(instructions.size() == 1 &&
|
||||
@@ -404,20 +403,20 @@ void TestOptimizationPipeline() {
|
||||
|
||||
void TestControlFlowValueSurvivesReadLaneFolding() {
|
||||
Fixture fixture(3);
|
||||
auto *entry = fixture.program.values->blocks[0];
|
||||
auto *taken = fixture.program.values->blocks[1];
|
||||
auto *other = fixture.program.values->blocks[2];
|
||||
auto *entry = fixture.program.blocks[0];
|
||||
auto *taken = fixture.program.blocks[1];
|
||||
auto *other = fixture.program.blocks[2];
|
||||
entry->AddBranch(taken);
|
||||
entry->AddBranch(other);
|
||||
|
||||
auto &entry_info = fixture.program.values->block_info[0];
|
||||
auto &entry_info = fixture.program.block_info[0];
|
||||
entry_info.terminator.kind =
|
||||
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch;
|
||||
entry_info.terminator.true_block = 1;
|
||||
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;
|
||||
fixture.program.values->block_info[2].terminator.kind =
|
||||
fixture.program.block_info[2].terminator.kind =
|
||||
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
|
||||
|
||||
const auto undef = fixture.Emit(ValueOpcode::UndefU32);
|
||||
@@ -429,16 +428,16 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
|
||||
fixture.Emit(ValueOpcode::IEqual32, {read, Value(42u)});
|
||||
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");
|
||||
ConstantPropagationPass(fixture.program.values->blocks);
|
||||
ResolveControlFlowIdentities(*fixture.program.values);
|
||||
RemoveIdentities(fixture.program.values->blocks);
|
||||
EliminateDeadCode(fixture.program.values->blocks);
|
||||
ConstantPropagationPass(fixture.program.blocks);
|
||||
ResolveControlFlowIdentities(fixture.program);
|
||||
RemoveIdentities(fixture.program.blocks);
|
||||
EliminateDeadCode(fixture.program.blocks);
|
||||
|
||||
std::string error;
|
||||
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.c_str());
|
||||
}
|
||||
@@ -447,7 +446,7 @@ void TestUndefinedRuntimeValueFails() {
|
||||
Fixture fixture;
|
||||
const auto undef = fixture.Emit(ValueOpcode::UndefU32);
|
||||
fixture.Plan();
|
||||
fixture.program.values->descriptor_sources.push_back(
|
||||
fixture.program.descriptor_sources.push_back(
|
||||
{.dwords = {undef}, .dword_count = 1});
|
||||
DescriptorValue result;
|
||||
result.dword_count = 3;
|
||||
@@ -499,7 +498,7 @@ int main() {
|
||||
#include "../src/graphics/shader/recompiler/ir/Block.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/Type.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/passes/ConstantPropagation.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp"
|
||||
@@ -38,7 +38,7 @@
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h"
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.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/rectListShader.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
@@ -967,7 +967,6 @@ struct TestCase {
|
||||
std::vector<u32> expected_gds;
|
||||
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>> native_ir_counts;
|
||||
u32 expected_storage_mip_descriptors = 0;
|
||||
};
|
||||
|
||||
@@ -1192,32 +1191,6 @@ CompiledShader CompileCase(const TestCase &test) {
|
||||
text + " count=" + std::to_string(actual) +
|
||||
", 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) {
|
||||
const auto image = std::ranges::find_if(
|
||||
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");
|
||||
}
|
||||
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++) {
|
||||
auto &user_data = block.AppendNewInst(
|
||||
ShaderRecompiler::IR::ValueOpcode::GetUserData,
|
||||
@@ -7519,8 +7492,6 @@ public:
|
||||
|
||||
ShaderRecompiler::IR::Program null_program{};
|
||||
null_program.stage = ShaderType::Compute;
|
||||
null_program.values =
|
||||
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
null_program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource null_resource{};
|
||||
null_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
@@ -7630,8 +7601,6 @@ public:
|
||||
storage.fields[5] = 0x00700000u;
|
||||
ShaderRecompiler::IR::Program storage_program{};
|
||||
storage_program.stage = ShaderType::Vertex;
|
||||
storage_program.values =
|
||||
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
storage_program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource storage_resource{};
|
||||
storage_resource.kind =
|
||||
@@ -7911,8 +7880,6 @@ public:
|
||||
|
||||
ShaderRecompiler::IR::Program sampled_program{};
|
||||
sampled_program.stage = ShaderType::Pixel;
|
||||
sampled_program.values =
|
||||
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
sampled_program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource sampled_resource{};
|
||||
sampled_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
@@ -8003,15 +7970,19 @@ public:
|
||||
RenderExecutorTestAccess::ResetBindings(executor);
|
||||
|
||||
auto vertex_sampled_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>(
|
||||
*sampled_runtime.program);
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
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{
|
||||
std::move(vertex_sampled_program), sampled_runtime.resources};
|
||||
auto pixel_storage_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>(
|
||||
*storage_runtime.program);
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
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),
|
||||
storage_runtime.resources};
|
||||
auto writable_alias_bindings =
|
||||
@@ -8083,8 +8054,6 @@ public:
|
||||
|
||||
auto split_program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
split_program->stage = ShaderType::Vertex;
|
||||
split_program->values =
|
||||
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
split_program->resource_tracking_complete = true;
|
||||
split_program->info.images = {storage_resource, sampled_resource};
|
||||
allocate_bindings(*split_program);
|
||||
@@ -8459,8 +8428,6 @@ public:
|
||||
|
||||
ShaderRecompiler::IR::Program array_program{};
|
||||
array_program.stage = ShaderType::Compute;
|
||||
array_program.values =
|
||||
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
array_program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource array_resource{};
|
||||
array_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
@@ -8544,10 +8511,13 @@ public:
|
||||
colliding_msaa_descriptor.dwords.begin());
|
||||
colliding_msaa_descriptor.dword_count = 8;
|
||||
auto colliding_msaa_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>(
|
||||
*array_runtime.program);
|
||||
colliding_msaa_program->info.images[0].dimension =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
colliding_msaa_program->stage = ShaderType::Compute;
|
||||
colliding_msaa_program->resource_tracking_complete = true;
|
||||
auto colliding_msaa_resource = array_resource;
|
||||
colliding_msaa_resource.dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa;
|
||||
colliding_msaa_program->info.images.push_back(colliding_msaa_resource);
|
||||
auto colliding_msaa_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
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),
|
||||
msaa_descriptor.dwords.begin());
|
||||
msaa_descriptor.dword_count = 8;
|
||||
auto msaa_program = std::make_shared<ShaderRecompiler::IR::Program>(
|
||||
*array_runtime.program);
|
||||
msaa_program->info.images[0].dimension =
|
||||
auto msaa_program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
msaa_program->stage = ShaderType::Compute;
|
||||
msaa_program->resource_tracking_complete = true;
|
||||
auto msaa_resource = array_resource;
|
||||
msaa_resource.dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa;
|
||||
msaa_program->info.images.push_back(msaa_resource);
|
||||
auto msaa_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
msaa_snapshot->images.push_back(msaa_descriptor);
|
||||
@@ -8643,10 +8616,14 @@ public:
|
||||
std::end(msaa_array_texture.fields),
|
||||
msaa_array_descriptor.dwords.begin());
|
||||
msaa_array_descriptor.dword_count = 8;
|
||||
auto msaa_array_program = std::make_shared<ShaderRecompiler::IR::Program>(
|
||||
*msaa_runtime.program);
|
||||
msaa_array_program->info.images[0].dimension =
|
||||
auto msaa_array_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
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;
|
||||
msaa_array_program->info.images.push_back(msaa_array_resource);
|
||||
auto msaa_array_snapshot =
|
||||
std::make_shared<ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
msaa_array_snapshot->images.push_back(msaa_array_descriptor);
|
||||
@@ -8868,7 +8845,6 @@ public:
|
||||
|
||||
ShaderRecompiler::IR::Program program{};
|
||||
program.stage = ShaderType::Compute;
|
||||
program.values = std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource stencil_resource{};
|
||||
stencil_resource.kind = ShaderRecompiler::IR::ResourceKind::ImageUint;
|
||||
@@ -8940,11 +8916,15 @@ public:
|
||||
RenderExecutorTestAccess::ResetBindings(executor);
|
||||
|
||||
auto stencil_storage_program =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>(*runtime.program);
|
||||
stencil_storage_program->info.images[0].kind =
|
||||
std::make_shared<ShaderRecompiler::IR::Program>();
|
||||
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;
|
||||
stencil_storage_program->info.images[0].read = false;
|
||||
stencil_storage_program->info.images[0].written = true;
|
||||
stencil_storage_resource.read = false;
|
||||
stencil_storage_resource.written = true;
|
||||
stencil_storage_program->info.images.push_back(stencil_storage_resource);
|
||||
auto storage_stencil = stencil;
|
||||
storage_stencil.fields[5] = 0x00700000u;
|
||||
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,
|
||||
O::S_ENDPGM};
|
||||
test.decoded_counts = {{"0x00000010: S_WAITCNT_DEPCTR 0x0000ffe3\n", 1}};
|
||||
test.native_ir_counts = {{"0x00000010: Waitcnt null, 0x0000ffe3", 1}};
|
||||
test.forbidden_spirv = {"OpControlBarrier", "OpMemoryBarrier"};
|
||||
return test;
|
||||
}
|
||||
@@ -13289,7 +13268,6 @@ TestCase Vop3Med3I16Captured() {
|
||||
test.decoded_counts = {{"0x00000020: V_MED3_I16 v3.sdwa(sel=5,sext=0), "
|
||||
"v5.opsel(lo=1,hi=0,neghi=0), s25, s27\n",
|
||||
1}};
|
||||
test.native_ir_counts = {{"IMed3I16", 1}};
|
||||
test.required_spirv = {"OpSLessThan"};
|
||||
return test;
|
||||
}
|
||||
@@ -15227,8 +15205,6 @@ TestCase VectorVopcCmpxNeU64CapturedExecMask() {
|
||||
O::S_MOV_B32, O::S_ENDPGM}};
|
||||
test.decoded_counts = {{"V_CMPX_NE_U64 exec_lo, 0, v7", 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;
|
||||
}
|
||||
|
||||
@@ -15258,7 +15234,6 @@ TestCase VectorVop3CmpxNeI64CapturedExecMask() {
|
||||
{O::V_MOV_B32, O::V_CMPX_NE_I64, O::BUFFER_STORE_DWORD,
|
||||
O::S_MOV_B32, O::S_ENDPGM}};
|
||||
test.decoded_counts = {{"V_CMPX_NE_I64 exec_lo, 0, v7", 2}};
|
||||
test.native_ir_counts = {{"CompareNeU64 exec_lo, 0x00000000, v7", 2}};
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -17738,7 +17713,6 @@ TestCase DsReadU16D16CapturedPreservesHighHalf() {
|
||||
{O::V_MOV_B32, O::DS_WRITE_B32, O::DS_READ_U16_D16,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
test.decoded_counts = {{"DS_READ_U16_D16", 2}};
|
||||
test.native_ir_counts = {{"DsReadUshort", 2}};
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -18652,7 +18626,6 @@ TestCase ImageLoadR128IgnoresAdjacentMaskSgprs() {
|
||||
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32UInt);
|
||||
test.has_user_data = true;
|
||||
test.decoded_counts = {{"r128=1", 1}};
|
||||
test.native_ir_counts = {{"image_r128=1", 1}};
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -18974,8 +18947,6 @@ TestCase ImageD16GatherPacksHalfPairs() {
|
||||
O::S_ENDPGM};
|
||||
test.sampled_image_rgba = image;
|
||||
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"};
|
||||
return test;
|
||||
}
|
||||
@@ -19196,8 +19167,6 @@ TestCase ImageD16StoreUnpacksHalfPairs() {
|
||||
test.storage_image_rgba = MakeRgbaImage(4, 4);
|
||||
test.expected_storage_image_rgba = expected_image;
|
||||
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"};
|
||||
return test;
|
||||
}
|
||||
@@ -19213,11 +19182,10 @@ void CheckIndirectImageKeySwitch() {
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
program.shader_info_complete = true;
|
||||
program.values = std::make_shared<ValueProgram>();
|
||||
program.values->block_storage.push_back(std::make_unique<Block>());
|
||||
auto *block = program.values->block_storage.back().get();
|
||||
program.values->blocks.push_back(block);
|
||||
program.values->block_info.push_back({.id = 0});
|
||||
program.block_storage.push_back(std::make_unique<Block>());
|
||||
auto *block = program.block_storage.back().get();
|
||||
program.blocks.push_back(block);
|
||||
program.block_info.push_back({.id = 0});
|
||||
|
||||
auto &key = block->AppendNewInst(ValueOpcode::LaneId);
|
||||
auto &image =
|
||||
@@ -19242,7 +19210,7 @@ void CheckIndirectImageKeySwitch() {
|
||||
memory.image_sample_flags = ShaderRecompiler::Decoder::ImageSampleFlagLod;
|
||||
memory.image_dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||
memory.image_address_components = 3;
|
||||
program.values->memory_info.push_back(memory);
|
||||
program.memory_info.push_back(memory);
|
||||
const MemoryFlags memory_flags{0u, 0x10f0u};
|
||||
uint64_t memory_flag_bits = 0;
|
||||
std::memcpy(&memory_flag_bits, &memory_flags, sizeof(memory_flags));
|
||||
@@ -19253,11 +19221,11 @@ void CheckIndirectImageKeySwitch() {
|
||||
{Value(&sample), Value(0u)});
|
||||
block->AppendNewInst(ValueOpcode::ReferenceU32, {Value(&sample_x)});
|
||||
|
||||
program.values->descriptor_sources.resize(2);
|
||||
program.values->descriptor_sources[0].dword_count = 8;
|
||||
program.values->descriptor_sources[0].indirect_image =
|
||||
program.descriptor_sources.resize(2);
|
||||
program.descriptor_sources[0].dword_count = 8;
|
||||
program.descriptor_sources[0].indirect_image =
|
||||
DescriptorSource::IndirectImage{0u, 0u, 224u, 12u, 0u};
|
||||
program.values->descriptor_sources[1].dword_count = 4;
|
||||
program.descriptor_sources[1].dword_count = 4;
|
||||
|
||||
ImageResource root{};
|
||||
root.source = 0;
|
||||
@@ -20394,8 +20362,8 @@ void CheckPs5GameExampleImageClearRuntimeShape() {
|
||||
const auto code = MakeCode();
|
||||
auto positive = Compile("exact Prospero kernel", code);
|
||||
|
||||
compute.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(positive.program);
|
||||
compute.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
std::move(positive.program));
|
||||
compute.stage.resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
positive.resources);
|
||||
@@ -20488,7 +20456,8 @@ void CheckEmbeddedFetchVertexOffset() {
|
||||
Require(name, "compile",
|
||||
ShaderRecompiler::TryRecompile(code, options, result, &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");
|
||||
return result;
|
||||
};
|
||||
@@ -20497,8 +20466,11 @@ void CheckEmbeddedFetchVertexOffset() {
|
||||
u32 index_offset) {
|
||||
ShaderVertexInputInfo vertex;
|
||||
vertex.fetch_embedded = true;
|
||||
vertex.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(result.program);
|
||||
auto program = std::make_shared<ShaderRecompiler::IR::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 =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>(
|
||||
result.resources);
|
||||
@@ -22366,52 +22338,55 @@ void CheckImageSpecializationId() {
|
||||
HW::ComputeShaderInfo regs{};
|
||||
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;
|
||||
image.kind = ShaderRecompiler::IR::ResourceKind::StorageImage;
|
||||
image.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
|
||||
identity_program.info.images.push_back(image);
|
||||
auto rgb1_program = identity_program;
|
||||
rgb1_program.info.images[0].shader_swizzle = DstSel(4, 5, 6, 1);
|
||||
auto converted_program = identity_program;
|
||||
converted_program.info.images[0].conversion_format =
|
||||
const auto make_program = [](ShaderRecompiler::IR::ImageResource resource) {
|
||||
ShaderRecompiler::IR::Program program;
|
||||
program.binding_layout_complete = true;
|
||||
program.info.images.push_back(std::move(resource));
|
||||
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;
|
||||
auto mip1_program = identity_program;
|
||||
mip1_program.info.images[0].mip_mode =
|
||||
ShaderRecompiler::IR::ImageMipMode::DynamicStorage;
|
||||
auto mip3_program = mip1_program;
|
||||
mip3_program.info.images[0].mip_count = 3;
|
||||
auto array_program = identity_program;
|
||||
array_program.info.images[0].dimension =
|
||||
auto mip1_image = image;
|
||||
mip1_image.mip_mode = ShaderRecompiler::IR::ImageMipMode::DynamicStorage;
|
||||
auto mip3_image = mip1_image;
|
||||
mip3_image.mip_count = 3;
|
||||
auto array_image = image;
|
||||
array_image.dimension =
|
||||
ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
|
||||
auto cube_program = array_program;
|
||||
cube_program.info.images[0].cube = true;
|
||||
auto cube_image = array_image;
|
||||
cube_image.cube = true;
|
||||
|
||||
const auto resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
ShaderComputeInputInfo identity_info;
|
||||
identity_info.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(identity_program);
|
||||
identity_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
make_program(image));
|
||||
identity_info.stage.resources = resources;
|
||||
auto rgb1_info = identity_info;
|
||||
rgb1_info.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(rgb1_program);
|
||||
rgb1_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
make_program(rgb1_image));
|
||||
auto converted_info = identity_info;
|
||||
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;
|
||||
mip1_info.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(mip1_program);
|
||||
mip1_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
make_program(mip1_image));
|
||||
auto mip3_info = identity_info;
|
||||
mip3_info.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(mip3_program);
|
||||
mip3_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
make_program(mip3_image));
|
||||
auto array_info = identity_info;
|
||||
array_info.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(array_program);
|
||||
array_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
make_program(array_image));
|
||||
auto cube_info = identity_info;
|
||||
cube_info.stage.program =
|
||||
std::make_shared<const ShaderRecompiler::IR::Program>(cube_program);
|
||||
cube_info.stage.program = std::make_shared<const ShaderRecompiler::IR::Program>(
|
||||
make_program(cube_image));
|
||||
|
||||
const auto identity_id = ShaderGetIdCS(regs, identity_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");
|
||||
|
||||
ShaderRecompiler::IR::Program mixed_sampler_program;
|
||||
mixed_sampler_program.values =
|
||||
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
mixed_sampler_program.resource_tracking_complete = true;
|
||||
ShaderRecompiler::IR::ImageResource sampled_image;
|
||||
sampled_image.kind = ShaderRecompiler::IR::ResourceKind::Image;
|
||||
@@ -22500,8 +22473,6 @@ void CheckImageSpecializationId() {
|
||||
"native variants");
|
||||
|
||||
ShaderRecompiler::IR::Program packed_atomic_program;
|
||||
packed_atomic_program.values =
|
||||
std::make_shared<ShaderRecompiler::IR::ValueProgram>();
|
||||
packed_atomic_program.resource_tracking_complete = true;
|
||||
auto packed_atomic_image = sampled_image;
|
||||
packed_atomic_image.kind =
|
||||
@@ -23245,7 +23216,7 @@ void CheckEmbeddedFetchLaneSpill() {
|
||||
Require("EmbeddedFetchLaneSpill", "compile",
|
||||
ShaderRecompiler::TryRecompile(code, options, result, &error), error);
|
||||
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");
|
||||
std::printf("[host] %-32s ok\n", "EmbeddedFetchLaneSpill");
|
||||
}
|
||||
|
||||
@@ -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/shader.h"
|
||||
|
||||
@@ -23,12 +23,11 @@ bool RejectSpecializationRead(void *userdata, uint64_t, uint32_t *) {
|
||||
Libs::Graphics::ShaderRecompiler::IR::Block &
|
||||
AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
program.values = std::make_shared<ValueProgram>();
|
||||
auto block = std::make_unique<Block>();
|
||||
auto *result = block.get();
|
||||
program.values->blocks.push_back(result);
|
||||
program.values->block_info.push_back({.id = 0});
|
||||
program.values->block_storage.push_back(std::move(block));
|
||||
program.blocks.push_back(result);
|
||||
program.block_info.push_back({.id = 0});
|
||||
program.block_storage.push_back(std::move(block));
|
||||
return *result;
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ SrtProgram(uint64_t address) {
|
||||
MemoryInfo memory;
|
||||
memory.kind = ResourceKind::ScalarAddress;
|
||||
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 high = Value(static_cast<uint32_t>(address >> 32u));
|
||||
auto &handle =
|
||||
@@ -53,7 +52,7 @@ SrtProgram(uint64_t address) {
|
||||
ValueOpcode::LoadAddressU32,
|
||||
{Value(&handle), Value(0u), Value(0u), Value(true)});
|
||||
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 &flat = value_block.AppendNewInst(ValueOpcode::ReadConst,
|
||||
@@ -62,7 +61,7 @@ SrtProgram(uint64_t address) {
|
||||
source.dwords[0] = Value(&flat);
|
||||
source.dwords[1] = Value(0u);
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -141,5 +140,5 @@ int main() {
|
||||
#include "graphics/shader/recompiler/ir/Block.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Type.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"
|
||||
+467
-388
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user