[Mlir-commits] [mlir] [MLIR][LLVM] Add bytecode support for several attributes (PR #162577)

Bruno Cardoso Lopes llvmlistbot at llvm.org
Fri Oct 10 12:03:59 PDT 2025


https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/162577

>From bce08030595c63a2afe5a438079d6d15d2c2232e Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Wed, 8 Oct 2025 17:25:08 -0700
Subject: [PATCH 1/4] [MLIR][LLVM] Add bytecode support for several attributes

---
 .../mlir/Dialect/LLVMIR/CMakeLists.txt        |   4 +
 .../Dialect/LLVMIR/LLVMDialectBytecode.td     | 357 ++++++++++++++++++
 mlir/lib/Dialect/LLVMIR/CMakeLists.txt        |   2 +
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    |   3 +
 .../Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp | 155 ++++++++
 .../Dialect/LLVMIR/IR/LLVMDialectBytecode.h   |  27 ++
 mlir/test/Dialect/LLVMIR/bytecode.mlir        |  69 ++++
 mlir/test/Dialect/LLVMIR/debuginfo.mlir       |   1 +
 mlir/test/Dialect/LLVMIR/roundtrip.mlir       |   5 +-
 9 files changed, 621 insertions(+), 2 deletions(-)
 create mode 100644 mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
 create mode 100644 mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
 create mode 100644 mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h
 create mode 100644 mlir/test/Dialect/LLVMIR/bytecode.mlir

diff --git a/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt b/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt
index 8d9474bf37894..c301e0b40e8fe 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt
+++ b/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt
@@ -48,6 +48,10 @@ mlir_tablegen(LLVMIntrinsicFromLLVMIRConversions.inc -gen-intr-from-llvmir-conve
 mlir_tablegen(LLVMConvertibleLLVMIRIntrinsics.inc -gen-convertible-llvmir-intrinsics)
 add_mlir_dialect_tablegen_target(MLIRLLVMIntrinsicConversionsIncGen)
 
+set(LLVM_TARGET_DEFINITIONS LLVMDialectBytecode.td)
+mlir_tablegen(LLVMDialectBytecode.cpp.inc -gen-bytecode -bytecode-dialect="LLVM")
+add_public_tablegen_target(MLIRLLVMDialectBytecodeIncGen)
+
 set(LLVM_TARGET_DEFINITIONS BasicPtxBuilderInterface.td)
 mlir_tablegen(BasicPtxBuilderInterface.h.inc -gen-op-interface-decls)
 mlir_tablegen(BasicPtxBuilderInterface.cpp.inc -gen-op-interface-defs)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
new file mode 100644
index 0000000000000..2474dece3f7c3
--- /dev/null
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
@@ -0,0 +1,357 @@
+//===-- LLVMDialectBytecode.td - LLVM bytecode defs --------*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the LLVM bytecode reader/writer definition file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DIALECT_BYTECODE
+#define LLVM_DIALECT_BYTECODE
+
+include "mlir/IR/BytecodeBase.td"
+
+//===----------------------------------------------------------------------===//
+// Bytecode classes for attributes and types.
+//===----------------------------------------------------------------------===//
+
+def String :
+  WithParser <"succeeded($_reader.readString($_var))",
+  WithBuilder<"$_args",
+  WithPrinter<"$_writer.writeOwnedString($_getter)",
+  WithType   <"StringRef">>>>;
+
+class Attr<string type> : WithType<type, Attribute>;
+
+class OptionalAttribute<string type> :
+  WithParser <"succeeded($_reader.readOptionalAttribute($_var))",
+  WithPrinter<"$_writer.writeOptionalAttribute($_getter)",
+  WithType<type, Attribute>>>;
+
+class OptionalInt<string type> :
+  WithParser <"succeeded(readOptionalInt($_reader, $_var))",
+  WithPrinter<"writeOptionalInt($_writer, $_getter)",
+  WithType<"std::optional<" # type # ">", VarInt>>>;
+
+class OptionalArrayRef<string eltType> :
+  WithParser <"succeeded(readOptionalArrayRef<"
+    # eltType # ">($_reader, $_var))",
+  WithPrinter<"writeOptionalArrayRef<"
+    # eltType # ">($_writer, $_getter)",
+  WithType<"SmallVector<"
+    # eltType # ">", Attribute>>>;
+
+class EnumClassFlag<string flag, string getter> :
+    WithParser<"succeeded($_reader.readVarInt($_var))",
+    WithBuilder<"(" # flag # ")$_args",
+    WithPrinter<"$_writer.writeVarInt((uint64_t)$_name." # getter # ")",
+    WithType<"uint64_t", VarInt>>>>;
+
+//===----------------------------------------------------------------------===//
+// General notes
+// - For each attribute or type entry, the argument names should match
+//   LLVMAttrDefs.td
+// - The mnemonics are either LLVM or builtin MLIR attributes and types, but
+//   regular C++ types are also allowed to match builders and parsers.
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// AliasScopeAttr
+//===----------------------------------------------------------------------===//
+
+def AliasScopeAttr : DialectAttribute<(attr
+  Attr<"Attribute">:$id,
+  Attr<"AliasScopeDomainAttr">:$domain,
+  OptionalAttribute<"StringAttr">:$description
+)>;
+
+//===----------------------------------------------------------------------===//
+// DIBasicTypeAttr
+//===----------------------------------------------------------------------===//
+
+def DIBasicTypeAttr : DialectAttribute<(attr
+  VarInt:$tag,
+  String:$name,
+  VarInt:$sizeInBits,
+  VarInt:$encoding
+)>;
+
+//===----------------------------------------------------------------------===//
+// DIExpressionAttr, DIExpressionElemAttr
+//===----------------------------------------------------------------------===//
+
+def DIExpressionElemAttr : DialectAttribute<(attr
+  VarInt:$opcode,
+  OptionalArrayRef<"uint64_t">:$arguments
+)>;
+
+def DIExpressionAttr : DialectAttribute<(attr
+  OptionalArrayRef<"DIExpressionElemAttr">:$operations
+)>;
+
+//===----------------------------------------------------------------------===//
+// DIFileAttr
+//===----------------------------------------------------------------------===//
+
+def DIFileAttr : DialectAttribute<(attr
+  String:$name,
+  String:$directory
+)>;
+
+//===----------------------------------------------------------------------===//
+// DILocalVariableAttr
+//===----------------------------------------------------------------------===//
+
+def DILocalVariableAttr : DialectAttribute<(attr
+  Attr<"DIScopeAttr">:$scope, // Non-optional attribute
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalAttribute<"DIFileAttr">:$file,
+  VarInt:$line,
+  VarInt:$arg,
+  VarInt:$alignInBits,
+  OptionalAttribute<"DITypeAttr">:$type,
+  EnumClassFlag<"DIFlags", "getFlags()">:$_rawflags,
+  LocalVar<"DIFlags", "(DIFlags)_rawflags">:$flags
+)> {
+  // DILocalVariableAttr direct getter uses a `StringRef` for `name`. Since the
+  // more direct getter is prefered during bytecode reading, force the base one
+  // and prevent crashes for empty `StringAttr`.
+  let cBuilder = "$_resultType::get(context, $_args)";
+}
+
+//===----------------------------------------------------------------------===//
+// DISubroutineTypeAttr
+//===----------------------------------------------------------------------===//
+
+def DISubroutineTypeAttr : DialectAttribute<(attr
+  VarInt:$callingConvention,
+  OptionalArrayRef<"DITypeAttr">:$types
+)>;
+
+//===----------------------------------------------------------------------===//
+// DICompileUnitAttr
+//===----------------------------------------------------------------------===//
+
+def DICompileUnitAttr : DialectAttribute<(attr
+  Attr<"DistinctAttr">:$id,
+  VarInt:$sourceLanguage,
+  Attr<"DIFileAttr">:$file,
+  OptionalAttribute<"StringAttr">:$producer,
+  Bool:$isOptimized,
+  EnumClassFlag<"DIEmissionKind", "getEmissionKind()">:$_rawEmissionKind,
+  LocalVar<"DIEmissionKind", "(DIEmissionKind)_rawEmissionKind">:$emissionKind,
+  EnumClassFlag<"DINameTableKind", "getNameTableKind()">:$_rawNameTableKind,
+  LocalVar<"DINameTableKind",
+           "(DINameTableKind)_rawNameTableKind">:$nameTableKind
+)>;
+
+//===----------------------------------------------------------------------===//
+// DISubprogramAttr
+//===----------------------------------------------------------------------===//
+
+def DISubprogramAttr : DialectAttribute<(attr
+  OptionalAttribute<"DistinctAttr">:$recId,
+  Bool:$isRecSelf,
+  OptionalAttribute<"DistinctAttr">:$id,
+  OptionalAttribute<"DICompileUnitAttr">:$compileUnit,
+  OptionalAttribute<"DIScopeAttr">:$scope, // TODO: DIScopeAttr
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalAttribute<"StringAttr">:$linkageName,
+  OptionalAttribute<"DIFileAttr">:$file,
+  VarInt:$line,
+  VarInt:$scopeLine,
+  EnumClassFlag<"DISubprogramFlags", "getSubprogramFlags()">:$_rawflags,
+  LocalVar<"DISubprogramFlags", "(DISubprogramFlags)_rawflags">:$subprogramFlags,
+  OptionalAttribute<"DISubroutineTypeAttr">:$type,
+  OptionalArrayRef<"DINodeAttr">:$retainedNodes,
+  OptionalArrayRef<"DINodeAttr">:$annotations
+)>;
+
+//===----------------------------------------------------------------------===//
+// DICompositeTypeAttr
+//===----------------------------------------------------------------------===//
+
+def DICompositeTypeAttr : DialectAttribute<(attr
+  OptionalAttribute<"DistinctAttr">:$recId,
+  Bool:$isRecSelf,
+  VarInt:$tag,
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalAttribute<"DIFileAttr">:$file,
+  VarInt:$line,
+  OptionalAttribute<"DIScopeAttr">:$scope,
+  OptionalAttribute<"DITypeAttr">:$baseType,
+  EnumClassFlag<"DIFlags", "getFlags()">:$_rawflags,
+  LocalVar<"DIFlags", "(DIFlags)_rawflags">:$flags,
+  VarInt:$sizeInBits,
+  VarInt:$alignInBits,
+  OptionalArrayRef<"DINodeAttr">:$elements,
+  OptionalAttribute<"DIExpressionAttr">:$dataLocation,
+  OptionalAttribute<"DIExpressionAttr">:$rank,
+  OptionalAttribute<"DIExpressionAttr">:$allocated,
+  OptionalAttribute<"DIExpressionAttr">:$associated
+)>;
+
+//===----------------------------------------------------------------------===//
+// DIDerivedTypeAttr
+//===----------------------------------------------------------------------===//
+
+def DIDerivedTypeAttr : DialectAttribute<(attr
+  VarInt:$tag,
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalAttribute<"DITypeAttr">:$baseType,
+  VarInt:$sizeInBits,
+  VarInt:$alignInBits,
+  VarInt:$offsetInBits,
+  OptionalInt<"unsigned">:$dwarfAddressSpace,
+  OptionalAttribute<"DINodeAttr">:$extraData // TODO: DINodeAttr
+)>;
+
+//===----------------------------------------------------------------------===//
+// DIImportedEntityAttr
+//===----------------------------------------------------------------------===//
+
+def DIImportedEntityAttr : DialectAttribute<(attr
+  VarInt:$tag,
+  Attr<"DIScopeAttr">:$scope,
+  Attr<"DINodeAttr">:$entity,
+  OptionalAttribute<"DIFileAttr">:$file,
+  VarInt:$line,
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalArrayRef<"DINodeAttr">:$elements
+)>;
+
+//===----------------------------------------------------------------------===//
+// DIGlobalVariableAttr, DIGlobalVariableExpressionAttr
+//===----------------------------------------------------------------------===//
+
+def DIGlobalVariableAttr : DialectAttribute<(attr
+  OptionalAttribute<"DIScopeAttr">:$scope,
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalAttribute<"StringAttr">:$linkageName,
+  Attr<"DIFileAttr">:$file,
+  VarInt:$line,
+  Attr<"DITypeAttr">:$type,
+  Bool:$isLocalToUnit,
+  Bool:$isDefined,
+  VarInt:$alignInBits
+)>;
+
+def DIGlobalVariableExpressionAttr : DialectAttribute<(attr
+  Attr<"DIGlobalVariableAttr">:$var,
+  OptionalAttribute<"DIExpressionAttr">:$expr
+)>;
+
+//===----------------------------------------------------------------------===//
+// DILabelAttr
+//===----------------------------------------------------------------------===//
+
+def DILabelAttr : DialectAttribute<(attr
+  Attr<"DIScopeAttr">:$scope,
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalAttribute<"DIFileAttr">:$file,
+  VarInt:$line
+)> {
+  // DILabelAttr direct getter uses a `StringRef` for `name`. Since the
+  // more direct getter is prefered during bytecode reading, force the base one
+  // and prevent crashes for empty `StringAttr`.
+  let cBuilder = "$_resultType::get(context, $_args)";
+}
+
+//===----------------------------------------------------------------------===//
+// DILexicalBlockAttr, DILexicalBlockFileAttr
+//===----------------------------------------------------------------------===//
+
+def DILexicalBlockAttr : DialectAttribute<(attr
+  Attr<"DIScopeAttr">:$scope,
+  OptionalAttribute<"DIFileAttr">:$file,
+  VarInt:$line,
+  VarInt:$column
+)>;
+
+def DILexicalBlockFileAttr : DialectAttribute<(attr
+  Attr<"DIScopeAttr">:$scope,
+  OptionalAttribute<"DIFileAttr">:$file,
+  VarInt:$discriminator
+)>;
+
+//===----------------------------------------------------------------------===//
+// DINamespaceAttr
+//===----------------------------------------------------------------------===//
+
+def DINamespaceAttr : DialectAttribute<(attr
+  OptionalAttribute<"StringAttr">:$name,
+  OptionalAttribute<"DIScopeAttr">:$scope,
+  Bool:$exportSymbols
+)>;
+
+//===----------------------------------------------------------------------===//
+// DISubrangeAttr
+//===----------------------------------------------------------------------===//
+
+def DISubrangeAttr : DialectAttribute<(attr
+  OptionalAttribute<"Attribute">:$count,
+  OptionalAttribute<"Attribute">:$lowerBound,
+  OptionalAttribute<"Attribute">:$upperBound,
+  OptionalAttribute<"Attribute">:$stride
+)>;
+
+//===----------------------------------------------------------------------===//
+// LoopAnnotationAttr
+//===----------------------------------------------------------------------===//
+
+def LoopAnnotationAttr : DialectAttribute<(attr
+  OptionalAttribute<"BoolAttr">:$disableNonforced,
+  OptionalAttribute<"LoopVectorizeAttr">:$vectorize, // TODO: LoopVectorizeAttr
+  OptionalAttribute<"LoopInterleaveAttr">:$interleave, // TODO: LoopInterleaveAttr
+  OptionalAttribute<"LoopUnrollAttr">:$unroll, // TODO: LoopUnrollAttr
+  OptionalAttribute<"LoopUnrollAndJamAttr">:$unrollAndJam, // TODO: LoopUnrollAndJamAttr
+  OptionalAttribute<"LoopLICMAttr">:$licm, // TODO: LoopLICMAttr
+  OptionalAttribute<"LoopDistributeAttr">:$distribute, // TODO: LoopDistributeAttr
+  OptionalAttribute<"LoopPipelineAttr">:$pipeline, // TODO: LoopPipelineAttr
+  OptionalAttribute<"LoopPeeledAttr">:$peeled, // TODO: LoopPeeledAttr
+  OptionalAttribute<"LoopUnswitchAttr">:$unswitch, // TODO: LoopUnswitchAttr
+  OptionalAttribute<"BoolAttr">:$mustProgress,
+  OptionalAttribute<"BoolAttr">:$isVectorized,
+  OptionalAttribute<"FusedLoc">:$startLoc,
+  OptionalAttribute<"FusedLoc">:$endLoc,
+  OptionalArrayRef<"AccessGroupAttr">:$parallelAccesses // TODO: AccessGroupAttr
+)>;
+
+//===----------------------------------------------------------------------===//
+// Attributes & Types with custom bytecode handling.
+//===----------------------------------------------------------------------===//
+
+def LLVMDialectAttributes : DialectAttributes<"LLVM"> {
+  let elems = [
+    AliasScopeAttr,
+    DIBasicTypeAttr,
+    DICompileUnitAttr,
+    DICompositeTypeAttr,
+    DIDerivedTypeAttr,
+    DIExpressionElemAttr,
+    DIExpressionAttr,
+    DIFileAttr,
+    DIGlobalVariableAttr,
+    DIGlobalVariableExpressionAttr,
+    DIImportedEntityAttr,
+    DILabelAttr,
+    DILexicalBlockAttr,
+    DILexicalBlockFileAttr,
+    DILocalVariableAttr,
+    DINamespaceAttr,
+    DISubprogramAttr,
+    DISubrangeAttr,
+    DISubroutineTypeAttr,
+    LoopAnnotationAttr
+  ];
+}
+
+def LLVMDialectTypes : DialectTypes<"LLVM"> {
+  let elems = [];
+}
+
+#endif // LLVM_DIALECT_BYTECODE
diff --git a/mlir/lib/Dialect/LLVMIR/CMakeLists.txt b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt
index ec581ac7277e3..cc66face1c002 100644
--- a/mlir/lib/Dialect/LLVMIR/CMakeLists.txt
+++ b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt
@@ -8,11 +8,13 @@ add_mlir_dialect_library(MLIRLLVMDialect
   IR/LLVMMemorySlot.cpp
   IR/LLVMTypes.cpp
   IR/LLVMTypeSyntax.cpp
+  IR/LLVMDialectBytecode.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/LLVMIR
 
   DEPENDS
+  MLIRLLVMDialectBytecodeIncGen
   MLIRLLVMOpsIncGen
   MLIRLLVMTypesIncGen
   MLIRLLVMIntrinsicOpsIncGen
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 5d08cccb4faab..7ca09d9c943e0 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -29,6 +29,8 @@
 #include "llvm/IR/DataLayout.h"
 #include "llvm/Support/Error.h"
 
+#include "LLVMDialectBytecode.h"
+
 #include <numeric>
 #include <optional>
 
@@ -4237,6 +4239,7 @@ void LLVMDialect::initialize() {
   // Support unknown operations because not all LLVM operations are registered.
   allowUnknownOperations();
   declarePromisedInterface<DialectInlinerInterface, LLVMDialect>();
+  detail::addBytecodeInterface(this);
 }
 
 #define GET_OP_CLASSES
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
new file mode 100644
index 0000000000000..f06c764ab5f6d
--- /dev/null
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
@@ -0,0 +1,155 @@
+//===- LLVMDialectBytecode.cpp - LLVM Bytecode Implementation -------------===//
+//
+// 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 "LLVMDialectBytecode.h"
+#include "mlir/Bytecode/BytecodeImplementation.h"
+#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
+#include "mlir/IR/Diagnostics.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include <type_traits>
+
+using namespace mlir;
+using namespace mlir::LLVM;
+
+namespace {
+
+// Provide some forward declarations of the functions that will be generated by
+// the include below.
+static void write(DIExpressionElemAttr attribute,
+                  DialectBytecodeWriter &writer);
+static LogicalResult writeAttribute(Attribute attribute,
+                                    DialectBytecodeWriter &writer);
+
+//===--------------------------------------------------------------------===//
+// Optional ArrayRefs
+//
+// Note that both the writer and reader functions consider attributes to be
+// optional. This is because the attribute may be present by empty.
+//===--------------------------------------------------------------------===//
+
+template <class EntryTy>
+static void writeOptionalArrayRef(DialectBytecodeWriter &writer,
+                                  ArrayRef<EntryTy> storage) {
+  if (!storage.empty()) {
+    writer.writeOwnedBool(true);
+    writer.writeList(storage, [&](EntryTy val) {
+      if constexpr (std::is_base_of_v<Attribute, EntryTy>) {
+        (void)writer.writeOptionalAttribute(val);
+      } else if constexpr (std::is_integral_v<EntryTy>) {
+        (void)writer.writeVarInt(val);
+      } else
+        static_assert(true, "EntryTy not supported");
+    });
+  } else {
+    writer.writeOwnedBool(false);
+  }
+}
+
+template <class EntryTy>
+static LogicalResult readOptionalArrayRef(DialectBytecodeReader &reader,
+                                          SmallVectorImpl<EntryTy> &storage) {
+  bool isPresent = false;
+  if (failed(reader.readBool(isPresent)))
+    return failure();
+  // Nothing to do here, the array is empty.
+  if (!isPresent)
+    return success();
+
+  auto readOperations = [&]() -> FailureOr<EntryTy> {
+    EntryTy temp;
+    if constexpr (std::is_base_of_v<Attribute, EntryTy>) {
+      if (succeeded(reader.readOptionalAttribute(temp)))
+        return temp;
+    } else if constexpr (std::is_integral_v<EntryTy>) {
+      if (succeeded(reader.readVarInt(temp)))
+        return temp;
+    } else
+      static_assert(true, "EntryTy not supported");
+    return failure();
+  };
+
+  return reader.readList(storage, readOperations);
+}
+
+//===--------------------------------------------------------------------===//
+// Optional integral types
+//===--------------------------------------------------------------------===//
+
+template <class EntryTy>
+static void writeOptionalInt(DialectBytecodeWriter &writer,
+                             std::optional<EntryTy> storage) {
+  static_assert(std::is_integral_v<EntryTy>,
+                "EntryTy must be an integral type");
+  EntryTy val = storage.value_or(0);
+  writer.writeVarIntWithFlag(val, storage.has_value());
+}
+
+template <class EntryTy>
+static LogicalResult readOptionalInt(DialectBytecodeReader &reader,
+                                     std::optional<EntryTy> &storage) {
+  static_assert(std::is_integral_v<EntryTy>,
+                "EntryTy must be an integral type");
+  uint64_t result = 0;
+  bool flag = false;
+  if (failed(reader.readVarIntWithFlag(result, flag)))
+    return failure();
+  if (flag)
+    storage = (decltype(storage.value()))result;
+  else
+    storage = std::nullopt;
+  return success();
+}
+
+//===--------------------------------------------------------------------===//
+// Tablegen generated bytecode functions
+//===--------------------------------------------------------------------===//
+
+#include "mlir/Dialect/LLVMIR/LLVMDialectBytecode.cpp.inc"
+
+//===--------------------------------------------------------------------===//
+// LLVMDialectBytecodeInterface
+//===--------------------------------------------------------------------===//
+
+/// This class implements the bytecode interface for the LLVM dialect.
+struct LLVMDialectBytecodeInterface : public BytecodeDialectInterface {
+  LLVMDialectBytecodeInterface(Dialect *dialect)
+      : BytecodeDialectInterface(dialect) {}
+
+  //===--------------------------------------------------------------------===//
+  // Attributes
+
+  Attribute readAttribute(DialectBytecodeReader &reader) const override {
+    return ::readAttribute(getContext(), reader);
+  }
+
+  LogicalResult writeAttribute(Attribute attr,
+                               DialectBytecodeWriter &writer) const override {
+    return ::writeAttribute(attr, writer);
+  }
+
+  //===--------------------------------------------------------------------===//
+  // Types
+
+  Type readType(DialectBytecodeReader &reader) const override {
+    return ::readType(getContext(), reader);
+  }
+
+  LogicalResult writeType(Type type,
+                          DialectBytecodeWriter &writer) const override {
+    return ::writeType(type, writer);
+  }
+};
+} // namespace
+
+void LLVM::detail::addBytecodeInterface(LLVMDialect *dialect) {
+  dialect->addInterfaces<LLVMDialectBytecodeInterface>();
+}
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h
new file mode 100644
index 0000000000000..5e25d3948afb9
--- /dev/null
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h
@@ -0,0 +1,27 @@
+//===- LLVMDialectBytecode.h - LLVM Bytecode Implementation -----*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines hooks into the LLVMization dialect bytecode
+// implementation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIB_MLIR_DIALECT_LLVM_IR_LLVMDIALECTBYTECODE_H
+#define LIB_MLIR_DIALECT_LLVM_IR_LLVMDIALECTBYTECODE_H
+
+namespace mlir::LLVM {
+class LLVMDialect;
+
+namespace detail {
+/// Add the interfaces necessary for encoding the LLVMization dialect
+/// components in bytecode.
+void addBytecodeInterface(LLVMDialect *dialect);
+} // namespace detail
+} // namespace mlir::LLVM
+
+#endif // LIB_MLIR_DIALECT_LLVM_IR_LLVMDIALECTBYTECODE_H
diff --git a/mlir/test/Dialect/LLVMIR/bytecode.mlir b/mlir/test/Dialect/LLVMIR/bytecode.mlir
new file mode 100644
index 0000000000000..6f273ea2d90e9
--- /dev/null
+++ b/mlir/test/Dialect/LLVMIR/bytecode.mlir
@@ -0,0 +1,69 @@
+// RUN: mlir-opt -emit-bytecode %s | mlir-opt --mlir-print-debuginfo | FileCheck %s
+
+#access_group = #llvm.access_group<id = distinct[0]<>>
+#access_group1 = #llvm.access_group<id = distinct[1]<>>
+#di_subprogram = #llvm.di_subprogram<recId = distinct[2]<>>
+#loc1 = loc("test.f90":12:14)
+#loc2 = loc("test":4:3)
+#loc6 = loc(fused<#di_subprogram>[#loc1])
+#loc7 = loc(fused<#di_subprogram>[#loc2])
+#loop_annotation = #llvm.loop_annotation<disableNonforced = false, mustProgress = true, startLoc = #loc6, endLoc = #loc7, parallelAccesses = #access_group, #access_group1>
+module {
+  llvm.func @imp_fn() {
+    llvm.return loc(#loc2)
+  } loc(#loc8)
+  llvm.func @loop_annotation_with_locs() {
+    llvm.br ^bb1 {loop_annotation = #loop_annotation} loc(#loc4)
+  ^bb1:  // pred: ^bb0
+    llvm.return loc(#loc5)
+  } loc(#loc3)
+} loc(#loc)
+#di_file = #llvm.di_file<"test.f90" in "">
+#di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_program>
+#loc = loc("test":0:0)
+#loc3 = loc("test-path":36:3)
+#loc4 = loc("test-path":37:5)
+#loc5 = loc("test-path":39:5)
+#di_compile_unit = #llvm.di_compile_unit<id = distinct[3]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+#di_compile_unit1 = #llvm.di_compile_unit<id = distinct[4]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+#di_compile_unit2 = #llvm.di_compile_unit<id = distinct[5]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+#di_module = #llvm.di_module<file = #di_file, scope = #di_compile_unit1, name = "mod1">
+#di_module1 = #llvm.di_module<file = #di_file, scope = #di_compile_unit2, name = "mod2">
+#di_imported_entity = #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #di_subprogram, entity = #di_module, file = #di_file, line = 1>
+#di_imported_entity1 = #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #di_subprogram, entity = #di_module1, file = #di_file, line = 1>
+#di_subprogram1 = #llvm.di_subprogram<recId = distinct[2]<>, id = distinct[6]<>, compileUnit = #di_compile_unit, scope = #di_file, name = "imp_fn", file = #di_file, subprogramFlags = Definition, type = #di_subroutine_type, retainedNodes = #di_imported_entity, #di_imported_entity1>
+#loc8 = loc(fused<#di_subprogram1>[#loc1])
+
+// CHECK: #access_group = #llvm.access_group<id = distinct[0]<>>
+// CHECK: #access_group1 = #llvm.access_group<id = distinct[1]<>>
+// CHECK: #di_subprogram = #llvm.di_subprogram<recId = distinct[2]<>>
+// CHECK: {{.*}} = loc("test.f90":12:14)
+// CHECK: {{.*}} = loc("test":4:3)
+// CHECK: {{.*}} = loc(fused<#di_subprogram>[{{.*}}])
+// CHECK: {{.*}} = loc(fused<#di_subprogram>[{{.*}}])
+// CHECK: #loop_annotation = #llvm.loop_annotation<disableNonforced = false, mustProgress = true, startLoc = {{.*}}, endLoc = {{.*}}, parallelAccesses = #access_group, #access_group1>
+// CHECK: module {
+// CHECK:   llvm.func @imp_fn() {
+// CHECK:     llvm.return loc({{.*}})
+// CHECK:   } loc({{.*}})
+// CHECK:   llvm.func @loop_annotation_with_locs() {
+// CHECK:     llvm.br ^bb1 {loop_annotation = #loop_annotation} loc({{.*}})
+// CHECK:   ^bb1:  // pred: ^bb0
+// CHECK:     llvm.return loc({{.*}})
+// CHECK:   } loc({{.*}})
+// CHECK: } loc({{.*}})
+// CHECK: #di_file = #llvm.di_file<"test.f90" in "">
+// CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_program>
+// CHECK: {{.*}} = loc("test":0:0)
+// CHECK: {{.*}} = loc("test-path":36:3)
+// CHECK: {{.*}} = loc("test-path":37:5)
+// CHECK: {{.*}} = loc("test-path":39:5)
+// CHECK: #di_compile_unit = #llvm.di_compile_unit<id = distinct[3]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+// CHECK: #di_compile_unit1 = #llvm.di_compile_unit<id = distinct[4]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+// CHECK: #di_compile_unit2 = #llvm.di_compile_unit<id = distinct[5]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+// CHECK: #di_module = #llvm.di_module<file = #di_file, scope = #di_compile_unit1, name = "mod1">
+// CHECK: #di_module1 = #llvm.di_module<file = #di_file, scope = #di_compile_unit2, name = "mod2">
+// CHECK: #di_imported_entity = #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #di_subprogram, entity = #di_module, file = #di_file, line = 1>
+// CHECK: #di_imported_entity1 = #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #di_subprogram, entity = #di_module1, file = #di_file, line = 1>
+// CHECK: #di_subprogram1 = #llvm.di_subprogram<recId = distinct[2]<>, id = distinct[6]<>, compileUnit = #di_compile_unit, scope = #di_file, name = "imp_fn", file = #di_file, subprogramFlags = Definition, type = #di_subroutine_type, retainedNodes = #di_imported_entity, #di_imported_entity1>
+// CHECK: {{.*}} = loc(fused<#di_subprogram1>[{{.*}}])
diff --git a/mlir/test/Dialect/LLVMIR/debuginfo.mlir b/mlir/test/Dialect/LLVMIR/debuginfo.mlir
index 1834b0a524705..d7bf99bfaed7f 100644
--- a/mlir/test/Dialect/LLVMIR/debuginfo.mlir
+++ b/mlir/test/Dialect/LLVMIR/debuginfo.mlir
@@ -1,4 +1,5 @@
 // RUN: mlir-opt %s | mlir-opt | FileCheck %s
+// RUN: mlir-opt -emit-bytecode %s | mlir-opt | FileCheck %s
 
 // CHECK-DAG: #[[FILE:.*]] = #llvm.di_file<"debuginfo.mlir" in "/test/">
 #file = #llvm.di_file<"debuginfo.mlir" in "/test/">
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index 73447978341dc..f1dfb6ab456ca 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -1,4 +1,5 @@
 // RUN: mlir-opt %s | mlir-opt | FileCheck %s
+// RUN: mlir-opt %s -emit-bytecode | mlir-opt | FileCheck %s
 
 
 // CHECK-LABEL: func @baz
@@ -757,7 +758,7 @@ llvm.func @stackrestore(%arg0: !llvm.ptr)  {
 
 // CHECK-LABEL: @experimental_noalias_scope_decl
 llvm.func @experimental_noalias_scope_decl() {
-  // CHECK: llvm.intr.experimental.noalias.scope.decl #{{.*}}
+  // CHECK: llvm.intr.experimental.noalias.scope.decl #alias_scope{{.*}}
   llvm.intr.experimental.noalias.scope.decl #alias_scope
   llvm.return
 }
@@ -767,7 +768,7 @@ llvm.func @experimental_noalias_scope_decl() {
 
 // CHECK-LABEL: @experimental_noalias_scope_with_string_id
 llvm.func @experimental_noalias_scope_with_string_id() {
-  // CHECK: llvm.intr.experimental.noalias.scope.decl #{{.*}}
+  // CHECK: llvm.intr.experimental.noalias.scope.decl #alias_scope{{.*}}
   llvm.intr.experimental.noalias.scope.decl #alias_scope2
   llvm.return
 }

>From eff046f3b2b720b6b58ea7d5a011ff6ddf5312c8 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Wed, 8 Oct 2025 19:01:55 -0700
Subject: [PATCH 2/4] Update order of elements to match upstream

---
 mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
index 2474dece3f7c3..fedb76b7f3123 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
@@ -188,11 +188,11 @@ def DICompositeTypeAttr : DialectAttribute<(attr
   LocalVar<"DIFlags", "(DIFlags)_rawflags">:$flags,
   VarInt:$sizeInBits,
   VarInt:$alignInBits,
-  OptionalArrayRef<"DINodeAttr">:$elements,
   OptionalAttribute<"DIExpressionAttr">:$dataLocation,
   OptionalAttribute<"DIExpressionAttr">:$rank,
   OptionalAttribute<"DIExpressionAttr">:$allocated,
-  OptionalAttribute<"DIExpressionAttr">:$associated
+  OptionalAttribute<"DIExpressionAttr">:$associated,
+  OptionalArrayRef<"DINodeAttr">:$elements
 )>;
 
 //===----------------------------------------------------------------------===//

>From dd8aa3d0fd75e15b1079f5e0b5b195929c422b2a Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Thu, 9 Oct 2025 12:17:59 -0700
Subject: [PATCH 3/4] Address review feedback

---
 .../Dialect/LLVMIR/LLVMDialectBytecode.td     | 33 +++++++++++--------
 .../Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp | 12 +++----
 .../Dialect/LLVMIR/IR/LLVMDialectBytecode.h   |  6 ++--
 mlir/test/Dialect/LLVMIR/bytecode.mlir        | 22 ++++++++++---
 4 files changed, 44 insertions(+), 29 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
index fedb76b7f3123..7eea9e4d7e1f4 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialectBytecode.td
@@ -57,6 +57,8 @@ class EnumClassFlag<string flag, string getter> :
 //   LLVMAttrDefs.td
 // - The mnemonics are either LLVM or builtin MLIR attributes and types, but
 //   regular C++ types are also allowed to match builders and parsers.
+// - DIScopeAttr and DINodeAttr are empty base classes, custom encoding not
+//   needed.
 //===----------------------------------------------------------------------===//
 
 //===----------------------------------------------------------------------===//
@@ -107,7 +109,7 @@ def DIFileAttr : DialectAttribute<(attr
 //===----------------------------------------------------------------------===//
 
 def DILocalVariableAttr : DialectAttribute<(attr
-  Attr<"DIScopeAttr">:$scope, // Non-optional attribute
+  Attr<"DIScopeAttr">:$scope,
   OptionalAttribute<"StringAttr">:$name,
   OptionalAttribute<"DIFileAttr">:$file,
   VarInt:$line,
@@ -158,7 +160,7 @@ def DISubprogramAttr : DialectAttribute<(attr
   Bool:$isRecSelf,
   OptionalAttribute<"DistinctAttr">:$id,
   OptionalAttribute<"DICompileUnitAttr">:$compileUnit,
-  OptionalAttribute<"DIScopeAttr">:$scope, // TODO: DIScopeAttr
+  OptionalAttribute<"DIScopeAttr">:$scope,
   OptionalAttribute<"StringAttr">:$name,
   OptionalAttribute<"StringAttr">:$linkageName,
   OptionalAttribute<"DIFileAttr">:$file,
@@ -207,7 +209,7 @@ def DIDerivedTypeAttr : DialectAttribute<(attr
   VarInt:$alignInBits,
   VarInt:$offsetInBits,
   OptionalInt<"unsigned">:$dwarfAddressSpace,
-  OptionalAttribute<"DINodeAttr">:$extraData // TODO: DINodeAttr
+  OptionalAttribute<"DINodeAttr">:$extraData
 )>;
 
 //===----------------------------------------------------------------------===//
@@ -305,26 +307,27 @@ def DISubrangeAttr : DialectAttribute<(attr
 
 def LoopAnnotationAttr : DialectAttribute<(attr
   OptionalAttribute<"BoolAttr">:$disableNonforced,
-  OptionalAttribute<"LoopVectorizeAttr">:$vectorize, // TODO: LoopVectorizeAttr
-  OptionalAttribute<"LoopInterleaveAttr">:$interleave, // TODO: LoopInterleaveAttr
-  OptionalAttribute<"LoopUnrollAttr">:$unroll, // TODO: LoopUnrollAttr
-  OptionalAttribute<"LoopUnrollAndJamAttr">:$unrollAndJam, // TODO: LoopUnrollAndJamAttr
-  OptionalAttribute<"LoopLICMAttr">:$licm, // TODO: LoopLICMAttr
-  OptionalAttribute<"LoopDistributeAttr">:$distribute, // TODO: LoopDistributeAttr
-  OptionalAttribute<"LoopPipelineAttr">:$pipeline, // TODO: LoopPipelineAttr
-  OptionalAttribute<"LoopPeeledAttr">:$peeled, // TODO: LoopPeeledAttr
-  OptionalAttribute<"LoopUnswitchAttr">:$unswitch, // TODO: LoopUnswitchAttr
+  OptionalAttribute<"LoopVectorizeAttr">:$vectorize,
+  OptionalAttribute<"LoopInterleaveAttr">:$interleave,
+  OptionalAttribute<"LoopUnrollAttr">:$unroll,
+  OptionalAttribute<"LoopUnrollAndJamAttr">:$unrollAndJam,
+  OptionalAttribute<"LoopLICMAttr">:$licm,
+  OptionalAttribute<"LoopDistributeAttr">:$distribute,
+  OptionalAttribute<"LoopPipelineAttr">:$pipeline,
+  OptionalAttribute<"LoopPeeledAttr">:$peeled,
+  OptionalAttribute<"LoopUnswitchAttr">:$unswitch,
   OptionalAttribute<"BoolAttr">:$mustProgress,
   OptionalAttribute<"BoolAttr">:$isVectorized,
   OptionalAttribute<"FusedLoc">:$startLoc,
   OptionalAttribute<"FusedLoc">:$endLoc,
-  OptionalArrayRef<"AccessGroupAttr">:$parallelAccesses // TODO: AccessGroupAttr
+  OptionalArrayRef<"AccessGroupAttr">:$parallelAccesses
 )>;
 
 //===----------------------------------------------------------------------===//
 // Attributes & Types with custom bytecode handling.
 //===----------------------------------------------------------------------===//
 
+// All the attributes with custom bytecode handling.
 def LLVMDialectAttributes : DialectAttributes<"LLVM"> {
   let elems = [
     AliasScopeAttr,
@@ -347,6 +350,10 @@ def LLVMDialectAttributes : DialectAttributes<"LLVM"> {
     DISubrangeAttr,
     DISubroutineTypeAttr,
     LoopAnnotationAttr
+    // Referenced attributes currently missing support:
+    // AccessGroupAttr, LoopVectorizeAttr, LoopInterleaveAttr, LoopUnrollAttr,
+    // LoopUnrollAndJamAttr, LoopLICMAttr, LoopDistributeAttr, LoopPipelineAttr,
+    // LoopPeeledAttr, LoopUnswitchAttr
   ];
 }
 
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
index f06c764ab5f6d..113637d479d0f 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
@@ -33,7 +33,7 @@ static LogicalResult writeAttribute(Attribute attribute,
 // Optional ArrayRefs
 //
 // Note that both the writer and reader functions consider attributes to be
-// optional. This is because the attribute may be present by empty.
+// optional. This is because the attribute may be present or empty.
 //===--------------------------------------------------------------------===//
 
 template <class EntryTy>
@@ -64,7 +64,7 @@ static LogicalResult readOptionalArrayRef(DialectBytecodeReader &reader,
   if (!isPresent)
     return success();
 
-  auto readOperations = [&]() -> FailureOr<EntryTy> {
+  auto readEntry = [&]() -> FailureOr<EntryTy> {
     EntryTy temp;
     if constexpr (std::is_base_of_v<Attribute, EntryTy>) {
       if (succeeded(reader.readOptionalAttribute(temp)))
@@ -77,7 +77,7 @@ static LogicalResult readOptionalArrayRef(DialectBytecodeReader &reader,
     return failure();
   };
 
-  return reader.readList(storage, readOperations);
+  return reader.readList(storage, readEntry);
 }
 
 //===--------------------------------------------------------------------===//
@@ -103,7 +103,7 @@ static LogicalResult readOptionalInt(DialectBytecodeReader &reader,
   if (failed(reader.readVarIntWithFlag(result, flag)))
     return failure();
   if (flag)
-    storage = (decltype(storage.value()))result;
+    storage = static_cast<EntryTy>(result);
   else
     storage = std::nullopt;
   return success();
@@ -124,9 +124,7 @@ struct LLVMDialectBytecodeInterface : public BytecodeDialectInterface {
   LLVMDialectBytecodeInterface(Dialect *dialect)
       : BytecodeDialectInterface(dialect) {}
 
-  //===--------------------------------------------------------------------===//
   // Attributes
-
   Attribute readAttribute(DialectBytecodeReader &reader) const override {
     return ::readAttribute(getContext(), reader);
   }
@@ -136,9 +134,7 @@ struct LLVMDialectBytecodeInterface : public BytecodeDialectInterface {
     return ::writeAttribute(attr, writer);
   }
 
-  //===--------------------------------------------------------------------===//
   // Types
-
   Type readType(DialectBytecodeReader &reader) const override {
     return ::readType(getContext(), reader);
   }
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h
index 5e25d3948afb9..1a17cb462ccf3 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This header defines hooks into the LLVMization dialect bytecode
+// This header defines hooks into the LLVM dialect bytecode
 // implementation.
 //
 //===----------------------------------------------------------------------===//
@@ -18,8 +18,8 @@ namespace mlir::LLVM {
 class LLVMDialect;
 
 namespace detail {
-/// Add the interfaces necessary for encoding the LLVMization dialect
-/// components in bytecode.
+/// Add the interfaces necessary for encoding the LLVM dialect components in
+/// bytecode.
 void addBytecodeInterface(LLVMDialect *dialect);
 } // namespace detail
 } // namespace mlir::LLVM
diff --git a/mlir/test/Dialect/LLVMIR/bytecode.mlir b/mlir/test/Dialect/LLVMIR/bytecode.mlir
index 6f273ea2d90e9..b8b95acfd5457 100644
--- a/mlir/test/Dialect/LLVMIR/bytecode.mlir
+++ b/mlir/test/Dialect/LLVMIR/bytecode.mlir
@@ -8,6 +8,8 @@
 #loc6 = loc(fused<#di_subprogram>[#loc1])
 #loc7 = loc(fused<#di_subprogram>[#loc2])
 #loop_annotation = #llvm.loop_annotation<disableNonforced = false, mustProgress = true, startLoc = #loc6, endLoc = #loc7, parallelAccesses = #access_group, #access_group1>
+#alias_scope_domain = #llvm.alias_scope_domain<id = distinct[0]<>, description = "The domain">
+#alias_scope = #llvm.alias_scope<id = distinct[0]<>, domain = #alias_scope_domain, description = "The domain">
 module {
   llvm.func @imp_fn() {
     llvm.return loc(#loc2)
@@ -17,6 +19,10 @@ module {
   ^bb1:  // pred: ^bb0
     llvm.return loc(#loc5)
   } loc(#loc3)
+  llvm.func @experimental_noalias_scope_decl() {
+    llvm.intr.experimental.noalias.scope.decl #alias_scope
+    llvm.return
+  }
 } loc(#loc)
 #di_file = #llvm.di_file<"test.f90" in "">
 #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_program>
@@ -36,9 +42,11 @@ module {
 
 // CHECK: #access_group = #llvm.access_group<id = distinct[0]<>>
 // CHECK: #access_group1 = #llvm.access_group<id = distinct[1]<>>
-// CHECK: #di_subprogram = #llvm.di_subprogram<recId = distinct[2]<>>
+// CHECK: #alias_scope_domain = #llvm.alias_scope_domain<id = distinct[2]<>, description = "The domain">
+// CHECK: #di_subprogram = #llvm.di_subprogram<recId = distinct[3]<>>
 // CHECK: {{.*}} = loc("test.f90":12:14)
 // CHECK: {{.*}} = loc("test":4:3)
+// CHECK: #alias_scope = #llvm.alias_scope<id = distinct[4]<>, domain = #alias_scope_domain, description = "The domain">
 // CHECK: {{.*}} = loc(fused<#di_subprogram>[{{.*}}])
 // CHECK: {{.*}} = loc(fused<#di_subprogram>[{{.*}}])
 // CHECK: #loop_annotation = #llvm.loop_annotation<disableNonforced = false, mustProgress = true, startLoc = {{.*}}, endLoc = {{.*}}, parallelAccesses = #access_group, #access_group1>
@@ -51,6 +59,10 @@ module {
 // CHECK:   ^bb1:  // pred: ^bb0
 // CHECK:     llvm.return loc({{.*}})
 // CHECK:   } loc({{.*}})
+// CHECK:   llvm.func @experimental_noalias_scope_decl() {
+// CHECK:     llvm.intr.experimental.noalias.scope.decl #alias_scope loc({{.*}})
+// CHECK:     llvm.return loc({{.*}})
+// CHECK:   } loc({{.*}})
 // CHECK: } loc({{.*}})
 // CHECK: #di_file = #llvm.di_file<"test.f90" in "">
 // CHECK: #di_subroutine_type = #llvm.di_subroutine_type<callingConvention = DW_CC_program>
@@ -58,12 +70,12 @@ module {
 // CHECK: {{.*}} = loc("test-path":36:3)
 // CHECK: {{.*}} = loc("test-path":37:5)
 // CHECK: {{.*}} = loc("test-path":39:5)
-// CHECK: #di_compile_unit = #llvm.di_compile_unit<id = distinct[3]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
-// CHECK: #di_compile_unit1 = #llvm.di_compile_unit<id = distinct[4]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
-// CHECK: #di_compile_unit2 = #llvm.di_compile_unit<id = distinct[5]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+// CHECK: #di_compile_unit = #llvm.di_compile_unit<id = distinct[5]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+// CHECK: #di_compile_unit1 = #llvm.di_compile_unit<id = distinct[6]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
+// CHECK: #di_compile_unit2 = #llvm.di_compile_unit<id = distinct[7]<>, sourceLanguage = DW_LANG_Fortran95, file = #di_file, isOptimized = false, emissionKind = Full>
 // CHECK: #di_module = #llvm.di_module<file = #di_file, scope = #di_compile_unit1, name = "mod1">
 // CHECK: #di_module1 = #llvm.di_module<file = #di_file, scope = #di_compile_unit2, name = "mod2">
 // CHECK: #di_imported_entity = #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #di_subprogram, entity = #di_module, file = #di_file, line = 1>
 // CHECK: #di_imported_entity1 = #llvm.di_imported_entity<tag = DW_TAG_imported_module, scope = #di_subprogram, entity = #di_module1, file = #di_file, line = 1>
-// CHECK: #di_subprogram1 = #llvm.di_subprogram<recId = distinct[2]<>, id = distinct[6]<>, compileUnit = #di_compile_unit, scope = #di_file, name = "imp_fn", file = #di_file, subprogramFlags = Definition, type = #di_subroutine_type, retainedNodes = #di_imported_entity, #di_imported_entity1>
+// CHECK: #di_subprogram1 = #llvm.di_subprogram<recId = distinct[3]<>, id = distinct[8]<>, compileUnit = #di_compile_unit, scope = #di_file, name = "imp_fn", file = #di_file, subprogramFlags = Definition, type = #di_subroutine_type, retainedNodes = #di_imported_entity, #di_imported_entity1>
 // CHECK: {{.*}} = loc(fused<#di_subprogram1>[{{.*}}])

>From 753f296504d7ea6d845a7d38c9b4678aa0c2b830 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 10 Oct 2025 11:39:59 -0700
Subject: [PATCH 4/4] Address more comments and use -verify-roundtrip

---
 .../Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp | 27 ++++++++++---------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
index 113637d479d0f..41d1f80580cf7 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialectBytecode.cpp
@@ -39,19 +39,21 @@ static LogicalResult writeAttribute(Attribute attribute,
 template <class EntryTy>
 static void writeOptionalArrayRef(DialectBytecodeWriter &writer,
                                   ArrayRef<EntryTy> storage) {
-  if (!storage.empty()) {
-    writer.writeOwnedBool(true);
-    writer.writeList(storage, [&](EntryTy val) {
-      if constexpr (std::is_base_of_v<Attribute, EntryTy>) {
-        (void)writer.writeOptionalAttribute(val);
-      } else if constexpr (std::is_integral_v<EntryTy>) {
-        (void)writer.writeVarInt(val);
-      } else
-        static_assert(true, "EntryTy not supported");
-    });
-  } else {
+  if (storage.empty()) {
     writer.writeOwnedBool(false);
+    return;
   }
+
+  writer.writeOwnedBool(true);
+  writer.writeList(storage, [&](EntryTy val) {
+    if constexpr (std::is_base_of_v<Attribute, EntryTy>) {
+      (void)writer.writeOptionalAttribute(val);
+    } else if constexpr (std::is_integral_v<EntryTy>) {
+      (void)writer.writeVarInt(val);
+    } else {
+      static_assert(true, "EntryTy not supported");
+    }
+  });
 }
 
 template <class EntryTy>
@@ -72,8 +74,9 @@ static LogicalResult readOptionalArrayRef(DialectBytecodeReader &reader,
     } else if constexpr (std::is_integral_v<EntryTy>) {
       if (succeeded(reader.readVarInt(temp)))
         return temp;
-    } else
+    } else {
       static_assert(true, "EntryTy not supported");
+    }
     return failure();
   };
 



More information about the Mlir-commits mailing list