https://github.com/hekota created 
https://github.com/llvm/llvm-project/pull/152454

Adds support for accessing individual resources from fixed-size resource arrays 
declared at global scope. When a global resource array is indexed to retrieve a 
specific resource, the codegen translates the `ArraySubscriptExpr` AST node to 
a constructor call for the corresponding resource record type and binding.

Closes #145424

>From 86902233a96b26b710bd39c096cb581f252e09a4 Mon Sep 17 00:00:00 2001
From: Helena Kotas <heko...@microsoft.com>
Date: Thu, 7 Aug 2025 01:30:36 -0700
Subject: [PATCH] [HLSL] Global resource arrays element access

Adds support for accessing individual resources from fixed-size resource
arrays declared at global scope. When a global resource array is indexed
 to retrieve a specific resource, the codegen translates the 
`ArraySubscriptExpr`
into a constructor call for the corresponding resource record type and binding.

Closes #145424
---
 clang/include/clang/Sema/SemaHLSL.h           |   9 +-
 clang/lib/CodeGen/CGExpr.cpp                  |  10 +
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 223 +++++++++++++++++-
 clang/lib/CodeGen/CGHLSLRuntime.h             |   6 +
 clang/lib/CodeGen/CodeGenModule.cpp           |   4 +-
 clang/lib/Sema/SemaHLSL.cpp                   |  93 ++++++--
 .../resources/res-array-global-multi-dim.hlsl |  32 +++
 .../resources/res-array-global.hlsl           |  59 +++++
 clang/test/CodeGenHLSL/static-local-ctor.hlsl |   5 +-
 9 files changed, 401 insertions(+), 40 deletions(-)
 create mode 100644 
clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl
 create mode 100644 clang/test/CodeGenHLSL/resources/res-array-global.hlsl

diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 085c9ed9f3ebd..0c215c6e10013 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -229,10 +229,17 @@ class SemaHLSL : public SemaBase {
 
   void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
 
-  bool initGlobalResourceDecl(VarDecl *VD);
   uint32_t getNextImplicitBindingOrderID() {
     return ImplicitBindingNextOrderID++;
   }
+
+  bool initGlobalResourceDecl(VarDecl *VD);
+  bool initGlobalResourceArrayDecl(VarDecl *VD);
+  void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName,
+                                    HLSLResourceBindingAttr *RBA,
+                                    HLSLVkBindingAttr *VkBinding,
+                                    uint32_t ArrayIndex,
+                                    llvm::SmallVector<Expr *> &Args);
 };
 
 } // namespace clang
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index ed35a055d8a7f..8c34fb501a3b8 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -16,6 +16,7 @@
 #include "CGCall.h"
 #include "CGCleanup.h"
 #include "CGDebugInfo.h"
+#include "CGHLSLRuntime.h"
 #include "CGObjCRuntime.h"
 #include "CGOpenMPRuntime.h"
 #include "CGRecordLayout.h"
@@ -4532,6 +4533,15 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const 
ArraySubscriptExpr *E,
                                  LHS.getBaseInfo(), TBAAAccessInfo());
   }
 
+  // The HLSL runtime handle the subscript expression on global resource 
arrays.
+  if (getLangOpts().HLSL && (E->getType()->isHLSLResourceRecord() ||
+                             E->getType()->isHLSLResourceRecordArray())) {
+    std::optional<LValue> LV =
+        CGM.getHLSLRuntime().emitResourceArraySubscriptExpr(E, *this);
+    if (LV.has_value())
+      return *LV;
+  }
+
   // All the other cases basically behave like simple offsetting.
 
   // Handle the extvector case we ignored above.
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 918cb3e38448d..a09e540367a18 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -84,6 +84,124 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion 
RootSigVer,
   RootSignatureValMD->addOperand(MDVals);
 }
 
