[Mlir-commits] [mlir] [mlir] Add loaded URI attribute type (PR #67276)

Jacques Pienaar llvmlistbot at llvm.org
Sun Sep 24 19:26:12 PDT 2023


https://github.com/jpienaar created https://github.com/llvm/llvm-project/pull/67276

Add attribute type that behaves like a regular ElementsAttr with the exception that it is printed and parsed by using a URI (and offset and size within) and references within URI. This allows for an external to context and IR constant. It is still a constant, so its not for parameter case nor an extern constant.

The offset and size allows for having a single file with multiple values inside (and for some custom formats that are mmap'able this ends up being sufficient if the layout matches). While the nested references allows for usage in formats that have an indexing table (e.g., MLIR bytecode). These differ as to when considered: the offset & size is used to dictate which part to load into a memory buffer, while the references are for once the file is loaded.

In effect this is an attribute whose value doesn't get printed and that is usable where the external file will be available. These are also usable in cases where ElementsAttr is supported, such as ml_program globals or the like where it is not immediately a constant --- such usage would allow for some final level of customization/downstream adaption to decide what to inline when (ideally the correct ops would be created from the start, either with URI, with loaded URI or constant with loaded URI, so that frontend dictates these). The other potential use is in the elide large attribute case, where it could be outlined to files now and you get the small IR while having exactly the same behaviour when passed through again (while being friendly on editor/human reader).

Note: this is a minimal initial version, itdoes not yet add support for different URI nor file types, nor lazy loading.

>From f48935c7eccb2b8b515251b3a3f9e07260236222 Mon Sep 17 00:00:00 2001
From: Jacques Pienaar <jpienaar at google.com>
Date: Mon, 18 Sep 2023 12:45:42 -0700
Subject: [PATCH] [mlir] Add loaded URI attribute type

Add attribute type that behaves like a regular ElementsAttr with the exception
that it is printed and parsed by using a URI (and offset and size within) and
references within URI. This allows for an external to context and IR constant.
It is still a constant, so its not for parameter case nor an extern constant.

The offset and size allows for having a single file with multiple values inside
(and for some custom formats that are mmap'able this ends up being sufficient
if the layout matches). While the nested references allows for usage in formats
that have an indexing table (e.g., MLIR bytecode). These differ as to when
considered: the offset & size is used to dictate which part to load into a
memory buffer, while the references are for once the file is loaded.

In effect this is an attribute whose value doesn't get printed and that is
usable where the external file will be available. These are also usable in
cases where ElementsAttr is supported, such as ml_program globals or the like
where it is not immediately a constant --- such usage would allow for some
final level of customization/downstream adaption to decide what to inline when
(ideally the correct ops would be created from the start, either with URI,
with loaded URI or constant with loaded URI, so that frontend dictates these).
The other potential use is in the elide large attribute case, where it could be
outlined to files now and you get the small IR while having exactly the same
behaviour when passed through again (while being friendly on editor/human
reader).

Note: this is a minimal initial version, itdoes not yet add support for
different URI nor file types, nor lazy loading.
---
 mlir/docs/Dialects/Builtin.md            |  21 ++-
 mlir/include/mlir/IR/BuiltinAttributes.h |  86 ++++++++++
 mlir/lib/AsmParser/AttributeParser.cpp   | 108 +++++++++++++
 mlir/lib/AsmParser/Parser.h              |   3 +
 mlir/lib/AsmParser/TokenKinds.def        |   1 +
 mlir/lib/IR/AsmPrinter.cpp               |  14 ++
 mlir/lib/IR/BuiltinAttributes.cpp        | 190 ++++++++++++++++++++++-
 mlir/test/IR/luri.mlir                   |  11 ++
 mlir/test/IR/luri.raw                    |   1 +
 9 files changed, 432 insertions(+), 3 deletions(-)
 create mode 100644 mlir/test/IR/luri.mlir
 create mode 100644 mlir/test/IR/luri.raw

diff --git a/mlir/docs/Dialects/Builtin.md b/mlir/docs/Dialects/Builtin.md
index 0a9b7ae8919b55e..c6c9f3206175ca6 100644
--- a/mlir/docs/Dialects/Builtin.md
+++ b/mlir/docs/Dialects/Builtin.md
@@ -15,7 +15,7 @@ extensible by design, any potential additions are heavily scrutinized.
 
 [include "Dialects/BuiltinAttributes.md"]
 
-## Location Attributes
+### Location Attributes
 
 A subset of the builtin attribute values correspond to
 [source locations](../Diagnostics.md/#source-locations), that may be attached to
@@ -23,7 +23,7 @@ Operations.
 
 [include "Dialects/BuiltinLocationAttributes.md"]
 
-## DistinctAttribute
+### DistinctAttribute
 
 A DistinctAttribute associates an attribute with a unique identifier.
 As a result, multiple DistinctAttribute instances may point to the same
@@ -54,6 +54,23 @@ identifier, which can be used to mark groups of operations that share a
 common property. For example, groups of aliasing memory operations may be
 marked using one DistinctAttribute instance per alias group.
 
+### LoadedURIDenseResourceAttr
+
+A LoadedURIDenseResourceAttr is an attribute that implements the ElementsAttr
+interface, prints & parses as URI, optional offset, size, alignment and nested
+symbol reference paramters while keeping the value represented in memory (but
+outside the context). This allows for having constants whose value are outside
+the context and (during roundtrip) the IR, but without any semantic differences.
+
+```
+luri ::= `luri` `<` string-literal
+    (`,` `::` symbol-ref-attribute)? 
+    (`,` `byte_offset` `=` integer-literal)?
+    (`,` `byte_size` `=` integer-literal)?
+    (`,` `alignment` `=` integer-literal)?
+  `>`
+```
+
 ## Operations
 
 [include "Dialects/BuiltinOps.md"]
diff --git a/mlir/include/mlir/IR/BuiltinAttributes.h b/mlir/include/mlir/IR/BuiltinAttributes.h
index c8161604aad3503..508a3e6e7da7317 100644
--- a/mlir/include/mlir/IR/BuiltinAttributes.h
+++ b/mlir/include/mlir/IR/BuiltinAttributes.h
@@ -1046,6 +1046,92 @@ class DistinctAttr
   static DistinctAttr create(Attribute referencedAttr);
 };
 
+//===----------------------------------------------------------------------===//
+// LoadedURIDenseResourceAttr
+//===----------------------------------------------------------------------===//
+
+namespace detail {
+struct LoadedURIDenseResourceAttrStorage;
+} // namespace detail
+
+/// An attribute that parses/prints as a identifier (file & symbol) while in
+/// memory representing the loaded attribute.
+
+class LoadedURIDenseResourceAttr
+    : public Attribute::AttrBase<LoadedURIDenseResourceAttr, Attribute,
+                                 detail::LoadedURIDenseResourceAttrStorage,
+                                 TypedAttr::Trait, ElementsAttr::Trait> {
+public:
+  using Base::Base;
+  using Base::getChecked;
+
+  static LoadedURIDenseResourceAttr
+  get(StringRef uri, Type type, std::optional<int64_t> alignment,
+      std::optional<int64_t> offset, std::optional<int64_t> size,
+      ArrayRef<FlatSymbolRefAttr> nestedReferences = {});
+
+  static LoadedURIDenseResourceAttr
+  getChecked(function_ref<::mlir::InFlightDiagnostic()> emitError,
+             StringRef uri, Type type, std::optional<int64_t> alignment,
+             std::optional<int64_t> offset, std::optional<int64_t> size,
+             ArrayRef<FlatSymbolRefAttr> nestedReferences = {});
+
+  /// The set of data types that can be iterated by this attribute.
+  // TODO: Expand as needed; consider maybe reuse with DenseElementsAttr.
+  using ContiguousIterableTypesT = std::tuple<>;
+  using NonContiguousIterableTypesT = std::tuple<
+      // Float types.
+      APFloat>;
+
+  /// Returns the loaded value.
+  ArrayRef<char> getRawData() const;
+
+  /// Returns the underlying data as an array of the given type. This is an
+  /// inherently unsafe operation, and should only be used when the data is
+  /// known to be of the correct type.
+  template <typename T>
+  ArrayRef<T> getDataAs() const {
+    auto data = getRawData();
+    return llvm::ArrayRef<T>((const T *)data.data(), data.size() / sizeof(T));
+  }
+  /// Provide a `try_value_begin_impl` to enable iteration within
+  /// ElementsAttr.
+  auto try_value_begin_impl(OverloadToken<llvm::APFloat>) const {
+    auto it = llvm::map_range(getDataAs<float>(), [=](float value) {
+                return llvm::APFloat(value);
+              }).begin();
+    return FailureOr<decltype(it)>(std::move(it));
+  }
+
+  // TODO: Add member functions to register URI/file type handlers.
+
+  /// Return the URI loaded.
+  StringRef getURI() const;
+
+  /// Return the Type of the data.
+  ShapedType getType() const;
+
+  /// Return the nested references inside the URI loaded.
+  ArrayRef<FlatSymbolRefAttr> getNestedReferences() const;
+
+  /// Return the alignment the resource is stored with.
+  // TODO: Should this be exposed?
+  std::optional<int64_t> getAlignment() const;
+
+  /// Return the byte offset at which entry was in URI.
+  std::optional<int64_t> getByteOffset() const;
+
+  /// Return the byte size at which entry was in URI.
+  std::optional<int64_t> getByteSize() const;
+
+  static LogicalResult
+  verify(function_ref<::mlir::InFlightDiagnostic()> emitError, StringRef uri,
+         Type type, std::optional<int64_t> alignment,
+         std::optional<int64_t> offset, std::optional<int64_t> size,
+         ArrayRef<FlatSymbolRefAttr> nestedReferences,
+         std::optional<FailureOr<DenseResourceElementsHandle>> handle);
+};
+
 //===----------------------------------------------------------------------===//
 // StringAttr
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/AsmParser/AttributeParser.cpp b/mlir/lib/AsmParser/AttributeParser.cpp
index 3437ac9addc5ff6..6cde5e2574a795f 100644
--- a/mlir/lib/AsmParser/AttributeParser.cpp
+++ b/mlir/lib/AsmParser/AttributeParser.cpp
@@ -148,6 +148,10 @@ Attribute Parser::parseAttribute(Type type) {
     return locAttr;
   }
 
+  // Parse a loaded URI elements attribute.
+  case Token::kw_luri:
+    return parseLoadedURIElementsAttr(type);
+
   // Parse a sparse elements attribute.
   case Token::kw_sparse:
     return parseSparseElementsAttr(type);
@@ -1065,6 +1069,110 @@ ShapedType Parser::parseElementsLiteralType(Type type) {
   return sType;
 }
 
+/// Parse a sparse elements attribute.
+Attribute Parser::parseLoadedURIElementsAttr(Type attrType) {
+  SMLoc loc = getToken().getLoc();
+  consumeToken(Token::kw_luri);
+  if (parseToken(Token::less, "expected '<' after 'luri'"))
+    return nullptr;
+
+  auto fileName = getToken().getStringValue();
+  if (parseToken(Token::string, "expected URI"))
+    return nullptr;
+
+  // Parse any nested references.
+  std::vector<FlatSymbolRefAttr> nestedRefs;
+  while (consumeIf(Token::colon)) {
+    // Check for the '::' prefix.
+    if (parseToken(Token::colon, "ex[ected '::' for nested reference"))
+      return nullptr;
+    // Parse the reference itself.
+    auto curLoc = getToken().getLoc();
+    if (getToken().isNot(Token::at_identifier)) {
+      emitError(curLoc, "expected nested symbol reference identifier");
+      return nullptr;
+    }
+
+    std::string nameStr = getToken().getSymbolReference();
+    consumeToken(Token::at_identifier);
+    nestedRefs.push_back(SymbolRefAttr::get(getContext(), nameStr));
+  }
+
+  std::optional<int64_t> alignment;
+  std::optional<int64_t> byteOffset;
+  std::optional<int64_t> byteSize;
+
+  StringRef keyword;
+  for (auto curLoc = getToken().getLoc();
+       consumeIf(Token::comma) && !parseOptionalKeyword(&keyword);) {
+    curLoc = getToken().getLoc();
+
+    if (keyword == "alignment") {
+      if (alignment.has_value()) {
+        emitError("multiple alignment values provided");
+        return nullptr;
+      }
+      if (parseToken(Token::equal, "expected '=' after alignment"))
+        return nullptr;
+      APInt val;
+      if (auto res = parseOptionalInteger(val);
+          !res.has_value() || failed(*res)) {
+        emitError("missing or invalid alignment");
+        return nullptr;
+      }
+      alignment = val.getSExtValue();
+      continue;
+    }
+
+    if (keyword == "byte_offset") {
+      if (byteOffset.has_value()) {
+        emitError("multiple offset values provided");
+        return nullptr;
+      }
+      if (parseToken(Token::equal, "expected '=' after offset"))
+        return nullptr;
+      APInt val;
+      if (auto res = parseOptionalInteger(val);
+          !res.has_value() || failed(*res)) {
+        emitError("missing or invalid offset");
+        return nullptr;
+      }
+      byteOffset = val.getSExtValue();
+      continue;
+    }
+
+    if (keyword == "byte_size") {
+      if (byteSize.has_value()) {
+        emitError("multiple size values provided");
+        return nullptr;
+      }
+      if (parseToken(Token::equal, "expected '=' after size"))
+        return nullptr;
+      APInt val;
+      if (auto res = parseOptionalInteger(val);
+          !res.has_value() || failed(*res)) {
+        emitError("missing or invalid size");
+        return nullptr;
+      }
+      byteSize = val.getSExtValue();
+      continue;
+    }
+
+    emitError(curLoc, "unexpected keyword: ") << keyword;
+  }
+
+  if (parseToken(Token::greater, "expected '>'"))
+    return nullptr;
+
+  auto type = parseElementsLiteralType(attrType);
+  if (!type)
+    return nullptr;
+
+  // Build the sparse elements attribute by the indices and values.
+  return getChecked<LoadedURIDenseResourceAttr>(
+      loc, fileName, type, alignment, byteOffset, byteSize, nestedRefs);
+}
+
 /// Parse a sparse elements attribute.
 Attribute Parser::parseSparseElementsAttr(Type attrType) {
   SMLoc loc = getToken().getLoc();
diff --git a/mlir/lib/AsmParser/Parser.h b/mlir/lib/AsmParser/Parser.h
index 01c55f97a08c2ce..dce9b4a758cf5b7 100644
--- a/mlir/lib/AsmParser/Parser.h
+++ b/mlir/lib/AsmParser/Parser.h
@@ -273,6 +273,9 @@ class Parser {
   /// Parse a DenseArrayAttr.
   Attribute parseDenseArrayAttr(Type type);
 
+  /// Parse a loaded URI elements attribute.
+  Attribute parseLoadedURIElementsAttr(Type type);
+
   /// Parse a sparse elements attribute.
   Attribute parseSparseElementsAttr(Type attrType);
 
diff --git a/mlir/lib/AsmParser/TokenKinds.def b/mlir/lib/AsmParser/TokenKinds.def
index 297e07459453013..7c867eeaddb7b38 100644
--- a/mlir/lib/AsmParser/TokenKinds.def
+++ b/mlir/lib/AsmParser/TokenKinds.def
@@ -106,6 +106,7 @@ TOK_KEYWORD(for)
 TOK_KEYWORD(func)
 TOK_KEYWORD(index)
 TOK_KEYWORD(loc)
+TOK_KEYWORD(luri)
 TOK_KEYWORD(max)
 TOK_KEYWORD(memref)
 TOK_KEYWORD(min)
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index 7b0da30541b16a4..1872a85cc00e280 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -2311,6 +2311,20 @@ void AsmPrinter::Impl::printAttributeImpl(Attribute attr,
     os << ">";
   } else if (auto locAttr = llvm::dyn_cast<LocationAttr>(attr)) {
     printLocation(locAttr);
+  } else if (auto luriAttr = llvm::dyn_cast<LoadedURIDenseResourceAttr>(attr)) {
+    os << "luri<";
+    printEscapedString(luriAttr.getURI());
+    for (FlatSymbolRefAttr nestedRef : luriAttr.getNestedReferences()) {
+      os << "::";
+      printSymbolReference(nestedRef.getValue(), os);
+    }
+    if (auto alignment = luriAttr.getAlignment())
+      os << ", alignment = " << alignment;
+    if (auto offset = luriAttr.getByteOffset())
+      os << ", byte_offset = " << offset;
+    if (auto size = luriAttr.getByteSize())
+      os << ", byte_size = " << size;
+    os << ">";
   } else {
     llvm::report_fatal_error("Unknown builtin attribute");
   }
diff --git a/mlir/lib/IR/BuiltinAttributes.cpp b/mlir/lib/IR/BuiltinAttributes.cpp
index 5f1129326f4f772..2a043b668806c98 100644
--- a/mlir/lib/IR/BuiltinAttributes.cpp
+++ b/mlir/lib/IR/BuiltinAttributes.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/SourceMgr.h"
 #include <optional>
 
 using namespace mlir;
@@ -42,7 +43,7 @@ void BuiltinDialect::registerAttributes() {
 #define GET_ATTRDEF_LIST
 #include "mlir/IR/BuiltinAttributes.cpp.inc"
       >();
-  addAttributes<DistinctAttr>();
+  addAttributes<DistinctAttr, LoadedURIDenseResourceAttr>();
 }
 
 //===----------------------------------------------------------------------===//
@@ -1761,6 +1762,193 @@ Attribute DistinctAttr::getReferencedAttr() const {
   return getImpl()->referencedAttr;
 }
 
+//===----------------------------------------------------------------------===//
+// LoadedURIDenseResourceAttr
+//===----------------------------------------------------------------------===//
+
+namespace mlir {
+namespace detail {
+struct LoadedURIDenseResourceAttrStorage : public ::mlir::AttributeStorage {
+  using KeyTy =
+      std::tuple<StringRef, Type, std::optional<int64_t>,
+                 std::optional<int64_t>, std::optional<int64_t>,
+                 ArrayRef<FlatSymbolRefAttr>,
+                 std::optional<FailureOr<DenseResourceElementsHandle>>>;
+  LoadedURIDenseResourceAttrStorage(
+      StringRef uri, Type type, std::optional<int64_t> alignment,
+      std::optional<int64_t> offset, std::optional<int64_t> size,
+      ArrayRef<FlatSymbolRefAttr> nestedReferences,
+      std::optional<FailureOr<DenseResourceElementsHandle>> rawHandle)
+      : uri(uri), type(type), nestedReferences(nestedReferences),
+        alignment(alignment), offset(offset), size(size), rawHandle(rawHandle) {
+  }
+
+  KeyTy getAsKey() const {
+    return KeyTy(uri, type, alignment, offset, size, nestedReferences,
+                 rawHandle);
+  }
+
+  bool operator==(const KeyTy &key) const {
+    return (uri == std::get<0>(key)) && (type == std::get<1>(key)) &&
+           (alignment == std::get<2>(key)) && (offset == std::get<3>(key)) &&
+           (size == std::get<4>(key)) && (nestedReferences == std::get<5>(key));
+  }
+
+  static llvm::hash_code hashKey(const KeyTy &key) {
+    return llvm::hash_combine(std::get<0>(key), std::get<1>(key),
+                              std::get<2>(key), std::get<3>(key),
+                              std::get<4>(key), std::get<5>(key));
+  }
+
+  static LoadedURIDenseResourceAttrStorage *
+  construct(AttributeStorageAllocator &allocator, const KeyTy &key) {
+    auto uri = std::get<0>(key);
+    auto type = std::get<1>(key);
+    auto alignment = std::get<2>(key);
+    auto offset = std::get<3>(key);
+    auto size = std::get<4>(key);
+    auto nestedReferences = std::get<5>(key);
+    auto rawHandle = std::get<6>(key);
+    uri = allocator.copyInto(uri);
+    nestedReferences = allocator.copyInto(nestedReferences);
+
+    return new (allocator.allocate<LoadedURIDenseResourceAttrStorage>())
+        LoadedURIDenseResourceAttrStorage(uri, type, alignment, offset, size,
+                                          nestedReferences, rawHandle);
+  }
+
+  void initialize(MLIRContext *context) {
+    // If a handle is provided, then skip initializing.
+    if (rawHandle.has_value())
+      return;
+
+    // Init to failure. initialize is called after verify when initially
+    // created, so can't use absense of handle as failure.
+    rawHandle = failure();
+
+    // TODO: Handling different URI types.
+
+    StringRef fname = uri;
+    (void)fname.consume_front("file://");
+
+    std::unique_ptr<llvm::MemoryBuffer> buffer;
+    if (size.has_value() || offset.has_value()) {
+      std::optional<llvm::Align> mbAlign;
+      if (alignment)
+        mbAlign = llvm::Align(*alignment);
+      auto bufferOr = llvm::MemoryBuffer::getFileSlice(
+          fname, size.value_or(uint64_t(-1)), offset.value_or(0),
+          /*IsVolatile=*/false, mbAlign);
+      if (!bufferOr) {
+        // TODO: Find way to flag on location.
+        emitError(UnknownLoc::get(context))
+            << "loading URI failed: " << bufferOr.getError().message();
+        return;
+      }
+      buffer.swap(*bufferOr);
+    } else {
+      std::optional<llvm::Align> mbAlign;
+      if (alignment)
+        mbAlign = llvm::Align(*alignment);
+      auto bufferOr = llvm::MemoryBuffer::getFile(
+          fname, /*IsText=*/false, /*RequiresNullTerminator=*/true,
+          /*IsVolatile=*/false, mbAlign);
+      if (!bufferOr) {
+        // TODO: Find way to flag on location.
+        emitError(UnknownLoc::get(context))
+            << "loading URI failed: " << bufferOr.getError().message();
+        return;
+      }
+      buffer.swap(*bufferOr);
+    }
+
+    // TODO: Handling of different file types.
+
+    auto *data = buffer.release();
+    auto deleter = [data](void *, unsigned long, unsigned long) {
+      delete data;
+    };
+
+    // Extract the builtin dialect resource manager from context and
+    // construct a handle by inserting a new resource using the
+    // provided blob.
+    auto &manager =
+        DenseResourceElementsHandle::getManagerInterface(type.getContext());
+    auto str = data->getBuffer();
+    auto blob = UnmanagedAsmResourceBlob::allocateWithAlign(
+        {str.data(), str.size()}, alignment.value_or(32), deleter);
+    rawHandle = manager.insert("luri", std::move(blob));
+  }
+
+  StringRef uri;
+  ShapedType type;
+  bool isSplat = false;
+  ArrayRef<FlatSymbolRefAttr> nestedReferences;
+  std::optional<int64_t> alignment;
+  std::optional<int64_t> offset;
+  std::optional<int64_t> size;
+  std::optional<FailureOr<DenseResourceElementsHandle>> rawHandle;
+};
+} // namespace detail
+
+LoadedURIDenseResourceAttr LoadedURIDenseResourceAttr::get(
+    StringRef uri, Type type, std::optional<int64_t> alignment,
+    std::optional<int64_t> offset, std::optional<int64_t> size,
+    ArrayRef<FlatSymbolRefAttr> nestedReferences) {
+  auto *ctxt = type.getContext();
+  return Base::get(ctxt, uri, type, alignment, offset, size, nestedReferences,
+                   std::nullopt);
+}
+
+LoadedURIDenseResourceAttr LoadedURIDenseResourceAttr::getChecked(
+    function_ref<::mlir::InFlightDiagnostic()> emitError, StringRef uri,
+    Type type, std::optional<int64_t> alignment, std::optional<int64_t> offset,
+    std::optional<int64_t> size, ArrayRef<FlatSymbolRefAttr> nestedReferences) {
+  auto *ctxt = type.getContext();
+  return Base::getChecked(emitError, ctxt, uri, type, alignment, offset, size,
+                          nestedReferences, std::nullopt);
+}
+
+LogicalResult LoadedURIDenseResourceAttr::verify(
+    function_ref<::mlir::InFlightDiagnostic()> emitError, StringRef uri,
+    Type type, std::optional<int64_t> alignment, std::optional<int64_t> offset,
+    std::optional<int64_t> size, ArrayRef<FlatSymbolRefAttr> nestedReferences,
+    std::optional<FailureOr<DenseResourceElementsHandle>> handle) {
+  if (handle.has_value() && failed(handle.value()))
+    return emitError() << "URI failed to load";
+  return success();
+}
+
+StringRef LoadedURIDenseResourceAttr::getURI() const { return getImpl()->uri; }
+
+ShapedType LoadedURIDenseResourceAttr::getType() const {
+  return getImpl()->type;
+}
+
+std::optional<int64_t> LoadedURIDenseResourceAttr::getAlignment() const {
+  return getImpl()->alignment;
+}
+
+std::optional<int64_t> LoadedURIDenseResourceAttr::getByteOffset() const {
+  return getImpl()->offset;
+}
+
+std::optional<int64_t> LoadedURIDenseResourceAttr::getByteSize() const {
+  return getImpl()->size;
+}
+
+ArrayRef<FlatSymbolRefAttr>
+LoadedURIDenseResourceAttr::getNestedReferences() const {
+  return getImpl()->nestedReferences;
+}
+
+ArrayRef<char> LoadedURIDenseResourceAttr::getRawData() const {
+  assert(getImpl()->rawHandle.has_value() && succeeded(*getImpl()->rawHandle));
+  return getImpl()->rawHandle->value().getBlob()->getData();
+}
+
+} // namespace mlir
+
 //===----------------------------------------------------------------------===//
 // Attribute Utilities
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/IR/luri.mlir b/mlir/test/IR/luri.mlir
new file mode 100644
index 000000000000000..d4083c47b562a9f
--- /dev/null
+++ b/mlir/test/IR/luri.mlir
@@ -0,0 +1,11 @@
+// RUN: cd %S && mlir-opt --canonicalize %s | FileCheck %s
+
+func.func @foo() -> (tensor<10xf32>) {
+  %0 = arith.constant luri<"luri.raw", byte_offset = 80, byte_size = 80> : tensor<10xf32>
+  %1 = arith.constant luri<"luri.raw", byte_offset =  0, byte_size = 80> : tensor<10xf32>
+  %c = arith.constant dense<1.0e5> : tensor<10xf32>
+  %mul = arith.mulf %0, %c : tensor<10xf32>
+  // CHECK: arith.constant{{.*}}785.0708
+  %add = arith.addf %1, %mul: tensor<10xf32>
+  return %add : tensor<10xf32>
+}
diff --git a/mlir/test/IR/luri.raw b/mlir/test/IR/luri.raw
new file mode 100644
index 000000000000000..c854b5b127b0245
--- /dev/null
+++ b/mlir/test/IR/luri.raw
@@ -0,0 +1 @@
+DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD33333333333333333333333333333333333333333333333333333333333333333333333333333333
\ No newline at end of file



More information about the Mlir-commits mailing list