Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions include/dxc/DXIL/DxilModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ class DxilModule {
void ResetEntryPropsMap(DxilEntryPropsMap &&PropMap);

bool StripReflection();
bool StripNamesSensitiveToDebug();
void StripDebugRelatedCode();
void RemoveUnusedTypeAnnotations();

Expand Down
49 changes: 49 additions & 0 deletions lib/DXIL/DxilModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,55 @@ bool DxilModule::StripReflection() {
return bChanged;
}

bool DxilModule::StripNamesSensitiveToDebug() {
bool bChanged = false;
bool bIsLib = GetShaderModel()->IsLib();

if (!bIsLib) {
// Strip struct names
vector<StructType *> structTypes = m_pModule->getIdentifiedStructTypes();
unsigned NextStructId = 0;
for (StructType *ST : structTypes) {
if (!ST->hasName())
continue;

StringRef Name = ST->getName();
if (Name.startswith("dx."))
continue;

ST->setName((Twine("dx.strip.struct.") + Twine(NextStructId++)).str());
bChanged = true;
}

// Strip entry function name
if (m_pEntryFunc) {
SetEntryFunctionName("dx.strip.entry.");
m_pEntryFunc->setName("dx.strip.entry.");
bChanged = true;
}

// Strip groupshared variable names
unsigned NextGroupSharedId = 0;
for (GlobalVariable &GV : m_pModule->globals()) {
if (GV.getType()->getPointerAddressSpace() == DXIL::kTGSMAddrSpace &&
GV.hasName()) {
StringRef Name = GV.getName();
if (Name.startswith("dx.") || Name.startswith("llvm."))
continue;
GV.setName(
(Twine("dx.strip.tgsm.") + Twine(NextGroupSharedId++)).str());
bChanged = true;
}
}

// ReEmit meta.
if (bChanged)
ReEmitDxilResources();
}

return bChanged;
}

static void RemoveTypesFromSet(Type *Ty,
SetVector<const StructType *> &typeSet) {
if (Ty->isPointerTy())
Expand Down
5 changes: 5 additions & 0 deletions tools/clang/tools/dxcompiler/dxclinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@ HRESULT STDMETHODCALLTYPE DxcLinker::Link(
SerializeFlags, pOutputStream, 0, opts.DebugFile, &Diag,
&ShaderHashContent, pReflectionStream, pRootSigStream, nullptr,
nullptr);

if (opts.StripDebug) {
inputs.pM->GetOrCreateDxilModule().StripNamesSensitiveToDebug();
}

if (needsValidation) {
valHR = dxcutil::ValidateAndAssembleToContainer(inputs);
} else {
Expand Down
4 changes: 4 additions & 0 deletions tools/clang/tools/dxcompiler/dxcompilerobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,10 @@ class DxcCompiler : public IDxcCompiler3,

inputs.pVersionInfo = static_cast<IDxcVersionInfo *>(this);

if (opts.StripDebug) {
inputs.pM->GetOrCreateDxilModule().StripNamesSensitiveToDebug();
}

if (needsValidation) {
valHR = dxcutil::ValidateAndAssembleToContainer(inputs);
} else {
Expand Down
98 changes: 95 additions & 3 deletions tools/clang/unittests/HLSL/DxilContainerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,11 @@ class DxilContainerTest : public ::testing::Test {
TEST_METHOD(DxilContainerUnitTest)
TEST_METHOD(DxilContainerCompilerVersionTest)
TEST_METHOD(ContainerBuilder_AddPrivateForceLast)

TEST_METHOD(ReflectionMatchesDXBC_CheckIn)
TEST_METHOD(StripReflectionRemovesStructNames)
TEST_METHOD(StripReflectionRemovesEntryFunctionName)
TEST_METHOD(StripReflectionRemovesGroupsharedNames)
TEST_METHOD(StripReflectionLibraryDoesNotStripNames)
BEGIN_TEST_METHOD(ReflectionMatchesDXBC_Full)
TEST_METHOD_PROPERTY(L"Priority", L"1")
END_TEST_METHOD()
Expand Down Expand Up @@ -607,12 +610,14 @@ class DxilContainerTest : public ::testing::Test {
}

std::string DisassembleProgram(LPCSTR program, LPCWSTR entryPoint,
LPCWSTR target) {
LPCWSTR target, LPCWSTR *pArguments = nullptr,
UINT32 argCount = 0) {
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcBlob> pProgram;
CComPtr<IDxcBlobEncoding> pDisassembly;

CompileToProgram(program, entryPoint, target, nullptr, 0, &pProgram);
CompileToProgram(program, entryPoint, target, pArguments, argCount,
&pProgram);
VERIFY_SUCCEEDED(CreateCompiler(&pCompiler));
VERIFY_SUCCEEDED(pCompiler->Disassemble(pProgram, &pDisassembly));
return BlobToUtf8(pDisassembly);
Expand Down Expand Up @@ -3081,3 +3086,90 @@ TEST_F(DxilContainerTest, DxilContainerUnitTest) {
hlsl::GetDxilProgramHeader(&header, hlsl::DxilFourCC::DFCC_DXIL));
VERIFY_IS_NULL(hlsl::GetDxilPartByType(&header, hlsl::DxilFourCC::DFCC_DXIL));
}

TEST_F(DxilContainerTest, StripReflectionRemovesStructNames) {
Copy link

@inbelic inbelic Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding testing, ideally we could use the LIT/Filecheck infrastructure for directly comparing the modified LLVM IR before and after the new pass. The tools/clang/test/DXC/Passes/ has examples of this.

For instance, we would have an llvm ir module and have the run line be something like: %dxopt %s -hlsl-dxil-strip-debug-info -S | FileCheck %s.

It would demonstrate the names being present before the pass and then use CHECK directives to make sure they are removed after.

Similar to here.

const char *Code = R"(
struct MyCustomStruct {
float4 position;
float4 color;
};

cbuffer MyCBuffer : register(b0) {
float4 data;
};

float4 main() : SV_Target {
MyCustomStruct s;
s.position = float4(0, 0, 0, 1);
s.color = float4(1, 1, 1, 1);
return s.color;
}
)";

LPCWSTR StripDebug = L"-Qstrip_debug";

std::string disassembly =
DisassembleProgram(Code, L"main", L"ps_6_0", &StripDebug, 1);

VERIFY_IS_TRUE(disassembly.find("MyCustomStruct") == std::string::npos);
}

TEST_F(DxilContainerTest, StripReflectionRemovesEntryFunctionName) {
const char *Code = R"(
float4 MyCustomEntryPoint() : SV_Target {
return float4(1, 0, 0, 1);
}
)";

LPCWSTR StripDebug = L"-Qstrip_debug";

std::string disassembly = DisassembleProgram(Code, L"MyCustomEntryPoint",
L"ps_6_0", &StripDebug, 1);

VERIFY_IS_TRUE(disassembly.find("MyCustomEntryPoint") == std::string::npos);
VERIFY_IS_TRUE(disassembly.find("dx.strip.entry.") != std::string::npos);
}

TEST_F(DxilContainerTest, StripReflectionRemovesGroupsharedNames) {
const char *Code = R"(
groupshared float mySharedData[256];
groupshared int mySharedCounter;

[numthreads(8, 8, 1)]
void CSMain(uint3 dispatchThreadID : SV_DispatchThreadID) {
mySharedData[dispatchThreadID.x] = 1.0f;
mySharedCounter = 42;
}
)";

LPCWSTR StripDebug = L"-Qstrip_debug";

std::string disassembly =
DisassembleProgram(Code, L"CSMain", L"cs_6_0", &StripDebug, 1);

VERIFY_IS_TRUE(disassembly.find("mySharedData") == std::string::npos);
VERIFY_IS_TRUE(disassembly.find("mySharedCounter") == std::string::npos);
VERIFY_IS_TRUE(disassembly.find("dx.strip.tgsm.") != std::string::npos);
}

TEST_F(DxilContainerTest, StripReflectionLibraryDoesNotStripNames) {
const char *Code = R"(
struct MyLibStruct {
float val;
};

[shader("raygeneration")]
void MyRayGenEntry() {
MyLibStruct s;
s.val = 1.0f;
}
)";

LPCWSTR StripDebug = L"-Qstrip_debug";

std::string disassembly =
DisassembleProgram(Code, L"", L"lib_6_3", &StripDebug, 1);

VERIFY_IS_TRUE(disassembly.find("MyLibStruct") != std::string::npos ||
disassembly.find("dx.strip.struct.") == std::string::npos);
}