+// If the specified expr is a simple decay from an array to pointer,
+// return the array subexpression. Otherwise, return nullptr.
+static const Expr *getSubExprFromArrayDecayOperand(const Expr *E) {
+  const auto *CE = dyn_cast<CastExpr>(E);
+  if (!CE || CE->getCastKind() != CK_ArrayToPointerDecay)
+    return nullptr;
+  return CE->getSubExpr();
+}
+
+// Find array variable declaration from nested array subscript AST nodes
+static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
+  const Expr *E = nullptr;
+  while (ASE != nullptr) {
+    E = getSubExprFromArrayDecayOperand(ASE->getBase());
+    if (!E)
+      return nullptr;
+    ASE = dyn_cast<ArraySubscriptExpr>(E);
+  }
+  if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E))
+    return DRE->getDecl();
+  return nullptr;
+}
+
+// Get the total size of the array, or -1 if the array is unbounded.
+static int getTotalArraySize(const clang::Type *Ty) {
+  assert(Ty->isArrayType() && "expected array type");
+  if (Ty->isIncompleteArrayType())
+    return -1;
+  int Size = 1;
+  while (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) {
+    Size *= CAT->getSExtSize();
+    Ty = CAT->getArrayElementTypeNoTypeQual();
+  }
+  return Size;
+}
+
+// Find constructor decl for a specific resource record type and binding
+// (implicit vs. explicit). The constructor has 6 parameters.
+// For explicit binding the signature is:
+//   void(unsigned, unsigned, int, unsigned, const char *).
+// For implicit binding the signature is:
+//   void(unsigned, int, unsigned, unsigned, const char *).
+static CXXConstructorDecl *findResourceConstructorDecl(ASTContext &AST,
+                                                       QualType ResTy,
+                                                       bool ExplicitBinding) {
+  SmallVector<QualType> ExpParmTypes = {
+      AST.UnsignedIntTy, AST.UnsignedIntTy, AST.UnsignedIntTy,
+      AST.UnsignedIntTy, AST.getPointerType(AST.CharTy.withConst())};
+  ExpParmTypes[ExplicitBinding ? 2 : 1] = AST.IntTy;
+
+  CXXRecordDecl *ResDecl = ResTy->getAsCXXRecordDecl();
+  for (auto *Ctor : ResDecl->ctors()) {
+    if (Ctor->getNumParams() != ExpParmTypes.size())
+      continue;
+    ParmVarDecl **ParmIt = Ctor->param_begin();
+    QualType *ExpTyIt = ExpParmTypes.begin();
+    for (; ParmIt != Ctor->param_end() && ExpTyIt != ExpParmTypes.end();
+         ++ParmIt, ++ExpTyIt) {
+      if ((*ParmIt)->getType() != *ExpTyIt)
+        break;
+    }
+    if (ParmIt == Ctor->param_end())
+      return Ctor;
+  }
+  llvm_unreachable("did not find constructor for resource class");
+}
+
+static Value *buildNameForResource(llvm::StringRef BaseName,
+                                   CodeGenModule &CGM) {
+  std::string Str(BaseName);
+  std::string GlobalName(Str + ".str");
+  return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
+}
+
+static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD,
+                                   llvm::Value *ThisPtr, llvm::Value *Range,
+                                   llvm::Value *Index, StringRef Name,
+                                   HLSLResourceBindingAttr *RBA,
+                                   HLSLVkBindingAttr *VkBinding,
+                                   CallArgList &Args) {
+  assert((VkBinding || RBA) && "at least one a binding attribute expected");
+
+  std::optional<uint32_t> RegisterSlot;
+  uint32_t SpaceNo = 0;
+  if (VkBinding) {
+    RegisterSlot = VkBinding->getBinding();
+    SpaceNo = VkBinding->getSet();
+  } else if (RBA) {
+    if (RBA->hasRegisterSlot())
+      RegisterSlot = RBA->getSlotNumber();
+    SpaceNo = RBA->getSpaceNumber();
+  }
+
+  ASTContext &AST = CD->getASTContext();
+  Value *NameStr = buildNameForResource(Name, CGM);
+  Value *Space = llvm::ConstantInt::get(CGM.IntTy, SpaceNo);
+
+  Args.add(RValue::get(ThisPtr), CD->getThisType());
+  if (RegisterSlot.has_value()) {
+    // explicit binding
+    auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RegisterSlot.value());
+    Args.add(RValue::get(RegSlot), AST.UnsignedIntTy);
+    Args.add(RValue::get(Space), AST.UnsignedIntTy);
+    Args.add(RValue::get(Range), AST.IntTy);
+    Args.add(RValue::get(Index), AST.UnsignedIntTy);
+
+  } else {
+    // implicit binding
+    auto *OrderID =
+        llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID());
+    Args.add(RValue::get(Space), AST.UnsignedIntTy);
+    Args.add(RValue::get(Range), AST.IntTy);
+    Args.add(RValue::get(Index), AST.UnsignedIntTy);
+    Args.add(RValue::get(OrderID), AST.UnsignedIntTy);
+  }
+  Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst()));
+}
+
 } // namespace
 
 llvm::Type *
