Skip to content

Commit af51b87

Browse files
s-perronsvkeerthy
authored andcommitted
[SPIRV][HLSL] Add Sema and CodeGen for implicit typed buffer counters (#162291)
This commit implements the Sema and CodeGen portions of the typed buffer counter proposal described in the HLSL WG proposal 0023. This change introduces the necessary Sema and CodeGen logic to handle implicit counter variables for typed buffers. This includes: - Extending `HLSLResourceBindingAttr` to store the implicit counter binding order ID. - Introducing the `__builtin_hlsl_resource_counterhandlefromimplicitbinding` builtin. - Updating `SemaHLSL` to correctly initialize global resource declarations and resource arrays with implicit counter buffers. - Adding CodeGen support for the new builtin, which generates a `llvm.spv.resource.counterhandlefromimplicitbinding` intrinsic for the SPIR-V target and aliases the main resource handle for the DXIL target. - Adding and updating tests to verify the new functionality. Fixes #137032
1 parent 581dfa8 commit af51b87

14 files changed

+486
-121
lines changed

clang/include/clang/AST/HLSLResource.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,19 @@ struct ResourceBindingAttrs {
7474
assert(hasBinding() && !isExplicit() && !hasImplicitOrderID());
7575
RegBinding->setImplicitBindingOrderID(Value);
7676
}
77+
void setCounterImplicitOrderID(unsigned Value) const {
78+
assert(hasBinding() && !hasCounterImplicitOrderID());
79+
RegBinding->setImplicitCounterBindingOrderID(Value);
80+
}
81+
82+
bool hasCounterImplicitOrderID() const {
83+
return RegBinding && RegBinding->hasImplicitCounterBindingOrderID();
84+
}
85+
86+
unsigned getCounterImplicitOrderID() const {
87+
assert(hasCounterImplicitOrderID());
88+
return RegBinding->getImplicitCounterBindingOrderID();
89+
}
7790
};
7891

7992
} // namespace hlsl

clang/include/clang/Basic/Attr.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4944,6 +4944,7 @@ def HLSLResourceBinding: InheritableAttr {
49444944
std::optional<unsigned> SlotNumber;
49454945
unsigned SpaceNumber;
49464946
std::optional<unsigned> ImplicitBindingOrderID;
4947+
std::optional<unsigned> ImplicitCounterBindingOrderID;
49474948

49484949
public:
49494950
void setBinding(RegisterType RT, std::optional<unsigned> SlotNum, unsigned SpaceNum) {
@@ -4976,6 +4977,17 @@ def HLSLResourceBinding: InheritableAttr {
49764977
assert(hasImplicitBindingOrderID() && "attribute does not have implicit binding order id");
49774978
return ImplicitBindingOrderID.value();
49784979
}
4980+
void setImplicitCounterBindingOrderID(uint32_t Value) {
4981+
assert(!hasImplicitCounterBindingOrderID() && "attribute already has implicit counter binding order id");
4982+
ImplicitCounterBindingOrderID = Value;
4983+
}
4984+
bool hasImplicitCounterBindingOrderID() const {
4985+
return ImplicitCounterBindingOrderID.has_value();
4986+
}
4987+
uint32_t getImplicitCounterBindingOrderID() const {
4988+
assert(hasImplicitCounterBindingOrderID() && "attribute does not have implicit counter binding order id");
4989+
return ImplicitCounterBindingOrderID.value();
4990+
}
49794991
}];
49804992
}
49814993

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4945,6 +4945,12 @@ def HLSLResourceHandleFromImplicitBinding : LangBuiltin<"HLSL_LANG"> {
49454945
let Prototype = "void(...)";
49464946
}
49474947

