// 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 #include #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 is an optional argument for OpVariable. If initializer // 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(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(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(&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