Coverage Report

Created: 2026-11-12 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/val/validate_layout.cpp
Line
Count
Source
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 "OpenCLDebugInfo100.h"
19
#include "source/opcode.h"
20
#include "source/operand.h"
21
#include "source/val/function.h"
22
#include "source/val/instruction.h"
23
#include "source/val/validate.h"
24
#include "source/val/validation_state.h"
25
#include "spirv/unified1/NonSemanticShaderDebugInfo.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.
34
spv_result_t ModuleScopedInstructions(ValidationState_t& _,
35
11.8M
                                      const Instruction* inst, spv::Op opcode) {
36
11.8M
  switch (opcode) {
37
21
    case spv::Op::OpExtInst:
38
32
    case spv::Op::OpExtInstWithForwardRefsKHR:
39
32
      if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
40
0
        const uint32_t ext_inst_index = inst->word(4);
41
0
        bool local_debug_info = false;
42
0
        if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
43
0
          const OpenCLDebugInfo100Instructions ext_inst_key =
44
0
              OpenCLDebugInfo100Instructions(ext_inst_index);
45
0
          if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
46
0
              ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
47
0
              ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
48
0
              ext_inst_key == OpenCLDebugInfo100DebugValue) {
49
0
            local_debug_info = true;
50
0
          }
51
0
        } else if (inst->ext_inst_type() ==
52
0
                   SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
53
0
          const NonSemanticShaderDebugInfoInstructions ext_inst_key =
54
0
              NonSemanticShaderDebugInfoInstructions(ext_inst_index);
55
0
          if (ext_inst_key == NonSemanticShaderDebugInfoDebugScope ||
56
0
              ext_inst_key == NonSemanticShaderDebugInfoDebugNoScope ||
57
0
              ext_inst_key == NonSemanticShaderDebugInfoDebugDeclare ||
58
0
              ext_inst_key == NonSemanticShaderDebugInfoDebugValue ||
59
0
              ext_inst_key == NonSemanticShaderDebugInfoDebugLine ||
60
0
              ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine ||
61
0
              ext_inst_key ==
62
0
                  NonSemanticShaderDebugInfoDebugFunctionDefinition) {
63
0
            local_debug_info = true;
64
0
          }
65
0
        } else {
66
0
          const DebugInfoInstructions ext_inst_key =
67
0
              DebugInfoInstructions(ext_inst_index);
68
0
          if (ext_inst_key == DebugInfoDebugScope ||
69
0
              ext_inst_key == DebugInfoDebugNoScope ||
70
0
              ext_inst_key == DebugInfoDebugDeclare ||
71
0
              ext_inst_key == DebugInfoDebugValue) {
72
0
            local_debug_info = true;
73
0
          }
74
0
        }
75
76
0
        if (local_debug_info) {
77
0
          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
0
            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
81
0
                   << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
82
0
                   << "of debug info extension must appear in a function "
83
0
                   << "body";
84
0
          }
85
0
        } 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
0
          if (_.current_layout_section() < kLayoutTypes ||
91
0
              _.current_layout_section() >= kLayoutFunctionDeclarations) {
92
0
            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
93
0
                   << "Debug info extension instructions other than "
94
0
                   << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
95
0
                   << "must appear between section 9 (types, constants, "
96
0
                   << "global variables) and section 10 (function "
97
0
                   << "declarations)";
98
0
          }
99
0
        }
100
32
      } 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
31
        if (_.current_layout_section() < kLayoutTypes) {
106
0
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
107
0
                 << "Non-semantic OpExtInst must not appear before types "
108
0
                 << "section";
109
0
        }
110
31
      } else {
111
        // otherwise they must be used in a block
112
1
        if (_.current_layout_section() < kLayoutFunctionDefinitions) {
113
1
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
114
1
                 << spvOpcodeString(opcode) << " must appear in a block";
115
1
        }
116
1
      }
117
31
      break;
118
11.8M
    default:
119
11.8M
      break;
120
11.8M
  }
121
122
12.0M
  while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) {
123
249k
    if (_.IsOpcodeInPreviousLayoutSection(opcode)) {
124
56
      return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
125
56
             << spvOpcodeString(opcode) << " is in an invalid layout section";
126
56
    }
127
128
249k
    _.ProgressToNextLayoutSectionOrder();
129
130
249k
    switch (_.current_layout_section()) {
131
23.2k
      case kLayoutMemoryModel:
132
23.2k
        if (opcode != spv::Op::OpMemoryModel) {
133
1.63k
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
134
1.63k
                 << spvOpcodeString(opcode)
135
1.63k
                 << " cannot appear before the memory model instruction";
136
1.63k
        }
137
21.6k
        break;
138
21.6k
      case kLayoutFunctionDeclarations:
139
        // All module sections have been processed. Recursively call
140
        // ModuleLayoutPass to process the next section of the module
141
14.6k
        return ModuleLayoutPass(_, inst);
142
211k
      default:
143
211k
        break;
144
249k
    }
145
249k
  }
