[llvm] [MergeFunctions] Preserve import GUIDs when folding functions (PR #207003)
Alok Kumar Sharma via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 1 07:55:40 PDT 2026
https://github.com/alokkrsharma created https://github.com/llvm/llvm-project/pull/207003
Computes the union of SamplePGO/ThinLTO import GUIDs from both inputs and attaches it to the surviving function.
>From 3c1213f770369de26902235ad0562cc75eafc433 Mon Sep 17 00:00:00 2001
From: Alok Kumar Sharma <AlokKumar.Sharma at amd.com>
Date: Wed, 1 Jul 2026 12:46:38 +0530
Subject: [PATCH] [MergeFunctions] Preserve import GUIDs when folding functions
Computes the union of SamplePGO/ThinLTO import GUIDs from both
inputs and attaches it to the surviving function.
---
llvm/lib/Transforms/IPO/MergeFunctions.cpp | 33 +++++--
.../merge-functions-entry-count-imports.ll | 85 +++++++++++++++++++
2 files changed, 110 insertions(+), 8 deletions(-)
create mode 100644 llvm/test/Transforms/MergeFunc/merge-functions-entry-count-imports.ll
diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index c4e56855ea2fb..3b98dadc68451 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -90,6 +90,7 @@
#include "llvm/Transforms/IPO/MergeFunctions.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Argument.h"
@@ -880,12 +881,27 @@ static bool isODR(const Function *F) {
return F->hasWeakODRLinkage() || F->hasLinkOnceODRLinkage();
}
-static void mergeEntryCountsInto(Function *F, std::optional<uint64_t> FC,
- std::optional<uint64_t> GC) {
- if (!FC && !GC)
+static DenseSet<GlobalValue::GUID> unionImportGUIDs(const Function *F,
+ const Function *G) {
+ DenseSet<GlobalValue::GUID> AllImports = F->getImportGUIDs();
+ DenseSet<GlobalValue::GUID> GImports = G->getImportGUIDs();
+ AllImports.insert(GImports.begin(), GImports.end());
+ return AllImports;
+}
+
+static void
+mergeEntryCountsAndImportsInto(Function *F, std::optional<uint64_t> FC,
+ std::optional<uint64_t> GC,
+ const DenseSet<GlobalValue::GUID> &Imports) {
+ if (!FC && !GC && Imports.empty())
return;
- uint64_t Sum = SaturatingAdd(FC ? *FC : uint64_t{0}, GC ? *GC : uint64_t{0});
- F->setEntryCount(Sum);
+
+ uint64_t Sum;
+ if (!FC && !GC)
+ Sum = static_cast<uint64_t>(-1);
+ else
+ Sum = SaturatingAdd(FC ? *FC : uint64_t{0}, GC ? *GC : uint64_t{0});
+ F->setEntryCount(Sum, Imports.empty() ? nullptr : &Imports);
}
// Merge two equivalent functions. Upon completion, Function G is deleted.
@@ -893,6 +909,7 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
std::optional<uint64_t> FEC = F->getEntryCount();
std::optional<uint64_t> GEC = G->getEntryCount();
+ DenseSet<GlobalValue::GUID> AllImports = unionImportGUIDs(F, G);
// Create a new thunk that both F and G can call, if F cannot call G directly.
// That is the case if F is either interposable or if G is either weak_odr or
@@ -947,7 +964,7 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
F->setLinkage(GlobalValue::PrivateLinkage);
// The private shared implementation accumulates both symbols' entries
// (FEC + GEC), while each ODR thunk retains its own per-symbol entry count.
- mergeEntryCountsInto(F, FEC, GEC);
+ mergeEntryCountsAndImportsInto(F, FEC, GEC, AllImports);
++NumDoubleWeak;
++NumFunctionsMerged;
} else {
@@ -975,14 +992,14 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
// stop here and delete G. There's no need for a thunk. (See note on
// MergeFunctionsPDI above).
if (G->isDiscardableIfUnused() && G->use_empty() && !MergeFunctionsPDI) {
- mergeEntryCountsInto(F, FEC, GEC);
+ mergeEntryCountsAndImportsInto(F, FEC, GEC, AllImports);
G->eraseFromParent();
++NumFunctionsMerged;
return;
}
if (writeThunkOrAliasIfNeeded(F, G)) {
- mergeEntryCountsInto(F, FEC, GEC);
+ mergeEntryCountsAndImportsInto(F, FEC, GEC, AllImports);
++NumFunctionsMerged;
}
}
diff --git a/llvm/test/Transforms/MergeFunc/merge-functions-entry-count-imports.ll b/llvm/test/Transforms/MergeFunc/merge-functions-entry-count-imports.ll
new file mode 100644
index 0000000000000..a3d972f7e984a
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/merge-functions-entry-count-imports.ll
@@ -0,0 +1,85 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=mergefunc -S | FileCheck %s
+
+define internal i32 @foo(i32 %x) !prof !12 {
+entry:
+ %add = add nsw i32 %x, 1
+ ret i32 %add
+}
+
+define internal i32 @bar(i32 %x) !prof !13 {
+; CHECK-LABEL: define internal i32 @bar(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF12:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[X]], 1
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+entry:
+ %add = add nsw i32 %x, 1
+ ret i32 %add
+}
+
+define internal i32 @f(i32 %x) !prof !14 {
+; CHECK-LABEL: define internal i32 @f(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF13:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[X]], 2
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+entry:
+ %add = add nsw i32 %x, 2
+ ret i32 %add
+}
+
+define internal i32 @g(i32 %x) !prof !15 {
+entry:
+ %add = add nsw i32 %x, 2
+ ret i32 %add
+}
+
+define i32 @main() {
+; CHECK-LABEL: define i32 @main() {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C1:%.*]] = call i32 @bar(i32 1)
+; CHECK-NEXT: [[C2:%.*]] = call i32 @bar(i32 2)
+; CHECK-NEXT: [[C3:%.*]] = call i32 @f(i32 3)
+; CHECK-NEXT: [[C4:%.*]] = call i32 @f(i32 4)
+; CHECK-NEXT: [[R1:%.*]] = add nsw i32 [[C1]], [[C2]]
+; CHECK-NEXT: [[R2:%.*]] = add nsw i32 [[C3]], [[C4]]
+; CHECK-NEXT: [[R:%.*]] = add nsw i32 [[R1]], [[R2]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+entry:
+ %c1 = call i32 @foo(i32 1)
+ %c2 = call i32 @bar(i32 2)
+ %c3 = call i32 @f(i32 3)
+ %c4 = call i32 @g(i32 4)
+ %r1 = add nsw i32 %c1, %c2
+ %r2 = add nsw i32 %c3, %c4
+ %r = add nsw i32 %r1, %r2
+ ret i32 %r
+}
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 1, !"ProfileSummary", !1}
+!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}
+!2 = !{!"ProfileFormat", !"InstrProf"}
+!3 = !{!"TotalCount", i64 300}
+!4 = !{!"MaxCount", i64 200}
+!5 = !{!"MaxInternalCount", i64 200}
+!6 = !{!"MaxFunctionCount", i64 200}
+!7 = !{!"NumCounts", i64 2}
+!8 = !{!"NumFunctions", i64 2}
+!9 = !{!"DetailedSummary", !10}
+!10 = !{!11}
+!11 = !{i32 10000, i64 100, i32 1}
+!12 = !{!"function_entry_count", i64 100, i64 1001, i64 2002}
+!13 = !{!"function_entry_count", i64 200, i64 2002, i64 3003}
+!14 = !{!"function_entry_count", i64 -1, i64 1001, i64 2002}
+!15 = !{!"function_entry_count", i64 -1, i64 2002, i64 3003}
+
+;.
+; CHECK: [[PROF12]] = !{!"function_entry_count", i64 300, i64 1001, i64 2002, i64 3003}
+; CHECK: [[PROF13]] = !{!"function_entry_count", i64 -1, i64 1001, i64 2002, i64 3003}
+;.
More information about the llvm-commits
mailing list