[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