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