From 00df2c6909323677926cb1d10c5f113173bdc3c3 Mon Sep 17 00:00:00 2001 From: Brendan Duncan Date: Mon, 3 Aug 2026 18:13:50 -0600 Subject: [PATCH 1/4] fix crash from static members of cbuffers --- tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 16 ++++++++- tools/clang/lib/SPIRV/SpirvEmitter.cpp | 17 ++++++++-- .../CodeGenSPIRV/cbuffer.static.member.hlsl | 34 +++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/cbuffer.static.member.hlsl diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 1a1078bf0b..7daf0635fa 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -299,6 +299,14 @@ LocationAndComponent getLocationAndComponentCount(const ASTContext &astContext, return {0, 0, false}; } +/// Returns true if the given decl is a static variable declared inside +/// a cbuffer/tbuffer/struct. These are not actually part of the +/// cbuffer/tbuffer/struct, and should not consume a member index. +bool isStaticBufferDecl(const Decl *decl) { + const auto *varDecl = dyn_cast(decl); + return varDecl && varDecl->getStorageClass() == StorageClass::SC_Static; +} + bool shouldSkipInStructLayout(const Decl *decl) { // Ignore implicit generated struct declarations/constructors/destructors if (decl->isImplicit()) @@ -1455,7 +1463,7 @@ SpirvVariable *DeclResultIdMapper::createStructOrStructArrayVarOfExplicitLayout( // Static variables are not part of the struct from a layout perspective. // Thus, they should not be listed in the struct fields. - if (fieldVar->getStorageClass() == StorageClass::SC_Static) { + if (isStaticBufferDecl(fieldVar)) { continue; } @@ -1565,6 +1573,9 @@ void DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) { continue; } + if (isStaticBufferDecl(varDecl)) + continue; + variablesToDeclare.push_back(varDecl); } @@ -1737,6 +1748,9 @@ DeclResultIdMapper::createShaderRecordBuffer(const HLSLBufferDecl *decl, if (isResourceType(varDecl->getType())) continue; + if (isStaticBufferDecl(varDecl)) + continue; + registerVariableForDecl(varDecl, createDeclSpirvInfo(bufferVar, index++)); } return bufferVar; diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index b7fd447787..e161724e69 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -1949,6 +1949,9 @@ void SpirvEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) { // supported in Vulkan for (const auto *member : bufferDecl->decls()) { if (const auto *varMember = dyn_cast(member)) { + if (varMember->getStorageClass() == StorageClass::SC_Static) + continue; + if (!spirvOptions.noWarnIgnoredFeatures) { if (const auto *init = varMember->getInit()) emitWarning("%select{tbuffer|cbuffer}0 member initializer " @@ -1978,6 +1981,12 @@ void SpirvEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) { } else { declIdMapper.createCTBuffer(bufferDecl); } + + for (const auto *member : bufferDecl->decls()) { + const auto *varMember = dyn_cast(member); + if (varMember && varMember->getStorageClass() == StorageClass::SC_Static) + doVarDecl(varMember); + } } void SpirvEmitter::doClassTemplateDecl( @@ -2173,9 +2182,11 @@ void SpirvEmitter::doVarDecl(const VarDecl *decl) { // ConstantBuffers and TextureBuffers are not HLSLBufferDecls. if (const auto *bufferDecl = dyn_cast(decl->getDeclContext())) { - // This is a VarDecl of cbuffer/tbuffer type. - doHLSLBufferDecl(bufferDecl); - return; + if (decl->getStorageClass() != StorageClass::SC_Static) { + // This is a VarDecl of cbuffer/tbuffer type. + doHLSLBufferDecl(bufferDecl); + return; + } } if (decl->getAttr()) { diff --git a/tools/clang/test/CodeGenSPIRV/cbuffer.static.member.hlsl b/tools/clang/test/CodeGenSPIRV/cbuffer.static.member.hlsl new file mode 100644 index 0000000000..3f5884e905 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/cbuffer.static.member.hlsl @@ -0,0 +1,34 @@ +// RUN: %dxc -T vs_6_0 -E main -fcgl %s -spirv | FileCheck %s + +// A `static` variable declared inside a cbuffer is an ordinary global variable, +// not a member of the buffer. It must be left out of the buffer's struct (it is +// not part of the layout) *and* it must not consume a member index, otherwise +// the access chains for the members declared after it are shifted, and the last +// one ends up out of bounds. + +// CHECK: OpMemberName %type_MyCBuffer 0 "a" +// CHECK: OpMemberName %type_MyCBuffer 1 "b" +// CHECK: OpMemberName %type_MyCBuffer 2 "c" +// CHECK-NOT: OpMemberName %type_MyCBuffer 3 + +// CHECK-DAG: %uint_10 = OpConstant %uint 10 +// CHECK-DAG: %uint_11 = OpConstant %uint 11 +// CHECK-DAG: %uint_12 = OpConstant %uint 12 + +// CHECK: %type_MyCBuffer = OpTypeStruct %uint %uint %uint + +cbuffer MyCBuffer { + uint a; + static const uint a_mode = 10; + uint b; + static const uint b_mode = 11; + uint c; + static const uint c_mode = 12; +}; + +float4 main() : SV_Position { +// CHECK: OpAccessChain %_ptr_Uniform_uint %MyCBuffer %int_0 +// CHECK: OpAccessChain %_ptr_Uniform_uint %MyCBuffer %int_1 +// CHECK: OpAccessChain %_ptr_Uniform_uint %MyCBuffer %int_2 + return float4(a + b + c, a_mode, b_mode, c_mode); +} From 626c7e2a6e70b5204e41132c3e8a9ec350bc354d Mon Sep 17 00:00:00 2001 From: Brendan Duncan Date: Mon, 3 Aug 2026 21:23:23 -0600 Subject: [PATCH 2/4] address notes --- docs/ReleaseNotes.md | 4 ++ tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 8 ++-- .../vk.shader-record-ext.static.member.hlsl | 41 +++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/vk.shader-record-ext.static.member.hlsl diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index d64b376d1d..faace72964 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -58,6 +58,10 @@ line upon naming the release. Refer to previous for appropriate section names. [#8278](https://github.com/microsoft/DirectXShaderCompiler/pull/8278). - Fixed a crash with out-of-line template declarations [#5823](https://github.com/microsoft/DirectXShaderCompiler/issues/5823). +- Fixed a crash from `static` members of a `cbuffer`/`tbuffer`/struct, which were + incorrectly counted as buffer members and shifted the member indices of the + members declared after them + [#8731](https://github.com/microsoft/DirectXShaderCompiler/pull/8731). - Fixed handling of `void` in extended instruction sets [#8012](https://github.com/microsoft/DirectXShaderCompiler/issues/8012). diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 7daf0635fa..b2fa02a42a 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1566,16 +1566,16 @@ void DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) { if (shouldSkipInStructLayout(subDecl)) continue; - // If the subDecl is a resource, it is lowered as a standalone variable. const auto *varDecl = cast(subDecl); + + if (isStaticBufferDecl(varDecl)) + continue; + if (isResourceType(varDecl->getType())) { createExternVar(varDecl); continue; } - if (isStaticBufferDecl(varDecl)) - continue; - variablesToDeclare.push_back(varDecl); } diff --git a/tools/clang/test/CodeGenSPIRV/vk.shader-record-ext.static.member.hlsl b/tools/clang/test/CodeGenSPIRV/vk.shader-record-ext.static.member.hlsl new file mode 100644 index 0000000000..44e9a05798 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.shader-record-ext.static.member.hlsl @@ -0,0 +1,41 @@ +// RUN: %dxc -T lib_6_3 -fspv-target-env=vulkan1.2 -fcgl %s -spirv | FileCheck %s + +// Same as cbuffer.static.member.hlsl, but for a shader record buffer, which +// goes through createShaderRecordBuffer() instead of createCTBuffer(). A +// `static` variable declared inside the buffer is an ordinary global variable, +// not a member of the buffer. It must be left out of the buffer's struct (it is +// not part of the layout) *and* it must not consume a member index, otherwise +// the access chains for the members declared after it are shifted, and the last +// one ends up out of bounds. + +// CHECK: OpMemberName %type_ShaderRecordBufferKHR_block 0 "a" +// CHECK: OpMemberName %type_ShaderRecordBufferKHR_block 1 "b" +// CHECK: OpMemberName %type_ShaderRecordBufferKHR_block 2 "c" +// CHECK-NOT: OpMemberName %type_ShaderRecordBufferKHR_block 3 + +// CHECK-DAG: %uint_10 = OpConstant %uint 10 +// CHECK-DAG: %uint_11 = OpConstant %uint 11 +// CHECK-DAG: %uint_12 = OpConstant %uint 12 + +// CHECK: %type_ShaderRecordBufferKHR_block = OpTypeStruct %uint %uint %uint + +[[vk::shader_record_ext]] +cbuffer block { + uint a; + static const uint a_mode = 10; + uint b; + static const uint b_mode = 11; + uint c; + static const uint c_mode = 12; +} + +struct Payload { float p; }; +struct Attr { float a; }; + +[shader("closesthit")] +void main(inout Payload P, in Attr A) { +// CHECK: OpAccessChain %_ptr_ShaderRecordBufferKHR_uint %block %int_0 +// CHECK: OpAccessChain %_ptr_ShaderRecordBufferKHR_uint %block %int_1 +// CHECK: OpAccessChain %_ptr_ShaderRecordBufferKHR_uint %block %int_2 + P.p = a + b + c + a_mode + b_mode + c_mode; +} From 4c81e42d69a0f09bdf585df5f043ed8f3080cd35 Mon Sep 17 00:00:00 2001 From: Brendan Duncan Date: Mon, 3 Aug 2026 21:39:46 -0600 Subject: [PATCH 3/4] update release notes --- docs/ReleaseNotes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index faace72964..413e37d514 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -58,10 +58,10 @@ line upon naming the release. Refer to previous for appropriate section names. [#8278](https://github.com/microsoft/DirectXShaderCompiler/pull/8278). - Fixed a crash with out-of-line template declarations [#5823](https://github.com/microsoft/DirectXShaderCompiler/issues/5823). -- Fixed a crash from `static` members of a `cbuffer`/`tbuffer`/struct, which were +- Fixed a crash from `static` members of a `cbuffer`/`tbuffer`, which were incorrectly counted as buffer members and shifted the member indices of the members declared after them - [#8731](https://github.com/microsoft/DirectXShaderCompiler/pull/8731). + [#8537](https://github.com/microsoft/DirectXShaderCompiler/issues/8537). - Fixed handling of `void` in extended instruction sets [#8012](https://github.com/microsoft/DirectXShaderCompiler/issues/8012). From a1762529a072e2cba7cfeed12ada6dc7fd91e754 Mon Sep 17 00:00:00 2001 From: Brendan Duncan Date: Tue, 4 Aug 2026 11:04:04 -0600 Subject: [PATCH 4/4] sync main, move release notes to upcoming --- docs/ReleaseNotes.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 4cae1bcf8c..065e19667d 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -34,6 +34,13 @@ line upon naming the release. Refer to previous for appropriate section names. instead of crashing [#6661](https://github.com/microsoft/DirectXShaderCompiler/issues/6661). +#### SPIR-V + +- Fixed a crash from `static` members of a `cbuffer`/`tbuffer`, which were + incorrectly counted as buffer members and shifted the member indices of the + members declared after them + [#8537](https://github.com/microsoft/DirectXShaderCompiler/issues/8537). + #### Bug Fixes - Fixed internal compiler errors when a member method is called on a ray payload @@ -97,10 +104,6 @@ first shipped in the 1.10.2605 preview. [#8278](https://github.com/microsoft/DirectXShaderCompiler/pull/8278). - Fixed a crash with out-of-line template declarations [#5823](https://github.com/microsoft/DirectXShaderCompiler/issues/5823). -- Fixed a crash from `static` members of a `cbuffer`/`tbuffer`, which were - incorrectly counted as buffer members and shifted the member indices of the - members declared after them - [#8537](https://github.com/microsoft/DirectXShaderCompiler/issues/8537). - Fixed handling of `void` in extended instruction sets [#8012](https://github.com/microsoft/DirectXShaderCompiler/issues/8012).