yum-mirror/glslang
git clone https://git.yummers.dev/yum-mirror/glslang
3c14556a
main
1// 2// Copyright (C) 2018 Google, Inc. 3// 4// All rights reserved. 5// 6// Redistribution and use in source and binary forms, with or without 7// modification, are permitted provided that the following conditions 8// are met: 9// 10// Redistributions of source code must retain the above copyright 11// notice, this list of conditions and the following disclaimer. 12// 13// Redistributions in binary form must reproduce the above 14// copyright notice, this list of conditions and the following 15// disclaimer in the documentation and/or other materials provided 16// with the distribution. 17// 18// Neither the name of 3Dlabs Inc. Ltd. nor the names of its 19// contributors may be used to endorse or promote products derived 20// from this software without specific prior written permission. 21// 22// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33// POSSIBILITY OF SUCH DAMAGE. 34 35// 36// Post-processing for SPIR-V IR, in internal form, not standard binary form. 37// 38 39#include <cassert> 40#include <cstdlib> 41 42#include <unordered_map> 43#include <unordered_set> 44#include <algorithm> 45 46#include "SPIRV/spvIR.h" 47#include "SpvBuilder.h" 48#include "spirv.hpp11" 49#include "spvUtil.h" 50 51namespace spv { 52#include "GLSL.std.450.h" 53#include "GLSL.ext.KHR.h" 54#include "GLSL.ext.EXT.h" 55#include "GLSL.ext.AMD.h" 56#include "GLSL.ext.NV.h" 57#include "GLSL.ext.ARM.h" 58#include "GLSL.ext.QCOM.h" 59} 60 61namespace spv { 62 63// Hook to visit each operand type and result type of an instruction. 64// Will be called multiple times for one instruction, once for each typed 65// operand and the result. 66void Builder ::postProcessType (const Instruction & inst ,Id typeId ) 67{ 68// Characterize the type being questioned 69Op basicTypeOp = getMostBasicTypeClass (typeId ); 70int width = 0 ; 71if (basicTypeOp == Op ::OpTypeFloat || basicTypeOp == Op ::OpTypeInt ) 72width = getScalarTypeWidth (typeId ); 73 74// Do opcode-specific checks 75switch (inst .getOpCode ()) { 76case Op ::OpLoad : 77case Op ::OpStore : 78if (basicTypeOp == Op ::OpTypeStruct ) { 79if (containsType (typeId ,Op ::OpTypeInt ,8 )) 80addCapability (Capability ::Int8 ); 81if (containsType (typeId ,Op ::OpTypeInt ,16 )) 82addCapability (Capability ::Int16 ); 83if (containsType (typeId ,Op ::OpTypeFloat ,16 )) 84addCapability (Capability ::Float16 ); 85 }else { 86StorageClass storageClass = StorageClass ::Max ; 87if (module .getInstruction (inst .getIdOperand (0 ))-> getOpCode ()!= Op ::OpUntypedAccessChainKHR ) { 88storageClass = getStorageClass (inst .getIdOperand (0 )); 89 } 90if (width == 8 ) { 91switch (storageClass ) { 92case StorageClass ::PhysicalStorageBufferEXT : 93case StorageClass ::Uniform : 94case StorageClass ::StorageBuffer : 95case StorageClass ::PushConstant : 96break ; 97default : 98addCapability (Capability ::Int8 ); 99break ; 100 } 101 }else if (width == 16 ) { 102switch (storageClass ) { 103case StorageClass ::PhysicalStorageBufferEXT : 104case StorageClass ::Uniform : 105case StorageClass ::StorageBuffer : 106case StorageClass ::PushConstant : 107case StorageClass ::Input : 108case StorageClass ::Output : 109break ; 110default : 111if (basicTypeOp == Op ::OpTypeInt ) 112addCapability (Capability ::Int16 ); 113if (basicTypeOp == Op ::OpTypeFloat ) 114addCapability (Capability ::Float16 ); 115break ; 116 } 117 } 118 } 119break ; 120case Op ::OpCopyObject : 121break ; 122case Op ::OpFConvert : 123case Op ::OpSConvert : 124case Op ::OpUConvert : 125// Look for any 8/16-bit storage capabilities. If there are none, assume that 126// the convert instruction requires the Float16/Int8/16 capability. 127if (containsType (typeId ,Op ::OpTypeFloat ,16 )|| containsType (typeId ,Op ::OpTypeInt ,16 )) { 128bool foundStorage = false; 129for (auto it = capabilities .begin ();it != capabilities .end ();++ it ) { 130 spv::Capability cap = * it ; 131if (cap == spv::Capability ::StorageInputOutput16 || 132cap == spv::Capability ::StoragePushConstant16 || 133cap == spv::Capability ::StorageUniformBufferBlock16 || 134cap == spv::Capability ::StorageUniform16 ) { 135foundStorage = true; 136break ; 137 } 138 } 139if (!foundStorage ) { 140if (containsType (typeId ,Op ::OpTypeFloat ,16 )) 141addCapability (Capability ::Float16 ); 142if (containsType (typeId ,Op ::OpTypeInt ,16 )) 143addCapability (Capability ::Int16 ); 144 } 145 } 146if (containsType (typeId ,Op ::OpTypeInt ,8 )) { 147bool foundStorage = false; 148for (auto it = capabilities .begin ();it != capabilities .end ();++ it ) { 149 spv::Capability cap = * it ; 150if (cap == spv::Capability ::StoragePushConstant8 || 151cap == spv::Capability ::UniformAndStorageBuffer8BitAccess || 152cap == spv::Capability ::StorageBuffer8BitAccess ) { 153foundStorage = true; 154break ; 155 } 156 } 157if (!foundStorage ) { 158addCapability (Capability ::Int8 ); 159 } 160 } 161break ; 162case Op ::OpExtInst : 163switch (inst .getImmediateOperand (1 )) { 164case GLSLstd450Frexp : 165case GLSLstd450FrexpStruct : 166if (getSpvVersion ()< spv::Spv_1_3 && containsType (typeId ,Op ::OpTypeInt ,16 )) 167addExtension (spv::E_SPV_AMD_gpu_shader_int16 ); 168break ; 169case GLSLstd450InterpolateAtCentroid : 170case GLSLstd450InterpolateAtSample : 171case GLSLstd450InterpolateAtOffset : 172if (getSpvVersion ()< spv::Spv_1_3 && containsType (typeId ,Op ::OpTypeFloat ,16 )) 173addExtension (spv::E_SPV_AMD_gpu_shader_half_float ); 174break ; 175default : 176break ; 177 } 178if (basicTypeOp == Op ::OpTypeInt ) { 179if (width == 16 ) 180addCapability (Capability ::Int16 ); 181else if (width == 8 ) 182addCapability (Capability ::Int8 ); 183 }else if (basicTypeOp == Op ::OpTypeFloat ) { 184if (width == 16 ) 185addCapability (Capability ::Float16 ); 186 } 187break ; 188case Op ::OpAccessChain : 189case Op ::OpPtrAccessChain : 190if (isPointerType (typeId )) 191break ; 192if (basicTypeOp == Op ::OpTypeInt ) { 193if (width == 16 ) 194addCapability (Capability ::Int16 ); 195else if (width == 8 ) 196addCapability (Capability ::Int8 ); 197 } 198break ; 199default : 200if (basicTypeOp == Op ::OpTypeInt ) { 201if (width == 16 ) 202addCapability (Capability ::Int16 ); 203else if (width == 8 ) 204addCapability (Capability ::Int8 ); 205else if (width == 64 ) 206addCapability (Capability ::Int64 ); 207 }else if (basicTypeOp == Op ::OpTypeFloat ) { 208if (width == 16 ) 209addCapability (Capability ::Float16 ); 210else if (width == 64 ) 211addCapability (Capability ::Float64 ); 212 } 213break ; 214 } 215} 216 217unsigned int Builder ::postProcessGetLargestScalarSize (const Instruction & type ) 218{ 219switch (type .getOpCode ()) { 220case Op ::OpTypeBool : 221return 1 ; 222case Op ::OpTypeInt : 223case Op ::OpTypeFloat : 224return type .getImmediateOperand (0 ) /8 ; 225case Op ::OpTypePointer : 226return 8 ; 227case Op ::OpTypeVector : 228case Op ::OpTypeMatrix : 229case Op ::OpTypeArray : 230case Op ::OpTypeRuntimeArray : { 231const Instruction * elem_type = module .getInstruction (type .getIdOperand (0 )); 232return postProcessGetLargestScalarSize (* elem_type ); 233 } 234case Op ::OpTypeStruct : { 235unsigned int largest = 0 ; 236for (int i = 0 ;i < type .getNumOperands ();++ i ) { 237const Instruction * elem_type = module .getInstruction (type .getIdOperand (i )); 238unsigned int elem_size = postProcessGetLargestScalarSize (* elem_type ); 239largest = std::max (largest ,elem_size ); 240 } 241return largest ; 242 } 243default : 244return 0 ; 245 } 246} 247 248// Called for each instruction that resides in a block. 249void Builder ::postProcess (Instruction & inst ) 250{ 251// Add capabilities based simply on the opcode. 252switch (inst .getOpCode ()) { 253case Op ::OpExtInst : 254switch (inst .getImmediateOperand (1 )) { 255case GLSLstd450InterpolateAtCentroid : 256case GLSLstd450InterpolateAtSample : 257case GLSLstd450InterpolateAtOffset : 258addCapability (Capability ::InterpolationFunction ); 259break ; 260default : 261break ; 262 } 263break ; 264case Op ::OpDPdxFine : 265case Op ::OpDPdyFine : 266case Op ::OpFwidthFine : 267case Op ::OpDPdxCoarse : 268case Op ::OpDPdyCoarse : 269case Op ::OpFwidthCoarse : 270addCapability (Capability ::DerivativeControl ); 271break ; 272 273case Op ::OpImageQueryLod : 274case Op ::OpImageQuerySize : 275case Op ::OpImageQuerySizeLod : 276case Op ::OpImageQuerySamples : 277case Op ::OpImageQueryLevels : 278addCapability (Capability ::ImageQuery ); 279break ; 280 281case Op ::OpGroupNonUniformPartitionNV : 282addExtension (E_SPV_NV_shader_subgroup_partitioned ); 283addCapability (Capability ::GroupNonUniformPartitionedNV ); 284break ; 285 286case Op ::OpLoad : 287case Op ::OpStore : 288 { 289// For any load/store to a PhysicalStorageBufferEXT, walk the accesschain 290// index list to compute the misalignment. The pre-existing alignment value 291// (set via Builder::AccessChain::alignment) only accounts for the base of 292// the reference type and any scalar component selection in the accesschain, 293// and this function computes the rest from the SPIR-V Offset decorations. 294Instruction * accessChain = module .getInstruction (inst .getIdOperand (0 )); 295if (accessChain -> getOpCode ()== Op ::OpAccessChain ) { 296const Instruction * base = module .getInstruction (accessChain -> getIdOperand (0 )); 297// Get the type of the base of the access chain. It must be a pointer type. 298Id typeId = base -> getTypeId (); 299Instruction * type = module .getInstruction (typeId ); 300assert (type -> getOpCode ()== Op ::OpTypePointer ); 301if (type -> getImmediateOperand (0 )!= StorageClass ::PhysicalStorageBuffer ) { 302break ; 303 } 304// Get the pointee type. 305typeId = type -> getIdOperand (1 ); 306type = module .getInstruction (typeId ); 307// Walk the index list for the access chain. For each index, find any 308// misalignment that can apply when accessing the member/element via 309// Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all 310// together. 311int alignment = 0 ; 312bool first_struct_elem = false; 313for (int i = 1 ;i < accessChain -> getNumOperands ();++ i ) { 314Instruction * idx = module .getInstruction (accessChain -> getIdOperand (i )); 315if (type -> getOpCode ()== Op ::OpTypeStruct ) { 316assert (idx -> getOpCode ()== Op ::OpConstant ); 317unsigned int c = idx -> getImmediateOperand (0 ); 318 319const auto function = [& ](const std::unique_ptr < Instruction >& decoration ) { 320if (decoration .get ()-> getOpCode ()== Op ::OpMemberDecorate && 321decoration .get ()-> getIdOperand (0 )== typeId && 322decoration .get ()-> getImmediateOperand (1 )== c && 323 (decoration .get ()-> getImmediateOperand (2 )== Decoration ::Offset || 324decoration .get ()-> getImmediateOperand (2 )== Decoration ::MatrixStride )) { 325unsigned int opernad_value = decoration .get ()-> getImmediateOperand (3 ); 326alignment |=opernad_value ; 327if (opernad_value == 0 && 328decoration .get ()-> getImmediateOperand (2 )== Decoration ::Offset ) { 329first_struct_elem = true; 330 } 331 } 332 }; 333 std::for_each (decorations .begin (),decorations .end (),function ); 334// get the next member type 335typeId = type -> getIdOperand (c ); 336type = module .getInstruction (typeId ); 337 }else if (type -> getOpCode ()== Op ::OpTypeArray || 338type -> getOpCode ()== Op ::OpTypeRuntimeArray ) { 339const auto function = [& ](const std::unique_ptr < Instruction >& decoration ) { 340if (decoration .get ()-> getOpCode ()== Op ::OpDecorate && 341decoration .get ()-> getIdOperand (0 )== typeId && 342decoration .get ()-> getImmediateOperand (1 )== Decoration ::ArrayStride ) { 343alignment |=decoration .get ()-> getImmediateOperand (2 ); 344 } 345 }; 346 std::for_each (decorations .begin (),decorations .end (),function ); 347// Get the element type 348typeId = type -> getIdOperand (0 ); 349type = module .getInstruction (typeId ); 350 }else { 351// Once we get to any non-aggregate type, we're done. 352break ; 353 } 354 } 355assert (inst .getNumOperands () >=3 ); 356const bool is_store = inst .getOpCode ()== Op ::OpStore ; 357auto const memoryAccess = (MemoryAccessMask )inst .getImmediateOperand (is_store ?2 :1 ); 358assert (anySet (memoryAccess ,MemoryAccessMask ::Aligned )); 359static_cast < void > (memoryAccess ); 360 361// Compute the index of the alignment operand. 362int alignmentIdx = 2 ; 363if (is_store ) 364alignmentIdx ++ ; 365// Merge new and old (mis)alignment 366alignment |=inst .getImmediateOperand (alignmentIdx ); 367 368Instruction * inst_type = nullptr ; 369if (is_store ) { 370// For a store, we are storing through a pointer, so what this ends up looking like when storing a BDA pointer is we have a "OpTypePointer PhysicalStorageBuffer" pointing at the actual "OpTypePointer PhysicalStorageBuffer" value storing 371// https://godbolt.org/z/58ff4P3sG 372inst_type = module .getInstruction (accessChain -> getTypeId ()); 373if (inst_type -> getOpCode ()== Op ::OpTypePointer && 374inst_type -> getImmediateOperand (0 )== StorageClass ::PhysicalStorageBuffer ) { 375inst_type = module .getInstruction (inst_type -> getIdOperand (1 )); 376 } 377 }else { 378inst_type = module .getInstruction (inst .getTypeId ()); 379 } 380 381if (inst_type -> getOpCode ()== Op ::OpTypePointer && 382inst_type -> getImmediateOperand (0 )== StorageClass ::PhysicalStorageBuffer ) { 383// This means we are loading a pointer which means need to ensure it is at least 8-byte aligned 384// See https://github.com/KhronosGroup/glslang/issues/4084 385// In case the alignment is currently 4, need to ensure it is 8 before grabbing the LSB 386alignment |=8 ; 387alignment &=8 ; 388 } 389 390// Pick the LSB 391alignment = alignment & ~(alignment & (alignment - 1 )); 392 393// The edge case we find is when copying a struct to another struct, we never find the alignment anywhere, 394// so in this case, fallback to doing a full size lookup on the type 395if (alignment == 0 && first_struct_elem ) { 396// Quick get the struct type back 397const Instruction * pointer_type = module .getInstruction (base -> getTypeId ()); 398const Instruction * struct_type = module .getInstruction (pointer_type -> getIdOperand (1 )); 399assert (struct_type -> getOpCode ()== Op ::OpTypeStruct ); 400 401const Instruction * elem_type = module .getInstruction (struct_type -> getIdOperand (0 )); 402unsigned int largest_scalar = postProcessGetLargestScalarSize (* elem_type ); 403if (largest_scalar != 0 ) { 404alignment = largest_scalar ; 405 }else { 406alignment = 16 ;// fallback if can't determine a godo alignment 407 } 408 } 409// update the Aligned operand 410assert (alignment != 0 ); 411inst .setImmediateOperand (alignmentIdx ,alignment ); 412 } 413break ; 414 } 415 416default : 417break ; 418 } 419 420// Checks based on type 421if (inst .getTypeId ()!= NoType ) 422postProcessType (inst ,inst .getTypeId ()); 423for (int op = 0 ;op < inst .getNumOperands ();++ op ) { 424if (inst .isIdOperand (op )) { 425// In blocks, these are always result ids, but we are relying on 426// getTypeId() to return NoType for things like OpLabel. 427if (getTypeId (inst .getIdOperand (op ))!= NoType ) 428postProcessType (inst ,getTypeId (inst .getIdOperand (op ))); 429 } 430 } 431} 432 433// comment in header 434void Builder ::postProcessCFG () 435{ 436// reachableBlocks is the set of blockss reached via control flow, or which are 437// unreachable continue targert or unreachable merge. 438 std::unordered_set < const Block *> reachableBlocks ; 439 std::unordered_map < Block * ,Block *> headerForUnreachableContinue ; 440 std::unordered_set < Block *> unreachableMerges ; 441 std::unordered_set < Id > unreachableDefinitions ; 442// Collect IDs defined in unreachable blocks. For each function, label the 443// reachable blocks first. Then for each unreachable block, collect the 444// result IDs of the instructions in it. 445for (auto fi = module .getFunctions ().cbegin ();fi != module .getFunctions ().cend ();fi ++ ) { 446Function * f = * fi ; 447Block * entry = f -> getEntryBlock (); 448inReadableOrder (entry , 449 [& reachableBlocks ,& unreachableMerges ,& headerForUnreachableContinue ] 450 (Block * b ,ReachReason why ,Block * header ) { 451reachableBlocks .insert (b ); 452if (why == ReachDeadContinue )headerForUnreachableContinue [b ]= header ; 453if (why == ReachDeadMerge )unreachableMerges .insert (b ); 454 }); 455for (auto bi = f -> getBlocks ().cbegin ();bi != f -> getBlocks ().cend ();bi ++ ) { 456Block * b = * bi ; 457if (unreachableMerges .count (b )!= 0 || headerForUnreachableContinue .count (b )!= 0 ) { 458auto ii = b -> getInstructions ().cbegin (); 459++ ii ;// Keep potential decorations on the label. 460for (;ii != b -> getInstructions ().cend ();++ ii ) 461unreachableDefinitions .insert (ii -> get ()-> getResultId ()); 462 }else if (reachableBlocks .count (b )== 0 ) { 463// The normal case for unreachable code. All definitions are considered dead. 464for (auto ii = b -> getInstructions ().cbegin ();ii != b -> getInstructions ().cend ();++ ii ) 465unreachableDefinitions .insert (ii -> get ()-> getResultId ()); 466 } 467 } 468 } 469 470// Modify unreachable merge blocks and unreachable continue targets. 471// Delete their contents. 472for (auto mergeIter = unreachableMerges .begin ();mergeIter != unreachableMerges .end ();++ mergeIter ) { 473 (* mergeIter )-> rewriteAsCanonicalUnreachableMerge (); 474 } 475for (auto continueIter = headerForUnreachableContinue .begin (); 476continueIter != headerForUnreachableContinue .end (); 477++ continueIter ) { 478Block * continue_target = continueIter -> first ; 479Block * header = continueIter -> second ; 480continue_target -> rewriteAsCanonicalUnreachableContinue (header ); 481 } 482 483// Remove unneeded decorations, for unreachable instructions 484for (auto decorationIter = decorations .begin ();decorationIter != decorations .end ();) { 485Id decorationId = (* decorationIter )-> getIdOperand (0 ); 486if (unreachableDefinitions .count (decorationId )!= 0 ) { 487decorationIter = decorations .erase (decorationIter ); 488 }else { 489++ decorationIter ; 490 } 491 } 492} 493 494// comment in header 495void Builder ::postProcessFeatures () { 496// Add per-instruction capabilities, extensions, etc., 497 498// Look for any 8/16 bit type in physical storage buffer class, and set the 499// appropriate capability. This happens in createSpvVariable for other storage 500// classes, but there isn't always a variable for physical storage buffer. 501for (int t = 0 ;t < (int )groupedTypes [enumCast (Op ::OpTypePointer )].size ();++ t ) { 502Instruction * type = groupedTypes [enumCast (Op ::OpTypePointer )][t ]; 503if (type -> getImmediateOperand (0 )== (unsigned )StorageClass ::PhysicalStorageBufferEXT ) { 504if (containsType (type -> getIdOperand (1 ),Op ::OpTypeInt ,8 )) { 505addIncorporatedExtension (spv::E_SPV_KHR_8bit_storage , spv::Spv_1_5 ); 506addCapability (spv::Capability ::StorageBuffer8BitAccess ); 507 } 508if (containsType (type -> getIdOperand (1 ),Op ::OpTypeInt ,16 )|| 509containsType (type -> getIdOperand (1 ),Op ::OpTypeFloat ,16 )) { 510addIncorporatedExtension (spv::E_SPV_KHR_16bit_storage , spv::Spv_1_3 ); 511addCapability (spv::Capability ::StorageBuffer16BitAccess ); 512 } 513 } 514 } 515 516// process all block-contained instructions 517for (auto fi = module .getFunctions ().cbegin ();fi != module .getFunctions ().cend ();fi ++ ) { 518Function * f = * fi ; 519for (auto bi = f -> getBlocks ().cbegin ();bi != f -> getBlocks ().cend ();bi ++ ) { 520Block * b = * bi ; 521for (auto ii = b -> getInstructions ().cbegin ();ii != b -> getInstructions ().cend ();ii ++ ) 522postProcess (* ii -> get ()); 523 524// For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether 525// there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the 526// default. 527for (auto vi = b -> getLocalVariables ().cbegin ();vi != b -> getLocalVariables ().cend ();vi ++ ) { 528const Instruction & inst = * vi -> get (); 529Id resultId = inst .getResultId (); 530if (containsPhysicalStorageBufferOrArray (getDerefTypeId (resultId ))) { 531bool foundDecoration = false; 532const auto function = [& ](const std::unique_ptr < Instruction >& decoration ) { 533if (decoration .get ()-> getIdOperand (0 )== resultId && 534decoration .get ()-> getOpCode ()== Op ::OpDecorate && 535 (decoration .get ()-> getImmediateOperand (1 )== spv::Decoration ::AliasedPointerEXT || 536decoration .get ()-> getImmediateOperand (1 )== spv::Decoration ::RestrictPointerEXT )) { 537foundDecoration = true; 538 } 539 }; 540 std::for_each (decorations .begin (),decorations .end (),function ); 541if (!foundDecoration ) { 542addDecoration (resultId , spv::Decoration ::AliasedPointerEXT ); 543 } 544 } 545 } 546 } 547 } 548 549// If any Vulkan memory model-specific functionality is used, update the 550// OpMemoryModel to match. 551if (capabilities .find (spv::Capability ::VulkanMemoryModelKHR )!= capabilities .end ()) { 552memoryModel = spv::MemoryModel ::VulkanKHR ; 553addIncorporatedExtension (spv::E_SPV_KHR_vulkan_memory_model , spv::Spv_1_5 ); 554 } 555 556// Add Aliased decoration if there's more than one Workgroup Block variable. 557// Compile-only mode emits no entry point, so there is no interface to scan. 558if (capabilities .find (spv::Capability ::WorkgroupMemoryExplicitLayoutKHR )!= capabilities .end ()&& 559 !entryPoints .empty ()) { 560auto & ep = entryPoints [0 ]; 561 562 std::vector < Id > workgroup_variables ; 563for (int i = 0 ;i < (int )ep -> getNumOperands ();i ++ ) { 564if (!ep -> isIdOperand (i )) 565continue ; 566 567const Id id = ep -> getIdOperand (i ); 568const Instruction * instr = module .getInstruction (id ); 569if (instr -> getOpCode ()!= spv::Op ::OpVariable ) 570continue ; 571 572if (instr -> getImmediateOperand (0 )== spv::StorageClass ::Workgroup ) 573workgroup_variables .push_back (id ); 574 } 575 576if (workgroup_variables .size ()> 1 ) { 577for (size_t i = 0 ;i < workgroup_variables .size ();i ++ ) 578addDecoration (workgroup_variables [i ], spv::Decoration ::Aliased ); 579 } 580 } 581} 582 583// SPIR-V requires that any instruction consuming the result of an OpSampledImage 584// be in the same block as the OpSampledImage instruction. This pass goes finds 585// uses of OpSampledImage where that is not the case and duplicates the 586// OpSampledImage to be immediately before the instruction that consumes it. 587// The old OpSampledImage is left in place, potentially with no users. 588void Builder ::postProcessSamplers () 589{ 590// first, find all OpSampledImage instructions and store them in a map. 591 std::map < Id ,Instruction *> sampledImageInstrs ; 592for (auto f :module .getFunctions ()) { 593for (auto b :f -> getBlocks ()) { 594for (auto & i :b -> getInstructions ()) { 595if (i -> getOpCode ()== spv::Op ::OpSampledImage ) { 596sampledImageInstrs [i -> getResultId ()]= i .get (); 597 } 598 } 599 } 600 } 601// next find all uses of the given ids and rewrite them if needed. 602for (auto f :module .getFunctions ()) { 603for (auto b :f -> getBlocks ()) { 604auto & instrs = b -> getInstructions (); 605for (size_t idx = 0 ;idx < instrs .size ();idx ++ ) { 606Instruction * i = instrs [idx ].get (); 607for (int opnum = 0 ;opnum < i -> getNumOperands ();opnum ++ ) { 608// Is this operand of the current instruction the result of an OpSampledImage? 609if (i -> isIdOperand (opnum )&& 610sampledImageInstrs .count (i -> getIdOperand (opnum ))) 611 { 612Instruction * opSampImg = sampledImageInstrs [i -> getIdOperand (opnum )]; 613if (i -> getBlock ()!= opSampImg -> getBlock ()) { 614Instruction * newInstr = new Instruction (getUniqueId (), 615opSampImg -> getTypeId (), 616 spv::Op ::OpSampledImage ); 617newInstr -> addIdOperand (opSampImg -> getIdOperand (0 )); 618newInstr -> addIdOperand (opSampImg -> getIdOperand (1 )); 619newInstr -> setBlock (b ); 620 621// rewrite the user of the OpSampledImage to use the new instruction. 622i -> setIdOperand (opnum ,newInstr -> getResultId ()); 623// insert the new OpSampledImage right before the current instruction. 624instrs .insert (instrs .begin ()+ idx , 625 std::unique_ptr < Instruction > (newInstr )); 626idx ++ ; 627 } 628 } 629 } 630 } 631 } 632 } 633} 634 635// comment in header 636void Builder ::postProcess (bool compileOnly ) 637{ 638// postProcessCFG needs an entrypoint to determine what is reachable, but if we are not creating an "executable" shader, we don't have an entrypoint 639if (!compileOnly ) 640postProcessCFG (); 641 642postProcessFeatures (); 643postProcessSamplers (); 644} 645 646}// end spv namespace