• 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_scopes.h"
16 
17 #include "source/diagnostic.h"
18 #include "source/spirv_target_env.h"
19 #include "source/val/instruction.h"
20 #include "source/val/validation_state.h"
21 
22 namespace spvtools {
23 namespace val {
24 
IsValidScope(uint32_t scope)25 bool IsValidScope(uint32_t scope) {
26   // Deliberately avoid a default case so we have to update the list when the
27   // scopes list changes.
28   switch (static_cast<SpvScope>(scope)) {
29     case SpvScopeCrossDevice:
30     case SpvScopeDevice:
31     case SpvScopeWorkgroup:
32     case SpvScopeSubgroup:
33     case SpvScopeInvocation:
34     case SpvScopeQueueFamilyKHR:
35     case SpvScopeShaderCallKHR:
36       return true;
37     case SpvScopeMax:
38       break;
39   }
40   return false;
41 }
42 
ValidateScope(ValidationState_t & _,const Instruction * inst,uint32_t scope)43 spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst,
44                            uint32_t scope) {
45   SpvOp opcode = inst->opcode();
46   bool is_int32 = false, is_const_int32 = false;
47   uint32_t value = 0;
48   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
49 
50   if (!is_int32) {
51     return _.diag(SPV_ERROR_INVALID_DATA, inst)
52            << spvOpcodeString(opcode) << ": expected scope to be a 32-bit int";
53   }
54 
55   if (!is_const_int32) {
56     if (_.HasCapability(SpvCapabilityShader) &&
57         !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
58       return _.diag(SPV_ERROR_INVALID_DATA, inst)
59              << "Scope ids must be OpConstant when Shader capability is "
60              << "present";
61     }
62     if (_.HasCapability(SpvCapabilityShader) &&
63         _.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
64         !spvOpcodeIsConstant(_.GetIdOpcode(scope))) {
65       return _.diag(SPV_ERROR_INVALID_DATA, inst)
66              << "Scope ids must be constant or specialization constant when "
67              << "CooperativeMatrixNV capability is present";
68     }
69   }
70 
71   if (is_const_int32 && !IsValidScope(value)) {
72     return _.diag(SPV_ERROR_INVALID_DATA, inst)
73            << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope));
74   }
75 
76   return SPV_SUCCESS;
77 }
78 
ValidateExecutionScope(ValidationState_t & _,const Instruction * inst,uint32_t scope)79 spv_result_t ValidateExecutionScope(ValidationState_t& _,
80                                     const Instruction* inst, uint32_t scope) {
81   SpvOp opcode = inst->opcode();
82   bool is_int32 = false, is_const_int32 = false;
83   uint32_t value = 0;
84   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
85 
86   if (auto error = ValidateScope(_, inst, scope)) {
87     return error;
88   }
89 
90   if (!is_const_int32) {
91     return SPV_SUCCESS;
92   }
93 
94   // Vulkan specific rules
95   if (spvIsVulkanEnv(_.context()->target_env)) {
96     // Vulkan 1.1 specific rules
97     if (_.context()->target_env != SPV_ENV_VULKAN_1_0) {
98       // Scope for Non Uniform Group Operations must be limited to Subgroup
99       if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
100           value != SpvScopeSubgroup) {
101         return _.diag(SPV_ERROR_INVALID_DATA, inst)
102                << _.VkErrorID(4642) << spvOpcodeString(opcode)
103                << ": in Vulkan environment Execution scope is limited to "
104                << "Subgroup";
105       }
106     }
107 
108     // OpControlBarrier must only use Subgroup execution scope for a subset of
109     // execution models.
110     if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) {
111       std::string errorVUID = _.VkErrorID(4682);
112       _.function(inst->function()->id())
113           ->RegisterExecutionModelLimitation([errorVUID](
114                                                  SpvExecutionModel model,
115                                                  std::string* message) {
116             if (model == SpvExecutionModelFragment ||
117                 model == SpvExecutionModelVertex ||
118                 model == SpvExecutionModelGeometry ||
119                 model == SpvExecutionModelTessellationEvaluation ||
120                 model == SpvExecutionModelRayGenerationKHR ||
121                 model == SpvExecutionModelIntersectionKHR ||
122                 model == SpvExecutionModelAnyHitKHR ||
123                 model == SpvExecutionModelClosestHitKHR ||
124                 model == SpvExecutionModelMissKHR) {
125               if (message) {
126                 *message =
127                     errorVUID +
128                     "in Vulkan environment, OpControlBarrier execution scope "
129                     "must be Subgroup for Fragment, Vertex, Geometry, "
130                     "TessellationEvaluation, RayGeneration, Intersection, "
131                     "AnyHit, ClosestHit, and Miss execution models";
132               }
133               return false;
134             }
135             return true;
136           });
137     }
138 
139     // Only subset of execution models support Workgroup.
140     if (value == SpvScopeWorkgroup) {
141       std::string errorVUID = _.VkErrorID(4637);
142       _.function(inst->function()->id())
143           ->RegisterExecutionModelLimitation(
144               [errorVUID](SpvExecutionModel model, std::string* message) {
145                 if (model != SpvExecutionModelTaskNV &&
146                     model != SpvExecutionModelMeshNV &&
147                     model != SpvExecutionModelTaskEXT &&
148                     model != SpvExecutionModelMeshEXT &&
149                     model != SpvExecutionModelTessellationControl &&
150                     model != SpvExecutionModelGLCompute) {
151                   if (message) {
152                     *message =
153                         errorVUID +
154                         "in Vulkan environment, Workgroup execution scope is "
155                         "only for TaskNV, MeshNV, TaskEXT, MeshEXT, "
156                         "TessellationControl, and GLCompute execution models";
157                   }
158                   return false;
159                 }
160                 return true;
161               });
162     }
163 
164     // Vulkan generic rules
165     // Scope for execution must be limited to Workgroup or Subgroup
166     if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
167       return _.diag(SPV_ERROR_INVALID_DATA, inst)
168              << _.VkErrorID(4636) << spvOpcodeString(opcode)
169              << ": in Vulkan environment Execution Scope is limited to "
170              << "Workgroup and Subgroup";
171     }
172   }
173 
174   // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
175 
176   // General SPIRV rules
177   // Scope for execution must be limited to Workgroup or Subgroup for
178   // non-uniform operations
179   if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
180       value != SpvScopeSubgroup && value != SpvScopeWorkgroup) {
181     return _.diag(SPV_ERROR_INVALID_DATA, inst)
182            << spvOpcodeString(opcode)
183            << ": Execution scope is limited to Subgroup or Workgroup";
184   }
185 
186   return SPV_SUCCESS;
187 }
188 
ValidateMemoryScope(ValidationState_t & _,const Instruction * inst,uint32_t scope)189 spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
190                                  uint32_t scope) {
191   const SpvOp opcode = inst->opcode();
192   bool is_int32 = false, is_const_int32 = false;
193   uint32_t value = 0;
194   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
195 
196   if (auto error = ValidateScope(_, inst, scope)) {
197     return error;
198   }
199 
200   if (!is_const_int32) {
201     return SPV_SUCCESS;
202   }
203 
204   if (value == SpvScopeQueueFamilyKHR) {
205     if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
206       return SPV_SUCCESS;
207     } else {
208       return _.diag(SPV_ERROR_INVALID_DATA, inst)
209              << spvOpcodeString(opcode)
210              << ": Memory Scope QueueFamilyKHR requires capability "
211              << "VulkanMemoryModelKHR";
212     }
213   }
214 
215   if (value == SpvScopeDevice &&
216       _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) &&
217       !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) {
218     return _.diag(SPV_ERROR_INVALID_DATA, inst)
219            << "Use of device scope with VulkanKHR memory model requires the "
220            << "VulkanMemoryModelDeviceScopeKHR capability";
221   }
222 
223   // Vulkan Specific rules
224   if (spvIsVulkanEnv(_.context()->target_env)) {
225     if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
226         value != SpvScopeSubgroup && value != SpvScopeInvocation &&
227         value != SpvScopeShaderCallKHR && value != SpvScopeQueueFamily) {
228       return _.diag(SPV_ERROR_INVALID_DATA, inst)
229              << _.VkErrorID(4638) << spvOpcodeString(opcode)
230              << ": in Vulkan environment Memory Scope is limited to Device, "
231                 "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or "
232                 "Invocation";
233     } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
234                value == SpvScopeSubgroup &&
235                !_.HasCapability(SpvCapabilitySubgroupBallotKHR) &&
236                !_.HasCapability(SpvCapabilitySubgroupVoteKHR)) {
237       return _.diag(SPV_ERROR_INVALID_DATA, inst)
238              << _.VkErrorID(6997) << spvOpcodeString(opcode)
239              << ": in Vulkan 1.0 environment Memory Scope is can not be "
240                 "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR "
241                 "declared";
242     }
243 
244     if (value == SpvScopeShaderCallKHR) {
245       std::string errorVUID = _.VkErrorID(4640);
246       _.function(inst->function()->id())
247           ->RegisterExecutionModelLimitation(
248               [errorVUID](SpvExecutionModel model, std::string* message) {
249                 if (model != SpvExecutionModelRayGenerationKHR &&
250                     model != SpvExecutionModelIntersectionKHR &&
251                     model != SpvExecutionModelAnyHitKHR &&
252                     model != SpvExecutionModelClosestHitKHR &&
253                     model != SpvExecutionModelMissKHR &&
254                     model != SpvExecutionModelCallableKHR) {
255                   if (message) {
256                     *message =
257                         errorVUID +
258                         "ShaderCallKHR Memory Scope requires a ray tracing "
259                         "execution model";
260                   }
261                   return false;
262                 }
263                 return true;
264               });
265     }
266 
267     if (value == SpvScopeWorkgroup) {
268       std::string errorVUID = _.VkErrorID(7321);
269       _.function(inst->function()->id())
270           ->RegisterExecutionModelLimitation(
271               [errorVUID](SpvExecutionModel model, std::string* message) {
272                 if (model != SpvExecutionModelGLCompute &&
273                     model != SpvExecutionModelTessellationControl &&
274                     model != SpvExecutionModelTaskNV &&
275                     model != SpvExecutionModelMeshNV &&
276                     model != SpvExecutionModelTaskEXT &&
277                     model != SpvExecutionModelMeshEXT) {
278                   if (message) {
279                     *message = errorVUID +
280                                "Workgroup Memory Scope is limited to MeshNV, "
281                                "TaskNV, MeshEXT, TaskEXT, TessellationControl, "
282                                "and GLCompute execution model";
283                   }
284                   return false;
285                 }
286                 return true;
287               });
288 
289       if (_.memory_model() == SpvMemoryModelGLSL450) {
290         errorVUID = _.VkErrorID(7320);
291         _.function(inst->function()->id())
292             ->RegisterExecutionModelLimitation(
293                 [errorVUID](SpvExecutionModel model, std::string* message) {
294                   if (model == SpvExecutionModelTessellationControl) {
295                     if (message) {
296                       *message =
297                           errorVUID +
298                           "Workgroup Memory Scope can't be used with "
299                           "TessellationControl using GLSL450 Memory Model";
300                     }
301                     return false;
302                   }
303                   return true;
304                 });
305       }
306     }
307   }
308 
309   // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
310 
311   return SPV_SUCCESS;
312 }
313 
314 }  // namespace val
315 }  // namespace spvtools
316