Browse Source

[spirv] Add support for four register number shifting options (#720)

Added the following four command line options to shift register
number for Vulkan:

* -fvk-b-shift
* -fvk-t-shift
* -fvk-s-shift
* -fvk-u-shift

These options are used to avoid assigning the same binding
number to more than one resources.
Lei Zhang 8 years ago
parent
commit
8702f97dff
42 changed files with 349 additions and 67 deletions
  1. 64 9
      docs/SPIR-V.rst
  2. 5 1
      include/dxc/Support/HLSLOptions.h
  3. 10 2
      include/dxc/Support/HLSLOptions.td
  4. 37 3
      lib/DxcSupport/HLSLOptions.cpp
  5. 5 0
      tools/clang/include/clang/SPIRV/EmitSPIRVOptions.h
  6. 61 6
      tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
  7. 2 2
      tools/clang/test/CodeGenSPIRV/passthru-cs.hlsl2spv
  8. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.gather-alpha.hlsl
  9. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.gather-blue.hlsl
  10. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.gather-cmp-red.hlsl
  11. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.gather-cmp.hlsl
  12. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.gather-green.hlsl
  13. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.gather-red.hlsl
  14. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.sample-bias.hlsl
  15. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.sample-cmp-level-zero.hlsl
  16. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.sample-cmp.hlsl
  17. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.sample-grad.hlsl
  18. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.sample-level.hlsl
  19. 1 1
      tools/clang/test/CodeGenSPIRV/texture.array.sample.hlsl
  20. 1 1
      tools/clang/test/CodeGenSPIRV/texture.gather-alpha.hlsl
  21. 1 1
      tools/clang/test/CodeGenSPIRV/texture.gather-blue.hlsl
  22. 1 1
      tools/clang/test/CodeGenSPIRV/texture.gather-cmp-red.hlsl
  23. 1 1
      tools/clang/test/CodeGenSPIRV/texture.gather-cmp.hlsl
  24. 1 1
      tools/clang/test/CodeGenSPIRV/texture.gather-green.hlsl
  25. 1 1
      tools/clang/test/CodeGenSPIRV/texture.gather-red.hlsl
  26. 1 1
      tools/clang/test/CodeGenSPIRV/texture.sample-bias.hlsl
  27. 1 1
      tools/clang/test/CodeGenSPIRV/texture.sample-cmp-level-zero.hlsl
  28. 1 1
      tools/clang/test/CodeGenSPIRV/texture.sample-cmp.hlsl
  29. 1 1
      tools/clang/test/CodeGenSPIRV/texture.sample-grad.hlsl
  30. 1 1
      tools/clang/test/CodeGenSPIRV/texture.sample-level.hlsl
  31. 1 1
      tools/clang/test/CodeGenSPIRV/texture.sample.hlsl
  32. 2 2
      tools/clang/test/CodeGenSPIRV/type.structured-buffer.hlsl
  33. 29 0
      tools/clang/test/CodeGenSPIRV/vk.binding.cl.error.hlsl
  34. 56 0
      tools/clang/test/CodeGenSPIRV/vk.binding.cl.hlsl
  35. 19 0
      tools/clang/test/CodeGenSPIRV/vk.binding.register.error.hlsl
  36. 6 8
      tools/clang/test/CodeGenSPIRV/vk.binding.register.hlsl
  37. 4 0
      tools/clang/tools/dxcompiler/dxcompilerobj.cpp
  38. 11 0
      tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp
  39. 1 1
      tools/clang/unittests/SPIRV/FileTestFixture.h
  40. 10 6
      tools/clang/unittests/SPIRV/FileTestUtils.cpp
  41. 2 2
      tools/clang/unittests/SPIRV/FileTestUtils.h
  42. 1 1
      tools/clang/unittests/SPIRV/WholeFileTestFixture.h

+ 64 - 9
docs/SPIR-V.rst

@@ -650,39 +650,72 @@ In shaders for DirectX, resources are accessed via registers; while in shaders
 for Vulkan, it is done via descriptor set and binding numbers. The developer
 for Vulkan, it is done via descriptor set and binding numbers. The developer
 can explicitly annotate variables in HLSL to specify descriptor set and binding
 can explicitly annotate variables in HLSL to specify descriptor set and binding
 numbers, or leave it to the compiler to derive implicitly from registers.
 numbers, or leave it to the compiler to derive implicitly from registers.
-The explicit way has precedence over the implicit way.
 
 
 Explicit binding number assignment
 Explicit binding number assignment
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
 ``[[vk::binding(X[, Y])]]`` can be attached to global variables to specify the
 ``[[vk::binding(X[, Y])]]`` can be attached to global variables to specify the
-descriptor set ``Y`` and binding ``X``. The descriptor set number is optional;
-if missing, it will be zero.
+descriptor set as ``Y`` and binding number as ``X``. The descriptor set number
+is optional; if missing, it will be zero.
 
 
 Implicit binding number assignment
 Implicit binding number assignment
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-Without explicit annotations, the compiler will try to deduce descriptor set and
-binding numbers in the following way:
+Without explicit annotations, the compiler will try to deduce descriptor sets
+and binding numbers in the following way:
 
 
 If there is ``:register(xX, spaceY)`` specified for the given global variable,
 If there is ``:register(xX, spaceY)`` specified for the given global variable,
 the corresponding resource will be assigned to descriptor set ``Y`` and binding
 the corresponding resource will be assigned to descriptor set ``Y`` and binding
-number ``X``, regardless the resource type ``x``. (Note that this can cause
-reassignment of the same set and binding number pair. [TODO])
+number ``X``, regardless of the register type ``x``. Note that this will cause
+binding number collision if, say, two resources are of different register
+type but the same register number. To solve this problem, four command-line
+options, ``-fvk-b-shift N M``, ``-fvk-s-shift N M``, ``-fvk-t-shift N M``, and
+``-fvk-u-shift N M``, are provided to shift by ``N`` all binding numbers
+inferred for register type ``b``, ``s``, ``t``, and ``u`` in space ``M``,
+respectively.
 
 
 If there is no register specification, the corresponding resource will be
 If there is no register specification, the corresponding resource will be
 assigned to the next available binding number, starting from 0, in descriptor
 assigned to the next available binding number, starting from 0, in descriptor
 set #0.
 set #0.
 
 
+Summary
+~~~~~~~
+
 In summary, the compiler essentially assigns binding numbers in three passes.
 In summary, the compiler essentially assigns binding numbers in three passes.
 
 
 - Firstly it handles all declarations with explicit ``[[vk::binding(X[, Y])]]``
 - Firstly it handles all declarations with explicit ``[[vk::binding(X[, Y])]]``
   annotation.
   annotation.
 - Then the compiler processes all remaining declarations with
 - Then the compiler processes all remaining declarations with
-  ``:register(xX, spaceY)`` annotation.
+  ``:register(xX, spaceY)`` annotation, by applying the shift passed in using
+  command-line option ``-fvk-{b|s|t|u}-shift N M``, if provided.
 - Finally, the compiler assigns next available binding numbers to the rest in
 - Finally, the compiler assigns next available binding numbers to the rest in
   the declaration order.
   the declaration order.
 
 
+As an example, for the following code:
+
+.. code:: hlsl
+
+  struct S { ... };
+
+  ConstantBuffer<S> cbuffer1 : register(b0);
+  Texture2D<float4> texture1 : register(t0);
+  Texture2D<float4> texture2 : register(t1, space1);
+  SamplerState      sampler1;
+  [[vk::binding(3)]]
+  RWBuffer<float4> rwbuffer1 : register(u5, space2);
+
+If we compile with ``-fvk-t-shift 0 10 -fvk-t-shift 1 20``:
+
+- ``rwbuffer1`` will take binding #3 in set #0, since explicit binding
+  assignment has precedence over the rest.
+- ``cbuffer1`` will take binding #0 in set #0, since that's what deduced from
+  the register assignment, and there is no shift requested from command line.
+- ``texture1`` will take binding #10 in set #0, and ``texture2`` will take
+  binding #21 in set #1, since we requested an 10 shift on t-type registers.
+- ``sampler1`` will take binding 1 in set #0, since that's the next available
+  binding number in set #0.
+
+.. code:: hlsl
 HLSL Expressions
 HLSL Expressions
 ================
 ================
 
 
@@ -1163,7 +1196,7 @@ HLSL Intrinsic Function   GLSL Extended Instruction
 ``tan``                 ``Tan``
 ``tan``                 ``Tan``
 ``tanh``                ``Tanh``
 ``tanh``                ``Tanh``
 ``trunc``               ``Trunc``
 ``trunc``               ``Trunc``
-======================= ===============================
+======================= ===================================
 
 
 HLSL OO features
 HLSL OO features
 ================
 ================
@@ -1745,3 +1778,25 @@ as stage output variables. The output struct of the patch constant function must
 ``SV_TessFactor`` and ``SV_InsideTessFactor`` fields which will translate to
 ``SV_TessFactor`` and ``SV_InsideTessFactor`` fields which will translate to
 ``TessLevelOuter`` and ``TessLevelInner`` builtin variables, respectively. And the rest
 ``TessLevelOuter`` and ``TessLevelInner`` builtin variables, respectively. And the rest
 will be flattened and translated into normal stage output variables, one for each field.
 will be flattened and translated into normal stage output variables, one for each field.
+
+Vulkan Command-line Options
+===========================
+
+The following command line options are added into ``dxc`` to support SPIR-V
+codegen for Vulkan:
+
+- ``-spirv``: Generates SPIR-V code.
+- ``-fvk-b-shift N M``: Shifts by ``N`` the inferred binding numbers for all
+  resources in b-type registers of space ``M``. Specifically, for a resouce
+  attached with ``:register(bX, spaceM)`` but not ``[vk::binding(...)]``,
+  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.
+- ``-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.
+- ``-fvk-stage-io-order={alpha|decl}``: Assigns the stage input/output variable
+  location number according to alphabetical order or declaration order. See
+  `HLSL semantic and Vulkan Location`_ for more details.

+ 5 - 1
include/dxc/Support/HLSLOptions.h

@@ -160,7 +160,11 @@ public:
   // SPIRV Change Starts
   // SPIRV Change Starts
 #ifdef ENABLE_SPIRV_CODEGEN
 #ifdef ENABLE_SPIRV_CODEGEN
   bool GenSPIRV; // OPT_spirv
   bool GenSPIRV; // OPT_spirv
-  llvm::StringRef VkStageIoOrder;
+  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
 #endif
 #endif
   // SPIRV Change Ends
   // SPIRV Change Ends
 };
 };

