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