| // Copyright (c) 2015-2016 The Khronos Group Inc. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // Source code for logical layout validation as described in section 2.4 | |
| #include <cassert> | |
| #include "DebugInfo.h" | |
| #include "OpenCLDebugInfo100.h" | |
| #include "source/diagnostic.h" | |
| #include "source/opcode.h" | |
| #include "source/operand.h" | |
| #include "source/val/function.h" | |
| #include "source/val/instruction.h" | |
| #include "source/val/validate.h" | |
| #include "source/val/validation_state.h" | |
| namespace spvtools { | |
| namespace val { | |
| 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 Instruction* inst, SpvOp opcode) { | |
| switch (opcode) { | |
| case SpvOpExtInst: | |
| if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { | |
| // non-semantic extinst opcodes are allowed beginning in the types | |
| // section, but since they must name a return type they cannot be the | |
| // first instruction in the types section. Therefore check that we are | |
| // already in it. | |
| if (_.current_layout_section() < kLayoutTypes) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Non-semantic OpExtInst must not appear before types " | |
| << "section"; | |
| } | |
| } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { | |
| const uint32_t ext_inst_index = inst->word(4); | |
| bool local_debug_info = false; | |
| if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { | |
| const OpenCLDebugInfo100Instructions ext_inst_key = | |
| OpenCLDebugInfo100Instructions(ext_inst_index); | |
| if (ext_inst_key == OpenCLDebugInfo100DebugScope || | |
| ext_inst_key == OpenCLDebugInfo100DebugNoScope || | |
| ext_inst_key == OpenCLDebugInfo100DebugDeclare || | |
| ext_inst_key == OpenCLDebugInfo100DebugValue) { | |
| local_debug_info = true; | |
| } | |
| } else { | |
| const DebugInfoInstructions ext_inst_key = | |
| DebugInfoInstructions(ext_inst_index); | |
| if (ext_inst_key == DebugInfoDebugScope || | |
| ext_inst_key == DebugInfoDebugNoScope || | |
| ext_inst_key == DebugInfoDebugDeclare || | |
| ext_inst_key == DebugInfoDebugValue) { | |
| local_debug_info = true; | |
| } | |
| } | |
| if (local_debug_info) { | |
| if (_.in_function_body() == false) { | |
| // DebugScope, DebugNoScope, DebugDeclare, DebugValue must | |
| // appear in a function body. | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " | |
| << "of debug info extension must appear in a function " | |
| << "body"; | |
| } | |
| } else { | |
| // Debug info extinst opcodes other than DebugScope, DebugNoScope, | |
| // DebugDeclare, DebugValue must be placed between section 9 (types, | |
| // constants, global variables) and section 10 (function | |
| // declarations). | |
| if (_.current_layout_section() < kLayoutTypes || | |
| _.current_layout_section() >= kLayoutFunctionDeclarations) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Debug info extension instructions other than " | |
| << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " | |
| << "must appear between section 9 (types, constants, " | |
| << "global variables) and section 10 (function " | |
| << "declarations)"; | |
| } | |
| } | |
| } else { | |
| // otherwise they must be used in a block | |
| if (_.current_layout_section() < kLayoutFunctionDefinitions) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << spvOpcodeString(opcode) << " must appear in a block"; | |
| } | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) { | |
| if (_.IsOpcodeInPreviousLayoutSection(opcode)) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << spvOpcodeString(opcode) << " is in an invalid layout section"; | |
| } | |
| _.ProgressToNextLayoutSectionOrder(); | |
| switch (_.current_layout_section()) { | |
| case kLayoutMemoryModel: | |
| if (opcode != SpvOpMemoryModel) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << spvOpcodeString(opcode) | |
| << " cannot appear before the memory model instruction"; | |
| } | |
| break; | |
| case kLayoutFunctionDeclarations: | |
| // All module sections have been processed. Recursively call | |
| // ModuleLayoutPass to process the next section of the module | |
| return 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 Instruction* inst, SpvOp opcode) { | |
| // Make sure we advance into the function definitions when we hit | |
| // non-function declaration instructions. | |
| if (_.current_layout_section() == kLayoutFunctionDeclarations && | |
| !_.IsOpcodeInCurrentLayoutSection(opcode)) { | |
| _.ProgressToNextLayoutSectionOrder(); | |
| if (_.in_function_body()) { | |
| if (auto error = _.current_function().RegisterSetFunctionDeclType( | |
| FunctionDecl::kFunctionDeclDefinition)) { | |
| return error; | |
| } | |
| } | |
| } | |
| if (_.IsOpcodeInCurrentLayoutSection(opcode)) { | |
| switch (opcode) { | |
| case SpvOpFunction: { | |
| if (_.in_function_body()) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Cannot declare a function in a function body"; | |
| } | |
| auto control_mask = inst->GetOperandAs<SpvFunctionControlMask>(2); | |
| if (auto error = | |
| _.RegisterFunction(inst->id(), inst->type_id(), control_mask, | |
| inst->GetOperandAs<uint32_t>(3))) | |
| return error; | |
| if (_.current_layout_section() == kLayoutFunctionDefinitions) { | |
| if (auto error = _.current_function().RegisterSetFunctionDeclType( | |
| FunctionDecl::kFunctionDeclDefinition)) | |
| return error; | |
| } | |
| } break; | |
| case SpvOpFunctionParameter: | |
| if (_.in_function_body() == false) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Function parameter instructions must be in a " | |
| "function body"; | |
| } | |
| if (_.current_function().block_count() != 0) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Function parameters must only appear immediately after " | |
| "the function definition"; | |
| } | |
| if (auto error = _.current_function().RegisterFunctionParameter( | |
| inst->id(), inst->type_id())) | |
| return error; | |
| break; | |
| case SpvOpFunctionEnd: | |
| if (_.in_function_body() == false) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Function end instructions must be in a function body"; | |
| } | |
| if (_.in_block()) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Function end cannot be called in blocks"; | |
| } | |
| if (_.current_function().block_count() == 0 && | |
| _.current_layout_section() == kLayoutFunctionDefinitions) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Function declarations must appear before " | |
| "function definitions."; | |
| } | |
| if (_.current_layout_section() == kLayoutFunctionDeclarations) { | |
| if (auto error = _.current_function().RegisterSetFunctionDeclType( | |
| FunctionDecl::kFunctionDeclDeclaration)) | |
| return error; | |
| } | |
| if (auto error = _.RegisterFunctionEnd()) return error; | |
| break; | |
| case SpvOpLine: | |
| case SpvOpNoLine: | |
| 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, inst) | |
| << "Label instructions must be in a function body"; | |
| } | |
| if (_.in_block()) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "A block must end with a branch instruction."; | |
| } | |
| break; | |
| case SpvOpExtInst: | |
| if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { | |
| // non-semantic extinst opcodes are allowed beginning in the types | |
| // section, but must either be placed outside a function declaration, | |
| // or inside a block. | |
| if (_.current_layout_section() < kLayoutTypes) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Non-semantic OpExtInst must not appear before types " | |
| << "section"; | |
| } else if (_.in_function_body() && _.in_block() == false) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Non-semantic OpExtInst within function definition must " | |
| "appear in a block"; | |
| } | |
| } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { | |
| const uint32_t ext_inst_index = inst->word(4); | |
| bool local_debug_info = false; | |
| if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { | |
| const OpenCLDebugInfo100Instructions ext_inst_key = | |
| OpenCLDebugInfo100Instructions(ext_inst_index); | |
| if (ext_inst_key == OpenCLDebugInfo100DebugScope || | |
| ext_inst_key == OpenCLDebugInfo100DebugNoScope || | |
| ext_inst_key == OpenCLDebugInfo100DebugDeclare || | |
| ext_inst_key == OpenCLDebugInfo100DebugValue) { | |
| local_debug_info = true; | |
| } | |
| } else { | |
| const DebugInfoInstructions ext_inst_key = | |
| DebugInfoInstructions(ext_inst_index); | |
| if (ext_inst_key == DebugInfoDebugScope || | |
| ext_inst_key == DebugInfoDebugNoScope || | |
| ext_inst_key == DebugInfoDebugDeclare || | |
| ext_inst_key == DebugInfoDebugValue) { | |
| local_debug_info = true; | |
| } | |
| } | |
| if (local_debug_info) { | |
| if (_.in_function_body() == false) { | |
| // DebugScope, DebugNoScope, DebugDeclare, DebugValue must | |
| // appear in a function body. | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " | |
| << "of debug info extension must appear in a function " | |
| << "body"; | |
| } | |
| } else { | |
| // Debug info extinst opcodes other than DebugScope, DebugNoScope, | |
| // DebugDeclare, DebugValue must be placed between section 9 (types, | |
| // constants, global variables) and section 10 (function | |
| // declarations). | |
| if (_.current_layout_section() < kLayoutTypes || | |
| _.current_layout_section() >= kLayoutFunctionDeclarations) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "Debug info extension instructions other than " | |
| << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " | |
| << "must appear between section 9 (types, constants, " | |
| << "global variables) and section 10 (function " | |
| << "declarations)"; | |
| } | |
| } | |
| } else { | |
| // otherwise they must be used in a block | |
| if (_.in_block() == false) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << spvOpcodeString(opcode) << " must appear in a block"; | |
| } | |
| } | |
| break; | |
| default: | |
| if (_.current_layout_section() == kLayoutFunctionDeclarations && | |
| _.in_function_body()) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << "A function must begin with a label"; | |
| } else { | |
| if (_.in_block() == false) { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << spvOpcodeString(opcode) << " must appear in a block"; | |
| } | |
| } | |
| break; | |
| } | |
| } else { | |
| return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) | |
| << spvOpcodeString(opcode) | |
| << " cannot appear in a function declaration"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| } // namespace | |
| // 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 Instruction* inst) { | |
| const SpvOp opcode = inst->opcode(); | |
| switch (_.current_layout_section()) { | |
| case kLayoutCapabilities: | |
| case kLayoutExtensions: | |
| case kLayoutExtInstImport: | |
| case kLayoutMemoryModel: | |
| case kLayoutEntryPoint: | |
| case kLayoutExecutionMode: | |
| case kLayoutDebug1: | |
| case kLayoutDebug2: | |
| case kLayoutDebug3: | |
| case kLayoutAnnotations: | |
| case kLayoutTypes: | |
| if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error; | |
| break; | |
| case kLayoutFunctionDeclarations: | |
| case kLayoutFunctionDefinitions: | |
| if (auto error = FunctionScopedInstructions(_, inst, opcode)) { | |
| return error; | |
| } | |
| break; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| } // namespace val | |
| } // namespace spvtools |