mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-25 21:11:25 +00:00
Start support for HLSL RWBuffer
Most of the change here is fleshing out the HLSLExternalSemaSource with builder implementations to build the builtin types. Eventually, I may move some of this code into tablegen or a more managable declarative file but I want to get the AST generation logic ready first. This code adds two new types into the HLSL AST, `hlsl::Resource` and `hlsl::RWBuffer`. The `Resource` type is just a wrapper around a handle identifier, and is largely unused in source. It will morph a bit over time as I work on getting the source compatability correct, but for now it is a reasonable stand-in. The `RWBuffer` type is not ready for use. I'm posting this change for review because it adds a lot of infrastructure code and is testable. There is one change to clang code outside the HLSL-specific logic here, which addresses a behavior change introduced a long time ago in 967d438439ac. That change resulted in unintentionally breaking situations where an incomplete template declaration was provided from an AST source, and needed to be completed later by the external AST. That situation doesn't happen in the normal AST importer flow, but can happen when an AST source provides incomplete declarations of templates. The solution is to annotate template specializations of incomplete types with the HasExternalLexicalSource bit from the base template. Depends on D128012. Differential Revision: https://reviews.llvm.org/D128569
This commit is contained in:
parent
bd08f413c0
commit
6e56d0dbe3
@ -12,6 +12,8 @@
|
||||
#ifndef CLANG_SEMA_HLSLEXTERNALSEMASOURCE_H
|
||||
#define CLANG_SEMA_HLSLEXTERNALSEMASOURCE_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
#include "clang/Sema/ExternalSemaSource.h"
|
||||
|
||||
namespace clang {
|
||||
@ -21,8 +23,16 @@ class Sema;
|
||||
class HLSLExternalSemaSource : public ExternalSemaSource {
|
||||
Sema *SemaPtr = nullptr;
|
||||
NamespaceDecl *HLSLNamespace;
|
||||
CXXRecordDecl *ResourceDecl;
|
||||
|
||||
using CompletionFunction = std::function<void(CXXRecordDecl *)>;
|
||||
llvm::DenseMap<CXXRecordDecl *, CompletionFunction> Completions;
|
||||
|
||||
void defineHLSLVectorAlias();
|
||||
void defineTrivialHLSLTypes();
|
||||
void forwardDeclareHLSLTypes();
|
||||
|
||||
void completeBufferType(CXXRecordDecl *Record);
|
||||
|
||||
public:
|
||||
~HLSLExternalSemaSource() override;
|
||||
@ -34,6 +44,9 @@ public:
|
||||
|
||||
/// Inform the semantic consumer that Sema is no longer available.
|
||||
void ForgetSema() override { SemaPtr = nullptr; }
|
||||
|
||||
/// Complete an incomplete HLSL builtin type
|
||||
void CompleteType(TagDecl *Tag) override;
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
@ -930,6 +930,14 @@ ClassTemplateSpecializationDecl::Create(ASTContext &Context, TagKind TK,
|
||||
SpecializedTemplate, Args, PrevDecl);
|
||||
Result->setMayHaveOutOfDateDef(false);
|
||||
|
||||
// If the template decl is incomplete, copy the external lexical storage from
|
||||
// the base template. This allows instantiations of incomplete types to
|
||||
// complete using the external AST if the template's declaration came from an
|
||||
// external AST.
|
||||
if (!SpecializedTemplate->getTemplatedDecl()->isCompleteDefinition())
|
||||
Result->setHasExternalLexicalStorage(
|
||||
SpecializedTemplate->getTemplatedDecl()->hasExternalLexicalStorage());
|
||||
|
||||
Context.getTypeDeclType(Result, PrevDecl);
|
||||
return Result;
|
||||
}
|
||||
|
@ -15,8 +15,161 @@
|
||||
#include "clang/Basic/AttrKinds.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
struct TemplateParameterListBuilder;
|
||||
|
||||
struct BuiltinTypeDeclBuilder {
|
||||
CXXRecordDecl *Record = nullptr;
|
||||
ClassTemplateDecl *Template = nullptr;
|
||||
NamespaceDecl *HLSLNamespace = nullptr;
|
||||
|
||||
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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BuiltinTypeDeclBuilder &
|
||||
addHandleMember(AccessSpecifier Access = AccessSpecifier::AS_private) {
|
||||
return addMemberVariable("h", Record->getASTContext().VoidPtrTy, Access);
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -28,7 +181,8 @@ void HLSLExternalSemaSource::InitializeSema(Sema &S) {
|
||||
SourceLocation(), SourceLocation(), &HLSL, nullptr);
|
||||
HLSLNamespace->setImplicit(true);
|
||||
AST.getTranslationUnitDecl()->addDecl(HLSLNamespace);
|
||||
defineHLSLVectorAlias();
|
||||
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
|
||||
@ -94,3 +248,44 @@ void HLSLExternalSemaSource::defineHLSLVectorAlias() {
|
||||
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().completeDefinition();
|
||||
}
|
||||
|
47
clang/test/AST/HLSL/RWBuffer-AST.hlsl
Normal file
47
clang/test/AST/HLSL/RWBuffer-AST.hlsl
Normal file
@ -0,0 +1,47 @@
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -fsyntax-only -ast-dump -DEMPTY %s | FileCheck -check-prefix=EMPTY %s
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -fsyntax-only -ast-dump %s | FileCheck %s
|
||||
|
||||
|
||||
// This test tests two different AST generations. The "EMPTY" test mode verifies
|
||||
// the AST generated by forward declaration of the HLSL types which happens on
|
||||
// initializing the HLSL external AST with an AST Context.
|
||||
|
||||
// The non-empty mode has a use that requires the RWBuffer type be complete,
|
||||
// which results in the AST being populated by the external AST source. That
|
||||
// case covers the full implementation of the template declaration and the
|
||||
// instantiated specialization.
|
||||
|
||||
// EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWBuffer
|
||||
// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
|
||||
// EMPTY-NEXT: TemplateArgument type 'float'
|
||||
// EMPTY-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'float'
|
||||
// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class RWBuffer
|
||||
// EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
|
||||
|
||||
// There should be no more occurrances of RWBuffer
|
||||
// EMPTY-NOT: RWBuffer
|
||||
|
||||
#ifndef EMPTY
|
||||
|
||||
RWBuffer<float> Buffer;
|
||||
|
||||
#endif
|
||||
|
||||
// CHECK: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class Resource definition
|
||||
// CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
|
||||
// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit h 'void *'
|
||||
|
||||
// CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit RWBuffer
|
||||
// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class depth 0 index 0 element_type
|
||||
// CHECK-NEXT: TemplateArgument type 'float'
|
||||
// CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'float'
|
||||
// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class RWBuffer definition
|
||||
|
||||
// CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
|
||||
// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit h 'void *'
|
||||
// CHECK: ClassTemplateSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class RWBuffer definition
|
||||
|
||||
// CHECK: TemplateArgument type 'float'
|
||||
// CHECK-NEXT: BuiltinType 0x{{[0-9A-Fa-f]+}} 'float'
|
||||
// CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
|
||||
// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit h 'void *'
|
14
clang/test/AST/HLSL/ResourceStruct.hlsl
Normal file
14
clang/test/AST/HLSL/ResourceStruct.hlsl
Normal file
@ -0,0 +1,14 @@
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -fsyntax-only -ast-dump %s | FileCheck %s
|
||||
|
||||
// CHECK: NamespaceDecl {{.*}} implicit hlsl
|
||||
// CHECK: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class Resource definition
|
||||
// CHECK-NEXT: DefinitionData
|
||||
// CHECK-NEXT: DefaultConstructor exists trivial needs_implicit
|
||||
// CHECK-NEXT: CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
|
||||
// CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit
|
||||
// CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
|
||||
// CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit
|
||||
// CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit
|
||||
// CHECK-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
|
||||
// CHECK-NEXT: FieldDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
|
||||
// implicit h 'void *'
|
12
clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
Normal file
12
clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
Normal file
@ -0,0 +1,12 @@
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -fsyntax-only -verify %s
|
||||
|
||||
Resource ResourceDescriptorHeap[5];
|
||||
typedef vector<float, 3> float3;
|
||||
|
||||
RWBuffer<float3> Buffer;
|
||||
|
||||
[numthreads(1,1,1)]
|
||||
void main() {
|
||||
(void)Buffer.h; // expected-error {{'h' is a private member of 'hlsl::RWBuffer<float __attribute__((ext_vector_type(3)))>'}}
|
||||
// expected-note@* {{implicitly declared private here}}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user