Home | History | Annotate | Download | only in val
      1 // Copyright (c) 2017 Google Inc.
      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 #include "source/val/validate.h"
     16 
     17 #include <algorithm>
     18 #include <cassert>
     19 #include <string>
     20 #include <tuple>
     21 #include <unordered_map>
     22 #include <unordered_set>
     23 #include <utility>
     24 #include <vector>
     25 
     26 #include "source/diagnostic.h"
     27 #include "source/opcode.h"
     28 #include "source/spirv_target_env.h"
     29 #include "source/spirv_validator_options.h"
     30 #include "source/val/validation_state.h"
     31 
     32 namespace spvtools {
     33 namespace val {
     34 namespace {
     35 
     36 // Distinguish between row and column major matrix layouts.
     37 enum MatrixLayout { kRowMajor, kColumnMajor };
     38 
     39 // A functor for hashing a pair of integers.
     40 struct PairHash {
     41   std::size_t operator()(const std::pair<uint32_t, uint32_t> pair) const {
     42     const uint32_t a = pair.first;
     43     const uint32_t b = pair.second;
     44     const uint32_t rotated_b = (b >> 2) | ((b & 3) << 30);
     45     return a ^ rotated_b;
     46   }
     47 };
     48 
     49 // A functor for hashing decoration types.
     50 struct SpvDecorationHash {
     51   std::size_t operator()(SpvDecoration dec) const {
     52     return static_cast<std::size_t>(dec);
     53   }
     54 };
     55 
     56 // Struct member layout attributes that are inherited through arrays.
     57 struct LayoutConstraints {
     58   explicit LayoutConstraints(
     59       MatrixLayout the_majorness = MatrixLayout::kColumnMajor,
     60       uint32_t stride = 0)
     61       : majorness(the_majorness), matrix_stride(stride) {}
     62   MatrixLayout majorness;
     63   uint32_t matrix_stride;
     64 };
     65 
     66 // A type for mapping (struct id, member id) to layout constraints.
     67 using MemberConstraints = std::unordered_map<std::pair<uint32_t, uint32_t>,
     68                                              LayoutConstraints, PairHash>;
     69 
     70 // Returns the array stride of the given array type.
     71 uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) {
     72   for (auto& decoration : vstate.id_decorations(array_id)) {
     73     if (SpvDecorationArrayStride == decoration.dec_type()) {
     74       return decoration.params()[0];
     75     }
     76   }
     77   return 0;
     78 }
     79 
     80 // Returns true if the given variable has a BuiltIn decoration.
     81 bool isBuiltInVar(uint32_t var_id, ValidationState_t& vstate) {
     82   const auto& decorations = vstate.id_decorations(var_id);
     83   return std::any_of(
     84       decorations.begin(), decorations.end(),
     85       [](const Decoration& d) { return SpvDecorationBuiltIn == d.dec_type(); });
     86 }
     87 
     88 // Returns true if the given structure type has any members with BuiltIn
     89 // decoration.
     90 bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) {
     91   const auto& decorations = vstate.id_decorations(struct_id);
     92   return std::any_of(
     93       decorations.begin(), decorations.end(), [](const Decoration& d) {
     94         return SpvDecorationBuiltIn == d.dec_type() &&
     95                Decoration::kInvalidMember != d.struct_member_index();
     96       });
     97 }
     98 
     99 // Returns true if the given ID has the Import LinkageAttributes decoration.
    100 bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) {
    101   const auto& decorations = vstate.id_decorations(id);
    102   return std::any_of(decorations.begin(), decorations.end(),
    103                      [](const Decoration& d) {
    104                        return SpvDecorationLinkageAttributes == d.dec_type() &&
    105                               d.params().size() >= 2u &&
    106                               d.params().back() == SpvLinkageTypeImport;
    107                      });
    108 }
    109 
    110 // Returns a vector of all members of a structure.
    111 std::vector<uint32_t> getStructMembers(uint32_t struct_id,
    112                                        ValidationState_t& vstate) {
    113   const auto inst = vstate.FindDef(struct_id);
    114   return std::vector<uint32_t>(inst->words().begin() + 2, inst->words().end());
    115 }
    116 
    117 // Returns a vector of all members of a structure that have specific type.
    118 std::vector<uint32_t> getStructMembers(uint32_t struct_id, SpvOp type,
    119                                        ValidationState_t& vstate) {
    120   std::vector<uint32_t> members;
    121   for (auto id : getStructMembers(struct_id, vstate)) {
    122     if (type == vstate.FindDef(id)->opcode()) {
    123       members.push_back(id);
    124     }
    125   }
    126   return members;
    127 }
    128 
    129 // Returns whether the given structure is missing Offset decoration for any
    130 // member. Handles also nested structures.
    131 bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
    132   std::vector<bool> hasOffset(getStructMembers(struct_id, vstate).size(),
    133                               false);
    134   // Check offsets of member decorations
    135   for (auto& decoration : vstate.id_decorations(struct_id)) {
    136     if (SpvDecorationOffset == decoration.dec_type() &&
    137         Decoration::kInvalidMember != decoration.struct_member_index()) {
    138       hasOffset[decoration.struct_member_index()] = true;
    139     }
    140   }
    141   // Check also nested structures
    142   bool nestedStructsMissingOffset = false;
    143   for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
    144     if (isMissingOffsetInStruct(id, vstate)) {
    145       nestedStructsMissingOffset = true;
    146       break;
    147     }
    148   }
    149   return nestedStructsMissingOffset ||
    150          !std::all_of(hasOffset.begin(), hasOffset.end(),
    151                       [](const bool b) { return b; });
    152 }
    153 
    154 // Rounds x up to the next alignment. Assumes alignment is a power of two.
    155 uint32_t align(uint32_t x, uint32_t alignment) {
    156   return (x + alignment - 1) & ~(alignment - 1);
    157 }
    158 
    159 // Returns base alignment of struct member. If |roundUp| is true, also
    160 // ensure that structs and arrays are aligned at least to a multiple of 16
    161 // bytes.
    162 uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
    163                           const LayoutConstraints& inherited,
    164                           MemberConstraints& constraints,
    165                           ValidationState_t& vstate) {
    166   const auto inst = vstate.FindDef(member_id);
    167   const auto& words = inst->words();
    168   // Minimal alignment is byte-aligned.
    169   uint32_t baseAlignment = 1;
    170   switch (inst->opcode()) {
    171     case SpvOpTypeInt:
    172     case SpvOpTypeFloat:
    173       baseAlignment = words[2] / 8;
    174       break;
    175     case SpvOpTypeVector: {
    176       const auto componentId = words[2];
    177       const auto numComponents = words[3];
    178       const auto componentAlignment = getBaseAlignment(
    179           componentId, roundUp, inherited, constraints, vstate);
    180       baseAlignment =
    181           componentAlignment * (numComponents == 3 ? 4 : numComponents);
    182       break;
    183     }
    184     case SpvOpTypeMatrix: {
    185       const auto column_type = words[2];
    186       if (inherited.majorness == kColumnMajor) {
    187         baseAlignment = getBaseAlignment(column_type, roundUp, inherited,
    188                                          constraints, vstate);
    189       } else {
    190         // A row-major matrix of C columns has a base alignment equal to the
    191         // base alignment of a vector of C matrix components.
    192         const auto num_columns = words[3];
    193         const auto component_inst = vstate.FindDef(column_type);
    194         const auto component_id = component_inst->words()[2];
    195         const auto componentAlignment = getBaseAlignment(
    196             component_id, roundUp, inherited, constraints, vstate);
    197         baseAlignment =
    198             componentAlignment * (num_columns == 3 ? 4 : num_columns);
    199       }
    200     } break;
    201     case SpvOpTypeArray:
    202     case SpvOpTypeRuntimeArray:
    203       baseAlignment =
    204           getBaseAlignment(words[2], roundUp, inherited, constraints, vstate);
    205       if (roundUp) baseAlignment = align(baseAlignment, 16u);
    206       break;
    207     case SpvOpTypeStruct: {
    208       const auto members = getStructMembers(member_id, vstate);
    209       for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
    210            memberIdx < numMembers; ++memberIdx) {
    211         const auto id = members[memberIdx];
    212         const auto& constraint =
    213             constraints[std::make_pair(member_id, memberIdx)];
    214         baseAlignment = std::max(
    215             baseAlignment,
    216             getBaseAlignment(id, roundUp, constraint, constraints, vstate));
    217       }
    218       if (roundUp) baseAlignment = align(baseAlignment, 16u);
    219       break;
    220     }
    221     case SpvOpTypePointer:
    222       baseAlignment = vstate.pointer_size_and_alignment();
    223       break;
    224     default:
    225       assert(0);
    226       break;
    227   }
    228 
    229   return baseAlignment;
    230 }
    231 
    232 // Returns scalar alignment of a type.
    233 uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
    234   const auto inst = vstate.FindDef(type_id);
    235   const auto& words = inst->words();
    236   switch (inst->opcode()) {
    237     case SpvOpTypeInt:
    238     case SpvOpTypeFloat:
    239       return words[2] / 8;
    240     case SpvOpTypeVector:
    241     case SpvOpTypeMatrix:
    242     case SpvOpTypeArray:
    243     case SpvOpTypeRuntimeArray: {
    244       const auto compositeMemberTypeId = words[2];
    245       return getScalarAlignment(compositeMemberTypeId, vstate);
    246     }
    247     case SpvOpTypeStruct: {
    248       const auto members = getStructMembers(type_id, vstate);
    249       uint32_t max_member_alignment = 1;
    250       for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
    251            memberIdx < numMembers; ++memberIdx) {
    252         const auto id = members[memberIdx];
    253         uint32_t member_alignment = getScalarAlignment(id, vstate);
    254         if (member_alignment > max_member_alignment) {
    255           max_member_alignment = member_alignment;
    256         }
    257       }
    258       return max_member_alignment;
    259     } break;
    260     case SpvOpTypePointer:
    261       return vstate.pointer_size_and_alignment();
    262     default:
    263       assert(0);
    264       break;
    265   }
    266 
    267   return 1;
    268 }
    269 
    270 // Returns size of a struct member. Doesn't include padding at the end of struct
    271 // or array.  Assumes that in the struct case, all members have offsets.
    272 uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
    273                  MemberConstraints& constraints, ValidationState_t& vstate) {
    274   const auto inst = vstate.FindDef(member_id);
    275   const auto& words = inst->words();
    276   switch (inst->opcode()) {
    277     case SpvOpTypeInt:
    278     case SpvOpTypeFloat:
    279       return words[2] / 8;
    280     case SpvOpTypeVector: {
    281       const auto componentId = words[2];
    282       const auto numComponents = words[3];
    283       const auto componentSize =
    284           getSize(componentId, inherited, constraints, vstate);
    285       const auto size = componentSize * numComponents;
    286       return size;
    287     }
    288     case SpvOpTypeArray: {
    289       const auto sizeInst = vstate.FindDef(words[3]);
    290       if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0;
    291       assert(SpvOpConstant == sizeInst->opcode());
    292       const uint32_t num_elem = sizeInst->words()[3];
    293       const uint32_t elem_type = words[2];
    294       const uint32_t elem_size =
    295           getSize(elem_type, inherited, constraints, vstate);
    296       // Account for gaps due to alignments in the first N-1 elements,
    297       // then add the size of the last element.
    298       const auto size =
    299           (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size;
    300       return size;
    301     }
    302     case SpvOpTypeRuntimeArray:
    303       return 0;
    304     case SpvOpTypeMatrix: {
    305       const auto num_columns = words[3];
    306       if (inherited.majorness == kColumnMajor) {
    307         return num_columns * inherited.matrix_stride;
    308       } else {
    309         // Row major case.
    310         const auto column_type = words[2];
    311         const auto component_inst = vstate.FindDef(column_type);
    312         const auto num_rows = component_inst->words()[3];
    313         const auto scalar_elem_type = component_inst->words()[2];
    314         const uint32_t scalar_elem_size =
    315             getSize(scalar_elem_type, inherited, constraints, vstate);
    316         return (num_rows - 1) * inherited.matrix_stride +
    317                num_columns * scalar_elem_size;
    318       }
    319     }
    320     case SpvOpTypeStruct: {
    321       const auto& members = getStructMembers(member_id, vstate);
    322       if (members.empty()) return 0;
    323       const auto lastIdx = uint32_t(members.size() - 1);
    324       const auto& lastMember = members.back();
    325       uint32_t offset = 0xffffffff;
    326       // Find the offset of the last element and add the size.
    327       for (auto& decoration : vstate.id_decorations(member_id)) {
    328         if (SpvDecorationOffset == decoration.dec_type() &&
    329             decoration.struct_member_index() == (int)lastIdx) {
    330           offset = decoration.params()[0];
    331         }
    332       }
    333       // This check depends on the fact that all members have offsets.  This
    334       // has been checked earlier in the flow.
    335       assert(offset != 0xffffffff);
    336       const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
    337       return offset + getSize(lastMember, constraint, constraints, vstate);
    338     }
    339     case SpvOpTypePointer:
    340       return vstate.pointer_size_and_alignment();
    341     default:
    342       assert(0);
    343       return 0;
    344   }
    345 }
    346 
    347 // A member is defined to improperly straddle if either of the following are
    348 // true:
    349 // - It is a vector with total size less than or equal to 16 bytes, and has
    350 // Offset decorations placing its first byte at F and its last byte at L, where
    351 // floor(F / 16) != floor(L / 16).
    352 // - It is a vector with total size greater than 16 bytes and has its Offset
    353 // decorations placing its first byte at a non-integer multiple of 16.
    354 bool hasImproperStraddle(uint32_t id, uint32_t offset,
    355                          const LayoutConstraints& inherited,
    356                          MemberConstraints& constraints,
    357                          ValidationState_t& vstate) {
    358   const auto size = getSize(id, inherited, constraints, vstate);
    359   const auto F = offset;
    360   const auto L = offset + size - 1;
    361   if (size <= 16) {
    362     if ((F >> 4) != (L >> 4)) return true;
    363   } else {
    364     if (F % 16 != 0) return true;
    365   }
    366   return false;
    367 }
    368 
    369 // Returns true if |offset| satsifies an alignment to |alignment|.  In the case
    370 // of |alignment| of zero, the |offset| must also be zero.
    371 bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
    372   if (alignment == 0) return offset == 0;
    373   return 0 == (offset % alignment);
    374 }
    375 
    376 // Returns SPV_SUCCESS if the given struct satisfies standard layout rules for
    377 // Block or BufferBlocks in Vulkan.  Otherwise emits a diagnostic and returns
    378 // something other than SPV_SUCCESS.  Matrices inherit the specified column
    379 // or row major-ness.
    380 spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
    381                          const char* decoration_str, bool blockRules,
    382                          MemberConstraints& constraints,
    383                          ValidationState_t& vstate) {
    384   if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
    385 
    386   // Relaxed layout and scalar layout can both be in effect at the same time.
    387   // For example, relaxed layout is implied by Vulkan 1.1.  But scalar layout
    388   // is more permissive than relaxed layout.
    389   const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
    390   const bool scalar_block_layout = vstate.options()->scalar_block_layout;
    391 
    392   auto fail = [&vstate, struct_id, storage_class_str, decoration_str,
    393                blockRules, relaxed_block_layout,
    394                scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
    395     DiagnosticStream ds =
    396         std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
    397                   << "Structure id " << struct_id << " decorated as "
    398                   << decoration_str << " for variable in " << storage_class_str
    399                   << " storage class must follow "
    400                   << (scalar_block_layout
    401                           ? "scalar "
    402                           : (relaxed_block_layout ? "relaxed " : "standard "))
    403                   << (blockRules ? "uniform buffer" : "storage buffer")
    404                   << " layout rules: member " << member_idx << " ");
    405     return ds;
    406   };
    407 
    408   const auto& members = getStructMembers(struct_id, vstate);
    409 
    410   // To check for member overlaps, we want to traverse the members in
    411   // offset order.
    412   struct MemberOffsetPair {
    413     uint32_t member;
    414     uint32_t offset;
    415   };
    416   std::vector<MemberOffsetPair> member_offsets;
    417   member_offsets.reserve(members.size());
    418   for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
    419        memberIdx < numMembers; memberIdx++) {
    420     uint32_t offset = 0xffffffff;
    421     for (auto& decoration : vstate.id_decorations(struct_id)) {
    422       if (decoration.struct_member_index() == (int)memberIdx) {
    423         switch (decoration.dec_type()) {
    424           case SpvDecorationOffset:
    425             offset = decoration.params()[0];
    426             break;
    427           default:
    428             break;
    429         }
    430       }
    431     }
    432     member_offsets.push_back(MemberOffsetPair{memberIdx, offset});
    433   }
    434   std::stable_sort(
    435       member_offsets.begin(), member_offsets.end(),
    436       [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) {
    437         return lhs.offset < rhs.offset;
    438       });
    439 
    440   // Now scan from lowest offest to highest offset.
    441   uint32_t nextValidOffset = 0;
    442   for (size_t ordered_member_idx = 0;
    443        ordered_member_idx < member_offsets.size(); ordered_member_idx++) {
    444     const auto& member_offset = member_offsets[ordered_member_idx];
    445     const auto memberIdx = member_offset.member;
    446     const auto offset = member_offset.offset;
    447     auto id = members[member_offset.member];
    448     const LayoutConstraints& constraint =
    449         constraints[std::make_pair(struct_id, uint32_t(memberIdx))];
    450     // Scalar layout takes precedence because it's more permissive, and implying
    451     // an alignment that divides evenly into the alignment that would otherwise
    452     // be used.
    453     const auto alignment =
    454         scalar_block_layout
    455             ? getScalarAlignment(id, vstate)
    456             : getBaseAlignment(id, blockRules, constraint, constraints, vstate);
    457     const auto inst = vstate.FindDef(id);
    458     const auto opcode = inst->opcode();
    459     const auto size = getSize(id, constraint, constraints, vstate);
    460     // Check offset.
    461     if (offset == 0xffffffff)
    462       return fail(memberIdx) << "is missing an Offset decoration";
    463     if (!scalar_block_layout && relaxed_block_layout &&
    464         opcode == SpvOpTypeVector) {
    465       // In relaxed block layout, the vector offset must be aligned to the
    466       // vector's scalar element type.
    467       const auto componentId = inst->words()[2];
    468       const auto scalar_alignment = getScalarAlignment(componentId, vstate);
    469       if (!IsAlignedTo(offset, scalar_alignment)) {
    470         return fail(memberIdx)
    471                << "at offset " << offset
    472                << " is not aligned to scalar element size " << scalar_alignment;
    473       }
    474     } else {
    475       // Without relaxed block layout, the offset must be divisible by the
    476       // alignment requirement.
    477       if (!IsAlignedTo(offset, alignment)) {
    478         return fail(memberIdx)
    479                << "at offset " << offset << " is not aligned to " << alignment;
    480       }
    481     }
    482     if (offset < nextValidOffset)
    483       return fail(memberIdx) << "at offset " << offset
    484                              << " overlaps previous member ending at offset "
    485                              << nextValidOffset - 1;
    486     if (!scalar_block_layout && relaxed_block_layout) {
    487       // Check improper straddle of vectors.
    488       if (SpvOpTypeVector == opcode &&
    489           hasImproperStraddle(id, offset, constraint, constraints, vstate))
    490         return fail(memberIdx)
    491                << "is an improperly straddling vector at offset " << offset;
    492     }
    493     // Check struct members recursively.
    494     spv_result_t recursive_status = SPV_SUCCESS;
    495     if (SpvOpTypeStruct == opcode &&
    496         SPV_SUCCESS != (recursive_status =
    497                             checkLayout(id, storage_class_str, decoration_str,
    498                                         blockRules, constraints, vstate)))
    499       return recursive_status;
    500     // Check matrix stride.
    501     if (SpvOpTypeMatrix == opcode) {
    502       for (auto& decoration : vstate.id_decorations(id)) {
    503         if (SpvDecorationMatrixStride == decoration.dec_type() &&
    504             !IsAlignedTo(decoration.params()[0], alignment))
    505           return fail(memberIdx)
    506                  << "is a matrix with stride " << decoration.params()[0]
    507                  << " not satisfying alignment to " << alignment;
    508       }
    509     }
    510     // Check arrays and runtime arrays.
    511     if (SpvOpTypeArray == opcode || SpvOpTypeRuntimeArray == opcode) {
    512       const auto typeId = inst->word(2);
    513       const auto arrayInst = vstate.FindDef(typeId);
    514       if (SpvOpTypeStruct == arrayInst->opcode() &&
    515           SPV_SUCCESS != (recursive_status = checkLayout(
    516                               typeId, storage_class_str, decoration_str,
    517                               blockRules, constraints, vstate)))
    518         return recursive_status;
    519       // Check array stride.
    520       for (auto& decoration : vstate.id_decorations(id)) {
    521         if (SpvDecorationArrayStride == decoration.dec_type() &&
    522             !IsAlignedTo(decoration.params()[0], alignment))
    523           return fail(memberIdx)
    524                  << "is an array with stride " << decoration.params()[0]
    525                  << " not satisfying alignment to " << alignment;
    526       }
    527     }
    528     nextValidOffset = offset + size;
    529     if (!scalar_block_layout && blockRules &&
    530         (SpvOpTypeArray == opcode || SpvOpTypeStruct == opcode)) {
    531       // Uniform block rules don't permit anything in the padding of a struct
    532       // or array.
    533       nextValidOffset = align(nextValidOffset, alignment);
    534     }
    535   }
    536   return SPV_SUCCESS;
    537 }
    538 
    539 // Returns true if variable or structure id has given decoration. Handles also
    540 // nested structures.
    541 bool hasDecoration(uint32_t id, SpvDecoration decoration,
    542                    ValidationState_t& vstate) {
    543   for (auto& dec : vstate.id_decorations(id)) {
    544     if (decoration == dec.dec_type()) return true;
    545   }
    546   if (SpvOpTypeStruct != vstate.FindDef(id)->opcode()) {
    547     return false;
    548   }
    549   for (auto member_id : getStructMembers(id, SpvOpTypeStruct, vstate)) {
    550     if (hasDecoration(member_id, decoration, vstate)) {
    551       return true;
    552     }
    553   }
    554   return false;
    555 }
    556 
    557 // Returns true if all ids of given type have a specified decoration.
    558 bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration,
    559                                 SpvOp type, ValidationState_t& vstate) {
    560   const auto& members = getStructMembers(struct_id, vstate);
    561   for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
    562     const auto id = members[memberIdx];
    563     if (type != vstate.FindDef(id)->opcode()) continue;
    564     bool found = false;
    565     for (auto& dec : vstate.id_decorations(id)) {
    566       if (decoration == dec.dec_type()) found = true;
    567     }
    568     for (auto& dec : vstate.id_decorations(struct_id)) {
    569       if (decoration == dec.dec_type() &&
    570           (int)memberIdx == dec.struct_member_index()) {
    571         found = true;
    572       }
    573     }
    574     if (!found) {
    575       return false;
    576     }
    577   }
    578   for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
    579     if (!checkForRequiredDecoration(id, decoration, type, vstate)) {
    580       return false;
    581     }
    582   }
    583   return true;
    584 }
    585 
    586 spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) {
    587   for (const auto& function : vstate.functions()) {
    588     if (function.block_count() == 0u) {
    589       // A function declaration (an OpFunction with no basic blocks), must have
    590       // a Linkage Attributes Decoration with the Import Linkage Type.
    591       if (!hasImportLinkageAttribute(function.id(), vstate)) {
    592         return vstate.diag(SPV_ERROR_INVALID_BINARY,
    593                            vstate.FindDef(function.id()))
    594                << "Function declaration (id " << function.id()
    595                << ") must have a LinkageAttributes decoration with the Import "
    596                   "Linkage type.";
    597       }
    598     } else {
    599       if (hasImportLinkageAttribute(function.id(), vstate)) {
    600         return vstate.diag(SPV_ERROR_INVALID_BINARY,
    601                            vstate.FindDef(function.id()))
    602                << "Function definition (id " << function.id()
    603                << ") may not be decorated with Import Linkage type.";
    604       }
    605     }
    606   }
    607   return SPV_SUCCESS;
    608 }
    609 
    610 // Checks whether an imported variable is initialized by this module.
    611 spv_result_t CheckImportedVariableInitialization(ValidationState_t& vstate) {
    612   // According the SPIR-V Spec 2.16.1, it is illegal to initialize an imported
    613   // variable. This means that a module-scope OpVariable with initialization
    614   // value cannot be marked with the Import Linkage Type (import type id = 1).
    615   for (auto global_var_id : vstate.global_vars()) {
    616     // Initializer <id> is an optional argument for OpVariable. If initializer
    617     // <id> is present, the instruction will have 5 words.
    618     auto variable_instr = vstate.FindDef(global_var_id);
    619     if (variable_instr->words().size() == 5u &&
    620         hasImportLinkageAttribute(global_var_id, vstate)) {
    621       return vstate.diag(SPV_ERROR_INVALID_ID, variable_instr)
    622              << "A module-scope OpVariable with initialization value "
    623                 "cannot be marked with the Import Linkage Type.";
    624     }
    625   }
    626   return SPV_SUCCESS;
    627 }
    628 
    629 // Checks whether a builtin variable is valid.
    630 spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) {
    631   const auto& decorations = vstate.id_decorations(var_id);
    632   for (const auto& d : decorations) {
    633     if (spvIsVulkanEnv(vstate.context()->target_env)) {
    634       if (d.dec_type() == SpvDecorationLocation ||
    635           d.dec_type() == SpvDecorationComponent) {
    636         return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
    637                << "A BuiltIn variable (id " << var_id
    638                << ") cannot have any Location or Component decorations";
    639       }
    640     }
    641   }
    642   return SPV_SUCCESS;
    643 }
    644 
    645 // Checks whether proper decorations have been appied to the entry points.
    646 spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
    647   for (uint32_t entry_point : vstate.entry_points()) {
    648     const auto& descs = vstate.entry_point_descriptions(entry_point);
    649     int num_builtin_inputs = 0;
    650     int num_builtin_outputs = 0;
    651     for (const auto& desc : descs) {
    652       for (auto interface : desc.interfaces) {
    653         Instruction* var_instr = vstate.FindDef(interface);
    654         if (!var_instr || SpvOpVariable != var_instr->opcode()) {
    655           return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
    656                  << "Interfaces passed to OpEntryPoint must be of type "
    657                     "OpTypeVariable. Found Op"
    658                  << spvOpcodeString(var_instr->opcode()) << ".";
    659         }
    660         const SpvStorageClass storage_class =
    661             var_instr->GetOperandAs<SpvStorageClass>(2);
    662         if (storage_class != SpvStorageClassInput &&
    663             storage_class != SpvStorageClassOutput) {
    664           return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
    665                  << "OpEntryPoint interfaces must be OpVariables with "
    666                     "Storage Class of Input(1) or Output(3). Found Storage "
    667                     "Class "
    668                  << storage_class << " for Entry Point id " << entry_point
    669                  << ".";
    670         }
    671 
    672         const uint32_t ptr_id = var_instr->word(1);
    673         Instruction* ptr_instr = vstate.FindDef(ptr_id);
    674         // It is guaranteed (by validator ID checks) that ptr_instr is
    675         // OpTypePointer. Word 3 of this instruction is the type being pointed
    676         // to.
    677         const uint32_t type_id = ptr_instr->word(3);
    678         Instruction* type_instr = vstate.FindDef(type_id);
    679         if (type_instr && SpvOpTypeStruct == type_instr->opcode() &&
    680             isBuiltInStruct(type_id, vstate)) {
    681           if (storage_class == SpvStorageClassInput) ++num_builtin_inputs;
    682           if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs;
    683           if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break;
    684           if (auto error = CheckBuiltInVariable(interface, vstate))
    685             return error;
    686         } else if (isBuiltInVar(interface, vstate)) {
    687           if (auto error = CheckBuiltInVariable(interface, vstate))
    688             return error;
    689         }
    690       }
    691       if (num_builtin_inputs > 1 || num_builtin_outputs > 1) {
    692         return vstate.diag(SPV_ERROR_INVALID_BINARY,
    693                            vstate.FindDef(entry_point))
    694                << "There must be at most one object per Storage Class that can "
    695                   "contain a structure type containing members decorated with "
    696                   "BuiltIn, consumed per entry-point. Entry Point id "
    697                << entry_point << " does not meet this requirement.";
    698       }
    699       // The LinkageAttributes Decoration cannot be applied to functions
    700       // targeted by an OpEntryPoint instruction
    701       for (auto& decoration : vstate.id_decorations(entry_point)) {
    702         if (SpvDecorationLinkageAttributes == decoration.dec_type()) {
    703           const char* linkage_name =
    704               reinterpret_cast<const char*>(&decoration.params()[0]);
    705           return vstate.diag(SPV_ERROR_INVALID_BINARY,
    706                              vstate.FindDef(entry_point))
    707                  << "The LinkageAttributes Decoration (Linkage name: "
    708                  << linkage_name << ") cannot be applied to function id "
    709                  << entry_point
    710                  << " because it is targeted by an OpEntryPoint instruction.";
    711         }
    712       }
    713     }
    714   }
    715   return SPV_SUCCESS;
    716 }
    717 
    718 // Load |constraints| with all the member constraints for structs contained
    719 // within the given array type.
    720 void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
    721                                       uint32_t array_id,
    722                                       const LayoutConstraints& inherited,
    723                                       ValidationState_t& vstate);
    724 
    725 // Load |constraints| with all the member constraints for the given struct,
    726 // and all its contained structs.
    727 void ComputeMemberConstraintsForStruct(MemberConstraints* constraints,
    728                                        uint32_t struct_id,
    729                                        const LayoutConstraints& inherited,
    730                                        ValidationState_t& vstate) {
    731   assert(constraints);
    732   const auto& members = getStructMembers(struct_id, vstate);
    733   for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
    734        memberIdx < numMembers; memberIdx++) {
    735     LayoutConstraints& constraint =
    736         (*constraints)[std::make_pair(struct_id, memberIdx)];
    737     constraint = inherited;
    738     for (auto& decoration : vstate.id_decorations(struct_id)) {
    739       if (decoration.struct_member_index() == (int)memberIdx) {
    740         switch (decoration.dec_type()) {
    741           case SpvDecorationRowMajor:
    742             constraint.majorness = kRowMajor;
    743             break;
    744           case SpvDecorationColMajor:
    745             constraint.majorness = kColumnMajor;
    746             break;
    747           case SpvDecorationMatrixStride:
    748             constraint.matrix_stride = decoration.params()[0];
    749             break;
    750           default:
    751             break;
    752         }
    753       }
    754     }
    755 
    756     // Now recurse
    757     auto member_type_id = members[memberIdx];
    758     const auto member_type_inst = vstate.FindDef(member_type_id);
    759     const auto opcode = member_type_inst->opcode();
    760     switch (opcode) {
    761       case SpvOpTypeArray:
    762       case SpvOpTypeRuntimeArray:
    763         ComputeMemberConstraintsForArray(constraints, member_type_id, inherited,
    764                                          vstate);
    765         break;
    766       case SpvOpTypeStruct:
    767         ComputeMemberConstraintsForStruct(constraints, member_type_id,
    768                                           inherited, vstate);
    769         break;
    770       default:
    771         break;
    772     }
    773   }
    774 }
    775 
    776 void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
    777                                       uint32_t array_id,
    778                                       const LayoutConstraints& inherited,
    779                                       ValidationState_t& vstate) {
    780   assert(constraints);
    781   auto elem_type_id = vstate.FindDef(array_id)->words()[2];
    782   const auto elem_type_inst = vstate.FindDef(elem_type_id);
    783   const auto opcode = elem_type_inst->opcode();
    784   switch (opcode) {
    785     case SpvOpTypeArray:
    786     case SpvOpTypeRuntimeArray:
    787       ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited,
    788                                        vstate);
    789       break;
    790     case SpvOpTypeStruct:
    791       ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited,
    792                                         vstate);
    793       break;
    794     default:
    795       break;
    796   }
    797 }
    798 
    799 spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
    800   // Set of entry points that are known to use a push constant.
    801   std::unordered_set<uint32_t> uses_push_constant;
    802   for (const auto& inst : vstate.ordered_instructions()) {
    803     const auto& words = inst.words();
    804     if (SpvOpVariable == inst.opcode()) {
    805       const auto var_id = inst.id();
    806       // For storage class / decoration combinations, see Vulkan 14.5.4 "Offset
    807       // and Stride Assignment".
    808       const auto storageClass = words[3];
    809       const bool uniform = storageClass == SpvStorageClassUniform;
    810       const bool uniform_constant =
    811           storageClass == SpvStorageClassUniformConstant;
    812       const bool push_constant = storageClass == SpvStorageClassPushConstant;
    813       const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer;
    814 
    815       if (spvIsVulkanEnv(vstate.context()->target_env)) {
    816         // Vulkan 14.5.1: There must be no more than one PushConstant block
    817         // per entry point.
    818         if (push_constant) {
    819           auto entry_points = vstate.EntryPointReferences(var_id);
    820           for (auto ep_id : entry_points) {
    821             const bool already_used = !uses_push_constant.insert(ep_id).second;
    822             if (already_used) {
    823               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
    824                      << "Entry point id '" << ep_id
    825                      << "' uses more than one PushConstant interface.\n"
    826                      << "From Vulkan spec, section 14.5.1:\n"
    827                      << "There must be no more than one push constant block "
    828                      << "statically used per shader entry point.";
    829             }
    830           }
    831         }
    832         // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
    833         // UniformConstant which cannot be a struct.
    834         if (uniform_constant) {
    835           auto entry_points = vstate.EntryPointReferences(var_id);
    836           if (!entry_points.empty() &&
    837               !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
    838             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
    839                    << "UniformConstant id '" << var_id
    840                    << "' is missing DescriptorSet decoration.\n"
    841                    << "From Vulkan spec, section 14.5.2:\n"
    842                    << "These variables must have DescriptorSet and Binding "
    843                       "decorations specified";
    844           }
    845           if (!entry_points.empty() &&
    846               !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
    847             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
    848                    << "UniformConstant id '" << var_id
    849                    << "' is missing Binding decoration.\n"
    850                    << "From Vulkan spec, section 14.5.2:\n"
    851                    << "These variables must have DescriptorSet and Binding "
    852                       "decorations specified";
    853           }
    854         }
    855       }
    856 
    857       const bool phys_storage_buffer =
    858           storageClass == SpvStorageClassPhysicalStorageBufferEXT;
    859       if (uniform || push_constant || storage_buffer || phys_storage_buffer) {
    860         const auto ptrInst = vstate.FindDef(words[1]);
    861         assert(SpvOpTypePointer == ptrInst->opcode());
    862         const auto id = ptrInst->words()[3];
    863         if (SpvOpTypeStruct != vstate.FindDef(id)->opcode()) continue;
    864         MemberConstraints constraints;
    865         ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
    866                                           vstate);
    867         // Prepare for messages
    868         const char* sc_str =
    869             uniform ? "Uniform"
    870                     : (push_constant ? "PushConstant" : "StorageBuffer");
    871 
    872         if (spvIsVulkanEnv(vstate.context()->target_env)) {
    873           // Vulkan 14.5.1: Check Block decoration for PushConstant variables.
    874           if (push_constant && !hasDecoration(id, SpvDecorationBlock, vstate)) {
    875             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
    876                    << "PushConstant id '" << id
    877                    << "' is missing Block decoration.\n"
    878                    << "From Vulkan spec, section 14.5.1:\n"
    879                    << "Such variables must be identified with a Block "
    880                       "decoration";
    881           }
    882           // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
    883           // Uniform and StorageBuffer variables.
    884           if (uniform || storage_buffer) {
    885             auto entry_points = vstate.EntryPointReferences(var_id);
    886             if (!entry_points.empty() &&
    887                 !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
    888               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
    889                      << sc_str << " id '" << var_id
    890                      << "' is missing DescriptorSet decoration.\n"
    891                      << "From Vulkan spec, section 14.5.2:\n"
    892                      << "These variables must have DescriptorSet and Binding "
    893                         "decorations specified";
    894             }
    895             if (!entry_points.empty() &&
    896                 !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
    897               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
    898                      << sc_str << " id '" << var_id
    899                      << "' is missing Binding decoration.\n"
    900                      << "From Vulkan spec, section 14.5.2:\n"
    901                      << "These variables must have DescriptorSet and Binding "
    902                         "decorations specified";
    903             }
    904           }
    905         }
    906 
    907         for (const auto& dec : vstate.id_decorations(id)) {
    908           const bool blockDeco = SpvDecorationBlock == dec.dec_type();
    909           const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type();
    910           const bool blockRules = uniform && blockDeco;
    911           const bool bufferRules =
    912               (uniform && bufferDeco) || (push_constant && blockDeco) ||
    913               ((storage_buffer || phys_storage_buffer) && blockDeco);
    914           if (blockRules || bufferRules) {
    915             const char* deco_str = blockDeco ? "Block" : "BufferBlock";
    916             spv_result_t recursive_status = SPV_SUCCESS;
    917             if (isMissingOffsetInStruct(id, vstate)) {
    918               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
    919                      << "Structure id " << id << " decorated as " << deco_str
    920                      << " must be explicitly laid out with Offset "
    921                         "decorations.";
    922             } else if (hasDecoration(id, SpvDecorationGLSLShared, vstate)) {
    923               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
    924                      << "Structure id " << id << " decorated as " << deco_str
    925                      << " must not use GLSLShared decoration.";
    926             } else if (hasDecoration(id, SpvDecorationGLSLPacked, vstate)) {
    927               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
    928                      << "Structure id " << id << " decorated as " << deco_str
    929                      << " must not use GLSLPacked decoration.";
    930             } else if (!checkForRequiredDecoration(id, SpvDecorationArrayStride,
    931                                                    SpvOpTypeArray, vstate)) {
    932               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
    933                      << "Structure id " << id << " decorated as " << deco_str
    934                      << " must be explicitly laid out with ArrayStride "
    935                         "decorations.";
    936             } else if (!checkForRequiredDecoration(id,
    937                                                    SpvDecorationMatrixStride,
    938                                                    SpvOpTypeMatrix, vstate)) {
    939               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
    940                      << "Structure id " << id << " decorated as " << deco_str
    941                      << " must be explicitly laid out with MatrixStride "
    942                         "decorations.";
    943             } else if (blockRules &&
    944                        (SPV_SUCCESS != (recursive_status = checkLayout(
    945                                             id, sc_str, deco_str, true,
    946                                             constraints, vstate)))) {
    947               return recursive_status;
    948             } else if (bufferRules &&
    949                        (SPV_SUCCESS != (recursive_status = checkLayout(
    950                                             id, sc_str, deco_str, false,
    951                                             constraints, vstate)))) {
    952               return recursive_status;
    953             }
    954           }
    955         }
    956       }
    957     }
    958   }
    959   return SPV_SUCCESS;
    960 }
    961 
    962 spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
    963   using AtMostOnceSet = std::unordered_set<SpvDecoration, SpvDecorationHash>;
    964   using MutuallyExclusiveSets =
    965       std::vector<std::unordered_set<SpvDecoration, SpvDecorationHash>>;
    966   using PerIDKey = std::tuple<SpvDecoration, uint32_t>;
    967   using PerMemberKey = std::tuple<SpvDecoration, uint32_t, uint32_t>;
    968   using DecorationNameTable =
    969       std::unordered_map<SpvDecoration, std::string, SpvDecorationHash>;
    970 
    971   static const auto* const at_most_once_per_id = new AtMostOnceSet{
    972       SpvDecorationArrayStride,
    973   };
    974   static const auto* const at_most_once_per_member = new AtMostOnceSet{
    975       SpvDecorationOffset,
    976       SpvDecorationMatrixStride,
    977       SpvDecorationRowMajor,
    978       SpvDecorationColMajor,
    979   };
    980   static const auto* const mutually_exclusive_per_id =
    981       new MutuallyExclusiveSets{
    982           {SpvDecorationBlock, SpvDecorationBufferBlock},
    983       };
    984   static const auto* const mutually_exclusive_per_member =
    985       new MutuallyExclusiveSets{
    986           {SpvDecorationRowMajor, SpvDecorationColMajor},
    987       };
    988   // For printing the decoration name.
    989   static const auto* const decoration_name = new DecorationNameTable{
    990       {SpvDecorationArrayStride, "ArrayStride"},
    991       {SpvDecorationOffset, "Offset"},
    992       {SpvDecorationMatrixStride, "MatrixStride"},
    993       {SpvDecorationRowMajor, "RowMajor"},
    994       {SpvDecorationColMajor, "ColMajor"},
    995       {SpvDecorationBlock, "Block"},
    996       {SpvDecorationBufferBlock, "BufferBlock"},
    997   };
    998 
    999   std::set<PerIDKey> seen_per_id;
   1000   std::set<PerMemberKey> seen_per_member;
   1001 
   1002   for (const auto& inst : vstate.ordered_instructions()) {
   1003     const auto& words = inst.words();
   1004     if (SpvOpDecorate == inst.opcode()) {
   1005       const auto id = words[1];
   1006       const auto dec_type = static_cast<SpvDecoration>(words[2]);
   1007       const auto k = PerIDKey(dec_type, id);
   1008       const auto already_used = !seen_per_id.insert(k).second;
   1009       if (already_used &&
   1010           at_most_once_per_id->find(dec_type) != at_most_once_per_id->end()) {
   1011         return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
   1012                << "ID '" << id << "' decorated with "
   1013                << decoration_name->at(dec_type)
   1014                << " multiple times is not allowed.";
   1015       }
   1016       // Verify certain mutually exclusive decorations are not both applied on
   1017       // an ID.
   1018       for (const auto& s : *mutually_exclusive_per_id) {
   1019         if (s.find(dec_type) == s.end()) continue;
   1020         for (auto excl_dec_type : s) {
   1021           if (excl_dec_type == dec_type) continue;
   1022           const auto excl_k = PerIDKey(excl_dec_type, id);
   1023           if (seen_per_id.find(excl_k) != seen_per_id.end()) {
   1024             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
   1025                    << "ID '" << id << "' decorated with both "
   1026                    << decoration_name->at(dec_type) << " and "
   1027                    << decoration_name->at(excl_dec_type) << " is not allowed.";
   1028           }
   1029         }
   1030       }
   1031     } else if (SpvOpMemberDecorate == inst.opcode()) {
   1032       const auto id = words[1];
   1033       const auto member_id = words[2];
   1034       const auto dec_type = static_cast<SpvDecoration>(words[3]);
   1035       const auto k = PerMemberKey(dec_type, id, member_id);
   1036       const auto already_used = !seen_per_member.insert(k).second;
   1037       if (already_used && at_most_once_per_member->find(dec_type) !=
   1038                               at_most_once_per_member->end()) {
   1039         return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
   1040                << "ID '" << id << "', member '" << member_id
   1041                << "' decorated with " << decoration_name->at(dec_type)
   1042                << " multiple times is not allowed.";
   1043       }
   1044       // Verify certain mutually exclusive decorations are not both applied on
   1045       // a (ID, member) tuple.
   1046       for (const auto& s : *mutually_exclusive_per_member) {
   1047         if (s.find(dec_type) == s.end()) continue;
   1048         for (auto excl_dec_type : s) {
   1049           if (excl_dec_type == dec_type) continue;
   1050           const auto excl_k = PerMemberKey(excl_dec_type, id, member_id);
   1051           if (seen_per_member.find(excl_k) != seen_per_member.end()) {
   1052             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
   1053                    << "ID '" << id << "', member '" << member_id
   1054                    << "' decorated with both " << decoration_name->at(dec_type)
   1055                    << " and " << decoration_name->at(excl_dec_type)
   1056                    << " is not allowed.";
   1057           }
   1058         }
   1059       }
   1060     }
   1061   }
   1062   return SPV_SUCCESS;
   1063 }
   1064 
   1065 spv_result_t CheckVulkanMemoryModelDeprecatedDecorations(
   1066     ValidationState_t& vstate) {
   1067   if (vstate.memory_model() != SpvMemoryModelVulkanKHR) return SPV_SUCCESS;
   1068 
   1069   std::string msg;
   1070   std::ostringstream str(msg);
   1071   for (const auto& def : vstate.all_definitions()) {
   1072     const auto inst = def.second;
   1073     const auto id = inst->id();
   1074     for (const auto& dec : vstate.id_decorations(id)) {
   1075       const auto member = dec.struct_member_index();
   1076       if (dec.dec_type() == SpvDecorationCoherent ||
   1077           dec.dec_type() == SpvDecorationVolatile) {
   1078         str << (dec.dec_type() == SpvDecorationCoherent ? "Coherent"
   1079                                                         : "Volatile");
   1080         str << " decoration targeting " << vstate.getIdName(id);
   1081         if (member != Decoration::kInvalidMember) {
   1082           str << " (member index " << member << ")";
   1083         }
   1084         str << " is banned when using the Vulkan memory model.";
   1085         return vstate.diag(SPV_ERROR_INVALID_ID, inst) << str.str();
   1086       }
   1087     }
   1088   }
   1089   return SPV_SUCCESS;
   1090 }
   1091 
   1092 // Returns SPV_SUCCESS if validation rules are satisfied for FPRoundingMode
   1093 // decorations.  Otherwise emits a diagnostic and returns something other than
   1094 // SPV_SUCCESS.
   1095 spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate,
   1096                                            const Instruction& inst) {
   1097   // Validates width-only conversion instruction for floating-point object
   1098   // i.e., OpFConvert
   1099   if (inst.opcode() != SpvOpFConvert) {
   1100     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1101            << "FPRoundingMode decoration can be applied only to a "
   1102               "width-only conversion instruction for floating-point "
   1103               "object.";
   1104   }
   1105 
   1106   // Validates Object operand of an OpStore
   1107   for (const auto& use : inst.uses()) {
   1108     const auto store = use.first;
   1109     if (store->opcode() != SpvOpStore) {
   1110       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1111              << "FPRoundingMode decoration can be applied only to the "
   1112                 "Object operand of an OpStore.";
   1113     }
   1114 
   1115     if (use.second != 2) {
   1116       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1117              << "FPRoundingMode decoration can be applied only to the "
   1118                 "Object operand of an OpStore.";
   1119     }
   1120 
   1121     const auto ptr_inst = vstate.FindDef(store->GetOperandAs<uint32_t>(0));
   1122     const auto ptr_type = vstate.FindDef(ptr_inst->GetOperandAs<uint32_t>(0));
   1123 
   1124     const auto half_float_id = ptr_type->GetOperandAs<uint32_t>(2);
   1125     if (!vstate.IsFloatScalarOrVectorType(half_float_id) ||
   1126         vstate.GetBitWidth(half_float_id) != 16) {
   1127       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1128              << "FPRoundingMode decoration can be applied only to the "
   1129                 "Object operand of an OpStore storing through a pointer "
   1130                 "to "
   1131                 "a 16-bit floating-point scalar or vector object.";
   1132     }
   1133 
   1134     // Validates storage class of the pointer to the OpStore
   1135     const auto storage = ptr_type->GetOperandAs<uint32_t>(1);
   1136     if (storage != SpvStorageClassStorageBuffer &&
   1137         storage != SpvStorageClassUniform &&
   1138         storage != SpvStorageClassPushConstant &&
   1139         storage != SpvStorageClassInput && storage != SpvStorageClassOutput &&
   1140         storage != SpvStorageClassPhysicalStorageBufferEXT) {
   1141       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1142              << "FPRoundingMode decoration can be applied only to the "
   1143                 "Object operand of an OpStore in the StorageBuffer, "
   1144                 "PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or "
   1145                 "Output Storage Classes.";
   1146     }
   1147   }
   1148   return SPV_SUCCESS;
   1149 }
   1150 
   1151 // Returns SPV_SUCCESS if validation rules are satisfied for Uniform
   1152 // decorations. Otherwise emits a diagnostic and returns something other than
   1153 // SPV_SUCCESS. Assumes each decoration on a group has been propagated down to
   1154 // the group members.
   1155 spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
   1156                                     const Instruction& inst,
   1157                                     const Decoration&) {
   1158   // Uniform must decorate an "object"
   1159   //  - has a result ID
   1160   //  - is an instantiation of a non-void type.  So it has a type ID, and that
   1161   //  type is not void.
   1162 
   1163   // We already know the result ID is non-zero.
   1164 
   1165   if (inst.type_id() == 0) {
   1166     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1167            << "Uniform decoration applied to a non-object";
   1168   }
   1169   if (Instruction* type_inst = vstate.FindDef(inst.type_id())) {
   1170     if (type_inst->opcode() == SpvOpTypeVoid) {
   1171       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1172              << "Uniform decoration applied to a value with void type";
   1173     }
   1174   } else {
   1175     // We might never get here because this would have been rejected earlier in
   1176     // the flow.
   1177     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1178            << "Uniform decoration applied to an object with invalid type";
   1179   }
   1180   return SPV_SUCCESS;
   1181 }
   1182 
   1183 // Returns SPV_SUCCESS if validation rules are satisfied for NoSignedWrap or
   1184 // NoUnsignedWrap decorations. Otherwise emits a diagnostic and returns
   1185 // something other than SPV_SUCCESS. Assumes each decoration on a group has been
   1186 // propagated down to the group members.
   1187 spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate,
   1188                                         const Instruction& inst,
   1189                                         const Decoration& decoration) {
   1190   switch (inst.opcode()) {
   1191     case SpvOpIAdd:
   1192     case SpvOpISub:
   1193     case SpvOpIMul:
   1194     case SpvOpShiftLeftLogical:
   1195     case SpvOpSNegate:
   1196       return SPV_SUCCESS;
   1197     case SpvOpExtInst:
   1198       // TODO(dneto): Only certain extended instructions allow these
   1199       // decorations.  For now allow anything.
   1200       return SPV_SUCCESS;
   1201     default:
   1202       break;
   1203   }
   1204 
   1205   return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
   1206          << (decoration.dec_type() == SpvDecorationNoSignedWrap
   1207                  ? "NoSignedWrap"
   1208                  : "NoUnsignedWrap")
   1209          << " decoration may not be applied to "
   1210          << spvOpcodeString(inst.opcode());
   1211 }
   1212 
   1213 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
   1214   {                                             \
   1215     spv_result_t e##LINE = (X);                 \
   1216     if (e##LINE != SPV_SUCCESS) return e##LINE; \
   1217   }
   1218 #define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__)
   1219 
   1220 // Check rules for decorations where we start from the decoration rather
   1221 // than the decorated object.  Assumes each decoration on a group have been
   1222 // propagated down to the group members.
   1223 spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
   1224   // Some rules are only checked for shaders.
   1225   const bool is_shader = vstate.HasCapability(SpvCapabilityShader);
   1226 
   1227   for (const auto& kv : vstate.id_decorations()) {
   1228     const uint32_t id = kv.first;
   1229     const auto& decorations = kv.second;
   1230     if (decorations.empty()) continue;
   1231 
   1232     const Instruction* inst = vstate.FindDef(id);
   1233     assert(inst);
   1234 
   1235     // We assume the decorations applied to a decoration group have already
   1236     // been propagated down to the group members.
   1237     if (inst->opcode() == SpvOpDecorationGroup) continue;
   1238 
   1239     // Validates FPRoundingMode decoration
   1240     for (const auto& decoration : decorations) {
   1241       switch (decoration.dec_type()) {
   1242         case SpvDecorationFPRoundingMode:
   1243           if (is_shader)
   1244             PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst));
   1245           break;
   1246         case SpvDecorationUniform:
   1247           PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration));
   1248           break;
   1249         case SpvDecorationNoSignedWrap:
   1250         case SpvDecorationNoUnsignedWrap:
   1251           PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration));
   1252           break;
   1253         default:
   1254           break;
   1255       }
   1256     }
   1257   }
   1258   return SPV_SUCCESS;
   1259 }
   1260 
   1261 }  // namespace
   1262 
   1263 spv_result_t ValidateDecorations(ValidationState_t& vstate) {
   1264   if (auto error = CheckImportedVariableInitialization(vstate)) return error;
   1265   if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error;
   1266   if (auto error = CheckDecorationsOfBuffers(vstate)) return error;
   1267   if (auto error = CheckDecorationsCompatibility(vstate)) return error;
   1268   if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error;
   1269   if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
   1270     return error;
   1271   if (auto error = CheckDecorationsFromDecoration(vstate)) return error;
   1272   return SPV_SUCCESS;
   1273 }
   1274 
   1275 }  // namespace val
   1276 }  // namespace spvtools
   1277