microsoft / microsoft/DirectXShaderCompiler

[Debug info] Mesh shader `vertices[]` / `primitives[]` / `indices[]`: missing or wrongly-offset debug info

Open
#8,754 2 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

Version: dxcompiler.dll 1.9(5402-0d3ee6b5)(1.9.0.5402) - 1.9.0.5402 (0d3ee6b55-dirty)

Repro files: mesh_shader_repro.zip

Both repros share this signature:

struct Vertex    { float4 position : SV_Position;    float3 normal : NORMAL; };
struct Primitive { uint   primID   : SV_PrimitiveID; float  shade  : COLOR0; };

[numthreads(4, 1, 1)]
[outputtopology("triangle")]
void main(
    uint gtid : SV_GroupThreadID,
    out vertices   Vertex    verts[3],
    out primitives Primitive prims[2],
    out indices    uint3     tris[2])

(a) Dynamic index: no debug info at all

dxc -T ms_6_5 -Zi -Qembed_debug -Od -Fc dynamic_index.ll dynamic_index.hlsl
    if (gtid < 3)
    {
        verts[gtid].position = float4(t, t, t, 1.0);
        verts[gtid].normal   = float3(0, 0, 1);
    }
    if (gtid < 2)
    {
        prims[gtid].primID = gtid;
        prims[gtid].shade  = t;
        tris[gtid]         = uint3(0, 1, 2);
    }

The only local variables in the whole module are gtid and t:

!DILocalVariable(tag: DW_TAG_arg_variable, name: "gtid", arg: 1, scope: !34, ...)
!DILocalVariable(tag: DW_TAG_auto_variable, name: "t",   scope: !34, ...)

verts, prims and tris have no DILocalVariable and no dbg.value, despite
the dx.op.storeVertexOutput, dx.op.storePrimitiveOutput and
dx.op.emitIndices calls in the function.

(b) Constant index: fragment offsets use the wrong layout

dxc -T ms_6_5 -Zi -Qembed_debug -Od -Fc constant_index.ll constant_index.hlsl

Now all three arrays get a DILocalVariable, each typed as a plain
array-of-struct:

!92  = !DILocalVariable(tag: DW_TAG_arg_variable, name: "verts", arg: 2, scope: !34, file: !1, line: 27, type: !39)
!104 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "prims", arg: 3, scope: !34, file: !1, line: 28, type: !47)
!109 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "tris",  arg: 4, scope: !34, file: !1, line: 29, type: !55)

!39 = !DICompositeType(tag: DW_TAG_array_type, baseType: !40, size: 672, align: 32, elements: !44)   ; Vertex[3]
!40 = !DICompositeType(tag: DW_TAG_structure_type, name: "Vertex", ..., size: 224, align: 32, elements: !41)
!42 = !DIDerivedType(tag: DW_TAG_member, name: "position", scope: !40, ..., size: 128, align: 32)
!43 = !DIDerivedType(tag: DW_TAG_member, name: "normal",   scope: !40, ..., size:  96, align: 32, offset: 128)

!47 = !DICompositeType(tag: DW_TAG_array_type, baseType: !48, size: 128, align: 32, elements: !52)   ; Primitive[2]
!48 = !DICompositeType(tag: DW_TAG_structure_type, name: "Primitive", ..., size: 64, align: 32, elements: !49)
!50 = !DIDerivedType(tag: DW_TAG_member, name: "primID", scope: !48, ..., size: 32, align: 32)
!51 = !DIDerivedType(tag: DW_TAG_member, name: "shade",  scope: !48, ..., size: 32, align: 32, offset: 32)

!55 = !DICompositeType(tag: DW_TAG_array_type, baseType: !24, size: 192, align: 32, elements: !52)   ; uint3[2]
verts: wrong
    verts[0].position = float4(t, t, t, 1.0);
    verts[0].normal   = float3(0, 0, 1);
    verts[1].position = float4(1, 1, 1, 1);
    verts[1].normal   = float3(0, 1, 0);
