Track uses and defs during parsing.

Replace two other, imperfect mechanisms for use-def tracking.

Use ValidationState_t::entry_points to track entry points.

Concentrate undefined-ID diagnostics in a single place.

Move validate_types.h content into validate.h due to increased
inter-dependency.

Track uses of all IDs: TYPE_ID, SCOPE_ID, ...

Also update some blurbs.

Fix entry-point accumulation and move it outside ProcessIds().

Remove validate_types.h from CMakeLists.txt.

Blurb for spvIsIdType.

Remove redundant diagnostics for undefined IDs.

Join "can not" and reformat.
This commit is contained in:
Dejan Mircevski 2016-01-15 11:25:11 -05:00 committed by David Neto
parent 383c83729e
commit 961f5dc544
14 changed files with 878 additions and 1119 deletions

View File

@ -121,7 +121,6 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/source/text.h
${CMAKE_CURRENT_SOURCE_DIR}/source/text_handler.h
${CMAKE_CURRENT_SOURCE_DIR}/source/validate.h
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_types.h
${CMAKE_CURRENT_SOURCE_DIR}/source/assembly_grammar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/source/binary.cpp
${CMAKE_CURRENT_SOURCE_DIR}/source/disassemble.cpp

View File

@ -1367,3 +1367,17 @@ spv_operand_pattern_t spvAlternatePatternFollowingImmediate(
// No result-id found, so just expect CIVs.
return {SPV_OPERAND_TYPE_OPTIONAL_CIV};
}
bool spvIsIdType(spv_operand_type_t type) {
switch (type) {
case SPV_OPERAND_TYPE_ID:
case SPV_OPERAND_TYPE_TYPE_ID:
case SPV_OPERAND_TYPE_RESULT_ID:
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
case SPV_OPERAND_TYPE_SCOPE_ID:
return true;
default:
return false;
}
return false;
}

View File

@ -125,4 +125,7 @@ spv_operand_type_t spvTakeFirstMatchableOperand(spv_operand_pattern_t* pattern);
spv_operand_pattern_t spvAlternatePatternFollowingImmediate(
const spv_operand_pattern_t& pattern);
// Is the operand an ID?
bool spvIsIdType(spv_operand_type_t type);
#endif // LIBSPIRV_OPERAND_H_

View File

