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