Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions source/slang/slang-emit-spirv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,9 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
}

IRInst* m_defaultDebugSource = nullptr;
IRInst* m_entryPointDebugSource = nullptr;

void setEntryPointDebugSource(IRInst* source) { m_entryPointDebugSource = source; }

Dictionary<UnownedStringSlice, SpvInst*> m_extensionInsts;
SpvInst* ensureExtensionDeclaration(UnownedStringSlice name)
Expand Down Expand Up @@ -2356,10 +2359,18 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
}

auto moduleInst = inst->getModule()->getModuleInst();
if (!m_defaultDebugSource)
m_defaultDebugSource = debugSource;
// Only create DebugCompilationUnit for non-included files
auto isIncludedFile = as<IRBoolLit>(debugSource->getIsIncludedFile())->getValue();
auto isIncludedFileLit = as<IRBoolLit>(debugSource->getIsIncludedFile());
bool isIncludedFile = isIncludedFileLit ? isIncludedFileLit->getValue() : false;
// Prefer the entry point's debug source as the default.
// Otherwise, only set default debug source to non-included files.
if (!m_defaultDebugSource)
{
if (m_entryPointDebugSource && debugSource == m_entryPointDebugSource)
m_defaultDebugSource = debugSource;
else if (!m_entryPointDebugSource && !isIncludedFile)
m_defaultDebugSource = debugSource;
}
if (!m_mapIRInstToSpvDebugInst.containsKey(moduleInst) && !isIncludedFile)
{
IRBuilder builder(inst);
Expand Down Expand Up @@ -9831,6 +9842,24 @@ SlangResult emitSPIRVFromIR(
CompilerOptionName::PreserveParameters);
auto generateWholeProgram = codeGenContext->getTargetProgram()->getOptionSet().getBoolOption(
CompilerOptionName::GenerateWholeProgram);

// Pre-identify the entry point's debug source to use as the default debug source.
// This ensures that global variables without explicit debug locations use the
// correct source file (the main shader file where the entry point is defined,
// not imported module files).
for (auto entryPoint : irEntryPoints)
{
if (auto debugLoc = entryPoint->findDecoration<IRDebugLocationDecoration>())
{
auto entryPointDebugSource = as<IRDebugSource>(debugLoc->getSource());
if (entryPointDebugSource)
{
context.setEntryPointDebugSource(entryPointDebugSource);
break;
}
}
}

for (auto inst : irModule->getGlobalInsts())
{
if (as<IRDebugSource>(inst))
Expand Down
43 changes: 39 additions & 4 deletions source/slang/slang-ir-link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2155,6 +2155,7 @@ LinkedIR linkIR(CodeGenContext* codeGenContext)

// Clone additional insts that should be included in the linked IR module
// even if they are not being directly referenced.
bool isFirstUserModule = true;
for (IRModule* irModule : userModules)
{
for (auto inst : irModule->getGlobalInsts())
Expand All @@ -2175,19 +2176,53 @@ LinkedIR linkIR(CodeGenContext* codeGenContext)
// to the relevant parameters and cloned via `cloneExtraDecorations`.
// In the long run we do not want to *ever* iterate over all the
// instructions in all the input modules.
[[fallthrough]];
case kIROp_DebugSource:
// Need to list all source files in the debug source file list,
// regardless if the source files participate in the line table or not.
cloneValue(context, inst);
break;
case kIROp_DebugSource:
{
// Need to list all source files in the debug source file list,
// regardless if the source files participate in the line table or not.
// For DebugSource from imported modules (not the first/main module),
// we need to mark them as included files so they don't become the
// default debug source for global variables.
auto debugSource = as<IRDebugSource>(inst);
auto isIncludedFileLit = as<IRBoolLit>(debugSource->getIsIncludedFile());

// If this is from an imported module and not already marked as included,
// create a new DebugSource with isIncludedFile=true
if (!isFirstUserModule && isIncludedFileLit && !isIncludedFileLit->getValue())
{
auto fileName = as<IRStringLit>(debugSource->getFileName());
auto source = as<IRStringLit>(debugSource->getSource());
if (fileName && source)
{
auto newDebugSource = context->builder->emitDebugSource(
fileName->getStringSlice(),
source->getStringSlice(),
true);
// Register this as the clone of the original so that references
// to the original will use the new one with isIncludedFile=true
registerClonedValue(context, newDebugSource, inst);
}
else
{
cloneValue(context, inst);
}
}
else
{
cloneValue(context, inst);
}
}
break;
case kIROp_DebugBuildIdentifier:
// The debug build identifier won't be referenced by anything,
// but we still need to keep it around if it is in the IR.
cloneValue(context, inst);
break;
}
}
isFirstUserModule = false;
}

