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-2020 Google, Inc. 4// Copyright (C) 2017 ARM Limited. 5// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. 6// 7// All rights reserved. 8// 9// Redistribution and use in source and binary forms, with or without 10// modification, are permitted provided that the following conditions 11// are met: 12// 13// Redistributions of source code must retain the above copyright 14// notice, this list of conditions and the following disclaimer. 15// 16// Redistributions in binary form must reproduce the above 17// copyright notice, this list of conditions and the following 18// disclaimer in the documentation and/or other materials provided 19// with the distribution. 20// 21// Neither the name of 3Dlabs Inc. Ltd. nor the names of its 22// contributors may be used to endorse or promote products derived 23// from this software without specific prior written permission. 24// 25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 28// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 30// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 32// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 35// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36// POSSIBILITY OF SUCH DAMAGE. 37 38// 39// "Builder" is an interface to fully build SPIR-V IR. Allocate one of 40// these to build (a thread safe) internal SPIR-V representation (IR), 41// and then dump it as a binary stream according to the SPIR-V specification. 42// 43// A Builder has a 1:1 relationship with a SPIR-V module. 44// 45 46#pragma once 47#ifndef SpvBuilder_H 48#define SpvBuilder_H 49 50#include "Logger.h" 51#define SPV_ENABLE_UTILITY_CODE 52#include "spirv.hpp11" 53#include "spvIR.h" 54#include "spvUtil.h" 55 56namespace spv { 57#include "GLSL.ext.KHR.h" 58#include "GLSL.ext.EXT.h" 59#include "NonSemanticShaderDebugInfo.h" 60} 61 62#include <algorithm> 63#include <cstdint> 64#include <map> 65#include <memory> 66#include <set> 67#include <sstream> 68#include <stack> 69#include <unordered_map> 70#include <unordered_set> 71#include <map> 72 73namespace spv { 74 75typedef enum { 76Spv_1_0 = (1 <<16 ), 77Spv_1_1 = (1 <<16 ) | (1 <<8 ), 78Spv_1_2 = (1 <<16 ) | (2 <<8 ), 79Spv_1_3 = (1 <<16 ) | (3 <<8 ), 80Spv_1_4 = (1 <<16 ) | (4 <<8 ), 81Spv_1_5 = (1 <<16 ) | (5 <<8 ), 82Spv_1_6 = (1 <<16 ) | (6 <<8 ), 83}SpvVersion ; 84 85struct StructMemberDebugInfo { 86std ::string name {}; 87int line {0 }; 88int column {0 }; 89// Set if the caller knows a better debug type than what is associated with the functional SPIR-V type. 90spv ::Id debugTypeOverride {0 }; 91}; 92 93class Builder { 94class CompositeConstantKey { 95public : 96enum Kind { 97Key_Instruction , 98Key_CompositeConstant , 99 }; 100 101CompositeConstantKey ( 102Op typeClass ,Instruction * instruction ) 103 :kind (Key_Instruction ) 104 ,instruction (instruction ) 105 ,typeClass (typeClass ) 106 ,opcode (Op ::OpNop ) 107 ,typeId (NoType ) 108 ,numMembers (0 ) 109 ,comps () 110 {} 111 112CompositeConstantKey ( 113Op typeClass ,Op opcode ,Id typeId ,const std ::vector < Id >& comps ,size_t numMembers ) 114 :kind (Key_CompositeConstant ) 115 ,instruction (nullptr ) 116 ,typeClass (typeClass ) 117 ,opcode (opcode ) 118 ,typeId (typeId ) 119 ,numMembers (numMembers ) 120 ,comps (comps ) 121 {} 122 123Instruction * getInstruction ()const { 124return instruction ; 125 } 126 127// Functor to hash an instruction key. 128struct Hash { 129size_t operator ()(CompositeConstantKey const & key )const { 130switch (key .kind ) { 131case Key_Instruction : 132 { 133Instruction * constant = key .instruction ; 134size_t num_operands = constant -> getNumOperands (); 135size_t hash = std ::hash < Op > ()(key. typeClass ) * 7 ^ 136std::hash < Op > ()(constant -> getOpCode ()) * 11 ^ 137std::hash < Id > ()(constant -> getTypeId ()) * 13 ^ 138std::hash < size_t > ()(num_operands) * 17 ; 139for ( size_t i = 0 ; i < num_operands ; ++ i ) { 140hash = hash * 19 ^ std ::hash < Id > ()(constant -> getIdOperand (i)); 141} 142return hash; 143} 144case Key_CompositeConstant: 145{ 146size_t hash = std::hash < Op > ()(key. typeClass ) * 7 ^ 147std::hash < Op > ()(key. opcode ) * 11 ^ 148std::hash < Id > ()(key. typeId ) * 13 ^ 149std::hash < size_t > ()(key. numMembers ) * 17 ; 150for ( size_t i = 0 ; i < key. numMembers ; ++ i) { 151hash = hash * 19 ^ std::hash < Id > ()(key. comps [i]); 152} 153return hash; 154} 155} 156return 0 ; 157} 158}; 159 160// Functor to compare two instruction keys. 161struct Equal { 162bool operator () ( CompositeConstantKey const & a, CompositeConstantKey const & b) const 163{ 164if ( a .typeClass != b.typeClass) 165return false ; 166 167if ( a .kind == b.kind) { 168// we need only the Instruction case, as the other keys only used for searching 169return 170a . kind == Key_Instruction && 171a . instruction == b . instruction ; 172} 173const CompositeConstantKey * s = & a; 174const CompositeConstantKey * o = & b; 175 176if (s -> kind == Key_CompositeConstant && o -> kind == Key_Instruction) { 177s = o; 178o = & a; 179} 180 181// compare a composite constant with an instruction 182if (s -> kind == Key_Instruction && o -> kind == Key_CompositeConstant) { 183Instruction * constant = s -> instruction ; 184if (constant -> getTypeId () != o -> typeId ) 185return false; 186 187if (constant -> getOpCode () != o -> opcode ) { 188return false; 189} 190 191if (constant -> getNumOperands () != ( int )o -> numMembers ) 192return false; 193 194// same contents? 195for ( int op = 0 ; op < constant -> getNumOperands (); ++ op) { 196if (constant -> getIdOperand (op) != o -> comps [op]) { 197return false; 198} 199} 200return true; 201} 202return false; 203} 204}; 205 206private : 207Kind kind; 208Instruction * instruction; 209Op typeClass; 210Op opcode; 211Id typeId; 212size_t numMembers; 213std ::vector < Id > comps; 214}; 215 216class StructConstantKey { 217public : 218enum Kind { 219Key_Instruction, 220Key_StructConstant, 221}; 222 223StructConstantKey ( 224Instruction * instruction) 225: kind (Key_Instruction) 226, instruction ( instruction ) 227, typeId ( NoType ) 228, numMembers ( 0 ) 229, comps () 230{} 231 232StructConstantKey ( 233Id typeId, const std ::vector < Id >& comps, size_t numMembers) 234: kind (Key_StructConstant) 235, instruction ( nullptr ) 236, typeId ( typeId ) 237, numMembers ( numMembers ) 238, comps ( comps ) 239{} 240 241Instruction * getInstruction () const { 242return instruction; 243} 244 245// Functor to hash an instruction key. 246struct Hash { 247size_t operator ()( StructConstantKey const & key) const { 248switch ( key .kind) { 249case Key_Instruction: 250{ 251Instruction * constant = key. instruction ; 252size_t hash = std::hash < Id > ()(constant -> getTypeId ()) * 13 ; 253for ( size_t i = 0 , n = constant -> getNumOperands(); i < n ; ++ i ) { 254hash = hash * 19 ^ std ::hash < Id > ()(constant -> getIdOperand (i)); 255} 256return hash; 257} 258case Key_StructConstant: 259{ 260size_t hash = std::hash < Id > ()(key. typeId ) * 13 ; 261for ( size_t i = 0 ; i < key. numMembers ; ++ i) { 262hash = hash * 19 ^ std::hash < Id > ()(key. comps [i]); 263} 264return hash; 265} 266} 267return 0 ; 268} 269}; 270 271// Functor to compare two instruction keys. 272struct Equal { 273bool operator () ( StructConstantKey const & a, StructConstantKey const & b) const 274{ 275if ( a .kind == b.kind) { 276// we need only the Instruction case, as the other keys only used for searching 277return 278a . kind == Key_Instruction && 279a . instruction == b . instruction ; 280} 281const StructConstantKey * s = & a; 282const StructConstantKey * o = & b; 283 284if (s -> kind == Key_StructConstant && o -> kind == Key_Instruction) { 285s = o; 286o = & a; 287} 288 289// compare a composite constant with an instruction 290if (s -> kind == Key_Instruction && o -> kind == Key_StructConstant) { 291Instruction * constant = s -> instruction ; 292if (constant -> getTypeId () != o -> typeId ) 293return false; 294 295if (constant -> getNumOperands () != ( int )o -> numMembers ) 296return false; 297 298// same contents? 299for ( int op = 0 ; op < constant -> getNumOperands (); ++ op) { 300if (constant -> getIdOperand (op) != o -> comps [op]) { 301return false; 302} 303} 304return true; 305} 306return false; 307} 308}; 309 310private : 311Kind kind; 312Instruction * instruction; 313Id typeId; 314size_t numMembers; 315std ::vector < Id > comps; 316}; 317 318public : 319Builder( unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger * logger); 320virtual ~ Builder (); 321 322static const int maxMatrixSize = 4 ; 323 324unsigned int getSpvVersion () const { return spvVersion; } 325 326void setSource ( spv ::SourceLanguage lang, int version) 327{ 328sourceLang = lang; 329sourceVersion = version; 330} 331spv:: Id getStringId( const std ::string & str) 332{ 333auto sItr = stringIds. find (str); 334if (sItr != stringIds. end ()) 335return sItr -> second ; 336spv :: Id strId = getUniqueId (); 337Instruction * fileString = new Instruction (strId, NoType, Op::OpString); 338const char * file_c_str = str. c_str (); 339fileString -> addStringOperand (file_c_str); 340strings. push_back (std::unique_ptr < Instruction > (fileString)); 341module. mapInstruction (fileString); 342stringIds[file_c_str] = strId; 343return strId; 344} 345 346spv :: Id getMainFileId () const { return mainFileId; } 347 348// Initialize the main source file name 349void setDebugMainSourceFile ( const std ::string & file) 350{ 351if (trackDebugInfo) { 352dirtyLineTracker = true; 353mainFileId = getStringId (file); 354currentFileId = mainFileId; 355} 356} 357 358// Set the debug source location tracker in the builder. 359// The upcoming instructions in basic blocks will be associated to this location. 360void setDebugSourceLocation ( int line, const char * filename) 361{ 362if (trackDebugInfo) { 363dirtyLineTracker = true; 364if (line != 0 ) { 365// TODO: This is special handling of some AST nodes having (untracked) line 0. 366// But they should have a valid line number. 367currentLine = line; 368if (filename) { 369currentFileId = getStringId (filename); 370} 371} 372} 373} 374 375void setSourceText ( const std ::string & text) { sourceText = text; } 376void addSourceExtension ( const char * ext) { sourceExtensions. push_back (ext); } 377void addModuleProcessed ( const std ::string & p) { moduleProcesses. push_back (p. c_str ()); } 378void setEmitSpirvDebugInfo () 379{ 380trackDebugInfo = true; 381emitSpirvDebugInfo = true; 382} 383void setEmitNonSemanticShaderDebugInfo ( bool emitSourceText) 384{ 385trackDebugInfo = true; 386emitNonSemanticShaderDebugInfo = true; 387importNonSemanticShaderDebugInfoInstructions (); 388 389if (emitSourceText) { 390emitNonSemanticShaderDebugSource = emitSourceText; 391} 392} 393// Emit only an OpString with the source file name and an OpSource referencing it. 394// Unlike the full debug modes, this does not enable line tracking or emit source text. 395void setEmitSourceFileName ( const std ::string & file) 396{ 397mainFileId = getStringId (file); 398currentFileId = mainFileId; 399} 400void addExtension ( const char * ext) { extensions. insert (ext); } 401void removeExtension ( const char * ext) 402{ 403extensions. erase (ext); 404} 405void addIncorporatedExtension ( const char * ext, SpvVersion incorporatedVersion) 406{ 407if ( getSpvVersion () < static_cast < unsigned > (incorporatedVersion)) 408addExtension (ext); 409} 410void promoteIncorporatedExtension ( const char * baseExt, const char * promoExt, SpvVersion incorporatedVersion) 411{ 412removeExtension (baseExt); 413addIncorporatedExtension (promoExt, incorporatedVersion); 414} 415void addInclude ( const std ::string & name, const std ::string & text) 416{ 417spv :: Id incId = getStringId (name); 418includeFiles[incId] = & text; 419} 420Id import ( const char * ); 421void setMemoryModel ( spv ::AddressingModel addr, spv ::MemoryModel mem) 422{ 423addressModel = addr; 424memoryModel = mem; 425} 426 427void addCapability ( spv ::Capability cap) { capabilities. insert (cap); } 428 429// To get a new <id> for anything needing a new one. 430Id getUniqueId () { return ++ uniqueId; } 431 432// To get a set of new <id>s, e.g., for a set of function parameters 433Id getUniqueIds ( int numIds) 434{ 435Id id = uniqueId + 1 ; 436uniqueId += numIds; 437return id; 438} 439 440// Maps the given OpType Id to a Non-Semantic DebugType Id. 441Id getDebugType ( Id type) { 442if (auto it = debugTypeIdLookup. find (type); it != debugTypeIdLookup. end ()) { 443return it -> second ; 444} 445 446return NoType; 447} 448 449// Maps the given OpFunction Id to a Non-Semantic DebugFunction Id. 450Id getDebugFunction ( Id func) { 451if (auto it = debugFuncIdLookup. find (func); it != debugFuncIdLookup. end ()) { 452return it -> second ; 453} 454 455return NoResult; 456} 457 458// For creating new types (will return old type if the requested one was already made). 459Id makeVoidType (); 460Id makeBoolType (); 461Id makePointer ( StorageClass , Id pointee); 462Id makeForwardPointer ( StorageClass ); 463Id makePointerFromForwardPointer ( StorageClass , Id forwardPointerType, Id pointee); 464Id makeUntypedPointer ( StorageClass storageClass, bool setBufferPointer = false); 465Id makeIntegerType ( int width, bool hasSign); // generic 466Id makeIntType ( int width) { return makeIntegerType (width, true); } 467Id makeUintType ( int width) { return makeIntegerType (width, false); } 468Id makeFloatType ( int width); 469Id makeBFloat16Type (); 470Id makeFloatE5M2Type (); 471Id makeFloatE4M3Type (); 472Id makeFloatE2M1Type (); 473Id makeFloatE3M2Type (); 474Id makeFloatE2M3Type (); 475Id makeFloatUE8M0Type (); 476Id makeFloatMXINT8Type (); 477Id makeFloatOcpMicroscalingType ( uint32_t width, FPEncoding encoding, Capability cap); 478Id makeStructType ( const std ::vector < Id >& members, const std ::vector < spv::StructMemberDebugInfo >& memberDebugInfo, 479const char * name, bool const compilerGenerated = true); 480Id makeStructResultType ( Id type0, Id type1); 481Id makeVectorType ( Id component, int size); 482Id makeMatrixType ( Id component, int cols, int rows); 483Id makeArrayType ( Id element, Id sizeId, int stride); // 0 stride means no stride decoration 484Id makeRuntimeArray ( Id element); 485Id makeFunctionType ( Id returnType, const std ::vector < Id >& paramTypes); 486Id makeImageType ( Id sampledType, Dim , bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format, const char * debugNames); 487Id makeSamplerType ( const char * debugName); 488Id makeSampledImageType ( Id imageType, const char * debugName); 489Id makeCooperativeMatrixTypeKHR ( Id component, Id scope, Id rows, Id cols, Id use); 490Id makeCooperativeMatrixTypeNV ( Id component, Id scope, Id rows, Id cols); 491Id makeCooperativeMatrixTypeWithSameShape ( Id component, Id otherType); 492Id makeCooperativeVectorTypeNV ( Id componentType, Id components); 493Id makeTensorTypeARM ( Id elementType, Id rank); 494Id makeGenericType ( spv ::Op opcode, std ::vector < spv::IdImmediate >& operands); 495 496// SPIR-V NonSemantic Shader DebugInfo Instructions 497Id makeDebugInfoNone (); 498Id makeBoolDebugType ( int const size); 499Id makeIntegerDebugType ( int const width, bool const hasSign); 500Id makeFloatDebugType ( int const width, Id const fpEncoding = NoType); 501Id makeSequentialDebugType ( Id const baseType, Id const componentCount, NonSemanticShaderDebugInfoInstructions const sequenceType); 502Id makeArrayDebugType ( Id const baseType, Id const componentCount); 503Id makeVectorDebugType ( Id const baseType, int const componentCount); 504Id makeMatrixDebugType ( Id const vectorType, int const vectorCount, bool columnMajor = true); 505Id makeMemberDebugType ( Id const memberType, StructMemberDebugInfo const & debugTypeLoc); 506Id makeCompositeDebugType ( std ::vector < Id > const & memberTypes, std ::vector < StructMemberDebugInfo > const & memberDebugInfo, 507char const * const name, NonSemanticShaderDebugInfoDebugCompositeType const tag); 508Id makeOpaqueDebugType ( char const * const name); 509Id makeVectorIdDebugType ( Id componentType, Id componentCount); 510Id makeCooperativeMatrixDebugTypeKHR ( Id componentType, Id scope, Id rows, Id cols, Id use); 511Id makePointerDebugType ( StorageClass storageClass, Id const baseType); 512Id makeForwardPointerDebugType ( StorageClass storageClass); 513Id makeDebugSource ( const Id fileName); 514Id makeDebugCompilationUnit (); 515Id createDebugGlobalVariable ( Id const type, char const * const name, Id const variable); 516Id createDebugLocalVariable ( Id type, char const * const name, size_t const argNumber = 0 ); 517Id makeDebugExpression (); 518Id makeDebugDeclare ( Id const debugLocalVariable, Id const pointer); 519Id makeDebugValue ( Id const debugLocalVariable, Id const value); 520Id makeDebugFunctionType ( Id returnType, const std ::vector < Id >& paramTypes); 521Id makeDebugFunction ( Function * function, Id nameId, Id funcTypeId); 522Id makeDebugEntryPoint ( Function * function, const char * compilerSignature, const char * commandLineArguments, 523const char * currentWorkingDirectory = nullptr ); 524Id makeDebugLexicalBlock ( uint32_t line, uint32_t column); 525std :: string unmangleFunctionName( std :: string const & name) const ; 526 527// Initialize non-semantic debug information for a function, including those of: 528// - The function definition 529// - The function parameters 530void setupFunctionDebugInfo ( Function * function, const char * name, const std ::vector < Id >& paramTypes, 531const std ::vector < char const *>& paramNames); 532 533// accelerationStructureNV type 534Id makeAccelerationStructureType (); 535// rayQueryEXT type 536Id makeRayQueryType (); 537// hitObjectNV type 538Id makeHitObjectNVType (); 539// hitObjectEXT type 540Id makeHitObjectEXTType (); 541 542// For querying about types. 543Id getTypeId ( Id resultId) const { return module. getTypeId (resultId); } 544Id getDerefTypeId ( Id resultId) const; 545Op getOpCode ( Id id) const { return module. getInstruction (id) -> getOpCode (); } 546Op getTypeClass ( Id typeId) const { return getOpCode (typeId); } 547Op getMostBasicTypeClass ( Id typeId) const; 548unsigned int getNumComponents ( Id resultId) const { return getNumTypeComponents ( getTypeId (resultId)); } 549unsigned int getNumTypeConstituents ( Id typeId) const; 550unsigned int getNumTypeComponents ( Id typeId) const { return getNumTypeConstituents (typeId); } 551Id getScalarTypeId ( Id typeId) const; 552Id getContainedTypeId ( Id typeId) const; 553Id getContainedTypeId ( Id typeId, int ) const; 554StorageClass getTypeStorageClass ( Id typeId) const { return module. getStorageClass (typeId); } 555ImageFormat getImageTypeFormat ( Id typeId) const 556{ return ( ImageFormat )module. getInstruction (typeId) -> getImmediateOperand ( 6 ); } 557Id getResultingAccessChainType () const; 558Id getIdOperand ( Id resultId, int idx) { return module. getInstruction (resultId) -> getIdOperand (idx); } 559Id getCooperativeVectorNumComponents ( Id typeId) const { return module. getInstruction (typeId) -> getIdOperand ( 1 ); } 560 561bool isPointer ( Id resultId) const { return isPointerType ( getTypeId (resultId)); } 562bool isUntypedPointer ( Id resultId) const 563{ 564const Id tid = getTypeId (resultId); 565// Expect that OpString have no type 566if (tid == 0 ) 567return false; 568return isUntypedPointerType (tid); 569} 570bool isScalar ( Id resultId) const { return isScalarType ( getTypeId (resultId)); } 571bool isVector ( Id resultId) const { return isVectorType ( getTypeId (resultId)); } 572bool isMatrix ( Id resultId) const { return isMatrixType ( getTypeId (resultId)); } 573bool isCooperativeMatrix ( Id resultId)const { return isCooperativeMatrixType ( getTypeId (resultId)); } 574bool isCooperativeVector ( Id resultId)const { return isCooperativeVectorType ( getTypeId (resultId)); } 575bool isAggregate ( Id resultId) const { return isAggregateType ( getTypeId (resultId)); } 576bool isSampledImage ( Id resultId) const { return isSampledImageType ( getTypeId (resultId)); } 577bool isTensorView ( Id resultId)const { return isTensorViewType ( getTypeId (resultId)); } 578 579bool isBoolType ( Id typeId) 580{ return groupedTypes[ enumCast (Op::OpTypeBool)]. size () > 0 && typeId == groupedTypes[ enumCast (Op::OpTypeBool)]. back () -> getResultId (); } 581bool isIntType ( Id typeId) const 582{ return getTypeClass (typeId) == Op::OpTypeInt && module. getInstruction (typeId) -> getImmediateOperand ( 1 ) != 0 ; } 583bool isUintType ( Id typeId) const 584{ return getTypeClass (typeId) == Op::OpTypeInt && module. getInstruction (typeId) -> getImmediateOperand ( 1 ) == 0 ; } 585bool isFloatType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeFloat; } 586bool isPointerType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypePointer; } 587bool isUntypedPointerType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeUntypedPointerKHR; } 588bool isScalarType ( Id typeId) const 589{ return getTypeClass (typeId) == Op::OpTypeFloat || getTypeClass (typeId) == Op::OpTypeInt || 590getTypeClass (typeId) == Op::OpTypeBool; } 591bool isVectorType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeVector; } 592bool isMatrixType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeMatrix; } 593bool isStructType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeStruct; } 594bool isArrayType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeArray; } 595bool isCooperativeMatrixType ( Id typeId)const 596{ 597return getTypeClass (typeId) == Op::OpTypeCooperativeMatrixKHR || getTypeClass (typeId) == Op::OpTypeCooperativeMatrixNV; 598} 599bool isTensorViewType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeTensorViewNV; } 600bool isCooperativeVectorType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeCooperativeVectorNV; } 601bool isTensorTypeARM ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeTensorARM; } 602bool isAggregateType ( Id typeId) const 603{ return isArrayType (typeId) || isStructType (typeId) || isCooperativeMatrixType (typeId); } 604bool isImageType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeImage; } 605bool isSamplerType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeSampler; } 606bool isSampledImageType ( Id typeId) const { return getTypeClass (typeId) == Op::OpTypeSampledImage; } 607bool containsType ( Id typeId, Op typeOp, unsigned int width) const; 608bool containsPhysicalStorageBufferOrArray ( Id typeId) const; 609 610bool isConstantOpCode ( Op opcode) const; 611bool isSpecConstantOpCode ( Op opcode) const; 612bool isConstant ( Id resultId) const { return isConstantOpCode ( getOpCode (resultId)); } 613bool isConstantScalar ( Id resultId) const { return getOpCode (resultId) == Op::OpConstant; } 614bool isSpecConstant ( Id resultId) const { return isSpecConstantOpCode ( getOpCode (resultId)); } 615unsigned int getConstantScalar ( Id resultId) const 616{ return module. getInstruction (resultId) -> getImmediateOperand ( 0 ); } 617StorageClass getStorageClass ( Id resultId) const { return getTypeStorageClass ( getTypeId (resultId)); } 618 619bool isVariableOpCode ( Op opcode) const { return opcode == Op::OpVariable; } 620bool isVariable ( Id resultId) const { return isVariableOpCode ( getOpCode (resultId)); } 621bool isGlobalStorage ( Id resultId) const { return getStorageClass (resultId) != StorageClass::Function; } 622bool isGlobalVariable ( Id resultId) const { return isVariable (resultId) && isGlobalStorage (resultId); } 623// See if a resultId is valid for use as an initializer. 624bool isValidInitializer ( Id resultId) const { return isConstant (resultId) || isGlobalVariable (resultId); } 625 626int getScalarTypeWidth ( Id typeId) const 627{ 628Id scalarTypeId = getScalarTypeId (typeId); 629assert ( getTypeClass (scalarTypeId) == Op::OpTypeInt || getTypeClass (scalarTypeId) == Op::OpTypeFloat); 630return module. getInstruction (scalarTypeId) -> getImmediateOperand ( 0 ); 631} 632 633unsigned int getTypeNumColumns ( Id typeId) const 634{ 635assert ( isMatrixType (typeId)); 636return getNumTypeConstituents (typeId); 637} 638unsigned int getNumColumns ( Id resultId) const { return getTypeNumColumns ( getTypeId (resultId)); } 639unsigned int getTypeNumRows ( Id typeId) const 640{ 641assert ( isMatrixType (typeId)); 642return getNumTypeComponents ( getContainedTypeId (typeId)); 643} 644unsigned int getNumRows ( Id resultId) const { return getTypeNumRows ( getTypeId (resultId)); } 645 646Dim getTypeDimensionality ( Id typeId) const 647{ 648assert ( isImageType (typeId)); 649return ( Dim )module. getInstruction (typeId) -> getImmediateOperand ( 1 ); 650} 651Id getImageType ( Id resultId) const 652{ 653Id typeId = getTypeId (resultId); 654assert ( isImageType (typeId) || isSampledImageType (typeId)); 655return isSampledImageType (typeId) ? module. getInstruction (typeId) -> getIdOperand ( 0 ) : typeId; 656} 657bool isArrayedImageType ( Id typeId) const 658{ 659assert ( isImageType (typeId)); 660return module. getInstruction (typeId) -> getImmediateOperand ( 3 ) != 0 ; 661} 662 663// For making new constants (will return old constant if the requested one was already made). 664Id makeNullConstant ( Id typeId); 665Id makeBoolConstant ( bool b, bool specConstant = false); 666Id makeIntConstant ( Id typeId, unsigned value, bool specConstant); 667Id makeInt64Constant ( Id typeId, unsigned long long value, bool specConstant); 668Id makeInt8Constant ( int i, bool specConstant = false) 669{ return makeIntConstant ( makeIntType ( 8 ), ( unsigned )i, specConstant); } 670Id makeUint8Constant ( unsigned u, bool specConstant = false) 671{ return makeIntConstant ( makeUintType ( 8 ), u, specConstant); } 672Id makeInt16Constant ( int i, bool specConstant = false) 673{ return makeIntConstant ( makeIntType ( 16 ), ( unsigned )i, specConstant); } 674Id makeUint16Constant ( unsigned u, bool specConstant = false) 675{ return makeIntConstant ( makeUintType ( 16 ), u, specConstant); } 676Id makeIntConstant ( int i, bool specConstant = false) 677{ return makeIntConstant ( makeIntType ( 32 ), ( unsigned )i, specConstant); } 678Id makeUintConstant ( unsigned u, bool specConstant = false) 679{ return makeIntConstant ( makeUintType ( 32 ), u, specConstant); } 680Id makeUintConstant ( Scope u, bool specConstant = false) 681{ return makeUintConstant (( unsigned )u, specConstant); } 682Id makeUintConstant ( StorageClass u, bool specConstant = false) 683{ return makeUintConstant (( unsigned )u, specConstant); } 684Id makeUintConstant ( MemorySemanticsMask u, bool specConstant = false) 685{ return makeUintConstant (( unsigned )u, specConstant); } 686Id makeUintConstant ( SourceLanguage u, bool specConstant = false) 687{ return makeUintConstant (( unsigned )u, specConstant); } 688Id makeInt64Constant ( long long i, bool specConstant = false) 689{ return makeInt64Constant ( makeIntType ( 64 ), ( unsigned long long )i, specConstant); } 690Id makeUint64Constant ( unsigned long long u, bool specConstant = false) 691{ return makeInt64Constant ( makeUintType ( 64 ), u, specConstant); } 692Id makeFloatConstant ( float f, bool specConstant = false); 693Id makeDoubleConstant ( double d, bool specConstant = false); 694Id makeFloat16Constant ( float f16, bool specConstant = false); 695Id makeBFloat16Constant ( float bf16, bool specConstant = false); 696Id makeFloatE5M2Constant ( float fe5m2, bool specConstant = false); 697Id makeFloatE4M3Constant ( float fe4m3, bool specConstant = false); 698Id makeFloatE2M1Constant ( float fe2m1, bool specConstant = false); 699Id makeFloatE3M2Constant ( float fe3m2, bool specConstant = false); 700Id makeFloatE2M3Constant ( float fe2m3, bool specConstant = false); 701Id makeFloatUE8M0Constant ( float fue8m0, bool specConstant = false); 702Id makeFloatMXINT8Constant ( float mxint8, bool specConstant = false); 703Id makeFpConstant ( Id type, double d, bool specConstant = false); 704 705Id importNonSemanticShaderDebugInfoInstructions (); 706// Ensure the NonSemantic.Shader.DebugInfo import string names at least `version`. 707// If the import instruction already exists, its name is patched in place. 708// If it has not been created yet, importNonSemanticShaderDebugInfoInstructions() 709// will use the updated version when it runs. 710void requireNonSemanticShaderDebugInfoVersion ( unsigned version); 711 712// Turn the array of constants into a proper spv constant of the requested type. 713Id makeCompositeConstant ( Id type, const std ::vector < Id >& comps, bool specConst = false); 714 715// Methods for adding information outside the CFG. 716Instruction * addEntryPoint ( ExecutionModel , Function * , const char * name); 717void addExecutionMode ( Function * , ExecutionMode mode, int value1 = -1 , int value2 = -1 , int value3 = -1 ); 718void addExecutionMode ( Function * , ExecutionMode mode, const std ::vector < unsigned >& literals); 719void addExecutionModeId ( Function * , ExecutionMode mode, const std ::vector < Id >& operandIds); 720void addName ( Id , const char * name); 721void addMemberName ( Id , int member, const char * name); 722void addDecoration ( Id , Decoration , int num = -1 ); 723void addDecoration ( Id , Decoration , const char * ); 724void addDecoration ( Id , Decoration , const std ::vector < unsigned >& literals); 725void addDecoration ( Id , Decoration , const std ::vector < const char *>& strings); 726void addLinkageDecoration ( Id id, const char * name, spv ::LinkageType linkType); 727void addDecorationId ( Id id, Decoration , Id idDecoration); 728void addDecorationId ( Id id, Decoration , const std ::vector < Id >& operandIds); 729void addMemberDecoration ( Id , unsigned int member, Decoration , int num = -1 ); 730void addMemberDecoration ( Id , unsigned int member, Decoration , const char * ); 731void addMemberDecoration ( Id , unsigned int member, Decoration , const std ::vector < unsigned >& literals); 732void addMemberDecoration ( Id , unsigned int member, Decoration , const std ::vector < const char *>& strings); 733void addMemberDecorationIdEXT ( Id , unsigned int member, Decoration , const std ::vector < unsigned >& operands); 734 735// At the end of what block do the next create*() instructions go? 736// Also reset current last DebugScope and current source line to unknown 737void setBuildPoint ( Block * bp) { 738buildPoint = bp; 739descHeapShiftedBaseCache. clear (); 740dirtyLineTracker = true; 741dirtyScopeTracker = true; 742} 743Block * getBuildPoint () const { return buildPoint; } 744 745// Append an instruction to the end of the current build point. 746// Optionally, additional debug info instructions may also be prepended. 747void addInstruction ( std ::unique_ptr < Instruction > inst); 748 749// Append an instruction to the end of the current build point without prepending any debug instructions. 750// This is useful for insertion of some debug info instructions themselves or some control flow instructions 751// that are attached to its predecessor instruction. 752void addInstructionNoDebugInfo ( std ::unique_ptr < Instruction > inst); 753 754// Make the entry-point function. The returned pointer is only valid 755// for the lifetime of this builder. 756Function * makeEntryPoint ( const char * ); 757 758// Make a shader-style function, and create its entry block if entry is non-zero. 759// Return the function, pass back the entry. 760// The returned pointer is only valid for the lifetime of this builder. 761Function * makeFunctionEntry ( Decoration precision, Id returnType, const char * name, LinkageType linkType, 762const std ::vector < Id >& paramTypes, 763const std ::vector < std::vector < Decoration>> & precisions, Block ** entry = nullptr); 764 765// Create a return. An 'implicit' return is one not appearing in the source 766// code. In the case of an implicit return, no post-return block is inserted. 767void makeReturn ( bool implicit, Id retVal = 0 ); 768 769// Initialize state and generate instructions for new lexical scope 770void enterLexicalBlock ( uint32_t line, uint32_t column); 771 772// Set state and generate instructions to exit current lexical scope 773void leaveLexicalBlock (); 774 775// Prepare builder for generation of instructions for a function. 776void enterFunction ( Function const * function); 777 778// Generate all the code needed to finish up a function. 779void leaveFunction (); 780 781// Create block terminator instruction for certain statements like 782// discard, terminate-invocation, terminateRayEXT, or ignoreIntersectionEXT 783void makeStatementTerminator ( spv ::Op opcode, const char * name); 784 785// Create block terminator instruction for statements that have input operands 786// such as OpEmitMeshTasksEXT 787void makeStatementTerminator ( spv ::Op opcode, const std ::vector < Id >& operands, const char * name); 788 789// Create a global/local constant. Because OpConstant is automatically emitted by getting the constant 790// ids, this function only handles debug info. 791void createConstVariable ( Id type, const char * name, Id constant, bool isGlobal); 792 793// Create a global or function local or IO variable. 794Id createVariable ( Decoration precision, StorageClass storageClass, Id type, const char * name = nullptr , 795Id initializer = NoResult, bool const compilerGenerated = true); 796 797// Create an untyped global or function local or IO variable. 798Id createUntypedVariable ( Decoration precision, StorageClass storageClass, const char * name = nullptr , 799Id dataType = NoResult, Id initializer = NoResult); 800 801// Create an intermediate with an undefined value. 802Id createUndefined ( Id type); 803 804// Create load/store instruction with a remapped descriptor heap base. 805Instruction * createDescHeapLoadStoreBaseRemap ( Id base, Op op); 806 807// Store into an Id and return the l-value 808void createStore ( Id rValue, Id lValue, spv ::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone, 809spv::Scope scope = spv::Scope::Max, unsigned int alignment = 0 ); 810 811// Load from an Id and return it 812Id createLoad ( Id lValue, spv ::Decoration precision, 813spv ::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone, 814spv::Scope scope = spv::Scope::Max, unsigned int alignment = 0 ); 815 816// Create an OpAccessChain instruction 817Id createAccessChain ( StorageClass , Id base, const std ::vector < Id >& offsets); 818 819// Create an OpUntypedAccessChainKHR instruction 820Id createUntypedAccessChain ( Id resultType, Id base, const std ::vector < Id >& offsets, Id resultId = NoResult); 821 822// Create an OpArrayLength instruction 823Id createArrayLength ( Id base, unsigned int member, unsigned int bits); 824 825// Create an OpCooperativeMatrixLengthKHR instruction 826Id createCooperativeMatrixLengthKHR ( Id type); 827// Create an OpCooperativeMatrixLengthNV instruction 828Id createCooperativeMatrixLengthNV ( Id type); 829 830// Create an OpCompositeExtract instruction 831Id createCompositeExtract ( Id composite, Id typeId, unsigned index); 832Id createCompositeExtract ( Id composite, Id typeId, const std ::vector < unsigned >& indexes); 833Id createCompositeInsert ( Id object, Id composite, Id typeId, unsigned index); 834Id createCompositeInsert ( Id object, Id composite, Id typeId, const std ::vector < unsigned >& indexes); 835 836Id createVectorExtractDynamic ( Id vector, Id typeId, Id componentIndex); 837Id createVectorInsertDynamic ( Id vector, Id typeId, Id component, Id componentIndex); 838 839void createNoResultOp ( Op ); 840void createNoResultOp ( Op , Id operand); 841void createNoResultOp ( Op , const std ::vector < Id >& operands); 842void createNoResultOp ( Op , const std ::vector < IdImmediate >& operands); 843void createControlBarrier ( Scope execution, Scope memory, MemorySemanticsMask ); 844void createSplitControlBarrier ( Op op, Scope execution, Scope memory, MemorySemanticsMask memorySem); 845void createMemoryBarrier ( Scope executionScope, MemorySemanticsMask memorySemantics); 846Id createUnaryOp ( Op , Id typeId, Id operand); 847Id createBinOp ( Op , Id typeId, Id operand1, Id operand2); 848Id createTriOp ( Op , Id typeId, Id operand1, Id operand2, Id operand3); 849Id createOp ( Op , Id typeId, const std ::vector < Id >& operands); 850Id createOp ( Op , Id typeId, const std ::vector < IdImmediate >& operands); 851Id createConstData ( Op opCode, Id typeId, const std ::vector < const char *> operands); 852Id createFunctionCall ( spv ::Function * , const std ::vector < spv::Id >& ); 853Id createSpecConstantOp ( Op , Id typeId, const std ::vector < spv::Id >& operands, const std ::vector < unsigned >& literals); 854Id createSpecConstantAlignTo ( Id value, Id alignment); 855Id createSpecConstantSelectMax ( Id lhs, Id rhs); 856 857// Take an rvalue (source) and a set of channels to extract from it to 858// make a new rvalue, which is returned. 859Id createRvalueSwizzle ( Decoration precision, Id typeId, Id source, const std ::vector < unsigned >& channels); 860 861// Take a copy of an lvalue (target) and a source of components, and set the 862// source components into the lvalue where the 'channels' say to put them. 863// An updated version of the target is returned. 864// (No true lvalue or stores are used.) 865Id createLvalueSwizzle ( Id typeId, Id target, Id source, const std ::vector < unsigned >& channels); 866 867// If both the id and precision are valid, the id 868// gets tagged with the requested precision. 869// The passed in id is always the returned id, to simplify use patterns. 870Id setPrecision ( Id id, Decoration precision) 871{ 872if (precision != NoPrecision && id != NoResult) 873addDecoration (id, precision); 874 875return id; 876} 877 878// Can smear a scalar to a vector for the following forms: 879// - promoteScalar(scalar, vector) // smear scalar to width of vector 880// - promoteScalar(vector, scalar) // smear scalar to width of vector 881// - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to 882// - promoteScalar(scalar, scalar) // do nothing 883// Other forms are not allowed. 884// 885// Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. 886// The type of the created vector is a vector of components of the same type as the scalar. 887// 888// Note: One of the arguments will change, with the result coming back that way rather than 889// through the return value. 890void promoteScalar ( Decoration precision, Id & left, Id & right); 891 892// Make a value by smearing the scalar to fill the type. 893// vectorType should be the correct type for making a vector of scalarVal. 894// (No conversions are done.) 895Id smearScalar ( Decoration precision, Id scalarVal, Id vectorType); 896 897// Create a call to a built-in function. 898Id createBuiltinCall ( Id resultType, Id builtins, int entryPoint, const std ::vector < Id >& args); 899Id createBuiltinCall ( Id resultType, Id builtins, int entryPoint, const std ::vector < IdImmediate >& args); 900 901// List of parameters used to create a texture operation 902struct TextureParameters { 903Id sampler ; 904Id coords ; 905Id bias ; 906Id lod ; 907Id Dref ; 908Id offset ; 909Id offsets ; 910Id gradX ; 911Id gradY ; 912Id sample ; 913Id component ; 914Id texelOut ; 915Id lodClamp ; 916Id granularity ; 917Id coarse ; 918Id gatherMode ; 919bool nonprivate ; 920bool volatil ; 921bool nontemporal ; 922}; 923 924// Select the correct texture operation based on all inputs, and emit the correct instruction 925Id createTextureCall ( Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, 926bool noImplicit, const TextureParameters & , ImageOperandsMask ); 927 928// Emit the OpTextureQuery* instruction that was passed in. 929// Figure out the right return value and type, and return it. 930Id createTextureQueryCall ( Op , const TextureParameters & , bool isUnsignedResult); 931 932Id createSamplePositionCall ( Decoration precision, Id , Id ); 933 934Id createBitFieldExtractCall ( Decoration precision, Id , Id , Id , bool isSigned); 935Id createBitFieldInsertCall ( Decoration precision, Id , Id , Id , Id ); 936 937// Reduction comparison for composites: For equal and not-equal resulting in a scalar. 938Id createCompositeCompare ( Decoration precision, Id , Id , bool /* true if for equal, false if for not-equal */ ); 939 940// OpCompositeConstruct 941Id createCompositeConstruct ( Id typeId, const std ::vector < Id >& constituents); 942 943// vector or scalar constructor 944Id createConstructor ( Decoration precision, const std ::vector < Id >& sources, Id resultTypeId); 945 946// matrix constructor 947Id createMatrixConstructor ( Decoration precision, const std ::vector < Id >& sources, Id constructee); 948 949// coopmat conversion 950Id createCooperativeMatrixConversion ( Id typeId, Id source); 951Id createCooperativeMatrixReduce ( Op opcode, Id typeId, Id source, unsigned int mask, Id func); 952Id createCooperativeMatrixPerElementOp ( Id typeId, const std ::vector < Id >& operands); 953 954// Helper to use for building nested control flow with if-then-else. 955class If { 956public : 957If( Id condition, SelectionControlMask ctrl, Builder & builder); 958~ If () {} 959 960void makeBeginElse (); 961void makeEndIf (); 962 963private : 964If( const If & ); 965If & operator = (If & ); 966 967Builder & builder; 968Id condition; 969SelectionControlMask control; 970Function * function; 971Block * headerBlock; 972Block * thenBlock; 973Block * elseBlock; 974Block * mergeBlock; 975}; 976 977// Make a switch statement. A switch has 'numSegments' of pieces of code, not containing 978// any case/default labels, all separated by one or more case/default labels. Each possible 979// case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this 980// number space. How to compute the value is given by 'condition', as in switch(condition). 981// 982// The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches. 983// 984// Use a defaultSegment < 0 if there is no default segment (to branch to post switch). 985// 986// Returns the right set of basic blocks to start each code segment with, so that the caller's 987// recursion stack can hold the memory for it. 988// 989void makeSwitch ( Id condition, SelectionControlMask control, int numSegments, const std ::vector < int >& caseValues, 990const std ::vector < int >& valueToSegment, int defaultSegment, std ::vector < Block *>& segmentBB); 991 992// Add a branch to the innermost switch's merge block. 993void addSwitchBreak ( bool implicit); 994 995// Move to the next code segment, passing in the return argument in makeSwitch() 996void nextSwitchSegment ( std ::vector < Block *>& segmentBB, int segment); 997 998// Finish off the innermost switch. 999void endSwitch ( std ::vector < Block *>& segmentBB); 1000 1001struct LoopBlocks { 1002LoopBlocks(Block & head, Block & body, Block & merge, Block & continue_target) : 1003head (head), body (body), merge (merge), continue_target (continue_target) { } 1004Block & head, & body, & merge, & continue_target; 1005private : 1006LoopBlocks (); 1007LoopBlocks & operator = ( const LoopBlocks & ) = delete; 1008}; 1009 1010// Start a new loop and prepare the builder to generate code for it. Until 1011// closeLoop() is called for this loop, createLoopContinue() and 1012// createLoopExit() will target its corresponding blocks. 1013LoopBlocks & makeNewLoop (); 1014 1015// Create a new block in the function containing the build point. Memory is 1016// owned by the function object. 1017Block & makeNewBlock (); 1018 1019// Add a branch to the continue_target of the current (innermost) loop. 1020void createLoopContinue (); 1021 1022// Add an exit (e.g. "break") from the innermost loop that we're currently 1023// in. 1024void createLoopExit (); 1025 1026// Close the innermost loop that you're in 1027void closeLoop (); 1028 1029// 1030// Access chain design for an R-Value vs. L-Value: 1031// 1032// There is a single access chain the builder is building at 1033// any particular time. Such a chain can be used to either to a load or 1034// a store, when desired. 1035// 1036// Expressions can be r-values, l-values, or both, or only r-values: 1037// a[b.c].d = .... // l-value 1038// ... = a[b.c].d; // r-value, that also looks like an l-value 1039// ++a[b.c].d; // r-value and l-value 1040// (x + y)[2]; // r-value only, can't possibly be l-value 1041// 1042// Computing an r-value means generating code. Hence, 1043// r-values should only be computed when they are needed, not speculatively. 1044// 1045// Computing an l-value means saving away information for later use in the compiler, 1046// no code is generated until the l-value is later dereferenced. It is okay 1047// to speculatively generate an l-value, just not okay to speculatively dereference it. 1048// 1049// The base of the access chain (the left-most variable or expression 1050// from which everything is based) can be set either as an l-value 1051// or as an r-value. Most efficient would be to set an l-value if one 1052// is available. If an expression was evaluated, the resulting r-value 1053// can be set as the chain base. 1054// 1055// The users of this single access chain can save and restore if they 1056// want to nest or manage multiple chains. 1057// 1058 1059struct AccessChain { 1060Id base ; // for l-values, pointer to the base object, for r-values, the base object 1061std :: vector < Id > indexChain ; 1062Id instr ; // cache the instruction that generates this access chain 1063std :: vector < unsigned > swizzle ; // each std::vector element selects the next GLSL component number 1064Id component ; // a dynamic component index, can coexist with a swizzle, 1065// done after the swizzle, NoResult if not present 1066Id preSwizzleBaseType ; // dereferenced type, before swizzle or component is applied; 1067// NoType unless a swizzle or component is present 1068bool isRValue ; // true if 'base' is an r-value, otherwise, base is an l-value 1069unsigned int alignment ; // bitwise OR of alignment values passed in. Accumulates worst alignment. 1070// Only tracks base and (optional) component selection alignment. 1071 1072struct DescHeapInfo { 1073Id descHeapBaseTy ; // for descriptor heap, record its base data type. 1074Id descHeapBaseOffset ; // byte offset applied to the heap base before descriptor lookup. 1075std :: vector < Id > descHeapIndexChain ; 1076Id descTy ; // for target resource type 1077StorageClass descStorageClass ; // for descriptor heap, record its basic storage class. 1078bool descReadonly ; // for decorating OpBufferPointerEXT results. 1079bool descWriteonly ; // for decorating OpBufferPointerEXT results. 1080std :: vector < Instruction *> descHeapInstId ; 1081// for descriptor heap, record its data type for loading/store results. 1082}; 1083DescHeapInfo descHeapInfo ; 1084 1085// Accumulate whether anything in the chain of structures has coherent decorations. 1086struct CoherentFlags { 1087CoherentFlags() { clear (); } 1088bool isVolatile () const { return volatil ; } 1089bool isNonUniform () const { return nonUniform; } 1090bool anyCoherent () const { 1091return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent || 1092subgroupcoherent || shadercallcoherent; 1093} 1094 1095unsigned coherent : 1 ; 1096unsigned devicecoherent : 1 ; 1097unsigned queuefamilycoherent : 1 ; 1098unsigned workgroupcoherent : 1 ; 1099unsigned subgroupcoherent : 1 ; 1100unsigned shadercallcoherent : 1 ; 1101unsigned nonprivate : 1 ; 1102unsigned volatil : 1 ; 1103unsigned nontemporal : 1 ; 1104unsigned isImage : 1 ; 1105unsigned nonUniform : 1 ; 1106 1107void clear () { 1108coherent = 0 ; 1109devicecoherent = 0 ; 1110queuefamilycoherent = 0 ; 1111workgroupcoherent = 0 ; 1112subgroupcoherent = 0 ; 1113shadercallcoherent = 0 ; 1114nonprivate = 0 ; 1115volatil = 0 ; 1116nontemporal = 0 ; 1117isImage = 0 ; 1118nonUniform = 0 ; 1119} 1120 1121CoherentFlags operator |=( const CoherentFlags & other) { 1122coherent |= other.coherent; 1123devicecoherent |= other. devicecoherent ; 1124queuefamilycoherent |= other. queuefamilycoherent ; 1125workgroupcoherent |= other. workgroupcoherent ; 1126subgroupcoherent |= other. subgroupcoherent ; 1127shadercallcoherent |= other. shadercallcoherent ; 1128nonprivate |= other. nonprivate ; 1129volatil |= other. volatil ; 1130nontemporal |= other. nontemporal ; 1131isImage |= other. isImage ; 1132nonUniform |= other. nonUniform ; 1133return * this; 1134} 1135}; 1136CoherentFlags coherentFlags; 1137}; 1138 1139// 1140// the SPIR-V builder maintains a single active chain that 1141// the following methods operate on 1142// 1143 1144// for external save and restore 1145AccessChain getAccessChain () { return accessChain; } 1146void setAccessChain ( AccessChain newChain) { accessChain = newChain; } 1147 1148// for EXT_descriptor_heap and EXT_structured_descriptor_heap 1149Id getAccessChainDescHeapBaseType () const { return accessChain. descHeapInfo . descHeapBaseTy ; } 1150void setAccessChainDescHeapBaseType ( Id baseType) { accessChain. descHeapInfo . descHeapBaseTy = baseType; } 1151void setAccessChainDescHeapBaseOffset ( Id baseOffset) { accessChain. descHeapInfo . descHeapBaseOffset = baseOffset; } 1152const std ::vector < Id >& getAccessChainDescHeapIndexChain () const { return accessChain.descHeapInfo.descHeapIndexChain; } 1153void accessChainPushDescHeapIndex ( Id index) { accessChain. descHeapInfo . descHeapIndexChain . push_back (index); } 1154bool hasAccessChainIndex () const { return !accessChain. indexChain . empty (); } 1155void moveAccessChainIndexToDescHeapIndexChain () 1156{ 1157assert (accessChain. indexChain . size () == 1 ); 1158accessChainPushDescHeapIndex (accessChain. indexChain . back ()); 1159accessChain. indexChain . pop_back (); 1160} 1161void setAccessChainDescHeapDescriptorType ( Id descTy, StorageClass storageClass, bool readonly, bool writeonly) 1162{ 1163accessChain. descHeapInfo . descTy = descTy; 1164accessChain. descHeapInfo . descStorageClass = storageClass; 1165accessChain. descHeapInfo . descReadonly = readonly; 1166accessChain. descHeapInfo . descWriteonly = writeonly; 1167} 1168 1169// clear accessChain 1170void clearAccessChain (); 1171 1172Id createDescHeapAccessChain (); 1173Id createConstantSizeOfEXT ( Id typeId); 1174 1175// set new base as an l-value base 1176void setAccessChainLValue ( Id lValue) 1177{ 1178assert ( isPointer (lValue) || isUntypedPointer (lValue)); 1179accessChain. base = lValue; 1180} 1181 1182// set new base value as an r-value 1183void setAccessChainRValue ( Id rValue) 1184{ 1185accessChain. isRValue = true; 1186accessChain. base = rValue; 1187} 1188 1189// push offset onto the end of the chain 1190void accessChainPush ( Id offset, AccessChain ::CoherentFlags coherentFlags, unsigned int alignment) 1191{ 1192accessChain. indexChain . push_back (offset); 1193accessChain. coherentFlags |= coherentFlags; 1194accessChain. alignment |= alignment; 1195} 1196 1197// push new swizzle onto the end of any existing swizzle, merging into a single swizzle 1198void accessChainPushSwizzle ( std ::vector < unsigned >& swizzle, Id preSwizzleBaseType, 1199AccessChain ::CoherentFlags coherentFlags, unsigned int alignment); 1200 1201// push a dynamic component selection onto the access chain, only applicable with a 1202// non-trivial swizzle or no swizzle 1203void accessChainPushComponent ( Id component, Id preSwizzleBaseType, AccessChain ::CoherentFlags coherentFlags, 1204unsigned int alignment) 1205{ 1206if (accessChain. swizzle . size () != 1 ) { 1207accessChain. component = component; 1208if (accessChain. preSwizzleBaseType == NoType) 1209accessChain. preSwizzleBaseType = preSwizzleBaseType; 1210} 1211accessChain. coherentFlags |= coherentFlags; 1212accessChain. alignment |= alignment; 1213} 1214 1215// use accessChain and swizzle to store value 1216void accessChainStore ( Id rvalue, Decoration nonUniform, 1217spv ::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone, 1218spv::Scope scope = spv::Scope::Max, unsigned int alignment = 0 ); 1219 1220// use accessChain and swizzle to load an r-value 1221Id accessChainLoad ( Decoration precision, Decoration l_nonUniform, Decoration r_nonUniform, Id ResultType, 1222spv ::MemoryAccessMask memoryAccess = spv::MemoryAccessMask::MaskNone, spv::Scope scope = spv::Scope::Max, 1223unsigned int alignment = 0 ); 1224 1225// Return whether or not the access chain can be represented in SPIR-V 1226// as an l-value. 1227// E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be. 1228bool isSpvLvalue () const { return accessChain. swizzle . size () <= 1 ; } 1229 1230// get the direct pointer for an l-value 1231Id accessChainGetLValue (); 1232 1233// Get the inferred SPIR-V type of the result of the current access chain, 1234// based on the type of the base and the chain of dereferences. 1235Id accessChainGetInferredType (); 1236 1237// Add capabilities, extensions, remove unneeded decorations, etc., 1238// based on the resulting SPIR-V. 1239void postProcess ( bool compileOnly); 1240 1241// Prune unreachable blocks in the CFG and remove unneeded decorations. 1242void postProcessCFG (); 1243 1244// Add capabilities, extensions based on instructions in the module. 1245void postProcessFeatures (); 1246// Hook to visit each instruction in a block in a function 1247void postProcess ( Instruction & ); 1248// Hook to visit each non-32-bit sized float/int operation in a block. 1249void postProcessType ( const Instruction & , spv ::Id typeId); 1250// move OpSampledImage instructions to be next to their users. 1251void postProcessSamplers (); 1252 1253void dump ( std ::vector < unsigned int >& ) const; 1254 1255// Add a branch to the target block. 1256// If set implicit, the branch instruction shouldn't have debug source location. 1257void createBranch ( bool implicit, Block * block); 1258void createConditionalBranch ( Id condition, Block * thenBlock, Block * elseBlock); 1259void createLoopMerge ( Block * mergeBlock, Block * continueBlock, LoopControlMask control, 1260const std ::vector < unsigned int >& operands); 1261 1262// Sets to generate opcode for specialization constants. 1263void setToSpecConstCodeGenMode () { generatingOpCodeForSpecConst = true; } 1264// Sets to generate opcode for non-specialization constants (normal mode). 1265void setToNormalCodeGenMode () { generatingOpCodeForSpecConst = false; } 1266// Check if the builder is generating code for spec constants. 1267bool isInSpecConstCodeGenMode () { return generatingOpCodeForSpecConst; } 1268 1269void setUseReplicatedComposites ( bool use) { useReplicatedComposites = use; } 1270 1271private : 1272// Helper to get size of a scalar (in bytes) 1273unsigned int postProcessGetLargestScalarSize ( const Instruction & type); 1274 1275protected : 1276Id findScalarConstant ( Op typeClass, Op opcode, Id typeId, unsigned value); 1277Id findScalarConstant ( Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); 1278Id findCompositeConstant ( Op typeClass, Op opcode, Id typeId, const std ::vector < Id >& comps, size_t numMembers); 1279Id findStructConstant ( Id typeId, const std ::vector < Id >& comps, size_t numMembers); 1280Id collapseAccessChain (); 1281Id getOrCreateDescHeapByteArrayType (); 1282void remapDynamicSwizzle (); 1283void transferAccessChainSwizzle ( bool dynamic); 1284void simplifyAccessChainSwizzle (); 1285void createAndSetNoPredecessorBlock ( const char * ); 1286void createSelectionMerge ( Block * mergeBlock, SelectionControlMask control); 1287void dumpSourceInstructions ( std ::vector < unsigned int >& ) const; 1288void dumpSourceInstructions ( const spv ::Id fileId, const std ::string & text, std ::vector < unsigned int >& ) const; 1289template < class Range > void dumpInstructions (std::vector < unsigned int >& out, const Range & instructions) const; 1290void dumpModuleProcesses ( std ::vector < unsigned int >& ) const; 1291spv :: MemoryAccessMask sanitizeMemoryAccessForStorageClass( spv :: MemoryAccessMask memoryAccess, StorageClass sc) 1292const ; 1293struct DecorationInstructionLessThan { 1294bool operator ()( const std ::unique_ptr < Instruction >& lhs, const std::unique_ptr < Instruction >& rhs) const ; 1295}; 1296template < typename SpvUtilsType > 1297Id makeFloatConstantHelper (float f, Id typeId, bool specConstant); 1298 1299unsigned int spvVersion; // the version of SPIR-V to emit in the header 1300SourceLanguage sourceLang; 1301int sourceVersion; 1302spv :: Id nonSemanticShaderCompilationUnitId { 0 }; 1303spv:: Id nonSemanticShaderDebugInfo { 0 }; 1304// Pointer to the OpExtInstImport instruction for NonSemantic.Shader.DebugInfo. 1305// Kept so requireNonSemanticShaderDebugInfoVersion() can patch the name in place. 1306Instruction * nonSemanticShaderDebugInfoImportInst { nullptr }; 1307// Spec version encoded in the NonSemantic.Shader.DebugInfo import name. 1308// Defaults to 100. Promoted to N the first time a version-N opcode is emitted. 1309unsigned int nonSemanticShaderDebugInfoVersion{ 100 }; 1310spv :: Id debugInfoNone { 0 }; 1311spv :: Id debugExpression { 0 }; // Debug expression with zero operations. 1312std :: string sourceText; 1313 1314// True if an new OpLine/OpDebugLine may need to be inserted. Either: 1315// 1. The current debug location changed 1316// 2. The current build point changed 1317bool dirtyLineTracker; 1318int currentLine = 0 ; 1319// OpString id of the current file name. Always 0 if debug info is off. 1320spv :: Id currentFileId = 0 ; 1321// OpString id of the main file name. Always 0 if debug info is off. 1322spv :: Id mainFileId = 0 ; 1323 1324// True if an new OpDebugScope may need to be inserted. Either: 1325// 1. A new lexical block is pushed 1326// 2. The current build point changed 1327bool dirtyScopeTracker; 1328std ::stack < spv::Id > currentDebugScopeId; 1329 1330// This flag toggles tracking of debug info while building the SPIR-V. 1331bool trackDebugInfo = false; 1332// This flag toggles emission of SPIR-V debug instructions, like OpLine and OpSource. 1333bool emitSpirvDebugInfo = false; 1334// This flag toggles emission of Non-Semantic Debug extension debug instructions. 1335bool emitNonSemanticShaderDebugInfo = false; 1336bool restoreNonSemanticShaderDebugInfo = false; 1337bool emitNonSemanticShaderDebugSource = false; 1338 1339std ::set < std::string > extensions; 1340std ::vector < const char *> sourceExtensions; 1341std ::vector < const char *> moduleProcesses; 1342AddressingModel addressModel; 1343MemoryModel memoryModel; 1344std ::set < spv::Capability > capabilities; 1345int builderNumber; 1346Module module; 1347Block * buildPoint; 1348Id uniqueId; 1349Function * entryPointFunction; 1350// This tracks the current function being built, or nullptr if not in a function. 1351Function const * currentFunction { nullptr }; 1352bool generatingOpCodeForSpecConst; 1353bool useReplicatedComposites { false }; 1354AccessChain accessChain; 1355 1356// special blocks of instructions for output 1357std::vector < std::unique_ptr < Instruction > > strings; 1358std::vector < std::unique_ptr < Instruction > > imports; 1359std::vector < std::unique_ptr < Instruction > > entryPoints; 1360std::vector < std::unique_ptr < Instruction > > executionModes; 1361std::vector < std::unique_ptr < Instruction > > names; 1362std::set < std::unique_ptr < Instruction > , DecorationInstructionLessThan > decorations; 1363std::vector < std::unique_ptr < Instruction > > constantsTypesGlobals; 1364std::vector < std::unique_ptr < Instruction > > externals; 1365std::vector < std::unique_ptr < Function > > functions; 1366 1367// not output, internally used for quick & dirty canonical (unique) creation 1368 1369// Key for scalar constants (handles both 32-bit and 64-bit) 1370struct ScalarConstantKey { 1371unsigned int typeClass ; // OpTypeInt, OpTypeFloat, OpTypeBool 1372unsigned int opcode ; // OpConstant, OpSpecConstant, OpConstantTrue, etc. 1373Id typeId ; // The specific type 1374unsigned value1 ; // First operand (or only operand) 1375unsigned value2 ; // Second operand (0 for single-operand constants) 1376 1377bool operator == ( const ScalarConstantKey & other ) const { 1378return typeClass == other . typeClass && 1379opcode == other . opcode && 1380typeId == other . typeId && 1381value1 == other . value1 && 1382value2 == other . value2 ; 1383} 1384}; 1385 1386struct ScalarConstantKeyHash { 1387// 64/32 bit mix function from MurmurHash3 1388inline std :: size_t hash_mix ( std :: size_t h) const { 1389if constexpr (sizeof( std :: size_t ) == 8 ) { 1390h ^= h >> 33 ; 1391h *= UINT64_C ( 0xff51afd7ed558ccd ); 1392h ^= h >> 33 ; 1393h *= UINT64_C ( 0xc4ceb9fe1a85ec53 ); 1394h ^= h >> 33 ; 1395return h ; 1396} else { 1397h ^= h >> 16 ; 1398h *= UINT32_C ( 0x85ebca6b ); 1399h ^= h >> 13 ; 1400h *= UINT32_C ( 0xc2b2ae35 ); 1401h ^= h >> 16 ; 1402return h; 1403} 1404} 1405 1406// Hash combine from boost 1407inline std :: size_t hash_combine (std:: size_t seed, std :: size_t v) const { 1408return hash_mix (seed + 0x9e3779b9 + v); 1409} 1410 1411std :: size_t operator ()( const ScalarConstantKey & k) const { 1412size_t hash1 = hash_combine (std::hash < unsigned > {}(k. typeClass ), std::hash < unsigned > {}(k. opcode )); 1413size_t hash2 = hash_combine (std::hash < Id > {}(k. value1 ), std::hash < unsigned > {}(k. value2 )); 1414size_t hash3 = hash_combine (hash1, hash2); 1415return hash_combine (hash3, std::hash < unsigned > {}(k. typeId )); 1416} 1417}; 1418 1419typedef std :: unordered_set < 1420CompositeConstantKey , 1421CompositeConstantKey ::Hash, 1422CompositeConstantKey ::Equal > CompositeConstantCache ; 1423 1424typedef std :: unordered_set < 1425StructConstantKey , 1426StructConstantKey ::Hash, 1427StructConstantKey ::Equal > StructConstantCache ; 1428 1429// map type opcodes to constant inst. 1430CompositeConstantCache groupedCompositeConstants; 1431// map struct-id to constant instructions 1432StructConstantCache groupedStructConstants; 1433// map type opcodes to type instructions 1434std ::unordered_map < unsigned int, std::vector < Instruction * >> groupedTypes; 1435// map type opcodes to debug type instructions 1436std ::unordered_map < unsigned int, std::vector < Instruction * >> groupedDebugTypes; 1437// list of OpConstantNull instructions 1438std ::vector < Instruction *> nullConstants; 1439// map scalar constants to result IDs 1440std ::unordered_map < ScalarConstantKey, Id, ScalarConstantKeyHash > groupedScalarConstantResultIDs; 1441// map type ids to OpConstantSizeOfEXT result IDs 1442std ::unordered_map < Id, Id > constantSizeOfEXTIds; 1443 1444// Descriptor heap shifted base cache. 1445std ::map < std::pair < Id, Id > , Id > descHeapShiftedBaseCache; 1446 1447// Descriptor heap byte array type. 1448Id descHeapByteArrayType; 1449 1450// Track which types have explicit layouts, to avoid reusing in storage classes without layout. 1451// Currently only tracks array types. 1452std ::unordered_set < unsigned int > explicitlyLaidOut; 1453 1454// stack of switches 1455std ::stack < Block *> switchMerges; 1456 1457// Our loop stack. 1458std ::stack < LoopBlocks > loops; 1459 1460// map from strings to their string ids 1461std ::unordered_map < std::string, spv::Id > stringIds; 1462 1463// map from include file name ids to their contents 1464std ::map < spv::Id, const std::string *> includeFiles; 1465 1466// maps from OpTypeXXX id to DebugTypeXXX id 1467std ::unordered_map < spv::Id, spv::Id > debugTypeIdLookup; 1468 1469// maps from OpFunction id to DebugFunction id 1470std ::unordered_map < spv::Id, spv::Id > debugFuncIdLookup; 1471 1472// map from file name string id to DebugSource id 1473std ::unordered_map < spv::Id, spv::Id > debugSourceId; 1474 1475// The stream for outputting warnings and errors. 1476SpvBuildLogger * logger; 1477}; // end Builder class 1478 1479} // end spv namespace 1480 1481#endif // SpvBuilder_H