4948+
def HLSLResourceCounterHandleFromImplicitBinding : LangBuiltin<"HLSL_LANG"> {
4949+
let Spellings = ["__builtin_hlsl_resource_counterhandlefromimplicitbinding"];
4950+
let Attributes = [NoThrow, CustomTypeChecking];
4951+
let Prototype = "void(...)";
4952+
}
4953+
49484954
def HLSLResourceNonUniformIndex : LangBuiltin<"HLSL_LANG"> {
49494955
let Spellings = ["__builtin_hlsl_resource_nonuniformindex"];
49504956
let Attributes = [NoThrow];

clang/lib/CodeGen/CGHLSLBuiltins.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,19 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
352352
SmallVector<Value *> Args{OrderID, SpaceOp, RangeOp, IndexOp, Name};
353353
return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
354354
}
355+
case Builtin::BI__builtin_hlsl_resource_counterhandlefromimplicitbinding: {
356+
Value *MainHandle = EmitScalarExpr(E->getArg(0));
357+
if (!CGM.getTriple().isSPIRV())
358+
return MainHandle;
359+
360+
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
361+
Value *OrderID = EmitScalarExpr(E->getArg(1));
362+
Value *SpaceOp = EmitScalarExpr(E->getArg(2));
363+
llvm::Intrinsic::ID IntrinsicID =
364+
llvm::Intrinsic::spv_resource_counterhandlefromimplicitbinding;
365+
SmallVector<Value *> Args{MainHandle, OrderID, SpaceOp};
366+
return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
367+
}
355368
case Builtin::BI__builtin_hlsl_resource_nonuniformindex: {
356369
Value *IndexOp = EmitScalarExpr(E->getArg(0));
357370
llvm::Type *RetTy = ConvertType(E->getType());

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,19 +145,29 @@ static CXXMethodDecl *lookupResourceInitMethodAndSetupArgs(
145145
// explicit binding
146146
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, Binding.getSlot());
147147
Args.add(RValue::get(RegSlot), AST.UnsignedIntTy);
148-
CreateMethod = lookupMethod(ResourceDecl, "__createFromBinding", SC_Static);
148+
const char *Name = Binding.hasCounterImplicitOrderID()
149+
? "__createFromBindingWithImplicitCounter"
150+
: "__createFromBinding";
151+
CreateMethod = lookupMethod(ResourceDecl, Name, SC_Static);
149152
} else {
150153
// implicit binding
151154
auto *OrderID =
152155
llvm::ConstantInt::get(CGM.IntTy, Binding.getImplicitOrderID());
153156
Args.add(RValue::get(OrderID), AST.UnsignedIntTy);
154-
CreateMethod =
155-
lookupMethod(ResourceDecl, "__createFromImplicitBinding", SC_Static);
157+
const char *Name = Binding.hasCounterImplicitOrderID()
158+
? "__createFromImplicitBindingWithImplicitCounter"
159+
: "__createFromImplicitBinding";
160+
CreateMethod = lookupMethod(ResourceDecl, Name, SC_Static);
156161
}
157162
Args.add(RValue::get(Space), AST.UnsignedIntTy);
158163
Args.add(RValue::get(Range), AST.IntTy);
159164
Args.add(RValue::get(Index), AST.UnsignedIntTy);
160165
Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst()));
166+
if (Binding.hasCounterImplicitOrderID()) {
167+
uint32_t CounterBinding = Binding.getCounterImplicitOrderID();
168+
auto *CounterOrderID = llvm::ConstantInt::get(CGM.IntTy, CounterBinding);
169+
Args.add(RValue::get(CounterOrderID), AST.UnsignedIntTy);
170+
}
161171

162172
return CreateMethod;
163173
}

clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ struct BuiltinTypeMethodBuilder {
144144
_2,
145145
_3,
146146
_4,
147+
_5,
147148
Handle = 128,
148149
CounterHandle,
149150
LastStmt
@@ -190,6 +191,9 @@ struct BuiltinTypeMethodBuilder {
190191
template <typename T>
191192
BuiltinTypeMethodBuilder &
192193
accessCounterHandleFieldOnResource(T ResourceRecord);
194+
template <typename ResourceT, typename ValueT>
195+
BuiltinTypeMethodBuilder &
196+
setCounterHandleFieldOnResource(ResourceT ResourceRecord, ValueT HandleValue);
193197
template <typename T> BuiltinTypeMethodBuilder &returnValue(T ReturnValue);
194198
BuiltinTypeMethodBuilder &returnThis();
195199
BuiltinTypeDeclBuilder &finalize();
@@ -205,6 +209,11 @@ struct BuiltinTypeMethodBuilder {
205209
if (!Method)
206210
createDecl();
207211
}
212+
213+
template <typename ResourceT, typename ValueT>
214+
BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
215+
ValueT HandleValue,
216+
FieldDecl *HandleField);
208217
};
209218

210219
TemplateParameterListBuilder::~TemplateParameterListBuilder() {
@@ -592,13 +601,27 @@ template <typename ResourceT, typename ValueT>
592601
BuiltinTypeMethodBuilder &
593602
BuiltinTypeMethodBuilder::setHandleFieldOnResource(ResourceT ResourceRecord,
594603
ValueT HandleValue) {
604+
return setFieldOnResource(ResourceRecord, HandleValue,
605+
DeclBuilder.getResourceHandleField());
606+
}
607+
608+
template <typename ResourceT, typename ValueT>
609+
BuiltinTypeMethodBuilder &
610+
BuiltinTypeMethodBuilder::setCounterHandleFieldOnResource(
611+
ResourceT ResourceRecord, ValueT HandleValue) {
612+
return setFieldOnResource(ResourceRecord, HandleValue,
613+
DeclBuilder.getResourceCounterHandleField());
614+
}
615+
616+
template <typename ResourceT, typename ValueT>
617+
BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::setFieldOnResource(
618+
ResourceT ResourceRecord, ValueT HandleValue, FieldDecl *HandleField) {
595619
ensureCompleteDecl();
596620

597621
Expr *ResourceExpr = convertPlaceholder(ResourceRecord);
598622
Expr *HandleValueExpr = convertPlaceholder(HandleValue);
599623

600624
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
601-
FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
602625
MemberExpr *HandleMemberExpr = MemberExpr::CreateImplicit(
603626
AST, ResourceExpr, false, HandleField, HandleField->getType(), VK_LValue,
604627
OK_Ordinary);
@@ -829,6 +852,18 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
829852
.finalize();
830853
}
831854

855+
BuiltinTypeDeclBuilder &
856+
BuiltinTypeDeclBuilder::addStaticInitializationFunctions(bool HasCounter) {
857+
if (HasCounter) {
858+
addCreateFromBindingWithImplicitCounter();
859+
addCreateFromImplicitBindingWithImplicitCounter();
860+
} else {
861+
addCreateFromBinding();
862+
addCreateFromImplicitBinding();
863+
}
864+
return *this;
865+
}
866+
832867
// Adds static method that initializes resource from binding:
833868
//
834869
// static Resource<T> __createFromBinding(unsigned registerNo,
@@ -903,6 +938,102 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCreateFromImplicitBinding() {
903938
.finalize();
904939
}
905940

941+
// Adds static method that initializes resource from binding:
942+
//
943+
// static Resource<T>
944+
// __createFromBindingWithImplicitCounter(unsigned registerNo,
945+
// unsigned spaceNo, int range,
946+
// unsigned index, const char *name,
947+
// unsigned counterOrderId) {
948+
// Resource<T> tmp;
949+
// tmp.__handle = __builtin_hlsl_resource_handlefrombinding(
950+
// tmp.__handle, registerNo, spaceNo, range, index, name);
951+
// tmp.__counter_handle =
952+
// __builtin_hlsl_resource_counterhandlefromimplicitbinding(
953+
// tmp.__handle, counterOrderId, spaceNo);
954+
// return tmp;
955+
// }
956+
BuiltinTypeDeclBuilder &
957+
BuiltinTypeDeclBuilder::addCreateFromBindingWithImplicitCounter() {
958+
assert(!Record->isCompleteDefinition() && "record is already complete");
959+
960+
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
961+
ASTContext &AST = SemaRef.getASTContext();
962+
QualType HandleType = getResourceHandleField()->getType();
963+
QualType RecordType = AST.getTypeDeclType(cast<TypeDecl>(Record));
964+
BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
965+
966+
return BuiltinTypeMethodBuilder(*this,
967+
"__createFromBindingWithImplicitCounter",
968+
RecordType, false, false, SC_Static)
969+
.addParam("registerNo", AST.UnsignedIntTy)
970+
.addParam("spaceNo", AST.UnsignedIntTy)
971+
.addParam("range", AST.IntTy)
972+
.addParam("index", AST.UnsignedIntTy)
973+
.addParam("name", AST.getPointerType(AST.CharTy.withConst()))
974+
.addParam("counterOrderId", AST.UnsignedIntTy)
975+
.declareLocalVar(TmpVar)
976+
.accessHandleFieldOnResource(TmpVar)
977+
.callBuiltin("__builtin_hlsl_resource_handlefrombinding", HandleType,
978+
PH::LastStmt, PH::_0, PH::_1, PH::_2, PH::_3, PH::_4)
979+
.setHandleFieldOnResource(TmpVar, PH::LastStmt)
980+
.accessHandleFieldOnResource(TmpVar)
981+
.callBuiltin("__builtin_hlsl_resource_counterhandlefromimplicitbinding",
982+
HandleType, PH::LastStmt, PH::_5, PH::_1)
983+
.setCounterHandleFieldOnResource(TmpVar, PH::LastStmt)
984+
.returnValue(TmpVar)
985+
.finalize();
986+
}
987+
988+
// Adds static method that initializes resource from binding:
989+
//
990+
// static Resource<T>
991+
// __createFromImplicitBindingWithImplicitCounter(unsigned orderId,
992+
// unsigned spaceNo, int range,
993+
// unsigned index,
994+
// const char *name,
995+
// unsigned counterOrderId) {
996+
// Resource<T> tmp;
997+
// tmp.__handle = __builtin_hlsl_resource_handlefromimplicitbinding(
998+
// tmp.__handle, orderId, spaceNo, range, index, name);
999+
// tmp.__counter_handle =
1000+
// __builtin_hlsl_resource_counterhandlefromimplicitbinding(
1001+
// tmp.__handle, counterOrderId, spaceNo);
1002+
// return tmp;
1003+
// }
1004+
BuiltinTypeDeclBuilder &
1005+
BuiltinTypeDeclBuilder::addCreateFromImplicitBindingWithImplicitCounter() {
1006+
assert(!Record->isCompleteDefinition() && "record is already complete");
1007+
1008+
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
1009+
ASTContext &AST = SemaRef.getASTContext();
1010+
QualType HandleType = getResourceHandleField()->getType();
1011+
QualType RecordType = AST.getTypeDeclType(cast<TypeDecl>(Record));
1012+
BuiltinTypeMethodBuilder::LocalVar TmpVar("tmp", RecordType);
1013+
1014+
return BuiltinTypeMethodBuilder(
1015+
*this, "__createFromImplicitBindingWithImplicitCounter",
1016+
RecordType, false, false, SC_Static)
1017+
.addParam("orderId", AST.UnsignedIntTy)
1018+
.addParam("spaceNo", AST.UnsignedIntTy)
1019+
.addParam("range", AST.IntTy)
1020+
.addParam("index", AST.UnsignedIntTy)
1021+
.addParam("name", AST.getPointerType(AST.CharTy.withConst()))
1022+
.addParam("counterOrderId", AST.UnsignedIntTy)
1023+
.declareLocalVar(TmpVar)
1024+
.accessHandleFieldOnResource(TmpVar)
1025+
.callBuiltin("__builtin_hlsl_resource_handlefromimplicitbinding",
1026+
HandleType, PH::LastStmt, PH::_0, PH::_1, PH::_2, PH::_3,
1027+
PH::_4)
1028+
.setHandleFieldOnResource(TmpVar, PH::LastStmt)
1029+
.accessHandleFieldOnResource(TmpVar)
1030+
.callBuiltin("__builtin_hlsl_resource_counterhandlefromimplicitbinding",
1031+
HandleType, PH::LastStmt, PH::_5, PH::_1)
1032+
.setCounterHandleFieldOnResource(TmpVar, PH::LastStmt)
1033+
.returnValue(TmpVar)
1034+
.finalize();
1035+
}
1036+
9061037
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() {
9071038
assert(!Record->isCompleteDefinition() && "record is already complete");
9081039

@@ -1048,7 +1179,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
10481179
return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
10491180
SemaRef.getASTContext().UnsignedIntTy)
10501181
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
1051-
PH::Handle, getConstantIntExpr(1))
1182+
PH::CounterHandle, getConstantIntExpr(1))
10521183
.finalize();
10531184
}
10541185

