|
15 | 15 | #include "clang/AST/Decl.h"
|
16 | 16 | #include "clang/AST/DeclBase.h"
|
17 | 17 | #include "clang/AST/DeclCXX.h"
|
| 18 | +#include "clang/AST/DeclarationName.h" |
18 | 19 | #include "clang/AST/DynamicRecursiveASTVisitor.h"
|
19 | 20 | #include "clang/AST/Expr.h"
|
20 | 21 | #include "clang/AST/Type.h"
|
21 | 22 | #include "clang/AST/TypeLoc.h"
|
22 | 23 | #include "clang/Basic/Builtins.h"
|
23 | 24 | #include "clang/Basic/DiagnosticSema.h"
|
| 25 | +#include "clang/Basic/IdentifierTable.h" |
24 | 26 | #include "clang/Basic/LLVM.h"
|
25 | 27 | #include "clang/Basic/SourceLocation.h"
|
26 | 28 | #include "clang/Basic/TargetInfo.h"
|
|
32 | 34 | #include "llvm/ADT/SmallVector.h"
|
33 | 35 | #include "llvm/ADT/StringExtras.h"
|
34 | 36 | #include "llvm/ADT/StringRef.h"
|
| 37 | +#include "llvm/ADT/Twine.h" |
35 | 38 | #include "llvm/Support/Casting.h"
|
36 | 39 | #include "llvm/Support/DXILABI.h"
|
37 | 40 | #include "llvm/Support/ErrorHandling.h"
|
38 | 41 | #include "llvm/TargetParser/Triple.h"
|
| 42 | +#include <cstddef> |
39 | 43 | #include <iterator>
|
40 | 44 | #include <utility>
|
41 | 45 |
|
42 | 46 | using namespace clang;
|
43 | 47 | using RegisterType = HLSLResourceBindingAttr::RegisterType;
|
44 | 48 |
|
| 49 | +static CXXRecordDecl *createHostLayoutStruct(Sema &S, |
| 50 | + CXXRecordDecl *StructDecl); |
| 51 | + |
45 | 52 | static RegisterType getRegisterType(ResourceClass RC) {
|
46 | 53 | switch (RC) {
|
47 | 54 | case ResourceClass::SRV:
|
@@ -253,12 +260,244 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) {
|
253 | 260 | }
|
254 | 261 | }
|
255 | 262 |
|
| 263 | +// Returns true if the array has a zero size = if any of the dimensions is 0 |
| 264 | +static bool isZeroSizedArray(const ConstantArrayType *CAT) { |
| 265 | + while (CAT && !CAT->isZeroSize()) |
| 266 | + CAT = dyn_cast<ConstantArrayType>( |
| 267 | + CAT->getElementType()->getUnqualifiedDesugaredType()); |
| 268 | + return CAT != nullptr; |
| 269 | +} |
| 270 | + |
| 271 | +// Returns true if the record type is an HLSL resource class |
| 272 | +static bool isResourceRecordType(const Type *Ty) { |
| 273 | + return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; |
| 274 | +} |
| 275 | + |
| 276 | +// Returns true if the type is a leaf element type that is not valid to be |
| 277 | +// included in HLSL Buffer, such as a resource class, empty struct, zero-sized |
| 278 | +// array, or a builtin intangible type. Returns false it is a valid leaf element |
| 279 | +// type or if it is a record type that needs to be inspected further. |
| 280 | +static bool isInvalidConstantBufferLeafElementType(const Type *Ty) { |
| 281 | + if (Ty->isRecordType()) { |
| 282 | + if (isResourceRecordType(Ty) || Ty->getAsCXXRecordDecl()->isEmpty()) |
| 283 | + return true; |
| 284 | + return false; |
| 285 | + } |
| 286 | + if (Ty->isConstantArrayType() && |
| 287 | + isZeroSizedArray(cast<ConstantArrayType>(Ty))) |
| 288 | + return true; |
| 289 | + if (Ty->isHLSLBuiltinIntangibleType()) |
| 290 | + return true; |
| 291 | + return false; |
| 292 | +} |
| 293 | + |
| 294 | +// Returns true if the struct contains at least one element that prevents it |
| 295 | +// from being included inside HLSL Buffer as is, such as an intangible type, |
| 296 | +// empty struct, or zero-sized array. If it does, a new implicit layout struct |
| 297 | +// needs to be created for HLSL Buffer use that will exclude these unwanted |
| 298 | +// declarations (see createHostLayoutStruct function). |
| 299 | +static bool requiresImplicitBufferLayoutStructure(const CXXRecordDecl *RD) { |
| 300 | + if (RD->getTypeForDecl()->isHLSLIntangibleType() || RD->isEmpty()) |
| 301 | + return true; |
| 302 | + // check fields |
| 303 | + for (const FieldDecl *Field : RD->fields()) { |
| 304 | + QualType Ty = Field->getType(); |
| 305 | + if (isInvalidConstantBufferLeafElementType(Ty.getTypePtr())) |
| 306 | + return true; |
| 307 | + if (Ty->isRecordType() && |
| 308 | + requiresImplicitBufferLayoutStructure(Ty->getAsCXXRecordDecl())) |
| 309 | + return true; |
| 310 | + } |
| 311 | + // check bases |
| 312 | + for (const CXXBaseSpecifier &Base : RD->bases()) |
| 313 | + if (requiresImplicitBufferLayoutStructure( |
| 314 | + Base.getType()->getAsCXXRecordDecl())) |
| 315 | + return true; |
| 316 | + return false; |
| 317 | +} |
| 318 | + |
| 319 | +static CXXRecordDecl *findRecordDeclInContext(IdentifierInfo *II, |
| 320 | + DeclContext *DC) { |
| 321 | + CXXRecordDecl *RD = nullptr; |
| 322 | + for (NamedDecl *Decl : |
| 323 | + DC->getNonTransparentContext()->lookup(DeclarationName(II))) { |
| 324 | + if (CXXRecordDecl *FoundRD = dyn_cast<CXXRecordDecl>(Decl)) { |
| 325 | + assert(RD == nullptr && |
| 326 | + "there should be at most 1 record by a given name in a scope"); |
| 327 | + RD = FoundRD; |
| 328 | + } |
| 329 | + } |
| 330 | + return RD; |
| 331 | +} |
| 332 | + |
| 333 | +// Creates a name for buffer layout struct using the provide name base. |
| 334 | +// If the name must be unique (not previously defined), a suffix is added |
| 335 | +// until a unique name is found. |
| 336 | +static IdentifierInfo *getHostLayoutStructName(Sema &S, NamedDecl *BaseDecl, |
| 337 | + bool MustBeUnique) { |
| 338 | + ASTContext &AST = S.getASTContext(); |
| 339 | + |
| 340 | + IdentifierInfo *NameBaseII = BaseDecl->getIdentifier(); |
| 341 | + llvm::SmallString<64> Name("__layout_"); |
| 342 | + if (NameBaseII) { |
| 343 | + Name.append(NameBaseII->getName()); |
| 344 | + } else { |
| 345 | + // anonymous struct |
| 346 | + Name.append("anon"); |
| 347 | + MustBeUnique = true; |
| 348 | + } |
| 349 | + |
| 350 | + size_t NameLength = Name.size(); |
| 351 | + IdentifierInfo *II = &AST.Idents.get(Name, tok::TokenKind::identifier); |
| 352 | + if (!MustBeUnique) |
| 353 | + return II; |
| 354 | + |
| 355 | + unsigned suffix = 0; |
| 356 | + while (true) { |
| 357 | + if (suffix != 0) { |
| 358 | + Name.append("_"); |
| 359 | + Name.append(llvm::Twine(suffix).str()); |
| 360 | + II = &AST.Idents.get(Name, tok::TokenKind::identifier); |
| 361 | + } |
| 362 | + if (!findRecordDeclInContext(II, BaseDecl->getDeclContext())) |
| 363 | + return II; |
| 364 | + // declaration with that name already exists - increment suffix and try |
| 365 | + // again until unique name is found |
| 366 | + suffix++; |
| 367 | + Name.truncate(NameLength); |
| 368 | + }; |
| 369 | +} |
| 370 | + |
| 371 | +// Creates a field declaration of given name and type for HLSL buffer layout |
| 372 | +// struct. Returns nullptr if the type cannot be use in HLSL Buffer layout. |
| 373 | +static FieldDecl *createFieldForHostLayoutStruct(Sema &S, const Type *Ty, |
| 374 | + IdentifierInfo *II, |
| 375 | + CXXRecordDecl *LayoutStruct) { |
| 376 | + if (isInvalidConstantBufferLeafElementType(Ty)) |
| 377 | + return nullptr; |
| 378 | + |
| 379 | + if (Ty->isRecordType()) { |
| 380 | + CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); |
| 381 | + if (requiresImplicitBufferLayoutStructure(RD)) { |
| 382 | + RD = createHostLayoutStruct(S, RD); |
| 383 | + if (!RD) |
| 384 | + return nullptr; |
| 385 | + Ty = RD->getTypeForDecl(); |
| 386 | + } |
| 387 | + } |
| 388 | + |
| 389 | + QualType QT = QualType(Ty, 0); |
| 390 | + ASTContext &AST = S.getASTContext(); |
| 391 | + TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(QT, SourceLocation()); |
| 392 | + auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(), |
| 393 | + SourceLocation(), II, QT, TSI, nullptr, false, |
| 394 | + InClassInitStyle::ICIS_NoInit); |
| 395 | + Field->setAccess(AccessSpecifier::AS_private); |
| 396 | + return Field; |
| 397 | +} |
| 398 | + |
| 399 | +// Creates host layout struct for a struct included in HLSL Buffer. |
| 400 | +// The layout struct will include only fields that are allowed in HLSL buffer. |
| 401 | +// These fields will be filtered out: |
| 402 | +// - resource classes |
| 403 | +// - empty structs |
| 404 | +// - zero-sized arrays |
| 405 | +// Returns nullptr if the resulting layout struct would be empty. |
| 406 | +static CXXRecordDecl *createHostLayoutStruct(Sema &S, |
| 407 | + CXXRecordDecl *StructDecl) { |
| 408 | + assert(requiresImplicitBufferLayoutStructure(StructDecl) && |
| 409 | + "struct is already HLSL buffer compatible"); |
| 410 | + |
| 411 | + ASTContext &AST = S.getASTContext(); |
| 412 | + DeclContext *DC = StructDecl->getDeclContext(); |
| 413 | + IdentifierInfo *II = getHostLayoutStructName(S, StructDecl, false); |
| 414 | + |
| 415 | + // reuse existing if the layout struct if it already exists |
| 416 | + if (CXXRecordDecl *RD = findRecordDeclInContext(II, DC)) |
| 417 | + return RD; |
| 418 | + |
| 419 | + CXXRecordDecl *LS = CXXRecordDecl::Create( |
| 420 | + AST, TagDecl::TagKind::Class, DC, SourceLocation(), SourceLocation(), II); |
| 421 | + LS->setImplicit(true); |
| 422 | + LS->startDefinition(); |
| 423 | + |
| 424 | + // copy base struct, create HLSL Buffer compatible version if needed |
| 425 | + if (unsigned NumBases = StructDecl->getNumBases()) { |
| 426 | + assert(NumBases == 1 && "HLSL supports only one base type"); |
| 427 | + CXXBaseSpecifier Base = *StructDecl->bases_begin(); |
| 428 | + CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); |
| 429 | + if (requiresImplicitBufferLayoutStructure(BaseDecl)) { |
| 430 | + BaseDecl = createHostLayoutStruct(S, BaseDecl); |
| 431 | + if (BaseDecl) { |
| 432 | + TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo( |
| 433 | + QualType(BaseDecl->getTypeForDecl(), 0)); |
| 434 | + Base = CXXBaseSpecifier(SourceRange(), false, StructDecl->isClass(), |
| 435 | + AS_none, TSI, SourceLocation()); |
| 436 | + } |
| 437 | + } |
| 438 | + if (BaseDecl) { |
| 439 | + const CXXBaseSpecifier *BasesArray[1] = {&Base}; |
| 440 | + LS->setBases(BasesArray, 1); |
| 441 | + } |
| 442 | + } |
| 443 | + |
| 444 | + // filter struct fields |
| 445 | + for (const FieldDecl *FD : StructDecl->fields()) { |
| 446 | + const Type *Ty = FD->getType()->getUnqualifiedDesugaredType(); |
| 447 | + if (FieldDecl *NewFD = |
| 448 | + createFieldForHostLayoutStruct(S, Ty, FD->getIdentifier(), LS)) |
| 449 | + LS->addDecl(NewFD); |
| 450 | + } |
| 451 | + LS->completeDefinition(); |
| 452 | + |
| 453 | + if (LS->field_empty() && LS->getNumBases() == 0) |
| 454 | + return nullptr; |
| 455 | + |
| 456 | + DC->addDecl(LS); |
| 457 | + return LS; |
| 458 | +} |
| 459 | + |
| 460 | +// Creates host layout struct for HLSL Buffer. The struct will include only |
| 461 | +// fields of types that are allowed in HLSL buffer and it will filter out: |
| 462 | +// - static variable declarations |
| 463 | +// - resource classes |
| 464 | +// - empty structs |
| 465 | +// - zero-sized arrays |
| 466 | +// - non-variable declarations |
| 467 | +// The layour struct will be added to the HLSLBufferDecl declarations. |
| 468 | +void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { |
| 469 | + ASTContext &AST = S.getASTContext(); |
| 470 | + IdentifierInfo *II = getHostLayoutStructName(S, BufDecl, true); |
| 471 | + |
| 472 | + CXXRecordDecl *LS = |
| 473 | + CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, BufDecl, |
| 474 | + SourceLocation(), SourceLocation(), II); |
| 475 | + LS->setImplicit(true); |
| 476 | + LS->startDefinition(); |
| 477 | + |
| 478 | + for (const Decl *D : BufDecl->decls()) { |
| 479 | + const VarDecl *VD = dyn_cast<VarDecl>(D); |
| 480 | + if (!VD || VD->getStorageClass() == SC_Static) |
| 481 | + continue; |
| 482 | + const Type *Ty = VD->getType()->getUnqualifiedDesugaredType(); |
| 483 | + if (FieldDecl *FD = |
| 484 | + createFieldForHostLayoutStruct(S, Ty, VD->getIdentifier(), LS)) |
| 485 | + LS->addDecl(FD); |
| 486 | + } |
| 487 | + LS->completeDefinition(); |
| 488 | + BufDecl->addDecl(LS); |
| 489 | +} |
| 490 | + |
| 491 | +// Handle end of cbuffer/tbuffer declaration |
256 | 492 | void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) {
|
257 | 493 | auto *BufDecl = cast<HLSLBufferDecl>(Dcl);
|
258 | 494 | BufDecl->setRBraceLoc(RBrace);
|
259 | 495 |
|
260 | 496 | validatePackoffset(SemaRef, BufDecl);
|
261 | 497 |
|
| 498 | + // create buffer layout struct |
| 499 | + createHostLayoutStructForBuffer(SemaRef, BufDecl); |
| 500 | + |
262 | 501 | SemaRef.PopDeclContext();
|
263 | 502 | }
|
264 | 503 |
|
|
0 commit comments