[llvm-branch-commits] [llvm] [HLSLSemanticSignatures] Implement the prefix packing of elements (PR #218062)

Helena Kotas via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Aug 28 17:49:56 PDT 2026


================
@@ -12,18 +12,356 @@
 
 #include "llvm/Frontend/HLSL/SemanticSignaturePacking.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/bit.h"
+#include <algorithm>
+#include <array>
 #include <cassert>
+#include <optional>
 
 using namespace llvm;
 using namespace llvm::hlsl;
 
 char SignaturePackingError::ID;
 
+namespace {
+
+// The range of rows covered by a dynamically indexable element. Only an
+// element that covers multiple rows is dynamically indexable, so a single-row
+// element has an empty range.
+struct IndexedRowRange {
+  uint8_t Begin = 0;
+  uint8_t End = 0;
+
+  static IndexedRowRange of(unsigned StartRow, unsigned RowCount) {
+    if (RowCount < 2)
+      return {};
+    return {static_cast<uint8_t>(StartRow),
+            static_cast<uint8_t>(StartRow + RowCount)};
+  }
+
+  bool isEmpty() const { return Begin == End; }
+
+  // An empty range is contained by every range.
+  bool contains(IndexedRowRange Other) const {
+    return Other.isEmpty() || (Begin <= Other.Begin && Other.End <= End);
+  }
+
+  IndexedRowRange unionWith(IndexedRowRange Other) const {
+    if (isEmpty())
+      return Other;
+    if (Other.isEmpty())
+      return *this;
+    return {std::min(Begin, Other.Begin), std::max(End, Other.End)};
+  }
+
+  bool operator==(IndexedRowRange Other) const {
+    return Begin == Other.Begin && End == Other.End;
+  }
+};
+
+static_assert(SemanticInterpretation::Arbitrary < SemanticInterpretation::SV &&
+                  SemanticInterpretation::SV < SemanticInterpretation::SGV &&
+                  SemanticInterpretation::SGV <
+                      SemanticInterpretation::ClipCull &&
+                  SemanticInterpretation::ClipCull <
+                      SemanticInterpretation::TessFactor,
+              "semantic interpretations must be in component packing order");
+
+struct SignatureRow {
+  uint8_t OccupiedColumns = 0;
+  IndexedRowRange IndexedRange;
+  bool IndexedRangeFixed = false;
+  unsigned ComponentWidth = 0;
+  dxbc::PSV::InterpolationMode InterpMode =
+      dxbc::PSV::InterpolationMode::Undefined;
+  SemanticInterpretation RightmostInterpretation =
+      SemanticInterpretation::Arbitrary;
+};
+
+// Everything the packing rules need to know about the element that is being
+// placed. It applies to every row that the element covers.
+struct ElementPlacement {
+  unsigned Rows;
+  unsigned Cols;
+  unsigned ComponentWidth;
+  dxbc::PSV::InterpolationMode InterpMode;
+  SemanticInterpretation Interpretation;
+};
+
+// Clip/cull elements are first packed into an independent two-row grid. Each
+// row used in that grid maps to a whole reserved row in the signature.
+struct ClipCullState {
+  std::array<SignatureRow, MaxClipCullRows> Rows;
+  std::array<unsigned, MaxClipCullRows> SignatureRows = {UnallocatedRow,
+                                                         UnallocatedRow};
+  unsigned RowsUsed = 0;
+};
+
+} // namespace
+
+static uint8_t getStartColumn(uint8_t ColumnMask) {
+  assert(ColumnMask != 0 && "expected at least one occupied column");
+  return countr_zero(ColumnMask);
+}
+
+static unsigned getComponentWidth(dxil::ElementType ComponentType,
+                                  bool UseNative16BitTypes) {
+  assert(ComponentType != dxil::ElementType::I64 &&
+         ComponentType != dxil::ElementType::U64 &&
+         ComponentType != dxil::ElementType::F64 &&
+         ComponentType != dxil::ElementType::SNormF64 &&
+         ComponentType != dxil::ElementType::UNormF64 &&
+         "64-bit types cannot be used in a signature");
+
+  switch (ComponentType) {
+  case dxil::ElementType::F16:
+  case dxil::ElementType::I16:
+  case dxil::ElementType::U16:
+  case dxil::ElementType::SNormF16:
+  case dxil::ElementType::UNormF16:
+    // Without native 16-bit types these are min-precision types that occupy a
+    // whole 32-bit component.
+    return UseNative16BitTypes ? 16 : 32;
+  default:
+    // A boolean is loaded and stored as a 32-bit value.
+    return 32;
+  }
+}
+
+// Returns whether Placement may be co-packed into a Row that it covers, where
+// IndexedRange is the range of rows that it is dynamically indexed over.
+static bool canCoPack(const SignatureRow &Row,
+                      const ElementPlacement &Placement,
+                      IndexedRowRange IndexedRange) {
+  const bool IsSystemValue =
+      Placement.Interpretation == SemanticInterpretation::SV ||
+      Placement.Interpretation == SemanticInterpretation::SGV;
+
+  // A system value is never dynamically indexable, so it cannot be placed in a
+  // row that is.
+  if (IsSystemValue && !Row.IndexedRange.isEmpty())
+    return false;
+
+  // A row whose indexed range is fixed only accepts elements that are indexed
+  // within that range.
+  if (Row.IndexedRangeFixed && !Row.IndexedRange.contains(IndexedRange))
+    return false;
+
+  // A tess factor fixes the indexed range of the rows it is reserved in, so it
+  // may only extend the range that those rows already have.
+  if (Placement.Interpretation == SemanticInterpretation::TessFactor &&
+      !IndexedRange.contains(Row.IndexedRange))
+    return false;
+
+  if (Row.OccupiedColumns && Row.ComponentWidth != Placement.ComponentWidth)
+    return false;
+  if (Row.InterpMode != dxbc::PSV::InterpolationMode::Undefined &&
+      Row.InterpMode != Placement.InterpMode)
+    return false;
+  if (Row.OccupiedColumns &&
+      Placement.Interpretation < Row.RightmostInterpretation &&
+      !(Placement.Interpretation == SemanticInterpretation::Arbitrary &&
+        Row.RightmostInterpretation == SemanticInterpretation::TessFactor))
+    return false;
+  return true;
+}
+
+// Returns the columns that Placement would occupy if it was placed at StartRow,
+// or nullopt if it cannot be placed there.
+static std::optional<uint8_t> canPlaceAt(ArrayRef<SignatureRow> Rows,
+                                         unsigned StartRow,
+                                         const ElementPlacement &Placement) {
+  if (StartRow > Rows.size() || Placement.Rows > Rows.size() - StartRow)
+    return std::nullopt;
----------------
hekota wrote:

```suggestion
  if (StartRow >= Rows.size() || Placement.Rows > Rows.size() - StartRow)
    return std::nullopt;
```
I think this is off by 1.

https://github.com/llvm/llvm-project/pull/218062


More information about the llvm-branch-commits mailing list