Skip to content

Commit 58161a2

Browse files
committed
[HLSL] Fix static resources
- Enable assignment to static resource variables (fixes #166458) - Initialize static resources and resource arrays with default constructor that sets the handle to poison
1 parent 883df7b commit 58161a2

File tree

4 files changed

+98
-8
lines changed

4 files changed

+98
-8
lines changed

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,12 +1025,13 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
10251025
ArraySubsExpr->getType()->isHLSLResourceRecordArray()) &&
10261026
"expected resource array subscript expression");
10271027

1028-
// Let clang codegen handle local resource array subscripts,
1028+
// Let Clang codegen handle local and static resource array subscripts,
10291029
// or when the subscript references on opaque expression (as part of
10301030
// ArrayInitLoopExpr AST node).
10311031
const VarDecl *ArrayDecl =
10321032
dyn_cast_or_null<VarDecl>(getArrayDecl(ArraySubsExpr));
1033-
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
1033+
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage() ||
1034+
ArrayDecl->getStorageClass() == SC_Static)
10341035
return std::nullopt;
10351036

10361037
// get the resource array type

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5918,7 +5918,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
59185918
(D->getType()->isHLSLResourceRecord() ||
59195919
D->getType()->isHLSLResourceRecordArray())) {
59205920
Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
5921-
NeedsGlobalCtor = D->getType()->isHLSLResourceRecord();
5921+
NeedsGlobalCtor = D->getType()->isHLSLResourceRecord() ||
5922+
D->getStorageClass() == SC_Static;
59225923
} else if (D->hasAttr<LoaderUninitializedAttr>()) {
59235924
Init = llvm::UndefValue::get(getTypes().ConvertTypeForMem(ASTTy));
59245925
} else if (!InitExpr) {

clang/lib/Sema/SemaHLSL.cpp

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

3927-
if (VD->getType()->isHLSLResourceRecordArray()) {
3927+
// Add implicit binding attribute to non-static resource arrays.
3928+
if (VD->getType()->isHLSLResourceRecordArray() &&
3929+
VD->getStorageClass() != SC_Static) {
39283930
// If the resource array does not have an explicit binding attribute,
39293931
// create an implicit one. It will be used to transfer implicit binding
39303932
// order_ID to codegen.
@@ -4118,8 +4120,8 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
41184120
if (VD->getType().getAddressSpace() == LangAS::hlsl_constant)
41194121
return true;
41204122

4121-
// Initialize resources at the global scope
4122-
if (VD->hasGlobalStorage()) {
4123+
// Initialize non-static resources at the global scope.
4124+
if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
41234125
const Type *Ty = VD->getType().getTypePtr();
41244126
if (Ty->isHLSLResourceRecord())
41254127
return initGlobalResourceDecl(VD);
@@ -4143,10 +4145,10 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr,
41434145
while (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
41444146
E = ASE->getBase()->IgnoreParenImpCasts();
41454147

4146-
// Report error if LHS is a resource declared at a global scope.
4148+
// Report error if LHS is a non-static resource declared at a global scope.
41474149
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
41484150
if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
4149-
if (VD->hasGlobalStorage()) {
4151+
if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
41504152
// assignment to global resource is not allowed
41514153
SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD;
41524154
SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s
2+
3+
// CHECK: [[ONE_STR:@.*]] = private unnamed_addr constant [4 x i8] c"One\00"
4+
// CHECK: [[ARRAY_STR:@.*]] = private unnamed_addr constant [6 x i8] c"Array\00"
5+
// CHECK-NOT: private unnamed_addr constant [{{[0-9]+}} x i8] c"Static
6+
7+
RWBuffer<float> One : register(u1, space5);
8+
RWBuffer<float> Array[4][2] : register(u10, space6);
9+
10+
// Check that the non-static resource One is initialized from binding on
11+
// startup (register 1, space 5).
12+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
13+
// CHECK-NEXT: entry:
14+
// CHECK-NEXT: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
15+
// CHECK-SAME: (ptr {{.*}} @One, i32 noundef 1, i32 noundef 5, i32 noundef 1, i32 noundef 0, ptr noundef [[ONE_STR]])
16+
17+
// Note that non-static resource arrays are not initialized on startup.
18+
// The individual resources from the array are initialized on access.
19+
20+
static RWBuffer<float> StaticOne;
21+
static RWBuffer<float> StaticArray[2];
22+
23+
// Check that StaticOne resource is initialized on startup with the default
24+
// constructor and not from binding. It will initalize the handle to poison.
25+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
26+
// CHECK-NEXT: entry:
27+
// CHECK-NEXT: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} @StaticOne)
28+
29+
// Check that StaticArray elements are initialized on startup with the default
30+
// constructor and not from binding. The initializer will loop over the array
31+
// elements and call the default constructor for each one, setting the handle to poison.
32+
// CHECK: define internal void @__cxx_global_var_init{{.*}}
33+
// CHECK-NEXT: entry:
34+
// CHECK-NEXT: br label %arrayctor.loop
35+
// CHECK: arrayctor.loop: ; preds = %arrayctor.loop, %entry
36+
// CHECK-NEXT: %arrayctor.cur = phi ptr [ @StaticArray, %entry ], [ %arrayctor.next, %arrayctor.loop ]
37+
// CHECK-NEXT: call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} %arrayctor.cur)
38+
// CHECK-NEXT: %arrayctor.next = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %arrayctor.cur, i32 1
39+
// CHECK-NEXT: %arrayctor.done = icmp eq ptr %arrayctor.next, getelementptr inbounds (%"class.hlsl::RWBuffer", ptr @StaticArray, i32 2)
40+
// CHECK-NEXT: br i1 %arrayctor.done, label %arrayctor.cont, label %arrayctor.loop
41+
// CHECK: arrayctor.cont: ; preds = %arrayctor.loop
42+
// CHECK-NEXT: ret void
43+
44+
// No other global initialization routines should be present.
45+
// CHECK-NOT: define internal void @__cxx_global_var_init{{.*}}
46+
47+
[numthreads(4,1,1)]
48+
void main() {
49+
// CHECK: define internal void @main()()
50+
// CHECK-NEXT: entry:
51+
// CHECK-NEXT: %[[TMP0:.*]] = alloca %"class.hlsl::RWBuffer"
52+
53+
static RWBuffer<float> StaticLocal;
54+
// Check that StaticLocal is initialized to by default constructor to poison and not from binding
55+
// call void @hlsl::RWBuffer<float>::RWBuffer()(ptr {{.*}} @main()::StaticLocal)
56+
57+
StaticLocal = Array[2][0];
58+
// A[2][0] is accessed here, so it should be initialized from binding (register 10, space 6, index 4),
59+
// and then assigned to StaticLocal using = operator.
60+
// CHECK: call void @hlsl::RWBuffer<float>::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*)
61+
// CHECK-SAME: (ptr {{.*}} %[[TMP0]], i32 noundef 10, i32 noundef 6, i32 noundef 8, i32 noundef 4, ptr noundef [[ARRAY_STR]])
62+
// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr {{.*}} @main()::StaticLocal, ptr {{.*}} %[[TMP0]])
63+
64+
StaticOne = One;
65+
// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=({{.*}})(ptr {{.*}} @StaticOne, ptr {{.*}} @One)
66+
67+
StaticArray[1] = One;
68+
// CHECK-NEXT: call {{.*}} ptr @hlsl::RWBuffer<float>::operator=(hlsl::RWBuffer<float> const&)
69+
// CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x %"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), ptr {{.*}} @One)
70+
71+
StaticLocal[0] = 123;
72+
// CHECK-NEXT: %[[PTR0:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}} @main()::StaticLocal, i32 noundef 0)
73+
// CHECK-NEXT: store float 1.230000e+02, ptr %[[PTR0]]
74+
75+
StaticOne[1] = 456;
76+
// CHECK-NEXT: %[[PTR1:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)(ptr {{.*}}) @StaticOne, i32 noundef 1)
77+
// CHECK-NEXT: store float 4.560000e+02, ptr %[[PTR1]], align 4
78+
79+
StaticArray[1][2] = 789;
80+
// CHECK-NEXT: %[[PTR2:.*]] = call {{.*}} ptr @hlsl::RWBuffer<float>::operator[](unsigned int)
81+
// CHECK-SAME: (ptr {{.*}} getelementptr inbounds ([2 x %"class.hlsl::RWBuffer"], ptr @StaticArray, i32 0, i32 1), i32 noundef 2)
82+
// CHECK-NEXT: store float 7.890000e+02, ptr %[[PTR2]], align 4
83+
}
84+
85+
// No other binding initialization calls should be present.
86+
// CHECK-NOT: call void @hlsl::RWBuffer<float>::__createFrom{{.*}}Binding{{.*}}

0 commit comments

Comments
 (0)