Browse Source

[spirv] Support cbuffer and layout decorations (#585)

* Added support for the cbuffer type
* Added support for accessing elements in cbuffer
* Added support for std140 layout rules
Lei Zhang 8 years ago
parent
commit
8bb01b2dac

+ 4 - 2
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -285,8 +285,10 @@ public:
   uint32_t getPointerType(uint32_t pointeeType, spv::StorageClass);
   uint32_t getPointerType(uint32_t pointeeType, spv::StorageClass);
   uint32_t getStructType(llvm::ArrayRef<uint32_t> fieldTypes,
   uint32_t getStructType(llvm::ArrayRef<uint32_t> fieldTypes,
                          llvm::StringRef structName = "",
                          llvm::StringRef structName = "",
-                         llvm::ArrayRef<llvm::StringRef> fieldNames = {});
-  uint32_t getArrayType(uint32_t elemType, uint32_t count);
+                         llvm::ArrayRef<llvm::StringRef> fieldNames = {},
+                         Type::DecorationSet decorations = {});
+  uint32_t getArrayType(uint32_t elemType, uint32_t count,
+                        Type::DecorationSet decorations = {});
   uint32_t getFunctionType(uint32_t returnType,
   uint32_t getFunctionType(uint32_t returnType,
                            llvm::ArrayRef<uint32_t> paramTypes);
                            llvm::ArrayRef<uint32_t> paramTypes);
   uint32_t getImageType(uint32_t sampledType, spv::Dim, bool isArray);
   uint32_t getImageType(uint32_t sampledType, spv::Dim, bool isArray);

+ 76 - 3
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -36,7 +36,7 @@ llvm::StringRef getStageVarSemantic(const NamedDecl *decl) {
 }
 }
 
 
 /// \brief Returns the stage variable's register assignment for the given Decl.
 /// \brief Returns the stage variable's register assignment for the given Decl.
