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 <algorithm>
16
17 #include "source/opcode.h"
18 #include "source/spirv_target_env.h"
19 #include "source/val/instruction.h"
20 #include "source/val/validate.h"
21 #include "source/val/validation_state.h"
22
23 namespace spvtools {
24 namespace val {
25 namespace {
26
ValidateEntryPoint(ValidationState_t & _,const Instruction * inst)27 spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
28 const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
29 auto entry_point = _.FindDef(entry_point_id);
30 if (!entry_point || spv::Op::OpFunction != entry_point->opcode()) {
31 return _.diag(SPV_ERROR_INVALID_ID, inst)
32 << "OpEntryPoint Entry Point <id> " << _.getIdName(entry_point_id)
33 << " is not a function.";
34 }
35
36 // Only check the shader execution models
37 const spv::ExecutionModel execution_model =
38 inst->GetOperandAs<spv::ExecutionModel>(0);
39 if (execution_model != spv::ExecutionModel::Kernel) {
40 const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
41 const auto entry_point_type = _.FindDef(entry_point_type_id);
42 if (!entry_point_type || 3 != entry_point_type->words().size()) {
43 return _.diag(SPV_ERROR_INVALID_ID, inst)
44 << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> "
45 << _.getIdName(entry_point_id)
46 << "s function parameter count is not zero.";
47 }
48 }
49
50 auto return_type = _.FindDef(entry_point->type_id());
51 if (!return_type || spv::Op::OpTypeVoid != return_type->opcode()) {
52 return _.diag(SPV_ERROR_INVALID_ID, inst)
53 << _.VkErrorID(4633) << "OpEntryPoint Entry Point <id> "
54 << _.getIdName(entry_point_id)
55 << "s function return type is not void.";
56 }
57
58 const auto* execution_modes = _.GetExecutionModes(entry_point_id);
59 if (_.HasCapability(spv::Capability::Shader)) {
60 switch (execution_model) {
61 case spv::ExecutionModel::Fragment:
62 if (execution_modes &&
63 execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
64 execution_modes->count(spv::ExecutionMode::OriginLowerLeft)) {
65 return _.diag(SPV_ERROR_INVALID_DATA, inst)
66 << "Fragment execution model entry points can only specify "
67 "one of OriginUpperLeft or OriginLowerLeft execution "
68 "modes.";
69 }
70 if (!execution_modes ||
71 (!execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
72 !execution_modes->count(spv::ExecutionMode::OriginLowerLeft))) {
73 return _.diag(SPV_ERROR_INVALID_DATA, inst)
74 << "Fragment execution model entry points require either an "
75 "OriginUpperLeft or OriginLowerLeft execution mode.";
76 }
77 if (execution_modes &&
78 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
79 [](const spv::ExecutionMode& mode) {
80 switch (mode) {
81 case spv::ExecutionMode::DepthGreater:
82 case spv::ExecutionMode::DepthLess:
83 case spv::ExecutionMode::DepthUnchanged:
84 return true;
85 default:
86 return false;
87 }
88 })) {
89 return _.diag(SPV_ERROR_INVALID_DATA, inst)
90 << "Fragment execution model entry points can specify at most "
91 "one of DepthGreater, DepthLess or DepthUnchanged "
92 "execution modes.";
93 }
94 if (execution_modes &&
95 1 < std::count_if(
96 execution_modes->begin(), execution_modes->end(),
97 [](const spv::ExecutionMode& mode) {
98 switch (mode) {
99 case spv::ExecutionMode::PixelInterlockOrderedEXT:
100 case spv::ExecutionMode::PixelInterlockUnorderedEXT:
101 case spv::ExecutionMode::SampleInterlockOrderedEXT:
102 case spv::ExecutionMode::SampleInterlockUnorderedEXT:
103 case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
104 case spv::ExecutionMode::
105 ShadingRateInterlockUnorderedEXT:
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 if (execution_modes &&
116 1 < std::count_if(
117 execution_modes->begin(), execution_modes->end(),
118 [](const spv::ExecutionMode& mode) {
119 switch (mode) {
120 case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
121 case spv::ExecutionMode::StencilRefLessFrontAMD:
122 case spv::ExecutionMode::StencilRefGreaterFrontAMD:
123 return true;
124 default:
125 return false;
126 }
127 })) {
128 return _.diag(SPV_ERROR_INVALID_DATA, inst)
129 << "Fragment execution model entry points can specify at most "
130 "one of StencilRefUnchangedFrontAMD, "
131 "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
132 "execution modes.";
133 }
134 if (execution_modes &&
135 1 < std::count_if(
136 execution_modes->begin(), execution_modes->end(),
137 [](const spv::ExecutionMode& mode) {
138 switch (mode) {
139 case spv::ExecutionMode::StencilRefUnchangedBackAMD:
140 case spv::ExecutionMode::StencilRefLessBackAMD:
141 case spv::ExecutionMode::StencilRefGreaterBackAMD:
142 return true;
143 default:
144 return false;
145 }
146 })) {
147 return _.diag(SPV_ERROR_INVALID_DATA, inst)
148 << "Fragment execution model entry points can specify at most "
149 "one of StencilRefUnchangedBackAMD, "
150 "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
151 "execution modes.";
152 }
153 break;
154 case spv::ExecutionModel::TessellationControl:
155 case spv::ExecutionModel::TessellationEvaluation:
156 if (execution_modes &&
157 1 < std::count_if(
158 execution_modes->begin(), execution_modes->end(),
159 [](const spv::ExecutionMode& mode) {
160 switch (mode) {
161 case spv::ExecutionMode::SpacingEqual:
162 case spv::ExecutionMode::SpacingFractionalEven:
163 case spv::ExecutionMode::SpacingFractionalOdd:
164 return true;
165 default:
166 return false;
167 }
168 })) {
169 return _.diag(SPV_ERROR_INVALID_DATA, inst)
170 << "Tessellation execution model entry points can specify at "
171 "most one of SpacingEqual, SpacingFractionalOdd or "
172 "SpacingFractionalEven execution modes.";
173 }
174 if (execution_modes &&
175 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
176 [](const spv::ExecutionMode& mode) {
177 switch (mode) {
178 case spv::ExecutionMode::Triangles:
179 case spv::ExecutionMode::Quads:
180 case spv::ExecutionMode::Isolines:
181 return true;
182 default:
183 return false;
184 }
185 })) {
186 return _.diag(SPV_ERROR_INVALID_DATA, inst)
187 << "Tessellation execution model entry points can specify at "
188 "most one of Triangles, Quads or Isolines execution modes.";
189 }
190 if (execution_modes &&
191 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
192 [](const spv::ExecutionMode& mode) {
193 switch (mode) {
194 case spv::ExecutionMode::VertexOrderCw:
195 case spv::ExecutionMode::VertexOrderCcw:
196 return true;
197 default:
198 return false;
199 }
200 })) {
201 return _.diag(SPV_ERROR_INVALID_DATA, inst)
202 << "Tessellation execution model entry points can specify at "
203 "most one of VertexOrderCw or VertexOrderCcw execution "
204 "modes.";
205 }
206 break;
207 case spv::ExecutionModel::Geometry:
208 if (!execution_modes ||
209 1 != std::count_if(
210 execution_modes->begin(), execution_modes->end(),
211 [](const spv::ExecutionMode& mode) {
212 switch (mode) {
213 case spv::ExecutionMode::InputPoints:
214 case spv::ExecutionMode::InputLines:
215 case spv::ExecutionMode::InputLinesAdjacency:
216 case spv::ExecutionMode::Triangles:
217 case spv::ExecutionMode::InputTrianglesAdjacency:
218 return true;
219 default:
220 return false;
221 }
222 })) {
223 return _.diag(SPV_ERROR_INVALID_DATA, inst)
224 << "Geometry execution model entry points must specify "
225 "exactly one of InputPoints, InputLines, "
226 "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
227 "execution modes.";
228 }
229 if (!execution_modes ||
230 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
231 [](const spv::ExecutionMode& mode) {
232 switch (mode) {
233 case spv::ExecutionMode::OutputPoints:
234 case spv::ExecutionMode::OutputLineStrip:
235 case spv::ExecutionMode::OutputTriangleStrip:
236 return true;
237 default:
238 return false;
239 }
240 })) {
241 return _.diag(SPV_ERROR_INVALID_DATA, inst)
242 << "Geometry execution model entry points must specify "
243 "exactly one of OutputPoints, OutputLineStrip or "
244 "OutputTriangleStrip execution modes.";
245 }
246 break;
247 case spv::ExecutionModel::MeshEXT:
248 if (!execution_modes ||
249 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
250 [](const spv::ExecutionMode& mode) {
251 switch (mode) {
252 case spv::ExecutionMode::OutputPoints:
253 case spv::ExecutionMode::OutputLinesEXT:
254 case spv::ExecutionMode::OutputTrianglesEXT:
255 return true;
256 default:
257 return false;
258 }
259 })) {
260 return _.diag(SPV_ERROR_INVALID_DATA, inst)
261 << "MeshEXT execution model entry points must specify exactly "
262 "one of OutputPoints, OutputLinesEXT, or "
263 "OutputTrianglesEXT Execution Modes.";
264 } else if (2 != std::count_if(
265 execution_modes->begin(), execution_modes->end(),
266 [](const spv::ExecutionMode& mode) {
267 switch (mode) {
268 case spv::ExecutionMode::OutputPrimitivesEXT:
269 case spv::ExecutionMode::OutputVertices:
270 return true;
271 default:
272 return false;
273 }
274 })) {
275 return _.diag(SPV_ERROR_INVALID_DATA, inst)
276 << "MeshEXT execution model entry points must specify both "
277 "OutputPrimitivesEXT and OutputVertices Execution Modes.";
278 }
279 break;
280 default:
281 break;
282 }
283 }
284
285 if (spvIsVulkanEnv(_.context()->target_env)) {
286 switch (execution_model) {
287 case spv::ExecutionModel::GLCompute:
288 if (!execution_modes ||
289 !execution_modes->count(spv::ExecutionMode::LocalSize)) {
290 bool ok = false;
291 for (auto& i : _.ordered_instructions()) {
292 if (i.opcode() == spv::Op::OpDecorate) {
293 if (i.operands().size() > 2) {
294 if (i.GetOperandAs<spv::Decoration>(1) ==
295 spv::Decoration::BuiltIn &&
296 i.GetOperandAs<spv::BuiltIn>(2) ==
297 spv::BuiltIn::WorkgroupSize) {
298 ok = true;
299 break;
300 }
301 }
302 }
303 if (i.opcode() == spv::Op::OpExecutionModeId) {
304 const auto mode = i.GetOperandAs<spv::ExecutionMode>(1);
305 if (mode == spv::ExecutionMode::LocalSizeId) {
306 ok = true;
307 break;
308 }
309 }
310 }
311 if (!ok) {
312 return _.diag(SPV_ERROR_INVALID_DATA, inst)
313 << _.VkErrorID(6426)
314 << "In the Vulkan environment, GLCompute execution model "
315 "entry points require either the LocalSize or "
316 "LocalSizeId execution mode or an object decorated with "
317 "WorkgroupSize must be specified.";
318 }
319 }
320 break;
321 default:
322 break;
323 }
324 }
325
326 return SPV_SUCCESS;
327 }
328
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)329 spv_result_t ValidateExecutionMode(ValidationState_t& _,
330 const Instruction* inst) {
331 const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
332 const auto found = std::find(_.entry_points().cbegin(),
333 _.entry_points().cend(), entry_point_id);
334 if (found == _.entry_points().cend()) {
335 return _.diag(SPV_ERROR_INVALID_ID, inst)
336 << "OpExecutionMode Entry Point <id> " << _.getIdName(entry_point_id)
337 << " is not the Entry Point "
338 "operand of an OpEntryPoint.";
339 }
340
341 const auto mode = inst->GetOperandAs<spv::ExecutionMode>(1);
342 if (inst->opcode() == spv::Op::OpExecutionModeId) {
343 bool valid_mode = false;
344 switch (mode) {
345 case spv::ExecutionMode::SubgroupsPerWorkgroupId:
346 case spv::ExecutionMode::LocalSizeHintId:
347 case spv::ExecutionMode::LocalSizeId:
348 case spv::ExecutionMode::FPFastMathDefault:
349 valid_mode = true;
350 break;
351 default:
352 valid_mode = false;
353 break;
354 }
355 if (!valid_mode) {
356 return _.diag(SPV_ERROR_INVALID_ID, inst)
357 << "OpExecutionModeId is only valid when the Mode operand is an "
358 "execution mode that takes Extra Operands that are id "
359 "operands.";
360 }
361
362 size_t operand_count = inst->operands().size();
363 for (size_t i = 2; i < operand_count; ++i) {
364 const auto operand_id = inst->GetOperandAs<uint32_t>(i);
365 const auto* operand_inst = _.FindDef(operand_id);
366 switch (mode) {
367 case spv::ExecutionMode::SubgroupsPerWorkgroupId:
368 case spv::ExecutionMode::LocalSizeHintId:
369 case spv::ExecutionMode::LocalSizeId:
370 if (!spvOpcodeIsConstant(operand_inst->opcode())) {
371 return _.diag(SPV_ERROR_INVALID_ID, inst)
372 << "For OpExecutionModeId all Extra Operand ids must be "
373 "constant instructions.";
374 }
375 break;
376 case spv::ExecutionMode::FPFastMathDefault:
377 if (i == 2) {
378 if (!_.IsFloatScalarType(operand_id)) {
379 return _.diag(SPV_ERROR_INVALID_ID, inst)
380 << "The Target Type operand must be a floating-point "
381 "scalar type";
382 }
383 } else {
384 bool is_int32 = false;
385 bool is_const = false;
386 uint32_t value = 0;
387 std::tie(is_int32, is_const, value) =
388 _.EvalInt32IfConst(operand_id);
389 if (is_int32 && is_const) {
390 // Valid values include up to 0x00040000 (AllowTransform).
391 uint32_t invalid_mask = 0xfff80000;
392 if ((invalid_mask & value) != 0) {
393 return _.diag(SPV_ERROR_INVALID_ID, inst)
394 << "The Fast Math Default operand is an invalid bitmask "
395 "value";
396 }
397 if (value &
398 static_cast<uint32_t>(spv::FPFastMathModeMask::Fast)) {
399 return _.diag(SPV_ERROR_INVALID_ID, inst)
400 << "The Fast Math Default operand must not include Fast";
401 }
402 const auto reassoc_contract =
403 spv::FPFastMathModeMask::AllowContract |
404 spv::FPFastMathModeMask::AllowReassoc;
405 if ((value & static_cast<uint32_t>(
406 spv::FPFastMathModeMask::AllowTransform)) != 0 &&
407 ((value & static_cast<uint32_t>(reassoc_contract)) !=
408 static_cast<uint32_t>(reassoc_contract))) {
409 return _.diag(SPV_ERROR_INVALID_ID, inst)
410 << "The Fast Math Default operand must include "
411 "AllowContract and AllowReassoc when AllowTransform "
412 "is specified";
413 }
414 } else {
415 return _.diag(SPV_ERROR_INVALID_ID, inst)
416 << "The Fast Math Default operand must be a "
417 "non-specialization constant";
418 }
419 }
420 break;
421 default:
422 break;
423 }
424 }
425 } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
426 mode == spv::ExecutionMode::LocalSizeHintId ||
427 mode == spv::ExecutionMode::LocalSizeId ||
428 mode == spv::ExecutionMode::FPFastMathDefault) {
429 return _.diag(SPV_ERROR_INVALID_DATA, inst)
430 << "OpExecutionMode is only valid when the Mode operand is an "
431 "execution mode that takes no Extra Operands, or takes Extra "
432 "Operands that are not id operands.";
433 }
434
435 const auto* models = _.GetExecutionModels(entry_point_id);
436 switch (mode) {
437 case spv::ExecutionMode::Invocations:
438 case spv::ExecutionMode::InputPoints:
439 case spv::ExecutionMode::InputLines:
440 case spv::ExecutionMode::InputLinesAdjacency:
441 case spv::ExecutionMode::InputTrianglesAdjacency:
442 case spv::ExecutionMode::OutputLineStrip:
443 case spv::ExecutionMode::OutputTriangleStrip:
444 if (!std::all_of(models->begin(), models->end(),
445 [](const spv::ExecutionModel& model) {
446 return model == spv::ExecutionModel::Geometry;
447 })) {
448 return _.diag(SPV_ERROR_INVALID_DATA, inst)
449 << "Execution mode can only be used with the Geometry execution "
450 "model.";
451 }
452 break;
453 case spv::ExecutionMode::OutputPoints:
454 if (!std::all_of(
455 models->begin(), models->end(),
456 [&_](const spv::ExecutionModel& model) {
457 switch (model) {
458 case spv::ExecutionModel::Geometry:
459 return true;
460 case spv::ExecutionModel::MeshNV:
461 return _.HasCapability(spv::Capability::MeshShadingNV);
462 case spv::ExecutionModel::MeshEXT:
463 return _.HasCapability(spv::Capability::MeshShadingEXT);
464 default:
465 return false;
466 }
467 })) {
468 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
469 _.HasCapability(spv::Capability::MeshShadingEXT)) {
470 return _.diag(SPV_ERROR_INVALID_DATA, inst)
471 << "Execution mode can only be used with the Geometry "
472 "MeshNV or MeshEXT execution model.";
473 } else {
474 return _.diag(SPV_ERROR_INVALID_DATA, inst)
475 << "Execution mode can only be used with the Geometry "
476 "execution "
477 "model.";
478 }
479 }
480 break;
481 case spv::ExecutionMode::SpacingEqual:
482 case spv::ExecutionMode::SpacingFractionalEven:
483 case spv::ExecutionMode::SpacingFractionalOdd:
484 case spv::ExecutionMode::VertexOrderCw:
485 case spv::ExecutionMode::VertexOrderCcw:
486 case spv::ExecutionMode::PointMode:
487 case spv::ExecutionMode::Quads:
488 case spv::ExecutionMode::Isolines:
489 if (!std::all_of(
490 models->begin(), models->end(),
491 [](const spv::ExecutionModel& model) {
492 return (model == spv::ExecutionModel::TessellationControl) ||
493 (model == spv::ExecutionModel::TessellationEvaluation);
494 })) {
495 return _.diag(SPV_ERROR_INVALID_DATA, inst)
496 << "Execution mode can only be used with a tessellation "
497 "execution model.";
498 }
499 break;
500 case spv::ExecutionMode::Triangles:
501 if (!std::all_of(models->begin(), models->end(),
502 [](const spv::ExecutionModel& model) {
503 switch (model) {
504 case spv::ExecutionModel::Geometry:
505 case spv::ExecutionModel::TessellationControl:
506 case spv::ExecutionModel::TessellationEvaluation:
507 return true;
508 default:
509 return false;
510 }
511 })) {
512 return _.diag(SPV_ERROR_INVALID_DATA, inst)
513 << "Execution mode can only be used with a Geometry or "
514 "tessellation execution model.";
515 }
516 break;
517 case spv::ExecutionMode::OutputVertices:
518 if (!std::all_of(
519 models->begin(), models->end(),
520 [&_](const spv::ExecutionModel& model) {
521 switch (model) {
522 case spv::ExecutionModel::Geometry:
523 case spv::ExecutionModel::TessellationControl:
524 case spv::ExecutionModel::TessellationEvaluation:
525 return true;
526 case spv::ExecutionModel::MeshNV:
527 return _.HasCapability(spv::Capability::MeshShadingNV);
528 case spv::ExecutionModel::MeshEXT:
529 return _.HasCapability(spv::Capability::MeshShadingEXT);
530 default:
531 return false;
532 }
533 })) {
534 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
535 _.HasCapability(spv::Capability::MeshShadingEXT)) {
536 return _.diag(SPV_ERROR_INVALID_DATA, inst)
537 << "Execution mode can only be used with a Geometry, "
538 "tessellation, MeshNV or MeshEXT execution model.";
539 } else {
540 return _.diag(SPV_ERROR_INVALID_DATA, inst)
541 << "Execution mode can only be used with a Geometry or "
542 "tessellation execution model.";
543 }
544 }
545 break;
546 case spv::ExecutionMode::OutputLinesEXT:
547 case spv::ExecutionMode::OutputTrianglesEXT:
548 case spv::ExecutionMode::OutputPrimitivesEXT:
549 if (!std::all_of(models->begin(), models->end(),
550 [](const spv::ExecutionModel& model) {
551 return (model == spv::ExecutionModel::MeshEXT ||
552 model == spv::ExecutionModel::MeshNV);
553 })) {
554 return _.diag(SPV_ERROR_INVALID_DATA, inst)
555 << "Execution mode can only be used with the MeshEXT or MeshNV "
556 "execution "
557 "model.";
558 }
559 break;
560 case spv::ExecutionMode::QuadDerivativesKHR:
561 if (!std::all_of(models->begin(), models->end(),
562 [](const spv::ExecutionModel& model) {
563 return (model == spv::ExecutionModel::Fragment ||
564 model == spv::ExecutionModel::GLCompute);
565 })) {
566 return _.diag(SPV_ERROR_INVALID_DATA, inst)
567 << "Execution mode can only be used with the Fragment or "
568 "GLCompute execution model.";
569 }
570 break;
571 case spv::ExecutionMode::PixelCenterInteger:
572 case spv::ExecutionMode::OriginUpperLeft:
573 case spv::ExecutionMode::OriginLowerLeft:
574 case spv::ExecutionMode::EarlyFragmentTests:
575 case spv::ExecutionMode::DepthReplacing:
576 case spv::ExecutionMode::DepthGreater:
577 case spv::ExecutionMode::DepthLess:
578 case spv::ExecutionMode::DepthUnchanged:
579 case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
580 case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
581 case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
582 case spv::ExecutionMode::PixelInterlockOrderedEXT:
583 case spv::ExecutionMode::PixelInterlockUnorderedEXT:
584 case spv::ExecutionMode::SampleInterlockOrderedEXT:
585 case spv::ExecutionMode::SampleInterlockUnorderedEXT:
586 case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
587 case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
588 case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
589 case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
590 case spv::ExecutionMode::StencilRefGreaterFrontAMD:
591 case spv::ExecutionMode::StencilRefLessFrontAMD:
592 case spv::ExecutionMode::StencilRefUnchangedBackAMD:
593 case spv::ExecutionMode::StencilRefGreaterBackAMD:
594 case spv::ExecutionMode::StencilRefLessBackAMD:
595 case spv::ExecutionMode::RequireFullQuadsKHR:
596 if (!std::all_of(models->begin(), models->end(),
597 [](const spv::ExecutionModel& model) {
598 return model == spv::ExecutionModel::Fragment;
599 })) {
600 return _.diag(SPV_ERROR_INVALID_DATA, inst)
601 << "Execution mode can only be used with the Fragment execution "
602 "model.";
603 }
604 break;
605 case spv::ExecutionMode::LocalSizeHint:
606 case spv::ExecutionMode::VecTypeHint:
607 case spv::ExecutionMode::ContractionOff:
608 case spv::ExecutionMode::LocalSizeHintId:
609 if (!std::all_of(models->begin(), models->end(),
610 [](const spv::ExecutionModel& model) {
611 return model == spv::ExecutionModel::Kernel;
612 })) {
613 return _.diag(SPV_ERROR_INVALID_DATA, inst)
614 << "Execution mode can only be used with the Kernel execution "
615 "model.";
616 }
617 break;
618 case spv::ExecutionMode::LocalSize:
619 case spv::ExecutionMode::LocalSizeId:
620 if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed())
621 return _.diag(SPV_ERROR_INVALID_DATA, inst)
622 << "LocalSizeId mode is not allowed by the current environment.";
623
624 if (!std::all_of(
625 models->begin(), models->end(),
626 [&_](const spv::ExecutionModel& model) {
627 switch (model) {
628 case spv::ExecutionModel::Kernel:
629 case spv::ExecutionModel::GLCompute:
630 return true;
631 case spv::ExecutionModel::TaskNV:
632 case spv::ExecutionModel::MeshNV:
633 return _.HasCapability(spv::Capability::MeshShadingNV);
634 case spv::ExecutionModel::TaskEXT:
635 case spv::ExecutionModel::MeshEXT:
636 return _.HasCapability(spv::Capability::MeshShadingEXT);
637 default:
638 return false;
639 }
640 })) {
641 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
642 _.HasCapability(spv::Capability::MeshShadingEXT)) {
643 return _.diag(SPV_ERROR_INVALID_DATA, inst)
644 << "Execution mode can only be used with a Kernel, GLCompute, "
645 "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
646 } else {
647 return _.diag(SPV_ERROR_INVALID_DATA, inst)
648 << "Execution mode can only be used with a Kernel or "
649 "GLCompute "
650 "execution model.";
651 }
652 }
653 default:
654 break;
655 }
656
657 if (mode == spv::ExecutionMode::FPFastMathDefault) {
658 const auto* modes = _.GetExecutionModes(entry_point_id);
659 if (modes && modes->count(spv::ExecutionMode::ContractionOff)) {
660 return _.diag(SPV_ERROR_INVALID_DATA, inst)
661 << "FPFastMathDefault and ContractionOff execution modes cannot "
662 "be applied to the same entry point";
663 }
664 if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) {
665 return _.diag(SPV_ERROR_INVALID_DATA, inst)
666 << "FPFastMathDefault and SignedZeroInfNanPreserve execution "
667 "modes cannot be applied to the same entry point";
668 }
669 }
670
671 if (spvIsVulkanEnv(_.context()->target_env)) {
672 if (mode == spv::ExecutionMode::OriginLowerLeft) {
673 return _.diag(SPV_ERROR_INVALID_DATA, inst)
674 << _.VkErrorID(4653)
675 << "In the Vulkan environment, the OriginLowerLeft execution mode "
676 "must not be used.";
677 }
678 if (mode == spv::ExecutionMode::PixelCenterInteger) {
679 return _.diag(SPV_ERROR_INVALID_DATA, inst)
680 << _.VkErrorID(4654)
681 << "In the Vulkan environment, the PixelCenterInteger execution "
682 "mode must not be used.";
683 }
684 }
685
686 return SPV_SUCCESS;
687 }
688
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)689 spv_result_t ValidateMemoryModel(ValidationState_t& _,
690 const Instruction* inst) {
691 // Already produced an error if multiple memory model instructions are
692 // present.
693 if (_.memory_model() != spv::MemoryModel::VulkanKHR &&
694 _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
695 return _.diag(SPV_ERROR_INVALID_DATA, inst)
696 << "VulkanMemoryModelKHR capability must only be specified if "
697 "the VulkanKHR memory model is used.";
698 }
699
700 if (spvIsOpenCLEnv(_.context()->target_env)) {
701 if ((_.addressing_model() != spv::AddressingModel::Physical32) &&
702 (_.addressing_model() != spv::AddressingModel::Physical64)) {
703 return _.diag(SPV_ERROR_INVALID_DATA, inst)
704 << "Addressing model must be Physical32 or Physical64 "
705 << "in the OpenCL environment.";
706 }
707 if (_.memory_model() != spv::MemoryModel::OpenCL) {
708 return _.diag(SPV_ERROR_INVALID_DATA, inst)
709 << "Memory model must be OpenCL in the OpenCL environment.";
710 }
711 }
712
713 if (spvIsVulkanEnv(_.context()->target_env)) {
714 if ((_.addressing_model() != spv::AddressingModel::Logical) &&
715 (_.addressing_model() !=
716 spv::AddressingModel::PhysicalStorageBuffer64)) {
717 return _.diag(SPV_ERROR_INVALID_DATA, inst)
718 << _.VkErrorID(4635)
719 << "Addressing model must be Logical or PhysicalStorageBuffer64 "
720 << "in the Vulkan environment.";
721 }
722 }
723 return SPV_SUCCESS;
724 }
725
726 } // namespace
727
ValidateFloatControls2(ValidationState_t & _)728 spv_result_t ValidateFloatControls2(ValidationState_t& _) {
729 std::unordered_set<uint32_t> fp_fast_math_default_entry_points;
730 for (auto entry_point : _.entry_points()) {
731 const auto* exec_modes = _.GetExecutionModes(entry_point);
732 if (exec_modes &&
733 exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) {
734 fp_fast_math_default_entry_points.insert(entry_point);
735 }
736 }
737
738 std::vector<std::pair<const Instruction*, spv::Decoration>> worklist;
739 for (const auto& inst : _.ordered_instructions()) {
740 if (inst.opcode() != spv::Op::OpDecorate) {
741 continue;
742 }
743
744 const auto decoration = inst.GetOperandAs<spv::Decoration>(1);
745 const auto target_id = inst.GetOperandAs<uint32_t>(0);
746 const auto target = _.FindDef(target_id);
747 if (decoration == spv::Decoration::NoContraction) {
748 worklist.push_back(std::make_pair(target, decoration));
749 } else if (decoration == spv::Decoration::FPFastMathMode) {
750 auto mask = inst.GetOperandAs<spv::FPFastMathModeMask>(2);
751 if ((mask & spv::FPFastMathModeMask::Fast) !=
752 spv::FPFastMathModeMask::MaskNone) {
753 worklist.push_back(std::make_pair(target, decoration));
754 }
755 }
756 }
757
758 std::unordered_set<const Instruction*> visited;
759 while (!worklist.empty()) {
760 const auto inst = worklist.back().first;
761 const auto decoration = worklist.back().second;
762 worklist.pop_back();
763
764 if (!visited.insert(inst).second) {
765 continue;
766 }
767
768 const auto function = inst->function();
769 if (function) {
770 const auto& entry_points = _.FunctionEntryPoints(function->id());
771 for (auto entry_point : entry_points) {
772 if (fp_fast_math_default_entry_points.count(entry_point)) {
773 const std::string dec = decoration == spv::Decoration::NoContraction
774 ? "NoContraction"
775 : "FPFastMathMode Fast";
776 return _.diag(SPV_ERROR_INVALID_DATA, inst)
777 << dec
778 << " cannot be used by an entry point with the "
779 "FPFastMathDefault execution mode";
780 }
781 }
782 } else {
783 for (const auto& pair : inst->uses()) {
784 worklist.push_back(std::make_pair(pair.first, decoration));
785 }
786 }
787 }
788
789 return SPV_SUCCESS;
790 }
791
ModeSettingPass(ValidationState_t & _,const Instruction * inst)792 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
793 switch (inst->opcode()) {
794 case spv::Op::OpEntryPoint:
795 if (auto error = ValidateEntryPoint(_, inst)) return error;
796 break;
797 case spv::Op::OpExecutionMode:
798 case spv::Op::OpExecutionModeId:
799 if (auto error = ValidateExecutionMode(_, inst)) return error;
800 break;
801 case spv::Op::OpMemoryModel:
802 if (auto error = ValidateMemoryModel(_, inst)) return error;
803 break;
804 default:
805 break;
806 }
807 return SPV_SUCCESS;
808 }
809
810 } // namespace val
811 } // namespace spvtools
812