Skip to content

Commit fb365c3

Browse files
hekotakcloudy0717
authored andcommitted
[HLSL] Static resources (llvm#166880)
This change fixes couple of issues with static resources: - Enables assignment to static resource or resource array variables (fixes llvm#166458) - Initializes static resources and resource arrays with default constructor that sets the handle to poison
1 parent a9a169b commit fb365c3

File tree

6 files changed

+213
-13
lines changed

6 files changed

+213
-13
lines changed

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6356,8 +6356,15 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
63566356
LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) {
63576357
// Don't emit an LValue for the RHS because it might not be an LValue
63586358
LValue LHS = EmitLValue(E->getLHS());
6359+
6360+
// If the RHS is a global resource array, copy all individual resources
6361+
// into LHS.
6362+
if (E->getRHS()->getType()->isHLSLResourceRecordArray())
6363+
if (CGM.getHLSLRuntime().emitResourceArrayCopy(LHS, E->getRHS(), *this))
6364+
return LHS;
6365+
63596366
// In C the RHS of an assignment operator is an RValue.
6360-
// EmitAggregateAssign takes anan LValue for the RHS. Instead we can call
6367+
// EmitAggregateAssign takes an LValue for the RHS. Instead we can call
63616368
// EmitInitializationToLValue to emit an RValue into an LValue.
63626369
EmitInitializationToLValue(E->getRHS(), LHS);
63636370
return LHS;

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "clang/AST/ASTContext.h"
2323
#include "clang/AST/Attrs.inc"
2424
#include "clang/AST/Decl.h"
25+
#include "clang/AST/Expr.h"
2526
#include "clang/AST/HLSLResource.h"
2627
#include "clang/AST/RecursiveASTVisitor.h"
2728
#include "clang/AST/Type.h"
@@ -94,6 +95,14 @@ void addRootSignatureMD(llvm::dxbc::RootSignatureVersion RootSigVer,
9495
RootSignatureValMD->addOperand(MDVals);
9596
}
9697

98+
// Find array variable declaration from DeclRef expression
99+
static const ValueDecl *getArrayDecl(const Expr *E) {
100+
if (const DeclRefExpr *DRE =
101+
dyn_cast_or_null<DeclRefExpr>(E->IgnoreImpCasts()))
102+
return DRE->getDecl();
103+
return nullptr;
104+
}
105+
97106
// Find array variable declaration from nested array subscript AST nodes
98107
static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
99108
const Expr *E = nullptr;
@@ -103,9 +112,7 @@ static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
103112
return nullptr;
104113
ASE = dyn_cast<ArraySubscriptExpr>(E);
105114
}
106-
if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E))
107-
return DRE->getDecl();
108-
return nullptr;
115+
return getArrayDecl(E);
109116
}
110117

111118
// Get the total size of the array, or -1 if the array is unbounded.
@@ -1214,12 +1221,13 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
12141221
ArraySubsExpr->getType()->isHLSLResourceRecordArray()) &&
12151222
"expected resource array subscript expression");
12161223

1217-
// Let clang codegen handle local resource array subscripts,
1224+
// Let clang codegen handle local and static resource array subscripts,
12181225
// or when the subscript references on opaque expression (as part of
12191226
// ArrayInitLoopExpr AST node).
12201227
const VarDecl *ArrayDecl =
12211228
dyn_cast_or_null<VarDecl>(getArrayDecl(ArraySubsExpr));
1222-
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
1229+
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
1230+
ArrayDecl->getStorageClass() == SC_Static)
12231231
return std::nullopt;
12241232

12251233
// get the resource array type
@@ -1249,7 +1257,7 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
12491257
// Find binding info for the resource array. For implicit binding
12501258
// an HLSLResourceBindingAttr should have been added by SemaHLSL.
12511259
ResourceBindingAttrs Binding(ArrayDecl);
1252-
assert((Binding.hasBinding()) &&
1260+
assert(Binding.hasBinding() &&
12531261
"resource array must have a binding attribute");
12541262

12551263
// Find the individual resource type.
@@ -1305,6 +1313,49 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
13051313
return CGF.MakeAddrLValue(TmpVar, ResultTy, AlignmentSource::Decl);
13061314
}
13071315