@ -25,7 +25,6 @@
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include "validate.h"
#include "validate_types.h"
#include "validate_passes.h"
#include "binary.h"
@ -206,81 +205,20 @@ spv_result_t spvValidateBasic(const spv_instruction_t* pInsts,
}
#endif
spv_result_t spvValidateIDs(const spv_instruction_t* pInsts,
const uint64_t count, const uint32_t bound,
const spv_opcode_table opcodeTable,
const spv_operand_table operandTable,
const spv_ext_inst_table extInstTable,
spv_position position,
spv_diagnostic* pDiagnostic) {
std::vector<spv_id_info_t> idUses;
std::vector<spv_id_info_t> idDefs;
for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
const uint32_t* words = pInsts[instIndex].words.data();
SpvOp opcode;
spvOpcodeSplit(words[0], nullptr, &opcode);
spv_opcode_desc opcodeEntry = nullptr;
if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
return SPV_ERROR_INVALID_BINARY;
}
spv_operand_desc operandEntry = nullptr;
position->index++; // NOTE: Account for Opcode word
for (uint16_t index = 1; index < pInsts[instIndex].words.size();
++index, position->index++) {
const uint32_t word = words[index];
spv_operand_type_t type = spvBinaryOperandInfo(
word, index, opcodeEntry, operandTable, &operandEntry);
if (SPV_OPERAND_TYPE_RESULT_ID == type || SPV_OPERAND_TYPE_ID == type) {
if (0 == word) {
DIAGNOSTIC << "Invalid ID of '0' is not allowed.";
return SPV_ERROR_INVALID_ID;
}
if (bound < word) {
DIAGNOSTIC << "Invalid ID '" << word << "' exceeds the bound '"
<< bound << "'.";
return SPV_ERROR_INVALID_ID;
}
}
if (SPV_OPERAND_TYPE_RESULT_ID == type) {
idDefs.push_back(
{word, opcodeEntry->opcode, &pInsts[instIndex], *position});
}
if (SPV_OPERAND_TYPE_ID == type) {
idUses.push_back({word, opcodeEntry->opcode, nullptr, *position});
}
}
spv_result_t spvValidateIDs(
const spv_instruction_t* pInsts, const uint64_t count,
const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
const spv_ext_inst_table extInstTable, const ValidationState_t& state,
spv_position position, spv_diagnostic* pDiagnostic) {
auto undefd = state.usedefs().FindUsesWithoutDefs();
for (auto id : undefd) {
DIAGNOSTIC << "Undefined ID: " << id;
}
// NOTE: Error on redefined ID
for (size_t outerIndex = 0; outerIndex < idDefs.size(); ++outerIndex) {
for (size_t innerIndex = 0; innerIndex < idDefs.size(); ++innerIndex) {
if (outerIndex == innerIndex) {
continue;
}
if (idDefs[outerIndex].id == idDefs[innerIndex].id) {
DIAGNOSTIC << "Multiply defined ID '" << idDefs[outerIndex].id << "'.";
return SPV_ERROR_INVALID_ID;
}
}
}
// NOTE: Validate ID usage, including use of undefined ID's
position->index = SPV_INDEX_INSTRUCTION;
if (spvValidateInstructionIDs(pInsts, count, idUses.data(), idUses.size(),
idDefs.data(), idDefs.size(), opcodeTable,
operandTable, extInstTable, position,
pDiagnostic))
return SPV_ERROR_INVALID_ID;
return SPV_SUCCESS;
spvCheckReturn(spvValidateInstructionIDs(pInsts, count, opcodeTable,
operandTable, extInstTable, state,
position, pDiagnostic));
return undefd.empty() ? SPV_SUCCESS : SPV_ERROR_INVALID_ID;
}
namespace {
@ -332,14 +270,28 @@ void DebugInstructionPass(ValidationState_t& _,
}
}
// Collects use-def info about an instruction's IDs.
void ProcessIds(ValidationState_t& _, const spv_parsed_instruction_t& inst) {
if (inst.result_id) {
_.usedefs().AddDef(
{inst.result_id, inst.opcode,
std::vector<uint32_t>(inst.words, inst.words + inst.num_words)});
}
for (auto op = inst.operands; op != inst.operands + inst.num_operands; ++op) {
if (spvIsIdType(op->type))
_.usedefs().AddUse(inst.words[op->offset]);
}
}
spv_result_t ProcessInstruction(void* user_data,
const spv_parsed_instruction_t* inst) {
const spv_parsed_instruction_t* inst) {
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
_.incrementInstructionCount();
if (inst->opcode == SpvOpEntryPoint) _.entry_points().push_back(inst->words[2]);
DebugInstructionPass(_, inst);
// TODO(umar): Perform data rules pass
ProcessIds(_, *inst);
spvCheckReturn(ModuleLayoutPass(_, inst));
spvCheckReturn(CfgPass(_, inst));
spvCheckReturn(SsaPass(_, inst));
@ -376,8 +328,8 @@ spv_result_t spvValidate(const spv_const_context context,
ProcessInstruction, pDiagnostic));
// TODO(umar): Add validation checks which require the parsing of the entire
// module. Use the information from the processInstructions pass to make
// the checks.
// module. Use the information from the ProcessInstruction pass to make the
// checks.
if (vstate.unresolvedForwardIdCount() > 0) {
stringstream ss;
@ -408,10 +360,10 @@ spv_result_t spvValidate(const spv_const_context context,
if (spvIsInBitfield(SPV_VALIDATE_ID_BIT, options)) {
position.index = SPV_INDEX_INSTRUCTION;
spvCheckReturn(
spvValidateIDs(instructions.data(), instructions.size(), header.bound,
context->opcode_table, context->operand_table,
context->ext_inst_table, &position, pDiagnostic));
spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(),
context->opcode_table, context->operand_table,
context->ext_inst_table, vstate, &position,
pDiagnostic));
}
return SPV_SUCCESS;

View File

