Skip to content

Commit

Permalink
[SPIRV] Emit DebugFunction and Definition for both wrapper and real f…
Browse files Browse the repository at this point in the history
…unctions
  • Loading branch information
SteveUrquhart committed Oct 20, 2024
1 parent dfa1c81 commit 6b5653a
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 57 deletions.
131 changes: 86 additions & 45 deletions tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<SpirvDebugCompilationUnit>(outer_scope);
Expand All @@ -1498,9 +1486,16 @@ void SpirvEmitter::doFunctionDecl(const FunctionDecl *decl) {
const QualType retType =
declIdMapper.getTypeAndCreateCounterForPotentialAliasVar(decl);

spvBuilder.beginFunction(retType, decl->getLocStart(), funcName,
decl->hasAttr<HLSLPreciseAttr>(),
decl->hasAttr<NoInlineAttr>(), func);
if (srcFunc) {
spvBuilder.beginFunction(retType, decl->getLocStart(), srcFuncName,
decl->hasAttr<HLSLPreciseAttr>(),
decl->hasAttr<NoInlineAttr>(), srcFunc);

} else {
spvBuilder.beginFunction(retType, decl->getLocStart(), funcName,
decl->hasAttr<HLSLPreciseAttr>(),
decl->hasAttr<NoInlineAttr>(), func);
}

bool isNonStaticMemberFn = false;
if (const auto *memberFn = dyn_cast<CXXMethodDecl>(decl)) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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()) {
Expand All @@ -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())
Expand All @@ -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())
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -13702,7 +13743,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
param->getLocStart());

if (!declIdMapper.createStageOutputVar(param, loadedParam, false))
return false;
return nullptr;
}
}

Expand All @@ -13717,7 +13758,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
if (spvContext.isHS())
doDecl(patchConstFunc);

return true;
return entryFunction;
}

bool SpirvEmitter::processHSEntryPointOutputAndPCF(
Expand Down
16 changes: 12 additions & 4 deletions tools/clang/lib/SPIRV/SpirvEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 12 additions & 7 deletions tools/clang/test/CodeGenSPIRV/shader.debug.function.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down

0 comments on commit 6b5653a

Please sign in to comment.