1 // Copyright (c) 2015-2016 The Khronos Group Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Source code for logical layout validation as described in section 2.4 16 17 #include "source/val/validate.h" 18 19 #include <cassert> 20 21 #include "source/diagnostic.h" 22 #include "source/opcode.h" 23 #include "source/operand.h" 24 #include "source/val/function.h" 25 #include "source/val/instruction.h" 26 #include "source/val/validation_state.h" 27 28 namespace spvtools { 29 namespace val { 30 namespace { 31 32 // Module scoped instructions are processed by determining if the opcode 33 // is part of the current layout section. If it is not then the next sections is 34 // checked. 35 spv_result_t ModuleScopedInstructions(ValidationState_t& _, 36 const Instruction* inst, SpvOp opcode) { 37 while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) { 38 _.ProgressToNextLayoutSectionOrder(); 39 40 switch (_.current_layout_section()) { 41 case kLayoutMemoryModel: 42 if (opcode != SpvOpMemoryModel) { 43 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 44 << spvOpcodeString(opcode) 45 << " cannot appear before the memory model instruction"; 46 } 47 break; 48 case kLayoutFunctionDeclarations: 49 // All module sections have been processed. Recursively call 50 // ModuleLayoutPass to process the next section of the module 51 return ModuleLayoutPass(_, inst); 52 default: 53 break; 54 } 55 } 56 return SPV_SUCCESS; 57 } 58 59 // Function declaration validation is performed by making sure that the 60 // FunctionParameter and FunctionEnd instructions only appear inside of 61 // functions. It also ensures that the Function instruction does not appear 62 // inside of another function. This stage ends when the first label is 63 // encountered inside of a function. 64 spv_result_t FunctionScopedInstructions(ValidationState_t& _, 65 const Instruction* inst, SpvOp opcode) { 66 if (_.IsOpcodeInCurrentLayoutSection(opcode)) { 67 switch (opcode) { 68 case SpvOpFunction: { 69 if (_.in_function_body()) { 70 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 71 << "Cannot declare a function in a function body"; 72 } 73 auto control_mask = inst->GetOperandAs<SpvFunctionControlMask>(2); 74 if (auto error = 75 _.RegisterFunction(inst->id(), inst->type_id(), control_mask, 76 inst->GetOperandAs<uint32_t>(3))) 77 return error; 78 if (_.current_layout_section() == kLayoutFunctionDefinitions) { 79 if (auto error = _.current_function().RegisterSetFunctionDeclType( 80 FunctionDecl::kFunctionDeclDefinition)) 81 return error; 82 } 83 } break; 84 85 case SpvOpFunctionParameter: 86 if (_.in_function_body() == false) { 87 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 88 << "Function parameter instructions must be in a " 89 "function body"; 90 } 91 if (_.current_function().block_count() != 0) { 92 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 93 << "Function parameters must only appear immediately after " 94 "the function definition"; 95 } 96 if (auto error = _.current_function().RegisterFunctionParameter( 97 inst->id(), inst->type_id())) 98 return error; 99 break; 100 101 case SpvOpFunctionEnd: 102 if (_.in_function_body() == false) { 103 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 104 << "Function end instructions must be in a function body"; 105 } 106 if (_.in_block()) { 107 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 108 << "Function end cannot be called in blocks"; 109 } 110 if (_.current_function().block_count() == 0 && 111 _.current_layout_section() == kLayoutFunctionDefinitions) { 112 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 113 << "Function declarations must appear before " 114 "function definitions."; 115 } 116 if (_.current_layout_section() == kLayoutFunctionDeclarations) { 117 if (auto error = _.current_function().RegisterSetFunctionDeclType( 118 FunctionDecl::kFunctionDeclDeclaration)) 119 return error; 120 } 121 if (auto error = _.RegisterFunctionEnd()) return error; 122 break; 123 124 case SpvOpLine: 125 case SpvOpNoLine: 126 break; 127 case SpvOpLabel: 128 // If the label is encountered then the current function is a 129 // definition so set the function to a declaration and update the 130 // module section 131 if (_.in_function_body() == false) { 132 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 133 << "Label instructions must be in a function body"; 134 } 135 if (_.in_block()) { 136 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 137 << "A block must end with a branch instruction."; 138 } 139 if (_.current_layout_section() == kLayoutFunctionDeclarations) { 140 _.ProgressToNextLayoutSectionOrder(); 141 if (auto error = _.current_function().RegisterSetFunctionDeclType( 142 FunctionDecl::kFunctionDeclDefinition)) 143 return error; 144 } 145 break; 146 147 default: 148 if (_.current_layout_section() == kLayoutFunctionDeclarations && 149 _.in_function_body()) { 150 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 151 << "A function must begin with a label"; 152 } else { 153 if (_.in_block() == false) { 154 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 155 << spvOpcodeString(opcode) << " must appear in a block"; 156 } 157 } 158 break; 159 } 160 } else { 161 return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) 162 << spvOpcodeString(opcode) 163 << " cannot appear in a function declaration"; 164 } 165 return SPV_SUCCESS; 166 } 167 168 } // namespace 169 170 // TODO(umar): Check linkage capabilities for function declarations 171 // TODO(umar): Better error messages 172 // NOTE: This function does not handle CFG related validation 173 // Performs logical layout validation. See Section 2.4 174 spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) { 175 const SpvOp opcode = inst->opcode(); 176 177 switch (_.current_layout_section()) { 178 case kLayoutCapabilities: 179 case kLayoutExtensions: 180 case kLayoutExtInstImport: 181 case kLayoutMemoryModel: 182 case kLayoutEntryPoint: 183 case kLayoutExecutionMode: 184 case kLayoutDebug1: 185 case kLayoutDebug2: 186 case kLayoutDebug3: 187 case kLayoutAnnotations: 188 case kLayoutTypes: 189 if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error; 190 break; 191 case kLayoutFunctionDeclarations: 192 case kLayoutFunctionDefinitions: 193 if (auto error = FunctionScopedInstructions(_, inst, opcode)) { 194 return error; 195 } 196 break; 197 } 198 return SPV_SUCCESS; 199 } 200 201 } // namespace val 202 } // namespace spvtools 203