Coverage Report

Created: 2026-09-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/val/validate_mode_setting.cpp
Line
Count
Source
1
// Copyright (c) 2018 Google LLC.
2
// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
3
// reserved.
4
// Copyright (C) 2026 Qualcomm Technologies, Inc.
5
//
6
// Licensed under the Apache License, Version 2.0 (the "License");
7
// you may not use this file except in compliance with the License.
8
// You may obtain a copy of the License at
9
//
10
//     http://www.apache.org/licenses/LICENSE-2.0
11
//
12
// Unless required by applicable law or agreed to in writing, software
13
// distributed under the License is distributed on an "AS IS" BASIS,
14
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
// See the License for the specific language governing permissions and
16
// limitations under the License.
17
//
18
#include <algorithm>
19
20
#include "source/opcode.h"
21
#include "source/spirv_target_env.h"
22
#include "source/table2.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
// TODO - Make a common util if someone else needs it too outside this file
32
0
const char* ExecutionModelToString(spv::ExecutionModel value) {
33
0
  switch (value) {
34
0
    case spv::ExecutionModel::Vertex:
35
0
      return "Vertex";
36
0
    case spv::ExecutionModel::TessellationControl:
37
0
      return "TessellationControl";
38
0
    case spv::ExecutionModel::TessellationEvaluation:
39
0
      return "TessellationEvaluation";
40
0
    case spv::ExecutionModel::Geometry:
41
0
      return "Geometry";
42
0
    case spv::ExecutionModel::Fragment:
43
0
      return "Fragment";
44
0
    case spv::ExecutionModel::GLCompute:
45
0
      return "GLCompute";
46
0
    case spv::ExecutionModel::Kernel:
47
0
      return "Kernel";
48
0
    case spv::ExecutionModel::TaskNV:
49
0
      return "TaskNV";
50
0
    case spv::ExecutionModel::MeshNV:
51
0
      return "MeshNV";
52
0
    case spv::ExecutionModel::RayGenerationKHR:
53
0
      return "RayGenerationKHR";
54
0
    case spv::ExecutionModel::IntersectionKHR:
55
0
      return "IntersectionKHR";
56
0
    case spv::ExecutionModel::AnyHitKHR:
57
0
      return "AnyHitKHR";
58
0
    case spv::ExecutionModel::ClosestHitKHR:
59
0
      return "ClosestHitKHR";
60
0
    case spv::ExecutionModel::MissKHR:
61
0
      return "MissKHR";
62
0
    case spv::ExecutionModel::CallableKHR:
63
0
      return "CallableKHR";
64
0
    case spv::ExecutionModel::TaskEXT:
65
0
      return "TaskEXT";
66
0
    case spv::ExecutionModel::MeshEXT:
67
0
      return "MeshEXT";
68
0
    default:
69
0
      return "Unknown";
70
0
  }
71
0
}
72
73
24.7k
spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
74
24.7k
  const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
75
24.7k
  auto entry_point = _.FindDef(entry_point_id);
76
24.7k
  if (!entry_point || spv::Op::OpFunction != entry_point->opcode()) {
77
14
    return _.diag(SPV_ERROR_INVALID_ID, inst)
78
14
           << "OpEntryPoint Entry Point <id> " << _.getIdName(entry_point_id)
79
14
           << " is not a function.";
80
14
  }
81
82
  // Only check the shader execution models
83
24.7k
  const spv::ExecutionModel execution_model =
84
24.7k
      inst->GetOperandAs<spv::ExecutionModel>(0);
85
24.7k
  if (execution_model != spv::ExecutionModel::Kernel) {
86
24.6k
    const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
87
24.6k
    const auto entry_point_type = _.FindDef(entry_point_type_id);
88
24.6k
    if (!entry_point_type || 3 != entry_point_type->words().size()) {
89
7
      return _.diag(SPV_ERROR_INVALID_ID, inst)
90
7
             << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> "
91
7
             << _.getIdName(entry_point_id)
92
7
             << "s function parameter count is not zero.";
93
7
    }
94
24.6k
  }
95
96
24.7k
  auto return_type = _.FindDef(entry_point->type_id());
97
24.7k
  if (!return_type || spv::Op::OpTypeVoid != return_type->opcode()) {
98
7
    return _.diag(SPV_ERROR_INVALID_ID, inst)
99
7
           << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> "
100
7
           << _.getIdName(entry_point_id)
101
7
           << "s function return type is not void.";
102
7
  }
103
104
24.7k
  const auto* execution_modes = _.GetExecutionModes(entry_point_id);
105
59.6k
  auto has_mode = [&execution_modes](spv::ExecutionMode mode) {
106
59.6k
    return execution_modes && execution_modes->count(mode);
107
59.6k
  };
