Browse Source

Merge branch 'rtmaster' into user/texr/library-reflection

Tex Riddell 7 years ago
parent
commit
42ca602351
51 changed files with 842 additions and 418 deletions
  1. 3 1
      README.md
  2. 2 2
      appveyor.yml
  3. 31 27
      docs/SPIR-V.rst
  4. 1 1
      external/SPIRV-Headers
  5. 4 4
      include/dxc/Support/HLSLOptions.h
  6. 37 20
      lib/DxcSupport/HLSLOptions.cpp
  7. 44 1
      lib/HLSL/HLOperationLower.cpp
  8. 5 4
      tools/clang/include/clang/SPIRV/EmitSPIRVOptions.h
  9. 1 2
      tools/clang/include/clang/SPIRV/FeatureManager.h
  10. 8 0
      tools/clang/include/clang/SPIRV/ModuleBuilder.h
  11. 9 1
      tools/clang/include/clang/SPIRV/Structure.h
  12. 58 84
      tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
  13. 2 18
      tools/clang/lib/SPIRV/DeclResultIdMapper.h
  14. 11 17
      tools/clang/lib/SPIRV/FeatureManager.cpp
  15. 0 1
      tools/clang/lib/SPIRV/ModuleBuilder.cpp
  16. 101 35
      tools/clang/lib/SPIRV/SPIRVEmitter.cpp
  17. 9 2
      tools/clang/lib/SPIRV/Structure.cpp
  18. 52 19
      tools/clang/lib/SPIRV/TypeTranslator.cpp
  19. 4 4
      tools/clang/lib/SPIRV/TypeTranslator.h
  20. 13 0
      tools/clang/test/CodeGenHLSL/quick-test/atan.hlsl
  21. 2 1
      tools/clang/test/CodeGenHLSL/quick-test/convergent.hlsl
  22. 10 0
      tools/clang/test/CodeGenSPIRV/intrinsics.interlocked-methods.cs.hlsl
  23. 21 3
      tools/clang/test/CodeGenSPIRV/oo.struct.method.hlsl
  24. 13 0
      tools/clang/test/CodeGenSPIRV/semantic.dispatch-thread-id.int2.cs.hlsl
  25. 0 12
      tools/clang/test/CodeGenSPIRV/semantic.dispatch-thread-id.uint2.cs.hlsl
  26. 13 0
      tools/clang/test/CodeGenSPIRV/semantic.group-id.int2.cs.hlsl
  27. 0 12
      tools/clang/test/CodeGenSPIRV/semantic.group-id.uint2.cs.hlsl
  28. 13 0
      tools/clang/test/CodeGenSPIRV/semantic.group-thread-id.int2.cs.hlsl
  29. 0 12
      tools/clang/test/CodeGenSPIRV/semantic.group-thread-id.uint2.cs.hlsl
  30. 9 0
      tools/clang/test/CodeGenSPIRV/spirv.debug.opsource.hlsl
  31. 0 1
      tools/clang/test/CodeGenSPIRV/spirv.interface.ds.hlsl
  32. 0 1
      tools/clang/test/CodeGenSPIRV/spirv.interface.gs.hlsl
  33. 0 1
      tools/clang/test/CodeGenSPIRV/spirv.interface.hs.hlsl
  34. 0 1
      tools/clang/test/CodeGenSPIRV/spirv.interface.ps.hlsl
  35. 0 1
      tools/clang/test/CodeGenSPIRV/spirv.interface.vs.hlsl
  36. 53 0
      tools/clang/test/CodeGenSPIRV/spirv.stage-io.16bit.hlsl
  37. 14 1
      tools/clang/test/CodeGenSPIRV/texture.array.gather-red.hlsl
  38. 14 1
      tools/clang/test/CodeGenSPIRV/texture.gather-red.hlsl
  39. 3 3
      tools/clang/test/CodeGenSPIRV/type.struct.hlsl
  40. 53 0
      tools/clang/test/CodeGenSPIRV/vk.binding.cl.all-sets.hlsl
  41. 0 30
      tools/clang/test/CodeGenSPIRV/vk.binding.cl.error.hlsl
  42. 0 42
      tools/clang/test/CodeGenSPIRV/vk.binding.explicit.error.hlsl
  43. 0 29
      tools/clang/test/CodeGenSPIRV/vk.binding.register.error.hlsl
  44. 41 1
      tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.boolean.hlsl
  45. 56 0
      tools/clang/test/CodeGenSPIRV/vk.layout.rwstructuredbuffer.boolean.hlsl
  46. 25 0
      tools/clang/test/CodeGenSPIRV/vk.push-constant.anon-struct.hlsl
  47. 1 0
      tools/clang/tools/dxcompiler/dxcompilerobj.cpp
  48. 22 15
      tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp
  49. 1 2
      tools/clang/unittests/SPIRV/FileTestUtils.cpp
  50. 82 5
      tools/clang/unittests/SPIRV/TestMain.cpp
  51. 1 1
      utils/appveyor/appveyor_test.ps1

+ 3 - 1
README.md

@@ -1,6 +1,6 @@
 # DirectX Shader Compiler
 
