From 6b5653a0efff64358bb73bfe8353f5695e74d29d Mon Sep 17 00:00:00 2001 From: Steve Urquhart Date: Mon, 7 Oct 2024 10:52:12 -0400 Subject: [PATCH] [SPIRV] Emit DebugFunction and Definition for both wrapper and real functions --- tools/clang/lib/SPIRV/SpirvEmitter.cpp | 131 ++++++++++++------ tools/clang/lib/SPIRV/SpirvEmitter.h | 16 ++- .../rich.debug.function.param.hlsl | 2 +- .../CodeGenSPIRV/shader.debug.function.hlsl | 19 ++- 4 files changed, 111 insertions(+), 57 deletions(-) diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 94952b3c34..d661ef8385 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -1442,36 +1442,18 @@ void SpirvEmitter::doFunctionDecl(const FunctionDecl *decl) { // This will allow the entry-point name to be something like // myNamespace::myEntrypointFunc. std::string funcName = getFnName(decl); - std::string debugFuncName = funcName; - + std::string srcFuncName = "src." + funcName; SpirvFunction *func = declIdMapper.getOrRegisterFn(decl); + SpirvFunction *srcFunc = nullptr; auto loc = decl->getLocStart(); auto range = decl->getSourceRange(); RichDebugInfo *info = nullptr; SpirvDebugFunction *debugFunction = nullptr; + SpirvDebugFunction *srcDebugFunction = nullptr; SpirvDebugInstruction *outer_scope = spvContext.getCurrentLexicalScope(); - const auto &sm = astContext.getSourceManager(); if (spirvOptions.debugInfoRich && decl->hasBody()) { - const uint32_t line = sm.getPresumedLineNumber(loc); - const uint32_t column = sm.getPresumedColumnNumber(loc); - info = getOrCreateRichDebugInfo(loc); - - auto *source = info->source; - // Note that info->scopeStack.back() is a lexical scope of the function - // caller. - auto *parentScope = info->compilationUnit; - // TODO: figure out the proper flag based on the function decl. - // using FlagIsPublic for now. - uint32_t flags = 3u; - // The line number in the source program at which the function scope begins. - auto scopeLine = sm.getPresumedLineNumber(decl->getBody()->getLocStart()); - debugFunction = spvBuilder.createDebugFunction(decl, debugFuncName, source, - line, column, parentScope, - "", flags, scopeLine, func); - func->setDebugScope(new (spvContext) SpirvDebugScope(debugFunction)); - - spvContext.pushDebugLexicalScope(info, debugFunction); + debugFunction = emitDebugFunction(decl, func, &info, funcName); } bool isEntry = false; @@ -1480,10 +1462,16 @@ void SpirvEmitter::doFunctionDecl(const FunctionDecl *decl) { const auto &entryInfo = iter->second; if (entryInfo->isEntryFunction) { isEntry = true; - funcName = "src." + funcName; // Create wrapper for the entry function - if (!emitEntryFunctionWrapper(decl, func)) + srcFunc = func; + func = emitEntryFunctionWrapper(decl, debugFunction, func); + if (func == nullptr) return; + + if (spirvOptions.debugInfoRich && decl->hasBody()) { + srcDebugFunction = emitDebugFunction(decl, srcFunc, &info, srcFuncName); + } + // Generate DebugEntryPoint if function definition if (spirvOptions.debugInfoVulkan && debugFunction) { auto *cu = dyn_cast(outer_scope); @@ -1498,9 +1486,16 @@ void SpirvEmitter::doFunctionDecl(const FunctionDecl *decl) { const QualType retType = declIdMapper.getTypeAndCreateCounterForPotentialAliasVar(decl); - spvBuilder.beginFunction(retType, decl->getLocStart(), funcName, - decl->hasAttr(), - decl->hasAttr(), func); + if (srcFunc) { + spvBuilder.beginFunction(retType, decl->getLocStart(), srcFuncName, + decl->hasAttr(), + decl->hasAttr(), srcFunc); + + } else { + spvBuilder.beginFunction(retType, decl->getLocStart(), funcName, + decl->hasAttr(), + decl->hasAttr(), func); + } bool isNonStaticMemberFn = false; if (const auto *memberFn = dyn_cast(decl)) { @@ -1551,7 +1546,9 @@ void SpirvEmitter::doFunctionDecl(const FunctionDecl *decl) { // Add DebugFunctionDefinition if we are emitting // NonSemantic.Shader.DebugInfo.100 debug info - if (spirvOptions.debugInfoVulkan && debugFunction) + if (spirvOptions.debugInfoVulkan && srcDebugFunction) + spvBuilder.createDebugFunctionDef(srcDebugFunction, srcFunc); + else if (spirvOptions.debugInfoVulkan && debugFunction) spvBuilder.createDebugFunctionDef(debugFunction, func); // Process all statments in the body. @@ -13197,11 +13194,17 @@ bool SpirvEmitter::processTessellationShaderAttributes( } bool SpirvEmitter::emitEntryFunctionWrapperForRayTracing( - const FunctionDecl *decl, SpirvFunction *entryFuncInstr) { + const FunctionDecl *decl, SpirvDebugFunction *debugFunction, + SpirvFunction *entryFuncInstr) { // The entry basic block. auto *entryLabel = spvBuilder.createBasicBlock(); spvBuilder.setInsertPoint(entryLabel); + // Add DebugFunctionDefinition if we are emitting + // NonSemantic.Shader.DebugInfo.100 debug info. + if (spirvOptions.debugInfoVulkan && debugFunction) + spvBuilder.createDebugFunctionDef(debugFunction, entryFunction); + // Initialize all global variables at the beginning of the wrapper for (const VarDecl *varDecl : toInitGloalVars) { const auto varInfo = @@ -13464,8 +13467,38 @@ bool SpirvEmitter::processMeshOrAmplificationShaderAttributes( return true; } -bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, - SpirvFunction *entryFuncInstr) { +SpirvDebugFunction *SpirvEmitter::emitDebugFunction(const FunctionDecl *decl, + SpirvFunction *func, + RichDebugInfo **info, + std::string name) { + auto loc = decl->getLocStart(); + const auto &sm = astContext.getSourceManager(); + const uint32_t line = sm.getPresumedLineNumber(loc); + const uint32_t column = sm.getPresumedColumnNumber(loc); + *info = getOrCreateRichDebugInfo(loc); + + SpirvDebugSource *source = (*info)->source; + // Note that info->scopeStack.back() is a lexical scope of the function + // caller. + SpirvDebugInstruction *parentScope = (*info)->compilationUnit; + // TODO: figure out the proper flag based on the function decl. + // using FlagIsPublic for now. + uint32_t flags = 3u; + // The line number in the source program at which the function scope begins. + auto scopeLine = sm.getPresumedLineNumber(decl->getBody()->getLocStart()); + SpirvDebugFunction *debugFunction = + spvBuilder.createDebugFunction(decl, name, source, line, column, + parentScope, "", flags, scopeLine, func); + func->setDebugScope(new (spvContext) SpirvDebugScope(debugFunction)); + + spvContext.pushDebugLexicalScope(*info, debugFunction); + return debugFunction; +} + +SpirvFunction * +SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, + SpirvDebugFunction *debugFunction, + SpirvFunction *entryFuncInstr) { // HS specific attributes uint32_t numOutputControlPoints = 0; SpirvInstruction *outputControlPointIdVal = @@ -13500,7 +13533,10 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, entryInfo->entryFunction = entryFunction; if (spvContext.isRay()) { - return emitEntryFunctionWrapperForRayTracing(decl, entryFuncInstr); + return emitEntryFunctionWrapperForRayTracing(decl, debugFunction, + entryFuncInstr) + ? entryFunction + : nullptr; } // Handle attributes specific to each shader stage if (spvContext.isPS()) { @@ -13509,7 +13545,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, processComputeShaderAttributes(decl); } else if (spvContext.isHS()) { if (!processTessellationShaderAttributes(decl, &numOutputControlPoints)) - return false; + return nullptr; // The input array size for HS is specified in the InputPatch parameter. for (const auto *param : decl->params()) @@ -13521,7 +13557,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, outputArraySize = numOutputControlPoints; } else if (spvContext.isDS()) { if (!processTessellationShaderAttributes(decl, &numOutputControlPoints)) - return false; + return nullptr; // The input array size for HS is specified in the OutputPatch parameter. for (const auto *param : decl->params()) @@ -13532,11 +13568,11 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, // The per-vertex output of DS is not an array. } else if (spvContext.isGS()) { if (!processGeometryShaderAttributes(decl, &inputArraySize)) - return false; + return nullptr; // The per-vertex output of GS is not an array. } else if (spvContext.isMS() || spvContext.isAS()) { if (!processMeshOrAmplificationShaderAttributes(decl, &outputArraySize)) - return false; + return nullptr; } // Go through all parameters and record the declaration of SV_ClipDistance @@ -13552,14 +13588,14 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, for (const auto *param : decl->params()) { if (canActAsInParmVar(param)) if (!declIdMapper.glPerVertex.recordGlPerVertexDeclFacts(param, true)) - return false; + return nullptr; if (canActAsOutParmVar(param)) if (!declIdMapper.glPerVertex.recordGlPerVertexDeclFacts(param, false)) - return false; + return nullptr; } // Also consider the SV_ClipDistance/SV_CullDistance in the return type if (!declIdMapper.glPerVertex.recordGlPerVertexDeclFacts(decl, false)) - return false; + return nullptr; // Calculate the total size of the ClipDistance/CullDistance array and the // offset of SV_ClipDistance/SV_CullDistance variables within the array. @@ -13581,6 +13617,11 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, // after the basic block is created and insert point is set. processInlineSpirvAttributes(decl); + // Add DebugFunctionDefinition if we are emitting + // NonSemantic.Shader.DebugInfo.100 debug info. + if (spirvOptions.debugInfoVulkan && debugFunction) + spvBuilder.createDebugFunctionDef(debugFunction, entryFunction); + // Initialize all global variables at the beginning of the wrapper for (const VarDecl *varDecl : toInitGloalVars) { // SPIR-V does not have string variables @@ -13632,7 +13673,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, SpirvInstruction *loadedValue = nullptr; if (!declIdMapper.createStageInputVar(param, &loadedValue, false)) - return false; + return nullptr; // Only initialize the temporary variable if the parameter is indeed used, // or if it is an inout parameter. @@ -13671,15 +13712,15 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, // Create stage output variables out of the return type. if (!declIdMapper.createStageOutputVar(decl, numOutputControlPoints, outputControlPointIdVal, retVal)) - return false; + return nullptr; if (!processHSEntryPointOutputAndPCF( decl, retType, retVal, numOutputControlPoints, outputControlPointIdVal, primitiveIdVar, viewIdVar, hullMainInputPatchParam)) - return false; + return nullptr; } else { if (!declIdMapper.createStageOutputVar(decl, retVal, /*forPCF*/ false)) - return false; + return nullptr; } // Create and write stage output variables for parameters marked as @@ -13702,7 +13743,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, param->getLocStart()); if (!declIdMapper.createStageOutputVar(param, loadedParam, false)) - return false; + return nullptr; } } @@ -13717,7 +13758,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, if (spvContext.isHS()) doDecl(patchConstFunc); - return true; + return entryFunction; } bool SpirvEmitter::processHSEntryPointOutputAndPCF( diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.h b/tools/clang/lib/SPIRV/SpirvEmitter.h index 242550c295..e41915eca5 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.h +++ b/tools/clang/lib/SPIRV/SpirvEmitter.h @@ -842,8 +842,14 @@ class SpirvEmitter : public ASTConsumer { processMeshOrAmplificationShaderAttributes(const FunctionDecl *decl, uint32_t *outVerticesArraySize); - /// \brief Emits a wrapper function for the entry function and returns true - /// on success. + /// \brief Emits a SpirvDebugFunction to match given SpirvFunction, and + /// returns a pointer to it. + SpirvDebugFunction *emitDebugFunction(const FunctionDecl *decl, + SpirvFunction *func, + RichDebugInfo **info, std::string name); + + /// \brief Emits a wrapper function for the entry function and returns a + /// pointer to the wrapper SpirvFunction on success. /// /// The wrapper function loads the values of all stage input variables and /// creates composites as expected by the source code entry function. It then @@ -853,8 +859,9 @@ class SpirvEmitter : public ASTConsumer { /// /// The wrapper function is also responsible for initializing global static /// variables for some cases. - bool emitEntryFunctionWrapper(const FunctionDecl *entryFunction, - SpirvFunction *entryFuncId); + SpirvFunction *emitEntryFunctionWrapper(const FunctionDecl *entryFunction, + SpirvDebugFunction *debugFunction, + SpirvFunction *entryFuncId); /// \brief Emits a wrapper function for the entry functions for raytracing /// stages and returns true on success. @@ -864,6 +871,7 @@ class SpirvEmitter : public ASTConsumer { /// The wrapper function is also responsible for initializing global static /// variables for some cases. bool emitEntryFunctionWrapperForRayTracing(const FunctionDecl *entryFunction, + SpirvDebugFunction *debugFunction, SpirvFunction *entryFuncId); /// \brief Performs the following operations for the Hull shader: diff --git a/tools/clang/test/CodeGenSPIRV/rich.debug.function.param.hlsl b/tools/clang/test/CodeGenSPIRV/rich.debug.function.param.hlsl index c30dc8c262..231750cd64 100644 --- a/tools/clang/test/CodeGenSPIRV/rich.debug.function.param.hlsl +++ b/tools/clang/test/CodeGenSPIRV/rich.debug.function.param.hlsl @@ -7,7 +7,7 @@ // CHECK: [[emptyStr:%[0-9]+]] = OpString "" // CHECK: [[y:%[0-9]+]] = OpString "y" // CHECK: [[x:%[0-9]+]] = OpString "x" -// CHECK: [[mainName:%[0-9]+]] = OpString "main" +// CHECK: [[mainName:%[0-9]+]] = OpString "src.main" // CHECK: [[color:%[0-9]+]] = OpString "color" // CHECK: [[int:%[0-9]+]] = OpExtInst %void [[set]] DebugTypeBasic {{%[0-9]+}} %uint_32 Signed diff --git a/tools/clang/test/CodeGenSPIRV/shader.debug.function.hlsl b/tools/clang/test/CodeGenSPIRV/shader.debug.function.hlsl index e59e9bc807..6d61d9f5c4 100644 --- a/tools/clang/test/CodeGenSPIRV/shader.debug.function.hlsl +++ b/tools/clang/test/CodeGenSPIRV/shader.debug.function.hlsl @@ -5,8 +5,9 @@ // CHECK: [[set:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" // CHECK: [[fooName:%[0-9]+]] = OpString "foo" // CHECK: [[emptyStr:%[0-9]+]] = OpString "" +// CHECK: [[srcMainName:%[0-9]+]] = OpString "src.main" // CHECK: [[mainName:%[0-9]+]] = OpString "main" -// CHECK: [[clOpts:%[0-9]+]] = OpString " -E main -T ps_6_0 -spirv -fcgl -fspv-debug=vulkan +// CHECK: [[clOpts:%[0-9]+]] = OpString " -E main -T ps_6_0 -spirv -fcgl -fspv-debug=vulkan // CHECK: [[int:%[0-9]+]] = OpExtInst %void [[set]] DebugTypeBasic {{%[0-9]+}} %uint_32 %uint_4 %uint_0 // CHECK: [[float:%[0-9]+]] = OpExtInst %void [[set]] DebugTypeBasic {{%[0-9]+}} %uint_32 %uint_3 %uint_0 @@ -17,18 +18,22 @@ // Check DebugFunction instructions // -// CHECK: {{%[0-9]+}} = OpExtInst %void [[set]] DebugFunction [[fooName]] [[fooFnType]] [[source]] %uint_34 %uint_1 [[compilationUnit]] [[emptyStr]] %uint_3 %uint_35 - // CHECK: [[float4:%[0-9]+]] = OpExtInst %void [[set]] DebugTypeVector [[float]] %uint_4 // CHECK: [[mainFnType:%[0-9]+]] = OpExtInst %void [[set]] DebugTypeFunction %uint_3 [[float4]] [[float4]] -// CHECK: [[mainDbgFn:%[0-9]+]] = OpExtInst %void [[set]] DebugFunction [[mainName]] [[mainFnType]] [[source]] %uint_39 %uint_1 [[compilationUnit]] [[emptyStr]] %uint_3 %uint_40 +// CHECK: [[srcMainDbgFn:%[0-9]+]] = OpExtInst %void [[set]] DebugFunction [[srcMainName]] [[mainFnType]] [[source]] %uint_44 %uint_1 [[compilationUnit]] [[emptyStr]] %uint_3 %uint_45 +// CHECK: [[mainDbgFn:%[0-9]+]] = OpExtInst %void [[set]] DebugFunction [[mainName]] [[mainFnType]] [[source]] %uint_44 %uint_1 [[compilationUnit]] [[emptyStr]] %uint_3 %uint_45 // CHECK: [[mainDbgEp:%[0-9]+]] = OpExtInst %void [[set]] DebugEntryPoint [[mainDbgFn]] [[compilationUnit]] {{%[0-9]+}} [[clOpts]] -// Check DebugFunctionDefintion is in src_main +// Check DebugFunctionDefinition is in main // -// CHECK: %src_main = OpFunction %v4float None {{%[0-9]+}} -// CHECK: {{%[0-9]+}} = OpExtInst %void [[set]] DebugFunctionDefinition [[mainDbgFn]] %src_main +// CHECK: %main = OpFunction %void None {{%[0-9]+}} +// CHECK: {{%[0-9]+}} = OpExtInst %void [[set]] DebugFunctionDefinition [[mainDbgFn]] %main // CHECK: OpFunctionEnd + +// Check DebugFunctionDefinition is in src.main +// +// CHECK: %src_main = OpFunction %v4float None {{%[0-9]+}} +// CHECK: {{%[0-9]+}} = OpExtInst %void [[set]] DebugFunctionDefinition [[srcMainDbgFn]] %src_main // CHECK: OpFunctionEnd void foo(int x, float y)