+ 10 - 2
include/dxc/Support/HLSLOptions.td

@@ -234,10 +234,18 @@ def no_min_precision: Flag<["-", "/"], "no-min-precision">, Flags<[CoreOption, D
 def ignore_line_directives : Flag<["-", "/"], "ignore-line-directives">, HelpText<"Ignore line directives">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 def ignore_line_directives : Flag<["-", "/"], "ignore-line-directives">, HelpText<"Ignore line directives">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 
 
 // SPIRV Change Starts
 // SPIRV Change Starts
-def spirv : Flag<["-"], "spirv">, Group<spirv_Group>, Flags<[CoreOption, DriverOption, HelpHidden]>,
-  HelpText<"Generate SPIR-V binary code">;
+def spirv : Flag<["-"], "spirv">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Generate SPIR-V code">;
 def fvk_stage_io_order_EQ : Joined<["-"], "fvk-stage-io-order=">, Group<spirv_Group>, Flags<[CoreOption, DriverOption, HelpHidden]>,
 def fvk_stage_io_order_EQ : Joined<["-"], "fvk-stage-io-order=">, Group<spirv_Group>, Flags<[CoreOption, DriverOption, HelpHidden]>,
   HelpText<"Specify Vulkan stage I/O location assignment order">;
   HelpText<"Specify Vulkan stage I/O location assignment order">;
+def fvk_b_shift : MultiArg<["-"], "fvk-b-shift", 2>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Specify Vulkan binding number shift for b-type register">;
+def fvk_t_shift : MultiArg<["-"], "fvk-t-shift", 2>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Specify Vulkan binding number shift for t-type register">;
+def fvk_s_shift : MultiArg<["-"], "fvk-s-shift", 2>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Specify Vulkan binding number shift for s-type register">;
+def fvk_u_shift : MultiArg<["-"], "fvk-u-shift", 2>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Specify Vulkan binding number shift for u-type register">;
 // SPIRV Change Ends
 // SPIRV Change Ends
 
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////

+ 37 - 3
lib/DxcSupport/HLSLOptions.cpp

@@ -443,16 +443,50 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
 
 
   // SPIRV Change Starts
   // SPIRV Change Starts
 #ifdef ENABLE_SPIRV_CODEGEN
 #ifdef ENABLE_SPIRV_CODEGEN
-  opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false);
+  const bool genSpirv = opts.GenSPIRV = Args.hasFlag(OPT_spirv, 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;
+  };
+
+  if (!handleVkShiftArgs(OPT_fvk_b_shift, "b", &opts.VkBShift) ||
+      !handleVkShiftArgs(OPT_fvk_t_shift, "t", &opts.VkTShift) ||
+      !handleVkShiftArgs(OPT_fvk_s_shift, "s", &opts.VkSShift) ||
+      !handleVkShiftArgs(OPT_fvk_u_shift, "u", &opts.VkUShift))
+    return 1;
+
   opts.VkStageIoOrder = Args.getLastArgValue(OPT_fvk_stage_io_order_EQ, "decl");
   opts.VkStageIoOrder = Args.getLastArgValue(OPT_fvk_stage_io_order_EQ, "decl");
   if (opts.VkStageIoOrder != "alpha" && opts.VkStageIoOrder != "decl") {
   if (opts.VkStageIoOrder != "alpha" && opts.VkStageIoOrder != "decl") {
-    errors << "Unknown Vulkan stage I/O location assignment order : "
+    errors << "unknown Vulkan stage I/O location assignment order: "
            << opts.VkStageIoOrder;
            << opts.VkStageIoOrder;
     return 1;
     return 1;
   }
   }
 #else
 #else
   if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) ||
   if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) ||
