• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 Google LLC.
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 #include <algorithm>
16 
17 #include "source/opcode.h"
18 #include "source/spirv_target_env.h"
19 #include "source/val/instruction.h"
20 #include "source/val/validate.h"
21 #include "source/val/validation_state.h"
22 
23 namespace spvtools {
24 namespace val {
25 namespace {
26 
ValidateEntryPoint(ValidationState_t & _,const Instruction * inst)27 spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
28   const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
29   auto entry_point = _.FindDef(entry_point_id);
30   if (!entry_point || spv::Op::OpFunction != entry_point->opcode()) {
31     return _.diag(SPV_ERROR_INVALID_ID, inst)
32            << "OpEntryPoint Entry Point <id> " << _.getIdName(entry_point_id)
33            << " is not a function.";
34   }
35 
36   // Only check the shader execution models
37   const spv::ExecutionModel execution_model =
38       inst->GetOperandAs<spv::ExecutionModel>(0);
39   if (execution_model != spv::ExecutionModel::Kernel) {
40     const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
41     const auto entry_point_type = _.FindDef(entry_point_type_id);
42     if (!entry_point_type || 3 != entry_point_type->words().size()) {
43       return _.diag(SPV_ERROR_INVALID_ID, inst)
44              << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> "
45              << _.getIdName(entry_point_id)
46              << "s function parameter count is not zero.";
47     }
48   }
49 
50   auto return_type = _.FindDef(entry_point->type_id());
51   if (!return_type || spv::Op::OpTypeVoid != return_type->opcode()) {
52     return _.diag(SPV_ERROR_INVALID_ID, inst)
53            << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> "
54            << _.getIdName(entry_point_id)
55            << "s function return type is not void.";
56   }
57 
58   const auto* execution_modes = _.GetExecutionModes(entry_point_id);
59   if (_.HasCapability(spv::Capability::Shader)) {
60     switch (execution_model) {
61       case spv::ExecutionModel::Fragment:
62         if (execution_modes &&
63             execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
64             execution_modes->count(spv::ExecutionMode::OriginLowerLeft)) {
65           return _.diag(SPV_ERROR_INVALID_DATA, inst)
66                  << "Fragment execution model entry points can only specify "
67                     "one of OriginUpperLeft or OriginLowerLeft execution "
68                     "modes.";
69         }
70         if (!execution_modes ||
71             (!execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
72              !execution_modes->count(spv::ExecutionMode::OriginLowerLeft))) {
73           return _.diag(SPV_ERROR_INVALID_DATA, inst)
74                  << "Fragment execution model entry points require either an "
75                     "OriginUpperLeft or OriginLowerLeft execution mode.";
76         }
77         if (execution_modes &&
78             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
79                               [](const spv::ExecutionMode& mode) {
80                                 switch (mode) {
81                                   case spv::ExecutionMode::DepthGreater:
82                                   case spv::ExecutionMode::DepthLess:
83                                   case spv::ExecutionMode::DepthUnchanged:
84                                     return true;
85                                   default:
86                                     return false;
87                                 }
88                               })) {
89           return _.diag(SPV_ERROR_INVALID_DATA, inst)
90                  << "Fragment execution model entry points can specify at most "
91                     "one of DepthGreater, DepthLess or DepthUnchanged "
92                     "execution modes.";
93         }
94         if (execution_modes &&
95             1 < std::count_if(
96                     execution_modes->begin(), execution_modes->end(),
97                     [](const spv::ExecutionMode& mode) {
98                       switch (mode) {
99                         case spv::ExecutionMode::PixelInterlockOrderedEXT:
100                         case spv::ExecutionMode::PixelInterlockUnorderedEXT:
101                         case spv::ExecutionMode::SampleInterlockOrderedEXT:
102                         case spv::ExecutionMode::SampleInterlockUnorderedEXT:
103                         case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
104                         case spv::ExecutionMode::
105                             ShadingRateInterlockUnorderedEXT:
106                           return true;
107                         default:
108                           return false;
109                       }
110                     })) {
111           return _.diag(SPV_ERROR_INVALID_DATA, inst)
112                  << "Fragment execution model entry points can specify at most "
113                     "one fragment shader interlock execution mode.";
114         }
115         if (execution_modes &&
116             1 < std::count_if(
117                     execution_modes->begin(), execution_modes->end(),
118                     [](const spv::ExecutionMode& mode) {
119                       switch (mode) {
120                         case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
121                         case spv::ExecutionMode::StencilRefLessFrontAMD:
122                         case spv::ExecutionMode::StencilRefGreaterFrontAMD:
123                           return true;
124                         default:
125                           return false;
126                       }
127                     })) {
128           return _.diag(SPV_ERROR_INVALID_DATA, inst)
129                  << "Fragment execution model entry points can specify at most "
130                     "one of StencilRefUnchangedFrontAMD, "
131                     "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
132                     "execution modes.";
133         }
134         if (execution_modes &&
135             1 < std::count_if(
136                     execution_modes->begin(), execution_modes->end(),
137                     [](const spv::ExecutionMode& mode) {
138                       switch (mode) {
139                         case spv::ExecutionMode::StencilRefUnchangedBackAMD:
140                         case spv::ExecutionMode::StencilRefLessBackAMD:
141                         case spv::ExecutionMode::StencilRefGreaterBackAMD:
142                           return true;
143                         default:
144                           return false;
145                       }
146                     })) {
147           return _.diag(SPV_ERROR_INVALID_DATA, inst)
148                  << "Fragment execution model entry points can specify at most "
149                     "one of StencilRefUnchangedBackAMD, "
150                     "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
151                     "execution modes.";
152         }
153         break;
154       case spv::ExecutionModel::TessellationControl:
155       case spv::ExecutionModel::TessellationEvaluation:
156         if (execution_modes &&
157             1 < std::count_if(
158                     execution_modes->begin(), execution_modes->end(),
159                     [](const spv::ExecutionMode& mode) {
160                       switch (mode) {
161                         case spv::ExecutionMode::SpacingEqual:
162                         case spv::ExecutionMode::SpacingFractionalEven:
163                         case spv::ExecutionMode::SpacingFractionalOdd:
164                           return true;
165                         default:
166                           return false;
167                       }
168                     })) {
169           return _.diag(SPV_ERROR_INVALID_DATA, inst)
170                  << "Tessellation execution model entry points can specify at "
171                     "most one of SpacingEqual, SpacingFractionalOdd or "
172                     "SpacingFractionalEven execution modes.";
173         }
174         if (execution_modes &&
175             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
176                               [](const spv::ExecutionMode& mode) {
177                                 switch (mode) {
178                                   case spv::ExecutionMode::Triangles:
179                                   case spv::ExecutionMode::Quads:
180                                   case spv::ExecutionMode::Isolines:
181                                     return true;
182                                   default:
183                                     return false;
184                                 }
185                               })) {
186           return _.diag(SPV_ERROR_INVALID_DATA, inst)
187                  << "Tessellation execution model entry points can specify at "
188                     "most one of Triangles, Quads or Isolines execution modes.";
189         }
190         if (execution_modes &&
191             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
192                               [](const spv::ExecutionMode& mode) {
193                                 switch (mode) {
194                                   case spv::ExecutionMode::VertexOrderCw:
195                                   case spv::ExecutionMode::VertexOrderCcw:
196                                     return true;
197                                   default:
198                                     return false;
199                                 }
200                               })) {
201           return _.diag(SPV_ERROR_INVALID_DATA, inst)
202                  << "Tessellation execution model entry points can specify at "
203                     "most one of VertexOrderCw or VertexOrderCcw execution "
204                     "modes.";
205         }
206         break;
207       case spv::ExecutionModel::Geometry:
208         if (!execution_modes ||
209             1 != std::count_if(
210                      execution_modes->begin(), execution_modes->end(),
211                      [](const spv::ExecutionMode& mode) {
212                        switch (mode) {
213                          case spv::ExecutionMode::InputPoints:
214                          case spv::ExecutionMode::InputLines:
215                          case spv::ExecutionMode::InputLinesAdjacency:
216                          case spv::ExecutionMode::Triangles:
217                          case spv::ExecutionMode::InputTrianglesAdjacency:
218                            return true;
219                          default:
220                            return false;
221                        }
222                      })) {
223           return _.diag(SPV_ERROR_INVALID_DATA, inst)
224                  << "Geometry execution model entry points must specify "
225                     "exactly one of InputPoints, InputLines, "
226                     "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
227                     "execution modes.";
228         }
229         if (!execution_modes ||
230             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
231                                [](const spv::ExecutionMode& mode) {
232                                  switch (mode) {
233                                    case spv::ExecutionMode::OutputPoints:
234                                    case spv::ExecutionMode::OutputLineStrip:
235                                    case spv::ExecutionMode::OutputTriangleStrip:
236                                      return true;
237                                    default:
238                                      return false;
239                                  }
240                                })) {
241           return _.diag(SPV_ERROR_INVALID_DATA, inst)
242                  << "Geometry execution model entry points must specify "
243                     "exactly one of OutputPoints, OutputLineStrip or "
244                     "OutputTriangleStrip execution modes.";
245         }
246         break;
247       case spv::ExecutionModel::MeshEXT:
248         if (!execution_modes ||
249             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
250                                [](const spv::ExecutionMode& mode) {
251                                  switch (mode) {
252                                    case spv::ExecutionMode::OutputPoints:
253                                    case spv::ExecutionMode::OutputLinesEXT:
254                                    case spv::ExecutionMode::OutputTrianglesEXT:
255                                      return true;
256                                    default:
257                                      return false;
258                                  }
259                                })) {
260           return _.diag(SPV_ERROR_INVALID_DATA, inst)
261                  << "MeshEXT execution model entry points must specify exactly "
262                     "one of OutputPoints, OutputLinesEXT, or "
263                     "OutputTrianglesEXT Execution Modes.";
264         } else if (2 != std::count_if(
265                             execution_modes->begin(), execution_modes->end(),
266                             [](const spv::ExecutionMode& mode) {
267                               switch (mode) {
268                                 case spv::ExecutionMode::OutputPrimitivesEXT:
269                                 case spv::ExecutionMode::OutputVertices:
270                                   return true;
271                                 default:
272                                   return false;
273                               }
274                             })) {
275           return _.diag(SPV_ERROR_INVALID_DATA, inst)
276                  << "MeshEXT execution model entry points must specify both "
277                     "OutputPrimitivesEXT and OutputVertices Execution Modes.";
278         }
279         break;
280       default:
281         break;
282     }
283   }
284 
285   if (spvIsVulkanEnv(_.context()->target_env)) {
286     switch (execution_model) {
287       case spv::ExecutionModel::GLCompute:
288         if (!execution_modes ||
289             !execution_modes->count(spv::ExecutionMode::LocalSize)) {
290           bool ok = false;
291           for (auto& i : _.ordered_instructions()) {
292             if (i.opcode() == spv::Op::OpDecorate) {
293               if (i.operands().size() > 2) {
294                 if (i.GetOperandAs<spv::Decoration>(1) ==
295                         spv::Decoration::BuiltIn &&
296                     i.GetOperandAs<spv::BuiltIn>(2) ==
297                         spv::BuiltIn::WorkgroupSize) {
298                   ok = true;
299                   break;
300                 }
301               }
302             }
303             if (i.opcode() == spv::Op::OpExecutionModeId) {
304               const auto mode = i.GetOperandAs<spv::ExecutionMode>(1);
305               if (mode == spv::ExecutionMode::LocalSizeId) {
306                 ok = true;
307                 break;
308               }
309             }
310           }
311           if (!ok) {
312             return _.diag(SPV_ERROR_INVALID_DATA, inst)
313                    << _.VkErrorID(6426)
314                    << "In the Vulkan environment, GLCompute execution model "
315                       "entry points require either the LocalSize or "
316                       "LocalSizeId execution mode or an object decorated with "
317                       "WorkgroupSize must be specified.";
318           }
319         }
320         break;
321       default:
322         break;
323     }
324   }
325 
326   return SPV_SUCCESS;
327 }
328 
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)329 spv_result_t ValidateExecutionMode(ValidationState_t& _,
330                                    const Instruction* inst) {
331   const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
332   const auto found = std::find(_.entry_points().cbegin(),
333                                _.entry_points().cend(), entry_point_id);
334   if (found == _.entry_points().cend()) {
335     return _.diag(SPV_ERROR_INVALID_ID, inst)
336            << "OpExecutionMode Entry Point <id> " << _.getIdName(entry_point_id)
337            << " is not the Entry Point "
338               "operand of an OpEntryPoint.";
339   }
340 
341   const auto mode = inst->GetOperandAs<spv::ExecutionMode>(1);
342   if (inst->opcode() == spv::Op::OpExecutionModeId) {
343     bool valid_mode = false;
344     switch (mode) {
345       case spv::ExecutionMode::SubgroupsPerWorkgroupId:
346       case spv::ExecutionMode::LocalSizeHintId:
347       case spv::ExecutionMode::LocalSizeId:
348       case spv::ExecutionMode::FPFastMathDefault:
349       case spv::ExecutionMode::MaximumRegistersIdINTEL:
350         valid_mode = true;
351         break;
352       default:
353         valid_mode = false;
354         break;
355     }
356     if (!valid_mode) {
357       return _.diag(SPV_ERROR_INVALID_ID, inst)
358              << "OpExecutionModeId is only valid when the Mode operand is an "
359                 "execution mode that takes Extra Operands that are id "
360                 "operands.";
361     }
362 
363     size_t operand_count = inst->operands().size();
364     for (size_t i = 2; i < operand_count; ++i) {
365       const auto operand_id = inst->GetOperandAs<uint32_t>(i);
366       const auto* operand_inst = _.FindDef(operand_id);
367       switch (mode) {
368         case spv::ExecutionMode::SubgroupsPerWorkgroupId:
369         case spv::ExecutionMode::LocalSizeHintId:
370         case spv::ExecutionMode::LocalSizeId:
371           if (!spvOpcodeIsConstant(operand_inst->opcode())) {
372             return _.diag(SPV_ERROR_INVALID_ID, inst)
373                    << "For OpExecutionModeId all Extra Operand ids must be "
374                       "constant instructions.";
375           }
376           break;
377         case spv::ExecutionMode::FPFastMathDefault:
378           if (i == 2) {
379             if (!_.IsFloatScalarType(operand_id)) {
380               return _.diag(SPV_ERROR_INVALID_ID, inst)
381                      << "The Target Type operand must be a floating-point "
382                         "scalar type";
383             }
384           } else {
385             bool is_int32 = false;
386             bool is_const = false;
387             uint32_t value = 0;
388             std::tie(is_int32, is_const, value) =
389                 _.EvalInt32IfConst(operand_id);
390             if (is_int32 && is_const) {
391               // Valid values include up to 0x00040000 (AllowTransform).
392               uint32_t invalid_mask = 0xfff80000;
393               if ((invalid_mask & value) != 0) {
394                 return _.diag(SPV_ERROR_INVALID_ID, inst)
395                        << "The Fast Math Default operand is an invalid bitmask "
396                           "value";
397               }
398               if (value &
399                   static_cast<uint32_t>(spv::FPFastMathModeMask::Fast)) {
400                 return _.diag(SPV_ERROR_INVALID_ID, inst)
401                        << "The Fast Math Default operand must not include Fast";
402               }
403               const auto reassoc_contract =
404                   spv::FPFastMathModeMask::AllowContract |
405                   spv::FPFastMathModeMask::AllowReassoc;
406               if ((value & static_cast<uint32_t>(
407                                spv::FPFastMathModeMask::AllowTransform)) != 0 &&
408                   ((value & static_cast<uint32_t>(reassoc_contract)) !=
409                    static_cast<uint32_t>(reassoc_contract))) {
410                 return _.diag(SPV_ERROR_INVALID_ID, inst)
411                        << "The Fast Math Default operand must include "
412                           "AllowContract and AllowReassoc when AllowTransform "
413                           "is specified";
414               }
415             } else {
416               return _.diag(SPV_ERROR_INVALID_ID, inst)
417                      << "The Fast Math Default operand must be a "
418                         "non-specialization constant";
419             }
420           }
421           break;
422         default:
423           break;
424       }
425     }
426   } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
427              mode == spv::ExecutionMode::LocalSizeHintId ||
428              mode == spv::ExecutionMode::LocalSizeId ||
429              mode == spv::ExecutionMode::FPFastMathDefault) {
430     return _.diag(SPV_ERROR_INVALID_DATA, inst)
431            << "OpExecutionMode is only valid when the Mode operand is an "
432               "execution mode that takes no Extra Operands, or takes Extra "
433               "Operands that are not id operands.";
434   }
435 
436   const auto* models = _.GetExecutionModels(entry_point_id);
437   switch (mode) {
438     case spv::ExecutionMode::Invocations:
439     case spv::ExecutionMode::InputPoints:
440     case spv::ExecutionMode::InputLines:
441     case spv::ExecutionMode::InputLinesAdjacency:
442     case spv::ExecutionMode::InputTrianglesAdjacency:
443     case spv::ExecutionMode::OutputLineStrip:
444     case spv::ExecutionMode::OutputTriangleStrip:
445       if (!std::all_of(models->begin(), models->end(),
446                        [](const spv::ExecutionModel& model) {
447                          return model == spv::ExecutionModel::Geometry;
448                        })) {
449         return _.diag(SPV_ERROR_INVALID_DATA, inst)
450                << "Execution mode can only be used with the Geometry execution "
451                   "model.";
452       }
453       break;
454     case spv::ExecutionMode::OutputPoints:
455       if (!std::all_of(
456               models->begin(), models->end(),
457               [&_](const spv::ExecutionModel& model) {
458                 switch (model) {
459                   case spv::ExecutionModel::Geometry:
460                     return true;
461                   case spv::ExecutionModel::MeshNV:
462                     return _.HasCapability(spv::Capability::MeshShadingNV);
463                   case spv::ExecutionModel::MeshEXT:
464                     return _.HasCapability(spv::Capability::MeshShadingEXT);
465                   default:
466                     return false;
467                 }
468               })) {
469         if (_.HasCapability(spv::Capability::MeshShadingNV) ||
470             _.HasCapability(spv::Capability::MeshShadingEXT)) {
471           return _.diag(SPV_ERROR_INVALID_DATA, inst)
472                  << "Execution mode can only be used with the Geometry "
473                     "MeshNV or MeshEXT execution model.";
474         } else {
475           return _.diag(SPV_ERROR_INVALID_DATA, inst)
476                  << "Execution mode can only be used with the Geometry "
477                     "execution "
478                     "model.";
479         }
480       }
481       break;
482     case spv::ExecutionMode::SpacingEqual:
483     case spv::ExecutionMode::SpacingFractionalEven:
484     case spv::ExecutionMode::SpacingFractionalOdd:
485     case spv::ExecutionMode::VertexOrderCw:
486     case spv::ExecutionMode::VertexOrderCcw:
487     case spv::ExecutionMode::PointMode:
488     case spv::ExecutionMode::Quads:
489     case spv::ExecutionMode::Isolines:
490       if (!std::all_of(
491               models->begin(), models->end(),
492               [](const spv::ExecutionModel& model) {
493                 return (model == spv::ExecutionModel::TessellationControl) ||
494                        (model == spv::ExecutionModel::TessellationEvaluation);
495               })) {
496         return _.diag(SPV_ERROR_INVALID_DATA, inst)
497                << "Execution mode can only be used with a tessellation "
498                   "execution model.";
499       }
500       break;
501     case spv::ExecutionMode::Triangles:
502       if (!std::all_of(models->begin(), models->end(),
503                        [](const spv::ExecutionModel& model) {
504                          switch (model) {
505                            case spv::ExecutionModel::Geometry:
506                            case spv::ExecutionModel::TessellationControl:
507                            case spv::ExecutionModel::TessellationEvaluation:
508                              return true;
509                            default:
510                              return false;
511                          }
512                        })) {
513         return _.diag(SPV_ERROR_INVALID_DATA, inst)
514                << "Execution mode can only be used with a Geometry or "
515                   "tessellation execution model.";
516       }
517       break;
518     case spv::ExecutionMode::OutputVertices:
519       if (!std::all_of(
520               models->begin(), models->end(),
521               [&_](const spv::ExecutionModel& model) {
522                 switch (model) {
523                   case spv::ExecutionModel::Geometry:
524                   case spv::ExecutionModel::TessellationControl:
525                   case spv::ExecutionModel::TessellationEvaluation:
526                     return true;
527                   case spv::ExecutionModel::MeshNV:
528                     return _.HasCapability(spv::Capability::MeshShadingNV);
529                   case spv::ExecutionModel::MeshEXT:
530                     return _.HasCapability(spv::Capability::MeshShadingEXT);
531                   default:
532                     return false;
533                 }
534               })) {
535         if (_.HasCapability(spv::Capability::MeshShadingNV) ||
536             _.HasCapability(spv::Capability::MeshShadingEXT)) {
537           return _.diag(SPV_ERROR_INVALID_DATA, inst)
538                  << "Execution mode can only be used with a Geometry, "
539                     "tessellation, MeshNV or MeshEXT execution model.";
540         } else {
541           return _.diag(SPV_ERROR_INVALID_DATA, inst)
542                  << "Execution mode can only be used with a Geometry or "
543                     "tessellation execution model.";
544         }
545       }
546       break;
547     case spv::ExecutionMode::OutputLinesEXT:
548     case spv::ExecutionMode::OutputTrianglesEXT:
549     case spv::ExecutionMode::OutputPrimitivesEXT:
550       if (!std::all_of(models->begin(), models->end(),
551                        [](const spv::ExecutionModel& model) {
552                          return (model == spv::ExecutionModel::MeshEXT ||
553                                  model == spv::ExecutionModel::MeshNV);
554                        })) {
555         return _.diag(SPV_ERROR_INVALID_DATA, inst)
556                << "Execution mode can only be used with the MeshEXT or MeshNV "
557                   "execution "
558                   "model.";
559       }
560       break;
561     case spv::ExecutionMode::QuadDerivativesKHR:
562       if (!std::all_of(models->begin(), models->end(),
563                        [](const spv::ExecutionModel& model) {
564                          return (model == spv::ExecutionModel::Fragment ||
565                                  model == spv::ExecutionModel::GLCompute);
566                        })) {
567         return _.diag(SPV_ERROR_INVALID_DATA, inst)
568                << "Execution mode can only be used with the Fragment or "
569                   "GLCompute execution model.";
570       }
571       break;
572     case spv::ExecutionMode::PixelCenterInteger:
573     case spv::ExecutionMode::OriginUpperLeft:
574     case spv::ExecutionMode::OriginLowerLeft:
575     case spv::ExecutionMode::EarlyFragmentTests:
576     case spv::ExecutionMode::DepthReplacing:
577     case spv::ExecutionMode::DepthGreater:
578     case spv::ExecutionMode::DepthLess:
579     case spv::ExecutionMode::DepthUnchanged:
580     case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
581     case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
582     case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
583     case spv::ExecutionMode::PixelInterlockOrderedEXT:
584     case spv::ExecutionMode::PixelInterlockUnorderedEXT:
585     case spv::ExecutionMode::SampleInterlockOrderedEXT:
586     case spv::ExecutionMode::SampleInterlockUnorderedEXT:
587     case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
588     case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
589     case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
590     case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
591     case spv::ExecutionMode::StencilRefGreaterFrontAMD:
592     case spv::ExecutionMode::StencilRefLessFrontAMD:
593     case spv::ExecutionMode::StencilRefUnchangedBackAMD:
594     case spv::ExecutionMode::StencilRefGreaterBackAMD:
595     case spv::ExecutionMode::StencilRefLessBackAMD:
596     case spv::ExecutionMode::RequireFullQuadsKHR:
597       if (!std::all_of(models->begin(), models->end(),
598                        [](const spv::ExecutionModel& model) {
599                          return model == spv::ExecutionModel::Fragment;
600                        })) {
601         return _.diag(SPV_ERROR_INVALID_DATA, inst)
602                << "Execution mode can only be used with the Fragment execution "
603                   "model.";
604       }
605       break;
606     case spv::ExecutionMode::LocalSizeHint:
607     case spv::ExecutionMode::VecTypeHint:
608     case spv::ExecutionMode::ContractionOff:
609     case spv::ExecutionMode::LocalSizeHintId:
610       if (!std::all_of(models->begin(), models->end(),
611                        [](const spv::ExecutionModel& model) {
612                          return model == spv::ExecutionModel::Kernel;
613                        })) {
614         return _.diag(SPV_ERROR_INVALID_DATA, inst)
615                << "Execution mode can only be used with the Kernel execution "
616                   "model.";
617       }
618       break;
619     case spv::ExecutionMode::LocalSize:
620     case spv::ExecutionMode::LocalSizeId:
621       if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed())
622         return _.diag(SPV_ERROR_INVALID_DATA, inst)
623                << "LocalSizeId mode is not allowed by the current environment.";
624 
625       if (!std::all_of(
626               models->begin(), models->end(),
627               [&_](const spv::ExecutionModel& model) {
628                 switch (model) {
629                   case spv::ExecutionModel::Kernel:
630                   case spv::ExecutionModel::GLCompute:
631                     return true;
632                   case spv::ExecutionModel::TaskNV:
633                   case spv::ExecutionModel::MeshNV:
634                     return _.HasCapability(spv::Capability::MeshShadingNV);
635                   case spv::ExecutionModel::TaskEXT:
636                   case spv::ExecutionModel::MeshEXT:
637                     return _.HasCapability(spv::Capability::MeshShadingEXT);
638                   default:
639                     return false;
640                 }
641               })) {
642         if (_.HasCapability(spv::Capability::MeshShadingNV) ||
643             _.HasCapability(spv::Capability::MeshShadingEXT)) {
644           return _.diag(SPV_ERROR_INVALID_DATA, inst)
645                  << "Execution mode can only be used with a Kernel, GLCompute, "
646                     "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
647         } else {
648           return _.diag(SPV_ERROR_INVALID_DATA, inst)
649                  << "Execution mode can only be used with a Kernel or "
650                     "GLCompute "
651                     "execution model.";
652         }
653       }
654     default:
655       break;
656   }
657 
658   if (mode == spv::ExecutionMode::FPFastMathDefault) {
659     const auto* modes = _.GetExecutionModes(entry_point_id);
660     if (modes && modes->count(spv::ExecutionMode::ContractionOff)) {
661       return _.diag(SPV_ERROR_INVALID_DATA, inst)
662              << "FPFastMathDefault and ContractionOff execution modes cannot "
663                 "be applied to the same entry point";
664     }
665     if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) {
666       return _.diag(SPV_ERROR_INVALID_DATA, inst)
667              << "FPFastMathDefault and SignedZeroInfNanPreserve execution "
668                 "modes cannot be applied to the same entry point";
669     }
670   }
671 
672   if (spvIsVulkanEnv(_.context()->target_env)) {
673     if (mode == spv::ExecutionMode::OriginLowerLeft) {
674       return _.diag(SPV_ERROR_INVALID_DATA, inst)
675              << _.VkErrorID(4653)
676              << "In the Vulkan environment, the OriginLowerLeft execution mode "
677                 "must not be used.";
678     }
679     if (mode == spv::ExecutionMode::PixelCenterInteger) {
680       return _.diag(SPV_ERROR_INVALID_DATA, inst)
681              << _.VkErrorID(4654)
682              << "In the Vulkan environment, the PixelCenterInteger execution "
683                 "mode must not be used.";
684     }
685   }
686 
687   return SPV_SUCCESS;
688 }
689 
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)690 spv_result_t ValidateMemoryModel(ValidationState_t& _,
691                                  const Instruction* inst) {
692   // Already produced an error if multiple memory model instructions are
693   // present.
694   if (_.memory_model() != spv::MemoryModel::VulkanKHR &&
695       _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
696     return _.diag(SPV_ERROR_INVALID_DATA, inst)
697            << "VulkanMemoryModelKHR capability must only be specified if "
698               "the VulkanKHR memory model is used.";
699   }
700 
701   if (spvIsOpenCLEnv(_.context()->target_env)) {
702     if ((_.addressing_model() != spv::AddressingModel::Physical32) &&
703         (_.addressing_model() != spv::AddressingModel::Physical64)) {
704       return _.diag(SPV_ERROR_INVALID_DATA, inst)
705              << "Addressing model must be Physical32 or Physical64 "
706              << "in the OpenCL environment.";
707     }
708     if (_.memory_model() != spv::MemoryModel::OpenCL) {
709       return _.diag(SPV_ERROR_INVALID_DATA, inst)
710              << "Memory model must be OpenCL in the OpenCL environment.";
711     }
712   }
713 
714   if (spvIsVulkanEnv(_.context()->target_env)) {
715     if ((_.addressing_model() != spv::AddressingModel::Logical) &&
716         (_.addressing_model() !=
717          spv::AddressingModel::PhysicalStorageBuffer64)) {
718       return _.diag(SPV_ERROR_INVALID_DATA, inst)
719              << _.VkErrorID(4635)
720              << "Addressing model must be Logical or PhysicalStorageBuffer64 "
721              << "in the Vulkan environment.";
722     }
723   }
724   return SPV_SUCCESS;
725 }
726 
PerEntryExecutionMode(spv::ExecutionMode mode)727 bool PerEntryExecutionMode(spv::ExecutionMode mode) {
728   switch (mode) {
729     // These execution modes can be specified multiple times per entry point.
730     case spv::ExecutionMode::DenormPreserve:
731     case spv::ExecutionMode::DenormFlushToZero:
732     case spv::ExecutionMode::SignedZeroInfNanPreserve:
733     case spv::ExecutionMode::RoundingModeRTE:
734     case spv::ExecutionMode::RoundingModeRTZ:
735     case spv::ExecutionMode::FPFastMathDefault:
736     case spv::ExecutionMode::RoundingModeRTPINTEL:
737     case spv::ExecutionMode::RoundingModeRTNINTEL:
738     case spv::ExecutionMode::FloatingPointModeALTINTEL:
739     case spv::ExecutionMode::FloatingPointModeIEEEINTEL:
740       return false;
741     default:
742       return true;
743   }
744 }
745 
746 }  // namespace
747 
ValidateFloatControls2(ValidationState_t & _)748 spv_result_t ValidateFloatControls2(ValidationState_t& _) {
749   std::unordered_set<uint32_t> fp_fast_math_default_entry_points;
750   for (auto entry_point : _.entry_points()) {
751     const auto* exec_modes = _.GetExecutionModes(entry_point);
752     if (exec_modes &&
753         exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) {
754       fp_fast_math_default_entry_points.insert(entry_point);
755     }
756   }
757 
758   std::vector<std::pair<const Instruction*, spv::Decoration>> worklist;
759   for (const auto& inst : _.ordered_instructions()) {
760     if (inst.opcode() != spv::Op::OpDecorate) {
761       continue;
762     }
763 
764     const auto decoration = inst.GetOperandAs<spv::Decoration>(1);
765     const auto target_id = inst.GetOperandAs<uint32_t>(0);
766     const auto target = _.FindDef(target_id);
767     if (decoration == spv::Decoration::NoContraction) {
768       worklist.push_back(std::make_pair(target, decoration));
769     } else if (decoration == spv::Decoration::FPFastMathMode) {
770       auto mask = inst.GetOperandAs<spv::FPFastMathModeMask>(2);
771       if ((mask & spv::FPFastMathModeMask::Fast) !=
772           spv::FPFastMathModeMask::MaskNone) {
773         worklist.push_back(std::make_pair(target, decoration));
774       }
775     }
776   }
777 
778   std::unordered_set<const Instruction*> visited;
779   while (!worklist.empty()) {
780     const auto inst = worklist.back().first;
781     const auto decoration = worklist.back().second;
782     worklist.pop_back();
783 
784     if (!visited.insert(inst).second) {
785       continue;
786     }
787 
788     const auto function = inst->function();
789     if (function) {
790       const auto& entry_points = _.FunctionEntryPoints(function->id());
791       for (auto entry_point : entry_points) {
792         if (fp_fast_math_default_entry_points.count(entry_point)) {
793           const std::string dec = decoration == spv::Decoration::NoContraction
794                                       ? "NoContraction"
795                                       : "FPFastMathMode Fast";
796           return _.diag(SPV_ERROR_INVALID_DATA, inst)
797                  << dec
798                  << " cannot be used by an entry point with the "
799                     "FPFastMathDefault execution mode";
800         }
801       }
802     } else {
803       for (const auto& pair : inst->uses()) {
804         worklist.push_back(std::make_pair(pair.first, decoration));
805       }
806     }
807   }
808 
809   return SPV_SUCCESS;
810 }
811 
ModeSettingPass(ValidationState_t & _,const Instruction * inst)812 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
813   switch (inst->opcode()) {
814     case spv::Op::OpEntryPoint:
815       if (auto error = ValidateEntryPoint(_, inst)) return error;
816       break;
817     case spv::Op::OpExecutionMode:
818     case spv::Op::OpExecutionModeId:
819       if (auto error = ValidateExecutionMode(_, inst)) return error;
820       break;
821     case spv::Op::OpMemoryModel:
822       if (auto error = ValidateMemoryModel(_, inst)) return error;
823       break;
824     default:
825       break;
826   }
827   return SPV_SUCCESS;
828 }
829 
ValidateDuplicateExecutionModes(ValidationState_t & _)830 spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _) {
831   using PerEntryKey = std::tuple<spv::ExecutionMode, uint32_t>;
832   using PerOperandKey = std::tuple<spv::ExecutionMode, uint32_t, uint32_t>;
833   std::set<PerEntryKey> seen_per_entry;
834   std::set<PerOperandKey> seen_per_operand;
835 
836   const auto lookupMode = [&_](spv::ExecutionMode mode) -> std::string {
837     spv_operand_desc desc = nullptr;
838     if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE,
839                                   static_cast<uint32_t>(mode),
840                                   &desc) == SPV_SUCCESS) {
841       return std::string(desc->name);
842     }
843     return "Unknown";
844   };
845 
846   for (const auto& inst : _.ordered_instructions()) {
847     if (inst.opcode() != spv::Op::OpExecutionMode &&
848         inst.opcode() != spv::Op::OpExecutionModeId) {
849       continue;
850     }
851 
852     const auto entry = inst.GetOperandAs<uint32_t>(0);
853     const auto mode = inst.GetOperandAs<spv::ExecutionMode>(1);
854     if (PerEntryExecutionMode(mode)) {
855       if (!seen_per_entry.insert(std::make_tuple(mode, entry)).second) {
856         return _.diag(SPV_ERROR_INVALID_ID, &inst)
857                << lookupMode(mode)
858                << " execution mode must not be specified multiple times per "
859                   "entry point";
860       }
861     } else {
862       // Execution modes allowed multiple times all take a single operand.
863       const auto operand = inst.GetOperandAs<uint32_t>(2);
864       if (!seen_per_operand.insert(std::make_tuple(mode, entry, operand))
865                .second) {
866         return _.diag(SPV_ERROR_INVALID_ID, &inst)
867                << lookupMode(mode)
868                << " execution mode must not be specified multiple times for "
869                   "the same entry point and operands";
870       }
871     }
872   }
873 
874   return SPV_SUCCESS;
875 }
876 
877 }  // namespace val
878 }  // namespace spvtools
879