Skip to content

Commit

Permalink
val: interface struct with builtins must be Block (#4665)
Browse files Browse the repository at this point in the history
For a shader input/output interface variable of structure type:
If the structure has BuiltIn members, then the structure type
must be Block decorated.

Otherwise, the variable, or the struct members must have locations,
but nothing can have both a location be a BuiltIn.

Implements validation needed to reject the example in
KhronosGroup/SPIRV-Registry#134
  • Loading branch information
dneto0 authored Dec 16, 2021
1 parent 5d0e324 commit df2aad6
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 8 deletions.
31 changes: 25 additions & 6 deletions source/val/validate_decorations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) {
});
}

// Returns true if the given structure type has a Block decoration.
bool isBlock(uint32_t struct_id, ValidationState_t& vstate) {
const auto& decorations = vstate.id_decorations(struct_id);
return std::any_of(
decorations.begin(), decorations.end(),
[](const Decoration& d) { return SpvDecorationBlock == d.dec_type(); });
}

// Returns true if the given ID has the Import LinkageAttributes decoration.
bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) {
const auto& decorations = vstate.id_decorations(id);
Expand Down Expand Up @@ -716,8 +724,8 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) {
spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
for (uint32_t entry_point : vstate.entry_points()) {
const auto& descs = vstate.entry_point_descriptions(entry_point);
int num_builtin_inputs = 0;
int num_builtin_outputs = 0;
int num_builtin_block_inputs = 0;
int num_builtin_block_outputs = 0;
int num_workgroup_variables = 0;
int num_workgroup_variables_with_block = 0;
int num_workgroup_variables_with_aliased = 0;
Expand Down Expand Up @@ -768,9 +776,20 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
Instruction* type_instr = vstate.FindDef(type_id);
if (type_instr && SpvOpTypeStruct == type_instr->opcode() &&
isBuiltInStruct(type_id, vstate)) {
if (storage_class == SpvStorageClassInput) ++num_builtin_inputs;
if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs;
if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break;
if (!isBlock(type_id, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_DATA, vstate.FindDef(type_id))
<< vstate.VkErrorID(4919)
<< "Interface struct has no Block decoration but has "
"BuiltIn members. "
"Location decorations must be used on each member of "
"OpVariable with a structure type that is a block not "
"decorated with Location.";
}
if (storage_class == SpvStorageClassInput) ++num_builtin_block_inputs;
if (storage_class == SpvStorageClassOutput)
++num_builtin_block_outputs;
if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1)
break;
if (auto error = CheckBuiltInVariable(interface, vstate))
return error;
} else if (isBuiltInVar(interface, vstate)) {
Expand All @@ -788,7 +807,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
}
}
}
if (num_builtin_inputs > 1 || num_builtin_outputs > 1) {
if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) {
return vstate.diag(SPV_ERROR_INVALID_BINARY,
vstate.FindDef(entry_point))
<< "There must be at most one object per Storage Class that can "
Expand Down
17 changes: 15 additions & 2 deletions test/val/val_builtins_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ CodeGenerator GetInMainCodeGenerator(const char* const built_in,
generator.extensions_ += extensions;
}

generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ = R"(OpDecorate %built_in_type Block
OpMemberDecorate %built_in_type 0 BuiltIn )";
generator.before_types_ += built_in;
generator.before_types_ += "\n";

Expand Down Expand Up @@ -251,7 +252,8 @@ CodeGenerator GetInFunctionCodeGenerator(const char* const built_in,
generator.extensions_ += extensions;
}

generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn ";
generator.before_types_ = R"(OpDecorate %built_in_type Block
OpMemberDecorate %built_in_type 0 BuiltIn )";
generator.before_types_ += built_in;
generator.before_types_ += "\n";

Expand Down Expand Up @@ -3098,6 +3100,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsFirstFails) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();

generator.before_types_ = R"(
OpDecorate %input_type Block
OpDecorate %output_type Block
OpMemberDecorate %input_type 0 BuiltIn FragCoord
OpMemberDecorate %output_type 0 BuiltIn Position
)";
Expand Down Expand Up @@ -3138,6 +3142,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsSecondFails) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();

generator.before_types_ = R"(
OpDecorate %input_type Block
OpDecorate %output_type Block
OpMemberDecorate %input_type 0 BuiltIn Position
OpMemberDecorate %output_type 0 BuiltIn FragCoord
)";
Expand Down Expand Up @@ -3201,6 +3207,7 @@ OpStore %position %f32vec4_0123
TEST_F(ValidateBuiltIns, FragmentPositionTwoEntryPoints) {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();
generator.before_types_ = R"(
OpDecorate %output_type Block
OpMemberDecorate %output_type 0 BuiltIn Position
)";