@@ -590,13 +708,6 @@ static void initializeBuffer(CodeGenModule &CGM, 
llvm::GlobalVariable *GV,
   CGM.AddCXXGlobalInit(InitResFunc);
 }
 
-static Value *buildNameForResource(llvm::StringRef BaseName,
-                                   CodeGenModule &CGM) {
-  std::string Str(BaseName);
-  std::string GlobalName(Str + ".str");
-  return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
-}
-
 void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
                                                 llvm::GlobalVariable *GV,
                                                 HLSLVkBindingAttr *VkBinding) {
@@ -624,17 +735,13 @@ void CGHLSLRuntime::initializeBufferFromBinding(const 
HLSLBufferDecl *BufDecl,
   auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0);
   auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1);
   auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber());
-  Value *Name = nullptr;
+  Value *Name = buildNameForResource(BufDecl->getName(), CGM);
 
   llvm::Intrinsic::ID IntrinsicID =
       RBA->hasRegisterSlot()
           ? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic()
           : CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();
 
-  std::string Str(BufDecl->getName());
-  std::string GlobalName(Str + ".str");
-  Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
-
   // buffer with explicit binding
   if (RBA->hasRegisterSlot()) {
     auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
@@ -701,3 +808,95 @@ void 
CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
     }
   }
 }