-      !Args.getLastArgValue(OPT_fvk_stage_io_order_EQ).empty()) {
+      !Args.getLastArgValue(OPT_fvk_stage_io_order_EQ).empty() ||
+      !Args.getLastArgValue(OPT_fvk_b_shift).empty() ||
+      !Args.getLastArgValue(OPT_fvk_t_shift).empty() ||
+      !Args.getLastArgValue(OPT_fvk_s_shift).empty() ||
+      !Args.getLastArgValue(OPT_fvk_u_shift).empty()
+      ) {
     errors << "SPIR-V CodeGen not available. "
     errors << "SPIR-V CodeGen not available. "
               "Please recompile with -DENABLE_SPIRV_CODEGEN=ON.";
               "Please recompile with -DENABLE_SPIRV_CODEGEN=ON.";
     return 1;
     return 1;

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

@@ -9,12 +9,17 @@
 #ifndef LLVM_CLANG_SPIRV_EMITSPIRVOPTIONS_H
 #ifndef LLVM_CLANG_SPIRV_EMITSPIRVOPTIONS_H
 #define LLVM_CLANG_SPIRV_EMITSPIRVOPTIONS_H
 #define LLVM_CLANG_SPIRV_EMITSPIRVOPTIONS_H
 
 
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringRef.h"
 
 
 namespace clang {
 namespace clang {
 /// Structs for controlling behaviors of SPIR-V codegen.
 /// Structs for controlling behaviors of SPIR-V codegen.
 struct EmitSPIRVOptions {
 struct EmitSPIRVOptions {
   llvm::StringRef stageIoOrder;
   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;
 };
 };
 } // end namespace clang
 } // end namespace clang
 
 

+ 61 - 6
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -465,6 +465,32 @@ bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
   return true;
   return true;
 }
 }
 
 
