microsoft / microsoft/DirectXShaderCompiler
[Crash + miscompile] (cond ? a : b) = expr; with HLSL resource types asserts in debug, silently drops in release
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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