Issue 207852
Summary [HLSL][DirectX] Instcombine's `foldPHIArgLoadIntoPHI` trips up DXILResourceAccess pass
Labels backend:DirectX, HLSL
Assignees
Reporter Icohedron
    Related to https://github.com/llvm/llvm-project/issues/201901

When an HLSL shader conditionally loads from different resources, InstCombine's `foldPHIArgLoadIntoPHI` transformation converts a PHI of loaded values into a load from a PHI of resource pointers. The DirectX backend's `DXILResourceAccess` pass cannot handle PHI users of  `dx.resource.getpointer` results and hits `llvm_unreachable`, crashing the compiler.

Repro: https://godbolt.org/z/rrP3dha6r

Given the shader:
```hlsl
 RWStructuredBuffer<float> bufA : register(u0);
 RWStructuredBuffer<float> bufB : register(u1);
 RWStructuredBuffer<float> output : register(u2);

 cbuffer Constants : register(b0) {
     uint condition;
 };

 [numthreads(64, 1, 1)]
 void CSMain(uint tid : SV_DispatchThreadID) {
    float val;
     if (condition == 0) {
        val = bufA[tid];
     } else {
        val = bufB[tid];
     }
     output[tid] = val;
 }
```

Compiling with `clang-dxc -T cs_6_8 -E CSMain` results in the following assertion:
```
clang: warning: argument unused during compilation: '-Qembed_debug' [-Wunused-command-line-argument]
error: Resource access is not guaranteed to map to a unique global resource
Unhandled instruction - pointer escaped?
UNREACHABLE executed at /root/llvm-project/llvm/lib/Target/DirectX/DXILResourceAccess.cpp:790!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and dumped files.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/clang --driver-mode=dxc -Zi -Qembed_debug -Fc /app/output.s -fcolor-diagnostics -fno-crash-diagnostics -T cs_6_8 -E CSMain <source>
1.	<eof> parser at end of file
2.	Code generation
3.	Running pass 'Function Pass Manager' on module '<source>'.
4.	Running pass 'DXIL Resource Access' on function '@CSMain'
 #0 0x0000000004393158 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4393158)
 #1 0x0000000004390074 llvm::sys::RunSignalHandlers() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4390074)
 #2 0x0000000004390694 llvm::sys::CleanupOnSignal(unsigned long) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4390694)
 #3 0x00000000042cf878 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #4 0x00007c4486a45330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330)
 #5 0x00007c4486a9eb2c pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x9eb2c)
 #6 0x00007c4486a4527e raise (/lib/x86_64-linux-gnu/libc.so.6+0x4527e)
 #7 0x00007c4486a288ff abort (/lib/x86_64-linux-gnu/libc.so.6+0x288ff)
 #8 0x00000000042d9bea (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x42d9bea)
 #9 0x0000000003066757 replaceAccess(llvm::IntrinsicInst*, llvm::dxil::ResourceTypeInfo&) DXILResourceAccess.cpp:0:0
#10 0x0000000003069128 transformResourcePointers(llvm::Function&, llvm::DXILResourceTypeMap&) DXILResourceAccess.cpp:0:0
#11 0x000000000306bd1b (anonymous namespace)::DXILResourceAccessLegacy::runOnFunction(llvm::Function&) DXILResourceAccess.cpp:0:0
#12 0x0000000003c7ab51 llvm::FPPassManager::runOnFunction(llvm::Function&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x3c7ab51)
#13 0x0000000003c7b081 llvm::FPPassManager::runOnModule(llvm::Module&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x3c7b081)
#14 0x0000000003c7ba41 llvm::legacy::PassManagerImpl::run(llvm::Module&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x3c7ba41)
#15 0x00000000047ab988 (anonymous namespace)::EmitAssemblyHelper::TimeCodegenPasses(llvm::function_ref<void ()>) (.isra.0) BackendUtil.cpp:0:0
#16 0x00000000047b8fdb (anonymous namespace)::EmitAssemblyHelper::emitAssembly(clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream>>, clang::BackendConsumer*) BackendUtil.cpp:0:0
#17 0x00000000047b9433 clang::emitBackendOutput(clang::CompilerInstance&, clang::CodeGenOptions&, llvm::StringRef, llvm::Module*, clang::BackendAction, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream>>, clang::BackendConsumer*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x47b9433)
#18 0x0000000004e80b6c clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4e80b6c)
#19 0x0000000006c8310c clang::ParseAST(clang::Sema&, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x6c8310c)
#20 0x0000000004e80f98 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4e80f98)
#21 0x00000000051d299d clang::HLSLFrontendAction::ExecuteAction() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x51d299d)
#22 0x00000000051acb08 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x51acb08)
#23 0x0000000005137f6d clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x5137f6d)
#24 0x00000000052af6fd clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x52af6fd)
#25 0x0000000000db49dc cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0xdb49dc)
#26 0x0000000000dab5ea ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) driver.cpp:0:0
#27 0x0000000000dab76d int llvm::function_ref<int (llvm::SmallVectorImpl<char const*>&)>::callback_fn<clang_main(int, char**, llvm::ToolContext const&)::'lambda'(llvm::SmallVectorImpl<char const*>&)>(long, llvm::SmallVectorImpl<char const*>&) driver.cpp:0:0
#28 0x0000000004ef3b59 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::'lambda'()>(long) Job.cpp:0:0
#29 0x00000000042cfcb4 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x42cfcb4)
#30 0x0000000004ef4685 clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const (.part.0) Job.cpp:0:0
#31 0x0000000004eafbf2 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4eafbf2)
#32 0x0000000004eb0c0e clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4eb0c0e)
#33 0x0000000004ebcd75 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0x4ebcd75)
#34 0x0000000000db0eca clang_main(int, char**, llvm::ToolContext const&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0xdb0eca)
#35 0x0000000000cccc0a main (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0xcccc0a)
#36 0x00007c4486a2a1ca (/lib/x86_64-linux-gnu/libc.so.6+0x2a1ca)
#37 0x00007c4486a2a28b __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28b)
#38 0x0000000000daaf95 _start (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+0xdaaf95)
clang: error: clang frontend command failed due to signal (use -v to see invocation)
Compiler returned: 134
```

