https://github.com/hekota updated 
https://github.com/llvm/llvm-project/pull/166880

>From 883df7bce01ddaedd133813b7e79397b974e6835 Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Thu, 6 Nov 2025 12:37:29 -0800
Subject: [PATCH 1/6] [HLSL] Add internal linkage attribute to resources

HLSL resources should not be externally visible from the module.
We made sure of this by marking them `static` as soon as they were declared.
However, this prevents us fixing issue #166458 because there is no way
to know if a resource has been explicitly marked `static`
by the user, and can therefore be assigned to.

This change is moves from making all resources `static` to adding Clang internal
linkage attribute to all non-static resource declarations as a global scope.
Existing tests verify that there is no change in how the resource globals are
emitted: `internal global`.
---
 clang/lib/Sema/SemaHLSL.cpp      | 13 ++++++++-----
 clang/test/AST/HLSL/cbuffer.hlsl |  2 +-
 clang/test/AST/HLSL/private.hlsl |  2 +-
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a06c57b15c585..e95fe16e6cb6c 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3910,12 +3910,15 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
     if (VD->getType()->isHLSLIntangibleType())
       collectResourceBindingsOnVarDecl(VD);
 
-    if (isResourceRecordTypeOrArrayOf(VD) ||
-        VD->hasAttr<HLSLVkConstantIdAttr>()) {
-      // Make the variable for resources static. The global externally visible
-      // storage is accessed through the handle, which is a member. The 
variable
-      // itself is not externally visible.
+    if (VD->hasAttr<HLSLVkConstantIdAttr>())
       VD->setStorageClass(StorageClass::SC_Static);
+
+    if (isResourceRecordTypeOrArrayOf(VD) &&
+        VD->getStorageClass() != SC_Static) {
+      // Add internal linkage attribute to non-static resource variables. The
+      // global externally visible storage is accessed through the handle, 
which
+      // is a member. The variable itself is not externally visible.
+      VD->addAttr(InternalLinkageAttr::CreateImplicit(getASTContext()));
     }
 
     // process explicit bindings
diff --git a/clang/test/AST/HLSL/cbuffer.hlsl b/clang/test/AST/HLSL/cbuffer.hlsl
index f3c6636232798..b0b5b989e36c2 100644
--- a/clang/test/AST/HLSL/cbuffer.hlsl
+++ b/clang/test/AST/HLSL/cbuffer.hlsl
@@ -153,7 +153,7 @@ cbuffer CB {
   static float SV;
   // CHECK: VarDecl {{.*}} s7 'EmptyStruct' callinit
   EmptyStruct s7;
-  // CHECK: VarDecl {{.*}} Buf 'RWBuffer<float>':'hlsl::RWBuffer<float>' 
static callinit
+  // CHECK: VarDecl {{.*}} Buf 'RWBuffer<float>':'hlsl::RWBuffer<float>' 
callinit
   RWBuffer<float> Buf;
   // CHECK: VarDecl {{.*}} ea 'EmptyArrayTypedef':'float[10][0]'
   EmptyArrayTypedef ea;
diff --git a/clang/test/AST/HLSL/private.hlsl b/clang/test/AST/HLSL/private.hlsl
index e00afb8f5cbd8..ba7380ec3cfda 100644
--- a/clang/test/AST/HLSL/private.hlsl
+++ b/clang/test/AST/HLSL/private.hlsl
@@ -3,7 +3,7 @@
 // CHECK: VarDecl {{.*}} global_scalar 'hlsl_private int' static cinit
 static int global_scalar = 0;
 
-// CHECK: VarDecl {{.*}} global_buffer 
'RWBuffer<float>':'hlsl::RWBuffer<float>' static callinit
+// CHECK: VarDecl {{.*}} global_buffer 
'RWBuffer<float>':'hlsl::RWBuffer<float>' callinit
 RWBuffer<float> global_buffer;
 
 class A {

>From 58161a23f0e4241e679c4d0bcbd0c8d55cb9682d Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Thu, 6 Nov 2025 17:40:22 -0800
Subject: [PATCH 2/6] [HLSL] Fix static resources

- Enable assignment to static resource variables (fixes #166458)
- Initialize static resources and resource arrays with default constructor that 
sets the handle to poison
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp       |  5 +-
 clang/lib/CodeGen/CodeGenModule.cpp       |  3 +-
 clang/lib/Sema/SemaHLSL.cpp               | 12 ++--
 clang/test/SemaHLSL/static_resources.hlsl | 86 +++++++++++++++++++++++
 4 files changed, 98 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/SemaHLSL/static_resources.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index e392a12044a39..2cf601ca6a424 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1025,12 +1025,13 @@ std::optional<LValue> 
CGHLSLRuntime::emitResourceArraySubscriptExpr(
           ArraySubsExpr->getType()->isHLSLResourceRecordArray()) &&
          "expected resource array subscript expression");
 
-  // Let clang codegen handle local resource array subscripts,
+  // Let Clang codegen handle local and static resource array subscripts,
   // or when the subscript references on opaque expression (as part of
   // ArrayInitLoopExpr AST node).
   const VarDecl *ArrayDecl =
       dyn_cast_or_null<VarDecl>(getArrayDecl(ArraySubsExpr));
-  if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
+  if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
+      ArrayDecl->getStorageClass() == SC_Static)
     return std::nullopt;
 
   // get the resource array type
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 0fea57b2e1799..b1256daafcdec 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5918,7 +5918,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl 
*D,
              (D->getType()->isHLSLResourceRecord() ||
               D->getType()->isHLSLResourceRecordArray())) {
     Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
-    NeedsGlobalCtor = D->getType()->isHLSLResourceRecord();
+    NeedsGlobalCtor = D->getType()->isHLSLResourceRecord() ||
+                      D->getStorageClass() == SC_Static;
   } else if (D->hasAttr<LoaderUninitializedAttr>()) {
     Init = llvm::UndefValue::get(getTypes().ConvertTypeForMem(ASTTy));
   } else if (!InitExpr) {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e95fe16e6cb6c..1bcc074c080b2 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3924,7 +3924,9 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
     // process explicit bindings
     processExplicitBindingsOnDecl(VD);
 
-    if (VD->getType()->isHLSLResourceRecordArray()) {
+    // Add implicit binding attribute to non-static resource arrays.
+    if (VD->getType()->isHLSLResourceRecordArray() &&
+        VD->getStorageClass() != SC_Static) {
       // If the resource array does not have an explicit binding attribute,
       // create an implicit one. It will be used to transfer implicit binding
       // order_ID to codegen.
@@ -4118,8 +4120,8 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
   if (VD->getType().getAddressSpace() == LangAS::hlsl_constant)
     return true;
 
-  // Initialize resources at the global scope
-  if (VD->hasGlobalStorage()) {
+  // Initialize non-static resources at the global scope.
+  if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
     const Type *Ty = VD->getType().getTypePtr();
     if (Ty->isHLSLResourceRecord())
       return initGlobalResourceDecl(VD);
@@ -4143,10 +4145,10 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind 
Opc, Expr *LHSExpr,
   while (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
     E = ASE->getBase()->IgnoreParenImpCasts();
 
-  // Report error if LHS is a resource declared at a global scope.
+  // Report error if LHS is a non-static resource declared at a global scope.
   if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
     if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
-      if (VD->hasGlobalStorage()) {
+      if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
         // assignment to global resource is not allowed
         SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD;
         SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD;
diff --git a/clang/test/SemaHLSL/static_resources.hlsl 
b/clang/test/SemaHLSL/static_resources.hlsl
new file mode 100644
index 0000000000000..9997bdf6dca7b
--- /dev/null
+++ b/clang/test/SemaHLSL/static_resources.hlsl
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -emit-llvm 
-disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s
+
+// CHECK: [[ONE_STR:@.*]] = private unnamed_addr constant [4 x i8] c"One\00"
+// CHECK: [[ARRAY_STR:@.*]] = private unnamed_addr constant [6 x i8] 
c"Array\00"
+// CHECK-NOT: private unnamed_addr constant [{{[0-9]+}} x i8] c"Static
+
+RWBuffer<float> One : register(u1, space5);
+RWBuffer<float> Array[4][2] : register(u10, space6);
+
+// Check that the non-static resource One is initialized from binding on
+// startup (register 1, space 5).
+// CHECK: define internal void @__cxx_global_var_init{{.*}}
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned 
int, unsigned int, int, unsigned int, char const*)
+// CHECK-SAME: (ptr {{.*}} @One, i32 noundef 1, i32 noundef 5, i32 noundef 1, 
i32 noundef 0, ptr noundef [[ONE_STR]])
+
+// Note that non-static resource arrays are not initialized on startup.
+// The individual resources from the array are initialized on access.
+
+static RWBuffer<float> StaticOne;
+static RWBuffer<float> StaticArray[2];
+
+// Check that StaticOne resource is initialized on startup with the default
+// constructor and not from binding. It will initalize the handle to poison.
+// CHECK: define internal void @__cxx_global_var_init{{.*}}
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} 
@StaticOne)
+
+// Check that StaticArray elements are initialized on startup with the default
+// constructor and not from binding. The initializer will loop over the array
+// elements and call the default constructor for each one, setting the handle 
to poison.
+// CHECK: define internal void @__cxx_global_var_init{{.*}}
+// CHECK-NEXT: entry:
+// CHECK-NEXT: br label %arrayctor.loop
+// CHECK: arrayctor.loop:                                   ; preds = 
%arrayctor.loop, %entry
+// CHECK-NEXT:   %arrayctor.cur = phi ptr [ @StaticArray, %entry ], [ 
%arrayctor.next, %arrayctor.loop ]
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} 
%arrayctor.cur)
+// CHECK-NEXT: %arrayctor.next = getelementptr inbounds 
%"class.hlsl::RWBuffer", ptr %arrayctor.cur, i32 1
+// CHECK-NEXT: %arrayctor.done = icmp eq ptr %arrayctor.next, getelementptr 
inbounds (%"class.hlsl::RWBuffer", ptr @StaticArray, i32 2)
+// CHECK-NEXT: br i1 %arrayctor.done, label %arrayctor.cont, label 
%arrayctor.loop
+// CHECK: arrayctor.cont:                                   ; preds = 
%arrayctor.loop
+// CHECK-NEXT: ret void
+
+// No other global initialization routines should be present.
+// CHECK-NOT: define internal void @__cxx_global_var_init{{.*}}
+
+[numthreads(4,1,1)]
+void main() {
+// CHECK: define internal void @main()()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %[[TMP0:.*]] = alloca %"class.hlsl::RWBuffer"
+
+  static RWBuffer<float> StaticLocal;
+// Check that StaticLocal is initialized to by default constructor to poison 
and not from binding
+// call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} 
@main()::StaticLocal)
+
+  StaticLocal = Array[2][0];
+// A[2][0] is accessed here, so it should be initialized from binding 
(register 10, space 6, index 4),
+// and then assigned to StaticLocal using = operator.
+// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, 
unsigned int, int, unsigned int, char const*)
+// CHECK-SAME: (ptr {{.*}} %[[TMP0]], i32 noundef 10, i32 noundef 6, i32 
noundef 8, i32 noundef 4, ptr noundef [[ARRAY_STR]])
+// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr 
{{.*}} @main()::StaticLocal, ptr {{.*}} %[[TMP0]])
+
+  StaticOne = One;
+// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr 
{{.*}} @StaticOne, ptr {{.*}} @One)
+
+  StaticArray[1] = One;
+// CHECK-NEXT: call {{.*}} ptr 
@hlsl::RWBuffer<float>::operator=(hlsl::RWBuffer<float> const&)
+// CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x 
%"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), ptr {{.*}} @One)
+
+  StaticLocal[0] = 123;
+// CHECK-NEXT: %[[PTR0:.*]] = call {{.*}} ptr 
@hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} 
@main()::StaticLocal, i32 noundef 0)
+// CHECK-NEXT: store float 1.230000e+02, ptr %[[PTR0]]
+
+  StaticOne[1] = 456;
+// CHECK-NEXT: %[[PTR1:.*]] = call {{.*}} ptr 
@hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}}) @StaticOne, i32 
noundef 1)
+// CHECK-NEXT: store float 4.560000e+02, ptr %[[PTR1]], align 4
+
+  StaticArray[1][2] = 789;
+// CHECK-NEXT: %[[PTR2:.*]] = call {{.*}} ptr 
@hlsl::RWBuffer<float>::operator[](unsigned int)
+// CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x 
%"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), i32 noundef 2)
+// CHECK-NEXT: store float 7.890000e+02, ptr %[[PTR2]], align 4
+}
+
+// No other binding initialization calls should be present.
+// CHECK-NOT: call void @hlsl::RWBuffer<float>::__createFrom{{.*}}Binding{{.*}}