@ -27,42 +27,290 @@
#ifndef LIBSPIRV_VALIDATE_H_
#define LIBSPIRV_VALIDATE_H_
#include "instruction.h"
#include <algorithm>
#include <map>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "libspirv/libspirv.h"
#include "binary.h"
#include "diagnostic.h"
#include "instruction.h"
#include "table.h"
// Structures
// Info about a result ID.
typedef struct spv_id_info_t {
// Id value.
uint32_t id;
// Opcode of the instruction defining the id.
SpvOp opcode;
const spv_instruction_t* inst;
spv_position_t position;
// Binary words of the instruction defining the id.
std::vector<uint32_t> words;
} spv_id_info_t;
namespace libspirv {
// This enum represents the sections of a SPIRV module. See section 2.4
// of the SPIRV spec for additional details of the order. The enumerant values
// are in the same order as the vector returned by GetModuleOrder
enum ModuleLayoutSection {
kLayoutCapabilities, // < Section 2.4 #1
kLayoutExtensions, // < Section 2.4 #2
kLayoutExtInstImport, // < Section 2.4 #3
kLayoutMemoryModel, // < Section 2.4 #4
kLayoutEntryPoint, // < Section 2.4 #5
kLayoutExecutionMode, // < Section 2.4 #6
kLayoutDebug1, // < Section 2.4 #7 > 1
kLayoutDebug2, // < Section 2.4 #7 > 2
kLayoutAnnotations, // < Section 2.4 #8
kLayoutTypes, // < Section 2.4 #9
kLayoutFunctionDeclarations, // < Section 2.4 #10
kLayoutFunctionDefinitions // < Section 2.4 #11
};
enum class FunctionDecl {
kFunctionDeclUnknown, // < Unknown function declaration
kFunctionDeclDeclaration, // < Function declaration
kFunctionDeclDefinition // < Function definition
};
class ValidationState_t;
// This class manages all function declaration and definitions in a module. It
// handles the state and id information while parsing a function in the SPIR-V
// binary.
//
// NOTE: This class is designed to be a Structure of Arrays. Therefore each
// member variable is a vector whose elements represent the values for the
// corresponding function in a SPIR-V module. Variables that are not vector
// types are used to manage the state while parsing the function.
class Functions {
public:
Functions(ValidationState_t& module);
// Registers the function in the module. Subsequent instructions will be
// called against this function
spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id,
uint32_t function_control,
uint32_t function_type_id);
// Registers a function parameter in the current function
spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
// Register a function end instruction
spv_result_t RegisterFunctionEnd();
// Sets the declaration type of the current function
spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
// Registers a block in the current function. Subsequent block instructions
// will target this block
// @param id The ID of the label of the block
spv_result_t RegisterBlock(uint32_t id);
// Registers a variable in the current block
spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
SpvStorageClass storage, uint32_t init_id);
spv_result_t RegisterBlockLoopMerge(uint32_t merge_id, uint32_t continue_id,
SpvLoopControlMask control);
spv_result_t RegisterBlockSelectionMerge(uint32_t merge_id,
SpvSelectionControlMask control);
// Registers the end of the block
spv_result_t RegisterBlockEnd();
// Returns the number of blocks in the current function being parsed
size_t get_block_count() const;
// Retuns true if called after a function instruction but before the
// function end instruction
bool in_function_body() const;
// Returns true if called after a label instruction but before a branch
// instruction
bool in_block() const;
libspirv::DiagnosticStream diag(spv_result_t error_code) const;
private:
// Parent module
ValidationState_t& module_;
// Funciton IDs in a module
std::vector<uint32_t> id_;
// OpTypeFunction IDs of each of the id_ functions
std::vector<uint32_t> type_id_;
// The type of declaration of each function
std::vector<FunctionDecl> declaration_type_;
// TODO(umar): Probably needs better abstractions
// The beginning of the block of functions
std::vector<std::vector<uint32_t>> block_ids_;
// The variable IDs of the functions
std::vector<std::vector<uint32_t>> variable_ids_;
// The function parameter ids of the functions
std::vector<std::vector<uint32_t>> parameter_ids_;
// NOTE: See correspoding getter functions
bool in_function_;
bool in_block_;
};
class ValidationState_t {
public:
ValidationState_t(spv_diagnostic* diagnostic, uint32_t options);
// 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;
// Returns true if an spv_validate_options_t option is enabled in the
// validation instruction
bool is_enabled(spv_validate_options_t flag) const;
// Increments the instruction count. Used for diagnostic
int incrementInstructionCount();
// Returns the current layout section which is being processed
ModuleLayoutSection getLayoutSection() const;
// Increments the module_layout_order_section_
void progressToNextLayoutSectionOrder();
// Determines if the op instruction is part of the current section
bool isOpcodeInCurrentLayoutSection(SpvOp op);
libspirv::DiagnosticStream diag(spv_result_t error_code) const;
// Returns the function states
Functions& get_functions();
// Retuns true if the called after a function instruction but before the
// function end instruction
bool in_function_body() const;
// Returns true if called after a label instruction but before a branch
// instruction
bool in_block() const;
// Keeps track of ID definitions and uses.
class UseDefTracker {
public:
void AddDef(const spv_id_info_t& def) { defs_.push_back(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 {
auto found =
std::find_if(defs_.cbegin(), defs_.cend(),
[id](const spv_id_info_t& e) { return e.id == id; });
if (found == defs_.cend()) {
return std::make_pair(false, spv_id_info_t{});
} else {
return std::make_pair(true, *found);
}
}
// Returns uses of IDs lacking defs.
std::unordered_set<uint32_t> FindUsesWithoutDefs() const {
auto diff = uses_;
for (const auto d : defs_) diff.erase(d.id);
return diff;
}
private:
std::unordered_set<uint32_t> uses_;
std::vector<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_; }
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_;
// Validation options to determine the passes to execute
uint32_t validation_flags_;
std::map<uint32_t, std::string> operand_names_;
// The section of the code being processed
ModuleLayoutSection current_layout_section_;
Functions module_functions_;
std::vector<SpvCapability> module_capabilities_;
// Definitions and uses of all the IDs in the module.
UseDefTracker usedefs_;
// IDs that are entry points, ie, arguments to OpEntryPoint.
std::vector<uint32_t> entry_points_;
};
} // namespace libspirv
// Functions
/// @brief Validate the ID usage of the instruction stream
///
/// @param[in] pInsts stream of instructions
/// @param[in] instCount number of instructions
/// @param[in] pIdUses stream of ID uses
/// @param[in] idUsesCount number of ID uses
/// @param[in] pIdDefs stream of ID uses
/// @param[in] idDefsCount number of ID uses
/// @param[in] opcodeTable table of specified Opcodes
/// @param[in] operandTable table of specified operands
/// @param[in] usedefs use-def info from module parsing
/// @param[in,out] position current position in the stream
/// @param[out] pDiag contains diagnostic on failure
///
/// @return result code
spv_result_t spvValidateInstructionIDs(
const spv_instruction_t* pInsts, const uint64_t instCount,
const spv_id_info_t* pIdUses, const uint64_t idUsesCount,
const spv_id_info_t* pIdDefs, const uint64_t idDefsCount,
const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
const spv_ext_inst_table extInstTable, spv_position position,
spv_diagnostic* pDiag);
spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts,
const uint64_t instCount,
const spv_opcode_table opcodeTable,
const spv_operand_table operandTable,
const spv_ext_inst_table extInstTable,
const libspirv::ValidationState_t& state,
spv_position position,
spv_diagnostic* pDiag);
/// @brief Validate the ID's within a SPIR-V binary
///
@ -82,4 +330,7 @@ spv_result_t spvValidateIDs(const spv_instruction_t* pInstructions,
const spv_ext_inst_table extInstTable,
spv_position position, spv_diagnostic* pDiagnostic);
#define spvCheckReturn(expression) \
if (spv_result_t error = (expression)) return error;
#endif // LIBSPIRV_VALIDATE_H_

