mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 18:06:05 +00:00
1060 lines
42 KiB
C++
1060 lines
42 KiB
C++
//===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===//
|
|
//
|
|
// 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 "clang/Sema/HLSLExternalSemaSource.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"
|
|
#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, ResourceKind RK, 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};
|
|
Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK);
|
|
if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
|
|
AttributedResTy))
|
|
addMemberVariable("__handle", AttributedResTy, {ResourceAttr}, 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;
|
|
}
|
|
|
|
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(); }
|
|
|
|
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() {}
|
|
|
|
void HLSLExternalSemaSource::InitializeSema(Sema &S) {
|
|
SemaPtr = &S;
|
|
ASTContext &AST = SemaPtr->getASTContext();
|
|
// If the translation unit has external storage force external decls to load.
|
|
if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage())
|
|
(void)AST.getTranslationUnitDecl()->decls_begin();
|
|
|
|
IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier);
|
|
LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName);
|
|
NamespaceDecl *PrevDecl = nullptr;
|
|
if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl()))
|
|
PrevDecl = Result.getAsSingle<NamespaceDecl>();
|
|
HLSLNamespace = NamespaceDecl::Create(
|
|
AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(),
|
|
SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false);
|
|
HLSLNamespace->setImplicit(true);
|
|
HLSLNamespace->setHasExternalLexicalStorage();
|
|
AST.getTranslationUnitDecl()->addDecl(HLSLNamespace);
|
|
|
|
// Force external decls in the HLSL namespace to load from the PCH.
|
|
(void)HLSLNamespace->getCanonicalDecl()->decls_begin();
|
|
defineTrivialHLSLTypes();
|
|
defineHLSLTypesWithForwardDeclarations();
|
|
|
|
// This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's
|
|
// built in types inside a namespace, but we are planning to change that in
|
|
// the near future. In order to be source compatible older versions of HLSL
|
|
// will need to implicitly use the hlsl namespace. For now in clang everything
|
|
// will get added to the namespace, and we can remove the using directive for
|
|
// future language versions to match HLSL's evolution.
|
|
auto *UsingDecl = UsingDirectiveDecl::Create(
|
|
AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
|
|
NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace,
|
|
AST.getTranslationUnitDecl());
|
|
|
|
AST.getTranslationUnitDecl()->addDecl(UsingDecl);
|
|
}
|
|
|
|
void HLSLExternalSemaSource::defineHLSLVectorAlias() {
|
|
ASTContext &AST = SemaPtr->getASTContext();
|
|
|
|
llvm::SmallVector<NamedDecl *> TemplateParams;
|
|
|
|
auto *TypeParam = TemplateTypeParmDecl::Create(
|
|
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0,
|
|
&AST.Idents.get("element", tok::TokenKind::identifier), false, false);
|
|
TypeParam->setDefaultArgument(
|
|
AST, SemaPtr->getTrivialTemplateArgumentLoc(
|
|
TemplateArgument(AST.FloatTy), QualType(), SourceLocation()));
|
|
|
|
TemplateParams.emplace_back(TypeParam);
|
|
|
|
auto *SizeParam = NonTypeTemplateParmDecl::Create(
|
|
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1,
|
|
&AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy,
|
|
false, AST.getTrivialTypeSourceInfo(AST.IntTy));
|
|
llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4);
|
|
TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy,
|
|
/*IsDefaulted=*/true);
|
|
SizeParam->setDefaultArgument(
|
|
AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy,
|
|
SourceLocation(), SizeParam));
|
|
TemplateParams.emplace_back(SizeParam);
|
|
|
|
auto *ParamList =
|
|
TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(),
|
|
TemplateParams, SourceLocation(), nullptr);
|
|
|
|
IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier);
|
|
|
|
QualType AliasType = AST.getDependentSizedExtVectorType(
|
|
AST.getTemplateTypeParmType(0, 0, false, TypeParam),
|
|
DeclRefExpr::Create(
|
|
AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false,
|
|
DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()),
|
|
AST.IntTy, VK_LValue),
|
|
SourceLocation());
|
|
|
|
auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(),
|
|
SourceLocation(), &II,
|
|
AST.getTrivialTypeSourceInfo(AliasType));
|
|
Record->setImplicit(true);
|
|
|
|
auto *Template =
|
|
TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(),
|
|
Record->getIdentifier(), ParamList, Record);
|
|
|
|
Record->setDescribedAliasTemplate(Template);
|
|
Template->setImplicit(true);
|
|
Template->setLexicalDeclContext(Record->getDeclContext());
|
|
HLSLNamespace->addDecl(Template);
|
|
}
|
|
|
|
void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
|
|
defineHLSLVectorAlias();
|
|
}
|
|
|
|
/// Set up common members and attributes for buffer types
|
|
static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
|
|
ResourceClass RC, ResourceKind RK,
|
|
bool IsROV, bool RawBuffer) {
|
|
return BuiltinTypeDeclBuilder(S, Decl)
|
|
.addHandleMember(RC, RK, IsROV, RawBuffer)
|
|
.addDefaultHandleConstructor();
|
|
}
|
|
|
|
static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
|
|
TemplateTypeParmDecl *T) {
|
|
ASTContext &Context = S.getASTContext();
|
|
|
|
// Obtain the QualType for 'bool'
|
|
QualType BoolTy = Context.BoolTy;
|
|
|
|
// Create a QualType that points to this TemplateTypeParmDecl
|
|
QualType TType = Context.getTypeDeclType(T);
|
|
|
|
// Create a TypeSourceInfo for the template type parameter 'T'
|
|
TypeSourceInfo *TTypeSourceInfo =
|
|
Context.getTrivialTypeSourceInfo(TType, NameLoc);
|
|
|
|
TypeTraitExpr *TypedResExpr = TypeTraitExpr::Create(
|
|
Context, BoolTy, NameLoc, UTT_IsTypedResourceElementCompatible,
|
|
{TTypeSourceInfo}, NameLoc, true);
|
|
|
|
return TypedResExpr;
|
|
}
|
|
|
|
static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
|
|
NamespaceDecl *NSD) {
|
|
ASTContext &Context = S.getASTContext();
|
|
DeclContext *DC = NSD->getDeclContext();
|
|
SourceLocation DeclLoc = SourceLocation();
|
|
|
|
IdentifierInfo &ElementTypeII = Context.Idents.get("element_type");
|
|
TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
|
|
Context, NSD->getDeclContext(), DeclLoc, DeclLoc,
|
|
/*D=*/0,
|
|
/*P=*/0,
|
|
/*Id=*/&ElementTypeII,
|
|
/*Typename=*/true,
|
|
/*ParameterPack=*/false);
|
|
|
|
T->setDeclContext(DC);
|
|
T->setReferenced();
|
|
|
|
// Create and Attach Template Parameter List to ConceptDecl
|
|
TemplateParameterList *ConceptParams = TemplateParameterList::Create(
|
|
Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr);
|
|
|
|
DeclarationName DeclName = DeclarationName(
|
|
&Context.Idents.get("__is_typed_resource_element_compatible"));
|
|
Expr *ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
|
|
|
|
// Create a ConceptDecl
|
|
ConceptDecl *CD =
|
|
ConceptDecl::Create(Context, NSD->getDeclContext(), DeclLoc, DeclName,
|
|
ConceptParams, ConstraintExpr);
|
|
|
|
// Attach the template parameter list to the ConceptDecl
|
|
CD->setTemplateParameters(ConceptParams);
|
|
|
|
// Add the concept declaration to the Translation Unit Decl
|
|
NSD->getDeclContext()->addDecl(CD);
|
|
|
|
return CD;
|
|
}
|
|
|
|
void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
|
CXXRecordDecl *Decl;
|
|
ConceptDecl *TypedBufferConcept =
|
|
constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace);
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
|
|
.addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
|
|
.finalizeForwardDeclaration();
|
|
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
|
|
ResourceKind::TypedBuffer, /*IsROV=*/false,
|
|
/*RawBuffer=*/false)
|
|
.addArraySubscriptOperators()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl =
|
|
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
|
|
ResourceKind::TypedBuffer, /*IsROV=*/true,
|
|
/*RawBuffer=*/false)
|
|
.addArraySubscriptOperators()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.addArraySubscriptOperators()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.addArraySubscriptOperators()
|
|
.addIncrementCounterMethod()
|
|
.addDecrementCounterMethod()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl =
|
|
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.addAppendMethod()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl =
|
|
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.addConsumeMethod()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
|
|
"RasterizerOrderedStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/true, /*RawBuffer=*/true)
|
|
.addArraySubscriptOperators()
|
|
.addIncrementCounterMethod()
|
|
.addDecrementCounterMethod()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ByteAddressBuffer")
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false,
|
|
/*RawBuffer=*/true)
|
|
.completeDefinition();
|
|
});
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWByteAddressBuffer")
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false,
|
|
/*RawBuffer=*/true)
|
|
.completeDefinition();
|
|
});
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
|
|
"RasterizerOrderedByteAddressBuffer")
|
|
.finalizeForwardDeclaration();
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/true,
|
|
/*RawBuffer=*/true)
|
|
.completeDefinition();
|
|
});
|
|
}
|
|
|
|
void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record,
|
|
CompletionFunction Fn) {
|
|
if (!Record->isCompleteDefinition())
|
|
Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn));
|
|
}
|
|
|
|
void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {
|
|
if (!isa<CXXRecordDecl>(Tag))
|
|
return;
|
|
auto Record = cast<CXXRecordDecl>(Tag);
|
|
|
|
// If this is a specialization, we need to get the underlying templated
|
|
// declaration and complete that.
|
|
if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(Record))
|
|
Record = TDecl->getSpecializedTemplate()->getTemplatedDecl();
|
|
Record = Record->getCanonicalDecl();
|
|
auto It = Completions.find(Record);
|
|
if (It == Completions.end())
|
|
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());
|
|
}
|