Path: blob/main/contrib/llvm-project/clang/lib/Sema/HLSLExternalSemaSource.cpp
35233 views
//===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8//9//===----------------------------------------------------------------------===//1011#include "clang/Sema/HLSLExternalSemaSource.h"12#include "clang/AST/ASTContext.h"13#include "clang/AST/Attr.h"14#include "clang/AST/DeclCXX.h"15#include "clang/Basic/AttrKinds.h"16#include "clang/Basic/HLSLRuntime.h"17#include "clang/Sema/Lookup.h"18#include "clang/Sema/Sema.h"19#include "llvm/Frontend/HLSL/HLSLResource.h"2021#include <functional>2223using namespace clang;24using namespace llvm::hlsl;2526namespace {2728struct TemplateParameterListBuilder;2930struct BuiltinTypeDeclBuilder {31CXXRecordDecl *Record = nullptr;32ClassTemplateDecl *Template = nullptr;33ClassTemplateDecl *PrevTemplate = nullptr;34NamespaceDecl *HLSLNamespace = nullptr;35llvm::StringMap<FieldDecl *> Fields;3637BuiltinTypeDeclBuilder(CXXRecordDecl *R) : Record(R) {38Record->startDefinition();39Template = Record->getDescribedClassTemplate();40}4142BuiltinTypeDeclBuilder(Sema &S, NamespaceDecl *Namespace, StringRef Name)43: HLSLNamespace(Namespace) {44ASTContext &AST = S.getASTContext();45IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);4647LookupResult Result(S, &II, SourceLocation(), Sema::LookupTagName);48CXXRecordDecl *PrevDecl = nullptr;49if (S.LookupQualifiedName(Result, HLSLNamespace)) {50NamedDecl *Found = Result.getFoundDecl();51if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) {52PrevDecl = TD->getTemplatedDecl();53PrevTemplate = TD;54} else55PrevDecl = dyn_cast<CXXRecordDecl>(Found);56assert(PrevDecl && "Unexpected lookup result type.");57}5859if (PrevDecl && PrevDecl->isCompleteDefinition()) {60Record = PrevDecl;61return;62}6364Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,65SourceLocation(), SourceLocation(), &II,66PrevDecl, true);67Record->setImplicit(true);68Record->setLexicalDeclContext(HLSLNamespace);69Record->setHasExternalLexicalStorage();7071// Don't let anyone derive from built-in types.72Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(),73FinalAttr::Keyword_final));74}7576~BuiltinTypeDeclBuilder() {77if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)78HLSLNamespace->addDecl(Record);79}8081BuiltinTypeDeclBuilder &82addMemberVariable(StringRef Name, QualType Type,83AccessSpecifier Access = AccessSpecifier::AS_private) {84if (Record->isCompleteDefinition())85return *this;86assert(Record->isBeingDefined() &&87"Definition must be started before adding members!");88ASTContext &AST = Record->getASTContext();8990IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);91TypeSourceInfo *MemTySource =92AST.getTrivialTypeSourceInfo(Type, SourceLocation());93auto *Field = FieldDecl::Create(94AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,95nullptr, false, InClassInitStyle::ICIS_NoInit);96Field->setAccess(Access);97Field->setImplicit(true);98Record->addDecl(Field);99Fields[Name] = Field;100return *this;101}102103BuiltinTypeDeclBuilder &104addHandleMember(AccessSpecifier Access = AccessSpecifier::AS_private) {105if (Record->isCompleteDefinition())106return *this;107QualType Ty = Record->getASTContext().VoidPtrTy;108if (Template) {109if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(110Template->getTemplateParameters()->getParam(0)))111Ty = Record->getASTContext().getPointerType(112QualType(TTD->getTypeForDecl(), 0));113}114return addMemberVariable("h", Ty, Access);115}116117BuiltinTypeDeclBuilder &annotateHLSLResource(ResourceClass RC,118ResourceKind RK, bool IsROV) {119if (Record->isCompleteDefinition())120return *this;121Record->addAttr(122HLSLResourceClassAttr::CreateImplicit(Record->getASTContext(), RC));123Record->addAttr(124HLSLResourceAttr::CreateImplicit(Record->getASTContext(), RK, IsROV));125return *this;126}127128static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S,129StringRef Name) {130IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);131DeclarationNameInfo NameInfo =132DeclarationNameInfo(DeclarationName(&II), SourceLocation());133LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);134// AllowBuiltinCreation is false but LookupDirect will create135// the builtin when searching the global scope anyways...136S.LookupName(R, S.getCurScope());137// FIXME: If the builtin function was user-declared in global scope,138// this assert *will* fail. Should this call LookupBuiltin instead?139assert(R.isSingleResult() &&140"Since this is a builtin it should always resolve!");141auto *VD = cast<ValueDecl>(R.getFoundDecl());142QualType Ty = VD->getType();143return DeclRefExpr::Create(AST, NestedNameSpecifierLoc(), SourceLocation(),144VD, false, NameInfo, Ty, VK_PRValue);145}146147static Expr *emitResourceClassExpr(ASTContext &AST, ResourceClass RC) {148return IntegerLiteral::Create(149AST,150llvm::APInt(AST.getIntWidth(AST.UnsignedCharTy),151static_cast<uint8_t>(RC)),152AST.UnsignedCharTy, SourceLocation());153}154155BuiltinTypeDeclBuilder &addDefaultHandleConstructor(Sema &S,156ResourceClass RC) {157if (Record->isCompleteDefinition())158return *this;159ASTContext &AST = Record->getASTContext();160161QualType ConstructorType =162AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo());163164CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified();165DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy);166CXXConstructorDecl *Constructor = CXXConstructorDecl::Create(167AST, Record, SourceLocation(),168DeclarationNameInfo(Name, SourceLocation()), ConstructorType,169AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()),170ExplicitSpecifier(), false, true, false,171ConstexprSpecKind::Unspecified);172173DeclRefExpr *Fn =174lookupBuiltinFunction(AST, S, "__builtin_hlsl_create_handle");175Expr *RCExpr = emitResourceClassExpr(AST, RC);176Expr *Call = CallExpr::Create(AST, Fn, {RCExpr}, AST.VoidPtrTy, VK_PRValue,177SourceLocation(), FPOptionsOverride());178179CXXThisExpr *This = CXXThisExpr::Create(180AST, SourceLocation(), Constructor->getFunctionObjectParameterType(),181true);182Expr *Handle = MemberExpr::CreateImplicit(AST, This, false, Fields["h"],183Fields["h"]->getType(), VK_LValue,184OK_Ordinary);185186// If the handle isn't a void pointer, cast the builtin result to the187// correct type.188if (Handle->getType().getCanonicalType() != AST.VoidPtrTy) {189Call = CXXStaticCastExpr::Create(190AST, Handle->getType(), VK_PRValue, CK_Dependent, Call, nullptr,191AST.getTrivialTypeSourceInfo(Handle->getType(), SourceLocation()),192FPOptionsOverride(), SourceLocation(), SourceLocation(),193SourceRange());194}195196BinaryOperator *Assign = BinaryOperator::Create(197AST, Handle, Call, BO_Assign, Handle->getType(), VK_LValue, OK_Ordinary,198SourceLocation(), FPOptionsOverride());199200Constructor->setBody(201CompoundStmt::Create(AST, {Assign}, FPOptionsOverride(),202SourceLocation(), SourceLocation()));203Constructor->setAccess(AccessSpecifier::AS_public);204Record->addDecl(Constructor);205return *this;206}207208BuiltinTypeDeclBuilder &addArraySubscriptOperators() {209if (Record->isCompleteDefinition())210return *this;211addArraySubscriptOperator(true);212addArraySubscriptOperator(false);213return *this;214}215216BuiltinTypeDeclBuilder &addArraySubscriptOperator(bool IsConst) {217if (Record->isCompleteDefinition())218return *this;219assert(Fields.count("h") > 0 &&220"Subscript operator must be added after the handle.");221222FieldDecl *Handle = Fields["h"];223ASTContext &AST = Record->getASTContext();224225assert(Handle->getType().getCanonicalType() != AST.VoidPtrTy &&226"Not yet supported for void pointer handles.");227228QualType ElemTy =229QualType(Handle->getType()->getPointeeOrArrayElementType(), 0);230QualType ReturnTy = ElemTy;231232FunctionProtoType::ExtProtoInfo ExtInfo;233234// Subscript operators return references to elements, const makes the235// reference and method const so that the underlying data is not mutable.236ReturnTy = AST.getLValueReferenceType(ReturnTy);237if (IsConst) {238ExtInfo.TypeQuals.addConst();239ReturnTy.addConst();240}241242QualType MethodTy =243AST.getFunctionType(ReturnTy, {AST.UnsignedIntTy}, ExtInfo);244auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation());245auto *MethodDecl = CXXMethodDecl::Create(246AST, Record, SourceLocation(),247DeclarationNameInfo(248AST.DeclarationNames.getCXXOperatorName(OO_Subscript),249SourceLocation()),250MethodTy, TSInfo, SC_None, false, false, ConstexprSpecKind::Unspecified,251SourceLocation());252253IdentifierInfo &II = AST.Idents.get("Idx", tok::TokenKind::identifier);254auto *IdxParam = ParmVarDecl::Create(255AST, MethodDecl->getDeclContext(), SourceLocation(), SourceLocation(),256&II, AST.UnsignedIntTy,257AST.getTrivialTypeSourceInfo(AST.UnsignedIntTy, SourceLocation()),258SC_None, nullptr);259MethodDecl->setParams({IdxParam});260261// Also add the parameter to the function prototype.262auto FnProtoLoc = TSInfo->getTypeLoc().getAs<FunctionProtoTypeLoc>();263FnProtoLoc.setParam(0, IdxParam);264265auto *This =266CXXThisExpr::Create(AST, SourceLocation(),267MethodDecl->getFunctionObjectParameterType(), true);268auto *HandleAccess = MemberExpr::CreateImplicit(269AST, This, false, Handle, Handle->getType(), VK_LValue, OK_Ordinary);270271auto *IndexExpr = DeclRefExpr::Create(272AST, NestedNameSpecifierLoc(), SourceLocation(), IdxParam, false,273DeclarationNameInfo(IdxParam->getDeclName(), SourceLocation()),274AST.UnsignedIntTy, VK_PRValue);275276auto *Array =277new (AST) ArraySubscriptExpr(HandleAccess, IndexExpr, ElemTy, VK_LValue,278OK_Ordinary, SourceLocation());279280auto *Return = ReturnStmt::Create(AST, SourceLocation(), Array, nullptr);281282MethodDecl->setBody(CompoundStmt::Create(AST, {Return}, FPOptionsOverride(),283SourceLocation(),284SourceLocation()));285MethodDecl->setLexicalDeclContext(Record);286MethodDecl->setAccess(AccessSpecifier::AS_public);287MethodDecl->addAttr(AlwaysInlineAttr::CreateImplicit(288AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));289Record->addDecl(MethodDecl);290291return *this;292}293294BuiltinTypeDeclBuilder &startDefinition() {295if (Record->isCompleteDefinition())296return *this;297Record->startDefinition();298return *this;299}300301BuiltinTypeDeclBuilder &completeDefinition() {302if (Record->isCompleteDefinition())303return *this;304assert(Record->isBeingDefined() &&305"Definition must be started before completing it.");306307Record->completeDefinition();308return *this;309}310311TemplateParameterListBuilder addTemplateArgumentList(Sema &S);312BuiltinTypeDeclBuilder &addSimpleTemplateParams(Sema &S,313ArrayRef<StringRef> Names);314};315316struct TemplateParameterListBuilder {317BuiltinTypeDeclBuilder &Builder;318Sema &S;319llvm::SmallVector<NamedDecl *> Params;320321TemplateParameterListBuilder(Sema &S, BuiltinTypeDeclBuilder &RB)322: Builder(RB), S(S) {}323324~TemplateParameterListBuilder() { finalizeTemplateArgs(); }325326TemplateParameterListBuilder &327addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) {328if (Builder.Record->isCompleteDefinition())329return *this;330unsigned Position = static_cast<unsigned>(Params.size());331auto *Decl = TemplateTypeParmDecl::Create(332S.Context, Builder.Record->getDeclContext(), SourceLocation(),333SourceLocation(), /* TemplateDepth */ 0, Position,334&S.Context.Idents.get(Name, tok::TokenKind::identifier),335/* Typename */ false,336/* ParameterPack */ false);337if (!DefaultValue.isNull())338Decl->setDefaultArgument(339S.Context, S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(),340SourceLocation()));341342Params.emplace_back(Decl);343return *this;344}345346BuiltinTypeDeclBuilder &finalizeTemplateArgs() {347if (Params.empty())348return Builder;349auto *ParamList = TemplateParameterList::Create(S.Context, SourceLocation(),350SourceLocation(), Params,351SourceLocation(), nullptr);352Builder.Template = ClassTemplateDecl::Create(353S.Context, Builder.Record->getDeclContext(), SourceLocation(),354DeclarationName(Builder.Record->getIdentifier()), ParamList,355Builder.Record);356Builder.Record->setDescribedClassTemplate(Builder.Template);357Builder.Template->setImplicit(true);358Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());359// NOTE: setPreviousDecl before addDecl so new decl replace old decl when360// make visible.361Builder.Template->setPreviousDecl(Builder.PrevTemplate);362Builder.Record->getDeclContext()->addDecl(Builder.Template);363Params.clear();364365QualType T = Builder.Template->getInjectedClassNameSpecialization();366T = S.Context.getInjectedClassNameType(Builder.Record, T);367368return Builder;369}370};371} // namespace372373TemplateParameterListBuilder374BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) {375return TemplateParameterListBuilder(S, *this);376}377378BuiltinTypeDeclBuilder &379BuiltinTypeDeclBuilder::addSimpleTemplateParams(Sema &S,380ArrayRef<StringRef> Names) {381TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S);382for (StringRef Name : Names)383Builder.addTypeParameter(Name);384return Builder.finalizeTemplateArgs();385}386387HLSLExternalSemaSource::~HLSLExternalSemaSource() {}388389void HLSLExternalSemaSource::InitializeSema(Sema &S) {390SemaPtr = &S;391ASTContext &AST = SemaPtr->getASTContext();392// If the translation unit has external storage force external decls to load.393if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage())394(void)AST.getTranslationUnitDecl()->decls_begin();395396IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier);397LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName);398NamespaceDecl *PrevDecl = nullptr;399if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl()))400PrevDecl = Result.getAsSingle<NamespaceDecl>();401HLSLNamespace = NamespaceDecl::Create(402AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(),403SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false);404HLSLNamespace->setImplicit(true);405HLSLNamespace->setHasExternalLexicalStorage();406AST.getTranslationUnitDecl()->addDecl(HLSLNamespace);407408// Force external decls in the HLSL namespace to load from the PCH.409(void)HLSLNamespace->getCanonicalDecl()->decls_begin();410defineTrivialHLSLTypes();411defineHLSLTypesWithForwardDeclarations();412413// This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's414// built in types inside a namespace, but we are planning to change that in415// the near future. In order to be source compatible older versions of HLSL416// will need to implicitly use the hlsl namespace. For now in clang everything417// will get added to the namespace, and we can remove the using directive for418// future language versions to match HLSL's evolution.419auto *UsingDecl = UsingDirectiveDecl::Create(420AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),421NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace,422AST.getTranslationUnitDecl());423424AST.getTranslationUnitDecl()->addDecl(UsingDecl);425}426427void HLSLExternalSemaSource::defineHLSLVectorAlias() {428ASTContext &AST = SemaPtr->getASTContext();429430llvm::SmallVector<NamedDecl *> TemplateParams;431432auto *TypeParam = TemplateTypeParmDecl::Create(433AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0,434&AST.Idents.get("element", tok::TokenKind::identifier), false, false);435TypeParam->setDefaultArgument(436AST, SemaPtr->getTrivialTemplateArgumentLoc(437TemplateArgument(AST.FloatTy), QualType(), SourceLocation()));438439TemplateParams.emplace_back(TypeParam);440441auto *SizeParam = NonTypeTemplateParmDecl::Create(442AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1,443&AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy,444false, AST.getTrivialTypeSourceInfo(AST.IntTy));445llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4);446TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy,447/*IsDefaulted=*/true);448SizeParam->setDefaultArgument(449AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy,450SourceLocation(), SizeParam));451TemplateParams.emplace_back(SizeParam);452453auto *ParamList =454TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(),455TemplateParams, SourceLocation(), nullptr);456457IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier);458459QualType AliasType = AST.getDependentSizedExtVectorType(460AST.getTemplateTypeParmType(0, 0, false, TypeParam),461DeclRefExpr::Create(462AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false,463DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()),464AST.IntTy, VK_LValue),465SourceLocation());466467auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(),468SourceLocation(), &II,469AST.getTrivialTypeSourceInfo(AliasType));470Record->setImplicit(true);471472auto *Template =473TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(),474Record->getIdentifier(), ParamList, Record);475476Record->setDescribedAliasTemplate(Template);477Template->setImplicit(true);478Template->setLexicalDeclContext(Record->getDeclContext());479HLSLNamespace->addDecl(Template);480}481482void HLSLExternalSemaSource::defineTrivialHLSLTypes() {483defineHLSLVectorAlias();484}485486/// Set up common members and attributes for buffer types487static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,488ResourceClass RC, ResourceKind RK,489bool IsROV) {490return BuiltinTypeDeclBuilder(Decl)491.addHandleMember()492.addDefaultHandleConstructor(S, RC)493.annotateHLSLResource(RC, RK, IsROV);494}495496void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {497CXXRecordDecl *Decl;498Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")499.addSimpleTemplateParams(*SemaPtr, {"element_type"})500.Record;501onCompletion(Decl, [this](CXXRecordDecl *Decl) {502setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,503ResourceKind::TypedBuffer, /*IsROV=*/false)504.addArraySubscriptOperators()505.completeDefinition();506});507508Decl =509BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")510.addSimpleTemplateParams(*SemaPtr, {"element_type"})511.Record;512onCompletion(Decl, [this](CXXRecordDecl *Decl) {513setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,514ResourceKind::TypedBuffer, /*IsROV=*/true)515.addArraySubscriptOperators()516.completeDefinition();517});518}519520void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record,521CompletionFunction Fn) {522Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn));523}524525void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {526if (!isa<CXXRecordDecl>(Tag))527return;528auto Record = cast<CXXRecordDecl>(Tag);529530// If this is a specialization, we need to get the underlying templated531// declaration and complete that.532if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(Record))533Record = TDecl->getSpecializedTemplate()->getTemplatedDecl();534Record = Record->getCanonicalDecl();535auto It = Completions.find(Record);536if (It == Completions.end())537return;538It->second(Record);539}540541542