[clang] [HLSL] Codegen for handling global resource array initialization (PR #198891)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Thu May 21 12:08:19 PDT 2026
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/198891
>From cd53b36ef74646fe182c69b8f558cdbee6bc7441 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 20 May 2026 13:09:08 -0700
Subject: [PATCH 1/2] [HLSL] Codegen for accessing resource arrays inside
structs
Fixes #187087
---
clang/lib/CodeGen/CGExpr.cpp | 19 +-
clang/lib/CodeGen/CGExprAgg.cpp | 4 +
clang/lib/CodeGen/CGHLSLRuntime.cpp | 124 +++++++++-----
clang/lib/CodeGen/CGHLSLRuntime.h | 13 +-
.../resources/res-array-global-to-local.hlsl | 157 +++++++++++++++++
.../resources/res-array-local2.hlsl | 17 +-
.../resources/resource-bindings.hlsl | 10 +-
.../resources-in-structs-array-to-local.hlsl | 162 ++++++++++++++++++
8 files changed, 450 insertions(+), 56 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/resources/res-array-global-to-local.hlsl
create mode 100644 clang/test/CodeGenHLSL/resources/resources-in-structs-array-to-local.hlsl
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 77fd47ed42f03..0160b353f8f32 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3460,6 +3460,16 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
return CGF.MakeAddrLValue(Addr, T, AlignmentSource::Decl);
}
+ // Global HLSL resource arrays initialized on access; create a temporary with
+ // the initialized global resource array.
+ if (CGF.getLangOpts().HLSL && VD->getType()->isHLSLResourceRecordArray()) {
+ std::optional<LValue> LV =
+ CGF.CGM.getHLSLRuntime().emitGlobalResourceArrayAsLValue(
+ CGF, VD, E->getExprLoc());
+ if (LV.has_value())
+ return LV.value();
+ }
+
llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD);
if (VD->getTLSKind() != VarDecl::TLS_None)
@@ -6767,9 +6777,14 @@ LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) {
// 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))
+ if (E->getRHS()->getType()->isHLSLResourceRecordArray()) {
+ AggValueSlot Slot = AggValueSlot::forAddr(
+ LHS.getAddress(), Qualifiers(), AggValueSlot::IsDestructed_t(true),
+ AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
+ AggValueSlot::DoesNotOverlap);
+ if (CGM.getHLSLRuntime().emitGlobalResourceArray(*this, E->getRHS(), Slot))
return LHS;
+ }
// In C the RHS of an assignment operator is an RValue.
// EmitAggregateAssign takes an LValue for the RHS. Instead we can call
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 876d184af1930..ff1eb5ccbbc69 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -966,6 +966,10 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
[[fallthrough]];
case CK_HLSLArrayRValue:
+ if (CGF.getLangOpts().HLSL &&
+ E->getSubExpr()->getType()->isHLSLResourceRecordArray())
+ if (CGF.CGM.getHLSLRuntime().emitGlobalResourceArray(CGF, E, Dest))
+ break;
Visit(E->getSubExpr());
break;
case CK_HLSLAggregateSplatCast: {
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 98d65715d45b9..348ff9e31dd34 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -194,6 +194,8 @@ static const ValueDecl *getArrayDecl(ASTContext &AST, const Expr *E) {
E = E->IgnoreImpCasts();
if (const auto *DRE = dyn_cast_or_null<DeclRefExpr>(E))
return DRE->getDecl();
+ if (auto *OVE = dyn_cast<OpaqueValueExpr>(E))
+ E = OVE->getSourceExpr()->IgnoreImpCasts();
if (isa<MemberExpr>(E))
return findAssociatedResourceDeclForStruct(AST, cast<MemberExpr>(E));
return nullptr;
@@ -296,12 +298,13 @@ static void callResourceInitMethod(CodeGenFunction &CGF,
CGF.EmitCall(FnInfo, Callee, ReturnValue, Args, nullptr);
}
-// Initializes local resource array variable. For multi-dimensional arrays it
-// calls itself recursively to initialize its sub-arrays. The Index used in the
-// resource constructor calls will begin at StartIndex and will be incremented
-// for each array element. The last used resource Index is returned to the
-// caller. If the function returns std::nullopt, it indicates an error.
-static std::optional<llvm::Value *> initializeLocalResourceArray(
+// Initializes local resource array variable with global resource array
+// elements. For multi-dimensional arrays it calls itself recursively to
+// initialize its sub-arrays. The Index used in the resource constructor calls
+// will begin at StartIndex and will be incremented for each array element. The
+// last used resource Index is returned to the caller. If the function returns
+// std::nullopt, it indicates an error.
+static std::optional<llvm::Value *> initializeResourceArrayFromGlobal(
CodeGenFunction &CGF, CXXRecordDecl *ResourceDecl,
const ConstantArrayType *ArrayTy, AggValueSlot &ValueSlot,
llvm::Value *Range, llvm::Value *StartIndex, StringRef ResourceName,
@@ -329,9 +332,10 @@ static std::optional<llvm::Value *> initializeLocalResourceArray(
Index = CGF.Builder.CreateAdd(Index, One);
GEPIndices.back() = llvm::ConstantInt::get(IntTy, I);
}
- std::optional<llvm::Value *> MaybeIndex = initializeLocalResourceArray(
- CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index, ResourceName,
- Binding, GEPIndices, ArraySubsExprLoc);
+ std::optional<llvm::Value *> MaybeIndex =
+ initializeResourceArrayFromGlobal(
+ CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index,
+ ResourceName, Binding, GEPIndices, ArraySubsExprLoc);
if (!MaybeIndex)
return std::nullopt;
Index = *MaybeIndex;
@@ -1426,7 +1430,7 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
// needs to be initialized.
const ConstantArrayType *ArrayTy =
cast<ConstantArrayType>(ResultTy.getTypePtr());
- std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray(
+ std::optional<llvm::Value *> EndIndex = initializeResourceArrayFromGlobal(
CGF, ResourceTy->getAsCXXRecordDecl(), ArrayTy, ValueSlot, Range, Index,
ArrayDecl->getName(), Binding, {llvm::ConstantInt::get(CGM.IntTy, 0)},
ArraySubsExpr->getExprLoc());
@@ -1436,20 +1440,15 @@ 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(CGF.CGM.getContext(), RHSExpr));
- if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
- ArrayDecl->getStorageClass() == SC_Static)
- return false;
+// Initialize all resources of a global resource array into provided slot.
+bool CGHLSLRuntime::initializeGlobalResourceArray(CodeGenFunction &CGF,
+ const VarDecl *ArrayDecl,
+ SourceLocation Loc,
+ AggValueSlot &DestSlot) {
+ assert(ArrayDecl->getType()->isHLSLResourceRecordArray() &&
+ ArrayDecl->hasGlobalStorage() &&
+ ArrayDecl->getStorageClass() != SC_Static &&
+ "expected global non-static resource array");
// Find binding info for the resource array. For implicit binding
// the HLSLResourceBindingAttr should have been added by SemaHLSL.
@@ -1459,27 +1458,63 @@ bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr,
// 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);
+ QualType ResTy = AST.getBaseElementType(ArrayDecl->getType());
+ const auto *ResArrayTy =
+ cast<ConstantArrayType>(ArrayDecl->getType().getTypePtr());
// 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());
+ // Initialize individual resources in the array into DestSlot.
+ std::optional<llvm::Value *> EndIndex = initializeResourceArrayFromGlobal(
+ CGF, ResTy->getAsCXXRecordDecl(), ResArrayTy, DestSlot, Range, Zero,
+ ArrayDecl->getName(), Binding, {Zero}, Loc);
return EndIndex.has_value();
}
+// If the expression is a global resource array, initialize all of its resources
+// into Dest. Returns false if no initialization has been performed and the
+// array copy should be handled by the default codegen.
+bool CGHLSLRuntime::emitGlobalResourceArray(CodeGenFunction &CGF, const Expr *E,
+ AggValueSlot &DestSlot) {
+ assert(E->getType()->isHLSLResourceRecordArray() &&
+ "expected resource array");
+
+ // Find the array declaration for the expression. Fallback to the default
+ // handling if it's not a global resource array.
+ const VarDecl *ArrayDecl =
+ dyn_cast_or_null<VarDecl>(getArrayDecl(CGF.CGM.getContext(), E));
+ if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
+ ArrayDecl->getStorageClass() == SC_Static)
+ return false;
+
+ return initializeGlobalResourceArray(CGF, ArrayDecl, E->getExprLoc(),
+ DestSlot);
+}
+
+// If the expression is a global resource array, create a temporary and
+// initialize all of its resources, and return is as an LValue. Returns false if
+// no initialization has been performed and the handling should follow the
+// default path.
+std::optional<LValue> CGHLSLRuntime::emitGlobalResourceArrayAsLValue(
+ CodeGenFunction &CGF, const VarDecl *ArrayDecl, SourceLocation Loc) {
+ assert(ArrayDecl->getType()->isHLSLResourceRecordArray() &&
+ "expected resource array declaration");
+
+ if (!ArrayDecl->hasGlobalStorage() ||
+ ArrayDecl->getStorageClass() == SC_Static)
+ return std::nullopt;
+
+ AggValueSlot TmpArraySlot =
+ CGF.CreateAggTemp(ArrayDecl->getType(), "tmpResArray");
+ if (initializeGlobalResourceArray(CGF, ArrayDecl, Loc, TmpArraySlot))
+ return CGF.MakeAddrLValue(TmpArraySlot.getAddress(), ArrayDecl->getType(),
+ AlignmentSource::Decl);
+ return std::nullopt;
+}
+
RawAddress CGHLSLRuntime::createBufferMatrixTempAddress(const LValue &LV,
SourceLocation Loc,
CodeGenFunction &CGF) {
@@ -1571,18 +1606,21 @@ CGHLSLRuntime::emitResourceMemberExpr(CodeGenFunction &CGF,
ME->getType()->isHLSLResourceRecordArray()) &&
"expected resource member expression");
- if (ME->getType()->isHLSLResourceRecordArray()) {
- // FIXME: Handle member access of the whole array of resources
- // (llvm/llvm-project#187087). Access to individual resource array elements
- // is already handled in emitResourceArraySubscriptExpr.
- return std::nullopt;
- }
-
const VarDecl *ResourceVD =
findAssociatedResourceDeclForStruct(CGF.CGM.getContext(), ME);
if (!ResourceVD)
return std::nullopt;
+ // Handle member of resource array type.
+ if (ResourceVD->getType()->isHLSLResourceRecordArray()) {
+ std::optional<LValue> LV =
+ emitGlobalResourceArrayAsLValue(CGF, ResourceVD, ME->getExprLoc());
+ if (LV)
+ return LV;
+ return std::nullopt;
+ }
+
+ // Handle member that is an individual resource.
GlobalVariable *ResGV =
cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(ResourceVD));
const DataLayout &DL = CGM.getDataLayout();
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index d7ac2346f2428..4be6d1c1271ac 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -79,6 +79,7 @@ namespace CodeGen {
class CodeGenModule;
class CodeGenFunction;
class LValue;
+class AggValueSlot;
class CGHLSLOffsetInfo {
SmallVector<uint32_t> Offsets;
@@ -299,7 +300,12 @@ class CGHLSLRuntime {
std::optional<LValue>
emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
CodeGenFunction &CGF);
- bool emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr, CodeGenFunction &CGF);
+
+ bool emitGlobalResourceArray(CodeGenFunction &CGF, const Expr *E,
+ AggValueSlot &DestSlot);
+ std::optional<LValue>
+ emitGlobalResourceArrayAsLValue(CodeGenFunction &CGF,
+ const VarDecl *ArrayDecl, SourceLocation Loc);
std::optional<LValue> emitBufferArraySubscriptExpr(
const ArraySubscriptExpr *E, CodeGenFunction &CGF,
@@ -349,6 +355,11 @@ class CGHLSLRuntime {
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
+ bool initializeGlobalResourceArray(CodeGenFunction &CGF,
+ const VarDecl *ArrayDecl,
+ SourceLocation Loc,
+ AggValueSlot &DestSlot);
+
llvm::Triple::ArchType getArch();
llvm::DenseMap<const clang::RecordType *, llvm::StructType *> LayoutTypes;
diff --git a/clang/test/CodeGenHLSL/resources/res-array-global-to-local.hlsl b/clang/test/CodeGenHLSL/resources/res-array-global-to-local.hlsl
new file mode 100644
index 0000000000000..d8a6c6a3b3e08
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/res-array-global-to-local.hlsl
@@ -0,0 +1,157 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header \
+// RUN: -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s -check-prefixes=CHECK
+
+// 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
+
+// one-dimensional array
+RWBuffer<float> A[2] : register(u10, space1);
+
+// multi-dimensional array
+[[vk::binding(13)]]
+RWBuffer<float> B[2][2] : register(u13, space0);
+
+void useArray(RWBuffer<float> LocalArg[2]) {
+}
+
+void useMultiArray(RWBuffer<float> LocalArg[2][2]) {
+}
+
+// CHECK-LABEL: case1
+// CHECK: %LocalOne = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK: [[ArrayTmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+
+// - Initialize all resources of the global array into a local temporary.
+// CHECK: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[ArrayTmp]], i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 0, ptr noundef @A.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[ArrayTmp]], i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 1, ptr noundef @A.str)
+
+// - Copy from temporary to local array (in a loop)
+// CHECK: %arrayinit.begin = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %LocalOne, i32 0, i32 0
+// CHECK: arrayinit.body:
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer(hlsl::RWBuffer<float> const&)(ptr {{.*}}, ptr {{.*}})
+// CHECK: arrayinit.end:
+// CHECK: ret void
+void case1() {
+ // local one-dimensional array initialialized with global array
+ RWBuffer<float> LocalOne[2] = A;
+}
+
+// CHECK-LABEL: case2
+// CHECK: %LocalTwo = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+
+// - Local array is first initialized to poison using the default constructor (in a loop)
+//
+// CHECK: %array.begin = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %LocalTwo, i32 0, i32 0
+// CHECK: arrayctor.loop
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}})
+// CHECK: arrayctor.cont:
+
+// - Initialize individual resource elements directly into the local array
+// CHECK: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr %LocalTwo, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 0, ptr noundef @A.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr %LocalTwo, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 1, ptr noundef @A.str)
+// CHECK: ret void
+void case2() {
+ // local one-dimensional array initialialized with assignment
+ RWBuffer<float> LocalTwo[2];
+ LocalTwo = A;
+}
+
+// CHECK-LABEL: case3
+// CHECK: [[AggTmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK-NEXT: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[AggTmp]], i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 0, ptr noundef @A.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[AggTmp]], i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 1, ptr noundef @A.str)
+// CHECK-NEXT: call void @useArray(hlsl::RWBuffer<float> [2])(ptr {{.*}} [[AggTmp]])
+void case3() {
+ // resource array as function argument
+ useArray(A);
+}
+
+// CHECK-LABEL: case4
+// CHECK: %LocalThree = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+// CHECK: [[TmpResArray:%.*]] = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+
+// - Initialize all resources of the global array into a local temporary.
+// CHECK: [[ArrayPtr00:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr00]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 0, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr01:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr01]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 1, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr10:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 1, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr10]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr11:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 1, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr11]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.str)
+
+// - Copy from temporary to local array (in a double loop)
+// CHECK: %arrayinit.begin = getelementptr inbounds [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalThree, i32 0, i32 0
+// CHECK: arrayinit.body:
+// CHECK: arrayinit.body2:
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer(hlsl::RWBuffer<float> const&)(ptr {{.*}}, ptr {{.*}})
+// CHECK: arrayinit.end:
+// CHECK: arrayinit.end{{[0-9]+}}:
+// CHECK: ret void
+void case4() {
+ // local multi-dimensional array initialialized with global array
+ RWBuffer<float> LocalThree[2][2] = B;
+}
+
+// CHECK-LABEL: case5
+// CHECK: %LocalFour = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+
+// - Local array is first initialized to poison using the default constructor (in a loop)
+//
+// CHECK: %array.begin = getelementptr inbounds [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 0, i32 0
+// CHECK: arrayctor.loop
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}})
+// CHECK: arrayctor.cont:
+
+// - Initialize individual resource elements directly into the local array
+// CHECK: [[ArrayPtr00:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr00]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 0, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr01:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr01]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 1, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr10:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 1, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr10]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr11:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 1, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr11]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.str)
+// CHECK: ret void
+void case5() {
+ // local multi-dimensional array initialialized with assignment
+ RWBuffer<float> LocalFour[2][2];
+ LocalFour = B;
+}
+
+// CHECK-LABEL: case6
+// CHECK: [[AggTmp:%.*]] = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+// CHECK: [[ArrayPtr00:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr00]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 0, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr01:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr01]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 1, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr10:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 1, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr10]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr11:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 1, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr11]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.str)
+// CHECK-NEXT: call void @useMultiArray(hlsl::RWBuffer<float> [2][2])(ptr noundef byval([2 x [2 x %"class.hlsl::RWBuffer"]]) align 4 %agg.tmp)
+// CHECK: ret void
+void case6() {
+ // resource array as function argument
+ useMultiArray(B);
+}
+
+// CHECK-LABEL: case7
+// CHECK: [[AggTmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK: [[Tmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK-NEXT: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[Tmp]], i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[Tmp]], i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.str)
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AggTmp]], ptr align 4 [[Tmp]], i32 8, i1 false)
+// CHECK-NEXT: call void @useArray(hlsl::RWBuffer<float> [2])(ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 [[AggTmp]])
+void case7() {
+ // subset of multi-dimensional resource array as function argument
+ useArray(B[1]);
+}
diff --git a/clang/test/CodeGenHLSL/resources/res-array-local2.hlsl b/clang/test/CodeGenHLSL/resources/res-array-local2.hlsl
index ffed362887426..b0c4059828ee9 100644
--- a/clang/test/CodeGenHLSL/resources/res-array-local2.hlsl
+++ b/clang/test/CodeGenHLSL/resources/res-array-local2.hlsl
@@ -3,7 +3,11 @@
// This test verifies handling of local arrays of resources when used as a function argument.
-// CHECK: @_ZL1A = internal global [3 x %"class.hlsl::RWBuffer"] poison, align 4
+// Resource array elements are initialized on access; there should never a global
+// array of resources (unless it is static).
+// CHECK-NOT: @_ZL1A = internal global [3 x %"class.hlsl::RWBuffer"] poison, align 4
+
+// CHECK: @A.str = private unnamed_addr constant [2 x i8] c"A\00", align 1
RWBuffer<float> A[3] : register(u0);
RWStructuredBuffer<float> Out : register(u0);
@@ -26,10 +30,15 @@ float foo(RWBuffer<float> LocalA[3]) {
[numthreads(4,1,1)]
void main() {
// Check that the `main` function calls `foo` with a local copy of the array
-// CHECK-NEXT: %[[Tmp:.*]] = alloca [3 x %"class.hlsl::RWBuffer"], align 4
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp]], ptr align 4 @_ZL1A, i32 12, i1 false)
+// CHECK-NEXT: [[Tmp:%.*]] = alloca [3 x %"class.hlsl::RWBuffer"], align 4
+// CHECK-NEXT: [[TmpPtr0:%.*]] = getelementptr [3 x %"class.hlsl::RWBuffer"], ptr [[Tmp]], i32 0, i32 0
+// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfE19__createFromBindingEjjijPKc(ptr {{.*}} [[TmpPtr0]], i32 noundef 0, i32 noundef 0, i32 noundef 3, i32 noundef 0, ptr noundef @A.str)
+// CHECK-NEXT: [[TmpPtr1:%.*]] = getelementptr [3 x %"class.hlsl::RWBuffer"], ptr [[Tmp]], i32 0, i32 1
+// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfE19__createFromBindingEjjijPKc(ptr {{.*}} [[TmpPtr1]], i32 noundef 0, i32 noundef 0, i32 noundef 3, i32 noundef 1, ptr noundef @A.str)
+// CHECK-NEXT: [[TmpPtr2:%.*]] = getelementptr [3 x %"class.hlsl::RWBuffer"], ptr [[Tmp]], i32 0, i32 2
+// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfE19__createFromBindingEjjijPKc(ptr {{.*}} [[TmpPtr2]], i32 noundef 0, i32 noundef 0, i32 noundef 3, i32 noundef 2, ptr noundef @A.str)
-// CHECK-NEXT: %[[ReturnedValue:.*]] = call {{.*}} float @_Z3fooA3_N4hlsl8RWBufferIfEE(ptr noundef byval([3 x %"class.hlsl::RWBuffer"]) align 4 %[[Tmp]])
+// CHECK-NEXT: %[[ReturnedValue:.*]] = call {{.*}} float @_Z3fooA3_N4hlsl8RWBufferIfEE(ptr {{.*}} [[Tmp]])
// CHECK-NEXT: %[[OutBufPtr:.*]] = call {{.*}} ptr @_ZNK4hlsl18RWStructuredBufferIfEixEj(ptr {{.*}} @_ZL3Out, i32 noundef 0)
// CHECK-NEXT: store float %[[ReturnedValue]], ptr %[[OutBufPtr]], align 4
// CHECK-NEXT: ret void
diff --git a/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl
index 1d85048db87a8..bd451874c123d 100644
--- a/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl
+++ b/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl
@@ -5,13 +5,15 @@
// CHECK: %"class.hlsl::RWBuffer.0" = type { target("dx.TypedBuffer", float, 1, 0, 0) }
// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", i32, 0, 0) }
// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", %struct.S, 1, 0), target("dx.RawBuffer", %struct.S, 1, 0) }
-// CHECK: %"class.hlsl::RWBuffer.1" = type { target("dx.TypedBuffer", double, 1, 0, 0) }
// CHECK: @_ZL4U0S0 = internal global %"class.hlsl::RWBuffer" poison, align 4
// CHECK: @_ZL4U5S3 = internal global %"class.hlsl::RWBuffer.0" poison, align 4
// CHECK: @_ZL4T2S2 = internal global %"class.hlsl::StructuredBuffer" poison, align 4
// CHECK: @_ZL4T3S0 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4
-// CHECK: @_ZL5Array = internal global [10 x %"class.hlsl::RWBuffer.1"] poison, align 4
+
+// Resource array elements are initialized on access; there should never a global
+// array of resources (unless it is static).
+// CHECK-NOT: @_ZL5Array = internal global [10 x %"class.hlsl::RWBuffer.1"] poison, align 4
// CHECK: call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(
@@ -40,8 +42,4 @@ RWBuffer<double> Array[10] : register(u4, space0);
[numthreads(4,1,1)]
void main() {
- // Reference Array to ensure it is emitted and we can test that it is initialized
- // to poison, but do not index it.
- // Non-array resources are always emitted because they have a constructor initializer.
- (void)Array;
}
diff --git a/clang/test/CodeGenHLSL/resources/resources-in-structs-array-to-local.hlsl b/clang/test/CodeGenHLSL/resources/resources-in-structs-array-to-local.hlsl
new file mode 100644
index 0000000000000..0a83517f36cbc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/resources-in-structs-array-to-local.hlsl
@@ -0,0 +1,162 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header \
+// RUN: -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s -check-prefixes=CHECK
+
+// CHECK: @A.Bufs.str = private unnamed_addr constant [7 x i8] c"A.Bufs\00", align 1
+// CHECK: @B.Bufs.str = private unnamed_addr constant [7 x i8] c"B.Bufs\00", align 1
+
+// structs with one-dimensional array
+struct StructA {
+ RWBuffer<float> Bufs[2];
+};
+StructA A : register(u10, space1);
+
+// struct with multi-dimensional array
+struct StructB {
+ RWBuffer<float> Bufs[2][2];
+};
+StructB B : register(u13, space0);
+
+void useArray(RWBuffer<float> LocalArg[2]) {
+}
+
+void useMultiArray(RWBuffer<float> LocalArg[2][2]) {
+}
+
+// CHECK-LABEL: case1
+// CHECK: %LocalOne = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK: [[ArrayTmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+
+// - Initialize all resources of the global array into a local temporary.
+// CHECK: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[ArrayTmp]], i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 0, ptr noundef @A.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[ArrayTmp]], i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 1, ptr noundef @A.Bufs.str)
+
+// - Copy from temporary to local array (in a loop)
+// CHECK: %arrayinit.begin = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %LocalOne, i32 0, i32 0
+// CHECK: arrayinit.body:
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer(hlsl::RWBuffer<float> const&)(ptr {{.*}}, ptr {{.*}})
+// CHECK: arrayinit.end:
+// CHECK: ret void
+void case1() {
+ // local one-dimensional array initialialized with global array
+ RWBuffer<float> LocalOne[2] = A.Bufs;
+}
+
+// CHECK-LABEL: case2
+// CHECK: %LocalTwo = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+
+// - Local array is first initialized to poison using the default constructor (in a loop)
+//
+// CHECK: %array.begin = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %LocalTwo, i32 0, i32 0
+// CHECK: arrayctor.loop
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}})
+// CHECK: arrayctor.cont:
+
+// - Initialize individual resource elements directly into the local array
+// CHECK: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr %LocalTwo, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 0, ptr noundef @A.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr %LocalTwo, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 1, ptr noundef @A.Bufs.str)
+// CHECK: ret void
+void case2() {
+ // local one-dimensional array initialialized with assignment
+ RWBuffer<float> LocalTwo[2];
+ LocalTwo = A.Bufs;
+}
+
+// CHECK-LABEL: case3
+// CHECK: [[AggTmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK-NEXT: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[AggTmp]], i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 0, ptr noundef @A.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[AggTmp]], i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 10, i32 noundef 1, i32 noundef 2, i32 noundef 1, ptr noundef @A.Bufs.str)
+// CHECK-NEXT: call void @useArray(hlsl::RWBuffer<float> [2])(ptr {{.*}} [[AggTmp]])
+void case3() {
+ // resource array as function argument
+ useArray(A.Bufs);
+}
+
+// CHECK-LABEL: case4
+// CHECK: %LocalThree = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+// CHECK: [[TmpResArray:%.*]] = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+
+// - Initialize all resources of the global array into a local temporary.
+// CHECK: [[ArrayPtr00:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr00]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 0, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr01:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr01]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 1, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr10:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 1, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr10]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr11:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[TmpResArray]], i32 0, i32 1, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr11]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.Bufs.str)
+
+// - Copy from temporary to local array (in a double loop)
+// CHECK: %arrayinit.begin = getelementptr inbounds [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalThree, i32 0, i32 0
+// CHECK: arrayinit.body:
+// CHECK: arrayinit.body2:
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer(hlsl::RWBuffer<float> const&)(ptr {{.*}}, ptr {{.*}})
+// CHECK: arrayinit.end:
+// CHECK: arrayinit.end{{[0-9]+}}:
+// CHECK: ret void
+void case4() {
+ // local multi-dimensional array initialialized with global array
+ RWBuffer<float> LocalThree[2][2] = B.Bufs;
+}
+
+// CHECK-LABEL: case5
+// CHECK: %LocalFour = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+
+// - Local array is first initialized to poison using the default constructor (in a loop)
+//
+// CHECK: %array.begin = getelementptr inbounds [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 0, i32 0
+// CHECK: arrayctor.loop
+// CHECK: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}})
+// CHECK: arrayctor.cont:
+
+// - Initialize individual resource elements directly into the local array
+// CHECK: [[ArrayPtr00:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr00]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 0, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr01:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr01]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 1, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr10:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 1, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr10]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr11:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %LocalFour, i32 0, i32 1, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr11]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.Bufs.str)
+// CHECK: ret void
+void case5() {
+ // local multi-dimensional array initialialized with assignment
+ RWBuffer<float> LocalFour[2][2];
+ LocalFour = B.Bufs;
+}
+
+// CHECK-LABEL: case6
+// CHECK: [[AggTmp:%.*]] = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
+// CHECK: [[ArrayPtr00:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr00]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 0, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr01:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr01]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 1, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr10:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 1, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr10]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr11:%.*]] = getelementptr [2 x [2 x %"class.hlsl::RWBuffer"]], ptr [[AggTmp]], i32 0, i32 1, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr11]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: call void @useMultiArray(hlsl::RWBuffer<float> [2][2])(ptr noundef byval([2 x [2 x %"class.hlsl::RWBuffer"]]) align 4 %agg.tmp)
+// CHECK: ret void
+void case6() {
+ // resource array as function argument
+ useMultiArray(B.Bufs);
+}
+
+// CHECK-LABEL: case7
+// CHECK: [[AggTmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK: [[Tmp:%.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
+// CHECK-NEXT: [[ArrayPtr0:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[Tmp]], i32 0, i32 0
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr0]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 2, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: [[ArrayPtr1:%.*]] = getelementptr [2 x %"class.hlsl::RWBuffer"], ptr [[Tmp]], i32 0, i32 1
+// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} [[ArrayPtr1]], i32 noundef 13, i32 noundef 0, i32 noundef 4, i32 noundef 3, ptr noundef @B.Bufs.str)
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AggTmp]], ptr align 4 [[Tmp]], i32 8, i1 false)
+// CHECK-NEXT: call void @useArray(hlsl::RWBuffer<float> [2])(ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 [[AggTmp]])
+void case7() {
+ // subset of multi-dimensional resource array as function argument
+ useArray(B.Bufs[1]);
+}
>From 5ede576ac952f7d3f63f47216f2df2faa240845d Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 21 May 2026 12:07:59 -0700
Subject: [PATCH 2/2] self review - simplify condition
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 348ff9e31dd34..2707e7d388d1e 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1612,13 +1612,8 @@ CGHLSLRuntime::emitResourceMemberExpr(CodeGenFunction &CGF,
return std::nullopt;
// Handle member of resource array type.
- if (ResourceVD->getType()->isHLSLResourceRecordArray()) {
- std::optional<LValue> LV =
- emitGlobalResourceArrayAsLValue(CGF, ResourceVD, ME->getExprLoc());
- if (LV)
- return LV;
- return std::nullopt;
- }
+ if (ResourceVD->getType()->isHLSLResourceRecordArray())
+ return emitGlobalResourceArrayAsLValue(CGF, ResourceVD, ME->getExprLoc());
// Handle member that is an individual resource.
GlobalVariable *ResGV =
More information about the cfe-commits
mailing list