microsoft / microsoft/DirectXShaderCompiler

[Crash + miscompile] (cond ? a : b) = expr; with HLSL resource types asserts in debug, silently drops in release

Open
#8,579 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug needs-triage
Dominant language
C++
Stars
3.7k
Forks
900
Avg merge
2d 11h
Merged PRs (30d)
44

Description

Repro: (compile with dxc -T cs_6_0):

 RWByteAddressBuffer gBuf0 : register(u0);
 RWByteAddressBuffer gBuf1 : register(u1);
 [numthreads(1,1,1)]
 void main(uint3 tid : SV_DispatchThreadID) {
     RWByteAddressBuffer a = gBuf0;
     RWByteAddressBuffer b = gBuf1;
     (true ? a : b) = gBuf1;            // expected: rebinds a to gBuf1
     a.Store(tid.x * 4, 42);            // expected: writes 42 to gBuf1
 }

Expected: Either (a) sema error "expression is not assignable" (matching DXC's own behavior for uint/float4/float4x4/scalar struct lvalue ternary), or (b) the assignment is honored and a.Store writes to gBuf1.

Actual:

  • Debug build: assert getValueKind() == VK_LValue fires in tools/clang/lib/AST/ExprClassification.cpp:56 (ClassifyImpl); compiler exits with status -5.
  • Release build: compile succeeds, but the ternary-lvalue assignment is silently dropped. Emitted DXIL stores 42 to gBuf0_UAV_rawbuf (register u0), not gBuf1_UAV_rawbuf (register u1).

Type-class scope: Bug only reproduces when the ternary's lvalue type is a resource/object type (RWByteAddressBuffer, RWStructuredBuffer, Texture2D, SamplerState, ...). Scalars/vectors/matrices/POD structs already produce the expected sema error.

This is the resulting IR for assert-disabled builds:

%dx.types.Handle = type { i8* }                                                                                                                  
 %struct.RWByteAddressBuffer = type { i32 }                                                                                                       
                                                                                                                                                  
 define void @main() {                                                                                                                            
   %gBuf1_UAV_rawbuf = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 1, i1 false), !dbg !41                                  
   %gBuf0_UAV_rawbuf = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 0, i1 false), !dbg !41                                  
   %1 = call i32 @dx.op.threadId.i32(i32 93, i32 0), !dbg !42                                                                                     
   call void @llvm.dbg.value(metadata i32 %1, i64 0, metadata !43, metadata !44), !dbg !45                                                        
   %2 = shl i32 %1, 2, !dbg !46                                                                                                                   
   call void @dx.op.rawBufferStore.i32(i32 140, %dx.types.Handle %gBuf0_UAV_rawbuf, i32 %2, i32 undef, i32 1, i32 undef, i32 undef, i32 undef, i8   i32 4), !dbg !47                                                                                                                                
   call void @dx.op.rawBufferStore.i32(i32 140, %dx.types.Handle %gBuf1_UAV_rawbuf, i32 %2, i32 undef, i32 2, i32 undef, i32 undef, i32 undef, i8   i32 4), !dbg !48                                                                                                                                
   ret void, !dbg !49                                                                                                                             
 }                                                                                                                                                
                                                                                                                                                  
 declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #0                                                                               
                                                                                                                                                  
 declare i32 @dx.op.threadId.i32(i32, i32) #0                                                                                                     
                                                                                                                                                  
 declare void @dx.op.rawBufferStore.i32(i32, %dx.types.Handle, i32, i32, i32, i32, i32, i32, i8, i32) #1                                          
                                                                                                                                                  
 declare %dx.types.Handle @dx.op.createHandle(i32, i8, i32, i32, i1) #2                                                                           
                                                                                                                                                  
 attributes #0 = { nounwind readnone }                                                                                                            
 attributes #1 = { nounwind }                                                                                                                     
 attributes #2 = { nounwind readonly }

An obscure LLVM internal failure occurs:

# .---command stderr------------
# | Internal compiler error: LLVM Assert
# `-----------------------------
# error: command failed with exit status: 0xe0000001

--

********************

When targeting warp d3d12 as well.

Assisted by: Github Copilot

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the ternary-lvalue classification path in tools/clang/lib/AST/ExprClassification.cpp, especially ClassifyImpl at line 56, and reproduce the case with dxc -T cs_6_0. Trace how resource/object lvalues differ from scalar lvalues; done means the compiler either reports the expected sema error or preserves the assignment so the store targets gBuf1, without debug assertions or release miscompilation.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.