Skip to content

Commit 4e18ca3

Browse files
authored
[SPIR-V] Fix invalid codegen for empty cbuffers (#7717)
Each variable in the Uniform/Uniform Constant storage class must be decorated with DescriptorSet + Binding. -> VUID-StandaloneSpirv-UniformConstant-06677 When a cbuffer was empty (or only contained resources), an empty struct and variable in the uniform storage class was created. Because the cbuffer was empty, no binding/descriptor set was assigned, hence no decoration was emitted. This was fine as long as we compiled with optimizations on, as DCE would get rid of this unused cbuffer. But compiling with -O0 would yield a validation error. Fixes #7681
1 parent dbcbac9 commit 4e18ca3

File tree

5 files changed

+52
-34
lines changed

5 files changed

+52
-34
lines changed

tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,7 +1381,6 @@ SpirvVariable *DeclResultIdMapper::createStructOrStructArrayVarOfExplicitLayout(
13811381
}
13821382

13831383
if (isResourceType(varType)) {
1384-
createExternVar(fieldVar);
13851384
continue;
13861385
}
13871386
}
@@ -1473,7 +1472,28 @@ void DeclResultIdMapper::createEnumConstant(const EnumConstantDecl *decl) {
14731472
astDecls[valueDecl] = createDeclSpirvInfo(varInstr);
14741473
}
14751474

1476-
SpirvVariable *DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
1475+
void DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
1476+
1477+
SmallVector<const VarDecl *, 4> variablesToDeclare;
1478+
for (const auto *subDecl : decl->decls()) {
1479+
if (shouldSkipInStructLayout(subDecl))
1480+
continue;
1481+
1482+
// If the subDecl is a resource, it is lowered as a standalone variable.
1483+
const auto *varDecl = cast<VarDecl>(subDecl);
1484+
if (isResourceType(varDecl->getType())) {
1485+
createExternVar(varDecl);
1486+
continue;
1487+
}
1488+
1489+
variablesToDeclare.push_back(varDecl);
1490+
}
1491+
1492+
// If the cbuffer is empty or only contains resources, skip the variable
1493+
// creation.
1494+
if (variablesToDeclare.size() == 0)
1495+
return;
1496+
14771497
// This function handles creation of cbuffer or tbuffer.
14781498
const auto usageKind =
14791499
decl->isCBuffer() ? ContextUsageKind::CBuffer : ContextUsageKind::TBuffer;
@@ -1486,30 +1506,16 @@ SpirvVariable *DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
14861506
// mapped to the <result-id> of the buffer object, which means when querying
14871507
// querying the <result-id> for a certain VarDecl, we need to do an extra
14881508
// OpAccessChain.
1489-
int index = 0;
1490-
for (const auto *subDecl : decl->decls()) {
1491-
if (shouldSkipInStructLayout(subDecl))
1492-
continue;
1493-
1494-
// If subDecl is a variable with resource type, we already added a separate
1495-
// OpVariable for it in createStructOrStructArrayVarOfExplicitLayout().
1496-
const auto *varDecl = cast<VarDecl>(subDecl);
1497-
if (isResourceType(varDecl->getType()))
1498-
continue;
1509+
for (unsigned I = 0; I < variablesToDeclare.size(); ++I)
1510+
registerVariableForDecl(variablesToDeclare[I],
1511+
createDeclSpirvInfo(bufferVar, I));
14991512

1500-
registerVariableForDecl(varDecl, createDeclSpirvInfo(bufferVar, index++));
1501-
}
1502-
// If it does not contains a member with non-resource type, we do not want to
1503-
// set a dedicated binding number.
1504-
if (index != 0) {
1505-
resourceVars.emplace_back(
1506-
bufferVar, decl, decl->getLocation(), getResourceBinding(decl),
1507-
decl->getAttr<VKBindingAttr>(), decl->getAttr<VKCounterBindingAttr>());
1508-
}
1513+
resourceVars.emplace_back(
1514+
bufferVar, decl, decl->getLocation(), getResourceBinding(decl),
1515+
decl->getAttr<VKBindingAttr>(), decl->getAttr<VKCounterBindingAttr>());
15091516

