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