| 
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