1 // Copyright (c) 2018 Google LLC. 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 // Validates correctness of built-in variables. 16 17 #include "source/val/validate.h" 18 19 #include <functional> 20 #include <list> 21 #include <map> 22 #include <set> 23 #include <sstream> 24 #include <stack> 25 #include <string> 26 #include <unordered_map> 27 #include <vector> 28 29 #include "source/diagnostic.h" 30 #include "source/opcode.h" 31 #include "source/spirv_target_env.h" 32 #include "source/util/bitutils.h" 33 #include "source/val/instruction.h" 34 #include "source/val/validation_state.h" 35 36 namespace spvtools { 37 namespace val { 38 namespace { 39 40 // Returns a short textual description of the id defined by the given 41 // instruction. 42 std::string GetIdDesc(const Instruction& inst) { 43 std::ostringstream ss; 44 ss << "ID <" << inst.id() << "> (Op" << spvOpcodeString(inst.opcode()) << ")"; 45 return ss.str(); 46 } 47 48 // Gets underlying data type which is 49 // - member type if instruction is OpTypeStruct 50 // (member index is taken from decoration). 51 // - data type if id creates a pointer. 52 // - type of the constant if instruction is OpConst or OpSpecConst. 53 // 54 // Fails in any other case. The function is based on built-ins allowed by 55 // the Vulkan spec. 56 // TODO: If non-Vulkan validation rules are added then it might need 57 // to be refactored. 58 spv_result_t GetUnderlyingType(ValidationState_t& _, 59 const Decoration& decoration, 60 const Instruction& inst, 61 uint32_t* underlying_type) { 62 if (decoration.struct_member_index() != Decoration::kInvalidMember) { 63 assert(inst.opcode() == SpvOpTypeStruct); 64 *underlying_type = inst.word(decoration.struct_member_index() + 2); 65 return SPV_SUCCESS; 66 } 67 68 assert(inst.opcode() != SpvOpTypeStruct); 69 70 if (spvOpcodeIsConstant(inst.opcode())) { 71 *underlying_type = inst.type_id(); 72 return SPV_SUCCESS; 73 } 74 75 uint32_t storage_class = 0; 76 if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) { 77 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 78 << GetIdDesc(inst) 79 << " is decorated with BuiltIn. BuiltIn decoration should only be " 80 "applied to struct types, variables and constants."; 81 } 82 return SPV_SUCCESS; 83 } 84 85 // Returns Storage Class used by the instruction if applicable. 86 // Returns SpvStorageClassMax if not. 87 SpvStorageClass GetStorageClass(const Instruction& inst) { 88 switch (inst.opcode()) { 89 case SpvOpTypePointer: 90 case SpvOpTypeForwardPointer: { 91 return SpvStorageClass(inst.word(2)); 92 } 93 case SpvOpVariable: { 94 return SpvStorageClass(inst.word(3)); 95 } 96 case SpvOpGenericCastToPtrExplicit: { 97 return SpvStorageClass(inst.word(4)); 98 } 99 default: { break; } 100 } 101 return SpvStorageClassMax; 102 } 103 104 // Helper class managing validation of built-ins. 105 // TODO: Generic functionality of this class can be moved into 106 // ValidationState_t to be made available to other users. 107 class BuiltInsValidator { 108 public: 109 BuiltInsValidator(ValidationState_t& vstate) : _(vstate) {} 110 111 // Run validation. 112 spv_result_t Run(); 113 114 private: 115 // Goes through all decorations in the module, if decoration is BuiltIn 116 // calls ValidateSingleBuiltInAtDefinition(). 117 spv_result_t ValidateBuiltInsAtDefinition(); 118 119 // Validates the instruction defining an id with built-in decoration. 120 // Can be called multiple times for the same id, if multiple built-ins are 121 // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed. 122 spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration, 123 const Instruction& inst); 124 125 // The following section contains functions which are called when id defined 126 // by |inst| is decorated with BuiltIn |decoration|. 127 // Most functions are specific to a single built-in and have naming scheme: 128 // ValidateXYZAtDefinition. Some functions are common to multiple kinds of 129 // BuiltIn. 130 spv_result_t ValidateClipOrCullDistanceAtDefinition( 131 const Decoration& decoration, const Instruction& inst); 132 spv_result_t ValidateFragCoordAtDefinition(const Decoration& decoration, 133 const Instruction& inst); 134 spv_result_t ValidateFragDepthAtDefinition(const Decoration& decoration, 135 const Instruction& inst); 136 spv_result_t ValidateFrontFacingAtDefinition(const Decoration& decoration, 137 const Instruction& inst); 138 spv_result_t ValidateHelperInvocationAtDefinition( 139 const Decoration& decoration, const Instruction& inst); 140 spv_result_t ValidateInvocationIdAtDefinition(const Decoration& decoration, 141 const Instruction& inst); 142 spv_result_t ValidateInstanceIndexAtDefinition(const Decoration& decoration, 143 const Instruction& inst); 144 spv_result_t ValidateLayerOrViewportIndexAtDefinition( 145 const Decoration& decoration, const Instruction& inst); 146 spv_result_t ValidatePatchVerticesAtDefinition(const Decoration& decoration, 147 const Instruction& inst); 148 spv_result_t ValidatePointCoordAtDefinition(const Decoration& decoration, 149 const Instruction& inst); 150 spv_result_t ValidatePointSizeAtDefinition(const Decoration& decoration, 151 const Instruction& inst); 152 spv_result_t ValidatePositionAtDefinition(const Decoration& decoration, 153 const Instruction& inst); 154 spv_result_t ValidatePrimitiveIdAtDefinition(const Decoration& decoration, 155 const Instruction& inst); 156 spv_result_t ValidateSampleIdAtDefinition(const Decoration& decoration, 157 const Instruction& inst); 158 spv_result_t ValidateSampleMaskAtDefinition(const Decoration& decoration, 159 const Instruction& inst); 160 spv_result_t ValidateSamplePositionAtDefinition(const Decoration& decoration, 161 const Instruction& inst); 162 spv_result_t ValidateTessCoordAtDefinition(const Decoration& decoration, 163 const Instruction& inst); 164 spv_result_t ValidateTessLevelOuterAtDefinition(const Decoration& decoration, 165 const Instruction& inst); 166 spv_result_t ValidateTessLevelInnerAtDefinition(const Decoration& decoration, 167 const Instruction& inst); 168 spv_result_t ValidateVertexIndexAtDefinition(const Decoration& decoration, 169 const Instruction& inst); 170 spv_result_t ValidateVertexIdOrInstanceIdAtDefinition( 171 const Decoration& decoration, const Instruction& inst); 172 spv_result_t ValidateWorkgroupSizeAtDefinition(const Decoration& decoration, 173 const Instruction& inst); 174 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId. 175 spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition( 176 const Decoration& decoration, const Instruction& inst); 177 178 // The following section contains functions which are called when id defined 179 // by |referenced_inst| is 180 // 1. referenced by |referenced_from_inst| 181 // 2. dependent on |built_in_inst| which is decorated with BuiltIn 182 // |decoration|. Most functions are specific to a single built-in and have 183 // naming scheme: ValidateXYZAtReference. Some functions are common to 184 // multiple kinds of BuiltIn. 185 spv_result_t ValidateFragCoordAtReference( 186 const Decoration& decoration, const Instruction& built_in_inst, 187 const Instruction& referenced_inst, 188 const Instruction& referenced_from_inst); 189 190 spv_result_t ValidateFragDepthAtReference( 191 const Decoration& decoration, const Instruction& built_in_inst, 192 const Instruction& referenced_inst, 193 const Instruction& referenced_from_inst); 194 195 spv_result_t ValidateFrontFacingAtReference( 196 const Decoration& decoration, const Instruction& built_in_inst, 197 const Instruction& referenced_inst, 198 const Instruction& referenced_from_inst); 199 200 spv_result_t ValidateHelperInvocationAtReference( 201 const Decoration& decoration, const Instruction& built_in_inst, 202 const Instruction& referenced_inst, 203 const Instruction& referenced_from_inst); 204 205 spv_result_t ValidateInvocationIdAtReference( 206 const Decoration& decoration, const Instruction& built_in_inst, 207 const Instruction& referenced_inst, 208 const Instruction& referenced_from_inst); 209 210 spv_result_t ValidateInstanceIdAtReference( 211 const Decoration& decoration, const Instruction& built_in_inst, 212 const Instruction& referenced_inst, 213 const Instruction& referenced_from_inst); 214 215 spv_result_t ValidateInstanceIndexAtReference( 216 const Decoration& decoration, const Instruction& built_in_inst, 217 const Instruction& referenced_inst, 218 const Instruction& referenced_from_inst); 219 220 spv_result_t ValidatePatchVerticesAtReference( 221 const Decoration& decoration, const Instruction& built_in_inst, 222 const Instruction& referenced_inst, 223 const Instruction& referenced_from_inst); 224 225 spv_result_t ValidatePointCoordAtReference( 226 const Decoration& decoration, const Instruction& built_in_inst, 227 const Instruction& referenced_inst, 228 const Instruction& referenced_from_inst); 229 230 spv_result_t ValidatePointSizeAtReference( 231 const Decoration& decoration, const Instruction& built_in_inst, 232 const Instruction& referenced_inst, 233 const Instruction& referenced_from_inst); 234 235 spv_result_t ValidatePositionAtReference( 236 const Decoration& decoration, const Instruction& built_in_inst, 237 const Instruction& referenced_inst, 238 const Instruction& referenced_from_inst); 239 240 spv_result_t ValidatePrimitiveIdAtReference( 241 const Decoration& decoration, const Instruction& built_in_inst, 242 const Instruction& referenced_inst, 243 const Instruction& referenced_from_inst); 244 245 spv_result_t ValidateSampleIdAtReference( 246 const Decoration& decoration, const Instruction& built_in_inst, 247 const Instruction& referenced_inst, 248 const Instruction& referenced_from_inst); 249 250 spv_result_t ValidateSampleMaskAtReference( 251 const Decoration& decoration, const Instruction& built_in_inst, 252 const Instruction& referenced_inst, 253 const Instruction& referenced_from_inst); 254 255 spv_result_t ValidateSamplePositionAtReference( 256 const Decoration& decoration, const Instruction& built_in_inst, 257 const Instruction& referenced_inst, 258 const Instruction& referenced_from_inst); 259 260 spv_result_t ValidateTessCoordAtReference( 261 const Decoration& decoration, const Instruction& built_in_inst, 262 const Instruction& referenced_inst, 263 const Instruction& referenced_from_inst); 264 265 spv_result_t ValidateTessLevelAtReference( 266 const Decoration& decoration, const Instruction& built_in_inst, 267 const Instruction& referenced_inst, 268 const Instruction& referenced_from_inst); 269 270 spv_result_t ValidateVertexIndexAtReference( 271 const Decoration& decoration, const Instruction& built_in_inst, 272 const Instruction& referenced_inst, 273 const Instruction& referenced_from_inst); 274 275 spv_result_t ValidateLayerOrViewportIndexAtReference( 276 const Decoration& decoration, const Instruction& built_in_inst, 277 const Instruction& referenced_inst, 278 const Instruction& referenced_from_inst); 279 280 spv_result_t ValidateWorkgroupSizeAtReference( 281 const Decoration& decoration, const Instruction& built_in_inst, 282 const Instruction& referenced_inst, 283 const Instruction& referenced_from_inst); 284 285 spv_result_t ValidateClipOrCullDistanceAtReference( 286 const Decoration& decoration, const Instruction& built_in_inst, 287 const Instruction& referenced_inst, 288 const Instruction& referenced_from_inst); 289 290 // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId. 291 spv_result_t ValidateComputeShaderI32Vec3InputAtReference( 292 const Decoration& decoration, const Instruction& built_in_inst, 293 const Instruction& referenced_inst, 294 const Instruction& referenced_from_inst); 295 296 // Validates that |built_in_inst| is not (even indirectly) referenced from 297 // within a function which can be called with |execution_model|. 298 // 299 // |comment| - text explaining why the restriction was imposed. 300 // |decoration| - BuiltIn decoration which causes the restriction. 301 // |referenced_inst| - instruction which is dependent on |built_in_inst| and 302 // defines the id which was referenced. 303 // |referenced_from_inst| - instruction which references id defined by 304 // |referenced_inst| from within a function. 305 spv_result_t ValidateNotCalledWithExecutionModel( 306 const char* comment, SpvExecutionModel execution_model, 307 const Decoration& decoration, const Instruction& built_in_inst, 308 const Instruction& referenced_inst, 309 const Instruction& referenced_from_inst); 310 311 // The following section contains functions which check that the decorated 312 // variable has the type specified in the function name. |diag| would be 313 // called with a corresponding error message, if validation is not successful. 314 spv_result_t ValidateBool( 315 const Decoration& decoration, const Instruction& inst, 316 const std::function<spv_result_t(const std::string& message)>& diag); 317 spv_result_t ValidateI32( 318 const Decoration& decoration, const Instruction& inst, 319 const std::function<spv_result_t(const std::string& message)>& diag); 320 spv_result_t ValidateI32Vec( 321 const Decoration& decoration, const Instruction& inst, 322 uint32_t num_components, 323 const std::function<spv_result_t(const std::string& message)>& diag); 324 spv_result_t ValidateI32Arr( 325 const Decoration& decoration, const Instruction& inst, 326 const std::function<spv_result_t(const std::string& message)>& diag); 327 spv_result_t ValidateF32( 328 const Decoration& decoration, const Instruction& inst, 329 const std::function<spv_result_t(const std::string& message)>& diag); 330 spv_result_t ValidateOptionalArrayedF32( 331 const Decoration& decoration, const Instruction& inst, 332 const std::function<spv_result_t(const std::string& message)>& diag); 333 spv_result_t ValidateF32Helper( 334 const Decoration& decoration, const Instruction& inst, 335 const std::function<spv_result_t(const std::string& message)>& diag, 336 uint32_t underlying_type); 337 spv_result_t ValidateF32Vec( 338 const Decoration& decoration, const Instruction& inst, 339 uint32_t num_components, 340 const std::function<spv_result_t(const std::string& message)>& diag); 341 spv_result_t ValidateOptionalArrayedF32Vec( 342 const Decoration& decoration, const Instruction& inst, 343 uint32_t num_components, 344 const std::function<spv_result_t(const std::string& message)>& diag); 345 spv_result_t ValidateF32VecHelper( 346 const Decoration& decoration, const Instruction& inst, 347 uint32_t num_components, 348 const std::function<spv_result_t(const std::string& message)>& diag, 349 uint32_t underlying_type); 350 // If |num_components| is zero, the number of components is not checked. 351 spv_result_t ValidateF32Arr( 352 const Decoration& decoration, const Instruction& inst, 353 uint32_t num_components, 354 const std::function<spv_result_t(const std::string& message)>& diag); 355 spv_result_t ValidateOptionalArrayedF32Arr( 356 const Decoration& decoration, const Instruction& inst, 357 uint32_t num_components, 358 const std::function<spv_result_t(const std::string& message)>& diag); 359 spv_result_t ValidateF32ArrHelper( 360 const Decoration& decoration, const Instruction& inst, 361 uint32_t num_components, 362 const std::function<spv_result_t(const std::string& message)>& diag, 363 uint32_t underlying_type); 364 365 // Generates strings like "Member #0 of struct ID <2>". 366 std::string GetDefinitionDesc(const Decoration& decoration, 367 const Instruction& inst) const; 368 369 // Generates strings like "ID <51> (OpTypePointer) is referencing ID <2> 370 // (OpTypeStruct) which is decorated with BuiltIn Position". 371 std::string GetReferenceDesc( 372 const Decoration& decoration, const Instruction& built_in_inst, 373 const Instruction& referenced_inst, 374 const Instruction& referenced_from_inst, 375 SpvExecutionModel execution_model = SpvExecutionModelMax) const; 376 377 // Generates strings like "ID <51> (OpTypePointer) uses storage class 378 // UniformConstant". 379 std::string GetStorageClassDesc(const Instruction& inst) const; 380 381 // Updates inner working of the class. Is called sequentially for every 382 // instruction. 383 void Update(const Instruction& inst); 384 385 ValidationState_t& _; 386 387 // Mapping id -> list of rules which validate instruction referencing the 388 // id. Rules can create new rules and add them to this container. 389 // Using std::map, and not std::unordered_map to avoid iterator invalidation 390 // during rehashing. 391 std::map<uint32_t, std::list<std::function<spv_result_t(const Instruction&)>>> 392 id_to_at_reference_checks_; 393 394 // Id of the function we are currently inside. 0 if not inside a function. 395 uint32_t function_id_ = 0; 396 397 // Entry points which can (indirectly) call the current function. 398 // The pointer either points to a vector inside to function_to_entry_points_ 399 // or to no_entry_points_. The pointer is guaranteed to never be null. 400 const std::vector<uint32_t> no_entry_points; 401 const std::vector<uint32_t>* entry_points_ = &no_entry_points; 402 403 // Execution models with which the current function can be called. 404 std::set<SpvExecutionModel> execution_models_; 405 }; 406 407 void BuiltInsValidator::Update(const Instruction& inst) { 408 const SpvOp opcode = inst.opcode(); 409 if (opcode == SpvOpFunction) { 410 // Entering a function. 411 assert(function_id_ == 0); 412 function_id_ = inst.id(); 413 execution_models_.clear(); 414 entry_points_ = &_.FunctionEntryPoints(function_id_); 415 // Collect execution models from all entry points from which the current 416 // function can be called. 417 for (const uint32_t entry_point : *entry_points_) { 418 if (const auto* models = _.GetExecutionModels(entry_point)) { 419 execution_models_.insert(models->begin(), models->end()); 420 } 421 } 422 } 423 424 if (opcode == SpvOpFunctionEnd) { 425 // Exiting a function. 426 assert(function_id_ != 0); 427 function_id_ = 0; 428 entry_points_ = &no_entry_points; 429 execution_models_.clear(); 430 } 431 } 432 433 std::string BuiltInsValidator::GetDefinitionDesc( 434 const Decoration& decoration, const Instruction& inst) const { 435 std::ostringstream ss; 436 if (decoration.struct_member_index() != Decoration::kInvalidMember) { 437 assert(inst.opcode() == SpvOpTypeStruct); 438 ss << "Member #" << decoration.struct_member_index(); 439 ss << " of struct ID <" << inst.id() << ">"; 440 } else { 441 ss << GetIdDesc(inst); 442 } 443 return ss.str(); 444 } 445 446 std::string BuiltInsValidator::GetReferenceDesc( 447 const Decoration& decoration, const Instruction& built_in_inst, 448 const Instruction& referenced_inst, const Instruction& referenced_from_inst, 449 SpvExecutionModel execution_model) const { 450 std::ostringstream ss; 451 ss << GetIdDesc(referenced_from_inst) << " is referencing " 452 << GetIdDesc(referenced_inst); 453 if (built_in_inst.id() != referenced_inst.id()) { 454 ss << " which is dependent on " << GetIdDesc(built_in_inst); 455 } 456 457 ss << " which is decorated with BuiltIn "; 458 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 459 decoration.params()[0]); 460 if (function_id_) { 461 ss << " in function <" << function_id_ << ">"; 462 if (execution_model != SpvExecutionModelMax) { 463 ss << " called with execution model "; 464 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL, 465 execution_model); 466 } 467 } 468 ss << "."; 469 return ss.str(); 470 } 471 472 std::string BuiltInsValidator::GetStorageClassDesc( 473 const Instruction& inst) const { 474 std::ostringstream ss; 475 ss << GetIdDesc(inst) << " uses storage class "; 476 ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS, 477 GetStorageClass(inst)); 478 ss << "."; 479 return ss.str(); 480 } 481 482 spv_result_t BuiltInsValidator::ValidateBool( 483 const Decoration& decoration, const Instruction& inst, 484 const std::function<spv_result_t(const std::string& message)>& diag) { 485 uint32_t underlying_type = 0; 486 if (spv_result_t error = 487 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 488 return error; 489 } 490 491 if (!_.IsBoolScalarType(underlying_type)) { 492 return diag(GetDefinitionDesc(decoration, inst) + " is not a bool scalar."); 493 } 494 495 return SPV_SUCCESS; 496 } 497 498 spv_result_t BuiltInsValidator::ValidateI32( 499 const Decoration& decoration, const Instruction& inst, 500 const std::function<spv_result_t(const std::string& message)>& diag) { 501 uint32_t underlying_type = 0; 502 if (spv_result_t error = 503 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 504 return error; 505 } 506 507 if (!_.IsIntScalarType(underlying_type)) { 508 return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar."); 509 } 510 511 const uint32_t bit_width = _.GetBitWidth(underlying_type); 512 if (bit_width != 32) { 513 std::ostringstream ss; 514 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width 515 << "."; 516 return diag(ss.str()); 517 } 518 519 return SPV_SUCCESS; 520 } 521 522 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32( 523 const Decoration& decoration, const Instruction& inst, 524 const std::function<spv_result_t(const std::string& message)>& diag) { 525 uint32_t underlying_type = 0; 526 if (spv_result_t error = 527 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 528 return error; 529 } 530 531 // Strip the array, if present. 532 if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) { 533 underlying_type = _.FindDef(underlying_type)->word(2u); 534 } 535 536 return ValidateF32Helper(decoration, inst, diag, underlying_type); 537 } 538 539 spv_result_t BuiltInsValidator::ValidateF32( 540 const Decoration& decoration, const Instruction& inst, 541 const std::function<spv_result_t(const std::string& message)>& diag) { 542 uint32_t underlying_type = 0; 543 if (spv_result_t error = 544 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 545 return error; 546 } 547 548 return ValidateF32Helper(decoration, inst, diag, underlying_type); 549 } 550 551 spv_result_t BuiltInsValidator::ValidateF32Helper( 552 const Decoration& decoration, const Instruction& inst, 553 const std::function<spv_result_t(const std::string& message)>& diag, 554 uint32_t underlying_type) { 555 if (!_.IsFloatScalarType(underlying_type)) { 556 return diag(GetDefinitionDesc(decoration, inst) + 557 " is not a float scalar."); 558 } 559 560 const uint32_t bit_width = _.GetBitWidth(underlying_type); 561 if (bit_width != 32) { 562 std::ostringstream ss; 563 ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width 564 << "."; 565 return diag(ss.str()); 566 } 567 568 return SPV_SUCCESS; 569 } 570 571 spv_result_t BuiltInsValidator::ValidateI32Vec( 572 const Decoration& decoration, const Instruction& inst, 573 uint32_t num_components, 574 const std::function<spv_result_t(const std::string& message)>& diag) { 575 uint32_t underlying_type = 0; 576 if (spv_result_t error = 577 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 578 return error; 579 } 580 581 if (!_.IsIntVectorType(underlying_type)) { 582 return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector."); 583 } 584 585 const uint32_t actual_num_components = _.GetDimension(underlying_type); 586 if (_.GetDimension(underlying_type) != num_components) { 587 std::ostringstream ss; 588 ss << GetDefinitionDesc(decoration, inst) << " has " 589 << actual_num_components << " components."; 590 return diag(ss.str()); 591 } 592 593 const uint32_t bit_width = _.GetBitWidth(underlying_type); 594 if (bit_width != 32) { 595 std::ostringstream ss; 596 ss << GetDefinitionDesc(decoration, inst) 597 << " has components with bit width " << bit_width << "."; 598 return diag(ss.str()); 599 } 600 601 return SPV_SUCCESS; 602 } 603 604 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec( 605 const Decoration& decoration, const Instruction& inst, 606 uint32_t num_components, 607 const std::function<spv_result_t(const std::string& message)>& diag) { 608 uint32_t underlying_type = 0; 609 if (spv_result_t error = 610 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 611 return error; 612 } 613 614 // Strip the array, if present. 615 if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) { 616 underlying_type = _.FindDef(underlying_type)->word(2u); 617 } 618 619 return ValidateF32VecHelper(decoration, inst, num_components, diag, 620 underlying_type); 621 } 622 623 spv_result_t BuiltInsValidator::ValidateF32Vec( 624 const Decoration& decoration, const Instruction& inst, 625 uint32_t num_components, 626 const std::function<spv_result_t(const std::string& message)>& diag) { 627 uint32_t underlying_type = 0; 628 if (spv_result_t error = 629 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 630 return error; 631 } 632 633 return ValidateF32VecHelper(decoration, inst, num_components, diag, 634 underlying_type); 635 } 636 637 spv_result_t BuiltInsValidator::ValidateF32VecHelper( 638 const Decoration& decoration, const Instruction& inst, 639 uint32_t num_components, 640 const std::function<spv_result_t(const std::string& message)>& diag, 641 uint32_t underlying_type) { 642 if (!_.IsFloatVectorType(underlying_type)) { 643 return diag(GetDefinitionDesc(decoration, inst) + 644 " is not a float vector."); 645 } 646 647 const uint32_t actual_num_components = _.GetDimension(underlying_type); 648 if (_.GetDimension(underlying_type) != num_components) { 649 std::ostringstream ss; 650 ss << GetDefinitionDesc(decoration, inst) << " has " 651 << actual_num_components << " components."; 652 return diag(ss.str()); 653 } 654 655 const uint32_t bit_width = _.GetBitWidth(underlying_type); 656 if (bit_width != 32) { 657 std::ostringstream ss; 658 ss << GetDefinitionDesc(decoration, inst) 659 << " has components with bit width " << bit_width << "."; 660 return diag(ss.str()); 661 } 662 663 return SPV_SUCCESS; 664 } 665 666 spv_result_t BuiltInsValidator::ValidateI32Arr( 667 const Decoration& decoration, const Instruction& inst, 668 const std::function<spv_result_t(const std::string& message)>& diag) { 669 uint32_t underlying_type = 0; 670 if (spv_result_t error = 671 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 672 return error; 673 } 674 675 const Instruction* const type_inst = _.FindDef(underlying_type); 676 if (type_inst->opcode() != SpvOpTypeArray) { 677 return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); 678 } 679 680 const uint32_t component_type = type_inst->word(2); 681 if (!_.IsIntScalarType(component_type)) { 682 return diag(GetDefinitionDesc(decoration, inst) + 683 " components are not int scalar."); 684 } 685 686 const uint32_t bit_width = _.GetBitWidth(component_type); 687 if (bit_width != 32) { 688 std::ostringstream ss; 689 ss << GetDefinitionDesc(decoration, inst) 690 << " has components with bit width " << bit_width << "."; 691 return diag(ss.str()); 692 } 693 694 return SPV_SUCCESS; 695 } 696 697 spv_result_t BuiltInsValidator::ValidateF32Arr( 698 const Decoration& decoration, const Instruction& inst, 699 uint32_t num_components, 700 const std::function<spv_result_t(const std::string& message)>& diag) { 701 uint32_t underlying_type = 0; 702 if (spv_result_t error = 703 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 704 return error; 705 } 706 707 return ValidateF32ArrHelper(decoration, inst, num_components, diag, 708 underlying_type); 709 } 710 711 spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Arr( 712 const Decoration& decoration, const Instruction& inst, 713 uint32_t num_components, 714 const std::function<spv_result_t(const std::string& message)>& diag) { 715 uint32_t underlying_type = 0; 716 if (spv_result_t error = 717 GetUnderlyingType(_, decoration, inst, &underlying_type)) { 718 return error; 719 } 720 721 // Strip an extra layer of arraying if present. 722 if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) { 723 uint32_t subtype = _.FindDef(underlying_type)->word(2u); 724 if (_.GetIdOpcode(subtype) == SpvOpTypeArray) { 725 underlying_type = subtype; 726 } 727 } 728 729 return ValidateF32ArrHelper(decoration, inst, num_components, diag, 730 underlying_type); 731 } 732 733 spv_result_t BuiltInsValidator::ValidateF32ArrHelper( 734 const Decoration& decoration, const Instruction& inst, 735 uint32_t num_components, 736 const std::function<spv_result_t(const std::string& message)>& diag, 737 uint32_t underlying_type) { 738 const Instruction* const type_inst = _.FindDef(underlying_type); 739 if (type_inst->opcode() != SpvOpTypeArray) { 740 return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); 741 } 742 743 const uint32_t component_type = type_inst->word(2); 744 if (!_.IsFloatScalarType(component_type)) { 745 return diag(GetDefinitionDesc(decoration, inst) + 746 " components are not float scalar."); 747 } 748 749 const uint32_t bit_width = _.GetBitWidth(component_type); 750 if (bit_width != 32) { 751 std::ostringstream ss; 752 ss << GetDefinitionDesc(decoration, inst) 753 << " has components with bit width " << bit_width << "."; 754 return diag(ss.str()); 755 } 756 757 if (num_components != 0) { 758 uint64_t actual_num_components = 0; 759 if (!_.GetConstantValUint64(type_inst->word(3), &actual_num_components)) { 760 assert(0 && "Array type definition is corrupt"); 761 } 762 if (actual_num_components != num_components) { 763 std::ostringstream ss; 764 ss << GetDefinitionDesc(decoration, inst) << " has " 765 << actual_num_components << " components."; 766 return diag(ss.str()); 767 } 768 } 769 770 return SPV_SUCCESS; 771 } 772 773 spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel( 774 const char* comment, SpvExecutionModel execution_model, 775 const Decoration& decoration, const Instruction& built_in_inst, 776 const Instruction& referenced_inst, 777 const Instruction& referenced_from_inst) { 778 if (function_id_) { 779 if (execution_models_.count(execution_model)) { 780 const char* execution_model_str = _.grammar().lookupOperandName( 781 SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model); 782 const char* built_in_str = _.grammar().lookupOperandName( 783 SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]); 784 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 785 << comment << " " << GetIdDesc(referenced_inst) << " depends on " 786 << GetIdDesc(built_in_inst) << " which is decorated with BuiltIn " 787 << built_in_str << "." 788 << " Id <" << referenced_inst.id() << "> is later referenced by " 789 << GetIdDesc(referenced_from_inst) << " in function <" 790 << function_id_ << "> which is called with execution model " 791 << execution_model_str << "."; 792 } 793 } else { 794 // Propagate this rule to all dependant ids in the global scope. 795 id_to_at_reference_checks_[referenced_from_inst.id()].push_back( 796 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 797 comment, execution_model, decoration, built_in_inst, 798 referenced_from_inst, std::placeholders::_1)); 799 } 800 return SPV_SUCCESS; 801 } 802 803 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtDefinition( 804 const Decoration& decoration, const Instruction& inst) { 805 // Seed at reference checks with this built-in. 806 return ValidateClipOrCullDistanceAtReference(decoration, inst, inst, inst); 807 } 808 809 spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( 810 const Decoration& decoration, const Instruction& built_in_inst, 811 const Instruction& referenced_inst, 812 const Instruction& referenced_from_inst) { 813 if (spvIsVulkanEnv(_.context()->target_env)) { 814 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 815 if (storage_class != SpvStorageClassMax && 816 storage_class != SpvStorageClassInput && 817 storage_class != SpvStorageClassOutput) { 818 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 819 << "Vulkan spec allows BuiltIn " 820 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 821 decoration.params()[0]) 822 << " to be only used for variables with Input or Output storage " 823 "class. " 824 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 825 referenced_from_inst) 826 << " " << GetStorageClassDesc(referenced_from_inst); 827 } 828 829 if (storage_class == SpvStorageClassInput) { 830 assert(function_id_ == 0); 831 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 832 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 833 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " 834 "used for variables with Input storage class if execution model is " 835 "Vertex.", 836 SpvExecutionModelVertex, decoration, built_in_inst, 837 referenced_from_inst, std::placeholders::_1)); 838 } 839 840 if (storage_class == SpvStorageClassOutput) { 841 assert(function_id_ == 0); 842 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 843 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 844 "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " 845 "used for variables with Output storage class if execution model is " 846 "Fragment.", 847 SpvExecutionModelFragment, decoration, built_in_inst, 848 referenced_from_inst, std::placeholders::_1)); 849 } 850 851 for (const SpvExecutionModel execution_model : execution_models_) { 852 switch (execution_model) { 853 case SpvExecutionModelFragment: 854 case SpvExecutionModelVertex: { 855 if (spv_result_t error = ValidateF32Arr( 856 decoration, built_in_inst, /* Any number of components */ 0, 857 [this, &decoration, &referenced_from_inst]( 858 const std::string& message) -> spv_result_t { 859 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 860 << "According to the Vulkan spec BuiltIn " 861 << _.grammar().lookupOperandName( 862 SPV_OPERAND_TYPE_BUILT_IN, 863 decoration.params()[0]) 864 << " variable needs to be a 32-bit float array. " 865 << message; 866 })) { 867 return error; 868 } 869 break; 870 } 871 case SpvExecutionModelTessellationControl: 872 case SpvExecutionModelTessellationEvaluation: 873 case SpvExecutionModelGeometry: 874 case SpvExecutionModelMeshNV: { 875 if (decoration.struct_member_index() != Decoration::kInvalidMember) { 876 // The outer level of array is applied on the variable. 877 if (spv_result_t error = ValidateF32Arr( 878 decoration, built_in_inst, /* Any number of components */ 0, 879 [this, &decoration, &referenced_from_inst]( 880 const std::string& message) -> spv_result_t { 881 return _.diag(SPV_ERROR_INVALID_DATA, 882 &referenced_from_inst) 883 << "According to the Vulkan spec BuiltIn " 884 << _.grammar().lookupOperandName( 885 SPV_OPERAND_TYPE_BUILT_IN, 886 decoration.params()[0]) 887 << " variable needs to be a 32-bit float array. " 888 << message; 889 })) { 890 return error; 891 } 892 } else { 893 if (spv_result_t error = ValidateOptionalArrayedF32Arr( 894 decoration, built_in_inst, /* Any number of components */ 0, 895 [this, &decoration, &referenced_from_inst]( 896 const std::string& message) -> spv_result_t { 897 return _.diag(SPV_ERROR_INVALID_DATA, 898 &referenced_from_inst) 899 << "According to the Vulkan spec BuiltIn " 900 << _.grammar().lookupOperandName( 901 SPV_OPERAND_TYPE_BUILT_IN, 902 decoration.params()[0]) 903 << " variable needs to be a 32-bit float array. " 904 << message; 905 })) { 906 return error; 907 } 908 } 909 break; 910 } 911 912 default: { 913 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 914 << "Vulkan spec allows BuiltIn " 915 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 916 decoration.params()[0]) 917 << " to be used only with Fragment, Vertex, " 918 "TessellationControl, TessellationEvaluation or Geometry " 919 "execution models. " 920 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 921 referenced_from_inst, execution_model); 922 } 923 } 924 } 925 } 926 927 if (function_id_ == 0) { 928 // Propagate this rule to all dependant ids in the global scope. 929 id_to_at_reference_checks_[referenced_from_inst.id()].push_back( 930 std::bind(&BuiltInsValidator::ValidateClipOrCullDistanceAtReference, 931 this, decoration, built_in_inst, referenced_from_inst, 932 std::placeholders::_1)); 933 } 934 935 return SPV_SUCCESS; 936 } 937 938 spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition( 939 const Decoration& decoration, const Instruction& inst) { 940 if (spvIsVulkanEnv(_.context()->target_env)) { 941 if (spv_result_t error = ValidateF32Vec( 942 decoration, inst, 4, 943 [this, &inst](const std::string& message) -> spv_result_t { 944 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 945 << "According to the Vulkan spec BuiltIn FragCoord " 946 "variable needs to be a 4-component 32-bit float " 947 "vector. " 948 << message; 949 })) { 950 return error; 951 } 952 } 953 954 // Seed at reference checks with this built-in. 955 return ValidateFragCoordAtReference(decoration, inst, inst, inst); 956 } 957 958 spv_result_t BuiltInsValidator::ValidateFragCoordAtReference( 959 const Decoration& decoration, const Instruction& built_in_inst, 960 const Instruction& referenced_inst, 961 const Instruction& referenced_from_inst) { 962 if (spvIsVulkanEnv(_.context()->target_env)) { 963 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 964 if (storage_class != SpvStorageClassMax && 965 storage_class != SpvStorageClassInput) { 966 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 967 << "Vulkan spec allows BuiltIn FragCoord to be only used for " 968 "variables with Input storage class. " 969 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 970 referenced_from_inst) 971 << " " << GetStorageClassDesc(referenced_from_inst); 972 } 973 974 for (const SpvExecutionModel execution_model : execution_models_) { 975 if (execution_model != SpvExecutionModelFragment) { 976 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 977 << "Vulkan spec allows BuiltIn FragCoord to be used only with " 978 "Fragment execution model. " 979 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 980 referenced_from_inst, execution_model); 981 } 982 } 983 } 984 985 if (function_id_ == 0) { 986 // Propagate this rule to all dependant ids in the global scope. 987 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 988 &BuiltInsValidator::ValidateFragCoordAtReference, this, decoration, 989 built_in_inst, referenced_from_inst, std::placeholders::_1)); 990 } 991 992 return SPV_SUCCESS; 993 } 994 995 spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition( 996 const Decoration& decoration, const Instruction& inst) { 997 if (spvIsVulkanEnv(_.context()->target_env)) { 998 if (spv_result_t error = ValidateF32( 999 decoration, inst, 1000 [this, &inst](const std::string& message) -> spv_result_t { 1001 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1002 << "According to the Vulkan spec BuiltIn FragDepth " 1003 "variable needs to be a 32-bit float scalar. " 1004 << message; 1005 })) { 1006 return error; 1007 } 1008 } 1009 1010 // Seed at reference checks with this built-in. 1011 return ValidateFragDepthAtReference(decoration, inst, inst, inst); 1012 } 1013 1014 spv_result_t BuiltInsValidator::ValidateFragDepthAtReference( 1015 const Decoration& decoration, const Instruction& built_in_inst, 1016 const Instruction& referenced_inst, 1017 const Instruction& referenced_from_inst) { 1018 if (spvIsVulkanEnv(_.context()->target_env)) { 1019 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1020 if (storage_class != SpvStorageClassMax && 1021 storage_class != SpvStorageClassOutput) { 1022 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1023 << "Vulkan spec allows BuiltIn FragDepth to be only used for " 1024 "variables with Output storage class. " 1025 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1026 referenced_from_inst) 1027 << " " << GetStorageClassDesc(referenced_from_inst); 1028 } 1029 1030 for (const SpvExecutionModel execution_model : execution_models_) { 1031 if (execution_model != SpvExecutionModelFragment) { 1032 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1033 << "Vulkan spec allows BuiltIn FragDepth to be used only with " 1034 "Fragment execution model. " 1035 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1036 referenced_from_inst, execution_model); 1037 } 1038 } 1039 1040 for (const uint32_t entry_point : *entry_points_) { 1041 // Every entry point from which this function is called needs to have 1042 // Execution Mode DepthReplacing. 1043 const auto* modes = _.GetExecutionModes(entry_point); 1044 if (!modes || !modes->count(SpvExecutionModeDepthReplacing)) { 1045 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1046 << "Vulkan spec requires DepthReplacing execution mode to be " 1047 "declared when using BuiltIn FragDepth. " 1048 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1049 referenced_from_inst); 1050 } 1051 } 1052 } 1053 1054 if (function_id_ == 0) { 1055 // Propagate this rule to all dependant ids in the global scope. 1056 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1057 &BuiltInsValidator::ValidateFragDepthAtReference, this, decoration, 1058 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1059 } 1060 1061 return SPV_SUCCESS; 1062 } 1063 1064 spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition( 1065 const Decoration& decoration, const Instruction& inst) { 1066 if (spvIsVulkanEnv(_.context()->target_env)) { 1067 if (spv_result_t error = ValidateBool( 1068 decoration, inst, 1069 [this, &inst](const std::string& message) -> spv_result_t { 1070 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1071 << "According to the Vulkan spec BuiltIn FrontFacing " 1072 "variable needs to be a bool scalar. " 1073 << message; 1074 })) { 1075 return error; 1076 } 1077 } 1078 1079 // Seed at reference checks with this built-in. 1080 return ValidateFrontFacingAtReference(decoration, inst, inst, inst); 1081 } 1082 1083 spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference( 1084 const Decoration& decoration, const Instruction& built_in_inst, 1085 const Instruction& referenced_inst, 1086 const Instruction& referenced_from_inst) { 1087 if (spvIsVulkanEnv(_.context()->target_env)) { 1088 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1089 if (storage_class != SpvStorageClassMax && 1090 storage_class != SpvStorageClassInput) { 1091 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1092 << "Vulkan spec allows BuiltIn FrontFacing to be only used for " 1093 "variables with Input storage class. " 1094 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1095 referenced_from_inst) 1096 << " " << GetStorageClassDesc(referenced_from_inst); 1097 } 1098 1099 for (const SpvExecutionModel execution_model : execution_models_) { 1100 if (execution_model != SpvExecutionModelFragment) { 1101 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1102 << "Vulkan spec allows BuiltIn FrontFacing to be used only with " 1103 "Fragment execution model. " 1104 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1105 referenced_from_inst, execution_model); 1106 } 1107 } 1108 } 1109 1110 if (function_id_ == 0) { 1111 // Propagate this rule to all dependant ids in the global scope. 1112 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1113 &BuiltInsValidator::ValidateFrontFacingAtReference, this, decoration, 1114 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1115 } 1116 1117 return SPV_SUCCESS; 1118 } 1119 1120 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtDefinition( 1121 const Decoration& decoration, const Instruction& inst) { 1122 if (spvIsVulkanEnv(_.context()->target_env)) { 1123 if (spv_result_t error = ValidateBool( 1124 decoration, inst, 1125 [this, &inst](const std::string& message) -> spv_result_t { 1126 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1127 << "According to the Vulkan spec BuiltIn HelperInvocation " 1128 "variable needs to be a bool scalar. " 1129 << message; 1130 })) { 1131 return error; 1132 } 1133 } 1134 1135 // Seed at reference checks with this built-in. 1136 return ValidateHelperInvocationAtReference(decoration, inst, inst, inst); 1137 } 1138 1139 spv_result_t BuiltInsValidator::ValidateHelperInvocationAtReference( 1140 const Decoration& decoration, const Instruction& built_in_inst, 1141 const Instruction& referenced_inst, 1142 const Instruction& referenced_from_inst) { 1143 if (spvIsVulkanEnv(_.context()->target_env)) { 1144 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1145 if (storage_class != SpvStorageClassMax && 1146 storage_class != SpvStorageClassInput) { 1147 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1148 << "Vulkan spec allows BuiltIn HelperInvocation to be only used " 1149 "for variables with Input storage class. " 1150 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1151 referenced_from_inst) 1152 << " " << GetStorageClassDesc(referenced_from_inst); 1153 } 1154 1155 for (const SpvExecutionModel execution_model : execution_models_) { 1156 if (execution_model != SpvExecutionModelFragment) { 1157 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1158 << "Vulkan spec allows BuiltIn HelperInvocation to be used only " 1159 "with Fragment execution model. " 1160 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1161 referenced_from_inst, execution_model); 1162 } 1163 } 1164 } 1165 1166 if (function_id_ == 0) { 1167 // Propagate this rule to all dependant ids in the global scope. 1168 id_to_at_reference_checks_[referenced_from_inst.id()].push_back( 1169 std::bind(&BuiltInsValidator::ValidateHelperInvocationAtReference, this, 1170 decoration, built_in_inst, referenced_from_inst, 1171 std::placeholders::_1)); 1172 } 1173 1174 return SPV_SUCCESS; 1175 } 1176 1177 spv_result_t BuiltInsValidator::ValidateInvocationIdAtDefinition( 1178 const Decoration& decoration, const Instruction& inst) { 1179 if (spvIsVulkanEnv(_.context()->target_env)) { 1180 if (spv_result_t error = ValidateI32( 1181 decoration, inst, 1182 [this, &inst](const std::string& message) -> spv_result_t { 1183 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1184 << "According to the Vulkan spec BuiltIn InvocationId " 1185 "variable needs to be a 32-bit int scalar. " 1186 << message; 1187 })) { 1188 return error; 1189 } 1190 } 1191 1192 // Seed at reference checks with this built-in. 1193 return ValidateInvocationIdAtReference(decoration, inst, inst, inst); 1194 } 1195 1196 spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference( 1197 const Decoration& decoration, const Instruction& built_in_inst, 1198 const Instruction& referenced_inst, 1199 const Instruction& referenced_from_inst) { 1200 if (spvIsVulkanEnv(_.context()->target_env)) { 1201 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1202 if (storage_class != SpvStorageClassMax && 1203 storage_class != SpvStorageClassInput) { 1204 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1205 << "Vulkan spec allows BuiltIn InvocationId to be only used for " 1206 "variables with Input storage class. " 1207 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1208 referenced_from_inst) 1209 << " " << GetStorageClassDesc(referenced_from_inst); 1210 } 1211 1212 for (const SpvExecutionModel execution_model : execution_models_) { 1213 if (execution_model != SpvExecutionModelTessellationControl && 1214 execution_model != SpvExecutionModelGeometry) { 1215 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1216 << "Vulkan spec allows BuiltIn InvocationId to be used only " 1217 "with TessellationControl or Geometry execution models. " 1218 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1219 referenced_from_inst, execution_model); 1220 } 1221 } 1222 } 1223 1224 if (function_id_ == 0) { 1225 // Propagate this rule to all dependant ids in the global scope. 1226 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1227 &BuiltInsValidator::ValidateInvocationIdAtReference, this, decoration, 1228 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1229 } 1230 1231 return SPV_SUCCESS; 1232 } 1233 1234 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition( 1235 const Decoration& decoration, const Instruction& inst) { 1236 if (spvIsVulkanEnv(_.context()->target_env)) { 1237 if (spv_result_t error = ValidateI32( 1238 decoration, inst, 1239 [this, &inst](const std::string& message) -> spv_result_t { 1240 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1241 << "According to the Vulkan spec BuiltIn InstanceIndex " 1242 "variable needs to be a 32-bit int scalar. " 1243 << message; 1244 })) { 1245 return error; 1246 } 1247 } 1248 1249 // Seed at reference checks with this built-in. 1250 return ValidateInstanceIndexAtReference(decoration, inst, inst, inst); 1251 } 1252 1253 spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference( 1254 const Decoration& decoration, const Instruction& built_in_inst, 1255 const Instruction& referenced_inst, 1256 const Instruction& referenced_from_inst) { 1257 if (spvIsVulkanEnv(_.context()->target_env)) { 1258 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1259 if (storage_class != SpvStorageClassMax && 1260 storage_class != SpvStorageClassInput) { 1261 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1262 << "Vulkan spec allows BuiltIn InstanceIndex to be only used for " 1263 "variables with Input storage class. " 1264 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1265 referenced_from_inst) 1266 << " " << GetStorageClassDesc(referenced_from_inst); 1267 } 1268 1269 for (const SpvExecutionModel execution_model : execution_models_) { 1270 if (execution_model != SpvExecutionModelVertex) { 1271 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1272 << "Vulkan spec allows BuiltIn InstanceIndex to be used only " 1273 "with Vertex execution model. " 1274 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1275 referenced_from_inst, execution_model); 1276 } 1277 } 1278 } 1279 1280 if (function_id_ == 0) { 1281 // Propagate this rule to all dependant ids in the global scope. 1282 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1283 &BuiltInsValidator::ValidateInstanceIndexAtReference, this, decoration, 1284 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1285 } 1286 1287 return SPV_SUCCESS; 1288 } 1289 1290 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtDefinition( 1291 const Decoration& decoration, const Instruction& inst) { 1292 if (spvIsVulkanEnv(_.context()->target_env)) { 1293 if (spv_result_t error = ValidateI32( 1294 decoration, inst, 1295 [this, &inst](const std::string& message) -> spv_result_t { 1296 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1297 << "According to the Vulkan spec BuiltIn PatchVertices " 1298 "variable needs to be a 32-bit int scalar. " 1299 << message; 1300 })) { 1301 return error; 1302 } 1303 } 1304 1305 // Seed at reference checks with this built-in. 1306 return ValidatePatchVerticesAtReference(decoration, inst, inst, inst); 1307 } 1308 1309 spv_result_t BuiltInsValidator::ValidatePatchVerticesAtReference( 1310 const Decoration& decoration, const Instruction& built_in_inst, 1311 const Instruction& referenced_inst, 1312 const Instruction& referenced_from_inst) { 1313 if (spvIsVulkanEnv(_.context()->target_env)) { 1314 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1315 if (storage_class != SpvStorageClassMax && 1316 storage_class != SpvStorageClassInput) { 1317 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1318 << "Vulkan spec allows BuiltIn PatchVertices to be only used for " 1319 "variables with Input storage class. " 1320 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1321 referenced_from_inst) 1322 << " " << GetStorageClassDesc(referenced_from_inst); 1323 } 1324 1325 for (const SpvExecutionModel execution_model : execution_models_) { 1326 if (execution_model != SpvExecutionModelTessellationControl && 1327 execution_model != SpvExecutionModelTessellationEvaluation) { 1328 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1329 << "Vulkan spec allows BuiltIn PatchVertices to be used only " 1330 "with TessellationControl or TessellationEvaluation " 1331 "execution models. " 1332 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1333 referenced_from_inst, execution_model); 1334 } 1335 } 1336 } 1337 1338 if (function_id_ == 0) { 1339 // Propagate this rule to all dependant ids in the global scope. 1340 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1341 &BuiltInsValidator::ValidatePatchVerticesAtReference, this, decoration, 1342 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1343 } 1344 1345 return SPV_SUCCESS; 1346 } 1347 1348 spv_result_t BuiltInsValidator::ValidatePointCoordAtDefinition( 1349 const Decoration& decoration, const Instruction& inst) { 1350 if (spvIsVulkanEnv(_.context()->target_env)) { 1351 if (spv_result_t error = ValidateF32Vec( 1352 decoration, inst, 2, 1353 [this, &inst](const std::string& message) -> spv_result_t { 1354 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1355 << "According to the Vulkan spec BuiltIn PointCoord " 1356 "variable needs to be a 2-component 32-bit float " 1357 "vector. " 1358 << message; 1359 })) { 1360 return error; 1361 } 1362 } 1363 1364 // Seed at reference checks with this built-in. 1365 return ValidatePointCoordAtReference(decoration, inst, inst, inst); 1366 } 1367 1368 spv_result_t BuiltInsValidator::ValidatePointCoordAtReference( 1369 const Decoration& decoration, const Instruction& built_in_inst, 1370 const Instruction& referenced_inst, 1371 const Instruction& referenced_from_inst) { 1372 if (spvIsVulkanEnv(_.context()->target_env)) { 1373 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1374 if (storage_class != SpvStorageClassMax && 1375 storage_class != SpvStorageClassInput) { 1376 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1377 << "Vulkan spec allows BuiltIn PointCoord to be only used for " 1378 "variables with Input storage class. " 1379 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1380 referenced_from_inst) 1381 << " " << GetStorageClassDesc(referenced_from_inst); 1382 } 1383 1384 for (const SpvExecutionModel execution_model : execution_models_) { 1385 if (execution_model != SpvExecutionModelFragment) { 1386 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1387 << "Vulkan spec allows BuiltIn PointCoord to be used only with " 1388 "Fragment execution model. " 1389 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1390 referenced_from_inst, execution_model); 1391 } 1392 } 1393 } 1394 1395 if (function_id_ == 0) { 1396 // Propagate this rule to all dependant ids in the global scope. 1397 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1398 &BuiltInsValidator::ValidatePointCoordAtReference, this, decoration, 1399 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1400 } 1401 1402 return SPV_SUCCESS; 1403 } 1404 1405 spv_result_t BuiltInsValidator::ValidatePointSizeAtDefinition( 1406 const Decoration& decoration, const Instruction& inst) { 1407 // Seed at reference checks with this built-in. 1408 return ValidatePointSizeAtReference(decoration, inst, inst, inst); 1409 } 1410 1411 spv_result_t BuiltInsValidator::ValidatePointSizeAtReference( 1412 const Decoration& decoration, const Instruction& built_in_inst, 1413 const Instruction& referenced_inst, 1414 const Instruction& referenced_from_inst) { 1415 if (spvIsVulkanEnv(_.context()->target_env)) { 1416 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1417 if (storage_class != SpvStorageClassMax && 1418 storage_class != SpvStorageClassInput && 1419 storage_class != SpvStorageClassOutput) { 1420 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1421 << "Vulkan spec allows BuiltIn PointSize to be only used for " 1422 "variables with Input or Output storage class. " 1423 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1424 referenced_from_inst) 1425 << " " << GetStorageClassDesc(referenced_from_inst); 1426 } 1427 1428 if (storage_class == SpvStorageClassInput) { 1429 assert(function_id_ == 0); 1430 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1431 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 1432 "Vulkan spec doesn't allow BuiltIn PointSize to be used for " 1433 "variables with Input storage class if execution model is Vertex.", 1434 SpvExecutionModelVertex, decoration, built_in_inst, 1435 referenced_from_inst, std::placeholders::_1)); 1436 } 1437 1438 for (const SpvExecutionModel execution_model : execution_models_) { 1439 switch (execution_model) { 1440 case SpvExecutionModelVertex: { 1441 if (spv_result_t error = ValidateF32( 1442 decoration, built_in_inst, 1443 [this, &referenced_from_inst]( 1444 const std::string& message) -> spv_result_t { 1445 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1446 << "According to the Vulkan spec BuiltIn PointSize " 1447 "variable needs to be a 32-bit float scalar. " 1448 << message; 1449 })) { 1450 return error; 1451 } 1452 break; 1453 } 1454 case SpvExecutionModelTessellationControl: 1455 case SpvExecutionModelTessellationEvaluation: 1456 case SpvExecutionModelGeometry: 1457 case SpvExecutionModelMeshNV: { 1458 // PointSize can be a per-vertex variable for tessellation control, 1459 // tessellation evaluation and geometry shader stages. In such cases 1460 // variables will have an array of 32-bit floats. 1461 if (decoration.struct_member_index() != Decoration::kInvalidMember) { 1462 // The array is on the variable, so this must be a 32-bit float. 1463 if (spv_result_t error = ValidateF32( 1464 decoration, built_in_inst, 1465 [this, &referenced_from_inst]( 1466 const std::string& message) -> spv_result_t { 1467 return _.diag(SPV_ERROR_INVALID_DATA, 1468 &referenced_from_inst) 1469 << "According to the Vulkan spec BuiltIn " 1470 "PointSize variable needs to be a 32-bit " 1471 "float scalar. " 1472 << message; 1473 })) { 1474 return error; 1475 } 1476 } else { 1477 if (spv_result_t error = ValidateOptionalArrayedF32( 1478 decoration, built_in_inst, 1479 [this, &referenced_from_inst]( 1480 const std::string& message) -> spv_result_t { 1481 return _.diag(SPV_ERROR_INVALID_DATA, 1482 &referenced_from_inst) 1483 << "According to the Vulkan spec BuiltIn " 1484 "PointSize variable needs to be a 32-bit " 1485 "float scalar. " 1486 << message; 1487 })) { 1488 return error; 1489 } 1490 } 1491 break; 1492 } 1493 1494 default: { 1495 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1496 << "Vulkan spec allows BuiltIn PointSize to be used only with " 1497 "Vertex, TessellationControl, TessellationEvaluation or " 1498 "Geometry execution models. " 1499 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1500 referenced_from_inst, execution_model); 1501 } 1502 } 1503 } 1504 } 1505 1506 if (function_id_ == 0) { 1507 // Propagate this rule to all dependant ids in the global scope. 1508 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1509 &BuiltInsValidator::ValidatePointSizeAtReference, this, decoration, 1510 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1511 } 1512 1513 return SPV_SUCCESS; 1514 } 1515 1516 spv_result_t BuiltInsValidator::ValidatePositionAtDefinition( 1517 const Decoration& decoration, const Instruction& inst) { 1518 // Seed at reference checks with this built-in. 1519 return ValidatePositionAtReference(decoration, inst, inst, inst); 1520 } 1521 1522 spv_result_t BuiltInsValidator::ValidatePositionAtReference( 1523 const Decoration& decoration, const Instruction& built_in_inst, 1524 const Instruction& referenced_inst, 1525 const Instruction& referenced_from_inst) { 1526 if (spvIsVulkanEnv(_.context()->target_env)) { 1527 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1528 if (storage_class != SpvStorageClassMax && 1529 storage_class != SpvStorageClassInput && 1530 storage_class != SpvStorageClassOutput) { 1531 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1532 << "Vulkan spec allows BuiltIn Position to be only used for " 1533 "variables with Input or Output storage class. " 1534 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1535 referenced_from_inst) 1536 << " " << GetStorageClassDesc(referenced_from_inst); 1537 } 1538 1539 if (storage_class == SpvStorageClassInput) { 1540 assert(function_id_ == 0); 1541 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1542 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 1543 "Vulkan spec doesn't allow BuiltIn Position to be used for variables " 1544 "with Input storage class if execution model is Vertex.", 1545 SpvExecutionModelVertex, decoration, built_in_inst, 1546 referenced_from_inst, std::placeholders::_1)); 1547 } 1548 1549 for (const SpvExecutionModel execution_model : execution_models_) { 1550 switch (execution_model) { 1551 case SpvExecutionModelVertex: { 1552 if (spv_result_t error = ValidateF32Vec( 1553 decoration, built_in_inst, 4, 1554 [this, &referenced_from_inst]( 1555 const std::string& message) -> spv_result_t { 1556 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1557 << "According to the Vulkan spec BuiltIn Position " 1558 "variable needs to be a 4-component 32-bit float " 1559 "vector. " 1560 << message; 1561 })) { 1562 return error; 1563 } 1564 break; 1565 } 1566 case SpvExecutionModelGeometry: 1567 case SpvExecutionModelTessellationControl: 1568 case SpvExecutionModelTessellationEvaluation: 1569 case SpvExecutionModelMeshNV: { 1570 // Position can be a per-vertex variable for tessellation control, 1571 // tessellation evaluation, geometry and mesh shader stages. In such 1572 // cases variables will have an array of 4-component 32-bit float 1573 // vectors. 1574 if (decoration.struct_member_index() != Decoration::kInvalidMember) { 1575 // The array is on the variable, so this must be a 4-component 1576 // 32-bit float vector. 1577 if (spv_result_t error = ValidateF32Vec( 1578 decoration, built_in_inst, 4, 1579 [this, &referenced_from_inst]( 1580 const std::string& message) -> spv_result_t { 1581 return _.diag(SPV_ERROR_INVALID_DATA, 1582 &referenced_from_inst) 1583 << "According to the Vulkan spec BuiltIn Position " 1584 "variable needs to be a 4-component 32-bit " 1585 "float vector. " 1586 << message; 1587 })) { 1588 return error; 1589 } 1590 } else { 1591 if (spv_result_t error = ValidateOptionalArrayedF32Vec( 1592 decoration, built_in_inst, 4, 1593 [this, &referenced_from_inst]( 1594 const std::string& message) -> spv_result_t { 1595 return _.diag(SPV_ERROR_INVALID_DATA, 1596 &referenced_from_inst) 1597 << "According to the Vulkan spec BuiltIn Position " 1598 "variable needs to be a 4-component 32-bit " 1599 "float vector. " 1600 << message; 1601 })) { 1602 return error; 1603 } 1604 } 1605 break; 1606 } 1607 1608 default: { 1609 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1610 << "Vulkan spec allows BuiltIn Position to be used only " 1611 "with Vertex, TessellationControl, TessellationEvaluation" 1612 " or Geometry execution models. " 1613 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1614 referenced_from_inst, execution_model); 1615 } 1616 } 1617 } 1618 } 1619 1620 if (function_id_ == 0) { 1621 // Propagate this rule to all dependant ids in the global scope. 1622 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1623 &BuiltInsValidator::ValidatePositionAtReference, this, decoration, 1624 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1625 } 1626 1627 return SPV_SUCCESS; 1628 } 1629 1630 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition( 1631 const Decoration& decoration, const Instruction& inst) { 1632 if (spvIsVulkanEnv(_.context()->target_env)) { 1633 if (spv_result_t error = ValidateI32( 1634 decoration, inst, 1635 [this, &inst](const std::string& message) -> spv_result_t { 1636 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1637 << "According to the Vulkan spec BuiltIn PrimitiveId " 1638 "variable needs to be a 32-bit int scalar. " 1639 << message; 1640 })) { 1641 return error; 1642 } 1643 } 1644 1645 // Seed at reference checks with this built-in. 1646 return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst); 1647 } 1648 1649 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference( 1650 const Decoration& decoration, const Instruction& built_in_inst, 1651 const Instruction& referenced_inst, 1652 const Instruction& referenced_from_inst) { 1653 if (spvIsVulkanEnv(_.context()->target_env)) { 1654 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1655 if (storage_class != SpvStorageClassMax && 1656 storage_class != SpvStorageClassInput && 1657 storage_class != SpvStorageClassOutput) { 1658 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1659 << "Vulkan spec allows BuiltIn PrimitiveId to be only used for " 1660 "variables with Input or Output storage class. " 1661 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1662 referenced_from_inst) 1663 << " " << GetStorageClassDesc(referenced_from_inst); 1664 } 1665 1666 if (storage_class == SpvStorageClassOutput) { 1667 assert(function_id_ == 0); 1668 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1669 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 1670 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " 1671 "variables with Output storage class if execution model is " 1672 "TessellationControl.", 1673 SpvExecutionModelTessellationControl, decoration, built_in_inst, 1674 referenced_from_inst, std::placeholders::_1)); 1675 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1676 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 1677 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " 1678 "variables with Output storage class if execution model is " 1679 "TessellationEvaluation.", 1680 SpvExecutionModelTessellationEvaluation, decoration, built_in_inst, 1681 referenced_from_inst, std::placeholders::_1)); 1682 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1683 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 1684 "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " 1685 "variables with Output storage class if execution model is " 1686 "Fragment.", 1687 SpvExecutionModelFragment, decoration, built_in_inst, 1688 referenced_from_inst, std::placeholders::_1)); 1689 } 1690 1691 for (const SpvExecutionModel execution_model : execution_models_) { 1692 switch (execution_model) { 1693 case SpvExecutionModelFragment: 1694 case SpvExecutionModelTessellationControl: 1695 case SpvExecutionModelTessellationEvaluation: 1696 case SpvExecutionModelGeometry: 1697 case SpvExecutionModelMeshNV: 1698 case SpvExecutionModelRayGenerationNV: 1699 case SpvExecutionModelIntersectionNV: 1700 case SpvExecutionModelAnyHitNV: 1701 case SpvExecutionModelClosestHitNV: 1702 case SpvExecutionModelMissNV: 1703 case SpvExecutionModelCallableNV: { 1704 // Ok. 1705 break; 1706 } 1707 1708 default: { 1709 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1710 << "Vulkan spec allows BuiltIn PrimitiveId to be used only " 1711 "with Fragment, TessellationControl, " 1712 "TessellationEvaluation or Geometry execution models. " 1713 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1714 referenced_from_inst, execution_model); 1715 } 1716 } 1717 } 1718 } 1719 1720 if (function_id_ == 0) { 1721 // Propagate this rule to all dependant ids in the global scope. 1722 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1723 &BuiltInsValidator::ValidatePrimitiveIdAtReference, this, decoration, 1724 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1725 } 1726 1727 return SPV_SUCCESS; 1728 } 1729 1730 spv_result_t BuiltInsValidator::ValidateSampleIdAtDefinition( 1731 const Decoration& decoration, const Instruction& inst) { 1732 if (spvIsVulkanEnv(_.context()->target_env)) { 1733 if (spv_result_t error = ValidateI32( 1734 decoration, inst, 1735 [this, &inst](const std::string& message) -> spv_result_t { 1736 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1737 << "According to the Vulkan spec BuiltIn SampleId " 1738 "variable needs to be a 32-bit int scalar. " 1739 << message; 1740 })) { 1741 return error; 1742 } 1743 } 1744 1745 // Seed at reference checks with this built-in. 1746 return ValidateSampleIdAtReference(decoration, inst, inst, inst); 1747 } 1748 1749 spv_result_t BuiltInsValidator::ValidateSampleIdAtReference( 1750 const Decoration& decoration, const Instruction& built_in_inst, 1751 const Instruction& referenced_inst, 1752 const Instruction& referenced_from_inst) { 1753 if (spvIsVulkanEnv(_.context()->target_env)) { 1754 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1755 if (storage_class != SpvStorageClassMax && 1756 storage_class != SpvStorageClassInput) { 1757 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1758 << "Vulkan spec allows BuiltIn SampleId to be only used for " 1759 "variables with Input storage class. " 1760 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1761 referenced_from_inst) 1762 << " " << GetStorageClassDesc(referenced_from_inst); 1763 } 1764 1765 for (const SpvExecutionModel execution_model : execution_models_) { 1766 if (execution_model != SpvExecutionModelFragment) { 1767 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1768 << "Vulkan spec allows BuiltIn SampleId to be used only with " 1769 "Fragment execution model. " 1770 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1771 referenced_from_inst, execution_model); 1772 } 1773 } 1774 } 1775 1776 if (function_id_ == 0) { 1777 // Propagate this rule to all dependant ids in the global scope. 1778 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1779 &BuiltInsValidator::ValidateSampleIdAtReference, this, decoration, 1780 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1781 } 1782 1783 return SPV_SUCCESS; 1784 } 1785 1786 spv_result_t BuiltInsValidator::ValidateSampleMaskAtDefinition( 1787 const Decoration& decoration, const Instruction& inst) { 1788 if (spvIsVulkanEnv(_.context()->target_env)) { 1789 if (spv_result_t error = ValidateI32Arr( 1790 decoration, inst, 1791 [this, &inst](const std::string& message) -> spv_result_t { 1792 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1793 << "According to the Vulkan spec BuiltIn SampleMask " 1794 "variable needs to be a 32-bit int array. " 1795 << message; 1796 })) { 1797 return error; 1798 } 1799 } 1800 1801 // Seed at reference checks with this built-in. 1802 return ValidateSampleMaskAtReference(decoration, inst, inst, inst); 1803 } 1804 1805 spv_result_t BuiltInsValidator::ValidateSampleMaskAtReference( 1806 const Decoration& decoration, const Instruction& built_in_inst, 1807 const Instruction& referenced_inst, 1808 const Instruction& referenced_from_inst) { 1809 if (spvIsVulkanEnv(_.context()->target_env)) { 1810 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1811 if (storage_class != SpvStorageClassMax && 1812 storage_class != SpvStorageClassInput && 1813 storage_class != SpvStorageClassOutput) { 1814 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1815 << "Vulkan spec allows BuiltIn SampleMask to be only used for " 1816 "variables with Input or Output storage class. " 1817 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1818 referenced_from_inst) 1819 << " " << GetStorageClassDesc(referenced_from_inst); 1820 } 1821 1822 for (const SpvExecutionModel execution_model : execution_models_) { 1823 if (execution_model != SpvExecutionModelFragment) { 1824 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1825 << "Vulkan spec allows BuiltIn SampleMask to be used only " 1826 "with " 1827 "Fragment execution model. " 1828 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1829 referenced_from_inst, execution_model); 1830 } 1831 } 1832 } 1833 1834 if (function_id_ == 0) { 1835 // Propagate this rule to all dependant ids in the global scope. 1836 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1837 &BuiltInsValidator::ValidateSampleMaskAtReference, this, decoration, 1838 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1839 } 1840 1841 return SPV_SUCCESS; 1842 } 1843 1844 spv_result_t BuiltInsValidator::ValidateSamplePositionAtDefinition( 1845 const Decoration& decoration, const Instruction& inst) { 1846 if (spvIsVulkanEnv(_.context()->target_env)) { 1847 if (spv_result_t error = ValidateF32Vec( 1848 decoration, inst, 2, 1849 [this, &inst](const std::string& message) -> spv_result_t { 1850 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1851 << "According to the Vulkan spec BuiltIn SamplePosition " 1852 "variable needs to be a 2-component 32-bit float " 1853 "vector. " 1854 << message; 1855 })) { 1856 return error; 1857 } 1858 } 1859 1860 // Seed at reference checks with this built-in. 1861 return ValidateSamplePositionAtReference(decoration, inst, inst, inst); 1862 } 1863 1864 spv_result_t BuiltInsValidator::ValidateSamplePositionAtReference( 1865 const Decoration& decoration, const Instruction& built_in_inst, 1866 const Instruction& referenced_inst, 1867 const Instruction& referenced_from_inst) { 1868 if (spvIsVulkanEnv(_.context()->target_env)) { 1869 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1870 if (storage_class != SpvStorageClassMax && 1871 storage_class != SpvStorageClassInput) { 1872 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1873 << "Vulkan spec allows BuiltIn SamplePosition to be only used " 1874 "for " 1875 "variables with Input storage class. " 1876 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1877 referenced_from_inst) 1878 << " " << GetStorageClassDesc(referenced_from_inst); 1879 } 1880 1881 for (const SpvExecutionModel execution_model : execution_models_) { 1882 if (execution_model != SpvExecutionModelFragment) { 1883 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1884 << "Vulkan spec allows BuiltIn SamplePosition to be used only " 1885 "with " 1886 "Fragment execution model. " 1887 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1888 referenced_from_inst, execution_model); 1889 } 1890 } 1891 } 1892 1893 if (function_id_ == 0) { 1894 // Propagate this rule to all dependant ids in the global scope. 1895 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1896 &BuiltInsValidator::ValidateSamplePositionAtReference, this, decoration, 1897 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1898 } 1899 1900 return SPV_SUCCESS; 1901 } 1902 1903 spv_result_t BuiltInsValidator::ValidateTessCoordAtDefinition( 1904 const Decoration& decoration, const Instruction& inst) { 1905 if (spvIsVulkanEnv(_.context()->target_env)) { 1906 if (spv_result_t error = ValidateF32Vec( 1907 decoration, inst, 3, 1908 [this, &inst](const std::string& message) -> spv_result_t { 1909 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1910 << "According to the Vulkan spec BuiltIn TessCoord " 1911 "variable needs to be a 3-component 32-bit float " 1912 "vector. " 1913 << message; 1914 })) { 1915 return error; 1916 } 1917 } 1918 1919 // Seed at reference checks with this built-in. 1920 return ValidateTessCoordAtReference(decoration, inst, inst, inst); 1921 } 1922 1923 spv_result_t BuiltInsValidator::ValidateTessCoordAtReference( 1924 const Decoration& decoration, const Instruction& built_in_inst, 1925 const Instruction& referenced_inst, 1926 const Instruction& referenced_from_inst) { 1927 if (spvIsVulkanEnv(_.context()->target_env)) { 1928 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 1929 if (storage_class != SpvStorageClassMax && 1930 storage_class != SpvStorageClassInput) { 1931 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1932 << "Vulkan spec allows BuiltIn TessCoord to be only used for " 1933 "variables with Input storage class. " 1934 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1935 referenced_from_inst) 1936 << " " << GetStorageClassDesc(referenced_from_inst); 1937 } 1938 1939 for (const SpvExecutionModel execution_model : execution_models_) { 1940 if (execution_model != SpvExecutionModelTessellationEvaluation) { 1941 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 1942 << "Vulkan spec allows BuiltIn TessCoord to be used only with " 1943 "TessellationEvaluation execution model. " 1944 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 1945 referenced_from_inst, execution_model); 1946 } 1947 } 1948 } 1949 1950 if (function_id_ == 0) { 1951 // Propagate this rule to all dependant ids in the global scope. 1952 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 1953 &BuiltInsValidator::ValidateTessCoordAtReference, this, decoration, 1954 built_in_inst, referenced_from_inst, std::placeholders::_1)); 1955 } 1956 1957 return SPV_SUCCESS; 1958 } 1959 1960 spv_result_t BuiltInsValidator::ValidateTessLevelOuterAtDefinition( 1961 const Decoration& decoration, const Instruction& inst) { 1962 if (spvIsVulkanEnv(_.context()->target_env)) { 1963 if (spv_result_t error = ValidateF32Arr( 1964 decoration, inst, 4, 1965 [this, &inst](const std::string& message) -> spv_result_t { 1966 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1967 << "According to the Vulkan spec BuiltIn TessLevelOuter " 1968 "variable needs to be a 4-component 32-bit float " 1969 "array. " 1970 << message; 1971 })) { 1972 return error; 1973 } 1974 } 1975 1976 // Seed at reference checks with this built-in. 1977 return ValidateTessLevelAtReference(decoration, inst, inst, inst); 1978 } 1979 1980 spv_result_t BuiltInsValidator::ValidateTessLevelInnerAtDefinition( 1981 const Decoration& decoration, const Instruction& inst) { 1982 if (spvIsVulkanEnv(_.context()->target_env)) { 1983 if (spv_result_t error = ValidateF32Arr( 1984 decoration, inst, 2, 1985 [this, &inst](const std::string& message) -> spv_result_t { 1986 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 1987 << "According to the Vulkan spec BuiltIn TessLevelOuter " 1988 "variable needs to be a 2-component 32-bit float " 1989 "array. " 1990 << message; 1991 })) { 1992 return error; 1993 } 1994 } 1995 1996 // Seed at reference checks with this built-in. 1997 return ValidateTessLevelAtReference(decoration, inst, inst, inst); 1998 } 1999 2000 spv_result_t BuiltInsValidator::ValidateTessLevelAtReference( 2001 const Decoration& decoration, const Instruction& built_in_inst, 2002 const Instruction& referenced_inst, 2003 const Instruction& referenced_from_inst) { 2004 if (spvIsVulkanEnv(_.context()->target_env)) { 2005 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 2006 if (storage_class != SpvStorageClassMax && 2007 storage_class != SpvStorageClassInput && 2008 storage_class != SpvStorageClassOutput) { 2009 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2010 << "Vulkan spec allows BuiltIn " 2011 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2012 decoration.params()[0]) 2013 << " to be only used for variables with Input or Output storage " 2014 "class. " 2015 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2016 referenced_from_inst) 2017 << " " << GetStorageClassDesc(referenced_from_inst); 2018 } 2019 2020 if (storage_class == SpvStorageClassInput) { 2021 assert(function_id_ == 0); 2022 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2023 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 2024 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " 2025 "used " 2026 "for variables with Input storage class if execution model is " 2027 "TessellationControl.", 2028 SpvExecutionModelTessellationControl, decoration, built_in_inst, 2029 referenced_from_inst, std::placeholders::_1)); 2030 } 2031 2032 if (storage_class == SpvStorageClassOutput) { 2033 assert(function_id_ == 0); 2034 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2035 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 2036 "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " 2037 "used " 2038 "for variables with Output storage class if execution model is " 2039 "TessellationEvaluation.", 2040 SpvExecutionModelTessellationEvaluation, decoration, built_in_inst, 2041 referenced_from_inst, std::placeholders::_1)); 2042 } 2043 2044 for (const SpvExecutionModel execution_model : execution_models_) { 2045 switch (execution_model) { 2046 case SpvExecutionModelTessellationControl: 2047 case SpvExecutionModelTessellationEvaluation: { 2048 // Ok. 2049 break; 2050 } 2051 2052 default: { 2053 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2054 << "Vulkan spec allows BuiltIn " 2055 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2056 decoration.params()[0]) 2057 << " to be used only with TessellationControl or " 2058 "TessellationEvaluation execution models. " 2059 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2060 referenced_from_inst, execution_model); 2061 } 2062 } 2063 } 2064 } 2065 2066 if (function_id_ == 0) { 2067 // Propagate this rule to all dependant ids in the global scope. 2068 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2069 &BuiltInsValidator::ValidateTessLevelAtReference, this, decoration, 2070 built_in_inst, referenced_from_inst, std::placeholders::_1)); 2071 } 2072 2073 return SPV_SUCCESS; 2074 } 2075 2076 spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition( 2077 const Decoration& decoration, const Instruction& inst) { 2078 if (spvIsVulkanEnv(_.context()->target_env)) { 2079 if (spv_result_t error = ValidateI32( 2080 decoration, inst, 2081 [this, &inst](const std::string& message) -> spv_result_t { 2082 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 2083 << "According to the Vulkan spec BuiltIn VertexIndex " 2084 "variable needs to be a 32-bit int scalar. " 2085 << message; 2086 })) { 2087 return error; 2088 } 2089 } 2090 2091 // Seed at reference checks with this built-in. 2092 return ValidateVertexIndexAtReference(decoration, inst, inst, inst); 2093 } 2094 2095 spv_result_t BuiltInsValidator::ValidateVertexIdOrInstanceIdAtDefinition( 2096 const Decoration& decoration, const Instruction& inst) { 2097 const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); 2098 bool allow_instance_id = _.HasCapability(SpvCapabilityRayTracingNV) && 2099 label == SpvBuiltInInstanceId; 2100 if (spvIsVulkanEnv(_.context()->target_env) && !allow_instance_id) { 2101 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 2102 << "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId " 2103 "to be used."; 2104 } 2105 2106 if (label == SpvBuiltInInstanceId) { 2107 return ValidateInstanceIdAtReference(decoration, inst, inst, inst); 2108 } 2109 return SPV_SUCCESS; 2110 } 2111 2112 spv_result_t BuiltInsValidator::ValidateInstanceIdAtReference( 2113 const Decoration& decoration, const Instruction& built_in_inst, 2114 const Instruction& referenced_inst, 2115 const Instruction& referenced_from_inst) { 2116 if (spvIsVulkanEnv(_.context()->target_env)) { 2117 for (const SpvExecutionModel execution_model : execution_models_) { 2118 switch (execution_model) { 2119 case SpvExecutionModelIntersectionNV: 2120 case SpvExecutionModelClosestHitNV: 2121 case SpvExecutionModelAnyHitNV: 2122 // Do nothing, valid stages 2123 break; 2124 default: 2125 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2126 << "Vulkan spec allows BuiltIn InstanceId to be used " 2127 "only with IntersectionNV, ClosestHitNV and AnyHitNV " 2128 "execution models. " 2129 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2130 referenced_from_inst); 2131 break; 2132 } 2133 } 2134 } 2135 2136 if (function_id_ == 0) { 2137 // Propagate this rule to all dependant ids in the global scope. 2138 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2139 &BuiltInsValidator::ValidateInstanceIdAtReference, this, decoration, 2140 built_in_inst, referenced_from_inst, std::placeholders::_1)); 2141 } 2142 2143 return SPV_SUCCESS; 2144 } 2145 2146 spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference( 2147 const Decoration& decoration, const Instruction& built_in_inst, 2148 const Instruction& referenced_inst, 2149 const Instruction& referenced_from_inst) { 2150 if (spvIsVulkanEnv(_.context()->target_env)) { 2151 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 2152 if (storage_class != SpvStorageClassMax && 2153 storage_class != SpvStorageClassInput) { 2154 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2155 << "Vulkan spec allows BuiltIn VertexIndex to be only used for " 2156 "variables with Input storage class. " 2157 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2158 referenced_from_inst) 2159 << " " << GetStorageClassDesc(referenced_from_inst); 2160 } 2161 2162 for (const SpvExecutionModel execution_model : execution_models_) { 2163 if (execution_model != SpvExecutionModelVertex) { 2164 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2165 << "Vulkan spec allows BuiltIn VertexIndex to be used only " 2166 "with " 2167 "Vertex execution model. " 2168 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2169 referenced_from_inst, execution_model); 2170 } 2171 } 2172 } 2173 2174 if (function_id_ == 0) { 2175 // Propagate this rule to all dependant ids in the global scope. 2176 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2177 &BuiltInsValidator::ValidateVertexIndexAtReference, this, decoration, 2178 built_in_inst, referenced_from_inst, std::placeholders::_1)); 2179 } 2180 2181 return SPV_SUCCESS; 2182 } 2183 2184 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition( 2185 const Decoration& decoration, const Instruction& inst) { 2186 if (spvIsVulkanEnv(_.context()->target_env)) { 2187 if (spv_result_t error = ValidateI32( 2188 decoration, inst, 2189 [this, &decoration, 2190 &inst](const std::string& message) -> spv_result_t { 2191 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 2192 << "According to the Vulkan spec BuiltIn " 2193 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2194 decoration.params()[0]) 2195 << "variable needs to be a 32-bit int scalar. " << message; 2196 })) { 2197 return error; 2198 } 2199 } 2200 2201 // Seed at reference checks with this built-in. 2202 return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst); 2203 } 2204 2205 spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( 2206 const Decoration& decoration, const Instruction& built_in_inst, 2207 const Instruction& referenced_inst, 2208 const Instruction& referenced_from_inst) { 2209 if (spvIsVulkanEnv(_.context()->target_env)) { 2210 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 2211 if (storage_class != SpvStorageClassMax && 2212 storage_class != SpvStorageClassInput && 2213 storage_class != SpvStorageClassOutput) { 2214 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2215 << "Vulkan spec allows BuiltIn " 2216 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2217 decoration.params()[0]) 2218 << " to be only used for variables with Input or Output storage " 2219 "class. " 2220 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2221 referenced_from_inst) 2222 << " " << GetStorageClassDesc(referenced_from_inst); 2223 } 2224 2225 if (storage_class == SpvStorageClassInput) { 2226 assert(function_id_ == 0); 2227 for (const auto em : 2228 {SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation, 2229 SpvExecutionModelGeometry}) { 2230 id_to_at_reference_checks_[referenced_from_inst.id()].push_back( 2231 std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, 2232 this, 2233 "Vulkan spec doesn't allow BuiltIn Layer and " 2234 "ViewportIndex to be " 2235 "used for variables with Input storage class if " 2236 "execution model is Vertex, TessellationEvaluation, or " 2237 "Geometry.", 2238 em, decoration, built_in_inst, referenced_from_inst, 2239 std::placeholders::_1)); 2240 } 2241 } 2242 2243 if (storage_class == SpvStorageClassOutput) { 2244 assert(function_id_ == 0); 2245 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2246 &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 2247 "Vulkan spec doesn't allow BuiltIn Layer and " 2248 "ViewportIndex to be " 2249 "used for variables with Output storage class if " 2250 "execution model is " 2251 "Fragment.", 2252 SpvExecutionModelFragment, decoration, built_in_inst, 2253 referenced_from_inst, std::placeholders::_1)); 2254 } 2255 2256 for (const SpvExecutionModel execution_model : execution_models_) { 2257 switch (execution_model) { 2258 case SpvExecutionModelGeometry: 2259 case SpvExecutionModelFragment: 2260 case SpvExecutionModelMeshNV: { 2261 // Ok. 2262 break; 2263 case SpvExecutionModelVertex: 2264 case SpvExecutionModelTessellationEvaluation: 2265 if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) { 2266 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2267 << "Using BuiltIn " 2268 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2269 decoration.params()[0]) 2270 << " in Vertex or Tessellation execution model requires " 2271 "the ShaderViewportIndexLayerEXT capability."; 2272 } 2273 break; 2274 } 2275 default: { 2276 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2277 << "Vulkan spec allows BuiltIn " 2278 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2279 decoration.params()[0]) 2280 << " to be used only with Vertex, TessellationEvaluation, " 2281 "Geometry, or Fragment execution models. " 2282 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2283 referenced_from_inst, execution_model); 2284 } 2285 } 2286 } 2287 } 2288 2289 if (function_id_ == 0) { 2290 // Propagate this rule to all dependant ids in the global scope. 2291 id_to_at_reference_checks_[referenced_from_inst.id()].push_back( 2292 std::bind(&BuiltInsValidator::ValidateLayerOrViewportIndexAtReference, 2293 this, decoration, built_in_inst, referenced_from_inst, 2294 std::placeholders::_1)); 2295 } 2296 2297 return SPV_SUCCESS; 2298 } 2299 2300 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition( 2301 const Decoration& decoration, const Instruction& inst) { 2302 if (spvIsVulkanEnv(_.context()->target_env)) { 2303 if (spv_result_t error = ValidateI32Vec( 2304 decoration, inst, 3, 2305 [this, &decoration, 2306 &inst](const std::string& message) -> spv_result_t { 2307 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 2308 << "According to the Vulkan spec BuiltIn " 2309 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2310 decoration.params()[0]) 2311 << " variable needs to be a 3-component 32-bit int " 2312 "vector. " 2313 << message; 2314 })) { 2315 return error; 2316 } 2317 } 2318 2319 // Seed at reference checks with this built-in. 2320 return ValidateComputeShaderI32Vec3InputAtReference(decoration, inst, inst, 2321 inst); 2322 } 2323 2324 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( 2325 const Decoration& decoration, const Instruction& built_in_inst, 2326 const Instruction& referenced_inst, 2327 const Instruction& referenced_from_inst) { 2328 if (spvIsVulkanEnv(_.context()->target_env)) { 2329 const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); 2330 if (storage_class != SpvStorageClassMax && 2331 storage_class != SpvStorageClassInput) { 2332 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2333 << "Vulkan spec allows BuiltIn " 2334 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2335 decoration.params()[0]) 2336 << " to be only used for variables with Input storage class. " 2337 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2338 referenced_from_inst) 2339 << " " << GetStorageClassDesc(referenced_from_inst); 2340 } 2341 2342 for (const SpvExecutionModel execution_model : execution_models_) { 2343 if (execution_model != SpvExecutionModelGLCompute && 2344 execution_model != SpvExecutionModelTaskNV && 2345 execution_model != SpvExecutionModelMeshNV) { 2346 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2347 << "Vulkan spec allows BuiltIn " 2348 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2349 decoration.params()[0]) 2350 << " to be used only with GLCompute execution model. " 2351 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2352 referenced_from_inst, execution_model); 2353 } 2354 } 2355 } 2356 2357 if (function_id_ == 0) { 2358 // Propagate this rule to all dependant ids in the global scope. 2359 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2360 &BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference, this, 2361 decoration, built_in_inst, referenced_from_inst, 2362 std::placeholders::_1)); 2363 } 2364 2365 return SPV_SUCCESS; 2366 } 2367 2368 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition( 2369 const Decoration& decoration, const Instruction& inst) { 2370 if (spvIsVulkanEnv(_.context()->target_env)) { 2371 if (!spvOpcodeIsConstant(inst.opcode())) { 2372 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 2373 << "Vulkan spec requires BuiltIn WorkgroupSize to be a " 2374 "constant. " 2375 << GetIdDesc(inst) << " is not a constant."; 2376 } 2377 2378 if (spv_result_t error = ValidateI32Vec( 2379 decoration, inst, 3, 2380 [this, &inst](const std::string& message) -> spv_result_t { 2381 return _.diag(SPV_ERROR_INVALID_DATA, &inst) 2382 << "According to the Vulkan spec BuiltIn WorkgroupSize " 2383 "variable " 2384 "needs to be a 3-component 32-bit int vector. " 2385 << message; 2386 })) { 2387 return error; 2388 } 2389 } 2390 2391 // Seed at reference checks with this built-in. 2392 return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst); 2393 } 2394 2395 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference( 2396 const Decoration& decoration, const Instruction& built_in_inst, 2397 const Instruction& referenced_inst, 2398 const Instruction& referenced_from_inst) { 2399 if (spvIsVulkanEnv(_.context()->target_env)) { 2400 for (const SpvExecutionModel execution_model : execution_models_) { 2401 if (execution_model != SpvExecutionModelGLCompute) { 2402 return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) 2403 << "Vulkan spec allows BuiltIn " 2404 << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, 2405 decoration.params()[0]) 2406 << " to be used only with GLCompute execution model. " 2407 << GetReferenceDesc(decoration, built_in_inst, referenced_inst, 2408 referenced_from_inst, execution_model); 2409 } 2410 } 2411 } 2412 2413 if (function_id_ == 0) { 2414 // Propagate this rule to all dependant ids in the global scope. 2415 id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( 2416 &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration, 2417 built_in_inst, referenced_from_inst, std::placeholders::_1)); 2418 } 2419 2420 return SPV_SUCCESS; 2421 } 2422 2423 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( 2424 const Decoration& decoration, const Instruction& inst) { 2425 const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); 2426 // If you are adding a new BuiltIn enum, please register it here. 2427 // If the newly added enum has validation rules associated with it 2428 // consider leaving a TODO and/or creating an issue. 2429 switch (label) { 2430 case SpvBuiltInClipDistance: 2431 case SpvBuiltInCullDistance: { 2432 return ValidateClipOrCullDistanceAtDefinition(decoration, inst); 2433 } 2434 case SpvBuiltInFragCoord: { 2435 return ValidateFragCoordAtDefinition(decoration, inst); 2436 } 2437 case SpvBuiltInFragDepth: { 2438 return ValidateFragDepthAtDefinition(decoration, inst); 2439 } 2440 case SpvBuiltInFrontFacing: { 2441 return ValidateFrontFacingAtDefinition(decoration, inst); 2442 } 2443 case SpvBuiltInGlobalInvocationId: 2444 case SpvBuiltInLocalInvocationId: 2445 case SpvBuiltInNumWorkgroups: 2446 case SpvBuiltInWorkgroupId: { 2447 return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst); 2448 } 2449 case SpvBuiltInHelperInvocation: { 2450 return ValidateHelperInvocationAtDefinition(decoration, inst); 2451 } 2452 case SpvBuiltInInvocationId: { 2453 return ValidateInvocationIdAtDefinition(decoration, inst); 2454 } 2455 case SpvBuiltInInstanceIndex: { 2456 return ValidateInstanceIndexAtDefinition(decoration, inst); 2457 } 2458 case SpvBuiltInLayer: 2459 case SpvBuiltInViewportIndex: { 2460 return ValidateLayerOrViewportIndexAtDefinition(decoration, inst); 2461 } 2462 case SpvBuiltInPatchVertices: { 2463 return ValidatePatchVerticesAtDefinition(decoration, inst); 2464 } 2465 case SpvBuiltInPointCoord: { 2466 return ValidatePointCoordAtDefinition(decoration, inst); 2467 } 2468 case SpvBuiltInPointSize: { 2469 return ValidatePointSizeAtDefinition(decoration, inst); 2470 } 2471 case SpvBuiltInPosition: { 2472 return ValidatePositionAtDefinition(decoration, inst); 2473 } 2474 case SpvBuiltInPrimitiveId: { 2475 return ValidatePrimitiveIdAtDefinition(decoration, inst); 2476 } 2477 case SpvBuiltInSampleId: { 2478 return ValidateSampleIdAtDefinition(decoration, inst); 2479 } 2480 case SpvBuiltInSampleMask: { 2481 return ValidateSampleMaskAtDefinition(decoration, inst); 2482 } 2483 case SpvBuiltInSamplePosition: { 2484 return ValidateSamplePositionAtDefinition(decoration, inst); 2485 } 2486 case SpvBuiltInTessCoord: { 2487 return ValidateTessCoordAtDefinition(decoration, inst); 2488 } 2489 case SpvBuiltInTessLevelOuter: { 2490 return ValidateTessLevelOuterAtDefinition(decoration, inst); 2491 } 2492 case SpvBuiltInTessLevelInner: { 2493 return ValidateTessLevelInnerAtDefinition(decoration, inst); 2494 } 2495 case SpvBuiltInVertexIndex: { 2496 return ValidateVertexIndexAtDefinition(decoration, inst); 2497 } 2498 case SpvBuiltInWorkgroupSize: { 2499 return ValidateWorkgroupSizeAtDefinition(decoration, inst); 2500 } 2501 case SpvBuiltInVertexId: 2502 case SpvBuiltInInstanceId: { 2503 return ValidateVertexIdOrInstanceIdAtDefinition(decoration, inst); 2504 } 2505 case SpvBuiltInLocalInvocationIndex: 2506 case SpvBuiltInWorkDim: 2507 case SpvBuiltInGlobalSize: 2508 case SpvBuiltInEnqueuedWorkgroupSize: 2509 case SpvBuiltInGlobalOffset: 2510 case SpvBuiltInGlobalLinearId: 2511 case SpvBuiltInSubgroupSize: 2512 case SpvBuiltInSubgroupMaxSize: 2513 case SpvBuiltInNumSubgroups: 2514 case SpvBuiltInNumEnqueuedSubgroups: 2515 case SpvBuiltInSubgroupId: 2516 case SpvBuiltInSubgroupLocalInvocationId: 2517 case SpvBuiltInSubgroupEqMaskKHR: 2518 case SpvBuiltInSubgroupGeMaskKHR: 2519 case SpvBuiltInSubgroupGtMaskKHR: 2520 case SpvBuiltInSubgroupLeMaskKHR: 2521 case SpvBuiltInSubgroupLtMaskKHR: 2522 case SpvBuiltInBaseVertex: 2523 case SpvBuiltInBaseInstance: 2524 case SpvBuiltInDrawIndex: 2525 case SpvBuiltInDeviceIndex: 2526 case SpvBuiltInViewIndex: 2527 case SpvBuiltInBaryCoordNoPerspAMD: 2528 case SpvBuiltInBaryCoordNoPerspCentroidAMD: 2529 case SpvBuiltInBaryCoordNoPerspSampleAMD: 2530 case SpvBuiltInBaryCoordSmoothAMD: 2531 case SpvBuiltInBaryCoordSmoothCentroidAMD: 2532 case SpvBuiltInBaryCoordSmoothSampleAMD: 2533 case SpvBuiltInBaryCoordPullModelAMD: 2534 case SpvBuiltInFragStencilRefEXT: 2535 case SpvBuiltInViewportMaskNV: 2536 case SpvBuiltInSecondaryPositionNV: 2537 case SpvBuiltInSecondaryViewportMaskNV: 2538 case SpvBuiltInPositionPerViewNV: 2539 case SpvBuiltInViewportMaskPerViewNV: 2540 case SpvBuiltInFullyCoveredEXT: 2541 case SpvBuiltInMax: 2542 case SpvBuiltInTaskCountNV: 2543 case SpvBuiltInPrimitiveCountNV: 2544 case SpvBuiltInPrimitiveIndicesNV: 2545 case SpvBuiltInClipDistancePerViewNV: 2546 case SpvBuiltInCullDistancePerViewNV: 2547 case SpvBuiltInLayerPerViewNV: 2548 case SpvBuiltInMeshViewCountNV: 2549 case SpvBuiltInMeshViewIndicesNV: 2550 case SpvBuiltInBaryCoordNV: 2551 case SpvBuiltInBaryCoordNoPerspNV: 2552 case SpvBuiltInFragmentSizeNV: // alias SpvBuiltInFragSizeEXT 2553 case SpvBuiltInInvocationsPerPixelNV: // alias 2554 // SpvBuiltInFragInvocationCountEXT 2555 case SpvBuiltInLaunchIdNV: 2556 case SpvBuiltInLaunchSizeNV: 2557 case SpvBuiltInWorldRayOriginNV: 2558 case SpvBuiltInWorldRayDirectionNV: 2559 case SpvBuiltInObjectRayOriginNV: 2560 case SpvBuiltInObjectRayDirectionNV: 2561 case SpvBuiltInRayTminNV: 2562 case SpvBuiltInRayTmaxNV: 2563 case SpvBuiltInInstanceCustomIndexNV: 2564 case SpvBuiltInObjectToWorldNV: 2565 case SpvBuiltInWorldToObjectNV: 2566 case SpvBuiltInHitTNV: 2567 case SpvBuiltInHitKindNV: 2568 case SpvBuiltInIncomingRayFlagsNV: { 2569 // No validation rules (for the moment). 2570 break; 2571 } 2572 } 2573 return SPV_SUCCESS; 2574 } 2575 2576 spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() { 2577 for (const auto& kv : _.id_decorations()) { 2578 const uint32_t id = kv.first; 2579 const auto& decorations = kv.second; 2580 if (decorations.empty()) { 2581 continue; 2582 } 2583 2584 const Instruction* inst = _.FindDef(id); 2585 assert(inst); 2586 2587 for (const auto& decoration : kv.second) { 2588 if (decoration.dec_type() != SpvDecorationBuiltIn) { 2589 continue; 2590 } 2591 2592 if (spv_result_t error = 2593 ValidateSingleBuiltInAtDefinition(decoration, *inst)) { 2594 return error; 2595 } 2596 } 2597 } 2598 2599 return SPV_SUCCESS; 2600 } 2601 2602 spv_result_t BuiltInsValidator::Run() { 2603 // First pass: validate all built-ins at definition and seed 2604 // id_to_at_reference_checks_ with built-ins. 2605 if (auto error = ValidateBuiltInsAtDefinition()) { 2606 return error; 2607 } 2608 2609 if (id_to_at_reference_checks_.empty()) { 2610 // No validation tasks were seeded. Nothing else to do. 2611 return SPV_SUCCESS; 2612 } 2613 2614 // Second pass: validate every id reference in the module using 2615 // rules in id_to_at_reference_checks_. 2616 for (const Instruction& inst : _.ordered_instructions()) { 2617 Update(inst); 2618 2619 std::set<uint32_t> already_checked; 2620 2621 for (const auto& operand : inst.operands()) { 2622 if (!spvIsIdType(operand.type)) { 2623 // Not id. 2624 continue; 2625 } 2626 2627 const uint32_t id = inst.word(operand.offset); 2628 if (id == inst.id()) { 2629 // No need to check result id. 2630 continue; 2631 } 2632 2633 if (!already_checked.insert(id).second) { 2634 // The instruction has already referenced this id. 2635 continue; 2636 } 2637 2638 // Instruction references the id. Run all checks associated with the id 2639 // on the instruction. id_to_at_reference_checks_ can be modified in the 2640 // process, iterators are safe because it's a tree-based map. 2641 const auto it = id_to_at_reference_checks_.find(id); 2642 if (it != id_to_at_reference_checks_.end()) { 2643 for (const auto& check : it->second) { 2644 if (spv_result_t error = check(inst)) { 2645 return error; 2646 } 2647 } 2648 } 2649 } 2650 } 2651 2652 return SPV_SUCCESS; 2653 } 2654 2655 } // namespace 2656 2657 // Validates correctness of built-in variables. 2658 spv_result_t ValidateBuiltIns(ValidationState_t& _) { 2659 if (!spvIsVulkanEnv(_.context()->target_env)) { 2660 // Early return. All currently implemented rules are based on Vulkan spec. 2661 // 2662 // TODO: If you are adding validation rules for environments other than 2663 // Vulkan (or general rules which are not environment independent), then you 2664 // need to modify or remove this condition. Consider also adding early 2665 // returns into BuiltIn-specific rules, so that the system doesn't spawn new 2666 // rules which don't do anything. 2667 return SPV_SUCCESS; 2668 } 2669 2670 BuiltInsValidator validator(_); 2671 return validator.Run(); 2672 } 2673 2674 } // namespace val 2675 } // namespace spvtools 2676