108
109
24.7k
  if (_.HasCapability(spv::Capability::Shader)) {
110
24.6k
    switch (execution_model) {
111
19.4k
      case spv::ExecutionModel::Fragment:
112
19.4k
        if (has_mode(spv::ExecutionMode::OriginUpperLeft) &&
113
18.3k
            has_mode(spv::ExecutionMode::OriginLowerLeft)) {
114
3
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
115
3
                 << "Fragment execution model entry points can only specify "
116
3
                    "one of OriginUpperLeft or OriginLowerLeft execution "
117
3
                    "modes.";
118
3
        }
119
19.4k
        if (!has_mode(spv::ExecutionMode::OriginUpperLeft) &&
120
1.11k
            !has_mode(spv::ExecutionMode::OriginLowerLeft)) {
121
27
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
122
27
                 << "Fragment execution model entry points require either an "
123
27
                    "OriginUpperLeft or OriginLowerLeft execution mode.";
124
27
        }
125
19.4k
        if (execution_modes &&
126
19.4k
            1 < std::count_if(execution_modes->begin(), execution_modes->end(),
127
20.8k
                              [](const spv::ExecutionMode& mode) {
128
20.8k
                                switch (mode) {
129
64
                                  case spv::ExecutionMode::DepthGreater:
130
110
                                  case spv::ExecutionMode::DepthLess:
131
365
                                  case spv::ExecutionMode::DepthUnchanged:
132
365
                                    return true;
133
20.5k
                                  default:
134
20.5k
                                    return false;
135
20.8k
                                }
136
20.8k
                              })) {
137
4
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
138
4
                 << "Fragment execution model entry points can specify at most "
139
4
                    "one of DepthGreater, DepthLess or DepthUnchanged "
140
4
                    "execution modes.";
141
4
        }
142
19.4k
        if (execution_modes &&
143
19.4k
            1 < std::count_if(
144
19.4k
                    execution_modes->begin(), execution_modes->end(),
145
20.8k
                    [](const spv::ExecutionMode& mode) {
146
20.8k
                      switch (mode) {
147
0
                        case spv::ExecutionMode::PixelInterlockOrderedEXT:
148
0
                        case spv::ExecutionMode::PixelInterlockUnorderedEXT:
149
0
                        case spv::ExecutionMode::SampleInterlockOrderedEXT:
150
0
                        case spv::ExecutionMode::SampleInterlockUnorderedEXT:
151
0
                        case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
152
0
                        case spv::ExecutionMode::
153
0
                            ShadingRateInterlockUnorderedEXT:
154
0
                          return true;
155
20.8k
                        default:
156
20.8k
                          return false;
157
20.8k
                      }
158
20.8k
                    })) {
159
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
160
0
                 << "Fragment execution model entry points can specify at most "
161
0
                    "one fragment shader interlock execution mode.";
162
0
        }
163
19.4k
        if (execution_modes &&
164
19.4k
            1 < std::count_if(
165
19.4k
                    execution_modes->begin(), execution_modes->end(),
166
20.8k
                    [](const spv::ExecutionMode& mode) {
167
20.8k
                      switch (mode) {
168
0
                        case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
169
0
                        case spv::ExecutionMode::StencilRefLessFrontAMD:
170
0
                        case spv::ExecutionMode::StencilRefGreaterFrontAMD:
171
0
                          return true;
172
20.8k
                        default:
173
20.8k
                          return false;
174
20.8k
                      }
175
20.8k
                    })) {
176
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
177
0
                 << "Fragment execution model entry points can specify at most "
178
0
                    "one of StencilRefUnchangedFrontAMD, "
179
0
                    "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
180
0
                    "execution modes.";
181
0
        }
182
19.4k
        if (execution_modes &&
183
19.4k
            1 < std::count_if(
184
19.4k
                    execution_modes->begin(), execution_modes->end(),
185
20.8k
                    [](const spv::ExecutionMode& mode) {
186
20.8k
                      switch (mode) {
187
0
                        case spv::ExecutionMode::StencilRefUnchangedBackAMD:
188
0
                        case spv::ExecutionMode::StencilRefLessBackAMD:
189
0
                        case spv::ExecutionMode::StencilRefGreaterBackAMD:
190
0
                          return true;
191
20.8k
                        default:
192
20.8k
                          return false;
193
20.8k
                      }
194
20.8k
                    })) {
195
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
196
0
                 << "Fragment execution model entry points can specify at most "
197
0
                    "one of StencilRefUnchangedBackAMD, "
198
0
                    "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
199
0
                    "execution modes.";
200
0
        }
201
19.4k
        break;
202
19.4k
      case spv::ExecutionModel::TessellationControl:
203
1.14k
      case spv::ExecutionModel::TessellationEvaluation:
204
1.14k
        if (execution_modes &&
205
596
            1 < std::count_if(
206
596
                    execution_modes->begin(), execution_modes->end(),
207
1.51k
                    [](const spv::ExecutionMode& mode) {
208
1.51k
                      switch (mode) {
209
150
                        case spv::ExecutionMode::SpacingEqual:
210
193
                        case spv::ExecutionMode::SpacingFractionalEven:
211
237
                        case spv::ExecutionMode::SpacingFractionalOdd:
212
237
                          return true;
213
1.27k
                        default:
214
1.27k
                          return false;
215
1.51k
                      }
216
1.51k
                    })) {
217
4
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
218
4
                 << "Tessellation execution model entry points can specify at "
219
4
                    "most one of SpacingEqual, SpacingFractionalOdd or "
220
4
                    "SpacingFractionalEven execution modes.";
221
4
        }
222
1.13k
        if (execution_modes &&
223
592
            1 < std::count_if(execution_modes->begin(), execution_modes->end(),
224
1.50k
                              [](const spv::ExecutionMode& mode) {
225
1.50k
                                switch (mode) {
226
100
                                  case spv::ExecutionMode::Triangles:
227
168
                                  case spv::ExecutionMode::Quads:
228
252
                                  case spv::ExecutionMode::Isolines:
229
252
                                    return true;
230
1.25k
                                  default:
231
1.25k
                                    return false;
232
1.50k
                                }
233
1.50k
                              })) {
234
3
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
235
3
                 << "Tessellation execution model entry points can specify at "
236
3
                    "most one of Triangles, Quads or Isolines execution modes.";
237
3
        }
238
1.13k
        if (execution_modes &&
239
589
            1 < std::count_if(execution_modes->begin(), execution_modes->end(),
240
1.49k
                              [](const spv::ExecutionMode& mode) {
241
1.49k
                                switch (mode) {
242
65
                                  case spv::ExecutionMode::VertexOrderCw:
243
223
                                  case spv::ExecutionMode::VertexOrderCcw:
244
223
                                    return true;
245
1.27k
                                  default:
246
1.27k
                                    return false;
247
1.49k
                                }
248
1.49k
                              })) {
249
4
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
250
4
                 << "Tessellation execution model entry points can specify at "
251
4
                    "most one of VertexOrderCw or VertexOrderCcw execution "
252
4
                    "modes.";
253
4
        }
254
1.13k
        break;
255
1.13k
      case spv::ExecutionModel::Geometry:
256
229
        if (!execution_modes ||
257
225
            1 != std::count_if(
258
225
                     execution_modes->begin(), execution_modes->end(),
259
618
                     [](const spv::ExecutionMode& mode) {
260
618
                       switch (mode) {
261
40
                         case spv::ExecutionMode::InputPoints:
262
77
                         case spv::ExecutionMode::InputLines:
263
185
                         case spv::ExecutionMode::InputLinesAdjacency:
264
189
                         case spv::ExecutionMode::Triangles:
265
224
                         case spv::ExecutionMode::InputTrianglesAdjacency:
266
224
                           return true;
267
394
                         default:
268
394
                           return false;
269
618
                       }
270
618
                     })) {
271
18
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
272
18
                 << "Geometry execution model entry points must specify "
273
18
                    "exactly one of InputPoints, InputLines, "
274
18
                    "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
275
18
                    "execution modes.";
276
18
        }
277
211
        if (!execution_modes ||
278
211
            1 != std::count_if(execution_modes->begin(), execution_modes->end(),
279
586
                               [](const spv::ExecutionMode& mode) {
280
586
                                 switch (mode) {
281
49
                                   case spv::ExecutionMode::OutputPoints:
282
91
                                   case spv::ExecutionMode::OutputLineStrip:
283
202
                                   case spv::ExecutionMode::OutputTriangleStrip:
284
202
                                     return true;
285
384
                                   default:
286
384
                                     return false;
287
586
                                 }
288
586
                               })) {
289
13
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
290
13
                 << "Geometry execution model entry points must specify "
291
13
                    "exactly one of OutputPoints, OutputLineStrip or "
292
13
                    "OutputTriangleStrip execution modes.";
293
13
        }
294
198
        break;
295
198
      case spv::ExecutionModel::MeshEXT:
296
0
        if (!execution_modes ||
297
0
            1 != std::count_if(execution_modes->begin(), execution_modes->end(),
298
0
                               [](const spv::ExecutionMode& mode) {
299
0
                                 switch (mode) {
300
0
                                   case spv::ExecutionMode::OutputPoints:
301
0
                                   case spv::ExecutionMode::OutputLinesEXT:
302
0
                                   case spv::ExecutionMode::OutputTrianglesEXT:
303
0
                                     return true;
304
0
                                   default:
305
0
                                     return false;
306
0
                                 }
307
0
                               })) {
308
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
309
0
                 << "MeshEXT execution model entry points must specify exactly "
310
0
                    "one of OutputPoints, OutputLinesEXT, or "
311
0
                    "OutputTrianglesEXT Execution Modes.";
312
0
        } else if (2 != std::count_if(
313
0
                            execution_modes->begin(), execution_modes->end(),
314
0
                            [](const spv::ExecutionMode& mode) {
315
0
                              switch (mode) {
316
0
                                case spv::ExecutionMode::OutputPrimitivesEXT:
317
0
                                case spv::ExecutionMode::OutputVertices:
318
0
                                  return true;
319
0
                                default:
320
0
                                  return false;
321
0
                              }
322
0
                            })) {
323
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
324
0
                 << "MeshEXT execution model entry points must specify both "
325
0
                    "OutputPrimitivesEXT and OutputVertices Execution Modes.";
326
0
        }
327
0
        break;
328
3.77k
      default:
329
3.77k
        break;
330
24.6k
    }
331
24.6k
  }
