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