clang 22.0.0git
HLSLBufferLayoutBuilder.cpp
Go to the documentation of this file.
1//===- HLSLBufferLayoutBuilder.cpp ----------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "CGHLSLRuntime.h"
11#include "CodeGenModule.h"
12#include "clang/AST/Type.h"
13#include <climits>
14
15//===----------------------------------------------------------------------===//
16// Implementation of constant buffer layout common between DirectX and
17// SPIR/SPIR-V.
18//===----------------------------------------------------------------------===//
19
20using namespace clang;
21using namespace clang::CodeGen;
22using llvm::hlsl::CBufferRowSizeInBytes;
23
24namespace {
25
26// Creates a new array type with the same dimentions but with the new
27// element type.
28static llvm::Type *
29createArrayWithNewElementType(CodeGenModule &CGM,
31 llvm::Type *NewElemType) {
32 const clang::Type *ArrayElemType = ArrayType->getArrayElementTypeNoTypeQual();
33 if (ArrayElemType->isConstantArrayType())
34 NewElemType = createArrayWithNewElementType(
35 CGM, cast<const ConstantArrayType>(ArrayElemType), NewElemType);
36 return llvm::ArrayType::get(NewElemType, ArrayType->getSExtSize());
37}
38
39// Returns the size of a scalar or vector in bytes
40static unsigned getScalarOrVectorSizeInBytes(llvm::Type *Ty) {
41 assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy());
42 if (Ty->isVectorTy()) {
43 llvm::FixedVectorType *FVT = cast<llvm::FixedVectorType>(Ty);
44 return FVT->getNumElements() *
45 (FVT->getElementType()->getScalarSizeInBits() / 8);
46 }
47 return Ty->getScalarSizeInBits() / 8;
48}
49
50} // namespace
51
52namespace clang {
53namespace CodeGen {
54
55// Creates a layout type for given struct or class with HLSL constant buffer
56// layout taking into account PackOffsets, if provided.
57// Previously created layout types are cached by CGHLSLRuntime.
58//
59// The function iterates over all fields of the record type (including base
60// classes) and calls layoutField to converts each field to its corresponding
61// LLVM type and to calculate its HLSL constant buffer layout. Any embedded
62// structs (or arrays of structs) are converted to target layout types as well.
63//
64// When PackOffsets are specified the elements will be placed based on the
65// user-specified offsets. Not all elements must have a packoffset/register(c#)
66// annotation though. For those that don't, the PackOffsets array will contain
67// -1 value instead. These elements must be placed at the end of the layout
68// after all of the elements with specific offset.
70 const RecordType *RT, const llvm::SmallVector<int32_t> *PackOffsets) {
71
72 // check if we already have the layout type for this struct
73 if (llvm::TargetExtType *Ty =
75 return Ty;
76
78 SmallVector<llvm::Type *> LayoutElements;
79 unsigned Index = 0; // packoffset index
80 unsigned EndOffset = 0;
81
83
84 // reserve first spot in the layout vector for buffer size
85 Layout.push_back(0);
86
87 // iterate over all fields of the record, including fields on base classes
89 RecordDecls.push_back(RT->castAsCXXRecordDecl());
90 while (RecordDecls.back()->getNumBases()) {
91 CXXRecordDecl *D = RecordDecls.back();
92 assert(D->getNumBases() == 1 &&
93 "HLSL doesn't support multiple inheritance");
94 RecordDecls.push_back(D->bases_begin()->getType()->castAsCXXRecordDecl());
95 }
96
97 unsigned FieldOffset;
98 llvm::Type *FieldType;
99
100 while (!RecordDecls.empty()) {
101 const CXXRecordDecl *RD = RecordDecls.pop_back_val();
102
103 for (const auto *FD : RD->fields()) {
104 assert((!PackOffsets || Index < PackOffsets->size()) &&
105 "number of elements in layout struct does not match number of "
106 "packoffset annotations");
107
108 // No PackOffset info at all, or have a valid packoffset/register(c#)
109 // annotations value -> layout the field.
110 const int PO = PackOffsets ? (*PackOffsets)[Index++] : -1;
111 if (!PackOffsets || PO != -1) {
112 if (!layoutField(FD, EndOffset, FieldOffset, FieldType, PO))
113 return nullptr;
114 Layout.push_back(FieldOffset);
115 LayoutElements.push_back(FieldType);
116 continue;
117 }
118 // Have PackOffset info, but there is no packoffset/register(cX)
119 // annotation on this field. Delay the layout until after all of the
120 // other elements with packoffsets/register(cX) are processed.
121 DelayLayoutFields.emplace_back(FD, LayoutElements.size());
122 // reserve space for this field in the layout vector and elements list
123 Layout.push_back(UINT_MAX);
124 LayoutElements.push_back(nullptr);
125 }
126 }
127
128 // process delayed layouts
129 for (auto I : DelayLayoutFields) {
130 const FieldDecl *FD = I.first;
131 const unsigned IndexInLayoutElements = I.second;
132 // the first item in layout vector is size, so we need to offset the index
133 // by 1
134 const unsigned IndexInLayout = IndexInLayoutElements + 1;
135 assert(Layout[IndexInLayout] == UINT_MAX &&
136 LayoutElements[IndexInLayoutElements] == nullptr);
137
138 if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
139 return nullptr;
140 Layout[IndexInLayout] = FieldOffset;
141 LayoutElements[IndexInLayoutElements] = FieldType;
142 }
143
144 // set the size of the buffer
145 Layout[0] = EndOffset;
146
147 // create the layout struct type; anonymous struct have empty name but
148 // non-empty qualified name
149 const auto *Decl = RT->castAsCXXRecordDecl();
150 std::string Name =
151 Decl->getName().empty() ? "anon" : Decl->getQualifiedNameAsString();
152 llvm::StructType *StructTy =
153 llvm::StructType::create(LayoutElements, Name, true);
154
155 // create target layout type
156 llvm::TargetExtType *NewLayoutTy = llvm::TargetExtType::get(
157 CGM.getLLVMContext(), LayoutTypeName, {StructTy}, Layout);
158 if (NewLayoutTy)
159 CGM.getHLSLRuntime().addHLSLBufferLayoutType(RT, NewLayoutTy);
160 return NewLayoutTy;
161}
162
163// The function converts a single field of HLSL Buffer to its corresponding
164// LLVM type and calculates it's layout. Any embedded structs (or
165// arrays of structs) are converted to target layout types as well.
166// The converted type is set to the FieldType parameter, the element
167// offset is set to the FieldOffset parameter. The EndOffset (=size of the
168// buffer) is also updated accordingly to the offset just after the placed
169// element, unless the incoming EndOffset already larger (may happen in case
170// of unsorted packoffset annotations).
171// Returns true if the conversion was successful.
172// The packoffset parameter contains the field's layout offset provided by the
173// user or -1 if there was no packoffset (or register(cX)) annotation.
174bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
175 unsigned &EndOffset,
176 unsigned &FieldOffset,
177 llvm::Type *&FieldType,
178 int Packoffset) {
179
180 // Size of element; for arrays this is a size of a single element in the
181 // array. Total array size of calculated as (ArrayCount-1) * ArrayStride +
182 // ElemSize.
183 unsigned ElemSize = 0;
184 unsigned ElemOffset = 0;
185 unsigned ArrayCount = 1;
186 unsigned ArrayStride = 0;
187
188 unsigned NextRowOffset = llvm::alignTo(EndOffset, CBufferRowSizeInBytes);
189
190 llvm::Type *ElemLayoutTy = nullptr;
191 QualType FieldTy = FD->getType();
192
193 if (FieldTy->isConstantArrayType()) {
194 // Unwrap array to find the element type and get combined array size.
195 QualType Ty = FieldTy;
196 while (Ty->isConstantArrayType()) {
197 auto *ArrayTy = CGM.getContext().getAsConstantArrayType(Ty);
198 ArrayCount *= ArrayTy->getSExtSize();
199 Ty = ArrayTy->getElementType();
200 }
201 // For array of structures, create a new array with a layout type
202 // instead of the structure type.
203 if (Ty->isStructureOrClassType()) {
204 llvm::Type *NewTy = cast<llvm::TargetExtType>(
206 if (!NewTy)
207 return false;
208 assert(isa<llvm::TargetExtType>(NewTy) && "expected target type");
209 ElemSize = cast<llvm::TargetExtType>(NewTy)->getIntParameter(0);
210 ElemLayoutTy = createArrayWithNewElementType(
211 CGM, cast<ConstantArrayType>(FieldTy.getTypePtr()), NewTy);
212 } else {
213 // Array of vectors or scalars
214 ElemSize =
215 getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty));
216 ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
217 }
218 ArrayStride = llvm::alignTo(ElemSize, CBufferRowSizeInBytes);
219 ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
220
221 } else if (FieldTy->isStructureOrClassType()) {
222 // Create a layout type for the structure
223 ElemLayoutTy = createLayoutType(
224 cast<RecordType>(FieldTy->getAsCanonical<RecordType>()));
225 if (!ElemLayoutTy)
226 return false;
227 assert(isa<llvm::TargetExtType>(ElemLayoutTy) && "expected target type");
228 ElemSize = cast<llvm::TargetExtType>(ElemLayoutTy)->getIntParameter(0);
229 ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
230
231 } else {
232 // scalar or vector - find element size and alignment
233 unsigned Align = 0;
234 ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
235 if (ElemLayoutTy->isVectorTy()) {
236 // align vectors by sub element size
237 const llvm::FixedVectorType *FVT =
238 cast<llvm::FixedVectorType>(ElemLayoutTy);
239 unsigned SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8;
240 ElemSize = FVT->getNumElements() * SubElemSize;
241 Align = SubElemSize;
242 } else {
243 assert(ElemLayoutTy->isIntegerTy() || ElemLayoutTy->isFloatingPointTy());
244 ElemSize = ElemLayoutTy->getScalarSizeInBits() / 8;
245 Align = ElemSize;
246 }
247
248 // calculate or get element offset for the vector or scalar
249 if (Packoffset != -1) {
250 ElemOffset = Packoffset;
251 } else {
252 ElemOffset = llvm::alignTo(EndOffset, Align);
253 // if the element does not fit, move it to the next row
254 if (ElemOffset + ElemSize > NextRowOffset)
255 ElemOffset = NextRowOffset;
256 }
257 }
258
259 // Update end offset of the layout; do not update it if the EndOffset
260 // is already bigger than the new value (which may happen with unordered
261 // packoffset annotations)
262 unsigned NewEndOffset =
263 ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize;
264 EndOffset = std::max<unsigned>(EndOffset, NewEndOffset);
265
266 // add the layout element and offset to the lists
267 FieldOffset = ElemOffset;
268 FieldType = ElemLayoutTy;
269 return true;
270}
271
272} // namespace CodeGen
273} // namespace clang
const Decl * D
C Language Family Type Representation.
const ConstantArrayType * getAsConstantArrayType(QualType T) const
Definition: ASTContext.h:3056
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition: TypeBase.h:3738
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
void addHLSLBufferLayoutType(const RecordType *LayoutStructTy, llvm::TargetExtType *LayoutTy)
llvm::TargetExtType * getHLSLBufferLayoutType(const RecordType *LayoutStructTy)
This class organizes the cross-function state that is used while generating LLVM code.
CGHLSLRuntime & getHLSLRuntime()
Return a reference to the configured HLSL runtime.
ASTContext & getContext() const
llvm::LLVMContext & getLLVMContext()
llvm::Type * ConvertTypeForMem(QualType T)
ConvertTypeForMem - Convert type T into a llvm::Type.
llvm::TargetExtType * createLayoutType(const RecordType *StructType, const llvm::SmallVector< int32_t > *Packoffsets=nullptr)
Represents the canonical version of C arrays with a specified constant size.
Definition: TypeBase.h:3776
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
Represents a member of a struct/union/class.
Definition: Decl.h:3157
A (possibly-)qualified type.
Definition: TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: TypeBase.h:8343
field_range fields() const
Definition: Decl.h:4512
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Definition: TypeBase.h:6502
The base class of the type hierarchy.
Definition: TypeBase.h:1833
bool isConstantArrayType() const
Definition: TypeBase.h:8683
CXXRecordDecl * castAsCXXRecordDecl() const
Definition: Type.h:36
const Type * getArrayElementTypeNoTypeQual() const
If this is an array type, return the element type of the array, potentially with type qualifiers miss...
Definition: Type.cpp:471
bool isStructureOrClassType() const
Definition: Type.cpp:706
const T * getAsCanonical() const
If this type is canonically the specified type, return its canonical type cast to that specified type...
Definition: TypeBase.h:2939
QualType getType() const
Definition: Decl.h:722
#define UINT_MAX
Definition: limits.h:64
The JSON file list parser is used to communicate input to InstallAPI.