332
333
24.6k
  bool has_workgroup_size = false;
334
24.6k
  bool has_local_size_id = false;
335
1.28M
  for (auto& i : _.ordered_instructions()) {
336
1.28M
    if (i.opcode() == spv::Op::OpFunction) break;
337
1.25M
    if (i.opcode() == spv::Op::OpDecorate && i.operands().size() > 2) {
338
200k
      if (i.GetOperandAs<spv::Decoration>(1) == spv::Decoration::BuiltIn &&
339
4.14k
          i.GetOperandAs<spv::BuiltIn>(2) == spv::BuiltIn::WorkgroupSize) {
340
260
        has_workgroup_size = true;
341
260
      }
342
200k
    }
343
1.25M
    if (i.opcode() == spv::Op::OpExecutionModeId) {
344
0
      if (i.GetOperandAs<spv::ExecutionMode>(1) ==
345
0
          spv::ExecutionMode::LocalSizeId) {
346
0
        has_local_size_id = true;
347
0
      }
348
0
    }
349
1.25M
  }
350
351
24.6k
  if (spvIsVulkanEnv(_.context()->target_env)) {
352
    // SPV_QCOM_tile_shading checks
353
0
    if (execution_model == spv::ExecutionModel::GLCompute) {
354
0
      if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
355
0
        if (has_mode(spv::ExecutionMode::TileShadingRateQCOM) &&
356
0
            (has_mode(spv::ExecutionMode::LocalSize) ||
357
0
             has_mode(spv::ExecutionMode::LocalSizeId))) {
358
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
359
0
                 << _.VkErrorID(10692)
360
0
                 << "If the TileShadingRateQCOM execution mode is used, "
361
0
                 << "LocalSize and LocalSizeId must not be specified.";
362
0
        }
363
0
        if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
364
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
365
0
                 << _.VkErrorID(10687)
366
0
                 << "The NonCoherentTileAttachmentQCOM execution mode must "
367
0
                    "not be used in any stage other than fragment.";
368
0
        }
369
0
      } else {
370
0
        if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
371
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
372
0
                 << _.VkErrorID(10691)
373
0
                 << "If the TileShadingRateQCOM execution mode is used, the "
374
0
                    "TileShadingQCOM capability must be enabled.";
375
0
        }
376
0
      }
377
0
    } else {
378
0
      if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
379
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
380
0
               << _.VkErrorID(10688)
381
0
               << "The TileShadingRateQCOM execution mode must not be used "
382
0
                  "in any stage other than compute.";
383
0
      }
384
0
      if (execution_model != spv::ExecutionModel::Fragment) {
385
0
        if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
386
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
387
0
                 << _.VkErrorID(10687)
388
0
                 << "The NonCoherentTileAttachmentQCOM execution mode must "
389
0
                    "not be used in any stage other than fragment.";
390
0
        }
391
0
        if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
392
0
          return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
393
0
                 << _.VkErrorID(10686)
394
0
                 << "The TileShadingQCOM capability must not be enabled in "
395
0
                    "any stage other than compute or fragment.";
396
0
        }
397
0
      } else {
398
0
        if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
399
0
          if (!_.HasCapability(spv::Capability::TileShadingQCOM)) {
400
0
            return _.diag(SPV_ERROR_INVALID_DATA, inst)
401
0
                   << _.VkErrorID(10690)
402
0
                   << "If the NonCoherentTileAttachmentReadQCOM execution "
403
0
                      "mode is used, the TileShadingQCOM capability must be "
404
0
                      "enabled.";
405
0
          }
406
0
        }
407
0
      }
408
0
    }
409
410
0
    switch (execution_model) {
411
0
      case spv::ExecutionModel::GLCompute:
412
0
      case spv::ExecutionModel::MeshEXT:
413
0
      case spv::ExecutionModel::MeshNV:
414
0
      case spv::ExecutionModel::TaskEXT:
415
0
      case spv::ExecutionModel::TaskNV:
416
0
        if (!has_mode(spv::ExecutionMode::LocalSize) && !has_workgroup_size &&
417
0
            !has_local_size_id &&
418
0
            !has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
419
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
420
0
                 << _.VkErrorID(10685) << "In the Vulkan environment, "
421
0
                 << ExecutionModelToString(execution_model)
422
0
                 << " execution model "
423
0
                    "entry points require either the "
424
0
                 << (_.HasCapability(spv::Capability::TileShadingQCOM)
425
0
                         ? "TileShadingRateQCOM, "
426
0
                         : "")
427
0
                 << "LocalSize or LocalSizeId execution mode or an object "
428
0
                    "decorated with WorkgroupSize must be specified.";
429
0
        }
430
0
        break;
431
0
      default:
432
0
        break;
433
0
    }
434
0
  }
435
436
  // WorkgroupSize decoration takes precedence over any LocalSize or LocalSizeId
437
  // execution mode, so the values can be ignored
