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