llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-hlsl Author: Helena Kotas (hekota) <details> <summary>Changes</summary> Moving builder classes into separate files `HLSLBuiltinTypeDeclBuilder.cpp`/`.h`, changing a some `HLSLExternalSemaSource` methods to private and removing unused methods. This is a prep work before we start adding more builtin types and methods, like textures, resource constructors or matrices. For example constructors could make use of the `BuiltinTypeMethodBuilder`, but this helper class was defined in `HLSLExternalSemaSource.cpp` after the method that creates a constructor. Rather than reshuffling the code one big source file I am moving the builders into a separate cpp & header file and placing the helper classes declarations up top. Currently the new header only exposes `BuiltinTypeDeclBuilder` to `HLSLExternalSemaSource`. In the future but we might decide to expose more helper classes as needed. --- Patch is 67.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131032.diff 5 Files Affected: - (modified) clang/include/clang/Sema/HLSLExternalSemaSource.h (+8-7) - (modified) clang/lib/Sema/CMakeLists.txt (+1) - (added) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp (+782) - (added) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h (+96) - (modified) clang/lib/Sema/HLSLExternalSemaSource.cpp (+2-748) ``````````diff diff --git a/clang/include/clang/Sema/HLSLExternalSemaSource.h b/clang/include/clang/Sema/HLSLExternalSemaSource.h index 3c7495e66055d..9c1b16ba3950c 100644 --- a/clang/include/clang/Sema/HLSLExternalSemaSource.h +++ b/clang/include/clang/Sema/HLSLExternalSemaSource.h @@ -21,20 +21,15 @@ class NamespaceDecl; class Sema; class HLSLExternalSemaSource : public ExternalSemaSource { +private: Sema *SemaPtr = nullptr; NamespaceDecl *HLSLNamespace = nullptr; using CompletionFunction = std::function<void(CXXRecordDecl *)>; llvm::DenseMap<CXXRecordDecl *, CompletionFunction> Completions; - void defineHLSLVectorAlias(); - void defineTrivialHLSLTypes(); - void defineHLSLTypesWithForwardDeclarations(); - - void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn); - public: - ~HLSLExternalSemaSource() override; + ~HLSLExternalSemaSource() override {} /// Initialize the semantic source with the Sema instance /// being used to perform semantic analysis on the abstract syntax @@ -47,6 +42,12 @@ class HLSLExternalSemaSource : public ExternalSemaSource { using ExternalASTSource::CompleteType; /// Complete an incomplete HLSL builtin type void CompleteType(TagDecl *Tag) override; + +private: + void defineTrivialHLSLTypes(); + void defineHLSLVectorAlias(); + void defineHLSLTypesWithForwardDeclarations(); + void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn); }; } // namespace clang diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 1a351684d133e..d3fe80f659f69 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -20,6 +20,7 @@ add_clang_library(clangSema DeclSpec.cpp DelayedDiagnostic.cpp HeuristicResolver.cpp + HLSLBuiltinTypeDeclBuilder.cpp HLSLExternalSemaSource.cpp IdentifierResolver.cpp JumpDiagnostics.cpp diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp new file mode 100644 index 0000000000000..db0ed3434d837 --- /dev/null +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -0,0 +1,782 @@ +//===--- HLSLBuiltinTypeDeclBuilder.cpp - HLSL Builtin Type Decl Builder --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#include "HLSLBuiltinTypeDeclBuilder.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaHLSL.h" +#include "llvm/ADT/SmallVector.h" + +using namespace llvm::hlsl; + +namespace clang { + +namespace hlsl { + +namespace { + +static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) { + IdentifierInfo &II = + S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); + DeclarationNameInfo NameInfo = + DeclarationNameInfo(DeclarationName(&II), SourceLocation()); + LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); + // AllowBuiltinCreation is false but LookupDirect will create + // the builtin when searching the global scope anyways... + S.LookupName(R, S.getCurScope()); + // FIXME: If the builtin function was user-declared in global scope, + // this assert *will* fail. Should this call LookupBuiltin instead? + assert(R.isSingleResult() && + "Since this is a builtin it should always resolve!"); + return cast<FunctionDecl>(R.getFoundDecl()); +} +} // namespace + +// Builder for template arguments of builtin types. Used internally +// by BuiltinTypeDeclBuilder. +struct TemplateParameterListBuilder { + BuiltinTypeDeclBuilder &Builder; + llvm::SmallVector<NamedDecl *> Params; + + TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {} + ~TemplateParameterListBuilder(); + + TemplateParameterListBuilder & + addTypeParameter(StringRef Name, QualType DefaultValue = QualType()); + + ConceptSpecializationExpr * + constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD); + + BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr); +}; + +// Builder for methods of builtin types. Allows adding methods to builtin types +// using the builder pattern like this: +// +// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType) +// .addParam("param_name", Type, InOutModifier) +// .callBuiltin("builtin_name", BuiltinParams...) +// .finalizeMethod(); +// +// 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 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). +struct BuiltinTypeMethodBuilder { +private: + struct MethodParam { + const IdentifierInfo &NameII; + QualType Ty; + HLSLParamModifierAttr::Spelling Modifier; + MethodParam(const IdentifierInfo &NameII, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier) + : NameII(NameII), Ty(Ty), Modifier(Modifier) {} + }; + + BuiltinTypeDeclBuilder &DeclBuilder; + DeclarationNameInfo NameInfo; + QualType ReturnTy; + CXXMethodDecl *Method; + bool IsConst; + llvm::SmallVector<MethodParam> Params; + llvm::SmallVector<Stmt *> StmtsList; + + // Argument placeholders, inspired by std::placeholder. These are the indices + // of arguments to forward to `callBuiltin` and other method builder methods. + // Additional special values are: + // Handle - refers to the resource handle. + // LastStmt - refers to the last statement in the method body; referencing + // LastStmt will remove the statement from the method body since + // it will be linked from the new expression being constructed. + enum class PlaceHolder { _0, _1, _2, _3, Handle = 128, LastStmt }; + + Expr *convertPlaceholder(PlaceHolder PH); + Expr *convertPlaceholder(Expr *E) { return E; } + +public: + 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); + BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete; + + ~BuiltinTypeMethodBuilder() { finalizeMethod(); } + + BuiltinTypeMethodBuilder & + operator=(const BuiltinTypeMethodBuilder &Other) = delete; + + BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier = + HLSLParamModifierAttr::Keyword_in); + template <typename... Ts> + BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName, + QualType ReturnType, Ts... ArgSpecs); + template <typename TLHS, typename TRHS> + BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS); + template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr); + BuiltinTypeDeclBuilder &finalizeMethod(); + Expr *getResourceHandleExpr(); + +private: + void createMethodDecl(); +}; + +TemplateParameterListBuilder::~TemplateParameterListBuilder() { + finalizeTemplateArgs(); +} + +TemplateParameterListBuilder & +TemplateParameterListBuilder::addTypeParameter(StringRef Name, + QualType DefaultValue) { + assert(!Builder.Record->isCompleteDefinition() && + "record is already complete"); + ASTContext &AST = Builder.SemaRef.getASTContext(); + unsigned Position = static_cast<unsigned>(Params.size()); + auto *Decl = TemplateTypeParmDecl::Create( + AST, Builder.Record->getDeclContext(), SourceLocation(), SourceLocation(), + /* TemplateDepth */ 0, Position, + &AST.Idents.get(Name, tok::TokenKind::identifier), + /* Typename */ true, + /* ParameterPack */ false, + /* HasTypeConstraint*/ false); + if (!DefaultValue.isNull()) + Decl->setDefaultArgument(AST, + Builder.SemaRef.getTrivialTemplateArgumentLoc( + DefaultValue, QualType(), SourceLocation())); + + Params.emplace_back(Decl); + return *this; +} + +// The concept specialization expression (CSE) constructed in +// constructConceptSpecializationExpr is constructed so that it +// matches the CSE that is constructed when parsing the below C++ code: +// +// template<typename T> +// concept is_typed_resource_element_compatible = +// __builtin_hlsl_typed_resource_element_compatible<T> +// +// template<typename element_type> requires +// is_typed_resource_element_compatible<element_type> +// struct RWBuffer { +// element_type Val; +// }; +// +// int fn() { +// RWBuffer<int> Buf; +// } +// +// When dumping the AST and filtering for "RWBuffer", the resulting AST +// structure is what we're trying to construct below, specifically the +// CSE portion. +ConceptSpecializationExpr * +TemplateParameterListBuilder::constructConceptSpecializationExpr( + Sema &S, ConceptDecl *CD) { + ASTContext &Context = S.getASTContext(); + SourceLocation Loc = Builder.Record->getBeginLoc(); + DeclarationNameInfo DNI(CD->getDeclName(), Loc); + NestedNameSpecifierLoc NNSLoc; + DeclContext *DC = Builder.Record->getDeclContext(); + TemplateArgumentListInfo TALI(Loc, Loc); + + // Assume that the concept decl has just one template parameter + // This parameter should have been added when CD was constructed + // in getTypedBufferConceptDecl + assert(CD->getTemplateParameters()->size() == 1 && + "unexpected concept decl parameter count"); + TemplateTypeParmDecl *ConceptTTPD = + dyn_cast<TemplateTypeParmDecl>(CD->getTemplateParameters()->getParam(0)); + + // this TemplateTypeParmDecl is the template for the resource, and is + // used to construct a template argumentthat will be used + // to construct the ImplicitConceptSpecializationDecl + TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create( + Context, // AST context + Builder.Record->getDeclContext(), // DeclContext + SourceLocation(), SourceLocation(), + /*D=*/0, // Depth in the template parameter list + /*P=*/0, // Position in the template parameter list + /*Id=*/nullptr, // Identifier for 'T' + /*Typename=*/true, // Indicates this is a 'typename' or 'class' + /*ParameterPack=*/false, // Not a parameter pack + /*HasTypeConstraint=*/false // Has no type constraint + ); + + T->setDeclContext(DC); + + QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD); + + // this is the 2nd template argument node, on which + // the concept constraint is actually being applied: 'element_type' + TemplateArgument ConceptTA = TemplateArgument(ConceptTType); + + QualType CSETType = Context.getTypeDeclType(T); + + // this is the 1st template argument node, which represents + // the abstract type that a concept would refer to: 'T' + TemplateArgument CSETA = TemplateArgument(CSETType); + + ImplicitConceptSpecializationDecl *ImplicitCSEDecl = + ImplicitConceptSpecializationDecl::Create( + Context, Builder.Record->getDeclContext(), Loc, {CSETA}); + + // Constraint satisfaction is used to construct the + // ConceptSpecailizationExpr, and represents the 2nd Template Argument, + // located at the bottom of the sample AST above. + const ConstraintSatisfaction CS(CD, {ConceptTA}); + TemplateArgumentLoc TAL = + S.getTrivialTemplateArgumentLoc(ConceptTA, QualType(), SourceLocation()); + + TALI.addArgument(TAL); + const ASTTemplateArgumentListInfo *ATALI = + ASTTemplateArgumentListInfo::Create(Context, TALI); + + // In the concept reference, ATALI is what adds the extra + // TemplateArgument node underneath CSE + ConceptReference *CR = + ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI); + + ConceptSpecializationExpr *CSE = + ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS); + + return CSE; +} + +BuiltinTypeDeclBuilder & +TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) { + if (Params.empty()) + return Builder; + + ASTContext &AST = Builder.SemaRef.Context; + ConceptSpecializationExpr *CSE = + CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr; + auto *ParamList = TemplateParameterList::Create( + AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE); + Builder.Template = ClassTemplateDecl::Create( + AST, Builder.Record->getDeclContext(), SourceLocation(), + DeclarationName(Builder.Record->getIdentifier()), ParamList, + Builder.Record); + + Builder.Record->setDescribedClassTemplate(Builder.Template); + Builder.Template->setImplicit(true); + Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext()); + + // NOTE: setPreviousDecl before addDecl so new decl replace old decl when + // make visible. + Builder.Template->setPreviousDecl(Builder.PrevTemplate); + Builder.Record->getDeclContext()->addDecl(Builder.Template); + Params.clear(); + + QualType T = Builder.Template->getInjectedClassNameSpecialization(); + T = AST.getInjectedClassNameType(Builder.Record, T); + + return Builder; +} + +Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) { + if (PH == PlaceHolder::Handle) + return getResourceHandleExpr(); + + if (PH == PlaceHolder::LastStmt) { + assert(!StmtsList.empty() && "no statements in the list"); + Stmt *LastStmt = StmtsList.pop_back_val(); + assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value"); + return cast<ValueStmt>(LastStmt)->getExprStmt(); + } + + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH)); + return DeclRefExpr::Create( + AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false, + DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()), + ParamDecl->getType(), VK_PRValue); +} + +BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, + StringRef Name, + 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()); +} + +BuiltinTypeMethodBuilder & +BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier) { + assert(Method == nullptr && "Cannot add param, method already created"); + const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get( + Name, tok::TokenKind::identifier); + Params.emplace_back(II, Ty, Modifier); + return *this; +} + +void BuiltinTypeMethodBuilder::createMethodDecl() { + assert(Method == nullptr && "Method already created"); + + // create method type + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + SmallVector<QualType> ParamTypes; + for (MethodParam &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()); + + // 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]; + ParmVarDecl *Parm = ParmVarDecl::Create( + AST, Method->getDeclContext(), SourceLocation(), SourceLocation(), + &MP.NameII, MP.Ty, + AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None, + nullptr); + if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) { + auto *Mod = + HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier); + Parm->addAttr(Mod); + } + ParmDecls.push_back(Parm); + FnProtoLoc.setParam(I, Parm); + } + Method->setParams({ParmDecls}); +} + +Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() { + // The first statement added to a method or access to 'this' creates the + // declaration. + if (!Method) + createMethodDecl(); + + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + CXXThisExpr *This = CXXThisExpr::Create( + AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); + FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); + return MemberExpr::CreateImplicit(AST, This, false, HandleField, + HandleField->getType(), VK_LValue, + OK_Ordinary); +} + +template <typename... Ts> +BuiltinTypeMethodBuilder & +BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName, + QualType ReturnType, Ts... ArgSpecs) { + 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(); + + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName); + DeclRefExpr *DRE = DeclRefExpr::Create( + AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false, + FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue); + + if (ReturnType.isNull()) + ReturnType = FD->getReturnType(); + + Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue, + SourceLocation(), FPOptionsOverride()); + StmtsList.push_back(Call); + return *this; +} + +template <typename TLHS, typename TRHS> +BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) { + Expr *LHSExpr = convertPlaceholder(LHS); + Expr *RHSExpr = convertPlaceholder(RHS); + Stmt *AssignStmt = BinaryOperator::Create( + DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign, + LHSExpr->getType(), ExprValueKind::VK_PRValue, + ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride()); + StmtsList.push_back(AssignStmt); + return *this; +} + +template <typename T> +BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) { + Expr *PtrExpr = convertPlaceholder(Ptr); + Expr *Deref = + UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr, + UO_Deref, PtrExpr->getType()->getPointeeType(), + VK_PRValue, OK_Ordinary, SourceLocation(), + /*CanOverflow=*/false, FPOp... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/131032 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits