• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Validates correctness of built-in variables.
16 
17 #include "source/val/validate.h"
18 
19 #include <functional>
20 #include <list>
21 #include <map>
22 #include <set>
23 #include <sstream>
24 #include <stack>
25 #include <string>
26 #include <unordered_map>
27 #include <vector>
28 
29 #include "source/diagnostic.h"
30 #include "source/opcode.h"
31 #include "source/spirv_target_env.h"
32 #include "source/util/bitutils.h"
33 #include "source/val/instruction.h"
34 #include "source/val/validation_state.h"
35 
36 namespace spvtools {
37 namespace val {
38 namespace {
39 
40 // Returns a short textual description of the id defined by the given
41 // instruction.
GetIdDesc(const Instruction & inst)42 std::string GetIdDesc(const Instruction& inst) {
43   std::ostringstream ss;
44   ss << "ID <" << inst.id() << "> (Op" << spvOpcodeString(inst.opcode()) << ")";
45   return ss.str();
46 }
47 
48 // Gets underlying data type which is
49 // - member type if instruction is OpTypeStruct
50 //   (member index is taken from decoration).
51 // - data type if id creates a pointer.
52 // - type of the constant if instruction is OpConst or OpSpecConst.
53 //
54 // Fails in any other case. The function is based on built-ins allowed by
55 // the Vulkan spec.
56 // TODO: If non-Vulkan validation rules are added then it might need
57 // to be refactored.
GetUnderlyingType(ValidationState_t & _,const Decoration & decoration,const Instruction & inst,uint32_t * underlying_type)58 spv_result_t GetUnderlyingType(ValidationState_t& _,
59                                const Decoration& decoration,
60                                const Instruction& inst,
61                                uint32_t* underlying_type) {
62   if (decoration.struct_member_index() != Decoration::kInvalidMember) {
63     if (inst.opcode() != SpvOpTypeStruct) {
64       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
65              << GetIdDesc(inst)
66              << "Attempted to get underlying data type via member index for "
67                 "non-struct type.";
68     }
69     *underlying_type = inst.word(decoration.struct_member_index() + 2);
70     return SPV_SUCCESS;
71   }
72 
73   if (inst.opcode() == SpvOpTypeStruct) {
74     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
75            << GetIdDesc(inst)
76            << " did not find an member index to get underlying data type for "
77               "struct type.";
78   }
79 
80   if (spvOpcodeIsConstant(inst.opcode())) {
81     *underlying_type = inst.type_id();
82     return SPV_SUCCESS;
83   }
84 
85   uint32_t storage_class = 0;
86   if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) {
87     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
88            << GetIdDesc(inst)
89            << " is decorated with BuiltIn. BuiltIn decoration should only be "
90               "applied to struct types, variables and constants.";
91   }
92   return SPV_SUCCESS;
93 }
94 
95 // Returns Storage Class used by the instruction if applicable.
96 // Returns SpvStorageClassMax if not.
GetStorageClass(const Instruction & inst)97 SpvStorageClass GetStorageClass(const Instruction& inst) {
98   switch (inst.opcode()) {
99     case SpvOpTypePointer:
100     case SpvOpTypeForwardPointer: {
101       return SpvStorageClass(inst.word(2));
102     }
103     case SpvOpVariable: {
104       return SpvStorageClass(inst.word(3));
105     }
106     case SpvOpGenericCastToPtrExplicit: {
107       return SpvStorageClass(inst.word(4));
108     }
109     default: { break; }
110   }
111   return SpvStorageClassMax;
112 }
113 
IsBuiltInValidForWebGPU(SpvBuiltIn label)114 bool IsBuiltInValidForWebGPU(SpvBuiltIn label) {
115   switch (label) {
116     case SpvBuiltInPosition:
117     case SpvBuiltInVertexIndex:
118     case SpvBuiltInInstanceIndex:
119     case SpvBuiltInFrontFacing:
120     case SpvBuiltInFragCoord:
121     case SpvBuiltInFragDepth:
122     case SpvBuiltInNumWorkgroups:
123     case SpvBuiltInWorkgroupSize:
124     case SpvBuiltInLocalInvocationId:
125     case SpvBuiltInGlobalInvocationId:
126     case SpvBuiltInLocalInvocationIndex: {
127       return true;
128     }
129     default:
130       break;
131   }
132 
133   return false;
134 }
135 
136 // Helper class managing validation of built-ins.
137 // TODO: Generic functionality of this class can be moved into
138 // ValidationState_t to be made available to other users.
139 class BuiltInsValidator {
140  public:
BuiltInsValidator(ValidationState_t & vstate)141   BuiltInsValidator(ValidationState_t& vstate) : _(vstate) {}
142 
143   // Run validation.
144   spv_result_t Run();
145 
146  private:
147   // Goes through all decorations in the module, if decoration is BuiltIn
148   // calls ValidateSingleBuiltInAtDefinition().
149   spv_result_t ValidateBuiltInsAtDefinition();
150 
151   // Validates the instruction defining an id with built-in decoration.
152   // Can be called multiple times for the same id, if multiple built-ins are
153   // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed.
154   spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration,
155                                                  const Instruction& inst);
156 
157   // The following section contains functions which are called when id defined
158   // by |inst| is decorated with BuiltIn |decoration|.
159   // Most functions are specific to a single built-in and have naming scheme:
160   // ValidateXYZAtDefinition. Some functions are common to multiple kinds of
161   // BuiltIn.
162   spv_result_t ValidateClipOrCullDistanceAtDefinition(
163       const Decoration& decoration, const Instruction& inst);
164   spv_result_t ValidateFragCoordAtDefinition(const Decoration& decoration,
165                                              const Instruction& inst);
166   spv_result_t ValidateFragDepthAtDefinition(const Decoration& decoration,
167                                              const Instruction& inst);
168   spv_result_t ValidateFrontFacingAtDefinition(const Decoration& decoration,
169                                                const Instruction& inst);
170   spv_result_t ValidateHelperInvocationAtDefinition(
171       const Decoration& decoration, const Instruction& inst);
172   spv_result_t ValidateInvocationIdAtDefinition(const Decoration& decoration,
173                                                 const Instruction& inst);
174   spv_result_t ValidateInstanceIndexAtDefinition(const Decoration& decoration,
175                                                  const Instruction& inst);
176   spv_result_t ValidateLayerOrViewportIndexAtDefinition(
177       const Decoration& decoration, const Instruction& inst);
178   spv_result_t ValidatePatchVerticesAtDefinition(const Decoration& decoration,
179                                                  const Instruction& inst);
180   spv_result_t ValidatePointCoordAtDefinition(const Decoration& decoration,
181                                               const Instruction& inst);
182   spv_result_t ValidatePointSizeAtDefinition(const Decoration& decoration,
183                                              const Instruction& inst);
184   spv_result_t ValidatePositionAtDefinition(const Decoration& decoration,
185                                             const Instruction& inst);
186   spv_result_t ValidatePrimitiveIdAtDefinition(const Decoration& decoration,
187                                                const Instruction& inst);
188   spv_result_t ValidateSampleIdAtDefinition(const Decoration& decoration,
189                                             const Instruction& inst);
190   spv_result_t ValidateSampleMaskAtDefinition(const Decoration& decoration,
191                                               const Instruction& inst);
192   spv_result_t ValidateSamplePositionAtDefinition(const Decoration& decoration,
193                                                   const Instruction& inst);
194   spv_result_t ValidateTessCoordAtDefinition(const Decoration& decoration,
195                                              const Instruction& inst);
196   spv_result_t ValidateTessLevelOuterAtDefinition(const Decoration& decoration,
197                                                   const Instruction& inst);
198   spv_result_t ValidateTessLevelInnerAtDefinition(const Decoration& decoration,
199                                                   const Instruction& inst);
200   spv_result_t ValidateVertexIndexAtDefinition(const Decoration& decoration,
201                                                const Instruction& inst);
202   spv_result_t ValidateVertexIdOrInstanceIdAtDefinition(
203       const Decoration& decoration, const Instruction& inst);
204   spv_result_t ValidateLocalInvocationIndexAtDefinition(
205       const Decoration& decoration, const Instruction& inst);
206   spv_result_t ValidateWorkgroupSizeAtDefinition(const Decoration& decoration,
207                                                  const Instruction& inst);
208   // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
209   spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition(
210       const Decoration& decoration, const Instruction& inst);
211   spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration,
212                                               const Instruction& inst);
213 
214   // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask,
215   // SubgroupLeMask.
216   spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration,
217                                                 const Instruction& inst);
218   // Used for SubgroupLocalInvocationId, SubgroupSize.
219   spv_result_t ValidateI32InputAtDefinition(const Decoration& decoration,
220                                             const Instruction& inst);
221   // Used for SubgroupId, NumSubgroups.
222   spv_result_t ValidateComputeI32InputAtDefinition(const Decoration& decoration,
223                                                    const Instruction& inst);
224 
225   // The following section contains functions which are called when id defined
226   // by |referenced_inst| is
227   // 1. referenced by |referenced_from_inst|
228   // 2. dependent on |built_in_inst| which is decorated with BuiltIn
229   // |decoration|. Most functions are specific to a single built-in and have
230   // naming scheme: ValidateXYZAtReference. Some functions are common to
231   // multiple kinds of BuiltIn.
232   spv_result_t ValidateFragCoordAtReference(
233       const Decoration& decoration, const Instruction& built_in_inst,
234       const Instruction& referenced_inst,
235       const Instruction& referenced_from_inst);
236 
237   spv_result_t ValidateFragDepthAtReference(
238       const Decoration& decoration, const Instruction& built_in_inst,
239       const Instruction& referenced_inst,
240       const Instruction& referenced_from_inst);
241 
242   spv_result_t ValidateFrontFacingAtReference(
243       const Decoration& decoration, const Instruction& built_in_inst,
244       const Instruction& referenced_inst,
245       const Instruction& referenced_from_inst);
246 
247   spv_result_t ValidateHelperInvocationAtReference(
248       const Decoration& decoration, const Instruction& built_in_inst,
249       const Instruction& referenced_inst,
250       const Instruction& referenced_from_inst);
251 
252   spv_result_t ValidateInvocationIdAtReference(
253       const Decoration& decoration, const Instruction& built_in_inst,
254       const Instruction& referenced_inst,
255       const Instruction& referenced_from_inst);
256 
257   spv_result_t ValidateInstanceIdAtReference(
258       const Decoration& decoration, const Instruction& built_in_inst,
259       const Instruction& referenced_inst,
260       const Instruction& referenced_from_inst);
261 
262   spv_result_t ValidateInstanceIndexAtReference(
263       const Decoration& decoration, const Instruction& built_in_inst,
264       const Instruction& referenced_inst,
265       const Instruction& referenced_from_inst);
266 
267   spv_result_t ValidatePatchVerticesAtReference(
268       const Decoration& decoration, const Instruction& built_in_inst,
269       const Instruction& referenced_inst,
270       const Instruction& referenced_from_inst);
271 
272   spv_result_t ValidatePointCoordAtReference(
273       const Decoration& decoration, const Instruction& built_in_inst,
274       const Instruction& referenced_inst,
275       const Instruction& referenced_from_inst);
276 
277   spv_result_t ValidatePointSizeAtReference(
278       const Decoration& decoration, const Instruction& built_in_inst,
279       const Instruction& referenced_inst,
280       const Instruction& referenced_from_inst);
281 
282   spv_result_t ValidatePositionAtReference(
283       const Decoration& decoration, const Instruction& built_in_inst,
284       const Instruction& referenced_inst,
285       const Instruction& referenced_from_inst);
286 
287   spv_result_t ValidatePrimitiveIdAtReference(
288       const Decoration& decoration, const Instruction& built_in_inst,
289       const Instruction& referenced_inst,
290       const Instruction& referenced_from_inst);
291 
292   spv_result_t ValidateSampleIdAtReference(
293       const Decoration& decoration, const Instruction& built_in_inst,
294       const Instruction& referenced_inst,
295       const Instruction& referenced_from_inst);
296 
297   spv_result_t ValidateSampleMaskAtReference(
298       const Decoration& decoration, const Instruction& built_in_inst,
299       const Instruction& referenced_inst,
300       const Instruction& referenced_from_inst);
301 
302   spv_result_t ValidateSamplePositionAtReference(
303       const Decoration& decoration, const Instruction& built_in_inst,
304       const Instruction& referenced_inst,
305       const Instruction& referenced_from_inst);
306 
307   spv_result_t ValidateTessCoordAtReference(
308       const Decoration& decoration, const Instruction& built_in_inst,
309       const Instruction& referenced_inst,
310       const Instruction& referenced_from_inst);
311 
312   spv_result_t ValidateTessLevelAtReference(
313       const Decoration& decoration, const Instruction& built_in_inst,
314       const Instruction& referenced_inst,
315       const Instruction& referenced_from_inst);
316 
317   spv_result_t ValidateLocalInvocationIndexAtReference(
318       const Decoration& decoration, const Instruction& built_in_inst,
319       const Instruction& referenced_inst,
320       const Instruction& referenced_from_inst);
321 
322   spv_result_t ValidateVertexIndexAtReference(
323       const Decoration& decoration, const Instruction& built_in_inst,
324       const Instruction& referenced_inst,
325       const Instruction& referenced_from_inst);
326 
327   spv_result_t ValidateLayerOrViewportIndexAtReference(
328       const Decoration& decoration, const Instruction& built_in_inst,
329       const Instruction& referenced_inst,
330       const Instruction& referenced_from_inst);
331 
332   spv_result_t ValidateWorkgroupSizeAtReference(
333       const Decoration& decoration, const Instruction& built_in_inst,
334       const Instruction& referenced_inst,
335       const Instruction& referenced_from_inst);
336 
337   spv_result_t ValidateClipOrCullDistanceAtReference(
338       const Decoration& decoration, const Instruction& built_in_inst,
339       const Instruction& referenced_inst,
340       const Instruction& referenced_from_inst);
341 
342   // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId.
343   spv_result_t ValidateComputeShaderI32Vec3InputAtReference(
344       const Decoration& decoration, const Instruction& built_in_inst,
345       const Instruction& referenced_inst,
346       const Instruction& referenced_from_inst);
347   // Used for SubgroupId and NumSubgroups.
348   spv_result_t ValidateComputeI32InputAtReference(
349       const Decoration& decoration, const Instruction& built_in_inst,
350       const Instruction& referenced_inst,
351       const Instruction& referenced_from_inst);
352 
353   spv_result_t ValidateSMBuiltinsAtReference(
354       const Decoration& decoration, const Instruction& built_in_inst,
355       const Instruction& referenced_inst,
356       const Instruction& referenced_from_inst);
357 
358   // Validates that |built_in_inst| is not (even indirectly) referenced from
359   // within a function which can be called with |execution_model|.
360   //
361   // |comment| - text explaining why the restriction was imposed.
362   // |decoration| - BuiltIn decoration which causes the restriction.
363   // |referenced_inst| - instruction which is dependent on |built_in_inst| and
364   //                     defines the id which was referenced.
365   // |referenced_from_inst| - instruction which references id defined by
366   //                          |referenced_inst| from within a function.
367   spv_result_t ValidateNotCalledWithExecutionModel(
368       const char* comment, SpvExecutionModel execution_model,
369       const Decoration& decoration, const Instruction& built_in_inst,
370       const Instruction& referenced_inst,
371       const Instruction& referenced_from_inst);
372 
373   // The following section contains functions which check that the decorated
374   // variable has the type specified in the function name. |diag| would be
375   // called with a corresponding error message, if validation is not successful.
376   spv_result_t ValidateBool(
377       const Decoration& decoration, const Instruction& inst,
378       const std::function<spv_result_t(const std::string& message)>& diag);
379   spv_result_t ValidateI32(
380       const Decoration& decoration, const Instruction& inst,
381       const std::function<spv_result_t(const std::string& message)>& diag);
382   spv_result_t ValidateI32Vec(
383       const Decoration& decoration, const Instruction& inst,
384       uint32_t num_components,
385       const std::function<spv_result_t(const std::string& message)>& diag);
386   spv_result_t ValidateI32Arr(
387       const Decoration& decoration, const Instruction& inst,
388       const std::function<spv_result_t(const std::string& message)>& diag);
389   spv_result_t ValidateOptionalArrayedI32(
390       const Decoration& decoration, const Instruction& inst,
391       const std::function<spv_result_t(const std::string& message)>& diag);
392   spv_result_t ValidateI32Helper(
393       const Decoration& decoration, const Instruction& inst,
394       const std::function<spv_result_t(const std::string& message)>& diag,
395       uint32_t underlying_type);
396   spv_result_t ValidateF32(
397       const Decoration& decoration, const Instruction& inst,
398       const std::function<spv_result_t(const std::string& message)>& diag);
399   spv_result_t ValidateOptionalArrayedF32(
400       const Decoration& decoration, const Instruction& inst,
401       const std::function<spv_result_t(const std::string& message)>& diag);
402   spv_result_t ValidateF32Helper(
403       const Decoration& decoration, const Instruction& inst,
404       const std::function<spv_result_t(const std::string& message)>& diag,
405       uint32_t underlying_type);
406   spv_result_t ValidateF32Vec(
407       const Decoration& decoration, const Instruction& inst,
408       uint32_t num_components,
409       const std::function<spv_result_t(const std::string& message)>& diag);
410   spv_result_t ValidateOptionalArrayedF32Vec(
411       const Decoration& decoration, const Instruction& inst,
412       uint32_t num_components,
413       const std::function<spv_result_t(const std::string& message)>& diag);
414   spv_result_t ValidateF32VecHelper(
415       const Decoration& decoration, const Instruction& inst,
416       uint32_t num_components,
417       const std::function<spv_result_t(const std::string& message)>& diag,
418       uint32_t underlying_type);
419   // If |num_components| is zero, the number of components is not checked.
420   spv_result_t ValidateF32Arr(
421       const Decoration& decoration, const Instruction& inst,
422       uint32_t num_components,
423       const std::function<spv_result_t(const std::string& message)>& diag);
424   spv_result_t ValidateOptionalArrayedF32Arr(
425       const Decoration& decoration, const Instruction& inst,
426       uint32_t num_components,
427       const std::function<spv_result_t(const std::string& message)>& diag);
428   spv_result_t ValidateF32ArrHelper(
429       const Decoration& decoration, const Instruction& inst,
430       uint32_t num_components,
431       const std::function<spv_result_t(const std::string& message)>& diag,
432       uint32_t underlying_type);
433 
434   // Generates strings like "Member #0 of struct ID <2>".
435   std::string GetDefinitionDesc(const Decoration& decoration,
436                                 const Instruction& inst) const;
437 
438   // Generates strings like "ID <51> (OpTypePointer) is referencing ID <2>
439   // (OpTypeStruct) which is decorated with BuiltIn Position".
440   std::string GetReferenceDesc(
441       const Decoration& decoration, const Instruction& built_in_inst,
442       const Instruction& referenced_inst,
443       const Instruction& referenced_from_inst,
444       SpvExecutionModel execution_model = SpvExecutionModelMax) const;
445 
446   // Generates strings like "ID <51> (OpTypePointer) uses storage class
447   // UniformConstant".
448   std::string GetStorageClassDesc(const Instruction& inst) const;
449 
450   // Updates inner working of the class. Is called sequentially for every
451   // instruction.
452   void Update(const Instruction& inst);
453 
454   ValidationState_t& _;
455 
456   // Mapping id -> list of rules which validate instruction referencing the
457   // id. Rules can create new rules and add them to this container.
458   // Using std::map, and not std::unordered_map to avoid iterator invalidation
459   // during rehashing.
460   std::map<uint32_t, std::list<std::function<spv_result_t(const Instruction&)>>>
461       id_to_at_reference_checks_;
462 
463   // Id of the function we are currently inside. 0 if not inside a function.
464   uint32_t function_id_ = 0;
465 
466   // Entry points which can (indirectly) call the current function.
467   // The pointer either points to a vector inside to function_to_entry_points_
468   // or to no_entry_points_. The pointer is guaranteed to never be null.
469   const std::vector<uint32_t> no_entry_points;
470   const std::vector<uint32_t>* entry_points_ = &no_entry_points;
471 
472   // Execution models with which the current function can be called.
473   std::set<SpvExecutionModel> execution_models_;
474 };
475 
Update(const Instruction & inst)476 void BuiltInsValidator::Update(const Instruction& inst) {
477   const SpvOp opcode = inst.opcode();
478   if (opcode == SpvOpFunction) {
479     // Entering a function.
480     assert(function_id_ == 0);
481     function_id_ = inst.id();
482     execution_models_.clear();
483     entry_points_ = &_.FunctionEntryPoints(function_id_);
484     // Collect execution models from all entry points from which the current
485     // function can be called.
486     for (const uint32_t entry_point : *entry_points_) {
487       if (const auto* models = _.GetExecutionModels(entry_point)) {
488         execution_models_.insert(models->begin(), models->end());
489       }
490     }
491   }
492 
493   if (opcode == SpvOpFunctionEnd) {
494     // Exiting a function.
495     assert(function_id_ != 0);
496     function_id_ = 0;
497     entry_points_ = &no_entry_points;
498     execution_models_.clear();
499   }
500 }
501 
GetDefinitionDesc(const Decoration & decoration,const Instruction & inst) const502 std::string BuiltInsValidator::GetDefinitionDesc(
503     const Decoration& decoration, const Instruction& inst) const {
504   std::ostringstream ss;
505   if (decoration.struct_member_index() != Decoration::kInvalidMember) {
506     assert(inst.opcode() == SpvOpTypeStruct);
507     ss << "Member #" << decoration.struct_member_index();
508     ss << " of struct ID <" << inst.id() << ">";
509   } else {
510     ss << GetIdDesc(inst);
511   }
512   return ss.str();
513 }
514 
GetReferenceDesc(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst,SpvExecutionModel execution_model) const515 std::string BuiltInsValidator::GetReferenceDesc(
516     const Decoration& decoration, const Instruction& built_in_inst,
517     const Instruction& referenced_inst, const Instruction& referenced_from_inst,
518     SpvExecutionModel execution_model) const {
519   std::ostringstream ss;
520   ss << GetIdDesc(referenced_from_inst) << " is referencing "
521      << GetIdDesc(referenced_inst);
522   if (built_in_inst.id() != referenced_inst.id()) {
523     ss << " which is dependent on " << GetIdDesc(built_in_inst);
524   }
525 
526   ss << " which is decorated with BuiltIn ";
527   ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
528                                       decoration.params()[0]);
529   if (function_id_) {
530     ss << " in function <" << function_id_ << ">";
531     if (execution_model != SpvExecutionModelMax) {
532       ss << " called with execution model ";
533       ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL,
534                                           execution_model);
535     }
536   }
537   ss << ".";
538   return ss.str();
539 }
540 
GetStorageClassDesc(const Instruction & inst) const541 std::string BuiltInsValidator::GetStorageClassDesc(
542     const Instruction& inst) const {
543   std::ostringstream ss;
544   ss << GetIdDesc(inst) << " uses storage class ";
545   ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS,
546                                       GetStorageClass(inst));
547   ss << ".";
548   return ss.str();
549 }
550 
ValidateBool(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)551 spv_result_t BuiltInsValidator::ValidateBool(
552     const Decoration& decoration, const Instruction& inst,
553     const std::function<spv_result_t(const std::string& message)>& diag) {
554   uint32_t underlying_type = 0;
555   if (spv_result_t error =
556           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
557     return error;
558   }
559 
560   if (!_.IsBoolScalarType(underlying_type)) {
561     return diag(GetDefinitionDesc(decoration, inst) + " is not a bool scalar.");
562   }
563 
564   return SPV_SUCCESS;
565 }
566 
ValidateI32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)567 spv_result_t BuiltInsValidator::ValidateI32(
568     const Decoration& decoration, const Instruction& inst,
569     const std::function<spv_result_t(const std::string& message)>& diag) {
570   uint32_t underlying_type = 0;
571   if (spv_result_t error =
572           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
573     return error;
574   }
575 
576   return ValidateI32Helper(decoration, inst, diag, underlying_type);
577 }
578 
ValidateOptionalArrayedI32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)579 spv_result_t BuiltInsValidator::ValidateOptionalArrayedI32(
580     const Decoration& decoration, const Instruction& inst,
581     const std::function<spv_result_t(const std::string& message)>& diag) {
582   uint32_t underlying_type = 0;
583   if (spv_result_t error =
584           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
585     return error;
586   }
587 
588   // Strip the array, if present.
589   if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
590     underlying_type = _.FindDef(underlying_type)->word(2u);
591   }
592 
593   return ValidateI32Helper(decoration, inst, diag, underlying_type);
594 }
595 
ValidateI32Helper(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)596 spv_result_t BuiltInsValidator::ValidateI32Helper(
597     const Decoration& decoration, const Instruction& inst,
598     const std::function<spv_result_t(const std::string& message)>& diag,
599     uint32_t underlying_type) {
600   if (!_.IsIntScalarType(underlying_type)) {
601     return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar.");
602   }
603 
604   const uint32_t bit_width = _.GetBitWidth(underlying_type);
605   if (bit_width != 32) {
606     std::ostringstream ss;
607     ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
608        << ".";
609     return diag(ss.str());
610   }
611 
612   return SPV_SUCCESS;
613 }
614 
ValidateOptionalArrayedF32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)615 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32(
616     const Decoration& decoration, const Instruction& inst,
617     const std::function<spv_result_t(const std::string& message)>& diag) {
618   uint32_t underlying_type = 0;
619   if (spv_result_t error =
620           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
621     return error;
622   }
623 
624   // Strip the array, if present.
625   if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
626     underlying_type = _.FindDef(underlying_type)->word(2u);
627   }
628 
629   return ValidateF32Helper(decoration, inst, diag, underlying_type);
630 }
631 
ValidateF32(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)632 spv_result_t BuiltInsValidator::ValidateF32(
633     const Decoration& decoration, const Instruction& inst,
634     const std::function<spv_result_t(const std::string& message)>& diag) {
635   uint32_t underlying_type = 0;
636   if (spv_result_t error =
637           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
638     return error;
639   }
640 
641   return ValidateF32Helper(decoration, inst, diag, underlying_type);
642 }
643 
ValidateF32Helper(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag,uint32_t underlying_type)644 spv_result_t BuiltInsValidator::ValidateF32Helper(
645     const Decoration& decoration, const Instruction& inst,
646     const std::function<spv_result_t(const std::string& message)>& diag,
647     uint32_t underlying_type) {
648   if (!_.IsFloatScalarType(underlying_type)) {
649     return diag(GetDefinitionDesc(decoration, inst) +
650                 " is not a float scalar.");
651   }
652 
653   const uint32_t bit_width = _.GetBitWidth(underlying_type);
654   if (bit_width != 32) {
655     std::ostringstream ss;
656     ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width
657        << ".";
658     return diag(ss.str());
659   }
660 
661   return SPV_SUCCESS;
662 }
663 
ValidateI32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)664 spv_result_t BuiltInsValidator::ValidateI32Vec(
665     const Decoration& decoration, const Instruction& inst,
666     uint32_t num_components,
667     const std::function<spv_result_t(const std::string& message)>& diag) {
668   uint32_t underlying_type = 0;
669   if (spv_result_t error =
670           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
671     return error;
672   }
673 
674   if (!_.IsIntVectorType(underlying_type)) {
675     return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector.");
676   }
677 
678   const uint32_t actual_num_components = _.GetDimension(underlying_type);
679   if (_.GetDimension(underlying_type) != num_components) {
680     std::ostringstream ss;
681     ss << GetDefinitionDesc(decoration, inst) << " has "
682        << actual_num_components << " components.";
683     return diag(ss.str());
684   }
685 
686   const uint32_t bit_width = _.GetBitWidth(underlying_type);
687   if (bit_width != 32) {
688     std::ostringstream ss;
689     ss << GetDefinitionDesc(decoration, inst)
690        << " has components with bit width " << bit_width << ".";
691     return diag(ss.str());
692   }
693 
694   return SPV_SUCCESS;
695 }
696 
ValidateOptionalArrayedF32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)697 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec(
698     const Decoration& decoration, const Instruction& inst,
699     uint32_t num_components,
700     const std::function<spv_result_t(const std::string& message)>& diag) {
701   uint32_t underlying_type = 0;
702   if (spv_result_t error =
703           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
704     return error;
705   }
706 
707   // Strip the array, if present.
708   if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
709     underlying_type = _.FindDef(underlying_type)->word(2u);
710   }
711 
712   return ValidateF32VecHelper(decoration, inst, num_components, diag,
713                               underlying_type);
714 }
715 
ValidateF32Vec(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)716 spv_result_t BuiltInsValidator::ValidateF32Vec(
717     const Decoration& decoration, const Instruction& inst,
718     uint32_t num_components,
719     const std::function<spv_result_t(const std::string& message)>& diag) {
720   uint32_t underlying_type = 0;
721   if (spv_result_t error =
722           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
723     return error;
724   }
725 
726   return ValidateF32VecHelper(decoration, inst, num_components, diag,
727                               underlying_type);
728 }
729 
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)730 spv_result_t BuiltInsValidator::ValidateF32VecHelper(
731     const Decoration& decoration, const Instruction& inst,
732     uint32_t num_components,
733     const std::function<spv_result_t(const std::string& message)>& diag,
734     uint32_t underlying_type) {
735   if (!_.IsFloatVectorType(underlying_type)) {
736     return diag(GetDefinitionDesc(decoration, inst) +
737                 " is not a float vector.");
738   }
739 
740   const uint32_t actual_num_components = _.GetDimension(underlying_type);
741   if (_.GetDimension(underlying_type) != num_components) {
742     std::ostringstream ss;
743     ss << GetDefinitionDesc(decoration, inst) << " has "
744        << actual_num_components << " components.";
745     return diag(ss.str());
746   }
747 
748   const uint32_t bit_width = _.GetBitWidth(underlying_type);
749   if (bit_width != 32) {
750     std::ostringstream ss;
751     ss << GetDefinitionDesc(decoration, inst)
752        << " has components with bit width " << bit_width << ".";
753     return diag(ss.str());
754   }
755 
756   return SPV_SUCCESS;
757 }
758 
ValidateI32Arr(const Decoration & decoration,const Instruction & inst,const std::function<spv_result_t (const std::string & message)> & diag)759 spv_result_t BuiltInsValidator::ValidateI32Arr(
760     const Decoration& decoration, const Instruction& inst,
761     const std::function<spv_result_t(const std::string& message)>& diag) {
762   uint32_t underlying_type = 0;
763   if (spv_result_t error =
764           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
765     return error;
766   }
767 
768   const Instruction* const type_inst = _.FindDef(underlying_type);
769   if (type_inst->opcode() != SpvOpTypeArray) {
770     return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
771   }
772 
773   const uint32_t component_type = type_inst->word(2);
774   if (!_.IsIntScalarType(component_type)) {
775     return diag(GetDefinitionDesc(decoration, inst) +
776                 " components are not int scalar.");
777   }
778 
779   const uint32_t bit_width = _.GetBitWidth(component_type);
780   if (bit_width != 32) {
781     std::ostringstream ss;
782     ss << GetDefinitionDesc(decoration, inst)
783        << " has components with bit width " << bit_width << ".";
784     return diag(ss.str());
785   }
786 
787   return SPV_SUCCESS;
788 }
789 
ValidateF32Arr(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)790 spv_result_t BuiltInsValidator::ValidateF32Arr(
791     const Decoration& decoration, const Instruction& inst,
792     uint32_t num_components,
793     const std::function<spv_result_t(const std::string& message)>& diag) {
794   uint32_t underlying_type = 0;
795   if (spv_result_t error =
796           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
797     return error;
798   }
799 
800   return ValidateF32ArrHelper(decoration, inst, num_components, diag,
801                               underlying_type);
802 }
803 
ValidateOptionalArrayedF32Arr(const Decoration & decoration,const Instruction & inst,uint32_t num_components,const std::function<spv_result_t (const std::string & message)> & diag)804 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Arr(
805     const Decoration& decoration, const Instruction& inst,
806     uint32_t num_components,
807     const std::function<spv_result_t(const std::string& message)>& diag) {
808   uint32_t underlying_type = 0;
809   if (spv_result_t error =
810           GetUnderlyingType(_, decoration, inst, &underlying_type)) {
811     return error;
812   }
813 
814   // Strip an extra layer of arraying if present.
815   if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) {
816     uint32_t subtype = _.FindDef(underlying_type)->word(2u);
817     if (_.GetIdOpcode(subtype) == SpvOpTypeArray) {
818       underlying_type = subtype;
819     }
820   }
821 
822   return ValidateF32ArrHelper(decoration, inst, num_components, diag,
823                               underlying_type);
824 }
825 
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)826 spv_result_t BuiltInsValidator::ValidateF32ArrHelper(
827     const Decoration& decoration, const Instruction& inst,
828     uint32_t num_components,
829     const std::function<spv_result_t(const std::string& message)>& diag,
830     uint32_t underlying_type) {
831   const Instruction* const type_inst = _.FindDef(underlying_type);
832   if (type_inst->opcode() != SpvOpTypeArray) {
833     return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
834   }
835 
836   const uint32_t component_type = type_inst->word(2);
837   if (!_.IsFloatScalarType(component_type)) {
838     return diag(GetDefinitionDesc(decoration, inst) +
839                 " components are not float scalar.");
840   }
841 
842   const uint32_t bit_width = _.GetBitWidth(component_type);
843   if (bit_width != 32) {
844     std::ostringstream ss;
845     ss << GetDefinitionDesc(decoration, inst)
846        << " has components with bit width " << bit_width << ".";
847     return diag(ss.str());
848   }
849 
850   if (num_components != 0) {
851     uint64_t actual_num_components = 0;
852     if (!_.GetConstantValUint64(type_inst->word(3), &actual_num_components)) {
853       assert(0 && "Array type definition is corrupt");
854     }
855     if (actual_num_components != num_components) {
856       std::ostringstream ss;
857       ss << GetDefinitionDesc(decoration, inst) << " has "
858          << actual_num_components << " components.";
859       return diag(ss.str());
860     }
861   }
862 
863   return SPV_SUCCESS;
864 }
865 
ValidateNotCalledWithExecutionModel(const char * comment,SpvExecutionModel execution_model,const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)866 spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel(
867     const char* comment, SpvExecutionModel execution_model,
868     const Decoration& decoration, const Instruction& built_in_inst,
869     const Instruction& referenced_inst,
870     const Instruction& referenced_from_inst) {
871   if (function_id_) {
872     if (execution_models_.count(execution_model)) {
873       const char* execution_model_str = _.grammar().lookupOperandName(
874           SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model);
875       const char* built_in_str = _.grammar().lookupOperandName(
876           SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]);
877       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
878              << comment << " " << GetIdDesc(referenced_inst) << " depends on "
879              << GetIdDesc(built_in_inst) << " which is decorated with BuiltIn "
880              << built_in_str << "."
881              << " Id <" << referenced_inst.id() << "> is later referenced by "
882              << GetIdDesc(referenced_from_inst) << " in function <"
883              << function_id_ << "> which is called with execution model "
884              << execution_model_str << ".";
885     }
886   } else {
887     // Propagate this rule to all dependant ids in the global scope.
888     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
889         std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
890                   comment, execution_model, decoration, built_in_inst,
891                   referenced_from_inst, std::placeholders::_1));
892   }
893   return SPV_SUCCESS;
894 }
895 
ValidateClipOrCullDistanceAtDefinition(const Decoration & decoration,const Instruction & inst)896 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtDefinition(
897     const Decoration& decoration, const Instruction& inst) {
898   // Seed at reference checks with this built-in.
899   return ValidateClipOrCullDistanceAtReference(decoration, inst, inst, inst);
900 }
901 
ValidateClipOrCullDistanceAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)902 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference(
903     const Decoration& decoration, const Instruction& built_in_inst,
904     const Instruction& referenced_inst,
905     const Instruction& referenced_from_inst) {
906   if (spvIsVulkanEnv(_.context()->target_env)) {
907     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
908     if (storage_class != SpvStorageClassMax &&
909         storage_class != SpvStorageClassInput &&
910         storage_class != SpvStorageClassOutput) {
911       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
912              << "Vulkan spec allows BuiltIn "
913              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
914                                               decoration.params()[0])
915              << " to be only used for variables with Input or Output storage "
916                 "class. "
917              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
918                                  referenced_from_inst)
919              << " " << GetStorageClassDesc(referenced_from_inst);
920     }
921 
922     if (storage_class == SpvStorageClassInput) {
923       assert(function_id_ == 0);
924       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
925           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
926           "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
927           "used for variables with Input storage class if execution model is "
928           "Vertex.",
929           SpvExecutionModelVertex, decoration, built_in_inst,
930           referenced_from_inst, std::placeholders::_1));
931     }
932 
933     if (storage_class == SpvStorageClassOutput) {
934       assert(function_id_ == 0);
935       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
936           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
937           "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be "
938           "used for variables with Output storage class if execution model is "
939           "Fragment.",
940           SpvExecutionModelFragment, decoration, built_in_inst,
941           referenced_from_inst, std::placeholders::_1));
942     }
943 
944     for (const SpvExecutionModel execution_model : execution_models_) {
945       switch (execution_model) {
946         case SpvExecutionModelFragment:
947         case SpvExecutionModelVertex: {
948           if (spv_result_t error = ValidateF32Arr(
949                   decoration, built_in_inst, /* Any number of components */ 0,
950                   [this, &decoration, &referenced_from_inst](
951                       const std::string& message) -> spv_result_t {
952                     return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
953                            << "According to the Vulkan spec BuiltIn "
954                            << _.grammar().lookupOperandName(
955                                   SPV_OPERAND_TYPE_BUILT_IN,
956                                   decoration.params()[0])
957                            << " variable needs to be a 32-bit float array. "
958                            << message;
959                   })) {
960             return error;
961           }
962           break;
963         }
964         case SpvExecutionModelTessellationControl:
965         case SpvExecutionModelTessellationEvaluation:
966         case SpvExecutionModelGeometry:
967         case SpvExecutionModelMeshNV: {
968           if (decoration.struct_member_index() != Decoration::kInvalidMember) {
969             // The outer level of array is applied on the variable.
970             if (spv_result_t error = ValidateF32Arr(
971                     decoration, built_in_inst, /* Any number of components */ 0,
972                     [this, &decoration, &referenced_from_inst](
973                         const std::string& message) -> spv_result_t {
974                       return _.diag(SPV_ERROR_INVALID_DATA,
975                                     &referenced_from_inst)
976                              << "According to the Vulkan spec BuiltIn "
977                              << _.grammar().lookupOperandName(
978                                     SPV_OPERAND_TYPE_BUILT_IN,
979                                     decoration.params()[0])
980                              << " variable needs to be a 32-bit float array. "
981                              << message;
982                     })) {
983               return error;
984             }
985           } else {
986             if (spv_result_t error = ValidateOptionalArrayedF32Arr(
987                     decoration, built_in_inst, /* Any number of components */ 0,
988                     [this, &decoration, &referenced_from_inst](
989                         const std::string& message) -> spv_result_t {
990                       return _.diag(SPV_ERROR_INVALID_DATA,
991                                     &referenced_from_inst)
992                              << "According to the Vulkan spec BuiltIn "
993                              << _.grammar().lookupOperandName(
994                                     SPV_OPERAND_TYPE_BUILT_IN,
995                                     decoration.params()[0])
996                              << " variable needs to be a 32-bit float array. "
997                              << message;
998                     })) {
999               return error;
1000             }
1001           }
1002           break;
1003         }
1004 
1005         default: {
1006           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1007                  << "Vulkan spec allows BuiltIn "
1008                  << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
1009                                                   decoration.params()[0])
1010                  << " to be used only with Fragment, Vertex, "
1011                     "TessellationControl, TessellationEvaluation or Geometry "
1012                     "execution models. "
1013                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1014                                      referenced_from_inst, execution_model);
1015         }
1016       }
1017     }
1018   }
1019 
1020   if (function_id_ == 0) {
1021     // Propagate this rule to all dependant ids in the global scope.
1022     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1023         std::bind(&BuiltInsValidator::ValidateClipOrCullDistanceAtReference,
1024                   this, decoration, built_in_inst, referenced_from_inst,
1025                   std::placeholders::_1));
1026   }
1027 
1028   return SPV_SUCCESS;
1029 }
1030 
ValidateFragCoordAtDefinition(const Decoration & decoration,const Instruction & inst)1031 spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition(
1032     const Decoration& decoration, const Instruction& inst) {
1033   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1034     if (spv_result_t error = ValidateF32Vec(
1035             decoration, inst, 4,
1036             [this, &inst](const std::string& message) -> spv_result_t {
1037               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1038                      << "According to the "
1039                      << spvLogStringForEnv(_.context()->target_env)
1040                      << " spec BuiltIn FragCoord "
1041                         "variable needs to be a 4-component 32-bit float "
1042                         "vector. "
1043                      << message;
1044             })) {
1045       return error;
1046     }
1047   }
1048 
1049   // Seed at reference checks with this built-in.
1050   return ValidateFragCoordAtReference(decoration, inst, inst, inst);
1051 }
1052 
ValidateFragCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1053 spv_result_t BuiltInsValidator::ValidateFragCoordAtReference(
1054     const Decoration& decoration, const Instruction& built_in_inst,
1055     const Instruction& referenced_inst,
1056     const Instruction& referenced_from_inst) {
1057   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1058     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1059     if (storage_class != SpvStorageClassMax &&
1060         storage_class != SpvStorageClassInput) {
1061       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1062              << spvLogStringForEnv(_.context()->target_env)
1063              << " spec allows BuiltIn FragCoord to be only used for "
1064                 "variables with Input storage class. "
1065              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1066                                  referenced_from_inst)
1067              << " " << GetStorageClassDesc(referenced_from_inst);
1068     }
1069 
1070     for (const SpvExecutionModel execution_model : execution_models_) {
1071       if (execution_model != SpvExecutionModelFragment) {
1072         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1073                << spvLogStringForEnv(_.context()->target_env)
1074                << " spec allows BuiltIn FragCoord to be used only with "
1075                   "Fragment execution model. "
1076                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1077                                    referenced_from_inst, execution_model);
1078       }
1079     }
1080   }
1081 
1082   if (function_id_ == 0) {
1083     // Propagate this rule to all dependant ids in the global scope.
1084     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1085         &BuiltInsValidator::ValidateFragCoordAtReference, this, decoration,
1086         built_in_inst, referenced_from_inst, std::placeholders::_1));
1087   }
1088 
1089   return SPV_SUCCESS;
1090 }
1091 
ValidateFragDepthAtDefinition(const Decoration & decoration,const Instruction & inst)1092 spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition(
1093     const Decoration& decoration, const Instruction& inst) {
1094   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1095     if (spv_result_t error = ValidateF32(
1096             decoration, inst,
1097             [this, &inst](const std::string& message) -> spv_result_t {
1098               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1099                      << "According to the "
1100                      << spvLogStringForEnv(_.context()->target_env)
1101                      << " spec BuiltIn FragDepth "
1102                         "variable needs to be a 32-bit float scalar. "
1103                      << message;
1104             })) {
1105       return error;
1106     }
1107   }
1108 
1109   // Seed at reference checks with this built-in.
1110   return ValidateFragDepthAtReference(decoration, inst, inst, inst);
1111 }
1112 
ValidateFragDepthAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1113 spv_result_t BuiltInsValidator::ValidateFragDepthAtReference(
1114     const Decoration& decoration, const Instruction& built_in_inst,
1115     const Instruction& referenced_inst,
1116     const Instruction& referenced_from_inst) {
1117   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1118     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1119     if (storage_class != SpvStorageClassMax &&
1120         storage_class != SpvStorageClassOutput) {
1121       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1122              << spvLogStringForEnv(_.context()->target_env)
1123              << " spec allows BuiltIn FragDepth to be only used for "
1124                 "variables with Output storage class. "
1125              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1126                                  referenced_from_inst)
1127              << " " << GetStorageClassDesc(referenced_from_inst);
1128     }
1129 
1130     for (const SpvExecutionModel execution_model : execution_models_) {
1131       if (execution_model != SpvExecutionModelFragment) {
1132         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1133                << spvLogStringForEnv(_.context()->target_env)
1134                << " spec allows BuiltIn FragDepth to be used only with "
1135                   "Fragment execution model. "
1136                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1137                                    referenced_from_inst, execution_model);
1138       }
1139     }
1140 
1141     for (const uint32_t entry_point : *entry_points_) {
1142       // Every entry point from which this function is called needs to have
1143       // Execution Mode DepthReplacing.
1144       const auto* modes = _.GetExecutionModes(entry_point);
1145       if (!modes || !modes->count(SpvExecutionModeDepthReplacing)) {
1146         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1147                << spvLogStringForEnv(_.context()->target_env)
1148                << " spec requires DepthReplacing execution mode to be "
1149                   "declared when using BuiltIn FragDepth. "
1150                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1151                                    referenced_from_inst);
1152       }
1153     }
1154   }
1155 
1156   if (function_id_ == 0) {
1157     // Propagate this rule to all dependant ids in the global scope.
1158     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1159         &BuiltInsValidator::ValidateFragDepthAtReference, this, decoration,
1160         built_in_inst, referenced_from_inst, std::placeholders::_1));
1161   }
1162 
1163   return SPV_SUCCESS;
1164 }
1165 
ValidateFrontFacingAtDefinition(const Decoration & decoration,const Instruction & inst)1166 spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition(
1167     const Decoration& decoration, const Instruction& inst) {
1168   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1169     if (spv_result_t error = ValidateBool(
1170             decoration, inst,
1171             [this, &inst](const std::string& message) -> spv_result_t {
1172               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1173                      << "According to the "
1174                      << spvLogStringForEnv(_.context()->target_env)
1175                      << " spec BuiltIn FrontFacing "
1176                         "variable needs to be a bool scalar. "
1177                      << message;
1178             })) {
1179       return error;
1180     }
1181   }
1182 
1183   // Seed at reference checks with this built-in.
1184   return ValidateFrontFacingAtReference(decoration, inst, inst, inst);
1185 }
1186 
ValidateFrontFacingAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1187 spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference(
1188     const Decoration& decoration, const Instruction& built_in_inst,
1189     const Instruction& referenced_inst,
1190     const Instruction& referenced_from_inst) {
1191   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1192     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1193     if (storage_class != SpvStorageClassMax &&
1194         storage_class != SpvStorageClassInput) {
1195       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1196              << spvLogStringForEnv(_.context()->target_env)
1197              << " spec allows BuiltIn FrontFacing to be only used for "
1198                 "variables with Input storage class. "
1199              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1200                                  referenced_from_inst)
1201              << " " << GetStorageClassDesc(referenced_from_inst);
1202     }
1203 
1204     for (const SpvExecutionModel execution_model : execution_models_) {
1205       if (execution_model != SpvExecutionModelFragment) {
1206         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1207                << spvLogStringForEnv(_.context()->target_env)
1208                << " spec allows BuiltIn FrontFacing to be used only with "
1209                   "Fragment execution model. "
1210                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1211                                    referenced_from_inst, execution_model);
1212       }
1213     }
1214   }
1215 
1216   if (function_id_ == 0) {
1217     // Propagate this rule to all dependant ids in the global scope.
1218     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1219         &BuiltInsValidator::ValidateFrontFacingAtReference, this, decoration,
1220         built_in_inst, referenced_from_inst, std::placeholders::_1));
1221   }
1222 
1223   return SPV_SUCCESS;
1224 }
1225 
ValidateHelperInvocationAtDefinition(const Decoration & decoration,const Instruction & inst)1226 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtDefinition(
1227     const Decoration& decoration, const Instruction& inst) {
1228   if (spvIsVulkanEnv(_.context()->target_env)) {
1229     if (spv_result_t error = ValidateBool(
1230             decoration, inst,
1231             [this, &inst](const std::string& message) -> spv_result_t {
1232               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1233                      << "According to the Vulkan spec BuiltIn HelperInvocation "
1234                         "variable needs to be a bool scalar. "
1235                      << message;
1236             })) {
1237       return error;
1238     }
1239   }
1240 
1241   // Seed at reference checks with this built-in.
1242   return ValidateHelperInvocationAtReference(decoration, inst, inst, inst);
1243 }
1244 
ValidateHelperInvocationAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1245 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtReference(
1246     const Decoration& decoration, const Instruction& built_in_inst,
1247     const Instruction& referenced_inst,
1248     const Instruction& referenced_from_inst) {
1249   if (spvIsVulkanEnv(_.context()->target_env)) {
1250     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1251     if (storage_class != SpvStorageClassMax &&
1252         storage_class != SpvStorageClassInput) {
1253       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1254              << "Vulkan spec allows BuiltIn HelperInvocation to be only used "
1255                 "for variables with Input storage class. "
1256              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1257                                  referenced_from_inst)
1258              << " " << GetStorageClassDesc(referenced_from_inst);
1259     }
1260 
1261     for (const SpvExecutionModel execution_model : execution_models_) {
1262       if (execution_model != SpvExecutionModelFragment) {
1263         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1264                << "Vulkan spec allows BuiltIn HelperInvocation to be used only "
1265                   "with Fragment execution model. "
1266                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1267                                    referenced_from_inst, execution_model);
1268       }
1269     }
1270   }
1271 
1272   if (function_id_ == 0) {
1273     // Propagate this rule to all dependant ids in the global scope.
1274     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
1275         std::bind(&BuiltInsValidator::ValidateHelperInvocationAtReference, this,
1276                   decoration, built_in_inst, referenced_from_inst,
1277                   std::placeholders::_1));
1278   }
1279 
1280   return SPV_SUCCESS;
1281 }
1282 
ValidateInvocationIdAtDefinition(const Decoration & decoration,const Instruction & inst)1283 spv_result_t BuiltInsValidator::ValidateInvocationIdAtDefinition(
1284     const Decoration& decoration, const Instruction& inst) {
1285   if (spvIsVulkanEnv(_.context()->target_env)) {
1286     if (spv_result_t error = ValidateI32(
1287             decoration, inst,
1288             [this, &inst](const std::string& message) -> spv_result_t {
1289               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1290                      << "According to the Vulkan spec BuiltIn InvocationId "
1291                         "variable needs to be a 32-bit int scalar. "
1292                      << message;
1293             })) {
1294       return error;
1295     }
1296   }
1297 
1298   // Seed at reference checks with this built-in.
1299   return ValidateInvocationIdAtReference(decoration, inst, inst, inst);
1300 }
1301 
ValidateInvocationIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1302 spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference(
1303     const Decoration& decoration, const Instruction& built_in_inst,
1304     const Instruction& referenced_inst,
1305     const Instruction& referenced_from_inst) {
1306   if (spvIsVulkanEnv(_.context()->target_env)) {
1307     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1308     if (storage_class != SpvStorageClassMax &&
1309         storage_class != SpvStorageClassInput) {
1310       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1311              << "Vulkan spec allows BuiltIn InvocationId to be only used for "
1312                 "variables with Input storage class. "
1313              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1314                                  referenced_from_inst)
1315              << " " << GetStorageClassDesc(referenced_from_inst);
1316     }
1317 
1318     for (const SpvExecutionModel execution_model : execution_models_) {
1319       if (execution_model != SpvExecutionModelTessellationControl &&
1320           execution_model != SpvExecutionModelGeometry) {
1321         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1322                << "Vulkan spec allows BuiltIn InvocationId to be used only "
1323                   "with TessellationControl or Geometry execution models. "
1324                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1325                                    referenced_from_inst, execution_model);
1326       }
1327     }
1328   }
1329 
1330   if (function_id_ == 0) {
1331     // Propagate this rule to all dependant ids in the global scope.
1332     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1333         &BuiltInsValidator::ValidateInvocationIdAtReference, this, decoration,
1334         built_in_inst, referenced_from_inst, std::placeholders::_1));
1335   }
1336 
1337   return SPV_SUCCESS;
1338 }
1339 
ValidateInstanceIndexAtDefinition(const Decoration & decoration,const Instruction & inst)1340 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition(
1341     const Decoration& decoration, const Instruction& inst) {
1342   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1343     if (spv_result_t error = ValidateI32(
1344             decoration, inst,
1345             [this, &inst](const std::string& message) -> spv_result_t {
1346               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1347                      << "According to the "
1348                      << spvLogStringForEnv(_.context()->target_env)
1349                      << " spec BuiltIn InstanceIndex "
1350                         "variable needs to be a 32-bit int scalar. "
1351                      << message;
1352             })) {
1353       return error;
1354     }
1355   }
1356 
1357   // Seed at reference checks with this built-in.
1358   return ValidateInstanceIndexAtReference(decoration, inst, inst, inst);
1359 }
1360 
ValidateInstanceIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1361 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference(
1362     const Decoration& decoration, const Instruction& built_in_inst,
1363     const Instruction& referenced_inst,
1364     const Instruction& referenced_from_inst) {
1365   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
1366     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1367     if (storage_class != SpvStorageClassMax &&
1368         storage_class != SpvStorageClassInput) {
1369       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1370              << spvLogStringForEnv(_.context()->target_env)
1371              << " spec allows BuiltIn InstanceIndex to be only used for "
1372                 "variables with Input storage class. "
1373              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1374                                  referenced_from_inst)
1375              << " " << GetStorageClassDesc(referenced_from_inst);
1376     }
1377 
1378     for (const SpvExecutionModel execution_model : execution_models_) {
1379       if (execution_model != SpvExecutionModelVertex) {
1380         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1381                << spvLogStringForEnv(_.context()->target_env)
1382                << " spec allows BuiltIn InstanceIndex to be used only "
1383                   "with Vertex execution model. "
1384                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1385                                    referenced_from_inst, execution_model);
1386       }
1387     }
1388   }
1389 
1390   if (function_id_ == 0) {
1391     // Propagate this rule to all dependant ids in the global scope.
1392     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1393         &BuiltInsValidator::ValidateInstanceIndexAtReference, this, decoration,
1394         built_in_inst, referenced_from_inst, std::placeholders::_1));
1395   }
1396 
1397   return SPV_SUCCESS;
1398 }
1399 
ValidatePatchVerticesAtDefinition(const Decoration & decoration,const Instruction & inst)1400 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtDefinition(
1401     const Decoration& decoration, const Instruction& inst) {
1402   if (spvIsVulkanEnv(_.context()->target_env)) {
1403     if (spv_result_t error = ValidateI32(
1404             decoration, inst,
1405             [this, &inst](const std::string& message) -> spv_result_t {
1406               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1407                      << "According to the Vulkan spec BuiltIn PatchVertices "
1408                         "variable needs to be a 32-bit int scalar. "
1409                      << message;
1410             })) {
1411       return error;
1412     }
1413   }
1414 
1415   // Seed at reference checks with this built-in.
1416   return ValidatePatchVerticesAtReference(decoration, inst, inst, inst);
1417 }
1418 
ValidatePatchVerticesAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1419 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtReference(
1420     const Decoration& decoration, const Instruction& built_in_inst,
1421     const Instruction& referenced_inst,
1422     const Instruction& referenced_from_inst) {
1423   if (spvIsVulkanEnv(_.context()->target_env)) {
1424     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1425     if (storage_class != SpvStorageClassMax &&
1426         storage_class != SpvStorageClassInput) {
1427       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1428              << "Vulkan spec allows BuiltIn PatchVertices to be only used for "
1429                 "variables with Input storage class. "
1430              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1431                                  referenced_from_inst)
1432              << " " << GetStorageClassDesc(referenced_from_inst);
1433     }
1434 
1435     for (const SpvExecutionModel execution_model : execution_models_) {
1436       if (execution_model != SpvExecutionModelTessellationControl &&
1437           execution_model != SpvExecutionModelTessellationEvaluation) {
1438         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1439                << "Vulkan spec allows BuiltIn PatchVertices to be used only "
1440                   "with TessellationControl or TessellationEvaluation "
1441                   "execution models. "
1442                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1443                                    referenced_from_inst, execution_model);
1444       }
1445     }
1446   }
1447 
1448   if (function_id_ == 0) {
1449     // Propagate this rule to all dependant ids in the global scope.
1450     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1451         &BuiltInsValidator::ValidatePatchVerticesAtReference, this, decoration,
1452         built_in_inst, referenced_from_inst, std::placeholders::_1));
1453   }
1454 
1455   return SPV_SUCCESS;
1456 }
1457 
ValidatePointCoordAtDefinition(const Decoration & decoration,const Instruction & inst)1458 spv_result_t BuiltInsValidator::ValidatePointCoordAtDefinition(
1459     const Decoration& decoration, const Instruction& inst) {
1460   if (spvIsVulkanEnv(_.context()->target_env)) {
1461     if (spv_result_t error = ValidateF32Vec(
1462             decoration, inst, 2,
1463             [this, &inst](const std::string& message) -> spv_result_t {
1464               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1465                      << "According to the Vulkan spec BuiltIn PointCoord "
1466                         "variable needs to be a 2-component 32-bit float "
1467                         "vector. "
1468                      << message;
1469             })) {
1470       return error;
1471     }
1472   }
1473 
1474   // Seed at reference checks with this built-in.
1475   return ValidatePointCoordAtReference(decoration, inst, inst, inst);
1476 }
1477 
ValidatePointCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1478 spv_result_t BuiltInsValidator::ValidatePointCoordAtReference(
1479     const Decoration& decoration, const Instruction& built_in_inst,
1480     const Instruction& referenced_inst,
1481     const Instruction& referenced_from_inst) {
1482   if (spvIsVulkanEnv(_.context()->target_env)) {
1483     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1484     if (storage_class != SpvStorageClassMax &&
1485         storage_class != SpvStorageClassInput) {
1486       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1487              << "Vulkan spec allows BuiltIn PointCoord to be only used for "
1488                 "variables with Input storage class. "
1489              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1490                                  referenced_from_inst)
1491              << " " << GetStorageClassDesc(referenced_from_inst);
1492     }
1493 
1494     for (const SpvExecutionModel execution_model : execution_models_) {
1495       if (execution_model != SpvExecutionModelFragment) {
1496         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1497                << "Vulkan spec allows BuiltIn PointCoord to be used only with "
1498                   "Fragment execution model. "
1499                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1500                                    referenced_from_inst, execution_model);
1501       }
1502     }
1503   }
1504 
1505   if (function_id_ == 0) {
1506     // Propagate this rule to all dependant ids in the global scope.
1507     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1508         &BuiltInsValidator::ValidatePointCoordAtReference, this, decoration,
1509         built_in_inst, referenced_from_inst, std::placeholders::_1));
1510   }
1511 
1512   return SPV_SUCCESS;
1513 }
1514 
ValidatePointSizeAtDefinition(const Decoration & decoration,const Instruction & inst)1515 spv_result_t BuiltInsValidator::ValidatePointSizeAtDefinition(
1516     const Decoration& decoration, const Instruction& inst) {
1517   // Seed at reference checks with this built-in.
1518   return ValidatePointSizeAtReference(decoration, inst, inst, inst);
1519 }
1520 
ValidatePointSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1521 spv_result_t BuiltInsValidator::ValidatePointSizeAtReference(
1522     const Decoration& decoration, const Instruction& built_in_inst,
1523     const Instruction& referenced_inst,
1524     const Instruction& referenced_from_inst) {
1525   if (spvIsVulkanEnv(_.context()->target_env)) {
1526     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1527     if (storage_class != SpvStorageClassMax &&
1528         storage_class != SpvStorageClassInput &&
1529         storage_class != SpvStorageClassOutput) {
1530       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1531              << "Vulkan spec allows BuiltIn PointSize to be only used for "
1532                 "variables with Input or Output storage class. "
1533              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1534                                  referenced_from_inst)
1535              << " " << GetStorageClassDesc(referenced_from_inst);
1536     }
1537 
1538     if (storage_class == SpvStorageClassInput) {
1539       assert(function_id_ == 0);
1540       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1541           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1542           "Vulkan spec doesn't allow BuiltIn PointSize to be used for "
1543           "variables with Input storage class if execution model is Vertex.",
1544           SpvExecutionModelVertex, decoration, built_in_inst,
1545           referenced_from_inst, std::placeholders::_1));
1546     }
1547 
1548     for (const SpvExecutionModel execution_model : execution_models_) {
1549       switch (execution_model) {
1550         case SpvExecutionModelVertex: {
1551           if (spv_result_t error = ValidateF32(
1552                   decoration, built_in_inst,
1553                   [this, &referenced_from_inst](
1554                       const std::string& message) -> spv_result_t {
1555                     return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1556                            << "According to the Vulkan spec BuiltIn PointSize "
1557                               "variable needs to be a 32-bit float scalar. "
1558                            << message;
1559                   })) {
1560             return error;
1561           }
1562           break;
1563         }
1564         case SpvExecutionModelTessellationControl:
1565         case SpvExecutionModelTessellationEvaluation:
1566         case SpvExecutionModelGeometry:
1567         case SpvExecutionModelMeshNV: {
1568           // PointSize can be a per-vertex variable for tessellation control,
1569           // tessellation evaluation and geometry shader stages. In such cases
1570           // variables will have an array of 32-bit floats.
1571           if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1572             // The array is on the variable, so this must be a 32-bit float.
1573             if (spv_result_t error = ValidateF32(
1574                     decoration, built_in_inst,
1575                     [this, &referenced_from_inst](
1576                         const std::string& message) -> spv_result_t {
1577                       return _.diag(SPV_ERROR_INVALID_DATA,
1578                                     &referenced_from_inst)
1579                              << "According to the Vulkan spec BuiltIn "
1580                                 "PointSize variable needs to be a 32-bit "
1581                                 "float scalar. "
1582                              << message;
1583                     })) {
1584               return error;
1585             }
1586           } else {
1587             if (spv_result_t error = ValidateOptionalArrayedF32(
1588                     decoration, built_in_inst,
1589                     [this, &referenced_from_inst](
1590                         const std::string& message) -> spv_result_t {
1591                       return _.diag(SPV_ERROR_INVALID_DATA,
1592                                     &referenced_from_inst)
1593                              << "According to the Vulkan spec BuiltIn "
1594                                 "PointSize variable needs to be a 32-bit "
1595                                 "float scalar. "
1596                              << message;
1597                     })) {
1598               return error;
1599             }
1600           }
1601           break;
1602         }
1603 
1604         default: {
1605           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1606                  << "Vulkan spec allows BuiltIn PointSize to be used only with "
1607                     "Vertex, TessellationControl, TessellationEvaluation or "
1608                     "Geometry execution models. "
1609                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1610                                      referenced_from_inst, execution_model);
1611         }
1612       }
1613     }
1614   }
1615 
1616   if (function_id_ == 0) {
1617     // Propagate this rule to all dependant ids in the global scope.
1618     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1619         &BuiltInsValidator::ValidatePointSizeAtReference, this, decoration,
1620         built_in_inst, referenced_from_inst, std::placeholders::_1));
1621   }
1622 
1623   return SPV_SUCCESS;
1624 }
1625 
ValidatePositionAtDefinition(const Decoration & decoration,const Instruction & inst)1626 spv_result_t BuiltInsValidator::ValidatePositionAtDefinition(
1627     const Decoration& decoration, const Instruction& inst) {
1628   // Seed at reference checks with this built-in.
1629   return ValidatePositionAtReference(decoration, inst, inst, inst);
1630 }
1631 
ValidatePositionAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1632 spv_result_t BuiltInsValidator::ValidatePositionAtReference(
1633     const Decoration& decoration, const Instruction& built_in_inst,
1634     const Instruction& referenced_inst,
1635     const Instruction& referenced_from_inst) {
1636   if (spvIsVulkanEnv(_.context()->target_env)) {
1637     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1638     if (storage_class != SpvStorageClassMax &&
1639         storage_class != SpvStorageClassInput &&
1640         storage_class != SpvStorageClassOutput) {
1641       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1642              << "Vulkan spec allows BuiltIn Position to be only used for "
1643                 "variables with Input or Output storage class. "
1644              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1645                                  referenced_from_inst)
1646              << " " << GetStorageClassDesc(referenced_from_inst);
1647     }
1648 
1649     if (storage_class == SpvStorageClassInput) {
1650       assert(function_id_ == 0);
1651       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1652           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1653           "Vulkan spec doesn't allow BuiltIn Position to be used for variables "
1654           "with Input storage class if execution model is Vertex.",
1655           SpvExecutionModelVertex, decoration, built_in_inst,
1656           referenced_from_inst, std::placeholders::_1));
1657     }
1658 
1659     for (const SpvExecutionModel execution_model : execution_models_) {
1660       switch (execution_model) {
1661         case SpvExecutionModelVertex: {
1662           if (spv_result_t error = ValidateF32Vec(
1663                   decoration, built_in_inst, 4,
1664                   [this, &referenced_from_inst](
1665                       const std::string& message) -> spv_result_t {
1666                     return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1667                            << "According to the Vulkan spec BuiltIn Position "
1668                               "variable needs to be a 4-component 32-bit float "
1669                               "vector. "
1670                            << message;
1671                   })) {
1672             return error;
1673           }
1674           break;
1675         }
1676         case SpvExecutionModelGeometry:
1677         case SpvExecutionModelTessellationControl:
1678         case SpvExecutionModelTessellationEvaluation:
1679         case SpvExecutionModelMeshNV: {
1680           // Position can be a per-vertex variable for tessellation control,
1681           // tessellation evaluation, geometry and mesh shader stages. In such
1682           // cases variables will have an array of 4-component 32-bit float
1683           // vectors.
1684           if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1685             // The array is on the variable, so this must be a 4-component
1686             // 32-bit float vector.
1687             if (spv_result_t error = ValidateF32Vec(
1688                     decoration, built_in_inst, 4,
1689                     [this, &referenced_from_inst](
1690                         const std::string& message) -> spv_result_t {
1691                       return _.diag(SPV_ERROR_INVALID_DATA,
1692                                     &referenced_from_inst)
1693                              << "According to the Vulkan spec BuiltIn Position "
1694                                 "variable needs to be a 4-component 32-bit "
1695                                 "float vector. "
1696                              << message;
1697                     })) {
1698               return error;
1699             }
1700           } else {
1701             if (spv_result_t error = ValidateOptionalArrayedF32Vec(
1702                     decoration, built_in_inst, 4,
1703                     [this, &referenced_from_inst](
1704                         const std::string& message) -> spv_result_t {
1705                       return _.diag(SPV_ERROR_INVALID_DATA,
1706                                     &referenced_from_inst)
1707                              << "According to the Vulkan spec BuiltIn Position "
1708                                 "variable needs to be a 4-component 32-bit "
1709                                 "float vector. "
1710                              << message;
1711                     })) {
1712               return error;
1713             }
1714           }
1715           break;
1716         }
1717 
1718         default: {
1719           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1720                  << "Vulkan spec allows BuiltIn Position to be used only "
1721                     "with Vertex, TessellationControl, TessellationEvaluation"
1722                     " or Geometry execution models. "
1723                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1724                                      referenced_from_inst, execution_model);
1725         }
1726       }
1727     }
1728   }
1729 
1730   if (spvIsWebGPUEnv(_.context()->target_env)) {
1731     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1732     if (storage_class != SpvStorageClassMax &&
1733         storage_class != SpvStorageClassOutput) {
1734       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1735              << "WebGPU spec allows BuiltIn Position to be only used for "
1736                 "variables with Output storage class. "
1737              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1738                                  referenced_from_inst)
1739              << " " << GetStorageClassDesc(referenced_from_inst);
1740     }
1741 
1742     for (const SpvExecutionModel execution_model : execution_models_) {
1743       switch (execution_model) {
1744         case SpvExecutionModelVertex: {
1745           if (spv_result_t error = ValidateF32Vec(
1746                   decoration, built_in_inst, 4,
1747                   [this, &referenced_from_inst](
1748                       const std::string& message) -> spv_result_t {
1749                     return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1750                            << "According to the WebGPU spec BuiltIn Position "
1751                               "variable needs to be a 4-component 32-bit float "
1752                               "vector. "
1753                            << message;
1754                   })) {
1755             return error;
1756           }
1757           break;
1758         }
1759         default: {
1760           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1761                  << "WebGPU spec allows BuiltIn Position to be used only "
1762                     "with the Vertex execution model. "
1763                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1764                                      referenced_from_inst, execution_model);
1765         }
1766       }
1767     }
1768   }
1769 
1770   if (function_id_ == 0) {
1771     // Propagate this rule to all dependant ids in the global scope.
1772     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1773         &BuiltInsValidator::ValidatePositionAtReference, this, decoration,
1774         built_in_inst, referenced_from_inst, std::placeholders::_1));
1775   }
1776 
1777   return SPV_SUCCESS;
1778 }
1779 
ValidatePrimitiveIdAtDefinition(const Decoration & decoration,const Instruction & inst)1780 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition(
1781     const Decoration& decoration, const Instruction& inst) {
1782   if (spvIsVulkanEnv(_.context()->target_env)) {
1783     // PrimitiveId can be a per-primitive variable for mesh shader stage.
1784     // In such cases variable will have an array of 32-bit integers.
1785     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
1786       // This must be a 32-bit int scalar.
1787       if (spv_result_t error = ValidateI32(
1788               decoration, inst,
1789               [this, &inst](const std::string& message) -> spv_result_t {
1790                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1791                        << "According to the Vulkan spec BuiltIn PrimitiveId "
1792                           "variable needs to be a 32-bit int scalar. "
1793                        << message;
1794               })) {
1795         return error;
1796       }
1797     } else {
1798       if (spv_result_t error = ValidateOptionalArrayedI32(
1799               decoration, inst,
1800               [this, &inst](const std::string& message) -> spv_result_t {
1801                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1802                        << "According to the Vulkan spec BuiltIn PrimitiveId "
1803                           "variable needs to be a 32-bit int scalar. "
1804                        << message;
1805               })) {
1806         return error;
1807       }
1808     }
1809   }
1810 
1811   // Seed at reference checks with this built-in.
1812   return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst);
1813 }
1814 
ValidatePrimitiveIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1815 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference(
1816     const Decoration& decoration, const Instruction& built_in_inst,
1817     const Instruction& referenced_inst,
1818     const Instruction& referenced_from_inst) {
1819   if (spvIsVulkanEnv(_.context()->target_env)) {
1820     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1821     if (storage_class != SpvStorageClassMax &&
1822         storage_class != SpvStorageClassInput &&
1823         storage_class != SpvStorageClassOutput) {
1824       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1825              << "Vulkan spec allows BuiltIn PrimitiveId to be only used for "
1826                 "variables with Input or Output storage class. "
1827              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1828                                  referenced_from_inst)
1829              << " " << GetStorageClassDesc(referenced_from_inst);
1830     }
1831 
1832     if (storage_class == SpvStorageClassOutput) {
1833       assert(function_id_ == 0);
1834       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1835           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1836           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
1837           "variables with Output storage class if execution model is "
1838           "TessellationControl.",
1839           SpvExecutionModelTessellationControl, decoration, built_in_inst,
1840           referenced_from_inst, std::placeholders::_1));
1841       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1842           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1843           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
1844           "variables with Output storage class if execution model is "
1845           "TessellationEvaluation.",
1846           SpvExecutionModelTessellationEvaluation, decoration, built_in_inst,
1847           referenced_from_inst, std::placeholders::_1));
1848       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1849           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
1850           "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
1851           "variables with Output storage class if execution model is "
1852           "Fragment.",
1853           SpvExecutionModelFragment, decoration, built_in_inst,
1854           referenced_from_inst, std::placeholders::_1));
1855     }
1856 
1857     for (const SpvExecutionModel execution_model : execution_models_) {
1858       switch (execution_model) {
1859         case SpvExecutionModelFragment:
1860         case SpvExecutionModelTessellationControl:
1861         case SpvExecutionModelTessellationEvaluation:
1862         case SpvExecutionModelGeometry:
1863         case SpvExecutionModelMeshNV:
1864         case SpvExecutionModelRayGenerationNV:
1865         case SpvExecutionModelIntersectionNV:
1866         case SpvExecutionModelAnyHitNV:
1867         case SpvExecutionModelClosestHitNV:
1868         case SpvExecutionModelMissNV:
1869         case SpvExecutionModelCallableNV: {
1870           // Ok.
1871           break;
1872         }
1873 
1874         default: {
1875           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1876                  << "Vulkan spec allows BuiltIn PrimitiveId to be used only "
1877                     "with Fragment, TessellationControl, "
1878                     "TessellationEvaluation or Geometry execution models. "
1879                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1880                                      referenced_from_inst, execution_model);
1881         }
1882       }
1883     }
1884   }
1885 
1886   if (function_id_ == 0) {
1887     // Propagate this rule to all dependant ids in the global scope.
1888     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1889         &BuiltInsValidator::ValidatePrimitiveIdAtReference, this, decoration,
1890         built_in_inst, referenced_from_inst, std::placeholders::_1));
1891   }
1892 
1893   return SPV_SUCCESS;
1894 }
1895 
ValidateSampleIdAtDefinition(const Decoration & decoration,const Instruction & inst)1896 spv_result_t BuiltInsValidator::ValidateSampleIdAtDefinition(
1897     const Decoration& decoration, const Instruction& inst) {
1898   if (spvIsVulkanEnv(_.context()->target_env)) {
1899     if (spv_result_t error = ValidateI32(
1900             decoration, inst,
1901             [this, &inst](const std::string& message) -> spv_result_t {
1902               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1903                      << "According to the Vulkan spec BuiltIn SampleId "
1904                         "variable needs to be a 32-bit int scalar. "
1905                      << message;
1906             })) {
1907       return error;
1908     }
1909   }
1910 
1911   // Seed at reference checks with this built-in.
1912   return ValidateSampleIdAtReference(decoration, inst, inst, inst);
1913 }
1914 
ValidateSampleIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1915 spv_result_t BuiltInsValidator::ValidateSampleIdAtReference(
1916     const Decoration& decoration, const Instruction& built_in_inst,
1917     const Instruction& referenced_inst,
1918     const Instruction& referenced_from_inst) {
1919   if (spvIsVulkanEnv(_.context()->target_env)) {
1920     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1921     if (storage_class != SpvStorageClassMax &&
1922         storage_class != SpvStorageClassInput) {
1923       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1924              << "Vulkan spec allows BuiltIn SampleId to be only used for "
1925                 "variables with Input storage class. "
1926              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1927                                  referenced_from_inst)
1928              << " " << GetStorageClassDesc(referenced_from_inst);
1929     }
1930 
1931     for (const SpvExecutionModel execution_model : execution_models_) {
1932       if (execution_model != SpvExecutionModelFragment) {
1933         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1934                << "Vulkan spec allows BuiltIn SampleId to be used only with "
1935                   "Fragment execution model. "
1936                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1937                                    referenced_from_inst, execution_model);
1938       }
1939     }
1940   }
1941 
1942   if (function_id_ == 0) {
1943     // Propagate this rule to all dependant ids in the global scope.
1944     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
1945         &BuiltInsValidator::ValidateSampleIdAtReference, this, decoration,
1946         built_in_inst, referenced_from_inst, std::placeholders::_1));
1947   }
1948 
1949   return SPV_SUCCESS;
1950 }
1951 
ValidateSampleMaskAtDefinition(const Decoration & decoration,const Instruction & inst)1952 spv_result_t BuiltInsValidator::ValidateSampleMaskAtDefinition(
1953     const Decoration& decoration, const Instruction& inst) {
1954   if (spvIsVulkanEnv(_.context()->target_env)) {
1955     if (spv_result_t error = ValidateI32Arr(
1956             decoration, inst,
1957             [this, &inst](const std::string& message) -> spv_result_t {
1958               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
1959                      << "According to the Vulkan spec BuiltIn SampleMask "
1960                         "variable needs to be a 32-bit int array. "
1961                      << message;
1962             })) {
1963       return error;
1964     }
1965   }
1966 
1967   // Seed at reference checks with this built-in.
1968   return ValidateSampleMaskAtReference(decoration, inst, inst, inst);
1969 }
1970 
ValidateSampleMaskAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)1971 spv_result_t BuiltInsValidator::ValidateSampleMaskAtReference(
1972     const Decoration& decoration, const Instruction& built_in_inst,
1973     const Instruction& referenced_inst,
1974     const Instruction& referenced_from_inst) {
1975   if (spvIsVulkanEnv(_.context()->target_env)) {
1976     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
1977     if (storage_class != SpvStorageClassMax &&
1978         storage_class != SpvStorageClassInput &&
1979         storage_class != SpvStorageClassOutput) {
1980       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1981              << "Vulkan spec allows BuiltIn SampleMask to be only used for "
1982                 "variables with Input or Output storage class. "
1983              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1984                                  referenced_from_inst)
1985              << " " << GetStorageClassDesc(referenced_from_inst);
1986     }
1987 
1988     for (const SpvExecutionModel execution_model : execution_models_) {
1989       if (execution_model != SpvExecutionModelFragment) {
1990         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
1991                << "Vulkan spec allows BuiltIn SampleMask to be used only "
1992                   "with "
1993                   "Fragment execution model. "
1994                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
1995                                    referenced_from_inst, execution_model);
1996       }
1997     }
1998   }
1999 
2000   if (function_id_ == 0) {
2001     // Propagate this rule to all dependant ids in the global scope.
2002     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2003         &BuiltInsValidator::ValidateSampleMaskAtReference, this, decoration,
2004         built_in_inst, referenced_from_inst, std::placeholders::_1));
2005   }
2006 
2007   return SPV_SUCCESS;
2008 }
2009 
ValidateSamplePositionAtDefinition(const Decoration & decoration,const Instruction & inst)2010 spv_result_t BuiltInsValidator::ValidateSamplePositionAtDefinition(
2011     const Decoration& decoration, const Instruction& inst) {
2012   if (spvIsVulkanEnv(_.context()->target_env)) {
2013     if (spv_result_t error = ValidateF32Vec(
2014             decoration, inst, 2,
2015             [this, &inst](const std::string& message) -> spv_result_t {
2016               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2017                      << "According to the Vulkan spec BuiltIn SamplePosition "
2018                         "variable needs to be a 2-component 32-bit float "
2019                         "vector. "
2020                      << message;
2021             })) {
2022       return error;
2023     }
2024   }
2025 
2026   // Seed at reference checks with this built-in.
2027   return ValidateSamplePositionAtReference(decoration, inst, inst, inst);
2028 }
2029 
ValidateSamplePositionAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2030 spv_result_t BuiltInsValidator::ValidateSamplePositionAtReference(
2031     const Decoration& decoration, const Instruction& built_in_inst,
2032     const Instruction& referenced_inst,
2033     const Instruction& referenced_from_inst) {
2034   if (spvIsVulkanEnv(_.context()->target_env)) {
2035     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2036     if (storage_class != SpvStorageClassMax &&
2037         storage_class != SpvStorageClassInput) {
2038       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2039              << "Vulkan spec allows BuiltIn SamplePosition to be only used "
2040                 "for "
2041                 "variables with Input storage class. "
2042              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2043                                  referenced_from_inst)
2044              << " " << GetStorageClassDesc(referenced_from_inst);
2045     }
2046 
2047     for (const SpvExecutionModel execution_model : execution_models_) {
2048       if (execution_model != SpvExecutionModelFragment) {
2049         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2050                << "Vulkan spec allows BuiltIn SamplePosition to be used only "
2051                   "with "
2052                   "Fragment execution model. "
2053                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2054                                    referenced_from_inst, execution_model);
2055       }
2056     }
2057   }
2058 
2059   if (function_id_ == 0) {
2060     // Propagate this rule to all dependant ids in the global scope.
2061     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2062         &BuiltInsValidator::ValidateSamplePositionAtReference, this, decoration,
2063         built_in_inst, referenced_from_inst, std::placeholders::_1));
2064   }
2065 
2066   return SPV_SUCCESS;
2067 }
2068 
ValidateTessCoordAtDefinition(const Decoration & decoration,const Instruction & inst)2069 spv_result_t BuiltInsValidator::ValidateTessCoordAtDefinition(
2070     const Decoration& decoration, const Instruction& inst) {
2071   if (spvIsVulkanEnv(_.context()->target_env)) {
2072     if (spv_result_t error = ValidateF32Vec(
2073             decoration, inst, 3,
2074             [this, &inst](const std::string& message) -> spv_result_t {
2075               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2076                      << "According to the Vulkan spec BuiltIn TessCoord "
2077                         "variable needs to be a 3-component 32-bit float "
2078                         "vector. "
2079                      << message;
2080             })) {
2081       return error;
2082     }
2083   }
2084 
2085   // Seed at reference checks with this built-in.
2086   return ValidateTessCoordAtReference(decoration, inst, inst, inst);
2087 }
2088 
ValidateTessCoordAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2089 spv_result_t BuiltInsValidator::ValidateTessCoordAtReference(
2090     const Decoration& decoration, const Instruction& built_in_inst,
2091     const Instruction& referenced_inst,
2092     const Instruction& referenced_from_inst) {
2093   if (spvIsVulkanEnv(_.context()->target_env)) {
2094     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2095     if (storage_class != SpvStorageClassMax &&
2096         storage_class != SpvStorageClassInput) {
2097       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2098              << "Vulkan spec allows BuiltIn TessCoord to be only used for "
2099                 "variables with Input storage class. "
2100              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2101                                  referenced_from_inst)
2102              << " " << GetStorageClassDesc(referenced_from_inst);
2103     }
2104 
2105     for (const SpvExecutionModel execution_model : execution_models_) {
2106       if (execution_model != SpvExecutionModelTessellationEvaluation) {
2107         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2108                << "Vulkan spec allows BuiltIn TessCoord to be used only with "
2109                   "TessellationEvaluation execution model. "
2110                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2111                                    referenced_from_inst, execution_model);
2112       }
2113     }
2114   }
2115 
2116   if (function_id_ == 0) {
2117     // Propagate this rule to all dependant ids in the global scope.
2118     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2119         &BuiltInsValidator::ValidateTessCoordAtReference, this, decoration,
2120         built_in_inst, referenced_from_inst, std::placeholders::_1));
2121   }
2122 
2123   return SPV_SUCCESS;
2124 }
2125 
ValidateTessLevelOuterAtDefinition(const Decoration & decoration,const Instruction & inst)2126 spv_result_t BuiltInsValidator::ValidateTessLevelOuterAtDefinition(
2127     const Decoration& decoration, const Instruction& inst) {
2128   if (spvIsVulkanEnv(_.context()->target_env)) {
2129     if (spv_result_t error = ValidateF32Arr(
2130             decoration, inst, 4,
2131             [this, &inst](const std::string& message) -> spv_result_t {
2132               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2133                      << "According to the Vulkan spec BuiltIn TessLevelOuter "
2134                         "variable needs to be a 4-component 32-bit float "
2135                         "array. "
2136                      << message;
2137             })) {
2138       return error;
2139     }
2140   }
2141 
2142   // Seed at reference checks with this built-in.
2143   return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2144 }
2145 
ValidateTessLevelInnerAtDefinition(const Decoration & decoration,const Instruction & inst)2146 spv_result_t BuiltInsValidator::ValidateTessLevelInnerAtDefinition(
2147     const Decoration& decoration, const Instruction& inst) {
2148   if (spvIsVulkanEnv(_.context()->target_env)) {
2149     if (spv_result_t error = ValidateF32Arr(
2150             decoration, inst, 2,
2151             [this, &inst](const std::string& message) -> spv_result_t {
2152               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2153                      << "According to the Vulkan spec BuiltIn TessLevelOuter "
2154                         "variable needs to be a 2-component 32-bit float "
2155                         "array. "
2156                      << message;
2157             })) {
2158       return error;
2159     }
2160   }
2161 
2162   // Seed at reference checks with this built-in.
2163   return ValidateTessLevelAtReference(decoration, inst, inst, inst);
2164 }
2165 
ValidateTessLevelAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2166 spv_result_t BuiltInsValidator::ValidateTessLevelAtReference(
2167     const Decoration& decoration, const Instruction& built_in_inst,
2168     const Instruction& referenced_inst,
2169     const Instruction& referenced_from_inst) {
2170   if (spvIsVulkanEnv(_.context()->target_env)) {
2171     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2172     if (storage_class != SpvStorageClassMax &&
2173         storage_class != SpvStorageClassInput &&
2174         storage_class != SpvStorageClassOutput) {
2175       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2176              << "Vulkan spec allows BuiltIn "
2177              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2178                                               decoration.params()[0])
2179              << " to be only used for variables with Input or Output storage "
2180                 "class. "
2181              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2182                                  referenced_from_inst)
2183              << " " << GetStorageClassDesc(referenced_from_inst);
2184     }
2185 
2186     if (storage_class == SpvStorageClassInput) {
2187       assert(function_id_ == 0);
2188       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2189           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
2190           "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2191           "used "
2192           "for variables with Input storage class if execution model is "
2193           "TessellationControl.",
2194           SpvExecutionModelTessellationControl, decoration, built_in_inst,
2195           referenced_from_inst, std::placeholders::_1));
2196     }
2197 
2198     if (storage_class == SpvStorageClassOutput) {
2199       assert(function_id_ == 0);
2200       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2201           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
2202           "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be "
2203           "used "
2204           "for variables with Output storage class if execution model is "
2205           "TessellationEvaluation.",
2206           SpvExecutionModelTessellationEvaluation, decoration, built_in_inst,
2207           referenced_from_inst, std::placeholders::_1));
2208     }
2209 
2210     for (const SpvExecutionModel execution_model : execution_models_) {
2211       switch (execution_model) {
2212         case SpvExecutionModelTessellationControl:
2213         case SpvExecutionModelTessellationEvaluation: {
2214           // Ok.
2215           break;
2216         }
2217 
2218         default: {
2219           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2220                  << "Vulkan spec allows BuiltIn "
2221                  << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2222                                                   decoration.params()[0])
2223                  << " to be used only with TessellationControl or "
2224                     "TessellationEvaluation execution models. "
2225                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2226                                      referenced_from_inst, execution_model);
2227         }
2228       }
2229     }
2230   }
2231 
2232   if (function_id_ == 0) {
2233     // Propagate this rule to all dependant ids in the global scope.
2234     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2235         &BuiltInsValidator::ValidateTessLevelAtReference, this, decoration,
2236         built_in_inst, referenced_from_inst, std::placeholders::_1));
2237   }
2238 
2239   return SPV_SUCCESS;
2240 }
2241 
ValidateVertexIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2242 spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition(
2243     const Decoration& decoration, const Instruction& inst) {
2244   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2245     if (spv_result_t error = ValidateI32(
2246             decoration, inst,
2247             [this, &inst](const std::string& message) -> spv_result_t {
2248               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2249                      << "According to the "
2250                      << spvLogStringForEnv(_.context()->target_env)
2251                      << " spec BuiltIn VertexIndex variable needs to be a "
2252                         "32-bit int scalar. "
2253                      << message;
2254             })) {
2255       return error;
2256     }
2257   }
2258 
2259   // Seed at reference checks with this built-in.
2260   return ValidateVertexIndexAtReference(decoration, inst, inst, inst);
2261 }
2262 
ValidateVertexIdOrInstanceIdAtDefinition(const Decoration & decoration,const Instruction & inst)2263 spv_result_t BuiltInsValidator::ValidateVertexIdOrInstanceIdAtDefinition(
2264     const Decoration& decoration, const Instruction& inst) {
2265   const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
2266   bool allow_instance_id =
2267       (_.HasCapability(SpvCapabilityRayTracingNV) ||
2268        _.HasCapability(SpvCapabilityRayTracingProvisionalKHR)) &&
2269       label == SpvBuiltInInstanceId;
2270 
2271   if (spvIsVulkanEnv(_.context()->target_env) && !allow_instance_id) {
2272     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2273            << "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId "
2274               "to be used.";
2275   }
2276 
2277   if (label == SpvBuiltInInstanceId) {
2278     return ValidateInstanceIdAtReference(decoration, inst, inst, inst);
2279   }
2280   return SPV_SUCCESS;
2281 }
2282 
ValidateInstanceIdAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2283 spv_result_t BuiltInsValidator::ValidateInstanceIdAtReference(
2284     const Decoration& decoration, const Instruction& built_in_inst,
2285     const Instruction& referenced_inst,
2286     const Instruction& referenced_from_inst) {
2287   if (spvIsVulkanEnv(_.context()->target_env)) {
2288     for (const SpvExecutionModel execution_model : execution_models_) {
2289       switch (execution_model) {
2290         case SpvExecutionModelIntersectionNV:
2291         case SpvExecutionModelClosestHitNV:
2292         case SpvExecutionModelAnyHitNV:
2293           // Do nothing, valid stages
2294           break;
2295         default:
2296           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2297                  << "Vulkan spec allows BuiltIn InstanceId to be used "
2298                     "only with IntersectionNV, ClosestHitNV and AnyHitNV "
2299                     "execution models. "
2300                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2301                                      referenced_from_inst);
2302           break;
2303       }
2304     }
2305   }
2306 
2307   if (function_id_ == 0) {
2308     // Propagate this rule to all dependant ids in the global scope.
2309     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2310         &BuiltInsValidator::ValidateInstanceIdAtReference, this, decoration,
2311         built_in_inst, referenced_from_inst, std::placeholders::_1));
2312   }
2313 
2314   return SPV_SUCCESS;
2315 }
2316 
ValidateLocalInvocationIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2317 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
2318     const Decoration& decoration, const Instruction& inst) {
2319   if (spvIsWebGPUEnv(_.context()->target_env)) {
2320     if (spv_result_t error = ValidateI32(
2321             decoration, inst,
2322             [this, &inst](const std::string& message) -> spv_result_t {
2323               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2324                      << "According to the WebGPU spec BuiltIn "
2325                         "LocalInvocationIndex variable needs to be a 32-bit "
2326                         "int."
2327                      << message;
2328             })) {
2329       return error;
2330     }
2331   }
2332 
2333   // Seed at reference checks with this built-in.
2334   return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
2335 }
2336 
ValidateLocalInvocationIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2337 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
2338     const Decoration& decoration, const Instruction& built_in_inst,
2339     const Instruction& referenced_inst,
2340     const Instruction& referenced_from_inst) {
2341   if (spvIsWebGPUEnv(_.context()->target_env)) {
2342     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2343     if (storage_class != SpvStorageClassMax &&
2344         storage_class != SpvStorageClassInput) {
2345       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2346              << "WebGPU spec allows BuiltIn LocalInvocationIndex to be only "
2347                 "used for variables with Input storage class. "
2348              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2349                                  referenced_from_inst)
2350              << " " << GetStorageClassDesc(referenced_from_inst);
2351     }
2352 
2353     for (const SpvExecutionModel execution_model : execution_models_) {
2354       if (execution_model != SpvExecutionModelGLCompute) {
2355         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2356                << "WebGPU spec allows BuiltIn VertexIndex to be used only "
2357                   "with GLCompute execution model. "
2358                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2359                                    referenced_from_inst, execution_model);
2360       }
2361     }
2362   }
2363 
2364   if (function_id_ == 0) {
2365     // Propagate this rule to all dependant ids in the global scope.
2366     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2367         std::bind(&BuiltInsValidator::ValidateLocalInvocationIndexAtReference,
2368                   this, decoration, built_in_inst, referenced_from_inst,
2369                   std::placeholders::_1));
2370   }
2371 
2372   return SPV_SUCCESS;
2373 }
2374 
ValidateVertexIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2375 spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference(
2376     const Decoration& decoration, const Instruction& built_in_inst,
2377     const Instruction& referenced_inst,
2378     const Instruction& referenced_from_inst) {
2379   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2380     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2381     if (storage_class != SpvStorageClassMax &&
2382         storage_class != SpvStorageClassInput) {
2383       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2384              << spvLogStringForEnv(_.context()->target_env)
2385              << " spec allows BuiltIn VertexIndex to be only used for "
2386                 "variables with Input storage class. "
2387              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2388                                  referenced_from_inst)
2389              << " " << GetStorageClassDesc(referenced_from_inst);
2390     }
2391 
2392     for (const SpvExecutionModel execution_model : execution_models_) {
2393       if (execution_model != SpvExecutionModelVertex) {
2394         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2395                << spvLogStringForEnv(_.context()->target_env)
2396                << " spec allows BuiltIn VertexIndex to be used only with "
2397                   "Vertex execution model. "
2398                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2399                                    referenced_from_inst, execution_model);
2400       }
2401     }
2402   }
2403 
2404   if (function_id_ == 0) {
2405     // Propagate this rule to all dependant ids in the global scope.
2406     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2407         &BuiltInsValidator::ValidateVertexIndexAtReference, this, decoration,
2408         built_in_inst, referenced_from_inst, std::placeholders::_1));
2409   }
2410 
2411   return SPV_SUCCESS;
2412 }
2413 
ValidateLayerOrViewportIndexAtDefinition(const Decoration & decoration,const Instruction & inst)2414 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition(
2415     const Decoration& decoration, const Instruction& inst) {
2416   if (spvIsVulkanEnv(_.context()->target_env)) {
2417     // This can be a per-primitive variable for mesh shader stage.
2418     // In such cases variable will have an array of 32-bit integers.
2419     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2420       // This must be a 32-bit int scalar.
2421       if (spv_result_t error = ValidateI32(
2422               decoration, inst,
2423               [this, &decoration,
2424                &inst](const std::string& message) -> spv_result_t {
2425                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2426                        << "According to the Vulkan spec BuiltIn "
2427                        << _.grammar().lookupOperandName(
2428                               SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2429                        << "variable needs to be a 32-bit int scalar. "
2430                        << message;
2431               })) {
2432         return error;
2433       }
2434     } else {
2435       if (spv_result_t error = ValidateOptionalArrayedI32(
2436               decoration, inst,
2437               [this, &decoration,
2438                &inst](const std::string& message) -> spv_result_t {
2439                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2440                        << "According to the Vulkan spec BuiltIn "
2441                        << _.grammar().lookupOperandName(
2442                               SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0])
2443                        << "variable needs to be a 32-bit int scalar. "
2444                        << message;
2445               })) {
2446         return error;
2447       }
2448     }
2449   }
2450 
2451   // Seed at reference checks with this built-in.
2452   return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst);
2453 }
2454 
ValidateLayerOrViewportIndexAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2455 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
2456     const Decoration& decoration, const Instruction& built_in_inst,
2457     const Instruction& referenced_inst,
2458     const Instruction& referenced_from_inst) {
2459   if (spvIsVulkanEnv(_.context()->target_env)) {
2460     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2461     if (storage_class != SpvStorageClassMax &&
2462         storage_class != SpvStorageClassInput &&
2463         storage_class != SpvStorageClassOutput) {
2464       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2465              << "Vulkan spec allows BuiltIn "
2466              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2467                                               decoration.params()[0])
2468              << " to be only used for variables with Input or Output storage "
2469                 "class. "
2470              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2471                                  referenced_from_inst)
2472              << " " << GetStorageClassDesc(referenced_from_inst);
2473     }
2474 
2475     if (storage_class == SpvStorageClassInput) {
2476       assert(function_id_ == 0);
2477       for (const auto em :
2478            {SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation,
2479             SpvExecutionModelGeometry}) {
2480         id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2481             std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
2482                       this,
2483                       "Vulkan spec doesn't allow BuiltIn Layer and "
2484                       "ViewportIndex to be "
2485                       "used for variables with Input storage class if "
2486                       "execution model is Vertex, TessellationEvaluation, or "
2487                       "Geometry.",
2488                       em, decoration, built_in_inst, referenced_from_inst,
2489                       std::placeholders::_1));
2490       }
2491     }
2492 
2493     if (storage_class == SpvStorageClassOutput) {
2494       assert(function_id_ == 0);
2495       id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2496           &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
2497           "Vulkan spec doesn't allow BuiltIn Layer and "
2498           "ViewportIndex to be "
2499           "used for variables with Output storage class if "
2500           "execution model is "
2501           "Fragment.",
2502           SpvExecutionModelFragment, decoration, built_in_inst,
2503           referenced_from_inst, std::placeholders::_1));
2504     }
2505 
2506     for (const SpvExecutionModel execution_model : execution_models_) {
2507       switch (execution_model) {
2508         case SpvExecutionModelGeometry:
2509         case SpvExecutionModelFragment:
2510         case SpvExecutionModelMeshNV:
2511           // Ok.
2512           break;
2513         case SpvExecutionModelVertex:
2514         case SpvExecutionModelTessellationEvaluation: {
2515           if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) {
2516             return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2517                    << "Using BuiltIn "
2518                    << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2519                                                     decoration.params()[0])
2520                    << " in Vertex or Tessellation execution model requires "
2521                       "the ShaderViewportIndexLayerEXT capability.";
2522           }
2523           break;
2524         }
2525         default: {
2526           return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2527                  << "Vulkan spec allows BuiltIn "
2528                  << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2529                                                   decoration.params()[0])
2530                  << " to be used only with Vertex, TessellationEvaluation, "
2531                     "Geometry, or Fragment execution models. "
2532                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2533                                      referenced_from_inst, execution_model);
2534         }
2535       }
2536     }
2537   }
2538 
2539   if (function_id_ == 0) {
2540     // Propagate this rule to all dependant ids in the global scope.
2541     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2542         std::bind(&BuiltInsValidator::ValidateLayerOrViewportIndexAtReference,
2543                   this, decoration, built_in_inst, referenced_from_inst,
2544                   std::placeholders::_1));
2545   }
2546 
2547   return SPV_SUCCESS;
2548 }
2549 
ValidateComputeShaderI32Vec3InputAtDefinition(const Decoration & decoration,const Instruction & inst)2550 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
2551     const Decoration& decoration, const Instruction& inst) {
2552   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2553     if (spv_result_t error = ValidateI32Vec(
2554             decoration, inst, 3,
2555             [this, &decoration,
2556              &inst](const std::string& message) -> spv_result_t {
2557               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2558                      << "According to the "
2559                      << spvLogStringForEnv(_.context()->target_env)
2560                      << " spec BuiltIn "
2561                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2562                                                       decoration.params()[0])
2563                      << " variable needs to be a 3-component 32-bit int "
2564                         "vector. "
2565                      << message;
2566             })) {
2567       return error;
2568     }
2569   }
2570 
2571   // Seed at reference checks with this built-in.
2572   return ValidateComputeShaderI32Vec3InputAtReference(decoration, inst, inst,
2573                                                       inst);
2574 }
2575 
ValidateComputeShaderI32Vec3InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2576 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
2577     const Decoration& decoration, const Instruction& built_in_inst,
2578     const Instruction& referenced_inst,
2579     const Instruction& referenced_from_inst) {
2580   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2581     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2582     if (storage_class != SpvStorageClassMax &&
2583         storage_class != SpvStorageClassInput) {
2584       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2585              << spvLogStringForEnv(_.context()->target_env)
2586              << " spec allows BuiltIn "
2587              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2588                                               decoration.params()[0])
2589              << " to be only used for variables with Input storage class. "
2590              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2591                                  referenced_from_inst)
2592              << " " << GetStorageClassDesc(referenced_from_inst);
2593     }
2594 
2595     for (const SpvExecutionModel execution_model : execution_models_) {
2596       bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
2597                               execution_model == SpvExecutionModelTaskNV ||
2598                               execution_model == SpvExecutionModelMeshNV;
2599       bool has_webgpu_model = execution_model == SpvExecutionModelGLCompute;
2600       if ((spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) ||
2601           (spvIsWebGPUEnv(_.context()->target_env) && !has_webgpu_model)) {
2602         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2603                << spvLogStringForEnv(_.context()->target_env)
2604                << " spec allows BuiltIn "
2605                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2606                                                 decoration.params()[0])
2607                << " to be used only with GLCompute execution model. "
2608                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2609                                    referenced_from_inst, execution_model);
2610       }
2611     }
2612   }
2613 
2614   if (function_id_ == 0) {
2615     // Propagate this rule to all dependant ids in the global scope.
2616     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2617         &BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference, this,
2618         decoration, built_in_inst, referenced_from_inst,
2619         std::placeholders::_1));
2620   }
2621 
2622   return SPV_SUCCESS;
2623 }
2624 
ValidateComputeI32InputAtDefinition(const Decoration & decoration,const Instruction & inst)2625 spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition(
2626     const Decoration& decoration, const Instruction& inst) {
2627   if (spvIsVulkanEnv(_.context()->target_env)) {
2628     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2629       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2630              << "BuiltIn "
2631              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2632                                               decoration.params()[0])
2633              << " cannot be used as a member decoration ";
2634     }
2635     if (spv_result_t error = ValidateI32(
2636             decoration, inst,
2637             [this, &decoration,
2638              &inst](const std::string& message) -> spv_result_t {
2639               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2640                      << "According to the "
2641                      << spvLogStringForEnv(_.context()->target_env)
2642                      << " spec BuiltIn "
2643                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2644                                                       decoration.params()[0])
2645                      << " variable needs to be a 32-bit int "
2646                         "vector. "
2647                      << message;
2648             })) {
2649       return error;
2650     }
2651   }
2652 
2653   // Seed at reference checks with this built-in.
2654   return ValidateComputeI32InputAtReference(decoration, inst, inst, inst);
2655 }
2656 
ValidateComputeI32InputAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2657 spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference(
2658     const Decoration& decoration, const Instruction& built_in_inst,
2659     const Instruction& referenced_inst,
2660     const Instruction& referenced_from_inst) {
2661   if (spvIsVulkanEnv(_.context()->target_env)) {
2662     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2663     if (storage_class != SpvStorageClassMax &&
2664         storage_class != SpvStorageClassInput) {
2665       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2666              << spvLogStringForEnv(_.context()->target_env)
2667              << " spec allows BuiltIn "
2668              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2669                                               decoration.params()[0])
2670              << " to be only used for variables with Input storage class. "
2671              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2672                                  referenced_from_inst)
2673              << " " << GetStorageClassDesc(referenced_from_inst);
2674     }
2675 
2676     for (const SpvExecutionModel execution_model : execution_models_) {
2677       bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
2678                               execution_model == SpvExecutionModelTaskNV ||
2679                               execution_model == SpvExecutionModelMeshNV;
2680       if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
2681         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2682                << spvLogStringForEnv(_.context()->target_env)
2683                << " spec allows BuiltIn "
2684                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2685                                                 decoration.params()[0])
2686                << " to be used only with GLCompute execution model. "
2687                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2688                                    referenced_from_inst, execution_model);
2689       }
2690     }
2691   }
2692 
2693   if (function_id_ == 0) {
2694     // Propagate this rule to all dependant ids in the global scope.
2695     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
2696         std::bind(&BuiltInsValidator::ValidateComputeI32InputAtReference, this,
2697                   decoration, built_in_inst, referenced_from_inst,
2698                   std::placeholders::_1));
2699   }
2700 
2701   return SPV_SUCCESS;
2702 }
2703 
ValidateI32InputAtDefinition(const Decoration & decoration,const Instruction & inst)2704 spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition(
2705     const Decoration& decoration, const Instruction& inst) {
2706   if (spvIsVulkanEnv(_.context()->target_env)) {
2707     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2708       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2709              << "BuiltIn "
2710              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2711                                               decoration.params()[0])
2712              << " cannot be used as a member decoration ";
2713     }
2714     if (spv_result_t error = ValidateI32(
2715             decoration, inst,
2716             [this, &decoration,
2717              &inst](const std::string& message) -> spv_result_t {
2718               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2719                      << "According to the "
2720                      << spvLogStringForEnv(_.context()->target_env)
2721                      << " spec BuiltIn "
2722                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2723                                                       decoration.params()[0])
2724                      << " variable needs to be a 32-bit int. " << message;
2725             })) {
2726       return error;
2727     }
2728 
2729     const SpvStorageClass storage_class = GetStorageClass(inst);
2730     if (storage_class != SpvStorageClassMax &&
2731         storage_class != SpvStorageClassInput) {
2732       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2733              << spvLogStringForEnv(_.context()->target_env)
2734              << " spec allows BuiltIn "
2735              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2736                                               decoration.params()[0])
2737              << " to be only used for variables with Input storage class. "
2738              << GetReferenceDesc(decoration, inst, inst, inst) << " "
2739              << GetStorageClassDesc(inst);
2740     }
2741   }
2742 
2743   return SPV_SUCCESS;
2744 }
2745 
ValidateI32Vec4InputAtDefinition(const Decoration & decoration,const Instruction & inst)2746 spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition(
2747     const Decoration& decoration, const Instruction& inst) {
2748   if (spvIsVulkanEnv(_.context()->target_env)) {
2749     if (decoration.struct_member_index() != Decoration::kInvalidMember) {
2750       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2751              << "BuiltIn "
2752              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2753                                               decoration.params()[0])
2754              << " cannot be used as a member decoration ";
2755     }
2756     if (spv_result_t error = ValidateI32Vec(
2757             decoration, inst, 4,
2758             [this, &decoration,
2759              &inst](const std::string& message) -> spv_result_t {
2760               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2761                      << "According to the "
2762                      << spvLogStringForEnv(_.context()->target_env)
2763                      << " spec BuiltIn "
2764                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2765                                                       decoration.params()[0])
2766                      << " variable needs to be a 4-component 32-bit int "
2767                         "vector. "
2768                      << message;
2769             })) {
2770       return error;
2771     }
2772 
2773     const SpvStorageClass storage_class = GetStorageClass(inst);
2774     if (storage_class != SpvStorageClassMax &&
2775         storage_class != SpvStorageClassInput) {
2776       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2777              << spvLogStringForEnv(_.context()->target_env)
2778              << " spec allows BuiltIn "
2779              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2780                                               decoration.params()[0])
2781              << " to be only used for variables with Input storage class. "
2782              << GetReferenceDesc(decoration, inst, inst, inst) << " "
2783              << GetStorageClassDesc(inst);
2784     }
2785   }
2786 
2787   return SPV_SUCCESS;
2788 }
2789 
ValidateWorkgroupSizeAtDefinition(const Decoration & decoration,const Instruction & inst)2790 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition(
2791     const Decoration& decoration, const Instruction& inst) {
2792   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2793     if (spvIsVulkanEnv(_.context()->target_env) &&
2794         !spvOpcodeIsConstant(inst.opcode())) {
2795       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2796              << "Vulkan spec requires BuiltIn WorkgroupSize to be a "
2797                 "constant. "
2798              << GetIdDesc(inst) << " is not a constant.";
2799     }
2800 
2801     if (spv_result_t error = ValidateI32Vec(
2802             decoration, inst, 3,
2803             [this, &inst](const std::string& message) -> spv_result_t {
2804               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2805                      << "According to the "
2806                      << spvLogStringForEnv(_.context()->target_env)
2807                      << " spec BuiltIn WorkgroupSize variable needs to be a "
2808                         "3-component 32-bit int vector. "
2809                      << message;
2810             })) {
2811       return error;
2812     }
2813   }
2814 
2815   // Seed at reference checks with this built-in.
2816   return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst);
2817 }
2818 
ValidateWorkgroupSizeAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2819 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
2820     const Decoration& decoration, const Instruction& built_in_inst,
2821     const Instruction& referenced_inst,
2822     const Instruction& referenced_from_inst) {
2823   if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2824     for (const SpvExecutionModel execution_model : execution_models_) {
2825       if (execution_model != SpvExecutionModelGLCompute) {
2826         return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2827                << spvLogStringForEnv(_.context()->target_env)
2828                << " spec allows BuiltIn "
2829                << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2830                                                 decoration.params()[0])
2831                << " to be used only with GLCompute execution model. "
2832                << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2833                                    referenced_from_inst, execution_model);
2834       }
2835     }
2836   }
2837 
2838   if (function_id_ == 0) {
2839     // Propagate this rule to all dependant ids in the global scope.
2840     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2841         &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration,
2842         built_in_inst, referenced_from_inst, std::placeholders::_1));
2843   }
2844 
2845   return SPV_SUCCESS;
2846 }
2847 
ValidateSMBuiltinsAtDefinition(const Decoration & decoration,const Instruction & inst)2848 spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition(
2849     const Decoration& decoration, const Instruction& inst) {
2850   if (spvIsVulkanEnv(_.context()->target_env)) {
2851     if (spv_result_t error = ValidateI32(
2852             decoration, inst,
2853             [this, &inst,
2854              &decoration](const std::string& message) -> spv_result_t {
2855               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2856                      << "According to the "
2857                      << spvLogStringForEnv(_.context()->target_env)
2858                      << " spec BuiltIn "
2859                      << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2860                                                       decoration.params()[0])
2861                      << " variable needs to be a 32-bit int scalar. "
2862                      << message;
2863             })) {
2864       return error;
2865     }
2866   }
2867 
2868   // Seed at reference checks with this built-in.
2869   return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst);
2870 }
2871 
ValidateSMBuiltinsAtReference(const Decoration & decoration,const Instruction & built_in_inst,const Instruction & referenced_inst,const Instruction & referenced_from_inst)2872 spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference(
2873     const Decoration& decoration, const Instruction& built_in_inst,
2874     const Instruction& referenced_inst,
2875     const Instruction& referenced_from_inst) {
2876   if (spvIsVulkanEnv(_.context()->target_env)) {
2877     const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
2878     if (storage_class != SpvStorageClassMax &&
2879         storage_class != SpvStorageClassInput) {
2880       return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
2881              << spvLogStringForEnv(_.context()->target_env)
2882              << " spec allows BuiltIn "
2883              << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2884                                               decoration.params()[0])
2885              << " to be only used for "
2886                 "variables with Input storage class. "
2887              << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
2888                                  referenced_from_inst)
2889              << " " << GetStorageClassDesc(referenced_from_inst);
2890     }
2891   }
2892 
2893   if (function_id_ == 0) {
2894     // Propagate this rule to all dependant ids in the global scope.
2895     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
2896         &BuiltInsValidator::ValidateSMBuiltinsAtReference, this, decoration,
2897         built_in_inst, referenced_from_inst, std::placeholders::_1));
2898   }
2899 
2900   return SPV_SUCCESS;
2901 }
2902 
ValidateSingleBuiltInAtDefinition(const Decoration & decoration,const Instruction & inst)2903 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
2904     const Decoration& decoration, const Instruction& inst) {
2905   const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
2906 
2907   // Builtins can only be applied to variables, structures or constants.
2908   auto target_opcode = inst.opcode();
2909   if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable &&
2910       !spvOpcodeIsConstant(target_opcode)) {
2911     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2912            << "BuiltIns can only target variables, structs or constants";
2913   }
2914 
2915   if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
2916     // Early return. All currently implemented rules are based on Vulkan or
2917     // WebGPU spec.
2918     //
2919     // TODO: If you are adding validation rules for environments other than
2920     // Vulkan or WebGPU (or general rules which are not environment
2921     // independent), then you need to modify or remove this condition. Consider
2922     // also adding early returns into BuiltIn-specific rules, so that the system
2923     // doesn't spawn new rules which don't do anything.
2924     return SPV_SUCCESS;
2925   }
2926 
2927   if (spvIsWebGPUEnv(_.context()->target_env) &&
2928       !IsBuiltInValidForWebGPU(label)) {
2929     return _.diag(SPV_ERROR_INVALID_DATA, &inst)
2930            << "WebGPU does not allow BuiltIn "
2931            << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
2932                                             decoration.params()[0]);
2933   }
2934 
2935   // If you are adding a new BuiltIn enum, please register it here.
2936   // If the newly added enum has validation rules associated with it
2937   // consider leaving a TODO and/or creating an issue.
2938   switch (label) {
2939     case SpvBuiltInClipDistance:
2940     case SpvBuiltInCullDistance: {
2941       return ValidateClipOrCullDistanceAtDefinition(decoration, inst);
2942     }
2943     case SpvBuiltInFragCoord: {
2944       return ValidateFragCoordAtDefinition(decoration, inst);
2945     }
2946     case SpvBuiltInFragDepth: {
2947       return ValidateFragDepthAtDefinition(decoration, inst);
2948     }
2949     case SpvBuiltInFrontFacing: {
2950       return ValidateFrontFacingAtDefinition(decoration, inst);
2951     }
2952     case SpvBuiltInGlobalInvocationId:
2953     case SpvBuiltInLocalInvocationId:
2954     case SpvBuiltInNumWorkgroups:
2955     case SpvBuiltInWorkgroupId: {
2956       return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
2957     }
2958     case SpvBuiltInHelperInvocation: {
2959       return ValidateHelperInvocationAtDefinition(decoration, inst);
2960     }
2961     case SpvBuiltInInvocationId: {
2962       return ValidateInvocationIdAtDefinition(decoration, inst);
2963     }
2964     case SpvBuiltInInstanceIndex: {
2965       return ValidateInstanceIndexAtDefinition(decoration, inst);
2966     }
2967     case SpvBuiltInLayer:
2968     case SpvBuiltInViewportIndex: {
2969       return ValidateLayerOrViewportIndexAtDefinition(decoration, inst);
2970     }
2971     case SpvBuiltInPatchVertices: {
2972       return ValidatePatchVerticesAtDefinition(decoration, inst);
2973     }
2974     case SpvBuiltInPointCoord: {
2975       return ValidatePointCoordAtDefinition(decoration, inst);
2976     }
2977     case SpvBuiltInPointSize: {
2978       return ValidatePointSizeAtDefinition(decoration, inst);
2979     }
2980     case SpvBuiltInPosition: {
2981       return ValidatePositionAtDefinition(decoration, inst);
2982     }
2983     case SpvBuiltInPrimitiveId: {
2984       return ValidatePrimitiveIdAtDefinition(decoration, inst);
2985     }
2986     case SpvBuiltInSampleId: {
2987       return ValidateSampleIdAtDefinition(decoration, inst);
2988     }
2989     case SpvBuiltInSampleMask: {
2990       return ValidateSampleMaskAtDefinition(decoration, inst);
2991     }
2992     case SpvBuiltInSamplePosition: {
2993       return ValidateSamplePositionAtDefinition(decoration, inst);
2994     }
2995     case SpvBuiltInSubgroupId:
2996     case SpvBuiltInNumSubgroups: {
2997       return ValidateComputeI32InputAtDefinition(decoration, inst);
2998     }
2999     case SpvBuiltInSubgroupLocalInvocationId:
3000     case SpvBuiltInSubgroupSize: {
3001       return ValidateI32InputAtDefinition(decoration, inst);
3002     }
3003     case SpvBuiltInSubgroupEqMask:
3004     case SpvBuiltInSubgroupGeMask:
3005     case SpvBuiltInSubgroupGtMask:
3006     case SpvBuiltInSubgroupLeMask:
3007     case SpvBuiltInSubgroupLtMask: {
3008       return ValidateI32Vec4InputAtDefinition(decoration, inst);
3009     }
3010     case SpvBuiltInTessCoord: {
3011       return ValidateTessCoordAtDefinition(decoration, inst);
3012     }
3013     case SpvBuiltInTessLevelOuter: {
3014       return ValidateTessLevelOuterAtDefinition(decoration, inst);
3015     }
3016     case SpvBuiltInTessLevelInner: {
3017       return ValidateTessLevelInnerAtDefinition(decoration, inst);
3018     }
3019     case SpvBuiltInVertexIndex: {
3020       return ValidateVertexIndexAtDefinition(decoration, inst);
3021     }
3022     case SpvBuiltInWorkgroupSize: {
3023       return ValidateWorkgroupSizeAtDefinition(decoration, inst);
3024     }
3025     case SpvBuiltInVertexId:
3026     case SpvBuiltInInstanceId: {
3027       return ValidateVertexIdOrInstanceIdAtDefinition(decoration, inst);
3028     }
3029     case SpvBuiltInLocalInvocationIndex: {
3030       return ValidateLocalInvocationIndexAtDefinition(decoration, inst);
3031     }
3032     case SpvBuiltInWarpsPerSMNV:
3033     case SpvBuiltInSMCountNV:
3034     case SpvBuiltInWarpIDNV:
3035     case SpvBuiltInSMIDNV: {
3036       return ValidateSMBuiltinsAtDefinition(decoration, inst);
3037     }
3038     case SpvBuiltInWorkDim:
3039     case SpvBuiltInGlobalSize:
3040     case SpvBuiltInEnqueuedWorkgroupSize:
3041     case SpvBuiltInGlobalOffset:
3042     case SpvBuiltInGlobalLinearId:
3043     case SpvBuiltInSubgroupMaxSize:
3044     case SpvBuiltInNumEnqueuedSubgroups:
3045     case SpvBuiltInBaseVertex:
3046     case SpvBuiltInBaseInstance:
3047     case SpvBuiltInDrawIndex:
3048     case SpvBuiltInDeviceIndex:
3049     case SpvBuiltInViewIndex:
3050     case SpvBuiltInBaryCoordNoPerspAMD:
3051     case SpvBuiltInBaryCoordNoPerspCentroidAMD:
3052     case SpvBuiltInBaryCoordNoPerspSampleAMD:
3053     case SpvBuiltInBaryCoordSmoothAMD:
3054     case SpvBuiltInBaryCoordSmoothCentroidAMD:
3055     case SpvBuiltInBaryCoordSmoothSampleAMD:
3056     case SpvBuiltInBaryCoordPullModelAMD:
3057     case SpvBuiltInFragStencilRefEXT:
3058     case SpvBuiltInViewportMaskNV:
3059     case SpvBuiltInSecondaryPositionNV:
3060     case SpvBuiltInSecondaryViewportMaskNV:
3061     case SpvBuiltInPositionPerViewNV:
3062     case SpvBuiltInViewportMaskPerViewNV:
3063     case SpvBuiltInFullyCoveredEXT:
3064     case SpvBuiltInMax:
3065     case SpvBuiltInTaskCountNV:
3066     case SpvBuiltInPrimitiveCountNV:
3067     case SpvBuiltInPrimitiveIndicesNV:
3068     case SpvBuiltInClipDistancePerViewNV:
3069     case SpvBuiltInCullDistancePerViewNV:
3070     case SpvBuiltInLayerPerViewNV:
3071     case SpvBuiltInMeshViewCountNV:
3072     case SpvBuiltInMeshViewIndicesNV:
3073     case SpvBuiltInBaryCoordNV:
3074     case SpvBuiltInBaryCoordNoPerspNV:
3075     case SpvBuiltInFragmentSizeNV:         // alias SpvBuiltInFragSizeEXT
3076     case SpvBuiltInInvocationsPerPixelNV:  // alias
3077                                            // SpvBuiltInFragInvocationCountEXT
3078     case SpvBuiltInLaunchIdNV:
3079     case SpvBuiltInLaunchSizeNV:
3080     case SpvBuiltInWorldRayOriginNV:
3081     case SpvBuiltInWorldRayDirectionNV:
3082     case SpvBuiltInObjectRayOriginNV:
3083     case SpvBuiltInObjectRayDirectionNV:
3084     case SpvBuiltInRayTminNV:
3085     case SpvBuiltInRayTmaxNV:
3086     case SpvBuiltInInstanceCustomIndexNV:
3087     case SpvBuiltInObjectToWorldNV:
3088     case SpvBuiltInWorldToObjectNV:
3089     case SpvBuiltInHitTNV:
3090     case SpvBuiltInHitKindNV:
3091     case SpvBuiltInIncomingRayFlagsNV:
3092     case SpvBuiltInRayGeometryIndexKHR: {
3093       // No validation rules (for the moment).
3094       break;
3095     }
3096   }
3097   return SPV_SUCCESS;
3098 }
3099 
ValidateBuiltInsAtDefinition()3100 spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() {
3101   for (const auto& kv : _.id_decorations()) {
3102     const uint32_t id = kv.first;
3103     const auto& decorations = kv.second;
3104     if (decorations.empty()) {
3105       continue;
3106     }
3107 
3108     const Instruction* inst = _.FindDef(id);
3109     assert(inst);
3110 
3111     for (const auto& decoration : kv.second) {
3112       if (decoration.dec_type() != SpvDecorationBuiltIn) {
3113         continue;
3114       }
3115 
3116       if (spv_result_t error =
3117               ValidateSingleBuiltInAtDefinition(decoration, *inst)) {
3118         return error;
3119       }
3120     }
3121   }
3122 
3123   return SPV_SUCCESS;
3124 }
3125 
Run()3126 spv_result_t BuiltInsValidator::Run() {
3127   // First pass: validate all built-ins at definition and seed
3128   // id_to_at_reference_checks_ with built-ins.
3129   if (auto error = ValidateBuiltInsAtDefinition()) {
3130     return error;
3131   }
3132 
3133   if (id_to_at_reference_checks_.empty()) {
3134     // No validation tasks were seeded. Nothing else to do.
3135     return SPV_SUCCESS;
3136   }
3137 
3138   // Second pass: validate every id reference in the module using
3139   // rules in id_to_at_reference_checks_.
3140   for (const Instruction& inst : _.ordered_instructions()) {
3141     Update(inst);
3142 
3143     std::set<uint32_t> already_checked;
3144 
3145     for (const auto& operand : inst.operands()) {
3146       if (!spvIsIdType(operand.type)) {
3147         // Not id.
3148         continue;
3149       }
3150 
3151       const uint32_t id = inst.word(operand.offset);
3152       if (id == inst.id()) {
3153         // No need to check result id.
3154         continue;
3155       }
3156 
3157       if (!already_checked.insert(id).second) {
3158         // The instruction has already referenced this id.
3159         continue;
3160       }
3161 
3162       // Instruction references the id. Run all checks associated with the id
3163       // on the instruction. id_to_at_reference_checks_ can be modified in the
3164       // process, iterators are safe because it's a tree-based map.
3165       const auto it = id_to_at_reference_checks_.find(id);
3166       if (it != id_to_at_reference_checks_.end()) {
3167         for (const auto& check : it->second) {
3168           if (spv_result_t error = check(inst)) {
3169             return error;
3170           }
3171         }
3172       }
3173     }
3174   }
3175 
3176   return SPV_SUCCESS;
3177 }
3178 
3179 }  // namespace
3180 
3181 // Validates correctness of built-in variables.
ValidateBuiltIns(ValidationState_t & _)3182 spv_result_t ValidateBuiltIns(ValidationState_t& _) {
3183   BuiltInsValidator validator(_);
3184   return validator.Run();
3185 }
3186 
3187 }  // namespace val
3188 }  // namespace spvtools
3189