diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 15 | ||||
| -rw-r--r-- | source/slang/slang-emit-spirv-ops-debug-info-ext.h | 86 | ||||
| -rw-r--r-- | source/slang/slang-emit-spirv.cpp | 212 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-ir-autodiff-fwd.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-ir-autodiff-rev.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-ir-autodiff.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-ir-inline.cpp | 258 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 8 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-pass-base.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 74 | ||||
| -rw-r--r-- | source/slang/slang-ir-strip-debug-info.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-ir-validate.cpp | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 52 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 25 |
15 files changed, 704 insertions, 51 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 18dbcf1cd..56668d092 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -3168,6 +3168,11 @@ void CLikeSourceEmitter::_emitInst(IRInst* inst) case kIROp_DebugLine: case kIROp_DebugVar: case kIROp_DebugValue: + case kIROp_DebugInlinedAt: + case kIROp_DebugScope: + case kIROp_DebugNoScope: + case kIROp_DebugInlinedVariable: + case kIROp_DebugFunction: break; case kIROp_Unmodified: @@ -5151,6 +5156,16 @@ void CLikeSourceEmitter::ensureGlobalInst( return; case kIROp_ThisType: return; + case kIROp_DebugInlinedAt: + case kIROp_DebugScope: + case kIROp_DebugNoScope: + case kIROp_DebugFunction: + case kIROp_DebugVar: + case kIROp_DebugLine: + case kIROp_DebugSource: + case kIROp_DebugValue: + case kIROp_DebugInlinedVariable: + return; default: break; } 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 41c6e1faa..d8826afdc 100644 --- a/source/slang/slang-emit-spirv-ops-debug-info-ext.h +++ b/source/slang/slang-emit-spirv-ops-debug-info-ext.h @@ -428,13 +428,75 @@ SpvInst* emitOpDebugScope( IRInst* inst, const T& idResultType, SpvInst* set, - SpvInst* scope) + SpvInst* scope, + SpvInst* inlinedAt = nullptr) { static_assert(isSingular<T>); + if (inlinedAt) + { + return emitInst( + parent, + inst, + SpvOpExtInst, + idResultType, + kResultID, + set, + SpvWord(23), + scope, + inlinedAt); + } return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(23), scope); } template<typename T> +SpvInst* emitOpDebugNoScope( + SpvInstParent* parent, + IRInst* inst, + const T& idResultType, + SpvInst* set) +{ + static_assert(isSingular<T>); + return emitInst(parent, inst, SpvOpExtInst, idResultType, kResultID, set, SpvWord(24)); +} + +template<typename T> +SpvInst* emitOpDebugInlinedAt( + SpvInstParent* parent, + IRInst* inst, + const T& idResultType, + SpvInst* set, + IRInst* line, + SpvInst* scope, + SpvInst* inlinedAt = nullptr) +{ + static_assert(isSingular<T>); + if (inlinedAt) + { + return emitInst( + parent, + inst, + SpvOpExtInst, + idResultType, + kResultID, + set, + SpvWord(25), + line, + scope, + inlinedAt); + } + return emitInst( + parent, + inst, + SpvOpExtInst, + idResultType, + kResultID, + set, + SpvWord(25), + line, + scope); +} + +template<typename T> SpvInst* emitOpDebugLocalVariable( SpvInstParent* parent, IRInst* inst, @@ -484,6 +546,28 @@ SpvInst* emitOpDebugLocalVariable( flags); } +template<typename T> +SpvInst* emitOpDebugInlinedVariable( + SpvInstParent* parent, + IRInst* inst, + const T& idResultType, + SpvInst* set, + IRInst* variable, + IRInst* inlined) +{ + static_assert(isSingular<T>); + return emitInst( + parent, + inst, + SpvOpExtInst, + idResultType, + kResultID, + set, + SpvWord(27), + variable, + inlined); +} + template<typename T, typename Ts> SpvInst* emitOpDebugValue( SpvInstParent* parent, diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp index ad1f1bdf8..bd9b23b2d 100644 --- a/source/slang/slang-emit-spirv.cpp +++ b/source/slang/slang-emit-spirv.cpp @@ -1952,6 +1952,17 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex case kIROp_IndicesType: case kIROp_PrimitivesType: return nullptr; + case kIROp_DebugFunction: + return emitDebugFunction( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + nullptr, + as<IRDebugFunction>(inst), + nullptr); + case kIROp_DebugInlinedAt: + return emitDebugInlinedAt( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + as<IRDebugInlinedAt>(inst)); default: { if (as<IRSPIRVAsmOperand>(inst)) @@ -3026,7 +3037,17 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex } } // DebugInfo. - funcDebugScope = emitDebugFunction(spvBlock, spvFunc, irFunc); + IRDebugFunction* irDebugFunc = nullptr; + if (auto debugFuncDecor = irFunc->findDecoration<IRDebugFuncDecoration>()) + { + irDebugFunc = as<IRDebugFunction>(debugFuncDecor->getDebugFunc()); + } + funcDebugScope = emitDebugFunction( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + spvBlock, + spvFunc, + irDebugFunc, + irFunc->getDataType()); } if (funcDebugScope) { @@ -4083,6 +4104,25 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex operands.getArrayView()); } break; + case kIROp_DebugFunction: + return emitDebugFunction( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + nullptr, + nullptr, + as<IRDebugFunction>(inst), + nullptr); + case kIROp_DebugInlinedAt: + return emitDebugInlinedAt( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + as<IRDebugInlinedAt>(inst)); + case kIROp_DebugScope: + return emitDebugScope(parent, as<IRDebugScope>(inst)); + case kIROp_DebugNoScope: + return emitDebugNoScope(parent); + case kIROp_DebugInlinedVariable: + return emitDebugInlinedVariable( + getSection(SpvLogicalSectionID::ConstantsAndTypes), + as<IRDebugInlinedVariable>(inst)); } if (result) emitDecorations(inst, getID(result)); @@ -7395,6 +7435,136 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex return m_nullDwarfExpr; } + SpvInst* emitDebugScope(SpvInstParent* parent, IRDebugScope* debugScope) + { + auto inlinedAt = ensureInst(debugScope->getInlinedAt()); + if (!inlinedAt) + return nullptr; + + SpvInst* scope = ensureInst(debugScope->getScope()); + if (!scope) + return nullptr; + + return emitOpDebugScope( + parent, + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + scope, + inlinedAt); + } + + + SpvInst* emitDebugFunction( + SpvInstParent* parent, + SpvInst* firstBlock, + SpvInst* spvFunc, + IRDebugFunction* debugFunc, + IRFuncType* debugType) + { + SpvInst* debugFuncInfo = nullptr; + if (debugFunc && m_mapIRInstToSpvInst.tryGetValue(debugFunc, debugFuncInfo)) + { + return debugFuncInfo; + } + + auto scope = findDebugScope(debugFunc); + if (!scope) + return nullptr; + + SpvInst* neededDebugType = nullptr; + if (debugType) + { + neededDebugType = emitDebugType(debugType); + } + else + { + neededDebugType = emitDebugType(as<IRFuncType>(debugFunc->getDebugType())); + } + + IRBuilder builder(debugFunc); + debugFuncInfo = emitOpDebugFunction( + parent, + debugFunc, + m_voidType, + getNonSemanticDebugInfoExtInst(), + debugFunc->getName(), + neededDebugType, + debugFunc->getFile(), + debugFunc->getLine(), + debugFunc->getCol(), + scope, + debugFunc->getName(), + builder.getIntValue(builder.getUIntType(), 0), + debugFunc->getLine()); + + if (firstBlock && spvFunc) + { + emitOpDebugFunctionDefinition( + firstBlock, + nullptr, + m_voidType, + getNonSemanticDebugInfoExtInst(), + debugFuncInfo, + spvFunc); + } + return debugFuncInfo; + } + + SpvInst* emitDebugNoScope(SpvInstParent* parent) + { + return emitOpDebugNoScope(parent, nullptr, m_voidType, getNonSemanticDebugInfoExtInst()); + } + + SpvInst* emitDebugInlinedAt(SpvInstParent* parent, IRDebugInlinedAt* debugInlinedAt) + { + IRInst* lineInst = debugInlinedAt->getLine(); + + SpvInst* scope = nullptr; + if (as<IRDebugFunction>(debugInlinedAt->getDebugFunc())) + { + scope = ensureInst(debugInlinedAt->getDebugFunc()); + } + if (scope == nullptr) + { + scope = findDebugScope(debugInlinedAt); + } + + // If it's not chained to another IRDebugInlinedAt, we don't use this. + SpvInst* inlined = nullptr; + if (as<IRDebugInlinedAt>(debugInlinedAt)->isOuterInlinedPresent()) + { + inlined = ensureInst(debugInlinedAt->getOuterInlinedAt()); + } + + return emitOpDebugInlinedAt( + parent, + debugInlinedAt, + m_voidType, + getNonSemanticDebugInfoExtInst(), + lineInst, + scope, + inlined); + } + + SpvInst* emitDebugInlinedVariable( + SpvInstParent* parent, + IRDebugInlinedVariable* debugInlinedVar) + { + // Get the operands from the IRDebugInlinedVariable instruction + IRInst* variable = debugInlinedVar->getVariable(); + IRInst* inlinedAt = debugInlinedVar->getInlinedAt(); + + // Emit the OpDebugInlinedVariable instruction + return emitOpDebugInlinedVariable( + parent, + debugInlinedVar, + m_voidType, + getNonSemanticDebugInfoExtInst(), + variable, + inlinedAt); + } + bool translateIRAccessChain( IRBuilder& builder, IRInst* baseType, @@ -7860,46 +8030,6 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex 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-emit.cpp b/source/slang/slang-emit.cpp index ac6336e9c..9410a8f45 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -350,6 +350,10 @@ void calcRequiredLoweringPassSet( case kIROp_DebugLine: case kIROp_DebugLocationDecoration: case kIROp_DebugSource: + case kIROp_DebugInlinedAt: + case kIROp_DebugScope: + case kIROp_DebugNoScope: + case kIROp_DebugFunction: result.debugInfo = true; break; case kIROp_ResultType: diff --git a/source/slang/slang-ir-autodiff-fwd.cpp b/source/slang/slang-ir-autodiff-fwd.cpp index febd30d7e..df690a4e2 100644 --- a/source/slang/slang-ir-autodiff-fwd.cpp +++ b/source/slang/slang-ir-autodiff-fwd.cpp @@ -2184,6 +2184,11 @@ InstPair ForwardDiffTranscriber::transcribeInstImpl(IRBuilder* builder, IRInst* case kIROp_DebugLine: case kIROp_DebugVar: case kIROp_DebugValue: + case kIROp_DebugInlinedAt: + case kIROp_DebugScope: + case kIROp_DebugNoScope: + case kIROp_DebugInlinedVariable: + case kIROp_DebugFunction: case kIROp_GetArrayLength: case kIROp_SizeOf: case kIROp_AlignOf: diff --git a/source/slang/slang-ir-autodiff-rev.cpp b/source/slang/slang-ir-autodiff-rev.cpp index 519f796b4..773a7741f 100644 --- a/source/slang/slang-ir-autodiff-rev.cpp +++ b/source/slang/slang-ir-autodiff-rev.cpp @@ -275,6 +275,11 @@ InstPair BackwardDiffTranscriberBase::transcribeInstImpl(IRBuilder* builder, IRI case kIROp_WrapExistential: case kIROp_MakeExistential: case kIROp_MakeExistentialWithRTTI: + case kIROp_DebugInlinedAt: + case kIROp_DebugScope: + case kIROp_DebugNoScope: + case kIROp_DebugInlinedVariable: + case kIROp_DebugFunction: return transcribeNonDiffInst(builder, origInst); case kIROp_StructKey: diff --git a/source/slang/slang-ir-autodiff.cpp b/source/slang/slang-ir-autodiff.cpp index 6ad8c5dbb..c8dc3b480 100644 --- a/source/slang/slang-ir-autodiff.cpp +++ b/source/slang/slang-ir-autodiff.cpp @@ -965,7 +965,9 @@ IRInst* DifferentialPairTypeBuilder::lowerDiffPairType(IRBuilder* builder, IRTyp if (as<IRThisType>(primalType) || as<IRAssociatedType>(primalType)) { List<IRInterfaceType*> constraintTypes; - constraintTypes.add(this->commonDiffPairInterface); + auto diffPairInterfaceType = + cast<IRInterfaceType>(getOrCreateCommonDiffPairInterface(builder)); + constraintTypes.add(diffPairInterfaceType); return builder->getAssociatedType(constraintTypes.getArrayView()); } diff --git a/source/slang/slang-ir-inline.cpp b/source/slang/slang-ir-inline.cpp index 73cf6eb39..8575330f8 100644 --- a/source/slang/slang-ir-inline.cpp +++ b/source/slang/slang-ir-inline.cpp @@ -3,6 +3,8 @@ #include "../core/slang-performance-profiler.h" #include "slang-ir-ssa-simplification.h" +#include "slang-ir-util.h" + // This file provides general facilities for inlining function calls. // @@ -290,6 +292,124 @@ struct InliningPassBase return true; } + // Find an existing debug function for the given function + IRInst* findExistingDebugFunc(IRFunc* func) + { + if (auto debugFuncDecor = func->findDecoration<IRDebugFuncDecoration>()) + { + return debugFuncDecor->getDebugFunc(); + } + return nullptr; + } + + struct DebugInlineInfo + { + IRInst* newDebugInlinedAt = nullptr; + IRInst* calleeDebugFunc = nullptr; + }; + + // Sets up the initial debug information structures required *before* inlining a call site. + // + // This function performs the following steps: + // 1. Checks if the callee function has associated debug location information. + // 2. Obtain the call inst's existing debug information. + // 3. Finds the last `IRDebugLine` preceding the `call` instruction to determine the source + // location (line, col, file) of the call site. + // 4. Emits an `IRDebugInlinedAt` instruction with outer set to callDebugInlinedAt. + // 5. Inserts the newly created `IRDebugInlinedAt` instruction immediately *before* the `call` + // instruction. + DebugInlineInfo emitCalleeDebugInlinedAt(IRCall* call, IRFunc* callee, IRBuilder& builder) + { + IRDebugLine* lastDebugLine = nullptr; + IRInst* newDebugInlinedAt = nullptr; + IRInst* callerDebugFunc = nullptr; + IRInst* calleeDebugFunc = nullptr; + + if (!callee->findDecoration<IRDebugLocationDecoration>()) + { + return DebugInlineInfo(); + } + else + { + // Check if the call inst is part of an existing scope. If yes, then we restore + // that scope after the inlining of callee. This case can occur when we have out of + // order inlining. See forceinline-basic-block-inline-order.slang test for that use + // case. If we are travesing back the call inst and if we find a DebugNoScope, it means + // that there's another function that was inlined. We don't want that scope. If the call + // inst truly belongs to another DebugScope, then we should hit a DebugScope inst + // *before* we see a DebugNoScope + IRDebugScope* callDebugScope = nullptr; + builder.setInsertAfter(call); + for (IRInst* inst = call->getPrevInst(); inst; inst = inst->getPrevInst()) + { + if (as<IRDebugNoScope>(inst)) + { + break; + } + if (as<IRDebugScope>(inst)) + { + callDebugScope = as<IRDebugScope>(inst); + builder.emitDebugScope( + callDebugScope->getScope(), + callDebugScope->getInlinedAt()); + break; + } + } + if (!callDebugScope) + { + builder.emitDebugNoScope(); + } + + IRDebugInlinedAt* callDebugInlinedAt = nullptr; + for (IRInst* inst = call->getPrevInst(); inst; inst = inst->getPrevInst()) + { + if (as<IRDebugNoScope>(inst)) + { + break; + } + if (as<IRDebugInlinedAt>(inst)) + { + callDebugInlinedAt = as<IRDebugInlinedAt>(inst); + break; + } + } + + // Find the last IRDebugLine to extract debug info. + for (IRInst* inst = call->getPrevInst(); inst; inst = inst->getPrevInst()) + { + if (auto debugLine = as<IRDebugLine>(inst)) + { + lastDebugLine = debugLine; + break; + } + } + + if (!lastDebugLine) + return DebugInlineInfo(); + + calleeDebugFunc = findExistingDebugFunc(callee); + + // The caller func is the right lexical scope needed for nsight to show where + // the function is getting inlined inside. + auto callerFunc = getParentFunc(call); + callerDebugFunc = findExistingDebugFunc(callerFunc); + + builder.setInsertBefore(call); + newDebugInlinedAt = builder.emitDebugInlinedAt( + lastDebugLine->getLineStart(), + lastDebugLine->getColStart(), + lastDebugLine->getSource(), + callerDebugFunc, + callDebugInlinedAt); + + if (newDebugInlinedAt && calleeDebugFunc) + { + return DebugInlineInfo{newDebugInlinedAt, calleeDebugFunc}; + } + } + return DebugInlineInfo(); + } + /// Inline the given `callSite`, which is assumed to have been validated void inlineCallSite(CallSiteInfo const& callSite) { @@ -481,7 +601,9 @@ struct InliningPassBase void inlineSingleBlockFuncBody( CallSiteInfo const& callSite, IRCloneEnv* env, - IRBuilder* builder) + IRBuilder* builder, + IRInst* newDebugInlinedAt, + IRInst* calleeDebugFunc) { auto call = callSite.call; auto callee = callSite.callee; @@ -495,18 +617,22 @@ struct InliningPassBase // them into the same basic block as the `call`. // builder->setInsertBefore(call); + IRInst* calleeDebugScope = nullptr; + if (calleeDebugFunc && newDebugInlinedAt) + { + calleeDebugScope = builder->emitDebugScope(calleeDebugFunc, newDebugInlinedAt); + } // Along the way, we will detect any `return` instruction, // and remember the (clone of the) returned value. // IRInst* returnVal = nullptr; - + List<IRDebugInlinedAt*> debugInlinedInsts; for (auto inst : firstBlock->getChildren()) { switch (inst->getOp()) { default: - // In the common case we just clone the instruction as-is _cloneInstWithSourceLoc(callSite, env, builder, inst); break; @@ -524,9 +650,40 @@ struct InliningPassBase SLANG_ASSERT(!returnVal); returnVal = findCloneForOperand(env, inst->getOperand(0)); break; + + case kIROp_DebugNoScope: + { + if (calleeDebugScope) + _cloneInstWithSourceLoc(callSite, env, builder, calleeDebugScope); + break; + } + + case kIROp_DebugInlinedAt: + { + auto clonedInst = _cloneInstWithSourceLoc(callSite, env, builder, inst); + if (!as<IRDebugInlinedAt>(clonedInst)->isOuterInlinedPresent()) + debugInlinedInsts.add(as<IRDebugInlinedAt>(clonedInst)); + break; + } + } + } + // For any debugInlinedAt without an outerinlinedAt, emit a new debugInlinedAt with the + // outer set, and delete the older debugInlinedAt + for (auto inst : debugInlinedInsts) + { + if (newDebugInlinedAt && !inst->isOuterInlinedPresent()) + { + builder->setInsertAfter(inst); + auto newInlinedAt = builder->emitDebugInlinedAt( + inst->getLine(), + inst->getCol(), + inst->getFile(), + inst->getDebugFunc(), + newDebugInlinedAt); + inst->replaceUsesWith(newInlinedAt); + inst->removeAndDeallocate(); } } - // We are going to remove the original `call` now that the callee // has been inlined, but before we do that we need to replace // all uses of the `call` with whatever value was produced by the @@ -547,10 +704,28 @@ struct InliningPassBase } /// Inline the body of the callee for `callSite`. + // Here is the algorithm for inserting debug information for slang inlined functions: + // 1. Check if the call inst belongs to an existing debug scope and find corresponding + // debugInlinedAt. [callDebugScope, callDebugInlinedAt] + // 1a. If callDebugScope exists, emit this debug Scope* after* the call inst. + // 2. Emit a new DebugInlinedAt inst, with debugFunc of the callee, and outer debugInlinedAt is + // callDebugInlinedAt. [newDebugInlinedAt] + // 2a. If calleDebugScope does not exist, emit debugNoScope after the call inst. + // 3. Clone the callee body. + // 4. For each cloned block, do this: + // 4a.Emit a new DebugScope inst setting the current scope to newDebugInlinedAt. + // [calleeDebugScope] 4b.Emit a DebugNoScope at the end of each block. 4c.If callDebugScope + // exists, do not emit a DebugNoScope for the last block. + // 5. For each cloned debugInlinedAt inst, if its outer inlined at operand is null, set it to + // the new DebugInlinedAt inst inserted at the top of the block. + // 6. For each cloned debugNoScope inst, replace it with calleeDebugScope. (This is because all + // cloned insts are in callee's scope). void inlineFuncBody(CallSiteInfo const& callSite, IRCloneEnv* env, IRBuilder* builder) { - auto call = callSite.call; auto callee = callSite.callee; + auto call = callSite.call; + + auto debugInlineInfo = emitCalleeDebugInlinedAt(call, callee, *builder); // If the callee consists of a single basic block *and* that block // ends with a `return` instruction, then we can apply a simple approach @@ -561,10 +736,35 @@ struct InliningPassBase SLANG_ASSERT(firstBlock); if (!firstBlock->getNextBlock() && as<IRReturn>(firstBlock->getTerminator())) { - inlineSingleBlockFuncBody(callSite, env, builder); + inlineSingleBlockFuncBody( + callSite, + env, + builder, + debugInlineInfo.newDebugInlinedAt, + debugInlineInfo.calleeDebugFunc); return; } + // If the callee has multiple blocks, use the more complex inlining approach + inlineMultipleBlockFuncBody( + callSite, + env, + builder, + debugInlineInfo.newDebugInlinedAt, + debugInlineInfo.calleeDebugFunc); + } + + // Inline the body of the callee for `callSite`, for a callee that has multiple basic blocks. + void inlineMultipleBlockFuncBody( + CallSiteInfo const& callSite, + IRCloneEnv* env, + IRBuilder* builder, + IRInst* newDebugInlinedAt, + IRInst* calleeDebugFunc) + { + auto call = callSite.call; + auto callee = callSite.callee; + // If the callee has any non-trivial control flow (multiple basic blocks // and terminators other than `return`), we will need to split the control // flow of the caller at the block that contains `call`. @@ -680,7 +880,53 @@ struct InliningPassBase } isFirstBlock = false; } + // For each existing debugNoScope inst, replace it with new debug scope we emit. + // For any debugInlinedAt without an outerinlinedAt, emit a new debugInlinedAt with the + // outer set, and delete the older debugInlinedAt + if (newDebugInlinedAt && callee->findDecoration<IRDebugLocationDecoration>()) + { + for (auto calleeBlock : callee->getBlocks()) + { + IRBlock* clonedBlock = as<IRBlock>(env->mapOldValToNew.getValue(calleeBlock)); + setInsertBeforeOrdinaryInst(builder, clonedBlock->getFirstOrdinaryInst()); + builder->emitDebugScope(calleeDebugFunc, newDebugInlinedAt); + List<IRInst*> debugNoScopeToRemove; + List<IRDebugInlinedAt*> debugInlinedAtToProcess; + for (auto inst : clonedBlock->getChildren()) + { + if (as<IRDebugNoScope>(inst)) + { + debugNoScopeToRemove.add(inst); + } + if (auto inlinedAt = as<IRDebugInlinedAt>(inst)) + { + if (!inlinedAt->isOuterInlinedPresent()) + { + debugInlinedAtToProcess.add(inlinedAt); + } + } + } + for (auto inst : debugNoScopeToRemove) + { + builder->setInsertAfter(inst); + builder->emitDebugScope(calleeDebugFunc, newDebugInlinedAt); + inst->removeAndDeallocate(); + } + for (auto inlinedAt : debugInlinedAtToProcess) + { + builder->setInsertAfter(inlinedAt); + auto newInlinedAt = builder->emitDebugInlinedAt( + inlinedAt->getLine(), + inlinedAt->getCol(), + inlinedAt->getFile(), + inlinedAt->getDebugFunc(), + newDebugInlinedAt); + inlinedAt->replaceUsesWith(newInlinedAt); + inlinedAt->removeAndDeallocate(); + } + } + } // If there was a `returnVal` instruction that established // the return value of the inlined function, then that value // should be used to replace any uses of the original call. diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 66505dbd0..5a62c8063 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -1165,6 +1165,9 @@ INST_RANGE(BindingQuery, GetRegisterIndex, GetRegisterSpace) /// Decorates an inst with a debug source location (IRDebugSource, IRIntLit(line), IRIntLit(col)). INST(DebugLocationDecoration, DebugLocation, 3, 0) + /// Decorates a function with a link to its debug function representation + INST(DebugFunctionDecoration, DebugFunction, 1, 0) + /// Recognized by SPIRV-emit pass so we can emit a SPIRV `Block` decoration. INST(SPIRVBlockDecoration, spvBlock, 0, 0) @@ -1356,6 +1359,11 @@ INST(DebugSource, DebugSource, 2, HOISTABLE) INST(DebugLine, DebugLine, 5, 0) INST(DebugVar, DebugVar, 4, 0) INST(DebugValue, DebugValue, 2, 0) +INST(DebugInlinedAt, DebugInlinedAt, 5, 0) +INST(DebugFunction, DebugFunction, 5, 0) +INST(DebugInlinedVariable, DebugInlinedVariable, 2, 0) +INST(DebugScope, DebugScope, 2, 0) +INST(DebugNoScope, DebugNoScope, 1, 0) /* Embedded Precompiled Libraries */ INST(EmbeddedDownstreamIR, EmbeddedDownstreamIR, 2, 0) diff --git a/source/slang/slang-ir-inst-pass-base.h b/source/slang/slang-ir-inst-pass-base.h index 5ce73f617..8b7685652 100644 --- a/source/slang/slang-ir-inst-pass-base.h +++ b/source/slang/slang-ir-inst-pass-base.h @@ -137,6 +137,8 @@ public: case kIROp_GenericSpecializationDictionary: case kIROp_ExistentialFuncSpecializationDictionary: case kIROp_ExistentialTypeSpecializationDictionary: + case kIROp_DebugInlinedAt: + case kIROp_DebugFunction: continue; default: break; diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 5b196cf24..4bef81f47 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -3442,6 +3442,60 @@ struct IRDebugValue : IRInst IRInst* getValue() { return getOperand(1); } }; +struct IRDebugInlinedAt : IRInst +{ + IR_LEAF_ISA(DebugInlinedAt) + IRInst* getLine() { return getOperand(0); } + IRInst* getCol() { return getOperand(1); } + IRInst* getFile() { return getOperand(2); } + IRInst* getDebugFunc() { return getOperand(3); } + IRInst* getOuterInlinedAt() + { + if (operandCount == 5) + return getOperand(4); + return nullptr; + } + void setDebugFunc(IRInst* func) { setOperand(3, func); } + bool isOuterInlinedPresent() { return operandCount == 5; } +}; + +struct IRDebugScope : IRInst +{ + IR_LEAF_ISA(DebugScope) + IRInst* getScope() { return getOperand(0); } + IRInst* getInlinedAt() { return getOperand(1); } + void setInlinedAt(IRInst* inlinedAt) { setOperand(1, inlinedAt); } +}; + +struct IRDebugNoScope : IRInst +{ + IR_LEAF_ISA(DebugNoScope) + IRInst* getScope() { return getOperand(0); } +}; + +struct IRDebugInlinedVariable : IRInst +{ + IR_LEAF_ISA(DebugInlinedVariable) + IRInst* getVariable() { return getOperand(0); } + IRInst* getInlinedAt() { return getOperand(1); } +}; + +struct IRDebugFunction : IRInst +{ + IR_LEAF_ISA(DebugFunction) + IRInst* getName() { return getOperand(0); } + IRInst* getLine() { return getOperand(1); } + IRInst* getCol() { return getOperand(2); } + IRInst* getFile() { return getOperand(3); } + IRInst* getDebugType() { return getOperand(4); } +}; + +struct IRDebugFuncDecoration : IRInst +{ + IR_LEAF_ISA(DebugFunctionDecoration) + IRInst* getDebugFunc() { return getOperand(0); } +}; + struct IRDebugLocationDecoration : IRDecoration { IRInst* getSource() { return getOperand(0); } @@ -3943,6 +3997,21 @@ public: IRInst* col, IRInst* argIndex = nullptr); IRInst* emitDebugValue(IRInst* debugVar, IRInst* debugValue); + IRInst* emitDebugInlinedAt( + IRInst* line, + IRInst* col, + IRInst* file, + IRInst* debugFunc, + IRInst* outerInlinedAt); + IRInst* emitDebugInlinedVariable(IRInst* variable, IRInst* inlinedAt); + IRInst* emitDebugScope(IRInst* scope, IRInst* inlinedAt); + IRInst* emitDebugNoScope(); + IRInst* emitDebugFunction( + IRInst* name, + IRInst* line, + IRInst* col, + IRInst* file, + IRInst* debugType); /// Emit an LiveRangeStart instruction indicating the referenced item is live following this /// instruction @@ -5012,6 +5081,11 @@ public: getIntValue(getUIntType(), col)); } + void addDebugFunctionDecoration(IRInst* value, IRInst* debugFunction) + { + addDecoration(value, kIROp_DebugFunctionDecoration, debugFunction); + } + void addUnsafeForceInlineDecoration(IRInst* value) { addDecoration(value, kIROp_UnsafeForceInlineEarlyDecoration); diff --git a/source/slang/slang-ir-strip-debug-info.cpp b/source/slang/slang-ir-strip-debug-info.cpp index 8b2a07663..124c41a5c 100644 --- a/source/slang/slang-ir-strip-debug-info.cpp +++ b/source/slang/slang-ir-strip-debug-info.cpp @@ -13,6 +13,10 @@ static void findDebugInfo(IRInst* inst, List<IRInst*>& debugInstructions) case kIROp_DebugLine: case kIROp_DebugLocationDecoration: case kIROp_DebugSource: + case kIROp_DebugInlinedAt: + case kIROp_DebugScope: + case kIROp_DebugNoScope: + case kIROp_DebugFunction: debugInstructions.add(inst); break; default: diff --git a/source/slang/slang-ir-validate.cpp b/source/slang/slang-ir-validate.cpp index 11587d600..19de64618 100644 --- a/source/slang/slang-ir-validate.cpp +++ b/source/slang/slang-ir-validate.cpp @@ -244,6 +244,7 @@ void validateIRInstOperand(IRValidateContext* context, IRInst* inst, IRUse* oper switch (inst->getOp()) { case kIROp_DifferentiableTypeDictionaryItem: + case kIROp_DebugScope: return; } // diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 98c0fa471..fb7d752d5 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -3411,6 +3411,53 @@ IRInst* IRBuilder::emitDebugValue(IRInst* debugVar, IRInst* debugValue) args.getBuffer()); } +IRInst* IRBuilder::emitDebugInlinedAt( + IRInst* line, + IRInst* col, + IRInst* file, + IRInst* debugFunc, + IRInst* outerInlinedAt) +{ + if (outerInlinedAt) + { + IRInst* args[] = {line, col, file, debugFunc, outerInlinedAt}; + return emitIntrinsicInst(getVoidType(), kIROp_DebugInlinedAt, 5, args); + } + else + { + IRInst* args[] = {line, col, file, debugFunc}; + return emitIntrinsicInst(getVoidType(), kIROp_DebugInlinedAt, 4, args); + } +} + +IRInst* IRBuilder::emitDebugFunction( + IRInst* name, + IRInst* line, + IRInst* col, + IRInst* file, + IRInst* debugType) +{ + IRInst* args[] = {name, line, col, file, debugType}; + return emitIntrinsicInst(getVoidType(), kIROp_DebugFunction, 5, args); +} + +IRInst* IRBuilder::emitDebugInlinedVariable(IRInst* variable, IRInst* inlinedAt) +{ + IRInst* args[] = {variable, inlinedAt}; + return emitIntrinsicInst(getVoidType(), kIROp_DebugInlinedVariable, 2, args); +} + +IRInst* IRBuilder::emitDebugScope(IRInst* scope, IRInst* inlinedAt) +{ + IRInst* args[] = {scope, inlinedAt}; + return emitIntrinsicInst(getVoidType(), kIROp_DebugScope, 2, args); +} + +IRInst* IRBuilder::emitDebugNoScope() +{ + return emitIntrinsicInst(getVoidType(), kIROp_DebugNoScope, 0, nullptr); +} + IRLiveRangeStart* IRBuilder::emitLiveRangeStart(IRInst* referenced) { // This instruction doesn't produce any result, @@ -7941,8 +7988,8 @@ IRInstList<IRDecoration> IRInst::getDecorations() IRInst* IRInst::getFirstChild() { // The children come after any decorations, - // so if there are any decorations, then the - // first child is right after the last decoration. + // so if there are any decorations, then + // the first child is right after the last decoration. // if (auto lastDecoration = getLastDecoration()) return lastDecoration->getNextInst(); @@ -8462,6 +8509,7 @@ bool IRInst::mightHaveSideEffects(SideEffectAnalysisOptions options) case kIROp_RTTIObject: case kIROp_RTTIType: case kIROp_Func: + case kIROp_DebugFunction: case kIROp_Generic: case kIROp_Var: case kIROp_Param: diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 2271e750e..c58eed1c1 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -11011,6 +11011,31 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> } } + // Add debugfunction decoration and emit debug function. This + // is needed for emitting debug information + auto nameHint = irFunc->findDecoration<IRNameHintDecoration>(); + IRStringLit* nameOperand = nameHint ? as<IRStringLit>(nameHint->getNameOperand()) : nullptr; + if (nameOperand) + { + getBuilder()->setInsertInto(getBuilder()->getModule()->getModuleInst()); + + auto locationDecor = irFunc->findDecoration<IRDebugLocationDecoration>(); + IRInst* debugType = irFunc->getDataType(); + + if (locationDecor && debugType) + { + auto debugFuncCallee = getBuilder()->emitDebugFunction( + nameOperand, + locationDecor->getLine(), + locationDecor->getCol(), + locationDecor->getSource(), + debugType); + + // Add a decoration to link the function to its debug function + getBuilder()->addDecoration(irFunc, kIROp_DebugFunctionDecoration, debugFuncCallee); + } + } + // For convenience, ensure that any additional global // values that were emitted while outputting the function // body appear before the function itself in the list |