146
11.8M
  return SPV_SUCCESS;
147
11.8M
}
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.
154
spv_result_t FunctionScopedInstructions(ValidationState_t& _,
155
                                        const Instruction* inst,
156
2.70M
                                        spv::Op opcode) {
157
  // Make sure we advance into the function definitions when we hit
158
  // non-function declaration instructions.
159
2.70M
  if (_.current_layout_section() == kLayoutFunctionDeclarations &&
160
30.0k
      !_.IsOpcodeInCurrentLayoutSection(opcode)) {
161
13.6k
    _.ProgressToNextLayoutSectionOrder();
162
163
13.6k
    if (_.in_function_body()) {
164
13.5k
      if (auto error = _.current_function().RegisterSetFunctionDeclType(
165
13.5k
              FunctionDecl::kFunctionDeclDefinition)) {
166
0
        return error;
167
0
      }
168
13.5k
    }
169
13.6k
  }
170
171
2.70M
  if (_.IsOpcodeInCurrentLayoutSection(opcode)) {
172
2.70M
    switch (opcode) {
173
30.1k
      case spv::Op::OpFunction: {
174
30.1k
        if (_.in_function_body()) {
175
4
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
176
4
                 << "Cannot declare a function in a function body";
177
4
        }
178
30.1k
        auto control_mask = inst->GetOperandAs<spv::FunctionControlMask>(2);
179
30.1k
        if (auto error =
180
30.1k
                _.RegisterFunction(inst->id(), inst->type_id(), control_mask,
181
30.1k
                                   inst->GetOperandAs<uint32_t>(3)))
182
0
          return error;
183
30.1k
        if (_.current_layout_section() == kLayoutFunctionDefinitions) {
184
15.2k
          if (auto error = _.current_function().RegisterSetFunctionDeclType(
185
15.2k
                  FunctionDecl::kFunctionDeclDefinition))
186
0
            return error;
187
15.2k
        }
188
30.1k
      } break;
189
190
30.1k
      case spv::Op::OpFunctionParameter:
191
16.0k
        if (_.in_function_body() == false) {
192
1
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
193
1
                 << "Function parameter instructions must be in a "
194
1
                    "function body";
195
1
        }
196
16.0k
        if (_.current_function().block_count() != 0) {
197
2
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
198
2
                 << "Function parameters must only appear immediately after "
199
2
                    "the function definition";
200
2
        }
201
16.0k
        if (auto error = _.current_function().RegisterFunctionParameter(
202
16.0k
                inst->id(), inst->type_id()))
203
0
          return error;
204
16.0k
        break;
205
206
28.4k
      case spv::Op::OpFunctionEnd:
207
28.4k
        if (_.in_function_body() == false) {
208
20
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
209
20
                 << "Function end instructions must be in a function body";
210
20
        }
211
28.3k
        if (_.in_block()) {
212
4
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
213
4
                 << "Function end cannot be called in blocks";
214
4
        }
215
28.3k
        if (_.current_function().block_count() == 0 &&
216
1.30k
            _.current_layout_section() == kLayoutFunctionDefinitions) {
217
2
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
218
2
                 << "Function declarations must appear before "
219
2
                    "function definitions.";
220
2
        }
221
28.3k
        if (_.current_layout_section() == kLayoutFunctionDeclarations) {
222
1.29k
          if (auto error = _.current_function().RegisterSetFunctionDeclType(
223
1.29k
                  FunctionDecl::kFunctionDeclDeclaration))
224
0
            return error;
225
1.29k
        }
226
28.3k
        if (auto error = _.RegisterFunctionEnd()) return error;
227
28.3k
        break;
228
229
28.3k
      case spv::Op::OpLine:
230
1.20k
      case spv::Op::OpNoLine:
231
1.20k
        break;
232
370k
      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
370k
        if (_.in_function_body() == false) {
237
30
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
238
30
                 << "Label instructions must be in a function body";
239
30
        }
240
370k
        if (_.in_block()) {
241
14
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
242
14
                 << "A block must end with a branch instruction.";
243
14
        }
244
370k
        break;
245
246
370k
      case spv::Op::OpExtInst:
247
55.6k
      case spv::Op::OpExtInstWithForwardRefsKHR:
248
55.6k
        if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
249
0
          const uint32_t ext_inst_index = inst->word(4);
250
0
          bool local_debug_info = false;
251
0
          if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
252
0
            const OpenCLDebugInfo100Instructions ext_inst_key =
253
0
                OpenCLDebugInfo100Instructions(ext_inst_index);
254
0
            if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
255
0
                ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
256
0
                ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
257
0
                ext_inst_key == OpenCLDebugInfo100DebugValue) {
258
0
              local_debug_info = true;
259
0
            }
260
0
          } else if (inst->ext_inst_type() ==
261
0
                     SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
262
0
            const NonSemanticShaderDebugInfoInstructions ext_inst_key =
263
0
                NonSemanticShaderDebugInfoInstructions(ext_inst_index);
264
0
            if (ext_inst_key == NonSemanticShaderDebugInfoDebugScope ||
265
0
                ext_inst_key == NonSemanticShaderDebugInfoDebugNoScope ||
266
0
                ext_inst_key == NonSemanticShaderDebugInfoDebugDeclare ||
267
0
                ext_inst_key == NonSemanticShaderDebugInfoDebugValue ||
268
0
                ext_inst_key == NonSemanticShaderDebugInfoDebugLine ||
269
0
                ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine ||
270
0
                ext_inst_key ==
271
0
                    NonSemanticShaderDebugInfoDebugFunctionDefinition) {
272
0
              local_debug_info = true;
273
0
            }
274
0
          } else {
275
0
            const DebugInfoInstructions ext_inst_key =
276
0
                DebugInfoInstructions(ext_inst_index);
277
0
            if (ext_inst_key == DebugInfoDebugScope ||
278
0
                ext_inst_key == DebugInfoDebugNoScope ||
279
0
                ext_inst_key == DebugInfoDebugDeclare ||
280
0
                ext_inst_key == DebugInfoDebugValue) {
281
0
              local_debug_info = true;
282
0
            }
283
0
          }
284
285
0
          if (local_debug_info) {
286
0
            if (_.in_function_body() == false) {
287
              // DebugScope, DebugNoScope, DebugDeclare, DebugValue must
288
              // appear in a function body.
289
0
              return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
290
0
                     << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
291
0
                     << "of debug info extension must appear in a function "
292
0
                     << "body";
293
0
            }
294
0
          } 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
0
            if (_.current_layout_section() < kLayoutTypes ||
300
0
                _.current_layout_section() >= kLayoutFunctionDeclarations) {
301
0
              return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
302
0
                     << "Debug info extension instructions other than "
303
0
                     << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
304
0
                     << "must appear between section 9 (types, constants, "
305
0
                     << "global variables) and section 10 (function "
306
0
                     << "declarations)";
307
0
            }
308
0
          }
