Skip to content

Commit 520773b

Browse files
authored
[HLSL] Add resource constructor with implicit binding for global resources (#138976)
Adds constructor for resources with implicit binding and applies it to all resources without binding at the global scope. Adds Clang builtin function `__builtin_hlsl_resource_handlefromimplicitbinding` that gets translated to `llvm.dx|spv.resource.handlefromimplicitbinding` intrinsic calls. Specific bindings are assigned in DXILResourceImplicitBinding pass. Design proposals: https://github.com/llvm/wg-hlsl/blob/main/proposals/0024-implicit-resource-binding.md https://github.com/llvm/wg-hlsl/blob/main/proposals/0025-resource-constructors.md One change from the proposals is that the `orderId` parameter is added onto the constructor. Originally it was supposed to be generated in codegen when the `llvm.dx|spv.resource.handlefromimplicitbinding` call is emitted, but that is not possible because the call is inside a constructor, and the constructor body is generated once per resource type and not resource instance. So the only way to inject instance-based data like `orderId` into the `llvm.dx|spv.resource.handlefromimplicitbinding` call is that it must come in via the constructor argument. Closes #136784
1 parent 34be80a commit 520773b

17 files changed

+231
-39
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4819,6 +4819,12 @@ def HLSLResourceHandleFromBinding : LangBuiltin<"HLSL_LANG"> {
48194819
let Prototype = "void(...)";
48204820
}
48214821

4822+
def HLSLResourceHandleFromImplicitBinding : LangBuiltin<"HLSL_LANG"> {
4823+
let Spellings = ["__builtin_hlsl_resource_handlefromimplicitbinding"];
4824+
let Attributes = [NoThrow];
4825+
let Prototype = "void(...)";
4826+
}
4827+
48224828
def HLSLAll : LangBuiltin<"HLSL_LANG"> {
48234829
let Spellings = ["__builtin_hlsl_all"];
48244830
let Attributes = [NoThrow, Const];

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,20 @@ class SemaHLSL : public SemaBase {
175175
// buffer which will be created at the end of the translation unit.
176176
llvm::SmallVector<Decl *> DefaultCBufferDecls;
177177

178+
uint32_t ImplicitBindingNextOrderID = 0;
179+
178180
private:
179181
void collectResourceBindingsOnVarDecl(VarDecl *D);
180182
void collectResourceBindingsOnUserRecordDecl(const VarDecl *VD,
181183
const RecordType *RT);
182184
void processExplicitBindingsOnDecl(VarDecl *D);
183185

184186
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
187+
188+
bool initGlobalResourceDecl(VarDecl *VD);
189+
uint32_t getNextImplicitBindingOrderID() {
190+
return ImplicitBindingNextOrderID++;
191+
}
185192
};
186193

187194
} // namespace clang

clang/lib/CodeGen/CGHLSLBuiltins.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,21 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
303303
HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(),
304304
ArrayRef<Value *>{SpaceOp, RegisterOp, RangeOp, IndexOp, NonUniform});
305305
}
306+
case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
307+
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
308+
Value *SpaceOp = EmitScalarExpr(E->getArg(1));
309+
Value *RangeOp = EmitScalarExpr(E->getArg(2));
310+
Value *IndexOp = EmitScalarExpr(E->getArg(3));
311+
Value *OrderID = EmitScalarExpr(E->getArg(4));
312+
// FIXME: NonUniformResourceIndex bit is not yet implemented
313+
// (llvm/llvm-project#135452)
314+
Value *NonUniform =
315+
llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), false);
316+
return Builder.CreateIntrinsic(
317+
HandleTy,
318+
CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic(),
319+
ArrayRef<Value *>{OrderID, SpaceOp, RangeOp, IndexOp, NonUniform});
320+
}
306321
case Builtin::BI__builtin_hlsl_all: {
307322
Value *Op0 = EmitScalarExpr(E->getArg(0));
308323
return Builder.CreateIntrinsic(

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ class CGHLSLRuntime {
119119
resource_getpointer)
120120
GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding,
121121
resource_handlefrombinding)
122+
GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromImplicitBinding,
123+
resource_handlefromimplicitbinding)
122124
GENERATE_HLSL_INTRINSIC_FUNCTION(BufferUpdateCounter, resource_updatecounter)
123125
GENERATE_HLSL_INTRINSIC_FUNCTION(GroupMemoryBarrierWithGroupSync,
124126
group_memory_barrier_with_group_sync)

clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,26 @@ BuiltinTypeDeclBuilder::addHandleConstructorFromBinding() {
668668
.finalize();
669669
}
670670

671+
BuiltinTypeDeclBuilder &
672+
BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() {
673+
if (Record->isCompleteDefinition())
674+
return *this;
675+
676+
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
677+
ASTContext &AST = SemaRef.getASTContext();
678+
QualType HandleType = getResourceHandleField()->getType();
679+
680+
return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
681+
.addParam("spaceNo", AST.UnsignedIntTy)
682+
.addParam("range", AST.IntTy)
683+
.addParam("index", AST.UnsignedIntTy)
684+
.addParam("orderId", AST.UnsignedIntTy)
685+
.callBuiltin("__builtin_hlsl_resource_handlefromimplicitbinding",
686+
HandleType, PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3)
687+
.assign(PH::Handle, PH::LastStmt)
688+
.finalize();
689+
}
690+
671691
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
672692
ASTContext &AST = Record->getASTContext();
673693
DeclarationName Subscript =

clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ class BuiltinTypeDeclBuilder {
7676
AccessSpecifier Access = AccessSpecifier::AS_private);
7777
BuiltinTypeDeclBuilder &addArraySubscriptOperators();
7878

79-
// Builtin types methods
79+
// Builtin types constructors
8080
BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
8181
BuiltinTypeDeclBuilder &addHandleConstructorFromBinding();
82+
BuiltinTypeDeclBuilder &addHandleConstructorFromImplicitBinding();
8283

8384
// Builtin types methods
8485
BuiltinTypeDeclBuilder &addLoadMethods();

clang/lib/Sema/HLSLExternalSemaSource.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
132132
return BuiltinTypeDeclBuilder(S, Decl)
133133
.addHandleMember(RC, IsROV, RawBuffer)
134134
.addDefaultHandleConstructor()
135-
.addHandleConstructorFromBinding();
135+
.addHandleConstructorFromBinding()
136+
.addHandleConstructorFromImplicitBinding();
136137
}
137138

138139
// This function is responsible for constructing the constraint expression for

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,6 +2454,20 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
24542454
TheCall->setType(ResourceTy);
24552455
break;
24562456
}
2457+
case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
2458+
ASTContext &AST = SemaRef.getASTContext();
2459+
if (SemaRef.checkArgCount(TheCall, 5) ||
2460+
CheckResourceHandle(&SemaRef, TheCall, 0) ||
2461+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(1), AST.UnsignedIntTy) ||
2462+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(2), AST.IntTy) ||
2463+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(3), AST.UnsignedIntTy) ||
2464+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(4), AST.UnsignedIntTy))
2465+
return true;
2466+
// use the type of the handle (arg0) as a return type
2467+
QualType ResourceTy = TheCall->getArg(0)->getType();
2468+
TheCall->setType(ResourceTy);
2469+
break;
2470+
}
24572471
case Builtin::BI__builtin_hlsl_and:
24582472
case Builtin::BI__builtin_hlsl_or: {
24592473
if (SemaRef.checkArgCount(TheCall, 2))
@@ -3285,8 +3299,10 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
32853299
VD->getLocation(), SourceLocation(), SourceLocation());
32863300

32873301
InitializationSequence InitSeq(S, Entity, Kind, Args);
3288-
ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args);
3302+
if (InitSeq.Failed())
3303+
return false;
32893304

3305+
ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args);
32903306
if (!Init.get())
32913307
return false;
32923308

@@ -3296,27 +3312,42 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
32963312
return true;
32973313
}
32983314

