mirror of
https://github.com/xenia-project/SPIRV-Tools.git
synced 2024-11-23 11:19:41 +00:00
Split validate_types file into multiple classes
Creates separate files for the ValidationState, Function and BasicBlock classes.
This commit is contained in:
parent
00b72c2995
commit
90a4252aae
@ -79,6 +79,8 @@ elseif(MSVC)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/source)
|
||||||
|
|
||||||
option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON)
|
option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON)
|
||||||
if(${SPIRV_COLOR_TERMINAL})
|
if(${SPIRV_COLOR_TERMINAL})
|
||||||
add_definitions(-DSPIRV_COLOR_TERMINAL)
|
add_definitions(-DSPIRV_COLOR_TERMINAL)
|
||||||
|
@ -167,7 +167,10 @@ set(SPIRV_SOURCES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/validate_ssa.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.
|
# The software_version.cpp file includes build-version.inc.
|
||||||
# Rebuild the software_version.cpp object file if it is older than
|
# 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
|
${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp
|
||||||
PROPERTIES OBJECT_DEPENDS "${SPIRV_TOOLS_BUILD_VERSION_INC}")
|
PROPERTIES OBJECT_DEPENDS "${SPIRV_TOOLS_BUILD_VERSION_INC}")
|
||||||
|
|
||||||
|
|
||||||
add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
|
add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
|
||||||
spvtools_default_compile_options(${SPIRV_TOOLS})
|
spvtools_default_compile_options(${SPIRV_TOOLS})
|
||||||
target_include_directories(${SPIRV_TOOLS}
|
target_include_directories(${SPIRV_TOOLS}
|
||||||
|
@ -148,4 +148,8 @@ spvResultToString(spv_result_t res) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void message(std::string file, size_t line, std::string name) {
|
||||||
|
std::cout << file << ":" << line << ": " << name << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace libspirv
|
} // namespace libspirv
|
||||||
|
@ -33,8 +33,21 @@
|
|||||||
|
|
||||||
#include "spirv-tools/libspirv.h"
|
#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 {
|
class diagnostic_helper {
|
||||||
public:
|
public:
|
||||||
diagnostic_helper(spv_position_t& position, spv_diagnostic* diagnostic)
|
diagnostic_helper(spv_position_t& position, spv_diagnostic* diagnostic)
|
||||||
@ -104,6 +117,14 @@ class DiagnosticStream {
|
|||||||
|
|
||||||
std::string spvResultToString(spv_result_t res);
|
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 <typename T>
|
||||||
|
void message(std::string file, size_t line, std::string name, T val) {
|
||||||
|
std::cout << file << ":" << line << ": " << name << " " << val << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace libspirv
|
} // namespace libspirv
|
||||||
|
|
||||||
#endif // LIBSPIRV_DIAGNOSTIC_H_
|
#endif // LIBSPIRV_DIAGNOSTIC_H_
|
||||||
|
107
source/val/BasicBlock.cpp
Normal file
107
source/val/BasicBlock.cpp
Normal file
@ -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 <vector>
|
||||||
|
|
||||||
|
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<BasicBlock*> 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
|
157
source/val/BasicBlock.h
Normal file
157
source/val/BasicBlock.h
Normal file
@ -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 <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<BasicBlock*>& get_predecessors() const {
|
||||||
|
return predecessors_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the predecessors of the BasicBlock
|
||||||
|
std::vector<BasicBlock*>& get_predecessors() { return predecessors_; }
|
||||||
|
|
||||||
|
/// Returns the successors of the BasicBlock
|
||||||
|
const std::vector<BasicBlock*>& get_successors() const { return successors_; }
|
||||||
|
|
||||||
|
/// Returns the successors of the BasicBlock
|
||||||
|
std::vector<BasicBlock*>& 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<BasicBlock*> 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<std::forward_iterator_tag, BasicBlock*> {
|
||||||
|
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<BasicBlock*> predecessors_;
|
||||||
|
|
||||||
|
/// The set of successors of the BasicBlock
|
||||||
|
std::vector<BasicBlock*> 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_
|
@ -24,32 +24,21 @@
|
|||||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
|
||||||
#ifndef LIBSPIRV_VALIDATE_PASSES_H_
|
#include "val/Construct.h"
|
||||||
#define LIBSPIRV_VALIDATE_PASSES_H_
|
|
||||||
|
|
||||||
#include "binary.h"
|
namespace libspirv {
|
||||||
#include "validate.h"
|
|
||||||
|
|
||||||
namespace libspirv
|
Construct::Construct(BasicBlock* header_block, BasicBlock* merge_block,
|
||||||
{
|
BasicBlock* continue_block)
|
||||||
// TODO(umar): Better docs
|
: 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
|
const BasicBlock* Construct::get_header() const { return header_block_; }
|
||||||
spv_result_t ModuleLayoutPass(ValidationState_t& _,
|
const BasicBlock* Construct::get_merge() const { return merge_block_; }
|
||||||
const spv_parsed_instruction_t* inst);
|
const BasicBlock* Construct::get_continue() const { return continue_block_; }
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
|
BasicBlock* Construct::get_header() { return header_block_; }
|
||||||
|
BasicBlock* Construct::get_merge() { return merge_block_; }
|
||||||
|
BasicBlock* Construct::get_continue() { return continue_block_; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
58
source/val/Construct.h
Normal file
58
source/val/Construct.h
Normal file
@ -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 <cstdint>
|
||||||
|
|
||||||
|
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_
|
232
source/val/Function.cpp
Normal file
232
source/val/Function.cpp
Normal file
@ -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 <cassert>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<uint32_t, BasicBlock>::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<uint32_t> 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<BasicBlock*> next_blocks;
|
||||||
|
next_blocks.reserve(next_list.size());
|
||||||
|
|
||||||
|
std::unordered_map<uint32_t, BasicBlock>::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<BasicBlock*>& Function::get_blocks() const {
|
||||||
|
return ordered_blocks_;
|
||||||
|
}
|
||||||
|
vector<BasicBlock*>& 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<Construct>& Function::get_constructs() const {
|
||||||
|
return cfg_constructs_;
|
||||||
|
}
|
||||||
|
list<Construct>& 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
|
195
source/val/Function.h
Normal file
195
source/val/Function.h
Normal file
@ -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 <list>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#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<uint32_t> 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<BasicBlock*>& get_blocks() const;
|
||||||
|
|
||||||
|
/// Returns a vector of all the blocks in the function
|
||||||
|
std::vector<BasicBlock*>& get_blocks();
|
||||||
|
|
||||||
|
/// Returns a list of all the cfg constructs in the function
|
||||||
|
const std::list<Construct>& get_constructs() const;
|
||||||
|
|
||||||
|
/// Returns a list of all the cfg constructs in the function
|
||||||
|
std::list<Construct>& 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<uint32_t>& 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<uint32_t, BasicBlock> blocks_;
|
||||||
|
|
||||||
|
/// A list of blocks in the order they appeared in the binary
|
||||||
|
std::vector<BasicBlock*> ordered_blocks_;
|
||||||
|
|
||||||
|
/// Blocks which are forward referenced by blocks but not defined
|
||||||
|
std::unordered_set<uint32_t> undefined_blocks_;
|
||||||
|
|
||||||
|
/// The block that is currently being parsed
|
||||||
|
BasicBlock* current_block_;
|
||||||
|
|
||||||
|
/// The constructs that are available in this function
|
||||||
|
std::list<Construct> cfg_constructs_;
|
||||||
|
|
||||||
|
/// The variable IDs of the functions
|
||||||
|
std::vector<uint32_t> variable_ids_;
|
||||||
|
|
||||||
|
/// The function parameter ids of the functions
|
||||||
|
std::vector<uint32_t> parameter_ids_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /// namespace libspirv
|
||||||
|
|
||||||
|
|
||||||
|
#endif /// LIBSPIRV_VAL_FUNCTION_H_
|
@ -24,40 +24,19 @@
|
|||||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
|
||||||
#include <algorithm>
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iterator>
|
|
||||||
#include <limits>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "spirv/spirv.h"
|
#include "val/BasicBlock.h"
|
||||||
#include "spirv_definition.h"
|
#include "val/Construct.h"
|
||||||
#include "validate.h"
|
#include "val/Function.h"
|
||||||
|
|
||||||
using std::find;
|
|
||||||
using std::list;
|
using std::list;
|
||||||
using std::numeric_limits;
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::unordered_set;
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
using libspirv::kLayoutCapabilities;
|
namespace libspirv {
|
||||||
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 {
|
namespace {
|
||||||
bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
|
bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
|
||||||
@ -211,12 +190,6 @@ bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // 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,
|
ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic,
|
||||||
const spv_const_context context)
|
const spv_const_context context)
|
||||||
: diagnostic_(diagnostic),
|
: diagnostic_(diagnostic),
|
||||||
@ -357,25 +330,6 @@ SpvMemoryModel ValidationState_t::getMemoryModel() const {
|
|||||||
return memory_model_;
|
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(
|
spv_result_t ValidationState_t::RegisterFunction(
|
||||||
uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control,
|
uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control,
|
||||||
uint32_t function_type_id) {
|
uint32_t function_type_id) {
|
||||||
@ -402,244 +356,4 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() {
|
|||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
spv_result_t Function::RegisterFunctionParameter(uint32_t id,
|
} /// namespace libspirv
|
||||||
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<uint32_t, BasicBlock>::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<uint32_t> 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<BasicBlock*> next_blocks;
|
|
||||||
next_blocks.reserve(next_list.size());
|
|
||||||
|
|
||||||
std::unordered_map<uint32_t, BasicBlock>::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<BasicBlock*>& Function::get_blocks() const {
|
|
||||||
return ordered_blocks_;
|
|
||||||
}
|
|
||||||
vector<BasicBlock*>& 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<CFConstruct>& Function::get_constructs() const {
|
|
||||||
return cfg_constructs_;
|
|
||||||
}
|
|
||||||
list<CFConstruct>& 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<BasicBlock*> 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
|
|
248
source/val/ValidationState.h
Normal file
248
source/val/ValidationState.h
Normal file
@ -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 <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<uint32_t> 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 <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;
|
||||||
|
|
||||||
|
/// 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<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
|
||||||
|
std::list<Function>& 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 <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_; }
|
||||||
|
|
||||||
|
/// Returns a list of entry point function ids
|
||||||
|
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);
|
||||||
|
|
||||||
|
/// 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<uint32_t> unresolved_forward_ids_;
|
||||||
|
|
||||||
|
/// A map of operand IDs and their names defined by the OpName instruction
|
||||||
|
std::map<uint32_t, std::string> operand_names_;
|
||||||
|
|
||||||
|
/// The section of the code being processed
|
||||||
|
ModuleLayoutSection current_layout_section_;
|
||||||
|
|
||||||
|
/// A list of functions in the module
|
||||||
|
std::list<Function> 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<uint32_t> 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_
|
@ -24,6 +24,8 @@
|
|||||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
@ -34,9 +36,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "validate.h"
|
|
||||||
#include "validate_passes.h"
|
|
||||||
|
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
#include "instruction.h"
|
#include "instruction.h"
|
||||||
@ -45,6 +44,9 @@
|
|||||||
#include "spirv-tools/libspirv.h"
|
#include "spirv-tools/libspirv.h"
|
||||||
#include "spirv_constant.h"
|
#include "spirv_constant.h"
|
||||||
#include "spirv_endian.h"
|
#include "spirv_endian.h"
|
||||||
|
#include "val/Construct.h"
|
||||||
|
#include "val/Function.h"
|
||||||
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
using std::function;
|
using std::function;
|
||||||
using std::ostream_iterator;
|
using std::ostream_iterator;
|
||||||
@ -123,6 +125,7 @@ void DebugInstructionPass(ValidationState_t& _,
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collects use-def info about an instruction's IDs.
|
// Collects use-def info about an instruction's IDs.
|
||||||
|
@ -44,517 +44,13 @@
|
|||||||
#include "spirv-tools/libspirv.h"
|
#include "spirv-tools/libspirv.h"
|
||||||
#include "spirv_definition.h"
|
#include "spirv_definition.h"
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
|
#include "val/BasicBlock.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)
|
|
||||||
|
|
||||||
// Structures
|
// 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 {
|
namespace libspirv {
|
||||||
|
|
||||||
void message(std::string file, size_t line, std::string name);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
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 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<BasicBlock*>& get_predecessors() const {
|
|
||||||
return predecessors_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the predecessors of the BasicBlock
|
|
||||||
std::vector<BasicBlock*>& get_predecessors() { return predecessors_; }
|
|
||||||
|
|
||||||
/// Returns the successors of the BasicBlock
|
|
||||||
const std::vector<BasicBlock*>& get_successors() const { return successors_; }
|
|
||||||
|
|
||||||
/// Returns the successors of the BasicBlock
|
|
||||||
std::vector<BasicBlock*>& 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<BasicBlock*> 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<std::forward_iterator_tag, BasicBlock*> {
|
|
||||||
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<BasicBlock*> predecessors_;
|
|
||||||
|
|
||||||
/// The set of successors of the BasicBlock
|
|
||||||
std::vector<BasicBlock*> 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<uint32_t> 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<BasicBlock*>& get_blocks() const;
|
|
||||||
|
|
||||||
/// Returns a vector of all the blocks in the function
|
|
||||||
std::vector<BasicBlock*>& get_blocks();
|
|
||||||
|
|
||||||
/// Returns a list of all the cfg constructs in the function
|
|
||||||
const std::list<CFConstruct>& get_constructs() const;
|
|
||||||
|
|
||||||
/// Returns a list of all the cfg constructs in the function
|
|
||||||
std::list<CFConstruct>& 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<uint32_t>& 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<uint32_t, BasicBlock> blocks_;
|
|
||||||
|
|
||||||
/// A list of blocks in the order they appeared in the binary
|
|
||||||
std::vector<BasicBlock*> ordered_blocks_;
|
|
||||||
|
|
||||||
/// Blocks which are forward referenced by blocks but not defined
|
|
||||||
std::unordered_set<uint32_t> undefined_blocks_;
|
|
||||||
|
|
||||||
/// The block that is currently being parsed
|
|
||||||
BasicBlock* current_block_;
|
|
||||||
|
|
||||||
/// The constructs that are available in this function
|
|
||||||
std::list<CFConstruct> cfg_constructs_;
|
|
||||||
|
|
||||||
/// The variable IDs of the functions
|
|
||||||
std::vector<uint32_t> variable_ids_;
|
|
||||||
|
|
||||||
/// The function parameter ids of the functions
|
|
||||||
std::vector<uint32_t> 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 <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;
|
|
||||||
|
|
||||||
/// 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<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
|
|
||||||
std::list<Function>& 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 <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);
|
|
||||||
|
|
||||||
// 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<uint32_t> unresolved_forward_ids_;
|
|
||||||
|
|
||||||
std::map<uint32_t, std::string> operand_names_;
|
|
||||||
|
|
||||||
// The section of the code being processed
|
|
||||||
ModuleLayoutSection current_layout_section_;
|
|
||||||
|
|
||||||
std::list<Function> 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<uint32_t> 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
|
/// @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
|
/// @param[in] first_block the root or entry BasicBlock of a function
|
||||||
///
|
///
|
||||||
/// @return a set of dominator edges represented as a pair of blocks
|
/// @return a set of dominator edges represented as a pair of blocks
|
||||||
std::vector<std::pair<BasicBlock*, BasicBlock*> > CalculateDominators(
|
std::vector<std::pair<BasicBlock*, BasicBlock*>> CalculateDominators(
|
||||||
const BasicBlock& first_block);
|
const BasicBlock& first_block);
|
||||||
|
|
||||||
/// @brief Performs the Control Flow Graph checks
|
/// @brief Performs the Control Flow Graph checks
|
||||||
@ -574,20 +70,37 @@ std::vector<std::pair<BasicBlock*, BasicBlock*> > CalculateDominators(
|
|||||||
/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_CFG otherwise
|
/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_CFG otherwise
|
||||||
spv_result_t PerformCfgChecks(ValidationState_t& _);
|
spv_result_t PerformCfgChecks(ValidationState_t& _);
|
||||||
|
|
||||||
// @brief Updates the immediate dominator for each of the block edges
|
/// @brief Updates the immediate dominator for each of the block edges
|
||||||
//
|
///
|
||||||
// Updates the immediate dominator of the blocks for each of the edges
|
/// Updates the immediate dominator of the blocks for each of the edges
|
||||||
// provided by the @p dom_edges parameter
|
/// provided by the @p dom_edges parameter
|
||||||
//
|
///
|
||||||
// @param[in,out] dom_edges The edges of the dominator tree
|
/// @param[in,out] dom_edges The edges of the dominator tree
|
||||||
void UpdateImmediateDominators(
|
void UpdateImmediateDominators(
|
||||||
std::vector<std::pair<BasicBlock*, BasicBlock*> >& dom_edges);
|
std::vector<std::pair<BasicBlock*, BasicBlock*>>& dom_edges);
|
||||||
|
|
||||||
// @brief Prints all of the dominators of a BasicBlock
|
/// @brief Prints all of the dominators of a BasicBlock
|
||||||
//
|
///
|
||||||
// @param[in] block The dominators of this block will be printed
|
/// @param[in] block The dominators of this block will be printed
|
||||||
void printDominatorList(BasicBlock& block);
|
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
|
} // namespace libspirv
|
||||||
|
|
||||||
/// @brief Validate the ID usage of the instruction stream
|
/// @brief Validate the ID usage of the instruction stream
|
||||||
|
@ -25,15 +25,20 @@
|
|||||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
|
||||||
#include "validate.h"
|
#include "validate.h"
|
||||||
#include "validate_passes.h"
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "val/BasicBlock.h"
|
||||||
|
#include "val/Construct.h"
|
||||||
|
#include "val/Function.h"
|
||||||
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
using std::find;
|
using std::find;
|
||||||
using std::get;
|
using std::get;
|
||||||
using std::make_pair;
|
using std::make_pair;
|
||||||
@ -242,7 +247,7 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check all headers dominate their merge blocks
|
// 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 header = construct.get_header();
|
||||||
auto merge = construct.get_merge();
|
auto merge = construct.get_merge();
|
||||||
// auto cont = construct.get_continue();
|
// auto cont = construct.get_continue();
|
||||||
|
@ -24,7 +24,10 @@
|
|||||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -33,7 +36,7 @@
|
|||||||
#include "instruction.h"
|
#include "instruction.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "spirv-tools/libspirv.h"
|
#include "spirv-tools/libspirv.h"
|
||||||
#include "validate.h"
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
#define spvCheck(condition, action) \
|
#define spvCheck(condition, action) \
|
||||||
if (condition) { \
|
if (condition) { \
|
||||||
|
@ -26,14 +26,18 @@
|
|||||||
|
|
||||||
// Performs validation on instructions that appear inside of a SPIR-V block.
|
// Performs validation on instructions that appear inside of a SPIR-V block.
|
||||||
|
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "spirv_definition.h"
|
#include "spirv_definition.h"
|
||||||
#include "validate_passes.h"
|
#include "val/Function.h"
|
||||||
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
using libspirv::AssemblyGrammar;
|
using libspirv::AssemblyGrammar;
|
||||||
using libspirv::DiagnosticStream;
|
using libspirv::DiagnosticStream;
|
||||||
|
@ -26,14 +26,16 @@
|
|||||||
|
|
||||||
// Source code for logical layout validation as described in section 2.4
|
// Source code for logical layout validation as described in section 2.4
|
||||||
|
|
||||||
#include "spirv-tools/libspirv.h"
|
#include "validate.h"
|
||||||
#include "validate_passes.h"
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "operand.h"
|
#include "operand.h"
|
||||||
|
#include "spirv-tools/libspirv.h"
|
||||||
#include <cassert>
|
#include "val/Function.h"
|
||||||
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
using libspirv::ValidationState_t;
|
using libspirv::ValidationState_t;
|
||||||
using libspirv::kLayoutMemoryModel;
|
using libspirv::kLayoutMemoryModel;
|
||||||
@ -42,7 +44,6 @@ using libspirv::kLayoutFunctionDefinitions;
|
|||||||
using libspirv::FunctionDecl;
|
using libspirv::FunctionDecl;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Module scoped instructions are processed by determining if the opcode
|
// 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
|
// is part of the current layout section. If it is not then the next sections is
|
||||||
// checked.
|
// checked.
|
||||||
@ -86,11 +87,11 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
|
|||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
<< "Cannot declare a function in a function body";
|
<< "Cannot declare a function in a function body";
|
||||||
}
|
}
|
||||||
auto control_mask = static_cast<SpvFunctionControlMask>(inst->words[inst->operands[2].offset]);
|
auto control_mask = static_cast<SpvFunctionControlMask>(
|
||||||
spvCheckReturn(_.RegisterFunction(
|
inst->words[inst->operands[2].offset]);
|
||||||
inst->result_id, inst->type_id,
|
spvCheckReturn(
|
||||||
control_mask,
|
_.RegisterFunction(inst->result_id, inst->type_id, control_mask,
|
||||||
inst->words[inst->operands[3].offset]));
|
inst->words[inst->operands[3].offset]));
|
||||||
if (_.getLayoutSection() == kLayoutFunctionDefinitions)
|
if (_.getLayoutSection() == kLayoutFunctionDefinitions)
|
||||||
spvCheckReturn(_.get_current_function().RegisterSetFunctionDeclType(
|
spvCheckReturn(_.get_current_function().RegisterSetFunctionDeclType(
|
||||||
FunctionDecl::kFunctionDeclDefinition));
|
FunctionDecl::kFunctionDeclDefinition));
|
||||||
@ -104,7 +105,8 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
|
|||||||
}
|
}
|
||||||
if (_.get_current_function().get_block_count() != 0) {
|
if (_.get_current_function().get_block_count() != 0) {
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
<< "Function parameters must only appear immediately after the "
|
<< "Function parameters must only appear immediately after "
|
||||||
|
"the "
|
||||||
"function definition";
|
"function definition";
|
||||||
}
|
}
|
||||||
spvCheckReturn(_.get_current_function().RegisterFunctionParameter(
|
spvCheckReturn(_.get_current_function().RegisterFunctionParameter(
|
||||||
@ -128,7 +130,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
|
|||||||
}
|
}
|
||||||
if (_.getLayoutSection() == kLayoutFunctionDeclarations) {
|
if (_.getLayoutSection() == kLayoutFunctionDeclarations) {
|
||||||
spvCheckReturn(_.get_current_function().RegisterSetFunctionDeclType(
|
spvCheckReturn(_.get_current_function().RegisterSetFunctionDeclType(
|
||||||
FunctionDecl::kFunctionDeclDeclaration));
|
FunctionDecl::kFunctionDeclDeclaration));
|
||||||
}
|
}
|
||||||
spvCheckReturn(_.RegisterFunctionEnd());
|
spvCheckReturn(_.RegisterFunctionEnd());
|
||||||
break;
|
break;
|
||||||
@ -174,7 +176,7 @@ spv_result_t FunctionScopedInstructions(ValidationState_t& _,
|
|||||||
}
|
}
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
} /// namespace
|
||||||
|
|
||||||
namespace libspirv {
|
namespace libspirv {
|
||||||
// TODO(umar): Check linkage capabilities for function declarations
|
// TODO(umar): Check linkage capabilities for function declarations
|
||||||
@ -205,4 +207,4 @@ spv_result_t ModuleLayoutPass(ValidationState_t& _,
|
|||||||
} // switch(getLayoutSection())
|
} // switch(getLayoutSection())
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
} /// namespace libspirv
|
||||||
|
@ -24,9 +24,12 @@
|
|||||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||||
|
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "opcode.h"
|
#include "opcode.h"
|
||||||
#include "validate_passes.h"
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
using std::function;
|
using std::function;
|
||||||
using libspirv::ValidationState_t;
|
using libspirv::ValidationState_t;
|
||||||
|
@ -31,15 +31,15 @@
|
|||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
#include <source/assembly_grammar.h>
|
#include "source/assembly_grammar.h"
|
||||||
#include <source/binary.h>
|
#include "source/binary.h"
|
||||||
#include <source/diagnostic.h>
|
#include "source/diagnostic.h"
|
||||||
#include <source/opcode.h>
|
#include "source/opcode.h"
|
||||||
#include <source/spirv_endian.h>
|
#include "source/spirv_endian.h"
|
||||||
#include <source/text.h>
|
#include "source/text.h"
|
||||||
#include <source/text_handler.h>
|
#include "source/text_handler.h"
|
||||||
#include <source/validate.h>
|
#include "source/validate.h"
|
||||||
#include <spirv-tools/libspirv.h>
|
#include "spirv-tools/libspirv.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
@ -27,12 +27,15 @@
|
|||||||
|
|
||||||
// Unit tests for ValidationState_t.
|
// Unit tests for ValidationState_t.
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include "val/ValidationState.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
#include "spirv/spirv.h"
|
#include "spirv/spirv.h"
|
||||||
|
#include "val/Construct.h"
|
||||||
#include "source/validate.h"
|
#include "val/Function.h"
|
||||||
|
#include "validate.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using libspirv::ValidationState_t;
|
using libspirv::ValidationState_t;
|
||||||
|
Loading…
Reference in New Issue
Block a user