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