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 != SpvExecutionModelTessellationControl &&
148 model != SpvExecutionModelGLCompute) {
149 if (message) {
150 *message =
151 errorVUID +
152 "in Vulkan environment, Workgroup execution scope is "
153 "only for TaskNV, MeshNV, TessellationControl, and "
154 "GLCompute execution models";
155 }
156 return false;
157 }
158 return true;
159 });
160 }
161
162 // Vulkan generic rules
163 // Scope for execution must be limited to Workgroup or Subgroup
164 if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) {
165 return _.diag(SPV_ERROR_INVALID_DATA, inst)
166 << _.VkErrorID(4636) << spvOpcodeString(opcode)
167 << ": in Vulkan environment Execution Scope is limited to "
168 << "Workgroup and Subgroup";
169 }
170 }
171
172 // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
173
174 // General SPIRV rules
175 // Scope for execution must be limited to Workgroup or Subgroup for
176 // non-uniform operations
177 if (spvOpcodeIsNonUniformGroupOperation(opcode) &&
178 value != SpvScopeSubgroup && value != SpvScopeWorkgroup) {
179 return _.diag(SPV_ERROR_INVALID_DATA, inst)
180 << spvOpcodeString(opcode)
181 << ": Execution scope is limited to Subgroup or Workgroup";
182 }
183
184 return SPV_SUCCESS;
185 }
186
ValidateMemoryScope(ValidationState_t & _,const Instruction * inst,uint32_t scope)187 spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
188 uint32_t scope) {
189 const SpvOp opcode = inst->opcode();
190 bool is_int32 = false, is_const_int32 = false;
191 uint32_t value = 0;
192 std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope);
193
194 if (auto error = ValidateScope(_, inst, scope)) {
195 return error;
196 }
197
198 if (!is_const_int32) {
199 return SPV_SUCCESS;
200 }
201
202 if (value == SpvScopeQueueFamilyKHR) {
203 if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
204 return SPV_SUCCESS;
205 } else {
206 return _.diag(SPV_ERROR_INVALID_DATA, inst)
207 << spvOpcodeString(opcode)
208 << ": Memory Scope QueueFamilyKHR requires capability "
209 << "VulkanMemoryModelKHR";
210 }
211 }
212
213 if (value == SpvScopeDevice &&
214 _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) &&
215 !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) {
216 return _.diag(SPV_ERROR_INVALID_DATA, inst)
217 << "Use of device scope with VulkanKHR memory model requires the "
218 << "VulkanMemoryModelDeviceScopeKHR capability";
219 }
220
221 // Vulkan Specific rules
222 if (spvIsVulkanEnv(_.context()->target_env)) {
223 if (value == SpvScopeCrossDevice) {
224 return _.diag(SPV_ERROR_INVALID_DATA, inst)
225 << _.VkErrorID(4638) << spvOpcodeString(opcode)
226 << ": in Vulkan environment, Memory Scope cannot be CrossDevice";
227 }
228 // Vulkan 1.0 specifc rules
229 if (_.context()->target_env == SPV_ENV_VULKAN_1_0 &&
230 value != SpvScopeDevice && value != SpvScopeWorkgroup &&
231 value != SpvScopeInvocation) {
232 return _.diag(SPV_ERROR_INVALID_DATA, inst)
233 << _.VkErrorID(4638) << spvOpcodeString(opcode)
234 << ": in Vulkan 1.0 environment Memory Scope is limited to "
235 << "Device, Workgroup and Invocation";
236 }
237 // Vulkan 1.1 specifc rules
238 if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 ||
239 _.context()->target_env == SPV_ENV_VULKAN_1_2) &&
240 value != SpvScopeDevice && value != SpvScopeWorkgroup &&
241 value != SpvScopeSubgroup && value != SpvScopeInvocation &&
242 value != SpvScopeShaderCallKHR) {
243 return _.diag(SPV_ERROR_INVALID_DATA, inst)
244 << _.VkErrorID(4638) << spvOpcodeString(opcode)
245 << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited "
246 << "to Device, Workgroup, Invocation, and ShaderCall";
247 }
248
249 if (value == SpvScopeShaderCallKHR) {
250 std::string errorVUID = _.VkErrorID(4640);
251 _.function(inst->function()->id())
252 ->RegisterExecutionModelLimitation(
253 [errorVUID](SpvExecutionModel model, std::string* message) {
254 if (model != SpvExecutionModelRayGenerationKHR &&
255 model != SpvExecutionModelIntersectionKHR &&
256 model != SpvExecutionModelAnyHitKHR &&
257 model != SpvExecutionModelClosestHitKHR &&
258 model != SpvExecutionModelMissKHR &&
259 model != SpvExecutionModelCallableKHR) {
260 if (message) {
261 *message =
262 errorVUID +
263 "ShaderCallKHR Memory Scope requires a ray tracing "
264 "execution model";
265 }
266 return false;
267 }
268 return true;
269 });
270 }
271
272 if (value == SpvScopeWorkgroup) {
273 std::string errorVUID = _.VkErrorID(4639);
274 _.function(inst->function()->id())
275 ->RegisterExecutionModelLimitation(
276 [errorVUID](SpvExecutionModel model, std::string* message) {
277 if (model != SpvExecutionModelGLCompute &&
278 model != SpvExecutionModelTaskNV &&
279 model != SpvExecutionModelMeshNV) {
280 if (message) {
281 *message = errorVUID +
282 "Workgroup Memory Scope is limited to MeshNV, "
283 "TaskNV, and GLCompute execution model";
284 }
285 return false;
286 }
287 return true;
288 });
289 }
290 }
291
292 // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
293
294 return SPV_SUCCESS;
295 }
296
297 } // namespace val
298 } // namespace spvtools
299