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