1 // Copyright (c) 2015-2016 The Khronos Group Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "validate.h" 16 17 #include <cassert> 18 19 #include <algorithm> 20 #include <iostream> 21 #include <unordered_set> 22 #include <utility> 23 #include <vector> 24 25 #include "diagnostic.h" 26 #include "instruction.h" 27 #include "message.h" 28 #include "opcode.h" 29 #include "spirv_validator_options.h" 30 #include "spirv-tools/libspirv.h" 31 #include "val/function.h" 32 #include "val/validation_state.h" 33 34 using libspirv::ValidationState_t; 35 using libspirv::Decoration; 36 using std::function; 37 using std::ignore; 38 using std::make_pair; 39 using std::pair; 40 using std::unordered_set; 41 using std::vector; 42 43 namespace { 44 45 class idUsage { 46 public: 47 idUsage(const spv_opcode_table opcodeTableArg, 48 const spv_operand_table operandTableArg, 49 const spv_ext_inst_table extInstTableArg, 50 const spv_instruction_t* pInsts, const uint64_t instCountArg, 51 const SpvMemoryModel memoryModelArg, 52 const SpvAddressingModel addressingModelArg, 53 const ValidationState_t& module, const vector<uint32_t>& entry_points, 54 spv_position positionArg, const spvtools::MessageConsumer& consumer) 55 : opcodeTable(opcodeTableArg), 56 operandTable(operandTableArg), 57 extInstTable(extInstTableArg), 58 firstInst(pInsts), 59 instCount(instCountArg), 60 memoryModel(memoryModelArg), 61 addressingModel(addressingModelArg), 62 position(positionArg), 63 consumer_(consumer), 64 module_(module), 65 entry_points_(entry_points) {} 66 67 bool isValid(const spv_instruction_t* inst); 68 69 template <SpvOp> 70 bool isValid(const spv_instruction_t* inst, const spv_opcode_desc); 71 72 private: 73 const spv_opcode_table opcodeTable; 74 const spv_operand_table operandTable; 75 const spv_ext_inst_table extInstTable; 76 const spv_instruction_t* const firstInst; 77 const uint64_t instCount; 78 const SpvMemoryModel memoryModel; 79 const SpvAddressingModel addressingModel; 80 spv_position position; 81 const spvtools::MessageConsumer& consumer_; 82 const ValidationState_t& module_; 83 vector<uint32_t> entry_points_; 84 }; 85 86 #define DIAG(INDEX) \ 87 position->index += INDEX; \ 88 libspirv::DiagnosticStream helper(*position, consumer_, \ 89 SPV_ERROR_INVALID_DIAGNOSTIC); \ 90 helper 91 92 #if 0 93 template <> 94 bool idUsage::isValid<SpvOpUndef>(const spv_instruction_t *inst, 95 const spv_opcode_desc) { 96 assert(0 && "Unimplemented!"); 97 return false; 98 } 99 #endif // 0 100 101 template <> 102 bool idUsage::isValid<SpvOpMemberName>(const spv_instruction_t* inst, 103 const spv_opcode_desc) { 104 auto typeIndex = 1; 105 auto type = module_.FindDef(inst->words[typeIndex]); 106 if (!type || SpvOpTypeStruct != type->opcode()) { 107 DIAG(typeIndex) << "OpMemberName Type <id> '" << inst->words[typeIndex] 108 << "' is not a struct type."; 109 return false; 110 } 111 auto memberIndex = 2; 112 auto member = inst->words[memberIndex]; 113 auto memberCount = (uint32_t)(type->words().size() - 2); 114 if (memberCount <= member) { 115 DIAG(memberIndex) << "OpMemberName Member <id> '" 116 << inst->words[memberIndex] 117 << "' index is larger than Type <id> '" << type->id() 118 << "'s member count."; 119 return false; 120 } 121 return true; 122 } 123 124 template <> 125 bool idUsage::isValid<SpvOpLine>(const spv_instruction_t* inst, 126 const spv_opcode_desc) { 127 auto fileIndex = 1; 128 auto file = module_.FindDef(inst->words[fileIndex]); 129 if (!file || SpvOpString != file->opcode()) { 130 DIAG(fileIndex) << "OpLine Target <id> '" << inst->words[fileIndex] 131 << "' is not an OpString."; 132 return false; 133 } 134 return true; 135 } 136 137 template <> 138 bool idUsage::isValid<SpvOpDecorate>(const spv_instruction_t* inst, 139 const spv_opcode_desc) { 140 auto decorationIndex = 2; 141 auto decoration = inst->words[decorationIndex]; 142 if (decoration == SpvDecorationSpecId) { 143 auto targetIndex = 1; 144 auto target = module_.FindDef(inst->words[targetIndex]); 145 if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) { 146 DIAG(targetIndex) << "OpDecorate SpectId decoration target <id> '" 147 << inst->words[decorationIndex] 148 << "' is not a scalar specialization constant."; 149 return false; 150 } 151 } 152 // TODO: Add validations for all decorations. 153 return true; 154 } 155 156 template <> 157 bool idUsage::isValid<SpvOpMemberDecorate>(const spv_instruction_t* inst, 158 const spv_opcode_desc) { 159 auto structTypeIndex = 1; 160 auto structType = module_.FindDef(inst->words[structTypeIndex]); 161 if (!structType || SpvOpTypeStruct != structType->opcode()) { 162 DIAG(structTypeIndex) << "OpMemberDecorate Structure type <id> '" 163 << inst->words[structTypeIndex] 164 << "' is not a struct type."; 165 return false; 166 } 167 auto memberIndex = 2; 168 auto member = inst->words[memberIndex]; 169 auto memberCount = static_cast<uint32_t>(structType->words().size() - 2); 170 if (memberCount < member) { 171 DIAG(memberIndex) << "Index " << member 172 << " provided in OpMemberDecorate for struct <id> " 173 << inst->words[structTypeIndex] 174 << " is out of bounds. The structure has " << memberCount 175 << " members. Largest valid index is " << memberCount - 1 176 << "."; 177 return false; 178 } 179 return true; 180 } 181 182 template <> 183 bool idUsage::isValid<SpvOpGroupDecorate>(const spv_instruction_t* inst, 184 const spv_opcode_desc) { 185 auto decorationGroupIndex = 1; 186 auto decorationGroup = module_.FindDef(inst->words[decorationGroupIndex]); 187 if (!decorationGroup || SpvOpDecorationGroup != decorationGroup->opcode()) { 188 DIAG(decorationGroupIndex) << "OpGroupDecorate Decoration group <id> '" 189 << inst->words[decorationGroupIndex] 190 << "' is not a decoration group."; 191 return false; 192 } 193 return true; 194 } 195 196 template <> 197 bool idUsage::isValid<SpvOpGroupMemberDecorate>( 198 const spv_instruction_t* inst, const spv_opcode_desc) { 199 auto decorationGroupIndex = 1; 200 auto decorationGroup = module_.FindDef(inst->words[decorationGroupIndex]); 201 if (!decorationGroup || SpvOpDecorationGroup != decorationGroup->opcode()) { 202 DIAG(decorationGroupIndex) 203 << "OpGroupMemberDecorate Decoration group <id> '" 204 << inst->words[decorationGroupIndex] << "' is not a decoration group."; 205 return false; 206 } 207 // Grammar checks ensures that the number of arguments to this instruction 208 // is an odd number: 1 decoration group + (id,literal) pairs. 209 for (size_t i = 2; i + 1 < inst->words.size(); i = i + 2) { 210 const uint32_t struct_id = inst->words[i]; 211 const uint32_t index = inst->words[i + 1]; 212 auto struct_instr = module_.FindDef(struct_id); 213 if (!struct_instr || SpvOpTypeStruct != struct_instr->opcode()) { 214 DIAG(i) << "OpGroupMemberDecorate Structure type <id> '" << struct_id 215 << "' is not a struct type."; 216 return false; 217 } 218 const uint32_t num_struct_members = 219 static_cast<uint32_t>(struct_instr->words().size() - 2); 220 if (index >= num_struct_members) { 221 DIAG(i) << "Index " << index 222 << " provided in OpGroupMemberDecorate for struct <id> " 223 << struct_id << " is out of bounds. The structure has " 224 << num_struct_members << " members. Largest valid index is " 225 << num_struct_members - 1 << "."; 226 return false; 227 } 228 } 229 return true; 230 } 231 232 #if 0 233 template <> 234 bool idUsage::isValid<SpvOpExtInst>(const spv_instruction_t *inst, 235 const spv_opcode_desc opcodeEntry) {} 236 #endif // 0 237 238 template <> 239 bool idUsage::isValid<SpvOpEntryPoint>(const spv_instruction_t* inst, 240 const spv_opcode_desc) { 241 auto entryPointIndex = 2; 242 auto entryPoint = module_.FindDef(inst->words[entryPointIndex]); 243 if (!entryPoint || SpvOpFunction != entryPoint->opcode()) { 244 DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '" 245 << inst->words[entryPointIndex] 246 << "' is not a function."; 247 return false; 248 } 249 // don't check kernel function signatures 250 auto executionModel = inst->words[1]; 251 if (executionModel != SpvExecutionModelKernel) { 252 // TODO: Check the entry point signature is void main(void), may be subject 253 // to change 254 auto entryPointType = module_.FindDef(entryPoint->words()[4]); 255 if (!entryPointType || 3 != entryPointType->words().size()) { 256 DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '" 257 << inst->words[entryPointIndex] 258 << "'s function parameter count is not zero."; 259 return false; 260 } 261 } 262 auto returnType = module_.FindDef(entryPoint->type_id()); 263 if (!returnType || SpvOpTypeVoid != returnType->opcode()) { 264 DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '" 265 << inst->words[entryPointIndex] 266 << "'s function return type is not void."; 267 return false; 268 } 269 return true; 270 } 271 272 template <> 273 bool idUsage::isValid<SpvOpExecutionMode>(const spv_instruction_t* inst, 274 const spv_opcode_desc) { 275 auto entryPointIndex = 1; 276 auto entryPointID = inst->words[entryPointIndex]; 277 auto found = 278 std::find(entry_points_.cbegin(), entry_points_.cend(), entryPointID); 279 if (found == entry_points_.cend()) { 280 DIAG(entryPointIndex) << "OpExecutionMode Entry Point <id> '" 281 << inst->words[entryPointIndex] 282 << "' is not the Entry Point " 283 "operand of an OpEntryPoint."; 284 return false; 285 } 286 return true; 287 } 288 289 template <> 290 bool idUsage::isValid<SpvOpTypeVector>(const spv_instruction_t* inst, 291 const spv_opcode_desc) { 292 auto componentIndex = 2; 293 auto componentType = module_.FindDef(inst->words[componentIndex]); 294 if (!componentType || !spvOpcodeIsScalarType(componentType->opcode())) { 295 DIAG(componentIndex) << "OpTypeVector Component Type <id> '" 296 << inst->words[componentIndex] 297 << "' is not a scalar type."; 298 return false; 299 } 300 return true; 301 } 302 303 template <> 304 bool idUsage::isValid<SpvOpTypeMatrix>(const spv_instruction_t* inst, 305 const spv_opcode_desc) { 306 auto columnTypeIndex = 2; 307 auto columnType = module_.FindDef(inst->words[columnTypeIndex]); 308 if (!columnType || SpvOpTypeVector != columnType->opcode()) { 309 DIAG(columnTypeIndex) << "OpTypeMatrix Column Type <id> '" 310 << inst->words[columnTypeIndex] 311 << "' is not a vector."; 312 return false; 313 } 314 return true; 315 } 316 317 template <> 318 bool idUsage::isValid<SpvOpTypeSampler>(const spv_instruction_t*, 319 const spv_opcode_desc) { 320 // OpTypeSampler takes no arguments in Rev31 and beyond. 321 return true; 322 } 323 324 // True if the integer constant is > 0. constWords are words of the 325 // constant-defining instruction (either OpConstant or 326 // OpSpecConstant). typeWords are the words of the constant's-type-defining 327 // OpTypeInt. 328 bool aboveZero(const vector<uint32_t>& constWords, 329 const vector<uint32_t>& typeWords) { 330 const uint32_t width = typeWords[2]; 331 const bool is_signed = typeWords[3] > 0; 332 const uint32_t loWord = constWords[3]; 333 if (width > 32) { 334 // The spec currently doesn't allow integers wider than 64 bits. 335 const uint32_t hiWord = constWords[4]; // Must exist, per spec. 336 if (is_signed && (hiWord >> 31)) return false; 337 return (loWord | hiWord) > 0; 338 } else { 339 if (is_signed && (loWord >> 31)) return false; 340 return loWord > 0; 341 } 342 } 343 344 template <> 345 bool idUsage::isValid<SpvOpTypeArray>(const spv_instruction_t* inst, 346 const spv_opcode_desc) { 347 auto elementTypeIndex = 2; 348 auto elementType = module_.FindDef(inst->words[elementTypeIndex]); 349 if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) { 350 DIAG(elementTypeIndex) << "OpTypeArray Element Type <id> '" 351 << inst->words[elementTypeIndex] 352 << "' is not a type."; 353 return false; 354 } 355 auto lengthIndex = 3; 356 auto length = module_.FindDef(inst->words[lengthIndex]); 357 if (!length || !spvOpcodeIsConstant(length->opcode())) { 358 DIAG(lengthIndex) << "OpTypeArray Length <id> '" << inst->words[lengthIndex] 359 << "' is not a scalar constant type."; 360 return false; 361 } 362 363 // NOTE: Check the initialiser value of the constant 364 auto constInst = length->words(); 365 auto constResultTypeIndex = 1; 366 auto constResultType = module_.FindDef(constInst[constResultTypeIndex]); 367 if (!constResultType || SpvOpTypeInt != constResultType->opcode()) { 368 DIAG(lengthIndex) << "OpTypeArray Length <id> '" << inst->words[lengthIndex] 369 << "' is not a constant integer type."; 370 return false; 371 } 372 373 switch (length->opcode()) { 374 case SpvOpSpecConstant: 375 case SpvOpConstant: 376 if (aboveZero(length->words(), constResultType->words())) break; 377 // Else fall through! 378 case SpvOpConstantNull: { 379 DIAG(lengthIndex) << "OpTypeArray Length <id> '" 380 << inst->words[lengthIndex] 381 << "' default value must be at least 1."; 382 return false; 383 } 384 case SpvOpSpecConstantOp: 385 // Assume it's OK, rather than try to evaluate the operation. 386 break; 387 default: 388 assert(0 && "bug in spvOpcodeIsConstant() or result type isn't int"); 389 } 390 return true; 391 } 392 393 template <> 394 bool idUsage::isValid<SpvOpTypeRuntimeArray>(const spv_instruction_t* inst, 395 const spv_opcode_desc) { 396 auto elementTypeIndex = 2; 397 auto elementType = module_.FindDef(inst->words[elementTypeIndex]); 398 if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) { 399 DIAG(elementTypeIndex) << "OpTypeRuntimeArray Element Type <id> '" 400 << inst->words[elementTypeIndex] 401 << "' is not a type."; 402 return false; 403 } 404 return true; 405 } 406 407 template <> 408 bool idUsage::isValid<SpvOpTypeStruct>(const spv_instruction_t* inst, 409 const spv_opcode_desc) { 410 ValidationState_t& vstate = const_cast<ValidationState_t&>(module_); 411 const uint32_t struct_id = inst->words[1]; 412 for (size_t memberTypeIndex = 2; memberTypeIndex < inst->words.size(); 413 ++memberTypeIndex) { 414 auto memberTypeId = inst->words[memberTypeIndex]; 415 auto memberType = module_.FindDef(memberTypeId); 416 if (!memberType || !spvOpcodeGeneratesType(memberType->opcode())) { 417 DIAG(memberTypeIndex) << "OpTypeStruct Member Type <id> '" 418 << inst->words[memberTypeIndex] 419 << "' is not a type."; 420 return false; 421 } 422 if (SpvOpTypeStruct == memberType->opcode() && 423 module_.IsStructTypeWithBuiltInMember(memberTypeId)) { 424 DIAG(memberTypeIndex) 425 << "Structure <id> " << memberTypeId 426 << " contains members with BuiltIn decoration. Therefore this " 427 "structure may not be contained as a member of another structure " 428 "type. Structure <id> " 429 << struct_id << " contains structure <id> " << memberTypeId << "."; 430 return false; 431 } 432 if (module_.IsForwardPointer(memberTypeId)) { 433 if (memberType->opcode() != SpvOpTypePointer) { 434 DIAG(memberTypeIndex) << "Found a forward reference to a non-pointer " 435 "type in OpTypeStruct instruction."; 436 return false; 437 } 438 // If we're dealing with a forward pointer: 439 // Find out the type that the pointer is pointing to (must be struct) 440 // word 3 is the <id> of the type being pointed to. 441 auto typePointingTo = module_.FindDef(memberType->words()[3]); 442 if (typePointingTo && typePointingTo->opcode() != SpvOpTypeStruct) { 443 // Forward declared operands of a struct may only point to a struct. 444 DIAG(memberTypeIndex) 445 << "A forward reference operand in an OpTypeStruct must be an " 446 "OpTypePointer that points to an OpTypeStruct. " 447 "Found OpTypePointer that points to Op" 448 << spvOpcodeString(static_cast<SpvOp>(typePointingTo->opcode())) 449 << "."; 450 return false; 451 } 452 } 453 } 454 std::unordered_set<uint32_t> built_in_members; 455 for (auto decoration : vstate.id_decorations(struct_id)) { 456 if (decoration.dec_type() == SpvDecorationBuiltIn && 457 decoration.struct_member_index() != Decoration::kInvalidMember) { 458 built_in_members.insert(decoration.struct_member_index()); 459 } 460 } 461 int num_struct_members = static_cast<int>(inst->words.size() - 2); 462 int num_builtin_members = static_cast<int>(built_in_members.size()); 463 if (num_builtin_members > 0 && num_builtin_members != num_struct_members) { 464 DIAG(0) 465 << "When BuiltIn decoration is applied to a structure-type member, " 466 "all members of that structure type must also be decorated with " 467 "BuiltIn (No allowed mixing of built-in variables and " 468 "non-built-in variables within a single structure). Structure id " 469 << struct_id << " does not meet this requirement."; 470 return false; 471 } 472 if (num_builtin_members > 0) { 473 vstate.RegisterStructTypeWithBuiltInMember(struct_id); 474 } 475 return true; 476 } 477 478 template <> 479 bool idUsage::isValid<SpvOpTypePointer>(const spv_instruction_t* inst, 480 const spv_opcode_desc) { 481 auto typeIndex = 3; 482 auto type = module_.FindDef(inst->words[typeIndex]); 483 if (!type || !spvOpcodeGeneratesType(type->opcode())) { 484 DIAG(typeIndex) << "OpTypePointer Type <id> '" << inst->words[typeIndex] 485 << "' is not a type."; 486 return false; 487 } 488 return true; 489 } 490 491 template <> 492 bool idUsage::isValid<SpvOpTypeFunction>(const spv_instruction_t* inst, 493 const spv_opcode_desc) { 494 auto returnTypeIndex = 2; 495 auto returnType = module_.FindDef(inst->words[returnTypeIndex]); 496 if (!returnType || !spvOpcodeGeneratesType(returnType->opcode())) { 497 DIAG(returnTypeIndex) << "OpTypeFunction Return Type <id> '" 498 << inst->words[returnTypeIndex] << "' is not a type."; 499 return false; 500 } 501 size_t num_args = 0; 502 for (size_t paramTypeIndex = 3; paramTypeIndex < inst->words.size(); 503 ++paramTypeIndex, ++num_args) { 504 auto paramType = module_.FindDef(inst->words[paramTypeIndex]); 505 if (!paramType || !spvOpcodeGeneratesType(paramType->opcode())) { 506 DIAG(paramTypeIndex) << "OpTypeFunction Parameter Type <id> '" 507 << inst->words[paramTypeIndex] << "' is not a type."; 508 return false; 509 } 510 } 511 const uint32_t num_function_args_limit = 512 module_.options()->universal_limits_.max_function_args; 513 if (num_args > num_function_args_limit) { 514 DIAG(returnTypeIndex) << "OpTypeFunction may not take more than " 515 << num_function_args_limit 516 << " arguments. OpTypeFunction <id> '" 517 << inst->words[1] << "' has " << num_args 518 << " arguments."; 519 return false; 520 } 521 return true; 522 } 523 524 template <> 525 bool idUsage::isValid<SpvOpTypePipe>(const spv_instruction_t*, 526 const spv_opcode_desc) { 527 // OpTypePipe has no ID arguments. 528 return true; 529 } 530 531 template <> 532 bool idUsage::isValid<SpvOpConstantTrue>(const spv_instruction_t* inst, 533 const spv_opcode_desc) { 534 auto resultTypeIndex = 1; 535 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 536 if (!resultType || SpvOpTypeBool != resultType->opcode()) { 537 DIAG(resultTypeIndex) << "OpConstantTrue Result Type <id> '" 538 << inst->words[resultTypeIndex] 539 << "' is not a boolean type."; 540 return false; 541 } 542 return true; 543 } 544 545 template <> 546 bool idUsage::isValid<SpvOpConstantFalse>(const spv_instruction_t* inst, 547 const spv_opcode_desc) { 548 auto resultTypeIndex = 1; 549 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 550 if (!resultType || SpvOpTypeBool != resultType->opcode()) { 551 DIAG(resultTypeIndex) << "OpConstantFalse Result Type <id> '" 552 << inst->words[resultTypeIndex] 553 << "' is not a boolean type."; 554 return false; 555 } 556 return true; 557 } 558 559 template <> 560 bool idUsage::isValid<SpvOpConstantComposite>(const spv_instruction_t* inst, 561 const spv_opcode_desc) { 562 auto resultTypeIndex = 1; 563 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 564 if (!resultType || !spvOpcodeIsComposite(resultType->opcode())) { 565 DIAG(resultTypeIndex) << "OpConstantComposite Result Type <id> '" 566 << inst->words[resultTypeIndex] 567 << "' is not a composite type."; 568 return false; 569 } 570 571 auto constituentCount = inst->words.size() - 3; 572 switch (resultType->opcode()) { 573 case SpvOpTypeVector: { 574 auto componentCount = resultType->words()[3]; 575 if (componentCount != constituentCount) { 576 // TODO: Output ID's on diagnostic 577 DIAG(inst->words.size() - 1) 578 << "OpConstantComposite Constituent <id> count does not match " 579 "Result Type <id> '" 580 << resultType->id() << "'s vector component count."; 581 return false; 582 } 583 auto componentType = module_.FindDef(resultType->words()[2]); 584 assert(componentType); 585 for (size_t constituentIndex = 3; constituentIndex < inst->words.size(); 586 constituentIndex++) { 587 auto constituent = module_.FindDef(inst->words[constituentIndex]); 588 if (!constituent || 589 !spvOpcodeIsConstantOrUndef(constituent->opcode())) { 590 DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '" 591 << inst->words[constituentIndex] 592 << "' is not a constant or undef."; 593 return false; 594 } 595 auto constituentResultType = module_.FindDef(constituent->type_id()); 596 if (!constituentResultType || 597 componentType->opcode() != constituentResultType->opcode()) { 598 DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '" 599 << inst->words[constituentIndex] 600 << "'s type does not match Result Type <id> '" 601 << resultType->id() 602 << "'s vector element type."; 603 return false; 604 } 605 } 606 } break; 607 case SpvOpTypeMatrix: { 608 auto columnCount = resultType->words()[3]; 609 if (columnCount != constituentCount) { 610 // TODO: Output ID's on diagnostic 611 DIAG(inst->words.size() - 1) 612 << "OpConstantComposite Constituent <id> count does not match " 613 "Result Type <id> '" 614 << resultType->id() << "'s matrix column count."; 615 return false; 616 } 617 618 auto columnType = module_.FindDef(resultType->words()[2]); 619 assert(columnType); 620 auto componentCount = columnType->words()[3]; 621 auto componentType = module_.FindDef(columnType->words()[2]); 622 assert(componentType); 623 624 for (size_t constituentIndex = 3; constituentIndex < inst->words.size(); 625 constituentIndex++) { 626 auto constituent = module_.FindDef(inst->words[constituentIndex]); 627 if (!constituent || 628 !(SpvOpConstantComposite == constituent->opcode() || 629 SpvOpUndef == constituent->opcode())) { 630 // The message says "... or undef" because the spec does not say 631 // undef is a constant. 632 DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '" 633 << inst->words[constituentIndex] 634 << "' is not a constant composite or undef."; 635 return false; 636 } 637 auto vector = module_.FindDef(constituent->type_id()); 638 assert(vector); 639 if (columnType->opcode() != vector->opcode()) { 640 DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '" 641 << inst->words[constituentIndex] 642 << "' type does not match Result Type <id> '" 643 << resultType->id() 644 << "'s matrix column type."; 645 return false; 646 } 647 auto vectorComponentType = module_.FindDef(vector->words()[2]); 648 assert(vectorComponentType); 649 if (componentType->id() != vectorComponentType->id()) { 650 DIAG(constituentIndex) 651 << "OpConstantComposite Constituent <id> '" 652 << inst->words[constituentIndex] 653 << "' component type does not match Result Type <id> '" 654 << resultType->id() << "'s matrix column component type."; 655 return false; 656 } 657 if (componentCount != vector->words()[3]) { 658 DIAG(constituentIndex) 659 << "OpConstantComposite Constituent <id> '" 660 << inst->words[constituentIndex] 661 << "' vector component count does not match Result Type <id> '" 662 << resultType->id() << "'s vector component count."; 663 return false; 664 } 665 } 666 } break; 667 case SpvOpTypeArray: { 668 auto elementType = module_.FindDef(resultType->words()[2]); 669 assert(elementType); 670 auto length = module_.FindDef(resultType->words()[3]); 671 assert(length); 672 if (length->words()[3] != constituentCount) { 673 DIAG(inst->words.size() - 1) 674 << "OpConstantComposite Constituent count does not match " 675 "Result Type <id> '" 676 << resultType->id() << "'s array length."; 677 return false; 678 } 679 for (size_t constituentIndex = 3; constituentIndex < inst->words.size(); 680 constituentIndex++) { 681 auto constituent = module_.FindDef(inst->words[constituentIndex]); 682 if (!constituent || 683 !spvOpcodeIsConstantOrUndef(constituent->opcode())) { 684 DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '" 685 << inst->words[constituentIndex] 686 << "' is not a constant or undef."; 687 return false; 688 } 689 auto constituentType = module_.FindDef(constituent->type_id()); 690 assert(constituentType); 691 if (elementType->id() != constituentType->id()) { 692 DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '" 693 << inst->words[constituentIndex] 694 << "'s type does not match Result Type <id> '" 695 << resultType->id() 696 << "'s array element type."; 697 return false; 698 } 699 } 700 } break; 701 case SpvOpTypeStruct: { 702 auto memberCount = resultType->words().size() - 2; 703 if (memberCount != constituentCount) { 704 DIAG(resultTypeIndex) << "OpConstantComposite Constituent <id> '" 705 << inst->words[resultTypeIndex] 706 << "' count does not match Result Type <id> '" 707 << resultType->id() << "'s struct member count."; 708 return false; 709 } 710 for (uint32_t constituentIndex = 3, memberIndex = 2; 711 constituentIndex < inst->words.size(); 712 constituentIndex++, memberIndex++) { 713 auto constituent = module_.FindDef(inst->words[constituentIndex]); 714 if (!constituent || 715 !spvOpcodeIsConstantOrUndef(constituent->opcode())) { 716 DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '" 717 << inst->words[constituentIndex] 718 << "' is not a constant or undef."; 719 return false; 720 } 721 auto constituentType = module_.FindDef(constituent->type_id()); 722 assert(constituentType); 723 724 auto memberType = module_.FindDef(resultType->words()[memberIndex]); 725 assert(memberType); 726 if (memberType->id() != constituentType->id()) { 727 DIAG(constituentIndex) 728 << "OpConstantComposite Constituent <id> '" 729 << inst->words[constituentIndex] 730 << "' type does not match the Result Type <id> '" 731 << resultType->id() << "'s member type."; 732 return false; 733 } 734 } 735 } break; 736 default: { assert(0 && "Unreachable!"); } break; 737 } 738 return true; 739 } 740 741 template <> 742 bool idUsage::isValid<SpvOpConstantSampler>(const spv_instruction_t* inst, 743 const spv_opcode_desc) { 744 auto resultTypeIndex = 1; 745 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 746 if (!resultType || SpvOpTypeSampler != resultType->opcode()) { 747 DIAG(resultTypeIndex) << "OpConstantSampler Result Type <id> '" 748 << inst->words[resultTypeIndex] 749 << "' is not a sampler type."; 750 return false; 751 } 752 return true; 753 } 754 755 // True if instruction defines a type that can have a null value, as defined by 756 // the SPIR-V spec. Tracks composite-type components through module to check 757 // nullability transitively. 758 bool IsTypeNullable(const vector<uint32_t>& instruction, 759 const ValidationState_t& module) { 760 uint16_t opcode; 761 uint16_t word_count; 762 spvOpcodeSplit(instruction[0], &word_count, &opcode); 763 switch (static_cast<SpvOp>(opcode)) { 764 case SpvOpTypeBool: 765 case SpvOpTypeInt: 766 case SpvOpTypeFloat: 767 case SpvOpTypePointer: 768 case SpvOpTypeEvent: 769 case SpvOpTypeDeviceEvent: 770 case SpvOpTypeReserveId: 771 case SpvOpTypeQueue: 772 return true; 773 case SpvOpTypeArray: 774 case SpvOpTypeMatrix: 775 case SpvOpTypeVector: { 776 auto base_type = module.FindDef(instruction[2]); 777 return base_type && IsTypeNullable(base_type->words(), module); 778 } 779 case SpvOpTypeStruct: { 780 for (size_t elementIndex = 2; elementIndex < instruction.size(); 781 ++elementIndex) { 782 auto element = module.FindDef(instruction[elementIndex]); 783 if (!element || !IsTypeNullable(element->words(), module)) return false; 784 } 785 return true; 786 } 787 default: 788 return false; 789 } 790 } 791 792 template <> 793 bool idUsage::isValid<SpvOpConstantNull>(const spv_instruction_t* inst, 794 const spv_opcode_desc) { 795 auto resultTypeIndex = 1; 796 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 797 if (!resultType || !IsTypeNullable(resultType->words(), module_)) { 798 DIAG(resultTypeIndex) << "OpConstantNull Result Type <id> '" 799 << inst->words[resultTypeIndex] 800 << "' cannot have a null value."; 801 return false; 802 } 803 return true; 804 } 805 806 template <> 807 bool idUsage::isValid<SpvOpSpecConstantTrue>(const spv_instruction_t* inst, 808 const spv_opcode_desc) { 809 auto resultTypeIndex = 1; 810 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 811 if (!resultType || SpvOpTypeBool != resultType->opcode()) { 812 DIAG(resultTypeIndex) << "OpSpecConstantTrue Result Type <id> '" 813 << inst->words[resultTypeIndex] 814 << "' is not a boolean type."; 815 return false; 816 } 817 return true; 818 } 819 820 template <> 821 bool idUsage::isValid<SpvOpSpecConstantFalse>(const spv_instruction_t* inst, 822 const spv_opcode_desc) { 823 auto resultTypeIndex = 1; 824 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 825 if (!resultType || SpvOpTypeBool != resultType->opcode()) { 826 DIAG(resultTypeIndex) << "OpSpecConstantFalse Result Type <id> '" 827 << inst->words[resultTypeIndex] 828 << "' is not a boolean type."; 829 return false; 830 } 831 return true; 832 } 833 834 template <> 835 bool idUsage::isValid<SpvOpSampledImage>(const spv_instruction_t* inst, 836 const spv_opcode_desc) { 837 auto resultTypeIndex = 2; 838 auto resultID = inst->words[resultTypeIndex]; 839 auto sampledImageInstr = module_.FindDef(resultID); 840 // We need to validate 2 things: 841 // * All OpSampledImage instructions must be in the same block in which their 842 // Result <id> are consumed. 843 // * Result <id> from OpSampledImage instructions must not appear as operands 844 // to OpPhi instructions or OpSelect instructions, or any instructions other 845 // than the image lookup and query instructions specified to take an operand 846 // whose type is OpTypeSampledImage. 847 std::vector<uint32_t> consumers = module_.getSampledImageConsumers(resultID); 848 if (!consumers.empty()) { 849 for (auto consumer_id : consumers) { 850 auto consumer_instr = module_.FindDef(consumer_id); 851 auto consumer_opcode = consumer_instr->opcode(); 852 if (consumer_instr->block() != sampledImageInstr->block()) { 853 DIAG(resultTypeIndex) 854 << "All OpSampledImage instructions must be in the same block in " 855 "which their Result <id> are consumed. OpSampledImage Result " 856 "Type <id> '" 857 << resultID << "' has a consumer in a different basic " 858 "block. The consumer instruction <id> is '" 859 << consumer_id << "'."; 860 return false; 861 } 862 // TODO: The following check is incomplete. We should also check that the 863 // Sampled Image is not used by instructions that should not take 864 // SampledImage as an argument. We could find the list of valid 865 // instructions by scanning for "Sampled Image" in the operand description 866 // field in the grammar file. 867 if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) { 868 DIAG(resultTypeIndex) 869 << "Result <id> from OpSampledImage instruction must not appear as " 870 "operands of Op" 871 << spvOpcodeString(static_cast<SpvOp>(consumer_opcode)) << "." 872 << " Found result <id> '" << resultID << "' as an operand of <id> '" 873 << consumer_id << "'."; 874 return false; 875 } 876 } 877 } 878 return true; 879 } 880 881 template <> 882 bool idUsage::isValid<SpvOpSpecConstantComposite>(const spv_instruction_t* inst, 883 const spv_opcode_desc) { 884 // The result type must be a composite type. 885 auto resultTypeIndex = 1; 886 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 887 if (!resultType || !spvOpcodeIsComposite(resultType->opcode())) { 888 DIAG(resultTypeIndex) << "OpSpecConstantComposite Result Type <id> '" 889 << inst->words[resultTypeIndex] 890 << "' is not a composite type."; 891 return false; 892 } 893 // Validation checks differ based on the type of composite type. 894 auto constituentCount = inst->words.size() - 3; 895 switch (resultType->opcode()) { 896 // For Vectors, the following must be met: 897 // * Number of constituents in the result type and the vector must match. 898 // * All the components of the vector must have the same type (or specialize 899 // to the same type). OpConstant and OpSpecConstant are allowed. 900 // To check that condition, we check each supplied value argument's type 901 // against the element type of the result type. 902 case SpvOpTypeVector: { 903 auto componentCount = resultType->words()[3]; 904 if (componentCount != constituentCount) { 905 DIAG(inst->words.size() - 1) 906 << "OpSpecConstantComposite Constituent <id> count does not match " 907 "Result Type <id> '" 908 << resultType->id() << "'s vector component count."; 909 return false; 910 } 911 auto componentType = module_.FindDef(resultType->words()[2]); 912 assert(componentType); 913 for (size_t constituentIndex = 3; constituentIndex < inst->words.size(); 914 constituentIndex++) { 915 auto constituent = module_.FindDef(inst->words[constituentIndex]); 916 if (!constituent || 917 !spvOpcodeIsConstantOrUndef(constituent->opcode())) { 918 DIAG(constituentIndex) << "OpSpecConstantComposite Constituent <id> '" 919 << inst->words[constituentIndex] 920 << "' is not a constant or undef."; 921 return false; 922 } 923 auto constituentResultType = module_.FindDef(constituent->type_id()); 924 if (!constituentResultType || 925 componentType->opcode() != constituentResultType->opcode()) { 926 DIAG(constituentIndex) << "OpSpecConstantComposite Constituent <id> '" 927 << inst->words[constituentIndex] 928 << "'s type does not match Result Type <id> '" 929 << resultType->id() 930 << "'s vector element type."; 931 return false; 932 } 933 } 934 break; 935 } 936 case SpvOpTypeMatrix: { 937 auto columnCount = resultType->words()[3]; 938 if (columnCount != constituentCount) { 939 DIAG(inst->words.size() - 1) 940 << "OpSpecConstantComposite Constituent <id> count does not match " 941 "Result Type <id> '" 942 << resultType->id() << "'s matrix column count."; 943 return false; 944 } 945 946 auto columnType = module_.FindDef(resultType->words()[2]); 947 assert(columnType); 948 auto componentCount = columnType->words()[3]; 949 auto componentType = module_.FindDef(columnType->words()[2]); 950 assert(componentType); 951 952 for (size_t constituentIndex = 3; constituentIndex < inst->words.size(); 953 constituentIndex++) { 954 auto constituent = module_.FindDef(inst->words[constituentIndex]); 955 auto constituentOpCode = constituent->opcode(); 956 if (!constituent || 957 !(SpvOpSpecConstantComposite == constituentOpCode || 958 SpvOpConstantComposite == constituentOpCode || 959 SpvOpUndef == constituentOpCode)) { 960 // The message says "... or undef" because the spec does not say 961 // undef is a constant. 962 DIAG(constituentIndex) << "OpSpecConstantComposite Constituent <id> '" 963 << inst->words[constituentIndex] 964 << "' is not a constant composite or undef."; 965 return false; 966 } 967 auto vector = module_.FindDef(constituent->type_id()); 968 assert(vector); 969 if (columnType->opcode() != vector->opcode()) { 970 DIAG(constituentIndex) << "OpSpecConstantComposite Constituent <id> '" 971 << inst->words[constituentIndex] 972 << "' type does not match Result Type <id> '" 973 << resultType->id() 974 << "'s matrix column type."; 975 return false; 976 } 977 auto vectorComponentType = module_.FindDef(vector->words()[2]); 978 assert(vectorComponentType); 979 if (componentType->id() != vectorComponentType->id()) { 980 DIAG(constituentIndex) 981 << "OpSpecConstantComposite Constituent <id> '" 982 << inst->words[constituentIndex] 983 << "' component type does not match Result Type <id> '" 984 << resultType->id() << "'s matrix column component type."; 985 return false; 986 } 987 if (componentCount != vector->words()[3]) { 988 DIAG(constituentIndex) 989 << "OpSpecConstantComposite Constituent <id> '" 990 << inst->words[constituentIndex] 991 << "' vector component count does not match Result Type <id> '" 992 << resultType->id() << "'s vector component count."; 993 return false; 994 } 995 } 996 break; 997 } 998 case SpvOpTypeArray: { 999 auto elementType = module_.FindDef(resultType->words()[2]); 1000 assert(elementType); 1001 auto length = module_.FindDef(resultType->words()[3]); 1002 assert(length); 1003 if (length->words()[3] != constituentCount) { 1004 DIAG(inst->words.size() - 1) 1005 << "OpSpecConstantComposite Constituent count does not match " 1006 "Result Type <id> '" 1007 << resultType->id() << "'s array length."; 1008 return false; 1009 } 1010 for (size_t constituentIndex = 3; constituentIndex < inst->words.size(); 1011 constituentIndex++) { 1012 auto constituent = module_.FindDef(inst->words[constituentIndex]); 1013 if (!constituent || 1014 !spvOpcodeIsConstantOrUndef(constituent->opcode())) { 1015 DIAG(constituentIndex) << "OpSpecConstantComposite Constituent <id> '" 1016 << inst->words[constituentIndex] 1017 << "' is not a constant or undef."; 1018 return false; 1019 } 1020 auto constituentType = module_.FindDef(constituent->type_id()); 1021 assert(constituentType); 1022 if (elementType->id() != constituentType->id()) { 1023 DIAG(constituentIndex) << "OpSpecConstantComposite Constituent <id> '" 1024 << inst->words[constituentIndex] 1025 << "'s type does not match Result Type <id> '" 1026 << resultType->id() 1027 << "'s array element type."; 1028 return false; 1029 } 1030 } 1031 break; 1032 } 1033 case SpvOpTypeStruct: { 1034 auto memberCount = resultType->words().size() - 2; 1035 if (memberCount != constituentCount) { 1036 DIAG(resultTypeIndex) << "OpSpecConstantComposite Constituent <id> '" 1037 << inst->words[resultTypeIndex] 1038 << "' count does not match Result Type <id> '" 1039 << resultType->id() << "'s struct member count."; 1040 return false; 1041 } 1042 for (uint32_t constituentIndex = 3, memberIndex = 2; 1043 constituentIndex < inst->words.size(); 1044 constituentIndex++, memberIndex++) { 1045 auto constituent = module_.FindDef(inst->words[constituentIndex]); 1046 if (!constituent || 1047 !spvOpcodeIsConstantOrUndef(constituent->opcode())) { 1048 DIAG(constituentIndex) << "OpSpecConstantComposite Constituent <id> '" 1049 << inst->words[constituentIndex] 1050 << "' is not a constant or undef."; 1051 return false; 1052 } 1053 auto constituentType = module_.FindDef(constituent->type_id()); 1054 assert(constituentType); 1055 1056 auto memberType = module_.FindDef(resultType->words()[memberIndex]); 1057 assert(memberType); 1058 if (memberType->id() != constituentType->id()) { 1059 DIAG(constituentIndex) 1060 << "OpSpecConstantComposite Constituent <id> '" 1061 << inst->words[constituentIndex] 1062 << "' type does not match the Result Type <id> '" 1063 << resultType->id() << "'s member type."; 1064 return false; 1065 } 1066 } 1067 break; 1068 } 1069 default: { assert(0 && "Unreachable!"); } break; 1070 } 1071 return true; 1072 } 1073 1074 #if 0 1075 template <> 1076 bool idUsage::isValid<SpvOpSpecConstantOp>(const spv_instruction_t *inst) {} 1077 #endif 1078 1079 template <> 1080 bool idUsage::isValid<SpvOpVariable>(const spv_instruction_t* inst, 1081 const spv_opcode_desc) { 1082 auto resultTypeIndex = 1; 1083 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 1084 if (!resultType || SpvOpTypePointer != resultType->opcode()) { 1085 DIAG(resultTypeIndex) << "OpVariable Result Type <id> '" 1086 << inst->words[resultTypeIndex] 1087 << "' is not a pointer type."; 1088 return false; 1089 } 1090 const auto initialiserIndex = 4; 1091 if (initialiserIndex < inst->words.size()) { 1092 const auto initialiser = module_.FindDef(inst->words[initialiserIndex]); 1093 const auto storageClassIndex = 3; 1094 const auto is_module_scope_var = 1095 initialiser && (initialiser->opcode() == SpvOpVariable) && 1096 (initialiser->word(storageClassIndex) != SpvStorageClassFunction); 1097 const auto is_constant = 1098 initialiser && spvOpcodeIsConstant(initialiser->opcode()); 1099 if (!initialiser || !(is_constant || is_module_scope_var)) { 1100 DIAG(initialiserIndex) << "OpVariable Initializer <id> '" 1101 << inst->words[initialiserIndex] 1102 << "' is not a constant or module-scope variable."; 1103 return false; 1104 } 1105 } 1106 return true; 1107 } 1108 1109 template <> 1110 bool idUsage::isValid<SpvOpLoad>(const spv_instruction_t* inst, 1111 const spv_opcode_desc) { 1112 auto resultTypeIndex = 1; 1113 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 1114 if (!resultType) { 1115 DIAG(resultTypeIndex) << "OpLoad Result Type <id> '" 1116 << inst->words[resultTypeIndex] << "' is not defind."; 1117 return false; 1118 } 1119 const bool uses_variable_pointer = 1120 module_.features().variable_pointers || 1121 module_.features().variable_pointers_storage_buffer; 1122 auto pointerIndex = 3; 1123 auto pointer = module_.FindDef(inst->words[pointerIndex]); 1124 if (!pointer || 1125 (addressingModel == SpvAddressingModelLogical && 1126 ((!uses_variable_pointer && 1127 !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || 1128 (uses_variable_pointer && 1129 !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { 1130 DIAG(pointerIndex) << "OpLoad Pointer <id> '" << inst->words[pointerIndex] 1131 << "' is not a logical pointer."; 1132 return false; 1133 } 1134 auto pointerType = module_.FindDef(pointer->type_id()); 1135 if (!pointerType || pointerType->opcode() != SpvOpTypePointer) { 1136 DIAG(pointerIndex) << "OpLoad type for pointer <id> '" 1137 << inst->words[pointerIndex] 1138 << "' is not a pointer type."; 1139 return false; 1140 } 1141 auto pointeeType = module_.FindDef(pointerType->words()[3]); 1142 if (!pointeeType || resultType->id() != pointeeType->id()) { 1143 DIAG(resultTypeIndex) << "OpLoad Result Type <id> '" 1144 << inst->words[resultTypeIndex] 1145 << "' does not match Pointer <id> '" << pointer->id() 1146 << "'s type."; 1147 return false; 1148 } 1149 return true; 1150 } 1151 1152 template <> 1153 bool idUsage::isValid<SpvOpStore>(const spv_instruction_t* inst, 1154 const spv_opcode_desc) { 1155 const bool uses_variable_pointer = 1156 module_.features().variable_pointers || 1157 module_.features().variable_pointers_storage_buffer; 1158 const auto pointerIndex = 1; 1159 auto pointer = module_.FindDef(inst->words[pointerIndex]); 1160 if (!pointer || 1161 (addressingModel == SpvAddressingModelLogical && 1162 ((!uses_variable_pointer && 1163 !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || 1164 (uses_variable_pointer && 1165 !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { 1166 DIAG(pointerIndex) << "OpStore Pointer <id> '" << inst->words[pointerIndex] 1167 << "' is not a logical pointer."; 1168 return false; 1169 } 1170 auto pointerType = module_.FindDef(pointer->type_id()); 1171 if (!pointer || pointerType->opcode() != SpvOpTypePointer) { 1172 DIAG(pointerIndex) << "OpStore type for pointer <id> '" 1173 << inst->words[pointerIndex] 1174 << "' is not a pointer type."; 1175 return false; 1176 } 1177 auto type = module_.FindDef(pointerType->words()[3]); 1178 assert(type); 1179 if (SpvOpTypeVoid == type->opcode()) { 1180 DIAG(pointerIndex) << "OpStore Pointer <id> '" << inst->words[pointerIndex] 1181 << "'s type is void."; 1182 return false; 1183 } 1184 1185 auto objectIndex = 2; 1186 auto object = module_.FindDef(inst->words[objectIndex]); 1187 if (!object || !object->type_id()) { 1188 DIAG(objectIndex) << "OpStore Object <id> '" << inst->words[objectIndex] 1189 << "' is not an object."; 1190 return false; 1191 } 1192 auto objectType = module_.FindDef(object->type_id()); 1193 assert(objectType); 1194 if (SpvOpTypeVoid == objectType->opcode()) { 1195 DIAG(objectIndex) << "OpStore Object <id> '" << inst->words[objectIndex] 1196 << "'s type is void."; 1197 return false; 1198 } 1199 1200 if (type->id() != objectType->id()) { 1201 DIAG(pointerIndex) << "OpStore Pointer <id> '" << inst->words[pointerIndex] 1202 << "'s type does not match Object <id> '" 1203 << objectType->id() << "'s type."; 1204 return false; 1205 } 1206 return true; 1207 } 1208 1209 template <> 1210 bool idUsage::isValid<SpvOpCopyMemory>(const spv_instruction_t* inst, 1211 const spv_opcode_desc) { 1212 auto targetIndex = 1; 1213 auto target = module_.FindDef(inst->words[targetIndex]); 1214 if (!target) return false; 1215 auto sourceIndex = 2; 1216 auto source = module_.FindDef(inst->words[sourceIndex]); 1217 if (!source) return false; 1218 auto targetPointerType = module_.FindDef(target->type_id()); 1219 assert(targetPointerType); 1220 auto targetType = module_.FindDef(targetPointerType->words()[3]); 1221 assert(targetType); 1222 auto sourcePointerType = module_.FindDef(source->type_id()); 1223 assert(sourcePointerType); 1224 auto sourceType = module_.FindDef(sourcePointerType->words()[3]); 1225 assert(sourceType); 1226 if (targetType->id() != sourceType->id()) { 1227 DIAG(sourceIndex) << "OpCopyMemory Target <id> '" 1228 << inst->words[sourceIndex] 1229 << "'s type does not match Source <id> '" 1230 << sourceType->id() << "'s type."; 1231 return false; 1232 } 1233 return true; 1234 } 1235 1236 template <> 1237 bool idUsage::isValid<SpvOpCopyMemorySized>(const spv_instruction_t* inst, 1238 const spv_opcode_desc) { 1239 auto targetIndex = 1; 1240 auto target = module_.FindDef(inst->words[targetIndex]); 1241 if (!target) return false; 1242 auto sourceIndex = 2; 1243 auto source = module_.FindDef(inst->words[sourceIndex]); 1244 if (!source) return false; 1245 auto sizeIndex = 3; 1246 auto size = module_.FindDef(inst->words[sizeIndex]); 1247 if (!size) return false; 1248 auto targetPointerType = module_.FindDef(target->type_id()); 1249 if (!targetPointerType || SpvOpTypePointer != targetPointerType->opcode()) { 1250 DIAG(targetIndex) << "OpCopyMemorySized Target <id> '" 1251 << inst->words[targetIndex] << "' is not a pointer."; 1252 return false; 1253 } 1254 auto sourcePointerType = module_.FindDef(source->type_id()); 1255 if (!sourcePointerType || SpvOpTypePointer != sourcePointerType->opcode()) { 1256 DIAG(sourceIndex) << "OpCopyMemorySized Source <id> '" 1257 << inst->words[sourceIndex] << "' is not a pointer."; 1258 return false; 1259 } 1260 switch (size->opcode()) { 1261 // TODO: The following opcode's are assumed to be valid, refer to the 1262 // following bug https://cvs.khronos.org/bugzilla/show_bug.cgi?id=13871 for 1263 // clarification 1264 case SpvOpConstant: 1265 case SpvOpSpecConstant: { 1266 auto sizeType = module_.FindDef(size->type_id()); 1267 assert(sizeType); 1268 if (SpvOpTypeInt != sizeType->opcode()) { 1269 DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '" 1270 << inst->words[sizeIndex] 1271 << "'s type is not an integer type."; 1272 return false; 1273 } 1274 } break; 1275 case SpvOpVariable: { 1276 auto pointerType = module_.FindDef(size->type_id()); 1277 assert(pointerType); 1278 auto sizeType = module_.FindDef(pointerType->type_id()); 1279 if (!sizeType || SpvOpTypeInt != sizeType->opcode()) { 1280 DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '" 1281 << inst->words[sizeIndex] 1282 << "'s variable type is not an integer type."; 1283 return false; 1284 } 1285 } break; 1286 default: 1287 DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '" 1288 << inst->words[sizeIndex] 1289 << "' is not a constant or variable."; 1290 return false; 1291 } 1292 // TODO: Check that consant is a least size 1, see the same bug as above for 1293 // clarification? 1294 return true; 1295 } 1296 1297 template <> 1298 bool idUsage::isValid<SpvOpAccessChain>(const spv_instruction_t* inst, 1299 const spv_opcode_desc) { 1300 std::string instr_name = 1301 "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode))); 1302 1303 // The result type must be OpTypePointer. Result Type is at word 1. 1304 auto resultTypeIndex = 1; 1305 auto resultTypeInstr = module_.FindDef(inst->words[resultTypeIndex]); 1306 if (SpvOpTypePointer != resultTypeInstr->opcode()) { 1307 DIAG(resultTypeIndex) << "The Result Type of " << instr_name << " <id> '" 1308 << inst->words[2] 1309 << "' must be OpTypePointer. Found Op" 1310 << spvOpcodeString( 1311 static_cast<SpvOp>(resultTypeInstr->opcode())) 1312 << "."; 1313 return false; 1314 } 1315 1316 // Result type is a pointer. Find out what it's pointing to. 1317 // This will be used to make sure the indexing results in the same type. 1318 // OpTypePointer word 3 is the type being pointed to. 1319 auto resultTypePointedTo = module_.FindDef(resultTypeInstr->word(3)); 1320 1321 // Base must be a pointer, pointing to the base of a composite object. 1322 auto baseIdIndex = 3; 1323 auto baseInstr = module_.FindDef(inst->words[baseIdIndex]); 1324 auto baseTypeInstr = module_.FindDef(baseInstr->type_id()); 1325 if (!baseTypeInstr || SpvOpTypePointer != baseTypeInstr->opcode()) { 1326 DIAG(baseIdIndex) << "The Base <id> '" << inst->words[baseIdIndex] 1327 << "' in " << instr_name 1328 << " instruction must be a pointer."; 1329 return false; 1330 } 1331 1332 // The result pointer storage class and base pointer storage class must match. 1333 // Word 2 of OpTypePointer is the Storage Class. 1334 auto resultTypeStorageClass = resultTypeInstr->word(2); 1335 auto baseTypeStorageClass = baseTypeInstr->word(2); 1336 if (resultTypeStorageClass != baseTypeStorageClass) { 1337 DIAG(resultTypeIndex) << "The result pointer storage class and base " 1338 "pointer storage class in " 1339 << instr_name << " do not match."; 1340 return false; 1341 } 1342 1343 // The type pointed to by OpTypePointer (word 3) must be a composite type. 1344 auto typePointedTo = module_.FindDef(baseTypeInstr->word(3)); 1345 1346 // Check Universal Limit (SPIR-V Spec. Section 2.17). 1347 // The number of indexes passed to OpAccessChain may not exceed 255 1348 // The instruction includes 4 words + N words (for N indexes) 1349 const size_t num_indexes = inst->words.size() - 4; 1350 const size_t num_indexes_limit = 1351 module_.options()->universal_limits_.max_access_chain_indexes; 1352 if (num_indexes > num_indexes_limit) { 1353 DIAG(resultTypeIndex) << "The number of indexes in " << instr_name 1354 << " may not exceed " << num_indexes_limit 1355 << ". Found " << num_indexes << " indexes."; 1356 return false; 1357 } 1358 // Indexes walk the type hierarchy to the desired depth, potentially down to 1359 // scalar granularity. The first index in Indexes will select the top-level 1360 // member/element/component/element of the base composite. All composite 1361 // constituents use zero-based numbering, as described by their OpType... 1362 // instruction. The second index will apply similarly to that result, and so 1363 // on. Once any non-composite type is reached, there must be no remaining 1364 // (unused) indexes. 1365 for (size_t i = 4; i < inst->words.size(); ++i) { 1366 const uint32_t cur_word = inst->words[i]; 1367 // Earlier ID checks ensure that cur_word definition exists. 1368 auto cur_word_instr = module_.FindDef(cur_word); 1369 // The index must be a scalar integer type (See OpAccessChain in the Spec.) 1370 auto indexTypeInstr = module_.FindDef(cur_word_instr->type_id()); 1371 if (!indexTypeInstr || SpvOpTypeInt != indexTypeInstr->opcode()) { 1372 DIAG(i) << "Indexes passed to " << instr_name 1373 << " must be of type integer."; 1374 return false; 1375 } 1376 switch (typePointedTo->opcode()) { 1377 case SpvOpTypeMatrix: 1378 case SpvOpTypeVector: 1379 case SpvOpTypeArray: 1380 case SpvOpTypeRuntimeArray: { 1381 // In OpTypeMatrix, OpTypeVector, OpTypeArray, and OpTypeRuntimeArray, 1382 // word 2 is the Element Type. 1383 typePointedTo = module_.FindDef(typePointedTo->word(2)); 1384 break; 1385 } 1386 case SpvOpTypeStruct: { 1387 // In case of structures, there is an additional constraint on the 1388 // index: the index must be an OpConstant. 1389 if (SpvOpConstant != cur_word_instr->opcode()) { 1390 DIAG(i) << "The <id> passed to " << instr_name 1391 << " to index into a " 1392 "structure must be an OpConstant."; 1393 return false; 1394 } 1395 // Get the index value from the OpConstant (word 3 of OpConstant). 1396 // OpConstant could be a signed integer. But it's okay to treat it as 1397 // unsigned because a negative constant int would never be seen as 1398 // correct as a struct offset, since structs can't have more than 2 1399 // billion members. 1400 const uint32_t cur_index = cur_word_instr->word(3); 1401 // The index points to the struct member we want, therefore, the index 1402 // should be less than the number of struct members. 1403 const uint32_t num_struct_members = 1404 static_cast<uint32_t>(typePointedTo->words().size() - 2); 1405 if (cur_index >= num_struct_members) { 1406 DIAG(i) << "Index is out of bounds: " << instr_name 1407 << " can not find index " << cur_index 1408 << " into the structure <id> '" << typePointedTo->id() 1409 << "'. This structure has " << num_struct_members 1410 << " members. Largest valid index is " 1411 << num_struct_members - 1 << "."; 1412 return false; 1413 } 1414 // Struct members IDs start at word 2 of OpTypeStruct. 1415 auto structMemberId = typePointedTo->word(cur_index + 2); 1416 typePointedTo = module_.FindDef(structMemberId); 1417 break; 1418 } 1419 default: { 1420 // Give an error. reached non-composite type while indexes still remain. 1421 DIAG(i) << instr_name << " reached non-composite type while indexes " 1422 "still remain to be traversed."; 1423 return false; 1424 } 1425 } 1426 } 1427 // At this point, we have fully walked down from the base using the indeces. 1428 // The type being pointed to should be the same as the result type. 1429 if (typePointedTo->id() != resultTypePointedTo->id()) { 1430 DIAG(resultTypeIndex) 1431 << instr_name << " result type (Op" 1432 << spvOpcodeString(static_cast<SpvOp>(resultTypePointedTo->opcode())) 1433 << ") does not match the type that results from indexing into the base " 1434 "<id> (Op" 1435 << spvOpcodeString(static_cast<SpvOp>(typePointedTo->opcode())) << ")."; 1436 return false; 1437 } 1438 1439 return true; 1440 } 1441 1442 template <> 1443 bool idUsage::isValid<SpvOpInBoundsAccessChain>( 1444 const spv_instruction_t* inst, const spv_opcode_desc opcodeEntry) { 1445 return isValid<SpvOpAccessChain>(inst, opcodeEntry); 1446 } 1447 1448 template <> 1449 bool idUsage::isValid<SpvOpPtrAccessChain>(const spv_instruction_t* inst, 1450 const spv_opcode_desc opcodeEntry) { 1451 // OpPtrAccessChain's validation rules are similar to OpAccessChain, with one 1452 // difference: word 4 must be id of an integer (Element <id>). 1453 // The grammar guarantees that there are at least 5 words in the instruction 1454 // (i.e. if there are fewer than 5 words, the SPIR-V code will not compile.) 1455 int elem_index = 4; 1456 // We can remove the Element <id> from the instruction words, and simply call 1457 // the validation code of OpAccessChain. 1458 spv_instruction_t new_inst = *inst; 1459 new_inst.words.erase(new_inst.words.begin() + elem_index); 1460 return isValid<SpvOpAccessChain>(&new_inst, opcodeEntry); 1461 } 1462 1463 template <> 1464 bool idUsage::isValid<SpvOpInBoundsPtrAccessChain>( 1465 const spv_instruction_t* inst, const spv_opcode_desc opcodeEntry) { 1466 // Has the same validation rules as OpPtrAccessChain 1467 return isValid<SpvOpPtrAccessChain>(inst, opcodeEntry); 1468 } 1469 1470 #if 0 1471 template <> 1472 bool idUsage::isValid<SpvOpArrayLength>(const spv_instruction_t *inst, 1473 const spv_opcode_desc opcodeEntry) {} 1474 #endif 1475 1476 #if 0 1477 template <> 1478 bool idUsage::isValid<SpvOpImagePointer>(const spv_instruction_t *inst, 1479 const spv_opcode_desc opcodeEntry) {} 1480 #endif 1481 1482 #if 0 1483 template <> 1484 bool idUsage::isValid<SpvOpGenericPtrMemSemantics>( 1485 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 1486 #endif 1487 1488 template <> 1489 bool idUsage::isValid<SpvOpFunction>(const spv_instruction_t* inst, 1490 const spv_opcode_desc) { 1491 auto resultTypeIndex = 1; 1492 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 1493 if (!resultType) return false; 1494 auto functionTypeIndex = 4; 1495 auto functionType = module_.FindDef(inst->words[functionTypeIndex]); 1496 if (!functionType || SpvOpTypeFunction != functionType->opcode()) { 1497 DIAG(functionTypeIndex) << "OpFunction Function Type <id> '" 1498 << inst->words[functionTypeIndex] 1499 << "' is not a function type."; 1500 return false; 1501 } 1502 auto returnType = module_.FindDef(functionType->words()[2]); 1503 assert(returnType); 1504 if (returnType->id() != resultType->id()) { 1505 DIAG(resultTypeIndex) << "OpFunction Result Type <id> '" 1506 << inst->words[resultTypeIndex] 1507 << "' does not match the Function Type <id> '" 1508 << resultType->id() << "'s return type."; 1509 return false; 1510 } 1511 return true; 1512 } 1513 1514 template <> 1515 bool idUsage::isValid<SpvOpFunctionParameter>(const spv_instruction_t* inst, 1516 const spv_opcode_desc) { 1517 auto resultTypeIndex = 1; 1518 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 1519 if (!resultType) return false; 1520 // NOTE: Find OpFunction & ensure OpFunctionParameter is not out of place. 1521 size_t paramIndex = 0; 1522 assert(firstInst < inst && "Invalid instruction pointer"); 1523 while (firstInst != --inst) { 1524 if (SpvOpFunction == inst->opcode) { 1525 break; 1526 } else if (SpvOpFunctionParameter == inst->opcode) { 1527 paramIndex++; 1528 } 1529 } 1530 auto functionType = module_.FindDef(inst->words[4]); 1531 assert(functionType); 1532 if (paramIndex >= functionType->words().size() - 3) { 1533 DIAG(0) << "Too many OpFunctionParameters for " << inst->words[2] 1534 << ": expected " << functionType->words().size() - 3 1535 << " based on the function's type"; 1536 return false; 1537 } 1538 auto paramType = module_.FindDef(functionType->words()[paramIndex + 3]); 1539 assert(paramType); 1540 if (resultType->id() != paramType->id()) { 1541 DIAG(resultTypeIndex) << "OpFunctionParameter Result Type <id> '" 1542 << inst->words[resultTypeIndex] 1543 << "' does not match the OpTypeFunction parameter " 1544 "type of the same index."; 1545 return false; 1546 } 1547 return true; 1548 } 1549 1550 template <> 1551 bool idUsage::isValid<SpvOpFunctionCall>(const spv_instruction_t* inst, 1552 const spv_opcode_desc) { 1553 auto resultTypeIndex = 1; 1554 auto resultType = module_.FindDef(inst->words[resultTypeIndex]); 1555 if (!resultType) return false; 1556 auto functionIndex = 3; 1557 auto function = module_.FindDef(inst->words[functionIndex]); 1558 if (!function || SpvOpFunction != function->opcode()) { 1559 DIAG(functionIndex) << "OpFunctionCall Function <id> '" 1560 << inst->words[functionIndex] << "' is not a function."; 1561 return false; 1562 } 1563 auto returnType = module_.FindDef(function->type_id()); 1564 assert(returnType); 1565 if (returnType->id() != resultType->id()) { 1566 DIAG(resultTypeIndex) << "OpFunctionCall Result Type <id> '" 1567 << inst->words[resultTypeIndex] 1568 << "'s type does not match Function <id> '" 1569 << returnType->id() << "'s return type."; 1570 return false; 1571 } 1572 auto functionType = module_.FindDef(function->words()[4]); 1573 assert(functionType); 1574 auto functionCallArgCount = inst->words.size() - 4; 1575 auto functionParamCount = functionType->words().size() - 3; 1576 if (functionParamCount != functionCallArgCount) { 1577 DIAG(inst->words.size() - 1) 1578 << "OpFunctionCall Function <id>'s parameter count does not match " 1579 "the argument count."; 1580 return false; 1581 } 1582 for (size_t argumentIndex = 4, paramIndex = 3; 1583 argumentIndex < inst->words.size(); argumentIndex++, paramIndex++) { 1584 auto argument = module_.FindDef(inst->words[argumentIndex]); 1585 if (!argument) return false; 1586 auto argumentType = module_.FindDef(argument->type_id()); 1587 assert(argumentType); 1588 auto parameterType = module_.FindDef(functionType->words()[paramIndex]); 1589 assert(parameterType); 1590 if (argumentType->id() != parameterType->id()) { 1591 DIAG(argumentIndex) << "OpFunctionCall Argument <id> '" 1592 << inst->words[argumentIndex] 1593 << "'s type does not match Function <id> '" 1594 << parameterType->id() << "'s parameter type."; 1595 return false; 1596 } 1597 } 1598 return true; 1599 } 1600 1601 #if 0 1602 template <> 1603 bool idUsage::isValid<OpConvertUToF>(const spv_instruction_t *inst, 1604 const spv_opcode_desc opcodeEntry) {} 1605 #endif 1606 1607 #if 0 1608 template <> 1609 bool idUsage::isValid<OpConvertFToS>(const spv_instruction_t *inst, 1610 const spv_opcode_desc opcodeEntry) {} 1611 #endif 1612 1613 #if 0 1614 template <> 1615 bool idUsage::isValid<OpConvertSToF>(const spv_instruction_t *inst, 1616 const spv_opcode_desc opcodeEntry) {} 1617 #endif 1618 1619 #if 0 1620 template <> 1621 bool idUsage::isValid<OpConvertUToF>(const spv_instruction_t *inst, 1622 const spv_opcode_desc opcodeEntry) {} 1623 #endif 1624 1625 #if 0 1626 template <> 1627 bool idUsage::isValid<OpUConvert>(const spv_instruction_t *inst, 1628 const spv_opcode_desc opcodeEntry) {} 1629 #endif 1630 1631 #if 0 1632 template <> 1633 bool idUsage::isValid<OpSConvert>(const spv_instruction_t *inst, 1634 const spv_opcode_desc opcodeEntry) {} 1635 #endif 1636 1637 #if 0 1638 template <> 1639 bool idUsage::isValid<OpFConvert>(const spv_instruction_t *inst, 1640 const spv_opcode_desc opcodeEntry) {} 1641 #endif 1642 1643 #if 0 1644 template <> 1645 bool idUsage::isValid<OpConvertPtrToU>(const spv_instruction_t *inst, 1646 const spv_opcode_desc opcodeEntry) { 1647 } 1648 #endif 1649 1650 #if 0 1651 template <> 1652 bool idUsage::isValid<OpConvertUToPtr>(const spv_instruction_t *inst, 1653 const spv_opcode_desc opcodeEntry) { 1654 } 1655 #endif 1656 1657 #if 0 1658 template <> 1659 bool idUsage::isValid<OpPtrCastToGeneric>( 1660 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 1661 #endif 1662 1663 #if 0 1664 template <> 1665 bool idUsage::isValid<OpGenericCastToPtr>( 1666 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 1667 #endif 1668 1669 #if 0 1670 template <> 1671 bool idUsage::isValid<OpBitcast>(const spv_instruction_t *inst, 1672 const spv_opcode_desc opcodeEntry) {} 1673 #endif 1674 1675 #if 0 1676 template <> 1677 bool idUsage::isValid<OpGenericCastToPtrExplicit>( 1678 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 1679 #endif 1680 1681 #if 0 1682 template <> 1683 bool idUsage::isValid<OpSatConvertSToU>(const spv_instruction_t *inst) {} 1684 #endif 1685 1686 #if 0 1687 template <> 1688 bool idUsage::isValid<OpSatConvertUToS>(const spv_instruction_t *inst) {} 1689 #endif 1690 1691 #if 0 1692 template <> 1693 bool idUsage::isValid<OpVectorExtractDynamic>( 1694 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 1695 #endif 1696 1697 #if 0 1698 template <> 1699 bool idUsage::isValid<OpVectorInsertDynamic>( 1700 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 1701 #endif 1702 1703 #if 0 1704 template <> 1705 bool idUsage::isValid<OpVectorShuffle>(const spv_instruction_t *inst, 1706 const spv_opcode_desc opcodeEntry) { 1707 } 1708 #endif 1709 1710 #if 0 1711 template <> 1712 bool idUsage::isValid<OpCompositeConstruct>( 1713 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 1714 #endif 1715 1716 // Walks the composite type hierarchy starting from the base. 1717 // At each step, the iterator is dereferenced to get the next literal index. 1718 // Indexes walk the type hierarchy to the desired depth, potentially down to 1719 // scalar granularity. The first index in Indexes will select the top-level 1720 // member/element/component/element of the base composite. All composite 1721 // constituents use zero-based numbering, as described by their OpType... 1722 // instruction. The second index will apply similarly to that result, and so 1723 // on. Once any non-composite type is reached, there must be no remaining 1724 // (unused) indexes. 1725 // Returns true on success and false otherwise. 1726 // If successful, the final type reached by indexing is returned by reference. 1727 // If an error occurs, the error string is returned by reference. 1728 bool walkCompositeTypeHierarchy( 1729 const ValidationState_t& module, 1730 std::vector<uint32_t>::const_iterator word_iter, 1731 std::vector<uint32_t>::const_iterator word_iter_end, 1732 const libspirv::Instruction* base, 1733 const libspirv::Instruction** result_type_instr, 1734 std::function<std::string(void)> instr_name, std::ostream* error) { 1735 auto cur_type = base; 1736 for (; word_iter != word_iter_end; ++word_iter) { 1737 switch (cur_type->opcode()) { 1738 case SpvOpTypeMatrix: 1739 case SpvOpTypeVector: 1740 case SpvOpTypeArray: 1741 case SpvOpTypeRuntimeArray: { 1742 // In OpTypeMatrix, OpTypeVector, OpTypeArray, and OpTypeRuntimeArray, 1743 // word 2 is the Element Type. 1744 cur_type = module.FindDef(cur_type->word(2)); 1745 break; 1746 } 1747 case SpvOpTypeStruct: { 1748 // Get the index into the structure. 1749 const uint32_t cur_index = *word_iter; 1750 // The index points to the struct member we want, therefore, the index 1751 // should be less than the number of struct members. 1752 const uint32_t num_struct_members = 1753 static_cast<uint32_t>(cur_type->words().size() - 2); 1754 if (cur_index >= num_struct_members) { 1755 *error << "Index is out of bounds: " << instr_name() 1756 << " can not find index " << cur_index 1757 << " into the structure <id> '" << cur_type->id() 1758 << "'. This structure has " << num_struct_members 1759 << " members. Largest valid index is " 1760 << num_struct_members - 1 << "."; 1761 return false; 1762 } 1763 // Struct members IDs start at word 2 of OpTypeStruct. 1764 auto structMemberId = cur_type->word(cur_index + 2); 1765 cur_type = module.FindDef(structMemberId); 1766 break; 1767 } 1768 default: { 1769 // Give an error. reached non-composite type while indexes still remain. 1770 *error << instr_name() << " reached non-composite type while indexes " 1771 "still remain to be traversed."; 1772 return false; 1773 } 1774 } 1775 } 1776 *result_type_instr = cur_type; 1777 return true; 1778 } 1779 1780 template <> 1781 bool idUsage::isValid<SpvOpCompositeExtract>(const spv_instruction_t* inst, 1782 const spv_opcode_desc) { 1783 auto instr_name = [&inst]() { 1784 std::string name = 1785 "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode))); 1786 return name; 1787 }; 1788 1789 // Remember the result type. Result Type is at word 1. 1790 // This will be used to make sure the indexing results in the same type. 1791 const size_t resultTypeIndex = 1; 1792 auto resultTypeInstr = module_.FindDef(inst->words[resultTypeIndex]); 1793 1794 // The Composite <id> is at word 3. ID definition checks ensure this id is 1795 // already defined. 1796 auto baseInstr = module_.FindDef(inst->words[3]); 1797 auto curTypeInstr = module_.FindDef(baseInstr->type_id()); 1798 1799 // Check Universal Limit (SPIR-V Spec. Section 2.17). 1800 // The number of indexes passed to OpCompositeExtract may not exceed 255. 1801 // The instruction includes 4 words + N words (for N indexes) 1802 const size_t num_indexes = inst->words.size() - 4; 1803 const size_t num_indexes_limit = 255; 1804 if (num_indexes > num_indexes_limit) { 1805 DIAG(resultTypeIndex) << "The number of indexes in " << instr_name() 1806 << " may not exceed " << num_indexes_limit 1807 << ". Found " << num_indexes << " indexes."; 1808 return false; 1809 } 1810 1811 // Walk down the composite type structure. Indexes start at word 4. 1812 const libspirv::Instruction* indexedTypeInstr = nullptr; 1813 std::ostringstream error; 1814 bool success = walkCompositeTypeHierarchy( 1815 module_, inst->words.begin() + 4, inst->words.end(), curTypeInstr, 1816 &indexedTypeInstr, instr_name, &error); 1817 if (!success) { 1818 DIAG(resultTypeIndex) << error.str(); 1819 return success; 1820 } 1821 1822 // At this point, we have fully walked down from the base using the indexes. 1823 // The type being pointed to should be the same as the result type. 1824 if (indexedTypeInstr->id() != resultTypeInstr->id()) { 1825 DIAG(resultTypeIndex) 1826 << instr_name() << " result type (Op" 1827 << spvOpcodeString(static_cast<SpvOp>(resultTypeInstr->opcode())) 1828 << ") does not match the type that results from indexing into the " 1829 "composite (Op" 1830 << spvOpcodeString(static_cast<SpvOp>(indexedTypeInstr->opcode())) 1831 << ")."; 1832 return false; 1833 } 1834 1835 return true; 1836 } 1837 1838 template <> 1839 bool idUsage::isValid<SpvOpCompositeInsert>(const spv_instruction_t* inst, 1840 const spv_opcode_desc) { 1841 auto instr_name = [&inst]() { 1842 std::string name = 1843 "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode))); 1844 return name; 1845 }; 1846 1847 // Result Type must be the same as Composite type. Result Type <id> is the 1848 // word at index 1. Composite is at word 4. 1849 // The grammar guarantees that the instruction has at least 5 words. 1850 // ID definition checks ensure these IDs are already defined. 1851 const size_t resultTypeIndex = 1; 1852 const size_t resultIdIndex = 2; 1853 const size_t compositeIndex = 4; 1854 auto resultTypeInstr = module_.FindDef(inst->words[resultTypeIndex]); 1855 auto compositeInstr = module_.FindDef(inst->words[compositeIndex]); 1856 auto compositeTypeInstr = module_.FindDef(compositeInstr->type_id()); 1857 if (resultTypeInstr != compositeTypeInstr) { 1858 DIAG(resultTypeIndex) 1859 << "The Result Type must be the same as Composite type in " 1860 << instr_name() << " yielding Result Id " << inst->words[resultIdIndex] 1861 << "."; 1862 return false; 1863 } 1864 1865 // Check Universal Limit (SPIR-V Spec. Section 2.17). 1866 // The number of indexes passed to OpCompositeInsert may not exceed 255. 1867 // The instruction includes 5 words + N words (for N indexes) 1868 const size_t num_indexes = inst->words.size() - 5; 1869 const size_t num_indexes_limit = 255; 1870 if (num_indexes > num_indexes_limit) { 1871 DIAG(resultTypeIndex) << "The number of indexes in " << instr_name() 1872 << " may not exceed " << num_indexes_limit 1873 << ". Found " << num_indexes << " indexes."; 1874 return false; 1875 } 1876 1877 // Walk the composite type structure. Indexes start at word 5. 1878 const libspirv::Instruction* indexedTypeInstr = nullptr; 1879 std::ostringstream error; 1880 bool success = walkCompositeTypeHierarchy( 1881 module_, inst->words.begin() + 5, inst->words.end(), compositeTypeInstr, 1882 &indexedTypeInstr, instr_name, &error); 1883 if (!success) { 1884 DIAG(resultTypeIndex) << error.str(); 1885 return success; 1886 } 1887 1888 // At this point, we have fully walked down from the base using the indexes. 1889 // The type being pointed to should be the same as the object type that is 1890 // about to be inserted. 1891 auto objectIdIndex = 3; 1892 auto objectInstr = module_.FindDef(inst->words[objectIdIndex]); 1893 auto objectTypeInstr = module_.FindDef(objectInstr->type_id()); 1894 if (indexedTypeInstr->id() != objectTypeInstr->id()) { 1895 DIAG(objectIdIndex) 1896 << "The Object type (Op" 1897 << spvOpcodeString(static_cast<SpvOp>(objectTypeInstr->opcode())) 1898 << ") in " << instr_name() << " does not match the type that results " 1899 "from indexing into the Composite (Op" 1900 << spvOpcodeString(static_cast<SpvOp>(indexedTypeInstr->opcode())) 1901 << ")."; 1902 return false; 1903 } 1904 1905 return true; 1906 } 1907 1908 #if 0 1909 template <> 1910 bool idUsage::isValid<OpCopyObject>(const spv_instruction_t *inst, 1911 const spv_opcode_desc opcodeEntry) {} 1912 #endif 1913 1914 #if 0 1915 template <> 1916 bool idUsage::isValid<OpTranspose>(const spv_instruction_t *inst, 1917 const spv_opcode_desc opcodeEntry) {} 1918 #endif 1919 1920 #if 0 1921 template <> 1922 bool idUsage::isValid<OpSNegate>(const spv_instruction_t *inst, 1923 const spv_opcode_desc opcodeEntry) {} 1924 #endif 1925 1926 #if 0 1927 template <> 1928 bool idUsage::isValid<OpFNegate>(const spv_instruction_t *inst, 1929 const spv_opcode_desc opcodeEntry) {} 1930 #endif 1931 1932 #if 0 1933 template <> 1934 bool idUsage::isValid<OpNot>(const spv_instruction_t *inst, 1935 const spv_opcode_desc opcodeEntry) {} 1936 #endif 1937 1938 #if 0 1939 template <> 1940 bool idUsage::isValid<OpIAdd>(const spv_instruction_t *inst, 1941 const spv_opcode_desc opcodeEntry) {} 1942 #endif 1943 1944 #if 0 1945 template <> 1946 bool idUsage::isValid<OpFAdd>(const spv_instruction_t *inst, 1947 const spv_opcode_desc opcodeEntry) {} 1948 #endif 1949 1950 #if 0 1951 template <> 1952 bool idUsage::isValid<OpISub>(const spv_instruction_t *inst, 1953 const spv_opcode_desc opcodeEntry) {} 1954 #endif 1955 1956 #if 0 1957 template <> 1958 bool idUsage::isValid<OpFSub>(const spv_instruction_t *inst, 1959 const spv_opcode_desc opcodeEntry) {} 1960 #endif 1961 1962 #if 0 1963 template <> 1964 bool idUsage::isValid<OpIMul>(const spv_instruction_t *inst, 1965 const spv_opcode_desc opcodeEntry) {} 1966 #endif 1967 1968 #if 0 1969 template <> 1970 bool idUsage::isValid<OpFMul>(const spv_instruction_t *inst, 1971 const spv_opcode_desc opcodeEntry) {} 1972 #endif 1973 1974 #if 0 1975 template <> 1976 bool idUsage::isValid<OpUDiv>(const spv_instruction_t *inst, 1977 const spv_opcode_desc opcodeEntry) {} 1978 #endif 1979 1980 #if 0 1981 template <> 1982 bool idUsage::isValid<OpSDiv>(const spv_instruction_t *inst, 1983 const spv_opcode_desc opcodeEntry) {} 1984 #endif 1985 1986 #if 0 1987 template <> 1988 bool idUsage::isValid<OpFDiv>(const spv_instruction_t *inst, 1989 const spv_opcode_desc opcodeEntry) {} 1990 #endif 1991 1992 #if 0 1993 template <> 1994 bool idUsage::isValid<OpUMod>(const spv_instruction_t *inst, 1995 const spv_opcode_desc opcodeEntry) {} 1996 #endif 1997 1998 #if 0 1999 template <> 2000 bool idUsage::isValid<OpSRem>(const spv_instruction_t *inst, 2001 const spv_opcode_desc opcodeEntry) {} 2002 #endif 2003 2004 #if 0 2005 template <> 2006 bool idUsage::isValid<OpSMod>(const spv_instruction_t *inst, 2007 const spv_opcode_desc opcodeEntry) {} 2008 #endif 2009 2010 #if 0 2011 template <> 2012 bool idUsage::isValid<OpFRem>(const spv_instruction_t *inst, 2013 const spv_opcode_desc opcodeEntry) {} 2014 #endif 2015 2016 #if 0 2017 template <> 2018 bool idUsage::isValid<OpFMod>(const spv_instruction_t *inst, 2019 const spv_opcode_desc opcodeEntry) {} 2020 #endif 2021 2022 #if 0 2023 template <> 2024 bool idUsage::isValid<OpVectorTimesScalar>( 2025 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2026 #endif 2027 2028 #if 0 2029 template <> 2030 bool idUsage::isValid<OpMatrixTimesScalar>( 2031 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2032 #endif 2033 2034 #if 0 2035 template <> 2036 bool idUsage::isValid<OpVectorTimesMatrix>( 2037 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2038 #endif 2039 2040 #if 0 2041 template <> 2042 bool idUsage::isValid<OpMatrixTimesVector>( 2043 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2044 #endif 2045 2046 #if 0 2047 template <> 2048 bool idUsage::isValid<OpMatrixTimesMatrix>( 2049 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2050 #endif 2051 2052 #if 0 2053 template <> 2054 bool idUsage::isValid<OpOuterProduct>(const spv_instruction_t *inst, 2055 const spv_opcode_desc opcodeEntry) {} 2056 #endif 2057 2058 #if 0 2059 template <> 2060 bool idUsage::isValid<OpDot>(const spv_instruction_t *inst, 2061 const spv_opcode_desc opcodeEntry) {} 2062 #endif 2063 2064 #if 0 2065 template <> 2066 bool idUsage::isValid<OpShiftRightLogical>( 2067 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2068 #endif 2069 2070 #if 0 2071 template <> 2072 bool idUsage::isValid<OpShiftRightArithmetic>( 2073 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2074 #endif 2075 2076 #if 0 2077 template <> 2078 bool idUsage::isValid<OpShiftLeftLogical>( 2079 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2080 #endif 2081 2082 #if 0 2083 template <> 2084 bool idUsage::isValid<OpBitwiseOr>(const spv_instruction_t *inst, 2085 const spv_opcode_desc opcodeEntry) {} 2086 #endif 2087 2088 #if 0 2089 template <> 2090 bool idUsage::isValid<OpBitwiseXor>(const spv_instruction_t *inst, 2091 const spv_opcode_desc opcodeEntry) {} 2092 #endif 2093 2094 #if 0 2095 template <> 2096 bool idUsage::isValid<OpBitwiseAnd>(const spv_instruction_t *inst, 2097 const spv_opcode_desc opcodeEntry) {} 2098 #endif 2099 2100 #if 0 2101 template <> 2102 bool idUsage::isValid<OpAny>(const spv_instruction_t *inst, 2103 const spv_opcode_desc opcodeEntry) {} 2104 #endif 2105 2106 #if 0 2107 template <> 2108 bool idUsage::isValid<OpAll>(const spv_instruction_t *inst, 2109 const spv_opcode_desc opcodeEntry) {} 2110 #endif 2111 2112 #if 0 2113 template <> 2114 bool idUsage::isValid<OpIsNan>(const spv_instruction_t *inst, 2115 const spv_opcode_desc opcodeEntry) {} 2116 #endif 2117 2118 #if 0 2119 template <> 2120 bool idUsage::isValid<OpIsInf>(const spv_instruction_t *inst, 2121 const spv_opcode_desc opcodeEntry) {} 2122 #endif 2123 2124 #if 0 2125 template <> 2126 bool idUsage::isValid<OpIsFinite>(const spv_instruction_t *inst, 2127 const spv_opcode_desc opcodeEntry) {} 2128 #endif 2129 2130 #if 0 2131 template <> 2132 bool idUsage::isValid<OpIsNormal>(const spv_instruction_t *inst, 2133 const spv_opcode_desc opcodeEntry) {} 2134 #endif 2135 2136 #if 0 2137 template <> 2138 bool idUsage::isValid<OpSignBitSet>(const spv_instruction_t *inst, 2139 const spv_opcode_desc opcodeEntry) {} 2140 #endif 2141 2142 #if 0 2143 template <> 2144 bool idUsage::isValid<OpLessOrGreater>(const spv_instruction_t *inst, 2145 const spv_opcode_desc opcodeEntry) { 2146 } 2147 #endif 2148 2149 #if 0 2150 template <> 2151 bool idUsage::isValid<OpOrdered>(const spv_instruction_t *inst, 2152 const spv_opcode_desc opcodeEntry) {} 2153 #endif 2154 2155 #if 0 2156 template <> 2157 bool idUsage::isValid<OpUnordered>(const spv_instruction_t *inst, 2158 const spv_opcode_desc opcodeEntry) {} 2159 #endif 2160 2161 #if 0 2162 template <> 2163 bool idUsage::isValid<OpLogicalOr>(const spv_instruction_t *inst, 2164 const spv_opcode_desc opcodeEntry) {} 2165 #endif 2166 2167 #if 0 2168 template <> 2169 bool idUsage::isValid<OpLogicalXor>(const spv_instruction_t *inst, 2170 const spv_opcode_desc opcodeEntry) {} 2171 #endif 2172 2173 #if 0 2174 template <> 2175 bool idUsage::isValid<OpLogicalAnd>(const spv_instruction_t *inst, 2176 const spv_opcode_desc opcodeEntry) {} 2177 #endif 2178 2179 #if 0 2180 template <> 2181 bool idUsage::isValid<OpSelect>(const spv_instruction_t *inst, 2182 const spv_opcode_desc opcodeEntry) {} 2183 #endif 2184 2185 #if 0 2186 template <> 2187 bool idUsage::isValid<OpIEqual>(const spv_instruction_t *inst, 2188 const spv_opcode_desc opcodeEntry) {} 2189 #endif 2190 2191 #if 0 2192 template <> 2193 bool idUsage::isValid<OpFOrdEqual>(const spv_instruction_t *inst, 2194 const spv_opcode_desc opcodeEntry) {} 2195 #endif 2196 2197 #if 0 2198 template <> 2199 bool idUsage::isValid<OpFUnordEqual>(const spv_instruction_t *inst, 2200 const spv_opcode_desc opcodeEntry) {} 2201 #endif 2202 2203 #if 0 2204 template <> 2205 bool idUsage::isValid<OpINotEqual>(const spv_instruction_t *inst, 2206 const spv_opcode_desc opcodeEntry) {} 2207 #endif 2208 2209 #if 0 2210 template <> 2211 bool idUsage::isValid<OpFOrdNotEqual>(const spv_instruction_t *inst, 2212 const spv_opcode_desc opcodeEntry) {} 2213 #endif 2214 2215 #if 0 2216 template <> 2217 bool idUsage::isValid<OpFUnordNotEqual>( 2218 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2219 #endif 2220 2221 #if 0 2222 template <> 2223 bool idUsage::isValid<OpULessThan>(const spv_instruction_t *inst, 2224 const spv_opcode_desc opcodeEntry) {} 2225 #endif 2226 2227 #if 0 2228 template <> 2229 bool idUsage::isValid<OpSLessThan>(const spv_instruction_t *inst, 2230 const spv_opcode_desc opcodeEntry) {} 2231 #endif 2232 2233 #if 0 2234 template <> 2235 bool idUsage::isValid<OpFOrdLessThan>(const spv_instruction_t *inst, 2236 const spv_opcode_desc opcodeEntry) {} 2237 #endif 2238 2239 #if 0 2240 template <> 2241 bool idUsage::isValid<OpFUnordLessThan>( 2242 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2243 #endif 2244 2245 #if 0 2246 template <> 2247 bool idUsage::isValid<OpUGreaterThan>(const spv_instruction_t *inst, 2248 const spv_opcode_desc opcodeEntry) {} 2249 #endif 2250 2251 #if 0 2252 template <> 2253 bool idUsage::isValid<OpSGreaterThan>(const spv_instruction_t *inst, 2254 const spv_opcode_desc opcodeEntry) {} 2255 #endif 2256 2257 #if 0 2258 template <> 2259 bool idUsage::isValid<OpFOrdGreaterThan>( 2260 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2261 #endif 2262 2263 #if 0 2264 template <> 2265 bool idUsage::isValid<OpFUnordGreaterThan>( 2266 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2267 #endif 2268 2269 #if 0 2270 template <> 2271 bool idUsage::isValid<OpULessThanEqual>( 2272 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2273 #endif 2274 2275 #if 0 2276 template <> 2277 bool idUsage::isValid<OpSLessThanEqual>( 2278 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2279 #endif 2280 2281 #if 0 2282 template <> 2283 bool idUsage::isValid<OpFOrdLessThanEqual>( 2284 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2285 #endif 2286 2287 #if 0 2288 template <> 2289 bool idUsage::isValid<OpFUnordLessThanEqual>( 2290 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2291 #endif 2292 2293 #if 0 2294 template <> 2295 bool idUsage::isValid<OpUGreaterThanEqual>( 2296 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2297 #endif 2298 2299 #if 0 2300 template <> 2301 bool idUsage::isValid<OpSGreaterThanEqual>( 2302 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2303 #endif 2304 2305 #if 0 2306 template <> 2307 bool idUsage::isValid<OpFOrdGreaterThanEqual>( 2308 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2309 #endif 2310 2311 #if 0 2312 template <> 2313 bool idUsage::isValid<OpFUnordGreaterThanEqual>( 2314 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2315 #endif 2316 2317 #if 0 2318 template <> 2319 bool idUsage::isValid<OpDPdx>(const spv_instruction_t *inst, 2320 const spv_opcode_desc opcodeEntry) {} 2321 #endif 2322 2323 #if 0 2324 template <> 2325 bool idUsage::isValid<OpDPdy>(const spv_instruction_t *inst, 2326 const spv_opcode_desc opcodeEntry) {} 2327 #endif 2328 2329 #if 0 2330 template <> 2331 bool idUsage::isValid<OpFWidth>(const spv_instruction_t *inst, 2332 const spv_opcode_desc opcodeEntry) {} 2333 #endif 2334 2335 #if 0 2336 template <> 2337 bool idUsage::isValid<OpDPdxFine>(const spv_instruction_t *inst, 2338 const spv_opcode_desc opcodeEntry) {} 2339 #endif 2340 2341 #if 0 2342 template <> 2343 bool idUsage::isValid<OpDPdyFine>(const spv_instruction_t *inst, 2344 const spv_opcode_desc opcodeEntry) {} 2345 #endif 2346 2347 #if 0 2348 template <> 2349 bool idUsage::isValid<OpFwidthFine>(const spv_instruction_t *inst, 2350 const spv_opcode_desc opcodeEntry) {} 2351 #endif 2352 2353 #if 0 2354 template <> 2355 bool idUsage::isValid<OpDPdxCoarse>(const spv_instruction_t *inst, 2356 const spv_opcode_desc opcodeEntry) {} 2357 #endif 2358 2359 #if 0 2360 template <> 2361 bool idUsage::isValid<OpDPdyCoarse>(const spv_instruction_t *inst, 2362 const spv_opcode_desc opcodeEntry) {} 2363 #endif 2364 2365 #if 0 2366 template <> 2367 bool idUsage::isValid<OpFwidthCoarse>(const spv_instruction_t *inst, 2368 const spv_opcode_desc opcodeEntry) {} 2369 #endif 2370 2371 #if 0 2372 template <> 2373 bool idUsage::isValid<OpPhi>(const spv_instruction_t *inst, 2374 const spv_opcode_desc opcodeEntry) {} 2375 #endif 2376 2377 #if 0 2378 template <> 2379 bool idUsage::isValid<OpLoopMerge>(const spv_instruction_t *inst, 2380 const spv_opcode_desc opcodeEntry) {} 2381 #endif 2382 2383 #if 0 2384 template <> 2385 bool idUsage::isValid<OpSelectionMerge>( 2386 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2387 #endif 2388 2389 #if 0 2390 template <> 2391 bool idUsage::isValid<OpBranch>(const spv_instruction_t *inst, 2392 const spv_opcode_desc opcodeEntry) {} 2393 #endif 2394 2395 #if 0 2396 template <> 2397 bool idUsage::isValid<OpBranchConditional>( 2398 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2399 #endif 2400 2401 #if 0 2402 template <> 2403 bool idUsage::isValid<OpSwitch>(const spv_instruction_t *inst, 2404 const spv_opcode_desc opcodeEntry) {} 2405 #endif 2406 2407 template <> 2408 bool idUsage::isValid<SpvOpReturnValue>(const spv_instruction_t* inst, 2409 const spv_opcode_desc) { 2410 auto valueIndex = 1; 2411 auto value = module_.FindDef(inst->words[valueIndex]); 2412 if (!value || !value->type_id()) { 2413 DIAG(valueIndex) << "OpReturnValue Value <id> '" << inst->words[valueIndex] 2414 << "' does not represent a value."; 2415 return false; 2416 } 2417 auto valueType = module_.FindDef(value->type_id()); 2418 if (!valueType || SpvOpTypeVoid == valueType->opcode()) { 2419 DIAG(valueIndex) << "OpReturnValue value's type <id> '" << value->type_id() 2420 << "' is missing or void."; 2421 return false; 2422 } 2423 const bool uses_variable_pointer = 2424 module_.features().variable_pointers || 2425 module_.features().variable_pointers_storage_buffer; 2426 if (addressingModel == SpvAddressingModelLogical && 2427 SpvOpTypePointer == valueType->opcode() && !uses_variable_pointer) { 2428 DIAG(valueIndex) 2429 << "OpReturnValue value's type <id> '" << value->type_id() 2430 << "' is a pointer, which is invalid in the Logical addressing model."; 2431 return false; 2432 } 2433 // NOTE: Find OpFunction 2434 const spv_instruction_t* function = inst - 1; 2435 while (firstInst != function) { 2436 if (SpvOpFunction == function->opcode) break; 2437 function--; 2438 } 2439 if (SpvOpFunction != function->opcode) { 2440 DIAG(valueIndex) << "OpReturnValue is not in a basic block."; 2441 return false; 2442 } 2443 auto returnType = module_.FindDef(function->words[1]); 2444 if (!returnType || returnType->id() != valueType->id()) { 2445 DIAG(valueIndex) << "OpReturnValue Value <id> '" << inst->words[valueIndex] 2446 << "'s type does not match OpFunction's return type."; 2447 return false; 2448 } 2449 return true; 2450 } 2451 2452 #if 0 2453 template <> 2454 bool idUsage::isValid<OpLifetimeStart>(const spv_instruction_t *inst, 2455 const spv_opcode_desc opcodeEntry) { 2456 } 2457 #endif 2458 2459 #if 0 2460 template <> 2461 bool idUsage::isValid<OpLifetimeStop>(const spv_instruction_t *inst, 2462 const spv_opcode_desc opcodeEntry) {} 2463 #endif 2464 2465 #if 0 2466 template <> 2467 bool idUsage::isValid<OpAtomicInit>(const spv_instruction_t *inst, 2468 const spv_opcode_desc opcodeEntry) {} 2469 #endif 2470 2471 #if 0 2472 template <> 2473 bool idUsage::isValid<OpAtomicLoad>(const spv_instruction_t *inst, 2474 const spv_opcode_desc opcodeEntry) {} 2475 #endif 2476 2477 #if 0 2478 template <> 2479 bool idUsage::isValid<OpAtomicStore>(const spv_instruction_t *inst, 2480 const spv_opcode_desc opcodeEntry) {} 2481 #endif 2482 2483 #if 0 2484 template <> 2485 bool idUsage::isValid<OpAtomicExchange>( 2486 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2487 #endif 2488 2489 #if 0 2490 template <> 2491 bool idUsage::isValid<OpAtomicCompareExchange>( 2492 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2493 #endif 2494 2495 #if 0 2496 template <> 2497 bool idUsage::isValid<OpAtomicCompareExchangeWeak>( 2498 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2499 #endif 2500 2501 #if 0 2502 template <> 2503 bool idUsage::isValid<OpAtomicIIncrement>( 2504 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2505 #endif 2506 2507 #if 0 2508 template <> 2509 bool idUsage::isValid<OpAtomicIDecrement>( 2510 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2511 #endif 2512 2513 #if 0 2514 template <> 2515 bool idUsage::isValid<OpAtomicIAdd>(const spv_instruction_t *inst, 2516 const spv_opcode_desc opcodeEntry) {} 2517 #endif 2518 2519 #if 0 2520 template <> 2521 bool idUsage::isValid<OpAtomicISub>(const spv_instruction_t *inst, 2522 const spv_opcode_desc opcodeEntry) {} 2523 #endif 2524 2525 #if 0 2526 template <> 2527 bool idUsage::isValid<OpAtomicUMin>(const spv_instruction_t *inst, 2528 const spv_opcode_desc opcodeEntry) {} 2529 #endif 2530 2531 #if 0 2532 template <> 2533 bool idUsage::isValid<OpAtomicUMax>(const spv_instruction_t *inst, 2534 const spv_opcode_desc opcodeEntry) {} 2535 #endif 2536 2537 #if 0 2538 template <> 2539 bool idUsage::isValid<OpAtomicAnd>(const spv_instruction_t *inst, 2540 const spv_opcode_desc opcodeEntry) {} 2541 #endif 2542 2543 #if 0 2544 template <> 2545 bool idUsage::isValid<OpAtomicOr>(const spv_instruction_t *inst, 2546 const spv_opcode_desc opcodeEntry) {} 2547 #endif 2548 2549 #if 0 2550 template <> 2551 bool idUsage::isValid<OpAtomicXor>(const spv_instruction_t *inst, 2552 const spv_opcode_desc opcodeEntry) {} 2553 #endif 2554 2555 #if 0 2556 template <> 2557 bool idUsage::isValid<OpAtomicIMin>(const spv_instruction_t *inst, 2558 const spv_opcode_desc opcodeEntry) {} 2559 #endif 2560 2561 #if 0 2562 template <> 2563 bool idUsage::isValid<OpAtomicIMax>(const spv_instruction_t *inst, 2564 const spv_opcode_desc opcodeEntry) {} 2565 #endif 2566 2567 #if 0 2568 template <> 2569 bool idUsage::isValid<OpEmitStreamVertex>( 2570 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2571 #endif 2572 2573 #if 0 2574 template <> 2575 bool idUsage::isValid<OpEndStreamPrimitive>( 2576 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2577 #endif 2578 2579 #if 0 2580 template <> 2581 bool idUsage::isValid<OpGroupAsyncCopy>( 2582 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2583 #endif 2584 2585 #if 0 2586 template <> 2587 bool idUsage::isValid<OpGroupWaitEvents>( 2588 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2589 #endif 2590 2591 #if 0 2592 template <> 2593 bool idUsage::isValid<OpGroupAll>(const spv_instruction_t *inst, 2594 const spv_opcode_desc opcodeEntry) {} 2595 #endif 2596 2597 #if 0 2598 template <> 2599 bool idUsage::isValid<OpGroupAny>(const spv_instruction_t *inst, 2600 const spv_opcode_desc opcodeEntry) {} 2601 #endif 2602 2603 #if 0 2604 template <> 2605 bool idUsage::isValid<OpGroupBroadcast>( 2606 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2607 #endif 2608 2609 #if 0 2610 template <> 2611 bool idUsage::isValid<OpGroupIAdd>(const spv_instruction_t *inst, 2612 const spv_opcode_desc opcodeEntry) {} 2613 #endif 2614 2615 #if 0 2616 template <> 2617 bool idUsage::isValid<OpGroupFAdd>(const spv_instruction_t *inst, 2618 const spv_opcode_desc opcodeEntry) {} 2619 #endif 2620 2621 #if 0 2622 template <> 2623 bool idUsage::isValid<OpGroupFMin>(const spv_instruction_t *inst, 2624 const spv_opcode_desc opcodeEntry) {} 2625 #endif 2626 2627 #if 0 2628 template <> 2629 bool idUsage::isValid<OpGroupUMin>(const spv_instruction_t *inst, 2630 const spv_opcode_desc opcodeEntry) {} 2631 #endif 2632 2633 #if 0 2634 template <> 2635 bool idUsage::isValid<OpGroupSMin>(const spv_instruction_t *inst, 2636 const spv_opcode_desc opcodeEntry) {} 2637 #endif 2638 2639 #if 0 2640 template <> 2641 bool idUsage::isValid<OpGroupFMax>(const spv_instruction_t *inst, 2642 const spv_opcode_desc opcodeEntry) {} 2643 #endif 2644 2645 #if 0 2646 template <> 2647 bool idUsage::isValid<OpGroupUMax>(const spv_instruction_t *inst, 2648 const spv_opcode_desc opcodeEntry) {} 2649 #endif 2650 2651 #if 0 2652 template <> 2653 bool idUsage::isValid<OpGroupSMax>(const spv_instruction_t *inst, 2654 const spv_opcode_desc opcodeEntry) {} 2655 #endif 2656 2657 #if 0 2658 template <> 2659 bool idUsage::isValid<OpEnqueueMarker>(const spv_instruction_t *inst, 2660 const spv_opcode_desc opcodeEntry) { 2661 } 2662 #endif 2663 2664 #if 0 2665 template <> 2666 bool idUsage::isValid<OpEnqueueKernel>(const spv_instruction_t *inst, 2667 const spv_opcode_desc opcodeEntry) { 2668 } 2669 #endif 2670 2671 #if 0 2672 template <> 2673 bool idUsage::isValid<OpGetKernelNDrangeSubGroupCount>( 2674 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2675 #endif 2676 2677 #if 0 2678 template <> 2679 bool idUsage::isValid<OpGetKernelNDrangeMaxSubGroupSize>( 2680 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2681 #endif 2682 2683 #if 0 2684 template <> 2685 bool idUsage::isValid<OpGetKernelWorkGroupSize>( 2686 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2687 #endif 2688 2689 #if 0 2690 template <> 2691 bool idUsage::isValid<OpGetKernelPreferredWorkGroupSizeMultiple>( 2692 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2693 #endif 2694 2695 #if 0 2696 template <> 2697 bool idUsage::isValid<OpRetainEvent>(const spv_instruction_t *inst, 2698 const spv_opcode_desc opcodeEntry) {} 2699 #endif 2700 2701 #if 0 2702 template <> 2703 bool idUsage::isValid<OpReleaseEvent>(const spv_instruction_t *inst, 2704 const spv_opcode_desc opcodeEntry) {} 2705 #endif 2706 2707 #if 0 2708 template <> 2709 bool idUsage::isValid<OpCreateUserEvent>( 2710 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2711 #endif 2712 2713 #if 0 2714 template <> 2715 bool idUsage::isValid<OpIsValidEvent>(const spv_instruction_t *inst, 2716 const spv_opcode_desc opcodeEntry) {} 2717 #endif 2718 2719 #if 0 2720 template <> 2721 bool idUsage::isValid<OpSetUserEventStatus>( 2722 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2723 #endif 2724 2725 #if 0 2726 template <> 2727 bool idUsage::isValid<OpCaptureEventProfilingInfo>( 2728 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2729 #endif 2730 2731 #if 0 2732 template <> 2733 bool idUsage::isValid<OpGetDefaultQueue>( 2734 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2735 #endif 2736 2737 #if 0 2738 template <> 2739 bool idUsage::isValid<OpBuildNDRange>(const spv_instruction_t *inst, 2740 const spv_opcode_desc opcodeEntry) {} 2741 #endif 2742 2743 #if 0 2744 template <> 2745 bool idUsage::isValid<OpReadPipe>(const spv_instruction_t *inst, 2746 const spv_opcode_desc opcodeEntry) {} 2747 #endif 2748 2749 #if 0 2750 template <> 2751 bool idUsage::isValid<OpWritePipe>(const spv_instruction_t *inst, 2752 const spv_opcode_desc opcodeEntry) {} 2753 #endif 2754 2755 #if 0 2756 template <> 2757 bool idUsage::isValid<OpReservedReadPipe>( 2758 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2759 #endif 2760 2761 #if 0 2762 template <> 2763 bool idUsage::isValid<OpReservedWritePipe>( 2764 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2765 #endif 2766 2767 #if 0 2768 template <> 2769 bool idUsage::isValid<OpReserveReadPipePackets>( 2770 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2771 #endif 2772 2773 #if 0 2774 template <> 2775 bool idUsage::isValid<OpReserveWritePipePackets>( 2776 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2777 #endif 2778 2779 #if 0 2780 template <> 2781 bool idUsage::isValid<OpCommitReadPipe>( 2782 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2783 #endif 2784 2785 #if 0 2786 template <> 2787 bool idUsage::isValid<OpCommitWritePipe>( 2788 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2789 #endif 2790 2791 #if 0 2792 template <> 2793 bool idUsage::isValid<OpIsValidReserveId>( 2794 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2795 #endif 2796 2797 #if 0 2798 template <> 2799 bool idUsage::isValid<OpGetNumPipePackets>( 2800 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2801 #endif 2802 2803 #if 0 2804 template <> 2805 bool idUsage::isValid<OpGetMaxPipePackets>( 2806 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2807 #endif 2808 2809 #if 0 2810 template <> 2811 bool idUsage::isValid<OpGroupReserveReadPipePackets>( 2812 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2813 #endif 2814 2815 #if 0 2816 template <> 2817 bool idUsage::isValid<OpGroupReserveWritePipePackets>( 2818 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2819 #endif 2820 2821 #if 0 2822 template <> 2823 bool idUsage::isValid<OpGroupCommitReadPipe>( 2824 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2825 #endif 2826 2827 #if 0 2828 template <> 2829 bool idUsage::isValid<OpGroupCommitWritePipe>( 2830 const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {} 2831 #endif 2832 2833 #undef DIAG 2834 2835 bool idUsage::isValid(const spv_instruction_t* inst) { 2836 spv_opcode_desc opcodeEntry = nullptr; 2837 if (spvOpcodeTableValueLookup(opcodeTable, inst->opcode, &opcodeEntry)) 2838 return false; 2839 #define CASE(OpCode) \ 2840 case Spv##OpCode: \ 2841 return isValid<Spv##OpCode>(inst, opcodeEntry); 2842 #define TODO(OpCode) \ 2843 case Spv##OpCode: \ 2844 return true; 2845 switch (inst->opcode) { 2846 TODO(OpUndef) 2847 CASE(OpMemberName) 2848 CASE(OpLine) 2849 CASE(OpDecorate) 2850 CASE(OpMemberDecorate) 2851 CASE(OpGroupDecorate) 2852 CASE(OpGroupMemberDecorate) 2853 TODO(OpExtInst) 2854 CASE(OpEntryPoint) 2855 CASE(OpExecutionMode) 2856 CASE(OpTypeVector) 2857 CASE(OpTypeMatrix) 2858 CASE(OpTypeSampler) 2859 CASE(OpTypeArray) 2860 CASE(OpTypeRuntimeArray) 2861 CASE(OpTypeStruct) 2862 CASE(OpTypePointer) 2863 CASE(OpTypeFunction) 2864 CASE(OpTypePipe) 2865 CASE(OpConstantTrue) 2866 CASE(OpConstantFalse) 2867 CASE(OpConstantComposite) 2868 CASE(OpConstantSampler) 2869 CASE(OpConstantNull) 2870 CASE(OpSpecConstantTrue) 2871 CASE(OpSpecConstantFalse) 2872 CASE(OpSpecConstantComposite) 2873 CASE(OpSampledImage) 2874 TODO(OpSpecConstantOp) 2875 CASE(OpVariable) 2876 CASE(OpLoad) 2877 CASE(OpStore) 2878 CASE(OpCopyMemory) 2879 CASE(OpCopyMemorySized) 2880 CASE(OpAccessChain) 2881 CASE(OpInBoundsAccessChain) 2882 CASE(OpPtrAccessChain) 2883 CASE(OpInBoundsPtrAccessChain) 2884 TODO(OpArrayLength) 2885 TODO(OpGenericPtrMemSemantics) 2886 CASE(OpFunction) 2887 CASE(OpFunctionParameter) 2888 CASE(OpFunctionCall) 2889 TODO(OpConvertUToF) 2890 TODO(OpConvertFToS) 2891 TODO(OpConvertSToF) 2892 TODO(OpUConvert) 2893 TODO(OpSConvert) 2894 TODO(OpFConvert) 2895 TODO(OpConvertPtrToU) 2896 TODO(OpConvertUToPtr) 2897 TODO(OpPtrCastToGeneric) 2898 TODO(OpGenericCastToPtr) 2899 TODO(OpBitcast) 2900 TODO(OpGenericCastToPtrExplicit) 2901 TODO(OpSatConvertSToU) 2902 TODO(OpSatConvertUToS) 2903 TODO(OpVectorExtractDynamic) 2904 TODO(OpVectorInsertDynamic) 2905 TODO(OpVectorShuffle) 2906 TODO(OpCompositeConstruct) 2907 CASE(OpCompositeExtract) 2908 CASE(OpCompositeInsert) 2909 TODO(OpCopyObject) 2910 TODO(OpTranspose) 2911 TODO(OpSNegate) 2912 TODO(OpFNegate) 2913 TODO(OpNot) 2914 TODO(OpIAdd) 2915 TODO(OpFAdd) 2916 TODO(OpISub) 2917 TODO(OpFSub) 2918 TODO(OpIMul) 2919 TODO(OpFMul) 2920 TODO(OpUDiv) 2921 TODO(OpSDiv) 2922 TODO(OpFDiv) 2923 TODO(OpUMod) 2924 TODO(OpSRem) 2925 TODO(OpSMod) 2926 TODO(OpFRem) 2927 TODO(OpFMod) 2928 TODO(OpVectorTimesScalar) 2929 TODO(OpMatrixTimesScalar) 2930 TODO(OpVectorTimesMatrix) 2931 TODO(OpMatrixTimesVector) 2932 TODO(OpMatrixTimesMatrix) 2933 TODO(OpOuterProduct) 2934 TODO(OpDot) 2935 TODO(OpShiftRightLogical) 2936 TODO(OpShiftRightArithmetic) 2937 TODO(OpShiftLeftLogical) 2938 TODO(OpBitwiseOr) 2939 TODO(OpBitwiseXor) 2940 TODO(OpBitwiseAnd) 2941 TODO(OpAny) 2942 TODO(OpAll) 2943 TODO(OpIsNan) 2944 TODO(OpIsInf) 2945 TODO(OpIsFinite) 2946 TODO(OpIsNormal) 2947 TODO(OpSignBitSet) 2948 TODO(OpLessOrGreater) 2949 TODO(OpOrdered) 2950 TODO(OpUnordered) 2951 TODO(OpLogicalOr) 2952 TODO(OpLogicalAnd) 2953 TODO(OpSelect) 2954 TODO(OpIEqual) 2955 TODO(OpFOrdEqual) 2956 TODO(OpFUnordEqual) 2957 TODO(OpINotEqual) 2958 TODO(OpFOrdNotEqual) 2959 TODO(OpFUnordNotEqual) 2960 TODO(OpULessThan) 2961 TODO(OpSLessThan) 2962 TODO(OpFOrdLessThan) 2963 TODO(OpFUnordLessThan) 2964 TODO(OpUGreaterThan) 2965 TODO(OpSGreaterThan) 2966 TODO(OpFOrdGreaterThan) 2967 TODO(OpFUnordGreaterThan) 2968 TODO(OpULessThanEqual) 2969 TODO(OpSLessThanEqual) 2970 TODO(OpFOrdLessThanEqual) 2971 TODO(OpFUnordLessThanEqual) 2972 TODO(OpUGreaterThanEqual) 2973 TODO(OpSGreaterThanEqual) 2974 TODO(OpFOrdGreaterThanEqual) 2975 TODO(OpFUnordGreaterThanEqual) 2976 TODO(OpDPdx) 2977 TODO(OpDPdy) 2978 TODO(OpFwidth) 2979 TODO(OpDPdxFine) 2980 TODO(OpDPdyFine) 2981 TODO(OpFwidthFine) 2982 TODO(OpDPdxCoarse) 2983 TODO(OpDPdyCoarse) 2984 TODO(OpFwidthCoarse) 2985 TODO(OpPhi) 2986 TODO(OpLoopMerge) 2987 TODO(OpSelectionMerge) 2988 TODO(OpBranch) 2989 TODO(OpBranchConditional) 2990 TODO(OpSwitch) 2991 CASE(OpReturnValue) 2992 TODO(OpLifetimeStart) 2993 TODO(OpLifetimeStop) 2994 TODO(OpAtomicLoad) 2995 TODO(OpAtomicStore) 2996 TODO(OpAtomicExchange) 2997 TODO(OpAtomicCompareExchange) 2998 TODO(OpAtomicCompareExchangeWeak) 2999 TODO(OpAtomicIIncrement) 3000 TODO(OpAtomicIDecrement) 3001 TODO(OpAtomicIAdd) 3002 TODO(OpAtomicISub) 3003 TODO(OpAtomicUMin) 3004 TODO(OpAtomicUMax) 3005 TODO(OpAtomicAnd) 3006 TODO(OpAtomicOr) 3007 TODO(OpAtomicSMin) 3008 TODO(OpAtomicSMax) 3009 TODO(OpEmitStreamVertex) 3010 TODO(OpEndStreamPrimitive) 3011 TODO(OpGroupAsyncCopy) 3012 TODO(OpGroupWaitEvents) 3013 TODO(OpGroupAll) 3014 TODO(OpGroupAny) 3015 TODO(OpGroupBroadcast) 3016 TODO(OpGroupIAdd) 3017 TODO(OpGroupFAdd) 3018 TODO(OpGroupFMin) 3019 TODO(OpGroupUMin) 3020 TODO(OpGroupSMin) 3021 TODO(OpGroupFMax) 3022 TODO(OpGroupUMax) 3023 TODO(OpGroupSMax) 3024 TODO(OpEnqueueMarker) 3025 TODO(OpEnqueueKernel) 3026 TODO(OpGetKernelNDrangeSubGroupCount) 3027 TODO(OpGetKernelNDrangeMaxSubGroupSize) 3028 TODO(OpGetKernelWorkGroupSize) 3029 TODO(OpGetKernelPreferredWorkGroupSizeMultiple) 3030 TODO(OpRetainEvent) 3031 TODO(OpReleaseEvent) 3032 TODO(OpCreateUserEvent) 3033 TODO(OpIsValidEvent) 3034 TODO(OpSetUserEventStatus) 3035 TODO(OpCaptureEventProfilingInfo) 3036 TODO(OpGetDefaultQueue) 3037 TODO(OpBuildNDRange) 3038 TODO(OpReadPipe) 3039 TODO(OpWritePipe) 3040 TODO(OpReservedReadPipe) 3041 TODO(OpReservedWritePipe) 3042 TODO(OpReserveReadPipePackets) 3043 TODO(OpReserveWritePipePackets) 3044 TODO(OpCommitReadPipe) 3045 TODO(OpCommitWritePipe) 3046 TODO(OpIsValidReserveId) 3047 TODO(OpGetNumPipePackets) 3048 TODO(OpGetMaxPipePackets) 3049 TODO(OpGroupReserveReadPipePackets) 3050 TODO(OpGroupReserveWritePipePackets) 3051 TODO(OpGroupCommitReadPipe) 3052 TODO(OpGroupCommitWritePipe) 3053 default: 3054 return true; 3055 } 3056 #undef TODO 3057 #undef CASE 3058 } 3059 // This function takes the opcode of an instruction and returns 3060 // a function object that will return true if the index 3061 // of the operand can be forwarad declared. This function will 3062 // used in the SSA validation stage of the pipeline 3063 function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) { 3064 function<bool(unsigned index)> out; 3065 switch (opcode) { 3066 case SpvOpExecutionMode: 3067 case SpvOpEntryPoint: 3068 case SpvOpName: 3069 case SpvOpMemberName: 3070 case SpvOpSelectionMerge: 3071 case SpvOpDecorate: 3072 case SpvOpMemberDecorate: 3073 case SpvOpTypeStruct: 3074 case SpvOpBranch: 3075 case SpvOpLoopMerge: 3076 out = [](unsigned) { return true; }; 3077 break; 3078 case SpvOpGroupDecorate: 3079 case SpvOpGroupMemberDecorate: 3080 case SpvOpBranchConditional: 3081 case SpvOpSwitch: 3082 out = [](unsigned index) { return index != 0; }; 3083 break; 3084 3085 case SpvOpFunctionCall: 3086 // The Function parameter. 3087 out = [](unsigned index) { return index == 2; }; 3088 break; 3089 3090 case SpvOpPhi: 3091 out = [](unsigned index) { return index > 1; }; 3092 break; 3093 3094 case SpvOpEnqueueKernel: 3095 // The Invoke parameter. 3096 out = [](unsigned index) { return index == 8; }; 3097 break; 3098 3099 case SpvOpGetKernelNDrangeSubGroupCount: 3100 case SpvOpGetKernelNDrangeMaxSubGroupSize: 3101 // The Invoke parameter. 3102 out = [](unsigned index) { return index == 3; }; 3103 break; 3104 3105 case SpvOpGetKernelWorkGroupSize: 3106 case SpvOpGetKernelPreferredWorkGroupSizeMultiple: 3107 // The Invoke parameter. 3108 out = [](unsigned index) { return index == 2; }; 3109 break; 3110 case SpvOpTypeForwardPointer: 3111 out = [](unsigned index) { return index == 0; }; 3112 break; 3113 default: 3114 out = [](unsigned) { return false; }; 3115 break; 3116 } 3117 return out; 3118 } 3119 } // anonymous namespace 3120 3121 namespace libspirv { 3122 3123 spv_result_t UpdateIdUse(ValidationState_t& _) { 3124 for (const auto& inst : _.ordered_instructions()) { 3125 for (auto& operand : inst.operands()) { 3126 const spv_operand_type_t& type = operand.type; 3127 const uint32_t operand_id = inst.word(operand.offset); 3128 if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) { 3129 if (auto def = _.FindDef(operand_id)) 3130 def->RegisterUse(&inst, operand.offset); 3131 } 3132 } 3133 } 3134 return SPV_SUCCESS; 3135 } 3136 3137 /// This function checks all ID definitions dominate their use in the CFG. 3138 /// 3139 /// This function will iterate over all ID definitions that are defined in the 3140 /// functions of a module and make sure that the definitions appear in a 3141 /// block that dominates their use. 3142 /// 3143 /// NOTE: This function does NOT check module scoped functions which are 3144 /// checked during the initial binary parse in the IdPass below 3145 spv_result_t CheckIdDefinitionDominateUse(const ValidationState_t& _) { 3146 unordered_set<const Instruction*> phi_instructions; 3147 for (const auto& definition : _.all_definitions()) { 3148 // Check only those definitions defined in a function 3149 if (const Function* func = definition.second->function()) { 3150 if (const BasicBlock* block = definition.second->block()) { 3151 if (!block->reachable()) continue; 3152 // If the Id is defined within a block then make sure all references to 3153 // that Id appear in a blocks that are dominated by the defining block 3154 for (auto& use_index_pair : definition.second->uses()) { 3155 const Instruction* use = use_index_pair.first; 3156 if (const BasicBlock* use_block = use->block()) { 3157 if (use_block->reachable() == false) continue; 3158 if (use->opcode() == SpvOpPhi) { 3159 phi_instructions.insert(use); 3160 } else if (!block->dominates(*use->block())) { 3161 return _.diag(SPV_ERROR_INVALID_ID) 3162 << "ID " << _.getIdName(definition.first) 3163 << " defined in block " << _.getIdName(block->id()) 3164 << " does not dominate its use in block " 3165 << _.getIdName(use_block->id()); 3166 } 3167 } 3168 } 3169 } else { 3170 // If the Ids defined within a function but not in a block(i.e. function 3171 // parameters, block ids), then make sure all references to that Id 3172 // appear within the same function 3173 for (auto use : definition.second->uses()) { 3174 const Instruction* inst = use.first; 3175 if (inst->function() && inst->function() != func) { 3176 return _.diag(SPV_ERROR_INVALID_ID) 3177 << "ID " << _.getIdName(definition.first) 3178 << " used in function " 3179 << _.getIdName(inst->function()->id()) 3180 << " is used outside of it's defining function " 3181 << _.getIdName(func->id()); 3182 } 3183 } 3184 } 3185 } 3186 // NOTE: Ids defined outside of functions must appear before they are used 3187 // This check is being performed in the IdPass function 3188 } 3189 3190 // Check all OpPhi parent blocks are dominated by the variable's defining 3191 // blocks 3192 for (const Instruction* phi : phi_instructions) { 3193 if (phi->block()->reachable() == false) continue; 3194 for (size_t i = 3; i < phi->operands().size(); i += 2) { 3195 const Instruction* variable = _.FindDef(phi->word(i)); 3196 const BasicBlock* parent = 3197 phi->function()->GetBlock(phi->word(i + 1)).first; 3198 if (variable->block() && !variable->block()->dominates(*parent)) { 3199 return _.diag(SPV_ERROR_INVALID_ID) 3200 << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID " 3201 << _.getIdName(variable->id()) 3202 << " definition does not dominate its parent " 3203 << _.getIdName(parent->id()); 3204 } 3205 } 3206 } 3207 3208 return SPV_SUCCESS; 3209 } 3210 3211 // Performs SSA validation on the IDs of an instruction. The 3212 // can_have_forward_declared_ids functor should return true if the 3213 // instruction operand's ID can be forward referenced. 3214 spv_result_t IdPass(ValidationState_t& _, 3215 const spv_parsed_instruction_t* inst) { 3216 auto can_have_forward_declared_ids = 3217 getCanBeForwardDeclaredFunction(static_cast<SpvOp>(inst->opcode)); 3218 3219 // Keep track of a result id defined by this instruction. 0 means it 3220 // does not define an id. 3221 uint32_t result_id = 0; 3222 3223 for (unsigned i = 0; i < inst->num_operands; i++) { 3224 const spv_parsed_operand_t& operand = inst->operands[i]; 3225 const spv_operand_type_t& type = operand.type; 3226 // We only care about Id operands, which are a single word. 3227 const uint32_t operand_word = inst->words[operand.offset]; 3228 3229 auto ret = SPV_ERROR_INTERNAL; 3230 switch (type) { 3231 case SPV_OPERAND_TYPE_RESULT_ID: 3232 // NOTE: Multiple Id definitions are being checked by the binary parser. 3233 // 3234 // Defer undefined-forward-reference removal until after we've analyzed 3235 // the remaining operands to this instruction. Deferral only matters 3236 // for 3237 // OpPhi since it's the only case where it defines its own forward 3238 // reference. Other instructions that can have forward references 3239 // either don't define a value or the forward reference is to a function 3240 // Id (and hence defined outside of a function body). 3241 result_id = operand_word; 3242 // NOTE: The result Id is added (in RegisterInstruction) *after* all of 3243 // the other Ids have been checked to avoid premature use in the same 3244 // instruction. 3245 ret = SPV_SUCCESS; 3246 break; 3247 case SPV_OPERAND_TYPE_ID: 3248 case SPV_OPERAND_TYPE_TYPE_ID: 3249 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: 3250 case SPV_OPERAND_TYPE_SCOPE_ID: 3251 if (_.IsDefinedId(operand_word)) { 3252 ret = SPV_SUCCESS; 3253 } else if (can_have_forward_declared_ids(i)) { 3254 ret = _.ForwardDeclareId(operand_word); 3255 } else { 3256 ret = _.diag(SPV_ERROR_INVALID_ID) << "ID " 3257 << _.getIdName(operand_word) 3258 << " has not been defined"; 3259 } 3260 break; 3261 default: 3262 ret = SPV_SUCCESS; 3263 break; 3264 } 3265 if (SPV_SUCCESS != ret) { 3266 return ret; 3267 } 3268 } 3269 if (result_id) { 3270 _.RemoveIfForwardDeclared(result_id); 3271 } 3272 _.RegisterInstruction(*inst); 3273 return SPV_SUCCESS; 3274 } 3275 } // namespace libspirv 3276 3277 spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts, 3278 const uint64_t instCount, 3279 const spv_opcode_table opcodeTable, 3280 const spv_operand_table operandTable, 3281 const spv_ext_inst_table extInstTable, 3282 const libspirv::ValidationState_t& state, 3283 spv_position position) { 3284 idUsage idUsage(opcodeTable, operandTable, extInstTable, pInsts, instCount, 3285 state.memory_model(), state.addressing_model(), state, 3286 state.entry_points(), position, state.context()->consumer); 3287 for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) { 3288 if (!idUsage.isValid(&pInsts[instIndex])) return SPV_ERROR_INVALID_ID; 3289 position->index += pInsts[instIndex].words.size(); 3290 } 3291 return SPV_SUCCESS; 3292 } 3293