Add Vulkan validation rules for BuiltIn variables

Added a framework for validation of BuiltIn variables. The framework
allows implementation of flexible abstract rules which are required for
built-ins as the information (decoration, definition, reference) is not
in one place, but is scattered all over the module.

Validation rules are implemented as a map
id -> list<functor(instrution)>

Ids which are dependent on built-in types or objects receive a task
list, such as "this id cannot be referenced from function which is
called from entry point with execution model X; propagate this rule
to your descendants in the global scope".

Also refactored test/val/val_fixtures.

All built-ins covered by tests
This commit is contained in:
Andrey Tuganov 2018-02-07 11:50:26 -05:00 committed by David Neto
parent 045cc8f75b
commit fe9121f721
13 changed files with 4283 additions and 125 deletions

View File

@ -39,6 +39,7 @@ SPVTOOLS_SRC_FILES := \
source/validate_atomics.cpp \
source/validate_barriers.cpp \
source/validate_bitwise.cpp \
source/validate_builtins.cpp \
source/validate_capability.cpp \
source/validate_cfg.cpp \
source/validate_composites.cpp \

View File

@ -284,6 +284,7 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/validate_atomics.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_barriers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_bitwise.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_builtins.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_capability.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_cfg.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_composites.cpp

View File

@ -66,6 +66,17 @@ class AssemblyGrammar {
spv_result_t lookupOperand(spv_operand_type_t type, uint32_t operand,
spv_operand_desc* desc) const;
// Finds operand entry in the grammar table and returns its name.
// Returns "Unknown" if not found.
const char* lookupOperandName(spv_operand_type_t type,
uint32_t operand) const {
spv_operand_desc desc = nullptr;
if (lookupOperand(type, operand, &desc) != SPV_SUCCESS || !desc) {
return "Unknown";
}
return desc->name;
}
// Finds the opcode for the given OpSpecConstantOp opcode name. The name
// should not have the "Op" prefix. For example, "IAdd" corresponds to
// the integer add opcode for OpSpecConstantOp. On success, returns

View File

@ -63,9 +63,7 @@ class Decoration {
: dec_type_(t), params_(parameters), struct_member_index_(member_index) {}
void set_struct_member_index(uint32_t index) { struct_member_index_ = index; }
int struct_member_index() { return struct_member_index_; }
int struct_member_index() const { return struct_member_index_; }
SpvDecoration dec_type() { return dec_type_; }
SpvDecoration dec_type() const { return dec_type_; }
std::vector<uint32_t>& params() { return params_; }
const std::vector<uint32_t>& params() const { return params_; }

View File

@ -269,9 +269,17 @@ class ValidationState_t {
return id_decorations_[id];
}
const std::vector<Decoration>& id_decorations(uint32_t id) const {
// TODO: This would throw or generate SIGABRT if id has no
// decorations. Remove/refactor this function.
return id_decorations_.at(id);
}
// Returns const pointer to the internal decoration container.
const std::unordered_map<uint32_t, std::vector<Decoration>>& id_decorations()
const {
return id_decorations_;
}
/// Finds id's def, if it exists. If found, returns the definition otherwise
/// nullptr
const Instruction* FindDef(uint32_t id) const;

View File

@ -336,8 +336,13 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
}
position.index = SPV_INDEX_INSTRUCTION;
return spvValidateIDs(instructions.data(), instructions.size(), *vstate,
&position);
if (auto error = spvValidateIDs(instructions.data(), instructions.size(),
*vstate, &position))
return error;
if (auto error = ValidateBuiltIns(*vstate)) return error;
return SPV_SUCCESS;
}
} // anonymous namespace

View File

