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