[Mlir-commits] [mlir] [MLIR][LLVM] add metadata attrs and `llvm.named_metadata` op (PR #186703)

Maksim Levental llvmlistbot at llvm.org
Sun Mar 15 15:06:57 PDT 2026


https://github.com/makslevental updated https://github.com/llvm/llvm-project/pull/186703

>From df6e557170ccd863222e219a71de6a74dd4236ab Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Sun, 15 Mar 2026 13:25:38 -0700
Subject: [PATCH 1/4] [MLIR][LLVM] add metadata attrs and llvm.named_metadata

---
 mlir/include/mlir-c/Dialect/LLVM.h            |  65 ++++++++
 .../mlir/Dialect/LLVMIR/LLVMAttrDefs.td       |  70 +++++++++
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   |  40 +++++
 mlir/lib/Bindings/Python/DialectLLVM.cpp      | 123 ++++++++++++++-
 mlir/lib/CAPI/Dialect/LLVM.cpp                |  88 ++++++++++-
 .../LLVMIR/LLVMToLLVMIRTranslation.cpp        | 143 ++++++++++++------
 .../Target/LLVMIR/llvmir-named-metadata.mlir  | 107 +++++++++++++
 7 files changed, 585 insertions(+), 51 deletions(-)
 create mode 100644 mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir

diff --git a/mlir/include/mlir-c/Dialect/LLVM.h b/mlir/include/mlir-c/Dialect/LLVM.h
index 93602a286c9c4..2d603ffc09edb 100644
--- a/mlir/include/mlir-c/Dialect/LLVM.h
+++ b/mlir/include/mlir-c/Dialect/LLVM.h
@@ -456,6 +456,71 @@ MLIR_CAPI_EXPORTED MlirStringRef mlirLLVMDIImportedEntityAttrGetName(void);
 MLIR_CAPI_EXPORTED MlirAttribute
 mlirLLVMDIModuleAttrGetScope(MlirAttribute diModule);
 
+//===----------------------------------------------------------------------===//
+// Metadata Attributes
+//===----------------------------------------------------------------------===//
+
+/// Creates an LLVM MDStringAttr.
+MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMMDStringAttrGet(MlirContext ctx,
+                                                         MlirStringRef value);
+
+/// Returns `true` if the attribute is an LLVM MDStringAttr.
+MLIR_CAPI_EXPORTED bool mlirLLVMAttrIsAMDStringAttr(MlirAttribute attr);
+
+/// Returns the TypeID of MDStringAttr.
+MLIR_CAPI_EXPORTED MlirTypeID mlirLLVMMDStringAttrGetTypeID(void);
+
+/// Returns the string value of an LLVM MDStringAttr.
+MLIR_CAPI_EXPORTED MlirStringRef
+mlirLLVMMDStringAttrGetValue(MlirAttribute attr);
+
+/// Creates an LLVM MDConstantAttr wrapping an integer attribute.
+MLIR_CAPI_EXPORTED MlirAttribute
+mlirLLVMMDConstantAttrGet(MlirContext ctx, MlirAttribute integerAttr);
+
+/// Returns `true` if the attribute is an LLVM MDConstantAttr.
+MLIR_CAPI_EXPORTED bool mlirLLVMAttrIsAMDConstantAttr(MlirAttribute attr);
+
+/// Returns the TypeID of MDConstantAttr.
+MLIR_CAPI_EXPORTED MlirTypeID mlirLLVMMDConstantAttrGetTypeID(void);
+
+/// Returns the integer attribute value of an LLVM MDConstantAttr.
+MLIR_CAPI_EXPORTED MlirAttribute
+mlirLLVMMDConstantAttrGetValue(MlirAttribute attr);
+
+/// Creates an LLVM MDFuncAttr referencing a function symbol.
+MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMMDFuncAttrGet(MlirContext ctx,
+                                                       MlirAttribute name);
+
+/// Returns `true` if the attribute is an LLVM MDFuncAttr.
+MLIR_CAPI_EXPORTED bool mlirLLVMAttrIsAMDFuncAttr(MlirAttribute attr);
+
+/// Returns the TypeID of MDFuncAttr.
+MLIR_CAPI_EXPORTED MlirTypeID mlirLLVMMDFuncAttrGetTypeID(void);
+
+/// Returns the symbol name of an LLVM MDFuncAttr.
+MLIR_CAPI_EXPORTED MlirAttribute
+mlirLLVMMDFuncAttrGetName(MlirAttribute attr);
+
+/// Creates an LLVM MDNodeAttr (metadata tuple).
+MLIR_CAPI_EXPORTED MlirAttribute
+mlirLLVMMDNodeAttrGet(MlirContext ctx, intptr_t nOperands,
+                      MlirAttribute const *operands);
+
+/// Returns `true` if the attribute is an LLVM MDNodeAttr.
+MLIR_CAPI_EXPORTED bool mlirLLVMAttrIsAMDNodeAttr(MlirAttribute attr);
+
+/// Returns the TypeID of MDNodeAttr.
+MLIR_CAPI_EXPORTED MlirTypeID mlirLLVMMDNodeAttrGetTypeID(void);
+
+/// Returns the number of operands in an LLVM MDNodeAttr.
+MLIR_CAPI_EXPORTED intptr_t
+mlirLLVMMDNodeAttrGetNumOperands(MlirAttribute attr);
+
+/// Returns the operand at the given index of an LLVM MDNodeAttr.
+MLIR_CAPI_EXPORTED MlirAttribute
+mlirLLVMMDNodeAttrGetOperand(MlirAttribute attr, intptr_t index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index 36acf244865eb..b315d563df031 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -1686,4 +1686,74 @@ def UWTableKindAttr : LLVM_Attr<"UWTableKind", "uwtableKind"> {
   let assemblyFormat = "`<` $uwtableKind `>`";
 }
 
+//===----------------------------------------------------------------------===//
+// Metadata Attributes
+//===----------------------------------------------------------------------===//
+//
+// These attributes model LLVM IR metadata nodes (llvm::Metadata and its
+// subclasses). They can be nested to form arbitrary metadata trees and are
+// translated to their LLVM IR counterparts during MLIR-to-LLVM-IR conversion.
+
+def LLVM_MDStringAttr : LLVM_Attr<"MDString", "md_string"> {
+  let summary = "LLVM metadata string";
+  let description = [{
+    Wraps a string as an LLVM metadata node, corresponding to
+    `llvm::MDString` in LLVM IR.
+
+    Example:
+    ```mlir
+    #llvm.md_string<"foo.buffer">
+    ```
+  }];
+  let parameters = (ins "StringAttr":$value);
+  let assemblyFormat = "`<` $value `>`";
+}
+
+def LLVM_MDConstantAttr : LLVM_Attr<"MDConstant", "md_const"> {
+  let summary = "LLVM constant-as-metadata";
+  let description = [{
+    Wraps an integer constant as an LLVM metadata node, corresponding to
+    `llvm::ConstantAsMetadata` wrapping a `llvm::ConstantInt` in LLVM IR.
+
+    Example:
+    ```mlir
+    #llvm.md_const<42 : i32>
+    ```
+  }];
+  let parameters = (ins "IntegerAttr":$value);
+  let assemblyFormat = "`<` $value `>`";
+}
+
+def LLVM_MDFuncAttr : LLVM_Attr<"MDFunc", "md_func"> {
+  let summary = "LLVM function-as-metadata";
+  let description = [{
+    References a function (or global) symbol as LLVM metadata, corresponding
+    to `llvm::ValueAsMetadata::get(function)` in LLVM IR.
+
+    Example:
+    ```mlir
+    #llvm.md_func<@my_kernel>
+    ```
+  }];
+  let parameters = (ins "FlatSymbolRefAttr":$name);
+  let assemblyFormat = "`<` $name `>`";
+}
+
+def LLVM_MDNodeAttr : LLVM_Attr<"MDNode", "md_node"> {
+  let summary = "LLVM metadata tuple node";
+  let description = [{
+    Represents an LLVM metadata tuple node (`llvm::MDTuple`). The operands
+    can be any combination of metadata attributes: `#llvm.md_string`,
+    `#llvm.md_const`, `#llvm.md_func`, or nested `#llvm.md_node`.
+
+    Example:
+    ```mlir
+    #llvm.md_node<[#llvm.md_const<0 : i32>, #llvm.md_string<"foo.buffer">]>
+    #llvm.md_node<[]>
+    ```
+  }];
+  let parameters = (ins "ArrayAttr":$operands);
+  let assemblyFormat = "`<` $operands `>`";
+}
+
 #endif // LLVMIR_ATTRDEFS
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index e781d4c876315..097c41ee4668f 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -2576,4 +2576,44 @@ def LLVM_ModuleFlagsOp
   let hasVerifier = 1;
 }
 
+//===--------------------------------------------------------------------===//
+// NamedMetadataOp
+//===--------------------------------------------------------------------===//
+
+def LLVM_NamedMetadataOp
+    : LLVM_Op<"named_metadata"> {
+  let summary = "Module-level named metadata";
+  let description = [{
+    Represents an LLVM named metadata node (`llvm::NamedMDNode`). Named
+    metadata nodes are module-level metadata that associate a name string
+    with a list of metadata nodes. Each operand must be an `#llvm.md_node`.
+
+    Example:
+    ```mlir
+    llvm.named_metadata "foo.version" [
+      #llvm.md_node<[#llvm.md_const<2 : i32>, #llvm.md_const<9 : i32>,
+                      #llvm.md_const<0 : i32>]>
+    ]
+    llvm.named_metadata "foo.kernel" [
+      #llvm.md_node<[
+        #llvm.md_func<@my_kernel>,
+        #llvm.md_node<[]>,
+        #llvm.md_node<[
+          #llvm.md_node<[#llvm.md_const<0 : i32>,
+                          #llvm.md_string<"foo.buffer">]>
+        ]>
+      ]>
+    ]
+    ```
+  }];
+  let arguments = (ins StrAttr:$metadata_name, ArrayAttr:$nodes);
+  let assemblyFormat = [{
+    $metadata_name $nodes attr-dict
+  }];
+
+  let llvmBuilder = [{
+    convertNamedMetadataOp($metadata_name, $nodes, builder, moduleTranslation);
+  }];
+}
+
 #endif // LLVMIR_OPS
diff --git a/mlir/lib/Bindings/Python/DialectLLVM.cpp b/mlir/lib/Bindings/Python/DialectLLVM.cpp
index 5c79f515c49eb..6db6748a40cb0 100644
--- a/mlir/lib/Bindings/Python/DialectLLVM.cpp
+++ b/mlir/lib/Bindings/Python/DialectLLVM.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <string>
+#include <vector>
 
 #include "mlir-c/Dialect/LLVM.h"
 #include "mlir-c/IR.h"
@@ -25,7 +26,8 @@ using namespace mlir::python::nanobind_adaptors;
 
 namespace mlir {
 namespace python {
-namespace MLIR_BINDINGS_PYTHON_DOMAIN {
+namespace
+MLIR_BINDINGS_PYTHON_DOMAIN {
 namespace llvm {
 //===--------------------------------------------------------------------===//
 // StructType
@@ -222,10 +224,129 @@ struct PointerType : PyConcreteType<PointerType> {
   }
 };
 
+//===--------------------------------------------------------------------===//
+// Metadata Attributes
+//===--------------------------------------------------------------------===//
+
+struct MDStringAttr : PyConcreteAttribute<MDStringAttr> {
+  static constexpr IsAFunctionTy isaFunction = mlirLLVMAttrIsAMDStringAttr;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLLVMMDStringAttrGetTypeID;
+  static constexpr const char *pyClassName = "MDStringAttr";
+  using Base::Base;
+
+  static void bindDerived(ClassTy &c) {
+    c.def_static(
+        "get",
+        [](const std::string &value, DefaultingPyMlirContext context) {
+          return MDStringAttr(context->getRef(),
+                              mlirLLVMMDStringAttrGet(
+                                  context.get()->get(),
+                                  mlirStringRefCreate(value.data(),
+                                    value.size())));
+        },
+        "value"_a, nb::kw_only(), "context"_a = nb::none());
+    c.def_prop_ro("value", [](const MDStringAttr &self) {
+      MlirStringRef ref =
+          mlirLLVMMDStringAttrGetValue(self);
+      return nb::str(ref.data, ref.length);
+    });
+  }
+};
+
+struct MDConstantAttr : PyConcreteAttribute<MDConstantAttr> {
+  static constexpr IsAFunctionTy isaFunction = mlirLLVMAttrIsAMDConstantAttr;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLLVMMDConstantAttrGetTypeID;
+  static constexpr const char *pyClassName = "MDConstantAttr";
+  using Base::Base;
+
+  static void bindDerived(ClassTy &c) {
+    c.def_static(
+        "get",
+        [](PyAttribute &integerAttr, DefaultingPyMlirContext context) {
+          return MDConstantAttr(
+              context->getRef(),
+              mlirLLVMMDConstantAttrGet(context.get()->get(), integerAttr));
+        },
+        "value"_a, nb::kw_only(), "context"_a = nb::none());
+    c.def_prop_ro("value", [](const MDConstantAttr &self) {
+      return mlirLLVMMDConstantAttrGetValue(self);
+    });
+  }
+};
+
+struct MDFuncAttr : PyConcreteAttribute<MDFuncAttr> {
+  static constexpr IsAFunctionTy isaFunction = mlirLLVMAttrIsAMDFuncAttr;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLLVMMDFuncAttrGetTypeID;
+  static constexpr const char *pyClassName = "MDFuncAttr";
+  using Base::Base;
+
+  static void bindDerived(ClassTy &c) {
+    c.def_static(
+        "get",
+        [](const std::string &name, DefaultingPyMlirContext context) {
+          MlirAttribute symRef = mlirFlatSymbolRefAttrGet(
+              context.get()->get(),
+              mlirStringRefCreate(name.data(), name.size()));
+          return MDFuncAttr(context->getRef(),
+                            mlirLLVMMDFuncAttrGet(context.get()->get(),
+                                                  symRef));
+        },
+        "name"_a, nb::kw_only(), "context"_a = nb::none());
+    c.def_prop_ro("name", [](const MDFuncAttr &self) {
+      MlirAttribute symRef = mlirLLVMMDFuncAttrGetName(self);
+      MlirStringRef ref = mlirFlatSymbolRefAttrGetValue(symRef);
+      return nb::str(ref.data, ref.length);
+    });
+  }
+};
+
+struct MDNodeAttr : PyConcreteAttribute<MDNodeAttr> {
+  static constexpr IsAFunctionTy isaFunction = mlirLLVMAttrIsAMDNodeAttr;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLLVMMDNodeAttrGetTypeID;
+  static constexpr const char *pyClassName = "MDNodeAttr";
+  using Base::Base;
+
+  static void bindDerived(ClassTy &c) {
+    c.def_static(
+        "get",
+        [](const std::vector<PyAttribute> &operands,
+           DefaultingPyMlirContext context) {
+          std::vector<MlirAttribute> operands_(operands.size());
+          std::copy(operands.begin(), operands.end(), operands_.begin());
+          return MDNodeAttr(
+              context->getRef(),
+              mlirLLVMMDNodeAttrGet(context.get()->get(), operands_.size(),
+                                    operands_.data()));
+        },
+        "operands"_a, nb::kw_only(), "context"_a = nb::none());
+    c.def_prop_ro("num_operands", [](const MDNodeAttr &self) {
+      return mlirLLVMMDNodeAttrGetNumOperands(self);
+    });
+    c.def("__getitem__",
+          [](const MDNodeAttr &self, intptr_t index) {
+            intptr_t n = mlirLLVMMDNodeAttrGetNumOperands(self);
+            if (index < 0 || index >= n)
+              throw nb::index_error("MDNodeAttr operand index out of range");
+            return mlirLLVMMDNodeAttrGetOperand(self, index);
+          });
+    c.def("__len__", [](const MDNodeAttr &self) {
+      return mlirLLVMMDNodeAttrGetNumOperands(self);
+    });
+  }
+};
+
 static void populateDialectLLVMSubmodule(nanobind::module_ &m) {
   StructType::bind(m);
   ArrayType::bind(m);
   PointerType::bind(m);
+  MDStringAttr::bind(m);
+  MDConstantAttr::bind(m);
+  MDFuncAttr::bind(m);
+  MDNodeAttr::bind(m);
 
   m.def(
       "translate_module_to_llvmir",
diff --git a/mlir/lib/CAPI/Dialect/LLVM.cpp b/mlir/lib/CAPI/Dialect/LLVM.cpp
index eb30f169e4289..c9b2e999c645e 100644
--- a/mlir/lib/CAPI/Dialect/LLVM.cpp
+++ b/mlir/lib/CAPI/Dialect/LLVM.cpp
@@ -92,7 +92,7 @@ intptr_t mlirLLVMFunctionTypeGetNumInputs(MlirType type) {
 MlirType mlirLLVMFunctionTypeGetInput(MlirType type, intptr_t pos) {
   assert(pos >= 0 && "pos in array must be positive");
   return wrap(llvm::cast<LLVM::LLVMFunctionType>(unwrap(type))
-                  .getParamType(static_cast<unsigned>(pos)));
+      .getParamType(static_cast<unsigned>(pos)));
 }
 
 MlirType mlirLLVMFunctionTypeGetReturnType(MlirType type) {
@@ -179,7 +179,7 @@ MlirLogicalResult mlirLLVMStructTypeSetBody(MlirType structType,
   SmallVector<Type> fields;
   return wrap(
       cast<LLVM::LLVMStructType>(unwrap(structType))
-          .setBody(unwrapList(nFieldTypes, fieldTypes, fields), isPacked));
+      .setBody(unwrapList(nFieldTypes, fieldTypes, fields), isPacked));
 }
 
 MlirAttribute mlirLLVMDIExpressionElemAttrGet(MlirContext ctx,
@@ -523,3 +523,87 @@ MlirAttribute mlirLLVMDIAnnotationAttrGet(MlirContext ctx, MlirAttribute name,
 MlirStringRef mlirLLVMDIAnnotationAttrGetName(void) {
   return wrap(DIAnnotationAttr::name);
 }
+
+//===----------------------------------------------------------------------===//
+// Metadata Attributes
+//===----------------------------------------------------------------------===//
+
+MlirAttribute mlirLLVMMDStringAttrGet(MlirContext ctx, MlirStringRef value) {
+  return wrap(
+      MDStringAttr::get(unwrap(ctx),
+                        StringAttr::get(unwrap(ctx), unwrap(value))));
+}
+
+bool mlirLLVMAttrIsAMDStringAttr(MlirAttribute attr) {
+  return isa<MDStringAttr>(unwrap(attr));
+}
+
+MlirTypeID mlirLLVMMDStringAttrGetTypeID(void) {
+  return wrap(MDStringAttr::getTypeID());
+}
+
+MlirStringRef mlirLLVMMDStringAttrGetValue(MlirAttribute attr) {
+  return wrap(cast<MDStringAttr>(unwrap(attr)).getValue().getValue());
+}
+
+MlirAttribute mlirLLVMMDConstantAttrGet(MlirContext ctx,
+                                        MlirAttribute integerAttr) {
+  return wrap(MDConstantAttr::get(unwrap(ctx),
+                                  cast<IntegerAttr>(unwrap(integerAttr))));
+}
+
+bool mlirLLVMAttrIsAMDConstantAttr(MlirAttribute attr) {
+  return isa<MDConstantAttr>(unwrap(attr));
+}
+
+MlirTypeID mlirLLVMMDConstantAttrGetTypeID(void) {
+  return wrap(MDConstantAttr::getTypeID());
+}
+
+MlirAttribute mlirLLVMMDConstantAttrGetValue(MlirAttribute attr) {
+  return wrap((Attribute)cast<MDConstantAttr>(unwrap(attr)).getValue());
+}
+
+MlirAttribute mlirLLVMMDFuncAttrGet(MlirContext ctx, MlirAttribute name) {
+  return wrap(
+      MDFuncAttr::get(unwrap(ctx), cast<FlatSymbolRefAttr>(unwrap(name))));
+}
+
+bool mlirLLVMAttrIsAMDFuncAttr(MlirAttribute attr) {
+  return isa<MDFuncAttr>(unwrap(attr));
+}
+
+MlirTypeID mlirLLVMMDFuncAttrGetTypeID(void) {
+  return wrap(MDFuncAttr::getTypeID());
+}
+
+MlirAttribute mlirLLVMMDFuncAttrGetName(MlirAttribute attr) {
+  return wrap((Attribute)cast<MDFuncAttr>(unwrap(attr)).getName());
+}
+
+MlirAttribute mlirLLVMMDNodeAttrGet(MlirContext ctx, intptr_t nOperands,
+                                    MlirAttribute const *operands) {
+  SmallVector<Attribute> attrStorage;
+  attrStorage.reserve(nOperands);
+  return wrap(MDNodeAttr::get(
+      unwrap(ctx),
+      ArrayAttr::get(unwrap(ctx),
+                     unwrapList(nOperands, operands, attrStorage))));
+}
+
+bool mlirLLVMAttrIsAMDNodeAttr(MlirAttribute attr) {
+  return isa<MDNodeAttr>(unwrap(attr));
+}
+
+MlirTypeID mlirLLVMMDNodeAttrGetTypeID(void) {
+  return wrap(MDNodeAttr::getTypeID());
+}
+
+intptr_t mlirLLVMMDNodeAttrGetNumOperands(MlirAttribute attr) {
+  return cast<MDNodeAttr>(unwrap(attr)).getOperands().size();
+}
+
+MlirAttribute mlirLLVMMDNodeAttrGetOperand(MlirAttribute attr,
+                                           intptr_t index) {
+  return wrap(cast<MDNodeAttr>(unwrap(attr)).getOperands()[index]);
+}
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 21f7954fd338a..9e758e9fdb0e4 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
+#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/IR/Operation.h"
 #include "mlir/Interfaces/CallInterfaces.h"
@@ -165,8 +166,8 @@ convertCallLLVMIntrinsicOp(CallIntrinsicOp op, llvm::IRBuilderBase &builder,
   // Check the result type of the call.
   const llvm::Type *intrinType =
       op.getNumResults() == 0
-          ? llvm::Type::getVoidTy(module->getContext())
-          : moduleTranslation.convertType(op.getResultTypes().front());
+        ? llvm::Type::getVoidTy(module->getContext())
+        : moduleTranslation.convertType(op.getResultTypes().front());
   if (intrinType != fn->getReturnType()) {
     return mlir::emitError(op.getLoc(), "intrinsic call returns ")
            << diagStr(intrinType) << " but " << op.getIntrinAttr()
@@ -215,6 +216,52 @@ convertCallLLVMIntrinsicOp(CallIntrinsicOp op, llvm::IRBuilderBase &builder,
   return success();
 }
 
+/// Recursively converts an MLIR metadata attribute to an LLVM metadata node.
+static llvm::Metadata *
+convertMetadataAttr(Attribute attr, llvm::IRBuilderBase &builder,
+                    LLVM::ModuleTranslation &moduleTranslation) {
+  return llvm::TypeSwitch<Attribute, llvm::Metadata *>(attr)
+         .Case<LLVM::MDStringAttr>([&](auto a) -> llvm::Metadata * {
+           return llvm::MDString::get(builder.getContext(),
+                                      a.getValue().getValue());
+         })
+         .Case<LLVM::MDConstantAttr>([&](auto a) -> llvm::Metadata * {
+           IntegerAttr intAttr = a.getValue();
+           return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+               llvm::Type::getIntNTy(builder.getContext(),
+                                     intAttr.getType().getIntOrFloatBitWidth()),
+               intAttr.getValue()));
+         })
+         .Case<LLVM::MDFuncAttr>([&](auto a) -> llvm::Metadata * {
+           if (llvm::Function *fn =
+               moduleTranslation.lookupFunction(a.getName().getValue()))
+             return llvm::ValueAsMetadata::get(fn);
+           return nullptr;
+         })
+         .Case<LLVM::MDNodeAttr>([&](auto a) -> llvm::Metadata * {
+           SmallVector<llvm::Metadata *> operands;
+           for (Attribute op : a.getOperands())
+             operands.push_back(
+                 convertMetadataAttr(op, builder, moduleTranslation));
+           return llvm::MDNode::get(builder.getContext(), operands);
+         })
+         .Default([](auto) -> llvm::Metadata * { return nullptr; });
+}
+
+static void convertNamedMetadataOp(StringRef metadataName, ArrayAttr nodes,
+                                   llvm::IRBuilderBase &builder,
+                                   LLVM::ModuleTranslation &moduleTranslation) {
+  llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+  llvm::NamedMDNode *namedMD =
+      llvmModule->getOrInsertNamedMetadata(metadataName);
+  for (Attribute nodeAttr : nodes) {
+    llvm::Metadata *md =
+        convertMetadataAttr(nodeAttr, builder, moduleTranslation);
+    if (auto *mdNode = llvm::dyn_cast_or_null<llvm::MDNode>(md))
+      namedMD->addOperand(mdNode);
+  }
+}
+
 static void convertLinkerOptionsOp(ArrayAttr options,
                                    llvm::IRBuilderBase &builder,
                                    LLVM::ModuleTranslation &moduleTranslation) {
@@ -247,7 +294,7 @@ convertModuleFlagValue(StringRef key, ArrayAttr arrayAttr,
         if (!sym)
           return nullptr;
         if (llvm::Function *fn =
-                moduleTranslation.lookupFunction(sym.getValue()))
+            moduleTranslation.lookupFunction(sym.getValue()))
           return llvm::ValueAsMetadata::get(fn);
         return nullptr;
       };
@@ -274,7 +321,7 @@ static llvm::Metadata *convertModuleFlagProfileSummaryAttr(
   auto getIntTuple = [&](StringRef key, uint64_t val) -> llvm::MDTuple * {
     SmallVector<llvm::Metadata *> tupleNodes{
         mdb.createString(key), mdb.createConstant(llvm::ConstantInt::get(
-                                   llvm::Type::getInt64Ty(context), val))};
+            llvm::Type::getInt64Ty(context), val))};
     return llvm::MDTuple::get(context, tupleNodes);
   };
 
@@ -333,26 +380,26 @@ static void convertModuleFlagsOp(ArrayAttr flags, llvm::IRBuilderBase &builder,
   for (auto flagAttr : flags.getAsRange<ModuleFlagAttr>()) {
     llvm::Metadata *valueMetadata =
         llvm::TypeSwitch<Attribute, llvm::Metadata *>(flagAttr.getValue())
-            .Case([&](StringAttr strAttr) {
-              return llvm::MDString::get(builder.getContext(),
-                                         strAttr.getValue());
-            })
-            .Case([&](IntegerAttr intAttr) {
-              return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
-                  llvm::Type::getInt32Ty(builder.getContext()),
-                  intAttr.getInt()));
-            })
-            .Case([&](ArrayAttr arrayAttr) {
-              return convertModuleFlagValue(flagAttr.getKey().getValue(),
-                                            arrayAttr, builder,
-                                            moduleTranslation);
-            })
-            .Case([&](ModuleFlagProfileSummaryAttr summaryAttr) {
-              return convertModuleFlagProfileSummaryAttr(
-                  flagAttr.getKey().getValue(), summaryAttr, builder,
-                  moduleTranslation);
-            })
-            .Default([](auto) { return nullptr; });
+        .Case([&](StringAttr strAttr) {
+          return llvm::MDString::get(builder.getContext(),
+                                     strAttr.getValue());
+        })
+        .Case([&](IntegerAttr intAttr) {
+          return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+              llvm::Type::getInt32Ty(builder.getContext()),
+              intAttr.getInt()));
+        })
+        .Case([&](ArrayAttr arrayAttr) {
+          return convertModuleFlagValue(flagAttr.getKey().getValue(),
+                                        arrayAttr, builder,
+                                        moduleTranslation);
+        })
+        .Case([&](ModuleFlagProfileSummaryAttr summaryAttr) {
+          return convertModuleFlagProfileSummaryAttr(
+              flagAttr.getKey().getValue(), summaryAttr, builder,
+              moduleTranslation);
+        })
+        .Default([](auto) { return nullptr; });
 
     assert(valueMetadata && "expected valid metadata");
     llvmModule->addModuleFlag(
@@ -365,9 +412,9 @@ static llvm::DILocalScope *
 getLocalScopeFromLoc(llvm::IRBuilderBase &builder, Location loc,
                      LLVM::ModuleTranslation &moduleTranslation) {
   if (auto scopeLoc =
-          loc->findInstanceOf<FusedLocWith<LLVM::DILocalScopeAttr>>())
+      loc->findInstanceOf<FusedLocWith<LLVM::DILocalScopeAttr>>())
     if (auto *localScope = llvm::dyn_cast<llvm::DILocalScope>(
-            moduleTranslation.translateDebugInfo(scopeLoc.getMetadata())))
+        moduleTranslation.translateDebugInfo(scopeLoc.getMetadata())))
       return localScope;
   return builder.GetInsertBlock()->getParent()->getSubprogram();
 }
@@ -396,7 +443,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     llvm::CallInst *call;
     if (auto attr = callOp.getCalleeAttr()) {
       if (llvm::Function *function =
-              moduleTranslation.lookupFunction(attr.getValue())) {
+          moduleTranslation.lookupFunction(attr.getValue())) {
         call = builder.CreateCall(function, operandsRef, opBundles);
       } else {
         Operation *moduleOp = parentLLVMModule(&opInst);
@@ -480,8 +527,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
         ModuleTranslation::convertDefaultFuncAttr);
 
     if (llvm::Attribute attr =
-            moduleTranslation.convertAllocsizeAttr(callOp.getAllocsizeAttr());
-        attr.isValid())
+          moduleTranslation.convertAllocsizeAttr(callOp.getAllocsizeAttr());
+      attr.isValid())
       call->addFnAttr(attr);
 
     if (failed(moduleTranslation.convertArgAndResultAttrs(callOp, call)))
@@ -513,7 +560,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     // that LLVM IR dialect CallOp has either 0 or 1 result.
     if (opInst.getNumResults() != 0)
       moduleTranslation.mapValue(opInst.getResult(0), call);
-    // Check that LLVM call returns void for 0-result functions.
+      // Check that LLVM call returns void for 0-result functions.
     else if (!call->getType()->isVoidTy())
       return failure();
     moduleTranslation.mapCall(callOp, call);
@@ -536,19 +583,19 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     auto ft = LLVM::LLVMFunctionType::get(resultType, operandTypes);
     llvm::InlineAsm *inlineAsmInst =
         inlineAsmOp.getAsmDialect()
-            ? llvm::InlineAsm::get(
-                  static_cast<llvm::FunctionType *>(
-                      moduleTranslation.convertType(ft)),
-                  inlineAsmOp.getAsmString(), inlineAsmOp.getConstraints(),
-                  inlineAsmOp.getHasSideEffects(),
-                  inlineAsmOp.getIsAlignStack(),
-                  convertAsmDialectToLLVM(*inlineAsmOp.getAsmDialect()))
-            : llvm::InlineAsm::get(static_cast<llvm::FunctionType *>(
-                                       moduleTranslation.convertType(ft)),
-                                   inlineAsmOp.getAsmString(),
-                                   inlineAsmOp.getConstraints(),
-                                   inlineAsmOp.getHasSideEffects(),
-                                   inlineAsmOp.getIsAlignStack());
+          ? llvm::InlineAsm::get(
+              static_cast<llvm::FunctionType *>(
+                moduleTranslation.convertType(ft)),
+              inlineAsmOp.getAsmString(), inlineAsmOp.getConstraints(),
+              inlineAsmOp.getHasSideEffects(),
+              inlineAsmOp.getIsAlignStack(),
+              convertAsmDialectToLLVM(*inlineAsmOp.getAsmDialect()))
+          : llvm::InlineAsm::get(static_cast<llvm::FunctionType *>(
+                                   moduleTranslation.convertType(ft)),
+                                 inlineAsmOp.getAsmString(),
+                                 inlineAsmOp.getConstraints(),
+                                 inlineAsmOp.getHasSideEffects(),
+                                 inlineAsmOp.getIsAlignStack());
     llvm::CallInst *inst = builder.CreateCall(
         inlineAsmInst,
         moduleTranslation.lookupValues(inlineAsmOp.getOperands()));
@@ -696,7 +743,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
 
     // The verifier should not have allowed this.
     assert((global || function || alias || ifunc) &&
-           "referencing an undefined global, function, alias, or ifunc");
+        "referencing an undefined global, function, alias, or ifunc");
 
     llvm::Value *llvmValue = nullptr;
     if (global)
@@ -715,7 +762,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
   // Emit dso_local_equivalent. We need to look up the global value referenced
   // by the operation and store it in the MLIR-to-LLVM value mapping.
   if (auto dsoLocalEquivalentOp =
-          dyn_cast<LLVM::DSOLocalEquivalentOp>(opInst)) {
+      dyn_cast<LLVM::DSOLocalEquivalentOp>(opInst)) {
     LLVM::LLVMFuncOp function =
         dsoLocalEquivalentOp.getFunction(moduleTranslation.symbolTable());
     LLVM::AliasOp alias =
@@ -723,7 +770,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
 
     // The verifier should not have allowed this.
     assert((function || alias) &&
-           "referencing an undefined function, or alias");
+        "referencing an undefined function, or alias");
 
     llvm::Value *llvmValue = nullptr;
     if (alias)
@@ -762,8 +809,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
           /*isConstant=*/true, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
           /*Initializer=*/nullptr,
           Twine("__mlir_block_address_")
-              .concat(Twine(fnName))
-              .concat(Twine((uint64_t)blockAddressOp.getOperation())));
+          .concat(Twine(fnName))
+          .concat(Twine((uint64_t)blockAddressOp.getOperation())));
       moduleTranslation.mapUnresolvedBlockAddress(blockAddressOp, llvmValue);
     }
 
