From d220f45d54277a2c7d7ffa9f08b918bbfa0309e9 Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Thu, 19 Feb 2026 10:31:47 -0800 Subject: [PATCH 1/4] Copilot's first fix attempt --- lib/HLSL/DxilCondenseResources.cpp | 26 +++++++++++++++++-- .../deterministic_output_resource_array.hlsl | 23 ++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tools/clang/test/DXC/deterministic_output_resource_array.hlsl diff --git a/lib/HLSL/DxilCondenseResources.cpp b/lib/HLSL/DxilCondenseResources.cpp index 09dd9cea64..8b23bc03c7 100644 --- a/lib/HLSL/DxilCondenseResources.cpp +++ b/lib/HLSL/DxilCondenseResources.cpp @@ -2206,8 +2206,30 @@ void DxilLowerCreateHandleForLib::TranslateDxilResourceUses( } } - for (auto U = GV->user_begin(), E = GV->user_end(); U != E;) { - User *user = *(U++); + // Collect GV users into a vector and sort them to ensure deterministic + // processing order. ConstantExpr GEP users of the GV may appear in + // non-deterministic order due to DenseMap-based uniquing of constants. + // Sort GEP users by their array index to produce stable output. + SmallVector GVUsers(GV->user_begin(), GV->user_end()); + std::sort(GVUsers.begin(), GVUsers.end(), [](User *A, User *B) { + GEPOperator *GEPA = dyn_cast(A); + GEPOperator *GEPB = dyn_cast(B); + if (GEPA && GEPB && GEPA->getNumIndices() >= 2 && + GEPB->getNumIndices() >= 2) { + ConstantInt *CIA = + dyn_cast((GEPA->idx_begin() + 1)->get()); + ConstantInt *CIB = + dyn_cast((GEPB->idx_begin() + 1)->get()); + if (CIA && CIB) + return CIA->getZExtValue() < CIB->getZExtValue(); + } + // Non-GEP users (loads, bitcasts) come after GEP users. + if (GEPA != GEPB) + return GEPA != nullptr; + return false; + }); + + for (User *user : GVUsers) { // Skip unused user. if (user->user_empty()) continue; diff --git a/tools/clang/test/DXC/deterministic_output_resource_array.hlsl b/tools/clang/test/DXC/deterministic_output_resource_array.hlsl new file mode 100644 index 0000000000..335260fbf7 --- /dev/null +++ b/tools/clang/test/DXC/deterministic_output_resource_array.hlsl @@ -0,0 +1,23 @@ +// Test that compiling a shader with resource arrays produces deterministic output. +// This is a regression test for https://github.com/microsoft/DirectXShaderCompiler/issues/8171 +// where compiling the same shader multiple times with -Zi and SM 6.6+ produced +// non-identical disassembly due to non-deterministic handle name suffixes. + +// RUN: %dxc -T cs_6_6 -E CSMain -Zi -Fc %t1 %s +// RUN: %dxc -T cs_6_6 -E CSMain -Zi -Fc %t2 %s +// RUN: diff %t1 %t2 + +Texture2D inMaps[16]; +RWTexture2D Output; + +[numthreads(1,1,1)] +void CSMain(uint3 threadID : SV_DispatchThreadID) +{ + Output[threadID.xy] = inMaps[0][threadID.xy]; + Output[threadID.xy] = inMaps[1][threadID.xy]; + Output[threadID.xy] = inMaps[2][threadID.xy]; + Output[threadID.xy] = inMaps[3][threadID.xy]; + Output[threadID.xy] = inMaps[4][threadID.xy]; + Output[threadID.xy] = inMaps[5][threadID.xy]; + Output[threadID.xy] = inMaps[6][threadID.xy]; +} From 873236b5fd98b8ec67d955fccd9ccef92dfdca87 Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Thu, 19 Feb 2026 12:08:22 -0800 Subject: [PATCH 2/4] Take 2: don't give the SSA instructions names --- lib/HLSL/DxilCondenseResources.cpp | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/lib/HLSL/DxilCondenseResources.cpp b/lib/HLSL/DxilCondenseResources.cpp index 8b23bc03c7..047f07da49 100644 --- a/lib/HLSL/DxilCondenseResources.cpp +++ b/lib/HLSL/DxilCondenseResources.cpp @@ -2206,30 +2206,8 @@ void DxilLowerCreateHandleForLib::TranslateDxilResourceUses( } } - // Collect GV users into a vector and sort them to ensure deterministic - // processing order. ConstantExpr GEP users of the GV may appear in - // non-deterministic order due to DenseMap-based uniquing of constants. - // Sort GEP users by their array index to produce stable output. - SmallVector GVUsers(GV->user_begin(), GV->user_end()); - std::sort(GVUsers.begin(), GVUsers.end(), [](User *A, User *B) { - GEPOperator *GEPA = dyn_cast(A); - GEPOperator *GEPB = dyn_cast(B); - if (GEPA && GEPB && GEPA->getNumIndices() >= 2 && - GEPB->getNumIndices() >= 2) { - ConstantInt *CIA = - dyn_cast((GEPA->idx_begin() + 1)->get()); - ConstantInt *CIB = - dyn_cast((GEPB->idx_begin() + 1)->get()); - if (CIA && CIB) - return CIA->getZExtValue() < CIB->getZExtValue(); - } - // Non-GEP users (loads, bitcasts) come after GEP users. - if (GEPA != GEPB) - return GEPA != nullptr; - return false; - }); - - for (User *user : GVUsers) { + for (auto U = GV->user_begin(), E = GV->user_end(); U != E;) { + User *user = *(U++); // Skip unused user. if (user->user_empty()) continue; @@ -2272,7 +2250,7 @@ void DxilLowerCreateHandleForLib::TranslateDxilResourceUses( IRBuilder<> Builder = IRBuilder<>(ldInst); Args[resIdxOpIdx] = Builder.CreateAdd(idx, resLowerBound); Instruction *localHandle = - Builder.CreateCall(createHandle, Args, handleName); + Builder.CreateCall(createHandle, Args); ReplaceResourceUserWithHandle(static_cast(res), ldInst, localHandle); } From fa41d23ae555e4bc91b2bee9fa5c01b0227259be Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Thu, 19 Feb 2026 12:39:06 -0800 Subject: [PATCH 3/4] Take 3: replace DenseSet with Vector --- lib/HLSL/DxilCondenseResources.cpp | 2 +- lib/HLSL/DxilPromoteResourcePasses.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/HLSL/DxilCondenseResources.cpp b/lib/HLSL/DxilCondenseResources.cpp index 047f07da49..09dd9cea64 100644 --- a/lib/HLSL/DxilCondenseResources.cpp +++ b/lib/HLSL/DxilCondenseResources.cpp @@ -2250,7 +2250,7 @@ void DxilLowerCreateHandleForLib::TranslateDxilResourceUses( IRBuilder<> Builder = IRBuilder<>(ldInst); Args[resIdxOpIdx] = Builder.CreateAdd(idx, resLowerBound); Instruction *localHandle = - Builder.CreateCall(createHandle, Args); + Builder.CreateCall(createHandle, Args, handleName); ReplaceResourceUserWithHandle(static_cast(res), ldInst, localHandle); } diff --git a/lib/HLSL/DxilPromoteResourcePasses.cpp b/lib/HLSL/DxilPromoteResourcePasses.cpp index 9ba6e3cb3d..afbd566298 100644 --- a/lib/HLSL/DxilPromoteResourcePasses.cpp +++ b/lib/HLSL/DxilPromoteResourcePasses.cpp @@ -16,6 +16,7 @@ #include "dxc/HLSL/DxilGenerationPass.h" #include "dxc/HLSL/HLModule.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SetVector.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/DebugInfo.h" @@ -313,7 +314,7 @@ class DxilMutateResourceToHandle : public ModulePass { hlsl::OP *hlslOP = nullptr; Function *createHandleForLibOnHandle = nullptr; DxilTypeSystem *pTypeSys; - DenseSet MutateValSet; + SetVector MutateValSet; DenseMap MutateTypeMap; }; @@ -613,7 +614,7 @@ void DxilMutateResourceToHandle::collectCandidates(Module &M) { for (Value *Val : newCandidates) { // New candidate find. - if (MutateValSet.insert(Val).second) { + if (MutateValSet.insert(Val)) { WorkList.emplace_back(Val); } } From 6ba5b5dc923079c9fe98c23d228fa6b2f7299b5c Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Fri, 20 Feb 2026 14:37:23 -0800 Subject: [PATCH 4/4] Add entry to ReleaseNodes. --- docs/ReleaseNotes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index b847ab51c3..0ef6621949 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -37,6 +37,11 @@ line upon naming the release. Refer to previous for appropriate section names. - `dx::IsDebuggerPresent()` returns true if a debugger is attached. - SPIR-V: `DebugBreak()` emits `NonSemantic.DebugBreak` extended instruction; `IsDebuggerPresent()` is not supported. +#### Bug Fixes + +- Fixed non-deterministic DXIL/PDB output when compiling shaders with resource arrays, debug info, and SM 6.6+. [#8171](https://github.com/microsoft/DirectXShaderCompiler/issues/8171) + + ### Version 1.9.2602 #### Shader Model 6.9 Release