438
24.6k
  if (_.EntryPointHasLocalSizeOrId(entry_point_id) && !has_workgroup_size) {
439
650
    const Instruction* local_size_inst =
440
650
        _.EntryPointLocalSizeOrId(entry_point_id);
441
650
    if (local_size_inst) {
442
650
      const auto mode = local_size_inst->GetOperandAs<spv::ExecutionMode>(1);
443
650
      const uint32_t operand_x = local_size_inst->GetOperandAs<uint32_t>(2);
444
650
      const uint32_t operand_y = local_size_inst->GetOperandAs<uint32_t>(3);
445
650
      const uint32_t operand_z = local_size_inst->GetOperandAs<uint32_t>(4);
446
650
      if (mode == spv::ExecutionMode::LocalSize) {
447
650
        const uint64_t product_size = operand_x * operand_y * operand_z;
448
650
        if (product_size == 0) {
449
6
          return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
450
6
                 << "Local Size execution mode must not have a product of zero "
451
6
                    "(X "
452
6
                    "= "
453
6
                 << operand_x << ", Y = " << operand_y << ", Z = " << operand_z
454
6
                 << ").";
455
6
        }
456
644
        if (has_mode(spv::ExecutionMode::DerivativeGroupQuadsKHR)) {
457
0
          if (operand_x % 2 != 0 || operand_y % 2 != 0) {
458
0
            return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
459
0
                   << _.VkErrorID(10151)
460
0
                   << "Local Size execution mode dimensions is "
461
0
                      "(X = "
462
0
                   << operand_x << ", Y = " << operand_y
463
0
                   << ") but Entry Point id " << entry_point_id
464
0
                   << " also has an DerivativeGroupQuadsKHR execution mode, so "
465
0
                      "both dimensions must be a multiple of 2";
466
0
          }
467
0
        }
468
644
        if (has_mode(spv::ExecutionMode::DerivativeGroupLinearKHR)) {
469
0
          if (product_size % 4 != 0) {
470
0
            return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
471
0
                   << _.VkErrorID(10152)
472
0
                   << "Local Size execution mode dimensions is (X = "
473
0
                   << operand_x << ", Y = " << operand_y
474
0
                   << ", Z = " << operand_z << ") but Entry Point id "
475
0
                   << entry_point_id
476
0
                   << " also has an DerivativeGroupLinearKHR execution mode, "
477
0
                      "so "
478
0
                      "the product ("
479
0
                   << product_size << ") must be a multiple of 4";
480
0
          }
481
0
        }
482
644
      } else if (mode == spv::ExecutionMode::LocalSizeId) {
483
        // can only validate product if static and not spec constant
484
        // (This is done for us in EvalConstantValUint64)
485
0
        uint64_t x_size, y_size, z_size;
486
0
        bool static_x = _.EvalConstantValUint64(operand_x, &x_size);
487
0
        bool static_y = _.EvalConstantValUint64(operand_y, &y_size);
488
0
        bool static_z = _.EvalConstantValUint64(operand_z, &z_size);
489
0
        if (static_x && static_y && static_z) {
490
0
          const uint64_t product_size = x_size * y_size * z_size;
491
0
          if (product_size == 0) {
492
0
            return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
493
0
                   << "LocalSizeId execution mode must not have a product of "
494
0
                      "zero "
495
0
                      "(X = "
496
0
                   << x_size << ", Y = " << y_size << ", Z = " << z_size
497
0
                   << ").";
498
0
          }
499
0
          if (has_mode(spv::ExecutionMode::DerivativeGroupQuadsKHR)) {
500
0
            if (x_size % 2 != 0 || y_size % 2 != 0) {
501
0
              return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
502
0
                     << _.VkErrorID(10151)
503
0
                     << "LocalSizeId execution mode dimensions is "
504
0
                        "(X = "
505
0
                     << x_size << ", Y = " << y_size << ") but Entry Point id "
506
0
                     << entry_point_id
507
0
                     << " also has an DerivativeGroupQuadsKHR execution mode, "
508
0
                        "so "
509
0
                        "both dimensions must be a multiple of 2";
510
0
            }
511
0
          }
512
0
          if (has_mode(spv::ExecutionMode::DerivativeGroupLinearKHR)) {
513
0
            if (product_size % 4 != 0) {
514
0
              return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
515
0
                     << _.VkErrorID(10152)
516
0
                     << "LocalSizeId execution mode dimensions is (X = "
517
0
                     << x_size << ", Y = " << y_size << ", Z = " << z_size
518
0
                     << ") but Entry Point id " << entry_point_id
519
0
                     << " also has an DerivativeGroupLinearKHR execution mode, "
520
0
                        "so "
521
0
                        "the product ("
522
0
                     << product_size << ") must be a multiple of 4";
523
0
            }
524
0
          }
525
0
        }
526
0
      }
527
650
    }
528
650
  }
529
530
24.6k
  return SPV_SUCCESS;
