-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir] Allow accessing DialectResourceBlobManager::blobMap #142352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-mlir-core Author: Andrei Golubev (andrey-golubev) ChangesAdd a new API to access all blobs that are stored in the blob manager. The main purpose (as of now) is to allow users of dialect resources to iterate over all blobs, especially when the blobs are no longer used in IR (e.g. the operation that uses the blob is deleted) and thus cannot be easily accessed without manual tracking of keys. Full diff: https://github.com/llvm/llvm-project/pull/142352.diff 4 Files Affected:
diff --git a/mlir/include/mlir/IR/DialectResourceBlobManager.h b/mlir/include/mlir/IR/DialectResourceBlobManager.h
index e3f32b7a9ab5f..f51b73ef39b5f 100644
--- a/mlir/include/mlir/IR/DialectResourceBlobManager.h
+++ b/mlir/include/mlir/IR/DialectResourceBlobManager.h
@@ -93,9 +93,14 @@ class DialectResourceBlobManager {
return HandleT(&entry, dialect);
}
+ /// Provide access to all the registered blobs via a callable. During access
+ /// the blobs are guaranteed to remain unchanged.
+ void access(llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)>
+ accessor) const;
+
private:
/// A mutex to protect access to the blob map.
- llvm::sys::SmartRWMutex<true> blobMapLock;
+ mutable llvm::sys::SmartRWMutex<true> blobMapLock;
/// The internal map of tracked blobs. StringMap stores entries in distinct
/// allocations, so we can freely take references to the data without fear of
diff --git a/mlir/lib/IR/DialectResourceBlobManager.cpp b/mlir/lib/IR/DialectResourceBlobManager.cpp
index b83b31e30ef1f..811e4459d518e 100644
--- a/mlir/lib/IR/DialectResourceBlobManager.cpp
+++ b/mlir/lib/IR/DialectResourceBlobManager.cpp
@@ -63,3 +63,11 @@ auto DialectResourceBlobManager::insert(StringRef name,
nameStorage.resize(name.size() + 1);
} while (true);
}
+
+void DialectResourceBlobManager::access(
+ llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)> accessor)
+ const {
+ llvm::sys::SmartScopedReader<true> reader(blobMapLock);
+
+ accessor(blobMap);
+}
diff --git a/mlir/unittests/IR/BlobManagerTest.cpp b/mlir/unittests/IR/BlobManagerTest.cpp
new file mode 100644
index 0000000000000..fe480a2189c5d
--- /dev/null
+++ b/mlir/unittests/IR/BlobManagerTest.cpp
@@ -0,0 +1,74 @@
+//===- mlir/unittest/IR/BlobManagerTest.cpp - Blob management unit tests --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../test/lib/Dialect/Test/TestDialect.h"
+#include "mlir/IR/DialectResourceBlobManager.h"
+#include "mlir/Parser/Parser.h"
+
+#include "gtest/gtest.h"
+
+using namespace mlir;
+
+namespace {
+
+StringLiteral moduleStr = R"mlir(
+"test.use1"() {attr = dense_resource<blob1> : tensor<1xi64> } : () -> ()
+
+{-#
+ dialect_resources: {
+ builtin: {
+ blob1: "0x08000000ABCDABCDABCDABCE"
+ }
+ }
+#-}
+)mlir";
+
+TEST(DialectResourceBlobManagerTest, Lookup) {
+ MLIRContext context;
+ context.loadDialect<test::TestDialect>();
+
+ OwningOpRef<ModuleOp> m = parseSourceString<ModuleOp>(moduleStr, &context);
+ ASSERT_TRUE(m);
+
+ const auto &dialectManager =
+ mlir::DenseResourceElementsHandle::getManagerInterface(&context);
+ ASSERT_NE(dialectManager.getBlobManager().lookup("blob1"), nullptr);
+}
+
+TEST(DialectResourceBlobManagerTest, Access) {
+ MLIRContext context;
+ context.loadDialect<test::TestDialect>();
+
+ OwningOpRef<ModuleOp> m = parseSourceString<ModuleOp>(moduleStr, &context);
+ ASSERT_TRUE(m);
+
+ Block *block = m->getBody();
+ auto &op = block->getOperations().front();
+ auto resourceAttr = op.getAttrOfType<DenseResourceElementsAttr>("attr");
+ ASSERT_NE(resourceAttr, nullptr);
+
+ const auto &dialectManager =
+ resourceAttr.getRawHandle().getManagerInterface(&context);
+
+ bool blobsArePresent = false;
+ dialectManager.getBlobManager().access(
+ [&](const llvm::StringMap<DialectResourceBlobManager::BlobEntry>
+ &blobMap) { blobsArePresent = blobMap.contains("blob1"); });
+ ASSERT_TRUE(blobsArePresent);
+
+ // remove operations that use resources - resources must still be accessible
+ block->clear();
+
+ blobsArePresent = false;
+ dialectManager.getBlobManager().access(
+ [&](const llvm::StringMap<DialectResourceBlobManager::BlobEntry>
+ &blobMap) { blobsArePresent = blobMap.contains("blob1"); });
+ ASSERT_TRUE(blobsArePresent);
+}
+
+} // end anonymous namespace
diff --git a/mlir/unittests/IR/CMakeLists.txt b/mlir/unittests/IR/CMakeLists.txt
index 9ab6029c3480d..7700644864570 100644
--- a/mlir/unittests/IR/CMakeLists.txt
+++ b/mlir/unittests/IR/CMakeLists.txt
@@ -18,6 +18,7 @@ add_mlir_unittest(MLIRIRTests
TypeAttrNamesTest.cpp
OpPropertiesTest.cpp
ValueTest.cpp
+ BlobManagerTest.cpp
DEPENDS
MLIRTestInterfaceIncGen
|
@llvm/pr-subscribers-mlir Author: Andrei Golubev (andrey-golubev) ChangesAdd a new API to access all blobs that are stored in the blob manager. The main purpose (as of now) is to allow users of dialect resources to iterate over all blobs, especially when the blobs are no longer used in IR (e.g. the operation that uses the blob is deleted) and thus cannot be easily accessed without manual tracking of keys. Full diff: https://github.com/llvm/llvm-project/pull/142352.diff 4 Files Affected:
diff --git a/mlir/include/mlir/IR/DialectResourceBlobManager.h b/mlir/include/mlir/IR/DialectResourceBlobManager.h
index e3f32b7a9ab5f..f51b73ef39b5f 100644
--- a/mlir/include/mlir/IR/DialectResourceBlobManager.h
+++ b/mlir/include/mlir/IR/DialectResourceBlobManager.h
@@ -93,9 +93,14 @@ class DialectResourceBlobManager {
return HandleT(&entry, dialect);
}
+ /// Provide access to all the registered blobs via a callable. During access
+ /// the blobs are guaranteed to remain unchanged.
+ void access(llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)>
+ accessor) const;
+
private:
/// A mutex to protect access to the blob map.
- llvm::sys::SmartRWMutex<true> blobMapLock;
+ mutable llvm::sys::SmartRWMutex<true> blobMapLock;
/// The internal map of tracked blobs. StringMap stores entries in distinct
/// allocations, so we can freely take references to the data without fear of
diff --git a/mlir/lib/IR/DialectResourceBlobManager.cpp b/mlir/lib/IR/DialectResourceBlobManager.cpp
index b83b31e30ef1f..811e4459d518e 100644
--- a/mlir/lib/IR/DialectResourceBlobManager.cpp
+++ b/mlir/lib/IR/DialectResourceBlobManager.cpp
@@ -63,3 +63,11 @@ auto DialectResourceBlobManager::insert(StringRef name,
nameStorage.resize(name.size() + 1);
} while (true);
}
+
+void DialectResourceBlobManager::access(
+ llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)> accessor)
+ const {
+ llvm::sys::SmartScopedReader<true> reader(blobMapLock);
+
+ accessor(blobMap);
+}
diff --git a/mlir/unittests/IR/BlobManagerTest.cpp b/mlir/unittests/IR/BlobManagerTest.cpp
new file mode 100644
index 0000000000000..fe480a2189c5d
--- /dev/null
+++ b/mlir/unittests/IR/BlobManagerTest.cpp
@@ -0,0 +1,74 @@
+//===- mlir/unittest/IR/BlobManagerTest.cpp - Blob management unit tests --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../test/lib/Dialect/Test/TestDialect.h"
+#include "mlir/IR/DialectResourceBlobManager.h"
+#include "mlir/Parser/Parser.h"
+
+#include "gtest/gtest.h"
+
+using namespace mlir;
+
+namespace {
+
+StringLiteral moduleStr = R"mlir(
+"test.use1"() {attr = dense_resource<blob1> : tensor<1xi64> } : () -> ()
+
+{-#
+ dialect_resources: {
+ builtin: {
+ blob1: "0x08000000ABCDABCDABCDABCE"
+ }
+ }
+#-}
+)mlir";
+
+TEST(DialectResourceBlobManagerTest, Lookup) {
+ MLIRContext context;
+ context.loadDialect<test::TestDialect>();
+
+ OwningOpRef<ModuleOp> m = parseSourceString<ModuleOp>(moduleStr, &context);
+ ASSERT_TRUE(m);
+
+ const auto &dialectManager =
+ mlir::DenseResourceElementsHandle::getManagerInterface(&context);
+ ASSERT_NE(dialectManager.getBlobManager().lookup("blob1"), nullptr);
+}
+
+TEST(DialectResourceBlobManagerTest, Access) {
+ MLIRContext context;
+ context.loadDialect<test::TestDialect>();
+
+ OwningOpRef<ModuleOp> m = parseSourceString<ModuleOp>(moduleStr, &context);
+ ASSERT_TRUE(m);
+
+ Block *block = m->getBody();
+ auto &op = block->getOperations().front();
+ auto resourceAttr = op.getAttrOfType<DenseResourceElementsAttr>("attr");
+ ASSERT_NE(resourceAttr, nullptr);
+
+ const auto &dialectManager =
+ resourceAttr.getRawHandle().getManagerInterface(&context);
+
+ bool blobsArePresent = false;
+ dialectManager.getBlobManager().access(
+ [&](const llvm::StringMap<DialectResourceBlobManager::BlobEntry>
+ &blobMap) { blobsArePresent = blobMap.contains("blob1"); });
+ ASSERT_TRUE(blobsArePresent);
+
+ // remove operations that use resources - resources must still be accessible
+ block->clear();
+
+ blobsArePresent = false;
+ dialectManager.getBlobManager().access(
+ [&](const llvm::StringMap<DialectResourceBlobManager::BlobEntry>
+ &blobMap) { blobsArePresent = blobMap.contains("blob1"); });
+ ASSERT_TRUE(blobsArePresent);
+}
+
+} // end anonymous namespace
diff --git a/mlir/unittests/IR/CMakeLists.txt b/mlir/unittests/IR/CMakeLists.txt
index 9ab6029c3480d..7700644864570 100644
--- a/mlir/unittests/IR/CMakeLists.txt
+++ b/mlir/unittests/IR/CMakeLists.txt
@@ -18,6 +18,7 @@ add_mlir_unittest(MLIRIRTests
TypeAttrNamesTest.cpp
OpPropertiesTest.cpp
ValueTest.cpp
+ BlobManagerTest.cpp
DEPENDS
MLIRTestInterfaceIncGen
|
cc @joker-eph @River707 (I think you're best reviewers here!) |
/// the blobs are guaranteed to remain unchanged. | ||
void access(llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)> | ||
accessor) const; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: the API itself is a bit weird since we need to keep access protected by a mutex. without a larger redesign of this, it seems to be the only option?
ping @joker-eph @River707 |
@joker-eph @River707 (gently pinging again) |
@@ -93,9 +93,14 @@ class DialectResourceBlobManager { | |||
return HandleT(&entry, dialect); | |||
} | |||
|
|||
/// Provide access to all the registered blobs via a callable. During access | |||
/// the blobs are guaranteed to remain unchanged. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can guarantee that the map isn't changed, but the blobs themselves aren't protected right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, good point. I guess better wording: "the blob map is guaranteed to remain unchanged".
if one is evil, they could hold a BlobEntry
for a while and modify it, etc, but this is pretty much out of scope of this (any?) API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rephrased a bit, thanks!
&blobMap) { blobsArePresent = blobMap.contains("blob1"); }); | ||
ASSERT_TRUE(blobsArePresent); | ||
|
||
// remove operations that use resources - resources must still be accessible |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really a guarantee of the system? Aren't we allowed to somehow have a reference-count mechanism?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
honestly, I'm not sure. but seems to be intended behavior? from what I understand:
- resources are "raw" (at best unique when with deleter) pointers
- blob - underlying data behind resources - are stored into a dialect manager (lifetime == MLIR context's lifetime)
- resources are used by operations but as the resources themselves are attributes (mlir::DenseResourceElementsAttr), they are not "owned" by operations
thus, what we effectively get is: dense_resource<> is never really deleted unless one explicitly does something at the dialect (aka blob) manager level. which is fine by me, to be honest.
side-note: i was experimenting with custom deletion on top of dialect resources before (e.g. via a manual ref-count) but I guess ideal solution is to switch to properties and have operation-driven auto-ref-count semantics (might require some resource redesign as well though).
@@ -93,9 +93,14 @@ class DialectResourceBlobManager { | |||
return HandleT(&entry, dialect); | |||
} | |||
|
|||
/// Provide access to all the registered blobs via a callable. During access | |||
/// the blobs are guaranteed to remain unchanged. | |||
void access(llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void access(llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)> | |
void getBlobMap(llvm::function_ref<void(const llvm::StringMap<BlobEntry> &)> |
"access" is a bit unusual, ultimately this is an accessor every if you're using a callback for purposes of mutex handling
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right. I was thinking that get
is supposed to return something. if you think this is a better name, though, i have nothing against. "access" does sound alien, agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to getBlobMap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not totally sure about the invariants we want around all this, but this would be a "low-level API / you must know what you're doing" kind of API.
Add a new API to access all blobs that are stored in the blob manager. The main purpose (as of now) is to allow users of dialect resources to iterate over all blobs, especially when the blobs are no longer used in IR (e.g. the operation that uses the blob is deleted) and thus cannot be easily accessed without manual tracking of keys.
2b76715
to
6417a02
Compare
@joker-eph updated according to your suggestions (and also rebased). do you mind merging (i have no rights)? |
Add a new API to access all blobs that are stored in the blob manager. The main purpose (as of now) is to allow users of dialect resources to iterate over all blobs, especially when the blobs are no longer used in IR (e.g. the operation that uses the blob is deleted) and thus cannot be easily accessed without manual tracking of keys.
Add a new API to access all blobs that are stored in the blob manager. The main purpose (as of now) is to allow users of dialect resources to iterate over all blobs, especially when the blobs are no longer used in IR (e.g. the operation that uses the blob is deleted) and thus cannot be easily accessed without manual tracking of keys.
Add a new API to access all blobs that are stored in the blob manager. The main purpose (as of now) is to allow users of dialect resources to iterate over all blobs, especially when the blobs are no longer used in IR (e.g. the operation that uses the blob is deleted) and thus cannot be easily accessed without manual tracking of keys.