1617a3babSopenharmony_ci// 2617a3babSopenharmony_ci// Copyright (C) 2018 Google, Inc. 3617a3babSopenharmony_ci// 4617a3babSopenharmony_ci// All rights reserved. 5617a3babSopenharmony_ci// 6617a3babSopenharmony_ci// Redistribution and use in source and binary forms, with or without 7617a3babSopenharmony_ci// modification, are permitted provided that the following conditions 8617a3babSopenharmony_ci// are met: 9617a3babSopenharmony_ci// 10617a3babSopenharmony_ci// Redistributions of source code must retain the above copyright 11617a3babSopenharmony_ci// notice, this list of conditions and the following disclaimer. 12617a3babSopenharmony_ci// 13617a3babSopenharmony_ci// Redistributions in binary form must reproduce the above 14617a3babSopenharmony_ci// copyright notice, this list of conditions and the following 15617a3babSopenharmony_ci// disclaimer in the documentation and/or other materials provided 16617a3babSopenharmony_ci// with the distribution. 17617a3babSopenharmony_ci// 18617a3babSopenharmony_ci// Neither the name of 3Dlabs Inc. Ltd. nor the names of its 19617a3babSopenharmony_ci// contributors may be used to endorse or promote products derived 20617a3babSopenharmony_ci// from this software without specific prior written permission. 21617a3babSopenharmony_ci// 22617a3babSopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23617a3babSopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24617a3babSopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25617a3babSopenharmony_ci// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26617a3babSopenharmony_ci// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27617a3babSopenharmony_ci// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28617a3babSopenharmony_ci// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29617a3babSopenharmony_ci// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30617a3babSopenharmony_ci// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31617a3babSopenharmony_ci// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32617a3babSopenharmony_ci// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33617a3babSopenharmony_ci// POSSIBILITY OF SUCH DAMAGE. 34617a3babSopenharmony_ci 35617a3babSopenharmony_ci// 36617a3babSopenharmony_ci// Post-processing for SPIR-V IR, in internal form, not standard binary form. 37617a3babSopenharmony_ci// 38617a3babSopenharmony_ci 39617a3babSopenharmony_ci#include <cassert> 40617a3babSopenharmony_ci#include <cstdlib> 41617a3babSopenharmony_ci 42617a3babSopenharmony_ci#include <unordered_map> 43617a3babSopenharmony_ci#include <unordered_set> 44617a3babSopenharmony_ci#include <algorithm> 45617a3babSopenharmony_ci 46617a3babSopenharmony_ci#include "SpvBuilder.h" 47617a3babSopenharmony_ci#include "spirv.hpp" 48617a3babSopenharmony_ci 49617a3babSopenharmony_cinamespace spv { 50617a3babSopenharmony_ci #include "GLSL.std.450.h" 51617a3babSopenharmony_ci #include "GLSL.ext.KHR.h" 52617a3babSopenharmony_ci #include "GLSL.ext.EXT.h" 53617a3babSopenharmony_ci #include "GLSL.ext.AMD.h" 54617a3babSopenharmony_ci #include "GLSL.ext.NV.h" 55617a3babSopenharmony_ci #include "GLSL.ext.ARM.h" 56617a3babSopenharmony_ci #include "GLSL.ext.QCOM.h" 57617a3babSopenharmony_ci} 58617a3babSopenharmony_ci 59617a3babSopenharmony_cinamespace spv { 60617a3babSopenharmony_ci 61617a3babSopenharmony_ci// Hook to visit each operand type and result type of an instruction. 62617a3babSopenharmony_ci// Will be called multiple times for one instruction, once for each typed 63617a3babSopenharmony_ci// operand and the result. 64617a3babSopenharmony_civoid Builder::postProcessType(const Instruction& inst, Id typeId) 65617a3babSopenharmony_ci{ 66617a3babSopenharmony_ci // Characterize the type being questioned 67617a3babSopenharmony_ci Id basicTypeOp = getMostBasicTypeClass(typeId); 68617a3babSopenharmony_ci int width = 0; 69617a3babSopenharmony_ci if (basicTypeOp == OpTypeFloat || basicTypeOp == OpTypeInt) 70617a3babSopenharmony_ci width = getScalarTypeWidth(typeId); 71617a3babSopenharmony_ci 72617a3babSopenharmony_ci // Do opcode-specific checks 73617a3babSopenharmony_ci switch (inst.getOpCode()) { 74617a3babSopenharmony_ci case OpLoad: 75617a3babSopenharmony_ci case OpStore: 76617a3babSopenharmony_ci if (basicTypeOp == OpTypeStruct) { 77617a3babSopenharmony_ci if (containsType(typeId, OpTypeInt, 8)) 78617a3babSopenharmony_ci addCapability(CapabilityInt8); 79617a3babSopenharmony_ci if (containsType(typeId, OpTypeInt, 16)) 80617a3babSopenharmony_ci addCapability(CapabilityInt16); 81617a3babSopenharmony_ci if (containsType(typeId, OpTypeFloat, 16)) 82617a3babSopenharmony_ci addCapability(CapabilityFloat16); 83617a3babSopenharmony_ci } else { 84617a3babSopenharmony_ci StorageClass storageClass = getStorageClass(inst.getIdOperand(0)); 85617a3babSopenharmony_ci if (width == 8) { 86617a3babSopenharmony_ci switch (storageClass) { 87617a3babSopenharmony_ci case StorageClassPhysicalStorageBufferEXT: 88617a3babSopenharmony_ci case StorageClassUniform: 89617a3babSopenharmony_ci case StorageClassStorageBuffer: 90617a3babSopenharmony_ci case StorageClassPushConstant: 91617a3babSopenharmony_ci break; 92617a3babSopenharmony_ci default: 93617a3babSopenharmony_ci addCapability(CapabilityInt8); 94617a3babSopenharmony_ci break; 95617a3babSopenharmony_ci } 96617a3babSopenharmony_ci } else if (width == 16) { 97617a3babSopenharmony_ci switch (storageClass) { 98617a3babSopenharmony_ci case StorageClassPhysicalStorageBufferEXT: 99617a3babSopenharmony_ci case StorageClassUniform: 100617a3babSopenharmony_ci case StorageClassStorageBuffer: 101617a3babSopenharmony_ci case StorageClassPushConstant: 102617a3babSopenharmony_ci case StorageClassInput: 103617a3babSopenharmony_ci case StorageClassOutput: 104617a3babSopenharmony_ci break; 105617a3babSopenharmony_ci default: 106617a3babSopenharmony_ci if (basicTypeOp == OpTypeInt) 107617a3babSopenharmony_ci addCapability(CapabilityInt16); 108617a3babSopenharmony_ci if (basicTypeOp == OpTypeFloat) 109617a3babSopenharmony_ci addCapability(CapabilityFloat16); 110617a3babSopenharmony_ci break; 111617a3babSopenharmony_ci } 112617a3babSopenharmony_ci } 113617a3babSopenharmony_ci } 114617a3babSopenharmony_ci break; 115617a3babSopenharmony_ci case OpCopyObject: 116617a3babSopenharmony_ci break; 117617a3babSopenharmony_ci case OpFConvert: 118617a3babSopenharmony_ci case OpSConvert: 119617a3babSopenharmony_ci case OpUConvert: 120617a3babSopenharmony_ci // Look for any 8/16-bit storage capabilities. If there are none, assume that 121617a3babSopenharmony_ci // the convert instruction requires the Float16/Int8/16 capability. 122617a3babSopenharmony_ci if (containsType(typeId, OpTypeFloat, 16) || containsType(typeId, OpTypeInt, 16)) { 123617a3babSopenharmony_ci bool foundStorage = false; 124617a3babSopenharmony_ci for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { 125617a3babSopenharmony_ci spv::Capability cap = *it; 126617a3babSopenharmony_ci if (cap == spv::CapabilityStorageInputOutput16 || 127617a3babSopenharmony_ci cap == spv::CapabilityStoragePushConstant16 || 128617a3babSopenharmony_ci cap == spv::CapabilityStorageUniformBufferBlock16 || 129617a3babSopenharmony_ci cap == spv::CapabilityStorageUniform16) { 130617a3babSopenharmony_ci foundStorage = true; 131617a3babSopenharmony_ci break; 132617a3babSopenharmony_ci } 133617a3babSopenharmony_ci } 134617a3babSopenharmony_ci if (!foundStorage) { 135617a3babSopenharmony_ci if (containsType(typeId, OpTypeFloat, 16)) 136617a3babSopenharmony_ci addCapability(CapabilityFloat16); 137617a3babSopenharmony_ci if (containsType(typeId, OpTypeInt, 16)) 138617a3babSopenharmony_ci addCapability(CapabilityInt16); 139617a3babSopenharmony_ci } 140617a3babSopenharmony_ci } 141617a3babSopenharmony_ci if (containsType(typeId, OpTypeInt, 8)) { 142617a3babSopenharmony_ci bool foundStorage = false; 143617a3babSopenharmony_ci for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { 144617a3babSopenharmony_ci spv::Capability cap = *it; 145617a3babSopenharmony_ci if (cap == spv::CapabilityStoragePushConstant8 || 146617a3babSopenharmony_ci cap == spv::CapabilityUniformAndStorageBuffer8BitAccess || 147617a3babSopenharmony_ci cap == spv::CapabilityStorageBuffer8BitAccess) { 148617a3babSopenharmony_ci foundStorage = true; 149617a3babSopenharmony_ci break; 150617a3babSopenharmony_ci } 151617a3babSopenharmony_ci } 152617a3babSopenharmony_ci if (!foundStorage) { 153617a3babSopenharmony_ci addCapability(CapabilityInt8); 154617a3babSopenharmony_ci } 155617a3babSopenharmony_ci } 156617a3babSopenharmony_ci break; 157617a3babSopenharmony_ci case OpExtInst: 158617a3babSopenharmony_ci switch (inst.getImmediateOperand(1)) { 159617a3babSopenharmony_ci case GLSLstd450Frexp: 160617a3babSopenharmony_ci case GLSLstd450FrexpStruct: 161617a3babSopenharmony_ci if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, OpTypeInt, 16)) 162617a3babSopenharmony_ci addExtension(spv::E_SPV_AMD_gpu_shader_int16); 163617a3babSopenharmony_ci break; 164617a3babSopenharmony_ci case GLSLstd450InterpolateAtCentroid: 165617a3babSopenharmony_ci case GLSLstd450InterpolateAtSample: 166617a3babSopenharmony_ci case GLSLstd450InterpolateAtOffset: 167617a3babSopenharmony_ci if (getSpvVersion() < spv::Spv_1_3 && containsType(typeId, OpTypeFloat, 16)) 168617a3babSopenharmony_ci addExtension(spv::E_SPV_AMD_gpu_shader_half_float); 169617a3babSopenharmony_ci break; 170617a3babSopenharmony_ci default: 171617a3babSopenharmony_ci break; 172617a3babSopenharmony_ci } 173617a3babSopenharmony_ci break; 174617a3babSopenharmony_ci case OpAccessChain: 175617a3babSopenharmony_ci case OpPtrAccessChain: 176617a3babSopenharmony_ci if (isPointerType(typeId)) 177617a3babSopenharmony_ci break; 178617a3babSopenharmony_ci if (basicTypeOp == OpTypeInt) { 179617a3babSopenharmony_ci if (width == 16) 180617a3babSopenharmony_ci addCapability(CapabilityInt16); 181617a3babSopenharmony_ci else if (width == 8) 182617a3babSopenharmony_ci addCapability(CapabilityInt8); 183617a3babSopenharmony_ci } 184617a3babSopenharmony_ci default: 185617a3babSopenharmony_ci if (basicTypeOp == OpTypeInt) { 186617a3babSopenharmony_ci if (width == 16) 187617a3babSopenharmony_ci addCapability(CapabilityInt16); 188617a3babSopenharmony_ci else if (width == 8) 189617a3babSopenharmony_ci addCapability(CapabilityInt8); 190617a3babSopenharmony_ci else if (width == 64) 191617a3babSopenharmony_ci addCapability(CapabilityInt64); 192617a3babSopenharmony_ci } else if (basicTypeOp == OpTypeFloat) { 193617a3babSopenharmony_ci if (width == 16) 194617a3babSopenharmony_ci addCapability(CapabilityFloat16); 195617a3babSopenharmony_ci else if (width == 64) 196617a3babSopenharmony_ci addCapability(CapabilityFloat64); 197617a3babSopenharmony_ci } 198617a3babSopenharmony_ci break; 199617a3babSopenharmony_ci } 200617a3babSopenharmony_ci} 201617a3babSopenharmony_ci 202617a3babSopenharmony_ci// Called for each instruction that resides in a block. 203617a3babSopenharmony_civoid Builder::postProcess(Instruction& inst) 204617a3babSopenharmony_ci{ 205617a3babSopenharmony_ci // Add capabilities based simply on the opcode. 206617a3babSopenharmony_ci switch (inst.getOpCode()) { 207617a3babSopenharmony_ci case OpExtInst: 208617a3babSopenharmony_ci switch (inst.getImmediateOperand(1)) { 209617a3babSopenharmony_ci case GLSLstd450InterpolateAtCentroid: 210617a3babSopenharmony_ci case GLSLstd450InterpolateAtSample: 211617a3babSopenharmony_ci case GLSLstd450InterpolateAtOffset: 212617a3babSopenharmony_ci addCapability(CapabilityInterpolationFunction); 213617a3babSopenharmony_ci break; 214617a3babSopenharmony_ci default: 215617a3babSopenharmony_ci break; 216617a3babSopenharmony_ci } 217617a3babSopenharmony_ci break; 218617a3babSopenharmony_ci case OpDPdxFine: 219617a3babSopenharmony_ci case OpDPdyFine: 220617a3babSopenharmony_ci case OpFwidthFine: 221617a3babSopenharmony_ci case OpDPdxCoarse: 222617a3babSopenharmony_ci case OpDPdyCoarse: 223617a3babSopenharmony_ci case OpFwidthCoarse: 224617a3babSopenharmony_ci addCapability(CapabilityDerivativeControl); 225617a3babSopenharmony_ci break; 226617a3babSopenharmony_ci 227617a3babSopenharmony_ci case OpImageQueryLod: 228617a3babSopenharmony_ci case OpImageQuerySize: 229617a3babSopenharmony_ci case OpImageQuerySizeLod: 230617a3babSopenharmony_ci case OpImageQuerySamples: 231617a3babSopenharmony_ci case OpImageQueryLevels: 232617a3babSopenharmony_ci addCapability(CapabilityImageQuery); 233617a3babSopenharmony_ci break; 234617a3babSopenharmony_ci 235617a3babSopenharmony_ci case OpGroupNonUniformPartitionNV: 236617a3babSopenharmony_ci addExtension(E_SPV_NV_shader_subgroup_partitioned); 237617a3babSopenharmony_ci addCapability(CapabilityGroupNonUniformPartitionedNV); 238617a3babSopenharmony_ci break; 239617a3babSopenharmony_ci 240617a3babSopenharmony_ci case OpLoad: 241617a3babSopenharmony_ci case OpStore: 242617a3babSopenharmony_ci { 243617a3babSopenharmony_ci // For any load/store to a PhysicalStorageBufferEXT, walk the accesschain 244617a3babSopenharmony_ci // index list to compute the misalignment. The pre-existing alignment value 245617a3babSopenharmony_ci // (set via Builder::AccessChain::alignment) only accounts for the base of 246617a3babSopenharmony_ci // the reference type and any scalar component selection in the accesschain, 247617a3babSopenharmony_ci // and this function computes the rest from the SPIR-V Offset decorations. 248617a3babSopenharmony_ci Instruction *accessChain = module.getInstruction(inst.getIdOperand(0)); 249617a3babSopenharmony_ci if (accessChain->getOpCode() == OpAccessChain) { 250617a3babSopenharmony_ci Instruction *base = module.getInstruction(accessChain->getIdOperand(0)); 251617a3babSopenharmony_ci // Get the type of the base of the access chain. It must be a pointer type. 252617a3babSopenharmony_ci Id typeId = base->getTypeId(); 253617a3babSopenharmony_ci Instruction *type = module.getInstruction(typeId); 254617a3babSopenharmony_ci assert(type->getOpCode() == OpTypePointer); 255617a3babSopenharmony_ci if (type->getImmediateOperand(0) != StorageClassPhysicalStorageBufferEXT) { 256617a3babSopenharmony_ci break; 257617a3babSopenharmony_ci } 258617a3babSopenharmony_ci // Get the pointee type. 259617a3babSopenharmony_ci typeId = type->getIdOperand(1); 260617a3babSopenharmony_ci type = module.getInstruction(typeId); 261617a3babSopenharmony_ci // Walk the index list for the access chain. For each index, find any 262617a3babSopenharmony_ci // misalignment that can apply when accessing the member/element via 263617a3babSopenharmony_ci // Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all 264617a3babSopenharmony_ci // together. 265617a3babSopenharmony_ci int alignment = 0; 266617a3babSopenharmony_ci for (int i = 1; i < accessChain->getNumOperands(); ++i) { 267617a3babSopenharmony_ci Instruction *idx = module.getInstruction(accessChain->getIdOperand(i)); 268617a3babSopenharmony_ci if (type->getOpCode() == OpTypeStruct) { 269617a3babSopenharmony_ci assert(idx->getOpCode() == OpConstant); 270617a3babSopenharmony_ci unsigned int c = idx->getImmediateOperand(0); 271617a3babSopenharmony_ci 272617a3babSopenharmony_ci const auto function = [&](const std::unique_ptr<Instruction>& decoration) { 273617a3babSopenharmony_ci if (decoration.get()->getOpCode() == OpMemberDecorate && 274617a3babSopenharmony_ci decoration.get()->getIdOperand(0) == typeId && 275617a3babSopenharmony_ci decoration.get()->getImmediateOperand(1) == c && 276617a3babSopenharmony_ci (decoration.get()->getImmediateOperand(2) == DecorationOffset || 277617a3babSopenharmony_ci decoration.get()->getImmediateOperand(2) == DecorationMatrixStride)) { 278617a3babSopenharmony_ci alignment |= decoration.get()->getImmediateOperand(3); 279617a3babSopenharmony_ci } 280617a3babSopenharmony_ci }; 281617a3babSopenharmony_ci std::for_each(decorations.begin(), decorations.end(), function); 282617a3babSopenharmony_ci // get the next member type 283617a3babSopenharmony_ci typeId = type->getIdOperand(c); 284617a3babSopenharmony_ci type = module.getInstruction(typeId); 285617a3babSopenharmony_ci } else if (type->getOpCode() == OpTypeArray || 286617a3babSopenharmony_ci type->getOpCode() == OpTypeRuntimeArray) { 287617a3babSopenharmony_ci const auto function = [&](const std::unique_ptr<Instruction>& decoration) { 288617a3babSopenharmony_ci if (decoration.get()->getOpCode() == OpDecorate && 289617a3babSopenharmony_ci decoration.get()->getIdOperand(0) == typeId && 290617a3babSopenharmony_ci decoration.get()->getImmediateOperand(1) == DecorationArrayStride) { 291617a3babSopenharmony_ci alignment |= decoration.get()->getImmediateOperand(2); 292617a3babSopenharmony_ci } 293617a3babSopenharmony_ci }; 294617a3babSopenharmony_ci std::for_each(decorations.begin(), decorations.end(), function); 295617a3babSopenharmony_ci // Get the element type 296617a3babSopenharmony_ci typeId = type->getIdOperand(0); 297617a3babSopenharmony_ci type = module.getInstruction(typeId); 298617a3babSopenharmony_ci } else { 299617a3babSopenharmony_ci // Once we get to any non-aggregate type, we're done. 300617a3babSopenharmony_ci break; 301617a3babSopenharmony_ci } 302617a3babSopenharmony_ci } 303617a3babSopenharmony_ci assert(inst.getNumOperands() >= 3); 304617a3babSopenharmony_ci unsigned int memoryAccess = inst.getImmediateOperand((inst.getOpCode() == OpStore) ? 2 : 1); 305617a3babSopenharmony_ci assert(memoryAccess & MemoryAccessAlignedMask); 306617a3babSopenharmony_ci static_cast<void>(memoryAccess); 307617a3babSopenharmony_ci // Compute the index of the alignment operand. 308617a3babSopenharmony_ci int alignmentIdx = 2; 309617a3babSopenharmony_ci if (inst.getOpCode() == OpStore) 310617a3babSopenharmony_ci alignmentIdx++; 311617a3babSopenharmony_ci // Merge new and old (mis)alignment 312617a3babSopenharmony_ci alignment |= inst.getImmediateOperand(alignmentIdx); 313617a3babSopenharmony_ci // Pick the LSB 314617a3babSopenharmony_ci alignment = alignment & ~(alignment & (alignment-1)); 315617a3babSopenharmony_ci // update the Aligned operand 316617a3babSopenharmony_ci inst.setImmediateOperand(alignmentIdx, alignment); 317617a3babSopenharmony_ci } 318617a3babSopenharmony_ci break; 319617a3babSopenharmony_ci } 320617a3babSopenharmony_ci 321617a3babSopenharmony_ci default: 322617a3babSopenharmony_ci break; 323617a3babSopenharmony_ci } 324617a3babSopenharmony_ci 325617a3babSopenharmony_ci // Checks based on type 326617a3babSopenharmony_ci if (inst.getTypeId() != NoType) 327617a3babSopenharmony_ci postProcessType(inst, inst.getTypeId()); 328617a3babSopenharmony_ci for (int op = 0; op < inst.getNumOperands(); ++op) { 329617a3babSopenharmony_ci if (inst.isIdOperand(op)) { 330617a3babSopenharmony_ci // In blocks, these are always result ids, but we are relying on 331617a3babSopenharmony_ci // getTypeId() to return NoType for things like OpLabel. 332617a3babSopenharmony_ci if (getTypeId(inst.getIdOperand(op)) != NoType) 333617a3babSopenharmony_ci postProcessType(inst, getTypeId(inst.getIdOperand(op))); 334617a3babSopenharmony_ci } 335617a3babSopenharmony_ci } 336617a3babSopenharmony_ci} 337617a3babSopenharmony_ci 338617a3babSopenharmony_ci// comment in header 339617a3babSopenharmony_civoid Builder::postProcessCFG() 340617a3babSopenharmony_ci{ 341617a3babSopenharmony_ci // reachableBlocks is the set of blockss reached via control flow, or which are 342617a3babSopenharmony_ci // unreachable continue targert or unreachable merge. 343617a3babSopenharmony_ci std::unordered_set<const Block*> reachableBlocks; 344617a3babSopenharmony_ci std::unordered_map<Block*, Block*> headerForUnreachableContinue; 345617a3babSopenharmony_ci std::unordered_set<Block*> unreachableMerges; 346617a3babSopenharmony_ci std::unordered_set<Id> unreachableDefinitions; 347617a3babSopenharmony_ci // Collect IDs defined in unreachable blocks. For each function, label the 348617a3babSopenharmony_ci // reachable blocks first. Then for each unreachable block, collect the 349617a3babSopenharmony_ci // result IDs of the instructions in it. 350617a3babSopenharmony_ci for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { 351617a3babSopenharmony_ci Function* f = *fi; 352617a3babSopenharmony_ci Block* entry = f->getEntryBlock(); 353617a3babSopenharmony_ci inReadableOrder(entry, 354617a3babSopenharmony_ci [&reachableBlocks, &unreachableMerges, &headerForUnreachableContinue] 355617a3babSopenharmony_ci (Block* b, ReachReason why, Block* header) { 356617a3babSopenharmony_ci reachableBlocks.insert(b); 357617a3babSopenharmony_ci if (why == ReachDeadContinue) headerForUnreachableContinue[b] = header; 358617a3babSopenharmony_ci if (why == ReachDeadMerge) unreachableMerges.insert(b); 359617a3babSopenharmony_ci }); 360617a3babSopenharmony_ci for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { 361617a3babSopenharmony_ci Block* b = *bi; 362617a3babSopenharmony_ci if (unreachableMerges.count(b) != 0 || headerForUnreachableContinue.count(b) != 0) { 363617a3babSopenharmony_ci auto ii = b->getInstructions().cbegin(); 364617a3babSopenharmony_ci ++ii; // Keep potential decorations on the label. 365617a3babSopenharmony_ci for (; ii != b->getInstructions().cend(); ++ii) 366617a3babSopenharmony_ci unreachableDefinitions.insert(ii->get()->getResultId()); 367617a3babSopenharmony_ci } else if (reachableBlocks.count(b) == 0) { 368617a3babSopenharmony_ci // The normal case for unreachable code. All definitions are considered dead. 369617a3babSopenharmony_ci for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ++ii) 370617a3babSopenharmony_ci unreachableDefinitions.insert(ii->get()->getResultId()); 371617a3babSopenharmony_ci } 372617a3babSopenharmony_ci } 373617a3babSopenharmony_ci } 374617a3babSopenharmony_ci 375617a3babSopenharmony_ci // Modify unreachable merge blocks and unreachable continue targets. 376617a3babSopenharmony_ci // Delete their contents. 377617a3babSopenharmony_ci for (auto mergeIter = unreachableMerges.begin(); mergeIter != unreachableMerges.end(); ++mergeIter) { 378617a3babSopenharmony_ci (*mergeIter)->rewriteAsCanonicalUnreachableMerge(); 379617a3babSopenharmony_ci } 380617a3babSopenharmony_ci for (auto continueIter = headerForUnreachableContinue.begin(); 381617a3babSopenharmony_ci continueIter != headerForUnreachableContinue.end(); 382617a3babSopenharmony_ci ++continueIter) { 383617a3babSopenharmony_ci Block* continue_target = continueIter->first; 384617a3babSopenharmony_ci Block* header = continueIter->second; 385617a3babSopenharmony_ci continue_target->rewriteAsCanonicalUnreachableContinue(header); 386617a3babSopenharmony_ci } 387617a3babSopenharmony_ci 388617a3babSopenharmony_ci // Remove unneeded decorations, for unreachable instructions 389617a3babSopenharmony_ci decorations.erase(std::remove_if(decorations.begin(), decorations.end(), 390617a3babSopenharmony_ci [&unreachableDefinitions](std::unique_ptr<Instruction>& I) -> bool { 391617a3babSopenharmony_ci Id decoration_id = I.get()->getIdOperand(0); 392617a3babSopenharmony_ci return unreachableDefinitions.count(decoration_id) != 0; 393617a3babSopenharmony_ci }), 394617a3babSopenharmony_ci decorations.end()); 395617a3babSopenharmony_ci} 396617a3babSopenharmony_ci 397617a3babSopenharmony_ci// comment in header 398617a3babSopenharmony_civoid Builder::postProcessFeatures() { 399617a3babSopenharmony_ci // Add per-instruction capabilities, extensions, etc., 400617a3babSopenharmony_ci 401617a3babSopenharmony_ci // Look for any 8/16 bit type in physical storage buffer class, and set the 402617a3babSopenharmony_ci // appropriate capability. This happens in createSpvVariable for other storage 403617a3babSopenharmony_ci // classes, but there isn't always a variable for physical storage buffer. 404617a3babSopenharmony_ci for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { 405617a3babSopenharmony_ci Instruction* type = groupedTypes[OpTypePointer][t]; 406617a3babSopenharmony_ci if (type->getImmediateOperand(0) == (unsigned)StorageClassPhysicalStorageBufferEXT) { 407617a3babSopenharmony_ci if (containsType(type->getIdOperand(1), OpTypeInt, 8)) { 408617a3babSopenharmony_ci addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); 409617a3babSopenharmony_ci addCapability(spv::CapabilityStorageBuffer8BitAccess); 410617a3babSopenharmony_ci } 411617a3babSopenharmony_ci if (containsType(type->getIdOperand(1), OpTypeInt, 16) || 412617a3babSopenharmony_ci containsType(type->getIdOperand(1), OpTypeFloat, 16)) { 413617a3babSopenharmony_ci addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); 414617a3babSopenharmony_ci addCapability(spv::CapabilityStorageBuffer16BitAccess); 415617a3babSopenharmony_ci } 416617a3babSopenharmony_ci } 417617a3babSopenharmony_ci } 418617a3babSopenharmony_ci 419617a3babSopenharmony_ci // process all block-contained instructions 420617a3babSopenharmony_ci for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { 421617a3babSopenharmony_ci Function* f = *fi; 422617a3babSopenharmony_ci for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { 423617a3babSopenharmony_ci Block* b = *bi; 424617a3babSopenharmony_ci for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) 425617a3babSopenharmony_ci postProcess(*ii->get()); 426617a3babSopenharmony_ci 427617a3babSopenharmony_ci // For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether 428617a3babSopenharmony_ci // there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the 429617a3babSopenharmony_ci // default. 430617a3babSopenharmony_ci for (auto vi = b->getLocalVariables().cbegin(); vi != b->getLocalVariables().cend(); vi++) { 431617a3babSopenharmony_ci const Instruction& inst = *vi->get(); 432617a3babSopenharmony_ci Id resultId = inst.getResultId(); 433617a3babSopenharmony_ci if (containsPhysicalStorageBufferOrArray(getDerefTypeId(resultId))) { 434617a3babSopenharmony_ci bool foundDecoration = false; 435617a3babSopenharmony_ci const auto function = [&](const std::unique_ptr<Instruction>& decoration) { 436617a3babSopenharmony_ci if (decoration.get()->getIdOperand(0) == resultId && 437617a3babSopenharmony_ci decoration.get()->getOpCode() == OpDecorate && 438617a3babSopenharmony_ci (decoration.get()->getImmediateOperand(1) == spv::DecorationAliasedPointerEXT || 439617a3babSopenharmony_ci decoration.get()->getImmediateOperand(1) == spv::DecorationRestrictPointerEXT)) { 440617a3babSopenharmony_ci foundDecoration = true; 441617a3babSopenharmony_ci } 442617a3babSopenharmony_ci }; 443617a3babSopenharmony_ci std::for_each(decorations.begin(), decorations.end(), function); 444617a3babSopenharmony_ci if (!foundDecoration) { 445617a3babSopenharmony_ci addDecoration(resultId, spv::DecorationAliasedPointerEXT); 446617a3babSopenharmony_ci } 447617a3babSopenharmony_ci } 448617a3babSopenharmony_ci } 449617a3babSopenharmony_ci } 450617a3babSopenharmony_ci } 451617a3babSopenharmony_ci 452617a3babSopenharmony_ci // If any Vulkan memory model-specific functionality is used, update the 453617a3babSopenharmony_ci // OpMemoryModel to match. 454617a3babSopenharmony_ci if (capabilities.find(spv::CapabilityVulkanMemoryModelKHR) != capabilities.end()) { 455617a3babSopenharmony_ci memoryModel = spv::MemoryModelVulkanKHR; 456617a3babSopenharmony_ci addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5); 457617a3babSopenharmony_ci } 458617a3babSopenharmony_ci 459617a3babSopenharmony_ci // Add Aliased decoration if there's more than one Workgroup Block variable. 460617a3babSopenharmony_ci if (capabilities.find(spv::CapabilityWorkgroupMemoryExplicitLayoutKHR) != capabilities.end()) { 461617a3babSopenharmony_ci assert(entryPoints.size() == 1); 462617a3babSopenharmony_ci auto &ep = entryPoints[0]; 463617a3babSopenharmony_ci 464617a3babSopenharmony_ci std::vector<Id> workgroup_variables; 465617a3babSopenharmony_ci for (int i = 0; i < (int)ep->getNumOperands(); i++) { 466617a3babSopenharmony_ci if (!ep->isIdOperand(i)) 467617a3babSopenharmony_ci continue; 468617a3babSopenharmony_ci 469617a3babSopenharmony_ci const Id id = ep->getIdOperand(i); 470617a3babSopenharmony_ci const Instruction *instr = module.getInstruction(id); 471617a3babSopenharmony_ci if (instr->getOpCode() != spv::OpVariable) 472617a3babSopenharmony_ci continue; 473617a3babSopenharmony_ci 474617a3babSopenharmony_ci if (instr->getImmediateOperand(0) == spv::StorageClassWorkgroup) 475617a3babSopenharmony_ci workgroup_variables.push_back(id); 476617a3babSopenharmony_ci } 477617a3babSopenharmony_ci 478617a3babSopenharmony_ci if (workgroup_variables.size() > 1) { 479617a3babSopenharmony_ci for (size_t i = 0; i < workgroup_variables.size(); i++) 480617a3babSopenharmony_ci addDecoration(workgroup_variables[i], spv::DecorationAliased); 481617a3babSopenharmony_ci } 482617a3babSopenharmony_ci } 483617a3babSopenharmony_ci} 484617a3babSopenharmony_ci 485617a3babSopenharmony_ci// comment in header 486617a3babSopenharmony_civoid Builder::postProcess(bool compileOnly) 487617a3babSopenharmony_ci{ 488617a3babSopenharmony_ci // postProcessCFG needs an entrypoint to determine what is reachable, but if we are not creating an "executable" shader, we don't have an entrypoint 489617a3babSopenharmony_ci if (!compileOnly) 490617a3babSopenharmony_ci postProcessCFG(); 491617a3babSopenharmony_ci 492617a3babSopenharmony_ci postProcessFeatures(); 493617a3babSopenharmony_ci} 494617a3babSopenharmony_ci 495617a3babSopenharmony_ci}; // end spv namespace 496