diff --git a/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir b/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
new file mode 100644
index 0000000000000..144285e9bfebc
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
@@ -0,0 +1,107 @@
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+// Tests LLVM named metadata translation with deeply nested metadata trees.
+
+// CHECK: !foo.version = !{![[VERSION:[0-9]+]]}
+// CHECK: !foo.language_version = !{![[LANG:[0-9]+]]}
+// CHECK: !foo.kernel = !{![[KERNEL:[0-9]+]]}
+
+llvm.func @my_kernel() {
+  llvm.return
+}
+
+llvm.named_metadata "foo.version" [
+  #llvm.md_node<[#llvm.md_const<1 : i32>, #llvm.md_const<0 : i32>,
+                  #llvm.md_const<0 : i32>]>
+]
+// CHECK-DAG: ![[VERSION]] = !{i32 1, i32 0, i32 0}
+
+llvm.named_metadata "foo.language_version" [
+  #llvm.md_node<[#llvm.md_string<"Bar">, #llvm.md_const<1 : i32>,
+                  #llvm.md_const<2 : i32>, #llvm.md_const<3 : i32>]>
+]
+// CHECK-DAG: ![[LANG]] = !{!"Bar", i32 1, i32 2, i32 3}
+
+#buf0 = #llvm.md_node<[
+  #llvm.md_const<0 : i32>, #llvm.md_string<"foo.buffer">,
+  #llvm.md_string<"foo.idx">, #llvm.md_const<0 : i32>,
+  #llvm.md_const<1 : i32>, #llvm.md_string<"foo.read">,
+  #llvm.md_string<"foo.address_space">, #llvm.md_const<1 : i32>,
+  #llvm.md_string<"foo.size">, #llvm.md_const<4 : i32>,
+  #llvm.md_string<"foo.align_size">, #llvm.md_const<4 : i32>
+]>
+// CHECK-DAG: ![[A0:[0-9]+]] = !{i32 0, !"foo.buffer", !"foo.idx", i32 0, i32 1, !"foo.read", !"foo.address_space", i32 1, !"foo.size", i32 4, !"foo.align_size", i32 4}
+
+#buf1 = #llvm.md_node<[
+  #llvm.md_const<1 : i32>, #llvm.md_string<"foo.buffer">,
+  #llvm.md_string<"foo.idx">, #llvm.md_const<1 : i32>,
+  #llvm.md_const<1 : i32>, #llvm.md_string<"foo.read_write">,
+  #llvm.md_string<"foo.address_space">, #llvm.md_const<1 : i32>,
+  #llvm.md_string<"foo.size">, #llvm.md_const<4 : i32>,
+  #llvm.md_string<"foo.align_size">, #llvm.md_const<4 : i32>
+]>
+// CHECK-DAG: ![[A1:[0-9]+]] = !{i32 1, !"foo.buffer", !"foo.idx", i32 1, i32 1, !"foo.read_write", !"foo.address_space", i32 1, !"foo.size", i32 4, !"foo.align_size", i32 4}
+
+#buf2 = #llvm.md_node<[
+  #llvm.md_const<2 : i32>, #llvm.md_string<"foo.buffer">,
+  #llvm.md_string<"foo.idx">, #llvm.md_const<2 : i32>,
+  #llvm.md_const<1 : i32>, #llvm.md_string<"foo.read">,
+  #llvm.md_string<"foo.address_space">, #llvm.md_const<2 : i32>,
+  #llvm.md_string<"foo.size">, #llvm.md_const<4 : i32>,
+  #llvm.md_string<"foo.align_size">, #llvm.md_const<4 : i32>
+]>
+// CHECK-DAG: ![[A2:[0-9]+]] = !{i32 2, !"foo.buffer", !"foo.idx", i32 2, i32 1, !"foo.read", !"foo.address_space", i32 2, !"foo.size", i32 4, !"foo.align_size", i32 4}
+
+#pos3 = #llvm.md_node<[
+  #llvm.md_const<3 : i32>, #llvm.md_string<"foo.block_position_in_grid">,
+  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
+  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"block_position_in_grid">
+]>
+// CHECK-DAG: ![[A3:[0-9]+]] = !{i32 3, !"foo.block_position_in_grid", !"foo.name", !"vec3", !"foo.arg_name", !"block_position_in_grid"}
+
+#pos4 = #llvm.md_node<[
+  #llvm.md_const<4 : i32>, #llvm.md_string<"foo.blocks_per_grid">,
+  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
+  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"blocks_per_grid">
+]>
+// CHECK-DAG: ![[A4:[0-9]+]] = !{i32 4, !"foo.blocks_per_grid", !"foo.name", !"vec3", !"foo.arg_name", !"blocks_per_grid"}
+
+#pos5 = #llvm.md_node<[
+  #llvm.md_const<5 : i32>, #llvm.md_string<"foo.thread_position_in_block">,
+  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
+  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"thread_position_in_block">
+]>
+// CHECK-DAG: ![[A5:[0-9]+]] = !{i32 5, !"foo.thread_position_in_block", !"foo.name", !"vec3", !"foo.arg_name", !"thread_position_in_block"}
+
+#pos6 = #llvm.md_node<[
+  #llvm.md_const<6 : i32>, #llvm.md_string<"foo.threads_per_block">,
+  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
+  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"threads_per_block">
+]>
+// CHECK-DAG: ![[A6:[0-9]+]] = !{i32 6, !"foo.threads_per_block", !"foo.name", !"vec3", !"foo.arg_name", !"threads_per_block"}
+
+#pos7 = #llvm.md_node<[
+  #llvm.md_const<7 : i32>, #llvm.md_string<"foo.thread_idx_in_warp">,
+  #llvm.md_string<"foo.name">, #llvm.md_string<"vec1">,
+  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"thread_idx_in_warp">
+]>
+// CHECK-DAG: ![[A7:[0-9]+]] = !{i32 7, !"foo.thread_idx_in_warp", !"foo.name", !"vec1", !"foo.arg_name", !"thread_idx_in_warp"}
+
+#pos8 = #llvm.md_node<[
+  #llvm.md_const<8 : i32>, #llvm.md_string<"foo.warp_idx_in_block">,
+  #llvm.md_string<"foo.name">, #llvm.md_string<"vec1">,
+  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"warp_idx_in_block">
+]>
+// CHECK-DAG: ![[A8:[0-9]+]] = !{i32 8, !"foo.warp_idx_in_block", !"foo.name", !"vec1", !"foo.arg_name", !"warp_idx_in_block"}
+
+llvm.named_metadata "foo.kernel" [
+  #llvm.md_node<[
+    #llvm.md_func<@my_kernel>,
+    #llvm.md_node<[]>,
+    #llvm.md_node<[#buf0, #buf1, #buf2,
+                    #pos3, #pos4, #pos5, #pos6, #pos7, #pos8]>
+  ]>
+]
+// CHECK-DAG: ![[KERNEL]] = !{ptr @my_kernel, ![[EMPTY:[0-9]+]], ![[ARGS:[0-9]+]]}
+// CHECK-DAG: ![[EMPTY]] = !{}
+// CHECK-DAG: ![[ARGS]] = !{![[A0]], ![[A1]], ![[A2]], ![[A3]], ![[A4]], ![[A5]], ![[A6]], ![[A7]], ![[A8]]}

