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 "source/opcode.h" 16 17 #include <assert.h> 18 #include <string.h> 19 20 #include <algorithm> 21 #include <cstdlib> 22 23 #include "source/instruction.h" 24 #include "source/macro.h" 25 #include "source/spirv_constant.h" 26 #include "source/spirv_endian.h" 27 #include "source/spirv_target_env.h" 28 #include "spirv-tools/libspirv.h" 29 30 namespace { 31 struct OpcodeDescPtrLen { 32 const spv_opcode_desc_t* ptr; 33 uint32_t len; 34 }; 35 36 #include "core.insts-unified1.inc" 37 38 static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries), 39 kOpcodeTableEntries}; 40 41 // Represents a vendor tool entry in the SPIR-V XML Regsitry. 42 struct VendorTool { 43 uint32_t value; 44 const char* vendor; 45 const char* tool; // Might be empty string. 46 const char* vendor_tool; // Combiantion of vendor and tool. 47 }; 48 49 const VendorTool vendor_tools[] = { 50 #include "generators.inc" 51 }; 52 53 } // anonymous namespace 54 55 // TODO(dneto): Move this to another file. It doesn't belong with opcode 56 // processing. 57 const char* spvGeneratorStr(uint32_t generator) { 58 auto where = std::find_if( 59 std::begin(vendor_tools), std::end(vendor_tools), 60 [generator](const VendorTool& vt) { return generator == vt.value; }); 61 if (where != std::end(vendor_tools)) return where->vendor_tool; 62 return "Unknown"; 63 } 64 65 uint32_t spvOpcodeMake(uint16_t wordCount, SpvOp opcode) { 66 return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16); 67 } 68 69 void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount, 70 uint16_t* pOpcode) { 71 if (pWordCount) { 72 *pWordCount = (uint16_t)((0xffff0000 & word) >> 16); 73 } 74 if (pOpcode) { 75 *pOpcode = 0x0000ffff & word; 76 } 77 } 78 79 spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) { 80 if (!pInstTable) return SPV_ERROR_INVALID_POINTER; 81 82 // Descriptions of each opcode. Each entry describes the format of the 83 // instruction that follows a particular opcode. 84 85 *pInstTable = &kOpcodeTable; 86 return SPV_SUCCESS; 87 } 88 89 spv_result_t spvOpcodeTableNameLookup(spv_target_env env, 90 const spv_opcode_table table, 91 const char* name, 92 spv_opcode_desc* pEntry) { 93 if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; 94 if (!table) return SPV_ERROR_INVALID_TABLE; 95 96 // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be 97 // preferable but the table requires sorting on the Opcode name, but it's 98 // static const initialized and matches the order of the spec. 99 const size_t nameLength = strlen(name); 100 for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) { 101 const spv_opcode_desc_t& entry = table->entries[opcodeIndex]; 102 // We considers the current opcode as available as long as 103 // 1. The target environment satisfies the minimal requirement of the 104 // opcode; or 105 // 2. There is at least one extension enabling this opcode. 106 // 107 // Note that the second rule assumes the extension enabling this instruction 108 // is indeed requested in the SPIR-V code; checking that should be 109 // validator's work. 110 if ((spvVersionForTargetEnv(env) >= entry.minVersion || 111 entry.numExtensions > 0u || entry.numCapabilities > 0u) && 112 nameLength == strlen(entry.name) && 113 !strncmp(name, entry.name, nameLength)) { 114 // NOTE: Found out Opcode! 115 *pEntry = &entry; 116 return SPV_SUCCESS; 117 } 118 } 119 120 return SPV_ERROR_INVALID_LOOKUP; 121 } 122 123 spv_result_t spvOpcodeTableValueLookup(spv_target_env env, 124 const spv_opcode_table table, 125 const SpvOp opcode, 126 spv_opcode_desc* pEntry) { 127 if (!table) return SPV_ERROR_INVALID_TABLE; 128 if (!pEntry) return SPV_ERROR_INVALID_POINTER; 129 130 const auto beg = table->entries; 131 const auto end = table->entries + table->count; 132 133 spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, 134 false, false, 0, nullptr, ~0u}; 135 136 auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { 137 return lhs.opcode < rhs.opcode; 138 }; 139 140 // We need to loop here because there can exist multiple symbols for the same 141 // opcode value, and they can be introduced in different target environments, 142 // which means they can have different minimal version requirements. 143 // Assumes the underlying table is already sorted ascendingly according to 144 // opcode value. 145 for (auto it = std::lower_bound(beg, end, needle, comp); 146 it != end && it->opcode == opcode; ++it) { 147 // We considers the current opcode as available as long as 148 // 1. The target environment satisfies the minimal requirement of the 149 // opcode; or 150 // 2. There is at least one extension enabling this opcode. 151 // 152 // Note that the second rule assumes the extension enabling this instruction 153 // is indeed requested in the SPIR-V code; checking that should be 154 // validator's work. 155 if (spvVersionForTargetEnv(env) >= it->minVersion || 156 it->numExtensions > 0u || it->numCapabilities > 0u) { 157 *pEntry = it; 158 return SPV_SUCCESS; 159 } 160 } 161 162 return SPV_ERROR_INVALID_LOOKUP; 163 } 164 165 void spvInstructionCopy(const uint32_t* words, const SpvOp opcode, 166 const uint16_t wordCount, const spv_endianness_t endian, 167 spv_instruction_t* pInst) { 168 pInst->opcode = opcode; 169 pInst->words.resize(wordCount); 170 for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) { 171 pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian); 172 if (!wordIndex) { 173 uint16_t thisWordCount; 174 uint16_t thisOpcode; 175 spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode); 176 assert(opcode == static_cast<SpvOp>(thisOpcode) && 177 wordCount == thisWordCount && "Endianness failed!"); 178 } 179 } 180 } 181 182 const char* spvOpcodeString(const SpvOp opcode) { 183 const auto beg = kOpcodeTableEntries; 184 const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries); 185 spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, 186 false, false, 0, nullptr, ~0u}; 187 auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { 188 return lhs.opcode < rhs.opcode; 189 }; 190 auto it = std::lower_bound(beg, end, needle, comp); 191 if (it != end && it->opcode == opcode) { 192 return it->name; 193 } 194 195 assert(0 && "Unreachable!"); 196 return "unknown"; 197 } 198 199 int32_t spvOpcodeIsScalarType(const SpvOp opcode) { 200 switch (opcode) { 201 case SpvOpTypeInt: 202 case SpvOpTypeFloat: 203 case SpvOpTypeBool: 204 return true; 205 default: 206 return false; 207 } 208 } 209 210 int32_t spvOpcodeIsSpecConstant(const SpvOp opcode) { 211 switch (opcode) { 212 case SpvOpSpecConstantTrue: 213 case SpvOpSpecConstantFalse: 214 case SpvOpSpecConstant: 215 case SpvOpSpecConstantComposite: 216 case SpvOpSpecConstantOp: 217 return true; 218 default: 219 return false; 220 } 221 } 222 223 int32_t spvOpcodeIsConstant(const SpvOp opcode) { 224 switch (opcode) { 225 case SpvOpConstantTrue: 226 case SpvOpConstantFalse: 227 case SpvOpConstant: 228 case SpvOpConstantComposite: 229 case SpvOpConstantSampler: 230 case SpvOpConstantNull: 231 case SpvOpSpecConstantTrue: 232 case SpvOpSpecConstantFalse: 233 case SpvOpSpecConstant: 234 case SpvOpSpecConstantComposite: 235 case SpvOpSpecConstantOp: 236 return true; 237 default: 238 return false; 239 } 240 } 241 242 bool spvOpcodeIsConstantOrUndef(const SpvOp opcode) { 243 return opcode == SpvOpUndef || spvOpcodeIsConstant(opcode); 244 } 245 246 bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode) { 247 switch (opcode) { 248 case SpvOpSpecConstantTrue: 249 case SpvOpSpecConstantFalse: 250 case SpvOpSpecConstant: 251 return true; 252 default: 253 return false; 254 } 255 } 256 257 int32_t spvOpcodeIsComposite(const SpvOp opcode) { 258 switch (opcode) { 259 case SpvOpTypeVector: 260 case SpvOpTypeMatrix: 261 case SpvOpTypeArray: 262 case SpvOpTypeStruct: 263 return true; 264 default: 265 return false; 266 } 267 } 268 269 bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode) { 270 switch (opcode) { 271 case SpvOpVariable: 272 case SpvOpAccessChain: 273 case SpvOpInBoundsAccessChain: 274 case SpvOpFunctionParameter: 275 case SpvOpImageTexelPointer: 276 case SpvOpCopyObject: 277 case SpvOpSelect: 278 case SpvOpPhi: 279 case SpvOpFunctionCall: 280 case SpvOpPtrAccessChain: 281 case SpvOpLoad: 282 case SpvOpConstantNull: 283 return true; 284 default: 285 return false; 286 } 287 } 288 289 int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode) { 290 switch (opcode) { 291 case SpvOpVariable: 292 case SpvOpAccessChain: 293 case SpvOpInBoundsAccessChain: 294 case SpvOpFunctionParameter: 295 case SpvOpImageTexelPointer: 296 case SpvOpCopyObject: 297 return true; 298 default: 299 return false; 300 } 301 } 302 303 int32_t spvOpcodeGeneratesType(SpvOp op) { 304 switch (op) { 305 case SpvOpTypeVoid: 306 case SpvOpTypeBool: 307 case SpvOpTypeInt: 308 case SpvOpTypeFloat: 309 case SpvOpTypeVector: 310 case SpvOpTypeMatrix: 311 case SpvOpTypeImage: 312 case SpvOpTypeSampler: 313 case SpvOpTypeSampledImage: 314 case SpvOpTypeArray: 315 case SpvOpTypeRuntimeArray: 316 case SpvOpTypeStruct: 317 case SpvOpTypeOpaque: 318 case SpvOpTypePointer: 319 case SpvOpTypeFunction: 320 case SpvOpTypeEvent: 321 case SpvOpTypeDeviceEvent: 322 case SpvOpTypeReserveId: 323 case SpvOpTypeQueue: 324 case SpvOpTypePipe: 325 case SpvOpTypePipeStorage: 326 case SpvOpTypeNamedBarrier: 327 case SpvOpTypeAccelerationStructureNV: 328 return true; 329 default: 330 // In particular, OpTypeForwardPointer does not generate a type, 331 // but declares a storage class for a pointer type generated 332 // by a different instruction. 333 break; 334 } 335 return 0; 336 } 337 338 bool spvOpcodeIsDecoration(const SpvOp opcode) { 339 switch (opcode) { 340 case SpvOpDecorate: 341 case SpvOpDecorateId: 342 case SpvOpMemberDecorate: 343 case SpvOpGroupDecorate: 344 case SpvOpGroupMemberDecorate: 345 case SpvOpDecorateStringGOOGLE: 346 case SpvOpMemberDecorateStringGOOGLE: 347 return true; 348 default: 349 break; 350 } 351 return false; 352 } 353 354 bool spvOpcodeIsLoad(const SpvOp opcode) { 355 switch (opcode) { 356 case SpvOpLoad: 357 case SpvOpImageSampleExplicitLod: 358 case SpvOpImageSampleImplicitLod: 359 case SpvOpImageSampleDrefImplicitLod: 360 case SpvOpImageSampleDrefExplicitLod: 361 case SpvOpImageSampleProjImplicitLod: 362 case SpvOpImageSampleProjExplicitLod: 363 case SpvOpImageSampleProjDrefImplicitLod: 364 case SpvOpImageSampleProjDrefExplicitLod: 365 case SpvOpImageFetch: 366 case SpvOpImageGather: 367 case SpvOpImageDrefGather: 368 case SpvOpImageRead: 369 case SpvOpImageSparseSampleImplicitLod: 370 case SpvOpImageSparseSampleExplicitLod: 371 case SpvOpImageSparseSampleDrefExplicitLod: 372 case SpvOpImageSparseSampleDrefImplicitLod: 373 case SpvOpImageSparseFetch: 374 case SpvOpImageSparseGather: 375 case SpvOpImageSparseDrefGather: 376 case SpvOpImageSparseRead: 377 return true; 378 default: 379 return false; 380 } 381 } 382 383 bool spvOpcodeIsBranch(SpvOp opcode) { 384 switch (opcode) { 385 case SpvOpBranch: 386 case SpvOpBranchConditional: 387 case SpvOpSwitch: 388 return true; 389 default: 390 return false; 391 } 392 } 393 394 bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) { 395 switch (opcode) { 396 case SpvOpAtomicLoad: 397 case SpvOpAtomicExchange: 398 case SpvOpAtomicCompareExchange: 399 case SpvOpAtomicCompareExchangeWeak: 400 case SpvOpAtomicIIncrement: 401 case SpvOpAtomicIDecrement: 402 case SpvOpAtomicIAdd: 403 case SpvOpAtomicISub: 404 case SpvOpAtomicSMin: 405 case SpvOpAtomicUMin: 406 case SpvOpAtomicSMax: 407 case SpvOpAtomicUMax: 408 case SpvOpAtomicAnd: 409 case SpvOpAtomicOr: 410 case SpvOpAtomicXor: 411 case SpvOpAtomicFlagTestAndSet: 412 return true; 413 default: 414 return false; 415 } 416 } 417 418 bool spvOpcodeIsAtomicOp(const SpvOp opcode) { 419 return (spvOpcodeIsAtomicWithLoad(opcode) || opcode == SpvOpAtomicStore || 420 opcode == SpvOpAtomicFlagClear); 421 } 422 423 bool spvOpcodeIsReturn(SpvOp opcode) { 424 switch (opcode) { 425 case SpvOpReturn: 426 case SpvOpReturnValue: 427 return true; 428 default: 429 return false; 430 } 431 } 432 433 bool spvOpcodeIsReturnOrAbort(SpvOp opcode) { 434 return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill || 435 opcode == SpvOpUnreachable; 436 } 437 438 bool spvOpcodeIsBlockTerminator(SpvOp opcode) { 439 return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode); 440 } 441 442 bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) { 443 switch (opcode) { 444 case SpvOpTypeImage: 445 case SpvOpTypeSampler: 446 case SpvOpTypeSampledImage: 447 case SpvOpTypeOpaque: 448 case SpvOpTypeEvent: 449 case SpvOpTypeDeviceEvent: 450 case SpvOpTypeReserveId: 451 case SpvOpTypeQueue: 452 case SpvOpTypePipe: 453 case SpvOpTypeForwardPointer: 454 case SpvOpTypePipeStorage: 455 case SpvOpTypeNamedBarrier: 456 return true; 457 default: 458 return false; 459 } 460 } 461 462 bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) { 463 switch (opcode) { 464 case SpvOpGroupNonUniformElect: 465 case SpvOpGroupNonUniformAll: 466 case SpvOpGroupNonUniformAny: 467 case SpvOpGroupNonUniformAllEqual: 468 case SpvOpGroupNonUniformBroadcast: 469 case SpvOpGroupNonUniformBroadcastFirst: 470 case SpvOpGroupNonUniformBallot: 471 case SpvOpGroupNonUniformInverseBallot: 472 case SpvOpGroupNonUniformBallotBitExtract: 473 case SpvOpGroupNonUniformBallotBitCount: 474 case SpvOpGroupNonUniformBallotFindLSB: 475 case SpvOpGroupNonUniformBallotFindMSB: 476 case SpvOpGroupNonUniformShuffle: 477 case SpvOpGroupNonUniformShuffleXor: 478 case SpvOpGroupNonUniformShuffleUp: 479 case SpvOpGroupNonUniformShuffleDown: 480 case SpvOpGroupNonUniformIAdd: 481 case SpvOpGroupNonUniformFAdd: 482 case SpvOpGroupNonUniformIMul: 483 case SpvOpGroupNonUniformFMul: 484 case SpvOpGroupNonUniformSMin: 485 case SpvOpGroupNonUniformUMin: 486 case SpvOpGroupNonUniformFMin: 487 case SpvOpGroupNonUniformSMax: 488 case SpvOpGroupNonUniformUMax: 489 case SpvOpGroupNonUniformFMax: 490 case SpvOpGroupNonUniformBitwiseAnd: 491 case SpvOpGroupNonUniformBitwiseOr: 492 case SpvOpGroupNonUniformBitwiseXor: 493 case SpvOpGroupNonUniformLogicalAnd: 494 case SpvOpGroupNonUniformLogicalOr: 495 case SpvOpGroupNonUniformLogicalXor: 496 case SpvOpGroupNonUniformQuadBroadcast: 497 case SpvOpGroupNonUniformQuadSwap: 498 return true; 499 default: 500 return false; 501 } 502 } 503 504 bool spvOpcodeIsScalarizable(SpvOp opcode) { 505 switch (opcode) { 506 case SpvOpPhi: 507 case SpvOpCopyObject: 508 case SpvOpConvertFToU: 509 case SpvOpConvertFToS: 510 case SpvOpConvertSToF: 511 case SpvOpConvertUToF: 512 case SpvOpUConvert: 513 case SpvOpSConvert: 514 case SpvOpFConvert: 515 case SpvOpQuantizeToF16: 516 case SpvOpVectorInsertDynamic: 517 case SpvOpSNegate: 518 case SpvOpFNegate: 519 case SpvOpIAdd: 520 case SpvOpFAdd: 521 case SpvOpISub: 522 case SpvOpFSub: 523 case SpvOpIMul: 524 case SpvOpFMul: 525 case SpvOpUDiv: 526 case SpvOpSDiv: 527 case SpvOpFDiv: 528 case SpvOpUMod: 529 case SpvOpSRem: 530 case SpvOpSMod: 531 case SpvOpFRem: 532 case SpvOpFMod: 533 case SpvOpVectorTimesScalar: 534 case SpvOpIAddCarry: 535 case SpvOpISubBorrow: 536 case SpvOpUMulExtended: 537 case SpvOpSMulExtended: 538 case SpvOpShiftRightLogical: 539 case SpvOpShiftRightArithmetic: 540 case SpvOpShiftLeftLogical: 541 case SpvOpBitwiseOr: 542 case SpvOpBitwiseAnd: 543 case SpvOpNot: 544 case SpvOpBitFieldInsert: 545 case SpvOpBitFieldSExtract: 546 case SpvOpBitFieldUExtract: 547 case SpvOpBitReverse: 548 case SpvOpBitCount: 549 case SpvOpIsNan: 550 case SpvOpIsInf: 551 case SpvOpIsFinite: 552 case SpvOpIsNormal: 553 case SpvOpSignBitSet: 554 case SpvOpLessOrGreater: 555 case SpvOpOrdered: 556 case SpvOpUnordered: 557 case SpvOpLogicalEqual: 558 case SpvOpLogicalNotEqual: 559 case SpvOpLogicalOr: 560 case SpvOpLogicalAnd: 561 case SpvOpLogicalNot: 562 case SpvOpSelect: 563 case SpvOpIEqual: 564 case SpvOpINotEqual: 565 case SpvOpUGreaterThan: 566 case SpvOpSGreaterThan: 567 case SpvOpUGreaterThanEqual: 568 case SpvOpSGreaterThanEqual: 569 case SpvOpULessThan: 570 case SpvOpSLessThan: 571 case SpvOpULessThanEqual: 572 case SpvOpSLessThanEqual: 573 case SpvOpFOrdEqual: 574 case SpvOpFUnordEqual: 575 case SpvOpFOrdNotEqual: 576 case SpvOpFUnordNotEqual: 577 case SpvOpFOrdLessThan: 578 case SpvOpFUnordLessThan: 579 case SpvOpFOrdGreaterThan: 580 case SpvOpFUnordGreaterThan: 581 case SpvOpFOrdLessThanEqual: 582 case SpvOpFUnordLessThanEqual: 583 case SpvOpFOrdGreaterThanEqual: 584 case SpvOpFUnordGreaterThanEqual: 585 return true; 586 default: 587 return false; 588 } 589 } 590 591 bool spvOpcodeIsDebug(SpvOp opcode) { 592 switch (opcode) { 593 case SpvOpName: 594 case SpvOpMemberName: 595 case SpvOpSource: 596 case SpvOpSourceContinued: 597 case SpvOpSourceExtension: 598 case SpvOpString: 599 case SpvOpLine: 600 case SpvOpNoLine: 601 return true; 602 default: 603 return false; 604 } 605 } 606