View File

@ -25,7 +25,6 @@
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include "validate_passes.h"
#include "validate_types.h"
namespace libspirv {

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,6 @@
// Performs validation on instructions that appear inside of a SPIR-V block.
#include "validate_passes.h"
#include "validate_types.h"
namespace libspirv {

View File

@ -26,7 +26,6 @@
// Source code for logical layout validation as described in section 2.4
#include "validate_types.h"
#include "validate_passes.h"
#include "libspirv/libspirv.h"

View File

@ -28,7 +28,7 @@
#define LIBSPIRV_VALIDATE_PASSES_H_
#include "binary.h"
#include "validate_types.h"
#include "validate.h"
namespace libspirv
{

View File

@ -112,7 +112,7 @@ spv_result_t SsaPass(ValidationState_t& _,
switch (type) {
case SPV_OPERAND_TYPE_RESULT_ID:
_.removeIfForwardDeclared(*operand_ptr);
ret = _.defineId(*operand_ptr);
ret = SPV_SUCCESS;
break;
case SPV_OPERAND_TYPE_ID:
case SPV_OPERAND_TYPE_TYPE_ID:

View File

@ -24,9 +24,6 @@
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include "headers/spirv.h"
#include "validate_types.h"
#include <algorithm>
#include <cassert>
#include <map>
@ -34,6 +31,10 @@
#include <unordered_set>
#include <vector>
#include "headers/spirv.h"
#include "validate.h"
using std::find;
using std::string;
using std::unordered_set;
@ -210,22 +211,12 @@ ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic,
uint32_t options)
: diagnostic_(diagnostic),
instruction_counter_(0),
defined_ids_{},
unresolved_forward_ids_{},
validation_flags_(options),
operand_names_{},
current_layout_stage_(kLayoutCapabilities),
current_layout_section_(kLayoutCapabilities),
module_functions_(*this) {}
spv_result_t ValidationState_t::defineId(uint32_t id) {
if (defined_ids_.find(id) == end(defined_ids_)) {
defined_ids_.insert(id);
} else {
return diag(SPV_ERROR_INVALID_ID) << "ID cannot be assigned multiple times";
}
return SPV_SUCCESS;
}
spv_result_t ValidationState_t::forwardDeclareId(uint32_t id) {
unresolved_forward_ids_.insert(id);
return SPV_SUCCESS;
@ -260,7 +251,7 @@ vector<uint32_t> ValidationState_t::unresolvedForwardIds() const {
}
bool ValidationState_t::isDefinedId(uint32_t id) const {
return defined_ids_.find(id) != end(defined_ids_);
return usedefs_.FindDef(id).first;
}
bool ValidationState_t::is_enabled(spv_validate_options_t flag) const {
@ -273,19 +264,19 @@ int ValidationState_t::incrementInstructionCount() {
}
ModuleLayoutSection ValidationState_t::getLayoutSection() const {
return current_layout_stage_;
return current_layout_section_;
}
void ValidationState_t::progressToNextLayoutSectionOrder() {
// Guard against going past the last element(kLayoutFunctionDefinitions)
if (current_layout_stage_ <= kLayoutFunctionDefinitions) {
current_layout_stage_ =
static_cast<ModuleLayoutSection>(current_layout_stage_ + 1);
if (current_layout_section_ <= kLayoutFunctionDefinitions) {
current_layout_section_ =
static_cast<ModuleLayoutSection>(current_layout_section_ + 1);
}
}
bool ValidationState_t::isOpcodeInCurrentLayoutSection(SpvOp op) {
return IsInstructionInLayoutSection(current_layout_stage_, op);
return IsInstructionInLayoutSection(current_layout_section_, op);
}
DiagnosticStream ValidationState_t::diag(spv_result_t error_code) const {

View File

@ -1,241 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#ifndef LIBSPIRV_VALIDATE_TYPES_H_
#define LIBSPIRV_VALIDATE_TYPES_H_
#include "binary.h"
#include "diagnostic.h"
#include "libspirv/libspirv.h"
#include <map>
#include <string>
#include <unordered_set>
#include <vector>
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:
Functions(ValidationState_t& module);
// Registers the function in the module. Subsequent instructions will be
// called against this function
spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id,
uint32_t function_control,
uint32_t function_type_id);
// Registers a function parameter in the current function
spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
// Register a function end instruction
spv_result_t RegisterFunctionEnd();
// Sets the declaration type of the current function
spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
// Registers a block in the current function. Subsequent block instructions
// will target this block
// @param id The ID of the label of the block
spv_result_t RegisterBlock(uint32_t id);
// Registers a variable in the current block
spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
SpvStorageClass storage, uint32_t init_id);
spv_result_t RegisterBlockLoopMerge(uint32_t merge_id, uint32_t continue_id,
SpvLoopControlMask control);
spv_result_t RegisterBlockSelectionMerge(uint32_t merge_id,
SpvSelectionControlMask control);
// Registers the end of the block
spv_result_t RegisterBlockEnd();
// Returns the number of blocks in the current function being parsed
size_t get_block_count() const;
// Retuns true if called after a function instruction but before the
// function end instruction
bool in_function_body() const;
// Returns true if called after a label instruction but before a branch
// instruction
bool in_block() const;
libspirv::DiagnosticStream diag(spv_result_t error_code) const;
private:
// Parent module
ValidationState_t& module_;
// Funciton IDs in a module
std::vector<uint32_t> id_;
// OpTypeFunction IDs of each of the id_ functions
std::vector<uint32_t> type_id_;
// The type of declaration of each function
std::vector<FunctionDecl> declaration_type_;
// TODO(umar): Probably needs better abstractions
// The beginning of the block of functions
std::vector<std::vector<uint32_t>> block_ids_;
// The variable IDs of the functions
std::vector<std::vector<uint32_t>> variable_ids_;
// The function parameter ids of the functions
std::vector<std::vector<uint32_t>> parameter_ids_;
// NOTE: See correspoding getter functions
bool in_function_;
bool in_block_;
};
class ValidationState_t {
public:
ValidationState_t(spv_diagnostic* diagnostic, uint32_t options);
// Defines the \p id for the module
spv_result_t defineId(uint32_t id);
// 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;
// Returns true if an spv_validate_options_t option is enabled in the
// validation instruction
bool is_enabled(spv_validate_options_t flag) 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_stage_
void progressToNextLayoutSectionOrder();
// Determines if the op instruction is part of the current stage
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;
private:
spv_diagnostic* diagnostic_;
// Tracks the number of instructions evaluated by the validator
int instruction_counter_;
// All IDs which have been defined
std::unordered_set<uint32_t> defined_ids_;
// IDs which have been forward declared but have not been defined
std::unordered_set<uint32_t> unresolved_forward_ids_;
// Validation options to determine the passes to execute
uint32_t validation_flags_;
std::map<uint32_t, std::string> operand_names_;
// The section of the code being processed
ModuleLayoutSection current_layout_stage_;
Functions module_functions_;
std::vector<SpvCapability> module_capabilities_;
};
}
#define spvCheckReturn(expression) \
if (spv_result_t error = (expression)) return error;
#endif

View File

@ -229,7 +229,8 @@ TEST_F(ValidateID, OpExecutionModeGood) {
OpFunctionEnd)";
CHECK(spirv, SPV_SUCCESS);
}
TEST_F(ValidateID, OpExecutionModeEntryPointBad) {
TEST_F(ValidateID, OpExecutionModeEntryPointMissing) {
const char* spirv = R"(
OpExecutionMode %3 LocalSize 1 1 1
%1 = OpTypeVoid
@ -241,6 +242,21 @@ TEST_F(ValidateID, OpExecutionModeEntryPointBad) {
CHECK(spirv, SPV_ERROR_INVALID_ID);
}
TEST_F(ValidateID, OpExecutionModeEntryPointBad) {
const char* spirv = R"(
OpEntryPoint GLCompute %3 "" %a
OpExecutionMode %a LocalSize 1 1 1
%void = OpTypeVoid
%ptr = OpTypePointer Input %void
%a = OpVariable %ptr Input
%2 = OpTypeFunction %void
%3 = OpFunction %void None %2
%4 = OpLabel
OpReturn
OpFunctionEnd)";
CHECK(spirv, SPV_ERROR_INVALID_ID);
}
TEST_F(ValidateID, OpTypeVectorGood) {
const char* spirv = R"(
%1 = OpTypeFloat 32
@ -1267,6 +1283,37 @@ TEST_F(ValidateID, OpReturnValueBad) {
CHECK(spirv, SPV_ERROR_INVALID_ID);
}
TEST_F(ValidateID, UndefinedTypeId) {
const char* spirv = R"(
%f32 = OpTypeFloat 32
%atype = OpTypeRuntimeArray %f32
%stype = OpTypeStruct %atype
%ptrtype = OpTypePointer Input %stype
%svar = OpVariable %ptrtype Input
%s = OpLoad %stype %svar
%len = OpArrayLength %undef %s 0
)";
CHECK(spirv, SPV_ERROR_INVALID_ID);
}
TEST_F(ValidateID, UndefinedIdScope) {
const char* spirv = R"(
%u32 = OpTypeInt 32 0
%memsem = OpConstant %u32 0
OpMemoryBarrier %undef %memsem
)";
CHECK(spirv, SPV_ERROR_INVALID_ID);
}
TEST_F(ValidateID, UndefinedIdMemSem) {
const char* spirv = R"(
%u32 = OpTypeInt 32 0
%scope = OpConstant %u32 0
OpMemoryBarrier %scope %undef
)";
CHECK(spirv, SPV_ERROR_INVALID_ID);
}
// TODO: OpLifetimeStart
// TODO: OpLifetimeStop
// TODO: OpAtomicInit