spirv: emit OpName only with debug info

This commit is contained in:
nmzik
2026-10-22 18:02:51 +02:00
parent a875d1b205
commit a951a91703
5 changed files with 42 additions and 11 deletions
@@ -169,6 +169,9 @@ uint32_t Builder::AddString(const char* text) {
} }
void Builder::AddName(uint32_t target, const char* name) { void Builder::AddName(uint32_t target, const char* name) {
if (!m_emit_names) {
return;
}
std::vector<uint32_t> operands = {target}; std::vector<uint32_t> operands = {target};
AppendString(operands, name); AppendString(operands, name);
AppendInstruction(m_debug, spv::OpName, operands); AppendInstruction(m_debug, spv::OpName, operands);
@@ -67,6 +67,7 @@ public:
} }
uint32_t AddString(const char* text); uint32_t AddString(const char* text);
void EnableNames() { m_emit_names = true; }
void AddName(uint32_t target, const char* name); void AddName(uint32_t target, const char* name);
template <typename... Args> template <typename... Args>
void AddAnnotation(spv::Op opcode, const Args&... operands) { void AddAnnotation(spv::Op opcode, const Args&... operands) {
@@ -159,6 +160,7 @@ private:
std::map<std::string, uint32_t> m_import_ids; std::map<std::string, uint32_t> m_import_ids;
std::map<std::vector<uint32_t>, uint32_t> m_declaration_ids; std::map<std::vector<uint32_t>, uint32_t> m_declaration_ids;
size_t m_unpatched_phi_incomings = 0; size_t m_unpatched_phi_incomings = 0;
bool m_emit_names = false;
}; };
} // namespace Libs::Graphics::ShaderRecompiler::Spirv } // namespace Libs::Graphics::ShaderRecompiler::Spirv
@@ -226,6 +226,7 @@ std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo inp
std::snprintf(name, sizeof(name), "rdna2-%016" PRIx64 "-block-byte-pc-plus-one", std::snprintf(name, sizeof(name), "rdna2-%016" PRIx64 "-block-byte-pc-plus-one",
program.shader_hash); program.shader_hash);
state.debug_source = state.builder.AddString(name); state.debug_source = state.builder.AddString(name);
state.builder.EnableNames();
} }
const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info); const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info);
state.lane_count = state.lane_count =
+18 -5
View File
@@ -1619,6 +1619,17 @@ void CheckRectListShaders() {
ValidateSpirv(name, position_only.evaluation); ValidateSpirv(name, position_only.evaluation);
} }
// Words without OpName/OpString/OpLine, which only graphics debug dumps emit.
size_t SpirvCodeWords(const std::vector<u32> &spirv) {
size_t words = spirv.size();
for (size_t offset = 5; offset < spirv.size(); offset += spirv[offset] >> 16u) {
const auto opcode = static_cast<spv::Op>(spirv[offset] & 0xffffu);
if (opcode == spv::OpName || opcode == spv::OpString || opcode == spv::OpLine)
words -= spirv[offset] >> 16u;
}
return words;
}
void CheckSpirvEmission(const TestCase &test, const std::vector<u32> &spirv) { void CheckSpirvEmission(const TestCase &test, const std::vector<u32> &spirv) {
std::vector<u32> result_types(spirv[3]); std::vector<u32> result_types(spirv[3]);
for (size_t offset = 5; offset < spirv.size();) { for (size_t offset = 5; offset < spirv.size();) {
@@ -1679,6 +1690,7 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
options.stage = ShaderType::Compute; options.stage = ShaderType::Compute;
options.profile = test.profile; options.profile = test.profile;
options.dump_ir = true; options.dump_ir = true;
options.emit_debug_info = true; // Checks find variables and functions by OpName.
auto compute_info = test.compute_info; auto compute_info = test.compute_info;
compute_info.host_subgroup_size = host_subgroup_size; compute_info.host_subgroup_size = host_subgroup_size;
options.input_info.compute = &compute_info; options.input_info.compute = &compute_info;
@@ -1750,7 +1762,7 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
result.program.shader_info_complete = false; result.program.shader_info_complete = false;
ShaderRecompiler::IR::CollectShaderInfo(result.program, options.input_info); ShaderRecompiler::IR::CollectShaderInfo(result.program, options.input_info);
result.spirv = ShaderRecompiler::Spirv::EmitProgram( result.spirv = ShaderRecompiler::Spirv::EmitProgram(
result.program, options.input_info, 0u, *options.profile); result.program, options.input_info, 0u, *options.profile, true);
const auto shader_data = result.program.bindings.descriptor_counts[ const auto shader_data = result.program.bindings.descriptor_counts[
static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::ShaderData)]; static_cast<size_t>(ShaderRecompiler::IR::DescriptorBindingKind::ShaderData)];
Require(test.name, "binding layout", Require(test.name, "binding layout",
@@ -1764,11 +1776,12 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
ValidateSpirv(test.name, result.spirv); ValidateSpirv(test.name, result.spirv);
CheckSpirvEmission(test, result.spirv); CheckSpirvEmission(test, result.spirv);
if (test.max_spirv_words != 0u) { if (test.max_spirv_words != 0u) {
Require(test.name, "emitted shader size", result.spirv.size() <= test.max_spirv_words, const auto words = SpirvCodeWords(result.spirv);
"SPIR-V words=" + std::to_string(result.spirv.size()) + Require(test.name, "emitted shader size", words <= test.max_spirv_words,
"SPIR-V words=" + std::to_string(words) +
", limit=" + std::to_string(test.max_spirv_words)); ", limit=" + std::to_string(test.max_spirv_words));
std::printf("[compute] %s wave=%u SPIR-V words=%zu\n", test.name, std::printf("[compute] %s wave=%u SPIR-V words=%zu\n", test.name,
result.program.wave_size, result.spirv.size()); result.program.wave_size, words);
} }
if (test.expected_buffer_resources) { if (test.expected_buffer_resources) {
std::vector<u32> resources; std::vector<u32> resources;
@@ -40580,7 +40593,7 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
compiled.program.info.samplers.size() == rows + 1u && compiled.program.info.samplers.size() == rows + 1u &&
tools.Disassemble(compiled.spirv, &text) && tools.Disassemble(compiled.spirv, &text) &&
CountText(text, "OpImageSampleExplicitLod") == halves && CountText(text, "OpImageSampleExplicitLod") == halves &&
compiled.spirv.size() <= 8192u * halves, SpirvCodeWords(compiled.spirv) <= 8192u * halves,
"59 sampler records hit the SGPR root limit or duplicated image sampling bodies"); "59 sampler records hit the SGPR root limit or duplicated image sampling bodies");
} }
if (large_samplers) if (large_samplers)
+18 -6
View File
@@ -99,6 +99,7 @@ ShaderRecompiler::CompileOptions MakeCompileOptions(ShaderType stage) {
ShaderRecompiler::CompileOptions options; ShaderRecompiler::CompileOptions options;
options.stage = stage; options.stage = stage;
options.user_data = user_data; options.user_data = user_data;
options.emit_debug_info = true; // Checks find variables and functions by OpName.
switch (stage) { switch (stage) {
case ShaderType::Vertex: case ShaderType::Vertex:
options.input_info.vertex = &vertex; options.input_info.vertex = &vertex;
@@ -403,6 +404,11 @@ SpirvMetrics MeasureSpirv(const std::vector<uint32_t> &binary) {
if (word_count == 0 || i + word_count > binary.size()) { if (word_count == 0 || i + word_count > binary.size()) {
break; break;
} }
if (opcode == spv::OpName || opcode == spv::OpString || opcode == spv::OpLine) {
metrics.words -= word_count; // Budgets measure the module without debug info.
i += word_count;
continue;
}
metrics.instructions++; metrics.instructions++;
switch (opcode) { switch (opcode) {
case 11u: // OpExtInstImport case 11u: // OpExtInstImport
@@ -10354,7 +10360,7 @@ void TestNewShaderRecompilerPlanningOnlyLoads() {
ir.Emit(IR::ValueOpcode::ReferenceU32, {read}); ir.Emit(IR::ValueOpcode::ReferenceU32, {read});
program.shader_info_complete = false; program.shader_info_complete = false;
IR::CollectShaderInfo(program, options.input_info); IR::CollectShaderInfo(program, options.input_info);
const auto spirv = Spirv::EmitProgram(program, options.input_info); const auto spirv = Spirv::EmitProgram(program, options.input_info, 0, {}, true);
CheckSpirvBinaryValidates(spirv); CheckSpirvBinaryValidates(spirv);
Check(spirv == result.spirv, Check(spirv == result.spirv,
"planning-only loads or references emitted GPU operands/instructions"); "planning-only loads or references emitted GPU operands/instructions");
@@ -11100,6 +11106,7 @@ void TestMeshExportStorage() {
options.stage = ShaderType::Mesh; options.stage = ShaderType::Mesh;
options.input_info.vertex = &input; options.input_info.vertex = &input;
options.user_data = user_data; options.user_data = user_data;
options.emit_debug_info = true; // The checks find push data by its OpName.
struct Case { struct Case {
uint32_t subgroup_size, push_data_start; uint32_t subgroup_size, push_data_start;
bool split; bool split;
@@ -14352,7 +14359,8 @@ void TestNewShaderRecompilerNativeBindingPlan() {
const auto bindings = result.program.bindings; const auto bindings = result.program.bindings;
result.program.bindings = {}; result.program.bindings = {};
result.program.info.buffers[0].descriptor_index = UINT32_MAX; result.program.info.buffers[0].descriptor_index = UINT32_MAX;
Check(ShaderRecompiler::Spirv::EmitProgram(result.program, options.input_info) == result.spirv && Check(ShaderRecompiler::Spirv::EmitProgram(result.program, options.input_info, 0, {}, true) ==
result.spirv &&
result.program.bindings == bindings && result.program.bindings == bindings &&
result.program.info.buffers[0].descriptor_index == 0, result.program.info.buffers[0].descriptor_index == 0,
"repeated emission did not rebuild descriptor slots deterministically"); "repeated emission did not rebuild descriptor slots deterministically");
@@ -16423,11 +16431,15 @@ void TestRepeatedExportsHaveOneInterface() {
void CheckSpirvBlockSourceMapping(TestCompileResult &result) { void CheckSpirvBlockSourceMapping(TestCompileResult &result) {
using namespace ShaderRecompiler; using namespace ShaderRecompiler;
const auto options = MakeCompileOptions(ShaderType::Compute); const auto options = MakeCompileOptions(ShaderType::Compute);
Check(SpirvInstructionOpcodeCount(result.spirv, spv::OpString) == 0 && const auto plain = Spirv::EmitProgram(result.program, options.input_info);
SpirvInstructionOpcodeCount(result.spirv, spv::OpLine) == 0, Check(SpirvInstructionOpcodeCount(plain, spv::OpString) == 0 &&
"default emission unexpectedly contains source mapping"); SpirvInstructionOpcodeCount(plain, spv::OpLine) == 0 &&
SpirvInstructionOpcodeCount(plain, spv::OpName) == 0,
"default emission unexpectedly contains debug info");
const auto mapped = const auto mapped =
Spirv::EmitProgram(result.program, options.input_info, 0, {}, true); Spirv::EmitProgram(result.program, options.input_info, 0, {}, true);
Check(SpirvInstructionOpcodeCount(mapped, spv::OpName) != 0,
"debug emission omitted names");
CheckSpirvBinaryValidates(mapped); CheckSpirvBinaryValidates(mapped);
uint32_t source = 0; uint32_t source = 0;
uint32_t label = 0; uint32_t label = 0;
@@ -16456,7 +16468,7 @@ void CheckSpirvBlockSourceMapping(TestCompileResult &result) {
} }
Check(source != 0 && lines >= result.program.blocks.size(), Check(source != 0 && lines >= result.program.blocks.size(),
"debug emission omitted native block source mapping"); "debug emission omitted native block source mapping");
Check(Spirv::EmitProgram(result.program, options.input_info) == result.spirv, Check(Spirv::EmitProgram(result.program, options.input_info) == plain,
"debug re-emission changed the default binary"); "debug re-emission changed the default binary");
} }