[Mlir-commits] [mlir] [mlir][llvm] Preserve function entry count metadata (PR #204707)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Jun 18 20:40:20 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/5] [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/5] 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/5] 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/5] [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}
 
 // -----
 

>From f37108c0b5ac9400ec292fca6d40f9c0da784468 Mon Sep 17 00:00:00 2001
From: "Cyne Jarvis J. Zarceno" <cynejarviszarceno at gmail.com>
Date: Fri, 19 Jun 2026 11:33:36 +0800
Subject: [PATCH 5/5] Refine MLIR function entry count metadata

---
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   |  9 +++
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    | 19 ++++++
 .../LLVMIR/LLVMIRToLLVMTranslation.cpp        | 64 +++++++++++++------
 mlir/lib/Target/LLVMIR/ModuleTranslation.cpp  |  5 +-
 mlir/test/Dialect/LLVMIR/invalid.mlir         | 49 +++++++++++++-
 .../LLVMIR/Import/function-attributes.ll      | 43 +++++++++++--
 .../Import/function-entry-count-roundtrip.ll  | 21 ++++++
 mlir/test/Target/LLVMIR/llvmir.mlir           | 32 ++++++----
 8 files changed, 204 insertions(+), 38 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 4dad2da58b1c0..557e9881bd921 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -2018,6 +2018,15 @@ 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. The
+    `function_entry_count_imports` attribute represents the non-empty 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. This attribute
+    must not be combined with `function_entry_count_synthetic`.
   }];
 
   let arguments = (ins
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 725c12386b2f2..238038a49fdb3 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3275,6 +3275,25 @@ LogicalResult LLVMFuncOp::verify() {
                               "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> values = imports.asArrayRef();
+    if (values.empty())
+      return emitOpError() << "requires function_entry_count_imports to be "
+                              "non-empty when set";
+
+    for (auto [previous, current] :
+         llvm::zip_equal(values.drop_back(), values.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 &&
         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 3d3e6677b7027..6d0290783c215 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"
@@ -24,6 +25,9 @@
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/MemoryModelRelaxationAnnotations.h"
 
+#include <algorithm>
+#include <optional>
+
 using namespace mlir;
 using namespace mlir::LLVM;
 using namespace mlir::LLVM::detail;
@@ -102,6 +106,14 @@ getSupportedMetadataImpl(llvm::LLVMContext &llvmContext) {
 /// Converts the given profiling metadata `node` to an MLIR profiling attribute
 /// and attaches it to the imported operation if the translation succeeds.
 /// Returns failure otherwise.
+static std::optional<uint64_t> getUInt64Metadata(llvm::Metadata *metadata) {
+  llvm::ConstantInt *constant =
+      llvm::mdconst::dyn_extract<llvm::ConstantInt>(metadata);
+  if (!constant)
+    return std::nullopt;
+  return constant->getValue().tryZExtValue();
+}
+
 static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
                                       Operation *op,
                                       LLVM::ModuleImport &moduleImport) {
@@ -119,33 +131,49 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
     if (node->getNumOperands() < 2)
       return failure();
 
-    llvm::ConstantInt *entryCount =
-        llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(1));
-    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()));
-        }
+
+      // LLVM's semantic import-GUID API only reads trailing GUID operands from
+      // "function_entry_count" metadata. Do not model trailing operands on
+      // "synthetic_function_entry_count" as import GUIDs in MLIR.
+      if (isSynthetic && node->getNumOperands() > 2)
+        return failure();
+
+      std::optional<uint64_t> entryCount =
+          getUInt64Metadata(node->getOperand(1));
+      if (!entryCount)
+        return failure();
+
+      SmallVector<uint64_t> importGUIDValues;
+      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);
       }
 
-      funcOp.setFunctionEntryCount(entryCount->getZExtValue());
+      // Import GUIDs are semantically a set in LLVM. Canonicalize them as
+      // unsigned sorted-unique values before storing the bit patterns in MLIR.
+      llvm::sort(importGUIDValues);
+      importGUIDValues.erase(
+          std::unique(importGUIDValues.begin(), importGUIDValues.end()),
+          importGUIDValues.end());
+
+      funcOp.setFunctionEntryCount(*entryCount);
       if (isSynthetic)
         funcOp.setFunctionEntryCountSynthetic(true);
-      if (!importGUIDs.empty())
+      if (!importGUIDValues.empty()) {
+        SmallVector<int64_t> importGUIDs;
+        importGUIDs.reserve(importGUIDValues.size());
+        for (uint64_t guid : importGUIDValues)
+          importGUIDs.push_back(static_cast<int64_t>(guid));
         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 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/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index 0a0ae6dd5bc0a..6acc7a410a5ce 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -45,12 +45,57 @@ 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}
