Skip to content

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

Description

@bob80905

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug, regression, crashneeds-triageAwaiting triage

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions