| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682 |
- // Copyright (c) 2018 Google LLC.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include <algorithm>
- #include <vector>
- #include "source/spirv_constant.h"
- #include "source/spirv_target_env.h"
- #include "source/val/function.h"
- #include "source/val/instruction.h"
- #include "source/val/validate.h"
- #include "source/val/validation_state.h"
- namespace spvtools {
- namespace val {
- namespace {
- // Limit the number of checked locations to 4096. Multiplied by 4 to represent
- // all the components. This limit is set to be well beyond practical use cases.
- const uint32_t kMaxLocations = 4096 * 4;
- // Returns true if \c inst is an input or output variable.
- bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
- if (is_spv_1_4) {
- // Starting in SPIR-V 1.4, all global variables are interface variables.
- return (inst->opcode() == spv::Op::OpVariable ||
- inst->opcode() == spv::Op::OpUntypedVariableKHR) &&
- inst->GetOperandAs<spv::StorageClass>(2u) !=
- spv::StorageClass::Function;
- } else {
- return (inst->opcode() == spv::Op::OpVariable ||
- inst->opcode() == spv::Op::OpUntypedVariableKHR) &&
- (inst->GetOperandAs<spv::StorageClass>(2u) ==
- spv::StorageClass::Input ||
- inst->GetOperandAs<spv::StorageClass>(2u) ==
- spv::StorageClass::Output);
- }
- }
- // Special validation for varibles that are between shader stages
- spv_result_t ValidateInputOutputInterfaceVariables(ValidationState_t& _,
- const Instruction* var) {
- auto var_pointer = _.FindDef(var->GetOperandAs<uint32_t>(0));
- uint32_t pointer_id = var_pointer->GetOperandAs<uint32_t>(2);
- const auto isPhysicalStorageBuffer = [](const Instruction* insn) {
- return insn->opcode() == spv::Op::OpTypePointer &&
- insn->GetOperandAs<spv::StorageClass>(1) ==
- spv::StorageClass::PhysicalStorageBuffer;
- };
- if (_.ContainsType(pointer_id, isPhysicalStorageBuffer)) {
- return _.diag(SPV_ERROR_INVALID_ID, var)
- << _.VkErrorID(9557) << "Input/Output interface variable id <"
- << var->id()
- << "> contains a PhysicalStorageBuffer pointer, which is not "
- "allowed. If you want to interface shader stages with a "
- "PhysicalStorageBuffer, cast to a uint64 or uvec2 instead.";
- }
- return SPV_SUCCESS;
- }
- // Checks that \c var is listed as an interface in all the entry points that use
- // it.
- spv_result_t check_interface_variable(ValidationState_t& _,
- const Instruction* var) {
- std::vector<const Function*> functions;
- std::vector<const Instruction*> uses;
- for (auto use : var->uses()) {
- uses.push_back(use.first);
- }
- for (uint32_t i = 0; i < uses.size(); ++i) {
- const auto user = uses[i];
- if (const Function* func = user->function()) {
- functions.push_back(func);
- } else {
- // In the rare case that the variable is used by another instruction in
- // the global scope, continue searching for an instruction used in a
- // function.
- for (auto use : user->uses()) {
- uses.push_back(use.first);
- }
- }
- }
- std::sort(functions.begin(), functions.end(),
- [](const Function* lhs, const Function* rhs) {
- return lhs->id() < rhs->id();
- });
- functions.erase(std::unique(functions.begin(), functions.end()),
- functions.end());
- std::vector<uint32_t> entry_points;
- for (const auto func : functions) {
- for (auto id : _.FunctionEntryPoints(func->id())) {
- entry_points.push_back(id);
- }
- }
- std::sort(entry_points.begin(), entry_points.end());
- entry_points.erase(std::unique(entry_points.begin(), entry_points.end()),
- entry_points.end());
- for (auto id : entry_points) {
- for (const auto& desc : _.entry_point_descriptions(id)) {
- bool found = false;
- for (auto interface : desc.interfaces) {
- if (var->id() == interface) {
- found = true;
- break;
- }
- }
- if (!found) {
- return _.diag(SPV_ERROR_INVALID_ID, var)
- << "Interface variable id <" << var->id()
- << "> is used by entry point '" << desc.name << "' id <" << id
- << ">, but is not listed as an interface";
- }
- }
- }
- if (var->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Input ||
- var->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Output) {
- if (auto error = ValidateInputOutputInterfaceVariables(_, var))
- return error;
- }
- return SPV_SUCCESS;
- }
- // This function assumes a base location has been determined already. As such
- // any further location decorations are invalid.
- // TODO: if this code turns out to be slow, there is an opportunity to cache
- // the result for a given type id.
- spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type,
- uint32_t* num_locations) {
- *num_locations = 0;
- switch (type->opcode()) {
- case spv::Op::OpTypeInt:
- case spv::Op::OpTypeFloat:
- // Scalars always consume a single location.
- *num_locations = 1;
- break;
- case spv::Op::OpTypeVector:
- // 3- and 4-component 64-bit vectors consume two locations.
- if ((_.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeInt, 64) ||
- _.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeFloat,
- 64)) &&
- (type->GetOperandAs<uint32_t>(2) > 2)) {
- *num_locations = 2;
- } else {
- *num_locations = 1;
- }
- break;
- case spv::Op::OpTypeMatrix:
- // Matrices consume locations equivalent to arrays of 4-component vectors.
- if (_.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeInt, 64) ||
- _.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeFloat, 64)) {
- *num_locations = 2;
- } else {
- *num_locations = 1;
- }
- *num_locations *= type->GetOperandAs<uint32_t>(2);
- break;
- case spv::Op::OpTypeArray: {
- // Arrays consume locations equal to the underlying type times the number
- // of elements in the vector.
- if (auto error = NumConsumedLocations(
- _, _.FindDef(type->GetOperandAs<uint32_t>(1)), num_locations)) {
- return error;
- }
- bool is_int = false;
- bool is_const = false;
- uint32_t value = 0;
- // Attempt to evaluate the number of array elements.
- std::tie(is_int, is_const, value) =
- _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
- if (is_int && is_const) *num_locations *= value;
- break;
- }
- case spv::Op::OpTypeStruct: {
- // Members cannot have location decorations at this point.
- if (_.HasDecoration(type->id(), spv::Decoration::Location)) {
- return _.diag(SPV_ERROR_INVALID_DATA, type)
- << _.VkErrorID(4918) << "Members cannot be assigned a location";
- }
- // Structs consume locations equal to the sum of the locations consumed
- // by the members.
- for (uint32_t i = 1; i < type->operands().size(); ++i) {
- uint32_t member_locations = 0;
- if (auto error = NumConsumedLocations(
- _, _.FindDef(type->GetOperandAs<uint32_t>(i)),
- &member_locations)) {
- return error;
- }
- *num_locations += member_locations;
- }
- break;
- }
- case spv::Op::OpTypePointer: {
- if (_.addressing_model() ==
- spv::AddressingModel::PhysicalStorageBuffer64 &&
- type->GetOperandAs<spv::StorageClass>(1) ==
- spv::StorageClass::PhysicalStorageBuffer) {
- *num_locations = 1;
- break;
- }
- [[fallthrough]];
- }
- default:
- return _.diag(SPV_ERROR_INVALID_DATA, type)
- << "Invalid type to assign a location";
- }
- return SPV_SUCCESS;
- }
- // Returns the number of components consumed by types that support a component
- // decoration.
- uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
- uint32_t num_components = 0;
- switch (type->opcode()) {
- case spv::Op::OpTypeInt:
- case spv::Op::OpTypeFloat:
- // 64-bit types consume two components.
- if (type->GetOperandAs<uint32_t>(1) == 64) {
- num_components = 2;
- } else {
- num_components = 1;
- }
- break;
- case spv::Op::OpTypeVector:
- // Vectors consume components equal to the underlying type's consumption
- // times the number of elements in the vector. Note that 3- and 4-element
- // vectors cannot have a component decoration (i.e. assumed to be zero).
- num_components =
- NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
- num_components *= type->GetOperandAs<uint32_t>(2);
- break;
- case spv::Op::OpTypeMatrix:
- // Matrices consume all components of the location.
- // Round up to next multiple of 4.
- num_components =
- NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
- num_components *= type->GetOperandAs<uint32_t>(2);
- num_components = ((num_components + 3) / 4) * 4;
- break;
- case spv::Op::OpTypeArray: {
- // Arrays consume all components of the location.
- // Round up to next multiple of 4.
- num_components =
- NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
- bool is_int = false;
- bool is_const = false;
- uint32_t value = 0;
- // Attempt to evaluate the number of array elements.
- std::tie(is_int, is_const, value) =
- _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
- if (is_int && is_const) num_components *= value;
- num_components = ((num_components + 3) / 4) * 4;
- return num_components;
- }
- case spv::Op::OpTypePointer:
- if (_.addressing_model() ==
- spv::AddressingModel::PhysicalStorageBuffer64 &&
- type->GetOperandAs<spv::StorageClass>(1) ==
- spv::StorageClass::PhysicalStorageBuffer) {
- return 2;
- }
- break;
- default:
- // This is an error that is validated elsewhere.
- break;
- }
- return num_components;
- }
- // Populates |locations| (and/or |output_index1_locations|) with the use
- // location and component coordinates for |variable|. Indices are calculated as
- // 4 * location + component.
- spv_result_t GetLocationsForVariable(
- ValidationState_t& _, const Instruction* entry_point,
- const Instruction* variable, std::unordered_set<uint32_t>* locations,
- std::unordered_set<uint32_t>* output_index1_locations) {
- const bool is_fragment = entry_point->GetOperandAs<spv::ExecutionModel>(0) ==
- spv::ExecutionModel::Fragment;
- const auto sc_index = 2u;
- const bool is_output = variable->GetOperandAs<spv::StorageClass>(sc_index) ==
- spv::StorageClass::Output;
- auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
- auto ptr_type = _.FindDef(ptr_type_id);
- auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
- auto type = _.FindDef(type_id);
- // Check for Location, Component and Index decorations on the variable. The
- // validator allows duplicate decorations if the location/component/index are
- // equal. Also track Patch and PerTaskNV decorations.
- bool has_location = false;
- uint32_t location = 0;
- uint32_t component = 0;
- bool has_index = false;
- uint32_t index = 0;
- bool has_patch = false;
- bool has_per_task_nv = false;
- bool has_per_vertex_khr = false;
- // Duplicate Location, Component, Index are checked elsewhere.
- for (auto& dec : _.id_decorations(variable->id())) {
- if (dec.dec_type() == spv::Decoration::Location) {
- has_location = true;
- location = dec.params()[0];
- } else if (dec.dec_type() == spv::Decoration::Component) {
- component = dec.params()[0];
- } else if (dec.dec_type() == spv::Decoration::Index) {
- if (!is_output || !is_fragment) {
- return _.diag(SPV_ERROR_INVALID_DATA, variable)
- << "Index can only be applied to Fragment output variables";
- }
- has_index = true;
- index = dec.params()[0];
- } else if (dec.dec_type() == spv::Decoration::BuiltIn) {
- // Don't check built-ins.
- return SPV_SUCCESS;
- } else if (dec.dec_type() == spv::Decoration::Patch) {
- has_patch = true;
- } else if (dec.dec_type() == spv::Decoration::PerTaskNV) {
- has_per_task_nv = true;
- } else if (dec.dec_type() == spv::Decoration::PerVertexKHR) {
- if (!is_fragment) {
- return _.diag(SPV_ERROR_INVALID_DATA, variable)
- << _.VkErrorID(6777)
- << "PerVertexKHR can only be applied to Fragment Execution "
- "Models";
- }
- if (type->opcode() != spv::Op::OpTypeArray &&
- type->opcode() != spv::Op::OpTypeRuntimeArray) {
- return _.diag(SPV_ERROR_INVALID_DATA, variable)
- << _.VkErrorID(6778)
- << "PerVertexKHR must be declared as arrays";
- }
- has_per_vertex_khr = true;
- }
- }
- // Vulkan 15.1.3 (Interface Matching): Tessellation control and mesh
- // per-vertex outputs and tessellation control, evaluation and geometry
- // per-vertex inputs have a layer of arraying that is not included in
- // interface matching.
- bool is_arrayed = false;
- switch (entry_point->GetOperandAs<spv::ExecutionModel>(0)) {
- case spv::ExecutionModel::TessellationControl:
- if (!has_patch) {
- is_arrayed = true;
- }
- break;
- case spv::ExecutionModel::TessellationEvaluation:
- if (!is_output && !has_patch) {
- is_arrayed = true;
- }
- break;
- case spv::ExecutionModel::Geometry:
- if (!is_output) {
- is_arrayed = true;
- }
- break;
- case spv::ExecutionModel::Fragment:
- if (!is_output && has_per_vertex_khr) {
- is_arrayed = true;
- }
- break;
- case spv::ExecutionModel::MeshNV:
- if (is_output && !has_per_task_nv) {
- is_arrayed = true;
- }
- break;
- default:
- break;
- }
- // Unpack arrayness.
- if (is_arrayed && (type->opcode() == spv::Op::OpTypeArray ||
- type->opcode() == spv::Op::OpTypeRuntimeArray)) {
- type_id = type->GetOperandAs<uint32_t>(1);
- type = _.FindDef(type_id);
- }
- if (type->opcode() == spv::Op::OpTypeStruct) {
- // Don't check built-ins.
- if (_.HasDecoration(type_id, spv::Decoration::BuiltIn)) return SPV_SUCCESS;
- }
- // Only block-decorated structs don't need a location on the variable.
- const bool is_block = _.HasDecoration(type_id, spv::Decoration::Block);
- if (!has_location && !is_block) {
- const auto vuid = (type->opcode() == spv::Op::OpTypeStruct) ? 4917 : 4916;
- return _.diag(SPV_ERROR_INVALID_DATA, variable)
- << _.VkErrorID(vuid) << "Variable must be decorated with a location";
- }
- const std::string storage_class = is_output ? "output" : "input";
- if (has_location) {
- uint32_t num_locations = 0;
- if (auto error = NumConsumedLocations(_, type, &num_locations))
- return error;
- uint32_t num_components = NumConsumedComponents(_, type);
- uint32_t start = location * 4;
- uint32_t end = (location + num_locations) * 4;
- if (num_components % 4 != 0) {
- start += component;
- end = start + num_components;
- }
- if (kMaxLocations <= start) {
- // Too many locations, give up.
- return SPV_SUCCESS;
- }
- auto locs = locations;
- if (has_index && index == 1) locs = output_index1_locations;
- for (uint32_t i = start; i < end; ++i) {
- if (!locs->insert(i).second) {
- return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
- << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
- << "Entry-point has conflicting " << storage_class
- << " location assignment at location " << i / 4 << ", component "
- << i % 4;
- }
- }
- } else {
- // For Block-decorated structs with no location assigned to the variable,
- // each member of the block must be assigned a location. Also record any
- // member component assignments. The validator allows duplicate decorations
- // if they agree on the location/component.
- std::unordered_map<uint32_t, uint32_t> member_locations;
- std::unordered_map<uint32_t, uint32_t> member_components;
- for (auto& dec : _.id_decorations(type_id)) {
- if (dec.dec_type() == spv::Decoration::Location) {
- auto where = member_locations.find(dec.struct_member_index());
- if (where == member_locations.end()) {
- member_locations[dec.struct_member_index()] = dec.params()[0];
- } else if (where->second != dec.params()[0]) {
- return _.diag(SPV_ERROR_INVALID_DATA, type)
- << "Member index " << dec.struct_member_index()
- << " has conflicting location assignments";
- }
- } else if (dec.dec_type() == spv::Decoration::Component) {
- auto where = member_components.find(dec.struct_member_index());
- if (where == member_components.end()) {
- member_components[dec.struct_member_index()] = dec.params()[0];
- } else if (where->second != dec.params()[0]) {
- return _.diag(SPV_ERROR_INVALID_DATA, type)
- << "Member index " << dec.struct_member_index()
- << " has conflicting component assignments";
- }
- }
- }
- for (uint32_t i = 1; i < type->operands().size(); ++i) {
- auto where = member_locations.find(i - 1);
- if (where == member_locations.end()) {
- return _.diag(SPV_ERROR_INVALID_DATA, type)
- << _.VkErrorID(4919) << "Member index " << i - 1
- << " is missing a location assignment";
- }
- location = where->second;
- auto member = _.FindDef(type->GetOperandAs<uint32_t>(i));
- uint32_t num_locations = 0;
- if (auto error = NumConsumedLocations(_, member, &num_locations))
- return error;
- // If the component is not specified, it is assumed to be zero.
- uint32_t num_components = NumConsumedComponents(_, member);
- component = 0;
- if (member_components.count(i - 1)) {
- component = member_components[i - 1];
- }
- uint32_t start = location * 4;
- if (kMaxLocations <= start) {
- // Too many locations, give up.
- continue;
- }
- uint32_t end = (location + num_locations) * 4;
- if (num_components % 4 != 0) {
- start += component;
- end = location * 4 + component + num_components;
- }
- for (uint32_t l = start; l < end; ++l) {
- if (!locations->insert(l).second) {
- return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
- << (is_output ? _.VkErrorID(8722) : _.VkErrorID(8721))
- << "Entry-point has conflicting " << storage_class
- << " location assignment at location " << l / 4
- << ", component " << l % 4;
- }
- }
- }
- }
- return SPV_SUCCESS;
- }
- spv_result_t ValidateLocations(ValidationState_t& _,
- const Instruction* entry_point) {
- // According to Vulkan 14.1 only the following execution models have
- // locations assigned.
- // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables,
- // in other shader stages. Similarly, the *provisional* version of
- // SPV_KHR_ray_tracing did as well, but not the final version.
- switch (entry_point->GetOperandAs<spv::ExecutionModel>(0)) {
- case spv::ExecutionModel::Vertex:
- case spv::ExecutionModel::TessellationControl:
- case spv::ExecutionModel::TessellationEvaluation:
- case spv::ExecutionModel::Geometry:
- case spv::ExecutionModel::Fragment:
- break;
- default:
- return SPV_SUCCESS;
- }
- // Locations are stored as a combined location and component values.
- std::unordered_set<uint32_t> input_locations;
- std::unordered_set<uint32_t> output_locations_index0;
- std::unordered_set<uint32_t> output_locations_index1;
- std::unordered_set<uint32_t> patch_locations_index0;
- std::unordered_set<uint32_t> patch_locations_index1;
- std::unordered_set<uint32_t> seen;
- for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
- auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
- auto interface_var = _.FindDef(interface_id);
- const auto sc_index = 2u;
- auto storage_class =
- interface_var->GetOperandAs<spv::StorageClass>(sc_index);
- if (storage_class != spv::StorageClass::Input &&
- storage_class != spv::StorageClass::Output) {
- continue;
- }
- if (!seen.insert(interface_id).second) {
- // Pre-1.4 an interface variable could be listed multiple times in an
- // entry point. Validation for 1.4 or later is done elsewhere.
- continue;
- }
- // The two Tessellation stages have a "Patch" variable that interface with
- // the Location mechanism, but are not suppose to be tied to the "normal"
- // input/output Location.
- // TODO - SPIR-V allows the Patch decoration to be applied to struct
- // members, but is not allowed in GLSL/HLSL
- bool has_patch = false;
- for (auto& dec : _.id_decorations(interface_var->id())) {
- if (dec.dec_type() == spv::Decoration::Patch) {
- has_patch = true;
- if (auto error = GetLocationsForVariable(_, entry_point, interface_var,
- &patch_locations_index0,
- &patch_locations_index1))
- return error;
- break;
- }
- }
- if (has_patch) {
- continue;
- }
- auto locations = (storage_class == spv::StorageClass::Input)
- ? &input_locations
- : &output_locations_index0;
- if (auto error = GetLocationsForVariable(
- _, entry_point, interface_var, locations, &output_locations_index1))
- return error;
- }
- return SPV_SUCCESS;
- }
- spv_result_t ValidateStorageClass(ValidationState_t& _,
- const Instruction* entry_point) {
- bool has_push_constant = false;
- bool has_ray_payload = false;
- bool has_hit_attribute = false;
- bool has_callable_data = false;
- for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
- auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
- auto interface_var = _.FindDef(interface_id);
- auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2);
- switch (storage_class) {
- case spv::StorageClass::PushConstant: {
- if (has_push_constant) {
- return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
- << _.VkErrorID(6673)
- << "Entry-point has more than one variable with the "
- "PushConstant storage class in the interface";
- }
- has_push_constant = true;
- break;
- }
- case spv::StorageClass::IncomingRayPayloadKHR: {
- if (has_ray_payload) {
- return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
- << _.VkErrorID(4700)
- << "Entry-point has more than one variable with the "
- "IncomingRayPayloadKHR storage class in the interface";
- }
- has_ray_payload = true;
- break;
- }
- case spv::StorageClass::HitAttributeKHR: {
- if (has_hit_attribute) {
- return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
- << _.VkErrorID(4702)
- << "Entry-point has more than one variable with the "
- "HitAttributeKHR storage class in the interface";
- }
- has_hit_attribute = true;
- break;
- }
- case spv::StorageClass::IncomingCallableDataKHR: {
- if (has_callable_data) {
- return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
- << _.VkErrorID(4706)
- << "Entry-point has more than one variable with the "
- "IncomingCallableDataKHR storage class in the interface";
- }
- has_callable_data = true;
- break;
- }
- default:
- break;
- }
- }
- return SPV_SUCCESS;
- }
- } // namespace
- spv_result_t ValidateInterfaces(ValidationState_t& _) {
- bool is_spv_1_4 = _.version() >= SPV_SPIRV_VERSION_WORD(1, 4);
- for (auto& inst : _.ordered_instructions()) {
- if (is_interface_variable(&inst, is_spv_1_4)) {
- if (auto error = check_interface_variable(_, &inst)) {
- return error;
- }
- }
- }
- if (spvIsVulkanEnv(_.context()->target_env)) {
- for (auto& inst : _.ordered_instructions()) {
- if (inst.opcode() == spv::Op::OpEntryPoint) {
- if (auto error = ValidateLocations(_, &inst)) {
- return error;
- }
- if (auto error = ValidateStorageClass(_, &inst)) {
- return error;
- }
- }
- if (inst.opcode() == spv::Op::OpTypeVoid) break;
- }
- }
- return SPV_SUCCESS;
- }
- } // namespace val
- } // namespace spvtools
|