Skip to content

[HLSL][NFC] Use method builder to create default resource constructor #131384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 28, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 94 additions & 76 deletions clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,45 +67,50 @@ struct TemplateParameterListBuilder {
BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
};

// Builder for methods of builtin types. Allows adding methods to builtin types
// using the builder pattern like this:
// Builder for methods or constructors of builtin types. Allows creating methods
// or constructors of builtin types using the builder pattern like this:
//
// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
// .addParam("param_name", Type, InOutModifier)
// .callBuiltin("builtin_name", BuiltinParams...)
// .finalizeMethod();
// .finalize();
//
// The builder needs to have all of the method parameters before it can create
// a CXXMethodDecl. It collects them in addParam calls and when a first
// method that builds the body is called or when access to 'this` is needed it
// creates the CXXMethodDecl and ParmVarDecls instances. These can then be
// referenced from the body building methods. Destructor or an explicit call to
// finalizeMethod() will complete the method definition.
// The builder needs to have all of the parameters before it can create
// a CXXMethodDecl or CXXConstructorDecl. It collects them in addParam calls and
// when a first method that builds the body is called or when access to 'this`
// is needed it creates the CXXMethodDecl/CXXConstructorDecl and ParmVarDecls
// instances. These can then be referenced from the body building methods.
// Destructor or an explicit call to finalize() will complete the method
// definition.
//
// The callBuiltin helper method accepts constants via `Expr *` or placeholder
// value arguments to indicate which function arguments to forward to the
// builtin.
//
// If the method that is being built has a non-void return type the
// finalizeMethod will create a return statent with the value of the last
// statement (unless the last statement is already a ReturnStmt).
// finalize() will create a return statement with the value of the last
// statement (unless the last statement is already a ReturnStmt or the return
// value is void).
struct BuiltinTypeMethodBuilder {
private:
struct MethodParam {
struct Param {
const IdentifierInfo &NameII;
QualType Ty;
HLSLParamModifierAttr::Spelling Modifier;
MethodParam(const IdentifierInfo &NameII, QualType Ty,
HLSLParamModifierAttr::Spelling Modifier)
Param(const IdentifierInfo &NameII, QualType Ty,
HLSLParamModifierAttr::Spelling Modifier)
: NameII(NameII), Ty(Ty), Modifier(Modifier) {}
};

BuiltinTypeDeclBuilder &DeclBuilder;
DeclarationNameInfo NameInfo;
DeclarationName Name;
QualType ReturnTy;
// method or constructor declaration
// (CXXConstructorDecl derives from CXXMethodDecl)
CXXMethodDecl *Method;
bool IsConst;
llvm::SmallVector<MethodParam> Params;
bool IsCtor;
llvm::SmallVector<Param> Params;
llvm::SmallVector<Stmt *> StmtsList;

// Argument placeholders, inspired by std::placeholder. These are the indices
Expand All @@ -124,15 +129,17 @@ struct BuiltinTypeMethodBuilder {
friend BuiltinTypeDeclBuilder;

BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
QualType ReturnTy, bool IsConst = false)
: DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())),
ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {}

BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name,
QualType ReturnTy, bool IsConst = false);
QualType ReturnTy, bool IsConst = false,
bool IsCtor = false)
: DeclBuilder(DB), Name(Name), ReturnTy(ReturnTy), Method(nullptr),
IsConst(IsConst), IsCtor(IsCtor) {}

BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef NameStr,
QualType ReturnTy, bool IsConst = false,
bool IsCtor = false);
BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;

~BuiltinTypeMethodBuilder() { finalizeMethod(); }
~BuiltinTypeMethodBuilder() { finalize(); }

BuiltinTypeMethodBuilder &
operator=(const BuiltinTypeMethodBuilder &Other) = delete;
Expand All @@ -146,11 +153,18 @@ struct BuiltinTypeMethodBuilder {
template <typename TLHS, typename TRHS>
BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
BuiltinTypeDeclBuilder &finalizeMethod();
BuiltinTypeDeclBuilder &finalize();
Expr *getResourceHandleExpr();

private:
void createMethodDecl();
void createDecl();

// Makes sure the declaration is created; should be called before any
// statement added to the body or when access to 'this' is needed.
void ensureCompleteDecl() {
if (!Method)
createDecl();
}
};

TemplateParameterListBuilder::~TemplateParameterListBuilder() {
Expand Down Expand Up @@ -325,13 +339,24 @@ Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
}

BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
StringRef Name,
StringRef NameStr,
QualType ReturnTy,
bool IsConst)
: DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {
const IdentifierInfo &II =
DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation());
bool IsConst, bool IsCtor)
: DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst),
IsCtor(IsCtor) {

assert((!NameStr.empty() || IsCtor) && "method needs a name");
assert(((IsCtor && !IsConst) || !IsCtor) && "constructor cannot be const");

ASTContext &AST = DB.SemaRef.getASTContext();
if (IsCtor) {
Name = AST.DeclarationNames.getCXXConstructorName(
DB.Record->getTypeForDecl()->getCanonicalTypeUnqualified());
} else {
const IdentifierInfo &II =
AST.Idents.get(NameStr, tok::TokenKind::identifier);
Name = DeclarationName(&II);
}
}

