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