>From 17bf204735551ea62fb458cbf1cacd2d4cb5dea9 Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Mon, 10 Nov 2025 20:41:59 -0800
Subject: [PATCH 3/6] Add support for assigning whole array.

---
 clang/lib/CodeGen/CGExpr.cpp              |  9 +++-
 clang/lib/CodeGen/CGHLSLRuntime.cpp       | 56 +++++++++++++++++++++--
 clang/lib/CodeGen/CGHLSLRuntime.h         |  2 +
 clang/test/SemaHLSL/static_resources.hlsl | 21 +++++++--
 4 files changed, 79 insertions(+), 9 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 01f2161f27555..998b69af86dff 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6291,8 +6291,15 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const 
BinaryOperator *E) {
 LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) {
   // Don't emit an LValue for the RHS because it might not be an LValue
   LValue LHS = EmitLValue(E->getLHS());
+
+  // If the RHS is a global resource array, copy all individual resources
+  // into LHS.
+  if (E->getRHS()->getType()->isHLSLResourceRecordArray())
+    if (CGM.getHLSLRuntime().emitResourceArrayCopy(LHS, E->getRHS(), *this))
+      return LHS;
+
   // In C the RHS of an assignment operator is an RValue.
-  // EmitAggregateAssign takes anan LValue for the RHS. Instead we can call
+  // EmitAggregateAssign takes an LValue for the RHS. Instead we can call
   // EmitInitializationToLValue to emit an RValue into an LValue.
   EmitInitializationToLValue(E->getRHS(), LHS);
   return LHS;
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 2cf601ca6a424..46e9307a2fe4d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attrs.inc"
 #include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/HLSLResource.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
@@ -91,6 +92,14 @@ void addRootSignatureMD(llvm::dxbc::RootSignatureVersion 
RootSigVer,
   RootSignatureValMD->addOperand(MDVals);
 }
 
+// Find array variable declaration from DeclRef expression
+static const ValueDecl *getArrayDecl(const Expr *E) {
+  if (const DeclRefExpr *DRE =
+          dyn_cast_or_null<DeclRefExpr>(E->IgnoreImpCasts()))
+    return DRE->getDecl();
+  return nullptr;
+}
+
 // Find array variable declaration from nested array subscript AST nodes
 static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
   const Expr *E = nullptr;
@@ -100,9 +109,7 @@ static const ValueDecl *getArrayDecl(const 
ArraySubscriptExpr *ASE) {
       return nullptr;
     ASE = dyn_cast<ArraySubscriptExpr>(E);
   }
-  if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E))
-    return DRE->getDecl();
-  return nullptr;
+  return getArrayDecl(E);
 }
 
 // Get the total size of the array, or -1 if the array is unbounded.
