Răsfoiți Sursa

[spirv] Respect the -auto-binding-space option in SPIR-V backend (#2211)

* [spirv] Respect the -auto-binding-space option in SPIR-V backend.

* [spirv] Use default space if vk::binding doesn't provide dset.

* Address code review comments.
Ehsan 6 ani în urmă
părinte
comite
101730c57b

+ 9 - 6
docs/SPIR-V.rst

@@ -1452,11 +1452,13 @@ 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 as ``Y`` and binding number as ``X``. The descriptor set number
 descriptor set as ``Y`` and binding number as ``X``. The descriptor set number
-is optional; if missing, it will be zero. RW/append/consume structured buffers
-have associated counters, which will occupy their own Vulkan descriptors.
-``[vk::counter_binding(Z)]`` can be attached to a RW/append/consume structured
-buffers to specify the binding number for the associated counter to ``Z``. Note
-that the set number of the counter is always the same as the main buffer.
+is optional; if missing, it will be zero (If ``-auto-binding-space N`` command
+line option is used, then descriptor set #N will be used instead of descriptor
+set #0). RW/append/consume structured buffers have associated counters, which
+will occupy their own Vulkan descriptors. ``[vk::counter_binding(Z)]`` can be
+attached to a RW/append/consume structured buffers to specify the binding number
+for the associated counter to ``Z``. Note that the set number of the counter is
+always the same as the main buffer.
 
 
 Implicit binding number assignment
 Implicit binding number assignment
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1476,7 +1478,8 @@ 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 (If ``-auto-binding-space N`` command line option is used, then
+descriptor set #N will be used instead of descriptor set #0).
 
 
 Summary
 Summary
 ~~~~~~~
 ~~~~~~~

+ 36 - 17
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -28,6 +28,15 @@ namespace spirv {
 
 
 namespace {
 namespace {
 
 
+uint32_t getVkBindingAttrSet(const VKBindingAttr *attr, uint32_t defaultSet) {
+  // If the [[vk::binding(x)]] attribute is provided without the descriptor set,
+  // we should use the default descriptor set.
+  if (attr->getSet() == INT_MIN) {
+    return defaultSet;
+  }
+  return attr->getSet();
+}
+
 /// Returns the :packoffset() annotation on the given decl. Returns nullptr if
 /// Returns the :packoffset() annotation on the given decl. Returns nullptr if
 /// the decl does not have one.
 /// the decl does not have one.
 hlsl::ConstantPacking *getPackOffset(const clang::NamedDecl *decl) {
 hlsl::ConstantPacking *getPackOffset(const clang::NamedDecl *decl) {
@@ -1383,11 +1392,11 @@ public:
 
 
   /// Returns true and set the correct set and binding number if we can find a
   /// Returns true and set the correct set and binding number if we can find a
   /// descriptor setting for the given register. False otherwise.
   /// descriptor setting for the given register. False otherwise.
-  bool getSetBinding(const hlsl::RegisterAssignment *regAttr, int *setNo,
-                     int *bindNo) const {
+  bool getSetBinding(const hlsl::RegisterAssignment *regAttr,
+                     uint32_t defaultSpace, int *setNo, int *bindNo) const {
     std::ostringstream iss;
     std::ostringstream iss;
-    iss << regAttr->RegisterSpace.getValueOr(0) << regAttr->RegisterType
-        << regAttr->RegisterNumber;
+    iss << regAttr->RegisterSpace.getValueOr(defaultSpace)
+        << regAttr->RegisterType << regAttr->RegisterNumber;
 
 
     auto found = mapping.find(iss.str());
     auto found = mapping.find(iss.str());
     if (found != mapping.end()) {
     if (found != mapping.end()) {
@@ -1424,6 +1433,13 @@ bool DeclResultIdMapper::decorateResourceBindings() {
   // - m2
   // - m2
   // - m3, m4, mX * c2
   // - m3, m4, mX * c2
 
 
+  // The "-auto-binding-space" command line option can be used to specify a
+  // certain space as default. UINT_MAX means the user has not provided this
+  // option. If not provided, the SPIR-V backend uses space "0" as default.
+  auto defaultSpaceOpt =
+      theEmitter.getCompilerInstance().getCodeGenOpts().HLSLDefaultSpace;
+  uint32_t defaultSpace = (defaultSpaceOpt == UINT_MAX) ? 0 : defaultSpaceOpt;
+
   const bool bindGlobals = !spirvOptions.bindGlobals.empty();
   const bool bindGlobals = !spirvOptions.bindGlobals.empty();
   int32_t globalsBindNo = -1, globalsSetNo = -1;
   int32_t globalsBindNo = -1, globalsSetNo = -1;
   if (bindGlobals) {
   if (bindGlobals) {
@@ -1459,11 +1475,12 @@ bool DeclResultIdMapper::decorateResourceBindings() {
       if (const auto *regAttr = var.getRegister()) {
       if (const auto *regAttr = var.getRegister()) {
         if (var.isCounter()) {
         if (var.isCounter()) {
           emitError("-fvk-bind-register for RW/Append/Consume StructuredBuffer "
           emitError("-fvk-bind-register for RW/Append/Consume StructuredBuffer "
-                    "umimplemented",
+                    "unimplemented",
                     var.getSourceLocation());
                     var.getSourceLocation());
         } else {
         } else {
           int setNo = 0, bindNo = 0;
           int setNo = 0, bindNo = 0;
-          if (!bindingMapper.getSetBinding(regAttr, &setNo, &bindNo)) {
+          if (!bindingMapper.getSetBinding(regAttr, defaultSpace, &setNo,
+                                           &bindNo)) {
             emitError("missing -fvk-bind-register for resource",
             emitError("missing -fvk-bind-register for resource",
                       var.getSourceLocation());
                       var.getSourceLocation());
             return false;
             return false;
@@ -1498,18 +1515,19 @@ bool DeclResultIdMapper::decorateResourceBindings() {
     if (var.isCounter()) {
     if (var.isCounter()) {
       if (const auto *vkCBinding = var.getCounterBinding()) {
       if (const auto *vkCBinding = var.getCounterBinding()) {
         // Process mX * c1
         // Process mX * c1
-        uint32_t set = 0;
+        uint32_t set = defaultSpace;
         if (const auto *vkBinding = var.getBinding())
         if (const auto *vkBinding = var.getBinding())
-          set = vkBinding->getSet();
+          set = getVkBindingAttrSet(vkBinding, defaultSpace);
         else if (const auto *reg = var.getRegister())
         else if (const auto *reg = var.getRegister())
-          set = reg->RegisterSpace.getValueOr(0);
+          set = reg->RegisterSpace.getValueOr(defaultSpace);
 
 
         tryToDecorate(var.getSpirvInstr(), set, vkCBinding->getBinding());
         tryToDecorate(var.getSpirvInstr(), set, vkCBinding->getBinding());
       }
       }
     } else {
     } else {
       if (const auto *vkBinding = var.getBinding()) {
       if (const auto *vkBinding = var.getBinding()) {
         // Process m1
         // Process m1
-        tryToDecorate(var.getSpirvInstr(), vkBinding->getSet(),
+        tryToDecorate(var.getSpirvInstr(),
+                      getVkBindingAttrSet(vkBinding, defaultSpace),
                       vkBinding->getBinding());
                       vkBinding->getBinding());
       }
       }
     }
     }
@@ -1528,7 +1546,7 @@ bool DeclResultIdMapper::decorateResourceBindings() {
         if (reg->isSpaceOnly())
         if (reg->isSpaceOnly())
           continue;
           continue;
 
 
-        const uint32_t set = reg->RegisterSpace.getValueOr(0);
+        const uint32_t set = reg->RegisterSpace.getValueOr(defaultSpace);
         uint32_t binding = reg->RegisterNumber;
         uint32_t binding = reg->RegisterNumber;
         switch (reg->RegisterType) {
         switch (reg->RegisterType) {
         case 'b':
         case 'b':
@@ -1557,11 +1575,11 @@ bool DeclResultIdMapper::decorateResourceBindings() {
     if (var.isCounter()) {
     if (var.isCounter()) {
       if (!var.getCounterBinding()) {
       if (!var.getCounterBinding()) {
         // Process mX * c2
         // Process mX * c2
-        uint32_t set = 0;
+        uint32_t set = defaultSpace;
         if (const auto *vkBinding = var.getBinding())
         if (const auto *vkBinding = var.getBinding())
-          set = vkBinding->getSet();
+          set = getVkBindingAttrSet(vkBinding, defaultSpace);
         else if (const auto *reg = var.getRegister())
         else if (const auto *reg = var.getRegister())
-          set = reg->RegisterSpace.getValueOr(0);
+          set = reg->RegisterSpace.getValueOr(defaultSpace);
 
 
         spvBuilder.decorateDSetBinding(var.getSpirvInstr(), set,
         spvBuilder.decorateDSetBinding(var.getSpirvInstr(), set,
                                        bindingSet.useNextBinding(set));
                                        bindingSet.useNextBinding(set));
@@ -1569,7 +1587,7 @@ bool DeclResultIdMapper::decorateResourceBindings() {
     } else if (!var.getBinding()) {
     } else if (!var.getBinding()) {
       const auto *reg = var.getRegister();
       const auto *reg = var.getRegister();
       if (reg && reg->isSpaceOnly()) {
       if (reg && reg->isSpaceOnly()) {
-        const uint32_t set = reg->RegisterSpace.getValueOr(0);
+        const uint32_t set = reg->RegisterSpace.getValueOr(defaultSpace);
         spvBuilder.decorateDSetBinding(var.getSpirvInstr(), set,
         spvBuilder.decorateDSetBinding(var.getSpirvInstr(), set,
                                        bindingSet.useNextBinding(set));
                                        bindingSet.useNextBinding(set));
       } else if (!reg) {
       } else if (!reg) {
@@ -1584,8 +1602,9 @@ bool DeclResultIdMapper::decorateResourceBindings() {
         }
         }
         // The normal case
         // The normal case
         else {
         else {
-          spvBuilder.decorateDSetBinding(var.getSpirvInstr(), 0,
-                                         bindingSet.useNextBinding(0));
+          spvBuilder.decorateDSetBinding(
+              var.getSpirvInstr(), defaultSpace,
+              bindingSet.useNextBinding(defaultSpace));
         }
         }
       }
       }
     }
     }

+ 1 - 0
tools/clang/lib/SPIRV/SpirvEmitter.h

@@ -51,6 +51,7 @@ public:
   ASTContext &getASTContext() { return astContext; }
   ASTContext &getASTContext() { return astContext; }
   SpirvBuilder &getSpirvBuilder() { return spvBuilder; }
   SpirvBuilder &getSpirvBuilder() { return spvBuilder; }
   DiagnosticsEngine &getDiagnosticsEngine() { return diags; }
   DiagnosticsEngine &getDiagnosticsEngine() { return diags; }
+  CompilerInstance &getCompilerInstance() { return theCompilerInstance; }
 
 
   void doDecl(const Decl *decl);
   void doDecl(const Decl *decl);
   void doStmt(const Stmt *stmt, llvm::ArrayRef<const Attr *> attrs = {});
   void doStmt(const Stmt *stmt, llvm::ArrayRef<const Attr *> attrs = {});

+ 4 - 3
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -11029,9 +11029,10 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
       ValidateAttributeIntArg(S, A), A.getAttributeSpellingListIndex());
       ValidateAttributeIntArg(S, A), A.getAttributeSpellingListIndex());
     break;
     break;
   case AttributeList::AT_VKBinding:
   case AttributeList::AT_VKBinding:
-    declAttr = ::new (S.Context) VKBindingAttr(A.getRange(), S.Context,
-      ValidateAttributeIntArg(S, A), ValidateAttributeIntArg(S, A, 1),
-      A.getAttributeSpellingListIndex());
+    declAttr = ::new (S.Context) VKBindingAttr(
+        A.getRange(), S.Context, ValidateAttributeIntArg(S, A),
+        A.getNumArgs() < 2 ? INT_MIN : ValidateAttributeIntArg(S, A, 1),
+        A.getAttributeSpellingListIndex());
     break;
     break;
   case AttributeList::AT_VKCounterBinding:
   case AttributeList::AT_VKCounterBinding:
     declAttr = ::new (S.Context) VKCounterBindingAttr(A.getRange(), S.Context,
     declAttr = ::new (S.Context) VKCounterBindingAttr(A.getRange(), S.Context,

+ 1 - 1
tools/clang/test/CodeGenSPIRV/vk.binding.cl.register.counter.hlsl

@@ -7,4 +7,4 @@ float4 main() : SV_Target {
   return MyBuffer[0].val;
   return MyBuffer[0].val;
 }
 }
 
 
-// CHECK: :4:24: error: -fvk-bind-register for RW/Append/Consume StructuredBuffer umimplemented
+// CHECK: :4:24: error: -fvk-bind-register for RW/Append/Consume StructuredBuffer unimplemented

+ 35 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.default-space.explicit.hlsl

@@ -0,0 +1,35 @@
+// Run: %dxc -T ps_6_0 -E main -auto-binding-space 77
+
+// Since both the descriptor set and binding are mentioned via "[[vk::binding]]",
+// the "-auto-binding-space" has no effect:
+//
+// CHECK:      OpDecorate %sampler1 DescriptorSet 6
+// CHECK-NEXT: OpDecorate %sampler1 Binding 5
+[[vk::binding(5,6)]]
+SamplerState sampler1 : register(s1, space2);
+
+// Since the space is NOT provided in "[[vk::binding]]",
+// we use the default space specified by "-auto-binding-space"
+//
+// CHECK:      OpDecorate %sampler2 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %sampler2 Binding 8
+[[vk::binding(8)]]
+SamplerState sampler2 : register(s4);
+
+// Since both the descriptor set and binding are mentioned via ":register",
+// the "-auto-binding-space" has no effect:
+//
+// CHECK:      OpDecorate %sampler3 DescriptorSet 9
+// CHECK-NEXT: OpDecorate %sampler3 Binding 1
+SamplerState sampler3 : register(s1, space9);
+
+// Since the space is NOT provided in ":register",
+// we use the default space specified by "-auto-binding-space"
+//
+// CHECK:      OpDecorate %sampler4 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %sampler4 Binding 3
+SamplerState sampler4 : register(s3);
+
+float4 main() : SV_Target {
+  return 1.0;
+}

+ 64 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.default-space.implicit.hlsl

@@ -0,0 +1,64 @@
+// Run: %dxc -T ps_6_0 -E main -auto-binding-space 77
+
+// CHECK:      OpDecorate %sampler1 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %sampler1 Binding 0
+SamplerState sampler1;
+
+// CHECK:      OpDecorate %texture1 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %texture1 Binding 1
+Texture2D<float4> texture1;
+
+// CHECK:      OpDecorate %texture2 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %texture2 Binding 2
+Texture3D<float4> texture2;
+
+// CHECK:      OpDecorate %sampler2 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %sampler2 Binding 3
+SamplerState sampler2;
+
+// CHECK:      OpDecorate %myCbuffer DescriptorSet 77
+// CHECK-NEXT: OpDecorate %myCbuffer Binding 4
+cbuffer myCbuffer {
+    float4 stuff;
+}
+
+// CHECK:      OpDecorate %myBuffer DescriptorSet 77
+// CHECK-NEXT: OpDecorate %myBuffer Binding 5
+Buffer<int> myBuffer;
+
+// CHECK:      OpDecorate %myRWBuffer DescriptorSet 77
+// CHECK-NEXT: OpDecorate %myRWBuffer Binding 6
+RWBuffer<float4> myRWBuffer;
+
+struct S {
+    float4 f;
+};
+
+// CHECK:      OpDecorate %myCbuffer2 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %myCbuffer2 Binding 7
+ConstantBuffer<S> myCbuffer2;
+
+// CHECK:      OpDecorate %sbuffer1 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %sbuffer1 Binding 8
+  StructuredBuffer<S> sbuffer1;
+// CHECK:      OpDecorate %sbuffer2 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %sbuffer2 Binding 9
+// CHECK-NEXT: OpDecorate %counter_var_sbuffer2 DescriptorSet 77
+// CHECK-NEXT: OpDecorate %counter_var_sbuffer2 Binding 10
+RWStructuredBuffer<S> sbuffer2;
+
+// CHECK:      OpDecorate %abuffer DescriptorSet 77
+// CHECK-NEXT: OpDecorate %abuffer Binding 11
+// CHECK-NEXT: OpDecorate %counter_var_abuffer DescriptorSet 77
+// CHECK-NEXT: OpDecorate %counter_var_abuffer Binding 12
+AppendStructuredBuffer<S> abuffer;
+
+// CHECK:      OpDecorate %csbuffer DescriptorSet 77
+// CHECK-NEXT: OpDecorate %csbuffer Binding 13
+// CHECK-NEXT: OpDecorate %counter_var_csbuffer DescriptorSet 77
+// CHECK-NEXT: OpDecorate %counter_var_csbuffer Binding 14
+ConsumeStructuredBuffer<S> csbuffer;
+
+float4 main() : SV_Target {
+    return 1.0;
+}

+ 54 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.default-space.with-shift-all.hlsl

@@ -0,0 +1,54 @@
+// Run: %dxc -T ps_6_0 -E main -auto-binding-space 77 -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 by the shift, but the default
+// space is used when the descriptor set is not provided in vk::binding.
+
+// CHECK: OpDecorate %cbuffer3 DescriptorSet 77
+// CHECK: OpDecorate %cbuffer3 Binding 42
+[[vk::binding(42)]]
+ConstantBuffer<S> cbuffer3 : register(b10, space2);
+
+// CHECK: OpDecorate %cbuffer1 DescriptorSet 77
+// 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 77
+// CHECK: OpDecorate %texture2 Binding 2001
+Texture2D<float4> texture2: register(t1);
+
+// CHECK: OpDecorate %sampler1 DescriptorSet 77
+// 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 77
+// CHECK: OpDecorate %rwbuffer2 Binding 4003
+RWBuffer<float4> rwbuffer2 : register(u3);
+
+// Lacking binding assignment is unaffacted.
+
+// CHECK: OpDecorate %cbuffer4 DescriptorSet 77
+// CHECK: OpDecorate %cbuffer4 Binding 0
+ConstantBuffer<S> cbuffer4;
+// CHECK: OpDecorate %cbuffer5 DescriptorSet 77
+// CHECK: OpDecorate %cbuffer5 Binding 1
+ConstantBuffer<S> cbuffer5;
+
+float4 main() : SV_Target {
+    return cbuffer1.f;
+}

+ 57 - 0
tools/clang/test/CodeGenSPIRV/vk.binding.default-space.with-shift.hlsl

@@ -0,0 +1,57 @@
+// Run: %dxc -T ps_6_0 -E main -auto-binding-space 77 -fvk-b-shift 100 77 -fvk-b-shift 200 2 -fvk-t-shift 400 77 -fvk-t-shift 300 1 -fvk-s-shift 500 77 -fvk-s-shift 600 2 -fvk-u-shift 700 77 -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 by the shift,
+// but the default space is used if no space is provided by vk::binding.
+
+// CHECK: OpDecorate %cbuffer3 DescriptorSet 77
+// CHECK: OpDecorate %cbuffer3 Binding 42
+[[vk::binding(42)]]
+ConstantBuffer<S> cbuffer3 : register(b10, space2);
+
+// CHECK: OpDecorate %cbuffer1 DescriptorSet 77
+// 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 77
+// CHECK: OpDecorate %texture2 Binding 401
+Texture2D<float4> texture2: register(t1);
+
+// CHECK: OpDecorate %sampler1 DescriptorSet 77
+// CHECK: OpDecorate %sampler1 Binding 500
+SamplerState sampler1: register(s0);
+// CHECK: OpDecorate %sampler2 DescriptorSet 2
+// CHECK: OpDecorate %sampler2 Binding 600
+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 77
+// CHECK: OpDecorate %rwbuffer2 Binding 703
+RWBuffer<float4> rwbuffer2 : register(u3);
+
+// Lacking binding assignment is unaffacted by the shift.
+
+// CHECK: OpDecorate %cbuffer4 DescriptorSet 77
+// CHECK: OpDecorate %cbuffer4 Binding 0
+ConstantBuffer<S> cbuffer4;
+// CHECK: OpDecorate %cbuffer5 DescriptorSet 77
+// CHECK: OpDecorate %cbuffer5 Binding 1
+ConstantBuffer<S> cbuffer5;
+
+float4 main() : SV_Target {
+    return cbuffer1.f;
+}

+ 17 - 0
tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp

@@ -1577,6 +1577,23 @@ TEST_F(FileTest, VulkanRegisterBinding1to1MappingMissingCLOption) {
 TEST_F(FileTest, VulkanRegisterBinding1to1MappingAssociatedCounter) {
 TEST_F(FileTest, VulkanRegisterBinding1to1MappingAssociatedCounter) {
   runFileTest("vk.binding.cl.register.counter.hlsl", Expect::Failure);
   runFileTest("vk.binding.cl.register.counter.hlsl", Expect::Failure);
 }
 }
+
+// For testing the "-auto-binding-space" command line option which specifies the
+// "default space" for resources.
+TEST_F(FileTest, VulkanRegisterBindingDefaultSpaceImplicit) {
+  runFileTest("vk.binding.default-space.implicit.hlsl");
+}
+TEST_F(FileTest, VulkanRegisterBindingDefaultSpaceExplicit) {
+  runFileTest("vk.binding.default-space.explicit.hlsl");
+}
+// Testing combinations of "-auto-binding-space" and other options.
+TEST_F(FileTest, VulkanRegisterBindingDefaultSpaceWithShift) {
+  runFileTest("vk.binding.default-space.with-shift.hlsl");
+}
+TEST_F(FileTest, VulkanRegisterBindingDefaultSpaceWithShiftAll) {
+  runFileTest("vk.binding.default-space.with-shift-all.hlsl");
+}
+
 TEST_F(FileTest, VulkanStructuredBufferCounter) {
 TEST_F(FileTest, VulkanStructuredBufferCounter) {
   // [[vk::counter_binding()]] for RWStructuredBuffer, AppendStructuredBuffer,
   // [[vk::counter_binding()]] for RWStructuredBuffer, AppendStructuredBuffer,
   // and ConsumeStructuredBuffer
   // and ConsumeStructuredBuffer