+namespace {
+/// A class for maintaining the binding number shift requested for descriptor
+/// sets.
+class BindingShiftMapper {
+public:
+  explicit BindingShiftMapper(const llvm::SmallVectorImpl<uint32_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];
+  }
+
+  /// Returns the shift amount for the given set.
+  uint32_t getShiftForSet(uint32_t set) const {
+    const auto found = perSetShift.find(set);
+    if (found != perSetShift.end())
+      return found->second;
+    return masterShift;
+  }
+
+private:
+  uint32_t masterShift; /// Shift amount applies to all sets.
+  llvm::DenseMap<uint32_t, uint32_t> perSetShift;
+};
+}
+
 bool DeclResultIdMapper::decorateResourceBindings() {
 bool DeclResultIdMapper::decorateResourceBindings() {
   BindingSet bindingSet;
   BindingSet bindingSet;
   bool noError = true;
   bool noError = true;
@@ -486,18 +512,47 @@ bool DeclResultIdMapper::decorateResourceBindings() {
       }
       }
     }
     }
 
 
+  BindingShiftMapper bShiftMapper(spirvOptions.bShift);
+  BindingShiftMapper tShiftMapper(spirvOptions.tShift);
+  BindingShiftMapper sShiftMapper(spirvOptions.sShift);
+  BindingShiftMapper uShiftMapper(spirvOptions.uShift);
+
   // Process variables with register(...) binding assignment
   // Process variables with register(...) binding assignment
   for (const auto &var : resourceVars)
   for (const auto &var : resourceVars)
     if (const auto *reg = var.getRegister())
     if (const auto *reg = var.getRegister())
       if (!var.getBinding()) {
       if (!var.getBinding()) {
         const uint32_t set = reg->RegisterSpace;
         const uint32_t set = reg->RegisterSpace;
-        const uint32_t binding = reg->RegisterNumber;
+        uint32_t binding = reg->RegisterNumber;
+        switch (reg->RegisterType) {
+        case 'b':
+          binding += bShiftMapper.getShiftForSet(set);
+          break;
+        case 't':
+          binding += tShiftMapper.getShiftForSet(set);
+          break;
+        case 's':
+          binding += sShiftMapper.getShiftForSet(set);
+          break;
+        case 'u':
+          binding += uShiftMapper.getShiftForSet(set);
+          break;
+        case 'c':
+          // For setting packing offset. Does not affect binding.
+          break;
+        default:
+          llvm_unreachable("unknown register type found");
+        }
 
 
-        // TODO: we can have duplicated set and binding number because of there
-        // are multiple resource types in the following. E.g., :register(s0) and
-        // :register(t0) will both map to set #0 and binding #0.
-        theBuilder.decorateDSetBinding(var.getSpirvId(), set, binding);
-        bindingSet.useBinding(binding, set);
+        if (bindingSet.isBindingUsed(binding, set)) {
+          emitError(
+              "resource binding #%0 in descriptor set #%1 already assigned",
+              reg->Loc)
+              << binding << set;
+          noError = false;
+        } else {
+          theBuilder.decorateDSetBinding(var.getSpirvId(), set, binding);
+          bindingSet.useBinding(binding, set);
+        }
       }
       }
 
 
   // Process variables with no binding assignment
   // Process variables with no binding assignment

+ 2 - 2
tools/clang/test/CodeGenSPIRV/passthru-cs.hlsl2spv

@@ -8,7 +8,7 @@
 //--------------------------------------------------------------------------------------
 //--------------------------------------------------------------------------------------
 
 
 ByteAddressBuffer Buffer0 : register(t0);
 ByteAddressBuffer Buffer0 : register(t0);
-RWByteAddressBuffer BufferOut : register(u0);
+RWByteAddressBuffer BufferOut : register(u1);
 
 
 [numthreads(1, 1, 1)]
 [numthreads(1, 1, 1)]
 void main( uint3 DTid : SV_DispatchThreadID )
 void main( uint3 DTid : SV_DispatchThreadID )
