diff options
Diffstat (limited to 'clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp')
| -rw-r--r-- | clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp | 548 |
1 files changed, 273 insertions, 275 deletions
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp index ac56dda74abb..838903cdcd1e 100644 --- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp +++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp @@ -1,275 +1,273 @@ -//===- 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"
-#include <climits>
-
-//===----------------------------------------------------------------------===//
-// Implementation of constant buffer layout common between DirectX and
-// SPIR/SPIR-V.
-//===----------------------------------------------------------------------===//
-
-using namespace clang;
-using namespace clang::CodeGen;
-using llvm::hlsl::CBufferRowSizeInBytes;
-
-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<const ConstantArrayType>(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<llvm::FixedVectorType>(Ty);
- return FVT->getNumElements() *
- (FVT->getElementType()->getScalarSizeInBits() / 8);
- }
- return Ty->getScalarSizeInBits() / 8;
-}
-
-} // namespace
-
-namespace clang {
-namespace CodeGen {
-
-// Creates a layout type for given struct or class 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 record type (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.
-//
-// 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 contain
-// -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 *RT, const llvm::SmallVector<int32_t> *PackOffsets) {
-
- // check if we already have the layout type for this struct
- if (llvm::TargetExtType *Ty =
- CGM.getHLSLRuntime().getHLSLBufferLayoutType(RT))
- return Ty;
-
- SmallVector<unsigned> Layout;
- SmallVector<llvm::Type *> LayoutElements;
- 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);
-
- // iterate over all fields of the record, including fields on base classes
- llvm::SmallVector<const RecordType *> RecordTypes;
- RecordTypes.push_back(RT);
- 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<RecordType>());
- }
-
- unsigned FieldOffset;
- llvm::Type *FieldType;
-
- while (!RecordTypes.empty()) {
- const RecordType *RT = RecordTypes.back();
- RecordTypes.pop_back();
-
- for (const auto *FD :
- RT->getOriginalDecl()->getDefinitionOrSelf()->fields()) {
- assert((!PackOffsets || Index < PackOffsets->size()) &&
- "number of elements in layout struct does not match number of "
- "packoffset annotations");
-
- // No PackOffset info at all, or have a valid packoffset/register(c#)
- // annotations value -> layout the field.
- const int PO = PackOffsets ? (*PackOffsets)[Index++] : -1;
- if (!PackOffsets || PO != -1) {
- if (!layoutField(FD, EndOffset, FieldOffset, FieldType, PO))
- return nullptr;
- Layout.push_back(FieldOffset);
- LayoutElements.push_back(FieldType);
- continue;
- }
- // Have PackOffset info, but there is no packoffset/register(cX)
- // annotation on this field. Delay the layout until after all of the
- // other elements 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);
- }
- }
-
- // process delayed layouts
- for (auto I : DelayLayoutFields) {
- const FieldDecl *FD = I.first;
- const unsigned IndexInLayoutElements = I.second;
- // the first item in layout vector is size, so we need to offset the index
- // by 1
- const unsigned IndexInLayout = IndexInLayoutElements + 1;
- assert(Layout[IndexInLayout] == UINT_MAX &&
- LayoutElements[IndexInLayoutElements] == nullptr);
-
- if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
- return nullptr;
- Layout[IndexInLayout] = FieldOffset;
- LayoutElements[IndexInLayoutElements] = FieldType;
- }
-
- // 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 = RT->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(RT, 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 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,
- 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 +
- // ElemSize.
- unsigned ElemSize = 0;
- unsigned ElemOffset = 0;
- unsigned ArrayCount = 1;
- unsigned ArrayStride = 0;
-
- unsigned NextRowOffset = llvm::alignTo(EndOffset, CBufferRowSizeInBytes);
-
- 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()) {
- auto *ArrayTy = CGM.getContext().getAsConstantArrayType(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->isStructureOrClassType()) {
- llvm::Type *NewTy =
- cast<llvm::TargetExtType>(createLayoutType(Ty->getAs<RecordType>()));
- if (!NewTy)
- return false;
- assert(isa<llvm::TargetExtType>(NewTy) && "expected target type");
- ElemSize = cast<llvm::TargetExtType>(NewTy)->getIntParameter(0);
- ElemLayoutTy = createArrayWithNewElementType(
- CGM, cast<ConstantArrayType>(FieldTy.getTypePtr()), NewTy);
- } else {
- // Array of vectors or scalars
- ElemSize =
- getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty));
- ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
- }
- ArrayStride = llvm::alignTo(ElemSize, CBufferRowSizeInBytes);
- ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;
-
- } else if (FieldTy->isStructureOrClassType()) {
- // Create a layout type for the structure
- ElemLayoutTy =
- createLayoutType(cast<RecordType>(FieldTy->getAs<RecordType>()));
- if (!ElemLayoutTy)
- return false;
- assert(isa<llvm::TargetExtType>(ElemLayoutTy) && "expected target type");
- ElemSize = cast<llvm::TargetExtType>(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<llvm::FixedVectorType>(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<unsigned>(EndOffset, NewEndOffset);
-
- // add the layout element and offset to the lists
- FieldOffset = ElemOffset;
- FieldType = ElemLayoutTy;
- return true;
-}
-
-} // namespace CodeGen
-} // namespace clang
+//===- 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" +#include <climits> + +//===----------------------------------------------------------------------===// +// Implementation of constant buffer layout common between DirectX and +// SPIR/SPIR-V. +//===----------------------------------------------------------------------===// + +using namespace clang; +using namespace clang::CodeGen; +using llvm::hlsl::CBufferRowSizeInBytes; + +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<const ConstantArrayType>(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<llvm::FixedVectorType>(Ty); + return FVT->getNumElements() * + (FVT->getElementType()->getScalarSizeInBits() / 8); + } + return Ty->getScalarSizeInBits() / 8; +} + +} // namespace + +namespace clang { +namespace CodeGen { + +// Creates a layout type for given struct or class 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 record type (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. +// +// 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 contain +// -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 *RT, const llvm::SmallVector<int32_t> *PackOffsets) { + + // check if we already have the layout type for this struct + if (llvm::TargetExtType *Ty = + CGM.getHLSLRuntime().getHLSLBufferLayoutType(RT)) + return Ty; + + SmallVector<unsigned> Layout; + SmallVector<llvm::Type *> LayoutElements; + 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); + + // iterate over all fields of the record, including fields on base classes + llvm::SmallVector<CXXRecordDecl *> RecordDecls; + RecordDecls.push_back(RT->castAsCXXRecordDecl()); + while (RecordDecls.back()->getNumBases()) { + CXXRecordDecl *D = RecordDecls.back(); + assert(D->getNumBases() == 1 && + "HLSL doesn't support multiple inheritance"); + RecordDecls.push_back(D->bases_begin()->getType()->castAsCXXRecordDecl()); + } + + unsigned FieldOffset; + llvm::Type *FieldType; + + while (!RecordDecls.empty()) { + const CXXRecordDecl *RD = RecordDecls.pop_back_val(); + + for (const auto *FD : RD->fields()) { + assert((!PackOffsets || Index < PackOffsets->size()) && + "number of elements in layout struct does not match number of " + "packoffset annotations"); + + // No PackOffset info at all, or have a valid packoffset/register(c#) + // annotations value -> layout the field. + const int PO = PackOffsets ? (*PackOffsets)[Index++] : -1; + if (!PackOffsets || PO != -1) { + if (!layoutField(FD, EndOffset, FieldOffset, FieldType, PO)) + return nullptr; + Layout.push_back(FieldOffset); + LayoutElements.push_back(FieldType); + continue; + } + // Have PackOffset info, but there is no packoffset/register(cX) + // annotation on this field. Delay the layout until after all of the + // other elements 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); + } + } + + // process delayed layouts + for (auto I : DelayLayoutFields) { + const FieldDecl *FD = I.first; + const unsigned IndexInLayoutElements = I.second; + // the first item in layout vector is size, so we need to offset the index + // by 1 + const unsigned IndexInLayout = IndexInLayoutElements + 1; + assert(Layout[IndexInLayout] == UINT_MAX && + LayoutElements[IndexInLayoutElements] == nullptr); + + if (!layoutField(FD, EndOffset, FieldOffset, FieldType)) + return nullptr; + Layout[IndexInLayout] = FieldOffset; + LayoutElements[IndexInLayoutElements] = FieldType; + } + + // 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 auto *Decl = RT->castAsCXXRecordDecl(); + 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(RT, 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 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, + 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 + + // ElemSize. + unsigned ElemSize = 0; + unsigned ElemOffset = 0; + unsigned ArrayCount = 1; + unsigned ArrayStride = 0; + + unsigned NextRowOffset = llvm::alignTo(EndOffset, CBufferRowSizeInBytes); + + 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()) { + auto *ArrayTy = CGM.getContext().getAsConstantArrayType(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->isStructureOrClassType()) { + llvm::Type *NewTy = cast<llvm::TargetExtType>( + createLayoutType(Ty->getAsCanonical<RecordType>())); + if (!NewTy) + return false; + assert(isa<llvm::TargetExtType>(NewTy) && "expected target type"); + ElemSize = cast<llvm::TargetExtType>(NewTy)->getIntParameter(0); + ElemLayoutTy = createArrayWithNewElementType( + CGM, cast<ConstantArrayType>(FieldTy.getTypePtr()), NewTy); + } else { + // Array of vectors or scalars + ElemSize = + getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty)); + ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy); + } + ArrayStride = llvm::alignTo(ElemSize, CBufferRowSizeInBytes); + ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset; + + } else if (FieldTy->isStructureOrClassType()) { + // Create a layout type for the structure + ElemLayoutTy = createLayoutType( + cast<RecordType>(FieldTy->getAsCanonical<RecordType>())); + if (!ElemLayoutTy) + return false; + assert(isa<llvm::TargetExtType>(ElemLayoutTy) && "expected target type"); + ElemSize = cast<llvm::TargetExtType>(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<llvm::FixedVectorType>(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<unsigned>(EndOffset, NewEndOffset); + + // add the layout element and offset to the lists + FieldOffset = ElemOffset; + FieldType = ElemLayoutTy; + return true; +} + +} // namespace CodeGen +} // namespace clang |
