mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 12:26:50 +00:00
[HLSL][NFC] Refactor HLSLExternalSemaSource (#131032)
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.
This commit is contained in:
parent
3df92197bb
commit
9a82f742b4
@ -12,9 +12,8 @@
|
||||
#ifndef CLANG_SEMA_HLSLEXTERNALSEMASOURCE_H
|
||||
#define CLANG_SEMA_HLSLEXTERNALSEMASOURCE_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
#include "clang/Sema/ExternalSemaSource.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
namespace clang {
|
||||
class NamespaceDecl;
|
||||
@ -27,14 +26,8 @@ class HLSLExternalSemaSource : public ExternalSemaSource {
|
||||
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 +40,12 @@ public:
|
||||
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
|
||||
|
@ -20,6 +20,7 @@ add_clang_library(clangSema
|
||||
DeclSpec.cpp
|
||||
DelayedDiagnostic.cpp
|
||||
HeuristicResolver.cpp
|
||||
HLSLBuiltinTypeDeclBuilder.cpp
|
||||
HLSLExternalSemaSource.cpp
|
||||
IdentifierResolver.cpp
|
||||
JumpDiagnostics.cpp
|
||||
|
781
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
Normal file
781
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
Normal file
@ -0,0 +1,781 @@
|
||||
//===--- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Helper classes for creating HLSL builtin class types. Used by external HLSL
|
||||
// sema source.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#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, FPOptionsOverride());
|
||||
StmtsList.push_back(Deref);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalizeMethod() {
|
||||
assert(!DeclBuilder.Record->isCompleteDefinition() &&
|
||||
"record is already complete");
|
||||
assert(Method != nullptr &&
|
||||
"method decl not created; are you missing a call to build the body?");
|
||||
|
||||
if (!Method->hasBody()) {
|
||||
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
||||
assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
|
||||
"nothing to return from non-void method");
|
||||
if (ReturnTy != AST.VoidTy) {
|
||||
if (Expr *LastExpr = dyn_cast<Expr>(StmtsList.back())) {
|
||||
assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
|
||||
ReturnTy.getNonReferenceType()) &&
|
||||
"Return type of the last statement must match the return type "
|
||||
"of the method");
|
||||
if (!isa<ReturnStmt>(LastExpr)) {
|
||||
StmtsList.pop_back();
|
||||
StmtsList.push_back(
|
||||
ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
|
||||
SourceLocation(), SourceLocation()));
|
||||
Method->setLexicalDeclContext(DeclBuilder.Record);
|
||||
Method->setAccess(AccessSpecifier::AS_public);
|
||||
Method->addAttr(AlwaysInlineAttr::CreateImplicit(
|
||||
AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
|
||||
DeclBuilder.Record->addDecl(Method);
|
||||
}
|
||||
return DeclBuilder;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
|
||||
: SemaRef(SemaRef), Record(R) {
|
||||
Record->startDefinition();
|
||||
Template = Record->getDescribedClassTemplate();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder::BuiltinTypeDeclBuilder(Sema &SemaRef,
|
||||
NamespaceDecl *Namespace,
|
||||
StringRef Name)
|
||||
: SemaRef(SemaRef), HLSLNamespace(Namespace) {
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
|
||||
|
||||
LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
|
||||
CXXRecordDecl *PrevDecl = nullptr;
|
||||
if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) {
|
||||
// Declaration already exists (from precompiled headers)
|
||||
NamedDecl *Found = Result.getFoundDecl();
|
||||
if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) {
|
||||
PrevDecl = TD->getTemplatedDecl();
|
||||
PrevTemplate = TD;
|
||||
} else
|
||||
PrevDecl = dyn_cast<CXXRecordDecl>(Found);
|
||||
assert(PrevDecl && "Unexpected lookup result type.");
|
||||
}
|
||||
|
||||
if (PrevDecl && PrevDecl->isCompleteDefinition()) {
|
||||
Record = PrevDecl;
|
||||
Template = PrevTemplate;
|
||||
return;
|
||||
}
|
||||
|
||||
Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
|
||||
SourceLocation(), SourceLocation(), &II,
|
||||
PrevDecl, true);
|
||||
Record->setImplicit(true);
|
||||
Record->setLexicalDeclContext(HLSLNamespace);
|
||||
Record->setHasExternalLexicalStorage();
|
||||
|
||||
// Don't let anyone derive from built-in types.
|
||||
Record->addAttr(
|
||||
FinalAttr::CreateImplicit(AST, SourceRange(), FinalAttr::Keyword_final));
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder::~BuiltinTypeDeclBuilder() {
|
||||
if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
|
||||
HLSLNamespace->addDecl(Record);
|
||||
}
|
||||
|
||||
CXXRecordDecl *BuiltinTypeDeclBuilder::finalizeForwardDeclaration() {
|
||||
// Force the QualType to be generated for the record declaration. In most
|
||||
// cases this will happen naturally when something uses the type the
|
||||
// QualType gets lazily created. Unfortunately, with our injected types if a
|
||||
// type isn't used in a translation unit the QualType may not get
|
||||
// automatically generated before a PCH is generated. To resolve this we
|
||||
// just force that the QualType is generated after we create a forward
|
||||
// declaration.
|
||||
(void)Record->getASTContext().getRecordType(Record);
|
||||
return Record;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type,
|
||||
llvm::ArrayRef<Attr *> Attrs,
|
||||
AccessSpecifier Access) {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
assert(Record->isBeingDefined() &&
|
||||
"Definition must be started before adding members!");
|
||||
ASTContext &AST = Record->getASTContext();
|
||||
|
||||
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
|
||||
TypeSourceInfo *MemTySource =
|
||||
AST.getTrivialTypeSourceInfo(Type, SourceLocation());
|
||||
auto *Field = FieldDecl::Create(
|
||||
AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,
|
||||
nullptr, false, InClassInitStyle::ICIS_NoInit);
|
||||
Field->setAccess(Access);
|
||||
Field->setImplicit(true);
|
||||
for (Attr *A : Attrs) {
|
||||
if (A)
|
||||
Field->addAttr(A);
|
||||
}
|
||||
|
||||
Record->addDecl(Field);
|
||||
Fields[Name] = Field;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember(
|
||||
ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
|
||||
ASTContext &Ctx = SemaRef.getASTContext();
|
||||
TypeSourceInfo *ElementTypeInfo =
|
||||
Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
|
||||
|
||||
// add handle member with resource type attributes
|
||||
QualType AttributedResTy = QualType();
|
||||
SmallVector<const Attr *> Attrs = {
|
||||
HLSLResourceClassAttr::CreateImplicit(Ctx, RC),
|
||||
IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
|
||||
RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
|
||||
ElementTypeInfo
|
||||
? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo)
|
||||
: nullptr};
|
||||
if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
|
||||
AttributedResTy))
|
||||
addMemberVariable("__handle", AttributedResTy, {}, Access);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
|
||||
ASTContext &AST = Record->getASTContext();
|
||||
DeclarationName Subscript =
|
||||
AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
|
||||
|
||||
addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true);
|
||||
addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
|
||||
if (Record->isCompleteDefinition())
|
||||
return *this;
|
||||
|
||||
ASTContext &AST = Record->getASTContext();
|
||||
IdentifierInfo &II = AST.Idents.get("Load", tok::TokenKind::identifier);
|
||||
DeclarationName Load(&II);
|
||||
// TODO: We also need versions with status for CheckAccessFullyMapped.
|
||||
addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() {
|
||||
auto I = Fields.find("__handle");
|
||||
assert(I != Fields.end() &&
|
||||
I->second->getType()->isHLSLAttributedResourceType() &&
|
||||
"record does not have resource handle field");
|
||||
return I->second;
|
||||
}
|
||||
|
||||
QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() {
|
||||
assert(Template && "record it not a template");
|
||||
if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
|
||||
Template->getTemplateParameters()->getParam(0))) {
|
||||
return QualType(TTD->getTypeForDecl(), 0);
|
||||
}
|
||||
return QualType();
|
||||
}
|
||||
|
||||
QualType BuiltinTypeDeclBuilder::getHandleElementType() {
|
||||
if (Template)
|
||||
return getFirstTemplateTypeParam();
|
||||
// TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
|
||||
return SemaRef.getASTContext().Char8Ty;
|
||||
}
|
||||
|
||||
// BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::startDefinition() {
|
||||
// assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
// Record->startDefinition();
|
||||
// return *this;
|
||||
// }
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::completeDefinition() {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
assert(Record->isBeingDefined() &&
|
||||
"Definition must be started before completing it.");
|
||||
|
||||
Record->completeDefinition();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Expr *BuiltinTypeDeclBuilder::getConstantIntExpr(int value) {
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
return IntegerLiteral::Create(
|
||||
AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
|
||||
SourceLocation());
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
|
||||
ConceptDecl *CD = nullptr) {
|
||||
if (Record->isCompleteDefinition()) {
|
||||
assert(Template && "existing record it not a template");
|
||||
assert(Template->getTemplateParameters()->size() == Names.size() &&
|
||||
"template param count mismatch");
|
||||
return *this;
|
||||
}
|
||||
|
||||
TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this);
|
||||
for (StringRef Name : Names)
|
||||
Builder.addTypeParameter(Name);
|
||||
return Builder.finalizeTemplateArgs(CD);
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
|
||||
SemaRef.getASTContext().UnsignedIntTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
|
||||
PH::Handle, getConstantIntExpr(1))
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
|
||||
SemaRef.getASTContext().UnsignedIntTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
|
||||
PH::Handle, getConstantIntExpr(-1))
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
|
||||
bool IsConst, bool IsRef) {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
|
||||
QualType ElemTy = getHandleElementType();
|
||||
// TODO: Map to an hlsl_device address space.
|
||||
QualType ElemPtrTy = AST.getPointerType(ElemTy);
|
||||
QualType ReturnTy = ElemTy;
|
||||
if (IsConst)
|
||||
ReturnTy.addConst();
|
||||
if (IsRef)
|
||||
ReturnTy = AST.getLValueReferenceType(ReturnTy);
|
||||
|
||||
return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
|
||||
.addParam("Index", AST.UnsignedIntTy)
|
||||
.callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
|
||||
PH::_0)
|
||||
.dereference(PH::LastStmt)
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
QualType ElemTy = getHandleElementType();
|
||||
return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
|
||||
.addParam("value", ElemTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
|
||||
PH::Handle, getConstantIntExpr(1))
|
||||
.callBuiltin("__builtin_hlsl_resource_getpointer",
|
||||
AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
|
||||
.dereference(PH::LastStmt)
|
||||
.assign(PH::LastStmt, PH::_0)
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
QualType ElemTy = getHandleElementType();
|
||||
return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
|
||||
PH::Handle, getConstantIntExpr(-1))
|
||||
.callBuiltin("__builtin_hlsl_resource_getpointer",
|
||||
AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
|
||||
.dereference(PH::LastStmt)
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
} // namespace hlsl
|
||||
} // namespace clang
|
102
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
Normal file
102
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
Normal file
@ -0,0 +1,102 @@
|
||||
//===--- HLSLBuiltinTypeDeclBuilder.h - 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Helper classes for creating HLSL builtin class types. Used by external HLSL
|
||||
// sema source.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_SEMA_HLSLBUILTINTYPEDECLBUILDER_H
|
||||
#define LLVM_CLANG_SEMA_HLSLBUILTINTYPEDECLBUILDER_H
|
||||
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
|
||||
using llvm::hlsl::ResourceClass;
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ClassTemplateDecl;
|
||||
class NamespaceDecl;
|
||||
class CXXRecordDecl;
|
||||
class FieldDecl;
|
||||
|
||||
namespace hlsl {
|
||||
|
||||
// Builder for builtin HLSL class types such as HLSL resource classes.
|
||||
// Allows creating declaration of builtin types using the builder pattern
|
||||
// like this:
|
||||
//
|
||||
// Decl = BuiltinTypeDeclBuilder(Sema, Namespace, "BuiltinClassName")
|
||||
// .addSimpleTemplateParams({"T"}, Concept)
|
||||
// .finalizeForwardDeclaration();
|
||||
//
|
||||
// And then completing the type like this:
|
||||
//
|
||||
// BuiltinTypeDeclBuilder(Sema, Decl)
|
||||
// .addDefaultHandleConstructor();
|
||||
// .addLoadMethods()
|
||||
// .completeDefinition();
|
||||
//
|
||||
class BuiltinTypeDeclBuilder {
|
||||
private:
|
||||
Sema &SemaRef;
|
||||
CXXRecordDecl *Record = nullptr;
|
||||
ClassTemplateDecl *Template = nullptr;
|
||||
ClassTemplateDecl *PrevTemplate = nullptr;
|
||||
NamespaceDecl *HLSLNamespace = nullptr;
|
||||
llvm::StringMap<FieldDecl *> Fields;
|
||||
|
||||
public:
|
||||
friend struct TemplateParameterListBuilder;
|
||||
friend struct BuiltinTypeMethodBuilder;
|
||||
|
||||
BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R);
|
||||
BuiltinTypeDeclBuilder(Sema &SemaRef, NamespaceDecl *Namespace,
|
||||
StringRef Name);
|
||||
~BuiltinTypeDeclBuilder();
|
||||
|
||||
BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names,
|
||||
ConceptDecl *CD);
|
||||
CXXRecordDecl *finalizeForwardDeclaration();
|
||||
BuiltinTypeDeclBuilder &completeDefinition();
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef<Attr *> Attrs,
|
||||
AccessSpecifier Access = AccessSpecifier::AS_private);
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
addHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer,
|
||||
AccessSpecifier Access = AccessSpecifier::AS_private);
|
||||
BuiltinTypeDeclBuilder &addArraySubscriptOperators();
|
||||
|
||||
// Builtin types methods
|
||||
BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
|
||||
|
||||
// Builtin types methods
|
||||
BuiltinTypeDeclBuilder &addLoadMethods();
|
||||
BuiltinTypeDeclBuilder &addIncrementCounterMethod();
|
||||
BuiltinTypeDeclBuilder &addDecrementCounterMethod();
|
||||
BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name,
|
||||
bool IsConst, bool IsRef);
|
||||
BuiltinTypeDeclBuilder &addAppendMethod();
|
||||
BuiltinTypeDeclBuilder &addConsumeMethod();
|
||||
|
||||
private:
|
||||
FieldDecl *getResourceHandleField();
|
||||
QualType getFirstTemplateTypeParam();
|
||||
QualType getHandleElementType();
|
||||
Expr *getConstantIntExpr(int value);
|
||||
};
|
||||
|
||||
} // namespace hlsl
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_SEMA_HLSLBUILTINTYPEDECLBUILDER_H
|
@ -10,6 +10,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Sema/HLSLExternalSemaSource.h"
|
||||
#include "HLSLBuiltinTypeDeclBuilder.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
@ -21,741 +22,11 @@
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Sema/SemaHLSL.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Frontend/HLSL/HLSLResource.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace clang;
|
||||
using namespace llvm::hlsl;
|
||||
|
||||
static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name);
|
||||
|
||||
namespace {
|
||||
|
||||
struct TemplateParameterListBuilder;
|
||||
|
||||
class BuiltinTypeDeclBuilder {
|
||||
ClassTemplateDecl *Template = nullptr;
|
||||
ClassTemplateDecl *PrevTemplate = nullptr;
|
||||
NamespaceDecl *HLSLNamespace = nullptr;
|
||||
llvm::StringMap<FieldDecl *> Fields;
|
||||
|
||||
public:
|
||||
Sema &SemaRef;
|
||||
CXXRecordDecl *Record = nullptr;
|
||||
friend struct TemplateParameterListBuilder;
|
||||
|
||||
BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
|
||||
: SemaRef(SemaRef), Record(R) {
|
||||
Record->startDefinition();
|
||||
Template = Record->getDescribedClassTemplate();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder(Sema &SemaRef, NamespaceDecl *Namespace,
|
||||
StringRef Name)
|
||||
: HLSLNamespace(Namespace), SemaRef(SemaRef) {
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
|
||||
|
||||
LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
|
||||
CXXRecordDecl *PrevDecl = nullptr;
|
||||
if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) {
|
||||
// Declaration already exists (from precompiled headers)
|
||||
NamedDecl *Found = Result.getFoundDecl();
|
||||
if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) {
|
||||
PrevDecl = TD->getTemplatedDecl();
|
||||
PrevTemplate = TD;
|
||||
} else
|
||||
PrevDecl = dyn_cast<CXXRecordDecl>(Found);
|
||||
assert(PrevDecl && "Unexpected lookup result type.");
|
||||
}
|
||||
|
||||
if (PrevDecl && PrevDecl->isCompleteDefinition()) {
|
||||
Record = PrevDecl;
|
||||
Template = PrevTemplate;
|
||||
return;
|
||||
}
|
||||
|
||||
Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
|
||||
SourceLocation(), SourceLocation(), &II,
|
||||
PrevDecl, true);
|
||||
Record->setImplicit(true);
|
||||
Record->setLexicalDeclContext(HLSLNamespace);
|
||||
Record->setHasExternalLexicalStorage();
|
||||
|
||||
// Don't let anyone derive from built-in types.
|
||||
Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(),
|
||||
FinalAttr::Keyword_final));
|
||||
}
|
||||
|
||||
~BuiltinTypeDeclBuilder() {
|
||||
if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
|
||||
HLSLNamespace->addDecl(Record);
|
||||
}
|
||||
|
||||
CXXRecordDecl *finalizeForwardDeclaration() {
|
||||
// Force the QualType to be generated for the record declaration. In most
|
||||
// cases this will happen naturally when something uses the type the
|
||||
// QualType gets lazily created. Unfortunately, with our injected types if a
|
||||
// type isn't used in a translation unit the QualType may not get
|
||||
// automatically generated before a PCH is generated. To resolve this we
|
||||
// just force that the QualType is generated after we create a forward
|
||||
// declaration.
|
||||
(void)Record->getASTContext().getRecordType(Record);
|
||||
return Record;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef<Attr *> Attrs,
|
||||
AccessSpecifier Access = AccessSpecifier::AS_private) {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
assert(Record->isBeingDefined() &&
|
||||
"Definition must be started before adding members!");
|
||||
ASTContext &AST = Record->getASTContext();
|
||||
|
||||
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
|
||||
TypeSourceInfo *MemTySource =
|
||||
AST.getTrivialTypeSourceInfo(Type, SourceLocation());
|
||||
auto *Field = FieldDecl::Create(
|
||||
AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,
|
||||
nullptr, false, InClassInitStyle::ICIS_NoInit);
|
||||
Field->setAccess(Access);
|
||||
Field->setImplicit(true);
|
||||
for (Attr *A : Attrs) {
|
||||
if (A)
|
||||
Field->addAttr(A);
|
||||
}
|
||||
|
||||
Record->addDecl(Field);
|
||||
Fields[Name] = Field;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
addHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer,
|
||||
AccessSpecifier Access = AccessSpecifier::AS_private) {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
|
||||
ASTContext &Ctx = SemaRef.getASTContext();
|
||||
TypeSourceInfo *ElementTypeInfo =
|
||||
Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
|
||||
|
||||
// add handle member with resource type attributes
|
||||
QualType AttributedResTy = QualType();
|
||||
SmallVector<const Attr *> Attrs = {
|
||||
HLSLResourceClassAttr::CreateImplicit(Ctx, RC),
|
||||
IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
|
||||
RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
|
||||
ElementTypeInfo
|
||||
? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo)
|
||||
: nullptr};
|
||||
if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
|
||||
AttributedResTy))
|
||||
addMemberVariable("__handle", AttributedResTy, {}, Access);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &addArraySubscriptOperators() {
|
||||
ASTContext &AST = Record->getASTContext();
|
||||
DeclarationName Subscript =
|
||||
AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
|
||||
|
||||
addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true);
|
||||
addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &addLoadMethods() {
|
||||
if (Record->isCompleteDefinition())
|
||||
return *this;
|
||||
|
||||
ASTContext &AST = Record->getASTContext();
|
||||
IdentifierInfo &II = AST.Idents.get("Load", tok::TokenKind::identifier);
|
||||
DeclarationName Load(&II);
|
||||
// TODO: We also need versions with status for CheckAccessFullyMapped.
|
||||
addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FieldDecl *getResourceHandleField() {
|
||||
auto I = Fields.find("__handle");
|
||||
assert(I != Fields.end() &&
|
||||
I->second->getType()->isHLSLAttributedResourceType() &&
|
||||
"record does not have resource handle field");
|
||||
return I->second;
|
||||
}
|
||||
|
||||
QualType getFirstTemplateTypeParam() {
|
||||
assert(Template && "record it not a template");
|
||||
if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
|
||||
Template->getTemplateParameters()->getParam(0))) {
|
||||
return QualType(TTD->getTypeForDecl(), 0);
|
||||
}
|
||||
return QualType();
|
||||
}
|
||||
|
||||
QualType getHandleElementType() {
|
||||
if (Template)
|
||||
return getFirstTemplateTypeParam();
|
||||
// TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
|
||||
return SemaRef.getASTContext().Char8Ty;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &startDefinition() {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
Record->startDefinition();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &completeDefinition() {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
assert(Record->isBeingDefined() &&
|
||||
"Definition must be started before completing it.");
|
||||
|
||||
Record->completeDefinition();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Expr *getConstantIntExpr(int value) {
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
return IntegerLiteral::Create(
|
||||
AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
|
||||
SourceLocation());
|
||||
}
|
||||
|
||||
TemplateParameterListBuilder addTemplateArgumentList();
|
||||
BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names,
|
||||
ConceptDecl *CD);
|
||||
|
||||
// Builtin types methods
|
||||
BuiltinTypeDeclBuilder &addIncrementCounterMethod();
|
||||
BuiltinTypeDeclBuilder &addDecrementCounterMethod();
|
||||
BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name,
|
||||
bool IsConst, bool IsRef);
|
||||
BuiltinTypeDeclBuilder &addAppendMethod();
|
||||
BuiltinTypeDeclBuilder &addConsumeMethod();
|
||||
};
|
||||
|
||||
struct TemplateParameterListBuilder {
|
||||
BuiltinTypeDeclBuilder &Builder;
|
||||
llvm::SmallVector<NamedDecl *> Params;
|
||||
|
||||
TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
|
||||
|
||||
~TemplateParameterListBuilder() { finalizeTemplateArgs(); }
|
||||
|
||||
TemplateParameterListBuilder &
|
||||
addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) {
|
||||
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 *
|
||||
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 &finalizeTemplateArgs(ConceptDecl *CD = nullptr) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// 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 {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
Expr *convertPlaceholder(Expr *E) { return E; }
|
||||
|
||||
public:
|
||||
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)
|
||||
: 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 &addParam(StringRef Name, QualType Ty,
|
||||
HLSLParamModifierAttr::Spelling Modifier =
|
||||
HLSLParamModifierAttr::Keyword_in) {
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
void 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});
|
||||
}
|
||||
|
||||
public:
|
||||
~BuiltinTypeMethodBuilder() { finalizeMethod(); }
|
||||
|
||||
BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
|
||||
BuiltinTypeMethodBuilder &
|
||||
operator=(const BuiltinTypeMethodBuilder &Other) = delete;
|
||||
|
||||
Expr *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 &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 &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 &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, FPOptionsOverride());
|
||||
StmtsList.push_back(Deref);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &finalizeMethod() {
|
||||
assert(!DeclBuilder.Record->isCompleteDefinition() &&
|
||||
"record is already complete");
|
||||
assert(
|
||||
Method != nullptr &&
|
||||
"method decl not created; are you missing a call to build the body?");
|
||||
|
||||
if (!Method->hasBody()) {
|
||||
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
||||
assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
|
||||
"nothing to return from non-void method");
|
||||
if (ReturnTy != AST.VoidTy) {
|
||||
if (Expr *LastExpr = dyn_cast<Expr>(StmtsList.back())) {
|
||||
assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
|
||||
ReturnTy.getNonReferenceType()) &&
|
||||
"Return type of the last statement must match the return type "
|
||||
"of the method");
|
||||
if (!isa<ReturnStmt>(LastExpr)) {
|
||||
StmtsList.pop_back();
|
||||
StmtsList.push_back(
|
||||
ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
|
||||
SourceLocation(), SourceLocation()));
|
||||
Method->setLexicalDeclContext(DeclBuilder.Record);
|
||||
Method->setAccess(AccessSpecifier::AS_public);
|
||||
Method->addAttr(AlwaysInlineAttr::CreateImplicit(
|
||||
AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
|
||||
DeclBuilder.Record->addDecl(Method);
|
||||
}
|
||||
return DeclBuilder;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() {
|
||||
return TemplateParameterListBuilder(*this);
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
|
||||
ConceptDecl *CD = nullptr) {
|
||||
if (Record->isCompleteDefinition()) {
|
||||
assert(Template && "existing record it not a template");
|
||||
assert(Template->getTemplateParameters()->size() == Names.size() &&
|
||||
"template param count mismatch");
|
||||
return *this;
|
||||
}
|
||||
|
||||
TemplateParameterListBuilder Builder = this->addTemplateArgumentList();
|
||||
for (StringRef Name : Names)
|
||||
Builder.addTypeParameter(Name);
|
||||
return Builder.finalizeTemplateArgs(CD);
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
|
||||
SemaRef.getASTContext().UnsignedIntTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
|
||||
PH::Handle, getConstantIntExpr(1))
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
|
||||
SemaRef.getASTContext().UnsignedIntTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
|
||||
PH::Handle, getConstantIntExpr(-1))
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
|
||||
bool IsConst, bool IsRef) {
|
||||
assert(!Record->isCompleteDefinition() && "record is already complete");
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
|
||||
QualType ElemTy = getHandleElementType();
|
||||
// TODO: Map to an hlsl_device address space.
|
||||
QualType ElemPtrTy = AST.getPointerType(ElemTy);
|
||||
QualType ReturnTy = ElemTy;
|
||||
if (IsConst)
|
||||
ReturnTy.addConst();
|
||||
if (IsRef)
|
||||
ReturnTy = AST.getLValueReferenceType(ReturnTy);
|
||||
|
||||
return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
|
||||
.addParam("Index", AST.UnsignedIntTy)
|
||||
.callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
|
||||
PH::_0)
|
||||
.dereference(PH::LastStmt)
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
QualType ElemTy = getHandleElementType();
|
||||
return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy)
|
||||
.addParam("value", ElemTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
|
||||
PH::Handle, getConstantIntExpr(1))
|
||||
.callBuiltin("__builtin_hlsl_resource_getpointer",
|
||||
AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
|
||||
.dereference(PH::LastStmt)
|
||||
.assign(PH::LastStmt, PH::_0)
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() {
|
||||
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
||||
ASTContext &AST = SemaRef.getASTContext();
|
||||
QualType ElemTy = getHandleElementType();
|
||||
return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy)
|
||||
.callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy,
|
||||
PH::Handle, getConstantIntExpr(-1))
|
||||
.callBuiltin("__builtin_hlsl_resource_getpointer",
|
||||
AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt)
|
||||
.dereference(PH::LastStmt)
|
||||
.finalizeMethod();
|
||||
}
|
||||
|
||||
HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
|
||||
using clang::hlsl::BuiltinTypeDeclBuilder;
|
||||
|
||||
void HLSLExternalSemaSource::InitializeSema(Sema &S) {
|
||||
SemaPtr = &S;
|
||||
@ -1122,19 +393,3 @@ void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {
|
||||
return;
|
||||
It->second(Record);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user