>From b748bea46836d94cc05a1348631e701e7820e843 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Sun, 15 Mar 2026 13:58:08 -0700
Subject: [PATCH 2/4] add python bindings test

---
 mlir/include/mlir-c/Dialect/LLVM.h       |  23 +++-
 mlir/lib/Bindings/Python/DialectLLVM.cpp |  48 +++++++++
 mlir/lib/CAPI/Dialect/LLVM.cpp           |  12 +++
 mlir/test/python/dialects/llvm.py        | 130 +++++++++++++++++++++++
 4 files changed, 208 insertions(+), 5 deletions(-)

diff --git a/mlir/include/mlir-c/Dialect/LLVM.h b/mlir/include/mlir-c/Dialect/LLVM.h
index 2d603ffc09edb..a78cb84642b1c 100644
--- a/mlir/include/mlir-c/Dialect/LLVM.h
+++ b/mlir/include/mlir-c/Dialect/LLVM.h
@@ -63,6 +63,12 @@ mlirLLVMFunctionTypeGet(MlirType resultType, intptr_t nArgumentTypes,
 
 MLIR_CAPI_EXPORTED MlirStringRef mlirLLVMFunctionTypeGetName(void);
 
+/// Returns `true` if the type is an LLVM dialect function type.
+MLIR_CAPI_EXPORTED bool mlirTypeIsALLVMFunctionType(MlirType type);
+
+/// Returns the TypeID of an LLVM function type.
+MLIR_CAPI_EXPORTED MlirTypeID mlirLLVMFunctionTypeGetTypeID(void);
+
 /// Returns the number of input types.
 MLIR_CAPI_EXPORTED intptr_t mlirLLVMFunctionTypeGetNumInputs(MlirType type);
 
@@ -70,6 +76,9 @@ MLIR_CAPI_EXPORTED intptr_t mlirLLVMFunctionTypeGetNumInputs(MlirType type);
 MLIR_CAPI_EXPORTED MlirType mlirLLVMFunctionTypeGetInput(MlirType type,
                                                          intptr_t pos);
 
+/// Returns `true` if the function type is variadic.
+MLIR_CAPI_EXPORTED bool mlirLLVMFunctionTypeIsVarArg(MlirType type);
+
 /// Returns the return type of the function type.
 MLIR_CAPI_EXPORTED MlirType mlirLLVMFunctionTypeGetReturnType(MlirType type);
 
@@ -190,6 +199,7 @@ enum MlirLLVMCConv {
   MlirLLVMCConvAMDGPU_Gfx = 100,
   MlirLLVMCConvM68k_INTR = 101,
 };
+
 typedef enum MlirLLVMCConv MlirLLVMCConv;
 
 /// Creates a LLVM CConv attribute.
@@ -205,6 +215,7 @@ enum MlirLLVMComdat {
   MlirLLVMComdatNoDeduplicate = 3,
   MlirLLVMComdatSameSize = 4,
 };
+
 typedef enum MlirLLVMComdat MlirLLVMComdat;
 
 /// Creates a LLVM Comdat attribute.
@@ -226,6 +237,7 @@ enum MlirLLVMLinkage {
   MlirLLVMLinkageExternWeak = 9,
   MlirLLVMLinkageCommon = 10,
 };
+
 typedef enum MlirLLVMLinkage MlirLLVMLinkage;
 
 /// Creates a LLVM Linkage attribute.
@@ -274,6 +286,7 @@ enum MlirLLVMTypeEncoding {
   MlirLLVMTypeEncodingLoUser = 0x80,
   MlirLLVMTypeEncodingHiUser = 0xff,
 };
+
 typedef enum MlirLLVMTypeEncoding MlirLLVMTypeEncoding;
 
 /// Creates a LLVM DIBasicType attribute.
@@ -337,6 +350,7 @@ enum MlirLLVMDIEmissionKind {
   MlirLLVMDIEmissionKindLineTablesOnly = 2,
   MlirLLVMDIEmissionKindDebugDirectivesOnly = 3,
 };
+
 typedef enum MlirLLVMDIEmissionKind MlirLLVMDIEmissionKind;
 
 enum MlirLLVMDINameTableKind {
@@ -345,6 +359,7 @@ enum MlirLLVMDINameTableKind {
   MlirLLVMDINameTableKindNone = 2,
   MlirLLVMDINameTableKindApple = 3,
 };
+
 typedef enum MlirLLVMDINameTableKind MlirLLVMDINameTableKind;
 
 /// Creates a LLVM DICompileUnit attribute.
@@ -499,13 +514,11 @@ MLIR_CAPI_EXPORTED bool mlirLLVMAttrIsAMDFuncAttr(MlirAttribute attr);
 MLIR_CAPI_EXPORTED MlirTypeID mlirLLVMMDFuncAttrGetTypeID(void);
 
 /// Returns the symbol name of an LLVM MDFuncAttr.
-MLIR_CAPI_EXPORTED MlirAttribute
-mlirLLVMMDFuncAttrGetName(MlirAttribute attr);
+MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMMDFuncAttrGetName(MlirAttribute attr);
 
 /// Creates an LLVM MDNodeAttr (metadata tuple).
-MLIR_CAPI_EXPORTED MlirAttribute
-mlirLLVMMDNodeAttrGet(MlirContext ctx, intptr_t nOperands,
-                      MlirAttribute const *operands);
+MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMMDNodeAttrGet(
+    MlirContext ctx, intptr_t nOperands, MlirAttribute const *operands);
 
 /// Returns `true` if the attribute is an LLVM MDNodeAttr.
 MLIR_CAPI_EXPORTED bool mlirLLVMAttrIsAMDNodeAttr(MlirAttribute attr);
diff --git a/mlir/lib/Bindings/Python/DialectLLVM.cpp b/mlir/lib/Bindings/Python/DialectLLVM.cpp
index 6db6748a40cb0..3f9442adf9de7 100644
--- a/mlir/lib/Bindings/Python/DialectLLVM.cpp
+++ b/mlir/lib/Bindings/Python/DialectLLVM.cpp
@@ -224,6 +224,53 @@ struct PointerType : PyConcreteType<PointerType> {
   }
 };
 
+//===--------------------------------------------------------------------===//
+// FunctionType
+//===--------------------------------------------------------------------===//
+
+struct FunctionType : PyConcreteType<FunctionType> {
+  static constexpr IsAFunctionTy isaFunction = mlirTypeIsALLVMFunctionType;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLLVMFunctionTypeGetTypeID;
+  static constexpr const char *pyClassName = "FunctionType";
+  static inline const MlirStringRef name = mlirLLVMFunctionTypeGetName();
+  using Base::Base;
+
+  static void bindDerived(ClassTy &c) {
+    c.def_static(
+        "get",
+        [](PyType &resultType, const std::vector<PyType> &argumentTypes,
+           bool isVarArg) {
+          std::vector<MlirType> argTypes(argumentTypes.size());
+          std::copy(argumentTypes.begin(), argumentTypes.end(),
+                    argTypes.begin());
+          return FunctionType(
+              resultType.getContext(),
+              mlirLLVMFunctionTypeGet(resultType, argTypes.size(),
+                                      argTypes.data(), isVarArg));
+        },
+        "result_type"_a, "argument_types"_a, nb::kw_only(),
+        "is_var_arg"_a = false);
+    c.def_prop_ro("return_type", [](const FunctionType &type) {
+      return mlirLLVMFunctionTypeGetReturnType(type);
+    });
+    c.def_prop_ro("num_inputs", [](const FunctionType &type) {
+      return mlirLLVMFunctionTypeGetNumInputs(type);
+    });
+    c.def_prop_ro("inputs", [](const FunctionType &type) {
+      nb::list inputs;
+      for (intptr_t i = 0, e = mlirLLVMFunctionTypeGetNumInputs(type); i < e;
+           ++i) {
+        inputs.append(mlirLLVMFunctionTypeGetInput(type, i));
+      }
+      return inputs;
+    });
+    c.def_prop_ro("is_var_arg", [](const FunctionType &type) {
+      return mlirLLVMFunctionTypeIsVarArg(type);
+    });
+  }
+};
+
 //===--------------------------------------------------------------------===//
 // Metadata Attributes
 //===--------------------------------------------------------------------===//
@@ -343,6 +390,7 @@ static void populateDialectLLVMSubmodule(nanobind::module_ &m) {
   StructType::bind(m);
   ArrayType::bind(m);
   PointerType::bind(m);
+  FunctionType::bind(m);
   MDStringAttr::bind(m);
   MDConstantAttr::bind(m);
   MDFuncAttr::bind(m);
diff --git a/mlir/lib/CAPI/Dialect/LLVM.cpp b/mlir/lib/CAPI/Dialect/LLVM.cpp
index c9b2e999c645e..8f46a60f5ad13 100644
--- a/mlir/lib/CAPI/Dialect/LLVM.cpp
+++ b/mlir/lib/CAPI/Dialect/LLVM.cpp
@@ -85,6 +85,14 @@ MlirStringRef mlirLLVMFunctionTypeGetName(void) {
   return wrap(LLVMFunctionType::name);
 }
 
+bool mlirTypeIsALLVMFunctionType(MlirType type) {
+  return isa<LLVM::LLVMFunctionType>(unwrap(type));
+}
+
+MlirTypeID mlirLLVMFunctionTypeGetTypeID(void) {
+  return wrap(LLVM::LLVMFunctionType::getTypeID());
+}
+
 intptr_t mlirLLVMFunctionTypeGetNumInputs(MlirType type) {
   return llvm::cast<LLVM::LLVMFunctionType>(unwrap(type)).getNumParams();
 }
@@ -99,6 +107,10 @@ MlirType mlirLLVMFunctionTypeGetReturnType(MlirType type) {
   return wrap(llvm::cast<LLVM::LLVMFunctionType>(unwrap(type)).getReturnType());
 }
 
+bool mlirLLVMFunctionTypeIsVarArg(MlirType type) {
+  return llvm::cast<LLVM::LLVMFunctionType>(unwrap(type)).isVarArg();
+}
+
 bool mlirTypeIsALLVMStructType(MlirType type) {
   return isa<LLVM::LLVMStructType>(unwrap(type));
 }
diff --git a/mlir/test/python/dialects/llvm.py b/mlir/test/python/dialects/llvm.py
index 7a2b8e1809c47..b1d64f5fc27fe 100644
--- a/mlir/test/python/dialects/llvm.py
+++ b/mlir/test/python/dialects/llvm.py
@@ -215,3 +215,133 @@ def testTranslateToLLVMIR():
         # CHECK:   ret i64 %3
         # CHECK: }
         print(llvm.translate_module_to_llvmir(module.operation))
+
+
+# CHECK-LABEL: testMetadataAttrs
+ at constructAndPrintInModule
+def testMetadataAttrs():
+    # MDStringAttr
+    md_str = llvm.MDStringAttr.get("foo.buffer")
+    # CHECK: #llvm.md_string<"foo.buffer">
+    print(md_str)
+    assert md_str.value == "foo.buffer"
+
+    # MDConstantAttr
+    i32 = IntegerType.get_signless(32)
+    md_const = llvm.MDConstantAttr.get(IntegerAttr.get(i32, 42))
+    # CHECK: #llvm.md_const<42 : i32>
+    print(md_const)
+
+    # MDFuncAttr
+    md_func = llvm.MDFuncAttr.get("my_kernel")
+    # CHECK: #llvm.md_func<@my_kernel>
+    print(md_func)
+    assert md_func.name == "my_kernel"
+
+    # MDNodeAttr - empty
+    md_empty = llvm.MDNodeAttr.get([])
+    # CHECK: #llvm.md_node<[]>
+    print(md_empty)
+    assert len(md_empty) == 0
+
+    # MDNodeAttr - with operands
+    md_node = llvm.MDNodeAttr.get([md_const, md_str])
+    # CHECK: #llvm.md_node<[#llvm.md_const<42 : i32>, #llvm.md_string<"foo.buffer">]>
+    print(md_node)
+    assert len(md_node) == 2
+
+    # MDNodeAttr - nested
+    md_nested = llvm.MDNodeAttr.get([md_node, md_empty])
+    # CHECK: #llvm.md_node<[#llvm.md_node<[#llvm.md_const<42 : i32>, #llvm.md_string<"foo.buffer">]>, #llvm.md_node<[]>]>
+    print(md_nested)
+    assert len(md_nested) == 2
+
+
+# CHECK-LABEL: testNamedMetadata
+ at constructAndPrintInModule
+def testNamedMetadata():
+    i32 = IntegerType.get_signless(32)
+
+    def md_const(val):
+        return llvm.MDConstantAttr.get(IntegerAttr.get(i32, val))
+
+    def md_str(s):
+        return llvm.MDStringAttr.get(s)
+
+    void = Type.parse("!llvm.void")
+    func_ty = llvm.FunctionType.get(void, [])
+
+    llvm.LLVMFuncOp("my_kernel", TypeAttr.get(func_ty))
+    # CHECK-LABEL:   llvm.func @my_kernel()
+
+    llvm.NamedMetadataOp(
+        metadata_name="foo.version",
+        nodes=ArrayAttr.get(
+            [llvm.MDNodeAttr.get([md_const(1), md_const(0), md_const(0)])]
+        ),
+    )
+    # CHECK: llvm.named_metadata "foo.version" [#llvm.md_node<[#llvm.md_const<1 : i32>, #llvm.md_const<0 : i32>, #llvm.md_const<0 : i32>]>]
+
+    llvm.NamedMetadataOp(
+        metadata_name="foo.language_version",
+        nodes=ArrayAttr.get(
+            [
+                llvm.MDNodeAttr.get(
+                    [md_str("Bar"), md_const(1), md_const(2), md_const(3)]
+                )
+            ]
+        ),
+    )
+    # CHECK: llvm.named_metadata "foo.language_version" [#llvm.md_node<[#llvm.md_string<"Bar">, #llvm.md_const<1 : i32>, #llvm.md_const<2 : i32>, #llvm.md_const<3 : i32>]>]
+
+    buf0 = llvm.MDNodeAttr.get(
+        [
+            md_const(0),
+            md_str("foo.buffer"),
+            md_str("foo.idx"),
+            md_const(0),
+            md_const(1),
+            md_str("foo.read"),
+            md_str("foo.address_space"),
+            md_const(1),
+            md_str("foo.size"),
+            md_const(4),
+            md_str("foo.align_size"),
+            md_const(4),
+        ]
+    )
+
+    llvm.NamedMetadataOp(
+        metadata_name="foo.kernel",
+        nodes=ArrayAttr.get(
+            [
+                llvm.MDNodeAttr.get(
+                    [
+                        llvm.MDFuncAttr.get("my_kernel"),
+                        llvm.MDNodeAttr.get([]),
+                        buf0,
+                    ]
+                )
+            ]
+        ),
+    )
+    # CHECK:       llvm.named_metadata "foo.kernel" [
+    # CHECK-SAME:  #llvm.md_node<[
+    # CHECK-SAME:      #llvm.md_func<@my_kernel>,
+    # CHECK-SAME:      #llvm.md_node<[]>,
+    # CHECK-SAME:      #llvm.md_node<[
+    # CHECK-SAME:          #llvm.md_const<0 : i32>,
+    # CHECK-SAME:          #llvm.md_string<"foo.buffer">,
+    # CHECK-SAME:          #llvm.md_string<"foo.idx">,
+    # CHECK-SAME:          #llvm.md_const<0 : i32>,
+    # CHECK-SAME:          #llvm.md_const<1 : i32>,
+    # CHECK-SAME:          #llvm.md_string<"foo.read">,
+    # CHECK-SAME:          #llvm.md_string<"foo.address_space">,
+    # CHECK-SAME:          #llvm.md_const<1 : i32>,
+    # CHECK-SAME:          #llvm.md_string<"foo.size">,
+    # CHECK-SAME:          #llvm.md_const<4 : i32>,
+    # CHECK-SAME:          #llvm.md_string<"foo.align_size">,
+    # CHECK-SAME:          #llvm.md_const<4 : i32>
+    # CHECK-SAME:      ]>
+    # CHECK-SAME:    ]>
+    # CHECK-SAME:  ]

>From 5a729d8ebaf1417e260666f52a51b65dff78f4c5 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Sun, 15 Mar 2026 14:42:26 -0700
Subject: [PATCH 3/4] format and document integer limitation

---
 mlir/include/mlir-c/Dialect/LLVM.h            |   2 +-
 .../mlir/Dialect/LLVMIR/LLVMAttrDefs.td       |   9 +-
 mlir/lib/Bindings/Python/DialectLLVM.cpp      |  47 +++---
 mlir/lib/CAPI/Dialect/LLVM.cpp                |  21 ++-
 .../LLVMIR/LLVMToLLVMIRTranslation.cpp        | 149 +++++++++---------
 mlir/python/mlir/dialects/llvm.py             |  17 +-
 mlir/test/python/dialects/llvm.py             |  53 ++++---
 7 files changed, 160 insertions(+), 138 deletions(-)

diff --git a/mlir/include/mlir-c/Dialect/LLVM.h b/mlir/include/mlir-c/Dialect/LLVM.h
index a78cb84642b1c..b36a5f6f98a97 100644
--- a/mlir/include/mlir-c/Dialect/LLVM.h
+++ b/mlir/include/mlir-c/Dialect/LLVM.h
@@ -516,7 +516,7 @@ MLIR_CAPI_EXPORTED MlirTypeID mlirLLVMMDFuncAttrGetTypeID(void);
 /// Returns the symbol name of an LLVM MDFuncAttr.
 MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMMDFuncAttrGetName(MlirAttribute attr);
 
-/// Creates an LLVM MDNodeAttr (metadata tuple).
+/// Creates an LLVM MDNodeAttr.
 MLIR_CAPI_EXPORTED MlirAttribute mlirLLVMMDNodeAttrGet(
     MlirContext ctx, intptr_t nOperands, MlirAttribute const *operands);
 
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index b315d563df031..f360c69c6436f 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -1713,14 +1713,15 @@ def LLVM_MDConstantAttr : LLVM_Attr<"MDConstant", "md_const"> {
   let summary = "LLVM constant-as-metadata";
   let description = [{
     Wraps an integer constant as an LLVM metadata node, corresponding to
-    `llvm::ConstantAsMetadata` wrapping a `llvm::ConstantInt` in LLVM IR.
+    `llvm::ConstantAsMetadata` wrapping a `llvm::Constant*` in LLVM IR.
+    Currently, only integers/IntegerAttrs supported.
 
     Example:
     ```mlir
     #llvm.md_const<42 : i32>
     ```
   }];
-  let parameters = (ins "IntegerAttr":$value);
+  let parameters = (ins "Attribute":$value);
   let assemblyFormat = "`<` $value `>`";
 }
 
@@ -1740,9 +1741,9 @@ def LLVM_MDFuncAttr : LLVM_Attr<"MDFunc", "md_func"> {
 }
 
 def LLVM_MDNodeAttr : LLVM_Attr<"MDNode", "md_node"> {
-  let summary = "LLVM metadata tuple node";
+  let summary = "LLVM metadata node";
   let description = [{
-    Represents an LLVM metadata tuple node (`llvm::MDTuple`). The operands
+    Represents an LLVM metadata node. The operands
     can be any combination of metadata attributes: `#llvm.md_string`,
     `#llvm.md_const`, `#llvm.md_func`, or nested `#llvm.md_node`.
 
diff --git a/mlir/lib/Bindings/Python/DialectLLVM.cpp b/mlir/lib/Bindings/Python/DialectLLVM.cpp
index 3f9442adf9de7..7e4f24b556613 100644
--- a/mlir/lib/Bindings/Python/DialectLLVM.cpp
+++ b/mlir/lib/Bindings/Python/DialectLLVM.cpp
@@ -26,8 +26,7 @@ using namespace mlir::python::nanobind_adaptors;
 
 namespace mlir {
 namespace python {
-namespace
-MLIR_BINDINGS_PYTHON_DOMAIN {
+namespace MLIR_BINDINGS_PYTHON_DOMAIN {
 namespace llvm {
 //===--------------------------------------------------------------------===//
 // StructType
@@ -286,16 +285,15 @@ struct MDStringAttr : PyConcreteAttribute<MDStringAttr> {
     c.def_static(
         "get",
         [](const std::string &value, DefaultingPyMlirContext context) {
-          return MDStringAttr(context->getRef(),
-                              mlirLLVMMDStringAttrGet(
-                                  context.get()->get(),
-                                  mlirStringRefCreate(value.data(),
-                                    value.size())));
+          return MDStringAttr(
+              context->getRef(),
+              mlirLLVMMDStringAttrGet(
+                  context.get()->get(),
+                  mlirStringRefCreate(value.data(), value.size())));
         },
         "value"_a, nb::kw_only(), "context"_a = nb::none());
     c.def_prop_ro("value", [](const MDStringAttr &self) {
-      MlirStringRef ref =
-          mlirLLVMMDStringAttrGetValue(self);
+      MlirStringRef ref = mlirLLVMMDStringAttrGetValue(self);
       return nb::str(ref.data, ref.length);
     });
   }
@@ -311,10 +309,10 @@ struct MDConstantAttr : PyConcreteAttribute<MDConstantAttr> {
   static void bindDerived(ClassTy &c) {
     c.def_static(
         "get",
-        [](PyAttribute &integerAttr, DefaultingPyMlirContext context) {
+        [](PyAttribute &valueAttr, DefaultingPyMlirContext context) {
           return MDConstantAttr(
               context->getRef(),
-              mlirLLVMMDConstantAttrGet(context.get()->get(), integerAttr));
+              mlirLLVMMDConstantAttrGet(context.get()->get(), valueAttr));
         },
         "value"_a, nb::kw_only(), "context"_a = nb::none());
     c.def_prop_ro("value", [](const MDConstantAttr &self) {
@@ -337,9 +335,9 @@ struct MDFuncAttr : PyConcreteAttribute<MDFuncAttr> {
           MlirAttribute symRef = mlirFlatSymbolRefAttrGet(
               context.get()->get(),
               mlirStringRefCreate(name.data(), name.size()));
-          return MDFuncAttr(context->getRef(),
-                            mlirLLVMMDFuncAttrGet(context.get()->get(),
-                                                  symRef));
+          return MDFuncAttr(
+              context->getRef(),
+              mlirLLVMMDFuncAttrGet(context.get()->get(), symRef));
         },
         "name"_a, nb::kw_only(), "context"_a = nb::none());
     c.def_prop_ro("name", [](const MDFuncAttr &self) {
@@ -364,22 +362,21 @@ struct MDNodeAttr : PyConcreteAttribute<MDNodeAttr> {
            DefaultingPyMlirContext context) {
           std::vector<MlirAttribute> operands_(operands.size());
           std::copy(operands.begin(), operands.end(), operands_.begin());
-          return MDNodeAttr(
-              context->getRef(),
-              mlirLLVMMDNodeAttrGet(context.get()->get(), operands_.size(),
-                                    operands_.data()));
+          return MDNodeAttr(context->getRef(),
+                            mlirLLVMMDNodeAttrGet(context.get()->get(),
+                                                  operands_.size(),
+                                                  operands_.data()));
         },
         "operands"_a, nb::kw_only(), "context"_a = nb::none());
     c.def_prop_ro("num_operands", [](const MDNodeAttr &self) {
       return mlirLLVMMDNodeAttrGetNumOperands(self);
     });
-    c.def("__getitem__",
-          [](const MDNodeAttr &self, intptr_t index) {
-            intptr_t n = mlirLLVMMDNodeAttrGetNumOperands(self);
-            if (index < 0 || index >= n)
-              throw nb::index_error("MDNodeAttr operand index out of range");
-            return mlirLLVMMDNodeAttrGetOperand(self, index);
-          });
+    c.def("__getitem__", [](const MDNodeAttr &self, intptr_t index) {
+      intptr_t n = mlirLLVMMDNodeAttrGetNumOperands(self);
+      if (index < 0 || index >= n)
+        throw nb::index_error("MDNodeAttr operand index out of range");
+      return mlirLLVMMDNodeAttrGetOperand(self, index);
+    });
     c.def("__len__", [](const MDNodeAttr &self) {
       return mlirLLVMMDNodeAttrGetNumOperands(self);
     });
diff --git a/mlir/lib/CAPI/Dialect/LLVM.cpp b/mlir/lib/CAPI/Dialect/LLVM.cpp
index 8f46a60f5ad13..f05529ba2ccdf 100644
--- a/mlir/lib/CAPI/Dialect/LLVM.cpp
+++ b/mlir/lib/CAPI/Dialect/LLVM.cpp
@@ -100,7 +100,7 @@ intptr_t mlirLLVMFunctionTypeGetNumInputs(MlirType type) {
 MlirType mlirLLVMFunctionTypeGetInput(MlirType type, intptr_t pos) {
   assert(pos >= 0 && "pos in array must be positive");
   return wrap(llvm::cast<LLVM::LLVMFunctionType>(unwrap(type))
-      .getParamType(static_cast<unsigned>(pos)));
+                  .getParamType(static_cast<unsigned>(pos)));
 }
 
 MlirType mlirLLVMFunctionTypeGetReturnType(MlirType type) {
@@ -191,7 +191,7 @@ MlirLogicalResult mlirLLVMStructTypeSetBody(MlirType structType,
   SmallVector<Type> fields;
   return wrap(
       cast<LLVM::LLVMStructType>(unwrap(structType))
-      .setBody(unwrapList(nFieldTypes, fieldTypes, fields), isPacked));
+          .setBody(unwrapList(nFieldTypes, fieldTypes, fields), isPacked));
 }
 
 MlirAttribute mlirLLVMDIExpressionElemAttrGet(MlirContext ctx,
@@ -541,9 +541,8 @@ MlirStringRef mlirLLVMDIAnnotationAttrGetName(void) {
 //===----------------------------------------------------------------------===//
 
 MlirAttribute mlirLLVMMDStringAttrGet(MlirContext ctx, MlirStringRef value) {
-  return wrap(
-      MDStringAttr::get(unwrap(ctx),
-                        StringAttr::get(unwrap(ctx), unwrap(value))));
+  return wrap(MDStringAttr::get(unwrap(ctx),
+                                StringAttr::get(unwrap(ctx), unwrap(value))));
 }
 
 bool mlirLLVMAttrIsAMDStringAttr(MlirAttribute attr) {
@@ -560,8 +559,8 @@ MlirStringRef mlirLLVMMDStringAttrGetValue(MlirAttribute attr) {
 
 MlirAttribute mlirLLVMMDConstantAttrGet(MlirContext ctx,
                                         MlirAttribute integerAttr) {
-  return wrap(MDConstantAttr::get(unwrap(ctx),
-                                  cast<IntegerAttr>(unwrap(integerAttr))));
+  return wrap(
+      MDConstantAttr::get(unwrap(ctx), cast<IntegerAttr>(unwrap(integerAttr))));
 }
 
 bool mlirLLVMAttrIsAMDConstantAttr(MlirAttribute attr) {
@@ -598,9 +597,8 @@ MlirAttribute mlirLLVMMDNodeAttrGet(MlirContext ctx, intptr_t nOperands,
   SmallVector<Attribute> attrStorage;
   attrStorage.reserve(nOperands);
   return wrap(MDNodeAttr::get(
-      unwrap(ctx),
-      ArrayAttr::get(unwrap(ctx),
-                     unwrapList(nOperands, operands, attrStorage))));
+      unwrap(ctx), ArrayAttr::get(unwrap(ctx), unwrapList(nOperands, operands,
+                                                          attrStorage))));
 }
 
 bool mlirLLVMAttrIsAMDNodeAttr(MlirAttribute attr) {
@@ -615,7 +613,6 @@ intptr_t mlirLLVMMDNodeAttrGetNumOperands(MlirAttribute attr) {
   return cast<MDNodeAttr>(unwrap(attr)).getOperands().size();
 }
 
-MlirAttribute mlirLLVMMDNodeAttrGetOperand(MlirAttribute attr,
-                                           intptr_t index) {
+MlirAttribute mlirLLVMMDNodeAttrGetOperand(MlirAttribute attr, intptr_t index) {
   return wrap(cast<MDNodeAttr>(unwrap(attr)).getOperands()[index]);
 }
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 9e758e9fdb0e4..30f11db091836 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -166,8 +166,8 @@ convertCallLLVMIntrinsicOp(CallIntrinsicOp op, llvm::IRBuilderBase &builder,
   // Check the result type of the call.
   const llvm::Type *intrinType =
       op.getNumResults() == 0
-        ? llvm::Type::getVoidTy(module->getContext())
-        : moduleTranslation.convertType(op.getResultTypes().front());
+          ? llvm::Type::getVoidTy(module->getContext())
+          : moduleTranslation.convertType(op.getResultTypes().front());
   if (intrinType != fn->getReturnType()) {
     return mlir::emitError(op.getLoc(), "intrinsic call returns ")
            << diagStr(intrinType) << " but " << op.getIntrinAttr()
@@ -221,31 +221,33 @@ static llvm::Metadata *
 convertMetadataAttr(Attribute attr, llvm::IRBuilderBase &builder,
                     LLVM::ModuleTranslation &moduleTranslation) {
   return llvm::TypeSwitch<Attribute, llvm::Metadata *>(attr)
-         .Case<LLVM::MDStringAttr>([&](auto a) -> llvm::Metadata * {
-           return llvm::MDString::get(builder.getContext(),
-                                      a.getValue().getValue());
-         })
-         .Case<LLVM::MDConstantAttr>([&](auto a) -> llvm::Metadata * {
-           IntegerAttr intAttr = a.getValue();
-           return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
-               llvm::Type::getIntNTy(builder.getContext(),
-                                     intAttr.getType().getIntOrFloatBitWidth()),
-               intAttr.getValue()));
-         })
-         .Case<LLVM::MDFuncAttr>([&](auto a) -> llvm::Metadata * {
-           if (llvm::Function *fn =
-               moduleTranslation.lookupFunction(a.getName().getValue()))
-             return llvm::ValueAsMetadata::get(fn);
-           return nullptr;
-         })
-         .Case<LLVM::MDNodeAttr>([&](auto a) -> llvm::Metadata * {
-           SmallVector<llvm::Metadata *> operands;
-           for (Attribute op : a.getOperands())
-             operands.push_back(
-                 convertMetadataAttr(op, builder, moduleTranslation));
-           return llvm::MDNode::get(builder.getContext(), operands);
-         })
-         .Default([](auto) -> llvm::Metadata * { return nullptr; });
+      .Case<LLVM::MDStringAttr>([&](auto a) -> llvm::Metadata * {
+        return llvm::MDString::get(builder.getContext(),
+                                   a.getValue().getValue());
+      })
+      .Case<LLVM::MDConstantAttr>([&](auto a) -> llvm::Metadata * {
+        IntegerAttr intAttr = llvm::dyn_cast<IntegerAttr>(a.getValue());
+        if (!intAttr)
+          return nullptr;
+        return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+            llvm::Type::getIntNTy(builder.getContext(),
+                                  intAttr.getType().getIntOrFloatBitWidth()),
+            intAttr.getValue()));
+      })
+      .Case<LLVM::MDFuncAttr>([&](auto a) -> llvm::Metadata * {
+        if (llvm::Function *fn =
+                moduleTranslation.lookupFunction(a.getName().getValue()))
+          return llvm::ValueAsMetadata::get(fn);
+        return nullptr;
+      })
+      .Case<LLVM::MDNodeAttr>([&](auto a) -> llvm::Metadata * {
+        SmallVector<llvm::Metadata *> operands;
+        for (Attribute op : a.getOperands())
+          operands.push_back(
+              convertMetadataAttr(op, builder, moduleTranslation));
+        return llvm::MDNode::get(builder.getContext(), operands);
+      })
+      .Default([](auto) -> llvm::Metadata * { return nullptr; });
 }
 
 static void convertNamedMetadataOp(StringRef metadataName, ArrayAttr nodes,
@@ -294,7 +296,7 @@ convertModuleFlagValue(StringRef key, ArrayAttr arrayAttr,
         if (!sym)
           return nullptr;
         if (llvm::Function *fn =
-            moduleTranslation.lookupFunction(sym.getValue()))
+                moduleTranslation.lookupFunction(sym.getValue()))
           return llvm::ValueAsMetadata::get(fn);
         return nullptr;
       };
@@ -321,7 +323,7 @@ static llvm::Metadata *convertModuleFlagProfileSummaryAttr(
   auto getIntTuple = [&](StringRef key, uint64_t val) -> llvm::MDTuple * {
     SmallVector<llvm::Metadata *> tupleNodes{
         mdb.createString(key), mdb.createConstant(llvm::ConstantInt::get(
-            llvm::Type::getInt64Ty(context), val))};
+                                   llvm::Type::getInt64Ty(context), val))};
     return llvm::MDTuple::get(context, tupleNodes);
   };
 
@@ -380,26 +382,26 @@ static void convertModuleFlagsOp(ArrayAttr flags, llvm::IRBuilderBase &builder,
   for (auto flagAttr : flags.getAsRange<ModuleFlagAttr>()) {
     llvm::Metadata *valueMetadata =
         llvm::TypeSwitch<Attribute, llvm::Metadata *>(flagAttr.getValue())
-        .Case([&](StringAttr strAttr) {
-          return llvm::MDString::get(builder.getContext(),
-                                     strAttr.getValue());
-        })
-        .Case([&](IntegerAttr intAttr) {
-          return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
-              llvm::Type::getInt32Ty(builder.getContext()),
-              intAttr.getInt()));
-        })
-        .Case([&](ArrayAttr arrayAttr) {
-          return convertModuleFlagValue(flagAttr.getKey().getValue(),
-                                        arrayAttr, builder,
-                                        moduleTranslation);
-        })
-        .Case([&](ModuleFlagProfileSummaryAttr summaryAttr) {
-          return convertModuleFlagProfileSummaryAttr(
-              flagAttr.getKey().getValue(), summaryAttr, builder,
-              moduleTranslation);
-        })
-        .Default([](auto) { return nullptr; });
+            .Case([&](StringAttr strAttr) {
+              return llvm::MDString::get(builder.getContext(),
+                                         strAttr.getValue());
+            })
+            .Case([&](IntegerAttr intAttr) {
+              return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+                  llvm::Type::getInt32Ty(builder.getContext()),
+                  intAttr.getInt()));
+            })
+            .Case([&](ArrayAttr arrayAttr) {
+              return convertModuleFlagValue(flagAttr.getKey().getValue(),
+                                            arrayAttr, builder,
+                                            moduleTranslation);
+            })
+            .Case([&](ModuleFlagProfileSummaryAttr summaryAttr) {
+              return convertModuleFlagProfileSummaryAttr(
+                  flagAttr.getKey().getValue(), summaryAttr, builder,
+                  moduleTranslation);
+            })
+            .Default([](auto) { return nullptr; });
 
     assert(valueMetadata && "expected valid metadata");
     llvmModule->addModuleFlag(
@@ -412,9 +414,9 @@ static llvm::DILocalScope *
 getLocalScopeFromLoc(llvm::IRBuilderBase &builder, Location loc,
                      LLVM::ModuleTranslation &moduleTranslation) {
   if (auto scopeLoc =
-      loc->findInstanceOf<FusedLocWith<LLVM::DILocalScopeAttr>>())
+          loc->findInstanceOf<FusedLocWith<LLVM::DILocalScopeAttr>>())
     if (auto *localScope = llvm::dyn_cast<llvm::DILocalScope>(
-        moduleTranslation.translateDebugInfo(scopeLoc.getMetadata())))
+            moduleTranslation.translateDebugInfo(scopeLoc.getMetadata())))
       return localScope;
   return builder.GetInsertBlock()->getParent()->getSubprogram();
 }
@@ -428,6 +430,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     builder.setFastMathFlags(getFastmathFlags(fmf));
 
 #include "mlir/Dialect/LLVMIR/LLVMConversions.inc"
+
 #include "mlir/Dialect/LLVMIR/LLVMIntrinsicConversions.inc"
 
   // Emit function calls.  If the "callee" attribute is present, this is a
@@ -443,7 +446,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     llvm::CallInst *call;
     if (auto attr = callOp.getCalleeAttr()) {
       if (llvm::Function *function =
-          moduleTranslation.lookupFunction(attr.getValue())) {
+              moduleTranslation.lookupFunction(attr.getValue())) {
         call = builder.CreateCall(function, operandsRef, opBundles);
       } else {
         Operation *moduleOp = parentLLVMModule(&opInst);
@@ -527,8 +530,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
         ModuleTranslation::convertDefaultFuncAttr);
 
     if (llvm::Attribute attr =
-          moduleTranslation.convertAllocsizeAttr(callOp.getAllocsizeAttr());
-      attr.isValid())
+            moduleTranslation.convertAllocsizeAttr(callOp.getAllocsizeAttr());
+        attr.isValid())
       call->addFnAttr(attr);
 
     if (failed(moduleTranslation.convertArgAndResultAttrs(callOp, call)))
@@ -560,7 +563,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     // that LLVM IR dialect CallOp has either 0 or 1 result.
     if (opInst.getNumResults() != 0)
       moduleTranslation.mapValue(opInst.getResult(0), call);
-      // Check that LLVM call returns void for 0-result functions.
+    // Check that LLVM call returns void for 0-result functions.
     else if (!call->getType()->isVoidTy())
       return failure();
     moduleTranslation.mapCall(callOp, call);
@@ -583,19 +586,19 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     auto ft = LLVM::LLVMFunctionType::get(resultType, operandTypes);
     llvm::InlineAsm *inlineAsmInst =
         inlineAsmOp.getAsmDialect()
-          ? llvm::InlineAsm::get(
-              static_cast<llvm::FunctionType *>(
-                moduleTranslation.convertType(ft)),
-              inlineAsmOp.getAsmString(), inlineAsmOp.getConstraints(),
-              inlineAsmOp.getHasSideEffects(),
-              inlineAsmOp.getIsAlignStack(),
-              convertAsmDialectToLLVM(*inlineAsmOp.getAsmDialect()))
-          : llvm::InlineAsm::get(static_cast<llvm::FunctionType *>(
-                                   moduleTranslation.convertType(ft)),
-                                 inlineAsmOp.getAsmString(),
-                                 inlineAsmOp.getConstraints(),
-                                 inlineAsmOp.getHasSideEffects(),
-                                 inlineAsmOp.getIsAlignStack());
+            ? llvm::InlineAsm::get(
+                  static_cast<llvm::FunctionType *>(
+                      moduleTranslation.convertType(ft)),
+                  inlineAsmOp.getAsmString(), inlineAsmOp.getConstraints(),
+                  inlineAsmOp.getHasSideEffects(),
+                  inlineAsmOp.getIsAlignStack(),
+                  convertAsmDialectToLLVM(*inlineAsmOp.getAsmDialect()))
+            : llvm::InlineAsm::get(static_cast<llvm::FunctionType *>(
+                                       moduleTranslation.convertType(ft)),
+                                   inlineAsmOp.getAsmString(),
+                                   inlineAsmOp.getConstraints(),
+                                   inlineAsmOp.getHasSideEffects(),
+                                   inlineAsmOp.getIsAlignStack());
     llvm::CallInst *inst = builder.CreateCall(
         inlineAsmInst,
         moduleTranslation.lookupValues(inlineAsmOp.getOperands()));
@@ -743,7 +746,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
 
     // The verifier should not have allowed this.
     assert((global || function || alias || ifunc) &&
-        "referencing an undefined global, function, alias, or ifunc");
+           "referencing an undefined global, function, alias, or ifunc");
 
     llvm::Value *llvmValue = nullptr;
     if (global)
@@ -762,7 +765,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
   // Emit dso_local_equivalent. We need to look up the global value referenced
   // by the operation and store it in the MLIR-to-LLVM value mapping.
   if (auto dsoLocalEquivalentOp =
-      dyn_cast<LLVM::DSOLocalEquivalentOp>(opInst)) {
+          dyn_cast<LLVM::DSOLocalEquivalentOp>(opInst)) {
     LLVM::LLVMFuncOp function =
         dsoLocalEquivalentOp.getFunction(moduleTranslation.symbolTable());
     LLVM::AliasOp alias =
@@ -770,7 +773,7 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
 
     // The verifier should not have allowed this.
     assert((function || alias) &&
-        "referencing an undefined function, or alias");
+           "referencing an undefined function, or alias");
 
     llvm::Value *llvmValue = nullptr;
     if (alias)
@@ -809,8 +812,8 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
           /*isConstant=*/true, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
           /*Initializer=*/nullptr,
           Twine("__mlir_block_address_")
-          .concat(Twine(fnName))
-          .concat(Twine((uint64_t)blockAddressOp.getOperation())));
+              .concat(Twine(fnName))
+              .concat(Twine((uint64_t)blockAddressOp.getOperation())));
       moduleTranslation.mapUnresolvedBlockAddress(blockAddressOp, llvmValue);
     }
 
diff --git a/mlir/python/mlir/dialects/llvm.py b/mlir/python/mlir/dialects/llvm.py
index 1fd7e64251e61..6ef2efc1dca04 100644
--- a/mlir/python/mlir/dialects/llvm.py
+++ b/mlir/python/mlir/dialects/llvm.py
@@ -6,7 +6,7 @@
 from ._llvm_ops_gen import _Dialect
 from ._llvm_enum_gen import *
 from .._mlir_libs._mlirDialectsLLVM import *
-from ..ir import Value
+from ..ir import Value, IntegerType, IntegerAttr
 from ._ods_common import get_op_result_or_op_results as _get_op_result_or_op_results
 
 
@@ -14,3 +14,18 @@ def mlir_constant(value, *, loc=None, ip=None) -> Value:
     return _get_op_result_or_op_results(
         ConstantOp(res=value.type, value=value, loc=loc, ip=ip)
     )
+
+
+def md_const(val, *, width=32, context=None):
+    """Create an MDConstantIntAttr wrapping an i<width> integer constant."""
+    if not isinstance(val, int):
+        raise NotImplementedError(
+            f"{val=} not supported; only integers currently supported."
+        )
+    i_type = IntegerType.get_signless(width, context=context)
+    return MDConstantAttr.get(IntegerAttr.get(i_type, val), context=context)
+
+
+def md_str(s, *, context=None):
+    """Create an MDStringAttr wrapping a string."""
+    return MDStringAttr.get(s, context=context)
diff --git a/mlir/test/python/dialects/llvm.py b/mlir/test/python/dialects/llvm.py
index b1d64f5fc27fe..52dd1109ab58f 100644
--- a/mlir/test/python/dialects/llvm.py
+++ b/mlir/test/python/dialects/llvm.py
@@ -250,6 +250,14 @@ def testMetadataAttrs():
     print(md_node)
     assert len(md_node) == 2
 
+    # MDNodeAttr - __getitem__
+    # CHECK: #llvm.md_const<42 : i32>
+    print(md_node[0])
+    # CHECK: #llvm.md_string<"foo.buffer">
+    print(md_node[1])
+    assert str(md_node[0]) == str(md_const)
+    assert str(md_node[1]) == str(md_str)
+
     # MDNodeAttr - nested
     md_nested = llvm.MDNodeAttr.get([md_node, md_empty])
     # CHECK: #llvm.md_node<[#llvm.md_node<[#llvm.md_const<42 : i32>, #llvm.md_string<"foo.buffer">]>, #llvm.md_node<[]>]>
@@ -260,14 +268,6 @@ def testMetadataAttrs():
 # CHECK-LABEL: testNamedMetadata
 @constructAndPrintInModule
 def testNamedMetadata():
-    i32 = IntegerType.get_signless(32)
-
-    def md_const(val):
-        return llvm.MDConstantAttr.get(IntegerAttr.get(i32, val))
-
-    def md_str(s):
-        return llvm.MDStringAttr.get(s)
-
     void = Type.parse("!llvm.void")
     func_ty = llvm.FunctionType.get(void, [])
 
@@ -277,7 +277,11 @@ def md_str(s):
     llvm.NamedMetadataOp(
         metadata_name="foo.version",
         nodes=ArrayAttr.get(
-            [llvm.MDNodeAttr.get([md_const(1), md_const(0), md_const(0)])]
+            [
+                llvm.MDNodeAttr.get(
+                    [llvm.md_const(1), llvm.md_const(0), llvm.md_const(0)]
+                )
+            ]
         ),
     )
     # CHECK: llvm.named_metadata "foo.version" [#llvm.md_node<[#llvm.md_const<1 : i32>, #llvm.md_const<0 : i32>, #llvm.md_const<0 : i32>]>]
@@ -287,7 +291,12 @@ def md_str(s):
         nodes=ArrayAttr.get(
             [
                 llvm.MDNodeAttr.get(
-                    [md_str("Bar"), md_const(1), md_const(2), md_const(3)]
+                    [
+                        llvm.md_str("Bar"),
+                        llvm.md_const(1),
+                        llvm.md_const(2),
+                        llvm.md_const(3),
+                    ]
                 )
             ]
         ),
@@ -296,18 +305,18 @@ def md_str(s):
 
     buf0 = llvm.MDNodeAttr.get(
         [
-            md_const(0),
-            md_str("foo.buffer"),
-            md_str("foo.idx"),
-            md_const(0),
-            md_const(1),
-            md_str("foo.read"),
-            md_str("foo.address_space"),
-            md_const(1),
-            md_str("foo.size"),
-            md_const(4),
-            md_str("foo.align_size"),
-            md_const(4),
+            llvm.md_const(0),
+            llvm.md_str("foo.buffer"),
+            llvm.md_str("foo.idx"),
+            llvm.md_const(0),
+            llvm.md_const(1),
+            llvm.md_str("foo.read"),
+            llvm.md_str("foo.address_space"),
+            llvm.md_const(1),
+            llvm.md_str("foo.size"),
+            llvm.md_const(4),
+            llvm.md_str("foo.align_size"),
+            llvm.md_const(4),
         ]
     )
 

>From c25d940634230b4abe5ddfac4cdae544903dec2c Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Sun, 15 Mar 2026 15:04:39 -0700
Subject: [PATCH 4/4] reduce lit test

---
 .../LLVMIR/LLVMToLLVMIRTranslation.cpp        |  1 -
 .../Target/LLVMIR/llvmir-named-metadata.mlir  | 80 +++----------------
 2 files changed, 11 insertions(+), 70 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 30f11db091836..e60a682b42ea1 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -430,7 +430,6 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
     builder.setFastMathFlags(getFastmathFlags(fmf));
 
 #include "mlir/Dialect/LLVMIR/LLVMConversions.inc"
-
 #include "mlir/Dialect/LLVMIR/LLVMIntrinsicConversions.inc"
 
   // Emit function calls.  If the "callee" attribute is present, this is a
diff --git a/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir b/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
index 144285e9bfebc..16b7162079583 100644
--- a/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
@@ -11,14 +11,19 @@ llvm.func @my_kernel() {
 }
 
 llvm.named_metadata "foo.version" [
-  #llvm.md_node<[#llvm.md_const<1 : i32>, #llvm.md_const<0 : i32>,
-                  #llvm.md_const<0 : i32>]>
+  #llvm.md_node<[#llvm.md_const<1 : i32>,
+                 #llvm.md_const<0 : i32>,
+                 #llvm.md_const<0 : i32>
+  ]>
 ]
 // CHECK-DAG: ![[VERSION]] = !{i32 1, i32 0, i32 0}
 
 llvm.named_metadata "foo.language_version" [
-  #llvm.md_node<[#llvm.md_string<"Bar">, #llvm.md_const<1 : i32>,
-                  #llvm.md_const<2 : i32>, #llvm.md_const<3 : i32>]>
+  #llvm.md_node<[#llvm.md_string<"Bar">,
+                 #llvm.md_const<1 : i32>,
+                 #llvm.md_const<2 : i32>,
+                 #llvm.md_const<3 : i32>
+  ]>
 ]
 // CHECK-DAG: ![[LANG]] = !{!"Bar", i32 1, i32 2, i32 3}
 
@@ -32,76 +37,13 @@ llvm.named_metadata "foo.language_version" [
 ]>
 // CHECK-DAG: ![[A0:[0-9]+]] = !{i32 0, !"foo.buffer", !"foo.idx", i32 0, i32 1, !"foo.read", !"foo.address_space", i32 1, !"foo.size", i32 4, !"foo.align_size", i32 4}
 
-#buf1 = #llvm.md_node<[
-  #llvm.md_const<1 : i32>, #llvm.md_string<"foo.buffer">,
-  #llvm.md_string<"foo.idx">, #llvm.md_const<1 : i32>,
-  #llvm.md_const<1 : i32>, #llvm.md_string<"foo.read_write">,
-  #llvm.md_string<"foo.address_space">, #llvm.md_const<1 : i32>,
-  #llvm.md_string<"foo.size">, #llvm.md_const<4 : i32>,
-  #llvm.md_string<"foo.align_size">, #llvm.md_const<4 : i32>
-]>
-// CHECK-DAG: ![[A1:[0-9]+]] = !{i32 1, !"foo.buffer", !"foo.idx", i32 1, i32 1, !"foo.read_write", !"foo.address_space", i32 1, !"foo.size", i32 4, !"foo.align_size", i32 4}
-
-#buf2 = #llvm.md_node<[
-  #llvm.md_const<2 : i32>, #llvm.md_string<"foo.buffer">,
-  #llvm.md_string<"foo.idx">, #llvm.md_const<2 : i32>,
-  #llvm.md_const<1 : i32>, #llvm.md_string<"foo.read">,
-  #llvm.md_string<"foo.address_space">, #llvm.md_const<2 : i32>,
-  #llvm.md_string<"foo.size">, #llvm.md_const<4 : i32>,
-  #llvm.md_string<"foo.align_size">, #llvm.md_const<4 : i32>
-]>
-// CHECK-DAG: ![[A2:[0-9]+]] = !{i32 2, !"foo.buffer", !"foo.idx", i32 2, i32 1, !"foo.read", !"foo.address_space", i32 2, !"foo.size", i32 4, !"foo.align_size", i32 4}
-
-#pos3 = #llvm.md_node<[
-  #llvm.md_const<3 : i32>, #llvm.md_string<"foo.block_position_in_grid">,
-  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
-  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"block_position_in_grid">
-]>
-// CHECK-DAG: ![[A3:[0-9]+]] = !{i32 3, !"foo.block_position_in_grid", !"foo.name", !"vec3", !"foo.arg_name", !"block_position_in_grid"}
-
-#pos4 = #llvm.md_node<[
-  #llvm.md_const<4 : i32>, #llvm.md_string<"foo.blocks_per_grid">,
-  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
-  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"blocks_per_grid">
-]>
-// CHECK-DAG: ![[A4:[0-9]+]] = !{i32 4, !"foo.blocks_per_grid", !"foo.name", !"vec3", !"foo.arg_name", !"blocks_per_grid"}
-
-#pos5 = #llvm.md_node<[
-  #llvm.md_const<5 : i32>, #llvm.md_string<"foo.thread_position_in_block">,
-  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
-  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"thread_position_in_block">
-]>
-// CHECK-DAG: ![[A5:[0-9]+]] = !{i32 5, !"foo.thread_position_in_block", !"foo.name", !"vec3", !"foo.arg_name", !"thread_position_in_block"}
-
-#pos6 = #llvm.md_node<[
-  #llvm.md_const<6 : i32>, #llvm.md_string<"foo.threads_per_block">,
-  #llvm.md_string<"foo.name">, #llvm.md_string<"vec3">,
-  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"threads_per_block">
-]>
-// CHECK-DAG: ![[A6:[0-9]+]] = !{i32 6, !"foo.threads_per_block", !"foo.name", !"vec3", !"foo.arg_name", !"threads_per_block"}
-
-#pos7 = #llvm.md_node<[
-  #llvm.md_const<7 : i32>, #llvm.md_string<"foo.thread_idx_in_warp">,
-  #llvm.md_string<"foo.name">, #llvm.md_string<"vec1">,
-  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"thread_idx_in_warp">
-]>
-// CHECK-DAG: ![[A7:[0-9]+]] = !{i32 7, !"foo.thread_idx_in_warp", !"foo.name", !"vec1", !"foo.arg_name", !"thread_idx_in_warp"}
-
-#pos8 = #llvm.md_node<[
-  #llvm.md_const<8 : i32>, #llvm.md_string<"foo.warp_idx_in_block">,
-  #llvm.md_string<"foo.name">, #llvm.md_string<"vec1">,
-  #llvm.md_string<"foo.arg_name">, #llvm.md_string<"warp_idx_in_block">
-]>
-// CHECK-DAG: ![[A8:[0-9]+]] = !{i32 8, !"foo.warp_idx_in_block", !"foo.name", !"vec1", !"foo.arg_name", !"warp_idx_in_block"}
-
 llvm.named_metadata "foo.kernel" [
   #llvm.md_node<[
     #llvm.md_func<@my_kernel>,
     #llvm.md_node<[]>,
-    #llvm.md_node<[#buf0, #buf1, #buf2,
-                    #pos3, #pos4, #pos5, #pos6, #pos7, #pos8]>
+    #llvm.md_node<[#buf0]>
   ]>
 ]
 // CHECK-DAG: ![[KERNEL]] = !{ptr @my_kernel, ![[EMPTY:[0-9]+]], ![[ARGS:[0-9]+]]}
 // CHECK-DAG: ![[EMPTY]] = !{}
-// CHECK-DAG: ![[ARGS]] = !{![[A0]], ![[A1]], ![[A2]], ![[A3]], ![[A4]], ![[A5]], ![[A6]], ![[A7]], ![[A8]]}
+// CHECK-DAG: ![[ARGS]] = !{![[A0]]}



More information about the Mlir-commits mailing list