#include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/stringUtils.h" #include "common/threads.h" #include "graphics/guest_gpu/hardwareContext.h" #include "graphics/guest_gpu/pm4.h" #include "graphics/host_gpu/renderer/image/textureCommon.h" #include "graphics/host_gpu/renderer/image/imageView.h" #include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h" #include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h" #include "graphics/shader/recompiler/ExecMask.h" #include "graphics/shader/recompiler/ir/ResourceTracking.h" #include "graphics/shader/recompiler/ir/ScalarProvenance.h" #include "graphics/shader/recompiler/cfg/ShaderCFG.h" #include "graphics/shader/recompiler/decompiler/ShaderDecoder.h" #include "graphics/shader/recompiler/ir/ShaderIR.h" #include "graphics/shader/recompiler/ir/ShaderInfoCollection.h" #include "graphics/shader/recompiler/ShaderRecompiler.h" #include "graphics/shader/recompiler/emitter/SpirvEmitter.h" #include "graphics/shader/recompiler/ir/SrtPatcher.h" #include "graphics/shader/recompiler/ir/SrtWalker.h" #include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h" #include "graphics/shader/shader.h" #include "libs/agc.h" #include "spirv-tools/libspirv.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace Libs::Graphics { namespace { void Check(bool value, const char* text) { if (!value) { std::fprintf(stderr, "ShaderCfgTests: failed: %s\n", text); std::abort(); } } void CheckSpirvBinaryValidates(const std::vector& binary) { spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2); std::string messages; tools.SetMessageConsumer([&messages](spv_message_level_t, const char*, const spv_position_t& position, const char* message) { char buffer[1024] = {}; std::snprintf(buffer, sizeof(buffer), "%zu:%zu: %s\n", position.line, position.column, message); messages += buffer; }); if (!tools.Validate(binary)) { std::fprintf(stderr, "SPIR-V binary validation failed:\n%s\n", messages.c_str()); std::abort(); } } std::string DisassembleSpirvBinary(const std::vector& binary) { spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2); std::string messages; std::string source; tools.SetMessageConsumer([&messages](spv_message_level_t, const char*, const spv_position_t& position, const char* message) { char buffer[1024] = {}; std::snprintf(buffer, sizeof(buffer), "%zu:%zu: %s\n", position.line, position.column, message); messages += buffer; }); if (!tools.Disassemble(binary.data(), binary.size(), &source, SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT)) { std::fprintf(stderr, "SPIR-V binary disassembly failed:\n%s\n", messages.c_str()); std::abort(); } return std::string(source.c_str()); } uint32_t CountSourceOccurrences(const std::string& source, const char* needle) { uint32_t count = 0; uint32_t from = 0; for (;;) { const auto found = Common::FindIndex(source, std::string(needle), from); if (found == Common::FIND_INVALID_INDEX) { return count; } count++; from = found + static_cast(std::strlen(needle)); } } bool SpirvSourceHasInstructionUsing(const std::string& source, const char* opcode, const char* name) { std::istringstream stream(source); std::string line; while (std::getline(stream, line)) { if (Common::ContainsStr(line, opcode) && Common::ContainsStr(line, name)) { return true; } } return false; } bool SpirvContainsOpcode(const std::vector& binary, uint32_t opcode) { for (uint32_t word: binary) { if ((word & 0xffffu) == opcode) { return true; } } return false; } uint32_t SpirvInstructionOpcodeCount(const std::vector& binary, uint32_t opcode) { uint32_t count = 0; for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t op = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return count; } if (op == opcode) { count++; } i += word_count; } return count; } bool SpirvContainsVectorShuffle(const std::vector& binary, const std::array& selectors) { for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t op = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return false; } if (op == 79u && word_count == 9u && std::equal(selectors.begin(), selectors.end(), binary.begin() + i + 5u)) { return true; } i += word_count; } return false; } bool SpirvContainsCapability(const std::vector& binary, uint32_t capability) { for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return false; } if (opcode == 17u && word_count >= 2u && binary[i + 1] == capability) { return true; } i += word_count; } return false; } bool SpirvContainsExecutionMode(const std::vector& binary, uint32_t mode) { for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return false; } if (opcode == 16u && word_count >= 3u && binary[i + 2] == mode) { return true; } i += word_count; } return false; } bool SpirvContainsExtInst(const std::vector& binary, uint32_t ext_inst) { for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return false; } if (opcode == 12u && word_count >= 5u && binary[i + 4] == ext_inst) { return true; } i += word_count; } return false; } uint32_t SpirvExtInstCount(const std::vector& binary, uint32_t ext_inst) { uint32_t count = 0; for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return count; } if (opcode == 12u && word_count >= 5u && binary[i + 4] == ext_inst) { count++; } i += word_count; } return count; } bool SpirvContainsTypeImage(const std::vector& binary, uint32_t dim, uint32_t arrayed, uint32_t sampled, uint32_t multisampled = 0) { for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return false; } if (opcode == 25u && word_count >= 9u && binary[i + 3] == dim && binary[i + 5] == arrayed && binary[i + 6] == multisampled && binary[i + 7] == sampled) { return true; } i += word_count; } return false; } uint32_t SpirvDecorationValueCount(const std::vector& binary, uint32_t decoration, uint32_t value) { uint32_t count = 0; for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return count; } if (opcode == 71u && word_count >= 4u && binary[i + 2] == decoration && binary[i + 3] == value) { count++; } i += word_count; } return count; } bool SpirvHasDecorationValue(const std::vector& binary, uint32_t decoration, uint32_t value) { return SpirvDecorationValueCount(binary, decoration, value) != 0; } std::vector SpirvDecorationValueTargets(const std::vector& binary, uint32_t decoration, uint32_t value) { std::vector targets; for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return targets; } if (opcode == 71u && word_count >= 4u && binary[i + 2] == decoration && binary[i + 3] == value) { targets.push_back(binary[i + 1]); } i += word_count; } return targets; } bool SpirvTargetHasDecoration(const std::vector& binary, uint32_t target, uint32_t decoration) { for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; const uint32_t word_count = word >> 16u; if (word_count == 0 || i + word_count > binary.size()) { return false; } if (opcode == 71u && word_count >= 3u && binary[i + 1] == target && binary[i + 2] == decoration) { return true; } i += word_count; } return false; } bool SpirvHasDecorationValueWithDecoration(const std::vector& binary, uint32_t value_decoration, uint32_t value, uint32_t decoration) { const auto targets = SpirvDecorationValueTargets(binary, value_decoration, value); return std::any_of(targets.begin(), targets.end(), [&](uint32_t target) { return SpirvTargetHasDecoration(binary, target, decoration); }); } bool ProgramHasInput(const ShaderRecompiler::IR::Program& program, ShaderRecompiler::IR::StageInputKind kind) { return std::any_of(program.info.inputs.begin(), program.info.inputs.end(), [kind](const auto& input) { return input.kind == kind; }); } uint32_t ProgramInputCount(const ShaderRecompiler::IR::Program& program, ShaderRecompiler::IR::StageInputKind kind) { return static_cast( std::count_if(program.info.inputs.begin(), program.info.inputs.end(), [kind](const auto& input) { return input.kind == kind; })); } ShaderComputeInputInfo RegressionComputeInputInfo() { ShaderComputeInputInfo input_info; input_info.threads_num[0] = 1; input_info.threads_num[1] = 1; input_info.threads_num[2] = 1; input_info.workgroup_register = 40; return input_info; } ShaderPixelInputInfo RegressionPixelInputInfo() { return {}; } void SetIdentityInterpolatorSettings(ShaderPixelInputInfo* input_info) { Check(input_info != nullptr, "invalid pixel input info"); for (uint32_t i = 0; i < std::size(input_info->interpolator_settings); i++) { input_info->interpolator_settings[i] = i; } } void EnsureConfigInitialized() { static bool config_initialized = false; if (!config_initialized) { Common::ThreadsSubsystem::Instance()->Init(nullptr); Config::ConfigSubsystem::Instance()->Init(nullptr); Config::ConfigOptions options; options.printf_direction = Config::OutputDirection::Silent; Config::Load(options); Log::LogSubsystem::Instance()->Init(nullptr); ShaderInit(); config_initialized = true; } } void TestResourceDescriptorClassification() { uint32_t raw_texture[8] = {}; raw_texture[3] = 9u << 28u; raw_texture[5] = 2u << 27u; Check(ShaderClassifyResourceDescriptor(raw_texture) == ResourceDescriptorType::Texture, "raw texture descriptor must not be classified by ResourceDescriptor byte 23 fields"); uint32_t tagged_buffer[8] = {}; tagged_buffer[5] = 1u << 27u; Check(ShaderClassifyResourceDescriptor(tagged_buffer) == ResourceDescriptorType::Buffer, "tagged ResourceDescriptor buffer was not recognized"); uint32_t tagged_sampler[8] = {}; tagged_sampler[5] = 2u << 27u; Check(ShaderClassifyResourceDescriptor(tagged_sampler) == ResourceDescriptorType::Sampler, "tagged ResourceDescriptor sampler was not recognized"); uint32_t tagged_unused[8] = {}; tagged_unused[5] = 3u << 27u; Check(ShaderClassifyResourceDescriptor(tagged_unused) == ResourceDescriptorType::Unused, "tagged ResourceDescriptor unused slot was not recognized"); uint32_t raw_buffer[8] = {}; Check(ShaderClassifyResourceDescriptor(raw_buffer) == ResourceDescriptorType::Buffer, "raw buffer descriptor fallback was not preserved"); } constexpr uint32_t EncodeSMovB32(uint32_t dst, uint32_t src) { return 0x80000000u | (0x7du << 23u) | ((dst & 0x7fu) << 16u) | (0x03u << 8u) | (src & 0xffu); } constexpr uint32_t EncodeSop1(uint32_t opcode, uint32_t dst, uint32_t src) { return 0x80000000u | (0x7du << 23u) | ((dst & 0x7fu) << 16u) | ((opcode & 0xffu) << 8u) | (src & 0xffu); } constexpr uint32_t EncodeSop2(uint32_t opcode, uint32_t dst, uint32_t src0, uint32_t src1) { return 0x80000000u | ((opcode & 0x7fu) << 23u) | ((dst & 0x7fu) << 16u) | ((src1 & 0xffu) << 8u) | (src0 & 0xffu); } constexpr uint32_t EncodeSopk(uint32_t opcode, uint32_t dst, int16_t imm) { return 0x80000000u | (((opcode + 0x60u) & 0x7fu) << 23u) | ((dst & 0x7fu) << 16u) | static_cast(imm); } constexpr uint32_t EncodeSopc(uint32_t opcode, uint32_t src0, uint32_t src1) { return 0x80000000u | (0x7eu << 23u) | ((opcode & 0x7fu) << 16u) | ((src1 & 0xffu) << 8u) | (src0 & 0xffu); } constexpr uint32_t EncodeSopp(uint32_t opcode, uint32_t simm = 0) { return 0x80000000u | (0x7fu << 23u) | ((opcode & 0x7fu) << 16u) | (simm & 0xffffu); } void TestNativeShaderResourceDependencies() { const auto stages = ShaderPipelineStages( vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment | vk::ShaderStageFlagBits::eCompute); Check(stages == (vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eComputeShader), "shader-stage dependency mapping was not exact"); const auto shader_barrier = MakeShaderWriteDependency(); Check(shader_barrier.srcAccessMask == vk::AccessFlagBits::eShaderWrite && (shader_barrier.dstAccessMask & vk::AccessFlagBits::eShaderRead) && (shader_barrier.dstAccessMask & vk::AccessFlagBits::eVertexAttributeRead), "shader-write dependency does not expose draw/dispatch buffer writes"); ShaderRecompiler::IR::Program program; program.info.buffers.resize(3); program.info.buffers[0].written = true; program.info.buffers[1].read = true; program.info.buffers[2].written = true; ShaderRecompiler::IR::ResourceSnapshot resources; resources.buffers.resize(3); auto set_buffer = [&](uint32_t index, uint64_t address, uint16_t stride, uint32_t records) { ShaderBufferResource descriptor; descriptor.UpdateAddress48(address); descriptor.fields[1] |= static_cast(stride) << 16u; descriptor.fields[2] = records; std::memcpy(resources.buffers[index].dwords.data(), descriptor.fields, sizeof(descriptor.fields)); resources.buffers[index].dword_count = 4; }; set_buffer(0, 0x1000, 16, 3); set_buffer(1, 0x1800, 4, 7); set_buffer(2, 0x2000, 0, 64); const auto writes = CollectShaderBufferWrites(program, resources); Check(writes == std::vector({{0x1000, 48}, {0x2000, 64}}), "graphics/compute write collector lost or invented a storage-buffer range"); VulkanBuffer buffer; buffer.buffer = reinterpret_cast(uintptr_t {1}); const auto gds_barrier = MakeGdsDependency(buffer.buffer); Check((gds_barrier.srcAccessMask & vk::AccessFlagBits::eHostWrite) && (gds_barrier.srcAccessMask & vk::AccessFlagBits::eTransferWrite) && (gds_barrier.srcAccessMask & vk::AccessFlagBits::eShaderWrite) && gds_barrier.dstAccessMask == (vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite) && gds_barrier.size == VK_WHOLE_SIZE, "GDS dependency does not order host/transfer/shader writes"); } void TestNormalizedImageContracts() { ImageInfo container {}; container.data = {0x10000, 0x15000}; container.pixel_format = vk::Format::eR8G8B8A8Unorm; container.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm); container.type = Prospero::ImageType::kColor2D; container.extent = {64, 64, 1}; container.resources = {3, 4}; container.pitch = 64; container.bytes_per_block = 4; container.samples = 1; container.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB); container.mip_layout[0] = {0, 0x10000, 64, 64}; container.mip_layout[1] = {0x10000, 0x4000, 32, 32}; container.mip_layout[2] = {0x14000, 0x1000, 16, 16}; ImageInfo subresource = container; subresource.data = {0x22000, 0x1000}; subresource.extent = {32, 32, 1}; subresource.resources = {1, 1}; subresource.pitch = 32; subresource.mip_layout = {}; subresource.mip_layout[0] = {0, 0x1000, 32, 32}; Check(container.BlockExtent() == vk::Extent2D {64, 64}, "normalized image block extent changed"); Check(subresource.IsCompatible(container), "normalized compatible image was rejected"); Check(subresource.MipOf(container) == 1, "normalized mip lookup missed a subresource"); Check(subresource.SliceOf(container, 1) == 2, "normalized slice lookup missed a subresource"); auto incompatible = subresource; incompatible.samples = 2; Check(!incompatible.IsCompatible(container) && incompatible.MipOf(container) == -1, "sample-count mismatch was accepted as a compatible image"); auto compressed = container; compressed.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::kBc3UNorm); compressed.pitch = 128; compressed.extent.height = 64; Check(compressed.BlockExtent() == vk::Extent2D {32, 16}, "block-compressed extent was not expressed in blocks"); Check(ImageViewOps::FormatsCompatible(vk::Format::eR8G8B8A8Unorm, vk::Format::eR8G8B8A8Uint) && !ImageViewOps::FormatsCompatible(vk::Format::eD32Sfloat, vk::Format::eR32Sfloat) && ImageViewOps::FormatsCompatible(vk::Format::eBc3UnormBlock, vk::Format::eR32G32B32A32Uint), "Vulkan image-view compatibility classes diverged from production"); } void TestNativeSubgroupPolicy() { GraphicContext context; context.subgroup_size = 32; context.min_subgroup_size = 32; context.max_subgroup_size = 32; context.subgroup_size_control_enabled = true; context.required_subgroup_size_stages = vk::ShaderStageFlagBits::eAll; ShaderRecompiler::IR::Program safe; safe.wave_size = 32; safe.lane_mask_mode = ShaderLaneMaskMode::NativeWave; Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eVertex, safe).mode == ShaderSubgroupMode::Natural, "native wave32 policy changed"); safe.wave_size = 64; Check(SelectGraphicsLaneMaskMode(safe.wave_size) == ShaderLaneMaskMode::PerInvocation && ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eVertex, safe).mode == ShaderSubgroupMode::Unsupported, "wave64 graphics mismatch accepted native-wave mask lowering"); safe.lane_mask_mode = ShaderLaneMaskMode::PerInvocation; Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eVertex, safe).mode == ShaderSubgroupMode::PerInvocationGraphics, "wave64 graphics mismatch did not select per-invocation masks"); ShaderRecompiler::IR::Program cross_lane = safe; cross_lane.blocks.emplace_back().instructions.emplace_back().op = ShaderRecompiler::IR::Opcode::ReadLaneU32; Check(ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(cross_lane) && ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eVertex, cross_lane).mode == ShaderSubgroupMode::PerInvocationGraphics, "graphics mismatch did not select per-invocation masks"); auto cross_lane_compute = cross_lane; cross_lane_compute.lane_mask_mode = ShaderLaneMaskMode::NativeWave; Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eCompute, cross_lane_compute).mode == ShaderSubgroupMode::Unsupported, "cross-lane compute mismatch bypassed the exact subgroup requirement"); ShaderRecompiler::IR::Program zero_exec = safe; zero_exec.lane_mask_mode = ShaderLaneMaskMode::NativeWave; zero_exec.provenance.values.resize(3); zero_exec.provenance.values[2].op = ShaderRecompiler::IR::ScalarValueOp::Constant; zero_exec.provenance.values[2].imm = 0; auto& zero_bfm = zero_exec.blocks.emplace_back().instructions.emplace_back(); zero_bfm.op = ShaderRecompiler::IR::Opcode::BitFieldMaskU64; zero_bfm.dst.kind = ShaderRecompiler::IR::OperandKind::Register; zero_bfm.dst.reg = {ShaderRecompiler::IR::RegisterFile::Exec, 0}; zero_bfm.src_count = 2; zero_bfm.scalar_sources[0] = 2; Check(!ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(zero_exec) && ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eCompute, zero_exec).mode == ShaderSubgroupMode::FlattenedMasks, "compile-time uniform-zero EXEC write did not stay on the mask-free path"); ShaderRecompiler::IR::Program selective_exec = safe; auto& move_exec = selective_exec.blocks.emplace_back().instructions.emplace_back(); move_exec.op = ShaderRecompiler::IR::Opcode::MoveU32; move_exec.dst.kind = ShaderRecompiler::IR::OperandKind::Register; move_exec.dst.reg = {ShaderRecompiler::IR::RegisterFile::Exec, 0}; Check(ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(selective_exec), "selective EXEC write was classified mask-free"); ShaderRecompiler::IR::Program carry = safe; auto& add_carry = carry.blocks.emplace_back().instructions.emplace_back(); add_carry.op = ShaderRecompiler::IR::Opcode::IAddCarryU32; add_carry.dst2.kind = ShaderRecompiler::IR::OperandKind::Register; add_carry.dst2.reg = {ShaderRecompiler::IR::RegisterFile::Vcc, 0}; Check(ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(carry), "lane-varying VCC carry producer was classified mask-free"); ShaderRecompiler::IR::Program vcc_branch = safe; auto& branch_block = vcc_branch.blocks.emplace_back(); branch_block.terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; branch_block.terminator.condition = ShaderRecompiler::CFG::BranchCondition::VccNonZero; Check(ShaderRecompiler::Spirv::ProgramRequiresExactSubgroupSize(vcc_branch), "whole-wave VCC branch was classified mask-free"); ShaderRecompiler::IR::Program ds_partial = safe; ds_partial.lane_mask_mode = ShaderLaneMaskMode::NativeWave; ds_partial.blocks.emplace_back().instructions.emplace_back().op = ShaderRecompiler::IR::Opcode::DsAppend; Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eCompute, ds_partial).mode == ShaderSubgroupMode::Unsupported, "partial wave64 DS append bypassed the exact subgroup requirement"); context.max_subgroup_size = 64; const auto controlled = ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eCompute, cross_lane_compute); Check(controlled.mode == ShaderSubgroupMode::Controlled && controlled.required_size == 64, "supported controlled wave64 was not preferred over splitting"); context.subgroup_size = 64; context.subgroup_size_control_enabled = false; cross_lane.wave_size = 32; cross_lane.lane_mask_mode = ShaderLaneMaskMode::PerInvocation; Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eFragment, cross_lane).mode == ShaderSubgroupMode::Unsupported, "inverse graphics mismatch was accepted as one guest wave"); cross_lane_compute.wave_size = 32; Check(ConfigureShaderSubgroup(ShaderSubgroupCapabilities {context}, vk::ShaderStageFlagBits::eCompute, cross_lane_compute).mode == ShaderSubgroupMode::Unsupported, "inverse cross-lane compute mismatch was accepted"); } std::array ImageTestUserData(Prospero::ImageType type = Prospero::ImageType::kColor2D) { std::array data {}; for (uint32_t start = 0; start + 3u < data.size(); start += 4u) { data[start] = 0x1000u + start * 0x100u; data[start + 2u] = UINT32_MAX; data[start + 3u] = Prospero::GpuEnumValue(type) << 28u; } return data; } void SetImageTestType(std::array* data, uint32_t srsrc, Prospero::ImageType type) { const auto type_dword = srsrc * 4u + 3u; Check(data != nullptr && type_dword < data->size(), "invalid image test descriptor source"); (*data)[type_dword] = Prospero::GpuEnumValue(type) << 28u; } void SetImageTestFormat(std::array* data, uint32_t srsrc, Prospero::BufferFormat format) { const auto format_dword = srsrc * 4u + 1u; Check(data != nullptr && format_dword < data->size(), "invalid image test descriptor source"); (*data)[format_dword] = Prospero::GpuEnumValue(format) << 20u; } bool ReadZeroTestMemory(void*, uint64_t, uint32_t* value) { if (value == nullptr) { return false; } *value = 0; return true; } constexpr uint32_t EncodeVop2(uint32_t opcode, uint32_t dst, uint32_t src0, uint32_t src1) { return ((opcode & 0x3fu) << 25u) | ((dst & 0xffu) << 17u) | ((src1 & 0xffu) << 9u) | (src0 & 0x1ffu); } constexpr uint32_t EncodeVop1(uint32_t opcode, uint32_t dst, uint32_t src0) { return (0x3fu << 25u) | ((dst & 0xffu) << 17u) | ((opcode & 0xffu) << 9u) | (src0 & 0x1ffu); } constexpr uint32_t EncodeVop1Sdwa(uint32_t src0, uint32_t dst_sel = 6, uint32_t dst_u = 0, uint32_t src0_sel = 6, uint32_t src0_sext = 0, uint32_t src0_neg = 0, uint32_t src0_abs = 0, uint32_t s0 = 0) { return (src0 & 0xffu) | ((dst_sel & 0x7u) << 8u) | ((dst_u & 0x3u) << 11u) | ((src0_sel & 0x7u) << 16u) | ((src0_sext & 0x1u) << 19u) | ((src0_neg & 0x1u) << 20u) | ((src0_abs & 0x1u) << 21u) | ((s0 & 0x1u) << 23u); } constexpr uint32_t EncodeVop1Dpp(uint32_t src0, uint32_t dpp_ctrl = 0, uint32_t row_mask = 0xf, uint32_t bank_mask = 0xf) { return (src0 & 0xffu) | ((dpp_ctrl & 0x1ffu) << 8u) | ((bank_mask & 0xfu) << 24u) | ((row_mask & 0xfu) << 28u); } constexpr uint32_t EncodeVop2Sdwa(uint32_t src0, uint32_t dst_sel = 6, uint32_t dst_u = 0, uint32_t src0_sel = 6, uint32_t src1_sel = 6, uint32_t src0_sext = 0, uint32_t src1_sext = 0, uint32_t src0_neg = 0, uint32_t src0_abs = 0, uint32_t src1_neg = 0, uint32_t src1_abs = 0, uint32_t s0 = 0, uint32_t s1 = 0) { return (src0 & 0xffu) | ((dst_sel & 0x7u) << 8u) | ((dst_u & 0x3u) << 11u) | ((src0_sel & 0x7u) << 16u) | ((src0_sext & 0x1u) << 19u) | ((src0_neg & 0x1u) << 20u) | ((src0_abs & 0x1u) << 21u) | ((s0 & 0x1u) << 23u) | ((src1_sel & 0x7u) << 24u) | ((src1_sext & 0x1u) << 27u) | ((src1_neg & 0x1u) << 28u) | ((src1_abs & 0x1u) << 29u) | ((s1 & 0x1u) << 31u); } constexpr uint32_t EncodeVop2Dpp(uint32_t src0, uint32_t dpp_ctrl = 0, uint32_t row_mask = 0xf, uint32_t bank_mask = 0xf, uint32_t src0_neg = 0, uint32_t src0_abs = 0, uint32_t src1_neg = 0, uint32_t src1_abs = 0) { return (src0 & 0xffu) | ((dpp_ctrl & 0x1ffu) << 8u) | ((src0_neg & 0x1u) << 20u) | ((src0_abs & 0x1u) << 21u) | ((src1_neg & 0x1u) << 22u) | ((src1_abs & 0x1u) << 23u) | ((bank_mask & 0xfu) << 24u) | ((row_mask & 0xfu) << 28u); } constexpr uint32_t EncodeVopc(uint32_t opcode, uint32_t src0, uint32_t src1) { return (0x3eu << 25u) | ((opcode & 0xffu) << 17u) | ((src1 & 0xffu) << 9u) | (src0 & 0x1ffu); } constexpr uint32_t EncodeVopcSdwa(uint32_t src0, uint32_t sdst = 0, uint32_t sd = 0, uint32_t src0_sel = 6, uint32_t src1_sel = 6, uint32_t src0_sext = 0, uint32_t src1_sext = 0, uint32_t src0_neg = 0, uint32_t src0_abs = 0, uint32_t src1_neg = 0, uint32_t src1_abs = 0, uint32_t s0 = 0, uint32_t s1 = 0) { return (src0 & 0xffu) | ((sdst & 0x7fu) << 8u) | ((sd & 0x1u) << 15u) | ((src0_sel & 0x7u) << 16u) | ((src0_sext & 0x1u) << 19u) | ((src0_neg & 0x1u) << 20u) | ((src0_abs & 0x1u) << 21u) | ((s0 & 0x1u) << 23u) | ((src1_sel & 0x7u) << 24u) | ((src1_sext & 0x1u) << 27u) | ((src1_neg & 0x1u) << 28u) | ((src1_abs & 0x1u) << 29u) | ((s1 & 0x1u) << 31u); } constexpr uint32_t EncodeVop3Word0(uint32_t opcode, uint32_t dst, uint32_t op_sel = 0, uint32_t abs = 0, bool clamp = false) { return (0x35u << 26u) | ((opcode & 0x3ffu) << 16u) | (dst & 0xffu) | ((abs & 0x7u) << 8u) | ((op_sel & 0xfu) << 11u) | (clamp ? (1u << 15u) : 0u); } constexpr uint32_t EncodeVop3Word0Sdst(uint32_t opcode, uint32_t dst, uint32_t sdst) { return (0x35u << 26u) | ((opcode & 0x3ffu) << 16u) | ((sdst & 0x7fu) << 8u) | (dst & 0xffu); } constexpr uint32_t EncodeVop3Word1(uint32_t src0, uint32_t src1, uint32_t src2) { return (src0 & 0x1ffu) | ((src1 & 0x1ffu) << 9u) | ((src2 & 0x1ffu) << 18u); } constexpr uint32_t EncodeVop3pWord0(uint32_t opcode, uint32_t dst, uint32_t op_sel = 0, uint32_t op_sel_hi = 0, uint32_t neg_hi = 0, bool clamp = false) { return (0x33u << 26u) | ((opcode & 0x7fu) << 16u) | (dst & 0xffu) | ((neg_hi & 0x7u) << 8u) | ((op_sel & 0x7u) << 11u) | (((op_sel_hi >> 2u) & 0x1u) << 14u) | (clamp ? (1u << 15u) : 0u); } constexpr uint32_t EncodeVop3pWord1(uint32_t src0, uint32_t src1, uint32_t src2, uint32_t op_sel_hi = 0, uint32_t neg = 0) { return (src0 & 0x1ffu) | ((src1 & 0x1ffu) << 9u) | ((src2 & 0x1ffu) << 18u) | ((op_sel_hi & 0x3u) << 27u) | ((neg & 0x7u) << 29u); } constexpr uint32_t EncodeSmem0(uint32_t opcode, uint32_t dst, uint32_t sbase) { return (0x3du << 26u) | ((opcode & 0xffu) << 18u) | ((dst & 0x7fu) << 6u) | (sbase & 0x3fu); } constexpr uint32_t EncodeMubuf0(uint32_t opcode, uint32_t offset = 0, bool idxen = true, bool glc = false) { return (0x38u << 26u) | ((opcode & 0x7fu) << 18u) | (idxen ? (1u << 13u) : 0u) | (glc ? (1u << 14u) : 0u) | (offset & 0xfffu); } constexpr uint32_t EncodeMubuf1(uint32_t vdata, uint32_t srsrc, uint32_t vaddr, uint32_t soffset = 128) { return ((soffset & 0xffu) << 24u) | ((srsrc & 0x1fu) << 16u) | ((vdata & 0xffu) << 8u) | (vaddr & 0xffu); } constexpr uint32_t EncodeMtbuf0(uint32_t opcode, uint32_t dfmt, uint32_t nfmt, uint32_t offset = 0, bool idxen = true, bool offen = false) { return (0x3au << 26u) | (offset & 0xfffu) | (offen ? (1u << 12u) : 0u) | (idxen ? (1u << 13u) : 0u) | ((opcode & 0x7u) << 16u) | ((dfmt & 0xfu) << 19u) | ((nfmt & 0x7u) << 23u); } constexpr uint32_t EncodeMtbuf1(uint32_t opcode, uint32_t vdata, uint32_t srsrc, uint32_t vaddr, uint32_t soffset = 128) { return ((opcode >> 3u) & 1u) << 21u | ((soffset & 0xffu) << 24u) | ((srsrc & 0x1fu) << 16u) | ((vdata & 0xffu) << 8u) | (vaddr & 0xffu); } constexpr uint32_t EncodeFlat0(uint32_t opcode, uint32_t seg, uint32_t offset = 0) { return (0x37u << 26u) | ((opcode & 0x7fu) << 18u) | ((seg & 0x3u) << 14u) | (offset & 0xfffu); } constexpr uint32_t EncodeFlat1(uint32_t vdst, uint32_t saddr, uint32_t data, uint32_t addr) { return ((vdst & 0xffu) << 24u) | ((saddr & 0x7fu) << 16u) | ((data & 0xffu) << 8u) | (addr & 0xffu); } constexpr uint32_t EncodeDs0(uint32_t opcode, uint32_t offset = 0) { return (0x36u << 26u) | ((opcode & 0xffu) << 18u) | (offset & 0xffffu); } constexpr uint32_t EncodeDs1Ex(uint32_t vdst, uint32_t data1, uint32_t data0, uint32_t addr) { return ((vdst & 0xffu) << 24u) | ((data1 & 0xffu) << 16u) | ((data0 & 0xffu) << 8u) | (addr & 0xffu); } constexpr uint32_t EncodeDs1(uint32_t vdst, uint32_t data0, uint32_t addr) { return EncodeDs1Ex(vdst, 0, data0, addr); } constexpr uint32_t EncodeMimg0(uint32_t opcode, uint32_t dmask, bool glc = false, uint32_t dim = 1) { return (0x3cu << 26u) | ((opcode & 0x7fu) << 18u) | ((dmask & 0xfu) << 8u) | ((dim & 0x7u) << 3u) | (glc ? (1u << 13u) : 0u) | ((opcode >> 7u) & 1u); } constexpr uint32_t EncodeMimg1(uint32_t vdata, uint32_t srsrc, uint32_t ssamp, uint32_t vaddr, bool a16 = false) { return ((ssamp & 0x1fu) << 21u) | ((srsrc & 0x1fu) << 16u) | ((vdata & 0xffu) << 8u) | (vaddr & 0xffu) | (a16 ? (1u << 30u) : 0u); } constexpr uint32_t EncodeVintrp(uint32_t opcode, uint32_t vdst, uint32_t attr, uint32_t chan, uint32_t vsrc) { return (0x32u << 26u) | ((opcode & 0x3u) << 16u) | ((vdst & 0xffu) << 18u) | ((attr & 0x3fu) << 10u) | ((chan & 0x3u) << 8u) | (vsrc & 0xffu); } constexpr uint32_t EncodeExp0(uint32_t target, uint32_t en, bool done = true, bool compr = false, bool vm = false) { return (0x3eu << 26u) | ((target & 0x3fu) << 4u) | (en & 0xfu) | (compr ? (1u << 10u) : 0u) | (done ? (1u << 11u) : 0u) | (vm ? (1u << 12u) : 0u); } constexpr uint32_t EncodeExp1(uint32_t src0, uint32_t src1, uint32_t src2, uint32_t src3) { return (src0 & 0xffu) | ((src1 & 0xffu) << 8u) | ((src2 & 0xffu) << 16u) | ((src3 & 0xffu) << 24u); } void TestNewShaderRecompilerSMovB32() { const uint32_t shader[] = { EncodeSMovB32(0, 129), // s_mov_b32 s0, 1 EncodeSMovB32(1, 255), // s_mov_b32 s1, 0x12345678 0x12345678u, EncodeSMovB32(2, 1), // s_mov_b32 s2, s1 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(!result.spirv.empty(), "new shader recompiler produced no SPIR-V"); Check(result.spirv.front() == 0x07230203u, "new shader recompiler did not emit SPIR-V binary"); Check(Common::ContainsStr(result.decoded_dump, "s_mov_b32 s0, 1"), "new decoder did not decode inline S_MOV_B32 operand"); Check(Common::ContainsStr(result.decoded_dump, "s_mov_b32 s1, 0x12345678"), "new decoder did not decode literal S_MOV_B32 operand"); Check(Common::ContainsStr(result.decoded_dump, "s_mov_b32 s2, s1"), "new decoder did not decode register S_MOV_B32 operand"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 s0, 0x00000001"), "new IR did not lower inline S_MOV_B32"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 s1, 0x12345678"), "new IR did not lower literal S_MOV_B32"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 s2, s1"), "new IR did not lower register S_MOV_B32"); Check(std::find(result.spirv.begin(), result.spirv.end(), 0x12345678u) != result.spirv.end(), "new SPIR-V emitter did not encode the literal as a binary word"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerSoppMarkers() { const uint32_t shader[] = { EncodeSopp(0x00, 3), // s_nop 3 EncodeSopp(0x0c, 0), // s_waitcnt 0 EncodeSopp(0x10, 0x0f), // s_sendmsg 15 EncodeSopp(0x16, 0x2a), // s_ttracedata 42 EncodeSopp(0x20, 1), // s_inst_prefetch 1 EncodeSopp(0x0a, 0), // s_barrier EncodeSopp(0x01, 0), // s_endpgm }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_nop 0x00000003"), "new decoder did not decode SOPP s_nop"); Check(Common::ContainsStr(result.decoded_dump, "s_waitcnt 0x00000000"), "new decoder did not decode SOPP s_waitcnt"); Check(Common::ContainsStr(result.decoded_dump, "s_sendmsg 0x0000000f"), "new decoder did not decode SOPP s_sendmsg"); Check(Common::ContainsStr(result.decoded_dump, "s_ttracedata 0x0000002a"), "new decoder did not decode SOPP s_ttracedata"); Check(Common::ContainsStr(result.decoded_dump, "s_inst_prefetch 0x00000001"), "new decoder did not decode SOPP s_inst_prefetch"); Check(Common::ContainsStr(result.decoded_dump, "s_barrier"), "new decoder did not decode SOPP s_barrier"); Check(Common::ContainsStr(result.ir_dump, "ControlNop null, 0x00000003"), "SOPP s_nop did not lower to an IR marker"); Check(Common::ContainsStr(result.ir_dump, "Waitcnt null, 0x00000000"), "SOPP s_waitcnt did not lower to an IR marker"); Check(Common::ContainsStr(result.ir_dump, "Sendmsg null, 0x0000000f"), "SOPP s_sendmsg did not lower to an IR marker"); Check(Common::ContainsStr(result.ir_dump, "TtraceData null, 0x0000002a"), "SOPP s_ttracedata did not lower to an IR marker"); Check(Common::ContainsStr(result.ir_dump, "InstPrefetch null, 0x00000001"), "SOPP s_inst_prefetch did not lower to an IR marker"); Check(Common::ContainsStr(result.ir_dump, "Barrier null"), "SOPP s_barrier did not lower to an IR marker"); Check(SpirvContainsOpcode(result.spirv, 224), "SPIR-V binary does not contain OpControlBarrier"); Check(std::find(result.spirv.begin(), result.spirv.end(), 264u) != result.spirv.end(), "SPIR-V barrier does not use workgroup acquire-release memory semantics"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerSopkWaitcntMarkers() { const uint32_t shader[] = { EncodeSopk(0x17, 125, 0xffff), // s_waitcnt_vscnt null, 0xffff EncodeSopk(0x18, 125, 0), // s_waitcnt_vmcnt null, 0 EncodeSopk(0x19, 125, 0), // s_waitcnt_expcnt null, 0 EncodeSopk(0x1a, 125, 0), // s_waitcnt_lgkmcnt null, 0 EncodeSopp(0x01, 0), // s_endpgm }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_waitcnt 0"), "new decoder did not decode SOPK waitcnt marker"); Check(Common::ContainsStr(result.decoded_dump, "s_waitcnt 65535"), "SOPK waitcnt marker immediate was not kept unsigned"); Check(Common::ContainsStr(result.ir_dump, "Waitcnt null, 0x00000000"), "SOPK waitcnt did not lower to an IR marker"); Check(Common::ContainsStr(result.ir_dump, "Waitcnt null, 0x0000ffff"), "SOPK waitcnt marker immediate was not lowered as 16-bit unsigned"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerRdna2ScalarOpcodes() { const uint32_t shader[] = { EncodeSMovB32(2, 135), // s2 = 7 EncodeSMovB32(106, 144), // vcc_lo = 16 EncodeSop1(0x1d, 106, 128), // s_bitset1_b32 vcc_lo, 0 EncodeSopk(0x13, 106, 0x1019), // s_setreg_b32 vcc_lo, 0x1019 EncodeSop2(0x02, 106, 2, 239), // s_add_i32 vcc_lo, s2, pops_exiting_wave_id EncodeSopp(0x0e, 0), // s_sleep 0 EncodeSop2(0x02, 106, 239, 2), // s_add_i32 vcc_lo, pops_exiting_wave_id, s2 EncodeSopp(0x01, 0), // s_endpgm }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_bitset1_b32 vcc_lo, 0"), "new decoder did not decode RDNA2 S_BITSET1_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_setreg_b32 vcc_lo, 0x00001019"), "new decoder did not decode S_SETREG_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_add_i32 vcc_lo, s2, pops_exiting_wave_id"), "new decoder did not decode pops_exiting_wave_id as RHS scalar source"); Check(Common::ContainsStr(result.decoded_dump, "s_add_i32 vcc_lo, pops_exiting_wave_id, s2"), "new decoder did not decode pops_exiting_wave_id as LHS scalar source"); Check(Common::ContainsStr(result.decoded_dump, "s_sleep 0x00000000"), "new decoder did not decode S_SLEEP"); Check(Common::ContainsStr(result.ir_dump, "BitSetU32 vcc_lo, vcc_lo, 0x00000000"), "S_BITSET1_B32 did not lower to bit-set IR using the destination as input"); Check(Common::ContainsStr(result.ir_dump, "ScalarSignedAddOverflowI32 vcc_lo, s2, 0x00000000"), "pops_exiting_wave_id RHS did not lower to a deterministic zero value"); Check(Common::ContainsStr(result.ir_dump, "ScalarSignedAddOverflowI32 vcc_lo, 0x00000000, s2"), "pops_exiting_wave_id LHS did not lower to a deterministic zero value"); Check(Common::ContainsStr(result.ir_dump, "ControlNop null, vcc_lo"), "S_SETREG_B32 did not lower to an explicit control marker"); Check(Common::ContainsStr(result.ir_dump, "ControlNop null, 0x00000000"), "S_SLEEP did not lower to an explicit control marker"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical for S_BITSET1_B32"); Check(SpirvContainsOpcode(result.spirv, 197), "SPIR-V binary does not contain OpBitwiseOr for S_BITSET1_B32"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerScalarVectorAlu() { const uint32_t shader[] = { EncodeSMovB32(0, 129), // s0 = 1 EncodeSMovB32(1, 130), // s1 = 2 EncodeSop2(0x00, 2, 0, 1), // s_add_u32 s2, s0, s1 EncodeSop2(0x01, 3, 2, 129), // s_sub_u32 s3, s2, 1 EncodeSop2(0x0e, 4, 2, 3), // s_and_b32 s4, s2, s3 EncodeSop2(0x10, 5, 2, 3), // s_or_b32 s5, s2, s3 EncodeSop2(0x12, 6, 2, 3), // s_xor_b32 s6, s2, s3 EncodeSop2(0x1e, 7, 2, 129), // s_lshl_b32 s7, s2, 1 EncodeSop2(0x20, 8, 7, 129), // s_lshr_b32 s8, s7, 1 EncodeSop2(0x2e, 9, 2, 1), // s_lshl1_add_u32 s9, s2, s1 EncodeSop2(0x2f, 10, 9, 1), // s_lshl2_add_u32 s10, s9, s1 EncodeSop2(0x30, 11, 10, 1), // s_lshl3_add_u32 s11, s10, s1 EncodeSop2(0x31, 12, 11, 1), // s_lshl4_add_u32 s12, s11, s1 EncodeSop2(0x35, 13, 12, 1), // s_mul_hi_u32 s13, s12, s1 EncodeSopc(0x08, 8, 1), // s_cmp_gt_u32 s8, s1 EncodeSop2(0x04, 14, 13, 129), // s_addc_u32 s14, s13, 1 EncodeVop2(0x03, 1, 242, 0), // v_add_f32 v1, 1.0, v0 EncodeVop2(0x08, 2, 1 + 256, 1), // v_mul_f32 v2, v1, v1 EncodeVop2(0x25, 3, 1 + 256, 2), // v_add_nc_u32 v3, v1, v2 EncodeVop2(0x1b, 4, 3 + 256, 2), // v_and_b32 v4, v3, v2 EncodeVop2(0x0b, 6, 3 + 256, 4), // v_mul_u32_u24 v6, v3, v4 EncodeVop2(0x09, 8, 4 + 256, 6), // v_mul_i32_i24 v8, v4, v6 EncodeVop2(0x22, 7, 4 + 256, 6), // v_bcnt_u32_b32 v7, v4, v6 EncodeVopc(0xc4, 3 + 256, 2), // v_cmp_gt_u32 v3, v2 EncodeVop2(0x01, 5, 1 + 256, 2), // v_cndmask_b32 v5, v1, v2 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_add_u32 s2, s0, s1"), "new decoder did not decode SOP2 add"); Check(Common::ContainsStr(result.decoded_dump, "s_addc_u32 s14, s13, 1"), "new decoder did not decode old-backed S_ADD_C_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_cmp_gt_u32 s8, s1"), "new decoder did not decode SOPC compare"); Check(Common::ContainsStr(result.decoded_dump, "s_lshl1_add_u32 s9, s2, s1"), "new decoder did not decode old-backed S_LSHL1_ADD_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_lshl2_add_u32 s10, s9, s1"), "new decoder did not decode old-backed S_LSHL2_ADD_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_lshl3_add_u32 s11, s10, s1"), "new decoder did not decode old-backed S_LSHL3_ADD_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_lshl4_add_u32 s12, s11, s1"), "new decoder did not decode old-backed S_LSHL4_ADD_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_mul_hi_u32 s13, s12, s1"), "new decoder did not decode old-backed S_MUL_HI_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_add_f32 v1"), "new decoder did not decode VOP2 float add"); Check(Common::ContainsStr(result.decoded_dump, "v_cndmask_b32 v5"), "new decoder did not decode VOP2 conditional mask select"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_u32_u24 v6"), "new decoder did not decode VOP2 24-bit multiply"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_i32_i24 v8"), "new decoder did not decode VOP2 signed 24-bit multiply"); Check(Common::ContainsStr(result.decoded_dump, "v_bcnt_u32_b32 v7"), "new decoder did not decode old-backed V_BCNT_U32_B32"); Check(Common::ContainsStr(result.ir_dump, "ScalarAddCarryU32 s2, s0, s1, 0x00000000"), "SOP2 add did not lower to scalar carry-writing IR"); Check(Common::ContainsStr(result.ir_dump, "ScalarAddCarryU32 s14, s13, 0x00000001, scc"), "S_ADD_C_U32 did not lower to scalar carry IR"); Check(Common::ContainsStr(result.ir_dump, "ScalarShiftLeftAddCarryU32 s9, s2, 0x00000001, s1"), "S_LSHL1_ADD_U32 did not lower through carry-writing shift-left-add IR"); Check(Common::ContainsStr(result.ir_dump, "ScalarShiftLeftAddCarryU32 s10, s9, 0x00000002, s1"), "S_LSHL2_ADD_U32 did not lower through carry-writing shift-left-add IR"); Check( Common::ContainsStr(result.ir_dump, "ScalarShiftLeftAddCarryU32 s11, s10, 0x00000003, s1"), "S_LSHL3_ADD_U32 did not lower through carry-writing shift-left-add IR"); Check( Common::ContainsStr(result.ir_dump, "ScalarShiftLeftAddCarryU32 s12, s11, 0x00000004, s1"), "S_LSHL4_ADD_U32 did not lower through carry-writing shift-left-add IR"); Check(Common::ContainsStr(result.ir_dump, "UMulHighU32 s13, s12, s1"), "S_MUL_HI_U32 did not lower to unsigned high-multiply IR"); Check(Common::ContainsStr(result.ir_dump, "CompareGtU32"), "SOPC compare did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FAddF32 v1"), "VOP2 float add did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseAndU32 v4"), "VOP2 bitwise op did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "SelectMaskU32 v5, vcc_lo, v2, v1"), "VOP2 conditional mask did not lower through lane-mask select IR"); Check(Common::ContainsStr(result.ir_dump, "UMulU24U32 v6"), "VOP2 24-bit multiply did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMulI24U32 v8"), "VOP2 signed 24-bit multiply did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitCountAddU32 v7, v4, v6"), "V_BCNT_U32_B32 did not lower to bit-count-add IR"); Check(SpirvContainsOpcode(result.spirv, 128), "SPIR-V binary does not contain OpIAdd"); Check(SpirvContainsOpcode(result.spirv, 149), "SPIR-V binary does not contain OpIAddCarry"); Check(SpirvContainsOpcode(result.spirv, 129), "SPIR-V binary does not contain OpFAdd"); Check(SpirvContainsOpcode(result.spirv, 132), "SPIR-V binary does not contain OpIMul"); Check(SpirvContainsOpcode(result.spirv, 151), "SPIR-V binary does not contain OpUMulExtended"); Check(SpirvContainsOpcode(result.spirv, 205), "SPIR-V binary does not contain OpBitCount"); Check(SpirvContainsOpcode(result.spirv, 169), "SPIR-V binary does not contain OpSelect"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerVop3LaneReadDestinationEncoding() { // Lane-read instructions have scalar results, but their VOP3A destination is encoded in // VDST [7:0], not the VOP3B SDST field [14:8]. const uint32_t shader[] = { EncodeVop3Word0(0x182, 25), EncodeVop3Word1(5 + 256, 0, 0), // v_readfirstlane_b32 s25, v5 EncodeVop3Word0(0x360, 26), EncodeVop3Word1(5 + 256, 130, 0), // v_readlane_b32 s26, v5, 2 EncodeSopp(0x01, 0), }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "v_readfirstlane_b32 s25, v5"), "VOP3 V_READFIRSTLANE_B32 destination was not decoded from VDST"); Check(Common::ContainsStr(result.decoded_dump, "v_readlane_b32 s26, v5, 2"), "VOP3 V_READLANE_B32 destination was not decoded from VDST"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerMoreAluFamilies() { const uint32_t shader[] = { EncodeSopk(0x00, 9, 7), // s_movk_i32 s9, 7 EncodeSopk(0x0b, 9, 3), // s_cmp_gt_u32 s9, 3 EncodeVop1(0x00, 0, 0), // v_nop EncodeVop1(0x01, 5, 9), // v_mov_b32 v5, s9 EncodeVop1(0x01, 103, 249), EncodeVop1Sdwa(5, 6, 0, 4), // v_mov_b32 v103, v5 word0 EncodeVop1(0x06, 104, 249), EncodeVop1Sdwa(103, 6, 0, 4), // v_cvt_f32_u32 v104, v103 word0 EncodeVop1(0x0a, 105, 249), EncodeVop1Sdwa(6, 5, 2, 6), // v_cvt_f16_f32 v105.hi, v6 EncodeVop1(0x01, 106, 250), EncodeVop1Dpp(5), // v_mov_b32 v106, v5 dpp EncodeVop1(0x02, 24, 5 + 256), // v_readfirstlane_b32 s24, v5 EncodeVop1(0x06, 6, 5 + 256), // v_cvt_f32_u32 v6, v5 EncodeVop1(0x07, 7, 6 + 256), // v_cvt_u32_f32 v7, v6 EncodeVop1(0x05, 9, 5 + 256), // v_cvt_f32_i32 v9, v5 EncodeVop1(0x08, 10, 6 + 256), // v_cvt_i32_f32 v10, v6 EncodeVop1(0x0a, 99, 6 + 256), // v_cvt_f16_f32 v99, v6 EncodeVop1(0x0b, 100, 99 + 256), // v_cvt_f32_f16 v100, v99 0x7e1016f9u, 0x00250602u, // v_cvt_f32_f16 v8, abs(v2.hi) EncodeVop1(0x0d, 64, 6 + 256), // v_cvt_flr_i32_f32 v64, v6 EncodeVop1(0x0e, 81, 5 + 256), // v_cvt_off_f32_i4 v81, v5 EncodeVop1(0x11, 65, 5 + 256), // v_cvt_f32_ubyte0 v65, v5 EncodeVop1(0x12, 66, 5 + 256), // v_cvt_f32_ubyte1 v66, v5 EncodeVop1(0x13, 67, 5 + 256), // v_cvt_f32_ubyte2 v67, v5 EncodeVop1(0x14, 68, 5 + 256), // v_cvt_f32_ubyte3 v68, v5 EncodeVop1(0x2a, 11, 6 + 256), // v_rcp_f32 v11, v6 EncodeVop1(0x20, 12, 6 + 256), // v_fract_f32 v12, v6 EncodeVop1(0x21, 13, 6 + 256), // v_trunc_f32 v13, v6 EncodeVop1(0x22, 14, 6 + 256), // v_ceil_f32 v14, v6 EncodeVop1(0x23, 15, 6 + 256), // v_rndne_f32 v15, v6 EncodeVop1(0x24, 16, 6 + 256), // v_floor_f32 v16, v6 EncodeVop1(0x25, 17, 6 + 256), // v_exp_f32 v17, v6 EncodeVop1(0x27, 18, 6 + 256), // v_log_f32 v18, v6 EncodeVop1(0x2e, 19, 6 + 256), // v_rsq_f32 v19, v6 EncodeVop1(0x33, 20, 6 + 256), // v_sqrt_f32 v20, v6 EncodeVop1(0x35, 21, 6 + 256), // v_sin_f32 v21, v6 EncodeVop1(0x36, 22, 6 + 256), // v_cos_f32 v22, v6 EncodeVop1(0x37, 27, 5 + 256), // v_not_b32 v27, v5 EncodeVop1(0x38, 28, 5 + 256), // v_bfrev_b32 v28, v5 EncodeVop1(0x39, 31, 5 + 256), // v_ffbh_u32 v31, v5 EncodeVop1(0x3a, 32, 5 + 256), // v_ffbl_b32 v32, v5 0x7e6e870cu, // v_movrels_b32 v55, v12 EncodeVop2(0x1e, 83, 5 + 256, 6), // v_xnor_b32 v83, v5, v6 EncodeVop2(0x23, 84, 5 + 256, 6), // v_mbcnt_lo_u32_b32 v84, v5, v6 EncodeVop2(0x24, 85, 5 + 256, 6), // v_mbcnt_hi_u32_b32 v85, v5, v6 EncodeVop2(0x1f, 89, 6 + 256, 6), // v_mac_f32 v89, v6, v6 EncodeVop2(0x20, 90, 6 + 256, 5), 0x3f800000u, // v_madmk_f32 v90, v6, 1.0, v5 EncodeVop2(0x21, 91, 6 + 256, 5), 0x40000000u, // v_madak_f32 v91, v6, v5, 2.0 EncodeVop2(0x2b, 92, 6 + 256, 6), // v_mac_f32 v92, v6, v6 EncodeVop2(0x2c, 93, 6 + 256, 5), 0x3f000000u, // v_madmk_f32 v93, v6, 0.5, v5 EncodeVop2(0x2d, 94, 6 + 256, 5), 0x40400000u, // v_madak_f32 v94, v6, v5, 3.0 EncodeVop2(0x2f, 95, 6 + 256, 6), // v_cvt_pkrtz_f16_f32 v95, v6, v6 EncodeVop2(0x02, 102, 95 + 256, 95), // v_dot2c_f32_f16 v102, v95, v95 EncodeVop2(0x28, 97, 5 + 256, 6), // v_addc_u32 v97, vcc, v5, v6, vcc EncodeVop2(0x25, 123, 249, 6), EncodeVop2Sdwa(5, 6, 0, 4, 6), EncodeVop2(0x1b, 124, 249, 6), EncodeVop2Sdwa(5, 6, 0, 4, 5), 0x025e6af9u, 0x16060635u, // v_cndmask_b32 v47, v53, -v53 0x100490f9u, 0x86860600u, // v_mul_f32 v2, s0, s72 (SDWA full) EncodeVop2(0x35, 9, 249, 1), EncodeVop2Sdwa(0, 4, 2, 4, 4), 0x660000f9u, EncodeVop2Sdwa(0, 4, 2, 4, 4), EncodeVop2(0x03, 125, 250, 6), EncodeVop2Dpp(5), EncodeVop3Word0(0x103, 35), EncodeVop3Word1(6 + 256, 6 + 256, 0), EncodeVop3Word0(0x10b, 36), EncodeVop3Word1(5 + 256, 5 + 256, 0), EncodeVop3Word0(0x11e, 86), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x11f, 96), EncodeVop3Word1(6 + 256, 6 + 256, 0), EncodeVop3Word0Sdst(0x128, 98, 30), EncodeVop3Word1(5 + 256, 6 + 256, 106), EncodeVop3Word0(0x123, 87), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x124, 88), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x180, 0), EncodeVop3Word1(0, 0, 0), EncodeVop3Word0(0x181, 23), EncodeVop3Word1(5 + 256, 0, 0), EncodeVop3Word0(0x182, 25), EncodeVop3Word1(5 + 256, 0, 0), EncodeVop3Word0(0x185, 24), EncodeVop3Word1(5 + 256, 0, 0), EncodeVop3Word0(0x18a, 101), EncodeVop3Word1(6 + 256, 0, 0), EncodeVop3Word0(0x18d, 74), EncodeVop3Word1(6 + 256, 0, 0), EncodeVop3Word0(0x18e, 82), EncodeVop3Word1(5 + 256, 0, 0), EncodeVop3Word0(0x1a0, 25), EncodeVop3Word1(6 + 256, 0, 0), EncodeVop3Word0(0x1aa, 26), EncodeVop3Word1(6 + 256, 0, 0), EncodeVop3Word0(0x1b7, 29), EncodeVop3Word1(5 + 256, 0, 0), EncodeVop3Word0(0x1b8, 30), EncodeVop3Word1(5 + 256, 0, 0), EncodeVop3Word0(0x1b9, 33), EncodeVop3Word1(5 + 256, 0, 0), EncodeVop3Word0(0x1ba, 34), EncodeVop3Word1(5 + 256, 0, 0), EncodeVopc(0x04, 6 + 256, 6), // v_cmp_gt_f32 v6, v6 EncodeVopc(0xc4, 7 + 256, 5), // v_cmp_gt_u32 v7, v5 EncodeVopc(0x14, 6 + 256, 6), // v_cmpx_gt_f32 v6, v6 EncodeVopc(0xd4, 7 + 256, 5), // v_cmpx_gt_u32 v7, v5 EncodeVopc(0x00, 6 + 256, 6), // v_cmp_f_f32 v6, v6 EncodeVopc(0x0f, 6 + 256, 6), // v_cmp_tru_f32 v6, v6 EncodeVopc(0x07, 6 + 256, 6), // v_cmp_o_f32 v6, v6 EncodeVopc(0x08, 6 + 256, 6), // v_cmp_u_f32 v6, v6 EncodeVopc(0x09, 6 + 256, 6), // v_cmp_nge_f32 v6, v6 EncodeVopc(0x0a, 6 + 256, 6), // v_cmp_nlg_f32 v6, v6 EncodeVopc(0x0b, 6 + 256, 6), // v_cmp_ngt_f32 v6, v6 EncodeVopc(0x0c, 6 + 256, 6), // v_cmp_nle_f32 v6, v6 EncodeVopc(0x0d, 6 + 256, 6), // v_cmp_neq_f32 v6, v6 0x7c1a02f9u, 0x068680f0u, // v_cmp_neq_f32 s0, 0.5, v1 (SDWA) EncodeVopc(0xd1, 249, 6), EncodeVopcSdwa(5, 0, 0, 4), // v_cmpx_lt_u32 exec, v5, v6 (SDWA) EncodeVopc(0x0e, 6 + 256, 6), // v_cmp_nlt_f32 v6, v6 EncodeVopc(0x19, 6 + 256, 6), // v_cmpx_nge_f32 v6, v6 EncodeVopc(0x1a, 6 + 256, 6), // v_cmpx_nlg_f32 v6, v6 EncodeVopc(0x1b, 6 + 256, 6), // v_cmpx_ngt_f32 v6, v6 EncodeVopc(0x1c, 6 + 256, 6), // v_cmpx_nle_f32 v6, v6 EncodeVopc(0x1d, 6 + 256, 6), // v_cmpx_neq_f32 v6, v6 EncodeVopc(0x1e, 6 + 256, 6), // v_cmpx_nlt_f32 v6, v6 EncodeVopc(0x80, 5 + 256, 5), // v_cmp_f_i32 v5, v5 EncodeVopc(0x87, 5 + 256, 5), // v_cmp_t_i32 v5, v5 EncodeVopc(0xc0, 5 + 256, 5), // v_cmp_f_u32 v5, v5 EncodeVopc(0xc7, 5 + 256, 5), // v_cmp_t_u32 v5, v5 0xd4e5006au, 0x0000d47eu, // v_cmp_ne_u64 vcc, exec, vcc EncodeVop3Word0(0x141, 8), EncodeVop3Word1(6 + 256, 6 + 256, 6 + 256), 0xd5410004u, 0x20121301u, // v_mad_f32 v4, -v1, v9, s4 EncodeVop3Word0(0x144, 110), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x145, 111), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x146, 112), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x147, 113), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), 0xd5470005u, 0x841a0102u, // v_cubema_f32 v5, v2, v0, -v6 EncodeVop3Word0(0x14b, 37), EncodeVop3Word1(6 + 256, 6 + 256, 6 + 256), EncodeVop3Word0(0x151, 38), EncodeVop3Word1(6 + 256, 6 + 256, 6 + 256), EncodeVop3Word0(0x154, 39), EncodeVop3Word1(6 + 256, 6 + 256, 6 + 256), EncodeVop3Word0(0x157, 40), EncodeVop3Word1(6 + 256, 6 + 256, 6 + 256), EncodeVop3Word0(0x152, 41), EncodeVop3Word1(5 + 256, 5 + 256, 5 + 256), EncodeVop3Word0(0x153, 42), EncodeVop3Word1(5 + 256, 5 + 256, 5 + 256), EncodeVop3Word0(0x155, 43), EncodeVop3Word1(5 + 256, 5 + 256, 5 + 256), EncodeVop3Word0(0x156, 44), EncodeVop3Word1(5 + 256, 5 + 256, 5 + 256), EncodeVop3Word0(0x158, 45), EncodeVop3Word1(5 + 256, 5 + 256, 5 + 256), EncodeVop3Word0(0x159, 46), EncodeVop3Word1(5 + 256, 5 + 256, 5 + 256), EncodeVop3Word0(0x148, 47), EncodeVop3Word1(5 + 256, 129, 132), EncodeVop3Word0(0x149, 48), EncodeVop3Word1(5 + 256, 129, 132), EncodeVop3Word0(0x14a, 49), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x14e, 50), EncodeVop3Word1(5 + 256, 6 + 256, 129), EncodeVop3Word0(0x36d, 51), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x169, 52), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x16a, 53), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x16c, 114), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x371, 54), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x372, 55), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x178, 56), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x346, 57), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x347, 58), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x345, 59), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x36f, 60), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x15d, 61), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x142, 62), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x143, 63), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x16b, 69), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x30f, 70), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x310, 71), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x319, 72), EncodeVop3Word1(5 + 256, 6 + 256, 0), 0xd70a1007u, 0x00020affu, 0x0000ffffu, // v_max_i16 v7, 0xffff, v5.hi 0xd70c5007u, 0x00020882u, // v_min_i16 v7, 2, v4.hi EncodeVop3Word0(0x363, 73), EncodeVop3Word1(129, 132, 0), EncodeVop3Word0(0x360, 26), EncodeVop3Word1(5 + 256, 130, 0), EncodeVop3Word0(0x361, 107), EncodeVop3Word1(5 + 256, 130, 0), EncodeVop3Word0(0x377, 108), EncodeVop3Word1(5 + 256, 128, 128), EncodeVop3Word0(0x378, 109), EncodeVop3Word1(5 + 256, 128, 128), EncodeVop3Word0(0x12f, 75), EncodeVop3Word1(6 + 256, 6 + 256, 0), 0xd52f0000u, 0x60020300u, // v_cvt_pkrtz_f16_f32 v0, -v0, -v1 EncodeVop3Word0(0x362, 76), EncodeVop3Word1(6 + 256, 129, 0), 0xd7620107u, 0x00018509u, // v_ldexp_f32 v7, abs(v9), -2 0xd762800du, 0x00018906u, // v_ldexp_f32 clamp v13, v6, -4 EncodeVop3Word0(0x364, 77), EncodeVop3Word1(5 + 256, 6 + 256, 0), EncodeVop3Word0(0x368, 78), EncodeVop3Word1(6 + 256, 6 + 256, 0), EncodeVop3Word0(0x369, 79), EncodeVop3Word1(6 + 256, 6 + 256, 0), EncodeVop3Word0(0x36a, 80), EncodeVop3Word1(5 + 256, 7 + 256, 0), 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_movk_i32 s9"), "new decoder did not decode SOPK mov"); Check(Common::ContainsStr(result.decoded_dump, "s_cmp_gt_u32"), "new decoder did not decode SOPK compare"); Check(Common::ContainsStr(result.decoded_dump, "v_nop"), "new decoder did not decode old-backed VOP1 no-op"); Check(Common::ContainsStr(result.decoded_dump, "v_mov_b32 v5"), "new decoder did not decode VOP1 mov"); Check(Common::ContainsStr(result.decoded_dump, "v_movrels_b32 v55, v12"), "new decoder did not decode VOP1 V_MOVRELS_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_mov_b32 v103, v5.sdwa(sel=4"), "new decoder did not decode VOP1 SDWA source selector"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f16_f32 v105.sdwa(sel=5"), "new decoder did not decode VOP1 SDWA destination selector"); Check(Common::ContainsStr(result.decoded_dump, "v_mov_b32 v106, v5.dpp"), "new decoder did not decode VOP1 DPP source metadata"); Check(!Common::ContainsStr(result.decoded_dump, "VOP1 SDWA/DPP modifiers are not implemented"), "new decoder still reports blanket VOP1 SDWA/DPP unsupported reason"); Check(Common::ContainsStr(result.decoded_dump, "v_add_nc_u32 v123, v5.sdwa(sel=4"), "new decoder did not decode VOP2 SDWA source selector"); Check(Common::ContainsStr(result.decoded_dump, "v_and_b32 v124, v5.sdwa(sel=4"), "new decoder did not decode VOP2 SDWA first source selector"); Check(Common::ContainsStr(result.decoded_dump, "v6.sdwa(sel=5"), "new decoder did not decode VOP2 SDWA second source selector"); Check(Common::ContainsStr(result.decoded_dump, "v_cndmask_b32 v47, v53,") && Common::ContainsStr(result.decoded_dump, "v53.neg"), "new decoder did not decode V_CNDMASK_B32 SDWA source modifier"); Check(Common::ContainsStr(result.decoded_dump, "v_add_f32 v125, v5.dpp"), "new decoder did not decode VOP2 DPP source metadata"); Check(!Common::ContainsStr(result.decoded_dump, "VOP2 SDWA/DPP modifiers are not implemented"), "new decoder still reports blanket VOP2 SDWA/DPP unsupported reason"); Check(Common::ContainsStr(result.decoded_dump, "v_readfirstlane_b32 s24, v5"), "new decoder did not decode old-backed V_READFIRSTLANE_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_u32 v6"), "new decoder did not decode VOP1 conversion"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_i32 v9"), "new decoder did not decode VOP1 signed int-to-float conversion"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_i32_f32 v10"), "new decoder did not decode VOP1 float-to-signed-int conversion"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f16_f32 v99"), "new decoder did not decode old-backed V_CVT_F16_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_f16 v100"), "new decoder did not decode old-backed native V_CVT_F32_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_f16 v8, v2.sdwa(sel=5") && Common::ContainsStr(result.decoded_dump, "v2.sdwa(sel=5,sext=0).abs"), "new decoder did not decode V_CVT_F32_F16 SDWA source selector modifier"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_flr_i32_f32 v64"), "new decoder did not decode old-backed V_CVT_FLR_I32_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_off_f32_i4 v81"), "new decoder did not decode old-backed V_CVT_OFF_F32_I4"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_ubyte0 v65"), "new decoder did not decode old-backed V_CVT_F32_UBYTE0"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_ubyte1 v66"), "new decoder did not decode old-backed V_CVT_F32_UBYTE1"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_ubyte2 v67"), "new decoder did not decode old-backed V_CVT_F32_UBYTE2"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_ubyte3 v68"), "new decoder did not decode old-backed V_CVT_F32_UBYTE3"); Check(Common::ContainsStr(result.decoded_dump, "v_rcp_f32 v11"), "new decoder did not decode VOP1 reciprocal"); Check(Common::ContainsStr(result.decoded_dump, "v_fract_f32 v12"), "new decoder did not decode VOP1 fract"); Check(Common::ContainsStr(result.decoded_dump, "v_cos_f32 v22"), "new decoder did not decode VOP1 cosine"); Check(Common::ContainsStr(result.decoded_dump, "v_not_b32 v27"), "new decoder did not decode VOP1 not"); Check(Common::ContainsStr(result.decoded_dump, "v_bfrev_b32 v28"), "new decoder did not decode VOP1 bit reverse"); Check(Common::ContainsStr(result.decoded_dump, "v_ffbh_u32 v31"), "new decoder did not decode VOP1 find-first-bit-high"); Check(Common::ContainsStr(result.decoded_dump, "v_ffbl_b32 v32"), "new decoder did not decode VOP1 find-first-bit-low"); Check(Common::ContainsStr(result.decoded_dump, "v_xnor_b32 v83, v5, v6"), "new decoder did not decode old-backed V_XNOR_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_mbcnt_lo_u32_b32 v84, v5, v6"), "new decoder did not decode old-backed V_MBCNT_LO_U32_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_mbcnt_hi_u32_b32 v85, v5, v6"), "new decoder did not decode old-backed V_MBCNT_HI_U32_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_mac_f32 v89, v6, v6"), "new decoder did not decode old-backed V_MAC_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_madmk_f32 v90, v6, 0x3f800000, v5"), "new decoder did not decode old-backed V_MADMK_F32 literal form"); Check(Common::ContainsStr(result.decoded_dump, "v_madak_f32 v91, v6, v5, 0x40000000"), "new decoder did not decode old-backed V_MADAK_F32 literal form"); Check(Common::ContainsStr(result.decoded_dump, "v_mac_f32 v92, v6, v6"), "new decoder did not decode old-backed alternate V_MAC_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_madmk_f32 v93, v6, 0x3f000000, v5"), "new decoder did not decode old-backed alternate V_MADMK_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_madak_f32 v94, v6, v5, 0x40400000"), "new decoder did not decode old-backed alternate V_MADAK_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_pkrtz_f16_f32 v95, v6, v6"), "new decoder did not decode old-backed native V_CVT_PKRTZ_F16_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_dot2c_f32_f16 v102, v95, v95"), "new decoder did not decode old-backed V_DOT2C_F32_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_addc_u32 v97, vcc_lo, v5, v6, vcc_lo"), "new decoder did not decode old-backed V_ADD_CO_U32 carry form"); Check(Common::ContainsStr(result.decoded_dump, "v_add_f32 v35"), "new decoder did not decode VOP3-encoded VOP2 float add"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_u32_u24 v36"), "new decoder did not decode VOP3-encoded VOP2 24-bit multiply"); Check(Common::ContainsStr(result.decoded_dump, "v_xnor_b32 v86, v5, v6"), "new decoder did not decode old-backed VOP3-encoded V_XNOR_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_mac_f32 v96, v6, v6"), "new decoder did not decode old-backed VOP3-encoded V_MAC_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_addc_u32 v98, s30, v5, v6, vcc_lo"), "new decoder did not decode old-backed VOP3 V_ADD_CO_U32 carry form"); Check(Common::ContainsStr(result.decoded_dump, "v_mbcnt_lo_u32_b32 v87, v5, v6"), "new decoder did not decode old-backed VOP3-encoded V_MBCNT_LO_U32_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_mbcnt_hi_u32_b32 v88, v5, v6"), "new decoder did not decode old-backed VOP3-encoded V_MBCNT_HI_U32_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_mov_b32 v23"), "new decoder did not decode VOP3-encoded VOP1 move"); Check(Common::ContainsStr(result.decoded_dump, "v_readfirstlane_b32 s25, v5"), "new decoder did not decode old-backed VOP3-encoded V_READFIRSTLANE_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f32_i32 v24"), "new decoder did not decode VOP3-encoded VOP1 signed conversion"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f16_f32 v101"), "new decoder did not decode old-backed VOP3-encoded V_CVT_F16_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_flr_i32_f32 v74"), "new decoder did not decode old-backed VOP3-encoded V_CVT_FLR_I32_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_off_f32_i4 v82"), "new decoder did not decode old-backed VOP3-encoded V_CVT_OFF_F32_I4"); Check(Common::ContainsStr(result.decoded_dump, "v_fract_f32 v25"), "new decoder did not decode VOP3-encoded VOP1 fract"); Check(Common::ContainsStr(result.decoded_dump, "v_rcp_f32 v26"), "new decoder did not decode VOP3-encoded VOP1 reciprocal"); Check(Common::ContainsStr(result.decoded_dump, "v_not_b32 v29"), "new decoder did not decode VOP3-encoded VOP1 not"); Check(Common::ContainsStr(result.decoded_dump, "v_bfrev_b32 v30"), "new decoder did not decode VOP3-encoded VOP1 bit reverse"); Check(Common::ContainsStr(result.decoded_dump, "v_ffbh_u32 v33"), "new decoder did not decode VOP3-encoded VOP1 find-first-bit-high"); Check(Common::ContainsStr(result.decoded_dump, "v_ffbl_b32 v34"), "new decoder did not decode VOP3-encoded VOP1 find-first-bit-low"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_gt_f32"), "new decoder did not decode VOPC float compare"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_gt_f32"), "new decoder did not decode VOPC float compare-and-mask"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_gt_u32"), "new decoder did not decode VOPC uint compare-and-mask"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_f_f32"), "new decoder did not decode old-backed V_CMP_F_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_tru_f32"), "new decoder did not decode old-backed V_CMP_TRU_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_o_f32"), "new decoder did not decode old-backed V_CMP_O_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_u_f32"), "new decoder did not decode old-backed V_CMP_U_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_nge_f32"), "new decoder did not decode old-backed V_CMP_NGE_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_nlg_f32"), "new decoder did not decode old-backed V_CMP_NLG_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_ngt_f32"), "new decoder did not decode old-backed V_CMP_NGT_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_nle_f32"), "new decoder did not decode old-backed V_CMP_NLE_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_neq_f32"), "new decoder did not decode old-backed V_CMP_NEQ_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_neq_f32 s0, 0.500000, v1"), "new decoder did not decode old-backed V_CMP_NEQ_F32 SDWA scalar destination"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_lt_u32 exec_lo, v5.sdwa(sel=4") && Common::ContainsStr(result.ir_dump, "CompareMaskLtU32 exec_lo"), "new decoder did not route V_CMPX SDWA destination to exec"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_nlt_f32"), "new decoder did not decode old-backed V_CMP_NLT_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_nge_f32"), "new decoder did not decode old-backed V_CMPX_NGE_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_nlg_f32"), "new decoder did not decode old-backed V_CMPX_NLG_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_ngt_f32"), "new decoder did not decode old-backed V_CMPX_NGT_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_nle_f32"), "new decoder did not decode old-backed V_CMPX_NLE_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_neq_f32"), "new decoder did not decode old-backed V_CMPX_NEQ_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_nlt_f32"), "new decoder did not decode old-backed V_CMPX_NLT_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_f_i32"), "new decoder did not decode old-backed V_CMP_F_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_t_i32"), "new decoder did not decode old-backed V_CMP_T_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_f_u32"), "new decoder did not decode old-backed V_CMP_F_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_t_u32"), "new decoder did not decode old-backed V_CMP_T_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_ne_u64 vcc_lo, exec_lo, vcc_lo"), "new decoder did not decode VOP3-encoded V_CMP_NE_U64"); Check(Common::ContainsStr(result.ir_dump, "CompareNeU64 vcc_lo, exec_lo, vcc_lo"), "V_CMP_NE_U64 did not lower to 64-bit compare IR"); Check(Common::ContainsStr(result.decoded_dump, "v_mad_f32 v8"), "new decoder did not decode VOP3 mad"); Check(Common::ContainsStr(result.decoded_dump, "v_mad_f32 v4, v1.neg, v9, s4"), "new decoder did not decode VOP3 mad source modifiers"); Check(Common::ContainsStr(result.decoded_dump, "v_cubeid_f32 v110, v5, v6, v7"), "new decoder did not decode old-backed V_CUBEID_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cubesc_f32 v111, v5, v6, v7"), "new decoder did not decode old-backed V_CUBESC_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cubetc_f32 v112, v5, v6, v7"), "new decoder did not decode old-backed V_CUBETC_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cubema_f32 v113, v5, v6, v7"), "new decoder did not decode old-backed V_CUBEMA_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cubema_f32 v5, v2, v0, v6.neg"), "new decoder did not decode V_CUBEMA_F32 source modifier"); Check(Common::ContainsStr(result.decoded_dump, "v_fma_f32 v37"), "new decoder did not decode VOP3 fma"); Check(Common::ContainsStr(result.decoded_dump, "v_min3_f32 v38"), "new decoder did not decode VOP3 min3"); Check(Common::ContainsStr(result.decoded_dump, "v_max3_f32 v39"), "new decoder did not decode VOP3 max3"); Check(Common::ContainsStr(result.decoded_dump, "v_med3_f32 v40"), "new decoder did not decode VOP3 med3"); Check(Common::ContainsStr(result.decoded_dump, "v_min3_i32 v41"), "new decoder did not decode VOP3 signed min3"); Check(Common::ContainsStr(result.decoded_dump, "v_min3_u32 v42"), "new decoder did not decode VOP3 unsigned min3"); Check(Common::ContainsStr(result.decoded_dump, "v_max3_i32 v43"), "new decoder did not decode VOP3 signed max3"); Check(Common::ContainsStr(result.decoded_dump, "v_max3_u32 v44"), "new decoder did not decode VOP3 unsigned max3"); Check(Common::ContainsStr(result.decoded_dump, "v_med3_i32 v45"), "new decoder did not decode VOP3 signed med3"); Check(Common::ContainsStr(result.decoded_dump, "v_med3_u32 v46"), "new decoder did not decode VOP3 unsigned med3"); Check(Common::ContainsStr(result.decoded_dump, "v_bfe_u32 v47"), "new decoder did not decode VOP3 unsigned bitfield extract"); Check(Common::ContainsStr(result.decoded_dump, "v_bfe_i32 v48"), "new decoder did not decode VOP3 signed bitfield extract"); Check(Common::ContainsStr(result.decoded_dump, "v_bfi_b32 v49"), "new decoder did not decode VOP3 bitfield insert-select"); Check(Common::ContainsStr(result.decoded_dump, "v_alignbit_b32 v50"), "new decoder did not decode VOP3 alignbit"); Check(Common::ContainsStr(result.decoded_dump, "v_add3_u32 v51"), "new decoder did not decode VOP3 add3"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_lo_u32 v52, v5, v6"), "new decoder did not decode old-backed V_MUL_LO_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_hi_u32 v53, v5, v6"), "new decoder did not decode old-backed V_MUL_HI_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_hi_i32 v114, v5, v6"), "new decoder did not decode RDNA2 V_MUL_HI_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_and_or_b32 v54, v5, v6, v7"), "new decoder did not decode old-backed V_AND_OR_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_or3_b32 v55, v5, v6, v7"), "new decoder did not decode old-backed V_OR3_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_xor3_b32 v56, v5, v6, v7"), "new decoder did not decode old-backed V_XOR3_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_lshl_add_u32 v57, v5, v6, v7"), "new decoder did not decode old-backed V_LSHL_ADD_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_add_lshl_u32 v58, v5, v6, v7"), "new decoder did not decode old-backed V_ADD_LSHL_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_xad_u32 v59, v5, v6, v7"), "new decoder did not decode old-backed V_XAD_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_lshl_or_b32 v60, v5, v6, v7"), "new decoder did not decode old-backed V_LSHL_OR_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_sad_u32 v61, v5, v6, v7"), "new decoder did not decode old-backed V_SAD_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_mad_i32_i24 v62, v5, v6, v7"), "new decoder did not decode old-backed V_MAD_I32_I24"); Check(Common::ContainsStr(result.decoded_dump, "v_mad_u32_u24 v63, v5, v6, v7"), "new decoder did not decode old-backed V_MAD_U32_U24"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_lo_i32 v69, v5, v6"), "new decoder did not decode old-backed V_MUL_LO_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_add_i32 v70, s0, v5, v6"), "new decoder did not decode old-backed V_ADD_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_sub_i32 v71, s0, v5, v6"), "new decoder did not decode RDNA2 V_SUB_CO_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_subrev_i32 v72, s0, v5, v6"), "new decoder did not decode old-backed V_SUBREV_I32"); Check(Common::ContainsStr( result.decoded_dump, "v_max_i16 v7.sdwa(sel=4,sext=0), 0x0000ffff, v5.opsel(lo=1,hi=0,neghi=0)"), "new decoder did not decode RDNA2 V_MAX_I16 literal/op_sel form"); Check(Common::ContainsStr(result.decoded_dump, "v_min_i16 v7.sdwa(sel=5,sext=0), 2, v4.opsel(lo=1,hi=0,neghi=0)"), "new decoder did not decode RDNA2 V_MIN_I16 op_sel form"); Check(Common::ContainsStr(result.decoded_dump, "v_bfm_b32 v73, 1, 4"), "new decoder did not decode old-backed V_BFM_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_readlane_b32 s26, v5, 2"), "new decoder did not decode old-backed V_READLANE_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_writelane_b32 v107, v5, 2"), "new decoder did not decode old-backed V_WRITELANE_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_permlane16_b32 v108, v5, 0, 0"), "new decoder did not decode old-backed V_PERMLANE16_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_permlanex16_b32 v109, v5, 0, 0"), "new decoder did not decode old-backed V_PERMLANEX16_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_pkrtz_f16_f32 v75, v6, v6"), "new decoder did not decode old-backed V_CVT_PKRTZ_F16_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_pkrtz_f16_f32 v0, v0.neg, v1.neg"), "new decoder did not decode V_CVT_PKRTZ_F16_F32 source modifiers"); Check(Common::ContainsStr(result.decoded_dump, "v_ldexp_f32 v76, v6, 1"), "new decoder did not decode old-backed V_LDEXP_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_ldexp_f32 v7, v9.abs, -2"), "new decoder did not decode V_LDEXP_F32 source modifier"); Check(Common::ContainsStr(result.decoded_dump, "v_ldexp_f32 v13.clamp, v6, -4"), "new decoder did not decode V_LDEXP_F32 clamp modifier"); Check(Common::ContainsStr(result.decoded_dump, "v_bcnt_u32_b32 v77, v5, v6"), "new decoder did not decode old-backed VOP3 V_BCNT_U32_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_pknorm_i16_f32 v78, v6, v6"), "new decoder did not decode old-backed V_CVT_PKNORM_I16_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_pknorm_u16_f32 v79, v6, v6"), "new decoder did not decode old-backed V_CVT_PKNORM_U16_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_pk_u16_u32 v80, v5, v7"), "new decoder did not decode old-backed V_CVT_PK_U16_U32"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_f32 v2, s0, s72"), "new decoder did not decode old-backed V_MUL_F32 SDWA full-width form"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_f16 v9.sdwa(sel=4") && Common::ContainsStr(result.decoded_dump, "v0.sdwa(sel=4") && Common::ContainsStr(result.decoded_dump, "v1.sdwa(sel=4"), "new decoder did not decode V_MUL_F16 SDWA low-half form"); Check(Common::ContainsStr(result.decoded_dump, "v_sub_f16 v0.sdwa(sel=4") && Common::ContainsStr(result.decoded_dump, "v0.sdwa(sel=4"), "new decoder did not decode V_SUB_F16 SDWA low-half form"); Check(Common::ContainsStr(result.ir_dump, "ConvertU32ToF32 v6"), "VOP1 uint-to-float conversion did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 v103, v5.sdwa(sel=4"), "VOP1 SDWA source selector did not lower to IR metadata"); Check(Common::ContainsStr(result.ir_dump, "ConvertF32ToF16 v105.sdwa(sel=5"), "VOP1 SDWA destination selector did not lower to IR metadata"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 v106.dpp") && Common::ContainsStr(result.ir_dump, "v5.dpp"), "VOP1 DPP source/destination did not lower to IR metadata"); Check(Common::ContainsStr(result.ir_dump, "MoveRelSourceU32 v55, v12, m0"), "V_MOVRELS_B32 did not lower to indexed VGPR-source IR"); Check(Common::ContainsStr(result.ir_dump, "IAddU32 v123, v5.sdwa(sel=4"), "VOP2 SDWA did not lower first source metadata to IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseAndU32 v124, v5.sdwa(sel=4"), "VOP2 SDWA bitwise op did not lower first source metadata to IR"); Check(Common::ContainsStr(result.ir_dump, "v6.sdwa(sel=5"), "VOP2 SDWA bitwise op did not lower second source metadata to IR"); Check(Common::ContainsStr(result.ir_dump, "SelectMaskF32Bits v47, vcc_lo, v53.neg, v53") && Common::ContainsStr(result.ir_dump, "v53.neg"), "V_CNDMASK_B32 SDWA source modifier did not lower to float-bit select IR"); Check(Common::ContainsStr(result.ir_dump, "FAddF32 v125.dpp") && Common::ContainsStr(result.ir_dump, "v5.dpp"), "VOP2 DPP source/destination did not lower to IR metadata"); Check(Common::ContainsStr(result.ir_dump, "FMulF32 v2, s0, s72"), "V_MUL_F32 SDWA full-width form did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "MulF16 v9.sdwa(sel=4") && Common::ContainsStr(result.ir_dump, "v0.sdwa(sel=4") && Common::ContainsStr(result.ir_dump, "v1.sdwa(sel=4"), "V_MUL_F16 SDWA low-half form did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "SubF16 v0.sdwa(sel=4") && Common::ContainsStr(result.ir_dump, "v0.sdwa(sel=4"), "V_SUB_F16 SDWA low-half form did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertF32ToU32 v7"), "VOP1 float-to-uint conversion did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertI32ToF32 v9"), "VOP1 signed int-to-float conversion did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertF32ToI32 v10"), "VOP1 float-to-signed-int conversion did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertF32ToF16 v99, v6"), "V_CVT_F16_F32 did not lower to shared half-pack IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertF16ToF32 v100, v99"), "V_CVT_F32_F16 did not lower to shared half-unpack IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertF16ToF32 v8, v2.sdwa(sel=5") && Common::ContainsStr(result.ir_dump, "v2.sdwa(sel=5,sext=0).abs"), "V_CVT_F32_F16 SDWA source selector modifier did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ReadFirstLaneU32 s24, v5"), "V_READFIRSTLANE_B32 did not lower to subgroup IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertFloorF32ToI32 v64, v6"), "V_CVT_FLR_I32_F32 did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertI4ToOffsetF32 v81, v5"), "V_CVT_OFF_F32_I4 did not lower to shared offset-convert IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertByteU32ToF32 v65, v5, 0x00000000"), "V_CVT_F32_UBYTE0 did not lower to shared byte-convert IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertByteU32ToF32 v66, v5, 0x00000001"), "V_CVT_F32_UBYTE1 did not lower to shared byte-convert IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertByteU32ToF32 v67, v5, 0x00000002"), "V_CVT_F32_UBYTE2 did not lower to shared byte-convert IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertByteU32ToF32 v68, v5, 0x00000003"), "V_CVT_F32_UBYTE3 did not lower to shared byte-convert IR"); Check(Common::ContainsStr(result.ir_dump, "RcpF32 v11"), "VOP1 reciprocal did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FractF32 v12"), "VOP1 fract did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "TruncF32 v13"), "VOP1 trunc did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CeilF32 v14"), "VOP1 ceil did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "RoundEvenF32 v15"), "VOP1 round-even did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FloorF32 v16"), "VOP1 floor did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "Exp2F32 v17"), "VOP1 exp2 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "Log2F32 v18"), "VOP1 log2 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "InverseSqrtF32 v19"), "VOP1 inverse-sqrt did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "SqrtF32 v20"), "VOP1 sqrt did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "SinF32 v21"), "VOP1 sin did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CosF32 v22"), "VOP1 cos did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNotU32 v27"), "VOP1 not did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitReverseU32 v28"), "VOP1 bit reverse did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FindMsbFromHighU32 v31"), "VOP1 find-first-bit-high did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FindLsbU32 v32"), "VOP1 find-first-bit-low did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseXnorU32 v83, v5, v6"), "V_XNOR_B32 did not lower to shared xnor IR"); Check(Common::ContainsStr(result.ir_dump, "MaskedBitCountLowU32 v84, v5, v6"), "V_MBCNT_LO_U32_B32 did not lower to shared masked bit-count IR"); Check(Common::ContainsStr(result.ir_dump, "MaskedBitCountHighU32 v85, v5, v6"), "V_MBCNT_HI_U32_B32 did not lower to shared masked bit-count IR"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v89, v6, v6, v89"), "V_MAC_F32 did not lower with the destination register as addend"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v90, v6, 0x3f800000, v5"), "V_MADMK_F32 did not lower with the literal in source 1"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v91, v6, v5, 0x40000000"), "V_MADAK_F32 did not lower with the literal in source 2"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v92, v6, v6, v92"), "alternate V_MAC_F32 did not lower with the destination register as addend"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v93, v6, 0x3f000000, v5"), "alternate V_MADMK_F32 did not lower with the literal in source 1"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v94, v6, v5, 0x40400000"), "alternate V_MADAK_F32 did not lower with the literal in source 2"); Check(Common::ContainsStr(result.ir_dump, "PackF32ToF16Rtz v95, v6, v6"), "native V_CVT_PKRTZ_F16_F32 did not lower to shared pack IR"); Check(Common::ContainsStr(result.ir_dump, "Dot2AccF32F16 v102, v95, v95, v102"), "V_DOT2C_F32_F16 did not lower to explicit dot-accumulate IR"); Check(Common::ContainsStr(result.ir_dump, "IAddCarryU32 v97, vcc_lo, v5, v6, vcc_lo"), "V_ADD_CO_U32 did not lower to carry-add IR"); Check(Common::ContainsStr(result.ir_dump, "FAddF32 v35"), "VOP3-encoded VOP2 float add did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "UMulU24U32 v36"), "VOP3-encoded VOP2 24-bit multiply did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseXnorU32 v86, v5, v6"), "VOP3-encoded V_XNOR_B32 did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v96, v6, v6, v96"), "VOP3-encoded V_MAC_F32 did not lower with the destination register as addend"); Check(Common::ContainsStr(result.ir_dump, "IAddCarryU32 v98, s30, v5, v6, vcc_lo"), "VOP3-encoded V_ADD_CO_U32 did not lower to carry-add IR"); Check(Common::ContainsStr(result.ir_dump, "MaskedBitCountLowU32 v87, v5, v6"), "VOP3-encoded V_MBCNT_LO_U32_B32 did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "MaskedBitCountHighU32 v88, v5, v6"), "VOP3-encoded V_MBCNT_HI_U32_B32 did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 v23"), "VOP3-encoded VOP1 move did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "ReadFirstLaneU32 s25, v5"), "VOP3-encoded V_READFIRSTLANE_B32 did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "ReadLaneU32 s26, v5, 0x00000002"), "V_READLANE_B32 did not lower to subgroup lane IR"); Check(Common::ContainsStr(result.ir_dump, "WriteLaneU32 v107, v5, 0x00000002"), "V_WRITELANE_B32 did not lower to subgroup lane IR"); Check(Common::ContainsStr(result.ir_dump, "Permlane16B32 v108, v5, 0x00000000, 0x00000000"), "V_PERMLANE16_B32 did not lower to subgroup perm-lane IR"); Check(Common::ContainsStr(result.ir_dump, "Permlanex16B32 v109, v5, 0x00000000, 0x00000000"), "V_PERMLANEX16_B32 did not lower to subgroup perm-lane IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertI32ToF32 v24"), "VOP3-encoded VOP1 signed conversion did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertFloorF32ToI32 v74, v6"), "VOP3-encoded V_CVT_FLR_I32_F32 did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertI4ToOffsetF32 v82, v5"), "VOP3-encoded V_CVT_OFF_F32_I4 did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "FractF32 v25"), "VOP3-encoded VOP1 fract did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "RcpF32 v26"), "VOP3-encoded VOP1 reciprocal did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNotU32 v29"), "VOP3-encoded VOP1 not did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "BitReverseU32 v30"), "VOP3-encoded VOP1 bit reverse did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "FindMsbFromHighU32 v33"), "VOP3-encoded VOP1 find-first-bit-high did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "FindLsbU32 v34"), "VOP3-encoded VOP1 find-first-bit-low did not lower through shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareGtF32"), "VOPC float compare did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtF32 exec_lo"), "VOPC float compare-and-mask did not lower to exec mask IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtU32 exec_lo"), "VOPC uint compare-and-mask did not lower to exec mask IR"); Check(Common::ContainsStr(result.ir_dump, "CompareFalse vcc_lo, v6, v6"), "VOPC false compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareTrue vcc_lo, v6, v6"), "VOPC true compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareOrderedF32 vcc_lo, v6, v6"), "VOPC ordered compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnorderedF32 vcc_lo, v6, v6"), "VOPC unordered compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordLtF32 vcc_lo, v6, v6"), "VOPC unordered-less compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordEqF32 vcc_lo, v6, v6"), "VOPC unordered-equal compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordLeF32 vcc_lo, v6, v6"), "VOPC unordered-less-equal compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordGtF32 vcc_lo, v6, v6"), "VOPC unordered-greater compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordNeF32 vcc_lo, v6, v6"), "VOPC unordered-not-equal compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordNeF32 s0, 0x3f000000, v1"), "VOPC SDWA unordered-not-equal compare did not lower to scalar-destination IR"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordGeF32 vcc_lo, v6, v6"), "VOPC unordered-greater-equal compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordLtF32 exec_lo, v6, v6"), "VOPC unordered-less compare-and-mask did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordEqF32 exec_lo, v6, v6"), "VOPC unordered-equal compare-and-mask did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordLeF32 exec_lo, v6, v6"), "VOPC unordered-less-equal compare-and-mask did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordGtF32 exec_lo, v6, v6"), "VOPC unordered-greater compare-and-mask did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordNeF32 exec_lo, v6, v6"), "VOPC unordered-not-equal compare-and-mask did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordGeF32 exec_lo, v6, v6"), "VOPC unordered-greater-equal compare-and-mask did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareFalse vcc_lo, v5, v5"), "VOPC integer false compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "CompareTrue vcc_lo, v5, v5"), "VOPC integer true compare did not lower to shared IR"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v8"), "VOP3 mad did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v4, v1.neg, v9, s4"), "VOP3 mad source modifiers did not lower to IR metadata"); Check(Common::ContainsStr(result.ir_dump, "CubeIdF32 v110, v5, v6, v7"), "V_CUBEID_F32 did not lower to cube IR"); Check(Common::ContainsStr(result.ir_dump, "CubeScF32 v111, v5, v6, v7"), "V_CUBESC_F32 did not lower to cube IR"); Check(Common::ContainsStr(result.ir_dump, "CubeTcF32 v112, v5, v6, v7"), "V_CUBETC_F32 did not lower to cube IR"); Check(Common::ContainsStr(result.ir_dump, "CubeMaF32 v113, v5, v6, v7"), "V_CUBEMA_F32 did not lower to cube IR"); Check(Common::ContainsStr(result.ir_dump, "CubeMaF32 v5, v2, v0, v6.neg"), "V_CUBEMA_F32 source modifier did not lower to cube IR"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v37"), "VOP3 fma did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FMin3F32 v38"), "VOP3 min3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FMax3F32 v39"), "VOP3 max3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FMed3F32 v40"), "VOP3 med3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMin3I32 v41"), "VOP3 signed min3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "UMin3U32 v42"), "VOP3 unsigned min3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMax3I32 v43"), "VOP3 signed max3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "UMax3U32 v44"), "VOP3 unsigned max3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMed3I32 v45"), "VOP3 signed med3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "UMed3U32 v46"), "VOP3 unsigned med3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldExtract3U32 v47"), "VOP3 unsigned bitfield extract did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldExtract3I32 v48"), "VOP3 signed bitfield extract did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldInsertSelectU32 v49"), "VOP3 bitfield insert-select did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AlignBitU32 v50"), "VOP3 alignbit did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IAdd3U32 v51"), "VOP3 add3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMulU32 v52, v5, v6"), "V_MUL_LO_U32 did not lower to multiply IR"); Check(Common::ContainsStr(result.ir_dump, "UMulHighU32 v53, v5, v6"), "V_MUL_HI_U32 did not lower to high-multiply IR"); Check(Common::ContainsStr(result.ir_dump, "SMulHighI32 v114, v5, v6"), "V_MUL_HI_I32 did not lower to signed high-multiply IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseAndOrU32 v54, v5, v6, v7"), "V_AND_OR_B32 did not lower to ternary bitwise IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseOr3U32 v55, v5, v6, v7"), "V_OR3_B32 did not lower to ternary bitwise IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseXor3U32 v56, v5, v6, v7"), "V_XOR3_B32 did not lower to ternary bitwise IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftLeftAddU32 v57, v5, v6, v7"), "V_LSHL_ADD_U32 did not lower to shared shift-left-add IR"); Check(Common::ContainsStr(result.ir_dump, "AddShiftLeftU32 v58, v5, v6, v7"), "V_ADD_LSHL_U32 did not lower to add-shift-left IR"); Check(Common::ContainsStr(result.ir_dump, "XorAddU32 v59, v5, v6, v7"), "V_XAD_U32 did not lower to xor-add IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftLeftOrU32 v60, v5, v6, v7"), "V_LSHL_OR_B32 did not lower to shift-left-or IR"); Check(Common::ContainsStr(result.ir_dump, "SadU32 v61, v5, v6, v7"), "V_SAD_U32 did not lower to sad IR"); Check(Common::ContainsStr(result.ir_dump, "IMadI24U32 v62, v5, v6, v7"), "V_MAD_I32_I24 did not lower to signed 24-bit mad IR"); Check(Common::ContainsStr(result.ir_dump, "UMadU24U32 v63, v5, v6, v7"), "V_MAD_U32_U24 did not lower to unsigned 24-bit mad IR"); Check(Common::ContainsStr(result.ir_dump, "IMulU32 v69, v5, v6"), "V_MUL_LO_I32 did not lower to multiply IR"); Check(Common::ContainsStr(result.ir_dump, "IAddCarryU32 v70, s0, v5, v6, 0x00000000"), "V_ADD_I32 did not lower to carry-out add IR"); Check(Common::ContainsStr(result.ir_dump, "ISubBorrowU32 v71, s0, v5, v6"), "V_SUB_I32 did not lower to borrow-out subtract IR"); Check(Common::ContainsStr(result.ir_dump, "ISubBorrowU32 v72, s0, v6, v5"), "V_SUBREV_I32 did not reverse source order in borrow-out IR"); Check(Common::ContainsStr( result.ir_dump, "IMaxI16 v7.sdwa(sel=4,sext=0), 0x0000ffff, v5.opsel(lo=1,hi=0,neghi=0)"), "V_MAX_I16 did not lower to signed halfword max IR"); Check(Common::ContainsStr( result.ir_dump, "IMinI16 v7.sdwa(sel=5,sext=0), 0x00000002, v4.opsel(lo=1,hi=0,neghi=0)"), "V_MIN_I16 did not lower to signed halfword min IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldMaskU32 v73, 0x00000001, 0x00000004"), "V_BFM_B32 did not lower to shared bitfield-mask IR"); Check(Common::ContainsStr(result.ir_dump, "PackF32ToF16Rtz v75, v6, v6"), "V_CVT_PKRTZ_F16_F32 did not lower to shared pack IR"); Check(Common::ContainsStr(result.ir_dump, "PackF32ToF16Rtz v0, v0.neg, v1.neg"), "V_CVT_PKRTZ_F16_F32 source modifiers did not lower to shared pack IR"); Check(Common::ContainsStr(result.ir_dump, "LdexpF32 v76, v6, 0x00000001"), "V_LDEXP_F32 did not lower to ldexp IR"); Check(Common::ContainsStr(result.ir_dump, "LdexpF32 v7, v9.abs, 0xfffffffe"), "V_LDEXP_F32 source modifier did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "LdexpF32 v13.clamp, v6, 0xfffffffc"), "V_LDEXP_F32 clamp modifier did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitCountAddU32 v77, v5, v6"), "VOP3 V_BCNT_U32_B32 did not lower to bit-count-add IR"); Check(Common::ContainsStr(result.ir_dump, "PackSnorm2x16F32 v78, v6, v6"), "V_CVT_PKNORM_I16_F32 did not lower to shared pack IR"); Check(Common::ContainsStr(result.ir_dump, "PackUnorm2x16F32 v79, v6, v6"), "V_CVT_PKNORM_U16_F32 did not lower to shared pack IR"); Check(Common::ContainsStr(result.ir_dump, "PackU16U32 v80, v5, v7"), "V_CVT_PK_U16_U32 did not lower to shared pack IR"); Check(SpirvContainsOpcode(result.spirv, 112), "SPIR-V binary does not contain OpConvertUToF"); Check(SpirvContainsOpcode(result.spirv, 109), "SPIR-V binary does not contain OpConvertFToU"); Check(SpirvContainsOpcode(result.spirv, 111), "SPIR-V binary does not contain OpConvertSToF"); Check(SpirvContainsOpcode(result.spirv, 110), "SPIR-V binary does not contain OpConvertFToS"); Check(SpirvContainsOpcode(result.spirv, 136), "SPIR-V binary does not contain OpFDiv"); Check(SpirvContainsOpcode(result.spirv, 127), "SPIR-V binary does not contain OpFNegate"); Check(SpirvContainsOpcode(result.spirv, 12), "SPIR-V binary does not contain OpExtInst"); Check(SpirvContainsExtInst(result.spirv, 58), "SPIR-V binary does not contain GLSL.std.450 PackHalf2x16"); Check(SpirvContainsExtInst(result.spirv, 62), "SPIR-V binary does not contain GLSL.std.450 UnpackHalf2x16"); Check(SpirvContainsExtInst(result.spirv, 50), "SPIR-V binary does not contain GLSL.std.450 Fma"); Check(SpirvContainsExtInst(result.spirv, 43), "SPIR-V binary does not contain GLSL.std.450 FClamp"); Check(SpirvContainsExtInst(result.spirv, 53), "SPIR-V binary does not contain GLSL.std.450 Ldexp"); Check(SpirvContainsOpcode(result.spirv, 128), "SPIR-V binary does not contain OpIAdd"); Check(SpirvContainsOpcode(result.spirv, 149), "SPIR-V binary does not contain OpIAddCarry"); Check(SpirvContainsOpcode(result.spirv, 130), "SPIR-V binary does not contain OpISub"); Check(SpirvContainsOpcode(result.spirv, 132), "SPIR-V binary does not contain OpIMul"); Check(SpirvContainsOpcode(result.spirv, 171), "SPIR-V binary does not contain OpINotEqual"); Check(SpirvContainsOpcode(result.spirv, 200), "SPIR-V binary does not contain OpNot"); Check(SpirvContainsOpcode(result.spirv, 204), "SPIR-V binary does not contain OpBitReverse"); Check(SpirvContainsOpcode(result.spirv, 205), "SPIR-V binary does not contain OpBitCount"); Check(SpirvContainsOpcode(result.spirv, 166), "SPIR-V binary does not contain OpLogicalOr"); Check(SpirvContainsOpcode(result.spirv, 167), "SPIR-V binary does not contain OpLogicalAnd"); Check(SpirvContainsOpcode(result.spirv, 186), "SPIR-V binary does not contain OpFOrdGreaterThan"); Check(SpirvContainsOpcode(result.spirv, 181), "SPIR-V binary does not contain OpFUnordEqual"); Check(SpirvContainsOpcode(result.spirv, 183), "SPIR-V binary does not contain OpFUnordNotEqual"); Check(SpirvContainsOpcode(result.spirv, 185), "SPIR-V binary does not contain OpFUnordLessThan"); Check(SpirvContainsOpcode(result.spirv, 187), "SPIR-V binary does not contain OpFUnordGreaterThan"); Check(SpirvContainsOpcode(result.spirv, 189), "SPIR-V binary does not contain OpFUnordLessThanEqual"); Check(SpirvContainsOpcode(result.spirv, 191), "SPIR-V binary does not contain OpFUnordGreaterThanEqual"); Check(SpirvContainsOpcode(result.spirv, 190), "SPIR-V binary does not contain OpFOrdGreaterThanEqual"); Check(SpirvContainsOpcode(result.spirv, 184), "SPIR-V binary does not contain OpFOrdLessThan"); Check(SpirvContainsOpcode(result.spirv, 173), "SPIR-V binary does not contain OpSGreaterThan"); Check(SpirvContainsOpcode(result.spirv, 174), "SPIR-V binary does not contain OpUGreaterThanEqual"); Check(SpirvContainsOpcode(result.spirv, 177), "SPIR-V binary does not contain OpSLessThan"); Check(SpirvContainsOpcode(result.spirv, 202), "SPIR-V binary does not contain OpBitFieldSExtract"); Check(SpirvContainsOpcode(result.spirv, 203), "SPIR-V binary does not contain OpBitFieldUExtract"); Check(SpirvContainsOpcode(result.spirv, 194), "SPIR-V binary does not contain OpShiftRightLogical"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad for cmpx old exec mask"); Check(SpirvContainsOpcode(result.spirv, 133), "SPIR-V binary does not contain OpFMul"); Check(SpirvContainsOpcode(result.spirv, 129), "SPIR-V binary does not contain OpFAdd"); Check(SpirvContainsOpcode(result.spirv, 151), "SPIR-V binary does not contain OpUMulExtended"); Check(SpirvContainsOpcode(result.spirv, 152), "SPIR-V binary does not contain OpSMulExtended"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical"); Check(SpirvContainsOpcode(result.spirv, 197), "SPIR-V binary does not contain OpBitwiseOr"); Check(SpirvContainsOpcode(result.spirv, 198), "SPIR-V binary does not contain OpBitwiseXor"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); Check(SpirvContainsOpcode(result.spirv, 80), "SPIR-V binary does not contain OpCompositeConstruct"); Check(SpirvContainsOpcode(result.spirv, 81), "SPIR-V binary does not contain OpCompositeExtract"); Check(SpirvContainsOpcode(result.spirv, 339), "SPIR-V binary does not contain OpGroupNonUniformBallot"); Check(SpirvContainsOpcode(result.spirv, 343), "SPIR-V binary does not contain OpGroupNonUniformBallotFindLSB"); Check(SpirvContainsOpcode(result.spirv, 345), "SPIR-V binary does not contain OpGroupNonUniformShuffle"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerExpandedAluBatch() { const uint32_t shader[] = { EncodeSopk(0x00, 9, 7), // s_movk_i32 s9, 7 EncodeSopk(0x0f, 9, 2), // s_add_i32 s9, s9, 2 EncodeSopk(0x10, 9, 3), // s_mulk_i32 s9, s9, 3 EncodeSop2(0x07, 10, 9, 128), // s_min_u32 s10, s9, 0 EncodeSop2(0x09, 11, 10, 130), // s_max_u32 s11, s10, 2 EncodeSop2(0x26, 12, 11, 130), // s_mul_i32 s12, s11, 2 EncodeVop2(0x05, 1, 242, 0), // v_subrev_f32 v1, 1.0, v0 EncodeVop2(0x0f, 2, 1 + 256, 0), // v_min_f32 v2, v1, v0 EncodeVop2(0x10, 3, 1 + 256, 2), // v_max_f32 v3, v1, v2 EncodeVop2(0x27, 4, 130, 3), // v_subrev_nc_u32 v4, 2, v3 EncodeVop2(0x16, 5, 129, 4), // v_lshrrev_b32 v5, 1, v4 EncodeVop2(0x1a, 6, 129, 5), // v_lshlrev_b32 v6, 1, v5 EncodeVop2(0x13, 7, 6 + 256, 5), // v_min_u32 v7, v6, v5 EncodeVop2(0x14, 8, 7 + 256, 6), // v_max_u32 v8, v7, v6 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_min_u32"), "new decoder did not decode S_MIN_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_mulk_i32"), "new decoder did not decode S_MULK_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_subrev_f32"), "new decoder did not decode V_SUBREV_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_lshlrev_b32"), "new decoder did not decode V_LSHLREV_B32"); Check(Common::ContainsStr(result.ir_dump, "IMulU32 s9, s9, 0x00000003"), "SOPK multiply did not lower to self-multiply IR"); Check(Common::ContainsStr(result.ir_dump, "UMinU32 s10"), "S_MIN_U32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "UMaxU32 s11"), "S_MAX_U32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FSubF32 v1, v0, 0x3f800000"), "V_SUBREV_F32 did not reverse source order in IR"); Check(Common::ContainsStr(result.ir_dump, "FMinF32 v2"), "V_MIN_F32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "FMaxF32 v3"), "V_MAX_F32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ISubU32 v4, v3, 0x00000002"), "V_SUBREV_NC_U32 did not reverse source order in IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftLeftLogicalU32 v6, v5, 0x00000001"), "V_LSHLREV_B32 did not reverse source order in IR"); Check(Common::ContainsStr(result.ir_dump, "UMinU32 v7"), "V_MIN_U32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "UMaxU32 v8"), "V_MAX_U32 did not lower to IR"); Check(SpirvContainsOpcode(result.spirv, 132), "SPIR-V binary does not contain OpIMul"); Check(SpirvContainsOpcode(result.spirv, 169), "SPIR-V binary does not contain OpSelect"); Check(SpirvContainsOpcode(result.spirv, 172), "SPIR-V binary does not contain OpUGreaterThan"); Check(SpirvContainsOpcode(result.spirv, 176), "SPIR-V binary does not contain OpULessThan"); Check(SpirvContainsOpcode(result.spirv, 184), "SPIR-V binary does not contain OpFOrdLessThan"); Check(SpirvContainsOpcode(result.spirv, 186), "SPIR-V binary does not contain OpFOrdGreaterThan"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerVop3pPackedF16() { const uint32_t shader[] = { EncodeVop3pWord0(0x0f, 114, 0, 7), EncodeVop3pWord1(5 + 256, 6 + 256, 0, 7), // v_pk_add_f16 v114, v5, v6 EncodeVop3pWord0(0x10, 115, 0, 7), EncodeVop3pWord1(5 + 256, 6 + 256, 0, 7), // v_pk_mul_f16 v115, v5, v6 EncodeVop3pWord0(0x11, 116, 0, 7), EncodeVop3pWord1(5 + 256, 6 + 256, 0, 7), // v_pk_min_f16 v116, v5, v6 EncodeVop3pWord0(0x12, 117, 0, 7), EncodeVop3pWord1(5 + 256, 6 + 256, 0, 7), // v_pk_max_f16 v117, v5, v6 EncodeVop3pWord0(0x0e, 118, 0, 7, 2), EncodeVop3pWord1(5 + 256, 6 + 256, 7 + 256, 7, 1), EncodeVop3pWord0(0x20, 119), EncodeVop3pWord1(6 + 256, 6 + 256, 6 + 256), // v_fma_f32 via VOP3P 0xcc204044u, 0x1a022110u, // v_fma_mix_f32 v68, v16.lo, v16.lo, 0.lo EncodeVop3pWord0(0x21, 120, 0, 7), EncodeVop3pWord1(5 + 256, 6 + 256, 7 + 256, 7), // v_mad_mixlo_f16 EncodeVop3pWord0(0x22, 121, 0, 7, 1), EncodeVop3pWord1(5 + 256, 6 + 256, 7 + 256, 7, 2), // v_mad_mixhi_f16 EncodeVop3Word0(0x34b, 122, 0x0f), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), // native v_fma_f16 EncodeVop3pWord0(0x22, 126, 0, 1, 0, true), EncodeVop3pWord1(242, 243, 3 + 256, 1), // clamped v_mad_mixhi_f16 EncodeVop3Word0(0x34b, 127, 0x8, 0, true), EncodeVop3Word1(240, 4 + 256, 241), // clamped native high-half v_fma_f16 0xd711001au, 0x0002170au, // v_pack_b32_f16 v26, v10, v11 0xd711182bu, 0x0002170au, // v_pack_b32_f16 v43, v10.hi, v11.hi 0x78765714u, // v_pk_fmac_f16 from boot shader 0x0000001c00f5c000 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "v_pk_add_f16 v114"), "new decoder did not decode old-backed V_PK_ADD_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_mul_f16 v115"), "new decoder did not decode old-backed V_PK_MUL_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_min_f16 v116"), "new decoder did not decode old-backed V_PK_MIN_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_max_f16 v117"), "new decoder did not decode old-backed V_PK_MAX_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_fma_f16 v118"), "new decoder did not decode old-backed V_PK_FMA_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_fma_f32 v119"), "new decoder did not decode old-backed VOP3P V_FMA_F32"); Check(Common::ContainsStr(result.decoded_dump, "v_fma_f32 v68, v16.opsel(lo=0,hi=1,neghi=0)"), "new decoder did not decode VOP3P V_FMA_MIX_F32 source selectors"); Check(Common::ContainsStr(result.decoded_dump, "v_mad_mixlo_f16 v120"), "new decoder did not decode old-backed V_MAD_MIXLO_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_mad_mixhi_f16 v121"), "new decoder did not decode old-backed V_MAD_MIXHI_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_fma_f16 v122"), "new decoder did not decode native VOP3 V_FMA_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_mad_mixhi_f16 v126.sdwa(sel=5"), "new decoder did not decode clamped V_MAD_MIXHI_F16 high-half destination"); Check(Common::ContainsStr(result.decoded_dump, "v_fma_f16 v127.sdwa(sel=5"), "new decoder did not decode clamped native high-half V_FMA_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_pack_b32_f16 v26"), "new decoder did not decode native V_PACK_B32_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_pack_b32_f16 v43") && Common::ContainsStr(result.decoded_dump, "v10.opsel(lo=1"), "new decoder did not decode V_PACK_B32_F16 source lane selectors"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_fmac_f16 v59"), "new decoder did not decode VOP2 V_PK_FMAC_F16"); Check(Common::ContainsStr(result.decoded_dump, "v6.opsel(lo=0,hi=1,neghi=1)"), "VOP3P high-lane source modifier was not dumped"); Check(Common::ContainsStr(result.decoded_dump, "v120.sdwa(sel=4"), "V_MAD_MIXLO_F16 did not expose low-half destination merge"); Check(Common::ContainsStr(result.decoded_dump, "v121.sdwa(sel=5"), "V_MAD_MIXHI_F16 did not expose high-half destination merge"); Check(Common::ContainsStr(result.ir_dump, "PackedAddF16 v114"), "V_PK_ADD_F16 did not lower to packed f16 IR"); Check(Common::ContainsStr(result.ir_dump, "PackedMulF16 v115"), "V_PK_MUL_F16 did not lower to packed f16 IR"); Check(Common::ContainsStr(result.ir_dump, "PackedMinF16 v116"), "V_PK_MIN_F16 did not lower to packed f16 IR"); Check(Common::ContainsStr(result.ir_dump, "PackedMaxF16 v117"), "V_PK_MAX_F16 did not lower to packed f16 IR"); Check(Common::ContainsStr(result.ir_dump, "PackedFmaF16 v118"), "V_PK_FMA_F16 did not lower to packed f16 IR"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v119"), "VOP3P V_FMA_F32 did not lower through shared fma IR"); Check(Common::ContainsStr(result.ir_dump, "FMadF32 v68, v16.opsel(lo=0,hi=1,neghi=0)"), "VOP3P V_FMA_MIX_F32 source selectors did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "MadMixF16 v120.sdwa(sel=4"), "V_MAD_MIXLO_F16 did not lower to shared mad-mix IR"); Check(Common::ContainsStr(result.ir_dump, "MadMixF16 v121.sdwa(sel=5"), "V_MAD_MIXHI_F16 did not lower to shared mad-mix IR"); Check(Common::ContainsStr(result.ir_dump, "FmaF16 v122"), "native VOP3 V_FMA_F16 did not lower to native f16 fma IR"); Check(Common::ContainsStr(result.ir_dump, "MadMixF16 v126.sdwa(sel=5"), "clamped V_MAD_MIXHI_F16 did not lower to shared mad-mix IR"); Check(Common::ContainsStr(result.ir_dump, "FmaF16 v127.sdwa(sel=5"), "clamped native V_FMA_F16 did not lower to native f16 fma IR"); Check(Common::ContainsStr(result.ir_dump, "PackB32F16 v26"), "V_PACK_B32_F16 did not lower to packed-bit IR"); Check(Common::ContainsStr(result.ir_dump, "PackB32F16 v43") && Common::ContainsStr(result.ir_dump, "v10.opsel(lo=1"), "V_PACK_B32_F16 source selectors did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "PackedFmaF16 v59, v20, v43, v59"), "V_PK_FMAC_F16 did not lower using destination as packed FMA accumulator"); Check(SpirvContainsExtInst(result.spirv, 62), "SPIR-V binary does not contain GLSL.std.450 UnpackHalf2x16 for VOP3P"); Check(SpirvContainsExtInst(result.spirv, 58), "SPIR-V binary does not contain GLSL.std.450 PackHalf2x16 for VOP3P"); Check(SpirvContainsExtInst(result.spirv, 50), "SPIR-V binary does not contain GLSL.std.450 Fma for VOP3P"); Check(SpirvContainsExtInst(result.spirv, 43), "SPIR-V binary does not contain GLSL.std.450 FClamp for clamped f16 ops"); Check(!SpirvContainsExtInst(result.spirv, 37), "SPIR-V binary still contains GLSL.std.450 FMin for VOP3P packed min"); Check(!SpirvContainsExtInst(result.spirv, 40), "SPIR-V binary still contains GLSL.std.450 FMax for VOP3P packed max"); Check(SpirvContainsOpcode(result.spirv, 169), "SPIR-V binary does not contain OpSelect for VOP3P packed min/max"); Check(SpirvContainsOpcode(result.spirv, 184), "SPIR-V binary does not contain OpFOrdLessThan for VOP3P packed min"); Check(SpirvContainsOpcode(result.spirv, 190), "SPIR-V binary does not contain OpFOrdGreaterThanEqual for VOP3P packed max"); Check(SpirvContainsOpcode(result.spirv, 197), "SPIR-V binary does not contain OpBitwiseOr for VOP3P packed min/max"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd for VOP3P packed min/max"); Check(SpirvContainsOpcode(result.spirv, 129), "SPIR-V binary does not contain OpFAdd"); Check(SpirvContainsOpcode(result.spirv, 133), "SPIR-V binary does not contain OpFMul"); Check(SpirvContainsOpcode(result.spirv, 81), "SPIR-V binary does not contain OpCompositeExtract"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerStagedShaderOps() { const uint32_t shader[] = { EncodeSMovB32(0, 132), // s0 = 4 EncodeSMovB32(1, 129), // s1 = 1 EncodeSop2(0x05, 2, 0, 1), // s_subb_u32 s2, s0, s1 EncodeSop1(0x1b, 3, 1), // s_bitset0_b32 s3, s1 EncodeVop2(0x36, 70, 5 + 256, 6), // v_fmac_f16 v70, v5, v6 EncodeVop2(0x37, 71, 7 + 256, 8), 0x3c003c00u, // v_fmamk_f16 EncodeVop2(0x38, 72, 9 + 256, 10), 0x40004000u, // v_fmaak_f16 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_subb_u32 s2, s0, s1"), "new decoder did not decode RDNA2 S_SUBB_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_bitset0_b32 s3, s1"), "new decoder did not decode RDNA2 S_BITSET0_B32"); Check(Common::ContainsStr(result.decoded_dump, "v_fmac_f16 v70.sdwa(sel=4"), "new decoder did not decode RDNA2 V_FMAC_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_fmamk_f16 v71.sdwa(sel=4,sext=0), v7, 0x3c003c00, v8"), "new decoder did not consume V_FMAMK_F16 literal as the multiply source"); Check(Common::ContainsStr(result.decoded_dump, "v_fmaak_f16 v72.sdwa(sel=4,sext=0), v9, v10, 0x40004000"), "new decoder did not consume V_FMAAK_F16 literal as the add source"); Check(Common::ContainsStr(result.ir_dump, "ScalarSubBorrowCarryU32 s2, s0, s1, scc"), "S_SUBB_U32 did not lower to scalar subtract-with-borrow IR"); Check(Common::ContainsStr(result.ir_dump, "BitClearU32 s3, s3, s1"), "S_BITSET0_B32 did not lower to bit-clear IR using the destination as input"); Check(Common::ContainsStr(result.ir_dump, "FmaF16 v70.sdwa(sel=4,sext=0), v5, v6, v70.sdwa(sel=4,sext=0)"), "V_FMAC_F16 did not lower using the destination as the FMA accumulator"); Check(Common::ContainsStr(result.ir_dump, "FmaF16 v71.sdwa(sel=4,sext=0), v7, 0x3c003c00, v8"), "V_FMAMK_F16 did not lower with the literal in source 1"); Check(Common::ContainsStr(result.ir_dump, "FmaF16 v72.sdwa(sel=4,sext=0), v9, v10, 0x40004000"), "V_FMAAK_F16 did not lower with the literal in source 2"); Check(SpirvContainsOpcode(result.spirv, 130), "SPIR-V binary does not contain OpISub for S_SUBB_U32"); Check(SpirvContainsOpcode(result.spirv, 166), "SPIR-V binary does not contain OpLogicalOr for S_SUBB_U32 borrow-out"); Check(SpirvContainsOpcode(result.spirv, 172), "SPIR-V binary does not contain OpUGreaterThan for S_SUBB_U32 borrow-out"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical for S_BITSET0_B32"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd for S_BITSET0_B32"); Check(SpirvContainsOpcode(result.spirv, 200), "SPIR-V binary does not contain OpNot for S_BITSET0_B32"); Check(SpirvContainsExtInst(result.spirv, 50), "SPIR-V binary does not contain GLSL.std.450 Fma for VOP2 F16 FMA ops"); Check(SpirvContainsExtInst(result.spirv, 58), "SPIR-V binary does not contain GLSL.std.450 PackHalf2x16 for VOP2 F16 FMA ops"); Check(SpirvContainsExtInst(result.spirv, 62), "SPIR-V binary does not contain GLSL.std.450 UnpackHalf2x16 for VOP2 F16 FMA ops"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerBootF16UnaryOpcodes() { const uint32_t shader[] = { EncodeVop1(0x55, 4, 249), EncodeVop1Sdwa(3, 5, 2, 6), EncodeVop1(0x5b, 5, 4 + 256), 0x7e12b8f9u, 0x0006150bu, 0x7e12b90cu, EncodeVop1(0x5d, 6, 5 + 256), 0x7e0e02f9u, 0x008616c1u, 0x7e1002f9u, 0x008616c1u, EncodeVop1(0x5e, 3, 3 + 256), 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "v_sqrt_f16 v4.sdwa(sel=5"), "new decoder did not decode V_SQRT_F16 SDWA high-half destination"); Check(Common::ContainsStr(result.decoded_dump, "v_rndne_f16 v3"), "new decoder did not decode V_RNDNE_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_floor_f16 v5"), "new decoder did not decode V_FLOOR_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_ceil_f16 v9.sdwa(sel=5"), "new decoder did not decode boot V_CEIL_F16 SDWA high-half destination"); Check(Common::ContainsStr(result.decoded_dump, "v_ceil_f16 v9"), "new decoder did not decode boot V_CEIL_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_trunc_f16 v6"), "new decoder did not decode V_TRUNC_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_mov_b32 v7, -1") && Common::ContainsStr(result.decoded_dump, "v_mov_b32 v8, -1"), "new decoder did not accept full-width V_MOV_B32 SDWA with DST_U=PRESERVE"); Check(!Common::ContainsStr(result.decoded_dump, "VOP1 SDWA destination selector is not supported"), "full-width V_MOV_B32 SDWA with DST_U=PRESERVE was rejected"); Check(!Common::ContainsStr(result.decoded_dump, "unsupported family=VOP2 opcode=0x00"), "VOP1 SDWA extension word was decoded as a phantom VOP2 instruction"); Check(Common::ContainsStr(result.ir_dump, "SqrtF16 v4.sdwa(sel=5"), "V_SQRT_F16 did not lower to f16 sqrt IR"); Check(Common::ContainsStr(result.ir_dump, "FloorF16 v5"), "V_FLOOR_F16 did not lower to f16 floor IR"); Check(Common::ContainsStr(result.ir_dump, "CeilF16 v9.sdwa(sel=5"), "boot V_CEIL_F16 SDWA did not lower to f16 ceil IR"); Check(Common::ContainsStr(result.ir_dump, "CeilF16 v9"), "V_CEIL_F16 did not lower to f16 ceil IR"); Check(Common::ContainsStr(result.ir_dump, "TruncF16 v6"), "V_TRUNC_F16 did not lower to f16 trunc IR"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 v7, 0xffffffff") && Common::ContainsStr(result.ir_dump, "MoveU32 v8, 0xffffffff"), "full-width V_MOV_B32 SDWA with DST_U=PRESERVE did not lower to move IR"); Check(Common::ContainsStr(result.ir_dump, "RoundEvenF16 v3"), "V_RNDNE_F16 did not lower to f16 round-even IR"); Check(SpirvContainsExtInst(result.spirv, 31), "SPIR-V binary does not contain GLSL.std.450 Sqrt for V_SQRT_F16"); Check(SpirvContainsExtInst(result.spirv, 8), "SPIR-V binary does not contain GLSL.std.450 Floor for V_FLOOR_F16"); Check(SpirvContainsExtInst(result.spirv, 9), "SPIR-V binary does not contain GLSL.std.450 Ceil for V_CEIL_F16"); Check(SpirvContainsExtInst(result.spirv, 3), "SPIR-V binary does not contain GLSL.std.450 Trunc for V_TRUNC_F16"); Check(SpirvContainsExtInst(result.spirv, 2), "SPIR-V binary does not contain GLSL.std.450 RoundEven for V_RNDNE_F16"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() { const uint32_t shader[] = { 0xd7070001u, 0x00020081u, // v_lshrrev_b16 v1.lo, 1, v0.lo 0xd7071008u, 0x0002128fu, // v_lshrrev_b16 with selected high source lane 0xd7144005u, 0x00020481u, // v_lshlrev_b16 v5.hi, 1, v2.lo 0xd7034001u, 0x0002066au, // v_add_nc_u16 v1.hi, v106.lo, v3.lo 0xd70e4011u, 0x00020e80u, // v_sub_nc_i16 v17.hi, 0, v7.lo 0xcc03400eu, 0x10020e80u, // v_pk_sub_i16 from boot shader 0x0000001980eb0000 0xcc0a4104u, 0x300202ffu, 0x00007fffu, // v_pk_add_u16 with literal 0x360e04f9u, 0x04861482u, // v_and_b32 SDWA partial low-half destination 0x381212f9u, 0x04041508u, // v_or_b32 SDWA partial high-half destination 0xcc10c00cu, 0x18021d0du, // clamped v_pk_mul_f16 0x7e08a0f9u, 0x00061500u, // v_cvt_f16_u16 v4.hi, v0 from boot shader EncodeVop1(0x50, 16, 10 + 256), // v_cvt_f16_u16 v16, v10 0x7e1ca307u, // v_cvt_f16_i16 v14, v7 0x7e1ca2f9u, 0x00051511u, // v_cvt_f16_i16 v14.hi, v17.hi 0x7e08a704u, // v_cvt_i16_f16 v4, v4 from boot shader EncodeVop1(0x52, 15, 4 + 256), // v_cvt_u16_f16 v15, v4 0x4a0a08f9u, 0x0c860688u, // v_add_nc_u32 v5, 8, sign-extended v4.lo 0x4a0c0cf9u, 0x0d860688u, // v_add_nc_u32 v6, 8, sign-extended v6.hi 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "v_lshrrev_b16 v1.sdwa(sel=4"), "new decoder did not decode low-half V_LSHRREV_B16"); Check(Common::ContainsStr(result.decoded_dump, "v_lshlrev_b16 v5.sdwa(sel=5"), "new decoder did not decode high-half V_LSHLREV_B16"); Check(Common::ContainsStr(result.decoded_dump, "v_add_nc_u16 v1.sdwa(sel=5"), "new decoder did not decode V_ADD_NC_U16 op_sel destination"); Check(Common::ContainsStr(result.decoded_dump, "v_sub_nc_i16 v17.sdwa(sel=5"), "new decoder did not decode V_SUB_NC_I16 op_sel destination"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_sub_i16 v14"), "new decoder did not decode V_PK_SUB_I16"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_add_u16 v4"), "new decoder did not decode V_PK_ADD_U16"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_add_u16 v4, 0x00007fff"), "V_PK_ADD_U16 literal was not consumed as a source operand"); Check(Common::ContainsStr(result.decoded_dump, "v_and_b32 v7.sdwa(sel=4"), "new decoder did not decode partial-destination V_AND_B32 SDWA"); Check(Common::ContainsStr(result.decoded_dump, "v_or_b32 v9.sdwa(sel=5"), "new decoder did not decode partial-destination V_OR_B32 SDWA"); Check(Common::ContainsStr(result.decoded_dump, "v_pk_mul_f16 v12"), "new decoder did not decode clamped V_PK_MUL_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f16_u16 v4.sdwa(sel=5"), "new decoder did not decode/consume SDWA V_CVT_F16_U16"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f16_u16 v16"), "new decoder did not decode plain V_CVT_F16_U16"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f16_i16 v14"), "new decoder did not decode plain V_CVT_F16_I16"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_f16_i16 v14.sdwa(sel=5"), "new decoder did not decode V_CVT_F16_I16 SDWA high-half destination"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_i16_f16 v4"), "new decoder did not decode plain V_CVT_I16_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_cvt_u16_f16 v15"), "new decoder still rejects plain V_CVT_U16_F16"); Check(Common::ContainsStr(result.decoded_dump, "v_add_nc_u32 v5") && Common::ContainsStr(result.decoded_dump, "v4.sdwa(sel=4,sext=1"), "new decoder did not decode V_ADD_NC_U32 SDWA sign-extended low word"); Check(Common::ContainsStr(result.decoded_dump, "v_add_nc_u32 v6") && Common::ContainsStr(result.decoded_dump, "v6.sdwa(sel=5,sext=1"), "new decoder did not decode V_ADD_NC_U32 SDWA sign-extended high word"); Check(!Common::ContainsStr(result.decoded_dump, "unsupported family=VOP2 opcode=0x00"), "literal/SDWA extension words were decoded as phantom VOP2 instructions"); Check(!Common::ContainsStr(result.decoded_dump, "VOP2 SDWA destination selector is not supported"), "partial bitwise SDWA destination is still rejected"); Check(Common::ContainsStr(result.ir_dump, "ShiftRightLogicalU16"), "V_LSHRREV_B16 did not lower to 16-bit logical right shift IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftLeftLogicalU16"), "V_LSHLREV_B16 did not lower to 16-bit logical left shift IR"); Check(Common::ContainsStr(result.ir_dump, "IAddU16 v1.sdwa(sel=5"), "V_ADD_NC_U16 did not lower to 16-bit add IR"); Check(Common::ContainsStr(result.ir_dump, "ISubI16 v17.sdwa(sel=5"), "V_SUB_NC_I16 did not lower to 16-bit subtract IR"); Check(Common::ContainsStr(result.ir_dump, "PackedSubI16 v14"), "V_PK_SUB_I16 did not lower to packed I16 subtract IR"); Check(Common::ContainsStr(result.ir_dump, "PackedAddU16 v4"), "V_PK_ADD_U16 did not lower to packed U16 add IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertI16ToF16 v14"), "V_CVT_F16_I16 did not lower to signed I16-to-F16 conversion IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertU16ToF16 v4.sdwa(sel=5"), "V_CVT_F16_U16 SDWA did not lower to unsigned U16-to-F16 conversion IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertU16ToF16 v16"), "V_CVT_F16_U16 plain form did not lower to unsigned U16-to-F16 conversion IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertF16ToI16 v4"), "V_CVT_I16_F16 did not lower to signed F16-to-I16 conversion IR"); Check(Common::ContainsStr(result.ir_dump, "ConvertF16ToU16 v15"), "V_CVT_U16_F16 did not lower to unsigned F16-to-U16 conversion IR"); Check(Common::ContainsStr(result.ir_dump, "IAddU32 v5") && Common::ContainsStr(result.ir_dump, "v4.sdwa(sel=4,sext=1"), "V_ADD_NC_U32 SDWA sign-extended low word did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IAddU32 v6") && Common::ContainsStr(result.ir_dump, "v6.sdwa(sel=5,sext=1"), "V_ADD_NC_U32 SDWA sign-extended high word did not lower to IR"); Check(SpirvContainsOpcode(result.spirv, 128), "SPIR-V binary does not contain OpIAdd for U16 operations"); Check(SpirvContainsOpcode(result.spirv, 202), "SPIR-V binary does not contain OpBitFieldSExtract for SDWA sign extension"); Check(SpirvContainsOpcode(result.spirv, 130), "SPIR-V binary does not contain OpISub for I16 operations"); Check(SpirvContainsOpcode(result.spirv, 194), "SPIR-V binary does not contain OpShiftRightLogical for B16 shift"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical for B16 shift"); Check(SpirvContainsOpcode(result.spirv, 197), "SPIR-V binary does not contain OpBitwiseOr for packed U16 result"); Check(SpirvContainsOpcode(result.spirv, 124), "SPIR-V binary does not contain OpConvertSToF for V_CVT_F16_I16"); Check(SpirvContainsOpcode(result.spirv, 109), "SPIR-V binary does not contain OpConvertFToU for V_CVT_U16_F16"); Check(SpirvContainsOpcode(result.spirv, 110), "SPIR-V binary does not contain OpConvertFToS for V_CVT_I16_F16"); Check(SpirvContainsOpcode(result.spirv, 112), "SPIR-V binary does not contain OpConvertUToF for V_CVT_F16_U16"); Check(SpirvContainsExtInst(result.spirv, 43), "SPIR-V binary does not contain GLSL.std.450 FClamp for clamped V_PK_MUL_F16"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerScalarB64Alu() { const uint32_t shader[] = { EncodeSMovB32(0, 129), // s0 = 1 EncodeSMovB32(1, 130), // s1 = 2 EncodeSop1(0x07, 41, 0), // s_not_b32 s41, s0 EncodeSop1(0x04, 2, 0), // s_mov_b64 s[2:3], s[0:1] EncodeSop1(0x08, 4, 2), // s_not_b64 s[4:5], s[2:3] EncodeSop2(0x0f, 6, 2, 4), // s_and_b64 s[6:7], s[2:3], s[4:5] EncodeSop2(0x11, 8, 6, 2), // s_or_b64 s[8:9], s[6:7], s[2:3] EncodeSop2(0x13, 10, 8, 4), // s_xor_b64 s[10:11], s[8:9], s[4:5] EncodeSop2(0x14, 36, 0, 1), // s_andn2_b32 s36, s0, s1 EncodeSop2(0x16, 37, 0, 1), // s_orn2_b32 s37, s0, s1 EncodeSop2(0x18, 38, 0, 1), // s_nand_b32 s38, s0, s1 EncodeSop2(0x1a, 39, 0, 1), // s_nor_b32 s39, s0, s1 EncodeSop2(0x1c, 40, 0, 1), // s_xnor_b32 s40, s0, s1 EncodeSopc(0x06, 0, 0), // s_cmp_eq_u32 s0, s0 EncodeSop2(0x0b, 12, 10, 2), // s_cselect_b64 s[12:13], s[10:11], s[2:3] EncodeSop2(0x15, 14, 10, 4), // s_andn2_b64 s[14:15], s[10:11], s[4:5] EncodeSop2(0x17, 16, 14, 6), // s_orn2_b64 s[16:17], s[14:15], s[6:7] EncodeSop2(0x19, 18, 16, 8), // s_nand_b64 s[18:19], s[16:17], s[8:9] EncodeSop2(0x1b, 20, 18, 10), // s_nor_b64 s[20:21], s[18:19], s[10:11] EncodeSop2(0x1d, 22, 20, 12), // s_xnor_b64 s[22:23], s[20:21], s[12:13] EncodeSop2(0x1f, 24, 22, 1), // s_lshl_b64 s[24:25], s[22:23], s1 EncodeSop2(0x21, 26, 24, 0), // s_lshr_b64 s[26:27], s[24:25], s0 EncodeSop2(0x25, 28, 132, 130), // s_bfm_b64 s[28:29], 4, 2 EncodeSop1(0x10, 30, 26), // s_bcnt1_i32_b64 s30, s[26:27] EncodeSop1(0x16, 106, 26), // s_flbit_i32_b64 vcc_lo, s[26:27] EncodeSop1(0x3b, 32, 30), // s_bitreplicate_b64_b32 s[32:33], s30 EncodeSop2(0x29, 34, 32, 255), // s_bfe_u64 s[34:35], s[32:33], 0x00040002 0x00040002u, EncodeSop1(0x0a, 126, 126), // s_wqm_b64 exec, exec 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_mov_b64 s2, s0"), "new decoder did not decode old-backed S_MOV_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_not_b32 s41, s0"), "new decoder did not decode RDNA2 S_NOT_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_not_b64 s4, s2"), "new decoder did not decode old-backed S_NOT_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_and_b64 s6, s2, s4"), "new decoder did not decode old-backed S_AND_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_or_b64 s8, s6, s2"), "new decoder did not decode old-backed S_OR_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_xor_b64 s10, s8, s4"), "new decoder did not decode old-backed S_XOR_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_andn2_b32 s36, s0, s1"), "new decoder did not decode RDNA2 S_ANDN2_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_orn2_b32 s37, s0, s1"), "new decoder did not decode RDNA2 S_ORN2_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_nand_b32 s38, s0, s1"), "new decoder did not decode RDNA2 S_NAND_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_nor_b32 s39, s0, s1"), "new decoder did not decode RDNA2 S_NOR_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_xnor_b32 s40, s0, s1"), "new decoder did not decode RDNA2 S_XNOR_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_cselect_b64 s12, s10, s2"), "new decoder did not decode old-backed S_CSELECT_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_andn2_b64 s14, s10, s4"), "new decoder did not decode old-backed S_ANDN2_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_orn2_b64 s16, s14, s6"), "new decoder did not decode old-backed S_ORN2_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_nand_b64 s18, s16, s8"), "new decoder did not decode old-backed S_NAND_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_nor_b64 s20, s18, s10"), "new decoder did not decode old-backed S_NOR_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_xnor_b64 s22, s20, s12"), "new decoder did not decode old-backed S_XNOR_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_lshl_b64 s24, s22, s1"), "new decoder did not decode old-backed S_LSHL_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_lshr_b64 s26, s24, s0"), "new decoder did not decode old-backed S_LSHR_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_bfm_b64 s28, 4, 2"), "new decoder did not decode old-backed S_BFM_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_bcnt1_i32_b64 s30, s26"), "new decoder did not decode old-backed S_BCNT1_I32_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_flbit_i32_b64 vcc_lo, s26"), "new decoder did not decode RDNA2 S_FLBIT_I32_B64"); Check(Common::ContainsStr(result.decoded_dump, "s_bitreplicate_b64_b32 s32, s30"), "new decoder did not decode old-backed S_BITREPLICATE_B64_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_bfe_u64 s34, s32, 0x00040002"), "new decoder did not decode old-backed S_BFE_U64"); Check(Common::ContainsStr(result.decoded_dump, "s_wqm_b64 exec_lo, exec_lo"), "new decoder did not decode old-backed S_WQM_B64"); Check(Common::ContainsStr(result.ir_dump, "MoveU64 s2, s0"), "S_MOV_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNotU32 s41, s0"), "S_NOT_B32 did not lower to scalar bitwise-not IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNotU64 s4, s2"), "S_NOT_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseAndU64 s6, s2, s4"), "S_AND_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseOrU64 s8, s6, s2"), "S_OR_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseXorU64 s10, s8, s4"), "S_XOR_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseAndNotU32 s36, s0, s1"), "S_ANDN2_B32 did not lower to scalar bitwise-and-not IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseOrNotU32 s37, s0, s1"), "S_ORN2_B32 did not lower to scalar bitwise-or-not IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNandU32 s38, s0, s1"), "S_NAND_B32 did not lower to scalar bitwise-nand IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNorU32 s39, s0, s1"), "S_NOR_B32 did not lower to scalar bitwise-nor IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseXnorU32 s40, s0, s1"), "S_XNOR_B32 did not lower to scalar bitwise-xnor IR"); Check(Common::ContainsStr(result.ir_dump, "SelectU64 s12"), "S_CSELECT_B64 did not lower to paired-dword select IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseAndNotU64 s14, s10, s4"), "S_ANDN2_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseOrNotU64 s16, s14, s6"), "S_ORN2_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNandU64 s18, s16, s8"), "S_NAND_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseNorU64 s20, s18, s10"), "S_NOR_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitwiseXnorU64 s22, s20, s12"), "S_XNOR_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftLeftLogicalU64 s24, s22, s1"), "S_LSHL_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftRightLogicalU64 s26, s24, s0"), "S_LSHR_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldMaskU64 s28"), "S_BFM_B64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitCountU64 s30, s26"), "S_BCNT1_I32_B64 did not lower to paired-source bit count IR"); Check(Common::ContainsStr(result.ir_dump, "FindMsbFromHighU64 vcc_lo, s26"), "S_FLBIT_I32_B64 did not lower to paired-source leading-zero IR"); Check(Common::ContainsStr(result.ir_dump, "BitReplicateB64B32 s32, s30"), "S_BITREPLICATE_B64_B32 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldExtractU64 s34, s32, 0x00040002"), "S_BFE_U64 did not lower to paired-dword IR"); Check(Common::ContainsStr(result.ir_dump, "WqmB64 exec_lo, exec_lo"), "S_WQM_B64 did not lower to whole-quad-mask IR"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad for scalar B64 ops"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore for scalar B64 ops"); Check(SpirvContainsOpcode(result.spirv, 197), "SPIR-V binary does not contain OpBitwiseOr for scalar B64 ops"); Check(SpirvContainsOpcode(result.spirv, 198), "SPIR-V binary does not contain OpBitwiseXor for scalar B64 ops"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd for scalar B64 ops"); Check(SpirvContainsOpcode(result.spirv, 200), "SPIR-V binary does not contain OpNot for scalar B64 ops"); Check(SpirvContainsOpcode(result.spirv, 169), "SPIR-V binary does not contain OpSelect for scalar B64 select"); Check(SpirvContainsOpcode(result.spirv, 171), "SPIR-V binary does not contain OpINotEqual for scalar B64 whole-quad mask"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical for scalar B64 shifts"); Check(SpirvContainsOpcode(result.spirv, 194), "SPIR-V binary does not contain OpShiftRightLogical for scalar B64 shifts"); Check(SpirvContainsOpcode(result.spirv, 201), "SPIR-V binary does not contain OpBitFieldInsert for scalar B64 mask"); Check(SpirvContainsOpcode(result.spirv, 203), "SPIR-V binary does not contain OpBitFieldUExtract for scalar B64 extract"); Check(SpirvContainsOpcode(result.spirv, 205), "SPIR-V binary does not contain OpBitCount for scalar B64 bit count"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerSignedCompareAlu() { const uint32_t shader[] = { EncodeSMovB32(0, 193), // s0 = -1 EncodeSMovB32(1, 129), // s1 = 1 EncodeSopc(0x02, 0, 1), // s_cmp_gt_i32 s0, s1 EncodeSopk(0x07, 0, -2), // s_cmp_lt_i32 s0, -2 EncodeVopc(0x84, 0, 1), // v_cmp_gt_i32 s0, v1 EncodeVopc(0x81, 0, 1), // v_cmp_lt_i32 s0, v1 0x7d130ef9u, 0x86068413u, // v_cmp_lt_i16 vcc_lo.sdwa, v19.word0, v7.word1 0x7d1d02f9u, 0x86068213u, // v_cmp_ge_i16 vcc_lo.sdwa, v19.word0, v1.word1 EncodeVopc(0xa9, 0, 1), // v_cmp_lt_u16 s0, v1 EncodeVopc(0x94, 0, 1), // v_cmpx_gt_i32 s0, v1 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_cmp_gt_i32"), "new decoder did not decode SOPC signed compare"); Check(Common::ContainsStr(result.decoded_dump, "s_cmp_lt_i32"), "new decoder did not decode SOPK signed compare"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_gt_i32"), "new decoder did not decode VOPC signed greater-than compare"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_lt_i32"), "new decoder did not decode VOPC signed less-than compare"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_lt_i16"), "new decoder did not decode VOPC signed halfword less-than compare"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_ge_i16"), "new decoder did not decode VOPC signed halfword greater-or-equal compare"); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_lt_u16"), "new decoder did not decode VOPC unsigned halfword less-than compare"); Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_gt_i32"), "new decoder did not decode VOPC signed compare-and-mask"); Check(Common::ContainsStr(result.ir_dump, "CompareGtI32"), "signed greater-than compare did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CompareLtI32"), "signed less-than compare did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CompareLtI16"), "signed halfword less-than compare did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CompareGeI16"), "signed halfword greater-or-equal compare did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CompareLtU16"), "unsigned halfword less-than compare did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtI32 exec_lo"), "signed compare-and-mask did not lower to exec mask IR"); Check(SpirvContainsOpcode(result.spirv, 173), "SPIR-V binary does not contain OpSGreaterThan"); Check(SpirvContainsOpcode(result.spirv, 177), "SPIR-V binary does not contain OpSLessThan"); Check(SpirvContainsOpcode(result.spirv, 202), "SPIR-V binary does not contain OpBitFieldSExtract for I16 compare"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerSignedMinShiftAlu() { const uint32_t shader[] = { EncodeSMovB32(0, 193), // s0 = -1 EncodeSMovB32(1, 130), // s1 = 2 EncodeSop2(0x06, 2, 0, 1), // s_min_i32 s2, s0, s1 EncodeSop2(0x08, 3, 0, 1), // s_max_i32 s3, s0, s1 EncodeSop2(0x22, 4, 0, 129), // s_ashr_i32 s4, s0, 1 EncodeVop2(0x11, 1, 0, 0), // v_min_i32 v1, s0, v0 EncodeVop2(0x12, 2, 1, 1), // v_max_i32 v2, s1, v1 EncodeVop2(0x17, 3, 1 + 256, 1), // v_ashr_i32 v3, v1, v1 EncodeVop2(0x18, 4, 129, 3), // v_ashrrev_i32 v4, 1, v3 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_min_i32"), "new decoder did not decode S_MIN_I32"); Check(Common::ContainsStr(result.decoded_dump, "s_ashr_i32"), "new decoder did not decode S_ASHR_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_min_i32"), "new decoder did not decode V_MIN_I32"); Check(Common::ContainsStr(result.decoded_dump, "v_ashrrev_i32"), "new decoder did not decode V_ASHRREV_I32"); Check(Common::ContainsStr(result.ir_dump, "IMinI32 s2"), "S_MIN_I32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMaxI32 s3"), "S_MAX_I32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftRightArithmeticI32 s4"), "S_ASHR_I32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMinI32 v1"), "V_MIN_I32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMaxI32 v2"), "V_MAX_I32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ShiftRightArithmeticI32 v4, v3, 0x00000001"), "V_ASHRREV_I32 did not reverse source order in IR"); Check(SpirvContainsOpcode(result.spirv, 173), "SPIR-V binary does not contain OpSGreaterThan"); Check(SpirvContainsOpcode(result.spirv, 177), "SPIR-V binary does not contain OpSLessThan"); Check(SpirvContainsOpcode(result.spirv, 195), "SPIR-V binary does not contain OpShiftRightArithmetic"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerScalarBitfieldAlu() { const uint32_t shader[] = { EncodeSMovB32(0, 193), // s0 = -1 EncodeSMovB32(1, 129), // s1 = 1 EncodeSopc(0x02, 1, 0), // s_cmp_gt_i32 s1, s0 EncodeSop2(0x0a, 2, 1, 0), // s_cselect_b32 s2, s1, s0 EncodeSop1(0x34, 3, 0), // s_abs_i32 s3, s0 EncodeSop1(0x0b, 4, 1), // s_brev_b32 s4, s1 EncodeSop2(0x24, 5, 132, 129), // s_bfm_b32 s5, 4, 1 EncodeSop2(0x27, 6, 4, 255), // s_bfe_u32 s6, s4, literal 0x00080004u, EncodeSop2(0x32, 7, 0, 1), // s_pack_ll_b32_b16 s7, s0, s1 EncodeSop2(0x33, 8, 0, 1), // s_pack_lh_b32_b16 s8, s0, s1 EncodeSop2(0x34, 9, 0, 1), // s_pack_hh_b32_b16 s9, s0, s1 EncodeSopc(0x0c, 0, 1), // s_bitcmp0_b32 s0, s1 EncodeSopc(0x0d, 1, 0), // s_bitcmp1_b32 s1, s0 EncodeSopc(0x13, 0, 2), // s_cmp_lg_u64 s[0:1], s[2:3] 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_cselect_b32"), "new decoder did not decode S_CSELECT_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_abs_i32"), "new decoder did not decode S_ABS_I32"); Check(Common::ContainsStr(result.decoded_dump, "s_brev_b32"), "new decoder did not decode S_BREV_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_bfm_b32"), "new decoder did not decode S_BFM_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_bfe_u32"), "new decoder did not decode S_BFE_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_pack_hh_b32_b16"), "new decoder did not decode S_PACK_HH_B32_B16"); Check(Common::ContainsStr(result.decoded_dump, "s_bitcmp0_b32"), "new decoder did not decode S_BITCMP0_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_bitcmp1_b32"), "new decoder did not decode S_BITCMP1_B32"); Check(Common::ContainsStr(result.decoded_dump, "s_cmp_lg_u64"), "new decoder did not decode S_CMP_LG_U64"); Check(Common::ContainsStr(result.ir_dump, "SelectU32 s2"), "S_CSELECT_B32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AbsI32 s3"), "S_ABS_I32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitReverseU32 s4"), "S_BREV_B32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldMaskU32 s5"), "S_BFM_B32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitFieldExtractU32 s6"), "S_BFE_U32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "PackLowLowU16 s7"), "S_PACK_LL_B32_B16 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "PackLowHighU16 s8"), "S_PACK_LH_B32_B16 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "PackHighHighU16 s9"), "S_PACK_HH_B32_B16 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitCompare0B32"), "S_BITCMP0_B32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BitCompare1B32"), "S_BITCMP1_B32 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "CompareNeU64"), "S_CMP_LG_U64 did not lower to IR"); Check(SpirvContainsOpcode(result.spirv, 126), "SPIR-V binary does not contain OpSNegate"); Check(SpirvContainsOpcode(result.spirv, 169), "SPIR-V binary does not contain OpSelect"); Check(SpirvContainsOpcode(result.spirv, 201), "SPIR-V binary does not contain OpBitFieldInsert"); Check(SpirvContainsOpcode(result.spirv, 203), "SPIR-V binary does not contain OpBitFieldUExtract"); Check(SpirvContainsOpcode(result.spirv, 171), "SPIR-V binary does not contain OpINotEqual"); Check(SpirvContainsOpcode(result.spirv, 166), "SPIR-V binary does not contain OpLogicalOr"); Check(SpirvContainsOpcode(result.spirv, 204), "SPIR-V binary does not contain OpBitReverse"); CheckSpirvBinaryValidates(result.spirv); } void CheckNewDecoderUnsupported(const uint32_t* shader, uint32_t words, const char* family, const char* opcode_name) { ShaderRecompiler::Decoder::Program program; std::string error; const std::span code {shader, words}; Check(ShaderRecompiler::Decoder::DecodeProgram(code, program, &error), error.c_str()); Check(program.instructions.size() >= 2, "decoder did not return instruction plus endpgm"); const auto text = ShaderRecompiler::Decoder::ProgramToString(program); Check(Common::ContainsStr(text, family), "decoder unsupported text did not include opcode family"); Check(Common::ContainsStr(text, opcode_name), "decoder unsupported text did not include opcode name"); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; Check(!ShaderRecompiler::TryRecompile(code, options, result, &error), "unsupported opcode unexpectedly lowered without implementation"); Check(Common::ContainsStr(error, "no IR lowering") || Common::ContainsStr(error, "unsupported decoded"), "unsupported lowering error was not explicit"); } void TestNewShaderRecompilerMemoryFamilyLowering() { const uint32_t shader[] = { EncodeSmem0(0x00, 0, 4), 0u, // s_load_dword s0 EncodeSmem0(0x08, 1, 4), 0u, // s_buffer_load_dword s1 EncodeMubuf0(0x0c, 4), EncodeMubuf1(0, 0, 1), // buffer_load_dword v0 EncodeMubuf0(0x1c, 8), EncodeMubuf1(0, 0, 1), // buffer_store_dword v0 EncodeDs0(0x0d), EncodeDs1(0, 0, 1), // ds_write_b32 v0, v1 EncodeDs0(0x36), EncodeDs1(2, 0, 1), // ds_read_b32 v2, v1 EncodeMimg0(0x0e, 0x1), EncodeMimg1(5, 0, 0, 1), // image_get_resinfo v5 EncodeMimg0(0x00, 0xf), EncodeMimg1(4, 0, 0, 1), // image_load v4 EncodeMimg0(0x20, 0xf), EncodeMimg1(3, 0, 0, 1), // image_sample v3 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); options.read_memory = ReadZeroTestMemory; ShaderRecompiler::CompileResult result; std::string error; const auto compiled = ShaderRecompiler::TryRecompile(shader, options, result, &error); Check(compiled, error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_load_dword"), "new decoder did not decode SMEM dword load"); Check(Common::ContainsStr(result.decoded_dump, "s_buffer_load_dword"), "new decoder did not decode SMEM scalar-buffer dword load"); Check(Common::ContainsStr(result.decoded_dump, "s_buffer_load_dword s1, s8"), "SMEM scalar-buffer SBASE was not decoded as an SGPR-pair index"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_dword"), "new decoder did not decode MUBUF dword load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_dword"), "new decoder did not decode MUBUF dword store"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_b32"), "new decoder did not decode DS dword read"); Check(Common::ContainsStr(result.decoded_dump, "image_get_resinfo"), "new decoder did not decode MIMG resinfo query"); Check(Common::ContainsStr(result.decoded_dump, "image_load"), "new decoder did not decode MIMG load"); Check(Common::ContainsStr(result.decoded_dump, "image_sample"), "new decoder did not decode MIMG sample"); Check(!Common::ContainsStr(result.decoded_dump, "lowering is not implemented"), "implemented memory decode still reports unsupported lowering"); Check(Common::ContainsStr(result.ir_dump, "SLoadDword s0"), "SMEM load did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "SBufferLoadDword s1"), "SMEM scalar-buffer load did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadDword v0"), "MUBUF load did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreDword null, v0"), "MUBUF store did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "DsWriteB32 null, v0"), "DS write did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v2"), "DS read did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ImageGetResinfo v5"), "MIMG resinfo query did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ImageLoad v4"), "MIMG load did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "ImageSample v3"), "MIMG sample did not lower to IR"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 95), "SPIR-V binary does not contain OpImageFetch"); Check(SpirvContainsOpcode(result.spirv, 103), "SPIR-V binary does not contain OpImageQuerySizeLod"); Check(SpirvContainsOpcode(result.spirv, 88), "SPIR-V binary does not contain OpImageSampleExplicitLod"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerImageQueryLowering() { const uint32_t shader[] = { EncodeMimg0(0x60, 0x3), EncodeMimg1(6, 0, 0, 1), // image_get_lod 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_get_lod"), "new decoder did not decode MIMG image get-lod query"); Check(Common::ContainsStr(result.decoded_dump, "dmask=0x3"), "image_get_lod decode did not preserve dmask metadata"); Check(Common::ContainsStr(result.ir_dump, "ImageGetLod v6"), "image_get_lod did not lower to explicit query IR"); Check(Common::ContainsStr(result.ir_dump, "data_dwords=2"), "image_get_lod did not preserve two-component result metadata"); Check(Common::ContainsStr(result.ir_dump, "image_addr=2"), "image_get_lod did not preserve address component metadata"); Check(SpirvContainsOpcode(result.spirv, 105), "SPIR-V binary does not contain OpImageQueryLod"); Check(SpirvContainsOpcode(result.spirv, 80), "SPIR-V binary does not contain coordinate composite for image_get_lod"); Check(SpirvContainsOpcode(result.spirv, 81), "SPIR-V binary does not contain dmask extraction for image_get_lod"); Check(SpirvContainsOpcode(result.spirv, 124), "SPIR-V binary does not contain result bitcast for image_get_lod"); Check(std::find(result.spirv.begin(), result.spirv.end(), 5288u) != result.spirv.end(), "SPIR-V binary does not request compute derivative group capability"); Check(std::find(result.spirv.begin(), result.spirv.end(), 5289u) != result.spirv.end(), "SPIR-V binary does not request compute derivative group execution mode"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCubeSampleCoordinates() { constexpr uint32_t MimgDimCube = 3; const uint32_t shader[] = { EncodeMimg0(0x20, 0xf, false, MimgDimCube), EncodeMimg1(0, 0, 1, 0), // image_sample cube EncodeMimg0(0x60, 0x3, false, MimgDimCube), EncodeMimg1(8, 0, 1, 4), // image_get_lod cube 0xbf810000u, }; auto user_data = ImageTestUserData(Prospero::ImageType::kCube); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(result.program.info.images.size() == 1 && result.program.info.images[0].cube, "cube descriptor identity was not preserved through compilation"); Check(SpirvInstructionOpcodeCount(result.spirv, 131) == 4, "cube sample/get-lod did not remove the RDNA2 S/T bias"); Check(SpirvInstructionOpcodeCount(result.spirv, 109) == 1 && SpirvInstructionOpcodeCount(result.spirv, 112) == 1 && SpirvInstructionOpcodeCount(result.spirv, 194) == 1 && SpirvInstructionOpcodeCount(result.spirv, 196) == 1 && SpirvInstructionOpcodeCount(result.spirv, 130) == 1, "cube sample did not repack its face ID exactly once, or get-lod used an array layer"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerImageSampleVariants() { const uint32_t shader[] = { EncodeMimg0(0x24, 0xf), EncodeMimg1(8, 0, 0, 1), // image_sample_l EncodeMimg0(0x25, 0xf), EncodeMimg1(12, 0, 0, 1), // image_sample_b EncodeMimg0(0x27, 0xf), EncodeMimg1(16, 0, 0, 1), // image_sample_lz EncodeMimg0(0x28, 0xf), EncodeMimg1(20, 0, 0, 1), // image_sample_c EncodeMimg0(0x30, 0xf), EncodeMimg1(24, 0, 0, 1), // image_sample_o EncodeMimg0(0x22, 0xf), EncodeMimg1(28, 0, 0, 1), // image_sample_d EncodeMimg0(0x2f, 0x1), EncodeMimg1(32, 0, 0, 1), // image_sample_c_lz EncodeMimg0(0x38, 0x1), EncodeMimg1(36, 0, 0, 1), // image_sample_c_o EncodeMimg0(0x2a, 0x1), EncodeMimg1(40, 0, 0, 1), // image_sample_c_d EncodeMimg0(0x20, 0x7), EncodeMimg1(48, 0, 0, 1, true), // image_sample with A16 bit 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_sample_l"), "new decoder did not decode IMAGE_SAMPLE_L through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_b"), "new decoder did not decode IMAGE_SAMPLE_B through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_lz"), "new decoder did not decode IMAGE_SAMPLE_LZ through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_c"), "new decoder did not decode IMAGE_SAMPLE_C through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_o"), "new decoder did not decode IMAGE_SAMPLE_O through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_d"), "new decoder did not decode IMAGE_SAMPLE_D through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_c_lz"), "new decoder did not decode IMAGE_SAMPLE_C_LZ through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_c_o"), "new decoder did not decode IMAGE_SAMPLE_C_O through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_c_d"), "new decoder did not decode IMAGE_SAMPLE_C_D through shared MIMG path"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=a16"), "IMAGE_SAMPLE with MIMG A16 bit did not expose A16 sample flag"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=lod"), "IMAGE_SAMPLE_L did not expose lod sample flag"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=bias"), "IMAGE_SAMPLE_B did not expose bias sample flag"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=level_zero"), "IMAGE_SAMPLE_LZ did not expose level-zero sample flag"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=compare"), "IMAGE_SAMPLE_C did not expose compare sample flag"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=offset addr_components=3"), "IMAGE_SAMPLE_O did not expose offset sample flag/address width"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=derivative addr_components=6"), "IMAGE_SAMPLE_D did not expose derivative sample flag/address width"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=compare|level_zero"), "IMAGE_SAMPLE_C_LZ did not expose compare+level-zero flags"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=compare|offset addr_components=4"), "IMAGE_SAMPLE_C_O did not expose compare+offset flags/address width"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=derivative|compare addr_components=7"), "IMAGE_SAMPLE_C_D did not expose compare+derivative address width"); Check(Common::ContainsStr(result.ir_dump, "ImageSample v8"), "IMAGE_SAMPLE_L did not lower to shared IR ImageSample"); Check(Common::ContainsStr(result.ir_dump, "ImageSample v28"), "IMAGE_SAMPLE_D did not lower to shared IR ImageSample"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x4"), "derivative sample flag did not survive into IR memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_addr=6"), "derivative sample address width did not survive into IR memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_addr=7"), "compare+derivative sample address width did not survive into IR memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x80"), "MIMG A16 bit did not survive into IR memory metadata"); Check(SpirvContainsOpcode(result.spirv, 88), "SPIR-V binary does not contain shared OpImageSampleExplicitLod emission"); Check(SpirvContainsOpcode(result.spirv, 90), "SPIR-V binary does not contain shared OpImageSampleDrefExplicitLod emission"); Check(SpirvContainsOpcode(result.spirv, 80), "SPIR-V binary does not contain coordinate/gradient composite construction"); Check(SpirvContainsExtInst(result.spirv, 62), "SPIR-V binary does not unpack A16 image address halves"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerImageSampleA16SamplerCoords() { constexpr uint32_t MimgDim3D = 2; const uint32_t shader[] = { EncodeMimg0(0x20, 0x7, false, MimgDim3D), EncodeMimg1(8, 0, 0, 4, true), // image_sample, A16 bit, 3D xyz coords 0xbf810000u, }; auto user_data = ImageTestUserData(Prospero::ImageType::kColor3D); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; const auto compiled = ShaderRecompiler::TryRecompile(shader, options, result, &error); Check(compiled, error.c_str()); Check( Common::ContainsStr(result.decoded_dump, "image_dim=3d sample_flags=a16 addr_components=3"), "3D IMAGE_SAMPLE with MIMG A16 bit did not decode as three A16 sampler coords"); Check(Common::ContainsStr(result.ir_dump, "ImageSample v8"), "3D A16 IMAGE_SAMPLE did not lower to sample IR"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x80"), "3D A16 IMAGE_SAMPLE did not preserve A16 memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_dim=3d"), "3D A16 IMAGE_SAMPLE did not preserve image dimension metadata"); Check(Common::ContainsStr(result.ir_dump, "image_addr=3"), "3D A16 IMAGE_SAMPLE did not preserve three logical address components"); Check(SpirvExtInstCount(result.spirv, 62) == 3, "sampler A16 xyz coordinates should be converted from three packed f16 components"); Check(SpirvContainsOpcode(result.spirv, 194), "sampler A16 high-half coordinate extraction is missing"); Check(SpirvContainsOpcode(result.spirv, 199), "sampler A16 low-half coordinate masking is missing"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerImageSampleOpcodeAliases() { const uint32_t shader[] = { 0xf0800109u, 0x00c00602u, // observed image_sample_a v6, v2, s0, s24 EncodeMimg0(0xa5, 0xf), EncodeMimg1(12, 0, 0, 4), // image_sample_b_a EncodeMimg0(0xa8, 0x1), EncodeMimg1(16, 0, 0, 4), // image_sample_c_a EncodeMimg0(0xad, 0x1), EncodeMimg1(20, 0, 0, 4), // image_sample_c_b_a 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_sample_a"), "MIMG opcode 0xa0 should decode as image_sample_a alias"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_b_a"), "MIMG opcode 0xa5 should decode as image_sample_b_a alias"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_c_a"), "MIMG opcode 0xa8 should decode as image_sample_c_a alias"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_c_b_a"), "MIMG opcode 0xad should decode as image_sample_c_b_a alias"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=none"), "opcode 0xa0 alias should use normal 32-bit sample coordinates"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=bias|compare"), "compare+bias opcode alias did not expose expected sample flags"); Check(!Common::ContainsStr(result.decoded_dump, "a16"), "A16 must come from MIMG bit 62, not from the opcode alias"); Check(!SpirvContainsExtInst(result.spirv, 62), "opcode aliases without bit 62 must not unpack sampled f16 address halves"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerImageSampleA16ExceptionComponents() { auto compile = [](uint32_t opcode, uint32_t dmask, uint32_t vdata) { const uint32_t shader[] = { EncodeMimg0(opcode, dmask), EncodeMimg1(vdata, 0, 0, 4, true), 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); CheckSpirvBinaryValidates(result.spirv); return result; }; { const auto result = compile(0x28, 0x1, 12); // image_sample_c with A16 bit Check( Common::ContainsStr(result.decoded_dump, "sample_flags=compare|a16 addr_components=3"), "A16 IMAGE_SAMPLE_C did not preserve compare+A16 metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x88"), "A16 IMAGE_SAMPLE_C did not preserve compare+A16 IR flags"); Check(SpirvInstructionOpcodeCount(result.spirv, 90) == 1, "A16 IMAGE_SAMPLE_C should emit one dref sample"); Check(SpirvExtInstCount(result.spirv, 62) == 2, "PCF reference must remain 32-bit while only xy sampler coords use A16"); } { const auto result = compile(0x30, 0xf, 16); // image_sample_o with A16 bit Check(Common::ContainsStr(result.decoded_dump, "sample_flags=offset|a16 addr_components=3"), "A16 IMAGE_SAMPLE_O did not preserve offset+A16 metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x90"), "A16 IMAGE_SAMPLE_O did not preserve offset+A16 IR flags"); Check(SpirvContainsOpcode(result.spirv, 202), "texel offset should still be decoded from its packed 6-bit fields"); Check(SpirvExtInstCount(result.spirv, 62) == 2, "texel offset must remain 32-bit while only xy sampler coords use A16"); } } void TestNewShaderRecompilerImageSampleA16Rdna2AddressOrder() { namespace Decoder = Libs::Graphics::ShaderRecompiler::Decoder; namespace Emitter = Libs::Graphics::ShaderRecompiler::Spirv::Emitter; namespace IR = Libs::Graphics::ShaderRecompiler::IR; auto make_inst = [](uint32_t flags) { IR::Instruction inst {}; inst.src[0] = Emitter::MakeRegisterOperand(IR::RegisterFile::Vector, 20); inst.memory.image_sample_flags = flags | Decoder::ImageSampleFlagA16; inst.memory.image_address_components = 5; return inst; }; auto check_vgpr = [](const IR::Operand& operand, uint32_t expected, const char* message) { Check(operand.kind == IR::OperandKind::Register && operand.reg.file == IR::RegisterFile::Vector && operand.reg.index == expected, message); }; { auto inst = make_inst(Decoder::ImageSampleFlagBias | Decoder::ImageSampleFlagCompare); const auto layout = Emitter::MakeImageSampleLayout(inst, Emitter::ImageViewKind::Dim2D); Check(layout.bias == 0u, "RDNA2 sample_c_b A16 bias must precede PCF reference"); Check(layout.dref == 1u, "RDNA2 sample_c_b A16 PCF reference must follow bias"); Check(layout.coord == 2u, "RDNA2 sample_c_b A16 body must follow bias and PCF"); Check(Emitter::ImageAddressHalfComponent(inst, layout.bias) == 0u, "A16 bias should use the first packed half"); Check(Emitter::ImageAddressHalfComponent(inst, layout.dref) == 2u, "32-bit PCF reference must align to the next VGPR after A16 bias"); Check(Emitter::ImageAddressHalfComponent(inst, layout.coord) == 4u, "A16 coordinates must start after the aligned PCF reference"); check_vgpr(Emitter::ImageAddressOperand(inst, inst.src[0], layout.bias), 20, "A16 sample_c_b bias should read v20"); check_vgpr(Emitter::ImageAddressOperand(inst, inst.src[0], layout.dref), 21, "A16 sample_c_b PCF reference should read v21"); check_vgpr(Emitter::ImageAddressOperand(inst, inst.src[0], layout.coord), 22, "A16 sample_c_b x/y coordinates should start in v22"); } { auto inst = make_inst(Decoder::ImageSampleFlagOffset | Decoder::ImageSampleFlagBias | Decoder::ImageSampleFlagCompare); const auto layout = Emitter::MakeImageSampleLayout(inst, Emitter::ImageViewKind::Dim2D); Check(layout.offset == 0u && layout.bias == 1u && layout.dref == 2u && layout.coord == 3u, "RDNA2 sample_c_b_o order must be offset, bias, PCF, body"); Check(Emitter::ImageAddressHalfComponent(inst, layout.offset) == 0u, "texel offset should stay a packed 32-bit VGPR"); Check(Emitter::ImageAddressHalfComponent(inst, layout.bias) == 2u, "A16 bias should follow the 32-bit texel offset"); Check(Emitter::ImageAddressHalfComponent(inst, layout.dref) == 4u, "PCF reference should align after A16 bias"); Check(Emitter::ImageAddressHalfComponent(inst, layout.coord) == 6u, "A16 coordinates should start after offset, bias, and PCF"); check_vgpr(Emitter::ImageAddressOperand(inst, inst.src[0], layout.offset), 20, "A16 sample_c_b_o offset should read v20"); check_vgpr(Emitter::ImageAddressOperand(inst, inst.src[0], layout.bias), 21, "A16 sample_c_b_o bias should read v21"); check_vgpr(Emitter::ImageAddressOperand(inst, inst.src[0], layout.dref), 22, "A16 sample_c_b_o PCF reference should read v22"); check_vgpr(Emitter::ImageAddressOperand(inst, inst.src[0], layout.coord), 23, "A16 sample_c_b_o x/y coordinates should start in v23"); } } void TestNewShaderRecompilerImageLoadA16UintCoords() { const uint32_t shader[] = { EncodeMimg0(0x00, 0x3), EncodeMimg1(20, 0, 0, 4, true), // image_load, A16 bit 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_load"), "A16 IMAGE_LOAD did not decode"); Check(Common::ContainsStr(result.ir_dump, "ImageLoad v20"), "A16 IMAGE_LOAD did not lower to image-load IR"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x80"), "A16 IMAGE_LOAD did not preserve A16 memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_addr=2"), "A16 IMAGE_LOAD did not preserve logical address component count"); Check(!SpirvContainsExtInst(result.spirv, 62), "image ops without sampler use u16 A16 addresses and must not unpack f16"); Check(SpirvContainsOpcode(result.spirv, 194), "u16 A16 high-half coordinate extraction is missing"); Check(SpirvContainsOpcode(result.spirv, 199), "u16 A16 low-half coordinate masking is missing"); Check(SpirvContainsOpcode(result.spirv, 95), "A16 IMAGE_LOAD did not emit an image fetch"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerPixelImageSampleLodSelection() { constexpr uint32_t OpImageSampleImplicitLod = 87; constexpr uint32_t OpImageSampleExplicitLod = 88; constexpr uint32_t OpImageSampleDrefImplicitLod = 89; constexpr uint32_t OpImageSampleDrefExplicitLod = 90; auto compile = [](uint32_t opcode, uint32_t dmask) { const uint32_t shader[] = { EncodeMimg0(opcode, dmask), EncodeMimg1(0, 0, 0, 4), 0xbf810000u, }; auto ps_info = RegressionPixelInputInfo(); auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.pixel_input_info = &ps_info; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); CheckSpirvBinaryValidates(result.spirv); return result; }; { const auto result = compile(0x20, 0xf); // image_sample Check(Common::ContainsStr(result.decoded_dump, "image_sample"), "plain pixel IMAGE_SAMPLE did not decode"); Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleImplicitLod) == 1, "plain pixel IMAGE_SAMPLE must use OpImageSampleImplicitLod"); Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleExplicitLod) == 0, "plain pixel IMAGE_SAMPLE unexpectedly used explicit lod"); } { const auto result = compile(0x25, 0xf); // image_sample_b Check(Common::ContainsStr(result.decoded_dump, "sample_flags=bias"), "pixel IMAGE_SAMPLE_B did not preserve bias metadata"); Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleImplicitLod) == 1, "pixel IMAGE_SAMPLE_B must use implicit lod with a bias operand"); Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleExplicitLod) == 0, "pixel IMAGE_SAMPLE_B unexpectedly used explicit lod"); } { const auto result = compile(0x27, 0xf); // image_sample_lz Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleExplicitLod) == 1, "pixel IMAGE_SAMPLE_LZ must use explicit lod"); } { const auto result = compile(0x28, 0x1); // image_sample_c Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleDrefImplicitLod) == 1, "pixel IMAGE_SAMPLE_C must use OpImageSampleDrefImplicitLod"); Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleDrefExplicitLod) == 0, "pixel IMAGE_SAMPLE_C unexpectedly used explicit dref lod"); } { const auto result = compile(0x2f, 0x1); // image_sample_c_lz Check(SpirvInstructionOpcodeCount(result.spirv, OpImageSampleDrefExplicitLod) == 1, "pixel IMAGE_SAMPLE_C_LZ must use explicit dref lod"); } } void TestNewShaderRecompilerImageViewDimensions() { constexpr uint32_t MimgDim1D = 0; constexpr uint32_t MimgDim3D = 2; constexpr uint32_t MimgDim1DArray = 4; constexpr uint32_t MimgDim2DArray = 5; constexpr uint32_t SpirvDim1D = 0; constexpr uint32_t SpirvDim2D = 1; constexpr uint32_t SpirvDim3D = 2; const uint32_t shader[] = { EncodeMimg0(0x20, 0xf, false, MimgDim2DArray), EncodeMimg1(0, 0, 1, 0), // image_sample 2D array EncodeMimg0(0x24, 0xf, false, MimgDim2DArray), EncodeMimg1(4, 1, 1, 4), // image_sample_l 2D array EncodeMimg0(0x20, 0xf, false, MimgDim3D), EncodeMimg1(8, 2, 1, 8), // image_sample 3D EncodeMimg0(0x01, 0xf, false, MimgDim2DArray), EncodeMimg1(12, 3, 0, 12), // image_load_mip 2D array EncodeMimg0(0x20, 0xf, false, MimgDim1D), EncodeMimg1(16, 4, 1, 16), // image_sample 1D EncodeMimg0(0x20, 0xf, false, MimgDim1DArray), EncodeMimg1(20, 5, 1, 20), // image_sample 1D array EncodeMimg0(0x22, 0xf, false, MimgDim1D), EncodeMimg1(24, 6, 1, 24), // image_sample_d 1D EncodeMimg0(0x01, 0xf, false, MimgDim1D), EncodeMimg1(28, 7, 0, 28), // image_load_mip 1D EncodeMimg0(0x0e, 0xf, false, MimgDim1D), EncodeMimg1(32, 8, 0, 32), // image_get_resinfo 1D EncodeMimg0(0x00, 0xf, false, MimgDim1D), EncodeMimg1(36, 9, 0, 36), // integer image_load 1D 0xbf810000u, }; auto ps_info = RegressionPixelInputInfo(); auto user_data = ImageTestUserData(); SetImageTestType(&user_data, 0, Prospero::ImageType::kColor2DArray); SetImageTestType(&user_data, 1, Prospero::ImageType::kColor2DArray); SetImageTestType(&user_data, 2, Prospero::ImageType::kColor3D); SetImageTestType(&user_data, 3, Prospero::ImageType::kColor2DArray); SetImageTestType(&user_data, 4, Prospero::ImageType::kColor1D); SetImageTestType(&user_data, 5, Prospero::ImageType::kColor1DArray); SetImageTestType(&user_data, 6, Prospero::ImageType::kColor1D); SetImageTestType(&user_data, 7, Prospero::ImageType::kColor1D); SetImageTestType(&user_data, 8, Prospero::ImageType::kColor1D); SetImageTestType(&user_data, 9, Prospero::ImageType::kColor1D); SetImageTestFormat(&user_data, 9, Prospero::BufferFormat::k32UInt); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.pixel_input_info = &ps_info; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; const auto compiled = ShaderRecompiler::TryRecompile(shader, options, result, &error); Check(compiled, error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_dim=2d_array"), "MIMG DIM did not decode 2D-array image view"); Check(Common::ContainsStr(result.decoded_dump, "image_dim=3d"), "MIMG DIM did not decode 3D image view"); Check(Common::ContainsStr(result.decoded_dump, "image_dim=1d sample_flags=none addr_components=1"), "1D sample did not preserve its scalar coordinate"); Check(Common::ContainsStr(result.decoded_dump, "image_dim=1d_array sample_flags=none addr_components=2"), "1D-array sample did not preserve coordinate plus layer"); Check(Common::ContainsStr(result.decoded_dump, "image_dim=1d sample_flags=derivative addr_components=3"), "1D derivative sample did not preserve scalar gradients"); Check(Common::ContainsStr(result.decoded_dump, "image_sample_l"), "2D-array LOD sample did not decode"); Check(Common::ContainsStr(result.decoded_dump, "image_dim=2d_array sample_flags=lod addr_components=4"), "2D-array LOD sample did not include slice plus LOD address components"); Check(Common::ContainsStr(result.ir_dump, "image_dim=2d_array"), "2D-array image view did not survive into IR metadata"); Check(Common::ContainsStr(result.ir_dump, "image_dim=3d"), "3D image view did not survive into IR metadata"); Check(Common::ContainsStr(result.ir_dump, "image_dim=1d"), "1D image view did not survive into IR metadata"); Check(Common::ContainsStr(result.ir_dump, "image_dim=1d_array"), "1D-array image view did not survive into IR metadata"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 0, 1), "SPIR-V binary does not contain sampled 1D image type"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 1, 1), "SPIR-V binary does not contain sampled 1D-array image type"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim2D, 1, 1), "SPIR-V binary does not contain sampled 2D-array image type"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim3D, 0, 1), "SPIR-V binary does not contain sampled 3D image type"); Check(SpirvContainsCapability(result.spirv, 43), "SPIR-V binary does not request Sampled1D"); Check(SpirvContainsCapability(result.spirv, 44), "SPIR-V binary does not request Image1D"); Check(SpirvContainsOpcode(result.spirv, 95), "SPIR-V binary does not contain array image fetch"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(Common::ContainsStr(source, " Grad "), "1D derivative sample did not emit scalar SPIR-V gradients"); Check(Common::ContainsStr(source, "OpImageQuerySizeLod"), "1D resource query did not emit a scalar size query"); Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_uint_1d"), "integer 1D load did not access the uint 1D descriptor binding"); } void TestNewShaderRecompilerStorageImage1DDescriptorVariants() { constexpr uint32_t MimgDim1D = 0; constexpr uint32_t MimgDim1DArray = 4; constexpr uint32_t SpirvDim1D = 0; const uint32_t shader[] = { EncodeMimg0(0x08, 0xf, false, MimgDim1D), EncodeMimg1(20, 4, 0, 4), // image_store 1D EncodeMimg0(0x08, 0xf, false, MimgDim1DArray), EncodeMimg1(24, 5, 0, 8), // image_store 1D array 0xbf810000u, }; auto user_data = ImageTestUserData(); SetImageTestType(&user_data, 4, Prospero::ImageType::kColor1D); SetImageTestType(&user_data, 5, Prospero::ImageType::kColor1DArray); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "image_dim=1d"), "1D storage dimension did not survive into IR metadata"); Check(Common::ContainsStr(result.ir_dump, "image_dim=1d_array"), "1D-array storage dimension did not survive into IR metadata"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 0, 2), "SPIR-V binary does not contain storage 1D image type"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim1D, 1, 2), "SPIR-V binary does not contain storage 1D-array image type"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_1d "), "1D store did not access the 1D storage descriptor binding"); Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_1d_array"), "1D-array store did not access the 1D-array storage descriptor binding"); } void TestNewShaderRecompilerNullImageUsesCanonical2DView() { const uint32_t shader[] = { EncodeMimg0(0x20, 0xf, false, 0), EncodeMimg1(0, 0, 1, 0), // 1D instruction with a null image descriptor 0xbf810000u, }; std::array user_data {}; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_2d "), "null image did not use the canonical 2D sampled descriptor binding"); Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_1d "), "null image retained the decoded 1D descriptor binding"); } void TestNewShaderRecompilerRejectsOneDimensionalGather() { constexpr std::array cases { std::pair {0u, Prospero::ImageType::kColor1D}, std::pair {4u, Prospero::ImageType::kColor1DArray}, }; for (const auto& [dimension, type]: cases) { const uint32_t shader[] = { EncodeMimg0(0x47, 0x1, false, dimension), EncodeMimg1(0, 0, 1, 0), 0xbf810000u, }; auto user_data = ImageTestUserData(type); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(!ShaderRecompiler::TryRecompile(shader, options, result, &error) && Common::ContainsStr(error, "1D image gather is not supported by SPIR-V"), "1D image gather escaped as invalid SPIR-V"); } } void TestNewShaderRecompilerImageGatherVariants() { const uint32_t shader[] = { EncodeMimg0(0x47, 0x1), EncodeMimg1(60, 0, 1, 4), // image_gather4_lz EncodeMimg0(0x48, 0x2), EncodeMimg1(64, 0, 1, 8), // image_gather4_c EncodeMimg0(0x4f, 0x1), EncodeMimg1(68, 0, 1, 12), // image_gather4_c_lz EncodeMimg0(0x57, 0x4), EncodeMimg1(72, 0, 1, 16), // image_gather4_lz_o EncodeMimg0(0x58, 0x1), EncodeMimg1(76, 0, 1, 20), // image_gather4_c_o EncodeMimg0(0x5f, 0x1), EncodeMimg1(80, 0, 1, 24), // image_gather4_c_lz_o 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_gather4_lz"), "new decoder did not decode IMAGE_GATHER4_LZ"); Check(Common::ContainsStr(result.decoded_dump, "image_gather4_lz_o"), "new decoder did not decode IMAGE_GATHER4_LZ_O"); Check(Common::ContainsStr(result.decoded_dump, "image_gather4_c"), "new decoder did not decode IMAGE_GATHER4_C"); Check(Common::ContainsStr(result.decoded_dump, "image_gather4_c_lz"), "new decoder did not decode IMAGE_GATHER4_C_LZ"); Check(Common::ContainsStr(result.decoded_dump, "image_gather4_c_o"), "new decoder did not decode IMAGE_GATHER4_C_O"); Check(Common::ContainsStr(result.decoded_dump, "image_gather4_c_lz_o"), "new decoder did not decode IMAGE_GATHER4_C_LZ_O"); Check(Common::ContainsStr(result.decoded_dump, "dmask=0x4"), "IMAGE_GATHER4_LZ_O did not preserve gather component dmask"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=offset|level_zero addr_components=3"), "IMAGE_GATHER4_LZ_O did not expose shared offset sample metadata"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=compare addr_components=3"), "IMAGE_GATHER4_C did not expose compare sample metadata"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=compare|level_zero addr_components=3"), "IMAGE_GATHER4_C_LZ did not expose compare+level-zero sample metadata"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=compare|offset addr_components=4"), "IMAGE_GATHER4_C_O did not expose compare+offset sample metadata"); Check(Common::ContainsStr(result.decoded_dump, "sample_flags=compare|offset|level_zero addr_components=4"), "IMAGE_GATHER4_C_LZ_O did not expose compare+offset+level-zero sample metadata"); Check(Common::ContainsStr(result.ir_dump, "ImageGather4 v60"), "IMAGE_GATHER4_LZ did not lower to shared IR ImageGather4"); Check(Common::ContainsStr(result.ir_dump, "ImageGather4 v64"), "IMAGE_GATHER4_C did not lower to shared IR ImageGather4"); Check(Common::ContainsStr(result.ir_dump, "ImageGather4 v68"), "IMAGE_GATHER4_C_LZ did not lower to shared IR ImageGather4"); Check(Common::ContainsStr(result.ir_dump, "ImageGather4 v72"), "IMAGE_GATHER4_LZ_O did not lower to shared IR ImageGather4"); Check(Common::ContainsStr(result.ir_dump, "ImageGather4 v76"), "IMAGE_GATHER4_C_O did not lower to shared IR ImageGather4"); Check(Common::ContainsStr(result.ir_dump, "ImageGather4 v80"), "IMAGE_GATHER4_C_LZ_O did not lower to shared IR ImageGather4"); Check(Common::ContainsStr(result.ir_dump, "data_dwords=4"), "IMAGE_GATHER4 did not preserve four-component result metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x30"), "IMAGE_GATHER4_LZ_O flags did not survive into IR memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x8"), "IMAGE_GATHER4_C flags did not survive into IR memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x28"), "IMAGE_GATHER4_C_LZ flags did not survive into IR memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x18"), "IMAGE_GATHER4_C_O flags did not survive into IR memory metadata"); Check(Common::ContainsStr(result.ir_dump, "image_flags=0x38"), "IMAGE_GATHER4_C_LZ_O flags did not survive into IR memory metadata"); Check(SpirvContainsCapability(result.spirv, 25), "SPIR-V binary does not request ImageGatherExtended"); Check(SpirvContainsOpcode(result.spirv, 96), "SPIR-V binary does not contain OpImageGather"); Check(SpirvContainsOpcode(result.spirv, 97), "SPIR-V binary does not contain OpImageDrefGather"); Check(SpirvContainsOpcode(result.spirv, 202), "SPIR-V binary does not contain packed gather offset extraction"); Check(SpirvContainsOpcode(result.spirv, 81), "SPIR-V binary does not contain gather result extraction"); Check(SpirvContainsOpcode(result.spirv, 124), "SPIR-V binary does not contain gather result bitcast"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerImageLoadVariants() { const uint32_t shader[] = { EncodeMimg0(0x00, 0x3), EncodeMimg1(8, 0, 0, 1), // image_load dmask xy EncodeMimg0(0x01, 0xf), EncodeMimg1(12, 1, 0, 4), // image_load_mip dmask xyzw 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_load"), "new decoder did not decode MIMG image load"); Check(Common::ContainsStr(result.decoded_dump, "image_load_mip"), "new decoder did not decode MIMG image load mip"); Check(Common::ContainsStr(result.decoded_dump, "dmask=0x3"), "image_load decode did not preserve partial dmask"); Check(Common::ContainsStr(result.decoded_dump, "dmask=0xf"), "image_load_mip decode did not preserve full dmask"); Check(Common::ContainsStr(result.ir_dump, "ImageLoad v8"), "image_load did not lower through shared image-load IR"); Check(Common::ContainsStr(result.ir_dump, "ImageLoad v12"), "image_load_mip did not lower through shared image-load IR"); Check(Common::ContainsStr(result.ir_dump, "data_dwords=2"), "image_load dmask xy did not preserve two-component result metadata"); Check(Common::ContainsStr(result.ir_dump, "data_dwords=4"), "image_load_mip dmask xyzw did not preserve four-component result metadata"); Check(Common::ContainsStr(result.ir_dump, "image_addr=3 image_mip=1"), "image_load_mip did not preserve mip address component metadata"); Check(SpirvContainsOpcode(result.spirv, 95), "SPIR-V binary does not contain OpImageFetch"); Check(SpirvContainsOpcode(result.spirv, 81), "SPIR-V binary does not contain OpCompositeExtract for image-load dmask"); Check(SpirvContainsOpcode(result.spirv, 124), "SPIR-V binary does not contain OpBitcast for image-load result bits"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerImageLoad2DMsaa() { const uint32_t shader[] = { 0xf0000130u, // image_load v3, v[5:7], s[0:7] dmask:x dim:2d_msaa 0x00000305u, 0xbf810000u, }; auto user_data = ImageTestUserData(Prospero::ImageType::kColor2DMsaa); user_data[3] |= 2u << 16u; user_data[5] |= 2u << 4u; user_data[6] |= 1u << 10u; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_dim=2d_msaa") && Common::ContainsStr(result.ir_dump, "image_dim=2d_msaa") && Common::ContainsStr(result.ir_dump, "image_addr=3 image_mip=0"), "RDNA2 2D-MSAA load did not preserve x, y, and fragment ID"); Check(result.program.info.images.size() == 1 && result.program.info.images[0].dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa, "2D-MSAA descriptor specialization lost the multisample dimension"); Check(ShaderRecompiler::IR::FindBinding( result.program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::Sampled2DMsaa) != nullptr, "2D-MSAA image did not receive a multisampled descriptor binding"); Check(SpirvContainsTypeImage(result.spirv, 1, 0, 1, 1), "SPIR-V binary does not contain a multisampled 2D image type"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_2d_msaa"), "2D-MSAA load did not access its multisampled descriptor"); Check(SpirvSourceHasInstructionUsing(source, "OpImageFetch", " Sample "), "2D-MSAA load did not emit the fragment ID as a SPIR-V Sample operand"); Check(!SpirvSourceHasInstructionUsing(source, "OpImageFetch", " Lod "), "2D-MSAA load incorrectly emitted its fragment ID as a mip level"); } void TestNewShaderRecompilerImageStoreLowering() { const uint32_t shader[] = { EncodeMimg0(0x08, 0xf), EncodeMimg1(20, 0, 0, 4), // image_store EncodeMimg0(0x09, 0x3), EncodeMimg1(24, 1, 0, 8), // image_store_mip 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_store"), "new decoder did not decode MIMG image store"); Check(Common::ContainsStr(result.decoded_dump, "image_store_mip"), "new decoder did not decode MIMG image store mip"); Check(Common::ContainsStr(result.decoded_dump, "dmask=0xf"), "image store decode did not preserve full dmask"); Check(Common::ContainsStr(result.decoded_dump, "dmask=0x3"), "image store mip decode did not preserve partial dmask"); Check(Common::ContainsStr(result.ir_dump, "ImageStore null, v20, v4"), "image_store did not lower to shared image-store IR"); Check(Common::ContainsStr(result.ir_dump, "ImageStore null, v24, v8"), "image_store_mip did not lower to shared image-store IR"); Check(Common::ContainsStr(result.ir_dump, "storage_image"), "image store IR did not use storage-image resource metadata"); Check(Common::ContainsStr(result.ir_dump, "data_dwords=4"), "full-dmask image store did not preserve data component count"); Check(Common::ContainsStr(result.ir_dump, "image_addr=3 image_mip=1"), "mip image store did not preserve mip address component count"); Check(SpirvContainsOpcode(result.spirv, 99), "SPIR-V binary does not contain OpImageWrite"); Check(SpirvContainsOpcode(result.spirv, 80), "SPIR-V binary does not contain image texel/coordinate composites"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerStorageImage3DDescriptorVariant() { constexpr uint32_t ImageTypeColor3D = 10; constexpr uint32_t ImageFormatRgba16f = 71; constexpr uint32_t SpirvDim3D = 2; constexpr uint32_t MimgDim3D = 2; const uint32_t shader[] = { EncodeMimg0(0x08, 0xf, false, MimgDim3D), EncodeMimg1(20, 5, 0, 4), // image_store 3D 0xbf810000u, }; ShaderComputeInputInfo input_info = RegressionComputeInputInfo(); std::array user_data {}; user_data[20] = 0x1000u; user_data[21] = ImageFormatRgba16f << 20u; user_data[22] = 255u | (255u << 14u); user_data[23] = ImageTypeColor3D << 28u; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.compute_input_info = &input_info; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_dim=3d"), "MIMG store did not decode the RDNA2 3D instruction dimension"); const auto has_3d_store = std::any_of( result.program.blocks.begin(), result.program.blocks.end(), [](const auto& block) { return std::any_of(block.instructions.begin(), block.instructions.end(), [](const auto& inst) { return inst.op == ShaderRecompiler::IR::Opcode::ImageStore && inst.memory.image_dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D && inst.memory.image_address_components == 3; }); }); Check(has_3d_store, "3D storage image store did not preserve three address components"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim3D, 0, 2), "SPIR-V binary does not contain storage 3D image type"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_3d"), "storage image store did not access the 3D storage descriptor binding"); } void TestNewShaderRecompilerStorageImage2DDescriptorOverridesMimg3D() { constexpr uint32_t ImageTypeColor2D = 9; constexpr uint32_t ImageFormatRgba16f = 71; constexpr uint32_t MimgDim3D = 2; constexpr uint32_t SpirvDim2D = 1; const uint32_t shader[] = { EncodeMimg0(0x08, 0xf, false, MimgDim3D), EncodeMimg1(20, 5, 0, 4), 0xbf810000u, }; ShaderComputeInputInfo input_info = RegressionComputeInputInfo(); std::array user_data {}; user_data[20] = 0x1000u; user_data[21] = ImageFormatRgba16f << 20u; user_data[22] = 255u | (255u << 14u); user_data[23] = ImageTypeColor2D << 28u; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.compute_input_info = &input_info; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_dim=3d"), "test MIMG store should decode as a 3D instruction"); Check(SpirvContainsTypeImage(result.spirv, SpirvDim2D, 0, 2), "SPIR-V binary does not contain storage 2D image type"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_2d"), "2D descriptor storage image store did not access the base storage binding"); Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_2d_array"), "2D descriptor storage image store unexpectedly used the array storage binding"); Check(!SpirvSourceHasInstructionUsing(source, "OpAccessChain", "storage_3d"), "2D descriptor storage image store unexpectedly used the 3D storage binding"); } void TestNewShaderRecompilerImageAtomicLowering() { const uint32_t shader[] = { EncodeMimg0(0x11, 0x1, true), EncodeMimg1(52, 0, 0, 1), // image_atomic_add EncodeMimg0(0x15, 0x1, true), EncodeMimg1(53, 0, 0, 1), // image_atomic_umin EncodeMimg0(0x17, 0x1, true), EncodeMimg1(57, 0, 0, 1), // image_atomic_umax EncodeMimg0(0x18, 0x1, true), EncodeMimg1(54, 0, 0, 1), // image_atomic_and EncodeMimg0(0x19, 0x1, true), EncodeMimg1(55, 0, 0, 1), // image_atomic_or EncodeMimg0(0x1a, 0x1, true), EncodeMimg1(56, 0, 0, 1), // image_atomic_xor 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "image_atomic_add"), "new decoder did not decode MIMG image atomic add"); Check(Common::ContainsStr(result.decoded_dump, "image_atomic_umin"), "new decoder did not decode MIMG image atomic umin"); Check(Common::ContainsStr(result.decoded_dump, "image_atomic_umax"), "new decoder did not decode MIMG image atomic umax"); Check(Common::ContainsStr(result.decoded_dump, "image_atomic_and"), "new decoder did not decode MIMG image atomic and"); Check(Common::ContainsStr(result.decoded_dump, "image_atomic_or"), "new decoder did not decode MIMG image atomic or"); Check(Common::ContainsStr(result.decoded_dump, "image_atomic_xor"), "new decoder did not decode MIMG image atomic xor"); Check(Common::ContainsStr(result.decoded_dump, "dmask=0x1"), "image atomic decode did not preserve dmask metadata"); Check(Common::ContainsStr(result.ir_dump, "AtomicAddU32 v52, v52, v1"), "image_atomic_add did not lower through shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMinU32 v53, v53, v1"), "image_atomic_umin did not lower through shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMaxU32 v57, v57, v1"), "image_atomic_umax did not lower through shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicAndU32 v54, v54, v1"), "image_atomic_and did not lower through shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicOrU32 v55, v55, v1"), "image_atomic_or did not lower through shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicXorU32 v56, v56, v1"), "image_atomic_xor did not lower through shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "storage_image_uint"), "image atomic IR did not use uint storage-image metadata"); Check(SpirvContainsOpcode(result.spirv, 60), "SPIR-V binary does not contain OpImageTexelPointer"); Check(SpirvContainsOpcode(result.spirv, 234), "SPIR-V binary does not contain OpAtomicIAdd"); Check(SpirvContainsOpcode(result.spirv, 237), "SPIR-V binary does not contain OpAtomicUMin"); Check(SpirvContainsOpcode(result.spirv, 239), "SPIR-V binary does not contain OpAtomicUMax"); Check(SpirvContainsOpcode(result.spirv, 240), "SPIR-V binary does not contain OpAtomicAnd"); Check(SpirvContainsOpcode(result.spirv, 241), "SPIR-V binary does not contain OpAtomicOr"); Check(SpirvContainsOpcode(result.spirv, 242), "SPIR-V binary does not contain OpAtomicXor"); Check(SpirvContainsOpcode(result.spirv, 225), "image atomic SPIR-V binary does not contain OpMemoryBarrier"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerVintrpLowering() { const uint32_t shader[] = { EncodeVintrp(0, 10, 1, 2, 4), // v_interp_p1_f32 v10, v4, attr1.z EncodeVintrp(1, 11, 1, 2, 4), // v_interp_p2_f32 v11, v4, attr1.z EncodeVintrp(2, 12, 0, 3, 2), // v_interp_mov_f32 v12, p0, attr0.w 0xbf810000u, }; ShaderPixelInputInfo ps_info {}; ps_info.input_num = 2; SetIdentityInterpolatorSettings(&ps_info); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.pixel_input_info = &ps_info; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "v_interp_p1_f32"), "new decoder did not decode VINTRP P1"); Check(Common::ContainsStr(result.decoded_dump, "v_interp_p2_f32"), "new decoder did not decode VINTRP P2"); Check(Common::ContainsStr(result.decoded_dump, "v_interp_mov_f32"), "new decoder did not decode VINTRP MOV"); Check(Common::ContainsStr(result.ir_dump, "ControlNop"), "VINTRP P1 did not lower to an explicit no-op marker"); Check(Common::ContainsStr(result.ir_dump, "LoadInputF32 v11"), "VINTRP P2 did not lower to input-load IR"); Check(Common::ContainsStr(result.ir_dump, "input_attr=1 input_chan=2"), "VINTRP P2 did not preserve attr/channel metadata"); Check(Common::ContainsStr(result.ir_dump, "LoadInputF32 v12"), "VINTRP MOV did not lower to input-load IR"); Check(Common::ContainsStr(result.ir_dump, "input_attr=0 input_chan=3"), "VINTRP MOV did not preserve attr/channel metadata"); Check(ProgramInputCount(result.program, ShaderRecompiler::IR::StageInputKind::Parameter) == 2, "VINTRP pixel shader did not reflect parameter inputs"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 81), "SPIR-V binary does not contain input component extraction"); Check(SpirvContainsOpcode(result.spirv, 124), "SPIR-V binary does not contain input bitcast"); CheckSpirvBinaryValidates(result.spirv); const uint32_t remapped_shader[] = { EncodeVintrp(1, 11, 2, 0, 4), // v_interp_p2_f32 v11, v4, attr2.x 0xbf810000u, }; ShaderPixelInputInfo remapped_ps_info {}; remapped_ps_info.input_num = 3; SetIdentityInterpolatorSettings(&remapped_ps_info); remapped_ps_info.interpolator_settings[2] = 3; options.pixel_input_info = &remapped_ps_info; ShaderRecompiler::CompileResult remapped_result; Check(ShaderRecompiler::TryRecompile(remapped_shader, options, remapped_result, &error), error.c_str()); Check(Common::ContainsStr(remapped_result.ir_dump, "input_attr=2 input_chan=0"), "remapped VINTRP did not preserve raw pixel attribute metadata"); Check(SpirvHasDecorationValue(remapped_result.spirv, 30u, 3u), "remapped VINTRP did not use SPI_PS_INPUT_CNTL input offset as Location"); Check(!SpirvHasDecorationValue(remapped_result.spirv, 30u, 2u), "remapped VINTRP still emitted the raw attribute as Location"); CheckSpirvBinaryValidates(remapped_result.spirv); const uint32_t duplicate_location_shader[] = { EncodeVintrp(0, 6, 1, 0, 4), // v_interp_p1_f32 v6, v4, attr1.x EncodeVintrp(0, 10, 0, 0, 4), // v_interp_p1_f32 v10, v4, attr0.x EncodeVintrp(1, 6, 1, 0, 4), // v_interp_p2_f32 v6, v4, attr1.x EncodeVintrp(1, 10, 0, 0, 4), // v_interp_p2_f32 v10, v4, attr0.x 0xbf810000u, }; ShaderPixelInputInfo duplicate_ps_info {}; duplicate_ps_info.input_num = 2; options.pixel_input_info = &duplicate_ps_info; ShaderRecompiler::CompileResult duplicate_result; Check(ShaderRecompiler::TryRecompile(duplicate_location_shader, options, duplicate_result, &error), error.c_str()); Check(ProgramInputCount(duplicate_result.program, ShaderRecompiler::IR::StageInputKind::Parameter) == 2, "duplicate-location VINTRP shader did not reflect both raw parameter attrs"); Check(SpirvDecorationValueCount(duplicate_result.spirv, 30u, 0u) == 1, "duplicate-location VINTRP emitted more than one Location 0 input"); Check(SpirvDecorationValueCount(duplicate_result.spirv, 30u, 1u) == 1, "duplicate-location VINTRP did not fall back attr1 to Location 1"); CheckSpirvBinaryValidates(duplicate_result.spirv); const uint32_t flat_shader[] = { EncodeVintrp(2, 12, 0, 0, 2), // v_interp_mov_f32 v12, p0, attr0.x 0xbf810000u, }; ShaderPixelInputInfo flat_ps_info {}; flat_ps_info.input_num = 1; SetIdentityInterpolatorSettings(&flat_ps_info); flat_ps_info.interpolator_settings[0] = 0x00000400u; options.pixel_input_info = &flat_ps_info; ShaderRecompiler::CompileResult flat_result; Check(ShaderRecompiler::TryRecompile(flat_shader, options, flat_result, &error), error.c_str()); Check(SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 14u), "flat VINTRP input did not emit a Flat decoration"); Check(!SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 13u), "flat VINTRP input should not also emit NoPerspective"); Check(SpirvHasDecorationValue(flat_result.spirv, 30u, 0u), "flat VINTRP input did not preserve Location 0"); CheckSpirvBinaryValidates(flat_result.spirv); ShaderPixelInputInfo no_perspective_ps_info {}; no_perspective_ps_info.input_num = 1; no_perspective_ps_info.ps_no_perspective = true; SetIdentityInterpolatorSettings(&no_perspective_ps_info); options.pixel_input_info = &no_perspective_ps_info; ShaderRecompiler::CompileResult no_perspective_result; Check(ShaderRecompiler::TryRecompile(flat_shader, options, no_perspective_result, &error), error.c_str()); Check(SpirvHasDecorationValueWithDecoration(no_perspective_result.spirv, 30u, 0u, 13u), "no-perspective VINTRP input did not emit a NoPerspective decoration"); Check(!SpirvHasDecorationValueWithDecoration(no_perspective_result.spirv, 30u, 0u, 14u), "non-flat no-perspective VINTRP input should not emit Flat"); CheckSpirvBinaryValidates(no_perspective_result.spirv); } void TestPsInputCountRegisterDecode() { HW::Context context; // NUM_INTERP is 3 while bit 14 is an independent control flag that must be preserved. context.SetPsInControl(0x00004003u); const auto ps_in_control = context.GetShaderRegisters().ps_in_control; Check(ps_in_control == 0x00004003u, "SPI_PS_IN_CONTROL flags were not preserved"); Check((ps_in_control & 0x3fu) == 3, "SPI_PS_IN_CONTROL NUM_INTERP decoding failed"); } void TestGraphicsCreateInterpolantMapping() { ShaderRegister regs[32] = {}; Check(Gen5::GraphicsCreateInterpolantMapping(regs, nullptr, nullptr) == 0, "null pixel shader interpolant mapping failed"); for (uint32_t i = 0; i < 32u; i++) { Check(regs[i].offset == Pm4::CX_PS_SHADER_USAGE_BASE + i, "identity interpolant register offset was unexpected"); Check(regs[i].value == i, "identity interpolant register value was unexpected"); } ShaderSemantic gs_semantics[3] = {}; gs_semantics[0].semantic = 7; gs_semantics[0].hardware_mapping = 5; gs_semantics[1].semantic = 9; gs_semantics[1].hardware_mapping = 12; gs_semantics[2].semantic = 10; gs_semantics[2].hardware_mapping = 4; gs_semantics[2].is_f16 = 1; Shader gs {}; gs.output_semantics = gs_semantics; gs.num_output_semantics = static_cast(std::size(gs_semantics)); ShaderSemantic ps_semantics[4] = {}; ps_semantics[0].semantic = 7; ps_semantics[1].semantic = 8; ps_semantics[1].default_value = 2; ps_semantics[2].semantic = 9; ps_semantics[2].is_flat_shaded = 1; ps_semantics[2].is_custom = 1; ps_semantics[2].default_value = 1; ps_semantics[3].semantic = 10; ps_semantics[3].is_f16 = 1; ps_semantics[3].default_value = 3; ps_semantics[3].default_value_hi = 2; Shader ps {}; ps.input_semantics = ps_semantics; ps.num_input_semantics = static_cast(std::size(ps_semantics)); Check(Gen5::GraphicsCreateInterpolantMapping(regs, &gs, &ps) == 0, "shader interpolant mapping failed"); Check(regs[0].value == 0x00000005u, "matched interpolant mapping did not use GS hardware mapping"); Check(regs[1].value == 0x00000220u, "missing interpolant mapping did not use PS default value"); Check(regs[2].value == 0x0000052cu, "flat/custom interpolant mapping bits were unexpected"); Check(regs[3].value == 0x01580304u, "f16 interpolant mapping bits were unexpected"); Check(regs[4].offset == Pm4::CX_PS_SHADER_USAGE_BASE + 4u && regs[4].value == 4u, "interpolant identity tail was not filled"); } void TestNewShaderRecompilerWideMemoryLowering() { const uint32_t shader[] = { EncodeSmem0(0x02, 4, 4), 0u, // s_load_dwordx4 s[4:7] EncodeSmem0(0x0a, 8, 4), 0u, // s_buffer_load_dwordx4 s[8:11] EncodeSmem0(0x09, 106, 4), 0u, // s_buffer_load_dwordx2 vcc_lo EncodeMubuf0(0x0d, 16), EncodeMubuf1(20, 0, 1), // buffer_load_dwordx2 v[20:21] EncodeMubuf0(0x0f, 32), EncodeMubuf1(24, 0, 1), // buffer_load_dwordx3 v[24:26] EncodeMubuf0(0x0e, 48), EncodeMubuf1(28, 0, 1), // buffer_load_dwordx4 v[28:31] EncodeMubuf0(0x1d, 64), EncodeMubuf1(32, 0, 1), // buffer_store_dwordx2 v[32:33] EncodeMubuf0(0x1f, 80), EncodeMubuf1(36, 0, 1), // buffer_store_dwordx3 v[36:38] EncodeMubuf0(0x1e, 96), EncodeMubuf1(40, 0, 1), // buffer_store_dwordx4 v[40:43] EncodeMubuf0(0x08, 2), EncodeMubuf1(44, 0, 1), // buffer_load_ubyte v44 EncodeMubuf0(0x0a, 4), EncodeMubuf1(45, 0, 1), // buffer_load_ushort v45 EncodeFlat0(0x0c, 0, 4), EncodeFlat1(50, 0x7d, 0, 1), // flat_load_dword EncodeFlat0(0x0d, 1, 8), EncodeFlat1(52, 0x7d, 0, 1), // scratch_load_dwordx2 EncodeFlat0(0x0e, 2, 12), EncodeFlat1(56, 0x7d, 0, 1), // global_load_dwordx4 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.flat_memory_base = 0; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_load_dwordx4"), "new decoder did not decode SMEM x4 load"); Check(Common::ContainsStr(result.decoded_dump, "s_buffer_load_dwordx4"), "new decoder did not decode scalar-buffer x4 load"); Check(Common::ContainsStr(result.decoded_dump, "s_buffer_load_dwordx2 vcc_lo"), "new decoder did not decode scalar-buffer x2 load into VCC"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_dwordx2"), "new decoder did not decode buffer x2 load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_dwordx3"), "new decoder did not decode buffer x3 load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_dwordx4"), "new decoder did not decode buffer x4 load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_dwordx4"), "new decoder did not decode buffer x4 store"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_ubyte"), "new decoder did not decode buffer ubyte load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_ushort"), "new decoder did not decode buffer ushort load"); Check(Common::ContainsStr(result.decoded_dump, "flat_load_dword"), "new decoder did not decode flat dword load"); Check(Common::ContainsStr(result.decoded_dump, "segment=1"), "new decoder did not preserve scratch segment metadata"); Check(Common::ContainsStr(result.decoded_dump, "segment=2"), "new decoder did not preserve global segment metadata"); Check(Common::ContainsStr(result.decoded_dump, "dwords=4 bits=32"), "new decoder did not expose width metadata for wide memory ops"); Check(Common::ContainsStr(result.ir_dump, "SLoadDword s4"), "s_load_dwordx4 did not expand to first scalar dword IR load"); Check(Common::ContainsStr(result.ir_dump, "SLoadDword s7"), "s_load_dwordx4 did not expand to last scalar dword IR load"); Check(Common::ContainsStr(result.ir_dump, "SBufferLoadDword s8"), "s_buffer_load_dwordx4 did not expand to first scalar-buffer IR load"); Check(Common::ContainsStr(result.ir_dump, "SBufferLoadDword s11"), "s_buffer_load_dwordx4 did not expand to last scalar-buffer IR load"); Check(Common::ContainsStr(result.ir_dump, "SBufferLoadDword vcc_lo"), "s_buffer_load_dwordx2 did not expand to VCC low dword"); Check(Common::ContainsStr(result.ir_dump, "SBufferLoadDword vcc_hi"), "s_buffer_load_dwordx2 did not expand to VCC high dword"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadDword v20"), "buffer_load_dwordx2 did not expand to dword IR loads"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadDword v31"), "buffer_load_dwordx4 did not expand to the last dword IR load"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreDword null, v40"), "buffer_store_dwordx4 did not expand to first dword IR store"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreDword null, v43"), "buffer_store_dwordx4 did not expand to last dword IR store"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadUbyte v44"), "buffer_load_ubyte did not lower to sub-dword IR load"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadUshort v45"), "buffer_load_ushort did not lower to sub-dword IR load"); Check(Common::ContainsStr(result.ir_dump, "FlatLoadDword v50"), "flat_load_dword did not lower to flat dword IR load"); Check(Common::ContainsStr(result.ir_dump, "scratch"), "scratch segment load did not lower with scratch metadata"); Check(Common::ContainsStr(result.ir_dump, "global"), "global segment load did not lower with global metadata"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 194), "SPIR-V binary does not contain OpShiftRightLogical"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerBufferSignedLoadLowering() { const uint32_t shader[] = { EncodeMubuf0(0x09, 3), EncodeMubuf1(46, 0, 1), // buffer_load_sbyte EncodeMubuf0(0x0b, 6), EncodeMubuf1(47, 0, 1), // buffer_load_sshort 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_sbyte"), "new decoder did not decode buffer signed byte load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_sshort"), "new decoder did not decode buffer signed short load"); Check(Common::ContainsStr(result.decoded_dump, "bits=8"), "signed byte buffer load did not preserve bit width metadata"); Check(Common::ContainsStr(result.decoded_dump, "bits=16"), "signed short buffer load did not preserve bit width metadata"); Check(Common::ContainsStr(result.decoded_dump, "signed=1"), "signed buffer loads did not preserve signed metadata"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadSbyte v46"), "buffer_load_sbyte did not lower to signed byte IR load"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadSshort v47"), "buffer_load_sshort did not lower to signed short IR load"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 195), "SPIR-V binary does not contain OpShiftRightArithmetic for sign extension"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical for sign extension"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerBufferSubDwordStoreLowering() { const uint32_t shader[] = { EncodeMubuf0(0x18, 2), EncodeMubuf1(48, 0, 1), // buffer_store_byte EncodeMubuf0(0x1a, 4), EncodeMubuf1(49, 0, 1), // buffer_store_short 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_byte"), "new decoder did not decode buffer byte store"); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_short"), "new decoder did not decode buffer short store"); Check(Common::ContainsStr(result.decoded_dump, "bits=8"), "buffer byte store did not preserve bit width metadata"); Check(Common::ContainsStr(result.decoded_dump, "bits=16"), "buffer short store did not preserve bit width metadata"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreByte null, v48"), "buffer_store_byte did not lower to byte store IR"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreShort null, v49"), "buffer_store_short did not lower to short store IR"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad for sub-dword store RMW"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore for sub-dword store RMW"); Check(SpirvContainsOpcode(result.spirv, 197), "SPIR-V binary does not contain OpBitwiseOr"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); Check(SpirvContainsOpcode(result.spirv, 200), "SPIR-V binary does not contain OpNot"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerMubufFormatLowering() { const uint32_t shader[] = { EncodeMubuf0(0x00, 4), EncodeMubuf1(84, 0, 1), // buffer_load_format_x EncodeMubuf0(0x01, 8), EncodeMubuf1(88, 0, 1), // buffer_load_format_xy EncodeMubuf0(0x02, 12), EncodeMubuf1(92, 0, 1), // buffer_load_format_xyz EncodeMubuf0(0x03, 16), EncodeMubuf1(96, 0, 1), // buffer_load_format_xyzw EncodeMubuf0(0x04, 20), EncodeMubuf1(100, 0, 1), // buffer_store_format_x EncodeMubuf0(0x05, 24), EncodeMubuf1(104, 0, 1), // buffer_store_format_xy EncodeMubuf0(0x06, 28), EncodeMubuf1(108, 0, 1), // buffer_store_format_xyz EncodeMubuf0(0x07, 32), EncodeMubuf1(112, 0, 1), // buffer_store_format_xyzw 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_format_x"), "new decoder did not decode MUBUF format-x load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_format_xyzw"), "new decoder did not decode MUBUF format-xyzw load"); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_format_x"), "new decoder did not decode MUBUF format-x store"); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_format_xyzw"), "new decoder did not decode MUBUF format-xyzw store"); Check(Common::ContainsStr(result.decoded_dump, "typed=0 formatted=1"), "MUBUF format decode did not preserve formatted non-typed metadata"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadDword v84"), "MUBUF format-x load did not lower through shared buffer load IR"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadDword v99"), "MUBUF format-xyzw load did not expand to the last shared dword load"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreDword null, v100"), "MUBUF format-x store did not lower through shared buffer store IR"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreDword null, v115"), "MUBUF format-xyzw store did not expand to the last shared dword store"); Check(Common::ContainsStr(result.ir_dump, "typed=0 formatted=1"), "MUBUF format metadata did not survive into IR"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerFormattedStoreUsesRuntimeArrayLengthOnly() { const uint32_t shader[] = { EncodeMubuf0(0x04), EncodeMubuf1(0, 0, 1), // buffer_store_format_x 0xbf810000u, }; std::array user_data {}; user_data[1] = 4u << 16u; user_data[2] = 5u; user_data[3] = Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt) << 12u; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(result.resources.buffers.size() == 1 && result.resources.buffers[0].dwords[2] == 5u, "formatted store test did not preserve descriptor NumRecords"); Check(Common::ContainsStr(result.decoded_dump, "buffer_store_format_x"), "formatted store regression did not decode buffer_store_format_x"); Check(Common::ContainsStr(result.ir_dump, "typed=0 formatted=1"), "formatted store regression did not preserve formatted metadata"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(Common::ContainsStr(source, "OpArrayLength"), "formatted store SPIR-V lacks runtime storage-buffer bounds check"); Check(!SpirvSourceHasInstructionUsing(source, "OpULessThan", "%uint_5"), "formatted store SPIR-V baked descriptor NumRecords into a store guard"); } void TestNewShaderRecompilerTypedBufferLowering() { const uint32_t shader[] = { EncodeMtbuf0(0x00, 14, 7, 4), EncodeMtbuf1(0x00, 60, 0, 1), // tbuffer_load_format_x EncodeMtbuf0(0x01, 13, 7, 8), EncodeMtbuf1(0x01, 61, 0, 1), // tbuffer_load_format_xy EncodeMtbuf0(0x02, 13, 4, 12), EncodeMtbuf1(0x02, 64, 0, 1), // tbuffer_load_format_xyz EncodeMtbuf0(0x03, 14, 7, 16, true, true), EncodeMtbuf1(0x03, 68, 0, 1), // tbuffer_load_format_xyzw EncodeMtbuf0(0x04, 14, 7, 20), EncodeMtbuf1(0x04, 72, 0, 1), // tbuffer_store_format_x EncodeMtbuf0(0x05, 13, 7, 24), EncodeMtbuf1(0x05, 76, 0, 1), // tbuffer_store_format_xy EncodeMtbuf0(0x07, 14, 7, 28), EncodeMtbuf1(0x07, 80, 0, 1), // tbuffer_store_format_xyzw 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "tbuffer_load_format_x"), "new decoder did not decode typed buffer format-x load"); Check(Common::ContainsStr(result.decoded_dump, "tbuffer_load_format_xy"), "new decoder did not decode typed buffer format-xy load"); Check(Common::ContainsStr(result.decoded_dump, "tbuffer_load_format_xyz"), "new decoder did not decode typed buffer format-xyz load"); Check(Common::ContainsStr(result.decoded_dump, "tbuffer_load_format_xyzw"), "new decoder did not decode typed buffer format-xyzw load"); Check(Common::ContainsStr(result.decoded_dump, "tbuffer_store_format_x"), "new decoder did not decode typed buffer format-x store"); Check(Common::ContainsStr(result.decoded_dump, "tbuffer_store_format_xy"), "new decoder did not decode typed buffer format-xy store"); Check(Common::ContainsStr(result.decoded_dump, "tbuffer_store_format_xyzw"), "new decoder did not decode typed buffer format-xyzw store"); Check(Common::ContainsStr(result.decoded_dump, "dfmt=14 nfmt=7"), "MTBUF decode did not expose dfmt/nfmt metadata"); Check(Common::ContainsStr(result.decoded_dump, "typed=1"), "MTBUF decode did not preserve typed metadata"); Check(Common::ContainsStr(result.decoded_dump, "offen=1"), "MTBUF decode did not preserve offen metadata"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadDword v60"), "typed buffer x load did not lower through shared buffer load IR"); Check(Common::ContainsStr(result.ir_dump, "BufferLoadDword v71"), "typed buffer xyzw load did not expand to last shared dword load"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreDword null, v72"), "typed buffer x store did not lower through shared buffer store IR"); Check(Common::ContainsStr(result.ir_dump, "BufferStoreDword null, v83"), "typed buffer xyzw store did not expand to last shared dword store"); Check(Common::ContainsStr(result.ir_dump, "typed=1"), "typed buffer metadata did not survive into IR"); Check(Common::ContainsStr(result.ir_dump, "dfmt=13 nfmt=7"), "typed buffer format metadata did not survive into IR"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerFlatOldBackedLowering() { const uint32_t shader[] = { EncodeFlat0(0x08, 0, 4), EncodeFlat1(9, 0x7d, 0, 1), // flat_load_ubyte 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.flat_memory_base = 0; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "flat_load_ubyte"), "new decoder did not decode old-backed FLAT ubyte load"); Check(Common::ContainsStr(result.ir_dump, "FlatLoadUbyte v9"), "old-backed FLAT ubyte load did not lower to IR"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 194), "SPIR-V binary does not contain OpShiftRightLogical"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerUnbasedFlatRequiresTranslator() { const uint32_t shader[] = { EncodeFlat0(0x0c, 0, 0), EncodeFlat1(0, 0x7d, 0, 1), 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; ShaderRecompiler::CompileResult result; std::string error; Check(!ShaderRecompiler::TryRecompile(shader, options, result, &error) && Common::ContainsStr(error, "requires runtime guest-address translation"), "unbased FLAT compiled without an explicit translator"); } void TestNewShaderRecompilerFlatSignedLoadLowering() { const uint32_t shader[] = { EncodeFlat0(0x09, 0, 4), EncodeFlat1(10, 0x7d, 0, 1), // flat_load_sbyte EncodeFlat0(0x0b, 1, 8), EncodeFlat1(11, 0x7d, 0, 1), // scratch_load_sshort EncodeFlat0(0x09, 2, 12), EncodeFlat1(12, 0x7d, 0, 1), // global_load_sbyte 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.flat_memory_base = 0; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "flat_load_sbyte"), "new decoder did not decode flat signed byte load"); Check(Common::ContainsStr(result.decoded_dump, "flat_load_sshort"), "new decoder did not decode flat signed short load"); Check(Common::ContainsStr(result.decoded_dump, "bits=8"), "signed byte flat load did not preserve bit width metadata"); Check(Common::ContainsStr(result.decoded_dump, "bits=16"), "signed short flat load did not preserve bit width metadata"); Check(Common::ContainsStr(result.decoded_dump, "signed=1"), "signed flat loads did not preserve signed metadata"); Check(Common::ContainsStr(result.ir_dump, "FlatLoadSbyte v10"), "flat_load_sbyte did not lower to signed byte IR load"); Check(Common::ContainsStr(result.ir_dump, "FlatLoadSshort v11"), "flat_load_sshort did not lower to signed short IR load"); Check(Common::ContainsStr(result.ir_dump, "scratch"), "signed scratch load did not preserve scratch metadata"); Check(Common::ContainsStr(result.ir_dump, "global"), "signed global load did not preserve global metadata"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 195), "SPIR-V binary does not contain OpShiftRightArithmetic for sign extension"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical for sign extension"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerFlatStoreLowering() { const uint32_t shader[] = { EncodeFlat0(0x18, 0, 2), EncodeFlat1(0, 0x7d, 72, 1), // flat_store_byte EncodeFlat0(0x1a, 1, 6), EncodeFlat1(0, 0x7d, 73, 1), // scratch_store_short EncodeFlat0(0x1c, 0, 4), EncodeFlat1(0, 0x7d, 60, 1), // flat_store_dword EncodeFlat0(0x1d, 1, 8), EncodeFlat1(0, 0x7d, 61, 1), // scratch_store_dwordx2 EncodeFlat0(0x1f, 2, 12), EncodeFlat1(0, 0x7d, 64, 1), // global_store_dwordx3 EncodeFlat0(0x1e, 2, 16), EncodeFlat1(0, 0x7d, 68, 1), // global_store_dwordx4 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.flat_memory_base = 0; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "flat_store_byte"), "new decoder did not decode flat byte store"); Check(Common::ContainsStr(result.decoded_dump, "flat_store_short"), "new decoder did not decode flat short store"); Check(Common::ContainsStr(result.decoded_dump, "flat_store_dword"), "new decoder did not decode flat dword store"); Check(Common::ContainsStr(result.decoded_dump, "flat_store_dwordx2"), "new decoder did not decode flat dwordx2 store"); Check(Common::ContainsStr(result.decoded_dump, "flat_store_dwordx3"), "new decoder did not decode flat dwordx3 store"); Check(Common::ContainsStr(result.decoded_dump, "flat_store_dwordx4"), "new decoder did not decode flat dwordx4 store"); Check(Common::ContainsStr(result.decoded_dump, "segment=1"), "flat store decode did not preserve scratch segment metadata"); Check(Common::ContainsStr(result.decoded_dump, "segment=2"), "flat store decode did not preserve global segment metadata"); Check(Common::ContainsStr(result.decoded_dump, "dwords=1 bits=8"), "flat byte store decode did not expose byte-width metadata"); Check(Common::ContainsStr(result.decoded_dump, "dwords=1 bits=16"), "flat short store decode did not expose short-width metadata"); Check(Common::ContainsStr(result.decoded_dump, "dwords=4 bits=32"), "flat store decode did not expose wide width metadata"); Check(Common::ContainsStr(result.ir_dump, "FlatStoreByte null, v72"), "flat_store_byte did not lower through shared flat store IR"); Check(Common::ContainsStr(result.ir_dump, "FlatStoreShort null, v73"), "flat_store_short did not lower through shared flat store IR"); Check(Common::ContainsStr(result.ir_dump, "FlatStoreDword null, v60"), "flat_store_dword did not lower through shared flat store IR"); Check(Common::ContainsStr(result.ir_dump, "FlatStoreDword null, v62"), "scratch_store_dwordx2 did not expand to the last dword store"); Check(Common::ContainsStr(result.ir_dump, "FlatStoreDword null, v66"), "global_store_dwordx3 did not expand to the last dword store"); Check(Common::ContainsStr(result.ir_dump, "FlatStoreDword null, v71"), "global_store_dwordx4 did not expand to the last dword store"); Check(Common::ContainsStr(result.ir_dump, "flat"), "flat store IR did not preserve flat resource metadata"); Check(Common::ContainsStr(result.ir_dump, "scratch"), "flat store IR did not preserve scratch resource metadata"); Check(Common::ContainsStr(result.ir_dump, "global"), "flat store IR did not preserve global resource metadata"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad for sub-dword flat store merge"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 197), "SPIR-V binary does not contain OpBitwiseOr for sub-dword flat store merge"); Check(SpirvContainsOpcode(result.spirv, 200), "SPIR-V binary does not contain OpNot for sub-dword flat store mask"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerAtomicLowering() { const uint32_t shader[] = { EncodeMubuf0(0x30, 4, true, true), EncodeMubuf1(0, 0, 1), // buffer_atomic_swap EncodeMubuf0(0x32, 8, true, true), EncodeMubuf1(1, 0, 1), // buffer_atomic_add EncodeMubuf0(0x33, 12, true, true), EncodeMubuf1(7, 0, 1), // buffer_atomic_sub EncodeMubuf0(0x35, 16, true, true), EncodeMubuf1(8, 0, 1), // buffer_atomic_smin EncodeMubuf0(0x36, 20, true, true), EncodeMubuf1(2, 0, 1), // buffer_atomic_umin EncodeMubuf0(0x37, 24, true, true), EncodeMubuf1(9, 0, 1), // buffer_atomic_smax EncodeMubuf0(0x38, 28, true, true), EncodeMubuf1(3, 0, 1), // buffer_atomic_umax EncodeMubuf0(0x39, 32, true, true), EncodeMubuf1(4, 0, 1), // buffer_atomic_and EncodeMubuf0(0x3a, 36, true, true), EncodeMubuf1(5, 0, 1), // buffer_atomic_or EncodeMubuf0(0x3b, 40, true, true), EncodeMubuf1(6, 0, 1), // buffer_atomic_xor EncodeDs0(0x00), EncodeDs1(0, 2, 1), // ds_add_u32 EncodeDs0(0x01), EncodeDs1(0, 10, 1), // ds_sub_u32 EncodeDs0(0x05), EncodeDs1(0, 11, 1), // ds_min_i32 EncodeDs0(0x06), EncodeDs1(0, 12, 1), // ds_max_i32 EncodeDs0(0x09), EncodeDs1(0, 13, 1), // ds_and_b32 EncodeDs0(0x0b), EncodeDs1(0, 14, 1), // ds_xor_b32 EncodeDs0(0x20), EncodeDs1(15, 2, 1), // ds_add_rtn_u32 EncodeDs0(0x21), EncodeDs1(16, 10, 1), // ds_sub_rtn_u32 EncodeDs0(0x25), EncodeDs1(17, 11, 1), // ds_min_rtn_i32 EncodeDs0(0x26), EncodeDs1(18, 12, 1), // ds_max_rtn_i32 EncodeDs0(0x27), EncodeDs1(19, 2, 1), // ds_min_rtn_u32 EncodeDs0(0x28), EncodeDs1(20, 2, 1), // ds_max_rtn_u32 EncodeDs0(0x29), EncodeDs1(21, 13, 1), // ds_and_rtn_b32 EncodeDs0(0x2a), EncodeDs1(22, 2, 1), // ds_or_rtn_b32 EncodeDs0(0x2b), EncodeDs1(23, 14, 1), // ds_xor_rtn_b32 EncodeDs0(0x2d), EncodeDs1(24, 15, 1), // ds_wrxchg_rtn_b32 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_swap"), "new decoder did not decode buffer atomic swap"); Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_add"), "new decoder did not decode buffer atomic add"); Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_sub"), "new decoder did not decode buffer atomic sub"); Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_smin"), "new decoder did not decode buffer atomic signed min"); Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_smax"), "new decoder did not decode buffer atomic signed max"); Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_xor"), "new decoder did not decode buffer atomic xor"); Check(Common::ContainsStr(result.decoded_dump, "ds_add_u32"), "new decoder did not decode DS atomic add"); Check(Common::ContainsStr(result.decoded_dump, "ds_sub_u32"), "new decoder did not decode DS atomic sub"); Check(Common::ContainsStr(result.decoded_dump, "ds_min_i32"), "new decoder did not decode DS signed min"); Check(Common::ContainsStr(result.decoded_dump, "ds_xor_rtn_b32"), "new decoder did not decode DS xor-return"); Check(Common::ContainsStr(result.decoded_dump, "ds_wrxchg_rtn_b32"), "new decoder did not decode DS write-exchange-return"); Check(Common::ContainsStr(result.decoded_dump, "ds_add_rtn_u32"), "new decoder did not decode DS atomic add-return"); Check(Common::ContainsStr(result.ir_dump, "AtomicSwapU32 v0"), "buffer atomic swap did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicAddU32 v1"), "buffer atomic add did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSubU32 v7"), "buffer atomic sub did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSMinI32 v8"), "buffer atomic signed min did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMinU32 v2"), "buffer atomic umin did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSMaxI32 v9"), "buffer atomic signed max did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMaxU32 v3"), "buffer atomic umax did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicAndU32 v4"), "buffer atomic and did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicOrU32 v5"), "buffer atomic or did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicXorU32 v6"), "buffer atomic xor did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicAddU32 null, v2"), "DS no-return atomic add did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSubU32 null, v10"), "DS no-return atomic sub did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSMinI32 null, v11"), "DS no-return signed min did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSMaxI32 null, v12"), "DS no-return signed max did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicAndU32 null, v13"), "DS no-return and did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicXorU32 null, v14"), "DS no-return xor did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicAddU32 v15"), "DS add-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSubU32 v16"), "DS sub-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSMinI32 v17"), "DS signed min-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSMaxI32 v18"), "DS signed max-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMinU32 v19"), "DS unsigned min-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMaxU32 v20"), "DS unsigned max-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicAndU32 v21"), "DS and-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicOrU32 v22"), "DS or-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicXorU32 v23"), "DS xor-return did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicSwapU32 v24"), "DS write-exchange-return did not lower to shared atomic swap IR"); Check(SpirvContainsOpcode(result.spirv, 229), "SPIR-V binary does not contain OpAtomicExchange"); Check(SpirvContainsOpcode(result.spirv, 234), "SPIR-V binary does not contain OpAtomicIAdd"); Check(SpirvContainsOpcode(result.spirv, 235), "SPIR-V binary does not contain OpAtomicISub"); Check(SpirvContainsOpcode(result.spirv, 236), "SPIR-V binary does not contain OpAtomicSMin"); Check(SpirvContainsOpcode(result.spirv, 237), "SPIR-V binary does not contain OpAtomicUMin"); Check(SpirvContainsOpcode(result.spirv, 238), "SPIR-V binary does not contain OpAtomicSMax"); Check(SpirvContainsOpcode(result.spirv, 239), "SPIR-V binary does not contain OpAtomicUMax"); Check(SpirvContainsOpcode(result.spirv, 240), "SPIR-V binary does not contain OpAtomicAnd"); Check(SpirvContainsOpcode(result.spirv, 241), "SPIR-V binary does not contain OpAtomicOr"); Check(SpirvContainsOpcode(result.spirv, 242), "SPIR-V binary does not contain OpAtomicXor"); Check(SpirvContainsOpcode(result.spirv, 225), "buffer atomic SPIR-V binary does not contain OpMemoryBarrier"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerDsReadWrite2Lowering() { const uint32_t shader[] = { EncodeDs0(0x0e, (3u << 8u) | 1u), EncodeDs1Ex(0, 61, 60, 1), EncodeDs0(0x37, (4u << 8u) | 2u), EncodeDs1Ex(70, 0, 0, 1), EncodeDs0(0x38, (5u << 8u) | 1u), EncodeDs1Ex(72, 0, 0, 1), EncodeDs0(0x77, (6u << 8u) | 2u), EncodeDs1Ex(80, 0, 0, 1), 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "ds_write2_b32"), "new decoder did not decode old-backed DS write2"); Check(Common::ContainsStr(result.decoded_dump, "ds_read2_b32"), "new decoder did not decode old-backed DS read2"); Check(Common::ContainsStr(result.decoded_dump, "ds_read2_b64"), "new decoder did not decode old-backed DS read2 b64"); Check(Common::ContainsStr(result.decoded_dump, "offset=4"), "DS write2 decode did not scale offset0 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "offset2=12"), "DS write2 decode did not scale offset1 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "offset=8"), "DS read2 decode did not scale offset0 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "offset2=16"), "DS read2 decode did not scale offset1 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "offset=256"), "DS read2 st64 decode did not scale offset0 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "offset2=1280"), "DS read2 st64 decode did not scale offset1 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "offset=16"), "DS read2 b64 decode did not scale offset0 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "offset2=48"), "DS read2 b64 decode did not scale offset1 to bytes"); Check(Common::ContainsStr(result.decoded_dump, "dwords=2 bits=32"), "DS read/write2 decode did not preserve two-dword metadata"); Check(Common::ContainsStr(result.decoded_dump, "dwords=4 bits=32"), "DS read2 b64 decode did not preserve four-dword metadata"); Check(Common::ContainsStr(result.ir_dump, "DsWriteB32 null, v60"), "DS write2 did not lower first dword through shared LDS store IR"); Check(Common::ContainsStr(result.ir_dump, "DsWriteB32 null, v61"), "DS write2 did not lower second dword through shared LDS store IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v70"), "DS read2 did not lower first dword through shared LDS load IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v71"), "DS read2 did not lower second dword through shared LDS load IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v72"), "DS read2 st64 did not lower first dword through shared LDS load IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v73"), "DS read2 st64 did not lower second dword through shared LDS load IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v80"), "DS read2 b64 did not lower first dword through shared LDS load IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v83"), "DS read2 b64 did not lower fourth dword through shared LDS load IR"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerDsSubDwordLowering() { const uint32_t shader[] = { EncodeDs0(0x1e, 1), EncodeDs1(0, 40, 1), // ds_write_b8 v40, v1 EncodeDs0(0x1f, 2), EncodeDs1(0, 41, 1), // ds_write_b16 v41, v1 EncodeDs0(0x39, 3), EncodeDs1(42, 0, 1), // ds_read_i8 v42, v1 EncodeDs0(0x3a, 4), EncodeDs1(43, 0, 1), // ds_read_u8 v43, v1 EncodeDs0(0x3b, 6), EncodeDs1(44, 0, 1), // ds_read_i16 v44, v1 EncodeDs0(0x3c, 8), EncodeDs1(45, 0, 1), // ds_read_u16 v45, v1 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "ds_write_b8"), "new decoder did not decode DS byte write"); Check(Common::ContainsStr(result.decoded_dump, "ds_write_b16"), "new decoder did not decode DS short write"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_i8"), "new decoder did not decode DS signed byte read"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_u8"), "new decoder did not decode DS unsigned byte read"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_i16"), "new decoder did not decode DS signed short read"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_u16"), "new decoder did not decode DS unsigned short read"); Check(Common::ContainsStr(result.decoded_dump, "dwords=1 bits=8"), "DS byte decode did not preserve byte-width metadata"); Check(Common::ContainsStr(result.decoded_dump, "dwords=1 bits=16"), "DS short decode did not preserve short-width metadata"); Check(Common::ContainsStr(result.ir_dump, "DsWriteByte null, v40"), "DS byte write did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsWriteShort null, v41"), "DS short write did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadSbyte v42"), "DS signed byte read did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadUbyte v43"), "DS unsigned byte read did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadSshort v44"), "DS signed short read did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadUshort v45"), "DS unsigned short read did not lower to explicit IR"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 194), "SPIR-V binary does not contain OpShiftRightLogical"); Check(SpirvContainsOpcode(result.spirv, 195), "SPIR-V binary does not contain OpShiftRightArithmetic"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); Check(SpirvContainsOpcode(result.spirv, 200), "SPIR-V binary does not contain OpNot"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerDsWideAndAtomicLowering() { const uint32_t shader[] = { EncodeDs0(0x4d, 4), EncodeDs1(0, 10, 1), // ds_write_b64 v[10:11], v1 EncodeDs0(0xde, 8), EncodeDs1(0, 12, 1), // ds_write_b96 v[12:14], v1 EncodeDs0(0xdf, 12), EncodeDs1(0, 16, 1), // ds_write_b128 v[16:19], v1 EncodeDs0(0x76, 16), EncodeDs1(20, 0, 1), // ds_read_b64 v[20:21], v1 EncodeDs0(0xfe, 20), EncodeDs1(24, 0, 1), // ds_read_b96 v[24:26], v1 EncodeDs0(0xff, 24), EncodeDs1(28, 0, 1), // ds_read_b128 v[28:31], v1 EncodeDs0(0x07, 28), EncodeDs1(0, 32, 1), // ds_min_u32 v32, v1 EncodeDs0(0x08, 32), EncodeDs1(0, 33, 1), // ds_max_u32 v33, v1 EncodeDs0(0x0a, 36), EncodeDs1(0, 34, 1), // ds_or_b32 v34, v1 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "ds_write_b64"), "new decoder did not decode DS b64 write"); Check(Common::ContainsStr(result.decoded_dump, "ds_write_b96"), "new decoder did not decode DS b96 write"); Check(Common::ContainsStr(result.decoded_dump, "ds_write_b128"), "new decoder did not decode DS b128 write"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_b64"), "new decoder did not decode DS b64 read"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_b96"), "new decoder did not decode DS b96 read"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_b128"), "new decoder did not decode DS b128 read"); Check(Common::ContainsStr(result.decoded_dump, "ds_min_u32"), "new decoder did not decode DS min atomic"); Check(Common::ContainsStr(result.decoded_dump, "ds_max_u32"), "new decoder did not decode DS max atomic"); Check(Common::ContainsStr(result.decoded_dump, "ds_or_b32"), "new decoder did not decode DS or atomic"); Check(Common::ContainsStr(result.decoded_dump, "dwords=4 bits=32"), "new decoder did not expose DS wide width metadata"); Check(Common::ContainsStr(result.ir_dump, "DsWriteB32 null, v10"), "DS b64 write did not expand to first dword store"); Check(Common::ContainsStr(result.ir_dump, "DsWriteB32 null, v19"), "DS b128 write did not expand to last dword store"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v20"), "DS b64 read did not expand to first dword load"); Check(Common::ContainsStr(result.ir_dump, "DsReadB32 v31"), "DS b128 read did not expand to last dword load"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMinU32 null, v32"), "DS min atomic did not lower to shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicUMaxU32 null, v33"), "DS max atomic did not lower to shared atomic IR"); Check(Common::ContainsStr(result.ir_dump, "AtomicOrU32 null, v34"), "DS or atomic did not lower to shared atomic IR"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 237), "SPIR-V binary does not contain OpAtomicUMin"); Check(SpirvContainsOpcode(result.spirv, 239), "SPIR-V binary does not contain OpAtomicUMax"); Check(SpirvContainsOpcode(result.spirv, 241), "SPIR-V binary does not contain OpAtomicOr"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerDsSwizzleLowering() { const uint32_t shader[] = { EncodeDs0(0x35, 0x001f), EncodeDs1(8, 0, 5), // ds_swizzle_b32 v8, v5 EncodeDs0(0x35, 0x801b), EncodeDs1(9, 0, 6), // ds_swizzle_b32 v9, v6 0xd8d4c480u, 0x45000045u, // ds_swizzle_b32 rotate mode from boot shader 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "ds_swizzle_b32"), "new decoder did not decode DS swizzle"); Check(Common::ContainsStr(result.decoded_dump, "offset=31"), "new decoder did not expose DS swizzle control"); Check(Common::ContainsStr(result.decoded_dump, "offset=32795"), "new decoder did not expose DS swizzle quad control"); Check(Common::ContainsStr(result.decoded_dump, "offset=50304"), "new decoder did not expose DS swizzle rotate control"); Check(Common::ContainsStr(result.ir_dump, "DsSwizzleB32 v8, v5, 0x0000001f"), "DS swizzle did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsSwizzleB32 v9, v6, 0x0000801b"), "DS quad swizzle did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsSwizzleB32 v69, v69, 0x0000c480"), "DS rotate swizzle did not lower to explicit IR"); Check(SpirvContainsOpcode(result.spirv, 345), "SPIR-V binary does not contain OpGroupNonUniformShuffle"); Check(SpirvContainsOpcode(result.spirv, 128), "SPIR-V binary does not contain OpIAdd for DS rotate swizzle"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical"); Check(SpirvContainsOpcode(result.spirv, 198), "SPIR-V binary does not contain OpBitwiseOr"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerDsAddtidLowering() { const uint32_t shader[] = { EncodeSMovB32(124, 132), // m0 = 4 EncodeDs0(0xb0, 8), EncodeDs1(0, 7, 0), // ds_write_addtid_b32 v7 EncodeDs0(0xb1, 12), EncodeDs1(8, 0, 0), // ds_read_addtid_b32 v8 0xbf810000u, }; ShaderComputeInputInfo input_info = RegressionComputeInputInfo(); input_info.thread_ids_num = 1; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.compute_input_info = &input_info; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "ds_write_addtid_b32"), "new decoder did not decode DS write addtid"); Check(Common::ContainsStr(result.decoded_dump, "ds_read_addtid_b32"), "new decoder did not decode DS read addtid"); Check(Common::ContainsStr(result.ir_dump, "DsWriteAddtidB32 null, v7, m0"), "DS write addtid did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsReadAddtidB32 v8, m0"), "DS read addtid did not lower to explicit IR"); Check( ProgramHasInput(result.program, ShaderRecompiler::IR::StageInputKind::LocalInvocationIndex), "DS addtid did not request LocalInvocationIndex input"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 128), "SPIR-V binary does not contain OpIAdd"); Check(SpirvContainsOpcode(result.spirv, 196), "SPIR-V binary does not contain OpShiftLeftLogical"); Check(SpirvContainsOpcode(result.spirv, 199), "SPIR-V binary does not contain OpBitwiseAnd"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerDsFloatMinMaxLowering() { const uint32_t shader[] = { EncodeDs0(0x12, 4), EncodeDs1Ex(0, 9, 7, 1), // ds_min_f32 v7, v9, v1 EncodeDs0(0x13, 8), EncodeDs1Ex(0, 10, 8, 1), // ds_max_f32 v8, v10, v1 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "ds_min_f32"), "new decoder did not decode DS float min"); Check(Common::ContainsStr(result.decoded_dump, "ds_max_f32"), "new decoder did not decode DS float max"); Check(Common::ContainsStr(result.ir_dump, "DsMinF32 null, v7, v1"), "DS float min did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "DsMaxF32 null, v8, v1"), "DS float max did not lower to explicit IR"); Check(Common::ContainsStr(result.ir_dump, "v9"), "DS float min did not retain DATA1 compare operand"); Check(Common::ContainsStr(result.ir_dump, "v10"), "DS float max did not retain DATA1 compare operand"); Check(SpirvContainsOpcode(result.spirv, 12), "SPIR-V binary does not contain OpExtInst"); Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 65), "SPIR-V binary does not contain OpAccessChain"); Check(SpirvContainsOpcode(result.spirv, 124), "SPIR-V binary does not contain OpBitcast"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgStraightLine() { const uint32_t shader[] = { EncodeSMovB32(0, 129), 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "CFG:"), "CFG dump was not emitted"); Check(Common::ContainsStr(result.ir_dump, "block_0"), "straight-line CFG block missing"); Check(Common::ContainsStr(result.ir_dump, "successors=["), "CFG successors were not dumped"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgIfElse() { const uint32_t shader[] = { EncodeSopc(0x06, 0, 0), // s_cmp_eq_u32 s0, s0 EncodeSopp(0x05, 2), // s_cbranch_scc1 else EncodeSMovB32(1, 129), // then EncodeSopp(0x02, 1), // s_branch merge EncodeSMovB32(1, 130), // else 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "condition=scc1"), "if/else branch condition missing"); Check(SpirvContainsOpcode(result.spirv, 247), "if/else SPIR-V lacks OpSelectionMerge"); Check(SpirvContainsOpcode(result.spirv, 250), "if/else SPIR-V lacks OpBranchConditional"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgTerminalExitMergePS() { const uint32_t shader[] = { EncodeSopp(0x04, 2), // s_cbranch_scc0 end EncodeSMovB32(0, 129), // fallthrough work 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "mode=structured"), "terminal PS branch should stay on structured path"); Check(!Common::ContainsStr(result.ir_dump, "conditional block 0 has no structured merge"), "known terminal PS branch shape still reports missing merge"); Check(SpirvContainsOpcode(result.spirv, 247), "terminal PS branch SPIR-V lacks OpSelectionMerge"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgPostEndTargetMergePS() { const uint32_t shader[] = { EncodeSopp(0x04, 2), // s_cbranch_scc0 label_000c EncodeSMovB32(0, 129), // fallthrough work 0xbf810000u, EncodeSMovB32(1, 129), // branch target after first s_endpgm 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; const bool ok = ShaderRecompiler::TryRecompile(shader, options, result, &error); if (!ok) { std::fprintf(stderr, "PostEndTargetMergePS compile error: %s\n", error.c_str()); } Check(ok, "post-end target PS shader failed to compile"); Check(Common::ContainsStr(result.decoded_dump, "0x0000000c: s_mov_b32"), "post-end branch target was not decoded"); Check(Common::ContainsStr(result.ir_dump, "mode=structured"), "post-end terminal branch should stay on structured path"); Check(Common::ContainsStr(result.ir_dump, "pc=0x0000000c"), "post-end branch target did not reach IR blocks"); Check(!Common::ContainsStr(result.ir_dump, "conditional block 0 has no structured merge"), "known post-end PS branch shape still reports missing merge"); Check(SpirvContainsOpcode(result.spirv, 247), "post-end terminal branch SPIR-V lacks OpSelectionMerge"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgLoopBreakContinue() { const uint32_t shader[] = { EncodeSMovB32(0, 128), // s0 = 0 EncodeSopc(0x0a, 0, 129), // s_cmp_lt_u32 s0, 1 EncodeSopp(0x04, 2), // break when scc == 0 EncodeSop2(0x00, 0, 0, 129), // s_add_u32 s0, s0, 1 EncodeSopp(0x02, 0xfffcu), // continue/backedge 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "backedge"), "loop backedge was not detected"); Check(Common::ContainsStr(result.ir_dump, "loop_header=1"), "loop header was not marked"); Check(Common::ContainsStr(result.ir_dump, "continue="), "loop continue block was not identified"); Check(SpirvContainsOpcode(result.spirv, 246), "loop SPIR-V lacks OpLoopMerge"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgLoopHeaderDynamicScalarBufferLoadStructured() { const uint32_t shader[] = { EncodeSMovB32(0, 128), // preheader: s0 = 0 EncodeSmem0(0x08, 8, 4), 0u, // loop: s_buffer_load_dword s8, s[4:7] EncodeSop2(0x00, 0, 0, 129), // s_add_u32 s0, s0, 1 EncodeSopc(0x0a, 0, 130), // s_cmp_lt_u32 s0, 2 EncodeSopp(0x05, 0xfffbu), // s_cbranch_scc1 loop 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(!ShaderRecompiler::TryRecompile(shader, options, result, &error) && Common::ContainsStr(error, "unsupported GPU selection"), "self-modifying scalar-buffer descriptor should fail explicitly"); } void TestNewShaderRecompilerCfgLoopHeaderBufferLoadDispatcher() { const uint32_t shader[] = { EncodeSMovB32(0, 128), // preheader: s0 = 0 EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), // loop: buffer_load_dword v0 EncodeSop2(0x00, 0, 0, 129), // s_add_u32 s0, s0, 1 EncodeSopc(0x0a, 0, 130), // s_cmp_lt_u32 s0, 2 EncodeSopp(0x05, 0xfffbu), // s_cbranch_scc1 loop 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(!ShaderRecompiler::TryRecompile(shader, options, result, &error) && Common::ContainsStr(error, "unsupported GPU selection"), "self-modifying vector-buffer descriptor should fail explicitly"); } void TestNewShaderRecompilerCfgLoopHeaderDsAppendConsumeDispatcher() { const uint32_t shader[] = { EncodeSMovB32(124, 129), // m0 = one counter EncodeDs0(0x3e), // loop: ds_append EncodeDs1(0, 0, 0), EncodeDs0(0x3d), // ds_consume EncodeDs1(1, 0, 0), EncodeSop2(0x00, 2, 2, 129), // s_add_u32 s2, s2, 1 EncodeSopc(0x0a, 2, 130), // s_cmp_lt_u32 s2, 2 EncodeSopp(0x05, 0xfff9u), // s_cbranch_scc1 loop 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "mode=dispatcher"), "DS append/consume loop header did not select dispatcher fallback"); Check(SpirvContainsOpcode(result.spirv, 246), "DS dispatcher SPIR-V lacks OpLoopMerge"); Check(SpirvContainsOpcode(result.spirv, 251), "DS dispatcher SPIR-V lacks OpSwitch"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgSharedOuterAndLoopMerge() { const uint32_t shader[] = { EncodeSopc(0x06, 0, 0), // s_cmp_eq_u32 s0, s0 EncodeSopp(0x05, 5), // outer early exit -> end EncodeSMovB32(2, 128), // s2 = 0 EncodeSopc(0x0a, 2, 129), // loop: s_cmp_lt_u32 s2, 1 EncodeSopp(0x04, 2), // loop exit -> same real end block EncodeSop2(0x00, 2, 2, 129), // s_add_u32 s2, s2, 1 EncodeSopp(0x02, 0xfffcu), // continue/backedge 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "mode=structured"), "shared outer/loop merge should stay on structured path"); Check(!Common::ContainsStr(result.ir_dump, "duplicate structured merge block"), "shared outer/loop merge was not split before structurization"); Check(SpirvContainsOpcode(result.spirv, 246), "shared outer/loop merge SPIR-V lacks OpLoopMerge"); Check(SpirvContainsOpcode(result.spirv, 247), "shared outer/loop merge SPIR-V lacks OpSelectionMerge"); Check(!SpirvContainsOpcode(result.spirv, 251), "shared outer/loop merge unexpectedly used dispatcher OpSwitch"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgLoopSharedContinueSelectionMerges() { const uint32_t shader[] = { EncodeSMovB32(0, 128), // s0 = 0 EncodeSopc(0x0a, 0, 130), // loop: s_cmp_lt_u32 s0, 2 EncodeSopp(0x04, 9), // loop exit -> end EncodeSopc(0x06, 1, 1), // s_cmp_eq_u32 s1, s1 EncodeSopp(0x05, 5), // first early continue -> continue block EncodeSopc(0x06, 2, 2), // s_cmp_eq_u32 s2, s2 EncodeSopp(0x05, 3), // second early continue -> continue block EncodeSopc(0x06, 3, 3), // s_cmp_eq_u32 s3, s3 EncodeSopp(0x05, 1), // third early continue -> continue block EncodeSMovB32(4, 129), // fallthrough work EncodeSop2(0x00, 0, 0, 129), // continue: s_add_u32 s0, s0, 1 EncodeSopp(0x02, 0xfff5u), // backedge -> loop header 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "mode=structured"), "shared loop continue selections should stay on structured path"); Check(!Common::ContainsStr(result.ir_dump, "duplicate structured merge block"), "shared loop continue selections were not split before structurization"); Check(SpirvContainsOpcode(result.spirv, 246), "shared loop continue selections SPIR-V lacks OpLoopMerge"); Check(SpirvContainsOpcode(result.spirv, 247), "shared loop continue selections SPIR-V lacks OpSelectionMerge"); Check(!SpirvContainsOpcode(result.spirv, 251), "shared loop continue selections unexpectedly used dispatcher OpSwitch"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgDuplicateMergeStructuredSplit() { const uint32_t shader[] = { EncodeSopc(0x06, 0, 0), // s_cmp_eq_u32 s0, s0 EncodeSopp(0x05, 2), // first early exit -> end EncodeSopp(0x05, 1), // second early exit -> same end merge EncodeSMovB32(0, 129), // fallthrough work 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "mode=structured"), "duplicate merge CFG did not stay on structured path"); Check(!Common::ContainsStr(result.ir_dump, "duplicate structured merge block"), "duplicate merge CFG was not split before structurization"); Check(SpirvContainsOpcode(result.spirv, 247), "duplicate merge SPIR-V lacks OpSelectionMerge"); Check(!SpirvContainsOpcode(result.spirv, 251), "duplicate merge CFG unexpectedly used dispatcher OpSwitch"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerCfgIrreducibleDispatcher() { const uint32_t shader[] = { EncodeSopp(0x05, 2), // entry -> B, fallthrough A EncodeSopp(0x02, 0), // A -> C EncodeSopp(0x05, 0xfffeu), // C -> A, fallthrough B EncodeSopp(0x02, 0xfffeu), // B -> C 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "irreducible CFG"), "irreducible CFG reason was not retained"); Check(Common::ContainsStr(result.ir_dump, "mode=dispatcher"), "irreducible CFG did not select dispatcher fallback"); Check(SpirvContainsOpcode(result.spirv, 246), "dispatcher SPIR-V lacks OpLoopMerge"); Check(SpirvContainsOpcode(result.spirv, 251), "dispatcher SPIR-V lacks OpSwitch"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerExecMaskHelpers() { using namespace ShaderRecompiler::Exec; State wave32; wave32.wave_size = 32; WriteExec(wave32, FullMask(64)); Check(ReadExec(wave32).low == 0xffffffffu && ReadExec(wave32).high == 0u, "wave32 EXEC high half was not masked off"); AndExec(wave32, {0x0fu, 0xffffffffu}); Check(ReadExec(wave32).low == 0x0fu && ReadExec(wave32).high == 0u, "wave32 AndExec did not preserve wave size"); InvertExec(wave32); Check(ReadExec(wave32).low == 0xfffffff0u && ReadExec(wave32).high == 0u, "wave32 InvertExec did not preserve wave size"); WriteExec(wave32, {0u, 0xffffffffu}); Check(IsExecZero(wave32), "wave32 IsExecZero used inactive high lanes"); State wave64; wave64.wave_size = 64; WriteExec(wave64, FullMask(64)); Check(ReadExec(wave64).low == 0xffffffffu && ReadExec(wave64).high == 0xffffffffu, "wave64 EXEC full mask was wrong"); XorExec(wave64, {0xffffffffu, 0u}); Check(ReadExec(wave64).low == 0u && ReadExec(wave64).high == 0xffffffffu, "wave64 XorExec did not update both halves correctly"); wave64.vcc = {0u, 1u}; Check(!IsVccZero(wave64), "wave64 VCC high half was ignored"); } void TestComputeShaderInputWaveSize() { const auto decode_wave_size = [](uint32_t rsrc1) { const bool wave32 = (((rsrc1 >> Pm4::COMPUTE_PGM_RSRC1_W32_EN_SHIFT) & Pm4::COMPUTE_PGM_RSRC1_W32_EN_MASK) != 0u); return wave32 ? 32u : 64u; }; constexpr uint32_t observed_wave32_rsrc1_a = 0x402c0146u; constexpr uint32_t observed_wave32_rsrc1_b = 0x402c00c1u; Check(decode_wave_size(observed_wave32_rsrc1_a) == 32u, "COMPUTE_PGM_RSRC1 W32_EN bit did not match observed PS5 value A"); Check(decode_wave_size(observed_wave32_rsrc1_b) == 32u, "COMPUTE_PGM_RSRC1 W32_EN bit did not match observed PS5 value B"); constexpr uint32_t w32_en_bit = 1u << Pm4::COMPUTE_PGM_RSRC1_W32_EN_SHIFT; Check(decode_wave_size(observed_wave32_rsrc1_a & ~w32_en_bit) == 64u, "cleared COMPUTE_PGM_RSRC1 W32_EN bit did not decode as wave64"); } void TestNewShaderRecompilerWave32MasksExecHighStores() { const uint32_t shader[] = { EncodeSop1(0x04, 126, 193), // s_mov_b64 exec, -1 EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.wave_size = 32; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_mov_b64 exec_lo, -1"), "wave32 EXEC mask regression did not decode s_mov_b64 exec, -1"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(CountSourceOccurrences(source, "OpStore %exec_hi %uint_0") >= 2u, "wave32 EXEC high half was not clamped to zero in SPIR-V stores"); Check(!Common::ContainsStr(source, "OpStore %exec_hi %uint_4294967295"), "wave32 EXEC high half was stored as a full 64-lane mask"); } void TestNewShaderRecompilerWave32VccHighScalarStores() { const uint32_t shader[] = { EncodeSmem0(0x09, 106, 4), 0u, // s_buffer_load_dwordx2 vcc_lo EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.wave_size = 32; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "SBufferLoadDword vcc_hi"), "wave32 VCC scalar regression did not lower the high VCC dword load"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(CountSourceOccurrences(source, "OpStore %vcc_hi") >= 2u, "wave32 VCC high scalar load did not store to vcc_hi"); Check(CountSourceOccurrences(source, "OpStore %vcc_hi %uint_0") == 1u, "wave32 VCC high scalar load was clamped to zero instead of preserving data"); } void TestNewShaderRecompilerCompareMaskIsFullWaveBallot() { const uint32_t shader[] = { EncodeVopc(0xc1, 0 + 256, 1), // v_cmp_lt_u32 vcc, v0, v1 EncodeSop2(0x0f, 2, 126, 106), // s_and_b64 s[2:3], exec, vcc EncodeSop1(0x04, 126, 2), // s_mov_b64 exec, s[2:3] EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.wave_size = 32; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "v_cmp_lt_u32"), "compare-mask ballot regression did not decode V_CMP_LT_U32"); Check(Common::ContainsStr(result.ir_dump, "CompareLtU32 vcc_lo"), "compare-mask ballot regression did not lower compare to VCC"); Check(Common::ContainsStr(result.ir_dump, "BitwiseAndU64 s2, exec_lo, vcc_lo"), "compare-mask ballot regression did not consume VCC through scalar mask ALU"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(Common::ContainsStr(source, "OpGroupNonUniformBallot"), "compare-mask result was not materialized with a subgroup ballot"); Check(Common::ContainsStr(source, "OpStore %vcc_lo"), "compare-mask ballot regression did not store VCC low mask"); Check(Common::ContainsStr(source, "OpStore %vcc_hi %uint_0"), "wave32 compare-mask VCC high half was not cleared"); } void TestNewShaderRecompilerBufferLoadsGuardedByExec() { const uint32_t shader[] = { EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), // buffer_load_dword v0 EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "buffer_load_dword"), "buffer load guard regression did not decode MUBUF load"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); const auto exec_branch = Common::FindIndex(source, std::string("OpBranchConditional"), 0); const auto array_length = Common::FindIndex(source, std::string("OpArrayLength"), 0); const auto bounds_branch = Common::FindIndex(source, std::string("OpBranchConditional"), array_length); const auto element_access = Common::FindIndex(source, std::string("OpAccessChain %_ptr_StorageBuffer_uint"), 0); Check(exec_branch != Common::FIND_INVALID_INDEX, "buffer load SPIR-V lacks EXEC guard branch"); Check(array_length != Common::FIND_INVALID_INDEX, "buffer load SPIR-V lacks storage buffer array-length bounds check"); Check(bounds_branch != Common::FIND_INVALID_INDEX, "buffer load SPIR-V lacks storage buffer bounds branch"); Check(element_access != Common::FIND_INVALID_INDEX, "buffer load SPIR-V lacks storage element access"); Check(exec_branch < array_length, "buffer load bounds check was emitted outside EXEC guard"); Check(bounds_branch < element_access, "buffer load storage element pointer was formed before bounds guard"); } void TestNewShaderRecompilerBufferAtomicsGuardedByBounds() { const uint32_t shader[] = { EncodeMubuf0(0x32, 0, true, true), EncodeMubuf1(0, 0, 1), // buffer_atomic_add EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_add"), "buffer atomic bounds regression did not decode MUBUF atomic"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); const auto array_length = Common::FindIndex(source, std::string("OpArrayLength"), 0); const auto bounds_branch = Common::FindIndex(source, std::string("OpBranchConditional"), array_length); const auto atomic = Common::FindIndex(source, std::string("OpAtomicIAdd"), 0); const auto memory_barrier = Common::FindIndex(source, std::string("OpMemoryBarrier"), atomic); Check(array_length != Common::FIND_INVALID_INDEX, "buffer atomic SPIR-V lacks storage buffer array-length bounds check"); Check(bounds_branch != Common::FIND_INVALID_INDEX, "buffer atomic SPIR-V lacks storage buffer bounds branch"); Check(atomic != Common::FIND_INVALID_INDEX, "buffer atomic SPIR-V lacks atomic operation"); Check(memory_barrier != Common::FIND_INVALID_INDEX, "buffer atomic SPIR-V lacks memory barrier after atomic operation"); Check(bounds_branch < atomic, "buffer atomic was emitted before bounds guard"); Check(atomic < memory_barrier, "buffer atomic memory barrier was emitted before atomic"); } void TestNewShaderRecompilerBranchConditionForms() { struct Case { uint32_t opcode; const char* condition; }; const Case cases[] = { {0x04, "condition=scc0"}, {0x08, "condition=execz"}, {0x07, "condition=vccnz"}, }; for (const auto& c: cases) { const uint32_t shader[] = { EncodeSopp(c.opcode, 1), EncodeSMovB32(0, 129), 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, c.condition), "branch condition was not preserved"); Check(SpirvContainsOpcode(result.spirv, 250), "branch condition SPIR-V lacks OpBranchConditional"); CheckSpirvBinaryValidates(result.spirv); if (c.opcode == 0x04) { const auto source = DisassembleSpirvBinary(result.spirv); Check(!Common::ContainsStr(source, "OpGroupNonUniformBallot"), "structured SCC branch retained a host-subgroup reduction"); } } } void TestNewShaderRecompilerSetpcBranch() { const uint32_t shader[] = { EncodeSop1(0x1f, 4, 0), // s_getpc_b64 s[4:5] EncodeSop2(0x00, 4, 4, 140), // s_add_u32 s4, s4, 12 EncodeSop1(0x20, 0, 4), // s_setpc_b64 s[4:5] EncodeSMovB32(0, 129), 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "s_setpc_b64"), "S_SETPC_B64 was not decoded"); Check(Common::ContainsStr(result.ir_dump, "successors=["), "S_SETPC_B64 did not participate in CFG"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerSetpcJumpTable() { const uint32_t shader[] = { EncodeSop2(0x07, 0, 0, 129), // s_min_u32 s0, s0, 1 EncodeSop2(0x1e, 0, 0, 131), // s_lshl_b32 s0, s0, 3 EncodeSop1(0x1f, 8, 0), // s_getpc_b64 s[8:9] EncodeSop2(0x00, 8, 8, 255), // s_add_u32 s8, s8, literal 0x00000038u, EncodeSop2(0x04, 9, 9, 128), // s_addc_u32 s9, s9, 0 EncodeSmem0(0x01, 4, 4), 0u, // s_load_dwordx2 s[4:5], s[8:9], s0 EncodeSopp(0x0c, 0), // s_waitcnt 0 EncodeSop1(0x1f, 10, 0), // s_getpc_b64 s[10:11] EncodeSop2(0x00, 10, 10, 4), // s_add_u32 s10, s10, s4 EncodeSop2(0x04, 11, 11, 5), // s_addc_u32 s11, s11, s5 EncodeSop1(0x20, 0, 10), // s_setpc_b64 s[10:11] EncodeSMovB32(1, 129), // case 0 EncodeSopp(0x02, 1), EncodeSMovB32(2, 129), // case 1 EncodeSopp(0x01, 0), 0x0000000cu, 0x00000000u, // table: target case 0 relative to pc 0x28 0x00000014u, 0x00000000u, // table: target case 1 relative to pc 0x28 }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "mode=dispatcher"), "S_SETPC_B64 jump table did not select dispatcher fallback"); Check(Common::ContainsStr(result.ir_dump, "indirect_targets=2"), "S_SETPC_B64 jump table did not retain both targets"); Check(Common::ContainsStr(result.ir_dump, "selector_values=2"), "S_SETPC_B64 jump table did not retain selector mapping"); Check(!Common::ContainsStr(result.ir_dump, "SLoadDword"), "S_SETPC_B64 jump table load was reflected as a raw scalar buffer load"); Check(SpirvContainsOpcode(result.spirv, 251), "dispatcher SPIR-V lacks OpSwitch"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerExpVertexOutputs() { const uint32_t shader[] = { EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 EncodeExp0(0x20, 0xf), EncodeExp1(4, 5, 6, 7), // PARAM0 EncodeExp0(0x14, 0x1), EncodeExp1(8, 0, 0, 0), // PRIM 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Vertex; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.decoded_dump, "target=0x0c"), "POS export was not decoded"); Check(Common::ContainsStr(result.decoded_dump, "target=0x20"), "PARAM export was not decoded"); Check(Common::ContainsStr(result.decoded_dump, "target=0x14"), "PRIM export was not decoded"); Check(Common::ContainsStr(result.ir_dump, "position"), "POS export did not reach IR"); Check(Common::ContainsStr(result.ir_dump, "parameter"), "PARAM export did not reach IR"); Check(Common::ContainsStr(result.ir_dump, "primitive"), "PRIM export did not reach IR"); Check(SpirvContainsOpcode(result.spirv, 62), "vertex export SPIR-V lacks OpStore"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerVertexSystemVgprs() { using StageInputKind = ShaderRecompiler::IR::StageInputKind; const uint32_t shader[] = { EncodeVop1(0x01, 0, 5 + 256), // v_mov_b32 v0, v5 EncodeVop1(0x01, 1, 8 + 256), // v_mov_b32 v1, v8 EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 EncodeExp0(0x20, 0x3), EncodeExp1(0, 1, 0, 0), // PARAM0 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Vertex; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(ProgramHasInput(result.program, StageInputKind::VertexIndex), "vertex shader missing VertexIndex input"); Check(ProgramHasInput(result.program, StageInputKind::InstanceIndex), "vertex shader missing InstanceIndex input"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 v0, v5"), "vertex shader did not keep v5 as a guest VGPR source"); Check(Common::ContainsStr(result.ir_dump, "MoveU32 v1, v8"), "vertex shader did not keep v8 as a guest VGPR source"); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(CountSourceOccurrences(source, "OpLoad %int %gl_VertexIndex") == 1u, "vertex SPIR-V does not load gl_VertexIndex"); Check(CountSourceOccurrences(source, "OpLoad %int %gl_InstanceIndex") == 1u, "vertex SPIR-V does not load gl_InstanceIndex"); Check(CountSourceOccurrences(source, "OpStore %v5") >= 2u, "vertex SPIR-V does not seed guest v5 from gl_VertexIndex"); Check(CountSourceOccurrences(source, "OpStore %v8") >= 2u, "vertex SPIR-V does not seed guest v8 from gl_InstanceIndex"); } void TestNewShaderRecompilerVertexExportUsesLaneExecMask() { const uint32_t shader[] = { EncodeSop1(0x04, 126, 129), // s_mov_b64 exec, 1 EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Vertex; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(Common::ContainsStr(source, "gl_SubgroupInvocationID"), "vertex export EXEC guard did not test the current lane"); Check(Common::ContainsStr(source, "OpBranchConditional"), "vertex export EXEC guard did not branch on the lane-active mask"); options.lane_mask_mode = ShaderLaneMaskMode::PerInvocation; ShaderRecompiler::CompileResult local_result; Check(ShaderRecompiler::TryRecompile(shader, options, local_result, &error), error.c_str()); Check(local_result.program.lane_mask_mode == ShaderLaneMaskMode::PerInvocation, "per-invocation mask mode was not preserved in the immutable program"); CheckSpirvBinaryValidates(local_result.spirv); const auto local_source = DisassembleSpirvBinary(local_result.spirv); Check(!Common::ContainsStr(local_source, "OpLoad %uint %gl_SubgroupInvocationID"), "per-invocation EXEC still depends on the native subgroup lane"); Check(Common::ContainsStr(local_source, "OpBranchConditional"), "per-invocation vertex export lost its EXEC guard"); } void TestNewShaderRecompilerPerInvocationMasks() { const uint32_t local_shader[] = { EncodeVopc(0xc1, 0 + 256, 1), // v_cmp_lt_u32 vcc, v0, v1 EncodeSop2(0x0f, 2, 126, 106), // s_and_b64 s[2:3], exec, vcc EncodeSop1(0x24, 4, 126), // s_and_saveexec_b64 s[4:5], exec EncodeSop2(0x25, 126, 132, 128), // s_bfm_b64 exec, 4, 0 EncodeSop1(0x08, 126, 126), // s_not_b64 exec, exec EncodeSop1(0x08, 126, 126), // s_not_b64 exec, exec EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Vertex; options.lane_mask_mode = ShaderLaneMaskMode::PerInvocation; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(local_shader, options, result, &error), error.c_str()); CheckSpirvBinaryValidates(result.spirv); const auto source = DisassembleSpirvBinary(result.spirv); Check(!Common::ContainsStr(source, "OpGroupNonUniformBallot"), "per-invocation VCC producer still materialized a shared subgroup mask"); Check(!Common::ContainsStr(source, "OpLoad %uint %gl_SubgroupInvocationID"), "per-invocation BFM EXEC prefix still selected native subgroup lanes"); Check(Common::ContainsStr(source, "OpLogicalAnd") && Common::ContainsStr(source, "OpLogicalNot"), "per-invocation wide mask ALU did not use Boolean operations"); Check(Common::ContainsStr(source, "OpStore %vcc_lo") && Common::ContainsStr(source, "OpStore %vcc_hi %uint_0"), "per-invocation comparison did not store a local Boolean VCC pair"); Check(Common::ContainsStr(result.ir_dump, "SaveexecB64 s4, exec_lo"), "per-invocation SAVEEXEC regression did not reach IR"); const uint32_t wqm_shader[] = { EncodeSop1(0x0a, 2, 126), // s_wqm_b64 s[2:3], exec EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 EncodeSopp(0x01), }; Check(ShaderRecompiler::TryRecompile(wqm_shader, options, result, &error), error.c_str()); CheckSpirvBinaryValidates(result.spirv); const auto wqm_source = DisassembleSpirvBinary(result.spirv); Check(Common::ContainsStr(wqm_source, "OpCapability GroupNonUniformBallot") && Common::ContainsStr(wqm_source, "OpGroupNonUniformBallot"), "per-invocation scalar WQM omitted its subgroup ballot capability"); const uint32_t cross_lane_shader[] = { EncodeSop2(0x25, 126, 132, 128), // s_bfm_b64 exec, 4, 0 EncodeVop1(0x01, 0, 250), EncodeVop1Dpp(1), // v_mov_b32 v0, v1 dpp EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), // POS0 EncodeSopp(0x01), }; Check(ShaderRecompiler::TryRecompile(cross_lane_shader, options, result, &error), error.c_str()); CheckSpirvBinaryValidates(result.spirv); Check(Common::ContainsStr(DisassembleSpirvBinary(result.spirv), "OpGroupNonUniformBallot"), "per-invocation cross-lane EXEC was not reconstructed as a subgroup ballot"); } void TestNewShaderRecompilerExpPixelOutputs() { const uint32_t shader[] = { EncodeExp0(0x00, 0xf), EncodeExp1(0, 1, 2, 3), // MRT0 EncodeExp0(0x08, 0x1), EncodeExp1(4, 0, 0, 0), // MRTZ depth EncodeExp0(0x09, 0x0), EncodeExp1(0, 0, 0, 0), // NULL EncodeExp0(0x00, 0xf, true, true), EncodeExp1(5, 6, 0, 0), // compressed MRT0 0xbf810000u, }; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.dump_ir = true; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(Common::ContainsStr(result.ir_dump, "mrt"), "MRT export did not reach IR"); Check(Common::ContainsStr(result.ir_dump, "mrtz"), "MRTZ export did not reach IR"); Check(Common::ContainsStr(result.ir_dump, "null"), "NULL export did not reach IR"); Check(Common::ContainsStr(result.ir_dump, "compr=1"), "compressed MRT export did not reach IR"); Check(SpirvContainsOpcode(result.spirv, 62), "pixel export SPIR-V lacks OpStore"); Check(SpirvContainsExtInst(result.spirv, 62), "compressed pixel export SPIR-V lacks GLSL.std.450 UnpackHalf2x16"); Check(SpirvContainsOpcode(result.spirv, 81), "compressed pixel export SPIR-V lacks OpCompositeExtract"); CheckSpirvBinaryValidates(result.spirv); ShaderPixelInputInfo uint16_info; uint16_info.target_output_mode[0] = 7; options.pixel_input_info = &uint16_info; ShaderRecompiler::CompileResult uint16_result; Check(ShaderRecompiler::TryRecompile(shader, options, uint16_result, &error), error.c_str()); const auto uint16_source = DisassembleSpirvBinary(uint16_result.spirv); Check(Common::ContainsStr(uint16_source, "OpVariable %_ptr_Output_v4uint Output"), "UINT16 MRT export did not use an unsigned integer output"); Check(CountSourceOccurrences(uint16_source, "OpBitFieldUExtract") == 4u && Common::ContainsStr(uint16_source, "%uint_0 %uint_16") && Common::ContainsStr(uint16_source, "%uint_16 %uint_16"), "compressed UINT16 MRT export did not extract all low/high 16-bit lanes"); Check(!SpirvContainsExtInst(uint16_result.spirv, 62), "compressed UINT16 MRT export was incorrectly decoded as FP16"); CheckSpirvBinaryValidates(uint16_result.spirv); } void TestRenderTargetReverseFloat16ExportMapping() { const auto format = TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k16_16_16_16), Prospero::GpuEnumValue(Prospero::ChannelType::kFloat), Prospero::GpuEnumValue(Prospero::ChannelOrder::kReversed)); Check(format.format == vk::Format::eR16G16B16A16Sfloat && format.bytes_per_element == 8u, "reverse RGBA16F render target did not retain its native host format " "and size"); Check(format.export_mapping == Prospero::ColorMappingAbgr && format.export_mapping.ApplyMask(0x1u) == 0x8u && format.export_mapping.ApplyMask(0x2u) == 0x4u && format.export_mapping.ApplyMask(0x4u) == 0x2u && format.export_mapping.ApplyMask(0x8u) == 0x1u && format.export_mapping.ApplyMask(0xfu) == 0xfu, "reverse RGBA16F render-target export or write-mask mapping is " "incorrect"); const auto legacy_alt = TextureGetRenderTargetFormat( Prospero::GpuEnumValue(Prospero::ChannelLayout::k8_8_8_8), Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm), Prospero::GpuEnumValue(Prospero::ChannelOrder::kAlt)); Check(legacy_alt.format == vk::Format::eB8G8R8A8Unorm && legacy_alt.export_mapping.IsIdentity(), "legacy BGRA render target acquired a duplicate shader export mapping"); const uint32_t shader[] = { EncodeExp0(0x00, 0xf), EncodeExp1(0, 1, 2, 3), // MRT0 0xbf810000u, }; ShaderPixelInputInfo identity_info; ShaderPixelInputInfo reversed_info; reversed_info.target_export_mapping[0] = format.export_mapping; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.pixel_input_info = &identity_info; ShaderRecompiler::CompileResult identity_result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, identity_result, &error), error.c_str()); Check(SpirvInstructionOpcodeCount(identity_result.spirv, 79u) == 0u, "identity MRT export unexpectedly added a component shuffle"); options.pixel_input_info = &reversed_info; ShaderRecompiler::CompileResult reversed_result; Check(ShaderRecompiler::TryRecompile(shader, options, reversed_result, &error), error.c_str()); Check(SpirvContainsVectorShuffle(reversed_result.spirv, {3u, 2u, 1u, 0u}), "reverse MRT export did not emit a WZYX component shuffle"); CheckSpirvBinaryValidates(reversed_result.spirv); HW::PixelShaderInfo regs {}; Check(ShaderGetIdPS(regs, identity_info, false) != ShaderGetIdPS(regs, reversed_info, false), "pixel shader cache identity omitted the render-target export mapping"); regs.ps_regs.data_addr = reinterpret_cast(shader); regs.ps_regs.chksum = 0xf16ab6f000000001ull; ShaderMappedData mapped {}; mapped.code_size_bytes = sizeof(shader); ShaderMapUserData(regs.ps_regs.data_addr, mapped); HW::ShaderRegisters sh {}; ShaderVertexInputInfo vs_info {}; vs_info.stage.program = std::make_shared(); std::array mappings {}; mappings[0] = format.export_mapping; ShaderPixelInputInfo compiled_info {}; std::span compiled_spirv; Check(ShaderCompileInfoPS(regs, sh, ShaderLaneMaskMode::NativeWave, vs_info, mappings, compiled_info, compiled_spirv) && compiled_info.target_export_mapping[0].IsIdentity(), "inactive reverse MRT mapping was not normalized out of the shader cache key"); sh.target_output_mode[0] = 4; Check(ShaderCompileInfoPS(regs, sh, ShaderLaneMaskMode::NativeWave, vs_info, mappings, compiled_info, compiled_spirv) && compiled_info.target_export_mapping[0] == format.export_mapping, "active reverse MRT mapping was lost before shader specialization"); } void TestNewShaderRecompilerEarlyZDisabledWhenPixelKillEnabled() { constexpr uint32_t ExecutionModeEarlyFragmentTests = 9; const uint32_t shader[] = { EncodeExp0(0x00, 0xf, true, false, true), EncodeExp1(0, 1, 2, 3), 0xbf810000u, }; ShaderPixelInputInfo ps_info; ps_info.ps_early_z = true; ps_info.ps_pixel_kill_enable = true; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.pixel_input_info = &ps_info; ShaderRecompiler::CompileResult result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); Check(SpirvContainsOpcode(result.spirv, 252), "pixel valid-mask export should still lower to OpKill"); Check(!SpirvContainsExecutionMode(result.spirv, ExecutionModeEarlyFragmentTests), "pixel shaders that may kill fragments must not request EarlyFragmentTests"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerNativeBindingPlan() { using BindingKind = ShaderRecompiler::IR::DescriptorBindingKind; const uint32_t shader[] = { EncodeSmem0(0x08, 0, 4), 0u, // s_buffer_load_dword s0 EncodeMubuf0(0x1c, 8), EncodeMubuf1(0, 1, 1), // buffer_store_dword EncodeMimg0(0x20, 0xf), EncodeMimg1(8, 2, 3, 1), // image_sample EncodeMimg0(0x08, 0xf), EncodeMimg1(12, 5, 0, 1), // image_store EncodeMimg0(0x11, 0x1), EncodeMimg1(16, 6, 0, 1), // image_atomic_add 0xbf810000u, }; auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.dump_ir = true; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; std::string error; const auto compiled = ShaderRecompiler::TryRecompile(shader, options, result, &error); Check(compiled, error.c_str()); const auto* buffers = ShaderRecompiler::IR::FindBinding(result.program.bindings, BindingKind::Buffers); const auto* sampled = ShaderRecompiler::IR::FindBinding(result.program.bindings, BindingKind::Sampled2D); const auto* storage = ShaderRecompiler::IR::FindBinding(result.program.bindings, BindingKind::Storage2D); const auto* uint_storage = ShaderRecompiler::IR::FindBinding(result.program.bindings, BindingKind::StorageUint2D); const auto* samplers = ShaderRecompiler::IR::FindBinding(result.program.bindings, BindingKind::Samplers); Check(buffers != nullptr && buffers->resources.size() == 2, "native binding plan did not allocate scalar/vector buffers"); Check(sampled != nullptr && sampled->resources.size() == 1, "native binding plan did not allocate the sampled image"); Check(storage != nullptr && storage->resources.size() == 1, "native binding plan did not allocate the float storage image"); Check(uint_storage != nullptr && uint_storage->resources.size() == 1, "native binding plan did not allocate the uint storage image"); Check(samplers != nullptr && samplers->resources.size() == 1, "native binding plan did not allocate the sampler"); Check(SpirvContainsOpcode(result.spirv, 86), "SPIR-V binary does not combine separate image/sampler descriptors"); Check(SpirvHasDecorationValue(result.spirv, 33u, buffers->binding), "SPIR-V lacks storage-buffer Binding decoration"); Check(SpirvHasDecorationValue(result.spirv, 33u, sampled->binding), "SPIR-V lacks sampled-image Binding decoration"); Check(SpirvHasDecorationValue(result.spirv, 33u, samplers->binding), "SPIR-V lacks sampler Binding decoration"); Check(SpirvHasDecorationValue(result.spirv, 34u, result.program.bindings.descriptor_set), "SPIR-V lacks DescriptorSet decoration"); CheckSpirvBinaryValidates(result.spirv); auto malformed = result.program; auto malformed_buffers = std::find_if(malformed.bindings.descriptors.begin(), malformed.bindings.descriptors.end(), [](const auto& binding) { return binding.kind == BindingKind::Buffers; }); Check(malformed_buffers != malformed.bindings.descriptors.end(), "native validation fixture lacks a buffer group"); malformed_buffers->resources.clear(); std::vector rejected_spirv = {0xdeadbeefu}; const auto rejected_before = rejected_spirv; std::string rejected_error; Check(!ShaderRecompiler::Spirv::EmitProgram(malformed, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("topology") != std::string::npos, "malformed native binding plan was not rejected transactionally"); const auto FindBufferInstruction = [](auto* program) { for (auto& block: program->blocks) { const auto found = std::find_if( block.instructions.begin(), block.instructions.end(), [](const auto& inst) { return inst.memory.kind == ShaderRecompiler::IR::ResourceKind::Buffer; }); if (found != block.instructions.end()) { return &*found; } } return static_cast(nullptr); }; auto malformed_inst = result.program; auto* buffer_inst = FindBufferInstruction(&malformed_inst); Check(buffer_inst != nullptr, "native validation fixture lacks a buffer instruction"); buffer_inst->op = ShaderRecompiler::IR::Opcode::FlatLoadDword; buffer_inst->memory.kind = ShaderRecompiler::IR::ResourceKind::Flat; buffer_inst->src_count = 1; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(malformed_inst, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("contract") != std::string::npos, "malformed native instruction was not rejected before fatal emitter paths"); auto missing_buffer_operand = result.program; buffer_inst = FindBufferInstruction(&missing_buffer_operand); Check(buffer_inst != nullptr, "native validation fixture lacks a buffer-arity candidate"); buffer_inst->src_count--; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(missing_buffer_operand, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("contract") != std::string::npos, "buffer instruction with a missing address operand was accepted"); auto extra_buffer_operand = result.program; buffer_inst = FindBufferInstruction(&extra_buffer_operand); Check(buffer_inst != nullptr, "native validation fixture lacks an extra-operand candidate"); buffer_inst->src_count++; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(extra_buffer_operand, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("contract") != std::string::npos, "buffer instruction with an extra address operand was accepted"); auto malformed_atomic = result.program; auto* atomic_inst = FindBufferInstruction(&malformed_atomic); Check(atomic_inst != nullptr, "native validation fixture lacks an atomic candidate"); atomic_inst->op = ShaderRecompiler::IR::Opcode::AtomicAddU32; atomic_inst->memory.kind = ShaderRecompiler::IR::ResourceKind::ScalarBuffer; atomic_inst->src_count = 2; rejected_spirv = rejected_before; rejected_error.clear(); Check( !ShaderRecompiler::Spirv::EmitProgram(malformed_atomic, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("atomic") != std::string::npos, "malformed atomic instruction reached a fatal descriptor path"); auto overwide_atomic = result.program; atomic_inst = FindBufferInstruction(&overwide_atomic); Check(atomic_inst != nullptr, "native validation fixture lacks an overwide atomic candidate"); atomic_inst->op = ShaderRecompiler::IR::Opcode::AtomicAddU32; atomic_inst->src_count = 5; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(overwide_atomic, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("fixed IR storage") != std::string::npos, "overwide atomic operands reached fixed emitter storage"); auto overwide_move = result.program; auto& move_inst = overwide_move.blocks.front().instructions.front(); move_inst.op = ShaderRecompiler::IR::Opcode::MoveU32; move_inst.memory = {}; move_inst.src_count = 5; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(overwide_move, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("fixed IR storage") != std::string::npos, "overwide non-memory operands reached fixed emitter storage"); auto malformed_image = result.program; auto* image_inst = static_cast(nullptr); for (auto& block: malformed_image.blocks) { const auto found = std::find_if( block.instructions.begin(), block.instructions.end(), [](const auto& inst) { return inst.op == ShaderRecompiler::IR::Opcode::ImageSample; }); if (found != block.instructions.end()) { image_inst = &*found; break; } } Check(image_inst != nullptr, "native validation fixture lacks a sampled image"); image_inst->src_count = 0; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(malformed_image, result.resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("sampled image") != std::string::npos, "malformed image operands reached fixed emitter storage"); auto stale_resources = result.resources; uint32_t float_storage = UINT32_MAX; for (uint32_t i = 0; i < result.program.info.images.size(); i++) { if (result.program.info.images[i].kind == ShaderRecompiler::IR::ResourceKind::StorageImage) { float_storage = i; break; } } Check(float_storage != UINT32_MAX, "native validation fixture lacks float storage image"); stale_resources.images[float_storage].dwords[1] = 20u << 20u; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(result.program, stale_resources, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("specialized format") != std::string::npos, "stale runtime image format re-selected an absent descriptor group"); auto stale_dimension = result.resources; stale_dimension.images[float_storage].dwords[3] &= 0x0fffffffu; rejected_spirv = rejected_before; rejected_error.clear(); Check(!ShaderRecompiler::Spirv::EmitProgram(result.program, stale_dimension, nullptr, nullptr, nullptr, rejected_spirv, &rejected_error) && rejected_spirv == rejected_before && rejected_error.find("specialized dimension") != std::string::npos, "unsupported runtime image type bypassed specialization validation"); } bool LowerProvenanceOnly(const uint32_t* code, uint32_t words, ShaderRecompiler::IR::Program& ir, std::string* error) { ShaderRecompiler::Decoder::Program decoded; if (!ShaderRecompiler::Decoder::DecodeProgram(std::span {code, words}, decoded, error)) { return false; } ShaderRecompiler::CFG::Graph cfg; if (!ShaderRecompiler::CFG::BuildGraph(decoded, cfg, error) || !ShaderRecompiler::IR::LowerProgram(decoded, cfg, ShaderType::Compute, 64, ir, error)) { return false; } return ShaderRecompiler::IR::BuildScalarProvenance(ir, error) && ShaderRecompiler::IR::BuildSrtPlan(ir, error); } bool ReadSrtHostDword(void*, uint64_t address, uint32_t* value) { if (address == 0 || value == nullptr) { return false; } std::memcpy(value, reinterpret_cast(address), sizeof(*value)); return true; } void TestScalarProvenanceRealWideMoveLowering() { const uint32_t shader[] = { EncodeSop1(0x04, 0, 20), // s_mov_b64 s[0:1], s[20:21] EncodeSop1(0x04, 2, 22), // s_mov_b64 s[2:3], s[22:23] EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), // buffer_load_dword via copied s[0:3] EncodeSopp(0x01), }; std::string error; ShaderRecompiler::IR::Program ir; const auto lowered = LowerProvenanceOnly(shader, static_cast(std::size(shader)), ir, &error); Check(lowered, error.c_str()); const ShaderRecompiler::IR::Instruction* use = nullptr; for (const auto& block: ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op == ShaderRecompiler::IR::Opcode::BufferLoadDword) { use = &inst; } } } Check(use != nullptr, "real wide-move shader did not lower a buffer use"); const auto* source = ShaderRecompiler::IR::GetDescriptorSource(ir, use->memory.resource_source); Check(source != nullptr && ShaderRecompiler::IR::DescriptorSourceResolved(ir, use->memory.resource_source), "real wide-move descriptor source was unresolved"); for (uint32_t i = 0; i < 4; i++) { const auto& value = ir.provenance.values[source->dwords[i]]; Check(value.op == ShaderRecompiler::IR::ScalarValueOp::UserData && value.imm == 20 + i, "real s_mov_b64 lowering did not copy both descriptor pairs"); } } void TestScalarProvenanceRealCarryAndScalarLoads() { const uint32_t carry_shader[] = { EncodeSop1(0x1f, 0, 0), // s_getpc_b64 s[0:1] EncodeSop2(0x00, 0, 0, 129), // s_add_u32 s0, s0, 1 EncodeSop2(0x04, 1, 1, 128), // s_addc_u32 s1, s1, 0 EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), // buffer_load_dword via computed s[0:3] EncodeSopp(0x01), }; std::string error; ShaderRecompiler::IR::Program carry_ir; const auto carry_lowered = LowerProvenanceOnly( carry_shader, static_cast(std::size(carry_shader)), carry_ir, &error); Check(carry_lowered, error.c_str()); const ShaderRecompiler::IR::DescriptorValue* carry_source = nullptr; for (const auto& block: carry_ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op == ShaderRecompiler::IR::Opcode::BufferLoadDword) { carry_source = ShaderRecompiler::IR::GetDescriptorSource( carry_ir, inst.memory.resource_source); } } } Check(carry_source != nullptr, "real add/addc descriptor source was not attached"); const auto& high = carry_ir.provenance.values[carry_source->dwords[1]]; Check(high.op == ShaderRecompiler::IR::ScalarValueOp::AddCarry && carry_ir.provenance.values[high.args[2]].op == ShaderRecompiler::IR::ScalarValueOp::Carry, "real s_add_u32/s_addc_u32 lowering lost SCC carry provenance"); const uint32_t load_shader[] = { EncodeSmem0(0x02, 0, 4), 0u, // s_load_dwordx4 s[0:3] EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), EncodeSmem0(0x0a, 4, 4), 0u, // s_buffer_load_dwordx4 s[4:7] EncodeMubuf0(0x0c), EncodeMubuf1(0, 1, 1), EncodeSopp(0x01), }; ShaderRecompiler::IR::Program load_ir; const auto load_lowered = LowerProvenanceOnly( load_shader, static_cast(std::size(load_shader)), load_ir, &error); Check(load_lowered, error.c_str()); uint32_t uses = 0; for (const auto& block: load_ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op != ShaderRecompiler::IR::Opcode::BufferLoadDword) { continue; } const auto* source = ShaderRecompiler::IR::GetDescriptorSource(load_ir, inst.memory.resource_source); Check(source != nullptr, "real scalar-load descriptor source was not attached"); const auto expected = uses++ == 0 ? ShaderRecompiler::IR::ScalarValueOp::ReadConst : ShaderRecompiler::IR::ScalarValueOp::ReadConstBuffer; for (uint32_t i = 0; i < 4; i++) { Check(load_ir.provenance.values[source->dwords[i]].op == expected, "real scalar load used the wrong provenance operation"); } } } Check(uses == 2, "real scalar-load provenance test did not find both buffer uses"); const uint32_t inline_sampler_shader[] = { EncodeSop2(0x25, 12, 128 + 12, 128 + 44), // s_bfm_b64 s[12:13], 12, 44 EncodeSop1(0x04, 14, 255), 0x09500000u, // s_mov_b64 s[14:15], 0x09500000 EncodeMimg0(0xa0, 0xf), EncodeMimg1(2, 1, 3, 6), // image_sample_a v2, v6, s[4:11], s[12:15] EncodeSopp(0x01), }; ShaderRecompiler::IR::Program inline_sampler_ir; error.clear(); Check(LowerProvenanceOnly(inline_sampler_shader, static_cast(std::size(inline_sampler_shader)), inline_sampler_ir, &error), error.c_str()); uint32_t sampler_source = 0; for (const auto& block: inline_sampler_ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op == ShaderRecompiler::IR::Opcode::ImageSample) { sampler_source = inst.memory.sampler_source; } } } ShaderRecompiler::IR::DescriptorValue sampler; ShaderRecompiler::IR::SrtRuntime runtime; Check(sampler_source >= 2 && ShaderRecompiler::IR::DescriptorSourceResolved(inline_sampler_ir, sampler_source) && ShaderRecompiler::IR::EvaluateDescriptorSource(inline_sampler_ir, sampler_source, 0x10, runtime, sampler, &error) && sampler.dwords[0] == 0 && sampler.dwords[1] == 0x00fff000u && sampler.dwords[2] == 0x09500000u && sampler.dwords[3] == 0, "real inline sampler construction was unresolved or evaluated incorrectly"); auto user_data = ImageTestUserData(); ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Pixel; options.user_data = user_data.data(); ShaderRecompiler::CompileResult result; error.clear(); Check(ShaderRecompiler::TryRecompile(inline_sampler_shader, options, result, &error), error.c_str()); } void TestSrtWalkerRealSmemLowering() { const uint32_t shader[] = { EncodeSMovB32(124, 130), // m0 = 2 EncodeSmem0(0x02, 0, 4), (124u << 25u) | 2u, // s_load_dwordx4 s[0:3], s[8:9], m0 offset + immediate 2 EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), EncodeSopp(0x01), }; std::string error; ShaderRecompiler::IR::Program ir; const auto lowered = LowerProvenanceOnly(shader, static_cast(std::size(shader)), ir, &error); Check(lowered, error.c_str()); Check(ir.srt.reads.size() == 4 && ir.srt.dynamic_reads.empty(), "real SMEM lowering did not build four compact SRT reads"); const std::array table = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u}; std::array user_data = {}; const auto address = reinterpret_cast(table.data()); user_data[8] = static_cast(address); user_data[9] = static_cast(address >> 32u); std::vector flat; const ShaderRecompiler::IR::SrtRuntime runtime {user_data, 0, ReadSrtHostDword, nullptr}; const auto walked = ShaderRecompiler::IR::WalkSrt(ir, runtime, flat, &error); Check(walked, error.c_str()); Check(flat.size() == table.size() && std::equal(flat.begin(), flat.end(), table.begin()), "real SMEM SRT walk did not apply component-level alignment"); Check(ShaderRecompiler::IR::PatchSrtReads(ir, &error), error.c_str()); uint32_t patched = 0; for (const auto& block: ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op == ShaderRecompiler::IR::Opcode::LoadSrtDword) { Check(inst.src[0].imm == patched++, "real SMEM patch used the wrong flat offset"); } } } Check(patched == 4, "real SMEM patch did not rewrite every component"); } void TestSrtWalkerRealSBufferLowering() { const uint32_t shader[] = { EncodeSMovB32(124, 130), // m0 = 2 EncodeSmem0(0x0a, 0, 4), (124u << 25u) | 2u, // s_buffer_load_dwordx4 s[0:3], s[8:11], m0 + 2 EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), EncodeSopp(0x01), }; std::string error; ShaderRecompiler::IR::Program ir; const auto lowered = LowerProvenanceOnly(shader, static_cast(std::size(shader)), ir, &error); Check(lowered, error.c_str()); Check(ir.srt.reads.size() == 4 && ir.srt.dynamic_reads.empty(), "real S_BUFFER_LOAD lowering did not build four compact reads"); const std::array table = {0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u, 0x55555555u}; std::array user_data = {}; const auto address = reinterpret_cast(table.data()); user_data[8] = static_cast(address); user_data[9] = static_cast(address >> 32u); user_data[10] = sizeof(table); std::vector flat; const ShaderRecompiler::IR::SrtRuntime runtime {user_data, 0, ReadSrtHostDword, nullptr}; const auto walked = ShaderRecompiler::IR::WalkSrt(ir, runtime, flat, &error); Check(walked, error.c_str()); Check(flat.size() == 4 && std::equal(flat.begin(), flat.end(), table.begin() + 1), "real S_BUFFER_LOAD walk used the wrong final alignment"); user_data[10] = 4 * sizeof(uint32_t); const auto flat_before_failure = flat; const auto bounds_walked = ShaderRecompiler::IR::WalkSrt(ir, runtime, flat, &error); Check(!bounds_walked && error.find("exceeds size=16") != std::string::npos, "real S_BUFFER_LOAD walk ignored descriptor bounds"); Check(flat == flat_before_failure, "failed real S_BUFFER_LOAD walk changed the prior flat snapshot"); Check(ShaderRecompiler::IR::PatchSrtReads(ir, &error), error.c_str()); uint32_t patched = 0; for (const auto& block: ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op == ShaderRecompiler::IR::Opcode::LoadSrtDword) { Check(inst.src[0].imm == patched++, "real S_BUFFER_LOAD patch used the wrong flat offset"); } } } Check(patched == 4, "real S_BUFFER_LOAD patch did not rewrite every component"); const uint32_t negative_shader[] = { EncodeSmem0(0x0a, 0, 4), (125u << 25u) | 0x1ffffeu, // illegal negative S_BUFFER immediate EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), EncodeSopp(0x01), }; ShaderRecompiler::IR::Program negative_ir; const auto negative_lowered = LowerProvenanceOnly( negative_shader, static_cast(std::size(negative_shader)), negative_ir, &error); Check(negative_lowered, error.c_str()); user_data[10] = sizeof(table); Check(!ShaderRecompiler::IR::WalkSrt(negative_ir, runtime, flat, &error) && error.find("negative immediate") != std::string::npos, "real S_BUFFER_LOAD walk accepted a negative immediate"); } void TestScalarMemoryLoadsSnapshotOverlappingOperands() { const auto CheckOverlap = [](uint32_t opcode) { const uint32_t shader[] = { EncodeSmem0(opcode, 0, 0), 125u << 25u, // load s[0:3] through overlapping s[0:*], null SOFFSET EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), EncodeSopp(0x01), }; std::string error; ShaderRecompiler::IR::Program ir; Check(LowerProvenanceOnly(shader, static_cast(std::size(shader)), ir, &error), error.c_str()); Check(ir.srt.reads.size() == 4 && ir.srt.dynamic_reads.empty(), opcode == 0x02 ? "overlapping S_LOAD operands were evaluated after a component write" : "overlapping S_BUFFER_LOAD operands were evaluated after a component write"); Check(ShaderRecompiler::IR::PatchSrtReads(ir, &error), error.c_str()); uint32_t patched = 0; for (const auto& block: ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op == ShaderRecompiler::IR::Opcode::LoadSrtDword) { Check(inst.src[0].imm == patched++, "overlapping scalar-memory patch used the wrong flat offset"); } } } Check(patched == 4, "overlapping scalar-memory load was not fully patched"); }; CheckOverlap(0x02); // s_load_dwordx4 CheckOverlap(0x0a); // s_buffer_load_dwordx4 } void TestScalarMemoryLoadCrossesIntoVcc() { const uint32_t shader[] = { EncodeSmem0(0x02, 104, 4), 125u << 25u, // s_load_dwordx4 s[104:105], vcc_lo, vcc_hi EncodeMubuf0(0x0c), EncodeMubuf1(0, 26, 1), EncodeSopp(0x01), }; std::string error; ShaderRecompiler::IR::Program ir; Check(LowerProvenanceOnly(shader, static_cast(std::size(shader)), ir, &error), error.c_str()); Check(ir.srt.reads.size() == 4 && ir.srt.dynamic_reads.empty(), "wide SMEM destination crossing into VCC lost scalar provenance"); Check(ShaderRecompiler::IR::PatchSrtReads(ir, &error), error.c_str()); uint32_t patched = 0; for (const auto& block: ir.blocks) { for (const auto& inst: block.instructions) { if (inst.op == ShaderRecompiler::IR::Opcode::LoadSrtDword) { Check(inst.src[0].imm == patched++, "wide SMEM destination crossing into VCC used the wrong flat offset"); } } } Check(patched == 4, "wide SMEM destination crossing into VCC was not fully patched"); } void TestResourceTrackingRealDensePatching() { const uint32_t shader[] = { EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), // buffer load s[0:3] EncodeMubuf0(0x0c), EncodeMubuf1(1, 0, 1), // same buffer EncodeMubuf0(0x1c), EncodeMubuf1(2, 1, 1), // buffer store s[4:7] EncodeMimg0(0x20, 0xf), EncodeMimg1(8, 0, 2, 1), // sampled image s[0:7], sampler s[8:11] EncodeMimg0(0x08, 0xf), EncodeMimg1(12, 0, 0, 1), // storage view of the same image source EncodeSopp(0x01), }; std::string error; ShaderRecompiler::IR::Program ir; const auto lowered = LowerProvenanceOnly(shader, static_cast(std::size(shader)), ir, &error); Check(lowered, error.c_str()); Check(ShaderRecompiler::IR::PatchSrtReads(ir, &error), error.c_str()); const auto tracked = ShaderRecompiler::IR::TrackResources(ir, &error); Check(tracked, error.c_str()); Check(ir.info.buffers.size() == 2 && ir.info.images.size() == 2 && ir.info.samplers.size() == 1, "real resource tracking produced the wrong dense list sizes"); uint32_t buffer_use = 0; uint32_t image_use = 0; for (const auto& block: ir.blocks) { for (const auto& inst: block.instructions) { switch (inst.memory.kind) { case ShaderRecompiler::IR::ResourceKind::Buffer: { const uint32_t expected = buffer_use++ < 2 ? 0 : 1; Check(inst.memory.resource == expected, "real buffer operand was not patched densely"); break; } case ShaderRecompiler::IR::ResourceKind::Image: case ShaderRecompiler::IR::ResourceKind::StorageImage: Check(inst.memory.resource == image_use++, "real image operand was not patched by view class"); if (inst.op == ShaderRecompiler::IR::Opcode::ImageSample) { Check(inst.memory.sampler == 0, "real sampler operand was not patched densely"); } break; default: break; } } } Check(buffer_use == 3 && image_use == 2 && ir.info.buffers[0].read && ir.info.buffers[1].written, "real tracked resource access facts were incomplete"); } void TestLowerProgramResetsAnalysisState() { const uint32_t first_shader[] = {EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), EncodeSopp(0x01)}; std::string error; ShaderRecompiler::IR::Program ir; Check(LowerProvenanceOnly(first_shader, static_cast(std::size(first_shader)), ir, &error), error.c_str()); Check(ShaderRecompiler::IR::PatchSrtReads(ir, &error), error.c_str()); Check(ShaderRecompiler::IR::TrackResources(ir, &error), error.c_str()); ShaderComputeInputInfo compute; Check(ShaderRecompiler::IR::CollectShaderInfo(ir, {.compute = &compute}, &error), error.c_str()); Check(ir.resource_tracking_complete && ir.shader_info_complete && !ir.info.buffers.empty(), "analysis-reset fixture did not reach completed state"); ir.shader_hash = 0xdeadbeef; const uint32_t second_shader[] = {EncodeSopp(0x01)}; Check(LowerProvenanceOnly(second_shader, static_cast(std::size(second_shader)), ir, &error), error.c_str()); Check(!ir.resource_tracking_complete && !ir.shader_info_complete && ir.srt_plan_complete && ir.shader_hash == 0 && ir.info.buffers.empty() && ir.info.images.empty() && ir.info.samplers.empty() && ir.info.sampled_pairs.empty() && ir.info.inputs.empty() && ir.info.outputs.empty(), "LowerProgram reused stale provenance/resource/interface state"); } void TestNewShaderRecompilerStageInputInfo() { using StageInputKind = ShaderRecompiler::IR::StageInputKind; const uint32_t shader[] = {0xbf810000u}; ShaderComputeInputInfo cs_info {}; cs_info.group_id[0] = true; cs_info.thread_ids_num = 3; cs_info.dispatch_thread_dimensions = true; ShaderRecompiler::CompileOptions cs_options; cs_options.stage = ShaderType::Compute; cs_options.compute_input_info = &cs_info; ShaderRecompiler::CompileResult cs_result; std::string error; Check(ShaderRecompiler::TryRecompile(shader, cs_options, cs_result, &error), error.c_str()); Check(ProgramHasInput(cs_result.program, StageInputKind::WorkgroupId), "compute WorkgroupId input missing from reflection"); Check(ProgramHasInput(cs_result.program, StageInputKind::LocalInvocationId), "compute LocalInvocationId input missing from reflection"); Check(ProgramHasInput(cs_result.program, StageInputKind::LocalInvocationIndex), "compute LocalInvocationIndex input missing from reflection"); Check(ProgramHasInput(cs_result.program, StageInputKind::GlobalInvocationId), "compute GlobalInvocationId input missing from reflection"); Check(SpirvHasDecorationValue(cs_result.spirv, 11u, 26u), "SPIR-V lacks WorkgroupId BuiltIn decoration"); Check(SpirvHasDecorationValue(cs_result.spirv, 11u, 27u), "SPIR-V lacks LocalInvocationId BuiltIn decoration"); Check(SpirvHasDecorationValue(cs_result.spirv, 11u, 28u), "SPIR-V lacks GlobalInvocationId BuiltIn decoration"); Check(SpirvHasDecorationValue(cs_result.spirv, 11u, 29u), "SPIR-V lacks LocalInvocationIndex BuiltIn decoration"); CheckSpirvBinaryValidates(cs_result.spirv); ShaderRecompiler::CompileOptions vs_options; vs_options.stage = ShaderType::Vertex; ShaderRecompiler::CompileResult vs_result; Check(ShaderRecompiler::TryRecompile(shader, vs_options, vs_result, &error), error.c_str()); Check(ProgramHasInput(vs_result.program, StageInputKind::VertexIndex), "vertex VertexIndex input missing from reflection"); Check(ProgramHasInput(vs_result.program, StageInputKind::InstanceIndex), "vertex InstanceIndex input missing from reflection"); Check(SpirvHasDecorationValue(vs_result.spirv, 11u, 42u), "SPIR-V lacks VertexIndex BuiltIn decoration"); Check(SpirvHasDecorationValue(vs_result.spirv, 11u, 43u), "SPIR-V lacks InstanceIndex BuiltIn decoration"); CheckSpirvBinaryValidates(vs_result.spirv); ShaderPixelInputInfo ps_info {}; ps_info.input_num = 2; ps_info.ps_pos_x = true; ps_info.ps_pos_y = true; ps_info.ps_pos_z = true; SetIdentityInterpolatorSettings(&ps_info); ShaderRecompiler::CompileOptions ps_options; ps_options.stage = ShaderType::Pixel; ps_options.pixel_input_info = &ps_info; ShaderRecompiler::CompileResult ps_result; Check(ShaderRecompiler::TryRecompile(shader, ps_options, ps_result, &error), error.c_str()); Check(ProgramHasInput(ps_result.program, StageInputKind::FragCoord), "pixel FragCoord input missing from reflection"); Check(ProgramInputCount(ps_result.program, StageInputKind::Parameter) == 2, "pixel interpolant inputs missing from reflection"); Check(SpirvHasDecorationValue(ps_result.spirv, 11u, 15u), "SPIR-V lacks FragCoord BuiltIn decoration"); Check(SpirvHasDecorationValue(ps_result.spirv, 30u, 0u), "SPIR-V lacks interpolant Location 0 decoration"); Check(SpirvHasDecorationValue(ps_result.spirv, 30u, 1u), "SPIR-V lacks interpolant Location 1 decoration"); CheckSpirvBinaryValidates(ps_result.spirv); ShaderPixelInputInfo ps_pos_y_info {}; ps_pos_y_info.input_num = 1; ps_pos_y_info.ps_system_input_base = 2; ps_pos_y_info.ps_pos_y = true; SetIdentityInterpolatorSettings(&ps_pos_y_info); ps_options.pixel_input_info = &ps_pos_y_info; ShaderRecompiler::CompileResult ps_pos_y_result; Check(ShaderRecompiler::TryRecompile(shader, ps_options, ps_pos_y_result, &error), error.c_str()); Check(ProgramHasInput(ps_pos_y_result.program, StageInputKind::FragCoord), "pixel POS_Y-only FragCoord input missing from reflection"); Check(SpirvHasDecorationValue(ps_pos_y_result.spirv, 11u, 15u), "SPIR-V lacks POS_Y-only FragCoord BuiltIn decoration"); CheckSpirvBinaryValidates(ps_pos_y_result.spirv); } void TestNewShaderRecompilerPixelPipelineEntry() { const uint32_t shader[] = {0xbf810000u}; EnsureConfigInitialized(); HW::PixelShaderInfo regs {}; regs.ps_regs.data_addr = reinterpret_cast(shader); regs.ps_regs.chksum = 0x1234567800000001ull; ShaderMappedData mapped {}; mapped.code_size_bytes = sizeof(shader); ShaderMapUserData(regs.ps_regs.data_addr, mapped); HW::ShaderRegisters sh {}; ShaderPixelInputInfo input_info {}; std::vector spirv; Check(ShaderCompileSpirvPS(regs, sh, ShaderLaneMaskMode::NativeWave, input_info, spirv), "new pixel shader recompiler wrapper did not produce SPIR-V"); Check(!spirv.empty(), "new pixel shader recompiler wrapper returned empty SPIR-V"); Check(spirv.front() == 0x07230203u, "new pixel shader recompiler wrapper did not emit SPIR-V binary"); CheckSpirvBinaryValidates(spirv); const uint32_t vcc_load_shader[] = { EncodeSop1(0x24, 0, 106), // s_and_saveexec_b64 s[0:1], vcc EncodeSMovB32(106, 27), EncodeSMovB32(107, 28), EncodeSmem0(0x02, 44, 53), (0x7du << 25u) | 160u, EncodeMubuf0(0x0c), EncodeMubuf1(0, 11, 1), EncodeSopp(0x01), }; std::array table {}; std::array buffer {}; const auto table_address = reinterpret_cast(table.data()); const auto buffer_address = reinterpret_cast(buffer.data()); table[40] = static_cast(buffer_address); table[41] = static_cast(buffer_address >> 32u); table[42] = 1; HW::PixelShaderInfo vcc_regs {}; vcc_regs.ps_regs.data_addr = reinterpret_cast(vcc_load_shader); vcc_regs.ps_regs.chksum = 0x6306606500000001ull; vcc_regs.ps_regs.rsrc2.user_sgpr = 29; vcc_regs.ps_user_sgpr.value[27] = static_cast(table_address); vcc_regs.ps_user_sgpr.value[28] = static_cast(table_address >> 32u); ShaderMappedData vcc_mapped {}; vcc_mapped.code_size_bytes = sizeof(vcc_load_shader); ShaderMapUserData(vcc_regs.ps_regs.data_addr, vcc_mapped); ShaderPixelInputInfo vcc_input {}; std::vector vcc_spirv; Check(ShaderCompileSpirvPS(vcc_regs, sh, ShaderLaneMaskMode::NativeWave, vcc_input, vcc_spirv), "VCC raw-load pointer was lost"); CheckSpirvBinaryValidates(vcc_spirv); } void TestPixelProgramCacheDescriptorSetIdentity() { const uint32_t shader_01[] = {0xbf810000u}; const uint32_t shader_10[] = {0xbf810000u}; const uint32_t shader_mask[] = {0xbf810000u}; HW::ShaderRegisters sh {}; auto check_transition = [&](const uint32_t* shader, uint64_t checksum, bool first_has_vs_descriptors, bool second_has_vs_descriptors) { HW::PixelShaderInfo regs {}; regs.ps_regs.data_addr = reinterpret_cast(shader); regs.ps_regs.chksum = checksum; ShaderMappedData mapped {}; mapped.code_size_bytes = sizeof(uint32_t); ShaderMapUserData(regs.ps_regs.data_addr, mapped); auto compile = [&](bool has_vs_descriptors) { auto vs_program = std::make_shared(); if (has_vs_descriptors) { vs_program->bindings.descriptors.emplace_back(); } ShaderVertexInputInfo vs_info {}; vs_info.stage.program = std::move(vs_program); const std::array identity_mappings {}; ShaderPixelInputInfo ps_info {}; std::span spirv; Check(ShaderCompileInfoPS(regs, sh, ShaderLaneMaskMode::NativeWave, vs_info, identity_mappings, ps_info, spirv), "pixel program-cache transition failed to compile"); const auto expected_set = has_vs_descriptors ? 1u : 0u; Check(ps_info.descriptor_set == expected_set && ps_info.stage.program != nullptr && ps_info.stage.program->bindings.descriptor_set == expected_set, "pixel program cache reused SPIR-V with the paired VS descriptor set"); }; compile(first_has_vs_descriptors); compile(second_has_vs_descriptors); }; check_transition(shader_01, 0x91a27e6300000001ull, false, true); check_transition(shader_10, 0x91a27e6300000002ull, true, false); HW::PixelShaderInfo mask_regs {}; mask_regs.ps_regs.data_addr = reinterpret_cast(shader_mask); mask_regs.ps_regs.chksum = 0x91a27e6300000003ull; ShaderMappedData mapped {}; mapped.code_size_bytes = sizeof(shader_mask); ShaderMapUserData(mask_regs.ps_regs.data_addr, mapped); auto compile_mask_mode = [&](ShaderLaneMaskMode mode) { ShaderVertexInputInfo vs_info {}; vs_info.stage.program = std::make_shared(); const std::array identity_mappings {}; ShaderPixelInputInfo ps_info {}; std::span spirv; Check(ShaderCompileInfoPS(mask_regs, sh, mode, vs_info, identity_mappings, ps_info, spirv), "pixel lane-mask cache transition failed to compile"); Check(ps_info.stage.program != nullptr && ps_info.stage.program->lane_mask_mode == mode, "pixel program cache reused a different lane-mask lowering"); }; compile_mask_mode(ShaderLaneMaskMode::NativeWave); compile_mask_mode(ShaderLaneMaskMode::PerInvocation); } void TestNewShaderRecompilerUnsupportedMemoryDecode() { const uint32_t mubuf_unknown[] = {EncodeMubuf0(0x7b), EncodeMubuf1(0, 0, 1), 0xbf810000u}; CheckNewDecoderUnsupported(mubuf_unknown, static_cast(std::size(mubuf_unknown)), "MUBUF", "opcode=0x7b"); const uint32_t flat_unknown[] = {EncodeFlat0(0x7e, 0, 4), EncodeFlat1(9, 0x7d, 0, 1), 0xbf810000u}; CheckNewDecoderUnsupported(flat_unknown, static_cast(std::size(flat_unknown)), "FLAT", "opcode=0x7e"); const uint32_t mtbuf_unknown[] = {EncodeMtbuf0(0x08, 14, 7, 4), EncodeMtbuf1(0x08, 9, 0, 1), 0xbf810000u}; CheckNewDecoderUnsupported(mtbuf_unknown, static_cast(std::size(mtbuf_unknown)), "MTBUF", "opcode=0x08"); } void TestNewShaderRecompilerFlatUserPointerProvenance() { const uint32_t shader[] = { EncodeVop2(0x25, 0, 0, 2), // v_add_nc_u32 v0, s0, v2 EncodeVop1(0x01, 1, 1), // v_mov_b32 v1, s1 EncodeFlat0(0x0c, 0, 0), EncodeFlat1(3, 0x7d, 0, 0), // flat_load_dword v3, v[0:1] 0xbf810000u, }; const uint32_t user_data[] = {0x34567000u, 0x00000012u}; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.user_data = user_data; options.user_data_count = static_cast(std::size(user_data)); ShaderRecompiler::CompileResult result; std::string error; const bool compiled = ShaderRecompiler::TryRecompile(shader, options, result, &error); Check(compiled, error.c_str()); Check(result.program.info.addresses.size() == 1 && result.program.info.addresses[0].source != ShaderRecompiler::IR::ScalarProvenance::Unknown, "FLAT user pointer provenance was not attached"); Check(result.resources.addresses.size() == 1 && result.resources.addresses[0].guest_base == 0x0000001234567000ull && result.resources.addresses[0].binding_base == 0x0000001234560000ull && result.program.info.addresses[0].specialized_base == 0x0000001234560000ull, "FLAT user pointer did not materialize its guest base"); CheckSpirvBinaryValidates(result.spirv); } void TestNewShaderRecompilerFlatAddressProvenanceBoundaries() { const uint32_t segmented_shader[] = { EncodeVop2(0x25, 0, 0, 2), EncodeVop1(0x01, 1, 1), EncodeFlat0(0x0c, 2, 0), EncodeFlat1(3, 0x7d, 0, 0), // global_load_dword v3, v[0:1] EncodeFlat0(0x0c, 1, 0), EncodeFlat1(4, 0x7d, 0, 0), // scratch_load_dword v4, v[0:1] 0xbf810000u, }; const uint32_t user_data[] = {0x34567000u, 0u, 0x1000u}; ShaderRecompiler::CompileOptions options; options.stage = ShaderType::Compute; options.user_data = user_data; options.user_data_count = static_cast(std::size(user_data)); options.flat_memory_base = 0; ShaderRecompiler::CompileResult result; std::string error; const bool compiled = ShaderRecompiler::TryRecompile(segmented_shader, options, result, &error); Check(compiled, error.c_str()); Check(result.program.info.addresses.size() == 2, "segmented address resources were not tracked independently"); for (const auto& address: result.program.info.addresses) { Check(address.source == ShaderRecompiler::IR::ScalarProvenance::Unknown, "GLOBAL/SCRATCH null-SADDR incorrectly inherited FLAT VGPR provenance"); } } } // namespace } // namespace Libs::Graphics int main() { using namespace Libs::Graphics; EnsureConfigInitialized(); TestResourceDescriptorClassification(); TestNativeShaderResourceDependencies(); TestNormalizedImageContracts(); TestNativeSubgroupPolicy(); TestNewShaderRecompilerSMovB32(); TestNewShaderRecompilerSoppMarkers(); TestNewShaderRecompilerSopkWaitcntMarkers(); TestNewShaderRecompilerRdna2ScalarOpcodes(); TestNewShaderRecompilerScalarVectorAlu(); TestNewShaderRecompilerVop3LaneReadDestinationEncoding(); TestNewShaderRecompilerMoreAluFamilies(); TestNewShaderRecompilerExpandedAluBatch(); TestNewShaderRecompilerVop3pPackedF16(); TestNewShaderRecompilerStagedShaderOps(); TestNewShaderRecompilerBootF16UnaryOpcodes(); TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes(); TestNewShaderRecompilerScalarB64Alu(); TestNewShaderRecompilerSignedCompareAlu(); TestNewShaderRecompilerSignedMinShiftAlu(); TestNewShaderRecompilerScalarBitfieldAlu(); TestNewShaderRecompilerMemoryFamilyLowering(); TestNewShaderRecompilerImageQueryLowering(); TestNewShaderRecompilerCubeSampleCoordinates(); TestNewShaderRecompilerImageSampleVariants(); TestNewShaderRecompilerImageSampleA16SamplerCoords(); TestNewShaderRecompilerImageSampleOpcodeAliases(); TestNewShaderRecompilerImageSampleA16ExceptionComponents(); TestNewShaderRecompilerImageSampleA16Rdna2AddressOrder(); TestNewShaderRecompilerPixelImageSampleLodSelection(); TestNewShaderRecompilerImageViewDimensions(); TestNewShaderRecompilerStorageImage1DDescriptorVariants(); TestNewShaderRecompilerNullImageUsesCanonical2DView(); TestNewShaderRecompilerRejectsOneDimensionalGather(); TestNewShaderRecompilerImageGatherVariants(); TestNewShaderRecompilerImageLoadA16UintCoords(); TestNewShaderRecompilerImageLoadVariants(); TestNewShaderRecompilerImageLoad2DMsaa(); TestNewShaderRecompilerImageStoreLowering(); TestNewShaderRecompilerStorageImage3DDescriptorVariant(); TestNewShaderRecompilerStorageImage2DDescriptorOverridesMimg3D(); TestNewShaderRecompilerImageAtomicLowering(); TestNewShaderRecompilerVintrpLowering(); TestPsInputCountRegisterDecode(); TestNewShaderRecompilerWideMemoryLowering(); TestNewShaderRecompilerBufferSignedLoadLowering(); TestNewShaderRecompilerBufferSubDwordStoreLowering(); TestNewShaderRecompilerMubufFormatLowering(); TestNewShaderRecompilerFormattedStoreUsesRuntimeArrayLengthOnly(); TestNewShaderRecompilerTypedBufferLowering(); TestNewShaderRecompilerFlatOldBackedLowering(); TestNewShaderRecompilerUnbasedFlatRequiresTranslator(); TestNewShaderRecompilerFlatUserPointerProvenance(); TestNewShaderRecompilerFlatAddressProvenanceBoundaries(); TestNewShaderRecompilerFlatSignedLoadLowering(); TestNewShaderRecompilerFlatStoreLowering(); TestNewShaderRecompilerAtomicLowering(); TestNewShaderRecompilerDsReadWrite2Lowering(); TestNewShaderRecompilerDsSubDwordLowering(); TestNewShaderRecompilerDsWideAndAtomicLowering(); TestNewShaderRecompilerDsSwizzleLowering(); TestNewShaderRecompilerDsAddtidLowering(); TestNewShaderRecompilerDsFloatMinMaxLowering(); TestNewShaderRecompilerCfgStraightLine(); TestNewShaderRecompilerCfgIfElse(); TestNewShaderRecompilerCfgTerminalExitMergePS(); TestNewShaderRecompilerCfgPostEndTargetMergePS(); TestNewShaderRecompilerCfgLoopBreakContinue(); TestNewShaderRecompilerCfgLoopHeaderDynamicScalarBufferLoadStructured(); TestNewShaderRecompilerCfgLoopHeaderBufferLoadDispatcher(); TestNewShaderRecompilerCfgLoopHeaderDsAppendConsumeDispatcher(); TestNewShaderRecompilerCfgSharedOuterAndLoopMerge(); TestNewShaderRecompilerCfgLoopSharedContinueSelectionMerges(); TestNewShaderRecompilerCfgDuplicateMergeStructuredSplit(); TestNewShaderRecompilerCfgIrreducibleDispatcher(); TestNewShaderRecompilerExecMaskHelpers(); TestComputeShaderInputWaveSize(); TestNewShaderRecompilerWave32MasksExecHighStores(); TestNewShaderRecompilerWave32VccHighScalarStores(); TestNewShaderRecompilerCompareMaskIsFullWaveBallot(); TestNewShaderRecompilerBufferLoadsGuardedByExec(); TestNewShaderRecompilerBufferAtomicsGuardedByBounds(); TestNewShaderRecompilerBranchConditionForms(); TestNewShaderRecompilerSetpcBranch(); TestNewShaderRecompilerSetpcJumpTable(); TestNewShaderRecompilerExpVertexOutputs(); TestNewShaderRecompilerVertexSystemVgprs(); TestNewShaderRecompilerVertexExportUsesLaneExecMask(); TestNewShaderRecompilerPerInvocationMasks(); TestNewShaderRecompilerExpPixelOutputs(); TestRenderTargetReverseFloat16ExportMapping(); TestNewShaderRecompilerEarlyZDisabledWhenPixelKillEnabled(); TestScalarProvenanceRealWideMoveLowering(); TestScalarProvenanceRealCarryAndScalarLoads(); TestSrtWalkerRealSmemLowering(); TestSrtWalkerRealSBufferLowering(); TestScalarMemoryLoadsSnapshotOverlappingOperands(); TestScalarMemoryLoadCrossesIntoVcc(); TestResourceTrackingRealDensePatching(); TestLowerProgramResetsAnalysisState(); TestNewShaderRecompilerNativeBindingPlan(); TestNewShaderRecompilerStageInputInfo(); TestGraphicsCreateInterpolantMapping(); TestNewShaderRecompilerPixelPipelineEntry(); TestPixelProgramCacheDescriptorSetIdentity(); TestNewShaderRecompilerUnsupportedMemoryDecode(); return 0; }