[llvm] [GlobalMergeFunctions] Add profile-based function filtering (PR #213943)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 4 23:49:00 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lto

Author: Karim Alweheshy (karim-alweheshy)

<details>
<summary>Changes</summary>

## Summary

- Add a hidden `-global-merging-profile-policy={none,hot,executed}` option to Global Function Merging.
- Apply the selected policy independently while producing and consuming CGData.
- Keep `none` as the default, preserve the CGData format, and add separate producer/consumer skip statistics.
- Add ThinLTO and local-mode coverage for explicit-hot, profile-hot, executed-but-not-hot, zero-count, and unprofiled functions.

## Motivation

Global Function Merging can reduce code size by replacing structurally similar functions with thunks into parameterized bodies. The current pass treats profile-hot startup and journey code like cold code, so a size-oriented deployment must choose between disabling the pass or adding calls and indirect control flow to functions that are known to execute.

`hot` protects functions classified as entry-hot by the standard profile summary. `executed` is a more conservative runtime policy: it protects every function with a nonzero entry count, including once-per-launch functions below the normal hot threshold. Both policies also protect functions carrying the explicit `hot` attribute. Zero-count and unprofiled functions remain eligible.

The policy is deliberately not serialized in CGData. Producers and consumers should use the same policy so candidate finalization and parameter selection see the same functions; a consumer still applies its current policy to each function when reading stale or differently filtered CGData.

The policy lets size-oriented users preserve profile-executed code while continuing to merge zero-count and unprofiled candidates. This PR makes no application-size or runtime claim.

Background: [Global Function Merging RFC](https://discourse.llvm.org/t/rfc-global-function-merging/82608) and [EuroLLVM 2025 presentation](https://llvm.org/devmtg/2025-04/slides/technical_talk/lee_function_merging.pdf).

## Testing

- Compiled the changed `GlobalMergeFunctions.cpp` source and relinked the patched `opt` and `llvm-lto2` tools.
- Ran the focused `llvm-lit` regression: 1/1 passed.
- Verified independent producer and consumer behavior using finalized CGData/YAML and stale unfiltered CGData.
- Verified `clang-format --dry-run --Werror` on the changed C++ sources.
- Verified `git diff --check`.
- A full LLVM build was not run in this sparse checkout.

---

Patch is 28.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/213943.diff


3 Files Affected:

- (modified) llvm/include/llvm/CodeGen/GlobalMergeFunctions.h (+6) 
- (modified) llvm/lib/CodeGen/GlobalMergeFunctions.cpp (+89-17) 
- (added) llvm/test/ThinLTO/AArch64/cgdata-merge-skip-hot.ll (+535) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/GlobalMergeFunctions.h b/llvm/include/llvm/CodeGen/GlobalMergeFunctions.h
index 6e765c130e69a..3ece003a52778 100644
--- a/llvm/include/llvm/CodeGen/GlobalMergeFunctions.h
+++ b/llvm/include/llvm/CodeGen/GlobalMergeFunctions.h
@@ -38,6 +38,8 @@ enum class HashFunctionMode {
 
 namespace llvm {
 
+class ProfileSummaryInfo;
+
 // A vector of locations (the pair of (instruction, operand) indices) reachable
 // from a parameter.
 using ParamLocs = SmallVector<IndexPair, 4>;
@@ -54,6 +56,10 @@ class GlobalMergeFunc {
 
   const ModuleSummaryIndex *Index;
 
+  void analyze(Module &M, const ProfileSummaryInfo *PSI);
+  bool merge(Module &M, const StableFunctionMap *FunctionMap,
+             const ProfileSummaryInfo *PSI);
+
 public:
   /// The suffix used to identify the merged function that parameterizes
   /// the constant values. Note that the original function, without this suffix,
diff --git a/llvm/lib/CodeGen/GlobalMergeFunctions.cpp b/llvm/lib/CodeGen/GlobalMergeFunctions.cpp
index dee20d601359f..254fea2d863b6 100644
--- a/llvm/lib/CodeGen/GlobalMergeFunctions.cpp
+++ b/llvm/lib/CodeGen/GlobalMergeFunctions.cpp
@@ -13,6 +13,7 @@
 #include "llvm/CodeGen/GlobalMergeFunctions.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/ModuleSummaryAnalysis.h"
+#include "llvm/Analysis/ProfileSummaryInfo.h"
 #include "llvm/CGData/CodeGenData.h"
 #include "llvm/CGData/CodeGenDataWriter.h"
 #include "llvm/CodeGen/Passes.h"
@@ -33,11 +34,37 @@ static cl::opt<bool> DisableCGDataForMerging(
              "merging is still enabled within a module."),
     cl::init(false));
 
+enum class GlobalMergingProfilePolicy { None, Hot, Executed };
+
+// The selected policy is intentionally not serialized in CGData. Producers and
+// consumers should use the same policy so candidate finalization and parameter
+// selection operate on the same set of functions. A consumer still applies its
+// current policy to each function when reading CGData produced with another
+// policy.
+static cl::opt<GlobalMergingProfilePolicy> GlobalMergingProfilePolicyOpt(
+    "global-merging-profile-policy", cl::Hidden,
+    cl::desc("Profile policy for excluding functions from global function "
+             "merging"),
+    cl::values(
+        clEnumValN(GlobalMergingProfilePolicy::None, "none",
+                   "Do not exclude functions based on profile information"),
+        clEnumValN(GlobalMergingProfilePolicy::Hot, "hot",
+                   "Exclude functions marked or profiled as hot"),
+        clEnumValN(GlobalMergingProfilePolicy::Executed, "executed",
+                   "Exclude functions marked hot or executed by the profile")),
+    cl::init(GlobalMergingProfilePolicy::None));
+
 STATISTIC(NumMergedFunctions,
           "Number of functions that are actually merged using function hash");
 STATISTIC(NumAnalyzedModues, "Number of modules that are analyzed");
 STATISTIC(NumAnalyzedFunctions, "Number of functions that are analyzed");
 STATISTIC(NumEligibleFunctions, "Number of functions that are eligible");
+STATISTIC(NumProducerProfileSkippedFunctions,
+          "Number of structurally eligible functions excluded while "
+          "producing global function merge data");
+STATISTIC(NumConsumerProfileSkippedFunctions,
+          "Number of structurally eligible functions excluded while "
+          "consuming global function merge data");
 
 /// Returns true if the \OpIdx operand of \p CI is the callee operand.
 static bool isCalleeOperand(const CallBase *CI, unsigned OpIdx) {
@@ -114,6 +141,31 @@ bool isEligibleFunction(Function *F) {
   return true;
 }
 
+static bool shouldSkipFunction(const Function &F,
+                               const ProfileSummaryInfo *PSI) {
+  switch (GlobalMergingProfilePolicyOpt) {
+  case GlobalMergingProfilePolicy::None:
+    return false;
+  case GlobalMergingProfilePolicy::Hot:
+    return F.hasFnAttribute(Attribute::Hot) ||
+           (PSI && PSI->isFunctionEntryHot(&F));
+  case GlobalMergingProfilePolicy::Executed:
+    if (F.hasFnAttribute(Attribute::Hot))
+      return true;
+    if (std::optional<uint64_t> EntryCount = F.getEntryCount())
+      return *EntryCount > 0;
+    return false;
+  }
+  llvm_unreachable("Unhandled global function merging profile policy");
+}
+
+static std::unique_ptr<ProfileSummaryInfo>
+createProfileSummaryInfoForMerging(Module &M) {
+  if (GlobalMergingProfilePolicyOpt != GlobalMergingProfilePolicy::Hot)
+    return nullptr;
+  return std::make_unique<ProfileSummaryInfo>(M);
+}
+
 static bool isEligibleInstructionForConstantSharing(const Instruction *I) {
   switch (I->getOpcode()) {
   case Instruction::Load:
@@ -146,30 +198,39 @@ static bool ignoreOp(const Instruction *I, unsigned OpIdx) {
   return true;
 }
 
-void GlobalMergeFunc::analyze(Module &M) {
+void GlobalMergeFunc::analyze(Module &M, const ProfileSummaryInfo *PSI) {
   ++NumAnalyzedModues;
   for (Function &Func : M) {
     ++NumAnalyzedFunctions;
-    if (isEligibleFunction(&Func)) {
-      ++NumEligibleFunctions;
+    if (!isEligibleFunction(&Func))
+      continue;
+    ++NumEligibleFunctions;
+    if (shouldSkipFunction(Func, PSI)) {
+      ++NumProducerProfileSkippedFunctions;
+      continue;
+    }
 
-      auto FI = llvm::StructuralHashWithDifferences(Func, ignoreOp);
+    auto FI = llvm::StructuralHashWithDifferences(Func, ignoreOp);
 
-      // Convert the operand map to a vector for a serialization-friendly
-      // format.
-      IndexOperandHashVecType IndexOperandHashes;
-      for (auto &Pair : *FI.IndexOperandHashMap)
-        IndexOperandHashes.emplace_back(Pair);
+    // Convert the operand map to a vector for a serialization-friendly
+    // format.
+    IndexOperandHashVecType IndexOperandHashes;
+    for (auto &Pair : *FI.IndexOperandHashMap)
+      IndexOperandHashes.emplace_back(Pair);
 
-      StableFunction SF(FI.FunctionHash, get_stable_name(Func.getName()).str(),
-                        M.getModuleIdentifier(), FI.IndexInstruction->size(),
-                        std::move(IndexOperandHashes));
+    StableFunction SF(FI.FunctionHash, get_stable_name(Func.getName()).str(),
+                      M.getModuleIdentifier(), FI.IndexInstruction->size(),
+                      std::move(IndexOperandHashes));
 
-      LocalFunctionMap->insert(SF);
-    }
+    LocalFunctionMap->insert(SF);
   }
 }
 
+void GlobalMergeFunc::analyze(Module &M) {
+  auto PSI = createProfileSummaryInfoForMerging(M);
+  analyze(M, PSI.get());
+}
+
 /// Tuple to hold function info to process merging.
 struct FuncMergeInfo {
   StableFunctionMap::StableFunctionEntry *SF;
@@ -401,7 +462,8 @@ computeParamInfo(const StableFunctionMap::StableFunctionEntries &SFS) {
   return ParamLocsVec;
 }
 
-bool GlobalMergeFunc::merge(Module &M, const StableFunctionMap *FunctionMap) {
+bool GlobalMergeFunc::merge(Module &M, const StableFunctionMap *FunctionMap,
+                            const ProfileSummaryInfo *PSI) {
   bool Changed = false;
 
   // Collect stable functions related to the current module.
@@ -410,6 +472,10 @@ bool GlobalMergeFunc::merge(Module &M, const StableFunctionMap *FunctionMap) {
   for (auto &F : M) {
     if (!isEligibleFunction(&F))
       continue;
+    if (shouldSkipFunction(F, PSI)) {
+      ++NumConsumerProfileSkippedFunctions;
+      continue;
+    }
     auto FI = llvm::StructuralHashWithDifferences(F, ignoreOp);
     if (FunctionMap->contains(FI.FunctionHash))
       HashToFuncs[FI.FunctionHash].emplace_back(&F, std::move(FI));
@@ -514,6 +580,11 @@ bool GlobalMergeFunc::merge(Module &M, const StableFunctionMap *FunctionMap) {
   return Changed;
 }
 
+bool GlobalMergeFunc::merge(Module &M, const StableFunctionMap *FunctionMap) {
+  auto PSI = createProfileSummaryInfoForMerging(M);
+  return merge(M, FunctionMap, PSI.get());
+}
+
 void GlobalMergeFunc::initializeMergerMode(const Module &M) {
   // Initialize the local function map regardless of the merger mode.
   LocalFunctionMap = std::make_unique<StableFunctionMap>();
@@ -558,13 +629,14 @@ void GlobalMergeFunc::emitFunctionMap(Module &M) {
 
 bool GlobalMergeFunc::run(Module &M) {
   initializeMergerMode(M);
+  auto PSI = createProfileSummaryInfoForMerging(M);
 
   const StableFunctionMap *FuncMap;
   if (MergerMode == HashFunctionMode::UsingHashFunction) {
     // Use the prior CG data to optimistically create global merge candidates.
     FuncMap = cgdata::getStableFunctionMap();
   } else {
-    analyze(M);
+    analyze(M, PSI.get());
     // Emit the local function map to the custom section, __llvm_merge before
     // finalizing it.
     if (MergerMode == HashFunctionMode::BuildingHashFuncion)
@@ -573,7 +645,7 @@ bool GlobalMergeFunc::run(Module &M) {
     FuncMap = LocalFunctionMap.get();
   }
 
-  return merge(M, FuncMap);
+  return merge(M, FuncMap, PSI.get());
 }
 
 namespace {
diff --git a/llvm/test/ThinLTO/AArch64/cgdata-merge-skip-hot.ll b/llvm/test/ThinLTO/AArch64/cgdata-merge-skip-hot.ll
new file mode 100644
index 0000000000000..19be019609ff3
--- /dev/null
+++ b/llvm/test/ThinLTO/AArch64/cgdata-merge-skip-hot.ll
@@ -0,0 +1,535 @@
+; Verify profile policies for global function merging in local and CGData modes.
+;
+; The policy is deliberately not serialized in CGData, so this does not change
+; the CGData format. Producers and consumers should use the same policy for best
+; profitability. A consumer of stale CGData still applies its current policy to
+; each function, but stale candidates can affect parameter selection.
+
+; RUN: rm -rf %t; split-file %s %t
+
+; Local mode has no profile summary. The hot policy therefore excludes only
+; functions with an explicit hot attribute. The executed policy additionally
+; excludes functions with a nonzero entry count. Zero-count and unprofiled
+; functions remain eligible under both policies.
+; RUN: opt -mtriple=arm64-apple-darwin -S --passes=global-merge-func \
+; RUN:   -global-merging-profile-policy=none %t/local.ll -o %t-local-none.ll
+; RUN: FileCheck %s --check-prefix=LOCAL-NONE < %t-local-none.ll
+; RUN: opt -mtriple=arm64-apple-darwin -S --passes=global-merge-func \
+; RUN:   -global-merging-profile-policy=hot %t/local.ll -o %t-local-hot.ll
+; RUN: FileCheck %s --check-prefix=LOCAL-HOT < %t-local-hot.ll
+; RUN: not grep -E '@attr_hot_local_[12]\.Tgm' %t-local-hot.ll
+; RUN: opt -mtriple=arm64-apple-darwin -S --passes=global-merge-func \
+; RUN:   -global-merging-profile-policy=executed %t/local.ll \
+; RUN:   -o %t-local-executed.ll
+; RUN: FileCheck %s --check-prefix=LOCAL-EXECUTED < %t-local-executed.ll
+; RUN: not grep -E '@(attr_hot|executed)_local_[12]\.Tgm' \
+; RUN:   %t-local-executed.ll
+
+; LOCAL-NONE-DAG: @attr_hot_local_1.Tgm
+; LOCAL-NONE-DAG: @attr_hot_local_2.Tgm
+; LOCAL-NONE-DAG: @executed_local_1.Tgm
+; LOCAL-NONE-DAG: @executed_local_2.Tgm
+; LOCAL-NONE-DAG: @zero_local_1.Tgm
+; LOCAL-NONE-DAG: @zero_local_2.Tgm
+; LOCAL-NONE-DAG: @unprofiled_local_1.Tgm
+; LOCAL-NONE-DAG: @unprofiled_local_2.Tgm
+; LOCAL-HOT-DAG: @executed_local_1.Tgm
+; LOCAL-HOT-DAG: @executed_local_2.Tgm
+; LOCAL-HOT-DAG: @zero_local_1.Tgm
+; LOCAL-HOT-DAG: @zero_local_2.Tgm
+; LOCAL-HOT-DAG: @unprofiled_local_1.Tgm
+; LOCAL-HOT-DAG: @unprofiled_local_2.Tgm
+; LOCAL-EXECUTED-DAG: @zero_local_1.Tgm
+; LOCAL-EXECUTED-DAG: @zero_local_2.Tgm
+; LOCAL-EXECUTED-DAG: @unprofiled_local_1.Tgm
+; LOCAL-EXECUTED-DAG: @unprofiled_local_2.Tgm
+
+; Build ThinLTO inputs with profile summaries so the hot policy can classify
+; profile-hot functions.
+; RUN: opt -module-summary -module-hash %t/foo.ll -o %t-foo.bc
+; RUN: opt -module-summary -module-hash %t/bar.ll -o %t-bar.bc
+
+; Produce CGData without profile filtering.
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN:   -codegen-data-generate=true \
+; RUN:   %t-foo.bc %t-bar.bc -o %t-default-write \
+; RUN:   -r %t-foo.bc,_profile_hot_1,px -r %t-bar.bc,_profile_hot_2,px \
+; RUN:   -r %t-foo.bc,_attr_hot_1,px -r %t-bar.bc,_attr_hot_2,px \
+; RUN:   -r %t-bar.bc,_mixed_hot,px -r %t-bar.bc,_mixed_cold_1,px \
+; RUN:   -r %t-bar.bc,_mixed_cold_2,px \
+; RUN:   -r %t-foo.bc,_cold_1,px -r %t-bar.bc,_cold_2,px \
+; RUN:   -r %t-foo.bc,_zero_1,px -r %t-bar.bc,_zero_2,px \
+; RUN:   -r %t-foo.bc,_unprofiled_1,px -r %t-bar.bc,_unprofiled_2,px \
+; RUN:   -r %t-foo.bc,_g1,l -r %t-bar.bc,_g2,l -r %t-bar.bc,_g3,l \
+; RUN:   -r %t-bar.bc,_g4,l
+; RUN: llvm-cgdata --merge -o %t-default.cgdata \
+; RUN:   %t-default-write.1 %t-default-write.2
+; RUN: llvm-cgdata --convert %t-default.cgdata -o %t-default.yaml
+; RUN: FileCheck %s --check-prefix=DEFAULT-CGDATA < %t-default.yaml
+
+; DEFAULT-CGDATA-DAG: FunctionName: mixed_hot
+; DEFAULT-CGDATA-DAG: FunctionName: mixed_cold_1
+; DEFAULT-CGDATA-DAG: FunctionName: mixed_cold_2
+; DEFAULT-CGDATA-DAG: FunctionName: zero_1
+; DEFAULT-CGDATA-DAG: FunctionName: zero_2
+
+; Default behavior remains unchanged and merges hot functions.
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN:   -codegen-data-use-path=%t-default.cgdata \
+; RUN:   %t-foo.bc %t-bar.bc -o %t-default-read \
+; RUN:   -r %t-foo.bc,_profile_hot_1,px -r %t-bar.bc,_profile_hot_2,px \
+; RUN:   -r %t-foo.bc,_attr_hot_1,px -r %t-bar.bc,_attr_hot_2,px \
+; RUN:   -r %t-bar.bc,_mixed_hot,px -r %t-bar.bc,_mixed_cold_1,px \
+; RUN:   -r %t-bar.bc,_mixed_cold_2,px \
+; RUN:   -r %t-foo.bc,_cold_1,px -r %t-bar.bc,_cold_2,px \
+; RUN:   -r %t-foo.bc,_zero_1,px -r %t-bar.bc,_zero_2,px \
+; RUN:   -r %t-foo.bc,_unprofiled_1,px -r %t-bar.bc,_unprofiled_2,px \
+; RUN:   -r %t-foo.bc,_g1,l -r %t-bar.bc,_g2,l -r %t-bar.bc,_g3,l \
+; RUN:   -r %t-bar.bc,_g4,l
+; RUN: llvm-nm %t-default-read.1 > %t-default-foo.nm
+; RUN: llvm-nm %t-default-read.2 > %t-default-bar.nm
+; RUN: FileCheck %s --check-prefix=DEFAULT-FOO < %t-default-foo.nm
+; RUN: FileCheck %s --check-prefix=DEFAULT-BAR < %t-default-bar.nm
+
+; DEFAULT-FOO-DAG: _profile_hot_1.Tgm
+; DEFAULT-FOO-DAG: _attr_hot_1.Tgm
+; DEFAULT-FOO-DAG: _zero_1.Tgm
+; DEFAULT-BAR-DAG: _profile_hot_2.Tgm
+; DEFAULT-BAR-DAG: _attr_hot_2.Tgm
+; DEFAULT-BAR-DAG: _mixed_hot.Tgm
+; DEFAULT-BAR-DAG: _zero_2.Tgm
+
+; Produce fresh CGData using the hot policy. The same-module group contains one
+; hot function and two cold functions: only the hot member is filtered, while
+; the two cold members remain a profitable finalized candidate.
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN:   -global-merging-profile-policy=hot -codegen-data-generate=true \
+; RUN:   %t-foo.bc %t-bar.bc -o %t-hot-write \
+; RUN:   -r %t-foo.bc,_profile_hot_1,px -r %t-bar.bc,_profile_hot_2,px \
+; RUN:   -r %t-foo.bc,_attr_hot_1,px -r %t-bar.bc,_attr_hot_2,px \
+; RUN:   -r %t-bar.bc,_mixed_hot,px -r %t-bar.bc,_mixed_cold_1,px \
+; RUN:   -r %t-bar.bc,_mixed_cold_2,px \
+; RUN:   -r %t-foo.bc,_cold_1,px -r %t-bar.bc,_cold_2,px \
+; RUN:   -r %t-foo.bc,_zero_1,px -r %t-bar.bc,_zero_2,px \
+; RUN:   -r %t-foo.bc,_unprofiled_1,px -r %t-bar.bc,_unprofiled_2,px \
+; RUN:   -r %t-foo.bc,_g1,l -r %t-bar.bc,_g2,l -r %t-bar.bc,_g3,l \
+; RUN:   -r %t-bar.bc,_g4,l
+; RUN: llvm-cgdata --merge -o %t-hot.cgdata %t-hot-write.1 %t-hot-write.2
+; RUN: llvm-cgdata --convert %t-hot.cgdata -o %t-hot.yaml
+; RUN: FileCheck %s --check-prefix=HOT-CGDATA < %t-hot.yaml
+; RUN: not grep -E 'profile_hot|attr_hot|mixed_hot' %t-hot.yaml
+
+; HOT-CGDATA-DAG: FunctionName: cold_1
+; HOT-CGDATA-DAG: FunctionName: cold_2
+; HOT-CGDATA-DAG: FunctionName: zero_1
+; HOT-CGDATA-DAG: FunctionName: zero_2
+; HOT-CGDATA-DAG: FunctionName: unprofiled_1
+; HOT-CGDATA-DAG: FunctionName: unprofiled_2
+; HOT-CGDATA-DAG: FunctionName: mixed_cold_1
+; HOT-CGDATA-DAG: FunctionName: mixed_cold_2
+
+; Consume fresh CGData with the same hot policy.
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN:   -global-merging-profile-policy=hot \
+; RUN:   -codegen-data-use-path=%t-hot.cgdata \
+; RUN:   %t-foo.bc %t-bar.bc -o %t-hot-read \
+; RUN:   -r %t-foo.bc,_profile_hot_1,px -r %t-bar.bc,_profile_hot_2,px \
+; RUN:   -r %t-foo.bc,_attr_hot_1,px -r %t-bar.bc,_attr_hot_2,px \
+; RUN:   -r %t-bar.bc,_mixed_hot,px -r %t-bar.bc,_mixed_cold_1,px \
+; RUN:   -r %t-bar.bc,_mixed_cold_2,px \
+; RUN:   -r %t-foo.bc,_cold_1,px -r %t-bar.bc,_cold_2,px \
+; RUN:   -r %t-foo.bc,_zero_1,px -r %t-bar.bc,_zero_2,px \
+; RUN:   -r %t-foo.bc,_unprofiled_1,px -r %t-bar.bc,_unprofiled_2,px \
+; RUN:   -r %t-foo.bc,_g1,l -r %t-bar.bc,_g2,l -r %t-bar.bc,_g3,l \
+; RUN:   -r %t-bar.bc,_g4,l
+; RUN: llvm-nm %t-hot-read.1 > %t-hot-foo.nm
+; RUN: llvm-nm %t-hot-read.2 > %t-hot-bar.nm
+; RUN: FileCheck %s --check-prefix=HOT-FOO < %t-hot-foo.nm
+; RUN: FileCheck %s --check-prefix=HOT-BAR < %t-hot-bar.nm
+; RUN: not grep -E '_profile_hot_1\.Tgm|_attr_hot_1\.Tgm' %t-hot-foo.nm
+; RUN: not grep -E '_profile_hot_2\.Tgm|_attr_hot_2\.Tgm|_mixed_hot\.Tgm' \
+; RUN:   %t-hot-bar.nm
+
+; HOT-FOO-DAG: _cold_1.Tgm
+; HOT-FOO-DAG: _zero_1.Tgm
+; HOT-FOO-DAG: _unprofiled_1.Tgm
+; HOT-BAR-DAG: _cold_2.Tgm
+; HOT-BAR-DAG: _zero_2.Tgm
+; HOT-BAR-DAG: _unprofiled_2.Tgm
+; HOT-BAR-DAG: _mixed_cold_1.Tgm
+; HOT-BAR-DAG: _mixed_cold_2.Tgm
+
+; Consume stale unfiltered CGData with the hot policy. The consumer still skips
+; only each currently hot function. In particular, it does not poison the hash
+; shared by the two cold peers. Matching production and consumption policies is
+; nevertheless preferred because stale records can change profitability and
+; parameter selection.
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN:   -global-merging-profile-policy=hot \
+; RUN:   -codegen-data-use-path=%t-default.cgdata \
+; RUN:   %t-foo.bc %t-bar.bc -o %t-stale-read \
+; RUN:   -r %t-foo.bc,_profile_hot_1,px -r %t-bar.bc,_profile_hot_2,px \
+; RUN:   -r %t-foo.bc,_attr_hot_1,px -r %t-bar.bc,_attr_hot_2,px \
+; RUN:   -r %t-bar.bc,_mixed_hot,px -r %t-bar.bc,_mixed_cold_1,px \
+; RUN:   -r %t-bar.bc,_mixed_cold_2,px \
+; RUN:   -r %t-foo.bc,_cold_1,px -r %t-bar.bc,_cold_2,px \
+; RUN:   -r %t-foo.bc,_zero_1,px -r %t-bar.bc,_zero_2,px \
+; RUN:   -r %t-foo.bc,_unprofiled_1,px -r %t-bar.bc,_unprofiled_2,px \
+; RUN:   -r %t-foo.bc,_g1,l -r %t-bar.bc,_g2,l -r %t-bar.bc,_g3,l \
+; RUN:   -r %t-bar.bc,_g4,l
+; RUN: llvm-nm %t-stale-read.1 > %t-stale-foo.nm
+; RUN: llvm-nm %t-stale-read.2 > %t-stale-bar.nm
+; RUN: FileCheck %s --check-prefix=STALE-FOO < %t-stale-foo.nm
+; RUN: FileCheck %s --check-prefix=STALE-BAR < %t-stale-bar.nm
+; RUN: not grep -E '_profile_hot_1\.Tgm|_attr_hot_1\.Tgm' %t-stale-foo.nm
+; RUN: not grep -E '_profile_hot_2\.Tgm|_attr_hot_2\.Tgm|_mixed_hot\.Tgm' \
+; RUN:   %t-stale-bar.nm
+
+; STALE-FOO-DAG: _cold_1.Tgm
+; STALE-FOO-DAG: _zero_1.Tgm
+; STALE-FOO-DAG: _unprofiled_1.Tgm
+; STALE-BAR-DAG: _cold_2.Tgm
+; STALE-BAR-DAG: _zero_2.Tgm
+; STALE-BAR-DAG: _unprofiled_2.Tgm
+; STALE-BAR-DAG: _mixed_cold_1.Tgm
+; STALE-BAR-DAG: _mixed_cold_2.Tgm
+
+; Produce CGData using the executed policy. Explicitly hot and every function
+; with a nonzero entry count are absent, while zero-count and unprofiled pairs
+; remain in the finalized map.
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN:   -global-merging-profile-policy=executed -codegen-data-generate=true \
+; RUN:   %t-foo.bc %t-bar.bc -o %t-executed-write \
+; RUN:   -r %t-foo.bc,_profile_hot_1,px -r %t-bar.bc,_profile_hot_2,px \
+; RUN:   -r %t-foo.bc,_attr_hot_1,px -r %t-bar.bc,_attr_hot_2,px \
+; RUN:   -r %t-bar.bc,_mixed_hot,px -r %t-bar.bc,_mixed_cold_1,px \
+; RUN:   -r %t-bar.bc,_mixed_cold_2,px \
+; RUN:   -r %t-foo.bc,_cold_1,px -r %t-bar.bc,_cold_2,px \
+; RUN:   -r %t-foo.bc,_zero_1,px -r %t-bar.bc,_zero_2,px \
+; RUN:   -r %t-foo.bc,_unprofiled_1,px -r %t-bar.bc,_unprofiled_2,px \
+; RUN:   -r %t-foo.bc,_g1,l -r %t-bar.bc,_g2,l -r %t-bar.bc,_g3,l \
+; RUN:   -r %t-bar.bc,_g4,l
+; RUN: llvm-cgdata --merge -o %t-executed.cgdata \
+; RUN:   %t-executed-write.1 %t-executed-write.2
+; RUN: llvm-cgdata --convert %t-executed.cgdata -o %t-executed.yaml
+; RUN: FileCheck %s --check-prefix=EXECUTED-CGDATA < %t-executed.yaml
+; RUN: not grep -E 'profile_hot|attr_hot|mixed_|FunctionName: cold_[12]' \
+; RUN:   %t-executed.yaml
+
+; EXECUTED-CGDATA-DAG: FunctionName: zero_1
+; EXECUTED-CGDATA-DAG: FunctionName: zero_2
+; EXECUTED-CGDATA-DAG: FunctionName: unprofiled_1
+; EXECUTED-CGDATA-DAG: FunctionName: unprofiled_2
+
+; Consume unfiltered CGData with the executed policy. The independent
+; consumer-side guard prevents merged instances for every executed or e...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/213943


More information about the llvm-commits mailing list