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