|
@@ -24,6 +24,37 @@ hlsl::ConstantPacking *getPackOffset(const clang::NamedDecl *decl) {
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
+/// 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(clang::QualType type, int size, int offset) {
|
|
|
+ assert(clang::spirv::isVectorType(type));
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
namespace clang {
|
|
@@ -147,17 +178,11 @@ const SpirvType *LowerTypeVisitor::lowerType(const SpirvType *type,
|
|
|
return spvContext.getFunctionType(spirvReturnType, paramTypes);
|
|
|
} else if (const auto *hybridStruct = dyn_cast<HybridStructType>(type)) {
|
|
|
// lower all fields of the struct.
|
|
|
- std::vector<StructType::FieldInfo> structFields;
|
|
|
- for (auto field : hybridStruct->getFields()) {
|
|
|
- const SpirvType *fieldSpirvType = lowerType(field.astType, rule, loc);
|
|
|
- llvm::Optional<bool> isRowMajor = isRowMajorMatrix(field.astType);
|
|
|
- structFields.push_back(
|
|
|
- StructType::FieldInfo(fieldSpirvType, field.name, field.vkOffsetAttr,
|
|
|
- field.packOffsetAttr, isRowMajor));
|
|
|
- }
|
|
|
- return spvContext.getStructType(structFields, hybridStruct->getStructName(),
|
|
|
- hybridStruct->isReadOnly(),
|
|
|
- hybridStruct->getInterfaceType());
|
|
|
+ auto loweredFields =
|
|
|
+ populateLayoutInformation(hybridStruct->getFields(), rule);
|
|
|
+ return spvContext.getStructType(
|
|
|
+ loweredFields, hybridStruct->getStructName(),
|
|
|
+ hybridStruct->isReadOnly(), hybridStruct->getInterfaceType());
|
|
|
}
|
|
|
// Void, bool, int, float cannot be further lowered.
|
|
|
// Matrices cannot contain hybrid types. Only matrices of scalars are valid.
|
|
@@ -186,8 +211,9 @@ const SpirvType *LowerTypeVisitor::lowerType(const SpirvType *type,
|
|
|
// If array didn't contain any hybrid types, return itself.
|
|
|
if (arrType->getElementType() == loweredElemType)
|
|
|
return arrType;
|
|
|
+
|
|
|
return spvContext.getArrayType(loweredElemType, arrType->getElementCount(),
|
|
|
- arrType->hasRowMajorElement());
|
|
|
+ arrType->getStride());
|
|
|
}
|
|
|
// Runtime arrays could contain a hybrid type
|
|
|
else if (const auto *raType = dyn_cast<RuntimeArrayType>(type)) {
|
|
@@ -195,32 +221,13 @@ const SpirvType *LowerTypeVisitor::lowerType(const SpirvType *type,
|
|
|
lowerType(raType->getElementType(), rule, loc);
|
|
|
// If runtime array didn't contain any hybrid types, return itself.
|
|
|
if (raType->getElementType() == loweredElemType)
|
|
|
- return arrType;
|
|
|
- return spvContext.getRuntimeArrayType(loweredElemType);
|
|
|
+ return raType;
|
|
|
+ return spvContext.getRuntimeArrayType(loweredElemType, raType->getStride());
|
|
|
}
|
|
|
// Struct types could contain a hybrid type
|
|
|
else if (const auto *structType = dyn_cast<StructType>(type)) {
|
|
|
- const auto &fields = structType->getFields();
|
|
|
- llvm::SmallVector<StructType::FieldInfo, 4> loweredFields;
|
|
|
- bool wasLowered = false;
|
|
|
- for (auto &field : fields) {
|
|
|
- const auto *loweredFieldType = lowerType(field.type, rule, loc);
|
|
|
- if (loweredFieldType != field.type) {
|
|
|
- wasLowered = true;
|
|
|
- loweredFields.push_back(StructType::FieldInfo(
|
|
|
- loweredFieldType, field.name, field.vkOffsetAttr,
|
|
|
- field.packOffsetAttr, field.isRowMajor));
|
|
|
- } else {
|
|
|
- loweredFields.push_back(field);
|
|
|
- }
|
|
|
- }
|
|
|
- // If the struct didn't contain any hybrid types, return itself.
|
|
|
- if (!wasLowered)
|
|
|
- return structType;
|
|
|
-
|
|
|
- return spvContext.getStructType(loweredFields, structType->getStructName(),
|
|
|
- structType->isReadOnly(),
|
|
|
- structType->getInterfaceType());
|
|
|
+ // Struct types can not contain hybrid types.
|
|
|
+ return structType;
|
|
|
}
|
|
|
// Pointer types could point to a hybrid type.
|
|
|
else if (const auto *ptrType = dyn_cast<SpirvPointerType>(type)) {
|
|
@@ -264,6 +271,11 @@ const SpirvType *LowerTypeVisitor::lowerType(QualType type,
|
|
|
|
|
|
if (desugaredType != type) {
|
|
|
const auto *spvType = lowerType(desugaredType, rule, srcLoc);
|
|
|
+ // Clear matrix majorness potentially set by previous desugarType() calls.
|
|
|
+ // This field will only be set when we were saying a matrix type. And the
|
|
|
+ // above lowerType() call already takes the majorness into consideration.
|
|
|
+ // So should be fine to clear now.
|
|
|
+ typeMatMajorAttr = llvm::None;
|
|
|
return spvType;
|
|
|
}
|
|
|
|
|
@@ -364,10 +376,16 @@ const SpirvType *LowerTypeVisitor::lowerType(QualType type,
|
|
|
|
|
|
// Non-float matrices are represented as an array of vectors.
|
|
|
if (!elemType->isFloatingType()) {
|
|
|
- // This return type is ArrayType
|
|
|
- // This is an array of vectors. No majorness information needed.
|
|
|
- return spvContext.getArrayType(vecType, rowCount,
|
|
|
- /*rowMajorElem*/ llvm::None);
|
|
|
+ llvm::Optional<uint32_t> arrayStride = llvm::None;
|
|
|
+ // If there is a layout rule, we need array stride information.
|
|
|
+ if (rule != SpirvLayoutRule::Void) {
|
|
|
+ uint32_t stride = 0;
|
|
|
+ (void)getAlignmentAndSize(type, rule, &stride);
|
|
|
+ arrayStride = stride;
|
|
|
+ }
|
|
|
+
|
|
|
+ // This return type is ArrayType.
|
|
|
+ return spvContext.getArrayType(vecType, rowCount, arrayStride);
|
|
|
}
|
|
|
|
|
|
return spvContext.getMatrixType(vecType, rowCount);
|
|
@@ -386,41 +404,52 @@ const SpirvType *LowerTypeVisitor::lowerType(QualType type,
|
|
|
return spvType;
|
|
|
|
|
|
// Collect all fields' information.
|
|
|
- llvm::SmallVector<StructType::FieldInfo, 8> fields;
|
|
|
+ llvm::SmallVector<HybridStructType::FieldInfo, 8> fields;
|
|
|
|
|
|
// If this struct is derived from some other struct, place an implicit
|
|
|
// field at the very beginning for the base struct.
|
|
|
- if (const auto *cxxDecl = dyn_cast<CXXRecordDecl>(decl))
|
|
|
+ if (const auto *cxxDecl = dyn_cast<CXXRecordDecl>(decl)) {
|
|
|
for (const auto base : cxxDecl->bases()) {
|
|
|
- fields.push_back(
|
|
|
- StructType::FieldInfo(lowerType(base.getType(), rule, srcLoc)));
|
|
|
+ fields.push_back(HybridStructType::FieldInfo(base.getType()));
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
// Create fields for all members of this struct
|
|
|
for (const auto *field : decl->fields()) {
|
|
|
- const SpirvType *fieldType = lowerType(field->getType(), rule, srcLoc);
|
|
|
- llvm::Optional<bool> isRowMajor = isRowMajorMatrix(field->getType());
|
|
|
- fields.push_back(StructType::FieldInfo(
|
|
|
- fieldType, field->getName(),
|
|
|
+ fields.push_back(HybridStructType::FieldInfo(
|
|
|
+ field->getType(), field->getName(),
|
|
|
/*vkoffset*/ field->getAttr<VKOffsetAttr>(),
|
|
|
- /*packoffset*/ getPackOffset(field), isRowMajor));
|
|
|
+ /*packoffset*/ getPackOffset(field)));
|
|
|
}
|
|
|
|
|
|
- return spvContext.getStructType(fields, decl->getName());
|
|
|
+ auto loweredFields = populateLayoutInformation(fields, rule);
|
|
|
+
|
|
|
+ return spvContext.getStructType(loweredFields, decl->getName());
|
|
|
}
|
|
|
|
|
|
// Array type
|
|
|
if (const auto *arrayType = astContext.getAsArrayType(type)) {
|
|
|
- const auto *elemType = lowerType(arrayType->getElementType(), rule, srcLoc);
|
|
|
+ const auto elemType = arrayType->getElementType();
|
|
|
+ const auto *loweredElemType =
|
|
|
+ lowerType(arrayType->getElementType(), rule, srcLoc);
|
|
|
+ llvm::Optional<uint32_t> arrayStride = llvm::None;
|
|
|
+
|
|
|
+ if (rule != SpirvLayoutRule::Void &&
|
|
|
+ // We won't have stride information for structured/byte buffers since
|
|
|
+ // they contain runtime arrays.
|
|
|
+ !isAKindOfStructuredOrByteBuffer(elemType)) {
|
|
|
+ uint32_t stride = 0;
|
|
|
+ (void)getAlignmentAndSize(type, rule, &stride);
|
|
|
+ arrayStride = stride;
|
|
|
+ }
|
|
|
|
|
|
if (const auto *caType = astContext.getAsConstantArrayType(type)) {
|
|
|
const auto size = static_cast<uint32_t>(caType->getSize().getZExtValue());
|
|
|
- llvm::Optional<bool> isRowMajor = isRowMajorMatrix(type);
|
|
|
- return spvContext.getArrayType(elemType, size, isRowMajor);
|
|
|
+ return spvContext.getArrayType(loweredElemType, size, arrayStride);
|
|
|
}
|
|
|
|
|
|
assert(type->isIncompleteArrayType());
|
|
|
- return spvContext.getRuntimeArrayType(elemType);
|
|
|
+ return spvContext.getRuntimeArrayType(loweredElemType, arrayStride);
|
|
|
}
|
|
|
|
|
|
// Reference types
|
|
@@ -528,13 +557,23 @@ const SpirvType *LowerTypeVisitor::lowerResourceType(QualType type,
|
|
|
else
|
|
|
structName = getAstTypeName(innerType);
|
|
|
|
|
|
- const auto *raType = spvContext.getRuntimeArrayType(structType);
|
|
|
+ uint32_t size = 0, stride = 0;
|
|
|
+ std::tie(std::ignore, size) = getAlignmentAndSize(s, rule, &stride);
|
|
|
+
|
|
|
+ // We have a runtime array of structures. So:
|
|
|
+ // The stride of the runtime array is the size of the struct.
|
|
|
+ const auto *raType = spvContext.getRuntimeArrayType(structType, size);
|
|
|
const bool isReadOnly = (name == "StructuredBuffer");
|
|
|
|
|
|
+ // Attach majorness decoration if this is a *StructuredBuffer<matrix>.
|
|
|
+ llvm::Optional<bool> isRowMajor =
|
|
|
+ isMxNMatrix(s) ? llvm::Optional<bool>(isRowMajorMatrix(s)) : llvm::None;
|
|
|
+
|
|
|
const std::string typeName = "type." + name.str() + "." + structName;
|
|
|
const auto *valType = spvContext.getStructType(
|
|
|
- {StructType::FieldInfo(raType)}, typeName, isReadOnly,
|
|
|
- StructInterfaceType::StorageBuffer);
|
|
|
+ {StructType::FieldInfo(raType, /*name*/ "", /*offset*/ 0,
|
|
|
+ /*matrixStride*/ llvm::None, isRowMajor)},
|
|
|
+ typeName, isReadOnly, StructInterfaceType::StorageBuffer);
|
|
|
|
|
|
if (asAlias) {
|
|
|
// All structured buffers are in the Uniform storage class.
|
|
@@ -591,17 +630,15 @@ const SpirvType *LowerTypeVisitor::lowerResourceType(QualType type,
|
|
|
if (name == "InputPatch") {
|
|
|
const auto elemType = hlsl::GetHLSLInputPatchElementType(type);
|
|
|
const auto elemCount = hlsl::GetHLSLInputPatchCount(type);
|
|
|
- llvm::Optional<bool> isRowMajor = isRowMajorMatrix(type);
|
|
|
return spvContext.getArrayType(lowerType(elemType, rule, srcLoc), elemCount,
|
|
|
- isRowMajor);
|
|
|
+ /*ArrayStride*/ llvm::None);
|
|
|
}
|
|
|
// OutputPatch
|
|
|
if (name == "OutputPatch") {
|
|
|
const auto elemType = hlsl::GetHLSLOutputPatchElementType(type);
|
|
|
const auto elemCount = hlsl::GetHLSLOutputPatchCount(type);
|
|
|
- llvm::Optional<bool> isRowMajor = isRowMajorMatrix(type);
|
|
|
- return spvContext.getArrayType(lowerType(elemType, rule, srcLoc),
|
|
|
- elemCount, isRowMajor);
|
|
|
+ return spvContext.getArrayType(lowerType(elemType, rule, srcLoc), elemCount,
|
|
|
+ /*ArrayStride*/ llvm::None);
|
|
|
}
|
|
|
// Output stream objects (TriangleStream, LineStream, and PointStream)
|
|
|
if (name == "TriangleStream" || name == "LineStream" ||
|
|
@@ -660,6 +697,15 @@ LowerTypeVisitor::translateSampledTypeToImageFormat(QualType sampledType,
|
|
|
|
|
|
QualType LowerTypeVisitor::desugarType(QualType type) {
|
|
|
if (const auto *attrType = type->getAs<AttributedType>()) {
|
|
|
+ switch (auto kind = attrType->getAttrKind()) {
|
|
|
+ case AttributedType::attr_hlsl_row_major:
|
|
|
+ case AttributedType::attr_hlsl_column_major:
|
|
|
+ typeMatMajorAttr = kind;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ // Only matrices should apply to typeMatMajorAttr.
|
|
|
+ break;
|
|
|
+ }
|
|
|
return desugarType(
|
|
|
attrType->getLocallyUnqualifiedSingleStepDesugaredType());
|
|
|
}
|
|
@@ -671,38 +717,419 @@ QualType LowerTypeVisitor::desugarType(QualType type) {
|
|
|
return type;
|
|
|
}
|
|
|
|
|
|
-llvm::Optional<bool>
|
|
|
-LowerTypeVisitor::isHLSLRowMajorMatrix(QualType type) const {
|
|
|
- if (const auto *attrType = type->getAs<AttributedType>()) {
|
|
|
- switch (auto kind = attrType->getAttrKind()) {
|
|
|
+bool LowerTypeVisitor::isHLSLRowMajorMatrix(QualType type) const {
|
|
|
+ // The type passed in may not be desugared. Check attributes on itself first.
|
|
|
+ bool attrRowMajor = false;
|
|
|
+ if (hlsl::HasHLSLMatOrientation(type, &attrRowMajor))
|
|
|
+ return attrRowMajor;
|
|
|
+
|
|
|
+ // Use the majorness info we recorded before.
|
|
|
+ if (typeMatMajorAttr.hasValue()) {
|
|
|
+ switch (typeMatMajorAttr.getValue()) {
|
|
|
case AttributedType::attr_hlsl_row_major:
|
|
|
return true;
|
|
|
case AttributedType::attr_hlsl_column_major:
|
|
|
return false;
|
|
|
- break;
|
|
|
default:
|
|
|
+ // Only oriented matrices are relevant.
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- if (const auto *typedefType = type->getAs<TypedefType>()) {
|
|
|
- return isHLSLRowMajorMatrix(typedefType->desugar());
|
|
|
+ return spvOptions.defaultRowMajor;
|
|
|
+}
|
|
|
+
|
|
|
+bool LowerTypeVisitor::isRowMajorMatrix(QualType type) const {
|
|
|
+ return !isHLSLRowMajorMatrix(type);
|
|
|
+}
|
|
|
+
|
|
|
+llvm::SmallVector<StructType::FieldInfo, 4>
|
|
|
+LowerTypeVisitor::populateLayoutInformation(
|
|
|
+ llvm::ArrayRef<HybridStructType::FieldInfo> fields, SpirvLayoutRule rule) {
|
|
|
+
|
|
|
+ // The resulting vector of fields with proper layout information.
|
|
|
+ llvm::SmallVector<StructType::FieldInfo, 4> loweredFields;
|
|
|
+
|
|
|
+ uint32_t offset = 0;
|
|
|
+ for (const auto field : fields) {
|
|
|
+ // The field can only be FieldDecl (for normal structs) or VarDecl (for
|
|
|
+ // HLSLBufferDecls).
|
|
|
+ auto fieldType = field.astType;
|
|
|
+
|
|
|
+ // Lower the field type fist. This call will populate proper matrix
|
|
|
+ // majorness information.
|
|
|
+ StructType::FieldInfo loweredField(lowerType(fieldType, rule, {}),
|
|
|
+ field.name);
|
|
|
+
|
|
|
+ // We only need layout information for strcutres with non-void layout rule.
|
|
|
+ if (rule == SpirvLayoutRule::Void) {
|
|
|
+ loweredFields.push_back(loweredField);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t memberAlignment = 0, memberSize = 0, stride = 0;
|
|
|
+ std::tie(memberAlignment, memberSize) =
|
|
|
+ getAlignmentAndSize(fieldType, rule, &stride);
|
|
|
+
|
|
|
+ // The next avaiable location after layouting the previos 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.
|
|
|
+ loweredField.offset = offset;
|
|
|
+ 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 (const auto *arrayType = astContext.getAsConstantArrayType(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.
|
|
|
+ QualType elemType = {};
|
|
|
+ if (isMxNMatrix(fieldType, &elemType) && elemType->isFloatingType()) {
|
|
|
+ memberAlignment = memberSize = stride = 0;
|
|
|
+ std::tie(memberAlignment, memberSize) =
|
|
|
+ getAlignmentAndSize(fieldType, rule, &stride);
|
|
|
+
|
|
|
+ loweredField.matrixStride = stride;
|
|
|
+ loweredField.isRowMajor = isRowMajorMatrix(fieldType);
|
|
|
+ }
|
|
|
+
|
|
|
+ loweredFields.push_back(loweredField);
|
|
|
}
|
|
|
- if (const auto *arrayType = astContext.getAsArrayType(type)) {
|
|
|
- return isHLSLRowMajorMatrix(arrayType->getElementType());
|
|
|
+
|
|
|
+ return loweredFields;
|
|
|
+}
|
|
|
+
|
|
|
+void LowerTypeVisitor::alignUsingHLSLRelaxedLayout(QualType fieldType,
|
|
|
+ uint32_t fieldSize,
|
|
|
+ uint32_t fieldAlignment,
|
|
|
+ uint32_t *currentOffset) {
|
|
|
+ QualType vecElemType = {};
|
|
|
+ const bool fieldIsVecType = isVectorType(fieldType, &vecElemType);
|
|
|
+
|
|
|
+ // 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.
|
|
|
+ if (fieldIsVecType) {
|
|
|
+ uint32_t scalarAlignment = 0;
|
|
|
+ std::tie(scalarAlignment, std::ignore) =
|
|
|
+ getAlignmentAndSize(vecElemType, 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 (fieldIsVecType &&
|
|
|
+ improperStraddle(fieldType, fieldSize, *currentOffset)) {
|
|
|
+ fieldAlignment = kStd140Vec4Alignment;
|
|
|
+ *currentOffset = roundToPow2(*currentOffset, fieldAlignment);
|
|
|
}
|
|
|
- return getCodeGenOptions().defaultRowMajor;
|
|
|
}
|
|
|
|
|
|
-llvm::Optional<bool> LowerTypeVisitor::isRowMajorMatrix(QualType type) const {
|
|
|
- // Row/Col majorness only applies to matrices or array of matrices.
|
|
|
- if (!isMatrixOrArrayOfMatrix(astContext, type))
|
|
|
- return llvm::None;
|
|
|
+std::pair<uint32_t, uint32_t>
|
|
|
+LowerTypeVisitor::getAlignmentAndSize(QualType 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.
|
|
|
+
|
|
|
+ const auto desugaredType = desugarType(type);
|
|
|
+ if (desugaredType != type) {
|
|
|
+ auto result = getAlignmentAndSize(desugaredType, rule, stride);
|
|
|
+ // Clear potentially set matrix majorness info
|
|
|
+ typeMatMajorAttr = llvm::None;
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ { // Rule 1
|
|
|
+ QualType ty = {};
|
|
|
+ if (isScalarType(type, &ty))
|
|
|
+ if (const auto *builtinType = ty->getAs<BuiltinType>())
|
|
|
+ switch (builtinType->getKind()) {
|
|
|
+ case BuiltinType::Bool:
|
|
|
+ case BuiltinType::Int:
|
|
|
+ case BuiltinType::UInt:
|
|
|
+ case BuiltinType::Float:
|
|
|
+ return {4, 4};
|
|
|
+ case BuiltinType::Double:
|
|
|
+ case BuiltinType::LongLong:
|
|
|
+ case BuiltinType::ULongLong:
|
|
|
+ return {8, 8};
|
|
|
+ case BuiltinType::Min12Int:
|
|
|
+ case BuiltinType::Min16Int:
|
|
|
+ case BuiltinType::Min16UInt:
|
|
|
+ case BuiltinType::Min16Float:
|
|
|
+ case BuiltinType::Min10Float: {
|
|
|
+ if (spvOptions.enable16BitTypes)
|
|
|
+ return {2, 2};
|
|
|
+ else
|
|
|
+ return {4, 4};
|
|
|
+ }
|
|
|
+ // the 'Half' enum always represents 16-bit floats.
|
|
|
+ // int16_t and uint16_t map to Short and UShort.
|
|
|
+ case BuiltinType::Short:
|
|
|
+ case BuiltinType::UShort:
|
|
|
+ case BuiltinType::Half:
|
|
|
+ return {2, 2};
|
|
|
+ // 'HalfFloat' always represents 32-bit floats.
|
|
|
+ case BuiltinType::HalfFloat:
|
|
|
+ return {4, 4};
|
|
|
+ default:
|
|
|
+ emitError("alignment and size calculation for type %0 unimplemented")
|
|
|
+ << type;
|
|
|
+ return {0, 0};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ { // Rule 2 and 3
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t elemCount = {};
|
|
|
+ if (isVectorType(type, &elemType, &elemCount)) {
|
|
|
+ uint32_t alignment = 0, size = 0;
|
|
|
+ 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
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t rowCount = 0, colCount = 0;
|
|
|
+ if (isMxNMatrix(type, &elemType, &rowCount, &colCount)) {
|
|
|
+ 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 = isRowMajorMatrix(type);
|
|
|
+
|
|
|
+ const uint32_t vecStorageSize = isRowMajor ? rowCount : colCount;
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::FxcSBuffer) {
|
|
|
+ *stride = vecStorageSize * size;
|
|
|
+ // Use element alignment for fxc structured buffers
|
|
|
+ return {alignment, rowCount * colCount * size};
|
|
|
+ }
|
|
|
|
|
|
- const auto hlslRowMajor = isHLSLRowMajorMatrix(type);
|
|
|
- if (!hlslRowMajor.hasValue())
|
|
|
- return hlslRowMajor;
|
|
|
+ alignment *= (vecStorageSize == 3 ? 4 : vecStorageSize);
|
|
|
+ if (rule == SpirvLayoutRule::GLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ alignment = roundToPow2(alignment, kStd140Vec4Alignment);
|
|
|
+ }
|
|
|
+ *stride = alignment;
|
|
|
+ size = (isRowMajor ? colCount : rowCount) * alignment;
|
|
|
+
|
|
|
+ return {alignment, size};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Rule 9
|
|
|
+ if (const auto *structType = type->getAs<RecordType>()) {
|
|
|
+ // Special case for handling empty structs, whose size is 0 and has no
|
|
|
+ // requirement over alignment (thus 1).
|
|
|
+ if (structType->getDecl()->field_empty())
|
|
|
+ return {1, 0};
|
|
|
+
|
|
|
+ uint32_t maxAlignment = 0;
|
|
|
+ uint32_t structSize = 0;
|
|
|
+
|
|
|
+ for (const auto *field : structType->getDecl()->fields()) {
|
|
|
+ uint32_t memberAlignment = 0, memberSize = 0;
|
|
|
+ std::tie(memberAlignment, memberSize) =
|
|
|
+ getAlignmentAndSize(field->getType(), rule, stride);
|
|
|
+
|
|
|
+ if (rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
|
|
|
+ rule == SpirvLayoutRule::RelaxedGLSLStd430 ||
|
|
|
+ rule == SpirvLayoutRule::FxcCTBuffer) {
|
|
|
+ alignUsingHLSLRelaxedLayout(field->getType(), 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 (const auto *offsetAttr = field->getAttr<VKOffsetAttr>()) {
|
|
|
+ structSize = offsetAttr->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 (const auto *arrayType = astContext.getAsConstantArrayType(type)) {
|
|
|
+ const auto elemCount = arrayType->getSize().getZExtValue();
|
|
|
+ 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};
|
|
|
+ }
|
|
|
|
|
|
- return !hlslRowMajor.getValue();
|
|
|
+ emitError("alignment and size calculation for type %0 unimplemented") << type;
|
|
|
+ return {0, 0};
|
|
|
}
|
|
|
|
|
|
} // namespace spirv
|