3299-
static bool initGlobalResourceDecl(Sema &S, VarDecl *VD) {
3315+
bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
3316+
std::optional<uint32_t> RegisterSlot;
3317+
uint32_t SpaceNo = 0;
33003318
HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
3301-
if (!RBA || !RBA->hasRegisterSlot())
3302-
// FIXME: add support for implicit binding (llvm/llvm-project#110722)
3303-
return false;
3319+
if (RBA) {
3320+
if (RBA->hasRegisterSlot())
3321+
RegisterSlot = RBA->getSlotNumber();
3322+
SpaceNo = RBA->getSpaceNumber();
3323+
}
33043324

3305-
ASTContext &AST = S.getASTContext();
3325+
ASTContext &AST = SemaRef.getASTContext();
33063326
uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy);
33073327
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
3308-
Expr *Args[] = {
3309-
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, RBA->getSlotNumber()),
3310-
AST.UnsignedIntTy, SourceLocation()),
3311-
IntegerLiteral::Create(AST,
3312-
llvm::APInt(UIntTySize, RBA->getSpaceNumber()),
3313-
AST.UnsignedIntTy, SourceLocation()),
3314-
IntegerLiteral::Create(AST, llvm::APInt(IntTySize, 1), AST.IntTy,
3315-
SourceLocation()),
3316-
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy,
3317-
SourceLocation())};
3318-
3319-
return initVarDeclWithCtor(S, VD, Args);
3328+
IntegerLiteral *RangeSize = IntegerLiteral::Create(
3329+
AST, llvm::APInt(IntTySize, 1), AST.IntTy, SourceLocation());
3330+
IntegerLiteral *Index = IntegerLiteral::Create(
3331+
AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy, SourceLocation());
3332+
IntegerLiteral *Space =
3333+
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo),
3334+
AST.UnsignedIntTy, SourceLocation());
3335+
3336+
// resource with explicit binding
3337+
if (RegisterSlot.has_value()) {
3338+
IntegerLiteral *RegSlot = IntegerLiteral::Create(
3339+
AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy,
3340+
SourceLocation());
3341+
Expr *Args[] = {RegSlot, Space, RangeSize, Index};
3342+
return initVarDeclWithCtor(SemaRef, VD, Args);
3343+
}
3344+
3345+
// resource with implicit binding
3346+
IntegerLiteral *OrderId = IntegerLiteral::Create(
3347+
AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()),
3348+
AST.UnsignedIntTy, SourceLocation());
3349+
Expr *Args[] = {Space, RangeSize, Index, OrderId};
3350+
return initVarDeclWithCtor(SemaRef, VD, Args);
33203351
}
33213352

33223353
// Returns true if the initialization has been handled.
@@ -3334,8 +3365,9 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
33343365
// FIXME: We currectly support only simple resources - no arrays of resources
33353366
// or resources in user defined structs.
33363367
// (llvm/llvm-project#133835, llvm/llvm-project#133837)
3337-
if (VD->getType()->isHLSLResourceRecord())
3338-
return initGlobalResourceDecl(SemaRef, VD);
3368+
// Initialize resources at the global scope
3369+
if (VD->hasGlobalStorage() && VD->getType()->isHLSLResourceRecord())
3370+
return initGlobalResourceDecl(VD);
33393371

33403372
return false;
33413373
}

clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,27 @@ RESOURCE Buffer;
7878
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
7979
// CHECK-NEXT: AlwaysInlineAttr
8080

81+
// Constructor from implicit binding
82+
83+
// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]] 'void (unsigned int, int, unsigned int, unsigned int)' inline
84+
// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
85+
// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
86+
// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
87+
// CHECK-NEXT: ParmVarDecl {{.*}} orderId 'unsigned int'
88+
// CHECK-NEXT: CompoundStmt {{.*}}
89+
// CHECK-NEXT: BinaryOperator {{.*}} '='
90+
// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
91+
// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this
92+
// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t
93+
// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr>
94+
// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding'
95+
// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
96+
// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this
97+
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
98+
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
99+
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
100+
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'orderId' 'unsigned int'
101+
// CHECK-NEXT: AlwaysInlineAttr
102+
81103
// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'const element_type &(unsigned int) const'
82104
// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'element_type &(unsigned int)'

clang/test/AST/HLSL/StructuredBuffers-AST.hlsl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,28 @@ RESOURCE<float> Buffer;
125125
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
126126
// CHECK-NEXT: AlwaysInlineAttr
127127

128+
// Constructor from implicit binding
129+
130+
// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]]<element_type> 'void (unsigned int, int, unsigned int, unsigned int)' inline
131+
// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
132+
// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
133+
// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
134+
// CHECK-NEXT: ParmVarDecl {{.*}} orderId 'unsigned int'
135+
// CHECK-NEXT: CompoundStmt {{.*}}
136+
// CHECK-NEXT: BinaryOperator {{.*}} '='
137+
// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
138+
// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this
139+
// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t
140+
// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr>
141+
// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding'
142+
// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
143+
// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this
144+
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
145+
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
146+
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
147+
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'orderId' 'unsigned int'
148+
// CHECK-NEXT: AlwaysInlineAttr
149+
128150
// Subscript operators
129151

130152
// CHECK-SUBSCRIPT: CXXMethodDecl {{.*}} operator[] 'const hlsl_device element_type &(unsigned int) const'

0 commit comments

Comments
 (0)