• 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           }
229           if (!ok) {
230             return _.diag(SPV_ERROR_INVALID_DATA, inst)
231                    << _.VkErrorID(4683)
232                    << "In the Vulkan environment, GLCompute execution model "
233                       "entry points require either the LocalSize execution "
234                       "mode or an object decorated with WorkgroupSize must be "
235                       "specified.";
236           }
237         }
238         break;
239       default:
240         break;
241     }
242   }
243 
244   return SPV_SUCCESS;
245 }
246 
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)247 spv_result_t ValidateExecutionMode(ValidationState_t& _,
248                                    const Instruction* inst) {
249   const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
250   const auto found = std::find(_.entry_points().cbegin(),
251                                _.entry_points().cend(), entry_point_id);
252   if (found == _.entry_points().cend()) {
253     return _.diag(SPV_ERROR_INVALID_ID, inst)
254            << "OpExecutionMode Entry Point <id> '"
255            << _.getIdName(entry_point_id)
256            << "' is not the Entry Point "
257               "operand of an OpEntryPoint.";
258   }
259 
260   const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
261   if (inst->opcode() == SpvOpExecutionModeId) {
262     size_t operand_count = inst->operands().size();
263     for (size_t i = 2; i < operand_count; ++i) {
264       const auto operand_id = inst->GetOperandAs<uint32_t>(2);
265       const auto* operand_inst = _.FindDef(operand_id);
266       if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
267           mode == SpvExecutionModeLocalSizeHintId ||
268           mode == SpvExecutionModeLocalSizeId) {
269         if (!spvOpcodeIsConstant(operand_inst->opcode())) {
270           return _.diag(SPV_ERROR_INVALID_ID, inst)
271                  << "For OpExecutionModeId all Extra Operand ids must be "
272                     "constant "
273                     "instructions.";
274         }
275       } else {
276         return _.diag(SPV_ERROR_INVALID_ID, inst)
277                << "OpExecutionModeId is only valid when the Mode operand is an "
278                   "execution mode that takes Extra Operands that are id "
279                   "operands.";
280       }
281     }
282   } else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
283              mode == SpvExecutionModeLocalSizeHintId ||
284              mode == SpvExecutionModeLocalSizeId) {
285     return _.diag(SPV_ERROR_INVALID_DATA, inst)
286            << "OpExecutionMode is only valid when the Mode operand is an "
287               "execution mode that takes no Extra Operands, or takes Extra "
288               "Operands that are not id operands.";
289   }
290 
291   const auto* models = _.GetExecutionModels(entry_point_id);
292   switch (mode) {
293     case SpvExecutionModeInvocations:
294     case SpvExecutionModeInputPoints:
295     case SpvExecutionModeInputLines:
296     case SpvExecutionModeInputLinesAdjacency:
297     case SpvExecutionModeInputTrianglesAdjacency:
298     case SpvExecutionModeOutputLineStrip:
299     case SpvExecutionModeOutputTriangleStrip:
300       if (!std::all_of(models->begin(), models->end(),
301                        [](const SpvExecutionModel& model) {
302                          return model == SpvExecutionModelGeometry;
303                        })) {
304         return _.diag(SPV_ERROR_INVALID_DATA, inst)
305                << "Execution mode can only be used with the Geometry execution "
306                   "model.";
307       }
308       break;
309     case SpvExecutionModeOutputPoints:
310       if (!std::all_of(models->begin(), models->end(),
311                        [&_](const SpvExecutionModel& model) {
312                          switch (model) {
313                            case SpvExecutionModelGeometry:
314                              return true;
315                            case SpvExecutionModelMeshNV:
316                              return _.HasCapability(SpvCapabilityMeshShadingNV);
317                            default:
318                              return false;
319                          }
320                        })) {
321         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
322           return _.diag(SPV_ERROR_INVALID_DATA, inst)
323                  << "Execution mode can only be used with the Geometry or "
324                     "MeshNV execution model.";
325         } else {
326           return _.diag(SPV_ERROR_INVALID_DATA, inst)
327                  << "Execution mode can only be used with the Geometry "
328                     "execution "
329                     "model.";
330         }
331       }
332       break;
333     case SpvExecutionModeSpacingEqual:
334     case SpvExecutionModeSpacingFractionalEven:
335     case SpvExecutionModeSpacingFractionalOdd:
336     case SpvExecutionModeVertexOrderCw:
337     case SpvExecutionModeVertexOrderCcw:
338     case SpvExecutionModePointMode:
339     case SpvExecutionModeQuads:
340     case SpvExecutionModeIsolines:
341       if (!std::all_of(
342               models->begin(), models->end(),
343               [](const SpvExecutionModel& model) {
344                 return (model == SpvExecutionModelTessellationControl) ||
345                        (model == SpvExecutionModelTessellationEvaluation);
346               })) {
347         return _.diag(SPV_ERROR_INVALID_DATA, inst)
348                << "Execution mode can only be used with a tessellation "
349                   "execution model.";
350       }
351       break;
352     case SpvExecutionModeTriangles:
353       if (!std::all_of(models->begin(), models->end(),
354                        [](const SpvExecutionModel& model) {
355                          switch (model) {
356                            case SpvExecutionModelGeometry:
357                            case SpvExecutionModelTessellationControl:
358                            case SpvExecutionModelTessellationEvaluation:
359                              return true;
360                            default:
361                              return false;
362                          }
363                        })) {
364         return _.diag(SPV_ERROR_INVALID_DATA, inst)
365                << "Execution mode can only be used with a Geometry or "
366                   "tessellation execution model.";
367       }
368       break;
369     case SpvExecutionModeOutputVertices:
370       if (!std::all_of(models->begin(), models->end(),
371                        [&_](const SpvExecutionModel& model) {
372                          switch (model) {
373                            case SpvExecutionModelGeometry:
374                            case SpvExecutionModelTessellationControl:
375                            case SpvExecutionModelTessellationEvaluation:
376                              return true;
377                            case SpvExecutionModelMeshNV:
378                              return _.HasCapability(SpvCapabilityMeshShadingNV);
379                            default:
380                              return false;
381                          }
382                        })) {
383         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
384           return _.diag(SPV_ERROR_INVALID_DATA, inst)
385                  << "Execution mode can only be used with a Geometry, "
386                     "tessellation or MeshNV execution model.";
387         } else {
388           return _.diag(SPV_ERROR_INVALID_DATA, inst)
389                  << "Execution mode can only be used with a Geometry or "
390                     "tessellation execution model.";
391         }
392       }
393       break;
394     case SpvExecutionModePixelCenterInteger:
395     case SpvExecutionModeOriginUpperLeft:
396     case SpvExecutionModeOriginLowerLeft:
397     case SpvExecutionModeEarlyFragmentTests:
398     case SpvExecutionModeDepthReplacing:
399     case SpvExecutionModeDepthGreater:
400     case SpvExecutionModeDepthLess:
401     case SpvExecutionModeDepthUnchanged:
402     case SpvExecutionModePixelInterlockOrderedEXT:
403     case SpvExecutionModePixelInterlockUnorderedEXT:
404     case SpvExecutionModeSampleInterlockOrderedEXT:
405     case SpvExecutionModeSampleInterlockUnorderedEXT:
406     case SpvExecutionModeShadingRateInterlockOrderedEXT:
407     case SpvExecutionModeShadingRateInterlockUnorderedEXT:
408       if (!std::all_of(models->begin(), models->end(),
409                        [](const SpvExecutionModel& model) {
410                          return model == SpvExecutionModelFragment;
411                        })) {
412         return _.diag(SPV_ERROR_INVALID_DATA, inst)
413                << "Execution mode can only be used with the Fragment execution "
414                   "model.";
415       }
416       break;
417     case SpvExecutionModeLocalSizeHint:
418     case SpvExecutionModeVecTypeHint:
419     case SpvExecutionModeContractionOff:
420     case SpvExecutionModeLocalSizeHintId:
421       if (!std::all_of(models->begin(), models->end(),
422                        [](const SpvExecutionModel& model) {
423                          return model == SpvExecutionModelKernel;
424                        })) {
425         return _.diag(SPV_ERROR_INVALID_DATA, inst)
426                << "Execution mode can only be used with the Kernel execution "
427                   "model.";
428       }
429       break;
430     case SpvExecutionModeLocalSize:
431     case SpvExecutionModeLocalSizeId:
432       if (!std::all_of(models->begin(), models->end(),
433                        [&_](const SpvExecutionModel& model) {
434                          switch (model) {
435                            case SpvExecutionModelKernel:
436                            case SpvExecutionModelGLCompute:
437                              return true;
438                            case SpvExecutionModelTaskNV:
439                            case SpvExecutionModelMeshNV:
440                              return _.HasCapability(SpvCapabilityMeshShadingNV);
441                            default:
442                              return false;
443                          }
444                        })) {
445         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
446           return _.diag(SPV_ERROR_INVALID_DATA, inst)
447                  << "Execution mode can only be used with a Kernel, GLCompute, "
448                     "MeshNV, or TaskNV execution model.";
449         } else {
450           return _.diag(SPV_ERROR_INVALID_DATA, inst)
451                  << "Execution mode can only be used with a Kernel or "
452                     "GLCompute "
453                     "execution model.";
454         }
455       }
456     default:
457       break;
458   }
459 
460   if (spvIsVulkanEnv(_.context()->target_env)) {
461     if (mode == SpvExecutionModeOriginLowerLeft) {
462       return _.diag(SPV_ERROR_INVALID_DATA, inst)
463              << _.VkErrorID(4653)
464              << "In the Vulkan environment, the OriginLowerLeft execution mode "
465                 "must not be used.";
466     }
467     if (mode == SpvExecutionModePixelCenterInteger) {
468       return _.diag(SPV_ERROR_INVALID_DATA, inst)
469              << _.VkErrorID(4654)
470              << "In the Vulkan environment, the PixelCenterInteger execution "
471                 "mode must not be used.";
472     }
473   }
474 
475   return SPV_SUCCESS;
476 }
477 
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)478 spv_result_t ValidateMemoryModel(ValidationState_t& _,
479                                  const Instruction* inst) {
480   // Already produced an error if multiple memory model instructions are
481   // present.
482   if (_.memory_model() != SpvMemoryModelVulkanKHR &&
483       _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
484     return _.diag(SPV_ERROR_INVALID_DATA, inst)
485            << "VulkanMemoryModelKHR capability must only be specified if "
486               "the VulkanKHR memory model is used.";
487   }
488 
489   if (spvIsOpenCLEnv(_.context()->target_env)) {
490     if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
491         (_.addressing_model() != SpvAddressingModelPhysical64)) {
492       return _.diag(SPV_ERROR_INVALID_DATA, inst)
493              << "Addressing model must be Physical32 or Physical64 "
494              << "in the OpenCL environment.";
495     }
496     if (_.memory_model() != SpvMemoryModelOpenCL) {
497       return _.diag(SPV_ERROR_INVALID_DATA, inst)
498              << "Memory model must be OpenCL in the OpenCL environment.";
499     }
500   }
501 
502   if (spvIsVulkanEnv(_.context()->target_env)) {
503     if ((_.addressing_model() != SpvAddressingModelLogical) &&
504         (_.addressing_model() != SpvAddressingModelPhysicalStorageBuffer64)) {
505       return _.diag(SPV_ERROR_INVALID_DATA, inst)
506              << _.VkErrorID(4635)
507              << "Addressing model must be Logical or PhysicalStorageBuffer64 "
508              << "in the Vulkan environment.";
509     }
510   }
511   return SPV_SUCCESS;
512 }
513 
514 }  // namespace
515 
ModeSettingPass(ValidationState_t & _,const Instruction * inst)516 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
517   switch (inst->opcode()) {
518     case SpvOpEntryPoint:
519       if (auto error = ValidateEntryPoint(_, inst)) return error;
520       break;
521     case SpvOpExecutionMode:
522     case SpvOpExecutionModeId:
523       if (auto error = ValidateExecutionMode(_, inst)) return error;
524       break;
525     case SpvOpMemoryModel:
526       if (auto error = ValidateMemoryModel(_, inst)) return error;
527       break;
528     default:
529       break;
530   }
531   return SPV_SUCCESS;
532 }
533 
534 }  // namespace val
535 }  // namespace spvtools
536