@ -117,6 +117,9 @@ spv_result_t InstructionPass(ValidationState_t& _,
/// Performs decoration validation.
spv_result_t ValidateDecorations(ValidationState_t& _);
/// Performs validation of built-in variables.
spv_result_t ValidateBuiltIns(const ValidationState_t& _);
/// Validates that type declarations are unique, unless multiple declarations
/// of the same data type are allowed by the specification.
/// (see section 2.8 Types and Variables)

2471
source/validate_builtins.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,6 @@ set(VAL_TEST_COMMON_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/../test_fixture.h
${CMAKE_CURRENT_SOURCE_DIR}/../unit_spirv.h
${CMAKE_CURRENT_SOURCE_DIR}/val_fixtures.h
${CMAKE_CURRENT_SOURCE_DIR}/val_fixtures.cpp
)
@ -110,6 +109,12 @@ add_spvtools_unittest(TARGET val_bitwise
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET val_builtins
SRCS val_builtins_test.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET val_image
SRCS val_image_test.cpp
${VAL_TEST_COMMON_SRCS}

File diff suppressed because it is too large Load Diff

View File

@ -1336,20 +1336,30 @@ INSTANTIATE_TEST_CASE_P(BuiltIn, ValidateCapabilityVulkan10,
ValuesIn(AllSpirV10Capabilities()),
Values(
make_pair(string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
"OpDecorate %intt BuiltIn PointSize\n"
"OpEntryPoint Vertex %func \"shader\" \n"
"OpMemberDecorate %block 0 BuiltIn PointSize\n"
"%f32 = OpTypeFloat 32\n"
"%block = OpTypeStruct %f32\n"
"%intt = OpTypeInt 32 0\n" + string(kVoidFVoid),
// Capabilities which should succeed.
AllVulkan10Capabilities()),
make_pair(string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
"OpDecorate %intt BuiltIn ClipDistance\n"
"%intt = OpTypeInt 32 0\n" + string(kVoidFVoid),
"OpEntryPoint Vertex %func \"shader\" \n"
"OpMemberDecorate %block 0 BuiltIn ClipDistance\n"
"%f32 = OpTypeFloat 32\n"
"%intt = OpTypeInt 32 0\n"
"%intt_4 = OpConstant %intt 4\n"
"%f32arr4 = OpTypeArray %f32 %intt_4\n"
"%block = OpTypeStruct %f32arr4\n" + string(kVoidFVoid),
AllVulkan10Capabilities()),
make_pair(string(kGLSL450MemoryModel) +
"OpEntryPoint Vertex %func \"shader\" \n" +
"OpDecorate %intt BuiltIn CullDistance\n"
"%intt = OpTypeInt 32 0\n" + string(kVoidFVoid),
"OpEntryPoint Vertex %func \"shader\" \n"
"OpMemberDecorate %block 0 BuiltIn CullDistance\n"
"%f32 = OpTypeFloat 32\n"
"%intt = OpTypeInt 32 0\n"
"%intt_4 = OpConstant %intt 4\n"
"%f32arr4 = OpTypeArray %f32 %intt_4\n"
"%block = OpTypeStruct %f32arr4\n" + string(kVoidFVoid),
AllVulkan10Capabilities())
)),);
@ -1598,8 +1608,9 @@ OpCapability DrawParameters
OpExtension "SPV_KHR_shader_draw_parameters"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %func "shader"
OpDecorate %intt BuiltIn PointSize
%intt = OpTypeInt 32 0
OpMemberDecorate %block 0 BuiltIn PointSize
%f32 = OpTypeFloat 32
%block = OpTypeStruct %f32
)" + string(kVoidFVoid);
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);

View File

@ -1,110 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Common validation fixtures for unit tests
#include "val_fixtures.h"
#include <functional>
#include <tuple>
#include <utility>
#include "test_fixture.h"
namespace spvtest {
template <typename T>
ValidateBase<T>::ValidateBase() : binary_(), diagnostic_() {
// Initialize to default command line options. Different tests can then
// specialize specific options as necessary.
options_ = spvValidatorOptionsCreate();
}
template <typename T>
spv_const_binary ValidateBase<T>::get_const_binary() {
return spv_const_binary(binary_);
}
template <typename T>
void ValidateBase<T>::TearDown() {
if (diagnostic_) {
spvDiagnosticPrint(diagnostic_);
}
spvDiagnosticDestroy(diagnostic_);
spvBinaryDestroy(binary_);
spvValidatorOptionsDestroy(options_);
}
template <typename T>
void ValidateBase<T>::CompileSuccessfully(std::string code,
spv_target_env env) {
spv_diagnostic diagnostic = nullptr;
ASSERT_EQ(SPV_SUCCESS,
spvTextToBinary(ScopedContext(env).context, code.c_str(),
code.size(), &binary_, &diagnostic))
<< "ERROR: " << diagnostic->error
<< "\nSPIR-V could not be compiled into binary:\n"
<< code;
}
template <typename T>
void ValidateBase<T>::OverwriteAssembledBinary(uint32_t index, uint32_t word) {
ASSERT_TRUE(index < binary_->wordCount)
<< "OverwriteAssembledBinary: The given index is larger than the binary "
"word count.";
binary_->code[index] = word;
}
template <typename T>
spv_result_t ValidateBase<T>::ValidateInstructions(spv_target_env env) {
return spvValidateWithOptions(ScopedContext(env).context, options_,
get_const_binary(), &diagnostic_);
}
template <typename T>
spv_result_t ValidateBase<T>::ValidateAndRetrieveValidationState(
spv_target_env env) {
return spvtools::ValidateBinaryAndKeepValidationState(
ScopedContext(env).context, options_, get_const_binary()->code,
get_const_binary()->wordCount, &diagnostic_, &vstate_);
}
template <typename T>
std::string ValidateBase<T>::getDiagnosticString() {
return diagnostic_ == nullptr ? std::string()
: std::string(diagnostic_->error);
}
template <typename T>
spv_validator_options ValidateBase<T>::getValidatorOptions() {
return options_;
}
template <typename T>
spv_position_t ValidateBase<T>::getErrorPosition() {
return diagnostic_ == nullptr ? spv_position_t() : diagnostic_->position;
}
template class spvtest::ValidateBase<bool>;
template class spvtest::ValidateBase<int>;
template class spvtest::ValidateBase<std::string>;
template class spvtest::ValidateBase<std::pair<std::string, bool>>;
template class spvtest::ValidateBase<
std::tuple<std::string, std::pair<std::string, std::vector<std::string>>>>;
template class spvtest::ValidateBase<
std::tuple<int, std::tuple<std::string, std::function<spv_result_t(int)>,
std::function<spv_result_t(int)>>>>;
template class spvtest::ValidateBase<SpvCapability>;
template class spvtest::ValidateBase<std::pair<std::string, std::string>>;
} // namespace spvtest

