Validate PushConstants annotation and type (#2140)
* Validate PushConstants have Block annotation and are struct or array of structs
* Add passing test and split into universal/vulkan environment tests
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index a5c2b62..96dd33d 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -795,6 +795,19 @@
MemberConstraints constraints;
ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
vstate);
+
+ // Vulkan 14.5.1: Check Block annotation for PushConstant variables.
+ if (spvIsVulkanEnv(vstate.context()->target_env)) {
+ if (push_constant && !hasDecoration(id, SpvDecorationBlock, vstate)) {
+ return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
+ << "PushConstant id '" << id
+ << "' is missing Block decoration.\n"
+ << "From Vulkan spec, section 14.5.1:\n"
+ << "Such variables must be identified with a Block "
+ "decoration";
+ }
+ }
+
// Prepare for messages
const char* sc_str =
uniform ? "Uniform"
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 8bb9eed..21b8c8b 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -73,9 +73,9 @@
bool HaveLayoutCompatibleMembers(ValidationState_t& _, const Instruction* type1,
const Instruction* type2) {
assert(type1->opcode() == SpvOpTypeStruct &&
- "type1 must be and OpTypeStruct instruction.");
+ "type1 must be an OpTypeStruct instruction.");
assert(type2->opcode() == SpvOpTypeStruct &&
- "type2 must be and OpTypeStruct instruction.");
+ "type2 must be an OpTypeStruct instruction.");
const auto& type1_operands = type1->operands();
const auto& type2_operands = type2->operands();
if (type1_operands.size() != type2_operands.size()) {
@@ -100,9 +100,9 @@
bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1,
const Instruction* type2) {
assert(type1->opcode() == SpvOpTypeStruct &&
- "type1 must be and OpTypeStruct instruction.");
+ "type1 must be an OpTypeStruct instruction.");
assert(type2->opcode() == SpvOpTypeStruct &&
- "type2 must be and OpTypeStruct instruction.");
+ "type2 must be an OpTypeStruct instruction.");
const std::vector<Decoration>& type1_decorations =
_.id_decorations(type1->id());
const std::vector<Decoration>& type2_decorations =
@@ -373,8 +373,20 @@
}
}
+ // Vulkan 14.5.1: Check type of PushConstant variables.
// Vulkan 14.5.2: Check type of UniformConstant and Uniform variables.
if (spvIsVulkanEnv(_.context()->target_env)) {
+ if (storage_class == SpvStorageClassPushConstant) {
+ if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "PushConstant OpVariable <id> '" << _.getIdName(inst->id())
+ << "' has illegal type.\n"
+ << "From Vulkan spec, section 14.5.1:\n"
+ << "Such variables must be typed as OpTypeStruct, "
+ << "or an array of this type";
+ }
+ }
+
if (storage_class == SpvStorageClassUniformConstant) {
if (!IsAllowedTypeOrArrayOfSame(
_, pointee,
@@ -382,7 +394,7 @@
SpvOpTypeAccelerationStructureNV})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "UniformConstant OpVariable <id> '" << _.getIdName(inst->id())
- << " 'has illegal type.\n"
+ << "' has illegal type.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< "Variables identified with the UniformConstant storage class "
<< "are used only as handles to refer to opaque resources. Such "
@@ -396,7 +408,7 @@
if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Uniform OpVariable <id> '" << _.getIdName(inst->id())
- << " 'has illegal type.\n"
+ << "' has illegal type.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< "Variables identified with the Uniform storage class are "
"used "
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index bbf7e94..c67cf45 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -2219,6 +2219,65 @@
"rules: member 1 at offset 4 is not aligned to 16"));
}
+TEST_F(ValidateDecorations, PushConstantMissingBlockGood) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+
+ OpMemberDecorate %struct 0 Offset 0
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %struct = OpTypeStruct %float
+ %ptr = OpTypePointer PushConstant %struct
+ %pc = OpVariable %ptr PushConstant
+
+ %1 = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
+ << getDiagnosticString();
+}
+
+TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+
+ OpMemberDecorate %struct 0 Offset 0
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %struct = OpTypeStruct %float
+ %ptr = OpTypePointer PushConstant %struct
+ %pc = OpVariable %ptr PushConstant
+
+ %1 = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID,
+ ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("PushConstant id '2' is missing Block decoration.\n"
+ "From Vulkan spec, section 14.5.1:\n"
+ "Such variables must be identified with a Block "
+ "decoration"));
+}
+
TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) {
// Spot check buffer rules when using StorageBuffer storage class with Block
// decoration.
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index a2e8bf3..8001523 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -838,6 +838,82 @@
"The Struture's type in OpArrayLength <id> '12' must be a pointer to "
"an OpTypeStruct.\n %12 = OpArrayLength %uint %11 0\n"));
}
+
+TEST_F(ValidateMemory, PushConstantNotStructGood) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %ptr = OpTypePointer PushConstant %float
+ %pc = OpVariable %ptr PushConstant
+
+ %1 = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(spirv);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMemory, VulkanPushConstantNotStructBad) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %ptr = OpTypePointer PushConstant %float
+ %pc = OpVariable %ptr PushConstant
+
+ %1 = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("PushConstant OpVariable <id> '6' has illegal type.\n"
+ "From Vulkan spec, section 14.5.1:\n"
+ "Such variables must be typed as OpTypeStruct, "
+ "or an array of this type"));
+}
+
+TEST_F(ValidateMemory, VulkanPushConstant) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+
+ OpDecorate %struct Block
+ OpMemberDecorate %struct 0 Offset 0
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %struct = OpTypeStruct %float
+ %ptr = OpTypePointer PushConstant %struct
+ %pc = OpVariable %ptr PushConstant
+
+ %1 = OpFunction %void None %voidfn
+ %label = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
} // namespace
} // namespace val
} // namespace spvtools