var:"verts" !DIExpression(DW_OP_bit_piece,   0,  32)   ; verts[0].position.x
var:"verts" !DIExpression(DW_OP_bit_piece,  32,  32)   ; verts[0].position.y
var:"verts" !DIExpression(DW_OP_bit_piece,  64,  32)   ; verts[0].position.z
var:"verts" !DIExpression(DW_OP_bit_piece,  96,  32)   ; verts[0].position.w
var:"verts" !DIExpression(DW_OP_bit_piece, 384,  96)   ; verts[0].normal
var:"verts" !DIExpression(DW_OP_bit_piece, 128, 128)   ; verts[1].position
var:"verts" !DIExpression(DW_OP_bit_piece, 480,  96)   ; verts[1].normal
field expected emitted
verts[0].position 0, 128 0, 128
verts[0].normal 128, 96 384, 96
verts[1].position 224, 128 128, 128
verts[1].normal 352, 96 480, 96
prims: wrong

The constant stores pin the mapping exactly:

call void @llvm.dbg.value(metadata i32 7,              ...)  ; var:"prims" !DIExpression(DW_OP_bit_piece,  0, 32)
call void @dx.op.storePrimitiveOutput.i32(i32 172, i32 0, i32 0, i8 0, i32 7,              i32 0)  ; prims[0].primID = 7

call void @llvm.dbg.value(metadata float 2.500000e-01, ...)  ; var:"prims" !DIExpression(DW_OP_bit_piece, 64, 32)
call void @dx.op.storePrimitiveOutput.f32(i32 172, i32 1, i32 0, i8 0, float 2.500000e-01, i32 0)  ; prims[0].shade = 0.25

call void @llvm.dbg.value(metadata i32 9,              ...)  ; var:"prims" !DIExpression(DW_OP_bit_piece, 32, 32)
call void @dx.op.storePrimitiveOutput.i32(i32 172, i32 0, i32 0, i8 0, i32 9,              i32 1)  ; prims[1].primID = 9

call void @llvm.dbg.value(metadata float 7.500000e-01, ...)  ; var:"prims" !DIExpression(DW_OP_bit_piece, 96, 32)
call void @dx.op.storePrimitiveOutput.f32(i32 172, i32 1, i32 0, i8 0, float 7.500000e-01, i32 1)  ; prims[1].shade = 0.75
field expected emitted
prims[0].primID 0, 32 0, 32
prims[0].shade 32, 32 64, 32
prims[1].primID 64, 32 32, 32
prims[1].shade 96, 32 96, 32

prims[0].shade and prims[1].primID are swapped: reading prims[0].shade
yields the bits of 9, and reading prims[1].primID yields the bits of 0.25.

tris: correct
var:"tris" !DIExpression(DW_OP_bit_piece,  0, 96)   ; tris[0]
var:"tris" !DIExpression(DW_OP_bit_piece, 96, 96)   ; tris[1]

Correct for uint3[2]. The index triple is a single signature row, so the
struct-of-arrays and array-of-struct layouts coincide and there is nothing to
transpose. tris is only affected by (a).

Root cause

The emitted offsets follow the struct-of-arrays layout of the lowered output
signature, one contiguous run per signature row indexed by element, while
the DILocalVariable's type is array-of-struct:

verts, 2 rows (position, normal):    position[3] @ 0..383    normal[3] @ 384..671
prims, 2 rows (primID, shade):       primID[2]   @ 0..63     shade[2]  @  64..127

Expected

  1. Writes through a non-constant index should still describe the output array.
  2. DW_OP_bit_piece offsets must be computed against the layout the
    DILocalVariable's type describes.

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 mesh_shader_repro.zip and run the two supplied dxc commands to inspect dynamic_index.ll and constant_index.ll. Trace the debug-info generation for mesh shader output arrays and compare emitted DW_OP_bit_piece offsets with the declared array-of-struct types. Done means dynamic indices have debug variables and constant-index offsets match those types.

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.