diff options
30 files changed, 922 insertions, 65 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj index 9d0b8d27a..53ceb4caf 100644 --- a/build/visual-studio/slang/slang.vcxproj +++ b/build/visual-studio/slang/slang.vcxproj @@ -399,6 +399,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla <ClInclude Include="..\..\..\source\slang\slang-ir-glsl-liveness.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-init-local-var.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-inline.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-ir-insert-debug-value-store.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-inst-defs.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-inst-pass-base.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-insts.h" />
@@ -619,6 +620,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla <ClCompile Include="..\..\..\source\slang\slang-ir-glsl-liveness.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-init-local-var.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-inline.cpp" />
+ <ClCompile Include="..\..\..\source\slang\slang-ir-insert-debug-value-store.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-layout.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-legalize-array-return-type.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-legalize-mesh-outputs.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters index 4e241f684..9a3f70a7e 100644 --- a/build/visual-studio/slang/slang.vcxproj.filters +++ b/build/visual-studio/slang/slang.vcxproj.filters @@ -285,6 +285,9 @@ <ClInclude Include="..\..\..\source\slang\slang-ir-inline.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-ir-insert-debug-value-store.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-ir-inst-defs.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -941,6 +944,9 @@ <ClCompile Include="..\..\..\source\slang\slang-ir-inline.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang\slang-ir-insert-debug-value-store.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang\slang-ir-layout.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/slang-gfx.h b/slang-gfx.h index 0ce61f9d2..0585de772 100644 --- a/slang-gfx.h +++ b/slang-gfx.h @@ -56,7 +56,7 @@ const uint64_t kTimeoutInfinite = 0xFFFFFFFFFFFFFFFF; enum class StructType { - D3D12DeviceExtendedDesc, D3D12ExperimentalFeaturesDesc + D3D12DeviceExtendedDesc, D3D12ExperimentalFeaturesDesc, SlangSessionExtendedDesc }; // TODO: Rename to Stage @@ -2215,7 +2215,7 @@ public: SlangFloatingPointMode floatingPointMode = SLANG_FLOATING_POINT_MODE_DEFAULT; SlangOptimizationLevel optimizationLevel = SLANG_OPTIMIZATION_LEVEL_DEFAULT; SlangTargetFlags targetFlags = kDefaultTargetFlags; - SlangLineDirectiveMode lineDirectiveMode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT; + SlangLineDirectiveMode lineDirectiveMode = SLANG_LINE_DIRECTIVE_MODE_DEFAULT;\ }; struct ShaderCacheDesc @@ -2718,4 +2718,11 @@ struct D3D12DeviceExtendedDesc uint32_t highestShaderModel = 0; }; +struct SlangSessionExtendedDesc +{ + StructType structType = StructType::SlangSessionExtendedDesc; + uint32_t compilerOptionEntryCount = 0; + slang::CompilerOptionEntry* compilerOptionEntries = nullptr; +}; + } diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index ece552cec..4876026a0 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -531,7 +531,7 @@ namespace Slang protected: ComponentType(Linkage* linkage); - private: + protected: Linkage* m_linkage; CompilerOptionSet m_optionSet; diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 1c01478ed..c7561e611 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -2661,6 +2661,8 @@ void CLikeSourceEmitter::_emitInst(IRInst* inst) case kIROp_DebugSource: case kIROp_DebugLine: + case kIROp_DebugVar: + case kIROp_DebugValue: break; // Insts that needs to be emitted as code blocks. diff --git a/source/slang/slang-emit-spirv-ops-debug-info-ext.h b/source/slang/slang-emit-spirv-ops-debug-info-ext.h index d1ca9665a..3d2a10aab 100644 --- a/source/slang/slang-emit-spirv-ops-debug-info-ext.h +++ b/source/slang/slang-emit-spirv-ops-debug-info-ext.h @@ -24,5 +24,104 @@ SpvInst* emitOpDebugLine(SpvInstParent* parent, IRInst* inst, const T& idResultT return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(103), source, lineStart, lineEnd, colStart, colEnd); } +// https://github.com/KhronosGroup/SPIRV-Registry/blob/main/nonsemantic/NonSemantic.Shader.DebugInfo.100.asciidoc#DebugFunction +template<typename T> +SpvInst* emitOpDebugFunction(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, IRInst* name, SpvInst* type, IRInst* source, IRInst* lineStart, IRInst* colStart, SpvInst* scope, IRInst* linkageName, IRInst* flag, IRInst* scopeLine) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(20), name, type, source, lineStart, colStart, scope, linkageName, flag, scopeLine); +} + +template<typename T> +SpvInst* emitOpDebugFunctionDefinition(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, SpvInst* debugFunc, SpvInst* spvFunc) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(101), debugFunc, spvFunc); +} + +template<typename T, typename Ts> +SpvInst* emitOpDebugTypeFunction(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, IRInst* flags, SpvInst* returnType, const Ts& argTypes) +{ + static_assert(isSingular<T>); + static_assert(isPlural<Ts>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(8), flags, returnType, argTypes); +} + +template<typename T, typename Ts> +SpvInst* emitOpDebugTypeComposite(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, IRInst* name, IRInst* tag, IRInst* source, IRInst* line, IRInst* col, SpvInst* scope, IRInst* linkageName, IRInst* size, IRInst* flags, const Ts& members) +{ + static_assert(isSingular<T>); + static_assert(isPlural<Ts>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(10), name, tag, source, line, col, scope, linkageName, size, flags, members); +} + +template<typename T> +SpvInst* emitOpDebugTypeMember(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, IRInst* name, SpvInst* type, IRInst* source, IRInst* line, IRInst* col, IRInst* offset, IRInst* size, IRInst* flags) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(11), name, type, source, line, col, offset, size, flags); +} + +template<typename T> +SpvInst* emitOpDebugTypeArray(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, SpvInst* baseType, IRInst* elementCount) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(5), baseType, elementCount); +} + +template<typename T> +SpvInst* emitOpDebugTypeBasic(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, IRInst* name, IRInst* size, IRInst* encoding, IRInst* flags) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(2), name, size, encoding, flags); +} + +template<typename T> +SpvInst* emitOpDebugTypeVector(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, SpvInst* baseType, IRInst* elementCount) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(6), baseType, elementCount); +} + +template<typename T> +SpvInst* emitOpDebugTypeMatrix(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, SpvInst* vectorType, IRInst* vectorCount, IRInst* columnMajor) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(108), vectorType, vectorCount, columnMajor); +} + +template<typename T> +SpvInst* emitOpDebugScope(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, SpvInst* scope) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(23), scope); +} + +template<typename T> +SpvInst* emitOpDebugLocalVariable(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, IRInst* name, SpvInst* type, IRInst* source, IRInst* line, IRInst* col, SpvInst* scope, IRInst* flags, IRInst* argIndex) +{ + static_assert(isSingular<T>); + if (argIndex) + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(26), name, type, source, line, col, scope, flags, argIndex); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(26), name, type, source, line, col, scope, flags); +} + +template<typename T, typename Ts> +SpvInst* emitOpDebugValue(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, IRInst* localVar, IRInst* value, SpvInst* expression, const Ts& indices) +{ + static_assert(isSingular<T>); + static_assert(isPlural<Ts>); + + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(29), localVar, value, expression, indices); +} + +template<typename T, typename Ts> +SpvInst* emitOpDebugExpression(SpvInstParent* parent, IRInst* inst, const T& idResultType, SpvInst* set, const Ts& operations) +{ + static_assert(isSingular<T>); + static_assert(isPlural<Ts>); + + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(31), operations); +} #endif // SLANG_IN_SPIRV_EMIT_CONTEXT diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp index 5761d3ec4..64ef469ef 100644 --- a/source/slang/slang-emit-spirv.cpp +++ b/source/slang/slang-emit-spirv.cpp @@ -478,6 +478,8 @@ struct SPIRVEmitContext OrderedHashSet<IRPtrTypeBase*> m_forwardDeclaredPointers; + SpvInst* m_nullDwarfExpr = nullptr; + // A hash set to prevent redecorating the same spv inst. HashSet<SpvId> m_decoratedSpvInsts; @@ -601,6 +603,9 @@ struct SPIRVEmitContext { for (auto parent = inst; parent; parent = parent->getParent()) { + if (!as<IRFunc>(parent) && !as<IRModuleInst>(parent)) + continue; + SpvInst* spvInst = nullptr; if (m_mapIRInstToSpvDebugInst.tryGetValue(parent, spvInst)) return spvInst; @@ -1259,6 +1264,8 @@ struct SPIRVEmitContext ); } + IRInst* m_defaultDebugSource = nullptr; + Dictionary<UnownedStringSlice, SpvInst*> m_extensionInsts; SpvInst* ensureExtensionDeclaration(UnownedStringSlice name) { @@ -1592,6 +1599,8 @@ struct SPIRVEmitContext debugSource->getFileName(), debugSource->getSource()); auto moduleInst = inst->getModule()->getModuleInst(); + if (!m_defaultDebugSource) + m_defaultDebugSource = debugSource; if (!m_mapIRInstToSpvDebugInst.containsKey(moduleInst)) { IRBuilder builder(inst); @@ -2219,6 +2228,7 @@ struct SPIRVEmitContext // not possible for ordinary instructions within // the blocks in the Slang IR) // + SpvInst* funcDebugScope = nullptr; for( auto irBlock : irFunc->getBlocks() ) { auto spvBlock = emitOpLabel(spvFunc, irBlock); @@ -2234,6 +2244,13 @@ struct SPIRVEmitContext emitLocalInst(spvBlock, inst); } } + // DebugInfo. + funcDebugScope = emitDebugFunction(spvBlock, spvFunc, irFunc); + } + + if (funcDebugScope) + { + emitOpDebugScope(spvBlock, nullptr, m_voidType, getNonSemanticDebugInfoExtInst(), funcDebugScope); } // In addition to normal basic blocks, @@ -2546,6 +2563,10 @@ struct SPIRVEmitContext return emitInst(parent, inst, SpvOpSelect, inst->getFullType(), kResultID, OperandsOf(inst)); case kIROp_DebugLine: return emitDebugLine(parent, as<IRDebugLine>(inst)); + case kIROp_DebugVar: + return emitDebugVar(parent, as<IRDebugVar>(inst)); + case kIROp_DebugValue: + return emitDebugValue(parent, as<IRDebugValue>(inst)); case kIROp_GetStringHash: return emitGetStringHash(inst); case kIROp_undefined: @@ -4826,6 +4847,326 @@ struct SPIRVEmitContext debugLine->getColEnd()); } + SpvInst* emitDebugVar(SpvInstParent* parent, IRDebugVar* debugVar) + { + SLANG_UNUSED(parent); + auto scope = findDebugScope(debugVar); + if (!scope) + return nullptr; + IRBuilder builder(debugVar); + auto name = getName(debugVar); + if (!name) + { + static uint32_t uid = 0; + uid++; + name = builder.getStringValue((String("unamed_local_var_") + String(uid)).getUnownedSlice()); + } + auto debugType = emitDebugType(debugVar->getDataType()); + auto spvLocalVar = emitOpDebugLocalVariable(getSection(SpvLogicalSectionID::ConstantsAndTypes), debugVar, m_voidType, getNonSemanticDebugInfoExtInst(), + name, debugType, debugVar->getSource(), debugVar->getLine(), debugVar->getCol(), scope, + builder.getIntValue(builder.getUIntType(), 0), nullptr); + return spvLocalVar; + } + + SpvInst* getDwarfExpr() + { + if (m_nullDwarfExpr) + return m_nullDwarfExpr; + m_nullDwarfExpr = emitOpDebugExpression( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + List<SpvInst*>()); + return m_nullDwarfExpr; + } + + SpvInst* emitDebugValue(SpvInstParent* parent, IRDebugValue* debugValue) + { + IRBuilder builder(debugValue); + + List<SpvInst*> accessChain; + auto type = unwrapAttributedType(debugValue->getDebugVar()->getDataType()); + for (UInt i = 0; i < debugValue->getAccessChainCount(); i++) + { + auto element = debugValue->getAccessChain(i); + if (element->getOp() == kIROp_StructKey) + { + auto key = as<IRStructKey>(element); + auto structType = as<IRStructType>(type); + if (!structType) + return nullptr; + UInt fieldIndex = 0; + for (auto field : structType->getFields()) + { + if (field->getKey() == key) + { + type = unwrapAttributedType(field->getFieldType()); + break; + } + fieldIndex++; + } + accessChain.add(emitIntConstant(fieldIndex, builder.getIntType())); + } + else + { + if (auto arrayType = as<IRArrayTypeBase>(type)) + type = arrayType->getElementType(); + else if (auto vectorType = as<IRVectorType>(type)) + type = vectorType->getElementType(); + else if (auto matrixType = as<IRMatrixType>(type)) + type = builder.getVectorType(matrixType->getElementType(), matrixType->getColumnCount()); + else + return nullptr; + accessChain.add(ensureInst(element)); + } + } + return emitOpDebugValue(parent, debugValue, m_voidType, getNonSemanticDebugInfoExtInst(), + debugValue->getDebugVar(), debugValue->getValue(), getDwarfExpr(), accessChain); + } + + IRInst* getName(IRInst* inst) + { + IRInst* nameOperand = nullptr; + for (auto decor : inst->getDecorations()) + { + if (auto nameHint = as<IRNameHintDecoration>(decor)) + return nameHint->getNameOperand(); + if (auto linkage = as<IRLinkageDecoration>(decor)) + nameOperand = linkage->getMangledNameOperand(); + } + if (nameOperand) + return nameOperand; + + IRBuilder builder(inst); + return builder.getStringValue(toSlice("unamed")); + } + + Dictionary<IRType*, SpvInst*> m_mapTypeToDebugType; + + SpvInst* emitDebugTypeImpl(IRType* type) + { + static const int kUnknownPhysicalLayout = 1 << 17; + + auto scope = findDebugScope(type); + if (!scope) + return ensureInst(m_voidType); + + IRBuilder builder(type); + if (auto funcType = as<IRFuncType>(type)) + { + List<SpvInst*> argTypes; + return emitOpDebugTypeFunction( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + builder.getIntValue(builder.getUIntType(), 0), + ensureInst(m_voidType), + argTypes); + } + + auto name = getName(type); + + if (auto structType = as<IRStructType>(type)) + { + auto loc = structType->findDecoration<IRDebugLocationDecoration>(); + IRInst* source = loc ? loc->getSource() : m_defaultDebugSource; + IRInst* line = loc ? loc->getLine() : builder.getIntValue(builder.getUIntType(), 0); + IRInst* col = loc ? loc->getCol() : line; + if (!name) + { + static uint32_t uid = 0; + uid++; + name = builder.getStringValue((String("unamed_type_") + String(uid)).getUnownedSlice()); + } + IRSizeAndAlignment structSizeAlignment; + getNaturalSizeAndAlignment(m_targetProgram->getOptionSet(), type, &structSizeAlignment); + + List<SpvInst*> members; + for (auto field : structType->getFields()) + { + IRIntegerValue offset = 0; + IRSizeAndAlignment sizeAlignment; + getNaturalOffset(m_targetProgram->getOptionSet(), field, &offset); + getNaturalSizeAndAlignment(m_targetProgram->getOptionSet(), field->getFieldType(), &sizeAlignment); + auto memberType = emitOpDebugTypeMember( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + getName(field->getKey()), + emitDebugType(field->getFieldType()), + source, + line, + col, + builder.getIntValue(builder.getUIntType(), offset * 8), + builder.getIntValue(builder.getUIntType(), sizeAlignment.size * 8), + builder.getIntValue(builder.getUIntType(), 0)); + members.add(memberType); + } + return emitOpDebugTypeComposite( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + name, + builder.getIntValue(builder.getUIntType(), 1), // struct + source, + line, + col, + scope, + name, + builder.getIntValue(builder.getUIntType(), structSizeAlignment.size * 8), + builder.getIntValue(builder.getUIntType(), kUnknownPhysicalLayout), + members); + } + + if (auto arrayType = as<IRArrayTypeBase>(type)) + { + auto sizedArrayType = as<IRArrayType>(arrayType); + return emitOpDebugTypeArray(getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + emitDebugType(arrayType->getElementType()), + sizedArrayType + ? builder.getIntValue(builder.getUIntType(), getIntVal(sizedArrayType->getElementCount())) + : builder.getIntValue(builder.getUIntType(), 0)); + } + else if (auto vectorType = as<IRVectorType>(type)) + { + auto elementType = emitDebugType(vectorType->getElementType()); + return emitOpDebugTypeVector( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + elementType, + builder.getIntValue(builder.getUIntType(), getIntVal(vectorType->getElementCount()))); + } + else if (auto matrixType = as<IRMatrixType>(type)) + { + IRInst* count = nullptr; + bool isColumnMajor = false; + IRType* innerVectorType = nullptr; + if (getIntVal(matrixType->getLayout()) == kMatrixLayoutMode_ColumnMajor) + { + innerVectorType = builder.getVectorType(matrixType->getElementType(), matrixType->getRowCount()); + isColumnMajor = true; + count = matrixType->getColumnCount(); + } + else + { + innerVectorType = builder.getVectorType(matrixType->getElementType(), matrixType->getColumnCount()); + count = matrixType->getRowCount(); + } + auto elementType = emitDebugType(innerVectorType); + return emitOpDebugTypeMatrix( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + elementType, + builder.getIntValue(builder.getUIntType(), getIntVal(vectorType->getElementCount())), + builder.getBoolValue(isColumnMajor)); + } + else if (auto basicType = as<IRBasicType>(type)) + { + IRSizeAndAlignment sizeAlignment; + getNaturalSizeAndAlignment(m_targetProgram->getOptionSet(), basicType, &sizeAlignment); + int spvEncoding = 0; + StringBuilder sbName; + getTypeNameHint(sbName, basicType); + switch (type->getOp()) + { + case kIROp_IntType: + case kIROp_Int16Type: + case kIROp_Int64Type: + case kIROp_Int8Type: + case kIROp_IntPtrType: + spvEncoding = 4; // Signed + break; + case kIROp_UIntType: + case kIROp_UInt16Type: + case kIROp_UInt64Type: + case kIROp_UInt8Type: + case kIROp_UIntPtrType: + spvEncoding = 6; // Unsigned + break; + case kIROp_FloatType: + case kIROp_DoubleType: + case kIROp_HalfType: + spvEncoding = 3; // Float + break; + case kIROp_BoolType: + spvEncoding = 2; // boolean + break; + default: + spvEncoding = 0; // Unspecified. + break; + } + return emitOpDebugTypeBasic( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + builder.getStringValue(sbName.getUnownedSlice()), + builder.getIntValue(builder.getUIntType(), sizeAlignment.size * 8), + builder.getIntValue(builder.getUIntType(), spvEncoding), + builder.getIntValue(builder.getUIntType(), kUnknownPhysicalLayout)); + } + else if (as<IRPtrTypeBase>(type)) + { + StringBuilder sbName; + getTypeNameHint(sbName, basicType); + return emitOpDebugTypeBasic( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + builder.getStringValue(sbName.getUnownedSlice()), + builder.getIntValue(builder.getUIntType(), 64), + builder.getIntValue(builder.getUIntType(), 0), + builder.getIntValue(builder.getUIntType(), kUnknownPhysicalLayout)); + } + return ensureInst(m_voidType); + } + + SpvInst* emitDebugType(IRType* type) + { + if (auto debugType = m_mapTypeToDebugType.tryGetValue(type)) + return *debugType; + auto result = emitDebugTypeImpl(type); + m_mapTypeToDebugType[type] = result; + return result; + } + + SpvInst* emitDebugFunction(SpvInst* firstBlock, SpvInst* spvFunc, IRFunc* function) + { + auto scope = findDebugScope(function); + if (!scope) + return nullptr; + auto name = getName(function); + if (!name) + return nullptr; + auto debugLoc = function->findDecoration<IRDebugLocationDecoration>(); + if (!debugLoc) + return nullptr; + auto debugType = emitDebugType(function->getDataType()); + IRBuilder builder(function); + auto debugFunc = emitOpDebugFunction( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, m_voidType, getNonSemanticDebugInfoExtInst(), + name, debugType, debugLoc->getSource(), debugLoc->getLine(), debugLoc->getCol(), scope, name, + builder.getIntValue(builder.getUIntType(), 0), debugLoc->getLine()); + registerDebugInst(function, debugFunc); + + emitOpDebugFunctionDefinition(firstBlock, nullptr, m_voidType, getNonSemanticDebugInfoExtInst(), debugFunc, spvFunc); + + return debugFunc; + } + SpvInst* emitSPIRVAsm(SpvInstParent* parent, IRSPIRVAsm* inst) { SpvInst* last = nullptr; diff --git a/source/slang/slang-ir-autodiff-fwd.cpp b/source/slang/slang-ir-autodiff-fwd.cpp index 334d4e678..546d24a4d 100644 --- a/source/slang/slang-ir-autodiff-fwd.cpp +++ b/source/slang/slang-ir-autodiff-fwd.cpp @@ -1920,6 +1920,9 @@ InstPair ForwardDiffTranscriber::transcribeInstImpl(IRBuilder* builder, IRInst* case kIROp_GetSequentialID: case kIROp_GetStringHash: case kIROp_SPIRVAsm: + case kIROp_DebugLine: + case kIROp_DebugVar: + case kIROp_DebugValue: return transcribeNonDiffInst(builder, origInst); // A call to createDynamicObject<T>(arbitraryData) cannot provide a diff value, diff --git a/source/slang/slang-ir-autodiff-rev.cpp b/source/slang/slang-ir-autodiff-rev.cpp index ed70862d1..9a69d9aa8 100644 --- a/source/slang/slang-ir-autodiff-rev.cpp +++ b/source/slang/slang-ir-autodiff-rev.cpp @@ -927,16 +927,16 @@ namespace Slang { // Create dOut param. auto diffParam = builder->emitParam(diffType); - copyNameHintDecoration(diffParam, fwdParam); + copyNameHintAndDebugDecorations(diffParam, fwdParam); result.propagateFuncParams.add(diffParam); primalRefReplacement = builder->emitParam(builder->getOutType(primalType)); - copyNameHintDecoration(primalRefReplacement, fwdParam); + copyNameHintAndDebugDecorations(primalRefReplacement, fwdParam); // Create a local var for read access in pre-transpose code. // This will the var from which we will fetch the final resulting derivative // after transposition. auto tempVar = nextBlockBuilder.emitVar(diffType); - copyNameHintDecoration(tempVar, fwdParam); + copyNameHintAndDebugDecorations(tempVar, fwdParam); result.propagateFuncSpecificPrimalInsts.add(tempVar); // Initialize the var with input diff param at start. @@ -953,13 +953,13 @@ namespace Slang else { primalRefReplacement = builder->emitParam(outType); - copyNameHintDecoration(primalRefReplacement, fwdParam); + copyNameHintAndDebugDecorations(primalRefReplacement, fwdParam); } result.primalFuncParams.add(primalRefReplacement); // Create a local var for the out param for the primal part of the prop func. auto tempPrimalVar = nextBlockBuilder.emitVar(outType->getValueType()); - copyNameHintDecoration(tempPrimalVar, fwdParam); + copyNameHintAndDebugDecorations(tempPrimalVar, fwdParam); result.mapPrimalSpecificParamToReplacementInPropFunc[primalRefReplacement] = tempPrimalVar; instsToRemove.add(fwdParam); @@ -979,12 +979,12 @@ namespace Slang // Create an in param for the prop func. auto propParam = builder->emitParam(inoutType->getValueType()); - copyNameHintDecoration(propParam, fwdParam); + copyNameHintAndDebugDecorations(propParam, fwdParam); result.propagateFuncParams.add(propParam); // Create a local var for the out param for the primal part of the prop func. auto tempPrimalVar = nextBlockBuilder.emitVar(inoutType->getValueType()); - copyNameHintDecoration(tempPrimalVar, fwdParam); + copyNameHintAndDebugDecorations(tempPrimalVar, fwdParam); result.propagateFuncSpecificPrimalInsts.add(tempPrimalVar); auto storeInst = nextBlockBuilder.emitStore(tempPrimalVar, propParam); @@ -1013,11 +1013,11 @@ namespace Slang // Create inout version. auto inoutDiffPairType = builder->getInOutType(diffPairType); primalRefReplacement = builder->emitParam(primalType); - copyNameHintDecoration(primalRefReplacement, fwdParam); + copyNameHintAndDebugDecorations(primalRefReplacement, fwdParam); result.primalFuncParams.add(primalRefReplacement); auto propParam = builder->emitParam(inoutDiffPairType); - copyNameHintDecoration(propParam, fwdParam); + copyNameHintAndDebugDecorations(propParam, fwdParam); result.propagateFuncParams.add(propParam); // A reference to this parameter from the diff blocks should be replaced with a load @@ -1049,11 +1049,11 @@ namespace Slang // Process differentiable inout parameters. auto primalParam = builder->emitParam(builder->getInOutType(primalType)); - copyNameHintDecoration(primalParam, fwdParam); + copyNameHintAndDebugDecorations(primalParam, fwdParam); result.primalFuncParams.add(primalParam); auto diffParam = builder->emitParam(inoutType); - copyNameHintDecoration(diffParam, fwdParam); + copyNameHintAndDebugDecorations(diffParam, fwdParam); result.propagateFuncParams.add(diffParam); // Primal references to this param is the new primal param. @@ -1071,7 +1071,7 @@ namespace Slang // Create a local var for diff read access. auto diffVar = nextBlockBuilder.emitVar(diffType); - copyNameHintDecoration(diffVar, fwdParam); + copyNameHintAndDebugDecorations(diffVar, fwdParam); result.propagateFuncSpecificPrimalInsts.add(diffVar); diffRefReplacement = diffVar; @@ -1084,7 +1084,7 @@ namespace Slang // Create a local var for diff write access. auto diffWriteVar = nextBlockBuilder.emitVar(diffType); result.propagateFuncSpecificPrimalInsts.add(diffWriteVar); - copyNameHintDecoration(diffWriteVar, fwdParam); + copyNameHintAndDebugDecorations(diffWriteVar, fwdParam); // Initialize write var to 0. auto writeStore = nextBlockBuilder.emitStore(diffWriteVar, initDiff); @@ -1094,7 +1094,7 @@ namespace Slang // Create a local var for the primal logic in the propagate func. auto primalVar = nextBlockBuilder.emitVar(primalType); - copyNameHintDecoration(primalVar, fwdParam); + copyNameHintAndDebugDecorations(primalVar, fwdParam); result.propagateFuncSpecificPrimalInsts.add(primalVar); auto initPrimalVal = nextBlockBuilder.emitDifferentialPairGetPrimal(loadedParam); diff --git a/source/slang/slang-ir-insert-debug-value-store.cpp b/source/slang/slang-ir-insert-debug-value-store.cpp new file mode 100644 index 000000000..2fec61f55 --- /dev/null +++ b/source/slang/slang-ir-insert-debug-value-store.cpp @@ -0,0 +1,116 @@ +#include "slang-ir-insert-debug-value-store.h" + +#include "slang-ir.h" +#include "slang-ir-insts.h" +#include "slang-ir-util.h" + +namespace Slang +{ + void insertDebugValueStore(IRFunc* func) + { + IRBuilder builder(func); + Dictionary<IRInst*, IRInst*> mapVarToDebugVar; + auto firstBlock = func->getFirstBlock(); + if (!firstBlock) + return; + auto funcDebugLoc = func->findDecoration<IRDebugLocationDecoration>(); + + List<IRInst*> params; + for (auto param : firstBlock->getParams()) + { + params.add(param); + } + + for (auto param : params) + { + builder.setInsertBefore(firstBlock->getFirstOrdinaryInst()); + auto paramType = param->getDataType(); + bool isRefParam = false; + if (auto outType = as<IROutTypeBase>(paramType)) + { + isRefParam = true; + paramType = outType->getValueType(); + } + auto debugVar = builder.emitDebugVar( + paramType, + funcDebugLoc->getSource(), + funcDebugLoc->getLine(), + funcDebugLoc->getCol()); + copyNameHintAndDebugDecorations(debugVar, param); + + mapVarToDebugVar[param] = debugVar; + + // Store the initial value of the parameter into the debug var. + IRInst* paramVal = nullptr; + if (!isRefParam) + paramVal = param; + else if (as<IRInOutType>(param->getDataType())) + paramVal = builder.emitLoad(param); + if (paramVal) + { + ArrayView<IRInst*> accessChain; + builder.emitDebugValue(debugVar, paramVal, accessChain); + } + } + + for (auto block : func->getBlocks()) + { + IRInst* nextInst = nullptr; + for (auto inst = block->getFirstInst(); inst; inst = nextInst) + { + nextInst = inst->getNextInst(); + if (auto varInst = as<IRVar>(inst)) + { + if (auto debugLoc = varInst->findDecoration<IRDebugLocationDecoration>()) + { + builder.setInsertBefore(varInst); + auto debugVar = builder.emitDebugVar( + tryGetPointedToType(&builder, varInst->getDataType()), + debugLoc->getSource(), + debugLoc->getLine(), + debugLoc->getCol()); + copyNameHintAndDebugDecorations(debugVar, varInst); + mapVarToDebugVar[varInst] = debugVar; + } + } + } + } + + // Collect all stores and insert debug value insts to update debug vars. + for (auto block : func->getBlocks()) + { + for (auto inst = block->getFirstInst(); inst; inst = inst->getNextInst()) + { + if (auto storeInst = as<IRStore>(inst)) + { + List<IRInst*> accessChain; + auto varInst = getRootAddr(storeInst->getPtr(), accessChain); + IRInst* debugVar = nullptr; + if (mapVarToDebugVar.tryGetValue(varInst, debugVar)) + { + builder.setInsertBefore(storeInst); + builder.emitDebugValue(debugVar, storeInst->getVal(), accessChain.getArrayView()); + } + } + } + } + } + + void insertDebugValueStore(IRModule* module) + { + for (auto globalInst : module->getGlobalInsts()) + { + if (auto genericInst = as<IRGeneric>(globalInst)) + { + if (auto func = as<IRFunc>(findGenericReturnVal(genericInst))) + { + insertDebugValueStore(func); + } + } + else if (auto func = as<IRFunc>(globalInst)) + { + insertDebugValueStore(func); + } + } + } +} diff --git a/source/slang/slang-ir-insert-debug-value-store.h b/source/slang/slang-ir-insert-debug-value-store.h new file mode 100644 index 000000000..ec51bba2b --- /dev/null +++ b/source/slang/slang-ir-insert-debug-value-store.h @@ -0,0 +1,10 @@ +#ifndef SLANG_IR_INSERT_DEBUG_VALUE_STORE_H +#define SLANG_IR_INSERT_DEBUG_VALUE_STORE_H + +namespace Slang +{ + struct IRModule; + void insertDebugValueStore(IRModule* module); +} + +#endif // SLANG_IR_INSERT_DEBUG_VALUE_STORE_H diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index cb89b265b..5439a8d5b 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -964,10 +964,12 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) /// Recognized by SPIRV-emit pass so we can emit a SPIRV `BufferBlock` decoration. INST(SPIRVBufferBlockDecoration, spvBufferBlock, 0, 0) + /// Decorates an inst with a debug source location (IRDebugSource, IRIntLit(line), IRIntLit(col)). + INST(DebugLocationDecoration, DebugLocation, 3, 0) + /// Recognized by SPIRV-emit pass so we can emit a SPIRV `Block` decoration. INST(SPIRVBlockDecoration, spvBlock, 0, 0) - INST_RANGE(Decoration, HighLevelDeclDecoration, SPIRVBlockDecoration) // @@ -1111,6 +1113,8 @@ INST(DifferentiableTypeDictionaryItem, DifferentiableTypeDictionaryItem, 0, 0) /* DebugInfo */ INST(DebugSource, DebugSource, 2, HOISTABLE) INST(DebugLine, DebugLine, 5, 0) +INST(DebugVar, DebugVar, 4, 0) +INST(DebugValue, DebugValue, 2, 0) /* Inline assembly */ diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index f1160c9a6..114250bae 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -3006,6 +3006,32 @@ struct IRDebugLine : IRInst IRInst* getColEnd() { return getOperand(4); } }; +struct IRDebugVar : IRInst +{ + IR_LEAF_ISA(DebugVar) + IRInst* getSource() { return getOperand(0); } + IRInst* getLine() { return getOperand(1); } + IRInst* getCol() { return getOperand(2); } +}; + +struct IRDebugValue : IRInst +{ + IR_LEAF_ISA(DebugValue) + IRInst* getDebugVar() { return getOperand(0); } + IRInst* getValue() { return getOperand(1); } + UInt getAccessChainCount() { return getOperandCount() - 2; } + IRInst* getAccessChain(UInt index) { return getOperand(2 + index); } +}; + +struct IRDebugLocationDecoration : IRDecoration +{ + IRInst* getSource() { return getOperand(0); } + IRInst* getLine() { return getOperand(1); } + IRInst* getCol() { return getOperand(2); } + + IR_LEAF_ISA(DebugLocationDecoration) +}; + struct IRSPIRVAsm; struct IRSPIRVAsmOperand : IRInst @@ -3441,6 +3467,8 @@ public: IRInst* emitDebugSource(UnownedStringSlice fileName, UnownedStringSlice source); IRInst* emitDebugLine(IRInst* source, IRIntegerValue lineStart, IRIntegerValue lineEnd, IRIntegerValue colStart, IRIntegerValue colEnd); + IRInst* emitDebugVar(IRType* type, IRInst* source, IRInst* line, IRInst* col); + IRInst* emitDebugValue(IRInst* debugVar, IRInst* debugValue, ArrayView<IRInst*> accessChain); /// Emit an LiveRangeStart instruction indicating the referenced item is live following this instruction IRLiveRangeStart* emitLiveRangeStart(IRInst* referenced); @@ -4367,6 +4395,11 @@ public: addDecoration(value, kIROp_ExternCDecoration); } + void addDebugLocationDecoration(IRInst* value, IRInst* debugSource, IRIntegerValue line, IRIntegerValue col) + { + addDecoration(value, kIROp_DebugLocationDecoration, debugSource, getIntValue(getUIntType(), line), getIntValue(getUIntType(), col)); + } + void addForceInlineDecoration(IRInst* value) { addDecoration(value, kIROp_ForceInlineDecoration); diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp index 6c5ab1223..93715bc36 100644 --- a/source/slang/slang-ir-legalize-types.cpp +++ b/source/slang/slang-ir-legalize-types.cpp @@ -814,6 +814,87 @@ static LegalVal legalizeLoad( } } +static LegalVal legalizeDebugVar(IRTypeLegalizationContext* context, LegalType type, IRDebugVar* originalInst) +{ + // For now we just discard any special part and keep the ordinary part. + + switch (type.flavor) + { + case LegalType::Flavor::simple: + { + auto legalVal = context->builder->emitDebugVar( + type.getSimple(), + originalInst->getSource(), + originalInst->getLine(), + originalInst->getCol()); + copyNameHintAndDebugDecorations(legalVal, originalInst); + return LegalVal::simple(legalVal); + } + case LegalType::Flavor::none: + return LegalVal(); + case LegalType::Flavor::pair: + { + auto pairType = type.getPair(); + auto ordinaryVal = legalizeDebugVar(context, pairType->ordinaryType, originalInst); + return ordinaryVal; + } + case LegalType::Flavor::tuple: + { + auto tupleType = type.getTuple(); + for (auto ee : tupleType->elements) + { + auto innerResult = legalizeDebugVar(context, ee.type, originalInst); + if (innerResult.flavor != LegalVal::Flavor::none) + return innerResult; + } + return LegalVal(); + } + default: + return LegalVal(); + } +} + +static LegalVal legalizeDebugValue(IRTypeLegalizationContext* context, LegalVal debugVar, LegalVal debugValue, IRDebugValue* originalInst) +{ + // For now we just discard any special part and keep the ordinary part. + List<IRInst*> accessChain; + for (UInt i = 0; i < originalInst->getAccessChainCount(); i++) + { + accessChain.add(originalInst->getAccessChain(i)); + } + switch (debugValue.flavor) + { + case LegalType::Flavor::simple: + return LegalVal::simple( + context->builder->emitDebugValue( + debugVar.getSimple(), + debugValue.getSimple(), + accessChain.getArrayView())); + case LegalType::Flavor::none: + return LegalVal(); + case LegalType::Flavor::pair: + { + auto ordinaryVal = legalizeDebugValue(context, debugVar, debugValue.getPair()->ordinaryVal, originalInst); + return ordinaryVal; + } + case LegalType::Flavor::tuple: + { + auto tupleVar = debugVar.getTuple(); + UInt index = 0; + for (auto ee : tupleVar->elements) + { + auto innerResult = legalizeDebugValue(context, debugVar, debugValue.getTuple()->elements[index].val, originalInst); + if (innerResult.flavor != LegalVal::Flavor::none) + return innerResult; + index++; + } + return LegalVal(); + } + default: + return LegalVal(); + } +} + static LegalVal legalizeStore( IRTypeLegalizationContext* context, LegalVal legalPtrVal, @@ -1921,6 +2002,12 @@ static LegalVal legalizeInst( return legalizeCall(context, (IRCall*)inst); case kIROp_Return: return legalizeRetVal(context, args[0], (IRReturn*)inst); + + case kIROp_DebugVar: + return legalizeDebugVar(context, type, (IRDebugVar*)inst); + case kIROp_DebugValue: + return legalizeDebugValue(context, args[0], args[1], (IRDebugValue*)inst); + case kIROp_MakeStruct: return legalizeMakeStruct( context, @@ -1939,6 +2026,7 @@ static LegalVal legalizeInst( return legalizeDefaultConstruct( context, type); + case kIROp_unconditionalBranch: case kIROp_loop: return legalizeUnconditionalBranch(context, args, (IRUnconditionalBranch*)inst); diff --git a/source/slang/slang-ir-spirv-legalize.h b/source/slang/slang-ir-spirv-legalize.h index 1064e5aa0..431c9627d 100644 --- a/source/slang/slang-ir-spirv-legalize.h +++ b/source/slang/slang-ir-spirv-legalize.h @@ -24,13 +24,19 @@ struct SPIRVEmitSharedContext DiagnosticSink* m_sink; const SPIRVCoreGrammarInfo* m_grammarInfo; + IRInst* m_voidType; + SPIRVEmitSharedContext(IRModule* module, TargetProgram* program, DiagnosticSink* sink) : m_irModule(module), m_targetProgram(program), m_targetRequest(program->getTargetReq()), m_sink(sink), m_grammarInfo(&module->getSession()->getSPIRVCoreGrammarInfo()) - {} + { + IRBuilder builder(module); + builder.setInsertInto(module); + m_voidType = builder.getVoidType(); + } SpvSnippet* getParsedSpvSnippet(IRTargetIntrinsicDecoration* intrinsic); }; diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp index f514fea1d..8b39e8b45 100644 --- a/source/slang/slang-ir-util.cpp +++ b/source/slang/slang-ir-util.cpp @@ -266,12 +266,38 @@ String dumpIRToString(IRInst* root, IRDumpOptions options) return sb.toString(); } -void copyNameHintDecoration(IRInst* dest, IRInst* src) +void copyNameHintAndDebugDecorations(IRInst* dest, IRInst* src) { - auto decor = src->findDecoration<IRNameHintDecoration>(); - if (decor) + IRDecoration* nameHintDecoration = nullptr; + IRDecoration* linkageDecoration = nullptr; + IRDecoration* debugLocationDecoration = nullptr; + for (auto decor = src->getFirstDecoration(); decor; decor = decor->getNextDecoration()) { - cloneDecoration(decor, dest); + switch (decor->getOp()) + { + case kIROp_NameHintDecoration: + nameHintDecoration = decor; + break; + case kIROp_ImportDecoration: + case kIROp_ExportDecoration: + linkageDecoration = decor; + break; + case kIROp_DebugLocationDecoration: + debugLocationDecoration = decor; + break; + } + } + if (nameHintDecoration) + { + cloneDecoration(nameHintDecoration, dest); + } + if (linkageDecoration) + { + cloneDecoration(linkageDecoration, dest); + } + if (debugLocationDecoration) + { + cloneDecoration(debugLocationDecoration, dest); } } @@ -557,6 +583,26 @@ IRInst* getRootAddr(IRInst* addr) return addr; } +IRInst* getRootAddr(IRInst* addr, List<IRInst*>& outAccessChain) +{ + for (;;) + { + switch (addr->getOp()) + { + case kIROp_GetElementPtr: + case kIROp_FieldAddress: + outAccessChain.add(addr->getOperand(1)); + addr = addr->getOperand(0); + continue; + default: + break; + } + break; + } + outAccessChain.reverse(); + return addr; +} + // A simple and conservative address aliasing check. bool canAddressesPotentiallyAlias(IRGlobalValueWithCode* func, IRInst* addr1, IRInst* addr2) { diff --git a/source/slang/slang-ir-util.h b/source/slang/slang-ir-util.h index c290f9392..648ba3531 100644 --- a/source/slang/slang-ir-util.h +++ b/source/slang/slang-ir-util.h @@ -167,8 +167,10 @@ inline IRInst* unwrapAttributedType(IRInst* type) IRType* dropNormAttributes(IRType* const t); void getTypeNameHint(StringBuilder& sb, IRInst* type); -void copyNameHintDecoration(IRInst* dest, IRInst* src); +void copyNameHintAndDebugDecorations(IRInst* dest, IRInst* src); IRInst* getRootAddr(IRInst* addrInst); +IRInst* getRootAddr(IRInst* addrInst, List<IRInst*>& outAccessChain); + bool canAddressesPotentiallyAlias(IRGlobalValueWithCode* func, IRInst* addr1, IRInst* addr2); String dumpIRToString( diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index a016679e7..3f10e3d3e 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -3214,13 +3214,28 @@ namespace Slang IRInst* args[] = { source, - getIntValue(getIntType(), lineStart), - getIntValue(getIntType(), lineEnd), - getIntValue(getIntType(), colStart), - getIntValue(getIntType(), colEnd) + getIntValue(getUIntType(), lineStart), + getIntValue(getUIntType(), lineEnd), + getIntValue(getUIntType(), colStart), + getIntValue(getUIntType(), colEnd) }; return emitIntrinsicInst(getVoidType(), kIROp_DebugLine, 5, args); } + IRInst* IRBuilder::emitDebugVar(IRType* type, IRInst* source, IRInst* line, IRInst* col) + { + IRInst* args[] = { source, line, col }; + return emitIntrinsicInst(type, kIROp_DebugVar, 3, args); + } + + IRInst* IRBuilder::emitDebugValue(IRInst* debugVar, IRInst* debugValue, ArrayView<IRInst*> accessChain) + { + List<IRInst*> args; + args.add(debugVar); + args.add(debugValue); + args.addRange(accessChain); + return emitIntrinsicInst(getVoidType(), kIROp_DebugValue, (UInt)args.getCount(), args.getBuffer()); + } + IRLiveRangeStart* IRBuilder::emitLiveRangeStart(IRInst* referenced) { // This instruction doesn't produce any result, @@ -8242,28 +8257,7 @@ namespace Slang bool isDefinition( IRInst* inVal) { - IRInst* val = inVal; - // unwrap any generic declarations to see - // the value they return. - for(;;) - { - // An instruciton marked `[import(...)]` cannot - // be a definition, since it is claiming that - // the actual body comes from another module. - // - if(val->findDecoration<IRImportDecoration>()) - return false; - - auto genericInst = as<IRGeneric>(val); - if(!genericInst) - break; - - auto returnVal = findGenericReturnVal(genericInst); - if(!returnVal) - break; - - val = returnVal; - } + IRInst* val = getResolvedInstForDecorations(inVal); // Some cases of instructions have structural // rules about when they are considered to have @@ -8272,7 +8266,6 @@ namespace Slang switch (val->getOp()) { case kIROp_Func: - case kIROp_Generic: return val->getFirstChild() != nullptr; case kIROp_GlobalConstant: @@ -8285,7 +8278,6 @@ namespace Slang // In all other cases, if we have an instruciton // that has *not* been marked for import, then // we consider it to be a definition. - return true; } diff --git a/source/slang/slang-legalize-types.cpp b/source/slang/slang-legalize-types.cpp index 8ed5a12b7..c8a840ce9 100644 --- a/source/slang/slang-legalize-types.cpp +++ b/source/slang/slang-legalize-types.cpp @@ -2,6 +2,7 @@ #include "slang-legalize-types.h" #include "slang-ir-insts.h" +#include "slang-ir-util.h" #include "slang-mangle.h" namespace Slang @@ -447,11 +448,7 @@ struct TupleTypeBuilder IRBuilder* builder = context->getBuilder(); IRStructType* ordinaryStructType = builder->createStructType(); ordinaryStructType->sourceLoc = originalStructType->sourceLoc; - - if(auto nameHintDecoration = originalStructType->findDecoration<IRNameHintDecoration>()) - { - builder->addNameHintDecoration(ordinaryStructType, nameHintDecoration->getNameOperand()); - } + copyNameHintAndDebugDecorations(ordinaryStructType, originalStructType); // The new struct type will appear right after the original in the IR, // so that we can be sure any instruction that could reference the diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 8b9a3c377..cb9c8fc40 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -17,6 +17,7 @@ #include "slang-ir-autodiff.h" #include "slang-ir-inline.h" #include "slang-ir-insts.h" +#include "slang-ir-insert-debug-value-store.h" #include "slang-ir-check-differentiability.h" #include "slang-ir-missing-return.h" #include "slang-ir-sccp.h" @@ -6298,6 +6299,22 @@ void maybeEmitDebugLine(IRGenContext* context, Stmt* stmt) } } +void maybeAddDebugLocationDecoration(IRGenContext* context, IRInst* inst) +{ + if (!context->includeDebugInfo) + return; + auto sourceView = context->getLinkage()->getSourceManager()->findSourceView(inst->sourceLoc); + if (!sourceView) + return; + auto source = sourceView->getSourceFile(); + IRInst* debugSourceInst = nullptr; + if (context->shared->mapSourceFileToDebugSourceInst.tryGetValue(source, debugSourceInst)) + { + auto humaneLoc = context->getLinkage()->getSourceManager()->getHumaneLoc(inst->sourceLoc, SourceLocType::Emit); + context->irBuilder->addDebugLocationDecoration(inst, debugSourceInst, humaneLoc.line, humaneLoc.column); + } +} + void lowerStmt( IRGenContext* context, Stmt* stmt) @@ -7777,6 +7794,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> LoweredValInfo varVal = createVar(context, varType, decl); + maybeAddDebugLocationDecoration(context, varVal.val); if( auto initExpr = decl->initExpr ) { @@ -8150,6 +8168,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> getSink()->diagnose(decl->loc, Diagnostics::unimplemented, "lower unknown AggType to IR"); return LoweredValInfo::simple(subBuilder->getVoidType()); } + + maybeAddDebugLocationDecoration(context, irAggType); auto finalFinishedVal = finishOuterGenerics(subBuilder, irAggType, outerGeneric); @@ -9043,7 +9063,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> IRFunc* irFunc = subBuilder->createFunc(); addNameHint(subContext, irFunc, decl); addLinkageDecoration(subContext, irFunc, decl); - + maybeAddDebugLocationDecoration(subContext, irFunc); + // Register the value now, to avoid any possible infinite recursion when lowering the body or attributes. context->setGlobalValue(decl, LoweredValInfo::simple(findOuterMostGeneric(irFunc))); @@ -10339,6 +10360,13 @@ RefPtr<IRModule> generateIRForTranslationUnit( // normal `call` + `ifElse`, etc. lowerErrorHandling(module, compileRequest->getSink()); + // Generate DebugValue insts to store values into debug variables, + // if debug symbols are enabled. + if (compileRequest->getLinkage()->m_optionSet.getEnumOption<DebugInfoLevel>(CompilerOptionName::DebugInformation) != DebugInfoLevel::None) + { + insertDebugValueStore(module); + } + // Next, attempt to promote local variables to SSA // temporaries and do basic simplifications. // @@ -10516,11 +10544,12 @@ struct SpecializedComponentTypeIRGenContext : ComponentTypeVisitor linkage = componentType->getLinkage(); session = linkage->getSessionImpl(); - + auto option = linkage->m_optionSet; + option.overrideWith(componentType->getOptionSet()); SharedIRGenContext sharedContextStorage( session, sink, - linkage->m_optionSet.shouldObfuscateCode(), + option.shouldObfuscateCode(), nullptr, linkage ); diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 9a653aded..4b11a5216 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -4728,6 +4728,8 @@ SpecializedComponentType::SpecializedComponentType( , m_specializationInfo(specializationInfo) , m_specializationArgs(specializationArgs) { + m_optionSet.overrideWith(base->getOptionSet()); + m_irModule = generateIRForSpecializedComponentType(this, sink); // We need to account for the fact that a specialized diff --git a/tests/spirv/debug-info.slang b/tests/spirv/debug-info.slang new file mode 100644 index 000000000..dc225f8cc --- /dev/null +++ b/tests/spirv/debug-info.slang @@ -0,0 +1,30 @@ +//TEST:SIMPLE(filecheck=CHECK):-target spirv -entry main -stage compute -g2 -emit-spirv-directly + +struct TestType +{ + float memberA; + float3 memberB; + RWStructuredBuffer<float> memberC; + float getValue() + { + return memberA; + } +} +RWStructuredBuffer<float> result; +void main() +{ + TestType t; + t.memberA = 1.0; + t.memberB = float3(1, 2, 3); + t.memberC = result; + var val = t.getValue(); + result[0] = val + t.memberB.x; +} + +// CHECK: OpExtInst %void {{.*}} DebugExpression +// CHECK: DebugTypeMember +// CHECK: DebugTypeComposite +// CHECK: DebugFunctionDefinition +// CHECK: DebugScope +// CHECK: DebugLine +// CHECK: DebugValue diff --git a/tools/gfx-unit-test/gfx-test-util.cpp b/tools/gfx-unit-test/gfx-test-util.cpp index 748ced5eb..fe06a1745 100644 --- a/tools/gfx-unit-test/gfx-test-util.cpp +++ b/tools/gfx-unit-test/gfx-test-util.cpp @@ -4,7 +4,7 @@ #include <slang-com-ptr.h> #define GFX_ENABLE_RENDERDOC_INTEGRATION 0 - +#define GFX_ENABLE_SPIRV_DEBUG 0 #if GFX_ENABLE_RENDERDOC_INTEGRATION # include "external/renderdoc_app.h" # include <windows.h> @@ -278,10 +278,25 @@ namespace gfx_test gfx::D3D12DeviceExtendedDesc extDesc = {}; extDesc.rootParameterShaderAttributeName = "root"; + + gfx::SlangSessionExtendedDesc slangExtDesc = {}; + Slang::List<slang::CompilerOptionEntry> entries; + slang::CompilerOptionEntry emitSpirvDirectlyEntry; + emitSpirvDirectlyEntry.name = slang::CompilerOptionName::EmitSpirvDirectly; + emitSpirvDirectlyEntry.value.intValue0 = 1; + entries.add(emitSpirvDirectlyEntry); +#if GFX_ENABLE_SPIRV_DEBUG + slang::CompilerOptionEntry debugLevelCompilerOptionEntry; + debugLevelCompilerOptionEntry.name = slang::CompilerOptionName::DebugInformation; + debugLevelCompilerOptionEntry.value.intValue0 = SLANG_DEBUG_INFO_LEVEL_STANDARD; + entries.add(debugLevelCompilerOptionEntry); +#endif + slangExtDesc.compilerOptionEntries = entries.getBuffer(); + slangExtDesc.compilerOptionEntryCount = (uint32_t)entries.getCount(); - deviceDesc.extendedDescCount = 1; - void* extDescPtr = &extDesc; - deviceDesc.extendedDescs = &extDescPtr; + deviceDesc.extendedDescCount = 2; + void* extDescPtrs[2] = { &extDesc, &slangExtDesc }; + deviceDesc.extendedDescs = extDescPtrs; // TODO: We should also set the debug callback // (And in general reduce the differences (and duplication) between diff --git a/tools/gfx/cpu/cpu-device.cpp b/tools/gfx/cpu/cpu-device.cpp index f248cf52a..bd747d998 100644 --- a/tools/gfx/cpu/cpu-device.cpp +++ b/tools/gfx/cpu/cpu-device.cpp @@ -27,6 +27,8 @@ namespace cpu { SLANG_RETURN_ON_FAIL(slangContext.initialize( desc.slang, + desc.extendedDescCount, + desc.extendedDescs, SLANG_SHADER_HOST_CALLABLE, "sm_5_1", makeArray(slang::PreprocessorMacroDesc{ "__CPU__", "1" }).getView())); diff --git a/tools/gfx/cuda/cuda-device.cpp b/tools/gfx/cuda/cuda-device.cpp index 377a5aba2..0fcf9319e 100644 --- a/tools/gfx/cuda/cuda-device.cpp +++ b/tools/gfx/cuda/cuda-device.cpp @@ -142,6 +142,8 @@ SLANG_NO_THROW SlangResult SLANG_MCALL DeviceImpl::initialize(const Desc& desc) { SLANG_RETURN_ON_FAIL(slangContext.initialize( desc.slang, + desc.extendedDescCount, + desc.extendedDescs, SLANG_PTX, "sm_5_1", makeArray(slang::PreprocessorMacroDesc{ "__CUDA_COMPUTE__", "1" }).getView())); diff --git a/tools/gfx/d3d11/d3d11-device.cpp b/tools/gfx/d3d11/d3d11-device.cpp index 20e17b082..148590831 100644 --- a/tools/gfx/d3d11/d3d11-device.cpp +++ b/tools/gfx/d3d11/d3d11-device.cpp @@ -34,6 +34,8 @@ SlangResult DeviceImpl::initialize(const Desc& desc) { SLANG_RETURN_ON_FAIL(slangContext.initialize( desc.slang, + desc.extendedDescCount, + desc.extendedDescs, SLANG_DXBC, "sm_5_0", makeArray(slang::PreprocessorMacroDesc{ "__D3D11__", "1" }).getView())); diff --git a/tools/gfx/d3d12/d3d12-device.cpp b/tools/gfx/d3d12/d3d12-device.cpp index bbf27b4f7..58b13fe41 100644 --- a/tools/gfx/d3d12/d3d12-device.cpp +++ b/tools/gfx/d3d12/d3d12-device.cpp @@ -910,6 +910,8 @@ Result DeviceImpl::initialize(const Desc& desc) } SLANG_RETURN_ON_FAIL(slangContext.initialize( desc.slang, + desc.extendedDescCount, + desc.extendedDescs, compileTarget, profileName, makeArray(slang::PreprocessorMacroDesc{ "__D3D12__", "1" }).getView())); diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp index b5ab14fe8..abb178375 100644 --- a/tools/gfx/open-gl/render-gl.cpp +++ b/tools/gfx/open-gl/render-gl.cpp @@ -2007,6 +2007,8 @@ SLANG_NO_THROW Result SLANG_MCALL GLDevice::initialize(const Desc& desc) { SLANG_RETURN_ON_FAIL(slangContext.initialize( desc.slang, + desc.extendedDescCount, + desc.extendedDescs, SLANG_GLSL, "glsl_440", makeArray( diff --git a/tools/gfx/slang-context.h b/tools/gfx/slang-context.h index 8ae14d4aa..79f39c3e6 100644 --- a/tools/gfx/slang-context.h +++ b/tools/gfx/slang-context.h @@ -10,7 +10,11 @@ namespace gfx public: Slang::ComPtr<slang::IGlobalSession> globalSession; Slang::ComPtr<slang::ISession> session; - Result initialize(const gfx::IDevice::SlangDesc& desc, SlangCompileTarget compileTarget, const char* defaultProfileName, + Result initialize(const gfx::IDevice::SlangDesc& desc, + uint32_t extendedDescCount, + void** extendedDescs, + SlangCompileTarget compileTarget, + const char* defaultProfileName, Slang::ConstArrayView<slang::PreprocessorMacroDesc> additionalMacros) { if (desc.slangGlobalSession) @@ -45,6 +49,17 @@ namespace gfx slangSessionDesc.targets = &targetDesc; slangSessionDesc.targetCount = 1; + for (uint32_t i = 0; i < extendedDescCount; i++) + { + if ((*(StructType*)extendedDescs[i]) == StructType::SlangSessionExtendedDesc) + { + auto extDesc = (SlangSessionExtendedDesc*)extendedDescs[i]; + slangSessionDesc.compilerOptionEntryCount = extDesc->compilerOptionEntryCount; + slangSessionDesc.compilerOptionEntries = extDesc->compilerOptionEntries; + break; + } + } + SLANG_RETURN_ON_FAIL(globalSession->createSession(slangSessionDesc, session.writeRef())); return SLANG_OK; } diff --git a/tools/gfx/vulkan/vk-device.cpp b/tools/gfx/vulkan/vk-device.cpp index 641b50bf6..1b79887ca 100644 --- a/tools/gfx/vulkan/vk-device.cpp +++ b/tools/gfx/vulkan/vk-device.cpp @@ -933,6 +933,8 @@ SlangResult DeviceImpl::initialize(const Desc& desc) SLANG_RETURN_ON_FAIL(slangContext.initialize( desc.slang, + desc.extendedDescCount, + desc.extendedDescs, SLANG_SPIRV, "sm_5_1", makeArray(slang::PreprocessorMacroDesc{ "__VK__", "1" }).getView())); |