531
24.6k
}
532
533
spv_result_t ValidateExecutionMode(ValidationState_t& _,
534
30.0k
                                   const Instruction* inst) {
535
30.0k
  const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
536
30.0k
  const auto found = std::find(_.entry_points().cbegin(),
537
30.0k
                               _.entry_points().cend(), entry_point_id);
538
30.0k
  if (found == _.entry_points().cend()) {
539
77
    return _.diag(SPV_ERROR_INVALID_ID, inst)
540
77
           << "OpExecutionMode Entry Point <id> " << _.getIdName(entry_point_id)
541
77
           << " is not the Entry Point "
542
77
              "operand of an OpEntryPoint.";
543
77
  }
544
545
30.0k
  const auto mode = inst->GetOperandAs<spv::ExecutionMode>(1);
546
30.0k
  if (inst->opcode() == spv::Op::OpExecutionModeId) {
547
0
    bool valid_mode = false;
548
0
    switch (mode) {
549
0
      case spv::ExecutionMode::SubgroupsPerWorkgroupId:
550
0
      case spv::ExecutionMode::LocalSizeHintId:
551
0
      case spv::ExecutionMode::LocalSizeId:
552
0
      case spv::ExecutionMode::OpacityMicromapIdKHR:
553
0
      case spv::ExecutionMode::FPFastMathDefault:
554
0
      case spv::ExecutionMode::MaximumRegistersIdINTEL:
555
0
      case spv::ExecutionMode::IsApiEntryAMDX:
556
0
      case spv::ExecutionMode::MaxNodeRecursionAMDX:
557
0
      case spv::ExecutionMode::MaxNumWorkgroupsAMDX:
558
0
      case spv::ExecutionMode::ShaderIndexAMDX:
559
0
      case spv::ExecutionMode::SharesInputWithAMDX:
560
0
      case spv::ExecutionMode::StaticNumWorkgroupsAMDX:
561
0
        valid_mode = true;
562
0
        break;
563
0
      default:
564
0
        valid_mode = false;
565
0
        break;
566
0
    }
567
0
    if (!valid_mode) {
568
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
569
0
             << "OpExecutionModeId is only valid when the Mode operand is an "
570
0
                "execution mode that takes Extra Operands that are id "
571
0
                "operands.";
572
0
    }
573
574
0
    size_t operand_count = inst->operands().size();
575
0
    for (size_t i = 2; i < operand_count; ++i) {
576
0
      const auto operand_id = inst->GetOperandAs<uint32_t>(i);
577
0
      const auto* operand_inst = _.FindDef(operand_id);
578
0
      switch (mode) {
579
0
        case spv::ExecutionMode::SubgroupsPerWorkgroupId:
580
0
        case spv::ExecutionMode::LocalSizeHintId:
581
0
        case spv::ExecutionMode::LocalSizeId:
582
0
        case spv::ExecutionMode::IsApiEntryAMDX:
583
0
        case spv::ExecutionMode::MaxNodeRecursionAMDX:
584
0
        case spv::ExecutionMode::MaxNumWorkgroupsAMDX:
585
0
        case spv::ExecutionMode::ShaderIndexAMDX:
586
0
        case spv::ExecutionMode::SharesInputWithAMDX:
587
0
        case spv::ExecutionMode::StaticNumWorkgroupsAMDX:
588
0
          if (!spvOpcodeIsConstant(operand_inst->opcode())) {
589
0
            return _.diag(SPV_ERROR_INVALID_ID, inst)
590
0
                   << "For OpExecutionModeId all Extra Operand ids must be "
591
0
                      "constant instructions.";
592
0
          }
593
0
          break;
594
0
        case spv::ExecutionMode::FPFastMathDefault:
595
0
          if (i == 2) {
596
0
            if (!_.IsFloatScalarType(operand_id)) {
597
0
              return _.diag(SPV_ERROR_INVALID_ID, inst)
598
0
                     << "The Target Type operand must be a floating-point "
599
0
                        "scalar type";
600
0
            }
601
0
          } else {
602
0
            bool is_int32 = false;
603
0
            bool is_const = false;
604
0
            uint32_t value = 0;
605
0
            std::tie(is_int32, is_const, value) =
606
0
                _.EvalInt32IfConst(operand_id);
607
0
            if (is_int32 && is_const) {
608
              // Valid values include up to 0x00040000 (AllowTransform).
609
0
              uint32_t invalid_mask = 0xfff80000;
610
0
              if ((invalid_mask & value) != 0) {
611
0
                return _.diag(SPV_ERROR_INVALID_ID, inst)
612
0
                       << "The Fast Math Default operand is an invalid bitmask "
613
0
                          "value";
614
0
              }
615
0
              if (value &
616
0
                  static_cast<uint32_t>(spv::FPFastMathModeMask::Fast)) {
617
0
                return _.diag(SPV_ERROR_INVALID_ID, inst)
618
0
                       << "The Fast Math Default operand must not include Fast";
619
0
              }
620
0
              const auto reassoc_contract =
621
0
                  spv::FPFastMathModeMask::AllowContract |
622
0
                  spv::FPFastMathModeMask::AllowReassoc;
623
0
              if ((value & static_cast<uint32_t>(
624
0
                               spv::FPFastMathModeMask::AllowTransform)) != 0 &&
625
0
                  ((value & static_cast<uint32_t>(reassoc_contract)) !=
626
0
                   static_cast<uint32_t>(reassoc_contract))) {
627
0
                return _.diag(SPV_ERROR_INVALID_ID, inst)
628
0
                       << "The Fast Math Default operand must include "
629
0
                          "AllowContract and AllowReassoc when AllowTransform "
630
0
                          "is specified";
631
0
              }
632
0
            } else {
633
0
              return _.diag(SPV_ERROR_INVALID_ID, inst)
634
0
                     << "The Fast Math Default operand must be a "
635
0
                        "non-specialization constant";
636
0
            }
637
0
          }
638
0
          break;
639
0
        case spv::ExecutionMode::OpacityMicromapIdKHR: {
640
0
          spv::Op operand_opcode = operand_inst->opcode();
641
0
          if (!spvOpcodeIsConstant(operand_opcode) ||
642
0
              !_.IsBoolScalarType(operand_inst->type_id())) {
643
0
            return _.diag(SPV_ERROR_INVALID_DATA, operand_inst)
644
0
                   << "OpacityMicromapIdKHR's operand must be an <id> "
645
0
                      "of a constant instruction of OpTypeBool.";
646
0
          }
647
0
          break;
648
0
        }
649
0
        default:
650
0
          break;
651
0
      }
652
0
    }
653
30.0k
  } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
654
30.0k
             mode == spv::ExecutionMode::LocalSizeHintId ||
655
30.0k
             mode == spv::ExecutionMode::LocalSizeId ||
656
30.0k
             mode == spv::ExecutionMode::FPFastMathDefault ||
657
30.0k
             mode == spv::ExecutionMode::IsApiEntryAMDX ||
658
30.0k
             mode == spv::ExecutionMode::MaxNodeRecursionAMDX ||
659
30.0k
             mode == spv::ExecutionMode::MaxNumWorkgroupsAMDX ||
660
30.0k
             mode == spv::ExecutionMode::ShaderIndexAMDX ||
661
30.0k
             mode == spv::ExecutionMode::SharesInputWithAMDX ||
662
30.0k
             mode == spv::ExecutionMode::StaticNumWorkgroupsAMDX) {
663
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
664
0
           << "OpExecutionMode is only valid when the Mode operand is an "
665
0
              "execution mode that takes no Extra Operands, or takes Extra "
666
0
              "Operands that are not id operands.";
667
0
  }
668
669
30.0k
  const bool is_vulkan_env = (spvIsVulkanEnv(_.context()->target_env));
670
30.0k
  const auto* models = _.GetExecutionModels(entry_point_id);
