Skip to content

Commit e982a61

Browse files
committed
[HLSL] Implement explicit layout for default constant buffer
Fixes #123801
1 parent 9faff90 commit e982a61

File tree

10 files changed

+175
-35
lines changed

10 files changed

+175
-35
lines changed

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
7070

7171
llvm::Type *
7272
CGHLSLRuntime::convertHLSLSpecificType(const Type *T,
73-
SmallVector<unsigned> *Packoffsets) {
73+
SmallVector<int32_t> *Packoffsets) {
7474
assert(T->isHLSLSpecificType() && "Not an HLSL specific type!");
7575

7676
// Check if the target has a specific translation for this type first.
@@ -179,21 +179,45 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) {
179179
return cast<HLSLAttributedResourceType>(QT.getTypePtr());
180180
}
181181

182+
// Iterates over all declarations in the HLSL buffer and based on the
183+
// packoffset or register(c#) annotations it fills outs the Layout
184+
// vector with the user-specified layout offsets.
185+
// The buffer offsets can be specified 2 ways:
186+
// 1. declarations in cbuffer {} block can have a packoffset annotation
187+
// (translates to HLSLPackOffsetAttr)
188+
// 2. default constant buffer declarations at global scope can have
189+
// register(c#) annotations (translates to HLSLResourceBindingAttr with
190+
// RegisterType::C)
191+
// It is not quaranteed that all declarations in a buffer have an annotation.
192+
// For those where it is not specified a -1 value is added to the Layout
193+
// vector. In the final layout these declarations will be placed at the end
194+
// of the HLSL buffer after all of the elements with specified offset.
182195
static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
183-
SmallVector<unsigned> &Layout) {
196+
SmallVector<int32_t> &Layout) {
184197
assert(Layout.empty() && "expected empty vector for layout");
185198
assert(BufDecl->hasValidPackoffset());
186199

187-
for (Decl *D : BufDecl->decls()) {
200+
for (Decl *D : BufDecl->buffer_decls()) {
188201
if (isa<CXXRecordDecl, EmptyDecl>(D) || isa<FunctionDecl>(D)) {
189202
continue;
190203
}
191204
VarDecl *VD = dyn_cast<VarDecl>(D);
192205
if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant)
193206
continue;
194-
assert(VD->hasAttr<HLSLPackOffsetAttr>() &&
195-
"expected packoffset attribute on every declaration");
196-
size_t Offset = VD->getAttr<HLSLPackOffsetAttr>()->getOffsetInBytes();
207+
size_t Offset = -1;
208+
if (VD->hasAttrs()) {
209+
for (auto *Attr : VD->getAttrs()) {
210+
if (auto *POA = dyn_cast<HLSLPackOffsetAttr>(Attr)) {
211+
Offset = POA->getOffsetInBytes();
212+
} else if (auto *RBA = dyn_cast<HLSLResourceBindingAttr>(Attr)) {
213+
if (RBA->getRegisterType() ==
214+
HLSLResourceBindingAttr::RegisterType::C) {
215+
// size of constant buffer row is 16 bytes
216+
Offset = RBA->getSlotNumber() * 16U;
217+
}
218+
}
219+
}
220+
}
197221
Layout.push_back(Offset);
198222
}
199223
}
@@ -212,7 +236,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
212236
return;
213237

214238
// create global variable for the constant buffer
215-
SmallVector<unsigned> Layout;
239+
SmallVector<int32_t> Layout;
216240
if (BufDecl->hasValidPackoffset())
217241
fillPackoffsetLayout(BufDecl, Layout);
218242

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class CGHLSLRuntime {
146146

147147
llvm::Type *
148148
convertHLSLSpecificType(const Type *T,
149-
SmallVector<unsigned> *Packoffsets = nullptr);
149+
SmallVector<int32_t> *Packoffsets = nullptr);
150150

151151
void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV);
152152
void generateGlobalCtorDtorCalls();

clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "CGHLSLRuntime.h"
1111
#include "CodeGenModule.h"
1212
#include "clang/AST/Type.h"
13+
#include <climits>
1314

