[Mlir-commits] [mlir] [mlir] Decouple DenseResourceElementsAttr from BuiltinDialect (PR #191486)

Pavel Prokofyev llvmlistbot at llvm.org
Fri Apr 10 12:27:03 PDT 2026


https://github.com/integralpro updated https://github.com/llvm/llvm-project/pull/191486

>From 539e77894f7f033fa99c35b2930d842467385dea Mon Sep 17 00:00:00 2001
From: Pavel Prokofyev <pprokofyev at apple.com>
Date: Fri, 10 Apr 2026 11:22:43 -0700
Subject: [PATCH] [mlir] Decouple DenseResourceElementsAttr from BuiltinDialect

DenseResourceElementsAttr was hardcoded to use BuiltinDialect's blob
manager via the DenseResourceElementsHandle type alias
(DialectResourceBlobHandle<BuiltinDialect>). This prevented custom
dialects from owning resource blobs referenced by this attribute.

Introduce DenseResourceBlobHandle, a dialect-generic handle that wraps
(BlobEntry*, Dialect*) without being templated on a specific dialect.
The attribute's internal parameter is renamed from rawHandle to
resourceHandle and now stores DenseResourceBlobHandle, with a new
getResourceHandle() accessor. The legacy getRawHandle() is preserved
and asserts that the owning dialect is BuiltinDialect.

New factory: DenseResourceElementsAttr::get(type, blobName, blob, dialect)
allows inserting a resource into any dialect's blob manager.

Parser/printer extended with "dialect::key" syntax for non-builtin
resources (e.g. dense_resource<"test::blob1">), while bare keys
continue to default to BuiltinDialect for backward compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply at anthropic.com>
---
 mlir/include/mlir/IR/BuiltinAttributes.h      |  1 +
 mlir/include/mlir/IR/BuiltinAttributes.td     | 38 ++++++++++--
 .../include/mlir/IR/BuiltinDialectBytecode.td |  4 +-
 .../mlir/IR/DialectResourceBlobManager.h      | 49 +++++++++++++++
 mlir/lib/AsmParser/AttributeParser.cpp        | 59 ++++++++++++++++---
 mlir/lib/IR/AsmPrinter.cpp                    | 15 ++++-
 mlir/lib/IR/BuiltinAttributes.cpp             | 30 +++++++++-
 mlir/lib/IR/BuiltinDialect.cpp                |  2 +-
 .../dense-resource-custom-dialect.mlir        | 33 +++++++++++
 .../IR/dense-resource-custom-dialect.mlir     | 32 ++++++++++
 mlir/unittests/IR/BlobManagerTest.cpp         | 34 +++++++++++
 11 files changed, 275 insertions(+), 22 deletions(-)
 create mode 100644 mlir/test/Bytecode/dense-resource-custom-dialect.mlir
 create mode 100644 mlir/test/IR/dense-resource-custom-dialect.mlir

diff --git a/mlir/include/mlir/IR/BuiltinAttributes.h b/mlir/include/mlir/IR/BuiltinAttributes.h
index 1f805882db2764..baa1609b6bacff 100644
--- a/mlir/include/mlir/IR/BuiltinAttributes.h
+++ b/mlir/include/mlir/IR/BuiltinAttributes.h
@@ -21,6 +21,7 @@ class AsmResourceBlob;
 class BoolAttr;
 class BuiltinDialect;
 class DenseIntElementsAttr;
+class DenseResourceBlobHandle;
 template <typename T>
 struct DialectResourceBlobHandle;
 class FlatSymbolRefAttr;
diff --git a/mlir/include/mlir/IR/BuiltinAttributes.td b/mlir/include/mlir/IR/BuiltinAttributes.td
index 299200788136a8..8cdb843c86670d 100644
--- a/mlir/include/mlir/IR/BuiltinAttributes.td
+++ b/mlir/include/mlir/IR/BuiltinAttributes.td
@@ -474,16 +474,16 @@ def Builtin_DenseResourceElementsAttr : Builtin_Attr<"DenseResourceElements",
     ```
 
     A dense resource elements attribute is an elements attribute backed by a
-    handle to a builtin dialect resource containing a densely packed array of
-    values. This class provides the low-level attribute, which should only be
+    handle to a dialect resource containing a densely packed array of values.
+    The resource blob may be owned by any dialect, not just BuiltinDialect.
+    This class provides the low-level attribute, which should only be
     interacted with in very generic terms, actual access to the underlying
     resource data is intended to be managed through one of the subclasses, such
     as; `DenseBoolResourceElementsAttr`, `DenseUI64ResourceElementsAttr`,
     `DenseI32ResourceElementsAttr`, `DenseF32ResourceElementsAttr`,
     `DenseF64ResourceElementsAttr`, etc.
 
-    Examples:
-
+    For resources owned by BuiltinDialect (the default):
     ```mlir
     "example.user_op"() {attr = dense_resource<blob1> : tensor<3xi64> } : () -> ()
 
@@ -495,14 +495,27 @@ def Builtin_DenseResourceElementsAttr : Builtin_Attr<"DenseResourceElements",
       }
     #-}
     ```
+
+    For resources owned by a custom dialect:
+    ```mlir
+    "example.user_op"() {attr = dense_resource<"my_dialect::blob1"> : tensor<3xi64> } : () -> ()
+
+    {-#
+    dialect_resources: {
+        my_dialect: {
+          blob1: "0x08000000010000000000000002000000000000000300000000000000"
+        }
+      }
+    #-}
+    ```
   }];
   let parameters = (ins
     AttributeSelfTypeParameter<"", "ShapedType">:$type,
-    ResourceHandleParameter<"DenseResourceElementsHandle">:$rawHandle
+    ResourceHandleParameter<"DenseResourceBlobHandle">:$resourceHandle
   );
   let builders = [
     AttrBuilderWithInferredContext<(ins
-      "ShapedType":$type, "DenseResourceElementsHandle":$handle
+      "ShapedType":$type, "DenseResourceBlobHandle":$handle
     )>,
     /// A builder that inserts a new resource into the builtin dialect's blob
     /// manager using the provided blob. The handle of the inserted blob is used
@@ -514,11 +527,24 @@ def Builtin_DenseResourceElementsAttr : Builtin_Attr<"DenseResourceElements",
     /// generic operations.
     AttrBuilderWithInferredContext<(ins
       "ShapedType":$type, "StringRef":$blobName, "AsmResourceBlob":$blob
+    )>,
+    /// A builder that inserts a new resource into the specified dialect's blob
+    /// manager. The dialect must have registered a
+    /// ResourceBlobManagerDialectInterface.
+    AttrBuilderWithInferredContext<(ins
+      "ShapedType":$type, "StringRef":$blobName, "AsmResourceBlob":$blob,
+      "Dialect *":$dialect
     )>
   ];
   let extraClassDeclaration = [{
     /// BlobAttrInterface method.
     ArrayRef<char> getData();
+
+    /// Legacy accessor. Returns the handle cast to DenseResourceElementsHandle
+    /// (DialectResourceBlobHandle<BuiltinDialect>). Asserts that the owning
+    /// dialect is BuiltinDialect; use getResourceHandle() for dialect-generic
+    /// access.
+    DenseResourceElementsHandle getRawHandle() const;
   }];
 
   let skipDefaultBuilders = 1;
diff --git a/mlir/include/mlir/IR/BuiltinDialectBytecode.td b/mlir/include/mlir/IR/BuiltinDialectBytecode.td
index c97d093c84e51f..2d1fb44e5b0bc1 100644
--- a/mlir/include/mlir/IR/BuiltinDialectBytecode.td
+++ b/mlir/include/mlir/IR/BuiltinDialectBytecode.td
@@ -145,10 +145,10 @@ def UnknownLoc : DialectAttribute<(attr)>;
 
 def DenseResourceElementsAttr : DialectAttribute<(attr
   ShapedType:$type,
-  ResourceHandle<"DenseResourceElementsHandle">:$rawHandle
+  ResourceHandle<"DenseResourceBlobHandle">:$resourceHandle
 )> {
   // Note: order of serialization does not match order of builder.
-  let cBuilder = "getChecked<$_resultType>([&]() { return reader.emitError(); }, context, type, *rawHandle)";
+  let cBuilder = "getChecked<$_resultType>([&]() { return reader.emitError(); }, context, type, *resourceHandle)";
 }
 
 let cType = "RankedTensorType" in {
diff --git a/mlir/include/mlir/IR/DialectResourceBlobManager.h b/mlir/include/mlir/IR/DialectResourceBlobManager.h
index 6c30efde306e40..b2e9bf45e379b1 100644
--- a/mlir/include/mlir/IR/DialectResourceBlobManager.h
+++ b/mlir/include/mlir/IR/DialectResourceBlobManager.h
@@ -216,6 +216,55 @@ struct DialectResourceBlobHandle
   }
 };
 
+//===----------------------------------------------------------------------===//
+// DenseResourceBlobHandle
+//===----------------------------------------------------------------------===//
+
+/// A dialect-generic handle to a resource blob entry. Unlike
+/// DialectResourceBlobHandle<T>, this is not parameterized on a specific
+/// dialect, allowing it to reference resource blobs owned by any dialect.
+/// This is used by DenseResourceElementsAttr to decouple the attribute from
+/// BuiltinDialect.
+class DenseResourceBlobHandle : public AsmDialectResourceHandle {
+public:
+  using BlobEntry = DialectResourceBlobManager::BlobEntry;
+
+  DenseResourceBlobHandle() = default;
+
+  /// Direct construction with an explicit dialect owner. Uses its own TypeID.
+  DenseResourceBlobHandle(BlobEntry *entry, Dialect *dialect)
+      : AsmDialectResourceHandle(entry, TypeID::get<DenseResourceBlobHandle>(),
+                                 dialect) {}
+
+  /// Implicit conversion from any dialect-specific blob handle. Preserves the
+  /// original handle's TypeID so that dyn_cast to the original type still
+  /// works.
+  template <typename DialectT>
+  DenseResourceBlobHandle(DialectResourceBlobHandle<DialectT> handle)
+      : AsmDialectResourceHandle(handle) {}
+
+  /// Construct from a type-erased base handle (used by the bytecode reader).
+  DenseResourceBlobHandle(AsmDialectResourceHandle handle)
+      : AsmDialectResourceHandle(handle) {}
+
+  /// Return the underlying blob entry.
+  BlobEntry *getEntry() const {
+    return static_cast<BlobEntry *>(AsmDialectResourceHandle::getResource());
+  }
+
+  /// Return the human-readable string key for this handle.
+  StringRef getKey() const { return getEntry()->getKey(); }
+
+  /// Return the blob referenced by this handle, or nullptr if uninitialized.
+  AsmResourceBlob *getBlob() { return getEntry()->getBlob(); }
+  const AsmResourceBlob *getBlob() const { return getEntry()->getBlob(); }
+
+  /// DenseResourceBlobHandle is a type-erased wrapper that accepts any
+  /// resource handle. Code needing dialect-specific checks should use
+  /// dyn_cast<DialectResourceBlobHandle<SpecificDialect>> instead.
+  static bool classof(const AsmDialectResourceHandle *) { return true; }
+};
+
 } // namespace mlir
 
 #endif // MLIR_IR_DIALECTRESOURCEBLOBMANAGER_H
diff --git a/mlir/lib/AsmParser/AttributeParser.cpp b/mlir/lib/AsmParser/AttributeParser.cpp
index d7075b795ccb9d..85a68e9b36d056 100644
--- a/mlir/lib/AsmParser/AttributeParser.cpp
+++ b/mlir/lib/AsmParser/AttributeParser.cpp
@@ -1103,15 +1103,56 @@ Attribute Parser::parseDenseResourceElementsAttr(Type attrType) {
   if (parseToken(Token::less, "expected '<' after 'dense_resource'"))
     return nullptr;
 
-  // Parse the resource handle.
-  FailureOr<AsmDialectResourceHandle> rawHandle =
-      parseResourceHandle(getContext()->getLoadedDialect<BuiltinDialect>());
-  if (failed(rawHandle) || parseToken(Token::greater, "expected '>'"))
-    return nullptr;
+  // Parse the resource key. This may be a bare identifier or a quoted string.
+  // If the key contains "::", the prefix identifies the dialect that owns the
+  // resource blob; otherwise BuiltinDialect is used as the default.
+  std::string keyStr;
+  if (failed(parseOptionalKeywordOrString(&keyStr)))
+    return emitError("expected resource key for 'dense_resource'"), nullptr;
+
+  Dialect *dialect = nullptr;
+  StringRef blobKey = keyStr;
+  size_t sepPos = keyStr.find("::");
+  if (sepPos != std::string::npos) {
+    StringRef dialectName = StringRef(keyStr).take_front(sepPos);
+    blobKey = StringRef(keyStr).drop_front(sepPos + 2);
+    dialect = getContext()->getLoadedDialect(dialectName);
+    if (!dialect)
+      return emitError(loc, "unknown dialect '" + dialectName +
+                                "' in dense_resource handle"),
+             nullptr;
+  } else {
+    dialect = getContext()->getOrLoadDialect<BuiltinDialect>();
+  }
+
+  // Resolve the resource handle through the dialect's OpAsmDialectInterface.
+  const auto *interface = dyn_cast<OpAsmDialectInterface>(dialect);
+  if (!interface)
+    return emitError(loc, "dialect '" + dialect->getNamespace() +
+                              "' does not support resource handles"),
+           nullptr;
 
-  auto *handle = dyn_cast<DenseResourceElementsHandle>(&*rawHandle);
-  if (!handle)
-    return emitError(loc, "invalid `dense_resource` handle type"), nullptr;
+  // Declare the resource and cache the handle.
+  auto &resources = getState().symbols.dialectResources;
+  std::string resolvedKey(blobKey);
+  std::pair<std::string, AsmDialectResourceHandle> &entry =
+      resources[interface][resolvedKey];
+  if (entry.first.empty()) {
+    FailureOr<AsmDialectResourceHandle> result =
+        interface->declareResource(blobKey);
+    if (failed(result))
+      return emitError(loc, "unknown 'resource' key '" + Twine(blobKey) +
+                                "' for dialect '" + dialect->getNamespace() +
+                                "'"),
+             nullptr;
+    entry.first = interface->getResourceKey(*result);
+    entry.second = *result;
+  }
+
+  DenseResourceBlobHandle handle(entry.second);
+
+  if (parseToken(Token::greater, "expected '>'"))
+    return nullptr;
 
   // Parse the type of the attribute if the user didn't provide one.
   SMLoc typeLoc = loc;
@@ -1127,7 +1168,7 @@ Attribute Parser::parseDenseResourceElementsAttr(Type attrType) {
     return nullptr;
   }
 
-  return DenseResourceElementsAttr::get(shapedType, *handle);
+  return DenseResourceElementsAttr::get(shapedType, handle);
 }
 
 /// Shaped type for elements attribute.
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index 75008d6cc2591c..8a6cbaecc8764b 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -2572,7 +2572,20 @@ void AsmPrinter::Impl::printAttributeImpl(Attribute attr,
   } else if (auto resourceAttr =
                  llvm::dyn_cast<DenseResourceElementsAttr>(attr)) {
     os << "dense_resource<";
-    printResourceHandle(resourceAttr.getRawHandle());
+    auto handle = resourceAttr.getResourceHandle();
+    Dialect *dialect = handle.getDialect();
+    if (!llvm::isa<BuiltinDialect>(dialect)) {
+      // Print as a quoted "dialect::key" string for non-builtin dialects.
+      os << '"';
+      llvm::printEscapedString(
+          (dialect->getNamespace() + "::" + handle.getKey()).str(), os);
+      os << '"';
+      // Register the resource with the correct dialect for the resource
+      // section.
+      state.getDialectResources()[dialect].insert(handle);
+    } else {
+      printResourceHandle(handle);
+    }
     os << ">";
   } else if (auto locAttr = llvm::dyn_cast<LocationAttr>(attr)) {
     printLocation(locAttr);
diff --git a/mlir/lib/IR/BuiltinAttributes.cpp b/mlir/lib/IR/BuiltinAttributes.cpp
index c06ae5b1786243..0811493a2d1f05 100644
--- a/mlir/lib/IR/BuiltinAttributes.cpp
+++ b/mlir/lib/IR/BuiltinAttributes.cpp
@@ -1396,7 +1396,7 @@ bool DenseIntElementsAttr::classof(Attribute attr) {
 
 DenseResourceElementsAttr
 DenseResourceElementsAttr::get(ShapedType type,
-                               DenseResourceElementsHandle handle) {
+                               DenseResourceBlobHandle handle) {
   return Base::get(type.getContext(), type, handle);
 }
 
@@ -1410,8 +1410,32 @@ DenseResourceElementsAttr DenseResourceElementsAttr::get(ShapedType type,
   return get(type, manager.insert(blobName, std::move(blob)));
 }
 
+DenseResourceElementsAttr DenseResourceElementsAttr::get(ShapedType type,
+                                                         StringRef blobName,
+                                                         AsmResourceBlob blob,
+                                                         Dialect *dialect) {
+  assert(dialect && "dialect must not be null");
+  auto *iface =
+      dialect->getRegisteredInterface<ResourceBlobManagerDialectInterface>();
+  assert(iface &&
+         "dialect does not provide ResourceBlobManagerDialectInterface");
+  auto &blobMgr = iface->getBlobManager();
+  auto &entry = blobMgr.insert(blobName, std::move(blob));
+  return get(type, DenseResourceBlobHandle(&entry, dialect));
+}
+
+DenseResourceElementsHandle DenseResourceElementsAttr::getRawHandle() const {
+  auto handle = getResourceHandle();
+  assert(isa<DenseResourceElementsHandle>(
+             static_cast<AsmDialectResourceHandle>(handle)) &&
+         "getRawHandle() called on non-BuiltinDialect resource; use "
+         "getResourceHandle() instead");
+  return DenseResourceElementsHandle(
+      static_cast<AsmDialectResourceHandle>(handle));
+}
+
 ArrayRef<char> DenseResourceElementsAttr::getData() {
-  if (AsmResourceBlob *blob = this->getRawHandle().getBlob())
+  if (AsmResourceBlob *blob = this->getResourceHandle().getBlob())
     return blob->getDataAs<char>();
   return {};
 }
@@ -1492,7 +1516,7 @@ DenseResourceElementsAttrBase<T>::get(ShapedType type, StringRef blobName,
 template <typename T>
 std::optional<ArrayRef<T>>
 DenseResourceElementsAttrBase<T>::tryGetAsArrayRef() const {
-  if (AsmResourceBlob *blob = this->getRawHandle().getBlob())
+  if (AsmResourceBlob *blob = this->getResourceHandle().getBlob())
     return blob->template getDataAs<T>();
   return std::nullopt;
 }
diff --git a/mlir/lib/IR/BuiltinDialect.cpp b/mlir/lib/IR/BuiltinDialect.cpp
index c88b3282822757..4a5499cd7207b8 100644
--- a/mlir/lib/IR/BuiltinDialect.cpp
+++ b/mlir/lib/IR/BuiltinDialect.cpp
@@ -74,7 +74,7 @@ struct BuiltinOpAsmDialectInterface : public OpAsmDialectInterface {
 
   std::string
   getResourceKey(const AsmDialectResourceHandle &handle) const override {
-    return cast<DenseResourceElementsHandle>(handle).getKey().str();
+    return DenseResourceBlobHandle(handle).getKey().str();
   }
   FailureOr<AsmDialectResourceHandle>
   declareResource(StringRef key) const final {
diff --git a/mlir/test/Bytecode/dense-resource-custom-dialect.mlir b/mlir/test/Bytecode/dense-resource-custom-dialect.mlir
new file mode 100644
index 00000000000000..38b88c348bd362
--- /dev/null
+++ b/mlir/test/Bytecode/dense-resource-custom-dialect.mlir
@@ -0,0 +1,33 @@
+// Verify text -> bytecode -> text roundtrip for dense_resource with custom
+// dialect resource handles.
+
+// RUN: mlir-opt -emit-bytecode %s | mlir-opt | FileCheck %s
+
+// CHECK-LABEL: @CustomDialectResources
+module @CustomDialectResources attributes {
+  // Custom dialect resource uses "dialect::key" syntax.
+  // CHECK: bytecode.test1 = dense_resource<"test::custom_blob"> : tensor<3xi64>
+  bytecode.test1 = dense_resource<"test::custom_blob"> : tensor<3xi64>,
+
+  // Builtin dialect resource uses bare key syntax (backward compatible).
+  // CHECK: bytecode.test2 = dense_resource<builtin_blob> : tensor<2xi32>
+  bytecode.test2 = dense_resource<builtin_blob> : tensor<2xi32>
+} {}
+
+// Verify resource data roundtrips correctly for both dialects.
+// CHECK: test: {
+// CHECK-NEXT: custom_blob: "0x08000000010000000000000002000000000000000300000000000000"
+
+// CHECK: builtin: {
+// CHECK-NEXT: builtin_blob: "0x04000000AABBCCDDAABBCCDD"
+
+{-#
+  dialect_resources: {
+    test: {
+      custom_blob: "0x08000000010000000000000002000000000000000300000000000000"
+    },
+    builtin: {
+      builtin_blob: "0x04000000AABBCCDDAABBCCDD"
+    }
+  }
+#-}
diff --git a/mlir/test/IR/dense-resource-custom-dialect.mlir b/mlir/test/IR/dense-resource-custom-dialect.mlir
new file mode 100644
index 00000000000000..26c12c7df73467
--- /dev/null
+++ b/mlir/test/IR/dense-resource-custom-dialect.mlir
@@ -0,0 +1,32 @@
+// Verify text -> text roundtrip.
+// RUN: mlir-opt %s -verify-diagnostics -split-input-file | FileCheck %s
+
+// Test that dense_resource can reference blobs owned by a custom dialect using
+// the "dialect::key" syntax.
+
+// CHECK: attr = dense_resource<"test::blob1"> : tensor<3xi64>
+"test.op_with_result_shape"() {attr = dense_resource<"test::blob1"> : tensor<3xi64> } : () -> ()
+
+{-#
+  dialect_resources: {
+    test: {
+      // CHECK: blob1: "0x08000000010000000000000002000000000000000300000000000000"
+      blob1: "0x08000000010000000000000002000000000000000300000000000000"
+    }
+  }
+#-}
+
+// -----
+
+// Verify that BuiltinDialect resources still work without a prefix.
+// CHECK: attr = dense_resource<blob2> : tensor<2xi32>
+"test.op_with_result_shape"() {attr = dense_resource<blob2> : tensor<2xi32> } : () -> ()
+
+{-#
+  dialect_resources: {
+    builtin: {
+      // CHECK: blob2: "0x0400000001000000020000000300000004000000"
+      blob2: "0x0400000001000000020000000300000004000000"
+    }
+  }
+#-}
diff --git a/mlir/unittests/IR/BlobManagerTest.cpp b/mlir/unittests/IR/BlobManagerTest.cpp
index d82482ddb79365..78c6d868aa5afd 100644
--- a/mlir/unittests/IR/BlobManagerTest.cpp
+++ b/mlir/unittests/IR/BlobManagerTest.cpp
@@ -7,6 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "../../test/lib/Dialect/Test/TestDialect.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/DialectResourceBlobManager.h"
 #include "mlir/Parser/Parser.h"
 
@@ -71,4 +73,36 @@ TEST(DialectResourceBlobManagerTest, GetBlobMap) {
   ASSERT_TRUE(blobsArePresent);
 }
 
+TEST(DialectResourceBlobManagerTest, CustomDialectResource) {
+  MLIRContext context;
+  context.loadDialect<test::TestDialect>();
+
+  // Create a resource blob owned by the test dialect (not BuiltinDialect).
+  auto *testDialect = context.getLoadedDialect<test::TestDialect>();
+  ASSERT_NE(testDialect, nullptr);
+
+  auto type = RankedTensorType::get({2}, IntegerType::get(&context, 32));
+  std::vector<int32_t> data = {42, 43};
+  AsmResourceBlob blob(
+      ArrayRef<char>(reinterpret_cast<const char *>(data.data()),
+                     data.size() * sizeof(int32_t)),
+      alignof(int32_t));
+
+  auto attr = DenseResourceElementsAttr::get(type, "test_blob", std::move(blob),
+                                             testDialect);
+  ASSERT_TRUE(attr);
+
+  // The resource handle should point to the test dialect, not BuiltinDialect.
+  auto handle = attr.getResourceHandle();
+  EXPECT_EQ(handle.getDialect(), testDialect);
+  EXPECT_EQ(handle.getKey(), "test_blob");
+  ASSERT_NE(handle.getBlob(), nullptr);
+
+  // Verify data is accessible.
+  auto blobData = handle.getBlob()->getDataAs<int32_t>();
+  ASSERT_EQ(blobData.size(), 2u);
+  EXPECT_EQ(blobData[0], 42);
+  EXPECT_EQ(blobData[1], 43);
+}
+
 } // end anonymous namespace



More information about the Mlir-commits mailing list