[Mlir-commits] [mlir] [mlir][sparse] Reject dense level after non-unique level in encoding verifier (PR #184157)

Mehdi Amini llvmlistbot at llvm.org
Mon Mar 2 07:42:54 PST 2026


https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/184157

The sparse tensor iteration model for dense levels requires exactly one parent position to linearize into a contiguous range. However, when a non-unique level (e.g. compressed(nonunique)) precedes a dense level, the DedupIterator provides a two-element cursor {posLo, segHi}, causing DenseLevel::peekRangeAt to assert and crash with:

  Assertion `parentPos.size() == 1 && "Dense level can not be non-unique."'

Fix this by adding a check in SparseTensorEncodingAttr::verify that rejects any encoding where a dense level directly follows a non-unique level, emitting a proper diagnostic instead of crashing during lowering.

Fixes #130008

>From edd7e0c82c2a7fa587c5d3f27528271ff8ac5274 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Mon, 2 Mar 2026 07:29:05 -0800
Subject: [PATCH] [mlir][sparse] Reject dense level after non-unique level in
 encoding verifier

The sparse tensor iteration model for dense levels requires exactly one
parent position to linearize into a contiguous range. However, when a
non-unique level (e.g. compressed(nonunique)) precedes a dense level, the
DedupIterator provides a two-element cursor {posLo, segHi}, causing
DenseLevel::peekRangeAt to assert and crash with:

  Assertion `parentPos.size() == 1 && "Dense level can not be non-unique."'

Fix this by adding a check in SparseTensorEncodingAttr::verify that
rejects any encoding where a dense level directly follows a non-unique
level, emitting a proper diagnostic instead of crashing during lowering.

Fixes #130008
---
 .../Dialect/SparseTensor/IR/SparseTensorDialect.cpp   |  9 +++++++++
 mlir/test/Dialect/SparseTensor/invalid_encoding.mlir  | 11 +++++++++++
 2 files changed, 20 insertions(+)

diff --git a/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp b/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp
index 3a34ad90941b0..05b2e07b88ba5 100644
--- a/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp
+++ b/mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp
@@ -826,6 +826,15 @@ LogicalResult SparseTensorEncodingAttr::verify(
     return emitError() << "SoA is only applicable to singleton lvlTypes.";
   }
 
+  // Dense levels cannot follow a non-unique level. The iteration model for
+  // dense levels requires exactly one parent position to linearize into a
+  // contiguous range, but a non-unique parent provides two cursor values
+  // (segment start and end), which the dense level cannot handle.
+  for (auto [i, lt] : llvm::drop_begin(llvm::enumerate(lvlTypes))) {
+    if (isDenseLT(lt) && !isUniqueLT(lvlTypes[i - 1]))
+      return emitError() << "dense level cannot follow a non-unique level";
+  }
+
   // TODO: audit formats that actually are supported by backend.
   if (auto it = llvm::find_if(lvlTypes, isNOutOfMLT);
       it != std::end(lvlTypes)) {
diff --git a/mlir/test/Dialect/SparseTensor/invalid_encoding.mlir b/mlir/test/Dialect/SparseTensor/invalid_encoding.mlir
index a3f72bd3ae971..0d0234149fb3e 100644
--- a/mlir/test/Dialect/SparseTensor/invalid_encoding.mlir
+++ b/mlir/test/Dialect/SparseTensor/invalid_encoding.mlir
@@ -256,6 +256,17 @@ func.func private @sparse_coo(tensor<?x?xf32, #COO_SoA>)
 
 // -----
 
+// expected-error at +1{{dense level cannot follow a non-unique level}}
+#COO_Dense = #sparse_tensor.encoding<{
+  map = (i, j) -> (
+    i : compressed(nonunique),
+    j : dense
+  )
+}>
+func.func private @dense_after_nonunique(tensor<?x?xf32, #COO_Dense>)
+
+// -----
+
 // expected-error at +2 {{use of undeclared identifier 'l1'}}
 #TooFewLvlDecl = #sparse_tensor.encoding<{
   map = {l0} (d0, d1) -> (l0 = d0 : dense, l1 = d1 : compressed)



More information about the Mlir-commits mailing list