@@ -47,7 +47,7 @@ void main( uint3 DTid : SV_DispatchThreadID )
 // OpDecorate %Buffer0 DescriptorSet 0
 // OpDecorate %Buffer0 DescriptorSet 0
 // OpDecorate %Buffer0 Binding 0
 // OpDecorate %Buffer0 Binding 0
 // OpDecorate %BufferOut DescriptorSet 0
 // OpDecorate %BufferOut DescriptorSet 0
-// OpDecorate %BufferOut Binding 0
+// OpDecorate %BufferOut Binding 1
 // %uint = OpTypeInt 32 0
 // %uint = OpTypeInt 32 0
 // %int = OpTypeInt 32 1
 // %int = OpTypeInt 32 1
 // %_runtimearr_uint = OpTypeRuntimeArray %uint
 // %_runtimearr_uint = OpTypeRuntimeArray %uint

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<uint4>  t2u4 : register(t2);
 Texture2DArray<uint4>  t2u4 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<int3>   t2i3 : register(t2);
 Texture2DArray<int3>   t2i3 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<uint>   t2u1 : register(t2);
 Texture2DArray<uint>   t2u1 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture2DArray<float4> t1 : register(t1);
 Texture2DArray<float4> t1 : register(t1);
 Texture2DArray<float2> t2 : register(t2);
 Texture2DArray<float2> t2 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<uint2>  t2u2 : register(t2);
 Texture2DArray<uint2>  t2u2 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<float4> t2f4 : register(t1);
 Texture2DArray<uint>   t2u1 : register(t2);
 Texture2DArray<uint>   t2u1 : register(t2);

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.array.sample-bias.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.array.sample-cmp-level-zero.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture1DArray   <float4> t1 : register(t1);
 Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float3> t2 : register(t2);
 Texture2DArray   <float3> t2 : register(t2);

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.array.sample-cmp.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture1DArray   <float4> t1 : register(t1);
 Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float3> t2 : register(t2);
 Texture2DArray   <float3> t2 : register(t2);

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.array.sample-grad.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.array.sample-level.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.array.sample.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<int4>   t2i4 : register(t2);
 Texture2D<int4>   t2i4 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<uint3>  t2u3 : register(t2);
 Texture2D<uint3>  t2u3 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<uint>   t2u1 : register(t2);
 Texture2D<uint>   t2u1 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture2D<float4> t1 : register(t1);
 Texture2D<float4> t1 : register(t1);
 Texture2D<float2> t2 : register(t2);
 Texture2D<float2> t2 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<uint2>  t2u2 : register(t2);
 Texture2D<uint2>  t2u2 : register(t2);

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

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<float4> t2f4 : register(t1);
 Texture2D<uint>   t2u1 : register(t2);
 Texture2D<uint>   t2u1 : register(t2);

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.sample-bias.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.sample-cmp-level-zero.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture1D   <float4> t1 : register(t1);
 Texture1D   <float4> t1 : register(t1);
 Texture2D   <float2> t2 : register(t2);
 Texture2D   <float2> t2 : register(t2);

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.sample-cmp.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerComparisonState gSampler : register(s1);
+SamplerComparisonState gSampler : register(s5);
 
 
 Texture1D   <float4> t1 : register(t1);
 Texture1D   <float4> t1 : register(t1);
 Texture2D   <float2> t2 : register(t2);
 Texture2D   <float2> t2 : register(t2);

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.sample-grad.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.sample-level.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

+ 1 - 1
tools/clang/test/CodeGenSPIRV/texture.sample.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T ps_6_0 -E main
 // Run: %dxc -T ps_6_0 -E main
 
 
-SamplerState gSampler : register(s1);
+SamplerState gSampler : register(s5);
 
 
 // Note: The front end forbids sampling from non-floating-point texture formats.
 // Note: The front end forbids sampling from non-floating-point texture formats.
 
 

+ 2 - 2
tools/clang/test/CodeGenSPIRV/type.structured-buffer.hlsl

@@ -39,9 +39,9 @@ StructuredBuffer<S> mySBuffer1 : register(t1);
 StructuredBuffer<T> mySBuffer2 : register(t2);
 StructuredBuffer<T> mySBuffer2 : register(t2);
 
 
 // CHECK: %mySBuffer3 = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
 // CHECK: %mySBuffer3 = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
-RWStructuredBuffer<S> mySBuffer3 : register(u1);
+RWStructuredBuffer<S> mySBuffer3 : register(u3);
 // CHECK: %mySBuffer4 = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_T Uniform
 // CHECK: %mySBuffer4 = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_T Uniform
