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 if (i.opcode() == SpvOpExecutionModeId) {
229 const auto mode = i.GetOperandAs<SpvExecutionMode>(1);
230 if (mode == SpvExecutionModeLocalSizeId) {
231 ok = true;
232 break;
233 }
234 }
235 }
236 if (!ok) {
237 return _.diag(SPV_ERROR_INVALID_DATA, inst)
238 << _.VkErrorID(6426)
239 << "In the Vulkan environment, GLCompute execution model "
240 "entry points require either the LocalSize or "
241 "LocalSizeId execution mode or an object decorated with "
242 "WorkgroupSize must be specified.";
243 }
244 }
245 break;
246 default:
247 break;
248 }
249 }
250
251 return SPV_SUCCESS;
252 }
253
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)254 spv_result_t ValidateExecutionMode(ValidationState_t& _,
255 const Instruction* inst) {
256 const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
257 const auto found = std::find(_.entry_points().cbegin(),
258 _.entry_points().cend(), entry_point_id);
259 if (found == _.entry_points().cend()) {
260 return _.diag(SPV_ERROR_INVALID_ID, inst)
261 << "OpExecutionMode Entry Point <id> '"
262 << _.getIdName(entry_point_id)
263 << "' is not the Entry Point "
264 "operand of an OpEntryPoint.";
265 }
266
267 const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
268 if (inst->opcode() == SpvOpExecutionModeId) {
269 size_t operand_count = inst->operands().size();
270 for (size_t i = 2; i < operand_count; ++i) {
271 const auto operand_id = inst->GetOperandAs<uint32_t>(2);
272 const auto* operand_inst = _.FindDef(operand_id);
273 if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
274 mode == SpvExecutionModeLocalSizeHintId ||
275 mode == SpvExecutionModeLocalSizeId) {
276 if (!spvOpcodeIsConstant(operand_inst->opcode())) {
277 return _.diag(SPV_ERROR_INVALID_ID, inst)
278 << "For OpExecutionModeId all Extra Operand ids must be "
279 "constant "
280 "instructions.";
281 }
282 } else {
283 return _.diag(SPV_ERROR_INVALID_ID, inst)
284 << "OpExecutionModeId is only valid when the Mode operand is an "
285 "execution mode that takes Extra Operands that are id "
286 "operands.";
287 }
288 }
289 } else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId ||
290 mode == SpvExecutionModeLocalSizeHintId ||
291 mode == SpvExecutionModeLocalSizeId) {
292 return _.diag(SPV_ERROR_INVALID_DATA, inst)
293 << "OpExecutionMode is only valid when the Mode operand is an "
294 "execution mode that takes no Extra Operands, or takes Extra "
295 "Operands that are not id operands.";
296 }
297
298 const auto* models = _.GetExecutionModels(entry_point_id);
299 switch (mode) {
300 case SpvExecutionModeInvocations:
301 case SpvExecutionModeInputPoints:
302 case SpvExecutionModeInputLines:
303 case SpvExecutionModeInputLinesAdjacency:
304 case SpvExecutionModeInputTrianglesAdjacency:
305 case SpvExecutionModeOutputLineStrip:
306 case SpvExecutionModeOutputTriangleStrip:
307 if (!std::all_of(models->begin(), models->end(),
308 [](const SpvExecutionModel& model) {
309 return model == SpvExecutionModelGeometry;
310 })) {
311 return _.diag(SPV_ERROR_INVALID_DATA, inst)
312 << "Execution mode can only be used with the Geometry execution "
313 "model.";
314 }
315 break;
316 case SpvExecutionModeOutputPoints:
317 if (!std::all_of(models->begin(), models->end(),
318 [&_](const SpvExecutionModel& model) {
319 switch (model) {
320 case SpvExecutionModelGeometry:
321 return true;
322 case SpvExecutionModelMeshNV:
323 return _.HasCapability(SpvCapabilityMeshShadingNV);
324 default:
325 return false;
326 }
327 })) {
328 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
329 return _.diag(SPV_ERROR_INVALID_DATA, inst)
330 << "Execution mode can only be used with the Geometry or "
331 "MeshNV execution model.";
332 } else {
333 return _.diag(SPV_ERROR_INVALID_DATA, inst)
334 << "Execution mode can only be used with the Geometry "
335 "execution "
336 "model.";
337 }
338 }
339 break;
340 case SpvExecutionModeSpacingEqual:
341 case SpvExecutionModeSpacingFractionalEven:
342 case SpvExecutionModeSpacingFractionalOdd:
343 case SpvExecutionModeVertexOrderCw:
344 case SpvExecutionModeVertexOrderCcw:
345 case SpvExecutionModePointMode:
346 case SpvExecutionModeQuads:
347 case SpvExecutionModeIsolines:
348 if (!std::all_of(
349 models->begin(), models->end(),
350 [](const SpvExecutionModel& model) {
351 return (model == SpvExecutionModelTessellationControl) ||
352 (model == SpvExecutionModelTessellationEvaluation);
353 })) {
354 return _.diag(SPV_ERROR_INVALID_DATA, inst)
355 << "Execution mode can only be used with a tessellation "
356 "execution model.";
357 }
358 break;
359 case SpvExecutionModeTriangles:
360 if (!std::all_of(models->begin(), models->end(),
361 [](const SpvExecutionModel& model) {
362 switch (model) {
363 case SpvExecutionModelGeometry:
364 case SpvExecutionModelTessellationControl:
365 case SpvExecutionModelTessellationEvaluation:
366 return true;
367 default:
368 return false;
369 }
370 })) {
371 return _.diag(SPV_ERROR_INVALID_DATA, inst)
372 << "Execution mode can only be used with a Geometry or "
373 "tessellation execution model.";
374 }
375 break;
376 case SpvExecutionModeOutputVertices:
377 if (!std::all_of(models->begin(), models->end(),
378 [&_](const SpvExecutionModel& model) {
379 switch (model) {
380 case SpvExecutionModelGeometry:
381 case SpvExecutionModelTessellationControl:
382 case SpvExecutionModelTessellationEvaluation:
383 return true;
384 case SpvExecutionModelMeshNV:
385 return _.HasCapability(SpvCapabilityMeshShadingNV);
386 default:
387 return false;
388 }
389 })) {
390 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
391 return _.diag(SPV_ERROR_INVALID_DATA, inst)
392 << "Execution mode can only be used with a Geometry, "
393 "tessellation or MeshNV execution model.";
394 } else {
395 return _.diag(SPV_ERROR_INVALID_DATA, inst)
396 << "Execution mode can only be used with a Geometry or "
397 "tessellation execution model.";
398 }
399 }
400 break;
401 case SpvExecutionModePixelCenterInteger:
402 case SpvExecutionModeOriginUpperLeft:
403 case SpvExecutionModeOriginLowerLeft:
404 case SpvExecutionModeEarlyFragmentTests:
405 case SpvExecutionModeDepthReplacing:
406 case SpvExecutionModeDepthGreater:
407 case SpvExecutionModeDepthLess:
408 case SpvExecutionModeDepthUnchanged:
409 case SpvExecutionModePixelInterlockOrderedEXT:
410 case SpvExecutionModePixelInterlockUnorderedEXT:
411 case SpvExecutionModeSampleInterlockOrderedEXT:
412 case SpvExecutionModeSampleInterlockUnorderedEXT:
413 case SpvExecutionModeShadingRateInterlockOrderedEXT:
414 case SpvExecutionModeShadingRateInterlockUnorderedEXT:
415 if (!std::all_of(models->begin(), models->end(),
416 [](const SpvExecutionModel& model) {
417 return model == SpvExecutionModelFragment;
418 })) {
419 return _.diag(SPV_ERROR_INVALID_DATA, inst)
420 << "Execution mode can only be used with the Fragment execution "
421 "model.";
422 }
423 break;
424 case SpvExecutionModeLocalSizeHint:
425 case SpvExecutionModeVecTypeHint:
426 case SpvExecutionModeContractionOff:
427 case SpvExecutionModeLocalSizeHintId:
428 if (!std::all_of(models->begin(), models->end(),
429 [](const SpvExecutionModel& model) {
430 return model == SpvExecutionModelKernel;
431 })) {
432 return _.diag(SPV_ERROR_INVALID_DATA, inst)
433 << "Execution mode can only be used with the Kernel execution "
434 "model.";
435 }
436 break;
437 case SpvExecutionModeLocalSize:
438 case SpvExecutionModeLocalSizeId:
439 if (mode == SpvExecutionModeLocalSizeId && !_.IsLocalSizeIdAllowed())
440 return _.diag(SPV_ERROR_INVALID_DATA, inst)
441 << "LocalSizeId mode is not allowed by the current environment.";
442
443 if (!std::all_of(models->begin(), models->end(),
444 [&_](const SpvExecutionModel& model) {
445 switch (model) {
446 case SpvExecutionModelKernel:
447 case SpvExecutionModelGLCompute:
448 return true;
449 case SpvExecutionModelTaskNV:
450 case SpvExecutionModelMeshNV:
451 return _.HasCapability(SpvCapabilityMeshShadingNV);
452 default:
453 return false;
454 }
455 })) {
456 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
457 return _.diag(SPV_ERROR_INVALID_DATA, inst)
458 << "Execution mode can only be used with a Kernel, GLCompute, "
459 "MeshNV, or TaskNV execution model.";
460 } else {
461 return _.diag(SPV_ERROR_INVALID_DATA, inst)
462 << "Execution mode can only be used with a Kernel or "
463 "GLCompute "
464 "execution model.";
465 }
466 }
467 default:
468 break;
469 }
470
471 if (spvIsVulkanEnv(_.context()->target_env)) {
472 if (mode == SpvExecutionModeOriginLowerLeft) {
473 return _.diag(SPV_ERROR_INVALID_DATA, inst)
474 << _.VkErrorID(4653)
475 << "In the Vulkan environment, the OriginLowerLeft execution mode "
476 "must not be used.";
477 }
478 if (mode == SpvExecutionModePixelCenterInteger) {
479 return _.diag(SPV_ERROR_INVALID_DATA, inst)
480 << _.VkErrorID(4654)
481 << "In the Vulkan environment, the PixelCenterInteger execution "
482 "mode must not be used.";
483 }
484 }
485
486 return SPV_SUCCESS;
487 }
488
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)489 spv_result_t ValidateMemoryModel(ValidationState_t& _,
490 const Instruction* inst) {
491 // Already produced an error if multiple memory model instructions are
492 // present.
493 if (_.memory_model() != SpvMemoryModelVulkanKHR &&
494 _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
495 return _.diag(SPV_ERROR_INVALID_DATA, inst)
496 << "VulkanMemoryModelKHR capability must only be specified if "
497 "the VulkanKHR memory model is used.";
498 }
499
500 if (spvIsOpenCLEnv(_.context()->target_env)) {
501 if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
502 (_.addressing_model() != SpvAddressingModelPhysical64)) {
503 return _.diag(SPV_ERROR_INVALID_DATA, inst)
504 << "Addressing model must be Physical32 or Physical64 "
505 << "in the OpenCL environment.";
506 }
507 if (_.memory_model() != SpvMemoryModelOpenCL) {
508 return _.diag(SPV_ERROR_INVALID_DATA, inst)
509 << "Memory model must be OpenCL in the OpenCL environment.";
510 }
511 }
512
513 if (spvIsVulkanEnv(_.context()->target_env)) {
514 if ((_.addressing_model() != SpvAddressingModelLogical) &&
515 (_.addressing_model() != SpvAddressingModelPhysicalStorageBuffer64)) {
516 return _.diag(SPV_ERROR_INVALID_DATA, inst)
517 << _.VkErrorID(4635)
518 << "Addressing model must be Logical or PhysicalStorageBuffer64 "
519 << "in the Vulkan environment.";
520 }
521 }
522 return SPV_SUCCESS;
523 }
524
525 } // namespace
526
ModeSettingPass(ValidationState_t & _,const Instruction * inst)527 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
528 switch (inst->opcode()) {
529 case SpvOpEntryPoint:
530 if (auto error = ValidateEntryPoint(_, inst)) return error;
531 break;
532 case SpvOpExecutionMode:
533 case SpvOpExecutionModeId:
534 if (auto error = ValidateExecutionMode(_, inst)) return error;
535 break;
536 case SpvOpMemoryModel:
537 if (auto error = ValidateMemoryModel(_, inst)) return error;
538 break;
539 default:
540 break;
541 }
542 return SPV_SUCCESS;
543 }
544
545 } // namespace val
546 } // namespace spvtools
547