mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 06:50:06 +00:00
Validation refactor
* Organize passes into seperate files * Remove module layout logic from Cfg pass * Remove module layout logic from Functions class * Refactor ModuleLayoutPass for readability * Adapt consistent naming of layout sections (Stage/Section -> Section)
This commit is contained in:
parent
866b6ab9da
commit
1ddeb246eb
@ -135,8 +135,12 @@ set(SPIRV_SOURCES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/text.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/source/text.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/text_handler.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/source/text_handler.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/validate.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/source/validate.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_types.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_cfg.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_id.cpp)
|
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_id.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_instruction.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_layout.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_ssa.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_types.cpp)
|
||||||
|
|
||||||
add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
|
add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
|
||||||
default_compile_options(${SPIRV_TOOLS})
|
default_compile_options(${SPIRV_TOOLS})
|
||||||
|
@ -260,9 +260,10 @@ typedef enum spv_validate_options_t {
|
|||||||
SPV_VALIDATE_ID_BIT = SPV_BIT(2),
|
SPV_VALIDATE_ID_BIT = SPV_BIT(2),
|
||||||
SPV_VALIDATE_RULES_BIT = SPV_BIT(3),
|
SPV_VALIDATE_RULES_BIT = SPV_BIT(3),
|
||||||
SPV_VALIDATE_SSA_BIT = SPV_BIT(4),
|
SPV_VALIDATE_SSA_BIT = SPV_BIT(4),
|
||||||
|
SPV_VALIDATE_INSTRUCTION_BIT = SPV_BIT(5),
|
||||||
SPV_VALIDATE_ALL = SPV_VALIDATE_BASIC_BIT | SPV_VALIDATE_LAYOUT_BIT |
|
SPV_VALIDATE_ALL = SPV_VALIDATE_BASIC_BIT | SPV_VALIDATE_LAYOUT_BIT |
|
||||||
SPV_VALIDATE_ID_BIT | SPV_VALIDATE_RULES_BIT |
|
SPV_VALIDATE_ID_BIT | SPV_VALIDATE_RULES_BIT |
|
||||||
SPV_VALIDATE_SSA_BIT,
|
SPV_VALIDATE_SSA_BIT | SPV_VALIDATE_INSTRUCTION_BIT ,
|
||||||
SPV_FORCE_32_BIT_ENUM(spv_validation_options_t)
|
SPV_FORCE_32_BIT_ENUM(spv_validation_options_t)
|
||||||
} spv_validate_options_t;
|
} spv_validate_options_t;
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "validate.h"
|
#include "validate.h"
|
||||||
#include "validate_types.h"
|
#include "validate_types.h"
|
||||||
|
#include "validate_passes.h"
|
||||||
|
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
@ -41,30 +42,23 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using std::function;
|
using std::function;
|
||||||
using std::map;
|
|
||||||
using std::ostream_iterator;
|
using std::ostream_iterator;
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::stringstream;
|
using std::stringstream;
|
||||||
using std::transform;
|
using std::transform;
|
||||||
using std::unordered_set;
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
|
using libspirv::CfgPass;
|
||||||
|
using libspirv::InstructionPass;
|
||||||
|
using libspirv::ModuleLayoutPass;
|
||||||
|
using libspirv::SsaPass;
|
||||||
using libspirv::ValidationState_t;
|
using libspirv::ValidationState_t;
|
||||||
using libspirv::kLayoutFunctionDeclarations;
|
|
||||||
using libspirv::kLayoutFunctionDefinitions;
|
|
||||||
using libspirv::kLayoutMemoryModel;
|
|
||||||
using libspirv::FunctionDecl;
|
|
||||||
|
|
||||||
#define spvCheckReturn(expression) \
|
|
||||||
if (spv_result_t error = (expression)) return error;
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
spv_result_t spvValidateOperandsString(const uint32_t* words,
|
spv_result_t spvValidateOperandsString(const uint32_t* words,
|
||||||
@ -309,108 +303,6 @@ spv_result_t setHeader(void* user_data, spv_endianness_t endian, uint32_t magic,
|
|||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs SSA validation on the IDs of an instruction. The
|
|
||||||
// can_have_forward_declared_ids functor should return true if the
|
|
||||||
// instruction operand's ID can be forward referenced.
|
|
||||||
//
|
|
||||||
// TODO(umar): Use dominators to correctly validate SSA. For example, the result
|
|
||||||
// id from a 'then' block cannot dominate its usage in the 'else' block. This
|
|
||||||
// is not yet performed by this funciton.
|
|
||||||
spv_result_t SsaPass(ValidationState_t& _,
|
|
||||||
function<bool(unsigned)> can_have_forward_declared_ids,
|
|
||||||
const spv_parsed_instruction_t* inst) {
|
|
||||||
if (_.is_enabled(SPV_VALIDATE_SSA_BIT)) {
|
|
||||||
for (unsigned i = 0; i < inst->num_operands; i++) {
|
|
||||||
const spv_parsed_operand_t& operand = inst->operands[i];
|
|
||||||
const spv_operand_type_t& type = operand.type;
|
|
||||||
const uint32_t* operand_ptr = inst->words + operand.offset;
|
|
||||||
|
|
||||||
auto ret = SPV_ERROR_INTERNAL;
|
|
||||||
switch (type) {
|
|
||||||
case SPV_OPERAND_TYPE_RESULT_ID:
|
|
||||||
_.removeIfForwardDeclared(*operand_ptr);
|
|
||||||
ret = _.defineId(*operand_ptr);
|
|
||||||
break;
|
|
||||||
case SPV_OPERAND_TYPE_ID:
|
|
||||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
|
||||||
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
|
|
||||||
case SPV_OPERAND_TYPE_SCOPE_ID:
|
|
||||||
if (_.isDefinedId(*operand_ptr)) {
|
|
||||||
ret = SPV_SUCCESS;
|
|
||||||
} else if (can_have_forward_declared_ids(i)) {
|
|
||||||
ret = _.forwardDeclareId(*operand_ptr);
|
|
||||||
} else {
|
|
||||||
ret = _.diag(SPV_ERROR_INVALID_ID) << "ID "
|
|
||||||
<< _.getIdName(*operand_ptr)
|
|
||||||
<< " has not been defined";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = SPV_SUCCESS;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (SPV_SUCCESS != ret) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SPV_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This funciton takes the opcode of an instruction and returns
|
|
||||||
// a function object that will return true if the index
|
|
||||||
// of the operand can be forwarad declared. This function will
|
|
||||||
// used in the SSA validation stage of the pipeline
|
|
||||||
function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) {
|
|
||||||
function<bool(unsigned index)> out;
|
|
||||||
switch (opcode) {
|
|
||||||
case SpvOpExecutionMode:
|
|
||||||
case SpvOpEntryPoint:
|
|
||||||
case SpvOpName:
|
|
||||||
case SpvOpMemberName:
|
|
||||||
case SpvOpSelectionMerge:
|
|
||||||
case SpvOpDecorate:
|
|
||||||
case SpvOpMemberDecorate:
|
|
||||||
case SpvOpBranch:
|
|
||||||
case SpvOpLoopMerge:
|
|
||||||
out = [](unsigned) { return true; };
|
|
||||||
break;
|
|
||||||
case SpvOpGroupDecorate:
|
|
||||||
case SpvOpGroupMemberDecorate:
|
|
||||||
case SpvOpBranchConditional:
|
|
||||||
case SpvOpSwitch:
|
|
||||||
out = [](unsigned index) { return index != 0; };
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SpvOpFunctionCall:
|
|
||||||
out = [](unsigned index) { return index == 2; };
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SpvOpPhi:
|
|
||||||
out = [](unsigned index) { return index > 1; };
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SpvOpEnqueueKernel:
|
|
||||||
out = [](unsigned index) { return index == 8; };
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SpvOpGetKernelNDrangeSubGroupCount:
|
|
||||||
case SpvOpGetKernelNDrangeMaxSubGroupSize:
|
|
||||||
out = [](unsigned index) { return index == 3; };
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SpvOpGetKernelWorkGroupSize:
|
|
||||||
case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
|
|
||||||
out = [](unsigned index) { return index == 2; };
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
out = [](unsigned) { return false; };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Improves diagnostic messages by collecting names of IDs
|
// Improves diagnostic messages by collecting names of IDs
|
||||||
// NOTE: This function returns void and is not involved in validation
|
// NOTE: This function returns void and is not involved in validation
|
||||||
void DebugInstructionPass(ValidationState_t& _,
|
void DebugInstructionPass(ValidationState_t& _,
|
||||||
@ -440,182 +332,18 @@ void DebugInstructionPass(ValidationState_t& _,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(umar): Check linkage capabilities for function declarations
|
spv_result_t ProcessInstruction(void* user_data,
|
||||||
// TODO(umar): Better error messages
|
|
||||||
// NOTE: This function does not handle CFG related validation
|
|
||||||
// Performs logical layout validation. See Section 2.4
|
|
||||||
spv_result_t ModuleLayoutPass(ValidationState_t& _,
|
|
||||||
const spv_parsed_instruction_t* inst) {
|
|
||||||
if (_.is_enabled(SPV_VALIDATE_LAYOUT_BIT)) {
|
|
||||||
SpvOp opcode = inst->opcode;
|
|
||||||
|
|
||||||
if (_.getLayoutStage() < kLayoutFunctionDeclarations) {
|
|
||||||
// Module scoped instructions are processed by determining if the opcode
|
|
||||||
// is part of the current stage. If it is not then the next stage is
|
|
||||||
// checked.
|
|
||||||
while (_.isOpcodeInCurrentLayoutStage(opcode) == false) {
|
|
||||||
_.progressToNextLayoutStageOrder();
|
|
||||||
|
|
||||||
if (_.getLayoutStage() == kLayoutMemoryModel &&
|
|
||||||
opcode != SpvOpMemoryModel) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< spvOpcodeString(opcode)
|
|
||||||
<< " cannot appear before the memory model instruction";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_.getLayoutStage() == kLayoutFunctionDeclarations) {
|
|
||||||
// All module stages have been processed. Recursivly call
|
|
||||||
// ModuleLayoutPass to process the next section of the module
|
|
||||||
return ModuleLayoutPass(_, inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opcode == SpvOpVariable) {
|
|
||||||
const uint32_t* storage_class = inst->words + inst->operands[2].offset;
|
|
||||||
if (*storage_class == SpvStorageClassFunction) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "Variables cannot have a function[7] storage class "
|
|
||||||
"outside of a function";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (_.getLayoutStage() == kLayoutFunctionDeclarations) {
|
|
||||||
if (_.isOpcodeInCurrentLayoutStage(opcode)) {
|
|
||||||
if (opcode == SpvOpVariable) {
|
|
||||||
const uint32_t* storage_class =
|
|
||||||
inst->words + inst->operands[2].offset;
|
|
||||||
if (*storage_class != SpvStorageClassFunction)
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "All Variable instructions in a function must have a "
|
|
||||||
"storage class of function[7]";
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (opcode) {
|
|
||||||
case SpvOpFunction:
|
|
||||||
if (_.in_function_body()) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "Cannot declare a function in a function body";
|
|
||||||
}
|
|
||||||
spvCheckReturn(_.get_functions().RegisterFunction(
|
|
||||||
inst->result_id, inst->type_id,
|
|
||||||
inst->words[inst->operands[2].offset],
|
|
||||||
inst->words[inst->operands[3].offset]));
|
|
||||||
break;
|
|
||||||
case SpvOpFunctionParameter:
|
|
||||||
if (_.in_function_body() == false) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT) << "Function parameter "
|
|
||||||
"instructions must be "
|
|
||||||
"in a function body";
|
|
||||||
}
|
|
||||||
spvCheckReturn(_.get_functions().RegisterFunctionParameter(
|
|
||||||
inst->result_id, inst->type_id));
|
|
||||||
break;
|
|
||||||
case SpvOpLine: // ??
|
|
||||||
break;
|
|
||||||
case SpvOpLabel:
|
|
||||||
if (_.in_function_body() == false) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "Label instructions must be in a function body";
|
|
||||||
}
|
|
||||||
_.progressToNextLayoutStageOrder();
|
|
||||||
spvCheckReturn(_.get_functions().RegisterSetFunctionDeclType(
|
|
||||||
FunctionDecl::kFunctionDeclDefinition));
|
|
||||||
break;
|
|
||||||
case SpvOpFunctionEnd:
|
|
||||||
assert(_.get_functions().get_block_count() == 0 &&
|
|
||||||
"Function contains blocks in function declaration section");
|
|
||||||
if (_.in_function_body() == false) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "Function end instructions must be in a function body";
|
|
||||||
}
|
|
||||||
spvCheckReturn(_.get_functions().RegisterSetFunctionDeclType(
|
|
||||||
FunctionDecl::kFunctionDeclDeclaration));
|
|
||||||
spvCheckReturn(_.get_functions().RegisterFunctionEnd());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "A function must begin with a label";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< spvOpcodeString(opcode)
|
|
||||||
<< " cannot appear in a function declaration";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_.isOpcodeInCurrentLayoutStage(opcode) == false) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< " cannot appear in a funciton definition";
|
|
||||||
}
|
|
||||||
// NOTE: Additional checks will be performed in the CfgPass function
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SPV_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(umar): Support for merge instructions
|
|
||||||
// TODO(umar): Structured control flow checks
|
|
||||||
spv_result_t CfgPass(ValidationState_t& _,
|
|
||||||
const spv_parsed_instruction_t* inst) {
|
|
||||||
if (_.getLayoutStage() == kLayoutFunctionDefinitions) {
|
|
||||||
SpvOp opcode = inst->opcode;
|
|
||||||
switch (opcode) {
|
|
||||||
case SpvOpFunction:
|
|
||||||
spvCheckReturn(_.get_functions().RegisterFunction(
|
|
||||||
inst->result_id, inst->type_id,
|
|
||||||
inst->words[inst->operands[2].offset],
|
|
||||||
inst->words[inst->operands[3].offset]));
|
|
||||||
spvCheckReturn(_.get_functions().RegisterSetFunctionDeclType(
|
|
||||||
FunctionDecl::kFunctionDeclDefinition));
|
|
||||||
break;
|
|
||||||
case SpvOpFunctionParameter:
|
|
||||||
spvCheckReturn(_.get_functions().RegisterFunctionParameter(
|
|
||||||
inst->result_id, inst->type_id));
|
|
||||||
break;
|
|
||||||
case SpvOpFunctionEnd:
|
|
||||||
if (_.get_functions().get_block_count() == 0)
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT) << "Function declarations "
|
|
||||||
"must appear before "
|
|
||||||
"function definitions.";
|
|
||||||
spvCheckReturn(_.get_functions().RegisterFunctionEnd());
|
|
||||||
break;
|
|
||||||
case SpvOpLabel:
|
|
||||||
spvCheckReturn(_.get_functions().RegisterBlock(inst->result_id));
|
|
||||||
break;
|
|
||||||
case SpvOpBranch:
|
|
||||||
case SpvOpBranchConditional:
|
|
||||||
case SpvOpSwitch:
|
|
||||||
case SpvOpKill:
|
|
||||||
case SpvOpReturn:
|
|
||||||
case SpvOpReturnValue:
|
|
||||||
case SpvOpUnreachable:
|
|
||||||
spvCheckReturn(_.get_functions().RegisterBlockEnd());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (_.in_block() == false) {
|
|
||||||
return _.diag(SPV_ERROR_INVALID_LAYOUT) << spvOpcodeString(opcode)
|
|
||||||
<< " must appear in a block";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SPV_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
spv_result_t ProcessInstructions(void* user_data,
|
|
||||||
const spv_parsed_instruction_t* inst) {
|
const spv_parsed_instruction_t* inst) {
|
||||||
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
|
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
|
||||||
_.incrementInstructionCount();
|
_.incrementInstructionCount();
|
||||||
|
|
||||||
auto can_have_forward_declared_ids =
|
|
||||||
getCanBeForwardDeclaredFunction(inst->opcode);
|
|
||||||
|
|
||||||
DebugInstructionPass(_, inst);
|
DebugInstructionPass(_, inst);
|
||||||
|
|
||||||
// TODO(umar): Perform data rules pass
|
// TODO(umar): Perform data rules pass
|
||||||
// TODO(umar): Perform instruction validation pass
|
|
||||||
spvCheckReturn(ModuleLayoutPass(_, inst));
|
spvCheckReturn(ModuleLayoutPass(_, inst));
|
||||||
spvCheckReturn(CfgPass(_, inst));
|
spvCheckReturn(CfgPass(_, inst));
|
||||||
spvCheckReturn(SsaPass(_, can_have_forward_declared_ids, inst));
|
spvCheckReturn(SsaPass(_, inst));
|
||||||
|
spvCheckReturn(InstructionPass(_, inst));
|
||||||
|
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -645,7 +373,7 @@ spv_result_t spvValidate(const spv_const_context context,
|
|||||||
ValidationState_t vstate(pDiagnostic, options);
|
ValidationState_t vstate(pDiagnostic, options);
|
||||||
spvCheckReturn(spvBinaryParse(context, &vstate, binary->code,
|
spvCheckReturn(spvBinaryParse(context, &vstate, binary->code,
|
||||||
binary->wordCount, setHeader,
|
binary->wordCount, setHeader,
|
||||||
ProcessInstructions, pDiagnostic));
|
ProcessInstruction, pDiagnostic));
|
||||||
|
|
||||||
// TODO(umar): Add validation checks which require the parsing of the entire
|
// TODO(umar): Add validation checks which require the parsing of the entire
|
||||||
// module. Use the information from the processInstructions pass to make
|
// module. Use the information from the processInstructions pass to make
|
||||||
|
58
source/validate_cfg.cpp
Normal file
58
source/validate_cfg.cpp
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.
|
||||||
|
|
||||||
|
#include "validate_passes.h"
|
||||||
|
#include "validate_types.h"
|
||||||
|
|
||||||
|
namespace libspirv {
|
||||||
|
|
||||||
|
// TODO(umar): Support for merge instructions
|
||||||
|
// TODO(umar): Structured control flow checks
|
||||||
|
spv_result_t CfgPass(ValidationState_t& _,
|
||||||
|
const spv_parsed_instruction_t* inst) {
|
||||||
|
if (_.getLayoutSection() == kLayoutFunctionDefinitions) {
|
||||||
|
SpvOp opcode = inst->opcode;
|
||||||
|
switch (opcode) {
|
||||||
|
case SpvOpLabel:
|
||||||
|
spvCheckReturn(_.get_functions().RegisterBlock(inst->result_id));
|
||||||
|
break;
|
||||||
|
case SpvOpBranch:
|
||||||
|
case SpvOpBranchConditional:
|
||||||
|
case SpvOpSwitch:
|
||||||
|
case SpvOpKill:
|
||||||
|
case SpvOpReturn:
|
||||||
|
case SpvOpReturnValue:
|
||||||
|
case SpvOpUnreachable:
|
||||||
|
spvCheckReturn(_.get_functions().RegisterBlockEnd());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SPV_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
62
source/validate_instruction.cpp
Normal file
62
source/validate_instruction.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Performs validation on instructions that appear inside of a SPIR-V block.
|
||||||
|
|
||||||
|
#include "validate_passes.h"
|
||||||
|
#include "validate_types.h"
|
||||||
|
|
||||||
|
namespace libspirv {
|
||||||
|
|
||||||
|
spv_result_t InstructionPass(ValidationState_t& _,
|
||||||
|
const spv_parsed_instruction_t* inst) {
|
||||||
|
if (_.is_enabled(SPV_VALIDATE_INSTRUCTION_BIT)) {
|
||||||
|
SpvOp opcode = inst->opcode;
|
||||||
|
switch (opcode) {
|
||||||
|
case SpvOpVariable: {
|
||||||
|
const uint32_t storage_class = inst->words[inst->operands[2].offset];
|
||||||
|
if (_.getLayoutSection() > kLayoutFunctionDeclarations) {
|
||||||
|
if (storage_class != SpvStorageClassFunction) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "Variables must have a function[7] storage class inside"
|
||||||
|
" of a function";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (storage_class == SpvStorageClassFunction) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "Variables can not have a function[7] storage class "
|
||||||
|
"outside of a function";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SPV_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
209
source/validate_layout.cpp
Normal file
209
source/validate_layout.cpp
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Source code for logical layout validation as described in section 2.4
|
||||||
|
|
||||||
|
#include "validate_types.h"
|
||||||
|
#include "validate_passes.h"
|
||||||
|
#include "libspirv/libspirv.h"
|
||||||
|
|
||||||
|
#include "diagnostic.h"
|
||||||
|
#include "opcode.h"
|
||||||
|
#include "operand.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using libspirv::ValidationState_t;
|
||||||
|
using libspirv::kLayoutMemoryModel;
|
||||||
|
using libspirv::kLayoutFunctionDeclarations;
|
||||||
|
using libspirv::kLayoutFunctionDefinitions;
|
||||||
|
using libspirv::FunctionDecl;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Module scoped instructions are processed by determining if the opcode
|
||||||
|
// is part of the current layout section. If it is not then the next sections is
|
||||||
|
// checked.
|
||||||
|
spv_result_t ModuleScopedInstructions(ValidationState_t& _,
|
||||||
|
const spv_parsed_instruction_t* inst,
|
||||||
|
SpvOp opcode) {
|
||||||
|
while (_.isOpcodeInCurrentLayoutSection(opcode) == false) {
|
||||||
|
_.progressToNextLayoutSectionOrder();
|
||||||
|
|
||||||
|
switch (_.getLayoutSection()) {
|
||||||
|
case kLayoutMemoryModel:
|
||||||
|
if (opcode != SpvOpMemoryModel) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< spvOpcodeString(opcode)
|
||||||
|
<< " cannot appear before the memory model instruction";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kLayoutFunctionDeclarations:
|
||||||
|
// All module sections have been processed. Recursivly call
|
||||||
|
// ModuleLayoutPass to process the next section of the module
|
||||||
|
return libspirv::ModuleLayoutPass(_, inst);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SPV_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function declaration validation is performed by making sure that the
|
||||||
|
// FunctionParameter and FunctionEnd instructions only appear inside of
|
||||||
|
// functions. It also ensures that the Function instruction does not appear
|
||||||
|
// inside of another function. This stage ends when the first label is
|
||||||
|
// encountered inside of a function.
|
||||||
|
spv_result_t FunctionScopedInstructions(ValidationState_t& _,
|
||||||
|
const spv_parsed_instruction_t* inst,
|
||||||
|
SpvOp opcode) {
|
||||||
|
if (_.isOpcodeInCurrentLayoutSection(opcode)) {
|
||||||
|
switch (opcode) {
|
||||||
|
case SpvOpFunction:
|
||||||
|
if (_.in_function_body()) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "Cannot declare a function in a function body";
|
||||||
|
}
|
||||||
|
spvCheckReturn(_.get_functions().RegisterFunction(
|
||||||
|
inst->result_id, inst->type_id,
|
||||||
|
inst->words[inst->operands[2].offset],
|
||||||
|
inst->words[inst->operands[3].offset]));
|
||||||
|
if (_.getLayoutSection() == kLayoutFunctionDefinitions)
|
||||||
|
spvCheckReturn(_.get_functions().RegisterSetFunctionDeclType(
|
||||||
|
FunctionDecl::kFunctionDeclDefinition));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpFunctionParameter:
|
||||||
|
if (_.in_function_body() == false) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT) << "Function parameter "
|
||||||
|
"instructions must be in "
|
||||||
|
"a function body";
|
||||||
|
}
|
||||||
|
if (_.get_functions().get_block_count() != 0) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "Function parameters must only appear immediatly after the "
|
||||||
|
"function definition";
|
||||||
|
}
|
||||||
|
spvCheckReturn(_.get_functions().RegisterFunctionParameter(
|
||||||
|
inst->result_id, inst->type_id));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpFunctionEnd:
|
||||||
|
if (_.in_function_body() == false) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "Function end instructions must be in a function body";
|
||||||
|
}
|
||||||
|
if (_.in_block()) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "Function end cannot be called in blocks";
|
||||||
|
}
|
||||||
|
if (_.get_functions().get_block_count() == 0 &&
|
||||||
|
_.getLayoutSection() == kLayoutFunctionDefinitions) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT) << "Function declarations "
|
||||||
|
"must appear before "
|
||||||
|
"function definitions.";
|
||||||
|
}
|
||||||
|
spvCheckReturn(_.get_functions().RegisterFunctionEnd());
|
||||||
|
if (_.getLayoutSection() == kLayoutFunctionDeclarations) {
|
||||||
|
spvCheckReturn(_.get_functions().RegisterSetFunctionDeclType(
|
||||||
|
FunctionDecl::kFunctionDeclDeclaration));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpLine: // ??
|
||||||
|
break;
|
||||||
|
case SpvOpLabel:
|
||||||
|
// If the label is encountered then the current function is a
|
||||||
|
// definition so set the function to a declaration and update the
|
||||||
|
// module section
|
||||||
|
if (_.in_function_body() == false) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "Label instructions must be in a function body";
|
||||||
|
}
|
||||||
|
if (_.in_block()) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "A block must end with a branch instruction.";
|
||||||
|
}
|
||||||
|
if (_.getLayoutSection() == kLayoutFunctionDeclarations) {
|
||||||
|
_.progressToNextLayoutSectionOrder();
|
||||||
|
spvCheckReturn(_.get_functions().RegisterSetFunctionDeclType(
|
||||||
|
FunctionDecl::kFunctionDeclDefinition));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (_.getLayoutSection() == kLayoutFunctionDeclarations) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< "A function must begin with a label";
|
||||||
|
} else {
|
||||||
|
if (_.in_block() == false) {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< spvOpcodeString(opcode) << " must appear in a block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return _.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||||
|
<< spvOpcodeString(opcode)
|
||||||
|
<< " cannot appear in a function declaration";
|
||||||
|
}
|
||||||
|
return SPV_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace libspirv {
|
||||||
|
// TODO(umar): Check linkage capabilities for function declarations
|
||||||
|
// TODO(umar): Better error messages
|
||||||
|
// NOTE: This function does not handle CFG related validation
|
||||||
|
// Performs logical layout validation. See Section 2.4
|
||||||
|
spv_result_t ModuleLayoutPass(ValidationState_t& _,
|
||||||
|
const spv_parsed_instruction_t* inst) {
|
||||||
|
if (_.is_enabled(SPV_VALIDATE_LAYOUT_BIT)) {
|
||||||
|
SpvOp opcode = inst->opcode;
|
||||||
|
|
||||||
|
switch (_.getLayoutSection()) {
|
||||||
|
case kLayoutCapabilities:
|
||||||
|
case kLayoutExtensions:
|
||||||
|
case kLayoutExtInstImport:
|
||||||
|
case kLayoutMemoryModel:
|
||||||
|
case kLayoutEntryPoint:
|
||||||
|
case kLayoutExecutionMode:
|
||||||
|
case kLayoutDebug1:
|
||||||
|
case kLayoutDebug2:
|
||||||
|
case kLayoutAnnotations:
|
||||||
|
case kLayoutTypes:
|
||||||
|
spvCheckReturn(ModuleScopedInstructions(_, inst, opcode));
|
||||||
|
break;
|
||||||
|
case kLayoutFunctionDeclarations:
|
||||||
|
case kLayoutFunctionDefinitions:
|
||||||
|
spvCheckReturn(FunctionScopedInstructions(_, inst, opcode));
|
||||||
|
break;
|
||||||
|
} // switch(getLayoutSection())
|
||||||
|
}
|
||||||
|
return SPV_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
55
source/validate_passes.h
Normal file
55
source/validate_passes.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// 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_PASSES_H_
|
||||||
|
#define LIBSPIRV_VALIDATE_PASSES_H_
|
||||||
|
|
||||||
|
#include "binary.h"
|
||||||
|
#include "validate_types.h"
|
||||||
|
|
||||||
|
namespace libspirv
|
||||||
|
{
|
||||||
|
// TODO(umar): Better docs
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
142
source/validate_ssa.cpp
Normal file
142
source/validate_ssa.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// 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 "opcode.h"
|
||||||
|
#include "validate_passes.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using std::function;
|
||||||
|
using libspirv::ValidationState_t;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// This funciton takes the opcode of an instruction and returns
|
||||||
|
// a function object that will return true if the index
|
||||||
|
// of the operand can be forwarad declared. This function will
|
||||||
|
// used in the SSA validation stage of the pipeline
|
||||||
|
function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) {
|
||||||
|
function<bool(unsigned index)> out;
|
||||||
|
switch (opcode) {
|
||||||
|
case SpvOpExecutionMode:
|
||||||
|
case SpvOpEntryPoint:
|
||||||
|
case SpvOpName:
|
||||||
|
case SpvOpMemberName:
|
||||||
|
case SpvOpSelectionMerge:
|
||||||
|
case SpvOpDecorate:
|
||||||
|
case SpvOpMemberDecorate:
|
||||||
|
case SpvOpBranch:
|
||||||
|
case SpvOpLoopMerge:
|
||||||
|
out = [](unsigned) { return true; };
|
||||||
|
break;
|
||||||
|
case SpvOpGroupDecorate:
|
||||||
|
case SpvOpGroupMemberDecorate:
|
||||||
|
case SpvOpBranchConditional:
|
||||||
|
case SpvOpSwitch:
|
||||||
|
out = [](unsigned index) { return index != 0; };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpFunctionCall:
|
||||||
|
out = [](unsigned index) { return index == 2; };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpPhi:
|
||||||
|
out = [](unsigned index) { return index > 1; };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpEnqueueKernel:
|
||||||
|
out = [](unsigned index) { return index == 8; };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpGetKernelNDrangeSubGroupCount:
|
||||||
|
case SpvOpGetKernelNDrangeMaxSubGroupSize:
|
||||||
|
out = [](unsigned index) { return index == 3; };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SpvOpGetKernelWorkGroupSize:
|
||||||
|
case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
|
||||||
|
out = [](unsigned index) { return index == 2; };
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
out = [](unsigned) { return false; };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace libspirv {
|
||||||
|
|
||||||
|
// Performs SSA validation on the IDs of an instruction. The
|
||||||
|
// can_have_forward_declared_ids functor should return true if the
|
||||||
|
// instruction operand's ID can be forward referenced.
|
||||||
|
//
|
||||||
|
// TODO(umar): Use dominators to correctly validate SSA. For example, the result
|
||||||
|
// id from a 'then' block cannot dominate its usage in the 'else' block. This
|
||||||
|
// is not yet performed by this funciton.
|
||||||
|
spv_result_t SsaPass(ValidationState_t& _,
|
||||||
|
const spv_parsed_instruction_t* inst) {
|
||||||
|
auto can_have_forward_declared_ids =
|
||||||
|
getCanBeForwardDeclaredFunction(inst->opcode);
|
||||||
|
|
||||||
|
if (_.is_enabled(SPV_VALIDATE_SSA_BIT)) {
|
||||||
|
for (unsigned i = 0; i < inst->num_operands; i++) {
|
||||||
|
const spv_parsed_operand_t& operand = inst->operands[i];
|
||||||
|
const spv_operand_type_t& type = operand.type;
|
||||||
|
const uint32_t* operand_ptr = inst->words + operand.offset;
|
||||||
|
|
||||||
|
auto ret = SPV_ERROR_INTERNAL;
|
||||||
|
switch (type) {
|
||||||
|
case SPV_OPERAND_TYPE_RESULT_ID:
|
||||||
|
_.removeIfForwardDeclared(*operand_ptr);
|
||||||
|
ret = _.defineId(*operand_ptr);
|
||||||
|
break;
|
||||||
|
case SPV_OPERAND_TYPE_ID:
|
||||||
|
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||||
|
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
|
||||||
|
case SPV_OPERAND_TYPE_SCOPE_ID:
|
||||||
|
if (_.isDefinedId(*operand_ptr)) {
|
||||||
|
ret = SPV_SUCCESS;
|
||||||
|
} else if (can_have_forward_declared_ids(i)) {
|
||||||
|
ret = _.forwardDeclareId(*operand_ptr);
|
||||||
|
} else {
|
||||||
|
ret = _.diag(SPV_ERROR_INVALID_ID) << "ID "
|
||||||
|
<< _.getIdName(*operand_ptr)
|
||||||
|
<< " has not been defined";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = SPV_SUCCESS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (SPV_SUCCESS != ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SPV_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,20 @@ using std::find;
|
|||||||
using std::string;
|
using std::string;
|
||||||
using std::unordered_set;
|
using std::unordered_set;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using namespace libspirv;
|
|
||||||
|
using libspirv::kLayoutCapabilities;
|
||||||
|
using libspirv::kLayoutExtensions;
|
||||||
|
using libspirv::kLayoutExtInstImport;
|
||||||
|
using libspirv::kLayoutMemoryModel;
|
||||||
|
using libspirv::kLayoutEntryPoint;
|
||||||
|
using libspirv::kLayoutExecutionMode;
|
||||||
|
using libspirv::kLayoutDebug1;
|
||||||
|
using libspirv::kLayoutDebug2;
|
||||||
|
using libspirv::kLayoutAnnotations;
|
||||||
|
using libspirv::kLayoutTypes;
|
||||||
|
using libspirv::kLayoutFunctionDeclarations;
|
||||||
|
using libspirv::kLayoutFunctionDefinitions;
|
||||||
|
using libspirv::ModuleLayoutSection;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
|
bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
|
||||||
@ -258,11 +271,11 @@ int ValidationState_t::incrementInstructionCount() {
|
|||||||
return instruction_counter_++;
|
return instruction_counter_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleLayoutSection ValidationState_t::getLayoutStage() const {
|
ModuleLayoutSection ValidationState_t::getLayoutSection() const {
|
||||||
return current_layout_stage_;
|
return current_layout_stage_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ValidationState_t::progressToNextLayoutStageOrder() {
|
void ValidationState_t::progressToNextLayoutSectionOrder() {
|
||||||
// Guard against going past the last element(kLayoutFunctionDefinitions)
|
// Guard against going past the last element(kLayoutFunctionDefinitions)
|
||||||
if (current_layout_stage_ <= kLayoutFunctionDefinitions) {
|
if (current_layout_stage_ <= kLayoutFunctionDefinitions) {
|
||||||
current_layout_stage_ =
|
current_layout_stage_ =
|
||||||
@ -270,7 +283,7 @@ void ValidationState_t::progressToNextLayoutStageOrder() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ValidationState_t::isOpcodeInCurrentLayoutStage(SpvOp op) {
|
bool ValidationState_t::isOpcodeInCurrentLayoutSection(SpvOp op) {
|
||||||
return IsInstructionInLayoutSection(current_layout_stage_, op);
|
return IsInstructionInLayoutSection(current_layout_stage_, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,15 +335,8 @@ spv_result_t Functions::RegisterFunctionParameter(uint32_t id,
|
|||||||
assert(in_function_ == true &&
|
assert(in_function_ == true &&
|
||||||
"Function parameter instructions cannot be declared outside of a "
|
"Function parameter instructions cannot be declared outside of a "
|
||||||
"function");
|
"function");
|
||||||
if (in_block()) {
|
assert(in_block() == false &&
|
||||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
"Function parameters cannot be called in blocks");
|
||||||
<< "Function parameters cannot be called in blocks";
|
|
||||||
}
|
|
||||||
if (block_ids_.back().size() != 0) {
|
|
||||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "Function parameters must only appear immediatly after the "
|
|
||||||
"function definition";
|
|
||||||
}
|
|
||||||
// TODO(umar): Validate function parameter type order and count
|
// TODO(umar): Validate function parameter type order and count
|
||||||
// TODO(umar): Use these variables to validate parameter type
|
// TODO(umar): Use these variables to validate parameter type
|
||||||
(void)id;
|
(void)id;
|
||||||
@ -339,33 +345,19 @@ spv_result_t Functions::RegisterFunctionParameter(uint32_t id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
spv_result_t Functions::RegisterSetFunctionDeclType(FunctionDecl type) {
|
spv_result_t Functions::RegisterSetFunctionDeclType(FunctionDecl type) {
|
||||||
assert(in_function_ == true &&
|
assert(declaration_type_.back() == FunctionDecl::kFunctionDeclUnknown);
|
||||||
"Function can not be declared inside of another function");
|
declaration_type_.back() = type;
|
||||||
if (declaration_type_.size() <= 1 || type == *(end(declaration_type_) - 2) ||
|
|
||||||
type == FunctionDecl::kFunctionDeclDeclaration) {
|
|
||||||
declaration_type_.back() = type;
|
|
||||||
} else if (type == FunctionDecl::kFunctionDeclDeclaration) {
|
|
||||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
|
||||||
<< "Function declartions must appear before function definitions";
|
|
||||||
} else {
|
|
||||||
declaration_type_.back() = type;
|
|
||||||
}
|
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
spv_result_t Functions::RegisterBlock(uint32_t id) {
|
spv_result_t Functions::RegisterBlock(uint32_t id) {
|
||||||
assert(in_function_ == true && "Labels can only exsist in functions");
|
assert(in_function_ == true && "Blocks can only exsist in functions");
|
||||||
if (module_.getLayoutStage() ==
|
assert(in_block_ == false && "Blocks cannot be nested");
|
||||||
ModuleLayoutSection::kLayoutFunctionDeclarations) {
|
assert(module_.getLayoutSection() !=
|
||||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
ModuleLayoutSection::kLayoutFunctionDeclarations &&
|
||||||
<< "Function declartions must appear before function definitions";
|
"Function declartions must appear before function definitions");
|
||||||
}
|
assert(declaration_type_.back() == FunctionDecl::kFunctionDeclDefinition &&
|
||||||
if (declaration_type_.back() != FunctionDecl::kFunctionDeclDefinition) {
|
"Function declaration type should have already been defined");
|
||||||
// NOTE: This should not happen. We should know that this function is a
|
|
||||||
// definition at this point.
|
|
||||||
return module_.diag(SPV_ERROR_INTERNAL)
|
|
||||||
<< "Function declaration type should have already been defined";
|
|
||||||
}
|
|
||||||
|
|
||||||
block_ids_.back().push_back(id);
|
block_ids_.back().push_back(id);
|
||||||
in_block_ = true;
|
in_block_ = true;
|
||||||
@ -375,24 +367,22 @@ spv_result_t Functions::RegisterBlock(uint32_t id) {
|
|||||||
spv_result_t Functions::RegisterFunctionEnd() {
|
spv_result_t Functions::RegisterFunctionEnd() {
|
||||||
assert(in_function_ == true &&
|
assert(in_function_ == true &&
|
||||||
"Function end can only be called in functions");
|
"Function end can only be called in functions");
|
||||||
if (in_block()) {
|
assert(in_block_ == false &&
|
||||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
"Function end cannot be called inside a block");
|
||||||
<< "Function end cannot be called in blocks";
|
|
||||||
}
|
|
||||||
in_function_ = false;
|
in_function_ = false;
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
spv_result_t Functions::RegisterBlockEnd() {
|
spv_result_t Functions::RegisterBlockEnd() {
|
||||||
|
assert(in_function_ == true &&
|
||||||
|
"Branch instruction can only be called in a function");
|
||||||
assert(in_block_ == true &&
|
assert(in_block_ == true &&
|
||||||
"Branch instruction can only be called in a block");
|
"Branch instruction can only be called in a block");
|
||||||
in_block_ = false;
|
in_block_ = false;
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Functions::get_block_count() {
|
size_t Functions::get_block_count() const {
|
||||||
assert(in_function_ == true &&
|
|
||||||
"Branch instruction can only be called in a block");
|
|
||||||
return block_ids_.back().size();
|
return block_ids_.back().size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ class Functions {
|
|||||||
spv_result_t RegisterBlockEnd();
|
spv_result_t RegisterBlockEnd();
|
||||||
|
|
||||||
// Returns the number of blocks in the current function being parsed
|
// Returns the number of blocks in the current function being parsed
|
||||||
size_t get_block_count();
|
size_t get_block_count() const;
|
||||||
|
|
||||||
// Retuns true if called after a function instruction but before the
|
// Retuns true if called after a function instruction but before the
|
||||||
// function end instruction
|
// function end instruction
|
||||||
@ -188,13 +188,13 @@ class ValidationState_t {
|
|||||||
int incrementInstructionCount();
|
int incrementInstructionCount();
|
||||||
|
|
||||||
// Returns the current layout section which is being processed
|
// Returns the current layout section which is being processed
|
||||||
ModuleLayoutSection getLayoutStage() const;
|
ModuleLayoutSection getLayoutSection() const;
|
||||||
|
|
||||||
// Increments the module_layout_order_stage_
|
// Increments the module_layout_order_stage_
|
||||||
void progressToNextLayoutStageOrder();
|
void progressToNextLayoutSectionOrder();
|
||||||
|
|
||||||
// Determines if the op instruction is part of the current stage
|
// Determines if the op instruction is part of the current stage
|
||||||
bool isOpcodeInCurrentLayoutStage(SpvOp op);
|
bool isOpcodeInCurrentLayoutSection(SpvOp op);
|
||||||
|
|
||||||
libspirv::DiagnosticStream diag(spv_result_t error_code) const;
|
libspirv::DiagnosticStream diag(spv_result_t error_code) const;
|
||||||
|
|
||||||
@ -234,4 +234,8 @@ class ValidationState_t {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define spvCheckReturn(expression) \
|
||||||
|
if (spv_result_t error = (expression)) return error;
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -252,7 +252,9 @@ TEST_F(ValidateLayout, VariableFunctionStorageGood) {
|
|||||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateLayout, VariableFunctionStorageBad) {
|
|
||||||
|
// TODO(umar): This function should be moved to another validation file
|
||||||
|
TEST_F(ValidateLayout, DISABLED_VariableFunctionStorageBad) {
|
||||||
char str[] = R"(
|
char str[] = R"(
|
||||||
OpMemoryModel Logical GLSL450
|
OpMemoryModel Logical GLSL450
|
||||||
OpDecorate %var Restrict
|
OpDecorate %var Restrict
|
||||||
|
Loading…
Reference in New Issue
Block a user