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 "source/val/validate.h"
16 
17 #include <algorithm>
18 
19 #include "source/opcode.h"
20 #include "source/spirv_target_env.h"
21 #include "source/val/instruction.h"
22 #include "source/val/validation_state.h"
23 
24 namespace spvtools {
25 namespace val {
26 namespace {
27 
ValidateEntryPoint(ValidationState_t & _,const Instruction * inst)28 spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
29   const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
30   auto entry_point = _.FindDef(entry_point_id);
31   if (!entry_point || SpvOpFunction != entry_point->opcode()) {
32     return _.diag(SPV_ERROR_INVALID_ID, inst)
33            << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
34            << "' is not a function.";
35   }
36 
37   // Only check the shader execution models
38   const SpvExecutionModel execution_model =
39       inst->GetOperandAs<SpvExecutionModel>(0);
40   if (execution_model != SpvExecutionModelKernel) {
41     const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
42     const auto entry_point_type = _.FindDef(entry_point_type_id);
43     if (!entry_point_type || 3 != entry_point_type->words().size()) {
44       return _.diag(SPV_ERROR_INVALID_ID, inst)
45              << "OpEntryPoint Entry Point <id> '" << _.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 || SpvOpTypeVoid != return_type->opcode()) {
52     return _.diag(SPV_ERROR_INVALID_ID, inst)
53            << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
54            << "'s function return type is not void.";
55   }
56 
57   const auto* execution_modes = _.GetExecutionModes(entry_point_id);
58   if (_.HasCapability(SpvCapabilityShader)) {
59     switch (execution_model) {
60       case SpvExecutionModelFragment:
61         if (execution_modes &&
62             execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
63             execution_modes->count(SpvExecutionModeOriginLowerLeft)) {
64           return _.diag(SPV_ERROR_INVALID_DATA, inst)
65                  << "Fragment execution model entry points can only specify "
66                     "one of OriginUpperLeft or OriginLowerLeft execution "
67                     "modes.";
68         }
69         if (!execution_modes ||
70             (!execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
71              !execution_modes->count(SpvExecutionModeOriginLowerLeft))) {
72           return _.diag(SPV_ERROR_INVALID_DATA, inst)
73                  << "Fragment execution model entry points require either an "
74                     "OriginUpperLeft or OriginLowerLeft execution mode.";
75         }
76         if (execution_modes &&
77             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
78                               [](const SpvExecutionMode& mode) {
79                                 switch (mode) {
80                                   case SpvExecutionModeDepthGreater:
81                                   case SpvExecutionModeDepthLess:
82                                   case SpvExecutionModeDepthUnchanged:
83                                     return true;
84                                   default:
85                                     return false;
86                                 }
87                               })) {
88           return _.diag(SPV_ERROR_INVALID_DATA, inst)
89                  << "Fragment execution model entry points can specify at most "
90                     "one of DepthGreater, DepthLess or DepthUnchanged "
91                     "execution modes.";
92         }
93         if (execution_modes &&
94             1 < std::count_if(
95                     execution_modes->begin(), execution_modes->end(),
96                     [](const SpvExecutionMode& mode) {
97                       switch (mode) {
98                         case SpvExecutionModePixelInterlockOrderedEXT:
99                         case SpvExecutionModePixelInterlockUnorderedEXT:
100                         case SpvExecutionModeSampleInterlockOrderedEXT:
101                         case SpvExecutionModeSampleInterlockUnorderedEXT:
102                         case SpvExecutionModeShadingRateInterlockOrderedEXT:
103                         case SpvExecutionModeShadingRateInterlockUnorderedEXT:
104                           return true;
105                         default:
106                           return false;
107                       }
108                     })) {
109           return _.diag(SPV_ERROR_INVALID_DATA, inst)
110                  << "Fragment execution model entry points can specify at most "
111                     "one fragment shader interlock execution mode.";
112         }
113         break;
114       case SpvExecutionModelTessellationControl:
115       case SpvExecutionModelTessellationEvaluation:
116         if (execution_modes &&
117             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
118                               [](const SpvExecutionMode& mode) {
119                                 switch (mode) {
120                                   case SpvExecutionModeSpacingEqual:
121                                   case SpvExecutionModeSpacingFractionalEven:
122                                   case SpvExecutionModeSpacingFractionalOdd:
123                                     return true;
124                                   default:
125                                     return false;
126                                 }
127                               })) {
128           return _.diag(SPV_ERROR_INVALID_DATA, inst)
129                  << "Tessellation execution model entry points can specify at "
130                     "most one of SpacingEqual, SpacingFractionalOdd or "
131                     "SpacingFractionalEven execution modes.";
132         }
133         if (execution_modes &&
134             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
135                               [](const SpvExecutionMode& mode) {
136                                 switch (mode) {
137                                   case SpvExecutionModeTriangles:
138                                   case SpvExecutionModeQuads:
139                                   case SpvExecutionModeIsolines:
140                                     return true;
141                                   default:
142                                     return false;
143                                 }
144                               })) {
145           return _.diag(SPV_ERROR_INVALID_DATA, inst)
146                  << "Tessellation execution model entry points can specify at "
147                     "most one of Triangles, Quads or Isolines execution modes.";
148         }
149         if (execution_modes &&
150             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
151                               [](const SpvExecutionMode& mode) {
152                                 switch (mode) {
153                                   case SpvExecutionModeVertexOrderCw:
154                                   case SpvExecutionModeVertexOrderCcw:
155                                     return true;
156                                   default:
157                                     return false;
158                                 }
159                               })) {
160           return _.diag(SPV_ERROR_INVALID_DATA, inst)
161                  << "Tessellation execution model entry points can specify at "
162                     "most one of VertexOrderCw or VertexOrderCcw execution "
163                     "modes.";
164         }
165         break;
166       case SpvExecutionModelGeometry:
167         if (!execution_modes ||
168             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
169                                [](const SpvExecutionMode& mode) {
170                                  switch (mode) {
171                                    case SpvExecutionModeInputPoints:
172                                    case SpvExecutionModeInputLines:
173                                    case SpvExecutionModeInputLinesAdjacency:
174                                    case SpvExecutionModeTriangles:
175                                    case SpvExecutionModeInputTrianglesAdjacency:
176                                      return true;
177                                    default:
178                                      return false;
179                                  }
180                                })) {
181           return _.diag(SPV_ERROR_INVALID_DATA, inst)
182                  << "Geometry execution model entry points must specify "
183                     "exactly one of InputPoints, InputLines, "
184                     "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
185                     "execution modes.";
186         }
187         if (!execution_modes ||
188             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
189                                [](const SpvExecutionMode& mode) {
190                                  switch (mode) {
191                                    case SpvExecutionModeOutputPoints:
192                                    case SpvExecutionModeOutputLineStrip:
193                                    case SpvExecutionModeOutputTriangleStrip:
194                                      return true;
195                                    default:
196                                      return false;
197                                  }
198                                })) {
199           return _.diag(SPV_ERROR_INVALID_DATA, inst)
200                  << "Geometry execution model entry points must specify "
201                     "exactly one of OutputPoints, OutputLineStrip or "
202                     "OutputTriangleStrip execution modes.";
203         }
204         break;
205       default:
206         break;
207     }
208   }
209 
210   if (spvIsVulkanEnv(_.context()->target_env)) {
211     switch (execution_model) {
212       case SpvExecutionModelGLCompute:
213         if (!execution_modes ||
214             !execution_modes->count(SpvExecutionModeLocalSize)) {
215           bool ok = false;
216           for (auto& i : _.ordered_instructions()) {
217             if (i.opcode() == SpvOpDecorate) {
218               if (i.operands().size() > 2) {
219                 if (i.GetOperandAs<SpvDecoration>(1) == SpvDecorationBuiltIn &&
220                     i.GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
221                   ok = true;
222                   break;
223                 }
224               }
225             }
226           }
227           if (!ok) {
228             return _.diag(SPV_ERROR_INVALID_DATA, inst)
229                    << "In the Vulkan environment, GLCompute execution model "
230                       "entry points require either the LocalSize execution "
231                       "mode or an object decorated with WorkgroupSize must be "
232                       "specified.";
233           }
234         }
235         break;
236       default:
237         break;
238     }
239   }
240 
241   return SPV_SUCCESS;
242 }
243 
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)244 spv_result_t ValidateExecutionMode(ValidationState_t& _,
245                                    const Instruction* inst) {
246   const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
247   const auto found = std::find(_.entry_points().cbegin(),
248                                _.entry_points().cend(), entry_point_id);
249   if (found == _.entry_points().cend()) {
250     return _.diag(SPV_ERROR_INVALID_ID, inst)
251            << "OpExecutionMode Entry Point <id> '"
252            << _.getIdName(entry_point_id)
253            << "' is not the Entry Point "
254               "operand of an OpEntryPoint.";
255   }
256 
257   const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
258   if (inst->opcode() == SpvOpExecutionModeId) {
259     size_t operand_count = inst->operands().size();
260     for (size_t i = 2; i < operand_count; ++i) {
261       const auto operand_id = inst->GetOperandAs<uint32_t>(2);
262       const auto* operand_inst = _.FindDef(operand_id);
263       if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
264           mode == SpvExecutionModeLocalSizeHintId ||
265           mode == SpvExecutionModeLocalSizeId) {
266         if (!spvOpcodeIsConstant(operand_inst->opcode())) {
267           return _.diag(SPV_ERROR_INVALID_ID, inst)
268                  << "For OpExecutionModeId all Extra Operand ids must be "
269                     "constant "
270                     "instructions.";
271         }
272       } else {
273         return _.diag(SPV_ERROR_INVALID_ID, inst)
274                << "OpExecutionModeId is only valid when the Mode operand is an "
275                   "execution mode that takes Extra Operands that are id "
276                   "operands.";
277       }
278     }
279   } else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
280              mode == SpvExecutionModeLocalSizeHintId ||
281              mode == SpvExecutionModeLocalSizeId) {
282     return _.diag(SPV_ERROR_INVALID_DATA, inst)
283            << "OpExecutionMode is only valid when the Mode operand is an "
284               "execution mode that takes no Extra Operands, or takes Extra "
285               "Operands that are not id operands.";
286   }
287 
288   const auto* models = _.GetExecutionModels(entry_point_id);
289   switch (mode) {
290     case SpvExecutionModeInvocations:
291     case SpvExecutionModeInputPoints:
292     case SpvExecutionModeInputLines:
293     case SpvExecutionModeInputLinesAdjacency:
294     case SpvExecutionModeInputTrianglesAdjacency:
295     case SpvExecutionModeOutputLineStrip:
296     case SpvExecutionModeOutputTriangleStrip:
297       if (!std::all_of(models->begin(), models->end(),
298                        [](const SpvExecutionModel& model) {
299                          return model == SpvExecutionModelGeometry;
300                        })) {
301         return _.diag(SPV_ERROR_INVALID_DATA, inst)
302                << "Execution mode can only be used with the Geometry execution "
303                   "model.";
304       }
305       break;
306     case SpvExecutionModeOutputPoints:
307       if (!std::all_of(models->begin(), models->end(),
308                        [&_](const SpvExecutionModel& model) {
309                          switch (model) {
310                            case SpvExecutionModelGeometry:
311                              return true;
312                            case SpvExecutionModelMeshNV:
313                              return _.HasCapability(SpvCapabilityMeshShadingNV);
314                            default:
315                              return false;
316                          }
317                        })) {
318         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
319           return _.diag(SPV_ERROR_INVALID_DATA, inst)
320                  << "Execution mode can only be used with the Geometry or "
321                     "MeshNV execution model.";
322         } else {
323           return _.diag(SPV_ERROR_INVALID_DATA, inst)
324                  << "Execution mode can only be used with the Geometry "
325                     "execution "
326                     "model.";
327         }
328       }
329       break;
330     case SpvExecutionModeSpacingEqual:
331     case SpvExecutionModeSpacingFractionalEven:
332     case SpvExecutionModeSpacingFractionalOdd:
333     case SpvExecutionModeVertexOrderCw:
334     case SpvExecutionModeVertexOrderCcw:
335     case SpvExecutionModePointMode:
336     case SpvExecutionModeQuads:
337     case SpvExecutionModeIsolines:
338       if (!std::all_of(
339               models->begin(), models->end(),
340               [](const SpvExecutionModel& model) {
341                 return (model == SpvExecutionModelTessellationControl) ||
342                        (model == SpvExecutionModelTessellationEvaluation);
343               })) {
344         return _.diag(SPV_ERROR_INVALID_DATA, inst)
345                << "Execution mode can only be used with a tessellation "
346                   "execution model.";
347       }
348       break;
349     case SpvExecutionModeTriangles:
350       if (!std::all_of(models->begin(), models->end(),
351                        [](const SpvExecutionModel& model) {
352                          switch (model) {
353                            case SpvExecutionModelGeometry:
354                            case SpvExecutionModelTessellationControl:
355                            case SpvExecutionModelTessellationEvaluation:
356                              return true;
357                            default:
358                              return false;
359                          }
360                        })) {
361         return _.diag(SPV_ERROR_INVALID_DATA, inst)
362                << "Execution mode can only be used with a Geometry or "
363                   "tessellation execution model.";
364       }
365       break;
366     case SpvExecutionModeOutputVertices:
367       if (!std::all_of(models->begin(), models->end(),
368                        [&_](const SpvExecutionModel& model) {
369                          switch (model) {
370                            case SpvExecutionModelGeometry:
371                            case SpvExecutionModelTessellationControl:
372                            case SpvExecutionModelTessellationEvaluation:
373                              return true;
374                            case SpvExecutionModelMeshNV:
375                              return _.HasCapability(SpvCapabilityMeshShadingNV);
376                            default:
377                              return false;
378                          }
379                        })) {
380         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
381           return _.diag(SPV_ERROR_INVALID_DATA, inst)
382                  << "Execution mode can only be used with a Geometry, "
383                     "tessellation or MeshNV execution model.";
384         } else {
385           return _.diag(SPV_ERROR_INVALID_DATA, inst)
386                  << "Execution mode can only be used with a Geometry or "
387                     "tessellation execution model.";
388         }
389       }
390       break;
391     case SpvExecutionModePixelCenterInteger:
392     case SpvExecutionModeOriginUpperLeft:
393     case SpvExecutionModeOriginLowerLeft:
394     case SpvExecutionModeEarlyFragmentTests:
395     case SpvExecutionModeDepthReplacing:
396     case SpvExecutionModeDepthGreater:
397     case SpvExecutionModeDepthLess:
398     case SpvExecutionModeDepthUnchanged:
399     case SpvExecutionModePixelInterlockOrderedEXT:
400     case SpvExecutionModePixelInterlockUnorderedEXT:
401     case SpvExecutionModeSampleInterlockOrderedEXT:
402     case SpvExecutionModeSampleInterlockUnorderedEXT:
403     case SpvExecutionModeShadingRateInterlockOrderedEXT:
404     case SpvExecutionModeShadingRateInterlockUnorderedEXT:
405       if (!std::all_of(models->begin(), models->end(),
406                        [](const SpvExecutionModel& model) {
407                          return model == SpvExecutionModelFragment;
408                        })) {
409         return _.diag(SPV_ERROR_INVALID_DATA, inst)
410                << "Execution mode can only be used with the Fragment execution "
411                   "model.";
412       }
413       break;
414     case SpvExecutionModeLocalSizeHint:
415     case SpvExecutionModeVecTypeHint:
416     case SpvExecutionModeContractionOff:
417     case SpvExecutionModeLocalSizeHintId:
418       if (!std::all_of(models->begin(), models->end(),
419                        [](const SpvExecutionModel& model) {
420                          return model == SpvExecutionModelKernel;
421                        })) {
422         return _.diag(SPV_ERROR_INVALID_DATA, inst)
423                << "Execution mode can only be used with the Kernel execution "
424                   "model.";
425       }
426       break;
427     case SpvExecutionModeLocalSize:
428     case SpvExecutionModeLocalSizeId:
429       if (!std::all_of(models->begin(), models->end(),
430                        [&_](const SpvExecutionModel& model) {
431                          switch (model) {
432                            case SpvExecutionModelKernel:
433                            case SpvExecutionModelGLCompute:
434                              return true;
435                            case SpvExecutionModelTaskNV:
436                            case SpvExecutionModelMeshNV:
437                              return _.HasCapability(SpvCapabilityMeshShadingNV);
438                            default:
439                              return false;
440                          }
441                        })) {
442         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
443           return _.diag(SPV_ERROR_INVALID_DATA, inst)
444                  << "Execution mode can only be used with a Kernel, GLCompute, "
445                     "MeshNV, or TaskNV execution model.";
446         } else {
447           return _.diag(SPV_ERROR_INVALID_DATA, inst)
448                  << "Execution mode can only be used with a Kernel or "
449                     "GLCompute "
450                     "execution model.";
451         }
452       }
453     default:
454       break;
455   }
456 
457   if (spvIsVulkanEnv(_.context()->target_env)) {
458     if (mode == SpvExecutionModeOriginLowerLeft) {
459       return _.diag(SPV_ERROR_INVALID_DATA, inst)
460              << "In the Vulkan environment, the OriginLowerLeft execution mode "
461                 "must not be used.";
462     }
463     if (mode == SpvExecutionModePixelCenterInteger) {
464       return _.diag(SPV_ERROR_INVALID_DATA, inst)
465              << "In the Vulkan environment, the PixelCenterInteger execution "
466                 "mode must not be used.";
467     }
468   }
469 
470   if (spvIsWebGPUEnv(_.context()->target_env)) {
471     if (mode != SpvExecutionModeOriginUpperLeft &&
472         mode != SpvExecutionModeDepthReplacing &&
473         mode != SpvExecutionModeDepthGreater &&
474         mode != SpvExecutionModeDepthLess &&
475         mode != SpvExecutionModeDepthUnchanged &&
476         mode != SpvExecutionModeLocalSize &&
477         mode != SpvExecutionModeLocalSizeHint) {
478       return _.diag(SPV_ERROR_INVALID_DATA, inst)
479              << "Execution mode must be one of OriginUpperLeft, "
480                 "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
481                 "LocalSize, or LocalSizeHint for WebGPU environment.";
482     }
483   }
484 
485   return SPV_SUCCESS;
486 }
487 
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)488 spv_result_t ValidateMemoryModel(ValidationState_t& _,
489                                  const Instruction* inst) {
490   // Already produced an error if multiple memory model instructions are
491   // present.
492   if (_.memory_model() != SpvMemoryModelVulkanKHR &&
493       _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
494     return _.diag(SPV_ERROR_INVALID_DATA, inst)
495            << "VulkanMemoryModelKHR capability must only be specified if "
496               "the VulkanKHR memory model is used.";
497   }
498 
499   if (spvIsWebGPUEnv(_.context()->target_env)) {
500     if (_.addressing_model() != SpvAddressingModelLogical) {
501       return _.diag(SPV_ERROR_INVALID_DATA, inst)
502              << "Addressing model must be Logical for WebGPU environment.";
503     }
504   }
505 
506   if (spvIsOpenCLEnv(_.context()->target_env)) {
507     if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
508         (_.addressing_model() != SpvAddressingModelPhysical64)) {
509       return _.diag(SPV_ERROR_INVALID_DATA, inst)
510              << "Addressing model must be Physical32 or Physical64 "
511              << "in the OpenCL environment.";
512     }
513     if (_.memory_model() != SpvMemoryModelOpenCL) {
514       return _.diag(SPV_ERROR_INVALID_DATA, inst)
515              << "Memory model must be OpenCL in the OpenCL environment.";
516     }
517   }
518 
519   return SPV_SUCCESS;
520 }
521 
522 }  // namespace
523 
ModeSettingPass(ValidationState_t & _,const Instruction * inst)524 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
525   switch (inst->opcode()) {
526     case SpvOpEntryPoint:
527       if (auto error = ValidateEntryPoint(_, inst)) return error;
528       break;
529     case SpvOpExecutionMode:
530     case SpvOpExecutionModeId:
531       if (auto error = ValidateExecutionMode(_, inst)) return error;
532       break;
533     case SpvOpMemoryModel:
534       if (auto error = ValidateMemoryModel(_, inst)) return error;
535       break;
536     default:
537       break;
538   }
539   return SPV_SUCCESS;
540 }
541 
542 }  // namespace val
543 }  // namespace spvtools
544