@@ -1116,3 +1123,46 @@ std::optional<LValue> 
CGHLSLRuntime::emitResourceArraySubscriptExpr(
   }
   return CGF.MakeAddrLValue(TmpVar, ResultTy, AlignmentSource::Decl);
 }
+
+// If RHSExpr is a global resource array, initialize all of its resources and
+// set them into LHS. Returns false if no copy has been performed and the
+// array copy should be handled by Clang codegen.
+bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr,
+                                          CodeGenFunction &CGF) {
+  QualType ResultTy = RHSExpr->getType();
+  assert((ResultTy->isHLSLResourceRecordArray()) && "expected resource array");
+
+  // Let Clang codegen handle local and static resource array copies.
+  const VarDecl *ArrayDecl = dyn_cast_or_null<VarDecl>(getArrayDecl(RHSExpr));
+  if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
+      ArrayDecl->getStorageClass() == SC_Static)
+    return false;
+
+  // Find binding info for the resource array. For implicit binding
+  // the HLSLResourceBindingAttr should have been added by SemaHLSL.
+  ResourceBindingAttrs Binding(ArrayDecl);
+  assert((Binding.hasBinding()) &&
+         "resource array must have a binding attribute");
+
+  // Find the individual resource type.
+  ASTContext &AST = ArrayDecl->getASTContext();
+  QualType ResTy = AST.getBaseElementType(ResultTy);
+  const auto *ResArrayTy = cast<ConstantArrayType>(ResultTy.getTypePtr());
+
+  // Use the provided LHS for the result.
+  AggValueSlot ValueSlot = AggValueSlot::forAddr(
+      LHS.getAddress(), Qualifiers(), AggValueSlot::IsDestructed_t(true),
+      AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
+      AggValueSlot::DoesNotOverlap);
+
+  // Create Value for index and total array size (= range size).
+  int Size = getTotalArraySize(AST, ResArrayTy);
+  llvm::Value *Zero = llvm::ConstantInt::get(CGM.IntTy, 0);
+  llvm::Value *Range = llvm::ConstantInt::get(CGM.IntTy, Size);
+
+  // Initialize individual resources in the array into LHS.
+  std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray(
+      CGF, ResTy->getAsCXXRecordDecl(), ResArrayTy, ValueSlot, Range, Zero,
+      ArrayDecl->getName(), Binding, {Zero}, RHSExpr->getExprLoc());
+  return EndIndex.has_value();
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index 9d31714ab8606..1f1bad3d5e337 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -195,6 +195,8 @@ class CGHLSLRuntime {
   emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
                                  CodeGenFunction &CGF);
 
+  bool emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr, CodeGenFunction &CGF);
+
 private:
   void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
                                     llvm::GlobalVariable *BufGV);
diff --git a/clang/test/SemaHLSL/static_resources.hlsl 
b/clang/test/SemaHLSL/static_resources.hlsl
index 9997bdf6dca7b..cab3346ff7d7f 100644
--- a/clang/test/SemaHLSL/static_resources.hlsl
+++ b/clang/test/SemaHLSL/static_resources.hlsl
@@ -5,7 +5,7 @@
 // CHECK-NOT: private unnamed_addr constant [{{[0-9]+}} x i8] c"Static
 
 RWBuffer<float> One : register(u1, space5);
-RWBuffer<float> Array[4][2] : register(u10, space6);
+RWBuffer<float> Array[2] : register(u10, space6);
 
 // Check that the non-static resource One is initialized from binding on
 // startup (register 1, space 5).
@@ -51,20 +51,31 @@ void main() {
 // CHECK-NEXT: %[[TMP0:.*]] = alloca %"class.hlsl::RWBuffer"
 
   static RWBuffer<float> StaticLocal;
-// Check that StaticLocal is initialized to by default constructor to poison 
and not from binding
+// Check that StaticLocal is initialized by default constructor (handle set to 
poison)
+// and not from binding.
 // call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} 
