blob: a640ee79ca77f38b85d6518ff47483d9d250f181 [file] [edit]
// 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