1316+
// If RHSExpr is a global resource array, initialize all of its resources and
1317+
// set them into LHS. Returns false if no copy has been performed and the
1318+
// array copy should be handled by Clang codegen.
1319+
bool CGHLSLRuntime::emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr,
1320+
CodeGenFunction &CGF) {
1321+
QualType ResultTy = RHSExpr->getType();
1322+
assert(ResultTy->isHLSLResourceRecordArray() && "expected resource array");
1323+
1324+
// Let Clang codegen handle local and static resource array copies.
1325+
const VarDecl *ArrayDecl = dyn_cast_or_null<VarDecl>(getArrayDecl(RHSExpr));
1326+
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
1327+
ArrayDecl->getStorageClass() == SC_Static)
1328+
return false;
1329+
1330+
// Find binding info for the resource array. For implicit binding
1331+
// the HLSLResourceBindingAttr should have been added by SemaHLSL.
1332+
ResourceBindingAttrs Binding(ArrayDecl);
1333+
assert(Binding.hasBinding() &&
1334+
"resource array must have a binding attribute");
1335+
1336+
// Find the individual resource type.
1337+
ASTContext &AST = ArrayDecl->getASTContext();
1338+
QualType ResTy = AST.getBaseElementType(ResultTy);
1339+
const auto *ResArrayTy = cast<ConstantArrayType>(ResultTy.getTypePtr());
1340+
1341+
// Use the provided LHS for the result.
1342+
AggValueSlot ValueSlot = AggValueSlot::forAddr(
1343+
LHS.getAddress(), Qualifiers(), AggValueSlot::IsDestructed_t(true),
1344+
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
1345+
AggValueSlot::DoesNotOverlap);
1346+
1347+
// Create Value for index and total array size (= range size).
1348+
int Size = getTotalArraySize(AST, ResArrayTy);
1349+
llvm::Value *Zero = llvm::ConstantInt::get(CGM.IntTy, 0);
1350+
llvm::Value *Range = llvm::ConstantInt::get(CGM.IntTy, Size);
1351+
1352+
// Initialize individual resources in the array into LHS.
1353+
std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray(
1354+
CGF, ResTy->getAsCXXRecordDecl(), ResArrayTy, ValueSlot, Range, Zero,
1355+
ArrayDecl->getName(), Binding, {Zero}, RHSExpr->getExprLoc());
1356+
return EndIndex.has_value();
1357+
}
1358+
13081359
std::optional<LValue> CGHLSLRuntime::emitBufferArraySubscriptExpr(
13091360
const ArraySubscriptExpr *E, CodeGenFunction &CGF,
13101361
llvm::function_ref<llvm::Value *(bool Promote)> EmitIdxAfterBase) {

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ class CGHLSLRuntime {
258258
std::optional<LValue>
259259
emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
260260
CodeGenFunction &CGF);
261+
bool emitResourceArrayCopy(LValue &LHS, Expr *RHSExpr, CodeGenFunction &CGF);
261262

262263
std::optional<LValue> emitBufferArraySubscriptExpr(
263264
const ArraySubscriptExpr *E, CodeGenFunction &CGF,

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5976,7 +5976,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
59765976
(D->getType()->isHLSLResourceRecord() ||
59775977
D->getType()->isHLSLResourceRecordArray())) {
59785978
Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
5979-
NeedsGlobalCtor = D->getType()->isHLSLResourceRecord();
5979+
NeedsGlobalCtor = D->getType()->isHLSLResourceRecord() ||
5980+
D->getStorageClass() == SC_Static;
59805981
} else if (D->hasAttr<LoaderUninitializedAttr>()) {
59815982
Init = llvm::UndefValue::get(getTypes().ConvertTypeForMem(ASTTy));
59825983
} else if (!InitExpr) {

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4018,7 +4018,9 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
40184018
// process explicit bindings
40194019
processExplicitBindingsOnDecl(VD);
40204020

4021-
if (VD->getType()->isHLSLResourceRecordArray()) {
4021+
// Add implicit binding attribute to non-static resource arrays.
4022+
if (VD->getType()->isHLSLResourceRecordArray() &&
4023+
VD->getStorageClass() != SC_Static) {
40224024
// If the resource array does not have an explicit binding attribute,
40234025
// create an implicit one. It will be used to transfer implicit binding
40244026
// order_ID to codegen.
@@ -4212,8 +4214,8 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
42124214
if (VD->getType().getAddressSpace() == LangAS::hlsl_constant)
42134215
return true;
42144216

4215-
// Initialize resources at the global scope
4216-
if (VD->hasGlobalStorage()) {
4217+
// Initialize non-static resources at the global scope.
4218+
if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
42174219
const Type *Ty = VD->getType().getTypePtr();
42184220
if (Ty->isHLSLResourceRecord())
42194221
return initGlobalResourceDecl(VD);
@@ -4237,10 +4239,10 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr,
42374239
while (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
42384240
E = ASE->getBase()->IgnoreParenImpCasts();
42394241

4240-
// Report error if LHS is a resource declared at a global scope.
4242+
// Report error if LHS is a non-static resource declared at a global scope.
42414243
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
42424244
if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
4243-
if (VD->hasGlobalStorage()) {
4245+
if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
42444246
// assignment to global resource is not allowed
42454247
SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD;
42464248
SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD;
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s
2+
3+
// CHECK-DAG: [[ONE_STR:@.*]] = private unnamed_addr constant [4 x i8] c"One\00"
4+
// CHECK-DAG: [[ARRAY_STR:@.*]] = private unnamed_addr constant [6 x i8] c"Array\00"
5+
// CHECK-DAG: [[ONEWITHCOUNTER_STR:@.*]] = private unnamed_addr constant [15 x i8] c"OneWithCounter\00"
6+
// CHECK-DAG: [[ARRAYWITHCOUNTER_STR:@.*]] = private unnamed_addr constant [17 x i8] c"ArrayWithCounter\00"
7+
// CHECK-NOT: private unnamed_addr constant [{{[0-9]+}} x i8] c"Static
8+
9+
RWBuffer<float> One : register(u1, space5);
10+
RWBuffer<float> Array[2] : register(u10, space6);
11+
RWStructuredBuffer<int> OneWithCounter : register(u2, space4);
12+
RWStructuredBuffer<int> ArrayWithCounter[2] : register(u7, space4);
13+
14+
// Check that the non-static resource One is initialized from binding on
15+
// startup (register 1, space 5).
16+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
17+
// CHECK-NEXT: entry:
18+
// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
19+
// CHECK-SAME: (ptr {{.*}} @One, i32 noundef 1, i32 noundef 5, i32 noundef 1, i32 noundef 0, ptr noundef [[ONE_STR]])
20+
21+
// Check that the non-static resource OneWithCounter is initialized from binding on
22+
// startup (register 2, space 4).
23+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
24+
// CHECK-NEXT: entry:
25+
// CHECK-NEXT: call void @hlsl::RWStructuredBuffer<int>::__createFromBindingWithImplicitCounter(unsigned int, unsigned int, int, unsigned int, char const*, unsigned int)
26+
// CHECK-SAME: (ptr {{.*}} @OneWithCounter, i32 noundef 2, i32 noundef 4, i32 noundef 1, i32 noundef 0, ptr noundef [[ONEWITHCOUNTER_STR]], i32 noundef 0)
27+
28+
// Note that non-static resource arrays are not initialized on startup.
29+
// The individual resources from the array are initialized on access.
30+
31+
static RWBuffer<float> StaticOne;
32+
static RWBuffer<float> StaticArray[2];
33+
34+
// Check that StaticOne resource is initialized on startup with the default
35+
// constructor and not from binding. It will initalize the handle to poison.
36+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
37+
// CHECK-NEXT: entry:
38+
// CHECK-NEXT: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} @StaticOne)
39+
40+
// Check that StaticArray elements are initialized on startup with the default
41+
// constructor and not from binding. The initializer will loop over the array
42+
// elements and call the default constructor for each one, setting the handle to poison.
43+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
44+
// CHECK-NEXT: entry:
45+
// CHECK-NEXT: br label %arrayctor.loop
46+
// CHECK: arrayctor.loop: ; preds = %arrayctor.loop, %entry
47+
// CHECK-NEXT: %arrayctor.cur = phi ptr [ @StaticArray, %entry ], [ %arrayctor.next, %arrayctor.loop ]
48+
// CHECK-NEXT: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} %arrayctor.cur)
49+
// CHECK-NEXT: %arrayctor.next = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %arrayctor.cur, i32 1
50+
// CHECK-NEXT: %arrayctor.done = icmp eq ptr %arrayctor.next, getelementptr inbounds (%"class.hlsl::RWBuffer", ptr @StaticArray, i32 2)
51+
// CHECK-NEXT: br i1 %arrayctor.done, label %arrayctor.cont, label %arrayctor.loop
52+
// CHECK: arrayctor.cont: ; preds = %arrayctor.loop
53+
// CHECK-NEXT: ret void
54+
55+
static RWStructuredBuffer<int> StaticOneWithCounter;
56+
57+
// Check that StaticOneWithCounter resource is initialized on startup with the default
58+
// constructor and not from binding. It will initalize the handle to poison.
59+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
60+
// CHECK-NEXT: entry:
61+
// CHECK-NEXT: call void @hlsl::RWStructuredBuffer<int>::RWStructuredBuffer()(ptr {{.*}} @StaticOneWithCounter)
62+
63+
// No other global initialization routines should be present.
64+
// CHECK-NOT: define internal void @__cxx_global_var_init{{.*}}
65+
66+
[numthreads(4,1,1)]
67+
void main() {
68+
// CHECK: define internal void @main()()
69+
// CHECK-NEXT: entry:
70+
// CHECK-NEXT: %[[TMP0:.*]] = alloca %"class.hlsl::RWBuffer"
71+
72+
static RWBuffer<float> StaticLocal;
73+
// Check that StaticLocal is initialized by default constructor (handle set to poison)
74+
// and not from binding.
75+
// call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} @main()::StaticLocal)
76+
77+
StaticLocal = Array[1];
78+
// A[2][0] is accessed here, so it should be initialized from binding (register 10, space 6, index 1),
79+
// and then assigned to StaticLocal using = operator.
80+
// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
81+
// CHECK-SAME: (ptr {{.*}} %[[TMP0]], i32 noundef 10, i32 noundef 6, i32 noundef 2, i32 noundef 1, ptr noundef [[ARRAY_STR]])
82+
// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr {{.*}} @main()::StaticLocal, ptr {{.*}} %[[TMP0]])
83+
84+
StaticOne = One;
85+
// Operator = call to assign non-static One handle to static StaticOne.
86+
// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr {{.*}} @StaticOne, ptr {{.*}} @One)
87+
88+
StaticArray = Array;
89+
// Check that each elements of StaticArray is initialized from binding (register 10, space 6, indices 0 and 1).
90+
// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
91+
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align 4 @StaticArray, i32 noundef 10, i32 noundef 6, i32 noundef 2, i32 noundef 0, ptr noundef [[ARRAY_STR]])
92+
// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
93+
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWBuffer") align 4 getelementptr ([2 x %"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1),
94+
// CHECK-SAME: i32 noundef 10, i32 noundef 6, i32 noundef 2, i32 noundef 1, ptr noundef [[ARRAY_STR]]
95+
96+
StaticArray[1] = One;
97+
// Operator = call to assign non-static One handle to StaticArray element.
98+
// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=(hlsl::RWBuffer<float> const&)
99+
// CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x %"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), ptr {{.*}} @One)
100+
101+
StaticLocal[0] = 123;
102+
// CHECK-NEXT: %[[PTR0:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @main()::StaticLocal, i32 noundef 0)
103+
// CHECK-NEXT: store float 1.230000e+02, ptr %[[PTR0]]
104+
105+
StaticOne[1] = 456;
106+
// CHECK-NEXT: %[[PTR1:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}}) @StaticOne, i32 noundef 1)
107+
// CHECK-NEXT: store float 4.560000e+02, ptr %[[PTR1]], align 4
108+
109+
StaticArray[1][2] = 789;
110+
// CHECK-NEXT: %[[PTR2:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)
111+
// CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x %"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), i32 noundef 2)
112+
// CHECK-NEXT: store float 7.890000e+02, ptr %[[PTR2]], align 4
113+
114+
static RWStructuredBuffer<int> StaticLocalWithCounter;
115+
// Check that StaticLocalWithCounter is initialized by default constructor (handle set to poison)
116+
// and not from binding.
117+
// call void @hlsl::RWStructuredBuffer<int>::RWStructuredBuffer()(ptr {{.*}} @main()::StaticLocalWithCounter)
118+
119+
static RWStructuredBuffer<int> StaticLocalArrayWithCounter[2];
120+
121+
StaticLocalWithCounter = OneWithCounter;
122+
// Operator = call to assign non-static OneWithCounter handles to StaticLocalWithCounter handles.
123+
// CHECK: call {{.*}} ptr @hlsl::RWStructuredBuffer<int>::operator=(hlsl::RWStructuredBuffer<int> const&)(ptr {{.*}} @main()::StaticLocalWithCounter, ptr {{.*}} @OneWithCounter)
124+
125+
StaticLocalArrayWithCounter = ArrayWithCounter;
126+
// Check that each elements of StaticLocalArrayWithCounter is initialized from binding
127+
// of ArrayWithCounter (register 7, space 4, indices 0 and 1).
128+
// CHECK: call void @hlsl::RWStructuredBuffer<int>::__createFromBindingWithImplicitCounter(unsigned int, unsigned int, int, unsigned int, char const*, unsigned int)
129+
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWStructuredBuffer") align 4 @main()::StaticLocalArrayWithCounter,
130+
// CHECK-SAME: i32 noundef 7, i32 noundef 4, i32 noundef 2, i32 noundef 0, ptr noundef [[ARRAYWITHCOUNTER_STR]], i32 noundef 1)
131+
132+
// CHECK-NEXT: call void @hlsl::RWStructuredBuffer<int>::__createFromBindingWithImplicitCounter(unsigned int, unsigned int, int, unsigned int, char const*, unsigned int)
133+
// CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWStructuredBuffer") align 4 getelementptr ([2 x %"class.hlsl::RWStructuredBuffer"], ptr @main()::StaticLocalArrayWithCounter, i32 0, i32 1),
134+
// CHECK-SAME: i32 noundef 7, i32 noundef 4, i32 noundef 2, i32 noundef 1, ptr noundef [[ARRAYWITHCOUNTER_STR]], i32 noundef 1)
135+
}
136+
137+
// No other binding initialization calls should be present.
138+
// CHECK-NOT: call void @hlsl::RWBuffer<float>::__createFrom{{.*}}Binding{{.*}}

0 commit comments

Comments
 (0)