671
30.0k
  switch (mode) {
672
48
    case spv::ExecutionMode::Invocations:
673
188
    case spv::ExecutionMode::InputPoints:
674
345
    case spv::ExecutionMode::InputLines:
675
463
    case spv::ExecutionMode::InputLinesAdjacency:
676
550
    case spv::ExecutionMode::InputTrianglesAdjacency:
677
732
    case spv::ExecutionMode::OutputLineStrip:
678
1.61k
    case spv::ExecutionMode::OutputTriangleStrip:
679
1.61k
      if (!std::all_of(models->begin(), models->end(),
680
1.61k
                       [](const spv::ExecutionModel& model) {
681
1.61k
                         return model == spv::ExecutionModel::Geometry;
682
1.61k
                       })) {
683
23
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
684
23
               << "Execution mode can only be used with the Geometry execution "
685
23
                  "model.";
686
23
      }
687
1.59k
      break;
688
1.59k
    case spv::ExecutionMode::OutputPoints:
689
117
      if (!std::all_of(
690
117
              models->begin(), models->end(),
691
120
              [&_](const spv::ExecutionModel& model) {
692
120
                switch (model) {
693
113
                  case spv::ExecutionModel::Geometry:
694
113
                    return true;
695
0
                  case spv::ExecutionModel::MeshNV:
696
0
                    return _.HasCapability(spv::Capability::MeshShadingNV);
697
0
                  case spv::ExecutionModel::MeshEXT:
698
0
                    return _.HasCapability(spv::Capability::MeshShadingEXT);
699
7
                  default:
700
7
                    return false;
701
120
                }
702
120
              })) {
703
7
        if (_.HasCapability(spv::Capability::MeshShadingNV) ||
704
7
            _.HasCapability(spv::Capability::MeshShadingEXT)) {
705
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
706
0
                 << "Execution mode can only be used with the Geometry "
707
0
                    "MeshNV or MeshEXT execution model.";
708
7
        } else {
709
7
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
710
7
                 << "Execution mode can only be used with the Geometry "
711
7
                    "execution "
712
7
                    "model.";
713
7
        }
714
7
      }
715
110
      break;
716
396
    case spv::ExecutionMode::SpacingEqual:
717
657
    case spv::ExecutionMode::SpacingFractionalEven:
718
763
    case spv::ExecutionMode::SpacingFractionalOdd:
719
1.29k
    case spv::ExecutionMode::VertexOrderCw:
720
1.50k
    case spv::ExecutionMode::VertexOrderCcw:
721
1.73k
    case spv::ExecutionMode::PointMode:
722
1.79k
    case spv::ExecutionMode::Quads:
723
1.83k
    case spv::ExecutionMode::Isolines:
724
1.83k
      if (!std::all_of(
725
1.83k
              models->begin(), models->end(),
726
2.31k
              [](const spv::ExecutionModel& model) {
727
2.31k
                return (model == spv::ExecutionModel::TessellationControl) ||
728
744
                       (model == spv::ExecutionModel::TessellationEvaluation);
729
2.31k
              })) {
730
64
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
731
64
               << "Execution mode can only be used with a tessellation "
732
64
                  "execution model.";
733
64
      }
734
1.77k
      break;
735
1.77k
    case spv::ExecutionMode::Triangles:
736
147
      if (!std::all_of(models->begin(), models->end(),
737
208
                       [](const spv::ExecutionModel& model) {
738
208
                         switch (model) {
739
1
                           case spv::ExecutionModel::Geometry:
740
112
                           case spv::ExecutionModel::TessellationControl:
741
199
                           case spv::ExecutionModel::TessellationEvaluation:
742
199
                             return true;
743
9
                           default:
744
9
                             return false;
745
208
                         }
746
208
                       })) {
747
9
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
748
9
               << "Execution mode can only be used with a Geometry or "
749
9
                  "tessellation execution model.";
750
9
      }
751
138
      break;
752
271
    case spv::ExecutionMode::OutputVertices:
753
271
      if (!std::all_of(
754
271
              models->begin(), models->end(),
755
281
              [&_](const spv::ExecutionModel& model) {
756
281
                switch (model) {
757
84
                  case spv::ExecutionModel::Geometry:
758
180
                  case spv::ExecutionModel::TessellationControl:
759
278
                  case spv::ExecutionModel::TessellationEvaluation:
760
278
                    return true;
761
0
                  case spv::ExecutionModel::MeshNV:
762
0
                    return _.HasCapability(spv::Capability::MeshShadingNV);
763
0
                  case spv::ExecutionModel::MeshEXT:
764
0
                    return _.HasCapability(spv::Capability::MeshShadingEXT);
765
3
                  default:
766
3
                    return false;
767
281
                }
768
281
              })) {
769
3
        if (_.HasCapability(spv::Capability::MeshShadingNV) ||
770
3
            _.HasCapability(spv::Capability::MeshShadingEXT)) {
771
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
772
0
                 << "Execution mode can only be used with a Geometry, "
773
0
                    "tessellation, MeshNV or MeshEXT execution model.";
774
3
        } else {
775
3
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
776
3
                 << "Execution mode can only be used with a Geometry or "
777
3
                    "tessellation execution model.";
778
3
        }
779
3
      }
780
268
      if (is_vulkan_env) {
781
0
        if (_.HasCapability(spv::Capability::MeshShadingEXT) &&
782
0
            inst->GetOperandAs<uint32_t>(2) == 0) {
783
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
784
0
                 << _.VkErrorID(7330)
785
0
                 << "In mesh shaders using the MeshEXT Execution Model the "
786
0
                    "OutputVertices Execution Mode must be greater than 0";
787
0
        }
788
0
      }
789
268
      break;
790
268
    case spv::ExecutionMode::OutputLinesEXT:
791
0
    case spv::ExecutionMode::OutputTrianglesEXT:
792
0
    case spv::ExecutionMode::OutputPrimitivesEXT:
793
0
      if (!std::all_of(models->begin(), models->end(),
794
0
                       [](const spv::ExecutionModel& model) {
795
0
                         return (model == spv::ExecutionModel::MeshEXT ||
796
0
                                 model == spv::ExecutionModel::MeshNV);
797
0
                       })) {
798
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
799
0
               << "Execution mode can only be used with the MeshEXT or MeshNV "
800
0
                  "execution "
801
0
                  "model.";
802
0
      }
803
0
      if (mode == spv::ExecutionMode::OutputPrimitivesEXT && is_vulkan_env) {
804
0
        if (_.HasCapability(spv::Capability::MeshShadingEXT) &&
805
0
            inst->GetOperandAs<uint32_t>(2) == 0) {
806
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
807
0
                 << _.VkErrorID(7331)
808
0
                 << "In mesh shaders using the MeshEXT Execution Model the "
809
0
                    "OutputPrimitivesEXT Execution Mode must be greater than 0";
810
0
        }
811
0
      }
812
0
      break;
813
0
    case spv::ExecutionMode::QuadDerivativesKHR:
814
0
      if (!std::all_of(models->begin(), models->end(),
815
0
                       [](const spv::ExecutionModel& model) {
816
0
                         return (model == spv::ExecutionModel::Fragment ||
817
0
                                 model == spv::ExecutionModel::GLCompute);
818
0
                       })) {
819
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
820
0
               << "Execution mode can only be used with the Fragment or "
821
0
                  "GLCompute execution model.";
822
0
      }
823
0
      break;
824
2.24k
    case spv::ExecutionMode::PixelCenterInteger:
825
20.4k
    case spv::ExecutionMode::OriginUpperLeft:
826
22.8k
    case spv::ExecutionMode::OriginLowerLeft:
827
23.6k
    case spv::ExecutionMode::EarlyFragmentTests:
828
23.8k
    case spv::ExecutionMode::DepthReplacing:
829
23.9k
    case spv::ExecutionMode::DepthGreater:
830
24.0k
    case spv::ExecutionMode::DepthLess:
831
25.2k
    case spv::ExecutionMode::DepthUnchanged:
832
25.2k
    case spv::ExecutionMode::StencilRefReplacingEXT:
833
25.2k
    case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
834
25.2k
    case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
835
25.2k
    case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
836
25.2k
    case spv::ExecutionMode::PixelInterlockOrderedEXT:
837
25.2k
    case spv::ExecutionMode::PixelInterlockUnorderedEXT:
838
25.2k
    case spv::ExecutionMode::SampleInterlockOrderedEXT:
839
25.2k
    case spv::ExecutionMode::SampleInterlockUnorderedEXT:
840
25.2k
    case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
841
25.2k
    case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
842
25.2k
    case spv::ExecutionMode::PostDepthCoverage:
843
25.2k
    case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
844
25.2k
    case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
845
25.2k
    case spv::ExecutionMode::StencilRefGreaterFrontAMD:
846
25.2k
    case spv::ExecutionMode::StencilRefLessFrontAMD:
847
25.2k
    case spv::ExecutionMode::StencilRefUnchangedBackAMD:
848
25.2k
    case spv::ExecutionMode::StencilRefGreaterBackAMD:
849
25.2k
    case spv::ExecutionMode::StencilRefLessBackAMD:
850
25.2k
    case spv::ExecutionMode::RequireFullQuadsKHR:
851
25.2k
      if (!std::all_of(models->begin(), models->end(),
852
25.2k
                       [](const spv::ExecutionModel& model) {
853
25.2k
                         return model == spv::ExecutionModel::Fragment;
854
25.2k
                       })) {
855
107
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
856
107
               << "Execution mode can only be used with the Fragment execution "
857
107
                  "model.";
858
107
      }
859
25.1k
      break;
860
25.1k
    case spv::ExecutionMode::LocalSizeHint:
861
45
    case spv::ExecutionMode::VecTypeHint:
862
84
    case spv::ExecutionMode::ContractionOff:
863
84
    case spv::ExecutionMode::LocalSizeHintId:
864
84
      if (!std::all_of(models->begin(), models->end(),
865
84
                       [](const spv::ExecutionModel& model) {
866
84
                         return model == spv::ExecutionModel::Kernel;
867
84
                       })) {
868
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
869
0
               << "Execution mode can only be used with the Kernel execution "
870
0
                  "model.";
871
0
      }
872
84
      break;
873
712
    case spv::ExecutionMode::LocalSize:
874
712
    case spv::ExecutionMode::LocalSizeId:
875
712
      if (mode == spv::ExecutionMode::LocalSizeId &&
876
0
          !_.IsLocalSizeIdAllowed()) {
877
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
878
0
               << "LocalSizeId mode is not allowed by the current environment."
879
0
               << (is_vulkan_env
880
0
                       ? _.MissingFeature("maintenance4 feature",
881
0
                                          "--allow-localsizeid", false)
882
0
                       : "");
883
0
      }
884
885
712
      if (!std::all_of(
886
712
              models->begin(), models->end(),
887
712
              [&_](const spv::ExecutionModel& model) {
888
712
                switch (model) {
889
99
                  case spv::ExecutionModel::Kernel:
890
680
                  case spv::ExecutionModel::GLCompute:
891
680
                    return true;
892
0
                  case spv::ExecutionModel::TaskNV:
893
0
                  case spv::ExecutionModel::MeshNV:
894
0
                    return _.HasCapability(spv::Capability::MeshShadingNV);
895
0
                  case spv::ExecutionModel::TaskEXT:
896
0
                  case spv::ExecutionModel::MeshEXT:
897
0
                    return _.HasCapability(spv::Capability::MeshShadingEXT);
898
32
                  default:
899
32
                    return false;
900
712
                }
901
712
              })) {
902
32
        if (_.HasCapability(spv::Capability::MeshShadingNV) ||
903
32
            _.HasCapability(spv::Capability::MeshShadingEXT)) {
904
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
905
0
                 << "Execution mode can only be used with a Kernel, GLCompute, "
906
0
                    "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
907
32
        } else {
908
32
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
909
32
                 << "Execution mode can only be used with a Kernel or "
910
32
                    "GLCompute "
911
32
                    "execution model.";
912
32
        }
913
32
      }
914
680
    default:
915
680
      break;
916
30.0k
  }
