[Mlir-commits] [mlir] [mlir][Printer] Honor --mlir-elide-resource-strings-if-larger when serializing of large blobs - #213920 (PR #217579)
Natanael Cintean
llvmlistbot at llvm.org
Thu Aug 20 03:29:50 PDT 2026
https://github.com/natanael-cintean created https://github.com/llvm/llvm-project/pull/217579
Bringing in changes merged in main [PR 213920](https://github.com/llvm/llvm-project/pull/213920)
OperationPrinter::printResourceFileMetadata elides resource entries larger than --mlir-elide-resource-strings-if-larger (charLimit), but always materializes the full serialized string first (e.g. hex-encoding the entire blob) and only checks the length afterward, discarding the string if it's too long. For large dialect/external resource blobs, this wastes significant time and memory hex-encoding data that is guaranteed to be elided.
The time and memory consumption is especially noticeable when printing the IR for a large number of passes.
Extend ResourceBuilder::PrintFn to accept an additional sizeHint — the exact number of characters the value will serialize to when known ahead of time (currently computed for buildBlob's hex-encoded output; -1 for buildBool/buildString where it's cheap to compute directly).
printResourceFileMetadata then skips calling the serialization callback entirely when sizeHint already exceeds charLimit, avoiding the wasted encoding work while preserving identical printed output.
>From 280ee792819534778f6fb8cb171b8c0f063d65f6 Mon Sep 17 00:00:00 2001
From: Natanael Cintean <natanael.cintean at intel.com>
Date: Tue, 4 Aug 2026 10:54:41 +0000
Subject: [PATCH 1/2] Add size check and skip serialization of large blobs.
---
mlir/lib/IR/AsmPrinter.cpp | 58 +++++++++++++++------
mlir/test/IR/pretty-resources-print.mlir | 64 +++++++++++++++++++++++-
2 files changed, 105 insertions(+), 17 deletions(-)
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index b95ab00bd5fdd..74b5c85a020cb 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -3467,33 +3467,49 @@ class OperationPrinter : public AsmPrinter::Impl, private OpAsmPrinter {
class ResourceBuilder : public AsmResourceBuilder {
public:
using ValueFn = function_ref<void(raw_ostream &)>;
- using PrintFn = function_ref<void(StringRef, ValueFn)>;
+ // `sizeHint` is the exact number of characters `valueFn` will write, or -1
+ // if unknown, so the char limit can be applied before paying the cost of
+ // invoking `valueFn` (e.g. hex-encoding a large blob).
+ using PrintFn = function_ref<void(StringRef, ValueFn, int64_t sizeHint)>;
ResourceBuilder(PrintFn printFn) : printFn(printFn) {}
~ResourceBuilder() override = default;
void buildBool(StringRef key, bool data) final {
- printFn(key, [&](raw_ostream &os) { os << (data ? "true" : "false"); });
+ printFn(
+ key, [&](raw_ostream &os) { os << (data ? "true" : "false"); },
+ /*sizeHint=*/-1);
}
void buildString(StringRef key, StringRef data) final {
- printFn(key, [&](raw_ostream &os) {
- os << "\"";
- llvm::printEscapedString(data, os);
- os << "\"";
- });
+ printFn(
+ key,
+ [&](raw_ostream &os) {
+ os << "\"";
+ llvm::printEscapedString(data, os);
+ os << "\"";
+ },
+ /*sizeHint=*/-1);
}
void buildBlob(StringRef key, ArrayRef<char> data,
uint32_t dataAlignment) final {
- printFn(key, [&](raw_ostream &os) {
- // Store the blob in a hex string containing the alignment and the data.
- llvm::support::ulittle32_t dataAlignmentLE(dataAlignment);
- os << "\"0x"
- << llvm::toHex(StringRef(reinterpret_cast<char *>(&dataAlignmentLE),
- sizeof(dataAlignment)))
- << llvm::toHex(StringRef(data.data(), data.size())) << "\"";
- });
+ // Two hex chars per byte of the alignment word and the data, plus the
+ // `"0x`/`"` wrapping; exact, so the limit can be checked pre-encoding.
+ int64_t sizeHint = 2 * int64_t(sizeof(dataAlignment) + data.size()) + 4;
+ printFn(
+ key,
+ [&](raw_ostream &os) {
+ // Store the blob in a hex string containing the alignment and the
+ // data.
+ llvm::support::ulittle32_t dataAlignmentLE(dataAlignment);
+ os << "\"0x"
+ << llvm::toHex(
+ StringRef(reinterpret_cast<char *>(&dataAlignmentLE),
+ sizeof(dataAlignment)))
+ << llvm::toHex(StringRef(data.data(), data.size())) << "\"";
+ },
+ sizeHint);
}
private:
@@ -3558,7 +3574,8 @@ void OperationPrinter::printResourceFileMetadata(
auto processProvider = [&](StringRef dictName, StringRef name, auto &provider,
auto &&...providerArgs) {
bool hadEntry = false;
- auto printFn = [&](StringRef key, ResourceBuilder::ValueFn valueFn) {
+ auto printFn = [&](StringRef key, ResourceBuilder::ValueFn valueFn,
+ int64_t sizeHint) {
checkAddMetadataDict();
std::string resourceStr;
@@ -3570,6 +3587,15 @@ void OperationPrinter::printResourceFileMetadata(
if (charLimit.value() == 0)
return;
+ // Skip serializing entirely if the exact size already exceeds the
+ // limit, e.g. hex-encoding a large blob.
+ if (sizeHint >= 0 && uint64_t(sizeHint) > charLimit.value()) {
+ LLVM_DEBUG(llvm::dbgs() << "eliding resource '" << key
+ << "' without materializing (sizeHint="
+ << sizeHint << ")\n");
+ return;
+ }
+
llvm::raw_string_ostream ss(resourceStr);
valueFn(ss);
diff --git a/mlir/test/IR/pretty-resources-print.mlir b/mlir/test/IR/pretty-resources-print.mlir
index 980af80343d47..bd3b271ca7500 100644
--- a/mlir/test/IR/pretty-resources-print.mlir
+++ b/mlir/test/IR/pretty-resources-print.mlir
@@ -1,20 +1,49 @@
+// REQUIRES: asserts
// Check printing with --mlir-elide-resource-strings-if-larger elides printing large resources
// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=20| FileCheck %s
// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=0| FileCheck %s --check-prefix=ZERO
+// blob3's exact serialized size (quotes + "0x" + hex(alignment) + hex(data)) is 14 chars;
+// these two RUN lines check the off-by-one boundary of the sizeHint-based elision.
+// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=14| FileCheck %s --check-prefix=BOUND14
+// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=13| FileCheck %s --check-prefix=BOUND13
+
+// These directly verify the sizeHint fast path itself (not just the resulting output) by
+// checking for its debug log: it must fire when blob3 is elided but not when it is printed.
+// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=13 --debug-only=mlir-asm-printer 2>&1 | FileCheck %s --check-prefix=DEBUG-ELIDE
+// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=14 --debug-only=mlir-asm-printer 2>&1 | FileCheck %s --check-prefix=DEBUG-PRINT
+
// To ensure we print the resource keys, have reference to them
// CHECK: attr = dense_resource<blob1> : tensor<3xi64>
// ZERO: attr = dense_resource<blob1> : tensor<3xi64>
+// BOUND14: attr = dense_resource<blob1> : tensor<3xi64>
+// BOUND13: attr = dense_resource<blob1> : tensor<3xi64>
"test.blob1op"() {attr = dense_resource<blob1> : tensor<3xi64> } : () -> ()
// CHECK-NEXT: attr = dense_resource<blob2> : tensor<3xi64>
// ZERO-NEXT: attr = dense_resource<blob2> : tensor<3xi64>
+// BOUND14-NEXT: attr = dense_resource<blob2> : tensor<3xi64>
+// BOUND13-NEXT: attr = dense_resource<blob2> : tensor<3xi64>
"test.blob2op"() {attr = dense_resource<blob2> : tensor<3xi64> } : () -> ()
+// CHECK-NEXT: attr = dense_resource<blob3> : tensor<1xi8>
+// ZERO-NEXT: attr = dense_resource<blob3> : tensor<1xi8>
+// BOUND14-NEXT: attr = dense_resource<blob3> : tensor<1xi8>
+// BOUND13-NEXT: attr = dense_resource<blob3> : tensor<1xi8>
+"test.blob3op"() {attr = dense_resource<blob3> : tensor<1xi8> } : () -> ()
+
+// DEBUG-ELIDE: eliding resource 'blob3' without materializing (sizeHint=14)
+// DEBUG-PRINT-NOT: eliding resource 'blob3' without materializing
+
// CHECK: {-#
+// CHECK-NEXT: dialect_resources: {
+// CHECK-NEXT: builtin: {
+// CHECK-NEXT: blob3: "0x0800000001"
+// CHECK-NEXT: }
+// CHECK-NEXT: },
// CHECK-NEXT: external_resources: {
// CHECK-NEXT: external: {
// CHECK-NEXT: "backslash\\tab\09": true,
@@ -31,11 +60,44 @@
// ZERO-EMPTY:
// ZERO-NEXT: #-}
+// At the exact boundary (limit == blob3's exact size) blob3 must still be printed,
+// since the sizeHint fast-path only elides when size is strictly greater than the limit.
+// BOUND14: {-#
+// BOUND14-NEXT: dialect_resources: {
+// BOUND14-NEXT: builtin: {
+// BOUND14-NEXT: blob3: "0x0800000001"
+// BOUND14-NEXT: }
+// BOUND14-NEXT: },
+// BOUND14-NEXT: external_resources: {
+// BOUND14-NEXT: external: {
+// BOUND14-NEXT: "backslash\\tab\09": true,
+// BOUND14-NEXT: string: "\22string\22"
+// BOUND14-NEXT: },
+// BOUND14-NEXT: other_stuff: {
+// BOUND14-NEXT: bool: true
+// BOUND14-NEXT: }
+// BOUND14-NEXT: }
+// BOUND14-NEXT: #-}
+
+// One below the boundary, blob3 is elided and no dialect_resources dict is emitted.
+// Note: the escaped `string` entry is also exactly 14 chars, so it is elided here too.
+// BOUND13: {-#
+// BOUND13-NEXT: external_resources: {
+// BOUND13-NEXT: external: {
+// BOUND13-NEXT: "backslash\\tab\09": true
+// BOUND13-NEXT: },
+// BOUND13-NEXT: other_stuff: {
+// BOUND13-NEXT: bool: true
+// BOUND13-NEXT: }
+// BOUND13-NEXT: }
+// BOUND13-NEXT: #-}
+
{-#
dialect_resources: {
builtin: {
blob1: "0x08000000010000000000000002000000000000000300000000000000",
- blob2: "0x08000000040000000000000005000000000000000600000000000000"
+ blob2: "0x08000000040000000000000005000000000000000600000000000000",
+ blob3: "0x0800000001"
}
},
external_resources: {
>From b6bf0cc3f730e3181a66ce7f4f3c8b1ef2f934f8 Mon Sep 17 00:00:00 2001
From: Natanael Cintean <natanael.cintean at intel.com>
Date: Wed, 5 Aug 2026 13:06:19 +0000
Subject: [PATCH 2/2] Remove debug print and testcase based on it as it's not
needed.
---
mlir/lib/IR/AsmPrinter.cpp | 6 +-----
mlir/test/IR/pretty-resources-print.mlir | 10 ----------
2 files changed, 1 insertion(+), 15 deletions(-)
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index 74b5c85a020cb..248d2943b9514 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -3589,12 +3589,8 @@ void OperationPrinter::printResourceFileMetadata(
// Skip serializing entirely if the exact size already exceeds the
// limit, e.g. hex-encoding a large blob.
- if (sizeHint >= 0 && uint64_t(sizeHint) > charLimit.value()) {
- LLVM_DEBUG(llvm::dbgs() << "eliding resource '" << key
- << "' without materializing (sizeHint="
- << sizeHint << ")\n");
+ if (sizeHint >= 0 && uint64_t(sizeHint) > charLimit.value())
return;
- }
llvm::raw_string_ostream ss(resourceStr);
valueFn(ss);
diff --git a/mlir/test/IR/pretty-resources-print.mlir b/mlir/test/IR/pretty-resources-print.mlir
index bd3b271ca7500..a2733d13cebe8 100644
--- a/mlir/test/IR/pretty-resources-print.mlir
+++ b/mlir/test/IR/pretty-resources-print.mlir
@@ -1,4 +1,3 @@
-// REQUIRES: asserts
// Check printing with --mlir-elide-resource-strings-if-larger elides printing large resources
// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=20| FileCheck %s
@@ -10,12 +9,6 @@
// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=14| FileCheck %s --check-prefix=BOUND14
// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=13| FileCheck %s --check-prefix=BOUND13
-// These directly verify the sizeHint fast path itself (not just the resulting output) by
-// checking for its debug log: it must fire when blob3 is elided but not when it is printed.
-// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=13 --debug-only=mlir-asm-printer 2>&1 | FileCheck %s --check-prefix=DEBUG-ELIDE
-// RUN: mlir-opt %s --mlir-elide-resource-strings-if-larger=14 --debug-only=mlir-asm-printer 2>&1 | FileCheck %s --check-prefix=DEBUG-PRINT
-
-
// To ensure we print the resource keys, have reference to them
// CHECK: attr = dense_resource<blob1> : tensor<3xi64>
// ZERO: attr = dense_resource<blob1> : tensor<3xi64>
@@ -35,9 +28,6 @@
// BOUND13-NEXT: attr = dense_resource<blob3> : tensor<1xi8>
"test.blob3op"() {attr = dense_resource<blob3> : tensor<1xi8> } : () -> ()
-// DEBUG-ELIDE: eliding resource 'blob3' without materializing (sizeHint=14)
-// DEBUG-PRINT-NOT: eliding resource 'blob3' without materializing
-
// CHECK: {-#
// CHECK-NEXT: dialect_resources: {
// CHECK-NEXT: builtin: {
More information about the Mlir-commits
mailing list