• 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            (opcode != spv::Op::OpGroupNonUniformQuadAllKHR) &&
102            (opcode != spv::Op::OpGroupNonUniformQuadAnyKHR)) &&
103           (value != spv::Scope::Subgroup)) {
104         return _.diag(SPV_ERROR_INVALID_DATA, inst)
105                << _.VkErrorID(4642) << spvOpcodeString(opcode)
106                << ": in Vulkan environment Execution scope is limited to "
107                << "Subgroup";
108       }
109     }
110 
111     // OpControlBarrier must only use Subgroup execution scope for a subset of
112     // execution models.
113     if (opcode == spv::Op::OpControlBarrier && value != spv::Scope::Subgroup) {
114       std::string errorVUID = _.VkErrorID(4682);
115       _.function(inst->function()->id())
116           ->RegisterExecutionModelLimitation([errorVUID](
117                                                  spv::ExecutionModel model,
118                                                  std::string* message) {
119             if (model == spv::ExecutionModel::Fragment ||
120                 model == spv::ExecutionModel::Vertex ||
121                 model == spv::ExecutionModel::Geometry ||
122                 model == spv::ExecutionModel::TessellationEvaluation ||
123                 model == spv::ExecutionModel::RayGenerationKHR ||
124                 model == spv::ExecutionModel::IntersectionKHR ||
125                 model == spv::ExecutionModel::AnyHitKHR ||
126                 model == spv::ExecutionModel::ClosestHitKHR ||
127                 model == spv::ExecutionModel::MissKHR) {
128               if (message) {
129                 *message =
130                     errorVUID +
131                     "in Vulkan environment, OpControlBarrier execution scope "
132                     "must be Subgroup for Fragment, Vertex, Geometry, "
133                     "TessellationEvaluation, RayGeneration, Intersection, "
134                     "AnyHit, ClosestHit, and Miss execution models";
135               }
136               return false;
137             }
138             return true;
139           });
140     }
141 
142     // Only subset of execution models support Workgroup.
143     if (value == spv::Scope::Workgroup) {
144       std::string errorVUID = _.VkErrorID(4637);
145       _.function(inst->function()->id())
146           ->RegisterExecutionModelLimitation(
147               [errorVUID](spv::ExecutionModel model, std::string* message) {
148                 if (model != spv::ExecutionModel::TaskNV &&
149                     model != spv::ExecutionModel::MeshNV &&
150                     model != spv::ExecutionModel::TaskEXT &&
151                     model != spv::ExecutionModel::MeshEXT &&
152                     model != spv::ExecutionModel::TessellationControl &&
153                     model != spv::ExecutionModel::GLCompute) {
154                   if (message) {
155                     *message =
156                         errorVUID +
157                         "in Vulkan environment, Workgroup execution scope is "
158                         "only for TaskNV, MeshNV, TaskEXT, MeshEXT, "
159                         "TessellationControl, and GLCompute execution models";
160                   }
161                   return false;
162                 }
163                 return true;
164               });
165     }
166 
167     // Vulkan generic rules
168     // Scope for execution must be limited to Workgroup or Subgroup
169     if (value != spv::Scope::Workgroup && value != spv::Scope::Subgroup) {
170       return _.diag(SPV_ERROR_INVALID_DATA, inst)
171              << _.VkErrorID(4636) << spvOpcodeString(opcode)
172              << ": in Vulkan environment Execution Scope is limited to "
173              << "Workgroup and Subgroup";
174     }
175   }
176 
177   // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
178 
179   // General SPIRV rules
180   // Scope for execution must be limited to Workgroup or Subgroup for
181   // non-uniform operations
182   if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
183       opcode != spv::Op::OpGroupNonUniformQuadAllKHR &&
184       opcode != spv::Op::OpGroupNonUniformQuadAnyKHR &&
185       value != spv::Scope::Subgroup && value != spv::Scope::Workgroup) {
186     return _.diag(SPV_ERROR_INVALID_DATA, inst)
187            << spvOpcodeString(opcode)
188            << ": Execution scope is limited to Subgroup or Workgroup";
189   }
190 
191   return SPV_SUCCESS;
192 }
193 
ValidateMemoryScope(ValidationState_t & _,const Instruction * inst,uint32_t scope)194 spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
195                                  uint32_t scope) {
196   const spv::Op opcode = inst->opcode();
197   bool is_int32 = false, is_const_int32 = false;
198   uint32_t tmp_value = 0;
199   std::tie(is_int32, is_const_int32, tmp_value) = _.EvalInt32IfConst(scope);
200 
201   if (auto error = ValidateScope(_, inst, scope)) {
202     return error;
203   }
204 
205   if (!is_const_int32) {
206     return SPV_SUCCESS;
207   }
208 
209   spv::Scope value = spv::Scope(tmp_value);
210 
211   if (value == spv::Scope::QueueFamilyKHR) {
212     if (_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
213       return SPV_SUCCESS;
214     } else {
215       return _.diag(SPV_ERROR_INVALID_DATA, inst)
216              << spvOpcodeString(opcode)
217              << ": Memory Scope QueueFamilyKHR requires capability "
218              << "VulkanMemoryModelKHR";
219     }
220   }
221 
222   if (value == spv::Scope::Device &&
223       _.HasCapability(spv::Capability::VulkanMemoryModelKHR) &&
224       !_.HasCapability(spv::Capability::VulkanMemoryModelDeviceScopeKHR)) {
225     return _.diag(SPV_ERROR_INVALID_DATA, inst)
226            << "Use of device scope with VulkanKHR memory model requires the "
227            << "VulkanMemoryModelDeviceScopeKHR capability";
228   }
229 
230   // Vulkan Specific rules
231   if (spvIsVulkanEnv(_.context()->target_env)) {
232     if (value != spv::Scope::Device && value != spv::Scope::Workgroup &&
233         value != spv::Scope::Subgroup && value != spv::Scope::Invocation &&
234         value != spv::Scope::ShaderCallKHR &&
235         value != spv::Scope::QueueFamily) {
236       return _.diag(SPV_ERROR_INVALID_DATA, inst)
237              << _.VkErrorID(4638) << spvOpcodeString(opcode)
238              << ": in Vulkan environment Memory Scope is limited to Device, "
239                 "QueueFamily, Workgroup, ShaderCallKHR, Subgroup, or "
240                 "Invocation";
241     } else if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
242                value == spv::Scope::Subgroup &&
243                !_.HasCapability(spv::Capability::SubgroupBallotKHR) &&
244                !_.HasCapability(spv::Capability::SubgroupVoteKHR)) {
245       return _.diag(SPV_ERROR_INVALID_DATA, inst)
246              << _.VkErrorID(7951) << spvOpcodeString(opcode)
247              << ": in Vulkan 1.0 environment Memory Scope is can not be "
248                 "Subgroup without SubgroupBallotKHR or SubgroupVoteKHR "
249                 "declared";
250     }
251 
252     if (value == spv::Scope::ShaderCallKHR) {
253       std::string errorVUID = _.VkErrorID(4640);
254       _.function(inst->function()->id())
255           ->RegisterExecutionModelLimitation(
256               [errorVUID](spv::ExecutionModel model, std::string* message) {
257                 if (model != spv::ExecutionModel::RayGenerationKHR &&
258                     model != spv::ExecutionModel::IntersectionKHR &&
259                     model != spv::ExecutionModel::AnyHitKHR &&
260                     model != spv::ExecutionModel::ClosestHitKHR &&
261                     model != spv::ExecutionModel::MissKHR &&
262                     model != spv::ExecutionModel::CallableKHR) {
263                   if (message) {
264                     *message =
265                         errorVUID +
266                         "ShaderCallKHR Memory Scope requires a ray tracing "
267                         "execution model";
268                   }
269                   return false;
270                 }
271                 return true;
272               });
273     }
274 
275     if (value == spv::Scope::Workgroup) {
276       std::string errorVUID = _.VkErrorID(7321);
277       _.function(inst->function()->id())
278           ->RegisterExecutionModelLimitation(
279               [errorVUID](spv::ExecutionModel model, std::string* message) {
280                 if (model != spv::ExecutionModel::GLCompute &&
281                     model != spv::ExecutionModel::TessellationControl &&
282                     model != spv::ExecutionModel::TaskNV &&
283                     model != spv::ExecutionModel::MeshNV &&
284                     model != spv::ExecutionModel::TaskEXT &&
285                     model != spv::ExecutionModel::MeshEXT) {
286                   if (message) {
287                     *message = errorVUID +
288                                "Workgroup Memory Scope is limited to MeshNV, "
289                                "TaskNV, MeshEXT, TaskEXT, TessellationControl, "
290                                "and GLCompute execution model";
291                   }
292                   return false;
293                 }
294                 return true;
295               });
296 
297       if (_.memory_model() == spv::MemoryModel::GLSL450) {
298         errorVUID = _.VkErrorID(7320);
299         _.function(inst->function()->id())
300             ->RegisterExecutionModelLimitation(
301                 [errorVUID](spv::ExecutionModel model, std::string* message) {
302                   if (model == spv::ExecutionModel::TessellationControl) {
303                     if (message) {
304                       *message =
305                           errorVUID +
306                           "Workgroup Memory Scope can't be used with "
307                           "TessellationControl using GLSL450 Memory Model";
308                     }
309                     return false;
310                   }
311                   return true;
312                 });
313       }
314     }
315   }
316 
317   // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
318 
319   return SPV_SUCCESS;
320 }
321 
322 }  // namespace val
323 }  // namespace spvtools
324