• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <algorithm>
16 #include <cassert>
17 #include <string>
18 #include <tuple>
19 #include <unordered_map>
20 #include <unordered_set>
21 #include <utility>
22 #include <vector>
23 
24 #include "source/diagnostic.h"
25 #include "source/opcode.h"
26 #include "source/spirv_constant.h"
27 #include "source/spirv_target_env.h"
28 #include "source/spirv_validator_options.h"
29 #include "source/util/string_utils.h"
30 #include "source/val/validate_scopes.h"
31 #include "source/val/validation_state.h"
32 
33 namespace spvtools {
34 namespace val {
35 namespace {
36 
37 // Distinguish between row and column major matrix layouts.
38 enum MatrixLayout { kRowMajor, kColumnMajor };
39 
40 // A functor for hashing a pair of integers.
41 struct PairHash {
operator ()spvtools::val::__anonee323c130111::PairHash42   std::size_t operator()(const std::pair<uint32_t, uint32_t> pair) const {
43     const uint32_t a = pair.first;
44     const uint32_t b = pair.second;
45     const uint32_t rotated_b = (b >> 2) | ((b & 3) << 30);
46     return a ^ rotated_b;
47   }
48 };
49 
50 // Struct member layout attributes that are inherited through arrays.
51 struct LayoutConstraints {
LayoutConstraintsspvtools::val::__anonee323c130111::LayoutConstraints52   explicit LayoutConstraints(
53       MatrixLayout the_majorness = MatrixLayout::kColumnMajor,
54       uint32_t stride = 0)
55       : majorness(the_majorness), matrix_stride(stride) {}
56   MatrixLayout majorness;
57   uint32_t matrix_stride;
58 };
59 
60 // A type for mapping (struct id, member id) to layout constraints.
61 using MemberConstraints = std::unordered_map<std::pair<uint32_t, uint32_t>,
62                                              LayoutConstraints, PairHash>;
63 
64 // Returns the array stride of the given array type.
GetArrayStride(uint32_t array_id,ValidationState_t & vstate)65 uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) {
66   for (auto& decoration : vstate.id_decorations(array_id)) {
67     if (spv::Decoration::ArrayStride == decoration.dec_type()) {
68       return decoration.params()[0];
69     }
70   }
71   return 0;
72 }
73 
74 // Returns true if the given structure type has a Block decoration.
isBlock(uint32_t struct_id,ValidationState_t & vstate)75 bool isBlock(uint32_t struct_id, ValidationState_t& vstate) {
76   const auto& decorations = vstate.id_decorations(struct_id);
77   return std::any_of(decorations.begin(), decorations.end(),
78                      [](const Decoration& d) {
79                        return spv::Decoration::Block == d.dec_type();
80                      });
81 }
82 
83 // Returns true if the given ID has the Import LinkageAttributes decoration.
hasImportLinkageAttribute(uint32_t id,ValidationState_t & vstate)84 bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) {
85   const auto& decorations = vstate.id_decorations(id);
86   return std::any_of(
87       decorations.begin(), decorations.end(), [](const Decoration& d) {
88         return spv::Decoration::LinkageAttributes == d.dec_type() &&
89                d.params().size() >= 2u &&
90                spv::LinkageType(d.params().back()) == spv::LinkageType::Import;
91       });
92 }
93 
94 // Returns a vector of all members of a structure.
getStructMembers(uint32_t struct_id,ValidationState_t & vstate)95 std::vector<uint32_t> getStructMembers(uint32_t struct_id,
96                                        ValidationState_t& vstate) {
97   const auto inst = vstate.FindDef(struct_id);
98   return std::vector<uint32_t>(inst->words().begin() + 2, inst->words().end());
99 }
100 
101 // Returns a vector of all members of a structure that have specific type.
getStructMembers(uint32_t struct_id,spv::Op type,ValidationState_t & vstate)102 std::vector<uint32_t> getStructMembers(uint32_t struct_id, spv::Op type,
103                                        ValidationState_t& vstate) {
104   std::vector<uint32_t> members;
105   for (auto id : getStructMembers(struct_id, vstate)) {
106     if (type == vstate.FindDef(id)->opcode()) {
107       members.push_back(id);
108     }
109   }
110   return members;
111 }
112 
113 // Returns whether the given structure is missing Offset decoration for any
114 // member. Handles also nested structures.
isMissingOffsetInStruct(uint32_t struct_id,ValidationState_t & vstate)115 bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
116   const auto* inst = vstate.FindDef(struct_id);
117   std::vector<bool> hasOffset;
118   std::vector<uint32_t> struct_members;
119   if (inst->opcode() == spv::Op::OpTypeStruct) {
120     // Check offsets of member decorations.
121     struct_members = getStructMembers(struct_id, vstate);
122     hasOffset.resize(struct_members.size(), false);
123 
124     for (auto& decoration : vstate.id_decorations(struct_id)) {
125       if (spv::Decoration::Offset == decoration.dec_type() &&
126           Decoration::kInvalidMember != decoration.struct_member_index()) {
127         // Offset 0xffffffff is not valid so ignore it for simplicity's sake.
128         if (decoration.params()[0] == 0xffffffff) return true;
129         hasOffset[decoration.struct_member_index()] = true;
130       }
131     }
132   } else if (inst->opcode() == spv::Op::OpTypeArray ||
133              inst->opcode() == spv::Op::OpTypeRuntimeArray) {
134     hasOffset.resize(1, true);
135     struct_members.push_back(inst->GetOperandAs<uint32_t>(1u));
136   }
137   // Look through nested structs (which may be in an array).
138   bool nestedStructsMissingOffset = false;
139   for (auto id : struct_members) {
140     if (isMissingOffsetInStruct(id, vstate)) {
141       nestedStructsMissingOffset = true;
142       break;
143     }
144   }
145   return nestedStructsMissingOffset ||
146          !std::all_of(hasOffset.begin(), hasOffset.end(),
147                       [](const bool b) { return b; });
148 }
149 
150 // Rounds x up to the next alignment. Assumes alignment is a power of two.
align(uint32_t x,uint32_t alignment)151 uint32_t align(uint32_t x, uint32_t alignment) {
152   return (x + alignment - 1) & ~(alignment - 1);
153 }
154 
155 // Returns base alignment of struct member. If |roundUp| is true, also
156 // ensure that structs, arrays, and matrices are aligned at least to a
157 // multiple of 16 bytes.  (That is, when roundUp is true, this function
158 // returns the *extended* alignment as it's called by the Vulkan spec.)
getBaseAlignment(uint32_t member_id,bool roundUp,const LayoutConstraints & inherited,MemberConstraints & constraints,ValidationState_t & vstate)159 uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
160                           const LayoutConstraints& inherited,
161                           MemberConstraints& constraints,
162                           ValidationState_t& vstate) {
163   const auto inst = vstate.FindDef(member_id);
164   const auto& words = inst->words();
165   // Minimal alignment is byte-aligned.
166   uint32_t baseAlignment = 1;
167   switch (inst->opcode()) {
168     case spv::Op::OpTypeSampledImage:
169     case spv::Op::OpTypeSampler:
170     case spv::Op::OpTypeImage:
171       if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
172         return baseAlignment = vstate.samplerimage_variable_address_mode() / 8;
173       assert(0);
174       return 0;
175     case spv::Op::OpTypeInt:
176     case spv::Op::OpTypeFloat:
177       baseAlignment = words[2] / 8;
178       break;
179     case spv::Op::OpTypeVector: {
180       const auto componentId = words[2];
181       const auto numComponents = words[3];
182       const auto componentAlignment = getBaseAlignment(
183           componentId, roundUp, inherited, constraints, vstate);
184       baseAlignment =
185           componentAlignment * (numComponents == 3 ? 4 : numComponents);
186       break;
187     }
188     case spv::Op::OpTypeMatrix: {
189       const auto column_type = words[2];
190       if (inherited.majorness == kColumnMajor) {
191         baseAlignment = getBaseAlignment(column_type, roundUp, inherited,
192                                          constraints, vstate);
193       } else {
194         // A row-major matrix of C columns has a base alignment equal to the
195         // base alignment of a vector of C matrix components.
196         const auto num_columns = words[3];
197         const auto component_inst = vstate.FindDef(column_type);
198         const auto component_id = component_inst->words()[2];
199         const auto componentAlignment = getBaseAlignment(
200             component_id, roundUp, inherited, constraints, vstate);
201         baseAlignment =
202             componentAlignment * (num_columns == 3 ? 4 : num_columns);
203       }
204       if (roundUp) baseAlignment = align(baseAlignment, 16u);
205     } break;
206     case spv::Op::OpTypeArray:
207     case spv::Op::OpTypeRuntimeArray:
208       baseAlignment =
209           getBaseAlignment(words[2], roundUp, inherited, constraints, vstate);
210       if (roundUp) baseAlignment = align(baseAlignment, 16u);
211       break;
212     case spv::Op::OpTypeStruct: {
213       const auto members = getStructMembers(member_id, vstate);
214       for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
215            memberIdx < numMembers; ++memberIdx) {
216         const auto id = members[memberIdx];
217         const auto& constraint =
218             constraints[std::make_pair(member_id, memberIdx)];
219         baseAlignment = std::max(
220             baseAlignment,
221             getBaseAlignment(id, roundUp, constraint, constraints, vstate));
222       }
223       if (roundUp) baseAlignment = align(baseAlignment, 16u);
224       break;
225     }
226     case spv::Op::OpTypePointer:
227       baseAlignment = vstate.pointer_size_and_alignment();
228       break;
229     default:
230       assert(0);
231       break;
232   }
233 
234   return baseAlignment;
235 }
236 
237 // Returns scalar alignment of a type.
getScalarAlignment(uint32_t type_id,ValidationState_t & vstate)238 uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
239   const auto inst = vstate.FindDef(type_id);
240   const auto& words = inst->words();
241   switch (inst->opcode()) {
242     case spv::Op::OpTypeSampledImage:
243     case spv::Op::OpTypeSampler:
244     case spv::Op::OpTypeImage:
245       if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
246         return vstate.samplerimage_variable_address_mode() / 8;
247       assert(0);
248       return 0;
249     case spv::Op::OpTypeInt:
250     case spv::Op::OpTypeFloat:
251       return words[2] / 8;
252     case spv::Op::OpTypeVector:
253     case spv::Op::OpTypeMatrix:
254     case spv::Op::OpTypeArray:
255     case spv::Op::OpTypeRuntimeArray: {
256       const auto compositeMemberTypeId = words[2];
257       return getScalarAlignment(compositeMemberTypeId, vstate);
258     }
259     case spv::Op::OpTypeStruct: {
260       const auto members = getStructMembers(type_id, vstate);
261       uint32_t max_member_alignment = 1;
262       for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
263            memberIdx < numMembers; ++memberIdx) {
264         const auto id = members[memberIdx];
265         uint32_t member_alignment = getScalarAlignment(id, vstate);
266         if (member_alignment > max_member_alignment) {
267           max_member_alignment = member_alignment;
268         }
269       }
270       return max_member_alignment;
271     } break;
272     case spv::Op::OpTypePointer:
273       return vstate.pointer_size_and_alignment();
274     default:
275       assert(0);
276       break;
277   }
278 
279   return 1;
280 }
281 
282 // Returns size of a struct member. Doesn't include padding at the end of struct
283 // or array.  Assumes that in the struct case, all members have offsets.
getSize(uint32_t member_id,const LayoutConstraints & inherited,MemberConstraints & constraints,ValidationState_t & vstate)284 uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
285                  MemberConstraints& constraints, ValidationState_t& vstate) {
286   const auto inst = vstate.FindDef(member_id);
287   const auto& words = inst->words();
288   switch (inst->opcode()) {
289     case spv::Op::OpTypeSampledImage:
290     case spv::Op::OpTypeSampler:
291     case spv::Op::OpTypeImage:
292       if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
293         return vstate.samplerimage_variable_address_mode() / 8;
294       assert(0);
295       return 0;
296     case spv::Op::OpTypeInt:
297     case spv::Op::OpTypeFloat:
298       return words[2] / 8;
299     case spv::Op::OpTypeVector: {
300       const auto componentId = words[2];
301       const auto numComponents = words[3];
302       const auto componentSize =
303           getSize(componentId, inherited, constraints, vstate);
304       const auto size = componentSize * numComponents;
305       return size;
306     }
307     case spv::Op::OpTypeArray: {
308       const auto sizeInst = vstate.FindDef(words[3]);
309       if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0;
310       assert(spv::Op::OpConstant == sizeInst->opcode());
311       const uint32_t num_elem = sizeInst->words()[3];
312       const uint32_t elem_type = words[2];
313       const uint32_t elem_size =
314           getSize(elem_type, inherited, constraints, vstate);
315       // Account for gaps due to alignments in the first N-1 elements,
316       // then add the size of the last element.
317       const auto size =
318           (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size;
319       return size;
320     }
321     case spv::Op::OpTypeRuntimeArray:
322       return 0;
323     case spv::Op::OpTypeMatrix: {
324       const auto num_columns = words[3];
325       if (inherited.majorness == kColumnMajor) {
326         return num_columns * inherited.matrix_stride;
327       } else {
328         // Row major case.
329         const auto column_type = words[2];
330         const auto component_inst = vstate.FindDef(column_type);
331         const auto num_rows = component_inst->words()[3];
332         const auto scalar_elem_type = component_inst->words()[2];
333         const uint32_t scalar_elem_size =
334             getSize(scalar_elem_type, inherited, constraints, vstate);
335         return (num_rows - 1) * inherited.matrix_stride +
336                num_columns * scalar_elem_size;
337       }
338     }
339     case spv::Op::OpTypeStruct: {
340       const auto& members = getStructMembers(member_id, vstate);
341       if (members.empty()) return 0;
342       const auto lastIdx = uint32_t(members.size() - 1);
343       const auto& lastMember = members.back();
344       uint32_t offset = 0xffffffff;
345       // Find the offset of the last element and add the size.
346       auto member_decorations =
347           vstate.id_member_decorations(member_id, lastIdx);
348       for (auto decoration = member_decorations.begin;
349            decoration != member_decorations.end; ++decoration) {
350         assert(decoration->struct_member_index() == (int)lastIdx);
351         if (spv::Decoration::Offset == decoration->dec_type()) {
352           offset = decoration->params()[0];
353         }
354       }
355       // This check depends on the fact that all members have offsets.  This
356       // has been checked earlier in the flow.
357       assert(offset != 0xffffffff);
358       const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
359       return offset + getSize(lastMember, constraint, constraints, vstate);
360     }
361     case spv::Op::OpTypePointer:
362       return vstate.pointer_size_and_alignment();
363     default:
364       assert(0);
365       return 0;
366   }
367 }
368 
369 // A member is defined to improperly straddle if either of the following are
370 // true:
371 // - It is a vector with total size less than or equal to 16 bytes, and has
372 // Offset decorations placing its first byte at F and its last byte at L, where
373 // floor(F / 16) != floor(L / 16).
374 // - It is a vector with total size greater than 16 bytes and has its Offset
375 // decorations placing its first byte at a non-integer multiple of 16.
hasImproperStraddle(uint32_t id,uint32_t offset,const LayoutConstraints & inherited,MemberConstraints & constraints,ValidationState_t & vstate)376 bool hasImproperStraddle(uint32_t id, uint32_t offset,
377                          const LayoutConstraints& inherited,
378                          MemberConstraints& constraints,
379                          ValidationState_t& vstate) {
380   const auto size = getSize(id, inherited, constraints, vstate);
381   const auto F = offset;
382   const auto L = offset + size - 1;
383   if (size <= 16) {
384     if ((F >> 4) != (L >> 4)) return true;
385   } else {
386     if (F % 16 != 0) return true;
387   }
388   return false;
389 }
390 
391 // Returns true if |offset| satsifies an alignment to |alignment|.  In the case
392 // of |alignment| of zero, the |offset| must also be zero.
IsAlignedTo(uint32_t offset,uint32_t alignment)393 bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
394   if (alignment == 0) return offset == 0;
395   return 0 == (offset % alignment);
396 }
397 
398 // Returns SPV_SUCCESS if the given struct satisfies standard layout rules for
399 // Block or BufferBlocks in Vulkan.  Otherwise emits a diagnostic and returns
400 // something other than SPV_SUCCESS.  Matrices inherit the specified column
401 // or row major-ness.
checkLayout(uint32_t struct_id,const char * storage_class_str,const char * decoration_str,bool blockRules,bool scalar_block_layout,uint32_t incoming_offset,MemberConstraints & constraints,ValidationState_t & vstate)402 spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
403                          const char* decoration_str, bool blockRules,
404                          bool scalar_block_layout,
405                          uint32_t incoming_offset,
406                          MemberConstraints& constraints,
407                          ValidationState_t& vstate) {
408   if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
409 
410   // blockRules are the same as bufferBlock rules if the uniform buffer
411   // standard layout extension is being used.
412   if (vstate.options()->uniform_buffer_standard_layout) blockRules = false;
413 
414   // Relaxed layout and scalar layout can both be in effect at the same time.
415   // For example, relaxed layout is implied by Vulkan 1.1.  But scalar layout
416   // is more permissive than relaxed layout.
417   const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
418 
419   auto fail = [&vstate, struct_id, storage_class_str, decoration_str,
420                blockRules, relaxed_block_layout,
421                scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
422     DiagnosticStream ds =
423         std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
424                   << "Structure id " << struct_id << " decorated as "
425                   << decoration_str << " for variable in " << storage_class_str
426                   << " storage class must follow "
427                   << (scalar_block_layout
428                           ? "scalar "
429                           : (relaxed_block_layout ? "relaxed " : "standard "))
430                   << (blockRules ? "uniform buffer" : "storage buffer")
431                   << " layout rules: member " << member_idx << " ");
432     return ds;
433   };
434 
435   // If we are checking physical storage buffer pointers, we may not actually
436   // have a struct here. Instead, pretend we have a struct with a single member
437   // at offset 0.
438   const auto& struct_type = vstate.FindDef(struct_id);
439   std::vector<uint32_t> members;
440   if (struct_type->opcode() == spv::Op::OpTypeStruct) {
441     members = getStructMembers(struct_id, vstate);
442   } else {
443     members.push_back(struct_id);
444   }
445 
446   // To check for member overlaps, we want to traverse the members in
447   // offset order.
448   struct MemberOffsetPair {
449     uint32_t member;
450     uint32_t offset;
451   };
452   std::vector<MemberOffsetPair> member_offsets;
453 
454   // With physical storage buffers, we might be checking layouts that do not
455   // originate from a structure.
456   if (struct_type->opcode() == spv::Op::OpTypeStruct) {
457     member_offsets.reserve(members.size());
458     for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
459          memberIdx < numMembers; memberIdx++) {
460       uint32_t offset = 0xffffffff;
461       auto member_decorations =
462           vstate.id_member_decorations(struct_id, memberIdx);
463       for (auto decoration = member_decorations.begin;
464            decoration != member_decorations.end; ++decoration) {
465         assert(decoration->struct_member_index() == (int)memberIdx);
466         switch (decoration->dec_type()) {
467           case spv::Decoration::Offset:
468             offset = decoration->params()[0];
469             break;
470           default:
471             break;
472         }
473       }
474       member_offsets.push_back(
475           MemberOffsetPair{memberIdx, incoming_offset + offset});
476     }
477     std::stable_sort(
478         member_offsets.begin(), member_offsets.end(),
479         [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) {
480           return lhs.offset < rhs.offset;
481         });
482   } else {
483     member_offsets.push_back({0, 0});
484   }
485 
486   // Now scan from lowest offset to highest offset.
487   uint32_t nextValidOffset = 0;
488   for (size_t ordered_member_idx = 0;
489        ordered_member_idx < member_offsets.size(); ordered_member_idx++) {
490     const auto& member_offset = member_offsets[ordered_member_idx];
491     const auto memberIdx = member_offset.member;
492     const auto offset = member_offset.offset;
493     auto id = members[member_offset.member];
494     const LayoutConstraints& constraint =
495         constraints[std::make_pair(struct_id, uint32_t(memberIdx))];
496     // Scalar layout takes precedence because it's more permissive, and implying
497     // an alignment that divides evenly into the alignment that would otherwise
498     // be used.
499     const auto alignment =
500         scalar_block_layout
501             ? getScalarAlignment(id, vstate)
502             : getBaseAlignment(id, blockRules, constraint, constraints, vstate);
503     const auto inst = vstate.FindDef(id);
504     const auto opcode = inst->opcode();
505     const auto size = getSize(id, constraint, constraints, vstate);
506     // Check offset.
507     if (offset == 0xffffffff)
508       return fail(memberIdx) << "is missing an Offset decoration";
509     if (!scalar_block_layout && relaxed_block_layout &&
510         opcode == spv::Op::OpTypeVector) {
511       // In relaxed block layout, the vector offset must be aligned to the
512       // vector's scalar element type.
513       const auto componentId = inst->words()[2];
514       const auto scalar_alignment = getScalarAlignment(componentId, vstate);
515       if (!IsAlignedTo(offset, scalar_alignment)) {
516         return fail(memberIdx)
517                << "at offset " << offset
518                << " is not aligned to scalar element size " << scalar_alignment;
519       }
520     } else {
521       // Without relaxed block layout, the offset must be divisible by the
522       // alignment requirement.
523       if (!IsAlignedTo(offset, alignment)) {
524         return fail(memberIdx)
525                << "at offset " << offset << " is not aligned to " << alignment;
526       }
527     }
528     if (offset < nextValidOffset)
529       return fail(memberIdx) << "at offset " << offset
530                              << " overlaps previous member ending at offset "
531                              << nextValidOffset - 1;
532     if (!scalar_block_layout && relaxed_block_layout) {
533       // Check improper straddle of vectors.
534       if (spv::Op::OpTypeVector == opcode &&
535           hasImproperStraddle(id, offset, constraint, constraints, vstate))
536         return fail(memberIdx)
537                << "is an improperly straddling vector at offset " << offset;
538     }
539     // Check struct members recursively.
540     spv_result_t recursive_status = SPV_SUCCESS;
541     if (spv::Op::OpTypeStruct == opcode &&
542         SPV_SUCCESS != (recursive_status = checkLayout(
543                             id, storage_class_str, decoration_str, blockRules,
544                             scalar_block_layout, offset, constraints, vstate)))
545       return recursive_status;
546     // Check matrix stride.
547     if (spv::Op::OpTypeMatrix == opcode) {
548       const auto stride = constraint.matrix_stride;
549       if (!IsAlignedTo(stride, alignment)) {
550         return fail(memberIdx) << "is a matrix with stride " << stride
551                                << " not satisfying alignment to " << alignment;
552       }
553     }
554 
555     // Check arrays and runtime arrays recursively.
556     auto array_inst = inst;
557     auto array_alignment = alignment;
558     while (array_inst->opcode() == spv::Op::OpTypeArray ||
559            array_inst->opcode() == spv::Op::OpTypeRuntimeArray) {
560       const auto typeId = array_inst->word(2);
561       const auto element_inst = vstate.FindDef(typeId);
562       // Check array stride.
563       uint32_t array_stride = 0;
564       for (auto& decoration : vstate.id_decorations(array_inst->id())) {
565         if (spv::Decoration::ArrayStride == decoration.dec_type()) {
566           array_stride = decoration.params()[0];
567           if (array_stride == 0) {
568             return fail(memberIdx) << "contains an array with stride 0";
569           }
570           if (!IsAlignedTo(array_stride, array_alignment))
571             return fail(memberIdx)
572                    << "contains an array with stride " << decoration.params()[0]
573                    << " not satisfying alignment to " << alignment;
574         }
575       }
576 
577       bool is_int32 = false;
578       bool is_const = false;
579       uint32_t num_elements = 0;
580       if (array_inst->opcode() == spv::Op::OpTypeArray) {
581         std::tie(is_int32, is_const, num_elements) =
582             vstate.EvalInt32IfConst(array_inst->word(3));
583       }
584       num_elements = std::max(1u, num_elements);
585       // Check each element recursively if it is a struct. There is a
586       // limitation to this check if the array size is a spec constant or is a
587       // runtime array then we will only check a single element. This means
588       // some improper straddles might be missed.
589       if (spv::Op::OpTypeStruct == element_inst->opcode()) {
590         std::vector<bool> seen(16, false);
591         for (uint32_t i = 0; i < num_elements; ++i) {
592           uint32_t next_offset = i * array_stride + offset;
593           // Stop checking if offsets repeat in terms of 16-byte multiples.
594           if (seen[next_offset % 16]) {
595             break;
596           }
597 
598           if (SPV_SUCCESS !=
599               (recursive_status = checkLayout(
600                    typeId, storage_class_str, decoration_str, blockRules,
601                    scalar_block_layout, next_offset, constraints, vstate)))
602             return recursive_status;
603 
604           seen[next_offset % 16] = true;
605         }
606       } else if (spv::Op::OpTypeMatrix == element_inst->opcode()) {
607         // Matrix stride would be on the array element in the struct.
608         const auto stride = constraint.matrix_stride;
609         if (!IsAlignedTo(stride, alignment)) {
610           return fail(memberIdx)
611                  << "is a matrix with stride " << stride
612                  << " not satisfying alignment to " << alignment;
613         }
614       }
615 
616       // Proceed to the element in case it is an array.
617       array_inst = element_inst;
618       array_alignment = scalar_block_layout
619                             ? getScalarAlignment(array_inst->id(), vstate)
620                             : getBaseAlignment(array_inst->id(), blockRules,
621                                                constraint, constraints, vstate);
622 
623       const auto element_size =
624           getSize(element_inst->id(), constraint, constraints, vstate);
625       if (element_size > array_stride) {
626         return fail(memberIdx)
627                << "contains an array with stride " << array_stride
628                << ", but with an element size of " << element_size;
629       }
630     }
631     nextValidOffset = offset + size;
632     if (!scalar_block_layout &&
633         (spv::Op::OpTypeArray == opcode || spv::Op::OpTypeStruct == opcode)) {
634       // Non-scalar block layout rules don't permit anything in the padding of
635       // a struct or array.
636       nextValidOffset = align(nextValidOffset, alignment);
637     }
638   }
639   return SPV_SUCCESS;
640 }
641 
642 // Returns true if variable or structure id has given decoration. Handles also
643 // nested structures.
hasDecoration(uint32_t id,spv::Decoration decoration,ValidationState_t & vstate)644 bool hasDecoration(uint32_t id, spv::Decoration decoration,
645                    ValidationState_t& vstate) {
646   for (auto& dec : vstate.id_decorations(id)) {
647     if (decoration == dec.dec_type()) return true;
648   }
649   if (spv::Op::OpTypeStruct != vstate.FindDef(id)->opcode()) {
650     return false;
651   }
652   for (auto member_id : getStructMembers(id, spv::Op::OpTypeStruct, vstate)) {
653     if (hasDecoration(member_id, decoration, vstate)) {
654       return true;
655     }
656   }
657   return false;
658 }
659 
660 // Returns true if all ids of given type have a specified decoration.
checkForRequiredDecoration(uint32_t struct_id,std::function<bool (spv::Decoration)> checker,spv::Op type,ValidationState_t & vstate)661 bool checkForRequiredDecoration(uint32_t struct_id,
662                                 std::function<bool(spv::Decoration)> checker,
663                                 spv::Op type, ValidationState_t& vstate) {
664   const auto& members = getStructMembers(struct_id, vstate);
665   for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
666     auto id = members[memberIdx];
667     if (type == spv::Op::OpTypeMatrix) {
668       // Matrix decorations also apply to arrays of matrices.
669       auto memberInst = vstate.FindDef(id);
670       while (memberInst->opcode() == spv::Op::OpTypeArray ||
671              memberInst->opcode() == spv::Op::OpTypeRuntimeArray) {
672         memberInst = vstate.FindDef(memberInst->GetOperandAs<uint32_t>(1u));
673       }
674       id = memberInst->id();
675     }
676     if (type != vstate.FindDef(id)->opcode()) continue;
677     bool found = false;
678     for (auto& dec : vstate.id_decorations(id)) {
679       if (checker(dec.dec_type())) found = true;
680     }
681     for (auto& dec : vstate.id_decorations(struct_id)) {
682       if (checker(dec.dec_type()) &&
683           (int)memberIdx == dec.struct_member_index()) {
684         found = true;
685       }
686     }
687     if (!found) {
688       return false;
689     }
690   }
691   for (auto id : getStructMembers(struct_id, spv::Op::OpTypeStruct, vstate)) {
692     if (!checkForRequiredDecoration(id, checker, type, vstate)) {
693       return false;
694     }
695   }
696   return true;
697 }
698 
CheckLinkageAttrOfFunctions(ValidationState_t & vstate)699 spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) {
700   for (const auto& function : vstate.functions()) {
701     if (function.block_count() == 0u) {
702       // A function declaration (an OpFunction with no basic blocks), must have
703       // a Linkage Attributes Decoration with the Import Linkage Type.
704       if (!hasImportLinkageAttribute(function.id(), vstate)) {
705         return vstate.diag(SPV_ERROR_INVALID_BINARY,
706                            vstate.FindDef(function.id()))
707                << "Function declaration (id " << function.id()
708                << ") must have a LinkageAttributes decoration with the Import "
709                   "Linkage type.";
710       }
711     } else {
712       if (hasImportLinkageAttribute(function.id(), vstate)) {
713         return vstate.diag(SPV_ERROR_INVALID_BINARY,
714                            vstate.FindDef(function.id()))
715                << "Function definition (id " << function.id()
716                << ") may not be decorated with Import Linkage type.";
717       }
718     }
719   }
720   return SPV_SUCCESS;
721 }
722 
723 // Checks whether an imported variable is initialized by this module.
CheckImportedVariableInitialization(ValidationState_t & vstate)724 spv_result_t CheckImportedVariableInitialization(ValidationState_t& vstate) {
725   // According the SPIR-V Spec 2.16.1, it is illegal to initialize an imported
726   // variable. This means that a module-scope OpVariable with initialization
727   // value cannot be marked with the Import Linkage Type (import type id = 1).
728   for (auto global_var_id : vstate.global_vars()) {
729     // Initializer <id> is an optional argument for OpVariable. If initializer
730     // <id> is present, the instruction will have 5 words.
731     auto variable_instr = vstate.FindDef(global_var_id);
732     if (variable_instr->words().size() == 5u &&
733         hasImportLinkageAttribute(global_var_id, vstate)) {
734       return vstate.diag(SPV_ERROR_INVALID_ID, variable_instr)
735              << "A module-scope OpVariable with initialization value "
736                 "cannot be marked with the Import Linkage Type.";
737     }
738   }
739   return SPV_SUCCESS;
740 }
741 
742 // Checks whether a builtin variable is valid.
CheckBuiltInVariable(uint32_t var_id,ValidationState_t & vstate)743 spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) {
744   const auto& decorations = vstate.id_decorations(var_id);
745   for (const auto& d : decorations) {
746     if (spvIsVulkanEnv(vstate.context()->target_env)) {
747       if (d.dec_type() == spv::Decoration::Location ||
748           d.dec_type() == spv::Decoration::Component) {
749         return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
750                << vstate.VkErrorID(4915) << "A BuiltIn variable (id " << var_id
751                << ") cannot have any Location or Component decorations";
752       }
753     }
754   }
755   return SPV_SUCCESS;
756 }
757 
758 // Checks whether proper decorations have been applied to the entry points.
CheckDecorationsOfEntryPoints(ValidationState_t & vstate)759 spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
760   for (uint32_t entry_point : vstate.entry_points()) {
761     const auto& descs = vstate.entry_point_descriptions(entry_point);
762     int num_builtin_block_inputs = 0;
763     int num_builtin_block_outputs = 0;
764     int num_workgroup_variables = 0;
765     int num_workgroup_variables_with_block = 0;
766     int num_workgroup_variables_with_aliased = 0;
767     for (const auto& desc : descs) {
768       std::unordered_set<Instruction*> seen_vars;
769       std::unordered_set<spv::BuiltIn> input_var_builtin;
770       std::unordered_set<spv::BuiltIn> output_var_builtin;
771       for (auto interface : desc.interfaces) {
772         Instruction* var_instr = vstate.FindDef(interface);
773         if (!var_instr || spv::Op::OpVariable != var_instr->opcode()) {
774           return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
775                  << "Interfaces passed to OpEntryPoint must be of type "
776                     "OpTypeVariable. Found Op"
777                  << spvOpcodeString(var_instr->opcode()) << ".";
778         }
779         const spv::StorageClass storage_class =
780             var_instr->GetOperandAs<spv::StorageClass>(2);
781         if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
782           // Starting in 1.4, OpEntryPoint must list all global variables
783           // it statically uses and those interfaces must be unique.
784           if (storage_class == spv::StorageClass::Function) {
785             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
786                    << "OpEntryPoint interfaces should only list global "
787                       "variables";
788           }
789 
790           if (!seen_vars.insert(var_instr).second) {
791             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
792                    << "Non-unique OpEntryPoint interface "
793                    << vstate.getIdName(interface) << " is disallowed";
794           }
795         } else {
796           if (storage_class != spv::StorageClass::Input &&
797               storage_class != spv::StorageClass::Output) {
798             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
799                    << "OpEntryPoint interfaces must be OpVariables with "
800                       "Storage Class of Input(1) or Output(3). Found Storage "
801                       "Class "
802                    << uint32_t(storage_class) << " for Entry Point id "
803                    << entry_point << ".";
804           }
805         }
806 
807         const uint32_t ptr_id = var_instr->word(1);
808         Instruction* ptr_instr = vstate.FindDef(ptr_id);
809         // It is guaranteed (by validator ID checks) that ptr_instr is
810         // OpTypePointer. Word 3 of this instruction is the type being pointed
811         // to.
812         const uint32_t type_id = ptr_instr->word(3);
813         Instruction* type_instr = vstate.FindDef(type_id);
814         const bool is_struct =
815             type_instr && spv::Op::OpTypeStruct == type_instr->opcode();
816 
817         // Search all Built-in (on the variable or the struct)
818         bool has_built_in = false;
819         for (auto& dec :
820              vstate.id_decorations(is_struct ? type_id : interface)) {
821           if (dec.dec_type() != spv::Decoration::BuiltIn) continue;
822           has_built_in = true;
823 
824           if (!spvIsVulkanEnv(vstate.context()->target_env)) continue;
825 
826           const spv::BuiltIn builtin = dec.builtin();
827           if (storage_class == spv::StorageClass::Input) {
828             if (!input_var_builtin.insert(builtin).second) {
829               return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
830                      << vstate.VkErrorID(9658)
831                      << "OpEntryPoint contains duplicate input variables "
832                         "with "
833                      << vstate.grammar().lookupOperandName(
834                             SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin)
835                      << " builtin";
836             }
837           }
838           if (storage_class == spv::StorageClass::Output) {
839             if (!output_var_builtin.insert(builtin).second) {
840               return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
841                      << vstate.VkErrorID(9659)
842                      << "OpEntryPoint contains duplicate output variables "
843                         "with "
844                      << vstate.grammar().lookupOperandName(
845                             SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin)
846                      << " builtin";
847             }
848           }
849         }
850 
851         if (has_built_in) {
852           if (auto error = CheckBuiltInVariable(interface, vstate))
853             return error;
854 
855           if (is_struct) {
856             if (!isBlock(type_id, vstate)) {
857               return vstate.diag(SPV_ERROR_INVALID_DATA,
858                                  vstate.FindDef(type_id))
859                      << vstate.VkErrorID(4919)
860                      << "Interface struct has no Block decoration but has "
861                         "BuiltIn members. "
862                         "Location decorations must be used on each member of "
863                         "OpVariable with a structure type that is a block not "
864                         "decorated with Location.";
865             }
866             if (storage_class == spv::StorageClass::Input)
867               ++num_builtin_block_inputs;
868             if (storage_class == spv::StorageClass::Output)
869               ++num_builtin_block_outputs;
870             if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1)
871               break;
872           }
873         }
874 
875         if (storage_class == spv::StorageClass::Workgroup) {
876           ++num_workgroup_variables;
877           if (is_struct) {
878             if (hasDecoration(type_id, spv::Decoration::Block, vstate))
879               ++num_workgroup_variables_with_block;
880             if (hasDecoration(var_instr->id(), spv::Decoration::Aliased,
881                               vstate))
882               ++num_workgroup_variables_with_aliased;
883           }
884         }
885 
886         if (spvIsVulkanEnv(vstate.context()->target_env)) {
887           const auto* models = vstate.GetExecutionModels(entry_point);
888           const bool has_frag =
889               models->find(spv::ExecutionModel::Fragment) != models->end();
890           const bool has_vert =
891               models->find(spv::ExecutionModel::Vertex) != models->end();
892           for (const auto& decoration :
893                vstate.id_decorations(var_instr->id())) {
894             if (decoration == spv::Decoration::Flat ||
895                 decoration == spv::Decoration::NoPerspective ||
896                 decoration == spv::Decoration::Sample ||
897                 decoration == spv::Decoration::Centroid) {
898               // VUID 04670 already validates these decorations are input/output
899               if (storage_class == spv::StorageClass::Input &&
900                   (models->size() > 1 || has_vert)) {
901                 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
902                        << vstate.VkErrorID(6202)
903                        << vstate.SpvDecorationString(decoration.dec_type())
904                        << " decorated variable must not be used in vertex "
905                           "execution model as an Input storage class for Entry "
906                           "Point id "
907                        << entry_point << ".";
908               } else if (storage_class == spv::StorageClass::Output &&
909                          (models->size() > 1 || has_frag)) {
910                 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
911                        << vstate.VkErrorID(6201)
912                        << vstate.SpvDecorationString(decoration.dec_type())
913                        << " decorated variable must not be used in fragment "
914                           "execution model as an Output storage class for "
915                           "Entry Point id "
916                        << entry_point << ".";
917               }
918             }
919           }
920 
921           const bool has_flat =
922               hasDecoration(var_instr->id(), spv::Decoration::Flat, vstate);
923           if (has_frag && storage_class == spv::StorageClass::Input &&
924               !has_flat &&
925               ((vstate.IsFloatScalarType(type_id) &&
926                 vstate.GetBitWidth(type_id) == 64) ||
927                vstate.IsIntScalarOrVectorType(type_id))) {
928             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
929                      << vstate.VkErrorID(4744)
930                      << "Fragment OpEntryPoint operand "
931                      << interface << " with Input interfaces with integer or "
932                                      "float type must have a Flat decoration "
933                                      "for Entry Point id "
934                      << entry_point << ".";
935           }
936         }
937       }
938       if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) {
939         return vstate.diag(SPV_ERROR_INVALID_BINARY,
940                            vstate.FindDef(entry_point))
941                << "There must be at most one object per Storage Class that can "
942                   "contain a structure type containing members decorated with "
943                   "BuiltIn, consumed per entry-point. Entry Point id "
944                << entry_point << " does not meet this requirement.";
945       }
946       // The LinkageAttributes Decoration cannot be applied to functions
947       // targeted by an OpEntryPoint instruction
948       for (auto& decoration : vstate.id_decorations(entry_point)) {
949         if (spv::Decoration::LinkageAttributes == decoration.dec_type()) {
950           const std::string linkage_name =
951               spvtools::utils::MakeString(decoration.params());
952           return vstate.diag(SPV_ERROR_INVALID_BINARY,
953                              vstate.FindDef(entry_point))
954                  << "The LinkageAttributes Decoration (Linkage name: "
955                  << linkage_name << ") cannot be applied to function id "
956                  << entry_point
957                  << " because it is targeted by an OpEntryPoint instruction.";
958         }
959       }
960 
961       const bool workgroup_blocks_allowed = vstate.HasCapability(
962           spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
963       if (workgroup_blocks_allowed && num_workgroup_variables > 0 &&
964           num_workgroup_variables_with_block > 0) {
965         if (num_workgroup_variables != num_workgroup_variables_with_block) {
966           return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point))
967                  << "When declaring WorkgroupMemoryExplicitLayoutKHR, "
968                     "either all or none of the Workgroup Storage Class variables "
969                     "in the entry point interface must point to struct types "
970                     "decorated with Block.  Entry point id "
971                  << entry_point << " does not meet this requirement.";
972         }
973         if (num_workgroup_variables_with_block > 1 &&
974             num_workgroup_variables_with_block !=
975             num_workgroup_variables_with_aliased) {
976           return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point))
977                  << "When declaring WorkgroupMemoryExplicitLayoutKHR, "
978                     "if more than one Workgroup Storage Class variable in "
979                     "the entry point interface point to a type decorated "
980                     "with Block, all of them must be decorated with Aliased. "
981                     "Entry point id "
982                  << entry_point << " does not meet this requirement.";
983         }
984       } else if (!workgroup_blocks_allowed &&
985                  num_workgroup_variables_with_block > 0) {
986         return vstate.diag(SPV_ERROR_INVALID_BINARY,
987                            vstate.FindDef(entry_point))
988                << "Workgroup Storage Class variables can't be decorated with "
989                   "Block unless declaring the WorkgroupMemoryExplicitLayoutKHR "
990                   "capability.";
991       }
992     }
993   }
994   return SPV_SUCCESS;
995 }
996 
997 // Load |constraints| with all the member constraints for structs contained
998 // within the given array type.
999 void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
1000                                       uint32_t array_id,
1001                                       const LayoutConstraints& inherited,
1002                                       ValidationState_t& vstate);
1003 
1004 // Load |constraints| with all the member constraints for the given struct,
1005 // and all its contained structs.
ComputeMemberConstraintsForStruct(MemberConstraints * constraints,uint32_t struct_id,const LayoutConstraints & inherited,ValidationState_t & vstate)1006 void ComputeMemberConstraintsForStruct(MemberConstraints* constraints,
1007                                        uint32_t struct_id,
1008                                        const LayoutConstraints& inherited,
1009                                        ValidationState_t& vstate) {
1010   assert(constraints);
1011   const auto& members = getStructMembers(struct_id, vstate);
1012   for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
1013        memberIdx < numMembers; memberIdx++) {
1014     LayoutConstraints& constraint =
1015         (*constraints)[std::make_pair(struct_id, memberIdx)];
1016     constraint = inherited;
1017     auto member_decorations =
1018         vstate.id_member_decorations(struct_id, memberIdx);
1019     for (auto decoration = member_decorations.begin;
1020          decoration != member_decorations.end; ++decoration) {
1021       assert(decoration->struct_member_index() == (int)memberIdx);
1022       switch (decoration->dec_type()) {
1023         case spv::Decoration::RowMajor:
1024           constraint.majorness = kRowMajor;
1025           break;
1026         case spv::Decoration::ColMajor:
1027           constraint.majorness = kColumnMajor;
1028           break;
1029         case spv::Decoration::MatrixStride:
1030           constraint.matrix_stride = decoration->params()[0];
1031           break;
1032         default:
1033           break;
1034       }
1035     }
1036 
1037     // Now recurse
1038     auto member_type_id = members[memberIdx];
1039     const auto member_type_inst = vstate.FindDef(member_type_id);
1040     const auto opcode = member_type_inst->opcode();
1041     switch (opcode) {
1042       case spv::Op::OpTypeArray:
1043       case spv::Op::OpTypeRuntimeArray:
1044         ComputeMemberConstraintsForArray(constraints, member_type_id, inherited,
1045                                          vstate);
1046         break;
1047       case spv::Op::OpTypeStruct:
1048         ComputeMemberConstraintsForStruct(constraints, member_type_id,
1049                                           inherited, vstate);
1050         break;
1051       default:
1052         break;
1053     }
1054   }
1055 }
1056 
ComputeMemberConstraintsForArray(MemberConstraints * constraints,uint32_t array_id,const LayoutConstraints & inherited,ValidationState_t & vstate)1057 void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
1058                                       uint32_t array_id,
1059                                       const LayoutConstraints& inherited,
1060                                       ValidationState_t& vstate) {
1061   assert(constraints);
1062   auto elem_type_id = vstate.FindDef(array_id)->words()[2];
1063   const auto elem_type_inst = vstate.FindDef(elem_type_id);
1064   const auto opcode = elem_type_inst->opcode();
1065   switch (opcode) {
1066     case spv::Op::OpTypeArray:
1067     case spv::Op::OpTypeRuntimeArray:
1068       ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited,
1069                                        vstate);
1070       break;
1071     case spv::Op::OpTypeStruct:
1072       ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited,
1073                                         vstate);
1074       break;
1075     default:
1076       break;
1077   }
1078 }
1079 
CheckDecorationsOfBuffers(ValidationState_t & vstate)1080 spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
1081   // Set of entry points that are known to use a push constant.
1082   std::unordered_set<uint32_t> uses_push_constant;
1083   for (const auto& inst : vstate.ordered_instructions()) {
1084     const auto& words = inst.words();
1085     auto type_id = inst.type_id();
1086     const Instruction* type_inst = vstate.FindDef(type_id);
1087     if (spv::Op::OpVariable == inst.opcode()) {
1088       const auto var_id = inst.id();
1089       // For storage class / decoration combinations, see Vulkan 14.5.4 "Offset
1090       // and Stride Assignment".
1091       const auto storageClass = inst.GetOperandAs<spv::StorageClass>(2);
1092       const bool uniform = storageClass == spv::StorageClass::Uniform;
1093       const bool uniform_constant =
1094           storageClass == spv::StorageClass::UniformConstant;
1095       const bool push_constant =
1096           storageClass == spv::StorageClass::PushConstant;
1097       const bool storage_buffer =
1098           storageClass == spv::StorageClass::StorageBuffer;
1099 
1100       if (spvIsVulkanEnv(vstate.context()->target_env)) {
1101         // Vulkan: There must be no more than one PushConstant block per entry
1102         // point.
1103         if (push_constant) {
1104           auto entry_points = vstate.EntryPointReferences(var_id);
1105           for (auto ep_id : entry_points) {
1106             const bool already_used = !uses_push_constant.insert(ep_id).second;
1107             if (already_used) {
1108               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1109                      << vstate.VkErrorID(6674) << "Entry point id '" << ep_id
1110                      << "' uses more than one PushConstant interface.\n"
1111                      << "From Vulkan spec:\n"
1112                      << "There must be no more than one push constant block "
1113                      << "statically used per shader entry point.";
1114             }
1115           }
1116         }
1117         // Vulkan: Check DescriptorSet and Binding decoration for
1118         // UniformConstant which cannot be a struct.
1119         if (uniform_constant) {
1120           auto entry_points = vstate.EntryPointReferences(var_id);
1121           if (!entry_points.empty() &&
1122               !hasDecoration(var_id, spv::Decoration::DescriptorSet, vstate)) {
1123             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1124                    << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
1125                    << "' is missing DescriptorSet decoration.\n"
1126                    << "From Vulkan spec:\n"
1127                    << "These variables must have DescriptorSet and Binding "
1128                       "decorations specified";
1129           }
1130           if (!entry_points.empty() &&
1131               !hasDecoration(var_id, spv::Decoration::Binding, vstate)) {
1132             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1133                    << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
1134                    << "' is missing Binding decoration.\n"
1135                    << "From Vulkan spec:\n"
1136                    << "These variables must have DescriptorSet and Binding "
1137                       "decorations specified";
1138           }
1139         }
1140       }
1141 
1142       if (spvIsOpenGLEnv(vstate.context()->target_env)) {
1143         bool has_block = hasDecoration(var_id, spv::Decoration::Block, vstate);
1144         bool has_buffer_block =
1145             hasDecoration(var_id, spv::Decoration::BufferBlock, vstate);
1146         if ((uniform && (has_block || has_buffer_block)) ||
1147             (storage_buffer && has_block)) {
1148           auto entry_points = vstate.EntryPointReferences(var_id);
1149           if (!entry_points.empty() &&
1150               !hasDecoration(var_id, spv::Decoration::Binding, vstate)) {
1151             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1152                    << (uniform ? "Uniform" : "Storage Buffer") << " id '"
1153                    << var_id << "' is missing Binding decoration.\n"
1154                    << "From ARB_gl_spirv extension:\n"
1155                    << "Uniform and shader storage block variables must "
1156                    << "also be decorated with a *Binding*.";
1157           }
1158         }
1159       }
1160 
1161       const bool phys_storage_buffer =
1162           storageClass == spv::StorageClass::PhysicalStorageBuffer;
1163       const bool workgroup =
1164           storageClass == spv::StorageClass::Workgroup &&
1165           vstate.HasCapability(
1166               spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
1167       if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
1168           workgroup) {
1169         const auto ptrInst = vstate.FindDef(words[1]);
1170         assert(spv::Op::OpTypePointer == ptrInst->opcode());
1171         auto id = ptrInst->words()[3];
1172         auto id_inst = vstate.FindDef(id);
1173         // Jump through one level of arraying.
1174         if (!workgroup && (id_inst->opcode() == spv::Op::OpTypeArray ||
1175                            id_inst->opcode() == spv::Op::OpTypeRuntimeArray)) {
1176           id = id_inst->GetOperandAs<uint32_t>(1u);
1177           id_inst = vstate.FindDef(id);
1178         }
1179         // Struct requirement is checked on variables so just move on here.
1180         if (spv::Op::OpTypeStruct != id_inst->opcode()) continue;
1181         MemberConstraints constraints;
1182         ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
1183                                           vstate);
1184         // Prepare for messages
1185         const char* sc_str =
1186             uniform ? "Uniform"
1187                     : (push_constant ? "PushConstant"
1188                                      : (workgroup ? "Workgroup"
1189                                                   : "StorageBuffer"));
1190 
1191         if (spvIsVulkanEnv(vstate.context()->target_env)) {
1192           const bool block = hasDecoration(id, spv::Decoration::Block, vstate);
1193           const bool buffer_block =
1194               hasDecoration(id, spv::Decoration::BufferBlock, vstate);
1195           if (storage_buffer && buffer_block) {
1196             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1197                    << vstate.VkErrorID(6675) << "Storage buffer id '" << var_id
1198                    << " In Vulkan, BufferBlock is disallowed on variables in "
1199                       "the StorageBuffer storage class";
1200           }
1201           // Vulkan: Check Block decoration for PushConstant, Uniform
1202           // and StorageBuffer variables. Uniform can also use BufferBlock.
1203           if (push_constant && !block) {
1204             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1205                    << vstate.VkErrorID(6675) << "PushConstant id '" << id
1206                    << "' is missing Block decoration.\n"
1207                    << "From Vulkan spec:\n"
1208                    << "Such variables must be identified with a Block "
1209                       "decoration";
1210           }
1211           if (storage_buffer && !block) {
1212             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1213                    << vstate.VkErrorID(6675) << "StorageBuffer id '" << id
1214                    << "' is missing Block decoration.\n"
1215                    << "From Vulkan spec:\n"
1216                    << "Such variables must be identified with a Block "
1217                       "decoration";
1218           }
1219           if (uniform && !block && !buffer_block) {
1220             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1221                    << vstate.VkErrorID(6676) << "Uniform id '" << id
1222                    << "' is missing Block or BufferBlock decoration.\n"
1223                    << "From Vulkan spec:\n"
1224                    << "Such variables must be identified with a Block or "
1225                       "BufferBlock decoration";
1226           }
1227           // Vulkan: Check DescriptorSet and Binding decoration for
1228           // Uniform and StorageBuffer variables.
1229           if (uniform || storage_buffer) {
1230             auto entry_points = vstate.EntryPointReferences(var_id);
1231             if (!entry_points.empty() &&
1232                 !hasDecoration(var_id, spv::Decoration::DescriptorSet,
1233                                vstate)) {
1234               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1235                      << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
1236                      << "' is missing DescriptorSet decoration.\n"
1237                      << "From Vulkan spec:\n"
1238                      << "These variables must have DescriptorSet and Binding "
1239                         "decorations specified";
1240             }
1241             if (!entry_points.empty() &&
1242                 !hasDecoration(var_id, spv::Decoration::Binding, vstate)) {
1243               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1244                      << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
1245                      << "' is missing Binding decoration.\n"
1246                      << "From Vulkan spec:\n"
1247                      << "These variables must have DescriptorSet and Binding "
1248                         "decorations specified";
1249             }
1250           }
1251         }
1252 
1253         for (const auto& dec : vstate.id_decorations(id)) {
1254           const bool blockDeco = spv::Decoration::Block == dec.dec_type();
1255           const bool bufferDeco =
1256               spv::Decoration::BufferBlock == dec.dec_type();
1257           const bool blockRules = uniform && blockDeco;
1258           const bool bufferRules =
1259               (uniform && bufferDeco) ||
1260               ((push_constant || storage_buffer ||
1261                 phys_storage_buffer || workgroup) && blockDeco);
1262           if (uniform && blockDeco) {
1263             vstate.RegisterPointerToUniformBlock(ptrInst->id());
1264             vstate.RegisterStructForUniformBlock(id);
1265           }
1266           if ((uniform && bufferDeco) ||
1267               ((storage_buffer || phys_storage_buffer) && blockDeco)) {
1268             vstate.RegisterPointerToStorageBuffer(ptrInst->id());
1269             vstate.RegisterStructForStorageBuffer(id);
1270           }
1271 
1272           if (blockRules || bufferRules) {
1273             const char* deco_str = blockDeco ? "Block" : "BufferBlock";
1274             spv_result_t recursive_status = SPV_SUCCESS;
1275             const bool scalar_block_layout = workgroup ?
1276                 vstate.options()->workgroup_scalar_block_layout :
1277                 vstate.options()->scalar_block_layout;
1278 
1279             if (isMissingOffsetInStruct(id, vstate)) {
1280               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1281                      << "Structure id " << id << " decorated as " << deco_str
1282                      << " must be explicitly laid out with Offset "
1283                         "decorations.";
1284             }
1285 
1286             if (!checkForRequiredDecoration(
1287                     id,
1288                     [](spv::Decoration d) {
1289                       return d == spv::Decoration::ArrayStride;
1290                     },
1291                     spv::Op::OpTypeArray, vstate)) {
1292               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1293                      << "Structure id " << id << " decorated as " << deco_str
1294                      << " must be explicitly laid out with ArrayStride "
1295                         "decorations.";
1296             }
1297 
1298             if (!checkForRequiredDecoration(
1299                     id,
1300                     [](spv::Decoration d) {
1301                       return d == spv::Decoration::MatrixStride;
1302                     },
1303                     spv::Op::OpTypeMatrix, vstate)) {
1304               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1305                      << "Structure id " << id << " decorated as " << deco_str
1306                      << " must be explicitly laid out with MatrixStride "
1307                         "decorations.";
1308             }
1309 
1310             if (!checkForRequiredDecoration(
1311                     id,
1312                     [](spv::Decoration d) {
1313                       return d == spv::Decoration::RowMajor ||
1314                              d == spv::Decoration::ColMajor;
1315                     },
1316                     spv::Op::OpTypeMatrix, vstate)) {
1317               return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1318                      << "Structure id " << id << " decorated as " << deco_str
1319                      << " must be explicitly laid out with RowMajor or "
1320                         "ColMajor decorations.";
1321             }
1322 
1323             if (spvIsVulkanEnv(vstate.context()->target_env)) {
1324               if (blockRules && (SPV_SUCCESS != (recursive_status = checkLayout(
1325                                                      id, sc_str, deco_str, true,
1326                                                      scalar_block_layout, 0,
1327                                                      constraints, vstate)))) {
1328                 return recursive_status;
1329               } else if (bufferRules &&
1330                          (SPV_SUCCESS !=
1331                           (recursive_status = checkLayout(
1332                                id, sc_str, deco_str, false, scalar_block_layout,
1333                                0, constraints, vstate)))) {
1334                 return recursive_status;
1335               }
1336             }
1337           }
1338         }
1339       }
1340     } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
1341                type_inst->GetOperandAs<spv::StorageClass>(1u) ==
1342                    spv::StorageClass::PhysicalStorageBuffer) {
1343       const bool scalar_block_layout = vstate.options()->scalar_block_layout;
1344       MemberConstraints constraints;
1345       const bool buffer = true;
1346       const auto data_type_id = type_inst->GetOperandAs<uint32_t>(2u);
1347       const auto* data_type_inst = vstate.FindDef(data_type_id);
1348       if (data_type_inst->opcode() == spv::Op::OpTypeStruct) {
1349         ComputeMemberConstraintsForStruct(&constraints, data_type_id,
1350                                           LayoutConstraints(), vstate);
1351       }
1352       if (auto res = checkLayout(data_type_id, "PhysicalStorageBuffer", "Block",
1353                                  !buffer, scalar_block_layout, 0, constraints,
1354                                  vstate)) {
1355         return res;
1356       }
1357     }
1358   }
1359   return SPV_SUCCESS;
1360 }
1361 
1362 // Returns true if |decoration| cannot be applied to the same id more than once.
AtMostOncePerId(spv::Decoration decoration)1363 bool AtMostOncePerId(spv::Decoration decoration) {
1364   return decoration != spv::Decoration::UserSemantic &&
1365          decoration != spv::Decoration::FuncParamAttr;
1366 }
1367 
1368 // Returns true if |decoration| cannot be applied to the same member more than
1369 // once.
AtMostOncePerMember(spv::Decoration decoration)1370 bool AtMostOncePerMember(spv::Decoration decoration) {
1371   return decoration != spv::Decoration::UserSemantic;
1372 }
1373 
CheckDecorationsCompatibility(ValidationState_t & vstate)1374 spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
1375   using PerIDKey = std::tuple<spv::Decoration, uint32_t>;
1376   using PerMemberKey = std::tuple<spv::Decoration, uint32_t, uint32_t>;
1377 
1378   // An Array of pairs where the decorations in the pair cannot both be applied
1379   // to the same id.
1380   static const spv::Decoration mutually_exclusive_per_id[][2] = {
1381       {spv::Decoration::Block, spv::Decoration::BufferBlock},
1382       {spv::Decoration::Restrict, spv::Decoration::Aliased}};
1383   static const auto num_mutually_exclusive_per_id_pairs =
1384       sizeof(mutually_exclusive_per_id) / (2 * sizeof(spv::Decoration));
1385 
1386   // An Array of pairs where the decorations in the pair cannot both be applied
1387   // to the same member.
1388   static const spv::Decoration mutually_exclusive_per_member[][2] = {
1389       {spv::Decoration::RowMajor, spv::Decoration::ColMajor}};
1390   static const auto num_mutually_exclusive_per_mem_pairs =
1391       sizeof(mutually_exclusive_per_member) / (2 * sizeof(spv::Decoration));
1392 
1393   std::set<PerIDKey> seen_per_id;
1394   std::set<PerMemberKey> seen_per_member;
1395 
1396   for (const auto& inst : vstate.ordered_instructions()) {
1397     const auto& words = inst.words();
1398     if (spv::Op::OpDecorate == inst.opcode()) {
1399       const auto id = words[1];
1400       const auto dec_type = static_cast<spv::Decoration>(words[2]);
1401       const auto k = PerIDKey(dec_type, id);
1402       const auto already_used = !seen_per_id.insert(k).second;
1403       if (already_used && AtMostOncePerId(dec_type)) {
1404         return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1405                << "ID '" << id << "' decorated with "
1406                << vstate.SpvDecorationString(dec_type)
1407                << " multiple times is not allowed.";
1408       }
1409       // Verify certain mutually exclusive decorations are not both applied on
1410       // an ID.
1411       for (uint32_t pair_idx = 0;
1412            pair_idx < num_mutually_exclusive_per_id_pairs; ++pair_idx) {
1413         spv::Decoration excl_dec_type = spv::Decoration::Max;
1414         if (mutually_exclusive_per_id[pair_idx][0] == dec_type) {
1415           excl_dec_type = mutually_exclusive_per_id[pair_idx][1];
1416         } else if (mutually_exclusive_per_id[pair_idx][1] == dec_type) {
1417           excl_dec_type = mutually_exclusive_per_id[pair_idx][0];
1418         } else {
1419           continue;
1420         }
1421 
1422         const auto excl_k = PerIDKey(excl_dec_type, id);
1423         if (seen_per_id.find(excl_k) != seen_per_id.end()) {
1424           return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1425                  << "ID '" << id << "' decorated with both "
1426                  << vstate.SpvDecorationString(dec_type) << " and "
1427                  << vstate.SpvDecorationString(excl_dec_type)
1428                  << " is not allowed.";
1429         }
1430       }
1431     } else if (spv::Op::OpMemberDecorate == inst.opcode()) {
1432       const auto id = words[1];
1433       const auto member_id = words[2];
1434       const auto dec_type = static_cast<spv::Decoration>(words[3]);
1435       const auto k = PerMemberKey(dec_type, id, member_id);
1436       const auto already_used = !seen_per_member.insert(k).second;
1437       if (already_used && AtMostOncePerMember(dec_type)) {
1438         return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1439                << "ID '" << id << "', member '" << member_id
1440                << "' decorated with " << vstate.SpvDecorationString(dec_type)
1441                << " multiple times is not allowed.";
1442       }
1443       // Verify certain mutually exclusive decorations are not both applied on
1444       // a (ID, member) tuple.
1445       for (uint32_t pair_idx = 0;
1446            pair_idx < num_mutually_exclusive_per_mem_pairs; ++pair_idx) {
1447         spv::Decoration excl_dec_type = spv::Decoration::Max;
1448         if (mutually_exclusive_per_member[pair_idx][0] == dec_type) {
1449           excl_dec_type = mutually_exclusive_per_member[pair_idx][1];
1450         } else if (mutually_exclusive_per_member[pair_idx][1] == dec_type) {
1451           excl_dec_type = mutually_exclusive_per_member[pair_idx][0];
1452         } else {
1453           continue;
1454         }
1455 
1456         const auto excl_k = PerMemberKey(excl_dec_type, id, member_id);
1457         if (seen_per_member.find(excl_k) != seen_per_member.end()) {
1458           return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1459                  << "ID '" << id << "', member '" << member_id
1460                  << "' decorated with both "
1461                  << vstate.SpvDecorationString(dec_type) << " and "
1462                  << vstate.SpvDecorationString(excl_dec_type)
1463                  << " is not allowed.";
1464         }
1465       }
1466     }
1467   }
1468   return SPV_SUCCESS;
1469 }
1470 
CheckVulkanMemoryModelDeprecatedDecorations(ValidationState_t & vstate)1471 spv_result_t CheckVulkanMemoryModelDeprecatedDecorations(
1472     ValidationState_t& vstate) {
1473   if (vstate.memory_model() != spv::MemoryModel::VulkanKHR) return SPV_SUCCESS;
1474 
1475   std::string msg;
1476   std::ostringstream str(msg);
1477   for (const auto& def : vstate.all_definitions()) {
1478     const auto inst = def.second;
1479     const auto id = inst->id();
1480     for (const auto& dec : vstate.id_decorations(id)) {
1481       const auto member = dec.struct_member_index();
1482       if (dec.dec_type() == spv::Decoration::Coherent ||
1483           dec.dec_type() == spv::Decoration::Volatile) {
1484         str << (dec.dec_type() == spv::Decoration::Coherent ? "Coherent"
1485                                                             : "Volatile");
1486         str << " decoration targeting " << vstate.getIdName(id);
1487         if (member != Decoration::kInvalidMember) {
1488           str << " (member index " << member << ")";
1489         }
1490         str << " is banned when using the Vulkan memory model.";
1491         return vstate.diag(SPV_ERROR_INVALID_ID, inst) << str.str();
1492       }
1493     }
1494   }
1495   return SPV_SUCCESS;
1496 }
1497 
1498 // Returns SPV_SUCCESS if validation rules are satisfied for FPRoundingMode
1499 // decorations.  Otherwise emits a diagnostic and returns something other than
1500 // SPV_SUCCESS.
CheckFPRoundingModeForShaders(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1501 spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate,
1502                                            const Instruction& inst,
1503                                            const Decoration& decoration) {
1504   // Validates width-only conversion instruction for floating-point object
1505   // i.e., OpFConvert
1506   if (inst.opcode() != spv::Op::OpFConvert) {
1507     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1508            << "FPRoundingMode decoration can be applied only to a "
1509               "width-only conversion instruction for floating-point "
1510               "object.";
1511   }
1512 
1513   if (spvIsVulkanEnv(vstate.context()->target_env)) {
1514     const auto mode = spv::FPRoundingMode(decoration.params()[0]);
1515     if ((mode != spv::FPRoundingMode::RTE) &&
1516         (mode != spv::FPRoundingMode::RTZ)) {
1517       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1518              << vstate.VkErrorID(4675)
1519              << "In Vulkan, the FPRoundingMode mode must only by RTE or RTZ.";
1520     }
1521   }
1522 
1523   // Validates Object operand of an OpStore
1524   for (const auto& use : inst.uses()) {
1525     const auto store = use.first;
1526     if (store->opcode() == spv::Op::OpFConvert) continue;
1527     if (spvOpcodeIsDebug(store->opcode())) continue;
1528     if (store->IsNonSemantic()) continue;
1529     if (spvOpcodeIsDecoration(store->opcode())) continue;
1530     if (store->opcode() != spv::Op::OpStore) {
1531       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1532              << "FPRoundingMode decoration can be applied only to the "
1533                 "Object operand of an OpStore.";
1534     }
1535 
1536     if (use.second != 2) {
1537       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1538              << "FPRoundingMode decoration can be applied only to the "
1539                 "Object operand of an OpStore.";
1540     }
1541 
1542     const auto ptr_inst = vstate.FindDef(store->GetOperandAs<uint32_t>(0));
1543     const auto ptr_type = vstate.FindDef(ptr_inst->GetOperandAs<uint32_t>(0));
1544 
1545     const auto half_float_id = ptr_type->GetOperandAs<uint32_t>(2);
1546     if (!vstate.IsFloatScalarOrVectorType(half_float_id) ||
1547         vstate.GetBitWidth(half_float_id) != 16) {
1548       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1549              << "FPRoundingMode decoration can be applied only to the "
1550                 "Object operand of an OpStore storing through a pointer "
1551                 "to "
1552                 "a 16-bit floating-point scalar or vector object.";
1553     }
1554 
1555     // Validates storage class of the pointer to the OpStore
1556     const auto storage = ptr_type->GetOperandAs<spv::StorageClass>(1);
1557     if (storage != spv::StorageClass::StorageBuffer &&
1558         storage != spv::StorageClass::Uniform &&
1559         storage != spv::StorageClass::PushConstant &&
1560         storage != spv::StorageClass::Input &&
1561         storage != spv::StorageClass::Output &&
1562         storage != spv::StorageClass::PhysicalStorageBuffer) {
1563       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1564              << "FPRoundingMode decoration can be applied only to the "
1565                 "Object operand of an OpStore in the StorageBuffer, "
1566                 "PhysicalStorageBuffer, Uniform, PushConstant, Input, or "
1567                 "Output Storage Classes.";
1568     }
1569   }
1570   return SPV_SUCCESS;
1571 }
1572 
1573 // Returns SPV_SUCCESS if validation rules are satisfied for the NonWritable
1574 // decoration.  Otherwise emits a diagnostic and returns something other than
1575 // SPV_SUCCESS.  The |inst| parameter is the object being decorated.  This must
1576 // be called after TypePass and AnnotateCheckDecorationsOfBuffers are called.
CheckNonWritableDecoration(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1577 spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate,
1578                                         const Instruction& inst,
1579                                         const Decoration& decoration) {
1580   assert(inst.id() && "Parser ensures the target of the decoration has an ID");
1581 
1582   if (decoration.struct_member_index() == Decoration::kInvalidMember) {
1583     // The target must be a memory object declaration.
1584     // First, it must be a variable or function parameter.
1585     const auto opcode = inst.opcode();
1586     const auto type_id = inst.type_id();
1587     if (opcode != spv::Op::OpVariable &&
1588         opcode != spv::Op::OpFunctionParameter &&
1589         opcode != spv::Op::OpRawAccessChainNV) {
1590       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1591              << "Target of NonWritable decoration must be a memory object "
1592                 "declaration (a variable or a function parameter)";
1593     }
1594     const auto var_storage_class = opcode == spv::Op::OpVariable
1595                                        ? inst.GetOperandAs<spv::StorageClass>(2)
1596                                        : spv::StorageClass::Max;
1597     if ((var_storage_class == spv::StorageClass::Function ||
1598          var_storage_class == spv::StorageClass::Private) &&
1599         vstate.features().nonwritable_var_in_function_or_private) {
1600       // New permitted feature in SPIR-V 1.4.
1601     } else if (
1602         // It may point to a UBO, SSBO, storage image, or raw access chain.
1603         vstate.IsPointerToUniformBlock(type_id) ||
1604         vstate.IsPointerToStorageBuffer(type_id) ||
1605         vstate.IsPointerToStorageImage(type_id) ||
1606         opcode == spv::Op::OpRawAccessChainNV) {
1607     } else {
1608       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1609              << "Target of NonWritable decoration is invalid: must point to a "
1610                 "storage image, uniform block, "
1611              << (vstate.features().nonwritable_var_in_function_or_private
1612                      ? "storage buffer, or variable in Private or Function "
1613                        "storage class"
1614                      : "or storage buffer");
1615     }
1616   }
1617 
1618   return SPV_SUCCESS;
1619 }
1620 
1621 // Returns SPV_SUCCESS if validation rules are satisfied for Uniform or
1622 // UniformId decorations. Otherwise emits a diagnostic and returns something
1623 // other than SPV_SUCCESS. Assumes each decoration on a group has been
1624 // propagated down to the group members.  The |inst| parameter is the object
1625 // being decorated.
CheckUniformDecoration(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1626 spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
1627                                     const Instruction& inst,
1628                                     const Decoration& decoration) {
1629   const char* const dec_name = decoration.dec_type() == spv::Decoration::Uniform
1630                                    ? "Uniform"
1631                                    : "UniformId";
1632 
1633   // Uniform or UniformId must decorate an "object"
1634   //  - has a result ID
1635   //  - is an instantiation of a non-void type.  So it has a type ID, and that
1636   //  type is not void.
1637 
1638   // We already know the result ID is non-zero.
1639 
1640   if (inst.type_id() == 0) {
1641     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1642            << dec_name << " decoration applied to a non-object";
1643   }
1644   if (Instruction* type_inst = vstate.FindDef(inst.type_id())) {
1645     if (type_inst->opcode() == spv::Op::OpTypeVoid) {
1646       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1647              << dec_name << " decoration applied to a value with void type";
1648     }
1649   } else {
1650     // We might never get here because this would have been rejected earlier in
1651     // the flow.
1652     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1653            << dec_name << " decoration applied to an object with invalid type";
1654   }
1655 
1656   // Use of Uniform with OpDecorate is checked elsewhere.
1657   // Use of UniformId with OpDecorateId is checked elsewhere.
1658 
1659   if (decoration.dec_type() == spv::Decoration::UniformId) {
1660     assert(decoration.params().size() == 1 &&
1661            "Grammar ensures UniformId has one parameter");
1662 
1663     // The scope id is an execution scope.
1664     if (auto error =
1665             ValidateExecutionScope(vstate, &inst, decoration.params()[0]))
1666       return error;
1667   }
1668 
1669   return SPV_SUCCESS;
1670 }
1671 
1672 // Returns SPV_SUCCESS if validation rules are satisfied for NoSignedWrap or
1673 // NoUnsignedWrap decorations. Otherwise emits a diagnostic and returns
1674 // something other than SPV_SUCCESS. Assumes each decoration on a group has been
1675 // propagated down to the group members.
CheckIntegerWrapDecoration(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1676 spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate,
1677                                         const Instruction& inst,
1678                                         const Decoration& decoration) {
1679   switch (inst.opcode()) {
1680     case spv::Op::OpIAdd:
1681     case spv::Op::OpISub:
1682     case spv::Op::OpIMul:
1683     case spv::Op::OpShiftLeftLogical:
1684     case spv::Op::OpSNegate:
1685       return SPV_SUCCESS;
1686     case spv::Op::OpExtInst:
1687     case spv::Op::OpExtInstWithForwardRefsKHR:
1688       // TODO(dneto): Only certain extended instructions allow these
1689       // decorations.  For now allow anything.
1690       return SPV_SUCCESS;
1691     default:
1692       break;
1693   }
1694 
1695   return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1696          << (decoration.dec_type() == spv::Decoration::NoSignedWrap
1697                  ? "NoSignedWrap"
1698                  : "NoUnsignedWrap")
1699          << " decoration may not be applied to "
1700          << spvOpcodeString(inst.opcode());
1701 }
1702 
1703 // Returns SPV_SUCCESS if validation rules are satisfied for the Component
1704 // decoration.  Otherwise emits a diagnostic and returns something other than
1705 // SPV_SUCCESS.
CheckComponentDecoration(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1706 spv_result_t CheckComponentDecoration(ValidationState_t& vstate,
1707                                       const Instruction& inst,
1708                                       const Decoration& decoration) {
1709   assert(inst.id() && "Parser ensures the target of the decoration has an ID");
1710   assert(decoration.params().size() == 1 &&
1711          "Grammar ensures Component has one parameter");
1712 
1713   uint32_t type_id;
1714   if (decoration.struct_member_index() == Decoration::kInvalidMember) {
1715     // The target must be a memory object declaration.
1716     const auto opcode = inst.opcode();
1717     if (opcode != spv::Op::OpVariable &&
1718         opcode != spv::Op::OpFunctionParameter) {
1719       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1720              << "Target of Component decoration must be a memory object "
1721                 "declaration (a variable or a function parameter)";
1722     }
1723 
1724     // Only valid for the Input and Output Storage Classes.
1725     const auto storage_class = opcode == spv::Op::OpVariable
1726                                    ? inst.GetOperandAs<spv::StorageClass>(2)
1727                                    : spv::StorageClass::Max;
1728     if (storage_class != spv::StorageClass::Input &&
1729         storage_class != spv::StorageClass::Output &&
1730         storage_class != spv::StorageClass::Max) {
1731       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1732              << "Target of Component decoration is invalid: must point to a "
1733                 "Storage Class of Input(1) or Output(3). Found Storage "
1734                 "Class "
1735              << uint32_t(storage_class);
1736     }
1737 
1738     type_id = inst.type_id();
1739     if (vstate.IsPointerType(type_id)) {
1740       const auto pointer = vstate.FindDef(type_id);
1741       type_id = pointer->GetOperandAs<uint32_t>(2);
1742     }
1743   } else {
1744     if (inst.opcode() != spv::Op::OpTypeStruct) {
1745       return vstate.diag(SPV_ERROR_INVALID_DATA, &inst)
1746              << "Attempted to get underlying data type via member index for "
1747                 "non-struct type.";
1748     }
1749     type_id = inst.word(decoration.struct_member_index() + 2);
1750   }
1751 
1752   if (spvIsVulkanEnv(vstate.context()->target_env)) {
1753     // Strip the array, if present.
1754     if (vstate.GetIdOpcode(type_id) == spv::Op::OpTypeArray) {
1755       type_id = vstate.FindDef(type_id)->word(2u);
1756     }
1757 
1758     if (!vstate.IsIntScalarOrVectorType(type_id) &&
1759         !vstate.IsFloatScalarOrVectorType(type_id)) {
1760       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1761              << vstate.VkErrorID(4924)
1762              << "Component decoration specified for type "
1763              << vstate.getIdName(type_id) << " that is not a scalar or vector";
1764     }
1765 
1766     const auto component = decoration.params()[0];
1767     if (component > 3) {
1768       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1769              << vstate.VkErrorID(4920)
1770              << "Component decoration value must not be greater than 3";
1771     }
1772 
1773     const auto dimension = vstate.GetDimension(type_id);
1774     const auto bit_width = vstate.GetBitWidth(type_id);
1775     if (bit_width == 16 || bit_width == 32) {
1776       const auto sum_component = component + dimension;
1777       if (sum_component > 4) {
1778         return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1779                << vstate.VkErrorID(4921)
1780                << "Sequence of components starting with " << component
1781                << " and ending with " << (sum_component - 1)
1782                << " gets larger than 3";
1783       }
1784     } else if (bit_width == 64) {
1785       if (dimension > 2) {
1786         return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1787                << vstate.VkErrorID(7703)
1788                << "Component decoration only allowed on 64-bit scalar and "
1789                   "2-component vector";
1790       }
1791       if (component == 1 || component == 3) {
1792         return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1793                << vstate.VkErrorID(4923)
1794                << "Component decoration value must not be 1 or 3 for 64-bit "
1795                   "data types";
1796       }
1797       // 64-bit is double per component dimension
1798       const auto sum_component = component + (2 * dimension);
1799       if (sum_component > 4) {
1800         return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1801                << vstate.VkErrorID(4922)
1802                << "Sequence of components starting with " << component
1803                << " and ending with " << (sum_component - 1)
1804                << " gets larger than 3";
1805       }
1806     }
1807   }
1808 
1809   return SPV_SUCCESS;
1810 }
1811 
1812 // Returns SPV_SUCCESS if validation rules are satisfied for the Block
1813 // decoration.  Otherwise emits a diagnostic and returns something other than
1814 // SPV_SUCCESS.
CheckBlockDecoration(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1815 spv_result_t CheckBlockDecoration(ValidationState_t& vstate,
1816                                   const Instruction& inst,
1817                                   const Decoration& decoration) {
1818   assert(inst.id() && "Parser ensures the target of the decoration has an ID");
1819   if (inst.opcode() != spv::Op::OpTypeStruct) {
1820     const char* const dec_name = decoration.dec_type() == spv::Decoration::Block
1821                                      ? "Block"
1822                                      : "BufferBlock";
1823     return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1824            << dec_name << " decoration on a non-struct type.";
1825   }
1826   return SPV_SUCCESS;
1827 }
1828 
CheckLocationDecoration(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1829 spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
1830                                      const Instruction& inst,
1831                                      const Decoration& decoration) {
1832   if (inst.opcode() == spv::Op::OpVariable) return SPV_SUCCESS;
1833 
1834   if (decoration.struct_member_index() != Decoration::kInvalidMember &&
1835       inst.opcode() == spv::Op::OpTypeStruct) {
1836     return SPV_SUCCESS;
1837   }
1838 
1839   return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1840          << "Location decoration can only be applied to a variable or member "
1841             "of a structure type";
1842 }
1843 
CheckRelaxPrecisionDecoration(ValidationState_t & vstate,const Instruction & inst,const Decoration & decoration)1844 spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate,
1845                                            const Instruction& inst,
1846                                            const Decoration& decoration) {
1847   // This is not the most precise check, but the rules for RelaxPrecision are
1848   // very general, and it will be difficult to implement precisely.  For now,
1849   // I will only check for the cases that cause problems for the optimizer.
1850   if (!spvOpcodeGeneratesType(inst.opcode())) {
1851     return SPV_SUCCESS;
1852   }
1853 
1854   if (decoration.struct_member_index() != Decoration::kInvalidMember &&
1855       inst.opcode() == spv::Op::OpTypeStruct) {
1856     return SPV_SUCCESS;
1857   }
1858   return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1859          << "RelaxPrecision decoration cannot be applied to a type";
1860 }
1861 
1862 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
1863   {                                             \
1864     spv_result_t e##LINE = (X);                 \
1865     if (e##LINE != SPV_SUCCESS) return e##LINE; \
1866   } static_assert(true, "require extra semicolon")
1867 #define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__)
1868 
1869 // Check rules for decorations where we start from the decoration rather
1870 // than the decorated object.  Assumes each decoration on a group have been
1871 // propagated down to the group members.
CheckDecorationsFromDecoration(ValidationState_t & vstate)1872 spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
1873   // Some rules are only checked for shaders.
1874   const bool is_shader = vstate.HasCapability(spv::Capability::Shader);
1875 
1876   for (const auto& kv : vstate.id_decorations()) {
1877     const uint32_t id = kv.first;
1878     const auto& decorations = kv.second;
1879     if (decorations.empty()) continue;
1880 
1881     const Instruction* inst = vstate.FindDef(id);
1882     assert(inst);
1883 
1884     // We assume the decorations applied to a decoration group have already
1885     // been propagated down to the group members.
1886     if (inst->opcode() == spv::Op::OpDecorationGroup) continue;
1887 
1888     for (const auto& decoration : decorations) {
1889       switch (decoration.dec_type()) {
1890         case spv::Decoration::Component:
1891           PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration));
1892           break;
1893         case spv::Decoration::FPRoundingMode:
1894           if (is_shader)
1895             PASS_OR_BAIL(
1896                 CheckFPRoundingModeForShaders(vstate, *inst, decoration));
1897           break;
1898         case spv::Decoration::NonWritable:
1899           PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration));
1900           break;
1901         case spv::Decoration::Uniform:
1902         case spv::Decoration::UniformId:
1903           PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration));
1904           break;
1905         case spv::Decoration::NoSignedWrap:
1906         case spv::Decoration::NoUnsignedWrap:
1907           PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration));
1908           break;
1909         case spv::Decoration::Block:
1910         case spv::Decoration::BufferBlock:
1911           PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
1912           break;
1913         case spv::Decoration::Location:
1914           PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
1915           break;
1916         case spv::Decoration::RelaxedPrecision:
1917           PASS_OR_BAIL(
1918               CheckRelaxPrecisionDecoration(vstate, *inst, decoration));
1919           break;
1920         default:
1921           break;
1922       }
1923     }
1924   }
1925   return SPV_SUCCESS;
1926 }
1927 
1928 }  // namespace
1929 
ValidateDecorations(ValidationState_t & vstate)1930 spv_result_t ValidateDecorations(ValidationState_t& vstate) {
1931   if (auto error = CheckImportedVariableInitialization(vstate)) return error;
1932   if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error;
1933   if (auto error = CheckDecorationsOfBuffers(vstate)) return error;
1934   if (auto error = CheckDecorationsCompatibility(vstate)) return error;
1935   if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error;
1936   if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
1937     return error;
1938   if (auto error = CheckDecorationsFromDecoration(vstate)) return error;
1939   return SPV_SUCCESS;
1940 }
1941 
1942 }  // namespace val
1943 }  // namespace spvtools
1944