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