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 size_t operand_count = inst->operands().size();
344 for (size_t i = 2; i < operand_count; ++i) {
345 const auto operand_id = inst->GetOperandAs<uint32_t>(2);
346 const auto* operand_inst = _.FindDef(operand_id);
347 if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
348 mode == spv::ExecutionMode::LocalSizeHintId ||
349 mode == spv::ExecutionMode::LocalSizeId) {
350 if (!spvOpcodeIsConstant(operand_inst->opcode())) {
351 return _.diag(SPV_ERROR_INVALID_ID, inst)
352 << "For OpExecutionModeId all Extra Operand ids must be "
353 "constant "
354 "instructions.";
355 }
356 } else {
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 } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
364 mode == spv::ExecutionMode::LocalSizeHintId ||
365 mode == spv::ExecutionMode::LocalSizeId) {
366 return _.diag(SPV_ERROR_INVALID_DATA, inst)
367 << "OpExecutionMode is only valid when the Mode operand is an "
368 "execution mode that takes no Extra Operands, or takes Extra "
369 "Operands that are not id operands.";
370 }
371
372 const auto* models = _.GetExecutionModels(entry_point_id);
373 switch (mode) {
374 case spv::ExecutionMode::Invocations:
375 case spv::ExecutionMode::InputPoints:
376 case spv::ExecutionMode::InputLines:
377 case spv::ExecutionMode::InputLinesAdjacency:
378 case spv::ExecutionMode::InputTrianglesAdjacency:
379 case spv::ExecutionMode::OutputLineStrip:
380 case spv::ExecutionMode::OutputTriangleStrip:
381 if (!std::all_of(models->begin(), models->end(),
382 [](const spv::ExecutionModel& model) {
383 return model == spv::ExecutionModel::Geometry;
384 })) {
385 return _.diag(SPV_ERROR_INVALID_DATA, inst)
386 << "Execution mode can only be used with the Geometry execution "
387 "model.";
388 }
389 break;
390 case spv::ExecutionMode::OutputPoints:
391 if (!std::all_of(
392 models->begin(), models->end(),
393 [&_](const spv::ExecutionModel& model) {
394 switch (model) {
395 case spv::ExecutionModel::Geometry:
396 return true;
397 case spv::ExecutionModel::MeshNV:
398 return _.HasCapability(spv::Capability::MeshShadingNV);
399 case spv::ExecutionModel::MeshEXT:
400 return _.HasCapability(spv::Capability::MeshShadingEXT);
401 default:
402 return false;
403 }
404 })) {
405 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
406 _.HasCapability(spv::Capability::MeshShadingEXT)) {
407 return _.diag(SPV_ERROR_INVALID_DATA, inst)
408 << "Execution mode can only be used with the Geometry "
409 "MeshNV or MeshEXT execution model.";
410 } else {
411 return _.diag(SPV_ERROR_INVALID_DATA, inst)
412 << "Execution mode can only be used with the Geometry "
413 "execution "
414 "model.";
415 }
416 }
417 break;
418 case spv::ExecutionMode::SpacingEqual:
419 case spv::ExecutionMode::SpacingFractionalEven:
420 case spv::ExecutionMode::SpacingFractionalOdd:
421 case spv::ExecutionMode::VertexOrderCw:
422 case spv::ExecutionMode::VertexOrderCcw:
423 case spv::ExecutionMode::PointMode:
424 case spv::ExecutionMode::Quads:
425 case spv::ExecutionMode::Isolines:
426 if (!std::all_of(
427 models->begin(), models->end(),
428 [](const spv::ExecutionModel& model) {
429 return (model == spv::ExecutionModel::TessellationControl) ||
430 (model == spv::ExecutionModel::TessellationEvaluation);
431 })) {
432 return _.diag(SPV_ERROR_INVALID_DATA, inst)
433 << "Execution mode can only be used with a tessellation "
434 "execution model.";
435 }
436 break;
437 case spv::ExecutionMode::Triangles:
438 if (!std::all_of(models->begin(), models->end(),
439 [](const spv::ExecutionModel& model) {
440 switch (model) {
441 case spv::ExecutionModel::Geometry:
442 case spv::ExecutionModel::TessellationControl:
443 case spv::ExecutionModel::TessellationEvaluation:
444 return true;
445 default:
446 return false;
447 }
448 })) {
449 return _.diag(SPV_ERROR_INVALID_DATA, inst)
450 << "Execution mode can only be used with a Geometry or "
451 "tessellation execution model.";
452 }
453 break;
454 case spv::ExecutionMode::OutputVertices:
455 if (!std::all_of(
456 models->begin(), models->end(),
457 [&_](const spv::ExecutionModel& model) {
458 switch (model) {
459 case spv::ExecutionModel::Geometry:
460 case spv::ExecutionModel::TessellationControl:
461 case spv::ExecutionModel::TessellationEvaluation:
462 return true;
463 case spv::ExecutionModel::MeshNV:
464 return _.HasCapability(spv::Capability::MeshShadingNV);
465 case spv::ExecutionModel::MeshEXT:
466 return _.HasCapability(spv::Capability::MeshShadingEXT);
467 default:
468 return false;
469 }
470 })) {
471 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
472 _.HasCapability(spv::Capability::MeshShadingEXT)) {
473 return _.diag(SPV_ERROR_INVALID_DATA, inst)
474 << "Execution mode can only be used with a Geometry, "
475 "tessellation, MeshNV or MeshEXT execution model.";
476 } else {
477 return _.diag(SPV_ERROR_INVALID_DATA, inst)
478 << "Execution mode can only be used with a Geometry or "
479 "tessellation execution model.";
480 }
481 }
482 break;
483 case spv::ExecutionMode::OutputLinesEXT:
484 case spv::ExecutionMode::OutputTrianglesEXT:
485 case spv::ExecutionMode::OutputPrimitivesEXT:
486 if (!std::all_of(models->begin(), models->end(),
487 [](const spv::ExecutionModel& model) {
488 return (model == spv::ExecutionModel::MeshEXT ||
489 model == spv::ExecutionModel::MeshNV);
490 })) {
491 return _.diag(SPV_ERROR_INVALID_DATA, inst)
492 << "Execution mode can only be used with the MeshEXT or MeshNV "
493 "execution "
494 "model.";
495 }
496 break;
497 case spv::ExecutionMode::PixelCenterInteger:
498 case spv::ExecutionMode::OriginUpperLeft:
499 case spv::ExecutionMode::OriginLowerLeft:
500 case spv::ExecutionMode::EarlyFragmentTests:
501 case spv::ExecutionMode::DepthReplacing:
502 case spv::ExecutionMode::DepthGreater:
503 case spv::ExecutionMode::DepthLess:
504 case spv::ExecutionMode::DepthUnchanged:
505 case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
506 case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
507 case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
508 case spv::ExecutionMode::PixelInterlockOrderedEXT:
509 case spv::ExecutionMode::PixelInterlockUnorderedEXT:
510 case spv::ExecutionMode::SampleInterlockOrderedEXT:
511 case spv::ExecutionMode::SampleInterlockUnorderedEXT:
512 case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
513 case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
514 case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
515 case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
516 case spv::ExecutionMode::StencilRefGreaterFrontAMD:
517 case spv::ExecutionMode::StencilRefLessFrontAMD:
518 case spv::ExecutionMode::StencilRefUnchangedBackAMD:
519 case spv::ExecutionMode::StencilRefGreaterBackAMD:
520 case spv::ExecutionMode::StencilRefLessBackAMD:
521 if (!std::all_of(models->begin(), models->end(),
522 [](const spv::ExecutionModel& model) {
523 return model == spv::ExecutionModel::Fragment;
524 })) {
525 return _.diag(SPV_ERROR_INVALID_DATA, inst)
526 << "Execution mode can only be used with the Fragment execution "
527 "model.";
528 }
529 break;
530 case spv::ExecutionMode::LocalSizeHint:
531 case spv::ExecutionMode::VecTypeHint:
532 case spv::ExecutionMode::ContractionOff:
533 case spv::ExecutionMode::LocalSizeHintId:
534 if (!std::all_of(models->begin(), models->end(),
535 [](const spv::ExecutionModel& model) {
536 return model == spv::ExecutionModel::Kernel;
537 })) {
538 return _.diag(SPV_ERROR_INVALID_DATA, inst)
539 << "Execution mode can only be used with the Kernel execution "
540 "model.";
541 }
542 break;
543 case spv::ExecutionMode::LocalSize:
544 case spv::ExecutionMode::LocalSizeId:
545 if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed())
546 return _.diag(SPV_ERROR_INVALID_DATA, inst)
547 << "LocalSizeId mode is not allowed by the current environment.";
548
549 if (!std::all_of(
550 models->begin(), models->end(),
551 [&_](const spv::ExecutionModel& model) {
552 switch (model) {
553 case spv::ExecutionModel::Kernel:
554 case spv::ExecutionModel::GLCompute:
555 return true;
556 case spv::ExecutionModel::TaskNV:
557 case spv::ExecutionModel::MeshNV:
558 return _.HasCapability(spv::Capability::MeshShadingNV);
559 case spv::ExecutionModel::TaskEXT:
560 case spv::ExecutionModel::MeshEXT:
561 return _.HasCapability(spv::Capability::MeshShadingEXT);
562 default:
563 return false;
564 }
565 })) {
566 if (_.HasCapability(spv::Capability::MeshShadingNV) ||
567 _.HasCapability(spv::Capability::MeshShadingEXT)) {
568 return _.diag(SPV_ERROR_INVALID_DATA, inst)
569 << "Execution mode can only be used with a Kernel, GLCompute, "
570 "MeshNV, MeshEXT, TaskNV or TaskEXT execution model.";
571 } else {
572 return _.diag(SPV_ERROR_INVALID_DATA, inst)
573 << "Execution mode can only be used with a Kernel or "
574 "GLCompute "
575 "execution model.";
576 }
577 }
578 default:
579 break;
580 }
581
582 if (spvIsVulkanEnv(_.context()->target_env)) {
583 if (mode == spv::ExecutionMode::OriginLowerLeft) {
584 return _.diag(SPV_ERROR_INVALID_DATA, inst)
585 << _.VkErrorID(4653)
586 << "In the Vulkan environment, the OriginLowerLeft execution mode "
587 "must not be used.";
588 }
589 if (mode == spv::ExecutionMode::PixelCenterInteger) {
590 return _.diag(SPV_ERROR_INVALID_DATA, inst)
591 << _.VkErrorID(4654)
592 << "In the Vulkan environment, the PixelCenterInteger execution "
593 "mode must not be used.";
594 }
595 }
596
597 return SPV_SUCCESS;
598 }
599
ValidateMemoryModel(ValidationState_t & _,const Instruction * inst)600 spv_result_t ValidateMemoryModel(ValidationState_t& _,
601 const Instruction* inst) {
602 // Already produced an error if multiple memory model instructions are
603 // present.
604 if (_.memory_model() != spv::MemoryModel::VulkanKHR &&
605 _.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
606 return _.diag(SPV_ERROR_INVALID_DATA, inst)
607 << "VulkanMemoryModelKHR capability must only be specified if "
608 "the VulkanKHR memory model is used.";
609 }
610
611 if (spvIsOpenCLEnv(_.context()->target_env)) {
612 if ((_.addressing_model() != spv::AddressingModel::Physical32) &&
613 (_.addressing_model() != spv::AddressingModel::Physical64)) {
614 return _.diag(SPV_ERROR_INVALID_DATA, inst)
615 << "Addressing model must be Physical32 or Physical64 "
616 << "in the OpenCL environment.";
617 }
618 if (_.memory_model() != spv::MemoryModel::OpenCL) {
619 return _.diag(SPV_ERROR_INVALID_DATA, inst)
620 << "Memory model must be OpenCL in the OpenCL environment.";
621 }
622 }
623
624 if (spvIsVulkanEnv(_.context()->target_env)) {
625 if ((_.addressing_model() != spv::AddressingModel::Logical) &&
626 (_.addressing_model() !=
627 spv::AddressingModel::PhysicalStorageBuffer64)) {
628 return _.diag(SPV_ERROR_INVALID_DATA, inst)
629 << _.VkErrorID(4635)
630 << "Addressing model must be Logical or PhysicalStorageBuffer64 "
631 << "in the Vulkan environment.";
632 }
633 }
634 return SPV_SUCCESS;
635 }
636
637 } // namespace
638
ModeSettingPass(ValidationState_t & _,const Instruction * inst)639 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
640 switch (inst->opcode()) {
641 case spv::Op::OpEntryPoint:
642 if (auto error = ValidateEntryPoint(_, inst)) return error;
643 break;
644 case spv::Op::OpExecutionMode:
645 case spv::Op::OpExecutionModeId:
646 if (auto error = ValidateExecutionMode(_, inst)) return error;
647 break;
648 case spv::Op::OpMemoryModel:
649 if (auto error = ValidateMemoryModel(_, inst)) return error;
650 break;
651 default:
652 break;
653 }
654 return SPV_SUCCESS;
655 }
656
657 } // namespace val
658 } // namespace spvtools
659