[llvm-branch-commits] [mlir] [MLIR][LLVM] Translate LLVMFuncOp function metadata (PR #203021)
Akimasa Watanuki via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jun 12 09:06:37 PDT 2026
https://github.com/Men-cotton updated https://github.com/llvm/llvm-project/pull/203021
>From 98c27df0b5038a358b7a9f72d130d7113b8b92a8 Mon Sep 17 00:00:00 2001
From: mencotton <mencotton0410 at gmail.com>
Date: Mon, 8 Jun 2026 20:52:56 +0900
Subject: [PATCH] [MLIR][LLVM] Translate LLVMFuncOp function metadata
Materialize LLVMFuncOp function_metadata through ModuleTranslation metadata conversion. Attach function metadata after module-level symbols are mapped so metadata references to functions, globals, aliases, and ifuncs can be resolved.
---
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 53 +++++-
.../test/Target/LLVMIR/function-metadata.mlir | 156 ++++++++++++++++++
2 files changed, 204 insertions(+), 5 deletions(-)
create mode 100644 mlir/test/Target/LLVMIR/function-metadata.mlir
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 2193b415637a3..c56104dbb8d4b 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1617,6 +1617,45 @@ FailureOr<llvm::Metadata *> ModuleTranslation::convertMetadataAttr(
});
}
+/// Converts generic function metadata from `func` and attaches it to
+/// `llvmFunc`.
+static LogicalResult convertFunctionMetadata(ModuleTranslation &translation,
+ LLVMFuncOp func,
+ llvm::Function *llvmFunc) {
+ ArrayAttr metadata = func.getFunctionMetadataAttr();
+ if (!metadata)
+ return success();
+
+ for (auto entry : metadata.getAsRange<LLVM::FunctionMetadataAttr>()) {
+ StringRef metadataName = entry.getMetadataName().getValue();
+
+ FailureOr<llvm::Metadata *> md =
+ translation.convertMetadataAttr(entry.getNode(), [&]() {
+ return func.emitError()
+ << "failed to convert function_metadata entry '"
+ << metadataName << "': ";
+ });
+ if (failed(md))
+ return failure();
+ llvm::MDNode *node = llvm::dyn_cast_if_present<llvm::MDNode>(*md);
+ if (!node)
+ return func.emitError() << "failed to convert function_metadata entry '"
+ << metadataName << "'";
+ llvmFunc->addMetadata(metadataName, *node);
+ }
+ return success();
+}
+
+static LogicalResult convertFunctionMetadata(ModuleTranslation &translation,
+ Operation *module) {
+ for (auto function : getModuleBody(module).getOps<LLVMFuncOp>()) {
+ llvm::Function *llvmFunc = translation.lookupFunction(function.getName());
+ if (failed(convertFunctionMetadata(translation, function, llvmFunc)))
+ return failure();
+ }
+ return success();
+}
+
LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
// Clear the block, branch value mappings, they are only relevant within one
// function.
@@ -2036,18 +2075,20 @@ ModuleTranslation::convertParameterAttrs(Location loc,
LogicalResult ModuleTranslation::convertFunctionSignatures() {
// Declare all functions first because there may be function calls that form a
- // call graph with cycles, or global initializers that reference functions.
+ // call graph with cycles, global initializers that reference functions, or
+ // metadata that references functions declared later in the module.
for (auto function : getModuleBody(mlirModule).getOps<LLVMFuncOp>()) {
llvm::FunctionCallee llvmFuncCst = llvmModule->getOrInsertFunction(
function.getName(),
cast<llvm::FunctionType>(convertType(function.getFunctionType())));
llvm::Function *llvmFunc = cast<llvm::Function>(llvmFuncCst.getCallee());
+ mapFunction(function.getName(), llvmFunc);
+ }
+
+ for (auto function : getModuleBody(mlirModule).getOps<LLVMFuncOp>()) {
+ llvm::Function *llvmFunc = lookupFunction(function.getName());
llvmFunc->setLinkage(convertLinkageToLLVM(function.getLinkage()));
llvmFunc->setCallingConv(convertCConvToLLVM(function.getCConv()));
- mapFunction(function.getName(), llvmFunc);
- if (function.getFunctionMetadataAttr())
- return function.emitError()
- << "not yet implemented: translating function_metadata to LLVM IR";
addRuntimePreemptionSpecifier(function.getDsoLocal(), llvmFunc);
// Convert function attributes.
@@ -2620,6 +2661,8 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext,
return nullptr;
if (failed(translator.convertIFuncs()))
return nullptr;
+ if (failed(convertFunctionMetadata(translator, module)))
+ return nullptr;
if (failed(translator.createTBAAMetadata()))
return nullptr;
if (failed(translator.createIdentMetadata()))
diff --git a/mlir/test/Target/LLVMIR/function-metadata.mlir b/mlir/test/Target/LLVMIR/function-metadata.mlir
new file mode 100644
index 0000000000000..8816369b3d02a
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/function-metadata.mlir
@@ -0,0 +1,156 @@
+// RUN: mlir-translate -verify-diagnostics -split-input-file -mlir-to-llvmir %s | FileCheck %s
+
+// CHECK-LABEL: define void @function_metadata()
+// CHECK-SAME: !type ![[TYPE:[0-9]+]]
+// CHECK-SAME: !annotation ![[ANNOTATION:[0-9]+]]
+llvm.func @function_metadata() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"annotation", #llvm.md_node<
+ #llvm.md_string<"function annotation">
+ >>,
+ #llvm.func_metadata<"type", #llvm.md_node<
+ #llvm.md_const<0 : i64>,
+ #llvm.md_string<"typeid">
+ >>
+ ]
+} {
+ llvm.return
+}
+
+// CHECK-DAG: ![[ANNOTATION]] = !{!"function annotation"}
+// CHECK-DAG: ![[TYPE]] = !{i64 0, !"typeid"}
+
+// -----
+
+// CHECK-LABEL: declare !annotation
+// CHECK-SAME: ![[DECL_ANNOTATION:[0-9]+]] void @declaration_metadata()
+llvm.func @declaration_metadata() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"annotation", #llvm.md_node<
+ #llvm.md_string<"declaration annotation">
+ >>
+ ]
+}
+
+// CHECK-DAG: ![[DECL_ANNOTATION]] = !{!"declaration annotation"}
+
+// -----
+
+// CHECK-LABEL: define void @uses_later()
+// CHECK-SAME: !callee ![[LATER_NODE:[0-9]+]]
+llvm.func @uses_later() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"callee", #llvm.md_node<#llvm.md_string<"later">, #llvm.md_value<@later>>>
+ ]
+} {
+ llvm.return
+}
+
+llvm.func @later() {
+ llvm.return
+}
+
+// CHECK-DAG: ![[LATER_NODE]] = !{!"later", ptr @later}
+
+// -----
+
+llvm.mlir.global internal @metadata_global(0 : i32) : i32
+
+// CHECK-LABEL: define void @uses_global()
+// CHECK-SAME: !callee ![[GLOBAL_NODE:[0-9]+]]
+llvm.func @uses_global() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"callee", #llvm.md_node<#llvm.md_value<@metadata_global>>>
+ ]
+} {
+ llvm.return
+}
+
+// CHECK-DAG: ![[GLOBAL_NODE]] = !{ptr @metadata_global}
+
+// -----
+
+llvm.func @metadata_alias_target() {
+ llvm.return
+}
+
+llvm.mlir.alias external @metadata_alias : !llvm.func<void ()> {
+ %0 = llvm.mlir.addressof @metadata_alias_target : !llvm.ptr
+ llvm.return %0 : !llvm.ptr
+}
+
+// CHECK-LABEL: define void @uses_alias()
+// CHECK-SAME: !callee ![[ALIAS_NODE:[0-9]+]]
+llvm.func @uses_alias() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"callee", #llvm.md_node<#llvm.md_value<@metadata_alias>>>
+ ]
+} {
+ llvm.return
+}
+
+// CHECK-DAG: ![[ALIAS_NODE]] = !{ptr @metadata_alias}
+
+// -----
+
+llvm.mlir.ifunc external @metadata_ifunc : !llvm.func<void ()>, !llvm.ptr @metadata_ifunc_resolver
+
+llvm.func @metadata_ifunc_resolver() -> !llvm.ptr {
+ %0 = llvm.mlir.addressof @metadata_ifunc_target : !llvm.ptr
+ llvm.return %0 : !llvm.ptr
+}
+
+llvm.func @metadata_ifunc_target() {
+ llvm.return
+}
+
+// CHECK-LABEL: define void @uses_ifunc()
+// CHECK-SAME: !callee ![[IFUNC_NODE:[0-9]+]]
+llvm.func @uses_ifunc() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"callee", #llvm.md_node<#llvm.md_value<@metadata_ifunc>>>
+ ]
+} {
+ llvm.return
+}
+
+// CHECK-DAG: ![[IFUNC_NODE]] = !{ptr @metadata_ifunc}
+
+// -----
+
+// CHECK-LABEL: define void @repeated_kind_metadata()
+// CHECK-SAME: !type ![[TYPE0:[0-9]+]]
+// CHECK-SAME: !type ![[TYPE1:[0-9]+]]
+llvm.func @repeated_kind_metadata() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"type", #llvm.md_node<#llvm.md_const<0 : i64>, #llvm.md_string<"typeid0">>>,
+ #llvm.func_metadata<"type", #llvm.md_node<#llvm.md_const<0 : i64>, #llvm.md_string<"typeid1">>>
+ ]
+} {
+ llvm.return
+}
+
+// CHECK-DAG: ![[TYPE0]] = !{i64 0, !"typeid0"}
+// CHECK-DAG: ![[TYPE1]] = !{i64 0, !"typeid1"}
+
+// -----
+
+// expected-error @below{{failed to convert function_metadata entry 'callee': could not resolve metadata reference '@missing'}}
+llvm.func @missing_function_metadata_ref() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"callee", #llvm.md_node<#llvm.md_value<@missing>>>
+ ]
+} {
+ llvm.return
+}
+
+// -----
+
+// expected-error @below{{failed to convert function_metadata entry 'bad': expected integer attribute in metadata constant}}
+llvm.func @malformed_function_metadata() attributes {
+ function_metadata = [
+ #llvm.func_metadata<"bad", #llvm.md_node<#llvm.md_const<"not an integer">>>
+ ]
+} {
+ llvm.return
+}
More information about the llvm-branch-commits
mailing list