+
+std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
+    const ArraySubscriptExpr *ArraySubsExpr, CodeGenFunction &CGF) {
+  assert(ArraySubsExpr->getType()->isHLSLResourceRecord() ||
+         ArraySubsExpr->getType()->isHLSLResourceRecordArray() &&
+             "expected resource array subscript expression");
+
+  // let clang codegen handle local resource array subscrips
+  const VarDecl *ArrayDecl = dyn_cast<VarDecl>(getArrayDecl(ArraySubsExpr));
+  if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
+    return std::nullopt;
+
+  // FIXME: this is not yet implemented (llvm/llvm-project#145426)
+  assert(!ArraySubsExpr->getType()->isArrayType() &&
+         "indexing of array subsets it not supported yet");
+
+  // get total array size (= range size)
+  const Type *ResArrayTy = ArrayDecl->getType().getTypePtr();
+  assert(ResArrayTy->isHLSLResourceRecordArray() &&
+         "expected array of resource classes");
+  llvm::Value *Range =
+      llvm::ConstantInt::get(CGM.IntTy, getTotalArraySize(ResArrayTy));
+
+  // Iterate through all nested array subscript expressions to calculate
+  // the index in the flattened resource array (if this is a multi-
+  // dimensional array). The index is calculated as a sum of all indices
+  // multiplied by the total size of the array at that level.
+  Value *Index = nullptr;
+  Value *Multiplier = nullptr;
+  const ArraySubscriptExpr *ASE = ArraySubsExpr;
+  while (ASE != nullptr) {
+    Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx());
+    if (const auto *ArrayTy =
+            dyn_cast<ConstantArrayType>(ASE->getType().getTypePtr())) {
+      Value *SubMultiplier =
+          llvm::ConstantInt::get(CGM.IntTy, ArrayTy->getSExtSize());
+      Multiplier = Multiplier ? CGF.Builder.CreateMul(Multiplier, 
SubMultiplier)
+                              : SubMultiplier;
+      SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier);
+    }
+
+    Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex;
+    ASE = dyn_cast<ArraySubscriptExpr>(
+        getSubExprFromArrayDecayOperand(ASE->getBase()));
+  }
+
+  // find binding info for the resource array
+  // (for implicit binding an HLSLResourceBindingAttr should have been added by
+  // SemaHLSL)
+  QualType ResourceTy = ArraySubsExpr->getType();
+  HLSLVkBindingAttr *VkBinding = ArrayDecl->getAttr<HLSLVkBindingAttr>();
+  HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>();
+  assert((VkBinding || RBA) && "resource array must have a binding attribute");
+
+  // lookup the resource class constructor based on the resource type and
+  // binding
+  CXXConstructorDecl *CD =
+      findResourceConstructorDecl(ArrayDecl->getASTContext(), ResourceTy,
+                                  VkBinding || RBA->hasRegisterSlot());
+
+  // create a temporary variable for the resource class instance (we need to
+  // return an LValue)
+  RawAddress TmpVar = CGF.CreateMemTemp(ResourceTy);
+  if (auto *Size = CGF.EmitLifetimeStart(
+          CGM.getDataLayout().getTypeAllocSize(TmpVar.getElementType()),
+          TmpVar.getPointer())) {
+    CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>(
+        NormalEHLifetimeMarker, TmpVar, Size);
+  }
+  AggValueSlot ValueSlot = AggValueSlot::forAddr(
+      TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true),
+      AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
+      AggValueSlot::MayOverlap);
+
+  Address ThisAddress = ValueSlot.getAddress();
+  llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(
+      ThisAddress, CD->getThisType()->getPointeeType());
+
+  // assemble the constructor parameters
+  CallArgList Args;
+  createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(),
+                         RBA, VkBinding, Args);
+
+  // call the constructor
+  CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress, 
Args,
+                             ValueSlot.mayOverlap(),
+                             ArraySubsExpr->getExprLoc(),
+                             ValueSlot.isSanitizerChecked());
+
+  return CGF.MakeAddrLValue(TmpVar, ArraySubsExpr->getType(),
+                            AlignmentSource::Decl);
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index 31d1728da9c56..b872f9ef0e9b6 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -68,6 +68,7 @@ class Type;
 class RecordType;
 class DeclContext;
 class HLSLPackOffsetAttr;
+class ArraySubscriptExpr;
 
 class FunctionDecl;
 
@@ -75,6 +76,7 @@ namespace CodeGen {
 
 class CodeGenModule;
 class CodeGenFunction;
+class LValue;
 
 class CGHLSLRuntime {
 public:
@@ -164,6 +166,10 @@ class CGHLSLRuntime {
                                llvm::TargetExtType *LayoutTy);
   void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E);
 
+  std::optional<LValue>
+  emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
+                                 CodeGenFunction &CGF);
+
 private:
   void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
                                     llvm::GlobalVariable *BufGV);
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 834b1c067d84c..1498c5d75fa53 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5775,8 +5775,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl 
*D,
       if (D->getType()->isReferenceType())
         T = D->getType();
 
