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
37 // Only check the shader execution models
38 const SpvExecutionModel execution_model =
39 inst->GetOperandAs<SpvExecutionModel>(0);
40 if (execution_model != SpvExecutionModelKernel) {
41 const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
42 const auto entry_point_type = _.FindDef(entry_point_type_id);
43 if (!entry_point_type || 3 != entry_point_type->words().size()) {
44 return _.diag(SPV_ERROR_INVALID_ID, inst)
45 << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> '"
46 << _.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 << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> '"
55 << _.getIdName(entry_point_id)
56 << "'s function return type is not void.";
57 }
58
59 const auto* execution_modes = _.GetExecutionModes(entry_point_id);
60 if (_.HasCapability(SpvCapabilityShader)) {
61 switch (execution_model) {
62 case SpvExecutionModelFragment:
63 if (execution_modes &&
64 execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
65 execution_modes->count(SpvExecutionModeOriginLowerLeft)) {
66 return _.diag(SPV_ERROR_INVALID_DATA, inst)
67 << "Fragment execution model entry points can only specify "
68 "one of OriginUpperLeft or OriginLowerLeft execution "
69 "modes.";
70 }
71 if (!execution_modes ||
72 (!execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
73 !execution_modes->count(SpvExecutionModeOriginLowerLeft))) {
74 return _.diag(SPV_ERROR_INVALID_DATA, inst)
75 << "Fragment execution model entry points require either an "
76 "OriginUpperLeft or OriginLowerLeft execution mode.";
77 }
78 if (execution_modes &&
79 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
80 [](const SpvExecutionMode& mode) {
81 switch (mode) {
82 case SpvExecutionModeDepthGreater:
83 case SpvExecutionModeDepthLess:
84 case SpvExecutionModeDepthUnchanged:
85 return true;
86 default:
87 return false;
88 }
89 })) {
90 return _.diag(SPV_ERROR_INVALID_DATA, inst)
91 << "Fragment execution model entry points can specify at most "
92 "one of DepthGreater, DepthLess or DepthUnchanged "
93 "execution modes.";
94 }
95 if (execution_modes &&
96 1 < std::count_if(
97 execution_modes->begin(), execution_modes->end(),
98 [](const SpvExecutionMode& mode) {
99 switch (mode) {
100 case SpvExecutionModePixelInterlockOrderedEXT:
101 case SpvExecutionModePixelInterlockUnorderedEXT:
102 case SpvExecutionModeSampleInterlockOrderedEXT:
103 case SpvExecutionModeSampleInterlockUnorderedEXT:
104 case SpvExecutionModeShadingRateInterlockOrderedEXT:
105 case SpvExecutionModeShadingRateInterlockUnorderedEXT:
106 return true;
107 default:
108 return false;
109 }
110 })) {
111 return _.diag(SPV_ERROR_INVALID_DATA, inst)
112 << "Fragment execution model entry points can specify at most "
113 "one fragment shader interlock execution mode.";
114 }
115 break;
116 case SpvExecutionModelTessellationControl:
117 case SpvExecutionModelTessellationEvaluation:
118 if (execution_modes &&
119 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
120 [](const SpvExecutionMode& mode) {
121 switch (mode) {
122 case SpvExecutionModeSpacingEqual:
123 case SpvExecutionModeSpacingFractionalEven:
124 case SpvExecutionModeSpacingFractionalOdd:
125 return true;
126 default:
127 return false;
128 }
129 })) {
130 return _.diag(SPV_ERROR_INVALID_DATA, inst)
131 << "Tessellation execution model entry points can specify at "
132 "most one of SpacingEqual, SpacingFractionalOdd or "
133 "SpacingFractionalEven execution modes.";
134 }
135 if (execution_modes &&
136 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
137 [](const SpvExecutionMode& mode) {
138 switch (mode) {
139 case SpvExecutionModeTriangles:
140 case SpvExecutionModeQuads:
141 case SpvExecutionModeIsolines:
142 return true;
143 default:
144 return false;
145 }
146 })) {
147 return _.diag(SPV_ERROR_INVALID_DATA, inst)
148 << "Tessellation execution model entry points can specify at "
149 "most one of Triangles, Quads or Isolines execution modes.";
150 }
151 if (execution_modes &&
152 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
153 [](const SpvExecutionMode& mode) {
154 switch (mode) {
155 case SpvExecutionModeVertexOrderCw:
156 case SpvExecutionModeVertexOrderCcw:
157 return true;
158 default:
159 return false;
160 }
161 })) {
162 return _.diag(SPV_ERROR_INVALID_DATA, inst)
163 << "Tessellation execution model entry points can specify at "
164 "most one of VertexOrderCw or VertexOrderCcw execution "
165 "modes.";
166 }
167 break;
168 case SpvExecutionModelGeometry:
169 if (!execution_modes ||
170 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
171 [](const SpvExecutionMode& mode) {
172 switch (mode) {
173 case SpvExecutionModeInputPoints:
174 case SpvExecutionModeInputLines:
175 case SpvExecutionModeInputLinesAdjacency:
176 case SpvExecutionModeTriangles:
177 case SpvExecutionModeInputTrianglesAdjacency:
178 return true;
179 default:
180 return false;
181 }
182 })) {
183 return _.diag(SPV_ERROR_INVALID_DATA, inst)
184 << "Geometry execution model entry points must specify "
185 "exactly one of InputPoints, InputLines, "
186 "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
187 "execution modes.";
188 }
189 if (!execution_modes ||
190 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
191 [](const SpvExecutionMode& mode) {
192 switch (mode) {
193 case SpvExecutionModeOutputPoints:
194 case SpvExecutionModeOutputLineStrip:
195 case SpvExecutionModeOutputTriangleStrip:
196 return true;
197 default:
198 return false;
199 }
200 })) {
201 return _.diag(SPV_ERROR_INVALID_DATA, inst)
202 << "Geometry execution model entry points must specify "
203 "exactly one of OutputPoints, OutputLineStrip or "
204 "OutputTriangleStrip execution modes.";
205 }
206 break;
207 default:
208 break;
209 }
210 }
211
212 if (spvIsVulkanEnv(_.context()->target_env)) {
213 switch (execution_model) {
214 case SpvExecutionModelGLCompute:
215 if (!execution_modes ||
216 !execution_modes->count(SpvExecutionModeLocalSize)) {
217 bool ok = false;
218 for (auto& i : _.ordered_instructions()) {
219 if (i.opcode() == SpvOpDecorate) {
220 if (i.operands().size() > 2) {
221 if (i.GetOperandAs<SpvDecoration>(1) == SpvDecorationBuiltIn &&
222 i.GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
223 ok = true;
224 break;
225 }
226 }
227 }
228 }
229 if (!ok) {
230 return _.diag(SPV_ERROR_INVALID_DATA, inst)
231 << _.VkErrorID(4683)
232 << "In the Vulkan environment, GLCompute execution model "
233 "entry points require either the LocalSize execution "
234 "mode or an object decorated with WorkgroupSize must be "
235 "specified.";
236 }
237 }
238 break;
239 default:
240 break;
241 }
242 }
243
244 return SPV_SUCCESS;
245 }
246
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)247 spv_result_t ValidateExecutionMode(ValidationState_t& _,
248 const Instruction* inst) {
249 const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
250 const auto found = std::find(_.entry_points().cbegin(),
251 _.entry_points().cend(), entry_point_id);
252 if (found == _.entry_points().cend()) {
253 return _.diag(SPV_ERROR_INVALID_ID, inst)
254 << "OpExecutionMode Entry Point <id> '"
255 << _.getIdName(entry_point_id)
256 << "' is not the Entry Point "
257 "operand of an OpEntryPoint.";
258 }
259
260 const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
261 if (inst->opcode() == SpvOpExecutionModeId) {
262 size_t operand_count = inst->operands().size();
263 for (size_t i = 2; i < operand_count; ++i) {
264 const auto operand_id = inst->GetOperandAs<uint32_t>(2);
265 const auto* operand_inst = _.FindDef(operand_id);
266 if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
267 mode == SpvExecutionModeLocalSizeHintId ||
268 mode == SpvExecutionModeLocalSizeId) {
269 if (!spvOpcodeIsConstant(operand_inst->opcode())) {
270 return _.diag(SPV_ERROR_INVALID_ID, inst)
271 << "For OpExecutionModeId all Extra Operand ids must be "
272 "constant "
273 "instructions.";
274 }
275 } else {
276 return _.diag(SPV_ERROR_INVALID_ID, inst)
277 << "OpExecutionModeId is only valid when the Mode operand is an "
278 "execution mode that takes Extra Operands that are id "
279 "operands.";
280 }
281 }
282 } else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
283 mode == SpvExecutionModeLocalSizeHintId ||
284 mode == SpvExecutionModeLocalSizeId) {
285 return _.diag(SPV_ERROR_INVALID_DATA, inst)
286 << "OpExecutionMode is only valid when the Mode operand is an "
287 "execution mode that takes no Extra Operands, or takes Extra "
288 "Operands that are not id operands.";
289 }
290
291 const auto* models = _.GetExecutionModels(entry_point_id);
292 switch (mode) {
293 case SpvExecutionModeInvocations:
294 case SpvExecutionModeInputPoints:
295 case SpvExecutionModeInputLines:
296 case SpvExecutionModeInputLinesAdjacency:
297 case SpvExecutionModeInputTrianglesAdjacency:
298 case SpvExecutionModeOutputLineStrip:
299 case SpvExecutionModeOutputTriangleStrip:
300 if (!std::all_of(models->begin(), models->end(),
301 [](const SpvExecutionModel& model) {
302 return model == SpvExecutionModelGeometry;
303 })) {
304 return _.diag(SPV_ERROR_INVALID_DATA, inst)
305 << "Execution mode can only be used with the Geometry execution "
306 "model.";
307 }
308 break;
309 case SpvExecutionModeOutputPoints:
310 if (!std::all_of(models->begin(), models->end(),
311 [&_](const SpvExecutionModel& model) {
312 switch (model) {
313 case SpvExecutionModelGeometry:
314 return true;
315 case SpvExecutionModelMeshNV:
316 return _.HasCapability(SpvCapabilityMeshShadingNV);
317 default:
318 return false;
319 }
320 })) {
321 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
322 return _.diag(SPV_ERROR_INVALID_DATA, inst)
323 << "Execution mode can only be used with the Geometry or "
324 "MeshNV execution model.";
325 } else {
326 return _.diag(SPV_ERROR_INVALID_DATA, inst)
327 << "Execution mode can only be used with the Geometry "
328 "execution "
329 "model.";
330 }
331 }
332 break;
333 case SpvExecutionModeSpacingEqual:
334 case SpvExecutionModeSpacingFractionalEven:
335 case SpvExecutionModeSpacingFractionalOdd:
336 case SpvExecutionModeVertexOrderCw:
337 case SpvExecutionModeVertexOrderCcw:
338 case SpvExecutionModePointMode:
339 case SpvExecutionModeQuads:
340 case SpvExecutionModeIsolines:
341 if (!std::all_of(
342 models->begin(), models->end(),
343 [](const SpvExecutionModel& model) {
344 return (model == SpvExecutionModelTessellationControl) ||
345 (model == SpvExecutionModelTessellationEvaluation);
346 })) {
347 return _.diag(SPV_ERROR_INVALID_DATA, inst)
348 << "Execution mode can only be used with a tessellation "
349 "execution model.";
350 }
351 break;
352 case SpvExecutionModeTriangles:
353 if (!std::all_of(models->begin(), models->end(),
354 [](const SpvExecutionModel& model) {
355 switch (model) {
356 case SpvExecutionModelGeometry:
357 case SpvExecutionModelTessellationControl:
358 case SpvExecutionModelTessellationEvaluation:
359 return true;
360 default:
361 return false;
362 }
363 })) {
364 return _.diag(SPV_ERROR_INVALID_DATA, inst)
365 << "Execution mode can only be used with a Geometry or "
366 "tessellation execution model.";
367 }
368 break;
369 case SpvExecutionModeOutputVertices:
370 if (!std::all_of(models->begin(), models->end(),
371 [&_](const SpvExecutionModel& model) {
372 switch (model) {
373 case SpvExecutionModelGeometry:
374 case SpvExecutionModelTessellationControl:
375 case SpvExecutionModelTessellationEvaluation:
376 return true;
377 case SpvExecutionModelMeshNV:
378 return _.HasCapability(SpvCapabilityMeshShadingNV);
379 default:
380 return false;
381 }
382 })) {
383 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
384 return _.diag(SPV_ERROR_INVALID_DATA, inst)
385 << "Execution mode can only be used with a Geometry, "
386 "tessellation or MeshNV execution model.";
387 } else {
388 return _.diag(SPV_ERROR_INVALID_DATA, inst)
389 << "Execution mode can only be used with a Geometry or "
390 "tessellation execution model.";
391 }
392 }
393 break;
394 case SpvExecutionModePixelCenterInteger:
395 case SpvExecutionModeOriginUpperLeft:
396 case SpvExecutionModeOriginLowerLeft:
397 case SpvExecutionModeEarlyFragmentTests:
398 case SpvExecutionModeDepthReplacing:
399 case SpvExecutionModeDepthGreater:
400 case SpvExecutionModeDepthLess:
401 case SpvExecutionModeDepthUnchanged:
402 case SpvExecutionModePixelInterlockOrderedEXT:
403 case SpvExecutionModePixelInterlockUnorderedEXT:
404 case SpvExecutionModeSampleInterlockOrderedEXT:
405 case SpvExecutionModeSampleInterlockUnorderedEXT:
406 case SpvExecutionModeShadingRateInterlockOrderedEXT:
407 case SpvExecutionModeShadingRateInterlockUnorderedEXT:
408 if (!std::all_of(models->begin(), models->end(),
409 [](const SpvExecutionModel& model) {
410 return model == SpvExecutionModelFragment;
411 })) {
412 return _.diag(SPV_ERROR_INVALID_DATA, inst)
413 << "Execution mode can only be used with the Fragment execution "
414 "model.";
415 }
416 break;
417 case SpvExecutionModeLocalSizeHint:
418 case SpvExecutionModeVecTypeHint:
419 case SpvExecutionModeContractionOff:
420 case SpvExecutionModeLocalSizeHintId:
421 if (!std::all_of(models->begin(), models->end(),
422 [](const SpvExecutionModel& model) {
423 return model == SpvExecutionModelKernel;
424 })) {
425 return _.diag(SPV_ERROR_INVALID_DATA, inst)
426 << "Execution mode can only be used with the Kernel execution "
427 "model.";
428 }
429 break;
430 case SpvExecutionModeLocalSize:
431 case SpvExecutionModeLocalSizeId:
432 if (!std::all_of(models->begin(), models->end(),
433 [&_](const SpvExecutionModel& model) {
434 switch (model) {
435 case SpvExecutionModelKernel:
436 case SpvExecutionModelGLCompute:
437 return true;
438 case SpvExecutionModelTaskNV:
439 case SpvExecutionModelMeshNV:
440 return _.HasCapability(SpvCapabilityMeshShadingNV);
441 default:
442 return false;
443 }
444 })) {
445 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
446 return _.diag(SPV_ERROR_INVALID_DATA, inst)
447 << "Execution mode can only be used with a Kernel, GLCompute, "
448 "MeshNV, or TaskNV execution model.";
449 } else {
450 return _.diag(SPV_ERROR_INVALID_DATA, inst)
451 << "Execution mode can only be used with a Kernel or "
452 "GLCompute "
453 "execution model.";
454 }
455 }
456 default:
457 break;
458 }
459
460 if (spvIsVulkanEnv(_.context()->target_env)) {
461 if (mode == SpvExecutionModeOriginLowerLeft) {
462 return _.diag(SPV_ERROR_INVALID_DATA, inst)
463 << _.VkErrorID(4653)
464 << "In the Vulkan environment, the OriginLowerLeft execution mode "
465 "must not be used.";
466 }
467 if (mode == SpvExecutionModePixelCenterInteger) {
468 return _.diag(SPV_ERROR_INVALID_DATA, inst)
469 << _.VkErrorID(4654)
470 << "In the Vulkan environment, the PixelCenterInteger execution "
471 "mode must not be used.";
472 }
473 }
474
475 return SPV_SUCCESS;
476 }
477
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)478 spv_result_t ValidateMemoryModel(ValidationState_t& _,
479 const Instruction* inst) {
480 // Already produced an error if multiple memory model instructions are
481 // present.
482 if (_.memory_model() != SpvMemoryModelVulkanKHR &&
483 _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
484 return _.diag(SPV_ERROR_INVALID_DATA, inst)
485 << "VulkanMemoryModelKHR capability must only be specified if "
486 "the VulkanKHR memory model is used.";
487 }
488
489 if (spvIsOpenCLEnv(_.context()->target_env)) {
490 if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
491 (_.addressing_model() != SpvAddressingModelPhysical64)) {
492 return _.diag(SPV_ERROR_INVALID_DATA, inst)
493 << "Addressing model must be Physical32 or Physical64 "
494 << "in the OpenCL environment.";
495 }
496 if (_.memory_model() != SpvMemoryModelOpenCL) {
497 return _.diag(SPV_ERROR_INVALID_DATA, inst)
498 << "Memory model must be OpenCL in the OpenCL environment.";
499 }
500 }
501
502 if (spvIsVulkanEnv(_.context()->target_env)) {
503 if ((_.addressing_model() != SpvAddressingModelLogical) &&
504 (_.addressing_model() != SpvAddressingModelPhysicalStorageBuffer64)) {
505 return _.diag(SPV_ERROR_INVALID_DATA, inst)
506 << _.VkErrorID(4635)
507 << "Addressing model must be Logical or PhysicalStorageBuffer64 "
508 << "in the Vulkan environment.";
509 }
510 }
511 return SPV_SUCCESS;
512 }
513
514 } // namespace
515
ModeSettingPass(ValidationState_t & _,const Instruction * inst)516 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
517 switch (inst->opcode()) {
518 case SpvOpEntryPoint:
519 if (auto error = ValidateEntryPoint(_, inst)) return error;
520 break;
521 case SpvOpExecutionMode:
522 case SpvOpExecutionModeId:
523 if (auto error = ValidateExecutionMode(_, inst)) return error;
524 break;
525 case SpvOpMemoryModel:
526 if (auto error = ValidateMemoryModel(_, inst)) return error;
527 break;
528 default:
529 break;
530 }
531 return SPV_SUCCESS;
532 }
533
534 } // namespace val
535 } // namespace spvtools
536