From 90a4252aae7f912345918b48e8b90c03e4c945d4 Mon Sep 17 00:00:00 2001 From: Umar Arshad Date: Thu, 2 Jun 2016 18:51:05 -0400 Subject: [PATCH] Split validate_types file into multiple classes Creates separate files for the ValidationState, Function and BasicBlock classes. --- CMakeLists.txt | 2 + source/CMakeLists.txt | 6 +- source/diagnostic.cpp | 4 + source/diagnostic.h | 23 +- source/val/BasicBlock.cpp | 107 ++++ source/val/BasicBlock.h | 157 ++++++ source/{validate_passes.h => val/Construct.cpp} | 37 +- source/val/Construct.h | 58 +++ source/val/Function.cpp | 232 +++++++++ source/val/Function.h | 195 ++++++++ .../ValidationState.cpp} | 304 +----------- source/val/ValidationState.h | 248 ++++++++++ source/validate.cpp | 9 +- source/validate.h | 545 ++------------------- source/validate_cfg.cpp | 11 +- source/validate_id.cpp | 5 +- source/validate_instruction.cpp | 6 +- source/validate_layout.cpp | 30 +- source/validate_ssa.cpp | 5 +- test/UnitSPIRV.h | 18 +- test/ValidationState.cpp | 9 +- 21 files changed, 1138 insertions(+), 873 deletions(-) create mode 100644 source/val/BasicBlock.cpp create mode 100644 source/val/BasicBlock.h rename source/{validate_passes.h => val/Construct.cpp} (62%) create mode 100644 source/val/Construct.h create mode 100644 source/val/Function.cpp create mode 100644 source/val/Function.h rename source/{validate_types.cpp => val/ValidationState.cpp} (54%) create mode 100644 source/val/ValidationState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ba61667..bdb9f3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ elseif(MSVC) endif() endif() +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/source) + option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON) if(${SPIRV_COLOR_TERMINAL}) add_definitions(-DSPIRV_COLOR_TERMINAL) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index c598846..ca06f28 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -167,7 +167,10 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_ssa.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/validate_types.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/val/BasicBlock.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/Construct.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/Function.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/ValidationState.cpp) # The software_version.cpp file includes build-version.inc. # Rebuild the software_version.cpp object file if it is older than @@ -184,7 +187,6 @@ set_source_files_properties( ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp PROPERTIES OBJECT_DEPENDS "${SPIRV_TOOLS_BUILD_VERSION_INC}") - add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES}) spvtools_default_compile_options(${SPIRV_TOOLS}) target_include_directories(${SPIRV_TOOLS} diff --git a/source/diagnostic.cpp b/source/diagnostic.cpp index 9da7e38..61dd325 100644 --- a/source/diagnostic.cpp +++ b/source/diagnostic.cpp @@ -148,4 +148,8 @@ spvResultToString(spv_result_t res) { return out; } +void message(std::string file, size_t line, std::string name) { + std::cout << file << ":" << line << ": " << name << std::endl; +} + } // namespace libspirv diff --git a/source/diagnostic.h b/source/diagnostic.h index 38a35ea..8dd079b 100644 --- a/source/diagnostic.h +++ b/source/diagnostic.h @@ -33,8 +33,21 @@ #include "spirv-tools/libspirv.h" -namespace libspirv { +/// For debugging purposes only +/// Prints the string to stdout +#define MSG(msg) \ + do { \ + libspirv::message(__FILE__, size_t(__LINE__), msg); \ + } while (0) + +/// For debugging purposes only +/// prints the variable value/location/and name to stdout +#define SHOW(exp) \ + do { \ + libspirv::message(__FILE__, size_t(__LINE__), #exp, (exp)); \ + } while (0) +namespace libspirv { class diagnostic_helper { public: diagnostic_helper(spv_position_t& position, spv_diagnostic* diagnostic) @@ -104,6 +117,14 @@ class DiagnosticStream { std::string spvResultToString(spv_result_t res); +/// Helper functions for printing debugging information +void message(std::string file, size_t line, std::string name); + +template +void message(std::string file, size_t line, std::string name, T val) { + std::cout << file << ":" << line << ": " << name << " " << val << std::endl; +} + } // namespace libspirv #endif // LIBSPIRV_DIAGNOSTIC_H_ diff --git a/source/val/BasicBlock.cpp b/source/val/BasicBlock.cpp new file mode 100644 index 0000000..55be3b8 --- /dev/null +++ b/source/val/BasicBlock.cpp @@ -0,0 +1,107 @@ +// 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. + +#include "BasicBlock.h" + +#include + +using std::vector; + +namespace libspirv { + +BasicBlock::BasicBlock(uint32_t id) + : id_(id), + immediate_dominator_(nullptr), + predecessors_(), + successors_(), + reachable_(false) {} + +void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) { + immediate_dominator_ = dom_block; +} + +const BasicBlock* BasicBlock::GetImmediateDominator() const { + return immediate_dominator_; +} + +BasicBlock* BasicBlock::GetImmediateDominator() { return immediate_dominator_; } + +void BasicBlock::RegisterSuccessors(vector next_blocks) { + for (auto& block : next_blocks) { + block->predecessors_.push_back(this); + successors_.push_back(block); + if (block->reachable_ == false) block->set_reachability(reachable_); + } +} + +void BasicBlock::RegisterBranchInstruction(SpvOp branch_instruction) { + if (branch_instruction == SpvOpUnreachable) reachable_ = false; + return; +} + +BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {} +BasicBlock::DominatorIterator::DominatorIterator(const BasicBlock* block) + : current_(block) {} + +BasicBlock::DominatorIterator& BasicBlock::DominatorIterator::operator++() { + if (current_ == current_->GetImmediateDominator()) { + current_ = nullptr; + } else { + current_ = current_->GetImmediateDominator(); + } + return *this; +} + +const BasicBlock::DominatorIterator BasicBlock::dom_begin() const { + return DominatorIterator(this); +} + +BasicBlock::DominatorIterator BasicBlock::dom_begin() { + return DominatorIterator(this); +} + +const BasicBlock::DominatorIterator BasicBlock::dom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::dom_end() { + return DominatorIterator(); +} + +bool operator==(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs) { + return lhs.current_ == rhs.current_; +} + +bool operator!=(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs) { + return !(lhs == rhs); +} + +const BasicBlock*& BasicBlock::DominatorIterator::operator*() { + return current_; +} +} // namespace libspirv diff --git a/source/val/BasicBlock.h b/source/val/BasicBlock.h new file mode 100644 index 0000000..0612db1 --- /dev/null +++ b/source/val/BasicBlock.h @@ -0,0 +1,157 @@ +// 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_VAL_BASICBLOCK_H_ +#define LIBSPIRV_VAL_BASICBLOCK_H_ + +#include "spirv/spirv.h" + +#include +#include + +namespace libspirv { + +// This class represents a basic block in a SPIR-V module +class BasicBlock { + public: + /// Constructor for a BasicBlock + /// + /// @param[in] id The ID of the basic block + explicit BasicBlock(uint32_t id); + + /// Returns the id of the BasicBlock + uint32_t get_id() const { return id_; } + + /// Returns the predecessors of the BasicBlock + const std::vector& get_predecessors() const { + return predecessors_; + } + + /// Returns the predecessors of the BasicBlock + std::vector& get_predecessors() { return predecessors_; } + + /// Returns the successors of the BasicBlock + const std::vector& get_successors() const { return successors_; } + + /// Returns the successors of the BasicBlock + std::vector& get_successors() { return successors_; } + + /// Returns true if the block should be reachable in the CFG + bool is_reachable() const { return reachable_; } + + void set_reachability(bool reachability) { reachable_ = reachability; } + + /// Sets the immedate dominator of this basic block + /// + /// @param[in] dom_block The dominator block + void SetImmediateDominator(BasicBlock* dom_block); + + /// Returns the immedate dominator of this basic block + BasicBlock* GetImmediateDominator(); + + /// Returns the immedate dominator of this basic block + const BasicBlock* GetImmediateDominator() const; + + /// Ends the block without a successor + void RegisterBranchInstruction(SpvOp branch_instruction); + + /// Adds @p next BasicBlocks as successors of this BasicBlock + void RegisterSuccessors(std::vector next = {}); + + /// Returns true if the id of the BasicBlock matches + bool operator==(const BasicBlock& other) const { return other.id_ == id_; } + + /// Returns true if the id of the BasicBlock matches + bool operator==(const uint32_t& id) const { return id == id_; } + + /// @brief A BasicBlock dominator iterator class + /// + /// This iterator will iterate over the dominators of the block + class DominatorIterator + : public std::iterator { + public: + /// @brief Constructs the end of dominator iterator + /// + /// This will create an iterator which will represent the element + /// before the root node of the dominator tree + DominatorIterator(); + + /// @brief Constructs an iterator for the given block which points to + /// @p block + /// + /// @param block The block which is referenced by the iterator + explicit DominatorIterator(const BasicBlock* block); + + /// @brief Advances the iterator + DominatorIterator& operator++(); + + /// @brief Returns the current element + const BasicBlock*& operator*(); + + friend bool operator==(const DominatorIterator& lhs, + const DominatorIterator& rhs); + + private: + const BasicBlock* current_; + }; + + /// Returns an iterator which points to the current block + const DominatorIterator dom_begin() const; + DominatorIterator dom_begin(); + + /// Returns an iterator which points to one element past the first block + const DominatorIterator dom_end() const; + DominatorIterator dom_end(); + + private: + /// Id of the BasicBlock + const uint32_t id_; + + /// Pointer to the immediate dominator of the BasicBlock + BasicBlock* immediate_dominator_; + + /// The set of predecessors of the BasicBlock + std::vector predecessors_; + + /// The set of successors of the BasicBlock + std::vector successors_; + + bool reachable_; +}; + +/// @brief Returns true if the iterators point to the same element or if both +/// iterators point to the @p dom_end block +bool operator==(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs); + +/// @brief Returns true if the iterators point to different elements and they +/// do not both point to the @p dom_end block +bool operator!=(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs); + +} /// namespace libspirv + +#endif /// LIBSPIRV_VAL_BASICBLOCK_H_ diff --git a/source/validate_passes.h b/source/val/Construct.cpp similarity index 62% rename from source/validate_passes.h rename to source/val/Construct.cpp index de1d44a..91140bf 100644 --- a/source/validate_passes.h +++ b/source/val/Construct.cpp @@ -24,32 +24,21 @@ // 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_PASSES_H_ -#define LIBSPIRV_VALIDATE_PASSES_H_ +#include "val/Construct.h" -#include "binary.h" -#include "validate.h" +namespace libspirv { -namespace libspirv -{ -// TODO(umar): Better docs +Construct::Construct(BasicBlock* header_block, BasicBlock* merge_block, + BasicBlock* continue_block) + : header_block_(header_block), + merge_block_(merge_block), + continue_block_(continue_block) {} -// Performs logical layout validation as described in section 2.4 of the SPIR-V spec -spv_result_t ModuleLayoutPass(ValidationState_t& _, - const spv_parsed_instruction_t* inst); - -// Performs Control Flow Graph validation of a module -spv_result_t CfgPass(ValidationState_t& _, - const spv_parsed_instruction_t* inst); - -// Performs SSA validation of a module -spv_result_t SsaPass(ValidationState_t& _, - const spv_parsed_instruction_t* inst); - -// Performs instruction validation. -spv_result_t InstructionPass(ValidationState_t& _, - const spv_parsed_instruction_t* inst); +const BasicBlock* Construct::get_header() const { return header_block_; } +const BasicBlock* Construct::get_merge() const { return merge_block_; } +const BasicBlock* Construct::get_continue() const { return continue_block_; } +BasicBlock* Construct::get_header() { return header_block_; } +BasicBlock* Construct::get_merge() { return merge_block_; } +BasicBlock* Construct::get_continue() { return continue_block_; } } - -#endif diff --git a/source/val/Construct.h b/source/val/Construct.h new file mode 100644 index 0000000..ef5fae4 --- /dev/null +++ b/source/val/Construct.h @@ -0,0 +1,58 @@ +// 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_VAL_CONSTRUCT_H_ +#define LIBSPIRV_VAL_CONSTRUCT_H_ + +#include + +namespace libspirv { + +class BasicBlock; + +/// @brief This class tracks the CFG constructs as defined in the SPIR-V spec +class Construct { + public: + Construct(BasicBlock* header_block, BasicBlock* merge_block, + BasicBlock* continue_block = nullptr); + + const BasicBlock* get_header() const; + const BasicBlock* get_merge() const; + const BasicBlock* get_continue() const; + + BasicBlock* get_header(); + BasicBlock* get_merge(); + BasicBlock* get_continue(); + + private: + BasicBlock* header_block_; ///< The header block of a loop or selection + BasicBlock* merge_block_; ///< The merge block of a loop or selection + BasicBlock* continue_block_; ///< The continue block of a loop block +}; + +} /// namespace libspirv + +#endif /// LIBSPIRV_VAL_CONSTRUCT_H_ diff --git a/source/val/Function.cpp b/source/val/Function.cpp new file mode 100644 index 0000000..dd8b386 --- /dev/null +++ b/source/val/Function.cpp @@ -0,0 +1,232 @@ +// 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. + +#include "val/Function.h" + +#include + +#include + +#include "val/BasicBlock.h" +#include "val/Construct.h" +#include "val/ValidationState.h" + +using std::list; +using std::string; +using std::vector; + +namespace libspirv { +namespace { + +void printDot(const BasicBlock& other, const ValidationState_t& module) { + string block_string; + if (other.get_successors().empty()) { + block_string += "end "; + } else { + for (auto& block : other.get_successors()) { + block_string += module.getIdOrName(block->get_id()) + " "; + } + } + printf("%10s -> {%s\b}\n", module.getIdOrName(other.get_id()).c_str(), + block_string.c_str()); +} +} /// namespace + +Function::Function(uint32_t id, uint32_t result_type_id, + SpvFunctionControlMask function_control, + uint32_t function_type_id, ValidationState_t& module) + : module_(module), + id_(id), + function_type_id_(function_type_id), + result_type_id_(result_type_id), + function_control_(function_control), + declaration_type_(FunctionDecl::kFunctionDeclUnknown), + blocks_(), + current_block_(nullptr), + cfg_constructs_(), + variable_ids_(), + parameter_ids_() {} + +bool Function::IsFirstBlock(uint32_t id) const { + return !ordered_blocks_.empty() && *get_first_block() == id; +} + +spv_result_t Function::RegisterFunctionParameter(uint32_t id, + uint32_t type_id) { + assert(module_.in_function_body() == true && + "RegisterFunctionParameter can only be called when parsing the binary " + "outside of another function"); + assert(current_block_ == nullptr && + "RegisterFunctionParameter can only be called when parsing the binary " + "ouside of a block"); + // TODO(umar): Validate function parameter type order and count + // TODO(umar): Use these variables to validate parameter type + (void)id; + (void)type_id; + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterLoopMerge(uint32_t merge_id, + uint32_t continue_id) { + RegisterBlock(merge_id, false); + RegisterBlock(continue_id, false); + cfg_constructs_.emplace_back(get_current_block(), &blocks_.at(merge_id), + &blocks_.at(continue_id)); + + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { + RegisterBlock(merge_id, false); + cfg_constructs_.emplace_back(get_current_block(), &blocks_.at(merge_id)); + return SPV_SUCCESS; +} + +void Function::printDotGraph() const { + if (get_first_block()) { + string func_name(module_.getIdOrName(id_)); + printf("digraph %s {\n", func_name.c_str()); + printBlocks(); + printf("}\n"); + } +} + +void Function::printBlocks() const { + if (get_first_block()) { + printf("%10s -> %s\n", module_.getIdOrName(id_).c_str(), + module_.getIdOrName(get_first_block()->get_id()).c_str()); + for (const auto& block : blocks_) { + printDot(block.second, module_); + } + } +} + +spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) { + assert(declaration_type_ == FunctionDecl::kFunctionDeclUnknown); + declaration_type_ = type; + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterBlock(uint32_t id, bool is_definition) { + assert(module_.in_function_body() == true && + "RegisterBlocks can only be called when parsing a binary inside of a " + "function"); + assert(module_.getLayoutSection() != + ModuleLayoutSection::kLayoutFunctionDeclarations && + "RegisterBlocks cannot be called within a function declaration"); + assert( + declaration_type_ == FunctionDecl::kFunctionDeclDefinition && + "RegisterBlocks can only be called after declaration_type_ is defined"); + + std::unordered_map::iterator inserted_block; + bool success = false; + tie(inserted_block, success) = blocks_.insert({id, BasicBlock(id)}); + if (is_definition) { // new block definition + assert(current_block_ == nullptr && + "Register Block can only be called when parsing a binary outside of " + "a BasicBlock"); + + undefined_blocks_.erase(id); + current_block_ = &inserted_block->second; + ordered_blocks_.push_back(current_block_); + if (IsFirstBlock(id)) current_block_->set_reachability(true); + } else if (success) { // Block doesn't exsist but this is not a definition + undefined_blocks_.insert(id); + } + + return SPV_SUCCESS; +} + +void Function::RegisterBlockEnd(vector next_list, + SpvOp branch_instruction) { + assert(module_.in_function_body() == true && + "RegisterBlockEnd can only be called when parsing a binary in a " + "function"); + assert( + current_block_ && + "RegisterBlockEnd can only be called when parsing a binary in a block"); + + vector next_blocks; + next_blocks.reserve(next_list.size()); + + std::unordered_map::iterator inserted_block; + bool success; + for (uint32_t id : next_list) { + tie(inserted_block, success) = blocks_.insert({id, BasicBlock(id)}); + if (success) { + undefined_blocks_.insert(id); + } + next_blocks.push_back(&inserted_block->second); + } + + current_block_->RegisterBranchInstruction(branch_instruction); + current_block_->RegisterSuccessors(next_blocks); + current_block_ = nullptr; + return; +} + +size_t Function::get_block_count() const { return blocks_.size(); } + +size_t Function::get_undefined_block_count() const { + return undefined_blocks_.size(); +} + +const vector& Function::get_blocks() const { + return ordered_blocks_; +} +vector& Function::get_blocks() { return ordered_blocks_; } + +const BasicBlock* Function::get_current_block() const { return current_block_; } +BasicBlock* Function::get_current_block() { return current_block_; } + +const list& Function::get_constructs() const { + return cfg_constructs_; +} +list& Function::get_constructs() { return cfg_constructs_; } + +const BasicBlock* Function::get_first_block() const { + if (ordered_blocks_.empty()) return nullptr; + return ordered_blocks_[0]; +} +BasicBlock* Function::get_first_block() { + if (ordered_blocks_.empty()) return nullptr; + return ordered_blocks_[0]; +} + +bool Function::IsMergeBlock(uint32_t merge_block_id) const { + const auto b = blocks_.find(merge_block_id); + if (b != end(blocks_)) { + return cfg_constructs_.end() != + find_if(begin(cfg_constructs_), end(cfg_constructs_), + [&](const Construct& construct) { + return construct.get_merge() == &b->second; + }); + } else { + return false; + } +} + +} /// namespace libspirv diff --git a/source/val/Function.h b/source/val/Function.h new file mode 100644 index 0000000..a6a4304 --- /dev/null +++ b/source/val/Function.h @@ -0,0 +1,195 @@ +// 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_VAL_FUNCTION_H_ +#define LIBSPIRV_VAL_FUNCTION_H_ + +#include +#include +#include +#include + +#include "spirv/spirv.h" +#include "spirv-tools/libspirv.h" +#include "val/BasicBlock.h" + +namespace libspirv { + +enum class FunctionDecl { + kFunctionDeclUnknown, /// < Unknown function declaration + kFunctionDeclDeclaration, /// < Function declaration + kFunctionDeclDefinition /// < Function definition +}; + +class Construct; +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. +class Function { + public: + Function(uint32_t id, uint32_t result_type_id, + SpvFunctionControlMask function_control, uint32_t function_type_id, + ValidationState_t& module); + + /// Registers a function parameter in the current function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id); + + /// Sets the declaration type of the current function + /// @return Returns SPV_SUCCESS if the call was successful + 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 + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterBlock(uint32_t id, bool is_definition = true); + + /// Registers a variable in the current block + /// + /// @param[in] type_id The type ID of the varaible + /// @param[in] id The ID of the varaible + /// @param[in] storage The storage of the variable + /// @param[in] init_id The initializer ID of the variable + /// + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id, + SpvStorageClass storage, uint32_t init_id); + + /// Registers a loop merge construct in the function + /// + /// @param[in] merge_id The merge block ID of the loop + /// @param[in] continue_id The continue block ID of the loop + /// + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterLoopMerge(uint32_t merge_id, uint32_t continue_id); + + /// Registers a selection merge construct in the function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterSelectionMerge(uint32_t merge_id); + + /// Registers the end of the block + /// + /// @param[in] successors_list A list of ids to the blocks successors + /// @param[in] branch_instruction the branch instruction that ended the block + void RegisterBlockEnd(std::vector successors_list, + SpvOp branch_instruction); + + /// Returns true if the \p merge_block_id is a merge block + bool IsMergeBlock(uint32_t merge_block_id) const; + + /// Returns true if the \p id is the first block of this function + bool IsFirstBlock(uint32_t id) const; + + /// Returns the first block of the current function + const BasicBlock* get_first_block() const; + + /// Returns the first block of the current function + BasicBlock* get_first_block(); + + /// Returns a vector of all the blocks in the function + const std::vector& get_blocks() const; + + /// Returns a vector of all the blocks in the function + std::vector& get_blocks(); + + /// Returns a list of all the cfg constructs in the function + const std::list& get_constructs() const; + + /// Returns a list of all the cfg constructs in the function + std::list& get_constructs(); + + /// Returns the number of blocks in the current function being parsed + size_t get_block_count() const; + + /// Returns the id of the funciton + uint32_t get_id() const { return id_; } + + /// Returns the number of blocks in the current function being parsed + size_t get_undefined_block_count() const; + const std::unordered_set& get_undefined_blocks() const { + return undefined_blocks_; + } + + /// Returns the block that is currently being parsed in the binary + BasicBlock* get_current_block(); + + /// Returns the block that is currently being parsed in the binary + const BasicBlock* get_current_block() const; + + /// Prints a GraphViz digraph of the CFG of the current funciton + void printDotGraph() const; + + /// Prints a directed graph of the CFG of the current funciton + void printBlocks() const; + + private: + /// Parent module + ValidationState_t& module_; + + /// The result id of the OpLabel that defined this block + uint32_t id_; + + /// The type of the function + uint32_t function_type_id_; + + /// The type of the return value + uint32_t result_type_id_; + + /// The control fo the funciton + SpvFunctionControlMask function_control_; + + /// The type of declaration of each function + FunctionDecl declaration_type_; + + /// The blocks in the function mapped by block ID + std::unordered_map blocks_; + + /// A list of blocks in the order they appeared in the binary + std::vector ordered_blocks_; + + /// Blocks which are forward referenced by blocks but not defined + std::unordered_set undefined_blocks_; + + /// The block that is currently being parsed + BasicBlock* current_block_; + + /// The constructs that are available in this function + std::list cfg_constructs_; + + /// The variable IDs of the functions + std::vector variable_ids_; + + /// The function parameter ids of the functions + std::vector parameter_ids_; +}; + +} /// namespace libspirv + + +#endif /// LIBSPIRV_VAL_FUNCTION_H_ diff --git a/source/validate_types.cpp b/source/val/ValidationState.cpp similarity index 54% rename from source/validate_types.cpp rename to source/val/ValidationState.cpp index bba979d..f937e2f 100644 --- a/source/validate_types.cpp +++ b/source/val/ValidationState.cpp @@ -24,40 +24,19 @@ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -#include +#include "val/ValidationState.h" + #include -#include -#include -#include -#include -#include -#include -#include - -#include "spirv/spirv.h" -#include "spirv_definition.h" -#include "validate.h" - -using std::find; + +#include "val/BasicBlock.h" +#include "val/Construct.h" +#include "val/Function.h" + using std::list; -using std::numeric_limits; using std::string; -using std::unordered_set; using std::vector; -using libspirv::kLayoutCapabilities; -using libspirv::kLayoutExtensions; -using libspirv::kLayoutExtInstImport; -using libspirv::kLayoutMemoryModel; -using libspirv::kLayoutEntryPoint; -using libspirv::kLayoutExecutionMode; -using libspirv::kLayoutDebug1; -using libspirv::kLayoutDebug2; -using libspirv::kLayoutAnnotations; -using libspirv::kLayoutTypes; -using libspirv::kLayoutFunctionDeclarations; -using libspirv::kLayoutFunctionDefinitions; -using libspirv::ModuleLayoutSection; +namespace libspirv { namespace { bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) { @@ -211,12 +190,6 @@ bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) { } // anonymous namespace -namespace libspirv { - -void message(std::string file, size_t line, std::string name) { - std::cout << file << ":" << line << ": " << name << std::endl; -} - ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic, const spv_const_context context) : diagnostic_(diagnostic), @@ -357,25 +330,6 @@ SpvMemoryModel ValidationState_t::getMemoryModel() const { return memory_model_; } -Function::Function(uint32_t id, uint32_t result_type_id, - SpvFunctionControlMask function_control, - uint32_t function_type_id, ValidationState_t& module) - : module_(module), - id_(id), - function_type_id_(function_type_id), - result_type_id_(result_type_id), - function_control_(function_control), - declaration_type_(FunctionDecl::kFunctionDeclUnknown), - blocks_(), - current_block_(nullptr), - cfg_constructs_(), - variable_ids_(), - parameter_ids_() {} - -bool Function::IsFirstBlock(uint32_t id) const { - return !ordered_blocks_.empty() && *get_first_block() == id; -} - spv_result_t ValidationState_t::RegisterFunction( uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control, uint32_t function_type_id) { @@ -402,244 +356,4 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() { return SPV_SUCCESS; } -spv_result_t Function::RegisterFunctionParameter(uint32_t id, - uint32_t type_id) { - assert(module_.in_function_body() == true && - "RegisterFunctionParameter can only be called when parsing the binary " - "outside of another function"); - assert(get_current_block() == nullptr && - "RegisterFunctionParameter can only be called when parsing the binary " - "ouside of a block"); - // TODO(umar): Validate function parameter type order and count - // TODO(umar): Use these variables to validate parameter type - (void)id; - (void)type_id; - return SPV_SUCCESS; -} - -spv_result_t Function::RegisterLoopMerge(uint32_t merge_id, - uint32_t continue_id) { - RegisterBlock(merge_id, false); - RegisterBlock(continue_id, false); - cfg_constructs_.emplace_back(get_current_block(), &blocks_.at(merge_id), - &blocks_.at(continue_id)); - - return SPV_SUCCESS; -} - -spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { - RegisterBlock(merge_id, false); - cfg_constructs_.emplace_back(get_current_block(), &blocks_.at(merge_id)); - return SPV_SUCCESS; -} - -void printDot(const BasicBlock& other, const ValidationState_t& module) { - string block_string; - if (other.get_successors().empty()) { - block_string += "end "; - } else { - for (auto& block : other.get_successors()) { - block_string += module.getIdOrName(block->get_id()) + " "; - } - } - printf("%10s -> {%s\b}\n", module.getIdOrName(other.get_id()).c_str(), - block_string.c_str()); -} - -void Function::printDotGraph() const { - if (get_first_block()) { - string func_name(module_.getIdOrName(id_)); - printf("digraph %s {\n", func_name.c_str()); - printBlocks(); - printf("}\n"); - } -} - -void Function::printBlocks() const { - if (get_first_block()) { - printf("%10s -> %s\n", module_.getIdOrName(id_).c_str(), - module_.getIdOrName(get_first_block()->get_id()).c_str()); - for (const auto& block : blocks_) { - printDot(block.second, module_); - } - } -} - -spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) { - assert(declaration_type_ == FunctionDecl::kFunctionDeclUnknown); - declaration_type_ = type; - return SPV_SUCCESS; -} - -spv_result_t Function::RegisterBlock(uint32_t id, bool is_definition) { - assert(module_.in_function_body() == true && - "RegisterBlocks can only be called when parsing a binary inside of a " - "function"); - assert(module_.getLayoutSection() != - ModuleLayoutSection::kLayoutFunctionDeclarations && - "RegisterBlocks cannot be called within a function declaration"); - assert( - declaration_type_ == FunctionDecl::kFunctionDeclDefinition && - "RegisterBlocks can only be called after declaration_type_ is defined"); - - std::unordered_map::iterator inserted_block; - bool success = false; - tie(inserted_block, success) = blocks_.insert({id, BasicBlock(id)}); - if (is_definition) { // new block definition - assert(get_current_block() == nullptr && - "Register Block can only be called when parsing a binary outside of " - "a BasicBlock"); - - undefined_blocks_.erase(id); - current_block_ = &inserted_block->second; - ordered_blocks_.push_back(current_block_); - if (IsFirstBlock(id)) current_block_->set_reachability(true); - } else if (success) { // Block doesn't exsist but this is not a definition - undefined_blocks_.insert(id); - } - - return SPV_SUCCESS; -} - -void Function::RegisterBlockEnd(vector next_list, - SpvOp branch_instruction) { - assert(module_.in_function_body() == true && - "RegisterBlockEnd can only be called when parsing a binary in a " - "function"); - assert( - get_current_block() && - "RegisterBlockEnd can only be called when parsing a binary in a block"); - - vector next_blocks; - next_blocks.reserve(next_list.size()); - - std::unordered_map::iterator inserted_block; - bool success; - for (uint32_t id : next_list) { - tie(inserted_block, success) = blocks_.insert({id, BasicBlock(id)}); - if (success) { - undefined_blocks_.insert(id); - } - next_blocks.push_back(&inserted_block->second); - } - - current_block_->RegisterBranchInstruction(branch_instruction); - current_block_->RegisterSuccessors(next_blocks); - current_block_ = nullptr; - return; -} - -size_t Function::get_block_count() const { return blocks_.size(); } - -size_t Function::get_undefined_block_count() const { - return undefined_blocks_.size(); -} - -const vector& Function::get_blocks() const { - return ordered_blocks_; -} -vector& Function::get_blocks() { return ordered_blocks_; } - -const BasicBlock* Function::get_current_block() const { return current_block_; } -BasicBlock* Function::get_current_block() { return current_block_; } - -const list& Function::get_constructs() const { - return cfg_constructs_; -} -list& Function::get_constructs() { return cfg_constructs_; } - -const BasicBlock* Function::get_first_block() const { - if (ordered_blocks_.empty()) return nullptr; - return ordered_blocks_[0]; -} -BasicBlock* Function::get_first_block() { - if (ordered_blocks_.empty()) return nullptr; - return ordered_blocks_[0]; -} - -BasicBlock::BasicBlock(uint32_t id) - : id_(id), - immediate_dominator_(nullptr), - predecessors_(), - successors_(), - reachable_(false) {} - -void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) { - immediate_dominator_ = dom_block; -} - -const BasicBlock* BasicBlock::GetImmediateDominator() const { - return immediate_dominator_; -} - -BasicBlock* BasicBlock::GetImmediateDominator() { return immediate_dominator_; } - -void BasicBlock::RegisterSuccessors(vector next_blocks) { - for (auto& block : next_blocks) { - block->predecessors_.push_back(this); - successors_.push_back(block); - if (block->reachable_ == false) block->set_reachability(reachable_); - } -} - -void BasicBlock::RegisterBranchInstruction(SpvOp branch_instruction) { - if (branch_instruction == SpvOpUnreachable) reachable_ = false; - return; -} - -bool Function::IsMergeBlock(uint32_t merge_block_id) const { - const auto b = blocks_.find(merge_block_id); - if (b != end(blocks_)) { - return cfg_constructs_.end() != - find_if(begin(cfg_constructs_), end(cfg_constructs_), - [&](const CFConstruct& construct) { - return construct.get_merge() == &b->second; - }); - } else { - return false; - } -} - -BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {} -BasicBlock::DominatorIterator::DominatorIterator(const BasicBlock* block) - : current_(block) {} - -BasicBlock::DominatorIterator& BasicBlock::DominatorIterator::operator++() { - if (current_ == current_->GetImmediateDominator()) { - current_ = nullptr; - } else { - current_ = current_->GetImmediateDominator(); - } - return *this; -} - -const BasicBlock::DominatorIterator BasicBlock::dom_begin() const { - return DominatorIterator(this); -} - -BasicBlock::DominatorIterator BasicBlock::dom_begin() { - return DominatorIterator(this); -} - -const BasicBlock::DominatorIterator BasicBlock::dom_end() const { - return DominatorIterator(); -} - -BasicBlock::DominatorIterator BasicBlock::dom_end() { - return DominatorIterator(); -} - -bool operator==(const BasicBlock::DominatorIterator& lhs, - const BasicBlock::DominatorIterator& rhs) { - return lhs.current_ == rhs.current_; -} - -bool operator!=(const BasicBlock::DominatorIterator& lhs, - const BasicBlock::DominatorIterator& rhs) { - return !(lhs == rhs); -} - -const BasicBlock*& BasicBlock::DominatorIterator::operator*() { - return current_; -} -} // namespace libspirv +} /// namespace libspirv diff --git a/source/val/ValidationState.h b/source/val/ValidationState.h new file mode 100644 index 0000000..41ccd99 --- /dev/null +++ b/source/val/ValidationState.h @@ -0,0 +1,248 @@ +// 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_VAL_VALIDATIONSTATE_H_ +#define LIBSPIRV_VAL_VALIDATIONSTATE_H_ + +#include +#include +#include +#include +#include +#include + +#include "assembly_grammar.h" +#include "diagnostic.h" +#include "spirv-tools/libspirv.h" +#include "spirv/spirv.h" +#include "spirv_definition.h" + +namespace libspirv { + +// 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 words; +} spv_id_info_t; + +/// 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 +}; + +class Function; + +/// This class manages the state of the SPIR-V validation as it is being parsed. +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 [Name] where + /// the 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; + + /// Like getIdName but does not display the id if the \p id has a name + std::string getIdOrName(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 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 + std::list& get_functions(); + + /// Returns the function states + Function& get_current_function(); + + /// Returns 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 . Otherwise, + /// returns . + std::pair 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 FindUsesWithoutDefs() const { + auto diff = uses_; + for (const auto d : defs_) diff.erase(d.first); + return diff; + } + + private: + std::unordered_set uses_; + std::unordered_map defs_; + }; + + UseDefTracker& usedefs() { return usedefs_; } + const UseDefTracker& usedefs() const { return usedefs_; } + + /// Returns a list of entry point function ids + std::vector& entry_points() { return entry_points_; } + const std::vector& entry_points() const { return entry_points_; } + + /// Registers the capability and its dependent capabilities + void RegisterCapability(SpvCapability cap); + + /// 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, + SpvFunctionControlMask function_control, + uint32_t function_type_id); + + /// Register a function end instruction + spv_result_t RegisterFunctionEnd(); + + /// 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; + + /// Sets the addressing model of this module (logical/physical). + void setAddressingModel(SpvAddressingModel am); + + /// Returns the addressing model of this module, or Logical if uninitialized. + SpvAddressingModel getAddressingModel() const; + + /// Sets the memory model of this module. + void setMemoryModel(SpvMemoryModel mm); + + /// Returns the memory model of this module, or Simple if uninitialized. + SpvMemoryModel getMemoryModel() 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 unresolved_forward_ids_; + + /// A map of operand IDs and their names defined by the OpName instruction + std::map operand_names_; + + /// The section of the code being processed + ModuleLayoutSection current_layout_section_; + + /// A list of functions in the module + std::list module_functions_; + + /// Mask of the capabilities available in the module + spv_capability_mask_t + module_capabilities_; /// Module's declared capabilities. + + /// Definitions and uses of all the IDs in the module. + UseDefTracker usedefs_; + + /// IDs that are entry points, ie, arguments to OpEntryPoint. + std::vector entry_points_; + + AssemblyGrammar grammar_; + + SpvAddressingModel addressing_model_; + SpvMemoryModel memory_model_; + + /// NOTE: See correspoding getter functions + bool in_function_; +}; + +} /// namespace libspirv + +#endif /// LIBSPIRV_VAL_VALIDATIONSTATE_H_ diff --git a/source/validate.cpp b/source/validate.cpp index 800cc51..63f7fe0 100644 --- a/source/validate.cpp +++ b/source/validate.cpp @@ -24,6 +24,8 @@ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +#include "validate.h" + #include #include @@ -34,9 +36,6 @@ #include #include -#include "validate.h" -#include "validate_passes.h" - #include "binary.h" #include "diagnostic.h" #include "instruction.h" @@ -45,6 +44,9 @@ #include "spirv-tools/libspirv.h" #include "spirv_constant.h" #include "spirv_endian.h" +#include "val/Construct.h" +#include "val/Function.h" +#include "val/ValidationState.h" using std::function; using std::ostream_iterator; @@ -123,6 +125,7 @@ void DebugInstructionPass(ValidationState_t& _, default: break; } + } // Collects use-def info about an instruction's IDs. diff --git a/source/validate.h b/source/validate.h index 35050cd..6f2b89e 100644 --- a/source/validate.h +++ b/source/validate.h @@ -44,517 +44,13 @@ #include "spirv-tools/libspirv.h" #include "spirv_definition.h" #include "table.h" - -#define MSG(msg) \ - do { \ - libspirv::message(__FILE__, size_t(__LINE__), msg); \ - } while (0) - -#define SHOW(exp) \ - do { \ - libspirv::message(__FILE__, size_t(__LINE__), #exp, (exp)); \ - } while (0) +#include "val/BasicBlock.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 words; -} spv_id_info_t; - namespace libspirv { -void message(std::string file, size_t line, std::string name); - -template -void message(std::string file, size_t line, std::string name, T val) { - std::cout << file << ":" << line << ": " << name << " " << val << std::endl; -} - -/// 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; -class Function; - -// This class represents a basic block in a SPIR-V module -class BasicBlock { - public: - /// Constructor for a BasicBlock - /// - /// @param[in] id The ID of the basic block - explicit BasicBlock(uint32_t id); - - /// Returns the id of the BasicBlock - uint32_t get_id() const { return id_; } - - /// Returns the predecessors of the BasicBlock - const std::vector& get_predecessors() const { - return predecessors_; - } - - /// Returns the predecessors of the BasicBlock - std::vector& get_predecessors() { return predecessors_; } - - /// Returns the successors of the BasicBlock - const std::vector& get_successors() const { return successors_; } - - /// Returns the successors of the BasicBlock - std::vector& get_successors() { return successors_; } - - /// Returns true if the block should be reachable in the CFG - bool is_reachable() const { return reachable_; } - - void set_reachability(bool reachability) { reachable_ = reachability; } - - /// Sets the immedate dominator of this basic block - /// - /// @param[in] dom_block The dominator block - void SetImmediateDominator(BasicBlock* dom_block); - - /// Returns the immedate dominator of this basic block - BasicBlock* GetImmediateDominator(); - - /// Returns the immedate dominator of this basic block - const BasicBlock* GetImmediateDominator() const; - - /// Ends the block without a successor - void RegisterBranchInstruction(SpvOp branch_instruction); - - /// Adds @p next BasicBlocks as successors of this BasicBlock - void RegisterSuccessors(std::vector next = {}); - - /// Returns true if the id of the BasicBlock matches - bool operator==(const BasicBlock& other) const { return other.id_ == id_; } - - /// Returns true if the id of the BasicBlock matches - bool operator==(const uint32_t& id) const { return id == id_; } - - /// @brief A BasicBlock dominator iterator class - /// - /// This iterator will iterate over the dominators of the block - class DominatorIterator - : public std::iterator { - public: - /// @brief Constructs the end of dominator iterator - /// - /// This will create an iterator which will represent the element - /// before the root node of the dominator tree - DominatorIterator(); - - /// @brief Constructs an iterator for the given block which points to - /// @p block - /// - /// @param block The block which is referenced by the iterator - explicit DominatorIterator(const BasicBlock* block); - - /// @brief Advances the iterator - DominatorIterator& operator++(); - - /// @brief Returns the current element - const BasicBlock*& operator*(); - - friend bool operator==(const DominatorIterator& lhs, - const DominatorIterator& rhs); - - private: - const BasicBlock* current_; - }; - - /// Returns an iterator which points to the current block - const DominatorIterator dom_begin() const; - DominatorIterator dom_begin(); - - /// Returns an iterator which points to one element past the first block - const DominatorIterator dom_end() const; - DominatorIterator dom_end(); - - private: - /// Id of the BasicBlock - const uint32_t id_; - - /// Pointer to the immediate dominator of the BasicBlock - BasicBlock* immediate_dominator_; - - /// The set of predecessors of the BasicBlock - std::vector predecessors_; - - /// The set of successors of the BasicBlock - std::vector successors_; - - SpvOp branch_instruction_; - - bool reachable_; -}; - -/// @brief Returns true if the iterators point to the same element or if both -/// iterators point to the @p dom_end block -bool operator==(const BasicBlock::DominatorIterator& lhs, - const BasicBlock::DominatorIterator& rhs); - -/// @brief Returns true if the iterators point to different elements and they -/// do not both point to the @p dom_end block -bool operator!=(const BasicBlock::DominatorIterator& lhs, - const BasicBlock::DominatorIterator& rhs); - -/// @brief This class tracks the CFG constructs as defined in the SPIR-V spec -class CFConstruct { - // Universal Limit of ResultID + 1 - static const uint32_t kInitialValue = 0x400000; - - public: - CFConstruct(BasicBlock* header_block, BasicBlock* merge_block, - BasicBlock* continue_block = nullptr) - : header_block_(header_block), - merge_block_(merge_block), - continue_block_(continue_block) {} - - const BasicBlock* get_header() const { return header_block_; } - const BasicBlock* get_merge() const { return merge_block_; } - const BasicBlock* get_continue() const { return continue_block_; } - - BasicBlock* get_header() { return header_block_; } - BasicBlock* get_merge() { return merge_block_; } - BasicBlock* get_continue() { return continue_block_; } - - private: - BasicBlock* header_block_; ///< The header block of a loop or selection - BasicBlock* merge_block_; ///< The merge block of a loop or selection - BasicBlock* continue_block_; ///< The continue block of a loop block -}; - -// 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. -class Function { - public: - Function(uint32_t id, uint32_t result_type_id, - SpvFunctionControlMask function_control, uint32_t function_type_id, - ValidationState_t& module); - - /// Registers a function parameter in the current function - /// @return Returns SPV_SUCCESS if the call was successful - spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id); - - /// Sets the declaration type of the current function - /// @return Returns SPV_SUCCESS if the call was successful - 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 - /// @return Returns SPV_SUCCESS if the call was successful - spv_result_t RegisterBlock(uint32_t id, bool is_definition = true); - - /// Registers a variable in the current block - /// - /// @param[in] type_id The type ID of the varaible - /// @param[in] id The ID of the varaible - /// @param[in] storage The storage of the variable - /// @param[in] init_id The initializer ID of the variable - /// - /// @return Returns SPV_SUCCESS if the call was successful - spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id, - SpvStorageClass storage, uint32_t init_id); - - /// Registers a loop merge construct in the function - /// - /// @param[in] merge_id The merge block ID of the loop - /// @param[in] continue_id The continue block ID of the loop - /// - /// @return Returns SPV_SUCCESS if the call was successful - spv_result_t RegisterLoopMerge(uint32_t merge_id, uint32_t continue_id); - - /// Registers a selection merge construct in the function - /// @return Returns SPV_SUCCESS if the call was successful - spv_result_t RegisterSelectionMerge(uint32_t merge_id); - - /// Registers the end of the block - /// - /// @param[in] successors_list A list of ids to the blocks successors - /// @param[in] branch_instruction the branch instruction that ended the block - void RegisterBlockEnd(std::vector successors_list, - SpvOp branch_instruction); - - /// Returns true if the \p merge_block_id is a merge block - bool IsMergeBlock(uint32_t merge_block_id) const; - - /// Returns true if the \p id is the first block of this function - bool IsFirstBlock(uint32_t id) const; - - /// Returns the first block of the current function - const BasicBlock* get_first_block() const; - - /// Returns the first block of the current function - BasicBlock* get_first_block(); - - /// Returns a vector of all the blocks in the function - const std::vector& get_blocks() const; - - /// Returns a vector of all the blocks in the function - std::vector& get_blocks(); - - /// Returns a list of all the cfg constructs in the function - const std::list& get_constructs() const; - - /// Returns a list of all the cfg constructs in the function - std::list& get_constructs(); - - // Returns the number of blocks in the current function being parsed - size_t get_block_count() const; - - /// Returns the id of the funciton - uint32_t get_id() const { return id_; } - - // Returns the number of blocks in the current function being parsed - size_t get_undefined_block_count() const; - const std::unordered_set& get_undefined_blocks() const { - return undefined_blocks_; - } - - /// Returns the block that is currently being parsed in the binary - BasicBlock* get_current_block(); - - /// Returns the block that is currently being parsed in the binary - const BasicBlock* get_current_block() const; - - /// Prints a GraphViz digraph of the CFG of the current funciton - void printDotGraph() const; - - /// Prints a directed graph of the CFG of the current funciton - void printBlocks() const; - - private: - /// Parent module - ValidationState_t& module_; - - /// The result id of the OpLabel that defined this block - uint32_t id_; - - /// The type of the function - uint32_t function_type_id_; - - /// The type of the return value - uint32_t result_type_id_; - - /// The control fo the funciton - SpvFunctionControlMask function_control_; - - /// The type of declaration of each function - FunctionDecl declaration_type_; - - /// The blocks in the function mapped by block ID - std::unordered_map blocks_; - - /// A list of blocks in the order they appeared in the binary - std::vector ordered_blocks_; - - /// Blocks which are forward referenced by blocks but not defined - std::unordered_set undefined_blocks_; - - /// The block that is currently being parsed - BasicBlock* current_block_; - - /// The constructs that are available in this function - std::list cfg_constructs_; - - /// The variable IDs of the functions - std::vector variable_ids_; - - /// The function parameter ids of the functions - std::vector parameter_ids_; -}; - -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 [Name] where - // the 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; - - /// Like getIdName but does not display the id if the \p id has a name - std::string getIdOrName(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 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 - std::list& get_functions(); - - // Returns the function states - Function& get_current_function(); - - // Returns 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 . Otherwise, - // returns . - std::pair 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 FindUsesWithoutDefs() const { - auto diff = uses_; - for (const auto d : defs_) diff.erase(d.first); - return diff; - } - - private: - std::unordered_set uses_; - std::unordered_map defs_; - }; - - UseDefTracker& usedefs() { return usedefs_; } - const UseDefTracker& usedefs() const { return usedefs_; } - - std::vector& entry_points() { return entry_points_; } - const std::vector& entry_points() const { return entry_points_; } - - // Registers the capability and its dependent capabilities - void RegisterCapability(SpvCapability cap); - - // 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, - SpvFunctionControlMask function_control, - uint32_t function_type_id); - - // Register a function end instruction - spv_result_t RegisterFunctionEnd(); - - // 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; - - // Sets the addressing model of this module (logical/physical). - void setAddressingModel(SpvAddressingModel am); - - // Returns the addressing model of this module, or Logical if uninitialized. - SpvAddressingModel getAddressingModel() const; - - // Sets the memory model of this module. - void setMemoryModel(SpvMemoryModel mm); - - // Returns the memory model of this module, or Simple if uninitialized. - SpvMemoryModel getMemoryModel() 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 unresolved_forward_ids_; - - std::map operand_names_; - - // The section of the code being processed - ModuleLayoutSection current_layout_section_; - - std::list module_functions_; - - spv_capability_mask_t - module_capabilities_; // Module's declared capabilities. - - // Definitions and uses of all the IDs in the module. - UseDefTracker usedefs_; - - // IDs that are entry points, ie, arguments to OpEntryPoint. - std::vector entry_points_; - - AssemblyGrammar grammar_; - - SpvAddressingModel addressing_model_; - SpvMemoryModel memory_model_; - - // NOTE: See correspoding getter functions - bool in_function_; -}; /// @brief Calculates dominator edges of a root basic block /// @@ -564,7 +60,7 @@ class ValidationState_t { /// @param[in] first_block the root or entry BasicBlock of a function /// /// @return a set of dominator edges represented as a pair of blocks -std::vector > CalculateDominators( +std::vector> CalculateDominators( const BasicBlock& first_block); /// @brief Performs the Control Flow Graph checks @@ -574,20 +70,37 @@ std::vector > CalculateDominators( /// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_CFG otherwise spv_result_t PerformCfgChecks(ValidationState_t& _); -// @brief Updates the immediate dominator for each of the block edges -// -// Updates the immediate dominator of the blocks for each of the edges -// provided by the @p dom_edges parameter -// -// @param[in,out] dom_edges The edges of the dominator tree +/// @brief Updates the immediate dominator for each of the block edges +/// +/// Updates the immediate dominator of the blocks for each of the edges +/// provided by the @p dom_edges parameter +/// +/// @param[in,out] dom_edges The edges of the dominator tree void UpdateImmediateDominators( - std::vector >& dom_edges); + std::vector>& dom_edges); -// @brief Prints all of the dominators of a BasicBlock -// -// @param[in] block The dominators of this block will be printed +/// @brief Prints all of the dominators of a BasicBlock +/// +/// @param[in] block The dominators of this block will be printed void printDominatorList(BasicBlock& block); +/// Performs logical layout validation as described in section 2.4 of the SPIR-V +/// spec. +spv_result_t ModuleLayoutPass(ValidationState_t& _, + const spv_parsed_instruction_t* inst); + +/// Performs Control Flow Graph validation of a module +spv_result_t CfgPass(ValidationState_t& _, + const spv_parsed_instruction_t* inst); + +/// Performs SSA validation of a module +spv_result_t SsaPass(ValidationState_t& _, + const spv_parsed_instruction_t* inst); + +/// Performs instruction validation. +spv_result_t InstructionPass(ValidationState_t& _, + const spv_parsed_instruction_t* inst); + } // namespace libspirv /// @brief Validate the ID usage of the instruction stream diff --git a/source/validate_cfg.cpp b/source/validate_cfg.cpp index 187d820..d6d1389 100644 --- a/source/validate_cfg.cpp +++ b/source/validate_cfg.cpp @@ -25,15 +25,20 @@ // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. #include "validate.h" -#include "validate_passes.h" -#include #include + +#include #include #include #include #include +#include "val/BasicBlock.h" +#include "val/Construct.h" +#include "val/Function.h" +#include "val/ValidationState.h" + using std::find; using std::get; using std::make_pair; @@ -242,7 +247,7 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { } // Check all headers dominate their merge blocks - for (CFConstruct& construct : function.get_constructs()) { + for (Construct& construct : function.get_constructs()) { auto header = construct.get_header(); auto merge = construct.get_merge(); // auto cont = construct.get_continue(); diff --git a/source/validate_id.cpp b/source/validate_id.cpp index ac68bc9..edb559b 100644 --- a/source/validate_id.cpp +++ b/source/validate_id.cpp @@ -24,7 +24,10 @@ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +#include "validate.h" + #include + #include #include #include @@ -33,7 +36,7 @@ #include "instruction.h" #include "opcode.h" #include "spirv-tools/libspirv.h" -#include "validate.h" +#include "val/ValidationState.h" #define spvCheck(condition, action) \ if (condition) { \ diff --git a/source/validate_instruction.cpp b/source/validate_instruction.cpp index b86028d..6098fb6 100644 --- a/source/validate_instruction.cpp +++ b/source/validate_instruction.cpp @@ -26,14 +26,18 @@ // Performs validation on instructions that appear inside of a SPIR-V block. +#include "validate.h" + #include + #include #include #include "diagnostic.h" #include "opcode.h" #include "spirv_definition.h" -#include "validate_passes.h" +#include "val/Function.h" +#include "val/ValidationState.h" using libspirv::AssemblyGrammar; using libspirv::DiagnosticStream; diff --git a/source/validate_layout.cpp b/source/validate_layout.cpp index 0b0b609..943fb59 100644 --- a/source/validate_layout.cpp +++ b/source/validate_layout.cpp @@ -26,14 +26,16 @@ // Source code for logical layout validation as described in section 2.4 -#include "spirv-tools/libspirv.h" -#include "validate_passes.h" +#include "validate.h" + +#include #include "diagnostic.h" #include "opcode.h" #include "operand.h" - -#include +#include "spirv-tools/libspirv.h" +#include "val/Function.h" +#include "val/ValidationState.h" using libspirv::ValidationState_t; using libspirv::kLayoutMemoryModel; @@ -42,7 +44,6 @@ using libspirv::kLayoutFunctionDefinitions; using libspirv::FunctionDecl; namespace { - // Module scoped instructions are processed by determining if the opcode // is part of the current layout section. If it is not then the next sections is // checked. @@ -86,11 +87,11 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_LAYOUT) << "Cannot declare a function in a function body"; } - auto control_mask = static_cast(inst->words[inst->operands[2].offset]); - spvCheckReturn(_.RegisterFunction( - inst->result_id, inst->type_id, - control_mask, - inst->words[inst->operands[3].offset])); + auto control_mask = static_cast( + inst->words[inst->operands[2].offset]); + spvCheckReturn( + _.RegisterFunction(inst->result_id, inst->type_id, control_mask, + inst->words[inst->operands[3].offset])); if (_.getLayoutSection() == kLayoutFunctionDefinitions) spvCheckReturn(_.get_current_function().RegisterSetFunctionDeclType( FunctionDecl::kFunctionDeclDefinition)); @@ -104,7 +105,8 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, } if (_.get_current_function().get_block_count() != 0) { return _.diag(SPV_ERROR_INVALID_LAYOUT) - << "Function parameters must only appear immediately after the " + << "Function parameters must only appear immediately after " + "the " "function definition"; } spvCheckReturn(_.get_current_function().RegisterFunctionParameter( @@ -128,7 +130,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, } if (_.getLayoutSection() == kLayoutFunctionDeclarations) { spvCheckReturn(_.get_current_function().RegisterSetFunctionDeclType( - FunctionDecl::kFunctionDeclDeclaration)); + FunctionDecl::kFunctionDeclDeclaration)); } spvCheckReturn(_.RegisterFunctionEnd()); break; @@ -174,7 +176,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _, } return SPV_SUCCESS; } -} +} /// namespace namespace libspirv { // TODO(umar): Check linkage capabilities for function declarations @@ -205,4 +207,4 @@ spv_result_t ModuleLayoutPass(ValidationState_t& _, } // switch(getLayoutSection()) return SPV_SUCCESS; } -} +} /// namespace libspirv diff --git a/source/validate_ssa.cpp b/source/validate_ssa.cpp index 38250bf..423221d 100644 --- a/source/validate_ssa.cpp +++ b/source/validate_ssa.cpp @@ -24,9 +24,12 @@ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +#include "validate.h" + #include + #include "opcode.h" -#include "validate_passes.h" +#include "val/ValidationState.h" using std::function; using libspirv::ValidationState_t; diff --git a/test/UnitSPIRV.h b/test/UnitSPIRV.h index 967d998..93bf791 100644 --- a/test/UnitSPIRV.h +++ b/test/UnitSPIRV.h @@ -31,15 +31,15 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_endian.h" +#include "source/text.h" +#include "source/text_handler.h" +#include "source/validate.h" +#include "spirv-tools/libspirv.h" #include diff --git a/test/ValidationState.cpp b/test/ValidationState.cpp index 58d15b6..0ab90c7 100644 --- a/test/ValidationState.cpp +++ b/test/ValidationState.cpp @@ -27,12 +27,15 @@ // Unit tests for ValidationState_t. -#include +#include "val/ValidationState.h" + #include +#include "gtest/gtest.h" #include "spirv/spirv.h" - -#include "source/validate.h" +#include "val/Construct.h" +#include "val/Function.h" +#include "validate.h" namespace { using libspirv::ValidationState_t; -- 2.7.4