@main()::StaticLocal)
 
-  StaticLocal = Array[2][0];
-// A[2][0] is accessed here, so it should be initialized from binding 
(register 10, space 6, index 4),
+  StaticLocal = Array[1];
+// A[2][0] is accessed here, so it should be initialized from binding 
(register 10, space 6, index 1),
 // and then assigned to StaticLocal using = operator.
 // CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, 
unsigned int, int, unsigned int, char const*)
-// CHECK-SAME: (ptr {{.*}} %[[TMP0]], i32 noundef 10, i32 noundef 6, i32 
noundef 8, i32 noundef 4, ptr noundef [[ARRAY_STR]])
+// CHECK-SAME: (ptr {{.*}} %[[TMP0]], i32 noundef 10, i32 noundef 6, i32 
noundef 2, i32 noundef 1, ptr noundef [[ARRAY_STR]])
 // CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr 
{{.*}} @main()::StaticLocal, ptr {{.*}} %[[TMP0]])
 
   StaticOne = One;
+// Operator = call to assign non-static One handle to static StaticOne.
 // CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr 
{{.*}} @StaticOne, ptr {{.*}} @One)
 
+  StaticArray = Array;
+// Check that each elements of StaticArray is initialized from binding 
(register 10, space 6, indices 0 and 1).
+// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, 
unsigned int, int, unsigned int, char const*)
+// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align 4 @StaticArray, 
i32 noundef 10, i32 noundef 6, i32 noundef 2, i32 noundef 0, ptr noundef 
[[ARRAY_STR]])
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned 
int, unsigned int, int, unsigned int, char const*)
+// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align 4 getelementptr 
([2 x %"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1),
+// CHECK-SAME: i32 noundef 10, i32 noundef 6, i32 noundef 2, i32 noundef 1, 
ptr noundef [[ARRAY_STR]]
+
   StaticArray[1] = One;
+// Operator = call to assign non-static One handle to StaticArray element.
 // CHECK-NEXT: call {{.*}} ptr 
@hlsl::RWBuffer<float>::operator=(hlsl::RWBuffer<float> const&)
 // CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x 
%"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), ptr {{.*}} @One)
 

>From d52b410909150c3444d82c9b95b1d2154f22052f Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Mon, 10 Nov 2025 20:47:22 -0800
Subject: [PATCH 4/6] tiny cleanup

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 46e9307a2fe4d..f16fe6d8d85cf 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1032,7 +1032,7 @@ std::optional<LValue> 
CGHLSLRuntime::emitResourceArraySubscriptExpr(
           ArraySubsExpr->getType()->isHLSLResourceRecordArray()) &&
          "expected resource array subscript expression");
 
-  // Let Clang codegen handle local and static resource array subscripts,
+  // Let clang codegen handle local and static resource array subscripts,
   // or when the subscript references on opaque expression (as part of
   // ArrayInitLoopExpr AST node).
   const VarDecl *ArrayDecl =

>From 36d35371941235bb0480371df42175170385d0aa Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Mon, 17 Nov 2025 19:39:50 -0800
Subject: [PATCH 5/6] Add structured buffer resource to the test

---
 clang/test/SemaHLSL/static_resources.hlsl | 45 ++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaHLSL/static_resources.hlsl 
b/clang/test/SemaHLSL/static_resources.hlsl
index cab3346ff7d7f..f71e9ea98e0d9 100644
--- a/clang/test/SemaHLSL/static_resources.hlsl
+++ b/clang/test/SemaHLSL/static_resources.hlsl
@@ -1,11 +1,15 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -emit-llvm 
-disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s
 
-// CHECK: [[ONE_STR:@.*]] = private unnamed_addr constant [4 x i8] c"One\00"
-// CHECK: [[ARRAY_STR:@.*]] = private unnamed_addr constant [6 x i8] 
c"Array\00"
+// CHECK-DAG: [[ONE_STR:@.*]] = private unnamed_addr constant [4 x i8] 
c"One\00"
+// CHECK-DAG: [[ARRAY_STR:@.*]] = private unnamed_addr constant [6 x i8] 
c"Array\00"
+// CHECK-DAG: [[ONEWITHCOUNTER_STR:@.*]] = private unnamed_addr constant [15 x 
i8] c"OneWithCounter\00"
+// CHECK-DAG: [[ARRAYWITHCOUNTER_STR:@.*]] = private unnamed_addr constant [17 
x i8] c"ArrayWithCounter\00"
 // CHECK-NOT: private unnamed_addr constant [{{[0-9]+}} x i8] c"Static
 
 RWBuffer<float> One : register(u1, space5);
 RWBuffer<float> Array[2] : register(u10, space6);