1415
//===----------------------------------------------------------------------===//
1516
// Implementation of constant buffer layout common between DirectX and
@@ -58,9 +59,15 @@ namespace CodeGen {
5859
// classes) and calls layoutField to converts each field to its corresponding
5960
// LLVM type and to calculate its HLSL constant buffer layout. Any embedded
6061
// structs (or arrays of structs) are converted to target layout types as well.
62+
//
63+
// When Packoffsets are specified the elements will be placed based on the
64+
// user-specified offsets. Not all elements must have a packoffset/register(c#)
65+
// annotation though. For those that don't, the Packoffsets array will constain
66+
// -1 value instead. These elements must be placed at the end of the layout
67+
// after all of the elements with specific offset.
6168
llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
6269
const RecordType *StructType,
63-
const llvm::SmallVector<unsigned> *Packoffsets) {
70+
const llvm::SmallVector<int32_t> *Packoffsets) {
6471

6572
// check if we already have the layout type for this struct
6673
if (llvm::TargetExtType *Ty =
@@ -72,6 +79,8 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
7279
unsigned Index = 0; // packoffset index
7380
unsigned EndOffset = 0;
7481

82+
SmallVector<std::pair<const FieldDecl *, unsigned>> DelayLayoutFields;
83+
7584
// reserve first spot in the layout vector for buffer size
7685
Layout.push_back(0);
7786

@@ -89,14 +98,57 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
8998
RecordTypes.pop_back();
9099

91100
for (const auto *FD : RT->getDecl()->fields()) {
92-
assert((!Packoffsets || Index < Packoffsets->size()) &&
93-
"number of elements in layout struct does not "
94-
"match number of packoffset annotations");
101+
unsigned FieldOffset = UINT_MAX;
102+
llvm::Type *FieldType = nullptr;
103+
104+
if (Packoffsets) {
105+
// have packoffset/register(c#) annotations
106+
assert(Index < Packoffsets->size() &&
107+
"number of elements in layout struct does not match number of "
108+
"packoffset annotations");
109+
int PO = (*Packoffsets)[Index++];
110+
if (PO != -1) {
111+
if (!layoutField(FD, EndOffset, FieldOffset, FieldType, PO))
112+
return nullptr;
113+
} else {
114+
// No packoffset/register(cX) annotation on this field;
115+
// Delay the layout until after all of the other elements
116+
// annotated with packoffsets/register(cX) are processed.
117+
DelayLayoutFields.emplace_back(FD, LayoutElements.size());
118+
// reserve space for this field in the layout vector and elements list
119+
Layout.push_back(UINT_MAX);
120+
LayoutElements.push_back(nullptr);
121+
continue;
122+
}
123+
} else {
124+
if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
125+
return nullptr;
126+
}
127+
128+
assert(FieldOffset != UINT_MAX && FieldType != nullptr);
129+
Layout.push_back((unsigned)FieldOffset);
130+
LayoutElements.push_back(FieldType);
131+
}
132+
}
95133

96-
if (!layoutField(FD, EndOffset, Layout, LayoutElements,
97-
Packoffsets ? (*Packoffsets)[Index] : -1))
134+
// process delayed layouts
135+
if (!DelayLayoutFields.empty()) {
136+
for (auto I : DelayLayoutFields) {
137+
const FieldDecl *FD = I.first;
138+
unsigned IndexInLayoutElements = I.second;
139+
// the first item in layout vector is size, so we need to offset the index
140+
// by 1
141+
unsigned IndexInLayout = IndexInLayoutElements + 1;
142+
assert(Layout[IndexInLayout] == UINT_MAX &&
143+
LayoutElements[IndexInLayoutElements] == nullptr);
144+
145+
unsigned FieldOffset = UINT_MAX;
146+
llvm::Type *FieldType = nullptr;
147+
if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
98148
return nullptr;
99-
Index++;
149+
150+
Layout[IndexInLayout] = (unsigned)FieldOffset;
151+
LayoutElements[IndexInLayoutElements] = FieldType;
100152
}
101153
}
102154

@@ -122,16 +174,19 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
122174
// The function converts a single field of HLSL Buffer to its corresponding
123175
// LLVM type and calculates it's layout. Any embedded structs (or
124176
// arrays of structs) are converted to target layout types as well.
125-
// The converted type is appended to the LayoutElements list, the element
126-
// offset is added to the Layout list and the EndOffset updated to the offset
127-
// just after the lay-ed out element (which is basically the size of the
128-
// buffer).
177+
// The converted type is set to the FieldType parameter, the element
178+
// offset is set to the FieldOffset parameter. The EndOffset (=size of the
179+
// buffer) is also updated accordingly to the offset just after the placed
180+
// element, unless the incoming EndOffset already larger (may happen in case
181+
// of unsorted packoffset annotations).
129182
// Returns true if the conversion was successful.
130183
// The packoffset parameter contains the field's layout offset provided by the
131184
// user or -1 if there was no packoffset (or register(cX)) annotation.
132-
bool HLSLBufferLayoutBuilder::layoutField(
133-
const FieldDecl *FD, unsigned &EndOffset, SmallVector<unsigned> &Layout,
134-
SmallVector<llvm::Type *> &LayoutElements, int Packoffset) {
185+
bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
186+
unsigned &EndOffset,
187+
unsigned &FieldOffset,
188+
llvm::Type *&FieldType,
189+
int Packoffset) {
135190

136191
// Size of element; for arrays this is a size of a single element in the
137192
// array. Total array size of calculated as (ArrayCount-1) * ArrayStride +
@@ -220,8 +275,8 @@ bool HLSLBufferLayoutBuilder::layoutField(
220275
EndOffset = std::max<unsigned>(EndOffset, NewEndOffset);
221276

222277
// add the layout element and offset to the lists
223-
Layout.push_back(ElemOffset);
224-
LayoutElements.push_back(ElemLayoutTy);
278+
FieldOffset = ElemOffset;
279+
FieldType = ElemLayoutTy;
225280
return true;
226281
}
227282

clang/lib/CodeGen/HLSLBufferLayoutBuilder.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,12 @@ class HLSLBufferLayoutBuilder {
3535
// the Layout is the size followed by offsets for each struct element.
3636
llvm::TargetExtType *
3737
createLayoutType(const RecordType *StructType,
38-
const llvm::SmallVector<unsigned> *Packoffsets = nullptr);
38+
const llvm::SmallVector<int32_t> *Packoffsets = nullptr);
3939

4040
private:
4141
bool layoutField(const clang::FieldDecl *FD, unsigned &EndOffset,
42-
llvm::SmallVector<unsigned> &Layout,
43-
llvm::SmallVector<llvm::Type *> &LayoutElements,
44-
int Packoffset);
42+
unsigned &FieldOffset, llvm::Type *&FieldType,
43+
int Packoffset = -1);
4544
};
4645

