Skip to content

Commit d3c8872

Browse files
committed
code review feedback - move layout calculations into a separate builder class instead of target info
1 parent 515c25e commit d3c8872

File tree

9 files changed

+303
-275
lines changed

9 files changed

+303
-275
lines changed

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
240240
RBA->getSpaceNumber());
241241
}
242242

243-
llvm::Type *
243+
llvm::TargetExtType *
244244
CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) {
245245
const auto Entry = LayoutTypes.find(StructType);
246246
if (Entry != LayoutTypes.end())
@@ -249,7 +249,7 @@ CGHLSLRuntime::getHLSLBufferLayoutType(const RecordType *StructType) {
249249
}
250250

251251
void CGHLSLRuntime::addHLSLBufferLayoutType(const RecordType *StructType,
252-
llvm::Type *LayoutTy) {
252+
llvm::TargetExtType *LayoutTy) {
253253
assert(getHLSLBufferLayoutType(StructType) == nullptr &&
254254
"layout type for this struct already exist");
255255
LayoutTypes[StructType] = LayoutTy;

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,10 @@ class CGHLSLRuntime {
162162

163163
llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
164164

165-
llvm::Type *getHLSLBufferLayoutType(const RecordType *LayoutStructTy);
165+
llvm::TargetExtType *
166+
getHLSLBufferLayoutType(const RecordType *LayoutStructTy);
166167
void addHLSLBufferLayoutType(const RecordType *LayoutStructTy,
167-
llvm::Type *LayoutTy);
168+
llvm::TargetExtType *LayoutTy);
168169
void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E);
169170

170171
private:
@@ -177,7 +178,7 @@ class CGHLSLRuntime {
177178
llvm::GlobalVariable *BufGV);
178179
llvm::Triple::ArchType getArch();
179180

180-
llvm::DenseMap<const clang::RecordType *, llvm::Type *> LayoutTypes;
181+
llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes;
181182
};
182183

183184
} // namespace CodeGen

clang/lib/CodeGen/CMakeLists.txt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,26 @@ set(LLVM_LINK_COMPONENTS
3131
Target
3232
TargetParser
3333
TransformUtils
34-
)
34+
)
3535

