mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
Move type instruction validation into separate file
* Moved type instruction validation out of validation idUsage into a new file * Consolidate type unique pass into new file * Removed one bad test * Reworked validation ordering
This commit is contained in:
parent
5c8b4f5a1c
commit
f2a990022a
@ -59,7 +59,7 @@ SPVTOOLS_SRC_FILES := \
|
||||
source/val/validate_logicals.cpp \
|
||||
source/val/validate_non_uniform.cpp \
|
||||
source/val/validate_primitives.cpp \
|
||||
source/val/validate_type_unique.cpp
|
||||
source/val/validate_type.cpp
|
||||
|
||||
SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/aggressive_dead_code_elim_pass.cpp \
|
||||
|
2
BUILD.gn
2
BUILD.gn
@ -382,7 +382,7 @@ static_library("spvtools_val") {
|
||||
"source/val/validate_memory.cpp",
|
||||
"source/val/validate_non_uniform.cpp",
|
||||
"source/val/validate_primitives.cpp",
|
||||
"source/val/validate_type_unique.cpp",
|
||||
"source/val/validate_type.cpp",
|
||||
"source/val/validation_state.cpp",
|
||||
]
|
||||
|
||||
|
@ -306,7 +306,7 @@ set(SPIRV_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type_unique.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/construct.cpp
|
||||
|
@ -317,23 +317,35 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
// Validate individual opcodes.
|
||||
for (size_t i = 0; i < vstate->ordered_instructions().size(); ++i) {
|
||||
auto& instruction = vstate->ordered_instructions()[i];
|
||||
if (auto error = TypeUniquePass(*vstate, &instruction)) return error;
|
||||
if (auto error = ArithmeticsPass(*vstate, &instruction)) return error;
|
||||
if (auto error = CompositesPass(*vstate, &instruction)) return error;
|
||||
if (auto error = ConversionPass(*vstate, &instruction)) return error;
|
||||
if (auto error = DerivativesPass(*vstate, &instruction)) return error;
|
||||
if (auto error = LogicalsPass(*vstate, &instruction)) return error;
|
||||
if (auto error = BitwisePass(*vstate, &instruction)) return error;
|
||||
|
||||
// Keep these passes in the order they appear in the SPIR-V specification
|
||||
// sections to maintain test consistency.
|
||||
// Miscellaneous
|
||||
// Debug
|
||||
// Annotation
|
||||
if (auto error = ExtInstPass(*vstate, &instruction)) return error;
|
||||
if (auto error = ImagePass(*vstate, &instruction)) return error;
|
||||
if (auto error = AtomicsPass(*vstate, &instruction)) return error;
|
||||
if (auto error = BarriersPass(*vstate, &instruction)) return error;
|
||||
if (auto error = PrimitivesPass(*vstate, &instruction)) return error;
|
||||
if (auto error = LiteralsPass(*vstate, &instruction)) return error;
|
||||
if (auto error = NonUniformPass(*vstate, &instruction)) return error;
|
||||
// Mode Setting
|
||||
if (auto error = TypePass(*vstate, &instruction)) return error;
|
||||
// Constants
|
||||
if (auto error = ValidateMemoryInstructions(*vstate, &instruction))
|
||||
return error;
|
||||
// Functions
|
||||
if (auto error = ImagePass(*vstate, &instruction)) return error;
|
||||
if (auto error = ConversionPass(*vstate, &instruction)) return error;
|
||||
if (auto error = CompositesPass(*vstate, &instruction)) return error;
|
||||
if (auto error = ArithmeticsPass(*vstate, &instruction)) return error;
|
||||
if (auto error = BitwisePass(*vstate, &instruction)) return error;
|
||||
if (auto error = LogicalsPass(*vstate, &instruction)) return error;
|
||||
if (auto error = DerivativesPass(*vstate, &instruction)) return error;
|
||||
if (auto error = AtomicsPass(*vstate, &instruction)) return error;
|
||||
if (auto error = PrimitivesPass(*vstate, &instruction)) return error;
|
||||
if (auto error = BarriersPass(*vstate, &instruction)) return error;
|
||||
// Group
|
||||
// Device-Side Enqueue
|
||||
// Pipe
|
||||
if (auto error = NonUniformPass(*vstate, &instruction)) return error;
|
||||
|
||||
if (auto error = LiteralsPass(*vstate, &instruction)) return error;
|
||||
// Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi
|
||||
// must only be preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine.
|
||||
if (auto error = ValidateAdjacency(*vstate, i)) return error;
|
||||
|
@ -136,10 +136,8 @@ 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)
|
||||
spv_result_t TypeUniquePass(ValidationState_t& _, const Instruction* inst);
|
||||
/// Validates type instructions.
|
||||
spv_result_t TypePass(ValidationState_t& _, const Instruction* inst);
|
||||
|
||||
/// Validates correctness of arithmetic instructions.
|
||||
spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst);
|
||||
|
@ -303,255 +303,6 @@ bool idUsage::isValid<SpvOpExecutionMode>(const spv_instruction_t* inst,
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypeVector>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
auto componentIndex = 2;
|
||||
auto componentType = module_.FindDef(inst->words[componentIndex]);
|
||||
if (!componentType || !spvOpcodeIsScalarType(componentType->opcode())) {
|
||||
DIAG(componentType) << "OpTypeVector Component Type <id> '"
|
||||
<< module_.getIdName(inst->words[componentIndex])
|
||||
<< "' is not a scalar type.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypeMatrix>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
auto columnTypeIndex = 2;
|
||||
auto columnType = module_.FindDef(inst->words[columnTypeIndex]);
|
||||
if (!columnType || SpvOpTypeVector != columnType->opcode()) {
|
||||
DIAG(columnType) << "OpTypeMatrix Column Type <id> '"
|
||||
<< module_.getIdName(inst->words[columnTypeIndex])
|
||||
<< "' is not a vector.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypeSampler>(const spv_instruction_t*,
|
||||
const spv_opcode_desc) {
|
||||
// OpTypeSampler takes no arguments in Rev31 and beyond.
|
||||
return true;
|
||||
}
|
||||
|
||||
// True if the integer constant is > 0. constWords are words of the
|
||||
// constant-defining instruction (either OpConstant or
|
||||
// OpSpecConstant). typeWords are the words of the constant's-type-defining
|
||||
// OpTypeInt.
|
||||
bool aboveZero(const std::vector<uint32_t>& constWords,
|
||||
const std::vector<uint32_t>& typeWords) {
|
||||
const uint32_t width = typeWords[2];
|
||||
const bool is_signed = typeWords[3] > 0;
|
||||
const uint32_t loWord = constWords[3];
|
||||
if (width > 32) {
|
||||
// The spec currently doesn't allow integers wider than 64 bits.
|
||||
const uint32_t hiWord = constWords[4]; // Must exist, per spec.
|
||||
if (is_signed && (hiWord >> 31)) return false;
|
||||
return (loWord | hiWord) > 0;
|
||||
} else {
|
||||
if (is_signed && (loWord >> 31)) return false;
|
||||
return loWord > 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypeArray>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
auto elementTypeIndex = 2;
|
||||
auto elementType = module_.FindDef(inst->words[elementTypeIndex]);
|
||||
if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) {
|
||||
DIAG(elementType) << "OpTypeArray Element Type <id> '"
|
||||
<< module_.getIdName(inst->words[elementTypeIndex])
|
||||
<< "' is not a type.";
|
||||
return false;
|
||||
}
|
||||
auto lengthIndex = 3;
|
||||
auto length = module_.FindDef(inst->words[lengthIndex]);
|
||||
if (!length || !spvOpcodeIsConstant(length->opcode())) {
|
||||
DIAG(length) << "OpTypeArray Length <id> '"
|
||||
<< module_.getIdName(inst->words[lengthIndex])
|
||||
<< "' is not a scalar constant type.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: Check the initialiser value of the constant
|
||||
auto constInst = length->words();
|
||||
auto constResultTypeIndex = 1;
|
||||
auto constResultType = module_.FindDef(constInst[constResultTypeIndex]);
|
||||
if (!constResultType || SpvOpTypeInt != constResultType->opcode()) {
|
||||
DIAG(length) << "OpTypeArray Length <id> '"
|
||||
<< module_.getIdName(inst->words[lengthIndex])
|
||||
<< "' is not a constant integer type.";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (length->opcode()) {
|
||||
case SpvOpSpecConstant:
|
||||
case SpvOpConstant:
|
||||
if (aboveZero(length->words(), constResultType->words())) break;
|
||||
// Else fall through!
|
||||
case SpvOpConstantNull: {
|
||||
DIAG(length) << "OpTypeArray Length <id> '"
|
||||
<< module_.getIdName(inst->words[lengthIndex])
|
||||
<< "' default value must be at least 1.";
|
||||
return false;
|
||||
}
|
||||
case SpvOpSpecConstantOp:
|
||||
// Assume it's OK, rather than try to evaluate the operation.
|
||||
break;
|
||||
default:
|
||||
assert(0 && "bug in spvOpcodeIsConstant() or result type isn't int");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypeRuntimeArray>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
auto elementTypeIndex = 2;
|
||||
auto elementType = module_.FindDef(inst->words[elementTypeIndex]);
|
||||
if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) {
|
||||
DIAG(elementType) << "OpTypeRuntimeArray Element Type <id> '"
|
||||
<< module_.getIdName(inst->words[elementTypeIndex])
|
||||
<< "' is not a type.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypeStruct>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
ValidationState_t& vstate = const_cast<ValidationState_t&>(module_);
|
||||
const uint32_t struct_id = inst->words[1];
|
||||
auto structType = module_.FindDef(struct_id);
|
||||
for (size_t memberTypeIndex = 2; memberTypeIndex < inst->words.size();
|
||||
++memberTypeIndex) {
|
||||
auto memberTypeId = inst->words[memberTypeIndex];
|
||||
auto memberType = module_.FindDef(memberTypeId);
|
||||
if (!memberType || !spvOpcodeGeneratesType(memberType->opcode())) {
|
||||
DIAG(memberType) << "OpTypeStruct Member Type <id> '"
|
||||
<< module_.getIdName(inst->words[memberTypeIndex])
|
||||
<< "' is not a type.";
|
||||
return false;
|
||||
}
|
||||
if (SpvOpTypeStruct == memberType->opcode() &&
|
||||
module_.IsStructTypeWithBuiltInMember(memberTypeId)) {
|
||||
DIAG(memberType)
|
||||
<< "Structure <id> " << module_.getIdName(memberTypeId)
|
||||
<< " contains members with BuiltIn decoration. Therefore this "
|
||||
"structure may not be contained as a member of another structure "
|
||||
"type. Structure <id> "
|
||||
<< module_.getIdName(struct_id) << " contains structure <id> "
|
||||
<< module_.getIdName(memberTypeId) << ".";
|
||||
return false;
|
||||
}
|
||||
if (module_.IsForwardPointer(memberTypeId)) {
|
||||
if (memberType->opcode() != SpvOpTypePointer) {
|
||||
DIAG(memberType) << "Found a forward reference to a non-pointer "
|
||||
"type in OpTypeStruct instruction.";
|
||||
return false;
|
||||
}
|
||||
// If we're dealing with a forward pointer:
|
||||
// Find out the type that the pointer is pointing to (must be struct)
|
||||
// word 3 is the <id> of the type being pointed to.
|
||||
auto typePointingTo = module_.FindDef(memberType->words()[3]);
|
||||
if (typePointingTo && typePointingTo->opcode() != SpvOpTypeStruct) {
|
||||
// Forward declared operands of a struct may only point to a struct.
|
||||
DIAG(memberType)
|
||||
<< "A forward reference operand in an OpTypeStruct must be an "
|
||||
"OpTypePointer that points to an OpTypeStruct. "
|
||||
"Found OpTypePointer that points to Op"
|
||||
<< spvOpcodeString(static_cast<SpvOp>(typePointingTo->opcode()))
|
||||
<< ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::unordered_set<uint32_t> built_in_members;
|
||||
for (auto decoration : vstate.id_decorations(struct_id)) {
|
||||
if (decoration.dec_type() == SpvDecorationBuiltIn &&
|
||||
decoration.struct_member_index() != Decoration::kInvalidMember) {
|
||||
built_in_members.insert(decoration.struct_member_index());
|
||||
}
|
||||
}
|
||||
int num_struct_members = static_cast<int>(inst->words.size() - 2);
|
||||
int num_builtin_members = static_cast<int>(built_in_members.size());
|
||||
if (num_builtin_members > 0 && num_builtin_members != num_struct_members) {
|
||||
DIAG(structType)
|
||||
<< "When BuiltIn decoration is applied to a structure-type member, "
|
||||
"all members of that structure type must also be decorated with "
|
||||
"BuiltIn (No allowed mixing of built-in variables and "
|
||||
"non-built-in variables within a single structure). Structure id "
|
||||
<< struct_id << " does not meet this requirement.";
|
||||
return false;
|
||||
}
|
||||
if (num_builtin_members > 0) {
|
||||
vstate.RegisterStructTypeWithBuiltInMember(struct_id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypePointer>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
auto typeIndex = 3;
|
||||
auto type = module_.FindDef(inst->words[typeIndex]);
|
||||
if (!type || !spvOpcodeGeneratesType(type->opcode())) {
|
||||
DIAG(type) << "OpTypePointer Type <id> '"
|
||||
<< module_.getIdName(inst->words[typeIndex])
|
||||
<< "' is not a type.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypeFunction>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
auto returnTypeIndex = 2;
|
||||
auto returnType = module_.FindDef(inst->words[returnTypeIndex]);
|
||||
if (!returnType || !spvOpcodeGeneratesType(returnType->opcode())) {
|
||||
DIAG(returnType) << "OpTypeFunction Return Type <id> '"
|
||||
<< module_.getIdName(inst->words[returnTypeIndex])
|
||||
<< "' is not a type.";
|
||||
return false;
|
||||
}
|
||||
size_t num_args = 0;
|
||||
for (size_t paramTypeIndex = 3; paramTypeIndex < inst->words.size();
|
||||
++paramTypeIndex, ++num_args) {
|
||||
auto paramType = module_.FindDef(inst->words[paramTypeIndex]);
|
||||
if (!paramType || !spvOpcodeGeneratesType(paramType->opcode())) {
|
||||
DIAG(paramType) << "OpTypeFunction Parameter Type <id> '"
|
||||
<< module_.getIdName(inst->words[paramTypeIndex])
|
||||
<< "' is not a type.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const uint32_t num_function_args_limit =
|
||||
module_.options()->universal_limits_.max_function_args;
|
||||
if (num_args > num_function_args_limit) {
|
||||
DIAG(returnType) << "OpTypeFunction may not take more than "
|
||||
<< num_function_args_limit
|
||||
<< " arguments. OpTypeFunction <id> '"
|
||||
<< module_.getIdName(inst->words[1]) << "' has "
|
||||
<< num_args << " arguments.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpTypePipe>(const spv_instruction_t*,
|
||||
const spv_opcode_desc) {
|
||||
// OpTypePipe has no ID arguments.
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool idUsage::isValid<SpvOpConstantTrue>(const spv_instruction_t* inst,
|
||||
const spv_opcode_desc) {
|
||||
@ -1441,15 +1192,6 @@ bool idUsage::isValid(const spv_instruction_t* inst) {
|
||||
CASE(OpGroupMemberDecorate)
|
||||
CASE(OpEntryPoint)
|
||||
CASE(OpExecutionMode)
|
||||
CASE(OpTypeVector)
|
||||
CASE(OpTypeMatrix)
|
||||
CASE(OpTypeSampler)
|
||||
CASE(OpTypeArray)
|
||||
CASE(OpTypeRuntimeArray)
|
||||
CASE(OpTypeStruct)
|
||||
CASE(OpTypePointer)
|
||||
CASE(OpTypeFunction)
|
||||
CASE(OpTypePipe)
|
||||
CASE(OpConstantTrue)
|
||||
CASE(OpConstantFalse)
|
||||
CASE(OpConstantComposite)
|
||||
|
301
source/val/validate_type.cpp
Normal file
301
source/val/validate_type.cpp
Normal file
@ -0,0 +1,301 @@
|
||||
// Copyright (c) 2018 Google LLC.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Ensures type declarations are unique unless allowed by the specification.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include "source/opcode.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
// True if the integer constant is > 0. |const_words| are words of the
|
||||
// constant-defining instruction (either OpConstant or
|
||||
// OpSpecConstant). typeWords are the words of the constant's-type-defining
|
||||
// OpTypeInt.
|
||||
bool AboveZero(const std::vector<uint32_t>& const_words,
|
||||
const std::vector<uint32_t>& type_words) {
|
||||
const uint32_t width = type_words[2];
|
||||
const bool is_signed = type_words[3] > 0;
|
||||
const uint32_t lo_word = const_words[3];
|
||||
if (width > 32) {
|
||||
// The spec currently doesn't allow integers wider than 64 bits.
|
||||
const uint32_t hi_word = const_words[4]; // Must exist, per spec.
|
||||
if (is_signed && (hi_word >> 31)) return false;
|
||||
return (lo_word | hi_word) > 0;
|
||||
} else {
|
||||
if (is_signed && (lo_word >> 31)) return false;
|
||||
return lo_word > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Doesn't do anything if SPV_VAL_ignore_type_decl_unique was declared in the
|
||||
// module.
|
||||
spv_result_t ValidateUniqueness(ValidationState_t& _, const Instruction* inst) {
|
||||
if (_.HasExtension(Extension::kSPV_VALIDATOR_ignore_type_decl_unique))
|
||||
return SPV_SUCCESS;
|
||||
|
||||
const auto opcode = inst->opcode();
|
||||
if (opcode != SpvOpTypeArray && opcode != SpvOpTypeRuntimeArray &&
|
||||
opcode != SpvOpTypeStruct && opcode != SpvOpTypePointer &&
|
||||
!_.RegisterUniqueTypeDeclaration(inst)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Duplicate non-aggregate type declarations are not allowed. "
|
||||
"Opcode: "
|
||||
<< spvOpcodeString(opcode) << " id: " << inst->id();
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto component_index = 1;
|
||||
const auto component_id = inst->GetOperandAs<uint32_t>(component_index);
|
||||
const auto component_type = _.FindDef(component_id);
|
||||
if (!component_type || !spvOpcodeIsScalarType(component_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeVector Component Type <id> '" << _.getIdName(component_id)
|
||||
<< "' is not a scalar type.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeMatrix(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto column_type_index = 1;
|
||||
const auto column_type_id = inst->GetOperandAs<uint32_t>(column_type_index);
|
||||
const auto column_type = _.FindDef(column_type_id);
|
||||
if (!column_type || SpvOpTypeVector != column_type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeMatrix Column Type <id> '" << _.getIdName(column_type_id)
|
||||
<< "' is not a vector.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto element_type_index = 1;
|
||||
const auto element_type_id = inst->GetOperandAs<uint32_t>(element_type_index);
|
||||
const auto element_type = _.FindDef(element_type_id);
|
||||
if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id)
|
||||
<< "' is not a type.";
|
||||
}
|
||||
const auto length_index = 2;
|
||||
const auto length_id = inst->GetOperandAs<uint32_t>(length_index);
|
||||
const auto length = _.FindDef(length_id);
|
||||
if (!length || !spvOpcodeIsConstant(length->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeArray Length <id> '" << _.getIdName(length_id)
|
||||
<< "' is not a scalar constant type.";
|
||||
}
|
||||
|
||||
// NOTE: Check the initialiser value of the constant
|
||||
const auto const_inst = length->words();
|
||||
const auto const_result_type_index = 1;
|
||||
const auto const_result_type = _.FindDef(const_inst[const_result_type_index]);
|
||||
if (!const_result_type || SpvOpTypeInt != const_result_type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeArray Length <id> '" << _.getIdName(length_id)
|
||||
<< "' is not a constant integer type.";
|
||||
}
|
||||
|
||||
switch (length->opcode()) {
|
||||
case SpvOpSpecConstant:
|
||||
case SpvOpConstant:
|
||||
if (AboveZero(length->words(), const_result_type->words())) break;
|
||||
// Else fall through!
|
||||
case SpvOpConstantNull: {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeArray Length <id> '" << _.getIdName(length_id)
|
||||
<< "' default value must be at least 1.";
|
||||
}
|
||||
case SpvOpSpecConstantOp:
|
||||
// Assume it's OK, rather than try to evaluate the operation.
|
||||
break;
|
||||
default:
|
||||
assert(0 && "bug in spvOpcodeIsConstant() or result type isn't int");
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto element_type_index = 1;
|
||||
const auto element_id = inst->GetOperandAs<uint32_t>(element_type_index);
|
||||
const auto element_type = _.FindDef(element_id);
|
||||
if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeRuntimeArray Element Type <id> '"
|
||||
<< _.getIdName(element_id) << "' is not a type.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
const uint32_t struct_id = inst->GetOperandAs<uint32_t>(0);
|
||||
for (size_t member_type_index = 1;
|
||||
member_type_index < inst->operands().size(); ++member_type_index) {
|
||||
auto member_type_id = inst->GetOperandAs<uint32_t>(member_type_index);
|
||||
auto member_type = _.FindDef(member_type_id);
|
||||
if (!member_type || !spvOpcodeGeneratesType(member_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeStruct Member Type <id> '" << _.getIdName(member_type_id)
|
||||
<< "' is not a type.";
|
||||
}
|
||||
if (SpvOpTypeStruct == member_type->opcode() &&
|
||||
_.IsStructTypeWithBuiltInMember(member_type_id)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Structure <id> " << _.getIdName(member_type_id)
|
||||
<< " contains members with BuiltIn decoration. Therefore this "
|
||||
"structure may not be contained as a member of another "
|
||||
"structure "
|
||||
"type. Structure <id> "
|
||||
<< _.getIdName(struct_id) << " contains structure <id> "
|
||||
<< _.getIdName(member_type_id) << ".";
|
||||
}
|
||||
if (_.IsForwardPointer(member_type_id)) {
|
||||
if (member_type->opcode() != SpvOpTypePointer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Found a forward reference to a non-pointer "
|
||||
"type in OpTypeStruct instruction.";
|
||||
}
|
||||
// If we're dealing with a forward pointer:
|
||||
// Find out the type that the pointer is pointing to (must be struct)
|
||||
// word 3 is the <id> of the type being pointed to.
|
||||
auto type_pointing_to = _.FindDef(member_type->words()[3]);
|
||||
if (type_pointing_to && type_pointing_to->opcode() != SpvOpTypeStruct) {
|
||||
// Forward declared operands of a struct may only point to a struct.
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "A forward reference operand in an OpTypeStruct must be an "
|
||||
"OpTypePointer that points to an OpTypeStruct. "
|
||||
"Found OpTypePointer that points to Op"
|
||||
<< spvOpcodeString(
|
||||
static_cast<SpvOp>(type_pointing_to->opcode()))
|
||||
<< ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
std::unordered_set<uint32_t> built_in_members;
|
||||
for (auto decoration : _.id_decorations(struct_id)) {
|
||||
if (decoration.dec_type() == SpvDecorationBuiltIn &&
|
||||
decoration.struct_member_index() != Decoration::kInvalidMember) {
|
||||
built_in_members.insert(decoration.struct_member_index());
|
||||
}
|
||||
}
|
||||
int num_struct_members = static_cast<int>(inst->operands().size() - 1);
|
||||
int num_builtin_members = static_cast<int>(built_in_members.size());
|
||||
if (num_builtin_members > 0 && num_builtin_members != num_struct_members) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "When BuiltIn decoration is applied to a structure-type member, "
|
||||
"all members of that structure type must also be decorated with "
|
||||
"BuiltIn (No allowed mixing of built-in variables and "
|
||||
"non-built-in variables within a single structure). Structure id "
|
||||
<< struct_id << " does not meet this requirement.";
|
||||
}
|
||||
if (num_builtin_members > 0) {
|
||||
_.RegisterStructTypeWithBuiltInMember(struct_id);
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypePointer(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto type_id = inst->GetOperandAs<uint32_t>(2);
|
||||
const auto type = _.FindDef(type_id);
|
||||
if (!type || !spvOpcodeGeneratesType(type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypePointer Type <id> '" << _.getIdName(type_id)
|
||||
<< "' is not a type.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeFunction(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto return_type_id = inst->GetOperandAs<uint32_t>(1);
|
||||
const auto return_type = _.FindDef(return_type_id);
|
||||
if (!return_type || !spvOpcodeGeneratesType(return_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeFunction Return Type <id> '" << _.getIdName(return_type_id)
|
||||
<< "' is not a type.";
|
||||
}
|
||||
size_t num_args = 0;
|
||||
for (size_t param_type_index = 2; param_type_index < inst->operands().size();
|
||||
++param_type_index, ++num_args) {
|
||||
const auto param_id = inst->GetOperandAs<uint32_t>(param_type_index);
|
||||
const auto param_type = _.FindDef(param_id);
|
||||
if (!param_type || !spvOpcodeGeneratesType(param_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeFunction Parameter Type <id> '" << _.getIdName(param_id)
|
||||
<< "' is not a type.";
|
||||
}
|
||||
}
|
||||
const uint32_t num_function_args_limit =
|
||||
_.options()->universal_limits_.max_function_args;
|
||||
if (num_args > num_function_args_limit) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeFunction may not take more than "
|
||||
<< num_function_args_limit << " arguments. OpTypeFunction <id> '"
|
||||
<< _.getIdName(inst->GetOperandAs<uint32_t>(0)) << "' has "
|
||||
<< num_args << " arguments.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (!spvOpcodeGeneratesType(inst->opcode())) return SPV_SUCCESS;
|
||||
|
||||
if (auto error = ValidateUniqueness(_, inst)) return error;
|
||||
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpTypeVector:
|
||||
if (auto error = ValidateTypeVector(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeMatrix:
|
||||
if (auto error = ValidateTypeMatrix(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeArray:
|
||||
if (auto error = ValidateTypeArray(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeRuntimeArray:
|
||||
if (auto error = ValidateTypeRuntimeArray(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeStruct:
|
||||
if (auto error = ValidateTypeStruct(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypePointer:
|
||||
if (auto error = ValidateTypePointer(_, inst)) return error;
|
||||
break;
|
||||
case SpvOpTypeFunction:
|
||||
if (auto error = ValidateTypeFunction(_, inst)) return error;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
@ -1,57 +0,0 @@
|
||||
// Copyright (c) 2017 Google 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.
|
||||
|
||||
// Ensures type declarations are unique unless allowed by the specification.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
|
||||
// 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)
|
||||
// Doesn't do anything if SPV_VAL_ignore_type_decl_unique was declared in the
|
||||
// module.
|
||||
spv_result_t TypeUniquePass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (_.HasExtension(Extension::kSPV_VALIDATOR_ignore_type_decl_unique))
|
||||
return SPV_SUCCESS;
|
||||
|
||||
const SpvOp opcode = inst->opcode();
|
||||
|
||||
if (spvOpcodeGeneratesType(opcode)) {
|
||||
if (opcode == SpvOpTypeArray || opcode == SpvOpTypeRuntimeArray ||
|
||||
opcode == SpvOpTypeStruct || opcode == SpvOpTypePointer) {
|
||||
// Duplicate declarations of aggregates are allowed.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (!_.RegisterUniqueTypeDeclaration(inst)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Duplicate non-aggregate type declarations are not allowed."
|
||||
<< " Opcode: " << spvOpcodeString(SpvOp(inst->opcode()))
|
||||
<< " id: " << inst->id();
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
@ -1037,23 +1037,6 @@ TEST_F(ValidateIdWithMessage,
|
||||
"component count does not match Result Type <id> '4's "
|
||||
"vector component count."));
|
||||
}
|
||||
TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixColumnTypeBad) {
|
||||
std::string spirv = kGLSL450MemoryModel + R"(
|
||||
%1 = OpTypeInt 32 0
|
||||
%2 = OpTypeFloat 32
|
||||
%3 = OpTypeVector %1 2
|
||||
%4 = OpTypeVector %3 2
|
||||
%5 = OpTypeMatrix %2 2
|
||||
%6 = OpConstant %1 42
|
||||
%7 = OpConstant %2 3.14
|
||||
%8 = OpConstantComposite %3 %6 %6
|
||||
%9 = OpConstantComposite %4 %7 %7
|
||||
%10 = OpConstantComposite %5 %8 %9)";
|
||||
CompileSuccessfully(spirv.c_str());
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Columns in a matrix must be of type vector."));
|
||||
}
|
||||
TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayGood) {
|
||||
std::string spirv = kGLSL450MemoryModel + R"(
|
||||
%1 = OpTypeInt 32 0
|
||||
|
Loading…
Reference in New Issue
Block a user