blob: 61998e5653a005b70206be74bff2779b378b289c [file] [edit]
//===--- 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 "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/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaHLSL.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
using namespace clang;
using namespace llvm::hlsl;
using clang::hlsl::BuiltinTypeDeclBuilder;
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();
defineHLSLAtomicIntrinsics();
// 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()));
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::defineHLSLMatrixAlias() {
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);
// these should be 64 bit to be consistent with other clang matrices.
auto *RowsParam = NonTypeTemplateParmDecl::Create(
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1,
&AST.Idents.get("rows_count", tok::TokenKind::identifier), AST.IntTy,
false, AST.getTrivialTypeSourceInfo(AST.IntTy));
llvm::APInt RVal(AST.getIntWidth(AST.IntTy), 4);
TemplateArgument RDefault(AST, llvm::APSInt(std::move(RVal)), AST.IntTy,
/*IsDefaulted=*/true);
RowsParam->setDefaultArgument(
AST, SemaPtr->getTrivialTemplateArgumentLoc(RDefault, AST.IntTy,
SourceLocation()));
TemplateParams.emplace_back(RowsParam);
auto *ColsParam = NonTypeTemplateParmDecl::Create(
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 2,
&AST.Idents.get("cols_count", tok::TokenKind::identifier), AST.IntTy,
false, AST.getTrivialTypeSourceInfo(AST.IntTy));
llvm::APInt CVal(AST.getIntWidth(AST.IntTy), 4);
TemplateArgument CDefault(AST, llvm::APSInt(std::move(CVal)), AST.IntTy,
/*IsDefaulted=*/true);
ColsParam->setDefaultArgument(
AST, SemaPtr->getTrivialTemplateArgumentLoc(CDefault, AST.IntTy,
SourceLocation()));
TemplateParams.emplace_back(ColsParam);
const unsigned MaxMatDim = SemaPtr->getLangOpts().MaxMatrixDimension;
auto *MaxRow = IntegerLiteral::Create(
AST, llvm::APInt(AST.getIntWidth(AST.IntTy), MaxMatDim), AST.IntTy,
SourceLocation());
auto *MaxCol = IntegerLiteral::Create(
AST, llvm::APInt(AST.getIntWidth(AST.IntTy), MaxMatDim), AST.IntTy,
SourceLocation());
auto *RowsRef = DeclRefExpr::Create(
AST, NestedNameSpecifierLoc(), SourceLocation(), RowsParam,
/*RefersToEnclosingVariableOrCapture*/ false,
DeclarationNameInfo(RowsParam->getDeclName(), SourceLocation()),
AST.IntTy, VK_LValue);
auto *ColsRef = DeclRefExpr::Create(
AST, NestedNameSpecifierLoc(), SourceLocation(), ColsParam,
/*RefersToEnclosingVariableOrCapture*/ false,
DeclarationNameInfo(ColsParam->getDeclName(), SourceLocation()),
AST.IntTy, VK_LValue);
auto *RowsLE = BinaryOperator::Create(AST, RowsRef, MaxRow, BO_LE, AST.BoolTy,
VK_PRValue, OK_Ordinary,
SourceLocation(), FPOptionsOverride());
auto *ColsLE = BinaryOperator::Create(AST, ColsRef, MaxCol, BO_LE, AST.BoolTy,
VK_PRValue, OK_Ordinary,
SourceLocation(), FPOptionsOverride());
auto *RequiresExpr = BinaryOperator::Create(
AST, RowsLE, ColsLE, BO_LAnd, AST.BoolTy, VK_PRValue, OK_Ordinary,
SourceLocation(), FPOptionsOverride());
auto *ParamList = TemplateParameterList::Create(
AST, SourceLocation(), SourceLocation(), TemplateParams, SourceLocation(),
RequiresExpr);
IdentifierInfo &II = AST.Idents.get("matrix", tok::TokenKind::identifier);
QualType AliasType = AST.getDependentSizedMatrixType(
AST.getTemplateTypeParmType(0, 0, false, TypeParam),
DeclRefExpr::Create(
AST, NestedNameSpecifierLoc(), SourceLocation(), RowsParam, false,
DeclarationNameInfo(RowsParam->getDeclName(), SourceLocation()),
AST.IntTy, VK_LValue),
DeclRefExpr::Create(
AST, NestedNameSpecifierLoc(), SourceLocation(), ColsParam, false,
DeclarationNameInfo(ColsParam->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();
defineHLSLMatrixAlias();
}
/// Set up common members and attributes for buffer types
static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
ResourceClass RC, bool IsROV,
bool RawBuffer, bool HasCounter) {
return BuiltinTypeDeclBuilder(S, Decl)
.addBufferHandles(RC, IsROV, RawBuffer, HasCounter)
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
.addStaticInitializationFunctions(HasCounter);
}
/// Set up common members and attributes for sampler types
static BuiltinTypeDeclBuilder setupSamplerType(CXXRecordDecl *Decl, Sema &S) {
return BuiltinTypeDeclBuilder(S, Decl)
.addSamplerHandle()
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
.addStaticInitializationFunctions(false);
}
/// Set up common members and attributes for texture types
static BuiltinTypeDeclBuilder setupTextureType(CXXRecordDecl *Decl, Sema &S,
ResourceClass RC, bool IsROV,
bool IsArray,
ResourceDimension Dim) {
return BuiltinTypeDeclBuilder(S, Decl)
.addTextureHandle(RC, IsROV, IsArray, Dim)
.addTextureLoadMethods(Dim, IsArray)
.addArraySubscriptOperators(Dim, IsArray)
.addMipsMember(Dim)
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
.addStaticInitializationFunctions(false)
.addSampleMethods(Dim, IsArray)
.addSampleBiasMethods(Dim, IsArray)
.addSampleGradMethods(Dim, IsArray)
.addSampleLevelMethods(Dim, IsArray)
.addSampleCmpMethods(Dim, IsArray)
.addSampleCmpLevelZeroMethods(Dim, IsArray)
.addCalculateLodMethods(Dim)
.addGetDimensionsMethods(Dim)
.addGatherMethods(Dim, IsArray)
.addGatherCmpMethods(Dim, IsArray);
}
/// Set up RWTexture type: UAV texture with only operator[] (uint2, read/write),
/// Load and GetDimensions (no sample/gather/mips/LOD).
static BuiltinTypeDeclBuilder setupRWTextureType(CXXRecordDecl *Decl, Sema &S,
bool IsArray,
ResourceDimension Dim) {
return BuiltinTypeDeclBuilder(S, Decl)
.addTextureHandle(ResourceClass::UAV, /*IsROV=*/false, IsArray, Dim)
.addTextureLoadMethods(Dim, IsArray)
.addArraySubscriptOperators(Dim, IsArray)
.addGetDimensionsMethods(Dim)
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
.addStaticInitializationFunctions(false);
}
// Add a partial specialization for a template. The `TextureTemplate` is
// `Texture<element_type>`, and it will be specialized for vectors:
// `Texture<vector<element_type, element_count>>`.
static ClassTemplatePartialSpecializationDecl *
addVectorTexturePartialSpecialization(Sema &S, NamespaceDecl *HLSLNamespace,
ClassTemplateDecl *TextureTemplate) {
ASTContext &AST = S.getASTContext();
// Create the template parameters: element_type and element_count.
auto *ElementType = TemplateTypeParmDecl::Create(
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0,
&AST.Idents.get("element_type"), false, false);
auto *ElementCount = NonTypeTemplateParmDecl::Create(
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1,
&AST.Idents.get("element_count"), AST.IntTy, false,
AST.getTrivialTypeSourceInfo(AST.IntTy));
auto *TemplateParams = TemplateParameterList::Create(
AST, SourceLocation(), SourceLocation(), {ElementType, ElementCount},
SourceLocation(), nullptr);
// Create the dependent vector type: vector<element_type, element_count>.
QualType VectorType = AST.getDependentSizedExtVectorType(
AST.getTemplateTypeParmType(0, 0, false, ElementType),
DeclRefExpr::Create(
AST, NestedNameSpecifierLoc(), SourceLocation(), ElementCount, false,
DeclarationNameInfo(ElementCount->getDeclName(), SourceLocation()),
AST.IntTy, VK_LValue),
SourceLocation());
// Create the partial specialization declaration.
QualType CanonInjectedTST =
AST.getCanonicalType(AST.getTemplateSpecializationType(
ElaboratedTypeKeyword::Class, TemplateName(TextureTemplate),
{TemplateArgument(VectorType)}, {}));
auto *PartialSpec = ClassTemplatePartialSpecializationDecl::Create(
AST, TagDecl::TagKind::Class, HLSLNamespace, SourceLocation(),
SourceLocation(), TemplateParams, TextureTemplate,
{TemplateArgument(VectorType)},
CanQualType::CreateUnsafe(CanonInjectedTST), nullptr);
// Set the template arguments as written.
TemplateArgument Arg(VectorType);
TemplateArgumentLoc ArgLoc =
S.getTrivialTemplateArgumentLoc(Arg, QualType(), SourceLocation());
TemplateArgumentListInfo ArgsInfo =
TemplateArgumentListInfo(SourceLocation(), SourceLocation());
ArgsInfo.addArgument(ArgLoc);
PartialSpec->setTemplateArgsAsWritten(
ASTTemplateArgumentListInfo::Create(AST, ArgsInfo));
PartialSpec->setImplicit(true);
PartialSpec->setLexicalDeclContext(HLSLNamespace);
PartialSpec->setHasExternalLexicalStorage();
// Add the partial specialization to the namespace and the class template.
HLSLNamespace->addDecl(PartialSpec);
TextureTemplate->AddPartialSpecialization(PartialSpec, nullptr);
return PartialSpec;
}
// This function is responsible for constructing the constraint expression for
// this concept:
// template<typename T> concept is_typed_resource_element_compatible =
// __is_typed_resource_element_compatible<T>;
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;
}
// This function is responsible for constructing the constraint expression for
// this concept:
// template<typename T> concept is_constant_buffer_element_compatible =
// std::is_class_v<T> && !__is_intangible(T);
static Expr *constructConstantBufferConstraintExpr(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 *ResExpr = TypeTraitExpr::Create(
Context, BoolTy, NameLoc, UTT_IsConstantBufferElementCompatible,
{TTypeSourceInfo}, NameLoc, true);
return ResExpr;
}
// This function is responsible for constructing the constraint expression for
// this concept:
// template<typename T> concept is_structured_resource_element_compatible =
// !__is_intangible<T> && sizeof(T) >= 1;
static Expr *constructStructuredBufferConstraintExpr(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 *IsIntangibleExpr =
TypeTraitExpr::Create(Context, BoolTy, NameLoc, UTT_IsIntangibleType,
{TTypeSourceInfo}, NameLoc, true);
// negate IsIntangibleExpr
UnaryOperator *NotIntangibleExpr = UnaryOperator::Create(
Context, IsIntangibleExpr, UO_LNot, BoolTy, VK_LValue, OK_Ordinary,
NameLoc, false, FPOptionsOverride());
// element types also may not be of 0 size
UnaryExprOrTypeTraitExpr *SizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
UETT_SizeOf, TTypeSourceInfo, BoolTy, NameLoc, NameLoc);
// Create a BinaryOperator that checks if the size of the type is not equal to
// 1 Empty structs have a size of 1 in HLSL, so we need to check for that
IntegerLiteral *rhs = IntegerLiteral::Create(
Context, llvm::APInt(Context.getTypeSize(Context.getSizeType()), 1, true),
Context.getSizeType(), NameLoc);
BinaryOperator *SizeGEQOneExpr =
BinaryOperator::Create(Context, SizeOfExpr, rhs, BO_GE, BoolTy, VK_LValue,
OK_Ordinary, NameLoc, FPOptionsOverride());
// Combine the two constraints
BinaryOperator *CombinedExpr = BinaryOperator::Create(
Context, NotIntangibleExpr, SizeGEQOneExpr, BO_LAnd, BoolTy, VK_LValue,
OK_Ordinary, NameLoc, FPOptionsOverride());
return CombinedExpr;
}
enum class HLSLBufferType { Typed, Structured, Constant };
static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD,
HLSLBufferType BT) {
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;
Expr *ConstraintExpr = nullptr;
switch (BT) {
case HLSLBufferType::Typed:
DeclName = DeclarationName(
&Context.Idents.get("__is_typed_resource_element_compatible"));
ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
break;
case HLSLBufferType::Structured:
DeclName = DeclarationName(
&Context.Idents.get("__is_structured_resource_element_compatible"));
ConstraintExpr = constructStructuredBufferConstraintExpr(S, DeclLoc, T);
break;
case HLSLBufferType::Constant:
DeclName = DeclarationName(
&Context.Idents.get("__is_constant_buffer_element_compatible"));
ConstraintExpr = constructConstantBufferConstraintExpr(S, DeclLoc, T);
break;
}
// 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() {
ASTContext &AST = SemaPtr->getASTContext();
CXXRecordDecl *Decl;
ConceptDecl *TypedBufferConcept = constructBufferConceptDecl(
*SemaPtr, HLSLNamespace, HLSLBufferType::Typed);
ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl(
*SemaPtr, HLSLNamespace, HLSLBufferType::Structured);
ConceptDecl *ConstantBufferConcept = constructBufferConceptDecl(
*SemaPtr, HLSLNamespace, HLSLBufferType::Constant);
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConstantBuffer")
.addSimpleTemplateParams({"element_type"}, ConstantBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::CBuffer, /*IsROV=*/false,
/*RawBuffer=*/false, /*HasCounter=*/false)
.addConstantBufferConversionToType()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Buffer")
.addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
/*RawBuffer=*/false, /*HasCounter=*/false)
.addArraySubscriptOperators()
.addLoadMethods()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
.addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false,
/*RawBuffer=*/false, /*HasCounter=*/false)
.addArraySubscriptOperators()
.addLoadMethods()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/true,
/*RawBuffer=*/false, /*HasCounter=*/false)
.addArraySubscriptOperators()
.addLoadMethods()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer")
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
/*RawBuffer=*/true, /*HasCounter=*/false)
.addArraySubscriptOperators()
.addLoadMethods()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer")
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false,
/*RawBuffer=*/true, /*HasCounter=*/true)
.addArraySubscriptOperators()
.addLoadMethods()
.addIncrementCounterMethod()
.addDecrementCounterMethod()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer")
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false,
/*RawBuffer=*/true, /*HasCounter=*/true)
.addAppendMethod()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer")
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false,
/*RawBuffer=*/true, /*HasCounter=*/true)
.addConsumeMethod()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
"RasterizerOrderedStructuredBuffer")
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/true,
/*RawBuffer=*/true, /*HasCounter=*/true)
.addArraySubscriptOperators()
.addLoadMethods()
.addIncrementCounterMethod()
.addDecrementCounterMethod()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ByteAddressBuffer")
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
/*RawBuffer=*/true, /*HasCounter=*/false)
.addByteAddressBufferLoadMethods()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWByteAddressBuffer")
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false,
/*RawBuffer=*/true, /*HasCounter=*/false)
.addByteAddressBufferLoadMethods()
.addByteAddressBufferStoreMethods()
.addByteAddressBufferInterlockedMethods()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
"RasterizerOrderedByteAddressBuffer")
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/true,
/*RawBuffer=*/true, /*HasCounter=*/false)
.addByteAddressBufferInterlockedMethods()
.addGetDimensionsMethodForBuffer()
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "SamplerState")
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupSamplerType(Decl, *SemaPtr).completeDefinition();
});
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "SamplerComparisonState")
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupSamplerType(Decl, *SemaPtr).completeDefinition();
});
QualType Float4Ty = AST.getExtVectorType(AST.FloatTy, 4);
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2D")
.addSimpleTemplateParams({"element_type"}, {Float4Ty},
TypedBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
/*IsArray=*/false, ResourceDimension::Dim2D)
.completeDefinition();
});
auto *PartialSpec = addVectorTexturePartialSpecialization(
*SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
onCompletion(PartialSpec, [this](CXXRecordDecl *Decl) {
setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
/*IsArray=*/false, ResourceDimension::Dim2D)
.completeDefinition();
});
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWTexture2D")
.addSimpleTemplateParams({"element_type"}, {Float4Ty},
TypedBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/false,
ResourceDimension::Dim2D)
.completeDefinition();
});
auto *PartialSpecRW = addVectorTexturePartialSpecialization(
*SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
onCompletion(PartialSpecRW, [this](CXXRecordDecl *Decl) {
setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/false,
ResourceDimension::Dim2D)
.completeDefinition();
});
// Texture2DArray — same as Texture2D but IsArray=true
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2DArray")
.addSimpleTemplateParams({"element_type"}, {Float4Ty},
TypedBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
/*IsArray=*/true, ResourceDimension::Dim2D)
.completeDefinition();
});
auto *PartialSpec2DA = addVectorTexturePartialSpecialization(
*SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
onCompletion(PartialSpec2DA, [this](CXXRecordDecl *Decl) {
setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
/*IsArray=*/true, ResourceDimension::Dim2D)
.completeDefinition();
});
// RWTexture2DArray — same as RWTexture2D but IsArray=true
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWTexture2DArray")
.addSimpleTemplateParams({"element_type"}, {Float4Ty},
TypedBufferConcept)
.finalizeForwardDeclaration();
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/true,
ResourceDimension::Dim2D)
.completeDefinition();
});
auto *PartialSpecRW2DA = addVectorTexturePartialSpecialization(
*SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
onCompletion(PartialSpecRW2DA, [this](CXXRecordDecl *Decl) {
setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/true,
ResourceDimension::Dim2D)
.completeDefinition();
});
}
// Build a single overload of an HLSL atomic intrinsic in the hlsl namespace.
// `dest` is an address-space-qualified reference; `original_value` (when
// present) is a plain reference. The synthesized FunctionDecl aliases the
// underlying clang builtin via BuiltinAliasAttr.
static void buildAtomicOverload(Sema &S, NamespaceDecl *NS, StringRef FuncName,
StringRef BuiltinName, QualType ElemTy,
LangAS DestAS, bool ThreeArg) {
ASTContext &AST = S.getASTContext();
QualType DestTy =
AST.getLValueReferenceType(AST.getAddrSpaceQualType(ElemTy, DestAS));
QualType OrigRefTy = AST.getLValueReferenceType(ElemTy);
SmallVector<QualType, 3> ParamTypes;
ParamTypes.push_back(DestTy);
ParamTypes.push_back(ElemTy);
if (ThreeArg)
ParamTypes.push_back(OrigRefTy);
FunctionProtoType::ExtProtoInfo EPI;
QualType FuncTy = AST.getFunctionType(AST.VoidTy, ParamTypes, EPI);
auto *TSInfo = AST.getTrivialTypeSourceInfo(FuncTy, SourceLocation());
IdentifierInfo &FuncII = AST.Idents.get(FuncName, tok::TokenKind::identifier);
DeclarationName FuncDeclName(&FuncII);
FunctionDecl *FD = FunctionDecl::Create(
AST, NS, SourceLocation(), SourceLocation(), FuncDeclName, FuncTy, TSInfo,
SC_Extern, /*UsesFPIntrin=*/false, /*isInlineSpecified=*/false,
/*hasWrittenPrototype=*/true);
constexpr const char *ParamNames[] = {"dest", "value", "original_value"};
SmallVector<ParmVarDecl *, 3> ParmDecls;
unsigned I = 0;
for (auto [ParamType, ParamName] : llvm::zip(ParamTypes, ParamNames)) {
IdentifierInfo &PII = AST.Idents.get(ParamName, tok::TokenKind::identifier);
ParmVarDecl *Parm = ParmVarDecl::Create(
AST, FD, SourceLocation(), SourceLocation(), &PII, ParamType,
AST.getTrivialTypeSourceInfo(ParamType, SourceLocation()), SC_None,
nullptr);
Parm->setScopeInfo(0, I++);
ParmDecls.push_back(Parm);
}
FD->setParams(ParmDecls);
IdentifierInfo &BuiltinII =
S.getPreprocessor().getIdentifierTable().get(BuiltinName);
FD->addAttr(BuiltinAliasAttr::CreateImplicit(AST, &BuiltinII));
FD->setImplicit();
NS->addDecl(FD);
}
// Synthesize the InterlockedFunc overload set: {int, uint, int64_t, uint64_t}
// x {groupshared, device} x {2-arg, 3-arg}.
static void defineHLSLInterlockedFunc(Sema &S, NamespaceDecl *NS,
StringRef FuncName,
StringRef BuiltinName) {
ASTContext &AST = S.getASTContext();
// HLSL: int64_t == long, uint64_t == unsigned long (see hlsl_basic_types.h).
QualType Elems[] = {AST.IntTy, AST.UnsignedIntTy, AST.LongTy,
AST.UnsignedLongTy};
LangAS AddrSpaces[] = {LangAS::hlsl_groupshared, LangAS::hlsl_device};
for (QualType ElemTy : Elems)
for (LangAS AS : AddrSpaces)
for (bool ThreeArg : {false, true})
buildAtomicOverload(S, NS, FuncName, BuiltinName, ElemTy, AS, ThreeArg);
}
void HLSLExternalSemaSource::defineHLSLAtomicIntrinsics() {
defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedAdd",
"__builtin_hlsl_interlocked_add");
defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedOr",
"__builtin_hlsl_interlocked_or");
defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedXor",
"__builtin_hlsl_interlocked_xor");
}
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)) {
if (!isa<ClassTemplatePartialSpecializationDecl>(TDecl)) {
ClassTemplateDecl *Template = TDecl->getSpecializedTemplate();
llvm::SmallVector<ClassTemplatePartialSpecializationDecl *, 4> Partials;
Template->getPartialSpecializations(Partials);
ClassTemplatePartialSpecializationDecl *MatchedPartial = nullptr;
for (auto *Partial : Partials) {
sema::TemplateDeductionInfo Info(TDecl->getLocation());
if (SemaPtr->DeduceTemplateArguments(Partial, TDecl->getTemplateArgs(),
Info) ==
TemplateDeductionResult::Success) {
MatchedPartial = Partial;
break;
}
}
if (MatchedPartial)
Record = MatchedPartial;
else
Record = Template->getTemplatedDecl();
}
}
Record = Record->getCanonicalDecl();
auto It = Completions.find(Record);
if (It == Completions.end())
return;
// Move out the callback and erase before invoking it: the callback can
// re-enter CompleteType and mutate Completions, which invalidates It under
// backward-shift deletion.
CompletionFunction Fn = std::move(It->second);
Completions.erase(It);
Fn(Record);
}