• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <cassert>
18 
19 #include "DebugInfo.h"
20 #include "OpenCLDebugInfo100.h"
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/validate.h"
27 #include "source/val/validation_state.h"
28 
29 namespace spvtools {
30 namespace val {
31 namespace {
32 
33 // Module scoped instructions are processed by determining if the opcode
34 // is part of the current layout section. If it is not then the next sections is
35 // checked.
ModuleScopedInstructions(ValidationState_t & _,const Instruction * inst,SpvOp opcode)36 spv_result_t ModuleScopedInstructions(ValidationState_t& _,
37                                       const Instruction* inst, SpvOp opcode) {
38   switch (opcode) {
39     case SpvOpExtInst:
40       if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
41         // non-semantic extinst opcodes are allowed beginning in the types
42         // section, but since they must name a return type they cannot be the
43         // first instruction in the types section. Therefore check that we are
44         // already in it.
45         if (_.current_layout_section() < kLayoutTypes) {
46           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
47                  << "Non-semantic OpExtInst must not appear before types "
48                  << "section";
49         }
50       } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
51         const uint32_t ext_inst_index = inst->word(4);
52         bool local_debug_info = false;
53         if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
54           const OpenCLDebugInfo100Instructions ext_inst_key =
55               OpenCLDebugInfo100Instructions(ext_inst_index);
56           if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
57               ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
58               ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
59               ext_inst_key == OpenCLDebugInfo100DebugValue) {
60             local_debug_info = true;
61           }
62         } else {
63           const DebugInfoInstructions ext_inst_key =
64               DebugInfoInstructions(ext_inst_index);
65           if (ext_inst_key == DebugInfoDebugScope ||
66               ext_inst_key == DebugInfoDebugNoScope ||
67               ext_inst_key == DebugInfoDebugDeclare ||
68               ext_inst_key == DebugInfoDebugValue) {
69             local_debug_info = true;
70           }
71         }
72 
73         if (local_debug_info) {
74           if (_.in_function_body() == false) {
75             // DebugScope, DebugNoScope, DebugDeclare, DebugValue must
76             // appear in a function body.
77             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
78                    << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
79                    << "of debug info extension must appear in a function "
80                    << "body";
81           }
82         } else {
83           // Debug info extinst opcodes other than DebugScope, DebugNoScope,
84           // DebugDeclare, DebugValue must be placed between section 9 (types,
85           // constants, global variables) and section 10 (function
86           // declarations).
87           if (_.current_layout_section() < kLayoutTypes ||
88               _.current_layout_section() >= kLayoutFunctionDeclarations) {
89             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
90                    << "Debug info extension instructions other than "
91                    << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
92                    << "must appear between section 9 (types, constants, "
93                    << "global variables) and section 10 (function "
94                    << "declarations)";
95           }
96         }
97       } else {
98         // otherwise they must be used in a block
99         if (_.current_layout_section() < kLayoutFunctionDefinitions) {
100           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
101                  << spvOpcodeString(opcode) << " must appear in a block";
102         }
103       }
104       break;
105     default:
106       break;
107   }
108 
109   while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) {
110     if (_.IsOpcodeInPreviousLayoutSection(opcode)) {
111       return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
112              << spvOpcodeString(opcode) << " is in an invalid layout section";
113     }
114 
115     _.ProgressToNextLayoutSectionOrder();
116 
117     switch (_.current_layout_section()) {
118       case kLayoutMemoryModel:
119         if (opcode != SpvOpMemoryModel) {
120           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
121                  << spvOpcodeString(opcode)
122                  << " cannot appear before the memory model instruction";
123         }
124         break;
125       case kLayoutFunctionDeclarations:
126         // All module sections have been processed. Recursively call
127         // ModuleLayoutPass to process the next section of the module
128         return ModuleLayoutPass(_, inst);
129       default:
130         break;
131     }
132   }
133   return SPV_SUCCESS;
134 }
135 
136 // Function declaration validation is performed by making sure that the
137 // FunctionParameter and FunctionEnd instructions only appear inside of
138 // functions. It also ensures that the Function instruction does not appear
139 // inside of another function. This stage ends when the first label is
140 // encountered inside of a function.
FunctionScopedInstructions(ValidationState_t & _,const Instruction * inst,SpvOp opcode)141 spv_result_t FunctionScopedInstructions(ValidationState_t& _,
142                                         const Instruction* inst, SpvOp opcode) {
143   // Make sure we advance into the function definitions when we hit
144   // non-function declaration instructions.
145   if (_.current_layout_section() == kLayoutFunctionDeclarations &&
146       !_.IsOpcodeInCurrentLayoutSection(opcode)) {
147     _.ProgressToNextLayoutSectionOrder();
148 
149     if (_.in_function_body()) {
150       if (auto error = _.current_function().RegisterSetFunctionDeclType(
151               FunctionDecl::kFunctionDeclDefinition)) {
152         return error;
153       }
154     }
155   }
156 
157   if (_.IsOpcodeInCurrentLayoutSection(opcode)) {
158     switch (opcode) {
159       case SpvOpFunction: {
160         if (_.in_function_body()) {
161           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
162                  << "Cannot declare a function in a function body";
163         }
164         auto control_mask = inst->GetOperandAs<SpvFunctionControlMask>(2);
165         if (auto error =
166                 _.RegisterFunction(inst->id(), inst->type_id(), control_mask,
167                                    inst->GetOperandAs<uint32_t>(3)))
168           return error;
169         if (_.current_layout_section() == kLayoutFunctionDefinitions) {
170           if (auto error = _.current_function().RegisterSetFunctionDeclType(
171                   FunctionDecl::kFunctionDeclDefinition))
172             return error;
173         }
174       } break;
175 
176       case SpvOpFunctionParameter:
177         if (_.in_function_body() == false) {
178           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
179                  << "Function parameter instructions must be in a "
180                     "function body";
181         }
182         if (_.current_function().block_count() != 0) {
183           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
184                  << "Function parameters must only appear immediately after "
185                     "the function definition";
186         }
187         if (auto error = _.current_function().RegisterFunctionParameter(
188                 inst->id(), inst->type_id()))
189           return error;
190         break;
191 
192       case SpvOpFunctionEnd:
193         if (_.in_function_body() == false) {
194           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
195                  << "Function end instructions must be in a function body";
196         }
197         if (_.in_block()) {
198           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
199                  << "Function end cannot be called in blocks";
200         }
201         if (_.current_function().block_count() == 0 &&
202             _.current_layout_section() == kLayoutFunctionDefinitions) {
203           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
204                  << "Function declarations must appear before "
205                     "function definitions.";
206         }
207         if (_.current_layout_section() == kLayoutFunctionDeclarations) {
208           if (auto error = _.current_function().RegisterSetFunctionDeclType(
209                   FunctionDecl::kFunctionDeclDeclaration))
210             return error;
211         }
212         if (auto error = _.RegisterFunctionEnd()) return error;
213         break;
214 
215       case SpvOpLine:
216       case SpvOpNoLine:
217         break;
218       case SpvOpLabel:
219         // If the label is encountered then the current function is a
220         // definition so set the function to a declaration and update the
221         // module section
222         if (_.in_function_body() == false) {
223           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
224                  << "Label instructions must be in a function body";
225         }
226         if (_.in_block()) {
227           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
228                  << "A block must end with a branch instruction.";
229         }
230         break;
231 
232       case SpvOpExtInst:
233         if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
234           // non-semantic extinst opcodes are allowed beginning in the types
235           // section, but must either be placed outside a function declaration,
236           // or inside a block.
237           if (_.current_layout_section() < kLayoutTypes) {
238             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
239                    << "Non-semantic OpExtInst must not appear before types "
240                    << "section";
241           } else if (_.in_function_body() && _.in_block() == false) {
242             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
243                    << "Non-semantic OpExtInst within function definition must "
244                       "appear in a block";
245           }
246         } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
247           const uint32_t ext_inst_index = inst->word(4);
248           bool local_debug_info = false;
249           if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
250             const OpenCLDebugInfo100Instructions ext_inst_key =
251                 OpenCLDebugInfo100Instructions(ext_inst_index);
252             if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
253                 ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
254                 ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
255                 ext_inst_key == OpenCLDebugInfo100DebugValue) {
256               local_debug_info = true;
257             }
258           } else {
259             const DebugInfoInstructions ext_inst_key =
260                 DebugInfoInstructions(ext_inst_index);
261             if (ext_inst_key == DebugInfoDebugScope ||
262                 ext_inst_key == DebugInfoDebugNoScope ||
263                 ext_inst_key == DebugInfoDebugDeclare ||
264                 ext_inst_key == DebugInfoDebugValue) {
265               local_debug_info = true;
266             }
267           }
268 
269           if (local_debug_info) {
270             if (_.in_function_body() == false) {
271               // DebugScope, DebugNoScope, DebugDeclare, DebugValue must
272               // appear in a function body.
273               return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
274                      << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
275                      << "of debug info extension must appear in a function "
276                      << "body";
277             }
278           } else {
279             // Debug info extinst opcodes other than DebugScope, DebugNoScope,
280             // DebugDeclare, DebugValue must be placed between section 9 (types,
281             // constants, global variables) and section 10 (function
282             // declarations).
283             if (_.current_layout_section() < kLayoutTypes ||
284                 _.current_layout_section() >= kLayoutFunctionDeclarations) {
285               return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
286                      << "Debug info extension instructions other than "
287                      << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
288                      << "must appear between section 9 (types, constants, "
289                      << "global variables) and section 10 (function "
290                      << "declarations)";
291             }
292           }
293         } else {
294           // otherwise they must be used in a block
295           if (_.in_block() == false) {
296             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
297                    << spvOpcodeString(opcode) << " must appear in a block";
298           }
299         }
300         break;
301 
302       default:
303         if (_.current_layout_section() == kLayoutFunctionDeclarations &&
304             _.in_function_body()) {
305           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
306                  << "A function must begin with a label";
307         } else {
308           if (_.in_block() == false) {
309             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
310                    << spvOpcodeString(opcode) << " must appear in a block";
311           }
312         }
313         break;
314     }
315   } else {
316     return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
317            << spvOpcodeString(opcode)
318            << " cannot appear in a function declaration";
319   }
320   return SPV_SUCCESS;
321 }
322 
323 }  // namespace
324 
325 // TODO(umar): Check linkage capabilities for function declarations
326 // TODO(umar): Better error messages
327 // NOTE: This function does not handle CFG related validation
328 // Performs logical layout validation. See Section 2.4
ModuleLayoutPass(ValidationState_t & _,const Instruction * inst)329 spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) {
330   const SpvOp opcode = inst->opcode();
331 
332   switch (_.current_layout_section()) {
333     case kLayoutCapabilities:
334     case kLayoutExtensions:
335     case kLayoutExtInstImport:
336     case kLayoutMemoryModel:
337     case kLayoutEntryPoint:
338     case kLayoutExecutionMode:
339     case kLayoutDebug1:
340     case kLayoutDebug2:
341     case kLayoutDebug3:
342     case kLayoutAnnotations:
343     case kLayoutTypes:
344       if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error;
345       break;
346     case kLayoutFunctionDeclarations:
347     case kLayoutFunctionDefinitions:
348       if (auto error = FunctionScopedInstructions(_, inst, opcode)) {
349         return error;
350       }
351       break;
352   }
353   return SPV_SUCCESS;
354 }
355 
356 }  // namespace val
357 }  // namespace spvtools
358