1510-
if (!spirvOptions.debugInfoRich) {
1511-
return bufferVar;
1512-
}
1517+
if (!spirvOptions.debugInfoRich)
1518+
return;
15131519

15141520
auto *dbgGlobalVar = createDebugGlobalVariable(
15151521
bufferVar, QualType(), decl->getLocation(), decl->getName());
@@ -1529,7 +1535,7 @@ SpirvVariable *DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
15291535
// mapping.
15301536
spvContext.registerStructDeclForSpirvType(resultType, decl);
15311537

1532-
return bufferVar;
1538+
return;
15331539
}
15341540

15351541
SpirvVariable *DeclResultIdMapper::createPushConstant(const VarDecl *decl) {

tools/clang/lib/SPIRV/DeclResultIdMapper.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ class DeclResultIdMapper {
326326
/// for the whole buffer. When we refer to the field VarDecl later, we need
327327
/// to do an extra OpAccessChain to get its pointer from the SPIR-V variable
328328
/// standing for the whole buffer.
329-
SpirvVariable *createCTBuffer(const HLSLBufferDecl *decl);
329+
void createCTBuffer(const HLSLBufferDecl *decl);
330330

331331
/// \brief Creates a PushConstant block from the given decl.
332332
SpirvVariable *createPushConstant(const VarDecl *decl);

tools/clang/lib/SPIRV/SpirvEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1942,7 +1942,7 @@ void SpirvEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
19421942
bufferDecl,
19431943
DeclResultIdMapper::ContextUsageKind::ShaderRecordBufferKHR);
19441944
} else {
1945-
(void)declIdMapper.createCTBuffer(bufferDecl);
1945+
declIdMapper.createCTBuffer(bufferDecl);
19461946
}
19471947
}
19481948

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %dxc -T ps_6_0 -E main -O0 %s -spirv | FileCheck %s
2+
3+
// CHECK-NOT: %empty = OpVariable %_ptr_Uniform_type_empty Uniform
4+
cbuffer empty {
5+
};
6+
7+
float4 main(float2 uv : TEXCOORD) : SV_TARGET {
8+
return uv.xyxy;
9+
}

tools/clang/test/CodeGenSPIRV/type.cbuffer.including.resource.hlsl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: %dxc -T ps_6_0 -E main -fcgl -Vd %s -spirv | FileCheck %s
2-
// FIXME(7681): Enable validation again once codegen is fixed.
1+
// RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv | FileCheck %s
32

43
// CHECK-NOT: OpDecorate %buf3
54

@@ -16,28 +15,32 @@
1615
// CHECK: OpDecorate %w DescriptorSet 0
1716
// CHECK: OpDecorate %w Binding 5
1817

19-
// CHECK: %type_buf0 = OpTypeStruct %v4float
18+
// CHECK: %type_buf0 = OpTypeStruct %v4float
19+
// CHECK: %type_buf1 = OpTypeStruct %v4float
20+
// CHECK: %type_buf2 = OpTypeStruct %v4float
21+
// CHECK-NOT: %type_buf3 = OpTypeStruct
22+
23+
// CHECK: %buf0 = OpVariable %_ptr_Uniform_type_buf0 Uniform
2024
cbuffer buf0 : register(b0) {
2125
float4 foo;
2226
};
2327

24-
// CHECK: %type_buf1 = OpTypeStruct %v4float
28+
// CHECK: %buf1 = OpVariable %_ptr_Uniform_type_buf1 Uniform
2529
cbuffer buf1 : register(b4) {
2630
float4 bar;
2731
};
2832

29-
// CHECK: %type_buf2 = OpTypeStruct %v4float
30-
// CHECK: %type_buf3 = OpTypeStruct
31-
3233
// CHECK: %y = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
3334
// CHECK: %z = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
35+
// CHECK: %buf2 = OpVariable %_ptr_Uniform_type_buf2 Uniform
3436
cbuffer buf2 {
3537
float4 x;
3638
Texture2D y;
3739
SamplerState z;
3840
};
3941

4042
// CHECK: %w = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
43+
// CHECK-NOT: %buf3 = OpVariable %_ptr_Uniform_type_buf3 Uniform
4144
cbuffer buf3 : register(b2) {
4245
SamplerState w;
4346
}

0 commit comments

Comments
 (0)