[llvm-branch-commits] [clang] [HLSL] Implement codegen for copying cbuffer structs with resources (PR #204232)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Jun 16 12:48:41 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Helena Kotas (hekota)

<details>
<summary>Changes</summary>

Global-scope structs are in `hlsl_constant` address space and use cbuffer layout. When those structs contain resources, the resources are not stored inline in the constant buffer. Instead, they are represented as separate globals, or in case of resource arrays are initialized on demand.

This change implements the HLSL codegen for cases where a cbuffer-backed struct with embedded resources is copied into a local variable or passed as a function argument. CodeGen materializes a temporary in the default address space, copies the constant-data fields using the cbuffer struct layout, and reconstruct the resource members in the local copy.

Depends on #<!-- -->203961.

Fixes #<!-- -->182990

---

Patch is 37.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/204232.diff


7 Files Affected:

- (modified) clang/include/clang/AST/HLSLResource.h (+2) 
- (modified) clang/lib/CodeGen/CGExprAgg.cpp (+10-4) 
- (modified) clang/lib/CodeGen/CGHLSLRuntime.cpp (+175-28) 
- (modified) clang/lib/CodeGen/CGHLSLRuntime.h (+2-2) 
- (modified) clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp (+6) 
- (modified) clang/lib/CodeGen/HLSLBufferLayoutBuilder.h (+1) 
- (added) clang/test/CodeGenHLSL/resources/cbuffer_struct_passing_with_resources.hlsl (+385) 


``````````diff
diff --git a/clang/include/clang/AST/HLSLResource.h b/clang/include/clang/AST/HLSLResource.h
index 585435ec1dd33..1acfecb0353ed 100644
--- a/clang/include/clang/AST/HLSLResource.h
+++ b/clang/include/clang/AST/HLSLResource.h
@@ -154,6 +154,8 @@ class EmbeddedResourceNameBuilder {
     return &AST.Idents.get(Name);
   }
 
+  llvm::StringRef getName() const { return Name; }
+
 private:
   void pushName(llvm::StringRef N, llvm::StringRef Delim);
 };
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 8540603c28e9a..bc35ffdaad2bd 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -254,6 +254,10 @@ void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) {
     return;
   }
 
+  if (E->getType().getAddressSpace() == LangAS::hlsl_constant)
+    if (CGF.CGM.getHLSLRuntime().emitBufferCopy(CGF, E, LV, Dest))
+      return;
+
   EmitFinalDestCopy(E->getType(), LV);
 }
 
@@ -2318,7 +2322,9 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty,
               Record->hasTrivialCopyAssignment() ||
               Record->hasTrivialMoveConstructor() ||
               Record->hasTrivialMoveAssignment() ||
-              Record->hasAttr<TrivialABIAttr>() || Record->isUnion()) &&
+              Record->hasAttr<TrivialABIAttr>() || Record->isUnion() ||
+              // HLSL uses aggregate-copy for user-defined record types.
+              (getLangOpts().HLSL && !Record->isHLSLBuiltinRecord())) &&
              "Trying to aggregate-copy a type without a trivial copy/move "
              "constructor or assignment operator");
       // Ignore empty classes in C++.
@@ -2339,9 +2345,9 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty,
     }
   }
 
-  if (getLangOpts().HLSL && Ty.getAddressSpace() == LangAS::hlsl_constant)
-    if (CGM.getHLSLRuntime().emitBufferCopy(*this, DestPtr, SrcPtr, Ty))
-      return;
+  assert(Ty.getAddressSpace() != LangAS::hlsl_constant &&
+         "copies of aggregates in hlsl_constant address space should be "
+         "handled earlier by the HLSL runtime");
 
   // Aggregate assignment turns into llvm.memcpy.  This is almost valid per
   // C99 6.5.16.1p3, which states "If the value being stored in an object is
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 40b29559c2a1c..1c6407b75ed0f 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -101,6 +101,13 @@ void addRootSignatureMD(llvm::dxbc::RootSignatureVersion RootSigVer,
   RootSignatureValMD->addOperand(MDVals);
 }
 
+static void copyGlobalResource(CodeGenFunction &CGF, const VarDecl *ResourceVD,
+                               AggValueSlot &DestSlot) {
+  GlobalVariable *ResGV =
+      cast<GlobalVariable>(CGF.CGM.GetAddrOfGlobalVar(ResourceVD));
+  CGF.Builder.CreateStore(ResGV, DestSlot.getAddress());
+}
+
 // Given a MemberExpr of a resource or resource array type, find the parent
 // VarDecl of the struct or class instance that contains this resource and
 // build the full resource name based on the member access path.
