|
@@ -17,6 +17,38 @@
|
|
|
#include "clang/SPIRV/String.h"
|
|
|
|
|
|
namespace {
|
|
|
+
|
|
|
+/// The alignment for 4-component float vectors.
|
|
|
+constexpr uint32_t kStd140Vec4Alignment = 16u;
|
|
|
+
|
|
|
+/// Rounds the given value up to the given power of 2.
|
|
|
+inline uint32_t roundToPow2(uint32_t val, uint32_t pow2) {
|
|
|
+ assert(pow2 != 0);
|
|
|
+ return (val + pow2 - 1) & ~(pow2 - 1);
|
|
|
+}
|
|
|
+
|
|
|
+/// Returns true if the given vector type (of the given size) crosses the
|
|
|
+/// 4-component vector boundary if placed at the given offset.
|
|
|
+bool improperStraddle(const clang::spirv::VectorType *type, int size,
|
|
|
+ int offset) {
|
|
|
+ return size <= 16 ? offset / 16 != (offset + size - 1) / 16
|
|
|
+ : offset % 16 != 0;
|
|
|
+}
|
|
|
+
|
|
|
+bool isAKindOfStructuredOrByteBuffer(const clang::spirv::SpirvType *type) {
|
|
|
+ // Strip outer arrayness first
|
|
|
+ while (llvm::isa<clang::spirv::ArrayType>(type))
|
|
|
+ type = llvm::cast<clang::spirv::ArrayType>(type)->getElementType();
|
|
|
+
|
|
|
+ // They are structures with the first member that is of RuntimeArray type.
|
|
|
+ if (auto *structType = llvm::dyn_cast<clang::spirv::StructType>(type))
|
|
|
+ return structType->getFields().size() == 1 &&
|
|
|
+ llvm::isa<clang::spirv::RuntimeArrayType>(
|
|
|
+ structType->getFields()[0].type);
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
uint32_t zeroExtendTo32Bits(uint16_t value) {
|
|
|
// TODO: The ordering of the 2 words depends on the endian-ness of the host
|
|
|
// machine. Assuming Little Endian at the moment.
|
|
@@ -956,7 +988,8 @@ uint32_t EmitTypeHandler::emitType(const SpirvType *type,
|
|
|
else if (const auto *arrayType = dyn_cast<ArrayType>(type)) {
|
|
|
// Emit the OpConstant instruction that is needed to get the result-id for
|
|
|
// the array length.
|
|
|
- auto *constant = context.getConstantUint32(arrayType->getElementCount());
|
|
|
+ SpirvConstant *constant =
|
|
|
+ spirvContext.getConstantUint32(arrayType->getElementCount());
|
|
|
if (constant->getResultId() == 0) {
|
|
|
constant->setResultId(takeNextIdFunction());
|
|
|
}
|
|
@@ -975,6 +1008,16 @@ uint32_t EmitTypeHandler::emitType(const SpirvType *type,
|
|
|
curTypeInst.push_back(elemTypeId);
|
|
|
curTypeInst.push_back(constant->getResultId());
|
|
|
finalizeTypeInstruction();
|
|
|
+
|
|
|
+ // ArrayStride decoration is needed for array types, but we won't have
|
|
|
+ // stride information for structured/byte buffers since they contain runtime
|
|
|
+ // arrays.
|
|
|
+ if (rule != SpirvLayoutRule::Void &&
|
|
|
+ !isAKindOfStructuredOrByteBuffer(type)) {
|
|
|
+ uint32_t stride = 0;
|
|
|
+ (void)getAlignmentAndSize(type, rule, &stride);
|
|
|
+ emitDecoration(id, spv::Decoration::ArrayStride, {stride});
|
|
|
+ }
|
|
|
}
|
|
|
// RuntimeArray types
|
|
|
else if (const auto *raType = dyn_cast<RuntimeArrayType>(type)) {
|
|
@@ -983,17 +1026,39 @@ uint32_t EmitTypeHandler::emitType(const SpirvType *type,
|
|
|
curTypeInst.push_back(id);
|
|
|
curTypeInst.push_back(elemTypeId);
|
|
|
finalizeTypeInstruction();
|
|
|
+
|
|
|
+ // ArrayStride decoration is needed for runtime array types.
|
|
|
+ if (rule != SpirvLayoutRule::Void) {
|
|
|
+ uint32_t stride = 0;
|
|
|
+ (void)getAlignmentAndSize(type, rule, &stride);
|
|
|
+ emitDecoration(id, spv::Decoration::ArrayStride, {stride});
|
|
|
+ }
|
|
|
}
|
|
|
// Structure types
|
|
|
else if (const auto *structType = dyn_cast<StructType>(type)) {
|
|
|
llvm::SmallVector<uint32_t, 4> fieldTypeIds;
|
|
|
- for (auto *fieldType : structType->getFieldTypes())
|
|
|
- fieldTypeIds.push_back(emitType(fieldType, rule));
|
|
|
+ for (auto &field : structType->getFields())
|
|
|
+ fieldTypeIds.push_back(emitType(field.type, rule));
|
|
|
initTypeInstruction(spv::Op::OpTypeStruct);
|
|
|
curTypeInst.push_back(id);
|
|
|
for (auto fieldTypeId : fieldTypeIds)
|
|
|
curTypeInst.push_back(fieldTypeId);
|
|
|
finalizeTypeInstruction();
|
|
|
+
|
|
|
+ // Emit the layout decorations for the structure.
|
|
|
+ emitLayoutDecorations(structType, rule);
|
|
|
+
|
|
|
+ // Emit NonWritable decorations
|
|
|
+ if (structType->isReadOnly())
|
|
|
+ for (size_t i = 0; i < structType->getFields().size(); ++i)
|
|
|
+ emitDecoration(id, spv::Decoration::NonWritable, {}, i);
|
|
|
+
|
|
|
+ // Emit Block or BufferBlock decorations if necessary.
|
|
|
+ auto interfaceType = structType->getInterfaceType();
|
|
|
+ if (interfaceType == StructType::InterfaceType::StorageBuffer)
|
|
|
+ emitDecoration(id, spv::Decoration::BufferBlock, {});
|
|
|
+ else if (interfaceType == StructType::InterfaceType::UniformBuffer)
|
|
|
+ emitDecoration(id, spv::Decoration::Block, {});
|
|
|
}
|
|
|
// Pointer types
|
|
|
else if (const auto *ptrType = dyn_cast<SpirvPointerType>(type)) {
|
|
@@ -1026,5 +1091,389 @@ uint32_t EmitTypeHandler::emitType(const SpirvType *type,
|
|
|
return id;
|
|
|
}
|
|
|
|
|
|
+std::pair<uint32_t, uint32_t>
|
|
|
+EmitTypeHandler::getAlignmentAndSize(const SpirvType *type,
|
|
|
+ SpirvLayoutRule rule, uint32_t *stride) {
|
|
|
+ // std140 layout rules:
|
|
|
+
|
|
|
+ // 1. If the member is a scalar consuming N basic machine units, the base
|
|
|
+ // alignment is N.
|
|
|
+ //
|
|
|
+ // 2. If the member is a two- or four-component vector with components
|
|
|
+ // consuming N basic machine units, the base alignment is 2N or 4N,
|
|
|
+ // respectively.
|
|
|
+ //
|
|
|
+ // 3. If the member is a three-component vector with components consuming N
|
|
|
+ // basic machine units, the base alignment is 4N.
|
|
|
+ //
|
|
|
+ // 4. If the member is an array of scalars or vectors, the base alignment and
|
|
|
+ // array stride are set to match the base alignment of a single array
|
|
|
+ // element, according to rules (1), (2), and (3), and rounded up to the
|
|
|
+ // base alignment of a vec4. The array may have padding at the end; the
|
|
|
+ // base offset of the member following the array is rounded up to the next
|
|
|
+ // multiple of the base alignment.
|
|
|
+ //
|
|
|
+ // 5. If the member is a column-major matrix with C columns and R rows, the
|
|
|
+ // matrix is stored identically to an array of C column vectors with R
|
|
|
+ // components each, according to rule (4).
|
|
|
+ //
|
|
|
+ // 6. If the member is an array of S column-major matrices with C columns and
|
|
|
+ // R rows, the matrix is stored identically to a row of S X C column
|
|
|
+ // vectors with R components each, according to rule (4).
|
|
|
+ //
|
|
|
+ // 7. If the member is a row-major matrix with C columns and R rows, the
|
|
|
+ // matrix is stored identically to an array of R row vectors with C
|
|
|
+ // components each, according to rule (4).
|
|
|
+ //
|
|
|
+ // 8. If the member is an array of S row-major matrices with C columns and R
|
|
|
+ // rows, the matrix is stored identically to a row of S X R row vectors
|
|
|
+ // with C components each, according to rule (4).
|
|
|
+ //
|
|
|
+ // 9. If the member is a structure, the base alignment of the structure is N,
|
|
|
+ // where N is the largest base alignment value of any of its members, and
|
|
|
+ // rounded up to the base alignment of a vec4. The individual members of
|
|
|
+ // this substructure are then assigned offsets by applying this set of
|
|
|
+ // rules recursively, where the base offset of the first member of the
|
|
|
+ // sub-structure is equal to the aligned offset of the structure. The
|
|
|
+ // structure may have padding at the end; the base offset of the member
|
|
|
+ // following the sub-structure is rounded up to the next multiple of the
|
|
|
+ // base alignment of the structure.
|
|
|
+ //
|
|
|
+ // 10. If the member is an array of S structures, the S elements of the array
|
|
|
+ // are laid out in order, according to rule (9).
|
|
|
+ //
|
|
|
+ // This method supports multiple layout rules, all of them modifying the
|
|
|
+ // std140 rules listed above:
|
|
|
+ //
|
|
|
+ // std430:
|
|
|
+ // - Array base alignment and stride does not need to be rounded up to a
|
|
|
+ // multiple of 16.
|
|
|
+ // - Struct base alignment does not need to be rounded up to a multiple of 16.
|
|
|
+ //
|
|
|
+ // Relaxed std140/std430:
|
|
|
+ // - Vector base alignment is set as its element type's base alignment.
|
|
|
+ //
|
|
|
+ // FxcCTBuffer:
|
|
|
+ // - Vector base alignment is set as its element type's base alignment.
|
|
|
+ // - Arrays/structs do not need to have padding at the end; arrays/structs do
|
|
|
+ // not affect the base offset of the member following them.
|
|
|
+ //
|
|
|
+ // FxcSBuffer:
|
|
|
+ // - Vector/matrix/array base alignment is set as its element type's base
|
|
|
+ // alignment.
|
|
|
+ // - Arrays/structs do not need to have padding at the end; arrays/structs do
|
|
|
+ // not affect the base offset of the member following them.
|
|
|
+ // - Struct base alignment does not need to be rounded up to a multiple of 16.
|
|
|
+
|
|
|
+ { // Rule 1
|
|
|
+ if (isa<BoolType>(type))
|
|
|
+ return {4, 4};
|
|
|
+ // Integer and Float types are NumericalType
|
|
|
+ if (auto *numericType = dyn_cast<NumericalType>(type)) {
|
|
|
+ switch (numericType->getBitwidth()) {
|
|
|
+ case 64:
|
|
|
+ return {8, 8};
|
|
|
+ case 32:
|
|
|
+ return {4, 4};
|
|
|
+ case 16:
|
|
|
+ return {2, 2};
|
|
|
+ default:
|
|
|
+ emitError("alignment and size calculation unimplemented for type");
|
|
|
+ return {0, 0};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ { // Rule 2 and 3
|
|
|
+ if (auto *vecType = dyn_cast<VectorType>(type)) {
|
|
|
+ uint32_t alignment = 0, size = 0;
|
|
|
+ uint32_t elemCount = vecType->getElementCount();
|
|
|
+ const SpirvType *elemType = vecType->getElementType();
|
|
|
+ std::tie(alignment, size) = getAlignmentAndSize(elemType, rule, stride);
|
|
|
+ // Use element alignment for fxc rules
|
|
|
+ if (rule != SpirvLayoutRule::FxcCTBuffer &&
|
|
|
+ rule != SpirvLayoutRule::FxcSBuffer)
|
|
|
+ alignment = (elemCount == 3 ? 4 : elemCount) * size;
|
|
|
+
|
|
|
+ return {alignment, elemCount * size};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ { // Rule 5 and 7
|
|
|
+ if (auto *matType = dyn_cast<MatrixType>(type)) {
|
|
|
+ const SpirvType *elemType = matType->getElementType();
|
|
|
+ uint32_t rowCount = matType->numRows();
|
|
|
+ uint32_t colCount = matType->numCols();
|
|
|
+ uint32_t alignment = 0, size = 0;
|
|
|
+ std::tie(alignment, size) = getAlignmentAndSize(elemType, rule, stride);
|
|
|
+
|
|
|
+ // Matrices are treated as arrays of vectors:
|
|
|
+ // The base alignment and array stride are set to match the base alignment
|
|
|
+ // of a single array element, according to rules 1, 2, and 3, and rounded
|
|
|
+ // up to the base alignment of a vec4.
|
|
|
+ bool isRowMajor = matType->isRowMajorMat();
|
|
|
+
|
|
|
+ const uint32_t vecStorageSize = isRowMajor ? colCount : rowCount;
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::FxcSBuffer) {
|
|
|
+ *stride = vecStorageSize * size;
|
|
|
+ // Use element alignment for fxc structured buffers
|
|
|
+ return {alignment, rowCount * colCount * size};
|
|
|
+ }
|
|
|
+
|
|
|
+ alignment *= (vecStorageSize == 3 ? 4 : vecStorageSize);
|
|
|
+ if (rule == SpirvLayoutRule::GLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ alignment = roundToPow2(alignment, kStd140Vec4Alignment);
|
|
|
+ }
|
|
|
+ *stride = alignment;
|
|
|
+ size = (isRowMajor ? rowCount : colCount) * alignment;
|
|
|
+
|
|
|
+ return {alignment, size};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Rule 9
|
|
|
+ if (auto *structType = dyn_cast<StructType>(type)) {
|
|
|
+ // Special case for handling empty structs, whose size is 0 and has no
|
|
|
+ // requirement over alignment (thus 1).
|
|
|
+ if (structType->getFields().size() == 0)
|
|
|
+ return {1, 0};
|
|
|
+
|
|
|
+ uint32_t maxAlignment = 0;
|
|
|
+ uint32_t structSize = 0;
|
|
|
+
|
|
|
+ for (auto &field : structType->getFields()) {
|
|
|
+ uint32_t memberAlignment = 0, memberSize = 0;
|
|
|
+ std::tie(memberAlignment, memberSize) =
|
|
|
+ getAlignmentAndSize(field.type, rule, stride);
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::RelaxedGLSLStd430 ||
|
|
|
+ rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ alignUsingHLSLRelaxedLayout(field.type, memberSize, memberAlignment,
|
|
|
+ &structSize);
|
|
|
+ } else {
|
|
|
+ structSize = roundToPow2(structSize, memberAlignment);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Reset the current offset to the one specified in the source code
|
|
|
+ // if exists. It's debatable whether we should do sanity check here.
|
|
|
+ // If the developers want manually control the layout, we leave
|
|
|
+ // everything to them.
|
|
|
+ if (field.vkOffsetAttr) {
|
|
|
+ structSize = field.vkOffsetAttr->getOffset();
|
|
|
+ }
|
|
|
+
|
|
|
+ // The base alignment of the structure is N, where N is the largest
|
|
|
+ // base alignment value of any of its members...
|
|
|
+ maxAlignment = std::max(maxAlignment, memberAlignment);
|
|
|
+ structSize += memberSize;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::GLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ // ... and rounded up to the base alignment of a vec4.
|
|
|
+ maxAlignment = roundToPow2(maxAlignment, kStd140Vec4Alignment);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rule != SpirvLayoutRule::FxcCTBuffer &&
|
|
|
+ rule != SpirvLayoutRule::FxcSBuffer) {
|
|
|
+ // The base offset of the member following the sub-structure is rounded up
|
|
|
+ // to the next multiple of the base alignment of the structure.
|
|
|
+ structSize = roundToPow2(structSize, maxAlignment);
|
|
|
+ }
|
|
|
+ return {maxAlignment, structSize};
|
|
|
+ }
|
|
|
+
|
|
|
+ // Rule 4, 6, 8, and 10
|
|
|
+ if (auto *arrayType = dyn_cast<ArrayType>(type)) {
|
|
|
+ const auto elemCount = arrayType->getElementCount();
|
|
|
+ uint32_t alignment = 0, size = 0;
|
|
|
+ std::tie(alignment, size) =
|
|
|
+ getAlignmentAndSize(arrayType->getElementType(), rule, stride);
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::FxcSBuffer) {
|
|
|
+ *stride = size;
|
|
|
+ // Use element alignment for fxc structured buffers
|
|
|
+ return {alignment, size * elemCount};
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::GLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ // The base alignment and array stride are set to match the base alignment
|
|
|
+ // of a single array element, according to rules 1, 2, and 3, and rounded
|
|
|
+ // up to the base alignment of a vec4.
|
|
|
+ alignment = roundToPow2(alignment, kStd140Vec4Alignment);
|
|
|
+ }
|
|
|
+ if (rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ // In fxc cbuffer/tbuffer packing rules, arrays does not affect the data
|
|
|
+ // packing after it. But we still need to make sure paddings are inserted
|
|
|
+ // internally if necessary.
|
|
|
+ *stride = roundToPow2(size, alignment);
|
|
|
+ size += *stride * (elemCount - 1);
|
|
|
+ } else {
|
|
|
+ // Need to round size up considering stride for scalar types
|
|
|
+ size = roundToPow2(size, alignment);
|
|
|
+ *stride = size; // Use size instead of alignment here for Rule 10
|
|
|
+ size *= elemCount;
|
|
|
+ // The base offset of the member following the array is rounded up to the
|
|
|
+ // next multiple of the base alignment.
|
|
|
+ size = roundToPow2(size, alignment);
|
|
|
+ }
|
|
|
+
|
|
|
+ return {alignment, size};
|
|
|
+ }
|
|
|
+
|
|
|
+ emitError("alignment and size calculation unimplemented for type");
|
|
|
+ return {0, 0};
|
|
|
+}
|
|
|
+
|
|
|
+void EmitTypeHandler::alignUsingHLSLRelaxedLayout(const SpirvType *fieldType,
|
|
|
+ uint32_t fieldSize,
|
|
|
+ uint32_t fieldAlignment,
|
|
|
+ uint32_t *currentOffset) {
|
|
|
+ if (auto *vecType = dyn_cast<VectorType>(fieldType)) {
|
|
|
+ const SpirvType *elemType = vecType->getElementType();
|
|
|
+ // Adjust according to HLSL relaxed layout rules.
|
|
|
+ // Aligning vectors as their element types so that we can pack a float
|
|
|
+ // and a float3 tightly together.
|
|
|
+ uint32_t scalarAlignment = 0;
|
|
|
+ std::tie(scalarAlignment, std::ignore) =
|
|
|
+ getAlignmentAndSize(elemType, SpirvLayoutRule::Void, nullptr);
|
|
|
+ if (scalarAlignment <= 4)
|
|
|
+ fieldAlignment = scalarAlignment;
|
|
|
+
|
|
|
+ *currentOffset = roundToPow2(*currentOffset, fieldAlignment);
|
|
|
+
|
|
|
+ // Adjust according to HLSL relaxed layout rules.
|
|
|
+ // Bump to 4-component vector alignment if there is a bad straddle
|
|
|
+ if (improperStraddle(vecType, fieldSize, *currentOffset)) {
|
|
|
+ fieldAlignment = kStd140Vec4Alignment;
|
|
|
+ *currentOffset = roundToPow2(*currentOffset, fieldAlignment);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Cases where the field is not a vector
|
|
|
+ else {
|
|
|
+ *currentOffset = roundToPow2(*currentOffset, fieldAlignment);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void EmitTypeHandler::emitLayoutDecorations(const StructType *structType,
|
|
|
+ SpirvLayoutRule rule) {
|
|
|
+ // Decorations for a type can be emitted after the type itself has been
|
|
|
+ // visited, because we need the result-id of the type as the target of the
|
|
|
+ // decoration.
|
|
|
+ bool visited = false;
|
|
|
+ const uint32_t typeResultId = getResultIdForType(structType, rule, &visited);
|
|
|
+ assert(visited);
|
|
|
+
|
|
|
+ uint32_t offset = 0, index = 0;
|
|
|
+ for (auto &field : structType->getFields()) {
|
|
|
+ const SpirvType *fieldType = field.type;
|
|
|
+ uint32_t memberAlignment = 0, memberSize = 0, stride = 0;
|
|
|
+ std::tie(memberAlignment, memberSize) =
|
|
|
+ getAlignmentAndSize(fieldType, rule, &stride);
|
|
|
+
|
|
|
+ // The next avaiable location after laying out the previous members
|
|
|
+ const uint32_t nextLoc = offset;
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::RelaxedGLSLStd430 ||
|
|
|
+ rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ alignUsingHLSLRelaxedLayout(fieldType, memberSize, memberAlignment,
|
|
|
+ &offset);
|
|
|
+ } else {
|
|
|
+ offset = roundToPow2(offset, memberAlignment);
|
|
|
+ }
|
|
|
+
|
|
|
+ // The vk::offset attribute takes precedence over all.
|
|
|
+ if (field.vkOffsetAttr) {
|
|
|
+ offset = field.vkOffsetAttr->getOffset();
|
|
|
+ }
|
|
|
+ // The :packoffset() annotation takes precedence over normal layout
|
|
|
+ // calculation.
|
|
|
+ else if (field.packOffsetAttr) {
|
|
|
+ const uint32_t packOffset = field.packOffsetAttr->Subcomponent * 16 +
|
|
|
+ field.packOffsetAttr->ComponentOffset * 4;
|
|
|
+ // Do minimal check to make sure the offset specified by packoffset does
|
|
|
+ // not cause overlap.
|
|
|
+ if (packOffset < nextLoc) {
|
|
|
+ emitError("packoffset caused overlap with previous members",
|
|
|
+ field.packOffsetAttr->Loc);
|
|
|
+ } else {
|
|
|
+ offset = packOffset;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Each structure-type member must have an Offset Decoration.
|
|
|
+ emitDecoration(typeResultId, spv::Decoration::Offset, {offset}, index);
|
|
|
+ offset += memberSize;
|
|
|
+
|
|
|
+ // Each structure-type member that is a matrix or array-of-matrices must be
|
|
|
+ // decorated with
|
|
|
+ // * A MatrixStride decoration, and
|
|
|
+ // * one of the RowMajor or ColMajor Decorations.
|
|
|
+ if (auto *arrayType = dyn_cast<ArrayType>(fieldType)) {
|
|
|
+ // We have an array of matrices as a field, we need to decorate
|
|
|
+ // MatrixStride on the field. So skip possible arrays here.
|
|
|
+ fieldType = arrayType->getElementType();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Non-floating point matrices are represented as arrays of vectors, and
|
|
|
+ // therefore ColMajor and RowMajor decorations should not be applied to
|
|
|
+ // them.
|
|
|
+ if (auto *matType = dyn_cast<MatrixType>(fieldType)) {
|
|
|
+ if (isa<FloatType>(matType->getElementType())) {
|
|
|
+ memberAlignment = memberSize = stride = 0;
|
|
|
+ std::tie(memberAlignment, memberSize) =
|
|
|
+ getAlignmentAndSize(fieldType, rule, &stride);
|
|
|
+
|
|
|
+ emitDecoration(typeResultId, spv::Decoration::MatrixStride, {stride},
|
|
|
+ index);
|
|
|
+
|
|
|
+ // We need to swap the RowMajor and ColMajor decorations since HLSL
|
|
|
+ // matrices are conceptually row-major while SPIR-V are conceptually
|
|
|
+ // column-major.
|
|
|
+ if (matType->isRowMajorMat()) {
|
|
|
+ emitDecoration(typeResultId, spv::Decoration::ColMajor, {}, index);
|
|
|
+ } else {
|
|
|
+ // If the source code has neither row_major nor column_major
|
|
|
+ // annotated, it should be treated as column_major since that's the
|
|
|
+ // default.
|
|
|
+ emitDecoration(typeResultId, spv::Decoration::RowMajor, {}, index);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ++index;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void EmitTypeHandler::emitDecoration(uint32_t typeResultId,
|
|
|
+ spv::Decoration decoration,
|
|
|
+ llvm::ArrayRef<uint32_t> decorationParams,
|
|
|
+ uint32_t memberIndex) {
|
|
|
+
|
|
|
+ spv::Op op = memberIndex ? spv::Op::OpMemberDecorate : spv::Op::OpDecorate;
|
|
|
+ assert(curDecorationInst.empty());
|
|
|
+ curDecorationInst.push_back(static_cast<uint32_t>(op));
|
|
|
+ curDecorationInst.push_back(typeResultId);
|
|
|
+ if (memberIndex)
|
|
|
+ curDecorationInst.push_back(memberIndex);
|
|
|
+ curDecorationInst.push_back(static_cast<uint32_t>(decoration));
|
|
|
+ for (auto param : decorationParams)
|
|
|
+ curDecorationInst.push_back(param);
|
|
|
+ curDecorationInst[0] |= static_cast<uint32_t>(curDecorationInst.size()) << 16;
|
|
|
+
|
|
|
+ // Add to the full annotations list
|
|
|
+ annotationsBinary->insert(annotationsBinary->end(), curDecorationInst.begin(),
|
|
|
+ curDecorationInst.end());
|
|
|
+ curDecorationInst.clear();
|
|
|
+}
|
|
|
+
|
|
|
} // end namespace spirv
|
|
|
} // end namespace clang
|