From 396eb66627f4f586cfb0a5d7ce77e124e145b872 Mon Sep 17 00:00:00 2001 From: mencotton Date: Mon, 8 Jun 2026 20:51:57 +0900 Subject: [PATCH] [MLIR][LLVM] Add function metadata to LLVMFuncOp Add a generic LLVM dialect carrier for LLVM IR function metadata on LLVMFuncOp. Represent attachments as an ordered list so repeated metadata kinds, such as multiple type metadata attachments, can be preserved while keeping metadata names language-agnostic. --- .../mlir/Dialect/LLVMIR/LLVMAttrDefs.td | 22 +++++++++++++++++++ mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 1 + mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp | 18 +++++++++++++++ mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 3 +++ mlir/test/Dialect/LLVMIR/invalid.mlir | 20 +++++++++++++++++ mlir/test/Dialect/LLVMIR/roundtrip.mlir | 22 +++++++++++++++++++ 6 files changed, 86 insertions(+) diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td index 55bbf2a02f70..29440ea351f4 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td @@ -1810,6 +1810,28 @@ def LLVM_MDNodeArrayAttr : TypedArrayAttrBase; +def LLVM_FunctionMetadataAttr + : LLVM_Attr<"FunctionMetadata", "func_metadata"> { + let summary = "LLVM function metadata attachment"; + let description = [{ + Models one LLVM IR function metadata attachment. LLVM IR allows several + attachments with the same metadata kind, so function metadata is represented + as an ordered list of these entries instead of a dictionary. + + Example: + ```mlir + #llvm.func_metadata<"type", <#llvm.md_string<"id">>> + ``` + }]; + let parameters = (ins "StringAttr":$metadataName, "MDNodeAttr":$node); + let assemblyFormat = "`<` $metadataName `,` $node `>`"; + let genVerifyDecl = 1; +} + +def LLVM_FunctionMetadataArrayAttr + : TypedArrayAttrBase; + def LLVM_AnyMDAttr : AnyAttrOf<[ LLVM_MDStringAttr, LLVM_MDConstantAttr, LLVM_MDGlobalValueAttr, LLVM_MDNodeAttr], diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index 84f71054994f..e7d17e1ef1f2 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -2077,6 +2077,7 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [ OptionalAttr:$work_group_size_hint, OptionalAttr:$reqd_work_group_size, OptionalAttr:$intel_reqd_sub_group_size, + OptionalAttr:$function_metadata, OptionalAttr:$uwtable_kind, OptionalAttr:$use_sample_profile ); diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp index 266faeca4553..bb655c749db4 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp @@ -135,6 +135,24 @@ bool AddressSpaceAttr::isValidPtrIntCast( return false; } +//===----------------------------------------------------------------------===// +// FunctionMetadataAttr +//===----------------------------------------------------------------------===// + +LogicalResult +FunctionMetadataAttr::verify(function_ref emitError, + StringAttr metadataName, MDNodeAttr node) { + (void)node; + StringRef name = metadataName.getValue(); + if (name.empty()) + return emitError() << "function_metadata entry name must not be empty"; + if (name == "dbg" || name == "prof") { + return emitError() << "reserved function_metadata entry '" << name + << "' must use a dedicated LLVM dialect representation"; + } + return success(); +} + //===----------------------------------------------------------------------===// // AliasScopeAttr //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index 47e63e2aa279..01c96ded535b 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -2063,6 +2063,9 @@ LogicalResult ModuleTranslation::convertFunctionSignatures() { 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. diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir index bde7807dadb8..28d6405582fb 100644 --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -1847,6 +1847,26 @@ llvm.mlir.alias external @y5 : i32 { // ----- +// expected-error@+1{{attribute 'function_metadata' failed to satisfy constraint: array of #llvm.func_metadata attributes}} +llvm.func @function_metadata_value() attributes {function_metadata = [#llvm.md_string<"int">]} + +// ----- + +// expected-error@+1{{function_metadata entry name must not be empty}} +llvm.func @empty_function_metadata_name() attributes {function_metadata = [#llvm.func_metadata<"", #llvm.md_node<#llvm.md_string<"x">>>]} + +// ----- + +// expected-error@+1{{reserved function_metadata entry 'dbg' must use a dedicated LLVM dialect representation}} +llvm.func @reserved_dbg_function_metadata() attributes {function_metadata = [#llvm.func_metadata<"dbg", #llvm.md_node<#llvm.md_string<"x">>>]} + +// ----- + +// expected-error@+1{{reserved function_metadata entry 'prof' must use a dedicated LLVM dialect representation}} +llvm.func @reserved_prof_function_metadata() attributes {function_metadata = [#llvm.func_metadata<"prof", #llvm.md_node<#llvm.md_string<"function_entry_count">, #llvm.md_const<1 : i64>>>]} + +// ----- + // expected-error@+1{{attribute 'nodes' failed to satisfy constraint: array of #llvm.md_node attributes}} llvm.named_metadata "not_node" [#llvm.md_string<"int">] diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir index d1766052964b..7a1c4ee95143 100644 --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -1244,3 +1244,25 @@ llvm.named_metadata "foo.kernel" [ > > ] + +// CHECK-LABEL: llvm.func @generic_function_metadata +// CHECK-SAME: function_metadata +// CHECK-SAME: #llvm.func_metadata<"annotation", <#llvm.md_string<"function annotation">>> +// CHECK-SAME: #llvm.func_metadata<"type", <#llvm.md_const<0 : i64>, #llvm.md_string<"typeid">>> +llvm.func @generic_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">>> + ] +} + +// CHECK-LABEL: llvm.func @repeated_function_metadata +// CHECK-SAME: function_metadata +// CHECK-SAME: #llvm.func_metadata<"type", <#llvm.md_const<0 : i64>, #llvm.md_string<"typeid0">>> +// CHECK-SAME: #llvm.func_metadata<"type", <#llvm.md_const<0 : i64>, #llvm.md_string<"typeid1">>> +llvm.func @repeated_function_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">>> + ] +}