2016-01-07 18:44:22 +00:00
|
|
|
// Copyright (c) 2015-2016 The Khronos Group Inc.
|
2015-05-22 17:26:19 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2015-10-27 20:27:05 +00:00
|
|
|
#ifndef LIBSPIRV_VALIDATE_H_
|
|
|
|
#define LIBSPIRV_VALIDATE_H_
|
2015-05-22 17:26:19 +00:00
|
|
|
|
2016-01-15 16:25:11 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
2016-02-18 19:38:12 +00:00
|
|
|
#include <unordered_map>
|
2016-01-15 16:25:11 +00:00
|
|
|
#include <unordered_set>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
2016-01-31 04:32:09 +00:00
|
|
|
#include "assembly_grammar.h"
|
2016-01-15 16:25:11 +00:00
|
|
|
#include "binary.h"
|
|
|
|
#include "diagnostic.h"
|
|
|
|
#include "instruction.h"
|
2016-02-17 19:44:00 +00:00
|
|
|
#include "spirv-tools/libspirv.h"
|
2016-01-31 04:32:09 +00:00
|
|
|
#include "spirv_definition.h"
|
2015-11-11 17:14:36 +00:00
|
|
|
#include "table.h"
|
2015-05-22 17:26:19 +00:00
|
|
|
|
|
|
|
// Structures
|
|
|
|
|
2016-01-15 16:25:11 +00:00
|
|
|
// Info about a result ID.
|
2015-05-22 17:26:19 +00:00
|
|
|
typedef struct spv_id_info_t {
|
2016-01-15 16:25:11 +00:00
|
|
|
// Id value.
|
2015-05-22 17:26:19 +00:00
|
|
|
uint32_t id;
|
2016-01-22 19:27:00 +00:00
|
|
|
// Type id, or 0 if no type.
|
|
|
|
uint32_t type_id;
|
2016-01-15 16:25:11 +00:00
|
|
|
// Opcode of the instruction defining the id.
|
2015-10-28 17:40:52 +00:00
|
|
|
SpvOp opcode;
|
2016-01-15 16:25:11 +00:00
|
|
|
// Binary words of the instruction defining the id.
|
|
|
|
std::vector<uint32_t> words;
|
2015-05-22 17:26:19 +00:00
|
|
|
} spv_id_info_t;
|
|
|
|
|
2016-01-15 16:25:11 +00:00
|
|
|
namespace libspirv {
|
|
|
|
|
|
|
|
// This enum represents the sections of a SPIRV module. See section 2.4
|
|
|
|
// of the SPIRV spec for additional details of the order. The enumerant values
|
|
|
|
// are in the same order as the vector returned by GetModuleOrder
|
|
|
|
enum ModuleLayoutSection {
|
|
|
|
kLayoutCapabilities, // < Section 2.4 #1
|
|
|
|
kLayoutExtensions, // < Section 2.4 #2
|
|
|
|
kLayoutExtInstImport, // < Section 2.4 #3
|
|
|
|
kLayoutMemoryModel, // < Section 2.4 #4
|
|
|
|
kLayoutEntryPoint, // < Section 2.4 #5
|
|
|
|
kLayoutExecutionMode, // < Section 2.4 #6
|
|
|
|
kLayoutDebug1, // < Section 2.4 #7 > 1
|
|
|
|
kLayoutDebug2, // < Section 2.4 #7 > 2
|
|
|
|
kLayoutAnnotations, // < Section 2.4 #8
|
|
|
|
kLayoutTypes, // < Section 2.4 #9
|
|
|
|
kLayoutFunctionDeclarations, // < Section 2.4 #10
|
|
|
|
kLayoutFunctionDefinitions // < Section 2.4 #11
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class FunctionDecl {
|
|
|
|
kFunctionDeclUnknown, // < Unknown function declaration
|
|
|
|
kFunctionDeclDeclaration, // < Function declaration
|
|
|
|
kFunctionDeclDefinition // < Function definition
|
|
|
|
};
|
|
|
|
|
|
|
|
class ValidationState_t;
|
|
|
|
|
|
|
|
// This class manages all function declaration and definitions in a module. It
|
|
|
|
// handles the state and id information while parsing a function in the SPIR-V
|
|
|
|
// binary.
|
|
|
|
//
|
|
|
|
// NOTE: This class is designed to be a Structure of Arrays. Therefore each
|
|
|
|
// member variable is a vector whose elements represent the values for the
|
|
|
|
// corresponding function in a SPIR-V module. Variables that are not vector
|
|
|
|
// types are used to manage the state while parsing the function.
|
|
|
|
class Functions {
|
|
|
|
public:
|
2016-02-25 21:11:16 +00:00
|
|
|
explicit Functions(ValidationState_t& module);
|
2016-01-15 16:25:11 +00:00
|
|
|
|
|
|
|
// Registers the function in the module. Subsequent instructions will be
|
|
|
|
// called against this function
|
|
|
|
spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id,
|
|
|
|
uint32_t function_control,
|
|
|
|
uint32_t function_type_id);
|
|
|
|
|
|
|
|
// Registers a function parameter in the current function
|
|
|
|
spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
|
|
|
|
|
|
|
|
// Register a function end instruction
|
|
|
|
spv_result_t RegisterFunctionEnd();
|
|
|
|
|
|
|
|
// Sets the declaration type of the current function
|
|
|
|
spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
|
|
|
|
|
|
|
|
// Registers a block in the current function. Subsequent block instructions
|
|
|
|
// will target this block
|
|
|
|
// @param id The ID of the label of the block
|
|
|
|
spv_result_t RegisterBlock(uint32_t id);
|
|
|
|
|
|
|
|
// Registers a variable in the current block
|
|
|
|
spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
|
|
|
|
SpvStorageClass storage, uint32_t init_id);
|
|
|
|
|
|
|
|
spv_result_t RegisterBlockLoopMerge(uint32_t merge_id, uint32_t continue_id,
|
|
|
|
SpvLoopControlMask control);
|
|
|
|
|
|
|
|
spv_result_t RegisterBlockSelectionMerge(uint32_t merge_id,
|
|
|
|
SpvSelectionControlMask control);
|
|
|
|
|
|
|
|
// Registers the end of the block
|
|
|
|
spv_result_t RegisterBlockEnd();
|
|
|
|
|
|
|
|
// Returns the number of blocks in the current function being parsed
|
|
|
|
size_t get_block_count() const;
|
|
|
|
|
|
|
|
// Retuns true if called after a function instruction but before the
|
|
|
|
// function end instruction
|
|
|
|
bool in_function_body() const;
|
|
|
|
|
|
|
|
// Returns true if called after a label instruction but before a branch
|
|
|
|
// instruction
|
|
|
|
bool in_block() const;
|
|
|
|
|
|
|
|
libspirv::DiagnosticStream diag(spv_result_t error_code) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Parent module
|
|
|
|
ValidationState_t& module_;
|
|
|
|
|
|
|
|
// Funciton IDs in a module
|
|
|
|
std::vector<uint32_t> id_;
|
|
|
|
|
|
|
|
// OpTypeFunction IDs of each of the id_ functions
|
|
|
|
std::vector<uint32_t> type_id_;
|
|
|
|
|
|
|
|
// The type of declaration of each function
|
|
|
|
std::vector<FunctionDecl> declaration_type_;
|
|
|
|
|
|
|
|
// TODO(umar): Probably needs better abstractions
|
|
|
|
// The beginning of the block of functions
|
|
|
|
std::vector<std::vector<uint32_t>> block_ids_;
|
|
|
|
|
|
|
|
// The variable IDs of the functions
|
|
|
|
std::vector<std::vector<uint32_t>> variable_ids_;
|
|
|
|
|
|
|
|
// The function parameter ids of the functions
|
|
|
|
std::vector<std::vector<uint32_t>> parameter_ids_;
|
|
|
|
|
|
|
|
// NOTE: See correspoding getter functions
|
|
|
|
bool in_function_;
|
|
|
|
bool in_block_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ValidationState_t {
|
|
|
|
public:
|
2016-02-22 20:25:50 +00:00
|
|
|
ValidationState_t(spv_diagnostic* diagnostic,
|
2016-01-31 04:32:09 +00:00
|
|
|
const spv_const_context context);
|
2016-01-15 16:25:11 +00:00
|
|
|
|
|
|
|
// Forward declares the id in the module
|
|
|
|
spv_result_t forwardDeclareId(uint32_t id);
|
|
|
|
|
|
|
|
// Removes a forward declared ID if it has been defined
|
|
|
|
spv_result_t removeIfForwardDeclared(uint32_t id);
|
|
|
|
|
|
|
|
// Assigns a name to an ID
|
|
|
|
void assignNameToId(uint32_t id, std::string name);
|
|
|
|
|
|
|
|
// Returns a string representation of the ID in the format <id>[Name] where
|
|
|
|
// the <id> is the numeric valid of the id and the Name is a name assigned by
|
|
|
|
// the OpName instruction
|
|
|
|
std::string getIdName(uint32_t id) const;
|
|
|
|
|
|
|
|
// Returns the number of ID which have been forward referenced but not defined
|
|
|
|
size_t unresolvedForwardIdCount() const;
|
|
|
|
|
|
|
|
// Returns a list of unresolved forward ids.
|
|
|
|
std::vector<uint32_t> unresolvedForwardIds() const;
|
|
|
|
|
|
|
|
// Returns true if the id has been defined
|
|
|
|
bool isDefinedId(uint32_t id) const;
|
|
|
|
|
|
|
|
// Increments the instruction count. Used for diagnostic
|
|
|
|
int incrementInstructionCount();
|
|
|
|
|
|
|
|
// Returns the current layout section which is being processed
|
|
|
|
ModuleLayoutSection getLayoutSection() const;
|
|
|
|
|
|
|
|
// Increments the module_layout_order_section_
|
|
|
|
void progressToNextLayoutSectionOrder();
|
|
|
|
|
|
|
|
// Determines if the op instruction is part of the current section
|
|
|
|
bool isOpcodeInCurrentLayoutSection(SpvOp op);
|
|
|
|
|
|
|
|
libspirv::DiagnosticStream diag(spv_result_t error_code) const;
|
|
|
|
|
|
|
|
// Returns the function states
|
|
|
|
Functions& get_functions();
|
|
|
|
|
|
|
|
// Retuns true if the called after a function instruction but before the
|
|
|
|
// function end instruction
|
|
|
|
bool in_function_body() const;
|
|
|
|
|
|
|
|
// Returns true if called after a label instruction but before a branch
|
|
|
|
// instruction
|
|
|
|
bool in_block() const;
|
|
|
|
|
|
|
|
// Keeps track of ID definitions and uses.
|
|
|
|
class UseDefTracker {
|
|
|
|
public:
|
2016-02-18 19:38:12 +00:00
|
|
|
void AddDef(const spv_id_info_t& def) { defs_[def.id] = def; }
|
2016-01-15 16:25:11 +00:00
|
|
|
|
|
|
|
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 {
|
2016-02-18 19:38:12 +00:00
|
|
|
if (defs_.count(id) == 0) {
|
2016-01-15 16:25:11 +00:00
|
|
|
return std::make_pair(false, spv_id_info_t{});
|
|
|
|
} else {
|
2016-02-18 19:38:12 +00:00
|
|
|
// 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));
|
2016-01-15 16:25:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns uses of IDs lacking defs.
|
|
|
|
std::unordered_set<uint32_t> FindUsesWithoutDefs() const {
|
|
|
|
auto diff = uses_;
|
2016-02-18 19:38:12 +00:00
|
|
|
for (const auto d : defs_) diff.erase(d.first);
|
2016-01-15 16:25:11 +00:00
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::unordered_set<uint32_t> uses_;
|
2016-02-18 19:38:12 +00:00
|
|
|
std::unordered_map<uint32_t, spv_id_info_t> defs_;
|
2016-01-15 16:25:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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_; }
|
|
|
|
|
2016-01-23 19:14:32 +00:00
|
|
|
// Registers the capability and its dependent capabilities
|
|
|
|
void registerCapability(SpvCapability cap);
|
|
|
|
|
2016-01-31 04:32:09 +00:00
|
|
|
// Returns true if the capability is enabled in the module.
|
|
|
|
bool hasCapability(SpvCapability cap) const;
|
|
|
|
|
|
|
|
// Returns true if any of the capabilities are enabled. Always true for
|
|
|
|
// capabilities==0.
|
|
|
|
bool HasAnyOf(spv_capability_mask_t capabilities) const;
|
|
|
|
|
|
|
|
AssemblyGrammar& grammar() { return grammar_; }
|
2016-01-23 19:14:32 +00:00
|
|
|
|
2016-01-15 16:25:11 +00:00
|
|
|
private:
|
|
|
|
spv_diagnostic* diagnostic_;
|
|
|
|
// Tracks the number of instructions evaluated by the validator
|
|
|
|
int instruction_counter_;
|
|
|
|
|
|
|
|
// IDs which have been forward declared but have not been defined
|
|
|
|
std::unordered_set<uint32_t> unresolved_forward_ids_;
|
|
|
|
|
|
|
|
std::map<uint32_t, std::string> operand_names_;
|
|
|
|
|
|
|
|
// The section of the code being processed
|
|
|
|
ModuleLayoutSection current_layout_section_;
|
|
|
|
|
|
|
|
Functions module_functions_;
|
|
|
|
|
2016-01-29 17:09:34 +00:00
|
|
|
// We are using vector to map the ID of the capability to its availability.
|
|
|
|
// The size of the vector needs to be the maximum ID plus one to cover the
|
|
|
|
// entire range of the capability.
|
2016-01-23 19:14:32 +00:00
|
|
|
std::vector<bool> module_capabilities_;
|
2016-01-15 16:25:11 +00:00
|
|
|
|
|
|
|
// 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_;
|
2016-01-23 19:14:32 +00:00
|
|
|
|
2016-01-31 04:32:09 +00:00
|
|
|
AssemblyGrammar grammar_;
|
2016-01-15 16:25:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace libspirv
|
|
|
|
|
2015-05-22 17:26:19 +00:00
|
|
|
// Functions
|
|
|
|
|
|
|
|
/// @brief Validate the ID usage of the instruction stream
|
|
|
|
///
|
|
|
|
/// @param[in] pInsts stream of instructions
|
|
|
|
/// @param[in] instCount number of instructions
|
|
|
|
/// @param[in] opcodeTable table of specified Opcodes
|
|
|
|
/// @param[in] operandTable table of specified operands
|
2016-01-15 16:25:11 +00:00
|
|
|
/// @param[in] usedefs use-def info from module parsing
|
2015-05-22 17:26:19 +00:00
|
|
|
/// @param[in,out] position current position in the stream
|
|
|
|
/// @param[out] pDiag contains diagnostic on failure
|
|
|
|
///
|
|
|
|
/// @return result code
|
2016-01-15 16:25:11 +00:00
|
|
|
spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts,
|
|
|
|
const uint64_t instCount,
|
|
|
|
const spv_opcode_table opcodeTable,
|
|
|
|
const spv_operand_table operandTable,
|
|
|
|
const spv_ext_inst_table extInstTable,
|
|
|
|
const libspirv::ValidationState_t& state,
|
|
|
|
spv_position position,
|
|
|
|
spv_diagnostic* pDiag);
|
2015-05-22 17:26:19 +00:00
|
|
|
|
|
|
|
/// @brief Validate the ID's within a SPIR-V binary
|
|
|
|
///
|
|
|
|
/// @param[in] pInstructions array of instructions
|
|
|
|
/// @param[in] count number of elements in instruction array
|
|
|
|
/// @param[in] bound the binary header
|
|
|
|
/// @param[in] opcodeTable table of specified Opcodes
|
|
|
|
/// @param[in] operandTable table of specified operands
|
|
|
|
/// @param[in,out] position current word in the binary
|
|
|
|
/// @param[out] pDiagnostic contains diagnostic on failure
|
|
|
|
///
|
|
|
|
/// @return result code
|
2015-11-02 14:41:20 +00:00
|
|
|
spv_result_t spvValidateIDs(const spv_instruction_t* pInstructions,
|
2015-05-22 17:26:19 +00:00
|
|
|
const uint64_t count, const uint32_t bound,
|
|
|
|
const spv_opcode_table opcodeTable,
|
|
|
|
const spv_operand_table operandTable,
|
|
|
|
const spv_ext_inst_table extInstTable,
|
2015-11-02 14:41:20 +00:00
|
|
|
spv_position position, spv_diagnostic* pDiagnostic);
|
2015-05-22 17:26:19 +00:00
|
|
|
|
2016-01-31 04:32:09 +00:00
|
|
|
#define spvCheckReturn(expression) \
|
2016-01-15 16:25:11 +00:00
|
|
|
if (spv_result_t error = (expression)) return error;
|
|
|
|
|
2015-10-27 20:27:05 +00:00
|
|
|
#endif // LIBSPIRV_VALIDATE_H_
|