Browse Source

[spirv] Booleans do not have a physical layout. (#1140)

* [spirv] Booleans do not have a physical layout.

Addresses issue #1136.
Ehsan 7 years ago
parent
commit
be8c74dd4f

+ 10 - 9
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -353,8 +353,8 @@ void DeclResultIdMapper::createCounterVarForDecl(const DeclaratorDecl *decl) {
   }
 }
 
-uint32_t DeclResultIdMapper::createFnVar(const VarDecl *var,
-                                         llvm::Optional<uint32_t> init) {
+SpirvEvalInfo DeclResultIdMapper::createFnVar(const VarDecl *var,
+                                              llvm::Optional<uint32_t> init) {
   bool isAlias = false;
   auto &info = astDecls[var].info;
   const uint32_t type =
@@ -362,10 +362,10 @@ uint32_t DeclResultIdMapper::createFnVar(const VarDecl *var,
   const uint32_t id = theBuilder.addFnVar(type, var->getName(), init);
   info.setResultId(id);
 
-  return id;
+  return info;
 }
 
-uint32_t DeclResultIdMapper::createFileVar(const VarDecl *var,
+SpirvEvalInfo DeclResultIdMapper::createFileVar(const VarDecl *var,
                                            llvm::Optional<uint32_t> init) {
   bool isAlias = false;
   auto &info = astDecls[var].info;
@@ -375,10 +375,10 @@ uint32_t DeclResultIdMapper::createFileVar(const VarDecl *var,
                                               var->getName(), init);
   info.setResultId(id).setStorageClass(spv::StorageClass::Private);
 
-  return id;
+  return info;
 }
 
-uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) {
+SpirvEvalInfo DeclResultIdMapper::createExternVar(const VarDecl *var) {
   auto storageClass = spv::StorageClass::UniformConstant;
   auto rule = LayoutRule::Void;
   bool isACRWSBuffer = false; // Whether is {Append|Consume|RW}StructuredBuffer
@@ -418,12 +418,13 @@ uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) {
 
   const uint32_t id = theBuilder.addModuleVar(varType, storageClass,
                                               var->getName(), llvm::None);
-  astDecls[var] =
+  const auto info =
       SpirvEvalInfo(id).setStorageClass(storageClass).setLayoutRule(rule);
+  astDecls[var] = info;
 
   // Variables in Workgroup do not need descriptor decorations.
   if (storageClass == spv::StorageClass::Workgroup)
-    return id;
+    return info;
 
   const auto *regAttr = getResourceBinding(var);
   const auto *bindingAttr = var->getAttr<VKBindingAttr>();
@@ -441,7 +442,7 @@ uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) {
     createCounterVar(var, /*isAlias=*/false);
   }
 
-  return id;
+  return info;
 }
 
 uint32_t DeclResultIdMapper::getMatrixStructType(const VarDecl *matVar,

+ 4 - 3
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -300,13 +300,14 @@ public:
 
   /// \brief Creates a function-scope variable in the current function and
   /// returns its <result-id>.
-  uint32_t createFnVar(const VarDecl *var, llvm::Optional<uint32_t> init);
+  SpirvEvalInfo createFnVar(const VarDecl *var, llvm::Optional<uint32_t> init);
 
   /// \brief Creates a file-scope variable and returns its <result-id>.
-  uint32_t createFileVar(const VarDecl *var, llvm::Optional<uint32_t> init);
+  SpirvEvalInfo createFileVar(const VarDecl *var,
+                              llvm::Optional<uint32_t> init);
 
   /// \brief Creates an external-visible variable and returns its <result-id>.
-  uint32_t createExternVar(const VarDecl *var);
+  SpirvEvalInfo createExternVar(const VarDecl *var);
 
   /// \brief Creates a cbuffer/tbuffer from the given decl.
   ///

+ 114 - 15
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -828,7 +828,58 @@ SpirvEvalInfo SPIRVEmitter::loadIfGLValue(const Expr *expr,
     valType =
         typeTranslator.translateType(expr->getType(), info.getLayoutRule());
   }
-  return info.setResultId(theBuilder.createLoad(valType, info)).setRValue();
+
+  uint32_t loadedId = theBuilder.createLoad(valType, info);
+
+  // Special-case: According to the SPIR-V Spec: There is no physical size or
+  // bit pattern defined for boolean type. Therefore an unsigned integer is used
+  // to represent booleans when layout is required. In such cases, after loading
+  // the uint, we should perform a comparison.
+  {
+    uint32_t vecSize = 1, numRows = 0, numCols = 0;
+    if (info.getLayoutRule() != LayoutRule::Void &&
+        isBoolOrVecMatOfBoolType(expr->getType())) {
+      const auto exprType = expr->getType();
+      QualType uintType = astContext.UnsignedIntTy;
+      QualType boolType = astContext.BoolTy;
+      if (TypeTranslator::isScalarType(exprType) ||
+          TypeTranslator::isVectorType(exprType, nullptr, &vecSize)) {
+        const auto fromType =
+            vecSize == 1 ? uintType
+                         : astContext.getExtVectorType(uintType, vecSize);
+        const auto toType =
+            vecSize == 1 ? boolType
+                         : astContext.getExtVectorType(boolType, vecSize);
+        loadedId = castToBool(loadedId, fromType, toType);
+      } else {
+        const bool isMat =
+            TypeTranslator::isMxNMatrix(exprType, nullptr, &numRows, &numCols);
+        assert(isMat);
+        const auto uintRowQualType =
+            astContext.getExtVectorType(uintType, numCols);
+        const auto uintRowQualTypeId =
+            typeTranslator.translateType(uintRowQualType);
+        const auto boolRowQualType =
+            astContext.getExtVectorType(boolType, numCols);
+        const auto boolRowQualTypeId =
+            typeTranslator.translateType(boolRowQualType);
+        const uint32_t resultTypeId =
+            theBuilder.getMatType(boolType, boolRowQualTypeId, numRows);
+        llvm::SmallVector<uint32_t, 4> rows;
+        for (uint32_t i = 0; i < numRows; ++i) {
+          const auto row = theBuilder.createCompositeExtract(uintRowQualTypeId,
+                                                             loadedId, {i});
+          rows.push_back(castToBool(row, uintRowQualType, boolRowQualType));
+        }
+        loadedId = theBuilder.createCompositeConstruct(resultTypeId, rows);
+      }
+      // Now that it is converted to Bool, it has no layout rule.
+      // This result-id should be evaluated as bool from here on out.
+      info.setLayoutRule(LayoutRule::Void);
+    }
+  }
+
+  return info.setResultId(loadedId).setRValue();
 }
 
 SpirvEvalInfo SPIRVEmitter::loadIfAliasVarRef(const Expr *expr) {
@@ -1165,7 +1216,7 @@ void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
     return;
   }
 
-  uint32_t varId = 0;
+  SpirvEvalInfo varId(0);
 
   // The contents in externally visible variables can be updated via the
   // pipeline. They should be handled differently from file and function scope
@@ -4181,9 +4232,11 @@ SpirvEvalInfo
 SPIRVEmitter::doExtMatrixElementExpr(const ExtMatrixElementExpr *expr) {
   const Expr *baseExpr = expr->getBase();
   const auto baseInfo = doExpr(baseExpr);
+  const auto layoutRule = baseInfo.getLayoutRule();
+  const auto elemType = hlsl::GetHLSLMatElementType(baseExpr->getType());
   const auto accessor = expr->getEncodedElementAccess();
-  const uint32_t elemType = typeTranslator.translateType(
-      hlsl::GetHLSLMatElementType(baseExpr->getType()));
+  const uint32_t elemTypeId =
+      typeTranslator.translateType(elemType, layoutRule);
 
   uint32_t rowCount = 0, colCount = 0;
   hlsl::GetHLSLMatRowColCount(baseExpr->getType(), rowCount, colCount);
@@ -4211,7 +4264,7 @@ SPIRVEmitter::doExtMatrixElementExpr(const ExtMatrixElementExpr *expr) {
         indices[i] = theBuilder.getConstantInt32(indices[i]);
 
       const uint32_t ptrType =
-          theBuilder.getPointerType(elemType, baseInfo.getStorageClass());
+          theBuilder.getPointerType(elemTypeId, baseInfo.getStorageClass());
       if (!indices.empty()) {
         assert(!baseInfo.isRValue());
         // Load the element via access chain
@@ -4221,19 +4274,32 @@ SPIRVEmitter::doExtMatrixElementExpr(const ExtMatrixElementExpr *expr) {
         // be the source pointer.
         elem = baseInfo;
       }
-      elem = theBuilder.createLoad(elemType, elem);
+      elem = theBuilder.createLoad(elemTypeId, elem);
     } else { // e.g., (mat1 + mat2)._m11
-      elem = theBuilder.createCompositeExtract(elemType, baseInfo, indices);
+      elem = theBuilder.createCompositeExtract(elemTypeId, baseInfo, indices);
     }
     elements.push_back(elem);
   }
 
+  const auto size = elements.size();
   auto valueId = elements.front();
-  if (elements.size() > 1) {
-    const uint32_t vecType = theBuilder.getVecType(elemType, elements.size());
+  if (size > 1) {
+    const uint32_t vecType = theBuilder.getVecType(elemTypeId, size);
     valueId = theBuilder.createCompositeConstruct(vecType, elements);
   }
 
+  // Note: Special-case: Booleans have no physical layout, and therefore when
+  // layout is required booleans are represented as unsigned integers.
+  // Therefore, after loading the uint we should convert it boolean.
+  if (elemType->isBooleanType() && layoutRule != LayoutRule::Void) {
+    const auto fromType =
+        size == 1 ? astContext.UnsignedIntTy
+                  : astContext.getExtVectorType(astContext.UnsignedIntTy, size);
+    const auto toType =
+        size == 1 ? astContext.BoolTy
+                  : astContext.getExtVectorType(astContext.BoolTy, size);
+    valueId = castToBool(valueId, fromType, toType);
+  }
   return SpirvEvalInfo(valueId).setRValue();
 }
 
@@ -4246,8 +4312,6 @@ SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
   const QualType baseType = baseExpr->getType();
   assert(hlsl::IsHLSLVecType(baseType));
   const auto baseSize = hlsl::GetHLSLVecSize(baseType);
-
-  const uint32_t type = typeTranslator.translateType(expr->getType());
   const auto accessorSize = static_cast<size_t>(accessor.Count);
 
   // Depending on the number of elements selected, we emit different
@@ -4273,18 +4337,28 @@ SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
     // we should use composite extraction. We should check the immediate base
     // instead of the original base here since we can have something like
     // v.xyyz to turn a lvalue v into rvalue.
+    const auto type =
+        typeTranslator.translateType(expr->getType(), baseInfo.getLayoutRule());
     if (!baseInfo.isRValue()) { // E.g., v.x;
       const uint32_t ptrType =
           theBuilder.getPointerType(type, baseInfo.getStorageClass());
       const uint32_t index = theBuilder.getConstantInt32(accessor.Swz0);
       // We need a lvalue here. Do not try to load.
-      return theBuilder.createAccessChain(ptrType, baseInfo, {index});
+      return baseInfo.setResultId(
+          theBuilder.createAccessChain(ptrType, baseInfo, {index}));
     } else { // E.g., (v + w).x;
       // The original base vector may not be a rvalue. Need to load it if
       // it is lvalue since ImplicitCastExpr (LValueToRValue) will be missing
       // for that case.
-      return baseInfo.setResultId(
-          theBuilder.createCompositeExtract(type, baseInfo, {accessor.Swz0}));
+      auto result =
+          theBuilder.createCompositeExtract(type, baseInfo, {accessor.Swz0});
+      // Special-case: Booleans in SPIR-V do not have a physical layout. Uint is
+      // used to represent them when layout is required.
+      if (expr->getType()->isBooleanType() &&
+          baseInfo.getLayoutRule() != LayoutRule::Void)
+        result =
+            castToBool(result, astContext.UnsignedIntTy, astContext.BoolTy);
+      return baseInfo.setResultId(result);
     }
   }
 
@@ -4292,6 +4366,8 @@ SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
     // Selecting more than one element from a size-1 vector, for example,
     // <scalar>.xx. Construct the vector.
     auto info = loadIfGLValue(baseExpr);
+    const auto type =
+        typeTranslator.translateType(expr->getType(), info.getLayoutRule());
     llvm::SmallVector<uint32_t, 4> components(accessorSize, info);
     return info
         .setResultId(theBuilder.createCompositeConstruct(type, components))
@@ -4313,6 +4389,8 @@ SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
     return doExpr(baseExpr);
 
   auto info = loadIfGLValue(baseExpr);
+  const auto type =
+      typeTranslator.translateType(expr->getType(), info.getLayoutRule());
   // Use base for both vectors. But we are only selecting values from the
   // first one.
   return info.setResultId(
@@ -4604,6 +4682,7 @@ void SPIRVEmitter::storeValue(const SpirvEvalInfo &lhsPtr,
   };
 
   QualType matElemType = {};
+  QualType elemType = {};
   uint32_t numRows = 0, numCols = 0;
   const bool lhsIsMat =
       typeTranslator.isMxNMatrix(lhsValType, &matElemType, &numRows, &numCols);
@@ -4612,7 +4691,27 @@ void SPIRVEmitter::storeValue(const SpirvEvalInfo &lhsPtr,
 
   if (typeTranslator.isScalarType(lhsValType) ||
       typeTranslator.isVectorType(lhsValType) || lhsIsFloatMat) {
-    theBuilder.createStore(lhsPtr, rhsVal);
+    uint32_t rhsValId = rhsVal;
+
+    // Special-case: According to the SPIR-V Spec: There is no physical size
+    // or bit pattern defined for boolean type. Therefore an unsigned integer
+    // is used to represent booleans when layout is required. In such cases,
+    // we should cast the boolean to uint before creating OpStore.
+    if (isBoolOrVecOfBoolType(lhsValType) &&
+        lhsPtr.getLayoutRule() != LayoutRule::Void) {
+      uint32_t vecSize = 1;
+      const bool isVec =
+          TypeTranslator::isVectorType(lhsValType, nullptr, &vecSize);
+      const auto toType =
+          isVec ? astContext.getExtVectorType(astContext.UnsignedIntTy, vecSize)
+                : astContext.UnsignedIntTy;
+      const auto fromType =
+          isVec ? astContext.getExtVectorType(astContext.BoolTy, vecSize)
+                : astContext.BoolTy;
+      rhsValId = castToInt(rhsValId, fromType, toType, {});
+    }
+
+    theBuilder.createStore(lhsPtr, rhsValId);
   } else if (TypeTranslator::isOpaqueType(lhsValType)) {
     // Resource types are represented using RecordType in the AST.
     // Handle them before the general RecordType.

+ 11 - 4
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -457,8 +457,15 @@ uint32_t TypeTranslator::translateType(QualType type, LayoutRule rule) {
         switch (builtinType->getKind()) {
         case BuiltinType::Void:
           return theBuilder.getVoidType();
-        case BuiltinType::Bool:
-          return theBuilder.getBoolType();
+        case BuiltinType::Bool: {
+          // According to the SPIR-V Spec: There is no physical size or bit
+          // pattern defined for boolean type. Therefore an unsigned integer is
+          // used to represent booleans when layout is required.
+          if (rule == LayoutRule::Void)
+            return theBuilder.getBoolType();
+          else
+            return theBuilder.getUint32Type();
+        }
         // All the ints
         case BuiltinType::Int:
         case BuiltinType::UInt:
@@ -518,7 +525,7 @@ uint32_t TypeTranslator::translateType(QualType type, LayoutRule rule) {
     QualType elemType = {};
     uint32_t elemCount = {};
     if (isVectorType(type, &elemType, &elemCount))
-      return theBuilder.getVecType(translateType(elemType), elemCount);
+      return theBuilder.getVecType(translateType(elemType, rule), elemCount);
   }
 
   // Matrix types
@@ -529,7 +536,7 @@ uint32_t TypeTranslator::translateType(QualType type, LayoutRule rule) {
       // 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);
+          theBuilder.getVecType(translateType(elemType, rule), colCount);
 
       // If the matrix element type is not float, it is represented as an array
       // of vectors, and should therefore have the ArrayStride decoration.

+ 1 - 1
tools/clang/test/CodeGenSPIRV/sm6.wave-active-any-true.hlsl

@@ -17,4 +17,4 @@ void main(uint3 id: SV_DispatchThreadID) {
 // CHECK:      [[cmp:%\d+]] = OpIEqual %bool {{%\d+}} %uint_0
 // CHECK-NEXT:     {{%\d+}} = OpGroupNonUniformAny %bool %int_3 [[cmp]]
     values[x].res = WaveActiveAnyTrue(values[x].val == 0);
-}
+}

+ 1 - 1
tools/clang/test/CodeGenSPIRV/type.cbuffer.hlsl

@@ -35,7 +35,7 @@ cbuffer AnotherCBuffer : register(b2) {
     float4 n;
 }
 
-// CHECK: %type_MyCbuffer = OpTypeStruct %bool %int %v2uint %mat3v4float %S %_arr_float_uint_4
+// CHECK: %type_MyCbuffer = OpTypeStruct %uint %int %v2uint %mat3v4float %S %_arr_float_uint_4
 
 // CHECK: %type_AnotherCBuffer = OpTypeStruct %v3float %v4float
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/type.constant-buffer.hlsl

@@ -15,7 +15,7 @@ struct S {
     float3 f2;
 };
 
-// CHECK: %type_ConstantBuffer_T = OpTypeStruct %bool %int %v2uint %mat3v4float %S %_arr_float_uint_4
+// CHECK: %type_ConstantBuffer_T = OpTypeStruct %uint %int %v2uint %mat3v4float %S %_arr_float_uint_4
 // CHECK: %_ptr_Uniform_type_ConstantBuffer_T = OpTypePointer Uniform %type_ConstantBuffer_T
 struct T {
     bool     a;

+ 1 - 1
tools/clang/test/CodeGenSPIRV/type.tbuffer.hlsl

@@ -35,7 +35,7 @@ tbuffer AnotherTbuffer : register(t2) {
     float4 n;
 }
 
-// CHECK: %type_MyTbuffer = OpTypeStruct %bool %int %v2uint %mat3v4float %S %_arr_float_uint_4
+// CHECK: %type_MyTbuffer = OpTypeStruct %uint %int %v2uint %mat3v4float %S %_arr_float_uint_4
 
 // CHECK: %type_AnotherTbuffer = OpTypeStruct %v3float %v4float
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/type.texture-buffer.hlsl

@@ -16,7 +16,7 @@ struct S {
   float3 f2;
 };
 
-// CHECK: %type_TextureBuffer_T = OpTypeStruct %bool %int %v2uint %mat3v4float %S %_arr_float_uint_4
+// CHECK: %type_TextureBuffer_T = OpTypeStruct %uint %int %v2uint %mat3v4float %S %_arr_float_uint_4
 // CHECK: %_ptr_Uniform_type_TextureBuffer_T = OpTypePointer Uniform %type_TextureBuffer_T
 struct T {
   bool     a;

+ 122 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.boolean.hlsl

@@ -0,0 +1,122 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
+// CHECK: OpMemberDecorate %FrameConstants 0 Offset 0
+// CHECK: OpMemberDecorate %FrameConstants 1 Offset 4
+// CHECK: OpMemberDecorate %FrameConstants 2 Offset 16
+// CHECK: OpMemberDecorate %type_CONSTANTS 0 Offset 0
+// CHECK: OpDecorate %type_CONSTANTS Block
+
+// CHECK: %FrameConstants = OpTypeStruct %uint %v3uint %_arr_v3uint_uint_2
+struct FrameConstants
+{
+  bool  boolScalar;
+  bool3 boolVec;
+  row_major bool2x3 boolMat;
+};
+
+[[vk::binding(0, 0)]]
+cbuffer CONSTANTS
+{
+  FrameConstants frameConstants;
+};
+
+// CHECK: [[v3uint0:%\d+]] = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
+// CHECK: [[v2uint0:%\d+]] = OpConstantComposite %v2uint %uint_0 %uint_0
+float4 main(in float4 texcoords : TEXCOORD0) : SV_TARGET
+{
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:     [[uintVecPtr:%\d+]] = OpAccessChain %_ptr_Uniform_v3uint [[FrameConstants]] %int_1
+// CHECK-NEXT:        [[uintVec:%\d+]] = OpLoad %v3uint [[uintVecPtr]]
+// CHECK-NEXT:        [[boolVec:%\d+]] = OpINotEqual %v3bool [[uintVec]] [[v3uint0]]
+// CHECK-NEXT:                           OpStore %a [[boolVec]]
+    bool3   a = frameConstants.boolVec;
+
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:        [[uintPtr:%\d+]] = OpAccessChain %_ptr_Uniform_uint [[FrameConstants]] %int_1 %uint_0
+// CHECK-NEXT:           [[uint:%\d+]] = OpLoad %uint [[uintPtr]]
+// CHECK-NEXT:           [[bool:%\d+]] = OpINotEqual %bool [[uint]] %uint_0
+// CHECK-NEXT:                           OpStore %b [[bool]]
+    bool    b = frameConstants.boolVec[0];
+
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:        [[uintPtr:%\d+]] = OpAccessChain %_ptr_Uniform_uint [[FrameConstants]] %int_0
+// CHECK-NEXT:           [[uint:%\d+]] = OpLoad %uint [[uintPtr]]
+// CHECK-NEXT:           [[bool:%\d+]] = OpINotEqual %bool [[uint]] %uint_0
+// CHECK-NEXT:                           OpStore %c [[bool]]
+    bool    c = frameConstants.boolScalar;
+
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:     [[uintMatPtr:%\d+]] = OpAccessChain %_ptr_Uniform__arr_v3uint_uint_2 [[FrameConstants]] %int_2
+// CHECK-NEXT:        [[uintMat:%\d+]] = OpLoad %_arr_v3uint_uint_2 [[uintMatPtr]]
+// CHECK-NEXT:       [[uintVec1:%\d+]] = OpCompositeExtract %v3uint [[uintMat]] 0
+// CHECK-NEXT:       [[boolVec1:%\d+]] = OpINotEqual %v3bool [[uintVec1]] [[v3uint0]]
+// CHECK-NEXT:       [[uintVec2:%\d+]] = OpCompositeExtract %v3uint [[uintMat]] 1
+// CHECK-NEXT:       [[boolVec2:%\d+]] = OpINotEqual %v3bool [[uintVec2]] [[v3uint0]]
+// CHECK-NEXT:        [[boolMat:%\d+]] = OpCompositeConstruct %_arr_v3bool_uint_2 [[boolVec1]] [[boolVec2]]
+// CHECK-NEXT:                           OpStore %d [[boolMat]]
+    bool2x3 d = frameConstants.boolMat;
+
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:     [[uintVecPtr:%\d+]] = OpAccessChain %_ptr_Uniform_v3uint [[FrameConstants]] %int_2 %uint_0
+// CHECK-NEXT:        [[uintVec:%\d+]] = OpLoad %v3uint [[uintVecPtr]]
+// CHECK-NEXT:        [[boolVec:%\d+]] = OpINotEqual %v3bool [[uintVec]] [[v3uint0]]
+// CHECK-NEXT:                           OpStore %e [[boolVec]]
+    bool3   e = frameConstants.boolMat[0];
+
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:        [[uintPtr:%\d+]] = OpAccessChain %_ptr_Uniform_uint [[FrameConstants]] %int_2 %uint_1 %uint_2
+// CHECK-NEXT:           [[uint:%\d+]] = OpLoad %uint [[uintPtr]]
+// CHECK-NEXT:           [[bool:%\d+]] = OpINotEqual %bool [[uint]] %uint_0
+// CHECK-NEXT:                           OpStore %f [[bool]]
+    bool    f = frameConstants.boolMat[1][2];
+
+// Swizzle Vector: out of order
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:     [[uintVecPtr:%\d+]] = OpAccessChain %_ptr_Uniform_v3uint [[FrameConstants]] %int_1
+// CHECK-NEXT:        [[uintVec:%\d+]] = OpLoad %v3uint [[uintVecPtr]]
+// CHECK-NEXT:        [[boolVec:%\d+]] = OpINotEqual %v3bool [[uintVec]] [[v3uint0]]
+// CHECK-NEXT:        [[swizzle:%\d+]] = OpVectorShuffle %v2bool [[boolVec]] [[boolVec]] 1 0
+// CHECK-NEXT:                           OpStore %g [[swizzle]]
+    bool2   g = frameConstants.boolVec.yx;
+
+// Swizzle Vector: one element only.
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:     [[uintVecPtr:%\d+]] = OpAccessChain %_ptr_Uniform_v3uint [[FrameConstants]] %int_1
+// CHECK-NEXT:        [[uintPtr:%\d+]] = OpAccessChain %_ptr_Uniform_uint [[uintVecPtr]] %int_0
+// CHECK-NEXT:           [[uint:%\d+]] = OpLoad %uint [[uintPtr]]
+// CHECK-NEXT:           [[bool:%\d+]] = OpINotEqual %bool [[uint]] %uint_0
+// CHECK-NEXT:                           OpStore %h [[bool]]
+    bool    h = frameConstants.boolVec.x;
+
+// Swizzle Vector: original indeces.
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:     [[uintVecPtr:%\d+]] = OpAccessChain %_ptr_Uniform_v3uint [[FrameConstants]] %int_1
+// CHECK-NEXT:        [[uintVec:%\d+]] = OpLoad %v3uint [[uintVecPtr]]
+// CHECK-NEXT:        [[boolVec:%\d+]] = OpINotEqual %v3bool [[uintVec]] [[v3uint0]]
+// CHECK-NEXT:                           OpStore %i [[boolVec]]
+    bool3   i = frameConstants.boolVec.xyz;
+
+// Swizzle Vector: on temporary value (rvalue)
+// CHECK:       [[uintVec1:%\d+]] = OpLoad %v3uint {{%\d+}}
+// CHECK-NEXT:  [[boolVec1:%\d+]] = OpINotEqual %v3bool [[uintVec1]] [[v3uint0]]
+// CHECK:       [[uintVec2:%\d+]] = OpLoad %v3uint {{%\d+}}
+// CHECK-NEXT:  [[boolVec2:%\d+]] = OpINotEqual %v3bool [[uintVec2]] [[v3uint0]]
+// CHECK-NEXT: [[temporary:%\d+]] = OpLogicalAnd %v3bool [[boolVec1]] [[boolVec2]]
+// CHECK-NEXT:      [[bool:%\d+]] = OpCompositeExtract %bool [[temporary]] 0
+// CHECK-NEXT:                      OpStore %j [[bool]]
+    bool    j = (frameConstants.boolVec && frameConstants.boolVec).x;
+
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:     [[uintMatPtr:%\d+]] = OpAccessChain %_ptr_Uniform__arr_v3uint_uint_2 [[FrameConstants]] %int_2
+// CHECK-NEXT:        [[uintPtr:%\d+]] = OpAccessChain %_ptr_Uniform_uint [[uintMatPtr]] %int_1 %int_2
+// CHECK-NEXT:          [[uint0:%\d+]] = OpLoad %uint [[uintPtr]]
+// CHECK-NEXT:        [[uintPtr:%\d+]] = OpAccessChain %_ptr_Uniform_uint [[uintMatPtr]] %int_0 %int_1
+// CHECK-NEXT:          [[uint1:%\d+]] = OpLoad %uint [[uintPtr]]
+// CHECK-NEXT:        [[uintVec:%\d+]] = OpCompositeConstruct %v2uint [[uint0]] [[uint1]]
+// CHECK-NEXT:        [[boolVec:%\d+]] = OpINotEqual %v2bool [[uintVec]] [[v2uint0]]
+// CHECK-NEXT:                           OpStore %k [[boolVec]]
+    bool2   k = frameConstants.boolMat._m12_m01;
+
+    return (1.0).xxxx;
+}

+ 35 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.rwstructuredbuffer.boolean.hlsl

@@ -0,0 +1,35 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S
+{
+  bool  boolScalar;
+  bool3 boolVec;
+  row_major bool2x3 boolMat;
+};
+
+RWStructuredBuffer<S> values;
+
+// CHECK: [[v3uint1:%\d+]] = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+// CHECK: [[v3uint0:%\d+]] = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
+void main()
+{
+  bool3 boolVecVar;
+  bool2 boolVecVar2;
+  row_major bool2x3 boolMatVar;
+
+// CHECK:                [[uintPtr:%\d+]] = OpAccessChain %_ptr_Uniform_uint %values %int_0 %uint_0 %int_0
+// CHECK-NEXT: [[convertTrueToUint:%\d+]] = OpSelect %uint %true %uint_1 %uint_0
+// CHECK-NEXT:                              OpStore [[uintPtr]] [[convertTrueToUint]]
+  values[0].boolScalar = true;
+
+// CHECK:      [[boolVecVar:%\d+]] = OpLoad %v3bool %boolVecVar
+// CHECK-NEXT: [[uintVecPtr:%\d+]] = OpAccessChain %_ptr_Uniform_v3uint %values %int_0 %uint_1 %int_1
+// CHECK-NEXT: [[uintVecVar:%\d+]] = OpSelect %v3uint [[boolVecVar]] [[v3uint1]] [[v3uint0]]
+// CHECK-NEXT:                       OpStore [[uintVecPtr]] [[uintVecVar]]
+  values[1].boolVec = boolVecVar;
+
+  // TODO: In the following cases, OpAccessChain runs into type mismatch issues due to decoration differences.
+  // values[2].boolMat = boolMatVar;
+  // values[0].boolVec.yzx = boolVecVar;
+  // values[0].boolMat._m12_m11 = boolVecVar2;
+}

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

@@ -1299,6 +1299,12 @@ TEST_F(FileTest, VulkanLayoutCBufferNestedStd140) {
 TEST_F(FileTest, VulkanLayoutCBufferNestedEmptyStd140) {
   runFileTest("vk.layout.cbuffer.nested.empty.std140.hlsl");
 }
+TEST_F(FileTest, VulkanLayoutCBufferBoolean) {
+  runFileTest("vk.layout.cbuffer.boolean.hlsl");
+}
+TEST_F(FileTest, VulkanLayoutRWStructuredBufferBoolean) {
+  runFileTest("vk.layout.rwstructuredbuffer.boolean.hlsl");
+}
 TEST_F(FileTest, VulkanLayoutSBufferStd430) {
   runFileTest("vk.layout.sbuffer.std430.hlsl");
 }

+ 5 - 0
tools/clang/unittests/SPIRV/WholeFileTestFixture.h

@@ -49,6 +49,10 @@ public:
   void runWholeFileTest(llvm::StringRef path, bool generateHeader = false,
                         bool runSpirvValidation = true);
 
+  WholeFileTest() : targetEnv(SPV_ENV_VULKAN_1_0) {}
+
+  void useVulkan1p1() { targetEnv = SPV_ENV_VULKAN_1_1; }
+
 private:
   /// \brief Reads in the given input file.
   /// Stores the SPIR-V portion of the file into the <expectedSpirvAsm>
@@ -63,6 +67,7 @@ private:
   std::vector<uint32_t> generatedBinary; ///< The generated SPIR-V Binary
   std::string expectedSpirvAsm;          ///< Expected SPIR-V parsed from input
   std::string generatedSpirvAsm;         ///< Disassembled binary (SPIR-V code)
+  spv_target_env targetEnv;              ///< Environment to validate against
 };
 
 } // end namespace spirv