https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/156075
>From cfaf08898c046baaa41cb1cbe75ed9682da8bc1f Mon Sep 17 00:00:00 2001 From: Steven Perron <stevenper...@google.com> Date: Fri, 29 Aug 2025 13:40:55 -0400 Subject: [PATCH 1/2] [HLSL] Add copy assignment and construtor to resource types The wrapper used to hold the handle for resource type has just the default copy constructor and assignment operator. This causes clang to insert memcpys when it does an assignment of a resource type. The memcpy then cause optimizations to fail when the memcpy is turned into a load and store of an i64. To fix this, we should define copying of a resource type by adding the operator= and copy constructor. Partially fixes #154669 --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 69 +++++++++++++++++++ clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 2 + clang/lib/Sema/HLSLExternalSemaSource.cpp | 2 + 3 files changed, 73 insertions(+) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 806800cb7b213..6c39fe504d3f3 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -676,6 +676,75 @@ BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() { .finalize(); } +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() { + if (Record->isCompleteDefinition()) + return *this; + + ASTContext &AST = SemaRef.getASTContext(); + QualType RecordType = AST.getCanonicalTagType(Record); + QualType ConstRecordType = RecordType.withConst(); + QualType ConstRecordRefType = AST.getLValueReferenceType(ConstRecordType); + + using PH = BuiltinTypeMethodBuilder::PlaceHolder; + + BuiltinTypeMethodBuilder Builder(*this, "", AST.VoidTy, false, true); + Builder.addParam("other", ConstRecordRefType); + Builder.ensureCompleteDecl(); + + ParmVarDecl *OtherParam = Builder.Method->getParamDecl(0); + + Expr *OtherDeclRef = DeclRefExpr::Create( + AST, NestedNameSpecifierLoc(), SourceLocation(), OtherParam, false, + DeclarationNameInfo(OtherParam->getDeclName(), SourceLocation()), + ConstRecordType, VK_LValue); + + FieldDecl *HandleField = getResourceHandleField(); + Expr *OtherHandleMemberExpr = MemberExpr::CreateImplicit( + AST, OtherDeclRef, false, HandleField, HandleField->getType(), VK_LValue, + OK_Ordinary); + + return Builder.assign(PH::Handle, OtherHandleMemberExpr).finalize(); +} + +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() { + if (Record->isCompleteDefinition()) + return *this; + + ASTContext &AST = SemaRef.getASTContext(); + QualType RecordType = AST.getCanonicalTagType(Record); + QualType ConstRecordType = RecordType.withConst(); + QualType ConstRecordRefType = AST.getLValueReferenceType(ConstRecordType); + QualType RecordRefType = AST.getLValueReferenceType(RecordType); + + using PH = BuiltinTypeMethodBuilder::PlaceHolder; + + DeclarationName Name = AST.DeclarationNames.getCXXOperatorName(OO_Equal); + BuiltinTypeMethodBuilder Builder(*this, Name, RecordRefType, false, false); + Builder.addParam("other", ConstRecordRefType); + Builder.ensureCompleteDecl(); + + ParmVarDecl *OtherParam = Builder.Method->getParamDecl(0); + Expr *OtherDeclRef = DeclRefExpr::Create( + AST, NestedNameSpecifierLoc(), SourceLocation(), OtherParam, false, + DeclarationNameInfo(OtherParam->getDeclName(), SourceLocation()), + ConstRecordType, VK_LValue); + + FieldDecl *HandleField = getResourceHandleField(); + Expr *OtherHandleMemberExpr = MemberExpr::CreateImplicit( + AST, OtherDeclRef, false, HandleField, HandleField->getType(), VK_LValue, + OK_Ordinary); + + Builder.assign(PH::Handle, OtherHandleMemberExpr); + + // return *this; + CXXThisExpr *This = CXXThisExpr::Create( + AST, SourceLocation(), Builder.Method->getFunctionObjectParameterType(), + true); + Builder.StmtsList.push_back(This); + + return Builder.finalize(); +} + BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() { ASTContext &AST = Record->getASTContext(); DeclarationName Subscript = diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 098b72692bd3a..4c4c2083a8440 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -80,6 +80,8 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder &addDefaultHandleConstructor(); BuiltinTypeDeclBuilder &addHandleConstructorFromBinding(); BuiltinTypeDeclBuilder &addHandleConstructorFromImplicitBinding(); + BuiltinTypeDeclBuilder &addCopyConstructor(); + BuiltinTypeDeclBuilder &addCopyAssignmentOperator(); // Builtin types methods BuiltinTypeDeclBuilder &addLoadMethods(); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 726581d131623..8c893c0c30baf 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -132,6 +132,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, return BuiltinTypeDeclBuilder(S, Decl) .addHandleMember(RC, IsROV, RawBuffer) .addDefaultHandleConstructor() + .addCopyConstructor() + .addCopyAssignmentOperator() .addHandleConstructorFromBinding() .addHandleConstructorFromImplicitBinding(); } >From 54cbbbc720388b69210bb1191bb61fe41c5ae35a Mon Sep 17 00:00:00 2001 From: Steven Perron <stevenper...@google.com> Date: Fri, 29 Aug 2025 14:47:41 -0400 Subject: [PATCH 2/2] update res-array-local tests --- .../resources/res-array-local1.hlsl | 128 +++++++++--------- .../resources/res-array-local3.hlsl | 124 ++++++++--------- 2 files changed, 126 insertions(+), 126 deletions(-) diff --git a/clang/test/CodeGenHLSL/resources/res-array-local1.hlsl b/clang/test/CodeGenHLSL/resources/res-array-local1.hlsl index c0d508b1395c3..9e31f4f150c52 100644 --- a/clang/test/CodeGenHLSL/resources/res-array-local1.hlsl +++ b/clang/test/CodeGenHLSL/resources/res-array-local1.hlsl @@ -1,64 +1,64 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \ -// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s - -// This test verifies local arrays of resources in HLSL. - -// CHECK: @_ZL1A = internal global %"class.hlsl::RWBuffer" poison, align 4 -// CHECK: @_ZL1B = internal global %"class.hlsl::RWBuffer" poison, align 4 -// CHECK: @_ZL1C = internal global %"class.hlsl::RWBuffer" poison, align 4 - -RWBuffer<float> A : register(u1); -RWBuffer<float> B : register(u2); -RWBuffer<float> C : register(u3); -RWStructuredBuffer<float> Out : register(u0); - -// CHECK: define internal void @_Z4mainv() -// CHECK-NEXT: entry: -[numthreads(4,1,1)] -void main() { -// CHECK-NEXT: %First = alloca [3 x %"class.hlsl::RWBuffer"], align 4 -// CHECK-NEXT: %Second = alloca [4 x %"class.hlsl::RWBuffer"], align 4 - RWBuffer<float> First[3] = { A, B, C }; - RWBuffer<float> Second[4]; - -// Verify initialization of First array from an initialization list -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %First, ptr align 4 @_ZL1A, i32 4, i1 false) -// CHECK-NEXT: %[[Ptr1:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %First, i32 1 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr1]], ptr align 4 @_ZL1B, i32 4, i1 false) -// CHECK-NEXT: %[[Ptr2:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %First, i32 2 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr2]], ptr align 4 @_ZL1C, i32 4, i1 false) - -// Verify default initialization of Second array, which means there is a loop iterating -// over the array elements and calling the default constructor for each -// CHECK-NEXT: %[[ArrayBeginPtr:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 0 -// CHECK-NEXT: %[[ArrayEndPtr:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[ArrayBeginPtr]], i32 4 -// CHECK-NEXT: br label %[[ArrayInitLoop:.*]] -// CHECK: [[ArrayInitLoop]]: -// CHECK-NEXT: %[[ArrayCurPtr:.*]] = phi ptr [ %[[ArrayBeginPtr]], %entry ], [ %[[ArrayNextPtr:.*]], %[[ArrayInitLoop]] ] -// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1Ev(ptr {{.*}} %[[ArrayCurPtr]]) -// CHECK-NEXT: %[[ArrayNextPtr]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[ArrayCurPtr]], i32 1 -// CHECK-NEXT: %[[ArrayInitDone:.*]] = icmp eq ptr %[[ArrayNextPtr]], %[[ArrayEndPtr]] -// CHECK-NEXT: br i1 %[[ArrayInitDone]], label %[[AfterArrayInit:.*]], label %[[ArrayInitLoop]] -// CHECK: [[AfterArrayInit]]: - -// Initialize First[2] with C -// CHECK: %[[Ptr3:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 2 -// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr3]], ptr align 4 @_ZL1C, i32 4, i1 false) - Second[2] = C; - - // NOTE: _ZN4hlsl8RWBufferIfEixEj is the subscript operator for RWBuffer<float> - -// get First[1][0] value -// CHECK: %[[First_1_Ptr:.*]] = getelementptr inbounds [3 x %"class.hlsl::RWBuffer"], ptr %First, i32 0, i32 1 -// CHECK: %[[BufPtr1:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[First_1_Ptr]], i32 noundef 0) -// CHECK: %[[Value1:.*]] = load float, ptr %[[BufPtr1]], align 4 - -// get Second[2][0] value -// CHECK: %[[Second_2_Ptr:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 2 -// CHECK: %[[BufPtr2:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[Second_2_Ptr]], i32 noundef 0) -// CHECK: %[[Value2:.*]] = load float, ptr %[[BufPtr2]], align 4 - -// add them -// CHECK: %{{.*}} = fadd {{.*}} float %[[Value1]], %[[Value2]] - Out[0] = First[1][0] + Second[2][0]; -} +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +// This test verifies local arrays of resources in HLSL. + +// CHECK: @_ZL1A = internal global %"class.hlsl::RWBuffer" poison, align 4 +// CHECK: @_ZL1B = internal global %"class.hlsl::RWBuffer" poison, align 4 +// CHECK: @_ZL1C = internal global %"class.hlsl::RWBuffer" poison, align 4 + +RWBuffer<float> A : register(u1); +RWBuffer<float> B : register(u2); +RWBuffer<float> C : register(u3); +RWStructuredBuffer<float> Out : register(u0); + +// CHECK: define internal void @_Z4mainv() +// CHECK-NEXT: entry: +[numthreads(4,1,1)] +void main() { +// CHECK-NEXT: %First = alloca [3 x %"class.hlsl::RWBuffer"], align 4 +// CHECK-NEXT: %Second = alloca [4 x %"class.hlsl::RWBuffer"], align 4 + RWBuffer<float> First[3] = { A, B, C }; + RWBuffer<float> Second[4]; + +// Verify initialization of First array from an initialization list +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr {{.*}} %First, ptr {{.*}} @_ZL1A) +// CHECK-NEXT: %[[Ptr1:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %First, i32 1 +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr {{.*}} %[[Ptr1]], ptr {{.*}} @_ZL1B) +// CHECK-NEXT: %[[Ptr2:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %First, i32 2 +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr {{.*}} %[[Ptr2]], ptr {{.*}} @_ZL1C) + +// Verify default initialization of Second array, which means there is a loop iterating +// over the array elements and calling the default constructor for each +// CHECK-NEXT: %[[ArrayBeginPtr:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 0 +// CHECK-NEXT: %[[ArrayEndPtr:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[ArrayBeginPtr]], i32 4 +// CHECK-NEXT: br label %[[ArrayInitLoop:.*]] +// CHECK: [[ArrayInitLoop]]: +// CHECK-NEXT: %[[ArrayCurPtr:.*]] = phi ptr [ %[[ArrayBeginPtr]], %entry ], [ %[[ArrayNextPtr:.*]], %[[ArrayInitLoop]] ] +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1Ev(ptr {{.*}} %[[ArrayCurPtr]]) +// CHECK-NEXT: %[[ArrayNextPtr]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[ArrayCurPtr]], i32 1 +// CHECK-NEXT: %[[ArrayInitDone:.*]] = icmp eq ptr %[[ArrayNextPtr]], %[[ArrayEndPtr]] +// CHECK-NEXT: br i1 %[[ArrayInitDone]], label %[[AfterArrayInit:.*]], label %[[ArrayInitLoop]] +// CHECK: [[AfterArrayInit]]: + +// Initialize First[2] with C +// CHECK: %[[Ptr3:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 2 +// CHECK: call {{.*}} @_ZN4hlsl8RWBufferIfEaSERKS1_(ptr {{.*}} %[[Ptr3]], ptr {{.*}} @_ZL1C) + Second[2] = C; + + // NOTE: _ZN4hlsl8RWBufferIfEixEj is the subscript operator for RWBuffer<float> + +// get First[1][0] value +// CHECK: %[[First_1_Ptr:.*]] = getelementptr inbounds [3 x %"class.hlsl::RWBuffer"], ptr %First, i32 0, i32 1 +// CHECK: %[[BufPtr1:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[First_1_Ptr]], i32 noundef 0) +// CHECK: %[[Value1:.*]] = load float, ptr %[[BufPtr1]], align 4 + +// get Second[2][0] value +// CHECK: %[[Second_2_Ptr:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 2 +// CHECK: %[[BufPtr2:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[Second_2_Ptr]], i32 noundef 0) +// CHECK: %[[Value2:.*]] = load float, ptr %[[BufPtr2]], align 4 + +// add them +// CHECK: %{{.*}} = fadd {{.*}} float %[[Value1]], %[[Value2]] + Out[0] = First[1][0] + Second[2][0]; +} diff --git a/clang/test/CodeGenHLSL/resources/res-array-local3.hlsl b/clang/test/CodeGenHLSL/resources/res-array-local3.hlsl index e5bcdc651254f..21ca3f4a98f99 100644 --- a/clang/test/CodeGenHLSL/resources/res-array-local3.hlsl +++ b/clang/test/CodeGenHLSL/resources/res-array-local3.hlsl @@ -1,62 +1,62 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \ -// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s - -// This test verifies handling of local arrays of resources when used -// as a function argument that is modified inside the function. - -// CHECK: @_ZL1X = internal global %"class.hlsl::RWBuffer" poison, align 4 -// CHECK: @_ZL1Y = internal global %"class.hlsl::RWBuffer" poison, align 4 - -RWBuffer<int> X : register(u0); -RWBuffer<int> Y : register(u1); - -// CHECK: define {{.*}} @_Z6SomeFnA2_N4hlsl8RWBufferIiEEji( -// CHECK-SAME: ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 %B, i32 noundef %Idx, i32 noundef %Val0) -// CHECK-NEXT: entry: -// CHECK-NEXT: %[[Idx_addr:.*]] = alloca i32, align 4 -// CHECK-NEXT: %[[Val0_addr:.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 %Idx, ptr %[[Idx_addr]], align 4 -// CHECK-NEXT: store i32 %Val0, ptr %[[Val0_addr]], align 4 -void SomeFn(RWBuffer<int> B[2], uint Idx, int Val0) { - -// CHECK-NEXT: %[[B_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %B, i32 0, i32 0 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[B_0_Ptr]], ptr align 4 @_ZL1Y, i32 4, i1 false) - B[0] = Y; - -// NOTE: _ZN4hlsl8RWBufferIiEixEj is the subscript operator for RWBuffer<int> - -// CHECK-NEXT: %[[Val0:.*]] = load i32, ptr %[[Val0_addr]], align 4 -// CHECK-NEXT: %[[B_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %B, i32 0, i32 0 -// CHECK-NEXT: %[[Idx:.*]] = load i32, ptr %[[Idx_addr]], align 4 -// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIiEixEj(ptr {{.*}} %[[B_0_Ptr]], i32 noundef %[[Idx]]) -// CHECK-NEXT: store i32 %[[Val0]], ptr %[[BufPtr]], align 4 - B[0][Idx] = Val0; -} - -// CHECK: define {{.*}} void @_Z4mainj(i32 noundef %GI) -// CHECK-NEXT: entry: -// CHECK-NEXT: %[[GI_addr:.*]] = alloca i32, align 4 -[numthreads(4,1,1)] -void main(uint GI : SV_GroupIndex) { -// CHECK-NEXT: %A = alloca [2 x %"class.hlsl::RWBuffer"], align 4 -// CHECK-NEXT: %[[Tmp:.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4 -// CHECK-NEXT: store i32 %GI, ptr %GI.addr, align 4 - -// Initialization of array A with resources X and Y -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %A, ptr align 4 @_ZL1X, i32 4, i1 false) -// CHECK-NEXT: %[[A_1_Ptr:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %A, i32 1 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[A_1_Ptr]], ptr align 4 @_ZL1Y, i32 4, i1 false) - RWBuffer<int> A[2] = {X, Y}; - -// Verify that SomeFn is called with a local copy of the array A -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp]], ptr align 4 %A, i32 8, i1 false) -// CHECK-NEXT: %[[GI:.*]] = load i32, ptr %[[GI_addr]], align 4 -// CHECK-NEXT: call void @_Z6SomeFnA2_N4hlsl8RWBufferIiEEji(ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 %[[Tmp]], i32 noundef %[[GI]], i32 noundef 1) - SomeFn(A, GI, 1); - -// CHECK-NEXT: %[[A_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %A, i32 0, i32 0 -// CHECK-NEXT: %[[GI:.*]] = load i32, ptr %[[GI_addr]], align 4 -// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIiEixEj(ptr {{.*}} %[[A_0_Ptr]], i32 noundef %[[GI]]) -// CHECK-NEXT: store i32 2, ptr %[[BufPtr]], align 4 - A[0][GI] = 2; -} +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +// This test verifies handling of local arrays of resources when used +// as a function argument that is modified inside the function. + +// CHECK: @_ZL1X = internal global %"class.hlsl::RWBuffer" poison, align 4 +// CHECK: @_ZL1Y = internal global %"class.hlsl::RWBuffer" poison, align 4 + +RWBuffer<int> X : register(u0); +RWBuffer<int> Y : register(u1); + +// CHECK: define {{.*}} @_Z6SomeFnA2_N4hlsl8RWBufferIiEEji( +// CHECK-SAME: ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 %B, i32 noundef %Idx, i32 noundef %Val0) +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[Idx_addr:.*]] = alloca i32, align 4 +// CHECK-NEXT: %[[Val0_addr:.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 %Idx, ptr %[[Idx_addr]], align 4 +// CHECK-NEXT: store i32 %Val0, ptr %[[Val0_addr]], align 4 +void SomeFn(RWBuffer<int> B[2], uint Idx, int Val0) { + +// CHECK-NEXT: %[[B_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %B, i32 0, i32 0 +// CHECK-NEXT: call {{.*}} @_ZN4hlsl8RWBufferIiEaSERKS1_(ptr {{.*}} %[[B_0_Ptr]], ptr {{.*}} @_ZL1Y) + B[0] = Y; + +// NOTE: _ZN4hlsl8RWBufferIiEixEj is the subscript operator for RWBuffer<int> + +// CHECK-NEXT: %[[Val0:.*]] = load i32, ptr %[[Val0_addr]], align 4 +// CHECK-NEXT: %[[B_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %B, i32 0, i32 0 +// CHECK-NEXT: %[[Idx:.*]] = load i32, ptr %[[Idx_addr]], align 4 +// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIiEixEj(ptr {{.*}} %[[B_0_Ptr]], i32 noundef %[[Idx]]) +// CHECK-NEXT: store i32 %[[Val0]], ptr %[[BufPtr]], align 4 + B[0][Idx] = Val0; +} + +// CHECK: define {{.*}} void @_Z4mainj(i32 noundef %GI) +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[GI_addr:.*]] = alloca i32, align 4 +[numthreads(4,1,1)] +void main(uint GI : SV_GroupIndex) { +// CHECK-NEXT: %A = alloca [2 x %"class.hlsl::RWBuffer"], align 4 +// CHECK-NEXT: %[[Tmp:.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4 +// CHECK-NEXT: store i32 %GI, ptr %GI.addr, align 4 + +// Initialization of array A with resources X and Y +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1ERKS1_(ptr {{.*}} %A, ptr {{.*}} @_ZL1X) +// CHECK-NEXT: %[[A_1_Ptr:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %A, i32 1 +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1ERKS1_(ptr {{.*}} %[[A_1_Ptr]], ptr {{.*}} @_ZL1Y) + RWBuffer<int> A[2] = {X, Y}; + +// Verify that SomeFn is called with a local copy of the array A +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp]], ptr align 4 %A, i32 8, i1 false) +// CHECK-NEXT: %[[GI:.*]] = load i32, ptr %[[GI_addr]], align 4 +// CHECK-NEXT: call void @_Z6SomeFnA2_N4hlsl8RWBufferIiEEji(ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 %[[Tmp]], i32 noundef %[[GI]], i32 noundef 1) + SomeFn(A, GI, 1); + +// CHECK-NEXT: %[[A_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %A, i32 0, i32 0 +// CHECK-NEXT: %[[GI:.*]] = load i32, ptr %[[GI_addr]], align 4 +// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIiEixEj(ptr {{.*}} %[[A_0_Ptr]], i32 noundef %[[GI]]) +// CHECK-NEXT: store i32 2, ptr %[[BufPtr]], align 4 + A[0][GI] = 2; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits