1 // Copyright (c) 2018 Google LLC.
2 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
3 // reserved.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 // Validates correctness of built-in variables.
18
19 #include <array>
20 #include <functional>
21 #include <list>
22 #include <map>
23 #include <set>
24 #include <sstream>
25 #include <stack>
26 #include <string>
27 #include <vector>
28
29 #include "source/opcode.h"
30 #include "source/spirv_target_env.h"
31 #include "source/util/bitutils.h"
32 #include "source/val/instruction.h"
33 #include "source/val/validate.h"
34 #include "source/val/validation_state.h"
35
36 namespace spvtools {
37 namespace val {
38 namespace {
39
40 // Returns a short textual description of the id defined by the given
41 // instruction.
GetIdDesc(const Instruction & inst)42 std::string GetIdDesc(const Instruction& inst) {
43 std::ostringstream ss;
44 ss << "ID <" << inst.id() << "> (Op" << spvOpcodeString(inst.opcode()) << ")";
45 return ss.str();
46 }
47
48 // Gets underlying data type which is
49 // - member type if instruction is OpTypeStruct
50 // (member index is taken from decoration).
51 // - data type if id creates a pointer.
52 // - type of the constant if instruction is OpConst or OpSpecConst.
53 //
54 // Fails in any other case. The function is based on built-ins allowed by
55 // the Vulkan spec.
56 // TODO: If non-Vulkan validation rules are added then it might need
57 // to be refactored.
GetUnderlyingType(ValidationState_t & _,const Decoration & decoration,const Instruction & inst,uint32_t * underlying_type)58 spv_result_t GetUnderlyingType(ValidationState_t& _,
59 const Decoration& decoration,
60 const Instruction& inst,
61 uint32_t* underlying_type) {
62 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
63 if (inst.opcode() != spv::Op::OpTypeStruct) {
64 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
65 << GetIdDesc(inst)
66 << "Attempted to get underlying data type via member index for "
67 "non-struct type.";
68 }
69 *underlying_type = inst.word(decoration.struct_member_index() + 2);
70 return SPV_SUCCESS;
71 }
72
73 if (inst.opcode() == spv::Op::OpTypeStruct) {
74 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
75 << GetIdDesc(inst)
76 << " did not find an member index to get underlying data type for "
77 "struct type.";
78 }
79
80 if (spvOpcodeIsConstant(inst.opcode())) {
81 *underlying_type = inst.type_id();
82 return SPV_SUCCESS;
83 }
84
85 spv::StorageClass storage_class;
86 if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) {
87 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
88 << GetIdDesc(inst)
89 << " is decorated with BuiltIn. BuiltIn decoration should only be "
90 "applied to struct types, variables and constants.";
91 }
92 return SPV_SUCCESS;
93 }
94
95 // Returns Storage Class used by the instruction if applicable.
96 // Returns spv::StorageClass::Max if not.
GetStorageClass(const Instruction & inst)97 spv::StorageClass GetStorageClass(const Instruction& inst) {
98 switch (inst.opcode()) {
99 case spv::Op::OpTypePointer:
100 case spv::Op::OpTypeForwardPointer: {
101 return spv::StorageClass(inst.word(2));
102 }
103 case spv::Op::OpVariable: {
104 return spv::StorageClass(inst.word(3));
105 }
106 case spv::Op::OpGenericCastToPtrExplicit: {
107 return spv::StorageClass(inst.word(4));
108 }
109 default: { break; }
110 }
111 return spv::StorageClass::Max;
112 }
113
114 typedef enum VUIDError_ {
115 VUIDErrorExecutionModel = 0,
116 VUIDErrorStorageClass = 1,
117 VUIDErrorType = 2,
118 VUIDErrorMax,
119 } VUIDError;
120
121 const static uint32_t NumVUIDBuiltins = 39;
122
123 typedef struct {
124 spv::BuiltIn builtIn;
125 uint32_t vuid[VUIDErrorMax]; // execution mode, storage class, type VUIDs
126 } BuiltinVUIDMapping;
127
128 // Many built-ins have the same checks (Storage Class, Type, etc)
129 // This table provides a nice LUT for the VUIDs
130 std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{
131 // clang-format off
132 {spv::BuiltIn::SubgroupEqMask, {0, 4370, 4371}},
133 {spv::BuiltIn::SubgroupGeMask, {0, 4372, 4373}},
134 {spv::BuiltIn::SubgroupGtMask, {0, 4374, 4375}},
135 {spv::BuiltIn::SubgroupLeMask, {0, 4376, 4377}},
136 {spv::BuiltIn::SubgroupLtMask, {0, 4378, 4379}},
137 {spv::BuiltIn::SubgroupLocalInvocationId, {0, 4380, 4381}},
138 {spv::BuiltIn::SubgroupSize, {0, 4382, 4383}},
139 {spv::BuiltIn::GlobalInvocationId, {4236, 4237, 4238}},
140 {spv::BuiltIn::LocalInvocationId, {4281, 4282, 4283}},
141 {spv::BuiltIn::NumWorkgroups, {4296, 4297, 4298}},
142 {spv::BuiltIn::NumSubgroups, {4293, 4294, 4295}},
143 {spv::BuiltIn::SubgroupId, {4367, 4368, 4369}},
144 {spv::BuiltIn::WorkgroupId, {4422, 4423, 4424}},
145 {spv::BuiltIn::HitKindKHR, {4242, 4243, 4244}},
146 {spv::BuiltIn::HitTNV, {4245, 4246, 4247}},
147 {spv::BuiltIn::InstanceCustomIndexKHR, {4251, 4252, 4253}},
148 {spv::BuiltIn::InstanceId, {4254, 4255, 4256}},
149 {spv::BuiltIn::RayGeometryIndexKHR, {4345, 4346, 4347}},
150 {spv::BuiltIn::ObjectRayDirectionKHR, {4299, 4300, 4301}},
151 {spv::BuiltIn::ObjectRayOriginKHR, {4302, 4303, 4304}},
152 {spv::BuiltIn::ObjectToWorldKHR, {4305, 4306, 4307}},
153 {spv::BuiltIn::WorldToObjectKHR, {4434, 4435, 4436}},
154 {spv::BuiltIn::IncomingRayFlagsKHR, {4248, 4249, 4250}},
155 {spv::BuiltIn::RayTminKHR, {4351, 4352, 4353}},
156 {spv::BuiltIn::RayTmaxKHR, {4348, 4349, 4350}},
157 {spv::BuiltIn::WorldRayDirectionKHR, {4428, 4429, 4430}},
158 {spv::BuiltIn::WorldRayOriginKHR, {4431, 4432, 4433}},
159 {spv::BuiltIn::LaunchIdKHR, {4266, 4267, 4268}},
160 {spv::BuiltIn::LaunchSizeKHR, {4269, 4270, 4271}},
161 {spv::BuiltIn::FragInvocationCountEXT, {4217, 4218, 4219}},
162 {spv::BuiltIn::FragSizeEXT, {4220, 4221, 4222}},
163 {spv::BuiltIn::FragStencilRefEXT, {4223, 4224, 4225}},
164 {spv::BuiltIn::FullyCoveredEXT, {4232, 4233, 4234}},
165 {spv::BuiltIn::CullMaskKHR, {6735, 6736, 6737}},
166 {spv::BuiltIn::BaryCoordKHR, {4154, 4155, 4156}},
167 {spv::BuiltIn::BaryCoordNoPerspKHR, {4160, 4161, 4162}},
168 {spv::BuiltIn::PrimitivePointIndicesEXT, {7041, 7043, 7044}},
169 {spv::BuiltIn::PrimitiveLineIndicesEXT, {7047, 7049, 7050}},
170 {spv::BuiltIn::PrimitiveTriangleIndicesEXT, {7053, 7055, 7056}},
171 // clang-format on
172 }};
173
GetVUIDForBuiltin(spv::BuiltIn builtIn,VUIDError type)174 uint32_t GetVUIDForBuiltin(spv::BuiltIn builtIn, VUIDError type) {
175 uint32_t vuid = 0;
176 for (const auto& iter: builtinVUIDInfo) {
177 if (iter.builtIn == builtIn) {
178 assert(type < VUIDErrorMax);
179 vuid = iter.vuid[type];
180 break;
181 }
182 }
183 return vuid;
184 }
185
IsExecutionModelValidForRtBuiltIn(spv::BuiltIn builtin,spv::ExecutionModel stage)186 bool IsExecutionModelValidForRtBuiltIn(spv::BuiltIn builtin,
187 spv::ExecutionModel stage) {
188 switch (builtin) {
189 case spv::BuiltIn::HitKindKHR:
190 case spv::BuiltIn::HitTNV:
191 if (stage == spv::ExecutionModel::AnyHitKHR ||
192 stage == spv::ExecutionModel::ClosestHitKHR) {
193 return true;
194 }
195 break;
196 case spv::BuiltIn::InstanceCustomIndexKHR:
197 case spv::BuiltIn::InstanceId:
198 case spv::BuiltIn::RayGeometryIndexKHR:
199 case spv::BuiltIn::ObjectRayDirectionKHR:
200 case spv::BuiltIn::ObjectRayOriginKHR:
201 case spv::BuiltIn::ObjectToWorldKHR:
202 case spv::BuiltIn::WorldToObjectKHR:
203 switch (stage) {
204 case spv::ExecutionModel::IntersectionKHR:
205 case spv::ExecutionModel::AnyHitKHR:
206 case spv::ExecutionModel::ClosestHitKHR:
207 return true;
208 default:
209 return false;
210 }
211 break;
212 case spv::BuiltIn::IncomingRayFlagsKHR:
213 case spv::BuiltIn::RayTminKHR:
214 case spv::BuiltIn::RayTmaxKHR:
215 case spv::BuiltIn::WorldRayDirectionKHR:
216 case spv::BuiltIn::WorldRayOriginKHR:
217 case spv::BuiltIn::CullMaskKHR:
218 switch (stage) {
219 case spv::ExecutionModel::IntersectionKHR:
220 case spv::ExecutionModel::AnyHitKHR:
221 case spv::ExecutionModel::ClosestHitKHR:
222 case spv::ExecutionModel::MissKHR:
223 return true;
224 default:
225 return false;
226 }
227 break;
228 case spv::BuiltIn::LaunchIdKHR:
229 case spv::BuiltIn::LaunchSizeKHR:
230 switch (stage) {
231 case spv::ExecutionModel::RayGenerationKHR:
232 case spv::ExecutionModel::IntersectionKHR:
233 case spv::ExecutionModel::AnyHitKHR:
234 case spv::ExecutionModel::ClosestHitKHR:
235 case spv::ExecutionModel::MissKHR:
236 case spv::ExecutionModel::CallableKHR:
237 return true;
238 default:
239 return false;
240 }
241 break;
242 default:
243 break;
244 }
245 return false;
246 }
247
248 // Helper class managing validation of built-ins.
249 // TODO: Generic functionality of this class can be moved into
250 // ValidationState_t to be made available to other users.
251 class BuiltInsValidator {
252 public:
BuiltInsValidator(ValidationState_t & vstate)253 BuiltInsValidator(ValidationState_t& vstate) : _(vstate) {}
254
255 // Run validation.
256 spv_result_t Run();
257
258 private:
259 // Goes through all decorations in the module, if decoration is BuiltIn
260 // calls ValidateSingleBuiltInAtDefinition().
261 spv_result_t ValidateBuiltInsAtDefinition();
262
263 // Validates the instruction defining an id with built-in decoration.
264 // Can be called multiple times for the same id, if multiple built-ins are
265 // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed.
266 spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration,
267 const Instruction& inst);
268
269 // The following section contains functions which are called when id defined
270 // by |inst| is decorated with BuiltIn |decoration|.
271 // Most functions are specific to a single built-in and have naming scheme:
272 // ValidateXYZAtDefinition. Some functions are common to multiple kinds of
273 // BuiltIn.
274 spv_result_t ValidateClipOrCullDistanceAtDefinition(
275 const Decoration& decoration, const Instruction& inst);
276 spv_result_t ValidateFragCoordAtDefinition(const Decoration& decoration,
277 const Instruction& inst);
278 spv_result_t ValidateFragDepthAtDefinition(const Decoration& decoration,
279 const Instruction& inst);
280 spv_result_t ValidateFrontFacingAtDefinition(const Decoration& decoration,
281 const Instruction& inst);
282 spv_result_t ValidateHelperInvocationAtDefinition(
283 const Decoration& decoration, const Instruction& inst);
284 spv_result_t ValidateInvocationIdAtDefinition(const Decoration& decoration,
285 const Instruction& inst);
286 spv_result_t ValidateInstanceIndexAtDefinition(const Decoration& decoration,
287 const Instruction& inst);
288 spv_result_t ValidateLayerOrViewportIndexAtDefinition(
289 const Decoration& decoration, const Instruction& inst);
290 spv_result_t ValidatePatchVerticesAtDefinition(const Decoration& decoration,
291 const Instruction& inst);
292 spv_result_t ValidatePointCoordAtDefinition(const Decoration& decoration,
293 const Instruction& inst);
294 spv_result_t ValidatePointSizeAtDefinition(const Decoration& decoration,
295 const Instruction& inst);
296 spv_result_t ValidatePositionAtDefinition(const Decoration& decoration,
297 const Instruction& inst);
298 spv_result_t ValidatePrimitiveIdAtDefinition(const Decoration& decoration,
299 const Instruction& inst);
300 spv_result_t ValidateSampleIdAtDefinition(const Decoration& decoration,
301 const Instruction& inst);
302 spv_result_t ValidateSampleMaskAtDefinition(const Decoration& decoration,
303 const Instruction& inst);
304 spv_result_t ValidateSamplePositionAtDefinition(const Decoration& decoration,
305 const Instruction& inst);
306 spv_result_t ValidateTessCoordAtDefinition(const Decoration& decoration,
307 const Instruction& inst);
308 spv_result_t ValidateTessLevelOuterAtDefinition(const Decoration& decoration,
309 const Instruction& inst);
310 spv_result_t ValidateTessLevelInnerAtDefinition(const Decoration& decoration,
311 const Instruction& inst);
312 spv_result_t ValidateVertexIndexAtDefinition(const Decoration& decoration,
313 const Instruction& inst);
314 spv_result_t ValidateVertexIdAtDefinition(const Decoration& decoration,
315 const Instruction& inst);
316 spv_result_t ValidateLocalInvocationIndexAtDefinition(
317 const Decoration& decoration, const Instruction& inst);
318 spv_result_t ValidateWorkgroupSizeAtDefinition(const Decoration& decoration,
319 const Instruction& inst);
320 spv_result_t ValidateBaseInstanceOrVertexAtDefinition(
321 const Decoration& decoration, const Instruction& inst);
322 spv_result_t ValidateDrawIndexAtDefinition(const Decoration& decoration,
323 const Instruction& inst);
324 spv_result_t ValidateViewIndexAtDefinition(const Decoration& decoration,
325 const Instruction& inst);
326 spv_result_t ValidateDeviceIndexAtDefinition(const Decoration& decoration,
327 const Instruction& inst);
328 spv_result_t ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
329 const Instruction& inst);
330 spv_result_t ValidateFragSizeAtDefinition(const Decoration& decoration,
331 const Instruction& inst);
332 spv_result_t ValidateFragStencilRefAtDefinition(const Decoration& decoration,
333 const Instruction& inst);
334 spv_result_t ValidateFullyCoveredAtDefinition(const Decoration& decoration,
335 const Instruction& inst);
336 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
337 spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition(
338 const Decoration& decoration, const Instruction& inst);
339 spv_result_t ValidateNVSMOrARMCoreBuiltinsAtDefinition(const Decoration& decoration,
340 const Instruction& inst);
341 // Used for BaryCoord, BaryCoordNoPersp.
342 spv_result_t ValidateFragmentShaderF32Vec3InputAtDefinition(
343 const Decoration& decoration, const Instruction& inst);
344 // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask,
345 // SubgroupLeMask.
346 spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration,
347 const Instruction& inst);
348 // Used for SubgroupLocalInvocationId, SubgroupSize.
349 spv_result_t ValidateI32InputAtDefinition(const Decoration& decoration,
350 const Instruction& inst);
351 // Used for SubgroupId, NumSubgroups.
352 spv_result_t ValidateComputeI32InputAtDefinition(const Decoration& decoration,
353 const Instruction& inst);
354
355 spv_result_t ValidatePrimitiveShadingRateAtDefinition(
356 const Decoration& decoration, const Instruction& inst);
357
358 spv_result_t ValidateShadingRateAtDefinition(const Decoration& decoration,
359 const Instruction& inst);
360
361 spv_result_t ValidateRayTracingBuiltinsAtDefinition(
362 const Decoration& decoration, const Instruction& inst);
363
364 spv_result_t ValidateMeshShadingEXTBuiltinsAtDefinition(
365 const Decoration& decoration, const Instruction& inst);
366
367 // The following section contains functions which are called when id defined
368 // by |referenced_inst| is
369 // 1. referenced by |referenced_from_inst|
370 // 2. dependent on |built_in_inst| which is decorated with BuiltIn
371 // |decoration|. Most functions are specific to a single built-in and have
372 // naming scheme: ValidateXYZAtReference. Some functions are common to
373 // multiple kinds of BuiltIn.
374 spv_result_t ValidateFragCoordAtReference(
375 const Decoration& decoration, const Instruction& built_in_inst,
376 const Instruction& referenced_inst,
377 const Instruction& referenced_from_inst);
378
379 spv_result_t ValidateFragDepthAtReference(
380 const Decoration& decoration, const Instruction& built_in_inst,
381 const Instruction& referenced_inst,
382 const Instruction& referenced_from_inst);
383
384 spv_result_t ValidateFrontFacingAtReference(
385 const Decoration& decoration, const Instruction& built_in_inst,
386 const Instruction& referenced_inst,
387 const Instruction& referenced_from_inst);
388
389 spv_result_t ValidateHelperInvocationAtReference(
390 const Decoration& decoration, const Instruction& built_in_inst,
391 const Instruction& referenced_inst,
392 const Instruction& referenced_from_inst);
393
394 spv_result_t ValidateInvocationIdAtReference(
395 const Decoration& decoration, const Instruction& built_in_inst,
396 const Instruction& referenced_inst,
397 const Instruction& referenced_from_inst);
398
399 spv_result_t ValidateInstanceIndexAtReference(
400 const Decoration& decoration, const Instruction& built_in_inst,
401 const Instruction& referenced_inst,
402 const Instruction& referenced_from_inst);
403
404 spv_result_t ValidatePatchVerticesAtReference(
405 const Decoration& decoration, const Instruction& built_in_inst,
406 const Instruction& referenced_inst,
407 const Instruction& referenced_from_inst);
408
409 spv_result_t ValidatePointCoordAtReference(
410 const Decoration& decoration, const Instruction& built_in_inst,
411 const Instruction& referenced_inst,
412 const Instruction& referenced_from_inst);
413
414 spv_result_t ValidatePointSizeAtReference(
415 const Decoration& decoration, const Instruction& built_in_inst,
416 const Instruction& referenced_inst,
417 const Instruction& referenced_from_inst);
418
419 spv_result_t ValidatePositionAtReference(
420 const Decoration& decoration, const Instruction& built_in_inst,
421 const Instruction& referenced_inst,
422 const Instruction& referenced_from_inst);
423
424 spv_result_t ValidatePrimitiveIdAtReference(
425 const Decoration& decoration, const Instruction& built_in_inst,
426 const Instruction& referenced_inst,
427 const Instruction& referenced_from_inst);
428
429 spv_result_t ValidateSampleIdAtReference(
430 const Decoration& decoration, const Instruction& built_in_inst,
431 const Instruction& referenced_inst,
432 const Instruction& referenced_from_inst);
433
434 spv_result_t ValidateSampleMaskAtReference(
435 const Decoration& decoration, const Instruction& built_in_inst,
436 const Instruction& referenced_inst,
437 const Instruction& referenced_from_inst);
438
439 spv_result_t ValidateSamplePositionAtReference(
440 const Decoration& decoration, const Instruction& built_in_inst,
441 const Instruction& referenced_inst,
442 const Instruction& referenced_from_inst);
443
444 spv_result_t ValidateTessCoordAtReference(
445 const Decoration& decoration, const Instruction& built_in_inst,
446 const Instruction& referenced_inst,
447 const Instruction& referenced_from_inst);
448
449 spv_result_t ValidateTessLevelAtReference(
450 const Decoration& decoration, const Instruction& built_in_inst,
451 const Instruction& referenced_inst,
452 const Instruction& referenced_from_inst);
453
454 spv_result_t ValidateLocalInvocationIndexAtReference(
455 const Decoration& decoration, const Instruction& built_in_inst,
456 const Instruction& referenced_inst,
457 const Instruction& referenced_from_inst);
458
459 spv_result_t ValidateVertexIndexAtReference(
460 const Decoration& decoration, const Instruction& built_in_inst,
461 const Instruction& referenced_inst,
462 const Instruction& referenced_from_inst);
463
464 spv_result_t ValidateLayerOrViewportIndexAtReference(
465 const Decoration& decoration, const Instruction& built_in_inst,
466 const Instruction& referenced_inst,
467 const Instruction& referenced_from_inst);
468
469 spv_result_t ValidateWorkgroupSizeAtReference(
470 const Decoration& decoration, const Instruction& built_in_inst,
471 const Instruction& referenced_inst,
472 const Instruction& referenced_from_inst);
473
474 spv_result_t ValidateClipOrCullDistanceAtReference(
475 const Decoration& decoration, const Instruction& built_in_inst,
476 const Instruction& referenced_inst,
477 const Instruction& referenced_from_inst);
478
479 spv_result_t ValidateBaseInstanceOrVertexAtReference(
480 const Decoration& decoration, const Instruction& built_in_inst,
481 const Instruction& referenced_inst,
482 const Instruction& referenced_from_inst);
483
484 spv_result_t ValidateDrawIndexAtReference(
485 const Decoration& decoration, const Instruction& built_in_inst,
486 const Instruction& referenced_inst,
487 const Instruction& referenced_from_inst);
488
489 spv_result_t ValidateViewIndexAtReference(
490 const Decoration& decoration, const Instruction& built_in_inst,
491 const Instruction& referenced_inst,
492 const Instruction& referenced_from_inst);
493
494 spv_result_t ValidateDeviceIndexAtReference(
495 const Decoration& decoration, const Instruction& built_in_inst,
496 const Instruction& referenced_inst,
497 const Instruction& referenced_from_inst);
498
499 spv_result_t ValidateFragInvocationCountAtReference(
500 const Decoration& decoration, const Instruction& built_in_inst,
501 const Instruction& referenced_inst,
502 const Instruction& referenced_from_inst);
503
504 spv_result_t ValidateFragSizeAtReference(
505 const Decoration& decoration, const Instruction& built_in_inst,
506 const Instruction& referenced_inst,
507 const Instruction& referenced_from_inst);
508
509 spv_result_t ValidateFragStencilRefAtReference(
510 const Decoration& decoration, const Instruction& built_in_inst,
511 const Instruction& referenced_inst,
512 const Instruction& referenced_from_inst);
513
514 spv_result_t ValidateFullyCoveredAtReference(
515 const Decoration& decoration, const Instruction& built_in_inst,
516 const Instruction& referenced_inst,
517 const Instruction& referenced_from_inst);
518
519 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
520 spv_result_t ValidateComputeShaderI32Vec3InputAtReference(
521 const Decoration& decoration, const Instruction& built_in_inst,
522 const Instruction& referenced_inst,
523 const Instruction& referenced_from_inst);
524
525 // Used for BaryCoord, BaryCoordNoPersp.
526 spv_result_t ValidateFragmentShaderF32Vec3InputAtReference(
527 const Decoration& decoration, const Instruction& built_in_inst,
528 const Instruction& referenced_inst,
529 const Instruction& referenced_from_inst);
530
531 // Used for SubgroupId and NumSubgroups.
532 spv_result_t ValidateComputeI32InputAtReference(
533 const Decoration& decoration, const Instruction& built_in_inst,
534 const Instruction& referenced_inst,
535 const Instruction& referenced_from_inst);
536
537 spv_result_t ValidateNVSMOrARMCoreBuiltinsAtReference(
538 const Decoration& decoration, const Instruction& built_in_inst,
539 const Instruction& referenced_inst,
540 const Instruction& referenced_from_inst);
541
542 spv_result_t ValidatePrimitiveShadingRateAtReference(
543 const Decoration& decoration, const Instruction& built_in_inst,
544 const Instruction& referenced_inst,
545 const Instruction& referenced_from_inst);
546
547 spv_result_t ValidateShadingRateAtReference(
548 const Decoration& decoration, const Instruction& built_in_inst,
549 const Instruction& referenced_inst,
550 const Instruction& referenced_from_inst);
551
552 spv_result_t ValidateRayTracingBuiltinsAtReference(
553 const Decoration& decoration, const Instruction& built_in_inst,
554 const Instruction& referenced_inst,
555 const Instruction& referenced_from_inst);
556
557 spv_result_t ValidateMeshShadingEXTBuiltinsAtReference(
558 const Decoration& decoration, const Instruction& built_in_inst,
559 const Instruction& referenced_inst,
560 const Instruction& referenced_from_inst);
561
562 // Validates that |built_in_inst| is not (even indirectly) referenced from
563 // within a function which can be called with |execution_model|.
564 //
565 // |vuid| - Vulkan ID for the error, or a negative value if none.
566 // |comment| - text explaining why the restriction was imposed.
567 // |decoration| - BuiltIn decoration which causes the restriction.
568 // |referenced_inst| - instruction which is dependent on |built_in_inst| and
569 // defines the id which was referenced.
570 // |referenced_from_inst| - instruction which references id defined by
571 // |referenced_inst| from within a function.
572 spv_result_t ValidateNotCalledWithExecutionModel(
573 int vuid, const char* comment, spv::ExecutionModel execution_model,
574 const Decoration& decoration, const Instruction& built_in_inst,
575 const Instruction& referenced_inst,
576 const Instruction& referenced_from_inst);
577
578 // The following section contains functions which check that the decorated
579 // variable has the type specified in the function name. |diag| would be
580 // called with a corresponding error message, if validation is not successful.
581 spv_result_t ValidateBool(
582 const Decoration& decoration, const Instruction& inst,
583 const std::function<spv_result_t(const std::string& message)>& diag);
584 spv_result_t ValidateI(
585 const Decoration& decoration, const Instruction& inst,
586 const std::function<spv_result_t(const std::string& message)>& diag);
587 spv_result_t ValidateI32(
588 const Decoration& decoration, const Instruction& inst,
589 const std::function<spv_result_t(const std::string& message)>& diag);
590 spv_result_t ValidateI32Vec(
591 const Decoration& decoration, const Instruction& inst,
592 uint32_t num_components,
593 const std::function<spv_result_t(const std::string& message)>& diag);
594 spv_result_t ValidateI32Arr(
595 const Decoration& decoration, const Instruction& inst,
596 const std::function<spv_result_t(const std::string& message)>& diag);
597 spv_result_t ValidateArrayedI32Vec(
598 const Decoration& decoration, const Instruction& inst,
599 uint32_t num_components,
600 const std::function<spv_result_t(const std::string& message)>& diag);
601 spv_result_t ValidateOptionalArrayedI32(
602 const Decoration& decoration, const Instruction& inst,
603 const std::function<spv_result_t(const std::string& message)>& diag);
604 spv_result_t ValidateI32Helper(
605 const Decoration& decoration, const Instruction& inst,
606 const std::function<spv_result_t(const std::string& message)>& diag,
607 uint32_t underlying_type);
608 spv_result_t ValidateF32(
609 const Decoration& decoration, const Instruction& inst,
610 const std::function<spv_result_t(const std::string& message)>& diag);
611 spv_result_t ValidateOptionalArrayedF32(
612 const Decoration& decoration, const Instruction& inst,
613 const std::function<spv_result_t(const std::string& message)>& diag);
614 spv_result_t ValidateF32Helper(
615 const Decoration& decoration, const Instruction& inst,
616 const std::function<spv_result_t(const std::string& message)>& diag,
617 uint32_t underlying_type);
618 spv_result_t ValidateF32Vec(
619 const Decoration& decoration, const Instruction& inst,
620 uint32_t num_components,
621 const std::function<spv_result_t(const std::string& message)>& diag);
622 spv_result_t ValidateOptionalArrayedF32Vec(
623 const Decoration& decoration, const Instruction& inst,
624 uint32_t num_components,
625 const std::function<spv_result_t(const std::string& message)>& diag);
626 spv_result_t ValidateF32VecHelper(
627 const Decoration& decoration, const Instruction& inst,
628 uint32_t num_components,
629 const std::function<spv_result_t(const std::string& message)>& diag,
630 uint32_t underlying_type);
631 // If |num_components| is zero, the number of components is not checked.
632 spv_result_t ValidateF32Arr(
633 const Decoration& decoration, const Instruction& inst,
634 uint32_t num_components,
635 const std::function<spv_result_t(const std::string& message)>& diag);
636 spv_result_t ValidateOptionalArrayedF32Arr(
637 const Decoration& decoration, const Instruction& inst,
638 uint32_t num_components,
639 const std::function<spv_result_t(const std::string& message)>& diag);
640 spv_result_t ValidateF32ArrHelper(
641 const Decoration& decoration, const Instruction& inst,
642 uint32_t num_components,
643 const std::function<spv_result_t(const std::string& message)>& diag,
644 uint32_t underlying_type);
645 spv_result_t ValidateF32Mat(
646 const Decoration& decoration, const Instruction& inst,
647 uint32_t req_num_rows, uint32_t req_num_columns,
648 const std::function<spv_result_t(const std::string& message)>& diag);
649
650 // Generates strings like "Member #0 of struct ID <2>".
651 std::string GetDefinitionDesc(const Decoration& decoration,
652 const Instruction& inst) const;
653
654 // Generates strings like "ID <51> (OpTypePointer) is referencing ID <2>
655 // (OpTypeStruct) which is decorated with BuiltIn Position".
656 std::string GetReferenceDesc(
657 const Decoration& decoration, const Instruction& built_in_inst,
658 const Instruction& referenced_inst,
659 const Instruction& referenced_from_inst,
660 spv::ExecutionModel execution_model = spv::ExecutionModel::Max) const;
661
662 // Generates strings like "ID <51> (OpTypePointer) uses storage class
663 // UniformConstant".
664 std::string GetStorageClassDesc(const Instruction& inst) const;
665
666 // Updates inner working of the class. Is called sequentially for every
667 // instruction.
668 void Update(const Instruction& inst);
669
670 ValidationState_t& _;
671
672 // Mapping id -> list of rules which validate instruction referencing the
673 // id. Rules can create new rules and add them to this container.
674 // Using std::map, and not std::unordered_map to avoid iterator invalidation
675 // during rehashing.
676 std::map<uint32_t, std::list<std::function<spv_result_t(const Instruction&)>>>
677 id_to_at_reference_checks_;
678
679 // Id of the function we are currently inside. 0 if not inside a function.
680 uint32_t function_id_ = 0;
681
682 // Entry points which can (indirectly) call the current function.
683 // The pointer either points to a vector inside to function_to_entry_points_
684 // or to no_entry_points_. The pointer is guaranteed to never be null.
685 const std::vector<uint32_t> no_entry_points;
686 const std::vector<uint32_t>* entry_points_ = &no_entry_points;
687
688 // Execution models with which the current function can be called.
689 std::set<spv::ExecutionModel> execution_models_;
690 };
691
Update(const Instruction & inst)692 void BuiltInsValidator::Update(const Instruction& inst) {
693 const spv::Op opcode = inst.opcode();
694 if (opcode == spv::Op::OpFunction) {
695 // Entering a function.
696 assert(function_id_ == 0);
697 function_id_ = inst.id();
698 execution_models_.clear();
699 entry_points_ = &_.FunctionEntryPoints(function_id_);
700 // Collect execution models from all entry points from which the current
701 // function can be called.
702 for (const uint32_t entry_point : *entry_points_) {
703 if (const auto* models = _.GetExecutionModels(entry_point)) {
704 execution_models_.insert(models->begin(), models->end());
705 }
706 }
707 }
708
709 if (opcode == spv::Op::OpFunctionEnd) {
710 // Exiting a function.
711 assert(function_id_ != 0);
712 function_id_ = 0;
713 entry_points_ = &no_entry_points;
714 execution_models_.clear();
715 }
716 }
717
GetDefinitionDesc(const Decoration & decoration,const Instruction & inst) const718 std::string BuiltInsValidator::GetDefinitionDesc(
719 const Decoration& decoration, const Instruction& inst) const {
720 std::ostringstream ss;
721 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
722 assert(inst.opcode() == spv::Op::OpTypeStruct);
723 ss << "Member #" << decoration.struct_member_index();
724 ss << " of struct ID <" << inst.id() << ">";
725 } else {
726 ss << GetIdDesc(inst);
727 }
728 return ss.str();
729 }
730
GetReferenceDesc(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst,spv::ExecutionModel execution_model) const731 std::string BuiltInsValidator::GetReferenceDesc(
732 const Decoration& decoration, const Instruction& built_in_inst,
733 const Instruction& referenced_inst, const Instruction& referenced_from_inst,
734 spv::ExecutionModel execution_model) const {
735 std::ostringstream ss;
736 ss << GetIdDesc(referenced_from_inst) << " is referencing "
737 << GetIdDesc(referenced_inst);
738 if (built_in_inst.id() != referenced_inst.id()) {
739 ss << " which is dependent on " << GetIdDesc(built_in_inst);
740 }
741
742 ss << " which is decorated with BuiltIn ";
743 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
744 decoration.params()[0]);
745 if (function_id_) {
746 ss << " in function <" << function_id_ << ">";
747 if (execution_model != spv::ExecutionModel::Max) {
748 ss << " called with execution model ";
749 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL,
750 uint32_t(execution_model));
751 }
752 }
753 ss << ".";
754 return ss.str();
755 }
756
GetStorageClassDesc(const Instruction & inst) const757 std::string BuiltInsValidator::GetStorageClassDesc(
758 const Instruction& inst) const {
759 std::ostringstream ss;
760 ss << GetIdDesc(inst) << " uses storage class ";
761 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS,
762 uint32_t(GetStorageClass(inst)));
763 ss << ".";
764 return ss.str();
765 }
766
ValidateBool(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)767 spv_result_t BuiltInsValidator::ValidateBool(
768 const Decoration& decoration, const Instruction& inst,
769 const std::function<spv_result_t(const std::string& message)>& diag) {
770 uint32_t underlying_type = 0;
771 if (spv_result_t error =
772 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
773 return error;
774 }
775
776 if (!_.IsBoolScalarType(underlying_type)) {
777 return diag(GetDefinitionDesc(decoration, inst) + " is not a bool scalar.");
778 }
779
780 return SPV_SUCCESS;
781 }
782
ValidateI(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)783 spv_result_t BuiltInsValidator::ValidateI(
784 const Decoration& decoration, const Instruction& inst,
785 const std::function<spv_result_t(const std::string& message)>& diag) {
786 uint32_t underlying_type = 0;
787 if (spv_result_t error =
788 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
789 return error;
790 }
791
792 if (!_.IsIntScalarType(underlying_type)) {
793 return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar.");
794 }
795
796 return SPV_SUCCESS;
797 }
798
ValidateI32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)799 spv_result_t BuiltInsValidator::ValidateI32(
800 const Decoration& decoration, const Instruction& inst,
801 const std::function<spv_result_t(const std::string& message)>& diag) {
802 uint32_t underlying_type = 0;
803 if (spv_result_t error =
804 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
805 return error;
806 }
807
808 return ValidateI32Helper(decoration, inst, diag, underlying_type);
809 }
810
ValidateOptionalArrayedI32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)811 spv_result_t BuiltInsValidator::ValidateOptionalArrayedI32(
812 const Decoration& decoration, const Instruction& inst,
813 const std::function<spv_result_t(const std::string& message)>& diag) {
814 uint32_t underlying_type = 0;
815 if (spv_result_t error =
816 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
817 return error;
818 }
819
820 // Strip the array, if present.
821 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
822 underlying_type = _.FindDef(underlying_type)->word(2u);
823 }
824
825 return ValidateI32Helper(decoration, inst, diag, underlying_type);
826 }
827
ValidateI32Helper(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)828 spv_result_t BuiltInsValidator::ValidateI32Helper(
829 const Decoration& decoration, const Instruction& inst,
830 const std::function<spv_result_t(const std::string& message)>& diag,
831 uint32_t underlying_type) {
832 if (!_.IsIntScalarType(underlying_type)) {
833 return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar.");
834 }
835
836 const uint32_t bit_width = _.GetBitWidth(underlying_type);
837 if (bit_width != 32) {
838 std::ostringstream ss;
839 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
840 << ".";
841 return diag(ss.str());
842 }
843
844 return SPV_SUCCESS;
845 }
846
ValidateOptionalArrayedF32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)847 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32(
848 const Decoration& decoration, const Instruction& inst,
849 const std::function<spv_result_t(const std::string& message)>& diag) {
850 uint32_t underlying_type = 0;
851 if (spv_result_t error =
852 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
853 return error;
854 }
855
856 // Strip the array, if present.
857 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
858 underlying_type = _.FindDef(underlying_type)->word(2u);
859 }
860
861 return ValidateF32Helper(decoration, inst, diag, underlying_type);
862 }
863
ValidateF32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)864 spv_result_t BuiltInsValidator::ValidateF32(
865 const Decoration& decoration, const Instruction& inst,
866 const std::function<spv_result_t(const std::string& message)>& diag) {
867 uint32_t underlying_type = 0;
868 if (spv_result_t error =
869 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
870 return error;
871 }
872
873 return ValidateF32Helper(decoration, inst, diag, underlying_type);
874 }
875
ValidateF32Helper(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)876 spv_result_t BuiltInsValidator::ValidateF32Helper(
877 const Decoration& decoration, const Instruction& inst,
878 const std::function<spv_result_t(const std::string& message)>& diag,
879 uint32_t underlying_type) {
880 if (!_.IsFloatScalarType(underlying_type)) {
881 return diag(GetDefinitionDesc(decoration, inst) +
882 " is not a float scalar.");
883 }
884
885 const uint32_t bit_width = _.GetBitWidth(underlying_type);
886 if (bit_width != 32) {
887 std::ostringstream ss;
888 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
889 << ".";
890 return diag(ss.str());
891 }
892
893 return SPV_SUCCESS;
894 }
895
ValidateI32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)896 spv_result_t BuiltInsValidator::ValidateI32Vec(
897 const Decoration& decoration, const Instruction& inst,
898 uint32_t num_components,
899 const std::function<spv_result_t(const std::string& message)>& diag) {
900 uint32_t underlying_type = 0;
901 if (spv_result_t error =
902 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
903 return error;
904 }
905
906 if (!_.IsIntVectorType(underlying_type)) {
907 return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector.");
908 }
909
910 const uint32_t actual_num_components = _.GetDimension(underlying_type);
911 if (_.GetDimension(underlying_type) != num_components) {
912 std::ostringstream ss;
913 ss << GetDefinitionDesc(decoration, inst) << " has "
914 << actual_num_components << " components.";
915 return diag(ss.str());
916 }
917
918 const uint32_t bit_width = _.GetBitWidth(underlying_type);
919 if (bit_width != 32) {
920 std::ostringstream ss;
921 ss << GetDefinitionDesc(decoration, inst)
922 << " has components with bit width " << bit_width << ".";
923 return diag(ss.str());
924 }
925
926 return SPV_SUCCESS;
927 }
928
ValidateArrayedI32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)929 spv_result_t BuiltInsValidator::ValidateArrayedI32Vec(
930 const Decoration& decoration, const Instruction& inst,
931 uint32_t num_components,
932 const std::function<spv_result_t(const std::string& message)>& diag) {
933 uint32_t underlying_type = 0;
934 if (spv_result_t error =
935 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
936 return error;
937 }
938
939 const Instruction* const type_inst = _.FindDef(underlying_type);
940 if (type_inst->opcode() != spv::Op::OpTypeArray) {
941 return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
942 }
943
944 const uint32_t component_type = type_inst->word(2);
945 if (!_.IsIntVectorType(component_type)) {
946 return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector.");
947 }
948
949 const uint32_t actual_num_components = _.GetDimension(component_type);
950 if (_.GetDimension(component_type) != num_components) {
951 std::ostringstream ss;
952 ss << GetDefinitionDesc(decoration, inst) << " has "
953 << actual_num_components << " components.";
954 return diag(ss.str());
955 }
956
957 const uint32_t bit_width = _.GetBitWidth(component_type);
958 if (bit_width != 32) {
959 std::ostringstream ss;
960 ss << GetDefinitionDesc(decoration, inst)
961 << " has components with bit width " << bit_width << ".";
962 return diag(ss.str());
963 }
964
965 return SPV_SUCCESS;
966 }
967
ValidateOptionalArrayedF32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)968 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec(
969 const Decoration& decoration, const Instruction& inst,
970 uint32_t num_components,
971 const std::function<spv_result_t(const std::string& message)>& diag) {
972 uint32_t underlying_type = 0;
973 if (spv_result_t error =
974 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
975 return error;
976 }
977
978 // Strip the array, if present.
979 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
980 underlying_type = _.FindDef(underlying_type)->word(2u);
981 }
982
983 return ValidateF32VecHelper(decoration, inst, num_components, diag,
984 underlying_type);
985 }
986
ValidateF32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)987 spv_result_t BuiltInsValidator::ValidateF32Vec(
988 const Decoration& decoration, const Instruction& inst,
989 uint32_t num_components,
990 const std::function<spv_result_t(const std::string& message)>& diag) {
991 uint32_t underlying_type = 0;
992 if (spv_result_t error =
993 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
994 return error;
995 }
996
997 return ValidateF32VecHelper(decoration, inst, num_components, diag,
998 underlying_type);
999 }
1000
ValidateF32VecHelper(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)1001 spv_result_t BuiltInsValidator::ValidateF32VecHelper(
1002 const Decoration& decoration, const Instruction& inst,
1003 uint32_t num_components,
1004 const std::function<spv_result_t(const std::string& message)>& diag,
1005 uint32_t underlying_type) {
1006 if (!_.IsFloatVectorType(underlying_type)) {
1007 return diag(GetDefinitionDesc(decoration, inst) +
1008 " is not a float vector.");
1009 }
1010
1011 const uint32_t actual_num_components = _.GetDimension(underlying_type);
1012 if (_.GetDimension(underlying_type) != num_components) {
1013 std::ostringstream ss;
1014 ss << GetDefinitionDesc(decoration, inst) << " has "
1015 << actual_num_components << " components.";
1016 return diag(ss.str());
1017 }
1018
1019 const uint32_t bit_width = _.GetBitWidth(underlying_type);
1020 if (bit_width != 32) {
1021 std::ostringstream ss;
1022 ss << GetDefinitionDesc(decoration, inst)
1023 << " has components with bit width " << bit_width << ".";
1024 return diag(ss.str());
1025 }
1026
1027 return SPV_SUCCESS;
1028 }
1029
ValidateI32Arr(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)1030 spv_result_t BuiltInsValidator::ValidateI32Arr(
1031 const Decoration& decoration, const Instruction& inst,
1032 const std::function<spv_result_t(const std::string& message)>& diag) {
1033 uint32_t underlying_type = 0;
1034 if (spv_result_t error =
1035 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
1036 return error;
1037 }
1038
1039 const Instruction* const type_inst = _.FindDef(underlying_type);
1040 if (type_inst->opcode() != spv::Op::OpTypeArray) {
1041 return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
1042 }
1043
1044 const uint32_t component_type = type_inst->word(2);
1045 if (!_.IsIntScalarType(component_type)) {
1046 return diag(GetDefinitionDesc(decoration, inst) +
1047 " components are not int scalar.");
1048 }
1049
1050 const uint32_t bit_width = _.GetBitWidth(component_type);
1051 if (bit_width != 32) {
1052 std::ostringstream ss;
1053 ss << GetDefinitionDesc(decoration, inst)
1054 << " has components with bit width " << bit_width << ".";
1055 return diag(ss.str());
1056 }
1057
1058 return SPV_SUCCESS;
1059 }
1060
ValidateF32Arr(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)1061 spv_result_t BuiltInsValidator::ValidateF32Arr(
1062 const Decoration& decoration, const Instruction& inst,
1063 uint32_t num_components,
1064 const std::function<spv_result_t(const std::string& message)>& diag) {
1065 uint32_t underlying_type = 0;
1066 if (spv_result_t error =
1067 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
1068 return error;
1069 }
1070
1071 return ValidateF32ArrHelper(decoration, inst, num_components, diag,
1072 underlying_type);
1073 }
1074
ValidateOptionalArrayedF32Arr(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)1075 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Arr(
1076 const Decoration& decoration, const Instruction& inst,
1077 uint32_t num_components,
1078 const std::function<spv_result_t(const std::string& message)>& diag) {
1079 uint32_t underlying_type = 0;
1080 if (spv_result_t error =
1081 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
1082 return error;
1083 }
1084
1085 // Strip an extra layer of arraying if present.
1086 if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
1087 uint32_t subtype = _.FindDef(underlying_type)->word(2u);
1088 if (_.GetIdOpcode(subtype) == spv::Op::OpTypeArray) {
1089 underlying_type = subtype;
1090 }
1091 }
1092
1093 return ValidateF32ArrHelper(decoration, inst, num_components, diag,
1094 underlying_type);
1095 }
1096
ValidateF32ArrHelper(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)1097 spv_result_t BuiltInsValidator::ValidateF32ArrHelper(
1098 const Decoration& decoration, const Instruction& inst,
1099 uint32_t num_components,
1100 const std::function<spv_result_t(const std::string& message)>& diag,
1101 uint32_t underlying_type) {
1102 const Instruction* const type_inst = _.FindDef(underlying_type);
1103 if (type_inst->opcode() != spv::Op::OpTypeArray) {
1104 return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
1105 }
1106
1107 const uint32_t component_type = type_inst->word(2);
1108 if (!_.IsFloatScalarType(component_type)) {
1109 return diag(GetDefinitionDesc(decoration, inst) +
1110 " components are not float scalar.");
1111 }
1112
1113 const uint32_t bit_width = _.GetBitWidth(component_type);
1114 if (bit_width != 32) {
1115 std::ostringstream ss;
1116 ss << GetDefinitionDesc(decoration, inst)
1117 << " has components with bit width " << bit_width << ".";
1118 return diag(ss.str());
1119 }
1120
1121 if (num_components != 0) {
1122 uint64_t actual_num_components = 0;
1123 if (!_.GetConstantValUint64(type_inst->word(3), &actual_num_components)) {
1124 assert(0 && "Array type definition is corrupt");
1125 }
1126 if (actual_num_components != num_components) {
1127 std::ostringstream ss;
1128 ss << GetDefinitionDesc(decoration, inst) << " has "
1129 << actual_num_components << " components.";
1130 return diag(ss.str());
1131 }
1132 }
1133
1134 return SPV_SUCCESS;
1135 }
1136
ValidateF32Mat(const Decoration & decoration,const Instruction & inst,uint32_t req_num_rows,uint32_t req_num_columns,const std::function<spv_result_t (const std::string & message)> & diag)1137 spv_result_t BuiltInsValidator::ValidateF32Mat(
1138 const Decoration& decoration, const Instruction& inst,
1139 uint32_t req_num_rows, uint32_t req_num_columns,
1140 const std::function<spv_result_t(const std::string& message)>& diag) {
1141 uint32_t underlying_type = 0;
1142 uint32_t num_rows = 0;
1143 uint32_t num_cols = 0;
1144 uint32_t col_type = 0;
1145 uint32_t component_type = 0;
1146 if (spv_result_t error =
1147 GetUnderlyingType(_, decoration, inst, &underlying_type)) {
1148 return error;
1149 }
1150 if (!_.GetMatrixTypeInfo(underlying_type, &num_rows, &num_cols, &col_type,
1151 &component_type) ||
1152 num_rows != req_num_rows || num_cols != req_num_columns) {
1153 std::ostringstream ss;
1154 ss << GetDefinitionDesc(decoration, inst) << " has columns " << num_cols
1155 << " and rows " << num_rows << " not equal to expected "
1156 << req_num_columns << "x" << req_num_rows << ".";
1157 return diag(ss.str());
1158 }
1159
1160 return ValidateF32VecHelper(decoration, inst, req_num_rows, diag, col_type);
1161 }
1162
ValidateNotCalledWithExecutionModel(int vuid,const char * comment,spv::ExecutionModel execution_model,const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1163 spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel(
1164 int vuid, const char* comment, spv::ExecutionModel execution_model,
1165 const Decoration& decoration, const Instruction& built_in_inst,
1166 const Instruction& referenced_inst,
1167 const Instruction& referenced_from_inst) {
1168 if (function_id_) {
1169 if (execution_models_.count(execution_model)) {
1170 const char* execution_model_str = _.grammar().lookupOperandName(
1171 SPV_OPERAND_TYPE_EXECUTION_MODEL, uint32_t(execution_model));
1172 const char* built_in_str = _.grammar().lookupOperandName(
1173 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]);
1174 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1175 << (vuid < 0 ? std::string("") : _.VkErrorID(vuid)) << comment
1176 << " " << GetIdDesc(referenced_inst) << " depends on "
1177 << GetIdDesc(built_in_inst) << " which is decorated with BuiltIn "
1178 << built_in_str << "."
1179 << " Id <" << referenced_inst.id() << "> is later referenced by "
1180 << GetIdDesc(referenced_from_inst) << " in function <"
1181 << function_id_ << "> which is called with execution model "
1182 << execution_model_str << ".";
1183 }
1184 } else {
1185 // Propagate this rule to all dependant ids in the global scope.
1186 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1187 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1188 vuid, comment, execution_model, decoration, built_in_inst,
1189 referenced_from_inst, std::placeholders::_1));
1190 }
1191 return SPV_SUCCESS;
1192 }
1193
ValidateClipOrCullDistanceAtDefinition(const Decoration & decoration,const Instruction & inst)1194 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtDefinition(
1195 const Decoration& decoration, const Instruction& inst) {
1196 // Seed at reference checks with this built-in.
1197 return ValidateClipOrCullDistanceAtReference(decoration, inst, inst, inst);
1198 }
1199
ValidateClipOrCullDistanceAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1200 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference(
1201 const Decoration& decoration, const Instruction& built_in_inst,
1202 const Instruction& referenced_inst,
1203 const Instruction& referenced_from_inst) {
1204 uint32_t operand = decoration.params()[0];
1205 if (spvIsVulkanEnv(_.context()->target_env)) {
1206 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1207 if (storage_class != spv::StorageClass::Max &&
1208 storage_class != spv::StorageClass::Input &&
1209 storage_class != spv::StorageClass::Output) {
1210 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4190 : 4199;
1211 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1212 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
1213 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
1214 operand)
1215 << " to be only used for variables with Input or Output storage "
1216 "class. "
1217 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1218 referenced_from_inst)
1219 << " " << GetStorageClassDesc(referenced_from_inst);
1220 }
1221
1222 if (storage_class == spv::StorageClass::Input) {
1223 assert(function_id_ == 0);
1224 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4188 : 4197;
1225 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1226 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1227 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1228 "used for variables with Input storage class if execution model is "
1229 "Vertex.",
1230 spv::ExecutionModel::Vertex, decoration, built_in_inst,
1231 referenced_from_inst, std::placeholders::_1));
1232 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1233 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1234 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1235 "used for variables with Input storage class if execution model is "
1236 "MeshNV.",
1237 spv::ExecutionModel::MeshNV, decoration, built_in_inst,
1238 referenced_from_inst, std::placeholders::_1));
1239 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1240 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1241 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1242 "used for variables with Input storage class if execution model is "
1243 "MeshEXT.",
1244 spv::ExecutionModel::MeshEXT, decoration, built_in_inst,
1245 referenced_from_inst, std::placeholders::_1));
1246 }
1247
1248 if (storage_class == spv::StorageClass::Output) {
1249 assert(function_id_ == 0);
1250 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4189 : 4198;
1251 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1252 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
1253 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
1254 "used for variables with Output storage class if execution model is "
1255 "Fragment.",
1256 spv::ExecutionModel::Fragment, decoration, built_in_inst,
1257 referenced_from_inst, std::placeholders::_1));
1258 }
1259
1260 for (const spv::ExecutionModel execution_model : execution_models_) {
1261 switch (execution_model) {
1262 case spv::ExecutionModel::Fragment:
1263 case spv::ExecutionModel::Vertex: {
1264 if (spv_result_t error = ValidateF32Arr(
1265 decoration, built_in_inst, /* Any number of components */ 0,
1266 [this, &decoration, &referenced_from_inst](
1267 const std::string& message) -> spv_result_t {
1268 uint32_t vuid =
1269 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
1270 ? 4191
1271 : 4200;
1272 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1273 << _.VkErrorID(vuid)
1274 << "According to the Vulkan spec BuiltIn "
1275 << _.grammar().lookupOperandName(
1276 SPV_OPERAND_TYPE_BUILT_IN,
1277 decoration.params()[0])
1278 << " variable needs to be a 32-bit float array. "
1279 << message;
1280 })) {
1281 return error;
1282 }
1283 break;
1284 }
1285 case spv::ExecutionModel::TessellationControl:
1286 case spv::ExecutionModel::TessellationEvaluation:
1287 case spv::ExecutionModel::Geometry:
1288 case spv::ExecutionModel::MeshNV:
1289 case spv::ExecutionModel::MeshEXT: {
1290 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1291 // The outer level of array is applied on the variable.
1292 if (spv_result_t error = ValidateF32Arr(
1293 decoration, built_in_inst, /* Any number of components */ 0,
1294 [this, &decoration, &referenced_from_inst](
1295 const std::string& message) -> spv_result_t {
1296 uint32_t vuid =
1297 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
1298 ? 4191
1299 : 4200;
1300 return _.diag(SPV_ERROR_INVALID_DATA,
1301 &referenced_from_inst)
1302 << _.VkErrorID(vuid)
1303 << "According to the Vulkan spec BuiltIn "
1304 << _.grammar().lookupOperandName(
1305 SPV_OPERAND_TYPE_BUILT_IN,
1306 decoration.params()[0])
1307 << " variable needs to be a 32-bit float array. "
1308 << message;
1309 })) {
1310 return error;
1311 }
1312 } else {
1313 if (spv_result_t error = ValidateOptionalArrayedF32Arr(
1314 decoration, built_in_inst, /* Any number of components */ 0,
1315 [this, &decoration, &referenced_from_inst](
1316 const std::string& message) -> spv_result_t {
1317 uint32_t vuid =
1318 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance)
1319 ? 4191
1320 : 4200;
1321 return _.diag(SPV_ERROR_INVALID_DATA,
1322 &referenced_from_inst)
1323 << _.VkErrorID(vuid)
1324 << "According to the Vulkan spec BuiltIn "
1325 << _.grammar().lookupOperandName(
1326 SPV_OPERAND_TYPE_BUILT_IN,
1327 decoration.params()[0])
1328 << " variable needs to be a 32-bit float array. "
1329 << message;
1330 })) {
1331 return error;
1332 }
1333 }
1334 break;
1335 }
1336
1337 default: {
1338 uint32_t vuid =
1339 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::ClipDistance) ? 4187 : 4196;
1340 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1341 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
1342 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
1343 operand)
1344 << " to be used only with Fragment, Vertex, "
1345 "TessellationControl, TessellationEvaluation or Geometry "
1346 "execution models. "
1347 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1348 referenced_from_inst, execution_model);
1349 }
1350 }
1351 }
1352 }
1353
1354 if (function_id_ == 0) {
1355 // Propagate this rule to all dependant ids in the global scope.
1356 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1357 std::bind(&BuiltInsValidator::ValidateClipOrCullDistanceAtReference,
1358 this, decoration, built_in_inst, referenced_from_inst,
1359 std::placeholders::_1));
1360 }
1361
1362 return SPV_SUCCESS;
1363 }
1364
ValidateFragCoordAtDefinition(const Decoration & decoration,const Instruction & inst)1365 spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition(
1366 const Decoration& decoration, const Instruction& inst) {
1367 if (spvIsVulkanEnv(_.context()->target_env)) {
1368 if (spv_result_t error = ValidateF32Vec(
1369 decoration, inst, 4,
1370 [this, &inst](const std::string& message) -> spv_result_t {
1371 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1372 << _.VkErrorID(4212) << "According to the "
1373 << spvLogStringForEnv(_.context()->target_env)
1374 << " spec BuiltIn FragCoord "
1375 "variable needs to be a 4-component 32-bit float "
1376 "vector. "
1377 << message;
1378 })) {
1379 return error;
1380 }
1381 }
1382
1383 // Seed at reference checks with this built-in.
1384 return ValidateFragCoordAtReference(decoration, inst, inst, inst);
1385 }
1386
ValidateFragCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1387 spv_result_t BuiltInsValidator::ValidateFragCoordAtReference(
1388 const Decoration& decoration, const Instruction& built_in_inst,
1389 const Instruction& referenced_inst,
1390 const Instruction& referenced_from_inst) {
1391 if (spvIsVulkanEnv(_.context()->target_env)) {
1392 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1393 if (storage_class != spv::StorageClass::Max &&
1394 storage_class != spv::StorageClass::Input) {
1395 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1396 << _.VkErrorID(4211) << spvLogStringForEnv(_.context()->target_env)
1397 << " spec allows BuiltIn FragCoord to be only used for "
1398 "variables with Input storage class. "
1399 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1400 referenced_from_inst)
1401 << " " << GetStorageClassDesc(referenced_from_inst);
1402 }
1403
1404 for (const spv::ExecutionModel execution_model : execution_models_) {
1405 if (execution_model != spv::ExecutionModel::Fragment) {
1406 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1407 << _.VkErrorID(4210)
1408 << spvLogStringForEnv(_.context()->target_env)
1409 << " spec allows BuiltIn FragCoord to be used only with "
1410 "Fragment execution model. "
1411 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1412 referenced_from_inst, execution_model);
1413 }
1414 }
1415 }
1416
1417 if (function_id_ == 0) {
1418 // Propagate this rule to all dependant ids in the global scope.
1419 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1420 &BuiltInsValidator::ValidateFragCoordAtReference, this, decoration,
1421 built_in_inst, referenced_from_inst, std::placeholders::_1));
1422 }
1423
1424 return SPV_SUCCESS;
1425 }
1426
ValidateFragDepthAtDefinition(const Decoration & decoration,const Instruction & inst)1427 spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition(
1428 const Decoration& decoration, const Instruction& inst) {
1429 if (spvIsVulkanEnv(_.context()->target_env)) {
1430 if (spv_result_t error = ValidateF32(
1431 decoration, inst,
1432 [this, &inst](const std::string& message) -> spv_result_t {
1433 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1434 << _.VkErrorID(4215) << "According to the "
1435 << spvLogStringForEnv(_.context()->target_env)
1436 << " spec BuiltIn FragDepth "
1437 "variable needs to be a 32-bit float scalar. "
1438 << message;
1439 })) {
1440 return error;
1441 }
1442 }
1443
1444 // Seed at reference checks with this built-in.
1445 return ValidateFragDepthAtReference(decoration, inst, inst, inst);
1446 }
1447
ValidateFragDepthAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1448 spv_result_t BuiltInsValidator::ValidateFragDepthAtReference(
1449 const Decoration& decoration, const Instruction& built_in_inst,
1450 const Instruction& referenced_inst,
1451 const Instruction& referenced_from_inst) {
1452 if (spvIsVulkanEnv(_.context()->target_env)) {
1453 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1454 if (storage_class != spv::StorageClass::Max &&
1455 storage_class != spv::StorageClass::Output) {
1456 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1457 << _.VkErrorID(4214) << spvLogStringForEnv(_.context()->target_env)
1458 << " spec allows BuiltIn FragDepth to be only used for "
1459 "variables with Output storage class. "
1460 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1461 referenced_from_inst)
1462 << " " << GetStorageClassDesc(referenced_from_inst);
1463 }
1464
1465 for (const spv::ExecutionModel execution_model : execution_models_) {
1466 if (execution_model != spv::ExecutionModel::Fragment) {
1467 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1468 << _.VkErrorID(4213)
1469 << spvLogStringForEnv(_.context()->target_env)
1470 << " spec allows BuiltIn FragDepth to be used only with "
1471 "Fragment execution model. "
1472 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1473 referenced_from_inst, execution_model);
1474 }
1475 }
1476
1477 for (const uint32_t entry_point : *entry_points_) {
1478 // Every entry point from which this function is called needs to have
1479 // Execution Mode DepthReplacing.
1480 const auto* modes = _.GetExecutionModes(entry_point);
1481 if (!modes || !modes->count(spv::ExecutionMode::DepthReplacing)) {
1482 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1483 << _.VkErrorID(4216)
1484 << spvLogStringForEnv(_.context()->target_env)
1485 << " spec requires DepthReplacing execution mode to be "
1486 "declared when using BuiltIn FragDepth. "
1487 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1488 referenced_from_inst);
1489 }
1490 }
1491 }
1492
1493 if (function_id_ == 0) {
1494 // Propagate this rule to all dependant ids in the global scope.
1495 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1496 &BuiltInsValidator::ValidateFragDepthAtReference, this, decoration,
1497 built_in_inst, referenced_from_inst, std::placeholders::_1));
1498 }
1499
1500 return SPV_SUCCESS;
1501 }
1502
ValidateFrontFacingAtDefinition(const Decoration & decoration,const Instruction & inst)1503 spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition(
1504 const Decoration& decoration, const Instruction& inst) {
1505 if (spvIsVulkanEnv(_.context()->target_env)) {
1506 if (spv_result_t error = ValidateBool(
1507 decoration, inst,
1508 [this, &inst](const std::string& message) -> spv_result_t {
1509 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1510 << _.VkErrorID(4231) << "According to the "
1511 << spvLogStringForEnv(_.context()->target_env)
1512 << " spec BuiltIn FrontFacing "
1513 "variable needs to be a bool scalar. "
1514 << message;
1515 })) {
1516 return error;
1517 }
1518 }
1519
1520 // Seed at reference checks with this built-in.
1521 return ValidateFrontFacingAtReference(decoration, inst, inst, inst);
1522 }
1523
ValidateFrontFacingAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1524 spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference(
1525 const Decoration& decoration, const Instruction& built_in_inst,
1526 const Instruction& referenced_inst,
1527 const Instruction& referenced_from_inst) {
1528 if (spvIsVulkanEnv(_.context()->target_env)) {
1529 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1530 if (storage_class != spv::StorageClass::Max &&
1531 storage_class != spv::StorageClass::Input) {
1532 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1533 << _.VkErrorID(4230) << spvLogStringForEnv(_.context()->target_env)
1534 << " spec allows BuiltIn FrontFacing to be only used for "
1535 "variables with Input storage class. "
1536 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1537 referenced_from_inst)
1538 << " " << GetStorageClassDesc(referenced_from_inst);
1539 }
1540
1541 for (const spv::ExecutionModel execution_model : execution_models_) {
1542 if (execution_model != spv::ExecutionModel::Fragment) {
1543 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1544 << _.VkErrorID(4229)
1545 << spvLogStringForEnv(_.context()->target_env)
1546 << " spec allows BuiltIn FrontFacing to be used only with "
1547 "Fragment execution model. "
1548 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1549 referenced_from_inst, execution_model);
1550 }
1551 }
1552 }
1553
1554 if (function_id_ == 0) {
1555 // Propagate this rule to all dependant ids in the global scope.
1556 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1557 &BuiltInsValidator::ValidateFrontFacingAtReference, this, decoration,
1558 built_in_inst, referenced_from_inst, std::placeholders::_1));
1559 }
1560
1561 return SPV_SUCCESS;
1562 }
1563
ValidateHelperInvocationAtDefinition(const Decoration & decoration,const Instruction & inst)1564 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtDefinition(
1565 const Decoration& decoration, const Instruction& inst) {
1566 if (spvIsVulkanEnv(_.context()->target_env)) {
1567 if (spv_result_t error = ValidateBool(
1568 decoration, inst,
1569 [this, &inst](const std::string& message) -> spv_result_t {
1570 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1571 << _.VkErrorID(4241)
1572 << "According to the Vulkan spec BuiltIn HelperInvocation "
1573 "variable needs to be a bool scalar. "
1574 << message;
1575 })) {
1576 return error;
1577 }
1578 }
1579
1580 // Seed at reference checks with this built-in.
1581 return ValidateHelperInvocationAtReference(decoration, inst, inst, inst);
1582 }
1583
ValidateHelperInvocationAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1584 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtReference(
1585 const Decoration& decoration, const Instruction& built_in_inst,
1586 const Instruction& referenced_inst,
1587 const Instruction& referenced_from_inst) {
1588 if (spvIsVulkanEnv(_.context()->target_env)) {
1589 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1590 if (storage_class != spv::StorageClass::Max &&
1591 storage_class != spv::StorageClass::Input) {
1592 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1593 << _.VkErrorID(4240)
1594 << "Vulkan spec allows BuiltIn HelperInvocation to be only used "
1595 "for variables with Input storage class. "
1596 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1597 referenced_from_inst)
1598 << " " << GetStorageClassDesc(referenced_from_inst);
1599 }
1600
1601 for (const spv::ExecutionModel execution_model : execution_models_) {
1602 if (execution_model != spv::ExecutionModel::Fragment) {
1603 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1604 << _.VkErrorID(4239)
1605 << "Vulkan spec allows BuiltIn HelperInvocation to be used only "
1606 "with Fragment execution model. "
1607 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1608 referenced_from_inst, execution_model);
1609 }
1610 }
1611 }
1612
1613 if (function_id_ == 0) {
1614 // Propagate this rule to all dependant ids in the global scope.
1615 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1616 std::bind(&BuiltInsValidator::ValidateHelperInvocationAtReference, this,
1617 decoration, built_in_inst, referenced_from_inst,
1618 std::placeholders::_1));
1619 }
1620
1621 return SPV_SUCCESS;
1622 }
1623
ValidateInvocationIdAtDefinition(const Decoration & decoration,const Instruction & inst)1624 spv_result_t BuiltInsValidator::ValidateInvocationIdAtDefinition(
1625 const Decoration& decoration, const Instruction& inst) {
1626 if (spvIsVulkanEnv(_.context()->target_env)) {
1627 if (spv_result_t error = ValidateI32(
1628 decoration, inst,
1629 [this, &inst](const std::string& message) -> spv_result_t {
1630 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1631 << _.VkErrorID(4259)
1632 << "According to the Vulkan spec BuiltIn InvocationId "
1633 "variable needs to be a 32-bit int scalar. "
1634 << message;
1635 })) {
1636 return error;
1637 }
1638 }
1639
1640 // Seed at reference checks with this built-in.
1641 return ValidateInvocationIdAtReference(decoration, inst, inst, inst);
1642 }
1643
ValidateInvocationIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1644 spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference(
1645 const Decoration& decoration, const Instruction& built_in_inst,
1646 const Instruction& referenced_inst,
1647 const Instruction& referenced_from_inst) {
1648 if (spvIsVulkanEnv(_.context()->target_env)) {
1649 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1650 if (storage_class != spv::StorageClass::Max &&
1651 storage_class != spv::StorageClass::Input) {
1652 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1653 << _.VkErrorID(4258)
1654 << "Vulkan spec allows BuiltIn InvocationId to be only used for "
1655 "variables with Input storage class. "
1656 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1657 referenced_from_inst)
1658 << " " << GetStorageClassDesc(referenced_from_inst);
1659 }
1660
1661 for (const spv::ExecutionModel execution_model : execution_models_) {
1662 if (execution_model != spv::ExecutionModel::TessellationControl &&
1663 execution_model != spv::ExecutionModel::Geometry) {
1664 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1665 << _.VkErrorID(4257)
1666 << "Vulkan spec allows BuiltIn InvocationId to be used only "
1667 "with TessellationControl or Geometry execution models. "
1668 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1669 referenced_from_inst, execution_model);
1670 }
1671 }
1672 }
1673
1674 if (function_id_ == 0) {
1675 // Propagate this rule to all dependant ids in the global scope.
1676 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1677 &BuiltInsValidator::ValidateInvocationIdAtReference, this, decoration,
1678 built_in_inst, referenced_from_inst, std::placeholders::_1));
1679 }
1680
1681 return SPV_SUCCESS;
1682 }
1683
ValidateInstanceIndexAtDefinition(const Decoration & decoration,const Instruction & inst)1684 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition(
1685 const Decoration& decoration, const Instruction& inst) {
1686 if (spvIsVulkanEnv(_.context()->target_env)) {
1687 if (spv_result_t error = ValidateI32(
1688 decoration, inst,
1689 [this, &inst](const std::string& message) -> spv_result_t {
1690 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1691 << _.VkErrorID(4265) << "According to the "
1692 << spvLogStringForEnv(_.context()->target_env)
1693 << " spec BuiltIn InstanceIndex "
1694 "variable needs to be a 32-bit int scalar. "
1695 << message;
1696 })) {
1697 return error;
1698 }
1699 }
1700
1701 // Seed at reference checks with this built-in.
1702 return ValidateInstanceIndexAtReference(decoration, inst, inst, inst);
1703 }
1704
ValidateInstanceIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1705 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference(
1706 const Decoration& decoration, const Instruction& built_in_inst,
1707 const Instruction& referenced_inst,
1708 const Instruction& referenced_from_inst) {
1709 if (spvIsVulkanEnv(_.context()->target_env)) {
1710 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1711 if (storage_class != spv::StorageClass::Max &&
1712 storage_class != spv::StorageClass::Input) {
1713 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1714 << _.VkErrorID(4264) << spvLogStringForEnv(_.context()->target_env)
1715 << " spec allows BuiltIn InstanceIndex to be only used for "
1716 "variables with Input storage class. "
1717 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1718 referenced_from_inst)
1719 << " " << GetStorageClassDesc(referenced_from_inst);
1720 }
1721
1722 for (const spv::ExecutionModel execution_model : execution_models_) {
1723 if (execution_model != spv::ExecutionModel::Vertex) {
1724 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1725 << _.VkErrorID(4263)
1726 << spvLogStringForEnv(_.context()->target_env)
1727 << " spec allows BuiltIn InstanceIndex to be used only "
1728 "with Vertex execution model. "
1729 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1730 referenced_from_inst, execution_model);
1731 }
1732 }
1733 }
1734
1735 if (function_id_ == 0) {
1736 // Propagate this rule to all dependant ids in the global scope.
1737 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1738 &BuiltInsValidator::ValidateInstanceIndexAtReference, this, decoration,
1739 built_in_inst, referenced_from_inst, std::placeholders::_1));
1740 }
1741
1742 return SPV_SUCCESS;
1743 }
1744
ValidatePatchVerticesAtDefinition(const Decoration & decoration,const Instruction & inst)1745 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtDefinition(
1746 const Decoration& decoration, const Instruction& inst) {
1747 if (spvIsVulkanEnv(_.context()->target_env)) {
1748 if (spv_result_t error = ValidateI32(
1749 decoration, inst,
1750 [this, &inst](const std::string& message) -> spv_result_t {
1751 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1752 << _.VkErrorID(4310)
1753 << "According to the Vulkan spec BuiltIn PatchVertices "
1754 "variable needs to be a 32-bit int scalar. "
1755 << message;
1756 })) {
1757 return error;
1758 }
1759 }
1760
1761 // Seed at reference checks with this built-in.
1762 return ValidatePatchVerticesAtReference(decoration, inst, inst, inst);
1763 }
1764
ValidatePatchVerticesAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1765 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtReference(
1766 const Decoration& decoration, const Instruction& built_in_inst,
1767 const Instruction& referenced_inst,
1768 const Instruction& referenced_from_inst) {
1769 if (spvIsVulkanEnv(_.context()->target_env)) {
1770 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1771 if (storage_class != spv::StorageClass::Max &&
1772 storage_class != spv::StorageClass::Input) {
1773 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1774 << _.VkErrorID(4309)
1775 << "Vulkan spec allows BuiltIn PatchVertices to be only used for "
1776 "variables with Input storage class. "
1777 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1778 referenced_from_inst)
1779 << " " << GetStorageClassDesc(referenced_from_inst);
1780 }
1781
1782 for (const spv::ExecutionModel execution_model : execution_models_) {
1783 if (execution_model != spv::ExecutionModel::TessellationControl &&
1784 execution_model != spv::ExecutionModel::TessellationEvaluation) {
1785 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1786 << _.VkErrorID(4308)
1787 << "Vulkan spec allows BuiltIn PatchVertices to be used only "
1788 "with TessellationControl or TessellationEvaluation "
1789 "execution models. "
1790 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1791 referenced_from_inst, execution_model);
1792 }
1793 }
1794 }
1795
1796 if (function_id_ == 0) {
1797 // Propagate this rule to all dependant ids in the global scope.
1798 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1799 &BuiltInsValidator::ValidatePatchVerticesAtReference, this, decoration,
1800 built_in_inst, referenced_from_inst, std::placeholders::_1));
1801 }
1802
1803 return SPV_SUCCESS;
1804 }
1805
ValidatePointCoordAtDefinition(const Decoration & decoration,const Instruction & inst)1806 spv_result_t BuiltInsValidator::ValidatePointCoordAtDefinition(
1807 const Decoration& decoration, const Instruction& inst) {
1808 if (spvIsVulkanEnv(_.context()->target_env)) {
1809 if (spv_result_t error = ValidateF32Vec(
1810 decoration, inst, 2,
1811 [this, &inst](const std::string& message) -> spv_result_t {
1812 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1813 << _.VkErrorID(4313)
1814 << "According to the Vulkan spec BuiltIn PointCoord "
1815 "variable needs to be a 2-component 32-bit float "
1816 "vector. "
1817 << message;
1818 })) {
1819 return error;
1820 }
1821 }
1822
1823 // Seed at reference checks with this built-in.
1824 return ValidatePointCoordAtReference(decoration, inst, inst, inst);
1825 }
1826
ValidatePointCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1827 spv_result_t BuiltInsValidator::ValidatePointCoordAtReference(
1828 const Decoration& decoration, const Instruction& built_in_inst,
1829 const Instruction& referenced_inst,
1830 const Instruction& referenced_from_inst) {
1831 if (spvIsVulkanEnv(_.context()->target_env)) {
1832 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1833 if (storage_class != spv::StorageClass::Max &&
1834 storage_class != spv::StorageClass::Input) {
1835 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1836 << _.VkErrorID(4312)
1837 << "Vulkan spec allows BuiltIn PointCoord to be only used for "
1838 "variables with Input storage class. "
1839 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1840 referenced_from_inst)
1841 << " " << GetStorageClassDesc(referenced_from_inst);
1842 }
1843
1844 for (const spv::ExecutionModel execution_model : execution_models_) {
1845 if (execution_model != spv::ExecutionModel::Fragment) {
1846 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1847 << _.VkErrorID(4311)
1848 << "Vulkan spec allows BuiltIn PointCoord to be used only with "
1849 "Fragment execution model. "
1850 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1851 referenced_from_inst, execution_model);
1852 }
1853 }
1854 }
1855
1856 if (function_id_ == 0) {
1857 // Propagate this rule to all dependant ids in the global scope.
1858 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1859 &BuiltInsValidator::ValidatePointCoordAtReference, this, decoration,
1860 built_in_inst, referenced_from_inst, std::placeholders::_1));
1861 }
1862
1863 return SPV_SUCCESS;
1864 }
1865
ValidatePointSizeAtDefinition(const Decoration & decoration,const Instruction & inst)1866 spv_result_t BuiltInsValidator::ValidatePointSizeAtDefinition(
1867 const Decoration& decoration, const Instruction& inst) {
1868 // Seed at reference checks with this built-in.
1869 return ValidatePointSizeAtReference(decoration, inst, inst, inst);
1870 }
1871
ValidatePointSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1872 spv_result_t BuiltInsValidator::ValidatePointSizeAtReference(
1873 const Decoration& decoration, const Instruction& built_in_inst,
1874 const Instruction& referenced_inst,
1875 const Instruction& referenced_from_inst) {
1876 if (spvIsVulkanEnv(_.context()->target_env)) {
1877 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1878 if (storage_class != spv::StorageClass::Max &&
1879 storage_class != spv::StorageClass::Input &&
1880 storage_class != spv::StorageClass::Output) {
1881 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1882 << _.VkErrorID(4316)
1883 << "Vulkan spec allows BuiltIn PointSize to be only used for "
1884 "variables with Input or Output storage class. "
1885 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1886 referenced_from_inst)
1887 << " " << GetStorageClassDesc(referenced_from_inst);
1888 }
1889
1890 if (storage_class == spv::StorageClass::Input) {
1891 assert(function_id_ == 0);
1892 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1893 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4315,
1894 "Vulkan spec doesn't allow BuiltIn PointSize to be used for "
1895 "variables with Input storage class if execution model is "
1896 "Vertex.",
1897 spv::ExecutionModel::Vertex, decoration, built_in_inst,
1898 referenced_from_inst, std::placeholders::_1));
1899 }
1900
1901 for (const spv::ExecutionModel execution_model : execution_models_) {
1902 switch (execution_model) {
1903 case spv::ExecutionModel::Vertex: {
1904 if (spv_result_t error = ValidateF32(
1905 decoration, built_in_inst,
1906 [this, &referenced_from_inst](
1907 const std::string& message) -> spv_result_t {
1908 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1909 << _.VkErrorID(4317)
1910 << "According to the Vulkan spec BuiltIn PointSize "
1911 "variable needs to be a 32-bit float scalar. "
1912 << message;
1913 })) {
1914 return error;
1915 }
1916 break;
1917 }
1918 case spv::ExecutionModel::TessellationControl:
1919 case spv::ExecutionModel::TessellationEvaluation:
1920 case spv::ExecutionModel::Geometry:
1921 case spv::ExecutionModel::MeshNV:
1922 case spv::ExecutionModel::MeshEXT: {
1923 // PointSize can be a per-vertex variable for tessellation control,
1924 // tessellation evaluation and geometry shader stages. In such cases
1925 // variables will have an array of 32-bit floats.
1926 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1927 // The array is on the variable, so this must be a 32-bit float.
1928 if (spv_result_t error = ValidateF32(
1929 decoration, built_in_inst,
1930 [this, &referenced_from_inst](
1931 const std::string& message) -> spv_result_t {
1932 return _.diag(SPV_ERROR_INVALID_DATA,
1933 &referenced_from_inst)
1934 << _.VkErrorID(4317)
1935 << "According to the Vulkan spec BuiltIn "
1936 "PointSize variable needs to be a 32-bit "
1937 "float scalar. "
1938 << message;
1939 })) {
1940 return error;
1941 }
1942 } else {
1943 if (spv_result_t error = ValidateOptionalArrayedF32(
1944 decoration, built_in_inst,
1945 [this, &referenced_from_inst](
1946 const std::string& message) -> spv_result_t {
1947 return _.diag(SPV_ERROR_INVALID_DATA,
1948 &referenced_from_inst)
1949 << _.VkErrorID(4317)
1950 << "According to the Vulkan spec BuiltIn "
1951 "PointSize variable needs to be a 32-bit "
1952 "float scalar. "
1953 << message;
1954 })) {
1955 return error;
1956 }
1957 }
1958 break;
1959 }
1960
1961 default: {
1962 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1963 << _.VkErrorID(4314)
1964 << "Vulkan spec allows BuiltIn PointSize to be used only with "
1965 "Vertex, TessellationControl, TessellationEvaluation or "
1966 "Geometry execution models. "
1967 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1968 referenced_from_inst, execution_model);
1969 }
1970 }
1971 }
1972 }
1973
1974 if (function_id_ == 0) {
1975 // Propagate this rule to all dependant ids in the global scope.
1976 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1977 &BuiltInsValidator::ValidatePointSizeAtReference, this, decoration,
1978 built_in_inst, referenced_from_inst, std::placeholders::_1));
1979 }
1980
1981 return SPV_SUCCESS;
1982 }
1983
ValidatePositionAtDefinition(const Decoration & decoration,const Instruction & inst)1984 spv_result_t BuiltInsValidator::ValidatePositionAtDefinition(
1985 const Decoration& decoration, const Instruction& inst) {
1986 // Seed at reference checks with this built-in.
1987 return ValidatePositionAtReference(decoration, inst, inst, inst);
1988 }
1989
ValidatePositionAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1990 spv_result_t BuiltInsValidator::ValidatePositionAtReference(
1991 const Decoration& decoration, const Instruction& built_in_inst,
1992 const Instruction& referenced_inst,
1993 const Instruction& referenced_from_inst) {
1994 if (spvIsVulkanEnv(_.context()->target_env)) {
1995 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
1996 if (storage_class != spv::StorageClass::Max &&
1997 storage_class != spv::StorageClass::Input &&
1998 storage_class != spv::StorageClass::Output) {
1999 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2000 << _.VkErrorID(4320) << "Vulkan spec allows BuiltIn Position to be only used for "
2001 "variables with Input or Output storage class. "
2002 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2003 referenced_from_inst)
2004 << " " << GetStorageClassDesc(referenced_from_inst);
2005 }
2006
2007 if (storage_class == spv::StorageClass::Input) {
2008 assert(function_id_ == 0);
2009 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2010 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
2011 "Vulkan spec doesn't allow BuiltIn Position to be used "
2012 "for variables "
2013 "with Input storage class if execution model is Vertex.",
2014 spv::ExecutionModel::Vertex, decoration, built_in_inst,
2015 referenced_from_inst, std::placeholders::_1));
2016 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2017 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
2018 "Vulkan spec doesn't allow BuiltIn Position to be used "
2019 "for variables "
2020 "with Input storage class if execution model is MeshNV.",
2021 spv::ExecutionModel::MeshNV, decoration, built_in_inst,
2022 referenced_from_inst, std::placeholders::_1));
2023 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2024 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4319,
2025 "Vulkan spec doesn't allow BuiltIn Position to be used "
2026 "for variables "
2027 "with Input storage class if execution model is MeshEXT.",
2028 spv::ExecutionModel::MeshEXT, decoration, built_in_inst,
2029 referenced_from_inst, std::placeholders::_1));
2030 }
2031
2032 for (const spv::ExecutionModel execution_model : execution_models_) {
2033 switch (execution_model) {
2034 case spv::ExecutionModel::Vertex: {
2035 if (spv_result_t error = ValidateF32Vec(
2036 decoration, built_in_inst, 4,
2037 [this, &referenced_from_inst](
2038 const std::string& message) -> spv_result_t {
2039 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2040 << _.VkErrorID(4321)
2041 << "According to the Vulkan spec BuiltIn Position "
2042 "variable needs to be a 4-component 32-bit float "
2043 "vector. "
2044 << message;
2045 })) {
2046 return error;
2047 }
2048 break;
2049 }
2050 case spv::ExecutionModel::Geometry:
2051 case spv::ExecutionModel::TessellationControl:
2052 case spv::ExecutionModel::TessellationEvaluation:
2053 case spv::ExecutionModel::MeshNV:
2054 case spv::ExecutionModel::MeshEXT: {
2055 // Position can be a per-vertex variable for tessellation control,
2056 // tessellation evaluation, geometry and mesh shader stages. In such
2057 // cases variables will have an array of 4-component 32-bit float
2058 // vectors.
2059 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2060 // The array is on the variable, so this must be a 4-component
2061 // 32-bit float vector.
2062 if (spv_result_t error = ValidateF32Vec(
2063 decoration, built_in_inst, 4,
2064 [this, &referenced_from_inst](
2065 const std::string& message) -> spv_result_t {
2066 return _.diag(SPV_ERROR_INVALID_DATA,
2067 &referenced_from_inst)
2068 << _.VkErrorID(4321)
2069 << "According to the Vulkan spec BuiltIn Position "
2070 "variable needs to be a 4-component 32-bit "
2071 "float vector. "
2072 << message;
2073 })) {
2074 return error;
2075 }
2076 } else {
2077 if (spv_result_t error = ValidateOptionalArrayedF32Vec(
2078 decoration, built_in_inst, 4,
2079 [this, &referenced_from_inst](
2080 const std::string& message) -> spv_result_t {
2081 return _.diag(SPV_ERROR_INVALID_DATA,
2082 &referenced_from_inst)
2083 << _.VkErrorID(4321)
2084 << "According to the Vulkan spec BuiltIn Position "
2085 "variable needs to be a 4-component 32-bit "
2086 "float vector. "
2087 << message;
2088 })) {
2089 return error;
2090 }
2091 }
2092 break;
2093 }
2094
2095 default: {
2096 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2097 << _.VkErrorID(4318)
2098 << "Vulkan spec allows BuiltIn Position to be used only "
2099 "with Vertex, TessellationControl, TessellationEvaluation"
2100 " or Geometry execution models. "
2101 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2102 referenced_from_inst, execution_model);
2103 }
2104 }
2105 }
2106 }
2107
2108 if (function_id_ == 0) {
2109 // Propagate this rule to all dependant ids in the global scope.
2110 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2111 &BuiltInsValidator::ValidatePositionAtReference, this, decoration,
2112 built_in_inst, referenced_from_inst, std::placeholders::_1));
2113 }
2114
2115 return SPV_SUCCESS;
2116 }
2117
ValidatePrimitiveIdAtDefinition(const Decoration & decoration,const Instruction & inst)2118 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition(
2119 const Decoration& decoration, const Instruction& inst) {
2120 if (spvIsVulkanEnv(_.context()->target_env)) {
2121 // PrimitiveId can be a per-primitive variable for mesh shader stage.
2122 // In such cases variable will have an array of 32-bit integers.
2123 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2124 // This must be a 32-bit int scalar.
2125 if (spv_result_t error = ValidateI32(
2126 decoration, inst,
2127 [this, &inst](const std::string& message) -> spv_result_t {
2128 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2129 << _.VkErrorID(4337)
2130 << "According to the Vulkan spec BuiltIn PrimitiveId "
2131 "variable needs to be a 32-bit int scalar. "
2132 << message;
2133 })) {
2134 return error;
2135 }
2136 } else {
2137 if (spv_result_t error = ValidateOptionalArrayedI32(
2138 decoration, inst,
2139 [this, &inst](const std::string& message) -> spv_result_t {
2140 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2141 << _.VkErrorID(4337)
2142 << "According to the Vulkan spec BuiltIn PrimitiveId "
2143 "variable needs to be a 32-bit int scalar. "
2144 << message;
2145 })) {
2146 return error;
2147 }
2148 }
2149 }
2150
2151 // Seed at reference checks with this built-in.
2152 return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst);
2153 }
2154
ValidatePrimitiveIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2155 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference(
2156 const Decoration& decoration, const Instruction& built_in_inst,
2157 const Instruction& referenced_inst,
2158 const Instruction& referenced_from_inst) {
2159 if (spvIsVulkanEnv(_.context()->target_env)) {
2160 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2161 if (storage_class != spv::StorageClass::Max &&
2162 storage_class != spv::StorageClass::Input &&
2163 storage_class != spv::StorageClass::Output) {
2164 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2165 << "Vulkan spec allows BuiltIn PrimitiveId to be only used for "
2166 "variables with Input or Output storage class. "
2167 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2168 referenced_from_inst)
2169 << " " << GetStorageClassDesc(referenced_from_inst);
2170 }
2171
2172 if (storage_class == spv::StorageClass::Output) {
2173 assert(function_id_ == 0);
2174 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2175 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2176 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2177 "variables with Output storage class if execution model is "
2178 "TessellationControl.",
2179 spv::ExecutionModel::TessellationControl, decoration, built_in_inst,
2180 referenced_from_inst, std::placeholders::_1));
2181 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2182 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2183 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2184 "variables with Output storage class if execution model is "
2185 "TessellationEvaluation.",
2186 spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst,
2187 referenced_from_inst, std::placeholders::_1));
2188 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2189 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2190 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2191 "variables with Output storage class if execution model is "
2192 "Fragment.",
2193 spv::ExecutionModel::Fragment, decoration, built_in_inst,
2194 referenced_from_inst, std::placeholders::_1));
2195 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2196 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2197 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2198 "variables with Output storage class if execution model is "
2199 "IntersectionKHR.",
2200 spv::ExecutionModel::IntersectionKHR, decoration, built_in_inst,
2201 referenced_from_inst, std::placeholders::_1));
2202 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2203 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2204 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2205 "variables with Output storage class if execution model is "
2206 "AnyHitKHR.",
2207 spv::ExecutionModel::AnyHitKHR, decoration, built_in_inst,
2208 referenced_from_inst, std::placeholders::_1));
2209 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2210 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4334,
2211 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
2212 "variables with Output storage class if execution model is "
2213 "ClosestHitKHR.",
2214 spv::ExecutionModel::ClosestHitKHR, decoration, built_in_inst,
2215 referenced_from_inst, std::placeholders::_1));
2216 }
2217
2218 for (const spv::ExecutionModel execution_model : execution_models_) {
2219 switch (execution_model) {
2220 case spv::ExecutionModel::Fragment:
2221 case spv::ExecutionModel::TessellationControl:
2222 case spv::ExecutionModel::TessellationEvaluation:
2223 case spv::ExecutionModel::Geometry:
2224 case spv::ExecutionModel::MeshNV:
2225 case spv::ExecutionModel::MeshEXT:
2226 case spv::ExecutionModel::IntersectionKHR:
2227 case spv::ExecutionModel::AnyHitKHR:
2228 case spv::ExecutionModel::ClosestHitKHR: {
2229 // Ok.
2230 break;
2231 }
2232
2233 default: {
2234 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2235 << _.VkErrorID(4330)
2236 << "Vulkan spec allows BuiltIn PrimitiveId to be used only "
2237 "with Fragment, TessellationControl, "
2238 "TessellationEvaluation, Geometry, MeshNV, MeshEXT, "
2239 "IntersectionKHR, AnyHitKHR, and ClosestHitKHR execution models. "
2240 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2241 referenced_from_inst, execution_model);
2242 }
2243 }
2244 }
2245 }
2246
2247 if (function_id_ == 0) {
2248 // Propagate this rule to all dependant ids in the global scope.
2249 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2250 &BuiltInsValidator::ValidatePrimitiveIdAtReference, this, decoration,
2251 built_in_inst, referenced_from_inst, std::placeholders::_1));
2252 }
2253
2254 return SPV_SUCCESS;
2255 }
2256
ValidateSampleIdAtDefinition(const Decoration & decoration,const Instruction & inst)2257 spv_result_t BuiltInsValidator::ValidateSampleIdAtDefinition(
2258 const Decoration& decoration, const Instruction& inst) {
2259 if (spvIsVulkanEnv(_.context()->target_env)) {
2260 if (spv_result_t error = ValidateI32(
2261 decoration, inst,
2262 [this, &inst](const std::string& message) -> spv_result_t {
2263 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2264 << _.VkErrorID(4356)
2265 << "According to the Vulkan spec BuiltIn SampleId "
2266 "variable needs to be a 32-bit int scalar. "
2267 << message;
2268 })) {
2269 return error;
2270 }
2271 }
2272
2273 // Seed at reference checks with this built-in.
2274 return ValidateSampleIdAtReference(decoration, inst, inst, inst);
2275 }
2276
ValidateSampleIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2277 spv_result_t BuiltInsValidator::ValidateSampleIdAtReference(
2278 const Decoration& decoration, const Instruction& built_in_inst,
2279 const Instruction& referenced_inst,
2280 const Instruction& referenced_from_inst) {
2281 if (spvIsVulkanEnv(_.context()->target_env)) {
2282 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2283 if (storage_class != spv::StorageClass::Max &&
2284 storage_class != spv::StorageClass::Input) {
2285 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2286 << _.VkErrorID(4355)
2287 << "Vulkan spec allows BuiltIn SampleId to be only used for "
2288 "variables with Input storage class. "
2289 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2290 referenced_from_inst)
2291 << " " << GetStorageClassDesc(referenced_from_inst);
2292 }
2293
2294 for (const spv::ExecutionModel execution_model : execution_models_) {
2295 if (execution_model != spv::ExecutionModel::Fragment) {
2296 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2297 << _.VkErrorID(4354)
2298 << "Vulkan spec allows BuiltIn SampleId to be used only with "
2299 "Fragment execution model. "
2300 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2301 referenced_from_inst, execution_model);
2302 }
2303 }
2304 }
2305
2306 if (function_id_ == 0) {
2307 // Propagate this rule to all dependant ids in the global scope.
2308 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2309 &BuiltInsValidator::ValidateSampleIdAtReference, this, decoration,
2310 built_in_inst, referenced_from_inst, std::placeholders::_1));
2311 }
2312
2313 return SPV_SUCCESS;
2314 }
2315
ValidateSampleMaskAtDefinition(const Decoration & decoration,const Instruction & inst)2316 spv_result_t BuiltInsValidator::ValidateSampleMaskAtDefinition(
2317 const Decoration& decoration, const Instruction& inst) {
2318 if (spvIsVulkanEnv(_.context()->target_env)) {
2319 if (spv_result_t error = ValidateI32Arr(
2320 decoration, inst,
2321 [this, &inst](const std::string& message) -> spv_result_t {
2322 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2323 << _.VkErrorID(4359)
2324 << "According to the Vulkan spec BuiltIn SampleMask "
2325 "variable needs to be a 32-bit int array. "
2326 << message;
2327 })) {
2328 return error;
2329 }
2330 }
2331
2332 // Seed at reference checks with this built-in.
2333 return ValidateSampleMaskAtReference(decoration, inst, inst, inst);
2334 }
2335
ValidateSampleMaskAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2336 spv_result_t BuiltInsValidator::ValidateSampleMaskAtReference(
2337 const Decoration& decoration, const Instruction& built_in_inst,
2338 const Instruction& referenced_inst,
2339 const Instruction& referenced_from_inst) {
2340 if (spvIsVulkanEnv(_.context()->target_env)) {
2341 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2342 if (storage_class != spv::StorageClass::Max &&
2343 storage_class != spv::StorageClass::Input &&
2344 storage_class != spv::StorageClass::Output) {
2345 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2346 << _.VkErrorID(4358)
2347 << "Vulkan spec allows BuiltIn SampleMask to be only used for "
2348 "variables with Input or Output storage class. "
2349 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2350 referenced_from_inst)
2351 << " " << GetStorageClassDesc(referenced_from_inst);
2352 }
2353
2354 for (const spv::ExecutionModel execution_model : execution_models_) {
2355 if (execution_model != spv::ExecutionModel::Fragment) {
2356 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2357 << _.VkErrorID(4357)
2358 << "Vulkan spec allows BuiltIn SampleMask to be used only "
2359 "with "
2360 "Fragment execution model. "
2361 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2362 referenced_from_inst, execution_model);
2363 }
2364 }
2365 }
2366
2367 if (function_id_ == 0) {
2368 // Propagate this rule to all dependant ids in the global scope.
2369 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2370 &BuiltInsValidator::ValidateSampleMaskAtReference, this, decoration,
2371 built_in_inst, referenced_from_inst, std::placeholders::_1));
2372 }
2373
2374 return SPV_SUCCESS;
2375 }
2376
ValidateSamplePositionAtDefinition(const Decoration & decoration,const Instruction & inst)2377 spv_result_t BuiltInsValidator::ValidateSamplePositionAtDefinition(
2378 const Decoration& decoration, const Instruction& inst) {
2379 if (spvIsVulkanEnv(_.context()->target_env)) {
2380 if (spv_result_t error = ValidateF32Vec(
2381 decoration, inst, 2,
2382 [this, &inst](const std::string& message) -> spv_result_t {
2383 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2384 << _.VkErrorID(4362)
2385 << "According to the Vulkan spec BuiltIn SamplePosition "
2386 "variable needs to be a 2-component 32-bit float "
2387 "vector. "
2388 << message;
2389 })) {
2390 return error;
2391 }
2392 }
2393
2394 // Seed at reference checks with this built-in.
2395 return ValidateSamplePositionAtReference(decoration, inst, inst, inst);
2396 }
2397
ValidateSamplePositionAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2398 spv_result_t BuiltInsValidator::ValidateSamplePositionAtReference(
2399 const Decoration& decoration, const Instruction& built_in_inst,
2400 const Instruction& referenced_inst,
2401 const Instruction& referenced_from_inst) {
2402 if (spvIsVulkanEnv(_.context()->target_env)) {
2403 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2404 if (storage_class != spv::StorageClass::Max &&
2405 storage_class != spv::StorageClass::Input) {
2406 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2407 << _.VkErrorID(4361)
2408 << "Vulkan spec allows BuiltIn SamplePosition to be only used "
2409 "for "
2410 "variables with Input storage class. "
2411 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2412 referenced_from_inst)
2413 << " " << GetStorageClassDesc(referenced_from_inst);
2414 }
2415
2416 for (const spv::ExecutionModel execution_model : execution_models_) {
2417 if (execution_model != spv::ExecutionModel::Fragment) {
2418 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2419 << _.VkErrorID(4360)
2420 << "Vulkan spec allows BuiltIn SamplePosition to be used only "
2421 "with "
2422 "Fragment execution model. "
2423 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2424 referenced_from_inst, execution_model);
2425 }
2426 }
2427 }
2428
2429 if (function_id_ == 0) {
2430 // Propagate this rule to all dependant ids in the global scope.
2431 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2432 &BuiltInsValidator::ValidateSamplePositionAtReference, this, decoration,
2433 built_in_inst, referenced_from_inst, std::placeholders::_1));
2434 }
2435
2436 return SPV_SUCCESS;
2437 }
2438
ValidateTessCoordAtDefinition(const Decoration & decoration,const Instruction & inst)2439 spv_result_t BuiltInsValidator::ValidateTessCoordAtDefinition(
2440 const Decoration& decoration, const Instruction& inst) {
2441 if (spvIsVulkanEnv(_.context()->target_env)) {
2442 if (spv_result_t error = ValidateF32Vec(
2443 decoration, inst, 3,
2444 [this, &inst](const std::string& message) -> spv_result_t {
2445 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2446 << _.VkErrorID(4389)
2447 << "According to the Vulkan spec BuiltIn TessCoord "
2448 "variable needs to be a 3-component 32-bit float "
2449 "vector. "
2450 << message;
2451 })) {
2452 return error;
2453 }
2454 }
2455
2456 // Seed at reference checks with this built-in.
2457 return ValidateTessCoordAtReference(decoration, inst, inst, inst);
2458 }
2459
ValidateTessCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2460 spv_result_t BuiltInsValidator::ValidateTessCoordAtReference(
2461 const Decoration& decoration, const Instruction& built_in_inst,
2462 const Instruction& referenced_inst,
2463 const Instruction& referenced_from_inst) {
2464 if (spvIsVulkanEnv(_.context()->target_env)) {
2465 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2466 if (storage_class != spv::StorageClass::Max &&
2467 storage_class != spv::StorageClass::Input) {
2468 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2469 << _.VkErrorID(4388)
2470 << "Vulkan spec allows BuiltIn TessCoord to be only used for "
2471 "variables with Input storage class. "
2472 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2473 referenced_from_inst)
2474 << " " << GetStorageClassDesc(referenced_from_inst);
2475 }
2476
2477 for (const spv::ExecutionModel execution_model : execution_models_) {
2478 if (execution_model != spv::ExecutionModel::TessellationEvaluation) {
2479 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2480 << _.VkErrorID(4387)
2481 << "Vulkan spec allows BuiltIn TessCoord to be used only with "
2482 "TessellationEvaluation execution model. "
2483 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2484 referenced_from_inst, execution_model);
2485 }
2486 }
2487 }
2488
2489 if (function_id_ == 0) {
2490 // Propagate this rule to all dependant ids in the global scope.
2491 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2492 &BuiltInsValidator::ValidateTessCoordAtReference, this, decoration,
2493 built_in_inst, referenced_from_inst, std::placeholders::_1));
2494 }
2495
2496 return SPV_SUCCESS;
2497 }
2498
ValidateTessLevelOuterAtDefinition(const Decoration & decoration,const Instruction & inst)2499 spv_result_t BuiltInsValidator::ValidateTessLevelOuterAtDefinition(
2500 const Decoration& decoration, const Instruction& inst) {
2501 if (spvIsVulkanEnv(_.context()->target_env)) {
2502 if (spv_result_t error = ValidateF32Arr(
2503 decoration, inst, 4,
2504 [this, &inst](const std::string& message) -> spv_result_t {
2505 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2506 << _.VkErrorID(4393)
2507 << "According to the Vulkan spec BuiltIn TessLevelOuter "
2508 "variable needs to be a 4-component 32-bit float "
2509 "array. "
2510 << message;
2511 })) {
2512 return error;
2513 }
2514 }
2515
2516 // Seed at reference checks with this built-in.
2517 return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2518 }
2519
ValidateTessLevelInnerAtDefinition(const Decoration & decoration,const Instruction & inst)2520 spv_result_t BuiltInsValidator::ValidateTessLevelInnerAtDefinition(
2521 const Decoration& decoration, const Instruction& inst) {
2522 if (spvIsVulkanEnv(_.context()->target_env)) {
2523 if (spv_result_t error = ValidateF32Arr(
2524 decoration, inst, 2,
2525 [this, &inst](const std::string& message) -> spv_result_t {
2526 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2527 << _.VkErrorID(4397)
2528 << "According to the Vulkan spec BuiltIn TessLevelOuter "
2529 "variable needs to be a 2-component 32-bit float "
2530 "array. "
2531 << message;
2532 })) {
2533 return error;
2534 }
2535 }
2536
2537 // Seed at reference checks with this built-in.
2538 return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2539 }
2540
ValidateTessLevelAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2541 spv_result_t BuiltInsValidator::ValidateTessLevelAtReference(
2542 const Decoration& decoration, const Instruction& built_in_inst,
2543 const Instruction& referenced_inst,
2544 const Instruction& referenced_from_inst) {
2545 uint32_t operand = decoration.params()[0];
2546 if (spvIsVulkanEnv(_.context()->target_env)) {
2547 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2548 if (storage_class != spv::StorageClass::Max &&
2549 storage_class != spv::StorageClass::Input &&
2550 storage_class != spv::StorageClass::Output) {
2551 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2552 << "Vulkan spec allows BuiltIn "
2553 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2554 operand)
2555 << " to be only used for variables with Input or Output storage "
2556 "class. "
2557 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2558 referenced_from_inst)
2559 << " " << GetStorageClassDesc(referenced_from_inst);
2560 }
2561
2562 if (storage_class == spv::StorageClass::Input) {
2563 assert(function_id_ == 0);
2564 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::TessLevelOuter) ? 4391 : 4395;
2565 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2566 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
2567 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2568 "used "
2569 "for variables with Input storage class if execution model is "
2570 "TessellationControl.",
2571 spv::ExecutionModel::TessellationControl, decoration, built_in_inst,
2572 referenced_from_inst, std::placeholders::_1));
2573 }
2574
2575 if (storage_class == spv::StorageClass::Output) {
2576 assert(function_id_ == 0);
2577 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::TessLevelOuter) ? 4392 : 4396;
2578 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2579 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, vuid,
2580 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2581 "used "
2582 "for variables with Output storage class if execution model is "
2583 "TessellationEvaluation.",
2584 spv::ExecutionModel::TessellationEvaluation, decoration, built_in_inst,
2585 referenced_from_inst, std::placeholders::_1));
2586 }
2587
2588 for (const spv::ExecutionModel execution_model : execution_models_) {
2589 switch (execution_model) {
2590 case spv::ExecutionModel::TessellationControl:
2591 case spv::ExecutionModel::TessellationEvaluation: {
2592 // Ok.
2593 break;
2594 }
2595
2596 default: {
2597 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::TessLevelOuter) ? 4390 : 4394;
2598 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2599 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
2600 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2601 operand)
2602 << " to be used only with TessellationControl or "
2603 "TessellationEvaluation execution models. "
2604 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2605 referenced_from_inst, execution_model);
2606 }
2607 }
2608 }
2609 }
2610
2611 if (function_id_ == 0) {
2612 // Propagate this rule to all dependant ids in the global scope.
2613 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2614 &BuiltInsValidator::ValidateTessLevelAtReference, this, decoration,
2615 built_in_inst, referenced_from_inst, std::placeholders::_1));
2616 }
2617
2618 return SPV_SUCCESS;
2619 }
2620
ValidateVertexIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2621 spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition(
2622 const Decoration& decoration, const Instruction& inst) {
2623 if (spvIsVulkanEnv(_.context()->target_env)) {
2624 if (spv_result_t error = ValidateI32(
2625 decoration, inst,
2626 [this, &inst](const std::string& message) -> spv_result_t {
2627 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2628 << _.VkErrorID(4400) << "According to the "
2629 << spvLogStringForEnv(_.context()->target_env)
2630 << " spec BuiltIn VertexIndex variable needs to be a "
2631 "32-bit int scalar. "
2632 << message;
2633 })) {
2634 return error;
2635 }
2636 }
2637
2638 // Seed at reference checks with this built-in.
2639 return ValidateVertexIndexAtReference(decoration, inst, inst, inst);
2640 }
2641
ValidateVertexIdAtDefinition(const Decoration & decoration,const Instruction & inst)2642 spv_result_t BuiltInsValidator::ValidateVertexIdAtDefinition(
2643 const Decoration& decoration, const Instruction& inst) {
2644 (void)decoration;
2645 if (spvIsVulkanEnv(_.context()->target_env)) {
2646 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2647 << "Vulkan spec doesn't allow BuiltIn VertexId "
2648 "to be used.";
2649 }
2650
2651 return SPV_SUCCESS;
2652 }
2653
ValidateLocalInvocationIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2654 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
2655 const Decoration& decoration, const Instruction& inst) {
2656 // Seed at reference checks with this built-in.
2657 return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
2658 }
2659
ValidateLocalInvocationIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction &,const Instruction & referenced_from_inst)2660 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
2661 const Decoration& decoration, const Instruction& built_in_inst,
2662 const Instruction&,
2663 const Instruction& referenced_from_inst) {
2664 if (function_id_ == 0) {
2665 // Propagate this rule to all dependant ids in the global scope.
2666 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2667 std::bind(&BuiltInsValidator::ValidateLocalInvocationIndexAtReference,
2668 this, decoration, built_in_inst, referenced_from_inst,
2669 std::placeholders::_1));
2670 }
2671
2672 return SPV_SUCCESS;
2673 }
2674
ValidateVertexIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2675 spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference(
2676 const Decoration& decoration, const Instruction& built_in_inst,
2677 const Instruction& referenced_inst,
2678 const Instruction& referenced_from_inst) {
2679 if (spvIsVulkanEnv(_.context()->target_env)) {
2680 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2681 if (storage_class != spv::StorageClass::Max &&
2682 storage_class != spv::StorageClass::Input) {
2683 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2684 << _.VkErrorID(4399) << spvLogStringForEnv(_.context()->target_env)
2685 << " spec allows BuiltIn VertexIndex to be only used for "
2686 "variables with Input storage class. "
2687 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2688 referenced_from_inst)
2689 << " " << GetStorageClassDesc(referenced_from_inst);
2690 }
2691
2692 for (const spv::ExecutionModel execution_model : execution_models_) {
2693 if (execution_model != spv::ExecutionModel::Vertex) {
2694 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2695 << _.VkErrorID(4398)
2696 << spvLogStringForEnv(_.context()->target_env)
2697 << " spec allows BuiltIn VertexIndex to be used only with "
2698 "Vertex execution model. "
2699 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2700 referenced_from_inst, execution_model);
2701 }
2702 }
2703 }
2704
2705 if (function_id_ == 0) {
2706 // Propagate this rule to all dependant ids in the global scope.
2707 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2708 &BuiltInsValidator::ValidateVertexIndexAtReference, this, decoration,
2709 built_in_inst, referenced_from_inst, std::placeholders::_1));
2710 }
2711
2712 return SPV_SUCCESS;
2713 }
2714
ValidateLayerOrViewportIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2715 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition(
2716 const Decoration& decoration, const Instruction& inst) {
2717 if (spvIsVulkanEnv(_.context()->target_env)) {
2718 // This can be a per-primitive variable for mesh shader stage.
2719 // In such cases variable will have an array of 32-bit integers.
2720 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2721 // This must be a 32-bit int scalar.
2722 if (spv_result_t error = ValidateI32(
2723 decoration, inst,
2724 [this, &decoration,
2725 &inst](const std::string& message) -> spv_result_t {
2726 uint32_t vuid =
2727 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::Layer) ? 4276 : 4408;
2728 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2729 << _.VkErrorID(vuid)
2730 << "According to the Vulkan spec BuiltIn "
2731 << _.grammar().lookupOperandName(
2732 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2733 << "variable needs to be a 32-bit int scalar. "
2734 << message;
2735 })) {
2736 return error;
2737 }
2738 } else {
2739 if (spv_result_t error = ValidateOptionalArrayedI32(
2740 decoration, inst,
2741 [this, &decoration,
2742 &inst](const std::string& message) -> spv_result_t {
2743 uint32_t vuid =
2744 (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::Layer) ? 4276 : 4408;
2745 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2746 << _.VkErrorID(vuid)
2747 << "According to the Vulkan spec BuiltIn "
2748 << _.grammar().lookupOperandName(
2749 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2750 << "variable needs to be a 32-bit int scalar. "
2751 << message;
2752 })) {
2753 return error;
2754 }
2755 }
2756 }
2757
2758 // Seed at reference checks with this built-in.
2759 return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst);
2760 }
2761
ValidateLayerOrViewportIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2762 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
2763 const Decoration& decoration, const Instruction& built_in_inst,
2764 const Instruction& referenced_inst,
2765 const Instruction& referenced_from_inst) {
2766 uint32_t operand = decoration.params()[0];
2767 if (spvIsVulkanEnv(_.context()->target_env)) {
2768 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2769 if (storage_class != spv::StorageClass::Max &&
2770 storage_class != spv::StorageClass::Input &&
2771 storage_class != spv::StorageClass::Output) {
2772 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2773 << "Vulkan spec allows BuiltIn "
2774 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2775 operand)
2776 << " to be only used for variables with Input or Output storage "
2777 "class. "
2778 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2779 referenced_from_inst)
2780 << " " << GetStorageClassDesc(referenced_from_inst);
2781 }
2782
2783 if (storage_class == spv::StorageClass::Input) {
2784 assert(function_id_ == 0);
2785 for (const auto em :
2786 {spv::ExecutionModel::Vertex, spv::ExecutionModel::TessellationEvaluation,
2787 spv::ExecutionModel::Geometry, spv::ExecutionModel::MeshNV,
2788 spv::ExecutionModel::MeshEXT}) {
2789 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2790 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
2791 this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4274 : 4406),
2792 "Vulkan spec doesn't allow BuiltIn Layer and "
2793 "ViewportIndex to be "
2794 "used for variables with Input storage class if "
2795 "execution model is Vertex, TessellationEvaluation, "
2796 "Geometry, MeshNV or MeshEXT.",
2797 em, decoration, built_in_inst, referenced_from_inst,
2798 std::placeholders::_1));
2799 }
2800 }
2801
2802 if (storage_class == spv::StorageClass::Output) {
2803 assert(function_id_ == 0);
2804 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2805 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
2806 this, ((spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4275 : 4407),
2807 "Vulkan spec doesn't allow BuiltIn Layer and "
2808 "ViewportIndex to be "
2809 "used for variables with Output storage class if "
2810 "execution model is "
2811 "Fragment.",
2812 spv::ExecutionModel::Fragment, decoration, built_in_inst,
2813 referenced_from_inst, std::placeholders::_1));
2814 }
2815
2816 for (const spv::ExecutionModel execution_model : execution_models_) {
2817 switch (execution_model) {
2818 case spv::ExecutionModel::Geometry:
2819 case spv::ExecutionModel::Fragment:
2820 case spv::ExecutionModel::MeshNV:
2821 case spv::ExecutionModel::MeshEXT:
2822 // Ok.
2823 break;
2824 case spv::ExecutionModel::Vertex:
2825 case spv::ExecutionModel::TessellationEvaluation: {
2826 if (!_.HasCapability(spv::Capability::ShaderViewportIndexLayerEXT)) {
2827 if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex &&
2828 _.HasCapability(spv::Capability::ShaderViewportIndex))
2829 break; // Ok
2830 if (spv::BuiltIn(operand) == spv::BuiltIn::Layer &&
2831 _.HasCapability(spv::Capability::ShaderLayer))
2832 break; // Ok
2833
2834 const char* capability = "ShaderViewportIndexLayerEXT";
2835
2836 if (spv::BuiltIn(operand) == spv::BuiltIn::ViewportIndex)
2837 capability = "ShaderViewportIndexLayerEXT or ShaderViewportIndex";
2838 if (spv::BuiltIn(operand) == spv::BuiltIn::Layer)
2839 capability = "ShaderViewportIndexLayerEXT or ShaderLayer";
2840
2841 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4273 : 4405;
2842 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2843 << _.VkErrorID(vuid) << "Using BuiltIn "
2844 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2845 operand)
2846 << " in Vertex or Tessellation execution model requires the "
2847 << capability << " capability.";
2848 }
2849 break;
2850 }
2851 default: {
2852 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::Layer) ? 4272 : 4404;
2853 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2854 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
2855 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2856 operand)
2857 << " to be used only with Vertex, TessellationEvaluation, "
2858 "Geometry, or Fragment execution models. "
2859 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2860 referenced_from_inst, execution_model);
2861 }
2862 }
2863 }
2864 }
2865
2866 if (function_id_ == 0) {
2867 // Propagate this rule to all dependant ids in the global scope.
2868 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2869 std::bind(&BuiltInsValidator::ValidateLayerOrViewportIndexAtReference,
2870 this, decoration, built_in_inst, referenced_from_inst,
2871 std::placeholders::_1));
2872 }
2873
2874 return SPV_SUCCESS;
2875 }
2876
ValidateFragmentShaderF32Vec3InputAtDefinition(const Decoration & decoration,const Instruction & inst)2877 spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition(
2878 const Decoration& decoration, const Instruction& inst) {
2879 if (spvIsVulkanEnv(_.context()->target_env)) {
2880 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2881 if (spv_result_t error = ValidateF32Vec(
2882 decoration, inst, 3,
2883 [this, &inst, builtin](const std::string& message) -> spv_result_t {
2884 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
2885 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2886 << _.VkErrorID(vuid) << "According to the "
2887 << spvLogStringForEnv(_.context()->target_env)
2888 << " spec BuiltIn "
2889 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2890 uint32_t(builtin))
2891 << " variable needs to be a 3-component 32-bit float "
2892 "vector. "
2893 << message;
2894 })) {
2895 return error;
2896 }
2897 }
2898
2899 // Seed at reference checks with this built-in.
2900 return ValidateFragmentShaderF32Vec3InputAtReference(decoration, inst, inst,
2901 inst);
2902 }
2903
ValidateFragmentShaderF32Vec3InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2904 spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference(
2905 const Decoration& decoration, const Instruction& built_in_inst,
2906 const Instruction& referenced_inst,
2907 const Instruction& referenced_from_inst) {
2908
2909 if (spvIsVulkanEnv(_.context()->target_env)) {
2910 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2911 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2912 if (storage_class != spv::StorageClass::Max &&
2913 storage_class != spv::StorageClass::Input) {
2914 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
2915 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2916 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
2917 << " spec allows BuiltIn "
2918 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2919 << " to be only used for variables with Input storage class. "
2920 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2921 referenced_from_inst)
2922 << " " << GetStorageClassDesc(referenced_from_inst);
2923 }
2924
2925 for (const spv::ExecutionModel execution_model : execution_models_) {
2926 if (execution_model != spv::ExecutionModel::Fragment) {
2927 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
2928 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2929 << _.VkErrorID(vuid)
2930 << spvLogStringForEnv(_.context()->target_env)
2931 << " spec allows BuiltIn "
2932 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2933 << " to be used only with Fragment execution model. "
2934 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2935 referenced_from_inst, execution_model);
2936 }
2937 }
2938 }
2939
2940 if (function_id_ == 0) {
2941 // Propagate this rule to all dependant ids in the global scope.
2942 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2943 &BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference, this,
2944 decoration, built_in_inst, referenced_from_inst,
2945 std::placeholders::_1));
2946 }
2947
2948 return SPV_SUCCESS;
2949 }
2950
ValidateComputeShaderI32Vec3InputAtDefinition(const Decoration & decoration,const Instruction & inst)2951 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
2952 const Decoration& decoration, const Instruction& inst) {
2953 if (spvIsVulkanEnv(_.context()->target_env)) {
2954 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2955 if (spv_result_t error = ValidateI32Vec(
2956 decoration, inst, 3,
2957 [this, &inst, builtin](const std::string& message) -> spv_result_t {
2958 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
2959 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2960 << _.VkErrorID(vuid) << "According to the "
2961 << spvLogStringForEnv(_.context()->target_env)
2962 << " spec BuiltIn "
2963 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2964 uint32_t(builtin))
2965 << " variable needs to be a 3-component 32-bit int "
2966 "vector. "
2967 << message;
2968 })) {
2969 return error;
2970 }
2971 }
2972
2973 // Seed at reference checks with this built-in.
2974 return ValidateComputeShaderI32Vec3InputAtReference(decoration, inst, inst,
2975 inst);
2976 }
2977
ValidateComputeShaderI32Vec3InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2978 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
2979 const Decoration& decoration, const Instruction& built_in_inst,
2980 const Instruction& referenced_inst,
2981 const Instruction& referenced_from_inst) {
2982 if (spvIsVulkanEnv(_.context()->target_env)) {
2983 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
2984 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
2985 if (storage_class != spv::StorageClass::Max &&
2986 storage_class != spv::StorageClass::Input) {
2987 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
2988 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2989 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
2990 << " spec allows BuiltIn "
2991 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
2992 << " to be only used for variables with Input storage class. "
2993 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2994 referenced_from_inst)
2995 << " " << GetStorageClassDesc(referenced_from_inst);
2996 }
2997
2998 for (const spv::ExecutionModel execution_model : execution_models_) {
2999 bool has_vulkan_model = execution_model == spv::ExecutionModel::GLCompute ||
3000 execution_model == spv::ExecutionModel::TaskNV ||
3001 execution_model == spv::ExecutionModel::MeshNV ||
3002 execution_model == spv::ExecutionModel::TaskEXT ||
3003 execution_model == spv::ExecutionModel::MeshEXT;
3004
3005 if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
3006 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3007 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3008 << _.VkErrorID(vuid)
3009 << spvLogStringForEnv(_.context()->target_env)
3010 << " spec allows BuiltIn "
3011 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3012 << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or"
3013 << " TaskEXT execution model. "
3014 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3015 referenced_from_inst, execution_model);
3016 }
3017 }
3018 }
3019
3020 if (function_id_ == 0) {
3021 // Propagate this rule to all dependant ids in the global scope.
3022 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3023 &BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference, this,
3024 decoration, built_in_inst, referenced_from_inst,
3025 std::placeholders::_1));
3026 }
3027
3028 return SPV_SUCCESS;
3029 }
3030
ValidateComputeI32InputAtDefinition(const Decoration & decoration,const Instruction & inst)3031 spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition(
3032 const Decoration& decoration, const Instruction& inst) {
3033 if (spvIsVulkanEnv(_.context()->target_env)) {
3034 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3035 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
3036 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3037 << "BuiltIn "
3038 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3039 << " cannot be used as a member decoration ";
3040 }
3041 if (spv_result_t error = ValidateI32(
3042 decoration, inst,
3043 [this, &inst, builtin](const std::string& message) -> spv_result_t {
3044 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3045 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3046 << _.VkErrorID(vuid)
3047 << "According to the "
3048 << spvLogStringForEnv(_.context()->target_env)
3049 << " spec BuiltIn "
3050 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3051 << " variable needs to be a 32-bit int "
3052 "vector. "
3053 << message;
3054 })) {
3055 return error;
3056 }
3057 }
3058
3059 // Seed at reference checks with this built-in.
3060 return ValidateComputeI32InputAtReference(decoration, inst, inst, inst);
3061 }
3062
ValidateComputeI32InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3063 spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference(
3064 const Decoration& decoration, const Instruction& built_in_inst,
3065 const Instruction& referenced_inst,
3066 const Instruction& referenced_from_inst) {
3067 if (spvIsVulkanEnv(_.context()->target_env)) {
3068 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3069 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3070 if (storage_class != spv::StorageClass::Max &&
3071 storage_class != spv::StorageClass::Input) {
3072 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3073 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3074 << _.VkErrorID(vuid)
3075 << spvLogStringForEnv(_.context()->target_env)
3076 << " spec allows BuiltIn "
3077 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3078 << " to be only used for variables with Input storage class. "
3079 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3080 referenced_from_inst)
3081 << " " << GetStorageClassDesc(referenced_from_inst);
3082 }
3083
3084 for (const spv::ExecutionModel execution_model : execution_models_) {
3085 bool has_vulkan_model = execution_model == spv::ExecutionModel::GLCompute ||
3086 execution_model == spv::ExecutionModel::TaskNV ||
3087 execution_model == spv::ExecutionModel::MeshNV ||
3088 execution_model == spv::ExecutionModel::TaskEXT ||
3089 execution_model == spv::ExecutionModel::MeshEXT;
3090 if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
3091 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3092 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3093 << _.VkErrorID(vuid)
3094 << spvLogStringForEnv(_.context()->target_env)
3095 << " spec allows BuiltIn "
3096 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3097 << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or "
3098 << "TaskEXT execution model. "
3099 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3100 referenced_from_inst, execution_model);
3101 }
3102 }
3103 }
3104
3105 if (function_id_ == 0) {
3106 // Propagate this rule to all dependant ids in the global scope.
3107 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3108 std::bind(&BuiltInsValidator::ValidateComputeI32InputAtReference, this,
3109 decoration, built_in_inst, referenced_from_inst,
3110 std::placeholders::_1));
3111 }
3112
3113 return SPV_SUCCESS;
3114 }
3115
ValidateI32InputAtDefinition(const Decoration & decoration,const Instruction & inst)3116 spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition(
3117 const Decoration& decoration, const Instruction& inst) {
3118 if (spvIsVulkanEnv(_.context()->target_env)) {
3119 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3120 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
3121 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3122 << "BuiltIn "
3123 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3124 << " cannot be used as a member decoration ";
3125 }
3126 if (spv_result_t error = ValidateI32(
3127 decoration, inst,
3128 [this, &inst, builtin](const std::string& message) -> spv_result_t {
3129 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3130 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3131 << _.VkErrorID(vuid)
3132 << "According to the "
3133 << spvLogStringForEnv(_.context()->target_env)
3134 << " spec BuiltIn "
3135 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3136 << " variable needs to be a 32-bit int. " << message;
3137 })) {
3138 return error;
3139 }
3140
3141 const spv::StorageClass storage_class = GetStorageClass(inst);
3142 if (storage_class != spv::StorageClass::Max &&
3143 storage_class != spv::StorageClass::Input) {
3144 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3145 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3146 << _.VkErrorID(vuid)
3147 << spvLogStringForEnv(_.context()->target_env)
3148 << " spec allows BuiltIn "
3149 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3150 << " to be only used for variables with Input storage class. "
3151 << GetReferenceDesc(decoration, inst, inst, inst) << " "
3152 << GetStorageClassDesc(inst);
3153 }
3154 }
3155
3156 return SPV_SUCCESS;
3157 }
3158
ValidateI32Vec4InputAtDefinition(const Decoration & decoration,const Instruction & inst)3159 spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition(
3160 const Decoration& decoration, const Instruction& inst) {
3161 if (spvIsVulkanEnv(_.context()->target_env)) {
3162 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3163 if (decoration.struct_member_index() != Decoration::kInvalidMember) {
3164 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3165 << "BuiltIn "
3166 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3167 << " cannot be used as a member decoration ";
3168 }
3169 if (spv_result_t error = ValidateI32Vec(
3170 decoration, inst, 4,
3171 [this, &inst, builtin](const std::string& message) -> spv_result_t {
3172 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3173 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3174 << _.VkErrorID(vuid)
3175 << "According to the "
3176 << spvLogStringForEnv(_.context()->target_env)
3177 << " spec BuiltIn "
3178 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3179 << " variable needs to be a 4-component 32-bit int "
3180 "vector. "
3181 << message;
3182 })) {
3183 return error;
3184 }
3185
3186 const spv::StorageClass storage_class = GetStorageClass(inst);
3187 if (storage_class != spv::StorageClass::Max &&
3188 storage_class != spv::StorageClass::Input) {
3189 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3190 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3191 << _.VkErrorID(vuid)
3192 << spvLogStringForEnv(_.context()->target_env)
3193 << " spec allows BuiltIn "
3194 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3195 << " to be only used for variables with Input storage class. "
3196 << GetReferenceDesc(decoration, inst, inst, inst) << " "
3197 << GetStorageClassDesc(inst);
3198 }
3199 }
3200
3201 return SPV_SUCCESS;
3202 }
3203
ValidateWorkgroupSizeAtDefinition(const Decoration & decoration,const Instruction & inst)3204 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition(
3205 const Decoration& decoration, const Instruction& inst) {
3206 if (spvIsVulkanEnv(_.context()->target_env)) {
3207 if (spvIsVulkanEnv(_.context()->target_env) &&
3208 !spvOpcodeIsConstant(inst.opcode())) {
3209 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3210 << _.VkErrorID(4426)
3211 << "Vulkan spec requires BuiltIn WorkgroupSize to be a "
3212 "constant. "
3213 << GetIdDesc(inst) << " is not a constant.";
3214 }
3215
3216 if (spv_result_t error = ValidateI32Vec(
3217 decoration, inst, 3,
3218 [this, &inst](const std::string& message) -> spv_result_t {
3219 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3220 << _.VkErrorID(4427) << "According to the "
3221 << spvLogStringForEnv(_.context()->target_env)
3222 << " spec BuiltIn WorkgroupSize variable needs to be a "
3223 "3-component 32-bit int vector. "
3224 << message;
3225 })) {
3226 return error;
3227 }
3228 }
3229
3230 // Seed at reference checks with this built-in.
3231 return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst);
3232 }
3233
ValidateWorkgroupSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3234 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
3235 const Decoration& decoration, const Instruction& built_in_inst,
3236 const Instruction& referenced_inst,
3237 const Instruction& referenced_from_inst) {
3238 if (spvIsVulkanEnv(_.context()->target_env)) {
3239 for (const spv::ExecutionModel execution_model : execution_models_) {
3240 if (execution_model != spv::ExecutionModel::GLCompute &&
3241 execution_model != spv::ExecutionModel::TaskNV &&
3242 execution_model != spv::ExecutionModel::MeshNV &&
3243 execution_model != spv::ExecutionModel::TaskEXT &&
3244 execution_model != spv::ExecutionModel::MeshEXT) {
3245 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3246 << _.VkErrorID(4425)
3247 << spvLogStringForEnv(_.context()->target_env)
3248 << " spec allows BuiltIn "
3249 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3250 decoration.params()[0])
3251 << " to be used only with GLCompute, MeshNV, TaskNV, MeshEXT or "
3252 << "TaskEXT execution model. "
3253 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3254 referenced_from_inst, execution_model);
3255 }
3256 }
3257 }
3258
3259 if (function_id_ == 0) {
3260 // Propagate this rule to all dependant ids in the global scope.
3261 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3262 &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration,
3263 built_in_inst, referenced_from_inst, std::placeholders::_1));
3264 }
3265
3266 return SPV_SUCCESS;
3267 }
3268
ValidateBaseInstanceOrVertexAtDefinition(const Decoration & decoration,const Instruction & inst)3269 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtDefinition(
3270 const Decoration& decoration, const Instruction& inst) {
3271 if (spvIsVulkanEnv(_.context()->target_env)) {
3272 if (spv_result_t error = ValidateI32(
3273 decoration, inst,
3274 [this, &inst,
3275 &decoration](const std::string& message) -> spv_result_t {
3276 uint32_t vuid = (spv::BuiltIn(decoration.params()[0]) == spv::BuiltIn::BaseInstance)
3277 ? 4183
3278 : 4186;
3279 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3280 << _.VkErrorID(vuid)
3281 << "According to the Vulkan spec BuiltIn "
3282 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3283 decoration.params()[0])
3284 << " variable needs to be a 32-bit int scalar. "
3285 << message;
3286 })) {
3287 return error;
3288 }
3289 }
3290
3291 return ValidateBaseInstanceOrVertexAtReference(decoration, inst, inst, inst);
3292 }
3293
ValidateBaseInstanceOrVertexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3294 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference(
3295 const Decoration& decoration, const Instruction& built_in_inst,
3296 const Instruction& referenced_inst,
3297 const Instruction& referenced_from_inst) {
3298 uint32_t operand = decoration.params()[0];
3299 if (spvIsVulkanEnv(_.context()->target_env)) {
3300 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3301 if (storage_class != spv::StorageClass::Max &&
3302 storage_class != spv::StorageClass::Input) {
3303 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4182 : 4185;
3304 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3305 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3306 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3307 operand)
3308 << " to be only used for variables with Input storage class. "
3309 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3310 referenced_from_inst)
3311 << " " << GetStorageClassDesc(referenced_from_inst);
3312 }
3313
3314 for (const spv::ExecutionModel execution_model : execution_models_) {
3315 if (execution_model != spv::ExecutionModel::Vertex) {
3316 uint32_t vuid = (spv::BuiltIn(operand) == spv::BuiltIn::BaseInstance) ? 4181 : 4184;
3317 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3318 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3319 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3320 operand)
3321 << " to be used only with Vertex execution model. "
3322 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3323 referenced_from_inst, execution_model);
3324 }
3325 }
3326 }
3327
3328 if (function_id_ == 0) {
3329 // Propagate this rule to all dependant ids in the global scope.
3330 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3331 std::bind(&BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference,
3332 this, decoration, built_in_inst, referenced_from_inst,
3333 std::placeholders::_1));
3334 }
3335
3336 return SPV_SUCCESS;
3337 }
3338
ValidateDrawIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3339 spv_result_t BuiltInsValidator::ValidateDrawIndexAtDefinition(
3340 const Decoration& decoration, const Instruction& inst) {
3341 if (spvIsVulkanEnv(_.context()->target_env)) {
3342 if (spv_result_t error = ValidateI32(
3343 decoration, inst,
3344 [this, &inst,
3345 &decoration](const std::string& message) -> spv_result_t {
3346 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3347 << _.VkErrorID(4209)
3348 << "According to the Vulkan spec BuiltIn "
3349 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3350 decoration.params()[0])
3351 << " variable needs to be a 32-bit int scalar. "
3352 << message;
3353 })) {
3354 return error;
3355 }
3356 }
3357
3358 return ValidateDrawIndexAtReference(decoration, inst, inst, inst);
3359 }
3360
ValidateDrawIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3361 spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference(
3362 const Decoration& decoration, const Instruction& built_in_inst,
3363 const Instruction& referenced_inst,
3364 const Instruction& referenced_from_inst) {
3365 uint32_t operand = decoration.params()[0];
3366 if (spvIsVulkanEnv(_.context()->target_env)) {
3367 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3368 if (storage_class != spv::StorageClass::Max &&
3369 storage_class != spv::StorageClass::Input) {
3370 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3371 << _.VkErrorID(4208) << "Vulkan spec allows BuiltIn "
3372 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3373 operand)
3374 << " to be only used for variables with Input storage class. "
3375 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3376 referenced_from_inst)
3377 << " " << GetStorageClassDesc(referenced_from_inst);
3378 }
3379
3380 for (const spv::ExecutionModel execution_model : execution_models_) {
3381 if (execution_model != spv::ExecutionModel::Vertex &&
3382 execution_model != spv::ExecutionModel::MeshNV &&
3383 execution_model != spv::ExecutionModel::TaskNV &&
3384 execution_model != spv::ExecutionModel::MeshEXT &&
3385 execution_model != spv::ExecutionModel::TaskEXT) {
3386 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3387 << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn "
3388 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3389 operand)
3390 << " to be used only with Vertex, MeshNV, TaskNV , MeshEXT or"
3391 << " TaskEXT execution "
3392 "model. "
3393 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3394 referenced_from_inst, execution_model);
3395 }
3396 }
3397 }
3398
3399 if (function_id_ == 0) {
3400 // Propagate this rule to all dependant ids in the global scope.
3401 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3402 &BuiltInsValidator::ValidateDrawIndexAtReference, this, decoration,
3403 built_in_inst, referenced_from_inst, std::placeholders::_1));
3404 }
3405
3406 return SPV_SUCCESS;
3407 }
3408
ValidateViewIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3409 spv_result_t BuiltInsValidator::ValidateViewIndexAtDefinition(
3410 const Decoration& decoration, const Instruction& inst) {
3411 if (spvIsVulkanEnv(_.context()->target_env)) {
3412 if (spv_result_t error = ValidateI32(
3413 decoration, inst,
3414 [this, &inst,
3415 &decoration](const std::string& message) -> spv_result_t {
3416 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3417 << _.VkErrorID(4403)
3418 << "According to the Vulkan spec BuiltIn "
3419 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3420 decoration.params()[0])
3421 << " variable needs to be a 32-bit int scalar. "
3422 << message;
3423 })) {
3424 return error;
3425 }
3426 }
3427
3428 return ValidateViewIndexAtReference(decoration, inst, inst, inst);
3429 }
3430
ValidateViewIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3431 spv_result_t BuiltInsValidator::ValidateViewIndexAtReference(
3432 const Decoration& decoration, const Instruction& built_in_inst,
3433 const Instruction& referenced_inst,
3434 const Instruction& referenced_from_inst) {
3435 uint32_t operand = decoration.params()[0];
3436 if (spvIsVulkanEnv(_.context()->target_env)) {
3437 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3438 if (storage_class != spv::StorageClass::Max &&
3439 storage_class != spv::StorageClass::Input) {
3440 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3441 << _.VkErrorID(4402) << "Vulkan spec allows BuiltIn "
3442 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3443 operand)
3444 << " to be only used for variables with Input storage class. "
3445 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3446 referenced_from_inst)
3447 << " " << GetStorageClassDesc(referenced_from_inst);
3448 }
3449
3450 for (const spv::ExecutionModel execution_model : execution_models_) {
3451 if (execution_model == spv::ExecutionModel::GLCompute) {
3452 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3453 << _.VkErrorID(4401) << "Vulkan spec allows BuiltIn "
3454 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3455 operand)
3456 << " to be not be used with GLCompute execution model. "
3457 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3458 referenced_from_inst, execution_model);
3459 }
3460 }
3461 }
3462
3463 if (function_id_ == 0) {
3464 // Propagate this rule to all dependant ids in the global scope.
3465 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3466 &BuiltInsValidator::ValidateViewIndexAtReference, this, decoration,
3467 built_in_inst, referenced_from_inst, std::placeholders::_1));
3468 }
3469
3470 return SPV_SUCCESS;
3471 }
3472
ValidateDeviceIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3473 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtDefinition(
3474 const Decoration& decoration, const Instruction& inst) {
3475 if (spvIsVulkanEnv(_.context()->target_env)) {
3476 if (spv_result_t error = ValidateI32(
3477 decoration, inst,
3478 [this, &inst,
3479 &decoration](const std::string& message) -> spv_result_t {
3480 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3481 << _.VkErrorID(4206)
3482 << "According to the Vulkan spec BuiltIn "
3483 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3484 decoration.params()[0])
3485 << " variable needs to be a 32-bit int scalar. "
3486 << message;
3487 })) {
3488 return error;
3489 }
3490 }
3491
3492 return ValidateDeviceIndexAtReference(decoration, inst, inst, inst);
3493 }
3494
ValidateDeviceIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3495 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtReference(
3496 const Decoration& decoration, const Instruction& built_in_inst,
3497 const Instruction& referenced_inst,
3498 const Instruction& referenced_from_inst) {
3499 uint32_t operand = decoration.params()[0];
3500 if (spvIsVulkanEnv(_.context()->target_env)) {
3501 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3502 if (storage_class != spv::StorageClass::Max &&
3503 storage_class != spv::StorageClass::Input) {
3504 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3505 << _.VkErrorID(4205) << "Vulkan spec allows BuiltIn "
3506 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3507 operand)
3508 << " to be only used for variables with Input storage class. "
3509 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3510 referenced_from_inst)
3511 << " " << GetStorageClassDesc(referenced_from_inst);
3512 }
3513 }
3514
3515 if (function_id_ == 0) {
3516 // Propagate this rule to all dependant ids in the global scope.
3517 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3518 &BuiltInsValidator::ValidateDeviceIndexAtReference, this, decoration,
3519 built_in_inst, referenced_from_inst, std::placeholders::_1));
3520 }
3521
3522 return SPV_SUCCESS;
3523 }
3524
ValidateFragInvocationCountAtDefinition(const Decoration & decoration,const Instruction & inst)3525 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
3526 const Instruction& inst) {
3527
3528 if (spvIsVulkanEnv(_.context()->target_env)) {
3529 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3530 if (spv_result_t error = ValidateI32(
3531 decoration, inst,
3532 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3533 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3534 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3535 << _.VkErrorID(vuid) << "According to the "
3536 << spvLogStringForEnv(_.context()->target_env)
3537 << " spec BuiltIn "
3538 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3539 uint32_t(builtin))
3540 << " variable needs to be a 32-bit int scalar. "
3541 << message;
3542 })) {
3543 return error;
3544 }
3545 }
3546
3547 return ValidateFragInvocationCountAtReference(decoration, inst, inst, inst);
3548 }
3549
ValidateFragInvocationCountAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3550 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtReference(
3551 const Decoration& decoration, const Instruction& built_in_inst,
3552 const Instruction& referenced_inst,
3553 const Instruction& referenced_from_inst) {
3554
3555 if (spvIsVulkanEnv(_.context()->target_env)) {
3556 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3557 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3558 if (storage_class != spv::StorageClass::Max &&
3559 storage_class != spv::StorageClass::Input) {
3560 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3561 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3562 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3563 << " spec allows BuiltIn "
3564 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3565 << " to be only used for variables with Input storage class. "
3566 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3567 referenced_from_inst)
3568 << " " << GetStorageClassDesc(referenced_from_inst);
3569 }
3570
3571 for (const spv::ExecutionModel execution_model : execution_models_) {
3572 if (execution_model != spv::ExecutionModel::Fragment) {
3573 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3574 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3575 << _.VkErrorID(vuid)
3576 << spvLogStringForEnv(_.context()->target_env)
3577 << " spec allows BuiltIn "
3578 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3579 << " to be used only with Fragment execution model. "
3580 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3581 referenced_from_inst, execution_model);
3582 }
3583 }
3584 }
3585
3586 if (function_id_ == 0) {
3587 // Propagate this rule to all dependant ids in the global scope.
3588 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3589 &BuiltInsValidator::ValidateFragInvocationCountAtReference, this, decoration,
3590 built_in_inst, referenced_from_inst, std::placeholders::_1));
3591 }
3592
3593 return SPV_SUCCESS;
3594 }
3595
ValidateFragSizeAtDefinition(const Decoration & decoration,const Instruction & inst)3596 spv_result_t BuiltInsValidator::ValidateFragSizeAtDefinition(const Decoration& decoration,
3597 const Instruction& inst) {
3598 if (spvIsVulkanEnv(_.context()->target_env)) {
3599 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3600 if (spv_result_t error = ValidateI32Vec(
3601 decoration, inst, 2,
3602 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3603 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3604 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3605 << _.VkErrorID(vuid) << "According to the "
3606 << spvLogStringForEnv(_.context()->target_env)
3607 << " spec BuiltIn "
3608 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3609 uint32_t(builtin))
3610 << " variable needs to be a 2-component 32-bit int vector. "
3611 << message;
3612 })) {
3613 return error;
3614 }
3615 }
3616
3617 return ValidateFragSizeAtReference(decoration, inst, inst, inst);
3618 }
3619
ValidateFragSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3620 spv_result_t BuiltInsValidator::ValidateFragSizeAtReference(
3621 const Decoration& decoration, const Instruction& built_in_inst,
3622 const Instruction& referenced_inst,
3623 const Instruction& referenced_from_inst) {
3624
3625 if (spvIsVulkanEnv(_.context()->target_env)) {
3626 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3627 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3628 if (storage_class != spv::StorageClass::Max &&
3629 storage_class != spv::StorageClass::Input) {
3630 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3631 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3632 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3633 << " spec allows BuiltIn "
3634 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3635 << " to be only used for variables with Input storage class. "
3636 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3637 referenced_from_inst)
3638 << " " << GetStorageClassDesc(referenced_from_inst);
3639 }
3640
3641 for (const spv::ExecutionModel execution_model : execution_models_) {
3642 if (execution_model != spv::ExecutionModel::Fragment) {
3643 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3644 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3645 << _.VkErrorID(vuid)
3646 << spvLogStringForEnv(_.context()->target_env)
3647 << " spec allows BuiltIn "
3648 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3649 << " to be used only with Fragment execution model. "
3650 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3651 referenced_from_inst, execution_model);
3652 }
3653 }
3654 }
3655
3656 if (function_id_ == 0) {
3657 // Propagate this rule to all dependant ids in the global scope.
3658 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3659 &BuiltInsValidator::ValidateFragSizeAtReference, this, decoration,
3660 built_in_inst, referenced_from_inst, std::placeholders::_1));
3661 }
3662
3663 return SPV_SUCCESS;
3664 }
3665
ValidateFragStencilRefAtDefinition(const Decoration & decoration,const Instruction & inst)3666 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtDefinition(const Decoration& decoration,
3667 const Instruction& inst) {
3668 if (spvIsVulkanEnv(_.context()->target_env)) {
3669 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3670 if (spv_result_t error = ValidateI(
3671 decoration, inst,
3672 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3673 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3674 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3675 << _.VkErrorID(vuid) << "According to the "
3676 << spvLogStringForEnv(_.context()->target_env)
3677 << " spec BuiltIn "
3678 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3679 uint32_t(builtin))
3680 << " variable needs to be a int scalar. "
3681 << message;
3682 })) {
3683 return error;
3684 }
3685 }
3686
3687 return ValidateFragStencilRefAtReference(decoration, inst, inst, inst);
3688 }
3689
ValidateFragStencilRefAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3690 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtReference(
3691 const Decoration& decoration, const Instruction& built_in_inst,
3692 const Instruction& referenced_inst,
3693 const Instruction& referenced_from_inst) {
3694
3695 if (spvIsVulkanEnv(_.context()->target_env)) {
3696 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3697 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3698 if (storage_class != spv::StorageClass::Max &&
3699 storage_class != spv::StorageClass::Output) {
3700 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3701 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3702 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3703 << " spec allows BuiltIn "
3704 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3705 << " to be only used for variables with Output storage class. "
3706 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3707 referenced_from_inst)
3708 << " " << GetStorageClassDesc(referenced_from_inst);
3709 }
3710
3711 for (const spv::ExecutionModel execution_model : execution_models_) {
3712 if (execution_model != spv::ExecutionModel::Fragment) {
3713 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3714 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3715 << _.VkErrorID(vuid)
3716 << spvLogStringForEnv(_.context()->target_env)
3717 << " spec allows BuiltIn "
3718 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3719 << " to be used only with Fragment execution model. "
3720 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3721 referenced_from_inst, execution_model);
3722 }
3723 }
3724 }
3725
3726 if (function_id_ == 0) {
3727 // Propagate this rule to all dependant ids in the global scope.
3728 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3729 &BuiltInsValidator::ValidateFragStencilRefAtReference, this, decoration,
3730 built_in_inst, referenced_from_inst, std::placeholders::_1));
3731 }
3732
3733 return SPV_SUCCESS;
3734 }
3735
ValidateFullyCoveredAtDefinition(const Decoration & decoration,const Instruction & inst)3736 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtDefinition(const Decoration& decoration,
3737 const Instruction& inst) {
3738 if (spvIsVulkanEnv(_.context()->target_env)) {
3739 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3740 if (spv_result_t error = ValidateBool(
3741 decoration, inst,
3742 [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3743 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3744 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3745 << _.VkErrorID(vuid) << "According to the "
3746 << spvLogStringForEnv(_.context()->target_env)
3747 << " spec BuiltIn "
3748 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3749 uint32_t(builtin))
3750 << " variable needs to be a bool scalar. "
3751 << message;
3752 })) {
3753 return error;
3754 }
3755 }
3756
3757 return ValidateFullyCoveredAtReference(decoration, inst, inst, inst);
3758 }
3759
ValidateFullyCoveredAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3760 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtReference(
3761 const Decoration& decoration, const Instruction& built_in_inst,
3762 const Instruction& referenced_inst,
3763 const Instruction& referenced_from_inst) {
3764
3765 if (spvIsVulkanEnv(_.context()->target_env)) {
3766 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
3767 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3768 if (storage_class != spv::StorageClass::Max &&
3769 storage_class != spv::StorageClass::Input) {
3770 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3771 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3772 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3773 << " spec allows BuiltIn "
3774 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3775 << " to be only used for variables with Input storage class. "
3776 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3777 referenced_from_inst)
3778 << " " << GetStorageClassDesc(referenced_from_inst);
3779 }
3780
3781 for (const spv::ExecutionModel execution_model : execution_models_) {
3782 if (execution_model != spv::ExecutionModel::Fragment) {
3783 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3784 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3785 << _.VkErrorID(vuid)
3786 << spvLogStringForEnv(_.context()->target_env)
3787 << " spec allows BuiltIn "
3788 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
3789 << " to be used only with Fragment execution model. "
3790 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3791 referenced_from_inst, execution_model);
3792 }
3793 }
3794 }
3795
3796 if (function_id_ == 0) {
3797 // Propagate this rule to all dependant ids in the global scope.
3798 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3799 &BuiltInsValidator::ValidateFullyCoveredAtReference, this, decoration,
3800 built_in_inst, referenced_from_inst, std::placeholders::_1));
3801 }
3802
3803 return SPV_SUCCESS;
3804 }
3805
ValidateNVSMOrARMCoreBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3806 spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtDefinition(
3807 const Decoration& decoration, const Instruction& inst) {
3808 if (spvIsVulkanEnv(_.context()->target_env)) {
3809 if (spv_result_t error = ValidateI32(
3810 decoration, inst,
3811 [this, &inst,
3812 &decoration](const std::string& message) -> spv_result_t {
3813 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3814 << "According to the "
3815 << spvLogStringForEnv(_.context()->target_env)
3816 << " spec BuiltIn "
3817 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3818 decoration.params()[0])
3819 << " variable needs to be a 32-bit int scalar. "
3820 << message;
3821 })) {
3822 return error;
3823 }
3824 }
3825
3826 // Seed at reference checks with this built-in.
3827 return ValidateNVSMOrARMCoreBuiltinsAtReference(decoration, inst, inst, inst);
3828 }
3829
ValidateNVSMOrARMCoreBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3830 spv_result_t BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference(
3831 const Decoration& decoration, const Instruction& built_in_inst,
3832 const Instruction& referenced_inst,
3833 const Instruction& referenced_from_inst) {
3834 if (spvIsVulkanEnv(_.context()->target_env)) {
3835 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3836 if (storage_class != spv::StorageClass::Max &&
3837 storage_class != spv::StorageClass::Input) {
3838 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3839 << spvLogStringForEnv(_.context()->target_env)
3840 << " spec allows BuiltIn "
3841 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3842 decoration.params()[0])
3843 << " to be only used for "
3844 "variables with Input storage class. "
3845 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3846 referenced_from_inst)
3847 << " " << GetStorageClassDesc(referenced_from_inst);
3848 }
3849 }
3850
3851 if (function_id_ == 0) {
3852 // Propagate this rule to all dependant ids in the global scope.
3853 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3854 &BuiltInsValidator::ValidateNVSMOrARMCoreBuiltinsAtReference, this, decoration,
3855 built_in_inst, referenced_from_inst, std::placeholders::_1));
3856 }
3857
3858 return SPV_SUCCESS;
3859 }
3860
ValidatePrimitiveShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3861 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition(
3862 const Decoration& decoration, const Instruction& inst) {
3863 if (spvIsVulkanEnv(_.context()->target_env)) {
3864 if (spv_result_t error = ValidateI32(
3865 decoration, inst,
3866 [this, &inst,
3867 &decoration](const std::string& message) -> spv_result_t {
3868 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3869 << _.VkErrorID(4486)
3870 << "According to the Vulkan spec BuiltIn "
3871 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3872 decoration.params()[0])
3873 << " variable needs to be a 32-bit int scalar. "
3874 << message;
3875 })) {
3876 return error;
3877 }
3878 }
3879
3880 // Seed at reference checks with this built-in.
3881 return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst);
3882 }
3883
ValidatePrimitiveShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3884 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference(
3885 const Decoration& decoration, const Instruction& built_in_inst,
3886 const Instruction& referenced_inst,
3887 const Instruction& referenced_from_inst) {
3888 if (spvIsVulkanEnv(_.context()->target_env)) {
3889 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3890 if (storage_class != spv::StorageClass::Max &&
3891 storage_class != spv::StorageClass::Output) {
3892 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3893 << _.VkErrorID(4485) << "Vulkan spec allows BuiltIn "
3894 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3895 decoration.params()[0])
3896 << " to be only used for variables with Output storage class. "
3897 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3898 referenced_from_inst)
3899 << " " << GetStorageClassDesc(referenced_from_inst);
3900 }
3901
3902 for (const spv::ExecutionModel execution_model : execution_models_) {
3903 switch (execution_model) {
3904 case spv::ExecutionModel::Vertex:
3905 case spv::ExecutionModel::Geometry:
3906 case spv::ExecutionModel::MeshNV:
3907 case spv::ExecutionModel::MeshEXT:
3908 break;
3909 default: {
3910 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3911 << _.VkErrorID(4484) << "Vulkan spec allows BuiltIn "
3912 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3913 decoration.params()[0])
3914 << " to be used only with Vertex, Geometry, or MeshNV "
3915 "execution models. "
3916 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3917 referenced_from_inst, execution_model);
3918 }
3919 }
3920 }
3921 }
3922
3923 if (function_id_ == 0) {
3924 // Propagate this rule to all dependant ids in the global scope.
3925 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3926 std::bind(&BuiltInsValidator::ValidatePrimitiveShadingRateAtReference,
3927 this, decoration, built_in_inst, referenced_from_inst,
3928 std::placeholders::_1));
3929 }
3930
3931 return SPV_SUCCESS;
3932 }
3933
ValidateShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3934 spv_result_t BuiltInsValidator::ValidateShadingRateAtDefinition(
3935 const Decoration& decoration, const Instruction& inst) {
3936 if (spvIsVulkanEnv(_.context()->target_env)) {
3937 if (spv_result_t error = ValidateI32(
3938 decoration, inst,
3939 [this, &inst,
3940 &decoration](const std::string& message) -> spv_result_t {
3941 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3942 << _.VkErrorID(4492)
3943 << "According to the Vulkan spec BuiltIn "
3944 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3945 decoration.params()[0])
3946 << " variable needs to be a 32-bit int scalar. "
3947 << message;
3948 })) {
3949 return error;
3950 }
3951 }
3952
3953 // Seed at reference checks with this built-in.
3954 return ValidateShadingRateAtReference(decoration, inst, inst, inst);
3955 }
3956
ValidateShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3957 spv_result_t BuiltInsValidator::ValidateShadingRateAtReference(
3958 const Decoration& decoration, const Instruction& built_in_inst,
3959 const Instruction& referenced_inst,
3960 const Instruction& referenced_from_inst) {
3961 if (spvIsVulkanEnv(_.context()->target_env)) {
3962 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
3963 if (storage_class != spv::StorageClass::Max &&
3964 storage_class != spv::StorageClass::Input) {
3965 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3966 << _.VkErrorID(4491) << "Vulkan spec allows BuiltIn "
3967 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3968 decoration.params()[0])
3969 << " to be only used for variables with Input storage class. "
3970 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3971 referenced_from_inst)
3972 << " " << GetStorageClassDesc(referenced_from_inst);
3973 }
3974
3975 for (const spv::ExecutionModel execution_model : execution_models_) {
3976 if (execution_model != spv::ExecutionModel::Fragment) {
3977 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3978 << _.VkErrorID(4490) << "Vulkan spec allows BuiltIn "
3979 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3980 decoration.params()[0])
3981 << " to be used only with the Fragment execution model. "
3982 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3983 referenced_from_inst, execution_model);
3984 }
3985 }
3986 }
3987
3988 if (function_id_ == 0) {
3989 // Propagate this rule to all dependant ids in the global scope.
3990 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3991 &BuiltInsValidator::ValidateShadingRateAtReference, this, decoration,
3992 built_in_inst, referenced_from_inst, std::placeholders::_1));
3993 }
3994
3995 return SPV_SUCCESS;
3996 }
3997
ValidateRayTracingBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3998 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition(
3999 const Decoration& decoration, const Instruction& inst) {
4000 if (spvIsVulkanEnv(_.context()->target_env)) {
4001 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
4002 switch (builtin) {
4003 case spv::BuiltIn::HitTNV:
4004 case spv::BuiltIn::RayTminKHR:
4005 case spv::BuiltIn::RayTmaxKHR:
4006 // f32 scalar
4007 if (spv_result_t error = ValidateF32(
4008 decoration, inst,
4009 [this, &inst,
4010 builtin](const std::string& message) -> spv_result_t {
4011 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4012 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4013 << _.VkErrorID(vuid)
4014 << "According to the Vulkan spec BuiltIn "
4015 << _.grammar().lookupOperandName(
4016 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4017 << " variable needs to be a 32-bit float scalar. "
4018 << message;
4019 })) {
4020 return error;
4021 }
4022 break;
4023 case spv::BuiltIn::HitKindKHR:
4024 case spv::BuiltIn::InstanceCustomIndexKHR:
4025 case spv::BuiltIn::InstanceId:
4026 case spv::BuiltIn::RayGeometryIndexKHR:
4027 case spv::BuiltIn::IncomingRayFlagsKHR:
4028 case spv::BuiltIn::CullMaskKHR:
4029 // i32 scalar
4030 if (spv_result_t error = ValidateI32(
4031 decoration, inst,
4032 [this, &inst,
4033 builtin](const std::string& message) -> spv_result_t {
4034 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4035 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4036 << _.VkErrorID(vuid)
4037 << "According to the Vulkan spec BuiltIn "
4038 << _.grammar().lookupOperandName(
4039 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4040 << " variable needs to be a 32-bit int scalar. "
4041 << message;
4042 })) {
4043 return error;
4044 }
4045 break;
4046 case spv::BuiltIn::ObjectRayDirectionKHR:
4047 case spv::BuiltIn::ObjectRayOriginKHR:
4048 case spv::BuiltIn::WorldRayDirectionKHR:
4049 case spv::BuiltIn::WorldRayOriginKHR:
4050 // f32 vec3
4051 if (spv_result_t error = ValidateF32Vec(
4052 decoration, inst, 3,
4053 [this, &inst,
4054 builtin](const std::string& message) -> spv_result_t {
4055 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4056 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4057 << _.VkErrorID(vuid)
4058 << "According to the Vulkan spec BuiltIn "
4059 << _.grammar().lookupOperandName(
4060 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4061 << " variable needs to be a 3-component 32-bit float "
4062 "vector. "
4063 << message;
4064 })) {
4065 return error;
4066 }
4067 break;
4068 case spv::BuiltIn::LaunchIdKHR:
4069 case spv::BuiltIn::LaunchSizeKHR:
4070 // i32 vec3
4071 if (spv_result_t error = ValidateI32Vec(
4072 decoration, inst, 3,
4073 [this, &inst,
4074 builtin](const std::string& message) -> spv_result_t {
4075 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4076 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4077 << _.VkErrorID(vuid)
4078 << "According to the Vulkan spec BuiltIn "
4079 << _.grammar().lookupOperandName(
4080 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4081 << " variable needs to be a 3-component 32-bit int "
4082 "vector. "
4083 << message;
4084 })) {
4085 return error;
4086 }
4087 break;
4088 case spv::BuiltIn::ObjectToWorldKHR:
4089 case spv::BuiltIn::WorldToObjectKHR:
4090 // f32 mat4x3
4091 if (spv_result_t error = ValidateF32Mat(
4092 decoration, inst, 3, 4,
4093 [this, &inst,
4094 builtin](const std::string& message) -> spv_result_t {
4095 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4096 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4097 << _.VkErrorID(vuid)
4098 << "According to the Vulkan spec BuiltIn "
4099 << _.grammar().lookupOperandName(
4100 SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
4101 << " variable needs to be a matrix with"
4102 << " 4 columns of 3-component vectors of 32-bit "
4103 "floats. "
4104 << message;
4105 })) {
4106 return error;
4107 }
4108 break;
4109 default:
4110 assert(0 && "Unexpected ray tracing builtin");
4111 break;
4112 }
4113 }
4114
4115 // Seed at reference checks with this built-in.
4116 return ValidateRayTracingBuiltinsAtReference(decoration, inst, inst, inst);
4117 }
4118
ValidateRayTracingBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)4119 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference(
4120 const Decoration& decoration, const Instruction& built_in_inst,
4121 const Instruction& referenced_inst,
4122 const Instruction& referenced_from_inst) {
4123 if (spvIsVulkanEnv(_.context()->target_env)) {
4124 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
4125 const spv::StorageClass storage_class = GetStorageClass(referenced_from_inst);
4126 if (storage_class != spv::StorageClass::Max &&
4127 storage_class != spv::StorageClass::Input) {
4128 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
4129 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
4130 << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
4131 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
4132 decoration.params()[0])
4133 << " to be only used for variables with Input storage class. "
4134 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
4135 referenced_from_inst)
4136 << " " << GetStorageClassDesc(referenced_from_inst);
4137 }
4138
4139 for (const spv::ExecutionModel execution_model : execution_models_) {
4140 if (!IsExecutionModelValidForRtBuiltIn(builtin, execution_model)) {
4141 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
4142 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
4143 << _.VkErrorID(vuid) << "Vulkan spec does not allow BuiltIn "
4144 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
4145 decoration.params()[0])
4146 << " to be used with the execution model "
4147 << _.grammar().lookupOperandName(
4148 SPV_OPERAND_TYPE_EXECUTION_MODEL, uint32_t(execution_model))
4149 << ".\n"
4150 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
4151 referenced_from_inst, execution_model);
4152 }
4153 }
4154 }
4155
4156 if (function_id_ == 0) {
4157 // Propagate this rule to all dependant ids in the global scope.
4158 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
4159 std::bind(&BuiltInsValidator::ValidateRayTracingBuiltinsAtReference,
4160 this, decoration, built_in_inst, referenced_from_inst,
4161 std::placeholders::_1));
4162 }
4163
4164 return SPV_SUCCESS;
4165 }
4166
ValidateMeshShadingEXTBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)4167 spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtDefinition(
4168 const Decoration& decoration, const Instruction& inst) {
4169 if (spvIsVulkanEnv(_.context()->target_env)) {
4170 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
4171 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
4172 if (builtin == spv::BuiltIn::PrimitivePointIndicesEXT) {
4173 if (spv_result_t error = ValidateI32Arr(
4174 decoration, inst,
4175 [this, &inst, &decoration,
4176 &vuid](const std::string& message) -> spv_result_t {
4177 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4178 << _.VkErrorID(vuid) << "According to the "
4179 << spvLogStringForEnv(_.context()->target_env)
4180 << " spec BuiltIn "
4181 << _.grammar().lookupOperandName(
4182 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
4183 << " variable needs to be a 32-bit int array."
4184 << message;
4185 })) {
4186 return error;
4187 }
4188 }
4189 if (builtin == spv::BuiltIn::PrimitiveLineIndicesEXT) {
4190 if (spv_result_t error = ValidateArrayedI32Vec(
4191 decoration, inst, 2,
4192 [this, &inst, &decoration,
4193 &vuid](const std::string& message) -> spv_result_t {
4194 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4195 << _.VkErrorID(vuid) << "According to the "
4196 << spvLogStringForEnv(_.context()->target_env)
4197 << " spec BuiltIn "
4198 << _.grammar().lookupOperandName(
4199 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
4200 << " variable needs to be a 2-component 32-bit int "
4201 "array."
4202 << message;
4203 })) {
4204 return error;
4205 }
4206 }
4207 if (builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT) {
4208 if (spv_result_t error = ValidateArrayedI32Vec(
4209 decoration, inst, 3,
4210 [this, &inst, &decoration,
4211 &vuid](const std::string& message) -> spv_result_t {
4212 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
4213 << _.VkErrorID(vuid) << "According to the "
4214 << spvLogStringForEnv(_.context()->target_env)
4215 << " spec BuiltIn "
4216 << _.grammar().lookupOperandName(
4217 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
4218 << " variable needs to be a 3-component 32-bit int "
4219 "array."
4220 << message;
4221 })) {
4222 return error;
4223 }
4224 }
4225 }
4226 // Seed at reference checks with this built-in.
4227 return ValidateMeshShadingEXTBuiltinsAtReference(decoration, inst, inst,
4228 inst);
4229 }
4230
ValidateMeshShadingEXTBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)4231 spv_result_t BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference(
4232 const Decoration& decoration, const Instruction& built_in_inst,
4233 const Instruction& referenced_inst,
4234 const Instruction& referenced_from_inst) {
4235 if (spvIsVulkanEnv(_.context()->target_env)) {
4236 const spv::BuiltIn builtin = spv::BuiltIn(decoration.params()[0]);
4237 const spv::StorageClass storage_class =
4238 GetStorageClass(referenced_from_inst);
4239 if (storage_class != spv::StorageClass::Max &&
4240 storage_class != spv::StorageClass::Output) {
4241 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
4242 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
4243 << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
4244 << " spec allows BuiltIn "
4245 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
4246 uint32_t(builtin))
4247 << " to be only used for variables with Output storage class. "
4248 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
4249 referenced_from_inst)
4250 << " " << GetStorageClassDesc(referenced_from_inst);
4251 }
4252
4253 for (const spv::ExecutionModel execution_model : execution_models_) {
4254 if (execution_model != spv::ExecutionModel::MeshEXT) {
4255 uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
4256 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
4257 << _.VkErrorID(vuid)
4258 << spvLogStringForEnv(_.context()->target_env)
4259 << " spec allows BuiltIn "
4260 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
4261 uint32_t(builtin))
4262 << " to be used only with MeshEXT execution model. "
4263 << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
4264 referenced_from_inst, execution_model);
4265 }
4266 }
4267 }
4268
4269 if (function_id_ == 0) {
4270 // Propagate this rule to all dependant ids in the global scope.
4271 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
4272 std::bind(&BuiltInsValidator::ValidateMeshShadingEXTBuiltinsAtReference,
4273 this, decoration, built_in_inst, referenced_from_inst,
4274 std::placeholders::_1));
4275 }
4276
4277 return SPV_SUCCESS;
4278 }
4279
ValidateSingleBuiltInAtDefinition(const Decoration & decoration,const Instruction & inst)4280 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
4281 const Decoration& decoration, const Instruction& inst) {
4282 const spv::BuiltIn label = spv::BuiltIn(decoration.params()[0]);
4283
4284 if (!spvIsVulkanEnv(_.context()->target_env)) {
4285 // Early return. All currently implemented rules are based on Vulkan spec.
4286 //
4287 // TODO: If you are adding validation rules for environments other than
4288 // Vulkan (or general rules which are not environment independent), then
4289 // you need to modify or remove this condition. Consider also adding early
4290 // returns into BuiltIn-specific rules, so that the system doesn't spawn new
4291 // rules which don't do anything.
4292 return SPV_SUCCESS;
4293 }
4294
4295 // If you are adding a new BuiltIn enum, please register it here.
4296 // If the newly added enum has validation rules associated with it
4297 // consider leaving a TODO and/or creating an issue.
4298 switch (label) {
4299 case spv::BuiltIn::ClipDistance:
4300 case spv::BuiltIn::CullDistance: {
4301 return ValidateClipOrCullDistanceAtDefinition(decoration, inst);
4302 }
4303 case spv::BuiltIn::FragCoord: {
4304 return ValidateFragCoordAtDefinition(decoration, inst);
4305 }
4306 case spv::BuiltIn::FragDepth: {
4307 return ValidateFragDepthAtDefinition(decoration, inst);
4308 }
4309 case spv::BuiltIn::FrontFacing: {
4310 return ValidateFrontFacingAtDefinition(decoration, inst);
4311 }
4312 case spv::BuiltIn::GlobalInvocationId:
4313 case spv::BuiltIn::LocalInvocationId:
4314 case spv::BuiltIn::NumWorkgroups:
4315 case spv::BuiltIn::WorkgroupId: {
4316 return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
4317 }
4318 case spv::BuiltIn::BaryCoordKHR:
4319 case spv::BuiltIn::BaryCoordNoPerspKHR: {
4320 return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst);
4321 }
4322 case spv::BuiltIn::HelperInvocation: {
4323 return ValidateHelperInvocationAtDefinition(decoration, inst);
4324 }
4325 case spv::BuiltIn::InvocationId: {
4326 return ValidateInvocationIdAtDefinition(decoration, inst);
4327 }
4328 case spv::BuiltIn::InstanceIndex: {
4329 return ValidateInstanceIndexAtDefinition(decoration, inst);
4330 }
4331 case spv::BuiltIn::Layer:
4332 case spv::BuiltIn::ViewportIndex: {
4333 return ValidateLayerOrViewportIndexAtDefinition(decoration, inst);
4334 }
4335 case spv::BuiltIn::PatchVertices: {
4336 return ValidatePatchVerticesAtDefinition(decoration, inst);
4337 }
4338 case spv::BuiltIn::PointCoord: {
4339 return ValidatePointCoordAtDefinition(decoration, inst);
4340 }
4341 case spv::BuiltIn::PointSize: {
4342 return ValidatePointSizeAtDefinition(decoration, inst);
4343 }
4344 case spv::BuiltIn::Position: {
4345 return ValidatePositionAtDefinition(decoration, inst);
4346 }
4347 case spv::BuiltIn::PrimitiveId: {
4348 return ValidatePrimitiveIdAtDefinition(decoration, inst);
4349 }
4350 case spv::BuiltIn::SampleId: {
4351 return ValidateSampleIdAtDefinition(decoration, inst);
4352 }
4353 case spv::BuiltIn::SampleMask: {
4354 return ValidateSampleMaskAtDefinition(decoration, inst);
4355 }
4356 case spv::BuiltIn::SamplePosition: {
4357 return ValidateSamplePositionAtDefinition(decoration, inst);
4358 }
4359 case spv::BuiltIn::SubgroupId:
4360 case spv::BuiltIn::NumSubgroups: {
4361 return ValidateComputeI32InputAtDefinition(decoration, inst);
4362 }
4363 case spv::BuiltIn::SubgroupLocalInvocationId:
4364 case spv::BuiltIn::SubgroupSize: {
4365 return ValidateI32InputAtDefinition(decoration, inst);
4366 }
4367 case spv::BuiltIn::SubgroupEqMask:
4368 case spv::BuiltIn::SubgroupGeMask:
4369 case spv::BuiltIn::SubgroupGtMask:
4370 case spv::BuiltIn::SubgroupLeMask:
4371 case spv::BuiltIn::SubgroupLtMask: {
4372 return ValidateI32Vec4InputAtDefinition(decoration, inst);
4373 }
4374 case spv::BuiltIn::TessCoord: {
4375 return ValidateTessCoordAtDefinition(decoration, inst);
4376 }
4377 case spv::BuiltIn::TessLevelOuter: {
4378 return ValidateTessLevelOuterAtDefinition(decoration, inst);
4379 }
4380 case spv::BuiltIn::TessLevelInner: {
4381 return ValidateTessLevelInnerAtDefinition(decoration, inst);
4382 }
4383 case spv::BuiltIn::VertexIndex: {
4384 return ValidateVertexIndexAtDefinition(decoration, inst);
4385 }
4386 case spv::BuiltIn::WorkgroupSize: {
4387 return ValidateWorkgroupSizeAtDefinition(decoration, inst);
4388 }
4389 case spv::BuiltIn::VertexId: {
4390 return ValidateVertexIdAtDefinition(decoration, inst);
4391 }
4392 case spv::BuiltIn::LocalInvocationIndex: {
4393 return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
4394 }
4395 case spv::BuiltIn::CoreIDARM:
4396 case spv::BuiltIn::CoreCountARM:
4397 case spv::BuiltIn::CoreMaxIDARM:
4398 case spv::BuiltIn::WarpIDARM:
4399 case spv::BuiltIn::WarpMaxIDARM:
4400 case spv::BuiltIn::WarpsPerSMNV:
4401 case spv::BuiltIn::SMCountNV:
4402 case spv::BuiltIn::WarpIDNV:
4403 case spv::BuiltIn::SMIDNV: {
4404 return ValidateNVSMOrARMCoreBuiltinsAtDefinition(decoration, inst);
4405 }
4406 case spv::BuiltIn::BaseInstance:
4407 case spv::BuiltIn::BaseVertex: {
4408 return ValidateBaseInstanceOrVertexAtDefinition(decoration, inst);
4409 }
4410 case spv::BuiltIn::DrawIndex: {
4411 return ValidateDrawIndexAtDefinition(decoration, inst);
4412 }
4413 case spv::BuiltIn::ViewIndex: {
4414 return ValidateViewIndexAtDefinition(decoration, inst);
4415 }
4416 case spv::BuiltIn::DeviceIndex: {
4417 return ValidateDeviceIndexAtDefinition(decoration, inst);
4418 }
4419 case spv::BuiltIn::FragInvocationCountEXT: {
4420 // alias spv::BuiltIn::InvocationsPerPixelNV
4421 return ValidateFragInvocationCountAtDefinition(decoration, inst);
4422 }
4423 case spv::BuiltIn::FragSizeEXT: {
4424 // alias spv::BuiltIn::FragmentSizeNV
4425 return ValidateFragSizeAtDefinition(decoration, inst);
4426 }
4427 case spv::BuiltIn::FragStencilRefEXT: {
4428 return ValidateFragStencilRefAtDefinition(decoration, inst);
4429 }
4430 case spv::BuiltIn::FullyCoveredEXT:{
4431 return ValidateFullyCoveredAtDefinition(decoration, inst);
4432 }
4433 // Ray tracing builtins
4434 case spv::BuiltIn::HitKindKHR: // alias spv::BuiltIn::HitKindNV
4435 case spv::BuiltIn::HitTNV: // NOT present in KHR
4436 case spv::BuiltIn::InstanceId:
4437 case spv::BuiltIn::LaunchIdKHR: // alias spv::BuiltIn::LaunchIdNV
4438 case spv::BuiltIn::LaunchSizeKHR: // alias spv::BuiltIn::LaunchSizeNV
4439 case spv::BuiltIn::WorldRayOriginKHR: // alias spv::BuiltIn::WorldRayOriginNV
4440 case spv::BuiltIn::WorldRayDirectionKHR: // alias spv::BuiltIn::WorldRayDirectionNV
4441 case spv::BuiltIn::ObjectRayOriginKHR: // alias spv::BuiltIn::ObjectRayOriginNV
4442 case spv::BuiltIn::ObjectRayDirectionKHR: // alias
4443 // spv::BuiltIn::ObjectRayDirectionNV
4444 case spv::BuiltIn::RayTminKHR: // alias spv::BuiltIn::RayTminNV
4445 case spv::BuiltIn::RayTmaxKHR: // alias spv::BuiltIn::RayTmaxNV
4446 case spv::BuiltIn::InstanceCustomIndexKHR: // alias
4447 // spv::BuiltIn::InstanceCustomIndexNV
4448 case spv::BuiltIn::ObjectToWorldKHR: // alias spv::BuiltIn::ObjectToWorldNV
4449 case spv::BuiltIn::WorldToObjectKHR: // alias spv::BuiltIn::WorldToObjectNV
4450 case spv::BuiltIn::IncomingRayFlagsKHR: // alias spv::BuiltIn::IncomingRayFlagsNV
4451 case spv::BuiltIn::RayGeometryIndexKHR: // NOT present in NV
4452 case spv::BuiltIn::CullMaskKHR: {
4453 return ValidateRayTracingBuiltinsAtDefinition(decoration, inst);
4454 }
4455 case spv::BuiltIn::PrimitivePointIndicesEXT:
4456 case spv::BuiltIn::PrimitiveLineIndicesEXT:
4457 case spv::BuiltIn::PrimitiveTriangleIndicesEXT: {
4458 return ValidateMeshShadingEXTBuiltinsAtDefinition(decoration, inst);
4459 }
4460 case spv::BuiltIn::PrimitiveShadingRateKHR: {
4461 return ValidatePrimitiveShadingRateAtDefinition(decoration, inst);
4462 }
4463 case spv::BuiltIn::ShadingRateKHR: {
4464 return ValidateShadingRateAtDefinition(decoration, inst);
4465 }
4466 default:
4467 // No validation rules (for the moment).
4468 break;
4469 }
4470 return SPV_SUCCESS;
4471 }
4472
ValidateBuiltInsAtDefinition()4473 spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() {
4474 for (const auto& kv : _.id_decorations()) {
4475 const uint32_t id = kv.first;
4476 const auto& decorations = kv.second;
4477 if (decorations.empty()) {
4478 continue;
4479 }
4480
4481 const Instruction* inst = _.FindDef(id);
4482 assert(inst);
4483
4484 for (const auto& decoration : kv.second) {
4485 if (decoration.dec_type() != spv::Decoration::BuiltIn) {
4486 continue;
4487 }
4488
4489 if (spv_result_t error =
4490 ValidateSingleBuiltInAtDefinition(decoration, *inst)) {
4491 return error;
4492 }
4493 }
4494 }
4495
4496 return SPV_SUCCESS;
4497 }
4498
Run()4499 spv_result_t BuiltInsValidator::Run() {
4500 // First pass: validate all built-ins at definition and seed
4501 // id_to_at_reference_checks_ with built-ins.
4502 if (auto error = ValidateBuiltInsAtDefinition()) {
4503 return error;
4504 }
4505
4506 if (id_to_at_reference_checks_.empty()) {
4507 // No validation tasks were seeded. Nothing else to do.
4508 return SPV_SUCCESS;
4509 }
4510
4511 // Second pass: validate every id reference in the module using
4512 // rules in id_to_at_reference_checks_.
4513 for (const Instruction& inst : _.ordered_instructions()) {
4514 Update(inst);
4515
4516 std::set<uint32_t> already_checked;
4517
4518 for (const auto& operand : inst.operands()) {
4519 if (!spvIsIdType(operand.type)) {
4520 // Not id.
4521 continue;
4522 }
4523
4524 const uint32_t id = inst.word(operand.offset);
4525 if (id == inst.id()) {
4526 // No need to check result id.
4527 continue;
4528 }
4529
4530 if (!already_checked.insert(id).second) {
4531 // The instruction has already referenced this id.
4532 continue;
4533 }
4534
4535 // Instruction references the id. Run all checks associated with the id
4536 // on the instruction. id_to_at_reference_checks_ can be modified in the
4537 // process, iterators are safe because it's a tree-based map.
4538 const auto it = id_to_at_reference_checks_.find(id);
4539 if (it != id_to_at_reference_checks_.end()) {
4540 for (const auto& check : it->second) {
4541 if (spv_result_t error = check(inst)) {
4542 return error;
4543 }
4544 }
4545 }
4546 }
4547 }
4548
4549 return SPV_SUCCESS;
4550 }
4551
4552 } // namespace
4553
4554 // Validates correctness of built-in variables.
ValidateBuiltIns(ValidationState_t & _)4555 spv_result_t ValidateBuiltIns(ValidationState_t& _) {
4556 BuiltInsValidator validator(_);
4557 return validator.Run();
4558 }
4559
4560 } // namespace val
4561 } // namespace spvtools
4562