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