| // Copyright (c) 2015-2016 The Khronos Group Inc. | |
| // Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights | |
| // reserved. | |
| // Copyright (C) 2026 Qualcomm Technologies, 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. | |
| #include "source/val/validation_state.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <sstream> | |
| #include <stack> | |
| #include <string> | |
| #include <utility> | |
| #include "source/binary.h" | |
| #include "source/opcode.h" | |
| #include "source/spirv_constant.h" | |
| #include "source/spirv_endian.h" | |
| #include "source/spirv_target_env.h" | |
| #include "source/table2.h" | |
| #include "source/util/bitutils.h" | |
| #include "source/util/make_unique.h" | |
| #include "source/val/basic_block.h" | |
| #include "source/val/construct.h" | |
| #include "source/val/function.h" | |
| #include "source/val/instruction.h" | |
| #include "spirv-tools/libspirv.h" | |
| #include "spirv/unified1/NonSemanticShaderDebugInfo.h" | |
| #include "spirv/unified1/spirv.hpp11" | |
| namespace spvtools { | |
| namespace val { | |
| namespace { | |
| ModuleLayoutSection InstructionLayoutSection( | |
| ModuleLayoutSection current_section, spv::Op op) { | |
| // See Section 2.4 | |
| if (spvOpcodeGeneratesType(op) || spvOpcodeIsConstant(op)) | |
| return kLayoutTypes; | |
| switch (op) { | |
| case spv::Op::OpCapability: | |
| case spv::Op::OpConditionalCapabilityINTEL: | |
| return kLayoutCapabilities; | |
| case spv::Op::OpExtension: | |
| case spv::Op::OpConditionalExtensionINTEL: | |
| return kLayoutExtensions; | |
| case spv::Op::OpExtInstImport: | |
| return kLayoutExtInstImport; | |
| case spv::Op::OpMemoryModel: | |
| return kLayoutMemoryModel; | |
| case spv::Op::OpEntryPoint: | |
| case spv::Op::OpConditionalEntryPointINTEL: | |
| return kLayoutEntryPoint; | |
| case spv::Op::OpExecutionMode: | |
| case spv::Op::OpExecutionModeId: | |
| return kLayoutExecutionMode; | |
| case spv::Op::OpSourceContinued: | |
| case spv::Op::OpSource: | |
| case spv::Op::OpSourceExtension: | |
| case spv::Op::OpString: | |
| return kLayoutDebug1; | |
| case spv::Op::OpName: | |
| case spv::Op::OpMemberName: | |
| return kLayoutDebug2; | |
| case spv::Op::OpModuleProcessed: | |
| return kLayoutDebug3; | |
| case spv::Op::OpDecorate: | |
| case spv::Op::OpMemberDecorate: | |
| case spv::Op::OpMemberDecorateIdEXT: | |
| case spv::Op::OpGroupDecorate: | |
| case spv::Op::OpGroupMemberDecorate: | |
| case spv::Op::OpDecorationGroup: | |
| case spv::Op::OpDecorateId: | |
| case spv::Op::OpDecorateStringGOOGLE: | |
| case spv::Op::OpMemberDecorateStringGOOGLE: | |
| // SPV_INTEL_memory_access_aliasing: alias scope instructions go in the | |
| // annotations section so that OpDecorateId can reference them without | |
| // requiring a forward reference across layout sections. | |
| case spv::Op::OpAliasDomainDeclINTEL: | |
| case spv::Op::OpAliasScopeDeclINTEL: | |
| case spv::Op::OpAliasScopeListDeclINTEL: | |
| return kLayoutAnnotations; | |
| case spv::Op::OpTypeForwardPointer: | |
| case spv::Op::OpTypeTaskSequenceINTEL: | |
| return kLayoutTypes; | |
| case spv::Op::OpVariable: | |
| case spv::Op::OpUntypedVariableKHR: | |
| if (current_section == kLayoutTypes) return kLayoutTypes; | |
| return kLayoutFunctionDefinitions; | |
| case spv::Op::OpExtInst: | |
| case spv::Op::OpExtInstWithForwardRefsKHR: | |
| // spv::Op::OpExtInst is only allowed in types section for certain | |
| // extended instruction sets. This will be checked separately. | |
| if (current_section == kLayoutTypes) return kLayoutTypes; | |
| // SpvOpExtInst is allowed in graph definitions. | |
| if (current_section == kLayoutGraphDefinitions) | |
| return kLayoutGraphDefinitions; | |
| return kLayoutFunctionDefinitions; | |
| case spv::Op::OpLine: | |
| case spv::Op::OpNoLine: | |
| case spv::Op::OpPoisonKHR: | |
| case spv::Op::OpUndef: | |
| if (current_section == kLayoutTypes) return kLayoutTypes; | |
| return kLayoutFunctionDefinitions; | |
| case spv::Op::OpFunction: | |
| case spv::Op::OpFunctionParameter: | |
| case spv::Op::OpFunctionEnd: | |
| if (current_section == kLayoutFunctionDeclarations) | |
| return kLayoutFunctionDeclarations; | |
| return kLayoutFunctionDefinitions; | |
| case spv::Op::OpSamplerImageAddressingModeNV: | |
| return kLayoutSamplerImageAddressMode; | |
| case spv::Op::OpGraphEntryPointARM: | |
| case spv::Op::OpGraphARM: | |
| case spv::Op::OpGraphInputARM: | |
| case spv::Op::OpGraphSetOutputARM: | |
| case spv::Op::OpGraphEndARM: | |
| return kLayoutGraphDefinitions; | |
| case spv::Op::OpCompositeExtract: | |
| if (current_section == kLayoutGraphDefinitions) | |
| return kLayoutGraphDefinitions; | |
| return kLayoutFunctionDefinitions; | |
| default: | |
| break; | |
| } | |
| return kLayoutFunctionDefinitions; | |
| } | |
| bool IsInstructionInLayoutSection(ModuleLayoutSection layout, spv::Op op) { | |
| return layout == InstructionLayoutSection(layout, op); | |
| } | |
| // Counts the number of instructions and OpFunctions in the binary, without | |
| // doing a full spvBinaryParse(). | |
| // | |
| // The whole module can be skip-scanned without any grammar-table | |
| void CountInstructionsAndFunctions(const uint32_t* words, size_t num_words, | |
| spv_endianness_t endian, | |
| size_t* total_instructions, | |
| size_t* total_functions) { | |
| size_t idx = SPV_INDEX_INSTRUCTION; | |
| while (idx < num_words) { | |
| const uint32_t first_word = spvFixWord(words[idx], endian); | |
| uint16_t word_count = 0; | |
| uint16_t opcode = 0; | |
| spvOpcodeSplit(first_word, &word_count, &opcode); | |
| // If we have something malformed, just stop and the real parse will run | |
| // next regardless and detect/report it | |
| if (word_count == 0 || idx + word_count > num_words) { | |
| break; | |
| } | |
| ++*total_instructions; | |
| if (static_cast<spv::Op>(opcode) == spv::Op::OpFunction) { | |
| ++*total_functions; | |
| } | |
| idx += word_count; | |
| } | |
| } | |
| // Add features based on SPIR-V core version number. | |
| void UpdateFeaturesBasedOnSpirvVersion(ValidationState_t::Feature* features, | |
| uint32_t version) { | |
| assert(features); | |
| if (version >= SPV_SPIRV_VERSION_WORD(1, 4)) { | |
| features->select_between_composites = true; | |
| features->copy_memory_permits_two_memory_accesses = true; | |
| features->uconvert_spec_constant_op = true; | |
| features->nonwritable_var_in_function_or_private = true; | |
| } | |
| } | |
| } // namespace | |
| ValidationState_t::ValidationState_t(const spv_const_context ctx, | |
| const spv_const_validator_options opt, | |
| const uint32_t* words, | |
| const size_t num_words, | |
| const uint32_t max_warnings) | |
| : context_(ctx), | |
| options_(opt), | |
| words_(words), | |
| num_words_(num_words), | |
| unresolved_forward_ids_{}, | |
| operand_names_{}, | |
| current_layout_section_(kLayoutCapabilities), | |
| module_functions_(), | |
| module_capabilities_(), | |
| module_extensions_(), | |
| ordered_instructions_(), | |
| all_definitions_(), | |
| global_vars_(), | |
| local_vars_(), | |
| struct_nesting_depth_(), | |
| struct_has_nested_blockorbufferblock_struct_(), | |
| grammar_(ctx), | |
| addressing_model_(spv::AddressingModel::Max), | |
| memory_model_(spv::MemoryModel::Max), | |
| pointer_size_and_alignment_(0), | |
| sampler_image_addressing_mode_(0), | |
| in_function_(false), | |
| graph_definition_region_(kGraphDefinitionOutside), | |
| num_of_warnings_(0), | |
| max_num_of_warnings_(max_warnings) { | |
| assert(opt && "Validator options may not be Null."); | |
| const auto env = context_->target_env; | |
| if (spvIsVulkanEnv(env)) { | |
| // Vulkan 1.1 includes VK_KHR_relaxed_block_layout in core. | |
| if (env != SPV_ENV_VULKAN_1_0) { | |
| features_.env_relaxed_block_layout = true; | |
| } | |
| } | |
| // LocalSizeId is only disallowed prior to Vulkan 1.3 without maintenance4. | |
| switch (env) { | |
| case SPV_ENV_VULKAN_1_0: | |
| case SPV_ENV_VULKAN_1_1: | |
| case SPV_ENV_VULKAN_1_1_SPIRV_1_4: | |
| case SPV_ENV_VULKAN_1_2: | |
| features_.env_allow_localsizeid = false; | |
| break; | |
| default: | |
| features_.env_allow_localsizeid = true; | |
| break; | |
| } | |
| // Only attempt to read the header if we have words, | |
| // otherwise let the other validation fail and generate an error. | |
| if (num_words > 0) { | |
| spv_const_binary_t binary = {words, num_words}; | |
| spv_endianness_t endian; | |
| spv_header_t header; | |
| if (SPV_SUCCESS == spvBinaryEndianness(&binary, &endian) && | |
| SPV_SUCCESS == spvBinaryHeaderGet(&binary, endian, &header)) { | |
| setIdBound(header.bound); | |
| setGenerator(header.generator); | |
| setVersion(header.version); | |
| size_t total_instructions = 0; | |
| size_t total_functions = 0; | |
| CountInstructionsAndFunctions(words, num_words, endian, | |
| &total_instructions, &total_functions); | |
| ordered_instructions_.reserve(total_instructions); | |
| module_functions_.reserve(total_functions); | |
| } | |
| // If the header couldn't be read, leave everything at its default | |
| // the real parse will report the invalid binary | |
| } | |
| UpdateFeaturesBasedOnSpirvVersion(&features_, version_); | |
| name_mapper_ = spvtools::GetTrivialNameMapper(); | |
| } | |
| spv_result_t ValidationState_t::ForwardDeclareId(uint32_t id) { | |
| unresolved_forward_ids_.insert(id); | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidationState_t::RemoveIfForwardDeclared(uint32_t id) { | |
| unresolved_forward_ids_.erase(id); | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidationState_t::RegisterForwardPointer(uint32_t id) { | |
| forward_pointer_ids_.insert(id); | |
| return SPV_SUCCESS; | |
| } | |
| bool ValidationState_t::IsForwardPointer(uint32_t id) const { | |
| return (forward_pointer_ids_.find(id) != forward_pointer_ids_.end()); | |
| } | |
| void ValidationState_t::AssignNameToId(uint32_t id, std::string name) { | |
| operand_names_[id] = name; | |
| } | |
| std::string ValidationState_t::getIdName(uint32_t id) const { | |
| // Building the FriendlyNameMapper can adds about 20% extra time to run | |
| // spirv-val. We lazily allocate it the first time as we assume most things | |
| // running spirv-val will be valid SPIR-V and this is not needed for the | |
| // average case. | |
| if (options_->use_friendly_names && !friendly_mapper_) { | |
| friendly_mapper_ = spvtools::MakeUnique<spvtools::FriendlyNameMapper>( | |
| context_, words_, num_words_); | |
| name_mapper_ = friendly_mapper_->GetNameMapper(); | |
| } | |
| const std::string id_name = name_mapper_(id); | |
| std::stringstream out; | |
| out << "'" << id << "[%" << id_name << "]'"; | |
| return out.str(); | |
| } | |
| size_t ValidationState_t::unresolved_forward_id_count() const { | |
| return unresolved_forward_ids_.size(); | |
| } | |
| std::vector<uint32_t> ValidationState_t::UnresolvedForwardIds() const { | |
| std::vector<uint32_t> out(std::begin(unresolved_forward_ids_), | |
| std::end(unresolved_forward_ids_)); | |
| return out; | |
| } | |
| bool ValidationState_t::IsDefinedId(uint32_t id) const { | |
| return FindDef(id) != nullptr; | |
| } | |
| const Instruction* ValidationState_t::FindDef(uint32_t id) const { | |
| if (id >= all_definitions_.size()) return nullptr; | |
| return all_definitions_[id]; | |
| } | |
| Instruction* ValidationState_t::FindDef(uint32_t id) { | |
| if (id >= all_definitions_.size()) return nullptr; | |
| return all_definitions_[id]; | |
| } | |
| ModuleLayoutSection ValidationState_t::current_layout_section() const { | |
| return current_layout_section_; | |
| } | |
| void ValidationState_t::ProgressToNextLayoutSectionOrder() { | |
| // Guard against going past the last element(kLayoutFunctionDefinitions) | |
| if (current_layout_section_ <= kLayoutFunctionDefinitions) { | |
| current_layout_section_ = | |
| static_cast<ModuleLayoutSection>(current_layout_section_ + 1); | |
| } | |
| } | |
| bool ValidationState_t::IsOpcodeInPreviousLayoutSection(spv::Op op) { | |
| ModuleLayoutSection section = | |
| InstructionLayoutSection(current_layout_section_, op); | |
| return section < current_layout_section_; | |
| } | |
| bool ValidationState_t::IsOpcodeInCurrentLayoutSection(spv::Op op) { | |
| return IsInstructionInLayoutSection(current_layout_section_, op); | |
| } | |
| DiagnosticStream ValidationState_t::diag(spv_result_t error_code, | |
| const Instruction* inst) { | |
| if (error_code == SPV_WARNING) { | |
| if (num_of_warnings_ == max_num_of_warnings_) { | |
| DiagnosticStream({0, 0, 0}, context_->consumer, "", error_code) | |
| << "Other warnings have been suppressed.\n"; | |
| } | |
| if (num_of_warnings_ >= max_num_of_warnings_) { | |
| return DiagnosticStream({0, 0, 0}, nullptr, "", error_code); | |
| } | |
| ++num_of_warnings_; | |
| } | |
| std::string disassembly; | |
| std::string shader_debug_info; | |
| if (inst) { | |
| disassembly = Disassemble(*inst); | |
| shader_debug_info = InspectShaderDebugInfo(*inst); | |
| } | |
| return DiagnosticStream({0, 0, inst ? inst->LineNum() : 0}, | |
| context_->consumer, disassembly, error_code, | |
| shader_debug_info); | |
| } | |
| std::vector<Function>& ValidationState_t::functions() { | |
| return module_functions_; | |
| } | |
| Function& ValidationState_t::current_function() { | |
| assert(in_function_body()); | |
| return module_functions_.back(); | |
| } | |
| const Function& ValidationState_t::current_function() const { | |
| assert(in_function_body()); | |
| return module_functions_.back(); | |
| } | |
| const Function* ValidationState_t::function(uint32_t id) const { | |
| const auto it = id_to_function_.find(id); | |
| if (it == id_to_function_.end()) return nullptr; | |
| return it->second; | |
| } | |
| Function* ValidationState_t::function(uint32_t id) { | |
| auto it = id_to_function_.find(id); | |
| if (it == id_to_function_.end()) return nullptr; | |
| return it->second; | |
| } | |
| bool ValidationState_t::in_function_body() const { return in_function_; } | |
| bool ValidationState_t::in_block() const { | |
| return module_functions_.empty() == false && | |
| module_functions_.back().current_block() != nullptr; | |
| } | |
| GraphDefinitionRegion ValidationState_t::graph_definition_region() const { | |
| return graph_definition_region_; | |
| } | |
| void ValidationState_t::RegisterCapability(spv::Capability cap) { | |
| // Avoid redundant work. Otherwise the recursion could induce work | |
| // quadrdatic in the capability dependency depth. (Ok, not much, but | |
| // it's something.) | |
| if (module_capabilities_.contains(cap)) return; | |
| module_capabilities_.insert(cap); | |
| const spvtools::OperandDesc* desc = nullptr; | |
| if (SPV_SUCCESS == spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, | |
| uint32_t(cap), &desc)) { | |
| for (auto capability : CapabilitySet(desc->capabilities_range.count(), | |
| desc->capabilities().data())) { | |
| RegisterCapability(capability); | |
| } | |
| } | |
| switch (cap) { | |
| case spv::Capability::Kernel: | |
| features_.group_ops_reduce_and_scans = true; | |
| break; | |
| case spv::Capability::Int8: | |
| features_.use_int8_type = true; | |
| features_.declare_int8_type = true; | |
| break; | |
| case spv::Capability::StorageBuffer8BitAccess: | |
| case spv::Capability::UniformAndStorageBuffer8BitAccess: | |
| case spv::Capability::StoragePushConstant8: | |
| case spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR: | |
| features_.declare_int8_type = true; | |
| break; | |
| case spv::Capability::Int16: | |
| features_.declare_int16_type = true; | |
| break; | |
| case spv::Capability::Float16: | |
| case spv::Capability::Float16Buffer: | |
| features_.declare_float16_type = true; | |
| break; | |
| case spv::Capability::Float8EXT: | |
| case spv::Capability::Float8UnsignedE8M0EXT: | |
| case spv::Capability::MXInt8EXT: | |
| features_.declare_float8_type = true; | |
| break; | |
| case spv::Capability::StorageUniformBufferBlock16: | |
| case spv::Capability::StorageUniform16: | |
| case spv::Capability::StoragePushConstant16: | |
| case spv::Capability::StorageInputOutput16: | |
| case spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR: | |
| features_.declare_int16_type = true; | |
| features_.declare_float16_type = true; | |
| features_.free_fp_rounding_mode = true; | |
| break; | |
| case spv::Capability::VariablePointers: | |
| case spv::Capability::VariablePointersStorageBuffer: | |
| features_.variable_pointers = true; | |
| break; | |
| default: | |
| // TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses | |
| // capability spv::Capability::RayTracingNV. | |
| // spv::Capability::RayTracingProvisionalKHR would need the same | |
| // treatment. One of the differences going from SPV_KHR_ray_tracing from | |
| // provisional to final spec was the provisional spec uses Locations | |
| // for variables in certain storage classes, just like the | |
| // SPV_NV_ray_tracing extension. So it mimics the NVIDIA extension. | |
| // The final SPV_KHR_ray_tracing uses a different capability token | |
| // number, so it doesn't fall into this case. | |
| break; | |
| } | |
| } | |
| void ValidationState_t::RegisterExtension(Extension ext) { | |
| if (module_extensions_.contains(ext)) return; | |
| module_extensions_.insert(ext); | |
| switch (ext) { | |
| case kSPV_AMD_gpu_shader_half_float: | |
| case kSPV_AMD_gpu_shader_half_float_fetch: | |
| // SPV_AMD_gpu_shader_half_float enables float16 type. | |
| // https://github.com/KhronosGroup/SPIRV-Tools/issues/1375 | |
| features_.declare_float16_type = true; | |
| break; | |
| case kSPV_AMD_gpu_shader_int16: | |
| // This is not yet in the extension, but it's recommended for it. | |
| // See https://github.com/KhronosGroup/glslang/issues/848 | |
| features_.uconvert_spec_constant_op = true; | |
| break; | |
| case kSPV_AMD_shader_ballot: | |
| // The grammar doesn't encode the fact that SPV_AMD_shader_ballot | |
| // enables the use of group operations Reduce, InclusiveScan, | |
| // and ExclusiveScan. Enable it manually. | |
| // https://github.com/KhronosGroup/SPIRV-Tools/issues/991 | |
| features_.group_ops_reduce_and_scans = true; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| bool ValidationState_t::HasAnyOfCapabilities( | |
| const CapabilitySet& capabilities) const { | |
| return module_capabilities_.HasAnyOf(capabilities); | |
| } | |
| bool ValidationState_t::HasAnyOfExtensions( | |
| const ExtensionSet& extensions) const { | |
| return module_extensions_.HasAnyOf(extensions); | |
| } | |
| void ValidationState_t::set_addressing_model(spv::AddressingModel am) { | |
| addressing_model_ = am; | |
| switch (am) { | |
| case spv::AddressingModel::Physical32: | |
| pointer_size_and_alignment_ = 4; | |
| break; | |
| default: | |
| // fall through | |
| case spv::AddressingModel::Physical64: | |
| case spv::AddressingModel::PhysicalStorageBuffer64: | |
| pointer_size_and_alignment_ = 8; | |
| break; | |
| } | |
| } | |
| spv::AddressingModel ValidationState_t::addressing_model() const { | |
| return addressing_model_; | |
| } | |
| void ValidationState_t::set_memory_model(spv::MemoryModel mm) { | |
| memory_model_ = mm; | |
| } | |
| spv::MemoryModel ValidationState_t::memory_model() const { | |
| return memory_model_; | |
| } | |
| void ValidationState_t::set_samplerimage_variable_address_mode( | |
| uint32_t bit_width) { | |
| sampler_image_addressing_mode_ = bit_width; | |
| } | |
| uint32_t ValidationState_t::samplerimage_variable_address_mode() const { | |
| return sampler_image_addressing_mode_; | |
| } | |
| spv_result_t ValidationState_t::RegisterFunction( | |
| uint32_t id, uint32_t ret_type_id, | |
| spv::FunctionControlMask function_control, uint32_t function_type_id) { | |
| assert(in_function_body() == false && | |
| "RegisterFunction can only be called when parsing the binary outside " | |
| "of another function"); | |
| in_function_ = true; | |
| module_functions_.emplace_back(id, ret_type_id, function_control, | |
| function_type_id); | |
| id_to_function_.emplace(id, ¤t_function()); | |
| // TODO(umar): validate function type and type_id | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidationState_t::RegisterFunctionEnd() { | |
| assert(in_function_body() == true && | |
| "RegisterFunctionEnd can only be called when parsing the binary " | |
| "inside of another function"); | |
| assert(in_block() == false && | |
| "RegisterFunctionParameter can only be called when parsing the binary " | |
| "outside of a block"); | |
| current_function().RegisterFunctionEnd(); | |
| in_function_ = false; | |
| return SPV_SUCCESS; | |
| } | |
| void ValidationState_t::SetGraphDefinitionRegion(GraphDefinitionRegion region) { | |
| assert((region == kGraphDefinitionOutside && | |
| graph_definition_region_ == kGraphDefinitionOutputs) || | |
| region >= graph_definition_region_); | |
| graph_definition_region_ = region; | |
| } | |
| Instruction* ValidationState_t::AddOrderedInstruction( | |
| const spv_parsed_instruction_t* inst) { | |
| ordered_instructions_.emplace_back(inst); | |
| ordered_instructions_.back().SetLineNum(ordered_instructions_.size()); | |
| return &ordered_instructions_.back(); | |
| } | |
| // Improves diagnostic messages by collecting names of IDs | |
| void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { | |
| switch (inst->opcode()) { | |
| case spv::Op::OpName: { | |
| const auto target = inst->GetOperandAs<uint32_t>(0); | |
| const std::string str = inst->GetOperandAs<std::string>(1); | |
| AssignNameToId(target, str); | |
| break; | |
| } | |
| case spv::Op::OpMemberName: { | |
| const auto target = inst->GetOperandAs<uint32_t>(0); | |
| const std::string str = inst->GetOperandAs<std::string>(2); | |
| AssignNameToId(target, str); | |
| break; | |
| } | |
| case spv::Op::OpSourceContinued: | |
| case spv::Op::OpSource: | |
| case spv::Op::OpSourceExtension: | |
| case spv::Op::OpString: | |
| case spv::Op::OpLine: | |
| case spv::Op::OpNoLine: | |
| default: | |
| break; | |
| } | |
| } | |
| void ValidationState_t::RegisterInstruction(Instruction* inst) { | |
| if (inst->id()) { | |
| // Should be bounded by the module's id bound (see LimitCheckIdBound) | |
| if (inst->id() >= all_definitions_.size()) { | |
| all_definitions_.resize(inst->id() + 1, nullptr); | |
| } | |
| all_definitions_[inst->id()] = inst; | |
| } | |
| // Some validation checks are easier by getting all the consumers | |
| for (size_t i = 0; i < inst->operands().size(); ++i) { | |
| const spv_parsed_operand_t& operand = inst->operand(i); | |
| if ((SPV_OPERAND_TYPE_ID == operand.type) || | |
| (SPV_OPERAND_TYPE_TYPE_ID == operand.type)) { | |
| const uint32_t operand_word = inst->word(operand.offset); | |
| Instruction* operand_inst = FindDef(operand_word); | |
| if (!operand_inst) { | |
| continue; | |
| } | |
| // If the instruction is using an OpTypeSampledImage as an operand, it | |
| // should be recorded. The validator will ensure that all usages of an | |
| // OpTypeSampledImage and its definition are in the same basic block. | |
| if ((SPV_OPERAND_TYPE_ID == operand.type) && | |
| (spv::Op::OpSampledImage == operand_inst->opcode())) { | |
| RegisterSampledImageConsumer(operand_word, inst); | |
| } | |
| // In order to track storage classes (not Function) used per execution | |
| // model we can't use RegisterExecutionModelLimitation on instructions | |
| // like OpTypePointer which are going to be in the pre-function section. | |
| // Instead just need to register storage class usage for consumers in a | |
| // function block. | |
| if (inst->function()) { | |
| if (operand_inst->opcode() == spv::Op::OpTypePointer) { | |
| RegisterStorageClassConsumer( | |
| operand_inst->GetOperandAs<spv::StorageClass>(1), inst); | |
| } else if (operand_inst->opcode() == spv::Op::OpVariable) { | |
| RegisterStorageClassConsumer( | |
| operand_inst->GetOperandAs<spv::StorageClass>(2), inst); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| std::vector<Instruction*> ValidationState_t::getSampledImageConsumers( | |
| uint32_t sampled_image_id) const { | |
| std::vector<Instruction*> result; | |
| auto iter = sampled_image_consumers_.find(sampled_image_id); | |
| if (iter != sampled_image_consumers_.end()) { | |
| result = iter->second; | |
| } | |
| return result; | |
| } | |
| void ValidationState_t::RegisterSampledImageConsumer(uint32_t sampled_image_id, | |
| Instruction* consumer) { | |
| sampled_image_consumers_[sampled_image_id].push_back(consumer); | |
| } | |
| void ValidationState_t::RegisterQCOMImageProcessingTextureConsumer( | |
| uint32_t texture_id, const Instruction* consumer0, | |
| const Instruction* consumer1) { | |
| if (HasDecoration(texture_id, spv::Decoration::WeightTextureQCOM) || | |
| HasDecoration(texture_id, spv::Decoration::BlockMatchTextureQCOM) || | |
| HasDecoration(texture_id, spv::Decoration::BlockMatchSamplerQCOM)) { | |
| qcom_image_processing_consumers_.insert(consumer0->id()); | |
| if (consumer1) { | |
| qcom_image_processing_consumers_.insert(consumer1->id()); | |
| } | |
| } | |
| } | |
| void ValidationState_t::RegisterStorageClassConsumer( | |
| spv::StorageClass storage_class, Instruction* consumer) { | |
| if (spvIsVulkanEnv(context()->target_env)) { | |
| if (storage_class == spv::StorageClass::Output) { | |
| std::string errorVUID = VkErrorID(4644); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation([errorVUID]( | |
| spv::ExecutionModel model, | |
| std::string* message) { | |
| if (model == spv::ExecutionModel::GLCompute || | |
| model == spv::ExecutionModel::RayGenerationKHR || | |
| model == spv::ExecutionModel::IntersectionKHR || | |
| model == spv::ExecutionModel::AnyHitKHR || | |
| model == spv::ExecutionModel::ClosestHitKHR || | |
| model == spv::ExecutionModel::MissKHR || | |
| model == spv::ExecutionModel::CallableKHR) { | |
| if (message) { | |
| *message = | |
| errorVUID + | |
| "in Vulkan environment, Output Storage Class must not be " | |
| "used in GLCompute, RayGenerationKHR, IntersectionKHR, " | |
| "AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR " | |
| "execution models"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } | |
| if (storage_class == spv::StorageClass::Workgroup) { | |
| std::string errorVUID = VkErrorID(4645); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation([errorVUID]( | |
| spv::ExecutionModel model, | |
| std::string* message) { | |
| if (model != spv::ExecutionModel::GLCompute && | |
| model != spv::ExecutionModel::TaskNV && | |
| model != spv::ExecutionModel::MeshNV && | |
| model != spv::ExecutionModel::TaskEXT && | |
| model != spv::ExecutionModel::MeshEXT) { | |
| if (message) { | |
| *message = | |
| errorVUID + | |
| "in Vulkan environment, Workgroup Storage Class is limited " | |
| "to MeshEXT, TaskEXT, MeshNV, TaskNV, and GLCompute " | |
| "execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } | |
| if (storage_class == spv::StorageClass::TileImageEXT) { | |
| std::string errorVUID = VkErrorID(8720); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [errorVUID](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::Fragment) { | |
| if (message) { | |
| *message = errorVUID + | |
| "in Vulkan environment, TileImageEXT Storage " | |
| "Class is limited " | |
| "to Fragment execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } | |
| } | |
| if (storage_class == spv::StorageClass::CallableDataKHR) { | |
| std::string errorVUID = VkErrorID(4704); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [errorVUID](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::RayGenerationKHR && | |
| model != spv::ExecutionModel::ClosestHitKHR && | |
| model != spv::ExecutionModel::CallableKHR && | |
| model != spv::ExecutionModel::MissKHR) { | |
| if (message) { | |
| *message = | |
| errorVUID + | |
| "CallableDataKHR Storage Class is limited to " | |
| "RayGenerationKHR, ClosestHitKHR, CallableKHR, and " | |
| "MissKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::IncomingCallableDataKHR) { | |
| std::string errorVUID = VkErrorID(4705); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [errorVUID](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::CallableKHR) { | |
| if (message) { | |
| *message = | |
| errorVUID + | |
| "IncomingCallableDataKHR Storage Class is limited to " | |
| "CallableKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::RayPayloadKHR) { | |
| std::string errorVUID = VkErrorID(4698); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation([errorVUID]( | |
| spv::ExecutionModel model, | |
| std::string* message) { | |
| if (model != spv::ExecutionModel::RayGenerationKHR && | |
| model != spv::ExecutionModel::ClosestHitKHR && | |
| model != spv::ExecutionModel::MissKHR) { | |
| if (message) { | |
| *message = | |
| errorVUID + | |
| "RayPayloadKHR Storage Class is limited to RayGenerationKHR, " | |
| "ClosestHitKHR, and MissKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::HitAttributeKHR) { | |
| std::string errorVUID = VkErrorID(4701); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [errorVUID](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::IntersectionKHR && | |
| model != spv::ExecutionModel::AnyHitKHR && | |
| model != spv::ExecutionModel::ClosestHitKHR) { | |
| if (message) { | |
| *message = errorVUID + | |
| "HitAttributeKHR Storage Class is limited to " | |
| "IntersectionKHR, AnyHitKHR, sand ClosestHitKHR " | |
| "execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::IncomingRayPayloadKHR) { | |
| std::string errorVUID = VkErrorID(4699); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [errorVUID](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::AnyHitKHR && | |
| model != spv::ExecutionModel::ClosestHitKHR && | |
| model != spv::ExecutionModel::MissKHR) { | |
| if (message) { | |
| *message = | |
| errorVUID + | |
| "IncomingRayPayloadKHR Storage Class is limited to " | |
| "AnyHitKHR, ClosestHitKHR, and MissKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::ShaderRecordBufferKHR) { | |
| std::string errorVUID = VkErrorID(7119); | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [errorVUID](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::RayGenerationKHR && | |
| model != spv::ExecutionModel::IntersectionKHR && | |
| model != spv::ExecutionModel::AnyHitKHR && | |
| model != spv::ExecutionModel::ClosestHitKHR && | |
| model != spv::ExecutionModel::CallableKHR && | |
| model != spv::ExecutionModel::MissKHR) { | |
| if (message) { | |
| *message = | |
| errorVUID + | |
| "ShaderRecordBufferKHR Storage Class is limited to " | |
| "RayGenerationKHR, IntersectionKHR, AnyHitKHR, " | |
| "ClosestHitKHR, CallableKHR, and MissKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) { | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::TaskEXT && | |
| model != spv::ExecutionModel::MeshEXT) { | |
| if (message) { | |
| *message = | |
| "TaskPayloadWorkgroupEXT Storage Class is limited to " | |
| "TaskEXT and MeshKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::HitObjectAttributeNV) { | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, | |
| std::string* message) { | |
| if (model != spv::ExecutionModel::RayGenerationKHR && | |
| model != spv::ExecutionModel::ClosestHitKHR && | |
| model != spv::ExecutionModel::MissKHR) { | |
| if (message) { | |
| *message = | |
| "HitObjectAttributeNV Storage Class is limited to " | |
| "RayGenerationKHR, ClosestHitKHR or MissKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } else if (storage_class == spv::StorageClass::HitObjectAttributeEXT) { | |
| function(consumer->function()->id()) | |
| ->RegisterExecutionModelLimitation([](spv::ExecutionModel model, | |
| std::string* message) { | |
| if (model != spv::ExecutionModel::RayGenerationKHR && | |
| model != spv::ExecutionModel::ClosestHitKHR && | |
| model != spv::ExecutionModel::MissKHR) { | |
| if (message) { | |
| *message = | |
| "HitObjectAttributeEXT Storage Class is limited to " | |
| "RayGenerationKHR, ClosestHitKHR or MissKHR execution model"; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } | |
| } | |
| uint32_t ValidationState_t::getIdBound() const { return id_bound_; } | |
| void ValidationState_t::setIdBound(const uint32_t bound) { id_bound_ = bound; } | |
| bool ValidationState_t::RegisterUniqueTypeDeclaration(const Instruction* inst) { | |
| std::vector<uint32_t> key; | |
| key.push_back(static_cast<uint32_t>(inst->opcode())); | |
| for (size_t index = 0; index < inst->operands().size(); ++index) { | |
| const spv_parsed_operand_t& operand = inst->operand(index); | |
| if (operand.type == SPV_OPERAND_TYPE_RESULT_ID) continue; | |
| const int words_begin = operand.offset; | |
| const int words_end = words_begin + operand.num_words; | |
| assert(words_end <= static_cast<int>(inst->words().size())); | |
| key.insert(key.end(), inst->words().begin() + words_begin, | |
| inst->words().begin() + words_end); | |
| } | |
| return unique_type_declarations_.insert(std::move(key)).second; | |
| } | |
| uint32_t ValidationState_t::GetTypeId(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst ? inst->type_id() : 0; | |
| } | |
| spv::Op ValidationState_t::GetIdOpcode(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst ? inst->opcode() : spv::Op::OpNop; | |
| } | |
| uint32_t ValidationState_t::GetComponentType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| assert(inst); | |
| switch (inst->opcode()) { | |
| case spv::Op::OpTypeFloat: | |
| case spv::Op::OpTypeInt: | |
| case spv::Op::OpTypeBool: | |
| case spv::Op::OpTypePointer: | |
| case spv::Op::OpTypeUntypedPointerKHR: | |
| return id; | |
| case spv::Op::OpTypeArray: | |
| case spv::Op::OpTypeRuntimeArray: | |
| return inst->word(2); | |
| case spv::Op::OpTypeVector: | |
| return inst->word(2); | |
| case spv::Op::OpTypeMatrix: | |
| return GetComponentType(inst->word(2)); | |
| case spv::Op::OpTypeCooperativeMatrixNV: | |
| case spv::Op::OpTypeCooperativeMatrixKHR: | |
| case spv::Op::OpTypeVectorIdEXT: | |
| return inst->word(2); | |
| case spv::Op::OpTypeTensorARM: | |
| return inst->word(2); | |
| default: | |
| break; | |
| } | |
| if (inst->type_id()) return GetComponentType(inst->type_id()); | |
| assert(0); | |
| return 0; | |
| } | |
| uint32_t ValidationState_t::GetDimension(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| assert(inst); | |
| switch (inst->opcode()) { | |
| case spv::Op::OpTypeFloat: | |
| case spv::Op::OpTypeInt: | |
| case spv::Op::OpTypeBool: | |
| return 1; | |
| case spv::Op::OpTypeVector: | |
| case spv::Op::OpTypeMatrix: | |
| return inst->word(3); | |
| case spv::Op::OpTypeCooperativeMatrixNV: | |
| case spv::Op::OpTypeCooperativeMatrixKHR: | |
| // Actual dimension isn't known, return 0 | |
| return 0; | |
| case spv::Op::OpTypeVectorIdEXT: { | |
| uint64_t value = 0; | |
| if (EvalConstantValUint64(inst->word(3), &value)) { | |
| return static_cast<uint32_t>(value); | |
| } | |
| return 0; | |
| } | |
| default: | |
| break; | |
| } | |
| if (inst->type_id()) return GetDimension(inst->type_id()); | |
| assert(0); | |
| return 0; | |
| } | |
| uint32_t ValidationState_t::GetBitWidth(uint32_t id) const { | |
| const uint32_t component_type_id = GetComponentType(id); | |
| const Instruction* inst = FindDef(component_type_id); | |
| assert(inst); | |
| switch (inst->opcode()) { | |
| case spv::Op::OpTypeFloat: | |
| case spv::Op::OpTypeInt: | |
| return inst->word(2); | |
| case spv::Op::OpTypeBool: | |
| return 1; | |
| case spv::Op::OpTypePointer: | |
| case spv::Op::OpTypeUntypedPointerKHR: | |
| assert(inst->GetOperandAs<spv::StorageClass>(1) == | |
| spv::StorageClass::PhysicalStorageBuffer); | |
| return 64; // all pointers to another PSB is 64-bit | |
| default: | |
| break; | |
| } | |
| assert(0); | |
| return 0; | |
| } | |
| bool ValidationState_t::IsVoidType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeVoid; | |
| } | |
| bool ValidationState_t::IsScalarType(uint32_t id) const { | |
| return IsIntScalarType(id) || IsFloatScalarType(id) || IsBoolScalarType(id); | |
| } | |
| bool ValidationState_t::IsVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| return inst->opcode() == spv::Op::OpTypeVector || | |
| inst->opcode() == spv::Op::OpTypeVectorIdEXT; | |
| } | |
| bool ValidationState_t::IsArrayType(uint32_t id, uint64_t length) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst || inst->opcode() != spv::Op::OpTypeArray) { | |
| return false; | |
| } | |
| if (length != 0) { | |
| const auto len_id = inst->GetOperandAs<uint32_t>(2); | |
| const auto len = FindDef(len_id); | |
| uint64_t len_value = 0; | |
| if (!len || !spvOpcodeIsConstant(len->opcode()) || | |
| (EvalConstantValUint64(len_id, &len_value) && (length != len_value))) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool ValidationState_t::IsBfloat16ScalarType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (inst && inst->opcode() == spv::Op::OpTypeFloat) { | |
| if (inst->words().size() > 3) { | |
| if (inst->GetOperandAs<spv::FPEncoding>(2) == | |
| spv::FPEncoding::BFloat16KHR) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsBfloat16VectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsBfloat16ScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsBfloat16CoopMatType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { | |
| return IsBfloat16ScalarType(inst->word(2)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsBfloat16Type(uint32_t id) const { | |
| return IsBfloat16ScalarType(id) || IsBfloat16VectorType(id) || | |
| IsBfloat16CoopMatType(id); | |
| } | |
| bool ValidationState_t::IsFP8ScalarType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (inst && inst->opcode() == spv::Op::OpTypeFloat) { | |
| if (inst->words().size() > 3) { | |
| auto encoding = inst->GetOperandAs<spv::FPEncoding>(2); | |
| if ((encoding == spv::FPEncoding::Float8E4M3EXT) || | |
| (encoding == spv::FPEncoding::Float8E5M2EXT)) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsFP8VectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsFP8ScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsFP8CoopMatType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { | |
| return IsFP8ScalarType(inst->word(2)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsFP8Type(uint32_t id) const { | |
| return IsFP8ScalarType(id) || IsFP8VectorType(id) || IsFP8CoopMatType(id); | |
| } | |
| bool ValidationState_t::IsOCPMicroscalingScalarType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (inst && inst->opcode() == spv::Op::OpTypeFloat && | |
| inst->words().size() > 3) { | |
| const auto encoding = inst->GetOperandAs<spv::FPEncoding>(2); | |
| return encoding == spv::FPEncoding::Float6E2M3EXT || | |
| encoding == spv::FPEncoding::Float6E3M2EXT || | |
| encoding == spv::FPEncoding::Float4E2M1EXT || | |
| encoding == spv::FPEncoding::Float8UnsignedE8M0EXT || | |
| encoding == spv::FPEncoding::MXInt8EXT; | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsOCPMicroscalingNonByteScalarType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (inst && inst->opcode() == spv::Op::OpTypeFloat && | |
| inst->words().size() > 3) { | |
| const auto encoding = inst->GetOperandAs<spv::FPEncoding>(2); | |
| return encoding == spv::FPEncoding::Float6E2M3EXT || | |
| encoding == spv::FPEncoding::Float6E3M2EXT || | |
| encoding == spv::FPEncoding::Float4E2M1EXT; | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsOCPMicroscalingType(uint32_t id) const { | |
| return ContainsOCPMicroscalingType(id); | |
| } | |
| bool ValidationState_t::ContainsOCPMicroscalingType(uint32_t id) const { | |
| return ContainsType(id, [this](const Instruction* inst) { | |
| return IsOCPMicroscalingScalarType(inst->id()); | |
| }); | |
| } | |
| bool ValidationState_t::ContainsOCPMicroscalingNonByteType(uint32_t id) const { | |
| return ContainsType(id, [this](const Instruction* inst) { | |
| return IsOCPMicroscalingNonByteScalarType(inst->id()); | |
| }); | |
| } | |
| bool ValidationState_t::IsFloatScalarType(uint32_t id, uint32_t width) const { | |
| const Instruction* inst = FindDef(id); | |
| bool is_float = inst && inst->opcode() == spv::Op::OpTypeFloat; | |
| if (!is_float) { | |
| return false; | |
| } | |
| if ((width != 0) && (width != inst->word(2))) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ValidationState_t::IsFloatArrayType(uint32_t id) const { | |
| return IsArrayType(id) && IsFloatScalarType(GetComponentType(id)); | |
| } | |
| bool ValidationState_t::IsFloatVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsFloatScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsFloat16Vector2Or4Type(uint32_t id) const { | |
| if (IsVectorType(id)) { | |
| uint32_t vectorDim = GetDimension(id); | |
| return IsFloatScalarType(GetComponentType(id)) && | |
| (vectorDim == 2 || vectorDim == 4) && | |
| (GetBitWidth(GetComponentType(id)) == 16); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeFloat) { | |
| return true; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsFloatScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsIntScalarType(uint32_t id, uint32_t width) const { | |
| const Instruction* inst = FindDef(id); | |
| bool is_int = inst && inst->opcode() == spv::Op::OpTypeInt; | |
| if (!is_int) { | |
| return false; | |
| } | |
| if ((width != 0) && (width != inst->word(2))) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ValidationState_t::IsIntScalarTypeWithSignedness( | |
| uint32_t id, uint32_t signedness) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeInt && | |
| inst->word(3) == signedness; | |
| } | |
| bool ValidationState_t::IsIntArrayType(uint32_t id, uint64_t length) const { | |
| return IsArrayType(id, length) && IsIntScalarType(GetComponentType(id)); | |
| } | |
| bool ValidationState_t::IsIntVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsIntScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsIntVectorType(uint32_t id, uint32_t width, | |
| uint32_t components) const { | |
| if (!IsIntVectorType(id)) { | |
| return false; | |
| } | |
| if (GetBitWidth(id) != width) { | |
| return false; | |
| } | |
| if (GetDimension(id) != components) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeInt) { | |
| return true; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsIntScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const { | |
| return IsIntScalarTypeWithSignedness(id, 0); | |
| } | |
| bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsUnsignedIntScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsUnsignedIntScalarOrVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeInt) { | |
| return inst->GetOperandAs<uint32_t>(2) == 0; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsUnsignedIntScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 1; | |
| } | |
| bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsSignedIntScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsBoolScalarType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeBool; | |
| } | |
| bool ValidationState_t::IsBoolVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsBoolScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeBool) { | |
| return true; | |
| } | |
| if (IsVectorType(id)) { | |
| return IsBoolScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsFloatMatrixType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| return false; | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeMatrix) { | |
| return IsFloatScalarType(GetComponentType(id)); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, | |
| uint32_t* num_cols, | |
| uint32_t* column_type, | |
| uint32_t* component_type) const { | |
| if (!id) return false; | |
| const Instruction* mat_inst = FindDef(id); | |
| assert(mat_inst); | |
| if (mat_inst->opcode() != spv::Op::OpTypeMatrix) return false; | |
| const uint32_t vec_type = mat_inst->word(2); | |
| const Instruction* vec_inst = FindDef(vec_type); | |
| assert(vec_inst); | |
| if (vec_inst->opcode() != spv::Op::OpTypeVector) { | |
| assert(0); | |
| return false; | |
| } | |
| *num_cols = mat_inst->word(3); | |
| *num_rows = vec_inst->word(3); | |
| *column_type = mat_inst->word(2); | |
| *component_type = vec_inst->word(2); | |
| return true; | |
| } | |
| bool ValidationState_t::GetStructMemberTypes( | |
| uint32_t struct_type_id, std::vector<uint32_t>* member_types) const { | |
| member_types->clear(); | |
| if (!struct_type_id) return false; | |
| const Instruction* inst = FindDef(struct_type_id); | |
| assert(inst); | |
| if (inst->opcode() != spv::Op::OpTypeStruct) return false; | |
| *member_types = | |
| std::vector<uint32_t>(inst->words().cbegin() + 2, inst->words().cend()); | |
| if (member_types->empty()) return false; | |
| return true; | |
| } | |
| bool ValidationState_t::IsPointerType(uint32_t id) const { | |
| if (!id) return false; | |
| const Instruction* inst = FindDef(id); | |
| assert(inst); | |
| return inst->opcode() == spv::Op::OpTypePointer || | |
| inst->opcode() == spv::Op::OpTypeUntypedPointerKHR; | |
| } | |
| bool ValidationState_t::GetPointerTypeInfo( | |
| uint32_t id, uint32_t* data_type, spv::StorageClass* storage_class) const { | |
| *storage_class = spv::StorageClass::Max; | |
| if (!id) return false; | |
| const Instruction* inst = FindDef(id); | |
| assert(inst); | |
| if (inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) { | |
| *storage_class = spv::StorageClass(inst->word(2)); | |
| *data_type = 0; | |
| return true; | |
| } | |
| if (inst->opcode() != spv::Op::OpTypePointer) return false; | |
| *storage_class = spv::StorageClass(inst->word(2)); | |
| *data_type = inst->word(3); | |
| return true; | |
| } | |
| uint32_t ValidationState_t::GetLargestScalarType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| switch (inst->opcode()) { | |
| case spv::Op::OpTypeStruct: { | |
| uint32_t size = 0; | |
| for (uint32_t i = 1; i < inst->operands().size(); ++i) { | |
| const uint32_t member_size = | |
| GetLargestScalarType(inst->GetOperandAs<uint32_t>(i)); | |
| size = std::max(size, member_size); | |
| } | |
| return size; | |
| } | |
| case spv::Op::OpTypeArray: | |
| return GetLargestScalarType(inst->GetOperandAs<uint32_t>(1)); | |
| case spv::Op::OpTypeVector: | |
| case spv::Op::OpTypeVectorIdEXT: | |
| return GetLargestScalarType(inst->GetOperandAs<uint32_t>(1)); | |
| default: | |
| return GetBitWidth(id) / 8; | |
| } | |
| } | |
| bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeAccelerationStructureKHR; | |
| } | |
| bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && (inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV || | |
| inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR); | |
| } | |
| bool ValidationState_t::IsCooperativeMatrixNVType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixNV; | |
| } | |
| bool ValidationState_t::IsCooperativeMatrixKHRType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR; | |
| } | |
| bool ValidationState_t::IsCooperativeMatrixAType(uint32_t id) const { | |
| if (!IsCooperativeMatrixKHRType(id)) return false; | |
| const Instruction* inst = FindDef(id); | |
| uint64_t matrixUse = 0; | |
| if (EvalConstantValUint64(inst->word(6), &matrixUse)) { | |
| return matrixUse == | |
| static_cast<uint64_t>(spv::CooperativeMatrixUse::MatrixAKHR); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsCooperativeMatrixBType(uint32_t id) const { | |
| if (!IsCooperativeMatrixKHRType(id)) return false; | |
| const Instruction* inst = FindDef(id); | |
| uint64_t matrixUse = 0; | |
| if (EvalConstantValUint64(inst->word(6), &matrixUse)) { | |
| return matrixUse == | |
| static_cast<uint64_t>(spv::CooperativeMatrixUse::MatrixBKHR); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsCooperativeMatrixAccType(uint32_t id) const { | |
| if (!IsCooperativeMatrixKHRType(id)) return false; | |
| const Instruction* inst = FindDef(id); | |
| uint64_t matrixUse = 0; | |
| if (EvalConstantValUint64(inst->word(6), &matrixUse)) { | |
| return matrixUse == static_cast<uint64_t>( | |
| spv::CooperativeMatrixUse::MatrixAccumulatorKHR); | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const { | |
| if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) | |
| return false; | |
| return IsFloatScalarType(FindDef(id)->word(2)); | |
| } | |
| bool ValidationState_t::IsIntCooperativeMatrixType(uint32_t id) const { | |
| if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) | |
| return false; | |
| return IsIntScalarType(FindDef(id)->word(2)); | |
| } | |
| bool ValidationState_t::IsUnsignedIntCooperativeMatrixType(uint32_t id) const { | |
| if (!IsCooperativeMatrixNVType(id) && !IsCooperativeMatrixKHRType(id)) | |
| return false; | |
| return IsUnsignedIntScalarType(FindDef(id)->word(2)); | |
| } | |
| // Either a 32 bit 2-component uint vector or a 64 bit uint scalar | |
| bool ValidationState_t::IsUnsigned64BitHandle(uint32_t id) const { | |
| return ((IsUnsignedIntScalarType(id) && GetBitWidth(id) == 64) || | |
| (IsUnsignedIntVectorType(id) && GetDimension(id) == 2 && | |
| GetBitWidth(id) == 32)); | |
| } | |
| bool ValidationState_t::IsCooperativeVectorNVType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeVectorIdEXT; | |
| } | |
| bool ValidationState_t::IsFloatCooperativeVectorNVType(uint32_t id) const { | |
| if (!IsCooperativeVectorNVType(id)) return false; | |
| return IsFloatScalarType(FindDef(id)->word(2)); | |
| } | |
| bool ValidationState_t::IsIntCooperativeVectorNVType(uint32_t id) const { | |
| if (!IsCooperativeVectorNVType(id)) return false; | |
| return IsIntScalarType(FindDef(id)->word(2)); | |
| } | |
| bool ValidationState_t::IsUnsignedIntCooperativeVectorNVType( | |
| uint32_t id) const { | |
| if (!IsCooperativeVectorNVType(id)) return false; | |
| return IsUnsignedIntScalarType(FindDef(id)->word(2)); | |
| } | |
| bool ValidationState_t::IsTensorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && inst->opcode() == spv::Op::OpTypeTensorARM; | |
| } | |
| // Opaque handles from [Descriptor] section (added from SPV_EXT_descriptor_heap) | |
| bool ValidationState_t::IsDescriptorType(spv::Op opcode) const { | |
| return opcode == spv::Op::OpTypeBufferEXT || opcode == spv::Op::OpTypeImage || | |
| opcode == spv::Op::OpTypeTensorARM || | |
| opcode == spv::Op::OpTypeSampler || | |
| opcode == spv::Op::OpTypeAccelerationStructureKHR; | |
| } | |
| // Opaque handles from [Descriptor] section (added from SPV_EXT_descriptor_heap) | |
| bool ValidationState_t::IsDescriptorType(uint32_t id) const { | |
| const Instruction* inst = FindDef(id); | |
| return inst && IsDescriptorType(inst->opcode()); | |
| } | |
| bool ValidationState_t::IsConcreteType(uint32_t id) const { | |
| const Instruction* type_inst = FindDef(id); | |
| if (!type_inst) return false; | |
| switch (type_inst->opcode()) { | |
| // scalars | |
| case spv::Op::OpTypeInt: | |
| case spv::Op::OpTypeFloat: | |
| return true; | |
| // vectors/matrix/arrays - only if they are made of concrete types | |
| // example: vector of boolean is not concrete. | |
| case spv::Op::OpTypeVector: | |
| case spv::Op::OpTypeVectorIdEXT: | |
| case spv::Op::OpTypeMatrix: | |
| case spv::Op::OpTypeArray: | |
| case spv::Op::OpTypeRuntimeArray: | |
| return IsConcreteType(type_inst->GetOperandAs<uint32_t>(1u)); | |
| case spv::Op::OpTypeStruct: | |
| for (uint32_t i = 1; i < type_inst->operands().size(); ++i) { | |
| if (!IsConcreteType(type_inst->GetOperandAs<uint32_t>(i))) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| case spv::Op::OpTypePointer: | |
| return addressing_model() == spv::AddressingModel::Physical32 || | |
| addressing_model() == spv::AddressingModel::Physical64 || | |
| type_inst->GetOperandAs<spv::StorageClass>(1u) == | |
| spv::StorageClass::PhysicalStorageBuffer; | |
| default: | |
| return false; | |
| } | |
| } | |
| const Instruction* ValidationState_t::FindUntypedBaseVariable( | |
| const Instruction* inst) { | |
| bool found_heap_base = false; | |
| const Instruction* base_inst = inst; | |
| while (!found_heap_base) { | |
| switch (base_inst->opcode()) { | |
| case spv::Op::OpUntypedAccessChainKHR: | |
| case spv::Op::OpUntypedInBoundsAccessChainKHR: | |
| case spv::Op::OpUntypedPtrAccessChainKHR: | |
| case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: | |
| case spv::Op::OpUntypedArrayLengthKHR: | |
| base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(3)); | |
| break; | |
| case spv::Op::OpLoad: | |
| case spv::Op::OpAtomicLoad: | |
| if (GetIdOpcode(GetOperandTypeId(base_inst, 2)) == | |
| spv::Op::OpTypeUntypedPointerKHR) { | |
| base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(2)); | |
| } else { | |
| return nullptr; | |
| } | |
| break; | |
| case spv::Op::OpAtomicExchange: | |
| case spv::Op::OpAtomicCompareExchange: | |
| case spv::Op::OpAtomicCompareExchangeWeak: | |
| case spv::Op::OpAtomicIIncrement: | |
| case spv::Op::OpAtomicIDecrement: | |
| case spv::Op::OpAtomicIAdd: | |
| case spv::Op::OpAtomicISub: | |
| case spv::Op::OpAtomicSMin: | |
| case spv::Op::OpAtomicUMin: | |
| case spv::Op::OpAtomicSMax: | |
| case spv::Op::OpAtomicUMax: | |
| case spv::Op::OpAtomicAnd: | |
| case spv::Op::OpAtomicOr: | |
| case spv::Op::OpAtomicXor: | |
| base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(2)); | |
| break; | |
| case spv::Op::OpStore: | |
| case spv::Op::OpAtomicStore: | |
| if (GetIdOpcode(GetOperandTypeId(base_inst, 0)) == | |
| spv::Op::OpTypeUntypedPointerKHR) { | |
| base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(0)); | |
| } else { | |
| return nullptr; | |
| } | |
| break; | |
| default: | |
| found_heap_base = true; | |
| break; | |
| } | |
| if (found_heap_base) { | |
| break; | |
| } | |
| } | |
| return base_inst; | |
| } | |
| bool ValidationState_t::IsDescriptorHeapBaseVariable(const Instruction* inst) { | |
| if (!HasCapability(spv::Capability::DescriptorHeapEXT)) { | |
| return false; | |
| } | |
| const Instruction* base_inst = FindUntypedBaseVariable(inst); | |
| if (!base_inst) { | |
| return false; | |
| } | |
| const bool is_heap_base = | |
| IsBuiltin(base_inst->id(), spv::BuiltIn::SamplerHeapEXT) || | |
| IsBuiltin(base_inst->id(), spv::BuiltIn::ResourceHeapEXT); | |
| return FindDef(base_inst->id())->opcode() == spv::Op::OpBufferPointerEXT || | |
| (FindDef(base_inst->id())->opcode() == spv::Op::OpUntypedVariableKHR && | |
| is_heap_base); | |
| } | |
| spv_result_t ValidationState_t::CooperativeMatrixShapesMatch( | |
| const Instruction* inst, uint32_t result_type_id, uint32_t m2, | |
| bool is_conversion, bool swap_row_col) { | |
| const auto m1_type = FindDef(result_type_id); | |
| const auto m2_type = FindDef(m2); | |
| if (m1_type->opcode() != m2_type->opcode()) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected cooperative matrix types"; | |
| } | |
| uint32_t m1_scope_id = m1_type->GetOperandAs<uint32_t>(2); | |
| uint32_t m1_rows_id = m1_type->GetOperandAs<uint32_t>(3); | |
| uint32_t m1_cols_id = m1_type->GetOperandAs<uint32_t>(4); | |
| uint32_t m2_scope_id = m2_type->GetOperandAs<uint32_t>(2); | |
| uint32_t m2_rows_id = m2_type->GetOperandAs<uint32_t>(3); | |
| uint32_t m2_cols_id = m2_type->GetOperandAs<uint32_t>(4); | |
| if (swap_row_col) { | |
| std::swap(m1_rows_id, m1_cols_id); | |
| } | |
| bool m1_is_int32 = false, m1_is_const_int32 = false, m2_is_int32 = false, | |
| m2_is_const_int32 = false; | |
| uint32_t m1_value = 0, m2_value = 0; | |
| std::tie(m1_is_int32, m1_is_const_int32, m1_value) = | |
| EvalInt32IfConst(m1_scope_id); | |
| std::tie(m2_is_int32, m2_is_const_int32, m2_value) = | |
| EvalInt32IfConst(m2_scope_id); | |
| if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected scopes of Matrix and Result Type to be " << "identical"; | |
| } | |
| std::tie(m1_is_int32, m1_is_const_int32, m1_value) = | |
| EvalInt32IfConst(m1_rows_id); | |
| std::tie(m2_is_int32, m2_is_const_int32, m2_value) = | |
| EvalInt32IfConst(m2_rows_id); | |
| if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected rows of Matrix type and Result Type to be " | |
| << (swap_row_col ? "swapped with columns" : "identical"); | |
| } | |
| std::tie(m1_is_int32, m1_is_const_int32, m1_value) = | |
| EvalInt32IfConst(m1_cols_id); | |
| std::tie(m2_is_int32, m2_is_const_int32, m2_value) = | |
| EvalInt32IfConst(m2_cols_id); | |
| if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected columns of Matrix type and Result Type to be " | |
| << (swap_row_col ? "swapped with rows" : "identical"); | |
| } | |
| if (m1_type->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) { | |
| uint32_t m1_use_id = m1_type->GetOperandAs<uint32_t>(5); | |
| uint32_t m2_use_id = m2_type->GetOperandAs<uint32_t>(5); | |
| std::tie(m1_is_int32, m1_is_const_int32, m1_value) = | |
| EvalInt32IfConst(m1_use_id); | |
| std::tie(m2_is_int32, m2_is_const_int32, m2_value) = | |
| EvalInt32IfConst(m2_use_id); | |
| const auto is_accumulator = [](uint32_t use) { | |
| return use == uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR); | |
| }; | |
| const auto is_a_or_b = [](uint32_t use) { | |
| return use == uint32_t(spv::CooperativeMatrixUse::MatrixAKHR) || | |
| use == uint32_t(spv::CooperativeMatrixUse::MatrixBKHR); | |
| }; | |
| if (swap_row_col && | |
| HasCapability(spv::Capability::CooperativeMatrixConversionsEXT) && | |
| (m1_use_id == m2_use_id || | |
| (m1_is_const_int32 && !is_a_or_b(m1_value)) || | |
| (m2_is_const_int32 && !is_accumulator(m2_value)))) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "A conversion decorated with CooperativeMatrixTransposeEXT " | |
| "must have a MatrixAccumulatorKHR Matrix and a MatrixAKHR or " | |
| "MatrixBKHR Result Type: " | |
| << spvOpcodeString(inst->opcode()); | |
| } | |
| if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { | |
| bool allow_conv = false; | |
| // Conversion from Acc->A,B allowed for NV or EXT | |
| if ((HasCapability(spv::Capability::CooperativeMatrixConversionsNV) || | |
| HasCapability(spv::Capability::CooperativeMatrixConversionsEXT)) && | |
| is_accumulator(m2_value) && is_a_or_b(m1_value)) { | |
| allow_conv = true; | |
| } | |
| // Conversion from A,B->Acc allowed for EXT | |
| if (HasCapability(spv::Capability::CooperativeMatrixConversionsEXT) && | |
| is_accumulator(m1_value) && is_a_or_b(m2_value)) { | |
| allow_conv = true; | |
| } | |
| if (!(is_conversion && allow_conv)) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Use of Matrix type and Result Type to be " | |
| << "identical"; | |
| } | |
| } | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidationState_t::CooperativeVectorDimensionsMatch( | |
| const Instruction* inst, uint32_t v1, uint32_t v2) { | |
| const auto v1_type = FindDef(v1); | |
| const auto v2_type = FindDef(v2); | |
| if (v1_type->opcode() != v2_type->opcode()) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected cooperative vector types"; | |
| } | |
| uint32_t v1_components_id = v1_type->GetOperandAs<uint32_t>(2); | |
| uint32_t v2_components_id = v2_type->GetOperandAs<uint32_t>(2); | |
| bool v1_is_int32 = false, v1_is_const_int32 = false, v2_is_int32 = false, | |
| v2_is_const_int32 = false; | |
| uint32_t v1_value = 0, v2_value = 0; | |
| std::tie(v1_is_int32, v1_is_const_int32, v1_value) = | |
| EvalInt32IfConst(v1_components_id); | |
| std::tie(v2_is_int32, v2_is_const_int32, v2_value) = | |
| EvalInt32IfConst(v2_components_id); | |
| if (v1_is_const_int32 && v2_is_const_int32 && v1_value != v2_value) { | |
| return diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected number of components to be identical"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| uint32_t ValidationState_t::GetOperandTypeId(const Instruction* inst, | |
| size_t operand_index) const { | |
| return GetTypeId(inst->GetOperandAs<uint32_t>(operand_index)); | |
| } | |
| bool ValidationState_t::EvalConstantValUint64(uint32_t id, | |
| uint64_t* val) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| assert(0 && "Instruction not found"); | |
| return false; | |
| } | |
| if (!IsIntScalarType(inst->type_id())) return false; | |
| if (inst->opcode() == spv::Op::OpConstantNull) { | |
| *val = 0; | |
| } else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) { | |
| auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u)); | |
| *val = 0; | |
| switch (type_op) { | |
| case spv::Op::OpTypeBufferEXT: | |
| case spv::Op::OpTypeAccelerationStructureKHR: | |
| *val = options()->buffer_descriptor_layout.size; | |
| break; | |
| case spv::Op::OpTypeSampler: | |
| *val = options()->sampler_descriptor_layout.size; | |
| break; | |
| case spv::Op::OpTypeImage: | |
| *val = options()->image_descriptor_layout.size; | |
| break; | |
| case spv::Op::OpTypeTensorARM: | |
| *val = options()->tensor_descriptor_layout.size; | |
| break; | |
| default: | |
| break; | |
| } | |
| return *val > 0; | |
| } else if (inst->opcode() != spv::Op::OpConstant) { | |
| // Spec constant values cannot be evaluated so don't consider constant for | |
| // static validation | |
| return false; | |
| } else if (inst->words().size() == 4) { | |
| *val = inst->word(3); | |
| } else if (inst->words().size() == 5) { | |
| *val = inst->word(3); | |
| *val |= uint64_t(inst->word(4)) << 32; | |
| } else { | |
| // Literal value wider than 64 bits does not fit in a uint64_t. | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ValidationState_t::EvalConstantValInt64(uint32_t id, int64_t* val) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| assert(0 && "Instruction not found"); | |
| return false; | |
| } | |
| if (!IsIntScalarType(inst->type_id())) return false; | |
| if (inst->opcode() == spv::Op::OpConstantNull) { | |
| *val = 0; | |
| } else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) { | |
| auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u)); | |
| *val = 0; | |
| switch (type_op) { | |
| case spv::Op::OpTypeBufferEXT: | |
| case spv::Op::OpTypeAccelerationStructureKHR: | |
| *val = static_cast<int64_t>(options()->buffer_descriptor_layout.size); | |
| break; | |
| case spv::Op::OpTypeSampler: | |
| *val = static_cast<int64_t>(options()->sampler_descriptor_layout.size); | |
| break; | |
| case spv::Op::OpTypeImage: | |
| *val = static_cast<int64_t>(options()->image_descriptor_layout.size); | |
| break; | |
| case spv::Op::OpTypeTensorARM: | |
| *val = static_cast<int64_t>(options()->tensor_descriptor_layout.size); | |
| break; | |
| default: | |
| break; | |
| } | |
| return *val > 0; | |
| } else if (inst->opcode() != spv::Op::OpConstant) { | |
| // Spec constant values cannot be evaluated so don't consider constant for | |
| // static validation | |
| return false; | |
| } else if (inst->words().size() == 4) { | |
| *val = int32_t(inst->word(3)); | |
| } else if (inst->words().size() == 5) { | |
| const uint32_t lo_word = inst->word(3); | |
| const uint32_t hi_word = inst->word(4); | |
| *val = static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32); | |
| } else { | |
| // Literal value wider than 64 bits does not fit in an int64_t. | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ValidationState_t::EvalConstantValFloat32(uint32_t id, float* val) const { | |
| const Instruction* inst = FindDef(id); | |
| if (!inst) { | |
| assert(0 && "Instruction not found"); | |
| return false; | |
| } | |
| if (!IsFloatScalarType(inst->type_id(), 32)) return false; | |
| if (inst->opcode() == spv::Op::OpConstantNull) { | |
| *val = 0.0f; | |
| } else if (inst->opcode() == spv::Op::OpConstant) { | |
| *val = spvtools::utils::BitwiseCast<float>(inst->word(3)); | |
| } else { | |
| // Spec constant values cannot be evaluated | |
| return false; | |
| } | |
| return true; | |
| } | |
| // <is_int32, is_const_int32, value> | |
| std::tuple<bool, bool, uint32_t> ValidationState_t::EvalInt32IfConst( | |
| uint32_t id) const { | |
| const Instruction* const inst = FindDef(id); | |
| assert(inst); | |
| const uint32_t type = inst->type_id(); | |
| if (type == 0 || !IsIntScalarType(type, 32)) { | |
| return std::make_tuple(false, false, 0); | |
| } | |
| // Spec constant values cannot be evaluated so don't consider constant for | |
| // the purpose of this method. | |
| if (!spvOpcodeIsConstant(inst->opcode()) || | |
| spvOpcodeIsSpecConstant(inst->opcode())) { | |
| return std::make_tuple(true, false, 0); | |
| } | |
| if (inst->opcode() == spv::Op::OpConstantNull) { | |
| return std::make_tuple(true, true, 0); | |
| } | |
| if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) { | |
| // The value is only known when the descriptor layout was provided. | |
| uint64_t value = 0; | |
| if (EvalConstantValUint64(id, &value)) { | |
| return std::make_tuple(true, true, static_cast<uint32_t>(value)); | |
| } | |
| return std::make_tuple(true, false, 0); | |
| } | |
| assert(inst->words().size() == 4); | |
| return std::make_tuple(true, true, inst->word(3)); | |
| } | |
| void ValidationState_t::ComputeFunctionToEntryPointMapping() { | |
| for (const uint32_t entry_point : entry_points()) { | |
| std::stack<uint32_t> call_stack; | |
| std::set<uint32_t> visited; | |
| call_stack.push(entry_point); | |
| while (!call_stack.empty()) { | |
| const uint32_t called_func_id = call_stack.top(); | |
| call_stack.pop(); | |
| if (!visited.insert(called_func_id).second) continue; | |
| function_to_entry_points_[called_func_id].push_back(entry_point); | |
| const Function* called_func = function(called_func_id); | |
| if (called_func) { | |
| // Other checks should error out on this invalid SPIR-V. | |
| for (const uint32_t new_call : called_func->function_call_targets()) { | |
| call_stack.push(new_call); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void ValidationState_t::ComputeRecursiveEntryPoints() { | |
| for (const Function& func : functions()) { | |
| std::stack<uint32_t> call_stack; | |
| std::set<uint32_t> visited; | |
| for (const uint32_t new_call : func.function_call_targets()) { | |
| call_stack.push(new_call); | |
| } | |
| while (!call_stack.empty()) { | |
| const uint32_t called_func_id = call_stack.top(); | |
| call_stack.pop(); | |
| if (!visited.insert(called_func_id).second) continue; | |
| if (called_func_id == func.id()) { | |
| for (const uint32_t entry_point : | |
| function_to_entry_points_[called_func_id]) | |
| recursive_entry_points_.insert(entry_point); | |
| break; | |
| } | |
| const Function* called_func = function(called_func_id); | |
| if (called_func) { | |
| // Other checks should error out on this invalid SPIR-V. | |
| for (const uint32_t new_call : called_func->function_call_targets()) { | |
| call_stack.push(new_call); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| const std::vector<uint32_t>& ValidationState_t::FunctionEntryPoints( | |
| uint32_t func) const { | |
| auto iter = function_to_entry_points_.find(func); | |
| if (iter == function_to_entry_points_.end()) { | |
| return empty_ids_; | |
| } else { | |
| return iter->second; | |
| } | |
| } | |
| std::set<uint32_t> ValidationState_t::EntryPointReferences(uint32_t id) const { | |
| std::set<uint32_t> referenced_entry_points; | |
| const auto inst = FindDef(id); | |
| if (!inst) return referenced_entry_points; | |
| std::vector<const Instruction*> stack; | |
| stack.push_back(inst); | |
| while (!stack.empty()) { | |
| const auto current_inst = stack.back(); | |
| stack.pop_back(); | |
| if (const auto func = current_inst->function()) { | |
| // Instruction lives in a function, we can stop searching. | |
| const auto function_entry_points = FunctionEntryPoints(func->id()); | |
| referenced_entry_points.insert(function_entry_points.begin(), | |
| function_entry_points.end()); | |
| } else { | |
| // Instruction is in the global scope, keep searching its uses. | |
| for (auto pair : current_inst->uses()) { | |
| const auto next_inst = pair.first; | |
| stack.push_back(next_inst); | |
| } | |
| } | |
| } | |
| return referenced_entry_points; | |
| } | |
| std::string ValidationState_t::Disassemble(const Instruction& inst) const { | |
| const spv_parsed_instruction_t& c_inst(inst.c_inst()); | |
| return Disassemble(c_inst.words, c_inst.num_words); | |
| } | |
| std::string ValidationState_t::Disassemble(const uint32_t* words, | |
| uint16_t num_words) const { | |
| uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | | |
| SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES; | |
| return spvInstructionBinaryToText(context()->target_env, words, num_words, | |
| words_, num_words_, disassembly_options); | |
| } | |
| bool ValidationState_t::LogicallyMatch(const Instruction* lhs, | |
| const Instruction* rhs, | |
| bool check_decorations) { | |
| if (lhs->opcode() != rhs->opcode()) { | |
| return false; | |
| } | |
| if (check_decorations) { | |
| const auto& dec_a = id_decorations(lhs->id()); | |
| const auto& dec_b = id_decorations(rhs->id()); | |
| for (const auto& dec : dec_b) { | |
| if (std::find(dec_a.begin(), dec_a.end(), dec) == dec_a.end()) { | |
| return false; | |
| } | |
| } | |
| } | |
| if (lhs->opcode() == spv::Op::OpTypeArray) { | |
| // Size operands must match. | |
| if (lhs->GetOperandAs<uint32_t>(2u) != rhs->GetOperandAs<uint32_t>(2u)) { | |
| return false; | |
| } | |
| // Elements must match or logically match. | |
| const auto lhs_ele_id = lhs->GetOperandAs<uint32_t>(1u); | |
| const auto rhs_ele_id = rhs->GetOperandAs<uint32_t>(1u); | |
| if (lhs_ele_id == rhs_ele_id) { | |
| return true; | |
| } | |
| const auto lhs_ele = FindDef(lhs_ele_id); | |
| const auto rhs_ele = FindDef(rhs_ele_id); | |
| if (!lhs_ele || !rhs_ele) { | |
| return false; | |
| } | |
| return LogicallyMatch(lhs_ele, rhs_ele, check_decorations); | |
| } else if (lhs->opcode() == spv::Op::OpTypeStruct) { | |
| // Number of elements must match. | |
| if (lhs->operands().size() != rhs->operands().size()) { | |
| return false; | |
| } | |
| for (size_t i = 1u; i < lhs->operands().size(); ++i) { | |
| const auto lhs_ele_id = lhs->GetOperandAs<uint32_t>(i); | |
| const auto rhs_ele_id = rhs->GetOperandAs<uint32_t>(i); | |
| // Elements must match or logically match. | |
| if (lhs_ele_id == rhs_ele_id) { | |
| continue; | |
| } | |
| const auto lhs_ele = FindDef(lhs_ele_id); | |
| const auto rhs_ele = FindDef(rhs_ele_id); | |
| if (!lhs_ele || !rhs_ele) { | |
| return false; | |
| } | |
| if (!LogicallyMatch(lhs_ele, rhs_ele, check_decorations)) { | |
| return false; | |
| } | |
| } | |
| // All checks passed. | |
| return true; | |
| } | |
| // No other opcodes are acceptable at this point. Arrays and structs are | |
| // caught above and if they're elements are not arrays or structs they are | |
| // required to match exactly. | |
| return false; | |
| } | |
| const Instruction* ValidationState_t::TracePointer( | |
| const Instruction* inst) const { | |
| auto base_ptr = inst; | |
| while (true) { | |
| const spv::Op opcode = base_ptr->opcode(); | |
| if (opcode == spv::Op::OpAccessChain || | |
| opcode == spv::Op::OpInBoundsAccessChain || | |
| opcode == spv::Op::OpPtrAccessChain || | |
| opcode == spv::Op::OpInBoundsPtrAccessChain || | |
| opcode == spv::Op::OpCopyObject) { | |
| base_ptr = FindDef(base_ptr->GetOperandAs<uint32_t>(2u)); | |
| } else if (opcode == spv::Op::OpUntypedAccessChainKHR || | |
| opcode == spv::Op::OpUntypedInBoundsAccessChainKHR || | |
| opcode == spv::Op::OpUntypedPtrAccessChainKHR || | |
| opcode == spv::Op::OpUntypedInBoundsPtrAccessChainKHR) { | |
| base_ptr = FindDef(base_ptr->GetOperandAs<uint32_t>(3u)); | |
| } else { | |
| break; | |
| } | |
| } | |
| return base_ptr; | |
| } | |
| bool ValidationState_t::ContainsType( | |
| uint32_t id, const std::function<bool(const Instruction*)>& f, | |
| bool traverse_all_types) const { | |
| const auto inst = FindDef(id); | |
| if (!inst) return false; | |
| if (f(inst)) return true; | |
| switch (inst->opcode()) { | |
| case spv::Op::OpTypeArray: | |
| case spv::Op::OpTypeRuntimeArray: | |
| case spv::Op::OpTypeVector: | |
| case spv::Op::OpTypeMatrix: | |
| case spv::Op::OpTypeImage: | |
| case spv::Op::OpTypeSampledImage: | |
| case spv::Op::OpTypeCooperativeMatrixNV: | |
| case spv::Op::OpTypeCooperativeMatrixKHR: | |
| case spv::Op::OpTypeVectorIdEXT: | |
| return ContainsType(inst->GetOperandAs<uint32_t>(1u), f, | |
| traverse_all_types); | |
| case spv::Op::OpTypePointer: | |
| if (IsForwardPointer(id)) return false; | |
| if (traverse_all_types) { | |
| return ContainsType(inst->GetOperandAs<uint32_t>(2u), f, | |
| traverse_all_types); | |
| } | |
| break; | |
| case spv::Op::OpTypeFunction: | |
| case spv::Op::OpTypeStruct: | |
| if (inst->opcode() == spv::Op::OpTypeFunction && !traverse_all_types) { | |
| return false; | |
| } | |
| for (uint32_t i = 1; i < inst->operands().size(); ++i) { | |
| if (ContainsType(inst->GetOperandAs<uint32_t>(i), f, | |
| traverse_all_types)) { | |
| return true; | |
| } | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, spv::Op type, | |
| uint32_t width) const { | |
| if (type != spv::Op::OpTypeInt && type != spv::Op::OpTypeFloat) return false; | |
| const auto f = [type, width](const Instruction* inst) { | |
| if (inst->opcode() == type) { | |
| // Bfloat16 is a special type. | |
| if (type == spv::Op::OpTypeFloat && inst->words().size() > 3) | |
| return false; | |
| return inst->GetOperandAs<uint32_t>(1u) == width; | |
| } | |
| return false; | |
| }; | |
| return ContainsType(id, f); | |
| } | |
| bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { | |
| const bool check_int16 = !HasCapability(spv::Capability::Int16); | |
| const bool check_int8 = !HasCapability(spv::Capability::Int8); | |
| const bool check_float16 = !HasCapability(spv::Capability::Float16); | |
| if (!check_int16 && !check_int8 && !check_float16) return false; | |
| // Used instead of ContainsSizedIntOrFloatType | |
| // found it was measurably faster then calling 3 times | |
| // (also its not a super complex function) | |
| const auto f = [check_int16, check_int8, | |
| check_float16](const Instruction* inst) { | |
| if (inst->opcode() == spv::Op::OpTypeInt) { | |
| const uint32_t width = inst->GetOperandAs<uint32_t>(1u); | |
| return (check_int16 && width == 16) || (check_int8 && width == 8); | |
| } | |
| if (inst->opcode() == spv::Op::OpTypeFloat) { | |
| // Bfloat16 is a special type. | |
| if (inst->words().size() > 3) return false; | |
| return check_float16 && inst->GetOperandAs<uint32_t>(1u) == 16; | |
| } | |
| return false; | |
| }; | |
| return ContainsType(id, f); | |
| } | |
| bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const { | |
| const auto f = [](const Instruction* inst) { | |
| return inst->opcode() == spv::Op::OpTypeRuntimeArray; | |
| }; | |
| return ContainsType(id, f, /* traverse_all_types = */ false); | |
| } | |
| bool ValidationState_t::ContainsUntypedPointer(uint32_t id) const { | |
| const auto inst = FindDef(id); | |
| if (!inst) return false; | |
| if (!spvOpcodeGeneratesType(inst->opcode())) return false; | |
| if (inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) return true; | |
| switch (inst->opcode()) { | |
| case spv::Op::OpTypeArray: | |
| case spv::Op::OpTypeRuntimeArray: | |
| case spv::Op::OpTypeVector: | |
| case spv::Op::OpTypeVectorIdEXT: | |
| case spv::Op::OpTypeMatrix: | |
| case spv::Op::OpTypeImage: | |
| case spv::Op::OpTypeSampledImage: | |
| case spv::Op::OpTypeCooperativeMatrixNV: | |
| return ContainsUntypedPointer(inst->GetOperandAs<uint32_t>(1u)); | |
| case spv::Op::OpTypePointer: | |
| if (IsForwardPointer(id)) return false; | |
| return ContainsUntypedPointer(inst->GetOperandAs<uint32_t>(2u)); | |
| case spv::Op::OpTypeFunction: | |
| case spv::Op::OpTypeStruct: { | |
| for (uint32_t i = 1; i < inst->operands().size(); ++i) { | |
| if (ContainsUntypedPointer(inst->GetOperandAs<uint32_t>(i))) | |
| return true; | |
| } | |
| return false; | |
| } | |
| default: | |
| return false; | |
| } | |
| return false; | |
| } | |
| std::vector<uint32_t>& ValidationState_t::GetDebugSourceLineLength( | |
| uint32_t id) { | |
| auto it = debug_source_line_length_.find(id); | |
| if (it == debug_source_line_length_.end()) { | |
| return debug_source_line_length_[id]; | |
| } | |
| return it->second; | |
| } | |
| // Main entrypoint to using ShaderDebugInfo to get better error messages | |
| std::string ValidationState_t::InspectShaderDebugInfo(const Instruction& inst) { | |
| if (ShaderDebugInfoSet() == 0) { | |
| return ""; // no ShaderDebugInfo found | |
| } | |
| std::ostringstream ss; | |
| const Function* func = inst.function(); | |
| const spv::Op opcode = inst.opcode(); | |
| if (func != nullptr) { | |
| if (opcode == spv::Op::OpVariable) { | |
| InspectDebugLocalVariable(ss, *func, inst); | |
| } else if (opcode == spv::Op::OpFunctionCall) { | |
| InspectFunctionCall(ss, inst); | |
| } else if (opcode == spv::Op::OpReturnValue || | |
| opcode == spv::Op::OpFunctionParameter) { | |
| InspectLineAndFunctionDefinition(ss, *func, inst); | |
| } else if (opcode == spv::Op::OpLoad || opcode == spv::Op::OpStore || | |
| opcode == spv::Op::OpAccessChain || | |
| opcode == spv::Op::OpInBoundsAccessChain || | |
| opcode == spv::Op::OpPtrAccessChain || | |
| opcode == spv::Op::OpInBoundsPtrAccessChain || | |
| opcode == spv::Op::OpUntypedAccessChainKHR || | |
| opcode == spv::Op::OpUntypedInBoundsAccessChainKHR || | |
| opcode == spv::Op::OpUntypedPtrAccessChainKHR || | |
| opcode == spv::Op::OpUntypedInBoundsPtrAccessChainKHR) { | |
| InspectLineAndVariable(ss, inst); | |
| } else { | |
| // Currently a fall back for anything in a function | |
| InspectDebugLine(ss, inst); | |
| } | |
| } else if (opcode == spv::Op::OpVariable || | |
| opcode == spv::Op::OpUntypedVariableKHR) { | |
| // Know are global because not in any function | |
| InspectDebugGlobalVariable(ss, inst); | |
| } else if (spvOpcodeIsDecoration(opcode)) { | |
| // Print the global variable being decorated | |
| const Instruction* target = FindDef(inst.GetOperandAs<uint32_t>(0)); | |
| if (target && | |
| (target->opcode() == spv::Op::OpVariable || | |
| target->opcode() == spv::Op::OpUntypedVariableKHR) && | |
| target->function() == nullptr) { | |
| InspectDebugGlobalVariable(ss, *target); | |
| } | |
| } else if (opcode == spv::Op::OpExecutionMode || | |
| opcode == spv::Op::OpExecutionModeId || | |
| opcode == spv::Op::OpEntryPoint) { | |
| InspectEntryPoint(ss, inst); | |
| } else if (opcode == spv::Op::OpFunction) { | |
| InspectDebugFunctionDefinition(ss, inst); | |
| } | |
| return ss.str(); | |
| } | |
| ValidationState_t::DebugSourceInfo ValidationState_t::GetDebugSourceInfo( | |
| const Instruction& inst) { | |
| assert(inst.opcode() == spv::Op::OpExtInst); | |
| assert(inst.word(3) == ShaderDebugInfoSet()); | |
| uint32_t line_start_id = 0, line_end_id = 0, column_start_id = 0, | |
| column_end_id = 0; | |
| switch (inst.word(4)) { | |
| case NonSemanticShaderDebugInfoDebugLine: | |
| line_start_id = 6; | |
| line_end_id = 7; | |
| column_start_id = 8; | |
| column_end_id = 9; | |
| break; | |
| case NonSemanticShaderDebugInfoDebugTypeTemplateParameterPack: | |
| line_start_id = 7; | |
| column_start_id = 8; | |
| break; | |
| case NonSemanticShaderDebugInfoDebugLexicalBlock: | |
| line_start_id = 6; | |
| column_start_id = 7; | |
| break; | |
| case NonSemanticShaderDebugInfoDebugLocalVariable: | |
| case NonSemanticShaderDebugInfoDebugGlobalVariable: | |
| case NonSemanticShaderDebugInfoDebugTypedef: | |
| case NonSemanticShaderDebugInfoDebugTypeEnum: | |
| case NonSemanticShaderDebugInfoDebugTypeComposite: | |
| case NonSemanticShaderDebugInfoDebugTypeMember: | |
| case NonSemanticShaderDebugInfoDebugTypeTemplateTemplateParameter: | |
| case NonSemanticShaderDebugInfoDebugFunctionDeclaration: | |
| case NonSemanticShaderDebugInfoDebugFunction: | |
| line_start_id = 8; | |
| column_start_id = 9; | |
| break; | |
| case NonSemanticShaderDebugInfoDebugTypeTemplateParameter: | |
| case NonSemanticShaderDebugInfoDebugImportedEntity: | |
| line_start_id = 9; | |
| column_start_id = 10; | |
| break; | |
| case NonSemanticShaderDebugInfoDebugMacroDef: | |
| case NonSemanticShaderDebugInfoDebugMacroUndef: | |
| line_start_id = 6; | |
| break; | |
| default: | |
| return {0, 0, 0, 0}; | |
| } | |
| // spirv-val enforces these are int32 constants | |
| bool is_int32 = false, is_const_int32 = false; | |
| uint32_t line_start = 0; | |
| uint32_t line_end = 0; | |
| uint32_t column_start = 0; | |
| uint32_t column_end = 0; | |
| std::tie(is_int32, is_const_int32, line_start) = | |
| EvalInt32IfConst(inst.word(line_start_id)); | |
| // Some instructions only provide a line and column, so set the "end" to be | |
| // same as "start" | |
| if (line_end_id != 0) { | |
| std::tie(is_int32, is_const_int32, line_end) = | |
| EvalInt32IfConst(inst.word(line_end_id)); | |
| } else { | |
| line_end = line_start; | |
| } | |
| if (column_start_id != 0) { | |
| std::tie(is_int32, is_const_int32, column_start) = | |
| EvalInt32IfConst(inst.word(column_start_id)); | |
| } | |
| if (column_end_id != 0) { | |
| std::tie(is_int32, is_const_int32, column_end) = | |
| EvalInt32IfConst(inst.word(column_end_id)); | |
| } else { | |
| column_end = column_start; | |
| } | |
| return {line_start, line_end, column_start, column_end}; | |
| } | |
| void ValidationState_t::InspectDebugLine(std::ostringstream& ss, | |
| const Instruction& inst) { | |
| const uint32_t set_id = ShaderDebugInfoSet(); | |
| // Find the DebugLine that is preceding | |
| const Instruction* debug_line_inst = nullptr; | |
| size_t idx = &inst - &ordered_instructions()[0]; | |
| while (idx > 0) { | |
| const Instruction* prev = &ordered_instructions()[--idx]; | |
| // A DebugLine only applies until the end of its block | |
| if (prev->opcode() == spv::Op::OpFunction || | |
| prev->opcode() == spv::Op::OpLabel || | |
| spvOpcodeIsBlockTerminator(prev->opcode())) { | |
| break; | |
| } | |
| if (prev->opcode() == spv::Op::OpExtInst && | |
| prev->GetOperandAs<uint32_t>(2) == set_id) { | |
| const uint32_t ext_inst = prev->GetOperandAs<uint32_t>(3); | |
| if (ext_inst == NonSemanticShaderDebugInfoDebugLine) { | |
| debug_line_inst = prev; | |
| break; | |
| } else if (ext_inst == NonSemanticShaderDebugInfoDebugNoLine) { | |
| // If we see a DebugNoLine first, this section has no valid line | |
| break; | |
| } | |
| } | |
| } | |
| if (!debug_line_inst) return; | |
| const Instruction* debug_source = | |
| FindDef(debug_line_inst->GetOperandAs<uint32_t>(4)); | |
| if (!debug_source || debug_source->GetOperandAs<uint32_t>(3) != | |
| NonSemanticShaderDebugInfoDebugSource) { | |
| return; | |
| } | |
| auto source_info = GetDebugSourceInfo(*debug_line_inst); | |
| PrintShaderDebugInfoSource(ss, *debug_source, source_info); | |
| } | |
| void ValidationState_t::InspectDebugGlobalVariable( | |
| std::ostringstream& ss, const Instruction& variable_inst) { | |
| const uint32_t set_id = ShaderDebugInfoSet(); | |
| const Instruction* debug_gloabl_var_inst = nullptr; | |
| for (const auto& inst : ordered_instructions()) { | |
| if (inst.opcode() == spv::Op::OpFunction) { | |
| return; // validated to not be in a function block | |
| } | |
| if (inst.opcode() == spv::Op::OpExtInst && | |
| inst.GetOperandAs<uint32_t>(2) == set_id && | |
| inst.GetOperandAs<uint32_t>(3) == | |
| NonSemanticShaderDebugInfoDebugGlobalVariable && | |
| inst.GetOperandAs<uint32_t>(11) == variable_inst.id()) { | |
| debug_gloabl_var_inst = &inst; | |
| break; | |
| } | |
| } | |
| if (!debug_gloabl_var_inst) return; | |
| const Instruction* debug_source = | |
| FindDef(debug_gloabl_var_inst->GetOperandAs<uint32_t>(6)); | |
| if (!debug_source || debug_source->GetOperandAs<uint32_t>(3) != | |
| NonSemanticShaderDebugInfoDebugSource) { | |
| return; | |
| } | |
| auto source_info = GetDebugSourceInfo(*debug_gloabl_var_inst); | |
| PrintShaderDebugInfoSource(ss, *debug_source, source_info); | |
| } | |
| void ValidationState_t::InspectDebugLocalVariable( | |
| std::ostringstream& ss, const Function& func, | |
| const Instruction& variable_inst) { | |
| const uint32_t set_id = ShaderDebugInfoSet(); | |
| const Instruction* debug_declare_inst = nullptr; | |
| const Instruction* function_inst = FindDef(func.id()); | |
| // Loop through the Function block as the DebugDeclare needs to be inside it | |
| size_t first_inst_id = (function_inst - &ordered_instructions()[0]) + 1; | |
| for (size_t i = first_inst_id + 1; i < ordered_instructions().size(); ++i) { | |
| const Instruction& current_inst = ordered_instructions()[i]; | |
| if (current_inst.opcode() == spv::Op::OpFunctionEnd) { | |
| break; // we hit the next Funciton block | |
| } | |
| if (current_inst.opcode() == spv::Op::OpExtInst && | |
| current_inst.GetOperandAs<uint32_t>(2) == set_id && | |
| current_inst.GetOperandAs<uint32_t>(3) == | |
| NonSemanticShaderDebugInfoDebugDeclare && | |
| current_inst.GetOperandAs<uint32_t>(5) == variable_inst.id()) { | |
| debug_declare_inst = ¤t_inst; | |
| break; | |
| } | |
| } | |
| if (!debug_declare_inst) return; | |
| const Instruction* debug_local_variable = | |
| FindDef(debug_declare_inst->GetOperandAs<uint32_t>(4)); | |
| if (!debug_local_variable || | |
| debug_local_variable->GetOperandAs<uint32_t>(3) != | |
| NonSemanticShaderDebugInfoDebugLocalVariable) { | |
| return; | |
| } | |
| const Instruction* debug_source = | |
| FindDef(debug_local_variable->GetOperandAs<uint32_t>(6)); | |
| if (!debug_source || debug_source->GetOperandAs<uint32_t>(3) != | |
| NonSemanticShaderDebugInfoDebugSource) { | |
| return; | |
| } | |
| auto source_info = GetDebugSourceInfo(*debug_local_variable); | |
| PrintShaderDebugInfoSource(ss, *debug_source, source_info); | |
| } | |
| void ValidationState_t::InspectFunctionCall( | |
| std::ostringstream& ss, const Instruction& function_call_inst) { | |
| // First print the caller, then print the callee if also found | |
| InspectDebugLine(ss, function_call_inst); | |
| const uint32_t callee_function_id = | |
| function_call_inst.GetOperandAs<uint32_t>(2); | |
| const Instruction* function_inst = FindDef(callee_function_id); | |
| if (function_inst) { | |
| InspectDebugFunctionDefinition(ss, *function_inst); | |
| } | |
| } | |
| // For instruction in the function that it would be value to both get the line | |
| // of the invalid instruction, but also the FunctionDefinition | |
| void ValidationState_t::InspectLineAndFunctionDefinition( | |
| std::ostringstream& ss, const Function& func, const Instruction& inst) { | |
| // First print the return line (if one) than find the function def | |
| InspectDebugLine(ss, inst); | |
| const Instruction* function_inst = FindDef(func.id()); | |
| if (function_inst) { | |
| InspectDebugFunctionDefinition(ss, *function_inst); | |
| } | |
| } | |
| // For memory accesses, print both the line of the access and where the | |
| // variable was declared | |
| void ValidationState_t::InspectLineAndVariable(std::ostringstream& ss, | |
| const Instruction& inst) { | |
| InspectDebugLine(ss, inst); | |
| // OpStore has no result, so the pointer is the first operand. | |
| // UntypedAccessChain have the Base Type operand before the Base. | |
| size_t pointer_index = 2; | |
| if (inst.opcode() == spv::Op::OpStore) { | |
| pointer_index = 0; | |
| } else if (spvOpcodeGeneratesUntypedPointer(inst.opcode())) { | |
| pointer_index = 3; | |
| } | |
| const Instruction* pointer = | |
| FindDef(inst.GetOperandAs<uint32_t>(pointer_index)); | |
| if (!pointer) return; | |
| const Instruction* variable = TracePointer(pointer); | |
| if (!variable || (variable->opcode() != spv::Op::OpVariable && | |
| variable->opcode() != spv::Op::OpUntypedVariableKHR)) { | |
| return; | |
| } | |
| if (variable->function()) { | |
| InspectDebugLocalVariable(ss, *variable->function(), *variable); | |
| } else { | |
| InspectDebugGlobalVariable(ss, *variable); | |
| } | |
| } | |
| void ValidationState_t::InspectEntryPoint(std::ostringstream& ss, | |
| const Instruction& inst) { | |
| const Instruction* function_inst = nullptr; | |
| if (inst.opcode() == spv::Op::OpExecutionMode || | |
| inst.opcode() == spv::Op::OpExecutionModeId) { | |
| function_inst = FindDef(inst.GetOperandAs<uint32_t>(0)); | |
| } else if (inst.opcode() == spv::Op::OpEntryPoint) { | |
| function_inst = FindDef(inst.GetOperandAs<uint32_t>(1)); | |
| } | |
| if (function_inst) { | |
| InspectDebugFunctionDefinition(ss, *function_inst); | |
| } | |
| } | |
| void ValidationState_t::InspectDebugFunctionDefinition( | |
| std::ostringstream& ss, const Instruction& function_inst) { | |
| assert(function_inst.opcode() == spv::Op::OpFunction); | |
| const uint32_t set_id = ShaderDebugInfoSet(); | |
| const Instruction* debug_func_def = nullptr; | |
| // Loop through the Function block as the DebugFunctionDefinition needs to be | |
| // inside it | |
| size_t first_inst_id = (&function_inst - &ordered_instructions()[0]) + 1; | |
| for (size_t i = first_inst_id + 1; i < ordered_instructions().size(); ++i) { | |
| const Instruction& current_inst = ordered_instructions()[i]; | |
| if (current_inst.opcode() == spv::Op::OpFunctionEnd) { | |
| break; // we hit the next Funciton block | |
| } | |
| if (current_inst.opcode() == spv::Op::OpExtInst && | |
| current_inst.GetOperandAs<uint32_t>(2) == set_id && | |
| current_inst.GetOperandAs<uint32_t>(3) == | |
| NonSemanticShaderDebugInfoDebugFunctionDefinition && | |
| current_inst.GetOperandAs<uint32_t>(5) == function_inst.id()) { | |
| debug_func_def = ¤t_inst; | |
| break; | |
| } | |
| } | |
| if (!debug_func_def) return; | |
| const Instruction* debug_function = | |
| FindDef(debug_func_def->GetOperandAs<uint32_t>(4)); | |
| if (!debug_function || debug_function->GetOperandAs<uint32_t>(3) != | |
| NonSemanticShaderDebugInfoDebugFunction) { | |
| return; | |
| } | |
| const Instruction* debug_source = | |
| FindDef(debug_function->GetOperandAs<uint32_t>(6)); | |
| if (!debug_source || debug_source->GetOperandAs<uint32_t>(3) != | |
| NonSemanticShaderDebugInfoDebugSource) { | |
| return; | |
| } | |
| auto source_info = GetDebugSourceInfo(*debug_function); | |
| PrintShaderDebugInfoSource(ss, *debug_source, source_info); | |
| } | |
| void ValidationState_t::PrintShaderDebugInfoSource( | |
| std::ostringstream& ss, const Instruction& debug_source, | |
| const DebugSourceInfo& source_info) { | |
| // Line 0 is used when it can't be attributed to any source line | |
| if (source_info.line_start == 0) return; | |
| // The left hand side line number, need to make sure if going from line number | |
| // 99 to 100 that all lines have the same padding | |
| const size_t vertical_line_padding = | |
| std::to_string(source_info.line_end).length(); | |
| auto add_vertical_line = [&](uint32_t line_number) { | |
| size_t padding = 1; | |
| if (line_number != 0) { | |
| ss << line_number; | |
| padding += (vertical_line_padding - std::to_string(line_number).length()); | |
| } else { | |
| padding += vertical_line_padding; | |
| } | |
| for (size_t p = 0; p < padding; p++) { | |
| ss << " "; | |
| } | |
| ss << "|"; | |
| if (line_number != 0) { | |
| ss << " "; // otherwise test will fail from trailing whitespace being | |
| // trimmed | |
| } | |
| }; | |
| uint32_t current_line_num = 1; | |
| auto stream_text = [&](const Instruction* op_string) { | |
| const std::string text = op_string->GetOperandAs<std::string>(1); | |
| for (const char c : text) { | |
| if (current_line_num >= source_info.line_start && | |
| current_line_num <= source_info.line_end) { | |
| ss << c; | |
| if (c == '\n' && current_line_num < source_info.line_end) { | |
| add_vertical_line(current_line_num + 1); | |
| } | |
| } | |
| if (c == '\n') { | |
| current_line_num++; | |
| } | |
| } | |
| }; | |
| const Instruction* file_string = | |
| FindDef(debug_source.GetOperandAs<uint32_t>(4)); | |
| ss << "\n --> " << file_string->GetOperandAs<std::string>(1) << ":" | |
| << source_info.line_start << ":" << source_info.column_start << '\n'; | |
| // The Text operand is optional | |
| if (debug_source.operands().size() < 6) return; | |
| add_vertical_line(0); | |
| ss << '\n'; | |
| const Instruction* source_string = | |
| FindDef(debug_source.GetOperandAs<uint32_t>(5)); | |
| add_vertical_line(source_info.line_start); | |
| stream_text(source_string); | |
| const uint32_t set_id = ShaderDebugInfoSet(); | |
| // Look for any DebugSourceContinued | |
| size_t src_idx = &debug_source - &ordered_instructions()[0] + 1; | |
| for (; src_idx < ordered_instructions().size(); ++src_idx) { | |
| const Instruction& continued_insn = ordered_instructions()[src_idx]; | |
| if (continued_insn.opcode() != spv::Op::OpExtInst || | |
| continued_insn.GetOperandAs<uint32_t>(2) != set_id || | |
| continued_insn.GetOperandAs<uint32_t>(3) != | |
| NonSemanticShaderDebugInfoDebugSourceContinued) { | |
| break; | |
| } | |
| const Instruction* continued_string = | |
| FindDef(continued_insn.GetOperandAs<uint32_t>(4)); | |
| stream_text(continued_string); | |
| } | |
| // This happens if the error is the last line of source | |
| if (current_line_num == source_info.line_end) ss << "\n"; | |
| add_vertical_line(0); | |
| } | |
| bool ValidationState_t::IsValidStorageClass( | |
| spv::StorageClass storage_class) const { | |
| if (spvIsVulkanEnv(context()->target_env)) { | |
| switch (storage_class) { | |
| case spv::StorageClass::UniformConstant: | |
| case spv::StorageClass::Uniform: | |
| case spv::StorageClass::StorageBuffer: | |
| case spv::StorageClass::Input: | |
| case spv::StorageClass::Output: | |
| case spv::StorageClass::Image: | |
| case spv::StorageClass::Workgroup: | |
| case spv::StorageClass::Private: | |
| case spv::StorageClass::Function: | |
| case spv::StorageClass::PushConstant: | |
| case spv::StorageClass::PhysicalStorageBuffer: | |
| case spv::StorageClass::RayPayloadKHR: | |
| case spv::StorageClass::IncomingRayPayloadKHR: | |
| case spv::StorageClass::HitAttributeKHR: | |
| case spv::StorageClass::CallableDataKHR: | |
| case spv::StorageClass::IncomingCallableDataKHR: | |
| case spv::StorageClass::ShaderRecordBufferKHR: | |
| case spv::StorageClass::TaskPayloadWorkgroupEXT: | |
| case spv::StorageClass::HitObjectAttributeNV: | |
| case spv::StorageClass::HitObjectAttributeEXT: | |
| case spv::StorageClass::TileImageEXT: | |
| case spv::StorageClass::NodePayloadAMDX: | |
| case spv::StorageClass::TileAttachmentQCOM: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| std::string ValidationState_t::MissingFeature(const std::string& feature, | |
| const std::string& cmdline, | |
| bool hint) const { | |
| return "\nThis is " + (hint ? std::string("may be ") : "") + | |
| "allowed if you enable the " + feature + " (or use the " + cmdline + | |
| " command line flag)"; | |
| } | |
| #define VUID_WRAP(vuid) "[" #vuid "] " | |
| // Currently no 2 VUID share the same id, so no need for |reference| | |
| std::string ValidationState_t::VkErrorID(uint32_t id, | |
| const char* /*reference*/) const { | |
| if (!spvIsVulkanEnv(context_->target_env)) { | |
| return ""; | |
| } | |
| // This large switch case is only searched when an error has occurred. | |
| // If an id is changed, the old case must be modified or removed. Each string | |
| // here is interpreted as being "implemented" | |
| // Clang format adds spaces between hyphens | |
| // clang-format off | |
| switch (id) { | |
| case 4154: | |
| return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04154); | |
| case 4155: | |
| return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04155); | |
| case 4156: | |
| return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04156); | |
| case 4160: | |
| return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160); | |
| case 4161: | |
| return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161); | |
| case 4162: | |
| return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162); | |
| case 4181: | |
| return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181); | |
| case 4182: | |
| return VUID_WRAP(VUID-BaseInstance-BaseInstance-04182); | |
| case 4183: | |
| return VUID_WRAP(VUID-BaseInstance-BaseInstance-04183); | |
| case 4184: | |
| return VUID_WRAP(VUID-BaseVertex-BaseVertex-04184); | |
| case 4185: | |
| return VUID_WRAP(VUID-BaseVertex-BaseVertex-04185); | |
| case 4186: | |
| return VUID_WRAP(VUID-BaseVertex-BaseVertex-04186); | |
| case 4187: | |
| return VUID_WRAP(VUID-ClipDistance-ClipDistance-04187); | |
| case 4188: | |
| return VUID_WRAP(VUID-ClipDistance-ClipDistance-04188); | |
| case 4189: | |
| return VUID_WRAP(VUID-ClipDistance-ClipDistance-04189); | |
| case 4190: | |
| return VUID_WRAP(VUID-ClipDistance-ClipDistance-04190); | |
| case 4191: | |
| return VUID_WRAP(VUID-ClipDistance-ClipDistance-04191); | |
| case 4196: | |
| return VUID_WRAP(VUID-CullDistance-CullDistance-04196); | |
| case 4197: | |
| return VUID_WRAP(VUID-CullDistance-CullDistance-04197); | |
| case 4198: | |
| return VUID_WRAP(VUID-CullDistance-CullDistance-04198); | |
| case 4199: | |
| return VUID_WRAP(VUID-CullDistance-CullDistance-04199); | |
| case 4200: | |
| return VUID_WRAP(VUID-CullDistance-CullDistance-04200); | |
| case 6735: | |
| return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06735); // Execution Model | |
| case 6736: | |
| return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06736); // input storage | |
| case 6737: | |
| return VUID_WRAP(VUID-CullMaskKHR-CullMaskKHR-06737); // 32 int scalar | |
| case 4205: | |
| return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04205); | |
| case 4206: | |
| return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04206); | |
| case 4207: | |
| return VUID_WRAP(VUID-DrawIndex-DrawIndex-04207); | |
| case 4208: | |
| return VUID_WRAP(VUID-DrawIndex-DrawIndex-04208); | |
| case 4209: | |
| return VUID_WRAP(VUID-DrawIndex-DrawIndex-04209); | |
| case 4210: | |
| return VUID_WRAP(VUID-FragCoord-FragCoord-04210); | |
| case 4211: | |
| return VUID_WRAP(VUID-FragCoord-FragCoord-04211); | |
| case 4212: | |
| return VUID_WRAP(VUID-FragCoord-FragCoord-04212); | |
| case 4213: | |
| return VUID_WRAP(VUID-FragDepth-FragDepth-04213); | |
| case 4214: | |
| return VUID_WRAP(VUID-FragDepth-FragDepth-04214); | |
| case 4215: | |
| return VUID_WRAP(VUID-FragDepth-FragDepth-04215); | |
| case 4216: | |
| return VUID_WRAP(VUID-FragDepth-FragDepth-04216); | |
| case 4217: | |
| return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04217); | |
| case 4218: | |
| return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04218); | |
| case 4219: | |
| return VUID_WRAP(VUID-FragInvocationCountEXT-FragInvocationCountEXT-04219); | |
| case 4220: | |
| return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04220); | |
| case 4221: | |
| return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04221); | |
| case 4222: | |
| return VUID_WRAP(VUID-FragSizeEXT-FragSizeEXT-04222); | |
| case 4223: | |
| return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04223); | |
| case 4224: | |
| return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04224); | |
| case 4225: | |
| return VUID_WRAP(VUID-FragStencilRefEXT-FragStencilRefEXT-04225); | |
| case 4229: | |
| return VUID_WRAP(VUID-FrontFacing-FrontFacing-04229); | |
| case 4230: | |
| return VUID_WRAP(VUID-FrontFacing-FrontFacing-04230); | |
| case 4231: | |
| return VUID_WRAP(VUID-FrontFacing-FrontFacing-04231); | |
| case 4232: | |
| return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04232); | |
| case 4233: | |
| return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04233); | |
| case 4234: | |
| return VUID_WRAP(VUID-FullyCoveredEXT-FullyCoveredEXT-04234); | |
| case 4236: | |
| return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04236); | |
| case 4237: | |
| return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04237); | |
| case 4238: | |
| return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04238); | |
| case 4239: | |
| return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04239); | |
| case 4240: | |
| return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04240); | |
| case 4241: | |
| return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04241); | |
| case 4242: | |
| return VUID_WRAP(VUID-HitKindKHR-HitKindKHR-04242); | |
| case 4243: | |
| return VUID_WRAP(VUID-HitKindKHR-HitKindKHR-04243); | |
| case 4244: | |
| return VUID_WRAP(VUID-HitKindKHR-HitKindKHR-04244); | |
| case 4245: | |
| return VUID_WRAP(VUID-HitTNV-HitTNV-04245); | |
| case 4246: | |
| return VUID_WRAP(VUID-HitTNV-HitTNV-04246); | |
| case 4247: | |
| return VUID_WRAP(VUID-HitTNV-HitTNV-04247); | |
| case 4248: | |
| return VUID_WRAP(VUID-IncomingRayFlagsKHR-IncomingRayFlagsKHR-04248); | |
| case 4249: | |
| return VUID_WRAP(VUID-IncomingRayFlagsKHR-IncomingRayFlagsKHR-04249); | |
| case 4250: | |
| return VUID_WRAP(VUID-IncomingRayFlagsKHR-IncomingRayFlagsKHR-04250); | |
| case 4251: | |
| return VUID_WRAP(VUID-InstanceCustomIndexKHR-InstanceCustomIndexKHR-04251); | |
| case 4252: | |
| return VUID_WRAP(VUID-InstanceCustomIndexKHR-InstanceCustomIndexKHR-04252); | |
| case 4253: | |
| return VUID_WRAP(VUID-InstanceCustomIndexKHR-InstanceCustomIndexKHR-04253); | |
| case 4254: | |
| return VUID_WRAP(VUID-InstanceId-InstanceId-04254); | |
| case 4255: | |
| return VUID_WRAP(VUID-InstanceId-InstanceId-04255); | |
| case 4256: | |
| return VUID_WRAP(VUID-InstanceId-InstanceId-04256); | |
| case 4257: | |
| return VUID_WRAP(VUID-InvocationId-InvocationId-04257); | |
| case 4258: | |
| return VUID_WRAP(VUID-InvocationId-InvocationId-04258); | |
| case 4259: | |
| return VUID_WRAP(VUID-InvocationId-InvocationId-04259); | |
| case 4263: | |
| return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04263); | |
| case 4264: | |
| return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04264); | |
| case 4265: | |
| return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04265); | |
| case 4266: | |
| return VUID_WRAP(VUID-LaunchIdKHR-LaunchIdKHR-04266); | |
| case 4267: | |
| return VUID_WRAP(VUID-LaunchIdKHR-LaunchIdKHR-04267); | |
| case 4268: | |
| return VUID_WRAP(VUID-LaunchIdKHR-LaunchIdKHR-04268); | |
| case 4269: | |
| return VUID_WRAP(VUID-LaunchSizeKHR-LaunchSizeKHR-04269); | |
| case 4270: | |
| return VUID_WRAP(VUID-LaunchSizeKHR-LaunchSizeKHR-04270); | |
| case 4271: | |
| return VUID_WRAP(VUID-LaunchSizeKHR-LaunchSizeKHR-04271); | |
| case 4272: | |
| return VUID_WRAP(VUID-Layer-Layer-04272); | |
| case 4273: | |
| return VUID_WRAP(VUID-Layer-Layer-04273); | |
| case 4274: | |
| return VUID_WRAP(VUID-Layer-Layer-04274); | |
| case 4275: | |
| return VUID_WRAP(VUID-Layer-Layer-04275); | |
| case 4276: | |
| return VUID_WRAP(VUID-Layer-Layer-04276); | |
| case 4281: | |
| return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04281); | |
| case 4282: | |
| return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04282); | |
| case 4283: | |
| return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04283); | |
| case 4284: | |
| return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04284); | |
| case 4285: | |
| return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04285); | |
| case 4286: | |
| return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04286); | |
| case 4293: | |
| return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04293); | |
| case 4294: | |
| return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04294); | |
| case 4295: | |
| return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04295); | |
| case 4296: | |
| return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04296); | |
| case 4297: | |
| return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04297); | |
| case 4298: | |
| return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04298); | |
| case 4299: | |
| return VUID_WRAP(VUID-ObjectRayDirectionKHR-ObjectRayDirectionKHR-04299); | |
| case 4300: | |
| return VUID_WRAP(VUID-ObjectRayDirectionKHR-ObjectRayDirectionKHR-04300); | |
| case 4301: | |
| return VUID_WRAP(VUID-ObjectRayDirectionKHR-ObjectRayDirectionKHR-04301); | |
| case 4302: | |
| return VUID_WRAP(VUID-ObjectRayOriginKHR-ObjectRayOriginKHR-04302); | |
| case 4303: | |
| return VUID_WRAP(VUID-ObjectRayOriginKHR-ObjectRayOriginKHR-04303); | |
| case 4304: | |
| return VUID_WRAP(VUID-ObjectRayOriginKHR-ObjectRayOriginKHR-04304); | |
| case 4305: | |
| return VUID_WRAP(VUID-ObjectToWorldKHR-ObjectToWorldKHR-04305); | |
| case 4306: | |
| return VUID_WRAP(VUID-ObjectToWorldKHR-ObjectToWorldKHR-04306); | |
| case 4307: | |
| return VUID_WRAP(VUID-ObjectToWorldKHR-ObjectToWorldKHR-04307); | |
| case 4308: | |
| return VUID_WRAP(VUID-PatchVertices-PatchVertices-04308); | |
| case 4309: | |
| return VUID_WRAP(VUID-PatchVertices-PatchVertices-04309); | |
| case 4310: | |
| return VUID_WRAP(VUID-PatchVertices-PatchVertices-04310); | |
| case 4311: | |
| return VUID_WRAP(VUID-PointCoord-PointCoord-04311); | |
| case 4312: | |
| return VUID_WRAP(VUID-PointCoord-PointCoord-04312); | |
| case 4313: | |
| return VUID_WRAP(VUID-PointCoord-PointCoord-04313); | |
| case 4314: | |
| return VUID_WRAP(VUID-PointSize-PointSize-04314); | |
| case 4315: | |
| return VUID_WRAP(VUID-PointSize-PointSize-04315); | |
| case 4316: | |
| return VUID_WRAP(VUID-PointSize-PointSize-04316); | |
| case 4317: | |
| return VUID_WRAP(VUID-PointSize-PointSize-04317); | |
| case 4318: | |
| return VUID_WRAP(VUID-Position-Position-04318); | |
| case 4319: | |
| return VUID_WRAP(VUID-Position-Position-04319); | |
| case 4320: | |
| return VUID_WRAP(VUID-Position-Position-04320); | |
| case 4321: | |
| return VUID_WRAP(VUID-Position-Position-04321); | |
| case 4330: | |
| return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04330); | |
| case 4333: | |
| return VUID_WRAP(VUID-PrimitiveId-Fragment-04333); | |
| case 4334: | |
| return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04334); | |
| case 4336: | |
| return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04336); | |
| case 4337: | |
| return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04337); | |
| case 4345: | |
| return VUID_WRAP(VUID-RayGeometryIndexKHR-RayGeometryIndexKHR-04345); | |
| case 4346: | |
| return VUID_WRAP(VUID-RayGeometryIndexKHR-RayGeometryIndexKHR-04346); | |
| case 4347: | |
| return VUID_WRAP(VUID-RayGeometryIndexKHR-RayGeometryIndexKHR-04347); | |
| case 4348: | |
| return VUID_WRAP(VUID-RayTmaxKHR-RayTmaxKHR-04348); | |
| case 4349: | |
| return VUID_WRAP(VUID-RayTmaxKHR-RayTmaxKHR-04349); | |
| case 4350: | |
| return VUID_WRAP(VUID-RayTmaxKHR-RayTmaxKHR-04350); | |
| case 4351: | |
| return VUID_WRAP(VUID-RayTminKHR-RayTminKHR-04351); | |
| case 4352: | |
| return VUID_WRAP(VUID-RayTminKHR-RayTminKHR-04352); | |
| case 4353: | |
| return VUID_WRAP(VUID-RayTminKHR-RayTminKHR-04353); | |
| case 4354: | |
| return VUID_WRAP(VUID-SampleId-SampleId-04354); | |
| case 4355: | |
| return VUID_WRAP(VUID-SampleId-SampleId-04355); | |
| case 4356: | |
| return VUID_WRAP(VUID-SampleId-SampleId-04356); | |
| case 4357: | |
| return VUID_WRAP(VUID-SampleMask-SampleMask-04357); | |
| case 4358: | |
| return VUID_WRAP(VUID-SampleMask-SampleMask-04358); | |
| case 4359: | |
| return VUID_WRAP(VUID-SampleMask-SampleMask-04359); | |
| case 4360: | |
| return VUID_WRAP(VUID-SamplePosition-SamplePosition-04360); | |
| case 4361: | |
| return VUID_WRAP(VUID-SamplePosition-SamplePosition-04361); | |
| case 4362: | |
| return VUID_WRAP(VUID-SamplePosition-SamplePosition-04362); | |
| case 4367: | |
| return VUID_WRAP(VUID-SubgroupId-SubgroupId-04367); | |
| case 4368: | |
| return VUID_WRAP(VUID-SubgroupId-SubgroupId-04368); | |
| case 4369: | |
| return VUID_WRAP(VUID-SubgroupId-SubgroupId-04369); | |
| case 4370: | |
| return VUID_WRAP(VUID-SubgroupEqMask-SubgroupEqMask-04370); | |
| case 4371: | |
| return VUID_WRAP(VUID-SubgroupEqMask-SubgroupEqMask-04371); | |
| case 4372: | |
| return VUID_WRAP(VUID-SubgroupGeMask-SubgroupGeMask-04372); | |
| case 4373: | |
| return VUID_WRAP(VUID-SubgroupGeMask-SubgroupGeMask-04373); | |
| case 4374: | |
| return VUID_WRAP(VUID-SubgroupGtMask-SubgroupGtMask-04374); | |
| case 4375: | |
| return VUID_WRAP(VUID-SubgroupGtMask-SubgroupGtMask-04375); | |
| case 4376: | |
| return VUID_WRAP(VUID-SubgroupLeMask-SubgroupLeMask-04376); | |
| case 4377: | |
| return VUID_WRAP(VUID-SubgroupLeMask-SubgroupLeMask-04377); | |
| case 4378: | |
| return VUID_WRAP(VUID-SubgroupLtMask-SubgroupLtMask-04378); | |
| case 4379: | |
| return VUID_WRAP(VUID-SubgroupLtMask-SubgroupLtMask-04379); | |
| case 4380: | |
| return VUID_WRAP(VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04380); | |
| case 4381: | |
| return VUID_WRAP(VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04381); | |
| case 4382: | |
| return VUID_WRAP(VUID-SubgroupSize-SubgroupSize-04382); | |
| case 4383: | |
| return VUID_WRAP(VUID-SubgroupSize-SubgroupSize-04383); | |
| case 4387: | |
| return VUID_WRAP(VUID-TessCoord-TessCoord-04387); | |
| case 4388: | |
| return VUID_WRAP(VUID-TessCoord-TessCoord-04388); | |
| case 4389: | |
| return VUID_WRAP(VUID-TessCoord-TessCoord-04389); | |
| case 4390: | |
| return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04390); | |
| case 4391: | |
| return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04391); | |
| case 4392: | |
| return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04392); | |
| case 4393: | |
| return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04393); | |
| case 4394: | |
| return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04394); | |
| case 4395: | |
| return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04395); | |
| case 4396: | |
| return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04396); | |
| case 4397: | |
| return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04397); | |
| case 4398: | |
| return VUID_WRAP(VUID-VertexIndex-VertexIndex-04398); | |
| case 4399: | |
| return VUID_WRAP(VUID-VertexIndex-VertexIndex-04399); | |
| case 4400: | |
| return VUID_WRAP(VUID-VertexIndex-VertexIndex-04400); | |
| case 4401: | |
| return VUID_WRAP(VUID-ViewIndex-ViewIndex-04401); | |
| case 4402: | |
| return VUID_WRAP(VUID-ViewIndex-ViewIndex-04402); | |
| case 4403: | |
| return VUID_WRAP(VUID-ViewIndex-ViewIndex-04403); | |
| case 4404: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04404); | |
| case 4405: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04405); | |
| case 4406: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04406); | |
| case 4407: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04407); | |
| case 4408: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04408); | |
| case 4422: | |
| return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04422); | |
| case 4423: | |
| return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04423); | |
| case 4424: | |
| return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04424); | |
| case 4425: | |
| return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04425); | |
| case 4426: | |
| return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04426); | |
| case 4427: | |
| return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04427); | |
| case 4428: | |
| return VUID_WRAP(VUID-WorldRayDirectionKHR-WorldRayDirectionKHR-04428); | |
| case 4429: | |
| return VUID_WRAP(VUID-WorldRayDirectionKHR-WorldRayDirectionKHR-04429); | |
| case 4430: | |
| return VUID_WRAP(VUID-WorldRayDirectionKHR-WorldRayDirectionKHR-04430); | |
| case 4431: | |
| return VUID_WRAP(VUID-WorldRayOriginKHR-WorldRayOriginKHR-04431); | |
| case 4432: | |
| return VUID_WRAP(VUID-WorldRayOriginKHR-WorldRayOriginKHR-04432); | |
| case 4433: | |
| return VUID_WRAP(VUID-WorldRayOriginKHR-WorldRayOriginKHR-04433); | |
| case 4434: | |
| return VUID_WRAP(VUID-WorldToObjectKHR-WorldToObjectKHR-04434); | |
| case 4435: | |
| return VUID_WRAP(VUID-WorldToObjectKHR-WorldToObjectKHR-04435); | |
| case 4436: | |
| return VUID_WRAP(VUID-WorldToObjectKHR-WorldToObjectKHR-04436); | |
| case 4484: | |
| return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04484); | |
| case 4485: | |
| return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485); | |
| case 4486: | |
| return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486); | |
| case 4490: | |
| return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04490); | |
| case 4491: | |
| return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04491); | |
| case 4492: | |
| return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04492); | |
| case 4633: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04633); | |
| case 4634: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04634); | |
| case 4635: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04635); | |
| case 4636: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04636); | |
| case 4637: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04637); | |
| case 4638: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04638); | |
| case 7321: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-07321); | |
| case 4640: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04640); | |
| case 4641: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04641); | |
| case 4642: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04642); | |
| case 4643: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04643); | |
| case 4644: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04644); | |
| case 4645: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04645); | |
| case 4651: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04651); | |
| case 4652: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpReadClockKHR-04652); | |
| case 4653: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OriginLowerLeft-04653); | |
| case 4654: | |
| return VUID_WRAP(VUID-StandaloneSpirv-PixelCenterInteger-04654); | |
| case 4655: | |
| return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-04655); | |
| case 4656: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04656); | |
| case 4657: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04657); | |
| case 4658: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658); | |
| case 4659: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659); | |
| case 4660: | |
| return VUID_WRAP(VUID-StandaloneSpirv-SubpassData-04660); | |
| case 4664: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664); | |
| case 4667: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04667); | |
| case 4669: | |
| return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); | |
| case 4670: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Flat-04670); | |
| case 4675: | |
| return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); | |
| case 4677: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); | |
| case 4678: | |
| return VUID_WRAP(VUID-StandaloneSpirv-VulkanMemoryModel-04678); | |
| case 4679: | |
| return VUID_WRAP(VUID-StandaloneSpirv-VulkanMemoryModel-04679); | |
| case 4680: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680); | |
| case 4682: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); | |
| case 4685: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); | |
| case 4686: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-04686); | |
| case 4698: | |
| return VUID_WRAP(VUID-StandaloneSpirv-RayPayloadKHR-04698); | |
| case 4699: | |
| return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04699); | |
| case 4700: | |
| return VUID_WRAP(VUID-StandaloneSpirv-IncomingRayPayloadKHR-04700); | |
| case 4701: | |
| return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04701); | |
| case 4702: | |
| return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04702); | |
| case 4703: | |
| return VUID_WRAP(VUID-StandaloneSpirv-HitAttributeKHR-04703); | |
| case 4704: | |
| return VUID_WRAP(VUID-StandaloneSpirv-CallableDataKHR-04704); | |
| case 4705: | |
| return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04705); | |
| case 4706: | |
| return VUID_WRAP(VUID-StandaloneSpirv-IncomingCallableDataKHR-04706); | |
| case 7119: | |
| return VUID_WRAP(VUID-StandaloneSpirv-ShaderRecordBufferKHR-07119); | |
| case 4708: | |
| return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708); | |
| case 4710: | |
| return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710); | |
| case 4711: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711); | |
| case 4716: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Offset-04716); | |
| case 4734: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734); | |
| case 4744: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Flat-04744); | |
| case 4777: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777); | |
| case 4780: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Result-04780); | |
| case 4865: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Offset-04865); | |
| case 4915: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Location-04915); | |
| case 4916: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Location-04916); | |
| case 4917: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Location-04917); | |
| case 4918: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Location-04918); | |
| case 4919: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Location-04919); | |
| case 4920: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Component-04920); | |
| case 4921: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Component-04921); | |
| case 4922: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Component-04922); | |
| case 4923: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Component-04923); | |
| case 4965: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Image-04965); | |
| case 6201: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201); | |
| case 6202: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202); | |
| case 6214: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214); | |
| case 6314: | |
| return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314); | |
| case 6349: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06349); | |
| case 6350: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpRayQueryInitializeKHR-06350); | |
| case 6356: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpTraceRayKHR-06356); | |
| case 6357: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpTraceRayKHR-06357); | |
| case 6491: | |
| return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491); | |
| case 6671: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeSampledImage-06671); | |
| case 6672: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Location-06672); | |
| case 6673: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-06673); | |
| case 6674: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-06674); | |
| case 6675: | |
| return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06675); | |
| case 6676: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06676); | |
| case 6677: | |
| return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677); | |
| case 6678: | |
| return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678); | |
| case 6777: | |
| return VUID_WRAP(VUID-StandaloneSpirv-PerVertexKHR-06777); | |
| case 6778: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Input-06778); | |
| case 6807: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06807); | |
| case 6808: | |
| return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808); | |
| case 6924: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06924); | |
| case 6925: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925); | |
| case 7034: | |
| return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034); | |
| case 7035: | |
| return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07035); | |
| case 7036: | |
| return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036); | |
| case 7038: | |
| return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07038); | |
| case 7039: | |
| return VUID_WRAP(VUID-Layer-Layer-07039); | |
| case 7040: | |
| return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-07040); | |
| case 7041: | |
| return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041); | |
| case 7042: | |
| return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042); | |
| case 7043: | |
| return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07043); | |
| case 7044: | |
| return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044); | |
| case 7046: | |
| return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07046); | |
| case 7047: | |
| return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07047); | |
| case 7048: | |
| return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048); | |
| case 7049: | |
| return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07049); | |
| case 7050: | |
| return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07050); | |
| case 7052: | |
| return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07052); | |
| case 7053: | |
| return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07053); | |
| case 7054: | |
| return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07054); | |
| case 7055: | |
| return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07055); | |
| case 7056: | |
| return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07056); | |
| case 7058: | |
| return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07058); | |
| case 7059: | |
| return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-07059); | |
| case 7060: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-07060); | |
| case 7102: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102); | |
| case 7107: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07107); | |
| case 7290: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Input-07290); | |
| case 7320: | |
| return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320); | |
| case 7330: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07330); | |
| case 7331: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07331); | |
| case 7650: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Base-07650); | |
| case 7651: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Base-07651); | |
| case 7652: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Base-07652); | |
| case 7703: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Component-07703); | |
| case 7706: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07706); | |
| case 7707: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpHitObjectTraceRayNV-07707); | |
| case 7951: | |
| return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-07951); | |
| case 8720: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-08720); | |
| case 8721: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08721); | |
| case 8722: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08722); | |
| case 8723: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileImageEXT-08723); | |
| case 8724: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-08724); | |
| case 8747: | |
| return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08747); | |
| case 8748: | |
| return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08748); | |
| case 8749: | |
| return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08749); | |
| case 8973: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Pointer-08973); | |
| case 9557: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Input-09557); | |
| case 9565: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MaximallyReconvergesKHR-09565); | |
| case 9638: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-09638); | |
| case 9658: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09658); | |
| case 9659: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09659); | |
| case 9931: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpGraphInputARM-09931); | |
| case 9932: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpGraphSetOutputARM-09932); | |
| case 10151: | |
| return VUID_WRAP(VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151); | |
| case 10152: | |
| return VUID_WRAP(VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152); | |
| case 10213: | |
| // This use to be a standalone, but maintenance8 will set allow_offset_texture_operand now | |
| return VUID_WRAP(VUID-RuntimeSpirv-Offset-10213); | |
| case 10370: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10370); | |
| case 10583: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Component-10583); | |
| case 10589: | |
| return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10589); | |
| case 10590: | |
| return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10590); | |
| case 10591: | |
| return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10591); | |
| case 10592: | |
| return VUID_WRAP(VUID-Layer-Layer-10592); | |
| case 10593: | |
| return VUID_WRAP(VUID-Layer-Layer-10593); | |
| case 10594: | |
| return VUID_WRAP(VUID-Layer-Layer-10594); | |
| case 10598: | |
| return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10598); | |
| case 10599: | |
| return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10599); | |
| case 10600: | |
| return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10600); | |
| case 10601: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10601); | |
| case 10602: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10602); | |
| case 10603: | |
| return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10603); | |
| case 10626: | |
| return VUID_WRAP(VUID-TileOffsetQCOM-TileOffsetQCOM-10626); | |
| case 10627: | |
| return VUID_WRAP(VUID-TileOffsetQCOM-TileOffsetQCOM-10627); | |
| case 10628: | |
| return VUID_WRAP(VUID-TileOffsetQCOM-TileOffsetQCOM-10628); | |
| case 10629: | |
| return VUID_WRAP(VUID-TileDimensionQCOM-TileDimensionQCOM-10629); | |
| case 10630: | |
| return VUID_WRAP(VUID-TileDimensionQCOM-TileDimensionQCOM-10630); | |
| case 10631: | |
| return VUID_WRAP(VUID-TileDimensionQCOM-TileDimensionQCOM-10631); | |
| case 10632: | |
| return VUID_WRAP(VUID-TileApronSizeQCOM-TileApronSizeQCOM-10632); | |
| case 10633: | |
| return VUID_WRAP(VUID-TileApronSizeQCOM-TileApronSizeQCOM-10633); | |
| case 10634: | |
| return VUID_WRAP(VUID-TileApronSizeQCOM-TileApronSizeQCOM-10634); | |
| case 10635: | |
| return VUID_WRAP(VUID-WorkgroupSize-TileShadingRateQCOM-10635); | |
| case 10684: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-10684); | |
| case 10685: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-10685); // formally 04683/06426 | |
| case 10686: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileShadingQCOM-10686); | |
| case 10687: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Execution-10687); | |
| case 10688: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Execution-10688); | |
| case 10689: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileAttachmentQCOM-10689); | |
| case 10690: | |
| return VUID_WRAP(VUID-StandaloneSpirv-NonCoherentTileAttachmentReadQCOM-10690); | |
| case 10691: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileShadingRateQCOM-10691); | |
| case 10692: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileShadingRateQCOM-10692); | |
| case 10693: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-10693); | |
| case 10694: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-10694); | |
| case 10695: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileAttachmentQCOM-10695); | |
| case 10696: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileAttachmentQCOM-10696); | |
| case 10697: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TileAttachmentQCOM-10697); | |
| case 10823: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10823); | |
| case 10824: | |
| // This use to be a standalone, but maintenance9 will set allow_vulkan_32_bit_bitwise now | |
| return VUID_WRAP(VUID-RuntimeSpirv-None-10824); | |
| case 10865: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10865); | |
| case 10866: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10866); | |
| case 10867: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10867); | |
| case 10868: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10868); | |
| case 10869: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10869); | |
| case 10870: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10870); | |
| case 10871: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10871); | |
| case 10872: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10872); | |
| case 10873: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10873); | |
| case 10875: | |
| return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10875); | |
| case 10876: | |
| return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10876); | |
| case 10877: | |
| return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10877); | |
| case 10878: | |
| return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10878); | |
| case 10879: | |
| return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10879); | |
| case 10880: | |
| return VUID_WRAP(VUID-StandaloneSpirv-TessLevelInner-10880); | |
| case 11165: | |
| // Validation (via GPU-AV) will catch this if a non-constant | |
| return VUID_WRAP(VUID-RuntimeSpirv-Size-11165); | |
| case 11167: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11167); | |
| case 11239: | |
| return VUID_WRAP(VUID-SamplerHeapEXT-SamplerHeapEXT-11239); | |
| case 11241: | |
| return VUID_WRAP(VUID-ResourceHeapEXT-ResourceHeapEXT-11241); | |
| case 11336: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Result-11336); | |
| case 11337: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Result-11337); | |
| case 11339: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Result-11339); | |
| case 11347: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11347); | |
| case 11416: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedImageTexelPointerEXT-11416); | |
| case 11417: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpTypeUntypedPointerKHR-11417); | |
| // These RuntimeSpirv are here because now we can pass in the size/alignment | |
| // which in turns mean we can validate things based on runtime properties | |
| case 11476: | |
| return VUID_WRAP(VUID-RuntimeSpirv-samplerDescriptorAlignment-11476); | |
| case 11477: | |
| return VUID_WRAP(VUID-RuntimeSpirv-imageDescriptorAlignment-11477); | |
| case 11478: | |
| return VUID_WRAP(VUID-RuntimeSpirv-bufferDescriptorAlignment-11478); | |
| case 11479: | |
| return VUID_WRAP(VUID-RuntimeSpirv-bufferDescriptorAlignment-11479); | |
| case 11480: | |
| return VUID_WRAP(VUID-RuntimeSpirv-tensorDescriptorAlignment-11480); | |
| case 11482: | |
| return VUID_WRAP(VUID-StandaloneSpirv-DescriptorHeapEXT-11482); | |
| case 11805: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpArrayLength-11805); | |
| case 11879: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpHitObjectTraceRayEXT-11879); | |
| case 11880: | |
| return VUID_WRAP(VUID-RuntimeSpirv-OpHitObjectTraceRayEXT-11880); | |
| case 12243: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Scope-12243); | |
| case 12294: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Function-12294); | |
| case 12295: | |
| return VUID_WRAP(VUID-StandaloneSpirv-None-12295); | |
| case 12297: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Type-12297); | |
| case 13551: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-13551); | |
| case 13552: | |
| return VUID_WRAP(VUID-StandaloneSpirv-SplitBarrierEXT-13552); | |
| case 13553: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrierArriveEXT-13553); | |
| case 13556: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-13556); | |
| case 13557: | |
| return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-13557); | |
| case 12464: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpUndef-12464); | |
| case 12465: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpConvertFToU-12465); | |
| case 12466: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpFConvert-12466); | |
| case 12467: | |
| return VUID_WRAP(VUID-StandaloneSpirv-OpFConvert-12467); | |
| case 12468: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Result-12468); | |
| case 12469: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Base-12469); | |
| case 12509: | |
| return VUID_WRAP(VUID-StandaloneSpirv-Offset-12509); | |
| default: | |
| return ""; // unknown id | |
| } | |
| // clang-format on | |
| } | |
| } // namespace val | |
| } // namespace spvtools |