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