llvm-project/clang/lib/Sema/HLSLExternalSemaSource.cpp
Chris Bieneman cc47db6737 [HLSL] Add HLSLResource attribute
HLSL Resource objects will have restrictions on use and codegen
requirements. This patch is fairly minimal just adding the attribute
with no spellings since it will only be attached by the
HLSLExternalSemaSource.

Depends on D1300017.

Differential Revision: https://reviews.llvm.org/D130018
2022-07-28 20:54:51 -05:00

374 lines
14 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/DeclCXX.h"
#include "clang/Basic/AttrKinds.h"
#include "clang/Basic/HLSLRuntime.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include <functional>
using namespace clang;
using namespace hlsl;
namespace {
struct TemplateParameterListBuilder;
struct BuiltinTypeDeclBuilder {
CXXRecordDecl *Record = nullptr;
ClassTemplateDecl *Template = nullptr;
NamespaceDecl *HLSLNamespace = nullptr;
llvm::StringMap<FieldDecl *> Fields;
BuiltinTypeDeclBuilder(CXXRecordDecl *R) : Record(R) {
Record->startDefinition();
Template = Record->getDescribedClassTemplate();
}
BuiltinTypeDeclBuilder(Sema &S, NamespaceDecl *Namespace, StringRef Name)
: HLSLNamespace(Namespace) {
ASTContext &AST = S.getASTContext();
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::TTK_Class,
HLSLNamespace, SourceLocation(),
SourceLocation(), &II, nullptr, true);
Record->setImplicit(true);
Record->setLexicalDeclContext(HLSLNamespace);
Record->setHasExternalLexicalStorage();
// Don't let anyone derive from built-in types
Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(),
AttributeCommonInfo::AS_Keyword,
FinalAttr::Keyword_final));
}
~BuiltinTypeDeclBuilder() {
if (HLSLNamespace && !Template)
HLSLNamespace->addDecl(Record);
}
BuiltinTypeDeclBuilder &
addTemplateArgumentList(llvm::ArrayRef<NamedDecl *> TemplateArgs) {
ASTContext &AST = Record->getASTContext();
auto *ParamList =
TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(),
TemplateArgs, SourceLocation(), nullptr);
Template = ClassTemplateDecl::Create(
AST, Record->getDeclContext(), SourceLocation(),
DeclarationName(Record->getIdentifier()), ParamList, Record);
Record->setDescribedClassTemplate(Template);
Template->setImplicit(true);
Template->setLexicalDeclContext(Record->getDeclContext());
Record->getDeclContext()->addDecl(Template);
// Requesting the class name specialization will fault in required types.
QualType T = Template->getInjectedClassNameSpecialization();
T = AST.getInjectedClassNameType(Record, T);
return *this;
}
BuiltinTypeDeclBuilder &
addMemberVariable(StringRef Name, QualType Type,
AccessSpecifier Access = AccessSpecifier::AS_private) {
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);
Record->addDecl(Field);
Fields[Name] = Field;
return *this;
}
BuiltinTypeDeclBuilder &
addHandleMember(AccessSpecifier Access = AccessSpecifier::AS_private) {
return addMemberVariable("h", Record->getASTContext().VoidPtrTy, Access);
}
BuiltinTypeDeclBuilder &
annotateResourceClass(HLSLResourceAttr::ResourceClass RC) {
Record->addAttr(
HLSLResourceAttr::CreateImplicit(Record->getASTContext(), RC));
return *this;
}
static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S,
StringRef Name) {
CXXScopeSpec SS;
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
DeclarationNameInfo NameInfo =
DeclarationNameInfo(DeclarationName(&II), SourceLocation());
LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
S.LookupParsedName(R, S.getCurScope(), &SS, false);
assert(R.isSingleResult() &&
"Since this is a builtin it should always resolve!");
auto *VD = cast<ValueDecl>(R.getFoundDecl());
QualType Ty = VD->getType();
return DeclRefExpr::Create(AST, NestedNameSpecifierLoc(), SourceLocation(),
VD, false, NameInfo, Ty, VK_PRValue);
}
static Expr *emitResourceClassExpr(ASTContext &AST, ResourceClass RC) {
return IntegerLiteral::Create(
AST,
llvm::APInt(AST.getIntWidth(AST.UnsignedCharTy),
static_cast<uint8_t>(RC)),
AST.UnsignedCharTy, SourceLocation());
}
BuiltinTypeDeclBuilder &addDefaultHandleConstructor(Sema &S,
ResourceClass RC) {
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);
DeclRefExpr *Fn =
lookupBuiltinFunction(AST, S, "__builtin_hlsl_create_handle");
Expr *RCExpr = emitResourceClassExpr(AST, RC);
CallExpr *Call =
CallExpr::Create(AST, Fn, {RCExpr}, AST.VoidPtrTy, VK_PRValue,
SourceLocation(), FPOptionsOverride());
CXXThisExpr *This = new (AST)
CXXThisExpr(SourceLocation(), Constructor->getThisType(), true);
MemberExpr *Handle = MemberExpr::CreateImplicit(
AST, This, true, Fields["h"], Fields["h"]->getType(), VK_LValue,
OK_Ordinary);
BinaryOperator *Assign = BinaryOperator::Create(
AST, Handle, Call, BO_Assign, Handle->getType(), VK_LValue, OK_Ordinary,
SourceLocation(), FPOptionsOverride());
Constructor->setBody(
CompoundStmt::Create(AST, {Assign}, FPOptionsOverride(),
SourceLocation(), SourceLocation()));
Constructor->setAccess(AccessSpecifier::AS_public);
Record->addDecl(Constructor);
return *this;
}
BuiltinTypeDeclBuilder &startDefinition() {
Record->startDefinition();
return *this;
}
BuiltinTypeDeclBuilder &completeDefinition() {
assert(Record->isBeingDefined() &&
"Definition must be started before completing it.");
Record->completeDefinition();
return *this;
}
TemplateParameterListBuilder addTemplateArgumentList();
};
struct TemplateParameterListBuilder {
BuiltinTypeDeclBuilder &Builder;
ASTContext &AST;
llvm::SmallVector<NamedDecl *> Params;
TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB)
: Builder(RB), AST(RB.Record->getASTContext()) {}
~TemplateParameterListBuilder() { finalizeTemplateArgs(); }
TemplateParameterListBuilder &
addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) {
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 */ false,
/* ParameterPack */ false);
if (!DefaultValue.isNull())
Decl->setDefaultArgument(AST.getTrivialTypeSourceInfo(DefaultValue));
Params.emplace_back(Decl);
return *this;
}
BuiltinTypeDeclBuilder &finalizeTemplateArgs() {
if (Params.empty())
return Builder;
auto *ParamList =
TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(),
Params, SourceLocation(), nullptr);
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());
Builder.Record->getDeclContext()->addDecl(Builder.Template);
Params.clear();
QualType T = Builder.Template->getInjectedClassNameSpecialization();
T = AST.getInjectedClassNameType(Builder.Record, T);
return Builder;
}
};
TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() {
return TemplateParameterListBuilder(*this);
}
} // namespace
HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
void HLSLExternalSemaSource::InitializeSema(Sema &S) {
SemaPtr = &S;
ASTContext &AST = SemaPtr->getASTContext();
IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier);
HLSLNamespace =
NamespaceDecl::Create(AST, AST.getTranslationUnitDecl(), false,
SourceLocation(), SourceLocation(), &HLSL, nullptr);
HLSLNamespace->setImplicit(true);
AST.getTranslationUnitDecl()->addDecl(HLSLNamespace);
defineTrivialHLSLTypes();
forwardDeclareHLSLTypes();
// 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.getTrivialTypeSourceInfo(AST.FloatTy));
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));
Expr *LiteralExpr =
IntegerLiteral::Create(AST, llvm::APInt(AST.getIntWidth(AST.IntTy), 4),
AST.IntTy, SourceLocation());
SizeParam->setDefaultArgument(LiteralExpr);
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();
ResourceDecl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Resource")
.startDefinition()
.addHandleMember(AccessSpecifier::AS_public)
.completeDefinition()
.Record;
}
void HLSLExternalSemaSource::forwardDeclareHLSLTypes() {
CXXRecordDecl *Decl;
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
.addTemplateArgumentList()
.addTypeParameter("element_type", SemaPtr->getASTContext().FloatTy)
.finalizeTemplateArgs()
.Record;
Completions.insert(std::make_pair(
Decl, std::bind(&HLSLExternalSemaSource::completeBufferType, this,
std::placeholders::_1)));
}
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();
auto It = Completions.find(Record);
if (It == Completions.end())
return;
It->second(Record);
}
void HLSLExternalSemaSource::completeBufferType(CXXRecordDecl *Record) {
BuiltinTypeDeclBuilder(Record)
.addHandleMember()
.addDefaultHandleConstructor(*SemaPtr, ResourceClass::UAV)
.annotateResourceClass(HLSLResourceAttr::UAV)
.completeDefinition();
}