-      if (getLangOpts().HLSL &&
-          D->getType().getTypePtr()->isHLSLResourceRecord()) {
+      if (getLangOpts().HLSL && (D->getType()->isHLSLResourceRecord() ||
+                                 D->getType()->isHLSLResourceRecordArray())) {
         Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
         NeedsGlobalCtor = true;
       } else if (getLangOpts().CPlusPlus) {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6811f3f27603b..7de529fc898ad 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -357,6 +357,14 @@ getResourceArrayHandleType(VarDecl *VD) {
   return HLSLAttributedResourceType::findHandleTypeOnResource(Ty);
 }
 
+// returns the element type of an array (including multi-dimensional array)
+static QualType getArrayElementType(QualType Ty) {
+  assert(Ty->isArrayType() && "expected array type");
+  while (const ArrayType *AT = dyn_cast<ArrayType>(Ty.getTypePtr()))
+    Ty = AT->getElementType();
+  return Ty;
+}
+
 // Returns true if the type is a leaf element type that is not valid to be
 // included in HLSL Buffer, such as a resource class, empty struct, zero-sized
 // array, or a builtin intangible type. Returns false it is a valid leaf 
element
@@ -3698,11 +3706,14 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
   return true;
 }
 
-bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
+void SemaHLSL::createResourceRecordCtorArgs(const Type *ResourceTy,
+                                            StringRef VarName,
+                                            HLSLResourceBindingAttr *RBA,
+                                            HLSLVkBindingAttr *VkBinding,
+                                            uint32_t ArrayIndex,
+                                            llvm::SmallVector<Expr *> &Args) {
   std::optional<uint32_t> RegisterSlot;
   uint32_t SpaceNo = 0;
-  HLSLVkBindingAttr *VkBinding = VD->getAttr<HLSLVkBindingAttr>();
-  HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
   if (VkBinding) {
     RegisterSlot = VkBinding->getBinding();
     SpaceNo = VkBinding->getSet();
@@ -3717,12 +3728,12 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
   uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
   IntegerLiteral *RangeSize = IntegerLiteral::Create(
       AST, llvm::APInt(IntTySize, 1), AST.IntTy, SourceLocation());
-  IntegerLiteral *Index = IntegerLiteral::Create(
-      AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy, SourceLocation());
+  IntegerLiteral *Index =
+      IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, ArrayIndex),
+                             AST.UnsignedIntTy, SourceLocation());
   IntegerLiteral *Space =
       IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo),
                              AST.UnsignedIntTy, SourceLocation());
-  StringRef VarName = VD->getName();
   StringLiteral *Name = StringLiteral::Create(
       AST, VarName, StringLiteralKind::Ordinary, false,
       AST.getStringLiteralArrayType(AST.CharTy.withConst(), VarName.size()),
@@ -3733,18 +3744,57 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
     IntegerLiteral *RegSlot = IntegerLiteral::Create(
         AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy,
         SourceLocation());
-    Expr *Args[] = {RegSlot, Space, RangeSize, Index, Name};
-    return initVarDeclWithCtor(SemaRef, VD, Args);
+    Args.append({RegSlot, Space, RangeSize, Index, Name});
+  } else {
+    // resource with implicit binding
+    uint32_t OrderID = (RBA && RBA->hasImplicitBindingOrderID())
+                           ? RBA->getImplicitBindingOrderID()
+                           : getNextImplicitBindingOrderID();
+    IntegerLiteral *OrderId =
+        IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, OrderID),
+                               AST.UnsignedIntTy, SourceLocation());
+    Args.append({Space, RangeSize, Index, OrderId, Name});
   }
+}
 
-  // resource with implicit binding
-  IntegerLiteral *OrderId = IntegerLiteral::Create(
-      AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()),
-      AST.UnsignedIntTy, SourceLocation());
-  Expr *Args[] = {Space, RangeSize, Index, OrderId, Name};
+bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
+  SmallVector<Expr *> Args;
+  createResourceRecordCtorArgs(VD->getType().getTypePtr(), VD->getName(),
+                               VD->getAttr<HLSLResourceBindingAttr>(),
+                               VD->getAttr<HLSLVkBindingAttr>(), 0, Args);
   return initVarDeclWithCtor(SemaRef, VD, Args);
 }
 
