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