@@ -531,8 +531,255 @@ binding order, to make the feature more robust and user-friendly.
531531
532532## Proposed solution
533533
534+ For each resource or resource array that is a member of a struct declared at
535+ global scope or inside a `cbuffer`, an implicit global variable of the resource
536+ type will be created and associated with the struct instance. All accesses to
537+ the resource member will be redirected to the associated global variable during
538+ Clang CodeGen.
539+
534540## Detailed design
535541
542+ ### Single Resources
543+
544+ For each resource member of a struct declared at global scope or inside a
545+ `cbuffer`, Clang will create an implicit global variable of the same resource
546+ type. The variable name will be derived from the struct instance name and the
547+ member name, following the naming convention used by DXC (see
548+ [Example 1](#example-1)).
549+
550+ For example, given the following struct definitions and instances:
551+ ```c++
552+ struct A {
553+ RWBuffer<float> Buf;
554+ };
555+
556+ struct B {
557+ A a;
558+ };
559+
560+ A a1;
561+ B b1 : register(u2);
562+ ```
563+
564+ For the resource inside ` a1 ` , Clang will create a global variable of type
565+ ` RWBuffer<float> ` named ` a1.Buf ` . For the nested resource in ` b1 ` , accessed via
566+ the member ` a ` , the global variable will be named ` b1.a.Buf ` .
567+
568+ When a resource is inherited from a base class, the variable name includes the
569+ base class name as a ` :: ` delimited component. For example:
570+
571+ ``` c++
572+ struct C : A {
573+ };
574+
575+ C c1;
576+ ```
577+
578+ The global variable for the inherited resource in `c1` will be named `c1.A::Buf`.
579+
580+ > **Note:** DXC uses `.` as the delimiter for both base classes and fields,
581+ > which can produce ambiguous names when a field and base class share the same
582+ > name. This ambiguity causes DXC to crash:
583+ > https://godbolt.org/z/5EM418s6T.
584+
585+ ### Associated Resource Decl Attribute
586+
587+ To enable efficient lookup of the implicit global variables associated with a
588+ struct instance, a new `HLSLAssociatedResourceDeclAttr` attribute will be
589+ introduced. Each attribute instance holds a pointer to one of the global
590+ resource variables created for the struct. The attribute is attached to the
591+ struct instance declaration, with one attribute per embedded resource or
592+ resource array.
593+
594+ ### Resources Arrays
595+
596+ Resource array members of a struct are handled similarly: for each resource
597+ array member, Clang will create a global variable with the same array type.
598+ Unlike DXC, which treats each array element as a separate resource, Clang will
599+ represent the entire array as a single global variable. This approach naturally
600+ supports dynamic indexing of the resource array.
601+
602+ For example:
603+
604+ ```c++
605+ struct D {
606+ RWBuffer<float> Bufs[10];
607+ };
608+
609+ D d1 : register(u5);
610+ ```
611+
612+ Clang creates a global variable named ` d1.Bufs ` of type ` RWBuffer<float>[10] ` ,
613+ with a binding range of ` 10 ` .
614+
615+ ```
616+ ; Resource Bindings:
617+ ;
618+ ; Name Type Format Dim ID HLSL Bind Count
619+ ; ------------------------------ ---------- ------- ----------- ------- -------------- ------
620+ ; d1.Bufs UAV f32 buf U0 u5 10
621+ ```
622+
623+ ### Resources inside Struct Arrays
624+
625+ When a resource (or resource array) is a member of a struct type used in an
626+ array, Clang will create a separate global variable for each array element. The
627+ variable names will be constructed from the struct array instance name, the
628+ array index, and the resource member name, matching DXC's behavior in [ Example
629+ 4] ( #example-4 ) . Since the array index is encoded in the resource name, dynamic
630+ indexing of the struct array will not be supported, consistent with DXC.
631+
632+ For example:
633+ ``` c++
634+ struct A {
635+ RWBuffer<float > Buf;
636+ };
637+
638+ A array[ 3] ;
639+ ```
640+
641+ Clang will create three global variables: `array.0.Buf`, `array.1.Buf`, and
642+ `array.2.Buf`.
643+
644+ Indexing the array with a non-constant index produces an error:
645+ `Index for resource array inside cbuffer must be a literal expression.`
646+
647+ ### Resource Binding
648+
649+ Each implicit global resource variable will have its own binding attribute
650+ specifying its register binding and whether the binding is explicit or implicit.
651+
652+ When a struct contains multiple resource or resource array members, each one
653+ receives a portion of the binding based on its register class and required
654+ range.
655+
656+ For example:
657+
658+ ```c++
659+ struct A {
660+ RWBuffer<float> Buf;
661+ };
662+
663+ struct E {
664+ A a;
665+ RWBuffer<int> array[5];
666+ StructuredBuffer<uint> SB;
667+ };
668+
669+ E e : register(u2) : register (t5);
670+ ```
671+
672+ Clang will create the following global resource declarations:
673+ - ` e.a.Buf ` of type ` RWBuffer<float> ` with binding ` u2 ` and range ` 1 `
674+ - ` e.array ` of type ` RWBuffer<int>[5] ` with binding ` u3 ` and range ` 5 `
675+ - ` e.SB ` of type ` StructuredBuffer<uint> ` with binding ` t5 `
676+
677+ For implicit binding of resources in structs, Clang will apply the same rules as
678+ for resources declared at global scope:
679+
680+ - Implicit bindings are assigned in declaration order of the resources or
681+ resource arrays.
682+ - Resource arrays are assigned a contiguous range of register slots matching the
683+ array size.
684+
685+ This differs from DXC's behavior, which mostly assigns bindings in the order
686+ resources are first used in the shader, though not consistently, making it
687+ unpredictable.
688+
689+ ### CodeGen
690+
691+ During Clang CodeGen, any expression that accesses a resource or resource array
692+ member of a global struct instance will be translated to an access of the
693+ corresponding implicit global variable.
694+
695+ #### Single Resource Access
696+
697+ When CodeGen encounters a ` MemberExpr ` of a resource type, it will traverse the
698+ AST to locate the parent struct declaration, building the expected global
699+ variable name along the way. If the parent is a non-static global struct
700+ instance, CodeGen will search its ` HLSLAssociatedResourceDeclAttr ` attributes to
701+ find the matching global variable, and then generate IR code to access it.
702+
703+ For example:
704+ ``` c++
705+ struct A {
706+ RWBuffer<float > Buf;
707+ };
708+
709+ A a1 : register(u5);
710+
711+ [numthreads(4 ,1 ,1 )]
712+ void main () {
713+ a1.Buf[ 0] = 13.4;
714+ }
715+ ```
716+
717+ The ` a1.Buf ` expression will be translated as access to ` @a1.Buf ` global
718+ variable, which has been initialized with resource handle from binding at the
719+ shader entry point.
720+
721+ #### Resource Array Element Access
722+
723+ Similarly to a single resource access, when Clang CodeGen sees an
724+ ` ArraySubscriptExpr ` of a resource or resource array type that is linked to a
725+ ` MemberExpr ` , it will walk the AST to find its parent struct declaration and the
726+ associated global resource array varible. Then it will generate IR code to
727+ access the array element (or array subset) the same way global resource arrays
728+ are handled.
729+
730+ For example:
731+ ``` c++
732+ struct B {
733+ RWBuffer<float > Bufs[ 4] ;
734+ };
735+
736+ B b1 : register(u2);
737+
738+ [numthreads(4 ,4 ,4 )]
739+ void main (uint3 ID : SV_GroupThreadID) {
740+ b1.Bufs[ ID.x] [ ID.y ] = 0;
741+ }
742+ ```
743+
744+ The expression `b1.Bufs[ID.x]` is translated to a resource handle initialized
745+ from the binding at index `ID.x` within the range of 4 registers starting at
746+ `u2`. The handle is initialized when the array element is accessed, matching
747+ the behavior of global resource arrays.
748+
749+ #### Resource Array Assignment
750+
751+ When an entire resource array is assigned or passed as a function argument,
752+ CodeGen creates a local copy of the array with each element initialized to a
753+ handle from its binding. This matches how global resource array assignments are
754+ handled.
755+
756+ #### Copy of struct with resources
757+
758+ When a struct with resources is assigned to a local variable or passed as a
759+ function parameter, CodeGen creates a local copy. Resource members are
760+ initialized with handle copies from the corresponding global variables, and
761+ resource arrays become local copies with each element initialized to a handle
762+ from its binding.
763+
764+ Note that structs declared at global scope reside in constant address space `2`
765+ and use `cbuffer` struct layout. Copying these structs requires HLSL-specific
766+ handling (see
767+ [llvm/llvm-project#153055](https://github.com/llvm/llvm-project/issues/153055)),
768+ and support for copying embedded resources and resource arrays must be built on
769+ top of that.
770+
771+ #### Binding Range Validation
772+
773+ Clang will detect out-of-range bindings during semantic analysis and report
774+ clear error messages pointing to the resource declaration. This improves upon
775+ DXC's range validation errors, which are often unclear and sometimes missing
776+ entirely.
777+
778+ #### Specifying `space` for resources in structs
779+
780+ Clang will support specifying register `space` for struct instances containing
781+ resources, addressing a limitation in DXC (see [Example 10](#example-10)).
782+
536783## Alternatives considered (Optional)
537784
538785## Acknowledgments (Optional)
0 commit comments