-RWStructuredBuffer<T> mySBuffer4 : register(u2);
+RWStructuredBuffer<T> mySBuffer4 : register(u4);
 
 
 float4 main() : SV_Target {
 float4 main() : SV_Target {
     return 1.0;
     return 1.0;

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

@@ -0,0 +1,29 @@
+// 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: error: resource binding #2 in descriptor set #0 already assigned
+
+//CHECK: :13:29: error: resource binding #2 in descriptor set #0 already assigned
+
+//CHECK: :19:30: error: resource binding #3 in descriptor set #0 already assigned

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

@@ -0,0 +1,56 @@
+// Run: %dxc -T ps_6_0 -E main -fvk-b-shift 100 0 -fvk-b-shift 200 2 -fvk-t-shift 200 0 -fvk-t-shift 300 1 -fvk-t-shift 400 0 -fvk-s-shift 500 0 -fvk-s-shift 600 2 -fvk-u-shift 700 0 -fvk-u-shift 800 3
+
+// Tests that we can set shift for more than one sets of the same register type
+// Tests that we can override shift for the same set
+
+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 100
+ConstantBuffer<S> cbuffer1 : register(b0);
+// CHECK: OpDecorate %cbuffer2 DescriptorSet 2
+// CHECK: OpDecorate %cbuffer2 Binding 200
+ConstantBuffer<S> cbuffer2 : register(b0, space2);
+
+// CHECK: OpDecorate %texture1 DescriptorSet 1
+// CHECK: OpDecorate %texture1 Binding 301
+Texture2D<float4> texture1: register(t1, space1);
+// CHECK: OpDecorate %texture2 DescriptorSet 0
+// CHECK: OpDecorate %texture2 Binding 401
+Texture2D<float4> texture2: register(t1);
+
+// CHECK: OpDecorate %sampler1 DescriptorSet 0
+// CHECK: OpDecorate %sampler1 Binding 500
+// CHECK: OpDecorate %sampler2 DescriptorSet 2
+// CHECK: OpDecorate %sampler2 Binding 600
+SamplerState sampler1: register(s0);
+SamplerState sampler2: register(s0, space2);
+
+// CHECK: OpDecorate %rwbuffer1 DescriptorSet 3
+// CHECK: OpDecorate %rwbuffer1 Binding 803
+RWBuffer<float4> rwbuffer1 : register(u3, space3);
+// CHECK: OpDecorate %rwbuffer2 DescriptorSet 0
+// CHECK: OpDecorate %rwbuffer2 Binding 703
+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;
+}

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

@@ -0,0 +1,19 @@
+// 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);         // duplicate
+RWStructuredBuffer<S> mySBuffer2 : register(u0, space1); // duplicate
+RWStructuredBuffer<S> mySBuffer3 : register(u0, space2);
+
+float4 main() : SV_Target {
+    return 1.0;
+}
+
+// CHECK: :10:36: error: resource binding #0 in descriptor set #0 already assigned
+// CHECK: :11:36: error: resource binding #0 in descriptor set #1 already assigned

+ 6 - 8
tools/clang/test/CodeGenSPIRV/vk.binding.register.hlsl

@@ -8,15 +8,13 @@ SamplerState sampler1: register(s1);
 // CHECK-NEXT: OpDecorate %sampler2 Binding 2
 // CHECK-NEXT: OpDecorate %sampler2 Binding 2
 SamplerState sampler2 : register(s2, space1);
 SamplerState sampler2 : register(s2, space1);
 
 
-// Note: overlapping set # and binding # for now.
 // CHECK:      OpDecorate %texture1 DescriptorSet 1
 // CHECK:      OpDecorate %texture1 DescriptorSet 1
-// CHECK-NEXT: OpDecorate %texture1 Binding 2
-Texture2D<float4> texture1: register(t2, space1);
+// CHECK-NEXT: OpDecorate %texture1 Binding 20
+Texture2D<float4> texture1: register(t20, space1);
 
 
-// Note: overlapping set # and binding # for now.
 // CHECK:      OpDecorate %texture2 DescriptorSet 0
 // CHECK:      OpDecorate %texture2 DescriptorSet 0
-// CHECK-NEXT: OpDecorate %texture2 Binding 1
-Texture3D<float4> texture2: register(t1);
+// CHECK-NEXT: OpDecorate %texture2 Binding 10
+Texture3D<float4> texture2: register(t10);
 
 
 SamplerState sampler3;
 SamplerState sampler3;
 
 
@@ -57,8 +55,8 @@ RWStructuredBuffer<S> sbuffer2 : register(u6, space1);
 
 
     // The counter variable will use the next unassigned number
     // The counter variable will use the next unassigned number
 // CHECK:      OpDecorate %abuffer DescriptorSet 0
 // CHECK:      OpDecorate %abuffer DescriptorSet 0
-// CHECK-NEXT: OpDecorate %abuffer Binding 5
-AppendStructuredBuffer<S> abuffer : register(u5);
+// CHECK-NEXT: OpDecorate %abuffer Binding 55
+AppendStructuredBuffer<S> abuffer : register(u55);
 
 
     // The counter variable will use the next unassigned number
     // The counter variable will use the next unassigned number
 // CHECK:      OpDecorate %csbuffer DescriptorSet 0
 // CHECK:      OpDecorate %csbuffer DescriptorSet 0

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

@@ -464,6 +464,10 @@ public:
       else if (opts.GenSPIRV) {
       else if (opts.GenSPIRV) {
           clang::EmitSPIRVOptions spirvOpts;
           clang::EmitSPIRVOptions spirvOpts;
           spirvOpts.stageIoOrder = opts.VkStageIoOrder;
           spirvOpts.stageIoOrder = opts.VkStageIoOrder;
+          spirvOpts.bShift = opts.VkBShift;
+          spirvOpts.tShift = opts.VkTShift;
+          spirvOpts.sShift = opts.VkSShift;
+          spirvOpts.uShift = opts.VkUShift;
           clang::EmitSPIRVAction action(spirvOpts);
           clang::EmitSPIRVAction action(spirvOpts);
           FrontendInputFile file(utf8SourceName.m_psz, IK_HLSL);
           FrontendInputFile file(utf8SourceName.m_psz, IK_HLSL);
           action.BeginSourceFile(compiler, file);
           action.BeginSourceFile(compiler, file);

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

@@ -695,9 +695,20 @@ TEST_F(FileTest, VulkanRegisterBinding) {
   // Resource binding from :register()
   // Resource binding from :register()
   runFileTest("vk.binding.register.hlsl");
   runFileTest("vk.binding.register.hlsl");
 }
 }
+TEST_F(FileTest, VulkanRegisterBindingShift) {
+  // Resource binding from :register() and with shift specified via
+  // command line option
+  runFileTest("vk.binding.cl.hlsl");
+}
 TEST_F(FileTest, VulkanExplicitBindingReassigned) {
 TEST_F(FileTest, VulkanExplicitBindingReassigned) {
   runFileTest("vk.binding.explicit.error.hlsl", /*expectSuccess*/ false);
   runFileTest("vk.binding.explicit.error.hlsl", /*expectSuccess*/ false);
 }
 }
+TEST_F(FileTest, VulkanRegisterBindingReassigned) {
+  runFileTest("vk.binding.register.error.hlsl", /*expectSuccess*/ false);
+}
+TEST_F(FileTest, VulkanRegisterBindingShiftReassigned) {
+  runFileTest("vk.binding.cl.error.hlsl", /*expectSuccess*/ false);
+}
 TEST_F(FileTest, VulkanLayoutCBufferStd140) {
 TEST_F(FileTest, VulkanLayoutCBufferStd140) {
   runFileTest("vk.layout.cbuffer.std140.hlsl");
   runFileTest("vk.layout.cbuffer.std140.hlsl");
 }
 }

+ 1 - 1
tools/clang/unittests/SPIRV/FileTestFixture.h

@@ -34,7 +34,7 @@ private:
 
 
   std::string targetProfile;             ///< Target profile (argument of -T)
   std::string targetProfile;             ///< Target profile (argument of -T)
   std::string entryPoint;                ///< Entry point name (argument of -E)
   std::string entryPoint;                ///< Entry point name (argument of -E)
-  std::string restArgs;                  ///< All the other arguments
+  std::vector<std::string> restArgs;     ///< All the other arguments
   std::string inputFilePath;             ///< Path to the input test file
   std::string inputFilePath;             ///< Path to the input test file
   std::vector<uint32_t> generatedBinary; ///< The generated SPIR-V Binary
   std::vector<uint32_t> generatedBinary; ///< The generated SPIR-V Binary
   std::string checkCommands;             ///< CHECK commands that verify output
   std::string checkCommands;             ///< CHECK commands that verify output

+ 10 - 6
tools/clang/unittests/SPIRV/FileTestUtils.cpp

@@ -42,7 +42,7 @@ bool validateSpirvBinary(std::vector<uint32_t> &binary) {
 
 
 bool processRunCommandArgs(const llvm::StringRef runCommandLine,
 bool processRunCommandArgs(const llvm::StringRef runCommandLine,
                            std::string *targetProfile, std::string *entryPoint,
                            std::string *targetProfile, std::string *entryPoint,
-                           std::string *restArgs) {
+                           std::vector<std::string> *restArgs) {
   std::istringstream buf(runCommandLine);
   std::istringstream buf(runCommandLine);
   std::istream_iterator<std::string> start(buf), end;
   std::istream_iterator<std::string> start(buf), end;
   std::vector<std::string> tokens(start, end);
   std::vector<std::string> tokens(start, end);
@@ -60,9 +60,8 @@ bool processRunCommandArgs(const llvm::StringRef runCommandLine,
     else if (tokens[i] == "-E" && (++i) < tokens.size())
     else if (tokens[i] == "-E" && (++i) < tokens.size())
       *entryPoint = tokens[i];
       *entryPoint = tokens[i];
     else
     else
-      rest << (restArgs->empty() ? "" : " ") << tokens[i];
+      restArgs->push_back(tokens[i]);
   }
   }
-  *restArgs = rest.str();
 
 
   if (targetProfile->empty()) {
   if (targetProfile->empty()) {
     fprintf(stderr, "Error: Missing target profile argument (-T).\n");
     fprintf(stderr, "Error: Missing target profile argument (-T).\n");
@@ -106,13 +105,17 @@ std::string getAbsPathOfInputDataFile(const llvm::StringRef filename) {
 bool runCompilerWithSpirvGeneration(const llvm::StringRef inputFilePath,
 bool runCompilerWithSpirvGeneration(const llvm::StringRef inputFilePath,
                                     const llvm::StringRef entryPoint,
                                     const llvm::StringRef entryPoint,
                                     const llvm::StringRef targetProfile,
                                     const llvm::StringRef targetProfile,
-                                    const llvm::StringRef restArgs,
+                                    const std::vector<std::string> &restArgs,
                                     std::vector<uint32_t> *generatedBinary,
                                     std::vector<uint32_t> *generatedBinary,
                                     std::string *errorMessages) {
                                     std::string *errorMessages) {
   std::wstring srcFile(inputFilePath.begin(), inputFilePath.end());
   std::wstring srcFile(inputFilePath.begin(), inputFilePath.end());
   std::wstring entry(entryPoint.begin(), entryPoint.end());
   std::wstring entry(entryPoint.begin(), entryPoint.end());
   std::wstring profile(targetProfile.begin(), targetProfile.end());
   std::wstring profile(targetProfile.begin(), targetProfile.end());
-  std::wstring rest(restArgs.begin(), restArgs.end());
+
+  std::vector<std::wstring> rest;
+  for (const auto &arg : restArgs)
+    rest.emplace_back(arg.begin(), arg.end());
+
   bool success = true;
   bool success = true;
 
 
   try {
   try {
@@ -135,7 +138,8 @@ bool runCompilerWithSpirvGeneration(const llvm::StringRef inputFilePath,
     flags.push_back(profile.c_str());
     flags.push_back(profile.c_str());
     flags.push_back(L"-spirv");
     flags.push_back(L"-spirv");
     flags.push_back(L"-O0"); // Disable optimization for testing
     flags.push_back(L"-O0"); // Disable optimization for testing
-    flags.push_back(rest.c_str());
+    for (const auto &arg : rest)
+      flags.push_back(arg.c_str());
 
 
     IFT(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
     IFT(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
     IFT(pLibrary->CreateBlobFromFile(srcFile.c_str(), nullptr, &pSource));
     IFT(pLibrary->CreateBlobFromFile(srcFile.c_str(), nullptr, &pSource));

+ 2 - 2
tools/clang/unittests/SPIRV/FileTestUtils.h

@@ -39,7 +39,7 @@ bool validateSpirvBinary(std::vector<uint32_t> &binary);
 /// Returns true on success, and false otherwise.
 /// Returns true on success, and false otherwise.
 bool processRunCommandArgs(const llvm::StringRef runCommandLine,
 bool processRunCommandArgs(const llvm::StringRef runCommandLine,
                            std::string *targetProfile, std::string *entryPoint,
                            std::string *targetProfile, std::string *entryPoint,
-                           std::string *restArgs);
+                           std::vector<std::string> *restArgs);
 
 
 /// \brief Converts an IDxcBlob into a vector of 32-bit unsigned integers which
 /// \brief Converts an IDxcBlob into a vector of 32-bit unsigned integers which
 /// is returned via the 'binaryWords' argument.
 /// is returned via the 'binaryWords' argument.
@@ -58,7 +58,7 @@ std::string getAbsPathOfInputDataFile(const llvm::StringRef filename);
 bool runCompilerWithSpirvGeneration(const llvm::StringRef inputFilePath,
 bool runCompilerWithSpirvGeneration(const llvm::StringRef inputFilePath,
                                     const llvm::StringRef entryPoint,
                                     const llvm::StringRef entryPoint,
                                     const llvm::StringRef targetProfile,
                                     const llvm::StringRef targetProfile,
-                                    const llvm::StringRef restArgs,
+                                    const std::vector<std::string> &restArgs,
                                     std::vector<uint32_t> *generatedBinary,
                                     std::vector<uint32_t> *generatedBinary,
                                     std::string *errorMessages);
                                     std::string *errorMessages);
 
 

+ 1 - 1
tools/clang/unittests/SPIRV/WholeFileTestFixture.h

@@ -58,7 +58,7 @@ private:
 
 
   std::string targetProfile;             ///< Target profile (argument of -T)
   std::string targetProfile;             ///< Target profile (argument of -T)
   std::string entryPoint;                ///< Entry point name (argument of -E)
   std::string entryPoint;                ///< Entry point name (argument of -E)
-  std::string restArgs;                  ///< All the other arguments
+  std::vector<std::string> restArgs;     ///< All the other arguments
   std::string inputFilePath;             ///< Path to the input test file
   std::string inputFilePath;             ///< Path to the input test file
   std::vector<uint32_t> generatedBinary; ///< The generated SPIR-V Binary
   std::vector<uint32_t> generatedBinary; ///< The generated SPIR-V Binary
   std::string expectedSpirvAsm;          ///< Expected SPIR-V parsed from input
   std::string expectedSpirvAsm;          ///< Expected SPIR-V parsed from input