[llvm-branch-commits] [llvm] wip: Extract HLSLBinding out of DXILResource (PR #150633)

Justin Bogner via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jul 25 08:32:18 PDT 2025


https://github.com/bogner created https://github.com/llvm/llvm-project/pull/150633

TODO:
- Move unit tests
- Slim API

>From 07535c4db1ed03999b97cf263b086ecc07749efd Mon Sep 17 00:00:00 2001
From: Justin Bogner <mail at justinbogner.com>
Date: Wed, 23 Jul 2025 13:40:01 -0700
Subject: [PATCH] wip: Extract HLSLBinding out of DXILResource

TODO:
- Move unit tests
- Slim API
---
 llvm/include/llvm/Analysis/DXILResource.h     |  82 ++--------
 llvm/include/llvm/Frontend/HLSL/HLSLBinding.h | 154 ++++++++++++++++++
 llvm/lib/Analysis/CMakeLists.txt              |   1 +
 llvm/lib/Analysis/DXILResource.cpp            | 136 +---------------
 llvm/lib/Frontend/HLSL/CMakeLists.txt         |   1 +
 llvm/lib/Frontend/HLSL/HLSLBinding.cpp        | 141 ++++++++++++++++
 .../DirectX/ResourceBindingAnalysisTests.cpp  |  27 +--
 7 files changed, 330 insertions(+), 212 deletions(-)
 create mode 100644 llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
 create mode 100644 llvm/lib/Frontend/HLSL/HLSLBinding.cpp

diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 956dcbcc33305..56f5c6aaf13e7 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -12,6 +12,7 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Frontend/HLSL/HLSLBinding.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/PassManager.h"
@@ -633,86 +634,29 @@ LLVM_ABI ModulePass *createDXILResourceWrapperPassPass();
 // register slots to resources with implicit bindings, and in a
 // post-optimization validation pass that will raise diagnostic about
 // overlapping bindings.
-//
-// For example for these resource bindings:
-//
-// RWBuffer<float> A[10] : register(u3);
-// RWBuffer<float> B[] : register(u5, space2)
-//
-// The analysis result for UAV binding type will look like this:
-//
-// UAVSpaces {
-//   ResClass = ResourceClass::UAV,
-//   Spaces = {
-//     { Space = 0, FreeRanges = {{ 0, 2 }, { 13, UINT32_MAX }} },
-//     { Space = 2, FreeRanges = {{ 0, 4 }} }
-//   }
-// }
-//
 class DXILResourceBindingInfo {
-public:
-  struct BindingRange {
-    uint32_t LowerBound;
-    uint32_t UpperBound;
-    BindingRange(uint32_t LB, uint32_t UB) : LowerBound(LB), UpperBound(UB) {}
-  };
-
-  struct RegisterSpace {
-    uint32_t Space;
-    SmallVector<BindingRange> FreeRanges;
-    RegisterSpace(uint32_t Space) : Space(Space) {
-      FreeRanges.emplace_back(0, UINT32_MAX);
-    }
-    // Size == -1 means unbounded array
-    LLVM_ABI std::optional<uint32_t> findAvailableBinding(int32_t Size);
-  };
-
-  struct BindingSpaces {
-    dxil::ResourceClass RC;
-    llvm::SmallVector<RegisterSpace> Spaces;
-    BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
-    LLVM_ABI RegisterSpace &getOrInsertSpace(uint32_t Space);
-  };
-
-private:
-  BindingSpaces SRVSpaces, UAVSpaces, CBufferSpaces, SamplerSpaces;
-  bool ImplicitBinding;
-  bool OverlappingBinding;
+  hlsl::BindingInfo Bindings;
+  bool HasImplicitBinding = false;
+  bool HasOverlappingBinding = false;
 
   // Populate the resource binding info given explicit resource binding calls
   // in the module.
   void populate(Module &M, DXILResourceTypeMap &DRTM);
 
 public:
-  DXILResourceBindingInfo()
-      : SRVSpaces(dxil::ResourceClass::SRV),
-        UAVSpaces(dxil::ResourceClass::UAV),
-        CBufferSpaces(dxil::ResourceClass::CBuffer),
-        SamplerSpaces(dxil::ResourceClass::Sampler), ImplicitBinding(false),
-        OverlappingBinding(false) {}
-
-  bool hasImplicitBinding() const { return ImplicitBinding; }
-  void setHasImplicitBinding(bool Value) { ImplicitBinding = Value; }
-  bool hasOverlappingBinding() const { return OverlappingBinding; }
-
-  BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
-    switch (RC) {
-    case dxil::ResourceClass::SRV:
-      return SRVSpaces;
-    case dxil::ResourceClass::UAV:
-      return UAVSpaces;
-    case dxil::ResourceClass::CBuffer:
-      return CBufferSpaces;
-    case dxil::ResourceClass::Sampler:
-      return SamplerSpaces;
-    }
+  bool hasImplicitBinding() const { return HasImplicitBinding; }
+  void setHasImplicitBinding(bool Value) { HasImplicitBinding = Value; }
+  bool hasOverlappingBinding() const { return HasOverlappingBinding; }
+  void setHasOverlappingBinding(bool Value) { HasOverlappingBinding = Value; }
 
-    llvm_unreachable("Invalid resource class");
+  hlsl::BindingInfo::BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
+    return Bindings.getBindingSpaces(RC);
   }
 
-  // Size == -1 means unbounded array
   LLVM_ABI std::optional<uint32_t>
-  findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size);
+  findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size) {
+    return Bindings.findAvailableBinding(RC, Space, Size);
+  }
 
   friend class DXILResourceBindingAnalysis;
   friend class DXILResourceBindingWrapperPass;
diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h b/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
new file mode 100644
index 0000000000000..6e4bd8b71ac4a
--- /dev/null
+++ b/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
@@ -0,0 +1,154 @@
+//===- HLSLBinding.h - Representation for resource bindings in HLSL -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file contains objects to represent resource bindings.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FRONTEND_HLSL_HLSLBINDING_H
+#define LLVM_FRONTEND_HLSL_HLSLBINDING_H
+
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/DXILABI.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace llvm {
+namespace hlsl {
+
+/// BindingInfo represents the ranges of bindings and free space for each
+/// `dxil::ResourceClass`. This can represent HLSL-level bindings as well as
+/// bindings described in root signatures, and can be used for analysis of
+/// overlapping or missing bindings as well as for finding space for implicit
+/// bindings.
+///
+/// As an example, given these resource bindings:
+///
+/// RWBuffer<float> A[10] : register(u3);
+/// RWBuffer<float> B[] : register(u5, space2)
+///
+/// The binding info for UAV bindings should look like this:
+///
+/// UAVSpaces {
+///   ResClass = ResourceClass::UAV,
+///   Spaces = {
+///     { Space = 0, FreeRanges = {{ 0, 2 }, { 13, UINT32_MAX }} },
+///     { Space = 2, FreeRanges = {{ 0, 4 }} }
+///   }
+/// }
+class BindingInfo {
+public:
+  struct BindingRange {
+    uint32_t LowerBound;
+    uint32_t UpperBound;
+    BindingRange(uint32_t LB, uint32_t UB) : LowerBound(LB), UpperBound(UB) {}
+  };
+
+  struct RegisterSpace {
+    uint32_t Space;
+    SmallVector<BindingRange> FreeRanges;
+    RegisterSpace(uint32_t Space) : Space(Space) {
+      FreeRanges.emplace_back(0, UINT32_MAX);
+    }
+    // Size == -1 means unbounded array
+    LLVM_ABI std::optional<uint32_t> findAvailableBinding(int32_t Size);
+  };
+
+  struct BindingSpaces {
+    dxil::ResourceClass RC;
+    llvm::SmallVector<RegisterSpace> Spaces;
+    BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
+    LLVM_ABI RegisterSpace &getOrInsertSpace(uint32_t Space);
+  };
+
+private:
+  BindingSpaces SRVSpaces{dxil::ResourceClass::SRV};
+  BindingSpaces UAVSpaces{dxil::ResourceClass::UAV};
+  BindingSpaces CBufferSpaces{dxil::ResourceClass::CBuffer};
+  BindingSpaces SamplerSpaces{dxil::ResourceClass::Sampler};
+
+public:
+  BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
+    switch (RC) {
+    case dxil::ResourceClass::SRV:
+      return SRVSpaces;
+    case dxil::ResourceClass::UAV:
+      return UAVSpaces;
+    case dxil::ResourceClass::CBuffer:
+      return CBufferSpaces;
+    case dxil::ResourceClass::Sampler:
+      return SamplerSpaces;
+    }
+
+    llvm_unreachable("Invalid resource class");
+  }
+  const BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) const {
+    return const_cast<BindingInfo *>(this)->getBindingSpaces(RC);
+  }
+
+  // Size == -1 means unbounded array
+  LLVM_ABI std::optional<uint32_t>
+  findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size);
+
+  friend class BindingInfoBuilder;
+};
+
+/// Builder class for creating a /c BindingInfo.
+class BindingInfoBuilder {
+public:
+  struct Binding {
+    const static uint32_t Unbounded = ~0u;
+
+    dxil::ResourceClass RC;
+    uint32_t Space;
+    uint32_t LowerBound;
+    uint32_t UpperBound;
+    const void *Cookie;
+
+    Binding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
+            uint32_t UpperBound, const void *Cookie)
+        : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),
+          Cookie(Cookie) {}
+
+    bool isUnbounded() const { return UpperBound == Unbounded; }
+
+    bool operator==(const Binding &RHS) const {
+      return std::tie(RC, Space, LowerBound, UpperBound, Cookie) ==
+             std::tie(RHS.RC, RHS.Space, RHS.LowerBound, RHS.UpperBound,
+                      RHS.Cookie);
+    }
+    bool operator!=(const Binding &RHS) const { return !(*this == RHS); }
+
+    bool operator<(const Binding &RHS) const {
+      return std::tie(RC, Space, LowerBound) <
+             std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
+    }
+  };
+
+private:
+  SmallVector<Binding> Bindings;
+
+public:
+  void trackBinding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
+                    uint32_t UpperBound, const void *Cookie) {
+    Bindings.emplace_back(RC, Space, LowerBound, UpperBound, Cookie);
+  }
+  BindingInfo calculateBindingInfo(
+      llvm::function_ref<void(const BindingInfoBuilder &Builder,
+                              const Binding &Overlapping)>
+          ReportOverlap);
+
+  /// For use in the \c ReportOverlap callback of \c calculateBindingInfo -
+  /// finds a binding that the \c ReportedBinding overlaps with.
+  const Binding &findOverlapping(const Binding &ReportedBinding) const;
+};
+
+} // namespace hlsl
+} // namespace llvm
+
+#endif // LLVM_FRONTEND_HLSL_HLSLBINDING_H
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index cfde787b17790..16dd6f8b86006 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -175,6 +175,7 @@ add_llvm_component_library(LLVMAnalysis
   LINK_COMPONENTS
   BinaryFormat
   Core
+  FrontendHLSL
   Object
   ProfileData
   Support
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 179b7b4dc17f9..629fa7cddb9d4 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -995,18 +995,7 @@ SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
 //===----------------------------------------------------------------------===//
 
 void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
-  struct Binding {
-    ResourceClass RC;
-    uint32_t Space;
-    uint32_t LowerBound;
-    uint32_t UpperBound;
-    Value *Name;
-    Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound,
-            uint32_t UpperBound, Value *Name)
-        : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),
-          Name(Name) {}
-  };
-  SmallVector<Binding> Bindings;
+  hlsl::BindingInfoBuilder Builder;
 
   // collect all of the llvm.dx.resource.handlefrombinding calls;
   // make a note if there is llvm.dx.resource.handlefromimplicitbinding
