xref: /aosp_15_r20/external/angle/third_party/spirv-tools/src/source/val/validate_layout.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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     case spv::Op::OpExtInstWithForwardRefsKHR:
39       if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
40         const uint32_t ext_inst_index = inst->word(4);
41         bool local_debug_info = false;
42         if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
43           const OpenCLDebugInfo100Instructions ext_inst_key =
44               OpenCLDebugInfo100Instructions(ext_inst_index);
45           if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
46               ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
47               ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
48               ext_inst_key == OpenCLDebugInfo100DebugValue) {
49             local_debug_info = true;
50           }
51         } else if (inst->ext_inst_type() ==
52                    SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
53           const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
54               NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
55           if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope ||
56               ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope ||
57               ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare ||
58               ext_inst_key == NonSemanticShaderDebugInfo100DebugValue ||
59               ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
60               ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine ||
61               ext_inst_key ==
62                   NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
63             local_debug_info = true;
64           }
65         } else {
66           const DebugInfoInstructions ext_inst_key =
67               DebugInfoInstructions(ext_inst_index);
68           if (ext_inst_key == DebugInfoDebugScope ||
69               ext_inst_key == DebugInfoDebugNoScope ||
70               ext_inst_key == DebugInfoDebugDeclare ||
71               ext_inst_key == DebugInfoDebugValue) {
72             local_debug_info = true;
73           }
74         }
75 
76         if (local_debug_info) {
77           if (_.in_function_body() == false) {
78             // TODO - Print the actual name of the instruction as this list is
79             // not complete (see ext_inst_name in ValidateExtInst() for example)
80             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
81                    << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
82                    << "of debug info extension must appear in a function "
83                    << "body";
84           }
85         } else {
86           // Debug info extinst opcodes other than DebugScope, DebugNoScope,
87           // DebugDeclare, DebugValue must be placed between section 9 (types,
88           // constants, global variables) and section 10 (function
89           // declarations).
90           if (_.current_layout_section() < kLayoutTypes ||
91               _.current_layout_section() >= kLayoutFunctionDeclarations) {
92             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
93                    << "Debug info extension instructions other than "
94                    << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
95                    << "must appear between section 9 (types, constants, "
96                    << "global variables) and section 10 (function "
97                    << "declarations)";
98           }
99         }
100       } else if (spvExtInstIsNonSemantic(inst->ext_inst_type())) {
101         // non-semantic extinst opcodes are allowed beginning in the types
102         // section, but since they must name a return type they cannot be the
103         // first instruction in the types section. Therefore check that we are
104         // already in it.
105         if (_.current_layout_section() < kLayoutTypes) {
106           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
107                  << "Non-semantic OpExtInst must not appear before types "
108                  << "section";
109         }
110       } else {
111         // otherwise they must be used in a block
112         if (_.current_layout_section() < kLayoutFunctionDefinitions) {
113           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
114                  << spvOpcodeString(opcode) << " must appear in a block";
115         }
116       }
117       break;
118     default:
119       break;
120   }
121 
122   while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) {
123     if (_.IsOpcodeInPreviousLayoutSection(opcode)) {
124       return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
125              << spvOpcodeString(opcode) << " is in an invalid layout section";
126     }
127 
128     _.ProgressToNextLayoutSectionOrder();
129 
130     switch (_.current_layout_section()) {
131       case kLayoutMemoryModel:
132         if (opcode != spv::Op::OpMemoryModel) {
133           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
134                  << spvOpcodeString(opcode)
135                  << " cannot appear before the memory model instruction";
136         }
137         break;
138       case kLayoutFunctionDeclarations:
139         // All module sections have been processed. Recursively call
140         // ModuleLayoutPass to process the next section of the module
141         return ModuleLayoutPass(_, inst);
142       default:
143         break;
144     }
145   }
146   return SPV_SUCCESS;
147 }
148 
149 // Function declaration validation is performed by making sure that the
150 // FunctionParameter and FunctionEnd instructions only appear inside of
151 // functions. It also ensures that the Function instruction does not appear
152 // inside of another function. This stage ends when the first label is
153 // encountered inside of a function.
FunctionScopedInstructions(ValidationState_t & _,const Instruction * inst,spv::Op opcode)154 spv_result_t FunctionScopedInstructions(ValidationState_t& _,
155                                         const Instruction* inst,
156                                         spv::Op opcode) {
157   // Make sure we advance into the function definitions when we hit
158   // non-function declaration instructions.
159   if (_.current_layout_section() == kLayoutFunctionDeclarations &&
160       !_.IsOpcodeInCurrentLayoutSection(opcode)) {
161     _.ProgressToNextLayoutSectionOrder();
162 
163     if (_.in_function_body()) {
164       if (auto error = _.current_function().RegisterSetFunctionDeclType(
165               FunctionDecl::kFunctionDeclDefinition)) {
166         return error;
167       }
168     }
169   }
170 
171   if (_.IsOpcodeInCurrentLayoutSection(opcode)) {
172     switch (opcode) {
173       case spv::Op::OpFunction: {
174         if (_.in_function_body()) {
175           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
176                  << "Cannot declare a function in a function body";
177         }
178         auto control_mask = inst->GetOperandAs<spv::FunctionControlMask>(2);
179         if (auto error =
180                 _.RegisterFunction(inst->id(), inst->type_id(), control_mask,
181                                    inst->GetOperandAs<uint32_t>(3)))
182           return error;
183         if (_.current_layout_section() == kLayoutFunctionDefinitions) {
184           if (auto error = _.current_function().RegisterSetFunctionDeclType(
185                   FunctionDecl::kFunctionDeclDefinition))
186             return error;
187         }
188       } break;
189 
190       case spv::Op::OpFunctionParameter:
191         if (_.in_function_body() == false) {
192           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
193                  << "Function parameter instructions must be in a "
194                     "function body";
195         }
196         if (_.current_function().block_count() != 0) {
197           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
198                  << "Function parameters must only appear immediately after "
199                     "the function definition";
200         }
201         if (auto error = _.current_function().RegisterFunctionParameter(
202                 inst->id(), inst->type_id()))
203           return error;
204         break;
205 
206       case spv::Op::OpFunctionEnd:
207         if (_.in_function_body() == false) {
208           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
209                  << "Function end instructions must be in a function body";
210         }
211         if (_.in_block()) {
212           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
213                  << "Function end cannot be called in blocks";
214         }
215         if (_.current_function().block_count() == 0 &&
216             _.current_layout_section() == kLayoutFunctionDefinitions) {
217           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
218                  << "Function declarations must appear before "
219                     "function definitions.";
220         }
221         if (_.current_layout_section() == kLayoutFunctionDeclarations) {
222           if (auto error = _.current_function().RegisterSetFunctionDeclType(
223                   FunctionDecl::kFunctionDeclDeclaration))
224             return error;
225         }
226         if (auto error = _.RegisterFunctionEnd()) return error;
227         break;
228 
229       case spv::Op::OpLine:
230       case spv::Op::OpNoLine:
231         break;
232       case spv::Op::OpLabel:
233         // If the label is encountered then the current function is a
234         // definition so set the function to a declaration and update the
235         // module section
236         if (_.in_function_body() == false) {
237           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
238                  << "Label instructions must be in a function body";
239         }
240         if (_.in_block()) {
241           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
242                  << "A block must end with a branch instruction.";
243         }
244         break;
245 
246       case spv::Op::OpExtInst:
247       case spv::Op::OpExtInstWithForwardRefsKHR:
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 spv::Op opcode = inst->opcode();
360 
361   switch (_.current_layout_section()) {
362     case kLayoutCapabilities:
363     case kLayoutExtensions:
364     case kLayoutExtInstImport:
365     case kLayoutMemoryModel:
366     case kLayoutSamplerImageAddressMode:
367     case kLayoutEntryPoint:
368     case kLayoutExecutionMode:
369     case kLayoutDebug1:
370     case kLayoutDebug2:
371     case kLayoutDebug3:
372     case kLayoutAnnotations:
373     case kLayoutTypes:
374       if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error;
375       break;
376     case kLayoutFunctionDeclarations:
377     case kLayoutFunctionDefinitions:
378       if (auto error = FunctionScopedInstructions(_, inst, opcode)) {
379         return error;
380       }
381       break;
382   }
383   return SPV_SUCCESS;
384 }
385 
386 }  // namespace val
387 }  // namespace spvtools
388