#include "slang-ir-glsl-liveness.h" #include "slang-ir-dominators.h" #include "slang-ir-insts.h" #include "slang-ir.h" namespace Slang { namespace { // anonymous struct GLSLLivenessContext { enum class Kind { Start, End, CountOf, }; /// Process the module void processModule(); GLSLLivenessContext(IRModule* module) : m_module(module), m_builder(module) { } void _replaceMarker(IRLiveRangeMarker* liveMarker); void _addDecorations(Kind kind, IRFunc* func); /// Process a function in the module void _processFunction(IRFunc* funcInst); IRType* _getReferencedType(IRInst* referenced); Kind getKind(IROp op) { switch (op) { case kIROp_LiveRangeStart: return Kind::Start; case kIROp_LiveRangeEnd: return Kind::End; default: break; } SLANG_UNREACHABLE("Invalid op"); } // Entry holds information about each of the function kinds struct Entry { Dictionary m_funcs; ///< Map of the parameter type to functions implementing (for the kind) IRStringLit* m_nameHintLiteral = nullptr; ///< Name hint string literal of the function IRInst* m_spirvOpLiteral = nullptr; ///< The SPIR-V opcode for the kind }; List m_markerInsts; ///< All of the liveness marker instrucitons found Entry m_entries[Index(Kind::CountOf)]; /// Entry for each kind of function IRInst* m_zeroIntLiteral = nullptr; ///< Zero value literal IRType* m_spirvIntLiteralType = nullptr; ///< Int type that emits as `spirv_literal` IRModule* m_module; IRBuilder m_builder; }; void GLSLLivenessContext::_processFunction(IRFunc* funcInst) { // Iterate through blocks in the function, looking for variables to live track for (auto block = funcInst->getFirstBlock(); block; block = block->getNextBlock()) { for (auto inst = block->getFirstChild(); inst; inst = inst->getNextInst()) { IRLiveRangeMarker* marker = as(inst); if (marker) { m_markerInsts.add(marker); } } } } void GLSLLivenessContext::_addDecorations(Kind kind, IRFunc* func) { // We might(?) want to add a decoration saying this is GLSL specific, but at this point // we can only be in GLSL dependent IR. // // m_builder.addTargetDecoration(); // We don't need to explictly add the "GL_EXT_spirv_intrinsics" // as it will be added on the GLSL emit, with the SPIRVOpDecoration is hit const auto& entry = m_entries[Index(kind)]; if (entry.m_nameHintLiteral) { m_builder.addNameHintDecoration(func, entry.m_nameHintLiteral); } m_builder.addDecoration(func, kIROp_SPIRVOpDecoration, entry.m_spirvOpLiteral); } IRType* GLSLLivenessContext::_getReferencedType(IRInst* referenced) { auto type = referenced->getDataType(); if (type->getOp() == kIROp_PtrType) { type = static_cast(type)->getValueType(); } return type; } void GLSLLivenessContext::_replaceMarker(IRLiveRangeMarker* markerInst) { const auto kind = getKind(markerInst->getOp()); auto& entry = m_entries[Index(kind)]; IRInst* referenced = markerInst->getReferenced(); IRType* referencedType = _getReferencedType(referenced); IRFunc* func = nullptr; if (IRFunc** funcPtr = entry.m_funcs.tryGetValue(referencedType)) { func = *funcPtr; } else { // We didn't find a function for the type, so lets create one. It has a signature of // // void func(ref ReferencedType target, int sizeInBytes) IRType* paramTypes[] = { m_builder.getRefParamType( referencedType, AddressSpace::Generic), ///< Use a reference to the referenced type m_spirvIntLiteralType, ///< The size type }; func = m_builder.createFunc(); auto funcType = m_builder.getFuncType(SLANG_COUNT_OF(paramTypes), paramTypes, m_builder.getVoidType()); m_builder.setDataType(func, funcType); // Add any decorations to the new function _addDecorations(kind, func); // Add to the map entry.m_funcs.add(referencedType, func); } SLANG_ASSERT(func); // Create a call to the function in the form of... // func(referencedItem, 0); // As per the SPIR-V documentation around the OpLifetimeStart/OpLifetimeEnd // https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#OpLifetimeStart // // If the type is known the size should be passed as 0 IRInst* args[] = { referenced, m_zeroIntLiteral, }; // Set the location at the marker to add the call m_builder.setInsertLoc(IRInsertLoc::after(markerInst)); m_builder.emitCallInst(m_builder.getVoidType(), func, SLANG_COUNT_OF(args), args); // We don't need the marker anymore markerInst->removeAndDeallocate(); } void GLSLLivenessContext::processModule() { // Find all of the liveness marker insts // // This is done prior to processing, so we don't need to worry about traversal when // instructions are replaced. IRModuleInst* moduleInst = m_module->getModuleInst(); for (IRInst* child : moduleInst->getChildren()) { // We want to find all of the functions, and process them if (auto funcInst = as(child)) { // Then we want to look through their definition // inserting instructions that mark the liveness start/end _processFunction(funcInst); } } // If we didn't find any liveness marker instructions then we are done if (!m_markerInsts.getCount()) { return; } // Int type that is SPIRV Literal (ie prefixed with spirv_literal) m_spirvIntLiteralType = m_builder.getSPIRVLiteralType(m_builder.getIntType()); // Zero value literal m_zeroIntLiteral = m_builder.getIntValue(m_builder.getIntType(), 0); // We don't need to explicitly add this decoration because it will be added as needed on GLSL // emit m_extensionStringLiteral = // m_builder.getStringValue(UnownedStringSlice::fromLiteral("GL_EXT_spirv_intrinsics")); // Set up some values that will be needed on instructions // The op values are from the SPIR-V spec // https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#OpLifetimeStart { auto& entry = m_entries[Index(Kind::Start)]; entry.m_nameHintLiteral = m_builder.getStringValue(UnownedStringSlice::fromLiteral("livenessStart")); entry.m_spirvOpLiteral = m_builder.getIntValue(m_builder.getIntType(), 256); } { auto& entry = m_entries[Index(Kind::End)]; entry.m_nameHintLiteral = m_builder.getStringValue(UnownedStringSlice::fromLiteral("livenessEnd")); entry.m_spirvOpLiteral = m_builder.getIntValue(m_builder.getIntType(), 257); } // Iterate across instructions, replacing with a call to a generated function (one that just is // a declaration defining the SPIR-V op) for (auto markerInst : m_markerInsts) { _replaceMarker(markerInst); } } } // namespace void applyGLSLLiveness(IRModule* module) { GLSLLivenessContext context(module); context.processModule(); } } // namespace Slang