[llvm] [HLSLSemanticSignatures] Implement the optimal packing of elements (PR #218064)

Helena Kotas via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 21:03:00 PDT 2026


================
@@ -1547,6 +1795,239 @@ TEST_F(HLSLSemanticSignaturePackingTest,
   }
 }
 
+TEST_F(HLSLSemanticSignaturePackingTest, OptimizedPreservesPartialAllocation) {
+  // Declaration order is Small, Full, Last. Optimized packing places Full
+  // first, then fails on Small. Report Small's original index (zero), not its
+  // index in packing order (one). Preserve Full's allocation for ordinary
+  // elements, but leave the entire clip/cull phase unallocated on failure.
+  //
+  // struct VSOut {
+  //   float2 Small    : Small;
+  //   float4 Full[32] : Full;
+  //   float Last      : Last;
+  // };
+  // The clip/cull case uses clip-distance semantics and Full[2] instead.
+  for (bool IsClipCull : {false, true}) {
+    SCOPED_TRACE(IsClipCull);
+    const auto Kind = IsClipCull ? dxbc::PSV::SemanticKind::ClipDistance
+                                 : dxbc::PSV::SemanticKind::Arbitrary;
+    const unsigned RowCount = IsClipCull ? MaxClipCullRows : MaxSignatureRows;
+    TestConfig Config(
+        Triple::Vertex, IOType::Out,
+        {{Kind, /*Rows=*/1, /*Cols=*/2, dxil::ElementType::F32,
+          dxbc::PSV::InterpolationMode::Linear},
+         {Kind, RowCount, /*Cols=*/MaxSignatureCols, dxil::ElementType::F32,
+          dxbc::PSV::InterpolationMode::Linear},
+         {Kind, /*Rows=*/1, /*Cols=*/1, dxil::ElementType::F32,
+          dxbc::PSV::InterpolationMode::Linear}});
+    verifyPackingError(PackingMethod::Optimized, Config,
+                       IsClipCull ? SignaturePackingError::ClipCullOverflow
+                                  : SignaturePackingError::SignatureOverflow,
+                       /*ExpectedElementIndex=*/0);
+
+    SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
+    EXPECT_THAT_EXPECTED(
+        pack(PackingMethod::Optimized, Elements, Config),
+        FailedWithMessage(
+            std::string(IsClipCull ? "clip/cull elements do not fit in "
+                                   : "signature elements do not fit in ") +
+            std::to_string(RowCount) + " rows (element 0)"));
+    EXPECT_EQ(Elements[1].StartRow, IsClipCull ? UnallocatedRow : 0u);
+    EXPECT_EQ(Elements[1].StartCol, IsClipCull ? UnallocatedCol : 0u);
+    for (unsigned I : {0u, 2u}) {
+      EXPECT_EQ(Elements[I].StartRow, UnallocatedRow) << "element " << I;
+      EXPECT_EQ(Elements[I].StartCol, UnallocatedCol) << "element " << I;
+    }
+    for (unsigned I = 0; I != Elements.size(); ++I) {
+      EXPECT_EQ(Elements[I].SigId, I) << "element " << I;
+      EXPECT_EQ(Elements[I].Rows, Config.Elements[I].Rows) << "element " << I;
+      EXPECT_EQ(Elements[I].Cols, Config.Elements[I].Cols) << "element " << I;
+    }
+  }
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest,
+       OptimizedSingleRowClipCullFailureIsAtomic) {
+  // struct VSOut {
+  //   float2 Clip1    : SV_ClipDistance1;
+  //   float4 Fill[31] : FILL;
+  //   float Cull      : SV_CullDistance0;
+  //   float3 Clip0    : SV_ClipDistance0;
+  // };
+  // The first bundle (Clip0 + Cull) fits in row 31, but Clip1's bundle fails.
+  // Neither bundle is published; only the preceding Fill allocation remains.
+  TestConfig Config(
+      Triple::Vertex, IOType::Out,
+      {{dxbc::PSV::SemanticKind::ClipDistance, /*Rows=*/1, /*Cols=*/2,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear,
+        /*SemanticIndex=*/1},
+       {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/MaxSignatureRows - 1,
+        /*Cols=*/4, dxil::ElementType::F32,
+        dxbc::PSV::InterpolationMode::Linear},
+       {dxbc::PSV::SemanticKind::CullDistance, /*Rows=*/1, /*Cols=*/1,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+       {dxbc::PSV::SemanticKind::ClipDistance, /*Rows=*/1, /*Cols=*/3,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear}});
+  verifyPackingError(PackingMethod::Optimized, Config,
+                     SignaturePackingError::SignatureOverflow,
+                     /*ExpectedElementIndex=*/0);
+  SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
+  const SmallVector<SemanticSignatureElement> Before = Elements;
+  EXPECT_THAT_EXPECTED(pack(PackingMethod::Optimized, Elements, Config),
+                       FailedWithMessage("signature elements do not fit in " +
+                                         std::to_string(MaxSignatureRows) +
+                                         " rows (element 0)"));
+  EXPECT_EQ(Elements[1].StartRow, 0u);
+  EXPECT_EQ(Elements[1].StartCol, 0u);
+  for (unsigned I : {0u, 2u, 3u}) {
+    EXPECT_EQ(Elements[I].StartRow, UnallocatedRow) << "element " << I;
+    EXPECT_EQ(Elements[I].StartCol, UnallocatedCol) << "element " << I;
+  }
+  for (unsigned I = 0; I != Elements.size(); ++I) {
+    SCOPED_TRACE(I);
+    verifyMetadata(Before[I], Elements[I]);
+  }
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest,
+       OptimizedIndexedClipCullFailureIsAtomic) {
+  // struct VSOut {
+  //   float2 Cull     : SV_CullDistance0;
+  //   float4 Fill[30] : FILL;
+  //   float A[2]      : A;
+  //   float2 Clip[2]  : SV_ClipDistance0;
+  // };
+  // The final pair fits Clip in yz but not Cull.xy. Do not retain Clip's
+  // speculative allocation. The error identifies Clip, first in group order.
+  TestConfig Config(
+      Triple::Vertex, IOType::Out,
+      {{dxbc::PSV::SemanticKind::CullDistance, /*Rows=*/1, /*Cols=*/2,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+       {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/MaxSignatureRows - 2,
+        /*Cols=*/4, dxil::ElementType::F32,
+        dxbc::PSV::InterpolationMode::Linear},
+       {dxbc::PSV::SemanticKind::Arbitrary, /*Rows=*/2, /*Cols=*/1,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear},
+       {dxbc::PSV::SemanticKind::ClipDistance, /*Rows=*/2, /*Cols=*/2,
+        dxil::ElementType::F32, dxbc::PSV::InterpolationMode::Linear}});
+  verifyPackingError(PackingMethod::Optimized, Config,
+                     SignaturePackingError::SignatureOverflow,
+                     /*ExpectedElementIndex=*/3);
+  SmallVector<SemanticSignatureElement> Elements = makeSignature(Config);
+  const SmallVector<SemanticSignatureElement> Before = Elements;
+  EXPECT_THAT_EXPECTED(pack(PackingMethod::Optimized, Elements, Config),
+                       FailedWithMessage("signature elements do not fit in " +
+                                         std::to_string(MaxSignatureRows) +
+                                         " rows (element 3)"));
+  EXPECT_EQ(Elements[1].StartRow, 0u);
+  EXPECT_EQ(Elements[1].StartCol, 0u);
+  EXPECT_EQ(Elements[2].StartRow, MaxSignatureRows - 2);
+  EXPECT_EQ(Elements[2].StartCol, 0u);
+  for (unsigned I : {0u, 3u}) {
+    EXPECT_EQ(Elements[I].StartRow, UnallocatedRow) << "element " << I;
+    EXPECT_EQ(Elements[I].StartCol, UnallocatedCol) << "element " << I;
+  }
+  for (unsigned I = 0; I != Elements.size(); ++I) {
+    SCOPED_TRACE(I);
+    verifyMetadata(Before[I], Elements[I]);
+  }
+}
+
+TEST_F(HLSLSemanticSignaturePackingTest,
+       OptimizedClipCullFailureIsAtomicAcrossStreams) {
+  const struct {
+    unsigned Rows;
+    unsigned Stream;
+    SignaturePackingError::ErrorKind Kind;
+  } Failures[] = {
+      {1, 1, SignaturePackingError::SignatureOverflow},
+      {3, 1, SignaturePackingError::ClipCullOverflow},
+      {1, MaxGeometryStreams, SignaturePackingError::InvalidGeometryStream}};
+  for (const auto &Failure : Failures) {
----------------
hekota wrote:

Could you please add a comment that shows what the struct to be packed looks like? It is missing here and in many other tests below.

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


More information about the llvm-commits mailing list