@@ -109,12 +116,16 @@ void addRootSignatureMD(llvm::dxbc::RootSignatureVersion RootSigVer,
 // this function will find the VarDecl of "myStructArray" and use the
 // EmbeddedResourceNameBuilder to build the resource name
 // "myStructArray.0.memberA".
+//
+// This also works for a record type expression that has some embedded
+// resources. It finds the parent VarDecl of that record and builds a partial
+// name which is the prefix of the resource globals associated with the
+// declaration.
 static const VarDecl *findStructResourceParentDeclAndBuildName(
-    const MemberExpr *ME, EmbeddedResourceNameBuilder &NameBuilder) {
+    const Expr *E, EmbeddedResourceNameBuilder &NameBuilder) {
 
   SmallVector<const Expr *> WorkList;
   const VarDecl *VD = nullptr;
-  const Expr *E = ME;
 
   for (;;) {
     if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
@@ -481,6 +492,8 @@ class HLSLBufferCopyEmitter {
   SmallVector<llvm::Value *> CurStoreIndices;
   SmallVector<llvm::Value *> CurLoadIndices;
 
+  using EmitResourceFn = llvm::function_ref<void(AggValueSlot &)>;
+
   // Creates & returns either a structured.gep or a ptradd/gep depending on
   // langopts.
   llvm::Value *emitAccessChain(llvm::Type *BaseTy, llvm::Value *Base,
@@ -523,8 +536,39 @@ class HLSLBufferCopyEmitter {
     return true;
   }
 
+  // Returns true if the type is either a struct represening a resource record,
+  // or an array of structs that are resource records. This assumes a struct is
+  // a resource record if the first element is a target type (resource handle).
+  // This is the case for all target types used by HLSL except the padding type
+  // ("{dx|spirv.Padding"), but padding will never be the first element of a
+  // struct.
+  bool isResourceOrResourceArray(llvm::Type *Ty) {
+    while (auto *AT = dyn_cast<llvm::ArrayType>(Ty))
+      Ty = AT->getElementType();
+
+    auto *ST = dyn_cast<llvm::StructType>(Ty);
+    if (!ST || ST->getNumElements() < 1)
+      return false;
+
+    auto *TargetTy = dyn_cast<llvm::TargetExtType>(ST->getElementType(0));
+    return TargetTy != nullptr;
+  }
+
+  void emitResourceOrResourceArray(Value *Dst, llvm::Type *DstTy,
+                                   EmitResourceFn EmitResFn) {
+    CharUnits DstAlign =
+        CharUnits::fromQuantity(CGF.CGM.getDataLayout().getABITypeAlign(DstTy));
+    Address DstAddr(Dst, DstTy, DstAlign);
+    AggValueSlot Slot = AggValueSlot::forAddr(
+        DstAddr, Qualifiers(), AggValueSlot::IsDestructed_t(true),
+        AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
+        AggValueSlot::DoesNotOverlap);
+
+    EmitResFn(Slot);
+  }
+
   void emitBufferLayoutCopy(Value *Src, llvm::StructType *SrcTy, Value *Dst,
-                            llvm::ArrayType *DstTy) {
+                            llvm::ArrayType *DstTy, EmitResourceFn EmitResFn) {
     // Those assumptions are checked by isBufferLayoutArray.
     auto *SrcPaddedArrayTy = cast<llvm::ArrayType>(SrcTy->getElementType(0));
     assert(SrcPaddedArrayTy->getNumElements() + 1 == DstTy->getNumElements());
@@ -538,7 +582,8 @@ class HLSLBufferCopyEmitter {
       auto Index = llvm::ConstantInt::get(CGF.IntTy, I);
       auto *SrcElt = emitAccessChain(SrcTy, Src, {Zero, Index, Zero});
       auto *DstElt = emitAccessChain(DstTy, Dst, {Index});
-      emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType());
+      emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType(),
+                      EmitResFn);
     }
 
     auto *SrcElt =
@@ -546,62 +591,78 @@ class HLSLBufferCopyEmitter {
     auto *DstElt = emitAccessChain(
         DstTy, Dst,
         {llvm::ConstantInt::get(CGF.IntTy, DstTy->getNumElements() - 1)});
-    emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType());
+    emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType(),
+                    EmitResFn);
   }
 
   void emitCopy(Value *Src, llvm::StructType *SrcTy, Value *Dst,
-                llvm::Type *DstTy) {
+                llvm::Type *DstTy, EmitResourceFn EmitResFn) {
+    assert(!isResourceOrResourceArray(DstTy) &&
+           "direct access to resources or resource arrays should be handled "
+           "separately");
+
     if (isBufferLayoutArray(SrcTy))
-      return emitBufferLayoutCopy(Src, SrcTy, Dst,
-                                  cast<llvm::ArrayType>(DstTy));
+      return emitBufferLayoutCopy(Src, SrcTy, Dst, cast<llvm::ArrayType>(DstTy),
+                                  EmitResFn);
 
     unsigned SrcIndex = 0;
     unsigned DstIndex = 0;
 
+    // DstTy layout is in default address space and can include resource types.
+    // SrcTy is in cbuffer layout where resources are filtered out, so the
+    // number of elements in SrcTy can be less than the number of elements in
+    // DstTy.
     auto *DstST = cast<llvm::StructType>(DstTy);
-    while (SrcIndex < SrcTy->getNumElements() &&
-           DstIndex < DstST->getNumElements()) {
-      if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(
-              SrcTy->getElementType(SrcIndex))) {
-        SrcIndex += 1;
+    while (DstIndex < DstST->getNumElements()) {
+      llvm::Type *DstEltTy = DstST->getElementType(DstIndex);
+      if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(DstEltTy)) {
+        DstIndex += 1;
         continue;
       }
-
-      if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(
-              DstST->getElementType(DstIndex))) {
+      if (isResourceOrResourceArray(DstEltTy)) {
+        auto *DstElt = emitAccessChain(
+            DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, DstIndex)});
+        emitResourceOrResourceArray(DstElt, DstEltTy, EmitResFn);
         DstIndex += 1;
         continue;
       }
 
+      assert(SrcIndex < SrcTy->getNumElements());
+      llvm::Type *SrcEltTy = SrcTy->getElementType(SrcIndex);
+      if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(SrcEltTy)) {
+        SrcIndex += 1;
+        continue;
+      }
+
       auto *SrcElt = emitAccessChain(
           SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, SrcIndex)});
       auto *DstElt = emitAccessChain(
           DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, DstIndex)});