+bool SemaHLSL::initGlobalResourceArrayDecl(VarDecl *VD) {
+  assert(VD->getType()->isHLSLResourceRecordArray() &&
+         "expected array of resource records");
+
+  // Individual resources in a resource array are not initialized here. They
+  // are initialized later on during codegen when the individual resources are
+  // accessed. Codegen will emit a call to the resource constructor with the
+  // specified array index. We need to make sure though that the constructor
+  // for the specific resource type is instantiated, so codegen can emit a call
+  // to it when the array element is accessed.
+  SmallVector<Expr *> Args;
+  QualType ResElementTy = getArrayElementType(VD->getType());
+  createResourceRecordCtorArgs(ResElementTy.getTypePtr(), VD->getName(),
+                               VD->getAttr<HLSLResourceBindingAttr>(),
+                               VD->getAttr<HLSLVkBindingAttr>(), 0, Args);
+
+  SourceLocation Loc = VD->getLocation();
+  InitializedEntity Entity =
+      InitializedEntity::InitializeTemporary(ResElementTy);
+  InitializationKind Kind = InitializationKind::CreateDirect(Loc, Loc, Loc);
+  InitializationSequence InitSeq(SemaRef, Entity, Kind, Args);
+  if (InitSeq.Failed())
+    return false;
+
+  // This takes care of instantiating and emitting of the constructor that will
+  // be called from codegen when the array is accessed.
+  ExprResult OneResInit = InitSeq.Perform(SemaRef, Entity, Kind, Args);
+  return !OneResInit.isInvalid();
+}
+
 // Returns true if the initialization has been handled.
 // Returns false to use default initialization.
 bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
@@ -3753,17 +3803,14 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
   if (VD->getType().getAddressSpace() == LangAS::hlsl_constant)
     return true;
 
-  // Initialize resources
-  if (!isResourceRecordTypeOrArrayOf(VD))
-    return false;
-
-  // FIXME: We currectly support only simple resources - no arrays of resources
-  // or resources in user defined structs.
-  // (llvm/llvm-project#133835, llvm/llvm-project#133837)
   // Initialize resources at the global scope
-  if (VD->hasGlobalStorage() && VD->getType()->isHLSLResourceRecord())
-    return initGlobalResourceDecl(VD);
-
+  if (VD->hasGlobalStorage()) {
+    const Type *Ty = VD->getType().getTypePtr();
+    if (Ty->isHLSLResourceRecord())
+      return initGlobalResourceDecl(VD);
+    if (Ty->isHLSLResourceRecordArray())
+      return initGlobalResourceArrayDecl(VD);
+  }
   return false;
 }
 
