[llvm] [MergeFunctions] Preserve instruction-level profile metadata during merging (PR #208009)

Mircea Trofin via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 10 08:14:45 PDT 2026


================
@@ -858,28 +902,236 @@ 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.
 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) {
----------------
mtrofin wrote:

sorry for the spam, github had an issue that kept saying "can't save your comment", so looks like my hitting "retry" did... this.

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


More information about the llvm-commits mailing list