BuiltinTypeMethodBuilder &
Expand All @@ -344,33 +369,41 @@ BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
return *this;
}

void BuiltinTypeMethodBuilder::createMethodDecl() {
assert(Method == nullptr && "Method already created");
void BuiltinTypeMethodBuilder::createDecl() {
assert(Method == nullptr && "Method or constructor is already created");

// create method type
// create method or constructor type
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
SmallVector<QualType> ParamTypes;
for (MethodParam &MP : Params)
for (Param &MP : Params)
ParamTypes.emplace_back(MP.Ty);

FunctionProtoType::ExtProtoInfo ExtInfo;
if (IsConst)
ExtInfo.TypeQuals.addConst();

QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);

// create method decl
auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation());
Method = CXXMethodDecl::Create(
AST, DeclBuilder.Record, SourceLocation(), NameInfo, MethodTy, TSInfo,
SC_None, false, false, ConstexprSpecKind::Unspecified, SourceLocation());
QualType FuncTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);

// create method or constructor decl
auto *TSInfo = AST.getTrivialTypeSourceInfo(FuncTy, SourceLocation());
DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation());
if (IsCtor)
Method = CXXConstructorDecl::Create(
AST, DeclBuilder.Record, SourceLocation(), NameInfo, FuncTy, TSInfo,
ExplicitSpecifier(), false, true, false,
ConstexprSpecKind::Unspecified);
else
Method =
CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(),
NameInfo, FuncTy, TSInfo, SC_None, false, false,
ConstexprSpecKind::Unspecified, SourceLocation());

// create params & set them to the function prototype
SmallVector<ParmVarDecl *> ParmDecls;
auto FnProtoLoc =
Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
for (int I = 0, E = Params.size(); I != E; I++) {
MethodParam &MP = Params[I];
Param &MP = Params[I];
ParmVarDecl *Parm = ParmVarDecl::Create(
AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
&MP.NameII, MP.Ty,
Expand All @@ -388,10 +421,7 @@ void BuiltinTypeMethodBuilder::createMethodDecl() {
}

Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
// The first statement added to a method or access to 'this' creates the
// declaration.
if (!Method)
createMethodDecl();
ensureCompleteDecl();

ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
CXXThisExpr *This = CXXThisExpr::Create(
Expand All @@ -409,10 +439,7 @@ BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
std::array<Expr *, sizeof...(ArgSpecs)> Args{
convertPlaceholder(std::forward<Ts>(ArgSpecs))...};

// The first statement added to a method or access to 'this` creates the
// declaration.
if (!Method)
createMethodDecl();
ensureCompleteDecl();

ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
Expand Down Expand Up @@ -453,11 +480,11 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
return *this;
}

BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalizeMethod() {
BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
assert(!DeclBuilder.Record->isCompleteDefinition() &&
"record is already complete");
assert(Method != nullptr &&
"method decl not created; are you missing a call to build the body?");

ensureCompleteDecl();

if (!Method->hasBody()) {
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
Expand Down Expand Up @@ -599,27 +626,18 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember(
return *this;
}

// Adds default constructor to the resource class:
// Resource::Resource()
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
if (Record->isCompleteDefinition())
return *this;
ASTContext &AST = Record->getASTContext();

QualType ConstructorType =
AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo());

CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified();
DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy);
CXXConstructorDecl *Constructor = CXXConstructorDecl::Create(
AST, Record, SourceLocation(),
DeclarationNameInfo(Name, SourceLocation()), ConstructorType,
AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()),
ExplicitSpecifier(), false, true, false, ConstexprSpecKind::Unspecified);

Constructor->setBody(CompoundStmt::Create(
AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation()));
Constructor->setAccess(AccessSpecifier::AS_public);
Record->addDecl(Constructor);
return *this;
// FIXME: initialize handle to poison value; this can be added after
// resource constructor from binding is implemented, otherwise the handle
// value will get overwritten.
return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
false, true)
.finalize();
}

BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
Expand Down Expand Up @@ -713,7 +731,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
SemaRef.getASTContext().UnsignedIntTy)
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
PH::Handle, getConstantIntExpr(1))
.finalizeMethod();
.finalize();
}

BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
Expand All @@ -722,7 +740,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
SemaRef.getASTContext().UnsignedIntTy)
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
PH::Handle, getConstantIntExpr(-1))
.finalizeMethod();
.finalize();
}

BuiltinTypeDeclBuilder &
Expand All @@ -746,7 +764,7 @@ BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
.callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
PH::_0)
.dereference(PH::LastStmt)
.finalizeMethod();
.finalize();
}

BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
Expand All @@ -761,7 +779,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
.dereference(PH::LastStmt)
.assign(PH::LastStmt, PH::_0)
.finalizeMethod();
.finalize();
}

BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
Expand All @@ -774,7 +792,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
.callBuiltin("__builtin_hlsl_resource_getpointer",
AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
.dereference(PH::LastStmt)
.finalizeMethod();
.finalize();
}

} // namespace hlsl
Expand Down
Loading