@@ -1036,133 +1025,20 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
           assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) &&
                  "upper bound register overflow");
           uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;
-          Bindings.emplace_back(RTI.getResourceClass(), Space, LowerBound,
-                                UpperBound, Name);
+          Builder.trackBinding(RTI.getResourceClass(), Space, LowerBound,
+                               UpperBound, Name);
         }
       break;
     }
     case Intrinsic::dx_resource_handlefromimplicitbinding: {
-      ImplicitBinding = true;
+      HasImplicitBinding = true;
       break;
     }
     }
   }
 
-  // sort all the collected bindings
-  llvm::stable_sort(Bindings, [](auto &LHS, auto &RHS) {
-    return std::tie(LHS.RC, LHS.Space, LHS.LowerBound) <
-           std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
-  });
-
-  // remove duplicates
-  Binding *NewEnd = llvm::unique(Bindings, [](auto &LHS, auto &RHS) {
-    return std::tie(LHS.RC, LHS.Space, LHS.LowerBound, LHS.UpperBound,
-                    LHS.Name) == std::tie(RHS.RC, RHS.Space, RHS.LowerBound,
-                                          RHS.UpperBound, RHS.Name);
-  });
-  if (NewEnd != Bindings.end())
-    Bindings.erase(NewEnd);
-
-  // Go over the sorted bindings and build up lists of free register ranges
-  // for each binding type and used spaces. Bindings are sorted by resource
-  // class, space, and lower bound register slot.
-  BindingSpaces *BS = &SRVSpaces;
-  for (const Binding &B : Bindings) {
-    if (BS->RC != B.RC)
-      // move to the next resource class spaces
-      BS = &getBindingSpaces(B.RC);
-
-    RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(B.Space)
-                                          : &BS->Spaces.back();
-    assert(S->Space <= B.Space && "bindings not sorted correctly?");
-    if (B.Space != S->Space)
-      // add new space
-      S = &BS->Spaces.emplace_back(B.Space);
-
-    // The space is full - there are no free slots left, or the rest of the
-    // slots are taken by an unbouded array. Set flag to report overlapping
-    // binding later.
-    if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < UINT32_MAX) {
-      OverlappingBinding = true;
-      continue;
-    }
-
-    // adjust the last free range lower bound, split it in two, or remove it
-    BindingRange &LastFreeRange = S->FreeRanges.back();
-    if (LastFreeRange.LowerBound == B.LowerBound) {
-      if (B.UpperBound < UINT32_MAX)
-        LastFreeRange.LowerBound = B.UpperBound + 1;
-      else
-        S->FreeRanges.pop_back();
-    } else if (LastFreeRange.LowerBound < B.LowerBound) {
-      LastFreeRange.UpperBound = B.LowerBound - 1;
-      if (B.UpperBound < UINT32_MAX)
-        S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);
-    } else {
-      OverlappingBinding = true;
-      if (B.UpperBound < UINT32_MAX)
-        LastFreeRange.LowerBound =
-            std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
-      else
-        S->FreeRanges.pop_back();
-    }
-  }
-}
-
-// returns std::nulopt if binding could not be found in given space
-std::optional<uint32_t>
-DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
-                                              uint32_t Space, int32_t Size) {
-  BindingSpaces &BS = getBindingSpaces(RC);
-  RegisterSpace &RS = BS.getOrInsertSpace(Space);
-  return RS.findAvailableBinding(Size);
-}
-
-DXILResourceBindingInfo::RegisterSpace &
-DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
-  for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
-    if (I->Space == Space)
-      return *I;
-    if (I->Space < Space)
-      continue;
-    return *Spaces.insert(I, Space);
-  }
-  return Spaces.emplace_back(Space);
-}
-
-std::optional<uint32_t>
-DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
-  assert((Size == -1 || Size > 0) && "invalid size");
-
-  if (FreeRanges.empty())
-    return std::nullopt;
-
-  // unbounded array
-  if (Size == -1) {
-    BindingRange &Last = FreeRanges.back();
-    if (Last.UpperBound != UINT32_MAX)
-      // this space is already occupied by an unbounded array
-      return std::nullopt;
-    uint32_t RegSlot = Last.LowerBound;
-    FreeRanges.pop_back();
-    return RegSlot;
-  }
-
-  // single resource or fixed-size array
-  for (BindingRange &R : FreeRanges) {
-    // compare the size as uint64_t to prevent overflow for range (0,
-    // UINT32_MAX)
-    if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
-      continue;
-    uint32_t RegSlot = R.LowerBound;
-    // This might create a range where (LowerBound == UpperBound + 1). When
-    // that happens, the next time this function is called the range will
-    // skipped over by the check above (at this point Size is always > 0).
-    R.LowerBound += Size;
-    return RegSlot;
-  }
-
-  return std::nullopt;
+  Bindings = Builder.calculateBindingInfo(
+      [this](auto, auto) { this->HasOverlappingBinding = true; });
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Frontend/HLSL/CMakeLists.txt b/llvm/lib/Frontend/HLSL/CMakeLists.txt
index 534346920ff19..3d225770e8d5b 100644
--- a/llvm/lib/Frontend/HLSL/CMakeLists.txt
+++ b/llvm/lib/Frontend/HLSL/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_llvm_component_library(LLVMFrontendHLSL
   CBuffer.cpp
+  HLSLBinding.cpp
   HLSLResource.cpp
   HLSLRootSignature.cpp
   RootSignatureMetadata.cpp
diff --git a/llvm/lib/Frontend/HLSL/HLSLBinding.cpp b/llvm/lib/Frontend/HLSL/HLSLBinding.cpp
new file mode 100644
index 0000000000000..c2ae4a44bb5d0
--- /dev/null
+++ b/llvm/lib/Frontend/HLSL/HLSLBinding.cpp
@@ -0,0 +1,141 @@
+//===- HLSLBinding.cpp - Representation for resource bindings in HLSL -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Frontend/HLSL/HLSLBinding.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace llvm;
+using namespace hlsl;
+
+std::optional<uint32_t>
+BindingInfo::findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
+                                  int32_t Size) {
+  BindingSpaces &BS = getBindingSpaces(RC);
+  RegisterSpace &RS = BS.getOrInsertSpace(Space);
+  return RS.findAvailableBinding(Size);
+}
+
+BindingInfo::RegisterSpace &
+BindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
+  for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
+    if (I->Space == Space)
+      return *I;
+    if (I->Space < Space)
+      continue;
+    return *Spaces.insert(I, Space);
+  }
+  return Spaces.emplace_back(Space);
+}
+
+std::optional<uint32_t>
+BindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
+  assert((Size == -1 || Size > 0) && "invalid size");
+
+  if (FreeRanges.empty())
+    return std::nullopt;
+
+  // unbounded array
+  if (Size == -1) {
+    BindingRange &Last = FreeRanges.back();
+    if (Last.UpperBound != UINT32_MAX)
+      // this space is already occupied by an unbounded array
+      return std::nullopt;
+    uint32_t RegSlot = Last.LowerBound;
+    FreeRanges.pop_back();
+    return RegSlot;
+  }
+
+  // single resource or fixed-size array
+  for (BindingRange &R : FreeRanges) {
+    // compare the size as uint64_t to prevent overflow for range (0,
+    // UINT32_MAX)
+    if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
+      continue;
+    uint32_t RegSlot = R.LowerBound;
+    // This might create a range where (LowerBound == UpperBound + 1). When
+    // that happens, the next time this function is called the range will
+    // skipped over by the check above (at this point Size is always > 0).
+    R.LowerBound += Size;
+    return RegSlot;
+  }
+
+  return std::nullopt;
+}
+
+BindingInfo BindingInfoBuilder::calculateBindingInfo(
+    llvm::function_ref<void(const BindingInfoBuilder &Builder,
+                            const Binding &Overlapping)>
+        ReportOverlap) {
+  // sort all the collected bindings
+  llvm::stable_sort(Bindings);
+
+  // remove duplicates
+  Binding *NewEnd = llvm::unique(Bindings);
+  if (NewEnd != Bindings.end())
+    Bindings.erase(NewEnd);
+
+  BindingInfo Info;
+
+  // Go over the sorted bindings and build up lists of free register ranges
+  // for each binding type and used spaces. Bindings are sorted by resource
+  // class, space, and lower bound register slot.
+  BindingInfo::BindingSpaces *BS =
+      &Info.getBindingSpaces(dxil::ResourceClass::SRV);
+  for (const Binding &B : Bindings) {
+    if (BS->RC != B.RC)
+      // move to the next resource class spaces
+      BS = &Info.getBindingSpaces(B.RC);
+
+    BindingInfo::RegisterSpace *S = BS->Spaces.empty()
+                                        ? &BS->Spaces.emplace_back(B.Space)
+                                        : &BS->Spaces.back();
+    assert(S->Space <= B.Space && "bindings not sorted correctly?");
+    if (B.Space != S->Space)
+      // add new space
+      S = &BS->Spaces.emplace_back(B.Space);
+
+    // The space is full - there are no free slots left, or the rest of the
+    // slots are taken by an unbounded array. Set flag to report overlapping
+    // binding later.
+    if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < UINT32_MAX) {
+      ReportOverlap(*this, B);
+      continue;
+    }
+    // adjust the last free range lower bound, split it in two, or remove it
+    BindingInfo::BindingRange &LastFreeRange = S->FreeRanges.back();
+    if (LastFreeRange.LowerBound == B.LowerBound) {
+      if (B.UpperBound < UINT32_MAX)
+        LastFreeRange.LowerBound = B.UpperBound + 1;
+      else
+        S->FreeRanges.pop_back();
+    } else if (LastFreeRange.LowerBound < B.LowerBound) {
+      LastFreeRange.UpperBound = B.LowerBound - 1;
+      if (B.UpperBound < UINT32_MAX)
+        S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);
+    } else {
+      ReportOverlap(*this, B);
+      if (B.UpperBound < UINT32_MAX)
+        LastFreeRange.LowerBound =
+            std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
+      else
+        S->FreeRanges.pop_back();
+    }
+  }
+
+  return Info;
+}
+
+const BindingInfoBuilder::Binding &BindingInfoBuilder::findOverlapping(
+    const BindingInfoBuilder::Binding &ReportedBinding) const {
+  for (const BindingInfoBuilder::Binding &Other : Bindings)
+    if (ReportedBinding.LowerBound <= Other.UpperBound &&
+        Other.LowerBound <= ReportedBinding.UpperBound)
+      return Other;
+
+  llvm_unreachable("Searching for overlap for binding that does not overlap");
+}
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
index 6cd1a48d8d5fd..7cd83bedd6140 100644
--- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -45,9 +45,10 @@ class ResourceBindingAnalysisTest : public testing::Test {
     delete Context;
   }
 