309
55.6k
        } 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
31
          if (_.current_layout_section() < kLayoutTypes) {
314
0
            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
315
0
                   << "Non-semantic OpExtInst must not appear before types "
316
0
                   << "section";
317
31
          } else if (_.in_function_body() && _.in_block() == false) {
318
2
            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
319
2
                   << "Non-semantic OpExtInst within function definition must "
320
2
                      "appear in a block";
321
2
          }
322
55.5k
        } else {
323
          // otherwise they must be used in a block
324
55.5k
          if (_.in_block() == false) {
325
1
            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
326
1
                   << spvOpcodeString(opcode) << " must appear in a block";
327
1
          }
328
55.5k
        }
329
55.5k
        break;
330
331
2.20M
      default:
332
2.20M
        if (_.current_layout_section() == kLayoutFunctionDeclarations &&
333
0
            _.in_function_body()) {
334
0
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
335
0
                 << "A function must begin with a label";
336
2.20M
        } else {
337
2.20M
          if (_.in_block() == false) {
338
103
            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
339
103
                   << spvOpcodeString(opcode) << " must appear in a block";
340
103
          }
341
2.20M
        }
342
2.20M
        break;
343
2.70M
    }
344
2.70M
  } else {
345
72
    _.ProgressToNextLayoutSectionOrder();
346
    // All function sections have been processed. Recursively call
347
    // ModuleLayoutPass to process the next section of the module
348
72
    return ModuleLayoutPass(_, inst);
349
72
  }
350
2.70M
  return SPV_SUCCESS;
