${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})
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;
#include "validate.h"
#include "validate_types.h"
+#include "validate_passes.h"
#include "binary.h"
#include "diagnostic.h"
#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,
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& _,
}
}
-// 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;
}
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
--- /dev/null
+// 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;
+}
+
+}
--- /dev/null
+// 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;
+}
+
+}
--- /dev/null
+// 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;
+}
+}
--- /dev/null
+// 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
--- /dev/null
+// 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;
+}
+}
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) {
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_ =
}
}
-bool ValidationState_t::isOpcodeInCurrentLayoutStage(SpvOp op) {
+bool ValidationState_t::isOpcodeInCurrentLayoutSection(SpvOp op) {
return IsInstructionInLayoutSection(current_layout_stage_, op);
}
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;
}
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;
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();
}
}
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
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;
};
}
+#define spvCheckReturn(expression) \
+ if (spv_result_t error = (expression)) return error;
+
+
#endif
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