yum-mirror/glslang
git clone https://git.yummers.dev/yum-mirror/glslang
c97e126b
main
1// 2// Copyright (C) 2014-2015 LunarG, Inc. 3// Copyright (C) 2015-2018 Google, Inc. 4// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. 5// 6// All rights reserved. 7// 8// Redistribution and use in source and binary forms, with or without 9// modification, are permitted provided that the following conditions 10// are met: 11// 12// Redistributions of source code must retain the above copyright 13// notice, this list of conditions and the following disclaimer. 14// 15// Redistributions in binary form must reproduce the above 16// copyright notice, this list of conditions and the following 17// disclaimer in the documentation and/or other materials provided 18// with the distribution. 19// 20// Neither the name of 3Dlabs Inc. Ltd. nor the names of its 21// contributors may be used to endorse or promote products derived 22// from this software without specific prior written permission. 23// 24// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35// POSSIBILITY OF SUCH DAMAGE. 36 37// 38// Helper for making SPIR-V IR. Generally, this is documented in the header 39// SpvBuilder.h. 40// 41 42#include <cassert> 43#include <cstdlib> 44 45#include <unordered_set> 46#include <algorithm> 47 48#include "SpvBuilder.h" 49#include "spvUtil.h" 50#include "hex_float.h" 51 52#ifndef _WIN32 53#include <cstdio> 54#endif 55 56namespace spv { 57 58Builder ::Builder (unsigned int spvVersion ,unsigned int magicNumber ,SpvBuildLogger * buildLogger ) : 59spvVersion (spvVersion ), 60sourceLang (SourceLanguage ::Unknown ), 61sourceVersion (0 ), 62addressModel (AddressingModel ::Logical ), 63memoryModel (MemoryModel ::GLSL450 ), 64builderNumber (magicNumber ), 65buildPoint (nullptr ), 66uniqueId (0 ), 67entryPointFunction (nullptr ), 68generatingOpCodeForSpecConst (false), 69descHeapByteArrayType (NoResult ), 70logger (buildLogger ) 71{ 72clearAccessChain (); 73} 74 75Builder ::~Builder () 76{ 77} 78 79Id Builder ::import (const char * name ) 80{ 81Instruction * import = new Instruction (getUniqueId (),NoType ,Op ::OpExtInstImport ); 82import -> addStringOperand (name ); 83module .mapInstruction (import ); 84 85imports .push_back (std::unique_ptr < Instruction > (import )); 86return import -> getResultId (); 87} 88 89// For creating new groupedTypes (will return old type if the requested one was already made). 90Id Builder ::makeVoidType () 91{ 92Instruction * type ; 93if (groupedTypes [enumCast (Op ::OpTypeVoid )].size ()== 0 ) { 94Id typeId = getUniqueId (); 95type = new Instruction (typeId ,NoType ,Op ::OpTypeVoid ); 96groupedTypes [enumCast (Op ::OpTypeVoid )].push_back (type ); 97constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 98module .mapInstruction (type ); 99// Core OpTypeVoid used for debug void type 100if (emitNonSemanticShaderDebugInfo ) 101debugTypeIdLookup [typeId ]= typeId ; 102 }else 103type = groupedTypes [enumCast (Op ::OpTypeVoid )].back (); 104 105return type -> getResultId (); 106} 107 108Id Builder ::makeBoolType () 109{ 110Instruction * type ; 111if (groupedTypes [enumCast (Op ::OpTypeBool )].size ()== 0 ) { 112type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeBool ); 113groupedTypes [enumCast (Op ::OpTypeBool )].push_back (type ); 114constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 115module .mapInstruction (type ); 116 117if (emitNonSemanticShaderDebugInfo ) { 118auto const debugResultId = makeBoolDebugType (32 ); 119debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 120 } 121 122 }else 123type = groupedTypes [enumCast (Op ::OpTypeBool )].back (); 124 125 126return type -> getResultId (); 127} 128 129Id Builder ::makeSamplerType (const char * debugName ) 130{ 131Instruction * type ; 132if (groupedTypes [enumCast (Op ::OpTypeSampler )].size ()== 0 ) { 133type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeSampler ); 134groupedTypes [enumCast (Op ::OpTypeSampler )].push_back (type ); 135constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 136module .mapInstruction (type ); 137 }else 138type = groupedTypes [enumCast (Op ::OpTypeSampler )].back (); 139 140if (emitNonSemanticShaderDebugInfo ) 141 { 142auto const debugResultId = makeOpaqueDebugType (debugName ); 143debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 144 } 145 146return type -> getResultId (); 147} 148 149Id Builder ::makePointer (StorageClass storageClass ,Id pointee ) 150{ 151// try to find it 152Instruction * type ; 153for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypePointer )].size ();++ t ) { 154type = groupedTypes [enumCast (Op ::OpTypePointer )][t ]; 155if (type -> getImmediateOperand (0 )== (unsigned )storageClass && 156type -> getIdOperand (1 )== pointee ) 157return type -> getResultId (); 158 } 159 160// not found, make it 161type = new Instruction (getUniqueId (),NoType ,Op ::OpTypePointer ); 162type -> reserveOperands (2 ); 163type -> addImmediateOperand (storageClass ); 164type -> addIdOperand (pointee ); 165groupedTypes [enumCast (Op ::OpTypePointer )].push_back (type ); 166constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 167module .mapInstruction (type ); 168 169if (emitNonSemanticShaderDebugInfo ) { 170const Id debugResultId = makePointerDebugType (storageClass ,pointee ); 171debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 172 } 173 174return type -> getResultId (); 175} 176 177Id Builder ::makeForwardPointer (StorageClass storageClass ) 178{ 179// Caching/uniquifying doesn't work here, because we don't know the 180// pointee type and there can be multiple forward pointers of the same 181// storage type. Somebody higher up in the stack must keep track. 182Instruction * type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeForwardPointer ); 183type -> addImmediateOperand (storageClass ); 184constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 185module .mapInstruction (type ); 186 187if (emitNonSemanticShaderDebugInfo ) { 188const Id debugResultId = makeForwardPointerDebugType (storageClass ); 189debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 190 } 191return type -> getResultId (); 192} 193 194Id Builder ::makeUntypedPointer (StorageClass storageClass ,bool setBufferPointer ) 195{ 196// try to find it 197Instruction * type ; 198// both typeBufferEXT and UntypedPointer only contains storage class info. 199 spv::Op typeOp = setBufferPointer ?Op ::OpTypeBufferEXT :Op ::OpTypeUntypedPointerKHR ; 200for (int t = 0 ;t < (int )groupedTypes [enumCast (typeOp )].size ();++ t ) { 201type = groupedTypes [enumCast (typeOp )][t ]; 202if (type -> getImmediateOperand (0 )== (unsigned )storageClass ) 203return type -> getResultId (); 204 } 205 206// not found, make it 207type = new Instruction (getUniqueId (),NoType ,typeOp ); 208type -> addImmediateOperand (storageClass ); 209groupedTypes [enumCast (typeOp )].push_back (type ); 210constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 211module .mapInstruction (type ); 212return type -> getResultId (); 213} 214 215Id Builder ::makePointerFromForwardPointer (StorageClass storageClass ,Id forwardPointerType ,Id pointee ) 216{ 217// try to find it 218Instruction * type ; 219for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypePointer )].size ();++ t ) { 220type = groupedTypes [enumCast (Op ::OpTypePointer )][t ]; 221if (type -> getImmediateOperand (0 )== (unsigned )storageClass && 222type -> getIdOperand (1 )== pointee ) 223return type -> getResultId (); 224 } 225 226type = new Instruction (forwardPointerType ,NoType ,Op ::OpTypePointer ); 227type -> reserveOperands (2 ); 228type -> addImmediateOperand (storageClass ); 229type -> addIdOperand (pointee ); 230groupedTypes [enumCast (Op ::OpTypePointer )].push_back (type ); 231constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 232module .mapInstruction (type ); 233 234// If we are emitting nonsemantic debuginfo, we need to patch the debug pointer type 235// that was emitted alongside the forward pointer, now that we have a pointee debug 236// type for it to point to. 237if (emitNonSemanticShaderDebugInfo ) { 238Instruction * debugForwardPointer = module .getInstruction (getDebugType (forwardPointerType )); 239assert (getDebugType (pointee )); 240debugForwardPointer -> setIdOperand (2 ,getDebugType (pointee )); 241 } 242 243return type -> getResultId (); 244} 245 246Id Builder ::makeIntegerType (int width ,bool hasSign ) 247{ 248// try to find it 249Instruction * type ; 250for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeInt )].size ();++ t ) { 251type = groupedTypes [enumCast (Op ::OpTypeInt )][t ]; 252if (type -> getImmediateOperand (0 )== (unsigned )width && 253type -> getImmediateOperand (1 )== (hasSign ?1u :0u )) 254return type -> getResultId (); 255 } 256 257// not found, make it 258type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeInt ); 259type -> reserveOperands (2 ); 260type -> addImmediateOperand (width ); 261type -> addImmediateOperand (hasSign ?1 :0 ); 262groupedTypes [enumCast (Op ::OpTypeInt )].push_back (type ); 263constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 264module .mapInstruction (type ); 265 266// deal with capabilities 267switch (width ) { 268case 8 : 269case 16 : 270// these are currently handled by storage-type declarations and post processing 271break ; 272case 64 : 273addCapability (Capability ::Int64 ); 274break ; 275default : 276break ; 277 } 278 279if (emitNonSemanticShaderDebugInfo ) 280 { 281auto const debugResultId = makeIntegerDebugType (width ,hasSign ); 282debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 283 } 284 285return type -> getResultId (); 286} 287 288Id Builder ::makeFloatType (int width ) 289{ 290// try to find it 291Instruction * type ; 292for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeFloat )].size ();++ t ) { 293type = groupedTypes [enumCast (Op ::OpTypeFloat )][t ]; 294if (type -> getNumOperands ()!= 1 ) { 295continue ; 296 } 297if (type -> getImmediateOperand (0 )== (unsigned )width ) 298return type -> getResultId (); 299 } 300 301// not found, make it 302type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeFloat ); 303type -> addImmediateOperand (width ); 304groupedTypes [enumCast (Op ::OpTypeFloat )].push_back (type ); 305constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 306module .mapInstruction (type ); 307 308// deal with capabilities 309switch (width ) { 310case 16 : 311// currently handled by storage-type declarations and post processing 312break ; 313case 64 : 314addCapability (Capability ::Float64 ); 315break ; 316default : 317break ; 318 } 319 320if (emitNonSemanticShaderDebugInfo ) 321 { 322auto const debugResultId = makeFloatDebugType (width ); 323debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 324 } 325 326return type -> getResultId (); 327} 328 329Id Builder ::makeBFloat16Type () 330{ 331// try to find it 332Instruction * type ; 333for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeFloat )].size ();++ t ) { 334type = groupedTypes [enumCast (Op ::OpTypeFloat )][t ]; 335if (type -> getNumOperands ()!= 2 ) { 336continue ; 337 } 338if (type -> getImmediateOperand (0 )== (unsigned )16 && 339type -> getImmediateOperand (1 )== FPEncoding ::BFloat16KHR ) 340return type -> getResultId (); 341 } 342 343// not found, make it 344type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeFloat ); 345type -> addImmediateOperand (16 ); 346type -> addImmediateOperand (FPEncoding ::BFloat16KHR ); 347groupedTypes [enumCast (Op ::OpTypeFloat )].push_back (type ); 348constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 349module .mapInstruction (type ); 350 351addExtension (spv::E_SPV_KHR_bfloat16 ); 352addCapability (Capability ::BFloat16TypeKHR ); 353 354if (emitNonSemanticShaderDebugInfo ) { 355auto const debugResultId = makeFloatDebugType (16 ,makeUintConstant ((unsigned )FPEncoding ::BFloat16KHR )); 356debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 357 } 358 359return type -> getResultId (); 360} 361 362Id Builder ::makeFloatE5M2Type () 363{ 364// try to find it 365Instruction * type ; 366for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeFloat )].size ();++ t ) { 367type = groupedTypes [enumCast (Op ::OpTypeFloat )][t ]; 368if (type -> getNumOperands ()!= 2 ) { 369continue ; 370 } 371if (type -> getImmediateOperand (0 )== (unsigned )8 && 372type -> getImmediateOperand (1 )== FPEncoding ::Float8E5M2EXT ) 373return type -> getResultId (); 374 } 375 376// not found, make it 377type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeFloat ); 378type -> addImmediateOperand (8 ); 379type -> addImmediateOperand (FPEncoding ::Float8E5M2EXT ); 380groupedTypes [enumCast (Op ::OpTypeFloat )].push_back (type ); 381constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 382module .mapInstruction (type ); 383 384addExtension (spv::E_SPV_EXT_float8 ); 385addCapability (Capability ::Float8EXT ); 386 387if (emitNonSemanticShaderDebugInfo ) { 388auto const debugResultId = makeFloatDebugType (8 ,makeUintConstant ((unsigned )FPEncoding ::Float8E5M2EXT )); 389debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 390 } 391 392return type -> getResultId (); 393} 394 395Id Builder ::makeFloatE4M3Type () 396{ 397// try to find it 398Instruction * type ; 399for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeFloat )].size ();++ t ) { 400type = groupedTypes [enumCast (Op ::OpTypeFloat )][t ]; 401if (type -> getNumOperands ()!= 2 ) { 402continue ; 403 } 404if (type -> getImmediateOperand (0 )== (unsigned )8 && 405type -> getImmediateOperand (1 )== FPEncoding ::Float8E4M3EXT ) 406return type -> getResultId (); 407 } 408 409// not found, make it 410type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeFloat ); 411type -> addImmediateOperand (8 ); 412type -> addImmediateOperand (FPEncoding ::Float8E4M3EXT ); 413groupedTypes [enumCast (Op ::OpTypeFloat )].push_back (type ); 414constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 415module .mapInstruction (type ); 416 417addExtension (spv::E_SPV_EXT_float8 ); 418addCapability (Capability ::Float8EXT ); 419 420if (emitNonSemanticShaderDebugInfo ) { 421auto const debugResultId = makeFloatDebugType (8 ,makeUintConstant ((unsigned )FPEncoding ::Float8E4M3EXT )); 422debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 423 } 424 425return type -> getResultId (); 426} 427 428Id Builder ::makeFloatOcpMicroscalingType (uint32_t width ,FPEncoding encoding ,Capability cap ) 429{ 430// try to find it 431Instruction * type ; 432for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeFloat )].size ();++ t ) { 433type = groupedTypes [enumCast (Op ::OpTypeFloat )][t ]; 434if (type -> getNumOperands ()!= 2 ) { 435continue ; 436 } 437if (type -> getImmediateOperand (0 )== (unsigned )width && 438type -> getImmediateOperand (1 )== encoding ) 439return type -> getResultId (); 440 } 441 442// not found, make it 443type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeFloat ); 444type -> addImmediateOperand (width ); 445type -> addImmediateOperand (encoding ); 446groupedTypes [enumCast (Op ::OpTypeFloat )].push_back (type ); 447constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 448module .mapInstruction (type ); 449 450addExtension (spv::E_SPV_EXT_ocp_microscaling_types ); 451addCapability (cap ); 452 453if (emitNonSemanticShaderDebugInfo ) 454 { 455auto const debugResultId = makeFloatDebugType (width ,makeUintConstant ((unsigned )encoding )); 456debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 457 } 458 459return type -> getResultId (); 460} 461 462Id Builder ::makeFloatE2M1Type () 463{ 464return makeFloatOcpMicroscalingType (4 ,FPEncoding ::Float4E2M1EXT ,Capability ::Float4EXT ); 465} 466 467Id Builder ::makeFloatE3M2Type () 468{ 469return makeFloatOcpMicroscalingType (6 ,FPEncoding ::Float6E3M2EXT ,Capability ::Float6EXT ); 470} 471 472Id Builder ::makeFloatE2M3Type () 473{ 474return makeFloatOcpMicroscalingType (6 ,FPEncoding ::Float6E2M3EXT ,Capability ::Float6EXT ); 475} 476 477Id Builder ::makeFloatUE8M0Type () 478{ 479return makeFloatOcpMicroscalingType (8 ,FPEncoding ::Float8UnsignedE8M0EXT ,Capability ::Float8UnsignedE8M0EXT ); 480} 481 482Id Builder ::makeFloatMXINT8Type () 483{ 484return makeFloatOcpMicroscalingType (8 ,FPEncoding ::MXInt8EXT ,Capability ::MXInt8EXT ); 485} 486 487// Make a struct without checking for duplication. 488// See makeStructResultType() for non-decorated structs 489// needed as the result of some instructions, which does 490// check for duplicates. 491// For compiler-generated structs, debug info is ignored. 492Id Builder ::makeStructType (const std::vector < Id >& members ,const std::vector < spv::StructMemberDebugInfo >& memberDebugInfo , 493const char * name ,bool const compilerGenerated ) 494{ 495// Don't look for previous one, because in the general case, 496// structs can be duplicated except for decorations. 497 498// not found, make it 499Instruction * type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeStruct ); 500for (int op = 0 ;op < (int )members .size ();++ op ) 501type -> addIdOperand (members [op ]); 502groupedTypes [enumCast (Op ::OpTypeStruct )].push_back (type ); 503constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 504module .mapInstruction (type ); 505addName (type -> getResultId (),name ); 506 507if (emitNonSemanticShaderDebugInfo && !compilerGenerated ) { 508assert (members .size ()== memberDebugInfo .size ()); 509auto const debugResultId = 510makeCompositeDebugType (members ,memberDebugInfo ,name ,NonSemanticShaderDebugInfoStructure ); 511debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 512 } 513 514return type -> getResultId (); 515} 516 517// Make a struct for the simple results of several instructions, 518// checking for duplication. 519Id Builder ::makeStructResultType (Id type0 ,Id type1 ) 520{ 521// try to find it 522Instruction * type ; 523for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeStruct )].size ();++ t ) { 524type = groupedTypes [enumCast (Op ::OpTypeStruct )][t ]; 525if (type -> getNumOperands ()!= 2 ) 526continue ; 527if (type -> getIdOperand (0 )!= type0 || 528type -> getIdOperand (1 )!= type1 ) 529continue ; 530return type -> getResultId (); 531 } 532 533// not found, make it 534 std::vector < spv::Id > members ; 535members .push_back (type0 ); 536members .push_back (type1 ); 537 538return makeStructType (members , {},"ResType" ); 539} 540 541Id Builder ::makeVectorType (Id component ,int size ) 542{ 543// try to find it 544Instruction * type ; 545for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeVector )].size ();++ t ) { 546type = groupedTypes [enumCast (Op ::OpTypeVector )][t ]; 547if (type -> getIdOperand (0 )== component && 548type -> getImmediateOperand (1 )== (unsigned )size ) 549return type -> getResultId (); 550 } 551 552// not found, make it 553type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeVector ); 554type -> reserveOperands (2 ); 555type -> addIdOperand (component ); 556type -> addImmediateOperand (size ); 557groupedTypes [enumCast (Op ::OpTypeVector )].push_back (type ); 558constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 559module .mapInstruction (type ); 560 561if (emitNonSemanticShaderDebugInfo ) 562 { 563auto const debugResultId = makeVectorDebugType (component ,size ); 564debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 565 } 566 567return type -> getResultId (); 568} 569 570Id Builder ::makeMatrixType (Id component ,int cols ,int rows ) 571{ 572assert (cols <=maxMatrixSize && rows <=maxMatrixSize ); 573 574Id column = makeVectorType (component ,rows ); 575 576// try to find it 577Instruction * type ; 578for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeMatrix )].size ();++ t ) { 579type = groupedTypes [enumCast (Op ::OpTypeMatrix )][t ]; 580if (type -> getIdOperand (0 )== column && 581type -> getImmediateOperand (1 )== (unsigned )cols ) 582return type -> getResultId (); 583 } 584 585// not found, make it 586type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeMatrix ); 587type -> reserveOperands (2 ); 588type -> addIdOperand (column ); 589type -> addImmediateOperand (cols ); 590groupedTypes [enumCast (Op ::OpTypeMatrix )].push_back (type ); 591constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 592module .mapInstruction (type ); 593 594if (emitNonSemanticShaderDebugInfo ) 595 { 596auto const debugResultId = makeMatrixDebugType (column ,cols ); 597debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 598 } 599 600return type -> getResultId (); 601} 602 603Id Builder ::makeCooperativeMatrixTypeKHR (Id component ,Id scope ,Id rows ,Id cols ,Id use ) 604{ 605// try to find it 606Instruction * type ; 607for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeCooperativeMatrixKHR )].size ();++ t ) { 608type = groupedTypes [enumCast (Op ::OpTypeCooperativeMatrixKHR )][t ]; 609if (type -> getIdOperand (0 )== component && 610type -> getIdOperand (1 )== scope && 611type -> getIdOperand (2 )== rows && 612type -> getIdOperand (3 )== cols && 613type -> getIdOperand (4 )== use ) 614return type -> getResultId (); 615 } 616 617// not found, make it 618type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeCooperativeMatrixKHR ); 619type -> reserveOperands (5 ); 620type -> addIdOperand (component ); 621type -> addIdOperand (scope ); 622type -> addIdOperand (rows ); 623type -> addIdOperand (cols ); 624type -> addIdOperand (use ); 625groupedTypes [enumCast (Op ::OpTypeCooperativeMatrixKHR )].push_back (type ); 626constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 627module .mapInstruction (type ); 628 629if (emitNonSemanticShaderDebugInfo ) { 630auto const debugResultId = makeCooperativeMatrixDebugTypeKHR (component ,scope ,rows ,cols ,use ); 631debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 632 } 633 634return type -> getResultId (); 635} 636 637Id Builder ::makeCooperativeMatrixTypeNV (Id component ,Id scope ,Id rows ,Id cols ) 638{ 639// try to find it 640Instruction * type ; 641for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeCooperativeMatrixNV )].size ();++ t ) { 642type = groupedTypes [enumCast (Op ::OpTypeCooperativeMatrixNV )][t ]; 643if (type -> getIdOperand (0 )== component && type -> getIdOperand (1 )== scope && type -> getIdOperand (2 )== rows && 644type -> getIdOperand (3 )== cols ) 645return type -> getResultId (); 646 } 647 648// not found, make it 649type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeCooperativeMatrixNV ); 650type -> reserveOperands (4 ); 651type -> addIdOperand (component ); 652type -> addIdOperand (scope ); 653type -> addIdOperand (rows ); 654type -> addIdOperand (cols ); 655groupedTypes [enumCast (Op ::OpTypeCooperativeMatrixNV )].push_back (type ); 656constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 657module .mapInstruction (type ); 658 659return type -> getResultId (); 660} 661 662Id Builder ::makeCooperativeMatrixTypeWithSameShape (Id component ,Id otherType ) 663{ 664Instruction * instr = module .getInstruction (otherType ); 665if (instr -> getOpCode ()== Op ::OpTypeCooperativeMatrixNV ) { 666return makeCooperativeMatrixTypeNV (component ,instr -> getIdOperand (1 ),instr -> getIdOperand (2 ),instr -> getIdOperand (3 )); 667 }else { 668assert (instr -> getOpCode ()== Op ::OpTypeCooperativeMatrixKHR ); 669return makeCooperativeMatrixTypeKHR (component ,instr -> getIdOperand (1 ),instr -> getIdOperand (2 ),instr -> getIdOperand (3 ),instr -> getIdOperand (4 )); 670 } 671} 672 673Id Builder ::makeCooperativeVectorTypeNV (Id componentType ,Id components ) 674{ 675// try to find it 676Instruction * type ; 677for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeCooperativeVectorNV )].size ();++ t ) { 678type = groupedTypes [enumCast (Op ::OpTypeCooperativeVectorNV )][t ]; 679if (type -> getIdOperand (0 )== componentType && 680type -> getIdOperand (1 )== components ) 681return type -> getResultId (); 682 } 683 684// not found, make it 685type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeCooperativeVectorNV ); 686type -> addIdOperand (componentType ); 687type -> addIdOperand (components ); 688groupedTypes [enumCast (Op ::OpTypeCooperativeVectorNV )].push_back (type ); 689constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 690module .mapInstruction (type ); 691 692if (emitNonSemanticShaderDebugInfo ) { 693auto const debugResultId = makeVectorIdDebugType (componentType ,components ); 694debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 695 } 696 697return type -> getResultId (); 698} 699 700Id Builder ::makeTensorTypeARM (Id elementType ,Id rank ) 701{ 702// See if an OpTypeTensorARM with same element type and rank already exists. 703for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeTensorARM )].size ();++ t ) { 704const Instruction * type = groupedTypes [enumCast (Op ::OpTypeTensorARM )][t ]; 705if (type -> getIdOperand (0 )== elementType && type -> getIdOperand (1 )== rank ) 706return type -> getResultId (); 707 } 708 709// Not found, make it. 710 std::unique_ptr < Instruction > type (new Instruction (getUniqueId (),NoType ,Op ::OpTypeTensorARM )); 711type -> addIdOperand (elementType ); 712type -> addIdOperand (rank ); 713groupedTypes [enumCast (Op ::OpTypeTensorARM )].push_back (type .get ()); 714module .mapInstruction (type .get ()); 715Id resultID = type -> getResultId (); 716constantsTypesGlobals .push_back (std::move (type )); 717return resultID ; 718} 719 720Id Builder ::makeGenericType (spv::Op opcode , std::vector < spv::IdImmediate >& operands ) 721{ 722// try to find it 723Instruction * type ; 724for (int t = 0 ;t < (int )groupedTypes [enumCast (opcode )].size ();++ t ) { 725type = groupedTypes [enumCast (opcode )][t ]; 726if (static_cast < size_t > (type -> getNumOperands ())!= operands .size ()) 727continue ;// Number mismatch, find next 728 729bool match = true; 730for (int op = 0 ;match && op < (int )operands .size ();++ op ) { 731match = (operands [op ].isId ?type -> getIdOperand (op ) :type -> getImmediateOperand (op ))== operands [op ].word ; 732 } 733if (match ) 734return type -> getResultId (); 735 } 736 737// not found, make it 738type = new Instruction (getUniqueId (),NoType ,opcode ); 739type -> reserveOperands (operands .size ()); 740for (size_t op = 0 ;op < operands .size ();++ op ) { 741if (operands [op ].isId ) 742type -> addIdOperand (operands [op ].word ); 743else 744type -> addImmediateOperand (operands [op ].word ); 745 } 746groupedTypes [enumCast (opcode )].push_back (type ); 747constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 748module .mapInstruction (type ); 749 750return type -> getResultId (); 751} 752 753// TODO: performance: track arrays per stride 754// If a stride is supplied (non-zero) make an array. 755// If no stride (0), reuse previous array types. 756// 'size' is an Id of a constant or specialization constant of the array size 757Id Builder ::makeArrayType (Id element ,Id sizeId ,int stride ) 758{ 759Instruction * type ; 760if (stride == 0 ) { 761// try to find existing type 762for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeArray )].size ();++ t ) { 763type = groupedTypes [enumCast (Op ::OpTypeArray )][t ]; 764if (type -> getIdOperand (0 )== element && 765type -> getIdOperand (1 )== sizeId && 766explicitlyLaidOut .find (type -> getResultId ())== explicitlyLaidOut .end ()) 767return type -> getResultId (); 768 } 769 } 770 771// not found, make it 772type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeArray ); 773type -> reserveOperands (2 ); 774type -> addIdOperand (element ); 775type -> addIdOperand (sizeId ); 776groupedTypes [enumCast (Op ::OpTypeArray )].push_back (type ); 777constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 778module .mapInstruction (type ); 779 780if (stride != 0 ) { 781explicitlyLaidOut .insert (type -> getResultId ()); 782 } 783 784if (emitNonSemanticShaderDebugInfo ) 785 { 786auto const debugResultId = makeArrayDebugType (element ,sizeId ); 787debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 788 } 789 790return type -> getResultId (); 791} 792 793Id Builder ::makeRuntimeArray (Id element ) 794{ 795Instruction * type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeRuntimeArray ); 796type -> addIdOperand (element ); 797constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 798module .mapInstruction (type ); 799 800if (emitNonSemanticShaderDebugInfo ) 801 { 802auto const debugResultId = makeArrayDebugType (element ,makeUintConstant (0 )); 803debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 804 } 805 806return type -> getResultId (); 807} 808 809Id Builder ::makeFunctionType (Id returnType ,const std::vector < Id >& paramTypes ) 810{ 811// try to find it 812Instruction * type ; 813for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeFunction )].size ();++ t ) { 814type = groupedTypes [enumCast (Op ::OpTypeFunction )][t ]; 815if (type -> getIdOperand (0 )!= returnType || (int )paramTypes .size ()!= type -> getNumOperands ()- 1 ) 816continue ; 817bool mismatch = false; 818for (int p = 0 ;p < (int )paramTypes .size ();++ p ) { 819if (paramTypes [p ]!= type -> getIdOperand (p + 1 )) { 820mismatch = true; 821break ; 822 } 823 } 824if (!mismatch ) 825 { 826// If compiling HLSL, glslang will create a wrapper function around the entrypoint. Accordingly, a void(void) 827// function type is created for the wrapper function. However, nonsemantic shader debug information is disabled 828// while creating the HLSL wrapper. Consequently, if we encounter another void(void) function, we need to create 829// the associated debug function type if it hasn't been created yet. 830if (emitNonSemanticShaderDebugInfo && getDebugType (type -> getResultId ())== NoType ) { 831assert (sourceLang == spv::SourceLanguage ::HLSL ); 832assert (getTypeClass (returnType )== Op ::OpTypeVoid && paramTypes .size ()== 0 ); 833 834Id id = makeDebugFunctionType (returnType , {}); 835debugTypeIdLookup [type -> getResultId ()]= id ; 836 } 837return type -> getResultId (); 838 } 839 } 840 841// not found, make it 842Id typeId = getUniqueId (); 843type = new Instruction (typeId ,NoType ,Op ::OpTypeFunction ); 844type -> reserveOperands (paramTypes .size ()+ 1 ); 845type -> addIdOperand (returnType ); 846for (int p = 0 ;p < (int )paramTypes .size ();++ p ) 847type -> addIdOperand (paramTypes [p ]); 848groupedTypes [enumCast (Op ::OpTypeFunction )].push_back (type ); 849constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 850module .mapInstruction (type ); 851 852// make debug type and map it 853if (emitNonSemanticShaderDebugInfo ) { 854Id debugTypeId = makeDebugFunctionType (returnType ,paramTypes ); 855debugTypeIdLookup [typeId ]= debugTypeId ; 856 } 857 858return type -> getResultId (); 859} 860 861Id Builder ::makeDebugFunctionType (Id returnType ,const std::vector < Id >& paramTypes ) 862{ 863assert (getDebugType (returnType )!= NoType ); 864 865Id typeId = getUniqueId (); 866auto type = new Instruction (typeId ,makeVoidType (),Op ::OpExtInst ); 867type -> reserveOperands (paramTypes .size ()+ 4 ); 868type -> addIdOperand (nonSemanticShaderDebugInfo ); 869type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeFunction ); 870type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFlagIsPublic )); 871type -> addIdOperand (getDebugType (returnType )); 872for (auto const paramType :paramTypes ) { 873if (isPointerType (paramType )|| isArrayType (paramType )) { 874type -> addIdOperand (getDebugType (getContainedTypeId (paramType ))); 875 } 876else { 877type -> addIdOperand (getDebugType (paramType )); 878 } 879 } 880constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 881module .mapInstruction (type ); 882return typeId ; 883} 884 885Id Builder ::makeImageType (Id sampledType ,Dim dim ,bool depth ,bool arrayed ,bool ms ,unsigned sampled , 886ImageFormat format ,const char * debugName ) 887{ 888assert (sampled == 1 || sampled == 2 ); 889 890// try to find it 891Instruction * type ; 892for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeImage )].size ();++ t ) { 893type = groupedTypes [enumCast (Op ::OpTypeImage )][t ]; 894if (type -> getIdOperand (0 )== sampledType && 895type -> getImmediateOperand (1 )== (unsigned int )dim && 896type -> getImmediateOperand (2 )== (depth ?1u :0u )&& 897type -> getImmediateOperand (3 )== (arrayed ?1u :0u )&& 898type -> getImmediateOperand (4 )== (ms ?1u :0u )&& 899type -> getImmediateOperand (5 )== sampled && 900type -> getImmediateOperand (6 )== (unsigned int )format ) 901return type -> getResultId (); 902 } 903 904// not found, make it 905type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeImage ); 906type -> reserveOperands (7 ); 907type -> addIdOperand (sampledType ); 908type -> addImmediateOperand (dim ); 909type -> addImmediateOperand (depth ?1 :0 ); 910type -> addImmediateOperand (arrayed ?1 :0 ); 911type -> addImmediateOperand (ms ?1 :0 ); 912type -> addImmediateOperand (sampled ); 913type -> addImmediateOperand ((unsigned int )format ); 914 915groupedTypes [enumCast (Op ::OpTypeImage )].push_back (type ); 916constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 917module .mapInstruction (type ); 918 919// deal with capabilities 920switch (dim ) { 921case Dim ::Buffer : 922if (sampled == 1 ) 923addCapability (Capability ::SampledBuffer ); 924else 925addCapability (Capability ::ImageBuffer ); 926break ; 927case Dim ::Dim1D : 928if (sampled == 1 ) 929addCapability (Capability ::Sampled1D ); 930else 931addCapability (Capability ::Image1D ); 932break ; 933case Dim ::Cube : 934if (arrayed ) { 935if (sampled == 1 ) 936addCapability (Capability ::SampledCubeArray ); 937else 938addCapability (Capability ::ImageCubeArray ); 939 } 940break ; 941case Dim ::Rect : 942if (sampled == 1 ) 943addCapability (Capability ::SampledRect ); 944else 945addCapability (Capability ::ImageRect ); 946break ; 947case Dim ::SubpassData : 948addCapability (Capability ::InputAttachment ); 949break ; 950default : 951break ; 952 } 953 954if (ms ) { 955if (sampled == 2 ) { 956// Images used with subpass data are not storage 957// images, so don't require the capability for them. 958if (dim != Dim ::SubpassData ) 959addCapability (Capability ::StorageImageMultisample ); 960if (arrayed ) 961addCapability (Capability ::ImageMSArray ); 962 } 963 } 964 965if (emitNonSemanticShaderDebugInfo ) 966 { 967auto const debugResultId = makeOpaqueDebugType (debugName ); 968debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 969 } 970 971return type -> getResultId (); 972} 973 974Id Builder ::makeSampledImageType (Id imageType ,const char * debugName ) 975{ 976// try to find it 977Instruction * type ; 978for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypeSampledImage )].size ();++ t ) { 979type = groupedTypes [enumCast (Op ::OpTypeSampledImage )][t ]; 980if (type -> getIdOperand (0 )== imageType ) 981return type -> getResultId (); 982 } 983 984// not found, make it 985type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeSampledImage ); 986type -> addIdOperand (imageType ); 987 988groupedTypes [enumCast (Op ::OpTypeSampledImage )].push_back (type ); 989constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 990module .mapInstruction (type ); 991 992if (emitNonSemanticShaderDebugInfo ) 993 { 994auto const debugResultId = makeOpaqueDebugType (debugName ); 995debugTypeIdLookup [type -> getResultId ()]= debugResultId ; 996 } 997 998return type -> getResultId (); 999} 1000 1001Id Builder ::makeDebugInfoNone () 1002{ 1003if (debugInfoNone != 0 ) 1004return debugInfoNone ; 1005 1006Instruction * inst = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1007inst -> reserveOperands (2 ); 1008inst -> addIdOperand (nonSemanticShaderDebugInfo ); 1009inst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugInfoNone ); 1010 1011constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (inst )); 1012module .mapInstruction (inst ); 1013 1014debugInfoNone = inst -> getResultId (); 1015 1016return debugInfoNone ; 1017} 1018 1019Id Builder ::makeBoolDebugType (int const size ) 1020{ 1021// try to find it 1022Instruction * type ; 1023for (int t = 0 ;t < (int )groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ].size ();++ t ) { 1024type = groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ][t ]; 1025if (type -> getIdOperand (0 )== getStringId ("bool" )&& 1026type -> getIdOperand (1 )== static_cast < unsigned int > (size )&& 1027type -> getIdOperand (2 )== NonSemanticShaderDebugInfoBoolean ) 1028return type -> getResultId (); 1029 } 1030 1031type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1032type -> reserveOperands (6 ); 1033type -> addIdOperand (nonSemanticShaderDebugInfo ); 1034type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeBasic ); 1035 1036type -> addIdOperand (getStringId ("bool" ));// name id 1037type -> addIdOperand (makeUintConstant (size ));// size id 1038type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoBoolean ));// encoding id 1039type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoNone ));// flags id 1040 1041groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ].push_back (type ); 1042constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1043module .mapInstruction (type ); 1044 1045return type -> getResultId (); 1046} 1047 1048Id Builder ::makeIntegerDebugType (int const width ,bool const hasSign ) 1049{ 1050const char * typeName = nullptr ; 1051switch (width ) { 1052case 8 :typeName = hasSign ?"int8_t" :"uint8_t" ;break ; 1053case 16 :typeName = hasSign ?"int16_t" :"uint16_t" ;break ; 1054case 64 :typeName = hasSign ?"int64_t" :"uint64_t" ;break ; 1055default :typeName = hasSign ?"int" :"uint" ; 1056 } 1057auto nameId = getStringId (typeName ); 1058// try to find it 1059Instruction * type ; 1060for (int t = 0 ;t < (int )groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ].size ();++ t ) { 1061type = groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ][t ]; 1062if (type -> getIdOperand (0 )== nameId && 1063type -> getIdOperand (1 )== static_cast < unsigned int > (width )&& 1064type -> getIdOperand (2 )== (hasSign ?NonSemanticShaderDebugInfoSigned :NonSemanticShaderDebugInfoUnsigned )) 1065return type -> getResultId (); 1066 } 1067 1068// not found, make it 1069type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1070type -> reserveOperands (6 ); 1071type -> addIdOperand (nonSemanticShaderDebugInfo ); 1072type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeBasic ); 1073type -> addIdOperand (nameId );// name id 1074type -> addIdOperand (makeUintConstant (width ));// size id 1075if (hasSign == true) { 1076type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoSigned ));// encoding id 1077 }else { 1078type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoUnsigned ));// encoding id 1079 } 1080type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoNone ));// flags id 1081 1082groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ].push_back (type ); 1083constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1084module .mapInstruction (type ); 1085 1086return type -> getResultId (); 1087} 1088 1089Id Builder ::makeFloatDebugType (int const width ,Id const fpEncoding ) 1090{ 1091if (fpEncoding != NoType ) 1092requireNonSemanticShaderDebugInfoVersion (101 ); 1093// Determine the debug type name. FP-encoded variants have distinct names. 1094// Id comparison works here because makeUintConstant deduplicates: two calls 1095// with the same value return the same Id. 1096const char * typeName = nullptr ; 1097if (fpEncoding != NoType ) { 1098if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::BFloat16KHR )) 1099typeName = "bfloat16_t" ; 1100else if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::Float8E4M3EXT )) 1101typeName = "floate4m3_t" ; 1102else if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::Float8E5M2EXT )) 1103typeName = "floate5m2_t" ; 1104else if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::Float4E2M1EXT )) 1105typeName = "floate2m1_t" ; 1106else if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::Float6E3M2EXT )) 1107typeName = "floate3m2_t" ; 1108else if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::Float6E2M3EXT )) 1109typeName = "floate2m3_t" ; 1110else if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::Float8UnsignedE8M0EXT )) 1111typeName = "floatue8m0_t" ; 1112else if (fpEncoding == makeUintConstant ((unsigned )FPEncoding ::MXInt8EXT )) 1113typeName = "floatmxint8_t" ; 1114else 1115typeName = "float" ; 1116 }else { 1117switch (width ) { 1118case 16 :typeName = "float16_t" ;break ; 1119case 64 :typeName = "double" ;break ; 1120default :typeName = "float" ;break ; 1121 } 1122 } 1123auto nameId = getStringId (typeName ); 1124// try to find it 1125Instruction * type ; 1126for (int t = 0 ;t < (int )groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ].size ();++ t ) { 1127type = groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ][t ]; 1128if (type -> getIdOperand (0 )== nameId && 1129type -> getIdOperand (1 )== static_cast < unsigned int > (width )&& 1130type -> getIdOperand (2 )== NonSemanticShaderDebugInfoFloat ) { 1131if (fpEncoding == NoType ) { 1132if (type -> getNumOperands ()== 6 ) 1133return type -> getResultId (); 1134 }else { 1135if (type -> getNumOperands ()== 7 && type -> getIdOperand (6 )== fpEncoding ) 1136return type -> getResultId (); 1137 } 1138 } 1139 } 1140 1141// not found, make it 1142type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1143type -> reserveOperands (fpEncoding == NoType ?6 :7 ); 1144type -> addIdOperand (nonSemanticShaderDebugInfo ); 1145type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeBasic ); 1146type -> addIdOperand (nameId );// name id 1147type -> addIdOperand (makeUintConstant (width ));// size id 1148type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFloat ));// encoding id 1149type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoNone ));// flags id 1150if (fpEncoding != NoType ) 1151type -> addIdOperand (fpEncoding );// optional FPEncoding id (NSDI.101) 1152 1153groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeBasic ].push_back (type ); 1154constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1155module .mapInstruction (type ); 1156 1157return type -> getResultId (); 1158} 1159 1160Id Builder ::makeSequentialDebugType (Id const baseType ,Id const componentCount ,NonSemanticShaderDebugInfoInstructions const sequenceType ) 1161{ 1162assert (sequenceType == NonSemanticShaderDebugInfoDebugTypeArray || 1163sequenceType == NonSemanticShaderDebugInfoDebugTypeVector ); 1164 1165// try to find it 1166Instruction * type ; 1167for (int t = 0 ;t < (int )groupedDebugTypes [sequenceType ].size ();++ t ) { 1168type = groupedDebugTypes [sequenceType ][t ]; 1169if (type -> getIdOperand (0 )== baseType && 1170type -> getIdOperand (1 )== makeUintConstant (componentCount )) 1171return type -> getResultId (); 1172 } 1173 1174// not found, make it 1175type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1176type -> reserveOperands (4 ); 1177type -> addIdOperand (nonSemanticShaderDebugInfo ); 1178type -> addImmediateOperand (sequenceType ); 1179type -> addIdOperand (getDebugType (baseType ));// base type 1180type -> addIdOperand (componentCount );// component count 1181 1182groupedDebugTypes [sequenceType ].push_back (type ); 1183constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1184module .mapInstruction (type ); 1185 1186return type -> getResultId (); 1187} 1188 1189Id Builder ::makeArrayDebugType (Id const baseType ,Id const componentCount ) 1190{ 1191return makeSequentialDebugType (baseType ,componentCount ,NonSemanticShaderDebugInfoDebugTypeArray ); 1192} 1193 1194Id Builder ::makeVectorDebugType (Id const baseType ,int const componentCount ) 1195{ 1196return makeSequentialDebugType (baseType ,makeUintConstant (componentCount ),NonSemanticShaderDebugInfoDebugTypeVector ); 1197} 1198 1199Id Builder ::makeMatrixDebugType (Id const vectorType ,int const vectorCount ,bool columnMajor ) 1200{ 1201// try to find it 1202Instruction * type ; 1203for (int t = 0 ;t < (int )groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeMatrix ].size ();++ t ) { 1204type = groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeMatrix ][t ]; 1205if (type -> getIdOperand (0 )== vectorType && 1206type -> getIdOperand (1 )== makeUintConstant (vectorCount )) 1207return type -> getResultId (); 1208 } 1209 1210// not found, make it 1211type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1212type -> reserveOperands (5 ); 1213type -> addIdOperand (nonSemanticShaderDebugInfo ); 1214type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeMatrix ); 1215type -> addIdOperand (getDebugType (vectorType ));// vector type id 1216type -> addIdOperand (makeUintConstant (vectorCount ));// component count id 1217type -> addIdOperand (makeBoolConstant (columnMajor ));// column-major id 1218 1219groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeMatrix ].push_back (type ); 1220constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1221module .mapInstruction (type ); 1222 1223return type -> getResultId (); 1224} 1225 1226// DebugTypeVectorIdEXT (NSDI.101 opcode 109): describes OpTypeVectorIdEXT and 1227// OpTypeCooperativeVectorNV types whose component count is a SPIR-V Id (not a 1228// literal), including specialization-constant component counts. 1229Id Builder ::makeVectorIdDebugType (Id const componentType ,Id const componentCount ) 1230{ 1231requireNonSemanticShaderDebugInfoVersion (101 ); 1232// try to find it 1233Instruction * type ; 1234for (int t = 0 ;t < (int )groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeVectorIdEXT ].size ();++ t ) { 1235type = groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeVectorIdEXT ][t ]; 1236if (type -> getIdOperand (2 )== getDebugType (componentType )&& 1237type -> getIdOperand (3 )== componentCount ) 1238return type -> getResultId (); 1239 } 1240 1241// not found, make it 1242type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1243type -> reserveOperands (4 ); 1244type -> addIdOperand (nonSemanticShaderDebugInfo ); 1245type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeVectorIdEXT ); 1246type -> addIdOperand (getDebugType (componentType ));// component debug type 1247type -> addIdOperand (componentCount );// component count (constant instruction Id) 1248 1249groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeVectorIdEXT ].push_back (type ); 1250constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1251module .mapInstruction (type ); 1252 1253return type -> getResultId (); 1254} 1255 1256// DebugTypeCooperativeMatrixKHR (NSDI.101 opcode 110): describes 1257// OpTypeCooperativeMatrixKHR types. 1258Id Builder ::makeCooperativeMatrixDebugTypeKHR (Id const componentType ,Id const scope , 1259Id const rows ,Id const cols ,Id const use ) 1260{ 1261requireNonSemanticShaderDebugInfoVersion (101 ); 1262// try to find it 1263Instruction * type ; 1264for (int t = 0 ;t < (int )groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR ].size ();++ t ) { 1265type = groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR ][t ]; 1266if (type -> getIdOperand (2 )== getDebugType (componentType )&& 1267type -> getIdOperand (3 )== scope && 1268type -> getIdOperand (4 )== rows && 1269type -> getIdOperand (5 )== cols && 1270type -> getIdOperand (6 )== use ) 1271return type -> getResultId (); 1272 } 1273 1274// not found, make it 1275type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1276type -> reserveOperands (7 ); 1277type -> addIdOperand (nonSemanticShaderDebugInfo ); 1278type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR ); 1279type -> addIdOperand (getDebugType (componentType ));// component debug type 1280type -> addIdOperand (scope );// scope 1281type -> addIdOperand (rows );// rows 1282type -> addIdOperand (cols );// columns 1283type -> addIdOperand (use );// use (MatrixA, MatrixB, Accumulator) 1284 1285groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR ].push_back (type ); 1286constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1287module .mapInstruction (type ); 1288 1289return type -> getResultId (); 1290} 1291 1292Id Builder ::makeMemberDebugType (Id const memberType ,StructMemberDebugInfo const & debugTypeLoc ) 1293{ 1294assert (getDebugType (memberType )!= NoType ); 1295 1296Instruction * type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1297type -> reserveOperands (10 ); 1298type -> addIdOperand (nonSemanticShaderDebugInfo ); 1299type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeMember ); 1300type -> addIdOperand (getStringId (debugTypeLoc .name ));// name id 1301type -> addIdOperand (debugTypeLoc .debugTypeOverride != 0 ?debugTypeLoc .debugTypeOverride 1302 :getDebugType (memberType ));// type id 1303type -> addIdOperand (makeDebugSource (currentFileId ));// source id 1304type -> addIdOperand (makeUintConstant (debugTypeLoc .line ));// line id TODO: currentLine is always zero 1305type -> addIdOperand (makeUintConstant (debugTypeLoc .column ));// TODO: column id 1306type -> addIdOperand (makeUintConstant (0 ));// TODO: offset id 1307type -> addIdOperand (makeUintConstant (0 ));// TODO: size id 1308type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFlagIsPublic ));// flags id 1309 1310groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeMember ].push_back (type ); 1311constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1312module .mapInstruction (type ); 1313 1314return type -> getResultId (); 1315} 1316 1317Id Builder ::makeCompositeDebugType (std::vector < Id > const & memberTypes , std::vector < StructMemberDebugInfo > const & memberDebugInfo , 1318char const * const name ,NonSemanticShaderDebugInfoDebugCompositeType const tag ) 1319{ 1320// Create the debug member types. 1321 std::vector < Id > memberDebugTypes ; 1322assert (memberTypes .size ()== memberDebugInfo .size ()); 1323for (size_t i = 0 ;i < memberTypes .size ();i ++ ) { 1324if (getDebugType (memberTypes [i ])!= NoType ) { 1325memberDebugTypes .emplace_back (makeMemberDebugType (memberTypes [i ],memberDebugInfo [i ])); 1326 } 1327 } 1328 1329// Create The structure debug type. 1330Instruction * type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1331type -> reserveOperands (memberDebugTypes .size ()+ 11 ); 1332type -> addIdOperand (nonSemanticShaderDebugInfo ); 1333type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeComposite ); 1334type -> addIdOperand (getStringId (name ));// name id 1335type -> addIdOperand (makeUintConstant (tag ));// tag id 1336type -> addIdOperand (makeDebugSource (currentFileId ));// source id 1337type -> addIdOperand (makeUintConstant (currentLine ));// line id TODO: currentLine always zero? 1338type -> addIdOperand (makeUintConstant (0 ));// TODO: column id 1339type -> addIdOperand (makeDebugCompilationUnit ());// scope id 1340type -> addIdOperand (getStringId (name ));// linkage name id 1341type -> addIdOperand (makeUintConstant (0 ));// TODO: size id 1342type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFlagIsPublic ));// flags id 1343for (auto const memberDebugType :memberDebugTypes ) { 1344type -> addIdOperand (memberDebugType ); 1345 } 1346 1347groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeComposite ].push_back (type ); 1348constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1349module .mapInstruction (type ); 1350 1351return type -> getResultId (); 1352} 1353 1354// The NonSemantic Shader Debug Info doesn't really have a dedicated opcode for opaque types. Instead, we use DebugTypeComposite. 1355// To represent a source language opaque type, this instruction must have no Members operands, Size operand must be 1356// DebugInfoNone, and Name must start with @ to avoid clashes with user defined names. 1357Id Builder ::makeOpaqueDebugType (char const * const name ) 1358{ 1359// Create The structure debug type. 1360Instruction * type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1361type -> reserveOperands (11 ); 1362type -> addIdOperand (nonSemanticShaderDebugInfo ); 1363type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypeComposite ); 1364type -> addIdOperand (getStringId (name ));// name id 1365type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoStructure ));// tag id 1366type -> addIdOperand (makeDebugSource (currentFileId ));// source id 1367type -> addIdOperand (makeUintConstant (currentLine ));// line id TODO: currentLine always zero? 1368type -> addIdOperand (makeUintConstant (0 ));// TODO: column id 1369type -> addIdOperand (makeDebugCompilationUnit ());// scope id 1370// Prepend '@' to opaque types. 1371type -> addIdOperand (getStringId ('@' + std::string (name )));// linkage name id 1372type -> addIdOperand (makeDebugInfoNone ());// size id 1373type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFlagIsPublic ));// flags id 1374 1375groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypeComposite ].push_back (type ); 1376constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1377module .mapInstruction (type ); 1378 1379return type -> getResultId (); 1380} 1381 1382Id Builder ::makePointerDebugType (StorageClass storageClass ,Id const baseType ) 1383{ 1384const Id debugBaseType = getDebugType (baseType ); 1385if (!debugBaseType ) { 1386return makeDebugInfoNone (); 1387 } 1388const Id scID = makeUintConstant (storageClass ); 1389for (Instruction * otherType :groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypePointer ]) { 1390if (otherType -> getIdOperand (2 )== debugBaseType && 1391otherType -> getIdOperand (3 )== scID ) { 1392return otherType -> getResultId (); 1393 } 1394 } 1395 1396Instruction * type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1397type -> reserveOperands (5 ); 1398type -> addIdOperand (nonSemanticShaderDebugInfo ); 1399type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypePointer ); 1400type -> addIdOperand (debugBaseType ); 1401type -> addIdOperand (scID ); 1402type -> addIdOperand (makeUintConstant (0 )); 1403 1404groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypePointer ].push_back (type ); 1405constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1406module .mapInstruction (type ); 1407 1408return type -> getResultId (); 1409} 1410 1411// Emit a OpExtInstWithForwardRefsKHR nonsemantic instruction for a pointer debug type 1412// where we don't have the pointee yet. Since we don't have the pointee yet, it just 1413// points to itself and we rely on patching it later. 1414Id Builder ::makeForwardPointerDebugType (StorageClass storageClass ) 1415{ 1416const Id scID = makeUintConstant (storageClass ); 1417 1418this -> addExtension (spv::E_SPV_KHR_relaxed_extended_instruction ); 1419 1420Instruction * type = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInstWithForwardRefsKHR ); 1421type -> addIdOperand (nonSemanticShaderDebugInfo ); 1422type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugTypePointer ); 1423type -> addIdOperand (type -> getResultId ()); 1424type -> addIdOperand (scID ); 1425type -> addIdOperand (makeUintConstant (0 )); 1426 1427groupedDebugTypes [NonSemanticShaderDebugInfoDebugTypePointer ].push_back (type ); 1428constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1429module .mapInstruction (type ); 1430 1431return type -> getResultId (); 1432} 1433 1434Id Builder ::makeDebugSource (const Id fileName ) { 1435if (debugSourceId .find (fileName )!= debugSourceId .end ()) 1436return debugSourceId [fileName ]; 1437 spv::Id resultId = getUniqueId (); 1438Instruction * sourceInst = new Instruction (resultId ,makeVoidType (),Op ::OpExtInst ); 1439sourceInst -> reserveOperands (3 ); 1440sourceInst -> addIdOperand (nonSemanticShaderDebugInfo ); 1441sourceInst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugSource ); 1442sourceInst -> addIdOperand (fileName ); 1443constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (sourceInst )); 1444module .mapInstruction (sourceInst ); 1445if (emitNonSemanticShaderDebugSource ) { 1446const int maxWordCount = 0xFFFF ; 1447const int opSourceWordCount = 4 ; 1448const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount )- 1 ; 1449auto processDebugSource = [& ](std::string source ) { 1450if (source .size ()> 0 ) { 1451int nextByte = 0 ; 1452while ((int )source .size ()- nextByte > 0 ) { 1453auto subString = source .substr (nextByte ,nonNullBytesPerInstruction ); 1454auto sourceId = getStringId (subString ); 1455if (nextByte == 0 ) { 1456// DebugSource 1457sourceInst -> addIdOperand (sourceId ); 1458 }else { 1459// DebugSourceContinued 1460Instruction * sourceContinuedInst = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1461sourceContinuedInst -> reserveOperands (2 ); 1462sourceContinuedInst -> addIdOperand (nonSemanticShaderDebugInfo ); 1463sourceContinuedInst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugSourceContinued ); 1464sourceContinuedInst -> addIdOperand (sourceId ); 1465constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (sourceContinuedInst )); 1466module .mapInstruction (sourceContinuedInst ); 1467 } 1468nextByte += nonNullBytesPerInstruction ; 1469 } 1470 }else { 1471auto sourceId = getStringId (source ); 1472sourceInst -> addIdOperand (sourceId ); 1473 } 1474 }; 1475if (fileName == mainFileId ) { 1476processDebugSource (sourceText ); 1477 }else { 1478auto incItr = includeFiles .find (fileName ); 1479if (incItr != includeFiles .end ()) { 1480processDebugSource (* incItr -> second ); 1481 }else { 1482// We omit the optional source text item if not available in glslang 1483 } 1484 } 1485 } 1486debugSourceId [fileName ]= resultId ; 1487return resultId ; 1488} 1489 1490Id Builder ::makeDebugCompilationUnit () { 1491if (nonSemanticShaderCompilationUnitId != 0 ) 1492return nonSemanticShaderCompilationUnitId ; 1493 spv::Id resultId = getUniqueId (); 1494Instruction * sourceInst = new Instruction (resultId ,makeVoidType (),Op ::OpExtInst ); 1495sourceInst -> reserveOperands (6 ); 1496sourceInst -> addIdOperand (nonSemanticShaderDebugInfo ); 1497sourceInst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugCompilationUnit ); 1498sourceInst -> addIdOperand (makeUintConstant (1 ));// TODO(greg-lunarg): Get rid of magic number 1499sourceInst -> addIdOperand (makeUintConstant (4 ));// TODO(greg-lunarg): Get rid of magic number 1500sourceInst -> addIdOperand (makeDebugSource (mainFileId )); 1501sourceInst -> addIdOperand (makeUintConstant (sourceLang )); 1502constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (sourceInst )); 1503module .mapInstruction (sourceInst ); 1504nonSemanticShaderCompilationUnitId = resultId ; 1505 1506// In the case of non-semantic shader debug info, preserve source text for every include 1507// even if no debug scope or line record ends up referencing that file. 1508if (emitNonSemanticShaderDebugSource ) { 1509for (const auto & includeFile :includeFiles ) { 1510makeDebugSource (includeFile .first ); 1511 } 1512 } 1513 1514// We can reasonably assume that makeDebugCompilationUnit will be called before any of 1515// debug-scope stack. Function scopes and lexical scopes will occur afterward. 1516assert (currentDebugScopeId .empty ()); 1517currentDebugScopeId .push (nonSemanticShaderCompilationUnitId ); 1518 1519return resultId ; 1520} 1521 1522Id Builder ::createDebugGlobalVariable (Id const type ,char const * const name ,Id const variable ) 1523{ 1524assert (type != 0 ); 1525 1526Instruction * inst = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1527inst -> reserveOperands (11 ); 1528inst -> addIdOperand (nonSemanticShaderDebugInfo ); 1529inst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugGlobalVariable ); 1530inst -> addIdOperand (getStringId (name ));// name id 1531inst -> addIdOperand (type );// type id 1532inst -> addIdOperand (makeDebugSource (currentFileId ));// source id 1533inst -> addIdOperand (makeUintConstant (currentLine ));// line id TODO: currentLine always zero? 1534inst -> addIdOperand (makeUintConstant (0 ));// TODO: column id 1535inst -> addIdOperand (makeDebugCompilationUnit ());// scope id 1536inst -> addIdOperand (getStringId (name ));// linkage name id 1537inst -> addIdOperand (variable );// variable id 1538inst -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFlagIsDefinition ));// flags id 1539 1540constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (inst )); 1541module .mapInstruction (inst ); 1542 1543return inst -> getResultId (); 1544} 1545 1546Id Builder ::createDebugLocalVariable (Id type ,char const * const name ,size_t const argNumber ) 1547{ 1548assert (name != nullptr ); 1549assert (!currentDebugScopeId .empty ()); 1550 1551Instruction * inst = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1552inst -> reserveOperands (9 ); 1553inst -> addIdOperand (nonSemanticShaderDebugInfo ); 1554inst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugLocalVariable ); 1555inst -> addIdOperand (getStringId (name ));// name id 1556inst -> addIdOperand (type );// type id 1557inst -> addIdOperand (makeDebugSource (currentFileId ));// source id 1558inst -> addIdOperand (makeUintConstant (currentLine ));// line id 1559inst -> addIdOperand (makeUintConstant (0 ));// TODO: column id 1560inst -> addIdOperand (currentDebugScopeId .top ());// scope id 1561inst -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFlagIsLocal ));// flags id 1562if (argNumber != 0 ) { 1563inst -> addIdOperand (makeUintConstant (static_cast < unsigned int > (argNumber ))); 1564 } 1565 1566constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (inst )); 1567module .mapInstruction (inst ); 1568 1569return inst -> getResultId (); 1570} 1571 1572Id Builder ::makeDebugExpression () 1573{ 1574if (debugExpression != 0 ) 1575return debugExpression ; 1576 1577Instruction * inst = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1578inst -> reserveOperands (2 ); 1579inst -> addIdOperand (nonSemanticShaderDebugInfo ); 1580inst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugExpression ); 1581 1582constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (inst )); 1583module .mapInstruction (inst ); 1584 1585debugExpression = inst -> getResultId (); 1586 1587return debugExpression ; 1588} 1589 1590Id Builder ::makeDebugDeclare (Id const debugLocalVariable ,Id const pointer ) 1591{ 1592Instruction * inst = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1593inst -> reserveOperands (5 ); 1594inst -> addIdOperand (nonSemanticShaderDebugInfo ); 1595inst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugDeclare ); 1596inst -> addIdOperand (debugLocalVariable );// debug local variable id 1597inst -> addIdOperand (pointer );// pointer to local variable id 1598inst -> addIdOperand (makeDebugExpression ());// expression id 1599addInstruction (std::unique_ptr < Instruction > (inst )); 1600 1601return inst -> getResultId (); 1602} 1603 1604Id Builder ::makeDebugValue (Id const debugLocalVariable ,Id const value ) 1605{ 1606Instruction * inst = new Instruction (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 1607inst -> reserveOperands (5 ); 1608inst -> addIdOperand (nonSemanticShaderDebugInfo ); 1609inst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugValue ); 1610inst -> addIdOperand (debugLocalVariable );// debug local variable id 1611inst -> addIdOperand (value );// value of local variable id 1612inst -> addIdOperand (makeDebugExpression ());// expression id 1613addInstruction (std::unique_ptr < Instruction > (inst )); 1614 1615return inst -> getResultId (); 1616} 1617 1618Id Builder ::makeAccelerationStructureType () 1619{ 1620Instruction * type ; 1621if (groupedTypes [enumCast (Op ::OpTypeAccelerationStructureKHR )].size ()== 0 ) { 1622type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeAccelerationStructureKHR ); 1623groupedTypes [enumCast (Op ::OpTypeAccelerationStructureKHR )].push_back (type ); 1624constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1625module .mapInstruction (type ); 1626if (emitNonSemanticShaderDebugInfo ) { 1627 spv::Id debugType = makeOpaqueDebugType ("accelerationStructure" ); 1628debugTypeIdLookup [type -> getResultId ()]= debugType ; 1629 } 1630 }else { 1631type = groupedTypes [enumCast (Op ::OpTypeAccelerationStructureKHR )].back (); 1632 } 1633 1634return type -> getResultId (); 1635} 1636 1637Id Builder ::makeRayQueryType () 1638{ 1639Instruction * type ; 1640if (groupedTypes [enumCast (Op ::OpTypeRayQueryKHR )].size ()== 0 ) { 1641type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeRayQueryKHR ); 1642groupedTypes [enumCast (Op ::OpTypeRayQueryKHR )].push_back (type ); 1643constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1644module .mapInstruction (type ); 1645if (emitNonSemanticShaderDebugInfo ) { 1646 spv::Id debugType = makeOpaqueDebugType ("rayQuery" ); 1647debugTypeIdLookup [type -> getResultId ()]= debugType ; 1648 } 1649 }else { 1650type = groupedTypes [enumCast (Op ::OpTypeRayQueryKHR )].back (); 1651 } 1652 1653return type -> getResultId (); 1654} 1655 1656Id Builder ::makeHitObjectEXTType () 1657{ 1658Instruction * type ; 1659if (groupedTypes [enumCast (Op ::OpTypeHitObjectEXT )].size ()== 0 ) { 1660type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeHitObjectEXT ); 1661groupedTypes [enumCast (Op ::OpTypeHitObjectEXT )].push_back (type ); 1662constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1663module .mapInstruction (type ); 1664 }else { 1665type = groupedTypes [enumCast (Op ::OpTypeHitObjectEXT )].back (); 1666 } 1667 1668return type -> getResultId (); 1669} 1670Id Builder ::makeHitObjectNVType () 1671{ 1672Instruction * type ; 1673if (groupedTypes [enumCast (Op ::OpTypeHitObjectNV )].size ()== 0 ) { 1674type = new Instruction (getUniqueId (),NoType ,Op ::OpTypeHitObjectNV ); 1675groupedTypes [enumCast (Op ::OpTypeHitObjectNV )].push_back (type ); 1676constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 1677module .mapInstruction (type ); 1678if (emitNonSemanticShaderDebugInfo ) { 1679 spv::Id debugType = makeOpaqueDebugType ("hitObjectNV" ); 1680debugTypeIdLookup [type -> getResultId ()]= debugType ; 1681 } 1682 }else { 1683type = groupedTypes [enumCast (Op ::OpTypeHitObjectNV )].back (); 1684 } 1685 1686return type -> getResultId (); 1687} 1688 1689Id Builder ::getDerefTypeId (Id resultId )const 1690{ 1691Id typeId = getTypeId (resultId ); 1692assert (isPointerType (typeId )); 1693 1694return module .getInstruction (typeId )-> getIdOperand (1 ); 1695} 1696 1697Op Builder ::getMostBasicTypeClass (Id typeId )const 1698{ 1699Instruction * instr = module .getInstruction (typeId ); 1700 1701Op typeClass = instr -> getOpCode (); 1702switch (typeClass ) 1703 { 1704case Op ::OpTypeVector : 1705case Op ::OpTypeMatrix : 1706case Op ::OpTypeArray : 1707case Op ::OpTypeRuntimeArray : 1708return getMostBasicTypeClass (instr -> getIdOperand (0 )); 1709case Op ::OpTypePointer : 1710return getMostBasicTypeClass (instr -> getIdOperand (1 )); 1711default : 1712return typeClass ; 1713 } 1714} 1715 1716unsigned int Builder ::getNumTypeConstituents (Id typeId )const 1717{ 1718Instruction * instr = module .getInstruction (typeId ); 1719 1720switch (instr -> getOpCode ()) 1721 { 1722case Op ::OpTypeBool : 1723case Op ::OpTypeInt : 1724case Op ::OpTypeFloat : 1725case Op ::OpTypePointer : 1726return 1 ; 1727case Op ::OpTypeVector : 1728case Op ::OpTypeMatrix : 1729return instr -> getImmediateOperand (1 ); 1730case Op ::OpTypeCooperativeVectorNV : 1731case Op ::OpTypeArray : 1732 { 1733Id lengthId = instr -> getIdOperand (1 ); 1734return module .getInstruction (lengthId )-> getImmediateOperand (0 ); 1735 } 1736case Op ::OpTypeStruct : 1737return instr -> getNumOperands (); 1738case Op ::OpTypeCooperativeMatrixKHR : 1739case Op ::OpTypeCooperativeMatrixNV : 1740// has only one constituent when used with OpCompositeConstruct. 1741return 1 ; 1742default : 1743assert (0 ); 1744return 1 ; 1745 } 1746} 1747 1748// Return the lowest-level type of scalar that an homogeneous composite is made out of. 1749// Typically, this is just to find out if something is made out of ints or floats. 1750// However, it includes returning a structure, if say, it is an array of structure. 1751Id Builder ::getScalarTypeId (Id typeId )const 1752{ 1753Instruction * instr = module .getInstruction (typeId ); 1754 1755Op typeClass = instr -> getOpCode (); 1756switch (typeClass ) 1757 { 1758case Op ::OpTypeVoid : 1759case Op ::OpTypeBool : 1760case Op ::OpTypeInt : 1761case Op ::OpTypeFloat : 1762case Op ::OpTypeStruct : 1763return instr -> getResultId (); 1764case Op ::OpTypeVector : 1765case Op ::OpTypeMatrix : 1766case Op ::OpTypeArray : 1767case Op ::OpTypeRuntimeArray : 1768case Op ::OpTypePointer : 1769case Op ::OpTypeCooperativeVectorNV : 1770return getScalarTypeId (getContainedTypeId (typeId )); 1771default : 1772assert (0 ); 1773return NoResult ; 1774 } 1775} 1776 1777// Return the type of 'member' of a composite. 1778Id Builder ::getContainedTypeId (Id typeId ,int member )const 1779{ 1780Instruction * instr = module .getInstruction (typeId ); 1781 1782Op typeClass = instr -> getOpCode (); 1783switch (typeClass ) 1784 { 1785case Op ::OpTypeVector : 1786case Op ::OpTypeMatrix : 1787case Op ::OpTypeArray : 1788case Op ::OpTypeRuntimeArray : 1789case Op ::OpTypeCooperativeMatrixKHR : 1790case Op ::OpTypeCooperativeMatrixNV : 1791case Op ::OpTypeCooperativeVectorNV : 1792return instr -> getIdOperand (0 ); 1793case Op ::OpTypePointer : 1794return instr -> getIdOperand (1 ); 1795case Op ::OpTypeStruct : 1796return instr -> getIdOperand (member ); 1797default : 1798assert (0 ); 1799return NoResult ; 1800 } 1801} 1802 1803// Figure out the final resulting type of the access chain. 1804Id Builder ::getResultingAccessChainType ()const 1805{ 1806assert (accessChain .base != NoResult ); 1807Id typeId = getTypeId (accessChain .base ); 1808 1809assert (isPointerType (typeId )); 1810typeId = getContainedTypeId (typeId ); 1811 1812for (int i = 0 ;i < (int )accessChain .indexChain .size ();++ i ) { 1813if (isStructType (typeId )) { 1814assert (isConstantScalar (accessChain .indexChain [i ])); 1815typeId = getContainedTypeId (typeId ,getConstantScalar (accessChain .indexChain [i ])); 1816 }else 1817typeId = getContainedTypeId (typeId ,accessChain .indexChain [i ]); 1818 } 1819 1820return typeId ; 1821} 1822 1823// Return the immediately contained type of a given composite type. 1824Id Builder ::getContainedTypeId (Id typeId )const 1825{ 1826return getContainedTypeId (typeId ,0 ); 1827} 1828 1829// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' 1830// of width 'width'. The 'width' is only consumed for int and float types. 1831// Returns false otherwise. 1832bool Builder ::containsType (Id typeId , spv::Op typeOp ,unsigned int width )const 1833{ 1834const Instruction & instr = * module .getInstruction (typeId ); 1835 1836Op typeClass = instr .getOpCode (); 1837switch (typeClass ) 1838 { 1839case Op ::OpTypeInt : 1840case Op ::OpTypeFloat : 1841return typeClass == typeOp && instr .getImmediateOperand (0 )== width ; 1842case Op ::OpTypeStruct : 1843for (int m = 0 ;m < instr .getNumOperands ();++ m ) { 1844if (containsType (instr .getIdOperand (m ),typeOp ,width )) 1845return true; 1846 } 1847return false; 1848case Op ::OpTypePointer : 1849return false; 1850case Op ::OpTypeVector : 1851case Op ::OpTypeMatrix : 1852case Op ::OpTypeArray : 1853case Op ::OpTypeRuntimeArray : 1854return containsType (getContainedTypeId (typeId ),typeOp ,width ); 1855default : 1856return typeClass == typeOp ; 1857 } 1858} 1859 1860// return true if the type is a pointer to PhysicalStorageBufferEXT or an 1861// contains such a pointer. These require restrict/aliased decorations. 1862bool Builder ::containsPhysicalStorageBufferOrArray (Id typeId )const 1863{ 1864const Instruction & instr = * module .getInstruction (typeId ); 1865 1866Op typeClass = instr .getOpCode (); 1867switch (typeClass ) 1868 { 1869case Op ::OpTypePointer : 1870return getTypeStorageClass (typeId )== StorageClass ::PhysicalStorageBufferEXT ; 1871case Op ::OpTypeArray : 1872return containsPhysicalStorageBufferOrArray (getContainedTypeId (typeId )); 1873case Op ::OpTypeStruct : 1874for (int m = 0 ;m < instr .getNumOperands ();++ m ) { 1875if (containsPhysicalStorageBufferOrArray (instr .getIdOperand (m ))) 1876return true; 1877 } 1878return false; 1879default : 1880return false; 1881 } 1882} 1883 1884// See if a scalar constant of this type has already been created, so it 1885// can be reused rather than duplicated. (Required by the specification). 1886Id Builder ::findScalarConstant (Op typeClass ,Op opcode ,Id typeId ,unsigned value ) 1887{ 1888ScalarConstantKey key {enumCast (typeClass ),enumCast (opcode ),typeId ,value ,0 }; 1889auto it = groupedScalarConstantResultIDs .find (key ); 1890return (it != groupedScalarConstantResultIDs .end ()) ?it -> second :0 ; 1891} 1892 1893// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). 1894Id Builder ::findScalarConstant (Op typeClass ,Op opcode ,Id typeId ,unsigned v1 ,unsigned v2 ) 1895{ 1896ScalarConstantKey key {enumCast (typeClass ),enumCast (opcode ),typeId ,v1 ,v2 }; 1897auto it = groupedScalarConstantResultIDs .find (key ); 1898return (it != groupedScalarConstantResultIDs .end ()) ?it -> second :0 ; 1899} 1900 1901// Return true if consuming 'opcode' means consuming a constant. 1902// "constant" here means after final transform to executable code, 1903// the value consumed will be a constant, so includes specialization. 1904bool Builder ::isConstantOpCode (Op opcode )const 1905{ 1906switch (opcode ) { 1907case Op ::OpUndef : 1908case Op ::OpConstantTrue : 1909case Op ::OpConstantFalse : 1910case Op ::OpConstant : 1911case Op ::OpConstantDataKHR : 1912case Op ::OpConstantComposite : 1913case Op ::OpConstantCompositeReplicateEXT : 1914case Op ::OpConstantSampler : 1915case Op ::OpConstantNull : 1916case Op ::OpSpecConstantTrue : 1917case Op ::OpSpecConstantFalse : 1918case Op ::OpSpecConstant : 1919case Op ::OpSpecConstantComposite : 1920case Op ::OpSpecConstantCompositeReplicateEXT : 1921case Op ::OpSpecConstantDataKHR : 1922case Op ::OpSpecConstantOp : 1923case Op ::OpConstantSizeOfEXT : 1924return true; 1925default : 1926return false; 1927 } 1928} 1929 1930// Return true if consuming 'opcode' means consuming a specialization constant. 1931bool Builder ::isSpecConstantOpCode (Op opcode )const 1932{ 1933switch (opcode ) { 1934case Op ::OpSpecConstantTrue : 1935case Op ::OpSpecConstantFalse : 1936case Op ::OpSpecConstant : 1937case Op ::OpSpecConstantComposite : 1938case Op ::OpSpecConstantDataKHR : 1939case Op ::OpSpecConstantOp : 1940case Op ::OpSpecConstantCompositeReplicateEXT : 1941return true; 1942default : 1943return false; 1944 } 1945} 1946 1947Id Builder ::makeNullConstant (Id typeId ) 1948{ 1949Instruction * constant ; 1950 1951// See if we already made it. 1952Id existing = NoResult ; 1953for (int i = 0 ;i < (int )nullConstants .size ();++ i ) { 1954constant = nullConstants [i ]; 1955if (constant -> getTypeId ()== typeId ) 1956existing = constant -> getResultId (); 1957 } 1958 1959if (existing != NoResult ) 1960return existing ; 1961 1962// Make it 1963Instruction * c = new Instruction (getUniqueId (),typeId ,Op ::OpConstantNull ); 1964constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 1965nullConstants .push_back (c ); 1966module .mapInstruction (c ); 1967 1968return c -> getResultId (); 1969} 1970 1971Id Builder ::makeBoolConstant (bool b ,bool specConstant ) 1972{ 1973Id typeId = makeBoolType (); 1974Op opcode = specConstant ? (b ?Op ::OpSpecConstantTrue :Op ::OpSpecConstantFalse ) : (b ?Op ::OpConstantTrue :Op ::OpConstantFalse ); 1975 1976// See if we already made it. Applies only to regular constants, because specialization constants 1977// must remain distinct for the purpose of applying a SpecId decoration. 1978if (!specConstant ) { 1979Id existing = findScalarConstant (Op ::OpTypeBool ,opcode ,typeId ,0 ); 1980if (existing ) 1981return existing ; 1982 } 1983 1984// Make it 1985Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 1986constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 1987module .mapInstruction (c ); 1988 1989Id resultId = c -> getResultId (); 1990if (!specConstant ) { 1991ScalarConstantKey key {enumCast (Op ::OpTypeBool ),enumCast (opcode ),typeId ,0 ,0 }; 1992groupedScalarConstantResultIDs [key ]= resultId ; 1993 } 1994return resultId ; 1995} 1996 1997Id Builder ::makeIntConstant (Id typeId ,unsigned value ,bool specConstant ) 1998{ 1999Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2000 2001// See if we already made it. Applies only to regular constants, because specialization constants 2002// must remain distinct for the purpose of applying a SpecId decoration. 2003if (!specConstant ) { 2004Id existing = findScalarConstant (Op ::OpTypeInt ,opcode ,typeId ,value ); 2005if (existing ) 2006return existing ; 2007 } 2008 2009Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2010c -> addImmediateOperand (value ); 2011constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2012module .mapInstruction (c ); 2013 2014Id resultId = c -> getResultId (); 2015if (!specConstant ) { 2016ScalarConstantKey key {enumCast (Op ::OpTypeInt ),enumCast (opcode ),typeId ,value ,0 }; 2017groupedScalarConstantResultIDs [key ]= resultId ; 2018 } 2019return resultId ; 2020} 2021 2022Id Builder ::makeInt64Constant (Id typeId ,unsigned long long value ,bool specConstant ) 2023{ 2024Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2025 2026unsigned op1 = value & 0xFFFFFFFF ; 2027unsigned op2 = value >>32 ; 2028 2029// See if we already made it. Applies only to regular constants, because specialization constants 2030// must remain distinct for the purpose of applying a SpecId decoration. 2031if (!specConstant ) { 2032Id existing = findScalarConstant (Op ::OpTypeInt ,opcode ,typeId ,op1 ,op2 ); 2033if (existing ) 2034return existing ; 2035 } 2036 2037Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2038c -> reserveOperands (2 ); 2039c -> addImmediateOperand (op1 ); 2040c -> addImmediateOperand (op2 ); 2041constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2042module .mapInstruction (c ); 2043 2044Id resultId = c -> getResultId (); 2045if (!specConstant ) { 2046ScalarConstantKey key {enumCast (Op ::OpTypeInt ),enumCast (opcode ),typeId ,op1 ,op2 }; 2047groupedScalarConstantResultIDs [key ]= resultId ; 2048 } 2049return resultId ; 2050} 2051 2052Id Builder ::makeFloatConstant (float f ,bool specConstant ) 2053{ 2054Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2055Id typeId = makeFloatType (32 ); 2056union {float fl ;unsigned int ui ; }u ; 2057u .fl = f ; 2058unsigned value = u .ui ; 2059 2060// See if we already made it. Applies only to regular constants, because specialization constants 2061// must remain distinct for the purpose of applying a SpecId decoration. 2062if (!specConstant ) { 2063Id existing = findScalarConstant (Op ::OpTypeFloat ,opcode ,typeId ,value ); 2064if (existing ) 2065return existing ; 2066 } 2067 2068Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2069c -> addImmediateOperand (value ); 2070constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2071module .mapInstruction (c ); 2072 2073Id resultId = c -> getResultId (); 2074if (!specConstant ) { 2075ScalarConstantKey key {enumCast (Op ::OpTypeFloat ),enumCast (opcode ),typeId ,value ,0 }; 2076groupedScalarConstantResultIDs [key ]= resultId ; 2077 } 2078return resultId ; 2079} 2080 2081Id Builder ::makeDoubleConstant (double d ,bool specConstant ) 2082{ 2083Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2084Id typeId = makeFloatType (64 ); 2085union {double db ;unsigned long long ull ; }u ; 2086u .db = d ; 2087unsigned long long value = u .ull ; 2088unsigned op1 = value & 0xFFFFFFFF ; 2089unsigned op2 = value >>32 ; 2090 2091// See if we already made it. Applies only to regular constants, because specialization constants 2092// must remain distinct for the purpose of applying a SpecId decoration. 2093if (!specConstant ) { 2094Id existing = findScalarConstant (Op ::OpTypeFloat ,opcode ,typeId ,op1 ,op2 ); 2095if (existing ) 2096return existing ; 2097 } 2098 2099Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2100c -> reserveOperands (2 ); 2101c -> addImmediateOperand (op1 ); 2102c -> addImmediateOperand (op2 ); 2103constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2104module .mapInstruction (c ); 2105 2106Id resultId = c -> getResultId (); 2107if (!specConstant ) { 2108ScalarConstantKey key {enumCast (Op ::OpTypeFloat ),enumCast (opcode ),typeId ,op1 ,op2 }; 2109groupedScalarConstantResultIDs [key ]= resultId ; 2110 } 2111return resultId ; 2112} 2113 2114Id Builder ::makeFloat16Constant (float f16 ,bool specConstant ) 2115{ 2116Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2117Id typeId = makeFloatType (16 ); 2118 2119 spvutils::HexFloat < spvutils::FloatProxy < float >> fVal (f16 ); 2120 spvutils::HexFloat < spvutils::FloatProxy < spvutils::Float16 >> f16Val (0 ); 2121fVal .castTo (f16Val , spvutils::kRoundToZero ); 2122 2123unsigned value = f16Val .value ().getAsFloat ().get_value (); 2124 2125// See if we already made it. Applies only to regular constants, because specialization constants 2126// must remain distinct for the purpose of applying a SpecId decoration. 2127if (!specConstant ) { 2128Id existing = findScalarConstant (Op ::OpTypeFloat ,opcode ,typeId ,value ); 2129if (existing ) 2130return existing ; 2131 } 2132 2133Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2134c -> addImmediateOperand (value ); 2135constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2136module .mapInstruction (c ); 2137 2138Id resultId = c -> getResultId (); 2139if (!specConstant ) { 2140ScalarConstantKey key {enumCast (Op ::OpTypeFloat ),enumCast (opcode ),typeId ,value ,0 }; 2141groupedScalarConstantResultIDs [key ]= resultId ; 2142 } 2143return resultId ; 2144} 2145 2146Id Builder ::makeBFloat16Constant (float bf16 ,bool specConstant ) 2147{ 2148Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2149Id typeId = makeBFloat16Type (); 2150 2151union { 2152float f ; 2153uint32_t u ; 2154 }un ; 2155un .f = bf16 ; 2156 2157// take high 16b of fp32 value. This is effectively round-to-zero, other than certain NaNs. 2158unsigned value = un .u >>16 ; 2159 2160// See if we already made it. Applies only to regular constants, because specialization constants 2161// must remain distinct for the purpose of applying a SpecId decoration. 2162if (!specConstant ) { 2163Id existing = findScalarConstant (Op ::OpTypeFloat ,opcode ,typeId ,value ); 2164if (existing ) 2165return existing ; 2166 } 2167 2168Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2169c -> addImmediateOperand (value ); 2170constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2171module .mapInstruction (c ); 2172 2173Id resultId = c -> getResultId (); 2174if (!specConstant ) { 2175ScalarConstantKey key {enumCast (Op ::OpTypeFloat ),enumCast (opcode ),typeId ,value ,0 }; 2176groupedScalarConstantResultIDs [key ]= resultId ; 2177 } 2178return resultId ; 2179} 2180 2181template < typename SpvUtilsType > 2182Id Builder ::makeFloatConstantHelper (float f ,Id typeId ,bool specConstant ) 2183{ 2184Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2185 2186 spvutils::HexFloat < spvutils::FloatProxy < float >> fVal (f ); 2187 spvutils::HexFloat < spvutils::FloatProxy < SpvUtilsType >> smallFloatVal (0 ); 2188fVal .castTo (smallFloatVal , spvutils::kRoundToZero ); 2189 2190unsigned value = smallFloatVal .value ().getAsFloat ().get_value (); 2191 2192// See if we already made it. Applies only to regular constants, because specialization constants 2193// must remain distinct for the purpose of applying a SpecId decoration. 2194if (!specConstant ) { 2195Id existing = findScalarConstant (Op ::OpTypeFloat ,opcode ,typeId ,value ); 2196if (existing ) 2197return existing ; 2198 } 2199 2200Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2201c -> addImmediateOperand (value ); 2202constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2203module .mapInstruction (c ); 2204 2205Id resultId = c -> getResultId (); 2206if (!specConstant ) { 2207ScalarConstantKey key {enumCast (Op ::OpTypeFloat ),enumCast (opcode ),typeId ,value ,0 }; 2208groupedScalarConstantResultIDs [key ]= resultId ; 2209 } 2210return resultId ; 2211} 2212 2213Id Builder ::makeFloatE5M2Constant (float fe5m2 ,bool specConstant ) 2214{ 2215return makeFloatConstantHelper < spvutils::FloatE5M2 > (fe5m2 ,makeFloatE5M2Type (),specConstant ); 2216} 2217 2218Id Builder ::makeFloatE4M3Constant (float fe4m3 ,bool specConstant ) 2219{ 2220return makeFloatConstantHelper < spvutils::FloatE4M3 > (fe4m3 ,makeFloatE4M3Type (),specConstant ); 2221} 2222 2223Id Builder ::makeFloatE2M1Constant (float fe2m1 ,bool specConstant ) 2224{ 2225return makeFloatConstantHelper < spvutils::FloatE2M1 > (fe2m1 ,makeFloatE2M1Type (),specConstant ); 2226} 2227 2228Id Builder ::makeFloatE3M2Constant (float fe3m2 ,bool specConstant ) 2229{ 2230return makeFloatConstantHelper < spvutils::FloatE3M2 > (fe3m2 ,makeFloatE3M2Type (),specConstant ); 2231} 2232 2233Id Builder ::makeFloatE2M3Constant (float fe2m3 ,bool specConstant ) 2234{ 2235return makeFloatConstantHelper < spvutils::FloatE2M3 > (fe2m3 ,makeFloatE2M3Type (),specConstant ); 2236} 2237 2238Id Builder ::makeFloatUE8M0Constant (float fue8m0 ,bool specConstant ) 2239{ 2240Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2241Id typeId = makeFloatUE8M0Type (); 2242 2243fue8m0 = std::max (fue8m0 ,0.f ); 2244union { 2245float f ; 2246uint32_t u ; 2247 }un ; 2248un .f = fue8m0 ; 2249// extract the exponent, this effectively rounds to zero. 2250uint32_t value = un .u >>23 ; 2251 2252// See if we already made it. Applies only to regular constants, because specialization constants 2253// must remain distinct for the purpose of applying a SpecId decoration. 2254if (!specConstant ) { 2255Id existing = findScalarConstant (Op ::OpTypeFloat ,opcode ,typeId ,value ); 2256if (existing ) 2257return existing ; 2258 } 2259 2260Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2261c -> addImmediateOperand (value ); 2262constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2263module .mapInstruction (c ); 2264 2265Id resultId = c -> getResultId (); 2266if (!specConstant ) { 2267ScalarConstantKey key {enumCast (Op ::OpTypeFloat ),enumCast (opcode ),typeId ,value ,0 }; 2268groupedScalarConstantResultIDs [key ]= resultId ; 2269 } 2270return resultId ; 2271} 2272 2273Id Builder ::makeFloatMXINT8Constant (float mxint8 ,bool specConstant ) 2274{ 2275Op opcode = specConstant ?Op ::OpSpecConstant :Op ::OpConstant ; 2276Id typeId = makeFloatMXINT8Type (); 2277 2278// clamp and convert to fixed point. 2279mxint8 = std::min (mxint8 ,127.f /64.f ); 2280mxint8 = std::max (mxint8 ,-127.f /64.f ); 2281mxint8 *=64 ; 2282mxint8 = roundf (mxint8 ); 2283uint32_t value = ((int32_t )mxint8 )& 0xFF ; 2284 2285// See if we already made it. Applies only to regular constants, because specialization constants 2286// must remain distinct for the purpose of applying a SpecId decoration. 2287if (!specConstant ) { 2288Id existing = findScalarConstant (Op ::OpTypeFloat ,opcode ,typeId ,value ); 2289if (existing ) 2290return existing ; 2291 } 2292 2293Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2294c -> addImmediateOperand (value ); 2295constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2296module .mapInstruction (c ); 2297 2298Id resultId = c -> getResultId (); 2299if (!specConstant ) { 2300ScalarConstantKey key {enumCast (Op ::OpTypeFloat ),enumCast (opcode ),typeId ,value ,0 }; 2301groupedScalarConstantResultIDs [key ]= resultId ; 2302 } 2303return resultId ; 2304} 2305 2306Id Builder ::makeFpConstant (Id type ,double d ,bool specConstant ) 2307{ 2308const int width = getScalarTypeWidth (type ); 2309 2310assert (isFloatType (type )); 2311 2312switch (width ) { 2313case 16 : 2314return makeFloat16Constant ((float )d ,specConstant ); 2315case 32 : 2316return makeFloatConstant ((float )d ,specConstant ); 2317case 64 : 2318return makeDoubleConstant (d ,specConstant ); 2319default : 2320break ; 2321 } 2322 2323assert (false); 2324return NoResult ; 2325} 2326 2327Id Builder ::importNonSemanticShaderDebugInfoInstructions () 2328{ 2329assert (emitNonSemanticShaderDebugInfo == true); 2330 2331if (nonSemanticShaderDebugInfo == 0 ) 2332 { 2333this -> addExtension (spv::E_SPV_KHR_non_semantic_info ); 2334// Create the import instruction directly so we can save a pointer for later 2335// patching by requireNonSemanticShaderDebugInfoVersion(). 2336 std::string importName = "NonSemantic.Shader.DebugInfo." + std::to_string (nonSemanticShaderDebugInfoVersion ); 2337auto * importInst = new Instruction (getUniqueId (),NoType ,Op ::OpExtInstImport ); 2338importInst -> addStringOperand (importName .c_str ()); 2339module .mapInstruction (importInst ); 2340imports .push_back (std::unique_ptr < Instruction > (importInst )); 2341nonSemanticShaderDebugInfo = importInst -> getResultId (); 2342nonSemanticShaderDebugInfoImportInst = importInst ; 2343 } 2344 2345return nonSemanticShaderDebugInfo ; 2346} 2347 2348void Builder ::requireNonSemanticShaderDebugInfoVersion (unsigned version ) 2349{ 2350if (nonSemanticShaderDebugInfoVersion >=version ) 2351return ; 2352nonSemanticShaderDebugInfoVersion = version ; 2353if (nonSemanticShaderDebugInfoImportInst != nullptr ) { 2354// The import instruction was already emitted with an older version string. 2355// Rebuild the string operands in place so all referencing OpExtInst instructions 2356// automatically pick up the new name without needing new IDs. 2357 std::string importName = "NonSemantic.Shader.DebugInfo." + std::to_string (version ); 2358nonSemanticShaderDebugInfoImportInst -> clearOperands (); 2359nonSemanticShaderDebugInfoImportInst -> addStringOperand (importName .c_str ()); 2360 } 2361} 2362 2363Id Builder ::findCompositeConstant (Op typeClass ,Op opcode ,Id typeId ,const std::vector < Id >& comps ,size_t numMembers ) 2364{ 2365auto it = groupedCompositeConstants .find ( 2366CompositeConstantKey (typeClass ,opcode ,typeId ,comps ,numMembers )); 2367if (it != groupedCompositeConstants .end ()) { 2368const Instruction * constant = (* it ).getInstruction (); 2369return constant -> getResultId (); 2370 } 2371return NoResult ; 2372} 2373 2374Id Builder ::findStructConstant (Id typeId ,const std::vector < Id >& comps ,size_t numMembers ) 2375{ 2376auto it = groupedStructConstants .find (StructConstantKey (typeId ,comps ,numMembers )); 2377if (it != groupedStructConstants .end ()) { 2378const Instruction * constant = (* it ).getInstruction (); 2379return constant -> getResultId (); 2380 } 2381return NoResult ; 2382} 2383 2384// Comments in header 2385Id Builder ::makeCompositeConstant (Id typeId ,const std::vector < Id >& members ,bool specConstant ) 2386{ 2387assert (typeId ); 2388Op typeClass = getTypeClass (typeId ); 2389 2390bool replicate = false; 2391size_t numMembers = members .size (); 2392if (useReplicatedComposites || typeClass == Op ::OpTypeCooperativeVectorNV ) { 2393// use replicate if all members are the same 2394replicate = numMembers > 0 && 2395 std::equal (members .begin ()+ 1 ,members .end (),members .begin ()); 2396 2397if (replicate ) { 2398numMembers = 1 ; 2399addCapability (spv::Capability ::ReplicatedCompositesEXT ); 2400addExtension (spv::E_SPV_EXT_replicated_composites ); 2401 } 2402 } 2403 2404Op opcode = replicate ? 2405 (specConstant ?Op ::OpSpecConstantCompositeReplicateEXT :Op ::OpConstantCompositeReplicateEXT ) : 2406 (specConstant ?Op ::OpSpecConstantComposite :Op ::OpConstantComposite ); 2407 2408switch (typeClass ) { 2409case Op ::OpTypeVector : 2410case Op ::OpTypeArray : 2411case Op ::OpTypeMatrix : 2412case Op ::OpTypeCooperativeMatrixKHR : 2413case Op ::OpTypeCooperativeMatrixNV : 2414case Op ::OpTypeCooperativeVectorNV : 2415if (!specConstant ) { 2416Id existing = findCompositeConstant (typeClass ,opcode ,typeId ,members ,numMembers ); 2417if (existing ) 2418return existing ; 2419 } 2420break ; 2421case Op ::OpTypeStruct : 2422if (!specConstant ) { 2423Id existing = findStructConstant (typeId ,members ,numMembers ); 2424if (existing ) 2425return existing ; 2426 } 2427break ; 2428default : 2429assert (0 ); 2430return makeFloatConstant (0.0 ); 2431 } 2432 2433Instruction * c = new Instruction (getUniqueId (),typeId ,opcode ); 2434c -> reserveOperands (members .size ()); 2435for (size_t op = 0 ;op < numMembers ;++ op ) 2436c -> addIdOperand (members [op ]); 2437constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (c )); 2438if (typeClass == Op ::OpTypeStruct ) 2439groupedStructConstants .insert (StructConstantKey (c )); 2440else 2441groupedCompositeConstants .insert (CompositeConstantKey (typeClass ,c )); 2442module .mapInstruction (c ); 2443 2444return c -> getResultId (); 2445} 2446 2447Instruction * Builder ::addEntryPoint (ExecutionModel model ,Function * function ,const char * name ) 2448{ 2449Instruction * entryPoint = new Instruction (Op ::OpEntryPoint ); 2450entryPoint -> reserveOperands (3 ); 2451entryPoint -> addImmediateOperand (model ); 2452entryPoint -> addIdOperand (function -> getId ()); 2453entryPoint -> addStringOperand (name ); 2454 2455entryPoints .push_back (std::unique_ptr < Instruction > (entryPoint )); 2456 2457return entryPoint ; 2458} 2459 2460// Currently relying on the fact that all 'value' of interest are small non-negative values. 2461void Builder ::addExecutionMode (Function * entryPoint ,ExecutionMode mode ,int value1 ,int value2 ,int value3 ) 2462{ 2463// entryPoint can be null if we are in compile-only mode 2464if (!entryPoint ) 2465return ; 2466 2467Instruction * instr = new Instruction (Op ::OpExecutionMode ); 2468instr -> reserveOperands (3 ); 2469instr -> addIdOperand (entryPoint -> getId ()); 2470instr -> addImmediateOperand (mode ); 2471if (value1 >=0 ) 2472instr -> addImmediateOperand (value1 ); 2473if (value2 >=0 ) 2474instr -> addImmediateOperand (value2 ); 2475if (value3 >=0 ) 2476instr -> addImmediateOperand (value3 ); 2477 2478executionModes .push_back (std::unique_ptr < Instruction > (instr )); 2479} 2480 2481void Builder ::addExecutionMode (Function * entryPoint ,ExecutionMode mode ,const std::vector < unsigned >& literals ) 2482{ 2483// entryPoint can be null if we are in compile-only mode 2484if (!entryPoint ) 2485return ; 2486 2487Instruction * instr = new Instruction (Op ::OpExecutionMode ); 2488instr -> reserveOperands (literals .size ()+ 2 ); 2489instr -> addIdOperand (entryPoint -> getId ()); 2490instr -> addImmediateOperand (mode ); 2491for (auto literal :literals ) 2492instr -> addImmediateOperand (literal ); 2493 2494executionModes .push_back (std::unique_ptr < Instruction > (instr )); 2495} 2496 2497void Builder ::addExecutionModeId (Function * entryPoint ,ExecutionMode mode ,const std::vector < Id >& operandIds ) 2498{ 2499// entryPoint can be null if we are in compile-only mode 2500if (!entryPoint ) 2501return ; 2502 2503Instruction * instr = new Instruction (Op ::OpExecutionModeId ); 2504instr -> reserveOperands (operandIds .size ()+ 2 ); 2505instr -> addIdOperand (entryPoint -> getId ()); 2506instr -> addImmediateOperand (mode ); 2507for (auto operandId :operandIds ) 2508instr -> addIdOperand (operandId ); 2509 2510executionModes .push_back (std::unique_ptr < Instruction > (instr )); 2511} 2512 2513void Builder ::addName (Id id ,const char * string ) 2514{ 2515Instruction * name = new Instruction (Op ::OpName ); 2516name -> reserveOperands (2 ); 2517name -> addIdOperand (id ); 2518name -> addStringOperand (string ); 2519 2520names .push_back (std::unique_ptr < Instruction > (name )); 2521} 2522 2523void Builder ::addMemberName (Id id ,int memberNumber ,const char * string ) 2524{ 2525Instruction * name = new Instruction (Op ::OpMemberName ); 2526name -> reserveOperands (3 ); 2527name -> addIdOperand (id ); 2528name -> addImmediateOperand (memberNumber ); 2529name -> addStringOperand (string ); 2530 2531names .push_back (std::unique_ptr < Instruction > (name )); 2532} 2533 2534void Builder ::addDecoration (Id id ,Decoration decoration ,int num ) 2535{ 2536if (decoration == spv::Decoration ::Max ) 2537return ; 2538 2539Instruction * dec = new Instruction (Op ::OpDecorate ); 2540dec -> reserveOperands (2 ); 2541dec -> addIdOperand (id ); 2542dec -> addImmediateOperand (decoration ); 2543if (num >=0 ) 2544dec -> addImmediateOperand (num ); 2545 2546decorations .insert (std::unique_ptr < Instruction > (dec )); 2547} 2548 2549void Builder ::addDecoration (Id id ,Decoration decoration ,const char * s ) 2550{ 2551if (decoration == spv::Decoration ::Max ) 2552return ; 2553 2554Instruction * dec = new Instruction (Op ::OpDecorateString ); 2555dec -> reserveOperands (3 ); 2556dec -> addIdOperand (id ); 2557dec -> addImmediateOperand (decoration ); 2558dec -> addStringOperand (s ); 2559 2560decorations .insert (std::unique_ptr < Instruction > (dec )); 2561} 2562 2563void Builder ::addDecoration (Id id ,Decoration decoration ,const std::vector < unsigned >& literals ) 2564{ 2565if (decoration == spv::Decoration ::Max ) 2566return ; 2567 2568Instruction * dec = new Instruction (Op ::OpDecorate ); 2569dec -> reserveOperands (literals .size ()+ 2 ); 2570dec -> addIdOperand (id ); 2571dec -> addImmediateOperand (decoration ); 2572for (auto literal :literals ) 2573dec -> addImmediateOperand (literal ); 2574 2575decorations .insert (std::unique_ptr < Instruction > (dec )); 2576} 2577 2578void Builder ::addDecoration (Id id ,Decoration decoration ,const std::vector < const char *>& strings ) 2579{ 2580if (decoration == spv::Decoration ::Max ) 2581return ; 2582 2583Instruction * dec = new Instruction (Op ::OpDecorateString ); 2584dec -> reserveOperands (strings .size ()+ 2 ); 2585dec -> addIdOperand (id ); 2586dec -> addImmediateOperand (decoration ); 2587for (auto string :strings ) 2588dec -> addStringOperand (string ); 2589 2590decorations .insert (std::unique_ptr < Instruction > (dec )); 2591} 2592 2593void Builder ::addLinkageDecoration (Id id ,const char * name , spv::LinkageType linkType ) { 2594Instruction * dec = new Instruction (Op ::OpDecorate ); 2595dec -> reserveOperands (4 ); 2596dec -> addIdOperand (id ); 2597dec -> addImmediateOperand (spv::Decoration ::LinkageAttributes ); 2598dec -> addStringOperand (name ); 2599dec -> addImmediateOperand (linkType ); 2600 2601decorations .insert (std::unique_ptr < Instruction > (dec )); 2602} 2603 2604void Builder ::addDecorationId (Id id ,Decoration decoration ,Id idDecoration ) 2605{ 2606if (decoration == spv::Decoration ::Max ) 2607return ; 2608 2609Instruction * dec = new Instruction (Op ::OpDecorateId ); 2610dec -> reserveOperands (3 ); 2611dec -> addIdOperand (id ); 2612dec -> addImmediateOperand (decoration ); 2613dec -> addIdOperand (idDecoration ); 2614 2615decorations .insert (std::unique_ptr < Instruction > (dec )); 2616} 2617 2618void Builder ::addDecorationId (Id id ,Decoration decoration ,const std::vector < Id >& operandIds ) 2619{ 2620if (decoration == spv::Decoration ::Max ) 2621return ; 2622 2623Instruction * dec = new Instruction (Op ::OpDecorateId ); 2624dec -> reserveOperands (operandIds .size ()+ 2 ); 2625dec -> addIdOperand (id ); 2626dec -> addImmediateOperand (decoration ); 2627 2628for (auto operandId :operandIds ) 2629dec -> addIdOperand (operandId ); 2630 2631decorations .insert (std::unique_ptr < Instruction > (dec )); 2632} 2633 2634void Builder ::addMemberDecorationIdEXT (Id id ,unsigned int member ,Decoration decoration , 2635const std::vector < unsigned >& operands ) 2636{ 2637if (decoration == spv::Decoration ::Max ) 2638return ; 2639 2640Instruction * dec = new Instruction (Op ::OpMemberDecorateIdEXT ); 2641dec -> reserveOperands (operands .size ()+ 3 ); 2642dec -> addIdOperand (id ); 2643dec -> addImmediateOperand (member ); 2644dec -> addImmediateOperand (decoration ); 2645for (auto operand :operands ) 2646dec -> addIdOperand (operand ); 2647 2648decorations .insert (std::unique_ptr < Instruction > (dec )); 2649} 2650 2651void Builder ::addMemberDecoration (Id id ,unsigned int member ,Decoration decoration ,int num ) 2652{ 2653if (decoration == spv::Decoration ::Max ) 2654return ; 2655 2656Instruction * dec = new Instruction (Op ::OpMemberDecorate ); 2657dec -> reserveOperands (3 ); 2658dec -> addIdOperand (id ); 2659dec -> addImmediateOperand (member ); 2660dec -> addImmediateOperand (decoration ); 2661if (num >=0 ) 2662dec -> addImmediateOperand (num ); 2663 2664decorations .insert (std::unique_ptr < Instruction > (dec )); 2665} 2666 2667void Builder ::addMemberDecoration (Id id ,unsigned int member ,Decoration decoration ,const char * s ) 2668{ 2669if (decoration == spv::Decoration ::Max ) 2670return ; 2671 2672Instruction * dec = new Instruction (Op ::OpMemberDecorateStringGOOGLE ); 2673dec -> reserveOperands (4 ); 2674dec -> addIdOperand (id ); 2675dec -> addImmediateOperand (member ); 2676dec -> addImmediateOperand (decoration ); 2677dec -> addStringOperand (s ); 2678 2679decorations .insert (std::unique_ptr < Instruction > (dec )); 2680} 2681 2682void Builder ::addMemberDecoration (Id id ,unsigned int member ,Decoration decoration ,const std::vector < unsigned >& literals ) 2683{ 2684if (decoration == spv::Decoration ::Max ) 2685return ; 2686 2687Instruction * dec = new Instruction (Op ::OpMemberDecorate ); 2688dec -> reserveOperands (literals .size ()+ 3 ); 2689dec -> addIdOperand (id ); 2690dec -> addImmediateOperand (member ); 2691dec -> addImmediateOperand (decoration ); 2692for (auto literal :literals ) 2693dec -> addImmediateOperand (literal ); 2694 2695decorations .insert (std::unique_ptr < Instruction > (dec )); 2696} 2697 2698void Builder ::addMemberDecoration (Id id ,unsigned int member ,Decoration decoration ,const std::vector < const char *>& strings ) 2699{ 2700if (decoration == spv::Decoration ::Max ) 2701return ; 2702 2703Instruction * dec = new Instruction (Op ::OpMemberDecorateString ); 2704dec -> reserveOperands (strings .size ()+ 3 ); 2705dec -> addIdOperand (id ); 2706dec -> addImmediateOperand (member ); 2707dec -> addImmediateOperand (decoration ); 2708for (auto string :strings ) 2709dec -> addStringOperand (string ); 2710 2711decorations .insert (std::unique_ptr < Instruction > (dec )); 2712} 2713 2714void Builder ::addInstruction (std::unique_ptr < Instruction > inst ) { 2715// Phis must appear first in their block, don't insert line tracking instructions 2716// in front of them, just add the OpPhi and return. 2717if (inst -> getOpCode ()== Op ::OpPhi ) { 2718buildPoint -> addInstruction (std::move (inst )); 2719return ; 2720 } 2721// Optionally insert OpDebugScope 2722if (emitNonSemanticShaderDebugInfo && dirtyScopeTracker ) { 2723if (buildPoint -> updateDebugScope (currentDebugScopeId .top ())) { 2724auto scopeInst = std::make_unique < Instruction > (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 2725scopeInst -> reserveOperands (3 ); 2726scopeInst -> addIdOperand (nonSemanticShaderDebugInfo ); 2727scopeInst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugScope ); 2728scopeInst -> addIdOperand (currentDebugScopeId .top ()); 2729buildPoint -> addInstruction (std::move (scopeInst )); 2730 } 2731 2732dirtyScopeTracker = false; 2733 } 2734 2735// Insert OpLine/OpDebugLine if the debug source location has changed 2736if (trackDebugInfo && dirtyLineTracker ) { 2737if (buildPoint -> updateDebugSourceLocation (currentLine ,0 ,currentFileId )) { 2738if (emitSpirvDebugInfo ) { 2739auto lineInst = std::make_unique < Instruction > (Op ::OpLine ); 2740lineInst -> reserveOperands (3 ); 2741lineInst -> addIdOperand (currentFileId ); 2742lineInst -> addImmediateOperand (currentLine ); 2743lineInst -> addImmediateOperand (0 ); 2744buildPoint -> addInstruction (std::move (lineInst )); 2745 } 2746if (emitNonSemanticShaderDebugInfo ) { 2747auto lineInst = std::make_unique < Instruction > (getUniqueId (),makeVoidType (),Op ::OpExtInst ); 2748lineInst -> reserveOperands (7 ); 2749lineInst -> addIdOperand (nonSemanticShaderDebugInfo ); 2750lineInst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugLine ); 2751lineInst -> addIdOperand (makeDebugSource (currentFileId )); 2752lineInst -> addIdOperand (makeUintConstant (currentLine )); 2753lineInst -> addIdOperand (makeUintConstant (currentLine )); 2754lineInst -> addIdOperand (makeUintConstant (0 )); 2755lineInst -> addIdOperand (makeUintConstant (0 )); 2756buildPoint -> addInstruction (std::move (lineInst )); 2757 } 2758 } 2759 2760dirtyLineTracker = false; 2761 } 2762 2763buildPoint -> addInstruction (std::move (inst )); 2764} 2765 2766void Builder ::addInstructionNoDebugInfo (std::unique_ptr < Instruction > inst ) { 2767buildPoint -> addInstruction (std::move (inst )); 2768} 2769 2770// Comments in header 2771Function * Builder ::makeEntryPoint (const char * entryPoint ) 2772{ 2773assert (!entryPointFunction ); 2774 2775auto const returnType = makeVoidType (); 2776 2777restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo ; 2778if (sourceLang == spv::SourceLanguage ::HLSL ) { 2779emitNonSemanticShaderDebugInfo = false; 2780 } 2781 2782Block * entry = nullptr ; 2783entryPointFunction = makeFunctionEntry (NoPrecision ,returnType ,entryPoint ,LinkageType ::Max , {}, {},& entry ); 2784 2785emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo ; 2786 2787return entryPointFunction ; 2788} 2789 2790// Comments in header 2791Function * Builder ::makeFunctionEntry (Decoration precision ,Id returnType ,const char * name ,LinkageType linkType , 2792const std::vector < Id >& paramTypes , 2793const std::vector < std::vector < Decoration >>& decorations ,Block ** entry ) 2794{ 2795// Make the function and initial instructions in it 2796Id typeId = makeFunctionType (returnType ,paramTypes ); 2797Id firstParamId = paramTypes .size ()== 0 ?0 :getUniqueIds ((int )paramTypes .size ()); 2798Id funcId = getUniqueId (); 2799Function * function = new Function (funcId ,returnType ,typeId ,firstParamId ,linkType ,name ,module ); 2800 2801// Set up the precisions 2802setPrecision (function -> getId (),precision ); 2803function -> setReturnPrecision (precision ); 2804for (unsigned p = 0 ;p < (unsigned )decorations .size ();++ p ) { 2805for (int d = 0 ;d < (int )decorations [p ].size ();++ d ) { 2806addDecoration (firstParamId + p ,decorations [p ][d ]); 2807function -> addParamPrecision (p ,decorations [p ][d ]); 2808 } 2809 } 2810 2811// reset last debug scope 2812if (emitNonSemanticShaderDebugInfo ) { 2813dirtyScopeTracker = true; 2814 } 2815 2816// CFG 2817assert (entry != nullptr ); 2818* entry = new Block (getUniqueId (),* function ); 2819function -> addBlock (* entry ); 2820setBuildPoint (* entry ); 2821 2822if (name ) 2823addName (function -> getId (),name ); 2824 2825functions .push_back (std::unique_ptr < Function > (function )); 2826 2827return function ; 2828} 2829 2830void Builder ::setupFunctionDebugInfo (Function * function ,const char * name ,const std::vector < Id >& paramTypes , 2831const std::vector < char const *>& paramNames ) 2832{ 2833 2834if (!emitNonSemanticShaderDebugInfo ) 2835return ; 2836 2837Id nameId = getStringId (unmangleFunctionName (name )); 2838Id funcTypeId = function -> getFuncTypeId (); 2839assert (getDebugType (funcTypeId )!= NoType ); 2840Id funcId = function -> getId (); 2841 2842assert (funcId != 0 ); 2843 2844// Make the debug function instruction 2845Id debugFuncId = makeDebugFunction (function ,nameId ,funcTypeId ); 2846debugFuncIdLookup [funcId ]= debugFuncId ; 2847currentDebugScopeId .push (debugFuncId ); 2848 2849// DebugScope and DebugLine for parameter DebugDeclares 2850assert (paramTypes .size ()== paramNames .size ()); 2851if ((int )paramTypes .size ()> 0 ) { 2852Id firstParamId = function -> getParamId (0 ); 2853 2854for (size_t p = 0 ;p < paramTypes .size ();++ p ) { 2855bool passByRef = false; 2856Id paramTypeId = paramTypes [p ]; 2857 2858// For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type. 2859if (isPointerType (paramTypeId )|| isArrayType (paramTypeId )) { 2860passByRef = true; 2861paramTypeId = getContainedTypeId (paramTypeId ); 2862 } 2863 2864auto const & paramName = paramNames [p ]; 2865auto const debugLocalVariableId = createDebugLocalVariable (getDebugType (paramTypeId ),paramName ,p + 1 ); 2866auto const paramId = static_cast < Id > (firstParamId + p ); 2867 2868if (passByRef ) { 2869makeDebugDeclare (debugLocalVariableId ,paramId ); 2870 }else { 2871makeDebugValue (debugLocalVariableId ,paramId ); 2872 } 2873 } 2874 } 2875 2876// Clear debug scope stack 2877if (emitNonSemanticShaderDebugInfo ) 2878currentDebugScopeId .pop (); 2879} 2880 2881Id Builder ::makeDebugFunction ([[maybe_unused ]]Function * function ,Id nameId ,Id funcTypeId ) 2882{ 2883assert (function != nullptr ); 2884assert (nameId != 0 ); 2885assert (funcTypeId != 0 ); 2886assert (getDebugType (funcTypeId )!= NoType ); 2887 2888Id funcId = getUniqueId (); 2889auto type = new Instruction (funcId ,makeVoidType (),Op ::OpExtInst ); 2890type -> reserveOperands (11 ); 2891type -> addIdOperand (nonSemanticShaderDebugInfo ); 2892type -> addImmediateOperand (NonSemanticShaderDebugInfoDebugFunction ); 2893type -> addIdOperand (nameId ); 2894type -> addIdOperand (getDebugType (funcTypeId )); 2895type -> addIdOperand (makeDebugSource (currentFileId ));// TODO: This points to file of definition instead of declaration 2896type -> addIdOperand (makeUintConstant (currentLine ));// TODO: This points to line of definition instead of declaration 2897type -> addIdOperand (makeUintConstant (0 ));// column 2898type -> addIdOperand (makeDebugCompilationUnit ());// scope 2899type -> addIdOperand (nameId );// linkage name 2900type -> addIdOperand (makeUintConstant (NonSemanticShaderDebugInfoFlagIsPublic )); 2901type -> addIdOperand (makeUintConstant (currentLine )); 2902constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (type )); 2903module .mapInstruction (type ); 2904return funcId ; 2905} 2906 2907Id Builder ::makeDebugEntryPoint (Function * function ,const char * compilerSignature ,const char * commandLineArguments , 2908const char * currentWorkingDirectory ) 2909{ 2910assert (function != nullptr ); 2911assert (compilerSignature != nullptr ); 2912assert (commandLineArguments != nullptr ); 2913 2914const Id debugFunction = getDebugFunction (function -> getId ()); 2915assert (debugFunction != NoResult ); 2916 2917if (currentWorkingDirectory != nullptr ) 2918requireNonSemanticShaderDebugInfoVersion (NonSemanticShaderDebugInfoVersion ); 2919 2920const Id entryPointId = getUniqueId (); 2921auto entryPoint = new Instruction (entryPointId ,makeVoidType (),Op ::OpExtInst ); 2922entryPoint -> reserveOperands (currentWorkingDirectory == nullptr ?6 :7 ); 2923entryPoint -> addIdOperand (nonSemanticShaderDebugInfo ); 2924entryPoint -> addImmediateOperand (NonSemanticShaderDebugInfoDebugEntryPoint ); 2925entryPoint -> addIdOperand (debugFunction ); 2926entryPoint -> addIdOperand (makeDebugCompilationUnit ()); 2927entryPoint -> addIdOperand (getStringId (compilerSignature )); 2928entryPoint -> addIdOperand (getStringId (commandLineArguments )); 2929if (currentWorkingDirectory != nullptr ) 2930entryPoint -> addIdOperand (getStringId (currentWorkingDirectory )); 2931constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (entryPoint )); 2932module .mapInstruction (entryPoint ); 2933return entryPointId ; 2934} 2935 2936Id Builder ::makeDebugLexicalBlock (uint32_t line ,uint32_t column ) { 2937assert (!currentDebugScopeId .empty ()); 2938 2939Id lexId = getUniqueId (); 2940auto lex = new Instruction (lexId ,makeVoidType (),Op ::OpExtInst ); 2941lex -> reserveOperands (6 ); 2942lex -> addIdOperand (nonSemanticShaderDebugInfo ); 2943lex -> addImmediateOperand (NonSemanticShaderDebugInfoDebugLexicalBlock ); 2944lex -> addIdOperand (makeDebugSource (currentFileId )); 2945lex -> addIdOperand (makeUintConstant (line )); 2946lex -> addIdOperand (makeUintConstant (column ));// column 2947lex -> addIdOperand (currentDebugScopeId .top ());// scope 2948constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (lex )); 2949module .mapInstruction (lex ); 2950return lexId ; 2951} 2952 2953std::string Builder ::unmangleFunctionName (std::string const & name )const 2954{ 2955assert (name .length ()> 0 ); 2956 2957if (name .rfind ('(' )!= std::string::npos ) { 2958return name .substr (0 ,name .rfind ('(' )); 2959 }else { 2960return name ; 2961 } 2962} 2963 2964// Comments in header 2965void Builder ::makeReturn (bool implicit ,Id retVal ) 2966{ 2967if (retVal ) { 2968Instruction * inst = new Instruction (NoResult ,NoType ,Op ::OpReturnValue ); 2969inst -> addIdOperand (retVal ); 2970addInstruction (std::unique_ptr < Instruction > (inst )); 2971 }else 2972addInstruction (std::unique_ptr < Instruction > (new Instruction (NoResult ,NoType ,Op ::OpReturn ))); 2973 2974if (!implicit ) 2975createAndSetNoPredecessorBlock ("post-return" ); 2976} 2977 2978// Comments in header 2979void Builder ::enterLexicalBlock (uint32_t line ,uint32_t column ) 2980{ 2981if (!emitNonSemanticShaderDebugInfo ) { 2982return ; 2983 } 2984 2985// Generate new lexical scope debug instruction 2986Id lexId = makeDebugLexicalBlock (line ,column ); 2987currentDebugScopeId .push (lexId ); 2988dirtyScopeTracker = true; 2989} 2990 2991// Comments in header 2992void Builder ::leaveLexicalBlock () 2993{ 2994if (!emitNonSemanticShaderDebugInfo ) { 2995return ; 2996 } 2997 2998// Pop current scope from stack and clear current scope 2999currentDebugScopeId .pop (); 3000dirtyScopeTracker = true; 3001} 3002 3003// Comments in header 3004void Builder ::enterFunction (Function const * function ) 3005{ 3006currentFunction = function ; 3007 3008// Save and disable debugInfo for HLSL entry point function. It is a wrapper 3009// function with no user code in it. 3010restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo ; 3011if (sourceLang == spv::SourceLanguage ::HLSL && function == entryPointFunction ) { 3012emitNonSemanticShaderDebugInfo = false; 3013 } 3014 3015if (emitNonSemanticShaderDebugInfo ) { 3016// Initialize scope state 3017Id funcId = function -> getFuncId (); 3018Id debugFuncId = getDebugFunction (funcId ); 3019currentDebugScopeId .push (debugFuncId ); 3020// Create DebugFunctionDefinition 3021 spv::Id resultId = getUniqueId (); 3022Instruction * defInst = new Instruction (resultId ,makeVoidType (),Op ::OpExtInst ); 3023defInst -> reserveOperands (4 ); 3024defInst -> addIdOperand (nonSemanticShaderDebugInfo ); 3025defInst -> addImmediateOperand (NonSemanticShaderDebugInfoDebugFunctionDefinition ); 3026defInst -> addIdOperand (debugFuncId ); 3027defInst -> addIdOperand (funcId ); 3028addInstruction (std::unique_ptr < Instruction > (defInst )); 3029 } 3030 3031if (auto linkType = function -> getLinkType ();linkType != LinkageType ::Max ) { 3032Id funcId = function -> getFuncId (); 3033addCapability (Capability ::Linkage ); 3034addLinkageDecoration (funcId ,function -> getExportName (),linkType ); 3035 } 3036} 3037 3038// Comments in header 3039void Builder ::leaveFunction () 3040{ 3041Block * block = buildPoint ; 3042Function & function = buildPoint -> getParent (); 3043assert (block ); 3044 3045// If our function did not contain a return, add a return void now. 3046if (!block -> isTerminated ()) { 3047if (function .getReturnType ()== makeVoidType ()) 3048makeReturn (true); 3049else { 3050makeReturn (true,createUndefined (function .getReturnType ())); 3051 } 3052 } 3053 3054// Clear function scope from debug scope stack 3055if (emitNonSemanticShaderDebugInfo ) 3056currentDebugScopeId .pop (); 3057 3058emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo ; 3059 3060// Clear current function record 3061currentFunction = nullptr ; 3062} 3063 3064// Comments in header 3065void Builder ::makeStatementTerminator (spv::Op opcode ,const char * name ) 3066{ 3067addInstruction (std::unique_ptr < Instruction > (new Instruction (opcode ))); 3068createAndSetNoPredecessorBlock (name ); 3069} 3070 3071// Comments in header 3072void Builder ::makeStatementTerminator (spv::Op opcode ,const std::vector < Id >& operands ,const char * name ) 3073{ 3074// It's assumed that the terminator instruction is always of void return type 3075// However in future if there is a need for non void return type, new helper 3076// methods can be created. 3077createNoResultOp (opcode ,operands ); 3078createAndSetNoPredecessorBlock (name ); 3079} 3080 3081void Builder ::createConstVariable (Id type ,const char * name ,Id constant ,bool isGlobal ) 3082{ 3083if (emitNonSemanticShaderDebugInfo ) { 3084Id debugType = getDebugType (type ); 3085if (isGlobal ) { 3086createDebugGlobalVariable (debugType ,name ,constant ); 3087 } 3088else { 3089auto debugLocal = createDebugLocalVariable (debugType ,name ); 3090makeDebugValue (debugLocal ,constant ); 3091 } 3092 } 3093} 3094 3095// Comments in header 3096Id Builder ::createUntypedVariable (Decoration precision ,StorageClass storageClass ,const char * name ,Id dataType , 3097Id initializer ) 3098{ 3099Id resultUntypedPointerType = makeUntypedPointer (storageClass ); 3100Instruction * inst = new Instruction (getUniqueId (),resultUntypedPointerType ,Op ::OpUntypedVariableKHR ); 3101inst -> addImmediateOperand (storageClass ); 3102if (dataType != NoResult ) { 3103Id dataPointerType = makePointer (storageClass ,dataType ); 3104inst -> addIdOperand (dataPointerType ); 3105 } 3106if (initializer != NoResult ) 3107inst -> addIdOperand (initializer ); 3108 3109switch (storageClass ) { 3110case StorageClass ::Function : 3111// Validation rules require the declaration in the entry block 3112buildPoint -> getParent ().addLocalVariable (std::unique_ptr < Instruction > (inst )); 3113break ; 3114default : 3115constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (inst )); 3116module .mapInstruction (inst ); 3117break ; 3118 } 3119 3120if (name ) 3121addName (inst -> getResultId (),name ); 3122setPrecision (inst -> getResultId (),precision ); 3123 3124return inst -> getResultId (); 3125} 3126 3127// Comments in header 3128Id Builder ::createVariable (Decoration precision ,StorageClass storageClass ,Id type ,const char * name ,Id initializer , 3129bool const compilerGenerated ) 3130{ 3131Id pointerType = makePointer (storageClass ,type ); 3132Instruction * inst = new Instruction (getUniqueId (),pointerType ,Op ::OpVariable ); 3133inst -> addImmediateOperand (storageClass ); 3134if (initializer != NoResult ) 3135inst -> addIdOperand (initializer ); 3136 3137if (storageClass == StorageClass ::Function ) { 3138// Validation rules require the declaration in the entry block 3139buildPoint -> getParent ().addLocalVariable (std::unique_ptr < Instruction > (inst )); 3140 } 3141else { 3142constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (inst )); 3143module .mapInstruction (inst ); 3144 } 3145 3146if (emitNonSemanticShaderDebugInfo && !compilerGenerated ) 3147 { 3148// For debug info, we prefer respecting how the variable is declared in source code. 3149// We may emulate some local variables as global variable with private storage in SPIR-V, but we still want to 3150// treat them as local variables in debug info. 3151if (storageClass == StorageClass ::Function || (currentFunction && storageClass == StorageClass ::Private )) { 3152auto const debugLocalVariableId = createDebugLocalVariable (getDebugType (type ),name ); 3153makeDebugDeclare (debugLocalVariableId ,inst -> getResultId ()); 3154 } 3155else { 3156createDebugGlobalVariable (getDebugType (type ),name ,inst -> getResultId ()); 3157 } 3158 } 3159 3160if (name ) 3161addName (inst -> getResultId (),name ); 3162setPrecision (inst -> getResultId (),precision ); 3163 3164return inst -> getResultId (); 3165} 3166 3167// Comments in header 3168Id Builder ::createUndefined (Id type ) 3169{ 3170Instruction * inst = new Instruction (getUniqueId (),type ,Op ::OpUndef ); 3171addInstruction (std::unique_ptr < Instruction > (inst )); 3172return inst -> getResultId (); 3173} 3174 3175// av/vis/nonprivate are unnecessary and illegal for some storage classes. 3176spv::MemoryAccessMask Builder ::sanitizeMemoryAccessForStorageClass (spv::MemoryAccessMask memoryAccess ,StorageClass sc ) 3177const 3178{ 3179switch (sc ) { 3180case spv::StorageClass ::Uniform : 3181case spv::StorageClass ::Workgroup : 3182case spv::StorageClass ::StorageBuffer : 3183case spv::StorageClass ::PhysicalStorageBufferEXT : 3184break ; 3185default : 3186memoryAccess = spv::MemoryAccessMask (memoryAccess & 3187 ~(spv::MemoryAccessMask ::MakePointerAvailableKHR | 3188 spv::MemoryAccessMask ::MakePointerVisibleKHR | 3189 spv::MemoryAccessMask ::NonPrivatePointerKHR )); 3190break ; 3191 } 3192return memoryAccess ; 3193} 3194 3195// Comments in header 3196void Builder ::createStore (Id rValue ,Id lValue , spv::MemoryAccessMask memoryAccess , spv::Scope scope , 3197unsigned int alignment ) 3198{ 3199Instruction * store = nullptr ; 3200if (isUntypedPointer (lValue )) 3201store = createDescHeapLoadStoreBaseRemap (lValue ,Op ::OpStore ); 3202else { 3203store = new Instruction (Op ::OpStore ); 3204store -> reserveOperands (2 ); 3205store -> addIdOperand (lValue ); 3206 } 3207store -> addIdOperand (rValue ); 3208 3209memoryAccess = sanitizeMemoryAccessForStorageClass (memoryAccess ,getStorageClass (lValue )); 3210 3211if (memoryAccess != MemoryAccessMask ::MaskNone ) { 3212store -> addImmediateOperand (memoryAccess ); 3213if (anySet (memoryAccess , spv::MemoryAccessMask ::Aligned )) { 3214store -> addImmediateOperand (alignment ); 3215 } 3216if (anySet (memoryAccess , spv::MemoryAccessMask ::MakePointerAvailableKHR )) { 3217store -> addIdOperand (makeUintConstant (scope )); 3218 } 3219 } 3220 3221addInstruction (std::unique_ptr < Instruction > (store )); 3222} 3223 3224// Comments in header 3225Id Builder ::createLoad (Id lValue , spv::Decoration precision , spv::MemoryAccessMask memoryAccess , 3226 spv::Scope scope ,unsigned int alignment ) 3227{ 3228Instruction * load = nullptr ; 3229if (isUntypedPointer (lValue )) 3230load = createDescHeapLoadStoreBaseRemap (lValue ,Op ::OpLoad ); 3231else { 3232load = new Instruction (getUniqueId (),getDerefTypeId (lValue ),Op ::OpLoad ); 3233load -> addIdOperand (lValue ); 3234 } 3235 3236memoryAccess = sanitizeMemoryAccessForStorageClass (memoryAccess ,getStorageClass (lValue )); 3237 3238if (memoryAccess != MemoryAccessMask ::MaskNone ) { 3239load -> addImmediateOperand (memoryAccess ); 3240if (anySet (memoryAccess , spv::MemoryAccessMask ::Aligned )) { 3241load -> addImmediateOperand (alignment ); 3242 } 3243if (anySet (memoryAccess , spv::MemoryAccessMask ::MakePointerVisibleKHR )) { 3244load -> addIdOperand (makeUintConstant (scope )); 3245 } 3246 } 3247 3248addInstruction (std::unique_ptr < Instruction > (load )); 3249setPrecision (load -> getResultId (),precision ); 3250 3251return load -> getResultId (); 3252} 3253 3254Instruction * Builder ::createDescHeapLoadStoreBaseRemap (Id baseId ,Op op ) 3255{ 3256// could only be untypedAccessChain op. 3257 spv::Op instOp = module .getInstruction (baseId )-> getOpCode (); 3258 spv::Id baseVal = baseId ; 3259// base type (from run time array) 3260 spv::Id resultTy = getIdOperand (baseId ,0 ); 3261// Descriptor heap using run time array. 3262if (accessChain .descHeapInfo .descStorageClass != StorageClass ::Max ) 3263resultTy = getIdOperand (resultTy ,0 ); 3264if (instOp != Op ::OpUntypedAccessChainKHR ) { 3265assert (false&& "Not a untyped load type" ); 3266 } 3267 3268Instruction * inst = nullptr ; 3269if (op == Op ::OpStore ) 3270inst = new Instruction (Op ::OpStore ); 3271else { 3272inst = new Instruction (getUniqueId (),resultTy ,Op ::OpLoad ); 3273accessChain .descHeapInfo .descHeapInstId .push_back (inst ); 3274 } 3275inst -> addIdOperand (baseVal ); 3276return inst ; 3277} 3278 3279spv::Id Builder ::getOrCreateDescHeapByteArrayType () 3280{ 3281if (descHeapByteArrayType != NoResult ) 3282return descHeapByteArrayType ; 3283 3284addCapability (Capability ::Int8 ); 3285descHeapByteArrayType = makeRuntimeArray (makeUintType (8 )); 3286addDecoration (descHeapByteArrayType ,Decoration ::ArrayStride ,1 ); 3287return descHeapByteArrayType ; 3288} 3289 3290// Comments in header 3291Id Builder ::createDescHeapAccessChain () 3292{ 3293 std::vector < Id >& heapOffsets = accessChain .descHeapInfo .descHeapIndexChain ; 3294assert (heapOffsets .size ()!= 0 ); 3295 3296Id heapBase = accessChain .base ; 3297Id heapBaseTy = accessChain .descHeapInfo .descHeapBaseTy ; 3298Id heapBaseOffset = accessChain .descHeapInfo .descHeapBaseOffset ; 3299StorageClass storageClass = (StorageClass )accessChain .descHeapInfo .descStorageClass ; 3300 3301if (heapBaseOffset != NoResult ) { 3302const std::pair < Id ,Id > cacheKey (heapBase ,heapBaseOffset ); 3303auto cachedShiftedBase = descHeapShiftedBaseCache .find (cacheKey ); 3304if (cachedShiftedBase != descHeapShiftedBaseCache .end ()) { 3305heapBase = cachedShiftedBase -> second ; 3306 }else { 3307Id shiftedBaseId = getUniqueId (); 3308const std::vector < Id > shiftedBaseOffsets (1 ,heapBaseOffset ); 3309heapBase = createUntypedAccessChain (getOrCreateDescHeapByteArrayType (),heapBase , 3310shiftedBaseOffsets ,shiftedBaseId ); 3311descHeapShiftedBaseCache .insert (std::make_pair (cacheKey ,heapBase )); 3312 } 3313 } 3314 3315// First descriptor heap access chain 3316Id chain = createUntypedAccessChain (heapBaseTy ,heapBase ,heapOffsets ); 3317 3318// Create OpBufferPointer for loading target buffer descriptor 3319if (storageClass == spv::StorageClass ::Uniform || storageClass == spv::StorageClass ::StorageBuffer ) { 3320// Create OpBufferPointer for the descriptor, then use a typed access chain for buffer data. 3321Id descriptorTy = accessChain .descHeapInfo .descTy ; 3322Id bufferPtrTy = makePointer (storageClass ,descriptorTy ); 3323Instruction * bufferDataPtr = new Instruction (getUniqueId (),bufferPtrTy ,Op ::OpBufferPointerEXT ); 3324bufferDataPtr -> addIdOperand (chain ); 3325addInstruction (std::unique_ptr < Instruction > (bufferDataPtr )); 3326if (accessChain .descHeapInfo .descWriteonly ) 3327addDecoration (bufferDataPtr -> getResultId (),Decoration ::NonReadable ); 3328if (accessChain .descHeapInfo .descReadonly && storageClass == StorageClass ::StorageBuffer ) 3329addDecoration (bufferDataPtr -> getResultId (),Decoration ::NonWritable ); 3330 3331 std::vector < Id >& offsets = accessChain .indexChain ; 3332if (offsets .empty ()) 3333return bufferDataPtr -> getResultId (); 3334 3335// Form a second, typed access chain for accessing buffer data. 3336Id resultTy = descriptorTy ; 3337for (int i = 0 ;i < (int )offsets .size ();++ i ) { 3338if (isStructType (resultTy )) { 3339assert (isConstantScalar (offsets [i ])); 3340resultTy = getContainedTypeId (resultTy ,getConstantScalar (offsets [i ])); 3341 }else 3342resultTy = getContainedTypeId (resultTy ,offsets [i ]); 3343 } 3344resultTy = makePointer (storageClass ,resultTy ); 3345 3346Instruction * bufferChain = new Instruction (getUniqueId (),resultTy ,Op ::OpAccessChain ); 3347bufferChain -> reserveOperands (offsets .size ()+ 1 ); 3348bufferChain -> addIdOperand (bufferDataPtr -> getResultId ()); 3349for (int i = 0 ;i < (int )offsets .size ();++ i ) 3350bufferChain -> addIdOperand (offsets [i ]); 3351addInstruction (std::unique_ptr < Instruction > (bufferChain )); 3352return bufferChain -> getResultId (); 3353 } 3354 3355return chain ; 3356} 3357 3358// Comments in header 3359Id Builder ::createAccessChain (StorageClass storageClass ,Id base ,const std::vector < Id >& offsets ) 3360{ 3361// Figure out the final resulting type. 3362Id typeId = getResultingAccessChainType (); 3363typeId = makePointer (storageClass ,typeId ); 3364 3365// Make the instruction 3366Instruction * chain = new Instruction (getUniqueId (),typeId ,Op ::OpAccessChain ); 3367chain -> reserveOperands (offsets .size ()+ 1 ); 3368chain -> addIdOperand (base ); 3369for (int i = 0 ;i < (int )offsets .size ();++ i ) 3370chain -> addIdOperand (offsets [i ]); 3371addInstruction (std::unique_ptr < Instruction > (chain )); 3372 3373return chain -> getResultId (); 3374} 3375 3376// Comments in header 3377Id Builder ::createUntypedAccessChain (Id resultType ,Id base ,const std::vector < Id >& offsets ,Id resultId ) 3378{ 3379if (resultId == NoResult ) 3380resultId = getUniqueId (); 3381 3382Instruction * chain = new Instruction (resultId ,makeUntypedPointer (getStorageClass (base )), 3383Op ::OpUntypedAccessChainKHR ); 3384chain -> reserveOperands (offsets .size ()+ 2 ); 3385chain -> addIdOperand (resultType ); 3386chain -> addIdOperand (base ); 3387for (int i = 0 ;i < (int )offsets .size ();++ i ) 3388chain -> addIdOperand (offsets [i ]); 3389addInstruction (std::unique_ptr < Instruction > (chain )); 3390 3391return chain -> getResultId (); 3392} 3393 3394Id Builder ::createArrayLength (Id base ,unsigned int member ,unsigned int bits ) 3395{ 3396 spv::Id intType = makeUintType (bits ); 3397Instruction * length = new Instruction (getUniqueId (),intType ,Op ::OpArrayLength ); 3398length -> reserveOperands (2 ); 3399length -> addIdOperand (base ); 3400length -> addImmediateOperand (member ); 3401addInstruction (std::unique_ptr < Instruction > (length )); 3402 3403return length -> getResultId (); 3404} 3405 3406Id Builder ::createCooperativeMatrixLengthKHR (Id type ) 3407{ 3408 spv::Id intType = makeUintType (32 ); 3409 3410// Generate code for spec constants if in spec constant operation 3411// generation mode. 3412if (generatingOpCodeForSpecConst ) { 3413return createSpecConstantOp (Op ::OpCooperativeMatrixLengthKHR ,intType , std::vector < Id > (1 ,type ), std::vector < Id > ()); 3414 } 3415 3416Instruction * length = new Instruction (getUniqueId (),intType ,Op ::OpCooperativeMatrixLengthKHR ); 3417length -> addIdOperand (type ); 3418addInstruction (std::unique_ptr < Instruction > (length )); 3419 3420return length -> getResultId (); 3421} 3422 3423Id Builder ::createCooperativeMatrixLengthNV (Id type ) 3424{ 3425 spv::Id intType = makeUintType (32 ); 3426 3427// Generate code for spec constants if in spec constant operation 3428// generation mode. 3429if (generatingOpCodeForSpecConst ) { 3430return createSpecConstantOp (Op ::OpCooperativeMatrixLengthNV ,intType , std::vector < Id > (1 ,type ), std::vector < Id > ()); 3431 } 3432 3433Instruction * length = new Instruction (getUniqueId (),intType ,Op ::OpCooperativeMatrixLengthNV ); 3434length -> addIdOperand (type ); 3435addInstruction (std::unique_ptr < Instruction > (length )); 3436 3437return length -> getResultId (); 3438} 3439 3440Id Builder ::createCompositeExtract (Id composite ,Id typeId ,unsigned index ) 3441{ 3442// Generate code for spec constants if in spec constant operation 3443// generation mode. 3444if (generatingOpCodeForSpecConst ) { 3445return createSpecConstantOp (Op ::OpCompositeExtract ,typeId , std::vector < Id > (1 ,composite ), 3446 std::vector < Id > (1 ,index )); 3447 } 3448Instruction * extract = new Instruction (getUniqueId (),typeId ,Op ::OpCompositeExtract ); 3449extract -> reserveOperands (2 ); 3450extract -> addIdOperand (composite ); 3451extract -> addImmediateOperand (index ); 3452addInstruction (std::unique_ptr < Instruction > (extract )); 3453 3454return extract -> getResultId (); 3455} 3456 3457Id Builder ::createCompositeExtract (Id composite ,Id typeId ,const std::vector < unsigned >& indexes ) 3458{ 3459// Generate code for spec constants if in spec constant operation 3460// generation mode. 3461if (generatingOpCodeForSpecConst ) { 3462return createSpecConstantOp (Op ::OpCompositeExtract ,typeId , std::vector < Id > (1 ,composite ),indexes ); 3463 } 3464Instruction * extract = new Instruction (getUniqueId (),typeId ,Op ::OpCompositeExtract ); 3465extract -> reserveOperands (indexes .size ()+ 1 ); 3466extract -> addIdOperand (composite ); 3467for (int i = 0 ;i < (int )indexes .size ();++ i ) 3468extract -> addImmediateOperand (indexes [i ]); 3469addInstruction (std::unique_ptr < Instruction > (extract )); 3470 3471return extract -> getResultId (); 3472} 3473 3474Id Builder ::createCompositeInsert (Id object ,Id composite ,Id typeId ,unsigned index ) 3475{ 3476Instruction * insert = new Instruction (getUniqueId (),typeId ,Op ::OpCompositeInsert ); 3477insert -> reserveOperands (3 ); 3478insert -> addIdOperand (object ); 3479insert -> addIdOperand (composite ); 3480insert -> addImmediateOperand (index ); 3481addInstruction (std::unique_ptr < Instruction > (insert )); 3482 3483return insert -> getResultId (); 3484} 3485 3486Id Builder ::createCompositeInsert (Id object ,Id composite ,Id typeId ,const std::vector < unsigned >& indexes ) 3487{ 3488Instruction * insert = new Instruction (getUniqueId (),typeId ,Op ::OpCompositeInsert ); 3489insert -> reserveOperands (indexes .size ()+ 2 ); 3490insert -> addIdOperand (object ); 3491insert -> addIdOperand (composite ); 3492for (int i = 0 ;i < (int )indexes .size ();++ i ) 3493insert -> addImmediateOperand (indexes [i ]); 3494addInstruction (std::unique_ptr < Instruction > (insert )); 3495 3496return insert -> getResultId (); 3497} 3498 3499Id Builder ::createVectorExtractDynamic (Id vector ,Id typeId ,Id componentIndex ) 3500{ 3501Instruction * extract = new Instruction (getUniqueId (),typeId ,Op ::OpVectorExtractDynamic ); 3502extract -> reserveOperands (2 ); 3503extract -> addIdOperand (vector ); 3504extract -> addIdOperand (componentIndex ); 3505addInstruction (std::unique_ptr < Instruction > (extract )); 3506 3507return extract -> getResultId (); 3508} 3509 3510Id Builder ::createVectorInsertDynamic (Id vector ,Id typeId ,Id component ,Id componentIndex ) 3511{ 3512Instruction * insert = new Instruction (getUniqueId (),typeId ,Op ::OpVectorInsertDynamic ); 3513insert -> reserveOperands (3 ); 3514insert -> addIdOperand (vector ); 3515insert -> addIdOperand (component ); 3516insert -> addIdOperand (componentIndex ); 3517addInstruction (std::unique_ptr < Instruction > (insert )); 3518 3519return insert -> getResultId (); 3520} 3521 3522// An opcode that has no operands, no result id, and no type 3523void Builder ::createNoResultOp (Op opCode ) 3524{ 3525Instruction * op = new Instruction (opCode ); 3526addInstruction (std::unique_ptr < Instruction > (op )); 3527} 3528 3529// An opcode that has one id operand, no result id, and no type 3530void Builder ::createNoResultOp (Op opCode ,Id operand ) 3531{ 3532Instruction * op = new Instruction (opCode ); 3533op -> addIdOperand (operand ); 3534addInstruction (std::unique_ptr < Instruction > (op )); 3535} 3536 3537// An opcode that has one or more operands, no result id, and no type 3538void Builder ::createNoResultOp (Op opCode ,const std::vector < Id >& operands ) 3539{ 3540Instruction * op = new Instruction (opCode ); 3541op -> reserveOperands (operands .size ()); 3542for (auto id :operands ) { 3543op -> addIdOperand (id ); 3544 } 3545addInstruction (std::unique_ptr < Instruction > (op )); 3546} 3547 3548// An opcode that has multiple operands, no result id, and no type 3549void Builder ::createNoResultOp (Op opCode ,const std::vector < IdImmediate >& operands ) 3550{ 3551Instruction * op = new Instruction (opCode ); 3552op -> reserveOperands (operands .size ()); 3553for (auto it = operands .cbegin ();it != operands .cend ();++ it ) { 3554if (it -> isId ) 3555op -> addIdOperand (it -> word ); 3556else 3557op -> addImmediateOperand (it -> word ); 3558 } 3559addInstruction (std::unique_ptr < Instruction > (op )); 3560} 3561 3562void Builder ::createControlBarrier (Scope execution ,Scope memory ,MemorySemanticsMask semantics ) 3563{ 3564Instruction * op = new Instruction (Op ::OpControlBarrier ); 3565op -> reserveOperands (3 ); 3566op -> addIdOperand (makeUintConstant (execution )); 3567op -> addIdOperand (makeUintConstant (memory )); 3568op -> addIdOperand (makeUintConstant (semantics )); 3569addInstruction (std::unique_ptr < Instruction > (op )); 3570} 3571 3572void Builder ::createSplitControlBarrier (Op opcode ,Scope execution ,Scope memory ,MemorySemanticsMask semantics ) 3573{ 3574Instruction * op = new Instruction (opcode ); 3575op -> reserveOperands (3 ); 3576op -> addIdOperand (makeUintConstant (execution )); 3577op -> addIdOperand (makeUintConstant (memory )); 3578op -> addIdOperand (makeUintConstant (semantics )); 3579addInstruction (std::unique_ptr < Instruction > (op )); 3580} 3581 3582void Builder ::createMemoryBarrier (Scope executionScope ,MemorySemanticsMask memorySemantics ) 3583{ 3584Instruction * op = new Instruction (Op ::OpMemoryBarrier ); 3585op -> reserveOperands (2 ); 3586op -> addIdOperand (makeUintConstant ((unsigned )executionScope )); 3587op -> addIdOperand (makeUintConstant ((unsigned )memorySemantics )); 3588addInstruction (std::unique_ptr < Instruction > (op )); 3589} 3590 3591// An opcode that has one operands, a result id, and a type 3592Id Builder ::createUnaryOp (Op opCode ,Id typeId ,Id operand ) 3593{ 3594// Generate code for spec constants if in spec constant operation 3595// generation mode. 3596if (generatingOpCodeForSpecConst ) { 3597return createSpecConstantOp (opCode ,typeId , std::vector < Id > (1 ,operand ), std::vector < Id > ()); 3598 } 3599Instruction * op = new Instruction (getUniqueId (),typeId ,opCode ); 3600op -> addIdOperand (operand ); 3601addInstruction (std::unique_ptr < Instruction > (op )); 3602 3603return op -> getResultId (); 3604} 3605 3606Id Builder ::createBinOp (Op opCode ,Id typeId ,Id left ,Id right ) 3607{ 3608// Generate code for spec constants if in spec constant operation 3609// generation mode. 3610if (generatingOpCodeForSpecConst ) { 3611 std::vector < Id > operands (2 ); 3612operands [0 ]= left ;operands [1 ]= right ; 3613return createSpecConstantOp (opCode ,typeId ,operands , std::vector < Id > ()); 3614 } 3615Instruction * op = new Instruction (getUniqueId (),typeId ,opCode ); 3616op -> reserveOperands (2 ); 3617op -> addIdOperand (left ); 3618op -> addIdOperand (right ); 3619addInstruction (std::unique_ptr < Instruction > (op )); 3620 3621return op -> getResultId (); 3622} 3623 3624Id Builder ::createTriOp (Op opCode ,Id typeId ,Id op1 ,Id op2 ,Id op3 ) 3625{ 3626// Generate code for spec constants if in spec constant operation 3627// generation mode. 3628if (generatingOpCodeForSpecConst ) { 3629 std::vector < Id > operands (3 ); 3630operands [0 ]= op1 ; 3631operands [1 ]= op2 ; 3632operands [2 ]= op3 ; 3633return createSpecConstantOp ( 3634opCode ,typeId ,operands , std::vector < Id > ()); 3635 } 3636Instruction * op = new Instruction (getUniqueId (),typeId ,opCode ); 3637op -> reserveOperands (3 ); 3638op -> addIdOperand (op1 ); 3639op -> addIdOperand (op2 ); 3640op -> addIdOperand (op3 ); 3641addInstruction (std::unique_ptr < Instruction > (op )); 3642 3643return op -> getResultId (); 3644} 3645 3646Id Builder ::createConstData (Op opCode ,Id typeId ,const std::vector < const char *> operands ) 3647{ 3648Instruction * op = new Instruction (getUniqueId (),typeId ,opCode ); 3649op -> reserveOperands (operands .size ()); 3650for (auto id :operands ) 3651op -> addStringOperand (id ); 3652module .mapInstruction (op ); 3653constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (op )); 3654 3655return op -> getResultId (); 3656} 3657 3658Id Builder ::createOp (Op opCode ,Id typeId ,const std::vector < Id >& operands ) 3659{ 3660Instruction * op = new Instruction (getUniqueId (),typeId ,opCode ); 3661op -> reserveOperands (operands .size ()); 3662for (auto id :operands ) 3663op -> addIdOperand (id ); 3664addInstruction (std::unique_ptr < Instruction > (op )); 3665 3666return op -> getResultId (); 3667} 3668 3669Id Builder ::createOp (Op opCode ,Id typeId ,const std::vector < IdImmediate >& operands ) 3670{ 3671Instruction * op = new Instruction (getUniqueId (),typeId ,opCode ); 3672op -> reserveOperands (operands .size ()); 3673for (auto it = operands .cbegin ();it != operands .cend ();++ it ) { 3674if (it -> isId ) 3675op -> addIdOperand (it -> word ); 3676else 3677op -> addImmediateOperand (it -> word ); 3678 } 3679addInstruction (std::unique_ptr < Instruction > (op )); 3680 3681return op -> getResultId (); 3682} 3683 3684Id Builder ::createSpecConstantOp (Op opCode ,Id typeId ,const std::vector < Id >& operands , 3685const std::vector < unsigned >& literals ) 3686{ 3687Instruction * op = new Instruction (getUniqueId (),typeId ,Op ::OpSpecConstantOp ); 3688op -> reserveOperands (operands .size ()+ literals .size ()+ 1 ); 3689op -> addImmediateOperand ((unsigned )opCode ); 3690for (auto it = operands .cbegin ();it != operands .cend ();++ it ) 3691op -> addIdOperand (* it ); 3692for (auto it = literals .cbegin ();it != literals .cend ();++ it ) 3693op -> addImmediateOperand (* it ); 3694module .mapInstruction (op ); 3695constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (op )); 3696 3697// OpSpecConstantOp's using 8 or 16 bit types require the associated capability 3698if (containsType (typeId ,Op ::OpTypeInt ,8 )) 3699addCapability (Capability ::Int8 ); 3700if (containsType (typeId ,Op ::OpTypeInt ,16 )) 3701addCapability (Capability ::Int16 ); 3702if (containsType (typeId ,Op ::OpTypeFloat ,16 )) 3703addCapability (Capability ::Float16 ); 3704 3705return op -> getResultId (); 3706} 3707 3708Id Builder ::createSpecConstantAlignTo (Id value ,Id alignment ) 3709{ 3710Id valueModAlignment = createSpecConstantOp (Op ::OpUMod ,makeUintType (32 ), {value ,alignment }, {}); 3711Id paddingToAlignment = createSpecConstantOp (Op ::OpISub ,makeUintType (32 ), {alignment ,valueModAlignment }, {}); 3712Id padding = createSpecConstantOp (Op ::OpUMod ,makeUintType (32 ), {paddingToAlignment ,alignment }, {}); 3713return createSpecConstantOp (Op ::OpIAdd ,makeUintType (32 ), {value ,padding }, {}); 3714} 3715 3716Id Builder ::createSpecConstantSelectMax (Id lhs ,Id rhs ) 3717{ 3718Id lhsGreater = createSpecConstantOp (Op ::OpUGreaterThan ,makeBoolType (), {lhs ,rhs }, {}); 3719return createSpecConstantOp (Op ::OpSelect ,makeUintType (32 ), {lhsGreater ,lhs ,rhs }, {}); 3720} 3721 3722Id Builder ::createFunctionCall (spv::Function * function ,const std::vector < spv::Id >& args ) 3723{ 3724Instruction * op = new Instruction (getUniqueId (),function -> getReturnType (),Op ::OpFunctionCall ); 3725op -> reserveOperands (args .size ()+ 1 ); 3726op -> addIdOperand (function -> getId ()); 3727for (int a = 0 ;a < (int )args .size ();++ a ) 3728op -> addIdOperand (args [a ]); 3729addInstruction (std::unique_ptr < Instruction > (op )); 3730 3731return op -> getResultId (); 3732} 3733 3734// Comments in header 3735Id Builder ::createRvalueSwizzle (Decoration precision ,Id typeId ,Id source ,const std::vector < unsigned >& channels ) 3736{ 3737if (channels .size ()== 1 ) 3738return setPrecision (createCompositeExtract (source ,typeId ,channels .front ()),precision ); 3739 3740if (generatingOpCodeForSpecConst ) { 3741 std::vector < Id > operands (2 ); 3742operands [0 ]= operands [1 ]= source ; 3743return setPrecision (createSpecConstantOp (Op ::OpVectorShuffle ,typeId ,operands ,channels ),precision ); 3744 } 3745Instruction * swizzle = new Instruction (getUniqueId (),typeId ,Op ::OpVectorShuffle ); 3746assert (isVector (source )); 3747swizzle -> reserveOperands (channels .size ()+ 2 ); 3748swizzle -> addIdOperand (source ); 3749swizzle -> addIdOperand (source ); 3750for (int i = 0 ;i < (int )channels .size ();++ i ) 3751swizzle -> addImmediateOperand (channels [i ]); 3752addInstruction (std::unique_ptr < Instruction > (swizzle )); 3753 3754return setPrecision (swizzle -> getResultId (),precision ); 3755} 3756 3757// Comments in header 3758Id Builder ::createLvalueSwizzle (Id typeId ,Id target ,Id source ,const std::vector < unsigned >& channels ) 3759{ 3760if (channels .size ()== 1 && getNumComponents (source )== 1 ) 3761return createCompositeInsert (source ,target ,typeId ,channels .front ()); 3762 3763Instruction * swizzle = new Instruction (getUniqueId (),typeId ,Op ::OpVectorShuffle ); 3764 3765assert (isVector (target )); 3766swizzle -> reserveOperands (2 ); 3767swizzle -> addIdOperand (target ); 3768 3769assert (getNumComponents (source )== channels .size ()); 3770assert (isVector (source )); 3771swizzle -> addIdOperand (source ); 3772 3773// Set up an identity shuffle from the base value to the result value 3774unsigned int components [4 ]; 3775int numTargetComponents = getNumComponents (target ); 3776for (int i = 0 ;i < numTargetComponents ;++ i ) 3777components [i ]= i ; 3778 3779// Punch in the l-value swizzle 3780for (int i = 0 ;i < (int )channels .size ();++ i ) 3781components [channels [i ]]= numTargetComponents + i ; 3782 3783// finish the instruction with these components selectors 3784swizzle -> reserveOperands (numTargetComponents ); 3785for (int i = 0 ;i < numTargetComponents ;++ i ) 3786swizzle -> addImmediateOperand (components [i ]); 3787addInstruction (std::unique_ptr < Instruction > (swizzle )); 3788 3789return swizzle -> getResultId (); 3790} 3791 3792// Comments in header 3793void Builder ::promoteScalar (Decoration precision ,Id & left ,Id & right ) 3794{ 3795// choose direction of promotion (+1 for left to right, -1 for right to left) 3796int direction = !isScalar (right )- !isScalar (left ); 3797 3798auto const & makeVec = [& ](Id component ,Id other ) { 3799if (isCooperativeVector (other )) { 3800return makeCooperativeVectorTypeNV (getTypeId (component ),getCooperativeVectorNumComponents (getTypeId (other ))); 3801 }else { 3802return makeVectorType (getTypeId (component ),getNumComponents (other )); 3803 } 3804 }; 3805 3806if (direction > 0 ) 3807left = smearScalar (precision ,left ,makeVec (left ,right )); 3808else if (direction < 0 ) 3809right = smearScalar (precision ,right ,makeVec (right ,left )); 3810 3811return ; 3812} 3813 3814// Comments in header 3815Id Builder ::smearScalar (Decoration precision ,Id scalar ,Id vectorType ) 3816{ 3817assert (getNumComponents (scalar )== 1 ); 3818assert (getTypeId (scalar )== getScalarTypeId (vectorType )); 3819 3820int numComponents = getNumTypeComponents (vectorType ); 3821if (numComponents == 1 && !isCooperativeVectorType (vectorType )&& !isVectorType (vectorType )) 3822return scalar ; 3823 3824Instruction * smear = nullptr ; 3825if (generatingOpCodeForSpecConst ) { 3826auto members = std::vector < spv::Id > (numComponents ,scalar ); 3827// Sometime even in spec-constant-op mode, the temporary vector created by 3828// promoting a scalar might not be a spec constant. This should depend on 3829// the scalar. 3830// e.g.: 3831// const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; 3832// In such cases, the temporary vector created from a_front_end_const_scalar 3833// is not a spec constant vector, even though the binary operation node is marked 3834// as 'specConstant' and we are in spec-constant-op mode. 3835auto result_id = makeCompositeConstant (vectorType ,members ,isSpecConstant (scalar )); 3836smear = module .getInstruction (result_id ); 3837 }else { 3838bool replicate = (useReplicatedComposites || isCooperativeVectorType (vectorType ))&& (numComponents > 0 ); 3839 3840if (replicate ) { 3841numComponents = 1 ; 3842addCapability (spv::Capability ::ReplicatedCompositesEXT ); 3843addExtension (spv::E_SPV_EXT_replicated_composites ); 3844 } 3845 3846Op opcode = replicate ?Op ::OpCompositeConstructReplicateEXT :Op ::OpCompositeConstruct ; 3847 3848smear = new Instruction (getUniqueId (),vectorType ,opcode ); 3849smear -> reserveOperands (numComponents ); 3850for (int c = 0 ;c < numComponents ;++ c ) 3851smear -> addIdOperand (scalar ); 3852addInstruction (std::unique_ptr < Instruction > (smear )); 3853 } 3854 3855return setPrecision (smear -> getResultId (),precision ); 3856} 3857 3858// Comments in header 3859Id Builder ::createBuiltinCall (Id resultType ,Id builtins ,int entryPoint ,const std::vector < Id >& args ) 3860{ 3861Instruction * inst = new Instruction (getUniqueId (),resultType ,Op ::OpExtInst ); 3862inst -> reserveOperands (args .size ()+ 2 ); 3863inst -> addIdOperand (builtins ); 3864inst -> addImmediateOperand (entryPoint ); 3865for (int arg = 0 ;arg < (int )args .size ();++ arg ) 3866inst -> addIdOperand (args [arg ]); 3867 3868addInstruction (std::unique_ptr < Instruction > (inst )); 3869 3870return inst -> getResultId (); 3871} 3872 3873// Comments in header 3874Id Builder ::createBuiltinCall (Id resultType ,Id builtins ,int entryPoint ,const std::vector < IdImmediate >& args ) 3875{ 3876Instruction * inst = new Instruction (getUniqueId (),resultType ,Op ::OpExtInst ); 3877inst -> reserveOperands (args .size ()+ 2 ); 3878inst -> addIdOperand (builtins ); 3879inst -> addImmediateOperand (entryPoint ); 3880for (auto it = args .cbegin ();it != args .cend ();++ it ) { 3881if (it -> isId ) 3882inst -> addIdOperand (it -> word ); 3883else 3884inst -> addImmediateOperand (it -> word ); 3885 } 3886 3887addInstruction (std::unique_ptr < Instruction > (inst )); 3888 3889return inst -> getResultId (); 3890} 3891 3892// Accept all parameters needed to create a texture instruction. 3893// Create the correct instruction based on the inputs, and make the call. 3894Id Builder ::createTextureCall (Decoration precision ,Id resultType ,bool sparse ,bool fetch ,bool proj ,bool gather , 3895bool noImplicitLod ,const TextureParameters & parameters ,ImageOperandsMask signExtensionMask ) 3896{ 3897 std::vector < Id > texArgs ; 3898 3899// 3900// Set up the fixed arguments 3901// 3902bool explicitLod = false; 3903texArgs .push_back (parameters .sampler ); 3904texArgs .push_back (parameters .coords ); 3905if (parameters .Dref != NoResult ) 3906texArgs .push_back (parameters .Dref ); 3907if (parameters .component != NoResult ) 3908texArgs .push_back (parameters .component ); 3909if (gather && parameters .gatherMode != NoResult ) 3910texArgs .push_back (parameters .gatherMode ); 3911 3912if (parameters .granularity != NoResult ) 3913texArgs .push_back (parameters .granularity ); 3914if (parameters .coarse != NoResult ) 3915texArgs .push_back (parameters .coarse ); 3916 3917// 3918// Set up the optional arguments 3919// 3920size_t optArgNum = texArgs .size ();// the position of the mask for the optional arguments, if any. 3921ImageOperandsMask mask = ImageOperandsMask ::MaskNone ;// the mask operand 3922if (parameters .bias ) { 3923mask = (ImageOperandsMask )(mask |ImageOperandsMask ::Bias ); 3924texArgs .push_back (parameters .bias ); 3925 } 3926if (parameters .lod ) { 3927mask = (ImageOperandsMask )(mask |ImageOperandsMask ::Lod ); 3928texArgs .push_back (parameters .lod ); 3929explicitLod = true; 3930 }else if (parameters .gradX ) { 3931mask = (ImageOperandsMask )(mask |ImageOperandsMask ::Grad ); 3932texArgs .push_back (parameters .gradX ); 3933texArgs .push_back (parameters .gradY ); 3934explicitLod = true; 3935 }else if (noImplicitLod && !fetch && !gather ) { 3936// have to explicitly use lod of 0 if not allowed to have them be implicit, and 3937// we would otherwise be about to issue an implicit instruction 3938mask = (ImageOperandsMask )(mask |ImageOperandsMask ::Lod ); 3939texArgs .push_back (makeFloatConstant (0.0 )); 3940explicitLod = true; 3941 } 3942if (parameters .offset ) { 3943if (isConstant (parameters .offset )) 3944mask = (ImageOperandsMask )(mask |ImageOperandsMask ::ConstOffset ); 3945else { 3946addCapability (Capability ::ImageGatherExtended ); 3947mask = (ImageOperandsMask )(mask |ImageOperandsMask ::Offset ); 3948 } 3949texArgs .push_back (parameters .offset ); 3950 } 3951if (parameters .offsets ) { 3952if (!isConstant (parameters .offsets )&& sourceLang == spv::SourceLanguage ::GLSL ) { 3953mask = (ImageOperandsMask )(mask |ImageOperandsMask ::Offsets ); 3954 }else { 3955addCapability (Capability ::ImageGatherExtended ); 3956mask = (ImageOperandsMask )(mask |ImageOperandsMask ::ConstOffsets ); 3957 } 3958texArgs .push_back (parameters .offsets ); 3959 } 3960if (parameters .sample ) { 3961mask = (ImageOperandsMask )(mask |ImageOperandsMask ::Sample ); 3962texArgs .push_back (parameters .sample ); 3963 } 3964if (parameters .lodClamp ) { 3965// capability if this bit is used 3966addCapability (Capability ::MinLod ); 3967 3968mask = (ImageOperandsMask )(mask |ImageOperandsMask ::MinLod ); 3969texArgs .push_back (parameters .lodClamp ); 3970 } 3971if (parameters .nonprivate ) { 3972mask = mask |ImageOperandsMask ::NonPrivateTexelKHR ; 3973 } 3974if (parameters .volatil ) { 3975mask = mask |ImageOperandsMask ::VolatileTexelKHR ; 3976 } 3977if (parameters .nontemporal ) { 3978mask = mask |ImageOperandsMask ::Nontemporal ; 3979 } 3980mask = mask |signExtensionMask ; 3981// insert the operand for the mask, if any bits were set. 3982if (mask != ImageOperandsMask ::MaskNone ) 3983texArgs .insert (texArgs .begin ()+ optArgNum , (Id )mask ); 3984 3985bool isTextureGatherExtended = (gather && parameters .gatherMode != NoResult ); 3986 3987// 3988// Set up the instruction 3989// 3990Op opCode = Op ::OpNop ;// All paths below need to set this 3991if (fetch ) { 3992if (sparse ) 3993opCode = Op ::OpImageSparseFetch ; 3994else 3995opCode = Op ::OpImageFetch ; 3996 }else if (parameters .granularity && parameters .coarse ) { 3997opCode = Op ::OpImageSampleFootprintNV ; 3998 }else if (gather ) { 3999if (parameters .Dref ) 4000if (sparse ) 4001opCode = Op ::OpImageSparseDrefGather ; 4002else 4003opCode = Op ::OpImageDrefGather ; 4004else 4005if (sparse ) 4006opCode = Op ::OpImageSparseGather ; 4007else if (isTextureGatherExtended ) 4008opCode = Op ::OpImageGatherQCOM ; 4009else 4010opCode = Op ::OpImageGather ; 4011 }else if (explicitLod ) { 4012if (parameters .Dref ) { 4013if (proj ) 4014if (sparse ) 4015opCode = Op ::OpImageSparseSampleProjDrefExplicitLod ; 4016else 4017opCode = Op ::OpImageSampleProjDrefExplicitLod ; 4018else 4019if (sparse ) 4020opCode = Op ::OpImageSparseSampleDrefExplicitLod ; 4021else 4022opCode = Op ::OpImageSampleDrefExplicitLod ; 4023 }else { 4024if (proj ) 4025if (sparse ) 4026opCode = Op ::OpImageSparseSampleProjExplicitLod ; 4027else 4028opCode = Op ::OpImageSampleProjExplicitLod ; 4029else 4030if (sparse ) 4031opCode = Op ::OpImageSparseSampleExplicitLod ; 4032else 4033opCode = Op ::OpImageSampleExplicitLod ; 4034 } 4035 }else { 4036if (parameters .Dref ) { 4037if (proj ) 4038if (sparse ) 4039opCode = Op ::OpImageSparseSampleProjDrefImplicitLod ; 4040else 4041opCode = Op ::OpImageSampleProjDrefImplicitLod ; 4042else 4043if (sparse ) 4044opCode = Op ::OpImageSparseSampleDrefImplicitLod ; 4045else 4046opCode = Op ::OpImageSampleDrefImplicitLod ; 4047 }else { 4048if (proj ) 4049if (sparse ) 4050opCode = Op ::OpImageSparseSampleProjImplicitLod ; 4051else 4052opCode = Op ::OpImageSampleProjImplicitLod ; 4053else 4054if (sparse ) 4055opCode = Op ::OpImageSparseSampleImplicitLod ; 4056else 4057opCode = Op ::OpImageSampleImplicitLod ; 4058 } 4059 } 4060 4061// See if the result type is expecting a smeared result. 4062// This happens when a legacy shadow*() call is made, which 4063// gets a vec4 back instead of a float. 4064Id smearedType = resultType ; 4065if (!isScalarType (resultType )) { 4066switch (opCode ) { 4067case Op ::OpImageSampleDrefImplicitLod : 4068case Op ::OpImageSampleDrefExplicitLod : 4069case Op ::OpImageSampleProjDrefImplicitLod : 4070case Op ::OpImageSampleProjDrefExplicitLod : 4071resultType = getScalarTypeId (resultType ); 4072break ; 4073default : 4074break ; 4075 } 4076 } 4077 4078Id typeId0 = 0 ; 4079Id typeId1 = 0 ; 4080 4081if (sparse ) { 4082typeId0 = resultType ; 4083typeId1 = getDerefTypeId (parameters .texelOut ); 4084resultType = makeStructResultType (typeId0 ,typeId1 ); 4085 } 4086 4087// Build the SPIR-V instruction 4088Instruction * textureInst = new Instruction (getUniqueId (),resultType ,opCode ); 4089textureInst -> reserveOperands (optArgNum + (texArgs .size ()- (optArgNum + 1 ))); 4090for (size_t op = 0 ;op < optArgNum ;++ op ) 4091textureInst -> addIdOperand (texArgs [op ]); 4092if (optArgNum < texArgs .size ()) 4093textureInst -> addImmediateOperand (texArgs [optArgNum ]); 4094for (size_t op = optArgNum + 1 ;op < texArgs .size ();++ op ) 4095textureInst -> addIdOperand (texArgs [op ]); 4096setPrecision (textureInst -> getResultId (),precision ); 4097addInstruction (std::unique_ptr < Instruction > (textureInst )); 4098 4099Id resultId = textureInst -> getResultId (); 4100 4101if (sparse ) { 4102// set capability 4103addCapability (Capability ::SparseResidency ); 4104 4105// Decode the return type that was a special structure 4106createStore (createCompositeExtract (resultId ,typeId1 ,1 ),parameters .texelOut ); 4107resultId = createCompositeExtract (resultId ,typeId0 ,0 ); 4108setPrecision (resultId ,precision ); 4109 }else { 4110// When a smear is needed, do it, as per what was computed 4111// above when resultType was changed to a scalar type. 4112if (resultType != smearedType ) 4113resultId = smearScalar (precision ,resultId ,smearedType ); 4114 } 4115 4116return resultId ; 4117} 4118 4119// Comments in header 4120Id Builder ::createTextureQueryCall (Op opCode ,const TextureParameters & parameters ,bool isUnsignedResult ) 4121{ 4122// Figure out the result type 4123Id resultType = 0 ; 4124switch (opCode ) { 4125case Op ::OpImageQuerySize : 4126case Op ::OpImageQuerySizeLod : 4127 { 4128int numComponents = 0 ; 4129switch (getTypeDimensionality (getImageType (parameters .sampler ))) { 4130case Dim ::Dim1D : 4131case Dim ::Buffer : 4132numComponents = 1 ; 4133break ; 4134case Dim ::Dim2D : 4135case Dim ::Cube : 4136case Dim ::Rect : 4137case Dim ::SubpassData : 4138numComponents = 2 ; 4139break ; 4140case Dim ::Dim3D : 4141numComponents = 3 ; 4142break ; 4143 4144default : 4145assert (0 ); 4146break ; 4147 } 4148if (isArrayedImageType (getImageType (parameters .sampler ))) 4149++ numComponents ; 4150 4151Id intType = isUnsignedResult ?makeUintType (32 ) :makeIntType (32 ); 4152if (numComponents == 1 ) 4153resultType = intType ; 4154else 4155resultType = makeVectorType (intType ,numComponents ); 4156 4157break ; 4158 } 4159case Op ::OpImageQueryLod : 4160resultType = makeVectorType (getScalarTypeId (getTypeId (parameters .coords )),2 ); 4161break ; 4162case Op ::OpImageQueryLevels : 4163case Op ::OpImageQuerySamples : 4164resultType = isUnsignedResult ?makeUintType (32 ) :makeIntType (32 ); 4165break ; 4166default : 4167assert (0 ); 4168break ; 4169 } 4170 4171Instruction * query = new Instruction (getUniqueId (),resultType ,opCode ); 4172query -> addIdOperand (parameters .sampler ); 4173if (parameters .coords ) 4174query -> addIdOperand (parameters .coords ); 4175if (parameters .lod ) 4176query -> addIdOperand (parameters .lod ); 4177addInstruction (std::unique_ptr < Instruction > (query )); 4178addCapability (Capability ::ImageQuery ); 4179 4180return query -> getResultId (); 4181} 4182 4183// External comments in header. 4184// Operates recursively to visit the composite's hierarchy. 4185Id Builder ::createCompositeCompare (Decoration precision ,Id value1 ,Id value2 ,bool equal ) 4186{ 4187Id boolType = makeBoolType (); 4188Id valueType = getTypeId (value1 ); 4189 4190Id resultId = NoResult ; 4191 4192int numConstituents = getNumTypeConstituents (valueType ); 4193 4194// Scalars and Vectors 4195 4196if (isScalarType (valueType )|| isVectorType (valueType )) { 4197assert (valueType == getTypeId (value2 )); 4198// These just need a single comparison, just have 4199// to figure out what it is. 4200Op op ; 4201switch (getMostBasicTypeClass (valueType )) { 4202case Op ::OpTypeFloat : 4203op = equal ?Op ::OpFOrdEqual :Op ::OpFUnordNotEqual ; 4204break ; 4205case Op ::OpTypeInt : 4206default : 4207op = equal ?Op ::OpIEqual :Op ::OpINotEqual ; 4208break ; 4209case Op ::OpTypeBool : 4210op = equal ?Op ::OpLogicalEqual :Op ::OpLogicalNotEqual ; 4211precision = NoPrecision ; 4212break ; 4213 } 4214 4215if (isScalarType (valueType )) { 4216// scalar 4217resultId = createBinOp (op ,boolType ,value1 ,value2 ); 4218 }else { 4219// vector 4220resultId = createBinOp (op ,makeVectorType (boolType ,numConstituents ),value1 ,value2 ); 4221setPrecision (resultId ,precision ); 4222// reduce vector compares... 4223resultId = createUnaryOp (equal ?Op ::OpAll :Op ::OpAny ,boolType ,resultId ); 4224 } 4225 4226return setPrecision (resultId ,precision ); 4227 } 4228 4229// Only structs, arrays, and matrices should be left. 4230// They share in common the reduction operation across their constituents. 4231assert (isAggregateType (valueType )|| isMatrixType (valueType )); 4232 4233// Compare each pair of constituents 4234for (int constituent = 0 ;constituent < numConstituents ;++ constituent ) { 4235 std::vector < unsigned > indexes (1 ,constituent ); 4236Id constituentType1 = getContainedTypeId (getTypeId (value1 ),constituent ); 4237Id constituentType2 = getContainedTypeId (getTypeId (value2 ),constituent ); 4238Id constituent1 = createCompositeExtract (value1 ,constituentType1 ,indexes ); 4239Id constituent2 = createCompositeExtract (value2 ,constituentType2 ,indexes ); 4240 4241Id subResultId = createCompositeCompare (precision ,constituent1 ,constituent2 ,equal ); 4242 4243if (constituent == 0 ) 4244resultId = subResultId ; 4245else 4246resultId = setPrecision (createBinOp (equal ?Op ::OpLogicalAnd :Op ::OpLogicalOr ,boolType ,resultId ,subResultId ), 4247precision ); 4248 } 4249 4250return resultId ; 4251} 4252 4253// OpCompositeConstruct 4254Id Builder ::createCompositeConstruct (Id typeId ,const std::vector < Id >& constituents ) 4255{ 4256assert (isAggregateType (typeId )|| (getNumTypeConstituents (typeId )> 1 && 4257getNumTypeConstituents (typeId )== constituents .size ())|| 4258 ((isCooperativeVectorType (typeId )|| isVectorType (typeId ))&& constituents .size ()== 1 )); 4259 4260if (generatingOpCodeForSpecConst ) { 4261// Sometime, even in spec-constant-op mode, the constant composite to be 4262// constructed may not be a specialization constant. 4263// e.g.: 4264// const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); 4265// The first column vector should be a spec constant one, as a_spec_const is a spec constant. 4266// The second column vector should NOT be spec constant, as it does not contain any spec constants. 4267// To handle such cases, we check the constituents of the constant vector to determine whether this 4268// vector should be created as a spec constant. 4269return makeCompositeConstant (typeId ,constituents , 4270 std::any_of (constituents .begin (),constituents .end (), 4271 [& ](spv::Id id ) {return isSpecConstant (id ); })); 4272 } 4273 4274bool replicate = false; 4275size_t numConstituents = constituents .size (); 4276 4277if (useReplicatedComposites || isCooperativeVectorType (typeId )) { 4278replicate = numConstituents > 0 && 4279 std::equal (constituents .begin ()+ 1 ,constituents .end (),constituents .begin ()); 4280 } 4281 4282if (replicate ) { 4283numConstituents = 1 ; 4284addCapability (spv::Capability ::ReplicatedCompositesEXT ); 4285addExtension (spv::E_SPV_EXT_replicated_composites ); 4286 } 4287 4288Op opcode = replicate ?Op ::OpCompositeConstructReplicateEXT :Op ::OpCompositeConstruct ; 4289 4290Instruction * op = new Instruction (getUniqueId (),typeId ,opcode ); 4291op -> reserveOperands (constituents .size ()); 4292for (size_t c = 0 ;c < numConstituents ;++ c ) 4293op -> addIdOperand (constituents [c ]); 4294addInstruction (std::unique_ptr < Instruction > (op )); 4295 4296return op -> getResultId (); 4297} 4298 4299// coopmat conversion 4300Id Builder ::createCooperativeMatrixConversion (Id typeId ,Id source ) 4301{ 4302Instruction * op = new Instruction (getUniqueId (),typeId ,Op ::OpCooperativeMatrixConvertNV ); 4303op -> addIdOperand (source ); 4304addInstruction (std::unique_ptr < Instruction > (op )); 4305 4306return op -> getResultId (); 4307} 4308 4309// coopmat reduce 4310Id Builder ::createCooperativeMatrixReduce (Op opcode ,Id typeId ,Id source ,unsigned int mask ,Id func ) 4311{ 4312Instruction * op = new Instruction (getUniqueId (),typeId ,opcode ); 4313op -> addIdOperand (source ); 4314op -> addImmediateOperand (mask ); 4315op -> addIdOperand (func ); 4316addInstruction (std::unique_ptr < Instruction > (op )); 4317 4318return op -> getResultId (); 4319} 4320 4321// coopmat per-element operation 4322Id Builder ::createCooperativeMatrixPerElementOp (Id typeId ,const std::vector < Id >& operands ) 4323{ 4324Instruction * op = new Instruction (getUniqueId (),typeId , spv::Op ::OpCooperativeMatrixPerElementOpNV ); 4325// skip operand[0], which is where the result is stored 4326for (uint32_t i = 1 ;i < operands .size ();++ i ) { 4327op -> addIdOperand (operands [i ]); 4328 } 4329addInstruction (std::unique_ptr < Instruction > (op )); 4330 4331return op -> getResultId (); 4332} 4333 4334// Vector or scalar constructor 4335Id Builder ::createConstructor (Decoration precision ,const std::vector < Id >& sources ,Id resultTypeId ) 4336{ 4337Id result = NoResult ; 4338unsigned int numTargetComponents = getNumTypeComponents (resultTypeId ); 4339unsigned int targetComponent = 0 ; 4340 4341// Special case: when calling a vector constructor with a single scalar 4342// argument, smear the scalar 4343if (sources .size ()== 1 && isScalar (sources [0 ])&& (numTargetComponents > 1 || isCooperativeVectorType (resultTypeId ))) 4344return smearScalar (precision ,sources [0 ],resultTypeId ); 4345 4346// Special case: 2 vectors of equal size 4347if (sources .size ()== 1 && 4348 (isVector (sources [0 ])|| isCooperativeVector (sources [0 ]))&& 4349numTargetComponents == getNumComponents (sources [0 ])) { 4350if (isCooperativeVector (sources [0 ])!= isCooperativeVectorType (resultTypeId )) { 4351assert (isVector (sources [0 ])!= isVectorType (resultTypeId )); 4352return createUnaryOp (spv::Op ::OpBitcast ,resultTypeId ,sources [0 ]); 4353 }else { 4354assert (resultTypeId == getTypeId (sources [0 ])); 4355return sources [0 ]; 4356 } 4357 } 4358 4359// accumulate the arguments for OpCompositeConstruct 4360 std::vector < Id > constituents ; 4361Id scalarTypeId = getScalarTypeId (resultTypeId ); 4362 4363// lambda to store the result of visiting an argument component 4364const auto latchResult = [& ](Id comp ) { 4365if (numTargetComponents > 1 || isVectorType (resultTypeId )) 4366constituents .push_back (comp ); 4367else 4368result = comp ; 4369++ targetComponent ; 4370 }; 4371 4372// lambda to visit a vector argument's components 4373const auto accumulateVectorConstituents = [& ](Id sourceArg ) { 4374unsigned int sourceSize = getNumComponents (sourceArg ); 4375unsigned int sourcesToUse = sourceSize ; 4376if (sourcesToUse + targetComponent > numTargetComponents ) 4377sourcesToUse = numTargetComponents - targetComponent ; 4378 4379for (unsigned int s = 0 ;s < sourcesToUse ;++ s ) { 4380 std::vector < unsigned > swiz ; 4381swiz .push_back (s ); 4382latchResult (createRvalueSwizzle (precision ,scalarTypeId ,sourceArg ,swiz )); 4383 } 4384 }; 4385 4386// lambda to visit a matrix argument's components 4387const auto accumulateMatrixConstituents = [& ](Id sourceArg ) { 4388unsigned int sourceSize = getNumColumns (sourceArg )* getNumRows (sourceArg ); 4389unsigned int sourcesToUse = sourceSize ; 4390if (sourcesToUse + targetComponent > numTargetComponents ) 4391sourcesToUse = numTargetComponents - targetComponent ; 4392 4393unsigned int col = 0 ; 4394unsigned int row = 0 ; 4395for (unsigned int s = 0 ;s < sourcesToUse ;++ s ) { 4396if (row >=getNumRows (sourceArg )) { 4397row = 0 ; 4398col ++ ; 4399 } 4400 std::vector < Id > indexes ; 4401indexes .push_back (col ); 4402indexes .push_back (row ); 4403latchResult (createCompositeExtract (sourceArg ,scalarTypeId ,indexes )); 4404row ++ ; 4405 } 4406 }; 4407 4408// Go through the source arguments, each one could have either 4409// a single or multiple components to contribute. 4410for (unsigned int i = 0 ;i < sources .size ();++ i ) { 4411 4412if (isScalar (sources [i ])|| isPointer (sources [i ])) 4413latchResult (sources [i ]); 4414else if (isVector (sources [i ])|| isCooperativeVector (sources [i ])) 4415accumulateVectorConstituents (sources [i ]); 4416else if (isMatrix (sources [i ])) 4417accumulateMatrixConstituents (sources [i ]); 4418else 4419assert (0 ); 4420 4421if (targetComponent >=numTargetComponents ) 4422break ; 4423 } 4424 4425// If the result is a vector, make it from the gathered constituents. 4426if (constituents .size ()> 0 ) { 4427result = createCompositeConstruct (resultTypeId ,constituents ); 4428return setPrecision (result ,precision ); 4429 }else { 4430// Precision was set when generating this component. 4431return result ; 4432 } 4433} 4434 4435// Comments in header 4436Id Builder ::createMatrixConstructor (Decoration precision ,const std::vector < Id >& sources ,Id resultTypeId ) 4437{ 4438Id componentTypeId = getScalarTypeId (resultTypeId ); 4439unsigned int numCols = getTypeNumColumns (resultTypeId ); 4440unsigned int numRows = getTypeNumRows (resultTypeId ); 4441 4442Instruction * instr = module .getInstruction (componentTypeId ); 4443const unsigned bitCount = instr -> getImmediateOperand (0 ); 4444 4445// Optimize matrix constructed from a bigger matrix 4446if (isMatrix (sources [0 ])&& getNumColumns (sources [0 ]) >=numCols && getNumRows (sources [0 ]) >=numRows ) { 4447// To truncate the matrix to a smaller number of rows/columns, we need to: 4448// 1. For each column, extract the column and truncate it to the required size using shuffle 4449// 2. Assemble the resulting matrix from all columns 4450Id matrix = sources [0 ]; 4451Id columnTypeId = getContainedTypeId (resultTypeId ); 4452Id sourceColumnTypeId = getContainedTypeId (getTypeId (matrix )); 4453 4454 std::vector < unsigned > channels ; 4455for (unsigned int row = 0 ;row < numRows ;++ row ) 4456channels .push_back (row ); 4457 4458 std::vector < Id > matrixColumns ; 4459for (unsigned int col = 0 ;col < numCols ;++ col ) { 4460 std::vector < unsigned > indexes ; 4461indexes .push_back (col ); 4462Id colv = createCompositeExtract (matrix ,sourceColumnTypeId ,indexes ); 4463setPrecision (colv ,precision ); 4464 4465if (numRows != getNumRows (matrix )) { 4466matrixColumns .push_back (createRvalueSwizzle (precision ,columnTypeId ,colv ,channels )); 4467 }else { 4468matrixColumns .push_back (colv ); 4469 } 4470 } 4471 4472return setPrecision (createCompositeConstruct (resultTypeId ,matrixColumns ),precision ); 4473 } 4474 4475// Detect a matrix being constructed from a repeated vector of the correct size. 4476// Create the composite directly from it. 4477if (sources .size ()== numCols && isVector (sources [0 ])&& getNumComponents (sources [0 ])== numRows && 4478 std::equal (sources .begin ()+ 1 ,sources .end (),sources .begin ())) { 4479return setPrecision (createCompositeConstruct (resultTypeId ,sources ),precision ); 4480 } 4481 4482// Otherwise, will use a two step process 4483// 1. make a compile-time 2D array of values 4484// 2. construct a matrix from that array 4485 4486// Step 1. 4487 4488// initialize the array to the identity matrix 4489Id ids [maxMatrixSize ][maxMatrixSize ]; 4490Id one ,zero ; 4491if (bitCount == 64 ) { 4492one = makeDoubleConstant (1.0 ); 4493zero = makeDoubleConstant (0.0 ); 4494 }else if (bitCount == 16 ) { 4495one = makeFloat16Constant (1.0 ); 4496zero = makeFloat16Constant (0.0 ); 4497 }else { 4498one = makeFloatConstant (1.0 ); 4499zero = makeFloatConstant (0.0 ); 4500 } 4501for (int col = 0 ;col < 4 ;++ col ) { 4502for (int row = 0 ;row < 4 ;++ row ) { 4503if (col == row ) 4504ids [col ][row ]= one ; 4505else 4506ids [col ][row ]= zero ; 4507 } 4508 } 4509 4510// modify components as dictated by the arguments 4511if (sources .size ()== 1 && isScalar (sources [0 ])) { 4512// a single scalar; resets the diagonals 4513for (int col = 0 ;col < 4 ;++ col ) 4514ids [col ][col ]= sources [0 ]; 4515 }else if (isMatrix (sources [0 ])) { 4516// constructing from another matrix; copy over the parts that exist in both the argument and constructee 4517Id matrix = sources [0 ]; 4518unsigned int minCols = std::min (numCols ,getNumColumns (matrix )); 4519unsigned int minRows = std::min (numRows ,getNumRows (matrix )); 4520for (unsigned int col = 0 ;col < minCols ;++ col ) { 4521 std::vector < unsigned > indexes ; 4522indexes .push_back (col ); 4523for (unsigned int row = 0 ;row < minRows ;++ row ) { 4524indexes .push_back (row ); 4525ids [col ][row ]= createCompositeExtract (matrix ,componentTypeId ,indexes ); 4526indexes .pop_back (); 4527setPrecision (ids [col ][row ],precision ); 4528 } 4529 } 4530 }else { 4531// fill in the matrix in column-major order with whatever argument components are available 4532unsigned int row = 0 ; 4533unsigned int col = 0 ; 4534 4535for (unsigned int arg = 0 ;arg < sources .size ()&& col < numCols ;++ arg ) { 4536Id argComp = sources [arg ]; 4537for (unsigned int comp = 0 ;comp < getNumComponents (sources [arg ]);++ comp ) { 4538if (getNumComponents (sources [arg ])> 1 ) { 4539argComp = createCompositeExtract (sources [arg ],componentTypeId ,comp ); 4540setPrecision (argComp ,precision ); 4541 } 4542ids [col ][row ++ ]= argComp ; 4543if (row == numRows ) { 4544row = 0 ; 4545col ++ ; 4546 } 4547if (col == numCols ) { 4548// If more components are provided than fit the matrix, discard the rest. 4549break ; 4550 } 4551 } 4552 } 4553 } 4554 4555// Step 2: Construct a matrix from that array. 4556// First make the column vectors, then make the matrix. 4557 4558// make the column vectors 4559Id columnTypeId = getContainedTypeId (resultTypeId ); 4560 std::vector < Id > matrixColumns ; 4561for (unsigned int col = 0 ;col < numCols ;++ col ) { 4562 std::vector < Id > vectorComponents ; 4563for (unsigned int row = 0 ;row < numRows ;++ row ) 4564vectorComponents .push_back (ids [col ][row ]); 4565Id column = createCompositeConstruct (columnTypeId ,vectorComponents ); 4566setPrecision (column ,precision ); 4567matrixColumns .push_back (column ); 4568 } 4569 4570// make the matrix 4571return setPrecision (createCompositeConstruct (resultTypeId ,matrixColumns ),precision ); 4572} 4573 4574// Comments in header 4575Builder ::If ::If (Id cond ,SelectionControlMask ctrl ,Builder & gb ) : 4576builder (gb ), 4577condition (cond ), 4578control (ctrl ), 4579elseBlock (nullptr ) 4580{ 4581function = & builder .getBuildPoint ()-> getParent (); 4582 4583// make the blocks, but only put the then-block into the function, 4584// the else-block and merge-block will be added later, in order, after 4585// earlier code is emitted 4586thenBlock = new Block (builder .getUniqueId (),* function ); 4587mergeBlock = new Block (builder .getUniqueId (),* function ); 4588 4589// Save the current block, so that we can add in the flow control split when 4590// makeEndIf is called. 4591headerBlock = builder .getBuildPoint (); 4592builder .createSelectionMerge (mergeBlock ,control ); 4593 4594function -> addBlock (thenBlock ); 4595builder .setBuildPoint (thenBlock ); 4596} 4597 4598// Comments in header 4599void Builder ::If ::makeBeginElse () 4600{ 4601// Close out the "then" by having it jump to the mergeBlock 4602builder .createBranch (true,mergeBlock ); 4603 4604// Make the first else block and add it to the function 4605elseBlock = new Block (builder .getUniqueId (),* function ); 4606function -> addBlock (elseBlock ); 4607 4608// Start building the else block 4609builder .setBuildPoint (elseBlock ); 4610} 4611 4612// Comments in header 4613void Builder ::If ::makeEndIf () 4614{ 4615// jump to the merge block 4616builder .createBranch (true,mergeBlock ); 4617 4618// Go back to the headerBlock and make the flow control split 4619builder .setBuildPoint (headerBlock ); 4620if (elseBlock ) 4621builder .createConditionalBranch (condition ,thenBlock ,elseBlock ); 4622else 4623builder .createConditionalBranch (condition ,thenBlock ,mergeBlock ); 4624 4625// add the merge block to the function 4626function -> addBlock (mergeBlock ); 4627builder .setBuildPoint (mergeBlock ); 4628} 4629 4630// Comments in header 4631void Builder ::makeSwitch (Id selector ,SelectionControlMask control ,int numSegments ,const std::vector < int >& caseValues , 4632const std::vector < int >& valueIndexToSegment ,int defaultSegment , 4633 std::vector < Block *>& segmentBlocks ) 4634{ 4635Function & function = buildPoint -> getParent (); 4636 4637// make all the blocks 4638for (int s = 0 ;s < numSegments ;++ s ) 4639segmentBlocks .push_back (new Block (getUniqueId (),function )); 4640 4641Block * mergeBlock = new Block (getUniqueId (),function ); 4642 4643// make and insert the switch's selection-merge instruction 4644createSelectionMerge (mergeBlock ,control ); 4645 4646// make the switch instruction 4647Instruction * switchInst = new Instruction (NoResult ,NoType ,Op ::OpSwitch ); 4648switchInst -> reserveOperands ((caseValues .size ()* 2 )+ 2 ); 4649switchInst -> addIdOperand (selector ); 4650auto defaultOrMerge = (defaultSegment >=0 ) ?segmentBlocks [defaultSegment ] :mergeBlock ; 4651switchInst -> addIdOperand (defaultOrMerge -> getId ()); 4652defaultOrMerge -> addPredecessor (buildPoint ); 4653for (int i = 0 ;i < (int )caseValues .size ();++ i ) { 4654switchInst -> addImmediateOperand (caseValues [i ]); 4655switchInst -> addIdOperand (segmentBlocks [valueIndexToSegment [i ]]-> getId ()); 4656segmentBlocks [valueIndexToSegment [i ]]-> addPredecessor (buildPoint ); 4657 } 4658addInstruction (std::unique_ptr < Instruction > (switchInst )); 4659 4660// push the merge block 4661switchMerges .push (mergeBlock ); 4662} 4663 4664// Comments in header 4665void Builder ::addSwitchBreak (bool implicit ) 4666{ 4667// branch to the top of the merge block stack 4668createBranch (implicit ,switchMerges .top ()); 4669createAndSetNoPredecessorBlock ("post-switch-break" ); 4670} 4671 4672// Comments in header 4673void Builder ::nextSwitchSegment (std::vector < Block *>& segmentBlock ,int nextSegment ) 4674{ 4675int lastSegment = nextSegment - 1 ; 4676if (lastSegment >=0 ) { 4677// Close out previous segment by jumping, if necessary, to next segment 4678if (!buildPoint -> isTerminated ()) 4679createBranch (true,segmentBlock [nextSegment ]); 4680 } 4681Block * block = segmentBlock [nextSegment ]; 4682block -> getParent ().addBlock (block ); 4683setBuildPoint (block ); 4684} 4685 4686// Comments in header 4687void Builder ::endSwitch (std::vector < Block *>& /*segmentBlock*/ ) 4688{ 4689// Close out previous segment by jumping, if necessary, to next segment 4690if (!buildPoint -> isTerminated ()) 4691addSwitchBreak (true); 4692 4693switchMerges .top ()-> getParent ().addBlock (switchMerges .top ()); 4694setBuildPoint (switchMerges .top ()); 4695 4696switchMerges .pop (); 4697} 4698 4699Block & Builder ::makeNewBlock () 4700{ 4701Function & function = buildPoint -> getParent (); 4702auto block = new Block (getUniqueId (),function ); 4703function .addBlock (block ); 4704return * block ; 4705} 4706 4707Builder ::LoopBlocks & Builder ::makeNewLoop () 4708{ 4709// This verbosity is needed to simultaneously get the same behavior 4710// everywhere (id's in the same order), have a syntax that works 4711// across lots of versions of C++, have no warnings from pedantic 4712// compilation modes, and leave the rest of the code alone. 4713Block & head = makeNewBlock (); 4714Block & body = makeNewBlock (); 4715Block & merge = makeNewBlock (); 4716Block & continue_target = makeNewBlock (); 4717LoopBlocks blocks (head ,body ,merge ,continue_target ); 4718loops .push (blocks ); 4719return loops .top (); 4720} 4721 4722void Builder ::createLoopContinue () 4723{ 4724createBranch (false,& loops .top ().continue_target ); 4725// Set up a block for dead code. 4726createAndSetNoPredecessorBlock ("post-loop-continue" ); 4727} 4728 4729void Builder ::createLoopExit () 4730{ 4731createBranch (false,& loops .top ().merge ); 4732// Set up a block for dead code. 4733createAndSetNoPredecessorBlock ("post-loop-break" ); 4734} 4735 4736void Builder ::closeLoop () 4737{ 4738loops .pop (); 4739} 4740 4741void Builder ::clearAccessChain () 4742{ 4743accessChain .base = NoResult ; 4744accessChain .indexChain .clear (); 4745accessChain .instr = NoResult ; 4746accessChain .swizzle .clear (); 4747accessChain .component = NoResult ; 4748accessChain .preSwizzleBaseType = NoType ; 4749accessChain .isRValue = false; 4750accessChain .coherentFlags .clear (); 4751accessChain .alignment = 0 ; 4752accessChain .descHeapInfo .descHeapBaseTy = NoResult ; 4753accessChain .descHeapInfo .descHeapBaseOffset = NoResult ; 4754accessChain .descHeapInfo .descHeapIndexChain .clear (); 4755accessChain .descHeapInfo .descTy = NoResult ; 4756accessChain .descHeapInfo .descStorageClass = StorageClass ::Max ; 4757accessChain .descHeapInfo .descReadonly = false; 4758accessChain .descHeapInfo .descWriteonly = false; 4759accessChain .descHeapInfo .descHeapInstId .clear (); 4760} 4761 4762// Comments in header 4763void Builder ::accessChainPushSwizzle (std::vector < unsigned >& swizzle ,Id preSwizzleBaseType , 4764AccessChain ::CoherentFlags coherentFlags ,unsigned int alignment ) 4765{ 4766accessChain .coherentFlags |=coherentFlags ; 4767accessChain .alignment |=alignment ; 4768 4769// swizzles can be stacked in GLSL, but simplified to a single 4770// one here; the base type doesn't change 4771if (accessChain .preSwizzleBaseType == NoType ) 4772accessChain .preSwizzleBaseType = preSwizzleBaseType ; 4773 4774// if needed, propagate the swizzle for the current access chain 4775if (accessChain .swizzle .size ()> 0 ) { 4776 std::vector < unsigned > oldSwizzle = accessChain .swizzle ; 4777accessChain .swizzle .resize (0 ); 4778for (unsigned int i = 0 ;i < swizzle .size ();++ i ) { 4779assert (swizzle [i ]< oldSwizzle .size ()); 4780accessChain .swizzle .push_back (oldSwizzle [swizzle [i ]]); 4781 } 4782 }else 4783accessChain .swizzle = swizzle ; 4784 4785// determine if we need to track this swizzle anymore 4786simplifyAccessChainSwizzle (); 4787} 4788 4789// Comments in header 4790void Builder ::accessChainStore (Id rvalue ,Decoration nonUniform , spv::MemoryAccessMask memoryAccess , spv::Scope scope ,unsigned int alignment ) 4791{ 4792assert (accessChain .isRValue == false); 4793 4794transferAccessChainSwizzle (true); 4795 4796// MeshShadingEXT outputs don't support loads, so split swizzled stores 4797bool isMeshOutput = getStorageClass (accessChain .base )== StorageClass ::Output && 4798capabilities .find (spv::Capability ::MeshShadingEXT )!= capabilities .end (); 4799 4800// If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores. 4801if (accessChain .swizzle .size ()> 0 && 4802 ((getNumTypeComponents (getResultingAccessChainType ())!= accessChain .swizzle .size ()&& accessChain .component == NoResult )|| isMeshOutput )) { 4803for (unsigned int i = 0 ;i < accessChain .swizzle .size ();++ i ) { 4804accessChain .indexChain .push_back (makeUintConstant (accessChain .swizzle [i ])); 4805accessChain .instr = NoResult ; 4806 4807Id base = collapseAccessChain (); 4808addDecoration (base ,nonUniform ); 4809 4810accessChain .indexChain .pop_back (); 4811accessChain .instr = NoResult ; 4812 4813// dynamic component should be gone 4814assert (accessChain .component == NoResult ); 4815 4816Id source = createCompositeExtract (rvalue ,getContainedTypeId (getTypeId (rvalue )),i ); 4817 4818// take LSB of alignment 4819alignment = alignment & ~(alignment & (alignment - 1 )); 4820if (getStorageClass (base )== StorageClass ::PhysicalStorageBufferEXT ) { 4821memoryAccess = (spv::MemoryAccessMask )(memoryAccess | spv::MemoryAccessMask ::Aligned ); 4822 } 4823 4824createStore (source ,base ,memoryAccess ,scope ,alignment ); 4825 } 4826 } 4827else { 4828Id base = collapseAccessChain (); 4829addDecoration (base ,nonUniform ); 4830 4831Id source = rvalue ; 4832 4833// dynamic component should be gone 4834assert (accessChain .component == NoResult ); 4835 4836// take LSB of alignment 4837alignment = alignment & ~(alignment & (alignment - 1 )); 4838if (getStorageClass (base )== StorageClass ::PhysicalStorageBufferEXT ) { 4839memoryAccess = (spv::MemoryAccessMask )(memoryAccess | spv::MemoryAccessMask ::Aligned ); 4840 } 4841 4842// If swizzle still exists, it may be out-of-order, we must load the target vector, 4843// extract and insert elements to perform writeMask and/or swizzle. 4844if (accessChain .swizzle .size ()> 0 ) { 4845// The read-modify-write load hits the same pointer as the store, so it needs the 4846// same memory operands (while flipping MakePointerAvailable to Visible). 4847// In particular a PhysicalStorageBuffer access must carry Aligned. 4848auto loadAccess = memoryAccess & ~spv::MemoryAccessMask ::MakePointerAvailableKHR ; 4849if (anySet (memoryAccess , spv::MemoryAccessMask ::MakePointerAvailableKHR )) 4850loadAccess = loadAccess | spv::MemoryAccessMask ::MakePointerVisibleKHR ; 4851Id tempBaseId = createLoad (base , spv::NoPrecision ,loadAccess ,scope ,alignment ); 4852source = createLvalueSwizzle (getTypeId (tempBaseId ),tempBaseId ,source ,accessChain .swizzle ); 4853 } 4854 4855createStore (source ,base ,memoryAccess ,scope ,alignment ); 4856 } 4857} 4858 4859// Comments in header 4860Id Builder ::accessChainLoad (Decoration precision ,Decoration l_nonUniform , 4861Decoration r_nonUniform ,Id resultType , spv::MemoryAccessMask memoryAccess , 4862 spv::Scope scope ,unsigned int alignment ) 4863{ 4864Id id ; 4865 4866if (accessChain .isRValue ) { 4867// transfer access chain, but try to stay in registers 4868transferAccessChainSwizzle (false); 4869if (accessChain .indexChain .size ()> 0 ) { 4870Id swizzleBase = accessChain .preSwizzleBaseType != NoType ?accessChain .preSwizzleBaseType :resultType ; 4871 4872// if all the accesses are constants, we can use OpCompositeExtract 4873 std::vector < unsigned > indexes ; 4874bool constant = true; 4875for (int i = 0 ;i < (int )accessChain .indexChain .size ();++ i ) { 4876if (isConstantScalar (accessChain .indexChain [i ])) 4877indexes .push_back (getConstantScalar (accessChain .indexChain [i ])); 4878else { 4879constant = false; 4880break ; 4881 } 4882 } 4883 4884if (constant ) { 4885id = createCompositeExtract (accessChain .base ,swizzleBase ,indexes ); 4886setPrecision (id ,precision ); 4887 }else if (isVector (accessChain .base )|| isCooperativeVector (accessChain .base )) { 4888assert (accessChain .indexChain .size ()== 1 ); 4889id = createVectorExtractDynamic (accessChain .base ,resultType ,accessChain .indexChain [0 ]); 4890 }else { 4891Id lValue = NoResult ; 4892if (spvVersion >=Spv_1_4 && isValidInitializer (accessChain .base )) { 4893// make a new function variable for this r-value, using an initializer, 4894// and mark it as NonWritable so that downstream it can be detected as a lookup 4895// table 4896lValue = createVariable (NoPrecision ,StorageClass ::Function ,getTypeId (accessChain .base ), 4897"indexable" ,accessChain .base ); 4898addDecoration (lValue ,Decoration ::NonWritable ); 4899 }else { 4900lValue = createVariable (NoPrecision ,StorageClass ::Function ,getTypeId (accessChain .base ), 4901"indexable" ); 4902// store into it 4903createStore (accessChain .base ,lValue ); 4904 } 4905// move base to the new variable 4906accessChain .base = lValue ; 4907accessChain .isRValue = false; 4908 4909// load through the access chain 4910id = createLoad (collapseAccessChain (),precision ); 4911 } 4912 }else 4913id = accessChain .base ;// no precision, it was set when this was defined 4914 }else { 4915transferAccessChainSwizzle (true); 4916 4917// take LSB of alignment 4918alignment = alignment & ~(alignment & (alignment - 1 )); 4919if (getStorageClass (accessChain .base )== StorageClass ::PhysicalStorageBufferEXT ) { 4920memoryAccess = (spv::MemoryAccessMask )(memoryAccess | spv::MemoryAccessMask ::Aligned ); 4921 } 4922 4923// load through the access chain 4924id = collapseAccessChain (); 4925// Apply nonuniform both to the access chain and the loaded value. 4926// Buffer accesses need the access chain decorated, and this is where 4927// loaded image types get decorated. TODO: This should maybe move to 4928// createImageTextureFunctionCall. 4929addDecoration (id ,l_nonUniform ); 4930id = createLoad (id ,precision ,memoryAccess ,scope ,alignment ); 4931addDecoration (id ,r_nonUniform ); 4932 } 4933 4934// Done, unless there are swizzles to do 4935if (accessChain .swizzle .size ()== 0 && accessChain .component == NoResult ) 4936return id ; 4937 4938// Do remaining swizzling 4939 4940// Do the basic swizzle 4941if (accessChain .swizzle .size ()> 0 ) { 4942Id swizzledType = getScalarTypeId (getTypeId (id )); 4943if (accessChain .swizzle .size ()> 1 ) 4944swizzledType = makeVectorType (swizzledType , (int )accessChain .swizzle .size ()); 4945id = createRvalueSwizzle (precision ,swizzledType ,id ,accessChain .swizzle ); 4946 } 4947 4948// Do the dynamic component 4949if (accessChain .component != NoResult ) 4950id = setPrecision (createVectorExtractDynamic (id ,resultType ,accessChain .component ),precision ); 4951 4952addDecoration (id ,r_nonUniform ); 4953return id ; 4954} 4955 4956Id Builder ::accessChainGetLValue () 4957{ 4958assert (accessChain .isRValue == false); 4959 4960transferAccessChainSwizzle (true); 4961Id lvalue = collapseAccessChain (); 4962 4963// If swizzle exists, it is out-of-order or not full, we must load the target vector, 4964// extract and insert elements to perform writeMask and/or swizzle. This does not 4965// go with getting a direct l-value pointer. 4966assert (accessChain .swizzle .size ()== 0 ); 4967assert (accessChain .component == NoResult ); 4968 4969return lvalue ; 4970} 4971 4972// comment in header 4973Id Builder ::accessChainGetInferredType () 4974{ 4975// anything to operate on? 4976// for untyped pointer, it may be remapped to a descriptor heap. 4977// for descriptor heap, its base data type will be determined later, 4978// according to load/store results' types. 4979if (accessChain .base == NoResult || isUntypedPointer (accessChain .base )|| 4980 !accessChain .descHeapInfo .descHeapIndexChain .empty ()) 4981return NoType ; 4982Id type = getTypeId (accessChain .base ); 4983// do initial dereference 4984if (!accessChain .isRValue ) 4985type = getContainedTypeId (type ); 4986 4987// dereference each index 4988for (auto it = accessChain .indexChain .cbegin ();it != accessChain .indexChain .cend ();++ it ) { 4989if (isStructType (type )) 4990type = getContainedTypeId (type ,getConstantScalar (* it )); 4991else 4992type = getContainedTypeId (type ); 4993 } 4994 4995// dereference swizzle 4996if (accessChain .swizzle .size ()== 1 ) 4997type = getContainedTypeId (type ); 4998else if (accessChain .swizzle .size ()> 1 ) 4999type = makeVectorType (getContainedTypeId (type ), (int )accessChain .swizzle .size ()); 5000 5001// dereference component selection 5002if (accessChain .component ) 5003type = getContainedTypeId (type ); 5004 5005return type ; 5006} 5007 5008void Builder ::dump (std::vector < unsigned int >& out )const 5009{ 5010// Header, before first instructions: 5011out .push_back (MagicNumber ); 5012out .push_back (spvVersion ); 5013out .push_back (builderNumber ); 5014out .push_back (uniqueId + 1 ); 5015out .push_back (0 ); 5016 5017// Capabilities 5018for (auto it = capabilities .cbegin ();it != capabilities .cend ();++ it ) { 5019Instruction capInst (0 ,0 ,Op ::OpCapability ); 5020capInst .addImmediateOperand (* it ); 5021capInst .dump (out ); 5022 } 5023 5024for (auto it = extensions .cbegin ();it != extensions .cend ();++ it ) { 5025Instruction extInst (0 ,0 ,Op ::OpExtension ); 5026extInst .addStringOperand (it -> c_str ()); 5027extInst .dump (out ); 5028 } 5029 5030dumpInstructions (out ,imports ); 5031Instruction memInst (0 ,0 ,Op ::OpMemoryModel ); 5032memInst .addImmediateOperand (addressModel ); 5033memInst .addImmediateOperand (memoryModel ); 5034memInst .dump (out ); 5035 5036// Instructions saved up while building: 5037dumpInstructions (out ,entryPoints ); 5038dumpInstructions (out ,executionModes ); 5039 5040// Debug instructions 5041dumpInstructions (out ,strings ); 5042dumpSourceInstructions (out ); 5043for (int e = 0 ;e < (int )sourceExtensions .size ();++ e ) { 5044Instruction sourceExtInst (0 ,0 ,Op ::OpSourceExtension ); 5045sourceExtInst .addStringOperand (sourceExtensions [e ]); 5046sourceExtInst .dump (out ); 5047 } 5048dumpInstructions (out ,names ); 5049dumpModuleProcesses (out ); 5050 5051// Annotation instructions 5052dumpInstructions (out ,decorations ); 5053 5054dumpInstructions (out ,constantsTypesGlobals ); 5055dumpInstructions (out ,externals ); 5056 5057// The functions 5058module .dump (out ); 5059} 5060 5061// 5062// Protected methods. 5063// 5064 5065// Turn the described access chain in 'accessChain' into an instruction(s) 5066// computing its address. This *cannot* include complex swizzles, which must 5067// be handled after this is called. 5068// 5069// Can generate code. 5070Id Builder ::collapseAccessChain () 5071{ 5072assert (accessChain .isRValue == false); 5073 5074// did we already emit an access chain for this? 5075if (accessChain .instr != NoResult ) 5076return accessChain .instr ; 5077 5078// If we have a dynamic component, we can still transfer 5079// that into a final operand to the access chain. We need to remap the 5080// dynamic component through the swizzle to get a new dynamic component to 5081// update. 5082// 5083// This was not done in transferAccessChainSwizzle() because it might 5084// generate code. 5085remapDynamicSwizzle (); 5086if (accessChain .component != NoResult ) { 5087// transfer the dynamic component to the access chain 5088accessChain .indexChain .push_back (accessChain .component ); 5089accessChain .component = NoResult ; 5090 } 5091 5092// note that non-trivial swizzling is left pending 5093 5094// do we have an access chain? 5095if (accessChain .indexChain .size ()== 0 && accessChain .descHeapInfo .descHeapIndexChain .empty ()) 5096return accessChain .base ; 5097 5098// emit the access chain 5099StorageClass storageClass = (StorageClass )module .getStorageClass (getTypeId (accessChain .base )); 5100// when descHeap info is set, use another access chain process. 5101if (isUntypedPointer (accessChain .base )|| !accessChain .descHeapInfo .descHeapIndexChain .empty ()) { 5102accessChain .instr = createDescHeapAccessChain (); 5103 }else { 5104accessChain .instr = createAccessChain (storageClass ,accessChain .base ,accessChain .indexChain ); 5105 } 5106 5107return accessChain .instr ; 5108} 5109 5110// For a dynamic component selection of a swizzle. 5111// 5112// Turn the swizzle and dynamic component into just a dynamic component. 5113// 5114// Generates code. 5115void Builder ::remapDynamicSwizzle () 5116{ 5117// do we have a swizzle to remap a dynamic component through? 5118if (accessChain .component != NoResult && accessChain .swizzle .size ()> 1 ) { 5119// build a vector of the swizzle for the component to map into 5120 std::vector < Id > components ; 5121for (int c = 0 ;c < (int )accessChain .swizzle .size ();++ c ) 5122components .push_back (makeUintConstant (accessChain .swizzle [c ])); 5123Id mapType = makeVectorType (makeUintType (32 ), (int )accessChain .swizzle .size ()); 5124Id map = makeCompositeConstant (mapType ,components ); 5125 5126// use it 5127accessChain .component = createVectorExtractDynamic (map ,makeUintType (32 ),accessChain .component ); 5128accessChain .swizzle .clear (); 5129 } 5130} 5131 5132// clear out swizzle if it is redundant, that is reselecting the same components 5133// that would be present without the swizzle. 5134void Builder ::simplifyAccessChainSwizzle () 5135{ 5136// If the swizzle has fewer components than the vector, it is subsetting, and must stay 5137// to preserve that fact. 5138if (getNumTypeComponents (accessChain .preSwizzleBaseType )> accessChain .swizzle .size ()) 5139return ; 5140 5141// if components are out of order, it is a swizzle 5142for (unsigned int i = 0 ;i < accessChain .swizzle .size ();++ i ) { 5143if (i != accessChain .swizzle [i ]) 5144return ; 5145 } 5146 5147// otherwise, there is no need to track this swizzle 5148accessChain .swizzle .clear (); 5149if (accessChain .component == NoResult ) 5150accessChain .preSwizzleBaseType = NoType ; 5151} 5152 5153// To the extent any swizzling can become part of the chain 5154// of accesses instead of a post operation, make it so. 5155// If 'dynamic' is true, include transferring the dynamic component, 5156// otherwise, leave it pending. 5157// 5158// Does not generate code. just updates the access chain. 5159void Builder ::transferAccessChainSwizzle (bool dynamic ) 5160{ 5161// non existent? 5162if (accessChain .swizzle .size ()== 0 && accessChain .component == NoResult ) 5163return ; 5164 5165// too complex? 5166// (this requires either a swizzle, or generating code for a dynamic component) 5167if (accessChain .swizzle .size ()> 1 ) 5168return ; 5169 5170// single component, either in the swizzle and/or dynamic component 5171if (accessChain .swizzle .size ()== 1 ) { 5172assert (accessChain .component == NoResult ); 5173// handle static component selection 5174accessChain .indexChain .push_back (makeUintConstant (accessChain .swizzle .front ())); 5175accessChain .swizzle .clear (); 5176accessChain .preSwizzleBaseType = NoType ; 5177 }else if (dynamic && accessChain .component != NoResult ) { 5178assert (accessChain .swizzle .size ()== 0 ); 5179// handle dynamic component 5180accessChain .indexChain .push_back (accessChain .component ); 5181accessChain .preSwizzleBaseType = NoType ; 5182accessChain .component = NoResult ; 5183 } 5184} 5185 5186// Utility method for creating a new block and setting the insert point to 5187// be in it. This is useful for flow-control operations that need a "dummy" 5188// block proceeding them (e.g. instructions after a discard, etc). 5189void Builder ::createAndSetNoPredecessorBlock (const char * /*name*/ ) 5190{ 5191Block * block = new Block (getUniqueId (),buildPoint -> getParent ()); 5192block -> setUnreachable (); 5193buildPoint -> getParent ().addBlock (block ); 5194setBuildPoint (block ); 5195 5196// if (name) 5197// addName(block->getId(), name); 5198} 5199 5200// Comments in header 5201void Builder ::createBranch (bool implicit ,Block * block ) 5202{ 5203Instruction * branch = new Instruction (Op ::OpBranch ); 5204branch -> addIdOperand (block -> getId ()); 5205if (implicit ) { 5206addInstructionNoDebugInfo (std::unique_ptr < Instruction > (branch )); 5207 } 5208else { 5209addInstruction (std::unique_ptr < Instruction > (branch )); 5210 } 5211block -> addPredecessor (buildPoint ); 5212} 5213 5214// Create OpConstantSizeOfEXT 5215Id Builder ::createConstantSizeOfEXT (Id typeId ) 5216{ 5217auto constantSize = constantSizeOfEXTIds .find (typeId ); 5218if (constantSize != constantSizeOfEXTIds .end ()) 5219return constantSize -> second ; 5220 5221Id resultType = makeUintType (32 ); 5222Instruction * inst = new Instruction (getUniqueId (),resultType ,Op ::OpConstantSizeOfEXT ); 5223inst -> addIdOperand (typeId ); 5224constantsTypesGlobals .push_back (std::unique_ptr < Instruction > (inst )); 5225module .mapInstruction (inst ); 5226constantSizeOfEXTIds [typeId ]= inst -> getResultId (); 5227return inst -> getResultId (); 5228} 5229 5230void Builder ::createSelectionMerge (Block * mergeBlock ,SelectionControlMask control ) 5231{ 5232Instruction * merge = new Instruction (Op ::OpSelectionMerge ); 5233merge -> reserveOperands (2 ); 5234merge -> addIdOperand (mergeBlock -> getId ()); 5235merge -> addImmediateOperand (control ); 5236addInstruction (std::unique_ptr < Instruction > (merge )); 5237} 5238 5239void Builder ::createLoopMerge (Block * mergeBlock ,Block * continueBlock ,LoopControlMask control , 5240const std::vector < unsigned int >& operands ) 5241{ 5242Instruction * merge = new Instruction (Op ::OpLoopMerge ); 5243merge -> reserveOperands (operands .size ()+ 3 ); 5244merge -> addIdOperand (mergeBlock -> getId ()); 5245merge -> addIdOperand (continueBlock -> getId ()); 5246merge -> addImmediateOperand (control ); 5247for (int op = 0 ;op < (int )operands .size ();++ op ) 5248merge -> addImmediateOperand (operands [op ]); 5249addInstruction (std::unique_ptr < Instruction > (merge )); 5250} 5251 5252void Builder ::createConditionalBranch (Id condition ,Block * thenBlock ,Block * elseBlock ) 5253{ 5254Instruction * branch = new Instruction (Op ::OpBranchConditional ); 5255branch -> reserveOperands (3 ); 5256branch -> addIdOperand (condition ); 5257branch -> addIdOperand (thenBlock -> getId ()); 5258branch -> addIdOperand (elseBlock -> getId ()); 5259 5260// A conditional branch is always attached to a condition expression 5261addInstructionNoDebugInfo (std::unique_ptr < Instruction > (branch )); 5262 5263thenBlock -> addPredecessor (buildPoint ); 5264elseBlock -> addPredecessor (buildPoint ); 5265} 5266 5267// OpSource 5268// [OpSourceContinued] 5269// ... 5270void Builder ::dumpSourceInstructions (const spv::Id fileId ,const std::string & text , 5271 std::vector < unsigned int >& out )const 5272{ 5273const int maxWordCount = 0xFFFF ; 5274const int opSourceWordCount = 4 ; 5275const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount )- 1 ; 5276 5277if (sourceLang != SourceLanguage ::Unknown ) { 5278// OpSource Language Version File Source 5279Instruction sourceInst (NoResult ,NoType ,Op ::OpSource ); 5280sourceInst .reserveOperands (3 ); 5281sourceInst .addImmediateOperand (sourceLang ); 5282sourceInst .addImmediateOperand (sourceVersion ); 5283// File operand 5284if (fileId != NoResult ) { 5285sourceInst .addIdOperand (fileId ); 5286// Source operand 5287if (text .size ()> 0 ) { 5288int nextByte = 0 ; 5289 std::string subString ; 5290while ((int )text .size ()- nextByte > 0 ) { 5291subString = text .substr (nextByte ,nonNullBytesPerInstruction ); 5292if (nextByte == 0 ) { 5293// OpSource 5294sourceInst .addStringOperand (subString .c_str ()); 5295sourceInst .dump (out ); 5296 }else { 5297// OpSourcContinued 5298Instruction sourceContinuedInst (Op ::OpSourceContinued ); 5299sourceContinuedInst .addStringOperand (subString .c_str ()); 5300sourceContinuedInst .dump (out ); 5301 } 5302nextByte += nonNullBytesPerInstruction ; 5303 } 5304 }else 5305sourceInst .dump (out ); 5306 }else 5307sourceInst .dump (out ); 5308 } 5309} 5310 5311// Dump an OpSource[Continued] sequence for the source and every include file 5312void Builder ::dumpSourceInstructions (std::vector < unsigned int >& out )const 5313{ 5314if (emitNonSemanticShaderDebugInfo )return ; 5315dumpSourceInstructions (mainFileId ,sourceText ,out ); 5316for (auto iItr = includeFiles .begin ();iItr != includeFiles .end ();++ iItr ) 5317dumpSourceInstructions (iItr -> first ,* iItr -> second ,out ); 5318} 5319 5320template < class Range > void Builder ::dumpInstructions (std::vector < unsigned int >& out ,const Range & instructions )const 5321{ 5322for (const auto & inst :instructions ) { 5323inst -> dump (out ); 5324 } 5325} 5326 5327void Builder ::dumpModuleProcesses (std::vector < unsigned int >& out )const 5328{ 5329for (int i = 0 ;i < (int )moduleProcesses .size ();++ i ) { 5330Instruction moduleProcessed (Op ::OpModuleProcessed ); 5331moduleProcessed .addStringOperand (moduleProcesses [i ]); 5332moduleProcessed .dump (out ); 5333 } 5334} 5335 5336bool Builder ::DecorationInstructionLessThan ::operator()(const std::unique_ptr < Instruction >& lhs , 5337const std::unique_ptr < Instruction >& rhs )const 5338{ 5339// Order by the id to which the decoration applies first. This is more intuitive. 5340assert (lhs -> isIdOperand (0 )&& rhs -> isIdOperand (0 )); 5341if (lhs -> getIdOperand (0 )!= rhs -> getIdOperand (0 )) { 5342return lhs -> getIdOperand (0 )< rhs -> getIdOperand (0 ); 5343 } 5344 5345if (lhs -> getOpCode ()!= rhs -> getOpCode ()) 5346return lhs -> getOpCode ()< rhs -> getOpCode (); 5347 5348// Now compare the operands. 5349int minSize = std::min (lhs -> getNumOperands (),rhs -> getNumOperands ()); 5350for (int i = 1 ;i < minSize ;++ i ) { 5351if (lhs -> isIdOperand (i )!= rhs -> isIdOperand (i )) { 5352return lhs -> isIdOperand (i )< rhs -> isIdOperand (i ); 5353 } 5354 5355if (lhs -> isIdOperand (i )) { 5356if (lhs -> getIdOperand (i )!= rhs -> getIdOperand (i )) { 5357return lhs -> getIdOperand (i )< rhs -> getIdOperand (i ); 5358 } 5359 }else { 5360if (lhs -> getImmediateOperand (i )!= rhs -> getImmediateOperand (i )) { 5361return lhs -> getImmediateOperand (i )< rhs -> getImmediateOperand (i ); 5362 } 5363 } 5364 } 5365 5366if (lhs -> getNumOperands ()!= rhs -> getNumOperands ()) 5367return lhs -> getNumOperands ()< rhs -> getNumOperands (); 5368 5369// In this case they are equal. 5370return false; 5371} 5372}// end spv namespace