+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>}
+llvm.func @function_entry_count_imports_requires_count() attributes {
+  function_entry_count_imports = array<i64: 1234>
+}
+
+// -----
+
+// 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>
+}
 
 // -----
 
diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll
index 596fcb0801644..d2e4f5993cca6 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>
+; 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}
+!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
 }
@@ -225,6 +224,40 @@ define void @entry_count_malformed_import() !prof !5 {
 
 ; // -----
 
+; CHECK-LABEL: @entry_count_too_wide_count
+; CHECK-NOT: function_entry_count
+; expected-warning @below {{unhandled function metadata}}
+define void @entry_count_too_wide_count() !prof !6 {
+  ret void
+}
+
+!6 = !{!"function_entry_count", i128 18446744073709551616}
+
+; // -----
+
+; CHECK-LABEL: @entry_count_too_wide_import
+; CHECK-NOT: function_entry_count
+; expected-warning @below {{unhandled function metadata}}
+define void @entry_count_too_wide_import() !prof !7 {
+  ret void
+}
+
+!7 = !{!"function_entry_count", i64 7, i128 18446744073709551616}
+
+; // -----
+
+; Preserve the raw i64 metadata bit pattern. LLVM's semantic getEntryCount()
+; treats real uint64_t(-1) as unknown, but translation preserves the metadata.
+; 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..e2b33d3179cd9
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/Import/function-entry-count-roundtrip.ll
@@ -0,0 +1,21 @@
+; RUN: mlir-translate -import-llvm %s | mlir-translate -mlir-to-llvmir | FileCheck %s
+
+define void @synthetic() !prof !0 {
+  ret void
+}
+
+define void @with_import_guid() !prof !1 {
+  ret void
+}
+
+!0 = !{!"synthetic_function_entry_count", i64 7}
+!1 = !{!"function_entry_count", i64 7, i64 1234}
+
+; CHECK-LABEL: define void @synthetic()
+; CHECK-SAME: !prof ![[SYNTH:[0-9]+]]
+
+; CHECK-LABEL: define void @with_import_guid()
+; CHECK-SAME: !prof ![[IMPORTS:[0-9]+]]
+
+; CHECK-DAG: ![[SYNTH]] = !{!"synthetic_function_entry_count", i64 7}
+; CHECK-DAG: ![[IMPORTS]] = !{!"function_entry_count", i64 7, i64 1234}
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 2fd1b879b7051..7a9aedb262333 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -1893,42 +1893,50 @@ llvm.func @my_allocator(i64) attributes {passthrough = [["allocsize", "429496729
 // -----
 
 // CHECK-LABEL: @functionEntryCount
-// CHECK-SAME: !prof ![[PROF_ID:[0-9]*]]
+// CHECK-SAME: !prof ![[PROF_ID:[0-9]+]]
 llvm.func @functionEntryCount() attributes {function_entry_count = 4242 : i64} {
   llvm.return
 }
 
-// CHECK: ![[PROF_ID]] = !{!"function_entry_count", i64 4242}
+// CHECK-DAG: ![[PROF_ID]] = !{!"function_entry_count", i64 4242}
 
 // -----
 
 // CHECK-LABEL: @syntheticFunctionEntryCount
-// CHECK-SAME: !prof ![[SYNTH_PROF_ID:[0-9]*]]
-llvm.func @syntheticFunctionEntryCount() attributes {function_entry_count = 7 : i64, function_entry_count_synthetic} {
+// 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-DAG: ![[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>} {
+// CHECK-SAME: !prof ![[IMPORTS_PROF_ID:[0-9]+]]
+llvm.func @functionEntryCountWithImports() attributes {
+  function_entry_count = 7 : i64,
+  function_entry_count_imports = array<i64: 4, 1234, -1>
+} {
   llvm.return
 }
 
-// CHECK: ![[IMPORTS_PROF_ID]] = !{!"function_entry_count", i64 7, i64 1234}
+// CHECK-DAG: ![[IMPORTS_PROF_ID]] = !{!"function_entry_count", i64 7, i64 4, i64 1234, i64 -1}
 
 // -----
 
-// 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: @functionEntryCountNegativeCount
+// CHECK-SAME: !prof ![[NEG_PROF_ID:[0-9]+]]
+llvm.func @functionEntryCountNegativeCount() attributes {
+  function_entry_count = -1 : i64
+} {
   llvm.return
 }
 
-// CHECK: ![[SYNTH_IMPORTS_PROF_ID]] = !{!"synthetic_function_entry_count", i64 7, i64 1234}
+// CHECK-DAG: ![[NEG_PROF_ID]] = !{!"function_entry_count", i64 -1}
 
 // -----
 



More information about the Mlir-commits mailing list