| // Copyright (c) 2017 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "validate.h" |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include "diagnostic.h" |
| #include "opcode.h" |
| #include "val/validation_state.h" |
| |
| using libspirv::Decoration; |
| using libspirv::DiagnosticStream; |
| using libspirv::Instruction; |
| using libspirv::ValidationState_t; |
| |
| namespace { |
| |
| // Returns whether the given structure type has any members with BuiltIn |
| // decoration. |
| bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) { |
| const auto& decorations = vstate.id_decorations(struct_id); |
| return std::any_of( |
| decorations.begin(), decorations.end(), [](const Decoration& d) { |
| return SpvDecorationBuiltIn == d.dec_type() && |
| Decoration::kInvalidMember != d.struct_member_index(); |
| }); |
| } |
| |
| // Returns true if the given ID has the Import LinkageAttributes decoration. |
| bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) { |
| const auto& decorations = vstate.id_decorations(id); |
| return std::any_of(decorations.begin(), decorations.end(), |
| [](const Decoration& d) { |
| return SpvDecorationLinkageAttributes == d.dec_type() && |
| d.params().size() >= 2u && |
| d.params().back() == SpvLinkageTypeImport; |
| }); |
| } |
| |
| spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) { |
| for (const auto& function : vstate.functions()) { |
| if (function.block_count() == 0u) { |
| // A function declaration (an OpFunction with no basic blocks), must have |
| // a Linkage Attributes Decoration with the Import Linkage Type. |
| if (!hasImportLinkageAttribute(function.id(), vstate)) { |
| return vstate.diag(SPV_ERROR_INVALID_BINARY) |
| << "Function declaration (id " << function.id() |
| << ") must have a LinkageAttributes decoration with the Import " |
| "Linkage type."; |
| } |
| } else { |
| if (hasImportLinkageAttribute(function.id(), vstate)) { |
| return vstate.diag(SPV_ERROR_INVALID_BINARY) |
| << "Function definition (id " << function.id() |
| << ") may not be decorated with Import Linkage type."; |
| } |
| } |
| } |
| return SPV_SUCCESS; |
| } |
| |
| // Checks whether an imported variable is initialized by this module. |
| spv_result_t CheckImportedVariableInitialization(ValidationState_t& vstate) { |
| // According the SPIR-V Spec 2.16.1, it is illegal to initialize an imported |
| // variable. This means that a module-scope OpVariable with initialization |
| // value cannot be marked with the Import Linkage Type (import type id = 1). |
| for (auto global_var_id : vstate.global_vars()) { |
| // Initializer <id> is an optional argument for OpVariable. If initializer |
| // <id> is present, the instruction will have 5 words. |
| auto variable_instr = vstate.FindDef(global_var_id); |
| if (variable_instr->words().size() == 5u && |
| hasImportLinkageAttribute(global_var_id, vstate)) { |
| return vstate.diag(SPV_ERROR_INVALID_ID) |
| << "A module-scope OpVariable with initialization value " |
| "cannot be marked with the Import Linkage Type."; |
| } |
| } |
| return SPV_SUCCESS; |
| } |
| |
| // Checks whether proper decorations have been appied to the entry points. |
| spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { |
| for (uint32_t entry_point : vstate.entry_points()) { |
| const auto& interfaces = vstate.entry_point_interfaces(entry_point); |
| int num_builtin_inputs = 0; |
| int num_builtin_outputs = 0; |
| for (auto interface : interfaces) { |
| Instruction* var_instr = vstate.FindDef(interface); |
| if (SpvOpVariable != var_instr->opcode()) { |
| return vstate.diag(SPV_ERROR_INVALID_ID) |
| << "Interfaces passed to OpEntryPoint must be of type " |
| "OpTypeVariable. Found Op" |
| << spvOpcodeString(static_cast<SpvOp>(var_instr->opcode())) |
| << "."; |
| } |
| const uint32_t ptr_id = var_instr->word(1); |
| Instruction* ptr_instr = vstate.FindDef(ptr_id); |
| // It is guaranteed (by validator ID checks) that ptr_instr is |
| // OpTypePointer. Word 3 of this instruction is the type being pointed to. |
| const uint32_t type_id = ptr_instr->word(3); |
| Instruction* type_instr = vstate.FindDef(type_id); |
| const auto storage_class = |
| static_cast<SpvStorageClass>(var_instr->word(3)); |
| if (storage_class != SpvStorageClassInput && |
| storage_class != SpvStorageClassOutput) { |
| return vstate.diag(SPV_ERROR_INVALID_ID) |
| << "OpEntryPoint interfaces must be OpVariables with " |
| "Storage Class of Input(1) or Output(3). Found Storage Class " |
| << storage_class << " for Entry Point id " << entry_point << "."; |
| } |
| if (type_instr && SpvOpTypeStruct == type_instr->opcode() && |
| isBuiltInStruct(type_id, vstate)) { |
| if (storage_class == SpvStorageClassInput) ++num_builtin_inputs; |
| if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs; |
| if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break; |
| } |
| } |
| if (num_builtin_inputs > 1 || num_builtin_outputs > 1) { |
| return vstate.diag(SPV_ERROR_INVALID_BINARY) |
| << "There must be at most one object per Storage Class that can " |
| "contain a structure type containing members decorated with " |
| "BuiltIn, consumed per entry-point. Entry Point id " |
| << entry_point << " does not meet this requirement."; |
| } |
| // The LinkageAttributes Decoration cannot be applied to functions targeted |
| // by an OpEntryPoint instruction |
| for (auto& decoration : vstate.id_decorations(entry_point)) { |
| if (SpvDecorationLinkageAttributes == decoration.dec_type()) { |
| const char* linkage_name = |
| reinterpret_cast<const char*>(&decoration.params()[0]); |
| return vstate.diag(SPV_ERROR_INVALID_BINARY) |
| << "The LinkageAttributes Decoration (Linkage name: " |
| << linkage_name << ") cannot be applied to function id " |
| << entry_point |
| << " because it is targeted by an OpEntryPoint instruction."; |
| } |
| } |
| } |
| return SPV_SUCCESS; |
| } |
| |
| } // anonymous namespace |
| |
| namespace libspirv { |
| |
| // Validates that decorations have been applied properly. |
| spv_result_t ValidateDecorations(ValidationState_t& vstate) { |
| if (auto error = CheckImportedVariableInitialization(vstate)) return error; |
| if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error; |
| if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error; |
| return SPV_SUCCESS; |
| } |
| |
| } // namespace libspirv |