View File

@ -18,6 +18,7 @@
#define LIBSPIRV_TEST_VALIDATE_FIXTURES_H_
#include "source/val/validation_state.h"
#include "test_fixture.h"
#include "unit_spirv.h"
namespace spvtest {
@ -60,5 +61,78 @@ class ValidateBase : public ::testing::Test,
spv_validator_options options_;
std::unique_ptr<libspirv::ValidationState_t> vstate_;
};
template <typename T>
ValidateBase<T>::ValidateBase() : binary_(), diagnostic_() {
// Initialize to default command line options. Different tests can then
// specialize specific options as necessary.
options_ = spvValidatorOptionsCreate();
}
template <typename T>
spv_const_binary ValidateBase<T>::get_const_binary() {
return spv_const_binary(binary_);
}
template <typename T>
void ValidateBase<T>::TearDown() {
if (diagnostic_) {
spvDiagnosticPrint(diagnostic_);
}
spvDiagnosticDestroy(diagnostic_);
spvBinaryDestroy(binary_);
spvValidatorOptionsDestroy(options_);
}
template <typename T>
void ValidateBase<T>::CompileSuccessfully(std::string code,
spv_target_env env) {
spv_diagnostic diagnostic = nullptr;
ASSERT_EQ(SPV_SUCCESS,
spvTextToBinary(ScopedContext(env).context, code.c_str(),
code.size(), &binary_, &diagnostic))
<< "ERROR: " << diagnostic->error
<< "\nSPIR-V could not be compiled into binary:\n"
<< code;
}
template <typename T>
void ValidateBase<T>::OverwriteAssembledBinary(uint32_t index, uint32_t word) {
ASSERT_TRUE(index < binary_->wordCount)
<< "OverwriteAssembledBinary: The given index is larger than the binary "
"word count.";
binary_->code[index] = word;
}
template <typename T>
spv_result_t ValidateBase<T>::ValidateInstructions(spv_target_env env) {
return spvValidateWithOptions(ScopedContext(env).context, options_,
get_const_binary(), &diagnostic_);
}
template <typename T>
spv_result_t ValidateBase<T>::ValidateAndRetrieveValidationState(
spv_target_env env) {
return spvtools::ValidateBinaryAndKeepValidationState(
ScopedContext(env).context, options_, get_const_binary()->code,
get_const_binary()->wordCount, &diagnostic_, &vstate_);
}
template <typename T>
std::string ValidateBase<T>::getDiagnosticString() {
return diagnostic_ == nullptr ? std::string()
: std::string(diagnostic_->error);
}
template <typename T>
spv_validator_options ValidateBase<T>::getValidatorOptions() {
return options_;
}
template <typename T>
spv_position_t ValidateBase<T>::getErrorPosition() {
return diagnostic_ == nullptr ? spv_position_t() : diagnostic_->position;
}
} // namespace spvtest
#endif