-[![Build status](https://ci.appveyor.com/api/projects/status/5cwy3b8y1oi71lvl?svg=true)](https://ci.appveyor.com/project/marcelolr/directxshadercompiler)
+[![Build status](https://ci.appveyor.com/api/projects/status/oaf66n7w30xbrg38/branch/master?svg=true)](https://ci.appveyor.com/project/antiagainst/directxshadercompiler/branch/master)
 
 The DirectX Shader Compiler project includes a compiler and related tools used to compile High-Level Shader Language (HLSL) programs into DirectX Intermediate Language (DXIL) representation. Applications that make use of DirectX for graphics, games, and computation can use it to generate shader programs.
 
@@ -28,6 +28,8 @@ As an example of community contribution, this project can also target the [SPIR-
 
 ## Building Sources
 
+Note: Instead of building manually, you can download the artifacts built by Appveyor for the latest master branch at [here](https://ci.appveyor.com/project/antiagainst/directxshadercompiler/branch/master/artifacts).
+
 Before you build, you will need to have some additional software installed. This is the most straightforward path - see [Building Sources](https://github.com/Microsoft/DirectXShaderCompiler/wiki/Building-Sources) on the Wiki for more options, including Visual Studio 2015 and Ninja support.
 
 * [Git](http://git-scm.com/downloads).

+ 2 - 2
appveyor.yml

@@ -2,7 +2,7 @@ version: 1.0.{build}
 
 image: Visual Studio 2017
 platform: x64
-configuration: Debug
+configuration: Release
 
 clone_folder: c:\projects\DirectXShaderCompiler
 clone_depth: 1
@@ -22,7 +22,7 @@ build_script:
 
 test_script:
 - ps:  utils\appveyor\appveyor_test.ps1
-- cmd: call utils\hct\hcttest spirv_only
+- cmd: call utils\hct\hcttest -rel spirv_only
 
 after_test:
 - cmd: cd build\%CONFIGURATION%\bin

+ 31 - 27
docs/SPIR-V.rst

@@ -297,22 +297,24 @@ interface variables:
 SPIR-V version and extension
 ----------------------------
 
-In the **defult** mode (without ``-fspv-extension=<extension>`` command-line
-option), SPIR-V CodeGen will try its best to use the lowest SPIR-V version, and
-only require higher SPIR-V versions and extensions when they are truly needed
-for translating the input source code.
-
-For example, unless `Shader Model 6.0 wave intrinsics`_ are used, the generated
-SPIR-V will always be of version 1.0. The ``SPV_KHR_multivew`` extension will
-not be emitted unless you use ``SV_ViewID``.
-
-You can of course have fine-grained control of what extensions are permitted
-in the CodeGen using the **explicit** mode, turned on by the
-``-fspv-extension=<extension>`` command-line option. Only extensions supplied
-via ``-fspv-extension=`` will be used. If that does not suffice, errors will
-be emitted explaining what additional extensions are required to translate what
-specific feature in the source code. If you want to allow all KHR extensions,
-you can use ``-fspv-extension=KHR``.
+SPIR-V CodeGen provides two command-line options for fine-grained SPIR-V target
+environment (hence SPIR-V version) and SPIR-V extension control:
+
+- ``-fspv-target-env=``: for specifying SPIR-V target environment
+- ``-fspv-extension=``: for specifying allowed SPIR-V extensions
+
+``-fspv-target-env=`` only accepts ``vulkan1.0`` and ``vulkan1.1`` right now.
+If such an option is not given, the CodeGen defaults to ``vulkan1.0``. When
+targeting ``vulkan1.0``, trying to use features that are only available
+in Vulkan 1.1 (SPIR-V 1.3), like `Shader Model 6.0 wave intrinsics`_, will
+trigger a compiler error.
+
+If ``-fspv-extension=`` is not specified, the CodeGen will select suitable
+SPIR-V extensions to translate the source code. Otherwise, only extensions
+supplied via ``-fspv-extension=`` will be used. If that does not suffice, errors
+will be emitted explaining what additional extensions are required to translate
+what specific feature in the source code. If you want to allow all KHR
+extensions, you can use ``-fspv-extension=KHR``.
 
 Legalization, optimization, validation
 --------------------------------------
@@ -667,12 +669,12 @@ right now:
    Uniform Buffer Layout" and "Standard Storage Buffer Layout" <https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#interfaces-resources-layout>`_,
    respectively.
    They are the default.
-2. Strict OpenGL ``std140`` for uniform buffers and strict OpenGL ``std430``
-   for storage buffers: they allow packing data on the application side that
-   can be shared with OpenGL. They can be enabled by ``-fvk-use-gl-layout``.
-3. DirectX memory layout rules for uniform buffers and storage buffers:
+2. DirectX memory layout rules for uniform buffers and storage buffers:
    they allow packing data on the application side that can be shared with
    DirectX. They can be enabled by ``-fvk-use-dx-layout``.
+3. Strict OpenGL ``std140`` for uniform buffers and strict OpenGL ``std430``
+   for storage buffers: they allow packing data on the application side that
+   can be shared with OpenGL. They can be enabled by ``-fvk-use-gl-layout``.
 
 In the above, "vector-relaxed OpenGL ``std140``/``std430``" rules mean OpenGL
 ``std140``/``std430`` rules with the following modification for vector type
@@ -2197,7 +2199,9 @@ There are a few overloads for these functions:
 
 - For those overloads taking 4 offset parameters, those offset parameters will
   be conveyed as an additional ``ConstOffsets`` image operands to the
-  instruction. So those offset parameters must all be constant values.
+  instruction if those offset parameters are all constants. Otherwise,
+  4 separate ``OpImageGather`` instructions will be emitted to get each texel
+  from each offset, using the ``Offset`` image operands.
 - For those overloads with the ``status`` parameter, ``OpImageSparseGather``
   is used instead, and the resulting SPIR-V ``Residency Code`` will be
   written to ``status``.
@@ -2631,11 +2635,10 @@ generated. ``.RestartStrip()`` method calls will be translated into the SPIR-V
 Shader Model 6.0 Wave Intrinsics
 ================================
 
-::
 
-  Wave intrinsics requires SPIR-V 1.3, which is supported by Vulkan 1.1.
-  If you use wave intrinsics in your source code, the generated SPIR-V code
-  will be of version 1.3 instead of 1.0, which is supported by Vulkan 1.0.
+Note that Wave intrinsics requires SPIR-V 1.3, which is supported by Vulkan 1.1.
+If you use wave intrinsics in your source code, you will need to specify
+-fspv-target-env=vulkan1.1 via the command line to target Vulkan 1.1.
 
 Shader model 6.0 introduces a set of wave operations. Apart from
 ``WaveGetLaneCount()`` and ``WaveGetLaneIndex()``, which are translated into
@@ -2683,8 +2686,9 @@ codegen for Vulkan:
   sets its Vulkan descriptor set to ``M`` and binding number to ``X + N``. If
   you need to shift the inferred binding numbers for more than one space,
   provide more than one such option. If more than one such option is provided
-  for the same space, the last one takes effect. See `HLSL register and Vulkan
-  binding`_ for explanation and examples.
+  for the same space, the last one takes effect. If you need to shift the
+  inferred binding numbers for all sets, use ``all`` as ``M``.
+  See `HLSL register and Vulkan binding`_ for explanation and examples.
 - ``-fvk-t-shift N M``, similar to ``-fvk-b-shift``, but for t-type registers.
 - ``-fvk-s-shift N M``, similar to ``-fvk-b-shift``, but for s-type registers.
 - ``-fvk-u-shift N M``, similar to ``-fvk-b-shift``, but for u-type registers.

+ 1 - 1
external/SPIRV-Headers

@@ -1 +1 @@
-Subproject commit 12f8de9f04327336b699b1b80aa390ae7f9ddbf4
+Subproject commit 3a4dbdde9a9b2cf23736694ba70262dce27fbeaa

+ 4 - 4
include/dxc/Support/HLSLOptions.h

@@ -167,10 +167,10 @@ public:
   bool VkUseDxLayout;                      // OPT_fvk_use_dx_layout
   bool SpvEnableReflect;                   // OPT_fspv_reflect
   llvm::StringRef VkStageIoOrder;          // OPT_fvk_stage_io_order
-  llvm::SmallVector<uint32_t, 4> VkBShift; // OPT_fvk_b_shift
-  llvm::SmallVector<uint32_t, 4> VkTShift; // OPT_fvk_t_shift
-  llvm::SmallVector<uint32_t, 4> VkSShift; // OPT_fvk_s_shift
-  llvm::SmallVector<uint32_t, 4> VkUShift; // OPT_fvk_u_shift
+  llvm::SmallVector<int32_t, 4> VkBShift;  // OPT_fvk_b_shift
+  llvm::SmallVector<int32_t, 4> VkTShift;  // OPT_fvk_t_shift
+  llvm::SmallVector<int32_t, 4> VkSShift;  // OPT_fvk_s_shift
+  llvm::SmallVector<int32_t, 4> VkUShift;  // OPT_fvk_u_shift
   llvm::SmallVector<llvm::StringRef, 4> SpvExtensions; // OPT_fspv_extension
   llvm::StringRef SpvTargetEnv;                        // OPT_fspv_target_env
 #endif

+ 37 - 20
lib/DxcSupport/HLSLOptions.cpp

@@ -490,26 +490,43 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   opts.VkIgnoreUnusedResources = Args.hasFlag(OPT_fvk_ignore_unused_resources, OPT_INVALID, false);
 
   // Collects the arguments for -fvk-{b|s|t|u}-shift.
-  const auto handleVkShiftArgs = [genSpirv, &Args, &errors](
-      OptSpecifier id, const char* name, llvm::SmallVectorImpl<uint32_t>* shifts) {
-    const auto values = Args.getAllArgValues(id);
-
-    if (!genSpirv && !values.empty()) {
-      errors << "-fvk-" << name << "-shift requires -spirv";
-      return false;
-    }
-
-    shifts->clear();
-    for (const auto& val : values) {
-      uint32_t number = 0;
-      if (llvm::StringRef(val).getAsInteger(10, number)) {
-        errors << "invalid -fvk-" << name << "-shift argument: " << val;
-        return false;
-      }
-      shifts->push_back(number);
-    }
-    return true;
-  };
+  const auto handleVkShiftArgs =
+      [genSpirv, &Args, &errors](OptSpecifier id, const char *name,
+                                 llvm::SmallVectorImpl<int32_t> *shifts) {
+        const auto values = Args.getAllArgValues(id);
+
+        if (!genSpirv && !values.empty()) {
+          errors << "-fvk-" << name << "-shift requires -spirv";
+          return false;
+        }
+
+        shifts->clear();
+        bool setForAll = false;
+
+        for (const auto &val : values) {
+          int32_t number = 0;
+          if (val == "all") {
+            number = -1;
+            setForAll = true;
+          } else {
+            if (llvm::StringRef(val).getAsInteger(10, number)) {
+              errors << "invalid -fvk-" << name << "-shift argument: " << val;
+              return false;
+            }
+            if (number < 0) {
+              errors << "negative -fvk-" << name << "-shift argument: " << val;
+              return false;
+            }
+          }
+          shifts->push_back(number);
+        }
+        if (setForAll && shifts->size() > 2) {
+          errors << "setting all sets via -fvk-" << name
+                 << "-shift argument should be used alone";
+          return false;
+        }
+        return true;
+      };
 
   if (!handleVkShiftArgs(OPT_fvk_b_shift, "b", &opts.VkBShift) ||
       !handleVkShiftArgs(OPT_fvk_t_shift, "t", &opts.VkTShift) ||

+ 44 - 1
lib/HLSL/HLOperationLower.cpp

@@ -1271,7 +1271,50 @@ Value *TranslateAtan2(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,
 
   IRBuilder<> Builder(CI);
   Value *tan = Builder.CreateFDiv(y, x);
-  return TrivialDxilUnaryOperation(OP::OpCode::Atan, tan, hlslOP, Builder);
+
+  Value *atan =
+      TrivialDxilUnaryOperation(OP::OpCode::Atan, tan, hlslOP, Builder);
+  // TODO: include M_PI from math.h.
+  const double M_PI = 3.14159265358979323846;
+  // Modify atan result based on https://en.wikipedia.org/wiki/Atan2.
+  Type *Ty = x->getType();
+  Constant *pi = ConstantFP::get(Ty->getScalarType(), M_PI);
+  Constant *halfPi = ConstantFP::get(Ty->getScalarType(), M_PI / 2);
+  Constant *negHalfPi = ConstantFP::get(Ty->getScalarType(), -M_PI / 2);
+  Constant *zero = ConstantFP::get(Ty->getScalarType(), 0);
+  if (Ty != Ty->getScalarType()) {
+    unsigned vecSize = Ty->getVectorNumElements();
+    pi = ConstantVector::getSplat(vecSize, pi);
+    halfPi = ConstantVector::getSplat(vecSize, halfPi);
+    negHalfPi = ConstantVector::getSplat(vecSize, negHalfPi);
+    zero = ConstantVector::getSplat(vecSize, zero);
+  }
+  Value *atanAddPi = Builder.CreateFAdd(atan, pi);
+  Value *atanSubPi = Builder.CreateFSub(atan, pi);
+
+  // x > 0 -> atan.
+  Value *result = atan;
+  Value *xLt0 = Builder.CreateFCmpOLT(x, zero);
+  Value *xEq0 = Builder.CreateFCmpOEQ(x, zero);
+
+  Value *yGe0 = Builder.CreateFCmpOGE(y, zero);
+  Value *yLt0 = Builder.CreateFCmpOLT(y, zero);
+  // x < 0, y >= 0 -> atan + pi.
+  Value *xLt0AndyGe0 = Builder.CreateAnd(xLt0, yGe0);
+  result = Builder.CreateSelect(xLt0AndyGe0, atanAddPi, result);
+
+  // x < 0, y < 0 -> atan - pi.
+  Value *xLt0AndYLt0 = Builder.CreateAnd(xLt0, yLt0);
+  result = Builder.CreateSelect(xLt0AndYLt0, atanSubPi, result);
+
+  // x == 0, y < 0 -> -pi/2
+  Value *xEq0AndYLt0 = Builder.CreateAnd(xEq0, yLt0);
+  result = Builder.CreateSelect(xEq0AndYLt0, negHalfPi, result);
+  // x == 0, y > 0 -> pi/2
+  Value *xEq0AndYGe0 = Builder.CreateAnd(xEq0, yGe0);
+  result = Builder.CreateSelect(xEq0AndYGe0, halfPi, result);
+
+  return result;
 }
 
 Value *TranslateClamp(CallInst *CI, IntrinsicOp IOP, OP::OpCode opcode,

+ 5 - 4
tools/clang/include/clang/SPIRV/EmitSPIRVOptions.h

@@ -38,11 +38,12 @@ struct EmitSPIRVOptions {
   bool ignoreUnusedResources;
   bool enable16BitTypes;
   bool enableReflect;
+  bool enableDebugInfo;
   llvm::StringRef stageIoOrder;
-  llvm::SmallVector<uint32_t, 4> bShift;
-  llvm::SmallVector<uint32_t, 4> tShift;
-  llvm::SmallVector<uint32_t, 4> sShift;
-  llvm::SmallVector<uint32_t, 4> uShift;
+  llvm::SmallVector<int32_t, 4> bShift;
+  llvm::SmallVector<int32_t, 4> tShift;
+  llvm::SmallVector<int32_t, 4> sShift;
+  llvm::SmallVector<int32_t, 4> uShift;
   llvm::SmallVector<llvm::StringRef, 4> allowedExtensions;
   llvm::StringRef targetEnv;
   spirv::LayoutRule cBufferLayoutRule;

+ 1 - 2
tools/clang/include/clang/SPIRV/FeatureManager.h

@@ -15,7 +15,6 @@
 
 #include <string>
 
-
 #include "spirv-tools/libspirv.h"
 
 #include "clang/Basic/Diagnostic.h"
@@ -31,6 +30,7 @@ namespace spirv {
 /// A list of SPIR-V extensions known to our CodeGen.
 enum class Extension {
   KHR = 0,
+  KHR_16bit_storage,
   KHR_device_group,
   KHR_multiview,
   KHR_shader_draw_parameters,
@@ -38,7 +38,6 @@ enum class Extension {
   EXT_shader_stencil_export,
   AMD_gpu_shader_half_float,
   AMD_shader_explicit_vertex_parameter,
-  GOOGLE_decorate_string,
   GOOGLE_hlsl_functionality1,
   Unknown,
 };

+ 8 - 0
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -332,6 +332,10 @@ public:
 
   inline void setShaderModelVersion(uint32_t major, uint32_t minor);
 
+  /// \brief Sets the source file name and the <result-id> for the OpString for
+  /// the file name.
+  inline void setSourceFileName(uint32_t id, std::string name);
+
   /// \brief Adds an execution mode to the module under construction.
   void addExecutionMode(uint32_t entryPointId, spv::ExecutionMode em,
                         llvm::ArrayRef<uint32_t> params);
@@ -516,6 +520,10 @@ void ModuleBuilder::setShaderModelVersion(uint32_t major, uint32_t minor) {
   theModule.setShaderModelVersion(major * 100 + minor * 10);
 }
 
+void ModuleBuilder::setSourceFileName(uint32_t id, std::string name) {
+  theModule.setSourceFileName(id, std::move(name));
+}
+
 } // end namespace spirv
 } // end namespace clang
 

+ 9 - 1
tools/clang/include/clang/SPIRV/Structure.h

@@ -307,6 +307,7 @@ public:
                             llvm::ArrayRef<uint32_t> intefaces);
   inline void addExecutionMode(Instruction &&);
   inline void setShaderModelVersion(uint32_t);
+  inline void setSourceFileName(uint32_t id, std::string name);
   // TODO: source code debug information
   inline void addDebugName(uint32_t targetId, llvm::StringRef name,
                            llvm::Optional<uint32_t> memberIndex = llvm::None);
@@ -338,6 +339,8 @@ private:
   std::vector<EntryPoint> entryPoints;
   std::vector<Instruction> executionModes;
   uint32_t shaderModelVersion;
+  uint32_t sourceFileNameId; // The <result-id> for the OpString for file name
+  std::string sourceFileName;
   // TODO: source code debug information
   std::set<DebugName> debugNames;
   llvm::SetVector<std::pair<uint32_t, const Decoration *>> decorations;
@@ -449,7 +452,7 @@ TypeIdPair::TypeIdPair(const Type &ty, uint32_t id) : type(ty), resultId(id) {}
 
 SPIRVModule::SPIRVModule()
     : addressingModel(llvm::None), memoryModel(llvm::None),
-      shaderModelVersion(0) {}
+      shaderModelVersion(0), sourceFileNameId(0) {}
 
 void SPIRVModule::setVersion(uint32_t version) { header.version = version; }
 void SPIRVModule::setBound(uint32_t newBound) { header.bound = newBound; }
@@ -495,6 +498,11 @@ void SPIRVModule::setShaderModelVersion(uint32_t version) {
   shaderModelVersion = version;
 }
 
+void SPIRVModule::setSourceFileName(uint32_t id, std::string name) {
+  sourceFileNameId = id;
+  sourceFileName = std::move(name);
+}
+
 void SPIRVModule::addDebugName(uint32_t targetId, llvm::StringRef name,
                                llvm::Optional<uint32_t> memberIndex) {
 

+ 58 - 84
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -37,15 +37,6 @@ const hlsl::RegisterAssignment *getResourceBinding(const NamedDecl *decl) {
   return nullptr;
 }
 
-/// \brief Returns the resource category for the given type.
-ResourceVar::Category getResourceCategory(QualType type) {
-  if (TypeTranslator::isTexture(type) || TypeTranslator::isRWTexture(type))
-    return ResourceVar::Category::Image;
-  if (TypeTranslator::isSampler(type))
-    return ResourceVar::Category::Sampler;
-  return ResourceVar::Category::Other;
-}
-
 /// \brief Returns true if the given declaration has a primitive type qualifier.
 /// Returns false otherwise.
 inline bool hasGSPrimitiveTypeQualifier(const Decl *decl) {
@@ -436,8 +427,7 @@ SpirvEvalInfo DeclResultIdMapper::createExternVar(const VarDecl *var) {
   const auto *bindingAttr = var->getAttr<VKBindingAttr>();
   const auto *counterBindingAttr = var->getAttr<VKCounterBindingAttr>();
 
-  resourceVars.emplace_back(id, getResourceCategory(var->getType()), regAttr,
-                            bindingAttr, counterBindingAttr);
+  resourceVars.emplace_back(id, regAttr, bindingAttr, counterBindingAttr);
 
   if (const auto *inputAttachment = var->getAttr<VKInputAttachmentIndexAttr>())
     theBuilder.decorateInputAttachmentIndex(id, inputAttachment->getIndex());
@@ -481,11 +471,10 @@ uint32_t DeclResultIdMapper::createStructOrStructArrayVarOfExplicitLayout(
     llvm::StringRef varName) {
   // cbuffers are translated into OpTypeStruct with Block decoration.
   // tbuffers are translated into OpTypeStruct with BufferBlock decoration.
-  // PushConstants are translated into OpTypeStruct with Block decoration.
+  // Push constants are translated into OpTypeStruct with Block decoration.
   //
-  // Both cbuffers and tbuffers have the SPIR-V Uniform storage class. cbuffers
-  // follow GLSL std140 layout rules, and tbuffers follow GLSL std430 layout
-  // rules. PushConstants follow GLSL std430 layout rules.
+  // Both cbuffers and tbuffers have the SPIR-V Uniform storage class.
+  // Push constants have the SPIR-V PushConstant storage class.
 
   const bool forCBuffer = usageKind == ContextUsageKind::CBuffer;
   const bool forTBuffer = usageKind == ContextUsageKind::TBuffer;
@@ -577,9 +566,9 @@ uint32_t DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
                                              : spirvOptions.tBufferLayoutRule);
     astDecls[varDecl].indexInCTBuffer = index++;
   }
-  resourceVars.emplace_back(
-      bufferVar, ResourceVar::Category::Other, getResourceBinding(decl),
-      decl->getAttr<VKBindingAttr>(), decl->getAttr<VKCounterBindingAttr>());
+  resourceVars.emplace_back(bufferVar, getResourceBinding(decl),
+                            decl->getAttr<VKBindingAttr>(),
+                            decl->getAttr<VKCounterBindingAttr>());
 
   return bufferVar;
 }
@@ -615,9 +604,9 @@ uint32_t DeclResultIdMapper::createCTBuffer(const VarDecl *decl) {
           .setStorageClass(spv::StorageClass::Uniform)
           .setLayoutRule(context->isCBuffer() ? spirvOptions.cBufferLayoutRule
                                               : spirvOptions.tBufferLayoutRule);
-  resourceVars.emplace_back(
-      bufferVar, ResourceVar::Category::Other, getResourceBinding(context),
-      decl->getAttr<VKBindingAttr>(), decl->getAttr<VKCounterBindingAttr>());
+  resourceVars.emplace_back(bufferVar, getResourceBinding(context),
+                            decl->getAttr<VKBindingAttr>(),
+                            decl->getAttr<VKCounterBindingAttr>());
 
   return bufferVar;
 }
@@ -652,8 +641,7 @@ void DeclResultIdMapper::createGlobalsCBuffer(const VarDecl *var) {
       context, /*arraySize*/ 0, ContextUsageKind::Globals, "type.$Globals",
       "$Globals");
 
-  resourceVars.emplace_back(globals, ResourceVar::Category::Other, nullptr,
-                            nullptr, nullptr);
+  resourceVars.emplace_back(globals, nullptr, nullptr, nullptr);
 
   uint32_t index = 0;
   for (const auto *decl : typeTranslator.collectDeclsInDeclContext(context))
@@ -760,8 +748,7 @@ void DeclResultIdMapper::createCounterVar(
   if (!isAlias) {
     // Non-alias counter variables should be put in to resourceVars so that
     // descriptors can be allocated for them.
-    resourceVars.emplace_back(counterId, ResourceVar::Category::Other,
-                              getResourceBinding(decl),
+    resourceVars.emplace_back(counterId, getResourceBinding(decl),
                               decl->getAttr<VKBindingAttr>(),
                               decl->getAttr<VKCounterBindingAttr>(), true);
     assert(declId);
@@ -861,39 +848,24 @@ private:
 /// set and binding number.
 class BindingSet {
 public:
-  /// Tries to use the given set and binding number. Returns true if possible,
-  /// false otherwise and writes the source location of where the binding number
-  /// is used to *usedLoc.
-  bool tryToUseBinding(uint32_t binding, uint32_t set,
-                       ResourceVar::Category category, SourceLocation tryLoc,
-                       SourceLocation *usedLoc) {
-    const auto cat = static_cast<uint32_t>(category);
-    // Note that we will create the entry for binding in bindings[set] here.
-    // But that should not have bad effects since it defaults to zero.
-    if ((usedBindings[set][binding] & cat) == 0) {
-      usedBindings[set][binding] |= cat;
-      whereUsed[set][binding] = tryLoc;
-      return true;
-    }
-    *usedLoc = whereUsed[set][binding];
-    return false;
+  /// Uses the given set and binding number.
+  void useBinding(uint32_t binding, uint32_t set) {
+    usedBindings[set].insert(binding);
   }
 
   /// Uses the next avaiable binding number in set 0.
-  uint32_t useNextBinding(uint32_t set, ResourceVar::Category category) {
+  uint32_t useNextBinding(uint32_t set) {
     auto &binding = usedBindings[set];
     auto &next = nextBindings[set];
     while (binding.count(next))
       ++next;
-    binding[next] = static_cast<uint32_t>(category);
+    binding.insert(next);
     return next++;
   }
 
 private:
-  ///< set number -> (binding number -> resource category)
-  llvm::DenseMap<uint32_t, llvm::DenseMap<uint32_t, uint32_t>> usedBindings;
-  ///< set number -> (binding number -> source location)
-  llvm::DenseMap<uint32_t, llvm::DenseMap<uint32_t, SourceLocation>> whereUsed;
+  ///< set number -> set of used binding number
+  llvm::DenseMap<uint32_t, llvm::DenseSet<uint32_t>> usedBindings;
   ///< set number -> next available binding number
   llvm::DenseMap<uint32_t, uint32_t> nextBindings;
 };
@@ -1043,15 +1015,19 @@ namespace {
 /// sets.
 class BindingShiftMapper {
 public:
-  explicit BindingShiftMapper(const llvm::SmallVectorImpl<uint32_t> &shifts)
+  explicit BindingShiftMapper(const llvm::SmallVectorImpl<int32_t> &shifts)
       : masterShift(0) {
     assert(shifts.size() % 2 == 0);
-    for (uint32_t i = 0; i < shifts.size(); i += 2)
-      perSetShift[shifts[i + 1]] = shifts[i];
+    if (shifts.size() == 2 && shifts[1] == -1) {
+      masterShift = shifts[0];
+    } else {
+      for (uint32_t i = 0; i < shifts.size(); i += 2)
+        perSetShift[shifts[i + 1]] = shifts[i];
+    }
   }
 
   /// Returns the shift amount for the given set.
-  uint32_t getShiftForSet(uint32_t set) const {
+  int32_t getShiftForSet(int32_t set) const {
     const auto found = perSetShift.find(set);
     if (found != perSetShift.end())
       return found->second;
@@ -1060,7 +1036,7 @@ public:
 
 private:
   uint32_t masterShift; /// Shift amount applies to all sets.
-  llvm::DenseMap<uint32_t, uint32_t> perSetShift;
+  llvm::DenseMap<int32_t, int32_t> perSetShift;
 };
 } // namespace
 
@@ -1086,19 +1062,11 @@ bool DeclResultIdMapper::decorateResourceBindings() {
   BindingSet bindingSet;
 
   // Decorates the given varId of the given category with set number
-  // setNo, binding number bindingNo. Emits warning if overlap.
-  const auto tryToDecorate = [this, &bindingSet](
-                                 const uint32_t varId, const uint32_t setNo,
-                                 const uint32_t bindingNo,
-                                 const ResourceVar::Category cat,
-                                 SourceLocation loc) {
-    SourceLocation prevUseLoc;
-    if (!bindingSet.tryToUseBinding(bindingNo, setNo, cat, loc, &prevUseLoc)) {
-      emitWarning("resource binding #%0 in descriptor set #%1 already assigned",
-                  loc)
-          << bindingNo << setNo;
-      emitNote("binding number previously assigned here", prevUseLoc);
-    }
+  // setNo, binding number bindingNo. Ignores overlaps.
+  const auto tryToDecorate = [this, &bindingSet](const uint32_t varId,
+                                                 const uint32_t setNo,
+                                                 const uint32_t bindingNo) {
+    bindingSet.useBinding(bindingNo, setNo);
     theBuilder.decorateDSetBinding(varId, setNo, bindingNo);
   };
 
@@ -1112,15 +1080,13 @@ bool DeclResultIdMapper::decorateResourceBindings() {
         if (const auto *reg = var.getRegister())
           set = reg->RegisterSpace;
 
-        tryToDecorate(var.getSpirvId(), set, vkCBinding->getBinding(),
-                      var.getCategory(), vkCBinding->getLocation());
+        tryToDecorate(var.getSpirvId(), set, vkCBinding->getBinding());
       }
     } else {
       if (const auto *vkBinding = var.getBinding()) {
         // Process m1
         tryToDecorate(var.getSpirvId(), vkBinding->getSet(),
-                      vkBinding->getBinding(), var.getCategory(),
-                      vkBinding->getLocation());
+                      vkBinding->getBinding());
       }
     }
   }
@@ -1156,12 +1122,10 @@ bool DeclResultIdMapper::decorateResourceBindings() {
           llvm_unreachable("unknown register type found");
         }
 
-        tryToDecorate(var.getSpirvId(), set, binding, var.getCategory(),
-                      reg->Loc);
+        tryToDecorate(var.getSpirvId(), set, binding);
       }
 
   for (const auto &var : resourceVars) {
-    const auto cat = var.getCategory();
     if (var.isCounter()) {
       if (!var.getCounterBinding()) {
         // Process mX * c2
@@ -1172,12 +1136,12 @@ bool DeclResultIdMapper::decorateResourceBindings() {
           set = reg->RegisterSpace;
 
         theBuilder.decorateDSetBinding(var.getSpirvId(), set,
-                                       bindingSet.useNextBinding(set, cat));
+                                       bindingSet.useNextBinding(set));
       }
     } else if (!var.getBinding() && !var.getRegister()) {
       // Process m3
       theBuilder.decorateDSetBinding(var.getSpirvId(), 0,
-                                     bindingSet.useNextBinding(0, cat));
+                                     bindingSet.useNextBinding(0));
     }
   }
 
@@ -1282,11 +1246,9 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
     //   SampleMask, must be an array of integers.
     // * SV_InnerCoverage is an uint value, but the corresponding builtin,
     //   FullyCoveredEXT, must be an boolean value.
-    // * SV_DispatchThreadID and SV_GroupThreadID are allowed to be uint, uint2,
-    //   or uint3, but the corresponding builtins (GlobalInvocationId and
-    //   LocalInvocationId) must be a uint3.
-    // * SV_GroupID is allowed to be uint, uint2, or uint3, but the
-    //   corresponding builtin (WorkgroupId) must be a uint3.
+    // * SV_DispatchThreadID, SV_GroupThreadID, and SV_GroupID are allowed to be
+    //   uint, uint2, or uint3, but the corresponding builtins
+    //   (GlobalInvocationId, LocalInvocationId, WorkgroupId) must be a uint3.
 
     if (glPerVertex.tryToAccess(sigPoint->GetKind(), semanticKind,
                                 semanticToUse->index, invocationId, value,
@@ -1294,6 +1256,7 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
       return true;
 
     const uint32_t srcTypeId = typeId; // Variable type in source code
+    uint32_t srcVecElemTypeId = 0;     // Variable element type if vector
 
     switch (semanticKind) {
     case hlsl::Semantic::Kind::DomainLocation:
@@ -1319,7 +1282,10 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
     case hlsl::Semantic::Kind::DispatchThreadID:
     case hlsl::Semantic::Kind::GroupThreadID:
     case hlsl::Semantic::Kind::GroupID:
-      typeId = theBuilder.getVecType(theBuilder.getUint32Type(), 3);
+      // Keep the original integer signedness
+      srcVecElemTypeId = typeTranslator.translateType(
+          hlsl::IsHLSLVecType(type) ? hlsl::GetHLSLVecElementType(type) : type);
+      typeId = theBuilder.getVecType(srcVecElemTypeId, 3);
       break;
     }
 
@@ -1356,6 +1322,13 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
     // Mark that we have used one index for this semantic
     ++semanticToUse->index;
 
+    // Require extension and capability if using 16-bit types
+    if (typeTranslator.getElementSpirvBitwidth(type) == 16) {
+      theBuilder.addExtension(Extension::KHR_16bit_storage,
+                              "16-bit stage IO variables", decl->getLocation());
+      theBuilder.requireCapability(spv::Capability::StorageInputOutput16);
+    }
+
     // TODO: the following may not be correct?
     if (sigPoint->GetSignatureKind() ==
         hlsl::DXIL::SignatureKind::PatchConstant)
@@ -1458,15 +1431,16 @@ bool DeclResultIdMapper::createStageVars(const hlsl::SigPoint *sigPoint,
                 semanticKind == hlsl::Semantic::Kind::GroupID) &&
                (!hlsl::IsHLSLVecType(type) ||
                 hlsl::GetHLSLVecSize(type) != 3)) {
+        assert(srcVecElemTypeId);
         const auto vecSize =
             hlsl::IsHLSLVecType(type) ? hlsl::GetHLSLVecSize(type) : 1;
         if (vecSize == 1)
-          *value = theBuilder.createCompositeExtract(theBuilder.getUint32Type(),
-                                                     *value, {0});
+          *value =
+              theBuilder.createCompositeExtract(srcVecElemTypeId, *value, {0});
         else if (vecSize == 2)
           *value = theBuilder.createVectorShuffle(
-              theBuilder.getVecType(theBuilder.getUint32Type(), 2), *value,
-              *value, {0, 1});
+              theBuilder.getVecType(srcVecElemTypeId, 2), *value, *value,
+              {0, 1});
       }
     } else {
       if (noWriteBack)

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

@@ -39,7 +39,6 @@ public:
   inline StageVar(const hlsl::SigPoint *sig, llvm::StringRef semaStr,
                   const hlsl::Semantic *sema, llvm::StringRef semaName,
                   uint32_t semaIndex, const VKBuiltInAttr *builtin,
-
                   uint32_t type, uint32_t locCount)
       : sigPoint(sig), semanticStr(semaStr), semantic(sema),
         semanticName(semaName), semanticIndex(semaIndex), builtinAttr(builtin),
@@ -103,27 +102,13 @@ private:
 
 class ResourceVar {
 public:
-  /// The category of this resource.
-  ///
-  /// We only care about Vulkan image and sampler types here, since they can be
-  /// bundled together as a combined image sampler which takes the same binding
-  /// number. The compiler should allow this case.
-  ///
-  /// Numbers are assigned to make bit check easiser.
-  enum class Category : uint32_t {
-    Image = 1,
-    Sampler = 2,
-    Other = 3,
-  };
-
-  ResourceVar(uint32_t id, Category cat, const hlsl::RegisterAssignment *r,
+  ResourceVar(uint32_t id, const hlsl::RegisterAssignment *r,
               const VKBindingAttr *b, const VKCounterBindingAttr *cb,
               bool counter = false)
-      : varId(id), category(cat), reg(r), binding(b), counterBinding(cb),
+      : varId(id), reg(r), binding(b), counterBinding(cb),
         isCounterVar(counter) {}
 
   uint32_t getSpirvId() const { return varId; }
-  Category getCategory() const { return category; }
   const hlsl::RegisterAssignment *getRegister() const { return reg; }
   const VKBindingAttr *getBinding() const { return binding; }
   bool isCounter() const { return isCounterVar; }
@@ -131,7 +116,6 @@ public:
 
 private:
   uint32_t varId;                             ///< <result-id>
-  Category category;                          ///< Resource category
   const hlsl::RegisterAssignment *reg;        ///< HLSL register assignment
   const VKBindingAttr *binding;               ///< Vulkan binding assignment
   const VKCounterBindingAttr *counterBinding; ///< Vulkan counter binding

+ 11 - 17
tools/clang/lib/SPIRV/FeatureManager.cpp

@@ -61,9 +61,6 @@ bool FeatureManager::allowExtension(llvm::StringRef name) {
   }
 
   allowedExtensions.set(static_cast<unsigned>(symbol));
-  if (symbol == Extension::GOOGLE_hlsl_functionality1)
-    allowedExtensions.set(
-        static_cast<unsigned>(Extension::GOOGLE_decorate_string));
 
   return true;
 }
@@ -87,7 +84,8 @@ bool FeatureManager::requestTargetEnv(spv_target_env requestedEnv,
   if (targetEnv == SPV_ENV_VULKAN_1_0 && requestedEnv == SPV_ENV_VULKAN_1_1) {
     emitError("Vulkan 1.1 is required for %0 but not permitted to use", srcLoc)
         << target;
-    emitNote("please specify your target environment via command line option -fspv-target-env=",
+    emitNote("please specify your target environment via command line option "
+             "-fspv-target-env=",
              {});
     return false;
   }
@@ -97,6 +95,7 @@ bool FeatureManager::requestTargetEnv(spv_target_env requestedEnv,
 Extension FeatureManager::getExtensionSymbol(llvm::StringRef name) {
   return llvm::StringSwitch<Extension>(name)
       .Case("KHR", Extension::KHR)
+      .Case("SPV_KHR_16bit_storage", Extension::KHR_16bit_storage)
       .Case("SPV_KHR_device_group", Extension::KHR_device_group)
       .Case("SPV_KHR_multiview", Extension::KHR_multiview)
       .Case("SPV_KHR_shader_draw_parameters",
@@ -109,7 +108,6 @@ Extension FeatureManager::getExtensionSymbol(llvm::StringRef name) {
             Extension::AMD_gpu_shader_half_float)
       .Case("SPV_AMD_shader_explicit_vertex_parameter",
             Extension::AMD_shader_explicit_vertex_parameter)
-      .Case("SPV_GOOGLE_decorate_string", Extension::GOOGLE_decorate_string)
       .Case("SPV_GOOGLE_hlsl_functionality1",
             Extension::GOOGLE_hlsl_functionality1)
       .Default(Extension::Unknown);
@@ -119,6 +117,8 @@ const char *FeatureManager::getExtensionName(Extension symbol) {
   switch (symbol) {
   case Extension::KHR:
     return "KHR";
+  case Extension::KHR_16bit_storage:
+    return "SPV_KHR_16bit_storage";
   case Extension::KHR_device_group:
     return "SPV_KHR_device_group";
   case Extension::KHR_multiview:
@@ -133,8 +133,6 @@ const char *FeatureManager::getExtensionName(Extension symbol) {
     return "SPV_AMD_gpu_shader_half_float";
   case Extension::AMD_shader_explicit_vertex_parameter:
     return "SPV_AMD_shader_explicit_vertex_parameter";
-  case Extension::GOOGLE_decorate_string:
-    return "SPV_GOOGLE_decorate_string";
   case Extension::GOOGLE_hlsl_functionality1:
     return "SPV_GOOGLE_hlsl_functionality1";
   default:
@@ -170,19 +168,15 @@ bool FeatureManager::isExtensionRequiredForTargetEnv(Extension ext) {
   bool required = true;
   if (targetEnv == SPV_ENV_VULKAN_1_1) {
     // The following extensions are incorporated into Vulkan 1.1, and are
-    // therefore not required to be emitted for that target environment. The
-    // last 3 are currently not supported by the FeatureManager.
-    // TODO: Add the last 3 extensions to the list if we start to support them.
-    // SPV_KHR_shader_draw_parameters
-    // SPV_KHR_device_group
-    // SPV_KHR_multiview
-    // SPV_KHR_16bit_storage
-    // SPV_KHR_storage_buffer_storage_class
-    // SPV_KHR_variable_pointers
+    // therefore not required to be emitted for that target environment.
+    // TODO: Also add the following extensions  if we start to support them.
+    // * SPV_KHR_storage_buffer_storage_class
+    // * SPV_KHR_variable_pointers
     switch (ext) {
-    case Extension::KHR_shader_draw_parameters:
+    case Extension::KHR_16bit_storage:
     case Extension::KHR_device_group:
     case Extension::KHR_multiview:
+    case Extension::KHR_shader_draw_parameters:
       required = false;
     }
   }

+ 0 - 1
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -845,7 +845,6 @@ void ModuleBuilder::decorateHlslSemantic(uint32_t targetId,
                                          llvm::StringRef semantic,
                                          llvm::Optional<uint32_t> memberIdx) {
   if (allowReflect) {
-    addExtension(Extension::GOOGLE_decorate_string, "SPIR-V reflection", {});
     addExtension(Extension::GOOGLE_hlsl_functionality1, "SPIR-V reflection",
                  {});
     theModule.addDecoration(

+ 101 - 35
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -510,7 +510,7 @@ spv::Capability getCapabilityForGroupNonUniform(spv::Op opcode) {
   return spv::Capability::Max;
 }
 
-std::string getNamespacePrefix(const Decl* decl) {
+std::string getNamespacePrefix(const Decl *decl) {
   std::string nsPrefix = "";
   const DeclContext *dc = decl->getDeclContext();
   while (dc && !dc->isTranslationUnit()) {
@@ -561,6 +561,16 @@ SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci, EmitSPIRVOptions &options)
               {});
 
   options.Initialize();
+
+  // Set shader module version
+  theBuilder.setShaderModelVersion(shaderModel.GetMajor(),
+                                   shaderModel.GetMinor());
+
+  // Set debug info
+  const auto &inputFiles = ci.getFrontendOpts().Inputs;
+  if (options.enableDebugInfo && !inputFiles.empty())
+    theBuilder.setSourceFileName(theContext.takeNextId(),
+                                 inputFiles.front().getFile().str());
 }
 
 void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
@@ -568,9 +578,6 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
   if (context.getDiagnostics().hasErrorOccurred())
     return;
 
-  theBuilder.setShaderModelVersion(shaderModel.GetMajor(),
-                                   shaderModel.GetMinor());
-
   TranslationUnitDecl *tu = context.getTranslationUnitDecl();
 
   // The entry function is the seed of the queue.
@@ -1950,7 +1957,7 @@ SpirvEvalInfo SPIRVEmitter::processCall(const CallExpr *callExpr) {
   bool isNonStaticMemberCall = false;
   QualType objectType = {};         // Type of the object (if exists)
   SpirvEvalInfo objectEvalInfo = 0; // EvalInfo for the object (if exists)
-  bool objectNeedsTempVar = false;  // Temporary variable for lvalue object
+  bool needsTempVar = false;        // Whether we need temporary variable.
 
   llvm::SmallVector<uint32_t, 4> params;    // Temporary variables
   llvm::SmallVector<SpirvEvalInfo, 4> args; // Evaluated arguments
@@ -1975,17 +1982,11 @@ SpirvEvalInfo SPIRVEmitter::processCall(const CallExpr *callExpr) {
       // If not already a variable, we need to create a temporary variable and
       // pass the object pointer to the function. Example:
       // getObject().objectMethod();
-      bool needsTempVar = objectEvalInfo.isRValue();
-
-      // Try to see if we are calling methods on a global variable, which is put
-      // in the Private storage class. We also need to create temporary variable
-      // for it since the function signature expects all arguments in the
-      // Function storage class.
-      if (!needsTempVar)
-        if (const auto *declRefExpr = dyn_cast<DeclRefExpr>(object))
-          if (const auto *refDecl = declRefExpr->getFoundDecl())
-            if (const auto *varDecl = dyn_cast<VarDecl>(refDecl))
-              needsTempVar = objectNeedsTempVar = varDecl->hasGlobalStorage();
+      // Also, any parameter passed to the member function must be of Function
+      // storage class.
+      needsTempVar =
+          objectEvalInfo.isRValue() ||
+          objectEvalInfo.getStorageClass() != spv::StorageClass::Function;
 
       if (needsTempVar) {
         objectId =
@@ -2041,10 +2042,10 @@ SpirvEvalInfo SPIRVEmitter::processCall(const CallExpr *callExpr) {
   const uint32_t retVal =
       theBuilder.createFunctionCall(retType, funcId, params);
 
-  // If we created a temporary variable for the object this method is invoked
-  // upon, we need to copy the contents in the temporary variable back to the
-  // original object's variable in case there are side effects.
-  if (objectNeedsTempVar) {
+  // If we created a temporary variable for the lvalue object this method is
+  // invoked upon, we need to copy the contents in the temporary variable back
+  // to the original object's variable in case there are side effects.
+  if (needsTempVar && !objectEvalInfo.isRValue()) {
     const uint32_t typeId = typeTranslator.translateType(objectType);
     const uint32_t value = theBuilder.createLoad(typeId, params.front());
     storeValue(objectEvalInfo, value, objectType);
@@ -2938,6 +2939,7 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
   const uint32_t compareVal = isCmp ? doExpr(expr->getArg(2)) : 0;
 
   // Handle offsets (if any).
+  bool needsEmulation = false;
   uint32_t constOffset = 0, varOffset = 0, constOffsets = 0;
   if (numOffsetArgs == 1) {
     // The offset arg is not optional.
@@ -2948,21 +2950,40 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
     const auto offset2 = tryToEvaluateAsConst(expr->getArg(4 + isCmp));
     const auto offset3 = tryToEvaluateAsConst(expr->getArg(5 + isCmp));
 
-    // Make sure we can generate the ConstOffsets image operands in SPIR-V.
-    if (!offset0 || !offset1 || !offset2 || !offset3) {
-      emitError("all offset parameters to '%0' method call must be constants",
-                expr->getExprLoc())
-          << callee->getName() << expr->getSourceRange();
-      return 0;
+    // If any of the offsets is not constant, we then need to emulate the call
+    // using 4 OpImageGather instructions. Otherwise, we can leverage the
+    // ConstOffsets image operand.
+    if (offset0 && offset1 && offset2 && offset3) {
+      const uint32_t v2i32 =
+          theBuilder.getVecType(theBuilder.getInt32Type(), 2);
+      const uint32_t offsetType =
+          theBuilder.getArrayType(v2i32, theBuilder.getConstantUint32(4));
+      constOffsets = theBuilder.getConstantComposite(
+          offsetType, {offset0, offset1, offset2, offset3});
+    } else {
+      needsEmulation = true;
     }
-    const uint32_t v2i32 = theBuilder.getVecType(theBuilder.getInt32Type(), 2);
-    const uint32_t offsetType =
-        theBuilder.getArrayType(v2i32, theBuilder.getConstantUint32(4));
-    constOffsets = theBuilder.getConstantComposite(
-        offsetType, {offset0, offset1, offset2, offset3});
   }
 
   const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
+
+  if (needsEmulation) {
+    const auto elemType = typeTranslator.translateType(
+        hlsl::GetHLSLVecElementType(callee->getReturnType()));
+
+    uint32_t texels[4];
+    for (uint32_t i = 0; i < 4; ++i) {
+      varOffset = doExpr(expr->getArg(2 + isCmp + i));
+      const uint32_t gatherRet = theBuilder.createImageGather(
+          retTypeId, imageTypeId, image, sampler, coordinate,
+          theBuilder.getConstantInt32(component), compareVal, /*constOffset*/ 0,
+          varOffset, /*constOffsets*/ 0, /*sampleNumber*/ 0, status);
+      texels[i] = theBuilder.createCompositeExtract(elemType, gatherRet, {i});
+    }
+    return theBuilder.createCompositeConstruct(
+        retTypeId, {texels[0], texels[1], texels[2], texels[3]});
+  }
+
   return theBuilder.createImageGather(
       retTypeId, imageTypeId, image, sampler, coordinate,
       theBuilder.getConstantInt32(component), compareVal, constOffset,
@@ -4824,6 +4845,45 @@ void SPIRVEmitter::storeValue(const SpirvEvalInfo &lhsPtr,
 uint32_t SPIRVEmitter::reconstructValue(const SpirvEvalInfo &srcVal,
                                         const QualType valType,
                                         LayoutRule dstLR) {
+  // Lambda for casting scalar or vector of bool<-->uint in cases where one side
+  // of the reconstruction (lhs or rhs) has a layout rule.
+  const auto handleBooleanLayout = [this, &srcVal, dstLR](uint32_t val,
+                                                          QualType valType) {
+    // We only need to cast if we have a scalar or vector of booleans.
+    if (!isBoolOrVecOfBoolType(valType))
+      return val;
+
+    LayoutRule srcLR = srcVal.getLayoutRule();
+    // Source value has a layout rule, and has therefore been represented
+    // as a uint. Cast it to boolean before using.
+    bool shouldCastToBool =
+        srcLR != LayoutRule::Void && dstLR == LayoutRule::Void;
+    // Destination has a layout rule, and should therefore be represented
+    // as a uint. Cast to uint before using.
+    bool shouldCastToUint =
+        srcLR == LayoutRule::Void && dstLR != LayoutRule::Void;
+    // No boolean layout issues to take care of.
+    if (!shouldCastToBool && !shouldCastToUint)
+      return val;
+
+    uint32_t vecSize = 1;
+    TypeTranslator::isVectorType(valType, nullptr, &vecSize);
+    QualType boolType =
+        vecSize == 1 ? astContext.BoolTy
+                     : astContext.getExtVectorType(astContext.BoolTy, vecSize);
+    QualType uintType =
+        vecSize == 1
+            ? astContext.UnsignedIntTy
+            : astContext.getExtVectorType(astContext.UnsignedIntTy, vecSize);
+
+    if (shouldCastToBool)
+      return castToBool(val, uintType, boolType);
+    if (shouldCastToUint)
+      return castToInt(val, boolType, uintType, {});
+
+    return val;
+  };
+
   // Lambda for cases where we want to reconstruct an array
   const auto reconstructArray = [this, &srcVal, valType,
                                  dstLR](uint32_t arraySize,
@@ -4834,7 +4894,6 @@ uint32_t SPIRVEmitter::reconstructValue(const SpirvEvalInfo &srcVal,
           typeTranslator.translateType(arrayElemType, srcVal.getLayoutRule());
       const auto subSrcVal =
           theBuilder.createCompositeExtract(subSrcValType, srcVal, {i});
-
       elements.push_back(reconstructValue(srcVal.substResultId(subSrcVal),
                                           arrayElemType, dstLR));
     }
@@ -4868,7 +4927,7 @@ uint32_t SPIRVEmitter::reconstructValue(const SpirvEvalInfo &srcVal,
   // Note: This check should happen before the RecordType check since
   // vector/matrix/resource types are represented as RecordType in the AST.
   if (hlsl::IsHLSLVecMatType(valType) || hlsl::IsHLSLResourceType(valType))
-    return srcVal;
+    return handleBooleanLayout(srcVal, valType);
 
   // Structs
   if (const auto *recordType = valType->getAs<RecordType>()) {
@@ -4888,7 +4947,7 @@ uint32_t SPIRVEmitter::reconstructValue(const SpirvEvalInfo &srcVal,
     return theBuilder.createCompositeConstruct(dstValType, elements);
   }
 
-  return srcVal;
+  return handleBooleanLayout(srcVal, valType);
 }
 
 SpirvEvalInfo SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
@@ -6509,7 +6568,14 @@ SPIRVEmitter::processIntrinsicInterlockedMethod(const CallExpr *expr,
     if (isBufferTextureIndexing(callExpr, &base, &index)) {
       const auto ptrType =
           theBuilder.getPointerType(baseTypeId, spv::StorageClass::Image);
-      const auto baseId = doExpr(base);
+      auto baseId = doExpr(base);
+      if (baseId.isRValue()) {
+        // OpImageTexelPointer's Image argument must have a type of
+        // OpTypePointer with Type OpTypeImage. Need to create a temporary
+        // variable if the baseId is an rvalue.
+        baseId = createTemporaryVar(
+            base->getType(), TypeTranslator::getName(base->getType()), baseId);
+      }
       const auto coordId = doExpr(index);
       ptr = theBuilder.createImageTexelPointer(ptrType, baseId, coordId, zero);
     }

+ 9 - 2
tools/clang/lib/SPIRV/Structure.cpp

@@ -282,11 +282,18 @@ void SPIRVModule::take(InstBuilder *builder) {
     consumer(inst.take());
   }
 
-  if (shaderModelVersion != 0)
+  if (shaderModelVersion != 0) {
+    llvm::Optional<uint32_t> fileName = llvm::None;
+    if (!sourceFileName.empty() && sourceFileNameId) {
+      builder->opString(sourceFileNameId, sourceFileName).x();
+      fileName = sourceFileNameId;
+    }
+
     builder
-        ->opSource(spv::SourceLanguage::HLSL, shaderModelVersion, llvm::None,
+        ->opSource(spv::SourceLanguage::HLSL, shaderModelVersion, fileName,
                    llvm::None)
         .x();
+  }
 
   // BasicBlock debug names should be emitted only for blocks that are
   // reachable.

+ 52 - 19
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -395,12 +395,34 @@ uint32_t TypeTranslator::getElementSpirvBitwidth(QualType type) {
       return getElementSpirvBitwidth(elemType);
   }
 
+  // Matrix types
+  if (hlsl::IsHLSLMatType(type))
+    return getElementSpirvBitwidth(hlsl::GetHLSLMatElementType(type));
+
+  // Array types
+  if (const auto *arrayType = type->getAsArrayTypeUnsafe()) {
+    return getElementSpirvBitwidth(arrayType->getElementType());
+  }
+
+  // Typedefs
+  if (const auto *typedefType = type->getAs<TypedefType>())
+    return getLocationCount(typedefType->desugar());
+
+  // Reference types
+  if (const auto *refType = type->getAs<ReferenceType>())
+    return getLocationCount(refType->getPointeeType());
+
+  // Pointer types
+  if (const auto *ptrType = type->getAs<PointerType>())
+    return getLocationCount(ptrType->getPointeeType());
+
   // Scalar types
   QualType ty = {};
   const bool isScalar = isScalarType(type, &ty);
   assert(isScalar);
   if (const auto *builtinType = ty->getAs<BuiltinType>()) {
     switch (builtinType->getKind()) {
+    case BuiltinType::Bool:
     case BuiltinType::Int:
     case BuiltinType::UInt:
     case BuiltinType::Float:
@@ -1133,41 +1155,52 @@ TypeTranslator::getCapabilityForStorageImageReadWrite(QualType type) {
 
 bool TypeTranslator::shouldSkipInStructLayout(const Decl *decl) {
   // Ignore implicit generated struct declarations/constructors/destructors
+  if (decl->isImplicit())
+    return true;
   // Ignore embedded type decls
+  if (isa<TypeDecl>(decl))
+    return true;
   // Ignore embeded function decls
+  if (isa<FunctionDecl>(decl))
+    return true;
   // Ignore empty decls
-  if (decl->isImplicit() || isa<TypeDecl>(decl) || isa<FunctionDecl>(decl) ||
-      isa<EmptyDecl>(decl))
+  if (isa<EmptyDecl>(decl))
     return true;
 
-  // For $Globals (whose "struct" is the TranslationUnit)
-  // Ignore resources in the TranslationUnit "struct"
-
   // For the $Globals cbuffer, we only care about externally-visiable
   // non-resource-type variables. The rest should be filtered out.
 
+  const auto *declContext = decl->getDeclContext();
+
   // Special check for ConstantBuffer/TextureBuffer, whose DeclContext is a
   // HLSLBufferDecl. So that we need to check the HLSLBufferDecl's parent decl
   // to check whether this is a ConstantBuffer/TextureBuffer defined in the
   // global namespace.
+  // Note that we should not be seeing ConstantBuffer/TextureBuffer for normal
+  // cbuffer/tbuffer or push constant blocks. So this case should only happen
+  // for $Globals cbuffer.
   if (isConstantTextureBuffer(decl) &&
-      decl->getDeclContext()->getLexicalParent()->isTranslationUnit())
+      declContext->getLexicalParent()->isTranslationUnit())
     return true;
 
-  // External visibility
-  if (const auto *declDecl = dyn_cast<DeclaratorDecl>(decl))
-    if (!declDecl->hasExternalFormalLinkage())
-      return true;
-
-  // cbuffer/tbuffer
-  if (isa<HLSLBufferDecl>(decl))
-    return true;
+  // $Globals' "struct" is the TranslationUnit, so we should ignore resources
+  // in the TranslationUnit "struct" and its child namespaces.
+  if (declContext->isTranslationUnit() || declContext->isNamespace()) {
+    // External visibility
+    if (const auto *declDecl = dyn_cast<DeclaratorDecl>(decl))
+      if (!declDecl->hasExternalFormalLinkage())
+        return true;
 
-  // Other resource types
-  if (const auto *valueDecl = dyn_cast<ValueDecl>(decl))
-    if (isResourceType(valueDecl))
+    // cbuffer/tbuffer
+    if (isa<HLSLBufferDecl>(decl))
       return true;
 
+    // Other resource types
+    if (const auto *valueDecl = dyn_cast<ValueDecl>(decl))
+      if (isResourceType(valueDecl))
+        return true;
+  }
+
   return false;
 }
 
@@ -1281,10 +1314,10 @@ void TypeTranslator::collectDeclsInField(
     return;
   }
 
-  (*decls).push_back(field);
+  decls->push_back(field);
 }
 
-const llvm::SmallVector<const Decl *, 4>
+llvm::SmallVector<const Decl *, 4>
 TypeTranslator::collectDeclsInDeclContext(const DeclContext *declContext) {
   llvm::SmallVector<const Decl *, 4> decls;
   for (const auto *field : declContext->decls()) {

+ 4 - 4
tools/clang/lib/SPIRV/TypeTranslator.h

@@ -140,9 +140,9 @@ public:
   uint32_t getTypeWithCustomBitwidth(QualType type, uint32_t bitwidth);
 
   /// \brief Returns the realized bitwidth of the given type when represented in
-  /// SPIR-V. Panics if the given type is not a scalar or vector of float or
-  /// integer. In case of vectors, it returns the realized SPIR-V bitwidth of
-  /// the vector elements.
+  /// SPIR-V. Panics if the given type is not a scalar, a vector/matrix of float
+  /// or integer, or an array of them. In case of vectors, it returns the
+  /// realized SPIR-V bitwidth of the vector elements.
   uint32_t getElementSpirvBitwidth(QualType type);
 
   /// \brief Returns true if the given type will be translated into a SPIR-V
@@ -274,7 +274,7 @@ public:
   /// DeclContext. If it sees a NamespaceDecl, it recursively dives in and
   /// collects decls in the correct order.
   /// Utilizes collectDeclsInNamespace and collectDeclsInField private methods.
-  const llvm::SmallVector<const Decl *, 4>
+  llvm::SmallVector<const Decl *, 4>
   collectDeclsInDeclContext(const DeclContext *declContext);
 
 private:

+ 13 - 0
tools/clang/test/CodeGenHLSL/quick-test/atan.hlsl

@@ -0,0 +1,13 @@
+// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s
+
+
+// Make sure there're select for atan2.
+// CHECK: Atan
+// CHECK: select
+// CHECK: select
+// CHECK: select
+// CHECK: select
+
+float main(float2 a : A) : SV_Target {
+  return atan2(a.y, a.x);
+}

+ 2 - 1
tools/clang/test/CodeGenHLSL/quick-test/convergent.hlsl

@@ -3,7 +3,8 @@
 // Make sure add is not sink into if.
 // CHECK: fadd
 // CHECK: fadd
-// CHECK: if.then
+// CHECK: fcmp
+// CHECK-NEXT: br
 
 Texture2D<float4> tex;
 SamplerState s;

+ 10 - 0
tools/clang/test/CodeGenSPIRV/intrinsics.interlocked-methods.cs.hlsl

@@ -3,6 +3,11 @@
 groupshared int dest_i;
 groupshared uint dest_u;
 
+RWBuffer<uint> buff;
+RWBuffer<uint> getDest() {
+  return buff;
+}
+
 void main()
 {
   uint original_u_val;
@@ -21,6 +26,11 @@ void main()
 // CHECK-NEXT:                   OpStore %original_i_val [[iadd27]]
   InterlockedAdd(dest_i, val1, original_i_val);
 
+// CHECK:      [[buff:%\d+]] = OpFunctionCall %type_buffer_image %getDest
+// CHECK-NEXT: OpStore %temp_var_RWBuffer [[buff]]
+// CHECK-NEXT: OpImageTexelPointer %_ptr_Image_uint %temp_var_RWBuffer %uint_0 %uint_0
+  InterlockedAdd(getDest()[0], val1, original_i_val);
+
 // CHECK:      [[and28:%\d+]] = OpAtomicAnd %uint %dest_u %uint_1 %uint_0 %uint_10
 // CHECK-NEXT:                  OpStore %original_u_val [[and28]]
   InterlockedAnd(dest_u, 10,  original_u_val);

+ 21 - 3
tools/clang/test/CodeGenSPIRV/oo.struct.method.hlsl

@@ -59,6 +59,12 @@ T foo() {
   return t;
 }
 
+struct R {
+  int a;
+  void incr() { ++a; }
+};
+RWStructuredBuffer<R> rwsb;
+
 // CHECK:     [[ft_f32:%\d+]] = OpTypeFunction %float
 // CHECK:       [[ft_S:%\d+]] = OpTypeFunction %float %_ptr_Function_S
 // CHECK:   [[ft_S_f32:%\d+]] = OpTypeFunction %float %_ptr_Function_S %_ptr_Function_float
@@ -98,6 +104,18 @@ float main() : A {
 // CHECK-NEXT:        {{%\d+}} = OpFunctionCall %float %S_fn_ref %temp_var_S
   float f2 = foo().get_S().fn_ref();
 
+// CHECK:         [[uniformPtr:%\d+]] = OpAccessChain %_ptr_Uniform_R %rwsb %int_0 %uint_0
+// CHECK-NEXT:   [[originalObj:%\d+]] = OpLoad %R [[uniformPtr]]
+// CHECK-NEXT:        [[member:%\d+]] = OpCompositeExtract %int [[originalObj]] 0
+// CHECK-NEXT:       [[tempVar:%\d+]] = OpCompositeConstruct %R_0 [[member]]
+// CHECK-NEXT:                          OpStore %temp_var_R [[tempVar]]
+// CHECK-NEXT:                          OpFunctionCall %void %R_incr %temp_var_R
+// CHECK-NEXT:       [[tempVar:%\d+]] = OpLoad %R_0 %temp_var_R
+// CHECK-NEXT: [[tempVarMember:%\d+]] = OpCompositeExtract %int [[tempVar]] 0
+// CHECK-NEXT:          [[newR:%\d+]] = OpCompositeConstruct %R [[tempVarMember]]
+// CHECK-NEXT:                          OpStore [[uniformPtr]] [[newR]]
+  rwsb[0].incr();
+
   return f1;
 // CHECK:                     OpFunctionEnd
 }
@@ -151,8 +169,8 @@ float main() : A {
 // CHECK:                      OpFunctionEnd
 
 // CHECK:        %S_fn_param = OpFunction %float None [[ft_S_f32]]
-// CHECK-NEXT: %param_this_4 = OpFunctionParameter %_ptr_Function_S
+// CHECK-NEXT: %param_this_5 = OpFunctionParameter %_ptr_Function_S
 // CHECK-NEXT:          %c_0 = OpFunctionParameter %_ptr_Function_float
-// CHECK-NEXT:   %bb_entry_8 = OpLabel
-// CHECK:           {{%\d+}} = OpAccessChain %_ptr_Function_float %param_this_4 %int_0
+// CHECK-NEXT:                 OpLabel
+// CHECK:                      OpAccessChain %_ptr_Function_float %param_this_5 %int_0
 // CHECK:                      OpFunctionEnd

+ 13 - 0
tools/clang/test/CodeGenSPIRV/semantic.dispatch-thread-id.int2.cs.hlsl

@@ -0,0 +1,13 @@
+// Run: %dxc -T cs_6_0 -E main
+
+// CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+// CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+// CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3int Input
+
+// CHECK:                  %param_var_tid = OpVariable %_ptr_Function_v2int Function
+// CHECK:  [[gl_GlobalInvocationID:%\d+]] = OpLoad %v3int %gl_GlobalInvocationID
+// CHECK:  [[int2_DispatchThreadID:%\d+]] = OpVectorShuffle %v2int [[gl_GlobalInvocationID]] [[gl_GlobalInvocationID]] 0 1
+// CHECK:                                   OpStore %param_var_tid [[int2_DispatchThreadID]]
+
+[numthreads(8, 8, 8)]
+void main(int2 tid : SV_DispatchThreadId) {}

+ 0 - 12
tools/clang/test/CodeGenSPIRV/semantic.dispatch-thread-id.uint2.cs.hlsl

@@ -1,12 +0,0 @@
-// Run: %dxc -T cs_6_0 -E main
-
-// CHECK: OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
-// CHECK: OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
-// CHECK: %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
-
-// CHECK:  [[gl_GlobalInvocationID:%\d+]] = OpLoad %v3uint %gl_GlobalInvocationID
-// CHECK: [[uint2_DispatchThreadID:%\d+]] = OpVectorShuffle %v2uint [[gl_GlobalInvocationID]] [[gl_GlobalInvocationID]] 0 1
-// CHECK:                                   OpStore %param_var_tid [[uint2_DispatchThreadID]]
-
-[numthreads(8, 8, 8)]
-void main(uint2 tid : SV_DispatchThreadId) {}

+ 13 - 0
tools/clang/test/CodeGenSPIRV/semantic.group-id.int2.cs.hlsl

@@ -0,0 +1,13 @@
+// Run: %dxc -T cs_6_0 -E main
+
+// CHECK: OpEntryPoint GLCompute %main "main" %gl_WorkGroupID
+// CHECK: OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId
+// CHECK: %gl_WorkGroupID = OpVariable %_ptr_Input_v3int Input
+
+// CHECK:         %param_var_tid = OpVariable %_ptr_Function_v2int Function
+// CHECK: [[gl_WorkGrouID:%\d+]] = OpLoad %v3int %gl_WorkGroupID
+// CHECK:  [[int2_GroupID:%\d+]] = OpVectorShuffle %v2int [[gl_WorkGrouID]] [[gl_WorkGrouID]] 0 1
+// CHECK:                          OpStore %param_var_tid [[int2_GroupID]]
+
+[numthreads(8, 8, 8)]
+void main(int2 tid : SV_GroupID) {}

+ 0 - 12
tools/clang/test/CodeGenSPIRV/semantic.group-id.uint2.cs.hlsl

@@ -1,12 +0,0 @@
-// Run: %dxc -T cs_6_0 -E main
-
-// CHECK: OpEntryPoint GLCompute %main "main" %gl_WorkGroupID
-// CHECK: OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId
-// CHECK: %gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input
-
-// CHECK: [[gl_WorkGrouID:%\d+]] = OpLoad %v3uint %gl_WorkGroupID
-// CHECK: [[uint2_GroupID:%\d+]] = OpVectorShuffle %v2uint [[gl_WorkGrouID]] [[gl_WorkGrouID]] 0 1
-// CHECK:                          OpStore %param_var_tid [[uint2_GroupID]]
-
-[numthreads(8, 8, 8)]
-void main(uint2 tid : SV_GroupID) {}

+ 13 - 0
tools/clang/test/CodeGenSPIRV/semantic.group-thread-id.int2.cs.hlsl

@@ -0,0 +1,13 @@
+// Run: %dxc -T cs_6_0 -E main
+
+// CHECK: OpEntryPoint GLCompute %main "main" %gl_LocalInvocationID
+// CHECK: OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
+// CHECK: %gl_LocalInvocationID = OpVariable %_ptr_Input_v3int Input
+
+// CHECK:               %param_var_gtid = OpVariable %_ptr_Function_v2int Function
+// CHECK: [[gl_LocalInvocationID:%\d+]] = OpLoad %v3int %gl_LocalInvocationID
+// CHECK:   [[int2_GroupThreadID:%\d+]] = OpVectorShuffle %v2int [[gl_LocalInvocationID]] [[gl_LocalInvocationID]] 0 1
+// CHECK:                                 OpStore %param_var_gtid [[int2_GroupThreadID]]
+
+[numthreads(8, 8, 8)]
+void main(int2 gtid : SV_GroupThreadID) {}

+ 0 - 12
tools/clang/test/CodeGenSPIRV/semantic.group-thread-id.uint2.cs.hlsl

@@ -1,12 +0,0 @@
-// Run: %dxc -T cs_6_0 -E main
-
-// CHECK: OpEntryPoint GLCompute %main "main" %gl_LocalInvocationID
-// CHECK: OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
-// CHECK: %gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
-
-// CHECK: [[gl_LocalInvocationID:%\d+]] = OpLoad %v3uint %gl_LocalInvocationID
-// CHECK:  [[uint2_GroupThreadID:%\d+]] = OpVectorShuffle %v2uint [[gl_LocalInvocationID]] [[gl_LocalInvocationID]] 0 1
-// CHECK:                                 OpStore %param_var_gtid [[uint2_GroupThreadID]]
-
-[numthreads(8, 8, 8)]
-void main(uint2 gtid : SV_GroupThreadID) {}

+ 9 - 0
tools/clang/test/CodeGenSPIRV/spirv.debug.opsource.hlsl

@@ -0,0 +1,9 @@
+// Run: %dxc -T cs_6_1 -E main -Zi
+
+// CHECK:      [[str:%\d+]] = OpString
+// CHECK-SAME: spirv.debug.opsource.hlsl
+// CHECK:      OpSource HLSL 610 [[str]]
+
+[numthreads(8, 1, 1)]
+void main() {
+}

+ 0 - 1
tools/clang/test/CodeGenSPIRV/spirv.interface.ds.hlsl

@@ -4,7 +4,6 @@
 // CHECK: OpCapability CullDistance
 // CHECK: OpCapability Tessellation
 
-// CHECK: OpExtension "SPV_GOOGLE_decorate_string"
 // CHECK: OpExtension "SPV_GOOGLE_hlsl_functionality1"
 
 // HS PCF output

+ 0 - 1
tools/clang/test/CodeGenSPIRV/spirv.interface.gs.hlsl

@@ -4,7 +4,6 @@
 // CHECK: OpCapability CullDistance
 // CHECK: OpCapability Geometry
 
-// CHECK: OpExtension "SPV_GOOGLE_decorate_string"
 // CHECK: OpExtension "SPV_GOOGLE_hlsl_functionality1"
 
 struct GsPerVertexIn {

+ 0 - 1
tools/clang/test/CodeGenSPIRV/spirv.interface.hs.hlsl

@@ -6,7 +6,6 @@
 // CHECK: OpCapability CullDistance
 // CHECK: OpCapability Tessellation
 
-// CHECK: OpExtension "SPV_GOOGLE_decorate_string"
 // CHECK: OpExtension "SPV_GOOGLE_hlsl_functionality1"
 
 // Input control point

+ 0 - 1
tools/clang/test/CodeGenSPIRV/spirv.interface.ps.hlsl

@@ -3,7 +3,6 @@
 // CHECK: OpCapability ClipDistance
 // CHECK: OpCapability CullDistance
 
-// CHECK: OpExtension "SPV_GOOGLE_decorate_string"
 // CHECK: OpExtension "SPV_GOOGLE_hlsl_functionality1"
 
 struct Inner {

+ 0 - 1
tools/clang/test/CodeGenSPIRV/spirv.interface.vs.hlsl

@@ -3,7 +3,6 @@
 // CHECK: OpCapability ClipDistance
 // CHECK: OpCapability CullDistance
 
-// CHECK: OpExtension "SPV_GOOGLE_decorate_string"
 // CHECK: OpExtension "SPV_GOOGLE_hlsl_functionality1"
 
 // CHECK: OpEntryPoint Vertex %main "main" %gl_PerVertexOut %in_var_TEXCOORD %in_var_SV_Position %in_var_SV_ClipDistance %in_var_SV_CullDistance0 %out_var_COLOR %out_var_TEXCOORD

+ 53 - 0
tools/clang/test/CodeGenSPIRV/spirv.stage-io.16bit.hlsl

@@ -0,0 +1,53 @@
+// Run: %dxc -T vs_6_2 -E main -enable-16bit-types
+
+// CHECK: OpCapability StorageInputOutput16
+
+// CHECK: OpExtension "SPV_KHR_16bit_storage"
+
+// CHECK: OpDecorate %in_var_A Location 0
+// CHECK: OpDecorate %in_var_B Location 4
+// CHECK: OpDecorate %in_var_C Location 6
+// CHECK: OpDecorate %in_var_D Location 7
+// CHECK: OpDecorate %in_var_E Location 8
+
+// CHECK: OpDecorate %out_var_A Location 0
+// CHECK: OpDecorate %out_var_B Location 2
+// CHECK: OpDecorate %out_var_C Location 6
+// CHECK: OpDecorate %out_var_D Location 7
+// CHECK: OpDecorate %out_var_E Location 8
+
+// CHECK:  %in_var_A = OpVariable %_ptr_Input__arr_v2half_uint_4 Input
+// CHECK:  %in_var_B = OpVariable %_ptr_Input__arr_v3ushort_uint_2 Input
+// CHECK:  %in_var_C = OpVariable %_ptr_Input_short Input
+// CHECK:  %in_var_D = OpVariable %_ptr_Input_v2ushort Input
+// CHECK:  %in_var_E = OpVariable %_ptr_Input_mat3v2half Input
+
+// CHECK: %out_var_A = OpVariable %_ptr_Output_mat2v3half Output
+// CHECK: %out_var_B = OpVariable %_ptr_Output__arr_v2short_uint_4 Output
+// CHECK: %out_var_C = OpVariable %_ptr_Output_half Output
+// CHECK: %out_var_D = OpVariable %_ptr_Output_v2short Output
+// CHECK: %out_var_E = OpVariable %_ptr_Output_v3ushort Output
+
+struct VSOut {
+    half2x3   outA    : A; // 2 locations: 0, 1
+    int16_t2  outB[4] : B; // 4 locations: 2, 3, 4, 5
+    half      outC    : C; // 1 location : 6
+    int16_t2  outD    : D; // 1 location : 7
+    uint16_t3 outE    : E; // 1 location : 8
+};
+
+VSOut main(
+    half2        inA[4] : A, // 4 locations: 0, 1, 2, 3
+    uint16_t2x3  inB    : B, // 2 locations: 4, 5
+    int16_t      inC    : C, // 1 location : 6
+    uint16_t2    inD    : D, // 1 location : 7
+    float16_t3x2 inE    : E  // 3 location : 8, 9, 10
+) {
+    VSOut o;
+    o.outA    = inA[0].x;
+    o.outB[0] = inB[0][0];
+    o.outC    = inC.x;
+    o.outD    = inD[0];
+    o.outE    = inE[0][0];
+    return o;
+}

+ 14 - 1
tools/clang/test/CodeGenSPIRV/texture.array.gather-red.hlsl

@@ -21,7 +21,7 @@ TextureCubeArray<int4> tCubeArray : register(t3);
 
 // CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4int
 
-float4 main(float3 location: A) : SV_Target {
+float4 main(float3 location: A, int2 offset : B) : SV_Target {
 // CHECK:            [[t2f4:%\d+]] = OpLoad %type_2d_image_array %t2f4
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:        [[loc:%\d+]] = OpLoad %v3float %location
@@ -85,5 +85,18 @@ float4 main(float3 location: A) : SV_Target {
 // CHECK-NEXT:                         OpStore %g [[result]]
     int4 g = tCubeArray.GatherRed(gSampler, /*location*/ float4(1.5, 1.5, 1.5, 1.5), status);
 
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel0:%\d+]] = OpCompositeExtract %float [[gather]] 0
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c34]]
+// CHECK: [[texel1:%\d+]] = OpCompositeExtract %float [[gather]] 1
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c56]]
+// CHECK: [[texel2:%\d+]] = OpCompositeExtract %float [[gather]] 2
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel3:%\d+]] = OpCompositeExtract %float [[gather]] 3
+// CHECK:                   OpCompositeConstruct %v4float [[texel0]] [[texel1]] [[texel2]] [[texel3]]
+    float4 h = t2f4.GatherRed(gSampler, location, offset, int2(3, 4), int2(5, 6), offset);
+
     return 1.0;
 }

+ 14 - 1
tools/clang/test/CodeGenSPIRV/texture.gather-red.hlsl

@@ -22,7 +22,7 @@ TextureCube<int4> tCube : register(t3);
 
 // CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4int
 
-float4 main(float2 location: A) : SV_Target {
+float4 main(float2 location: A, int2 offset : B) : SV_Target {
 // CHECK:            [[t2f4:%\d+]] = OpLoad %type_2d_image %t2f4
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:        [[loc:%\d+]] = OpLoad %v2float %location
@@ -86,5 +86,18 @@ float4 main(float2 location: A) : SV_Target {
 // CHECK-NEXT:                        OpStore %g [[result]]
     int4 g = tCube.GatherRed(gSampler, /*location*/ float3(1.5, 1.5, 1.5), status);
 
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c12]]
+// CHECK: [[texel0:%\d+]] = OpCompositeExtract %float [[gather]] 0
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel1:%\d+]] = OpCompositeExtract %float [[gather]] 1
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel2:%\d+]] = OpCompositeExtract %float [[gather]] 2
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c78]]
+// CHECK: [[texel3:%\d+]] = OpCompositeExtract %float [[gather]] 3
+// CHECK:                   OpCompositeConstruct %v4float [[texel0]] [[texel1]] [[texel2]] [[texel3]]
+    float4 h = t2f4.GatherRed(gSampler, location, int2(1, 2), offset, offset, int2(7, 8));
+
     return 1.0;
 }

+ 3 - 3
tools/clang/test/CodeGenSPIRV/type.struct.hlsl

@@ -57,13 +57,13 @@ void main() {
   S s;
   T t;
 
-// CHECK: %_ptr_Function__struct_[[num]] = OpTypePointer Function %_struct_[[num]]
+// CHECK: %R = OpTypeStruct %v2float
 
-// CHECK: %r0 = OpVariable %_ptr_Function__struct_[[num]] Function
+// CHECK: %r0 = OpVariable %_ptr_Function_R Function
   struct R {
     float2 rVal;
   } r0;
 
-// CHECK: %r1 = OpVariable %_ptr_Function__struct_[[num]] Function
+// CHECK: %r1 = OpVariable %_ptr_Function_R Function
   R r1;
 }

+ 53 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.cl.all-sets.hlsl

@@ -0,0 +1,53 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-b-shift 1000 all -fvk-t-shift 2000 all -fvk-s-shift 3000 all -fvk-u-shift 4000 all
+
+struct S {
+    float4 f;
+};
+
+// Explicit binding assignment is unaffected.
+
+// CHECK: OpDecorate %cbuffer3 DescriptorSet 0
+// CHECK: OpDecorate %cbuffer3 Binding 42
+[[vk::binding(42)]]
+ConstantBuffer<S> cbuffer3 : register(b10, space2);
+
+// CHECK: OpDecorate %cbuffer1 DescriptorSet 0
+// CHECK: OpDecorate %cbuffer1 Binding 1000
+ConstantBuffer<S> cbuffer1 : register(b0);
+// CHECK: OpDecorate %cbuffer2 DescriptorSet 2
+// CHECK: OpDecorate %cbuffer2 Binding 1000
+ConstantBuffer<S> cbuffer2 : register(b0, space2);
+
+// CHECK: OpDecorate %texture1 DescriptorSet 1
+// CHECK: OpDecorate %texture1 Binding 2001
+Texture2D<float4> texture1: register(t1, space1);
+// CHECK: OpDecorate %texture2 DescriptorSet 0
+// CHECK: OpDecorate %texture2 Binding 2001
+Texture2D<float4> texture2: register(t1);
+
+// CHECK: OpDecorate %sampler1 DescriptorSet 0
+// CHECK: OpDecorate %sampler1 Binding 3000
+// CHECK: OpDecorate %sampler2 DescriptorSet 2
+// CHECK: OpDecorate %sampler2 Binding 3000
+SamplerState sampler1: register(s0);
+SamplerState sampler2: register(s0, space2);
+
+// CHECK: OpDecorate %rwbuffer1 DescriptorSet 3
+// CHECK: OpDecorate %rwbuffer1 Binding 4003
+RWBuffer<float4> rwbuffer1 : register(u3, space3);
+// CHECK: OpDecorate %rwbuffer2 DescriptorSet 0
+// CHECK: OpDecorate %rwbuffer2 Binding 4003
+RWBuffer<float4> rwbuffer2 : register(u3);
+
+// Lacking binding assignment is unaffacted.
+
+// CHECK: OpDecorate %cbuffer4 DescriptorSet 0
+// CHECK: OpDecorate %cbuffer4 Binding 0
+ConstantBuffer<S> cbuffer4;
+// CHECK: OpDecorate %cbuffer5 DescriptorSet 0
+// CHECK: OpDecorate %cbuffer5 Binding 1
+ConstantBuffer<S> cbuffer5;
+
+float4 main() : SV_Target {
+    return cbuffer1.f;
+}

+ 0 - 30
tools/clang/test/CodeGenSPIRV/vk.binding.cl.error.hlsl

@@ -1,30 +0,0 @@
-// Run: %dxc -T ps_6_0 -E main -fvk-b-shift 2 0 -fvk-t-shift 2 0 -fvk-s-shift 3 0 -fvk-u-shift 3 0
-
-struct S {
-    float4 f;
-};
-
-[[vk::binding(2)]]
-ConstantBuffer<S> cbuffer3;
-
-ConstantBuffer<S> cbuffer1 : register(b0); // Collision with cbuffer3 after shift
-
-Texture2D<float4> texture1: register(t0, space1);
-Texture2D<float4> texture2: register(t0); // Collision with cbuffer3 after shift
-
-SamplerState sampler1: register(s0);
-SamplerState sampler2: register(s0, space2);
-
-RWBuffer<float4> rwbuffer1 : register(u0, space3);
-RWBuffer<float4> rwbuffer2 : register(u0); // Collision with sampler1 after shift
-
-float4 main() : SV_Target {
-    return cbuffer1.f;
-}
-
-//CHECK: :10:30: warning: resource binding #2 in descriptor set #0 already assigned
-//CHECK:   :7:3: note: binding number previously assigned here
-
-//CHECK: :13:29: warning: resource binding #2 in descriptor set #0 already assigned
-
-//CHECK: :19:30: warning: resource binding #3 in descriptor set #0 already assigned

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

@@ -1,42 +0,0 @@
-// Run: %dxc -T ps_6_0 -E main
-
-[[vk::binding(1)]]
-SamplerState sampler1      : register(s1, space1);
-
-[[vk::binding(3, 1)]]
-SamplerState sampler2      : register(s2);
-
-[[vk::binding(1)]] // reuse - allowed for combined image sampler
-Texture2D<float4> texture1;
-
-[[vk::binding(3, 1)]] // reuse - allowed for combined image sampler
-Texture3D<float4> texture2 : register(t0, space0);
-
-[[vk::binding(3, 1)]] // reuse - disallowed
-Texture3D<float4> texture3 : register(t0, space0);
-
-[[vk::binding(1)]] // reuse - disallowed
-SamplerState sampler3      : register(s1, space1);
-
-struct S { float f; };
-
-[[vk::binding(5)]]
-StructuredBuffer<S> buf1;
-
-[[vk::binding(5)]] // reuse - disallowed
-SamplerState sampler4;
-
-[[vk::binding(5)]] // reuse - disallowed
-Texture2D<float4> texture4;
-
-float4 main() : SV_Target {
-    return 1.0;
-}
-
-// CHECK-NOT:  :9:{{%\d+}}: warning: resource binding #1 in descriptor set #0 already assigned
-// CHECK-NOT: :12:{{%\d+}}: warning: resource binding #3 in descriptor set #1 already assigned
-// CHECK: :15:3: warning: resource binding #3 in descriptor set #1 already assigned
-// CHECK: :12:3: note: binding number previously assigned here
-// CHECK: :18:3: warning: resource binding #1 in descriptor set #0 already assigned
-// CHECK: :26:3: warning: resource binding #5 in descriptor set #0 already assigned
-// CHECK: :29:3: warning: resource binding #5 in descriptor set #0 already assigned

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

@@ -1,29 +0,0 @@
-// Run: %dxc -T ps_6_0 -E main
-
-struct S {
-    float4 f;
-};
-
-ConstantBuffer<S>     myCbuffer1 : register(b0);
-ConstantBuffer<S>     myCbuffer2 : register(b0, space1);
-
-RWStructuredBuffer<S> mySBuffer1 : register(u0);         // reuse - disallowed
-RWStructuredBuffer<S> mySBuffer2 : register(u0, space1); // reuse - disallowed
-RWStructuredBuffer<S> mySBuffer3 : register(u0, space2);
-
-SamplerState mySampler1 : register(s5, space1);
-Texture2D    myTexture1 : register(t5, space1); // reuse - allowed
-
-Texture2D    myTexture2 : register(t6, space6);
-[[vk::binding(6, 6)]] // reuse - allowed
-SamplerState mySampler2;
-
-float4 main() : SV_Target {
-    return 1.0;
-}
-
-// CHECK: :10:36: warning: resource binding #0 in descriptor set #0 already assigned
-// CHECK:  :7:36: note: binding number previously assigned here
-// CHECK: :11:36: warning: resource binding #0 in descriptor set #1 already assigned
-// CHECK-NOT: :15:{{%\d+}}: warning: resource binding #5 in descriptor set #1 already assigned
-// CHECK-NOT: :18:{{%\d+}}: warning: resource binding #6 in descriptor set #6 already assigned

+ 41 - 1
tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.boolean.hlsl

@@ -7,12 +7,18 @@
 // CHECK: OpMemberDecorate %type_CONSTANTS 0 Offset 0
 // CHECK: OpDecorate %type_CONSTANTS Block
 
-// CHECK: %FrameConstants = OpTypeStruct %uint %v3uint %_arr_v3uint_uint_2
+// CHECK: %T = OpTypeStruct %_arr_uint_uint_1
+struct T {
+  bool boolArray[1];
+};
+
+// CHECK: %FrameConstants = OpTypeStruct %uint %v3uint %_arr_v3uint_uint_2 %T
 struct FrameConstants
 {
   bool  boolScalar;
   bool3 boolVec;
   row_major bool2x3 boolMat;
+  T t;
 };
 
 [[vk::binding(0, 0)]]
@@ -23,6 +29,11 @@ cbuffer CONSTANTS
 
 // CHECK: [[v3uint0:%\d+]] = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
 // CHECK: [[v2uint0:%\d+]] = OpConstantComposite %v2uint %uint_0 %uint_0
+
+// These are the types that hold SPIR-V booleans, rather than Uints.
+// CHECK:              %T_0 = OpTypeStruct %_arr_bool_uint_1
+// CHECK: %FrameConstants_0 = OpTypeStruct %bool %v3bool %_arr_v3bool_uint_2 %T_0
+
 float4 main(in float4 texcoords : TEXCOORD0) : SV_TARGET
 {
 // CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
@@ -118,5 +129,34 @@ float4 main(in float4 texcoords : TEXCOORD0) : SV_TARGET
 // CHECK-NEXT:                           OpStore %k [[boolVec]]
     bool2   k = frameConstants.boolMat._m12_m01;
 
+// CHECK:      [[FrameConstants:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:            [[ptr:%\d+]] = OpAccessChain %_ptr_Uniform_uint [[FrameConstants]] %int_3 %int_0 %int_2
+// CHECK-NEXT:           [[uint:%\d+]] = OpLoad %uint [[ptr]]
+// CHECK-NEXT:           [[bool:%\d+]] = OpINotEqual %bool [[uint]] %uint_0
+// CHECK-NEXT:                           OpStore %l [[bool]]
+    bool    l = frameConstants.t.boolArray[2];
+
+// CHECK:           [[FrameConstantsPtr:%\d+]] = OpAccessChain %_ptr_Uniform_FrameConstants %CONSTANTS %int_0
+// CHECK-NEXT:         [[FrameConstants:%\d+]] = OpLoad %FrameConstants [[FrameConstantsPtr]]
+// CHECK-NEXT:              [[fc_0_uint:%\d+]] = OpCompositeExtract %uint [[FrameConstants]] 0
+// CHECK-NEXT:              [[fc_0_bool:%\d+]] = OpINotEqual %bool [[fc_0_uint]] %uint_0
+// CHECK-NEXT:             [[fc_1_uint3:%\d+]] = OpCompositeExtract %v3uint [[FrameConstants]] 1
+// CHECK-NEXT:             [[fc_1_bool3:%\d+]] = OpINotEqual %v3bool [[fc_1_uint3]] [[v3uint0]]
+// CHECK-NEXT:           [[fc_2_uintMat:%\d+]] = OpCompositeExtract %_arr_v3uint_uint_2 [[FrameConstants]] 2
+// CHECK-NEXT: [[fc_2_uintMat_row0_uint:%\d+]] = OpCompositeExtract %v3uint [[fc_2_uintMat]] 0
+// CHECK-NEXT: [[fc_2_uintMat_row0_bool:%\d+]] = OpINotEqual %v3bool [[fc_2_uintMat_row0_uint]] [[v3uint0]]
+// CHECK-NEXT: [[fc_2_uintMat_row1_uint:%\d+]] = OpCompositeExtract %v3uint [[fc_2_uintMat]] 1
+// CHECK-NEXT: [[fc_2_uintMat_row1_bool:%\d+]] = OpINotEqual %v3bool [[fc_2_uintMat_row1_uint]] [[v3uint0]]
+// CHECK-NEXT:           [[fc_2_boolMat:%\d+]] = OpCompositeConstruct %_arr_v3bool_uint_2 [[fc_2_uintMat_row0_bool]] [[fc_2_uintMat_row1_bool]]
+// CHECK-NEXT:                 [[fc_3_T:%\d+]] = OpCompositeExtract %T [[FrameConstants]] 3
+// CHECK-NEXT:      [[fc_3_T_0_uint_arr:%\d+]] = OpCompositeExtract %_arr_uint_uint_1 [[fc_3_T]] 0
+// CHECK-NEXT:        [[fc_3_T_0_0_uint:%\d+]] = OpCompositeExtract %uint [[fc_3_T_0_uint_arr]] 0
+// CHECK-NEXT:        [[fc_3_T_0_0_bool:%\d+]] = OpINotEqual %bool [[fc_3_T_0_0_uint]] %uint_0
+// CHECK-NEXT:      [[fc_3_T_0_bool_arr:%\d+]] = OpCompositeConstruct %_arr_bool_uint_1 [[fc_3_T_0_0_bool]]
+// CHECK-NEXT:            [[fc_3_T_bool:%\d+]] = OpCompositeConstruct %T_0 [[fc_3_T_0_bool_arr]]
+// CHECK-NEXT:                     [[fc:%\d+]] = OpCompositeConstruct %FrameConstants_0 [[fc_0_bool]] [[fc_1_bool3]] [[fc_2_boolMat]] [[fc_3_T_bool]]
+// CHECK-NEXT:                                   OpStore %fc [[fc]]
+    FrameConstants fc = frameConstants;
+
     return (1.0).xxxx;
 }

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

@@ -1,16 +1,28 @@
 // Run: %dxc -T vs_6_0 -E main
 
+// CHECK: %T = OpTypeStruct %_arr_uint_uint_1
+struct T {
+  bool boolArray[1];
+};
+
+// CHECK: %S = OpTypeStruct %uint %v3uint %_arr_v3uint_uint_2 %T
 struct S
 {
   bool  boolScalar;
   bool3 boolVec;
   row_major bool2x3 boolMat;
+  T t;
 };
 
 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
+
+// These are the types that hold SPIR-V booleans, rather than Uints.
+// CHECK: %T_0 = OpTypeStruct %_arr_bool_uint_1
+// CHECK: %S_0 = OpTypeStruct %bool %v3bool %_arr_v3bool_uint_2 %T_0
+
 void main()
 {
   bool3 boolVecVar;
@@ -32,4 +44,48 @@ void main()
   // values[2].boolMat = boolMatVar;
   // values[0].boolVec.yzx = boolVecVar;
   // values[0].boolMat._m12_m11 = boolVecVar2;
+
+// CHECK:              [[sPtr:%\d+]] = OpAccessChain %_ptr_Uniform_S %values %int_0 %uint_0
+// CHECK-NEXT:            [[s:%\d+]] = OpLoad %S [[sPtr]]
+// CHECK-NEXT:           [[s0:%\d+]] = OpCompositeExtract %uint [[s]] 0
+// CHECK-NEXT:      [[s0_bool:%\d+]] = OpINotEqual %bool [[s0]] %uint_0
+// CHECK-NEXT:           [[s1:%\d+]] = OpCompositeExtract %v3uint [[s]] 1
+// CHECK-NEXT:      [[s1_bool:%\d+]] = OpINotEqual %v3bool [[s1]] [[v3uint0]]
+// CHECK-NEXT:           [[s2:%\d+]] = OpCompositeExtract %_arr_v3uint_uint_2 [[s]] 2
+// CHECK-NEXT:      [[s2_row0:%\d+]] = OpCompositeExtract %v3uint [[s2]] 0
+// CHECK-NEXT: [[s2_row0_bool:%\d+]] = OpINotEqual %v3bool [[s2_row0]] [[v3uint0]]
+// CHECK-NEXT:      [[s2_row1:%\d+]] = OpCompositeExtract %v3uint [[s2]] 1
+// CHECK-NEXT: [[s2_row1_bool:%\d+]] = OpINotEqual %v3bool [[s2_row1]] [[v3uint0]]
+// CHECK-NEXT:      [[s2_bool:%\d+]] = OpCompositeConstruct %_arr_v3bool_uint_2 [[s2_row0_bool]] [[s2_row1_bool]]
+// CHECK-NEXT:            [[t:%\d+]] = OpCompositeExtract %T [[s]] 3
+// CHECK-NEXT:  [[t0_uint_arr:%\d+]] = OpCompositeExtract %_arr_uint_uint_1 [[t]] 0
+// CHECK-NEXT:    [[t0_0_uint:%\d+]] = OpCompositeExtract %uint [[t0_uint_arr]] 0
+// CHECK-NEXT:    [[t0_0_bool:%\d+]] = OpINotEqual %bool [[t0_0_uint]] %uint_0
+// CHECK-NEXT:      [[t0_bool:%\d+]] = OpCompositeConstruct %_arr_bool_uint_1 [[t0_0_bool]]
+// CHECK-NEXT:       [[t_bool:%\d+]] = OpCompositeConstruct %T_0 [[t0_bool]]
+// CHECK-NEXT:       [[s_bool:%\d+]] = OpCompositeConstruct %S_0 [[s0_bool]] [[s1_bool]] [[s2_bool]] [[t_bool]]
+// CHECK-NEXT:                         OpStore %s [[s_bool]]
+  S s = values[0];
+
+// CHECK:                         [[s:%\d+]] = OpLoad %S_0 %s
+// CHECK-NEXT:            [[resultPtr:%\d+]] = OpAccessChain %_ptr_Uniform_S %values %int_0 %uint_1
+// CHECK-NEXT:              [[s0_bool:%\d+]] = OpCompositeExtract %bool [[s]] 0
+// CHECK-NEXT:              [[s0_uint:%\d+]] = OpSelect %uint [[s0_bool]] %uint_1 %uint_0
+// CHECK-NEXT:           [[s1_boolVec:%\d+]] = OpCompositeExtract %v3bool [[s]] 1
+// CHECK-NEXT:           [[s1_uintVec:%\d+]] = OpSelect %v3uint [[s1_boolVec]]
+// CHECK-NEXT:           [[s2_boolMat:%\d+]] = OpCompositeExtract %_arr_v3bool_uint_2 [[s]] 2
+// CHECK-NEXT:      [[s2_boolMat_row0:%\d+]] = OpCompositeExtract %v3bool [[s2_boolMat]] 0
+// CHECK-NEXT: [[s2_boolMat_row0_uint:%\d+]] = OpSelect %v3uint [[s2_boolMat_row0]]
+// CHECK-NEXT:      [[s2_boolMat_row1:%\d+]] = OpCompositeExtract %v3bool [[s2_boolMat]] 1
+// CHECK-NEXT: [[s2_boolMat_row1_uint:%\d+]] = OpSelect %v3uint [[s2_boolMat_row1]]
+// CHECK-NEXT:           [[s2_uintMat:%\d+]] = OpCompositeConstruct %_arr_v3uint_uint_2 [[s2_boolMat_row0_uint]] [[s2_boolMat_row1_uint]]
+// CHECK-NEXT:                    [[t:%\d+]] = OpCompositeExtract %T_0 [[s]] 3
+// CHECK-NEXT:          [[t0_bool_arr:%\d+]] = OpCompositeExtract %_arr_bool_uint_1 [[t]] 0
+// CHECK-NEXT:        [[t0_bool_arr_0:%\d+]] = OpCompositeExtract %bool [[t0_bool_arr]] 0
+// CHECK-NEXT:   [[t0_bool_arr_0_uint:%\d+]] = OpSelect %uint [[t0_bool_arr_0]] %uint_1 %uint_0
+// CHECK-NEXT:          [[t0_uint_arr:%\d+]] = OpCompositeConstruct %_arr_uint_uint_1 [[t0_bool_arr_0_uint]]
+// CHECK-NEXT:               [[t_uint:%\d+]] = OpCompositeConstruct %T [[t0_uint_arr]]
+// CHECK-NEXT:               [[s_uint:%\d+]] = OpCompositeConstruct %S [[s0_uint]] [[s1_uintVec]] [[s2_uintMat]] [[t_uint]]
+// CHECK-NEXT:                                 OpStore [[resultPtr:%\d+]] [[s_uint]]
+  values[1] = s;
 }

+ 25 - 0
tools/clang/test/CodeGenSPIRV/vk.push-constant.anon-struct.hlsl

@@ -0,0 +1,25 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK: OpName %type_PushConstant_ "type.PushConstant."
+// CHECK: OpMemberName %type_PushConstant_ 0 "a"
+// CHECK: OpMemberName %type_PushConstant_ 1 "b"
+// CHECK: OpMemberName %type_PushConstant_ 2 "c"
+
+// CHECK: %type_PushConstant_ = OpTypeStruct %int %float %v3float
+// CHECK: %_ptr_PushConstant_type_PushConstant_ = OpTypePointer PushConstant %type_PushConstant_
+[[vk::push_constant]]
+struct {
+    int    a;
+    float  b;
+    float3 c;
+}
+// CHECK: %PushConstants = OpVariable %_ptr_PushConstant_type_PushConstant_ PushConstant
+PushConstants;
+
+RWBuffer<int> Output;
+
+[numthreads(1, 1, 1)]
+void main() {
+// CHECK: OpAccessChain %_ptr_PushConstant_int %PushConstants %int_0
+    Output[0] = PushConstants.a;
+}

+ 1 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -460,6 +460,7 @@ public:
           spirvOpts.allowedExtensions = opts.SpvExtensions;
           spirvOpts.targetEnv = opts.SpvTargetEnv;
           spirvOpts.enable16BitTypes = opts.Enable16BitTypes;
+          spirvOpts.enableDebugInfo = opts.DebugInfo;
           clang::EmitSPIRVAction action(spirvOpts);
           FrontendInputFile file(utf8SourceName.m_psz, IK_HLSL);
           action.BeginSourceFile(compiler, file);

+ 22 - 15
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -501,15 +501,15 @@ TEST_F(FileTest, SemanticDispatchThreadId) {
 TEST_F(FileTest, SemanticDispatchThreadIdUint) {
   runFileTest("semantic.dispatch-thread-id.uint.cs.hlsl");
 }
-TEST_F(FileTest, SemanticDispatchThreadIdUint2) {
-  runFileTest("semantic.dispatch-thread-id.uint2.cs.hlsl");
+TEST_F(FileTest, SemanticDispatchThreadIdInt2) {
+  runFileTest("semantic.dispatch-thread-id.int2.cs.hlsl");
 }
 TEST_F(FileTest, SemanticGroupID) { runFileTest("semantic.group-id.cs.hlsl"); }
 TEST_F(FileTest, SemanticGroupIDUint) {
   runFileTest("semantic.group-id.uint.cs.hlsl");
 }
-TEST_F(FileTest, SemanticGroupIDUint2) {
-  runFileTest("semantic.group-id.uint2.cs.hlsl");
+TEST_F(FileTest, SemanticGroupIDInt2) {
+  runFileTest("semantic.group-id.int2.cs.hlsl");
 }
 TEST_F(FileTest, SemanticGroupThreadID) {
   runFileTest("semantic.group-thread-id.cs.hlsl");
@@ -517,8 +517,8 @@ TEST_F(FileTest, SemanticGroupThreadID) {
 TEST_F(FileTest, SemanticGroupThreadIDUint) {
   runFileTest("semantic.group-thread-id.uint.cs.hlsl");
 }
-TEST_F(FileTest, SemanticGroupThreadIDUint2) {
-  runFileTest("semantic.group-thread-id.uint2.cs.hlsl");
+TEST_F(FileTest, SemanticGroupThreadIDInt2) {
+  runFileTest("semantic.group-thread-id.int2.cs.hlsl");
 }
 TEST_F(FileTest, SemanticGroupIndex) {
   runFileTest("semantic.group-index.cs.hlsl");
@@ -1216,6 +1216,10 @@ TEST_F(FileTest, SpirvStageIOInterfacePS) {
   runFileTest("spirv.interface.ps.hlsl");
 }
 
+TEST_F(FileTest, SpirvStageIO16bitTypes) {
+  runFileTest("spirv.stage-io.16bit.hlsl");
+}
+
 TEST_F(FileTest, SpirvInterpolation) {
   runFileTest("spirv.interpolation.hlsl");
 }
@@ -1262,6 +1266,10 @@ TEST_F(FileTest, SpirvLegalizationTextureBuffer) {
               /*runValidation=*/false);
 }
 
+TEST_F(FileTest, SpirvDebugOpSource) {
+  runFileTest("spirv.debug.opsource.hlsl");
+}
+
 TEST_F(FileTest, VulkanAttributeErrors) {
   runFileTest("vk.attribute.error.hlsl", Expect::Failure);
 }
@@ -1317,18 +1325,14 @@ TEST_F(FileTest, VulkanRegisterBinding) {
   runFileTest("vk.binding.register.hlsl");
 }
 TEST_F(FileTest, VulkanRegisterBindingShift) {
-  // Resource binding from :register() and with shift specified via
+  // Resource binding from :register() with shift specified via
   // command line option
   runFileTest("vk.binding.cl.hlsl");
 }
-TEST_F(FileTest, VulkanExplicitBindingReassigned) {
-  runFileTest("vk.binding.explicit.error.hlsl", Expect::Warning);
-}
-TEST_F(FileTest, VulkanRegisterBindingReassigned) {
-  runFileTest("vk.binding.register.error.hlsl", Expect::Warning);
-}
-TEST_F(FileTest, VulkanRegisterBindingShiftReassigned) {
-  runFileTest("vk.binding.cl.error.hlsl", Expect::Warning);
+TEST_F(FileTest, VulkanRegisterBindingShiftAllSets) {
+  // Resource binding from :register() with shift specified for all sets via
+  // command line option
+  runFileTest("vk.binding.cl.all-sets.hlsl");
 }
 TEST_F(FileTest, VulkanStructuredBufferCounter) {
   // [[vk::counter_binding()]] for RWStructuredBuffer, AppendStructuredBuffer,
@@ -1340,6 +1344,9 @@ TEST_F(FileTest, VulkanPushConstant) { runFileTest("vk.push-constant.hlsl"); }
 TEST_F(FileTest, VulkanPushConstantOffset) {
   runFileTest("vk.push-constant.offset.hlsl");
 }
+TEST_F(FileTest, VulkanPushConstantAnonymousStruct) {
+  runFileTest("vk.push-constant.anon-struct.hlsl");
+}
 TEST_F(FileTest, VulkanMultiplePushConstant) {
   runFileTest("vk.push-constant.multiple.hlsl", Expect::Failure);
 }

+ 1 - 2
tools/clang/unittests/SPIRV/FileTestUtils.cpp

@@ -157,11 +157,10 @@ bool runCompilerWithSpirvGeneration(const llvm::StringRef inputFilePath,
     // Get compilation results.
     IFT(pResult->GetStatus(&resultStatus));
 
-    // Get diagnostics string and print warnings and errors to stderr.
+    // Get diagnostics string.
     IFT(pResult->GetErrorBuffer(&pErrorBuffer));
     const std::string diagnostics((char *)pErrorBuffer->GetBufferPointer(),
                                   pErrorBuffer->GetBufferSize());
-    fprintf(stderr, "%s\n", diagnostics.c_str());
     *errorMessages = diagnostics;
 
     if (SUCCEEDED(resultStatus)) {

+ 82 - 5
tools/clang/unittests/SPIRV/TestMain.cpp

@@ -21,20 +21,87 @@
 #endif
 #endif
 
+namespace {
+using namespace ::testing;
+
+/// A GoogleTest event printer that only prints test failures.
+class FailurePrinter : public TestEventListener {
+public:
+  explicit FailurePrinter(TestEventListener *listener)
+      : defaultListener(listener) {}
+
+  ~FailurePrinter() override { delete defaultListener; }
+
+  void OnTestProgramStart(const UnitTest &ut) override {
+    defaultListener->OnTestProgramStart(ut);
+  }
+
+  void OnTestIterationStart(const UnitTest &ut, int iteration) override {
+    defaultListener->OnTestIterationStart(ut, iteration);
+  }
+
+  void OnEnvironmentsSetUpStart(const UnitTest &ut) override {
+    defaultListener->OnEnvironmentsSetUpStart(ut);
+  }
+
+  void OnEnvironmentsSetUpEnd(const UnitTest &ut) override {
+    defaultListener->OnEnvironmentsSetUpEnd(ut);
+  }
+
+  void OnTestCaseStart(const TestCase &tc) override {
+    defaultListener->OnTestCaseStart(tc);
+  }
+
+  void OnTestStart(const TestInfo &ti) override {
+    // Do not output on test start
+    // defaultListener->OnTestStart(ti);
+  }
+
+  void OnTestPartResult(const TestPartResult &result) override {
+    defaultListener->OnTestPartResult(result);
+  }
+
+  void OnTestEnd(const TestInfo &ti) override {
+    // Only output if failure on test end
+    if (ti.result()->Failed())
+      defaultListener->OnTestEnd(ti);
+  }
+
+  void OnTestCaseEnd(const TestCase &tc) override {
+    defaultListener->OnTestCaseEnd(tc);
+  }
+
+  void OnEnvironmentsTearDownStart(const UnitTest &ut) override {
+    defaultListener->OnEnvironmentsTearDownStart(ut);
+  }
+
+  void OnEnvironmentsTearDownEnd(const UnitTest &ut) override {
+    defaultListener->OnEnvironmentsTearDownEnd(ut);
+  }
+
+  void OnTestIterationEnd(const UnitTest &ut, int iteration) override {
+    defaultListener->OnTestIterationEnd(ut, iteration);
+  }
+
+  void OnTestProgramEnd(const UnitTest &ut) override {
+    defaultListener->OnTestProgramEnd(ut);
+  }
+
+private:
+  TestEventListener *defaultListener;
+};
+} // namespace
+
 const char *TestMainArgv0;
 
 int main(int argc, char **argv) {
   llvm::sys::PrintStackTraceOnErrorSignal(true /* Disable crash reporting */);
 
-  // Initialize both gmock and gtest.
-  testing::InitGoogleMock(&argc, argv);
-
   for (int i = 1; i < argc; ++i) {
     if (std::string("--spirv-test-root") == argv[i]) {
       // Allow the user set the root directory for test input files.
       if (i + 1 < argc) {
-        clang::spirv::testOptions::inputDataDir = argv[i + 1];
-        i++;
+        clang::spirv::testOptions::inputDataDir = argv[++i];
       } else {
         fprintf(stderr, "Error: --spirv-test-root requires an argument\n");
         return 1;
@@ -42,6 +109,16 @@ int main(int argc, char **argv) {
     }
   }
 
+  // Initialize both gmock and gtest.
+  testing::InitGoogleMock(&argc, argv);
+
+  // Switch event listener to one that only prints failures.
+  testing::TestEventListeners &listeners =
+      ::testing::UnitTest::GetInstance()->listeners();
+  auto *defaultPrinter = listeners.Release(listeners.default_result_printer());
+  // Google Test takes the ownership.
+  listeners.Append(new FailurePrinter(defaultPrinter));
+
   // Make it easy for a test to re-execute itself by saving argv[0].
   TestMainArgv0 = argv[0];
 

+ 1 - 1
utils/appveyor/appveyor_test.ps1

@@ -39,7 +39,7 @@ function Invoke-AppveyorTestsRestMethod($appveyorTests) {
 }
 
 function Invoke-TE($logfile) {
-    $testdll = "$env:HLSL_BLD_DIR\Debug\bin\clang-hlsl-tests.dll"
+    $testdll = "$env:HLSL_BLD_DIR\Release\bin\clang-hlsl-tests.dll"
     $p = Start-Process "te.exe" -Args "$testdll /logOutput:Low /logFile:$logfile /enableWttLogging /p:HlslDataDir=%HLSL_SRC_DIR%\tools\clang\test\HLSL /labMode /miniDumpOnCrash" -Wait -NoNewWindow -PassThru
     return $p.ExitCode
 }