summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCopilot <198982749+Copilot@users.noreply.github.com>2025-07-18 11:24:06 -0700
committerGitHub <noreply@github.com>2025-07-18 18:24:06 +0000
commit70273599e2bb8a8e2b0f7b5e7ad47fe2f00b2b40 (patch)
tree29d18f4b20c00bfbe684978d35d02da77713a4ae
parentc901338e3b443be4c72308f4f473892404b73268 (diff)
Fix debug info generation for let variables in SPIR-V output (#7743)
* Initial plan * Fix debug info for let variables Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Fix parameter count for emitDebugVar function call Fixed regression where let variable debug info generation was missing the optional argIndex parameter in emitDebugVar call. Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Add location validity check for debug info generation Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Don't insert debug value for nondebuggable types. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> Co-authored-by: Yong He <yonghe@outlook.com>
-rw-r--r--source/slang/slang-ir-insert-debug-value-store.cpp353
-rw-r--r--source/slang/slang-ir-insert-debug-value-store.h16
-rw-r--r--source/slang/slang-lower-to-ir.cpp43
-rw-r--r--tests/language-feature/let-debug-info.slang21
4 files changed, 252 insertions, 181 deletions
diff --git a/source/slang/slang-ir-insert-debug-value-store.cpp b/source/slang/slang-ir-insert-debug-value-store.cpp
index b5486e891..86ad377a2 100644
--- a/source/slang/slang-ir-insert-debug-value-store.cpp
+++ b/source/slang/slang-ir-insert-debug-value-store.cpp
@@ -6,236 +6,231 @@
namespace Slang
{
-struct DebugValueStoreContext
+bool DebugValueStoreContext::isTypeKind(IRInst* inst)
{
- Dictionary<IRType*, bool> m_mapTypeToDebugability;
- bool isTypeKind(IRInst* inst)
+ if (!inst)
+ return true;
+ switch (inst->getOp())
{
- if (!inst)
- return true;
- switch (inst->getOp())
- {
- case kIROp_TypeKind:
- case kIROp_TypeType:
- return true;
- default:
- return false;
- }
+ case kIROp_TypeKind:
+ case kIROp_TypeType:
+ return true;
+ default:
+ return false;
}
- bool isDebuggableType(IRType* type)
- {
- if (bool* result = m_mapTypeToDebugability.tryGetValue(type))
- return *result;
+}
+bool DebugValueStoreContext::isDebuggableType(IRType* type)
+{
+ if (bool* result = m_mapTypeToDebugability.tryGetValue(type))
+ return *result;
- bool debuggable = false;
- switch (type->getOp())
+ bool debuggable = false;
+ switch (type->getOp())
+ {
+ case kIROp_VoidType:
+ break;
+ case kIROp_StructType:
{
- case kIROp_VoidType:
- break;
- case kIROp_StructType:
+ auto structType = static_cast<IRStructType*>(type);
+ bool structDebuggable = true;
+ for (auto field : structType->getFields())
{
- auto structType = static_cast<IRStructType*>(type);
- bool structDebuggable = true;
- for (auto field : structType->getFields())
+ if (!isDebuggableType(field->getFieldType()))
{
- if (!isDebuggableType(field->getFieldType()))
- {
- structDebuggable = false;
- break;
- }
+ structDebuggable = false;
+ break;
}
- debuggable = structDebuggable;
- break;
- }
- case kIROp_ArrayType:
- case kIROp_UnsizedArrayType:
- {
- auto arrayType = static_cast<IRArrayTypeBase*>(type);
- debuggable = isDebuggableType(arrayType->getElementType());
- break;
}
- case kIROp_VectorType:
- case kIROp_MatrixType:
- case kIROp_PtrType:
- debuggable = true;
+ debuggable = structDebuggable;
break;
- case kIROp_Param:
- // Assume generic parameters are debuggable.
- debuggable = true;
+ }
+ case kIROp_ArrayType:
+ case kIROp_UnsizedArrayType:
+ {
+ auto arrayType = static_cast<IRArrayTypeBase*>(type);
+ debuggable = isDebuggableType(arrayType->getElementType());
break;
- case kIROp_Specialize:
+ }
+ case kIROp_VectorType:
+ case kIROp_MatrixType:
+ case kIROp_PtrType:
+ debuggable = true;
+ break;
+ case kIROp_Param:
+ // Assume generic parameters are debuggable.
+ debuggable = true;
+ break;
+ case kIROp_Specialize:
+ {
+ auto specType = as<IRSpecialize>(type);
+ auto specTypeDebuggable =
+ isDebuggableType((IRType*)getResolvedInstForDecorations(specType));
+ if (!specTypeDebuggable)
+ break;
+ for (UInt i = 0; i < specType->getArgCount(); i++)
{
- auto specType = as<IRSpecialize>(type);
- auto specTypeDebuggable =
- isDebuggableType((IRType*)getResolvedInstForDecorations(specType));
- if (!specTypeDebuggable)
- break;
- for (UInt i = 0; i < specType->getArgCount(); i++)
+ auto arg = specType->getArg(i);
+ if (isTypeKind(arg->getDataType()) &&
+ !isDebuggableType((IRType*)specType->getArg(i)))
{
- auto arg = specType->getArg(i);
- if (isTypeKind(arg->getDataType()) &&
- !isDebuggableType((IRType*)specType->getArg(i)))
- {
- specTypeDebuggable = false;
- break;
- }
+ specTypeDebuggable = false;
+ break;
}
- debuggable = false; // specTypeDebuggable;
- break;
}
- default:
- if (as<IRBasicType>(type))
- debuggable = true;
+ debuggable = false; // specTypeDebuggable;
break;
}
- m_mapTypeToDebugability[type] = debuggable;
- return debuggable;
+ default:
+ if (as<IRBasicType>(type))
+ debuggable = true;
+ break;
}
+ m_mapTypeToDebugability[type] = debuggable;
+ return debuggable;
+}
- void insertDebugValueStore(IRFunc* func)
+void DebugValueStoreContext::insertDebugValueStore(IRFunc* func)
+{
+ IRBuilder builder(func);
+ Dictionary<IRInst*, IRInst*> mapVarToDebugVar;
+ auto firstBlock = func->getFirstBlock();
+ if (!firstBlock)
+ return;
+ auto funcDebugLoc = func->findDecoration<IRDebugLocationDecoration>();
+ if (!funcDebugLoc)
+ return;
+ List<IRInst*> params;
+ for (auto param : firstBlock->getParams())
{
- IRBuilder builder(func);
- Dictionary<IRInst*, IRInst*> mapVarToDebugVar;
- auto firstBlock = func->getFirstBlock();
- if (!firstBlock)
- return;
- auto funcDebugLoc = func->findDecoration<IRDebugLocationDecoration>();
- if (!funcDebugLoc)
- return;
- List<IRInst*> params;
- for (auto param : firstBlock->getParams())
+ params.add(param);
+ }
+ Index paramIndex = 0;
+ for (auto param : params)
+ {
+ builder.setInsertBefore(firstBlock->getFirstOrdinaryInst());
+ auto paramType = param->getDataType();
+ bool isRefParam = false;
+ if (auto outType = as<IROutTypeBase>(paramType))
{
- params.add(param);
+ isRefParam = true;
+ paramType = outType->getValueType();
}
- Index paramIndex = 0;
- 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();
- }
- if (!isDebuggableType(paramType))
- continue;
- auto debugVar = builder.emitDebugVar(
- paramType,
- funcDebugLoc->getSource(),
- funcDebugLoc->getLine(),
- funcDebugLoc->getCol(),
- builder.getIntValue(builder.getUIntType(), paramIndex));
- copyNameHintAndDebugDecorations(debugVar, param);
+ if (!isDebuggableType(paramType))
+ continue;
+ auto debugVar = builder.emitDebugVar(
+ paramType,
+ funcDebugLoc->getSource(),
+ funcDebugLoc->getLine(),
+ funcDebugLoc->getCol(),
+ builder.getIntValue(builder.getUIntType(), paramIndex));
+ copyNameHintAndDebugDecorations(debugVar, param);
- mapVarToDebugVar[param] = debugVar;
+ 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)
- {
- builder.emitDebugValue(debugVar, paramVal);
- }
- paramIndex++;
+ // 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)
+ {
+ builder.emitDebugValue(debugVar, paramVal);
}
+ paramIndex++;
+ }
- for (auto block : func->getBlocks())
+ for (auto block : func->getBlocks())
+ {
+ IRInst* nextInst = nullptr;
+ for (auto inst = block->getFirstInst(); inst; inst = nextInst)
{
- IRInst* nextInst = nullptr;
- for (auto inst = block->getFirstInst(); inst; inst = nextInst)
+ nextInst = inst->getNextInst();
+ if (auto varInst = as<IRVar>(inst))
{
- nextInst = inst->getNextInst();
- if (auto varInst = as<IRVar>(inst))
+ if (auto debugLoc = varInst->findDecoration<IRDebugLocationDecoration>())
{
- if (auto debugLoc = varInst->findDecoration<IRDebugLocationDecoration>())
- {
- auto varType = tryGetPointedToType(&builder, varInst->getDataType());
- builder.setInsertBefore(varInst);
- if (!isDebuggableType(varType))
- continue;
- auto debugVar = builder.emitDebugVar(
- varType,
- debugLoc->getSource(),
- debugLoc->getLine(),
- debugLoc->getCol());
- copyNameHintAndDebugDecorations(debugVar, varInst);
- mapVarToDebugVar[varInst] = debugVar;
- }
+ auto varType = tryGetPointedToType(&builder, varInst->getDataType());
+ builder.setInsertBefore(varInst);
+ if (!isDebuggableType(varType))
+ continue;
+ auto debugVar = builder.emitDebugVar(
+ varType,
+ debugLoc->getSource(),
+ debugLoc->getLine(),
+ debugLoc->getCol());
+ copyNameHintAndDebugDecorations(debugVar, varInst);
+ mapVarToDebugVar[varInst] = debugVar;
}
}
}
+ }
- // Collect all stores and insert debug value insts to update debug vars.
+ // Collect all stores and insert debug value insts to update debug vars.
- // Helper func to insert debugValue updates.
- auto setDebugValue = [&](IRInst* debugVar, IRInst* newValue, ArrayView<IRInst*> accessChain)
- {
- auto ptr = builder.emitElementAddress(debugVar, accessChain);
- builder.emitDebugValue(ptr, newValue);
- };
- for (auto block : func->getBlocks())
+ // Helper func to insert debugValue updates.
+ auto setDebugValue = [&](IRInst* debugVar, IRInst* newValue, ArrayView<IRInst*> accessChain)
+ {
+ auto ptr = builder.emitElementAddress(debugVar, accessChain);
+ builder.emitDebugValue(ptr, newValue);
+ };
+ for (auto block : func->getBlocks())
+ {
+ IRInst* nextInst = nullptr;
+ for (auto inst = block->getFirstInst(); inst; inst = nextInst)
{
- IRInst* nextInst = nullptr;
- for (auto inst = block->getFirstInst(); inst; inst = nextInst)
- {
- nextInst = inst->getNextInst();
+ nextInst = inst->getNextInst();
- if (auto storeInst = as<IRStore>(inst))
+ if (auto storeInst = as<IRStore>(inst))
+ {
+ List<IRInst*> accessChain;
+ auto varInst = getRootAddr(storeInst->getPtr(), accessChain);
+ IRInst* debugVar = nullptr;
+ if (mapVarToDebugVar.tryGetValue(varInst, debugVar))
{
- List<IRInst*> accessChain;
- auto varInst = getRootAddr(storeInst->getPtr(), accessChain);
- IRInst* debugVar = nullptr;
- if (mapVarToDebugVar.tryGetValue(varInst, debugVar))
- {
- builder.setInsertAfter(storeInst);
- setDebugValue(debugVar, storeInst->getVal(), accessChain.getArrayView());
- }
+ builder.setInsertAfter(storeInst);
+ setDebugValue(debugVar, storeInst->getVal(), accessChain.getArrayView());
+ }
+ }
+ else if (auto swizzledStore = as<IRSwizzledStore>(inst))
+ {
+ List<IRInst*> accessChain;
+ auto varInst = getRootAddr(swizzledStore->getDest(), accessChain);
+ IRInst* debugVar = nullptr;
+ if (mapVarToDebugVar.tryGetValue(varInst, debugVar))
+ {
+ builder.setInsertAfter(swizzledStore);
+ auto loadVal = builder.emitLoad(swizzledStore->getDest());
+ setDebugValue(debugVar, loadVal, accessChain.getArrayView());
}
- else if (auto swizzledStore = as<IRSwizzledStore>(inst))
+ }
+ else if (auto callInst = as<IRCall>(inst))
+ {
+ auto funcValue = getResolvedInstForDecorations(callInst->getCallee());
+ if (!funcValue)
+ continue;
+ for (UInt i = 0; i < callInst->getArgCount(); i++)
{
+ auto arg = callInst->getArg(i);
+ if (!as<IRPtrTypeBase>(arg->getDataType()))
+ continue;
List<IRInst*> accessChain;
- auto varInst = getRootAddr(swizzledStore->getDest(), accessChain);
+ auto varInst = getRootAddr(arg, accessChain);
IRInst* debugVar = nullptr;
if (mapVarToDebugVar.tryGetValue(varInst, debugVar))
{
- builder.setInsertAfter(swizzledStore);
- auto loadVal = builder.emitLoad(swizzledStore->getDest());
+ builder.setInsertAfter(callInst);
+ auto loadVal = builder.emitLoad(arg);
setDebugValue(debugVar, loadVal, accessChain.getArrayView());
}
}
- else if (auto callInst = as<IRCall>(inst))
- {
- auto funcValue = getResolvedInstForDecorations(callInst->getCallee());
- if (!funcValue)
- continue;
- for (UInt i = 0; i < callInst->getArgCount(); i++)
- {
- auto arg = callInst->getArg(i);
- if (!as<IRPtrTypeBase>(arg->getDataType()))
- continue;
- List<IRInst*> accessChain;
- auto varInst = getRootAddr(arg, accessChain);
- IRInst* debugVar = nullptr;
- if (mapVarToDebugVar.tryGetValue(varInst, debugVar))
- {
- builder.setInsertAfter(callInst);
- auto loadVal = builder.emitLoad(arg);
- setDebugValue(debugVar, loadVal, accessChain.getArrayView());
- }
- }
- }
}
}
}
-};
+}
-void insertDebugValueStore(IRModule* module)
+void insertDebugValueStore(DebugValueStoreContext& context, IRModule* module)
{
- DebugValueStoreContext context;
for (auto globalInst : module->getGlobalInsts())
{
if (auto genericInst = as<IRGeneric>(globalInst))
diff --git a/source/slang/slang-ir-insert-debug-value-store.h b/source/slang/slang-ir-insert-debug-value-store.h
index 3067c8cdd..0dc289b2e 100644
--- a/source/slang/slang-ir-insert-debug-value-store.h
+++ b/source/slang/slang-ir-insert-debug-value-store.h
@@ -1,10 +1,24 @@
#ifndef SLANG_IR_INSERT_DEBUG_VALUE_STORE_H
#define SLANG_IR_INSERT_DEBUG_VALUE_STORE_H
+#include "core/slang-basic.h"
+
namespace Slang
{
struct IRModule;
-void insertDebugValueStore(IRModule* module);
+struct IRType;
+struct IRFunc;
+struct IRInst;
+
+struct DebugValueStoreContext
+{
+ Dictionary<IRType*, bool> m_mapTypeToDebugability;
+ bool isDebuggableType(IRType* type);
+ void insertDebugValueStore(IRFunc* func);
+ bool isTypeKind(IRInst* inst);
+};
+void insertDebugValueStore(DebugValueStoreContext& context, IRModule* module);
+
} // namespace Slang
#endif // SLANG_IR_INSERT_DEBUG_VALUE_STORE_H
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index ab9f85b21..526e2f952 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -535,6 +535,8 @@ struct SharedIRGenContext
// in the source code.
//
List<IRInst*> m_stringLiterals;
+
+ DebugValueStoreContext debugValueContext;
};
struct IRGenContext;
@@ -9155,6 +9157,45 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
{
auto initVal = lowerRValueExpr(context, initExpr);
initVal = LoweredValInfo::simple(getSimpleVal(context, initVal));
+
+ // For debug builds, still create debug information for let variables
+ // even though we're not creating an actual variable
+ if (context->includeDebugInfo && decl->loc.isValid() &&
+ context->shared->debugValueContext.isDebuggableType(initVal.val->getDataType()))
+ {
+ // Create a debug variable for this let declaration
+ auto builder = context->irBuilder;
+ auto humaneLoc = context->getLinkage()->getSourceManager()->getHumaneLoc(
+ decl->loc,
+ SourceLocType::Emit);
+
+ // Find the debug source for this file
+ auto sourceView =
+ context->getLinkage()->getSourceManager()->findSourceView(decl->loc);
+ if (sourceView)
+ {
+ auto source = sourceView->getSourceFile();
+ IRInst* debugSourceInst = nullptr;
+ if (context->shared->mapSourceFileToDebugSourceInst.tryGetValue(
+ source,
+ debugSourceInst))
+ {
+ auto debugVar = builder->emitDebugVar(
+ varType,
+ debugSourceInst,
+ builder->getIntValue(builder->getUIntType(), humaneLoc.line),
+ builder->getIntValue(builder->getUIntType(), humaneLoc.column),
+ nullptr);
+
+ // Copy name hint from the declaration
+ addNameHint(context, debugVar, decl);
+
+ // Emit debug value to associate the constant with the debug variable
+ builder->emitDebugValue(debugVar, initVal.val);
+ }
+ }
+ }
+
context->setGlobalValue(decl, initVal);
return initVal;
}
@@ -12152,7 +12193,7 @@ RefPtr<IRModule> generateIRForTranslationUnit(
// if debug symbols are enabled.
if (context->includeDebugInfo)
{
- insertDebugValueStore(module);
+ insertDebugValueStore(context->shared->debugValueContext, module);
}
diff --git a/tests/language-feature/let-debug-info.slang b/tests/language-feature/let-debug-info.slang
new file mode 100644
index 000000000..206f234cc
--- /dev/null
+++ b/tests/language-feature/let-debug-info.slang
@@ -0,0 +1,21 @@
+//TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type
+
+//TEST_INPUT: set outputBuffer = out ubuffer(data=[0 0 0 0], stride=4)
+RWStructuredBuffer<float> outputBuffer;
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ let x = float(2); // Let variable should generate debug info
+ var y = float(3); // Var variable for comparison
+ float z = float(4); // Regular variable declaration
+
+ // Use the variables so they don't get optimized away
+ float result = x + y + z;
+
+ // CHECK: 9.0
+ outputBuffer[0] = result;
+}
+
+// This test verifies that 'let' variables now generate debug information
+// Previously, 'let' variables did not generate debug info, causing issues with debuggers \ No newline at end of file