|
| 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 |
0 commit comments