mirror of
https://gitee.com/openharmony/third_party_spirv-tools
synced 2024-11-23 07:20:28 +00:00
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:
parent
383c83729e
commit
961f5dc544
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
|
@ -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_
|
||||
|
@ -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
@ -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 {
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
#define LIBSPIRV_VALIDATE_PASSES_H_
|
||||
|
||||
#include "binary.h"
|
||||
#include "validate_types.h"
|
||||
#include "validate.h"
|
||||
|
||||
namespace libspirv
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
@ -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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user