[Mlir-commits] [mlir] [mlir][bytecode] Allow selecting dialect versions in mlir-opt (PR #233149)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sat Oct 17 16:57:59 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-core

@llvm/pr-subscribers-mlir

Author: Mehdi Amini (joker-eph)

<details>
<summary>Changes</summary>

Add a repeatable --emit-bytecode-dialect-version=dialect=version option backed by BytecodeWriterConfig. Let dialects parse their own version strings through a defaulted BytecodeDialectInterface hook.

Support builtin versions 0 and 1, preserving the absence of dialect version metadata in version 0. Restore the historical builtin fixture and retain the current encoding as a separate fixture. Check emission and direct decoding of both versions, along with option diagnostics and selection of multiple dialect versions.

mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_0.mlirbc is restored back to its original content pre- #<!-- -->232733

Assisted-by: Codex

---
Full diff: https://github.com/llvm/llvm-project/pull/233149.diff


10 Files Affected:

- (modified) mlir/docs/BytecodeFormat.md (+10) 
- (modified) mlir/include/mlir/Bytecode/BytecodeDialectInterface.td (+9) 
- (modified) mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h (+15) 
- (modified) mlir/lib/IR/BuiltinDialectBytecode.cpp (+12-1) 
- (modified) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp (+39) 
- (added) mlir/test/Bytecode/versioning/emit-dialect-version.mlir (+31) 
- (modified) mlir/test/Dialect/Builtin/Bytecode/builtin_fixed.mlir (+10-4) 
- (modified) mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_0.mlirbc () 
- (added) mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_1.mlirbc () 
- (modified) mlir/test/lib/Dialect/Test/TestDialectInterfaces.cpp (+10) 


``````````diff
diff --git a/mlir/docs/BytecodeFormat.md b/mlir/docs/BytecodeFormat.md
index f50ddeb25cc332..36d4c1f0c54c1c 100644
--- a/mlir/docs/BytecodeFormat.md
+++ b/mlir/docs/BytecodeFormat.md
@@ -21,6 +21,16 @@ upgrades post-parsing through the `upgradeFromVersion` method.
 There is no restriction on what kind of information a dialect
 is allowed to encode to model its versioning.
 
+When emitting bytecode, `mlir-opt --emit-bytecode-version=<version>` selects the
+overall bytecode format version. Dialect versions are selected separately with
+`--emit-bytecode-dialect-version=<dialect>=<version>`, which may be repeated for
+different dialects. For example, `mlir-opt --emit-bytecode
+--emit-bytecode-dialect-version=builtin=0` selects the historical builtin dialect
+encoding. A dialect must implement `BytecodeDialectInterface::parseVersion` to
+accept a version string; the dialect defines its syntax and supported versions.
+The resulting version object is passed to the bytecode writer through
+`BytecodeWriterConfig::setDialectVersion`.
+
 [TOC]
 
 ## Magic Number
diff --git a/mlir/include/mlir/Bytecode/BytecodeDialectInterface.td b/mlir/include/mlir/Bytecode/BytecodeDialectInterface.td
index f06715e2339604..d9528504a908ba 100644
--- a/mlir/include/mlir/Bytecode/BytecodeDialectInterface.td
+++ b/mlir/include/mlir/Bytecode/BytecodeDialectInterface.td
@@ -57,6 +57,15 @@ def BytecodeDialectInterface : DialectInterface<"BytecodeDialectInterface"> {
         return ::llvm::failure();
       }]
     >,
+    InterfaceMethod<[{
+        Parse a target dialect version for bytecode emission from a string, for
+        example from a command-line option. Return null if the version is invalid
+        or unsupported. The dialect defines the syntax of the version string.
+      }],
+      "std::unique_ptr<::mlir::DialectVersion>", "parseVersion",
+      (ins "::llvm::StringRef":$version),
+      [{ return nullptr; }]
+    >,
     InterfaceMethod<[{
         Write the version of this dialect to the given writer.
       }],
diff --git a/mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h b/mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h
index 7e0b7024608c91..e6697b376fa005 100644
--- a/mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h
+++ b/mlir/include/mlir/Tools/mlir-opt/MlirOptMain.h
@@ -15,6 +15,7 @@
 
 #include "mlir/Debug/CLOptionsSetup.h"
 #include "mlir/Support/ToolUtilities.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 
 #include <cstdlib>
@@ -128,6 +129,17 @@ class MlirOptMainConfig {
     return emitBytecodeVersion;
   }
 
