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, MeshNV, or TaskNV 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, MeshNV, or TaskNV 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           execution_model != SpvExecutionModelTaskNV &&
3075           execution_model != SpvExecutionModelMeshNV) {
3076         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3077                << _.VkErrorID(4425)
3078                << spvLogStringForEnv(_.context()->target_env)
3079                << " spec allows BuiltIn "
3080                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3081                                                 decoration.params()[0])
3082                << " to be used only with GLCompute, MeshNV, or TaskNV execution model. "
3083                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3084                                    referenced_from_inst, execution_model);
3085       }
3086     }
3087   }
3088 
3089   if (function_id_ == 0) {
3090     // Propagate this rule to all dependant ids in the global scope.
3091     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3092         &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration,
3093         built_in_inst, referenced_from_inst, std::placeholders::_1));
3094   }
3095 
3096   return SPV_SUCCESS;
3097 }
3098 
ValidateBaseInstanceOrVertexAtDefinition(const Decoration & decoration,const Instruction & inst)3099 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtDefinition(
3100     const Decoration& decoration, const Instruction& inst) {
3101   if (spvIsVulkanEnv(_.context()->target_env)) {
3102     if (spv_result_t error = ValidateI32(
3103             decoration, inst,
3104             [this, &inst,
3105              &decoration](const std::string& message) -> spv_result_t {
3106               uint32_t vuid = (decoration.params()[0] == SpvBuiltInBaseInstance)
3107                                   ? 4183
3108                                   : 4186;
3109               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3110                      << _.VkErrorID(vuid)
3111                      << "According to the Vulkan spec BuiltIn "
3112                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3113                                                       decoration.params()[0])
3114                      << " variable needs to be a 32-bit int scalar. "
3115                      << message;
3116             })) {
3117       return error;
3118     }
3119   }
3120 
3121   return ValidateBaseInstanceOrVertexAtReference(decoration, inst, inst, inst);
3122 }
3123 
ValidateBaseInstanceOrVertexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3124 spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference(
3125     const Decoration& decoration, const Instruction& built_in_inst,
3126     const Instruction& referenced_inst,
3127     const Instruction& referenced_from_inst) {
3128   uint32_t operand = decoration.params()[0];
3129   if (spvIsVulkanEnv(_.context()->target_env)) {
3130     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3131     if (storage_class != SpvStorageClassMax &&
3132         storage_class != SpvStorageClassInput) {
3133       uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4182 : 4185;
3134       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3135              << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3136              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3137                                               operand)
3138              << " to be only used for variables with Input storage class. "
3139              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3140                                  referenced_from_inst)
3141              << " " << GetStorageClassDesc(referenced_from_inst);
3142     }
3143 
3144     for (const SpvExecutionModel execution_model : execution_models_) {
3145       if (execution_model != SpvExecutionModelVertex) {
3146         uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4181 : 4184;
3147         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3148                << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3149                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3150                                                 operand)
3151                << " to be used only with Vertex execution model. "
3152                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3153                                    referenced_from_inst, execution_model);
3154       }
3155     }
3156   }
3157 
3158   if (function_id_ == 0) {
3159     // Propagate this rule to all dependant ids in the global scope.
3160     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3161         std::bind(&BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference,
3162                   this, decoration, built_in_inst, referenced_from_inst,
3163                   std::placeholders::_1));
3164   }
3165 
3166   return SPV_SUCCESS;
3167 }
3168 
ValidateDrawIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3169 spv_result_t BuiltInsValidator::ValidateDrawIndexAtDefinition(
3170     const Decoration& decoration, const Instruction& inst) {
3171   if (spvIsVulkanEnv(_.context()->target_env)) {
3172     if (spv_result_t error = ValidateI32(
3173             decoration, inst,
3174             [this, &inst,
3175              &decoration](const std::string& message) -> spv_result_t {
3176               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3177                      << _.VkErrorID(4209)
3178                      << "According to the Vulkan spec BuiltIn "
3179                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3180                                                       decoration.params()[0])
3181                      << " variable needs to be a 32-bit int scalar. "
3182                      << message;
3183             })) {
3184       return error;
3185     }
3186   }
3187 
3188   return ValidateDrawIndexAtReference(decoration, inst, inst, inst);
3189 }
3190 
ValidateDrawIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3191 spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference(
3192     const Decoration& decoration, const Instruction& built_in_inst,
3193     const Instruction& referenced_inst,
3194     const Instruction& referenced_from_inst) {
3195   uint32_t operand = decoration.params()[0];
3196   if (spvIsVulkanEnv(_.context()->target_env)) {
3197     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3198     if (storage_class != SpvStorageClassMax &&
3199         storage_class != SpvStorageClassInput) {
3200       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3201              << _.VkErrorID(4208) << "Vulkan spec allows BuiltIn "
3202              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3203                                               operand)
3204              << " to be only used for variables with Input storage class. "
3205              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3206                                  referenced_from_inst)
3207              << " " << GetStorageClassDesc(referenced_from_inst);
3208     }
3209 
3210     for (const SpvExecutionModel execution_model : execution_models_) {
3211       if (execution_model != SpvExecutionModelVertex &&
3212           execution_model != SpvExecutionModelMeshNV &&
3213           execution_model != SpvExecutionModelTaskNV) {
3214         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3215                << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn "
3216                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3217                                                 operand)
3218                << " to be used only with Vertex, MeshNV, or TaskNV execution "
3219                   "model. "
3220                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3221                                    referenced_from_inst, execution_model);
3222       }
3223     }
3224   }
3225 
3226   if (function_id_ == 0) {
3227     // Propagate this rule to all dependant ids in the global scope.
3228     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3229         &BuiltInsValidator::ValidateDrawIndexAtReference, this, decoration,
3230         built_in_inst, referenced_from_inst, std::placeholders::_1));
3231   }
3232 
3233   return SPV_SUCCESS;
3234 }
3235 
ValidateViewIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3236 spv_result_t BuiltInsValidator::ValidateViewIndexAtDefinition(
3237     const Decoration& decoration, const Instruction& inst) {
3238   if (spvIsVulkanEnv(_.context()->target_env)) {
3239     if (spv_result_t error = ValidateI32(
3240             decoration, inst,
3241             [this, &inst,
3242              &decoration](const std::string& message) -> spv_result_t {
3243               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3244                      << _.VkErrorID(4403)
3245                      << "According to the Vulkan spec BuiltIn "
3246                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3247                                                       decoration.params()[0])
3248                      << " variable needs to be a 32-bit int scalar. "
3249                      << message;
3250             })) {
3251       return error;
3252     }
3253   }
3254 
3255   return ValidateViewIndexAtReference(decoration, inst, inst, inst);
3256 }
3257 
ValidateViewIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3258 spv_result_t BuiltInsValidator::ValidateViewIndexAtReference(
3259     const Decoration& decoration, const Instruction& built_in_inst,
3260     const Instruction& referenced_inst,
3261     const Instruction& referenced_from_inst) {
3262   uint32_t operand = decoration.params()[0];
3263   if (spvIsVulkanEnv(_.context()->target_env)) {
3264     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3265     if (storage_class != SpvStorageClassMax &&
3266         storage_class != SpvStorageClassInput) {
3267       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3268              << _.VkErrorID(4402) << "Vulkan spec allows BuiltIn "
3269              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3270                                               operand)
3271              << " to be only used for variables with Input storage class. "
3272              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3273                                  referenced_from_inst)
3274              << " " << GetStorageClassDesc(referenced_from_inst);
3275     }
3276 
3277     for (const SpvExecutionModel execution_model : execution_models_) {
3278       if (execution_model == SpvExecutionModelGLCompute) {
3279         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3280                << _.VkErrorID(4401) << "Vulkan spec allows BuiltIn "
3281                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3282                                                 operand)
3283                << " to be not be used with GLCompute execution model. "
3284                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3285                                    referenced_from_inst, execution_model);
3286       }
3287     }
3288   }
3289 
3290   if (function_id_ == 0) {
3291     // Propagate this rule to all dependant ids in the global scope.
3292     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3293         &BuiltInsValidator::ValidateViewIndexAtReference, this, decoration,
3294         built_in_inst, referenced_from_inst, std::placeholders::_1));
3295   }
3296 
3297   return SPV_SUCCESS;
3298 }
3299 
ValidateDeviceIndexAtDefinition(const Decoration & decoration,const Instruction & inst)3300 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtDefinition(
3301     const Decoration& decoration, const Instruction& inst) {
3302   if (spvIsVulkanEnv(_.context()->target_env)) {
3303     if (spv_result_t error = ValidateI32(
3304             decoration, inst,
3305             [this, &inst,
3306              &decoration](const std::string& message) -> spv_result_t {
3307               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3308                      << _.VkErrorID(4206)
3309                      << "According to the Vulkan spec BuiltIn "
3310                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3311                                                       decoration.params()[0])
3312                      << " variable needs to be a 32-bit int scalar. "
3313                      << message;
3314             })) {
3315       return error;
3316     }
3317   }
3318 
3319   return ValidateDeviceIndexAtReference(decoration, inst, inst, inst);
3320 }
3321 
ValidateDeviceIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3322 spv_result_t BuiltInsValidator::ValidateDeviceIndexAtReference(
3323     const Decoration& decoration, const Instruction& built_in_inst,
3324     const Instruction& referenced_inst,
3325     const Instruction& referenced_from_inst) {
3326   uint32_t operand = decoration.params()[0];
3327   if (spvIsVulkanEnv(_.context()->target_env)) {
3328     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3329     if (storage_class != SpvStorageClassMax &&
3330         storage_class != SpvStorageClassInput) {
3331       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3332              << _.VkErrorID(4205) << "Vulkan spec allows BuiltIn "
3333              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3334                                               operand)
3335              << " to be only used for variables with Input storage class. "
3336              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3337                                  referenced_from_inst)
3338              << " " << GetStorageClassDesc(referenced_from_inst);
3339     }
3340   }
3341 
3342   if (function_id_ == 0) {
3343     // Propagate this rule to all dependant ids in the global scope.
3344     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3345         &BuiltInsValidator::ValidateDeviceIndexAtReference, this, decoration,
3346         built_in_inst, referenced_from_inst, std::placeholders::_1));
3347   }
3348 
3349   return SPV_SUCCESS;
3350 }
3351 
ValidateFragInvocationCountAtDefinition(const Decoration & decoration,const Instruction & inst)3352 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtDefinition(const Decoration& decoration,
3353                                             const Instruction& inst) {
3354 
3355   if (spvIsVulkanEnv(_.context()->target_env)) {
3356     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3357     if (spv_result_t error = ValidateI32(
3358             decoration, inst,
3359             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3360               uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3361               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3362                      << _.VkErrorID(vuid) << "According to the "
3363                      << spvLogStringForEnv(_.context()->target_env)
3364                      << " spec BuiltIn "
3365                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3366                                                       builtin)
3367                      << " variable needs to be a 32-bit int scalar. "
3368                      << message;
3369             })) {
3370       return error;
3371     }
3372   }
3373 
3374   return ValidateFragInvocationCountAtReference(decoration, inst, inst, inst);
3375 }
3376 
ValidateFragInvocationCountAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3377 spv_result_t BuiltInsValidator::ValidateFragInvocationCountAtReference(
3378     const Decoration& decoration, const Instruction& built_in_inst,
3379     const Instruction& referenced_inst,
3380     const Instruction& referenced_from_inst) {
3381 
3382   if (spvIsVulkanEnv(_.context()->target_env)) {
3383     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3384     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3385     if (storage_class != SpvStorageClassMax &&
3386         storage_class != SpvStorageClassInput) {
3387       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3388       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3389              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3390              << " spec allows BuiltIn "
3391              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3392              << " to be only used for variables with Input storage class. "
3393              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3394                                  referenced_from_inst)
3395              << " " << GetStorageClassDesc(referenced_from_inst);
3396     }
3397 
3398     for (const SpvExecutionModel execution_model : execution_models_) {
3399       if (execution_model != SpvExecutionModelFragment) {
3400         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3401         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3402                << _.VkErrorID(vuid)
3403                << spvLogStringForEnv(_.context()->target_env)
3404                << " spec allows BuiltIn "
3405                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3406                << " to be used only with Fragment execution model. "
3407                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3408                                    referenced_from_inst, execution_model);
3409       }
3410     }
3411   }
3412 
3413   if (function_id_ == 0) {
3414     // Propagate this rule to all dependant ids in the global scope.
3415     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3416         &BuiltInsValidator::ValidateFragInvocationCountAtReference, this, decoration,
3417         built_in_inst, referenced_from_inst, std::placeholders::_1));
3418   }
3419 
3420   return SPV_SUCCESS;
3421 }
3422 
ValidateFragSizeAtDefinition(const Decoration & decoration,const Instruction & inst)3423 spv_result_t BuiltInsValidator::ValidateFragSizeAtDefinition(const Decoration& decoration,
3424                                             const Instruction& inst) {
3425   if (spvIsVulkanEnv(_.context()->target_env)) {
3426     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3427     if (spv_result_t error = ValidateI32Vec(
3428             decoration, inst, 2,
3429             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3430               uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3431               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3432                      << _.VkErrorID(vuid) << "According to the "
3433                      << spvLogStringForEnv(_.context()->target_env)
3434                      << " spec BuiltIn "
3435                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3436                                                       builtin)
3437                      << " variable needs to be a 2-component 32-bit int vector. "
3438                      << message;
3439             })) {
3440       return error;
3441     }
3442   }
3443 
3444   return ValidateFragSizeAtReference(decoration, inst, inst, inst);
3445 }
3446 
ValidateFragSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3447 spv_result_t BuiltInsValidator::ValidateFragSizeAtReference(
3448     const Decoration& decoration, const Instruction& built_in_inst,
3449     const Instruction& referenced_inst,
3450     const Instruction& referenced_from_inst) {
3451 
3452   if (spvIsVulkanEnv(_.context()->target_env)) {
3453     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3454     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3455     if (storage_class != SpvStorageClassMax &&
3456         storage_class != SpvStorageClassInput) {
3457       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3458       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3459              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3460              << " spec allows BuiltIn "
3461              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3462              << " to be only used for variables with Input storage class. "
3463              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3464                                  referenced_from_inst)
3465              << " " << GetStorageClassDesc(referenced_from_inst);
3466     }
3467 
3468     for (const SpvExecutionModel execution_model : execution_models_) {
3469       if (execution_model != SpvExecutionModelFragment) {
3470         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3471         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3472                << _.VkErrorID(vuid)
3473                << spvLogStringForEnv(_.context()->target_env)
3474                << " spec allows BuiltIn "
3475                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3476                << " to be used only with Fragment execution model. "
3477                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3478                                    referenced_from_inst, execution_model);
3479       }
3480     }
3481   }
3482 
3483   if (function_id_ == 0) {
3484     // Propagate this rule to all dependant ids in the global scope.
3485     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3486         &BuiltInsValidator::ValidateFragSizeAtReference, this, decoration,
3487         built_in_inst, referenced_from_inst, std::placeholders::_1));
3488   }
3489 
3490   return SPV_SUCCESS;
3491 }
3492 
ValidateFragStencilRefAtDefinition(const Decoration & decoration,const Instruction & inst)3493 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtDefinition(const Decoration& decoration,
3494                                             const Instruction& inst) {
3495   if (spvIsVulkanEnv(_.context()->target_env)) {
3496     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3497     if (spv_result_t error = ValidateI(
3498             decoration, inst,
3499             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3500               uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3501               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3502                      << _.VkErrorID(vuid) << "According to the "
3503                      << spvLogStringForEnv(_.context()->target_env)
3504                      << " spec BuiltIn "
3505                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3506                                                       builtin)
3507                      << " variable needs to be a int scalar. "
3508                      << message;
3509             })) {
3510       return error;
3511     }
3512   }
3513 
3514   return ValidateFragStencilRefAtReference(decoration, inst, inst, inst);
3515 }
3516 
ValidateFragStencilRefAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3517 spv_result_t BuiltInsValidator::ValidateFragStencilRefAtReference(
3518     const Decoration& decoration, const Instruction& built_in_inst,
3519     const Instruction& referenced_inst,
3520     const Instruction& referenced_from_inst) {
3521 
3522   if (spvIsVulkanEnv(_.context()->target_env)) {
3523     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3524     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3525     if (storage_class != SpvStorageClassMax &&
3526         storage_class != SpvStorageClassOutput) {
3527       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3528       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3529              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3530              << " spec allows BuiltIn "
3531              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3532              << " to be only used for variables with Output storage class. "
3533              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3534                                  referenced_from_inst)
3535              << " " << GetStorageClassDesc(referenced_from_inst);
3536     }
3537 
3538     for (const SpvExecutionModel execution_model : execution_models_) {
3539       if (execution_model != SpvExecutionModelFragment) {
3540         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3541         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3542                << _.VkErrorID(vuid)
3543                << spvLogStringForEnv(_.context()->target_env)
3544                << " spec allows BuiltIn "
3545                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3546                << " to be used only with Fragment execution model. "
3547                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3548                                    referenced_from_inst, execution_model);
3549       }
3550     }
3551   }
3552 
3553   if (function_id_ == 0) {
3554     // Propagate this rule to all dependant ids in the global scope.
3555     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3556         &BuiltInsValidator::ValidateFragStencilRefAtReference, this, decoration,
3557         built_in_inst, referenced_from_inst, std::placeholders::_1));
3558   }
3559 
3560   return SPV_SUCCESS;
3561 }
3562 
ValidateFullyCoveredAtDefinition(const Decoration & decoration,const Instruction & inst)3563 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtDefinition(const Decoration& decoration,
3564                                                const Instruction& inst) {
3565   if (spvIsVulkanEnv(_.context()->target_env)) {
3566     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3567     if (spv_result_t error = ValidateBool(
3568             decoration, inst,
3569             [this, &inst, &builtin](const std::string& message) -> spv_result_t {
3570               uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3571               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3572                      << _.VkErrorID(vuid) << "According to the "
3573                      << spvLogStringForEnv(_.context()->target_env)
3574                      << " spec BuiltIn "
3575                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3576                                                       builtin)
3577                      << " variable needs to be a bool scalar. "
3578                      << message;
3579             })) {
3580       return error;
3581     }
3582   }
3583 
3584   return ValidateFullyCoveredAtReference(decoration, inst, inst, inst);
3585 }
3586 
ValidateFullyCoveredAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3587 spv_result_t BuiltInsValidator::ValidateFullyCoveredAtReference(
3588     const Decoration& decoration, const Instruction& built_in_inst,
3589     const Instruction& referenced_inst,
3590     const Instruction& referenced_from_inst) {
3591 
3592   if (spvIsVulkanEnv(_.context()->target_env)) {
3593     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3594     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3595     if (storage_class != SpvStorageClassMax &&
3596         storage_class != SpvStorageClassInput) {
3597       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3598       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3599              << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
3600              << " spec allows BuiltIn "
3601              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3602              << " to be only used for variables with Input storage class. "
3603              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3604                                  referenced_from_inst)
3605              << " " << GetStorageClassDesc(referenced_from_inst);
3606     }
3607 
3608     for (const SpvExecutionModel execution_model : execution_models_) {
3609       if (execution_model != SpvExecutionModelFragment) {
3610         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3611         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3612                << _.VkErrorID(vuid)
3613                << spvLogStringForEnv(_.context()->target_env)
3614                << " spec allows BuiltIn "
3615                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
3616                << " to be used only with Fragment execution model. "
3617                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3618                                    referenced_from_inst, execution_model);
3619       }
3620     }
3621   }
3622 
3623   if (function_id_ == 0) {
3624     // Propagate this rule to all dependant ids in the global scope.
3625     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3626         &BuiltInsValidator::ValidateFullyCoveredAtReference, this, decoration,
3627         built_in_inst, referenced_from_inst, std::placeholders::_1));
3628   }
3629 
3630   return SPV_SUCCESS;
3631 }
3632 
ValidateSMBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3633 spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition(
3634     const Decoration& decoration, const Instruction& inst) {
3635   if (spvIsVulkanEnv(_.context()->target_env)) {
3636     if (spv_result_t error = ValidateI32(
3637             decoration, inst,
3638             [this, &inst,
3639              &decoration](const std::string& message) -> spv_result_t {
3640               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3641                      << "According to the "
3642                      << spvLogStringForEnv(_.context()->target_env)
3643                      << " spec BuiltIn "
3644                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3645                                                       decoration.params()[0])
3646                      << " variable needs to be a 32-bit int scalar. "
3647                      << message;
3648             })) {
3649       return error;
3650     }
3651   }
3652 
3653   // Seed at reference checks with this built-in.
3654   return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst);
3655 }
3656 
ValidateSMBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3657 spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference(
3658     const Decoration& decoration, const Instruction& built_in_inst,
3659     const Instruction& referenced_inst,
3660     const Instruction& referenced_from_inst) {
3661   if (spvIsVulkanEnv(_.context()->target_env)) {
3662     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3663     if (storage_class != SpvStorageClassMax &&
3664         storage_class != SpvStorageClassInput) {
3665       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3666              << spvLogStringForEnv(_.context()->target_env)
3667              << " spec allows BuiltIn "
3668              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3669                                               decoration.params()[0])
3670              << " to be only used for "
3671                 "variables with Input storage class. "
3672              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3673                                  referenced_from_inst)
3674              << " " << GetStorageClassDesc(referenced_from_inst);
3675     }
3676   }
3677 
3678   if (function_id_ == 0) {
3679     // Propagate this rule to all dependant ids in the global scope.
3680     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3681         &BuiltInsValidator::ValidateSMBuiltinsAtReference, this, decoration,
3682         built_in_inst, referenced_from_inst, std::placeholders::_1));
3683   }
3684 
3685   return SPV_SUCCESS;
3686 }
3687 
ValidatePrimitiveShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3688 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition(
3689     const Decoration& decoration, const Instruction& inst) {
3690   if (spvIsVulkanEnv(_.context()->target_env)) {
3691     if (spv_result_t error = ValidateI32(
3692             decoration, inst,
3693             [this, &inst,
3694              &decoration](const std::string& message) -> spv_result_t {
3695               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3696                      << _.VkErrorID(4486)
3697                      << "According to the Vulkan spec BuiltIn "
3698                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3699                                                       decoration.params()[0])
3700                      << " variable needs to be a 32-bit int scalar. "
3701                      << message;
3702             })) {
3703       return error;
3704     }
3705   }
3706 
3707   // Seed at reference checks with this built-in.
3708   return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst);
3709 }
3710 
ValidatePrimitiveShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3711 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference(
3712     const Decoration& decoration, const Instruction& built_in_inst,
3713     const Instruction& referenced_inst,
3714     const Instruction& referenced_from_inst) {
3715   if (spvIsVulkanEnv(_.context()->target_env)) {
3716     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3717     if (storage_class != SpvStorageClassMax &&
3718         storage_class != SpvStorageClassOutput) {
3719       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3720              << _.VkErrorID(4485) << "Vulkan spec allows BuiltIn "
3721              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3722                                               decoration.params()[0])
3723              << " to be only used for variables with Output storage class. "
3724              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3725                                  referenced_from_inst)
3726              << " " << GetStorageClassDesc(referenced_from_inst);
3727     }
3728 
3729     for (const SpvExecutionModel execution_model : execution_models_) {
3730       switch (execution_model) {
3731         case SpvExecutionModelVertex:
3732         case SpvExecutionModelGeometry:
3733         case SpvExecutionModelMeshNV:
3734           break;
3735         default: {
3736           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3737                  << _.VkErrorID(4484) << "Vulkan spec allows BuiltIn "
3738                  << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3739                                                   decoration.params()[0])
3740                  << " to be used only with Vertex, Geometry, or MeshNV "
3741                     "execution models. "
3742                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3743                                      referenced_from_inst, execution_model);
3744         }
3745       }
3746     }
3747   }
3748 
3749   if (function_id_ == 0) {
3750     // Propagate this rule to all dependant ids in the global scope.
3751     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3752         std::bind(&BuiltInsValidator::ValidatePrimitiveShadingRateAtReference,
3753                   this, decoration, built_in_inst, referenced_from_inst,
3754                   std::placeholders::_1));
3755   }
3756 
3757   return SPV_SUCCESS;
3758 }
3759 
ValidateShadingRateAtDefinition(const Decoration & decoration,const Instruction & inst)3760 spv_result_t BuiltInsValidator::ValidateShadingRateAtDefinition(
3761     const Decoration& decoration, const Instruction& inst) {
3762   if (spvIsVulkanEnv(_.context()->target_env)) {
3763     if (spv_result_t error = ValidateI32(
3764             decoration, inst,
3765             [this, &inst,
3766              &decoration](const std::string& message) -> spv_result_t {
3767               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3768                      << _.VkErrorID(4492)
3769                      << "According to the Vulkan spec BuiltIn "
3770                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3771                                                       decoration.params()[0])
3772                      << " variable needs to be a 32-bit int scalar. "
3773                      << message;
3774             })) {
3775       return error;
3776     }
3777   }
3778 
3779   // Seed at reference checks with this built-in.
3780   return ValidateShadingRateAtReference(decoration, inst, inst, inst);
3781 }
3782 
ValidateShadingRateAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3783 spv_result_t BuiltInsValidator::ValidateShadingRateAtReference(
3784     const Decoration& decoration, const Instruction& built_in_inst,
3785     const Instruction& referenced_inst,
3786     const Instruction& referenced_from_inst) {
3787   if (spvIsVulkanEnv(_.context()->target_env)) {
3788     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3789     if (storage_class != SpvStorageClassMax &&
3790         storage_class != SpvStorageClassInput) {
3791       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3792              << _.VkErrorID(4491) << "Vulkan spec allows BuiltIn "
3793              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3794                                               decoration.params()[0])
3795              << " to be only used for variables with Input storage class. "
3796              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3797                                  referenced_from_inst)
3798              << " " << GetStorageClassDesc(referenced_from_inst);
3799     }
3800 
3801     for (const SpvExecutionModel execution_model : execution_models_) {
3802       if (execution_model != SpvExecutionModelFragment) {
3803         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3804                << _.VkErrorID(4490) << "Vulkan spec allows BuiltIn "
3805                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3806                                                 decoration.params()[0])
3807                << " to be used only with the Fragment execution model. "
3808                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3809                                    referenced_from_inst, execution_model);
3810       }
3811     }
3812   }
3813 
3814   if (function_id_ == 0) {
3815     // Propagate this rule to all dependant ids in the global scope.
3816     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
3817         &BuiltInsValidator::ValidateShadingRateAtReference, this, decoration,
3818         built_in_inst, referenced_from_inst, std::placeholders::_1));
3819   }
3820 
3821   return SPV_SUCCESS;
3822 }
3823 
ValidateRayTracingBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)3824 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition(
3825     const Decoration& decoration, const Instruction& inst) {
3826   if (spvIsVulkanEnv(_.context()->target_env)) {
3827     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3828     switch (builtin) {
3829       case SpvBuiltInHitTNV:
3830       case SpvBuiltInRayTminKHR:
3831       case SpvBuiltInRayTmaxKHR:
3832         // f32 scalar
3833         if (spv_result_t error = ValidateF32(
3834                 decoration, inst,
3835                 [this, &inst,
3836                  builtin](const std::string& message) -> spv_result_t {
3837                   uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3838                   return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3839                          << _.VkErrorID(vuid)
3840                          << "According to the Vulkan spec BuiltIn "
3841                          << _.grammar().lookupOperandName(
3842                                 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3843                          << " variable needs to be a 32-bit float scalar. "
3844                          << message;
3845                 })) {
3846           return error;
3847         }
3848         break;
3849       case SpvBuiltInHitKindKHR:
3850       case SpvBuiltInInstanceCustomIndexKHR:
3851       case SpvBuiltInInstanceId:
3852       case SpvBuiltInRayGeometryIndexKHR:
3853       case SpvBuiltInIncomingRayFlagsKHR:
3854         // i32 scalar
3855         if (spv_result_t error = ValidateI32(
3856                 decoration, inst,
3857                 [this, &inst,
3858                  builtin](const std::string& message) -> spv_result_t {
3859                   uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3860                   return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3861                          << _.VkErrorID(vuid)
3862                          << "According to the Vulkan spec BuiltIn "
3863                          << _.grammar().lookupOperandName(
3864                                 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3865                          << " variable needs to be a 32-bit int scalar. "
3866                          << message;
3867                 })) {
3868           return error;
3869         }
3870         break;
3871       case SpvBuiltInObjectRayDirectionKHR:
3872       case SpvBuiltInObjectRayOriginKHR:
3873       case SpvBuiltInWorldRayDirectionKHR:
3874       case SpvBuiltInWorldRayOriginKHR:
3875         // f32 vec3
3876         if (spv_result_t error = ValidateF32Vec(
3877                 decoration, inst, 3,
3878                 [this, &inst,
3879                  builtin](const std::string& message) -> spv_result_t {
3880                   uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3881                   return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3882                          << _.VkErrorID(vuid)
3883                          << "According to the Vulkan spec BuiltIn "
3884                          << _.grammar().lookupOperandName(
3885                                 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3886                          << " variable needs to be a 3-component 32-bit float "
3887                             "vector. "
3888                          << message;
3889                 })) {
3890           return error;
3891         }
3892         break;
3893       case SpvBuiltInLaunchIdKHR:
3894       case SpvBuiltInLaunchSizeKHR:
3895         // i32 vec3
3896         if (spv_result_t error = ValidateI32Vec(
3897                 decoration, inst, 3,
3898                 [this, &inst,
3899                  builtin](const std::string& message) -> spv_result_t {
3900                   uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3901                   return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3902                          << _.VkErrorID(vuid)
3903                          << "According to the Vulkan spec BuiltIn "
3904                          << _.grammar().lookupOperandName(
3905                                 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3906                          << " variable needs to be a 3-component 32-bit int "
3907                             "vector. "
3908                          << message;
3909                 })) {
3910           return error;
3911         }
3912         break;
3913       case SpvBuiltInObjectToWorldKHR:
3914       case SpvBuiltInWorldToObjectKHR:
3915         // f32 mat4x3
3916         if (spv_result_t error = ValidateF32Mat(
3917                 decoration, inst, 3, 4,
3918                 [this, &inst,
3919                  builtin](const std::string& message) -> spv_result_t {
3920                   uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
3921                   return _.diag(SPV_ERROR_INVALID_DATA, &inst)
3922                          << _.VkErrorID(vuid)
3923                          << "According to the Vulkan spec BuiltIn "
3924                          << _.grammar().lookupOperandName(
3925                                 SPV_OPERAND_TYPE_BUILT_IN, builtin)
3926                          << " variable needs to be a matrix with"
3927                          << " 4 columns of 3-component vectors of 32-bit "
3928                             "floats. "
3929                          << message;
3930                 })) {
3931           return error;
3932         }
3933         break;
3934       default:
3935         assert(0 && "Unexpected ray tracing builtin");
3936         break;
3937     }
3938   }
3939 
3940   // Seed at reference checks with this built-in.
3941   return ValidateRayTracingBuiltinsAtReference(decoration, inst, inst, inst);
3942 }
3943 
ValidateRayTracingBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)3944 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtReference(
3945     const Decoration& decoration, const Instruction& built_in_inst,
3946     const Instruction& referenced_inst,
3947     const Instruction& referenced_from_inst) {
3948   if (spvIsVulkanEnv(_.context()->target_env)) {
3949     const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
3950     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
3951     if (storage_class != SpvStorageClassMax &&
3952         storage_class != SpvStorageClassInput) {
3953       uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
3954       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3955              << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn "
3956              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3957                                               decoration.params()[0])
3958              << " to be only used for variables with Input storage class. "
3959              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3960                                  referenced_from_inst)
3961              << " " << GetStorageClassDesc(referenced_from_inst);
3962     }
3963 
3964     for (const SpvExecutionModel execution_model : execution_models_) {
3965       if (!IsExecutionModelValidForRtBuiltIn(builtin, execution_model)) {
3966         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
3967         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
3968                << _.VkErrorID(vuid) << "Vulkan spec does not allow BuiltIn "
3969                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
3970                                                 decoration.params()[0])
3971                << " to be used with the execution model "
3972                << _.grammar().lookupOperandName(
3973                       SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model)
3974                << ".\n"
3975                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
3976                                    referenced_from_inst, execution_model);
3977       }
3978     }
3979   }
3980 
3981   if (function_id_ == 0) {
3982     // Propagate this rule to all dependant ids in the global scope.
3983     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
3984         std::bind(&BuiltInsValidator::ValidateRayTracingBuiltinsAtReference,
3985                   this, decoration, built_in_inst, referenced_from_inst,
3986                   std::placeholders::_1));
3987   }
3988 
3989   return SPV_SUCCESS;
3990 }
3991 
ValidateSingleBuiltInAtDefinition(const Decoration & decoration,const Instruction & inst)3992 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
3993     const Decoration& decoration, const Instruction& inst) {
3994   const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
3995 
3996   if (!spvIsVulkanEnv(_.context()->target_env)) {
3997     // Early return. All currently implemented rules are based on Vulkan spec.
3998     //
3999     // TODO: If you are adding validation rules for environments other than
4000     // Vulkan (or general rules which are not environment independent), then
4001     // you need to modify or remove this condition. Consider also adding early
4002     // returns into BuiltIn-specific rules, so that the system doesn't spawn new
4003     // rules which don't do anything.
4004     return SPV_SUCCESS;
4005   }
4006 
4007   // If you are adding a new BuiltIn enum, please register it here.
4008   // If the newly added enum has validation rules associated with it
4009   // consider leaving a TODO and/or creating an issue.
4010   switch (label) {
4011     case SpvBuiltInClipDistance:
4012     case SpvBuiltInCullDistance: {
4013       return ValidateClipOrCullDistanceAtDefinition(decoration, inst);
4014     }
4015     case SpvBuiltInFragCoord: {
4016       return ValidateFragCoordAtDefinition(decoration, inst);
4017     }
4018     case SpvBuiltInFragDepth: {
4019       return ValidateFragDepthAtDefinition(decoration, inst);
4020     }
4021     case SpvBuiltInFrontFacing: {
4022       return ValidateFrontFacingAtDefinition(decoration, inst);
4023     }
4024     case SpvBuiltInGlobalInvocationId:
4025     case SpvBuiltInLocalInvocationId:
4026     case SpvBuiltInNumWorkgroups:
4027     case SpvBuiltInWorkgroupId: {
4028       return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
4029     }
4030     case SpvBuiltInHelperInvocation: {
4031       return ValidateHelperInvocationAtDefinition(decoration, inst);
4032     }
4033     case SpvBuiltInInvocationId: {
4034       return ValidateInvocationIdAtDefinition(decoration, inst);
4035     }
4036     case SpvBuiltInInstanceIndex: {
4037       return ValidateInstanceIndexAtDefinition(decoration, inst);
4038     }
4039     case SpvBuiltInLayer:
4040     case SpvBuiltInViewportIndex: {
4041       return ValidateLayerOrViewportIndexAtDefinition(decoration, inst);
4042     }
4043     case SpvBuiltInPatchVertices: {
4044       return ValidatePatchVerticesAtDefinition(decoration, inst);
4045     }
4046     case SpvBuiltInPointCoord: {
4047       return ValidatePointCoordAtDefinition(decoration, inst);
4048     }
4049     case SpvBuiltInPointSize: {
4050       return ValidatePointSizeAtDefinition(decoration, inst);
4051     }
4052     case SpvBuiltInPosition: {
4053       return ValidatePositionAtDefinition(decoration, inst);
4054     }
4055     case SpvBuiltInPrimitiveId: {
4056       return ValidatePrimitiveIdAtDefinition(decoration, inst);
4057     }
4058     case SpvBuiltInSampleId: {
4059       return ValidateSampleIdAtDefinition(decoration, inst);
4060     }
4061     case SpvBuiltInSampleMask: {
4062       return ValidateSampleMaskAtDefinition(decoration, inst);
4063     }
4064     case SpvBuiltInSamplePosition: {
4065       return ValidateSamplePositionAtDefinition(decoration, inst);
4066     }
4067     case SpvBuiltInSubgroupId:
4068     case SpvBuiltInNumSubgroups: {
4069       return ValidateComputeI32InputAtDefinition(decoration, inst);
4070     }
4071     case SpvBuiltInSubgroupLocalInvocationId:
4072     case SpvBuiltInSubgroupSize: {
4073       return ValidateI32InputAtDefinition(decoration, inst);
4074     }
4075     case SpvBuiltInSubgroupEqMask:
4076     case SpvBuiltInSubgroupGeMask:
4077     case SpvBuiltInSubgroupGtMask:
4078     case SpvBuiltInSubgroupLeMask:
4079     case SpvBuiltInSubgroupLtMask: {
4080       return ValidateI32Vec4InputAtDefinition(decoration, inst);
4081     }
4082     case SpvBuiltInTessCoord: {
4083       return ValidateTessCoordAtDefinition(decoration, inst);
4084     }
4085     case SpvBuiltInTessLevelOuter: {
4086       return ValidateTessLevelOuterAtDefinition(decoration, inst);
4087     }
4088     case SpvBuiltInTessLevelInner: {
4089       return ValidateTessLevelInnerAtDefinition(decoration, inst);
4090     }
4091     case SpvBuiltInVertexIndex: {
4092       return ValidateVertexIndexAtDefinition(decoration, inst);
4093     }
4094     case SpvBuiltInWorkgroupSize: {
4095       return ValidateWorkgroupSizeAtDefinition(decoration, inst);
4096     }
4097     case SpvBuiltInVertexId: {
4098       return ValidateVertexIdAtDefinition(decoration, inst);
4099     }
4100     case SpvBuiltInLocalInvocationIndex: {
4101       return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
4102     }
4103     case SpvBuiltInWarpsPerSMNV:
4104     case SpvBuiltInSMCountNV:
4105     case SpvBuiltInWarpIDNV:
4106     case SpvBuiltInSMIDNV: {
4107       return ValidateSMBuiltinsAtDefinition(decoration, inst);
4108     }
4109     case SpvBuiltInBaseInstance:
4110     case SpvBuiltInBaseVertex: {
4111       return ValidateBaseInstanceOrVertexAtDefinition(decoration, inst);
4112     }
4113     case SpvBuiltInDrawIndex: {
4114       return ValidateDrawIndexAtDefinition(decoration, inst);
4115     }
4116     case SpvBuiltInViewIndex: {
4117       return ValidateViewIndexAtDefinition(decoration, inst);
4118     }
4119     case SpvBuiltInDeviceIndex: {
4120       return ValidateDeviceIndexAtDefinition(decoration, inst);
4121     }
4122     case SpvBuiltInFragInvocationCountEXT: {
4123       // alias SpvBuiltInInvocationsPerPixelNV
4124       return ValidateFragInvocationCountAtDefinition(decoration, inst);
4125     }
4126     case SpvBuiltInFragSizeEXT: {
4127       // alias SpvBuiltInFragmentSizeNV
4128       return ValidateFragSizeAtDefinition(decoration, inst);
4129     }
4130     case SpvBuiltInFragStencilRefEXT: {
4131       return ValidateFragStencilRefAtDefinition(decoration, inst);
4132     }
4133     case SpvBuiltInFullyCoveredEXT:{
4134       return ValidateFullyCoveredAtDefinition(decoration, inst);
4135     }
4136     // Ray tracing builtins
4137     case SpvBuiltInHitKindKHR:  // alias SpvBuiltInHitKindNV
4138     case SpvBuiltInHitTNV:      // NOT present in KHR
4139     case SpvBuiltInInstanceId:
4140     case SpvBuiltInLaunchIdKHR:           // alias SpvBuiltInLaunchIdNV
4141     case SpvBuiltInLaunchSizeKHR:         // alias SpvBuiltInLaunchSizeNV
4142     case SpvBuiltInWorldRayOriginKHR:     // alias SpvBuiltInWorldRayOriginNV
4143     case SpvBuiltInWorldRayDirectionKHR:  // alias SpvBuiltInWorldRayDirectionNV
4144     case SpvBuiltInObjectRayOriginKHR:    // alias SpvBuiltInObjectRayOriginNV
4145     case SpvBuiltInObjectRayDirectionKHR:   // alias
4146                                             // SpvBuiltInObjectRayDirectionNV
4147     case SpvBuiltInRayTminKHR:              // alias SpvBuiltInRayTminNV
4148     case SpvBuiltInRayTmaxKHR:              // alias SpvBuiltInRayTmaxNV
4149     case SpvBuiltInInstanceCustomIndexKHR:  // alias
4150                                             // SpvBuiltInInstanceCustomIndexNV
4151     case SpvBuiltInObjectToWorldKHR:        // alias SpvBuiltInObjectToWorldNV
4152     case SpvBuiltInWorldToObjectKHR:        // alias SpvBuiltInWorldToObjectNV
4153     case SpvBuiltInIncomingRayFlagsKHR:    // alias SpvBuiltInIncomingRayFlagsNV
4154     case SpvBuiltInRayGeometryIndexKHR: {  // NOT present in NV
4155       return ValidateRayTracingBuiltinsAtDefinition(decoration, inst);
4156     }
4157     case SpvBuiltInWorkDim:
4158     case SpvBuiltInGlobalSize:
4159     case SpvBuiltInEnqueuedWorkgroupSize:
4160     case SpvBuiltInGlobalOffset:
4161     case SpvBuiltInGlobalLinearId:
4162     case SpvBuiltInSubgroupMaxSize:
4163     case SpvBuiltInNumEnqueuedSubgroups:
4164     case SpvBuiltInBaryCoordNoPerspAMD:
4165     case SpvBuiltInBaryCoordNoPerspCentroidAMD:
4166     case SpvBuiltInBaryCoordNoPerspSampleAMD:
4167     case SpvBuiltInBaryCoordSmoothAMD:
4168     case SpvBuiltInBaryCoordSmoothCentroidAMD:
4169     case SpvBuiltInBaryCoordSmoothSampleAMD:
4170     case SpvBuiltInBaryCoordPullModelAMD:
4171     case SpvBuiltInViewportMaskNV:
4172     case SpvBuiltInSecondaryPositionNV:
4173     case SpvBuiltInSecondaryViewportMaskNV:
4174     case SpvBuiltInPositionPerViewNV:
4175     case SpvBuiltInViewportMaskPerViewNV:
4176     case SpvBuiltInMax:
4177     case SpvBuiltInTaskCountNV:
4178     case SpvBuiltInPrimitiveCountNV:
4179     case SpvBuiltInPrimitiveIndicesNV:
4180     case SpvBuiltInClipDistancePerViewNV:
4181     case SpvBuiltInCullDistancePerViewNV:
4182     case SpvBuiltInLayerPerViewNV:
4183     case SpvBuiltInMeshViewCountNV:
4184     case SpvBuiltInMeshViewIndicesNV:
4185     case SpvBuiltInBaryCoordNV:
4186     case SpvBuiltInBaryCoordNoPerspNV:
4187     case SpvBuiltInCurrentRayTimeNV:
4188       // No validation rules (for the moment).
4189       break;
4190 
4191     case SpvBuiltInPrimitiveShadingRateKHR: {
4192       return ValidatePrimitiveShadingRateAtDefinition(decoration, inst);
4193     }
4194     case SpvBuiltInShadingRateKHR: {
4195       return ValidateShadingRateAtDefinition(decoration, inst);
4196     }
4197   }
4198   return SPV_SUCCESS;
4199 }
4200 
ValidateBuiltInsAtDefinition()4201 spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() {
4202   for (const auto& kv : _.id_decorations()) {
4203     const uint32_t id = kv.first;
4204     const auto& decorations = kv.second;
4205     if (decorations.empty()) {
4206       continue;
4207     }
4208 
4209     const Instruction* inst = _.FindDef(id);
4210     assert(inst);
4211 
4212     for (const auto& decoration : kv.second) {
4213       if (decoration.dec_type() != SpvDecorationBuiltIn) {
4214         continue;
4215       }
4216 
4217       if (spv_result_t error =
4218               ValidateSingleBuiltInAtDefinition(decoration, *inst)) {
4219         return error;
4220       }
4221     }
4222   }
4223 
4224   return SPV_SUCCESS;
4225 }
4226 
Run()4227 spv_result_t BuiltInsValidator::Run() {
4228   // First pass: validate all built-ins at definition and seed
4229   // id_to_at_reference_checks_ with built-ins.
4230   if (auto error = ValidateBuiltInsAtDefinition()) {
4231     return error;
4232   }
4233 
4234   if (id_to_at_reference_checks_.empty()) {
4235     // No validation tasks were seeded. Nothing else to do.
4236     return SPV_SUCCESS;
4237   }
4238 
4239   // Second pass: validate every id reference in the module using
4240   // rules in id_to_at_reference_checks_.
4241   for (const Instruction& inst : _.ordered_instructions()) {
4242     Update(inst);
4243 
4244     std::set<uint32_t> already_checked;
4245 
4246     for (const auto& operand : inst.operands()) {
4247       if (!spvIsIdType(operand.type)) {
4248         // Not id.
4249         continue;
4250       }
4251 
4252       const uint32_t id = inst.word(operand.offset);
4253       if (id == inst.id()) {
4254         // No need to check result id.
4255         continue;
4256       }
4257 
4258       if (!already_checked.insert(id).second) {
4259         // The instruction has already referenced this id.
4260         continue;
4261       }
4262 
4263       // Instruction references the id. Run all checks associated with the id
4264       // on the instruction. id_to_at_reference_checks_ can be modified in the
4265       // process, iterators are safe because it's a tree-based map.
4266       const auto it = id_to_at_reference_checks_.find(id);
4267       if (it != id_to_at_reference_checks_.end()) {
4268         for (const auto& check : it->second) {
4269           if (spv_result_t error = check(inst)) {
4270             return error;
4271           }
4272         }
4273       }
4274     }
4275   }
4276 
4277   return SPV_SUCCESS;
4278 }
4279 
4280 }  // namespace
4281 
4282 // Validates correctness of built-in variables.
ValidateBuiltIns(ValidationState_t & _)4283 spv_result_t ValidateBuiltIns(ValidationState_t& _) {
4284   BuiltInsValidator validator(_);
4285   return validator.Run();
4286 }
4287 
4288 }  // namespace val
4289 }  // namespace spvtools
4290