| // Copyright (C) 2026 NVIDIA Corporation. |
| // |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions |
| // are met: |
| // |
| // Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // |
| // Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // |
| // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| // POSSIBILITY OF SUCH DAMAGE. |
| |
| // Tests for NonSemantic.Shader.DebugInfo (NSDI) features. |
| |
| #include "TestFixture.h" |
| #include "SPIRV/NonSemanticShaderDebugInfo.h" |
| #include "glslang/Public/ResourceLimits.h" |
| #include <gtest/gtest.h> |
| #include <cstdint> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| namespace glslangtest { |
| namespace { |
| |
| std::string decodeLiteralString(const uint32_t* words, size_t wordCount) |
| { |
| std::string result; |
| for (size_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) { |
| for (unsigned int byteIndex = 0; byteIndex < 4; ++byteIndex) { |
| const char character = static_cast<char>((words[wordIndex] >> (8 * byteIndex)) & 0xff); |
| if (character == '\0') |
| return result; |
| result.push_back(character); |
| } |
| } |
| return result; |
| } |
| |
| bool containsDebugTypeBasic(const std::vector<uint32_t>& spirv, const char* expectedName, |
| uint32_t expectedWidth, spv::FPEncoding expectedFPEncoding) |
| { |
| if (spirv.size() < 5 || spirv[0] != spv::MagicNumber) |
| return false; |
| |
| const uint32_t idBound = spirv[3]; |
| std::vector<std::string> strings(idBound); |
| std::vector<uint32_t> constants(idBound); |
| std::vector<bool> isConstant(idBound); |
| uint32_t debugInfoImportId = 0; |
| |
| size_t end = spirv.size(); |
| |
| // Resolve the IDs referenced by DebugTypeBasic before inspecting the instruction itself. |
| // OpExtInstImport gives the result ID used to select the .101 debug-info instruction set, |
| // OpString gives the source-level type name, and OpConstant gives the values of the width, |
| // base encoding, flags, and optional FPEncoding operands. |
| // |
| // For example, the relevant SPIR-V for floate2m1_t has this shape: |
| // %debug = OpExtInstImport "NonSemantic.Shader.DebugInfo.101" |
| // %name = OpString "floate2m1_t" |
| // %width = OpConstant %uint 4 |
| // %encoding = OpConstant %uint 3 ; NonSemanticShaderDebugInfoFloat |
| // %flags = OpConstant %uint 0 ; NonSemanticShaderDebugInfoNone |
| // %fpEncoding = OpConstant %uint 4225 ; Float4E2M1EXT |
| // %type = OpExtInst %void %debug DebugTypeBasic |
| // %name %width %encoding %flags %fpEncoding |
| for (size_t offset = 5; offset < spirv.size();) { |
| const uint32_t wordCount = spirv[offset] >> spv::WordCountShift; |
| const spv::Op opcode = static_cast<spv::Op>(spirv[offset] & spv::OpCodeMask); |
| if (wordCount == 0 || offset + wordCount > spirv.size()) |
| return false; |
| |
| if (opcode == spv::Op::OpFunction) { |
| // SPIR-V's logical layout places imports, debug strings, constants, and global |
| // debug-type instructions before the first OpFunction. Record that boundary while |
| // resolving IDs so this pass can exit early and the subsequent DebugTypeBasic scan |
| // can stop there as well. |
| end = offset; |
| break; |
| } |
| |
| if (opcode == spv::Op::OpExtInstImport && wordCount >= 3) { |
| const uint32_t resultId = spirv[offset + 1]; |
| if (resultId < idBound && |
| decodeLiteralString(&spirv[offset + 2], wordCount - 2) == |
| "NonSemantic.Shader.DebugInfo.101") { |
| debugInfoImportId = resultId; |
| } |
| } else if (opcode == spv::Op::OpString && wordCount >= 3) { |
| const uint32_t resultId = spirv[offset + 1]; |
| if (resultId < idBound) |
| strings[resultId] = decodeLiteralString(&spirv[offset + 2], wordCount - 2); |
| } else if (opcode == spv::Op::OpConstant && wordCount == 4) { |
| const uint32_t resultId = spirv[offset + 2]; |
| if (resultId < idBound) { |
| constants[resultId] = spirv[offset + 3]; |
| isConstant[resultId] = true; |
| } |
| } |
| |
| offset += wordCount; |
| } |
| |
| if (debugInfoImportId == 0) |
| return false; |
| |
| const auto hasConstantValue = [&](uint32_t id, uint32_t value) { |
| return id < idBound && isConstant[id] && constants[id] == value; |
| }; |
| |
| // A DebugTypeBasic with an FPEncoding has five operands after its instruction number: |
| // Name, Size, Encoding, Flags, and FPEncoding. Including the five-word OpExtInst prefix, |
| // that gives a word count of 10. Match the imported .101 instruction set and resolve each |
| // operand ID through the OpString and OpConstant tables populated above. |
| for (size_t offset = 5; offset < end;) { |
| const uint32_t wordCount = spirv[offset] >> spv::WordCountShift; |
| const spv::Op opcode = static_cast<spv::Op>(spirv[offset] & spv::OpCodeMask); |
| if (wordCount == 0 || offset + wordCount > spirv.size()) |
| return false; |
| |
| if (opcode == spv::Op::OpExtInst && wordCount == 10 && |
| spirv[offset + 3] == debugInfoImportId && |
| spirv[offset + 4] == NonSemanticShaderDebugInfoDebugTypeBasic) { |
| const uint32_t nameId = spirv[offset + 5]; |
| const uint32_t widthId = spirv[offset + 6]; |
| const uint32_t encodingId = spirv[offset + 7]; |
| const uint32_t flagsId = spirv[offset + 8]; |
| const uint32_t fpEncodingId = spirv[offset + 9]; |
| if (nameId < idBound && strings[nameId] == expectedName && |
| hasConstantValue(widthId, expectedWidth) && |
| hasConstantValue(encodingId, NonSemanticShaderDebugInfoFloat) && |
| hasConstantValue(flagsId, NonSemanticShaderDebugInfoNone) && |
| hasConstantValue(fpEncodingId, static_cast<uint32_t>(expectedFPEncoding))) { |
| return true; |
| } |
| } |
| |
| offset += wordCount; |
| } |
| |
| return false; |
| } |
| |
| class SpvDebugInfoTest : public ::testing::Test { |
| protected: |
| bool compileToSpirvWithDebugInfo(const std::string& source, std::vector<uint32_t>& spirv, |
| std::string& error) |
| { |
| spirv.clear(); |
| error.clear(); |
| glslang::TShader shader(EShLangCompute); |
| const char* str = source.c_str(); |
| shader.setStrings(&str, 1); |
| shader.setEnvInput(glslang::EShSourceGlsl, EShLangCompute, |
| glslang::EShClientVulkan, 110); |
| shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1); |
| shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3); |
| |
| EShMessages messages = static_cast<EShMessages>(EShMsgDefault | EShMsgDebugInfo); |
| if (!shader.parse(GetDefaultResources(), 450, false, messages)) { |
| error = "COMPILATION_FAILED: " + std::string(shader.getInfoLog()); |
| return false; |
| } |
| |
| glslang::TProgram program; |
| program.addShader(&shader); |
| if (!program.link(messages)) { |
| error = "LINKING_FAILED: " + std::string(program.getInfoLog()); |
| return false; |
| } |
| |
| glslang::SpvOptions opts; |
| opts.generateDebugInfo = true; |
| opts.emitNonSemanticShaderDebugInfo = true; |
| opts.disableOptimizer = true; |
| |
| glslang::GlslangToSpv(*program.getIntermediate(EShLangCompute), spirv, &opts); |
| return true; |
| } |
| |
| // Compile a GLSL compute shader with NonSemantic debug info enabled, targeting |
| // Vulkan 1.1 / SPIR-V 1.3, and return the disassembly. |
| std::string compileWithDebugInfo(const std::string& source) |
| { |
| std::vector<uint32_t> spirv; |
| std::string error; |
| if (!compileToSpirvWithDebugInfo(source, spirv, error)) |
| return error; |
| |
| std::ostringstream out; |
| spv::Disassemble(out, spirv); |
| return out.str(); |
| } |
| }; |
| |
| // DebugTypeVectorIdEXT (opcode 109) must be emitted for OpTypeCooperativeVectorNV |
| // when NonSemantic debug info is enabled. |
| TEST_F(SpvDebugInfoTest, CooperativeVectorNVEmitsDebugTypeVectorIdEXT) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #extension GL_KHR_memory_scope_semantics : enable |
| #extension GL_NV_cooperative_vector : enable |
| #extension GL_EXT_shader_explicit_arithmetic_types : enable |
| layout(local_size_x = 64) in; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| coopvecNV<float, 8> v = coopvecNV<float, 8>(0.0); |
| v = v + v; |
| buf.data[gl_GlobalInvocationID.x] = v[0]; |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("DebugTypeVectorIdEXT"), std::string::npos) |
| << "Expected DebugTypeVectorIdEXT in disassembly.\nSPIR-V:\n" << spirv; |
| EXPECT_NE(spirv.find("TypeCooperativeVectorNV"), std::string::npos) |
| << "Expected TypeCooperativeVectorNV in disassembly.\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeVectorIdEXT must also be emitted when the component count is a |
| // specialization constant (OpSpecConstant), since the spec allows any constant |
| // instruction for the ComponentCount operand. |
| TEST_F(SpvDebugInfoTest, CooperativeVectorNVSpecConstCountEmitsDebugTypeVectorIdEXT) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #extension GL_KHR_memory_scope_semantics : enable |
| #extension GL_NV_cooperative_vector : enable |
| #extension GL_EXT_shader_explicit_arithmetic_types : enable |
| layout(local_size_x = 64) in; |
| layout(constant_id = 0) const uint N = 16; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| coopvecNV<float, N> v = coopvecNV<float, N>(1.0); |
| buf.data[gl_GlobalInvocationID.x] = v[0]; |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("DebugTypeVectorIdEXT"), std::string::npos) |
| << "Expected DebugTypeVectorIdEXT with spec-constant count.\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeCooperativeMatrixKHR (opcode 110) must replace the former |
| // opaque-composite workaround for OpTypeCooperativeMatrixKHR. |
| TEST_F(SpvDebugInfoTest, CooperativeMatrixKHREmitsDebugTypeCooperativeMatrixKHR) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #pragma use_vulkan_memory_model |
| #extension GL_KHR_memory_scope_semantics : enable |
| #extension GL_KHR_cooperative_matrix : enable |
| #extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable |
| layout(local_size_x = 32) in; |
| layout(set = 0, binding = 0) buffer B { float16_t data[]; } buf; |
| void main() { |
| coopmat<float16_t, gl_ScopeSubgroup, 16, 16, gl_MatrixUseAccumulator> m = |
| coopmat<float16_t, gl_ScopeSubgroup, 16, 16, gl_MatrixUseAccumulator>(float16_t(0.0)); |
| coopMatStore(m, buf.data, 0, 0, gl_CooperativeMatrixLayoutRowMajor); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("DebugTypeCooperativeMatrixKHR"), std::string::npos) |
| << "Expected DebugTypeCooperativeMatrixKHR in disassembly.\nSPIR-V:\n" << spirv; |
| // The old opaque-composite workaround must not appear. |
| EXPECT_EQ(spirv.find("coopmat<"), std::string::npos) |
| << "Opaque-composite workaround should not appear in disassembly.\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeCooperativeMatrixKHR must be emitted when Rows and Columns are |
| // specialization constants (OpSpecConstant), since the spec allows any constant |
| // instruction for those operands. |
| TEST_F(SpvDebugInfoTest, CooperativeMatrixKHRSpecConstDimsEmitsDebugType) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #pragma use_vulkan_memory_model |
| #extension GL_KHR_memory_scope_semantics : enable |
| #extension GL_KHR_cooperative_matrix : enable |
| #extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable |
| layout(local_size_x = 32) in; |
| layout(constant_id = 0) const uint M = 16; |
| layout(constant_id = 1) const uint N = 16; |
| layout(set = 0, binding = 0) buffer B { float16_t data[]; } buf; |
| void main() { |
| coopmat<float16_t, gl_ScopeSubgroup, M, N, gl_MatrixUseAccumulator> m = |
| coopmat<float16_t, gl_ScopeSubgroup, M, N, gl_MatrixUseAccumulator>(float16_t(0.0)); |
| coopMatStore(m, buf.data, 0, 0, gl_CooperativeMatrixLayoutRowMajor); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("DebugTypeCooperativeMatrixKHR"), std::string::npos) |
| << "Expected DebugTypeCooperativeMatrixKHR with spec-constant dims.\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeCooperativeMatrixKHR must be emitted for each distinct (component, |
| // scope, rows, cols, use) tuple -- MatrixUseA, MatrixUseB, and MatrixUseAccumulator |
| // with different component types all produce separate debug type instructions. |
| TEST_F(SpvDebugInfoTest, CooperativeMatrixKHRMultipleUsesEmitDistinctDebugTypes) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #pragma use_vulkan_memory_model |
| #extension GL_KHR_memory_scope_semantics : enable |
| #extension GL_KHR_cooperative_matrix : enable |
| #extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable |
| layout(local_size_x = 32) in; |
| layout(set = 0, binding = 0) buffer B { float16_t data[]; } buf; |
| void main() { |
| coopmat<float16_t, gl_ScopeSubgroup, 16, 16, gl_MatrixUseA> matA; |
| coopmat<float16_t, gl_ScopeSubgroup, 16, 16, gl_MatrixUseB> matB; |
| coopmat<float, gl_ScopeSubgroup, 16, 16, gl_MatrixUseAccumulator> matC = |
| coopmat<float, gl_ScopeSubgroup, 16, 16, gl_MatrixUseAccumulator>(0.0); |
| matC = coopMatMulAdd(matA, matB, matC); |
| coopMatStore(matC, buf.data, 0, 0, gl_CooperativeMatrixLayoutRowMajor); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| |
| // Count occurrences of DebugTypeCooperativeMatrixKHR: one for each distinct |
| // (component, scope, rows, cols, use) combination used above. |
| size_t count = 0; |
| size_t pos = 0; |
| while ((pos = spirv.find("DebugTypeCooperativeMatrixKHR", pos)) != std::string::npos) { |
| ++count; |
| pos += 1; |
| } |
| EXPECT_GE(count, 3u) |
| << "Expected at least 3 DebugTypeCooperativeMatrixKHR instructions.\nSPIR-V:\n" << spirv; |
| } |
| |
| // The removed golden-file test spv.debuginfo.coopmatKHR.comp combined spec-constant |
| // dimensions with all three matrix use types. Verify that distinct |
| // DebugTypeCooperativeMatrixKHR instructions are emitted for MatrixUseA (lM x lK), |
| // MatrixUseB (lK x lN), and MatrixUseAccumulator (lM x lN) when all dimensions are |
| // specialization constants. |
| TEST_F(SpvDebugInfoTest, CooperativeMatrixKHRSpecConstDimsMultipleUsesEmitDistinctDebugTypes) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #pragma use_vulkan_memory_model |
| #extension GL_KHR_memory_scope_semantics : enable |
| #extension GL_KHR_cooperative_matrix : enable |
| #extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable |
| layout(local_size_x = 32) in; |
| layout(constant_id = 0) const uint lM = 16; |
| layout(constant_id = 1) const uint lN = 16; |
| layout(constant_id = 2) const uint lK = 16; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| coopmat<float16_t, gl_ScopeSubgroup, lM, lK, gl_MatrixUseA> matA; |
| coopmat<float16_t, gl_ScopeSubgroup, lK, lN, gl_MatrixUseB> matB; |
| coopmat<float, gl_ScopeSubgroup, lM, lN, gl_MatrixUseAccumulator> matC = |
| coopmat<float, gl_ScopeSubgroup, lM, lN, gl_MatrixUseAccumulator>(0.0); |
| matC = coopMatMulAdd(matA, matB, matC); |
| coopMatStore(matC, buf.data, 0, 0, gl_CooperativeMatrixLayoutRowMajor); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| |
| size_t count = 0; |
| size_t pos = 0; |
| while ((pos = spirv.find("DebugTypeCooperativeMatrixKHR", pos)) != std::string::npos) { |
| ++count; |
| pos += 1; |
| } |
| EXPECT_GE(count, 3u) |
| << "Expected at least 3 DebugTypeCooperativeMatrixKHR instructions " |
| "(one per use type with spec-constant dimensions).\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeBasic for bfloat16_t must carry the debug type name "bfloat16_t", |
| // which is emitted only when the FPEncoding operand (BFloat16KHR = 0) is present. |
| TEST_F(SpvDebugInfoTest, BFloat16EmitsDebugTypeBasicWithFPEncoding) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #extension GL_EXT_bfloat16 : require |
| #extension GL_EXT_shader_explicit_arithmetic_types : enable |
| layout(local_size_x = 64) in; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| bfloat16_t bf = bfloat16_t(1.0); |
| buf.data[gl_GlobalInvocationID.x] = float(bf); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| // The DebugTypeBasic for bfloat16_t should contain the name "bfloat16_t". |
| EXPECT_NE(spirv.find("\"bfloat16_t\""), std::string::npos) |
| << "Expected \"bfloat16_t\" string in debug type.\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeBasic for floate4m3_t must carry the debug type name "floate4m3_t", |
| // which is emitted only when the FPEncoding operand (Float8E4M3EXT = 4214) is present. |
| TEST_F(SpvDebugInfoTest, Float8E4M3EmitsDebugTypeBasicWithFPEncoding) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #extension GL_EXT_bfloat16 : require |
| #extension GL_EXT_float_e4m3 : require |
| #extension GL_EXT_shader_explicit_arithmetic_types : enable |
| layout(local_size_x = 64) in; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| floate4m3_t f = floate4m3_t(2.0); |
| buf.data[gl_GlobalInvocationID.x] = float(f); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("\"floate4m3_t\""), std::string::npos) |
| << "Expected \"floate4m3_t\" string in debug type.\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeBasic for floate5m2_t must carry the debug type name "floate5m2_t", |
| // which is emitted only when the FPEncoding operand (Float8E5M2EXT = 4215) is present. |
| TEST_F(SpvDebugInfoTest, Float8E5M2EmitsDebugTypeBasicWithFPEncoding) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #extension GL_EXT_bfloat16 : require |
| #extension GL_EXT_float_e5m2 : require |
| #extension GL_EXT_shader_explicit_arithmetic_types : enable |
| layout(local_size_x = 64) in; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| floate5m2_t f = floate5m2_t(3.0); |
| buf.data[gl_GlobalInvocationID.x] = float(f); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("\"floate5m2_t\""), std::string::npos) |
| << "Expected \"floate5m2_t\" string in debug type.\nSPIR-V:\n" << spirv; |
| } |
| |
| // DebugTypeBasic for OCP microscaling types must carry their source-level type |
| // names, which are emitted only when the FPEncoding operand is present. |
| TEST_F(SpvDebugInfoTest, OcpMicroscalingTypesEmitDebugTypeBasicWithFPEncoding) |
| { |
| const std::string source = R"( |
| #version 450 core |
| #extension GL_EXT_float_e2m1 : require |
| #extension GL_EXT_float_e3m2 : require |
| #extension GL_EXT_float_e2m3 : require |
| #extension GL_EXT_float_ue8m0 : require |
| #extension GL_EXT_float_mxint8 : require |
| #extension GL_EXT_shader_explicit_arithmetic_types : enable |
| layout(local_size_x = 64) in; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| floate2m1_t e2m1 = floate2m1_t(1.0fe2m1); |
| floate3m2_t e3m2 = floate3m2_t(1.0fe3m2); |
| floate2m3_t e2m3 = floate2m3_t(1.0fe2m3); |
| floatue8m0_t ue8m0 = floatue8m0_t(1.0fue8m0); |
| floatmxint8_t mxint8 = floatmxint8_t(1.0fmxint8); |
| buf.data[gl_GlobalInvocationID.x] = |
| float(e2m1) + float(e3m2) + float(e2m3) + float(ue8m0) + float(mxint8); |
| } |
| )"; |
| std::vector<uint32_t> spirv; |
| std::string error; |
| ASSERT_TRUE(compileToSpirvWithDebugInfo(source, spirv, error)) << error; |
| |
| struct ExpectedDebugType { |
| const char* name; |
| uint32_t width; |
| spv::FPEncoding fpEncoding; |
| }; |
| const ExpectedDebugType expectedTypes[] = { |
| {"floate2m1_t", 4, spv::FPEncoding::Float4E2M1EXT}, |
| {"floate3m2_t", 6, spv::FPEncoding::Float6E3M2EXT}, |
| {"floate2m3_t", 6, spv::FPEncoding::Float6E2M3EXT}, |
| {"floatue8m0_t", 8, spv::FPEncoding::Float8UnsignedE8M0EXT}, |
| {"floatmxint8_t", 8, spv::FPEncoding::MXInt8EXT}, |
| }; |
| |
| std::ostringstream disassembly; |
| spv::Disassemble(disassembly, spirv); |
| for (const auto& expected : expectedTypes) { |
| EXPECT_TRUE(containsDebugTypeBasic(spirv, expected.name, expected.width, expected.fpEncoding)) |
| << "Expected DebugTypeBasic for \"" << expected.name << "\" with width " << expected.width |
| << " and FPEncoding " << static_cast<uint32_t>(expected.fpEncoding) |
| << ".\nSPIR-V:\n" << disassembly.str(); |
| } |
| } |
| |
| // When a version-101 opcode is emitted the import string must be promoted to |
| // NonSemantic.Shader.DebugInfo.101. |
| TEST_F(SpvDebugInfoTest, Version101OpcodePromotesImportStringTo101) |
| { |
| // DebugTypeCooperativeMatrixKHR is a version-101 opcode; using it must |
| // cause the import string to read ".101", not ".100". |
| const std::string source = R"( |
| #version 450 core |
| #pragma use_vulkan_memory_model |
| #extension GL_KHR_memory_scope_semantics : enable |
| #extension GL_KHR_cooperative_matrix : enable |
| #extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable |
| layout(local_size_x = 32) in; |
| layout(set = 0, binding = 0) buffer B { float16_t data[]; } buf; |
| void main() { |
| coopmat<float16_t, gl_ScopeSubgroup, 16, 16, gl_MatrixUseAccumulator> m = |
| coopmat<float16_t, gl_ScopeSubgroup, 16, 16, gl_MatrixUseAccumulator>(float16_t(0.0)); |
| coopMatStore(m, buf.data, 0, 0, gl_CooperativeMatrixLayoutRowMajor); |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("\"NonSemantic.Shader.DebugInfo.101\""), std::string::npos) |
| << "Expected import string NonSemantic.Shader.DebugInfo.101.\nSPIR-V:\n" << spirv; |
| EXPECT_EQ(spirv.find("\"NonSemantic.Shader.DebugInfo.100\""), std::string::npos) |
| << "Import string must not be .100 when version-101 opcodes are used.\nSPIR-V:\n" << spirv; |
| } |
| |
| // When only version-100 opcodes are emitted the import string must stay at |
| // NonSemantic.Shader.DebugInfo.100 and must not be promoted to .101. |
| TEST_F(SpvDebugInfoTest, Version100OnlyKeepsImportStringAt100) |
| { |
| const std::string source = R"( |
| #version 450 core |
| layout(local_size_x = 64) in; |
| layout(set = 0, binding = 0) buffer B { float data[]; } buf; |
| void main() { |
| float x = buf.data[gl_GlobalInvocationID.x]; |
| buf.data[gl_GlobalInvocationID.x] = x * 2.0; |
| } |
| )"; |
| std::string spirv = compileWithDebugInfo(source); |
| EXPECT_NE(spirv.find("\"NonSemantic.Shader.DebugInfo.100\""), std::string::npos) |
| << "Expected import string NonSemantic.Shader.DebugInfo.100.\nSPIR-V:\n" << spirv; |
| EXPECT_EQ(spirv.find("\"NonSemantic.Shader.DebugInfo.101\""), std::string::npos) |
| << "Import string must not be .101 when only version-100 opcodes are used.\nSPIR-V:\n" << spirv; |
| } |
| |
| } // namespace |
| } // namespace glslangtest |