[clang] [llvm] [HLSL] Implement explicit layout for default constant buffer ($Globals) (PR #128991)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 10 18:52:39 PDT 2025
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/128991
>From 9faff902639aece87b72ed5235d71b8b68533074 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 26 Feb 2025 17:39:16 -0800
Subject: [PATCH 1/6] Add resource binding attribute on $Globals numeric
constants
---
clang/lib/Sema/SemaHLSL.cpp | 10 +--
.../test/AST/HLSL/resource_binding_attr.hlsl | 67 ++++++++++++-------
2 files changed, 47 insertions(+), 30 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 283a9801fc707..ffc3ac1b65854 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1446,18 +1446,20 @@ static bool DiagnoseLocalRegisterBinding(Sema &S, SourceLocation &ArgLoc,
Ty = Ty->getArrayElementTypeNoTypeQual();
// Basic types
- if (Ty->isArithmeticType()) {
+ if (Ty->isArithmeticType() || Ty->isVectorType()) {
bool DeclaredInCOrTBuffer = isa<HLSLBufferDecl>(D->getDeclContext());
if (SpecifiedSpace && !DeclaredInCOrTBuffer)
S.Diag(ArgLoc, diag::err_hlsl_space_on_global_constant);
- if (!DeclaredInCOrTBuffer &&
- (Ty->isIntegralType(S.getASTContext()) || Ty->isFloatingType())) {
- // Default Globals
+ if (!DeclaredInCOrTBuffer && (Ty->isIntegralType(S.getASTContext()) ||
+ Ty->isFloatingType() || Ty->isVectorType())) {
+ // Register annotation on default constant buffer declaration ($Globals)
if (RegType == RegisterType::CBuffer)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
else if (RegType != RegisterType::C)
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
+ else
+ return true;
} else {
if (RegType == RegisterType::C)
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_packoffset);
diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl
index 6fac903f75e18..7ba4f0f60e83c 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -1,41 +1,56 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -ast-dump -o - %s | FileCheck %s
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:8:9 cbuffer CB
-// CHECK-NEXT:HLSLResourceClassAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer
-// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer
-// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "b3" "space2"
-// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'hlsl_constant float'
+// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 5]]:9 cbuffer CB
+// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer
+// CHECK-NEXT: HLSLResourceAttr {{.*}} Implicit CBuffer
+// CHECK-NEXT: HLSLResourceBindingAttr {{.*}} "b3" "space2"
+// CHECK-NEXT: VarDecl {{.*}} used a 'hlsl_constant float'
cbuffer CB : register(b3, space2) {
float a;
}
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:17:9 tbuffer TB
-// CHECK-NEXT:HLSLResourceClassAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit SRV
-// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit TBuffer
-// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "t2" "space1"
-// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'hlsl_constant float'
+// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 5]]:9 tbuffer TB
+// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit SRV
+// CHECK-NEXT: HLSLResourceAttr {{.*}} Implicit TBuffer
+// CHECK-NEXT: HLSLResourceBindingAttr {{.*}} "t2" "space1"
+// CHECK-NEXT: VarDecl {{.*}} used b 'hlsl_constant float'
tbuffer TB : register(t2, space1) {
float b;
}
-float foo() {
-// CHECK: BinaryOperator 0x{{[0-9a-f]+}} <col:10, col:14> 'float' '+'
-// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:10> 'float' <LValueToRValue>
-// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:10> 'hlsl_constant float' lvalue Var 0x[[A]] 'a' 'hlsl_constant float'
-// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:14> 'float' <LValueToRValue>
-// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:14> 'hlsl_constant float' lvalue Var 0x[[B]] 'b' 'hlsl_constant float'
+export float foo() {
return a + b;
}
-// CHECK: VarDecl 0x{{[0-9a-f]+}} <{{.*}}> col:17 UAV 'RWBuffer<float>':'hlsl::RWBuffer<float>' callinit
-// CHECK-NEXT:-CXXConstructExpr 0x{{[0-9a-f]+}} <col:17> 'RWBuffer<float>':'hlsl::RWBuffer<float>' 'void ()'
-// CHECK-NEXT:-HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:23> "u3" "space0"
+// CHECK: VarDecl {{.*}} UAV 'RWBuffer<float>':'hlsl::RWBuffer<float>'
+// CHECK: HLSLResourceBindingAttr {{.*}} "u3" "space0"
RWBuffer<float> UAV : register(u3);
-// CHECK: -VarDecl 0x{{[0-9a-f]+}} <{{.*}}> col:17 UAV1 'RWBuffer<float>':'hlsl::RWBuffer<float>' callinit
-// CHECK-NEXT:-CXXConstructExpr 0x{{[0-9a-f]+}} <col:17> 'RWBuffer<float>':'hlsl::RWBuffer<float>' 'void ()'
-// CHECK-NEXT:-HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:24> "u2" "space0"
-// CHECK-NEXT:-VarDecl 0x{{[0-9a-f]+}} <col:1, col:38> col:38 UAV2 'RWBuffer<float>':'hlsl::RWBuffer<float>' callinit
-// CHECK-NEXT:-CXXConstructExpr 0x{{[0-9a-f]+}} <col:38> 'RWBuffer<float>':'hlsl::RWBuffer<float>' 'void ()'
-// CHECK-NEXT:-HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:45> "u4" "space0"
+// CHECK: VarDecl {{.*}} UAV1 'RWBuffer<float>':'hlsl::RWBuffer<float>'
+// CHECK: HLSLResourceBindingAttr {{.*}} "u2" "space0"
+// CHECK: VarDecl {{.*}} UAV2 'RWBuffer<float>':'hlsl::RWBuffer<float>'
+// CHECK: HLSLResourceBindingAttr {{.*}} "u4" "space0"
RWBuffer<float> UAV1 : register(u2), UAV2 : register(u4);
+
+//
+// Default constants ($Globals) layout annotations
+
+// CHECK: VarDecl {{.*}} f 'hlsl_constant float'
+// CHECK: HLSLResourceBindingAttr {{.*}} "c5" "space0"
+float f : register(c5);
+
+// CHECK: VarDecl {{.*}} intv 'hlsl_constant int4':'vector<int hlsl_constant, 4>'
+// CHECK: HLSLResourceBindingAttr {{.*}} "c2" "space0"
+int4 intv : register(c2);
+
+// CHECK: VarDecl {{.*}} dar 'hlsl_constant double[5]'
+// CHECK: HLSLResourceBindingAttr {{.*}} "c3" "space0"
+double dar[5] : register(c3);
+
+struct S {
+ int a;
+};
+
+// CHECK: VarDecl {{.*}} s 'hlsl_constant S'
+// CHECK: HLSLResourceBindingAttr {{.*}} "c10" "space0
+S s : register(c10);
\ No newline at end of file
>From e982a61657da5eb4c7f2618c95f0c6d3493cb854 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 26 Feb 2025 19:14:20 -0800
Subject: [PATCH 2/6] [HLSL] Implement explicit layout for default constant
buffer
Fixes #123801
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 38 ++++++--
clang/lib/CodeGen/CGHLSLRuntime.h | 2 +-
clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp | 87 +++++++++++++++----
clang/lib/CodeGen/HLSLBufferLayoutBuilder.h | 7 +-
clang/lib/CodeGen/TargetInfo.h | 2 +-
clang/lib/CodeGen/Targets/DirectX.cpp | 4 +-
clang/lib/CodeGen/Targets/SPIR.cpp | 4 +-
clang/lib/Sema/SemaHLSL.cpp | 12 +++
.../CodeGenHLSL/cbuffer_with_packoffset.hlsl | 17 +++-
.../default_cbuffer_with_layout.hlsl | 37 ++++++++
10 files changed, 175 insertions(+), 35 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/default_cbuffer_with_layout.hlsl
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index ed6d2036cb984..6f476d7df4578 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -70,7 +70,7 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
llvm::Type *
CGHLSLRuntime::convertHLSLSpecificType(const Type *T,
- SmallVector<unsigned> *Packoffsets) {
+ SmallVector<int32_t> *Packoffsets) {
assert(T->isHLSLSpecificType() && "Not an HLSL specific type!");
// Check if the target has a specific translation for this type first.
@@ -179,21 +179,45 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) {
return cast<HLSLAttributedResourceType>(QT.getTypePtr());
}
+// Iterates over all declarations in the HLSL buffer and based on the
+// packoffset or register(c#) annotations it fills outs the Layout
+// vector with the user-specified layout offsets.
+// The buffer offsets can be specified 2 ways:
+// 1. declarations in cbuffer {} block can have a packoffset annotation
+// (translates to HLSLPackOffsetAttr)
+// 2. default constant buffer declarations at global scope can have
+// register(c#) annotations (translates to HLSLResourceBindingAttr with
+// RegisterType::C)
+// It is not quaranteed that all declarations in a buffer have an annotation.
+// For those where it is not specified a -1 value is added to the Layout
+// vector. In the final layout these declarations will be placed at the end
+// of the HLSL buffer after all of the elements with specified offset.
static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
- SmallVector<unsigned> &Layout) {
+ SmallVector<int32_t> &Layout) {
assert(Layout.empty() && "expected empty vector for layout");
assert(BufDecl->hasValidPackoffset());
- for (Decl *D : BufDecl->decls()) {
+ for (Decl *D : BufDecl->buffer_decls()) {
if (isa<CXXRecordDecl, EmptyDecl>(D) || isa<FunctionDecl>(D)) {
continue;
}
VarDecl *VD = dyn_cast<VarDecl>(D);
if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant)
continue;
- assert(VD->hasAttr<HLSLPackOffsetAttr>() &&
- "expected packoffset attribute on every declaration");
- size_t Offset = VD->getAttr<HLSLPackOffsetAttr>()->getOffsetInBytes();
+ size_t Offset = -1;
+ if (VD->hasAttrs()) {
+ for (auto *Attr : VD->getAttrs()) {
+ if (auto *POA = dyn_cast<HLSLPackOffsetAttr>(Attr)) {
+ Offset = POA->getOffsetInBytes();
+ } else if (auto *RBA = dyn_cast<HLSLResourceBindingAttr>(Attr)) {
+ if (RBA->getRegisterType() ==
+ HLSLResourceBindingAttr::RegisterType::C) {
+ // size of constant buffer row is 16 bytes
+ Offset = RBA->getSlotNumber() * 16U;
+ }
+ }
+ }
+ }
Layout.push_back(Offset);
}
}
@@ -212,7 +236,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
return;
// create global variable for the constant buffer
- SmallVector<unsigned> Layout;
+ SmallVector<int32_t> Layout;
if (BufDecl->hasValidPackoffset())
fillPackoffsetLayout(BufDecl, Layout);
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index a9da42324a038..c4550056175c1 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -146,7 +146,7 @@ class CGHLSLRuntime {
llvm::Type *
convertHLSLSpecificType(const Type *T,
- SmallVector<unsigned> *Packoffsets = nullptr);
+ SmallVector<int32_t> *Packoffsets = nullptr);
void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV);
void generateGlobalCtorDtorCalls();
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
index 97262b76c0164..bf9bca48a4dd6 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -10,6 +10,7 @@
#include "CGHLSLRuntime.h"
#include "CodeGenModule.h"
#include "clang/AST/Type.h"
+#include <climits>
//===----------------------------------------------------------------------===//
// Implementation of constant buffer layout common between DirectX and
@@ -58,9 +59,15 @@ namespace CodeGen {
// 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.
+//
+// When Packoffsets are specified the elements will be placed based on the
+// user-specified offsets. Not all elements must have a packoffset/register(c#)
+// annotation though. For those that don't, the Packoffsets array will constain
+// -1 value instead. These elements must be placed at the end of the layout
+// after all of the elements with specific offset.
llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
const RecordType *StructType,
- const llvm::SmallVector<unsigned> *Packoffsets) {
+ const llvm::SmallVector<int32_t> *Packoffsets) {
// check if we already have the layout type for this struct
if (llvm::TargetExtType *Ty =
@@ -72,6 +79,8 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
unsigned Index = 0; // packoffset index
unsigned EndOffset = 0;
+ SmallVector<std::pair<const FieldDecl *, unsigned>> DelayLayoutFields;
+
// reserve first spot in the layout vector for buffer size
Layout.push_back(0);
@@ -89,14 +98,57 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
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");
+ unsigned FieldOffset = UINT_MAX;
+ llvm::Type *FieldType = nullptr;
+
+ if (Packoffsets) {
+ // have packoffset/register(c#) annotations
+ assert(Index < Packoffsets->size() &&
+ "number of elements in layout struct does not match number of "
+ "packoffset annotations");
+ int PO = (*Packoffsets)[Index++];
+ if (PO != -1) {
+ if (!layoutField(FD, EndOffset, FieldOffset, FieldType, PO))
+ return nullptr;
+ } else {
+ // No packoffset/register(cX) annotation on this field;
+ // Delay the layout until after all of the other elements
+ // annotated with packoffsets/register(cX) are processed.
+ DelayLayoutFields.emplace_back(FD, LayoutElements.size());
+ // reserve space for this field in the layout vector and elements list
+ Layout.push_back(UINT_MAX);
+ LayoutElements.push_back(nullptr);
+ continue;
+ }
+ } else {
+ if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
+ return nullptr;
+ }
+
+ assert(FieldOffset != UINT_MAX && FieldType != nullptr);
+ Layout.push_back((unsigned)FieldOffset);
+ LayoutElements.push_back(FieldType);
+ }
+ }
- if (!layoutField(FD, EndOffset, Layout, LayoutElements,
- Packoffsets ? (*Packoffsets)[Index] : -1))
+ // process delayed layouts
+ if (!DelayLayoutFields.empty()) {
+ for (auto I : DelayLayoutFields) {
+ const FieldDecl *FD = I.first;
+ unsigned IndexInLayoutElements = I.second;
+ // the first item in layout vector is size, so we need to offset the index
+ // by 1
+ unsigned IndexInLayout = IndexInLayoutElements + 1;
+ assert(Layout[IndexInLayout] == UINT_MAX &&
+ LayoutElements[IndexInLayoutElements] == nullptr);
+
+ unsigned FieldOffset = UINT_MAX;
+ llvm::Type *FieldType = nullptr;
+ if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
return nullptr;
- Index++;
+
+ Layout[IndexInLayout] = (unsigned)FieldOffset;
+ LayoutElements[IndexInLayoutElements] = FieldType;
}
}
@@ -122,16 +174,19 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
// 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).
+// The converted type is set to the FieldType parameter, the element
+// offset is set to the FieldOffset parameter. The EndOffset (=size of the
+// buffer) is also updated accordingly to the offset just after the placed
+// element, unless the incoming EndOffset already larger (may happen in case
+// of unsorted packoffset annotations).
// 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<unsigned> &Layout,
- SmallVector<llvm::Type *> &LayoutElements, int Packoffset) {
+bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
+ unsigned &EndOffset,
+ unsigned &FieldOffset,
+ llvm::Type *&FieldType,
+ 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 +
@@ -220,8 +275,8 @@ bool HLSLBufferLayoutBuilder::layoutField(
EndOffset = std::max<unsigned>(EndOffset, NewEndOffset);
// add the layout element and offset to the lists
- Layout.push_back(ElemOffset);
- LayoutElements.push_back(ElemLayoutTy);
+ FieldOffset = ElemOffset;
+ FieldType = ElemLayoutTy;
return true;
}
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
index 57bb17c557b9c..b4550757a93b4 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
@@ -35,13 +35,12 @@ class HLSLBufferLayoutBuilder {
// the Layout is the size followed by offsets for each struct element.
llvm::TargetExtType *
createLayoutType(const RecordType *StructType,
- const llvm::SmallVector<unsigned> *Packoffsets = nullptr);
+ const llvm::SmallVector<int32_t> *Packoffsets = nullptr);
private:
bool layoutField(const clang::FieldDecl *FD, unsigned &EndOffset,
- llvm::SmallVector<unsigned> &Layout,
- llvm::SmallVector<llvm::Type *> &LayoutElements,
- int Packoffset);
+ unsigned &FieldOffset, llvm::Type *&FieldType,
+ int Packoffset = -1);
};
} // namespace CodeGen
diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index 86057c14a549e..5df19fbef1e5b 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -441,7 +441,7 @@ class TargetCodeGenInfo {
/// Return an LLVM type that corresponds to a HLSL type
virtual llvm::Type *
getHLSLType(CodeGenModule &CGM, const Type *T,
- const SmallVector<unsigned> *Packoffsets = nullptr) const {
+ const SmallVector<int32_t> *Packoffsets = nullptr) const {
return nullptr;
}
diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp
index 77091eb45f5cf..8b94d01048c5d 100644
--- a/clang/lib/CodeGen/Targets/DirectX.cpp
+++ b/clang/lib/CodeGen/Targets/DirectX.cpp
@@ -31,12 +31,12 @@ class DirectXTargetCodeGenInfo : public TargetCodeGenInfo {
llvm::Type *getHLSLType(
CodeGenModule &CGM, const Type *T,
- const SmallVector<unsigned> *Packoffsets = nullptr) const override;
+ const SmallVector<int32_t> *Packoffsets = nullptr) const override;
};
llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(
CodeGenModule &CGM, const Type *Ty,
- const SmallVector<unsigned> *Packoffsets) const {
+ const SmallVector<int32_t> *Packoffsets) const {
auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
if (!ResType)
return nullptr;
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp
index c94db31ae1a89..056228563aa25 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -54,7 +54,7 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo {
llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
llvm::Type *getHLSLType(
CodeGenModule &CGM, const Type *Ty,
- const SmallVector<unsigned> *Packoffsets = nullptr) const override;
+ const SmallVector<int32_t> *Packoffsets = nullptr) const override;
llvm::Type *getSPIRVImageTypeFromHLSLResource(
const HLSLAttributedResourceType::Attributes &attributes,
llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
@@ -371,7 +371,7 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(
CodeGenModule &CGM, const Type *Ty,
- const SmallVector<unsigned> *Packoffsets) const {
+ const SmallVector<int32_t> *Packoffsets) const {
auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
if (!ResType)
return nullptr;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ffc3ac1b65854..507278219e500 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1961,6 +1961,18 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);
+ // Set HasValidPackoffset if any of the decls has a register(c#) annotation;
+ for (const Decl *VD : DefaultCBufferDecls) {
+ if (const HLSLResourceBindingAttr *RBA =
+ VD->getAttr<HLSLResourceBindingAttr>()) {
+ if (RBA->getRegisterType() ==
+ HLSLResourceBindingAttr::RegisterType::C) {
+ DefaultCBuffer->setHasValidPackoffset(true);
+ break;
+ }
+ }
+ }
+
DeclGroupRef DG(DefaultCBuffer);
SemaRef.Consumer.HandleTopLevelDecl(DG);
}
diff --git a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
index 870593986a976..81697cdc0f045 100644
--- a/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
+++ b/clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
@@ -3,6 +3,7 @@
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s
// CHECK: %__cblayout_CB = type <{ float, double, <2 x i32> }>
+// CHECK: %__cblayout_CB_1 = type <{ float, <2 x float> }>
// 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
@@ -15,6 +16,17 @@ cbuffer CB : register(b1, space3) {
int2 c : packoffset(c5.z);
}
+// CHECK: @CB.cb.1 = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB_1, 92, 88, 80))
+// CHECK: @x = external addrspace(2) global float, align 4
+// CHECK: @y = external addrspace(2) global <2 x float>, align 8
+
+// Missing packoffset annotation will produce a warning.
+// Element x will be placed after the element y that has an explicit packoffset.
+cbuffer CB : register(b0) {
+ float x;
+ float2 y : packoffset(c5);
+}
+
// CHECK: define internal void @_init_resource_CB.cb()
// CHECK-NEXT: entry:
// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88))
@@ -34,5 +46,6 @@ void main() {
foo();
}
-// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]}
-// CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}
+// CHECK: !hlsl.cbs = !{![[CB1:[0-9]+]], ![[CB2:[0-9]+]]}
+// CHECK: ![[CB1]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}
+// CHECK: ![[CB2]] = !{ptr @CB.cb.1, ptr addrspace(2) @x, ptr addrspace(2) @y}
diff --git a/clang/test/CodeGenHLSL/default_cbuffer_with_layout.hlsl b/clang/test/CodeGenHLSL/default_cbuffer_with_layout.hlsl
new file mode 100644
index 0000000000000..c4fd83679f84b
--- /dev/null
+++ b/clang/test/CodeGenHLSL/default_cbuffer_with_layout.hlsl
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \
+// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+
+// CHECK: %"__cblayout_$Globals" = type <{ i32, float, [4 x double], <4 x i32>, <4 x float>,
+// CHECK-SAME: target("dx.Layout", %S, 8, 0) }>
+// CHECK: %S = type <{ <2 x float> }>
+
+// CHECK-DAG: @b = external addrspace(2) global float, align 4
+// CHECK-DAG: @d = external addrspace(2) global <4 x i32>, align 16
+// CHECK-DAG: @"$Globals.cb" = external constant target("dx.CBuffer",
+// CHECK-DAG-SAME: target("dx.Layout", %"__cblayout_$Globals", 144, 120, 16, 32, 64, 128, 112))
+// CHECK-DAG: @a = external addrspace(2) global i32, align 4
+// CHECK-DAG: @c = external addrspace(2) global [4 x double], align 8
+// CHECK-DAG: @e = external addrspace(2) global <4 x float>, align 16
+// CHECK-DAG: @s = external addrspace(2) global target("dx.Layout", %S, 8, 0), align 8
+
+struct S {
+ float2 v;
+};
+
+int a;
+float b : register(c1);
+double c[4] : register(c2);
+int4 d : register(c4);
+float4 e;
+S s : register(c7);
+
+RWBuffer<float> Buf;
+
+[numthreads(4,1,1)]
+void main() {
+ Buf[0] = a;
+}
+
+// CHECK: !hlsl.cbs = !{![[CB:.*]]}
+// CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c,
+// CHECK-SAME: ptr addrspace(2) @d, ptr addrspace(2) @e, ptr addrspace(2) @s}
\ No newline at end of file
>From 458d5f0b0583fb4cbeb1c8b8f6f460e231b66474 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 26 Feb 2025 19:27:16 -0800
Subject: [PATCH 3/6] clang-format
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 2 +-
clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp | 2 +-
clang/lib/CodeGen/Targets/DirectX.cpp | 6 +++---
clang/lib/CodeGen/Targets/SPIR.cpp | 6 +++---
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 6f476d7df4578..67256aa2be339 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -191,7 +191,7 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) {
// It is not quaranteed that all declarations in a buffer have an annotation.
// For those where it is not specified a -1 value is added to the Layout
// vector. In the final layout these declarations will be placed at the end
-// of the HLSL buffer after all of the elements with specified offset.
+// of the HLSL buffer after all of the elements with specified offset.
static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
SmallVector<int32_t> &Layout) {
assert(Layout.empty() && "expected empty vector for layout");
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
index bf9bca48a4dd6..7a92233ac9055 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -64,7 +64,7 @@ namespace CodeGen {
// user-specified offsets. Not all elements must have a packoffset/register(c#)
// annotation though. For those that don't, the Packoffsets array will constain
// -1 value instead. These elements must be placed at the end of the layout
-// after all of the elements with specific offset.
+// after all of the elements with specific offset.
llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
const RecordType *StructType,
const llvm::SmallVector<int32_t> *Packoffsets) {
diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp
index 8b94d01048c5d..88a4b812db675 100644
--- a/clang/lib/CodeGen/Targets/DirectX.cpp
+++ b/clang/lib/CodeGen/Targets/DirectX.cpp
@@ -29,9 +29,9 @@ class DirectXTargetCodeGenInfo : public TargetCodeGenInfo {
DirectXTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)
: TargetCodeGenInfo(std::make_unique<DefaultABIInfo>(CGT)) {}
- llvm::Type *getHLSLType(
- CodeGenModule &CGM, const Type *T,
- const SmallVector<int32_t> *Packoffsets = nullptr) const override;
+ llvm::Type *
+ getHLSLType(CodeGenModule &CGM, const Type *T,
+ const SmallVector<int32_t> *Packoffsets = nullptr) const override;
};
llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp
index 056228563aa25..43f511e572d37 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,9 +52,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 SmallVector<int32_t> *Packoffsets = nullptr) const override;
+ llvm::Type *
+ getHLSLType(CodeGenModule &CGM, const Type *Ty,
+ const SmallVector<int32_t> *Packoffsets = nullptr) const override;
llvm::Type *getSPIRVImageTypeFromHLSLResource(
const HLSLAttributedResourceType::Attributes &attributes,
llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
>From 22d9d1f241b966389f8947ca0de7b9f8cb63995b Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 27 Feb 2025 11:33:46 -0800
Subject: [PATCH 4/6] code review feedback - fix typo and naked constant
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 4 ++--
clang/lib/CodeGen/CGHLSLRuntime.h | 2 ++
clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp | 6 +++---
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 67256aa2be339..e8a54bff28e51 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -188,7 +188,7 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) {
// 2. default constant buffer declarations at global scope can have
// register(c#) annotations (translates to HLSLResourceBindingAttr with
// RegisterType::C)
-// It is not quaranteed that all declarations in a buffer have an annotation.
+// It is not guaranteed that all declarations in a buffer have an annotation.
// For those where it is not specified a -1 value is added to the Layout
// vector. In the final layout these declarations will be placed at the end
// of the HLSL buffer after all of the elements with specified offset.
@@ -213,7 +213,7 @@ static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
if (RBA->getRegisterType() ==
HLSLResourceBindingAttr::RegisterType::C) {
// size of constant buffer row is 16 bytes
- Offset = RBA->getSlotNumber() * 16U;
+ Offset = RBA->getSlotNumber() * CGHLSLRuntime::BufferRowSizeInBytes;
}
}
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c4550056175c1..eaccfa9852d34 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -134,6 +134,8 @@ class CGHLSLRuntime {
BufferResBinding(HLSLResourceBindingAttr *Attr);
};
+ static const unsigned BufferRowSizeInBytes = 16U;
+
protected:
CodeGenModule &CGM;
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
index 7a92233ac9055..5163d69a8bd41 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -196,8 +196,8 @@ bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
unsigned ArrayCount = 1;
unsigned ArrayStride = 0;
- const unsigned BufferRowAlign = 16U;
- unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign);
+ unsigned NextRowOffset =
+ llvm::alignTo(EndOffset, CGHLSLRuntime::BufferRowSizeInBytes);
llvm::Type *ElemLayoutTy = nullptr;
QualType FieldTy = FD->getType();
@@ -227,7 +227,7 @@ bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty));
ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
}
- ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign);
+ ArrayStride = llvm::alignTo(ElemSize, CGHLSLRuntime::BufferRowSizeInBytes);
ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
} else if (FieldTy->isStructureType()) {
>From 496ed3f5ba3acc5084d601ba8eaa10b14118d4df Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 10 Mar 2025 18:37:09 -0700
Subject: [PATCH 5/6] Moving CBufferRowSizeInBytes constant to HLSLResource.h
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 4 +++-
clang/lib/CodeGen/CGHLSLRuntime.h | 2 --
clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp | 5 +++--
llvm/include/llvm/Frontend/HLSL/HLSLResource.h | 2 ++
4 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index f86b33cf95bc8..11fcfcf09a70d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -40,6 +40,8 @@ using namespace CodeGen;
using namespace clang::hlsl;
using namespace llvm;
+using llvm::hlsl::CBufferRowSizeInBytes;
+
static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
unsigned Slot, unsigned Space);
@@ -208,7 +210,7 @@ static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
if (RBA->getRegisterType() ==
HLSLResourceBindingAttr::RegisterType::C) {
// size of constant buffer row is 16 bytes
- Offset = RBA->getSlotNumber() * CGHLSLRuntime::BufferRowSizeInBytes;
+ Offset = RBA->getSlotNumber() * CBufferRowSizeInBytes;
}
}
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index eaccfa9852d34..c4550056175c1 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -134,8 +134,6 @@ class CGHLSLRuntime {
BufferResBinding(HLSLResourceBindingAttr *Attr);
};
- static const unsigned BufferRowSizeInBytes = 16U;
-
protected:
CodeGenModule &CGM;
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
index 5163d69a8bd41..c737a582aa978 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -19,6 +19,7 @@
using namespace clang;
using namespace clang::CodeGen;
+using llvm::hlsl::CBufferRowSizeInBytes;
namespace {
@@ -197,7 +198,7 @@ bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
unsigned ArrayStride = 0;
unsigned NextRowOffset =
- llvm::alignTo(EndOffset, CGHLSLRuntime::BufferRowSizeInBytes);
+ llvm::alignTo(EndOffset, CBufferRowSizeInBytes);
llvm::Type *ElemLayoutTy = nullptr;
QualType FieldTy = FD->getType();
@@ -227,7 +228,7 @@ bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty));
ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
}
- ArrayStride = llvm::alignTo(ElemSize, CGHLSLRuntime::BufferRowSizeInBytes);
+ ArrayStride = llvm::alignTo(ElemSize, CBufferRowSizeInBytes);
ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
} else if (FieldTy->isStructureType()) {
diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLResource.h b/llvm/include/llvm/Frontend/HLSL/HLSLResource.h
index 989893bcaccec..c59ad3f8d7b03 100644
--- a/llvm/include/llvm/Frontend/HLSL/HLSLResource.h
+++ b/llvm/include/llvm/Frontend/HLSL/HLSLResource.h
@@ -27,6 +27,8 @@ using dxil::ResourceClass;
using dxil::ElementType;
using dxil::ResourceKind;
+const unsigned CBufferRowSizeInBytes = 16U;
+
class FrontendResource {
MDNode *Entry;
>From 333966bb157cb9f65f5ea1731e4ff8ba67e5987a Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 10 Mar 2025 18:52:16 -0700
Subject: [PATCH 6/6] clang-format
---
clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
index c737a582aa978..fe491f1988535 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -197,8 +197,7 @@ bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
unsigned ArrayCount = 1;
unsigned ArrayStride = 0;
- unsigned NextRowOffset =
- llvm::alignTo(EndOffset, CBufferRowSizeInBytes);
+ unsigned NextRowOffset = llvm::alignTo(EndOffset, CBufferRowSizeInBytes);
llvm::Type *ElemLayoutTy = nullptr;
QualType FieldTy = FD->getType();
More information about the cfe-commits
mailing list