Skip to content

[SPIR-V] SPV_EXT_descriptor_heap: cross-function heap buffer/image alias propagation not implemented, passing or returning a heap alias is rejected or miscompiles #8784

Description

Apart of #8517

Description

Under -fspv-use-descriptor-heap, a local variable initialized from ResourceDescriptorHeap[i] (heap alias) cannot be passed to a user function or returned from one. Depending on the resource family, DXC either emits a compile-time error and rejects the shader, or silently emits invalid SPIR-V that passes DXC but is rejected by the SPIR-V validator.

Two resource families are affected:

  • Buffer aliases (StructuredBuffer, ConstantBuffer, ByteAddressBuffer, etc.): the alias is backed by an OpBufferPointerEXT value whose type is OpTypePointer StorageBuffer. Passing it to a function would require the callee parameter to have the same pointer type, which in turn requires the VariablePointersStorageBuffer capability and matching OpFunctionParameter types that the compiler does not currently generate.
  • Image aliases (RWTexture2D, RWBuffer, etc.): the alias's descriptor index is tracked per-VarDecl in the emitter's internal alias map. When an image alias is passed to or returned from a function, the callee's parameter (or the caller's return-value variable) is a different VarDecl that is absent from that map. Non-atomic reads and writes (OpImageRead/OpImageWrite) work correctly because the image handle value itself is sufficient. Atomic operations require OpImageTexelPointer, which needs the heap slot — the emitter falls back to emitting a plain OpStore to an OpTypeImage-typed temporary, which the SPIR-V validator rejects. DXC emits no diagnostic for this case.

Steps to Reproduce

Buffer alias passing to function (DXC compile error):

// dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv repro_buf_param.hlsl
RWByteAddressBuffer out : register(u0);
uint consume(StructuredBuffer<uint> buf) { return buf[0]; }
[numthreads(1, 1, 1)]
void main() {
    StructuredBuffer<uint> sb = ResourceDescriptorHeap[0];
    out.Store(0, consume(sb));
}

Buffer alias returning from function (DXC compile error):

// dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv repro_buf_ret.hlsl
RWByteAddressBuffer out : register(u0);
StructuredBuffer<uint> makeAlias() {
    StructuredBuffer<uint> sb = ResourceDescriptorHeap[0];
    return sb;
}
[numthreads(1, 1, 1)]
void main() { out.Store(0, makeAlias()[0]); }

Image alias passing to function as in param (DXC silent miscompile, atomic only):

// dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv repro_img_param.hlsl
RWByteAddressBuffer out : register(u0);
void bump(RWTexture2D<uint> t, uint2 c, out uint orig) { InterlockedAdd(t[c], 1, orig); }
[numthreads(1, 1, 1)]
void main(uint3 tid : SV_DispatchThreadID) {
    RWTexture2D<uint> tex = ResourceDescriptorHeap[0];
    uint r;
    bump(tex, tid.xy, r);
    out.Store(0, r);
}

Image alias passing to function as inout param (DXC silent miscompile, atomic only):

// dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv repro_img_inout.hlsl
RWByteAddressBuffer out : register(u0);
void bump(inout RWTexture2D<uint> t, uint2 c, out uint orig) { InterlockedAdd(t[c], 1, orig); }
[numthreads(1, 1, 1)]
void main(uint3 tid : SV_DispatchThreadID) {
    RWTexture2D<uint> tex = ResourceDescriptorHeap[0];
    uint r;
    bump(tex, tid.xy, r);
    out.Store(0, r);
}

Image alias returning from function, caller uses atomic (DXC silent miscompile):

// dxc -T cs_6_6 -E main -fspv-use-descriptor-heap -fspv-target-env=vulkan1.3 -spirv repro_img_ret.hlsl
RWByteAddressBuffer out : register(u0);
RWTexture2D<uint> makeAlias() {
    RWTexture2D<uint> tex = ResourceDescriptorHeap[0];
    return tex;
}
[numthreads(1, 1, 1)]
void main(uint3 tid : SV_DispatchThreadID) {
    uint orig;
    InterlockedAdd(makeAlias()[tid.xy], 1, orig);
    out.Store(0, orig);
}

Actual Behavior

  • Buffer alias passed to function: DXC emits a compile-time error and rejects the shader:
    error: heap buffer alias cannot be passed to a user function; access the buffer element directly at the call site
    
  • Buffer alias returned from function: DXC emits a compile-time error and rejects the shader:
    error: heap buffer alias cannot be returned from a function; access the buffer element directly at the return site
    
  • Image alias passed to function as in or inout param (atomic operations): DXC produces no diagnostic and exits successfully, but emits invalid SPIR-V. The SPIR-V validator then reports:
    fatal error: generated SPIR-V is invalid: [VUID-StandaloneSpirv-OpTypeImage-06924] Cannot store to OpTypeImage
    
    The callee's parameter VarDecl is absent from the internal alias map, so emitDescriptorHeapImageTexelPointer returns nullptr. The fallback emits OpStore to an OpTypeImage-typed temporary — invalid per the SPIR-V spec.
  • Image alias returned from function, caller uses atomic: DXC produces no diagnostic and exits successfully, but emits invalid SPIR-V with the same [VUID-06924] validator error. The return path (doReturnStmt) only guards buffer aliases; image aliases fall through to a plain load of the image handle, stripping alias metadata. The caller's use of the returned value for atomics hits the same fallback.
  • Image alias passed to function or returned (non-atomic load/store): works correctly — OpImageRead/OpImageWrite only need the image handle value, which survives function call boundaries. Only operations requiring OpImageTexelPointer (atomics) are affected.

Expected Behavior

The descriptor index should propagate across function call boundaries. The correct SPIR-V requires either:

  • VariablePointersStorageBuffer capability so that OpBufferPointerEXT values can be passed as function parameters and returned as values for buffer aliases.
  • Descriptor-index out-parameters (or return-value encoding) for image aliases so that the callee's atomic operations target the heap slot from the caller.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug, regression, crashneeds-triageAwaiting triagespirvWork related to SPIR-V

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions