Author: Helena Kotas Date: 2026-05-27T23:08:30-07:00 New Revision: 9cd3c0b99e5df66adcc96a2fc03b9b177d6ca487
URL: https://github.com/llvm/llvm-project/commit/9cd3c0b99e5df66adcc96a2fc03b9b177d6ca487 DIFF: https://github.com/llvm/llvm-project/commit/9cd3c0b99e5df66adcc96a2fc03b9b177d6ca487.diff LOG: [HLSL] Codegen for handling global resource array initialization (#198891) When a global resource array is accessed - whether it is declared at a global scope or as part of a global struct instance - all of its resource elements should be initialized from binding into a temporary local resource array. This change intercepts the Clang codegen at the relevant places to allow `CGHLSLRuntime` handle this special global resource array initialization. Fixes #187087 Fixes #198888 Added: clang/test/CodeGenHLSL/resources/res-array-global-to-local.hlsl clang/test/CodeGenHLSL/resources/resources-in-structs-array-to-local.hlsl Modified: clang/lib/CodeGen/CGExpr.cpp clang/lib/CodeGen/CGExprAgg.cpp clang/lib/CodeGen/CGHLSLRuntime.cpp clang/lib/CodeGen/CGHLSLRuntime.h clang/test/CodeGenHLSL/resources/res-array-local2.hlsl clang/test/CodeGenHLSL/resources/resource-bindings.hlsl Removed: ################################################################################ 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 9f42ea09e3617..befc2659b2f4c 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -972,6 +972,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 911d8f1523f7c..33d76cbda494a 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; @@ -1443,7 +1447,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()); @@ -1453,20 +1457,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. @@ -1476,27 +1475,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 it as an LValue. Returns nullopt +// 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) { @@ -1588,18 +1623,16 @@ 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()) + return emitGlobalResourceArrayAsLValue(CGF, ResourceVD, ME->getExprLoc()); + + // 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..44a9ae6695e77 --- /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 [[BufA]]) +// 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 [[BufA]]) + +// - 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 initialized 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 [[BufA]]) +// 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 [[BufA]]) +// CHECK: ret void +void case2() { + // local one-dimensional array initialized 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 [[BufA]]) +// 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 [[BufA]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) + +// - 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 initialized 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// CHECK: ret void +void case5() { + // local multi-dimensional array initialized 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// 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 [[BufB]]) +// 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 cde351fe6e66f..0aaa608258669 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: [[BufA:@.*]] = private unnamed_addr constant [2 x i8] c"A\00", align 1 RWBuffer<float> A[3] : register(u0); RWStructuredBuffer<float> Out : register(u0); @@ -28,10 +32,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 [[BufA]]) +// 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 [[BufA]]) +// 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 [[BufA]]) -// 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]); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