3636
# Workaround for MSVC ARM64 performance regression:
3737
# https://developercommunity.visualstudio.com/t/Compiling-a-specific-code-for-ARM64-with/10444970
3838
# Since /O1 and /O2 represent a set of optimizations,
3939
# our goal is to disable the /Og flag while retaining the other optimizations from the /O1|/O2 set
4040
if(MSVC AND NOT CMAKE_CXX_COMPILER_ID MATCHES Clang
41-
AND MSVC_VERSION VERSION_GREATER_EQUAL 1932
42-
AND MSVC_VERSION VERSION_LESS 1939
43-
AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64")
44-
41+
AND MSVC_VERSION VERSION_GREATER_EQUAL 1932
42+
AND MSVC_VERSION VERSION_LESS 1939
43+
AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64")
4544
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
4645
string(REGEX MATCHALL "/[Oo][12]" opt_flags "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
47-
if (opt_flags)
46+
47+
if(opt_flags)
4848
if(opt_flags MATCHES "1$")
4949
set(opt_flags "/Od;/Os;/Oy;/Ob2;/GF;/Gy")
50-
elseif (opt_flags MATCHES "2$")
50+
elseif(opt_flags MATCHES "2$")
5151
set(opt_flags "/Od;/Oi;/Ot;/Oy;/Ob2;/GF;/Gy")
5252
endif()
53+
5354
set_source_files_properties(CGBuiltin.cpp PROPERTIES COMPILE_OPTIONS "${opt_flags}")
5455
endif()
5556
endif()
@@ -106,6 +107,7 @@ add_clang_library(clangCodeGen
106107
ConstantInitBuilder.cpp
107108
CoverageMappingGen.cpp
108109
ItaniumCXXABI.cpp
110+
HLSLBufferLayoutBuilder.cpp
109111
LinkInModulesPass.cpp
110112
MacroPPCallbacks.cpp
111113
MicrosoftCXXABI.cpp
@@ -124,7 +126,6 @@ add_clang_library(clangCodeGen
124126
Targets/CSKY.cpp
125127
Targets/DirectX.cpp
126128
Targets/Hexagon.cpp
127-
Targets/HLSLTargetInfo.cpp
128129
Targets/Lanai.cpp
129130
Targets/LoongArch.cpp
130131
Targets/M68k.cpp
@@ -148,6 +149,7 @@ add_clang_library(clangCodeGen
148149
vt_gen
149150
intrinsics_gen
150151
ClangDriverOptions
152+
151153
# These generated headers are included transitively.
152154
ARMTargetParserTableGen
153155
AArch64TargetParserTableGen
@@ -159,4 +161,4 @@ add_clang_library(clangCodeGen
159161
clangFrontend
160162
clangLex
161163
clangSerialization
162-
)
164+
)
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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+
9+
#include "HLSLBufferLayoutBuilder.h"
10+
#include "CGHLSLRuntime.h"
11+
#include "CodeGenModule.h"
12+
#include "clang/AST/Type.h"
13+
14+
//===----------------------------------------------------------------------===//
15+
// Implementation of constant buffer layout common between DirectX and
16+
// SPIR/SPIR-V.
17+
//===----------------------------------------------------------------------===//
18+
19+
using namespace clang;
20+
using namespace clang::CodeGen;
21+
22+
namespace {
23+
24+
// Creates a new array type with the same dimentions but with the new
25+
// element type.
26+
static llvm::Type *
27+
createArrayWithNewElementType(CodeGenModule &CGM,
28+
const ConstantArrayType *ArrayType,
29+
llvm::Type *NewElemType) {
30+
const clang::Type *ArrayElemType = ArrayType->getArrayElementTypeNoTypeQual();
31+
if (ArrayElemType->isConstantArrayType())
32+
NewElemType = createArrayWithNewElementType(
33+
CGM, cast<const ConstantArrayType>(ArrayElemType), NewElemType);
34+
return llvm::ArrayType::get(NewElemType, ArrayType->getSExtSize());
35+
}
36+
37+
// Returns the size of a scalar or vector in bytes
38+
static unsigned getScalarOrVectorSizeInBytes(llvm::Type *Ty) {
39+
assert(Ty->isVectorTy() || Ty->isIntegerTy() || Ty->isFloatingPointTy());
40+
if (Ty->isVectorTy()) {
41+
llvm::FixedVectorType *FVT = cast<llvm::FixedVectorType>(Ty);
42+
return FVT->getNumElements() *
43+
(FVT->getElementType()->getScalarSizeInBits() / 8);
44+
}
45+
return Ty->getScalarSizeInBits() / 8;
46+
}
47+
48+
} // namespace
49+
50+
namespace clang {
51+
namespace CodeGen {
52+
53+
// Creates a layout type for given struct with HLSL constant buffer layout
54+
// taking into account Packoffsets, if provided.
55+
// Previously created layout types are cached by CGHLSLRuntime.
56+
//
57+
// The function iterates over all fields of the StructType (including base
58+
// classes) and calls layoutField to converts each field to its corresponding
59+
// LLVM type and to calculate its HLSL constant buffer layout. Any embedded
60+
// structs (or arrays of structs) are converted to target layout types as well.
61+
llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
62+
const RecordType *StructType,
63+
const llvm::SmallVector<unsigned> *Packoffsets) {
64+
65+
// check if we already have the layout type for this struct
66+
if (llvm::TargetExtType *Ty =
67+
CGM.getHLSLRuntime().getHLSLBufferLayoutType(StructType))
68+
return Ty;
69+
70+
SmallVector<unsigned> Layout;
71+
SmallVector<llvm::Type *> LayoutElements;
72+
unsigned Index = 0; // packoffset index
73+
unsigned EndOffset = 0;
74+
75+
// reserve first spot in the layout vector for buffer size
76+
Layout.push_back(0);
77+
78+
// iterate over all fields of the record, including fields on base classes
79+
llvm::SmallVector<const RecordType *> RecordTypes;
80+
RecordTypes.push_back(StructType);
81+
while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
82+
CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl();
83+
assert(D->getNumBases() == 1 &&
84+
"HLSL doesn't support multiple inheritance");
85+
RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>());
86+
}
87+
while (!RecordTypes.empty()) {
88+
const RecordType *RT = RecordTypes.back();
89+
RecordTypes.pop_back();
90+
91+
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");
95+
96+
if (!layoutField(FD, EndOffset, Layout, LayoutElements,
97+
Packoffsets ? (*Packoffsets)[Index] : -1))
98+
return nullptr;
99+
Index++;
100+
}
101+
}
102+
103+
// set the size of the buffer
104+
Layout[0] = EndOffset;
105+
106+
// create the layout struct type; anonymous struct have empty name but
107+
// non-empty qualified name
108+
const CXXRecordDecl *Decl = StructType->getAsCXXRecordDecl();
109+
std::string Name =
110+
Decl->getName().empty() ? "anon" : Decl->getQualifiedNameAsString();
111+
llvm::StructType *StructTy =
112+
llvm::StructType::create(LayoutElements, Name, true);
113+
114+
// create target layout type
115+
llvm::TargetExtType *NewLayoutTy = llvm::TargetExtType::get(
116+
CGM.getLLVMContext(), LayoutTypeName, {StructTy}, Layout);
117+
if (NewLayoutTy)
118+
CGM.getHLSLRuntime().addHLSLBufferLayoutType(StructType, NewLayoutTy);
119+
return NewLayoutTy;
120+
}
121+
122+
// The function converts a single field of HLSL Buffer to its corresponding
123+
// LLVM type and calculates it's layout. Any embedded structs (or
124+
// 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).
129+
// Returns true if the conversion was successful.
130+
// The packoffset parameter contains the field's layout offset provided by the
131+
// 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) {
135+
136+
// Size of element; for arrays this is a size of a single element in the
137+
// array. Total array size of calculated as (ArrayCount-1) * ArrayStride +
138+
// ElemSize.
139+
unsigned ElemSize = 0;
140+
unsigned ElemOffset = 0;
141+
unsigned ArrayCount = 1;
142+
unsigned ArrayStride = 0;
143+
144+
const unsigned BufferRowAlign = 16U;
145+
unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign);
146+
147+
llvm::Type *ElemLayoutTy = nullptr;
148+
QualType FieldTy = FD->getType();
149+
150+
if (FieldTy->isConstantArrayType()) {
151+
// Unwrap array to find the element type and get combined array size.
152+
QualType Ty = FieldTy;
153+
while (Ty->isConstantArrayType()) {
154+
const ConstantArrayType *ArrayTy = cast<ConstantArrayType>(Ty);
155+
ArrayCount *= ArrayTy->getSExtSize();
156+
Ty = ArrayTy->getElementType();
157+
}
158+
// For array of structures, create a new array with a layout type
159+
// instead of the structure type.
160+
if (Ty->isStructureType()) {
161+
llvm::Type *NewTy =
162+
cast<llvm::TargetExtType>(createLayoutType(Ty->getAsStructureType()));
163+
if (!NewTy)
164+
return false;
165+
assert(isa<llvm::TargetExtType>(NewTy) && "expected target type");
166+
ElemSize = cast<llvm::TargetExtType>(NewTy)->getIntParameter(0);
167+
ElemLayoutTy = createArrayWithNewElementType(
168+
CGM, cast<ConstantArrayType>(FieldTy.getTypePtr()), NewTy);
169+
} else {
170+
// Array of vectors or scalars
171+
ElemSize =
172+
getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty));
173+
ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
174+
}
175+
ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign);
176+
ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
177+
178+
} else if (FieldTy->isStructureType()) {
179+
// Create a layout type for the structure
180+
ElemLayoutTy = createLayoutType(FieldTy->getAsStructureType());
181+
if (!ElemLayoutTy)
182+
return false;
183+
assert(isa<llvm::TargetExtType>(ElemLayoutTy) && "expected target type");
184+
ElemSize = cast<llvm::TargetExtType>(ElemLayoutTy)->getIntParameter(0);
185+
ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
186+
187+
} else {
188+
// scalar or vector - find element size and alignment
189+
unsigned Align = 0;
190+
ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
191+
if (ElemLayoutTy->isVectorTy()) {
192+
// align vectors by sub element size
193+
const llvm::FixedVectorType *FVT =
194+
cast<llvm::FixedVectorType>(ElemLayoutTy);
195+
unsigned SubElemSize = FVT->getElementType()->getScalarSizeInBits() / 8;
196+
ElemSize = FVT->getNumElements() * SubElemSize;
197+
Align = SubElemSize;
198+
} else {
199+
assert(ElemLayoutTy->isIntegerTy() || ElemLayoutTy->isFloatingPointTy());
200+
ElemSize = ElemLayoutTy->getScalarSizeInBits() / 8;
201+
Align = ElemSize;
202+
}
203+
204+
// calculate or get element offset for the vector or scalar
205+
if (Packoffset != -1) {
206+
ElemOffset = Packoffset;
207+
} else {
208+
ElemOffset = llvm::alignTo(EndOffset, Align);
209+
// if the element does not fit, move it to the next row
210+
if (ElemOffset + ElemSize > NextRowOffset)
211+
ElemOffset = NextRowOffset;
212+
}
213+
}
214+
215+
// Update end offset of the layout; do not update it if the EndOffset
216+
// is already bigger than the new value (which may happen with unordered
217+
// packoffset annotations)
218+
unsigned NewEndOffset =
219+
ElemOffset + (ArrayCount - 1) * ArrayStride + ElemSize;
220+
EndOffset = std::max<unsigned>(EndOffset, NewEndOffset);
221+
222+
// add the layout element and offset to the lists
223+
Layout.push_back(ElemOffset);
224+
LayoutElements.push_back(ElemLayoutTy);
225+
return true;
226+
}
227+
228+
} // namespace CodeGen
229+
} // namespace clang
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===- HLSLBufferLayoutBuilder.h ------------------------------------------===//
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+
9+
#include "llvm/ADT/StringRef.h"
10+
#include "llvm/IR/DerivedTypes.h"
11+
12+
namespace clang {
13+
class RecordType;
14+
class FieldDecl;
15+
16+
namespace CodeGen {
17+
class CodeGenModule;
18+
19+
//===----------------------------------------------------------------------===//
20+
// Implementation of constant buffer layout common between DirectX and
21+
// SPIR/SPIR-V.
22+
//===----------------------------------------------------------------------===//
23+
24+
class HLSLBufferLayoutBuilder {
25+
private:
26+
CodeGenModule &CGM;
27+
llvm::StringRef LayoutTypeName;
28+
29+
public:
30+
HLSLBufferLayoutBuilder(CodeGenModule &CGM, llvm::StringRef LayoutTypeName)
31+
: CGM(CGM), LayoutTypeName(LayoutTypeName) {}
32+
33+
// Returns LLVM target extension type with the name LayoutTypeName
34+
// for given structure type and layout data. The first number in
35+
// the Layout is the size followed by offsets for each struct element.
36+
llvm::TargetExtType *
37+
createLayoutType(const RecordType *StructType,
38+
const llvm::SmallVector<unsigned> *Packoffsets = nullptr);
39+
40+
private:
41+
bool layoutField(const clang::FieldDecl *FD, unsigned &EndOffset,
42+
llvm::SmallVector<unsigned> &Layout,
43+
llvm::SmallVector<llvm::Type *> &LayoutElements,
44+
int Packoffset);
45+
};
46+
47+
} // namespace CodeGen
48+
} // namespace clang

0 commit comments

Comments
 (0)