-      emitElementCopy(SrcElt, SrcTy->getElementType(SrcIndex), DstElt,
-                      DstST->getElementType(DstIndex));
+      emitElementCopy(SrcElt, SrcEltTy, DstElt, DstEltTy, EmitResFn);
       DstIndex += 1;
       SrcIndex += 1;
     }
   }
 
   void emitCopy(Value *Src, llvm::ArrayType *SrcTy, Value *Dst,
-                llvm::Type *DstTy) {
+                llvm::Type *DstTy, EmitResourceFn EmitResFn) {
     for (unsigned I = 0, E = SrcTy->getNumElements(); I < E; ++I) {
       auto *SrcElt =
           emitAccessChain(SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, I)});
       auto *DstElt =
           emitAccessChain(DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, I)});
       emitElementCopy(SrcElt, SrcTy->getElementType(), DstElt,
-                      cast<llvm::ArrayType>(DstTy)->getElementType());
+                      cast<llvm::ArrayType>(DstTy)->getElementType(),
+                      EmitResFn);
     }
   }
 
   void emitElementCopy(Value *Src, llvm::Type *SrcTy, Value *Dst,
-                       llvm::Type *DstTy) {
+                       llvm::Type *DstTy, EmitResourceFn EmitResFn) {
     if (auto *AT = dyn_cast<llvm::ArrayType>(SrcTy))
-      return emitCopy(Src, AT, Dst, DstTy);
+      return emitCopy(Src, AT, Dst, DstTy, EmitResFn);
     if (auto *ST = dyn_cast<llvm::StructType>(SrcTy))
-      return emitCopy(Src, ST, Dst, DstTy);
+      return emitCopy(Src, ST, Dst, DstTy, EmitResFn);
 
     // When we have a scalar or vector element we can emit the copy.
     CharUnits SrcAlign =
@@ -618,7 +679,7 @@ class HLSLBufferCopyEmitter {
   HLSLBufferCopyEmitter(CodeGenFunction &CGF, Address DstPtr, Address SrcPtr)
       : CGF(CGF), DstPtr(DstPtr), SrcPtr(SrcPtr) {}
 
-  bool emitCopy(QualType CType) {
+  bool emitCopy(QualType CType, EmitResourceFn EmitResFn = nullptr) {
     LayoutTy = HLSLBufferLayoutBuilder(CGF.CGM).layOutType(CType);
 
     // TODO: We should be able to fall back to a regular memcpy if the layout
@@ -627,11 +688,53 @@ class HLSLBufferCopyEmitter {
     //
     // See https://github.com/llvm/wg-hlsl/issues/351
     emitElementCopy(SrcPtr.getBasePointer(), LayoutTy, DstPtr.getBasePointer(),
-                    DstPtr.getElementType());
+                    DstPtr.getElementType(), EmitResFn);
     return true;
   }
 };
 
+// Represents a list resources associated with a global struct whose name
+// starts with the specified prefix.
+// The order of HLSLAssociatedResourceDeclAttr attributes is identical to the
+// order of the depth-first traversal of the corresponding fields in the struct.
+// The resources are always returned in that order, which is the same order
+// we need when a struct is copied element-by-element.
+class AssociatedResourcesList {
+  specific_attr_iterator<HLSLAssociatedResourceDeclAttr> Begin, End, Next;
+
+public:
+  AssociatedResourcesList(const VarDecl *StructVD,
+                          StringRef ResourceNamePrefix) {
+    auto I = StructVD->specific_attr_begin<HLSLAssociatedResourceDeclAttr>();
+    auto E = StructVD->specific_attr_end<HLSLAssociatedResourceDeclAttr>();
+
+    // Skip over associated resources that don't match the prefix.
+    while (I != E &&
+           !I->getResDecl()->getName().starts_with(ResourceNamePrefix))
+      ++I;
+    assert(I != E && "expected associated resource not found");
+    Begin = End = I;
+
+    // Scan over associated resources that do match the prefix to find the end
+    // of the range.
+    while (I != E && ((HLSLAssociatedResourceDeclAttr *)*I)
+                         ->getResDecl()
+                         ->getName()
+                         .starts_with(ResourceNamePrefix))
+      End = ++I;
+    Next = Begin;
+  }
+
+  const VarDecl *getNextResource() {
+    if (Next == End)
+      return nullptr;
+
+    const VarDecl *Res = Next->getResDecl();
+    ++Next;
+    return Res;
+  }
+};
+
 } // namespace
 
 llvm::Type *
@@ -1917,9 +2020,53 @@ CGHLSLRuntime::emitResourceMemberExpr(CodeGenFunction &CGF,
   return LV;
 }
 
-bool CGHLSLRuntime::emitBufferCopy(CodeGenFunction &CGF, Address DstPtr,
-                                   Address SrcPtr, QualType CType) {
-  return HLSLBufferCopyEmitter(CGF, DstPtr, SrcPtr).emitCopy(CType);
+bool CGHLSLRuntime::emitBufferCopy(CodeGenFunction &CGF, const Expr *E,
+                                   const LValue &SrcLV,
+                                   AggValueSlot &DestSlot) {
+  assert(E->getType().getAddressSpace() == LangAS::hlsl_constant &&
+         "expected expression in HLSL constant address space");
+  assert(!E->getType()->isHLSLResourceRecord() &&
+         !E->getType()->isHLSLResourceRecordArray() &&
+         "direct accesses to resource types should be handled separately");
+
+  if (DestSlot.isIgnored())
+    return false;
+
+  QualType Ty = E->getType();
+  Address DstPtr = DestSlot.getAddress();
+  Address SrcPtr = SrcLV.getAddress();
+
+  // If there are no intangible types, we don't need to lookup associated
+  // resources.
+  if (!Ty->isHLSLIntangibleType())
+    return HLSLBufferCopyEmitter(CGF, DstPtr, SrcPtr).emitCopy(Ty);
+
+  // Handle structs with intangible types by setting the resource fields
+  // of the destination struct with the resources associated with the global
+  // struct.
+  EmbeddedResourceNameBuilder NameBuilder;
+  const VarDecl *VD = findStructResourceParentDeclAndBuildName(E, NameBuilder);
+  AssociatedResourcesList AssociatedResources(VD, NameBuilder.getName());
+
+  // Callback to fill in the associated resource.
+  auto EmitResFn = [&](AggValueSlot &ResSlot) {
+    const VarDecl *ResDecl = AssociatedResources.getNextResource();
+    assert(ResDecl && "associated resource declaration not found");
+
+    // Check that the resource type of dest and src matches.
+    [[maybe_unused]] llvm::Type *DestType =
+        ResSlot.getAddress().getElementType();
+    [[maybe_unused]] llvm::Type *SrcConvertedType =
+        CGM.getTypes().ConvertTypeForMem(ResDecl->getType());
+    assert(DestType == SrcConvertedType && "resource slot type mismatch");
+
+    if (ResDecl->getType()->isHLSLResourceRecord())
+      copyGlobalResource(CGF, ResDecl, ResSlot);
+    else
+      initializeGlobalResourceArray(CGF, ResDecl, ResSlot);
+  };
+
+  return HLSLBufferCopyEmitter(CGF, DstPtr, SrcPtr).emitCopy(Ty, EmitResFn);
 }
 
 LValue CGHLSLRuntime::emitBufferMemberExpr(CodeGenFunction &CGF,
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index cafa073fc4e68..b936030df33d1 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -315,8 +315,8 @@ class CGHLSLRuntime {
   RawAddress createBufferMatrixTempAddress(const LValue &LV,
                                            CodeGenFunction &CGF);
 
-  bool emitBufferCopy(CodeGenFunction &CGF, Address DestPtr, Address SrcPtr,
-                      QualType CType);
+  bool emitBufferCopy(CodeGenFunction &CGF, const Expr *E, const LValue &SrcLV,
+                      AggValueSlot &DestSlot);
 
   LValue emitBufferMemberExpr(CodeGenFunction &CGF, const MemberExpr *E);
   std::optional<LValue> emitResourceMemberExpr(CodeGenFunction &CGF,
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
index 9a18e84b7339d..b09fc4c91df2a 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -61,6 +61,8 @@ HLSLBufferLayoutBuilder::layOutStruct(const RecordType *RT,
   CharUnits CurrentOffset = CharUnits::Zero();
   for (auto &[FD, Offset] : FieldsWithOffset) {
     llvm::Type *LayoutType = layOutType(FD->getType());
+    if (!LayoutType)
+      continue;
 
     const llvm::DataLayout &DL = CGM.getDataLayout();
     CharUnits Size =
@@ -142,6 +144,10 @@ llvm::Type *HLSLBufferLayoutBuilder::layOutMatrix(QualType Ty) {
 }
 
 llvm::Type *HLSLBufferLayoutBuilder::layOutType(QualType Ty) {
+  // HLSL resource types are not included in the buffer layout.
+  if (Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray())
+    return nullptr;
+
   if (const auto *AT = CGM.getContext().getAsConstantArrayType(Ty))
     return layOutArray(AT);
 
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
index 8f1530308bf30..4e7fb3e11768f 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
@@ -38,6 +38,7 @@ class HLSLBufferLayoutBuilder {
   ///
   /// The function iterates over all fields of the record type (including base
   /// classes) and works out a padded llvm type to represent the buffer layout.
+  /// If the field is a resource or resource array, it will be ignored.
   ///
   /// If a non-empty OffsetInfo is provided (ie, from `packoffset` annotations
   /// in the source), any provided offsets offsets will be respected. If the
diff --git a/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing_with_resources.hlsl b/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing_with_resources.hlsl
new file mode 100644
index 0000000000000..8c2f03e23b879
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing_with_resources.hlsl
@@ -0,0 +1,385 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s
+
+struct MyStruct {
+  int a;
+  RWBuffer<float> Buf;
+};
+
+struct MyStructWithCounter {
+  RWStructuredBuffer<int> StructBuf;
+  float f;
+};
+
+struct WrappaStruct {
+  float b;
+  MyStruct s[2];
+  RWStructuredBuffer<int> BufArray[2];
+};
+
+cbuffer CB {
+  MyStruct cbs;
+  MyStructWithCounter cbsWithCounter;
+  WrappaStruct cbw;
+}
+
+// Resource record types
+// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) }
+// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) }
+
+// cbuffer layout structs
+// CHECK: %__cblayout_CB = type <{ %__cblayout_MyStruct, target("dx.Padding", 12), %__cblayout_MyStructWithCounter, target("dx.Padding", 12), %__cblayout_WrappaStruct }>
+// CHECK: %__cblayout_MyStruct = type <{ i32 }>
+// CHECK: %__cblayout_MyStructWithCounter = type <{ float }>
+// CHECK: %__cblayout_WrappaStruct = type <{ float, target("dx.Padding", 12), <{ [1 x <{ %__cblayout_MyStruct, target("dx.Padding", 12) }>], %__cblayout_MyStruct }> }>
+
+// struct in default address space
+// CHECK: %struct.MyStruct = type { i32, %"class.hlsl::RWBuffer" }
+// CHECK: %struct.MyStructWithCounter = type { %"class.hlsl::RWStructuredBuffer", float }
+// CHECK: %struct.WrappaStruct = type { float, [2 x %struct.MyStruct], [2 x %"class.hlsl::RWStructuredBuffer"] }
+
+// Resource globals associated with the cbuffer structs.
+// Only individual resources have globals. Resource arrays such as WrappaStruct::BufArray
+// are initialized on access.
+// CHECK: @cbs.Buf = internal global %"class.hlsl::RWBuffer" poison, align 4
+// CHECK: @cbsWithCounter.StructBuf = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4
+// CHECK: @cbw.s.0.Buf...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/204232


More information about the llvm-branch-commits mailing list