1 // 2 // Copyright (C) 2014-2015 LunarG, Inc. 3 // Copyright (C) 2015-2018 Google, Inc. 4 // 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions 9 // are met: 10 // 11 // Redistributions of source code must retain the above copyright 12 // notice, this list of conditions and the following disclaimer. 13 // 14 // Redistributions in binary form must reproduce the above 15 // copyright notice, this list of conditions and the following 16 // disclaimer in the documentation and/or other materials provided 17 // with the distribution. 18 // 19 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 20 // contributors may be used to endorse or promote products derived 21 // from this software without specific prior written permission. 22 // 23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 // POSSIBILITY OF SUCH DAMAGE. 35 36 // 37 // Helper for making SPIR-V IR. Generally, this is documented in the header 38 // SpvBuilder.h. 39 // 40 41 #include <cassert> 42 #include <cstdlib> 43 44 #include <unordered_set> 45 #include <algorithm> 46 47 #include "SpvBuilder.h" 48 49 #include "hex_float.h" 50 51 #ifndef _WIN32 52 #include <cstdio> 53 #endif 54 55 namespace spv { 56 57 Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : 58 spvVersion(spvVersion), 59 source(SourceLanguageUnknown), 60 sourceVersion(0), 61 sourceFileStringId(NoResult), 62 currentLine(0), 63 currentFile(nullptr), 64 emitOpLines(false), 65 addressModel(AddressingModelLogical), 66 memoryModel(MemoryModelGLSL450), 67 builderNumber(magicNumber), 68 buildPoint(0), 69 uniqueId(0), 70 entryPointFunction(0), 71 generatingOpCodeForSpecConst(false), 72 logger(buildLogger) 73 { 74 clearAccessChain(); 75 } 76 77 Builder::~Builder() 78 { 79 } 80 81 Id Builder::import(const char* name) 82 { 83 Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); 84 import->addStringOperand(name); 85 module.mapInstruction(import); 86 87 imports.push_back(std::unique_ptr<Instruction>(import)); 88 return import->getResultId(); 89 } 90 91 // Emit instruction for non-filename-based #line directives (ie. no filename 92 // seen yet): emit an OpLine if we've been asked to emit OpLines and the line 93 // number has changed since the last time, and is a valid line number. 94 void Builder::setLine(int lineNum) 95 { 96 if (lineNum != 0 && lineNum != currentLine) { 97 currentLine = lineNum; 98 if (emitOpLines) 99 addLine(sourceFileStringId, currentLine, 0); 100 } 101 } 102 103 // If no filename, do non-filename-based #line emit. Else do filename-based emit. 104 // Emit OpLine if we've been asked to emit OpLines and the line number or filename 105 // has changed since the last time, and line number is valid. 106 void Builder::setLine(int lineNum, const char* filename) 107 { 108 if (filename == nullptr) { 109 setLine(lineNum); 110 return; 111 } 112 if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr || 113 strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) { 114 currentLine = lineNum; 115 currentFile = filename; 116 if (emitOpLines) { 117 spv::Id strId = getStringId(filename); 118 addLine(strId, currentLine, 0); 119 } 120 } 121 } 122 123 void Builder::addLine(Id fileName, int lineNum, int column) 124 { 125 Instruction* line = new Instruction(OpLine); 126 line->addIdOperand(fileName); 127 line->addImmediateOperand(lineNum); 128 line->addImmediateOperand(column); 129 buildPoint->addInstruction(std::unique_ptr<Instruction>(line)); 130 } 131 132 // For creating new groupedTypes (will return old type if the requested one was already made). 133 Id Builder::makeVoidType() 134 { 135 Instruction* type; 136 if (groupedTypes[OpTypeVoid].size() == 0) { 137 type = new Instruction(getUniqueId(), NoType, OpTypeVoid); 138 groupedTypes[OpTypeVoid].push_back(type); 139 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 140 module.mapInstruction(type); 141 } else 142 type = groupedTypes[OpTypeVoid].back(); 143 144 return type->getResultId(); 145 } 146 147 Id Builder::makeBoolType() 148 { 149 Instruction* type; 150 if (groupedTypes[OpTypeBool].size() == 0) { 151 type = new Instruction(getUniqueId(), NoType, OpTypeBool); 152 groupedTypes[OpTypeBool].push_back(type); 153 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 154 module.mapInstruction(type); 155 } else 156 type = groupedTypes[OpTypeBool].back(); 157 158 return type->getResultId(); 159 } 160 161 Id Builder::makeSamplerType() 162 { 163 Instruction* type; 164 if (groupedTypes[OpTypeSampler].size() == 0) { 165 type = new Instruction(getUniqueId(), NoType, OpTypeSampler); 166 groupedTypes[OpTypeSampler].push_back(type); 167 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 168 module.mapInstruction(type); 169 } else 170 type = groupedTypes[OpTypeSampler].back(); 171 172 return type->getResultId(); 173 } 174 175 Id Builder::makePointer(StorageClass storageClass, Id pointee) 176 { 177 // try to find it 178 Instruction* type; 179 for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { 180 type = groupedTypes[OpTypePointer][t]; 181 if (type->getImmediateOperand(0) == (unsigned)storageClass && 182 type->getIdOperand(1) == pointee) 183 return type->getResultId(); 184 } 185 186 // not found, make it 187 type = new Instruction(getUniqueId(), NoType, OpTypePointer); 188 type->addImmediateOperand(storageClass); 189 type->addIdOperand(pointee); 190 groupedTypes[OpTypePointer].push_back(type); 191 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 192 module.mapInstruction(type); 193 194 return type->getResultId(); 195 } 196 197 Id Builder::makeForwardPointer(StorageClass storageClass) 198 { 199 // Caching/uniquifying doesn't work here, because we don't know the 200 // pointee type and there can be multiple forward pointers of the same 201 // storage type. Somebody higher up in the stack must keep track. 202 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer); 203 type->addImmediateOperand(storageClass); 204 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 205 module.mapInstruction(type); 206 207 return type->getResultId(); 208 } 209 210 Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) 211 { 212 // try to find it 213 Instruction* type; 214 for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { 215 type = groupedTypes[OpTypePointer][t]; 216 if (type->getImmediateOperand(0) == (unsigned)storageClass && 217 type->getIdOperand(1) == pointee) 218 return type->getResultId(); 219 } 220 221 type = new Instruction(forwardPointerType, NoType, OpTypePointer); 222 type->addImmediateOperand(storageClass); 223 type->addIdOperand(pointee); 224 groupedTypes[OpTypePointer].push_back(type); 225 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 226 module.mapInstruction(type); 227 228 return type->getResultId(); 229 } 230 231 Id Builder::makeIntegerType(int width, bool hasSign) 232 { 233 // try to find it 234 Instruction* type; 235 for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { 236 type = groupedTypes[OpTypeInt][t]; 237 if (type->getImmediateOperand(0) == (unsigned)width && 238 type->getImmediateOperand(1) == (hasSign ? 1u : 0u)) 239 return type->getResultId(); 240 } 241 242 // not found, make it 243 type = new Instruction(getUniqueId(), NoType, OpTypeInt); 244 type->addImmediateOperand(width); 245 type->addImmediateOperand(hasSign ? 1 : 0); 246 groupedTypes[OpTypeInt].push_back(type); 247 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 248 module.mapInstruction(type); 249 250 // deal with capabilities 251 switch (width) { 252 case 8: 253 case 16: 254 // these are currently handled by storage-type declarations and post processing 255 break; 256 case 64: 257 addCapability(CapabilityInt64); 258 break; 259 default: 260 break; 261 } 262 263 return type->getResultId(); 264 } 265 266 Id Builder::makeFloatType(int width) 267 { 268 // try to find it 269 Instruction* type; 270 for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { 271 type = groupedTypes[OpTypeFloat][t]; 272 if (type->getImmediateOperand(0) == (unsigned)width) 273 return type->getResultId(); 274 } 275 276 // not found, make it 277 type = new Instruction(getUniqueId(), NoType, OpTypeFloat); 278 type->addImmediateOperand(width); 279 groupedTypes[OpTypeFloat].push_back(type); 280 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 281 module.mapInstruction(type); 282 283 // deal with capabilities 284 switch (width) { 285 case 16: 286 // currently handled by storage-type declarations and post processing 287 break; 288 case 64: 289 addCapability(CapabilityFloat64); 290 break; 291 default: 292 break; 293 } 294 295 return type->getResultId(); 296 } 297 298 // Make a struct without checking for duplication. 299 // See makeStructResultType() for non-decorated structs 300 // needed as the result of some instructions, which does 301 // check for duplicates. 302 Id Builder::makeStructType(const std::vector<Id>& members, const char* name) 303 { 304 // Don't look for previous one, because in the general case, 305 // structs can be duplicated except for decorations. 306 307 // not found, make it 308 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct); 309 for (int op = 0; op < (int)members.size(); ++op) 310 type->addIdOperand(members[op]); 311 groupedTypes[OpTypeStruct].push_back(type); 312 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 313 module.mapInstruction(type); 314 addName(type->getResultId(), name); 315 316 return type->getResultId(); 317 } 318 319 // Make a struct for the simple results of several instructions, 320 // checking for duplication. 321 Id Builder::makeStructResultType(Id type0, Id type1) 322 { 323 // try to find it 324 Instruction* type; 325 for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) { 326 type = groupedTypes[OpTypeStruct][t]; 327 if (type->getNumOperands() != 2) 328 continue; 329 if (type->getIdOperand(0) != type0 || 330 type->getIdOperand(1) != type1) 331 continue; 332 return type->getResultId(); 333 } 334 335 // not found, make it 336 std::vector<spv::Id> members; 337 members.push_back(type0); 338 members.push_back(type1); 339 340 return makeStructType(members, "ResType"); 341 } 342 343 Id Builder::makeVectorType(Id component, int size) 344 { 345 // try to find it 346 Instruction* type; 347 for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) { 348 type = groupedTypes[OpTypeVector][t]; 349 if (type->getIdOperand(0) == component && 350 type->getImmediateOperand(1) == (unsigned)size) 351 return type->getResultId(); 352 } 353 354 // not found, make it 355 type = new Instruction(getUniqueId(), NoType, OpTypeVector); 356 type->addIdOperand(component); 357 type->addImmediateOperand(size); 358 groupedTypes[OpTypeVector].push_back(type); 359 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 360 module.mapInstruction(type); 361 362 return type->getResultId(); 363 } 364 365 Id Builder::makeMatrixType(Id component, int cols, int rows) 366 { 367 assert(cols <= maxMatrixSize && rows <= maxMatrixSize); 368 369 Id column = makeVectorType(component, rows); 370 371 // try to find it 372 Instruction* type; 373 for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) { 374 type = groupedTypes[OpTypeMatrix][t]; 375 if (type->getIdOperand(0) == column && 376 type->getImmediateOperand(1) == (unsigned)cols) 377 return type->getResultId(); 378 } 379 380 // not found, make it 381 type = new Instruction(getUniqueId(), NoType, OpTypeMatrix); 382 type->addIdOperand(column); 383 type->addImmediateOperand(cols); 384 groupedTypes[OpTypeMatrix].push_back(type); 385 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 386 module.mapInstruction(type); 387 388 return type->getResultId(); 389 } 390 391 // TODO: performance: track arrays per stride 392 // If a stride is supplied (non-zero) make an array. 393 // If no stride (0), reuse previous array types. 394 // 'size' is an Id of a constant or specialization constant of the array size 395 Id Builder::makeArrayType(Id element, Id sizeId, int stride) 396 { 397 Instruction* type; 398 if (stride == 0) { 399 // try to find existing type 400 for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) { 401 type = groupedTypes[OpTypeArray][t]; 402 if (type->getIdOperand(0) == element && 403 type->getIdOperand(1) == sizeId) 404 return type->getResultId(); 405 } 406 } 407 408 // not found, make it 409 type = new Instruction(getUniqueId(), NoType, OpTypeArray); 410 type->addIdOperand(element); 411 type->addIdOperand(sizeId); 412 groupedTypes[OpTypeArray].push_back(type); 413 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 414 module.mapInstruction(type); 415 416 return type->getResultId(); 417 } 418 419 Id Builder::makeRuntimeArray(Id element) 420 { 421 Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray); 422 type->addIdOperand(element); 423 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 424 module.mapInstruction(type); 425 426 return type->getResultId(); 427 } 428 429 Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes) 430 { 431 // try to find it 432 Instruction* type; 433 for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) { 434 type = groupedTypes[OpTypeFunction][t]; 435 if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) 436 continue; 437 bool mismatch = false; 438 for (int p = 0; p < (int)paramTypes.size(); ++p) { 439 if (paramTypes[p] != type->getIdOperand(p + 1)) { 440 mismatch = true; 441 break; 442 } 443 } 444 if (! mismatch) 445 return type->getResultId(); 446 } 447 448 // not found, make it 449 type = new Instruction(getUniqueId(), NoType, OpTypeFunction); 450 type->addIdOperand(returnType); 451 for (int p = 0; p < (int)paramTypes.size(); ++p) 452 type->addIdOperand(paramTypes[p]); 453 groupedTypes[OpTypeFunction].push_back(type); 454 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 455 module.mapInstruction(type); 456 457 return type->getResultId(); 458 } 459 460 Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format) 461 { 462 assert(sampled == 1 || sampled == 2); 463 464 // try to find it 465 Instruction* type; 466 for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { 467 type = groupedTypes[OpTypeImage][t]; 468 if (type->getIdOperand(0) == sampledType && 469 type->getImmediateOperand(1) == (unsigned int)dim && 470 type->getImmediateOperand(2) == ( depth ? 1u : 0u) && 471 type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && 472 type->getImmediateOperand(4) == ( ms ? 1u : 0u) && 473 type->getImmediateOperand(5) == sampled && 474 type->getImmediateOperand(6) == (unsigned int)format) 475 return type->getResultId(); 476 } 477 478 // not found, make it 479 type = new Instruction(getUniqueId(), NoType, OpTypeImage); 480 type->addIdOperand(sampledType); 481 type->addImmediateOperand( dim); 482 type->addImmediateOperand( depth ? 1 : 0); 483 type->addImmediateOperand(arrayed ? 1 : 0); 484 type->addImmediateOperand( ms ? 1 : 0); 485 type->addImmediateOperand(sampled); 486 type->addImmediateOperand((unsigned int)format); 487 488 groupedTypes[OpTypeImage].push_back(type); 489 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 490 module.mapInstruction(type); 491 492 // deal with capabilities 493 switch (dim) { 494 case DimBuffer: 495 if (sampled == 1) 496 addCapability(CapabilitySampledBuffer); 497 else 498 addCapability(CapabilityImageBuffer); 499 break; 500 case Dim1D: 501 if (sampled == 1) 502 addCapability(CapabilitySampled1D); 503 else 504 addCapability(CapabilityImage1D); 505 break; 506 case DimCube: 507 if (arrayed) { 508 if (sampled == 1) 509 addCapability(CapabilitySampledCubeArray); 510 else 511 addCapability(CapabilityImageCubeArray); 512 } 513 break; 514 case DimRect: 515 if (sampled == 1) 516 addCapability(CapabilitySampledRect); 517 else 518 addCapability(CapabilityImageRect); 519 break; 520 case DimSubpassData: 521 addCapability(CapabilityInputAttachment); 522 break; 523 default: 524 break; 525 } 526 527 if (ms) { 528 if (sampled == 2) { 529 // Images used with subpass data are not storage 530 // images, so don't require the capability for them. 531 if (dim != Dim::DimSubpassData) 532 addCapability(CapabilityStorageImageMultisample); 533 if (arrayed) 534 addCapability(CapabilityImageMSArray); 535 } 536 } 537 538 return type->getResultId(); 539 } 540 541 Id Builder::makeSampledImageType(Id imageType) 542 { 543 // try to find it 544 Instruction* type; 545 for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) { 546 type = groupedTypes[OpTypeSampledImage][t]; 547 if (type->getIdOperand(0) == imageType) 548 return type->getResultId(); 549 } 550 551 // not found, make it 552 type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage); 553 type->addIdOperand(imageType); 554 555 groupedTypes[OpTypeSampledImage].push_back(type); 556 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 557 module.mapInstruction(type); 558 559 return type->getResultId(); 560 } 561 562 #ifdef NV_EXTENSIONS 563 Id Builder::makeAccelerationStructureNVType() 564 { 565 Instruction *type; 566 if (groupedTypes[OpTypeAccelerationStructureNV].size() == 0) { 567 type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureNV); 568 groupedTypes[OpTypeAccelerationStructureNV].push_back(type); 569 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type)); 570 module.mapInstruction(type); 571 } else { 572 type = groupedTypes[OpTypeAccelerationStructureNV].back(); 573 } 574 575 return type->getResultId(); 576 } 577 #endif 578 Id Builder::getDerefTypeId(Id resultId) const 579 { 580 Id typeId = getTypeId(resultId); 581 assert(isPointerType(typeId)); 582 583 return module.getInstruction(typeId)->getIdOperand(1); 584 } 585 586 Op Builder::getMostBasicTypeClass(Id typeId) const 587 { 588 Instruction* instr = module.getInstruction(typeId); 589 590 Op typeClass = instr->getOpCode(); 591 switch (typeClass) 592 { 593 case OpTypeVector: 594 case OpTypeMatrix: 595 case OpTypeArray: 596 case OpTypeRuntimeArray: 597 return getMostBasicTypeClass(instr->getIdOperand(0)); 598 case OpTypePointer: 599 return getMostBasicTypeClass(instr->getIdOperand(1)); 600 default: 601 return typeClass; 602 } 603 } 604 605 int Builder::getNumTypeConstituents(Id typeId) const 606 { 607 Instruction* instr = module.getInstruction(typeId); 608 609 switch (instr->getOpCode()) 610 { 611 case OpTypeBool: 612 case OpTypeInt: 613 case OpTypeFloat: 614 case OpTypePointer: 615 return 1; 616 case OpTypeVector: 617 case OpTypeMatrix: 618 return instr->getImmediateOperand(1); 619 case OpTypeArray: 620 { 621 Id lengthId = instr->getIdOperand(1); 622 return module.getInstruction(lengthId)->getImmediateOperand(0); 623 } 624 case OpTypeStruct: 625 return instr->getNumOperands(); 626 default: 627 assert(0); 628 return 1; 629 } 630 } 631 632 // Return the lowest-level type of scalar that an homogeneous composite is made out of. 633 // Typically, this is just to find out if something is made out of ints or floats. 634 // However, it includes returning a structure, if say, it is an array of structure. 635 Id Builder::getScalarTypeId(Id typeId) const 636 { 637 Instruction* instr = module.getInstruction(typeId); 638 639 Op typeClass = instr->getOpCode(); 640 switch (typeClass) 641 { 642 case OpTypeVoid: 643 case OpTypeBool: 644 case OpTypeInt: 645 case OpTypeFloat: 646 case OpTypeStruct: 647 return instr->getResultId(); 648 case OpTypeVector: 649 case OpTypeMatrix: 650 case OpTypeArray: 651 case OpTypeRuntimeArray: 652 case OpTypePointer: 653 return getScalarTypeId(getContainedTypeId(typeId)); 654 default: 655 assert(0); 656 return NoResult; 657 } 658 } 659 660 // Return the type of 'member' of a composite. 661 Id Builder::getContainedTypeId(Id typeId, int member) const 662 { 663 Instruction* instr = module.getInstruction(typeId); 664 665 Op typeClass = instr->getOpCode(); 666 switch (typeClass) 667 { 668 case OpTypeVector: 669 case OpTypeMatrix: 670 case OpTypeArray: 671 case OpTypeRuntimeArray: 672 return instr->getIdOperand(0); 673 case OpTypePointer: 674 return instr->getIdOperand(1); 675 case OpTypeStruct: 676 return instr->getIdOperand(member); 677 default: 678 assert(0); 679 return NoResult; 680 } 681 } 682 683 // Return the immediately contained type of a given composite type. 684 Id Builder::getContainedTypeId(Id typeId) const 685 { 686 return getContainedTypeId(typeId, 0); 687 } 688 689 // Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' 690 // of width 'width'. The 'width' is only consumed for int and float types. 691 // Returns false otherwise. 692 bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const 693 { 694 const Instruction& instr = *module.getInstruction(typeId); 695 696 Op typeClass = instr.getOpCode(); 697 switch (typeClass) 698 { 699 case OpTypeInt: 700 case OpTypeFloat: 701 return typeClass == typeOp && instr.getImmediateOperand(0) == width; 702 case OpTypeStruct: 703 for (int m = 0; m < instr.getNumOperands(); ++m) { 704 if (containsType(instr.getIdOperand(m), typeOp, width)) 705 return true; 706 } 707 return false; 708 case OpTypePointer: 709 return false; 710 case OpTypeVector: 711 case OpTypeMatrix: 712 case OpTypeArray: 713 case OpTypeRuntimeArray: 714 return containsType(getContainedTypeId(typeId), typeOp, width); 715 default: 716 return typeClass == typeOp; 717 } 718 } 719 720 // return true if the type is a pointer to PhysicalStorageBufferEXT or an 721 // array of such pointers. These require restrict/aliased decorations. 722 bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const 723 { 724 const Instruction& instr = *module.getInstruction(typeId); 725 726 Op typeClass = instr.getOpCode(); 727 switch (typeClass) 728 { 729 case OpTypePointer: 730 return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT; 731 case OpTypeArray: 732 return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId)); 733 default: 734 return false; 735 } 736 } 737 738 // See if a scalar constant of this type has already been created, so it 739 // can be reused rather than duplicated. (Required by the specification). 740 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) 741 { 742 Instruction* constant; 743 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { 744 constant = groupedConstants[typeClass][i]; 745 if (constant->getOpCode() == opcode && 746 constant->getTypeId() == typeId && 747 constant->getImmediateOperand(0) == value) 748 return constant->getResultId(); 749 } 750 751 return 0; 752 } 753 754 // Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). 755 Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) 756 { 757 Instruction* constant; 758 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { 759 constant = groupedConstants[typeClass][i]; 760 if (constant->getOpCode() == opcode && 761 constant->getTypeId() == typeId && 762 constant->getImmediateOperand(0) == v1 && 763 constant->getImmediateOperand(1) == v2) 764 return constant->getResultId(); 765 } 766 767 return 0; 768 } 769 770 // Return true if consuming 'opcode' means consuming a constant. 771 // "constant" here means after final transform to executable code, 772 // the value consumed will be a constant, so includes specialization. 773 bool Builder::isConstantOpCode(Op opcode) const 774 { 775 switch (opcode) { 776 case OpUndef: 777 case OpConstantTrue: 778 case OpConstantFalse: 779 case OpConstant: 780 case OpConstantComposite: 781 case OpConstantSampler: 782 case OpConstantNull: 783 case OpSpecConstantTrue: 784 case OpSpecConstantFalse: 785 case OpSpecConstant: 786 case OpSpecConstantComposite: 787 case OpSpecConstantOp: 788 return true; 789 default: 790 return false; 791 } 792 } 793 794 // Return true if consuming 'opcode' means consuming a specialization constant. 795 bool Builder::isSpecConstantOpCode(Op opcode) const 796 { 797 switch (opcode) { 798 case OpSpecConstantTrue: 799 case OpSpecConstantFalse: 800 case OpSpecConstant: 801 case OpSpecConstantComposite: 802 case OpSpecConstantOp: 803 return true; 804 default: 805 return false; 806 } 807 } 808 809 Id Builder::makeBoolConstant(bool b, bool specConstant) 810 { 811 Id typeId = makeBoolType(); 812 Instruction* constant; 813 Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse); 814 815 // See if we already made it. Applies only to regular constants, because specialization constants 816 // must remain distinct for the purpose of applying a SpecId decoration. 817 if (! specConstant) { 818 Id existing = 0; 819 for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { 820 constant = groupedConstants[OpTypeBool][i]; 821 if (constant->getTypeId() == typeId && constant->getOpCode() == opcode) 822 existing = constant->getResultId(); 823 } 824 825 if (existing) 826 return existing; 827 } 828 829 // Make it 830 Instruction* c = new Instruction(getUniqueId(), typeId, opcode); 831 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); 832 groupedConstants[OpTypeBool].push_back(c); 833 module.mapInstruction(c); 834 835 return c->getResultId(); 836 } 837 838 Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) 839 { 840 Op opcode = specConstant ? OpSpecConstant : OpConstant; 841 842 // See if we already made it. Applies only to regular constants, because specialization constants 843 // must remain distinct for the purpose of applying a SpecId decoration. 844 if (! specConstant) { 845 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value); 846 if (existing) 847 return existing; 848 } 849 850 Instruction* c = new Instruction(getUniqueId(), typeId, opcode); 851 c->addImmediateOperand(value); 852 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); 853 groupedConstants[OpTypeInt].push_back(c); 854 module.mapInstruction(c); 855 856 return c->getResultId(); 857 } 858 859 Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) 860 { 861 Op opcode = specConstant ? OpSpecConstant : OpConstant; 862 863 unsigned op1 = value & 0xFFFFFFFF; 864 unsigned op2 = value >> 32; 865 866 // See if we already made it. Applies only to regular constants, because specialization constants 867 // must remain distinct for the purpose of applying a SpecId decoration. 868 if (! specConstant) { 869 Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2); 870 if (existing) 871 return existing; 872 } 873 874 Instruction* c = new Instruction(getUniqueId(), typeId, opcode); 875 c->addImmediateOperand(op1); 876 c->addImmediateOperand(op2); 877 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); 878 groupedConstants[OpTypeInt].push_back(c); 879 module.mapInstruction(c); 880 881 return c->getResultId(); 882 } 883 884 Id Builder::makeFloatConstant(float f, bool specConstant) 885 { 886 Op opcode = specConstant ? OpSpecConstant : OpConstant; 887 Id typeId = makeFloatType(32); 888 union { float fl; unsigned int ui; } u; 889 u.fl = f; 890 unsigned value = u.ui; 891 892 // See if we already made it. Applies only to regular constants, because specialization constants 893 // must remain distinct for the purpose of applying a SpecId decoration. 894 if (! specConstant) { 895 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); 896 if (existing) 897 return existing; 898 } 899 900 Instruction* c = new Instruction(getUniqueId(), typeId, opcode); 901 c->addImmediateOperand(value); 902 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); 903 groupedConstants[OpTypeFloat].push_back(c); 904 module.mapInstruction(c); 905 906 return c->getResultId(); 907 } 908 909 Id Builder::makeDoubleConstant(double d, bool specConstant) 910 { 911 Op opcode = specConstant ? OpSpecConstant : OpConstant; 912 Id typeId = makeFloatType(64); 913 union { double db; unsigned long long ull; } u; 914 u.db = d; 915 unsigned long long value = u.ull; 916 unsigned op1 = value & 0xFFFFFFFF; 917 unsigned op2 = value >> 32; 918 919 // See if we already made it. Applies only to regular constants, because specialization constants 920 // must remain distinct for the purpose of applying a SpecId decoration. 921 if (! specConstant) { 922 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2); 923 if (existing) 924 return existing; 925 } 926 927 Instruction* c = new Instruction(getUniqueId(), typeId, opcode); 928 c->addImmediateOperand(op1); 929 c->addImmediateOperand(op2); 930 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); 931 groupedConstants[OpTypeFloat].push_back(c); 932 module.mapInstruction(c); 933 934 return c->getResultId(); 935 } 936 937 Id Builder::makeFloat16Constant(float f16, bool specConstant) 938 { 939 Op opcode = specConstant ? OpSpecConstant : OpConstant; 940 Id typeId = makeFloatType(16); 941 942 spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16); 943 spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0); 944 fVal.castTo(f16Val, spvutils::kRoundToZero); 945 946 unsigned value = f16Val.value().getAsFloat().get_value(); 947 948 // See if we already made it. Applies only to regular constants, because specialization constants 949 // must remain distinct for the purpose of applying a SpecId decoration. 950 if (!specConstant) { 951 Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); 952 if (existing) 953 return existing; 954 } 955 956 Instruction* c = new Instruction(getUniqueId(), typeId, opcode); 957 c->addImmediateOperand(value); 958 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); 959 groupedConstants[OpTypeFloat].push_back(c); 960 module.mapInstruction(c); 961 962 return c->getResultId(); 963 } 964 965 Id Builder::makeFpConstant(Id type, double d, bool specConstant) 966 { 967 assert(isFloatType(type)); 968 969 switch (getScalarTypeWidth(type)) { 970 case 16: 971 return makeFloat16Constant((float)d, specConstant); 972 case 32: 973 return makeFloatConstant((float)d, specConstant); 974 case 64: 975 return makeDoubleConstant(d, specConstant); 976 default: 977 break; 978 } 979 980 assert(false); 981 return NoResult; 982 } 983 984 Id Builder::findCompositeConstant(Op typeClass, const std::vector<Id>& comps) 985 { 986 Instruction* constant = 0; 987 bool found = false; 988 for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { 989 constant = groupedConstants[typeClass][i]; 990 991 // same shape? 992 if (constant->getNumOperands() != (int)comps.size()) 993 continue; 994 995 // same contents? 996 bool mismatch = false; 997 for (int op = 0; op < constant->getNumOperands(); ++op) { 998 if (constant->getIdOperand(op) != comps[op]) { 999 mismatch = true; 1000 break; 1001 } 1002 } 1003 if (! mismatch) { 1004 found = true; 1005 break; 1006 } 1007 } 1008 1009 return found ? constant->getResultId() : NoResult; 1010 } 1011 1012 Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps) 1013 { 1014 Instruction* constant = 0; 1015 bool found = false; 1016 for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { 1017 constant = groupedStructConstants[typeId][i]; 1018 1019 // same contents? 1020 bool mismatch = false; 1021 for (int op = 0; op < constant->getNumOperands(); ++op) { 1022 if (constant->getIdOperand(op) != comps[op]) { 1023 mismatch = true; 1024 break; 1025 } 1026 } 1027 if (! mismatch) { 1028 found = true; 1029 break; 1030 } 1031 } 1032 1033 return found ? constant->getResultId() : NoResult; 1034 } 1035 1036 // Comments in header 1037 Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant) 1038 { 1039 Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite; 1040 assert(typeId); 1041 Op typeClass = getTypeClass(typeId); 1042 1043 switch (typeClass) { 1044 case OpTypeVector: 1045 case OpTypeArray: 1046 case OpTypeMatrix: 1047 if (! specConstant) { 1048 Id existing = findCompositeConstant(typeClass, members); 1049 if (existing) 1050 return existing; 1051 } 1052 break; 1053 case OpTypeStruct: 1054 if (! specConstant) { 1055 Id existing = findStructConstant(typeId, members); 1056 if (existing) 1057 return existing; 1058 } 1059 break; 1060 default: 1061 assert(0); 1062 return makeFloatConstant(0.0); 1063 } 1064 1065 Instruction* c = new Instruction(getUniqueId(), typeId, opcode); 1066 for (int op = 0; op < (int)members.size(); ++op) 1067 c->addIdOperand(members[op]); 1068 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c)); 1069 if (typeClass == OpTypeStruct) 1070 groupedStructConstants[typeId].push_back(c); 1071 else 1072 groupedConstants[typeClass].push_back(c); 1073 module.mapInstruction(c); 1074 1075 return c->getResultId(); 1076 } 1077 1078 Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) 1079 { 1080 Instruction* entryPoint = new Instruction(OpEntryPoint); 1081 entryPoint->addImmediateOperand(model); 1082 entryPoint->addIdOperand(function->getId()); 1083 entryPoint->addStringOperand(name); 1084 1085 entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint)); 1086 1087 return entryPoint; 1088 } 1089 1090 // Currently relying on the fact that all 'value' of interest are small non-negative values. 1091 void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) 1092 { 1093 Instruction* instr = new Instruction(OpExecutionMode); 1094 instr->addIdOperand(entryPoint->getId()); 1095 instr->addImmediateOperand(mode); 1096 if (value1 >= 0) 1097 instr->addImmediateOperand(value1); 1098 if (value2 >= 0) 1099 instr->addImmediateOperand(value2); 1100 if (value3 >= 0) 1101 instr->addImmediateOperand(value3); 1102 1103 executionModes.push_back(std::unique_ptr<Instruction>(instr)); 1104 } 1105 1106 void Builder::addName(Id id, const char* string) 1107 { 1108 Instruction* name = new Instruction(OpName); 1109 name->addIdOperand(id); 1110 name->addStringOperand(string); 1111 1112 names.push_back(std::unique_ptr<Instruction>(name)); 1113 } 1114 1115 void Builder::addMemberName(Id id, int memberNumber, const char* string) 1116 { 1117 Instruction* name = new Instruction(OpMemberName); 1118 name->addIdOperand(id); 1119 name->addImmediateOperand(memberNumber); 1120 name->addStringOperand(string); 1121 1122 names.push_back(std::unique_ptr<Instruction>(name)); 1123 } 1124 1125 void Builder::addDecoration(Id id, Decoration decoration, int num) 1126 { 1127 if (decoration == spv::DecorationMax) 1128 return; 1129 1130 Instruction* dec = new Instruction(OpDecorate); 1131 dec->addIdOperand(id); 1132 dec->addImmediateOperand(decoration); 1133 if (num >= 0) 1134 dec->addImmediateOperand(num); 1135 1136 decorations.push_back(std::unique_ptr<Instruction>(dec)); 1137 } 1138 1139 void Builder::addDecoration(Id id, Decoration decoration, const char* s) 1140 { 1141 if (decoration == spv::DecorationMax) 1142 return; 1143 1144 Instruction* dec = new Instruction(OpDecorateStringGOOGLE); 1145 dec->addIdOperand(id); 1146 dec->addImmediateOperand(decoration); 1147 dec->addStringOperand(s); 1148 1149 decorations.push_back(std::unique_ptr<Instruction>(dec)); 1150 } 1151 1152 void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) 1153 { 1154 if (decoration == spv::DecorationMax) 1155 return; 1156 1157 Instruction* dec = new Instruction(OpDecorateId); 1158 dec->addIdOperand(id); 1159 dec->addImmediateOperand(decoration); 1160 dec->addIdOperand(idDecoration); 1161 1162 decorations.push_back(std::unique_ptr<Instruction>(dec)); 1163 } 1164 1165 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) 1166 { 1167 if (decoration == spv::DecorationMax) 1168 return; 1169 1170 Instruction* dec = new Instruction(OpMemberDecorate); 1171 dec->addIdOperand(id); 1172 dec->addImmediateOperand(member); 1173 dec->addImmediateOperand(decoration); 1174 if (num >= 0) 1175 dec->addImmediateOperand(num); 1176 1177 decorations.push_back(std::unique_ptr<Instruction>(dec)); 1178 } 1179 1180 void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) 1181 { 1182 if (decoration == spv::DecorationMax) 1183 return; 1184 1185 Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE); 1186 dec->addIdOperand(id); 1187 dec->addImmediateOperand(member); 1188 dec->addImmediateOperand(decoration); 1189 dec->addStringOperand(s); 1190 1191 decorations.push_back(std::unique_ptr<Instruction>(dec)); 1192 } 1193 1194 // Comments in header 1195 Function* Builder::makeEntryPoint(const char* entryPoint) 1196 { 1197 assert(! entryPointFunction); 1198 1199 Block* entry; 1200 std::vector<Id> params; 1201 std::vector<std::vector<Decoration>> decorations; 1202 1203 entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry); 1204 1205 return entryPointFunction; 1206 } 1207 1208 // Comments in header 1209 Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, 1210 const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& decorations, Block **entry) 1211 { 1212 // Make the function and initial instructions in it 1213 Id typeId = makeFunctionType(returnType, paramTypes); 1214 Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); 1215 Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module); 1216 1217 // Set up the precisions 1218 setPrecision(function->getId(), precision); 1219 for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { 1220 for (int d = 0; d < (int)decorations[p].size(); ++d) 1221 addDecoration(firstParamId + p, decorations[p][d]); 1222 } 1223 1224 // CFG 1225 if (entry) { 1226 *entry = new Block(getUniqueId(), *function); 1227 function->addBlock(*entry); 1228 setBuildPoint(*entry); 1229 } 1230 1231 if (name) 1232 addName(function->getId(), name); 1233 1234 functions.push_back(std::unique_ptr<Function>(function)); 1235 1236 return function; 1237 } 1238 1239 // Comments in header 1240 void Builder::makeReturn(bool implicit, Id retVal) 1241 { 1242 if (retVal) { 1243 Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); 1244 inst->addIdOperand(retVal); 1245 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); 1246 } else 1247 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn))); 1248 1249 if (! implicit) 1250 createAndSetNoPredecessorBlock("post-return"); 1251 } 1252 1253 // Comments in header 1254 void Builder::leaveFunction() 1255 { 1256 Block* block = buildPoint; 1257 Function& function = buildPoint->getParent(); 1258 assert(block); 1259 1260 // If our function did not contain a return, add a return void now. 1261 if (! block->isTerminated()) { 1262 if (function.getReturnType() == makeVoidType()) 1263 makeReturn(true); 1264 else { 1265 makeReturn(true, createUndefined(function.getReturnType())); 1266 } 1267 } 1268 } 1269 1270 // Comments in header 1271 void Builder::makeDiscard() 1272 { 1273 buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(OpKill))); 1274 createAndSetNoPredecessorBlock("post-discard"); 1275 } 1276 1277 // Comments in header 1278 Id Builder::createVariable(StorageClass storageClass, Id type, const char* name) 1279 { 1280 Id pointerType = makePointer(storageClass, type); 1281 Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); 1282 inst->addImmediateOperand(storageClass); 1283 1284 switch (storageClass) { 1285 case StorageClassFunction: 1286 // Validation rules require the declaration in the entry block 1287 buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst)); 1288 break; 1289 1290 default: 1291 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst)); 1292 module.mapInstruction(inst); 1293 break; 1294 } 1295 1296 if (name) 1297 addName(inst->getResultId(), name); 1298 1299 return inst->getResultId(); 1300 } 1301 1302 // Comments in header 1303 Id Builder::createUndefined(Id type) 1304 { 1305 Instruction* inst = new Instruction(getUniqueId(), type, OpUndef); 1306 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); 1307 return inst->getResultId(); 1308 } 1309 1310 // av/vis/nonprivate are unnecessary and illegal for some storage classes. 1311 spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) const 1312 { 1313 switch (sc) { 1314 case spv::StorageClassUniform: 1315 case spv::StorageClassWorkgroup: 1316 case spv::StorageClassStorageBuffer: 1317 case spv::StorageClassPhysicalStorageBufferEXT: 1318 break; 1319 default: 1320 memoryAccess = spv::MemoryAccessMask(memoryAccess & 1321 ~(spv::MemoryAccessMakePointerAvailableKHRMask | 1322 spv::MemoryAccessMakePointerVisibleKHRMask | 1323 spv::MemoryAccessNonPrivatePointerKHRMask)); 1324 break; 1325 } 1326 return memoryAccess; 1327 } 1328 1329 // Comments in header 1330 void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) 1331 { 1332 Instruction* store = new Instruction(OpStore); 1333 store->addIdOperand(lValue); 1334 store->addIdOperand(rValue); 1335 1336 memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); 1337 1338 if (memoryAccess != MemoryAccessMaskNone) { 1339 store->addImmediateOperand(memoryAccess); 1340 if (memoryAccess & spv::MemoryAccessAlignedMask) { 1341 store->addImmediateOperand(alignment); 1342 } 1343 if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) { 1344 store->addIdOperand(makeUintConstant(scope)); 1345 } 1346 } 1347 1348 buildPoint->addInstruction(std::unique_ptr<Instruction>(store)); 1349 } 1350 1351 // Comments in header 1352 Id Builder::createLoad(Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) 1353 { 1354 Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad); 1355 load->addIdOperand(lValue); 1356 1357 memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); 1358 1359 if (memoryAccess != MemoryAccessMaskNone) { 1360 load->addImmediateOperand(memoryAccess); 1361 if (memoryAccess & spv::MemoryAccessAlignedMask) { 1362 load->addImmediateOperand(alignment); 1363 } 1364 if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) { 1365 load->addIdOperand(makeUintConstant(scope)); 1366 } 1367 } 1368 1369 buildPoint->addInstruction(std::unique_ptr<Instruction>(load)); 1370 1371 return load->getResultId(); 1372 } 1373 1374 // Comments in header 1375 Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets) 1376 { 1377 // Figure out the final resulting type. 1378 spv::Id typeId = getTypeId(base); 1379 assert(isPointerType(typeId) && offsets.size() > 0); 1380 typeId = getContainedTypeId(typeId); 1381 for (int i = 0; i < (int)offsets.size(); ++i) { 1382 if (isStructType(typeId)) { 1383 assert(isConstantScalar(offsets[i])); 1384 typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i])); 1385 } else 1386 typeId = getContainedTypeId(typeId, offsets[i]); 1387 } 1388 typeId = makePointer(storageClass, typeId); 1389 1390 // Make the instruction 1391 Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain); 1392 chain->addIdOperand(base); 1393 for (int i = 0; i < (int)offsets.size(); ++i) 1394 chain->addIdOperand(offsets[i]); 1395 buildPoint->addInstruction(std::unique_ptr<Instruction>(chain)); 1396 1397 return chain->getResultId(); 1398 } 1399 1400 Id Builder::createArrayLength(Id base, unsigned int member) 1401 { 1402 spv::Id intType = makeUintType(32); 1403 Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); 1404 length->addIdOperand(base); 1405 length->addImmediateOperand(member); 1406 buildPoint->addInstruction(std::unique_ptr<Instruction>(length)); 1407 1408 return length->getResultId(); 1409 } 1410 1411 Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) 1412 { 1413 // Generate code for spec constants if in spec constant operation 1414 // generation mode. 1415 if (generatingOpCodeForSpecConst) { 1416 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), std::vector<Id>(1, index)); 1417 } 1418 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); 1419 extract->addIdOperand(composite); 1420 extract->addImmediateOperand(index); 1421 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); 1422 1423 return extract->getResultId(); 1424 } 1425 1426 Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes) 1427 { 1428 // Generate code for spec constants if in spec constant operation 1429 // generation mode. 1430 if (generatingOpCodeForSpecConst) { 1431 return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes); 1432 } 1433 Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); 1434 extract->addIdOperand(composite); 1435 for (int i = 0; i < (int)indexes.size(); ++i) 1436 extract->addImmediateOperand(indexes[i]); 1437 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); 1438 1439 return extract->getResultId(); 1440 } 1441 1442 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) 1443 { 1444 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); 1445 insert->addIdOperand(object); 1446 insert->addIdOperand(composite); 1447 insert->addImmediateOperand(index); 1448 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); 1449 1450 return insert->getResultId(); 1451 } 1452 1453 Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes) 1454 { 1455 Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); 1456 insert->addIdOperand(object); 1457 insert->addIdOperand(composite); 1458 for (int i = 0; i < (int)indexes.size(); ++i) 1459 insert->addImmediateOperand(indexes[i]); 1460 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); 1461 1462 return insert->getResultId(); 1463 } 1464 1465 Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex) 1466 { 1467 Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic); 1468 extract->addIdOperand(vector); 1469 extract->addIdOperand(componentIndex); 1470 buildPoint->addInstruction(std::unique_ptr<Instruction>(extract)); 1471 1472 return extract->getResultId(); 1473 } 1474 1475 Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) 1476 { 1477 Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic); 1478 insert->addIdOperand(vector); 1479 insert->addIdOperand(component); 1480 insert->addIdOperand(componentIndex); 1481 buildPoint->addInstruction(std::unique_ptr<Instruction>(insert)); 1482 1483 return insert->getResultId(); 1484 } 1485 1486 // An opcode that has no operands, no result id, and no type 1487 void Builder::createNoResultOp(Op opCode) 1488 { 1489 Instruction* op = new Instruction(opCode); 1490 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1491 } 1492 1493 // An opcode that has one id operand, no result id, and no type 1494 void Builder::createNoResultOp(Op opCode, Id operand) 1495 { 1496 Instruction* op = new Instruction(opCode); 1497 op->addIdOperand(operand); 1498 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1499 } 1500 1501 // An opcode that has one or more operands, no result id, and no type 1502 void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands) 1503 { 1504 Instruction* op = new Instruction(opCode); 1505 for (auto it = operands.cbegin(); it != operands.cend(); ++it) { 1506 op->addIdOperand(*it); 1507 } 1508 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1509 } 1510 1511 // An opcode that has multiple operands, no result id, and no type 1512 void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands) 1513 { 1514 Instruction* op = new Instruction(opCode); 1515 for (auto it = operands.cbegin(); it != operands.cend(); ++it) { 1516 if (it->isId) 1517 op->addIdOperand(it->word); 1518 else 1519 op->addImmediateOperand(it->word); 1520 } 1521 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1522 } 1523 1524 void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) 1525 { 1526 Instruction* op = new Instruction(OpControlBarrier); 1527 op->addIdOperand(makeUintConstant(execution)); 1528 op->addIdOperand(makeUintConstant(memory)); 1529 op->addIdOperand(makeUintConstant(semantics)); 1530 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1531 } 1532 1533 void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) 1534 { 1535 Instruction* op = new Instruction(OpMemoryBarrier); 1536 op->addIdOperand(makeUintConstant(executionScope)); 1537 op->addIdOperand(makeUintConstant(memorySemantics)); 1538 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1539 } 1540 1541 // An opcode that has one operands, a result id, and a type 1542 Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) 1543 { 1544 // Generate code for spec constants if in spec constant operation 1545 // generation mode. 1546 if (generatingOpCodeForSpecConst) { 1547 return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>()); 1548 } 1549 Instruction* op = new Instruction(getUniqueId(), typeId, opCode); 1550 op->addIdOperand(operand); 1551 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1552 1553 return op->getResultId(); 1554 } 1555 1556 Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) 1557 { 1558 // Generate code for spec constants if in spec constant operation 1559 // generation mode. 1560 if (generatingOpCodeForSpecConst) { 1561 std::vector<Id> operands(2); 1562 operands[0] = left; operands[1] = right; 1563 return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>()); 1564 } 1565 Instruction* op = new Instruction(getUniqueId(), typeId, opCode); 1566 op->addIdOperand(left); 1567 op->addIdOperand(right); 1568 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1569 1570 return op->getResultId(); 1571 } 1572 1573 Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) 1574 { 1575 // Generate code for spec constants if in spec constant operation 1576 // generation mode. 1577 if (generatingOpCodeForSpecConst) { 1578 std::vector<Id> operands(3); 1579 operands[0] = op1; 1580 operands[1] = op2; 1581 operands[2] = op3; 1582 return createSpecConstantOp( 1583 opCode, typeId, operands, std::vector<Id>()); 1584 } 1585 Instruction* op = new Instruction(getUniqueId(), typeId, opCode); 1586 op->addIdOperand(op1); 1587 op->addIdOperand(op2); 1588 op->addIdOperand(op3); 1589 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1590 1591 return op->getResultId(); 1592 } 1593 1594 Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands) 1595 { 1596 Instruction* op = new Instruction(getUniqueId(), typeId, opCode); 1597 for (auto it = operands.cbegin(); it != operands.cend(); ++it) 1598 op->addIdOperand(*it); 1599 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1600 1601 return op->getResultId(); 1602 } 1603 1604 Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands) 1605 { 1606 Instruction* op = new Instruction(getUniqueId(), typeId, opCode); 1607 for (auto it = operands.cbegin(); it != operands.cend(); ++it) { 1608 if (it->isId) 1609 op->addIdOperand(it->word); 1610 else 1611 op->addImmediateOperand(it->word); 1612 } 1613 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1614 1615 return op->getResultId(); 1616 } 1617 1618 Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, const std::vector<unsigned>& literals) 1619 { 1620 Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); 1621 op->addImmediateOperand((unsigned) opCode); 1622 for (auto it = operands.cbegin(); it != operands.cend(); ++it) 1623 op->addIdOperand(*it); 1624 for (auto it = literals.cbegin(); it != literals.cend(); ++it) 1625 op->addImmediateOperand(*it); 1626 module.mapInstruction(op); 1627 constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op)); 1628 1629 return op->getResultId(); 1630 } 1631 1632 Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args) 1633 { 1634 Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); 1635 op->addIdOperand(function->getId()); 1636 for (int a = 0; a < (int)args.size(); ++a) 1637 op->addIdOperand(args[a]); 1638 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 1639 1640 return op->getResultId(); 1641 } 1642 1643 // Comments in header 1644 Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels) 1645 { 1646 if (channels.size() == 1) 1647 return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); 1648 1649 if (generatingOpCodeForSpecConst) { 1650 std::vector<Id> operands(2); 1651 operands[0] = operands[1] = source; 1652 return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision); 1653 } 1654 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); 1655 assert(isVector(source)); 1656 swizzle->addIdOperand(source); 1657 swizzle->addIdOperand(source); 1658 for (int i = 0; i < (int)channels.size(); ++i) 1659 swizzle->addImmediateOperand(channels[i]); 1660 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle)); 1661 1662 return setPrecision(swizzle->getResultId(), precision); 1663 } 1664 1665 // Comments in header 1666 Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels) 1667 { 1668 if (channels.size() == 1 && getNumComponents(source) == 1) 1669 return createCompositeInsert(source, target, typeId, channels.front()); 1670 1671 Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); 1672 1673 assert(isVector(target)); 1674 swizzle->addIdOperand(target); 1675 1676 assert(getNumComponents(source) == (int)channels.size()); 1677 assert(isVector(source)); 1678 swizzle->addIdOperand(source); 1679 1680 // Set up an identity shuffle from the base value to the result value 1681 unsigned int components[4]; 1682 int numTargetComponents = getNumComponents(target); 1683 for (int i = 0; i < numTargetComponents; ++i) 1684 components[i] = i; 1685 1686 // Punch in the l-value swizzle 1687 for (int i = 0; i < (int)channels.size(); ++i) 1688 components[channels[i]] = numTargetComponents + i; 1689 1690 // finish the instruction with these components selectors 1691 for (int i = 0; i < numTargetComponents; ++i) 1692 swizzle->addImmediateOperand(components[i]); 1693 buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle)); 1694 1695 return swizzle->getResultId(); 1696 } 1697 1698 // Comments in header 1699 void Builder::promoteScalar(Decoration precision, Id& left, Id& right) 1700 { 1701 int direction = getNumComponents(right) - getNumComponents(left); 1702 1703 if (direction > 0) 1704 left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right))); 1705 else if (direction < 0) 1706 right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left))); 1707 1708 return; 1709 } 1710 1711 // Comments in header 1712 Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) 1713 { 1714 assert(getNumComponents(scalar) == 1); 1715 assert(getTypeId(scalar) == getScalarTypeId(vectorType)); 1716 1717 int numComponents = getNumTypeComponents(vectorType); 1718 if (numComponents == 1) 1719 return scalar; 1720 1721 Instruction* smear = nullptr; 1722 if (generatingOpCodeForSpecConst) { 1723 auto members = std::vector<spv::Id>(numComponents, scalar); 1724 // Sometime even in spec-constant-op mode, the temporary vector created by 1725 // promoting a scalar might not be a spec constant. This should depend on 1726 // the scalar. 1727 // e.g.: 1728 // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; 1729 // In such cases, the temporary vector created from a_front_end_const_scalar 1730 // is not a spec constant vector, even though the binary operation node is marked 1731 // as 'specConstant' and we are in spec-constant-op mode. 1732 auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); 1733 smear = module.getInstruction(result_id); 1734 } else { 1735 smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); 1736 for (int c = 0; c < numComponents; ++c) 1737 smear->addIdOperand(scalar); 1738 buildPoint->addInstruction(std::unique_ptr<Instruction>(smear)); 1739 } 1740 1741 return setPrecision(smear->getResultId(), precision); 1742 } 1743 1744 // Comments in header 1745 Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args) 1746 { 1747 Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); 1748 inst->addIdOperand(builtins); 1749 inst->addImmediateOperand(entryPoint); 1750 for (int arg = 0; arg < (int)args.size(); ++arg) 1751 inst->addIdOperand(args[arg]); 1752 1753 buildPoint->addInstruction(std::unique_ptr<Instruction>(inst)); 1754 1755 return inst->getResultId(); 1756 } 1757 1758 // Accept all parameters needed to create a texture instruction. 1759 // Create the correct instruction based on the inputs, and make the call. 1760 Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, 1761 bool noImplicitLod, const TextureParameters& parameters) 1762 { 1763 static const int maxTextureArgs = 10; 1764 Id texArgs[maxTextureArgs] = {}; 1765 1766 // 1767 // Set up the fixed arguments 1768 // 1769 int numArgs = 0; 1770 bool explicitLod = false; 1771 texArgs[numArgs++] = parameters.sampler; 1772 texArgs[numArgs++] = parameters.coords; 1773 if (parameters.Dref != NoResult) 1774 texArgs[numArgs++] = parameters.Dref; 1775 if (parameters.component != NoResult) 1776 texArgs[numArgs++] = parameters.component; 1777 1778 #ifdef NV_EXTENSIONS 1779 if (parameters.granularity != NoResult) 1780 texArgs[numArgs++] = parameters.granularity; 1781 if (parameters.coarse != NoResult) 1782 texArgs[numArgs++] = parameters.coarse; 1783 #endif 1784 1785 // 1786 // Set up the optional arguments 1787 // 1788 int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments 1789 ++numArgs; // speculatively make room for the mask operand 1790 ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand 1791 if (parameters.bias) { 1792 mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask); 1793 texArgs[numArgs++] = parameters.bias; 1794 } 1795 if (parameters.lod) { 1796 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); 1797 texArgs[numArgs++] = parameters.lod; 1798 explicitLod = true; 1799 } else if (parameters.gradX) { 1800 mask = (ImageOperandsMask)(mask | ImageOperandsGradMask); 1801 texArgs[numArgs++] = parameters.gradX; 1802 texArgs[numArgs++] = parameters.gradY; 1803 explicitLod = true; 1804 } else if (noImplicitLod && ! fetch && ! gather) { 1805 // have to explicitly use lod of 0 if not allowed to have them be implicit, and 1806 // we would otherwise be about to issue an implicit instruction 1807 mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); 1808 texArgs[numArgs++] = makeFloatConstant(0.0); 1809 explicitLod = true; 1810 } 1811 if (parameters.offset) { 1812 if (isConstant(parameters.offset)) 1813 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); 1814 else { 1815 addCapability(CapabilityImageGatherExtended); 1816 mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); 1817 } 1818 texArgs[numArgs++] = parameters.offset; 1819 } 1820 if (parameters.offsets) { 1821 addCapability(CapabilityImageGatherExtended); 1822 mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask); 1823 texArgs[numArgs++] = parameters.offsets; 1824 } 1825 if (parameters.sample) { 1826 mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask); 1827 texArgs[numArgs++] = parameters.sample; 1828 } 1829 if (parameters.lodClamp) { 1830 // capability if this bit is used 1831 addCapability(CapabilityMinLod); 1832 1833 mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask); 1834 texArgs[numArgs++] = parameters.lodClamp; 1835 } 1836 if (parameters.nonprivate) { 1837 mask = mask | ImageOperandsNonPrivateTexelKHRMask; 1838 } 1839 if (parameters.volatil) { 1840 mask = mask | ImageOperandsVolatileTexelKHRMask; 1841 } 1842 if (mask == ImageOperandsMaskNone) 1843 --numArgs; // undo speculative reservation for the mask argument 1844 else 1845 texArgs[optArgNum] = mask; 1846 1847 // 1848 // Set up the instruction 1849 // 1850 Op opCode = OpNop; // All paths below need to set this 1851 if (fetch) { 1852 if (sparse) 1853 opCode = OpImageSparseFetch; 1854 else 1855 opCode = OpImageFetch; 1856 #ifdef NV_EXTENSIONS 1857 } else if (parameters.granularity && parameters.coarse) { 1858 opCode = OpImageSampleFootprintNV; 1859 #endif 1860 } else if (gather) { 1861 if (parameters.Dref) 1862 if (sparse) 1863 opCode = OpImageSparseDrefGather; 1864 else 1865 opCode = OpImageDrefGather; 1866 else 1867 if (sparse) 1868 opCode = OpImageSparseGather; 1869 else 1870 opCode = OpImageGather; 1871 } else if (explicitLod) { 1872 if (parameters.Dref) { 1873 if (proj) 1874 if (sparse) 1875 opCode = OpImageSparseSampleProjDrefExplicitLod; 1876 else 1877 opCode = OpImageSampleProjDrefExplicitLod; 1878 else 1879 if (sparse) 1880 opCode = OpImageSparseSampleDrefExplicitLod; 1881 else 1882 opCode = OpImageSampleDrefExplicitLod; 1883 } else { 1884 if (proj) 1885 if (sparse) 1886 opCode = OpImageSparseSampleProjExplicitLod; 1887 else 1888 opCode = OpImageSampleProjExplicitLod; 1889 else 1890 if (sparse) 1891 opCode = OpImageSparseSampleExplicitLod; 1892 else 1893 opCode = OpImageSampleExplicitLod; 1894 } 1895 } else { 1896 if (parameters.Dref) { 1897 if (proj) 1898 if (sparse) 1899 opCode = OpImageSparseSampleProjDrefImplicitLod; 1900 else 1901 opCode = OpImageSampleProjDrefImplicitLod; 1902 else 1903 if (sparse) 1904 opCode = OpImageSparseSampleDrefImplicitLod; 1905 else 1906 opCode = OpImageSampleDrefImplicitLod; 1907 } else { 1908 if (proj) 1909 if (sparse) 1910 opCode = OpImageSparseSampleProjImplicitLod; 1911 else 1912 opCode = OpImageSampleProjImplicitLod; 1913 else 1914 if (sparse) 1915 opCode = OpImageSparseSampleImplicitLod; 1916 else 1917 opCode = OpImageSampleImplicitLod; 1918 } 1919 } 1920 1921 // See if the result type is expecting a smeared result. 1922 // This happens when a legacy shadow*() call is made, which 1923 // gets a vec4 back instead of a float. 1924 Id smearedType = resultType; 1925 if (! isScalarType(resultType)) { 1926 switch (opCode) { 1927 case OpImageSampleDrefImplicitLod: 1928 case OpImageSampleDrefExplicitLod: 1929 case OpImageSampleProjDrefImplicitLod: 1930 case OpImageSampleProjDrefExplicitLod: 1931 resultType = getScalarTypeId(resultType); 1932 break; 1933 default: 1934 break; 1935 } 1936 } 1937 1938 Id typeId0 = 0; 1939 Id typeId1 = 0; 1940 1941 if (sparse) { 1942 typeId0 = resultType; 1943 typeId1 = getDerefTypeId(parameters.texelOut); 1944 resultType = makeStructResultType(typeId0, typeId1); 1945 } 1946 1947 // Build the SPIR-V instruction 1948 Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); 1949 for (int op = 0; op < optArgNum; ++op) 1950 textureInst->addIdOperand(texArgs[op]); 1951 if (optArgNum < numArgs) 1952 textureInst->addImmediateOperand(texArgs[optArgNum]); 1953 for (int op = optArgNum + 1; op < numArgs; ++op) 1954 textureInst->addIdOperand(texArgs[op]); 1955 setPrecision(textureInst->getResultId(), precision); 1956 buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst)); 1957 1958 Id resultId = textureInst->getResultId(); 1959 1960 if (sparse) { 1961 // set capability 1962 addCapability(CapabilitySparseResidency); 1963 1964 // Decode the return type that was a special structure 1965 createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut); 1966 resultId = createCompositeExtract(resultId, typeId0, 0); 1967 setPrecision(resultId, precision); 1968 } else { 1969 // When a smear is needed, do it, as per what was computed 1970 // above when resultType was changed to a scalar type. 1971 if (resultType != smearedType) 1972 resultId = smearScalar(precision, resultId, smearedType); 1973 } 1974 1975 return resultId; 1976 } 1977 1978 // Comments in header 1979 Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) 1980 { 1981 // Figure out the result type 1982 Id resultType = 0; 1983 switch (opCode) { 1984 case OpImageQuerySize: 1985 case OpImageQuerySizeLod: 1986 { 1987 int numComponents = 0; 1988 switch (getTypeDimensionality(getImageType(parameters.sampler))) { 1989 case Dim1D: 1990 case DimBuffer: 1991 numComponents = 1; 1992 break; 1993 case Dim2D: 1994 case DimCube: 1995 case DimRect: 1996 case DimSubpassData: 1997 numComponents = 2; 1998 break; 1999 case Dim3D: 2000 numComponents = 3; 2001 break; 2002 2003 default: 2004 assert(0); 2005 break; 2006 } 2007 if (isArrayedImageType(getImageType(parameters.sampler))) 2008 ++numComponents; 2009 2010 Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); 2011 if (numComponents == 1) 2012 resultType = intType; 2013 else 2014 resultType = makeVectorType(intType, numComponents); 2015 2016 break; 2017 } 2018 case OpImageQueryLod: 2019 #ifdef AMD_EXTENSIONS 2020 resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2); 2021 #else 2022 resultType = makeVectorType(makeFloatType(32), 2); 2023 #endif 2024 break; 2025 case OpImageQueryLevels: 2026 case OpImageQuerySamples: 2027 resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); 2028 break; 2029 default: 2030 assert(0); 2031 break; 2032 } 2033 2034 Instruction* query = new Instruction(getUniqueId(), resultType, opCode); 2035 query->addIdOperand(parameters.sampler); 2036 if (parameters.coords) 2037 query->addIdOperand(parameters.coords); 2038 if (parameters.lod) 2039 query->addIdOperand(parameters.lod); 2040 buildPoint->addInstruction(std::unique_ptr<Instruction>(query)); 2041 2042 return query->getResultId(); 2043 } 2044 2045 // External comments in header. 2046 // Operates recursively to visit the composite's hierarchy. 2047 Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) 2048 { 2049 Id boolType = makeBoolType(); 2050 Id valueType = getTypeId(value1); 2051 2052 Id resultId = NoResult; 2053 2054 int numConstituents = getNumTypeConstituents(valueType); 2055 2056 // Scalars and Vectors 2057 2058 if (isScalarType(valueType) || isVectorType(valueType)) { 2059 assert(valueType == getTypeId(value2)); 2060 // These just need a single comparison, just have 2061 // to figure out what it is. 2062 Op op; 2063 switch (getMostBasicTypeClass(valueType)) { 2064 case OpTypeFloat: 2065 op = equal ? OpFOrdEqual : OpFOrdNotEqual; 2066 break; 2067 case OpTypeInt: 2068 default: 2069 op = equal ? OpIEqual : OpINotEqual; 2070 break; 2071 case OpTypeBool: 2072 op = equal ? OpLogicalEqual : OpLogicalNotEqual; 2073 precision = NoPrecision; 2074 break; 2075 } 2076 2077 if (isScalarType(valueType)) { 2078 // scalar 2079 resultId = createBinOp(op, boolType, value1, value2); 2080 } else { 2081 // vector 2082 resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2); 2083 setPrecision(resultId, precision); 2084 // reduce vector compares... 2085 resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId); 2086 } 2087 2088 return setPrecision(resultId, precision); 2089 } 2090 2091 // Only structs, arrays, and matrices should be left. 2092 // They share in common the reduction operation across their constituents. 2093 assert(isAggregateType(valueType) || isMatrixType(valueType)); 2094 2095 // Compare each pair of constituents 2096 for (int constituent = 0; constituent < numConstituents; ++constituent) { 2097 std::vector<unsigned> indexes(1, constituent); 2098 Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent); 2099 Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent); 2100 Id constituent1 = createCompositeExtract(value1, constituentType1, indexes); 2101 Id constituent2 = createCompositeExtract(value2, constituentType2, indexes); 2102 2103 Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal); 2104 2105 if (constituent == 0) 2106 resultId = subResultId; 2107 else 2108 resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), precision); 2109 } 2110 2111 return resultId; 2112 } 2113 2114 // OpCompositeConstruct 2115 Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents) 2116 { 2117 assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size())); 2118 2119 if (generatingOpCodeForSpecConst) { 2120 // Sometime, even in spec-constant-op mode, the constant composite to be 2121 // constructed may not be a specialization constant. 2122 // e.g.: 2123 // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); 2124 // The first column vector should be a spec constant one, as a_spec_const is a spec constant. 2125 // The second column vector should NOT be spec constant, as it does not contain any spec constants. 2126 // To handle such cases, we check the constituents of the constant vector to determine whether this 2127 // vector should be created as a spec constant. 2128 return makeCompositeConstant(typeId, constituents, 2129 std::any_of(constituents.begin(), constituents.end(), 2130 [&](spv::Id id) { return isSpecConstant(id); })); 2131 } 2132 2133 Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); 2134 for (int c = 0; c < (int)constituents.size(); ++c) 2135 op->addIdOperand(constituents[c]); 2136 buildPoint->addInstruction(std::unique_ptr<Instruction>(op)); 2137 2138 return op->getResultId(); 2139 } 2140 2141 // Vector or scalar constructor 2142 Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) 2143 { 2144 Id result = NoResult; 2145 unsigned int numTargetComponents = getNumTypeComponents(resultTypeId); 2146 unsigned int targetComponent = 0; 2147 2148 // Special case: when calling a vector constructor with a single scalar 2149 // argument, smear the scalar 2150 if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1) 2151 return smearScalar(precision, sources[0], resultTypeId); 2152 2153 // accumulate the arguments for OpCompositeConstruct 2154 std::vector<Id> constituents; 2155 Id scalarTypeId = getScalarTypeId(resultTypeId); 2156 2157 // lambda to store the result of visiting an argument component 2158 const auto latchResult = [&](Id comp) { 2159 if (numTargetComponents > 1) 2160 constituents.push_back(comp); 2161 else 2162 result = comp; 2163 ++targetComponent; 2164 }; 2165 2166 // lambda to visit a vector argument's components 2167 const auto accumulateVectorConstituents = [&](Id sourceArg) { 2168 unsigned int sourceSize = getNumComponents(sourceArg); 2169 unsigned int sourcesToUse = sourceSize; 2170 if (sourcesToUse + targetComponent > numTargetComponents) 2171 sourcesToUse = numTargetComponents - targetComponent; 2172 2173 for (unsigned int s = 0; s < sourcesToUse; ++s) { 2174 std::vector<unsigned> swiz; 2175 swiz.push_back(s); 2176 latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); 2177 } 2178 }; 2179 2180 // lambda to visit a matrix argument's components 2181 const auto accumulateMatrixConstituents = [&](Id sourceArg) { 2182 unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); 2183 unsigned int sourcesToUse = sourceSize; 2184 if (sourcesToUse + targetComponent > numTargetComponents) 2185 sourcesToUse = numTargetComponents - targetComponent; 2186 2187 int col = 0; 2188 int row = 0; 2189 for (unsigned int s = 0; s < sourcesToUse; ++s) { 2190 if (row >= getNumRows(sourceArg)) { 2191 row = 0; 2192 col++; 2193 } 2194 std::vector<Id> indexes; 2195 indexes.push_back(col); 2196 indexes.push_back(row); 2197 latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); 2198 row++; 2199 } 2200 }; 2201 2202 // Go through the source arguments, each one could have either 2203 // a single or multiple components to contribute. 2204 for (unsigned int i = 0; i < sources.size(); ++i) { 2205 2206 if (isScalar(sources[i]) || isPointer(sources[i])) 2207 latchResult(sources[i]); 2208 else if (isVector(sources[i])) 2209 accumulateVectorConstituents(sources[i]); 2210 else if (isMatrix(sources[i])) 2211 accumulateMatrixConstituents(sources[i]); 2212 else 2213 assert(0); 2214 2215 if (targetComponent >= numTargetComponents) 2216 break; 2217 } 2218 2219 // If the result is a vector, make it from the gathered constituents. 2220 if (constituents.size() > 0) 2221 result = createCompositeConstruct(resultTypeId, constituents); 2222 2223 return setPrecision(result, precision); 2224 } 2225 2226 // Comments in header 2227 Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId) 2228 { 2229 Id componentTypeId = getScalarTypeId(resultTypeId); 2230 int numCols = getTypeNumColumns(resultTypeId); 2231 int numRows = getTypeNumRows(resultTypeId); 2232 2233 Instruction* instr = module.getInstruction(componentTypeId); 2234 unsigned bitCount = instr->getImmediateOperand(0); 2235 2236 // Optimize matrix constructed from a bigger matrix 2237 if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) { 2238 // To truncate the matrix to a smaller number of rows/columns, we need to: 2239 // 1. For each column, extract the column and truncate it to the required size using shuffle 2240 // 2. Assemble the resulting matrix from all columns 2241 Id matrix = sources[0]; 2242 Id columnTypeId = getContainedTypeId(resultTypeId); 2243 Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix)); 2244 2245 std::vector<unsigned> channels; 2246 for (int row = 0; row < numRows; ++row) 2247 channels.push_back(row); 2248 2249 std::vector<Id> matrixColumns; 2250 for (int col = 0; col < numCols; ++col) { 2251 std::vector<unsigned> indexes; 2252 indexes.push_back(col); 2253 Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes); 2254 setPrecision(colv, precision); 2255 2256 if (numRows != getNumRows(matrix)) { 2257 matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels)); 2258 } else { 2259 matrixColumns.push_back(colv); 2260 } 2261 } 2262 2263 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); 2264 } 2265 2266 // Otherwise, will use a two step process 2267 // 1. make a compile-time 2D array of values 2268 // 2. construct a matrix from that array 2269 2270 // Step 1. 2271 2272 // initialize the array to the identity matrix 2273 Id ids[maxMatrixSize][maxMatrixSize]; 2274 Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); 2275 Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); 2276 for (int col = 0; col < 4; ++col) { 2277 for (int row = 0; row < 4; ++row) { 2278 if (col == row) 2279 ids[col][row] = one; 2280 else 2281 ids[col][row] = zero; 2282 } 2283 } 2284 2285 // modify components as dictated by the arguments 2286 if (sources.size() == 1 && isScalar(sources[0])) { 2287 // a single scalar; resets the diagonals 2288 for (int col = 0; col < 4; ++col) 2289 ids[col][col] = sources[0]; 2290 } else if (isMatrix(sources[0])) { 2291 // constructing from another matrix; copy over the parts that exist in both the argument and constructee 2292 Id matrix = sources[0]; 2293 int minCols = std::min(numCols, getNumColumns(matrix)); 2294 int minRows = std::min(numRows, getNumRows(matrix)); 2295 for (int col = 0; col < minCols; ++col) { 2296 std::vector<unsigned> indexes; 2297 indexes.push_back(col); 2298 for (int row = 0; row < minRows; ++row) { 2299 indexes.push_back(row); 2300 ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes); 2301 indexes.pop_back(); 2302 setPrecision(ids[col][row], precision); 2303 } 2304 } 2305 } else { 2306 // fill in the matrix in column-major order with whatever argument components are available 2307 int row = 0; 2308 int col = 0; 2309 2310 for (int arg = 0; arg < (int)sources.size(); ++arg) { 2311 Id argComp = sources[arg]; 2312 for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) { 2313 if (getNumComponents(sources[arg]) > 1) { 2314 argComp = createCompositeExtract(sources[arg], componentTypeId, comp); 2315 setPrecision(argComp, precision); 2316 } 2317 ids[col][row++] = argComp; 2318 if (row == numRows) { 2319 row = 0; 2320 col++; 2321 } 2322 } 2323 } 2324 } 2325 2326 // Step 2: Construct a matrix from that array. 2327 // First make the column vectors, then make the matrix. 2328 2329 // make the column vectors 2330 Id columnTypeId = getContainedTypeId(resultTypeId); 2331 std::vector<Id> matrixColumns; 2332 for (int col = 0; col < numCols; ++col) { 2333 std::vector<Id> vectorComponents; 2334 for (int row = 0; row < numRows; ++row) 2335 vectorComponents.push_back(ids[col][row]); 2336 Id column = createCompositeConstruct(columnTypeId, vectorComponents); 2337 setPrecision(column, precision); 2338 matrixColumns.push_back(column); 2339 } 2340 2341 // make the matrix 2342 return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); 2343 } 2344 2345 // Comments in header 2346 Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : 2347 builder(gb), 2348 condition(cond), 2349 control(ctrl), 2350 elseBlock(0) 2351 { 2352 function = &builder.getBuildPoint()->getParent(); 2353 2354 // make the blocks, but only put the then-block into the function, 2355 // the else-block and merge-block will be added later, in order, after 2356 // earlier code is emitted 2357 thenBlock = new Block(builder.getUniqueId(), *function); 2358 mergeBlock = new Block(builder.getUniqueId(), *function); 2359 2360 // Save the current block, so that we can add in the flow control split when 2361 // makeEndIf is called. 2362 headerBlock = builder.getBuildPoint(); 2363 2364 function->addBlock(thenBlock); 2365 builder.setBuildPoint(thenBlock); 2366 } 2367 2368 // Comments in header 2369 void Builder::If::makeBeginElse() 2370 { 2371 // Close out the "then" by having it jump to the mergeBlock 2372 builder.createBranch(mergeBlock); 2373 2374 // Make the first else block and add it to the function 2375 elseBlock = new Block(builder.getUniqueId(), *function); 2376 function->addBlock(elseBlock); 2377 2378 // Start building the else block 2379 builder.setBuildPoint(elseBlock); 2380 } 2381 2382 // Comments in header 2383 void Builder::If::makeEndIf() 2384 { 2385 // jump to the merge block 2386 builder.createBranch(mergeBlock); 2387 2388 // Go back to the headerBlock and make the flow control split 2389 builder.setBuildPoint(headerBlock); 2390 builder.createSelectionMerge(mergeBlock, control); 2391 if (elseBlock) 2392 builder.createConditionalBranch(condition, thenBlock, elseBlock); 2393 else 2394 builder.createConditionalBranch(condition, thenBlock, mergeBlock); 2395 2396 // add the merge block to the function 2397 function->addBlock(mergeBlock); 2398 builder.setBuildPoint(mergeBlock); 2399 } 2400 2401 // Comments in header 2402 void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues, 2403 const std::vector<int>& valueIndexToSegment, int defaultSegment, 2404 std::vector<Block*>& segmentBlocks) 2405 { 2406 Function& function = buildPoint->getParent(); 2407 2408 // make all the blocks 2409 for (int s = 0; s < numSegments; ++s) 2410 segmentBlocks.push_back(new Block(getUniqueId(), function)); 2411 2412 Block* mergeBlock = new Block(getUniqueId(), function); 2413 2414 // make and insert the switch's selection-merge instruction 2415 createSelectionMerge(mergeBlock, control); 2416 2417 // make the switch instruction 2418 Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); 2419 switchInst->addIdOperand(selector); 2420 auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; 2421 switchInst->addIdOperand(defaultOrMerge->getId()); 2422 defaultOrMerge->addPredecessor(buildPoint); 2423 for (int i = 0; i < (int)caseValues.size(); ++i) { 2424 switchInst->addImmediateOperand(caseValues[i]); 2425 switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); 2426 segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint); 2427 } 2428 buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst)); 2429 2430 // push the merge block 2431 switchMerges.push(mergeBlock); 2432 } 2433 2434 // Comments in header 2435 void Builder::addSwitchBreak() 2436 { 2437 // branch to the top of the merge block stack 2438 createBranch(switchMerges.top()); 2439 createAndSetNoPredecessorBlock("post-switch-break"); 2440 } 2441 2442 // Comments in header 2443 void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment) 2444 { 2445 int lastSegment = nextSegment - 1; 2446 if (lastSegment >= 0) { 2447 // Close out previous segment by jumping, if necessary, to next segment 2448 if (! buildPoint->isTerminated()) 2449 createBranch(segmentBlock[nextSegment]); 2450 } 2451 Block* block = segmentBlock[nextSegment]; 2452 block->getParent().addBlock(block); 2453 setBuildPoint(block); 2454 } 2455 2456 // Comments in header 2457 void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/) 2458 { 2459 // Close out previous segment by jumping, if necessary, to next segment 2460 if (! buildPoint->isTerminated()) 2461 addSwitchBreak(); 2462 2463 switchMerges.top()->getParent().addBlock(switchMerges.top()); 2464 setBuildPoint(switchMerges.top()); 2465 2466 switchMerges.pop(); 2467 } 2468 2469 Block& Builder::makeNewBlock() 2470 { 2471 Function& function = buildPoint->getParent(); 2472 auto block = new Block(getUniqueId(), function); 2473 function.addBlock(block); 2474 return *block; 2475 } 2476 2477 Builder::LoopBlocks& Builder::makeNewLoop() 2478 { 2479 // This verbosity is needed to simultaneously get the same behavior 2480 // everywhere (id's in the same order), have a syntax that works 2481 // across lots of versions of C++, have no warnings from pedantic 2482 // compilation modes, and leave the rest of the code alone. 2483 Block& head = makeNewBlock(); 2484 Block& body = makeNewBlock(); 2485 Block& merge = makeNewBlock(); 2486 Block& continue_target = makeNewBlock(); 2487 LoopBlocks blocks(head, body, merge, continue_target); 2488 loops.push(blocks); 2489 return loops.top(); 2490 } 2491 2492 void Builder::createLoopContinue() 2493 { 2494 createBranch(&loops.top().continue_target); 2495 // Set up a block for dead code. 2496 createAndSetNoPredecessorBlock("post-loop-continue"); 2497 } 2498 2499 void Builder::createLoopExit() 2500 { 2501 createBranch(&loops.top().merge); 2502 // Set up a block for dead code. 2503 createAndSetNoPredecessorBlock("post-loop-break"); 2504 } 2505 2506 void Builder::closeLoop() 2507 { 2508 loops.pop(); 2509 } 2510 2511 void Builder::clearAccessChain() 2512 { 2513 accessChain.base = NoResult; 2514 accessChain.indexChain.clear(); 2515 accessChain.instr = NoResult; 2516 accessChain.swizzle.clear(); 2517 accessChain.component = NoResult; 2518 accessChain.preSwizzleBaseType = NoType; 2519 accessChain.isRValue = false; 2520 accessChain.coherentFlags.clear(); 2521 accessChain.alignment = 0; 2522 } 2523 2524 // Comments in header 2525 void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) 2526 { 2527 accessChain.coherentFlags |= coherentFlags; 2528 accessChain.alignment |= alignment; 2529 2530 // swizzles can be stacked in GLSL, but simplified to a single 2531 // one here; the base type doesn't change 2532 if (accessChain.preSwizzleBaseType == NoType) 2533 accessChain.preSwizzleBaseType = preSwizzleBaseType; 2534 2535 // if needed, propagate the swizzle for the current access chain 2536 if (accessChain.swizzle.size() > 0) { 2537 std::vector<unsigned> oldSwizzle = accessChain.swizzle; 2538 accessChain.swizzle.resize(0); 2539 for (unsigned int i = 0; i < swizzle.size(); ++i) { 2540 assert(swizzle[i] < oldSwizzle.size()); 2541 accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); 2542 } 2543 } else 2544 accessChain.swizzle = swizzle; 2545 2546 // determine if we need to track this swizzle anymore 2547 simplifyAccessChainSwizzle(); 2548 } 2549 2550 // Comments in header 2551 void Builder::accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) 2552 { 2553 assert(accessChain.isRValue == false); 2554 2555 transferAccessChainSwizzle(true); 2556 Id base = collapseAccessChain(); 2557 Id source = rvalue; 2558 2559 // dynamic component should be gone 2560 assert(accessChain.component == NoResult); 2561 2562 // If swizzle still exists, it is out-of-order or not full, we must load the target vector, 2563 // extract and insert elements to perform writeMask and/or swizzle. 2564 if (accessChain.swizzle.size() > 0) { 2565 Id tempBaseId = createLoad(base); 2566 source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); 2567 } 2568 2569 // take LSB of alignment 2570 alignment = alignment & ~(alignment & (alignment-1)); 2571 if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { 2572 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); 2573 } 2574 2575 createStore(source, base, memoryAccess, scope, alignment); 2576 } 2577 2578 // Comments in header 2579 Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) 2580 { 2581 Id id; 2582 2583 if (accessChain.isRValue) { 2584 // transfer access chain, but try to stay in registers 2585 transferAccessChainSwizzle(false); 2586 if (accessChain.indexChain.size() > 0) { 2587 Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; 2588 2589 // if all the accesses are constants, we can use OpCompositeExtract 2590 std::vector<unsigned> indexes; 2591 bool constant = true; 2592 for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { 2593 if (isConstantScalar(accessChain.indexChain[i])) 2594 indexes.push_back(getConstantScalar(accessChain.indexChain[i])); 2595 else { 2596 constant = false; 2597 break; 2598 } 2599 } 2600 2601 if (constant) 2602 id = createCompositeExtract(accessChain.base, swizzleBase, indexes); 2603 else { 2604 // make a new function variable for this r-value 2605 Id lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable"); 2606 2607 // store into it 2608 createStore(accessChain.base, lValue); 2609 2610 // move base to the new variable 2611 accessChain.base = lValue; 2612 accessChain.isRValue = false; 2613 2614 // load through the access chain 2615 id = createLoad(collapseAccessChain()); 2616 } 2617 setPrecision(id, precision); 2618 } else 2619 id = accessChain.base; // no precision, it was set when this was defined 2620 } else { 2621 transferAccessChainSwizzle(true); 2622 2623 // take LSB of alignment 2624 alignment = alignment & ~(alignment & (alignment-1)); 2625 if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) { 2626 memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); 2627 } 2628 2629 // load through the access chain 2630 id = createLoad(collapseAccessChain(), memoryAccess, scope, alignment); 2631 setPrecision(id, precision); 2632 addDecoration(id, nonUniform); 2633 } 2634 2635 // Done, unless there are swizzles to do 2636 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) 2637 return id; 2638 2639 // Do remaining swizzling 2640 2641 // Do the basic swizzle 2642 if (accessChain.swizzle.size() > 0) { 2643 Id swizzledType = getScalarTypeId(getTypeId(id)); 2644 if (accessChain.swizzle.size() > 1) 2645 swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); 2646 id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); 2647 } 2648 2649 // Do the dynamic component 2650 if (accessChain.component != NoResult) 2651 id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); 2652 2653 addDecoration(id, nonUniform); 2654 return id; 2655 } 2656 2657 Id Builder::accessChainGetLValue() 2658 { 2659 assert(accessChain.isRValue == false); 2660 2661 transferAccessChainSwizzle(true); 2662 Id lvalue = collapseAccessChain(); 2663 2664 // If swizzle exists, it is out-of-order or not full, we must load the target vector, 2665 // extract and insert elements to perform writeMask and/or swizzle. This does not 2666 // go with getting a direct l-value pointer. 2667 assert(accessChain.swizzle.size() == 0); 2668 assert(accessChain.component == NoResult); 2669 2670 return lvalue; 2671 } 2672 2673 // comment in header 2674 Id Builder::accessChainGetInferredType() 2675 { 2676 // anything to operate on? 2677 if (accessChain.base == NoResult) 2678 return NoType; 2679 Id type = getTypeId(accessChain.base); 2680 2681 // do initial dereference 2682 if (! accessChain.isRValue) 2683 type = getContainedTypeId(type); 2684 2685 // dereference each index 2686 for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { 2687 if (isStructType(type)) 2688 type = getContainedTypeId(type, getConstantScalar(*it)); 2689 else 2690 type = getContainedTypeId(type); 2691 } 2692 2693 // dereference swizzle 2694 if (accessChain.swizzle.size() == 1) 2695 type = getContainedTypeId(type); 2696 else if (accessChain.swizzle.size() > 1) 2697 type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); 2698 2699 // dereference component selection 2700 if (accessChain.component) 2701 type = getContainedTypeId(type); 2702 2703 return type; 2704 } 2705 2706 void Builder::dump(std::vector<unsigned int>& out) const 2707 { 2708 // Header, before first instructions: 2709 out.push_back(MagicNumber); 2710 out.push_back(spvVersion); 2711 out.push_back(builderNumber); 2712 out.push_back(uniqueId + 1); 2713 out.push_back(0); 2714 2715 // Capabilities 2716 for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { 2717 Instruction capInst(0, 0, OpCapability); 2718 capInst.addImmediateOperand(*it); 2719 capInst.dump(out); 2720 } 2721 2722 for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { 2723 Instruction extInst(0, 0, OpExtension); 2724 extInst.addStringOperand(it->c_str()); 2725 extInst.dump(out); 2726 } 2727 2728 dumpInstructions(out, imports); 2729 Instruction memInst(0, 0, OpMemoryModel); 2730 memInst.addImmediateOperand(addressModel); 2731 memInst.addImmediateOperand(memoryModel); 2732 memInst.dump(out); 2733 2734 // Instructions saved up while building: 2735 dumpInstructions(out, entryPoints); 2736 dumpInstructions(out, executionModes); 2737 2738 // Debug instructions 2739 dumpInstructions(out, strings); 2740 dumpSourceInstructions(out); 2741 for (int e = 0; e < (int)sourceExtensions.size(); ++e) { 2742 Instruction sourceExtInst(0, 0, OpSourceExtension); 2743 sourceExtInst.addStringOperand(sourceExtensions[e]); 2744 sourceExtInst.dump(out); 2745 } 2746 dumpInstructions(out, names); 2747 dumpModuleProcesses(out); 2748 2749 // Annotation instructions 2750 dumpInstructions(out, decorations); 2751 2752 dumpInstructions(out, constantsTypesGlobals); 2753 dumpInstructions(out, externals); 2754 2755 // The functions 2756 module.dump(out); 2757 } 2758 2759 // 2760 // Protected methods. 2761 // 2762 2763 // Turn the described access chain in 'accessChain' into an instruction(s) 2764 // computing its address. This *cannot* include complex swizzles, which must 2765 // be handled after this is called. 2766 // 2767 // Can generate code. 2768 Id Builder::collapseAccessChain() 2769 { 2770 assert(accessChain.isRValue == false); 2771 2772 // did we already emit an access chain for this? 2773 if (accessChain.instr != NoResult) 2774 return accessChain.instr; 2775 2776 // If we have a dynamic component, we can still transfer 2777 // that into a final operand to the access chain. We need to remap the 2778 // dynamic component through the swizzle to get a new dynamic component to 2779 // update. 2780 // 2781 // This was not done in transferAccessChainSwizzle() because it might 2782 // generate code. 2783 remapDynamicSwizzle(); 2784 if (accessChain.component != NoResult) { 2785 // transfer the dynamic component to the access chain 2786 accessChain.indexChain.push_back(accessChain.component); 2787 accessChain.component = NoResult; 2788 } 2789 2790 // note that non-trivial swizzling is left pending 2791 2792 // do we have an access chain? 2793 if (accessChain.indexChain.size() == 0) 2794 return accessChain.base; 2795 2796 // emit the access chain 2797 StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); 2798 accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); 2799 2800 return accessChain.instr; 2801 } 2802 2803 // For a dynamic component selection of a swizzle. 2804 // 2805 // Turn the swizzle and dynamic component into just a dynamic component. 2806 // 2807 // Generates code. 2808 void Builder::remapDynamicSwizzle() 2809 { 2810 // do we have a swizzle to remap a dynamic component through? 2811 if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { 2812 // build a vector of the swizzle for the component to map into 2813 std::vector<Id> components; 2814 for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) 2815 components.push_back(makeUintConstant(accessChain.swizzle[c])); 2816 Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); 2817 Id map = makeCompositeConstant(mapType, components); 2818 2819 // use it 2820 accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); 2821 accessChain.swizzle.clear(); 2822 } 2823 } 2824 2825 // clear out swizzle if it is redundant, that is reselecting the same components 2826 // that would be present without the swizzle. 2827 void Builder::simplifyAccessChainSwizzle() 2828 { 2829 // If the swizzle has fewer components than the vector, it is subsetting, and must stay 2830 // to preserve that fact. 2831 if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size()) 2832 return; 2833 2834 // if components are out of order, it is a swizzle 2835 for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { 2836 if (i != accessChain.swizzle[i]) 2837 return; 2838 } 2839 2840 // otherwise, there is no need to track this swizzle 2841 accessChain.swizzle.clear(); 2842 if (accessChain.component == NoResult) 2843 accessChain.preSwizzleBaseType = NoType; 2844 } 2845 2846 // To the extent any swizzling can become part of the chain 2847 // of accesses instead of a post operation, make it so. 2848 // If 'dynamic' is true, include transferring the dynamic component, 2849 // otherwise, leave it pending. 2850 // 2851 // Does not generate code. just updates the access chain. 2852 void Builder::transferAccessChainSwizzle(bool dynamic) 2853 { 2854 // non existent? 2855 if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) 2856 return; 2857 2858 // too complex? 2859 // (this requires either a swizzle, or generating code for a dynamic component) 2860 if (accessChain.swizzle.size() > 1) 2861 return; 2862 2863 // single component, either in the swizzle and/or dynamic component 2864 if (accessChain.swizzle.size() == 1) { 2865 assert(accessChain.component == NoResult); 2866 // handle static component selection 2867 accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); 2868 accessChain.swizzle.clear(); 2869 accessChain.preSwizzleBaseType = NoType; 2870 } else if (dynamic && accessChain.component != NoResult) { 2871 assert(accessChain.swizzle.size() == 0); 2872 // handle dynamic component 2873 accessChain.indexChain.push_back(accessChain.component); 2874 accessChain.preSwizzleBaseType = NoType; 2875 accessChain.component = NoResult; 2876 } 2877 } 2878 2879 // Utility method for creating a new block and setting the insert point to 2880 // be in it. This is useful for flow-control operations that need a "dummy" 2881 // block proceeding them (e.g. instructions after a discard, etc). 2882 void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) 2883 { 2884 Block* block = new Block(getUniqueId(), buildPoint->getParent()); 2885 block->setUnreachable(); 2886 buildPoint->getParent().addBlock(block); 2887 setBuildPoint(block); 2888 2889 // if (name) 2890 // addName(block->getId(), name); 2891 } 2892 2893 // Comments in header 2894 void Builder::createBranch(Block* block) 2895 { 2896 Instruction* branch = new Instruction(OpBranch); 2897 branch->addIdOperand(block->getId()); 2898 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch)); 2899 block->addPredecessor(buildPoint); 2900 } 2901 2902 void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) 2903 { 2904 Instruction* merge = new Instruction(OpSelectionMerge); 2905 merge->addIdOperand(mergeBlock->getId()); 2906 merge->addImmediateOperand(control); 2907 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge)); 2908 } 2909 2910 void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, 2911 unsigned int dependencyLength) 2912 { 2913 Instruction* merge = new Instruction(OpLoopMerge); 2914 merge->addIdOperand(mergeBlock->getId()); 2915 merge->addIdOperand(continueBlock->getId()); 2916 merge->addImmediateOperand(control); 2917 if ((control & LoopControlDependencyLengthMask) != 0) 2918 merge->addImmediateOperand(dependencyLength); 2919 buildPoint->addInstruction(std::unique_ptr<Instruction>(merge)); 2920 } 2921 2922 void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) 2923 { 2924 Instruction* branch = new Instruction(OpBranchConditional); 2925 branch->addIdOperand(condition); 2926 branch->addIdOperand(thenBlock->getId()); 2927 branch->addIdOperand(elseBlock->getId()); 2928 buildPoint->addInstruction(std::unique_ptr<Instruction>(branch)); 2929 thenBlock->addPredecessor(buildPoint); 2930 elseBlock->addPredecessor(buildPoint); 2931 } 2932 2933 // OpSource 2934 // [OpSourceContinued] 2935 // ... 2936 void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, 2937 std::vector<unsigned int>& out) const 2938 { 2939 const int maxWordCount = 0xFFFF; 2940 const int opSourceWordCount = 4; 2941 const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; 2942 2943 if (source != SourceLanguageUnknown) { 2944 // OpSource Language Version File Source 2945 Instruction sourceInst(NoResult, NoType, OpSource); 2946 sourceInst.addImmediateOperand(source); 2947 sourceInst.addImmediateOperand(sourceVersion); 2948 // File operand 2949 if (fileId != NoResult) { 2950 sourceInst.addIdOperand(fileId); 2951 // Source operand 2952 if (text.size() > 0) { 2953 int nextByte = 0; 2954 std::string subString; 2955 while ((int)text.size() - nextByte > 0) { 2956 subString = text.substr(nextByte, nonNullBytesPerInstruction); 2957 if (nextByte == 0) { 2958 // OpSource 2959 sourceInst.addStringOperand(subString.c_str()); 2960 sourceInst.dump(out); 2961 } else { 2962 // OpSourcContinued 2963 Instruction sourceContinuedInst(OpSourceContinued); 2964 sourceContinuedInst.addStringOperand(subString.c_str()); 2965 sourceContinuedInst.dump(out); 2966 } 2967 nextByte += nonNullBytesPerInstruction; 2968 } 2969 } else 2970 sourceInst.dump(out); 2971 } else 2972 sourceInst.dump(out); 2973 } 2974 } 2975 2976 // Dump an OpSource[Continued] sequence for the source and every include file 2977 void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const 2978 { 2979 dumpSourceInstructions(sourceFileStringId, sourceText, out); 2980 for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) 2981 dumpSourceInstructions(iItr->first, *iItr->second, out); 2982 } 2983 2984 void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const 2985 { 2986 for (int i = 0; i < (int)instructions.size(); ++i) { 2987 instructions[i]->dump(out); 2988 } 2989 } 2990 2991 void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const 2992 { 2993 for (int i = 0; i < (int)moduleProcesses.size(); ++i) { 2994 Instruction moduleProcessed(OpModuleProcessed); 2995 moduleProcessed.addStringOperand(moduleProcesses[i]); 2996 moduleProcessed.dump(out); 2997 } 2998 } 2999 3000 }; // end spv namespace 3001