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/val/validate_scopes.h"
30#include "source/val/validation_state.h"
31
32namespace spvtools {
33namespace val {
34namespace {
35
36// Distinguish between row and column major matrix layouts.
37enum MatrixLayout { kRowMajor, kColumnMajor };
38
39// A functor for hashing a pair of integers.
40struct PairHash {
41 std::size_t operator()(const std::pair<uint32_t, uint32_t> pair) const {
42 const uint32_t a = pair.first;
43 const uint32_t b = pair.second;
44 const uint32_t rotated_b = (b >> 2) | ((b & 3) << 30);
45 return a ^ rotated_b;
46 }
47};
48
49// A functor for hashing decoration types.
50struct SpvDecorationHash {
51 std::size_t operator()(SpvDecoration dec) const {
52 return static_cast<std::size_t>(dec);
53 }
54};
55
56// Struct member layout attributes that are inherited through arrays.
57struct LayoutConstraints {
58 explicit LayoutConstraints(
59 MatrixLayout the_majorness = MatrixLayout::kColumnMajor,
60 uint32_t stride = 0)
61 : majorness(the_majorness), matrix_stride(stride) {}
62 MatrixLayout majorness;
63 uint32_t matrix_stride;
64};
65
66// A type for mapping (struct id, member id) to layout constraints.
67using MemberConstraints = std::unordered_map<std::pair<uint32_t, uint32_t>,
68 LayoutConstraints, PairHash>;
69
70// Returns the array stride of the given array type.
71uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) {
72 for (auto& decoration : vstate.id_decorations(array_id)) {
73 if (SpvDecorationArrayStride == decoration.dec_type()) {
74 return decoration.params()[0];
75 }
76 }
77 return 0;
78}
79
80// Returns true if the given variable has a BuiltIn decoration.
81bool isBuiltInVar(uint32_t var_id, ValidationState_t& vstate) {
82 const auto& decorations = vstate.id_decorations(var_id);
83 return std::any_of(
84 decorations.begin(), decorations.end(),
85 [](const Decoration& d) { return SpvDecorationBuiltIn == d.dec_type(); });
86}
87
88// Returns true if the given structure type has any members with BuiltIn
89// decoration.
90bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) {
91 const auto& decorations = vstate.id_decorations(struct_id);
92 return std::any_of(
93 decorations.begin(), decorations.end(), [](const Decoration& d) {
94 return SpvDecorationBuiltIn == d.dec_type() &&
95 Decoration::kInvalidMember != d.struct_member_index();
96 });
97}
98
99// Returns true if the given ID has the Import LinkageAttributes decoration.
100bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) {
101 const auto& decorations = vstate.id_decorations(id);
102 return std::any_of(decorations.begin(), decorations.end(),
103 [](const Decoration& d) {
104 return SpvDecorationLinkageAttributes == d.dec_type() &&
105 d.params().size() >= 2u &&
106 d.params().back() == SpvLinkageTypeImport;
107 });
108}
109
110// Returns a vector of all members of a structure.
111std::vector<uint32_t> getStructMembers(uint32_t struct_id,
112 ValidationState_t& vstate) {
113 const auto inst = vstate.FindDef(struct_id);
114 return std::vector<uint32_t>(inst->words().begin() + 2, inst->words().end());
115}
116
117// Returns a vector of all members of a structure that have specific type.
118std::vector<uint32_t> getStructMembers(uint32_t struct_id, SpvOp type,
119 ValidationState_t& vstate) {
120 std::vector<uint32_t> members;
121 for (auto id : getStructMembers(struct_id, vstate)) {
122 if (type == vstate.FindDef(id)->opcode()) {
123 members.push_back(id);
124 }
125 }
126 return members;
127}
128
129// Returns whether the given structure is missing Offset decoration for any
130// member. Handles also nested structures.
131bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
132 std::vector<bool> hasOffset(getStructMembers(struct_id, vstate).size(),
133 false);
134 // Check offsets of member decorations
135 for (auto& decoration : vstate.id_decorations(struct_id)) {
136 if (SpvDecorationOffset == decoration.dec_type() &&
137 Decoration::kInvalidMember != decoration.struct_member_index()) {
138 hasOffset[decoration.struct_member_index()] = true;
139 }
140 }
141 // Check also nested structures
142 bool nestedStructsMissingOffset = false;
143 for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
144 if (isMissingOffsetInStruct(id, vstate)) {
145 nestedStructsMissingOffset = true;
146 break;
147 }
148 }
149 return nestedStructsMissingOffset ||
150 !std::all_of(hasOffset.begin(), hasOffset.end(),
151 [](const bool b) { return b; });
152}
153
154// Rounds x up to the next alignment. Assumes alignment is a power of two.
155uint32_t align(uint32_t x, uint32_t alignment) {
156 return (x + alignment - 1) & ~(alignment - 1);
157}
158
159// Returns base alignment of struct member. If |roundUp| is true, also
160// ensure that structs and arrays are aligned at least to a multiple of 16
161// bytes.
162uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
163 const LayoutConstraints& inherited,
164 MemberConstraints& constraints,
165 ValidationState_t& vstate) {
166 const auto inst = vstate.FindDef(member_id);
167 const auto& words = inst->words();
168 // Minimal alignment is byte-aligned.
169 uint32_t baseAlignment = 1;
170 switch (inst->opcode()) {
171 case SpvOpTypeInt:
172 case SpvOpTypeFloat:
173 baseAlignment = words[2] / 8;
174 break;
175 case SpvOpTypeVector: {
176 const auto componentId = words[2];
177 const auto numComponents = words[3];
178 const auto componentAlignment = getBaseAlignment(
179 componentId, roundUp, inherited, constraints, vstate);
180 baseAlignment =
181 componentAlignment * (numComponents == 3 ? 4 : numComponents);
182 break;
183 }
184 case SpvOpTypeMatrix: {
185 const auto column_type = words[2];
186 if (inherited.majorness == kColumnMajor) {
187 baseAlignment = getBaseAlignment(column_type, roundUp, inherited,
188 constraints, vstate);
189 } else {
190 // A row-major matrix of C columns has a base alignment equal to the
191 // base alignment of a vector of C matrix components.
192 const auto num_columns = words[3];
193 const auto component_inst = vstate.FindDef(column_type);
194 const auto component_id = component_inst->words()[2];
195 const auto componentAlignment = getBaseAlignment(
196 component_id, roundUp, inherited, constraints, vstate);
197 baseAlignment =
198 componentAlignment * (num_columns == 3 ? 4 : num_columns);
199 }
200 } break;
201 case SpvOpTypeArray:
202 case SpvOpTypeRuntimeArray:
203 baseAlignment =
204 getBaseAlignment(words[2], roundUp, inherited, constraints, vstate);
205 if (roundUp) baseAlignment = align(baseAlignment, 16u);
206 break;
207 case SpvOpTypeStruct: {
208 const auto members = getStructMembers(member_id, vstate);
209 for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
210 memberIdx < numMembers; ++memberIdx) {
211 const auto id = members[memberIdx];
212 const auto& constraint =
213 constraints[std::make_pair(member_id, memberIdx)];
214 baseAlignment = std::max(
215 baseAlignment,
216 getBaseAlignment(id, roundUp, constraint, constraints, vstate));
217 }
218 if (roundUp) baseAlignment = align(baseAlignment, 16u);
219 break;
220 }
221 case SpvOpTypePointer:
222 baseAlignment = vstate.pointer_size_and_alignment();
223 break;
224 default:
225 assert(0);
226 break;
227 }
228
229 return baseAlignment;
230}
231
232// Returns scalar alignment of a type.
233uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
234 const auto inst = vstate.FindDef(type_id);
235 const auto& words = inst->words();
236 switch (inst->opcode()) {
237 case SpvOpTypeInt:
238 case SpvOpTypeFloat:
239 return words[2] / 8;
240 case SpvOpTypeVector:
241 case SpvOpTypeMatrix:
242 case SpvOpTypeArray:
243 case SpvOpTypeRuntimeArray: {
244 const auto compositeMemberTypeId = words[2];
245 return getScalarAlignment(compositeMemberTypeId, vstate);
246 }
247 case SpvOpTypeStruct: {
248 const auto members = getStructMembers(type_id, vstate);
249 uint32_t max_member_alignment = 1;
250 for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
251 memberIdx < numMembers; ++memberIdx) {
252 const auto id = members[memberIdx];
253 uint32_t member_alignment = getScalarAlignment(id, vstate);
254 if (member_alignment > max_member_alignment) {
255 max_member_alignment = member_alignment;
256 }
257 }
258 return max_member_alignment;
259 } break;
260 case SpvOpTypePointer:
261 return vstate.pointer_size_and_alignment();
262 default:
263 assert(0);
264 break;
265 }
266
267 return 1;
268}
269
270// Returns size of a struct member. Doesn't include padding at the end of struct
271// or array. Assumes that in the struct case, all members have offsets.
272uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
273 MemberConstraints& constraints, ValidationState_t& vstate) {
274 const auto inst = vstate.FindDef(member_id);
275 const auto& words = inst->words();
276 switch (inst->opcode()) {
277 case SpvOpTypeInt:
278 case SpvOpTypeFloat:
279 return words[2] / 8;
280 case SpvOpTypeVector: {
281 const auto componentId = words[2];
282 const auto numComponents = words[3];
283 const auto componentSize =
284 getSize(componentId, inherited, constraints, vstate);
285 const auto size = componentSize * numComponents;
286 return size;
287 }
288 case SpvOpTypeArray: {
289 const auto sizeInst = vstate.FindDef(words[3]);
290 if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0;
291 assert(SpvOpConstant == sizeInst->opcode());
292 const uint32_t num_elem = sizeInst->words()[3];
293 const uint32_t elem_type = words[2];
294 const uint32_t elem_size =
295 getSize(elem_type, inherited, constraints, vstate);
296 // Account for gaps due to alignments in the first N-1 elements,
297 // then add the size of the last element.
298 const auto size =
299 (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size;
300 return size;
301 }
302 case SpvOpTypeRuntimeArray:
303 return 0;
304 case SpvOpTypeMatrix: {
305 const auto num_columns = words[3];
306 if (inherited.majorness == kColumnMajor) {
307 return num_columns * inherited.matrix_stride;
308 } else {
309 // Row major case.
310 const auto column_type = words[2];
311 const auto component_inst = vstate.FindDef(column_type);
312 const auto num_rows = component_inst->words()[3];
313 const auto scalar_elem_type = component_inst->words()[2];
314 const uint32_t scalar_elem_size =
315 getSize(scalar_elem_type, inherited, constraints, vstate);
316 return (num_rows - 1) * inherited.matrix_stride +
317 num_columns * scalar_elem_size;
318 }
319 }
320 case SpvOpTypeStruct: {
321 const auto& members = getStructMembers(member_id, vstate);
322 if (members.empty()) return 0;
323 const auto lastIdx = uint32_t(members.size() - 1);
324 const auto& lastMember = members.back();
325 uint32_t offset = 0xffffffff;
326 // Find the offset of the last element and add the size.
327 for (auto& decoration : vstate.id_decorations(member_id)) {
328 if (SpvDecorationOffset == decoration.dec_type() &&
329 decoration.struct_member_index() == (int)lastIdx) {
330 offset = decoration.params()[0];
331 }
332 }
333 // This check depends on the fact that all members have offsets. This
334 // has been checked earlier in the flow.
335 assert(offset != 0xffffffff);
336 const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
337 return offset + getSize(lastMember, constraint, constraints, vstate);
338 }
339 case SpvOpTypePointer:
340 return vstate.pointer_size_and_alignment();
341 default:
342 assert(0);
343 return 0;
344 }
345}
346
347// A member is defined to improperly straddle if either of the following are
348// true:
349// - It is a vector with total size less than or equal to 16 bytes, and has
350// Offset decorations placing its first byte at F and its last byte at L, where
351// floor(F / 16) != floor(L / 16).
352// - It is a vector with total size greater than 16 bytes and has its Offset
353// decorations placing its first byte at a non-integer multiple of 16.
354bool hasImproperStraddle(uint32_t id, uint32_t offset,
355 const LayoutConstraints& inherited,
356 MemberConstraints& constraints,
357 ValidationState_t& vstate) {
358 const auto size = getSize(id, inherited, constraints, vstate);
359 const auto F = offset;
360 const auto L = offset + size - 1;
361 if (size <= 16) {
362 if ((F >> 4) != (L >> 4)) return true;
363 } else {
364 if (F % 16 != 0) return true;
365 }
366 return false;
367}
368
369// Returns true if |offset| satsifies an alignment to |alignment|. In the case
370// of |alignment| of zero, the |offset| must also be zero.
371bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
372 if (alignment == 0) return offset == 0;
373 return 0 == (offset % alignment);
374}
375
376// Returns SPV_SUCCESS if the given struct satisfies standard layout rules for
377// Block or BufferBlocks in Vulkan. Otherwise emits a diagnostic and returns
378// something other than SPV_SUCCESS. Matrices inherit the specified column
379// or row major-ness.
380spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
381 const char* decoration_str, bool blockRules,
382 uint32_t incoming_offset,
383 MemberConstraints& constraints,
384 ValidationState_t& vstate) {
385 if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
386
387 // blockRules are the same as bufferBlock rules if the uniform buffer
388 // standard layout extension is being used.
389 if (vstate.options()->uniform_buffer_standard_layout) blockRules = false;
390
391 // Relaxed layout and scalar layout can both be in effect at the same time.
392 // For example, relaxed layout is implied by Vulkan 1.1. But scalar layout
393 // is more permissive than relaxed layout.
394 const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
395 const bool scalar_block_layout = vstate.options()->scalar_block_layout;
396
397 auto fail = [&vstate, struct_id, storage_class_str, decoration_str,
398 blockRules, relaxed_block_layout,
399 scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
400 DiagnosticStream ds =
401 std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
402 << "Structure id " << struct_id << " decorated as "
403 << decoration_str << " for variable in " << storage_class_str
404 << " storage class must follow "
405 << (scalar_block_layout
406 ? "scalar "
407 : (relaxed_block_layout ? "relaxed " : "standard "))
408 << (blockRules ? "uniform buffer" : "storage buffer")
409 << " layout rules: member " << member_idx << " ");
410 return ds;
411 };
412
413 const auto& members = getStructMembers(struct_id, vstate);
414
415 // To check for member overlaps, we want to traverse the members in
416 // offset order.
417 struct MemberOffsetPair {
418 uint32_t member;
419 uint32_t offset;
420 };
421 std::vector<MemberOffsetPair> member_offsets;
422 member_offsets.reserve(members.size());
423 for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
424 memberIdx < numMembers; memberIdx++) {
425 uint32_t offset = 0xffffffff;
426 for (auto& decoration : vstate.id_decorations(struct_id)) {
427 if (decoration.struct_member_index() == (int)memberIdx) {
428 switch (decoration.dec_type()) {
429 case SpvDecorationOffset:
430 offset = decoration.params()[0];
431 break;
432 default:
433 break;
434 }
435 }
436 }
437 member_offsets.push_back(
438 MemberOffsetPair{memberIdx, incoming_offset + offset});
439 }
440 std::stable_sort(
441 member_offsets.begin(), member_offsets.end(),
442 [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) {
443 return lhs.offset < rhs.offset;
444 });
445
446 // Now scan from lowest offest to highest offset.
447 uint32_t nextValidOffset = 0;
448 for (size_t ordered_member_idx = 0;
449 ordered_member_idx < member_offsets.size(); ordered_member_idx++) {
450 const auto& member_offset = member_offsets[ordered_member_idx];
451 const auto memberIdx = member_offset.member;
452 const auto offset = member_offset.offset;
453 auto id = members[member_offset.member];
454 const LayoutConstraints& constraint =
455 constraints[std::make_pair(struct_id, uint32_t(memberIdx))];
456 // Scalar layout takes precedence because it's more permissive, and implying
457 // an alignment that divides evenly into the alignment that would otherwise
458 // be used.
459 const auto alignment =
460 scalar_block_layout
461 ? getScalarAlignment(id, vstate)
462 : getBaseAlignment(id, blockRules, constraint, constraints, vstate);
463 const auto inst = vstate.FindDef(id);
464 const auto opcode = inst->opcode();
465 const auto size = getSize(id, constraint, constraints, vstate);
466 // Check offset.
467 if (offset == 0xffffffff)
468 return fail(memberIdx) << "is missing an Offset decoration";
469 if (!scalar_block_layout && relaxed_block_layout &&
470 opcode == SpvOpTypeVector) {
471 // In relaxed block layout, the vector offset must be aligned to the
472 // vector's scalar element type.
473 const auto componentId = inst->words()[2];
474 const auto scalar_alignment = getScalarAlignment(componentId, vstate);
475 if (!IsAlignedTo(offset, scalar_alignment)) {
476 return fail(memberIdx)
477 << "at offset " << offset
478 << " is not aligned to scalar element size " << scalar_alignment;
479 }
480 } else {
481 // Without relaxed block layout, the offset must be divisible by the
482 // alignment requirement.
483 if (!IsAlignedTo(offset, alignment)) {
484 return fail(memberIdx)
485 << "at offset " << offset << " is not aligned to " << alignment;
486 }
487 }
488 if (offset < nextValidOffset)
489 return fail(memberIdx) << "at offset " << offset
490 << " overlaps previous member ending at offset "
491 << nextValidOffset - 1;
492 if (!scalar_block_layout && relaxed_block_layout) {
493 // Check improper straddle of vectors.
494 if (SpvOpTypeVector == opcode &&
495 hasImproperStraddle(id, offset, constraint, constraints, vstate))
496 return fail(memberIdx)
497 << "is an improperly straddling vector at offset " << offset;
498 }
499 // Check struct members recursively.
500 spv_result_t recursive_status = SPV_SUCCESS;
501 if (SpvOpTypeStruct == opcode &&
502 SPV_SUCCESS != (recursive_status = checkLayout(
503 id, storage_class_str, decoration_str, blockRules,
504 offset, constraints, vstate)))
505 return recursive_status;
506 // Check matrix stride.
507 if (SpvOpTypeMatrix == opcode) {
508 for (auto& decoration : vstate.id_decorations(id)) {
509 if (SpvDecorationMatrixStride == decoration.dec_type() &&
510 !IsAlignedTo(decoration.params()[0], alignment))
511 return fail(memberIdx)
512 << "is a matrix with stride " << decoration.params()[0]
513 << " not satisfying alignment to " << alignment;
514 }
515 }
516
517 // Check arrays and runtime arrays recursively.
518 auto array_inst = inst;
519 auto array_alignment = alignment;
520 while (array_inst->opcode() == SpvOpTypeArray ||
521 array_inst->opcode() == SpvOpTypeRuntimeArray) {
522 const auto typeId = array_inst->word(2);
523 const auto element_inst = vstate.FindDef(typeId);
524 // Check array stride.
525 uint32_t array_stride = 0;
526 for (auto& decoration : vstate.id_decorations(array_inst->id())) {
527 if (SpvDecorationArrayStride == decoration.dec_type()) {
528 array_stride = decoration.params()[0];
529 if (array_stride == 0) {
530 return fail(memberIdx) << "contains an array with stride 0";
531 }
532 if (!IsAlignedTo(array_stride, array_alignment))
533 return fail(memberIdx)
534 << "contains an array with stride " << decoration.params()[0]
535 << " not satisfying alignment to " << alignment;
536 }
537 }
538
539 bool is_int32 = false;
540 bool is_const = false;
541 uint32_t num_elements = 0;
542 if (array_inst->opcode() == SpvOpTypeArray) {
543 std::tie(is_int32, is_const, num_elements) =
544 vstate.EvalInt32IfConst(array_inst->word(3));
545 }
546 num_elements = std::max(1u, num_elements);
547 // Check each element recursively if it is a struct. There is a
548 // limitation to this check if the array size is a spec constant or is a
549 // runtime array then we will only check a single element. This means
550 // some improper straddles might be missed.
551 for (uint32_t i = 0; i < num_elements; ++i) {
552 uint32_t next_offset = i * array_stride + offset;
553 if (SpvOpTypeStruct == element_inst->opcode() &&
554 SPV_SUCCESS != (recursive_status = checkLayout(
555 typeId, storage_class_str, decoration_str,
556 blockRules, next_offset, constraints, vstate)))
557 return recursive_status;
558 // If offsets accumulate up to a 16-byte multiple stop checking since
559 // it will just repeat.
560 if (i > 0 && (next_offset % 16 == 0)) break;
561 }
562
563 // Proceed to the element in case it is an array.
564 array_inst = element_inst;
565 array_alignment = scalar_block_layout
566 ? getScalarAlignment(array_inst->id(), vstate)
567 : getBaseAlignment(array_inst->id(), blockRules,
568 constraint, constraints, vstate);
569
570 const auto element_size =
571 getSize(element_inst->id(), constraint, constraints, vstate);
572 if (element_size > array_stride) {
573 return fail(memberIdx)
574 << "contains an array with stride " << array_stride
575 << ", but with an element size of " << element_size;
576 }
577 }
578 nextValidOffset = offset + size;
579 if (!scalar_block_layout && blockRules &&
580 (SpvOpTypeArray == opcode || SpvOpTypeStruct == opcode)) {
581 // Uniform block rules don't permit anything in the padding of a struct
582 // or array.
583 nextValidOffset = align(nextValidOffset, alignment);
584 }
585 }
586 return SPV_SUCCESS;
587}
588
589// Returns true if variable or structure id has given decoration. Handles also
590// nested structures.
591bool hasDecoration(uint32_t id, SpvDecoration decoration,
592 ValidationState_t& vstate) {
593 for (auto& dec : vstate.id_decorations(id)) {
594 if (decoration == dec.dec_type()) return true;
595 }
596 if (SpvOpTypeStruct != vstate.FindDef(id)->opcode()) {
597 return false;
598 }
599 for (auto member_id : getStructMembers(id, SpvOpTypeStruct, vstate)) {
600 if (hasDecoration(member_id, decoration, vstate)) {
601 return true;
602 }
603 }
604 return false;
605}
606
607// Returns true if all ids of given type have a specified decoration.
608bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration,
609 SpvOp type, ValidationState_t& vstate) {
610 const auto& members = getStructMembers(struct_id, vstate);
611 for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
612 const auto id = members[memberIdx];
613 if (type != vstate.FindDef(id)->opcode()) continue;
614 bool found = false;
615 for (auto& dec : vstate.id_decorations(id)) {
616 if (decoration == dec.dec_type()) found = true;
617 }
618 for (auto& dec : vstate.id_decorations(struct_id)) {
619 if (decoration == dec.dec_type() &&
620 (int)memberIdx == dec.struct_member_index()) {
621 found = true;
622 }
623 }
624 if (!found) {
625 return false;
626 }
627 }
628 for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
629 if (!checkForRequiredDecoration(id, decoration, type, vstate)) {
630 return false;
631 }
632 }
633 return true;
634}
635
636spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) {
637 for (const auto& function : vstate.functions()) {
638 if (function.block_count() == 0u) {
639 // A function declaration (an OpFunction with no basic blocks), must have
640 // a Linkage Attributes Decoration with the Import Linkage Type.
641 if (!hasImportLinkageAttribute(function.id(), vstate)) {
642 return vstate.diag(SPV_ERROR_INVALID_BINARY,
643 vstate.FindDef(function.id()))
644 << "Function declaration (id " << function.id()
645 << ") must have a LinkageAttributes decoration with the Import "
646 "Linkage type.";
647 }
648 } else {
649 if (hasImportLinkageAttribute(function.id(), vstate)) {
650 return vstate.diag(SPV_ERROR_INVALID_BINARY,
651 vstate.FindDef(function.id()))
652 << "Function definition (id " << function.id()
653 << ") may not be decorated with Import Linkage type.";
654 }
655 }
656 }
657 return SPV_SUCCESS;
658}
659
660// Checks whether an imported variable is initialized by this module.
661spv_result_t CheckImportedVariableInitialization(ValidationState_t& vstate) {
662 // According the SPIR-V Spec 2.16.1, it is illegal to initialize an imported
663 // variable. This means that a module-scope OpVariable with initialization
664 // value cannot be marked with the Import Linkage Type (import type id = 1).
665 for (auto global_var_id : vstate.global_vars()) {
666 // Initializer <id> is an optional argument for OpVariable. If initializer
667 // <id> is present, the instruction will have 5 words.
668 auto variable_instr = vstate.FindDef(global_var_id);
669 if (variable_instr->words().size() == 5u &&
670 hasImportLinkageAttribute(global_var_id, vstate)) {
671 return vstate.diag(SPV_ERROR_INVALID_ID, variable_instr)
672 << "A module-scope OpVariable with initialization value "
673 "cannot be marked with the Import Linkage Type.";
674 }
675 }
676 return SPV_SUCCESS;
677}
678
679// Checks whether a builtin variable is valid.
680spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) {
681 const auto& decorations = vstate.id_decorations(var_id);
682 for (const auto& d : decorations) {
683 if (spvIsVulkanEnv(vstate.context()->target_env)) {
684 if (d.dec_type() == SpvDecorationLocation ||
685 d.dec_type() == SpvDecorationComponent) {
686 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
687 << "A BuiltIn variable (id " << var_id
688 << ") cannot have any Location or Component decorations";
689 }
690 }
691 }
692 return SPV_SUCCESS;
693}
694
695// Checks whether proper decorations have been appied to the entry points.
696spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
697 for (uint32_t entry_point : vstate.entry_points()) {
698 const auto& descs = vstate.entry_point_descriptions(entry_point);
699 int num_builtin_inputs = 0;
700 int num_builtin_outputs = 0;
701 for (const auto& desc : descs) {
702 std::unordered_set<Instruction*> seen_vars;
703 for (auto interface : desc.interfaces) {
704 Instruction* var_instr = vstate.FindDef(interface);
705 if (!var_instr || SpvOpVariable != var_instr->opcode()) {
706 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
707 << "Interfaces passed to OpEntryPoint must be of type "
708 "OpTypeVariable. Found Op"
709 << spvOpcodeString(var_instr->opcode()) << ".";
710 }
711 const SpvStorageClass storage_class =
712 var_instr->GetOperandAs<SpvStorageClass>(2);
713 if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
714 // Starting in 1.4, OpEntryPoint must list all global variables
715 // it statically uses and those interfaces must be unique.
716 if (storage_class == SpvStorageClassFunction) {
717 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
718 << "OpEntryPoint interfaces should only list global "
719 "variables";
720 }
721
722 if (!seen_vars.insert(var_instr).second) {
723 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
724 << "Non-unique OpEntryPoint interface "
725 << vstate.getIdName(interface) << " is disallowed";
726 }
727 } else {
728 if (storage_class != SpvStorageClassInput &&
729 storage_class != SpvStorageClassOutput) {
730 return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
731 << "OpEntryPoint interfaces must be OpVariables with "
732 "Storage Class of Input(1) or Output(3). Found Storage "
733 "Class "
734 << storage_class << " for Entry Point id " << entry_point
735 << ".";
736 }
737 }
738
739 const uint32_t ptr_id = var_instr->word(1);
740 Instruction* ptr_instr = vstate.FindDef(ptr_id);
741 // It is guaranteed (by validator ID checks) that ptr_instr is
742 // OpTypePointer. Word 3 of this instruction is the type being pointed
743 // to.
744 const uint32_t type_id = ptr_instr->word(3);
745 Instruction* type_instr = vstate.FindDef(type_id);
746 if (type_instr && SpvOpTypeStruct == type_instr->opcode() &&
747 isBuiltInStruct(type_id, vstate)) {
748 if (storage_class == SpvStorageClassInput) ++num_builtin_inputs;
749 if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs;
750 if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break;
751 if (auto error = CheckBuiltInVariable(interface, vstate))
752 return error;
753 } else if (isBuiltInVar(interface, vstate)) {
754 if (auto error = CheckBuiltInVariable(interface, vstate))
755 return error;
756 }
757 }
758 if (num_builtin_inputs > 1 || num_builtin_outputs > 1) {
759 return vstate.diag(SPV_ERROR_INVALID_BINARY,
760 vstate.FindDef(entry_point))
761 << "There must be at most one object per Storage Class that can "
762 "contain a structure type containing members decorated with "
763 "BuiltIn, consumed per entry-point. Entry Point id "
764 << entry_point << " does not meet this requirement.";
765 }
766 // The LinkageAttributes Decoration cannot be applied to functions
767 // targeted by an OpEntryPoint instruction
768 for (auto& decoration : vstate.id_decorations(entry_point)) {
769 if (SpvDecorationLinkageAttributes == decoration.dec_type()) {
770 const char* linkage_name =
771 reinterpret_cast<const char*>(&decoration.params()[0]);
772 return vstate.diag(SPV_ERROR_INVALID_BINARY,
773 vstate.FindDef(entry_point))
774 << "The LinkageAttributes Decoration (Linkage name: "
775 << linkage_name << ") cannot be applied to function id "
776 << entry_point
777 << " because it is targeted by an OpEntryPoint instruction.";
778 }
779 }
780 }
781 }
782 return SPV_SUCCESS;
783}
784
785// Load |constraints| with all the member constraints for structs contained
786// within the given array type.
787void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
788 uint32_t array_id,
789 const LayoutConstraints& inherited,
790 ValidationState_t& vstate);
791
792// Load |constraints| with all the member constraints for the given struct,
793// and all its contained structs.
794void ComputeMemberConstraintsForStruct(MemberConstraints* constraints,
795 uint32_t struct_id,
796 const LayoutConstraints& inherited,
797 ValidationState_t& vstate) {
798 assert(constraints);
799 const auto& members = getStructMembers(struct_id, vstate);
800 for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
801 memberIdx < numMembers; memberIdx++) {
802 LayoutConstraints& constraint =
803 (*constraints)[std::make_pair(struct_id, memberIdx)];
804 constraint = inherited;
805 for (auto& decoration : vstate.id_decorations(struct_id)) {
806 if (decoration.struct_member_index() == (int)memberIdx) {
807 switch (decoration.dec_type()) {
808 case SpvDecorationRowMajor:
809 constraint.majorness = kRowMajor;
810 break;
811 case SpvDecorationColMajor:
812 constraint.majorness = kColumnMajor;
813 break;
814 case SpvDecorationMatrixStride:
815 constraint.matrix_stride = decoration.params()[0];
816 break;
817 default:
818 break;
819 }
820 }
821 }
822
823 // Now recurse
824 auto member_type_id = members[memberIdx];
825 const auto member_type_inst = vstate.FindDef(member_type_id);
826 const auto opcode = member_type_inst->opcode();
827 switch (opcode) {
828 case SpvOpTypeArray:
829 case SpvOpTypeRuntimeArray:
830 ComputeMemberConstraintsForArray(constraints, member_type_id, inherited,
831 vstate);
832 break;
833 case SpvOpTypeStruct:
834 ComputeMemberConstraintsForStruct(constraints, member_type_id,
835 inherited, vstate);
836 break;
837 default:
838 break;
839 }
840 }
841}
842
843void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
844 uint32_t array_id,
845 const LayoutConstraints& inherited,
846 ValidationState_t& vstate) {
847 assert(constraints);
848 auto elem_type_id = vstate.FindDef(array_id)->words()[2];
849 const auto elem_type_inst = vstate.FindDef(elem_type_id);
850 const auto opcode = elem_type_inst->opcode();
851 switch (opcode) {
852 case SpvOpTypeArray:
853 case SpvOpTypeRuntimeArray:
854 ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited,
855 vstate);
856 break;
857 case SpvOpTypeStruct:
858 ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited,
859 vstate);
860 break;
861 default:
862 break;
863 }
864}
865
866spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
867 // Set of entry points that are known to use a push constant.
868 std::unordered_set<uint32_t> uses_push_constant;
869 for (const auto& inst : vstate.ordered_instructions()) {
870 const auto& words = inst.words();
871 if (SpvOpVariable == inst.opcode()) {
872 const auto var_id = inst.id();
873 // For storage class / decoration combinations, see Vulkan 14.5.4 "Offset
874 // and Stride Assignment".
875 const auto storageClass = words[3];
876 const bool uniform = storageClass == SpvStorageClassUniform;
877 const bool uniform_constant =
878 storageClass == SpvStorageClassUniformConstant;
879 const bool push_constant = storageClass == SpvStorageClassPushConstant;
880 const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer;
881
882 if (spvIsVulkanEnv(vstate.context()->target_env)) {
883 // Vulkan 14.5.1: There must be no more than one PushConstant block
884 // per entry point.
885 if (push_constant) {
886 auto entry_points = vstate.EntryPointReferences(var_id);
887 for (auto ep_id : entry_points) {
888 const bool already_used = !uses_push_constant.insert(ep_id).second;
889 if (already_used) {
890 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
891 << "Entry point id '" << ep_id
892 << "' uses more than one PushConstant interface.\n"
893 << "From Vulkan spec, section 14.5.1:\n"
894 << "There must be no more than one push constant block "
895 << "statically used per shader entry point.";
896 }
897 }
898 }
899 // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
900 // UniformConstant which cannot be a struct.
901 if (uniform_constant) {
902 auto entry_points = vstate.EntryPointReferences(var_id);
903 if (!entry_points.empty() &&
904 !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
905 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
906 << "UniformConstant id '" << var_id
907 << "' is missing DescriptorSet decoration.\n"
908 << "From Vulkan spec, section 14.5.2:\n"
909 << "These variables must have DescriptorSet and Binding "
910 "decorations specified";
911 }
912 if (!entry_points.empty() &&
913 !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
914 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
915 << "UniformConstant id '" << var_id
916 << "' is missing Binding decoration.\n"
917 << "From Vulkan spec, section 14.5.2:\n"
918 << "These variables must have DescriptorSet and Binding "
919 "decorations specified";
920 }
921 }
922 }
923
924 if (spvIsOpenGLEnv(vstate.context()->target_env)) {
925 bool has_block = hasDecoration(var_id, SpvDecorationBlock, vstate);
926 bool has_buffer_block =
927 hasDecoration(var_id, SpvDecorationBufferBlock, vstate);
928 if ((uniform && (has_block || has_buffer_block)) ||
929 (storage_buffer && has_block)) {
930 auto entry_points = vstate.EntryPointReferences(var_id);
931 if (!entry_points.empty() &&
932 !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
933 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
934 << (uniform ? "Uniform" : "Storage Buffer") << " id '"
935 << var_id << "' is missing Binding decoration.\n"
936 << "From ARB_gl_spirv extension:\n"
937 << "Uniform and shader storage block variables must "
938 << "also be decorated with a *Binding*.";
939 }
940 }
941 }
942
943 const bool phys_storage_buffer =
944 storageClass == SpvStorageClassPhysicalStorageBufferEXT;
945 if (uniform || push_constant || storage_buffer || phys_storage_buffer) {
946 const auto ptrInst = vstate.FindDef(words[1]);
947 assert(SpvOpTypePointer == ptrInst->opcode());
948 auto id = ptrInst->words()[3];
949 auto id_inst = vstate.FindDef(id);
950 // Jump through one level of arraying.
951 if (id_inst->opcode() == SpvOpTypeArray ||
952 id_inst->opcode() == SpvOpTypeRuntimeArray) {
953 id = id_inst->GetOperandAs<uint32_t>(1u);
954 id_inst = vstate.FindDef(id);
955 }
956 // Struct requirement is checked on variables so just move on here.
957 if (SpvOpTypeStruct != id_inst->opcode()) continue;
958 MemberConstraints constraints;
959 ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
960 vstate);
961 // Prepare for messages
962 const char* sc_str =
963 uniform ? "Uniform"
964 : (push_constant ? "PushConstant" : "StorageBuffer");
965
966 if (spvIsVulkanEnv(vstate.context()->target_env)) {
967 const bool block = hasDecoration(id, SpvDecorationBlock, vstate);
968 const bool buffer_block =
969 hasDecoration(id, SpvDecorationBufferBlock, vstate);
970 if (storage_buffer && buffer_block) {
971 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
972 << "Storage buffer id '" << var_id
973 << " In Vulkan, BufferBlock is disallowed on variables in "
974 "the StorageBuffer storage class";
975 }
976 // Vulkan 14.5.1/2: Check Block decoration for PushConstant, Uniform
977 // and StorageBuffer variables. Uniform can also use BufferBlock.
978 if (push_constant && !block) {
979 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
980 << "PushConstant id '" << id
981 << "' is missing Block decoration.\n"
982 << "From Vulkan spec, section 14.5.1:\n"
983 << "Such variables must be identified with a Block "
984 "decoration";
985 }
986 if (storage_buffer && !block) {
987 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
988 << "StorageBuffer id '" << id
989 << "' is missing Block decoration.\n"
990 << "From Vulkan spec, section 14.5.2:\n"
991 << "Such variables must be identified with a Block "
992 "decoration";
993 }
994 if (uniform && !block && !buffer_block) {
995 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
996 << "Uniform id '" << id
997 << "' is missing Block or BufferBlock decoration.\n"
998 << "From Vulkan spec, section 14.5.2:\n"
999 << "Such variables must be identified with a Block or "
1000 "BufferBlock decoration";
1001 }
1002 // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
1003 // Uniform and StorageBuffer variables.
1004 if (uniform || storage_buffer) {
1005 auto entry_points = vstate.EntryPointReferences(var_id);
1006 if (!entry_points.empty() &&
1007 !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
1008 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1009 << sc_str << " id '" << var_id
1010 << "' is missing DescriptorSet decoration.\n"
1011 << "From Vulkan spec, section 14.5.2:\n"
1012 << "These variables must have DescriptorSet and Binding "
1013 "decorations specified";
1014 }
1015 if (!entry_points.empty() &&
1016 !hasDecoration(var_id, SpvDecorationBinding, vstate)) {
1017 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
1018 << sc_str << " id '" << var_id
1019 << "' is missing Binding decoration.\n"
1020 << "From Vulkan spec, section 14.5.2:\n"
1021 << "These variables must have DescriptorSet and Binding "
1022 "decorations specified";
1023 }
1024 }
1025 }
1026
1027 for (const auto& dec : vstate.id_decorations(id)) {
1028 const bool blockDeco = SpvDecorationBlock == dec.dec_type();
1029 const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type();
1030 const bool blockRules = uniform && blockDeco;
1031 const bool bufferRules =
1032 (uniform && bufferDeco) || (push_constant && blockDeco) ||
1033 ((storage_buffer || phys_storage_buffer) && blockDeco);
1034 if (uniform && blockDeco) {
1035 vstate.RegisterPointerToUniformBlock(ptrInst->id());
1036 vstate.RegisterStructForUniformBlock(id);
1037 }
1038 if ((uniform && bufferDeco) ||
1039 ((storage_buffer || phys_storage_buffer) && blockDeco)) {
1040 vstate.RegisterPointerToStorageBuffer(ptrInst->id());
1041 vstate.RegisterStructForStorageBuffer(id);
1042 }
1043
1044 if (blockRules || bufferRules) {
1045 const char* deco_str = blockDeco ? "Block" : "BufferBlock";
1046 spv_result_t recursive_status = SPV_SUCCESS;
1047 if (isMissingOffsetInStruct(id, vstate)) {
1048 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1049 << "Structure id " << id << " decorated as " << deco_str
1050 << " must be explicitly laid out with Offset "
1051 "decorations.";
1052 } else if (hasDecoration(id, SpvDecorationGLSLShared, vstate)) {
1053 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1054 << "Structure id " << id << " decorated as " << deco_str
1055 << " must not use GLSLShared decoration.";
1056 } else if (hasDecoration(id, SpvDecorationGLSLPacked, vstate)) {
1057 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1058 << "Structure id " << id << " decorated as " << deco_str
1059 << " must not use GLSLPacked decoration.";
1060 } else if (!checkForRequiredDecoration(id, SpvDecorationArrayStride,
1061 SpvOpTypeArray, vstate)) {
1062 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1063 << "Structure id " << id << " decorated as " << deco_str
1064 << " must be explicitly laid out with ArrayStride "
1065 "decorations.";
1066 } else if (!checkForRequiredDecoration(id,
1067 SpvDecorationMatrixStride,
1068 SpvOpTypeMatrix, vstate)) {
1069 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1070 << "Structure id " << id << " decorated as " << deco_str
1071 << " must be explicitly laid out with MatrixStride "
1072 "decorations.";
1073 } else if (blockRules &&
1074 (SPV_SUCCESS != (recursive_status = checkLayout(
1075 id, sc_str, deco_str, true, 0,
1076 constraints, vstate)))) {
1077 return recursive_status;
1078 } else if (bufferRules &&
1079 (SPV_SUCCESS != (recursive_status = checkLayout(
1080 id, sc_str, deco_str, false, 0,
1081 constraints, vstate)))) {
1082 return recursive_status;
1083 }
1084 }
1085 }
1086 }
1087 }
1088 }
1089 return SPV_SUCCESS;
1090}
1091
1092// Returns true if |decoration| cannot be applied to the same id more than once.
1093bool AtMostOncePerId(SpvDecoration decoration) {
1094 return decoration == SpvDecorationArrayStride;
1095}
1096
1097// Returns true if |decoration| cannot be applied to the same member more than
1098// once.
1099bool AtMostOncePerMember(SpvDecoration decoration) {
1100 switch (decoration) {
1101 case SpvDecorationOffset:
1102 case SpvDecorationMatrixStride:
1103 case SpvDecorationRowMajor:
1104 case SpvDecorationColMajor:
1105 return true;
1106 default:
1107 return false;
1108 }
1109}
1110
1111// Returns the string name for |decoration|.
1112const char* GetDecorationName(SpvDecoration decoration) {
1113 switch (decoration) {
1114 case SpvDecorationAliased:
1115 return "Aliased";
1116 case SpvDecorationRestrict:
1117 return "Restrict";
1118 case SpvDecorationArrayStride:
1119 return "ArrayStride";
1120 case SpvDecorationOffset:
1121 return "Offset";
1122 case SpvDecorationMatrixStride:
1123 return "MatrixStride";
1124 case SpvDecorationRowMajor:
1125 return "RowMajor";
1126 case SpvDecorationColMajor:
1127 return "ColMajor";
1128 case SpvDecorationBlock:
1129 return "Block";
1130 case SpvDecorationBufferBlock:
1131 return "BufferBlock";
1132 default:
1133 return "";
1134 }
1135}
1136
1137spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {
1138 using PerIDKey = std::tuple<SpvDecoration, uint32_t>;
1139 using PerMemberKey = std::tuple<SpvDecoration, uint32_t, uint32_t>;
1140
1141 // An Array of pairs where the decorations in the pair cannot both be applied
1142 // to the same id.
1143 static const SpvDecoration mutually_exclusive_per_id[][2] = {
1144 {SpvDecorationBlock, SpvDecorationBufferBlock},
1145 {SpvDecorationRestrict, SpvDecorationAliased}};
1146 static const auto num_mutually_exclusive_per_id_pairs =
1147 sizeof(mutually_exclusive_per_id) / (2 * sizeof(SpvDecoration));
1148
1149 // An Array of pairs where the decorations in the pair cannot both be applied
1150 // to the same member.
1151 static const SpvDecoration mutually_exclusive_per_member[][2] = {
1152 {SpvDecorationRowMajor, SpvDecorationColMajor}};
1153 static const auto num_mutually_exclusive_per_mem_pairs =
1154 sizeof(mutually_exclusive_per_member) / (2 * sizeof(SpvDecoration));
1155
1156 std::set<PerIDKey> seen_per_id;
1157 std::set<PerMemberKey> seen_per_member;
1158
1159 for (const auto& inst : vstate.ordered_instructions()) {
1160 const auto& words = inst.words();
1161 if (SpvOpDecorate == inst.opcode()) {
1162 const auto id = words[1];
1163 const auto dec_type = static_cast<SpvDecoration>(words[2]);
1164 const auto k = PerIDKey(dec_type, id);
1165 const auto already_used = !seen_per_id.insert(k).second;
1166 if (already_used && AtMostOncePerId(dec_type)) {
1167 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1168 << "ID '" << id << "' decorated with "
1169 << GetDecorationName(dec_type)
1170 << " multiple times is not allowed.";
1171 }
1172 // Verify certain mutually exclusive decorations are not both applied on
1173 // an ID.
1174 for (uint32_t pair_idx = 0;
1175 pair_idx < num_mutually_exclusive_per_id_pairs; ++pair_idx) {
1176 SpvDecoration excl_dec_type = SpvDecorationMax;
1177 if (mutually_exclusive_per_id[pair_idx][0] == dec_type) {
1178 excl_dec_type = mutually_exclusive_per_id[pair_idx][1];
1179 } else if (mutually_exclusive_per_id[pair_idx][1] == dec_type) {
1180 excl_dec_type = mutually_exclusive_per_id[pair_idx][0];
1181 } else {
1182 continue;
1183 }
1184
1185 const auto excl_k = PerIDKey(excl_dec_type, id);
1186 if (seen_per_id.find(excl_k) != seen_per_id.end()) {
1187 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1188 << "ID '" << id << "' decorated with both "
1189 << GetDecorationName(dec_type) << " and "
1190 << GetDecorationName(excl_dec_type) << " is not allowed.";
1191 }
1192 }
1193 } else if (SpvOpMemberDecorate == inst.opcode()) {
1194 const auto id = words[1];
1195 const auto member_id = words[2];
1196 const auto dec_type = static_cast<SpvDecoration>(words[3]);
1197 const auto k = PerMemberKey(dec_type, id, member_id);
1198 const auto already_used = !seen_per_member.insert(k).second;
1199 if (already_used && AtMostOncePerMember(dec_type)) {
1200 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1201 << "ID '" << id << "', member '" << member_id
1202 << "' decorated with " << GetDecorationName(dec_type)
1203 << " multiple times is not allowed.";
1204 }
1205 // Verify certain mutually exclusive decorations are not both applied on
1206 // a (ID, member) tuple.
1207 for (uint32_t pair_idx = 0;
1208 pair_idx < num_mutually_exclusive_per_mem_pairs; ++pair_idx) {
1209 SpvDecoration excl_dec_type = SpvDecorationMax;
1210 if (mutually_exclusive_per_member[pair_idx][0] == dec_type) {
1211 excl_dec_type = mutually_exclusive_per_member[pair_idx][1];
1212 } else if (mutually_exclusive_per_member[pair_idx][1] == dec_type) {
1213 excl_dec_type = mutually_exclusive_per_member[pair_idx][0];
1214 } else {
1215 continue;
1216 }
1217
1218 const auto excl_k = PerMemberKey(excl_dec_type, id, member_id);
1219 if (seen_per_member.find(excl_k) != seen_per_member.end()) {
1220 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
1221 << "ID '" << id << "', member '" << member_id
1222 << "' decorated with both " << GetDecorationName(dec_type)
1223 << " and " << GetDecorationName(excl_dec_type)
1224 << " is not allowed.";
1225 }
1226 }
1227 }
1228 }
1229 return SPV_SUCCESS;
1230}
1231
1232spv_result_t CheckVulkanMemoryModelDeprecatedDecorations(
1233 ValidationState_t& vstate) {
1234 if (vstate.memory_model() != SpvMemoryModelVulkanKHR) return SPV_SUCCESS;
1235
1236 std::string msg;
1237 std::ostringstream str(msg);
1238 for (const auto& def : vstate.all_definitions()) {
1239 const auto inst = def.second;
1240 const auto id = inst->id();
1241 for (const auto& dec : vstate.id_decorations(id)) {
1242 const auto member = dec.struct_member_index();
1243 if (dec.dec_type() == SpvDecorationCoherent ||
1244 dec.dec_type() == SpvDecorationVolatile) {
1245 str << (dec.dec_type() == SpvDecorationCoherent ? "Coherent"
1246 : "Volatile");
1247 str << " decoration targeting " << vstate.getIdName(id);
1248 if (member != Decoration::kInvalidMember) {
1249 str << " (member index " << member << ")";
1250 }
1251 str << " is banned when using the Vulkan memory model.";
1252 return vstate.diag(SPV_ERROR_INVALID_ID, inst) << str.str();
1253 }
1254 }
1255 }
1256 return SPV_SUCCESS;
1257}
1258
1259// Returns SPV_SUCCESS if validation rules are satisfied for FPRoundingMode
1260// decorations. Otherwise emits a diagnostic and returns something other than
1261// SPV_SUCCESS.
1262spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate,
1263 const Instruction& inst) {
1264 // Validates width-only conversion instruction for floating-point object
1265 // i.e., OpFConvert
1266 if (inst.opcode() != SpvOpFConvert) {
1267 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1268 << "FPRoundingMode decoration can be applied only to a "
1269 "width-only conversion instruction for floating-point "
1270 "object.";
1271 }
1272
1273 // Validates Object operand of an OpStore
1274 for (const auto& use : inst.uses()) {
1275 const auto store = use.first;
1276 if (store->opcode() == SpvOpFConvert) continue;
1277 if (spvOpcodeIsDebug(store->opcode())) continue;
1278 if (store->IsNonSemantic()) continue;
1279 if (spvOpcodeIsDecoration(store->opcode())) continue;
1280 if (store->opcode() != SpvOpStore) {
1281 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1282 << "FPRoundingMode decoration can be applied only to the "
1283 "Object operand of an OpStore.";
1284 }
1285
1286 if (use.second != 2) {
1287 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1288 << "FPRoundingMode decoration can be applied only to the "
1289 "Object operand of an OpStore.";
1290 }
1291
1292 const auto ptr_inst = vstate.FindDef(store->GetOperandAs<uint32_t>(0));
1293 const auto ptr_type = vstate.FindDef(ptr_inst->GetOperandAs<uint32_t>(0));
1294
1295 const auto half_float_id = ptr_type->GetOperandAs<uint32_t>(2);
1296 if (!vstate.IsFloatScalarOrVectorType(half_float_id) ||
1297 vstate.GetBitWidth(half_float_id) != 16) {
1298 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1299 << "FPRoundingMode decoration can be applied only to the "
1300 "Object operand of an OpStore storing through a pointer "
1301 "to "
1302 "a 16-bit floating-point scalar or vector object.";
1303 }
1304
1305 // Validates storage class of the pointer to the OpStore
1306 const auto storage = ptr_type->GetOperandAs<uint32_t>(1);
1307 if (storage != SpvStorageClassStorageBuffer &&
1308 storage != SpvStorageClassUniform &&
1309 storage != SpvStorageClassPushConstant &&
1310 storage != SpvStorageClassInput && storage != SpvStorageClassOutput &&
1311 storage != SpvStorageClassPhysicalStorageBufferEXT) {
1312 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1313 << "FPRoundingMode decoration can be applied only to the "
1314 "Object operand of an OpStore in the StorageBuffer, "
1315 "PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or "
1316 "Output Storage Classes.";
1317 }
1318 }
1319 return SPV_SUCCESS;
1320}
1321
1322// Returns SPV_SUCCESS if validation rules are satisfied for the NonWritable
1323// decoration. Otherwise emits a diagnostic and returns something other than
1324// SPV_SUCCESS. The |inst| parameter is the object being decorated. This must
1325// be called after TypePass and AnnotateCheckDecorationsOfBuffers are called.
1326spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate,
1327 const Instruction& inst,
1328 const Decoration& decoration) {
1329 assert(inst.id() && "Parser ensures the target of the decoration has an ID");
1330
1331 if (decoration.struct_member_index() == Decoration::kInvalidMember) {
1332 // The target must be a memory object declaration.
1333 // First, it must be a variable or function parameter.
1334 const auto opcode = inst.opcode();
1335 const auto type_id = inst.type_id();
1336 if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
1337 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1338 << "Target of NonWritable decoration must be a memory object "
1339 "declaration (a variable or a function parameter)";
1340 }
1341 const auto var_storage_class = opcode == SpvOpVariable
1342 ? inst.GetOperandAs<SpvStorageClass>(2)
1343 : SpvStorageClassMax;
1344 if ((var_storage_class == SpvStorageClassFunction ||
1345 var_storage_class == SpvStorageClassPrivate) &&
1346 vstate.features().nonwritable_var_in_function_or_private) {
1347 // New permitted feature in SPIR-V 1.4.
1348 } else if (
1349 // It may point to a UBO, SSBO, or storage image.
1350 vstate.IsPointerToUniformBlock(type_id) ||
1351 vstate.IsPointerToStorageBuffer(type_id) ||
1352 vstate.IsPointerToStorageImage(type_id)) {
1353 } else {
1354 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1355 << "Target of NonWritable decoration is invalid: must point to a "
1356 "storage image, uniform block, "
1357 << (vstate.features().nonwritable_var_in_function_or_private
1358 ? "storage buffer, or variable in Private or Function "
1359 "storage class"
1360 : "or storage buffer");
1361 }
1362 }
1363
1364 return SPV_SUCCESS;
1365}
1366
1367// Returns SPV_SUCCESS if validation rules are satisfied for Uniform or
1368// UniformId decorations. Otherwise emits a diagnostic and returns something
1369// other than SPV_SUCCESS. Assumes each decoration on a group has been
1370// propagated down to the group members. The |inst| parameter is the object
1371// being decorated.
1372spv_result_t CheckUniformDecoration(ValidationState_t& vstate,
1373 const Instruction& inst,
1374 const Decoration& decoration) {
1375 const char* const dec_name =
1376 decoration.dec_type() == SpvDecorationUniform ? "Uniform" : "UniformId";
1377
1378 // Uniform or UniformId must decorate an "object"
1379 // - has a result ID
1380 // - is an instantiation of a non-void type. So it has a type ID, and that
1381 // type is not void.
1382
1383 // We already know the result ID is non-zero.
1384
1385 if (inst.type_id() == 0) {
1386 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1387 << dec_name << " decoration applied to a non-object";
1388 }
1389 if (Instruction* type_inst = vstate.FindDef(inst.type_id())) {
1390 if (type_inst->opcode() == SpvOpTypeVoid) {
1391 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1392 << dec_name << " decoration applied to a value with void type";
1393 }
1394 } else {
1395 // We might never get here because this would have been rejected earlier in
1396 // the flow.
1397 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1398 << dec_name << " decoration applied to an object with invalid type";
1399 }
1400
1401 // Use of Uniform with OpDecorate is checked elsewhere.
1402 // Use of UniformId with OpDecorateId is checked elsewhere.
1403
1404 if (decoration.dec_type() == SpvDecorationUniformId) {
1405 assert(decoration.params().size() == 1 &&
1406 "Grammar ensures UniformId has one parameter");
1407
1408 // The scope id is an execution scope.
1409 if (auto error =
1410 ValidateExecutionScope(vstate, &inst, decoration.params()[0]))
1411 return error;
1412 }
1413
1414 return SPV_SUCCESS;
1415}
1416
1417// Returns SPV_SUCCESS if validation rules are satisfied for NoSignedWrap or
1418// NoUnsignedWrap decorations. Otherwise emits a diagnostic and returns
1419// something other than SPV_SUCCESS. Assumes each decoration on a group has been
1420// propagated down to the group members.
1421spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate,
1422 const Instruction& inst,
1423 const Decoration& decoration) {
1424 switch (inst.opcode()) {
1425 case SpvOpIAdd:
1426 case SpvOpISub:
1427 case SpvOpIMul:
1428 case SpvOpShiftLeftLogical:
1429 case SpvOpSNegate:
1430 return SPV_SUCCESS;
1431 case SpvOpExtInst:
1432 // TODO(dneto): Only certain extended instructions allow these
1433 // decorations. For now allow anything.
1434 return SPV_SUCCESS;
1435 default:
1436 break;
1437 }
1438
1439 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1440 << (decoration.dec_type() == SpvDecorationNoSignedWrap
1441 ? "NoSignedWrap"
1442 : "NoUnsignedWrap")
1443 << " decoration may not be applied to "
1444 << spvOpcodeString(inst.opcode());
1445}
1446
1447// Returns SPV_SUCCESS if validation rules are satisfied for the Component
1448// decoration. Otherwise emits a diagnostic and returns something other than
1449// SPV_SUCCESS.
1450spv_result_t CheckComponentDecoration(ValidationState_t& vstate,
1451 const Instruction& inst,
1452 const Decoration& decoration) {
1453 assert(inst.id() && "Parser ensures the target of the decoration has an ID");
1454
1455 uint32_t type_id;
1456 if (decoration.struct_member_index() == Decoration::kInvalidMember) {
1457 // The target must be a memory object declaration.
1458 const auto opcode = inst.opcode();
1459 if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
1460 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1461 << "Target of Component decoration must be a memory object "
1462 "declaration (a variable or a function parameter)";
1463 }
1464
1465 // Only valid for the Input and Output Storage Classes.
1466 const auto storage_class = opcode == SpvOpVariable
1467 ? inst.GetOperandAs<SpvStorageClass>(2)
1468 : SpvStorageClassMax;
1469 if (storage_class != SpvStorageClassInput &&
1470 storage_class != SpvStorageClassOutput &&
1471 storage_class != SpvStorageClassMax) {
1472 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1473 << "Target of Component decoration is invalid: must point to a "
1474 "Storage Class of Input(1) or Output(3). Found Storage "
1475 "Class "
1476 << storage_class;
1477 }
1478
1479 type_id = inst.type_id();
1480 if (vstate.IsPointerType(type_id)) {
1481 const auto pointer = vstate.FindDef(type_id);
1482 type_id = pointer->GetOperandAs<uint32_t>(2);
1483 }
1484 } else {
1485 if (inst.opcode() != SpvOpTypeStruct) {
1486 return vstate.diag(SPV_ERROR_INVALID_DATA, &inst)
1487 << "Attempted to get underlying data type via member index for "
1488 "non-struct type.";
1489 }
1490 type_id = inst.word(decoration.struct_member_index() + 2);
1491 }
1492
1493 if (spvIsVulkanEnv(vstate.context()->target_env)) {
1494 // Strip the array, if present.
1495 if (vstate.GetIdOpcode(type_id) == SpvOpTypeArray) {
1496 type_id = vstate.FindDef(type_id)->word(2u);
1497 }
1498
1499 if (!vstate.IsIntScalarOrVectorType(type_id) &&
1500 !vstate.IsFloatScalarOrVectorType(type_id)) {
1501 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1502 << "Component decoration specified for type "
1503 << vstate.getIdName(type_id) << " that is not a scalar or vector";
1504 }
1505
1506 // For 16-, and 32-bit types, it is invalid if this sequence of components
1507 // gets larger than 3.
1508 const auto bit_width = vstate.GetBitWidth(type_id);
1509 if (bit_width == 16 || bit_width == 32) {
1510 assert(decoration.params().size() == 1 &&
1511 "Grammar ensures Component has one parameter");
1512
1513 const auto component = decoration.params()[0];
1514 const auto last_component = component + vstate.GetDimension(type_id) - 1;
1515 if (last_component > 3) {
1516 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1517 << "Sequence of components starting with " << component
1518 << " and ending with " << last_component
1519 << " gets larger than 3";
1520 }
1521 }
1522 }
1523
1524 return SPV_SUCCESS;
1525}
1526
1527// Returns SPV_SUCCESS if validation rules are satisfied for the Block
1528// decoration. Otherwise emits a diagnostic and returns something other than
1529// SPV_SUCCESS.
1530spv_result_t CheckBlockDecoration(ValidationState_t& vstate,
1531 const Instruction& inst,
1532 const Decoration& decoration) {
1533 assert(inst.id() && "Parser ensures the target of the decoration has an ID");
1534 if (inst.opcode() != SpvOpTypeStruct) {
1535 const char* const dec_name =
1536 decoration.dec_type() == SpvDecorationBlock ? "Block" : "BufferBlock";
1537 return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
1538 << dec_name << " decoration on a non-struct type.";
1539 }
1540 return SPV_SUCCESS;
1541}
1542
1543#define PASS_OR_BAIL_AT_LINE(X, LINE) \
1544 { \
1545 spv_result_t e##LINE = (X); \
1546 if (e##LINE != SPV_SUCCESS) return e##LINE; \
1547 }
1548#define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__)
1549
1550// Check rules for decorations where we start from the decoration rather
1551// than the decorated object. Assumes each decoration on a group have been
1552// propagated down to the group members.
1553spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
1554 // Some rules are only checked for shaders.
1555 const bool is_shader = vstate.HasCapability(SpvCapabilityShader);
1556
1557 for (const auto& kv : vstate.id_decorations()) {
1558 const uint32_t id = kv.first;
1559 const auto& decorations = kv.second;
1560 if (decorations.empty()) continue;
1561
1562 const Instruction* inst = vstate.FindDef(id);
1563 assert(inst);
1564
1565 // We assume the decorations applied to a decoration group have already
1566 // been propagated down to the group members.
1567 if (inst->opcode() == SpvOpDecorationGroup) continue;
1568
1569 for (const auto& decoration : decorations) {
1570 switch (decoration.dec_type()) {
1571 case SpvDecorationComponent:
1572 PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration));
1573 break;
1574 case SpvDecorationFPRoundingMode:
1575 if (is_shader)
1576 PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst));
1577 break;
1578 case SpvDecorationNonWritable:
1579 PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration));
1580 break;
1581 case SpvDecorationUniform:
1582 case SpvDecorationUniformId:
1583 PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration));
1584 break;
1585 case SpvDecorationNoSignedWrap:
1586 case SpvDecorationNoUnsignedWrap:
1587 PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration));
1588 break;
1589 case SpvDecorationBlock:
1590 case SpvDecorationBufferBlock:
1591 PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
1592 break;
1593 default:
1594 break;
1595 }
1596 }
1597 }
1598 return SPV_SUCCESS;
1599}
1600
1601} // namespace
1602
1603spv_result_t ValidateDecorations(ValidationState_t& vstate) {
1604 if (auto error = CheckImportedVariableInitialization(vstate)) return error;
1605 if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error;
1606 if (auto error = CheckDecorationsOfBuffers(vstate)) return error;
1607 if (auto error = CheckDecorationsCompatibility(vstate)) return error;
1608 if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error;
1609 if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
1610 return error;
1611 if (auto error = CheckDecorationsFromDecoration(vstate)) return error;
1612 return SPV_SUCCESS;
1613}
1614
1615} // namespace val
1616} // namespace spvtools
1617