[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