|  | // Copyright (c) 2015-2016 The Khronos Group Inc. | 
|  | // | 
|  | // Permission is hereby granted, free of charge, to any person obtaining a | 
|  | // copy of this software and/or associated documentation files (the | 
|  | // "Materials"), to deal in the Materials without restriction, including | 
|  | // without limitation the rights to use, copy, modify, merge, publish, | 
|  | // distribute, sublicense, and/or sell copies of the Materials, and to | 
|  | // permit persons to whom the Materials are furnished to do so, subject to | 
|  | // the following conditions: | 
|  | // | 
|  | // The above copyright notice and this permission notice shall be included | 
|  | // in all copies or substantial portions of the Materials. | 
|  | // | 
|  | // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS | 
|  | // KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS | 
|  | // SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT | 
|  | //    https://www.khronos.org/registry/ | 
|  | // | 
|  | // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|  | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
|  | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 
|  | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | 
|  | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | 
|  | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | 
|  | // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. | 
|  |  | 
|  | #ifndef LIBSPIRV_VALIDATE_H_ | 
|  | #define LIBSPIRV_VALIDATE_H_ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <map> | 
|  | #include <string> | 
|  | #include <unordered_map> | 
|  | #include <unordered_set> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "assembly_grammar.h" | 
|  | #include "binary.h" | 
|  | #include "diagnostic.h" | 
|  | #include "instruction.h" | 
|  | #include "spirv-tools/libspirv.h" | 
|  | #include "spirv_definition.h" | 
|  | #include "table.h" | 
|  |  | 
|  | // Structures | 
|  |  | 
|  | // Info about a result ID. | 
|  | typedef struct spv_id_info_t { | 
|  | // Id value. | 
|  | uint32_t id; | 
|  | // Type id, or 0 if no type. | 
|  | uint32_t type_id; | 
|  | // Opcode of the instruction defining the id. | 
|  | SpvOp opcode; | 
|  | // Binary words of the instruction defining the id. | 
|  | std::vector<uint32_t> words; | 
|  | } spv_id_info_t; | 
|  |  | 
|  | namespace libspirv { | 
|  |  | 
|  | // This enum represents the sections of a SPIRV module. See section 2.4 | 
|  | // of the SPIRV spec for additional details of the order. The enumerant values | 
|  | // are in the same order as the vector returned by GetModuleOrder | 
|  | enum ModuleLayoutSection { | 
|  | kLayoutCapabilities,          // < Section 2.4 #1 | 
|  | kLayoutExtensions,            // < Section 2.4 #2 | 
|  | kLayoutExtInstImport,         // < Section 2.4 #3 | 
|  | kLayoutMemoryModel,           // < Section 2.4 #4 | 
|  | kLayoutEntryPoint,            // < Section 2.4 #5 | 
|  | kLayoutExecutionMode,         // < Section 2.4 #6 | 
|  | kLayoutDebug1,                // < Section 2.4 #7 > 1 | 
|  | kLayoutDebug2,                // < Section 2.4 #7 > 2 | 
|  | kLayoutAnnotations,           // < Section 2.4 #8 | 
|  | kLayoutTypes,                 // < Section 2.4 #9 | 
|  | kLayoutFunctionDeclarations,  // < Section 2.4 #10 | 
|  | kLayoutFunctionDefinitions    // < Section 2.4 #11 | 
|  | }; | 
|  |  | 
|  | enum class FunctionDecl { | 
|  | kFunctionDeclUnknown,      // < Unknown function declaration | 
|  | kFunctionDeclDeclaration,  // < Function declaration | 
|  | kFunctionDeclDefinition    // < Function definition | 
|  | }; | 
|  |  | 
|  | class ValidationState_t; | 
|  |  | 
|  | // This class manages all function declaration and definitions in a module. It | 
|  | // handles the state and id information while parsing a function in the SPIR-V | 
|  | // binary. | 
|  | // | 
|  | // NOTE: This class is designed to be a Structure of Arrays. Therefore each | 
|  | // member variable is a vector whose elements represent the values for the | 
|  | // corresponding function in a SPIR-V module. Variables that are not vector | 
|  | // types are used to manage the state while parsing the function. | 
|  | class Functions { | 
|  | public: | 
|  | explicit Functions(ValidationState_t& module); | 
|  |  | 
|  | // Registers the function in the module. Subsequent instructions will be | 
|  | // called against this function | 
|  | spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id, | 
|  | uint32_t function_control, | 
|  | uint32_t function_type_id); | 
|  |  | 
|  | // Registers a function parameter in the current function | 
|  | spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id); | 
|  |  | 
|  | // Register a function end instruction | 
|  | spv_result_t RegisterFunctionEnd(); | 
|  |  | 
|  | // Sets the declaration type of the current function | 
|  | spv_result_t RegisterSetFunctionDeclType(FunctionDecl type); | 
|  |  | 
|  | // Registers a block in the current function. Subsequent block instructions | 
|  | // will target this block | 
|  | // @param id The ID of the label of the block | 
|  | spv_result_t RegisterBlock(uint32_t id); | 
|  |  | 
|  | // Registers a variable in the current block | 
|  | spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id, | 
|  | SpvStorageClass storage, uint32_t init_id); | 
|  |  | 
|  | spv_result_t RegisterBlockLoopMerge(uint32_t merge_id, uint32_t continue_id, | 
|  | SpvLoopControlMask control); | 
|  |  | 
|  | spv_result_t RegisterBlockSelectionMerge(uint32_t merge_id, | 
|  | SpvSelectionControlMask control); | 
|  |  | 
|  | // Registers the end of the block | 
|  | spv_result_t RegisterBlockEnd(); | 
|  |  | 
|  | // Returns the number of blocks in the current function being parsed | 
|  | size_t get_block_count() const; | 
|  |  | 
|  | // Retuns true if called after a function instruction but before the | 
|  | // function end instruction | 
|  | bool in_function_body() const; | 
|  |  | 
|  | // Returns true if called after a label instruction but before a branch | 
|  | // instruction | 
|  | bool in_block() const; | 
|  |  | 
|  | libspirv::DiagnosticStream diag(spv_result_t error_code) const; | 
|  |  | 
|  | private: | 
|  | // Parent module | 
|  | ValidationState_t& module_; | 
|  |  | 
|  | // Funciton IDs in a module | 
|  | std::vector<uint32_t> id_; | 
|  |  | 
|  | // OpTypeFunction IDs of each of the id_ functions | 
|  | std::vector<uint32_t> type_id_; | 
|  |  | 
|  | // The type of declaration of each function | 
|  | std::vector<FunctionDecl> declaration_type_; | 
|  |  | 
|  | // TODO(umar): Probably needs better abstractions | 
|  | // The beginning of the block of functions | 
|  | std::vector<std::vector<uint32_t>> block_ids_; | 
|  |  | 
|  | // The variable IDs of the functions | 
|  | std::vector<std::vector<uint32_t>> variable_ids_; | 
|  |  | 
|  | // The function parameter ids of the functions | 
|  | std::vector<std::vector<uint32_t>> parameter_ids_; | 
|  |  | 
|  | // NOTE: See correspoding getter functions | 
|  | bool in_function_; | 
|  | bool in_block_; | 
|  | }; | 
|  |  | 
|  | class ValidationState_t { | 
|  | public: | 
|  | ValidationState_t(spv_diagnostic* diagnostic, | 
|  | const spv_const_context context); | 
|  |  | 
|  | // Forward declares the id in the module | 
|  | spv_result_t forwardDeclareId(uint32_t id); | 
|  |  | 
|  | // Removes a forward declared ID if it has been defined | 
|  | spv_result_t removeIfForwardDeclared(uint32_t id); | 
|  |  | 
|  | // Assigns a name to an ID | 
|  | void assignNameToId(uint32_t id, std::string name); | 
|  |  | 
|  | // Returns a string representation of the ID in the format <id>[Name] where | 
|  | // the <id> is the numeric valid of the id and the Name is a name assigned by | 
|  | // the OpName instruction | 
|  | std::string getIdName(uint32_t id) const; | 
|  |  | 
|  | // Returns the number of ID which have been forward referenced but not defined | 
|  | size_t unresolvedForwardIdCount() const; | 
|  |  | 
|  | // Returns a list of unresolved forward ids. | 
|  | std::vector<uint32_t> unresolvedForwardIds() const; | 
|  |  | 
|  | // Returns true if the id has been defined | 
|  | bool isDefinedId(uint32_t id) const; | 
|  |  | 
|  | // Increments the instruction count. Used for diagnostic | 
|  | int incrementInstructionCount(); | 
|  |  | 
|  | // Returns the current layout section which is being processed | 
|  | ModuleLayoutSection getLayoutSection() const; | 
|  |  | 
|  | // Increments the module_layout_order_section_ | 
|  | void progressToNextLayoutSectionOrder(); | 
|  |  | 
|  | // Determines if the op instruction is part of the current section | 
|  | bool isOpcodeInCurrentLayoutSection(SpvOp op); | 
|  |  | 
|  | libspirv::DiagnosticStream diag(spv_result_t error_code) const; | 
|  |  | 
|  | // Returns the function states | 
|  | Functions& get_functions(); | 
|  |  | 
|  | // Retuns true if the called after a function instruction but before the | 
|  | // function end instruction | 
|  | bool in_function_body() const; | 
|  |  | 
|  | // Returns true if called after a label instruction but before a branch | 
|  | // instruction | 
|  | bool in_block() const; | 
|  |  | 
|  | // Keeps track of ID definitions and uses. | 
|  | class UseDefTracker { | 
|  | public: | 
|  | void AddDef(const spv_id_info_t& def) { defs_[def.id] = def; } | 
|  |  | 
|  | void AddUse(uint32_t id) { uses_.insert(id); } | 
|  |  | 
|  | // Finds id's def, if it exists.  If found, returns <true, def>.  Otherwise, | 
|  | // returns <false, something>. | 
|  | std::pair<bool, spv_id_info_t> FindDef(uint32_t id) const { | 
|  | if (defs_.count(id) == 0) { | 
|  | return std::make_pair(false, spv_id_info_t{}); | 
|  | } else { | 
|  | // We are in a const function, so we cannot use defs.operator[](). | 
|  | // Luckily we know the key exists, so defs_.at() won't throw an | 
|  | // exception. | 
|  | return std::make_pair(true, defs_.at(id)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Returns uses of IDs lacking defs. | 
|  | std::unordered_set<uint32_t> FindUsesWithoutDefs() const { | 
|  | auto diff = uses_; | 
|  | for (const auto d : defs_) diff.erase(d.first); | 
|  | return diff; | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unordered_set<uint32_t> uses_; | 
|  | std::unordered_map<uint32_t, spv_id_info_t> defs_; | 
|  | }; | 
|  |  | 
|  | UseDefTracker& usedefs() { return usedefs_; } | 
|  | const UseDefTracker& usedefs() const { return usedefs_; } | 
|  |  | 
|  | std::vector<uint32_t>& entry_points() { return entry_points_; } | 
|  | const std::vector<uint32_t>& entry_points() const { return entry_points_; } | 
|  |  | 
|  | // Registers the capability and its dependent capabilities | 
|  | void registerCapability(SpvCapability cap); | 
|  |  | 
|  | // Returns true if the capability is enabled in the module. | 
|  | bool hasCapability(SpvCapability cap) const; | 
|  |  | 
|  | // Returns true if any of the capabilities are enabled.  Always true for | 
|  | // capabilities==0. | 
|  | bool HasAnyOf(spv_capability_mask_t capabilities) const; | 
|  |  | 
|  | AssemblyGrammar& grammar() { return grammar_; } | 
|  |  | 
|  | private: | 
|  | spv_diagnostic* diagnostic_; | 
|  | // Tracks the number of instructions evaluated by the validator | 
|  | int instruction_counter_; | 
|  |  | 
|  | // IDs which have been forward declared but have not been defined | 
|  | std::unordered_set<uint32_t> unresolved_forward_ids_; | 
|  |  | 
|  | std::map<uint32_t, std::string> operand_names_; | 
|  |  | 
|  | // The section of the code being processed | 
|  | ModuleLayoutSection current_layout_section_; | 
|  |  | 
|  | Functions module_functions_; | 
|  |  | 
|  | // We are using vector to map the ID of the capability to its availability. | 
|  | // The size of the vector needs to be the maximum ID plus one to cover the | 
|  | // entire range of the capability. | 
|  | std::vector<bool> module_capabilities_; | 
|  |  | 
|  | // Definitions and uses of all the IDs in the module. | 
|  | UseDefTracker usedefs_; | 
|  |  | 
|  | // IDs that are entry points, ie, arguments to OpEntryPoint. | 
|  | std::vector<uint32_t> entry_points_; | 
|  |  | 
|  | AssemblyGrammar grammar_; | 
|  | }; | 
|  |  | 
|  | }  // namespace libspirv | 
|  |  | 
|  | // Functions | 
|  |  | 
|  | /// @brief Validate the ID usage of the instruction stream | 
|  | /// | 
|  | /// @param[in] pInsts stream of instructions | 
|  | /// @param[in] instCount number of instructions | 
|  | /// @param[in] opcodeTable table of specified Opcodes | 
|  | /// @param[in] operandTable table of specified operands | 
|  | /// @param[in] usedefs use-def info from module parsing | 
|  | /// @param[in,out] position current position in the stream | 
|  | /// @param[out] pDiag contains diagnostic on failure | 
|  | /// | 
|  | /// @return result code | 
|  | spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts, | 
|  | const uint64_t instCount, | 
|  | const spv_opcode_table opcodeTable, | 
|  | const spv_operand_table operandTable, | 
|  | const spv_ext_inst_table extInstTable, | 
|  | const libspirv::ValidationState_t& state, | 
|  | spv_position position, | 
|  | spv_diagnostic* pDiag); | 
|  |  | 
|  | /// @brief Validate the ID's within a SPIR-V binary | 
|  | /// | 
|  | /// @param[in] pInstructions array of instructions | 
|  | /// @param[in] count number of elements in instruction array | 
|  | /// @param[in] bound the binary header | 
|  | /// @param[in] opcodeTable table of specified Opcodes | 
|  | /// @param[in] operandTable table of specified operands | 
|  | /// @param[in,out] position current word in the binary | 
|  | /// @param[out] pDiagnostic contains diagnostic on failure | 
|  | /// | 
|  | /// @return result code | 
|  | spv_result_t spvValidateIDs(const spv_instruction_t* pInstructions, | 
|  | const uint64_t count, const uint32_t bound, | 
|  | const spv_opcode_table opcodeTable, | 
|  | const spv_operand_table operandTable, | 
|  | const spv_ext_inst_table extInstTable, | 
|  | spv_position position, spv_diagnostic* pDiagnostic); | 
|  |  | 
|  | #define spvCheckReturn(expression) \ | 
|  | if (spv_result_t error = (expression)) return error; | 
|  |  | 
|  | #endif  // LIBSPIRV_VALIDATE_H_ |