917
918
29.7k
  if (mode == spv::ExecutionMode::FPFastMathDefault) {
919
0
    const auto* modes = _.GetExecutionModes(entry_point_id);
920
0
    if (modes && modes->count(spv::ExecutionMode::ContractionOff)) {
921
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
922
0
             << "FPFastMathDefault and ContractionOff execution modes cannot "
923
0
                "be applied to the same entry point";
924
0
    }
925
0
    if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) {
926
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
927
0
             << "FPFastMathDefault and SignedZeroInfNanPreserve execution "
928
0
                "modes cannot be applied to the same entry point";
929
0
    }
930
0
  }
931
932
29.7k
  if (is_vulkan_env) {
933
0
    if (mode == spv::ExecutionMode::OriginLowerLeft) {
934
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
935
0
             << _.VkErrorID(4653)
936
0
             << "In the Vulkan environment, the OriginLowerLeft execution mode "
937
0
                "must not be used.";
938
0
    }
939
0
    if (mode == spv::ExecutionMode::PixelCenterInteger) {
940
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
941
0
             << _.VkErrorID(4654)
942
0
             << "In the Vulkan environment, the PixelCenterInteger execution "
943
0
                "mode must not be used.";
944
0
    }
945
0
    if (mode == spv::ExecutionMode::TileShadingRateQCOM) {
946
0
      const auto rateX = inst->GetOperandAs<int>(2);
947
0
      const auto rateY = inst->GetOperandAs<int>(3);
948
0
      if ((rateX & (rateX - 1)) != 0 || (rateY & (rateY - 1)) != 0)
949
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
950
0
               << "The TileShadingRateQCOM execution mode's x and y values "
951
0
                  "must be powers of 2.";
952
0
    }
953
0
  }
954
955
29.7k
  return SPV_SUCCESS;