## Root-Cause Analysis

**After inlining + SROA (correct IR):**

Each branch loads from its own resource and the loaded values are merged via a
half-valued PHI:

```llvm
if.then.i:
  %handle0 = call @llvm.dx.resource.handlefrombinding(0, 0, ...)  ; buf0
  %ptr0 = call ptr @llvm.dx.resource.getpointer(%handle0, %offset)
  %v0 = load half, ptr %ptr0
  br label %merge

if.else.i:
  %handle1 = call @llvm.dx.resource.handlefrombinding(0, 1, ...)  ; buf1
  %ptr1 = call ptr @llvm.dx.resource.getpointer(%handle1, %offset)
  %v1 = load half, ptr %ptr1
  br label %merge

merge:
  %val = phi half [ %v0, %if.then.i ], [ %v1, %if.else.i ]   ; ← value PHI ✓
```

**After `InstCombinePass` — `foldPHIArgLoadIntoPHI` (broken IR):**

InstCombine sees that both incoming values of the PHI are loads of the same type
and folds them into a single load from a pointer PHI:

```llvm
if.then.i:
  %handle0 = call @llvm.dx.resource.handlefrombinding(0, 0, ...)
  %ptr0 = call ptr @llvm.dx.resource.getpointer(%handle0, %offset)
  br label %merge

if.else.i:
  %handle1 = call @llvm.dx.resource.handlefrombinding(0, 1, ...)
  %ptr1 = call ptr @llvm.dx.resource.getpointer(%handle1, %offset)
  br label %merge

merge:
  %ptr = phi ptr [ %ptr0, %if.then.i ], [ %ptr1, %if.else.i ] ; ← pointer PHI ✗
  %val = load half, ptr %ptr
```

### Why it crashes

The `DXILResourceAccess` pass runs in the DirectX backend and converts
pointer-based resource accesses into DXIL buffer load/store intrinsics. Its
`replaceAccess()` function (DXILResourceAccess.cpp:767) walks users of each
`dx.resource.getpointer` call and expects only `GEP`, `Load`, or `Store`
instructions. A `PHINode` user triggers `llvm_unreachable("Unhandled
instruction - pointer escaped?")` at line 790.

The pass does have `legalizeResourceHandles()` which can decompose PHIs of
resource handles into PHIs of indices — but only when all handles refer to the
**same global binding**. When the handles come from different bindings (as in
this case: `buf0` at register u0 vs `buf1` at register u1), it emits a
diagnostic and continues without fixing the issue, leading to the crash in
`replaceAccess()`.


The debugging, analysis, and write-up of this issue was performed with the assistance of Claude Opus 4.8.

_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to