Check that certain decorations cannot be used more than once and/or are mutually exclusive (#2171)

Fixes #1636

* Add a hash functor for decoration types for c++11 compliance
* Change non-POD static variables and add test for Block+BufferBlock
This commit is contained in:
Alejandro Lopez 2018-12-07 12:46:27 -05:00 committed by alan-baker
parent 6679d5df89
commit de797ddcb5
2 changed files with 333 additions and 0 deletions

View File

@ -17,7 +17,9 @@
#include <algorithm>
#include <cassert>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@ -44,6 +46,13 @@ struct PairHash {
}
};
// A functor for hashing decoration types.
struct SpvDecorationHash {
std::size_t operator()(SpvDecoration dec) const {
return static_cast<std::size_t>(dec);
}
};
// Struct member layout attributes that are inherited through arrays.
struct LayoutConstraints {
explicit LayoutConstraints(
@ -941,6 +950,109 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
return SPV_SUCCESS;
}
spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
using AtMostOnceSet = std::unordered_set<SpvDecoration, SpvDecorationHash>;
using MutuallyExclusiveSets =
std::vector<std::unordered_set<SpvDecoration, SpvDecorationHash>>;
using PerIDKey = std::tuple<SpvDecoration, uint32_t>;
using PerMemberKey = std::tuple<SpvDecoration, uint32_t, uint32_t>;
using DecorationNameTable =
std::unordered_map<SpvDecoration, std::string, SpvDecorationHash>;
static const auto* const at_most_once_per_id = new AtMostOnceSet{
SpvDecorationArrayStride,
};
static const auto* const at_most_once_per_member = new AtMostOnceSet{
SpvDecorationOffset,
SpvDecorationMatrixStride,
SpvDecorationRowMajor,
SpvDecorationColMajor,
};
static const auto* const mutually_exclusive_per_id =
new MutuallyExclusiveSets{
{SpvDecorationBlock, SpvDecorationBufferBlock},
};
static const auto* const mutually_exclusive_per_member =
new MutuallyExclusiveSets{
{SpvDecorationRowMajor, SpvDecorationColMajor},
};
// For printing the decoration name.
static const auto* const decoration_name = new DecorationNameTable{
{SpvDecorationArrayStride, "ArrayStride"},
{SpvDecorationOffset, "Offset"},
{SpvDecorationMatrixStride, "MatrixStride"},
{SpvDecorationRowMajor, "RowMajor"},
{SpvDecorationColMajor, "ColMajor"},
{SpvDecorationBlock, "Block"},
{SpvDecorationBufferBlock, "BufferBlock"},
};
std::set<PerIDKey> seen_per_id;
std::set<PerMemberKey> seen_per_member;
for (const auto& inst : vstate.ordered_instructions()) {
const auto& words = inst.words();
if (SpvOpDecorate == inst.opcode()) {
const auto id = words[1];
const auto dec_type = static_cast<SpvDecoration>(words[2]);
const auto k = PerIDKey(dec_type, id);
const auto already_used = !seen_per_id.insert(k).second;
if (already_used &&
at_most_once_per_id->find(dec_type) != at_most_once_per_id->end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "' decorated with "
<< decoration_name->at(dec_type)
<< " multiple times is not allowed.";
}
// Verify certain mutually exclusive decorations are not both applied on
// an ID.
for (const auto& s : *mutually_exclusive_per_id) {
if (s.find(dec_type) == s.end()) continue;
for (auto excl_dec_type : s) {
if (excl_dec_type == dec_type) continue;
const auto excl_k = PerIDKey(excl_dec_type, id);
if (seen_per_id.find(excl_k) != seen_per_id.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "' decorated with both "
<< decoration_name->at(dec_type) << " and "
<< decoration_name->at(excl_dec_type) << " is not allowed.";
}
}
}
} else if (SpvOpMemberDecorate == inst.opcode()) {
const auto id = words[1];
const auto member_id = words[2];
const auto dec_type = static_cast<SpvDecoration>(words[3]);
const auto k = PerMemberKey(dec_type, id, member_id);
const auto already_used = !seen_per_member.insert(k).second;
if (already_used && at_most_once_per_member->find(dec_type) !=
at_most_once_per_member->end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "', member '" << member_id
<< "' decorated with " << decoration_name->at(dec_type)
<< " multiple times is not allowed.";
}
// Verify certain mutually exclusive decorations are not both applied on
// a (ID, member) tuple.
for (const auto& s : *mutually_exclusive_per_member) {
if (s.find(dec_type) == s.end()) continue;
for (auto excl_dec_type : s) {
if (excl_dec_type == dec_type) continue;
const auto excl_k = PerMemberKey(excl_dec_type, id, member_id);
if (seen_per_member.find(excl_k) != seen_per_member.end()) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "ID '" << id << "', member '" << member_id
<< "' decorated with both " << decoration_name->at(dec_type)
<< " and " << decoration_name->at(excl_dec_type)
<< " is not allowed.";
}
}
}
}
}
return SPV_SUCCESS;
}
spv_result_t CheckVulkanMemoryModelDeprecatedDecorations(
ValidationState_t& vstate) {
if (vstate.memory_model() != SpvMemoryModelVulkanKHR) return SPV_SUCCESS;
@ -1108,6 +1220,7 @@ spv_result_t ValidateDecorations(ValidationState_t& vstate) {
if (auto error = CheckImportedVariableInitialization(vstate)) return error;
if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error;
if (auto error = CheckDecorationsOfBuffers(vstate)) return error;
if (auto error = CheckDecorationsCompatibility(vstate)) return error;
if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error;
if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
return error;

View File

@ -4590,6 +4590,226 @@ OpFunctionEnd
" %call = OpFunctionCall %void %myfunc"));
}
TEST_F(ValidateDecorations, MultipleOffsetDecorationsOnSameID) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 0 Offset 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ID '2', member '0' decorated with Offset multiple "
"times is not allowed."));
}
TEST_F(ValidateDecorations, MultipleArrayStrideDecorationsOnSameID) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %array ArrayStride 4
OpDecorate %array ArrayStride 4
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%uint = OpTypeInt 32 0
%uint_4 = OpConstant %uint 4
%array = OpTypeArray %float %uint_4
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ID '2' decorated with ArrayStride multiple "
"times is not allowed."));
}
TEST_F(ValidateDecorations, MultipleMatrixStrideDecorationsOnSameID) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 0 ColMajor
OpMemberDecorate %struct 0 MatrixStride 16
OpMemberDecorate %struct 0 MatrixStride 16
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%fvec4 = OpTypeVector %float 4
%fmat4 = OpTypeMatrix %fvec4 4
%struct = OpTypeStruct %fmat4
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ID '2', member '0' decorated with MatrixStride "
"multiple times is not allowed."));
}
TEST_F(ValidateDecorations, MultipleRowMajorDecorationsOnSameID) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 0 MatrixStride 16
OpMemberDecorate %struct 0 RowMajor
OpMemberDecorate %struct 0 RowMajor
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%fvec4 = OpTypeVector %float 4
%fmat4 = OpTypeMatrix %fvec4 4
%struct = OpTypeStruct %fmat4
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ID '2', member '0' decorated with RowMajor multiple "
"times is not allowed."));
}
TEST_F(ValidateDecorations, MultipleColMajorDecorationsOnSameID) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 0 MatrixStride 16
OpMemberDecorate %struct 0 ColMajor
OpMemberDecorate %struct 0 ColMajor
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%fvec4 = OpTypeVector %float 4
%fmat4 = OpTypeMatrix %fvec4 4
%struct = OpTypeStruct %fmat4
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ID '2', member '0' decorated with ColMajor multiple "
"times is not allowed."));
}
TEST_F(ValidateDecorations, RowMajorAndColMajorDecorationsOnSameID) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 0 MatrixStride 16
OpMemberDecorate %struct 0 ColMajor
OpMemberDecorate %struct 0 RowMajor
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%fvec4 = OpTypeVector %float 4
%fmat4 = OpTypeMatrix %fvec4 4
%struct = OpTypeStruct %fmat4
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ID '2', member '0' decorated with both RowMajor and "
"ColMajor is not allowed."));
}
TEST_F(ValidateDecorations, BlockAndBufferBlockDecorationsOnSameID) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpDecorate %struct BufferBlock
OpMemberDecorate %struct 0 Offset 0
OpMemberDecorate %struct 0 MatrixStride 16
OpMemberDecorate %struct 0 RowMajor
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%fvec4 = OpTypeVector %float 4
%fmat4 = OpTypeMatrix %fvec4 4
%struct = OpTypeStruct %fmat4
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"ID '2' decorated with both BufferBlock and Block is not allowed."));
}
} // namespace
} // namespace val
} // namespace spvtools