4746
} // namespace CodeGen

clang/lib/CodeGen/TargetInfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ class TargetCodeGenInfo {
441441
/// Return an LLVM type that corresponds to a HLSL type
442442
virtual llvm::Type *
443443
getHLSLType(CodeGenModule &CGM, const Type *T,
444-
const SmallVector<unsigned> *Packoffsets = nullptr) const {
444+
const SmallVector<int32_t> *Packoffsets = nullptr) const {
445445
return nullptr;
446446
}
447447

clang/lib/CodeGen/Targets/DirectX.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ class DirectXTargetCodeGenInfo : public TargetCodeGenInfo {
3131

3232
llvm::Type *getHLSLType(
3333
CodeGenModule &CGM, const Type *T,
34-
const SmallVector<unsigned> *Packoffsets = nullptr) const override;
34+
const SmallVector<int32_t> *Packoffsets = nullptr) const override;
3535
};
3636

3737
llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(
3838
CodeGenModule &CGM, const Type *Ty,
39-
const SmallVector<unsigned> *Packoffsets) const {
39+
const SmallVector<int32_t> *Packoffsets) const {
4040
auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
4141
if (!ResType)
4242
return nullptr;

clang/lib/CodeGen/Targets/SPIR.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo {
5454
llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
5555
llvm::Type *getHLSLType(
5656
CodeGenModule &CGM, const Type *Ty,
57-
const SmallVector<unsigned> *Packoffsets = nullptr) const override;
57+
const SmallVector<int32_t> *Packoffsets = nullptr) const override;
5858
llvm::Type *getSPIRVImageTypeFromHLSLResource(
5959
const HLSLAttributedResourceType::Attributes &attributes,
6060
llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
@@ -371,7 +371,7 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
371371

372372
llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(
373373
CodeGenModule &CGM, const Type *Ty,
374-
const SmallVector<unsigned> *Packoffsets) const {
374+
const SmallVector<int32_t> *Packoffsets) const {
375375
auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
376376
if (!ResType)
377377
return nullptr;

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,18 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
19611961
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
19621962
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);
19631963

1964+
// Set HasValidPackoffset if any of the decls has a register(c#) annotation;
1965+
for (const Decl *VD : DefaultCBufferDecls) {
1966+
if (const HLSLResourceBindingAttr *RBA =
1967+
VD->getAttr<HLSLResourceBindingAttr>()) {
1968+
if (RBA->getRegisterType() ==
1969+
HLSLResourceBindingAttr::RegisterType::C) {
1970+
DefaultCBuffer->setHasValidPackoffset(true);
1971+
break;
1972+
}
1973+
}
1974+
}
1975+
19641976
DeclGroupRef DG(DefaultCBuffer);
19651977
SemaRef.Consumer.HandleTopLevelDecl(DG);
19661978
}

clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s
44

55
// CHECK: %__cblayout_CB = type <{ float, double, <2 x i32> }>
6+
// CHECK: %__cblayout_CB_1 = type <{ float, <2 x float> }>
67

78
// CHECK: @CB.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88))
89
// CHECK: @a = external addrspace(2) global float, align 4
@@ -15,6 +16,17 @@ cbuffer CB : register(b1, space3) {
1516
int2 c : packoffset(c5.z);
1617
}
1718

19+
// CHECK: @CB.cb.1 = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB_1, 92, 88, 80))
20+
// CHECK: @x = external addrspace(2) global float, align 4
21+
// CHECK: @y = external addrspace(2) global <2 x float>, align 8
22+
23+
// Missing packoffset annotation will produce a warning.
24+
// Element x will be placed after the element y that has an explicit packoffset.
25+
cbuffer CB : register(b0) {
26+
float x;
27+
float2 y : packoffset(c5);
28+
}
29+
1830
// CHECK: define internal void @_init_resource_CB.cb()
1931
// CHECK-NEXT: entry:
2032
// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88))
@@ -34,5 +46,6 @@ void main() {
3446
foo();
3547
}
3648

37-
// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]}
38-
// CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}
49+
// CHECK: !hlsl.cbs = !{![[CB1:[0-9]+]], ![[CB2:[0-9]+]]}
50+
// CHECK: ![[CB1]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}
51+
// CHECK: ![[CB2]] = !{ptr @CB.cb.1, ptr addrspace(2) @x, ptr addrspace(2) @y}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-compute \
2+
// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
3+
4+
// CHECK: %"__cblayout_$Globals" = type <{ i32, float, [4 x double], <4 x i32>, <4 x float>,
5+
// CHECK-SAME: target("dx.Layout", %S, 8, 0) }>
6+
// CHECK: %S = type <{ <2 x float> }>
7+
8+
// CHECK-DAG: @b = external addrspace(2) global float, align 4
9+
// CHECK-DAG: @d = external addrspace(2) global <4 x i32>, align 16
10+
// CHECK-DAG: @"$Globals.cb" = external constant target("dx.CBuffer",
11+
// CHECK-DAG-SAME: target("dx.Layout", %"__cblayout_$Globals", 144, 120, 16, 32, 64, 128, 112))
12+
// CHECK-DAG: @a = external addrspace(2) global i32, align 4
13+
// CHECK-DAG: @c = external addrspace(2) global [4 x double], align 8
14+
// CHECK-DAG: @e = external addrspace(2) global <4 x float>, align 16
15+
// CHECK-DAG: @s = external addrspace(2) global target("dx.Layout", %S, 8, 0), align 8
16+
17+
struct S {
18+
float2 v;
19+
};
20+
21+
int a;
22+
float b : register(c1);
23+
double c[4] : register(c2);
24+
int4 d : register(c4);
25+
float4 e;
26+
S s : register(c7);
27+
28+
RWBuffer<float> Buf;
29+
30+
[numthreads(4,1,1)]
31+
void main() {
32+
Buf[0] = a;
33+
}
34+
35+
// CHECK: !hlsl.cbs = !{![[CB:.*]]}
36+
// CHECK: ![[CB]] = !{ptr @"$Globals.cb", ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c,
37+
// CHECK-SAME: ptr addrspace(2) @d, ptr addrspace(2) @e, ptr addrspace(2) @s}

0 commit comments

Comments
 (0)