+  /// Set target dialect versions as strings of the form "dialect=version".
+  /// The dialect defines the syntax of its version string.
+  MlirOptMainConfig &
+  setEmitBytecodeDialectVersions(ArrayRef<std::string> versions) {
+    emitBytecodeDialectVersions.assign(versions.begin(), versions.end());
+    return *this;
+  }
+  ArrayRef<std::string> bytecodeDialectVersionsToEmit() const {
+    return emitBytecodeDialectVersions;
+  }
+
   /// Set the bytecode producer to use.
   MlirOptMainConfig &emitBytecodeProducer(StringRef producer) {
     emitBytecodeProducerFlag = producer.str();
@@ -320,6 +332,9 @@ class MlirOptMainConfig {
   /// Emit bytecode at given version.
   std::optional<int64_t> emitBytecodeVersion = std::nullopt;
 
+  /// Target dialect versions for bytecode emission, as "dialect=version".
+  std::vector<std::string> emitBytecodeDialectVersions;
+
   /// Emit bytecode with given producer.
   std::string emitBytecodeProducerFlag = "";
 
diff --git a/mlir/lib/IR/BuiltinDialectBytecode.cpp b/mlir/lib/IR/BuiltinDialectBytecode.cpp
index e5476082cd6a04..b8bf331f2bbb64 100644
--- a/mlir/lib/IR/BuiltinDialectBytecode.cpp
+++ b/mlir/lib/IR/BuiltinDialectBytecode.cpp
@@ -578,13 +578,24 @@ struct BuiltinDialectBytecodeInterface : public BytecodeDialectInterface {
   //===--------------------------------------------------------------------===//
   // Version
 
+  std::unique_ptr<DialectVersion>
+  parseVersion(StringRef version) const override {
+    int64_t parsedVersion;
+    if (version.getAsInteger(10, parsedVersion) || parsedVersion < 0 ||
+        parsedVersion > BuiltinDialectVersion::getCurrentVersion().getVersion())
+      return nullptr;
+    return std::make_unique<BuiltinDialectVersion>(parsedVersion);
+  }
+
   void writeVersion(DialectBytecodeWriter &writer) const override {
     auto configVersion = writer.getDialectVersion(getDialect()->getNamespace());
     // Write version set in config.
     if (succeeded(configVersion)) {
       auto *version =
           static_cast<const BuiltinDialectVersion *>(*configVersion);
-      writer.writeVarInt(static_cast<uint64_t>(version->getVersion()));
+      // Version 0 predates dialect version metadata. Preserve its encoding.
+      if (version->getVersion() > 0)
+        writer.writeVarInt(static_cast<uint64_t>(version->getVersion()));
       return;
     }
     // Else, write current set version version if not 0.
diff --git a/mlir/lib/Tools/mlir-opt/MlirOptMain.cpp b/mlir/lib/Tools/mlir-opt/MlirOptMain.cpp
index d4812aec50fc19..490e0c20f86f6c 100644
--- a/mlir/lib/Tools/mlir-opt/MlirOptMain.cpp
+++ b/mlir/lib/Tools/mlir-opt/MlirOptMain.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "mlir/Tools/mlir-opt/MlirOptMain.h"
+#include "mlir/Bytecode/BytecodeImplementation.h"
 #include "mlir/Bytecode/BytecodeWriter.h"
 #include "mlir/Debug/CLOptionsSetup.h"
 #include "mlir/Debug/Counter.h"
@@ -36,6 +37,7 @@
 #include "mlir/Tools/Plugins/PassPlugin.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/Remarks/RemarkFormat.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -107,6 +109,15 @@ struct MlirOptMainConfigCLOptions : public MlirOptMainConfig {
             cl::desc("Use specified bytecode when generating output"),
             cl::location(emitBytecodeVersion), cl::init(std::nullopt));
 
+    static cl::list<std::string, std::vector<std::string>>
+        bytecodeDialectVersions(
+            "emit-bytecode-dialect-version",
+            cl::desc(
+                "Use the specified dialect version when generating bytecode "
+                "(may be repeated for different dialects)"),
+            cl::value_desc("dialect=version"),
+            cl::location(emitBytecodeDialectVersions));
+
     static cl::opt<std::string, /*ExternalStorage=*/true> irdlFile(
         "irdl-file",
         cl::desc("IRDL file to register before processing the input"),
@@ -620,6 +631,30 @@ performActions(raw_ostream &os,
                  : BytecodeWriterConfig(fallbackResourceMap);
     if (auto v = config.bytecodeVersionToEmit())
       writerConfig.setDesiredBytecodeVersion(*v);
+    llvm::StringSet<> versionedDialects;
+    for (StringRef entry : config.bytecodeDialectVersionsToEmit()) {
+      auto [name, version] = entry.split('=');
+      if (name.empty() || version.empty())
+        return op.get()->emitError() << "expected 'dialect=version' for "
+                                        "--emit-bytecode-dialect-version, got '"
+                                     << entry << "'";
+      if (!versionedDialects.insert(name).second)
+        return op.get()->emitError()
+               << "duplicate bytecode version for dialect '" << name << "'";
+      Dialect *dialect = context->getOrLoadDialect(name);
+      if (!dialect)
+        return op.get()->emitError()
+               << "unknown dialect '" << name << "' for bytecode emission";
+      auto *interface =
+          dialect->getRegisteredInterface<BytecodeDialectInterface>();
+      std::unique_ptr<DialectVersion> parsedVersion =
+          interface ? interface->parseVersion(version) : nullptr;
+      if (!parsedVersion)
+        return op.get()->emitError()
+               << "dialect '" << name << "' does not support emitting bytecode "
+               << "version '" << version << "'";
+      writerConfig.setDialectVersion(name, std::move(parsedVersion));
+    }
     if (config.shouldElideResourceDataFromBytecode())
       writerConfig.setElideResourceDataFlag();
     return writeBytecodeToFile(op.get(), os, writerConfig);
@@ -629,6 +664,10 @@ performActions(raw_ostream &os,
     return emitError(UnknownLoc::get(pm.getContext()))
            << "bytecode version while not emitting bytecode";
 
+  if (!config.bytecodeDialectVersionsToEmit().empty())
+    return emitError(UnknownLoc::get(pm.getContext()))
+           << "dialect bytecode version while not emitting bytecode";
+
   // Don't re-run the verifier if we already ran the verifier at the end of the
   // pass pipeline.
   AsmState asmState(op.get(),
diff --git a/mlir/test/Bytecode/versioning/emit-dialect-version.mlir b/mlir/test/Bytecode/versioning/emit-dialect-version.mlir
new file mode 100644
index 00000000000000..166ee3f08326d8
--- /dev/null
+++ b/mlir/test/Bytecode/versioning/emit-dialect-version.mlir
@@ -0,0 +1,31 @@
+// RUN: mlir-opt %s -emit-bytecode -emit-bytecode-producer=fixed -emit-bytecode-dialect-version=builtin=0 -emit-bytecode-dialect-version=test=2.0 -o %t.0
+// RUN: mlir-opt %s -emit-bytecode -emit-bytecode-producer=fixed -emit-bytecode-dialect-version=builtin=1 -emit-bytecode-dialect-version=test=2.0 -o %t.1
+// RUN: not cmp -s %t.0 %t.1
+// RUN: mlir-opt %t.0 | FileCheck %s
+// RUN: mlir-opt %t.1 | FileCheck %s
+// RUN: mlir-opt %s -emit-bytecode -emit-bytecode-producer=fixed -o %t.default
+// RUN: cmp -s %t.1 %t.default
+// RUN: mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=builtin=0 -emit-bytecode-dialect-version=test=1.12 -o %t.old-test
+// RUN: mlir-opt %t.old-test | FileCheck %s
+// RUN: not mlir-opt %s -emit-bytecode-dialect-version=builtin=0 2>&1 | FileCheck %s --check-prefix=NO-BYTECODE
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=builtin 2>&1 | FileCheck %s --check-prefix=MALFORMED
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=builtin= 2>&1 | FileCheck %s --check-prefix=MALFORMED
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version==0 2>&1 | FileCheck %s --check-prefix=MALFORMED
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=unknown=0 2>&1 | FileCheck %s --check-prefix=UNKNOWN
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=arith=0 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED-DIALECT
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=func=1 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED-PARSER
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=builtin=-1 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED-VERSION
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=builtin=2 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED-VERSION
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=builtin=abc 2>&1 | FileCheck %s --check-prefix=UNSUPPORTED-VERSION
+// RUN: not mlir-opt %s -emit-bytecode -emit-bytecode-dialect-version=builtin=0 -emit-bytecode-dialect-version=builtin=1 2>&1 | FileCheck %s --check-prefix=DUPLICATE
+
+// NO-BYTECODE: dialect bytecode version while not emitting bytecode
+// MALFORMED: expected 'dialect=version' for --emit-bytecode-dialect-version
+// UNKNOWN: unknown dialect 'unknown' for bytecode emission
+// UNSUPPORTED-DIALECT: dialect 'arith' does not support emitting bytecode version '0'
+// UNSUPPORTED-PARSER: dialect 'func' does not support emitting bytecode version '1'
+// UNSUPPORTED-VERSION: dialect 'builtin' does not support emitting bytecode version
+// DUPLICATE: duplicate bytecode version for dialect 'builtin'
+
+// CHECK: "test.versionedA"() <{dims = 123 : i64, modifier = false}>
+"test.versionedA"() <{dims = 123 : i64, modifier = false}> : () -> ()
diff --git a/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed.mlir b/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed.mlir
index ebb198456b3c12..1a2527a45f01b5 100644
--- a/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed.mlir
+++ b/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed.mlir
@@ -1,8 +1,14 @@
+// Verify that both historical and current builtin encodings are unchanged.
+// RUN: mlir-opt -allow-unregistered-dialect -emit-bytecode -emit-bytecode-producer=fixed -emit-bytecode-dialect-version=builtin=0 %s -o %t.0
+// RUN: cmp -s %t.0 %p/builtin_fixed_0.mlirbc
+// RUN: mlir-opt -allow-unregistered-dialect -emit-bytecode -emit-bytecode-producer=fixed -emit-bytecode-dialect-version=builtin=1 %s -o %t.1
+// RUN: cmp -s %t.1 %p/builtin_fixed_1.mlirbc
+// Verify that the default encoding is the current version.
 // RUN: mlir-opt -allow-unregistered-dialect -emit-bytecode -emit-bytecode-producer=fixed %s -o %t
-// Verify unchanged.
-// RUN: cmp -s %t %p/builtin_fixed_0.mlirbc
-// Verify can read as expected.
-// RUN: mlir-opt -allow-unregistered-dialect -mlir-print-local-scope %t | FileCheck %s
+// RUN: cmp -s %t %t.1
+// Verify that the checked-in fixtures can be read as expected.
+// RUN: mlir-opt -allow-unregistered-dialect -mlir-print-local-scope %p/builtin_fixed_0.mlirbc | FileCheck %s
+// RUN: mlir-opt -allow-unregistered-dialect -mlir-print-local-scope %p/builtin_fixed_1.mlirbc | FileCheck %s
 
 // Regression test for all builtin dialect bytecode attribute and
 // type encodings. Exercises every entry in BuiltinDialectAttributes and
diff --git a/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_0.mlirbc b/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_0.mlirbc
index ee79226875ffae..4f661b5a1d2333 100644
Binary files a/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_0.mlirbc and b/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_0.mlirbc differ
diff --git a/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_1.mlirbc b/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_1.mlirbc
new file mode 100644
index 00000000000000..ee79226875ffae
Binary files /dev/null and b/mlir/test/Dialect/Builtin/Bytecode/builtin_fixed_1.mlirbc differ
diff --git a/mlir/test/lib/Dialect/Test/TestDialectInterfaces.cpp b/mlir/test/lib/Dialect/Test/TestDialectInterfaces.cpp
index 2b52978fe3636d..7a567aecb4333e 100644
--- a/mlir/test/lib/Dialect/Test/TestDialectInterfaces.cpp
+++ b/mlir/test/lib/Dialect/Test/TestDialectInterfaces.cpp
@@ -97,6 +97,16 @@ struct TestBytecodeDialectInterface : public BytecodeDialectInterface {
     return Attribute();
   }
 
+  std::unique_ptr<DialectVersion> parseVersion(StringRef version) const final {
+    auto [major, minor] = version.split('.');
+    uint32_t majorVersion, minorVersion;
+    if (major.getAsInteger(10, majorVersion) ||
+        minor.getAsInteger(10, minorVersion) || majorVersion > 2 ||
+        (majorVersion == 2 && minorVersion > 0))
+      return nullptr;
+    return std::make_unique<TestDialectVersion>(majorVersion, minorVersion);
+  }
+
   // Emit a specific version of the dialect.
   void writeVersion(DialectBytecodeWriter &writer) const final {
     // Construct the current dialect version.

``````````

</details>


https://github.com/llvm/llvm-project/pull/233149


More information about the Mlir-commits mailing list