Expand Down Expand Up @@ -3252,6 +3259,7 @@ CodeGenerator GetNoDepthReplacingGenerator() {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();

generator.before_types_ = R"(
OpDecorate %output_type Block
OpMemberDecorate %output_type 0 BuiltIn FragDepth
)";

Expand Down Expand Up @@ -3303,6 +3311,7 @@ CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator() {
CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator();

generator.before_types_ = R"(
OpDecorate %output_type Block
OpMemberDecorate %output_type 0 BuiltIn FragDepth
)";

Expand Down Expand Up @@ -3374,6 +3383,7 @@ OpExtension "SPV_NV_ray_tracing"
)";

generator.before_types_ = R"(
OpDecorate %input_type Block
OpMemberDecorate %input_type 0 BuiltIn InstanceId
)";

Expand Down Expand Up @@ -3609,6 +3619,7 @@ OpCapability GroupNonUniformBallot
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %foo "foo"
OpExecutionMode %foo LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 BuiltIn SubgroupEqMask
%void = OpTypeVoid
%int = OpTypeInt 32 0
Expand Down Expand Up @@ -3663,6 +3674,7 @@ OpCapability GroupNonUniform
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %foo "foo"
OpExecutionMode %foo LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 BuiltIn SubgroupSize
%void = OpTypeVoid
%int = OpTypeInt 32 0
Expand Down Expand Up @@ -3723,6 +3735,7 @@ OpCapability GroupNonUniform
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %foo "foo"
OpExecutionMode %foo LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 BuiltIn SubgroupId
%void = OpTypeVoid
%int = OpTypeInt 32 0
Expand Down
7 changes: 7 additions & 0 deletions test/val/val_decoration_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) {
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %_struct_1 Block
OpMemberDecorate %_struct_1 0 BuiltIn Position
OpMemberDecorate %_struct_1 1 BuiltIn Position
OpMemberDecorate %_struct_1 2 BuiltIn Position
Expand All @@ -243,6 +244,7 @@ TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) {
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %_struct_1 Block
OpMemberDecorate %_struct_1 0 BuiltIn Position
OpMemberDecorate %_struct_1 1 BuiltIn Position
%float = OpTypeFloat 32
Expand All @@ -265,6 +267,7 @@ TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) {
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %_struct_1 Block
OpMemberDecorate %_struct_1 0 BuiltIn Position
OpMemberDecorate %_struct_1 1 BuiltIn Position
OpMemberDecorate %_struct_1 2 BuiltIn Position
Expand Down Expand Up @@ -305,6 +308,8 @@ TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) {
OpEntryPoint Geometry %main "main" %in_1 %in_2
OpExecutionMode %main InputPoints
OpExecutionMode %main OutputPoints
OpDecorate %struct_1 Block
OpDecorate %struct_2 Block
OpMemberDecorate %struct_1 0 BuiltIn InvocationId
OpMemberDecorate %struct_2 0 BuiltIn Position
%int = OpTypeInt 32 1
Expand Down Expand Up @@ -339,6 +344,8 @@ TEST_F(ValidateDecorations,
OpEntryPoint Geometry %main "main" %in_1 %out_1
OpExecutionMode %main InputPoints
OpExecutionMode %main OutputPoints
OpDecorate %struct_1 Block
OpDecorate %struct_2 Block
OpMemberDecorate %struct_1 0 BuiltIn InvocationId
OpMemberDecorate %struct_2 0 BuiltIn Position
%int = OpTypeInt 32 1
Expand Down
36 changes: 36 additions & 0 deletions test/val/val_interfaces_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,42 @@ OpFunctionEnd
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
}

TEST_F(ValidateInterfacesTest, StructWithBuiltinsMissingBlock_Bad) {
// See https://github.com/KhronosGroup/SPIRV-Registry/issues/134
//
// When a shader input or output is a struct that does not have Block,
// then it must have a Location.
// But BuiltIns must not have locations.
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %in
OpExecutionMode %main OriginUpperLeft
; %struct needs a Block decoration
OpMemberDecorate %struct 0 BuiltIn Position
%void = OpTypeVoid
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%struct = OpTypeStruct %v4float
%in_ptr = OpTypePointer Input %struct
%in = OpVariable %in_ptr Input
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";

CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(),
AnyVUID("VUID-StandaloneSpirv-Location-04919"));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Interface struct has no Block decoration but has BuiltIn members."));
}

} // namespace
} // namespace val
} // namespace spvtools

0 comments on commit df2aad6

Please sign in to comment.