mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-27 18:30:14 +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_handler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/source/validate.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_types.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_id.cpp)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/source/validate_cfg.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})
|
||||
default_compile_options(${SPIRV_TOOLS})
|
||||
|
@ -260,9 +260,10 @@ typedef enum spv_validate_options_t {
|
||||
SPV_VALIDATE_ID_BIT = SPV_BIT(2),
|
||||
SPV_VALIDATE_RULES_BIT = SPV_BIT(3),
|
||||
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_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_validate_options_t;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "validate.h"
|
||||
#include "validate_types.h"
|
||||
#include "validate_passes.h"
|
||||
|
||||
#include "binary.h"
|
||||
#include "diagnostic.h"
|
||||
@ -41,30 +42,23 @@
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
using std::function;
|
||||
using std::map;
|
||||
using std::ostream_iterator;
|
||||
using std::placeholders::_1;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::transform;
|
||||
using std::unordered_set;
|
||||
using std::vector;
|
||||
|
||||
using libspirv::CfgPass;
|
||||
using libspirv::InstructionPass;
|
||||
using libspirv::ModuleLayoutPass;
|
||||
using libspirv::SsaPass;
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
// NOTE: This function returns void and is not involved in validation
|
||||
void DebugInstructionPass(ValidationState_t& _,
|
||||
@ -440,182 +332,18 @@ void DebugInstructionPass(ValidationState_t& _,
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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,
|
||||
spv_result_t ProcessInstruction(void* user_data,
|
||||
const spv_parsed_instruction_t* inst) {
|
||||
ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
|
||||
_.incrementInstructionCount();
|
||||
|
||||
auto can_have_forward_declared_ids =
|
||||
getCanBeForwardDeclaredFunction(inst->opcode);
|
||||
|
||||
DebugInstructionPass(_, inst);
|
||||
|
||||
// TODO(umar): Perform data rules pass
|
||||
// TODO(umar): Perform instruction validation pass
|
||||
spvCheckReturn(ModuleLayoutPass(_, inst));
|
||||
spvCheckReturn(CfgPass(_, inst));
|
||||
spvCheckReturn(SsaPass(_, can_have_forward_declared_ids, inst));
|
||||
spvCheckReturn(SsaPass(_, inst));
|
||||
spvCheckReturn(InstructionPass(_, inst));
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@ -645,7 +373,7 @@ spv_result_t spvValidate(const spv_const_context context,
|
||||
ValidationState_t vstate(pDiagnostic, options);
|
||||
spvCheckReturn(spvBinaryParse(context, &vstate, binary->code,
|
||||
binary->wordCount, setHeader,
|
||||
ProcessInstructions, pDiagnostic));
|
||||
ProcessInstruction, pDiagnostic));
|
||||
|
||||
// TODO(umar): Add validation checks which require the parsing of the entire
|
||||
// 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::unordered_set;
|
||||
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 {
|
||||
bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) {
|
||||
@ -258,11 +271,11 @@ int ValidationState_t::incrementInstructionCount() {
|
||||
return instruction_counter_++;
|
||||
}
|
||||
|
||||
ModuleLayoutSection ValidationState_t::getLayoutStage() const {
|
||||
ModuleLayoutSection ValidationState_t::getLayoutSection() const {
|
||||
return current_layout_stage_;
|
||||
}
|
||||
|
||||
void ValidationState_t::progressToNextLayoutStageOrder() {
|
||||
void ValidationState_t::progressToNextLayoutSectionOrder() {
|
||||
// Guard against going past the last element(kLayoutFunctionDefinitions)
|
||||
if (current_layout_stage_ <= kLayoutFunctionDefinitions) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -322,15 +335,8 @@ spv_result_t Functions::RegisterFunctionParameter(uint32_t id,
|
||||
assert(in_function_ == true &&
|
||||
"Function parameter instructions cannot be declared outside of a "
|
||||
"function");
|
||||
if (in_block()) {
|
||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||
<< "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";
|
||||
}
|
||||
assert(in_block() == false &&
|
||||
"Function parameters cannot be called in blocks");
|
||||
// TODO(umar): Validate function parameter type order and count
|
||||
// TODO(umar): Use these variables to validate parameter type
|
||||
(void)id;
|
||||
@ -339,33 +345,19 @@ spv_result_t Functions::RegisterFunctionParameter(uint32_t id,
|
||||
}
|
||||
|
||||
spv_result_t Functions::RegisterSetFunctionDeclType(FunctionDecl type) {
|
||||
assert(in_function_ == true &&
|
||||
"Function can not be declared inside of another function");
|
||||
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;
|
||||
}
|
||||
assert(declaration_type_.back() == FunctionDecl::kFunctionDeclUnknown);
|
||||
declaration_type_.back() = type;
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t Functions::RegisterBlock(uint32_t id) {
|
||||
assert(in_function_ == true && "Labels can only exsist in functions");
|
||||
if (module_.getLayoutStage() ==
|
||||
ModuleLayoutSection::kLayoutFunctionDeclarations) {
|
||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||
<< "Function declartions must appear before function definitions";
|
||||
}
|
||||
if (declaration_type_.back() != FunctionDecl::kFunctionDeclDefinition) {
|
||||
// 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";
|
||||
}
|
||||
assert(in_function_ == true && "Blocks can only exsist in functions");
|
||||
assert(in_block_ == false && "Blocks cannot be nested");
|
||||
assert(module_.getLayoutSection() !=
|
||||
ModuleLayoutSection::kLayoutFunctionDeclarations &&
|
||||
"Function declartions must appear before function definitions");
|
||||
assert(declaration_type_.back() == FunctionDecl::kFunctionDeclDefinition &&
|
||||
"Function declaration type should have already been defined");
|
||||
|
||||
block_ids_.back().push_back(id);
|
||||
in_block_ = true;
|
||||
@ -375,24 +367,22 @@ spv_result_t Functions::RegisterBlock(uint32_t id) {
|
||||
spv_result_t Functions::RegisterFunctionEnd() {
|
||||
assert(in_function_ == true &&
|
||||
"Function end can only be called in functions");
|
||||
if (in_block()) {
|
||||
return module_.diag(SPV_ERROR_INVALID_LAYOUT)
|
||||
<< "Function end cannot be called in blocks";
|
||||
}
|
||||
assert(in_block_ == false &&
|
||||
"Function end cannot be called inside a block");
|
||||
in_function_ = false;
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t Functions::RegisterBlockEnd() {
|
||||
assert(in_function_ == true &&
|
||||
"Branch instruction can only be called in a function");
|
||||
assert(in_block_ == true &&
|
||||
"Branch instruction can only be called in a block");
|
||||
in_block_ = false;
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
size_t Functions::get_block_count() {
|
||||
assert(in_function_ == true &&
|
||||
"Branch instruction can only be called in a block");
|
||||
size_t Functions::get_block_count() const {
|
||||
return block_ids_.back().size();
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ class Functions {
|
||||
spv_result_t RegisterBlockEnd();
|
||||
|
||||
// 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
|
||||
// function end instruction
|
||||
@ -188,13 +188,13 @@ class ValidationState_t {
|
||||
int incrementInstructionCount();
|
||||
|
||||
// Returns the current layout section which is being processed
|
||||
ModuleLayoutSection getLayoutStage() const;
|
||||
ModuleLayoutSection getLayoutSection() const;
|
||||
|
||||
// Increments the module_layout_order_stage_
|
||||
void progressToNextLayoutStageOrder();
|
||||
void progressToNextLayoutSectionOrder();
|
||||
|
||||
// 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;
|
||||
|
||||
@ -234,4 +234,8 @@ class ValidationState_t {
|
||||
};
|
||||
}
|
||||
|
||||
#define spvCheckReturn(expression) \
|
||||
if (spv_result_t error = (expression)) return error;
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -252,7 +252,9 @@ TEST_F(ValidateLayout, VariableFunctionStorageGood) {
|
||||
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"(
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpDecorate %var Restrict
|
||||
|
Loading…
Reference in New Issue
Block a user