-const hlsl::RegisterAssignment *getResourceBinding(const VarDecl *decl) {
+const hlsl::RegisterAssignment *getResourceBinding(const NamedDecl *decl) {
   for (auto *annotation : decl->getUnusualAnnotations()) {
   for (auto *annotation : decl->getUnusualAnnotations()) {
     if (auto *reg = dyn_cast<hlsl::RegisterAssignment>(annotation)) {
     if (auto *reg = dyn_cast<hlsl::RegisterAssignment>(annotation)) {
       return reg;
       return reg;
@@ -65,9 +65,33 @@ DeclResultIdMapper::getDeclSpirvInfo(const NamedDecl *decl) const {
   return nullptr;
   return nullptr;
 }
 }
 
 
-uint32_t DeclResultIdMapper::getDeclResultId(const NamedDecl *decl) const {
+uint32_t DeclResultIdMapper::getDeclResultId(const NamedDecl *decl) {
   if (const auto *info = getDeclSpirvInfo(decl))
   if (const auto *info = getDeclSpirvInfo(decl))
-    return info->resultId;
+    if (const auto *bufferDecl =
+            dyn_cast<HLSLBufferDecl>(decl->getDeclContext())) {
+      // If this is a VarDecl inside a HLSLBufferDecl, we need to do an extra
+      // OpAccessChain to get the pointer to the variable since we created
+      // a single variable for the whole buffer object.
+
+      uint32_t index = 0;
+      for (const auto *subDecl : bufferDecl->decls()) {
+        if (subDecl == decl)
+          break;
+        ++index;
+      }
+
+      const uint32_t varType = typeTranslator.translateType(
+          // Should only have VarDecls in a HLSLBufferDecl.
+          cast<VarDecl>(decl)->getType(),
+          // We need to set decorateLayout here to avoid creating SPIR-V
+          // instructions for the current type without decorations.
+          /*decorateLayout*/ true);
+      return theBuilder.createAccessChain(
+          theBuilder.getPointerType(varType, info->storageClass),
+          info->resultId, {theBuilder.getConstantInt32(index)});
+    } else {
+      return info->resultId;
+    }
 
 
   assert(false && "found unregistered decl");
   assert(false && "found unregistered decl");
   return 0;
   return 0;
@@ -118,6 +142,55 @@ uint32_t DeclResultIdMapper::createExternVar(uint32_t varType,
   return id;
   return id;
 }
 }
 
 
+uint32_t DeclResultIdMapper::createExternVar(const HLSLBufferDecl *decl) {
+  // In the AST, cbuffer/tbuffer is represented as a HLSLBufferDecl, which is
+  // a DeclContext, and all fields in the buffer are represented as VarDecls.
+  // We cannot do the normal translation path, which will translate a field
+  // into a standalone variable. We need to create a single SPIR-V variable
+  // for the whole buffer.
+
+  // Collect the type and name for each field
+  llvm::SmallVector<uint32_t, 4> fieldTypes;
+  llvm::SmallVector<llvm::StringRef, 4> fieldNames;
+  for (const auto *subDecl : decl->decls()) {
+    // All the fields should be VarDecls.
+    const auto *varDecl = cast<VarDecl>(subDecl);
+    // All fields are qualified with const. It will affect the debug name.
+    // We don't need it here.
+    auto varType = varDecl->getType();
+    varType.removeLocalConst();
+
+    fieldTypes.push_back(typeTranslator.translateType(
+        varType, true, varDecl->hasAttr<HLSLRowMajorAttr>()));
+    fieldNames.push_back(varDecl->getName());
+  }
+
+  // Get the type for the whole buffer
+  const std::string structName = "type." + decl->getName().str();
+  auto decorations = typeTranslator.getLayoutDecorations(decl);
+  decorations.push_back(Decoration::getBlock(*theBuilder.getSPIRVContext()));
+  const uint32_t structType =
+      theBuilder.getStructType(fieldTypes, structName, fieldNames, decorations);
+
+  // Create the variable for the whole buffer
+  const std::string varName = "var." + decl->getName().str();
+  const uint32_t bufferVar =
+      theBuilder.addModuleVar(structType, spv::StorageClass::Uniform, varName);
+
+  // We still register all VarDecls seperately here. All the VarDecls are
+  // mapped to the <result-id> of the buffer object, which means when querying
+  // querying the <result-id> for a certain VarDecl, we need to do an extra
+  // OpAccessChain.
+  for (const auto *subDecl : decl->decls()) {
+    const auto *varDecl = cast<VarDecl>(subDecl);
+    astDecls[varDecl] = {bufferVar, spv::StorageClass::Uniform};
+  }
+  resourceVars.emplace_back(bufferVar, getResourceBinding(decl),
+                            decl->getAttr<VKBindingAttr>());
+
+  return bufferVar;
+}
+
 uint32_t DeclResultIdMapper::getOrRegisterFnResultId(const FunctionDecl *fn) {
 uint32_t DeclResultIdMapper::getOrRegisterFnResultId(const FunctionDecl *fn) {
   if (const auto *info = getDeclSpirvInfo(fn))
   if (const auto *info = getDeclSpirvInfo(fn))
     return info->resultId;
     return info->resultId;

+ 2 - 1
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -147,6 +147,7 @@ public:
 
 
   /// \brief Creates an external-visible variable and returns its <result-id>.
   /// \brief Creates an external-visible variable and returns its <result-id>.
   uint32_t createExternVar(uint32_t varType, const VarDecl *var);
   uint32_t createExternVar(uint32_t varType, const VarDecl *var);
+  uint32_t createExternVar(const HLSLBufferDecl *decl);
 
 
   /// \brief Sets the <result-id> of the entry function.
   /// \brief Sets the <result-id> of the entry function.
   void setEntryFunctionId(uint32_t id) { entryFunctionId = id; }
   void setEntryFunctionId(uint32_t id) { entryFunctionId = id; }
@@ -165,7 +166,7 @@ public:
   /// \brief Returns the <result-id> for the given decl.
   /// \brief Returns the <result-id> for the given decl.
   ///
   ///
   /// This method will panic if the given decl is not registered.
   /// This method will panic if the given decl is not registered.
-  uint32_t getDeclResultId(const NamedDecl *decl) const;
+  uint32_t getDeclResultId(const NamedDecl *decl);
 
 
   /// \brief Returns the <result-id> for the given function if already
   /// \brief Returns the <result-id> for the given function if already
   /// registered; otherwise, treats the given function as a normal decl and
   /// registered; otherwise, treats the given function as a normal decl and

+ 12 - 8
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -515,6 +515,9 @@ void ModuleBuilder::decorate(uint32_t targetId, spv::Decoration decoration) {
   case spv::Decoration::Sample:
   case spv::Decoration::Sample:
     d = Decoration::getSample(theContext);
     d = Decoration::getSample(theContext);
     break;
     break;
+  case spv::Decoration::Block:
+    d = Decoration::getBlock(theContext);
+    break;
   }
   }
 
 
   assert(d && "unimplemented decoration");
   assert(d && "unimplemented decoration");
@@ -582,8 +585,9 @@ uint32_t ModuleBuilder::getPointerType(uint32_t pointeeType,
 uint32_t
 uint32_t
 ModuleBuilder::getStructType(llvm::ArrayRef<uint32_t> fieldTypes,
 ModuleBuilder::getStructType(llvm::ArrayRef<uint32_t> fieldTypes,
                              llvm::StringRef structName,
                              llvm::StringRef structName,
-                             llvm::ArrayRef<llvm::StringRef> fieldNames) {
-  const Type *type = Type::getStruct(theContext, fieldTypes);
+                             llvm::ArrayRef<llvm::StringRef> fieldNames,
+                             Type::DecorationSet decorations) {
+  const Type *type = Type::getStruct(theContext, fieldTypes, decorations);
   bool isRegistered = false;
   bool isRegistered = false;
   const uint32_t typeId = theContext.getResultIdForType(type, &isRegistered);
   const uint32_t typeId = theContext.getResultIdForType(type, &isRegistered);
   theModule.addType(type, typeId);
   theModule.addType(type, typeId);
@@ -601,8 +605,9 @@ ModuleBuilder::getStructType(llvm::ArrayRef<uint32_t> fieldTypes,
   return typeId;
   return typeId;
 }
 }
 
 
-uint32_t ModuleBuilder::getArrayType(uint32_t elemType, uint32_t count) {
-  const Type *type = Type::getArray(theContext, elemType, count);
+uint32_t ModuleBuilder::getArrayType(uint32_t elemType, uint32_t count,
+                                     Type::DecorationSet decorations) {
+  const Type *type = Type::getArray(theContext, elemType, count, decorations);
   const uint32_t typeId = theContext.getResultIdForType(type);
   const uint32_t typeId = theContext.getResultIdForType(type);
   theModule.addType(type, typeId);
   theModule.addType(type, typeId);
   return typeId;
   return typeId;
@@ -665,7 +670,6 @@ uint32_t ModuleBuilder::getSamplerType() {
   return typeId;
   return typeId;
 }
 }
 
 
-
 uint32_t ModuleBuilder::getSampledImageType(uint32_t imageType) {
 uint32_t ModuleBuilder::getSampledImageType(uint32_t imageType) {
   const Type *type = Type::getSampledImage(theContext, imageType);
   const Type *type = Type::getSampledImage(theContext, imageType);
   const uint32_t typeId = theContext.getResultIdForType(type);
   const uint32_t typeId = theContext.getResultIdForType(type);
@@ -687,7 +691,7 @@ uint32_t ModuleBuilder::getByteAddressBufferType(bool isRW) {
   // The struct must also be decorated as BufferBlock. The offset decoration
   // The struct must also be decorated as BufferBlock. The offset decoration
   // should also be applied to the first (only) member. NonWritable decoration
   // should also be applied to the first (only) member. NonWritable decoration
   // should also be applied to the first member if isRW is true.
   // should also be applied to the first member if isRW is true.
-  llvm::SmallVector<const Decoration*, 3> typeDecs;
+  llvm::SmallVector<const Decoration *, 3> typeDecs;
   typeDecs.push_back(Decoration::getBufferBlock(theContext));
   typeDecs.push_back(Decoration::getBufferBlock(theContext));
   typeDecs.push_back(Decoration::getOffset(theContext, 0, 0));
   typeDecs.push_back(Decoration::getOffset(theContext, 0, 0));
   if (!isRW)
   if (!isRW)
@@ -696,8 +700,8 @@ uint32_t ModuleBuilder::getByteAddressBufferType(bool isRW) {
   const Type *type = Type::getStruct(theContext, {raTypeId}, typeDecs);
   const Type *type = Type::getStruct(theContext, {raTypeId}, typeDecs);
   const uint32_t typeId = theContext.getResultIdForType(type);
   const uint32_t typeId = theContext.getResultIdForType(type);
   theModule.addType(type, typeId);
   theModule.addType(type, typeId);
-  theModule.addDebugName(typeId, isRW ? "type.RWByteAddressBuffer"
-                                      : "type.ByteAddressBuffer");
+  theModule.addDebugName(
+      typeId, isRW ? "type.RWByteAddressBuffer" : "type.ByteAddressBuffer");
   return typeId;
   return typeId;
 }
 }
 
 

+ 4 - 0
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -167,6 +167,8 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
       }
       }
     } else if (auto *varDecl = dyn_cast<VarDecl>(decl)) {
     } else if (auto *varDecl = dyn_cast<VarDecl>(decl)) {
       doVarDecl(varDecl);
       doVarDecl(varDecl);
+    } else if (auto *bufferDecl = dyn_cast<HLSLBufferDecl>(decl)) {
+      (void)declIdMapper.createExternVar(bufferDecl);
     }
     }
   }
   }
 
 
@@ -201,6 +203,8 @@ void SPIRVEmitter::doDecl(const Decl *decl) {
     doVarDecl(varDecl);
     doVarDecl(varDecl);
   } else if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
   } else if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
     doFunctionDecl(funcDecl);
     doFunctionDecl(funcDecl);
+  } else if (dyn_cast<HLSLBufferDecl>(decl)) {
+    llvm_unreachable("HLSLBufferDecl should not be handled here");
   } else {
   } else {
     // TODO: Implement handling of other Decl types.
     // TODO: Implement handling of other Decl types.
     emitWarning("Decl type '%0' is not supported yet.")
     emitWarning("Decl type '%0' is not supported yet.")

+ 296 - 41
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -9,23 +9,47 @@
 
 
 #include "TypeTranslator.h"
 #include "TypeTranslator.h"
 
 
+#include <algorithm>
+#include <tuple>
+
 #include "dxc/HLSL/DxilConstants.h"
 #include "dxc/HLSL/DxilConstants.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/HlslTypes.h"
 #include "clang/AST/HlslTypes.h"
 
 
 namespace clang {
 namespace clang {
 namespace spirv {
 namespace spirv {
 
 
-uint32_t TypeTranslator::translateType(QualType type) {
+namespace {
+/// The alignment for 4-component float vectors.
+constexpr uint32_t kStd140Vec4Alignment = 16u;
+
+/// Returns true if the given value is a power of 2.
+inline bool isPow2(int val) { return (val & (val - 1)) == 0; }
+
+/// Rounds the given value up to the given power of 2.
+inline void roundToPow2(uint32_t *val, uint32_t pow2) {
+  assert(pow2 != 0);
+  *val = (*val + pow2 - 1) & ~(pow2 - 1);
+}
+} // anonymous namespace
+
+uint32_t TypeTranslator::translateType(QualType type, bool decorateLayout,
+                                       bool isRowMajor) {
+  // We can only apply row_major to matrices or arrays of matrices.
+  if (isRowMajor)
+    assert(isMxNMatrix(type) || type->isArrayType());
+
   // Try to translate the canonical type first
   // Try to translate the canonical type first
   const auto canonicalType = type.getCanonicalType();
   const auto canonicalType = type.getCanonicalType();
   if (canonicalType != type)
   if (canonicalType != type)
-    return translateType(canonicalType);
+    return translateType(canonicalType, decorateLayout, isRowMajor);
 
 
   // Primitive types
   // Primitive types
   {
   {
     QualType ty = {};
     QualType ty = {};
     if (isScalarType(type, &ty))
     if (isScalarType(type, &ty))
-      if (const auto *builtinType = cast<BuiltinType>(ty.getTypePtr()))
+      if (const auto *builtinType = ty->getAs<BuiltinType>())
         switch (builtinType->getKind()) {
         switch (builtinType->getKind()) {
         case BuiltinType::Void:
         case BuiltinType::Void:
           return theBuilder.getVoidType();
           return theBuilder.getVoidType();
@@ -45,9 +69,8 @@ uint32_t TypeTranslator::translateType(QualType type) {
   }
   }
 
 
   // Typedefs
   // Typedefs
-  if (const auto *typedefType = type->getAs<TypedefType>()) {
-    return translateType(typedefType->desugar());
-  }
+  if (const auto *typedefType = type->getAs<TypedefType>())
+    return translateType(typedefType->desugar(), decorateLayout, isRowMajor);
 
 
   // Reference types
   // Reference types
   if (const auto *refType = type->getAs<ReferenceType>()) {
   if (const auto *refType = type->getAs<ReferenceType>()) {
@@ -57,7 +80,7 @@ uint32_t TypeTranslator::translateType(QualType type) {
     // We already pass function arguments via pointers to tempoary local
     // We already pass function arguments via pointers to tempoary local
     // variables. So it should be fine to drop the pointer type and treat it
     // variables. So it should be fine to drop the pointer type and treat it
     // as the underlying pointee type here.
     // as the underlying pointee type here.
-    return translateType(refType->getPointeeType());
+    return translateType(refType->getPointeeType(), decorateLayout, isRowMajor);
   }
   }
 
 
   // In AST, vector/matrix types are TypedefType of TemplateSpecializationType.
   // In AST, vector/matrix types are TypedefType of TemplateSpecializationType.
@@ -67,41 +90,32 @@ uint32_t TypeTranslator::translateType(QualType type) {
   {
   {
     QualType elemType = {};
     QualType elemType = {};
     uint32_t elemCount = {};
     uint32_t elemCount = {};
-    if (TypeTranslator::isVectorType(type, &elemType, &elemCount)) {
-      // In SPIR-V, vectors must have two or more elements. So translate vectors
-      // of size 1 into the underlying primitive types directly.
-      if (elemCount == 1) {
-        return translateType(elemType);
-      }
+    if (isVectorType(type, &elemType, &elemCount))
       return theBuilder.getVecType(translateType(elemType), elemCount);
       return theBuilder.getVecType(translateType(elemType), elemCount);
-    }
   }
   }
 
 
   // Matrix types
   // Matrix types
-  if (hlsl::IsHLSLMatType(type)) {
-    // The other cases should already be handled in the above.
-    assert(isMxNMatrix(type));
-
-    const auto elemTy = hlsl::GetHLSLMatElementType(type);
-    // NOTE: According to Item "Data rules" of SPIR-V Spec 2.16.1 "Universal
-    // Validation Rules":
-    //   Matrix types can only be parameterized with floating-point types.
-    //
-    // So we need special handling of non-fp matrices, probably by emulating
-    // them using other types. But for now just disable them.
-    if (!elemTy->isFloatingType()) {
-      emitError("Non-floating-point matrices not supported yet");
-      return 0;
-    }
-
-    const auto elemType = translateType(elemTy);
+  {
+    QualType elemType = {};
     uint32_t rowCount = 0, colCount = 0;
     uint32_t rowCount = 0, colCount = 0;
-    hlsl::GetHLSLMatRowColCount(type, rowCount, colCount);
+    if (isMxNMatrix(type, &elemType, &rowCount, &colCount)) {
+      // NOTE: According to Item "Data rules" of SPIR-V Spec 2.16.1 "Universal
+      // Validation Rules":
+      //   Matrix types can only be parameterized with floating-point types.
+      //
+      // So we need special handling of non-fp matrices, probably by emulating
+      // them using other types. But for now just disable them.
+      if (!elemType->isFloatingType()) {
+        emitError("Non-floating-point matrices not supported yet");
+        return 0;
+      }
 
 
-    // HLSL matrices are row major, while SPIR-V matrices are column major.
-    // We are mapping what HLSL semantically mean a row into a column here.
-    const uint32_t vecType = theBuilder.getVecType(elemType, colCount);
-    return theBuilder.getMatType(vecType, rowCount);
+      // HLSL matrices are row major, while SPIR-V matrices are column major.
+      // We are mapping what HLSL semantically mean a row into a column here.
+      const uint32_t vecType =
+          theBuilder.getVecType(translateType(elemType), colCount);
+      return theBuilder.getMatType(vecType, rowCount);
+    }
   }
   }
 
 
   // Struct type
   // Struct type
@@ -119,20 +133,37 @@ uint32_t TypeTranslator::translateType(QualType type) {
     llvm::SmallVector<uint32_t, 4> fieldTypes;
     llvm::SmallVector<uint32_t, 4> fieldTypes;
     llvm::SmallVector<llvm::StringRef, 4> fieldNames;
     llvm::SmallVector<llvm::StringRef, 4> fieldNames;
     for (const auto *field : decl->fields()) {
     for (const auto *field : decl->fields()) {
-      fieldTypes.push_back(translateType(field->getType()));
+      fieldTypes.push_back(translateType(field->getType(), decorateLayout,
+                                         field->hasAttr<HLSLRowMajorAttr>()));
       fieldNames.push_back(field->getName());
       fieldNames.push_back(field->getName());
     }
     }
 
 
-    return theBuilder.getStructType(fieldTypes, decl->getName(), fieldNames);
+    llvm::SmallVector<const Decoration *, 4> decorations;
+    if (decorateLayout) {
+      decorations = getLayoutDecorations(decl);
+    }
+
+    return theBuilder.getStructType(fieldTypes, decl->getName(), fieldNames,
+                                    decorations);
   }
   }
 
 
   if (const auto *arrayType = astContext.getAsConstantArrayType(type)) {
   if (const auto *arrayType = astContext.getAsConstantArrayType(type)) {
-    const uint32_t elemType = translateType(arrayType->getElementType());
+    const uint32_t elemType =
+        translateType(arrayType->getElementType(), decorateLayout, isRowMajor);
     // TODO: handle extra large array size?
     // TODO: handle extra large array size?
     const auto size =
     const auto size =
         static_cast<uint32_t>(arrayType->getSize().getZExtValue());
         static_cast<uint32_t>(arrayType->getSize().getZExtValue());
-    return theBuilder.getArrayType(elemType,
-                                   theBuilder.getConstantUint32(size));
+
+    llvm::SmallVector<const Decoration *, 4> decorations;
+    if (decorateLayout) {
+      uint32_t stride = 0;
+      (void)getAlignmentAndSize(type, &stride, isRowMajor);
+      decorations.push_back(
+          Decoration::getArrayStride(*theBuilder.getSPIRVContext(), stride));
+    }
+
+    return theBuilder.getArrayType(elemType, theBuilder.getConstantUint32(size),
+                                   decorations);
   }
   }
 
 
   emitError("Type '%0' is not supported yet.") << type->getTypeClassName();
   emitError("Type '%0' is not supported yet.") << type->getTypeClassName();
@@ -316,6 +347,68 @@ uint32_t TypeTranslator::getComponentVectorType(QualType matrixType) {
   return theBuilder.getVecType(elemType, colCount);
   return theBuilder.getVecType(elemType, colCount);
 }
 }
 
 
+llvm::SmallVector<const Decoration *, 4>
+TypeTranslator::getLayoutDecorations(const DeclContext *decl) {
+  const auto spirvContext = theBuilder.getSPIRVContext();
+  llvm::SmallVector<const Decoration *, 4> decorations;
+  uint32_t offset = 0, index = 0;
+
+  for (const auto *field : decl->decls()) {
+    if (const auto *f = dyn_cast<CXXRecordDecl>(field)) {
+      // Implicit generated struct declarations should be ignored.
+      if (f->isImplicit())
+        continue;
+    }
+
+    // The field can only be FieldDecl (for normal structs) or VarDecl (for
+    // HLSLBufferDecls).
+    auto fieldType = cast<DeclaratorDecl>(field)->getType();
+    const bool isRowMajor = field->hasAttr<HLSLRowMajorAttr>();
+
+    uint32_t memberAlignment = 0, memberSize = 0, stride = 0;
+    std::tie(memberAlignment, memberSize) =
+        getAlignmentAndSize(fieldType, &stride, isRowMajor);
+
+    // Each structure-type member must have an Offset Decoration.
+    roundToPow2(&offset, memberAlignment);
+    decorations.push_back(Decoration::getOffset(*spirvContext, 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 (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();
+    }
+    if (isMxNMatrix(fieldType)) {
+      memberAlignment = memberSize = stride = 0;
+      std::tie(memberAlignment, memberSize) =
+          getAlignmentAndSize(fieldType, &stride, isRowMajor);
+
+      decorations.push_back(
+          Decoration::getMatrixStride(*spirvContext, 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 (isRowMajor) {
+        decorations.push_back(Decoration::getColMajor(*spirvContext, 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.
+        decorations.push_back(Decoration::getRowMajor(*spirvContext, index));
+      }
+    }
+
+    ++index;
+  }
+
+  return decorations;
+}
+
 uint32_t TypeTranslator::translateResourceType(QualType type) {
 uint32_t TypeTranslator::translateResourceType(QualType type) {
   const auto *recordType = type->getAs<RecordType>();
   const auto *recordType = type->getAs<RecordType>();
   assert(recordType);
   assert(recordType);
@@ -360,5 +453,167 @@ uint32_t TypeTranslator::translateResourceType(QualType type) {
   return 0;
   return 0;
 }
 }
 
 
+std::pair<uint32_t, uint32_t>
+TypeTranslator::getAlignmentAndSize(QualType type, uint32_t *stride,
+                                    const bool isRowMajor) {
+  // 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).
+  const auto canonicalType = type.getCanonicalType();
+  if (canonicalType != type)
+    return getAlignmentAndSize(canonicalType, stride, isRowMajor);
+
+  if (const auto *typedefType = type->getAs<TypedefType>())
+    return getAlignmentAndSize(typedefType->desugar(), stride, isRowMajor);
+
+  { // Rule 1
+    QualType ty = {};
+    if (isScalarType(type, &ty))
+      if (const auto *builtinType = ty->getAs<BuiltinType>())
+        switch (builtinType->getKind()) {
+        case BuiltinType::Void:
+          return {0, 0};
+        case BuiltinType::Bool:
+        case BuiltinType::Int:
+        case BuiltinType::UInt:
+        case BuiltinType::Float:
+          return {4, 4};
+        default:
+          emitError("Primitive type '%0' is not supported yet.")
+              << builtinType->getTypeClassName();
+          return {0, 0};
+        }
+  }
+
+  { // Rule 2 and 3
+    QualType elemType = {};
+    uint32_t elemCount = {};
+    if (isVectorType(type, &elemType, &elemCount)) {
+      uint32_t size = 0;
+      std::tie(std::ignore, size) =
+          getAlignmentAndSize(elemType, stride, isRowMajor);
+
+      return {(elemCount == 3 ? 4 : elemCount) * size, 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, std::ignore) =
+          getAlignmentAndSize(elemType, stride, isRowMajor);
+
+      // 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.
+      const uint32_t vecStorageSize = isRowMajor ? colCount : rowCount;
+      alignment *= (vecStorageSize == 3 ? 4 : vecStorageSize);
+      roundToPow2(&alignment, kStd140Vec4Alignment);
+      *stride = alignment;
+      size = (isRowMajor ? rowCount : colCount) * alignment;
+
+      return {alignment, size};
+    }
+  }
+
+  // Rule 9
+  if (const auto *structType = type->getAs<RecordType>()) {
+    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(), stride, field->hasAttr<HLSLRowMajorAttr>());
+
+      // 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);
+      roundToPow2(&structSize, memberAlignment);
+      structSize += memberSize;
+    }
+
+    // ... and rounded up to the base alignment of a vec4.
+    roundToPow2(&maxAlignment, kStd140Vec4Alignment);
+    // The base offset of the member following the sub-structure is rounded up
+    // to the next multiple of the base alignment of the structure.
+    roundToPow2(&structSize, maxAlignment);
+    return {maxAlignment, structSize};
+  }
+
+  // Rule 4, 6, 8, and 10
+  if (const auto *arrayType = astContext.getAsConstantArrayType(type)) {
+    uint32_t alignment = 0, size = 0;
+    std::tie(alignment, size) =
+        getAlignmentAndSize(arrayType->getElementType(), stride, isRowMajor);
+
+    // 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.
+    roundToPow2(&alignment, kStd140Vec4Alignment);
+    // Need to round size up considering stride for scalar types
+    roundToPow2(&size, alignment);
+    *stride = size; // Use size instead of alignment here for Rule 10
+    // TODO: handle extra large array size?
+    size *= static_cast<uint32_t>(arrayType->getSize().getZExtValue());
+    // The base offset of the member following the array is rounded up to the
+    // next multiple of the base alignment.
+    roundToPow2(&size, alignment);
+
+    return {alignment, size};
+  }
+
+  emitError("Type '%0' is not supported yet.") << type->getTypeClassName();
+  return {0, 0};
+}
+
 } // end namespace spirv
 } // end namespace spirv
 } // end namespace clang
 } // end namespace clang

+ 35 - 3
tools/clang/lib/SPIRV/TypeTranslator.h

@@ -10,6 +10,8 @@
 #ifndef LLVM_CLANG_LIB_SPIRV_TYPETRANSLATOR_H
 #ifndef LLVM_CLANG_LIB_SPIRV_TYPETRANSLATOR_H
 #define LLVM_CLANG_LIB_SPIRV_TYPETRANSLATOR_H
 #define LLVM_CLANG_LIB_SPIRV_TYPETRANSLATOR_H
 
 
+#include <utility>
+
 #include "clang/AST/Type.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/SPIRV/ModuleBuilder.h"
 #include "clang/SPIRV/ModuleBuilder.h"
@@ -32,11 +34,17 @@ public:
 
 
   /// \brief Generates the corresponding SPIR-V type for the given Clang
   /// \brief Generates the corresponding SPIR-V type for the given Clang
   /// frontend type and returns the type's <result-id>. On failure, reports
   /// frontend type and returns the type's <result-id>. On failure, reports
-  /// the error and returns 0.
+  /// the error and returns 0. If decorateLayout is true, layout decorations
+  /// (Offset, MatrixStride, ArrayStride, RowMajor, ColMajor) will be attached
+  /// to the struct or array types. If decorateLayout is true and type is a
+  /// matrix or array of matrix type, isRowMajor will indicate whether it is
+  /// decorated with row_major in the source code.
   ///
   ///
   /// The translation is recursive; all the types that the target type depends
   /// The translation is recursive; all the types that the target type depends
-  /// on will be generated.
-  uint32_t translateType(QualType type);
+  /// on will be generated and all with layout decorations (if decorateLayout
+  /// is true).
+  uint32_t translateType(QualType type, bool decorateLayout = false,
+                         bool isRowMajor = false);
 
 
   /// \brief Returns true if the given type is the HLSL ByteAddressBufferType.
   /// \brief Returns true if the given type is the HLSL ByteAddressBufferType.
   bool isByteAddressBuffer(QualType type);
   bool isByteAddressBuffer(QualType type);
@@ -99,6 +107,16 @@ public:
   /// matrix type.
   /// matrix type.
   uint32_t getComponentVectorType(QualType matrixType);
   uint32_t getComponentVectorType(QualType matrixType);
 
 
+  /// \brief Generates layout decorations (Offset, MatrixStride, RowMajor,
+  /// ColMajor) for the given type.
+  ///
+  /// This method is not recursive; it only handles the top-level member/field
+  /// of the given DeclContext. Besides, it does not handle ArrayStride, which
+  /// according to the spec, must be attached to the array type itself instead
+  /// of a struct member.
+  llvm::SmallVector<const Decoration *, 4>
+  getLayoutDecorations(const DeclContext *decl);
+
 private:
 private:
   /// \brief Wrapper method to create an error message and report it
   /// \brief Wrapper method to create an error message and report it
   /// in the diagnostic engine associated with this consumer.
   /// in the diagnostic engine associated with this consumer.
@@ -112,6 +130,20 @@ private:
   /// instructions and returns the <result-id>. Returns 0 on failure.
   /// instructions and returns the <result-id>. Returns 0 on failure.
   uint32_t translateResourceType(QualType type);
   uint32_t translateResourceType(QualType type);
 
 
+  /// \brief Returns the alignment and size in bytes for the given type
+  /// according to std140.
+
+  /// If the type is an array/matrix type, writes the array/matrix stride to
+  /// stride. If the type is a matrix, isRowMajor will be used to indicate
+  /// whether it is labelled as row_major in the source code.
+  ///
+  /// Note that the size returned is not exactly how many bytes the type
+  /// will occupy in memory; rather it is used in conjunction with alignment
+  /// to get the next available location (alignment + size), which means
+  /// size contains post-paddings required by the given type.
+  std::pair<uint32_t, uint32_t>
+  getAlignmentAndSize(QualType type, uint32_t *stride, bool isRowMajor);
+
 private:
 private:
   ASTContext &astContext;
   ASTContext &astContext;
   ModuleBuilder &theBuilder;
   ModuleBuilder &theBuilder;

+ 35 - 0
tools/clang/test/CodeGenSPIRV/op.cbuffer.access.hlsl

@@ -0,0 +1,35 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S {
+    float  f;
+};
+
+cbuffer MyCbuffer : register(b1) {
+    float    a;
+    float2   b;
+    float3x4 c;
+    S        s;
+    float    t[4];
+};
+
+float main() : A {
+// CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %var_MyCbuffer %int_0
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[a]]
+
+// CHECK:      [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %var_MyCbuffer %int_1
+// CHECK-NEXT: [[b0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[b]] %int_0
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[b0]]
+
+// CHECK:      [[c:%\d+]] = OpAccessChain %_ptr_Uniform_mat3v4float %var_MyCbuffer %int_2
+// CHECK-NEXT: [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float [[c]] %uint_1 %uint_2
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[c12]]
+
+// CHECK:      [[s:%\d+]] = OpAccessChain %_ptr_Uniform_S %var_MyCbuffer %int_3
+// CHECK-NEXT: [[s0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[s]] %int_0
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[s0]]
+
+// CHECK:      [[t:%\d+]] = OpAccessChain %_ptr_Uniform__arr_float_uint_4 %var_MyCbuffer %int_4
+// CHECK-NEXT: [[t3:%\d+]] = OpAccessChain %_ptr_Uniform_float [[t]] %int_3
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[t3]]
+    return a + b.x + c[1][2] + s.f + t[3];
+}

+ 50 - 0
tools/clang/test/CodeGenSPIRV/type.cbuffer.hlsl

@@ -0,0 +1,50 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK:      OpName %S "S"
+// CHECK-NEXT: OpMemberName %S 0 "f1"
+// CHECK-NEXT: OpMemberName %S 1 "f2"
+
+// CHECK:      OpName %type_MyCbuffer "type.MyCbuffer"
+// CHECK-NEXT: OpMemberName %type_MyCbuffer 0 "a"
+// CHECK-NEXT: OpMemberName %type_MyCbuffer 1 "b"
+// CHECK-NEXT: OpMemberName %type_MyCbuffer 2 "c"
+// CHECK-NEXT: OpMemberName %type_MyCbuffer 3 "d"
+// CHECK-NEXT: OpMemberName %type_MyCbuffer 4 "s"
+// CHECK-NEXT: OpMemberName %type_MyCbuffer 5 "t"
+
+// CHECK:      OpName %var_MyCbuffer "var.MyCbuffer"
+
+// CHECK:      OpName %type_AnotherCBuffer "type.AnotherCBuffer"
+// CHECK-NEXT: OpMemberName %type_AnotherCBuffer 0 "m"
+// CHECK-NEXT: OpMemberName %type_AnotherCBuffer 1 "n"
+
+// CHECK:      OpName %var_AnotherCBuffer "var.AnotherCBuffer"
+
+struct S {
+    float  f1;
+    float3 f2;
+};
+
+cbuffer MyCbuffer : register(b1) {
+    bool     a;
+    int      b;
+    uint2    c;
+    float3x4 d;
+    S        s;
+    float    t[4];
+};
+
+cbuffer AnotherCBuffer : register(b2) {
+    float3 m;
+    float4 n;
+}
+
+// CHECK: %type_MyCbuffer = OpTypeStruct %bool %int %v2uint %mat3v4float %S %_arr_float_uint_4
+
+// CHECK: %type_AnotherCBuffer = OpTypeStruct %v3float %v4float
+
+// CHECK: %var_MyCbuffer = OpVariable %_ptr_Uniform_type_MyCbuffer Uniform
+// CHECK: %var_AnotherCBuffer = OpVariable %_ptr_Uniform_type_AnotherCBuffer Uniform
+
+void main() {
+}

+ 2 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.explicit.hlsl

@@ -20,6 +20,8 @@ Texture2D<float4> texture1;
 [[vk::binding(2, 2)]]
 [[vk::binding(2, 2)]]
 Texture3D<float4> texture2 : register(t0, space0);
 Texture3D<float4> texture2 : register(t0, space0);
 
 
+// TODO: support [[vk::binding()]] on cbuffer
+
 float4 main() : SV_Target {
 float4 main() : SV_Target {
     return 1.0;
     return 1.0;
 }
 }

+ 5 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.implicit.hlsl

@@ -16,6 +16,11 @@ Texture3D<float4> texture2;
 // CHECK-NEXT: OpDecorate %sampler2 Binding 3
 // CHECK-NEXT: OpDecorate %sampler2 Binding 3
 SamplerState sampler2;
 SamplerState sampler2;
 
 
+// CHECK:      OpDecorate %var_myCbuffer DescriptorSet 0
+// CHECK-NEXT: OpDecorate %var_myCbuffer Binding 4
+cbuffer myCbuffer {
+    float4 stuff;
+}
 
 
 float4 main() : SV_Target {
 float4 main() : SV_Target {
     return 1.0;
     return 1.0;

+ 5 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.register.hlsl

@@ -28,6 +28,11 @@ SamplerState sampler3;
 // CHECK-NEXT: OpDecorate %sampler4 Binding 2
 // CHECK-NEXT: OpDecorate %sampler4 Binding 2
 SamplerState sampler4;
 SamplerState sampler4;
 
 
+// CHECK:      OpDecorate %var_myCbuffer DescriptorSet 3
+// CHECK-NEXT: OpDecorate %var_myCbuffer Binding 1
+cbuffer myCbuffer : register(b1, space3) {
+    float4 stuff;
+}
 
 
 float4 main() : SV_Target {
 float4 main() : SV_Target {
     return 1.0;
     return 1.0;

+ 86 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.nested.std140.hlsl

@@ -0,0 +1,86 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// Deep nested array of matrices
+// Depp nested majorness
+struct R {                         // Alignment    Offset  Size                              Next
+    row_major    float2x3 rf1[3];  // 16(vec4)  -> 0     + 3(array) * stride(2 * 16(vec4)) = 96
+    column_major float2x3 rf2[4];  // 16(vec4)  -> 96    + 4(array) * stride(3 * 16(vec4)) = 288
+                 float2x3 rf3[2];  // 16(vec4)  -> 288   + 2(array) * stride(3 * 16(vec4)) = 384
+                 int      rf4;     // 4         -> 384   + 4                               = 388
+};                                 // 16(max)                                                400 (388 round up to R alignment)
+
+// Array of scalars, vectors, matrices, and structs
+struct S {                         // Alignment   Offset  Size                              Next
+    float3       sf1[3];           // 16(vec4) -> 0     + 3(array) * 16(vec4)             = 48
+    float        sf2[3];           // 4        -> 48    + 3(array) * 16(vec4)             = 96
+    R            sf3[4];           // 16       -> 96    + 4(array) * stride(400)          = 1696
+    row_major    float3x2 sf4[2];  // 16(vec4) -> 1696  + 2(array) * stride(3 * 16(vec4)) = 1792
+    column_major float3x2 sf5[3];  // 16(vec4) -> 1792  + 3(array) * stride(2 * 16(vec4)) = 1888
+                 float3x2 sf6[4];  // 16(vec4) -> 1888  + 4(array) * stride(2 * 16(vec4)) = 2016
+                 float    sf7;     // 4        -> 2016  + 4                               = 2020
+};                                 // 16(max)                                               2032 (2020 round up to S alignment)
+
+struct T {        // Alignment    Offset  Size              Next
+    R    tf1[2];  // 16        -> 0     + 2(array) * 400  = 800
+    S    tf2[3];  // 16        -> 800   + 3(array) * 2032 = 6896
+    uint tf3;     // 4         -> 6896  + 4               = 6900
+};                // 16(max)                                6912 (6900 round up to T alignment)
+
+cbuffer MyCbuffer {  // Alignment   Offset   Size              Next
+    T    t[2];       // 16       -> 0      + 2(array) * 6912 = 13824
+    bool z;          // 4        -> 13824
+};
+
+// CHECK:      OpDecorate %_arr_mat2v3float_uint_3 ArrayStride 32
+// CHECK:      OpDecorate %_arr_mat2v3float_uint_4 ArrayStride 48
+// CHECK:      OpDecorate %_arr_mat2v3float_uint_2 ArrayStride 48
+
+// CHECK:      OpMemberDecorate %R 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %R 0 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %R 0 ColMajor
+// CHECK-NEXT: OpMemberDecorate %R 1 Offset 96
+// CHECK-NEXT: OpMemberDecorate %R 1 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %R 1 RowMajor
+// CHECK-NEXT: OpMemberDecorate %R 2 Offset 288
+// CHECK-NEXT: OpMemberDecorate %R 2 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %R 2 RowMajor
+// CHECK-NEXT: OpMemberDecorate %R 3 Offset 384
+
+// CHECK:      OpDecorate %_arr_R_uint_2 ArrayStride 400
+// CHECK:      OpDecorate %_arr_v3float_uint_3 ArrayStride 16
+// CHECK:      OpDecorate %_arr_float_uint_3 ArrayStride 16
+// CHECK:      OpDecorate %_arr_R_uint_4 ArrayStride 400
+
+// CHECK:      OpDecorate %_arr_mat3v2float_uint_2 ArrayStride 48
+// CHECK:      OpDecorate %_arr_mat3v2float_uint_3 ArrayStride 32
+// CHECK:      OpDecorate %_arr_mat3v2float_uint_4 ArrayStride 32
+
+// CHECK:      OpMemberDecorate %S 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %S 1 Offset 48
+// CHECK-NEXT: OpMemberDecorate %S 2 Offset 96
+// CHECK-NEXT: OpMemberDecorate %S 3 Offset 1696
+// CHECK-NEXT: OpMemberDecorate %S 3 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %S 3 ColMajor
+// CHECK-NEXT: OpMemberDecorate %S 4 Offset 1792
+// CHECK-NEXT: OpMemberDecorate %S 4 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %S 4 RowMajor
+// CHECK-NEXT: OpMemberDecorate %S 5 Offset 1888
+// CHECK-NEXT: OpMemberDecorate %S 5 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %S 5 RowMajor
+// CHECK-NEXT: OpMemberDecorate %S 6 Offset 2016
+
+// CHECK:      OpDecorate %_arr_S_uint_3 ArrayStride 2032
+
+// CHECK:      OpMemberDecorate %T 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %T 1 Offset 800
+// CHECK-NEXT: OpMemberDecorate %T 2 Offset 6896
+
+// CHECK:      OpDecorate %_arr_T_uint_2 ArrayStride 6912
+
+// CHECK-NEXT: OpMemberDecorate %type_MyCbuffer 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %type_MyCbuffer 1 Offset 13824
+
+// CHECK:      OpDecorate %type_MyCbuffer Block
+float main() : A {
+    return 1.0;
+}

+ 78 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.std140.hlsl

@@ -0,0 +1,78 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct R {     // Alignment                           Offset     Size       Next
+    float2 rf; // 8(vec2)                          -> 0        + 8(vec2)  = 8
+};             // 16(8 round up to vec4 alignment)               8          16(8 round up to R max alignment)
+
+struct S {      // Alignment    Offset                                Size        Next
+    R      sf1; // 16        -> 0                                   + 16        = 16
+    float  sf2; // 4         -> 16                                  + 4         = 20
+    float3 sf3; // 16(vec4)  -> 32 (20 round up to vec4 alignment)  + 12(vec3)  = 44
+    float  sf4; // 4         -> 44                                  + 4         = 48
+};              // 16(max)                                                        48(48 round up to S max alignment)
+
+struct T {           // Alignment     Offset                               Size              = Next
+    int      tf1;    // 4          -> 0                                  + 4                 = 4
+    R        tf2[3]; // 16         -> 16 (4 rounded up to R alignment)   + 3 * stride(16)    = 64
+    float3x2 tf3;    // 16(vec4)   -> 64 (64 round up to vec4 alignment) + 2 * stride(vec4)  = 96
+    S        tf4;    // 16         -> 96 (96 round up to S alignment)    + 48                = 144
+    float    tf5;    // 4          -> 144                                + 4                 = 148
+};                   // 16(max)                                                                160(148 round up to T max alignment)
+
+cbuffer MyCBuffer {              // Alignment   Offset                                 Size                     Next
+                 bool     a;     // 4        -> 0                                    +     4                  = 4
+                 uint1    b;     // 4        -> 4                                    +     4                  = 8
+                 float3   c;     // 16(vec4) -> 16 (8 round up to vec4 alignment)    + 3 * 4                  = 28
+    row_major    float2x3 d;     // 16(vec4) -> 32 (28 round up to vec4 alignment)   + 2 * stride(vec4)       = 64
+    column_major float2x3 e;     // 16(vec4) -> 64 (64 round up to vec4 alignment)   + 3 * stride(vec4)       = 112
+                 float2x1 f;     // 8(vec2)  -> 112 (112 round up to vec2 aligment)  + 2 * 4                  = 120
+    row_major    float2x3 g[3];  // 16(vec4) -> 128 (120 round up to vec4 alignment) + 3 * 2 * stride(vec4)   = 224
+    column_major float2x2 h[4];  // 16(vec4) -> 224 (224 round up to vec4 alignment) + 4 * 2 * stride(vec4)   = 352
+                 T        t;     // 16       -> 352 (352 round up to vec4 alignment) + 160                    = 512
+                 float    z;     // 4        -> 512
+
+};
+
+// CHECK:      OpDecorate %_arr_mat2v3float_uint_3 ArrayStride 32
+// CHECK:      OpDecorate %_arr_mat2v2float_uint_4 ArrayStride 32
+
+// CHECK:      OpMemberDecorate %R 0 Offset 0
+
+// CHECK:      OpDecorate %_arr_R_uint_3 ArrayStride 16
+
+// CHECK:      OpMemberDecorate %S 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %S 1 Offset 16
+// CHECK-NEXT: OpMemberDecorate %S 2 Offset 32
+// CHECK-NEXT: OpMemberDecorate %S 3 Offset 44
+
+// CHECK:      OpMemberDecorate %T 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %T 1 Offset 16
+// CHECK-NEXT: OpMemberDecorate %T 2 Offset 64
+// CHECK-NEXT: OpMemberDecorate %T 2 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %T 2 RowMajor
+// CHECK-NEXT: OpMemberDecorate %T 3 Offset 96
+// CHECK-NEXT: OpMemberDecorate %T 4 Offset 144
+
+// CHECK:      OpMemberDecorate %type_MyCBuffer 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 1 Offset 4
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 2 Offset 16
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 Offset 32
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 ColMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 Offset 64
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 RowMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 5 Offset 112
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 Offset 128
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 ColMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 Offset 224
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 MatrixStride 16
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 RowMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 8 Offset 352
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 9 Offset 512
+// CHECK-NEXT: OpDecorate %type_MyCBuffer Block
+
+float main() : A {
+    return 1.0;
+}

+ 8 - 0
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -43,6 +43,7 @@ TEST_F(FileTest, ArrayTypes) { runFileTest("type.array.hlsl"); }
 TEST_F(FileTest, TypedefTypes) { runFileTest("type.typedef.hlsl"); }
 TEST_F(FileTest, TypedefTypes) { runFileTest("type.typedef.hlsl"); }
 TEST_F(FileTest, SamplerTypes) { runFileTest("type.sampler.hlsl"); }
 TEST_F(FileTest, SamplerTypes) { runFileTest("type.sampler.hlsl"); }
 TEST_F(FileTest, TextureTypes) { runFileTest("type.texture.hlsl"); }
 TEST_F(FileTest, TextureTypes) { runFileTest("type.texture.hlsl"); }
+TEST_F(FileTest, CBufferType) { runFileTest("type.cbuffer.hlsl"); }
 TEST_F(FileTest, ByteAddressBufferTypes) {
 TEST_F(FileTest, ByteAddressBufferTypes) {
   runFileTest("type.byte-address-buffer.hlsl");
   runFileTest("type.byte-address-buffer.hlsl");
 }
 }
@@ -188,6 +189,7 @@ TEST_F(FileTest, OpMatrixAccess1x1) {
 
 
 // For struct & array accessing operator
 // For struct & array accessing operator
 TEST_F(FileTest, OpStructAccess) { runFileTest("op.struct.access.hlsl"); }
 TEST_F(FileTest, OpStructAccess) { runFileTest("op.struct.access.hlsl"); }
+TEST_F(FileTest, OpCBufferAccess) { runFileTest("op.cbuffer.access.hlsl"); }
 TEST_F(FileTest, OpStructArray) { runFileTest("op.array.access.hlsl"); }
 TEST_F(FileTest, OpStructArray) { runFileTest("op.array.access.hlsl"); }
 
 
 // For casting
 // For casting
@@ -444,5 +446,11 @@ TEST_F(FileTest, VulkanRegisterBinding) {
 TEST_F(FileTest, VulkanExplicitBindingReassigned) {
 TEST_F(FileTest, VulkanExplicitBindingReassigned) {
   runFileTest("vk.binding.explicit.error.hlsl", /*expectSuccess*/ false);
   runFileTest("vk.binding.explicit.error.hlsl", /*expectSuccess*/ false);
 }
 }
+TEST_F(FileTest, VulkanLayoutCBufferStd140) {
+  runFileTest("vk.layout.cbuffer.std140.hlsl");
+}
+TEST_F(FileTest, VulkanLayoutCBufferNestedStd140) {
+  runFileTest("vk.layout.cbuffer.nested.std140.hlsl");
+}
 
 
 } // namespace
 } // namespace