• 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         valid_mode = true;
350         break;
351       default:
352         valid_mode = false;
353         break;
354     }
355     if (!valid_mode) {
356       return _.diag(SPV_ERROR_INVALID_ID, inst)
357              << "OpExecutionModeId is only valid when the Mode operand is an "
358                 "execution mode that takes Extra Operands that are id "
359                 "operands.";
360     }
361 
362     size_t operand_count = inst->operands().size();
363     for (size_t i = 2; i < operand_count; ++i) {
364       const auto operand_id = inst->GetOperandAs<uint32_t>(i);
365       const auto* operand_inst = _.FindDef(operand_id);
366       switch (mode) {
367         case spv::ExecutionMode::SubgroupsPerWorkgroupId:
368         case spv::ExecutionMode::LocalSizeHintId:
369         case spv::ExecutionMode::LocalSizeId:
370           if (!spvOpcodeIsConstant(operand_inst->opcode())) {
371             return _.diag(SPV_ERROR_INVALID_ID, inst)
372                    << "For OpExecutionModeId all Extra Operand ids must be "
373                       "constant instructions.";
374           }
375           break;
376         case spv::ExecutionMode::FPFastMathDefault:
377           if (i == 2) {
378             if (!_.IsFloatScalarType(operand_id)) {
379               return _.diag(SPV_ERROR_INVALID_ID, inst)
380                      << "The Target Type operand must be a floating-point "
381                         "scalar type";
382             }
383           } else {
384             bool is_int32 = false;
385             bool is_const = false;
386             uint32_t value = 0;
387             std::tie(is_int32, is_const, value) =
388                 _.EvalInt32IfConst(operand_id);
389             if (is_int32 && is_const) {
390               // Valid values include up to 0x00040000 (AllowTransform).
391               uint32_t invalid_mask = 0xfff80000;
392               if ((invalid_mask & value) != 0) {
393                 return _.diag(SPV_ERROR_INVALID_ID, inst)
394                        << "The Fast Math Default operand is an invalid bitmask "
395                           "value";
396               }
397               if (value &
398                   static_cast<uint32_t>(spv::FPFastMathModeMask::Fast)) {
399                 return _.diag(SPV_ERROR_INVALID_ID, inst)
400                        << "The Fast Math Default operand must not include Fast";
401               }
402               const auto reassoc_contract =
403                   spv::FPFastMathModeMask::AllowContract |
404                   spv::FPFastMathModeMask::AllowReassoc;
405               if ((value & static_cast<uint32_t>(
406                                spv::FPFastMathModeMask::AllowTransform)) != 0 &&
407                   ((value & static_cast<uint32_t>(reassoc_contract)) !=
408                    static_cast<uint32_t>(reassoc_contract))) {
409                 return _.diag(SPV_ERROR_INVALID_ID, inst)
410                        << "The Fast Math Default operand must include "
411                           "AllowContract and AllowReassoc when AllowTransform "
412                           "is specified";
413               }
414             } else {
415               return _.diag(SPV_ERROR_INVALID_ID, inst)
416                      << "The Fast Math Default operand must be a "
417                         "non-specialization constant";
418             }
419           }
420           break;
421         default:
422           break;
423       }
424     }
425   } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
426              mode == spv::ExecutionMode::LocalSizeHintId ||
427              mode == spv::ExecutionMode::LocalSizeId ||
428              mode == spv::ExecutionMode::FPFastMathDefault) {
429     return _.diag(SPV_ERROR_INVALID_DATA, inst)
430            << "OpExecutionMode is only valid when the Mode operand is an "
431               "execution mode that takes no Extra Operands, or takes Extra "
432               "Operands that are not id operands.";
433   }
434 
435   const auto* models = _.GetExecutionModels(entry_point_id);
436   switch (mode) {
437     case spv::ExecutionMode::Invocations:
438     case spv::ExecutionMode::InputPoints:
439     case spv::ExecutionMode::InputLines:
440     case spv::ExecutionMode::InputLinesAdjacency:
441     case spv::ExecutionMode::InputTrianglesAdjacency:
442     case spv::ExecutionMode::OutputLineStrip:
443     case spv::ExecutionMode::OutputTriangleStrip:
444       if (!std::all_of(models->begin(), models->end(),
445                        [](const spv::ExecutionModel& model) {
446                          return model == spv::ExecutionModel::Geometry;
447                        })) {
448         return _.diag(SPV_ERROR_INVALID_DATA, inst)
449                << "Execution mode can only be used with the Geometry execution "
450                   "model.";
451       }
452       break;
453     case spv::ExecutionMode::OutputPoints:
454       if (!std::all_of(
455               models->begin(), models->end(),
456               [&_](const spv::ExecutionModel& model) {
457                 switch (model) {
458                   case spv::ExecutionModel::Geometry:
459                     return true;
460                   case spv::ExecutionModel::MeshNV:
461                     return _.HasCapability(spv::Capability::MeshShadingNV);
462                   case spv::ExecutionModel::MeshEXT:
463                     return _.HasCapability(spv::Capability::MeshShadingEXT);
464                   default:
465                     return false;
466                 }
467               })) {
468         if (_.HasCapability(spv::Capability::MeshShadingNV) ||
469             _.HasCapability(spv::Capability::MeshShadingEXT)) {
470           return _.diag(SPV_ERROR_INVALID_DATA, inst)
471                  << "Execution mode can only be used with the Geometry "
472                     "MeshNV or MeshEXT execution model.";
473         } else {
474           return _.diag(SPV_ERROR_INVALID_DATA, inst)
475                  << "Execution mode can only be used with the Geometry "
476                     "execution "
477                     "model.";
478         }
479       }
480       break;
481     case spv::ExecutionMode::SpacingEqual:
482     case spv::ExecutionMode::SpacingFractionalEven:
483     case spv::ExecutionMode::SpacingFractionalOdd:
484     case spv::ExecutionMode::VertexOrderCw:
485     case spv::ExecutionMode::VertexOrderCcw:
486     case spv::ExecutionMode::PointMode:
487     case spv::ExecutionMode::Quads:
488     case spv::ExecutionMode::Isolines:
489       if (!std::all_of(
490               models->begin(), models->end(),
491               [](const spv::ExecutionModel& model) {
492                 return (model == spv::ExecutionModel::TessellationControl) ||
493                        (model == spv::ExecutionModel::TessellationEvaluation);
494               })) {
495         return _.diag(SPV_ERROR_INVALID_DATA, inst)
496                << "Execution mode can only be used with a tessellation "
497                   "execution model.";
498       }
499       break;
500     case spv::ExecutionMode::Triangles:
501       if (!std::all_of(models->begin(), models->end(),
502                        [](const spv::ExecutionModel& model) {
503                          switch (model) {
504                            case spv::ExecutionModel::Geometry:
505                            case spv::ExecutionModel::TessellationControl:
506                            case spv::ExecutionModel::TessellationEvaluation:
507                              return true;
508                            default:
509                              return false;
510                          }
511                        })) {
512         return _.diag(SPV_ERROR_INVALID_DATA, inst)
513                << "Execution mode can only be used with a Geometry or "
514                   "tessellation execution model.";
515       }
516       break;
517     case spv::ExecutionMode::OutputVertices:
518       if (!std::all_of(
519               models->begin(), models->end(),
520               [&_](const spv::ExecutionModel& model) {
521                 switch (model) {
522                   case spv::ExecutionModel::Geometry:
523                   case spv::ExecutionModel::TessellationControl:
524                   case spv::ExecutionModel::TessellationEvaluation:
525                     return true;
526                   case spv::ExecutionModel::MeshNV:
527                     return _.HasCapability(spv::Capability::MeshShadingNV);
528                   case spv::ExecutionModel::MeshEXT:
529                     return _.HasCapability(spv::Capability::MeshShadingEXT);
530                   default:
531                     return false;
532                 }
533               })) {
534         if (_.HasCapability(spv::Capability::MeshShadingNV) ||
535             _.HasCapability(spv::Capability::MeshShadingEXT)) {
536           return _.diag(SPV_ERROR_INVALID_DATA, inst)
537                  << "Execution mode can only be used with a Geometry, "
538                     "tessellation, MeshNV or MeshEXT execution model.";
539         } else {
540           return _.diag(SPV_ERROR_INVALID_DATA, inst)
541                  << "Execution mode can only be used with a Geometry or "
542                     "tessellation execution model.";
543         }
544       }
545       break;
546     case spv::ExecutionMode::OutputLinesEXT:
547     case spv::ExecutionMode::OutputTrianglesEXT:
548     case spv::ExecutionMode::OutputPrimitivesEXT:
549       if (!std::all_of(models->begin(), models->end(),
550                        [](const spv::ExecutionModel& model) {
551                          return (model == spv::ExecutionModel::MeshEXT ||
552                                  model == spv::ExecutionModel::MeshNV);
553                        })) {
554         return _.diag(SPV_ERROR_INVALID_DATA, inst)
555                << "Execution mode can only be used with the MeshEXT or MeshNV "
556                   "execution "
557                   "model.";
558       }
559       break;
560     case spv::ExecutionMode::QuadDerivativesKHR:
561       if (!std::all_of(models->begin(), models->end(),
562                        [](const spv::ExecutionModel& model) {
563                          return (model == spv::ExecutionModel::Fragment ||
564                                  model == spv::ExecutionModel::GLCompute);
565                        })) {
566         return _.diag(SPV_ERROR_INVALID_DATA, inst)
567                << "Execution mode can only be used with the Fragment or "
568                   "GLCompute execution model.";
569       }
570       break;
571     case spv::ExecutionMode::PixelCenterInteger:
572     case spv::ExecutionMode::OriginUpperLeft:
573     case spv::ExecutionMode::OriginLowerLeft:
574     case spv::ExecutionMode::EarlyFragmentTests:
575     case spv::ExecutionMode::DepthReplacing:
576     case spv::ExecutionMode::DepthGreater:
577     case spv::ExecutionMode::DepthLess:
578     case spv::ExecutionMode::DepthUnchanged:
579     case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
580     case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
581     case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
582     case spv::ExecutionMode::PixelInterlockOrderedEXT:
583     case spv::ExecutionMode::PixelInterlockUnorderedEXT:
584     case spv::ExecutionMode::SampleInterlockOrderedEXT:
585     case spv::ExecutionMode::SampleInterlockUnorderedEXT:
586     case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
587     case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
588     case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
589     case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
590     case spv::ExecutionMode::StencilRefGreaterFrontAMD:
591     case spv::ExecutionMode::StencilRefLessFrontAMD:
592     case spv::ExecutionMode::StencilRefUnchangedBackAMD:
593     case spv::ExecutionMode::StencilRefGreaterBackAMD:
594     case spv::ExecutionMode::StencilRefLessBackAMD:
595     case spv::ExecutionMode::RequireFullQuadsKHR:
596       if (!std::all_of(models->begin(), models->end(),
597                        [](const spv::ExecutionModel& model) {
598                          return model == spv::ExecutionModel::Fragment;
599                        })) {
600         return _.diag(SPV_ERROR_INVALID_DATA, inst)
601                << "Execution mode can only be used with the Fragment execution "
602                   "model.";
603       }
604       break;
605     case spv::ExecutionMode::LocalSizeHint:
606     case spv::ExecutionMode::VecTypeHint:
607     case spv::ExecutionMode::ContractionOff:
608     case spv::ExecutionMode::LocalSizeHintId:
609       if (!std::all_of(models->begin(), models->end(),
610                        [](const spv::ExecutionModel& model) {
611                          return model == spv::ExecutionModel::Kernel;
612                        })) {
613         return _.diag(SPV_ERROR_INVALID_DATA, inst)
614                << "Execution mode can only be used with the Kernel execution "
615                   "model.";
616       }
617       break;
618     case spv::ExecutionMode::LocalSize:
619     case spv::ExecutionMode::LocalSizeId:
620       if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed())
621         return _.diag(SPV_ERROR_INVALID_DATA, inst)
622                << "LocalSizeId mode is not allowed by the current environment.";
623 
624       if (!std::all_of(
625               models->begin(), models->end(),
626               [&_](const spv::ExecutionModel& model) {
627                 switch (model) {
628                   case spv::ExecutionModel::Kernel:
629                   case spv::ExecutionModel::GLCompute:
630                     return true;
631                   case spv::ExecutionModel::TaskNV:
632                   case spv::ExecutionModel::MeshNV:
633                     return _.HasCapability(spv::Capability::MeshShadingNV);
634                   case spv::ExecutionModel::TaskEXT:
635                   case spv::ExecutionModel::MeshEXT:
636                     return _.HasCapability(spv::Capability::MeshShadingEXT);
637                   default:
638                     return false;
639                 }
640               })) {
641         if (_.HasCapability(spv::Capability::MeshShadingNV) ||
642             _.HasCapability(spv::Capability::MeshShadingEXT)) {
643           return _.diag(SPV_ERROR_INVALID_DATA, inst)
644                  << "Execution mode can only be used with a Kernel, GLCompute, "
645                     "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
646         } else {
647           return _.diag(SPV_ERROR_INVALID_DATA, inst)
648                  << "Execution mode can only be used with a Kernel or "
649                     "GLCompute "
650                     "execution model.";
651         }
652       }
653     default:
654       break;
655   }
656 
657   if (mode == spv::ExecutionMode::FPFastMathDefault) {
658     const auto* modes = _.GetExecutionModes(entry_point_id);
659     if (modes && modes->count(spv::ExecutionMode::ContractionOff)) {
660       return _.diag(SPV_ERROR_INVALID_DATA, inst)
661              << "FPFastMathDefault and ContractionOff execution modes cannot "
662                 "be applied to the same entry point";
663     }
664     if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) {
665       return _.diag(SPV_ERROR_INVALID_DATA, inst)
666              << "FPFastMathDefault and SignedZeroInfNanPreserve execution "
667                 "modes cannot be applied to the same entry point";
668     }
669   }
670 
671   if (spvIsVulkanEnv(_.context()->target_env)) {
672     if (mode == spv::ExecutionMode::OriginLowerLeft) {
673       return _.diag(SPV_ERROR_INVALID_DATA, inst)
674              << _.VkErrorID(4653)
675              << "In the Vulkan environment, the OriginLowerLeft execution mode "
676                 "must not be used.";
677     }
678     if (mode == spv::ExecutionMode::PixelCenterInteger) {
679       return _.diag(SPV_ERROR_INVALID_DATA, inst)
680              << _.VkErrorID(4654)
681              << "In the Vulkan environment, the PixelCenterInteger execution "
682                 "mode must not be used.";
683     }
684   }
685 
686   return SPV_SUCCESS;
687 }
688 
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)689 spv_result_t ValidateMemoryModel(ValidationState_t& _,
690                                  const Instruction* inst) {
691   // Already produced an error if multiple memory model instructions are
692   // present.
693   if (_.memory_model() != spv::MemoryModel::VulkanKHR &&
694       _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
695     return _.diag(SPV_ERROR_INVALID_DATA, inst)
696            << "VulkanMemoryModelKHR capability must only be specified if "
697               "the VulkanKHR memory model is used.";
698   }
699 
700   if (spvIsOpenCLEnv(_.context()->target_env)) {
701     if ((_.addressing_model() != spv::AddressingModel::Physical32) &&
702         (_.addressing_model() != spv::AddressingModel::Physical64)) {
703       return _.diag(SPV_ERROR_INVALID_DATA, inst)
704              << "Addressing model must be Physical32 or Physical64 "
705              << "in the OpenCL environment.";
706     }
707     if (_.memory_model() != spv::MemoryModel::OpenCL) {
708       return _.diag(SPV_ERROR_INVALID_DATA, inst)
709              << "Memory model must be OpenCL in the OpenCL environment.";
710     }
711   }
712 
713   if (spvIsVulkanEnv(_.context()->target_env)) {
714     if ((_.addressing_model() != spv::AddressingModel::Logical) &&
715         (_.addressing_model() !=
716          spv::AddressingModel::PhysicalStorageBuffer64)) {
717       return _.diag(SPV_ERROR_INVALID_DATA, inst)
718              << _.VkErrorID(4635)
719              << "Addressing model must be Logical or PhysicalStorageBuffer64 "
720              << "in the Vulkan environment.";
721     }
722   }
723   return SPV_SUCCESS;
724 }
725 
726 }  // namespace
727 
ValidateFloatControls2(ValidationState_t & _)728 spv_result_t ValidateFloatControls2(ValidationState_t& _) {
729   std::unordered_set<uint32_t> fp_fast_math_default_entry_points;
730   for (auto entry_point : _.entry_points()) {
731     const auto* exec_modes = _.GetExecutionModes(entry_point);
732     if (exec_modes &&
733         exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) {
734       fp_fast_math_default_entry_points.insert(entry_point);
735     }
736   }
737 
738   std::vector<std::pair<const Instruction*, spv::Decoration>> worklist;
739   for (const auto& inst : _.ordered_instructions()) {
740     if (inst.opcode() != spv::Op::OpDecorate) {
741       continue;
742     }
743 
744     const auto decoration = inst.GetOperandAs<spv::Decoration>(1);
745     const auto target_id = inst.GetOperandAs<uint32_t>(0);
746     const auto target = _.FindDef(target_id);
747     if (decoration == spv::Decoration::NoContraction) {
748       worklist.push_back(std::make_pair(target, decoration));
749     } else if (decoration == spv::Decoration::FPFastMathMode) {
750       auto mask = inst.GetOperandAs<spv::FPFastMathModeMask>(2);
751       if ((mask & spv::FPFastMathModeMask::Fast) !=
752           spv::FPFastMathModeMask::MaskNone) {
753         worklist.push_back(std::make_pair(target, decoration));
754       }
755     }
756   }
757 
758   std::unordered_set<const Instruction*> visited;
759   while (!worklist.empty()) {
760     const auto inst = worklist.back().first;
761     const auto decoration = worklist.back().second;
762     worklist.pop_back();
763 
764     if (!visited.insert(inst).second) {
765       continue;
766     }
767 
768     const auto function = inst->function();
769     if (function) {
770       const auto& entry_points = _.FunctionEntryPoints(function->id());
771       for (auto entry_point : entry_points) {
772         if (fp_fast_math_default_entry_points.count(entry_point)) {
773           const std::string dec = decoration == spv::Decoration::NoContraction
774                                       ? "NoContraction"
775                                       : "FPFastMathMode Fast";
776           return _.diag(SPV_ERROR_INVALID_DATA, inst)
777                  << dec
778                  << " cannot be used by an entry point with the "
779                     "FPFastMathDefault execution mode";
780         }
781       }
782     } else {
783       for (const auto& pair : inst->uses()) {
784         worklist.push_back(std::make_pair(pair.first, decoration));
785       }
786     }
787   }
788 
789   return SPV_SUCCESS;
790 }
791 
ModeSettingPass(ValidationState_t & _,const Instruction * inst)792 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
793   switch (inst->opcode()) {
794     case spv::Op::OpEntryPoint:
795       if (auto error = ValidateEntryPoint(_, inst)) return error;
796       break;
797     case spv::Op::OpExecutionMode:
798     case spv::Op::OpExecutionModeId:
799       if (auto error = ValidateExecutionMode(_, inst)) return error;
800       break;
801     case spv::Op::OpMemoryModel:
802       if (auto error = ValidateMemoryModel(_, inst)) return error;
803       break;
804     default:
805       break;
806   }
807   return SPV_SUCCESS;
808 }
809 
810 }  // namespace val
811 }  // namespace spvtools
812