diff --git a/clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl 
b/clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl
new file mode 100644
index 0000000000000..97df14769b132
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute 
-finclude-default-header \
+// RUN:   -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+// RUN: %clang_cc1 -finclude-default-header -triple 
spirv-unknown-vulkan-compute \
+// RUN:   -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+
+// CHECK: @[[BufB:.*]] = private unnamed_addr constant [2 x i8] c"B\00", align 
1
+// CHECK: @[[BufC:.*]] = private unnamed_addr constant [2 x i8] c"C\00", align 
1
+// CHECK: @[[BufD:.*]] = private unnamed_addr constant [2 x i8] c"D\00", align 
1
+
+RWBuffer<float> B[4][4] : register(u2);
+RWBuffer<int> C[2][2][5] : register(u10, space1);
+RWBuffer<uint> D[10][5]; // implicit binding -> u18, space0
+
+RWStructuredBuffer<float> Out;
+
+[numthreads(4,1,1)]
+void main() {
+  // CHECK: define internal{{.*}} void @_Z4mainv()
+  // CHECK: %[[Tmp0:.*]] = alloca %"class.hlsl::RWBuffer
+  // CHECK: %[[Tmp1:.*]] = alloca %"class.hlsl::RWBuffer
+  // CHECK: %[[Tmp2:.*]] = alloca %"class.hlsl::RWBuffer
+
+  // Make sure that B[2][3] is translated to a RWBuffer<float> constructor 
call for explicit binding (u2, space0) with range 16 and index 14
+  // CHECK: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], 
i32 noundef 2, i32 noundef 0, i32 noundef 16, i32 noundef 14, ptr noundef 
@[[BufB]])
+
+  // Make sure that C[1][0][3] is translated to a RWBuffer<int> constructor 
call for explicit binding (u10, space1) with range 20 and index 13
+  // CHECK: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp1]], 
i32 noundef 10, i32 noundef 1, i32 noundef 20, i32 noundef 13, ptr noundef 
@[[BufC]])
+
+  // Make sure that D[9][2] is translated to a RWBuffer<uint> constructor call 
for implicit binding (u18, space0) with range 50 and index 47
+  // CHECK: call void @_ZN4hlsl8RWBufferIjEC1EjijjPKc(ptr {{.*}} %[[Tmp2]], 
i32 noundef 0, i32 noundef 50, i32 noundef 47, i32 noundef 0, ptr noundef 
@[[BufD]])
+  Out[0] =  B[3][2][0] + (float)C[1][0][3][0] + (float)D[9][2][0];
+}
diff --git a/clang/test/CodeGenHLSL/resources/res-array-global.hlsl 
b/clang/test/CodeGenHLSL/resources/res-array-global.hlsl
new file mode 100644
index 0000000000000..26526e9a5c9d8
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/res-array-global.hlsl
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute 
-finclude-default-header \
+// RUN:   -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
-check-prefixes=CHECK,DXIL
+// RUN: %clang_cc1 -finclude-default-header -triple 
spirv-unknown-vulkan-compute \
+// RUN:   -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
-check-prefixes=CHECK,SPV
+
+// CHECK: @[[BufA:.*]] = private unnamed_addr constant [2 x i8] c"A\00", align 
1
+// CHECK: @[[BufB:.*]] = private unnamed_addr constant [2 x i8] c"B\00", align 
1
+// CHECK: @[[BufC:.*]] = private unnamed_addr constant [2 x i8] c"C\00", align 
1
+// CHECK: @[[BufD:.*]] = private unnamed_addr constant [2 x i8] c"D\00", align 
1
+
+// different explicit binding for DXIL and SPIR-V
+[[vk::binding(12, 2)]]
+RWBuffer<float> A[4] : register(u10, space1);
+
+[[vk::binding(13)]] // SPIR-V explicit binding 13, set 0
+RWBuffer<int> B[5]; // DXIL implicit binding in space0
+
+// same explicit binding for both DXIL and SPIR-V
+// (SPIR-V takes the binding from register annotation if there is no 
vk::binding attribute))
+RWBuffer<int> C[3] : register(u2);
+
+// implicit binding for both DXIL and SPIR-V in space/set 0 
+RWBuffer<double> D[10];
+
+RWStructuredBuffer<float> Out;
+
+[numthreads(4,1,1)]
+void main() {
+  // CHECK: define internal{{.*}} void @_Z4mainv()
+  // CHECK: %[[Tmp0:.*]] = alloca %"class.hlsl::RWBuffer
+  // CHECK: %[[Tmp1:.*]] = alloca %"class.hlsl::RWBuffer
+  // CHECK: %[[Tmp2:.*]] = alloca %"class.hlsl::RWBuffer
+  // CHECK: %[[Tmp3:.*]] = alloca %"class.hlsl::RWBuffer
+
+  // Make sure A[2] is translated to a RWBuffer<float> constructor call with 
range 4 and index 2
+  // and DXIL explicit binding (u10, space1)
+  // and SPIR-V explicit binding (binding 12, set 2) 
+  // DXIL: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], i32 
noundef 10, i32 noundef 1, i32 noundef 4, i32 noundef 2, ptr noundef @[[BufA]])
+  // SPV: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], i32 
noundef 12, i32 noundef 2, i32 noundef 4, i32 noundef 2, ptr noundef @[[BufA]])
+
+  // Make sure B[3] is translated to a RWBuffer<int> constructor call with 
range 5 and index 3
+  // and DXIL for implicit binding in space0, order id 0
+  // and SPIR-V explicit binding (binding 13, set 0)
+  // DXIL: call void @_ZN4hlsl8RWBufferIiEC1EjijjPKc(ptr {{.*}} %[[Tmp1]], i32 
noundef 0, i32 noundef 5, i32 noundef 3, i32 noundef 0, ptr noundef @[[BufB]])
+  // SPV: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp1]], i32 
noundef 13, i32 noundef 0, i32 noundef 5, i32 noundef 3, ptr noundef @[[BufB]])
+
+  // Make sure C[1] is translated to a RWBuffer<int> constructor call with 
range 3 and index 1
+  // and DXIL explicit binding (u2, space0) 
+  // and SPIR-V explicit binding (binding 2, set 0)
+  // DXIL: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp2]], i32 
noundef 2, i32 noundef 0, i32 noundef 3, i32 noundef 1, ptr noundef @[[BufC]])
+  // SPV: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp2]], i32 
noundef 2, i32 noundef 0, i32 noundef 3, i32 noundef 1, ptr noundef @[[BufC]])
+
+  // Make sure D[7] is translated to a RWBuffer<doublet> constructor call with 
range 10 and index 7
+  // and DXIL for implicit binding in space0, order id 1
+  // and SPIR-V explicit binding (binding 13, set 0), order id 0
+  // DXIL: call void @_ZN4hlsl8RWBufferIdEC1EjijjPKc(ptr {{.*}} %[[Tmp3]], i32 
noundef 0, i32 noundef 10, i32 noundef 7, i32 noundef 1, ptr noundef @[[BufD]])
+  // SPV: call void @_ZN4hlsl8RWBufferIdEC1EjijjPKc(ptr {{.*}} %[[Tmp3]], i32 
noundef 0, i32 noundef 10, i32 noundef 7, i32 noundef 0, ptr noundef @[[BufD]])
+  Out[0] = A[2][0] + (float)B[3][0] + (float)C[1][0] + (float)D[7][0];
+}
diff --git a/clang/test/CodeGenHLSL/static-local-ctor.hlsl 
b/clang/test/CodeGenHLSL/static-local-ctor.hlsl
index 87f49b8bf7ac7..9a4bf66f030ed 100644
--- a/clang/test/CodeGenHLSL/static-local-ctor.hlsl
+++ b/clang/test/CodeGenHLSL/static-local-ctor.hlsl
@@ -2,7 +2,7 @@
 
 // Verify that no per variable _Init_thread instructions are emitted for 
