From 63c465b40512ad4c5fff84c0b2b022b49d46eaa7 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 28 Jan 2025 13:20:30 -0800 Subject: [PATCH 01/19] [HLSL] Constant buffer layout struct update - create structs with public fields instead of classes with private fields - add Packed attribute to prevent struct padding - use __cblayout_ prefix in name - filter out arrays of resources (bug fix) - don't create implicit initializer for constant buffer decls - update tests --- clang/lib/Sema/SemaDecl.cpp | 7 + clang/lib/Sema/SemaHLSL.cpp | 29 ++-- .../AST/HLSL/ast-dump-comment-cbuffer.hlsl | 3 - clang/test/AST/HLSL/cbuffer.hlsl | 144 ++++++++++-------- .../test/AST/HLSL/cbuffer_and_namespaces.hlsl | 30 ++-- clang/test/AST/HLSL/pch_hlsl_buffer.hlsl | 4 +- 6 files changed, 121 insertions(+), 96 deletions(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fe68eadc951b5..ba424567b23e0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14183,6 +14183,13 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { if (getLangOpts().OpenCL && Var->getType().getAddressSpace() == LangAS::opencl_local) return; + + // In HLSL, objects in the hlsl_constat address space are initialized + // externaly, so don't synthesize an implicit initializer. + if (getLangOpts().HLSL && + Var->getType().getAddressSpace() == LangAS::hlsl_constant) + return; + // C++03 [dcl.init]p9: // If no initializer is specified for an object, and the // object is of (possibly cv-qualified) non-POD class type (or diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index aa99b44958eaf..97df01a5dd307 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -269,8 +269,11 @@ static bool isZeroSizedArray(const ConstantArrayType *CAT) { return CAT != nullptr; } -// Returns true if the record type is an HLSL resource class -static bool isResourceRecordType(const Type *Ty) { +// Returns true if the record type is an HLSL resource class or an array of +// HLSL resource classes +static bool isResourceRecordTypeOrArrayOf(const Type *Ty) { + while (const ConstantArrayType *CAT = dyn_cast(Ty)) + Ty = CAT->getArrayElementTypeNoTypeQual(); return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; } @@ -279,11 +282,10 @@ static bool isResourceRecordType(const Type *Ty) { // array, or a builtin intangible type. Returns false it is a valid leaf element // type or if it is a record type that needs to be inspected further. static bool isInvalidConstantBufferLeafElementType(const Type *Ty) { - if (Ty->isRecordType()) { - if (isResourceRecordType(Ty) || Ty->getAsCXXRecordDecl()->isEmpty()) - return true; - return false; - } + if (isResourceRecordTypeOrArrayOf(Ty)) + return true; + if (Ty->isRecordType()) + return Ty->getAsCXXRecordDecl()->isEmpty(); if (Ty->isConstantArrayType() && isZeroSizedArray(cast(Ty))) return true; @@ -339,7 +341,7 @@ static IdentifierInfo *getHostLayoutStructName(Sema &S, NamedDecl *BaseDecl, ASTContext &AST = S.getASTContext(); IdentifierInfo *NameBaseII = BaseDecl->getIdentifier(); - llvm::SmallString<64> Name("__layout_"); + llvm::SmallString<64> Name("__cblayout_"); if (NameBaseII) { Name.append(NameBaseII->getName()); } else { @@ -393,7 +395,7 @@ static FieldDecl *createFieldForHostLayoutStruct(Sema &S, const Type *Ty, auto *Field = FieldDecl::Create(AST, LayoutStruct, SourceLocation(), SourceLocation(), II, QT, TSI, nullptr, false, InClassInitStyle::ICIS_NoInit); - Field->setAccess(AccessSpecifier::AS_private); + Field->setAccess(AccessSpecifier::AS_public); return Field; } @@ -417,9 +419,11 @@ static CXXRecordDecl *createHostLayoutStruct(Sema &S, if (CXXRecordDecl *RD = findRecordDeclInContext(II, DC)) return RD; - CXXRecordDecl *LS = CXXRecordDecl::Create( - AST, TagDecl::TagKind::Class, DC, SourceLocation(), SourceLocation(), II); + CXXRecordDecl *LS = + CXXRecordDecl::Create(AST, TagDecl::TagKind::Struct, DC, SourceLocation(), + SourceLocation(), II); LS->setImplicit(true); + LS->addAttr(PackedAttr::CreateImplicit(AST)); LS->startDefinition(); // copy base struct, create HLSL Buffer compatible version if needed @@ -472,8 +476,9 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { IdentifierInfo *II = getHostLayoutStructName(S, BufDecl, true); CXXRecordDecl *LS = - CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, BufDecl, + CXXRecordDecl::Create(AST, TagDecl::TagKind::Struct, BufDecl, SourceLocation(), SourceLocation(), II); + LS->addAttr(PackedAttr::CreateImplicit(AST)); LS->setImplicit(true); LS->startDefinition(); diff --git a/clang/test/AST/HLSL/ast-dump-comment-cbuffer.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffer.hlsl index b2b3e13308da3..eca2ed0211d1b 100644 --- a/clang/test/AST/HLSL/ast-dump-comment-cbuffer.hlsl +++ b/clang/test/AST/HLSL/ast-dump-comment-cbuffer.hlsl @@ -27,6 +27,3 @@ cbuffer A { // AST-NEXT: TextComment {{.*}} Text=" CBuffer decl." // AST-NEXT: VarDecl {{.*}} a 'hlsl_constant float' // AST-NEXT: VarDecl {{.*}} b 'hlsl_constant int' -// AST-NEXT: CXXRecordDecl {{.*}} implicit class __layout_A definition -// AST: FieldDecl {{.*}} a 'float' -// AST-NEXT: FieldDecl {{.*}} b 'int' diff --git a/clang/test/AST/HLSL/cbuffer.hlsl b/clang/test/AST/HLSL/cbuffer.hlsl index f516cf5099e82..8254f0ee00b14 100644 --- a/clang/test/AST/HLSL/cbuffer.hlsl +++ b/clang/test/AST/HLSL/cbuffer.hlsl @@ -48,75 +48,83 @@ struct TwoFloats { // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { - // CHECK: VarDecl {{.*}} col:9 used a1 'hlsl_constant float' + // CHECK: VarDecl {{.*}} used a1 'hlsl_constant float' float a1; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB definition - // CHECK: FieldDecl {{.*}} a1 'float' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} a1 'float' } -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_CB), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_CB), ""); // Check that buffer layout struct does not include resources or empty types -// CHECK: HLSLBufferDecl {{.*}} line:62:9 cbuffer CB +// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { - // CHECK: VarDecl {{.*}} col:9 used a2 'hlsl_constant float' + // CHECK: VarDecl {{.*}} used a2 'hlsl_constant float' float a2; - // CHECK: VarDecl {{.*}} col:19 b2 'RWBuffer':'hlsl::RWBuffer' + // CHECK: VarDecl {{.*}} b2 'RWBuffer':'hlsl::RWBuffer' RWBuffer b2; - // CHECK: VarDecl {{.*}} col:15 c2 'EmptyStruct' + // CHECK: VarDecl {{.*}} c2 'EmptyStruct' EmptyStruct c2; - // CHECK: VarDecl {{.*}} col:9 d2 'float[0]' + // CHECK: VarDecl {{.*}} d2 'float[0]' float d2[0]; - // CHECK: VarDecl {{.*}} col:9 e2 'hlsl_constant float' + // CHECK: VarDecl {{.*}} f2 'RWBuffer[2]' + RWBuffer f2[2]; + // CHECK: VarDecl {{.*}} e2 'hlsl_constant float' float e2; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_1 definition - // CHECK: FieldDecl {{.*}} a2 'float' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_1 definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} a2 'float' // CHECK-NEXT: FieldDecl {{.*}} e2 'float' } -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_1), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_1), ""); // Check that layout struct is created for B and the empty struct C is removed -// CHECK: HLSLBufferDecl {{.*}} line:83:9 cbuffer CB +// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { - // CHECK: VarDecl {{.*}} col:5 used s1 'hlsl_constant A' + // CHECK: VarDecl {{.*}} used s1 'hlsl_constant A' A s1; - // CHECK: VarDecl {{.*}} col:5 s2 'hlsl_constant B' + // CHECK: VarDecl {{.*}} s2 'hlsl_constant B' B s2; - // CHECK: VarDecl {{.*}} col:12 s3 'CTypedef':'C' + // CHECK: VarDecl {{.*}} s3 'CTypedef':'C' CTypedef s3; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_2 definition - // CHECK: FieldDecl {{.*}} s1 'A' - // CHECK: FieldDecl {{.*}} s2 '__layout_B' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_2 definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} s1 'A' + // CHECK-NEXT: FieldDecl {{.*}} s2 '__cblayout_B' } -// CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_B definition -// CHECK: FieldDecl {{.*}} a 'float' +// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_B definition +// CHECK: PackedAttr +// CHECK-NEXT: FieldDecl {{.*}} a 'float' -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_B), ""); -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_2), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_B), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_2), ""); // check that layout struct is created for D because of its base struct -// CHECK: HLSLBufferDecl {{.*}} line:104:9 cbuffer CB +// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { // CHECK: VarDecl {{.*}} s4 'hlsl_constant D' D s4; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_3 definition - // CHECK: FieldDecl {{.*}} s4 '__layout_D' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_3 definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} s4 '__cblayout_D' } - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_D definition - // CHECK: public '__layout_B' - // CHECK: FieldDecl {{.*}} b 'float' -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_D), ""); -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_3), ""); + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_D definition + // CHECK: public '__cblayout_B' + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} b 'float' +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_D), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_3), ""); // check that layout struct is created for E because because its base struct // is empty and should be eliminated, and BTypedef should reuse the previously -// defined '__layout_B' -// CHECK: HLSLBufferDecl {{.*}} line:122:9 cbuffer CB +// defined '__cblayout_B' +// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { @@ -124,18 +132,20 @@ cbuffer CB { E s5; // CHECK: VarDecl {{.*}} s6 'hlsl_constant BTypedef':'hlsl_constant B' BTypedef s6; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_4 definition - // CHECK: FieldDecl {{.*}} s5 '__layout_E' - // CHECK: FieldDecl {{.*}} s6 '__layout_B' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_4 definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} s5 '__cblayout_E' + // CHECK-NEXT: FieldDecl {{.*}} s6 '__cblayout_B' } - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_E definition - // CHECK: FieldDecl {{.*}} c 'float' - // CHECK-NOT: CXXRecordDecl {{.*}} class __layout_B definition -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_E), ""); -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_4), ""); +// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_E definition +// CHECK: PackedAttr +// CHECK-NEXT: FieldDecl {{.*}} c 'float' +// CHECK-NOT: CXXRecordDecl {{.*}} struct __cblayout_B definition +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_E), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_4), ""); // check that this produces empty layout struct -// CHECK: HLSLBufferDecl {{.*}} line:141:9 cbuffer CB +// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { @@ -149,27 +159,30 @@ cbuffer CB { RWBuffer Buf; // CHECK: VarDecl {{.*}} ea 'EmptyArrayTypedef':'float[10][0]' EmptyArrayTypedef ea; - // CHECK: CXXRecordDecl {{.*}} implicit class __layout_CB_5 definition + // CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_CB_5 definition + // CHECK: PackedAttr // CHECK-NOT: FieldDecl } // check host layout struct with compatible base struct -// CHECK: HLSLBufferDecl {{.*}} line:160:9 cbuffer CB +// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { // CHECK: VarDecl {{.*}} s8 'hlsl_constant F' F s8; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_6 definition - // CHECK: FieldDecl {{.*}} s8 '__layout_F' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_6 definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} s8 '__cblayout_F' } - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_F definition - // CHECK: public 'A' -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_F), ""); -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_CB_6), ""); +// CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_F definition +// CHECK: public 'A' +// CHECK: PackedAttr +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_F), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_CB_6), ""); // anonymous structs -// CHECK: HLSLBufferDecl {{.*}} line:175:9 cbuffer CB +// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB // CHECK: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK: HLSLResourceAttr {{.*}} Implicit CBuffer cbuffer CB { @@ -182,7 +195,7 @@ cbuffer CB { // CHECK: FieldDecl {{.*}} f 'RWBuffer':'hlsl::RWBuffer' RWBuffer f; } s9; - // CHECK: VarDecl {{.*}} s9 'hlsl_constant struct (unnamed struct at {{.*}}cbuffer.hlsl:177:3 + // CHECK: VarDecl {{.*}} s9 'hlsl_constant struct (unnamed struct at {{.*}}cbuffer.hlsl:[[# @LINE - 8]]:3 // CHECK: CXXRecordDecl {{.*}} struct definition struct { // CHECK: FieldDecl {{.*}} g 'float' @@ -190,18 +203,21 @@ cbuffer CB { // CHECK: FieldDecl {{.*}} f 'RWBuffer':'hlsl::RWBuffer' RWBuffer f; } s10; - // CHECK: VarDecl {{.*}} s10 'hlsl_constant struct (unnamed struct at {{.*}}cbuffer.hlsl:187:3 - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_anon definition - // CHECK: FieldDecl {{.*}} e 'float' - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_anon_1 definition - // CHECK: FieldDecl {{.*}} g 'float' - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB_7 definition - // CHECK: FieldDecl {{.*}} s9 '__layout_anon' - // CHECK: FieldDecl {{.*}} s10 '__layout_anon_1' + // CHECK: VarDecl {{.*}} s10 'hlsl_constant struct (unnamed struct at {{.*}}cbuffer.hlsl:[[# @LINE - 6]]:3 + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_anon definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} e 'float' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_anon_1 definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} g 'float' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_7 definition + // CHECK: PackedAttr + // CHECK-NEXT: FieldDecl {{.*}} s9 '__cblayout_anon' + // CHECK-NEXT: FieldDecl {{.*}} s10 '__cblayout_anon_1' } -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_anon), ""); -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __layout_anon_1), ""); -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __layout_CB_7), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_anon), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(OneFloat, __cblayout_anon_1), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(TwoFloats, __cblayout_CB_7), ""); // Add uses for the constant buffer declarations so they are not optimized away export float foo() { diff --git a/clang/test/AST/HLSL/cbuffer_and_namespaces.hlsl b/clang/test/AST/HLSL/cbuffer_and_namespaces.hlsl index 12ce327d8be02..09596eda90b6a 100644 --- a/clang/test/AST/HLSL/cbuffer_and_namespaces.hlsl +++ b/clang/test/AST/HLSL/cbuffer_and_namespaces.hlsl @@ -19,10 +19,10 @@ namespace NS1 { int b; EmptyStruct es; }; - // CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition + // CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_Foo definition // CHECK: FieldDecl {{.*}} b 'int' }; - // CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition + // CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_Foo definition // CHECK: FieldDecl {{.*}} a 'float' } @@ -41,12 +41,12 @@ cbuffer CB1 { NS1::Foo foo2; // CHECK: VarDecl {{.*}} foo3 'hlsl_constant NS1::Bar::Foo' NS1::Bar::Foo foo3; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB1 definition - // CHECK: FieldDecl {{.*}} foo1 '__layout_Foo' - // CHECK: FieldDecl {{.*}} foo2 'NS1::__layout_Foo' - // CHECK: FieldDecl {{.*}} foo3 'NS1::Bar::__layout_Foo' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB1 definition + // CHECK: FieldDecl {{.*}} foo1 '__cblayout_Foo' + // CHECK: FieldDecl {{.*}} foo2 'NS1::__cblayout_Foo' + // CHECK: FieldDecl {{.*}} foo3 'NS1::Bar::__cblayout_Foo' } -// CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition +// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_Foo definition // CHECK: FieldDecl {{.*}} c 'double' struct CB1ExpectedShape { @@ -54,7 +54,7 @@ struct CB1ExpectedShape { float a2; int a; }; -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(CB1ExpectedShape, __layout_CB1), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(CB1ExpectedShape, __cblayout_CB1), ""); namespace NS2 { struct Foo { @@ -73,13 +73,13 @@ namespace NS2 { NS1::Foo foo2; // CHECK: VarDecl {{.*}} foo3 'hlsl_constant NS1::Bar::Foo' NS1::Bar::Foo foo3; - // CHECK: CXXRecordDecl {{.*}} implicit referenced class __layout_CB2 definition - // CHECK: FieldDecl {{.*}} foo0 '__layout_Foo' - // CHECK: FieldDecl {{.*}} foo1 'NS2::__layout_Foo' - // CHECK: FieldDecl {{.*}} foo2 'NS1::__layout_Foo' - // CHECK: FieldDecl {{.*}} foo3 'NS1::Bar::__layout_Foo' + // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB2 definition + // CHECK: FieldDecl {{.*}} foo0 '__cblayout_Foo' + // CHECK: FieldDecl {{.*}} foo1 'NS2::__cblayout_Foo' + // CHECK: FieldDecl {{.*}} foo2 'NS1::__cblayout_Foo' + // CHECK: FieldDecl {{.*}} foo3 'NS1::Bar::__cblayout_Foo' } - // CHECK: CXXRecordDecl {{.*}} implicit class __layout_Foo definition + // CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_Foo definition // CHECK: FieldDecl {{.*}} d 'float[4]' } @@ -89,7 +89,7 @@ struct CB2ExpectedShape { float a2; int a; }; -_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(CB2ExpectedShape, NS2::__layout_CB2), ""); +_Static_assert(__builtin_hlsl_is_scalarized_layout_compatible(CB2ExpectedShape, NS2::__cblayout_CB2), ""); // Add uses for the constant buffer declarations so they are not optimized away // CHECK: ExportDecl diff --git a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl index 98d7aba397852..754948931ee53 100644 --- a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl +++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl @@ -21,14 +21,14 @@ float foo() { // CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer // CHECK-NEXT: HLSLResourceAttr {{.*}} Implicit CBuffer // CHECK-NEXT: VarDecl 0x[[A:[0-9a-f]+]] {{.*}} imported used a 'hlsl_constant float' -// CHECK-NEXT: CXXRecordDecl {{.*}} imported implicit class __layout_A definition +// CHECK-NEXT: CXXRecordDecl {{.*}} imported implicit struct __cblayout_A definition // CHECK: FieldDecl {{.*}} imported a 'float' // CHECK: HLSLBufferDecl {{.*}} line:11:9 imported tbuffer B // CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit SRV // CHECK-NEXT: HLSLResourceAttr {{.*}} Implicit TBuffer // CHECK-NEXT: VarDecl 0x[[B:[0-9a-f]+]] {{.*}} imported used b 'hlsl_constant float' -// CHECK-NEXT: CXXRecordDecl 0x{{[0-9a-f]+}} {{.*}} imported implicit class __layout_B definition +// CHECK-NEXT: CXXRecordDecl 0x{{[0-9a-f]+}} {{.*}} imported implicit struct __cblayout_B definition // CHECK: FieldDecl 0x{{[0-9a-f]+}} {{.*}} imported b 'float' // CHECK-NEXT: FunctionDecl {{.*}} line:15:7 imported foo 'float ()' From c80415a22bf9b1e06928e84d8d0efa646fbaa5f9 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 28 Jan 2025 16:52:45 -0800 Subject: [PATCH 02/19] filter groupshared var decls --- clang/lib/Sema/SemaHLSL.cpp | 3 ++- clang/test/AST/HLSL/cbuffer.hlsl | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 97df01a5dd307..b71dd2b273a1c 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -484,7 +484,8 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { for (Decl *D : BufDecl->decls()) { VarDecl *VD = dyn_cast(D); - if (!VD || VD->getStorageClass() == SC_Static) + if (!VD || VD->getStorageClass() == SC_Static || + VD->getType().getAddressSpace() == LangAS::hlsl_groupshared) continue; const Type *Ty = VD->getType()->getUnqualifiedDesugaredType(); if (FieldDecl *FD = diff --git a/clang/test/AST/HLSL/cbuffer.hlsl b/clang/test/AST/HLSL/cbuffer.hlsl index 8254f0ee00b14..865db1201baa5 100644 --- a/clang/test/AST/HLSL/cbuffer.hlsl +++ b/clang/test/AST/HLSL/cbuffer.hlsl @@ -71,6 +71,8 @@ cbuffer CB { float d2[0]; // CHECK: VarDecl {{.*}} f2 'RWBuffer[2]' RWBuffer f2[2]; + // CHECK: VarDecl {{.*}} g2 'groupshared float' + groupshared float g2; // CHECK: VarDecl {{.*}} e2 'hlsl_constant float' float e2; // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_1 definition From cf08adb6b9e181613e81d2cfbadbbb68e645fe33 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 28 Jan 2025 14:46:26 -0800 Subject: [PATCH 03/19] [HLSL] Translate cbuffer declarations to target type dx.CBuffer - initial commit --- clang/include/clang/AST/Decl.h | 6 + clang/include/clang/AST/Type.h | 4 +- clang/lib/AST/Decl.cpp | 17 +- clang/lib/CodeGen/CGHLSLRuntime.cpp | 409 ++++++++++++------ clang/lib/CodeGen/CGHLSLRuntime.h | 31 +- clang/lib/CodeGen/Targets/DirectX.cpp | 11 +- clang/lib/Sema/SemaHLSL.cpp | 3 + clang/test/CodeGenHLSL/cbuf.hlsl | 33 -- clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl | 29 -- clang/test/CodeGenHLSL/cbuffer.hlsl | 200 +++++++++ .../CodeGenHLSL/cbuffer_and_namespaces.hlsl | 63 +++ .../CodeGenHLSL/cbuffer_with_packoffset.hlsl | 40 ++ ...uffer_with_static_global_and_function.hlsl | 32 ++ clang/test/CodeGenHLSL/resource-bindings.hlsl | 4 + .../static_global_and_function_in_cb.hlsl | 22 - 15 files changed, 679 insertions(+), 225 deletions(-) delete mode 100644 clang/test/CodeGenHLSL/cbuf.hlsl delete mode 100644 clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl create mode 100644 clang/test/CodeGenHLSL/cbuffer.hlsl create mode 100644 clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl create mode 100644 clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl create mode 100644 clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl delete mode 100644 clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 16403774e72b3..e1c7e3817699c 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -5032,6 +5032,9 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { SourceLocation KwLoc; /// IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer). bool IsCBuffer; + /// HasValidPackoffset - Whether the buffer has valid packoffset annotations + // on all declarations + bool HasPackoffset; HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *ID, SourceLocation IDLoc, @@ -5052,6 +5055,9 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { SourceLocation getRBraceLoc() const { return RBraceLoc; } void setRBraceLoc(SourceLocation L) { RBraceLoc = L; } bool isCBuffer() const { return IsCBuffer; } + void setHasPackoffset(bool PO) { HasPackoffset = PO; } + bool hasPackoffset() const { return HasPackoffset; } + const CXXRecordDecl *getLayoutStruct() const; // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 1d9743520654e..c3ff7ebd88516 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -6266,8 +6266,8 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { LLVM_PREFERRED_TYPE(bool) uint8_t RawBuffer : 1; - Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV, - bool RawBuffer) + Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV = false, + bool RawBuffer = false) : ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer) {} Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, false, false) {} diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index beb5fcaefac53..fa7d03354a993 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1747,6 +1747,10 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, } } + // Suppress transparent contexts like export or HLSLBufferDecl context + if (Ctx->isTransparentContext()) + continue; + // Skip non-named contexts such as linkage specifications and ExportDecls. const NamedDecl *ND = dyn_cast(Ctx); if (!ND) @@ -5713,7 +5717,7 @@ HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation IDLoc, SourceLocation LBrace) : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)), DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc), - IsCBuffer(CBuffer) {} + IsCBuffer(CBuffer), HasPackoffset(false) {} HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, DeclContext *LexicalParent, bool CBuffer, @@ -5743,6 +5747,17 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, SourceLocation(), SourceLocation()); } +const CXXRecordDecl *HLSLBufferDecl::getLayoutStruct() const { + // Layout struct is the last decl in the HLSLBufferDecl. + if (CXXRecordDecl *RD = llvm::dyn_cast_or_null(LastDecl)) { + assert(RD->getName().starts_with( + ("__cblayout_" + getIdentifier()->getName()).str()) && + "expected buffer layout struct"); + return RD; + } + llvm_unreachable("HLSL buffer is missing a layout struct"); +} + //===----------------------------------------------------------------------===// // ImportDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 2ce54cc3c52ef..d3d9d42664359 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -16,15 +16,20 @@ #include "CGDebugInfo.h" #include "CodeGenModule.h" #include "TargetInfo.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/Type.h" #include "clang/Basic/TargetOptions.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/Support/Alignment.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" using namespace clang; @@ -32,6 +37,9 @@ using namespace CodeGen; using namespace clang::hlsl; using namespace llvm; +static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV, + unsigned Slot, unsigned Space); + namespace { void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { @@ -54,54 +62,11 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey); DXILValMD->addOperand(Val); } + void addDisableOptimizations(llvm::Module &M) { StringRef Key = "dx.disable_optimizations"; M.addModuleFlag(llvm::Module::ModFlagBehavior::Override, Key, 1); } -// cbuffer will be translated into global variable in special address space. -// If translate into C, -// cbuffer A { -// float a; -// float b; -// } -// float foo() { return a + b; } -// -// will be translated into -// -// struct A { -// float a; -// float b; -// } cbuffer_A __attribute__((address_space(4))); -// float foo() { return cbuffer_A.a + cbuffer_A.b; } -// -// layoutBuffer will create the struct A type. -// replaceBuffer will replace use of global variable a and b with cbuffer_A.a -// and cbuffer_A.b. -// -void layoutBuffer(CGHLSLRuntime::Buffer &Buf, const DataLayout &DL) { - if (Buf.Constants.empty()) - return; - - std::vector EltTys; - for (auto &Const : Buf.Constants) { - GlobalVariable *GV = Const.first; - Const.second = EltTys.size(); - llvm::Type *Ty = GV->getValueType(); - EltTys.emplace_back(Ty); - } - Buf.LayoutStruct = llvm::StructType::get(EltTys[0]->getContext(), EltTys); -} - -GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) { - // Create global variable for CB. - GlobalVariable *CBGV = new GlobalVariable( - Buf.LayoutStruct, /*isConstant*/ true, - GlobalValue::LinkageTypes::ExternalLinkage, nullptr, - llvm::formatv("{0}{1}", Buf.Name, Buf.IsCBuffer ? ".cb." : ".tb."), - GlobalValue::NotThreadLocal); - - return CBGV; -} } // namespace @@ -119,48 +84,280 @@ llvm::Triple::ArchType CGHLSLRuntime::getArch() { return CGM.getTarget().getTriple().getArch(); } -void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) { - if (D->getStorageClass() == SC_Static) { - // For static inside cbuffer, take as global static. - // Don't add to cbuffer. - CGM.EmitGlobal(D); - return; +// Returns true if the type is an HLSL resource class +static bool isResourceRecordType(const clang::Type *Ty) { + return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; +} + +// Returns true if the type is an HLSL resource class or an array of +// HLSL resource classes +static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) { + while (const ConstantArrayType *CAT = dyn_cast(Ty)) + Ty = CAT->getArrayElementTypeNoTypeQual(); + return isResourceRecordType(Ty); +} + +static ConstantAsMetadata *getConstIntMetadata(LLVMContext &Ctx, uint32_t value, + bool isSigned = false) { + return ConstantAsMetadata::get( + ConstantInt::get(Ctx, llvm::APInt(32, value, isSigned))); +} + +static unsigned getScalarOrVectorSize(llvm::Type *Ty) { + assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy()); + if (Ty->isVectorTy()) { + llvm::FixedVectorType *FVT = cast(Ty); + return FVT->getNumElements() * + (FVT->getElementType()->getScalarSizeInBits() / 8); + } + return Ty->getScalarSizeInBits() / 8; +} + +size_t +CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) { + assert(StructTy->isStructTy()); + + // check if we already have a side for this struct + auto SizeIt = StructSizesForBuffer.find(StructTy); + if (SizeIt != StructSizesForBuffer.end()) + return SizeIt->getSecond(); + + // if not, calculate the struct layout and add it to metadata + LLVMContext &Ctx = CGM.getLLVMContext(); + SmallVector LayoutItems; + LayoutItems.push_back(MDString::get(Ctx, StructTy->getName())); + + size_t StructSize = 0; + LayoutItems.push_back(nullptr); // reserve one slot for the buffer size + + for (llvm::Type *ElTy : StructTy->elements()) + addLayoutInfoForBufferElement(StructSize, LayoutItems, ElTy); + + // set the size of the buffer + LayoutItems[1] = getConstIntMetadata(Ctx, StructSize); + + // add the struct layout info to metadata + MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems); + CGM.getModule() + .getOrInsertNamedMetadata("hlsl.cblayouts") + ->addOperand(LayoutMDNode); + + // add struct size to list and return it + StructSizesForBuffer[StructTy] = StructSize; + return StructSize; +} + +void CGHLSLRuntime::addLayoutInfoForBufferElement( + size_t &EndOffset, SmallVector &LayoutItems, + llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr) { + + // calculate element offset and size; for arrays also calculate array + // element count and stride + size_t ElemOffset = 0; + size_t ElemSize = 0; + size_t ArrayCount = 1; + size_t ArrayStride = 0; + size_t NextRowOffset = llvm::alignTo(EndOffset, 16U); + + if (LayoutTy->isArrayTy()) { + llvm::Type *Ty = LayoutTy; + while (Ty->isArrayTy()) { + ArrayCount *= Ty->getArrayNumElements(); + Ty = Ty->getArrayElementType(); + } + ElemSize = + Ty->isStructTy() + ? getOrCalculateStructSizeForBuffer(cast(Ty)) + : getScalarOrVectorSize(Ty); + ArrayStride = llvm::alignTo(ElemSize, 16U); + ElemOffset = + PackoffsetAttr ? PackoffsetAttr->getOffsetInBytes() : NextRowOffset; + + } else if (LayoutTy->isStructTy()) { + ElemOffset = + PackoffsetAttr ? PackoffsetAttr->getOffsetInBytes() : NextRowOffset; + ElemSize = + getOrCalculateStructSizeForBuffer(cast(LayoutTy)); + + } else { + size_t Align = 0; + if (LayoutTy->isVectorTy()) { + llvm::FixedVectorType *FVT = cast(LayoutTy); + size_t SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8; + ElemSize = FVT->getNumElements() * SubElemSize; + Align = SubElemSize; + } else { + assert(LayoutTy->isIntegerTy() || LayoutTy->isFloatingPointTy()); + ElemSize = LayoutTy->getScalarSizeInBits() / 8; + Align = ElemSize; + } + if (PackoffsetAttr) { + ElemOffset = PackoffsetAttr->getOffsetInBytes(); + } else { + ElemOffset = llvm::alignTo(EndOffset, Align); + if (ElemOffset + ElemSize > NextRowOffset) + ElemOffset = NextRowOffset; + } } - auto *GV = cast(CGM.GetAddrOfGlobalVar(D)); - GV->setExternallyInitialized(true); - // Add debug info for constVal. - if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) - if (CGM.getCodeGenOpts().getDebugInfo() >= - codegenoptions::DebugInfoKind::LimitedDebugInfo) - DI->EmitGlobalVariable(cast(GV), D); - - // FIXME: support packoffset. - // See https://github.com/llvm/llvm-project/issues/57914. - uint32_t Offset = 0; - bool HasUserOffset = false; - - unsigned LowerBound = HasUserOffset ? Offset : UINT_MAX; - CB.Constants.emplace_back(std::make_pair(GV, LowerBound)); + // Update end offset of the buffer/struct layout; do not update it if + // the provided EndOffset is already bigger than the new one (which may happen + // with packoffset annotations) + unsigned NewEndOffset = + ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; + EndOffset = std::max(EndOffset, NewEndOffset); + + // create metadata constan with the offset and stride and add to list + LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), ElemOffset)); + if (ArrayStride) + LayoutItems.push_back( + getConstIntMetadata(CGM.getLLVMContext(), ArrayStride)); } -void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) { - for (Decl *it : DC->decls()) { - if (auto *ConstDecl = dyn_cast(it)) { - addConstant(ConstDecl, CB); - } else if (isa(it)) { +void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, + llvm::GlobalVariable *BufGV) { + llvm::StructType *LayoutStruct = cast( + cast(BufGV->getValueType())->getTypeParameter(0)); + + LLVMContext &Ctx = CGM.getLLVMContext(); + + SmallVector BufGlobals; + BufGlobals.push_back(ValueAsMetadata::get(BufGV)); + + SmallVector LayoutItems; + LayoutItems.push_back(MDString::get(Ctx, LayoutStruct->getName())); + + size_t BufferSize = 0; + size_t BufferSizeIndex = LayoutItems.size(); + LayoutItems.push_back(nullptr); // reserve one slot for the buffer size + + bool UsePackoffset = BufDecl->hasPackoffset(); + + const auto *ElemIt = LayoutStruct->element_begin(); + for (Decl *D : BufDecl->decls()) { + if (isa(D)) // Nothing to do for this declaration. - } else if (isa(it)) { + continue; + if (isa(D)) { // A function within an cbuffer is effectively a top-level function, // as it only refers to globally scoped declarations. - CGM.EmitTopLevelDecl(it); + CGM.EmitTopLevelDecl(D); + continue; + } + if (VarDecl *VD = dyn_cast(D)) { + QualType VDTy = VD->getType(); + if (VDTy.getAddressSpace() != LangAS::hlsl_constant) { + if (VD->getStorageClass() == SC_Static || + isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) { + // Emit static variables and resource classes inside cbuffer as + // regular globals + CGM.EmitGlobal(VD); + } + // Anything else that is not in the hlsl_constant address space must be + // an empty struct or a zero-sized array and can be ignored + continue; + } + + assert(ElemIt != LayoutStruct->element_end() && + "number of elements in layout struct does not match"); + llvm::Type *LayoutType = *ElemIt++; + + assert((CGM.getTypes().ConvertTypeForMem(VDTy) == LayoutType || + (LayoutType->isStructTy() && + cast(LayoutType) + ->getName() + .starts_with(("struct.__cblayout_" + + VDTy->getAsCXXRecordDecl()->getName()) + .str()))) && + "layout type does not match the converted element type"); + + // handle any resources declarations inside the struct + if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType()) + // FIXME: handle resources in cbuffer structs + llvm_unreachable("resources in cbuffer are not supported yet"); + + GlobalVariable *ElemGV = + cast(CGM.GetAddrOfGlobalVar(VD, LayoutType)); + BufGlobals.push_back(ValueAsMetadata::get(ElemGV)); + + assert(((UsePackoffset && VD->hasAttr()) || + !UsePackoffset) && + "expected packoffset attribute on every declaration"); + + addLayoutInfoForBufferElement( + BufferSize, LayoutItems, LayoutType, + UsePackoffset ? VD->getAttr() : nullptr); } } + assert(ElemIt == LayoutStruct->element_end() && + "number of elements in layout struct does not match"); + + // add buffer global and a list of its constants to metadata + MDNode *BufMDNode = MDNode::get(CGM.getLLVMContext(), BufGlobals); + CGM.getModule().getOrInsertNamedMetadata("hlsl.cbs")->addOperand(BufMDNode); + + // set the size of the buffer + LayoutItems[BufferSizeIndex] = getConstIntMetadata(Ctx, BufferSize); + + // add buffer layout to metadata + MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems); + CGM.getModule() + .getOrInsertNamedMetadata("hlsl.cblayouts") + ->addOperand(LayoutMDNode); } -void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *D) { - Buffers.emplace_back(Buffer(D)); - addBufferDecls(D, Buffers.back()); +// Creates resource handle type for the HLSL buffer +static const clang::HLSLAttributedResourceType * +createBufferHandleType(const HLSLBufferDecl *BufDecl) { + ASTContext &AST = BufDecl->getASTContext(); + QualType QT = AST.getHLSLAttributedResourceType( + AST.HLSLResourceTy, + QualType(BufDecl->getLayoutStruct()->getTypeForDecl(), 0), + HLSLAttributedResourceType::Attributes(ResourceClass::CBuffer)); + return cast(QT.getTypePtr()); +} + +// Creates temporary global variables for all declarations within the constant +// buffer context, creates a global variable for the constant buffer and adds +// it to the module. +// All uses of the temporary constant globals will be replaced with buffer +// access intrinsic resource.getpointer in CGHLSLRuntime::finishCodeGen. +// Later on in DXILResourceAccess pass these will be transtaled +// to dx.op.cbufferLoadLegacy instructions. +void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { + + assert(BufDecl->isCBuffer() && "tbuffer codegen is not supported yet"); + + // Create resource handle type for the buffer + const clang::HLSLAttributedResourceType *ResHandleTy = + createBufferHandleType(BufDecl); + + // ignore empty constant buffer + if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty()) + return; + + // Create global variable for the buffer + llvm::Module &M = CGM.getModule(); + llvm::TargetExtType *TargetTy = + cast(convertHLSLSpecificType(ResHandleTy)); + llvm::GlobalVariable *BufGV = + new GlobalVariable(TargetTy, /*isConstant*/ true, + GlobalValue::LinkageTypes::ExternalLinkage, nullptr, + llvm::formatv("{0}{1}", BufDecl->getName(), + BufDecl->isCBuffer() ? ".cb" : ".tb"), + GlobalValue::NotThreadLocal); + M.insertGlobalVariable(BufGV); + + // Add globals for buffer elements and create metadata node for the buffer + emitBufferGlobalsAndMetadata(BufDecl, BufGV); + + // Add cbuffer resource initialization + const HLSLResourceBindingAttr *RBA = + BufDecl->getAttr(); + // FIXME: handle implicit binding if no binding attribute is found + if (RBA) + createResourceInitFn(CGM, BufGV, RBA->getSlotNumber(), + RBA->getSpaceNumber()); } void CGHLSLRuntime::finishCodeGen() { @@ -173,28 +370,8 @@ void CGHLSLRuntime::finishCodeGen() { generateGlobalCtorDtorCalls(); if (CGM.getCodeGenOpts().OptimizationLevel == 0) addDisableOptimizations(M); - - const DataLayout &DL = M.getDataLayout(); - - for (auto &Buf : Buffers) { - layoutBuffer(Buf, DL); - GlobalVariable *GV = replaceBuffer(Buf); - M.insertGlobalVariable(GV); - llvm::hlsl::ResourceClass RC = Buf.IsCBuffer - ? llvm::hlsl::ResourceClass::CBuffer - : llvm::hlsl::ResourceClass::SRV; - llvm::hlsl::ResourceKind RK = Buf.IsCBuffer - ? llvm::hlsl::ResourceKind::CBuffer - : llvm::hlsl::ResourceKind::TBuffer; - addBufferResourceAnnotation(GV, RC, RK, /*IsROV=*/false, - llvm::hlsl::ElementType::Invalid, Buf.Binding); - } } -CGHLSLRuntime::Buffer::Buffer(const HLSLBufferDecl *D) - : Name(D->getName()), IsCBuffer(D->isCBuffer()), - Binding(D->getAttr()) {} - void CGHLSLRuntime::addBufferResourceAnnotation(llvm::GlobalVariable *GV, llvm::hlsl::ResourceClass RC, llvm::hlsl::ResourceKind RK, @@ -521,21 +698,15 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() { } } -// Returns true if the type is an HLSL resource class -static bool isResourceRecordType(const clang::Type *Ty) { - return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; -} - -static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD, - llvm::GlobalVariable *GV, unsigned Slot, - unsigned Space) { +static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV, + unsigned Slot, unsigned Space) { LLVMContext &Ctx = CGM.getLLVMContext(); llvm::Type *Int1Ty = llvm::Type::getInt1Ty(Ctx); llvm::Function *InitResFunc = llvm::Function::Create( llvm::FunctionType::get(CGM.VoidTy, false), llvm::GlobalValue::InternalLinkage, - ("_init_resource_" + VD->getName()).str(), CGM.getModule()); + ("_init_resource_" + GV->getName()).str(), CGM.getModule()); InitResFunc->addFnAttr(llvm::Attribute::AlwaysInline); llvm::BasicBlock *EntryBB = @@ -544,20 +715,15 @@ static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD, const DataLayout &DL = CGM.getModule().getDataLayout(); Builder.SetInsertPoint(EntryBB); - const HLSLAttributedResourceType *AttrResType = - HLSLAttributedResourceType::findHandleTypeOnResource( - VD->getType().getTypePtr()); - - // FIXME: Only simple declarations of resources are supported for now. - // Arrays of resources or resources in user defined classes are - // not implemented yet. - assert(AttrResType != nullptr && - "Resource class must have a handle of HLSLAttributedResourceType"); - - llvm::Type *TargetTy = - CGM.getTargetCodeGenInfo().getHLSLType(CGM, AttrResType); - assert(TargetTy != nullptr && - "Failed to convert resource handle to target type"); + // Make sure the global variable is resource handle (cbuffer) or + // resource class (=class where the first element is a resource handle). + llvm::Type *HandleTy = GV->getValueType(); + assert((HandleTy->isTargetExtTy() || + (HandleTy->isStructTy() && + HandleTy->getStructElementType(0)->isTargetExtTy())) && + "unexpected type of the global"); + if (!HandleTy->isTargetExtTy()) + HandleTy = HandleTy->getStructElementType(0); llvm::Value *Args[] = { llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */ @@ -569,9 +735,9 @@ static void createResourceInitFn(CodeGenModule &CGM, const VarDecl *VD, llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */ }; llvm::Value *CreateHandle = Builder.CreateIntrinsic( - /*ReturnType=*/TargetTy, + /*ReturnType=*/HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), Args, nullptr, - Twine(VD->getName()).concat("_h")); + Twine(GV->getName()).concat("_h")); llvm::Value *HandleRef = Builder.CreateStructGEP(GV->getValueType(), GV, 0); Builder.CreateAlignedStore(CreateHandle, HandleRef, @@ -598,8 +764,7 @@ void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD, // not implemented yet. return; - createResourceInitFn(CGM, VD, GV, RBA->getSlotNumber(), - RBA->getSpaceNumber()); + createResourceInitFn(CGM, GV, RBA->getSlotNumber(), RBA->getSpaceNumber()); } llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) { diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 032b2dee82f21..a6e3c78f52285 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H #define LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H +#include "llvm/ADT/DenseMap.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/IntrinsicsDirectX.h" @@ -46,25 +47,31 @@ } \ } +using ResourceClass = llvm::dxil::ResourceClass; + namespace llvm { class GlobalVariable; class Function; class StructType; +class Metadata; } // namespace llvm namespace clang { +class NamedDecl; class VarDecl; class ParmVarDecl; class HLSLBufferDecl; class HLSLResourceBindingAttr; class Type; class DeclContext; +class HLSLPackOffsetAttr; class FunctionDecl; namespace CodeGen { class CodeGenModule; +class CGBuilderTy; class CGHLSLRuntime { public: @@ -124,16 +131,6 @@ class CGHLSLRuntime { unsigned Space; BufferResBinding(HLSLResourceBindingAttr *Attr); }; - struct Buffer { - Buffer(const HLSLBufferDecl *D); - llvm::StringRef Name; - // IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer). - bool IsCBuffer; - BufferResBinding Binding; - // Global variable and offset for each constant. - std::vector> Constants; - llvm::StructType *LayoutStruct = nullptr; - }; protected: CodeGenModule &CGM; @@ -167,10 +164,18 @@ class CGHLSLRuntime { llvm::hlsl::ResourceKind RK, bool IsROV, llvm::hlsl::ElementType ET, BufferResBinding &Binding); - void addConstant(VarDecl *D, Buffer &CB); - void addBufferDecls(const DeclContext *DC, Buffer &CB); + void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, + llvm::GlobalVariable *BufGV); + void addLayoutInfoForBufferElement( + size_t &EndOffset, SmallVector &LayoutItems, + llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr = nullptr); + + size_t getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy); + llvm::Triple::ArchType getArch(); - llvm::SmallVector Buffers; + + // sizes of structs that in constant buffer layout + llvm::DenseMap StructSizesForBuffer; }; } // namespace CodeGen diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index 7935f7ae37004..33bddc56d83f2 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -44,6 +44,7 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, return nullptr; // convert element type + // llvm::Type *ElemType = CGM.getTypes().ConvertTypeForMem(ContainedTy); llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy); llvm::StringRef TypeName = @@ -56,9 +57,13 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, return llvm::TargetExtType::get(Ctx, TypeName, {ElemType}, Ints); } - case llvm::dxil::ResourceClass::CBuffer: - llvm_unreachable("dx.CBuffer handles are not implemented yet"); - break; + case llvm::dxil::ResourceClass::CBuffer: { + QualType StructTy = ResType->getContainedType(); + if (StructTy.isNull()) + return nullptr; + llvm::Type *Ty = CGM.getTypes().ConvertType(StructTy); + return llvm::TargetExtType::get(Ctx, "dx.CBuffer", {Ty}); + } case llvm::dxil::ResourceClass::Sampler: llvm_unreachable("dx.Sampler handles are not implemented yet"); break; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index b71dd2b273a1c..2807cc773320b 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -239,6 +239,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) { // Make sure there is no overlap in packoffset - sort PackOffsetVec by offset // and compare adjacent values. + bool IsValid = true; ASTContext &Context = S.getASTContext(); std::sort(PackOffsetVec.begin(), PackOffsetVec.end(), [](const std::pair &LHS, @@ -257,8 +258,10 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) { VarDecl *NextVar = PackOffsetVec[i + 1].first; S.Diag(NextVar->getLocation(), diag::err_hlsl_packoffset_overlap) << NextVar << Var; + IsValid = false; } } + BufDecl->setHasPackoffset(IsValid); } // Returns true if the array has a zero size = if any of the dimensions is 0 diff --git a/clang/test/CodeGenHLSL/cbuf.hlsl b/clang/test/CodeGenHLSL/cbuf.hlsl deleted file mode 100644 index 825e7b8161a60..0000000000000 --- a/clang/test/CodeGenHLSL/cbuf.hlsl +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// CHECK: @a = external addrspace(2) externally_initialized global float, align 4 -// CHECK: @b = external addrspace(2) externally_initialized global double, align 8 -// CHECK: @c = external addrspace(2) externally_initialized global float, align 4 -// CHECK: @d = external addrspace(2) externally_initialized global double, align 8 - -// CHECK: @[[CB:.+]] = external constant { float, double } -cbuffer A : register(b0, space2) { - float a; - double b; -} - -// CHECK: @[[TB:.+]] = external constant { float, double } -tbuffer A : register(t2, space1) { - float c; - double d; -} - -float foo() { -// CHECK: load float, ptr addrspace(2) @a, align 4 -// CHECK: load double, ptr addrspace(2) @b, align 8 -// CHECK: load float, ptr addrspace(2) @c, align 4 -// CHECK: load double, ptr addrspace(2) @d, align 8 - return a + b + c*d; -} - -// CHECK: !hlsl.cbufs = !{![[CBMD:[0-9]+]]} -// CHECK: ![[CBMD]] = !{ptr @[[CB]], i32 13, i32 0, i1 false, i32 0, i32 2} diff --git a/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl b/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl deleted file mode 100644 index 13c401d428331..0000000000000 --- a/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// Make sure cbuffer inside namespace works. - -// CHECK: @_ZN2n02n11aE = external addrspace(2) externally_initialized global float, align 4 -// CHECK: @_ZN2n01bE = external addrspace(2) externally_initialized global float, align 4 - -// CHECK: @[[CB:.+]] = external constant { float } -// CHECK: @[[TB:.+]] = external constant { float } -namespace n0 { -namespace n1 { - cbuffer A { - float a; - } -} - tbuffer B { - float b; - } -} - -float foo() { -// CHECK: load float, ptr addrspace(2) @_ZN2n02n11aE, align 4 -// CHECK: load float, ptr addrspace(2) @_ZN2n01bE, align 4 - return n0::n1::a + n0::b; -} diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl new file mode 100644 index 0000000000000..99f16ea62cdcb --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer.hlsl @@ -0,0 +1,200 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \ +// RUN: -fnative-half-type -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +// CHECK: %struct.__cblayout_CBScalars = type <{ float, double, half, i64, i32, i16, i32, i64 }> +// CHECK: %struct.__cblayout_CBVectors = type <{ <3 x float>, <3 x double>, <2 x half>, <3 x i64>, <4 x i32>, <3 x i16>, <3 x i64> }> +// CHECK: %struct.__cblayout_CBArrays = type <{ [3 x float], [2 x <3 x double>], [2 x [2 x half]], [3 x i64], [2 x [3 x [4 x <4 x i32>]]], [1 x i16], [2 x i64], [4 x i32] }> +// CHECK: %struct.__cblayout_CBStructs = type { %struct.A, %struct.B, %struct.C, [5 x %struct.A], %struct.__cblayout_D, half, %struct.B, <3 x i16> } +// CHECK: %struct.A = type { <2 x float> } +// CHECK: %struct.C = type { i32, %struct.A } +// CHECK: %struct.__cblayout_D = type { [2 x [3 x %struct.B]] } +// CHECK: %struct.B = type { %struct.A, <3 x i16> } + +cbuffer CBScalars : register(b1, space5) { + float a1; + double a2; + float16_t a3; + uint64_t a4; + int a5; + uint16_t a6; + bool a7; + int64_t a8; +} + +// CHECK: @CBScalars.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBScalars) +// CHECK: @a1 = external addrspace(2) global float, align 4 +// CHECK: @a2 = external addrspace(2) global double, align 8 +// CHECK: @a3 = external addrspace(2) global half, align 2 +// CHECK: @a4 = external addrspace(2) global i64, align 8 +// CHECK: @a5 = external addrspace(2) global i32, align 4 +// CHECK: @a6 = external addrspace(2) global i16, align 2 +// CHECK: @a7 = external addrspace(2) global i32, align 4 +// CHECK: @a8 = external addrspace(2) global i64, align 8 + +cbuffer CBVectors { + float3 b1; + double3 b2; + float16_t2 b3; + uint64_t3 b4; + int4 b5; + uint16_t3 b6; + int64_t3 b7; + // FIXME: add s bool vectors after llvm-project/llvm#91639 is added +} + +// CHECK: @CBVectors.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBVectors) +// CHECK: @b1 = external addrspace(2) global <3 x float>, align 16 +// CHECK: @b2 = external addrspace(2) global <3 x double>, align 32 +// CHECK: @b3 = external addrspace(2) global <2 x half>, align 4 +// CHECK: @b4 = external addrspace(2) global <3 x i64>, align 32 +// CHECK: @b5 = external addrspace(2) global <4 x i32>, align 16 +// CHECK: @b6 = external addrspace(2) global <3 x i16>, align 8 +// CHECK: @b7 = external addrspace(2) global <3 x i64>, align 32 + +cbuffer CBArrays : register(b2) { + float c1[3]; + double3 c2[2]; + float16_t c3[2][2]; + uint64_t c4[3]; + int4 c5[2][3][4]; + uint16_t c6[1]; + int64_t c7[2]; + bool c8[4]; +} + +// CHECK: @CBArrays.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBArrays) +// CHECK: @c1 = external addrspace(2) global [3 x float], align 4 +// CHECK: @c2 = external addrspace(2) global [2 x <3 x double>], align 32 +// CHECK: @c3 = external addrspace(2) global [2 x [2 x half]], align 2 +// CHECK: @c4 = external addrspace(2) global [3 x i64], align 8 +// CHECK: @c5 = external addrspace(2) global [2 x [3 x [4 x <4 x i32>]]], align 16 +// CHECK: @c6 = external addrspace(2) global [1 x i16], align 2 +// CHECK: @c7 = external addrspace(2) global [2 x i64], align 8 +// CHECK: @c8 = external addrspace(2) global [4 x i32], align 4 + +struct Empty {}; + +struct A { + float2 f1; +}; + +struct B : A { + uint16_t3 f2; +}; + +struct C { + int i; + A f3; +}; + +struct D { + B array_of_B[2][3]; + Empty es; +}; + +// CHECK: @CBStructs.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBStructs) +// CHECK: @a = external addrspace(2) global %struct.A, align 8 +// CHECK: @b = external addrspace(2) global %struct.B, align 8 +// CHECK: @c = external addrspace(2) global %struct.C, align 8 +// CHECK: @array_of_A = external addrspace(2) global [5 x %struct.A], align 8 +// CHECK: @d = external addrspace(2) global %struct.__cblayout_D, align 8 +// CHECK: @e = external addrspace(2) global half, align 2 + +cbuffer CBStructs { + A a; + B b; + C c; + A array_of_A[5]; + D d; + half e; + B f; + uint16_t3 g; +}; + +struct Test { + float a, b; +}; + +// CHECK: @CBMix.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBMix) +// CHECK: @test = external addrspace(2) global [2 x %struct.Test], align 4 +// CHECK: @f1 = external addrspace(2) global float, align 4 +// CHECK: @f2 = external addrspace(2) global [3 x [2 x <2 x float>]], align 8 +// CHECK: @f3 = external addrspace(2) global float, align 4 +// CHECK: @s = external addrspace(2) global %struct.anon, align 4 +// CHECK: @dd = external addrspace(2) global double, align 8 +// CHECK: @f4 = external addrspace(2) global float, align 4 +// CHECK: @dv = external addrspace(2) global <1 x double>, align 8 +// CHECK: @uv = external addrspace(2) global i16, align 2 + +cbuffer CBMix { + Test test[2]; + float f1; + float2 f2[3][2]; + float f3; + struct { float c; } s; + double dd; + float f4; + vector dv; + uint16_t uv; +}; + +// CHECK: efine internal void @_init_resource_CBScalars.cb() +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[HANDLE1:.*]] = call target("dx.CBuffer", %struct.__cblayout_CBScalars) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBScalarsst(i32 5, i32 1, i32 1, i32 0, i1 false) +// CHECK-NEXT: store target("dx.CBuffer", %struct.__cblayout_CBScalars) %[[HANDLE1]], ptr @CBScalars.cb, align 4 + +// CHECK: define internal void @_init_resource_CBArrays.cb() +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[HANDLE2:.*]] = call target("dx.CBuffer", %struct.__cblayout_CBArrays) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBArraysst(i32 0, i32 2, i32 1, i32 0, i1 false) +// CHECK-NEXT: store target("dx.CBuffer", %struct.__cblayout_CBArrays) %[[HANDLE2]], ptr @CBArrays.cb, align 4 + +RWBuffer Buf; + +[numthreads(4,1,1)] +void main() { + //Buf[0] = a1 + b1.z + c1[2] + a.f1.y; + Buf[0] = a.f1.y; +} + +// CHECK: define internal void @_GLOBAL__sub_I_cbuffer.hlsl() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_init_resource_CBScalars.cb() +// CHECK-NEXT: call void @_init_resource_CBArrays.cb() + +// CHECK: !hlsl.cbs = !{![[CBSCALARS:[0-9]+]], ![[CBVECTORS:[0-9]+]], ![[CBARRAYS:[0-9]+]], ![[CBSTRUCTS:[0-9]+]], ![[CBMIX:[0-9]+]]} +// CHECK: !hlsl.cblayouts = !{![[CBSCALARS_LAYOUT:[0-9]+]], ![[CBVECTORS_LAYOUT:[0-9]+]], ![[CBARRAYS_LAYOUT:[0-9]+]], ![[A_LAYOUT:[0-9]+]], +// CHECK-SAME: ![[B_LAYOUT:[0-9]+]], ![[C_LAYOUT:[0-9]+]], ![[D_LAYOUT:[0-9]+]], ![[CBSTRUCTS_LAYOUT:[0-9]+]], ![[TEST_LAYOUT:[0-9]+]], +// CHECK-SAME: ![[ANON_LAYOUT:[0-9]+]], ![[CBMIX_LAYOUT:[0-9]+]]} + +// CHECK: ![[CBSCALARS]] = !{ptr @CBScalars.cb, ptr addrspace(2) @a1, ptr addrspace(2) @a2, ptr addrspace(2) @a3, ptr addrspace(2) @a4, +// CHECK-SAME: ptr addrspace(2) @a5, ptr addrspace(2) @a6, ptr addrspace(2) @a7, ptr addrspace(2) @a8} + +// CHECK: ![[CBVECTORS]] = !{ptr @CBVectors.cb, ptr addrspace(2) @b1, ptr addrspace(2) @b2, ptr addrspace(2) @b3, ptr addrspace(2) @b4, +// CHECK-SAME: ptr addrspace(2) @b5, ptr addrspace(2) @b6, ptr addrspace(2) @b7} + +// CHECK: ![[CBARRAYS]] = !{ptr @CBArrays.cb, ptr addrspace(2) @c1, ptr addrspace(2) @c2, ptr addrspace(2) @c3, ptr addrspace(2) @c4, +// CHECK-SAME: ptr addrspace(2) @c5, ptr addrspace(2) @c6, ptr addrspace(2) @c7, ptr addrspace(2) @c8} + +// CHECK: ![[CBSTRUCTS]] = !{ptr @CBStructs.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c, ptr addrspace(2) @array_of_A, +// CHECK-SAME: ptr addrspace(2) @d, ptr addrspace(2) @e, ptr addrspace(2) @f, ptr addrspace(2) @g} + +// CHECK: ![[CBMIX]] = !{ptr @CBMix.cb, ptr addrspace(2) @test, ptr addrspace(2) @f1, ptr addrspace(2) @f2, ptr addrspace(2) @f3, +// CHECK-SAME: ptr addrspace(2) @s, ptr addrspace(2) @dd, ptr addrspace(2) @f4, ptr addrspace(2) @dv, ptr addrspace(2) @uv} + +// CHECK: ![[CBSCALARS_LAYOUT]] = !{!"struct.__cblayout_CBScalars", i32 56, i32 0, i32 8, i32 16, i32 24, i32 32, i32 36, i32 40, i32 48} +// CHECK: ![[CBVECTORS_LAYOUT]] = !{!"struct.__cblayout_CBVectors", i32 136, i32 0, i32 16, i32 40, i32 48, i32 80, i32 96, i32 112} + +// CHECK: ![[CBARRAYS_LAYOUT]] = !{!"struct.__cblayout_CBArrays", i32 708, i32 0, i32 16, i32 48, i32 32, i32 112, i32 16, i32 176, i32 16, +// CHECK-SAME: i32 224, i32 16, i32 608, i32 16, i32 624, i32 16, i32 656, i32 16} + +// CHECK: ![[A_LAYOUT]] = !{!"struct.A", i32 8, i32 0} +// CHECK: ![[B_LAYOUT]] = !{!"struct.B", i32 14, i32 0, i32 8} +// CHECK: ![[C_LAYOUT]] = !{!"struct.C", i32 24, i32 0, i32 16} +// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0, i32 16} +// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 16, i32 144, i32 238, i32 240, i32 256} + +// CHECK: ![[TEST_LAYOUT]] = !{!"struct.Test", i32 8, i32 0, i32 4} +// CHECK: ![[ANON_LAYOUT]] = !{!"struct.anon", i32 4, i32 0} +// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 16, i32 24, i32 32, i32 16, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160} diff --git a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl new file mode 100644 index 0000000000000..aa5659af414ef --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// Make sure cbuffer inside namespace works. + +// CHECK: %"struct.n0::n1::__cblayout_A" = type { float } +// CHECK: %"struct.n0::__cblayout_B" = type { float } +// CHECK: %"struct.n0::n2::__cblayout_C" = type { float, %"struct.n0::Foo" } +// CHECK: %"struct.n0::Foo" = type { float } + +// CHECK: @A.cb = external constant target("dx.CBuffer", %"struct.n0::n1::__cblayout_A") +// CHECK: @_ZN2n02n11aE = external addrspace(2) global float, align 4 + +// CHECK: @B.cb = external constant target("dx.CBuffer", %"struct.n0::__cblayout_B") +// CHECK: @_ZN2n01aE = external addrspace(2) global float, align 4 + +// CHECK: @C.cb = external constant target("dx.CBuffer", %"struct.n0::n2::__cblayout_C") +// CHECK: @_ZN2n02n21aE = external addrspace(2) global float, align 4 +// CHECK: @_ZN2n02n21bE = external addrspace(2) global %"struct.n0::Foo", align 4 + +namespace n0 { + struct Foo { + float f; + }; + + namespace n1 { + cbuffer A { + float a; + } + } + cbuffer B { + float a; + } + namespace n2 { + cbuffer C { + float a; + Foo b; + } + } +} + +float foo() { + // CHECK: load float, ptr addrspace(2) @_ZN2n02n11aE, align 4 + // CHECK: load float, ptr addrspace(2) @_ZN2n01aE, align 4 + // CHECK: load float, ptr addrspace(2) @_ZN2n02n21aE, align 4 + return n0::n1::a + n0::a + n0::n2::a; +} + +[numthreads(4,1,1)] +void main() {} + +// CHECK: !hlsl.cbs = !{![[A:[0-9]+]], ![[B:[0-9]+]], ![[C:[0-9]+]]} +// CHECK: !hlsl.cblayouts = !{![[A_LAYOUT:[0-9]+]], ![[B_LAYOUT:[0-9]+]], ![[FOO_LAYOUT:[0-9]+]], ![[C_LAYOUT:[0-9]+]]} + +// CHECK: [[A]] = !{ptr @A.cb, ptr addrspace(2) @_ZN2n02n11aE} +// CHECK: [[B]] = !{ptr @B.cb, ptr addrspace(2) @_ZN2n01aE} +// CHECK: [[C]] = !{ptr @C.cb, ptr addrspace(2) @_ZN2n02n21aE, ptr addrspace(2) @_ZN2n02n21bE} + +// CHECK: ![[A_LAYOUT]] = !{!"struct.n0::n1::__cblayout_A", i32 4, i32 0} +// CHECK: ![[B_LAYOUT]] = !{!"struct.n0::__cblayout_B", i32 4, i32 0} +// CHECK: ![[FOO_LAYOUT]] = !{!"struct.n0::Foo", i32 4, i32 0} +// CHECK: ![[C_LAYOUT]] = !{!"struct.n0::n2::__cblayout_C", i32 20, i32 0, i32 16} diff --git a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl new file mode 100644 index 0000000000000..f5b5cba6197b3 --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-compute %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// CHECK: %struct.__cblayout_CB = type <{ float, double, <2 x i32> }> + +// CHECK: @CB.cb = external constant target("dx.CBuffer", %struct.__cblayout_CB) +// CHECK: @a = external addrspace(2) global float, align 4 +// CHECK: @b = external addrspace(2) global double, align 8 +// CHECK: @c = external addrspace(2) global <2 x i32>, align 8 + +cbuffer CB : register(b1, space3) { + float a : packoffset(c1.x); + double b : packoffset(c10.z); + int2 c : packoffset(c5.z); +} + +// CHECK: define internal void @_init_resource_CB.cb() +// CHECK-NEXT: entry: +// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", %struct.__cblayout_CB) @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBst(i32 3, i32 1, i32 1, i32 0, i1 false) + +float foo() { + // CHECK: load float, ptr addrspace(2) @a, align 4 + // CHECK: load double, ptr addrspace(2) @b, align 8 + return a + b; +} +// CHECK: define internal void @_GLOBAL__sub_I_cbuffer_with_packoffset.hlsl() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_init_resource_CB.cb() + +[numthreads(4,1,1)] +void main() { + foo(); +} + +// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]} +// CHECK: !hlsl.cblayouts = !{![[CB_LAYOUT:[0-9]+]]} + +// CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c} +// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_CB", i32 176, i32 16, i32 168, i32 88} diff --git a/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl new file mode 100644 index 0000000000000..8994640d5b311 --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// CHECK: %struct.__cblayout_A = type { float } + +// CHECK: @A.cb = external constant target("dx.CBuffer", %struct.__cblayout_A) +// CHECK: @a = external addrspace(2) global float, align 4 +// CHECK-DAG: @_ZL1b = internal global float 3.000000e+00, align 4 +// CHECK-NOT: @B.cb + +cbuffer A { + float a; + static float b = 3; + float foo() { return a + b; } +} + +cbuffer B { + // intentionally empty +} + +// CHECK: define {{.*}} float @_Z3foov() #0 { +// CHECK: load float, ptr addrspace(2) @a, align 4 + +extern float bar() { + return foo(); +} + +// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]} +// CHECK: !hlsl.cblayouts = !{![[CB_LAYOUT:[0-9]+]]} + +// CHECK: ![[CB]] = !{ptr @A.cb, ptr addrspace(2) @a} +// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_A", i32 4, i32 0} diff --git a/clang/test/CodeGenHLSL/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resource-bindings.hlsl index bfa7896bd9811..57e8cc29572b1 100644 --- a/clang/test/CodeGenHLSL/resource-bindings.hlsl +++ b/clang/test/CodeGenHLSL/resource-bindings.hlsl @@ -2,14 +2,17 @@ // CHECK: define internal void @_init_resource_U0S0() // CHECK: %U0S0_h = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false) +// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %U0S0_h, ptr @U0S0, align 4 RWBuffer U0S0 : register(u0); // CHECK: define internal void @_init_resource_U5S3() // CHECK: %U5S3_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false) +// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) %U5S3_h, ptr @U5S3, align 4 RWBuffer U5S3 : register(u5, space3); // CHECK: define internal void @_init_resource_T2S2() // CHECK: %T2S2_h = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false) +// CHECK: store target("dx.RawBuffer", i32, 0, 0) %T2S2_h, ptr @T2S2, align 4 StructuredBuffer T2S2 : register(t2, space2); struct S { float4 f; @@ -18,6 +21,7 @@ struct S { // CHECK: define internal void @_init_resource_T3S0() // CHECK: %T3S0_h = call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false) +// CHECK: store target("dx.RawBuffer", %struct.S, 0, 0) %T3S0_h, ptr @T3S0, align 4 StructuredBuffer T3S0 : register(t3); // CHECK: define void @main() diff --git a/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl b/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl deleted file mode 100644 index 25f51cce2017d..0000000000000 --- a/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan-library %s \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s - -cbuffer A { - // CHECK: @a = external addrspace(2) externally_initialized global float, align 4 - float a; - // CHECK: @_ZL1b = internal global float 3.000000e+00, align 4 - static float b = 3; - float foo() { return a + b; } -} -// CHECK: @[[CB:.+]] = external constant { float } - -// CHECK:define {{.*}} float @_Z3foov() -// CHECK:load float, ptr addrspace(2) @a, align 4 -// CHECK:load float, ptr @_ZL1b, align 4 - -float bar() { - return foo(); -} From 56beb45a133be18b7e6649cc0a4af852bbed3db7 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 28 Jan 2025 16:06:03 -0800 Subject: [PATCH 04/19] remove stride --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 32 +++++++++++++++-------------- clang/lib/CodeGen/CGHLSLRuntime.h | 7 ++++--- clang/test/CodeGenHLSL/cbuffer.hlsl | 9 ++++---- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index d3d9d42664359..128ca86f66b5a 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -130,8 +130,11 @@ CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) { size_t StructSize = 0; LayoutItems.push_back(nullptr); // reserve one slot for the buffer size - for (llvm::Type *ElTy : StructTy->elements()) - addLayoutInfoForBufferElement(StructSize, LayoutItems, ElTy); + for (llvm::Type *ElTy : StructTy->elements()) { + size_t Offset = calculateBufferElementOffset(ElTy, &StructSize); + // create metadata constant with the element start offset + LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset)); + } // set the size of the buffer LayoutItems[1] = getConstIntMetadata(Ctx, StructSize); @@ -147,16 +150,16 @@ CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) { return StructSize; } -void CGHLSLRuntime::addLayoutInfoForBufferElement( - size_t &EndOffset, SmallVector &LayoutItems, - llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr) { +size_t CGHLSLRuntime::calculateBufferElementOffset( + llvm::Type *LayoutTy, size_t *LayoutEndOffset, + HLSLPackOffsetAttr *PackoffsetAttr) { - // calculate element offset and size; for arrays also calculate array - // element count and stride + // calculate element offset and size size_t ElemOffset = 0; size_t ElemSize = 0; size_t ArrayCount = 1; size_t ArrayStride = 0; + size_t EndOffset = *LayoutEndOffset; size_t NextRowOffset = llvm::alignTo(EndOffset, 16U); if (LayoutTy->isArrayTy()) { @@ -205,13 +208,9 @@ void CGHLSLRuntime::addLayoutInfoForBufferElement( // with packoffset annotations) unsigned NewEndOffset = ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; - EndOffset = std::max(EndOffset, NewEndOffset); + *LayoutEndOffset = std::max(EndOffset, NewEndOffset); - // create metadata constan with the offset and stride and add to list - LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), ElemOffset)); - if (ArrayStride) - LayoutItems.push_back( - getConstIntMetadata(CGM.getLLVMContext(), ArrayStride)); + return ElemOffset; } void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, @@ -284,9 +283,12 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, !UsePackoffset) && "expected packoffset attribute on every declaration"); - addLayoutInfoForBufferElement( - BufferSize, LayoutItems, LayoutType, + size_t Offset = calculateBufferElementOffset( + LayoutType, &BufferSize, UsePackoffset ? VD->getAttr() : nullptr); + + // create metadata constant with the element start offset + LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset)); } } assert(ElemIt == LayoutStruct->element_end() && diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index a6e3c78f52285..d1f678863d4fb 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -166,9 +166,10 @@ class CGHLSLRuntime { BufferResBinding &Binding); void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *BufGV); - void addLayoutInfoForBufferElement( - size_t &EndOffset, SmallVector &LayoutItems, - llvm::Type *LayoutTy, HLSLPackOffsetAttr *PackoffsetAttr = nullptr); + + size_t + calculateBufferElementOffset(llvm::Type *LayoutTy, size_t *LayoutEndOffset, + HLSLPackOffsetAttr *PackoffsetAttr = nullptr); size_t getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy); diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl index 99f16ea62cdcb..e600d25c4e19c 100644 --- a/clang/test/CodeGenHLSL/cbuffer.hlsl +++ b/clang/test/CodeGenHLSL/cbuffer.hlsl @@ -186,15 +186,14 @@ void main() { // CHECK: ![[CBSCALARS_LAYOUT]] = !{!"struct.__cblayout_CBScalars", i32 56, i32 0, i32 8, i32 16, i32 24, i32 32, i32 36, i32 40, i32 48} // CHECK: ![[CBVECTORS_LAYOUT]] = !{!"struct.__cblayout_CBVectors", i32 136, i32 0, i32 16, i32 40, i32 48, i32 80, i32 96, i32 112} -// CHECK: ![[CBARRAYS_LAYOUT]] = !{!"struct.__cblayout_CBArrays", i32 708, i32 0, i32 16, i32 48, i32 32, i32 112, i32 16, i32 176, i32 16, -// CHECK-SAME: i32 224, i32 16, i32 608, i32 16, i32 624, i32 16, i32 656, i32 16} +// CHECK: ![[CBARRAYS_LAYOUT]] = !{!"struct.__cblayout_CBArrays", i32 708, i32 0, i32 48, i32 112, i32 176, i32 224, i32 608, i32 624, i32 656} // CHECK: ![[A_LAYOUT]] = !{!"struct.A", i32 8, i32 0} // CHECK: ![[B_LAYOUT]] = !{!"struct.B", i32 14, i32 0, i32 8} // CHECK: ![[C_LAYOUT]] = !{!"struct.C", i32 24, i32 0, i32 16} -// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0, i32 16} -// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 16, i32 144, i32 238, i32 240, i32 256} +// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0} +// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 144, i32 238, i32 240, i32 256} // CHECK: ![[TEST_LAYOUT]] = !{!"struct.Test", i32 8, i32 0, i32 4} // CHECK: ![[ANON_LAYOUT]] = !{!"struct.anon", i32 4, i32 0} -// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 16, i32 24, i32 32, i32 16, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160} +// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 24, i32 32, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160} From 749e88e87f36b8b4f2a8b5a4ddf02230a4be9600 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 28 Jan 2025 22:02:24 -0800 Subject: [PATCH 05/19] cleanup, update comments --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 188 +++++++++++++------------- clang/lib/CodeGen/CGHLSLRuntime.h | 4 +- clang/lib/CodeGen/Targets/DirectX.cpp | 1 - clang/test/CodeGenHLSL/cbuffer.hlsl | 2 +- 4 files changed, 100 insertions(+), 95 deletions(-) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 128ca86f66b5a..52247173b6990 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -89,8 +89,7 @@ static bool isResourceRecordType(const clang::Type *Ty) { return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; } -// Returns true if the type is an HLSL resource class or an array of -// HLSL resource classes +// Returns true if the type is an HLSL resource class or an array of them static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) { while (const ConstantArrayType *CAT = dyn_cast(Ty)) Ty = CAT->getArrayElementTypeNoTypeQual(); @@ -113,48 +112,56 @@ static unsigned getScalarOrVectorSize(llvm::Type *Ty) { return Ty->getScalarSizeInBits() / 8; } +// Returns size of a struct in constant buffer layout. The sizes are cached +// in StructSizesForBuffer map. The map is also an indicator if a layout +// metadata for this struct has been added to the module. +// If the struct type is not in the map, this method will calculate the struct +// layout, add a metadata node describing it to the module, and add the struct +// size to the map. size_t CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) { - assert(StructTy->isStructTy()); - // check if we already have a side for this struct auto SizeIt = StructSizesForBuffer.find(StructTy); if (SizeIt != StructSizesForBuffer.end()) return SizeIt->getSecond(); - // if not, calculate the struct layout and add it to metadata + // if not, calculate the struct layout and create a metadata node LLVMContext &Ctx = CGM.getLLVMContext(); SmallVector LayoutItems; + + // start metadata list with a struct name and reserve one slot for its size LayoutItems.push_back(MDString::get(Ctx, StructTy->getName())); + LayoutItems.push_back(nullptr); + // add element offsets size_t StructSize = 0; - LayoutItems.push_back(nullptr); // reserve one slot for the buffer size - for (llvm::Type *ElTy : StructTy->elements()) { size_t Offset = calculateBufferElementOffset(ElTy, &StructSize); - // create metadata constant with the element start offset LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset)); } - - // set the size of the buffer + // set the size of the buffer to the reserved slot LayoutItems[1] = getConstIntMetadata(Ctx, StructSize); - // add the struct layout info to metadata - MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems); + // add the struct layout to metadata CGM.getModule() .getOrInsertNamedMetadata("hlsl.cblayouts") - ->addOperand(LayoutMDNode); + ->addOperand(MDNode::get(CGM.getLLVMContext(), LayoutItems)); // add struct size to list and return it StructSizesForBuffer[StructTy] = StructSize; return StructSize; } +// Calculates offset of a single element in constant buffer layout. +// The provided LayoutEndOffset marks the end of the layout so far (end offset +// of the buffer or struct). After the element offset calculations are done it +// will be updated the new end of layout value. +// If the PackoffsetAttrs is not nullptr the offset will be based on the +// packoffset annotation. size_t CGHLSLRuntime::calculateBufferElementOffset( llvm::Type *LayoutTy, size_t *LayoutEndOffset, HLSLPackOffsetAttr *PackoffsetAttr) { - // calculate element offset and size size_t ElemOffset = 0; size_t ElemSize = 0; size_t ArrayCount = 1; @@ -198,14 +205,15 @@ size_t CGHLSLRuntime::calculateBufferElementOffset( ElemOffset = PackoffsetAttr->getOffsetInBytes(); } else { ElemOffset = llvm::alignTo(EndOffset, Align); + // if the element does not fit, move it to the next row if (ElemOffset + ElemSize > NextRowOffset) ElemOffset = NextRowOffset; } } // Update end offset of the buffer/struct layout; do not update it if - // the provided EndOffset is already bigger than the new one (which may happen - // with packoffset annotations) + // the provided EndOffset is already bigger than the new one value + // (which may happen with packoffset annotations) unsigned NewEndOffset = ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; *LayoutEndOffset = std::max(EndOffset, NewEndOffset); @@ -213,102 +221,106 @@ size_t CGHLSLRuntime::calculateBufferElementOffset( return ElemOffset; } +// Emits constant global variables for buffer declarations, creates metadata +// linking the constant globals with the buffer. Also calculates the buffer +// layout and creates metadata node describing it. void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *BufGV) { + LLVMContext &Ctx = CGM.getLLVMContext(); llvm::StructType *LayoutStruct = cast( cast(BufGV->getValueType())->getTypeParameter(0)); - LLVMContext &Ctx = CGM.getLLVMContext(); - + // Start metadata list associating the buffer global variable with its + // constatns SmallVector BufGlobals; BufGlobals.push_back(ValueAsMetadata::get(BufGV)); + // Start layout metadata list with a struct name and reserve one slot for + // the buffer size SmallVector LayoutItems; LayoutItems.push_back(MDString::get(Ctx, LayoutStruct->getName())); + LayoutItems.push_back(nullptr); size_t BufferSize = 0; - size_t BufferSizeIndex = LayoutItems.size(); - LayoutItems.push_back(nullptr); // reserve one slot for the buffer size - bool UsePackoffset = BufDecl->hasPackoffset(); - const auto *ElemIt = LayoutStruct->element_begin(); for (Decl *D : BufDecl->decls()) { if (isa(D)) // Nothing to do for this declaration. continue; if (isa(D)) { - // A function within an cbuffer is effectively a top-level function, - // as it only refers to globally scoped declarations. + // A function within an cbuffer is effectively a top-level function. CGM.EmitTopLevelDecl(D); continue; } - if (VarDecl *VD = dyn_cast(D)) { - QualType VDTy = VD->getType(); - if (VDTy.getAddressSpace() != LangAS::hlsl_constant) { - if (VD->getStorageClass() == SC_Static || - isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) { - // Emit static variables and resource classes inside cbuffer as - // regular globals - CGM.EmitGlobal(VD); - } - // Anything else that is not in the hlsl_constant address space must be - // an empty struct or a zero-sized array and can be ignored - continue; - } + VarDecl *VD = dyn_cast(D); + if (!VD) + continue; - assert(ElemIt != LayoutStruct->element_end() && - "number of elements in layout struct does not match"); - llvm::Type *LayoutType = *ElemIt++; - - assert((CGM.getTypes().ConvertTypeForMem(VDTy) == LayoutType || - (LayoutType->isStructTy() && - cast(LayoutType) - ->getName() - .starts_with(("struct.__cblayout_" + - VDTy->getAsCXXRecordDecl()->getName()) - .str()))) && - "layout type does not match the converted element type"); - - // handle any resources declarations inside the struct - if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType()) - // FIXME: handle resources in cbuffer structs - llvm_unreachable("resources in cbuffer are not supported yet"); - - GlobalVariable *ElemGV = - cast(CGM.GetAddrOfGlobalVar(VD, LayoutType)); - BufGlobals.push_back(ValueAsMetadata::get(ElemGV)); - - assert(((UsePackoffset && VD->hasAttr()) || - !UsePackoffset) && - "expected packoffset attribute on every declaration"); - - size_t Offset = calculateBufferElementOffset( - LayoutType, &BufferSize, - UsePackoffset ? VD->getAttr() : nullptr); - - // create metadata constant with the element start offset - LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset)); + QualType VDTy = VD->getType(); + if (VDTy.getAddressSpace() != LangAS::hlsl_constant) { + if (VD->getStorageClass() == SC_Static || + VDTy.getAddressSpace() == LangAS::hlsl_groupshared || + isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) { + // Emit static and groupshared variables and resource classes inside + // cbuffer as regular globals + CGM.EmitGlobal(VD); + } + // Anything else that is not in the hlsl_constant address space must be + // an empty struct or a zero-sized array and can be ignored + continue; } + + assert(ElemIt != LayoutStruct->element_end() && + "number of elements in layout struct does not match"); + llvm::Type *LayoutType = *ElemIt++; + + // Make sure the type of the VarDecl type matches the type of the layout + // struct element, or that it is a layout struct with the same name + assert((CGM.getTypes().ConvertTypeForMem(VDTy) == LayoutType || + (LayoutType->isStructTy() && + cast(LayoutType) + ->getName() + .starts_with(("struct.__cblayout_" + + VDTy->getAsCXXRecordDecl()->getName()) + .str()))) && + "layout type does not match the converted element type"); + + // there might be resources inside the used defined structs + if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType()) + // FIXME: handle resources in cbuffer structs + llvm_unreachable("resources in cbuffer are not supported yet"); + + // create global variable for the constant and to metadata list + GlobalVariable *ElemGV = + cast(CGM.GetAddrOfGlobalVar(VD, LayoutType)); + BufGlobals.push_back(ValueAsMetadata::get(ElemGV)); + + // get offset of the global and and to metadata list + assert(((UsePackoffset && VD->hasAttr()) || + !UsePackoffset) && + "expected packoffset attribute on every declaration"); + size_t Offset = calculateBufferElementOffset( + LayoutType, &BufferSize, + UsePackoffset ? VD->getAttr() : nullptr); + LayoutItems.push_back(getConstIntMetadata(Ctx, Offset)); } assert(ElemIt == LayoutStruct->element_end() && "number of elements in layout struct does not match"); - - // add buffer global and a list of its constants to metadata - MDNode *BufMDNode = MDNode::get(CGM.getLLVMContext(), BufGlobals); - CGM.getModule().getOrInsertNamedMetadata("hlsl.cbs")->addOperand(BufMDNode); - // set the size of the buffer - LayoutItems[BufferSizeIndex] = getConstIntMetadata(Ctx, BufferSize); + LayoutItems[1] = getConstIntMetadata(Ctx, BufferSize); + + // add buffer metadata to the module + CGM.getModule() + .getOrInsertNamedMetadata("hlsl.cbs") + ->addOperand(MDNode::get(Ctx, BufGlobals)); - // add buffer layout to metadata - MDNode *LayoutMDNode = MDNode::get(CGM.getLLVMContext(), LayoutItems); CGM.getModule() .getOrInsertNamedMetadata("hlsl.cblayouts") - ->addOperand(LayoutMDNode); + ->addOperand(MDNode::get(Ctx, LayoutItems)); } -// Creates resource handle type for the HLSL buffer +// Creates resource handle type for the HLSL buffer declaration static const clang::HLSLAttributedResourceType * createBufferHandleType(const HLSLBufferDecl *BufDecl) { ASTContext &AST = BufDecl->getASTContext(); @@ -319,26 +331,20 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) { return cast(QT.getTypePtr()); } -// Creates temporary global variables for all declarations within the constant -// buffer context, creates a global variable for the constant buffer and adds -// it to the module. -// All uses of the temporary constant globals will be replaced with buffer -// access intrinsic resource.getpointer in CGHLSLRuntime::finishCodeGen. -// Later on in DXILResourceAccess pass these will be transtaled -// to dx.op.cbufferLoadLegacy instructions. +// Codegen for HLSLBufferDecl void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { assert(BufDecl->isCBuffer() && "tbuffer codegen is not supported yet"); - // Create resource handle type for the buffer + // create resource handle type for the buffer const clang::HLSLAttributedResourceType *ResHandleTy = createBufferHandleType(BufDecl); - // ignore empty constant buffer + // empty constant buffer is ignored if (ResHandleTy->getContainedType()->getAsCXXRecordDecl()->isEmpty()) return; - // Create global variable for the buffer + // create global variable for the constant buffer llvm::Module &M = CGM.getModule(); llvm::TargetExtType *TargetTy = cast(convertHLSLSpecificType(ResHandleTy)); @@ -350,10 +356,10 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { GlobalValue::NotThreadLocal); M.insertGlobalVariable(BufGV); - // Add globals for buffer elements and create metadata node for the buffer + // Add globals for constant buffer elements and create metadata nodes emitBufferGlobalsAndMetadata(BufDecl, BufGV); - // Add cbuffer resource initialization + // Resource initialization const HLSLResourceBindingAttr *RBA = BufDecl->getAttr(); // FIXME: handle implicit binding if no binding attribute is found diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index d1f678863d4fb..8658cb8653fb4 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -71,7 +71,6 @@ class FunctionDecl; namespace CodeGen { class CodeGenModule; -class CGBuilderTy; class CGHLSLRuntime { public: @@ -175,7 +174,8 @@ class CGHLSLRuntime { llvm::Triple::ArchType getArch(); - // sizes of structs that in constant buffer layout + // Sizes of structs in constant buffer layout. Structs in the map + // had their layout calculated and added to the module as metadata. llvm::DenseMap StructSizesForBuffer; }; diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index 33bddc56d83f2..ec7fa342d5846 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -44,7 +44,6 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, return nullptr; // convert element type - // llvm::Type *ElemType = CGM.getTypes().ConvertTypeForMem(ContainedTy); llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy); llvm::StringRef TypeName = diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl index e600d25c4e19c..9a3f632e859c7 100644 --- a/clang/test/CodeGenHLSL/cbuffer.hlsl +++ b/clang/test/CodeGenHLSL/cbuffer.hlsl @@ -164,6 +164,7 @@ void main() { // CHECK-NEXT: call void @_init_resource_CBArrays.cb() // CHECK: !hlsl.cbs = !{![[CBSCALARS:[0-9]+]], ![[CBVECTORS:[0-9]+]], ![[CBARRAYS:[0-9]+]], ![[CBSTRUCTS:[0-9]+]], ![[CBMIX:[0-9]+]]} + // CHECK: !hlsl.cblayouts = !{![[CBSCALARS_LAYOUT:[0-9]+]], ![[CBVECTORS_LAYOUT:[0-9]+]], ![[CBARRAYS_LAYOUT:[0-9]+]], ![[A_LAYOUT:[0-9]+]], // CHECK-SAME: ![[B_LAYOUT:[0-9]+]], ![[C_LAYOUT:[0-9]+]], ![[D_LAYOUT:[0-9]+]], ![[CBSTRUCTS_LAYOUT:[0-9]+]], ![[TEST_LAYOUT:[0-9]+]], // CHECK-SAME: ![[ANON_LAYOUT:[0-9]+]], ![[CBMIX_LAYOUT:[0-9]+]]} @@ -193,7 +194,6 @@ void main() { // CHECK: ![[C_LAYOUT]] = !{!"struct.C", i32 24, i32 0, i32 16} // CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0} // CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 144, i32 238, i32 240, i32 256} - // CHECK: ![[TEST_LAYOUT]] = !{!"struct.Test", i32 8, i32 0, i32 4} // CHECK: ![[ANON_LAYOUT]] = !{!"struct.anon", i32 4, i32 0} // CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 24, i32 32, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160} From a12bcbc287d706c2c7fa56005badb40d4a0fa6fc Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 4 Feb 2025 12:59:13 -0800 Subject: [PATCH 06/19] Move skipping of implicit initializer of hlsl_constant decls to a different PR. --- clang/lib/Sema/SemaDecl.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ba424567b23e0..7c70150871da9 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14184,12 +14184,6 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { Var->getType().getAddressSpace() == LangAS::opencl_local) return; - // In HLSL, objects in the hlsl_constat address space are initialized - // externaly, so don't synthesize an implicit initializer. - if (getLangOpts().HLSL && - Var->getType().getAddressSpace() == LangAS::hlsl_constant) - return; - // C++03 [dcl.init]p9: // If no initializer is specified for an object, and the // object is of (possibly cv-qualified) non-POD class type (or From 66ae78472080bb2b9de4b99ff63b5219f14c75c8 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 4 Feb 2025 17:58:43 -0800 Subject: [PATCH 07/19] Add LayoutStruct to HLSLBufferDecl; add skipping of implicit initializers --- clang/include/clang/AST/Decl.h | 5 ++++- clang/lib/AST/Decl.cpp | 15 +++++---------- clang/lib/Sema/SemaDecl.cpp | 6 ++++++ clang/lib/Sema/SemaHLSL.cpp | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index e1c7e3817699c..05e56978977f2 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -5035,6 +5035,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { /// HasValidPackoffset - Whether the buffer has valid packoffset annotations // on all declarations bool HasPackoffset; + // LayoutStruct - Layout struct for the buffer + CXXRecordDecl *LayoutStruct; HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *ID, SourceLocation IDLoc, @@ -5057,7 +5059,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { bool isCBuffer() const { return IsCBuffer; } void setHasPackoffset(bool PO) { HasPackoffset = PO; } bool hasPackoffset() const { return HasPackoffset; } - const CXXRecordDecl *getLayoutStruct() const; + const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; } + void addLayoutStruct(CXXRecordDecl *LS); // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index fa7d03354a993..4c5201d0ba2ee 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5717,7 +5717,7 @@ HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation IDLoc, SourceLocation LBrace) : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)), DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc), - IsCBuffer(CBuffer), HasPackoffset(false) {} + IsCBuffer(CBuffer), HasPackoffset(false), LayoutStruct(nullptr) {} HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, DeclContext *LexicalParent, bool CBuffer, @@ -5747,15 +5747,10 @@ HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, SourceLocation(), SourceLocation()); } -const CXXRecordDecl *HLSLBufferDecl::getLayoutStruct() const { - // Layout struct is the last decl in the HLSLBufferDecl. - if (CXXRecordDecl *RD = llvm::dyn_cast_or_null(LastDecl)) { - assert(RD->getName().starts_with( - ("__cblayout_" + getIdentifier()->getName()).str()) && - "expected buffer layout struct"); - return RD; - } - llvm_unreachable("HLSL buffer is missing a layout struct"); +void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) { + assert(LayoutStruct == nullptr && "layout struct has already been set"); + LayoutStruct = LS; + addDecl(LS); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 7c70150871da9..5497c30ed09ff 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14184,6 +14184,12 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { Var->getType().getAddressSpace() == LangAS::opencl_local) return; + // In HLSL, objects in the hlsl_constant address space are initialized + // externally, so don't synthesize an implicit initializer. + if (getLangOpts().HLSL && + Var->getType().getAddressSpace() == LangAS::hlsl_constant) + return; + // C++03 [dcl.init]p9: // If no initializer is specified for an object, and the // object is of (possibly cv-qualified) non-POD class type (or diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 2807cc773320b..d80e605055ad7 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -502,7 +502,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { } } LS->completeDefinition(); - BufDecl->addDecl(LS); + BufDecl->addLayoutStruct(LS); } // Handle end of cbuffer/tbuffer declaration From e042f72eee72ffb58d27dfd5e6011bd2e510ab84 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 4 Feb 2025 20:52:25 -0800 Subject: [PATCH 08/19] desugar types and check for resource builtin type --- clang/lib/Sema/SemaHLSL.cpp | 3 ++- clang/test/AST/HLSL/cbuffer.hlsl | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index b71dd2b273a1c..a4d5b89794af4 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -282,6 +282,7 @@ static bool isResourceRecordTypeOrArrayOf(const Type *Ty) { // array, or a builtin intangible type. Returns false it is a valid leaf element // type or if it is a record type that needs to be inspected further. static bool isInvalidConstantBufferLeafElementType(const Type *Ty) { + Ty = Ty->getUnqualifiedDesugaredType(); if (isResourceRecordTypeOrArrayOf(Ty)) return true; if (Ty->isRecordType()) @@ -289,7 +290,7 @@ static bool isInvalidConstantBufferLeafElementType(const Type *Ty) { if (Ty->isConstantArrayType() && isZeroSizedArray(cast(Ty))) return true; - if (Ty->isHLSLBuiltinIntangibleType()) + if (Ty->isHLSLBuiltinIntangibleType() || Ty->isHLSLAttributedResourceType()) return true; return false; } diff --git a/clang/test/AST/HLSL/cbuffer.hlsl b/clang/test/AST/HLSL/cbuffer.hlsl index 865db1201baa5..87202d7998bdb 100644 --- a/clang/test/AST/HLSL/cbuffer.hlsl +++ b/clang/test/AST/HLSL/cbuffer.hlsl @@ -75,6 +75,8 @@ cbuffer CB { groupshared float g2; // CHECK: VarDecl {{.*}} e2 'hlsl_constant float' float e2; + // CHECK: VarDecl {{.*}} h2 '__hlsl_resource_t' + __hlsl_resource_t h2; // CHECK: CXXRecordDecl {{.*}} implicit referenced struct __cblayout_CB_1 definition // CHECK: PackedAttr // CHECK-NEXT: FieldDecl {{.*}} a2 'float' From 42bb34f66f0030f55e1055c4ee0b362511b7f45b Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 4 Feb 2025 22:01:49 -0800 Subject: [PATCH 09/19] [HLSL] Implement default constant buffer `$Globals` All variable declarations in the global scope that are not resources, static or empty are implicitly added to implicit constant buffer `$Globals`. Fixes #123801 --- clang/include/clang/AST/Decl.h | 22 +++++++ clang/include/clang/Sema/SemaHLSL.h | 7 ++- clang/lib/AST/Decl.cpp | 41 ++++++++++++- clang/lib/CodeGen/CGHLSLRuntime.cpp | 7 +-- clang/lib/CodeGen/CodeGenModule.cpp | 5 ++ clang/lib/Sema/Sema.cpp | 3 +- clang/lib/Sema/SemaHLSL.cpp | 47 +++++++++++++-- clang/test/AST/HLSL/default_cbuffer.hlsl | 50 ++++++++++++++++ clang/test/CodeGenHLSL/basic_types.hlsl | 64 ++++++++++----------- clang/test/CodeGenHLSL/default_cbuffer.hlsl | 43 ++++++++++++++ 10 files changed, 242 insertions(+), 47 deletions(-) create mode 100644 clang/test/AST/HLSL/default_cbuffer.hlsl create mode 100644 clang/test/CodeGenHLSL/default_cbuffer.hlsl diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 05e56978977f2..f86ddaf89bd9c 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -5038,6 +5038,11 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { // LayoutStruct - Layout struct for the buffer CXXRecordDecl *LayoutStruct; + // For default (implicit) constant buffer, a lisf of references of global + // decls that belong to the buffer. The decls are already parented by the + // translation unit context. + SmallVector DefaultBufferDecls; + HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *ID, SourceLocation IDLoc, SourceLocation LBrace); @@ -5047,6 +5052,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { bool CBuffer, SourceLocation KwLoc, IdentifierInfo *ID, SourceLocation IDLoc, SourceLocation LBrace); + static HLSLBufferDecl *CreateDefaultCBuffer(ASTContext &C, + DeclContext *LexicalParent); static HLSLBufferDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); SourceRange getSourceRange() const override LLVM_READONLY { @@ -5061,6 +5068,7 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { bool hasPackoffset() const { return HasPackoffset; } const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; } void addLayoutStruct(CXXRecordDecl *LS); + void addDefaultBufferDecl(Decl *D); // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } @@ -5072,6 +5080,20 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { return static_cast(const_cast(DC)); } + // Iterator for the buffer decls. Concatenates the list of decls parented + // by this HLSLBufferDecl with the list of default buffer decls. + using buffer_decl_iterator = + llvm::concat_iterator::const_iterator, + decl_iterator>; + using buffer_decl_range = llvm::iterator_range; + + buffer_decl_range buffer_decls() const { + return buffer_decl_range(buffer_decls_begin(), buffer_decls_end()); + } + buffer_decl_iterator buffer_decls_begin() const; + buffer_decl_iterator buffer_decls_end() const; + bool buffer_decls_empty(); + friend class ASTDeclReader; friend class ASTDeclWriter; }; diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index f4cd11f423a84..b1cc856975532 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -103,13 +103,13 @@ class SemaHLSL : public SemaBase { HLSLParamModifierAttr::Spelling Spelling); void ActOnTopLevelFunction(FunctionDecl *FD); void ActOnVariableDeclarator(VarDecl *VD); + void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU); void CheckEntryPoint(FunctionDecl *FD); void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param, const HLSLAnnotationAttr *AnnotationAttr); void DiagnoseAttrStageMismatch( const Attr *A, llvm::Triple::EnvironmentType Stage, std::initializer_list AllowedStages); - void DiagnoseAvailabilityViolations(TranslationUnitDecl *TU); QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS, QualType LHSType, QualType RHSType, @@ -159,11 +159,16 @@ class SemaHLSL : public SemaBase { // List of all resource bindings ResourceBindings Bindings; + // default constant buffer $Globals + HLSLBufferDecl *DefaultCBuffer; + private: void collectResourcesOnVarDecl(VarDecl *D); void collectResourcesOnUserRecordDecl(const VarDecl *VD, const RecordType *RT); void processExplicitBindingsOnDecl(VarDecl *D); + + void diagnoseAvailabilityViolations(TranslationUnitDecl *TU); }; } // namespace clang diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 4c5201d0ba2ee..eede936577f09 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -57,6 +57,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -5741,10 +5742,22 @@ HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, return Result; } +HLSLBufferDecl * +HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C, + DeclContext *LexicalParent) { + DeclContext *DC = LexicalParent; + IdentifierInfo *II = &C.Idents.get("$Globals", tok::TokenKind::identifier); + HLSLBufferDecl *Result = new (C, DC) HLSLBufferDecl( + DC, true, SourceLocation(), II, SourceLocation(), SourceLocation()); + Result->setImplicit(true); + return Result; +} + HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { - return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr, - SourceLocation(), SourceLocation()); + return new (C, ID) + HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr, + SourceLocation(), SourceLocation()); } void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) { @@ -5753,6 +5766,30 @@ void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) { addDecl(LS); } +void HLSLBufferDecl::addDefaultBufferDecl(Decl *D) { + assert(isImplicit() && + "default decls can only be added to the implicit/default constant " + "buffer $Globals"); + DefaultBufferDecls.push_back(D); +} + +HLSLBufferDecl::buffer_decl_iterator +HLSLBufferDecl::buffer_decls_begin() const { + return buffer_decl_iterator(llvm::iterator_range(DefaultBufferDecls.begin(), + DefaultBufferDecls.end()), + decl_range(decls_begin(), decls_end())); +} + +HLSLBufferDecl::buffer_decl_iterator HLSLBufferDecl::buffer_decls_end() const { + return buffer_decl_iterator( + llvm::iterator_range(DefaultBufferDecls.end(), DefaultBufferDecls.end()), + decl_range(decls_end(), decls_end())); +} + +bool HLSLBufferDecl::buffer_decls_empty() { + return DefaultBufferDecls.empty() && decls_empty(); +} + //===----------------------------------------------------------------------===// // ImportDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 52247173b6990..a24f5ac878667 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -244,7 +244,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, size_t BufferSize = 0; bool UsePackoffset = BufDecl->hasPackoffset(); const auto *ElemIt = LayoutStruct->element_begin(); - for (Decl *D : BufDecl->decls()) { + for (Decl *D : BufDecl->buffer_decls()) { if (isa(D)) // Nothing to do for this declaration. continue; @@ -286,10 +286,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, .str()))) && "layout type does not match the converted element type"); - // there might be resources inside the used defined structs - if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType()) - // FIXME: handle resources in cbuffer structs - llvm_unreachable("resources in cbuffer are not supported yet"); + // FIXME: handle resources in cbuffer user-defined structs // create global variable for the constant and to metadata list GlobalVariable *ElemGV = diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index eb8d3ceeeba4c..65ec9c06b9ee5 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -5503,6 +5503,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, if (getLangOpts().OpenCL && ASTTy->isSamplerT()) return; + // HLSL default buffer constants will be emitted during HLSLBufferDecl codegen + if (getLangOpts().HLSL && + D->getType().getAddressSpace() == LangAS::hlsl_constant) + return; + // If this is OpenMP device, check if it is legal to emit this global // normally. if (LangOpts.OpenMPIsTargetDevice && OpenMPRuntime && diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index abb46d3a84e74..d3c350b58da1a 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1416,8 +1416,7 @@ void Sema::ActOnEndOfTranslationUnit() { } if (LangOpts.HLSL) - HLSL().DiagnoseAvailabilityViolations( - getASTContext().getTranslationUnitDecl()); + HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl()); // If there were errors, disable 'unused' warnings since they will mostly be // noise. Don't warn for a use from a module: either we should warn on all diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 1537a88731892..977185fcf9997 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -9,6 +9,7 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/SemaHLSL.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Attrs.inc" @@ -147,7 +148,7 @@ bool ResourceBindings::hasBindingInfoForDecl(const VarDecl *VD) const { return DeclToBindingListIndex.contains(VD); } -SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {} +SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S), DefaultCBuffer(nullptr) {} Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *Ident, @@ -219,7 +220,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) { // or on none. bool HasPackOffset = false; bool HasNonPackOffset = false; - for (auto *Field : BufDecl->decls()) { + for (auto *Field : BufDecl->buffer_decls()) { VarDecl *Var = dyn_cast(Field); if (!Var) continue; @@ -486,7 +487,7 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) { LS->setImplicit(true); LS->startDefinition(); - for (Decl *D : BufDecl->decls()) { + for (Decl *D : BufDecl->buffer_decls()) { VarDecl *VD = dyn_cast(D); if (!VD || VD->getStorageClass() == SC_Static || VD->getType().getAddressSpace() == LangAS::hlsl_groupshared) @@ -1922,7 +1923,21 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, } // namespace -void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) { +void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) { + + // process default CBuffer - create buffer layout struct and invoke codegenCGH + if (DefaultCBuffer) { + SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer); + createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer); + + DeclGroupRef DG(DefaultCBuffer); + SemaRef.Consumer.HandleTopLevelDecl(DG); + } + + diagnoseAvailabilityViolations(TU); +} + +void SemaHLSL::diagnoseAvailabilityViolations(TranslationUnitDecl *TU) { // Skip running the diagnostics scan if the diagnostic mode is // strict (-fhlsl-strict-availability) and the target shader stage is known // because all relevant diagnostics were already emitted in the @@ -2784,6 +2799,14 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) { return Ty; } +static bool IsDefaultBufferConstantDecl(VarDecl *VD) { + QualType QT = VD->getType(); + return VD->getDeclContext()->isTranslationUnit() && + QT.getAddressSpace() == LangAS::Default && + VD->getStorageClass() != SC_Static && + !isInvalidConstantBufferLeafElementType(QT.getTypePtr()); +} + void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) { if (VD->hasGlobalStorage()) { // make sure the declaration has a complete type @@ -2795,7 +2818,21 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) { return; } - // find all resources on decl + // Global variables outside a cbuffer block that are not a resource, static, + // groupshared, or an empty array or struct belong to the default constant + // buffer $Globals + if (IsDefaultBufferConstantDecl(VD)) { + if (DefaultCBuffer == nullptr) + DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer( + SemaRef.getASTContext(), SemaRef.getCurLexicalContext()); + // update address space to hlsl_constant + QualType NewTy = getASTContext().getAddrSpaceQualType( + VD->getType(), LangAS::hlsl_constant); + VD->setType(NewTy); + DefaultCBuffer->addDefaultBufferDecl(VD); + } + + // find all resources bindings on decl if (VD->getType()->isHLSLIntangibleType()) collectResourcesOnVarDecl(VD); diff --git a/clang/test/AST/HLSL/default_cbuffer.hlsl b/clang/test/AST/HLSL/default_cbuffer.hlsl new file mode 100644 index 0000000000000..9e0fce7cc53cf --- /dev/null +++ b/clang/test/AST/HLSL/default_cbuffer.hlsl @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump -o - %s | FileCheck %s + +struct EmptyStruct { +}; + +struct S { + RWBuffer buf; + EmptyStruct es; + float ea[0]; + float b; +}; + +// CHECK: VarDecl {{.*}} used a 'hlsl_constant float' +float a; + +// CHECK: VarDecl {{.*}} b 'RWBuffer':'hlsl::RWBuffer' +RWBuffer b; + +// CHECK: VarDecl {{.*}} c 'EmptyStruct' +EmptyStruct c; + +// CHECK: VarDecl {{.*}} d 'float[0]' +float d[0]; + +// CHECK: VarDecl {{.*}} e 'RWBuffer[2]' +RWBuffer e[2]; + +// CHECK: VarDecl {{.*}} f 'groupshared float' +groupshared float f; + +// CHECK: VarDecl {{.*}} g 'hlsl_constant float' +float g; + +// CHECK: VarDecl {{.*}} h 'hlsl_constant S' +S h; + +// CHECK: HLSLBufferDecl {{.*}} implicit cbuffer $Globals +// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_$Globals definition +// CHECK: PackedAttr +// CHECK-NEXT: FieldDecl {{.*}} a 'float' +// CHECK-NEXT: FieldDecl {{.*}} g 'float' +// CHECK-NEXT: FieldDecl {{.*}} h '__cblayout_S' + +// CHECK: CXXRecordDecl {{.*}} implicit struct __cblayout_S definition +// CHECK: PackedAttr {{.*}} Implicit +// CHECK-NEXT: FieldDecl {{.*}} b 'float' + +export float foo() { + return a; +} diff --git a/clang/test/CodeGenHLSL/basic_types.hlsl b/clang/test/CodeGenHLSL/basic_types.hlsl index d987af45a649f..362042654ea8c 100644 --- a/clang/test/CodeGenHLSL/basic_types.hlsl +++ b/clang/test/CodeGenHLSL/basic_types.hlsl @@ -6,38 +6,38 @@ // RUN: -emit-llvm -disable-llvm-passes -o - -DNAMESPACED| FileCheck %s -// CHECK: @uint16_t_Val = global i16 0, align 2 -// CHECK: @int16_t_Val = global i16 0, align 2 -// CHECK: @uint_Val = global i32 0, align 4 -// CHECK: @uint64_t_Val = global i64 0, align 8 -// CHECK: @int64_t_Val = global i64 0, align 8 -// CHECK: @int16_t2_Val = global <2 x i16> zeroinitializer, align 4 -// CHECK: @int16_t3_Val = global <3 x i16> zeroinitializer, align 8 -// CHECK: @int16_t4_Val = global <4 x i16> zeroinitializer, align 8 -// CHECK: @uint16_t2_Val = global <2 x i16> zeroinitializer, align 4 -// CHECK: @uint16_t3_Val = global <3 x i16> zeroinitializer, align 8 -// CHECK: @uint16_t4_Val = global <4 x i16> zeroinitializer, align 8 -// CHECK: @int2_Val = global <2 x i32> zeroinitializer, align 8 -// CHECK: @int3_Val = global <3 x i32> zeroinitializer, align 16 -// CHECK: @int4_Val = global <4 x i32> zeroinitializer, align 16 -// CHECK: @uint2_Val = global <2 x i32> zeroinitializer, align 8 -// CHECK: @uint3_Val = global <3 x i32> zeroinitializer, align 16 -// CHECK: @uint4_Val = global <4 x i32> zeroinitializer, align 16 -// CHECK: @int64_t2_Val = global <2 x i64> zeroinitializer, align 16 -// CHECK: @int64_t3_Val = global <3 x i64> zeroinitializer, align 32 -// CHECK: @int64_t4_Val = global <4 x i64> zeroinitializer, align 32 -// CHECK: @uint64_t2_Val = global <2 x i64> zeroinitializer, align 16 -// CHECK: @uint64_t3_Val = global <3 x i64> zeroinitializer, align 32 -// CHECK: @uint64_t4_Val = global <4 x i64> zeroinitializer, align 32 -// CHECK: @half2_Val = global <2 x half> zeroinitializer, align 4 -// CHECK: @half3_Val = global <3 x half> zeroinitializer, align 8 -// CHECK: @half4_Val = global <4 x half> zeroinitializer, align 8 -// CHECK: @float2_Val = global <2 x float> zeroinitializer, align 8 -// CHECK: @float3_Val = global <3 x float> zeroinitializer, align 16 -// CHECK: @float4_Val = global <4 x float> zeroinitializer, align 16 -// CHECK: @double2_Val = global <2 x double> zeroinitializer, align 16 -// CHECK: @double3_Val = global <3 x double> zeroinitializer, align 32 -// CHECK: @double4_Val = global <4 x double> zeroinitializer, align 32 +// CHECK: @uint16_t_Val = external addrspace(2) global i16, align 2 +// CHECK: @int16_t_Val = external addrspace(2) global i16, align 2 +// CHECK: @uint_Val = external addrspace(2) global i32, align 4 +// CHECK: @uint64_t_Val = external addrspace(2) global i64, align 8 +// CHECK: @int64_t_Val = external addrspace(2) global i64, align 8 +// CHECK: @int16_t2_Val = external addrspace(2) global <2 x i16>, align 4 +// CHECK: @int16_t3_Val = external addrspace(2) global <3 x i16>, align 8 +// CHECK: @int16_t4_Val = external addrspace(2) global <4 x i16>, align 8 +// CHECK: @uint16_t2_Val = external addrspace(2) global <2 x i16>, align 4 +// CHECK: @uint16_t3_Val = external addrspace(2) global <3 x i16>, align 8 +// CHECK: @uint16_t4_Val = external addrspace(2) global <4 x i16>, align 8 +// CHECK: @int2_Val = external addrspace(2) global <2 x i32>, align 8 +// CHECK: @int3_Val = external addrspace(2) global <3 x i32>, align 16 +// CHECK: @int4_Val = external addrspace(2) global <4 x i32>, align 16 +// CHECK: @uint2_Val = external addrspace(2) global <2 x i32>, align 8 +// CHECK: @uint3_Val = external addrspace(2) global <3 x i32>, align 16 +// CHECK: @uint4_Val = external addrspace(2) global <4 x i32>, align 16 +// CHECK: @int64_t2_Val = external addrspace(2) global <2 x i64>, align 16 +// CHECK: @int64_t3_Val = external addrspace(2) global <3 x i64>, align 32 +// CHECK: @int64_t4_Val = external addrspace(2) global <4 x i64>, align 32 +// CHECK: @uint64_t2_Val = external addrspace(2) global <2 x i64>, align 16 +// CHECK: @uint64_t3_Val = external addrspace(2) global <3 x i64>, align 32 +// CHECK: @uint64_t4_Val = external addrspace(2) global <4 x i64>, align 32 +// CHECK: @half2_Val = external addrspace(2) global <2 x half>, align 4 +// CHECK: @half3_Val = external addrspace(2) global <3 x half>, align 8 +// CHECK: @half4_Val = external addrspace(2) global <4 x half>, align 8 +// CHECK: @float2_Val = external addrspace(2) global <2 x float>, align 8 +// CHECK: @float3_Val = external addrspace(2) global <3 x float>, align 16 +// CHECK: @float4_Val = external addrspace(2) global <4 x float>, align 16 +// CHECK: @double2_Val = external addrspace(2) global <2 x double>, align 16 +// CHECK: @double3_Val = external addrspace(2) global <3 x double>, align 32 +// CHECK: @double4_Val = external addrspace(2) global <4 x double>, align 32 #ifdef NAMESPACED #define TYPE_DECL(T) hlsl::T T##_Val diff --git a/clang/test/CodeGenHLSL/default_cbuffer.hlsl b/clang/test/CodeGenHLSL/default_cbuffer.hlsl new file mode 100644 index 0000000000000..7368997b51ac9 --- /dev/null +++ b/clang/test/CodeGenHLSL/default_cbuffer.hlsl @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \ +// RUN: -fnative-half-type -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +// CHECK: %"struct.__cblayout_$Globals" = type { float, float, %struct.__cblayout_S } +// CHECK: %struct.__cblayout_S = type { float } + +// CHECK-DAG: @"$Globals.cb" = external constant target("dx.CBuffer", %"struct.__cblayout_$Globals") +// CHECK-DAG: @a = external addrspace(2) global float +// CHECK-DAG: @g = external addrspace(2) global float +// CHECK-DAG: @h = external addrspace(2) global %struct.__cblayout_S + +struct EmptyStruct { +}; + +struct S { + RWBuffer buf; + EmptyStruct es; + float ea[0]; + float b; +}; + +float a; +RWBuffer b; +EmptyStruct c; +float d[0]; +RWBuffer e[2]; +groupshared float f; +float g; +S h; + +RWBuffer Buf; + +[numthreads(4,1,1)] +void main() { + Buf[0] = a; +} + +// CHECK: !hlsl.cblayouts = !{![[S_LAYOUT:.*]], ![[CB_LAYOUT:.*]]} +// CHECK: !hlsl.cbs = !{![[CB:.*]]} + +// CHECK: ![[S_LAYOUT]] = !{!"struct.__cblayout_S", i32 4, i32 0} +// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_$Globals", i32 20, i32 0, i32 4, i32 16} +// CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @g, ptr addrspace(2) @h} \ No newline at end of file From 648548220abd23fe716fdc9a6b87257f82234ff5 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 4 Feb 2025 22:21:11 -0800 Subject: [PATCH 10/19] clang-format --- clang/lib/AST/Decl.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index eede936577f09..4eb648ce95f25 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5755,9 +5755,8 @@ HLSLBufferDecl::CreateDefaultCBuffer(ASTContext &C, HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { - return new (C, ID) - HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr, - SourceLocation(), SourceLocation()); + return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr, + SourceLocation(), SourceLocation()); } void HLSLBufferDecl::addLayoutStruct(CXXRecordDecl *LS) { From 991ba1d81364fe21da9678d0db94c551abfe853a Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 5 Feb 2025 10:21:04 -0800 Subject: [PATCH 11/19] whitespace change so I can force-push the branch to unblock stuck PR --- clang/lib/Sema/SemaHLSL.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 977185fcf9997..72485322fc288 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1924,7 +1924,6 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D, } // namespace void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) { - // process default CBuffer - create buffer layout struct and invoke codegenCGH if (DefaultCBuffer) { SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer); @@ -1933,7 +1932,6 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) { DeclGroupRef DG(DefaultCBuffer); SemaRef.Consumer.HandleTopLevelDecl(DG); } - diagnoseAvailabilityViolations(TU); } From b62863b900dd63eb1c8e3ffe656bbcbb6770dbdd Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 11 Feb 2025 20:32:51 -0800 Subject: [PATCH 12/19] Rework cbuffer codegen to use the target("dx.Layout", ...) type --- clang/include/clang/AST/Decl.h | 6 +- clang/lib/AST/Decl.cpp | 2 +- clang/lib/CodeGen/CGHLSLRuntime.cpp | 226 +++++------------- clang/lib/CodeGen/CGHLSLRuntime.h | 20 +- clang/lib/CodeGen/CMakeLists.txt | 1 + clang/lib/CodeGen/TargetInfo.h | 4 +- clang/lib/CodeGen/Targets/DirectX.cpp | 42 +++- clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp | 201 ++++++++++++++++ clang/lib/CodeGen/Targets/HLSLTargetInfo.h | 39 +++ clang/lib/CodeGen/Targets/SPIR.cpp | 16 +- clang/lib/Sema/SemaHLSL.cpp | 2 +- clang/test/CodeGenHLSL/cbuffer.hlsl | 120 +++++----- .../CodeGenHLSL/cbuffer_and_namespaces.hlsl | 23 +- .../CodeGenHLSL/cbuffer_and_namespaces.ll | 49 ++++ .../CodeGenHLSL/cbuffer_with_packoffset.hlsl | 10 +- ...uffer_with_static_global_and_function.hlsl | 7 +- 16 files changed, 482 insertions(+), 286 deletions(-) create mode 100644 clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp create mode 100644 clang/lib/CodeGen/Targets/HLSLTargetInfo.h create mode 100644 clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 05e56978977f2..85cef38bc03db 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -5034,7 +5034,7 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { bool IsCBuffer; /// HasValidPackoffset - Whether the buffer has valid packoffset annotations // on all declarations - bool HasPackoffset; + bool HasValidPackoffset; // LayoutStruct - Layout struct for the buffer CXXRecordDecl *LayoutStruct; @@ -5057,8 +5057,8 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { SourceLocation getRBraceLoc() const { return RBraceLoc; } void setRBraceLoc(SourceLocation L) { RBraceLoc = L; } bool isCBuffer() const { return IsCBuffer; } - void setHasPackoffset(bool PO) { HasPackoffset = PO; } - bool hasPackoffset() const { return HasPackoffset; } + void setHasValidPackoffset(bool PO) { HasValidPackoffset = PO; } + bool hasValidPackoffset() const { return HasValidPackoffset; } const CXXRecordDecl *getLayoutStruct() const { return LayoutStruct; } void addLayoutStruct(CXXRecordDecl *LS); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 4c5201d0ba2ee..95b25164d63dc 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5717,7 +5717,7 @@ HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation IDLoc, SourceLocation LBrace) : NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)), DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc), - IsCBuffer(CBuffer), HasPackoffset(false), LayoutStruct(nullptr) {} + IsCBuffer(CBuffer), HasValidPackoffset(false), LayoutStruct(nullptr) {} HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C, DeclContext *LexicalParent, bool CBuffer, diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 52247173b6990..fde801dd1a8a4 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Type.h" #include "clang/Basic/TargetOptions.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/LLVMContext.h" @@ -70,11 +71,14 @@ void addDisableOptimizations(llvm::Module &M) { } // namespace -llvm::Type *CGHLSLRuntime::convertHLSLSpecificType(const Type *T) { +llvm::Type * +CGHLSLRuntime::convertHLSLSpecificType(const Type *T, + SmallVector *Packoffsets) { assert(T->isHLSLSpecificType() && "Not an HLSL specific type!"); // Check if the target has a specific translation for this type first. - if (llvm::Type *TargetTy = CGM.getTargetCodeGenInfo().getHLSLType(CGM, T)) + if (llvm::Type *TargetTy = + CGM.getTargetCodeGenInfo().getHLSLType(CGM, T, Packoffsets)) return TargetTy; llvm_unreachable("Generic handling of HLSL types is not supported."); @@ -96,153 +100,28 @@ static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) { return isResourceRecordType(Ty); } -static ConstantAsMetadata *getConstIntMetadata(LLVMContext &Ctx, uint32_t value, - bool isSigned = false) { - return ConstantAsMetadata::get( - ConstantInt::get(Ctx, llvm::APInt(32, value, isSigned))); -} - -static unsigned getScalarOrVectorSize(llvm::Type *Ty) { - assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy()); - if (Ty->isVectorTy()) { - llvm::FixedVectorType *FVT = cast(Ty); - return FVT->getNumElements() * - (FVT->getElementType()->getScalarSizeInBits() / 8); - } - return Ty->getScalarSizeInBits() / 8; -} - -// Returns size of a struct in constant buffer layout. The sizes are cached -// in StructSizesForBuffer map. The map is also an indicator if a layout -// metadata for this struct has been added to the module. -// If the struct type is not in the map, this method will calculate the struct -// layout, add a metadata node describing it to the module, and add the struct -// size to the map. -size_t -CGHLSLRuntime::getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy) { - // check if we already have a side for this struct - auto SizeIt = StructSizesForBuffer.find(StructTy); - if (SizeIt != StructSizesForBuffer.end()) - return SizeIt->getSecond(); - - // if not, calculate the struct layout and create a metadata node - LLVMContext &Ctx = CGM.getLLVMContext(); - SmallVector LayoutItems; - - // start metadata list with a struct name and reserve one slot for its size - LayoutItems.push_back(MDString::get(Ctx, StructTy->getName())); - LayoutItems.push_back(nullptr); - - // add element offsets - size_t StructSize = 0; - for (llvm::Type *ElTy : StructTy->elements()) { - size_t Offset = calculateBufferElementOffset(ElTy, &StructSize); - LayoutItems.push_back(getConstIntMetadata(CGM.getLLVMContext(), Offset)); - } - // set the size of the buffer to the reserved slot - LayoutItems[1] = getConstIntMetadata(Ctx, StructSize); - - // add the struct layout to metadata - CGM.getModule() - .getOrInsertNamedMetadata("hlsl.cblayouts") - ->addOperand(MDNode::get(CGM.getLLVMContext(), LayoutItems)); - - // add struct size to list and return it - StructSizesForBuffer[StructTy] = StructSize; - return StructSize; -} - -// Calculates offset of a single element in constant buffer layout. -// The provided LayoutEndOffset marks the end of the layout so far (end offset -// of the buffer or struct). After the element offset calculations are done it -// will be updated the new end of layout value. -// If the PackoffsetAttrs is not nullptr the offset will be based on the -// packoffset annotation. -size_t CGHLSLRuntime::calculateBufferElementOffset( - llvm::Type *LayoutTy, size_t *LayoutEndOffset, - HLSLPackOffsetAttr *PackoffsetAttr) { - - size_t ElemOffset = 0; - size_t ElemSize = 0; - size_t ArrayCount = 1; - size_t ArrayStride = 0; - size_t EndOffset = *LayoutEndOffset; - size_t NextRowOffset = llvm::alignTo(EndOffset, 16U); - - if (LayoutTy->isArrayTy()) { - llvm::Type *Ty = LayoutTy; - while (Ty->isArrayTy()) { - ArrayCount *= Ty->getArrayNumElements(); - Ty = Ty->getArrayElementType(); - } - ElemSize = - Ty->isStructTy() - ? getOrCalculateStructSizeForBuffer(cast(Ty)) - : getScalarOrVectorSize(Ty); - ArrayStride = llvm::alignTo(ElemSize, 16U); - ElemOffset = - PackoffsetAttr ? PackoffsetAttr->getOffsetInBytes() : NextRowOffset; - - } else if (LayoutTy->isStructTy()) { - ElemOffset = - PackoffsetAttr ? PackoffsetAttr->getOffsetInBytes() : NextRowOffset; - ElemSize = - getOrCalculateStructSizeForBuffer(cast(LayoutTy)); - - } else { - size_t Align = 0; - if (LayoutTy->isVectorTy()) { - llvm::FixedVectorType *FVT = cast(LayoutTy); - size_t SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8; - ElemSize = FVT->getNumElements() * SubElemSize; - Align = SubElemSize; - } else { - assert(LayoutTy->isIntegerTy() || LayoutTy->isFloatingPointTy()); - ElemSize = LayoutTy->getScalarSizeInBits() / 8; - Align = ElemSize; - } - if (PackoffsetAttr) { - ElemOffset = PackoffsetAttr->getOffsetInBytes(); - } else { - ElemOffset = llvm::alignTo(EndOffset, Align); - // if the element does not fit, move it to the next row - if (ElemOffset + ElemSize > NextRowOffset) - ElemOffset = NextRowOffset; - } - } - - // Update end offset of the buffer/struct layout; do not update it if - // the provided EndOffset is already bigger than the new one value - // (which may happen with packoffset annotations) - unsigned NewEndOffset = - ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; - *LayoutEndOffset = std::max(EndOffset, NewEndOffset); - - return ElemOffset; -} - -// Emits constant global variables for buffer declarations, creates metadata -// linking the constant globals with the buffer. Also calculates the buffer -// layout and creates metadata node describing it. +// Emits constant global variables for buffer constants declarations +// and creates metadata linking the constant globals with the buffer global. void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *BufGV) { LLVMContext &Ctx = CGM.getLLVMContext(); + + // get the layout struct from constant buffer target type + llvm::Type *BufType = BufGV->getValueType(); + assert(isa(BufType) && + "expected target type for HLSL buffer resource"); + llvm::Type *BufLayoutType = + cast(BufType)->getTypeParameter(0); + assert(isa(BufLayoutType) && + "expected target type for buffer layout struct"); llvm::StructType *LayoutStruct = cast( - cast(BufGV->getValueType())->getTypeParameter(0)); + cast(BufLayoutType)->getTypeParameter(0)); // Start metadata list associating the buffer global variable with its // constatns SmallVector BufGlobals; BufGlobals.push_back(ValueAsMetadata::get(BufGV)); - // Start layout metadata list with a struct name and reserve one slot for - // the buffer size - SmallVector LayoutItems; - LayoutItems.push_back(MDString::get(Ctx, LayoutStruct->getName())); - LayoutItems.push_back(nullptr); - - size_t BufferSize = 0; - bool UsePackoffset = BufDecl->hasPackoffset(); const auto *ElemIt = LayoutStruct->element_begin(); for (Decl *D : BufDecl->decls()) { if (isa(D)) @@ -275,17 +154,6 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, "number of elements in layout struct does not match"); llvm::Type *LayoutType = *ElemIt++; - // Make sure the type of the VarDecl type matches the type of the layout - // struct element, or that it is a layout struct with the same name - assert((CGM.getTypes().ConvertTypeForMem(VDTy) == LayoutType || - (LayoutType->isStructTy() && - cast(LayoutType) - ->getName() - .starts_with(("struct.__cblayout_" + - VDTy->getAsCXXRecordDecl()->getName()) - .str()))) && - "layout type does not match the converted element type"); - // there might be resources inside the used defined structs if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType()) // FIXME: handle resources in cbuffer structs @@ -295,29 +163,15 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, GlobalVariable *ElemGV = cast(CGM.GetAddrOfGlobalVar(VD, LayoutType)); BufGlobals.push_back(ValueAsMetadata::get(ElemGV)); - - // get offset of the global and and to metadata list - assert(((UsePackoffset && VD->hasAttr()) || - !UsePackoffset) && - "expected packoffset attribute on every declaration"); - size_t Offset = calculateBufferElementOffset( - LayoutType, &BufferSize, - UsePackoffset ? VD->getAttr() : nullptr); - LayoutItems.push_back(getConstIntMetadata(Ctx, Offset)); } assert(ElemIt == LayoutStruct->element_end() && "number of elements in layout struct does not match"); // set the size of the buffer - LayoutItems[1] = getConstIntMetadata(Ctx, BufferSize); // add buffer metadata to the module CGM.getModule() .getOrInsertNamedMetadata("hlsl.cbs") ->addOperand(MDNode::get(Ctx, BufGlobals)); - - CGM.getModule() - .getOrInsertNamedMetadata("hlsl.cblayouts") - ->addOperand(MDNode::get(Ctx, LayoutItems)); } // Creates resource handle type for the HLSL buffer declaration @@ -331,6 +185,25 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) { return cast(QT.getTypePtr()); } +static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl, + SmallVector &Layout) { + assert(Layout.empty() && "expected empty vector for layout"); + assert(BufDecl->hasValidPackoffset()); + + for (Decl *D : BufDecl->decls()) { + if (isa(D) || isa(D)) { + continue; + } + VarDecl *VD = dyn_cast(D); + if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant) + continue; + assert(VD->hasAttr() && + "expected packoffset attribute on every declaration"); + size_t Offset = VD->getAttr()->getOffsetInBytes(); + Layout.push_back(Offset); + } +} + // Codegen for HLSLBufferDecl void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { @@ -345,16 +218,20 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { return; // create global variable for the constant buffer - llvm::Module &M = CGM.getModule(); + SmallVector Layout; + if (BufDecl->hasValidPackoffset()) + fillPackoffsetLayout(BufDecl, Layout); + llvm::TargetExtType *TargetTy = - cast(convertHLSLSpecificType(ResHandleTy)); + cast(convertHLSLSpecificType( + ResHandleTy, BufDecl->hasValidPackoffset() ? &Layout : nullptr)); llvm::GlobalVariable *BufGV = new GlobalVariable(TargetTy, /*isConstant*/ true, GlobalValue::LinkageTypes::ExternalLinkage, nullptr, llvm::formatv("{0}{1}", BufDecl->getName(), BufDecl->isCBuffer() ? ".cb" : ".tb"), GlobalValue::NotThreadLocal); - M.insertGlobalVariable(BufGV); + CGM.getModule().insertGlobalVariable(BufGV); // Add globals for constant buffer elements and create metadata nodes emitBufferGlobalsAndMetadata(BufDecl, BufGV); @@ -368,6 +245,21 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { RBA->getSpaceNumber()); } +llvm::Type * +CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) { + const auto Entry = LayoutTypes.find(StructType); + if (Entry != LayoutTypes.end()) + return Entry->getSecond(); + return nullptr; +} + +void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType, + llvm::Type *LayoutTy) { + assert(getHLSLBufferLayoutType(StructType) == nullptr && + "layout type for this struct already exist"); + LayoutTypes[StructType] = LayoutTy; +} + void CGHLSLRuntime::finishCodeGen() { auto &TargetOpts = CGM.getTarget().getTargetOpts(); llvm::Module &M = CGM.getModule(); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 8658cb8653fb4..6d2054e9efd50 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -63,6 +63,7 @@ class ParmVarDecl; class HLSLBufferDecl; class HLSLResourceBindingAttr; class Type; +class RecordType; class DeclContext; class HLSLPackOffsetAttr; @@ -141,7 +142,9 @@ class CGHLSLRuntime { CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {} virtual ~CGHLSLRuntime() {} - llvm::Type *convertHLSLSpecificType(const Type *T); + llvm::Type * + convertHLSLSpecificType(const Type *T, + SmallVector *Packoffsets = nullptr); void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV); void generateGlobalCtorDtorCalls(); @@ -157,6 +160,10 @@ class CGHLSLRuntime { llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB); + llvm::Type *getHLSLBufferLayoutType(const RecordType *LayoutStructTy); + void addHLSLBufferLayoutType(const RecordType *LayoutStructTy, + llvm::Type *LayoutTy); + private: void addBufferResourceAnnotation(llvm::GlobalVariable *GV, llvm::hlsl::ResourceClass RC, @@ -165,18 +172,9 @@ class CGHLSLRuntime { BufferResBinding &Binding); void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *BufGV); - - size_t - calculateBufferElementOffset(llvm::Type *LayoutTy, size_t *LayoutEndOffset, - HLSLPackOffsetAttr *PackoffsetAttr = nullptr); - - size_t getOrCalculateStructSizeForBuffer(llvm::StructType *StructTy); - llvm::Triple::ArchType getArch(); - // Sizes of structs in constant buffer layout. Structs in the map - // had their layout calculated and added to the module as metadata. - llvm::DenseMap StructSizesForBuffer; + llvm::DenseMap LayoutTypes; }; } // namespace CodeGen diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 868ec847b9634..1a6b25883d9c8 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -124,6 +124,7 @@ add_clang_library(clangCodeGen Targets/CSKY.cpp Targets/DirectX.cpp Targets/Hexagon.cpp + Targets/HLSLTargetInfo.cpp Targets/Lanai.cpp Targets/LoongArch.cpp Targets/M68k.cpp diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h index 4a66683a3b91f..86057c14a549e 100644 --- a/clang/lib/CodeGen/TargetInfo.h +++ b/clang/lib/CodeGen/TargetInfo.h @@ -439,7 +439,9 @@ class TargetCodeGenInfo { } /// Return an LLVM type that corresponds to a HLSL type - virtual llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *T) const { + virtual llvm::Type * + getHLSLType(CodeGenModule &CGM, const Type *T, + const SmallVector *Packoffsets = nullptr) const { return nullptr; } diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index ec7fa342d5846..3d4b65aec0a3a 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -7,8 +7,13 @@ //===----------------------------------------------------------------------===// #include "ABIInfoImpl.h" +#include "CodeGenModule.h" +#include "HLSLTargetInfo.h" #include "TargetInfo.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Type.h" using namespace clang; using namespace clang::CodeGen; @@ -19,16 +24,30 @@ using namespace clang::CodeGen; namespace { -class DirectXTargetCodeGenInfo : public TargetCodeGenInfo { +class DirectXTargetCodeGenInfo : public CommonHLSLTargetCodeGenInfo { public: DirectXTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) - : TargetCodeGenInfo(std::make_unique(CGT)) {} + : CommonHLSLTargetCodeGenInfo(std::make_unique(CGT)) {} - llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *T) const override; + llvm::Type *getHLSLType( + CodeGenModule &CGM, const Type *T, + const SmallVector *Packoffsets = nullptr) const override; + + llvm::Type *getHLSLLayoutType(CodeGenModule &CGM, + llvm::StructType *LayoutStructTy, + SmallVector Layout) const override; }; -llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, - const Type *Ty) const { +llvm::Type *DirectXTargetCodeGenInfo::getHLSLLayoutType( + CodeGenModule &CGM, llvm::StructType *LayoutStructTy, + SmallVector Layout) const { + return llvm::TargetExtType::get(CGM.getLLVMContext(), "dx.Layout", + {LayoutStructTy}, Layout); +} + +llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( + CodeGenModule &CGM, const Type *Ty, + const SmallVector *Packoffsets) const { auto *ResType = dyn_cast(Ty); if (!ResType) return nullptr; @@ -57,11 +76,16 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, return llvm::TargetExtType::get(Ctx, TypeName, {ElemType}, Ints); } case llvm::dxil::ResourceClass::CBuffer: { - QualType StructTy = ResType->getContainedType(); - if (StructTy.isNull()) + QualType ContainedTy = ResType->getContainedType(); + if (ContainedTy.isNull() || !ContainedTy->isStructureType()) return nullptr; - llvm::Type *Ty = CGM.getTypes().ConvertType(StructTy); - return llvm::TargetExtType::get(Ctx, "dx.CBuffer", {Ty}); + + llvm::Type *BufferLayoutTy = this->createHLSLBufferLayoutType( + CGM, ContainedTy->getAsStructureType(), Packoffsets); + if (!BufferLayoutTy) + return nullptr; + + return llvm::TargetExtType::get(Ctx, "dx.CBuffer", {BufferLayoutTy}); } case llvm::dxil::ResourceClass::Sampler: llvm_unreachable("dx.Sampler handles are not implemented yet"); diff --git a/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp b/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp new file mode 100644 index 0000000000000..e0e7f6daddcfa --- /dev/null +++ b/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp @@ -0,0 +1,201 @@ +//===- HLSLTargetInto.cpp--------------------------------------------------===// +// +// 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 "HLSLTargetInfo.h" +#include "CGHLSLRuntime.h" +#include "TargetInfo.h" +#include "clang/AST/DeclCXX.h" + +//===----------------------------------------------------------------------===// +// Target codegen info implementation common between DirectX and SPIR/SPIR-V. +//===----------------------------------------------------------------------===// + +namespace { + +// Creates a new array type with the same dimentions +// but with the new element type. +static llvm::Type * +createArrayWithNewElementType(CodeGenModule &CGM, + const ConstantArrayType *ArrayType, + llvm::Type *NewElemType) { + const clang::Type *ArrayElemType = ArrayType->getArrayElementTypeNoTypeQual(); + if (ArrayElemType->isConstantArrayType()) + NewElemType = createArrayWithNewElementType( + CGM, cast(ArrayElemType), NewElemType); + return llvm::ArrayType::get(NewElemType, ArrayType->getSExtSize()); +} + +// Returns the size of a scalar or vector in bytes/ +static unsigned getScalarOrVectorSize(llvm::Type *Ty) { + assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy()); + if (Ty->isVectorTy()) { + llvm::FixedVectorType *FVT = cast(Ty); + return FVT->getNumElements() * + (FVT->getElementType()->getScalarSizeInBits() / 8); + } + return Ty->getScalarSizeInBits() / 8; +} + +} // namespace + +// Creates a layout type for given struct with HLSL constant buffer layout +// taking into account Packoffsets, if provided. +// Previously created layout types are cached in CGHLSLRuntime because +// TargetCodeGenInto info is cannot store any data +// (CGM.getTargetCodeGenInfo() returns a const reference to TargetCondegenInfo). +// +// The function iterates over all fields of the StructType (including base +// classes), converts each field to its corresponding LLVM type and calculated +// it's HLSL constant bufffer layout (offset and size). Any embedded struct (or +// arrays of structs) are converted to target layout types as well. +llvm::Type *CommonHLSLTargetCodeGenInfo::createHLSLBufferLayoutType( + CodeGenModule &CGM, const RecordType *StructType, + const SmallVector *Packoffsets) const { + + // check if we already have the layout type for this struct + if (llvm::Type *Ty = CGM.getHLSLRuntime().getHLSLBufferLayoutType(StructType)) + return Ty; + + SmallVector Layout; + SmallVector LayoutElements; + unsigned Index = 0; // packoffset index + unsigned EndOffset = 0; + + // reserve first spot in the layout vector for buffer size + Layout.push_back(0); + + // iterate over all fields of the record, including fields on base classes + llvm::SmallVector RecordTypes; + RecordTypes.push_back(StructType); + while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { + CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); + assert(D->getNumBases() == 1 && + "HLSL doesn't support multiple inheritance"); + RecordTypes.push_back(D->bases_begin()->getType()->getAs()); + } + while (!RecordTypes.empty()) { + const RecordType *RT = RecordTypes.back(); + RecordTypes.pop_back(); + + for (const auto *FD : RT->getDecl()->fields()) { + assert(!Packoffsets || Index < Packoffsets->size() && + "number of elements in layout struct does not " + "match number of packoffset annotations"); + // Size of element; for arrays this is a size of a single element in the + // array. Total array size of calculated as (ArrayCount-1) * ArrayStride + + // ElemSize. + unsigned ElemSize = 0; + + unsigned ElemOffset = 0; + unsigned ArrayCount = 1; + unsigned ArrayStride = 0; + unsigned NextRowOffset = llvm::alignTo(EndOffset, 16U); + llvm::Type *ElemLayoutTy = nullptr; + + QualType FieldTy = FD->getType(); + + if (FieldTy->isConstantArrayType()) { + // Unwrap array to find the element type and get combined array size. + QualType Ty = FieldTy; + while (Ty->isConstantArrayType()) { + const ConstantArrayType *ArrayTy = cast(Ty); + ArrayCount *= ArrayTy->getSExtSize(); + Ty = ArrayTy->getElementType(); + } + // For array of structures, create a new array with a layout type + // instead of the structure type. + if (Ty->isStructureType()) { + llvm::Type *NewTy = cast( + createHLSLBufferLayoutType(CGM, Ty->getAsStructureType())); + if (!NewTy) + return nullptr; + assert(isa(NewTy) && "expected target type"); + ElemSize = cast(NewTy)->getIntParameter(0); + ElemLayoutTy = createArrayWithNewElementType( + CGM, cast(FieldTy.getTypePtr()), NewTy); + } else { + // Array of vectors or scalars + ElemSize = + getScalarOrVectorSize(CGM.getTypes().ConvertTypeForMem(Ty)); + ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); + } + ArrayStride = llvm::alignTo(ElemSize, 16U); + ElemOffset = + Packoffsets != nullptr ? (*Packoffsets)[Index] : NextRowOffset; + + } else if (FieldTy->isStructureType()) { + // Create a layout type for the structure + ElemLayoutTy = + createHLSLBufferLayoutType(CGM, FieldTy->getAsStructureType()); + if (!ElemLayoutTy) + return nullptr; + assert(isa(ElemLayoutTy) && + "expected target type"); + ElemSize = cast(ElemLayoutTy)->getIntParameter(0); + ElemOffset = + Packoffsets != nullptr ? (*Packoffsets)[Index] : NextRowOffset; + } else { + // scalar or vector - find element size and alignment + unsigned Align = 0; + ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); + if (ElemLayoutTy->isVectorTy()) { + // align vectors by sub element size + const llvm::FixedVectorType *FVT = + cast(ElemLayoutTy); + unsigned SubElemSize = + FVT->getElementType()->getScalarSizeInBits() / 8; + ElemSize = FVT->getNumElements() * SubElemSize; + Align = SubElemSize; + } else { + assert(ElemLayoutTy->isIntegerTy() || + ElemLayoutTy->isFloatingPointTy()); + ElemSize = ElemLayoutTy->getScalarSizeInBits() / 8; + Align = ElemSize; + } + // calculate or get element offset for the vector or scalar + if (Packoffsets != nullptr) { + ElemOffset = (*Packoffsets)[Index]; + } else { + ElemOffset = llvm::alignTo(EndOffset, Align); + // if the element does not fit, move it to the next row + if (ElemOffset + ElemSize > NextRowOffset) + ElemOffset = NextRowOffset; + } + } + + // Update end offset of the layout; do not update it if the EndOffset + // is already bigger than the new value (which may happen with unordered + // packoffset annotations) + unsigned NewEndOffset = + ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; + EndOffset = std::max(EndOffset, NewEndOffset); + + // add the layout element and offset to the lists + Layout.push_back(ElemOffset); + LayoutElements.push_back(ElemLayoutTy); + Index++; + } + } + + // set the size of the buffer + Layout[0] = EndOffset; + + // create the layout struct type; anonymous struct have empty name but + // non-empty qualified name + const CXXRecordDecl *Decl = StructType->getAsCXXRecordDecl(); + std::string Name = + Decl->getName().empty() ? "anon" : Decl->getQualifiedNameAsString(); + llvm::StructType *StructTy = + llvm::StructType::create(LayoutElements, Name, true); + + // create target layout type + llvm::Type *NewLayoutTy = this->getHLSLLayoutType(CGM, StructTy, Layout); + if (NewLayoutTy) + CGM.getHLSLRuntime().addHLSLBufferLayoutType(StructType, NewLayoutTy); + return NewLayoutTy; +} diff --git a/clang/lib/CodeGen/Targets/HLSLTargetInfo.h b/clang/lib/CodeGen/Targets/HLSLTargetInfo.h new file mode 100644 index 0000000000000..279ce4fba3dad --- /dev/null +++ b/clang/lib/CodeGen/Targets/HLSLTargetInfo.h @@ -0,0 +1,39 @@ +//===- HLSLTargetInfo.h ---------------------------------------------------===// +// +// 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 "ABIInfoImpl.h" +#include "TargetInfo.h" + +using namespace clang; +using namespace clang::CodeGen; + +//===----------------------------------------------------------------------===// +// Target codegen info implementation common between DirectX and SPIR/SPIR-V. +//===----------------------------------------------------------------------===// + +class CommonHLSLTargetCodeGenInfo : public TargetCodeGenInfo { +public: + CommonHLSLTargetCodeGenInfo(std::unique_ptr Info) + : TargetCodeGenInfo(std::move(Info)) {} + + // Returns LLVM target extension type "dx.Layout" or "spv.Layout" + // for given structure type and layout data. The first number in + // the Layout is the size followed by offsets for each struct element. + virtual llvm::Type *getHLSLLayoutType(CodeGenModule &CGM, + llvm::StructType *LayoutStructTy, + SmallVector Layout) const { + return nullptr; + }; + +protected: + // Creates a layout type for given struct with HLSL constant buffer layout + // taking into account Packoffsets, if provided. + virtual llvm::Type *createHLSLBufferLayoutType( + CodeGenModule &CGM, const clang::RecordType *StructType, + const SmallVector *Packoffsets = nullptr) const; +}; diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index 5c75e985e953d..a1d8ffbf73284 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ABIInfoImpl.h" +#include "HLSLTargetInfo.h" #include "TargetInfo.h" using namespace clang; @@ -38,12 +39,12 @@ class SPIRVABIInfo : public CommonSPIRABIInfo { }; } // end anonymous namespace namespace { -class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo { +class CommonSPIRTargetCodeGenInfo : public CommonHLSLTargetCodeGenInfo { public: CommonSPIRTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) - : TargetCodeGenInfo(std::make_unique(CGT)) {} + : CommonHLSLTargetCodeGenInfo(std::make_unique(CGT)) {} CommonSPIRTargetCodeGenInfo(std::unique_ptr ABIInfo) - : TargetCodeGenInfo(std::move(ABIInfo)) {} + : CommonHLSLTargetCodeGenInfo(std::move(ABIInfo)) {} LangAS getASTAllocaAddressSpace() const override { return getLangASFromTargetAS( @@ -52,7 +53,9 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo { unsigned getOpenCLKernelCallingConv() const override; llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override; - llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override; + llvm::Type *getHLSLType( + CodeGenModule &CGM, const Type *Ty, + const SmallVector *Packoffsets = nullptr) const override; llvm::Type *getSPIRVImageTypeFromHLSLResource( const HLSLAttributedResourceType::Attributes &attributes, llvm::Type *ElementType, llvm::LLVMContext &Ctx) const; @@ -364,8 +367,9 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM, return nullptr; } -llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM, - const Type *Ty) const { +llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType( + CodeGenModule &CGM, const Type *Ty, + const SmallVector *Packoffsets) const { auto *ResType = dyn_cast(Ty); if (!ResType) return nullptr; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 1537a88731892..c011b46d11ac1 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -261,7 +261,7 @@ static void validatePackoffset(Sema &S, HLSLBufferDecl *BufDecl) { IsValid = false; } } - BufDecl->setHasPackoffset(IsValid); + BufDecl->setHasValidPackoffset(IsValid); } // Returns true if the array has a zero size = if any of the dimensions is 0 diff --git a/clang/test/CodeGenHLSL/cbuffer.hlsl b/clang/test/CodeGenHLSL/cbuffer.hlsl index 9a3f632e859c7..38093c6dfacd7 100644 --- a/clang/test/CodeGenHLSL/cbuffer.hlsl +++ b/clang/test/CodeGenHLSL/cbuffer.hlsl @@ -1,14 +1,24 @@ // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \ // RUN: -fnative-half-type -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -// CHECK: %struct.__cblayout_CBScalars = type <{ float, double, half, i64, i32, i16, i32, i64 }> -// CHECK: %struct.__cblayout_CBVectors = type <{ <3 x float>, <3 x double>, <2 x half>, <3 x i64>, <4 x i32>, <3 x i16>, <3 x i64> }> -// CHECK: %struct.__cblayout_CBArrays = type <{ [3 x float], [2 x <3 x double>], [2 x [2 x half]], [3 x i64], [2 x [3 x [4 x <4 x i32>]]], [1 x i16], [2 x i64], [4 x i32] }> -// CHECK: %struct.__cblayout_CBStructs = type { %struct.A, %struct.B, %struct.C, [5 x %struct.A], %struct.__cblayout_D, half, %struct.B, <3 x i16> } -// CHECK: %struct.A = type { <2 x float> } -// CHECK: %struct.C = type { i32, %struct.A } -// CHECK: %struct.__cblayout_D = type { [2 x [3 x %struct.B]] } -// CHECK: %struct.B = type { %struct.A, <3 x i16> } +// CHECK: %__cblayout_CBScalars = type <{ float, double, half, i64, i32, i16, i32, i64 }> +// CHECK: %__cblayout_CBVectors = type <{ <3 x float>, <3 x double>, <2 x half>, <3 x i64>, <4 x i32>, <3 x i16>, <3 x i64> }> +// CHECK: %__cblayout_CBArrays = type <{ [3 x float], [2 x <3 x double>], [2 x [2 x half]], [3 x i64], [2 x [3 x [4 x <4 x i32>]]], [1 x i16], [2 x i64], [4 x i32] }> +// CHECK: %__cblayout_CBStructs = type <{ target("dx.Layout", %A, 8, 0), target("dx.Layout", %B, 14, 0, 8), +// CHECK-SAME: target("dx.Layout", %C, 24, 0, 16), [5 x target("dx.Layout", %A, 8, 0)], +// CHECK-SAME: target("dx.Layout", %__cblayout_D, 94, 0), half, <3 x i16> }> + +// CHECK: %A = type <{ <2 x float> }> +// CHECK: %B = type <{ <2 x float>, <3 x i16> }> +// CHECK: %C = type <{ i32, target("dx.Layout", %A, 8, 0) }> +// CHECK: %__cblayout_D = type <{ [2 x [3 x target("dx.Layout", %B, 14, 0, 8)]] }> + +// CHECK: %__cblayout_CBMix = type <{ [2 x target("dx.Layout", %Test, 8, 0, 4)], float, [3 x [2 x <2 x float>]], float, +// CHECK-SAME: target("dx.Layout", %anon, 4, 0), double, target("dx.Layout", %anon.0, 8, 0), float, <1 x double>, i16 }> + +// CHECK: %Test = type <{ float, float }> +// CHECK: %anon = type <{ float }> +// CHECK: %anon.0 = type <{ <2 x i32> }> cbuffer CBScalars : register(b1, space5) { float a1; @@ -21,7 +31,8 @@ cbuffer CBScalars : register(b1, space5) { int64_t a8; } -// CHECK: @CBScalars.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBScalars) +// CHECK: @CBScalars.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, +// CHECK-SAME: 56, 0, 8, 16, 24, 32, 36, 40, 48)) // CHECK: @a1 = external addrspace(2) global float, align 4 // CHECK: @a2 = external addrspace(2) global double, align 8 // CHECK: @a3 = external addrspace(2) global half, align 2 @@ -39,10 +50,11 @@ cbuffer CBVectors { int4 b5; uint16_t3 b6; int64_t3 b7; - // FIXME: add s bool vectors after llvm-project/llvm#91639 is added + // FIXME: add a bool vectors after llvm-project/llvm#91639 is added } -// CHECK: @CBVectors.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBVectors) +// CHECK: @CBVectors.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBVectors, +// CHECK-SAME: 136, 0, 16, 40, 48, 80, 96, 112)) // CHECK: @b1 = external addrspace(2) global <3 x float>, align 16 // CHECK: @b2 = external addrspace(2) global <3 x double>, align 32 // CHECK: @b3 = external addrspace(2) global <2 x half>, align 4 @@ -62,7 +74,8 @@ cbuffer CBArrays : register(b2) { bool c8[4]; } -// CHECK: @CBArrays.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBArrays) +// CHECK: @CBArrays.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, +// CHECK-SAME: 708, 0, 48, 112, 176, 224, 608, 624, 656)) // CHECK: @c1 = external addrspace(2) global [3 x float], align 4 // CHECK: @c2 = external addrspace(2) global [2 x <3 x double>], align 32 // CHECK: @c3 = external addrspace(2) global [2 x [2 x half]], align 2 @@ -92,13 +105,15 @@ struct D { Empty es; }; -// CHECK: @CBStructs.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBStructs) -// CHECK: @a = external addrspace(2) global %struct.A, align 8 -// CHECK: @b = external addrspace(2) global %struct.B, align 8 -// CHECK: @c = external addrspace(2) global %struct.C, align 8 -// CHECK: @array_of_A = external addrspace(2) global [5 x %struct.A], align 8 -// CHECK: @d = external addrspace(2) global %struct.__cblayout_D, align 8 +// CHECK: @CBStructs.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBStructs, +// CHECK-SAME: 246, 0, 16, 32, 64, 144, 238, 240)) +// CHECK: @a = external addrspace(2) global target("dx.Layout", %A, 8, 0), align 8 +// CHECK: @b = external addrspace(2) global target("dx.Layout", %B, 14, 0, 8), align 8 +// CHECK: @c = external addrspace(2) global target("dx.Layout", %C, 24, 0, 16), align 8 +// CHECK: @array_of_A = external addrspace(2) global [5 x target("dx.Layout", %A, 8, 0)], align 8 +// CHECK: @d = external addrspace(2) global target("dx.Layout", %__cblayout_D, 94, 0), align 8 // CHECK: @e = external addrspace(2) global half, align 2 +// CHECK: @f = external addrspace(2) global <3 x i16>, align 8 cbuffer CBStructs { A a; @@ -107,55 +122,56 @@ cbuffer CBStructs { A array_of_A[5]; D d; half e; - B f; - uint16_t3 g; + uint16_t3 f; }; struct Test { float a, b; }; -// CHECK: @CBMix.cb = external constant target("dx.CBuffer", %struct.__cblayout_CBMix) -// CHECK: @test = external addrspace(2) global [2 x %struct.Test], align 4 +// CHECK: @CBMix.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CBMix, +// CHECK-SAME: 170, 0, 24, 32, 120, 128, 136, 144, 152, 160, 168)) +// CHECK: @test = external addrspace(2) global [2 x target("dx.Layout", %Test, 8, 0, 4)], align 4 // CHECK: @f1 = external addrspace(2) global float, align 4 // CHECK: @f2 = external addrspace(2) global [3 x [2 x <2 x float>]], align 8 // CHECK: @f3 = external addrspace(2) global float, align 4 -// CHECK: @s = external addrspace(2) global %struct.anon, align 4 -// CHECK: @dd = external addrspace(2) global double, align 8 -// CHECK: @f4 = external addrspace(2) global float, align 4 -// CHECK: @dv = external addrspace(2) global <1 x double>, align 8 -// CHECK: @uv = external addrspace(2) global i16, align 2 +// CHECK: @f4 = external addrspace(2) global target("dx.Layout", %anon, 4, 0), align 4 +// CHECK: @f5 = external addrspace(2) global double, align 8 +// CHECK: @f6 = external addrspace(2) global target("dx.Layout", %anon.0, 8, 0), align 8 +// CHECK: @f7 = external addrspace(2) global float, align 4 +// CHECK: @f8 = external addrspace(2) global <1 x double>, align 8 +// CHECK: @f9 = external addrspace(2) global i16, align 2 cbuffer CBMix { Test test[2]; float f1; float2 f2[3][2]; float f3; - struct { float c; } s; - double dd; - float f4; - vector dv; - uint16_t uv; + struct { float c; } f4; + double f5; + struct { int2 i; } f6; + float f7; + vector f8; + uint16_t f9; }; -// CHECK: efine internal void @_init_resource_CBScalars.cb() +// CHECK: define internal void @_init_resource_CBScalars.cb() // CHECK-NEXT: entry: -// CHECK-NEXT: %[[HANDLE1:.*]] = call target("dx.CBuffer", %struct.__cblayout_CBScalars) -// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBScalarsst(i32 5, i32 1, i32 1, i32 0, i1 false) -// CHECK-NEXT: store target("dx.CBuffer", %struct.__cblayout_CBScalars) %[[HANDLE1]], ptr @CBScalars.cb, align 4 +// CHECK-NEXT: %[[HANDLE1:.*]] = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 56, 0, 8, 16, 24, 32, 36, 40, 48)) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBScalarss_56_0_8_16_24_32_36_40_48tt(i32 5, i32 1, i32 1, i32 0, i1 false) +// CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", %__cblayout_CBScalars, 56, 0, 8, 16, 24, 32, 36, 40, 48)) %CBScalars.cb_h, ptr @CBScalars.cb, align 4 // CHECK: define internal void @_init_resource_CBArrays.cb() // CHECK-NEXT: entry: -// CHECK-NEXT: %[[HANDLE2:.*]] = call target("dx.CBuffer", %struct.__cblayout_CBArrays) -// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBArraysst(i32 0, i32 2, i32 1, i32 0, i1 false) -// CHECK-NEXT: store target("dx.CBuffer", %struct.__cblayout_CBArrays) %[[HANDLE2]], ptr @CBArrays.cb, align 4 +// CHECK-NEXT: %[[HANDLE2:.*]] = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, 708, 0, 48, 112, 176, 224, 608, 624, 656)) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBArrayss_708_0_48_112_176_224_608_624_656tt(i32 0, i32 2, i32 1, i32 0, i1 false) +// CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", %__cblayout_CBArrays, 708, 0, 48, 112, 176, 224, 608, 624, 656)) %CBArrays.cb_h, ptr @CBArrays.cb, align 4 RWBuffer Buf; [numthreads(4,1,1)] void main() { - //Buf[0] = a1 + b1.z + c1[2] + a.f1.y; - Buf[0] = a.f1.y; + Buf[0] = a1 + b1.z + c1[2] + a.f1.y + f1; } // CHECK: define internal void @_GLOBAL__sub_I_cbuffer.hlsl() @@ -165,10 +181,6 @@ void main() { // CHECK: !hlsl.cbs = !{![[CBSCALARS:[0-9]+]], ![[CBVECTORS:[0-9]+]], ![[CBARRAYS:[0-9]+]], ![[CBSTRUCTS:[0-9]+]], ![[CBMIX:[0-9]+]]} -// CHECK: !hlsl.cblayouts = !{![[CBSCALARS_LAYOUT:[0-9]+]], ![[CBVECTORS_LAYOUT:[0-9]+]], ![[CBARRAYS_LAYOUT:[0-9]+]], ![[A_LAYOUT:[0-9]+]], -// CHECK-SAME: ![[B_LAYOUT:[0-9]+]], ![[C_LAYOUT:[0-9]+]], ![[D_LAYOUT:[0-9]+]], ![[CBSTRUCTS_LAYOUT:[0-9]+]], ![[TEST_LAYOUT:[0-9]+]], -// CHECK-SAME: ![[ANON_LAYOUT:[0-9]+]], ![[CBMIX_LAYOUT:[0-9]+]]} - // CHECK: ![[CBSCALARS]] = !{ptr @CBScalars.cb, ptr addrspace(2) @a1, ptr addrspace(2) @a2, ptr addrspace(2) @a3, ptr addrspace(2) @a4, // CHECK-SAME: ptr addrspace(2) @a5, ptr addrspace(2) @a6, ptr addrspace(2) @a7, ptr addrspace(2) @a8} @@ -179,21 +191,7 @@ void main() { // CHECK-SAME: ptr addrspace(2) @c5, ptr addrspace(2) @c6, ptr addrspace(2) @c7, ptr addrspace(2) @c8} // CHECK: ![[CBSTRUCTS]] = !{ptr @CBStructs.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c, ptr addrspace(2) @array_of_A, -// CHECK-SAME: ptr addrspace(2) @d, ptr addrspace(2) @e, ptr addrspace(2) @f, ptr addrspace(2) @g} +// CHECK-SAME: ptr addrspace(2) @d, ptr addrspace(2) @e, ptr addrspace(2) @f} // CHECK: ![[CBMIX]] = !{ptr @CBMix.cb, ptr addrspace(2) @test, ptr addrspace(2) @f1, ptr addrspace(2) @f2, ptr addrspace(2) @f3, -// CHECK-SAME: ptr addrspace(2) @s, ptr addrspace(2) @dd, ptr addrspace(2) @f4, ptr addrspace(2) @dv, ptr addrspace(2) @uv} - -// CHECK: ![[CBSCALARS_LAYOUT]] = !{!"struct.__cblayout_CBScalars", i32 56, i32 0, i32 8, i32 16, i32 24, i32 32, i32 36, i32 40, i32 48} -// CHECK: ![[CBVECTORS_LAYOUT]] = !{!"struct.__cblayout_CBVectors", i32 136, i32 0, i32 16, i32 40, i32 48, i32 80, i32 96, i32 112} - -// CHECK: ![[CBARRAYS_LAYOUT]] = !{!"struct.__cblayout_CBArrays", i32 708, i32 0, i32 48, i32 112, i32 176, i32 224, i32 608, i32 624, i32 656} - -// CHECK: ![[A_LAYOUT]] = !{!"struct.A", i32 8, i32 0} -// CHECK: ![[B_LAYOUT]] = !{!"struct.B", i32 14, i32 0, i32 8} -// CHECK: ![[C_LAYOUT]] = !{!"struct.C", i32 24, i32 0, i32 16} -// CHECK: ![[D_LAYOUT]] = !{!"struct.__cblayout_D", i32 94, i32 0} -// CHECK: ![[CBSTRUCTS_LAYOUT]] = !{!"struct.__cblayout_CBStructs", i32 262, i32 0, i32 16, i32 32, i32 64, i32 144, i32 238, i32 240, i32 256} -// CHECK: ![[TEST_LAYOUT]] = !{!"struct.Test", i32 8, i32 0, i32 4} -// CHECK: ![[ANON_LAYOUT]] = !{!"struct.anon", i32 4, i32 0} -// CHECK: ![[CBMIX_LAYOUT]] = !{!"struct.__cblayout_CBMix", i32 162, i32 0, i32 24, i32 32, i32 120, i32 128, i32 136, i32 144, i32 152, i32 160} +// CHECK-SAME: ptr addrspace(2) @f4, ptr addrspace(2) @f5, ptr addrspace(2) @f6, ptr addrspace(2) @f7, ptr addrspace(2) @f8, ptr addrspace(2) @f9} diff --git a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl index aa5659af414ef..393ca3825c638 100644 --- a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl +++ b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.hlsl @@ -4,20 +4,20 @@ // Make sure cbuffer inside namespace works. -// CHECK: %"struct.n0::n1::__cblayout_A" = type { float } -// CHECK: %"struct.n0::__cblayout_B" = type { float } -// CHECK: %"struct.n0::n2::__cblayout_C" = type { float, %"struct.n0::Foo" } -// CHECK: %"struct.n0::Foo" = type { float } +// CHECK: %"n0::n1::__cblayout_A" = type <{ float }> +// CHECK: %"n0::__cblayout_B" = type <{ float }> +// CHECK: %"n0::n2::__cblayout_C" = type <{ float, target("dx.Layout", %"n0::Foo", 4, 0) }> +// CHECK: %"n0::Foo" = type <{ float }> -// CHECK: @A.cb = external constant target("dx.CBuffer", %"struct.n0::n1::__cblayout_A") +// CHECK: @A.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::n1::__cblayout_A", 4, 0)) // CHECK: @_ZN2n02n11aE = external addrspace(2) global float, align 4 -// CHECK: @B.cb = external constant target("dx.CBuffer", %"struct.n0::__cblayout_B") +// CHECK: @B.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::__cblayout_B", 4, 0)) // CHECK: @_ZN2n01aE = external addrspace(2) global float, align 4 -// CHECK: @C.cb = external constant target("dx.CBuffer", %"struct.n0::n2::__cblayout_C") +// CHECK: @C.cb = external constant target("dx.CBuffer", target("dx.Layout", %"n0::n2::__cblayout_C", 20, 0, 16)) // CHECK: @_ZN2n02n21aE = external addrspace(2) global float, align 4 -// CHECK: @_ZN2n02n21bE = external addrspace(2) global %"struct.n0::Foo", align 4 +// CHECK: external addrspace(2) global target("dx.Layout", %"n0::Foo", 4, 0), align 4 namespace n0 { struct Foo { @@ -51,13 +51,6 @@ float foo() { void main() {} // CHECK: !hlsl.cbs = !{![[A:[0-9]+]], ![[B:[0-9]+]], ![[C:[0-9]+]]} -// CHECK: !hlsl.cblayouts = !{![[A_LAYOUT:[0-9]+]], ![[B_LAYOUT:[0-9]+]], ![[FOO_LAYOUT:[0-9]+]], ![[C_LAYOUT:[0-9]+]]} - // CHECK: [[A]] = !{ptr @A.cb, ptr addrspace(2) @_ZN2n02n11aE} // CHECK: [[B]] = !{ptr @B.cb, ptr addrspace(2) @_ZN2n01aE} // CHECK: [[C]] = !{ptr @C.cb, ptr addrspace(2) @_ZN2n02n21aE, ptr addrspace(2) @_ZN2n02n21bE} - -// CHECK: ![[A_LAYOUT]] = !{!"struct.n0::n1::__cblayout_A", i32 4, i32 0} -// CHECK: ![[B_LAYOUT]] = !{!"struct.n0::__cblayout_B", i32 4, i32 0} -// CHECK: ![[FOO_LAYOUT]] = !{!"struct.n0::Foo", i32 4, i32 0} -// CHECK: ![[C_LAYOUT]] = !{!"struct.n0::n2::__cblayout_C", i32 20, i32 0, i32 16} diff --git a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll new file mode 100644 index 0000000000000..faa1931222ade --- /dev/null +++ b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll @@ -0,0 +1,49 @@ +; ModuleID = 'C:\llvm-project\clang\test\CodeGenHLSL\cbuffer_and_namespaces.hlsl' +source_filename = "C:\\llvm-project\\clang\\test\\CodeGenHLSL\\cbuffer_and_namespaces.hlsl" +target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" +target triple = "dxilv1.3-pc-shadermodel6.3-library" + +%__cblayout_A = type <{ float }> +%__cblayout_B = type <{ float }> +%__cblayout_C = type <{ float, target("dx.Layout", %Foo, 4, 0) }> +%Foo = type <{ float }> + +@A.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_A, 4, 0)) +@_ZN2n02n11aE = external addrspace(2) global float, align 4 +@B.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_B, 4, 0)) +@_ZN2n01aE = external addrspace(2) global float, align 4 +@C.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_C, 20, 0, 16)) +@_ZN2n02n21aE = external addrspace(2) global float, align 4 +@_ZN2n02n21bE = external addrspace(2) global target("dx.Layout", %Foo, 4, 0), align 4 + +; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind +define noundef nofpclass(nan inf) float @_Z3foov() #0 { +entry: + %0 = load float, ptr addrspace(2) @_ZN2n02n11aE, align 4 + %1 = load float, ptr addrspace(2) @_ZN2n01aE, align 4 + %add = fadd reassoc nnan ninf nsz arcp afn float %0, %1 + %2 = load float, ptr addrspace(2) @_ZN2n02n21aE, align 4 + %add1 = fadd reassoc nnan ninf nsz arcp afn float %add, %2 + ret float %add1 +} + +; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind +define void @_Z4mainv() #0 { +entry: + ret void +} + +attributes #0 = { alwaysinline convergent mustprogress norecurse nounwind "approx-func-fp-math"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!hlsl.cbs = !{!0, !1, !2} +!llvm.module.flags = !{!3, !4} +!dx.valver = !{!5} +!llvm.ident = !{!6} + +!0 = !{ptr @A.cb, ptr addrspace(2) @_ZN2n02n11aE} +!1 = !{ptr @B.cb, ptr addrspace(2) @_ZN2n01aE} +!2 = !{ptr @C.cb, ptr addrspace(2) @_ZN2n02n21aE, ptr addrspace(2) @_ZN2n02n21bE} +!3 = !{i32 1, !"wchar_size", i32 4} +!4 = !{i32 4, !"dx.disable_optimizations", i32 1} +!5 = !{i32 1, i32 8} +!6 = !{!"clang version 20.0.0git (C:/llvm-project/clang a8cdd4536867465e3d6e2b4ad8c49b27ee94dec8)"} diff --git a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl index f5b5cba6197b3..870593986a976 100644 --- a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl +++ b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl @@ -2,9 +2,9 @@ // RUN: dxil-pc-shadermodel6.3-compute %s \ // RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s -// CHECK: %struct.__cblayout_CB = type <{ float, double, <2 x i32> }> +// CHECK: %__cblayout_CB = type <{ float, double, <2 x i32> }> -// CHECK: @CB.cb = external constant target("dx.CBuffer", %struct.__cblayout_CB) +// CHECK: @CB.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88)) // CHECK: @a = external addrspace(2) global float, align 4 // CHECK: @b = external addrspace(2) global double, align 8 // CHECK: @c = external addrspace(2) global <2 x i32>, align 8 @@ -17,7 +17,8 @@ cbuffer CB : register(b1, space3) { // CHECK: define internal void @_init_resource_CB.cb() // CHECK-NEXT: entry: -// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", %struct.__cblayout_CB) @llvm.dx.resource.handlefrombinding.tdx.CBuffer_s_struct.__cblayout_CBst(i32 3, i32 1, i32 1, i32 0, i1 false) +// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88)) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.CBuffer_tdx.Layout_s___cblayout_CBs_176_16_168_88tt(i32 3, i32 1, i32 1, i32 0, i1 false) float foo() { // CHECK: load float, ptr addrspace(2) @a, align 4 @@ -34,7 +35,4 @@ void main() { } // CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]} -// CHECK: !hlsl.cblayouts = !{![[CB_LAYOUT:[0-9]+]]} - // CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c} -// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_CB", i32 176, i32 16, i32 168, i32 88} diff --git a/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl index 8994640d5b311..99f40d8fc93d7 100644 --- a/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl +++ b/clang/test/CodeGenHLSL/cbuffer_with_static_global_and_function.hlsl @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \ // RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s -// CHECK: %struct.__cblayout_A = type { float } +// CHECK: %__cblayout_A = type <{ float }> -// CHECK: @A.cb = external constant target("dx.CBuffer", %struct.__cblayout_A) +// CHECK: @A.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_A, 4, 0)) // CHECK: @a = external addrspace(2) global float, align 4 // CHECK-DAG: @_ZL1b = internal global float 3.000000e+00, align 4 // CHECK-NOT: @B.cb @@ -26,7 +26,4 @@ extern float bar() { } // CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]} -// CHECK: !hlsl.cblayouts = !{![[CB_LAYOUT:[0-9]+]]} - // CHECK: ![[CB]] = !{ptr @A.cb, ptr addrspace(2) @a} -// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_A", i32 4, i32 0} From 62094b2c5c754d4d077cbd2925950914e7c1c4af Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 14 Feb 2025 18:31:31 -0800 Subject: [PATCH 13/19] code review feedback - add named constant, rename function, remove file --- clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp | 11 +++-- .../CodeGenHLSL/cbuffer_and_namespaces.ll | 49 ------------------- 2 files changed, 6 insertions(+), 54 deletions(-) delete mode 100644 clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll diff --git a/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp b/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp index e0e7f6daddcfa..e634e47853d35 100644 --- a/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp +++ b/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp @@ -31,7 +31,7 @@ createArrayWithNewElementType(CodeGenModule &CGM, } // Returns the size of a scalar or vector in bytes/ -static unsigned getScalarOrVectorSize(llvm::Type *Ty) { +static unsigned getScalarOrVectorSizeInBytes(llvm::Type *Ty) { assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy()); if (Ty->isVectorTy()) { llvm::FixedVectorType *FVT = cast(Ty); @@ -65,6 +65,7 @@ llvm::Type *CommonHLSLTargetCodeGenInfo::createHLSLBufferLayoutType( SmallVector LayoutElements; unsigned Index = 0; // packoffset index unsigned EndOffset = 0; + const unsigned BufferRowAlign = 16U; // reserve first spot in the layout vector for buffer size Layout.push_back(0); @@ -94,7 +95,7 @@ llvm::Type *CommonHLSLTargetCodeGenInfo::createHLSLBufferLayoutType( unsigned ElemOffset = 0; unsigned ArrayCount = 1; unsigned ArrayStride = 0; - unsigned NextRowOffset = llvm::alignTo(EndOffset, 16U); + unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign); llvm::Type *ElemLayoutTy = nullptr; QualType FieldTy = FD->getType(); @@ -120,11 +121,11 @@ llvm::Type *CommonHLSLTargetCodeGenInfo::createHLSLBufferLayoutType( CGM, cast(FieldTy.getTypePtr()), NewTy); } else { // Array of vectors or scalars - ElemSize = - getScalarOrVectorSize(CGM.getTypes().ConvertTypeForMem(Ty)); + ElemSize = getScalarOrVectorSizeInBytes( + CGM.getTypes().ConvertTypeForMem(Ty)); ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); } - ArrayStride = llvm::alignTo(ElemSize, 16U); + ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign); ElemOffset = Packoffsets != nullptr ? (*Packoffsets)[Index] : NextRowOffset; diff --git a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll b/clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll deleted file mode 100644 index faa1931222ade..0000000000000 --- a/clang/test/CodeGenHLSL/cbuffer_and_namespaces.ll +++ /dev/null @@ -1,49 +0,0 @@ -; ModuleID = 'C:\llvm-project\clang\test\CodeGenHLSL\cbuffer_and_namespaces.hlsl' -source_filename = "C:\\llvm-project\\clang\\test\\CodeGenHLSL\\cbuffer_and_namespaces.hlsl" -target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" -target triple = "dxilv1.3-pc-shadermodel6.3-library" - -%__cblayout_A = type <{ float }> -%__cblayout_B = type <{ float }> -%__cblayout_C = type <{ float, target("dx.Layout", %Foo, 4, 0) }> -%Foo = type <{ float }> - -@A.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_A, 4, 0)) -@_ZN2n02n11aE = external addrspace(2) global float, align 4 -@B.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_B, 4, 0)) -@_ZN2n01aE = external addrspace(2) global float, align 4 -@C.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_C, 20, 0, 16)) -@_ZN2n02n21aE = external addrspace(2) global float, align 4 -@_ZN2n02n21bE = external addrspace(2) global target("dx.Layout", %Foo, 4, 0), align 4 - -; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind -define noundef nofpclass(nan inf) float @_Z3foov() #0 { -entry: - %0 = load float, ptr addrspace(2) @_ZN2n02n11aE, align 4 - %1 = load float, ptr addrspace(2) @_ZN2n01aE, align 4 - %add = fadd reassoc nnan ninf nsz arcp afn float %0, %1 - %2 = load float, ptr addrspace(2) @_ZN2n02n21aE, align 4 - %add1 = fadd reassoc nnan ninf nsz arcp afn float %add, %2 - ret float %add1 -} - -; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind -define void @_Z4mainv() #0 { -entry: - ret void -} - -attributes #0 = { alwaysinline convergent mustprogress norecurse nounwind "approx-func-fp-math"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } - -!hlsl.cbs = !{!0, !1, !2} -!llvm.module.flags = !{!3, !4} -!dx.valver = !{!5} -!llvm.ident = !{!6} - -!0 = !{ptr @A.cb, ptr addrspace(2) @_ZN2n02n11aE} -!1 = !{ptr @B.cb, ptr addrspace(2) @_ZN2n01aE} -!2 = !{ptr @C.cb, ptr addrspace(2) @_ZN2n02n21aE, ptr addrspace(2) @_ZN2n02n21bE} -!3 = !{i32 1, !"wchar_size", i32 4} -!4 = !{i32 4, !"dx.disable_optimizations", i32 1} -!5 = !{i32 1, i32 8} -!6 = !{!"clang version 20.0.0git (C:/llvm-project/clang a8cdd4536867465e3d6e2b4ad8c49b27ee94dec8)"} From 43aedd4ac16a5cd9782ebc63f7e56bc739f81df8 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 14 Feb 2025 19:12:40 -0800 Subject: [PATCH 14/19] fix test after merge --- clang/test/CodeGenHLSL/default_cbuffer.hlsl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clang/test/CodeGenHLSL/default_cbuffer.hlsl b/clang/test/CodeGenHLSL/default_cbuffer.hlsl index 7368997b51ac9..8f89606fdd04f 100644 --- a/clang/test/CodeGenHLSL/default_cbuffer.hlsl +++ b/clang/test/CodeGenHLSL/default_cbuffer.hlsl @@ -1,13 +1,13 @@ // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \ // RUN: -fnative-half-type -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -// CHECK: %"struct.__cblayout_$Globals" = type { float, float, %struct.__cblayout_S } -// CHECK: %struct.__cblayout_S = type { float } +// CHECK: %"__cblayout_$Globals" = type <{ float, float, target("dx.Layout", %__cblayout_S, 4, 0) }> +// CHECK: %__cblayout_S = type <{ float }> -// CHECK-DAG: @"$Globals.cb" = external constant target("dx.CBuffer", %"struct.__cblayout_$Globals") +// CHECK-DAG: @"$Globals.cb" = external constant target("dx.CBuffer", target("dx.Layout", %"__cblayout_$Globals", 20, 0, 4, 16)) // CHECK-DAG: @a = external addrspace(2) global float // CHECK-DAG: @g = external addrspace(2) global float -// CHECK-DAG: @h = external addrspace(2) global %struct.__cblayout_S +// CHECK-DAG: @h = external addrspace(2) global target("dx.Layout", %__cblayout_S, 4, 0), align 4 struct EmptyStruct { }; @@ -35,9 +35,5 @@ void main() { Buf[0] = a; } -// CHECK: !hlsl.cblayouts = !{![[S_LAYOUT:.*]], ![[CB_LAYOUT:.*]]} // CHECK: !hlsl.cbs = !{![[CB:.*]]} - -// CHECK: ![[S_LAYOUT]] = !{!"struct.__cblayout_S", i32 4, i32 0} -// CHECK: ![[CB_LAYOUT]] = !{!"struct.__cblayout_$Globals", i32 20, i32 0, i32 4, i32 16} // CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @g, ptr addrspace(2) @h} \ No newline at end of file From a90b3a1166eb58395a3dd799d6ee078709bc5c69 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 18 Feb 2025 12:45:48 -0800 Subject: [PATCH 15/19] code review feedback - update comment, add new line --- clang/include/clang/AST/Decl.h | 12 ++++++++++-- clang/lib/CodeGen/CGHLSLRuntime.cpp | 1 + clang/test/CodeGenHLSL/default_cbuffer.hlsl | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 0dd4ca8cfd38b..86e6d1417ae79 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -5087,8 +5087,16 @@ class HLSLBufferDecl final : public NamedDecl, public DeclContext { return static_cast(const_cast(DC)); } - // Iterator for the buffer decls. Concatenates the list of decls parented - // by this HLSLBufferDecl with the list of default buffer decls. + // Iterator for the buffer decls. For constant buffers explicitly declared + // with `cbuffer` keyword this will the list of decls parented by this + // HLSLBufferDecl (equal to `decls()`). + // For implicit $Globals buffer this will be the list of default buffer + // declarations stored in DefaultBufferDecls plus the implicit layout + // struct (the only child of HLSLBufferDecl in this case). + // + // The iterator uses llvm::concat_iterator to concatenate the lists + // `decls()` and `DefaultBufferDecls`. For non-default buffers + // `DefaultBufferDecls` is always empty. using buffer_decl_iterator = llvm::concat_iterator::const_iterator, decl_iterator>; diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 80ef24d54f128..32cdcbb705ee6 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -150,6 +150,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, llvm::Type *LayoutType = *ElemIt++; // FIXME: handle resources in cbuffer user-defined structs + // Issue llvm/wg-hlsl#175 // create global variable for the constant and to metadata list GlobalVariable *ElemGV = diff --git a/clang/test/CodeGenHLSL/default_cbuffer.hlsl b/clang/test/CodeGenHLSL/default_cbuffer.hlsl index 8f89606fdd04f..c5176aa8466e4 100644 --- a/clang/test/CodeGenHLSL/default_cbuffer.hlsl +++ b/clang/test/CodeGenHLSL/default_cbuffer.hlsl @@ -36,4 +36,4 @@ void main() { } // CHECK: !hlsl.cbs = !{![[CB:.*]]} -// CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @g, ptr addrspace(2) @h} \ No newline at end of file +// CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @g, ptr addrspace(2) @h} From eaeaf2a76d78a1070a733e0692654d059c41a4ea Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 19 Feb 2025 11:13:08 -0800 Subject: [PATCH 16/19] code review feedback - minor changes --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 31da7432b3145..3f1ccce9c6271 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -103,12 +103,8 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, // get the layout struct from constant buffer target type llvm::Type *BufType = BufGV->getValueType(); - assert(isa(BufType) && - "expected target type for HLSL buffer resource"); llvm::Type *BufLayoutType = cast(BufType)->getTypeParameter(0); - assert(isa(BufLayoutType) && - "expected target type for buffer layout struct"); llvm::StructType *LayoutStruct = cast( cast(BufLayoutType)->getTypeParameter(0)); @@ -139,9 +135,13 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, // Emit static and groupshared variables and resource classes inside // cbuffer as regular globals CGM.EmitGlobal(VD); + } else { + // Anything else that is not in the hlsl_constant address space must be + // an empty struct or a zero-sized array and can be ignored + assert(BufDecl->getASTContext().getTypeSize(VDTy) == 0 && + "constant buffer decl with non-zero sized type outside of " + "hlsl_constant address space"); } - // Anything else that is not in the hlsl_constant address space must be - // an empty struct or a zero-sized array and can be ignored continue; } @@ -149,10 +149,8 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, "number of elements in layout struct does not match"); llvm::Type *LayoutType = *ElemIt++; - // there might be resources inside the used defined structs - if (VDTy->isStructureType() && VDTy->isHLSLIntangibleType()) - // FIXME: handle resources in cbuffer structs - llvm_unreachable("resources in cbuffer are not supported yet"); + // FIXME: handle resources inside user defined structs + // (llvm/wg-hlsl#175) // create global variable for the constant and to metadata list GlobalVariable *ElemGV = @@ -161,7 +159,6 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, } assert(ElemIt == LayoutStruct->element_end() && "number of elements in layout struct does not match"); - // set the size of the buffer // add buffer metadata to the module CGM.getModule() @@ -235,6 +232,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { const HLSLResourceBindingAttr *RBA = BufDecl->getAttr(); // FIXME: handle implicit binding if no binding attribute is found + // (llvm/llvm-project#110722) if (RBA) createResourceInitFn(CGM, BufGV, RBA->getSlotNumber(), RBA->getSpaceNumber()); From d3c887280943d8d3ac5184088bcc39f8a5e42dce Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 19 Feb 2025 14:14:13 -0800 Subject: [PATCH 17/19] code review feedback - move layout calculations into a separate builder class instead of target info --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 4 +- clang/lib/CodeGen/CGHLSLRuntime.h | 7 +- clang/lib/CodeGen/CMakeLists.txt | 20 +- clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp | 229 ++++++++++++++++++ clang/lib/CodeGen/HLSLBufferLayoutBuilder.h | 48 ++++ clang/lib/CodeGen/Targets/DirectX.cpp | 22 +- clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp | 202 --------------- clang/lib/CodeGen/Targets/HLSLTargetInfo.h | 39 --- clang/lib/CodeGen/Targets/SPIR.cpp | 7 +- 9 files changed, 303 insertions(+), 275 deletions(-) create mode 100644 clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp create mode 100644 clang/lib/CodeGen/HLSLBufferLayoutBuilder.h delete mode 100644 clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp delete mode 100644 clang/lib/CodeGen/Targets/HLSLTargetInfo.h diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index b9ded3d3700db..4aa5a7ead14a2 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -240,7 +240,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) { RBA->getSpaceNumber()); } -llvm::Type * +llvm::TargetExtType * CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) { const auto Entry = LayoutTypes.find(StructType); if (Entry != LayoutTypes.end()) @@ -249,7 +249,7 @@ CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) { } void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType, - llvm::Type *LayoutTy) { + llvm::TargetExtType *LayoutTy) { assert(getHLSLBufferLayoutType(StructType) == nullptr && "layout type for this struct already exist"); LayoutTypes[StructType] = LayoutTy; diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 0f346410eccd4..a9da42324a038 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -162,9 +162,10 @@ class CGHLSLRuntime { llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB); - llvm::Type *getHLSLBufferLayoutType(const RecordType *LayoutStructTy); + llvm::TargetExtType * + getHLSLBufferLayoutType(const RecordType *LayoutStructTy); void addHLSLBufferLayoutType(const RecordType *LayoutStructTy, - llvm::Type *LayoutTy); + llvm::TargetExtType *LayoutTy); void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E); private: @@ -177,7 +178,7 @@ class CGHLSLRuntime { llvm::GlobalVariable *BufGV); llvm::Triple::ArchType getArch(); - llvm::DenseMap LayoutTypes; + llvm::DenseMap LayoutTypes; }; } // namespace CodeGen diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 1a6b25883d9c8..0f3a106cea1b9 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -31,25 +31,26 @@ set(LLVM_LINK_COMPONENTS Target TargetParser TransformUtils - ) +) # Workaround for MSVC ARM64 performance regression: # https://developercommunity.visualstudio.com/t/Compiling-a-specific-code-for-ARM64-with/10444970 # Since /O1 and /O2 represent a set of optimizations, # our goal is to disable the /Og flag while retaining the other optimizations from the /O1|/O2 set if(MSVC AND NOT CMAKE_CXX_COMPILER_ID MATCHES Clang - AND MSVC_VERSION VERSION_GREATER_EQUAL 1932 - AND MSVC_VERSION VERSION_LESS 1939 - AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") - + AND MSVC_VERSION VERSION_GREATER_EQUAL 1932 + AND MSVC_VERSION VERSION_LESS 1939 + AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) string(REGEX MATCHALL "/[Oo][12]" opt_flags "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}") - if (opt_flags) + + if(opt_flags) if(opt_flags MATCHES "1$") set(opt_flags "/Od;/Os;/Oy;/Ob2;/GF;/Gy") - elseif (opt_flags MATCHES "2$") + elseif(opt_flags MATCHES "2$") set(opt_flags "/Od;/Oi;/Ot;/Oy;/Ob2;/GF;/Gy") endif() + set_source_files_properties(CGBuiltin.cpp PROPERTIES COMPILE_OPTIONS "${opt_flags}") endif() endif() @@ -106,6 +107,7 @@ add_clang_library(clangCodeGen ConstantInitBuilder.cpp CoverageMappingGen.cpp ItaniumCXXABI.cpp + HLSLBufferLayoutBuilder.cpp LinkInModulesPass.cpp MacroPPCallbacks.cpp MicrosoftCXXABI.cpp @@ -124,7 +126,6 @@ add_clang_library(clangCodeGen Targets/CSKY.cpp Targets/DirectX.cpp Targets/Hexagon.cpp - Targets/HLSLTargetInfo.cpp Targets/Lanai.cpp Targets/LoongArch.cpp Targets/M68k.cpp @@ -148,6 +149,7 @@ add_clang_library(clangCodeGen vt_gen intrinsics_gen ClangDriverOptions + # These generated headers are included transitively. ARMTargetParserTableGen AArch64TargetParserTableGen @@ -159,4 +161,4 @@ add_clang_library(clangCodeGen clangFrontend clangLex clangSerialization - ) +) diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp new file mode 100644 index 0000000000000..1ae00023ab2bc --- /dev/null +++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp @@ -0,0 +1,229 @@ +//===- HLSLBufferLayoutBuilder.cpp ----------------------------------------===// +// +// 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 "HLSLBufferLayoutBuilder.h" +#include "CGHLSLRuntime.h" +#include "CodeGenModule.h" +#include "clang/AST/Type.h" + +//===----------------------------------------------------------------------===// +// Implementation of constant buffer layout common between DirectX and +// SPIR/SPIR-V. +//===----------------------------------------------------------------------===// + +using namespace clang; +using namespace clang::CodeGen; + +namespace { + +// Creates a new array type with the same dimentions but with the new +// element type. +static llvm::Type * +createArrayWithNewElementType(CodeGenModule &CGM, + const ConstantArrayType *ArrayType, + llvm::Type *NewElemType) { + const clang::Type *ArrayElemType = ArrayType->getArrayElementTypeNoTypeQual(); + if (ArrayElemType->isConstantArrayType()) + NewElemType = createArrayWithNewElementType( + CGM, cast(ArrayElemType), NewElemType); + return llvm::ArrayType::get(NewElemType, ArrayType->getSExtSize()); +} + +// Returns the size of a scalar or vector in bytes +static unsigned getScalarOrVectorSizeInBytes(llvm::Type *Ty) { + assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy()); + if (Ty->isVectorTy()) { + llvm::FixedVectorType *FVT = cast(Ty); + return FVT->getNumElements() * + (FVT->getElementType()->getScalarSizeInBits() / 8); + } + return Ty->getScalarSizeInBits() / 8; +} + +} // namespace + +namespace clang { +namespace CodeGen { + +// Creates a layout type for given struct with HLSL constant buffer layout +// taking into account Packoffsets, if provided. +// Previously created layout types are cached by CGHLSLRuntime. +// +// The function iterates over all fields of the StructType (including base +// classes) and calls layoutField to converts each field to its corresponding +// LLVM type and to calculate its HLSL constant buffer layout. Any embedded +// structs (or arrays of structs) are converted to target layout types as well. +llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType( + const RecordType *StructType, + const llvm::SmallVector *Packoffsets) { + + // check if we already have the layout type for this struct + if (llvm::TargetExtType *Ty = + CGM.getHLSLRuntime().getHLSLBufferLayoutType(StructType)) + return Ty; + + SmallVector Layout; + SmallVector LayoutElements; + unsigned Index = 0; // packoffset index + unsigned EndOffset = 0; + + // reserve first spot in the layout vector for buffer size + Layout.push_back(0); + + // iterate over all fields of the record, including fields on base classes + llvm::SmallVector RecordTypes; + RecordTypes.push_back(StructType); + while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { + CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); + assert(D->getNumBases() == 1 && + "HLSL doesn't support multiple inheritance"); + RecordTypes.push_back(D->bases_begin()->getType()->getAs()); + } + while (!RecordTypes.empty()) { + const RecordType *RT = RecordTypes.back(); + RecordTypes.pop_back(); + + for (const auto *FD : RT->getDecl()->fields()) { + assert(!Packoffsets || Index < Packoffsets->size() && + "number of elements in layout struct does not " + "match number of packoffset annotations"); + + if (!layoutField(FD, EndOffset, Layout, LayoutElements, + Packoffsets ? (*Packoffsets)[Index] : -1)) + return nullptr; + Index++; + } + } + + // set the size of the buffer + Layout[0] = EndOffset; + + // create the layout struct type; anonymous struct have empty name but + // non-empty qualified name + const CXXRecordDecl *Decl = StructType->getAsCXXRecordDecl(); + std::string Name = + Decl->getName().empty() ? "anon" : Decl->getQualifiedNameAsString(); + llvm::StructType *StructTy = + llvm::StructType::create(LayoutElements, Name, true); + + // create target layout type + llvm::TargetExtType *NewLayoutTy = llvm::TargetExtType::get( + CGM.getLLVMContext(), LayoutTypeName, {StructTy}, Layout); + if (NewLayoutTy) + CGM.getHLSLRuntime().addHLSLBufferLayoutType(StructType, NewLayoutTy); + return NewLayoutTy; +} + +// The function converts a single field of HLSL Buffer to its corresponding +// LLVM type and calculates it's layout. Any embedded structs (or +// arrays of structs) are converted to target layout types as well. +// The converted type is appended to the LayoutElements list, the element +// offset is added to the Layout list and the EndOffset updated to the offset +// just after the lay-ed out element (which is basically the size of the +// buffer). +// Returns true if the conversion was successful. +// The packoffset parameter contains the field's layout offset provided by the +// user or -1 if there was no packoffset (or register(cX)) annotation. +bool HLSLBufferLayoutBuilder::layoutField( + const FieldDecl *FD, unsigned &EndOffset, SmallVector &Layout, + SmallVector &LayoutElements, int Packoffset) { + + // Size of element; for arrays this is a size of a single element in the + // array. Total array size of calculated as (ArrayCount-1) * ArrayStride + + // ElemSize. + unsigned ElemSize = 0; + unsigned ElemOffset = 0; + unsigned ArrayCount = 1; + unsigned ArrayStride = 0; + + const unsigned BufferRowAlign = 16U; + unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign); + + llvm::Type *ElemLayoutTy = nullptr; + QualType FieldTy = FD->getType(); + + if (FieldTy->isConstantArrayType()) { + // Unwrap array to find the element type and get combined array size. + QualType Ty = FieldTy; + while (Ty->isConstantArrayType()) { + const ConstantArrayType *ArrayTy = cast(Ty); + ArrayCount *= ArrayTy->getSExtSize(); + Ty = ArrayTy->getElementType(); + } + // For array of structures, create a new array with a layout type + // instead of the structure type. + if (Ty->isStructureType()) { + llvm::Type *NewTy = + cast(createLayoutType(Ty->getAsStructureType())); + if (!NewTy) + return false; + assert(isa(NewTy) && "expected target type"); + ElemSize = cast(NewTy)->getIntParameter(0); + ElemLayoutTy = createArrayWithNewElementType( + CGM, cast(FieldTy.getTypePtr()), NewTy); + } else { + // Array of vectors or scalars + ElemSize = + getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty)); + ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); + } + ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign); + ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset; + + } else if (FieldTy->isStructureType()) { + // Create a layout type for the structure + ElemLayoutTy = createLayoutType(FieldTy->getAsStructureType()); + if (!ElemLayoutTy) + return false; + assert(isa(ElemLayoutTy) && "expected target type"); + ElemSize = cast(ElemLayoutTy)->getIntParameter(0); + ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset; + + } else { + // scalar or vector - find element size and alignment + unsigned Align = 0; + ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); + if (ElemLayoutTy->isVectorTy()) { + // align vectors by sub element size + const llvm::FixedVectorType *FVT = + cast(ElemLayoutTy); + unsigned SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8; + ElemSize = FVT->getNumElements() * SubElemSize; + Align = SubElemSize; + } else { + assert(ElemLayoutTy->isIntegerTy() || ElemLayoutTy->isFloatingPointTy()); + ElemSize = ElemLayoutTy->getScalarSizeInBits() / 8; + Align = ElemSize; + } + + // calculate or get element offset for the vector or scalar + if (Packoffset != -1) { + ElemOffset = Packoffset; + } else { + ElemOffset = llvm::alignTo(EndOffset, Align); + // if the element does not fit, move it to the next row + if (ElemOffset + ElemSize > NextRowOffset) + ElemOffset = NextRowOffset; + } + } + + // Update end offset of the layout; do not update it if the EndOffset + // is already bigger than the new value (which may happen with unordered + // packoffset annotations) + unsigned NewEndOffset = + ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; + EndOffset = std::max(EndOffset, NewEndOffset); + + // add the layout element and offset to the lists + Layout.push_back(ElemOffset); + LayoutElements.push_back(ElemLayoutTy); + return true; +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h new file mode 100644 index 0000000000000..57bb17c557b9c --- /dev/null +++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h @@ -0,0 +1,48 @@ +//===- HLSLBufferLayoutBuilder.h ------------------------------------------===// +// +// 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 "llvm/ADT/StringRef.h" +#include "llvm/IR/DerivedTypes.h" + +namespace clang { +class RecordType; +class FieldDecl; + +namespace CodeGen { +class CodeGenModule; + +//===----------------------------------------------------------------------===// +// Implementation of constant buffer layout common between DirectX and +// SPIR/SPIR-V. +//===----------------------------------------------------------------------===// + +class HLSLBufferLayoutBuilder { +private: + CodeGenModule &CGM; + llvm::StringRef LayoutTypeName; + +public: + HLSLBufferLayoutBuilder(CodeGenModule &CGM, llvm::StringRef LayoutTypeName) + : CGM(CGM), LayoutTypeName(LayoutTypeName) {} + + // Returns LLVM target extension type with the name LayoutTypeName + // for given structure type and layout data. The first number in + // the Layout is the size followed by offsets for each struct element. + llvm::TargetExtType * + createLayoutType(const RecordType *StructType, + const llvm::SmallVector *Packoffsets = nullptr); + +private: + bool layoutField(const clang::FieldDecl *FD, unsigned &EndOffset, + llvm::SmallVector &Layout, + llvm::SmallVector &LayoutElements, + int Packoffset); +}; + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index 3d4b65aec0a3a..77091eb45f5cf 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -8,7 +8,7 @@ #include "ABIInfoImpl.h" #include "CodeGenModule.h" -#include "HLSLTargetInfo.h" +#include "HLSLBufferLayoutBuilder.h" #include "TargetInfo.h" #include "clang/AST/Type.h" #include "llvm/ADT/SmallVector.h" @@ -24,27 +24,16 @@ using namespace clang::CodeGen; namespace { -class DirectXTargetCodeGenInfo : public CommonHLSLTargetCodeGenInfo { +class DirectXTargetCodeGenInfo : public TargetCodeGenInfo { public: DirectXTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) - : CommonHLSLTargetCodeGenInfo(std::make_unique(CGT)) {} + : TargetCodeGenInfo(std::make_unique(CGT)) {} llvm::Type *getHLSLType( CodeGenModule &CGM, const Type *T, const SmallVector *Packoffsets = nullptr) const override; - - llvm::Type *getHLSLLayoutType(CodeGenModule &CGM, - llvm::StructType *LayoutStructTy, - SmallVector Layout) const override; }; -llvm::Type *DirectXTargetCodeGenInfo::getHLSLLayoutType( - CodeGenModule &CGM, llvm::StructType *LayoutStructTy, - SmallVector Layout) const { - return llvm::TargetExtType::get(CGM.getLLVMContext(), "dx.Layout", - {LayoutStructTy}, Layout); -} - llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( CodeGenModule &CGM, const Type *Ty, const SmallVector *Packoffsets) const { @@ -80,8 +69,9 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( if (ContainedTy.isNull() || !ContainedTy->isStructureType()) return nullptr; - llvm::Type *BufferLayoutTy = this->createHLSLBufferLayoutType( - CGM, ContainedTy->getAsStructureType(), Packoffsets); + llvm::Type *BufferLayoutTy = + HLSLBufferLayoutBuilder(CGM, "dx.Layout") + .createLayoutType(ContainedTy->getAsStructureType(), Packoffsets); if (!BufferLayoutTy) return nullptr; diff --git a/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp b/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp deleted file mode 100644 index e634e47853d35..0000000000000 --- a/clang/lib/CodeGen/Targets/HLSLTargetInfo.cpp +++ /dev/null @@ -1,202 +0,0 @@ -//===- HLSLTargetInto.cpp--------------------------------------------------===// -// -// 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 "HLSLTargetInfo.h" -#include "CGHLSLRuntime.h" -#include "TargetInfo.h" -#include "clang/AST/DeclCXX.h" - -//===----------------------------------------------------------------------===// -// Target codegen info implementation common between DirectX and SPIR/SPIR-V. -//===----------------------------------------------------------------------===// - -namespace { - -// Creates a new array type with the same dimentions -// but with the new element type. -static llvm::Type * -createArrayWithNewElementType(CodeGenModule &CGM, - const ConstantArrayType *ArrayType, - llvm::Type *NewElemType) { - const clang::Type *ArrayElemType = ArrayType->getArrayElementTypeNoTypeQual(); - if (ArrayElemType->isConstantArrayType()) - NewElemType = createArrayWithNewElementType( - CGM, cast(ArrayElemType), NewElemType); - return llvm::ArrayType::get(NewElemType, ArrayType->getSExtSize()); -} - -// Returns the size of a scalar or vector in bytes/ -static unsigned getScalarOrVectorSizeInBytes(llvm::Type *Ty) { - assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy()); - if (Ty->isVectorTy()) { - llvm::FixedVectorType *FVT = cast(Ty); - return FVT->getNumElements() * - (FVT->getElementType()->getScalarSizeInBits() / 8); - } - return Ty->getScalarSizeInBits() / 8; -} - -} // namespace - -// Creates a layout type for given struct with HLSL constant buffer layout -// taking into account Packoffsets, if provided. -// Previously created layout types are cached in CGHLSLRuntime because -// TargetCodeGenInto info is cannot store any data -// (CGM.getTargetCodeGenInfo() returns a const reference to TargetCondegenInfo). -// -// The function iterates over all fields of the StructType (including base -// classes), converts each field to its corresponding LLVM type and calculated -// it's HLSL constant bufffer layout (offset and size). Any embedded struct (or -// arrays of structs) are converted to target layout types as well. -llvm::Type *CommonHLSLTargetCodeGenInfo::createHLSLBufferLayoutType( - CodeGenModule &CGM, const RecordType *StructType, - const SmallVector *Packoffsets) const { - - // check if we already have the layout type for this struct - if (llvm::Type *Ty = CGM.getHLSLRuntime().getHLSLBufferLayoutType(StructType)) - return Ty; - - SmallVector Layout; - SmallVector LayoutElements; - unsigned Index = 0; // packoffset index - unsigned EndOffset = 0; - const unsigned BufferRowAlign = 16U; - - // reserve first spot in the layout vector for buffer size - Layout.push_back(0); - - // iterate over all fields of the record, including fields on base classes - llvm::SmallVector RecordTypes; - RecordTypes.push_back(StructType); - while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { - CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); - assert(D->getNumBases() == 1 && - "HLSL doesn't support multiple inheritance"); - RecordTypes.push_back(D->bases_begin()->getType()->getAs()); - } - while (!RecordTypes.empty()) { - const RecordType *RT = RecordTypes.back(); - RecordTypes.pop_back(); - - for (const auto *FD : RT->getDecl()->fields()) { - assert(!Packoffsets || Index < Packoffsets->size() && - "number of elements in layout struct does not " - "match number of packoffset annotations"); - // Size of element; for arrays this is a size of a single element in the - // array. Total array size of calculated as (ArrayCount-1) * ArrayStride + - // ElemSize. - unsigned ElemSize = 0; - - unsigned ElemOffset = 0; - unsigned ArrayCount = 1; - unsigned ArrayStride = 0; - unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign); - llvm::Type *ElemLayoutTy = nullptr; - - QualType FieldTy = FD->getType(); - - if (FieldTy->isConstantArrayType()) { - // Unwrap array to find the element type and get combined array size. - QualType Ty = FieldTy; - while (Ty->isConstantArrayType()) { - const ConstantArrayType *ArrayTy = cast(Ty); - ArrayCount *= ArrayTy->getSExtSize(); - Ty = ArrayTy->getElementType(); - } - // For array of structures, create a new array with a layout type - // instead of the structure type. - if (Ty->isStructureType()) { - llvm::Type *NewTy = cast( - createHLSLBufferLayoutType(CGM, Ty->getAsStructureType())); - if (!NewTy) - return nullptr; - assert(isa(NewTy) && "expected target type"); - ElemSize = cast(NewTy)->getIntParameter(0); - ElemLayoutTy = createArrayWithNewElementType( - CGM, cast(FieldTy.getTypePtr()), NewTy); - } else { - // Array of vectors or scalars - ElemSize = getScalarOrVectorSizeInBytes( - CGM.getTypes().ConvertTypeForMem(Ty)); - ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); - } - ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign); - ElemOffset = - Packoffsets != nullptr ? (*Packoffsets)[Index] : NextRowOffset; - - } else if (FieldTy->isStructureType()) { - // Create a layout type for the structure - ElemLayoutTy = - createHLSLBufferLayoutType(CGM, FieldTy->getAsStructureType()); - if (!ElemLayoutTy) - return nullptr; - assert(isa(ElemLayoutTy) && - "expected target type"); - ElemSize = cast(ElemLayoutTy)->getIntParameter(0); - ElemOffset = - Packoffsets != nullptr ? (*Packoffsets)[Index] : NextRowOffset; - } else { - // scalar or vector - find element size and alignment - unsigned Align = 0; - ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); - if (ElemLayoutTy->isVectorTy()) { - // align vectors by sub element size - const llvm::FixedVectorType *FVT = - cast(ElemLayoutTy); - unsigned SubElemSize = - FVT->getElementType()->getScalarSizeInBits() / 8; - ElemSize = FVT->getNumElements() * SubElemSize; - Align = SubElemSize; - } else { - assert(ElemLayoutTy->isIntegerTy() || - ElemLayoutTy->isFloatingPointTy()); - ElemSize = ElemLayoutTy->getScalarSizeInBits() / 8; - Align = ElemSize; - } - // calculate or get element offset for the vector or scalar - if (Packoffsets != nullptr) { - ElemOffset = (*Packoffsets)[Index]; - } else { - ElemOffset = llvm::alignTo(EndOffset, Align); - // if the element does not fit, move it to the next row - if (ElemOffset + ElemSize > NextRowOffset) - ElemOffset = NextRowOffset; - } - } - - // Update end offset of the layout; do not update it if the EndOffset - // is already bigger than the new value (which may happen with unordered - // packoffset annotations) - unsigned NewEndOffset = - ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize; - EndOffset = std::max(EndOffset, NewEndOffset); - - // add the layout element and offset to the lists - Layout.push_back(ElemOffset); - LayoutElements.push_back(ElemLayoutTy); - Index++; - } - } - - // set the size of the buffer - Layout[0] = EndOffset; - - // create the layout struct type; anonymous struct have empty name but - // non-empty qualified name - const CXXRecordDecl *Decl = StructType->getAsCXXRecordDecl(); - std::string Name = - Decl->getName().empty() ? "anon" : Decl->getQualifiedNameAsString(); - llvm::StructType *StructTy = - llvm::StructType::create(LayoutElements, Name, true); - - // create target layout type - llvm::Type *NewLayoutTy = this->getHLSLLayoutType(CGM, StructTy, Layout); - if (NewLayoutTy) - CGM.getHLSLRuntime().addHLSLBufferLayoutType(StructType, NewLayoutTy); - return NewLayoutTy; -} diff --git a/clang/lib/CodeGen/Targets/HLSLTargetInfo.h b/clang/lib/CodeGen/Targets/HLSLTargetInfo.h deleted file mode 100644 index 279ce4fba3dad..0000000000000 --- a/clang/lib/CodeGen/Targets/HLSLTargetInfo.h +++ /dev/null @@ -1,39 +0,0 @@ -//===- HLSLTargetInfo.h ---------------------------------------------------===// -// -// 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 "ABIInfoImpl.h" -#include "TargetInfo.h" - -using namespace clang; -using namespace clang::CodeGen; - -//===----------------------------------------------------------------------===// -// Target codegen info implementation common between DirectX and SPIR/SPIR-V. -//===----------------------------------------------------------------------===// - -class CommonHLSLTargetCodeGenInfo : public TargetCodeGenInfo { -public: - CommonHLSLTargetCodeGenInfo(std::unique_ptr Info) - : TargetCodeGenInfo(std::move(Info)) {} - - // Returns LLVM target extension type "dx.Layout" or "spv.Layout" - // for given structure type and layout data. The first number in - // the Layout is the size followed by offsets for each struct element. - virtual llvm::Type *getHLSLLayoutType(CodeGenModule &CGM, - llvm::StructType *LayoutStructTy, - SmallVector Layout) const { - return nullptr; - }; - -protected: - // Creates a layout type for given struct with HLSL constant buffer layout - // taking into account Packoffsets, if provided. - virtual llvm::Type *createHLSLBufferLayoutType( - CodeGenModule &CGM, const clang::RecordType *StructType, - const SmallVector *Packoffsets = nullptr) const; -}; diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index 3cf95483dc4fa..c94db31ae1a89 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "ABIInfoImpl.h" -#include "HLSLTargetInfo.h" #include "TargetInfo.h" using namespace clang; @@ -39,12 +38,12 @@ class SPIRVABIInfo : public CommonSPIRABIInfo { }; } // end anonymous namespace namespace { -class CommonSPIRTargetCodeGenInfo : public CommonHLSLTargetCodeGenInfo { +class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo { public: CommonSPIRTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) - : CommonHLSLTargetCodeGenInfo(std::make_unique(CGT)) {} + : TargetCodeGenInfo(std::make_unique(CGT)) {} CommonSPIRTargetCodeGenInfo(std::unique_ptr ABIInfo) - : CommonHLSLTargetCodeGenInfo(std::move(ABIInfo)) {} + : TargetCodeGenInfo(std::move(ABIInfo)) {} LangAS getASTAllocaAddressSpace() const override { return getLangASFromTargetAS( From 77987bd26cc81c2b08e52a3b2cdf6ac2c18ae0e0 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 19 Feb 2025 14:20:15 -0800 Subject: [PATCH 18/19] revert clang/lib/CodeGen/CMakeLists.txt whitespace changes --- clang/lib/CodeGen/CMakeLists.txt | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 0f3a106cea1b9..05ab6671453f8 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -31,26 +31,25 @@ set(LLVM_LINK_COMPONENTS Target TargetParser TransformUtils -) + ) # Workaround for MSVC ARM64 performance regression: # https://developercommunity.visualstudio.com/t/Compiling-a-specific-code-for-ARM64-with/10444970 # Since /O1 and /O2 represent a set of optimizations, # our goal is to disable the /Og flag while retaining the other optimizations from the /O1|/O2 set if(MSVC AND NOT CMAKE_CXX_COMPILER_ID MATCHES Clang - AND MSVC_VERSION VERSION_GREATER_EQUAL 1932 - AND MSVC_VERSION VERSION_LESS 1939 - AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + AND MSVC_VERSION VERSION_GREATER_EQUAL 1932 + AND MSVC_VERSION VERSION_LESS 1939 + AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) string(REGEX MATCHALL "/[Oo][12]" opt_flags "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}") - - if(opt_flags) + if (opt_flags) if(opt_flags MATCHES "1$") set(opt_flags "/Od;/Os;/Oy;/Ob2;/GF;/Gy") - elseif(opt_flags MATCHES "2$") + elseif (opt_flags MATCHES "2$") set(opt_flags "/Od;/Oi;/Ot;/Oy;/Ob2;/GF;/Gy") endif() - set_source_files_properties(CGBuiltin.cpp PROPERTIES COMPILE_OPTIONS "${opt_flags}") endif() endif() @@ -149,7 +148,6 @@ add_clang_library(clangCodeGen vt_gen intrinsics_gen ClangDriverOptions - # These generated headers are included transitively. ARMTargetParserTableGen AArch64TargetParserTableGen @@ -161,4 +159,4 @@ add_clang_library(clangCodeGen clangFrontend clangLex clangSerialization -) + ) From b93407368d9b65e2e07f82e342e6fec9a1bc8263 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 19 Feb 2025 14:27:45 -0800 Subject: [PATCH 19/19] clang-format --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 4aa5a7ead14a2..547220fb1f1e1 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -19,8 +19,8 @@ #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" -#include "clang/AST/Type.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" #include "clang/Basic/TargetOptions.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/DerivedTypes.h"