351
2.70M
}
352
353
spv_result_t GraphScopedInstructions(ValidationState_t& _,
354
72
                                     const Instruction* inst, spv::Op opcode) {
355
72
  if (_.IsOpcodeInCurrentLayoutSection(opcode)) {
356
16
    switch (opcode) {
357
3
      case spv::Op::OpGraphARM: {
358
3
        if (_.graph_definition_region() > kGraphDefinitionOutside) {
359
0
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
360
0
                 << "Cannot define a graph in a graph";
361
0
        }
362
3
        _.SetGraphDefinitionRegion(kGraphDefinitionBegin);
363
3
      } break;
364
2
      case spv::Op::OpGraphInputARM: {
365
2
        if ((_.graph_definition_region() != kGraphDefinitionBegin) &&
366
2
            (_.graph_definition_region() != kGraphDefinitionInputs)) {
367
2
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
368
2
                 << "OpGraphInputARM"
369
2
                 << " must immediately follow an OpGraphARM or OpGraphInputARM "
370
2
                    "instruction.";
371
2
        }
372
0
        _.SetGraphDefinitionRegion(kGraphDefinitionInputs);
373
0
      } break;
374
3
      case spv::Op::OpGraphSetOutputARM: {
375
3
        if ((_.graph_definition_region() != kGraphDefinitionBegin) &&
376
3
            (_.graph_definition_region() != kGraphDefinitionInputs) &&
377
3
            (_.graph_definition_region() != kGraphDefinitionBody) &&
378
3
            (_.graph_definition_region() != kGraphDefinitionOutputs)) {
379
3
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
380
3
                 << "Op" << spvOpcodeString(opcode)
381
3
                 << " must immediately precede an OpGraphEndARM or "
382
3
                    "OpGraphSetOutputARM instruction.";
383
3
        }
384
0
        _.SetGraphDefinitionRegion(kGraphDefinitionOutputs);
385
0
      } break;
386
3
      case spv::Op::OpGraphEndARM: {
387
3
        if (_.graph_definition_region() != kGraphDefinitionOutputs) {
388
3
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
389
3
                 << spvOpcodeString(opcode)
390
3
                 << " must be preceded by at least one OpGraphSetOutputARM "
391
3
                    "instruction";
392
3
        }
393
0
        _.SetGraphDefinitionRegion(kGraphDefinitionOutside);
394
0
      } break;
395
5
      case spv::Op::OpGraphEntryPointARM:
396
5
        if (_.graph_definition_region() != kGraphDefinitionOutside) {
397
0
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
398
0
                 << spvOpcodeString(opcode)
399
0
                 << " cannot appear in the definition of a graph";
400
0
        }
401
5
        break;
402
5
      default:
403
0
        if ((opcode == spv::Op::OpExtInst) &&
404
0
            spvExtInstIsNonSemantic(inst->ext_inst_type())) {
405
0
          break;
406
0
        }
407
408
0
        if (_.graph_definition_region() == kGraphDefinitionOutside) {
409
0
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
410
0
                 << "Op" << spvOpcodeString(opcode)
411
0
                 << " must appear in a graph body";
412
0
        }
413
0
        if (_.graph_definition_region() == kGraphDefinitionOutputs) {
414
0
          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
415
0
                 << spvOpcodeString(opcode)
416
0
                 << " cannot appear after a graph output instruction";
417
0
        }
418
0
        _.SetGraphDefinitionRegion(kGraphDefinitionBody);
419
0
        break;
420
16
    }
421
56
  } else {
422
56
    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
423
56
           << "Op" << spvOpcodeString(opcode)
424
56
           << " cannot appear in the graph definitions section";
425
56
  }
426
8
  return SPV_SUCCESS;
427
72
}
428
}  // namespace
429
430
// TODO(umar): Check linkage capabilities for function declarations
431
// TODO(umar): Better error messages
432
// NOTE: This function does not handle CFG related validation
433
// Performs logical layout validation. See Section 2.4
434
14.5M
spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) {
435
14.5M
  const spv::Op opcode = inst->opcode();
436
437
14.5M
  switch (_.current_layout_section()) {
438
54.8k
    case kLayoutCapabilities:
439
61.3k
    case kLayoutExtensions:
440
68.9k
    case kLayoutExtInstImport:
441
90.2k
    case kLayoutMemoryModel:
442
90.2k
    case kLayoutSamplerImageAddressMode:
443
102k
    case kLayoutEntryPoint:
444
124k
    case kLayoutExecutionMode:
445
132k
    case kLayoutDebug1:
446
232k
    case kLayoutDebug2:
447
232k
    case kLayoutDebug3:
448
11.4M
    case kLayoutAnnotations:
449
11.8M
    case kLayoutTypes:
450
11.8M
      if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error;
451
11.8M
      break;
452
11.8M
    case kLayoutFunctionDeclarations:
453
2.70M
    case kLayoutFunctionDefinitions:
454
2.70M
      if (auto error = FunctionScopedInstructions(_, inst, opcode)) {
455
247
        return error;
456
247
      }
457
2.70M
      break;
458
2.70M
    case kLayoutGraphDefinitions:
459
72
      if (auto error = GraphScopedInstructions(_, inst, opcode)) {
460
64
        return error;
461
64
      }
462
8
      break;
463
14.5M
  }
464
14.5M
  return SPV_SUCCESS;
465
14.5M
}
466
467
}  // namespace val
468
}  // namespace spvtools