@@ -1057,7 +1188,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
10571188
return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
10581189
SemaRef.getASTContext().UnsignedIntTy)
10591190
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
1060-
PH::Handle, getConstantIntExpr(-1))
1191+
PH::CounterHandle, getConstantIntExpr(-1))
10611192
.finalize();
10621193
}
10631194

@@ -1102,7 +1233,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
11021233
return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
11031234
.addParam("value", ElemTy)
11041235
.callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
1105-
PH::Handle, getConstantIntExpr(1))
1236+
PH::CounterHandle, getConstantIntExpr(1))
11061237
.callBuiltin("__builtin_hlsl_resource_getpointer",
11071238
AST.getPointerType(AddrSpaceElemTy), PH::Handle,
11081239
PH::LastStmt)
@@ -1119,7 +1250,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
11191250
AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device);
11201251
return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
11211252
.callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
1122-
PH::Handle, getConstantIntExpr(-1))
1253+
PH::CounterHandle, getConstantIntExpr(-1))
11231254
.callBuiltin("__builtin_hlsl_resource_getpointer",
11241255
AST.getPointerType(AddrSpaceElemTy), PH::Handle,
11251256
PH::LastStmt)

clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ class BuiltinTypeDeclBuilder {
8383
BuiltinTypeDeclBuilder &addCopyAssignmentOperator();
8484

8585
// Static create methods
86-
BuiltinTypeDeclBuilder &addCreateFromBinding();
87-
BuiltinTypeDeclBuilder &addCreateFromImplicitBinding();
86+
BuiltinTypeDeclBuilder &addStaticInitializationFunctions(bool HasCounter);
8887

8988
// Builtin types methods
9089
BuiltinTypeDeclBuilder &addLoadMethods();
@@ -96,6 +95,10 @@ class BuiltinTypeDeclBuilder {
9695
BuiltinTypeDeclBuilder &addConsumeMethod();
9796

9897
private:
98+
BuiltinTypeDeclBuilder &addCreateFromBinding();
99+
BuiltinTypeDeclBuilder &addCreateFromImplicitBinding();
100+
BuiltinTypeDeclBuilder &addCreateFromBindingWithImplicitCounter();
101+
BuiltinTypeDeclBuilder &addCreateFromImplicitBindingWithImplicitCounter();
99102
BuiltinTypeDeclBuilder &addResourceMember(StringRef MemberName,
100103
ResourceClass RC, bool IsROV,
101104
bool RawBuffer, bool IsCounter,

clang/lib/Sema/HLSLExternalSemaSource.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,7 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
236236
.addDefaultHandleConstructor()
237237
.addCopyConstructor()
238238
.addCopyAssignmentOperator()
239-
.addCreateFromBinding()
240-
.addCreateFromImplicitBinding();
239+
.addStaticInitializationFunctions(HasCounter);
241240
}
242241

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

0 commit comments

Comments
 (0)