https://github.com/hekota created https://github.com/llvm/llvm-project/pull/187127
Depends on #184731 Any expression that accesses a resource or resource array member of a global struct instance must be during codegen replaced by an access of the corresponding implicit global resource variable. When codegen encounters a `MemberExpr` of a resource type, it traverses the AST to locate the parent struct declaration, building the expected global resource variable name along the way. If the parent declaration is a non-static global struct instance, codegen searches its `HLSLAssociatedResourceDeclAttr` attributes to locate the matching global resource variable and then generates IR code to access the resource global in place of the member access. Fixes #182989 >From 34006baedc586f4e40bbe8eb7db1bdd852694aae Mon Sep 17 00:00:00 2001 From: Helena Kotas <[email protected]> Date: Tue, 17 Mar 2026 13:45:05 -0700 Subject: [PATCH] [HLSL] Implement CodeGen for accessing resource members of a struct Fixes #182989 --- clang/include/clang/AST/HLSLResource.h | 1 + clang/lib/AST/HLSLResource.cpp | 14 ++ clang/lib/CodeGen/CGExpr.cpp | 16 +- clang/lib/CodeGen/CGHLSLRuntime.cpp | 138 +++++++++++++++++- clang/lib/CodeGen/CGHLSLRuntime.h | 2 + .../resources/resources-in-structs-array.hlsl | 83 +++++++++++ .../resources-in-structs-inheritance.hlsl | 132 +++++++++++++++++ .../resources/resources-in-structs.hlsl | 53 +++++++ 8 files changed, 428 insertions(+), 11 deletions(-) create mode 100644 clang/test/CodeGenHLSL/resources/resources-in-structs-array.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/resources-in-structs-inheritance.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/resources-in-structs.hlsl diff --git a/clang/include/clang/AST/HLSLResource.h b/clang/include/clang/AST/HLSLResource.h index a37acb3660d00..aeea3bdac99af 100644 --- a/clang/include/clang/AST/HLSLResource.h +++ b/clang/include/clang/AST/HLSLResource.h @@ -128,6 +128,7 @@ class EmbeddedResourceNameBuilder { void pushName(llvm::StringRef N) { pushName(N, FieldDelim); } void pushBaseName(llvm::StringRef N); void pushArrayIndex(uint64_t Index); + void pushBaseNameHierarchy(CXXRecordDecl *DerivedRD, CXXRecordDecl *BaseRD); void pop() { assert(!Offsets.empty() && "no name to pop"); diff --git a/clang/lib/AST/HLSLResource.cpp b/clang/lib/AST/HLSLResource.cpp index 19321625222f3..4545d94f6a5a9 100644 --- a/clang/lib/AST/HLSLResource.cpp +++ b/clang/lib/AST/HLSLResource.cpp @@ -42,5 +42,19 @@ void EmbeddedResourceNameBuilder::pushArrayIndex(uint64_t Index) { OS << Index; } +void EmbeddedResourceNameBuilder::pushBaseNameHierarchy( + CXXRecordDecl *DerivedRD, CXXRecordDecl *BaseRD) { + Offsets.push_back(Name.size()); + Name.append(FieldDelim); + while (BaseRD != DerivedRD) { + assert(DerivedRD->getNumBases() == 1 && + "HLSL does not support multiple inheritance"); + DerivedRD = DerivedRD->bases_begin()->getType()->getAsCXXRecordDecl(); + assert(DerivedRD && "base class not found"); + Name.append(DerivedRD->getName()); + Name.append(BaseClassDelim); + } +} + } // namespace hlsl } // namespace clang diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index eebb36276e0eb..c1f478b2855db 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5472,10 +5472,20 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { EmitIgnoredExpr(E->getBase()); return EmitDeclRefLValue(DRE); } - if (getLangOpts().HLSL && - E->getType().getAddressSpace() == LangAS::hlsl_constant) { + + if (getLangOpts().HLSL) { + QualType QT = E->getType(); // We have an HLSL buffer - emit using HLSL's layout rules. - return CGM.getHLSLRuntime().emitBufferMemberExpr(*this, E); + if (QT.getAddressSpace() == LangAS::hlsl_constant) + return CGM.getHLSLRuntime().emitBufferMemberExpr(*this, E); + + // Resource or resource array member of a global struct/class + if (QT->isHLSLResourceRecord() || QT->isHLSLResourceRecordArray()) { + std::optional<LValue> LV; + LV = CGM.getHLSLRuntime().emitResourceMemberExpr(*this, E); + if (LV.has_value()) + return *LV; + } } Expr *BaseExpr = E->getBase(); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index c1329ede7430f..0e8f4be33df44 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -54,6 +54,7 @@ using namespace clang::hlsl; using namespace llvm; using llvm::hlsl::CBufferRowSizeInBytes; +using EmbeddedResourceNameBuilder = clang::hlsl::EmbeddedResourceNameBuilder; namespace { @@ -96,16 +97,105 @@ void addRootSignatureMD(llvm::dxbc::RootSignatureVersion RootSigVer, RootSignatureValMD->addOperand(MDVals); } +// Gived a MemberExpr of a resource or resource array type, find the parent +// VarDecl of the struct or class instance that contains this resource and +// build the full resource name based on the member access path. +// +// For example, for a member access like "myStructArray[0].memberA", +// this function will find the VarDecl of "myStructArray" and use the +// EmbeddedResourceNameBuilder to build the resource name +// "myStructArray.0.memberA". +static const VarDecl *getStructResourceParentDeclAndBuildName( + const MemberExpr *ME, EmbeddedResourceNameBuilder &NameBuilder) { + + SmallVector<const Expr *> WorkList; + const VarDecl *VD = nullptr; + const Expr *E = ME; + + while (!VD) { + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { + assert(isa<VarDecl>(DRE->getDecl()) && + "member expr base is not a var decl"); + VD = cast<VarDecl>(DRE->getDecl()); + NameBuilder.pushName(VD->getName()); + break; + } + + WorkList.push_back(E); + if (const auto *ME = dyn_cast<MemberExpr>(E)) + E = ME->getBase(); + else if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) + E = ICE->getSubExpr(); + else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) + E = ASE->getBase(); + else + llvm_unreachable("unexpected expr type in resource member access"); + } + + while (!WorkList.empty()) { + E = WorkList.pop_back_val(); + if (const auto *ME = dyn_cast<MemberExpr>(E)) { + NameBuilder.pushName( + ME->getMemberNameInfo().getName().getAsIdentifierInfo()->getName()); + } else if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) { + if (ICE->getCastKind() == CK_UncheckedDerivedToBase) { + CXXRecordDecl *DerivedRD = + ICE->getSubExpr()->getType()->getAsCXXRecordDecl(); + CXXRecordDecl *BaseRD = ICE->getType()->getAsCXXRecordDecl(); + NameBuilder.pushBaseNameHierarchy(DerivedRD, BaseRD); + } + } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) { + const Expr *IdxExpr = ASE->getIdx(); + std::optional<llvm::APSInt> Value = + IdxExpr->getIntegerConstantExpr(VD->getASTContext()); + assert(Value && + "expected constant index in struct with resource array access"); + NameBuilder.pushArrayIndex(Value->getZExtValue()); + } else { + llvm_unreachable("unexpected expr type in resource member access"); + } + } + return VD; +} + +// Given a MemberExpr of a resource or resource array type, find the +// corresponding global resource declaration associated with the owning struct +// or class instance via HLSLAssociatedResourceDeclAttr. +static const VarDecl * +findAssociatedResourceDeclForStruct(ASTContext &AST, const MemberExpr *ME) { + + EmbeddedResourceNameBuilder NameBuilder; + const VarDecl *ParentVD = + getStructResourceParentDeclAndBuildName(ME, NameBuilder); + + if (!ParentVD->hasGlobalStorage()) + return nullptr; + + IdentifierInfo *II = NameBuilder.getNameAsIdentifier(AST); + for (const Attr *A : ParentVD->getAttrs()) { + const auto *ADA = dyn_cast<HLSLAssociatedResourceDeclAttr>(A); + if (!ADA) + continue; + VarDecl *AssocResVD = dyn_cast<VarDecl>(ADA->getResDecl()); + if (AssocResVD->getIdentifier() == II) + return AssocResVD; + } + return nullptr; +} + // 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())) +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 (isa<MemberExpr>(E)) + return findAssociatedResourceDeclForStruct(AST, cast<MemberExpr>(E)); return nullptr; } // Find array variable declaration from nested array subscript AST nodes -static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) { +static const ValueDecl *getArrayDecl(ASTContext &AST, + const ArraySubscriptExpr *ASE) { const Expr *E = nullptr; while (ASE != nullptr) { E = ASE->getBase()->IgnoreImpCasts(); @@ -113,7 +203,7 @@ static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) { return nullptr; ASE = dyn_cast<ArraySubscriptExpr>(E); } - return getArrayDecl(E); + return getArrayDecl(AST, E); } // Get the total size of the array, or -1 if the array is unbounded. @@ -1227,8 +1317,8 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr( // 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)); + const VarDecl *ArrayDecl = dyn_cast_or_null<VarDecl>( + getArrayDecl(CGF.CGM.getContext(), ArraySubsExpr)); if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() || ArrayDecl->getStorageClass() == SC_Static) return std::nullopt; @@ -1325,7 +1415,8 @@ bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr, 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)); + const VarDecl *ArrayDecl = + dyn_cast_or_null<VarDecl>(getArrayDecl(CGF.CGM.getContext(), RHSExpr)); if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() || ArrayDecl->getStorageClass() == SC_Static) return false; @@ -1448,6 +1539,37 @@ std::optional<LValue> CGHLSLRuntime::emitBufferArraySubscriptExpr( return CGF.MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo); } +std::optional<LValue> +CGHLSLRuntime::emitResourceMemberExpr(CodeGenFunction &CGF, + const MemberExpr *ME) { + assert((ME->getType()->isHLSLResourceRecord() || + 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; + + GlobalVariable *ResGV = + cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(ResourceVD)); + const DataLayout &DL = CGM.getDataLayout(); + llvm::Type *Ty = ResGV->getValueType(); + CharUnits Align = CharUnits::fromQuantity(DL.getABITypeAlign(Ty)); + Address Addr = Address(ResGV, Ty, Align); + LValue LV = LValue::MakeAddr(Addr, ME->getType(), CGM.getContext(), + LValueBaseInfo(AlignmentSource::Type), + CGM.getTBAAAccessInfo(ME->getType())); + return LV; +} + namespace { /// Utility for emitting copies following the HLSL buffer layout rules (ie, /// copying out of a cbuffer). diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 466c809fdef78..1977dad693b52 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -291,6 +291,8 @@ class CGHLSLRuntime { QualType CType); LValue emitBufferMemberExpr(CodeGenFunction &CGF, const MemberExpr *E); + std::optional<LValue> emitResourceMemberExpr(CodeGenFunction &CGF, + const MemberExpr *E); private: void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, diff --git a/clang/test/CodeGenHLSL/resources/resources-in-structs-array.hlsl b/clang/test/CodeGenHLSL/resources/resources-in-structs-array.hlsl new file mode 100644 index 0000000000000..283d3616aa660 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/resources-in-structs-array.hlsl @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s + +// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) } + +// Array of structs with resources +struct A { + RWBuffer<float> Buf; +}; + +// CHECK: @arrayOfA.0.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[arrayOfA0BufStr:.*]] = private unnamed_addr constant [15 x i8] c"arrayOfA.0.Buf\00" +// CHECK: @arrayOfA.1.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[arrayOfA1BufStr:.*]] = private unnamed_addr constant [15 x i8] c"arrayOfA.1.Buf\00" + +[[vk::binding(0, 1)]] +A arrayOfA[2] : register(u0, space1); + +// Nested struct arrays with resources +struct G { + A multiArray[2][2]; +}; + +// CHECK: @gArray.0.multiArray.0.0.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray0MultiArray00BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.0.multiArray.0.0.Buf\00" +// CHECK: @gArray.0.multiArray.0.1.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray0MultiArray01BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.0.multiArray.0.1.Buf\00" +// CHECK: @gArray.0.multiArray.1.0.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray0MultiArray10BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.0.multiArray.1.0.Buf\00" +// CHECK: @gArray.0.multiArray.1.1.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray0MultiArray11BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.0.multiArray.1.1.Buf\00" +// CHECK: @gArray.1.multiArray.0.0.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray1MultiArray00BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.1.multiArray.0.0.Buf\00" +// CHECK: @gArray.1.multiArray.0.1.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray1MultiArray01BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.1.multiArray.0.1.Buf\00" +// CHECK: @gArray.1.multiArray.1.0.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray1MultiArray10BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.1.multiArray.1.0.Buf\00" +// CHECK: @gArray.1.multiArray.1.1.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[gArray1MultiArray11BufStr:.*]] = private unnamed_addr constant [28 x i8] c"gArray.1.multiArray.1.1.Buf\00" + +// Make sure they are initialized from binding +// +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @arrayOfA.0.Buf, +// CHECK-SAME: i32 noundef 0, i32 noundef 1, i32 noundef 1, i32 noundef 0, ptr noundef @[[arrayOfA0BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @arrayOfA.1.Buf, +// CHECK-SAME: i32 noundef 1, i32 noundef 1, i32 noundef 1, i32 noundef 0, ptr noundef @[[arrayOfA1BufStr]]) +// +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.0.multiArray.0.0.Buf, +// CHECK-SAME: i32 noundef 10, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray0MultiArray00BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.0.multiArray.0.1.Buf, +// CHECK-SAME: i32 noundef 11, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray0MultiArray01BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.0.multiArray.1.0.Buf, +// CHECK-SAME: i32 noundef 12, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray0MultiArray10BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.0.multiArray.1.1.Buf, +// CHECK-SAME: i32 noundef 13, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray0MultiArray11BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.1.multiArray.0.0.Buf, +// CHECK-SAME: i32 noundef 14, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray1MultiArray00BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.1.multiArray.0.1.Buf, +// CHECK-SAME: i32 noundef 15, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray1MultiArray01BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.1.multiArray.1.0.Buf, +// CHECK-SAME: i32 noundef 16, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray1MultiArray10BufStr]]) +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @gArray.1.multiArray.1.1.Buf, +// CHECK-SAME: i32 noundef 17, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[gArray1MultiArray11BufStr]]) + +[[vk::binding(10, 2)]] +G gArray[2] : register(u10, space2); + +// CHECK: define internal void @main()() +// CHECK-NEXT: entry: +[numthreads(1, 1, 1)] +void main() { + +// CHECK-NEXT: %[[PTR1:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @arrayOfA.1.Buf, i32 noundef 0) +// CHECK-NEXT: store float 1.000000e+00, ptr %[[PTR1]] + arrayOfA[1].Buf[0] = 1.0f; + +// CHECK-NEXT: %[[PTR2:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @gArray.1.multiArray.1.0.Buf, i32 noundef 0) +// CHECK-NEXT: store float 2.000000e+00, ptr %[[PTR2]] + gArray[1].multiArray[1][0].Buf[0] = 2.0f; + +// CHECK-NEXT: %[[PTR3:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @gArray.0.multiArray.0.1.Buf, i32 noundef 0) +// CHECK-NEXT: store float 3.000000e+00, ptr %[[PTR3]] + gArray[0].multiArray[0][1].Buf[0] = 3.0f; +} diff --git a/clang/test/CodeGenHLSL/resources/resources-in-structs-inheritance.hlsl b/clang/test/CodeGenHLSL/resources/resources-in-structs-inheritance.hlsl new file mode 100644 index 0000000000000..5b5b8270f8e86 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/resources-in-structs-inheritance.hlsl @@ -0,0 +1,132 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s + +// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) } +// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 0, 0) } +// CHECK: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) } +// CHECK: %"class.hlsl::StructuredBuffer.0" = type { target("dx.RawBuffer", i32, 0, 0) } + +// Simple inheritance +struct A { + RWBuffer<float> Buf; +}; + +struct C : A { + RWBuffer<float> Buf2; +}; + +// Global variables for resources c.A::Buf and c.Buf2 +// (Looks like llvm-cxxfilt doesn't demangle names with `::`.) +// +// CHECK: @"_ZL8c.A::Buf" = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[cABufStr:.*]] = private unnamed_addr constant [9 x i8] c"c.A::Buf\00" +// CHECK: @c.Buf2 = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[cBuf2Str:.*]] = private unnamed_addr constant [7 x i8] c"c.Buf2\00" + +[[vk::binding(3)]] +C c : register(u3); + +// Global variables for resources d.A::Buf and d.A.Buf +// +// CHECK: @"_ZL8d.A::Buf" = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[dABufStr1:.*]] = private unnamed_addr constant [9 x i8] c"d.A::Buf\00" +// CHECK: @d.A.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[dABufStr2:.*]] = private unnamed_addr constant [8 x i8] c"d.A.Buf\00" + +// Inheritance with same named field +struct D : A { + A A; +}; +D d; + +// Multiple resources kinds and inheritance +class B { + StructuredBuffer<int> SrvBufs[2]; +}; + +class E : B { +}; + +class F : E { + A a; + StructuredBuffer<float> SrvBuf; + SamplerState Samp; +}; + +// Global variables for resources f.a.Buf, f.SrvBuf and f.Samp. +// Resource array f.E::B::SrvBufs does not have a global, it is initialized on demand. +// +// CHECK: @f.a.Buf = internal global %"class.hlsl::RWBuffer" poison +// CHECK: @[[fABufStr:.*]] = private unnamed_addr constant [8 x i8] c"f.a.Buf\00" +// CHECK: @f.SrvBuf = internal global %"class.hlsl::StructuredBuffer" poison +// CHECK: @[[fSrvBufStr:.*]] = private unnamed_addr constant [9 x i8] c"f.SrvBuf\00" +// CHECK: @f.Samp = internal global %"class.hlsl::SamplerState" poison +// CHECK: @[[fSampStr:.*]] = private unnamed_addr constant [7 x i8] c"f.Samp\00" +// CHECK: @[[fEBSrvBufStr:.*]] = private unnamed_addr constant [16 x i8] c"f.E::B::SrvBufs\00" + +[[vk::binding(10)]] +F f : register(t0) : register(u20) : register(s3); + +// Make sure they are initialized from binding +// +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @"_ZL8c.A::Buf", +// CHECK-SAME: i32 noundef 3, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[cABufStr]]) + +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @c.Buf2, +// CHECK-SAME: i32 noundef 4, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[cBuf2Str]]) + +// CHECK: call void @hlsl::RWBuffer<float>::__createFromImplicitBinding({{.*}})(ptr {{.*}} @"_ZL8d.A::Buf", +// CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[dABufStr1]]) + +// CHECK: call void @hlsl::RWBuffer<float>::__createFromImplicitBinding({{.*}})(ptr {{.*}} @d.A.Buf, +// CHECK-SAME: i32 noundef 1, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[dABufStr2]]) + +// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @f.a.Buf, +// CHECK-SAME: i32 noundef 20, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[fABufStr]]) + +// CHECK: call void @hlsl::StructuredBuffer<float>::__createFromBinding({{.*}})(ptr {{.*}} @f.SrvBuf, +// CHECK-SAME: i32 noundef 2, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[fSrvBufStr]]) + +// CHECK: call void @hlsl::SamplerState::__createFromBinding({{.*}})(ptr {{.*}} @f.Samp, +// CHECK-SAME: i32 noundef 3, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[fSampStr]]) + +// CHECK: define internal void @main()() +// CHECK-NEXT: entry: +[numthreads(1, 1, 1)] +void main() { +// CHECK-NEXT: %i = alloca i32 +// CHECK-NEXT: %[[TMP:.*]] = alloca %"class.hlsl::StructuredBuffer.0" +// CHECK-NEXT: %a = alloca float + +// CHECK-NEXT: %[[PTR1:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @"_ZL8c.A::Buf", i32 noundef 0) +// CHECK-NEXT: store float 0x3FF3AE1480000000, ptr %[[PTR1:]] + c.Buf[0] = 1.230f; + +// CHECK-NEXT: %[[PTR2:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @c.Buf2, i32 noundef 0) +// CHECK-NEXT: store float 0x4002B851E0000000, ptr %[[PTR2:]] + c.Buf2[0] = 2.340f; + +// CHECK-NEXT: %[[PTR3:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @"_ZL8d.A::Buf", i32 noundef 0) +// CHECK-NEXT: store float 0x400B9999A0000000, ptr %[[PTR3:]] + d.Buf[0] = 3.450f; + +// CHECK-NEXT: %[[PTR4:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @d.A.Buf, i32 noundef 0) +// CHECK-NEXT: store float 0x40123D70A0000000, ptr %[[PTR4:]] + d.A.Buf[0] = 4.560f; + +// Resource array access - initilized on demand: +// CHECK-NEXT: call void @hlsl::StructuredBuffer<int>::__createFromBinding({{.*}})(ptr {{.*}} %[[TMP]], +// CHECK-SAME: i32 noundef 0, i32 noundef 0, i32 noundef 2, i32 noundef 0, ptr noundef @[[fEBSrvBufStr]]) +// CHECK-NEXT: %[[PTR5:.*]] = call {{.*}} ptr @hlsl::StructuredBuffer<int>::operator[](unsigned int) const(ptr {{.*}} %[[TMP]], i32 noundef 1) +// CHECK-NEXT: %[[VAL1:.*]] = load i32, ptr %[[PTR5]] +// CHECK-NEXT: store i32 %[[VAL1]], ptr %i + int i = f.SrvBufs[0][1]; + +// CHECK-NEXT: %[[PTR6:.*]] = call {{.*}} ptr @hlsl::StructuredBuffer<float>::operator[](unsigned int) const({{.*}} @f.SrvBuf, i32 noundef 0) +// CHECK-NEXT: %[[VAL2:.*]] = load float, ptr %[[PTR6]] +// CHECK-NEXT: store float %[[VAL2]], ptr %a + float a = f.SrvBuf[0]; + +// CHECK: [[PTR7:.*]]= call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @f.a.Buf, i32 noundef 0) +// CHECK: store float %{{.*}}, ptr %call6 + f.a.Buf[0] = (float)i + a; +} diff --git a/clang/test/CodeGenHLSL/resources/resources-in-structs.hlsl b/clang/test/CodeGenHLSL/resources/resources-in-structs.hlsl new file mode 100644 index 0000000000000..ed16b5be0cbb0 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/resources-in-structs.hlsl @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s + +// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) } + +// Single resource field in struct. +struct A { + RWBuffer<float> Buf; +}; + +// Global variable for resource a.Buf +// +// CHECK: @a.Buf = internal global %"class.hlsl::RWBuffer" poison, align 4 +// CHECK: @[[aBufStr:.*]] = private unnamed_addr constant [6 x i8] c"a.Buf\00", align 1 +[[vk::binding(0)]] +A a : register(u0); + +// Resource array in struct. +struct B { + RWBuffer<float> Bufs[10]; +}; + +// Resource arrays do not have a global, they are initialized on demand. Just check the string name is generated correctly. +// +// CHECK: @[[bBufsStr:.*]] = private unnamed_addr constant [7 x i8] c"b.Bufs\00", align 1 +[[vk::binding(2)]] +B b : register(u2); + +// Check that a.Buf is initialized from binding +// +// 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 {{.*}}(%"class.hlsl::RWBuffer") align 4 @a.Buf, i32 noundef 0, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[aBufStr]]) +// CHECK-NEXT: ret void + +// CHECK: define internal void @main()() +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[TMP:.*]] = alloca %"class.hlsl::RWBuffer", align 4 +[numthreads(1, 1, 1)] +void main() { + +// CHECK-NEXT: %[[PTR:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr noundef nonnull align 4 dereferenceable(4) @a.Buf, i32 noundef 0) #5 +// CHECK-NEXT: store float 0x3FF3AE1480000000, ptr %[[PTR]], align 4 + a.Buf[0] = 1.230f; + +// Resource array access - first create the resource from binding, then access the element and store to it. +// 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 %[[TMP]], i32 noundef 2, i32 noundef 0, i32 noundef 10, i32 noundef 5, ptr noundef @[[bBufsStr]]) +// CHECK-NEXT: %[[PTR2:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} %[[TMP]], i32 noundef 0) +// CHECK-NEXT: store float 0x40123D70A0000000, ptr %[[PTR2]], align 4 +// CHECK-NEXT: ret void + b.Bufs[5][0] = 4.56f; +} _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