956
29.7k
}
957
958
spv_result_t ValidateMemoryModel(ValidationState_t& _,
959
39.2k
                                 const Instruction* inst) {
960
  // Already produced an error if multiple memory model instructions are
961
  // present.
962
39.2k
  if (_.memory_model() != spv::MemoryModel::VulkanKHR &&
963
39.2k
      _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
964
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
965
0
           << "VulkanMemoryModelKHR capability must only be specified if "
966
0
              "the VulkanKHR memory model is used.";
967
0
  }
968
969
39.2k
  if (spvIsOpenCLEnv(_.context()->target_env)) {
970
0
    if ((_.addressing_model() != spv::AddressingModel::Physical32) &&
971
0
        (_.addressing_model() != spv::AddressingModel::Physical64)) {
972
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
973
0
             << "Addressing model must be Physical32 or Physical64 "
974
0
             << "in the OpenCL environment.";
975
0
    }
976
0
    if (_.memory_model() != spv::MemoryModel::OpenCL) {
977
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
978
0
             << "Memory model must be OpenCL in the OpenCL environment.";
979
0
    }
980
0
  }
981
982
39.2k
  if (spvIsVulkanEnv(_.context()->target_env)) {
983
0
    if ((_.addressing_model() != spv::AddressingModel::Logical) &&
984
0
        (_.addressing_model() !=
985
0
         spv::AddressingModel::PhysicalStorageBuffer64)) {
986
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
987
0
             << _.VkErrorID(4635)
988
0
             << "Addressing model must be Logical or PhysicalStorageBuffer64 "
989
0
             << "in the Vulkan environment.";
990
0
    }
991
0
  }
992
39.2k
  return SPV_SUCCESS;
993
39.2k
}
994
995
18.3k
bool PerEntryExecutionMode(spv::ExecutionMode mode) {
996
18.3k
  switch (mode) {
997
    // These execution modes can be specified multiple times per entry point.
998
0
    case spv::ExecutionMode::DenormPreserve:
999
0
    case spv::ExecutionMode::DenormFlushToZero:
1000
0
    case spv::ExecutionMode::SignedZeroInfNanPreserve:
1001
0
    case spv::ExecutionMode::RoundingModeRTE:
1002
0
    case spv::ExecutionMode::RoundingModeRTZ:
1003
0
    case spv::ExecutionMode::FPFastMathDefault:
1004
0
    case spv::ExecutionMode::RoundingModeRTPINTEL:
1005
0
    case spv::ExecutionMode::RoundingModeRTNINTEL:
1006
0
    case spv::ExecutionMode::FloatingPointModeALTINTEL:
1007
0
    case spv::ExecutionMode::FloatingPointModeIEEEINTEL:
1008
0
      return false;
1009
18.3k
    default:
1010
18.3k
      return true;
1011
18.3k
  }
1012
18.3k
}
1013
1014
52.4k
spv_result_t ValidateCapability(ValidationState_t& _, const Instruction* inst) {
1015
52.4k
  auto cap = inst->GetOperandAs<spv::Capability>(0);
1016
52.4k
  if (cap == spv::Capability::CooperativeMatrixKHR) {
1017
0
    if (_.HasCapability(spv::Capability::Shader) &&
1018
0
        !_.HasCapability(spv::Capability::VulkanMemoryModel)) {
1019
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
1020
0
             << "If the Shader and CooperativeMatrixKHR capabilities are "
1021
0
                "declared, the VulkanMemoryModel capability must also be "
1022
0
                "declared";
1023
0
    }
1024
0
  }
1025
52.4k
  return SPV_SUCCESS;
1026
52.4k
}
1027
1028
}  // namespace
1029
1030
27.4k
spv_result_t ValidateFloatControls2(ValidationState_t& _) {
1031
27.4k
  std::unordered_set<uint32_t> fp_fast_math_default_entry_points;
1032
27.4k
  for (auto entry_point : _.entry_points()) {
1033
21.3k
    const auto* exec_modes = _.GetExecutionModes(entry_point);
1034
21.3k
    if (exec_modes &&
1035
18.1k
        exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) {
1036
0
      fp_fast_math_default_entry_points.insert(entry_point);
1037
0
    }
1038
21.3k
  }
1039
1040
27.4k
  std::vector<std::pair<const Instruction*, spv::Decoration>> worklist;
1041
13.9M
  for (const auto& inst : _.ordered_instructions()) {
1042
13.9M
    if (inst.opcode() != spv::Op::OpDecorate) {
1043
13.6M
      continue;
1044
13.6M
    }
1045
1046
385k
    const auto decoration = inst.GetOperandAs<spv::Decoration>(1);
1047
385k
    const auto target_id = inst.GetOperandAs<uint32_t>(0);
1048
385k
    const auto target = _.FindDef(target_id);
1049
385k
    if (decoration == spv::Decoration::NoContraction) {
1050
21.5k
      worklist.push_back(std::make_pair(target, decoration));
1051
364k
    } else if (decoration == spv::Decoration::FPFastMathMode) {
1052
2.13k
      auto mask = inst.GetOperandAs<spv::FPFastMathModeMask>(2);
1053
2.13k
      if ((mask & spv::FPFastMathModeMask::Fast) !=
1054
2.13k
          spv::FPFastMathModeMask::MaskNone) {
1055
1.59k
        worklist.push_back(std::make_pair(target, decoration));
1056
1.59k
      }
1057
2.13k
    }
1058
385k
  }
1059
1060
27.4k
  std::unordered_set<const Instruction*> visited;
1061
1.26M
  while (!worklist.empty()) {
1062
1.23M
    const auto inst = worklist.back().first;
1063
1.23M
    const auto decoration = worklist.back().second;
1064
1.23M
    worklist.pop_back();
1065
1066
1.23M
    if (!visited.insert(inst).second) {
1067
23.3k
      continue;
1068
23.3k
    }
1069
1070
1.21M
    const auto function = inst->function();
1071
1.21M
    if (function) {
1072
6.07k
      const auto& entry_points = _.FunctionEntryPoints(function->id());
1073
6.07k
      for (auto entry_point : entry_points) {
1074
5.16k
        if (fp_fast_math_default_entry_points.count(entry_point)) {
1075
0
          const std::string dec = decoration == spv::Decoration::NoContraction
1076
0
                                      ? "NoContraction"
1077
0
                                      : "FPFastMathMode Fast";
1078
0
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
1079
0
                 << dec
1080
0
                 << " cannot be used by an entry point with the "
1081
0
                    "FPFastMathDefault execution mode";
1082
0
        }
1083
5.16k
      }
1084
1.20M
    } else {
1085
1.21M
      for (const auto& pair : inst->uses()) {
1086
1.21M
        worklist.push_back(std::make_pair(pair.first, decoration));
1087
1.21M
      }
1088
1.20M
    }
1089
1.21M
  }
1090
1091
27.4k
  return SPV_SUCCESS;
1092
27.4k
}
1093
1094
14.3M
spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
1095
14.3M
  switch (inst->opcode()) {
1096
24.7k
    case spv::Op::OpEntryPoint:
1097
24.7k
      if (auto error = ValidateEntryPoint(_, inst)) return error;
1098
24.6k
      break;
1099
30.0k
    case spv::Op::OpExecutionMode:
1100
30.0k
    case spv::Op::OpExecutionModeId:
1101
30.0k
      if (auto error = ValidateExecutionMode(_, inst)) return error;
1102
29.7k
      break;
1103
39.2k
    case spv::Op::OpMemoryModel:
1104
39.2k
      if (auto error = ValidateMemoryModel(_, inst)) return error;
1105
39.2k
      break;
1106
52.4k
    case spv::Op::OpCapability:
1107
52.4k
      if (auto error = ValidateCapability(_, inst)) return error;
1108
52.4k
      break;
1109
14.2M
    default:
1110
14.2M
      break;
1111
14.3M
  }
1112
14.3M
  return SPV_SUCCESS;
1113
14.3M
}
1114
1115
27.4k
spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _) {
1116
27.4k
  using PerEntryKey = std::tuple<spv::ExecutionMode, uint32_t>;
1117
27.4k
  using PerOperandKey = std::tuple<spv::ExecutionMode, uint32_t, uint32_t>;
1118
27.4k
  std::set<PerEntryKey> seen_per_entry;
1119
27.4k
  std::set<PerOperandKey> seen_per_operand;
1120
1121
27.4k
  const auto lookupMode = [](spv::ExecutionMode mode) -> std::string {
1122
360
    const spvtools::OperandDesc* desc = nullptr;
1123
360
    if (spvtools::LookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE,
1124
360
                                static_cast<uint32_t>(mode),
1125
360
                                &desc) == SPV_SUCCESS) {
1126
360
      return std::string(desc->name().data());
1127
360
    }
1128
0
    return "Unknown";
1129
360
  };
1130
1131
13.9M
  for (const auto& inst : _.ordered_instructions()) {
1132
13.9M
    if (inst.opcode() != spv::Op::OpExecutionMode &&
1133
13.9M
        inst.opcode() != spv::Op::OpExecutionModeId) {
1134
13.9M
      continue;
1135
13.9M
    }
1136
1137
18.3k
    const auto entry = inst.GetOperandAs<uint32_t>(0);
1138
18.3k
    const auto mode = inst.GetOperandAs<spv::ExecutionMode>(1);
1139
18.3k
    if (PerEntryExecutionMode(mode)) {
1140
18.3k
      if (!seen_per_entry.insert(std::make_tuple(mode, entry)).second) {
1141
360
        return _.diag(SPV_ERROR_INVALID_ID, &inst)
1142
360
               << lookupMode(mode)
1143
360
               << " execution mode must not be specified multiple times per "
1144
360
                  "entry point";
1145
360
      }
1146
18.3k
    } else {
1147
      // Execution modes allowed multiple times all take a single operand.
1148
0
      const auto operand = inst.GetOperandAs<uint32_t>(2);
1149
0
      if (!seen_per_operand.insert(std::make_tuple(mode, entry, operand))
1150
0
               .second) {
1151
0
        return _.diag(SPV_ERROR_INVALID_ID, &inst)
1152
0
               << lookupMode(mode)
1153
0
               << " execution mode must not be specified multiple times for "
1154
0
                  "the same entry point and operands";
1155
0
      }
1156
0
    }
1157
18.3k
  }
1158
1159
27.0k
  return SPV_SUCCESS;
1160
27.4k
}
1161
1162
}  // namespace val
1163
}  // namespace spvtools