+RWStructuredBuffer<int> OneWithCounter : register(u2, space4);
+RWStructuredBuffer<int> ArrayWithCounter[2] : register(u7, space4);
 
 // Check that the non-static resource One is initialized from binding on
 // startup (register 1, space 5).
@@ -14,6 +18,13 @@ RWBuffer<float> Array[2] : register(u10, space6);
 // CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned 
int, unsigned int, int, unsigned int, char const*)
 // CHECK-SAME: (ptr {{.*}} @One, i32 noundef 1, i32 noundef 5, i32 noundef 1, 
i32 noundef 0, ptr noundef [[ONE_STR]])
 
+// Check that the non-static resource OneWithCounter is initialized from 
binding on
+// startup (register 2, space 4).
+// CHECK: define internal void @__cxx_global_var_init{{.*}}
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void 
@hlsl::RWStructuredBuffer<int>::__createFromBindingWithImplicitCounter(unsigned 
int, unsigned int, int, unsigned int, char const*, unsigned int)
+// CHECK-SAME: (ptr {{.*}} @OneWithCounter, i32 noundef 2, i32 noundef 4, i32 
noundef 1, i32 noundef 0, ptr noundef [[ONEWITHCOUNTER_STR]], i32 noundef 0)
+
 // Note that non-static resource arrays are not initialized on startup.
 // The individual resources from the array are initialized on access.
 
