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