-  void checkExpectedSpaceAndFreeRanges(
-      DXILResourceBindingInfo::RegisterSpace &RegSpace, uint32_t ExpSpace,
-      ArrayRef<uint32_t> ExpValues) {
+  void
+  checkExpectedSpaceAndFreeRanges(hlsl::BindingInfo::RegisterSpace &RegSpace,
+                                  uint32_t ExpSpace,
+                                  ArrayRef<uint32_t> ExpValues) {
     EXPECT_EQ(RegSpace.Space, ExpSpace);
     EXPECT_EQ(RegSpace.FreeRanges.size() * 2, ExpValues.size());
     unsigned I = 0;
@@ -78,7 +79,7 @@ define void @main() {
   EXPECT_EQ(false, DRBI.hasOverlappingBinding());
 
   // check that UAV has exactly one gap
-  DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+  hlsl::BindingInfo::BindingSpaces &UAVSpaces =
       DRBI.getBindingSpaces(ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
@@ -88,7 +89,7 @@ define void @main() {
   // check that other kinds of register spaces are all available
   for (auto RC :
        {ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) {
-    DXILResourceBindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
+    hlsl::BindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
     EXPECT_EQ(Spaces.RC, RC);
     EXPECT_EQ(Spaces.Spaces.size(), 0u);
   }
@@ -129,7 +130,7 @@ define void @main() {
   EXPECT_EQ(false, DRBI.hasImplicitBinding());
   EXPECT_EQ(false, DRBI.hasOverlappingBinding());
 
-  DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+  hlsl::BindingInfo::BindingSpaces &SRVSpaces =
       DRBI.getBindingSpaces(ResourceClass::SRV);
   EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
   EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
@@ -137,7 +138,7 @@ define void @main() {
   // (SRVSpaces has only one free space range {6, UINT32_MAX}).
   checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6, UINT32_MAX});
 
-  DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+  hlsl::BindingInfo::BindingSpaces &UAVSpaces =
       DRBI.getBindingSpaces(ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.Spaces.size(), 2u);
@@ -146,14 +147,14 @@ define void @main() {
   checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20,
                                   {0, 9, 15, UINT32_MAX});
 
-  DXILResourceBindingInfo::BindingSpaces &CBufferSpaces =
+  hlsl::BindingInfo::BindingSpaces &CBufferSpaces =
       DRBI.getBindingSpaces(ResourceClass::CBuffer);
   EXPECT_EQ(CBufferSpaces.RC, ResourceClass::CBuffer);
   EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u);
   checkExpectedSpaceAndFreeRanges(CBufferSpaces.Spaces[0], 0,
                                   {0, 2, 4, UINT32_MAX});
 
-  DXILResourceBindingInfo::BindingSpaces &SamplerSpaces =
+  hlsl::BindingInfo::BindingSpaces &SamplerSpaces =
       DRBI.getBindingSpaces(ResourceClass::Sampler);
   EXPECT_EQ(SamplerSpaces.RC, ResourceClass::Sampler);
   EXPECT_EQ(SamplerSpaces.Spaces.size(), 1u);
@@ -185,7 +186,7 @@ define void @main() {
   EXPECT_EQ(false, DRBI.hasImplicitBinding());
   EXPECT_EQ(true, DRBI.hasOverlappingBinding());
 
-  DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+  hlsl::BindingInfo::BindingSpaces &SRVSpaces =
       DRBI.getBindingSpaces(ResourceClass::SRV);
   EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
   EXPECT_EQ(SRVSpaces.Spaces.size(), 2u);
@@ -215,7 +216,7 @@ define void @main() {
   EXPECT_EQ(false, DRBI.hasImplicitBinding());
   EXPECT_EQ(true, DRBI.hasOverlappingBinding());
 
-  DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+  hlsl::BindingInfo::BindingSpaces &SRVSpaces =
       DRBI.getBindingSpaces(ResourceClass::SRV);
   EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
   EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
@@ -248,7 +249,7 @@ define void @main() {
   EXPECT_EQ(false, DRBI.hasImplicitBinding());
   EXPECT_EQ(false, DRBI.hasOverlappingBinding());
 
-  DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+  hlsl::BindingInfo::BindingSpaces &UAVSpaces =
       DRBI.getBindingSpaces(ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.Spaces.size(), 3u);
@@ -278,7 +279,7 @@ define void @main() {
   EXPECT_EQ(true, DRBI.hasImplicitBinding());
   EXPECT_EQ(false, DRBI.hasOverlappingBinding());
 
-  DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+  hlsl::BindingInfo::BindingSpaces &UAVSpaces =
       DRBI.getBindingSpaces(ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
   EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);



More information about the llvm-branch-commits mailing list