[llvm] [MergeFunctions] Preserve instruction-level profile metadata during merging (PR #208009)
Alok Kumar Sharma via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 13 06:26:46 PDT 2026
https://github.com/alokkrsharma updated https://github.com/llvm/llvm-project/pull/208009
>From 1b064ff0394b536662b6ea107f285677ef945d52 Mon Sep 17 00:00:00 2001
From: Alok Kumar Sharma <AlokKumar.Sharma at amd.com>
Date: Tue, 7 Jul 2026 18:39:52 +0530
Subject: [PATCH 1/4] [MergeFunctions] Preserve instruction-level profile
metadata during merging
Merge branch weights and value profile metadata when equivalent
functions are folded. Instruction-level branch weights and value
profiles are scaled to absolute counts using BlockFrequencyInfo
before being aggregated.
---
.../llvm/Transforms/IPO/MergeFunctions.h | 6 +-
llvm/lib/Transforms/IPO/MergeFunctions.cpp | 288 ++++++++++-
.../merge-functions-prof-metadata.ll | 460 ++++++++++++++++++
3 files changed, 736 insertions(+), 18 deletions(-)
create mode 100644 llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll
diff --git a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
index 75399d3297a05..218bd0702e1d2 100644
--- a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
+++ b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
@@ -28,9 +28,11 @@ class MergeFunctionsPass : public OptionalPassInfoMixin<MergeFunctionsPass> {
public:
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
- LLVM_ABI static bool runOnModule(Module &M);
+ LLVM_ABI static bool runOnModule(Module &M,
+ FunctionAnalysisManager *FAM = nullptr);
LLVM_ABI static DenseMap<Function *, Function *>
- runOnFunctions(ArrayRef<Function *> F);
+ runOnFunctions(ArrayRef<Function *> F,
+ FunctionAnalysisManager *FAM = nullptr);
};
} // end namespace llvm
diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index c4e56855ea2fb..c78bb2b0f357f 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -89,14 +89,21 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/IPO/MergeFunctions.h"
+#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/BlockFrequencyInfo.h"
+#include "llvm/Analysis/BranchProbabilityInfo.h"
+#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/IRBuilder.h"
@@ -104,16 +111,21 @@
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/ProfDataUtils.h"
#include "llvm/IR/StructuralHash.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/ValueHandle.h"
+#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO.h"
@@ -121,7 +133,10 @@
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <algorithm>
#include <cassert>
+#include <cstddef>
+#include <cstdint>
#include <iterator>
+#include <memory>
#include <optional>
#include <set>
#include <utility>
@@ -198,10 +213,13 @@ class MergeFunctions {
}
template <typename FuncContainer> bool run(FuncContainer &Functions);
- DenseMap<Function *, Function *> runOnFunctions(ArrayRef<Function *> F);
+ DenseMap<Function *, Function *>
+ runOnFunctions(ArrayRef<Function *> F, FunctionAnalysisManager *AM = nullptr);
SmallPtrSet<GlobalValue *, 4> &getUsed();
+ void setFunctionAnalysisManager(FunctionAnalysisManager *AM) { FAM = AM; }
+
private:
// The function comparison operator is provided here so that FunctionNodes do
// not need to become larger with another pointer.
@@ -257,6 +275,10 @@ class MergeFunctions {
/// again.
void mergeTwoFunctions(Function *F, Function *G);
+ const BlockFrequencyInfo &getBFI(Function &F);
+
+ void mergeProfMetadataInto(Function *F, Function *G);
+
/// Fill PDIUnrelatedWL with instructions from the entry block that are
/// unrelated to parameter related debug info.
/// \param PDVRUnrelatedWL The equivalent non-intrinsic debug records.
@@ -287,7 +309,9 @@ class MergeFunctions {
// If needed, replace G with an alias to F if possible, or a thunk to F if
// profitable. Returns false if neither is the case. If \p G is not needed
// (i.e. it is discardable and not used), \p G is removed directly.
- bool writeThunkOrAliasIfNeeded(Function *F, Function *G);
+ // \p MergeProfile must be true when G's profile should be preserved, it is
+ // merged into F before G is erased or rewritten.
+ bool writeThunkOrAliasIfNeeded(Function *F, Function *G, bool MergeProfile);
/// Replace function F with function G in the function tree.
void replaceFunctionInTree(const FunctionNode &FN, Function *G);
@@ -305,20 +329,36 @@ class MergeFunctions {
/// Deleted-New functions mapping
DenseMap<Function *, Function *> DelToNewMap;
+
+ FunctionAnalysisManager *FAM = nullptr;
+
+ struct CachedProfileAnalyses {
+ DominatorTree DT;
+ LoopInfo LI;
+ BranchProbabilityInfo BPI;
+ BlockFrequencyInfo BFI;
+
+ CachedProfileAnalyses(Function &F)
+ : DT(F), LI(DT), BPI(F, LI, nullptr, &DT), BFI(F, BPI, LI) {}
+ };
+ DenseMap<const Function *, std::unique_ptr<CachedProfileAnalyses>> CachedBFI;
};
} // end anonymous namespace
PreservedAnalyses MergeFunctionsPass::run(Module &M,
ModuleAnalysisManager &AM) {
- if (!MergeFunctionsPass::runOnModule(M))
+ auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
+ if (!MergeFunctionsPass::runOnModule(M, &FAM))
return PreservedAnalyses::all();
return PreservedAnalyses::none();
}
SmallPtrSet<GlobalValue *, 4> &MergeFunctions::getUsed() { return Used; }
-bool MergeFunctionsPass::runOnModule(Module &M) {
+bool MergeFunctionsPass::runOnModule(Module &M, FunctionAnalysisManager *FAM) {
MergeFunctions MF;
+ if (FAM)
+ MF.setFunctionAnalysisManager(FAM);
SmallVector<GlobalValue *, 4> UsedV;
collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/false);
collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/true);
@@ -327,9 +367,10 @@ bool MergeFunctionsPass::runOnModule(Module &M) {
}
DenseMap<Function *, Function *>
-MergeFunctionsPass::runOnFunctions(ArrayRef<Function *> F) {
+MergeFunctionsPass::runOnFunctions(ArrayRef<Function *> F,
+ FunctionAnalysisManager *FAM) {
MergeFunctions MF;
- return MF.runOnFunctions(F);
+ return MF.runOnFunctions(F, FAM);
}
#ifndef NDEBUG
@@ -493,7 +534,10 @@ template <typename FuncContainer> bool MergeFunctions::run(FuncContainer &M) {
}
DenseMap<Function *, Function *>
-MergeFunctions::runOnFunctions(ArrayRef<Function *> F) {
+MergeFunctions::runOnFunctions(ArrayRef<Function *> F,
+ FunctionAnalysisManager *AM) {
+ if (AM)
+ setFunctionAnalysisManager(AM);
[[maybe_unused]] bool MergeResult = this->run(F);
assert(MergeResult == !DelToNewMap.empty());
return this->DelToNewMap;
@@ -858,21 +902,36 @@ void MergeFunctions::writeAlias(Function *F, Function *G) {
// If needed, replace G with an alias to F if possible, or a thunk to F if
// profitable. Returns false if neither is the case. If \p G is not needed (i.e.
-// it is discardable and unused), \p G is removed directly.
-bool MergeFunctions::writeThunkOrAliasIfNeeded(Function *F, Function *G) {
- if (G->isDiscardableIfUnused() && G->use_empty() && !MergeFunctionsPDI) {
+// it is discardable and unused), \p G is removed directly. If \p MergeProfile
+// is set, G's profile metadata is merged into F.
+bool MergeFunctions::writeThunkOrAliasIfNeeded(Function *F, Function *G,
+ bool MergeProfile) {
+ bool ShouldErase =
+ G->isDiscardableIfUnused() && G->use_empty() && !MergeFunctionsPDI;
+ bool ShouldAlias = canCreateAliasFor(G);
+ bool ShouldThunk = canCreateThunkFor(F);
+
+ if (!ShouldErase && !ShouldAlias && !ShouldThunk)
+ return false;
+
+ if (MergeProfile)
+ mergeProfMetadataInto(F, G);
+
+ if (ShouldErase) {
G->eraseFromParent();
return true;
}
- if (canCreateAliasFor(G)) {
+
+ if (ShouldAlias) {
writeAlias(F, G);
return true;
}
- if (canCreateThunkFor(F)) {
+ if (ShouldThunk) {
writeThunk(F, G);
return true;
}
- return false;
+
+ llvm_unreachable("Erase, alias or thunk must apply");
}
/// Returns true if \p F is either weak_odr or linkonce_odr.
@@ -880,6 +939,199 @@ static bool isODR(const Function *F) {
return F->hasWeakODRLinkage() || F->hasLinkOnceODRLinkage();
}
+static uint64_t getBlockCountForMerging(const BlockFrequencyInfo *BFI,
+ const BasicBlock *BB,
+ std::optional<uint64_t> EC) {
+ if (BFI) {
+ if (auto Count = BFI->getBlockProfileCount(BB, /*AllowSynthetic=*/true))
+ return *Count;
+ // BFI present but no count for this block, fall through to EC.
+ }
+ if (EC)
+ return *EC;
+ return 1;
+}
+
+// The branch/value profile weights are relative within a function. Before
+// merge we need to normalize weights to absolute counts.
+// (weight * BlockCount / TotalWeight)
+static uint64_t scaleToBlockCount(uint64_t Weight, uint64_t TotalWeight,
+ uint64_t BlockCount) {
+ if (Weight == 0 || TotalWeight == 0 || BlockCount == 0)
+ return 0;
+ APInt Num(128, BlockCount);
+ Num *= APInt(128, Weight);
+ APInt Den(128, TotalWeight);
+ Num = (Num + Den.lshr(1)).udiv(Den);
+ return Num.getLimitedValue();
+}
+
+// Combine the scaled branch_weights of corresponding instructions of F and G.
+static void mergeBranchWeightsOnInstructions(Instruction *FI,
+ const Instruction *GI,
+ const BlockFrequencyInfo *BfiF,
+ const BlockFrequencyInfo *BfiG,
+ std::optional<uint64_t> FEC,
+ std::optional<uint64_t> GEC) {
+ SmallVector<uint32_t, 8> FWeights, GWeights;
+ bool HasF = extractBranchWeights(*FI, FWeights);
+ bool HasG = extractBranchWeights(*GI, GWeights);
+ if (!HasF && !HasG)
+ return;
+
+ uint64_t BlockCountF = getBlockCountForMerging(BfiF, FI->getParent(), FEC);
+ uint64_t BlockCountG = getBlockCountForMerging(BfiG, GI->getParent(), GEC);
+
+ uint64_t TotalF = 0, TotalG = 0;
+ if (HasF)
+ extractProfTotalWeight(*FI, TotalF);
+ if (HasG)
+ extractProfTotalWeight(*GI, TotalG);
+
+ size_t NumWeights = std::max(HasF ? FWeights.size() : size_t{0},
+ HasG ? GWeights.size() : size_t{0});
+ SmallVector<uint64_t, 8> MergedWeights;
+ MergedWeights.reserve(NumWeights);
+ for (size_t I = 0; I < NumWeights; ++I) {
+ uint64_t FW = (HasF && I < FWeights.size()) ? FWeights[I] : 0;
+ uint64_t GW = (HasG && I < GWeights.size()) ? GWeights[I] : 0;
+ uint64_t AbsF = HasF ? scaleToBlockCount(FW, TotalF, BlockCountF) : 0;
+ uint64_t AbsG = HasG ? scaleToBlockCount(GW, TotalG, BlockCountG) : 0;
+ MergedWeights.push_back(SaturatingAdd(AbsF, AbsG));
+ }
+
+ bool IsExpected = hasBranchWeightOrigin(*FI) || hasBranchWeightOrigin(*GI);
+ setFittedBranchWeights(*FI, MergedWeights, IsExpected);
+}
+
+// Accumulate scaled value profile counts of Instruction I into Merged.
+static void addScaledValueProfile(const Instruction &I, InstrProfValueKind Kind,
+ uint64_t BlockCount,
+ DenseMap<uint64_t, uint64_t> &Merged) {
+ uint64_t Total = 0;
+ SmallVector<InstrProfValueData, 4> VDs =
+ getValueProfDataFromInst(I, Kind, /*MaxNumValueData=*/UINT32_MAX, Total);
+ if (VDs.empty() || Total == 0)
+ return;
+ for (const InstrProfValueData &VD : VDs) {
+ uint64_t Abs = scaleToBlockCount(VD.Count, Total, BlockCount);
+ Merged[VD.Value] = SaturatingAdd(Merged[VD.Value], Abs);
+ }
+}
+
+// Merge (union) scaled value profiles of F and G.
+static void mergeValueProfileOnInstructions(Instruction *FI,
+ const Instruction *GI,
+ const BlockFrequencyInfo *BfiF,
+ const BlockFrequencyInfo *BfiG,
+ std::optional<uint64_t> FEC,
+ std::optional<uint64_t> GEC) {
+ MDNode *ProfF = FI->getMetadata(LLVMContext::MD_prof);
+ MDNode *ProfG = GI->getMetadata(LLVMContext::MD_prof);
+ bool HasF = ProfF && isValueProfileMD(ProfF);
+ bool HasG = ProfG && isValueProfileMD(ProfG);
+ if (!HasF && !HasG)
+ return;
+
+ auto *KindF =
+ HasF ? mdconst::dyn_extract<ConstantInt>(ProfF->getOperand(1)) : nullptr;
+ auto *KindG =
+ HasG ? mdconst::dyn_extract<ConstantInt>(ProfG->getOperand(1)) : nullptr;
+ if (HasF && HasG && KindF && KindG &&
+ KindF->getZExtValue() != KindG->getZExtValue()) {
+ FI->setMetadata(LLVMContext::MD_prof, nullptr);
+ return;
+ }
+
+ const ConstantInt *KindCI = KindF ? KindF : KindG;
+ if (!KindCI) {
+ FI->setMetadata(LLVMContext::MD_prof, nullptr);
+ return;
+ }
+
+ InstrProfValueKind Kind =
+ static_cast<InstrProfValueKind>(KindCI->getZExtValue());
+
+ DenseMap<uint64_t, uint64_t> Merged;
+ uint64_t BCF = getBlockCountForMerging(BfiF, FI->getParent(), FEC);
+ uint64_t BCG = getBlockCountForMerging(BfiG, GI->getParent(), GEC);
+ if (HasF)
+ addScaledValueProfile(*FI, Kind, BCF, Merged);
+ if (HasG)
+ addScaledValueProfile(*GI, Kind, BCG, Merged);
+
+ if (Merged.empty())
+ return;
+
+ SmallVector<InstrProfValueData, 8> VDs;
+ VDs.reserve(Merged.size());
+ uint64_t Sum = 0;
+ for (auto &[Value, Count] : Merged) {
+ VDs.push_back({Value, Count});
+ Sum = SaturatingAdd(Sum, Count);
+ }
+ llvm::sort(VDs, [](const InstrProfValueData &A, const InstrProfValueData &B) {
+ return A.Count > B.Count;
+ });
+ annotateValueSite(*FI->getFunction()->getParent(), *FI, VDs, Sum, Kind,
+ VDs.size());
+}
+
+const BlockFrequencyInfo &MergeFunctions::getBFI(Function &F) {
+ if (FAM)
+ return FAM->getResult<BlockFrequencyAnalysis>(F);
+ auto &Entry = CachedBFI[&F];
+ if (!Entry)
+ Entry = std::make_unique<CachedProfileAnalyses>(F);
+ return Entry->BFI;
+}
+
+void MergeFunctions::mergeProfMetadataInto(Function *F, Function *G) {
+ std::optional<uint64_t> FEC = F->getEntryCount();
+ std::optional<uint64_t> GEC = G->getEntryCount();
+ const BlockFrequencyInfo *BfiF = &getBFI(*F);
+ const BlockFrequencyInfo *BfiG = &getBFI(*G);
+
+ SmallVector<std::pair<BasicBlock *, const BasicBlock *>, 8> BBWorklist;
+ SmallPtrSet<const BasicBlock *, 32> VisitedBBs;
+
+ BBWorklist.emplace_back(&F->getEntryBlock(), &G->getEntryBlock());
+ VisitedBBs.insert(BBWorklist.back().first);
+
+ while (!BBWorklist.empty()) {
+ auto [BBF, BBG] = BBWorklist.pop_back_val();
+
+ for (auto [FI, GI] : llvm::zip(*BBF, *BBG)) {
+ MDNode *ProfF = FI.getMetadata(LLVMContext::MD_prof);
+ MDNode *ProfG = GI.getMetadata(LLVMContext::MD_prof);
+ if ((ProfF && isValueProfileMD(ProfF)) ||
+ (ProfG && isValueProfileMD(ProfG)))
+ mergeValueProfileOnInstructions(&FI, &GI, BfiF, BfiG, FEC, GEC);
+ if (isa<SelectInst>(FI))
+ mergeBranchWeightsOnInstructions(&FI, &GI, BfiF, BfiG, FEC, GEC);
+ }
+
+ Instruction *TermF = BBF->getTerminator();
+ const Instruction *TermG = BBG->getTerminator();
+ mergeBranchWeightsOnInstructions(TermF, TermG, BfiF, BfiG, FEC, GEC);
+ for (unsigned I = 0, E = TermF->getNumSuccessors(); I != E; ++I) {
+ if (!VisitedBBs.insert(TermF->getSuccessor(I)).second)
+ continue;
+ BBWorklist.emplace_back(TermF->getSuccessor(I), TermG->getSuccessor(I));
+ }
+ }
+
+ if (FAM) {
+ PreservedAnalyses PA = PreservedAnalyses::all();
+ PA.abandon<BranchProbabilityAnalysis>();
+ PA.abandon<BlockFrequencyAnalysis>();
+ FAM->invalidate(*F, PA);
+ } else {
+ CachedBFI.erase(F);
+ CachedBFI.erase(G);
+ }
+}
+
static void mergeEntryCountsInto(Function *F, std::optional<uint64_t> FC,
std::optional<uint64_t> GC) {
if (!FC && !GC)
@@ -935,10 +1187,13 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
const MaybeAlign NewFAlign = NewF->getAlign();
const MaybeAlign GAlign = G->getAlign();
- writeThunkOrAliasIfNeeded(F, G);
+ // Merge !prof, while G still has its body.
+ writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true);
if (FEC)
NewF->setEntryCount(*FEC);
- writeThunkOrAliasIfNeeded(F, NewF);
+ // NewF becomes thunk/alias to the shared body F, it has no profile to be
+ // merged.
+ writeThunkOrAliasIfNeeded(F, NewF, /*MergeProfile*/ false);
if (NewFAlign || GAlign)
F->setAlignment(std::max(NewFAlign.valueOrOne(), GAlign.valueOrOne()));
@@ -975,13 +1230,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) {
+ mergeProfMetadataInto(F, G);
mergeEntryCountsInto(F, FEC, GEC);
G->eraseFromParent();
++NumFunctionsMerged;
return;
}
- if (writeThunkOrAliasIfNeeded(F, G)) {
+ if (writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true)) {
mergeEntryCountsInto(F, FEC, GEC);
++NumFunctionsMerged;
}
diff --git a/llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll b/llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll
new file mode 100644
index 0000000000000..278ded8693662
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll
@@ -0,0 +1,460 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 6
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+
+; Check mergefunc combines branch/value profile data.
+
+define internal i32 @bw_asym_a(i32 %x) !prof !1 {
+entry:
+ %cmp = icmp slt i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !2
+
+then:
+ ret i32 1
+
+else:
+ ret i32 0
+}
+
+define internal i32 @bw_asym_b(i32 %x) !prof !3 {
+entry:
+ %cmp = icmp slt i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !4
+
+then:
+ ret i32 1
+
+else:
+ ret i32 0
+}
+
+define internal i32 @bw_sym_a(i32 %x) !prof !5 {
+entry:
+ %cmp = icmp ne i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !6
+
+then:
+ ret i32 4
+
+else:
+ ret i32 5
+}
+
+define internal i32 @bw_sym_b(i32 %x) !prof !7 {
+entry:
+ %cmp = icmp ne i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !8
+
+then:
+ ret i32 4
+
+else:
+ ret i32 5
+}
+
+define internal i32 @bw_g_only_a(i32 %x) !prof !24 {
+entry:
+ %cmp = icmp sgt i32 %x, 0
+ br i1 %cmp, label %then, label %else
+
+then:
+ ret i32 7
+
+else:
+ ret i32 8
+}
+
+define internal i32 @bw_g_only_b(i32 %x) !prof !25 {
+entry:
+ %cmp = icmp sgt i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !26
+
+then:
+ ret i32 7
+
+else:
+ ret i32 8
+}
+
+define internal i32 @select_bw_a(i32 %x) !prof !9 {
+entry:
+ %cmp = icmp slt i32 %x, 0
+ %sel = select i1 %cmp, i32 1, i32 0, !prof !10
+ ret i32 %sel
+}
+
+define internal i32 @select_bw_b(i32 %x) !prof !11 {
+entry:
+ %cmp = icmp slt i32 %x, 0
+ %sel = select i1 %cmp, i32 1, i32 0, !prof !12
+ ret i32 %sel
+}
+
+define internal i32 @vp_sym_a(ptr %fn) !prof !13 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !14
+ ret i32 %r
+}
+
+define internal i32 @vp_sym_b(ptr %fn) !prof !15 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !16
+ ret i32 %r
+}
+
+; add i32 1 keeps @vp_asym_* distinct from @vp_sym_*.
+define internal i32 @vp_asym_a(ptr %fn) !prof !17 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !18
+ %adj = add i32 %r, 1
+ ret i32 %adj
+}
+
+define internal i32 @vp_asym_b(ptr %fn) !prof !19 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val()
+ %adj = add i32 %r, 1
+ ret i32 %adj
+}
+
+define internal i32 @vp_g_only_a(ptr %fn) !prof !27 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val()
+ %adj = add i32 %r, 3
+ ret i32 %adj
+}
+
+define internal i32 @vp_g_only_b(ptr %fn) !prof !28 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !29
+ %adj = add i32 %r, 3
+ ret i32 %adj
+}
+
+define internal i32 @vp_kind_mismatch_a(ptr %fn) !prof !20 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !21
+ %adj = add i32 %r, 2
+ ret i32 %adj
+}
+
+define internal i32 @vp_kind_mismatch_b(ptr %fn) !prof !22 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !23
+ %adj = add i32 %r, 2
+ ret i32 %adj
+}
+
+define dso_local i32 @thunk_path_a(i32 %x) unnamed_addr !prof !30 {
+entry:
+ %cmp = icmp ult i32 %x, 3
+ br i1 %cmp, label %then, label %else, !prof !31
+
+then:
+ ret i32 9
+
+else:
+ ret i32 10
+}
+
+define dso_local i32 @thunk_path_b(i32 %x) unnamed_addr !prof !32 {
+entry:
+ %cmp = icmp ult i32 %x, 3
+ br i1 %cmp, label %then, label %else, !prof !33
+
+then:
+ ret i32 9
+
+else:
+ ret i32 10
+}
+
+define weak_odr i32 @odr_path_a(i32 %x) !prof !34 {
+entry:
+ %cmp = icmp ugt i32 %x, 4
+ br i1 %cmp, label %then, label %else, !prof !35
+
+then:
+ ret i32 11
+
+else:
+ ret i32 12
+}
+
+define weak_odr i32 @odr_path_b(i32 %x) !prof !36 {
+entry:
+ %cmp = icmp ugt i32 %x, 4
+ br i1 %cmp, label %then, label %else, !prof !37
+
+then:
+ ret i32 11
+
+else:
+ ret i32 12
+}
+
+define i32 @use(i32 %x, ptr %fn) {
+entry:
+ %a = call i32 @bw_asym_a(i32 %x)
+ %b = call i32 @bw_asym_b(i32 %x)
+ %c = call i32 @bw_sym_a(i32 %x)
+ %d = call i32 @bw_sym_b(i32 %x)
+ %e = call i32 @bw_g_only_a(i32 %x)
+ %f = call i32 @bw_g_only_b(i32 %x)
+ %g = call i32 @select_bw_a(i32 %x)
+ %h = call i32 @select_bw_b(i32 %x)
+ %i = call i32 @vp_sym_a(ptr %fn)
+ %j = call i32 @vp_sym_b(ptr %fn)
+ %k = call i32 @vp_asym_a(ptr %fn)
+ %l = call i32 @vp_asym_b(ptr %fn)
+ %m = call i32 @vp_g_only_a(ptr %fn)
+ %n = call i32 @vp_g_only_b(ptr %fn)
+ %o = call i32 @vp_kind_mismatch_a(ptr %fn)
+ %p = call i32 @vp_kind_mismatch_b(ptr %fn)
+ %q = call i32 @thunk_path_a(i32 %x)
+ %r = call i32 @thunk_path_b(i32 %x)
+ %s = call i32 @odr_path_a(i32 %x)
+ %t = call i32 @odr_path_b(i32 %x)
+ %sum0 = add i32 %a, %b
+ %sum1 = add i32 %sum0, %c
+ %sum2 = add i32 %sum1, %d
+ %sum3 = add i32 %sum2, %e
+ %sum4 = add i32 %sum3, %f
+ %sum5 = add i32 %sum4, %g
+ %sum6 = add i32 %sum5, %h
+ %sum7 = add i32 %sum6, %i
+ %sum8 = add i32 %sum7, %j
+ %sum9 = add i32 %sum8, %k
+ %sum10 = add i32 %sum9, %l
+ %sum11 = add i32 %sum10, %m
+ %sum12 = add i32 %sum11, %n
+ %sum13 = add i32 %sum12, %o
+ %sum14 = add i32 %sum13, %p
+ %sum15 = add i32 %sum14, %q
+ %sum16 = add i32 %sum15, %r
+ %sum17 = add i32 %sum16, %s
+ %sum18 = add i32 %sum17, %t
+ ret i32 %sum18
+}
+
+!1 = !{!"function_entry_count", i64 100}
+!2 = !{!"branch_weights", i32 10, i32 90}
+!3 = !{!"function_entry_count", i64 200}
+!4 = !{!"branch_weights", i32 30, i32 70}
+!5 = !{!"function_entry_count", i64 40}
+!6 = !{!"branch_weights", i32 60, i32 40}
+!7 = !{!"function_entry_count", i64 60}
+!8 = !{!"branch_weights", i32 20, i32 80}
+!9 = !{!"function_entry_count", i64 50}
+!10 = !{!"branch_weights", i32 10, i32 90}
+!11 = !{!"function_entry_count", i64 150}
+!12 = !{!"branch_weights", i32 30, i32 70}
+!13 = !{!"function_entry_count", i64 100}
+!14 = !{!"VP", i32 0, i64 100, i64 111, i64 60, i64 222, i64 40}
+!15 = !{!"function_entry_count", i64 200}
+!16 = !{!"VP", i32 0, i64 200, i64 111, i64 120, i64 333, i64 80}
+!17 = !{!"function_entry_count", i64 80}
+!18 = !{!"VP", i32 0, i64 80, i64 444, i64 50, i64 555, i64 30}
+!19 = !{!"function_entry_count", i64 120}
+!20 = !{!"function_entry_count", i64 30}
+!21 = !{!"VP", i32 0, i64 30, i64 666, i64 20}
+!22 = !{!"function_entry_count", i64 70}
+!23 = !{!"VP", i32 1, i64 70, i64 777, i64 50}
+!24 = !{!"function_entry_count", i64 40}
+!25 = !{!"function_entry_count", i64 60}
+!26 = !{!"branch_weights", i32 20, i32 50}
+!27 = !{!"function_entry_count", i64 30}
+!28 = !{!"function_entry_count", i64 70}
+!29 = !{!"VP", i32 0, i64 70, i64 888, i64 50}
+!30 = !{!"function_entry_count", i64 10}
+!31 = !{!"branch_weights", i32 1, i32 9}
+!32 = !{!"function_entry_count", i64 20}
+!33 = !{!"branch_weights", i32 3, i32 17}
+!34 = !{!"function_entry_count", i64 10}
+!35 = !{!"branch_weights", i32 1, i32 9}
+!36 = !{!"function_entry_count", i64 20}
+!37 = !{!"branch_weights", i32 3, i32 17}
+
+; CHECK-LABEL: define internal i32 @bw_asym_a(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF0:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF1:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 1
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 0
+;
+;
+; CHECK-LABEL: define internal i32 @bw_sym_a(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF2:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF3:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 4
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 5
+;
+;
+; CHECK-LABEL: define internal i32 @bw_g_only_a(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF4:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 7
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 8
+;
+;
+; CHECK-LABEL: define internal i32 @select_bw_a(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF5:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 0, !prof [[PROF6:![0-9]+]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+;
+; CHECK-LABEL: define internal i32 @vp_sym_a(
+; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF7:![0-9]+]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+;
+; CHECK-LABEL: define internal i32 @vp_asym_a(
+; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF5]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF8:![0-9]+]]
+; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 1
+; CHECK-NEXT: ret i32 [[ADJ]]
+;
+;
+; CHECK-LABEL: define internal i32 @vp_g_only_a(
+; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF9:![0-9]+]]
+; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 3
+; CHECK-NEXT: ret i32 [[ADJ]]
+;
+;
+; CHECK-LABEL: define internal i32 @vp_kind_mismatch_a(
+; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]]()
+; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 2
+; CHECK-NEXT: ret i32 [[ADJ]]
+;
+;
+; CHECK-LABEL: define dso_local i32 @thunk_path_a(
+; CHECK-SAME: i32 [[X:%.*]]) unnamed_addr !prof [[PROF10:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], 3
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF11:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 9
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 10
+;
+;
+; CHECK-LABEL: define private i32 @0(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF10]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 4
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF11]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 11
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 12
+;
+;
+; CHECK-LABEL: define i32 @use(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[FN:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[A:%.*]] = call i32 @bw_asym_a(i32 [[X]])
+; CHECK-NEXT: [[B:%.*]] = call i32 @bw_asym_a(i32 [[X]])
+; CHECK-NEXT: [[C:%.*]] = call i32 @bw_sym_a(i32 [[X]])
+; CHECK-NEXT: [[D:%.*]] = call i32 @bw_sym_a(i32 [[X]])
+; CHECK-NEXT: [[E:%.*]] = call i32 @bw_g_only_a(i32 [[X]])
+; CHECK-NEXT: [[F:%.*]] = call i32 @bw_g_only_a(i32 [[X]])
+; CHECK-NEXT: [[G:%.*]] = call i32 @select_bw_a(i32 [[X]])
+; CHECK-NEXT: [[H:%.*]] = call i32 @select_bw_a(i32 [[X]])
+; CHECK-NEXT: [[I:%.*]] = call i32 @vp_sym_a(ptr [[FN]])
+; CHECK-NEXT: [[J:%.*]] = call i32 @vp_sym_a(ptr [[FN]])
+; CHECK-NEXT: [[K:%.*]] = call i32 @vp_asym_a(ptr [[FN]])
+; CHECK-NEXT: [[L:%.*]] = call i32 @vp_asym_a(ptr [[FN]])
+; CHECK-NEXT: [[M:%.*]] = call i32 @vp_g_only_a(ptr [[FN]])
+; CHECK-NEXT: [[N:%.*]] = call i32 @vp_g_only_a(ptr [[FN]])
+; CHECK-NEXT: [[O:%.*]] = call i32 @vp_kind_mismatch_a(ptr [[FN]])
+; CHECK-NEXT: [[P:%.*]] = call i32 @vp_kind_mismatch_a(ptr [[FN]])
+; CHECK-NEXT: [[Q:%.*]] = call i32 @thunk_path_a(i32 [[X]])
+; CHECK-NEXT: [[R:%.*]] = call i32 @thunk_path_a(i32 [[X]])
+; CHECK-NEXT: [[S:%.*]] = call i32 @[[GLOB0:[0-9]+]](i32 [[X]])
+; CHECK-NEXT: [[T:%.*]] = call i32 @[[GLOB0]](i32 [[X]])
+; CHECK-NEXT: [[SUM0:%.*]] = add i32 [[A]], [[B]]
+; CHECK-NEXT: [[SUM1:%.*]] = add i32 [[SUM0]], [[C]]
+; CHECK-NEXT: [[SUM2:%.*]] = add i32 [[SUM1]], [[D]]
+; CHECK-NEXT: [[SUM3:%.*]] = add i32 [[SUM2]], [[E]]
+; CHECK-NEXT: [[SUM4:%.*]] = add i32 [[SUM3]], [[F]]
+; CHECK-NEXT: [[SUM5:%.*]] = add i32 [[SUM4]], [[G]]
+; CHECK-NEXT: [[SUM6:%.*]] = add i32 [[SUM5]], [[H]]
+; CHECK-NEXT: [[SUM7:%.*]] = add i32 [[SUM6]], [[I]]
+; CHECK-NEXT: [[SUM8:%.*]] = add i32 [[SUM7]], [[J]]
+; CHECK-NEXT: [[SUM9:%.*]] = add i32 [[SUM8]], [[K]]
+; CHECK-NEXT: [[SUM10:%.*]] = add i32 [[SUM9]], [[L]]
+; CHECK-NEXT: [[SUM11:%.*]] = add i32 [[SUM10]], [[M]]
+; CHECK-NEXT: [[SUM12:%.*]] = add i32 [[SUM11]], [[N]]
+; CHECK-NEXT: [[SUM13:%.*]] = add i32 [[SUM12]], [[O]]
+; CHECK-NEXT: [[SUM14:%.*]] = add i32 [[SUM13]], [[P]]
+; CHECK-NEXT: [[SUM15:%.*]] = add i32 [[SUM14]], [[Q]]
+; CHECK-NEXT: [[SUM16:%.*]] = add i32 [[SUM15]], [[R]]
+; CHECK-NEXT: [[SUM17:%.*]] = add i32 [[SUM16]], [[S]]
+; CHECK-NEXT: [[SUM18:%.*]] = add i32 [[SUM17]], [[T]]
+; CHECK-NEXT: ret i32 [[SUM18]]
+;
+;
+; CHECK-LABEL: define dso_local i32 @thunk_path_b(
+; CHECK-SAME: i32 [[TMP0:%.*]]) unnamed_addr !prof [[PROF12:![0-9]+]] {
+; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @thunk_path_a(i32 [[TMP0]])
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+;
+; CHECK-LABEL: define weak_odr i32 @odr_path_b(
+; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF12]] {
+; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @[[GLOB0]](i32 [[TMP0]])
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+;
+; CHECK-LABEL: define weak_odr i32 @odr_path_a(
+; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF13:![0-9]+]] {
+; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @[[GLOB0]](i32 [[TMP0]])
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+;.
+; CHECK: [[PROF0]] = !{!"function_entry_count", i64 300}
+; CHECK: [[PROF1]] = !{!"branch_weights", i32 70, i32 230}
+; CHECK: [[PROF2]] = !{!"function_entry_count", i64 100}
+; CHECK: [[PROF3]] = !{!"branch_weights", i32 36, i32 64}
+; CHECK: [[PROF4]] = !{!"branch_weights", i32 17, i32 43}
+; CHECK: [[PROF5]] = !{!"function_entry_count", i64 200}
+; CHECK: [[PROF6]] = !{!"branch_weights", i32 50, i32 150}
+; CHECK: [[PROF7]] = !{!"VP", i32 0, i64 300, i64 111, i64 180, i64 333, i64 80, i64 222, i64 40}
+; CHECK: [[PROF8]] = !{!"VP", i32 0, i64 80, i64 444, i64 50, i64 555, i64 30}
+; CHECK: [[PROF9]] = !{!"VP", i32 0, i64 50, i64 888, i64 50}
+; CHECK: [[PROF10]] = !{!"function_entry_count", i64 30}
+; CHECK: [[PROF11]] = !{!"branch_weights", i32 4, i32 26}
+; CHECK: [[PROF12]] = !{!"function_entry_count", i64 20}
+; CHECK: [[PROF13]] = !{!"function_entry_count", i64 10}
+;.
>From cd303e1b3353fd7a44354ee7efc94035fedc7b7d Mon Sep 17 00:00:00 2001
From: Alok Kumar Sharma <AlokKumar.Sharma at amd.com>
Date: Fri, 10 Jul 2026 12:32:02 +0530
Subject: [PATCH 2/4] Review comments.
---
.../llvm/Transforms/IPO/MergeFunctions.h | 2 +
llvm/lib/Transforms/IPO/MergeFunctions.cpp | 264 +++++-----
.../merge-functions-branch-weights.ll | 239 +++++++++
.../merge-functions-prof-metadata.ll | 460 ------------------
.../merge-functions-select-weights.ll | 50 ++
.../merge-functions-value-profile.ll | 143 ++++++
6 files changed, 558 insertions(+), 600 deletions(-)
create mode 100644 llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll
delete mode 100644 llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll
create mode 100644 llvm/test/Transforms/MergeFunc/merge-functions-select-weights.ll
create mode 100644 llvm/test/Transforms/MergeFunc/merge-functions-value-profile.ll
diff --git a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
index 218bd0702e1d2..e5fbf0ee6c77a 100644
--- a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
+++ b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
@@ -28,6 +28,8 @@ class MergeFunctionsPass : public OptionalPassInfoMixin<MergeFunctionsPass> {
public:
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+ /// When null, instruction-level profile merging scales using function entry
+ /// counts only. The NewPM path always provides FAM.
LLVM_ABI static bool runOnModule(Module &M,
FunctionAnalysisManager *FAM = nullptr);
LLVM_ABI static DenseMap<Function *, Function *>
diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index c78bb2b0f357f..a3dd33cefe6ec 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -97,13 +97,11 @@
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/BranchProbabilityInfo.h"
-#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DerivedTypes.h"
-#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/IRBuilder.h"
@@ -209,17 +207,14 @@ class FunctionNode {
/// bitcast of the other.
class MergeFunctions {
public:
- MergeFunctions() : FnTree(FunctionNodeCmp(&GlobalNumbers)) {
- }
+ explicit MergeFunctions(FunctionAnalysisManager *FAM = nullptr)
+ : FnTree(FunctionNodeCmp(&GlobalNumbers)), FAM(FAM) {}
template <typename FuncContainer> bool run(FuncContainer &Functions);
- DenseMap<Function *, Function *>
- runOnFunctions(ArrayRef<Function *> F, FunctionAnalysisManager *AM = nullptr);
+ DenseMap<Function *, Function *> runOnFunctions(ArrayRef<Function *> F);
SmallPtrSet<GlobalValue *, 4> &getUsed();
- void setFunctionAnalysisManager(FunctionAnalysisManager *AM) { FAM = AM; }
-
private:
// The function comparison operator is provided here so that FunctionNodes do
// not need to become larger with another pointer.
@@ -275,9 +270,9 @@ class MergeFunctions {
/// again.
void mergeTwoFunctions(Function *F, Function *G);
- const BlockFrequencyInfo &getBFI(Function &F);
+ const BlockFrequencyInfo* getBFI(Function &F);
- void mergeProfMetadataInto(Function *F, Function *G);
+ void mergeInstrProfMetadataInto(Function *Dst, Function *Src);
/// Fill PDIUnrelatedWL with instructions from the entry block that are
/// unrelated to parameter related debug info.
@@ -331,17 +326,6 @@ class MergeFunctions {
DenseMap<Function *, Function *> DelToNewMap;
FunctionAnalysisManager *FAM = nullptr;
-
- struct CachedProfileAnalyses {
- DominatorTree DT;
- LoopInfo LI;
- BranchProbabilityInfo BPI;
- BlockFrequencyInfo BFI;
-
- CachedProfileAnalyses(Function &F)
- : DT(F), LI(DT), BPI(F, LI, nullptr, &DT), BFI(F, BPI, LI) {}
- };
- DenseMap<const Function *, std::unique_ptr<CachedProfileAnalyses>> CachedBFI;
};
} // end anonymous namespace
@@ -356,9 +340,7 @@ PreservedAnalyses MergeFunctionsPass::run(Module &M,
SmallPtrSet<GlobalValue *, 4> &MergeFunctions::getUsed() { return Used; }
bool MergeFunctionsPass::runOnModule(Module &M, FunctionAnalysisManager *FAM) {
- MergeFunctions MF;
- if (FAM)
- MF.setFunctionAnalysisManager(FAM);
+ MergeFunctions MF(FAM);
SmallVector<GlobalValue *, 4> UsedV;
collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/false);
collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/true);
@@ -369,8 +351,8 @@ bool MergeFunctionsPass::runOnModule(Module &M, FunctionAnalysisManager *FAM) {
DenseMap<Function *, Function *>
MergeFunctionsPass::runOnFunctions(ArrayRef<Function *> F,
FunctionAnalysisManager *FAM) {
- MergeFunctions MF;
- return MF.runOnFunctions(F, FAM);
+ MergeFunctions MF(FAM);
+ return MF.runOnFunctions(F);
}
#ifndef NDEBUG
@@ -534,10 +516,7 @@ template <typename FuncContainer> bool MergeFunctions::run(FuncContainer &M) {
}
DenseMap<Function *, Function *>
-MergeFunctions::runOnFunctions(ArrayRef<Function *> F,
- FunctionAnalysisManager *AM) {
- if (AM)
- setFunctionAnalysisManager(AM);
+MergeFunctions::runOnFunctions(ArrayRef<Function *> F) {
[[maybe_unused]] bool MergeResult = this->run(F);
assert(MergeResult == !DelToNewMap.empty());
return this->DelToNewMap;
@@ -778,7 +757,7 @@ static void copyMetadataIfPresent(Function *From, Function *To,
// For better debugability, under MergeFunctionsPDI, we do not modify G's
// call sites to point to F even when within the same translation unit.
void MergeFunctions::writeThunk(Function *F, Function *G) {
- std::optional<uint64_t> GEC = G->getEntryCount();
+ std::optional<uint64_t> GEntryCount = G->getEntryCount();
BasicBlock *GEntryBlock = nullptr;
std::vector<Instruction *> PDIUnrelatedWL;
std::vector<DbgVariableRecord *> PDVRUnrelatedWL;
@@ -848,8 +827,8 @@ void MergeFunctions::writeThunk(Function *F, Function *G) {
<< G->getName() << "()\n");
} else {
NewG->copyAttributesFrom(G);
- if (GEC)
- NewG->setEntryCount(*GEC);
+ if (GEntryCount)
+ NewG->setEntryCount(*GEntryCount);
NewG->takeName(G);
// Ensure CFI type metadata is propagated to the new function.
copyMetadataIfPresent(G, NewG, "type");
@@ -915,7 +894,7 @@ bool MergeFunctions::writeThunkOrAliasIfNeeded(Function *F, Function *G,
return false;
if (MergeProfile)
- mergeProfMetadataInto(F, G);
+ mergeInstrProfMetadataInto(F, G);
if (ShouldErase) {
G->eraseFromParent();
@@ -963,45 +942,52 @@ static uint64_t scaleToBlockCount(uint64_t Weight, uint64_t TotalWeight,
Num *= APInt(128, Weight);
APInt Den(128, TotalWeight);
Num = (Num + Den.lshr(1)).udiv(Den);
+ assert(Num.getActiveBits() <= 64 &&
+ "scaleToBlockCount: result exceeds uint64_t; Weight > TotalWeight?");
return Num.getLimitedValue();
}
// Combine the scaled branch_weights of corresponding instructions of F and G.
-static void mergeBranchWeightsOnInstructions(Instruction *FI,
- const Instruction *GI,
- const BlockFrequencyInfo *BfiF,
- const BlockFrequencyInfo *BfiG,
- std::optional<uint64_t> FEC,
- std::optional<uint64_t> GEC) {
- SmallVector<uint32_t, 8> FWeights, GWeights;
- bool HasF = extractBranchWeights(*FI, FWeights);
- bool HasG = extractBranchWeights(*GI, GWeights);
- if (!HasF && !HasG)
+static void
+mergeBranchWeightsOnInstructions(Instruction *DstI, const Instruction *SrcI,
+ const BlockFrequencyInfo *DstBFI,
+ const BlockFrequencyInfo *SrcBFI,
+ std::optional<uint64_t> DstEntryCount,
+ std::optional<uint64_t> SrcEntryCount) {
+ SmallVector<uint32_t, 8> DstWeights, SrcWeights;
+ bool HasDst = extractBranchWeights(*DstI, DstWeights);
+ bool HasSrc = extractBranchWeights(*SrcI, SrcWeights);
+ if (!HasDst && !HasSrc)
return;
- uint64_t BlockCountF = getBlockCountForMerging(BfiF, FI->getParent(), FEC);
- uint64_t BlockCountG = getBlockCountForMerging(BfiG, GI->getParent(), GEC);
+ uint64_t DstBlockCount =
+ getBlockCountForMerging(DstBFI, DstI->getParent(), DstEntryCount);
+ uint64_t SrcBlockCount =
+ getBlockCountForMerging(SrcBFI, SrcI->getParent(), SrcEntryCount);
- uint64_t TotalF = 0, TotalG = 0;
- if (HasF)
- extractProfTotalWeight(*FI, TotalF);
- if (HasG)
- extractProfTotalWeight(*GI, TotalG);
+ uint64_t DstTotal = 0, SrcTotal = 0;
+ if (HasDst)
+ extractProfTotalWeight(*DstI, DstTotal);
+ if (HasSrc)
+ extractProfTotalWeight(*SrcI, SrcTotal);
- size_t NumWeights = std::max(HasF ? FWeights.size() : size_t{0},
- HasG ? GWeights.size() : size_t{0});
+ size_t NumWeights = std::max(HasDst ? DstWeights.size() : size_t{0},
+ HasSrc ? SrcWeights.size() : size_t{0});
SmallVector<uint64_t, 8> MergedWeights;
MergedWeights.reserve(NumWeights);
for (size_t I = 0; I < NumWeights; ++I) {
- uint64_t FW = (HasF && I < FWeights.size()) ? FWeights[I] : 0;
- uint64_t GW = (HasG && I < GWeights.size()) ? GWeights[I] : 0;
- uint64_t AbsF = HasF ? scaleToBlockCount(FW, TotalF, BlockCountF) : 0;
- uint64_t AbsG = HasG ? scaleToBlockCount(GW, TotalG, BlockCountG) : 0;
- MergedWeights.push_back(SaturatingAdd(AbsF, AbsG));
+ uint64_t DstW = (HasDst && I < DstWeights.size()) ? DstWeights[I] : 0;
+ uint64_t SrcW = (HasSrc && I < SrcWeights.size()) ? SrcWeights[I] : 0;
+ uint64_t DstAbs =
+ HasDst ? scaleToBlockCount(DstW, DstTotal, DstBlockCount) : 0;
+ uint64_t SrcAbs =
+ HasSrc ? scaleToBlockCount(SrcW, SrcTotal, SrcBlockCount) : 0;
+ MergedWeights.push_back(SaturatingAdd(DstAbs, SrcAbs));
}
- bool IsExpected = hasBranchWeightOrigin(*FI) || hasBranchWeightOrigin(*GI);
- setFittedBranchWeights(*FI, MergedWeights, IsExpected);
+ bool IsExpected =
+ hasBranchWeightOrigin(*DstI) && hasBranchWeightOrigin(*SrcI);
+ setFittedBranchWeights(*DstI, MergedWeights, IsExpected);
}
// Accumulate scaled value profile counts of Instruction I into Merged.
@@ -1019,33 +1005,35 @@ static void addScaledValueProfile(const Instruction &I, InstrProfValueKind Kind,
}
}
-// Merge (union) scaled value profiles of F and G.
-static void mergeValueProfileOnInstructions(Instruction *FI,
- const Instruction *GI,
- const BlockFrequencyInfo *BfiF,
- const BlockFrequencyInfo *BfiG,
- std::optional<uint64_t> FEC,
- std::optional<uint64_t> GEC) {
- MDNode *ProfF = FI->getMetadata(LLVMContext::MD_prof);
- MDNode *ProfG = GI->getMetadata(LLVMContext::MD_prof);
- bool HasF = ProfF && isValueProfileMD(ProfF);
- bool HasG = ProfG && isValueProfileMD(ProfG);
- if (!HasF && !HasG)
+// Merge (union) scaled value profiles of Dst and Src.
+static void
+mergeValueProfileOnInstructions(Instruction *DstI, const Instruction *SrcI,
+ const BlockFrequencyInfo *DstBFI,
+ const BlockFrequencyInfo *SrcBFI,
+ std::optional<uint64_t> DstEntryCount,
+ std::optional<uint64_t> SrcEntryCount) {
+ MDNode *DstProf = DstI->getMetadata(LLVMContext::MD_prof);
+ MDNode *SrcProf = SrcI->getMetadata(LLVMContext::MD_prof);
+ bool HasDst = DstProf && isValueProfileMD(DstProf);
+ bool HasSrc = SrcProf && isValueProfileMD(SrcProf);
+ if (!HasDst && !HasSrc)
return;
- auto *KindF =
- HasF ? mdconst::dyn_extract<ConstantInt>(ProfF->getOperand(1)) : nullptr;
- auto *KindG =
- HasG ? mdconst::dyn_extract<ConstantInt>(ProfG->getOperand(1)) : nullptr;
- if (HasF && HasG && KindF && KindG &&
- KindF->getZExtValue() != KindG->getZExtValue()) {
- FI->setMetadata(LLVMContext::MD_prof, nullptr);
+ auto *DstKind =
+ HasDst ? mdconst::dyn_extract<ConstantInt>(DstProf->getOperand(1))
+ : nullptr;
+ auto *SrcKind =
+ HasSrc ? mdconst::dyn_extract<ConstantInt>(SrcProf->getOperand(1))
+ : nullptr;
+ if (HasDst && HasSrc && DstKind && SrcKind &&
+ DstKind->getZExtValue() != SrcKind->getZExtValue()) {
+ DstI->setMetadata(LLVMContext::MD_prof, nullptr);
return;
}
- const ConstantInt *KindCI = KindF ? KindF : KindG;
+ const ConstantInt *KindCI = DstKind ? DstKind : SrcKind;
if (!KindCI) {
- FI->setMetadata(LLVMContext::MD_prof, nullptr);
+ DstI->setMetadata(LLVMContext::MD_prof, nullptr);
return;
}
@@ -1053,12 +1041,14 @@ static void mergeValueProfileOnInstructions(Instruction *FI,
static_cast<InstrProfValueKind>(KindCI->getZExtValue());
DenseMap<uint64_t, uint64_t> Merged;
- uint64_t BCF = getBlockCountForMerging(BfiF, FI->getParent(), FEC);
- uint64_t BCG = getBlockCountForMerging(BfiG, GI->getParent(), GEC);
- if (HasF)
- addScaledValueProfile(*FI, Kind, BCF, Merged);
- if (HasG)
- addScaledValueProfile(*GI, Kind, BCG, Merged);
+ uint64_t DstBlockCount =
+ getBlockCountForMerging(DstBFI, DstI->getParent(), DstEntryCount);
+ uint64_t SrcBlockCount =
+ getBlockCountForMerging(SrcBFI, SrcI->getParent(), SrcEntryCount);
+ if (HasDst)
+ addScaledValueProfile(*DstI, Kind, DstBlockCount, Merged);
+ if (HasSrc)
+ addScaledValueProfile(*SrcI, Kind, SrcBlockCount, Merged);
if (Merged.empty())
return;
@@ -1073,62 +1063,55 @@ static void mergeValueProfileOnInstructions(Instruction *FI,
llvm::sort(VDs, [](const InstrProfValueData &A, const InstrProfValueData &B) {
return A.Count > B.Count;
});
- annotateValueSite(*FI->getFunction()->getParent(), *FI, VDs, Sum, Kind,
+ annotateValueSite(*DstI->getFunction()->getParent(), *DstI, VDs, Sum, Kind,
VDs.size());
}
-const BlockFrequencyInfo &MergeFunctions::getBFI(Function &F) {
- if (FAM)
- return FAM->getResult<BlockFrequencyAnalysis>(F);
- auto &Entry = CachedBFI[&F];
- if (!Entry)
- Entry = std::make_unique<CachedProfileAnalyses>(F);
- return Entry->BFI;
+const BlockFrequencyInfo *MergeFunctions::getBFI(Function &F) {
+ if (!FAM)
+ return nullptr;
+
+ return &FAM->getResult<BlockFrequencyAnalysis>(F);
}
-void MergeFunctions::mergeProfMetadataInto(Function *F, Function *G) {
- std::optional<uint64_t> FEC = F->getEntryCount();
- std::optional<uint64_t> GEC = G->getEntryCount();
- const BlockFrequencyInfo *BfiF = &getBFI(*F);
- const BlockFrequencyInfo *BfiG = &getBFI(*G);
-
- SmallVector<std::pair<BasicBlock *, const BasicBlock *>, 8> BBWorklist;
- SmallPtrSet<const BasicBlock *, 32> VisitedBBs;
-
- BBWorklist.emplace_back(&F->getEntryBlock(), &G->getEntryBlock());
- VisitedBBs.insert(BBWorklist.back().first);
-
- while (!BBWorklist.empty()) {
- auto [BBF, BBG] = BBWorklist.pop_back_val();
-
- for (auto [FI, GI] : llvm::zip(*BBF, *BBG)) {
- MDNode *ProfF = FI.getMetadata(LLVMContext::MD_prof);
- MDNode *ProfG = GI.getMetadata(LLVMContext::MD_prof);
- if ((ProfF && isValueProfileMD(ProfF)) ||
- (ProfG && isValueProfileMD(ProfG)))
- mergeValueProfileOnInstructions(&FI, &GI, BfiF, BfiG, FEC, GEC);
- if (isa<SelectInst>(FI))
- mergeBranchWeightsOnInstructions(&FI, &GI, BfiF, BfiG, FEC, GEC);
- }
-
- Instruction *TermF = BBF->getTerminator();
- const Instruction *TermG = BBG->getTerminator();
- mergeBranchWeightsOnInstructions(TermF, TermG, BfiF, BfiG, FEC, GEC);
- for (unsigned I = 0, E = TermF->getNumSuccessors(); I != E; ++I) {
- if (!VisitedBBs.insert(TermF->getSuccessor(I)).second)
- continue;
- BBWorklist.emplace_back(TermF->getSuccessor(I), TermG->getSuccessor(I));
+/// Merge \p Src's instruction-level branch weights and value profile
+/// metadata into the corresponding instructions of \p Dst. \p Dst is the
+/// surviving function; \p Src will be erased or rewritten after this call.
+/// Both functions must be structurally identical.
+void MergeFunctions::mergeInstrProfMetadataInto(Function *Dst, Function *Src) {
+ std::optional<uint64_t> DstEntryCount = Dst->getEntryCount();
+ std::optional<uint64_t> SrcEntryCount = Src->getEntryCount();
+ const BlockFrequencyInfo *DstBFI = getBFI(*Dst);
+ const BlockFrequencyInfo *SrcBFI = getBFI(*Src);
+
+ // FunctionComparator guarantees identical CFG topology and instruction
+ // ordering, allowing us to walk both functions in lockstep.
+ for (auto [DstBB, SrcBB] : llvm::zip_equal(*Dst, *Src)) {
+ for (auto [DstI, SrcI] : llvm::zip_equal(DstBB, SrcBB)) {
+ MDNode *DstProf = DstI.getMetadata(LLVMContext::MD_prof);
+ MDNode *SrcProf = SrcI.getMetadata(LLVMContext::MD_prof);
+ if ((DstProf && isValueProfileMD(DstProf)) ||
+ (SrcProf && isValueProfileMD(SrcProf)))
+ mergeValueProfileOnInstructions(&DstI, &SrcI, DstBFI, SrcBFI,
+ DstEntryCount, SrcEntryCount);
+
+ // Handle branch weights on SelectInsts here. Terminators are handled
+ // separately below, outside the instruction loop.
+ if (isa<SelectInst>(DstI))
+ mergeBranchWeightsOnInstructions(&DstI, &SrcI, DstBFI, SrcBFI,
+ DstEntryCount, SrcEntryCount);
}
+ Instruction *DstTerm = DstBB.getTerminator();
+ const Instruction *SrcTerm = SrcBB.getTerminator();
+ mergeBranchWeightsOnInstructions(DstTerm, SrcTerm, DstBFI, SrcBFI,
+ DstEntryCount, SrcEntryCount);
}
if (FAM) {
PreservedAnalyses PA = PreservedAnalyses::all();
PA.abandon<BranchProbabilityAnalysis>();
PA.abandon<BlockFrequencyAnalysis>();
- FAM->invalidate(*F, PA);
- } else {
- CachedBFI.erase(F);
- CachedBFI.erase(G);
+ FAM->invalidate(*Dst, PA);
}
}
@@ -1143,8 +1126,8 @@ static void mergeEntryCountsInto(Function *F, std::optional<uint64_t> FC,
// Merge two equivalent functions. Upon completion, Function G is deleted.
void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
- std::optional<uint64_t> FEC = F->getEntryCount();
- std::optional<uint64_t> GEC = G->getEntryCount();
+ std::optional<uint64_t> FEntryCount = F->getEntryCount();
+ std::optional<uint64_t> GEntryCount = G->getEntryCount();
// 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
@@ -1189,8 +1172,8 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
// Merge !prof, while G still has its body.
writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true);
- if (FEC)
- NewF->setEntryCount(*FEC);
+ if (FEntryCount)
+ NewF->setEntryCount(*FEntryCount);
// NewF becomes thunk/alias to the shared body F, it has no profile to be
// merged.
writeThunkOrAliasIfNeeded(F, NewF, /*MergeProfile*/ false);
@@ -1201,8 +1184,9 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
F->setAlignment(std::nullopt);
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);
+ // (FEntryCount + GEntryCount), while each ODR thunk retains its own
+ // per-symbol entry count.
+ mergeEntryCountsInto(F, FEntryCount, GEntryCount);
++NumDoubleWeak;
++NumFunctionsMerged;
} else {
@@ -1230,15 +1214,15 @@ 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) {
- mergeProfMetadataInto(F, G);
- mergeEntryCountsInto(F, FEC, GEC);
+ mergeInstrProfMetadataInto(F, G);
+ mergeEntryCountsInto(F, FEntryCount, GEntryCount);
G->eraseFromParent();
++NumFunctionsMerged;
return;
}
if (writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true)) {
- mergeEntryCountsInto(F, FEC, GEC);
+ mergeEntryCountsInto(F, FEntryCount, GEntryCount);
++NumFunctionsMerged;
}
}
diff --git a/llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll b/llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll
new file mode 100644
index 0000000000000..46a80c2916766
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll
@@ -0,0 +1,239 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 6
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+
+; Verify that MergeFunctions correctly combines branch_weights on
+; conditional branches when merging structurally identical functions.
+; Because FunctionComparator only matches functions whose control-flow
+; structure and instruction sequence are identical, MergeFunctions can
+; process both functions side by side and merge any instruction-level
+; metadata, including branch weight information.
+; To compute the merged weights, the pass converts each function's
+; relative branch weights into absolute execution counts using the
+; function's entry count, then adds the corresponding counts together.
+
+; --------------------------------------------------------------------------
+; Both functions provide branch weight metadata.
+; br_both_a: entry count = 100, weights = 10:90
+; absolute counts: true = 10, false = 90
+; br_both_b: entry count = 200, weights = 30:70
+; absolute counts: true = 60, false = 140
+; After merging:
+; true = 10 + 60 = 70
+; false = 90 + 140 = 230
+; --------------------------------------------------------------------------
+define internal i32 @br_both_a(i32 %x) !prof !1 {
+entry:
+ %cmp = icmp slt i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !2
+
+then:
+ ret i32 1
+
+else:
+ ret i32 0
+}
+
+define internal i32 @br_both_b(i32 %x) !prof !3 {
+entry:
+ %cmp = icmp slt i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !4
+
+then:
+ ret i32 1
+
+else:
+ ret i32 0
+}
+
+; --------------------------------------------------------------------------
+; Only one of the functions provides branch weight metadata.
+; br_one_a has an entry count of 40 but does not specify any
+; branch_weights.
+; br_one_b has an entry count of 60 with branch weights of 40:80,
+; which correspond to absolute counts of true = 20 and false = 40.
+; Since only br_one_b contributes branch weight information, the
+; merged branch retains those counts:
+; true = 20
+; false = 40
+; --------------------------------------------------------------------------
+define internal i32 @br_one_a(i32 %x) !prof !5 {
+entry:
+ %cmp = icmp sgt i32 %x, 0
+ br i1 %cmp, label %then, label %else
+
+then:
+ ret i32 7
+
+else:
+ ret i32 8
+}
+
+define internal i32 @br_one_b(i32 %x) !prof !6 {
+entry:
+ %cmp = icmp sgt i32 %x, 0
+ br i1 %cmp, label %then, label %else, !prof !7
+
+then:
+ ret i32 7
+
+else:
+ ret i32 8
+}
+
+; --------------------------------------------------------------------------
+; Thunk case: two dso_local functions are merged through a thunk, and
+; their branch weight metadata is combined as part of the merge.
+; thunk_a has an entry count of 10 with branch weights of 1:9,
+; resulting in absolute counts of true = 1 and false = 9.
+; thunk_b has an entry count of 20 with branch weights of 3:17,
+; resulting in absolute counts of true = 3 and false = 17.
+; After merging the two functions, the corresponding counts are added:
+; true = 1 + 3 = 4
+; false = 9 + 17 = 26
+; --------------------------------------------------------------------------
+define dso_local i32 @thunk_a(i32 %x) unnamed_addr !prof !8 {
+entry:
+ %cmp = icmp ult i32 %x, 3
+ br i1 %cmp, label %then, label %else, !prof !9
+
+then:
+ ret i32 9
+
+else:
+ ret i32 10
+}
+
+define dso_local i32 @thunk_b(i32 %x) unnamed_addr !prof !10 {
+entry:
+ %cmp = icmp ult i32 %x, 3
+ br i1 %cmp, label %then, label %else, !prof !11
+
+then:
+ ret i32 9
+
+else:
+ ret i32 10
+}
+
+; --------------------------------------------------------------------------
+; ODR case: two weak_odr functions are merged into a single shared
+; implementation, and their branch weight metadata is merged as well.
+; odr_a has an entry count of 100 with branch weights of 1:9,
+; corresponding to absolute counts of true = 10 and false = 90.
+; odr_b has an entry count of 200 with branch weights of 3:17,
+; corresponding to absolute counts of true = 30 and false = 170.
+; The merged function combines the counts from both inputs:
+; true = 10 + 30 = 40
+; false = 90 + 170 = 260
+; --------------------------------------------------------------------------
+define weak_odr i32 @odr_a(i32 %x) !prof !12 {
+entry:
+ %cmp = icmp ugt i32 %x, 4
+ br i1 %cmp, label %then, label %else, !prof !13
+
+then:
+ ret i32 11
+
+else:
+ ret i32 12
+}
+
+define weak_odr i32 @odr_b(i32 %x) !prof !14 {
+entry:
+ %cmp = icmp ugt i32 %x, 4
+ br i1 %cmp, label %then, label %else, !prof !15
+
+then:
+ ret i32 11
+
+else:
+ ret i32 12
+}
+
+!1 = !{!"function_entry_count", i64 100}
+!2 = !{!"branch_weights", i32 10, i32 90}
+!3 = !{!"function_entry_count", i64 200}
+!4 = !{!"branch_weights", i32 30, i32 70}
+!5 = !{!"function_entry_count", i64 40}
+!6 = !{!"function_entry_count", i64 60}
+!7 = !{!"branch_weights", i32 40, i32 80}
+!8 = !{!"function_entry_count", i64 10}
+!9 = !{!"branch_weights", i32 1, i32 9}
+!10 = !{!"function_entry_count", i64 20}
+!11 = !{!"branch_weights", i32 3, i32 17}
+!12 = !{!"function_entry_count", i64 100}
+!13 = !{!"branch_weights", i32 1, i32 9}
+!14 = !{!"function_entry_count", i64 200}
+!15 = !{!"branch_weights", i32 3, i32 17}
+; CHECK-LABEL: define internal i32 @br_both_a(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF0:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF1:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 1
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 0
+;
+;
+; CHECK-LABEL: define internal i32 @br_one_a(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF2:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF3:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 7
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 8
+;
+;
+; CHECK-LABEL: define dso_local i32 @thunk_a(
+; CHECK-SAME: i32 [[X:%.*]]) unnamed_addr !prof [[PROF4:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], 3
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF5:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 9
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 10
+;
+;
+; CHECK-LABEL: define private i32 @0(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 4
+; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF6:![0-9]+]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret i32 11
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret i32 12
+;
+;
+; CHECK-LABEL: define dso_local i32 @thunk_b(
+; CHECK-SAME: i32 [[TMP0:%.*]]) unnamed_addr !prof [[PROF7:![0-9]+]] {
+; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @thunk_a(i32 [[TMP0]])
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+;
+; CHECK-LABEL: define weak_odr i32 @odr_b(
+; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF8:![0-9]+]] {
+; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @[[GLOB0:[0-9]+]](i32 [[TMP0]])
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+;
+; CHECK-LABEL: define weak_odr i32 @odr_a(
+; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF2]] {
+; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @[[GLOB0]](i32 [[TMP0]])
+; CHECK-NEXT: ret i32 [[TMP2]]
+;
+;.
+; CHECK: [[PROF0]] = !{!"function_entry_count", i64 300}
+; CHECK: [[PROF1]] = !{!"branch_weights", i32 70, i32 230}
+; CHECK: [[PROF2]] = !{!"function_entry_count", i64 100}
+; CHECK: [[PROF3]] = !{!"branch_weights", i32 20, i32 40}
+; CHECK: [[PROF4]] = !{!"function_entry_count", i64 30}
+; CHECK: [[PROF5]] = !{!"branch_weights", i32 4, i32 26}
+; CHECK: [[PROF6]] = !{!"branch_weights", i32 40, i32 260}
+; CHECK: [[PROF7]] = !{!"function_entry_count", i64 20}
+; CHECK: [[PROF8]] = !{!"function_entry_count", i64 200}
+;.
diff --git a/llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll b/llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll
deleted file mode 100644
index 278ded8693662..0000000000000
--- a/llvm/test/Transforms/MergeFunc/merge-functions-prof-metadata.ll
+++ /dev/null
@@ -1,460 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 6
-; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
-
-; Check mergefunc combines branch/value profile data.
-
-define internal i32 @bw_asym_a(i32 %x) !prof !1 {
-entry:
- %cmp = icmp slt i32 %x, 0
- br i1 %cmp, label %then, label %else, !prof !2
-
-then:
- ret i32 1
-
-else:
- ret i32 0
-}
-
-define internal i32 @bw_asym_b(i32 %x) !prof !3 {
-entry:
- %cmp = icmp slt i32 %x, 0
- br i1 %cmp, label %then, label %else, !prof !4
-
-then:
- ret i32 1
-
-else:
- ret i32 0
-}
-
-define internal i32 @bw_sym_a(i32 %x) !prof !5 {
-entry:
- %cmp = icmp ne i32 %x, 0
- br i1 %cmp, label %then, label %else, !prof !6
-
-then:
- ret i32 4
-
-else:
- ret i32 5
-}
-
-define internal i32 @bw_sym_b(i32 %x) !prof !7 {
-entry:
- %cmp = icmp ne i32 %x, 0
- br i1 %cmp, label %then, label %else, !prof !8
-
-then:
- ret i32 4
-
-else:
- ret i32 5
-}
-
-define internal i32 @bw_g_only_a(i32 %x) !prof !24 {
-entry:
- %cmp = icmp sgt i32 %x, 0
- br i1 %cmp, label %then, label %else
-
-then:
- ret i32 7
-
-else:
- ret i32 8
-}
-
-define internal i32 @bw_g_only_b(i32 %x) !prof !25 {
-entry:
- %cmp = icmp sgt i32 %x, 0
- br i1 %cmp, label %then, label %else, !prof !26
-
-then:
- ret i32 7
-
-else:
- ret i32 8
-}
-
-define internal i32 @select_bw_a(i32 %x) !prof !9 {
-entry:
- %cmp = icmp slt i32 %x, 0
- %sel = select i1 %cmp, i32 1, i32 0, !prof !10
- ret i32 %sel
-}
-
-define internal i32 @select_bw_b(i32 %x) !prof !11 {
-entry:
- %cmp = icmp slt i32 %x, 0
- %sel = select i1 %cmp, i32 1, i32 0, !prof !12
- ret i32 %sel
-}
-
-define internal i32 @vp_sym_a(ptr %fn) !prof !13 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val(), !prof !14
- ret i32 %r
-}
-
-define internal i32 @vp_sym_b(ptr %fn) !prof !15 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val(), !prof !16
- ret i32 %r
-}
-
-; add i32 1 keeps @vp_asym_* distinct from @vp_sym_*.
-define internal i32 @vp_asym_a(ptr %fn) !prof !17 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val(), !prof !18
- %adj = add i32 %r, 1
- ret i32 %adj
-}
-
-define internal i32 @vp_asym_b(ptr %fn) !prof !19 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val()
- %adj = add i32 %r, 1
- ret i32 %adj
-}
-
-define internal i32 @vp_g_only_a(ptr %fn) !prof !27 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val()
- %adj = add i32 %r, 3
- ret i32 %adj
-}
-
-define internal i32 @vp_g_only_b(ptr %fn) !prof !28 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val(), !prof !29
- %adj = add i32 %r, 3
- ret i32 %adj
-}
-
-define internal i32 @vp_kind_mismatch_a(ptr %fn) !prof !20 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val(), !prof !21
- %adj = add i32 %r, 2
- ret i32 %adj
-}
-
-define internal i32 @vp_kind_mismatch_b(ptr %fn) !prof !22 {
-entry:
- %fn_val = load ptr, ptr %fn
- %r = call i32 %fn_val(), !prof !23
- %adj = add i32 %r, 2
- ret i32 %adj
-}
-
-define dso_local i32 @thunk_path_a(i32 %x) unnamed_addr !prof !30 {
-entry:
- %cmp = icmp ult i32 %x, 3
- br i1 %cmp, label %then, label %else, !prof !31
-
-then:
- ret i32 9
-
-else:
- ret i32 10
-}
-
-define dso_local i32 @thunk_path_b(i32 %x) unnamed_addr !prof !32 {
-entry:
- %cmp = icmp ult i32 %x, 3
- br i1 %cmp, label %then, label %else, !prof !33
-
-then:
- ret i32 9
-
-else:
- ret i32 10
-}
-
-define weak_odr i32 @odr_path_a(i32 %x) !prof !34 {
-entry:
- %cmp = icmp ugt i32 %x, 4
- br i1 %cmp, label %then, label %else, !prof !35
-
-then:
- ret i32 11
-
-else:
- ret i32 12
-}
-
-define weak_odr i32 @odr_path_b(i32 %x) !prof !36 {
-entry:
- %cmp = icmp ugt i32 %x, 4
- br i1 %cmp, label %then, label %else, !prof !37
-
-then:
- ret i32 11
-
-else:
- ret i32 12
-}
-
-define i32 @use(i32 %x, ptr %fn) {
-entry:
- %a = call i32 @bw_asym_a(i32 %x)
- %b = call i32 @bw_asym_b(i32 %x)
- %c = call i32 @bw_sym_a(i32 %x)
- %d = call i32 @bw_sym_b(i32 %x)
- %e = call i32 @bw_g_only_a(i32 %x)
- %f = call i32 @bw_g_only_b(i32 %x)
- %g = call i32 @select_bw_a(i32 %x)
- %h = call i32 @select_bw_b(i32 %x)
- %i = call i32 @vp_sym_a(ptr %fn)
- %j = call i32 @vp_sym_b(ptr %fn)
- %k = call i32 @vp_asym_a(ptr %fn)
- %l = call i32 @vp_asym_b(ptr %fn)
- %m = call i32 @vp_g_only_a(ptr %fn)
- %n = call i32 @vp_g_only_b(ptr %fn)
- %o = call i32 @vp_kind_mismatch_a(ptr %fn)
- %p = call i32 @vp_kind_mismatch_b(ptr %fn)
- %q = call i32 @thunk_path_a(i32 %x)
- %r = call i32 @thunk_path_b(i32 %x)
- %s = call i32 @odr_path_a(i32 %x)
- %t = call i32 @odr_path_b(i32 %x)
- %sum0 = add i32 %a, %b
- %sum1 = add i32 %sum0, %c
- %sum2 = add i32 %sum1, %d
- %sum3 = add i32 %sum2, %e
- %sum4 = add i32 %sum3, %f
- %sum5 = add i32 %sum4, %g
- %sum6 = add i32 %sum5, %h
- %sum7 = add i32 %sum6, %i
- %sum8 = add i32 %sum7, %j
- %sum9 = add i32 %sum8, %k
- %sum10 = add i32 %sum9, %l
- %sum11 = add i32 %sum10, %m
- %sum12 = add i32 %sum11, %n
- %sum13 = add i32 %sum12, %o
- %sum14 = add i32 %sum13, %p
- %sum15 = add i32 %sum14, %q
- %sum16 = add i32 %sum15, %r
- %sum17 = add i32 %sum16, %s
- %sum18 = add i32 %sum17, %t
- ret i32 %sum18
-}
-
-!1 = !{!"function_entry_count", i64 100}
-!2 = !{!"branch_weights", i32 10, i32 90}
-!3 = !{!"function_entry_count", i64 200}
-!4 = !{!"branch_weights", i32 30, i32 70}
-!5 = !{!"function_entry_count", i64 40}
-!6 = !{!"branch_weights", i32 60, i32 40}
-!7 = !{!"function_entry_count", i64 60}
-!8 = !{!"branch_weights", i32 20, i32 80}
-!9 = !{!"function_entry_count", i64 50}
-!10 = !{!"branch_weights", i32 10, i32 90}
-!11 = !{!"function_entry_count", i64 150}
-!12 = !{!"branch_weights", i32 30, i32 70}
-!13 = !{!"function_entry_count", i64 100}
-!14 = !{!"VP", i32 0, i64 100, i64 111, i64 60, i64 222, i64 40}
-!15 = !{!"function_entry_count", i64 200}
-!16 = !{!"VP", i32 0, i64 200, i64 111, i64 120, i64 333, i64 80}
-!17 = !{!"function_entry_count", i64 80}
-!18 = !{!"VP", i32 0, i64 80, i64 444, i64 50, i64 555, i64 30}
-!19 = !{!"function_entry_count", i64 120}
-!20 = !{!"function_entry_count", i64 30}
-!21 = !{!"VP", i32 0, i64 30, i64 666, i64 20}
-!22 = !{!"function_entry_count", i64 70}
-!23 = !{!"VP", i32 1, i64 70, i64 777, i64 50}
-!24 = !{!"function_entry_count", i64 40}
-!25 = !{!"function_entry_count", i64 60}
-!26 = !{!"branch_weights", i32 20, i32 50}
-!27 = !{!"function_entry_count", i64 30}
-!28 = !{!"function_entry_count", i64 70}
-!29 = !{!"VP", i32 0, i64 70, i64 888, i64 50}
-!30 = !{!"function_entry_count", i64 10}
-!31 = !{!"branch_weights", i32 1, i32 9}
-!32 = !{!"function_entry_count", i64 20}
-!33 = !{!"branch_weights", i32 3, i32 17}
-!34 = !{!"function_entry_count", i64 10}
-!35 = !{!"branch_weights", i32 1, i32 9}
-!36 = !{!"function_entry_count", i64 20}
-!37 = !{!"branch_weights", i32 3, i32 17}
-
-; CHECK-LABEL: define internal i32 @bw_asym_a(
-; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF0:![0-9]+]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF1:![0-9]+]]
-; CHECK: [[THEN]]:
-; CHECK-NEXT: ret i32 1
-; CHECK: [[ELSE]]:
-; CHECK-NEXT: ret i32 0
-;
-;
-; CHECK-LABEL: define internal i32 @bw_sym_a(
-; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF2:![0-9]+]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[X]], 0
-; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF3:![0-9]+]]
-; CHECK: [[THEN]]:
-; CHECK-NEXT: ret i32 4
-; CHECK: [[ELSE]]:
-; CHECK-NEXT: ret i32 5
-;
-;
-; CHECK-LABEL: define internal i32 @bw_g_only_a(
-; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF2]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 0
-; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF4:![0-9]+]]
-; CHECK: [[THEN]]:
-; CHECK-NEXT: ret i32 7
-; CHECK: [[ELSE]]:
-; CHECK-NEXT: ret i32 8
-;
-;
-; CHECK-LABEL: define internal i32 @select_bw_a(
-; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF5:![0-9]+]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 0, !prof [[PROF6:![0-9]+]]
-; CHECK-NEXT: ret i32 [[SEL]]
-;
-;
-; CHECK-LABEL: define internal i32 @vp_sym_a(
-; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF0]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
-; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF7:![0-9]+]]
-; CHECK-NEXT: ret i32 [[R]]
-;
-;
-; CHECK-LABEL: define internal i32 @vp_asym_a(
-; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF5]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
-; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF8:![0-9]+]]
-; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 1
-; CHECK-NEXT: ret i32 [[ADJ]]
-;
-;
-; CHECK-LABEL: define internal i32 @vp_g_only_a(
-; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
-; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF9:![0-9]+]]
-; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 3
-; CHECK-NEXT: ret i32 [[ADJ]]
-;
-;
-; CHECK-LABEL: define internal i32 @vp_kind_mismatch_a(
-; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
-; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]]()
-; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 2
-; CHECK-NEXT: ret i32 [[ADJ]]
-;
-;
-; CHECK-LABEL: define dso_local i32 @thunk_path_a(
-; CHECK-SAME: i32 [[X:%.*]]) unnamed_addr !prof [[PROF10:![0-9]+]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], 3
-; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF11:![0-9]+]]
-; CHECK: [[THEN]]:
-; CHECK-NEXT: ret i32 9
-; CHECK: [[ELSE]]:
-; CHECK-NEXT: ret i32 10
-;
-;
-; CHECK-LABEL: define private i32 @0(
-; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF10]] {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 4
-; CHECK-NEXT: br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]], !prof [[PROF11]]
-; CHECK: [[THEN]]:
-; CHECK-NEXT: ret i32 11
-; CHECK: [[ELSE]]:
-; CHECK-NEXT: ret i32 12
-;
-;
-; CHECK-LABEL: define i32 @use(
-; CHECK-SAME: i32 [[X:%.*]], ptr [[FN:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[A:%.*]] = call i32 @bw_asym_a(i32 [[X]])
-; CHECK-NEXT: [[B:%.*]] = call i32 @bw_asym_a(i32 [[X]])
-; CHECK-NEXT: [[C:%.*]] = call i32 @bw_sym_a(i32 [[X]])
-; CHECK-NEXT: [[D:%.*]] = call i32 @bw_sym_a(i32 [[X]])
-; CHECK-NEXT: [[E:%.*]] = call i32 @bw_g_only_a(i32 [[X]])
-; CHECK-NEXT: [[F:%.*]] = call i32 @bw_g_only_a(i32 [[X]])
-; CHECK-NEXT: [[G:%.*]] = call i32 @select_bw_a(i32 [[X]])
-; CHECK-NEXT: [[H:%.*]] = call i32 @select_bw_a(i32 [[X]])
-; CHECK-NEXT: [[I:%.*]] = call i32 @vp_sym_a(ptr [[FN]])
-; CHECK-NEXT: [[J:%.*]] = call i32 @vp_sym_a(ptr [[FN]])
-; CHECK-NEXT: [[K:%.*]] = call i32 @vp_asym_a(ptr [[FN]])
-; CHECK-NEXT: [[L:%.*]] = call i32 @vp_asym_a(ptr [[FN]])
-; CHECK-NEXT: [[M:%.*]] = call i32 @vp_g_only_a(ptr [[FN]])
-; CHECK-NEXT: [[N:%.*]] = call i32 @vp_g_only_a(ptr [[FN]])
-; CHECK-NEXT: [[O:%.*]] = call i32 @vp_kind_mismatch_a(ptr [[FN]])
-; CHECK-NEXT: [[P:%.*]] = call i32 @vp_kind_mismatch_a(ptr [[FN]])
-; CHECK-NEXT: [[Q:%.*]] = call i32 @thunk_path_a(i32 [[X]])
-; CHECK-NEXT: [[R:%.*]] = call i32 @thunk_path_a(i32 [[X]])
-; CHECK-NEXT: [[S:%.*]] = call i32 @[[GLOB0:[0-9]+]](i32 [[X]])
-; CHECK-NEXT: [[T:%.*]] = call i32 @[[GLOB0]](i32 [[X]])
-; CHECK-NEXT: [[SUM0:%.*]] = add i32 [[A]], [[B]]
-; CHECK-NEXT: [[SUM1:%.*]] = add i32 [[SUM0]], [[C]]
-; CHECK-NEXT: [[SUM2:%.*]] = add i32 [[SUM1]], [[D]]
-; CHECK-NEXT: [[SUM3:%.*]] = add i32 [[SUM2]], [[E]]
-; CHECK-NEXT: [[SUM4:%.*]] = add i32 [[SUM3]], [[F]]
-; CHECK-NEXT: [[SUM5:%.*]] = add i32 [[SUM4]], [[G]]
-; CHECK-NEXT: [[SUM6:%.*]] = add i32 [[SUM5]], [[H]]
-; CHECK-NEXT: [[SUM7:%.*]] = add i32 [[SUM6]], [[I]]
-; CHECK-NEXT: [[SUM8:%.*]] = add i32 [[SUM7]], [[J]]
-; CHECK-NEXT: [[SUM9:%.*]] = add i32 [[SUM8]], [[K]]
-; CHECK-NEXT: [[SUM10:%.*]] = add i32 [[SUM9]], [[L]]
-; CHECK-NEXT: [[SUM11:%.*]] = add i32 [[SUM10]], [[M]]
-; CHECK-NEXT: [[SUM12:%.*]] = add i32 [[SUM11]], [[N]]
-; CHECK-NEXT: [[SUM13:%.*]] = add i32 [[SUM12]], [[O]]
-; CHECK-NEXT: [[SUM14:%.*]] = add i32 [[SUM13]], [[P]]
-; CHECK-NEXT: [[SUM15:%.*]] = add i32 [[SUM14]], [[Q]]
-; CHECK-NEXT: [[SUM16:%.*]] = add i32 [[SUM15]], [[R]]
-; CHECK-NEXT: [[SUM17:%.*]] = add i32 [[SUM16]], [[S]]
-; CHECK-NEXT: [[SUM18:%.*]] = add i32 [[SUM17]], [[T]]
-; CHECK-NEXT: ret i32 [[SUM18]]
-;
-;
-; CHECK-LABEL: define dso_local i32 @thunk_path_b(
-; CHECK-SAME: i32 [[TMP0:%.*]]) unnamed_addr !prof [[PROF12:![0-9]+]] {
-; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @thunk_path_a(i32 [[TMP0]])
-; CHECK-NEXT: ret i32 [[TMP2]]
-;
-;
-; CHECK-LABEL: define weak_odr i32 @odr_path_b(
-; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF12]] {
-; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @[[GLOB0]](i32 [[TMP0]])
-; CHECK-NEXT: ret i32 [[TMP2]]
-;
-;
-; CHECK-LABEL: define weak_odr i32 @odr_path_a(
-; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF13:![0-9]+]] {
-; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @[[GLOB0]](i32 [[TMP0]])
-; CHECK-NEXT: ret i32 [[TMP2]]
-;
-;.
-; CHECK: [[PROF0]] = !{!"function_entry_count", i64 300}
-; CHECK: [[PROF1]] = !{!"branch_weights", i32 70, i32 230}
-; CHECK: [[PROF2]] = !{!"function_entry_count", i64 100}
-; CHECK: [[PROF3]] = !{!"branch_weights", i32 36, i32 64}
-; CHECK: [[PROF4]] = !{!"branch_weights", i32 17, i32 43}
-; CHECK: [[PROF5]] = !{!"function_entry_count", i64 200}
-; CHECK: [[PROF6]] = !{!"branch_weights", i32 50, i32 150}
-; CHECK: [[PROF7]] = !{!"VP", i32 0, i64 300, i64 111, i64 180, i64 333, i64 80, i64 222, i64 40}
-; CHECK: [[PROF8]] = !{!"VP", i32 0, i64 80, i64 444, i64 50, i64 555, i64 30}
-; CHECK: [[PROF9]] = !{!"VP", i32 0, i64 50, i64 888, i64 50}
-; CHECK: [[PROF10]] = !{!"function_entry_count", i64 30}
-; CHECK: [[PROF11]] = !{!"branch_weights", i32 4, i32 26}
-; CHECK: [[PROF12]] = !{!"function_entry_count", i64 20}
-; CHECK: [[PROF13]] = !{!"function_entry_count", i64 10}
-;.
diff --git a/llvm/test/Transforms/MergeFunc/merge-functions-select-weights.ll b/llvm/test/Transforms/MergeFunc/merge-functions-select-weights.ll
new file mode 100644
index 0000000000000..c052cabba6288
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/merge-functions-select-weights.ll
@@ -0,0 +1,50 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+;
+
+; Verify that MergeFunctions correctly combines select-instruction
+; branch_weights when merging two structurally identical functions.
+; Since FunctionComparator only considers functions equal when their
+; control-flow graphs and instruction order match exactly, MergeFunctions
+; can safely walk both functions in parallel and merge any instruction-level
+; metadata it encounters.
+;
+; The functions sel_a and sel_b differ only in their entry counts and
+; select branch_weights. When merged, the pass first converts each
+; function's relative branch weights into absolute execution counts using
+; its entry count, and then adds the counts together to produce the
+; final merged weights.
+;
+; sel_a: entry 50, weights 10:90 -> true=5, false=45
+; sel_b: entry 150, weights 30:70 -> true=45, false=105
+; Merged: true=5+45=50, false=45+105=150
+
+define internal i32 @sel_a(i32 %x) !prof !1 {
+; CHECK-LABEL: define internal i32 @sel_a(
+; CHECK-SAME: i32 [[X:%.*]]) !prof [[PROF0:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 0, !prof [[PROF1:![0-9]+]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+entry:
+ %cmp = icmp slt i32 %x, 0
+ %sel = select i1 %cmp, i32 1, i32 0, !prof !2
+ ret i32 %sel
+}
+
+define internal i32 @sel_b(i32 %x) !prof !3 {
+entry:
+ %cmp = icmp slt i32 %x, 0
+ %sel = select i1 %cmp, i32 1, i32 0, !prof !4
+ ret i32 %sel
+}
+
+!1 = !{!"function_entry_count", i64 50}
+!2 = !{!"branch_weights", i32 10, i32 90}
+!3 = !{!"function_entry_count", i64 150}
+!4 = !{!"branch_weights", i32 30, i32 70}
+;.
+; CHECK: [[PROF0]] = !{!"function_entry_count", i64 200}
+; CHECK: [[PROF1]] = !{!"branch_weights", i32 50, i32 150}
+;.
diff --git a/llvm/test/Transforms/MergeFunc/merge-functions-value-profile.ll b/llvm/test/Transforms/MergeFunc/merge-functions-value-profile.ll
new file mode 100644
index 0000000000000..c12a1e244f818
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/merge-functions-value-profile.ll
@@ -0,0 +1,143 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+;
+
+; Verify that MergeFunctions correctly merges value profile (VP) metadata
+; on indirect calls when combining structurally identical functions.
+; Because FunctionComparator only considers functions equivalent when
+; their control-flow structure and instruction ordering match exactly,
+; MergeFunctions can walk the two functions in parallel and safely merge
+; instruction-level metadata.
+;
+; To produce the merged profile, the pass first scales each function's
+; VP counts based on its entry count to obtain absolute execution counts.
+; It then combines the profiles, adding together counts for targets that
+; appear in both functions and preserving targets that appear in only one.
+
+; --------------------------------------------------------------------------
+; Both functions provide value profile metadata for the indirect call.
+;
+; vp_both_a has an entry count of 100 and a profile of:
+; {111:60, 222:40}
+;
+; vp_both_b has an entry count of 200 and a profile of:
+; {111:120, 333:80}
+;
+; After merging, counts for shared targets are summed while unique
+; targets are carried over:
+;
+; entry count = 300
+; VP {111:180, 333:80, 222:40}
+; --------------------------------------------------------------------------
+define internal i32 @vp_both_a(ptr %fn) !prof !1 {
+; CHECK-LABEL: define internal i32 @vp_both_a(
+; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF0:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF1:![0-9]+]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !2
+ ret i32 %r
+}
+
+define internal i32 @vp_both_b(ptr %fn) !prof !3 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !4
+ ret i32 %r
+}
+
+; --------------------------------------------------------------------------
+; Only one of the functions provides value profile metadata for the
+; indirect call.
+;
+; vp_one_a has an entry count of 30 but does not contain any VP data
+; on the call site.
+;
+; vp_one_b has an entry count of 70 and records the target profile:
+; {888:50}
+;
+; Since only vp_one_b contributes value profile information, the merged
+; function carries that profile forward unchanged while combining the
+; overall entry counts.
+;
+; entry count = 100
+; VP {888:50}
+; --------------------------------------------------------------------------
+define internal i32 @vp_one_a(ptr %fn) !prof !5 {
+; CHECK-LABEL: define internal i32 @vp_one_a(
+; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]](), !prof [[PROF3:![0-9]+]]
+; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 3
+; CHECK-NEXT: ret i32 [[ADJ]]
+;
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val()
+ %adj = add i32 %r, 3
+ ret i32 %adj
+}
+
+define internal i32 @vp_one_b(ptr %fn) !prof !6 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !7
+ %adj = add i32 %r, 3
+ ret i32 %adj
+}
+
+; --------------------------------------------------------------------------
+; Both functions attach value profile metadata to the indirect call, but
+; they use different VP kinds.
+;
+; Because the profiles are not compatible, MergeFunctions cannot safely
+; merge them into a single representation. Rather than producing
+; potentially misleading profile data, the pass drops the call-site
+; !prof metadata during the merge.
+; --------------------------------------------------------------------------
+define internal i32 @vp_kind_a(ptr %fn) !prof !8 {
+; CHECK-LABEL: define internal i32 @vp_kind_a(
+; CHECK-SAME: ptr [[FN:%.*]]) !prof [[PROF2]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN_VAL:%.*]] = load ptr, ptr [[FN]], align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 [[FN_VAL]]()
+; CHECK-NEXT: [[ADJ:%.*]] = add i32 [[R]], 2
+; CHECK-NEXT: ret i32 [[ADJ]]
+;
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !9
+ %adj = add i32 %r, 2
+ ret i32 %adj
+}
+
+define internal i32 @vp_kind_b(ptr %fn) !prof !10 {
+entry:
+ %fn_val = load ptr, ptr %fn
+ %r = call i32 %fn_val(), !prof !11
+ %adj = add i32 %r, 2
+ ret i32 %adj
+}
+
+!1 = !{!"function_entry_count", i64 100}
+!2 = !{!"VP", i32 0, i64 100, i64 111, i64 60, i64 222, i64 40}
+!3 = !{!"function_entry_count", i64 200}
+!4 = !{!"VP", i32 0, i64 200, i64 111, i64 120, i64 333, i64 80}
+!5 = !{!"function_entry_count", i64 30}
+!6 = !{!"function_entry_count", i64 70}
+!7 = !{!"VP", i32 0, i64 70, i64 888, i64 50}
+!8 = !{!"function_entry_count", i64 30}
+!9 = !{!"VP", i32 0, i64 30, i64 666, i64 20}
+!10 = !{!"function_entry_count", i64 70}
+!11 = !{!"VP", i32 1, i64 70, i64 777, i64 50}
+;.
+; CHECK: [[PROF0]] = !{!"function_entry_count", i64 300}
+; CHECK: [[PROF1]] = !{!"VP", i32 0, i64 300, i64 111, i64 180, i64 333, i64 80, i64 222, i64 40}
+; CHECK: [[PROF2]] = !{!"function_entry_count", i64 100}
+; CHECK: [[PROF3]] = !{!"VP", i32 0, i64 50, i64 888, i64 50}
+;.
>From f8c01470ae507c3c3cc9d2f048eb88c376f3785e Mon Sep 17 00:00:00 2001
From: Alok Kumar Sharma <AlokKumar.Sharma at amd.com>
Date: Fri, 10 Jul 2026 13:10:46 +0530
Subject: [PATCH 3/4] fixed format.
---
llvm/lib/Transforms/IPO/MergeFunctions.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index a3dd33cefe6ec..4a29c49e493d5 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -270,7 +270,7 @@ class MergeFunctions {
/// again.
void mergeTwoFunctions(Function *F, Function *G);
- const BlockFrequencyInfo* getBFI(Function &F);
+ const BlockFrequencyInfo *getBFI(Function &F);
void mergeInstrProfMetadataInto(Function *Dst, Function *Src);
>From bb964c3beaf5a8d3e3f576fe17bd6f381b689b1e Mon Sep 17 00:00:00 2001
From: Alok Kumar Sharma <AlokKumar.Sharma at amd.com>
Date: Mon, 13 Jul 2026 15:53:55 +0530
Subject: [PATCH 4/4] Review comments.
---
.../llvm/Transforms/IPO/MergeFunctions.h | 8 +-
llvm/lib/Transforms/IPO/MergeFunctions.cpp | 35 +++++----
.../Transforms/IPO/MergeFunctionsTest.cpp | 75 ++++++++++++-------
3 files changed, 69 insertions(+), 49 deletions(-)
diff --git a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
index e5fbf0ee6c77a..5d73e0d90c8c6 100644
--- a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
+++ b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
@@ -28,13 +28,9 @@ class MergeFunctionsPass : public OptionalPassInfoMixin<MergeFunctionsPass> {
public:
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
- /// When null, instruction-level profile merging scales using function entry
- /// counts only. The NewPM path always provides FAM.
- LLVM_ABI static bool runOnModule(Module &M,
- FunctionAnalysisManager *FAM = nullptr);
+ LLVM_ABI static bool runOnModule(Module &M, ModuleAnalysisManager &AM);
LLVM_ABI static DenseMap<Function *, Function *>
- runOnFunctions(ArrayRef<Function *> F,
- FunctionAnalysisManager *FAM = nullptr);
+ runOnFunctions(ArrayRef<Function *> F, ModuleAnalysisManager &AM);
};
} // end namespace llvm
diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index 4a29c49e493d5..04d56ebdfde27 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -207,7 +207,7 @@ class FunctionNode {
/// bitcast of the other.
class MergeFunctions {
public:
- explicit MergeFunctions(FunctionAnalysisManager *FAM = nullptr)
+ explicit MergeFunctions(FunctionAnalysisManager &FAM)
: FnTree(FunctionNodeCmp(&GlobalNumbers)), FAM(FAM) {}
template <typename FuncContainer> bool run(FuncContainer &Functions);
@@ -325,21 +325,21 @@ class MergeFunctions {
/// Deleted-New functions mapping
DenseMap<Function *, Function *> DelToNewMap;
- FunctionAnalysisManager *FAM = nullptr;
+ FunctionAnalysisManager &FAM;
};
} // end anonymous namespace
PreservedAnalyses MergeFunctionsPass::run(Module &M,
ModuleAnalysisManager &AM) {
- auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
- if (!MergeFunctionsPass::runOnModule(M, &FAM))
+ if (!MergeFunctionsPass::runOnModule(M, AM))
return PreservedAnalyses::all();
return PreservedAnalyses::none();
}
SmallPtrSet<GlobalValue *, 4> &MergeFunctions::getUsed() { return Used; }
-bool MergeFunctionsPass::runOnModule(Module &M, FunctionAnalysisManager *FAM) {
+bool MergeFunctionsPass::runOnModule(Module &M, ModuleAnalysisManager &AM) {
+ auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
MergeFunctions MF(FAM);
SmallVector<GlobalValue *, 4> UsedV;
collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/false);
@@ -350,7 +350,15 @@ bool MergeFunctionsPass::runOnModule(Module &M, FunctionAnalysisManager *FAM) {
DenseMap<Function *, Function *>
MergeFunctionsPass::runOnFunctions(ArrayRef<Function *> F,
- FunctionAnalysisManager *FAM) {
+ ModuleAnalysisManager &AM) {
+ if (F.empty())
+ return DenseMap<Function *, Function *>();
+
+ Module &M = *F.front()->getParent();
+ assert(
+ llvm::all_of(F, [&M](Function *Fn) { return Fn->getParent() == &M; }) &&
+ "all functions must belong to the same module");
+ auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
MergeFunctions MF(FAM);
return MF.runOnFunctions(F);
}
@@ -1068,10 +1076,7 @@ mergeValueProfileOnInstructions(Instruction *DstI, const Instruction *SrcI,
}
const BlockFrequencyInfo *MergeFunctions::getBFI(Function &F) {
- if (!FAM)
- return nullptr;
-
- return &FAM->getResult<BlockFrequencyAnalysis>(F);
+ return &FAM.getResult<BlockFrequencyAnalysis>(F);
}
/// Merge \p Src's instruction-level branch weights and value profile
@@ -1107,12 +1112,10 @@ void MergeFunctions::mergeInstrProfMetadataInto(Function *Dst, Function *Src) {
DstEntryCount, SrcEntryCount);
}
- if (FAM) {
- PreservedAnalyses PA = PreservedAnalyses::all();
- PA.abandon<BranchProbabilityAnalysis>();
- PA.abandon<BlockFrequencyAnalysis>();
- FAM->invalidate(*Dst, PA);
- }
+ PreservedAnalyses PA = PreservedAnalyses::all();
+ PA.abandon<BranchProbabilityAnalysis>();
+ PA.abandon<BlockFrequencyAnalysis>();
+ FAM.invalidate(*Dst, PA);
}
static void mergeEntryCountsInto(Function *F, std::optional<uint64_t> FC,
diff --git a/llvm/unittests/Transforms/IPO/MergeFunctionsTest.cpp b/llvm/unittests/Transforms/IPO/MergeFunctionsTest.cpp
index 5be92b5ae3188..879b48e6457a3 100644
--- a/llvm/unittests/Transforms/IPO/MergeFunctionsTest.cpp
+++ b/llvm/unittests/Transforms/IPO/MergeFunctionsTest.cpp
@@ -9,9 +9,15 @@
#include "llvm/Transforms/IPO/MergeFunctions.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/Analysis/BlockFrequencyInfo.h"
+#include "llvm/Analysis/BranchProbabilityInfo.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/PassInstrumentation.h"
#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
#include <memory>
@@ -20,10 +26,35 @@ using namespace llvm;
namespace {
-TEST(MergeFunctions, TrueOutputModuleTest) {
+class MergeFunctionsTest : public testing::Test {
+protected:
LLVMContext Ctx;
- SMDiagnostic Err;
- std::unique_ptr<Module> M(parseAssemblyString(R"invalid(
+ ModuleAnalysisManager MAM;
+ FunctionAnalysisManager FAM;
+
+ MergeFunctionsTest() {
+ FAM.registerPass([&] { return TargetLibraryAnalysis(); });
+ FAM.registerPass([&] { return DominatorTreeAnalysis(); });
+ FAM.registerPass([&] { return PostDominatorTreeAnalysis(); });
+ FAM.registerPass([&] { return LoopAnalysis(); });
+ FAM.registerPass([&] { return BranchProbabilityAnalysis(); });
+ FAM.registerPass([&] { return BlockFrequencyAnalysis(); });
+ FAM.registerPass([&] { return PassInstrumentationAnalysis(); });
+ FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
+ MAM.registerPass([&] { return PassInstrumentationAnalysis(); });
+ MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
+ }
+
+ std::unique_ptr<Module> parseModule(StringRef IR) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx);
+ EXPECT_TRUE(M);
+ return M;
+ }
+};
+
+TEST_F(MergeFunctionsTest, TrueOutputModuleTest) {
+ std::unique_ptr<Module> M = parseModule(R"invalid(
@.str = private unnamed_addr constant [10 x i8] c"On f: %d\0A\00", align 1
@.str.1 = private unnamed_addr constant [13 x i8] c"On main: %d\0A\00", align 1
@@ -64,17 +95,14 @@ TEST(MergeFunctions, TrueOutputModuleTest) {
%4 = add nsw i32 %3, 2
ret i32 %4
}
- )invalid",
- Err, Ctx));
+ )invalid");
// Expects true after merging _slice_add10 and _slice_add10_alt
- EXPECT_TRUE(MergeFunctionsPass::runOnModule(*M));
+ EXPECT_TRUE(MergeFunctionsPass::runOnModule(*M, MAM));
}
-TEST(MergeFunctions, TrueOutputFunctionsTest) {
- LLVMContext Ctx;
- SMDiagnostic Err;
- std::unique_ptr<Module> M(parseAssemblyString(R"invalid(
+TEST_F(MergeFunctionsTest, TrueOutputFunctionsTest) {
+ std::unique_ptr<Module> M = parseModule(R"invalid(
@.str = private unnamed_addr constant [10 x i8] c"On f: %d\0A\00", align 1
@.str.1 = private unnamed_addr constant [13 x i8] c"On main: %d\0A\00", align 1
@@ -115,15 +143,14 @@ TEST(MergeFunctions, TrueOutputFunctionsTest) {
%4 = add nsw i32 %3, 2
ret i32 %4
}
- )invalid",
- Err, Ctx));
+ )invalid");
SetVector<Function *> FunctionsSet;
for (Function &F : *M)
FunctionsSet.insert(&F);
DenseMap<Function *, Function *> MergeResult =
- MergeFunctionsPass::runOnFunctions(FunctionsSet.getArrayRef());
+ MergeFunctionsPass::runOnFunctions(FunctionsSet.getArrayRef(), MAM);
// Expects that both functions (_slice_add10 and _slice_add10_alt)
// be mapped to the same new function
@@ -134,10 +161,8 @@ TEST(MergeFunctions, TrueOutputFunctionsTest) {
EXPECT_EQ(P.second, NewFunction);
}
-TEST(MergeFunctions, FalseOutputModuleTest) {
- LLVMContext Ctx;
- SMDiagnostic Err;
- std::unique_ptr<Module> M(parseAssemblyString(R"invalid(
+TEST_F(MergeFunctionsTest, FalseOutputModuleTest) {
+ std::unique_ptr<Module> M = parseModule(R"invalid(
@.str = private unnamed_addr constant [10 x i8] c"On f: %d\0A\00", align 1
@.str.1 = private unnamed_addr constant [13 x i8] c"On main: %d\0A\00", align 1
@@ -178,17 +203,14 @@ TEST(MergeFunctions, FalseOutputModuleTest) {
%4 = add nsw i32 %3, 2
ret i32 %0
}
- )invalid",
- Err, Ctx));
+ )invalid");
// Expects false after trying to merge _slice_add10 and _slice_add10_alt
- EXPECT_FALSE(MergeFunctionsPass::runOnModule(*M));
+ EXPECT_FALSE(MergeFunctionsPass::runOnModule(*M, MAM));
}
-TEST(MergeFunctions, FalseOutputFunctionsTest) {
- LLVMContext Ctx;
- SMDiagnostic Err;
- std::unique_ptr<Module> M(parseAssemblyString(R"invalid(
+TEST_F(MergeFunctionsTest, FalseOutputFunctionsTest) {
+ std::unique_ptr<Module> M = parseModule(R"invalid(
@.str = private unnamed_addr constant [10 x i8] c"On f: %d\0A\00", align 1
@.str.1 = private unnamed_addr constant [13 x i8] c"On main: %d\0A\00", align 1
@@ -229,15 +251,14 @@ TEST(MergeFunctions, FalseOutputFunctionsTest) {
%4 = add nsw i32 %3, 2
ret i32 %0
}
- )invalid",
- Err, Ctx));
+ )invalid");
SetVector<Function *> FunctionsSet;
for (Function &F : *M)
FunctionsSet.insert(&F);
DenseMap<Function *, Function *> MergeResult =
- MergeFunctionsPass::runOnFunctions(FunctionsSet.getArrayRef());
+ MergeFunctionsPass::runOnFunctions(FunctionsSet.getArrayRef(), MAM);
// Expects empty map
EXPECT_EQ(MergeResult.size(), 0u);
More information about the llvm-commits
mailing list