bool shouldCopyGlobalParams =
Expand Down
2 changes: 1 addition & 1 deletion source/slang/slang-lower-to-ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8019,7 +8019,7 @@ IRInst* getOrEmitDebugSource(IRGenContext* context, PathInfo path)
content = UnownedStringSlice((char*)outBlob->getBufferPointer(), outBlob->getBufferSize());
IRBuilder builder(*context->irBuilder);
builder.setInsertInto(context->irBuilder->getModule());
auto debugSrcInst = builder.emitDebugSource(path.foundPath.getUnownedSlice(), content, false);
auto debugSrcInst = builder.emitDebugSource(path.foundPath.getUnownedSlice(), content, true);
context->shared->mapSourcePathToDebugSourceInst[path.foundPath] = debugSrcInst;
return debugSrcInst;
}
Expand Down
35 changes: 35 additions & 0 deletions tests/spirv/debug-global-variable-source-import.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//TEST:SIMPLE(filecheck=CHECK):-target spirv -entry main -stage fragment -g2 -emit-spirv-directly

// Test that DebugGlobalVariable references the correct source file (this main file)
// when using import syntax instead of #include.

import shader_utils_module;

struct VertexOutput
{
float4 position : SV_Position;
float2 texCoord : TEXCOORD0;
}

[shader("fragment")]
float4 main(VertexOutput input) : SV_Target
{
float2 uv = input.texCoord;
float distanceFromCenter = calculateDistanceFromCenterModule(uv);
float radialFade = createRadialFadeModule(uv, 0.7);
float4 color = float4(
uv.x * (1.0 - distanceFromCenter * 0.5),
uv.y * radialFade,
uv.x * uv.y,
1.0
);
return color;
}

// Verify that DebugSource for the main file is defined
// CHECK-DAG: [[MAIN_FILE:%[0-9]+]] = OpString "{{.*}}debug-global-variable-source-import.slang"
// CHECK-DAG: [[SOURCE_MAIN:%[0-9]+]] = OpExtInst %void %{{[0-9]+}} DebugSource [[MAIN_FILE]] %{{[0-9]+}}

// Verify that DebugGlobalVariable for entry point parameters references the main file source
// The key fix: DebugGlobalVariable should reference the main file source, not the imported file source
// CHECK-DAG: OpExtInst %void %{{[0-9]+}} DebugGlobalVariable {{%[0-9]+}} {{%[0-9]+}} [[SOURCE_MAIN]] %uint_0 %uint_0
35 changes: 35 additions & 0 deletions tests/spirv/debug-global-variable-source.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//TEST:SIMPLE(filecheck=CHECK):-target spirv -entry main -stage fragment -g2 -emit-spirv-directly

// Test that DebugGlobalVariable references the correct source file (this main file)
// rather than an included header file.

#include "shader-utils.slang"

struct VertexOutput
{
float4 position : SV_Position;
float2 texCoord : TEXCOORD0;
}

[shader("fragment")]
float4 main(VertexOutput input) : SV_Target
{
float2 uv = input.texCoord;
float distanceFromCenter = calculateDistanceFromCenter(uv);
float radialFade = createRadialFade(uv, 0.7);
float4 color = float4(
uv.x * (1.0 - distanceFromCenter * 0.5),
uv.y * radialFade,
uv.x * uv.y,
1.0
);
return color;
}

// Verify that DebugSource for the main file is defined
// CHECK-DAG: [[MAIN_FILE:%[0-9]+]] = OpString "{{.*}}debug-global-variable-source.slang"
// CHECK-DAG: [[SOURCE_MAIN:%[0-9]+]] = OpExtInst %void %{{[0-9]+}} DebugSource [[MAIN_FILE]] %{{[0-9]+}}

// Verify that DebugGlobalVariable for entry point parameters references the main file source
// The key fix: DebugGlobalVariable should reference the main file source, not the included file source
// CHECK-DAG: OpExtInst %void %{{[0-9]+}} DebugGlobalVariable {{%[0-9]+}} {{%[0-9]+}} [[SOURCE_MAIN]] %uint_0 %uint_0
16 changes: 16 additions & 0 deletions tests/spirv/shader-utils-module.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Module containing utility functions for debug tests
module shader_utils_module;

// Helper function to calculate distance from center
public float calculateDistanceFromCenterModule(float2 uv)
{
float2 center = float2(0.5, 0.5);
return length(uv - center);
}

// Additional utility function - simple smoothstep fade
public float createRadialFadeModule(float2 uv, float radius)
{
float dist = calculateDistanceFromCenterModule(uv);
return smoothstep(radius, 0.0, dist);
}