@@ -41,6 +52,14 @@ static RWBuffer<float> StaticArray[2];
 // CHECK: arrayctor.cont:                                   ; preds = 
%arrayctor.loop
 // CHECK-NEXT: ret void
 
+static RWStructuredBuffer<int> StaticOneWithCounter;
+
+// Check that StaticOneWithCounter resource is initialized on startup with the 
default
+// constructor and not from binding. It will initalize the handle to poison.
+// CHECK: define internal void @__cxx_global_var_init{{.*}}
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void 
@hlsl::RWStructuredBuffer<int>::RWStructuredBuffer()(ptr {{.*}} 
@StaticOneWithCounter)
+
 // No other global initialization routines should be present.
 // CHECK-NOT: define internal void @__cxx_global_var_init{{.*}}
 
@@ -91,6 +110,28 @@ void main() {
 // CHECK-NEXT: %[[PTR2:.*]] = call {{.*}} ptr 
@hlsl::RWBuffer<float>::operator[](unsigned int)
 // CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x 
%"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), i32 noundef 2)
 // CHECK-NEXT: store float 7.890000e+02, ptr %[[PTR2]], align 4
+
+  static RWStructuredBuffer<int> StaticLocalWithCounter;
+// Check that StaticLocalWithCounter is initialized by default constructor 
(handle set to poison)
+// and not from binding.
+// call void @hlsl::RWStructuredBuffer<int>::RWStructuredBuffer()(ptr {{.*}} 
@main()::StaticLocalWithCounter)
+
+  static RWStructuredBuffer<int> StaticLocalArrayWithCounter[2];
+
+  StaticLocalWithCounter = OneWithCounter;
+// Operator = call to assign non-static OneWithCounter handles to 
StaticLocalWithCounter handles.
+// CHECK: call {{.*}} ptr 
@hlsl::RWStructuredBuffer<int>::operator=(hlsl::RWStructuredBuffer<int> 
const&)(ptr {{.*}} @main()::StaticLocalWithCounter, ptr {{.*}} @OneWithCounter)
+
+  StaticLocalArrayWithCounter = ArrayWithCounter;
+// Check that each elements of StaticLocalArrayWithCounter is initialized from 
binding
+// of ArrayWithCounter (register 7, space 4, indices 0 and 1).
+// CHECK: call void 
@hlsl::RWStructuredBuffer<int>::__createFromBindingWithImplicitCounter(unsigned 
int, unsigned int, int, unsigned int, char const*, unsigned int)
+// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWStructuredBuffer") align 4 
@main()::StaticLocalArrayWithCounter,
+// CHECK-SAME: i32 noundef 7, i32 noundef 4, i32 noundef 2, i32 noundef 0, ptr 
noundef [[ARRAYWITHCOUNTER_STR]], i32 noundef 1)
+
+// CHECK-NEXT: call void 
@hlsl::RWStructuredBuffer<int>::__createFromBindingWithImplicitCounter(unsigned 
int, unsigned int, int, unsigned int, char const*, unsigned int)
+// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWStructuredBuffer") align 4 
getelementptr ([2 x %"class.hlsl::RWStructuredBuffer"], ptr 
@main()::StaticLocalArrayWithCounter, i32 0, i32 1),
+// CHECK-SAME: i32 noundef 7, i32 noundef 4, i32 noundef 2, i32 noundef 1, ptr 
noundef [[ARRAYWITHCOUNTER_STR]], i32 noundef 1)
 }
 
 // No other binding initialization calls should be present.

>From 6e697fdf8d26fda27eb4548d3c5443017ca95216 Mon Sep 17 00:00:00 2001
From: Helena Kotas <[email protected]>
Date: Wed, 19 Nov 2025 11:53:24 -0800
Subject: [PATCH 6/6] clang-format

---
 clang/lib/CodeGen/CGHLSLRuntime.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index fa7b9c4682371..dbcc193172d00 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -258,7 +258,7 @@ class CGHLSLRuntime {
   emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
                                  CodeGenFunction &CGF);
   bool emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr, CodeGenFunction &CGF);
-  
+
   std::optional<LValue> emitBufferArraySubscriptExpr(
       const ArraySubscriptExpr *E, CodeGenFunction &CGF,
       llvm::function_ref<llvm::Value *(bool Promote)> EmitIdxAfterBase);

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to