[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.
This commit is contained in:
mencotton 2026-06-08 20:51:57 +09:00
parent c938d3b85f
commit 396eb66627
6 changed files with 86 additions and 0 deletions

View file

@ -1810,6 +1810,28 @@ def LLVM_MDNodeArrayAttr
: TypedArrayAttrBase<LLVM_MDNodeAttr,
"array of #llvm.md_node attributes">;
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<LLVM_FunctionMetadataAttr,
"array of #llvm.func_metadata attributes">;
def LLVM_AnyMDAttr : AnyAttrOf<[
LLVM_MDStringAttr, LLVM_MDConstantAttr, LLVM_MDGlobalValueAttr,
LLVM_MDNodeAttr],

View file

@ -2077,6 +2077,7 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
OptionalAttr<DenseI32ArrayAttr>:$work_group_size_hint,
OptionalAttr<DenseI32ArrayAttr>:$reqd_work_group_size,
OptionalAttr<I32Attr>:$intel_reqd_sub_group_size,
OptionalAttr<LLVM_FunctionMetadataArrayAttr>:$function_metadata,
OptionalAttr<UWTableKindAttr>:$uwtable_kind,
OptionalAttr<BoolAttr>:$use_sample_profile
);

View file

@ -135,6 +135,24 @@ bool AddressSpaceAttr::isValidPtrIntCast(
return false;
}
//===----------------------------------------------------------------------===//
// FunctionMetadataAttr
//===----------------------------------------------------------------------===//
LogicalResult
FunctionMetadataAttr::verify(function_ref<InFlightDiagnostic()> 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
//===----------------------------------------------------------------------===//

View file

@ -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.

View file

@ -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">]

View file

@ -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">>>
]
}