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   // don't check kernel function signatures
37   const SpvExecutionModel execution_model =
38       inst->GetOperandAs<SpvExecutionModel>(0);
39   if (execution_model != SpvExecutionModelKernel) {
40     // TODO: Check the entry point signature is void main(void), may be subject
41     // to change
42     const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
43     const auto entry_point_type = _.FindDef(entry_point_type_id);
44     if (!entry_point_type || 3 != entry_point_type->words().size()) {
45       return _.diag(SPV_ERROR_INVALID_ID, inst)
46              << "OpEntryPoint Entry Point <id> '" << _.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            << "OpEntryPoint Entry Point <id> '" << _.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(SpvCapabilityShader)) {
60     switch (execution_model) {
61       case SpvExecutionModelFragment:
62         if (execution_modes &&
63             execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
64             execution_modes->count(SpvExecutionModeOriginLowerLeft)) {
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(SpvExecutionModeOriginUpperLeft) &&
72              !execution_modes->count(SpvExecutionModeOriginLowerLeft))) {
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 SpvExecutionMode& mode) {
80                                 switch (mode) {
81                                   case SpvExecutionModeDepthGreater:
82                                   case SpvExecutionModeDepthLess:
83                                   case SpvExecutionModeDepthUnchanged:
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         break;
95       case SpvExecutionModelTessellationControl:
96       case SpvExecutionModelTessellationEvaluation:
97         if (execution_modes &&
98             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
99                               [](const SpvExecutionMode& mode) {
100                                 switch (mode) {
101                                   case SpvExecutionModeSpacingEqual:
102                                   case SpvExecutionModeSpacingFractionalEven:
103                                   case SpvExecutionModeSpacingFractionalOdd:
104                                     return true;
105                                   default:
106                                     return false;
107                                 }
108                               })) {
109           return _.diag(SPV_ERROR_INVALID_DATA, inst)
110                  << "Tessellation execution model entry points can specify at "
111                     "most one of SpacingEqual, SpacingFractionalOdd or "
112                     "SpacingFractionalEven execution modes.";
113         }
114         if (execution_modes &&
115             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
116                               [](const SpvExecutionMode& mode) {
117                                 switch (mode) {
118                                   case SpvExecutionModeTriangles:
119                                   case SpvExecutionModeQuads:
120                                   case SpvExecutionModeIsolines:
121                                     return true;
122                                   default:
123                                     return false;
124                                 }
125                               })) {
126           return _.diag(SPV_ERROR_INVALID_DATA, inst)
127                  << "Tessellation execution model entry points can specify at "
128                     "most one of Triangles, Quads or Isolines execution modes.";
129         }
130         if (execution_modes &&
131             1 < std::count_if(execution_modes->begin(), execution_modes->end(),
132                               [](const SpvExecutionMode& mode) {
133                                 switch (mode) {
134                                   case SpvExecutionModeVertexOrderCw:
135                                   case SpvExecutionModeVertexOrderCcw:
136                                     return true;
137                                   default:
138                                     return false;
139                                 }
140                               })) {
141           return _.diag(SPV_ERROR_INVALID_DATA, inst)
142                  << "Tessellation execution model entry points can specify at "
143                     "most one of VertexOrderCw or VertexOrderCcw execution "
144                     "modes.";
145         }
146         break;
147       case SpvExecutionModelGeometry:
148         if (!execution_modes ||
149             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
150                                [](const SpvExecutionMode& mode) {
151                                  switch (mode) {
152                                    case SpvExecutionModeInputPoints:
153                                    case SpvExecutionModeInputLines:
154                                    case SpvExecutionModeInputLinesAdjacency:
155                                    case SpvExecutionModeTriangles:
156                                    case SpvExecutionModeInputTrianglesAdjacency:
157                                      return true;
158                                    default:
159                                      return false;
160                                  }
161                                })) {
162           return _.diag(SPV_ERROR_INVALID_DATA, inst)
163                  << "Geometry execution model entry points must specify "
164                     "exactly one of InputPoints, InputLines, "
165                     "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
166                     "execution modes.";
167         }
168         if (!execution_modes ||
169             1 != std::count_if(execution_modes->begin(), execution_modes->end(),
170                                [](const SpvExecutionMode& mode) {
171                                  switch (mode) {
172                                    case SpvExecutionModeOutputPoints:
173                                    case SpvExecutionModeOutputLineStrip:
174                                    case SpvExecutionModeOutputTriangleStrip:
175                                      return true;
176                                    default:
177                                      return false;
178                                  }
179                                })) {
180           return _.diag(SPV_ERROR_INVALID_DATA, inst)
181                  << "Geometry execution model entry points must specify "
182                     "exactly one of OutputPoints, OutputLineStrip or "
183                     "OutputTriangleStrip execution modes.";
184         }
185         break;
186       default:
187         break;
188     }
189   }
190 
191   if (spvIsVulkanEnv(_.context()->target_env)) {
192     switch (execution_model) {
193       case SpvExecutionModelGLCompute:
194         if (!execution_modes ||
195             !execution_modes->count(SpvExecutionModeLocalSize)) {
196           bool ok = false;
197           for (auto& i : _.ordered_instructions()) {
198             if (i.opcode() == SpvOpDecorate) {
199               if (i.operands().size() > 2) {
200                 if (i.GetOperandAs<SpvDecoration>(1) == SpvDecorationBuiltIn &&
201                     i.GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
202                   ok = true;
203                   break;
204                 }
205               }
206             }
207           }
208           if (!ok) {
209             return _.diag(SPV_ERROR_INVALID_DATA, inst)
210                    << "In the Vulkan environment, GLCompute execution model "
211                       "entry points require either the LocalSize execution "
212                       "mode or an object decorated with WorkgroupSize must be "
213                       "specified.";
214           }
215         }
216         break;
217       default:
218         break;
219     }
220   }
221 
222   return SPV_SUCCESS;
223 }
224 
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)225 spv_result_t ValidateExecutionMode(ValidationState_t& _,
226                                    const Instruction* inst) {
227   const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
228   const auto found = std::find(_.entry_points().cbegin(),
229                                _.entry_points().cend(), entry_point_id);
230   if (found == _.entry_points().cend()) {
231     return _.diag(SPV_ERROR_INVALID_ID, inst)
232            << "OpExecutionMode Entry Point <id> '"
233            << _.getIdName(entry_point_id)
234            << "' is not the Entry Point "
235               "operand of an OpEntryPoint.";
236   }
237 
238   const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
239   const auto* models = _.GetExecutionModels(entry_point_id);
240   switch (mode) {
241     case SpvExecutionModeInvocations:
242     case SpvExecutionModeInputPoints:
243     case SpvExecutionModeInputLines:
244     case SpvExecutionModeInputLinesAdjacency:
245     case SpvExecutionModeInputTrianglesAdjacency:
246     case SpvExecutionModeOutputLineStrip:
247     case SpvExecutionModeOutputTriangleStrip:
248       if (!std::all_of(models->begin(), models->end(),
249                        [](const SpvExecutionModel& model) {
250                          return model == SpvExecutionModelGeometry;
251                        })) {
252         return _.diag(SPV_ERROR_INVALID_DATA, inst)
253                << "Execution mode can only be used with the Geometry execution "
254                   "model.";
255       }
256       break;
257     case SpvExecutionModeOutputPoints:
258       if (!std::all_of(models->begin(), models->end(),
259                        [&_](const SpvExecutionModel& model) {
260                          switch (model) {
261                            case SpvExecutionModelGeometry:
262                              return true;
263                            case SpvExecutionModelMeshNV:
264                              return _.HasCapability(SpvCapabilityMeshShadingNV);
265                            default:
266                              return false;
267                          }
268                        })) {
269         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
270           return _.diag(SPV_ERROR_INVALID_DATA, inst)
271                  << "Execution mode can only be used with the Geometry or "
272                     "MeshNV execution model.";
273         } else {
274           return _.diag(SPV_ERROR_INVALID_DATA, inst)
275                  << "Execution mode can only be used with the Geometry "
276                     "execution "
277                     "model.";
278         }
279       }
280       break;
281     case SpvExecutionModeSpacingEqual:
282     case SpvExecutionModeSpacingFractionalEven:
283     case SpvExecutionModeSpacingFractionalOdd:
284     case SpvExecutionModeVertexOrderCw:
285     case SpvExecutionModeVertexOrderCcw:
286     case SpvExecutionModePointMode:
287     case SpvExecutionModeQuads:
288     case SpvExecutionModeIsolines:
289       if (!std::all_of(
290               models->begin(), models->end(),
291               [](const SpvExecutionModel& model) {
292                 return (model == SpvExecutionModelTessellationControl) ||
293                        (model == SpvExecutionModelTessellationEvaluation);
294               })) {
295         return _.diag(SPV_ERROR_INVALID_DATA, inst)
296                << "Execution mode can only be used with a tessellation "
297                   "execution model.";
298       }
299       break;
300     case SpvExecutionModeTriangles:
301       if (!std::all_of(models->begin(), models->end(),
302                        [](const SpvExecutionModel& model) {
303                          switch (model) {
304                            case SpvExecutionModelGeometry:
305                            case SpvExecutionModelTessellationControl:
306                            case SpvExecutionModelTessellationEvaluation:
307                              return true;
308                            default:
309                              return false;
310                          }
311                        })) {
312         return _.diag(SPV_ERROR_INVALID_DATA, inst)
313                << "Execution mode can only be used with a Geometry or "
314                   "tessellation execution model.";
315       }
316       break;
317     case SpvExecutionModeOutputVertices:
318       if (!std::all_of(models->begin(), models->end(),
319                        [&_](const SpvExecutionModel& model) {
320                          switch (model) {
321                            case SpvExecutionModelGeometry:
322                            case SpvExecutionModelTessellationControl:
323                            case SpvExecutionModelTessellationEvaluation:
324                              return true;
325                            case SpvExecutionModelMeshNV:
326                              return _.HasCapability(SpvCapabilityMeshShadingNV);
327                            default:
328                              return false;
329                          }
330                        })) {
331         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
332           return _.diag(SPV_ERROR_INVALID_DATA, inst)
333                  << "Execution mode can only be used with a Geometry, "
334                     "tessellation or MeshNV execution model.";
335         } else {
336           return _.diag(SPV_ERROR_INVALID_DATA, inst)
337                  << "Execution mode can only be used with a Geometry or "
338                     "tessellation execution model.";
339         }
340       }
341       break;
342     case SpvExecutionModePixelCenterInteger:
343     case SpvExecutionModeOriginUpperLeft:
344     case SpvExecutionModeOriginLowerLeft:
345     case SpvExecutionModeEarlyFragmentTests:
346     case SpvExecutionModeDepthReplacing:
347     case SpvExecutionModeDepthLess:
348     case SpvExecutionModeDepthUnchanged:
349       if (!std::all_of(models->begin(), models->end(),
350                        [](const SpvExecutionModel& model) {
351                          return model == SpvExecutionModelFragment;
352                        })) {
353         return _.diag(SPV_ERROR_INVALID_DATA, inst)
354                << "Execution mode can only be used with the Fragment execution "
355                   "model.";
356       }
357       break;
358     case SpvExecutionModeLocalSizeHint:
359     case SpvExecutionModeVecTypeHint:
360     case SpvExecutionModeContractionOff:
361     case SpvExecutionModeLocalSizeHintId:
362       if (!std::all_of(models->begin(), models->end(),
363                        [](const SpvExecutionModel& model) {
364                          return model == SpvExecutionModelKernel;
365                        })) {
366         return _.diag(SPV_ERROR_INVALID_DATA, inst)
367                << "Execution mode can only be used with the Kernel execution "
368                   "model.";
369       }
370       break;
371     case SpvExecutionModeLocalSize:
372     case SpvExecutionModeLocalSizeId:
373       if (!std::all_of(models->begin(), models->end(),
374                        [&_](const SpvExecutionModel& model) {
375                          switch (model) {
376                            case SpvExecutionModelKernel:
377                            case SpvExecutionModelGLCompute:
378                              return true;
379                            case SpvExecutionModelTaskNV:
380                            case SpvExecutionModelMeshNV:
381                              return _.HasCapability(SpvCapabilityMeshShadingNV);
382                            default:
383                              return false;
384                          }
385                        })) {
386         if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
387           return _.diag(SPV_ERROR_INVALID_DATA, inst)
388                  << "Execution mode can only be used with a Kernel, GLCompute, "
389                     "MeshNV, or TaskNV execution model.";
390         } else {
391           return _.diag(SPV_ERROR_INVALID_DATA, inst)
392                  << "Execution mode can only be used with a Kernel or "
393                     "GLCompute "
394                     "execution model.";
395         }
396       }
397     default:
398       break;
399   }
400 
401   if (spvIsVulkanEnv(_.context()->target_env)) {
402     if (mode == SpvExecutionModeOriginLowerLeft) {
403       return _.diag(SPV_ERROR_INVALID_DATA, inst)
404              << "In the Vulkan environment, the OriginLowerLeft execution mode "
405                 "must not be used.";
406     }
407     if (mode == SpvExecutionModePixelCenterInteger) {
408       return _.diag(SPV_ERROR_INVALID_DATA, inst)
409              << "In the Vulkan environment, the PixelCenterInteger execution "
410                 "mode must not be used.";
411     }
412   }
413 
414   return SPV_SUCCESS;
415 }
416 
417 }  // namespace
418 
ModeSettingPass(ValidationState_t & _,const Instruction * inst)419 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
420   switch (inst->opcode()) {
421     case SpvOpEntryPoint:
422       if (auto error = ValidateEntryPoint(_, inst)) return error;
423       break;
424     case SpvOpExecutionMode:
425     case SpvOpExecutionModeId:
426       if (auto error = ValidateExecutionMode(_, inst)) return error;
427       break;
428     default:
429       break;
430   }
431   return SPV_SUCCESS;
432 }
433 
434 }  // namespace val
435 }  // namespace spvtools
436