[Mlir-commits] [mlir] [mlir][llvm] Preserve function entry count metadata (PR #204707)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Jun 18 18:27:08 PDT 2026
https://github.com/Kuhai9801 updated https://github.com/llvm/llvm-project/pull/204707
>From eed46d68d03c17d16157aa62d27772a5d4bb6e73 Mon Sep 17 00:00:00 2001
From: "Cyne Jarvis J. Zarceno" <cynejarviszarceno at gmail.com>
Date: Fri, 19 Jun 2026 08:09:20 +0800
Subject: [PATCH 1/4] [mlir][llvm] Preserve function entry count metadata
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 2 +
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 9 ++++
.../LLVMIR/LLVMIRToLLVMTranslation.cpp | 28 ++++++++++--
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 20 +++++++--
mlir/test/Dialect/LLVMIR/invalid.mlir | 10 +++++
.../LLVMIR/Import/function-attributes.ll | 45 +++++++++++++++++++
mlir/test/Target/LLVMIR/llvmir.mlir | 30 +++++++++++++
7 files changed, 137 insertions(+), 7 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 9d112e5ea227e..4dad2da58b1c0 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -2035,6 +2035,8 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs,
OptionalAttr<I64Attr>:$function_entry_count,
+ UnitAttr:$function_entry_count_synthetic,
+ OptionalAttr<DenseI64ArrayAttr>:$function_entry_count_imports,
OptionalAttr<LLVM_MemoryEffectsAttr>:$memory_effects,
DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_,
UnitAttr:$arm_streaming, UnitAttr:$arm_locally_streaming,
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 58f569abff8ea..725c12386b2f2 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3266,6 +3266,15 @@ LogicalResult LLVMFuncOp::verify() {
if (failed(verifyComdat(*this, getComdat())))
return failure();
+ if (!getFunctionEntryCount()) {
+ if (getFunctionEntryCountSynthetic())
+ return emitOpError() << "requires function_entry_count when "
+ "function_entry_count_synthetic is set";
+ if (getFunctionEntryCountImportsAttr())
+ return emitOpError() << "requires function_entry_count when "
+ "function_entry_count_imports is set";
+ }
+
if (isExternal()) {
if (getLinkage() != LLVM::Linkage::External &&
getLinkage() != LLVM::Linkage::ExternWeak)
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index e9cd335835263..3d3e6677b7027 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -114,10 +114,9 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
return failure();
// Handle function entry count metadata.
- if (name->getString() == llvm::MDProfLabels::FunctionEntryCount) {
-
- // TODO support function entry count metadata with GUID fields.
- if (node->getNumOperands() != 2)
+ if (name->getString() == llvm::MDProfLabels::FunctionEntryCount ||
+ name->getString() == llvm::MDProfLabels::SyntheticFunctionEntryCount) {
+ if (node->getNumOperands() < 2)
return failure();
llvm::ConstantInt *entryCount =
@@ -125,7 +124,28 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
if (!entryCount)
return failure();
if (auto funcOp = dyn_cast<LLVMFuncOp>(op)) {
+ bool isSynthetic =
+ name->getString() == llvm::MDProfLabels::SyntheticFunctionEntryCount;
+ SmallVector<int64_t> importGUIDs;
+ if (node->getNumOperands() > 2) {
+ importGUIDs.reserve(node->getNumOperands() - 2);
+ for (unsigned idx = 2, e = node->getNumOperands(); idx < e; ++idx) {
+ llvm::ConstantInt *guid =
+ llvm::mdconst::dyn_extract<llvm::ConstantInt>(
+ node->getOperand(idx));
+ if (!guid)
+ return failure();
+ importGUIDs.push_back(
+ static_cast<int64_t>(guid->getValue().getZExtValue()));
+ }
+ }
+
funcOp.setFunctionEntryCount(entryCount->getZExtValue());
+ if (isSynthetic)
+ funcOp.setFunctionEntryCountSynthetic(true);
+ if (!importGUIDs.empty())
+ funcOp.setFunctionEntryCountImportsAttr(
+ DenseI64ArrayAttr::get(builder.getContext(), importGUIDs));
return success();
}
return op->emitWarning()
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 442e6e16e955e..b3bcec22e7703 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -31,6 +31,7 @@
#include "mlir/Target/LLVMIR/LLVMTranslationInterface.h"
#include "mlir/Target/LLVMIR/TypeToLLVM.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/TypeSwitch.h"
@@ -2003,9 +2004,22 @@ LogicalResult ModuleTranslation::convertFunctionSignatures() {
// Convert function kernel attributes to metadata.
convertFunctionKernelAttributes(function, llvmFunc, *this);
- // Convert function_entry_count attribute to metadata.
- if (std::optional<uint64_t> entryCount = function.getFunctionEntryCount())
- llvmFunc->setEntryCount(entryCount.value());
+ // Convert function_entry_count attributes to metadata.
+ if (std::optional<uint64_t> entryCount = function.getFunctionEntryCount()) {
+ llvm::Function::ProfileCount profileCount(
+ entryCount.value(), function.getFunctionEntryCountSynthetic()
+ ? llvm::Function::PCT_Synthetic
+ : llvm::Function::PCT_Real);
+ std::optional<llvm::DenseSet<llvm::GlobalValue::GUID>> importGUIDs;
+ if (DenseI64ArrayAttr imports =
+ function.getFunctionEntryCountImportsAttr()) {
+ importGUIDs.emplace();
+ for (int64_t guid : imports.asArrayRef())
+ importGUIDs->insert(static_cast<uint64_t>(guid));
+ }
+ llvmFunc->setEntryCount(profileCount,
+ importGUIDs ? &*importGUIDs : nullptr);
+ }
// Convert result attributes.
if (ArrayAttr allResultAttrs = function.getAllResultAttrs()) {
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index d5ea5c8de862e..0a0ae6dd5bc0a 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -44,6 +44,16 @@ llvm.mlir.global_dtors dtors = [@dtor], priorities = [0 : i32], data = [0 : i32]
////////////////////////////////////////////////////////////////////////////////
+// expected-error at +1{{requires function_entry_count when function_entry_count_synthetic is set}}
+llvm.func @function_entry_count_synthetic_requires_count() attributes {function_entry_count_synthetic}
+
+// -----
+
+// expected-error at +1{{requires function_entry_count when function_entry_count_imports is set}}
+llvm.func @function_entry_count_imports_requires_count() attributes {function_entry_count_imports = array<i64: 1234>}
+
+// -----
+
// Check that parser errors are properly produced and do not crash the compiler.
// -----
diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index 5d664519e100c..596fcb0801644 100644
--- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll
+++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
@@ -180,6 +180,51 @@ define void @entry_count() !prof !1 {
; // -----
+; CHECK-LABEL: @synthetic_entry_count
+; CHECK-SAME: attributes {function_entry_count = 7 : i64
+; CHECK-SAME: function_entry_count_synthetic
+define void @synthetic_entry_count() !prof !2 {
+ ret void
+}
+
+!2 = !{!"synthetic_function_entry_count", i64 7}
+
+; // -----
+
+; CHECK-LABEL: @entry_count_imports
+; CHECK-SAME: attributes {function_entry_count = 7 : i64
+; CHECK-SAME: function_entry_count_imports = array<i64: 1234>
+define void @entry_count_imports() !prof !3 {
+ ret void
+}
+
+!3 = !{!"function_entry_count", i64 7, i64 1234}
+
+; // -----
+
+; CHECK-LABEL: @synthetic_entry_count_imports
+; CHECK-SAME: attributes {function_entry_count = 7 : i64
+; CHECK-SAME: function_entry_count_imports = array<i64: 1234>
+; CHECK-SAME: function_entry_count_synthetic
+define void @synthetic_entry_count_imports() !prof !4 {
+ ret void
+}
+
+!4 = !{!"synthetic_function_entry_count", i64 7, i64 1234}
+
+; // -----
+
+; CHECK-LABEL: @entry_count_malformed_import
+; CHECK-NOT: function_entry_count
+; expected-warning @below {{unhandled function metadata}}
+define void @entry_count_malformed_import() !prof !5 {
+ ret void
+}
+
+!5 = !{!"function_entry_count", i64 7, !"bad"}
+
+; // -----
+
; CHECK-LABEL: @func_memory
; CHECK-SAME: attributes {memory_effects = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite, errnoMem = readwrite, targetMem0 = readwrite, targetMem1 = readwrite>}
; CHECK: llvm.return
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 5eca8f19154a1..2fd1b879b7051 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -1902,6 +1902,36 @@ llvm.func @functionEntryCount() attributes {function_entry_count = 4242 : i64} {
// -----
+// CHECK-LABEL: @syntheticFunctionEntryCount
+// CHECK-SAME: !prof ![[SYNTH_PROF_ID:[0-9]*]]
+llvm.func @syntheticFunctionEntryCount() attributes {function_entry_count = 7 : i64, function_entry_count_synthetic} {
+ llvm.return
+}
+
+// CHECK: ![[SYNTH_PROF_ID]] = !{!"synthetic_function_entry_count", i64 7}
+
+// -----
+
+// CHECK-LABEL: @functionEntryCountWithImports
+// CHECK-SAME: !prof ![[IMPORTS_PROF_ID:[0-9]*]]
+llvm.func @functionEntryCountWithImports() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 1234>} {
+ llvm.return
+}
+
+// CHECK: ![[IMPORTS_PROF_ID]] = !{!"function_entry_count", i64 7, i64 1234}
+
+// -----
+
+// CHECK-LABEL: @syntheticFunctionEntryCountWithImports
+// CHECK-SAME: !prof ![[SYNTH_IMPORTS_PROF_ID:[0-9]*]]
+llvm.func @syntheticFunctionEntryCountWithImports() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 1234>, function_entry_count_synthetic} {
+ llvm.return
+}
+
+// CHECK: ![[SYNTH_IMPORTS_PROF_ID]] = !{!"synthetic_function_entry_count", i64 7, i64 1234}
+
+// -----
+
// CHECK-LABEL: @constant_bf16
llvm.func @constant_bf16() -> bf16 {
%0 = llvm.mlir.constant(1.000000e+01 : bf16) : bf16
>From 67e05a19a86604a117ee0277d4dfbb94942e5e95 Mon Sep 17 00:00:00 2001
From: "Cyne Jarvis J. Zarceno" <cynejarviszarceno at gmail.com>
Date: Fri, 19 Jun 2026 08:29:01 +0800
Subject: [PATCH 2/4] Guard function entry count metadata import
---
.../Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp | 13 ++++++++++---
.../Target/LLVMIR/Import/function-attributes.ll | 15 +++++++++++++--
mlir/test/Target/LLVMIR/llvmir.mlir | 4 ++--
3 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index 3d3e6677b7027..e9dab234bc852 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -23,6 +23,7 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/MemoryModelRelaxationAnnotations.h"
+#include <optional>
using namespace mlir;
using namespace mlir::LLVM;
@@ -123,6 +124,10 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(1));
if (!entryCount)
return failure();
+ std::optional<uint64_t> entryCountValue =
+ entryCount->getValue().tryZExtValue();
+ if (!entryCountValue)
+ return failure();
if (auto funcOp = dyn_cast<LLVMFuncOp>(op)) {
bool isSynthetic =
name->getString() == llvm::MDProfLabels::SyntheticFunctionEntryCount;
@@ -135,12 +140,14 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
node->getOperand(idx));
if (!guid)
return failure();
- importGUIDs.push_back(
- static_cast<int64_t>(guid->getValue().getZExtValue()));
+ std::optional<uint64_t> guidValue = guid->getValue().tryZExtValue();
+ if (!guidValue)
+ return failure();
+ importGUIDs.push_back(static_cast<int64_t>(*guidValue));
}
}
- funcOp.setFunctionEntryCount(entryCount->getZExtValue());
+ funcOp.setFunctionEntryCount(*entryCountValue);
if (isSynthetic)
funcOp.setFunctionEntryCountSynthetic(true);
if (!importGUIDs.empty())
diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index 596fcb0801644..0563758dda77d 100644
--- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll
+++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
@@ -193,12 +193,12 @@ define void @synthetic_entry_count() !prof !2 {
; CHECK-LABEL: @entry_count_imports
; CHECK-SAME: attributes {function_entry_count = 7 : i64
-; CHECK-SAME: function_entry_count_imports = array<i64: 1234>
+; CHECK-SAME: function_entry_count_imports = array<i64: 1234, -1>
define void @entry_count_imports() !prof !3 {
ret void
}
-!3 = !{!"function_entry_count", i64 7, i64 1234}
+!3 = !{!"function_entry_count", i64 7, i64 1234, i64 -1}
; // -----
@@ -225,6 +225,17 @@ define void @entry_count_malformed_import() !prof !5 {
; // -----
+; CHECK-LABEL: @entry_count_wide_import
+; CHECK-NOT: function_entry_count
+; expected-warning @below {{unhandled function metadata}}
+define void @entry_count_wide_import() !prof !6 {
+ ret void
+}
+
+!6 = !{!"function_entry_count", i64 7, i128 18446744073709551616}
+
+; // -----
+
; CHECK-LABEL: @func_memory
; CHECK-SAME: attributes {memory_effects = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite, errnoMem = readwrite, targetMem0 = readwrite, targetMem1 = readwrite>}
; CHECK: llvm.return
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 2fd1b879b7051..6cc633c3e34f2 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -1914,11 +1914,11 @@ llvm.func @syntheticFunctionEntryCount() attributes {function_entry_count = 7 :
// CHECK-LABEL: @functionEntryCountWithImports
// CHECK-SAME: !prof ![[IMPORTS_PROF_ID:[0-9]*]]
-llvm.func @functionEntryCountWithImports() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 1234>} {
+llvm.func @functionEntryCountWithImports() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 1234, -1>} {
llvm.return
}
-// CHECK: ![[IMPORTS_PROF_ID]] = !{!"function_entry_count", i64 7, i64 1234}
+// CHECK: ![[IMPORTS_PROF_ID]] = !{!"function_entry_count", i64 7, i64 1234, i64 -1}
// -----
>From 65f4cce3a45d713120787b980176846614c567a1 Mon Sep 17 00:00:00 2001
From: "Cyne Jarvis J. Zarceno" <cynejarviszarceno at gmail.com>
Date: Fri, 19 Jun 2026 08:56:50 +0800
Subject: [PATCH 3/4] Polish function entry count metadata handling
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 8 +++++
.../LLVMIR/LLVMIRToLLVMTranslation.cpp | 33 ++++++++++---------
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 5 ++-
.../LLVMIR/Import/function-attributes.ll | 15 +++++++--
4 files changed, 43 insertions(+), 18 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 4dad2da58b1c0..554e11733a2a3 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -2018,6 +2018,14 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
llvm.return
}
```
+
+ The `function_entry_count` attribute models function-level `!prof`
+ entry-count metadata. The `function_entry_count_synthetic` unit attribute
+ selects the `"synthetic_function_entry_count"` metadata label, and
+ `function_entry_count_imports` preserves trailing import GUID operands.
+ Those GUID operands are unsigned 64-bit LLVM GUID bit patterns stored in a
+ signed i64 array attribute, and are preserved for both real and synthetic
+ entry-count metadata.
}];
let arguments = (ins
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index e9dab234bc852..6fc72352f119f 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -113,36 +113,39 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
auto *name = dyn_cast<llvm::MDString>(node->getOperand(0));
if (!name)
return failure();
+ StringRef profName = name->getString();
// Handle function entry count metadata.
- if (name->getString() == llvm::MDProfLabels::FunctionEntryCount ||
- name->getString() == llvm::MDProfLabels::SyntheticFunctionEntryCount) {
+ if (profName == llvm::MDProfLabels::FunctionEntryCount ||
+ profName == llvm::MDProfLabels::SyntheticFunctionEntryCount) {
if (node->getNumOperands() < 2)
return failure();
- llvm::ConstantInt *entryCount =
- llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(1));
- if (!entryCount)
- return failure();
+ auto getUInt64Metadata =
+ [](llvm::Metadata *metadata) -> std::optional<uint64_t> {
+ auto *constant = llvm::mdconst::dyn_extract<llvm::ConstantInt>(metadata);
+ if (!constant)
+ return std::nullopt;
+ return constant->getValue().tryZExtValue();
+ };
+
std::optional<uint64_t> entryCountValue =
- entryCount->getValue().tryZExtValue();
+ getUInt64Metadata(node->getOperand(1));
if (!entryCountValue)
return failure();
if (auto funcOp = dyn_cast<LLVMFuncOp>(op)) {
bool isSynthetic =
- name->getString() == llvm::MDProfLabels::SyntheticFunctionEntryCount;
+ profName == llvm::MDProfLabels::SyntheticFunctionEntryCount;
SmallVector<int64_t> importGUIDs;
if (node->getNumOperands() > 2) {
importGUIDs.reserve(node->getNumOperands() - 2);
for (unsigned idx = 2, e = node->getNumOperands(); idx < e; ++idx) {
- llvm::ConstantInt *guid =
- llvm::mdconst::dyn_extract<llvm::ConstantInt>(
- node->getOperand(idx));
- if (!guid)
- return failure();
- std::optional<uint64_t> guidValue = guid->getValue().tryZExtValue();
+ std::optional<uint64_t> guidValue =
+ getUInt64Metadata(node->getOperand(idx));
if (!guidValue)
return failure();
+ // Import GUIDs are unsigned 64-bit values in LLVM IR. Store the same
+ // bit pattern in MLIR's signed i64 array attribute.
importGUIDs.push_back(static_cast<int64_t>(*guidValue));
}
}
@@ -159,7 +162,7 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
<< "expected function_entry_count to be attached to a function";
}
- if (name->getString() != llvm::MDProfLabels::BranchWeights)
+ if (profName != llvm::MDProfLabels::BranchWeights)
return failure();
// The branch_weights metadata must have at least 2 operands.
if (node->getNumOperands() < 2)
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index b3bcec22e7703..1dbed59dae278 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -2014,8 +2014,11 @@ LogicalResult ModuleTranslation::convertFunctionSignatures() {
if (DenseI64ArrayAttr imports =
function.getFunctionEntryCountImportsAttr()) {
importGUIDs.emplace();
- for (int64_t guid : imports.asArrayRef())
+ for (int64_t guid : imports.asArrayRef()) {
+ // The MLIR attribute preserves the unsigned GUID bit pattern in a
+ // signed i64 element.
importGUIDs->insert(static_cast<uint64_t>(guid));
+ }
}
llvmFunc->setEntryCount(profileCount,
importGUIDs ? &*importGUIDs : nullptr);
diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index 0563758dda77d..ad7642f20cd02 100644
--- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll
+++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
@@ -225,14 +225,25 @@ define void @entry_count_malformed_import() !prof !5 {
; // -----
+; CHECK-LABEL: @entry_count_wide_count
+; CHECK-NOT: function_entry_count
+; expected-warning @below {{unhandled function metadata}}
+define void @entry_count_wide_count() !prof !6 {
+ ret void
+}
+
+!6 = !{!"function_entry_count", i128 18446744073709551616}
+
+; // -----
+
; CHECK-LABEL: @entry_count_wide_import
; CHECK-NOT: function_entry_count
; expected-warning @below {{unhandled function metadata}}
-define void @entry_count_wide_import() !prof !6 {
+define void @entry_count_wide_import() !prof !7 {
ret void
}
-!6 = !{!"function_entry_count", i64 7, i128 18446744073709551616}
+!7 = !{!"function_entry_count", i64 7, i128 18446744073709551616}
; // -----
>From 1dcdbb8d11c93948eb240d1cd6493da60975322a Mon Sep 17 00:00:00 2001
From: "Cyne Jarvis J. Zarceno" <cynejarviszarceno at gmail.com>
Date: Fri, 19 Jun 2026 09:26:43 +0800
Subject: [PATCH 4/4] [mlir][llvm] Tighten function entry count import
semantics
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 8 +++---
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 17 +++++++++++++
.../LLVMIR/LLVMIRToLLVMTranslation.cpp | 16 ++++++++++--
mlir/test/Dialect/LLVMIR/invalid.mlir | 25 +++++++++++++++++++
.../LLVMIR/Import/function-attributes.ll | 19 ++++++++++----
.../Import/function-entry-count-roundtrip.ll | 19 ++++++++++++++
mlir/test/Target/LLVMIR/llvmir.mlir | 8 +++---
7 files changed, 97 insertions(+), 15 deletions(-)
create mode 100644 mlir/test/Target/LLVMIR/Import/function-entry-count-roundtrip.ll
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 554e11733a2a3..d9650e3b6b54e 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -2022,10 +2022,10 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
The `function_entry_count` attribute models function-level `!prof`
entry-count metadata. The `function_entry_count_synthetic` unit attribute
selects the `"synthetic_function_entry_count"` metadata label, and
- `function_entry_count_imports` preserves trailing import GUID operands.
- Those GUID operands are unsigned 64-bit LLVM GUID bit patterns stored in a
- signed i64 array attribute, and are preserved for both real and synthetic
- entry-count metadata.
+ `function_entry_count_imports` represents the set of trailing import GUID
+ operands on real `"function_entry_count"` metadata. Those GUID operands are
+ unsigned 64-bit LLVM GUID bit patterns stored in a signed i64 array
+ attribute in unsigned sorted-unique order.
}];
let arguments = (ins
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 725c12386b2f2..3866abf693477 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -26,6 +26,7 @@
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/Support/Error.h"
@@ -3274,6 +3275,22 @@ LogicalResult LLVMFuncOp::verify() {
return emitOpError() << "requires function_entry_count when "
"function_entry_count_imports is set";
}
+ if (DenseI64ArrayAttr imports = getFunctionEntryCountImportsAttr()) {
+ if (getFunctionEntryCountSynthetic())
+ return emitOpError() << "does not support function_entry_count_imports "
+ "with function_entry_count_synthetic";
+ ArrayRef<int64_t> importGUIDs = imports.asArrayRef();
+ if (importGUIDs.empty())
+ return emitOpError() << "requires function_entry_count_imports to be "
+ "non-empty when set";
+ for (auto [previous, current] :
+ llvm::zip_equal(importGUIDs.drop_back(), importGUIDs.drop_front())) {
+ if (static_cast<uint64_t>(previous) >= static_cast<uint64_t>(current))
+ return emitOpError()
+ << "requires function_entry_count_imports to be sorted and "
+ "unique by unsigned GUID value";
+ }
+ }
if (isExternal()) {
if (getLinkage() != LLVM::Linkage::External &&
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index 6fc72352f119f..518e1ec278849 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -17,6 +17,7 @@
#include "mlir/Support/LLVM.h"
#include "mlir/Target/LLVMIR/ModuleImport.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/InlineAsm.h"
@@ -136,17 +137,28 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
if (auto funcOp = dyn_cast<LLVMFuncOp>(op)) {
bool isSynthetic =
profName == llvm::MDProfLabels::SyntheticFunctionEntryCount;
+ if (isSynthetic && node->getNumOperands() > 2)
+ return failure();
+
+ SmallVector<uint64_t> importGUIDValues;
SmallVector<int64_t> importGUIDs;
if (node->getNumOperands() > 2) {
- importGUIDs.reserve(node->getNumOperands() - 2);
+ importGUIDValues.reserve(node->getNumOperands() - 2);
for (unsigned idx = 2, e = node->getNumOperands(); idx < e; ++idx) {
std::optional<uint64_t> guidValue =
getUInt64Metadata(node->getOperand(idx));
if (!guidValue)
return failure();
+ importGUIDValues.push_back(*guidValue);
+ }
+ llvm::sort(importGUIDValues);
+ for (uint64_t guidValue : importGUIDValues) {
+ if (!importGUIDs.empty() &&
+ static_cast<uint64_t>(importGUIDs.back()) == guidValue)
+ continue;
// Import GUIDs are unsigned 64-bit values in LLVM IR. Store the same
// bit pattern in MLIR's signed i64 array attribute.
- importGUIDs.push_back(static_cast<int64_t>(*guidValue));
+ importGUIDs.push_back(static_cast<int64_t>(guidValue));
}
}
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index 0a0ae6dd5bc0a..5b78b7b2cdb43 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -54,6 +54,31 @@ llvm.func @function_entry_count_imports_requires_count() attributes {function_en
// -----
+// expected-error at +1{{does not support function_entry_count_imports with function_entry_count_synthetic}}
+llvm.func @function_entry_count_imports_requires_real_count() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 1234>, function_entry_count_synthetic}
+
+// -----
+
+// expected-error at +1{{requires function_entry_count_imports to be non-empty when set}}
+llvm.func @function_entry_count_imports_non_empty() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64>}
+
+// -----
+
+// expected-error at +1{{requires function_entry_count_imports to be sorted and unique by unsigned GUID value}}
+llvm.func @function_entry_count_imports_sorted() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 9, 4>}
+
+// -----
+
+// expected-error at +1{{requires function_entry_count_imports to be sorted and unique by unsigned GUID value}}
+llvm.func @function_entry_count_imports_unique() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 4, 4>}
+
+// -----
+
+// expected-error at +1{{requires function_entry_count_imports to be sorted and unique by unsigned GUID value}}
+llvm.func @function_entry_count_imports_unsigned_order() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: -1, 4>}
+
+// -----
+
// Check that parser errors are properly produced and do not crash the compiler.
// -----
diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index ad7642f20cd02..c9b6bb585d76c 100644
--- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll
+++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
@@ -193,19 +193,18 @@ define void @synthetic_entry_count() !prof !2 {
; CHECK-LABEL: @entry_count_imports
; CHECK-SAME: attributes {function_entry_count = 7 : i64
-; CHECK-SAME: function_entry_count_imports = array<i64: 1234, -1>
+; CHECK-SAME: function_entry_count_imports = array<i64: 4, 1234, -1>
define void @entry_count_imports() !prof !3 {
ret void
}
-!3 = !{!"function_entry_count", i64 7, i64 1234, i64 -1}
+!3 = !{!"function_entry_count", i64 7, i64 1234, i64 -1, i64 4, i64 1234}
; // -----
; CHECK-LABEL: @synthetic_entry_count_imports
-; CHECK-SAME: attributes {function_entry_count = 7 : i64
-; CHECK-SAME: function_entry_count_imports = array<i64: 1234>
-; CHECK-SAME: function_entry_count_synthetic
+; CHECK-NOT: function_entry_count
+; expected-warning @below {{unhandled function metadata}}
define void @synthetic_entry_count_imports() !prof !4 {
ret void
}
@@ -247,6 +246,16 @@ define void @entry_count_wide_import() !prof !7 {
; // -----
+; CHECK-LABEL: @entry_count_negative_count
+; CHECK-SAME: attributes {function_entry_count = -1 : i64}
+define void @entry_count_negative_count() !prof !8 {
+ ret void
+}
+
+!8 = !{!"function_entry_count", i64 -1}
+
+; // -----
+
; CHECK-LABEL: @func_memory
; CHECK-SAME: attributes {memory_effects = #llvm.memory_effects<other = readwrite, argMem = none, inaccessibleMem = readwrite, errnoMem = readwrite, targetMem0 = readwrite, targetMem1 = readwrite>}
; CHECK: llvm.return
diff --git a/mlir/test/Target/LLVMIR/Import/function-entry-count-roundtrip.ll b/mlir/test/Target/LLVMIR/Import/function-entry-count-roundtrip.ll
new file mode 100644
index 0000000000000..f305e21ecc4e5
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/Import/function-entry-count-roundtrip.ll
@@ -0,0 +1,19 @@
+; RUN: mlir-translate -import-llvm %s | mlir-translate -mlir-to-llvmir | FileCheck %s
+
+; CHECK-LABEL: define void @synthetic()
+; CHECK-SAME: !prof ![[SYNTH_PROF_ID:[0-9]*]]
+define void @synthetic() !prof !0 {
+ ret void
+}
+
+; CHECK-LABEL: define void @with_import_guid()
+; CHECK-SAME: !prof ![[IMPORTS_PROF_ID:[0-9]*]]
+define void @with_import_guid() !prof !1 {
+ ret void
+}
+
+!0 = !{!"synthetic_function_entry_count", i64 7}
+!1 = !{!"function_entry_count", i64 7, i64 9, i64 4, i64 9}
+
+; CHECK-DAG: ![[SYNTH_PROF_ID]] = !{!"synthetic_function_entry_count", i64 7}
+; CHECK-DAG: ![[IMPORTS_PROF_ID]] = !{!"function_entry_count", i64 7, i64 4, i64 9}
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 6cc633c3e34f2..de6f965cef417 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -1922,13 +1922,13 @@ llvm.func @functionEntryCountWithImports() attributes {function_entry_count = 7
// -----
-// CHECK-LABEL: @syntheticFunctionEntryCountWithImports
-// CHECK-SAME: !prof ![[SYNTH_IMPORTS_PROF_ID:[0-9]*]]
-llvm.func @syntheticFunctionEntryCountWithImports() attributes {function_entry_count = 7 : i64, function_entry_count_imports = array<i64: 1234>, function_entry_count_synthetic} {
+// CHECK-LABEL: @negativeFunctionEntryCount
+// CHECK-SAME: !prof ![[NEGATIVE_COUNT_PROF_ID:[0-9]*]]
+llvm.func @negativeFunctionEntryCount() attributes {function_entry_count = -1 : i64} {
llvm.return
}
-// CHECK: ![[SYNTH_IMPORTS_PROF_ID]] = !{!"synthetic_function_entry_count", i64 7, i64 1234}
+// CHECK: ![[NEGATIVE_COUNT_PROF_ID]] = !{!"function_entry_count", i64 -1}
// -----
More information about the Mlir-commits
mailing list