[llvm] Revert "[MergeFunctions] Preserve instruction-level profile metadata during merging" (PR #209987)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 16 00:01:12 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Nikita Popov (nikic)
<details>
<summary>Changes</summary>
Reverts llvm/llvm-project#<!-- -->208009
Causes unit test failures.
---
Patch is 45.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209987.diff
7 Files Affected:
- (modified) llvm/include/llvm/Transforms/IPO/MergeFunctions.h (+2-2)
- (modified) llvm/lib/Transforms/IPO/MergeFunctions.cpp (+32-247)
- (removed) llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll (-239)
- (removed) llvm/test/Transforms/MergeFunc/merge-functions-reordered-blocks-branch-weights.ll (-121)
- (removed) llvm/test/Transforms/MergeFunc/merge-functions-select-weights.ll (-50)
- (removed) llvm/test/Transforms/MergeFunc/merge-functions-value-profile.ll (-142)
- (modified) llvm/unittests/Transforms/IPO/MergeFunctionsTest.cpp (+27-48)
``````````diff
diff --git a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
index d28d6167e5555..75399d3297a05 100644
--- a/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
+++ b/llvm/include/llvm/Transforms/IPO/MergeFunctions.h
@@ -28,9 +28,9 @@ class MergeFunctionsPass : public OptionalPassInfoMixin<MergeFunctionsPass> {
public:
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
- LLVM_ABI static bool runOnModule(Module &M, ModuleAnalysisManager &AM);
+ LLVM_ABI static bool runOnModule(Module &M);
LLVM_ABI static DenseMap<Function *, Function *>
- runOnFunctions(ArrayRef<Function *> Funcs, ModuleAnalysisManager &AM);
+ runOnFunctions(ArrayRef<Function *> F);
};
} // end namespace llvm
diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index afe80da5843ca..c4e56855ea2fb 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -89,15 +89,9 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/IPO/MergeFunctions.h"
-#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/PostOrderIterator.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/IR/Argument.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/DebugInfoMetadata.h"
@@ -110,21 +104,16 @@
#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"
@@ -132,10 +121,7 @@
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <algorithm>
#include <cassert>
-#include <cstddef>
-#include <cstdint>
#include <iterator>
-#include <memory>
#include <optional>
#include <set>
#include <utility>
@@ -208,11 +194,11 @@ class FunctionNode {
/// bitcast of the other.
class MergeFunctions {
public:
- explicit MergeFunctions(FunctionAnalysisManager &FAM)
- : FnTree(FunctionNodeCmp(&GlobalNumbers)), FAM(FAM) {}
+ MergeFunctions() : FnTree(FunctionNodeCmp(&GlobalNumbers)) {
+ }
template <typename FuncContainer> bool run(FuncContainer &Functions);
- DenseMap<Function *, Function *> runOnFunctions(ArrayRef<Function *> Funcs);
+ DenseMap<Function *, Function *> runOnFunctions(ArrayRef<Function *> F);
SmallPtrSet<GlobalValue *, 4> &getUsed();
@@ -271,8 +257,6 @@ class MergeFunctions {
/// again.
void mergeTwoFunctions(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.
/// \param PDVRUnrelatedWL The equivalent non-intrinsic debug records.
@@ -303,9 +287,7 @@ 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.
- // \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);
+ bool writeThunkOrAliasIfNeeded(Function *F, Function *G);
/// Replace function F with function G in the function tree.
void replaceFunctionInTree(const FunctionNode &FN, Function *G);
@@ -323,23 +305,20 @@ class MergeFunctions {
/// Deleted-New functions mapping
DenseMap<Function *, Function *> DelToNewMap;
-
- FunctionAnalysisManager &FAM;
};
} // end anonymous namespace
PreservedAnalyses MergeFunctionsPass::run(Module &M,
ModuleAnalysisManager &AM) {
- if (!MergeFunctionsPass::runOnModule(M, AM))
+ if (!MergeFunctionsPass::runOnModule(M))
return PreservedAnalyses::all();
return PreservedAnalyses::none();
}
SmallPtrSet<GlobalValue *, 4> &MergeFunctions::getUsed() { return Used; }
-bool MergeFunctionsPass::runOnModule(Module &M, ModuleAnalysisManager &AM) {
- auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
- MergeFunctions MF(FAM);
+bool MergeFunctionsPass::runOnModule(Module &M) {
+ MergeFunctions MF;
SmallVector<GlobalValue *, 4> UsedV;
collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/false);
collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/true);
@@ -348,15 +327,9 @@ bool MergeFunctionsPass::runOnModule(Module &M, ModuleAnalysisManager &AM) {
}
DenseMap<Function *, Function *>
-MergeFunctionsPass::runOnFunctions(ArrayRef<Function *> Funcs,
- ModuleAnalysisManager &AM) {
- if (Funcs.empty())
- return DenseMap<Function *, Function *>();
-
- Module &M = *Funcs.front()->getParent();
- auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
- MergeFunctions MF(FAM);
- return MF.runOnFunctions(Funcs);
+MergeFunctionsPass::runOnFunctions(ArrayRef<Function *> F) {
+ MergeFunctions MF;
+ return MF.runOnFunctions(F);
}
#ifndef NDEBUG
@@ -520,8 +493,8 @@ template <typename FuncContainer> bool MergeFunctions::run(FuncContainer &M) {
}
DenseMap<Function *, Function *>
-MergeFunctions::runOnFunctions(ArrayRef<Function *> Funcs) {
- [[maybe_unused]] bool MergeResult = this->run(Funcs);
+MergeFunctions::runOnFunctions(ArrayRef<Function *> F) {
+ [[maybe_unused]] bool MergeResult = this->run(F);
assert(MergeResult == !DelToNewMap.empty());
return this->DelToNewMap;
}
@@ -761,7 +734,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> GEntryCount = G->getEntryCount();
+ std::optional<uint64_t> GEC = G->getEntryCount();
BasicBlock *GEntryBlock = nullptr;
std::vector<Instruction *> PDIUnrelatedWL;
std::vector<DbgVariableRecord *> PDVRUnrelatedWL;
@@ -831,8 +804,8 @@ void MergeFunctions::writeThunk(Function *F, Function *G) {
<< G->getName() << "()\n");
} else {
NewG->copyAttributesFrom(G);
- if (GEntryCount)
- NewG->setEntryCount(*GEntryCount);
+ if (GEC)
+ NewG->setEntryCount(*GEC);
NewG->takeName(G);
// Ensure CFI type metadata is propagated to the new function.
copyMetadataIfPresent(G, NewG, "type");
@@ -885,36 +858,21 @@ 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. 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)
- mergeInstrProfMetadataInto(F, G);
-
- if (ShouldErase) {
+// it is discardable and unused), \p G is removed directly.
+bool MergeFunctions::writeThunkOrAliasIfNeeded(Function *F, Function *G) {
+ if (G->isDiscardableIfUnused() && G->use_empty() && !MergeFunctionsPDI) {
G->eraseFromParent();
return true;
}
-
- if (ShouldAlias) {
+ if (canCreateAliasFor(G)) {
writeAlias(F, G);
return true;
}
- if (ShouldThunk) {
+ if (canCreateThunkFor(F)) {
writeThunk(F, G);
return true;
}
-
- llvm_unreachable("Erase, alias or thunk must apply");
+ return false;
}
/// Returns true if \p F is either weak_odr or linkonce_odr.
@@ -922,174 +880,6 @@ static bool isODR(const Function *F) {
return F->hasWeakODRLinkage() || F->hasLinkOnceODRLinkage();
}
-static uint64_t getBlockCountForMerging(const BlockFrequencyInfo &BFI,
- const BasicBlock *BB) {
- if (auto Count = BFI.getBlockProfileCount(BB, /*AllowSynthetic=*/true))
- return *Count;
- return 1;
-}
-
-// The branch weights are relative within a function. Before merging we
-// normalize these 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);
- 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 *DstI,
- const Instruction *SrcI,
- const BlockFrequencyInfo &DstBFI,
- const BlockFrequencyInfo &SrcBFI) {
- SmallVector<uint32_t, 8> DstWeights, SrcWeights;
- bool HasDst = extractBranchWeights(*DstI, DstWeights);
- bool HasSrc = extractBranchWeights(*SrcI, SrcWeights);
- if (!HasDst && !HasSrc)
- return;
-
- uint64_t DstBlockCount = getBlockCountForMerging(DstBFI, DstI->getParent());
- uint64_t SrcBlockCount = getBlockCountForMerging(SrcBFI, SrcI->getParent());
-
- uint64_t DstTotal = 0, SrcTotal = 0;
- if (HasDst)
- extractProfTotalWeight(*DstI, DstTotal);
- if (HasSrc)
- extractProfTotalWeight(*SrcI, SrcTotal);
-
- assert((!HasDst || !HasSrc || DstWeights.size() == SrcWeights.size()) &&
- "equivalent branch/select instructions must have matching weight "
- "arity");
- size_t NumWeights = HasDst ? DstWeights.size() : SrcWeights.size();
- SmallVector<uint64_t, 8> MergedWeights;
- MergedWeights.reserve(NumWeights);
- for (size_t I = 0; I < NumWeights; ++I) {
- uint64_t DstW = HasDst ? DstWeights[I] : 0;
- uint64_t SrcW = HasSrc ? SrcWeights[I] : 0;
- uint64_t DstAbs = scaleToBlockCount(DstW, DstTotal, DstBlockCount);
- uint64_t SrcAbs = scaleToBlockCount(SrcW, SrcTotal, SrcBlockCount);
- MergedWeights.push_back(SaturatingAdd(DstAbs, SrcAbs));
- }
-
- bool IsExpected =
- hasBranchWeightOrigin(*DstI) && hasBranchWeightOrigin(*SrcI);
- setFittedBranchWeights(*DstI, MergedWeights, IsExpected);
-}
-
-// Accumulate value profile counts of Instruction I into Merged. Value profile
-// counts are absolute, not relative branch-style weights.
-static void addValueProfile(const Instruction &I, InstrProfValueKind Kind,
- DenseMap<uint64_t, uint64_t> &Merged) {
- uint64_t Total = 0;
- SmallVector<InstrProfValueData, 4> VDs =
- getValueProfDataFromInst(I, Kind, /*MaxNumValueData=*/UINT32_MAX, Total);
- if (VDs.empty())
- return;
- for (const InstrProfValueData &VD : VDs)
- Merged[VD.Value] = SaturatingAdd(Merged[VD.Value], VD.Count);
-}
-
-// Merge (union) value profiles of Dst and Src.
-static void mergeValueProfileOnInstructions(Instruction *DstI,
- const Instruction *SrcI) {
- 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 *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 = DstKind ? DstKind : SrcKind;
- if (!KindCI) {
- DstI->setMetadata(LLVMContext::MD_prof, nullptr);
- return;
- }
-
- InstrProfValueKind Kind =
- static_cast<InstrProfValueKind>(KindCI->getZExtValue());
-
- DenseMap<uint64_t, uint64_t> Merged;
- if (HasDst)
- addValueProfile(*DstI, Kind, Merged);
- if (HasSrc)
- addValueProfile(*SrcI, Kind, 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(*DstI->getFunction()->getParent(), *DstI, VDs, Sum, Kind,
- VDs.size());
-}
-
-/// 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) {
- const BlockFrequencyInfo &DstBFI =
- FAM.getResult<BlockFrequencyAnalysis>(*Dst);
- const BlockFrequencyInfo &SrcBFI =
- FAM.getResult<BlockFrequencyAnalysis>(*Src);
-
- // FunctionComparator guarantees identical CFG topology and instruction
- // ordering. Walk the CFGs in RPO rather than function block-list order, as
- // equivalent functions need not store their basic blocks in the same order.
- ReversePostOrderTraversal<Function *> DstRPOT(Dst);
- ReversePostOrderTraversal<Function *> SrcRPOT(Src);
- for (auto [DstBB, SrcBB] : llvm::zip_equal(DstRPOT, SrcRPOT)) {
- 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);
-
- // Handle branch weights on SelectInsts here. Terminators are handled
- // separately below, outside the instruction loop.
- if (isa<SelectInst>(DstI))
- mergeBranchWeightsOnInstructions(&DstI, &SrcI, DstBFI, SrcBFI);
- }
- Instruction *DstTerm = DstBB->getTerminator();
- const Instruction *SrcTerm = SrcBB->getTerminator();
- mergeBranchWeightsOnInstructions(DstTerm, SrcTerm, DstBFI, SrcBFI);
- }
-
- PreservedAnalyses PA = PreservedAnalyses::all();
- PA.abandon<BranchProbabilityAnalysis>();
- PA.abandon<BlockFrequencyAnalysis>();
- FAM.invalidate(*Dst, PA);
-}
-
static void mergeEntryCountsInto(Function *F, std::optional<uint64_t> FC,
std::optional<uint64_t> GC) {
if (!FC && !GC)
@@ -1101,8 +891,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> FEntryCount = F->getEntryCount();
- std::optional<uint64_t> GEntryCount = G->getEntryCount();
+ std::optional<uint64_t> FEC = F->getEntryCount();
+ std::optional<uint64_t> GEC = 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
@@ -1145,13 +935,10 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
const MaybeAlign NewFAlign = NewF->getAlign();
const MaybeAlign GAlign = G->getAlign();
- // Merge !prof, while G still has its body.
- writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true);
- 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);
+ writeThunkOrAliasIfNeeded(F, G);
+ if (FEC)
+ NewF->setEntryCount(*FEC);
+ writeThunkOrAliasIfNeeded(F, NewF);
if (NewFAlign || GAlign)
F->setAlignment(std::max(NewFAlign.valueOrOne(), GAlign.valueOrOne()));
@@ -1159,9 +946,8 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
F->setAlignment(std::nullopt);
F->setLinkage(GlobalValue::PrivateLinkage);
// The private shared implementation accumulates both symbols' entries
- // (FEntryCount + GEntryCount), while each ODR thunk retains its own
- // per-symbol entry count.
- mergeEntryCountsInto(F, FEntryCount, GEntryCount);
+ // (FEC + GEC), while each ODR thunk retains its own per-symbol entry count.
+ mergeEntryCountsInto(F, FEC, GEC);
++NumDoubleWeak;
++NumFunctionsMerged;
} else {
@@ -1189,15 +975,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) {
- mergeInstrProfMetadataInto(F, G);
- mergeEntryCountsInto(F, FEntryCount, GEntryCount);
+ mergeEntryCountsInto(F, FEC, GEC);
G->eraseFromParent();
++NumFunctionsMerged;
return;
}
- if (writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true)) {
- mergeEntryCountsInto(F, FEntryCount, GEntryCount);
+ if (writeThunkOrAliasIfNeeded(F, G)) {
+ mergeEntryCountsInto(F, FEC, GEC);
++NumFunctionsMerged;
}
}
diff --git a/llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll b/llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll
deleted file mode 100644
index 46a80c2916766..0000000000000
--- a/llvm/test/Transforms/MergeFunc/merge-functions-branch-weights.ll
+++ /dev/null
@@ -1,239 +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
-
-; 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 ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/209987
More information about the llvm-commits
mailing list