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 case spv::ExecutionMode::MaximumRegistersIdINTEL:
350 valid_mode = true;
351 break;
352 default:
353 valid_mode = false;
354 break;
355 }
356 if (!valid_mode) {
357 return _.diag(SPV_ERROR_INVALID_ID, inst)
358 << "OpExecutionModeId is only valid when the Mode operand is an "
359 "execution mode that takes Extra Operands that are id "
360 "operands.";
361 }
362
363 size_t operand_count = inst->operands().size();
364 for (size_t i = 2; i < operand_count; ++i) {
365 const auto operand_id = inst->GetOperandAs<uint32_t>(i);
366 const auto* operand_inst = _.FindDef(operand_id);
367 switch (mode) {
368 case spv::ExecutionMode::SubgroupsPerWorkgroupId:
369 case spv::ExecutionMode::LocalSizeHintId:
370 case spv::ExecutionMode::LocalSizeId:
371 if (!spvOpcodeIsConstant(operand_inst->opcode())) {
372 return _.diag(SPV_ERROR_INVALID_ID, inst)
373 << "For OpExecutionModeId all Extra Operand ids must be "
374 "constant instructions.";
375 }
376 break;
377 case spv::ExecutionMode::FPFastMathDefault:
378 if (i == 2) {
379 if (!_.IsFloatScalarType(operand_id)) {
380 return _.diag(SPV_ERROR_INVALID_ID, inst)
381 << "The Target Type operand must be a floating-point "
382 "scalar type";
383 }
384 } else {
385 bool is_int32 = false;
386 bool is_const = false;
387 uint32_t value = 0;
388 std::tie(is_int32, is_const, value) =
389 _.EvalInt32IfConst(operand_id);
390 if (is_int32 && is_const) {
391 // Valid values include up to 0x00040000 (AllowTransform).
392 uint32_t invalid_mask = 0xfff80000;
393 if ((invalid_mask & value) != 0) {
394 return _.diag(SPV_ERROR_INVALID_ID, inst)
395 << "The Fast Math Default operand is an invalid bitmask "
396 "value";
397 }
398 if (value &
399 static_cast<uint32_t>(spv::FPFastMathModeMask::Fast)) {
400 return _.diag(SPV_ERROR_INVALID_ID, inst)
401 << "The Fast Math Default operand must not include Fast";
402 }
403 const auto reassoc_contract =
404 spv::FPFastMathModeMask::AllowContract |
405 spv::FPFastMathModeMask::AllowReassoc;
406 if ((value & static_cast<uint32_t>(
407 spv::FPFastMathModeMask::AllowTransform)) != 0 &&
408 ((value & static_cast<uint32_t>(reassoc_contract)) !=
409 static_cast<uint32_t>(reassoc_contract))) {
410 return _.diag(SPV_ERROR_INVALID_ID, inst)
411 << "The Fast Math Default operand must include "
412 "AllowContract and AllowReassoc when AllowTransform "
413 "is specified";
414 }
415 } else {
416 return _.diag(SPV_ERROR_INVALID_ID, inst)
417 << "The Fast Math Default operand must be a "
418 "non-specialization constant";
419 }
420 }
421 break;
422 default:
423 break;
424 }
425 }
426 } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
427 mode == spv::ExecutionMode::LocalSizeHintId ||
428 mode == spv::ExecutionMode::LocalSizeId ||
429 mode == spv::ExecutionMode::FPFastMathDefault) {
430 return _.diag(SPV_ERROR_INVALID_DATA, inst)
431 << "OpExecutionMode is only valid when the Mode operand is an "
432 "execution mode that takes no Extra Operands, or takes Extra "
433 "Operands that are not id operands.";
434 }
435
436 const auto* models = _.GetExecutionModels(entry_point_id);
437 switch (mode) {
438 case spv::ExecutionMode::Invocations:
439 case spv::ExecutionMode::InputPoints:
440 case spv::ExecutionMode::InputLines:
441 case spv::ExecutionMode::InputLinesAdjacency:
442 case spv::ExecutionMode::InputTrianglesAdjacency:
443 case spv::ExecutionMode::OutputLineStrip:
444 case spv::ExecutionMode::OutputTriangleStrip:
445 if (!std::all_of(models->begin(), models->end(),
446 [](const spv::ExecutionModel& model) {
447 return model == spv::ExecutionModel::Geometry;
448 })) {
449 return _.diag(SPV_ERROR_INVALID_DATA, inst)
450 << "Execution mode can only be used with the Geometry execution "
451 "model.";
452 }
453 break;
454 case spv::ExecutionMode::OutputPoints:
455 if (!std::all_of(
456 models->begin(), models->end(),
457 [&_](const spv::ExecutionModel& model) {
458 switch (model) {
459 case spv::ExecutionModel::Geometry:
460 return true;
461 case spv::ExecutionModel::MeshNV:
462 return _.HasCapability(spv::Capability::MeshShadingNV);
463 case spv::ExecutionModel::MeshEXT:
464 return _.HasCapability(spv::Capability::MeshShadingEXT);
465 default:
466 return false;
467 }
468 })) {
469 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
470 _.HasCapability(spv::Capability::MeshShadingEXT)) {
471 return _.diag(SPV_ERROR_INVALID_DATA, inst)
472 << "Execution mode can only be used with the Geometry "
473 "MeshNV or MeshEXT execution model.";
474 } else {
475 return _.diag(SPV_ERROR_INVALID_DATA, inst)
476 << "Execution mode can only be used with the Geometry "
477 "execution "
478 "model.";
479 }
480 }
481 break;
482 case spv::ExecutionMode::SpacingEqual:
483 case spv::ExecutionMode::SpacingFractionalEven:
484 case spv::ExecutionMode::SpacingFractionalOdd:
485 case spv::ExecutionMode::VertexOrderCw:
486 case spv::ExecutionMode::VertexOrderCcw:
487 case spv::ExecutionMode::PointMode:
488 case spv::ExecutionMode::Quads:
489 case spv::ExecutionMode::Isolines:
490 if (!std::all_of(
491 models->begin(), models->end(),
492 [](const spv::ExecutionModel& model) {
493 return (model == spv::ExecutionModel::TessellationControl) ||
494 (model == spv::ExecutionModel::TessellationEvaluation);
495 })) {
496 return _.diag(SPV_ERROR_INVALID_DATA, inst)
497 << "Execution mode can only be used with a tessellation "
498 "execution model.";
499 }
500 break;
501 case spv::ExecutionMode::Triangles:
502 if (!std::all_of(models->begin(), models->end(),
503 [](const spv::ExecutionModel& model) {
504 switch (model) {
505 case spv::ExecutionModel::Geometry:
506 case spv::ExecutionModel::TessellationControl:
507 case spv::ExecutionModel::TessellationEvaluation:
508 return true;
509 default:
510 return false;
511 }
512 })) {
513 return _.diag(SPV_ERROR_INVALID_DATA, inst)
514 << "Execution mode can only be used with a Geometry or "
515 "tessellation execution model.";
516 }
517 break;
518 case spv::ExecutionMode::OutputVertices:
519 if (!std::all_of(
520 models->begin(), models->end(),
521 [&_](const spv::ExecutionModel& model) {
522 switch (model) {
523 case spv::ExecutionModel::Geometry:
524 case spv::ExecutionModel::TessellationControl:
525 case spv::ExecutionModel::TessellationEvaluation:
526 return true;
527 case spv::ExecutionModel::MeshNV:
528 return _.HasCapability(spv::Capability::MeshShadingNV);
529 case spv::ExecutionModel::MeshEXT:
530 return _.HasCapability(spv::Capability::MeshShadingEXT);
531 default:
532 return false;
533 }
534 })) {
535 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
536 _.HasCapability(spv::Capability::MeshShadingEXT)) {
537 return _.diag(SPV_ERROR_INVALID_DATA, inst)
538 << "Execution mode can only be used with a Geometry, "
539 "tessellation, MeshNV or MeshEXT execution model.";
540 } else {
541 return _.diag(SPV_ERROR_INVALID_DATA, inst)
542 << "Execution mode can only be used with a Geometry or "
543 "tessellation execution model.";
544 }
545 }
546 break;
547 case spv::ExecutionMode::OutputLinesEXT:
548 case spv::ExecutionMode::OutputTrianglesEXT:
549 case spv::ExecutionMode::OutputPrimitivesEXT:
550 if (!std::all_of(models->begin(), models->end(),
551 [](const spv::ExecutionModel& model) {
552 return (model == spv::ExecutionModel::MeshEXT ||
553 model == spv::ExecutionModel::MeshNV);
554 })) {
555 return _.diag(SPV_ERROR_INVALID_DATA, inst)
556 << "Execution mode can only be used with the MeshEXT or MeshNV "
557 "execution "
558 "model.";
559 }
560 break;
561 case spv::ExecutionMode::QuadDerivativesKHR:
562 if (!std::all_of(models->begin(), models->end(),
563 [](const spv::ExecutionModel& model) {
564 return (model == spv::ExecutionModel::Fragment ||
565 model == spv::ExecutionModel::GLCompute);
566 })) {
567 return _.diag(SPV_ERROR_INVALID_DATA, inst)
568 << "Execution mode can only be used with the Fragment or "
569 "GLCompute execution model.";
570 }
571 break;
572 case spv::ExecutionMode::PixelCenterInteger:
573 case spv::ExecutionMode::OriginUpperLeft:
574 case spv::ExecutionMode::OriginLowerLeft:
575 case spv::ExecutionMode::EarlyFragmentTests:
576 case spv::ExecutionMode::DepthReplacing:
577 case spv::ExecutionMode::DepthGreater:
578 case spv::ExecutionMode::DepthLess:
579 case spv::ExecutionMode::DepthUnchanged:
580 case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
581 case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
582 case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
583 case spv::ExecutionMode::PixelInterlockOrderedEXT:
584 case spv::ExecutionMode::PixelInterlockUnorderedEXT:
585 case spv::ExecutionMode::SampleInterlockOrderedEXT:
586 case spv::ExecutionMode::SampleInterlockUnorderedEXT:
587 case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
588 case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
589 case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
590 case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
591 case spv::ExecutionMode::StencilRefGreaterFrontAMD:
592 case spv::ExecutionMode::StencilRefLessFrontAMD:
593 case spv::ExecutionMode::StencilRefUnchangedBackAMD:
594 case spv::ExecutionMode::StencilRefGreaterBackAMD:
595 case spv::ExecutionMode::StencilRefLessBackAMD:
596 case spv::ExecutionMode::RequireFullQuadsKHR:
597 if (!std::all_of(models->begin(), models->end(),
598 [](const spv::ExecutionModel& model) {
599 return model == spv::ExecutionModel::Fragment;
600 })) {
601 return _.diag(SPV_ERROR_INVALID_DATA, inst)
602 << "Execution mode can only be used with the Fragment execution "
603 "model.";
604 }
605 break;
606 case spv::ExecutionMode::LocalSizeHint:
607 case spv::ExecutionMode::VecTypeHint:
608 case spv::ExecutionMode::ContractionOff:
609 case spv::ExecutionMode::LocalSizeHintId:
610 if (!std::all_of(models->begin(), models->end(),
611 [](const spv::ExecutionModel& model) {
612 return model == spv::ExecutionModel::Kernel;
613 })) {
614 return _.diag(SPV_ERROR_INVALID_DATA, inst)
615 << "Execution mode can only be used with the Kernel execution "
616 "model.";
617 }
618 break;
619 case spv::ExecutionMode::LocalSize:
620 case spv::ExecutionMode::LocalSizeId:
621 if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed())
622 return _.diag(SPV_ERROR_INVALID_DATA, inst)
623 << "LocalSizeId mode is not allowed by the current environment.";
624
625 if (!std::all_of(
626 models->begin(), models->end(),
627 [&_](const spv::ExecutionModel& model) {
628 switch (model) {
629 case spv::ExecutionModel::Kernel:
630 case spv::ExecutionModel::GLCompute:
631 return true;
632 case spv::ExecutionModel::TaskNV:
633 case spv::ExecutionModel::MeshNV:
634 return _.HasCapability(spv::Capability::MeshShadingNV);
635 case spv::ExecutionModel::TaskEXT:
636 case spv::ExecutionModel::MeshEXT:
637 return _.HasCapability(spv::Capability::MeshShadingEXT);
638 default:
639 return false;
640 }
641 })) {
642 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
643 _.HasCapability(spv::Capability::MeshShadingEXT)) {
644 return _.diag(SPV_ERROR_INVALID_DATA, inst)
645 << "Execution mode can only be used with a Kernel, GLCompute, "
646 "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
647 } else {
648 return _.diag(SPV_ERROR_INVALID_DATA, inst)
649 << "Execution mode can only be used with a Kernel or "
650 "GLCompute "
651 "execution model.";
652 }
653 }
654 default:
655 break;
656 }
657
658 if (mode == spv::ExecutionMode::FPFastMathDefault) {
659 const auto* modes = _.GetExecutionModes(entry_point_id);
660 if (modes && modes->count(spv::ExecutionMode::ContractionOff)) {
661 return _.diag(SPV_ERROR_INVALID_DATA, inst)
662 << "FPFastMathDefault and ContractionOff execution modes cannot "
663 "be applied to the same entry point";
664 }
665 if (modes && modes->count(spv::ExecutionMode::SignedZeroInfNanPreserve)) {
666 return _.diag(SPV_ERROR_INVALID_DATA, inst)
667 << "FPFastMathDefault and SignedZeroInfNanPreserve execution "
668 "modes cannot be applied to the same entry point";
669 }
670 }
671
672 if (spvIsVulkanEnv(_.context()->target_env)) {
673 if (mode == spv::ExecutionMode::OriginLowerLeft) {
674 return _.diag(SPV_ERROR_INVALID_DATA, inst)
675 << _.VkErrorID(4653)
676 << "In the Vulkan environment, the OriginLowerLeft execution mode "
677 "must not be used.";
678 }
679 if (mode == spv::ExecutionMode::PixelCenterInteger) {
680 return _.diag(SPV_ERROR_INVALID_DATA, inst)
681 << _.VkErrorID(4654)
682 << "In the Vulkan environment, the PixelCenterInteger execution "
683 "mode must not be used.";
684 }
685 }
686
687 return SPV_SUCCESS;
688 }
689
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)690 spv_result_t ValidateMemoryModel(ValidationState_t& _,
691 const Instruction* inst) {
692 // Already produced an error if multiple memory model instructions are
693 // present.
694 if (_.memory_model() != spv::MemoryModel::VulkanKHR &&
695 _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
696 return _.diag(SPV_ERROR_INVALID_DATA, inst)
697 << "VulkanMemoryModelKHR capability must only be specified if "
698 "the VulkanKHR memory model is used.";
699 }
700
701 if (spvIsOpenCLEnv(_.context()->target_env)) {
702 if ((_.addressing_model() != spv::AddressingModel::Physical32) &&
703 (_.addressing_model() != spv::AddressingModel::Physical64)) {
704 return _.diag(SPV_ERROR_INVALID_DATA, inst)
705 << "Addressing model must be Physical32 or Physical64 "
706 << "in the OpenCL environment.";
707 }
708 if (_.memory_model() != spv::MemoryModel::OpenCL) {
709 return _.diag(SPV_ERROR_INVALID_DATA, inst)
710 << "Memory model must be OpenCL in the OpenCL environment.";
711 }
712 }
713
714 if (spvIsVulkanEnv(_.context()->target_env)) {
715 if ((_.addressing_model() != spv::AddressingModel::Logical) &&
716 (_.addressing_model() !=
717 spv::AddressingModel::PhysicalStorageBuffer64)) {
718 return _.diag(SPV_ERROR_INVALID_DATA, inst)
719 << _.VkErrorID(4635)
720 << "Addressing model must be Logical or PhysicalStorageBuffer64 "
721 << "in the Vulkan environment.";
722 }
723 }
724 return SPV_SUCCESS;
725 }
726
PerEntryExecutionMode(spv::ExecutionMode mode)727 bool PerEntryExecutionMode(spv::ExecutionMode mode) {
728 switch (mode) {
729 // These execution modes can be specified multiple times per entry point.
730 case spv::ExecutionMode::DenormPreserve:
731 case spv::ExecutionMode::DenormFlushToZero:
732 case spv::ExecutionMode::SignedZeroInfNanPreserve:
733 case spv::ExecutionMode::RoundingModeRTE:
734 case spv::ExecutionMode::RoundingModeRTZ:
735 case spv::ExecutionMode::FPFastMathDefault:
736 case spv::ExecutionMode::RoundingModeRTPINTEL:
737 case spv::ExecutionMode::RoundingModeRTNINTEL:
738 case spv::ExecutionMode::FloatingPointModeALTINTEL:
739 case spv::ExecutionMode::FloatingPointModeIEEEINTEL:
740 return false;
741 default:
742 return true;
743 }
744 }
745
746 } // namespace
747
ValidateFloatControls2(ValidationState_t & _)748 spv_result_t ValidateFloatControls2(ValidationState_t& _) {
749 std::unordered_set<uint32_t> fp_fast_math_default_entry_points;
750 for (auto entry_point : _.entry_points()) {
751 const auto* exec_modes = _.GetExecutionModes(entry_point);
752 if (exec_modes &&
753 exec_modes->count(spv::ExecutionMode::FPFastMathDefault)) {
754 fp_fast_math_default_entry_points.insert(entry_point);
755 }
756 }
757
758 std::vector<std::pair<const Instruction*, spv::Decoration>> worklist;
759 for (const auto& inst : _.ordered_instructions()) {
760 if (inst.opcode() != spv::Op::OpDecorate) {
761 continue;
762 }
763
764 const auto decoration = inst.GetOperandAs<spv::Decoration>(1);
765 const auto target_id = inst.GetOperandAs<uint32_t>(0);
766 const auto target = _.FindDef(target_id);
767 if (decoration == spv::Decoration::NoContraction) {
768 worklist.push_back(std::make_pair(target, decoration));
769 } else if (decoration == spv::Decoration::FPFastMathMode) {
770 auto mask = inst.GetOperandAs<spv::FPFastMathModeMask>(2);
771 if ((mask & spv::FPFastMathModeMask::Fast) !=
772 spv::FPFastMathModeMask::MaskNone) {
773 worklist.push_back(std::make_pair(target, decoration));
774 }
775 }
776 }
777
778 std::unordered_set<const Instruction*> visited;
779 while (!worklist.empty()) {
780 const auto inst = worklist.back().first;
781 const auto decoration = worklist.back().second;
782 worklist.pop_back();
783
784 if (!visited.insert(inst).second) {
785 continue;
786 }
787
788 const auto function = inst->function();
789 if (function) {
790 const auto& entry_points = _.FunctionEntryPoints(function->id());
791 for (auto entry_point : entry_points) {
792 if (fp_fast_math_default_entry_points.count(entry_point)) {
793 const std::string dec = decoration == spv::Decoration::NoContraction
794 ? "NoContraction"
795 : "FPFastMathMode Fast";
796 return _.diag(SPV_ERROR_INVALID_DATA, inst)
797 << dec
798 << " cannot be used by an entry point with the "
799 "FPFastMathDefault execution mode";
800 }
801 }
802 } else {
803 for (const auto& pair : inst->uses()) {
804 worklist.push_back(std::make_pair(pair.first, decoration));
805 }
806 }
807 }
808
809 return SPV_SUCCESS;
810 }
811
ModeSettingPass(ValidationState_t & _,const Instruction * inst)812 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
813 switch (inst->opcode()) {
814 case spv::Op::OpEntryPoint:
815 if (auto error = ValidateEntryPoint(_, inst)) return error;
816 break;
817 case spv::Op::OpExecutionMode:
818 case spv::Op::OpExecutionModeId:
819 if (auto error = ValidateExecutionMode(_, inst)) return error;
820 break;
821 case spv::Op::OpMemoryModel:
822 if (auto error = ValidateMemoryModel(_, inst)) return error;
823 break;
824 default:
825 break;
826 }
827 return SPV_SUCCESS;
828 }
829
ValidateDuplicateExecutionModes(ValidationState_t & _)830 spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _) {
831 using PerEntryKey = std::tuple<spv::ExecutionMode, uint32_t>;
832 using PerOperandKey = std::tuple<spv::ExecutionMode, uint32_t, uint32_t>;
833 std::set<PerEntryKey> seen_per_entry;
834 std::set<PerOperandKey> seen_per_operand;
835
836 const auto lookupMode = [&_](spv::ExecutionMode mode) -> std::string {
837 spv_operand_desc desc = nullptr;
838 if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE,
839 static_cast<uint32_t>(mode),
840 &desc) == SPV_SUCCESS) {
841 return std::string(desc->name);
842 }
843 return "Unknown";
844 };
845
846 for (const auto& inst : _.ordered_instructions()) {
847 if (inst.opcode() != spv::Op::OpExecutionMode &&
848 inst.opcode() != spv::Op::OpExecutionModeId) {
849 continue;
850 }
851
852 const auto entry = inst.GetOperandAs<uint32_t>(0);
853 const auto mode = inst.GetOperandAs<spv::ExecutionMode>(1);
854 if (PerEntryExecutionMode(mode)) {
855 if (!seen_per_entry.insert(std::make_tuple(mode, entry)).second) {
856 return _.diag(SPV_ERROR_INVALID_ID, &inst)
857 << lookupMode(mode)
858 << " execution mode must not be specified multiple times per "
859 "entry point";
860 }
861 } else {
862 // Execution modes allowed multiple times all take a single operand.
863 const auto operand = inst.GetOperandAs<uint32_t>(2);
864 if (!seen_per_operand.insert(std::make_tuple(mode, entry, operand))
865 .second) {
866 return _.diag(SPV_ERROR_INVALID_ID, &inst)
867 << lookupMode(mode)
868 << " execution mode must not be specified multiple times for "
869 "the same entry point and operands";
870 }
871 }
872 }
873
874 return SPV_SUCCESS;
875 }
876
877 } // namespace val
878 } // namespace spvtools
879