non-trivial static locals
 // These would normally be emitted by the MicrosoftCXXABI, but the DirectX 
backend should exlude them
-// Instead, check for the guardvar oparations that should protect the 
constructor initialization should
+// Instead, check for the guardvar operations that should protect the 
constructor initialization should
 // only take place once.
 
 RWBuffer<int> buf[10];
@@ -15,13 +15,14 @@ void InitBuf(RWBuffer<int> buf) {
 // CHECK-NOT: _Init_thread_epoch
 // CHECK: define internal void @_Z4mainv
 // CHECK-NEXT: entry:
+// CHECK-NEXT: [[Tmp0:%.*]] = alloca %"class.hlsl::RWBuffer"
 // CHECK-NEXT: [[Tmp1:%.*]] = alloca %"class.hlsl::RWBuffer"
 // CHECK-NEXT: [[Tmp2:%.*]] = load i8, ptr @_ZGVZ4mainvE5mybuf
 // CHECK-NEXT: [[Tmp3:%.*]] = icmp eq i8 [[Tmp2]], 0
 // CHECK-NEXT: br i1 [[Tmp3]]
 // CHECK-NOT: _Init_thread_header
 // CHECK: init.check:
-// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1Ejijj
+// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1EjijjPKc(
 // CHECK-NEXT: store i8 1, ptr @_ZGVZ4mainvE5mybuf
 // CHECK-NOT: _Init_thread_footer
 

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to